403Webshell
Server IP : 146.59.209.152  /  Your IP : 216.73.216.46
Web Server : Apache
System : Linux webm005.cluster131.gra.hosting.ovh.net 5.15.167-ovh-vps-grsec-zfs-classid #1 SMP Tue Sep 17 08:14:20 UTC 2024 x86_64
User : infrafs ( 43850)
PHP Version : 8.2.29
Disable Function : _dyuweyrj4,_dyuweyrj4r,dl
MySQL : OFF  |  cURL : ON  |  WGET : ON  |  Perl : ON  |  Python : ON  |  Sudo : OFF  |  Pkexec : OFF
Directory :  /home/i/n/f/infrafs/INFRABIKEIT/wp-content/plugins/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Command :


[ Back ]     

Current File : /home/i/n/f/infrafs/INFRABIKEIT/wp-content/plugins/wpvivid-backuprestore.tar
uninstall.php000064400000010226151327705670007304 0ustar00<?php

// If uninstall not called from WordPress, then exit.
if ( ! defined( 'WP_UNINSTALL_PLUGIN' ) ) {
	exit;
}

function wpvivid_clear_free_dir($directory){
    if(file_exists($directory)){
        if($dir_handle=@opendir($directory)){
            while($filename=readdir($dir_handle)){
                if($filename!='.' && $filename!='..'){
                    $subFile=$directory."/".$filename;
                    if(is_dir($subFile)){
                        wpvivid_clear_free_dir($subFile);
                    }
                    if(is_file($subFile)){
                        wp_delete_file($subFile);
                    }
                }
            }
            closedir($dir_handle);
            rmdir($directory);
        }
    }
}

$wpvivid_common_setting = get_option('wpvivid_common_setting', array());
if(!empty($wpvivid_common_setting))
{
    if(isset($wpvivid_common_setting['uninstall_clear_folder']) && $wpvivid_common_setting['uninstall_clear_folder'])
    {
        $wpvivid_local_setting = get_option('wpvivid_local_setting', array());
        if(isset($wpvivid_local_setting['path']))
        {
            if($wpvivid_local_setting['path'] !== 'wpvividbackups')
            {
                wpvivid_clear_free_dir(WP_CONTENT_DIR.DIRECTORY_SEPARATOR.'wpvividbackups');
            }
            $wpvivid_local_setting['path']=basename($wpvivid_local_setting['path']);
            $source_array=array('.', '..', 'plugins', 'themes', 'uploads');
            if(!empty($wpvivid_local_setting['path']) && !in_array($wpvivid_local_setting['path'], $source_array))
            {
                if(is_dir(WP_CONTENT_DIR.DIRECTORY_SEPARATOR.$wpvivid_local_setting['path']))
                {
                    wpvivid_clear_free_dir(WP_CONTENT_DIR.DIRECTORY_SEPARATOR.$wpvivid_local_setting['path']);
                }
            }
        }
        else
        {
            wpvivid_clear_free_dir(WP_CONTENT_DIR.DIRECTORY_SEPARATOR.'wpvividbackups');
        }
    }
}

delete_option('wpvivid_schedule_setting');
delete_option('wpvivid_email_setting');
delete_option('wpvivid_compress_setting');
delete_option('wpvivid_local_setting');
delete_option('wpvivid_upload_setting');
delete_option('wpvivid_common_setting');
delete_option('wpvivid_backup_list');
delete_option('wpvivid_task_list');
delete_option('wpvivid_init');
delete_option('wpvivid_remote_init');
delete_option('wpvivid_last_msg');
delete_option('wpvivid_download_cache');
delete_option('wpvivid_download_task');
delete_option('wpvivid_user_history');
delete_option('wpvivid_saved_api_token');
delete_option('wpvivid_import_list_cache');
delete_option('wpvivid_importer_task_list');
delete_option('wpvivid_list_cache');
delete_option('wpvivid_exporter_task_list');
delete_option('wpvivid_need_review');
delete_option('wpvivid_review_msg');
delete_option('wpvivid_review_time');
delete_option('wpvivid_review_type');
delete_option('wpvivid_migrate_status');
delete_option('clean_task');
delete_option('cron_backup_count');
delete_option('wpvivid_backup_success_count');
delete_option('wpvivid_backup_error_array');
delete_option('wpvivid_amazons3_notice');
delete_option('wpvivid_hide_mwp_tab_page_v1');
delete_option('wpvivid_hide_wp_cron_notice');
delete_option('wpvivid_transfer_error_array');
delete_option('wpvivid_transfer_success_count');
delete_option('wpvivid_api_token');
delete_option('wpvivid_download_task_v2');
delete_option('wpvivid_export_list');
delete_option('wpvivid_backup_report');

$options=get_option('wpvivid_staging_options',array());
$staging_keep_setting=isset($options['staging_keep_setting']) ? $options['staging_keep_setting'] : true;
if($staging_keep_setting)
{

}
else
{
    delete_option('wpvivid_staging_task_list');
    delete_option('wpvivid_staging_task_cancel');
    delete_option('wpvivid_staging_options');
    delete_option('wpvivid_staging_history');
    delete_option('wpvivid_staging_list');
}

define('WPVIVID_MAIN_SCHEDULE_EVENT','wpvivid_main_schedule_event');

if(wp_get_schedule(WPVIVID_MAIN_SCHEDULE_EVENT))
{
    wp_clear_scheduled_hook(WPVIVID_MAIN_SCHEDULE_EVENT);
    $timestamp = wp_next_scheduled(WPVIVID_MAIN_SCHEDULE_EVENT);
    wp_unschedule_event($timestamp,WPVIVID_MAIN_SCHEDULE_EVENT);
}
includes/class-wpvivid-i18n.php000064400000001603151327705670012450 0ustar00<?php
if (!defined('WPVIVID_PLUGIN_DIR')){
    die;
}
/**
 * Define the internationalization functionality
 *
 * Loads and defines the internationalization files for this plugin
 * so that it is ready for translation.
 *
 * @link       https://wpvivid.com
 * @since      0.9.1
 *
 * @package    wpvivid
 * @subpackage wpvivid/includes
 */

/**
 * Define the internationalization functionality.
 *
 * Loads and defines the internationalization files for this plugin
 * so that it is ready for translation.
 *
 * @since      0.9.1
 * @package    wpvivid
 * @subpackage wpvivid/includes
 * @author     wpvivid team
 */
class WPvivid_i18n {


	/**
	 * Load the plugin text domain for translation.
	 *
	 * 
	 */
	public function load_plugin_textdomain() {

		load_plugin_textdomain(
			'wpvivid-backuprestore',
			false,
			dirname( dirname( plugin_basename( __FILE__ ) ) ) . '/languages/'
		);

	}



}
includes/class-wpvivid-zipclass.php000064400000152531151327705670013530 0ustar00<?php

if (!defined('WPVIVID_PLUGIN_DIR')){
    die;
}

require_once WPVIVID_PLUGIN_DIR . '/includes/class-wpvivid-compress-default.php';

$wpvivid_extract_option = array();

class WPvivid_ZipClass extends Wpvivid_Compress_Default
{
	public $last_error = '';
	public $path_filter=array();

	public function __construct()
    {
		if (!class_exists('PclZip'))
		    include_once(ABSPATH.'/wp-admin/includes/class-pclzip.php');
		if (!class_exists('PclZip'))
		{
			$this->last_error = array('result'=>WPVIVID_FAILED,'error'=>"Class PclZip is not detected. Please update or reinstall your WordPress.");
		}
    }

	public function get_packages($data,$write_child_files_json=false)
    {
        if(!function_exists('get_home_path'))
            require_once(ABSPATH . 'wp-admin/includes/file.php');
        $files = $this -> filesplit($data['compress']['max_file_size'],$data['files']);

        $temp_dir = $data['path'].'temp-'.$data['prefix'].DIRECTORY_SEPARATOR;
        if(!file_exists($temp_dir))
            @mkdir($temp_dir);
        $packages = array();
        if(sizeof($files) > 1)
        {
            for($i =0;$i <sizeof($files);$i ++)
            {
                $package = array();
                $path = $data['path'].$data['prefix'].'.part'.sprintf('%03d',($i +1)).'.zip';

                if(isset($data['json_info']))
                {
                    $package['json']=$data['json_info'];
                }
                /*
                $remove_path_size = strlen( $this -> transfer_path(get_home_path()));
                $package['json']['root'] = substr($data['root_path'], $remove_path_size);
                */
                if($write_child_files_json)
                {
                    foreach ($files[$i] as $file)
                    {
                        $ret_file=$this->get_json_data($file);
                        if($ret_file['result']==WPVIVID_SUCCESS)
                        {
                            $json=$ret_file['json_data'];
                            $json = json_decode($json, 1);
                            $package['json']['child_file'][basename($file)]=$json;
                        }
                    }
                }
                if(isset($data['root_flag']))
                    $package['json']['root_flag'] = $data['root_flag'];
                if(isset($data['root_path']))
                    $package['json']['root_path'] = $data['root_path'];
                $package['json']['file']=basename($path);
                $package['path'] = $path;
                $package['files'] = $files[$i];
                $packages[] = $package;
            }
        }else {
            $package = array();
            $path = $data['path'].$data['prefix'].'.zip';

            if(isset($data['json_info']))
            {
                $package['json']=$data['json_info'];
            }

            if($write_child_files_json)
            {
                foreach ($files[0] as $file)
                {
                    $ret_file=$this->get_json_data($file);
                    if($ret_file['result']==WPVIVID_SUCCESS)
                    {
                        $json=$ret_file['json_data'];
                        $json = json_decode($json, 1);
                        $package['json']['child_file'][basename($file)]=$json;
                    }
                }
            }
            /*
            $remove_path_size = strlen( $this -> transfer_path(get_home_path()));
            $package['json']['root'] = substr($data['root_path'], $remove_path_size);
            */
            if(isset($data['root_flag']))
                $package['json']['root_flag'] = $data['root_flag'];
            if(isset($data['root_path']))
                $package['json']['root_path'] = $data['root_path'];
            $package['json']['file']=basename($path);
            $package['path'] = $path;
            $package['files'] = $files[0];
            $packages[] = $package;
        }

        $ret['packages']=$packages;
        $ret['temp_dir']=$temp_dir;
        return $ret;
    }

    public function get_plugin_packages($data)
    {
        if(!function_exists('get_home_path'))
            require_once(ABSPATH . 'wp-admin/includes/file.php');

        $max_size= $data['compress']['max_file_size'];

        $max_size = str_replace('M', '', $max_size);
        if($max_size==0)
            $max_size=200;
        $size = intval($max_size) * 1024 * 1024;

        $files = $this -> filesplit_plugin($size,$data['files'],false);

        $temp_dir = $data['path'].'temp-'.$data['prefix'].DIRECTORY_SEPARATOR;
        if(!file_exists($temp_dir))
            @mkdir($temp_dir);
        $packages = array();

        if(sizeof($files) > 1)
        {
            for($i =0;$i <sizeof($files);$i ++)
            {
                $package = array();
                $path = $data['path'].$data['prefix'].'.part'.sprintf('%03d',($i +1)).'.zip';
                if(isset($data['json_info']))
                {
                    $package['json']=$data['json_info'];
                }
                /*
                $remove_path_size = strlen( $this -> transfer_path(get_home_path()));
                $package['json']['root'] = substr($data['root_path'], $remove_path_size);
                */
                if(isset($data['root_flag']))
                    $package['json']['root_flag'] = $data['root_flag'];
                if(isset($data['root_path']))
                    $package['json']['root_path'] = $data['root_path'];
                $package['json']['file']=basename($path);
                $package['path'] = $path;
                $package['files'] = $files[$i];
                $packages[] = $package;
            }
        }else {
            $package = array();
            $path = $data['path'].$data['prefix'].'.zip';

            if(isset($data['json_info']))
            {
                $package['json']=$data['json_info'];
            }
            /*
            $remove_path_size = strlen( $this -> transfer_path(get_home_path()));
            $package['json']['root'] = substr($data['root_path'], $remove_path_size);
            */
            if(isset($data['root_flag']))
                $package['json']['root_flag'] = $data['root_flag'];
            if(isset($data['root_path']))
                $package['json']['root_path'] = $data['root_path'];
            $package['json']['file']=basename($path);
            $package['path'] = $path;
            $package['files'] = $files[0];
            $packages[] = $package;
        }

        $ret['packages']=$packages;
        $ret['temp_dir']=$temp_dir;
        return $ret;
    }

    public function get_upload_packages($data)
    {
        if(!function_exists('get_home_path'))
            require_once(ABSPATH . 'wp-admin/includes/file.php');

        $max_size= $data['compress']['max_file_size'];

        $max_size = str_replace('M', '', $max_size);
        if($max_size==0)
            $max_size=200;
        $size = intval($max_size) * 1024 * 1024;

        $files = $this -> get_files_cache($size,$data);

        $temp_dir = $data['path'].'temp-'.$data['prefix'].DIRECTORY_SEPARATOR;
        if(!file_exists($temp_dir))
            @mkdir($temp_dir);
        $packages = array();

        if(sizeof($files) > 1)
        {
            $i=0;
            foreach ($files as $file)
            {
                $package = array();
                $path = $data['path'].$data['prefix'].'.part'.sprintf('%03d',($i +1)).'.zip';
                if(isset($data['json_info']))
                {
                    $package['json']=$data['json_info'];
                }
                /*
                $remove_path_size = strlen( $this -> transfer_path(get_home_path()));
                $package['json']['root'] = substr($data['root_path'], $remove_path_size);
                */
                if(isset($data['root_flag']))
                    $package['json']['root_flag'] = $data['root_flag'];
                if(isset($data['root_path']))
                    $package['json']['root_path'] = $data['root_path'];
                $package['json']['file']=basename($path);
                $package['path'] = $path;
                $package['files'] = $file;
                $packages[] = $package;
                $i++;
            }
        }else {
            $package = array();
            $path = $data['path'].$data['prefix'].'.zip';
            if(isset($data['json_info']))
            {
                $package['json']=$data['json_info'];
            }
            /*
            $remove_path_size = strlen( $this -> transfer_path(get_home_path()));
            $package['json']['root'] = substr($data['root_path'], $remove_path_size);
            */
            if(isset($data['root_flag']))
                $package['json']['root_flag'] = $data['root_flag'];
            if(isset($data['root_path']))
                $package['json']['root_path'] = $data['root_path'];
            $package['json']['file']=basename($path);
            $package['path'] = $path;
            $package['files'] = $files[0];
            $packages[] = $package;
        }

        $ret['packages']=$packages;
        return $ret;
    }

    public function compress_additional_database($data){
        if(!function_exists('get_home_path'))
            require_once(ABSPATH . 'wp-admin/includes/file.php');
        global $wpvivid_plugin;
        $wpvivid_plugin->wpvivid_log->WriteLog('Start compressing '.$data['key'],'notice');
        $files = $data['files'];
        $temp_dir = $data['path'].'temp-'.$data['prefix'].DIRECTORY_SEPARATOR;
        if(!file_exists($temp_dir))
            @mkdir($temp_dir);

        $package_file = array();

        $ret['result']=WPVIVID_SUCCESS;
        $ret['files']=array();

        foreach ($files as $file){
            $file_name = $file;
            $file_name = str_replace($data['path'], '', $file_name);
            $file_name = str_replace('.sql', '', $file_name);
            $path = $data['path'].$file_name.'.zip';
            if(isset($data['json_info']))
            {
                $package_file['json']=$data['json_info'];
                foreach ($data['sql_file_name'] as $sql_info){
                    if($file === $sql_info['file_name']){
                        $package_file['json']['database'] = $sql_info['database'];
                    }
                }
            }
            if(isset($data['root_path']))
                $package['json']['root_path'] = $data['root_path'];
            if(isset($data['root_flag']))
                $package_file['json']['root_flag'] = $data['root_flag'];
            $package_file['json']['file']=basename($path);
            $package_file['path'] = $path;
            $package_file['files'] = $file;
            $wpvivid_plugin->set_time_limit($wpvivid_plugin->current_task['id']);
            $zip_ret=$this -> _zip($package_file['path'],$package_file['files'], $data, $package_file['json']);
            if($zip_ret['result']==WPVIVID_SUCCESS)
            {
                $ret['files'][] = $zip_ret['file_data'];
            }
            else
            {
                $ret=$zip_ret;
                break;
            }
        }

        $wpvivid_plugin->wpvivid_log->WriteLog('Compressing '.$data['key'].' completed','notice');
        return $ret;
    }

	public function compress($data,$write_child_files_json=false)
    {
        if(!function_exists('get_home_path'))
            require_once(ABSPATH . 'wp-admin/includes/file.php');
        global $wpvivid_plugin;
        $wpvivid_plugin->wpvivid_log->WriteLog('Start compressing '.$data['key'],'notice');
	    $files = $this -> filesplit($data['compress']['max_file_size'],$data['files']);

        $temp_dir = $data['path'].'temp-'.$data['prefix'].DIRECTORY_SEPARATOR;
        if(!file_exists($temp_dir))
            @mkdir($temp_dir);
        $packages = array();
	    if(sizeof($files) > 1)
	    {
            for($i =0;$i <sizeof($files);$i ++)
            {
                $package = array();
                $path = $data['path'].$data['prefix'].'.part'.sprintf('%03d',($i +1)).'.zip';
                if(isset($data['json_info']))
                {
                    $package['json']=$data['json_info'];
                }
                /*
                $remove_path_size = strlen( $this -> transfer_path(get_home_path()));
                $package['json']['root'] = substr($data['root_path'], $remove_path_size);
                */
                if(isset($data['root_flag']))
                    $package['json']['root_flag'] = $data['root_flag'];
                if(isset($options['root_path']))
                    $package['json']['root_path'] = $data['root_path'];
                $package['json']['file']=basename($path);
                $package['path'] = $path;
                $package['files'] = $files[$i];
                $packages[] = $package;
            }
        }else {
	        $package = array();
            $path = $data['path'].$data['prefix'].'.zip';
            if(isset($data['json_info']))
            {
                $package['json']=$data['json_info'];
            }
            /*
            $remove_path_size = strlen( $this -> transfer_path(get_home_path()));
            $package['json']['root'] = substr($data['root_path'], $remove_path_size);
            */
            if(isset($data['root_flag']))
                $package['json']['root_flag'] = $data['root_flag'];
            if(isset($options['root_path']))
                $package['json']['root_path'] = $data['root_path'];

            $package['json']['file']=basename($path);
            $package['path'] = $path;
            $package['files'] = $files[0];
            $packages[] = $package;
        }

        $ret['result']=WPVIVID_SUCCESS;
        $ret['files']=array();

        foreach ($packages as $package)
        {
            if(!empty($package['files']))
            {
                $wpvivid_plugin->set_time_limit($wpvivid_plugin->current_task['id']);
                $zip_ret=$this -> _zip($package['path'],$package['files'], $data,$package['json']);
                if($zip_ret['result']==WPVIVID_SUCCESS)
                {
                    $ret['files'][] = $zip_ret['file_data'];
                }
                else
                {
                    $ret=$zip_ret;
                    break;
                }
            }else {
                continue;
            }
        }
        $wpvivid_plugin->wpvivid_log->WriteLog('Compressing '.$data['key'].' completed','notice');
        return $ret;
    }

    public function extract($files, $path = '', $option = array())
    {
        if (!class_exists('WPvivid_PclZip'))
            include_once WPVIVID_PLUGIN_DIR . '/includes/zip/class-wpvivid-pclzip.php';

        if(!empty($option)){
            $GLOBALS['wpvivid_extract_option'] = $option;
        }

        global $wpvivid_plugin;
        //$wpvivid_plugin->restore_data->write_log('start prepare extract','notice');
        define(PCLZIP_TEMPORARY_DIR,dirname($path));

        $ret['result']=WPVIVID_SUCCESS;
        foreach ($files as $file)
        {
            $wpvivid_plugin->restore_data->write_log('start extracting file:'.$file,'notice');
            $archive = new WPvivid_PclZip($file);
            $zip_ret = $archive->extract(WPVIVID_PCLZIP_OPT_PATH, $path,WPVIVID_PCLZIP_OPT_REPLACE_NEWER,WPVIVID_PCLZIP_CB_PRE_EXTRACT,'wpvivid_function_pre_extract_callback',WPVIVID_PCLZIP_OPT_TEMP_FILE_THRESHOLD,16);
            if(!$zip_ret)
            {
                $ret['result']=WPVIVID_FAILED;
                $ret['error'] = $archive->errorInfo(true);
                $wpvivid_plugin->restore_data->write_log('extract finished:'.wp_json_encode($ret),'notice');
                break;
            }
            else
            {
                $wpvivid_plugin->restore_data->write_log('extract finished file:'.$file,'notice');
            }
        }
        //$this->restore_data->write_log('extract finished files:'.wp_json_encode($all_files),'notice');

        return $ret;
    }

    public function extract_ex($files,$path = '',$extract_files=array())
    {
        if (!class_exists('WPvivid_PclZip'))
            include_once WPVIVID_PLUGIN_DIR . '/includes/zip/class-wpvivid-pclzip.php';

        global $wpvivid_plugin;
        //$wpvivid_plugin->restore_data->write_log('start prepare extract','notice');
        define(PCLZIP_TEMPORARY_DIR,dirname($path));

        $ret['result']=WPVIVID_SUCCESS;
        foreach ($files as $file)
        {
            $wpvivid_plugin->restore_data->write_log('start extracting file:'.$file,'notice');
            $wpvivid_plugin->restore_data->write_log('extract child file:'.wp_json_encode($extract_files),'notice');
            $archive = new WPvivid_PclZip($file);
            $zip_ret = $archive->extract(WPVIVID_PCLZIP_OPT_BY_NAME,$extract_files,WPVIVID_PCLZIP_OPT_PATH, $path,WPVIVID_PCLZIP_OPT_REPLACE_NEWER,WPVIVID_PCLZIP_CB_PRE_EXTRACT,'wpvivid_function_pre_extract_callback',WPVIVID_PCLZIP_OPT_TEMP_FILE_THRESHOLD,16);
            if(!$zip_ret)
            {
                $ret['result']=WPVIVID_FAILED;
                $ret['error'] = $archive->errorInfo(true);
                $wpvivid_plugin->restore_data->write_log('extract finished:'.wp_json_encode($ret),'notice');
                break;
            }
            else
            {
                $wpvivid_plugin->restore_data->write_log('extract finished file:'.$file,'notice');
            }
        }
        //$this->restore_data->write_log('extract finished files:'.wp_json_encode($all_files),'notice');

        return $ret;
    }

    public function extract_by_files($files,$zip,$path = '')
    {
        if (!class_exists('WPvivid_PclZip'))
            include_once WPVIVID_PLUGIN_DIR . '/includes/zip/class-wpvivid-pclzip.php';

        define(PCLZIP_TEMPORARY_DIR,$path);
        $flag = true;
        $table = array();
        $archive = new WPvivid_PclZip($zip);
        $list = $archive -> listContent();
        foreach ($list as $item)
        {
            if(strstr($item['filename'],WPVIVID_ZIPCLASS_JSONFILE_NAME))
            {
                $result = $archive->extract(WPVIVID_PCLZIP_OPT_BY_NAME, WPVIVID_ZIPCLASS_JSONFILE_NAME);
                if($result)
                {
                    $json = json_decode(file_get_contents(dirname($zip).WPVIVID_ZIPCLASS_JSONFILE_NAME),true);
                    $path = $json['root_path'];
                }
            }
        }

        $str = $archive->extract(WPVIVID_PCLZIP_OPT_PATH, $path, WPVIVID_PCLZIP_OPT_BY_NAME, $files, WPVIVID_PCLZIP_OPT_REPLACE_NEWER,WPVIVID_PCLZIP_OPT_TEMP_FILE_THRESHOLD,16);
        if(!$str){
            $flag = false;
            $error = $archive->errorInfo(true);
        }else{
            $success_num = 0;
            $error_num = 0;
            $last_error = '';
            foreach ($str as $item){
                if($item['status'] === 'ok'){
                    $success_num ++;
                }else{
                    $error_num ++;
                    $last_error = 'restore '.$item['filename'].' failed status:'.$item['status'];
                }
            }
            $table['succeed'] = $success_num;
            $table['failed'] = $error_num;
            $error = $last_error;
        }

        if($flag){
            return array('result'=>WPVIVID_SUCCESS,'table'=>$table,'error' => $error);
        }else{
            return array('result'=>'failed','error'=>$error);
        }
    }

    public function get_include_zip($files,$allpackages){
        if (!class_exists('WPvivid_PclZip'))
            include_once WPVIVID_PLUGIN_DIR . '/includes/zip/class-wpvivid-pclzip.php';

        $i = sizeof($files);
        $zips = array();
        foreach ( $allpackages as $item){
            $archive = new WPvivid_PclZip($item);
            $lists = $archive -> listContent();
            foreach ($lists as $file){
                if($this -> _in_array($file['filename'],$files)){
                    $zips[$item][] = $file['filename'];
                    if($i -- === 0)
                        break 2;
                }
            }
        }
        return $zips;
    }

    public function _zip($name,$files,$options,$json_info=false)
    {
        $zip_object_class=apply_filters('wpvivid_get_zip_object_class_ex','WPvivid_PclZip_Class',$options);
        $zip=new $zip_object_class();
        return $zip->zip($name,$files,$options,$json_info);
    }

    public function listcontent($path){
        if (!class_exists('WPvivid_PclZip'))
            include_once WPVIVID_PLUGIN_DIR . '/includes/zip/class-wpvivid-pclzip.php';
        $zip = new WPvivid_PclZip($path);
        $list = $zip->listContent();
        return $list;
    }
    public function listnum($path , $includeFolder = false){
        if (!class_exists('WPvivid_PclZip'))
            include_once WPVIVID_PLUGIN_DIR . '/includes/zip/class-wpvivid-pclzip.php';
        $zip = new WPvivid_PclZip($path);
        $list = $zip->listContent();
        $index = 0;
        foreach ($list as $item){
            if(!$includeFolder && $item['folder'])
                continue;
            $index ++;
        }
        return $index;
    }

    private function transfer_path($path)
    {
        $path = str_replace('\\','/',$path);
        $values = explode('/',$path);
        return implode(DIRECTORY_SEPARATOR,$values);
    }

    public function get_json_data($path, $json_type = 'backup')
    {
        if (!class_exists('WPvivid_PclZip'))
            include_once WPVIVID_PLUGIN_DIR . '/includes/zip/class-wpvivid-pclzip.php';
        $json_file_name = $json_type === 'backup' ? 'wpvivid_package_info.json' : 'wpvivid_export_package_info.json';
        $archive = new WPvivid_PclZip($path);
        $list = $archive->listContent();
        if($list == false){
            return array('result'=>WPVIVID_FAILED,'error'=>$archive->errorInfo(true));
        }
        else {
            $b_exist = false;
            foreach ($list as $item) {
                if (basename($item['filename']) === $json_file_name) {
                    $b_exist = true;
                    $result = $archive->extract(WPVIVID_PCLZIP_OPT_BY_NAME, $json_file_name, WPVIVID_PCLZIP_OPT_EXTRACT_AS_STRING);
                    if ($result != 0) {
                        return array('result'=>WPVIVID_SUCCESS,'json_data'=>$result[0]['content']);
                    } else {
                        return array('result'=>WPVIVID_FAILED,'error'=>$archive->errorInfo(true));
                    }
                }
            }
            if(!$b_exist){
                return array('result'=>WPVIVID_FAILED,'error'=>'Failed to get json, this may be a old version backup.');
            }
        }
        return array('result'=>WPVIVID_FAILED,'error'=>'Unknown error');
    }

    public function list_file($path)
    {
        if (!class_exists('WPvivid_PclZip'))
            include_once WPVIVID_PLUGIN_DIR . '/includes/zip/class-wpvivid-pclzip.php';
        $archive = new WPvivid_PclZip($path);
        $list = $archive->listContent();

        $files=array();
        foreach ($list as $item)
        {
            if(basename($item['filename'])==='wpvivid_package_info.json')
            {
                continue;
            }
            $file['file_name']=$item['filename'];
            $files[]=$file;
        }

        return $files;
    }

    public function filesplit_plugin($max_file_size,$files,$is_num=true)
    {
        $packages=array();
        if($max_file_size == 0 || empty($max_file_size))
        {
            $packages[] = $files;
        }else{
            $folder_num_sum = 0;
            $package = array();

            if($is_num)
            {
                foreach ($files as $file)
                {
                    $folder_num=0;
                    if(is_dir($file))
                    {
                        $folder_num=$this->get_folder_file_count($file);
                    }
                    else
                    {
                        $folder_num_sum+=filesize($file);
                    }

                    if($folder_num > $max_file_size)
                    {
                        $temp_package[] = $file;
                        $packages[] = $temp_package;
                        $temp_package = array();
                        continue;
                    }
                    else
                    {
                        $folder_num_sum+=$folder_num;
                    }

                    if($folder_num_sum > $max_file_size)
                    {
                        $package[] = $file;
                        $packages[] = $package;
                        $package = array();
                        $folder_num_sum=0;
                    }
                    else{
                        $package[] = $file;
                    }
                }
            }
            else
            {
                foreach ($files as $file)
                {
                    $folder_num=0;
                    if(is_dir($file))
                    {
                        $folder_num=$this->get_folder_file_size($file);
                    }
                    else
                    {
                        $folder_num_sum+=filesize($file);
                    }

                    if($folder_num > $max_file_size)
                    {
                        $temp_package[] = $file;
                        $packages[] = $temp_package;
                        $temp_package = array();
                        continue;
                    }
                    else
                    {
                        $folder_num_sum+=$folder_num;
                    }

                    if($folder_num_sum > $max_file_size)
                    {
                        $package[] = $file;
                        $packages[] = $package;
                        $package = array();
                        $folder_num_sum=0;
                    }
                    else{
                        $package[] = $file;
                    }
                }
            }

            if(!empty($package))
                $packages[] = $package;
        }
        return $packages;
    }

    public function get_folder_file_count($file)
    {
        $count=0;
        $this->get_folder_file_count_loop($file,$count);

        return $count;
    }

    function get_folder_file_count_loop($path,&$count)
    {
        $handler = opendir($path);
        if($handler!==false)
        {
            while (($filename = readdir($handler)) !== false)
            {
                if ($filename != "." && $filename != "..")
                {
                    $count++;

                    if(is_dir($path . DIRECTORY_SEPARATOR . $filename))
                    {
                        $this->get_folder_file_count_loop($path . DIRECTORY_SEPARATOR . $filename,$count);
                    }
                }
            }
            if($handler)
                @closedir($handler);
        }
    }

    function get_folder_file_size($file)
    {
        $count=0;
        $this->get_folder_file_size_loop($file,$count);

        return $count;
    }

    function get_folder_file_size_loop($path,&$count)
    {
        $handler = opendir($path);
        if($handler!==false)
        {
            while (($filename = readdir($handler)) !== false)
            {
                if ($filename != "." && $filename != "..")
                {
                    if(is_dir($path . DIRECTORY_SEPARATOR . $filename))
                    {
                        $this->get_folder_file_size_loop($path . DIRECTORY_SEPARATOR . $filename,$count);
                    }
                    else
                    {
                        $count+=filesize($path . DIRECTORY_SEPARATOR . $filename);
                    }
                }
            }
            if($handler)
                @closedir($handler);
        }
    }

    public function get_root_flag_path($flag)
    {
        $path='';
        if($flag==WPVIVID_BACKUP_ROOT_WP_CONTENT)
        {
            $path=WP_CONTENT_DIR;
        }
        else if($flag==WPVIVID_BACKUP_ROOT_CUSTOM)
        {
            $path=WP_CONTENT_DIR.DIRECTORY_SEPARATOR.WPvivid_Setting::get_backupdir();
        }
        else if($flag==WPVIVID_BACKUP_ROOT_WP_ROOT)
        {
            $path=ABSPATH;
        }
        return $path;
    }

    public function get_files_cache($size,$data)
    {
        $number=1;
        $cache_perfix = $data['path'].$data['prefix'].'_file_cache_';
        $cache_file_handle=false;
        $sumsize=0;

        if(isset($data['exclude_files_regex']))
            $exclude_files_regex=$data['exclude_files_regex'];
        else
            $exclude_files_regex=array();

        if(isset($data['exclude_regex']))
            $exclude_regex=$data['exclude_regex'];
        else
            $exclude_regex=array();

        if(isset($data['compress'])&&$data['compress']['exclude_file_size'])
            $exclude_file_size=$data['compress']['exclude_file_size'];
        else
            $exclude_file_size=0;

        if(isset($data['skip_files_time']))
        {
            $skip_files_time=$data['skip_files_time'];
        }
        else
        {
            $skip_files_time=0;
        }
        global $wpvivid_plugin;
        $wpvivid_plugin->wpvivid_log->WriteLog('exclude_files_regex:'.wp_json_encode($exclude_files_regex),'notice');

        foreach ($data['files'] as $file)
        {
            $this->get_file_cache($size,$file,$cache_perfix,$cache_file_handle,$number,$sumsize,$exclude_regex,$exclude_files_regex,$exclude_file_size,$skip_files_time);
        }

        $file_cache=array();

        for($i=1;$i<$number+1;$i++)
        {
            $file_cache[]=$cache_perfix.$i.'.txt';
        }
        return $file_cache;
    }

    public function get_file_cache($size,$path,$cache_perfix,&$cache_file_handle,&$number,&$sumsize,$exclude_regex,$exclude_files_regex,$exclude_file_size,$skip_files_time)
    {
        if(!$cache_file_handle)
        {
            $cache_file=$cache_perfix.$number.'.txt';
            $cache_file_handle=fopen($cache_file,'a');
        }
        $handler = opendir($path);

        if($handler===false)
            return;

        while (($filename = readdir($handler)) !== false)
        {
            if ($filename != "." && $filename != "..")
            {
                if (is_dir($path . DIRECTORY_SEPARATOR . $filename))
                {
                    if ($this->regex_match($exclude_regex, $path . DIRECTORY_SEPARATOR . $filename, 0))
                    {
                        $this->get_file_cache($size,$path . DIRECTORY_SEPARATOR . $filename,$cache_perfix,$cache_file_handle,$number,$sumsize,$exclude_regex,$exclude_files_regex,$exclude_file_size,$skip_files_time);
                    }
                }
                /*if(is_dir($path . DIRECTORY_SEPARATOR . $filename))
                {
                    $this->get_file_cache($size,$path . DIRECTORY_SEPARATOR . $filename,$cache_perfix,$cache_file_handle,$number,$sumsize,$exclude_regex,$exclude_files_regex,$exclude_file_size,$skip_files_time);
                }*/
                else
                {
                    if($this->regex_match($exclude_files_regex, $filename, 0))
                    {
                        if ($exclude_file_size == 0||(filesize($path . DIRECTORY_SEPARATOR . $filename) < $exclude_file_size * 1024 * 1024))
                        {
                            if(is_readable($path . DIRECTORY_SEPARATOR . $filename))
                            {
                                if($skip_files_time>0)
                                {
                                    $file_time=filemtime($path . DIRECTORY_SEPARATOR . $filename);
                                    if($file_time>0&&$file_time>$skip_files_time)
                                    {
                                        $sumsize+=filesize($path . DIRECTORY_SEPARATOR . $filename);
                                        if($sumsize>$size)
                                        {
                                            $number++;
                                            fclose($cache_file_handle);
                                            $cache_file=$cache_perfix.$number.'.txt';
                                            $cache_file_handle=fopen($cache_file,'a');

                                            $line = $path . DIRECTORY_SEPARATOR . $filename.PHP_EOL;
                                            fwrite($cache_file_handle, $line);

                                            $sumsize=filesize($path . DIRECTORY_SEPARATOR . $filename);
                                        }
                                        else
                                        {
                                            $line = $path . DIRECTORY_SEPARATOR . $filename.PHP_EOL;
                                            fwrite($cache_file_handle, $line);
                                        }
                                    }
                                }
                                else
                                {
                                    $sumsize+=filesize($path . DIRECTORY_SEPARATOR . $filename);
                                    if($sumsize>$size)
                                    {
                                        $number++;
                                        fclose($cache_file_handle);
                                        $cache_file=$cache_perfix.$number.'.txt';
                                        $cache_file_handle=fopen($cache_file,'a');

                                        $line = $path . DIRECTORY_SEPARATOR . $filename.PHP_EOL;
                                        fwrite($cache_file_handle, $line);

                                        $sumsize=filesize($path . DIRECTORY_SEPARATOR . $filename);
                                    }
                                    else
                                    {
                                        $line = $path . DIRECTORY_SEPARATOR . $filename.PHP_EOL;
                                        fwrite($cache_file_handle, $line);
                                    }
                                    $files[] = $path . DIRECTORY_SEPARATOR . $filename;
                                }
                            }
                        }
                    }
                }
            }
        }
        if($handler)
            @closedir($handler);
    }

    private function regex_match($regex_array,$string,$mode)
    {
        if(empty($regex_array))
        {
            return true;
        }

        if($mode==0)
        {
            foreach ($regex_array as $regex)
            {
                if(preg_match($regex,$string))
                {
                    return false;
                }
            }

            return true;
        }

        if($mode==1)
        {
            foreach ($regex_array as $regex)
            {
                if(preg_match($regex,$string))
                {
                    return true;
                }
            }

            return false;
        }

        return true;
    }

    public function get_upload_files_from_cache($file)
    {
        $files=array();
        $file = new SplFileObject($file);
        $file->seek(0);

        $file->setFlags( \SplFileObject::SKIP_EMPTY | \SplFileObject::READ_AHEAD );

        while(!$file->eof())
        {
            $src = $file->fgets();

            $src=trim($src,PHP_EOL);

            if(empty($src))
                continue;

            if(!file_exists($src))
            {
                continue;
            }

            $files[]=$src;
        }
        return $files;
    }
}

class WPvivid_PclZip_Class
{
    public function zip($name,$files,$options,$json_info=false)
    {
        global $wpvivid_plugin;

        if(file_exists($name))
            @wp_delete_file($name);

        if (!class_exists('WPvivid_PclZip'))
            include_once WPVIVID_PLUGIN_DIR . '/includes/zip/class-wpvivid-pclzip.php';
        $archive = new WPvivid_PclZip($name);
        if(isset($options['compress']['no_compress']))
        {
            $no_compress=$options['compress']['no_compress'];
        }
        else
        {
            $no_compress=1;
        }

        if(isset($options['compress']['use_temp_file']))
        {
            $use_temp_file=1;
        }
        else
        {
            $use_temp_file=0;
        }

        if(isset($options['compress']['use_temp_size']))
        {
            $use_temp_size=$options['compress']['use_temp_size'];
        }
        else
        {
            $use_temp_size=16;
        }

        if(isset($options['root_path']))
        {
            $replace_path=$options['root_path'];
        }
        else if(isset($options['root_flag']))
        {
            $replace_path=$this->get_root_flag_path($options['root_flag']);
        }
        else
        {
            $replace_path=WP_CONTENT_DIR.DIRECTORY_SEPARATOR.WPvivid_Setting::get_backupdir();
        }

        if($json_info!==false)
        {
            $temp_path = dirname($name).DIRECTORY_SEPARATOR.'wpvivid_package_info.json';
            if(file_exists($temp_path))
            {
                @wp_delete_file($temp_path);
            }
            $json_info['php_version'] = phpversion();
            global $wpdb;
            $json_info['mysql_version'] = $wpdb->db_version();
            file_put_contents($temp_path,print_r(wp_json_encode($json_info),true));
            $archive -> add($temp_path,WPVIVID_PCLZIP_OPT_REMOVE_PATH,dirname($temp_path));
            @wp_delete_file($temp_path);
        }

        $wpvivid_plugin->wpvivid_log->WriteLog('Prepare to zip files. file: '.basename($name),'notice');

        /*foreach ($files as $index => $file){
            if(!is_dir($file) && filesize($file) === 0){
                $wpvivid_plugin->wpvivid_log->WriteLog('Ignore files with size 0. file: '.$file,'notice');
                unset($files[$index]);
            }
        }*/

        if($no_compress)
        {
            if($use_temp_file==1)
            {
                if($use_temp_size!=0)
                {
                    $ret = $archive -> add($files,WPVIVID_PCLZIP_OPT_REMOVE_PATH,$replace_path,WPVIVID_PCLZIP_CB_PRE_ADD,'wpvivid_function_per_add_callback',WPVIVID_PCLZIP_OPT_NO_COMPRESSION,WPVIVID_PCLZIP_OPT_TEMP_FILE_THRESHOLD,$use_temp_size);
                }
                else
                {
                    $ret = $archive -> add($files,WPVIVID_PCLZIP_OPT_REMOVE_PATH,$replace_path,WPVIVID_PCLZIP_CB_PRE_ADD,'wpvivid_function_per_add_callback',WPVIVID_PCLZIP_OPT_NO_COMPRESSION,WPVIVID_PCLZIP_OPT_TEMP_FILE_ON);
                }
            }
            else
            {
                $ret = $archive -> add($files,WPVIVID_PCLZIP_OPT_REMOVE_PATH,$replace_path,WPVIVID_PCLZIP_CB_PRE_ADD,'wpvivid_function_per_add_callback',WPVIVID_PCLZIP_OPT_NO_COMPRESSION,WPVIVID_PCLZIP_OPT_TEMP_FILE_OFF);
            }
        }
        else
        {
            if($use_temp_file==1)
            {
                if($use_temp_size!=0)
                {
                    $ret = $archive -> add($files,WPVIVID_PCLZIP_OPT_REMOVE_PATH,$replace_path,WPVIVID_PCLZIP_CB_PRE_ADD,'wpvivid_function_per_add_callback',WPVIVID_PCLZIP_OPT_TEMP_FILE_THRESHOLD,$use_temp_size);
                }
                else
                {
                    $ret = $archive -> add($files,WPVIVID_PCLZIP_OPT_REMOVE_PATH,$replace_path,WPVIVID_PCLZIP_CB_PRE_ADD,'wpvivid_function_per_add_callback',WPVIVID_PCLZIP_OPT_TEMP_FILE_ON);
                }
            }
            else
            {
                $ret = $archive -> add($files,WPVIVID_PCLZIP_OPT_REMOVE_PATH,$replace_path,WPVIVID_PCLZIP_CB_PRE_ADD,'wpvivid_function_per_add_callback',WPVIVID_PCLZIP_OPT_TEMP_FILE_OFF);
            }
        }

        if(!$ret)
        {
            $wpvivid_plugin->wpvivid_log->WriteLog('Failed to add zip files, error: '.$archive->errorInfo(true),'notice');
            $size=size_format(disk_free_space(dirname($name)),2);
            $wpvivid_plugin->wpvivid_log->WriteLog('disk_free_space : '.$size,'notice');
            return array('result'=>WPVIVID_FAILED,'error'=>$archive->errorInfo(true));
        }

        $size=filesize($name);
        if($size===false)
        {
            $wpvivid_plugin->wpvivid_log->WriteLog('Failed to add zip files, error: file not found after backup success','error');
            $size=size_format(disk_free_space(dirname($name)),2);
            $wpvivid_plugin->wpvivid_log->WriteLog('disk_free_space : '.$size,'notice');
            return array('result'=>WPVIVID_FAILED,'error'=>'The file compression failed while backing up becuase of '.$name.' file not found. Please try again. The available disk space: '.$size.'.');
        }
        else if($size==0)
        {
            $wpvivid_plugin->wpvivid_log->WriteLog('Failed to add zip files, error: file size 0B after backup success','error');
            $size=size_format(disk_free_space(dirname($name)),2);
            $wpvivid_plugin->wpvivid_log->WriteLog('disk_free_space : '.$size,'notice');
            return array('result'=>WPVIVID_FAILED,'error'=>'The file compression failed while backing up. The size of '.$name.' file is 0. Please make sure there is an enough disk space to backup. Then try again. The available disk space: '.$size.'.');
        }

        $wpvivid_plugin->wpvivid_log->WriteLog('Adding zip files completed.'.basename($name).', filesize: '.size_format(filesize($name),2),'notice');
        $file_data = array();
        $file_data['file_name'] = basename($name);
        $file_data['size'] = filesize($name);

        return array('result'=>WPVIVID_SUCCESS,'file_data'=>$file_data);
    }

    public function get_root_flag_path($flag)
    {
        $path='';
        if($flag==WPVIVID_BACKUP_ROOT_WP_CONTENT)
        {
            $path=WP_CONTENT_DIR;
        }
        else if($flag==WPVIVID_BACKUP_ROOT_CUSTOM)
        {
            $path=WP_CONTENT_DIR.DIRECTORY_SEPARATOR.WPvivid_Setting::get_backupdir();
        }
        else if($flag==WPVIVID_BACKUP_ROOT_WP_ROOT)
        {
            $path=ABSPATH;
        }
        return $path;
    }
}

class WPvivid_PclZip_Class_Ex
{
    public function zip($name,$files,$options,$json_info=false)
    {
        global $wpvivid_plugin;

        if(file_exists($name))
            @wp_delete_file($name);

        if (!class_exists('WPvivid_PclZip'))
            include_once WPVIVID_PLUGIN_DIR . '/includes/zip/class-wpvivid-pclzip.php';
        $archive = new WPvivid_PclZip($name);

        if(isset($options['compress']['no_compress']))
        {
            $no_compress=$options['compress']['no_compress'];
        }
        else
        {
            $no_compress=1;
        }

        if(isset($options['compress']['use_temp_file']))
        {
            $use_temp_file=1;
        }
        else
        {
            $use_temp_file=0;
        }

        if(isset($options['compress']['use_temp_size']))
        {
            $use_temp_size=$options['compress']['use_temp_size'];
        }
        else
        {
            $use_temp_size=16;
        }

        if(isset($options['root_path']))
        {
            $replace_path=$options['root_path'];
        }
        else if(isset($options['root_flag']))
        {
            $replace_path=$this->get_root_flag_path($options['root_flag']);
        }
        else
        {
            $replace_path=WP_CONTENT_DIR.DIRECTORY_SEPARATOR.WPvivid_Setting::get_backupdir();
        }

        if($json_info!==false)
        {
            $temp_path = dirname($name).DIRECTORY_SEPARATOR.'wpvivid_package_info.json';
            if(file_exists($temp_path))
            {
                @wp_delete_file($temp_path);
            }
            $json_info['php_version'] = phpversion();
            global $wpdb;
            $json_info['mysql_version'] = $wpdb->db_version();
            file_put_contents($temp_path,print_r(wp_json_encode($json_info),true));
            $archive -> add($temp_path,WPVIVID_PCLZIP_OPT_REMOVE_PATH,dirname($temp_path));
            @wp_delete_file($temp_path);
        }

        $wpvivid_plugin->wpvivid_log->WriteLog('Prepare to zip files. file: '.basename($name),'notice');

        if($no_compress)
        {
            if($use_temp_file==1)
            {
                if($use_temp_size!=0)
                {
                    $ret = $archive -> add($files,WPVIVID_PCLZIP_OPT_REMOVE_PATH,$replace_path,WPVIVID_PCLZIP_CB_PRE_ADD,'wpvivid_function_per_add_callback',WPVIVID_PCLZIP_OPT_NO_COMPRESSION,WPVIVID_PCLZIP_OPT_TEMP_FILE_THRESHOLD,$use_temp_size);
                }
                else
                {
                    $ret = $archive -> add($files,WPVIVID_PCLZIP_OPT_REMOVE_PATH,$replace_path,WPVIVID_PCLZIP_CB_PRE_ADD,'wpvivid_function_per_add_callback',WPVIVID_PCLZIP_OPT_NO_COMPRESSION,WPVIVID_PCLZIP_OPT_TEMP_FILE_ON);
                }
            }
            else
            {
                $ret = $archive -> add($files,WPVIVID_PCLZIP_OPT_REMOVE_PATH,$replace_path,WPVIVID_PCLZIP_CB_PRE_ADD,'wpvivid_function_per_add_callback',WPVIVID_PCLZIP_OPT_NO_COMPRESSION,WPVIVID_PCLZIP_OPT_TEMP_FILE_OFF);
            }
        }
        else
        {
            if($use_temp_file==1)
            {
                if($use_temp_size!=0)
                {
                    $ret = $archive -> add($files,WPVIVID_PCLZIP_OPT_REMOVE_PATH,$replace_path,WPVIVID_PCLZIP_CB_PRE_ADD,'wpvivid_function_per_add_callback',WPVIVID_PCLZIP_OPT_TEMP_FILE_THRESHOLD,$use_temp_size);
                }
                else
                {
                    $ret = $archive -> add($files,WPVIVID_PCLZIP_OPT_REMOVE_PATH,$replace_path,WPVIVID_PCLZIP_CB_PRE_ADD,'wpvivid_function_per_add_callback',WPVIVID_PCLZIP_OPT_TEMP_FILE_ON);
                }
            }
            else
            {
                $ret = $archive -> add($files,WPVIVID_PCLZIP_OPT_REMOVE_PATH,$replace_path,WPVIVID_PCLZIP_CB_PRE_ADD,'wpvivid_function_per_add_callback',WPVIVID_PCLZIP_OPT_TEMP_FILE_OFF);
            }
        }

        if(!$ret)
        {
            $wpvivid_plugin->wpvivid_log->WriteLog('Failed to add zip files, error: '.$archive->errorInfo(true),'notice');
            $size=size_format(disk_free_space(dirname($name)),2);
            $wpvivid_plugin->wpvivid_log->WriteLog('disk_free_space : '.$size,'notice');
            return array('result'=>WPVIVID_FAILED,'error'=>$archive->errorInfo(true));
        }

        $size=filesize($name);
        if($size===false)
        {
            $wpvivid_plugin->wpvivid_log->WriteLog('Failed to add zip files, error: file not found after backup success','error');
            $size=size_format(disk_free_space(dirname($name)),2);
            $wpvivid_plugin->wpvivid_log->WriteLog('disk_free_space : '.$size,'notice');
            return array('result'=>WPVIVID_FAILED,'error'=>'The file compression failed while backing up becuase of '.$name.' file not found. Please try again. The available disk space: '.$size.'.');
        }
        else if($size==0)
        {
            $wpvivid_plugin->wpvivid_log->WriteLog('Failed to add zip files, error: file size 0B after backup success','error');
            $size=size_format(disk_free_space(dirname($name)),2);
            $wpvivid_plugin->wpvivid_log->WriteLog('disk_free_space : '.$size,'notice');
            return array('result'=>WPVIVID_FAILED,'error'=>'The file compression failed while backing up. The size of '.$name.' file is 0. Please make sure there is an enough disk space to backup. Then try again. The available disk space: '.$size.'.');
        }

        $wpvivid_plugin->wpvivid_log->WriteLog('Adding zip files completed.'.basename($name).', filesize: '.size_format(filesize($name),2),'notice');
        $file_data = array();
        $file_data['file_name'] = basename($name);
        $file_data['size'] = filesize($name);

        return array('result'=>WPVIVID_SUCCESS,'file_data'=>$file_data);
    }

    public function get_root_flag_path($flag)
    {
        $path='';
        if($flag==WPVIVID_BACKUP_ROOT_WP_CONTENT)
        {
            $path=WP_CONTENT_DIR;
        }
        else if($flag==WPVIVID_BACKUP_ROOT_CUSTOM)
        {
            $path=WP_CONTENT_DIR.DIRECTORY_SEPARATOR.WPvivid_Setting::get_backupdir();
        }
        else if($flag==WPVIVID_BACKUP_ROOT_WP_ROOT)
        {
            $path=ABSPATH;
        }
        return $path;
    }
}

$wpvivid_old_time=0;

function wpvivid_function_per_add_callback($p_event, &$p_header)
{
    if(!file_exists($p_header['filename'])){
        return 0;
    }
    /*if($p_header['size'] === 0){
        return 0;
    }*/

    $path = str_replace('\\','/',WP_CONTENT_DIR);
    $content_path = $path.'/';
    if(strpos($p_header['filename'], $content_path.'mu-plugins/endurance-browser-cache.php')!==false)
    {
        return 0;
    }

    if(strpos($p_header['filename'], $content_path.'mu-plugins/endurance-page-cache.php')!==false)
    {
        return 0;
    }

    if(strpos($p_header['filename'], $content_path.'mu-plugins/endurance-php-edge.php')!==false)
    {
        return 0;
    }

    $plugins = substr(WP_PLUGIN_DIR, strpos(WP_PLUGIN_DIR, 'wp-content/'));

    if(strpos($p_header['filename'],$plugins.'/wpvivid-backuprestore')!==false)
    {
        return 0;
    }

    if(strpos($p_header['filename'],$plugins.'/wpvivid-backup-pro')!==false)
    {
        return 0;
    }

    global $wpvivid_old_time;
    if(time()-$wpvivid_old_time>30)
    {
        $wpvivid_old_time=time();
        global $wpvivid_plugin;
        if(isset($wpvivid_plugin->current_task['id']))
        {
            $wpvivid_plugin->check_cancel_backup($wpvivid_plugin->current_task['id']);
            WPvivid_taskmanager::update_backup_task_status($wpvivid_plugin->current_task['id']);
        }
    }

    return 1;
}

function wpvivid_function_pre_extract_callback($p_event, &$p_header)
{
    $plugins = substr(WP_PLUGIN_DIR, strpos(WP_PLUGIN_DIR, 'wp-content/'));

    if ( isset( $GLOBALS['wpvivid_extract_option'] ) )
    {
        $option = $GLOBALS['wpvivid_extract_option'];
        if (isset($option['file_type']))
        {
            if ($option['file_type'] == 'themes')
            {
                if (isset($option['remove_themes']))
                {
                    foreach ($option['remove_themes'] as $slug => $themes)
                    {
                        if (empty($slug))
                            continue;
                        if(strpos($p_header['filename'],$plugins.DIRECTORY_SEPARATOR.$slug)!==false)
                        {
                            return 0;
                        }
                    }
                }
            }
            else if ($option['file_type'] == 'plugin')
            {
                if (isset($option['remove_plugin']))
                {
                    foreach ($option['remove_plugin'] as $slug => $plugin)
                    {
                        if (empty($slug))
                            continue;
                        if(strpos($p_header['filename'],$plugins.'/'.$slug)!==false)
                        {
                            return 0;
                        }
                    }
                }
            }
        }
    }

    $path = str_replace('\\','/',WP_CONTENT_DIR);
    $content_path = $path.'/';
    if(strpos($p_header['filename'], $content_path.'advanced-cache.php')!==false)
    {
        return 0;
    }

    if(strpos($p_header['filename'], $content_path.'db.php')!==false)
    {
        return 0;
    }

    if(strpos($p_header['filename'], $content_path.'object-cache.php')!==false)
    {
        return 0;
    }

    if(strpos($p_header['filename'],$plugins.'/wpvivid-backuprestore')!==false)
    {
        return 0;
    }

    if(strpos($p_header['filename'],'wp-config.php')!==false)
    {
        return 0;
    }

    if(strpos($p_header['filename'],'wpvivid_package_info.json')!==false)
    {
        return 0;
    }

    if(strpos($p_header['filename'],'.htaccess')!==false)
    {
        return 0;
    }

    if(strpos($p_header['filename'],'.user.ini')!==false)
    {
        return 0;
    }

    if(strpos($p_header['filename'],'wordfence-waf.php')!==false)
    {
        return 0;
    }

    if(strpos($p_header['filename'], $content_path.'mu-plugins/endurance-browser-cache.php')!==false)
    {
        return 0;
    }

    if(strpos($p_header['filename'], $content_path.'mu-plugins/endurance-page-cache.php')!==false)
    {
        return 0;
    }

    if(strpos($p_header['filename'], $content_path.'mu-plugins/endurance-php-edge.php')!==false)
    {
        return 0;
    }

    if(strpos($p_header['filename'], $content_path.'mu-plugins/wp-stack-cache.php')!==false)
    {
        return 0;
    }

    return 1;
}includes/class-wpvivid-tab-page-container.php000064400000023303151327705670015332 0ustar00<?php
if (!defined('WPVIVID_PLUGIN_DIR'))
{
    die;
}
class WPvivid_Tab_Page_Container
{
    public $tabs;
    public $container_id;
    public $is_parent_tab=1;

    public function __construct( $args = array() )
    {
        $this->tabs=array();
        $this->container_id=uniqid('tab-');
    }

    public function add_tab($title,$slug,$callback,$args=array())
    {
        $new_tab['title']=$title;
        $new_tab['slug']=$slug;
        $new_tab['page']=$callback;
        foreach ($args as $key=>$arg)
        {
            $new_tab[$key]=$arg;
            if($key === 'is_parent_tab') {
                $this->is_parent_tab = $arg;
            }
        }

        $this->tabs[]=$new_tab;
    }

    public function set_tab($tabs)
    {
        foreach ($tabs as $tab)
        {
            $new_tab['title']=$tab['title'];
            $new_tab['slug']=$tab['slug'];
            $new_tab['page']=$tab['page'];
            $this->tabs[]=$new_tab;
        }
    }

    public function display()
    {
        $class = '';
        ?>
        <div id="<?php echo esc_attr($this->container_id)?>">
            <h2 class="nav-tab-wrapper <?php echo esc_attr($class); ?>" style="padding-bottom:0!important;">
                <?php
                $this->display_tabs();
                ?>
            </h2>
            <?php
            if($this->is_parent_tab){
                ?>
                <div style="margin: 10px 0 0 2px;">
                    <div id="poststuff" style="padding-top: 0;">
                        <div id="post-body" class="metabox-holder columns-2">
                            <div id="post-body-content">
                                <div class="inside" style="margin-top:0;">
                                    <div>
                                        <?php
                                        $this->display_page();
                                        ?>
                                    </div>
                                </div>
                            </div>
                            <div id="postbox-container-1" class="postbox-container">
                                <div class="meta-box-sortables">
                                    <?php
                                    if(has_filter('wpvivid_add_side_bar')){
                                        $side_bar = '1';
                                    }
                                    else{
                                        $side_bar = '0';
                                    }
                                    //$side_bar = '';
                                    if(get_current_screen()->id=='wpvivid-backup_page_wpvivid-staging')
                                    {
                                        do_action('wpvivid_add_staging_side_bar_ex' ,$side_bar, false);
                                    }
                                    else if(get_current_screen()->id=='wpvivid-backup_page_wpvivid-snapshot-ex')
                                    {
                                        do_action('wpvivid_snapshot_add_sidebar_free');
                                    }
                                    else
                                    {
                                        do_action('wpvivid_add_side_bar' ,$side_bar, false);
                                    }
                                    //$side_bar = apply_filters('wpvivid_add_side_bar', $side_bar, false);
                                    //echo $side_bar;
                                    ?>
                                </div>
                            </div>
                        </div>
                        <br class="clear">
                    </div>
                </div>
                <?php
            }
            else{
                ?>
                <div>
                    <?php
                    $this->display_page();
                    ?>
                </div>
                <?php
            }
            ?>
        </div>
        <script>
            jQuery('#<?php echo esc_attr($this->container_id)?>').on("click",".<?php echo esc_attr($this->container_id)?>-tab",function()
            {
                jQuery('#<?php echo esc_attr($this->container_id)?>').find( '.<?php echo esc_attr($this->container_id)?>-tab' ).each(function()
                {
                    jQuery(this).removeClass( "nav-tab-active" );
                });

                jQuery('#<?php echo esc_attr($this->container_id)?>').find( '.<?php echo esc_attr($this->container_id)?>-content' ).each(function()
                {
                    jQuery(this).hide();
                });

                var id=jQuery(this).attr('id');
                id= id.substr(12);

                jQuery("#wpvivid_page_"+id).show();
                jQuery(this).addClass( "nav-tab-active" );
            });

            jQuery('#<?php echo esc_attr($this->container_id)?>').on("click",".nav-tab-delete-img",function(event)
            {
                event.stopPropagation();
                var redirect=jQuery(this).attr('redirect');
                jQuery(this).parent().hide();

                jQuery('#<?php echo esc_attr($this->container_id)?>').find( '.<?php echo esc_attr($this->container_id)?>-tab' ).each(function()
                {
                    jQuery(this).removeClass( "nav-tab-active" );
                });

                jQuery('#<?php echo esc_attr($this->container_id)?>').find( '.<?php echo esc_attr($this->container_id)?>-content' ).each(function()
                {
                    jQuery(this).hide();
                });

                jQuery("#wpvivid_page_"+redirect).show();
                jQuery("#wpvivid_tab_"+redirect).addClass( "nav-tab-active" );
                //jQuery(this).addClass( "nav-tab-active" );
            });

            jQuery(document).ready(function($)
            {
                jQuery(document).on('<?php echo esc_attr($this->container_id)?>-show', function(event,id,redirect)
                {
                    jQuery('#<?php echo esc_attr($this->container_id)?>').find( '.<?php echo esc_attr($this->container_id)?>-tab' ).each(function()
                    {
                        jQuery(this).removeClass( "nav-tab-active" );
                    });

                    jQuery('#<?php echo esc_attr($this->container_id)?>').find( '.<?php echo esc_attr($this->container_id)?>-content' ).each(function()
                    {
                        jQuery(this).hide();
                    });

                    jQuery("#wpvivid_page_"+id).show();
                    jQuery("#wpvivid_tab_"+id).show();
                    jQuery("#wpvivid_tab_"+id).find( '.nav-tab-delete-img' ).each(function()
                    {
                        jQuery(this).attr('redirect',redirect);
                    });
                    jQuery("#wpvivid_tab_"+id).addClass( "nav-tab-active" );
                    var top = jQuery("#wpvivid_tab_"+id).offset().top-jQuery("#wpvivid_tab_"+id).height();
                    jQuery('html, body').animate({scrollTop:top}, 'slow');
                });
            });
        </script>
        <?php
    }

    public function display_tabs()
    {
        $first=true;

        foreach ($this->tabs as $tab)
        {
            $class='nav-tab '.$this->container_id.'-tab';
            if($first)
            {
                $class.=' nav-tab-active';
                $first=false;
            }

            $style='cursor:pointer;';

            if(isset($tab['hide']))
            {
                $style.=' display: none';
            }

            if(isset($tab['can_delete']))
            {
                $class.=' delete';
            }
            if(isset($tab['transparency']))
            {
                $class.=' wpvivid-transparency-tab';
            }

            echo '<a id="wpvivid_tab_'.esc_attr($tab['slug']).'" class="'.esc_attr($class).'" style="'.esc_attr($style).'">';

            if(isset($tab['can_delete']))
            {
                echo '<div style="margin-right: 15px;">'.esc_html($tab['title']).'</div>';
                if(isset($tab['redirect']))
                {
                    echo '<div class="nav-tab-delete-img" redirect="'.esc_url($tab['redirect']).'">
                          <img src="'.esc_url( WPVIVID_PLUGIN_URL.'/admin/partials/images/delete-tab.png' ).'" style="vertical-align:middle; cursor:pointer;">
                       </div>';
                }
                else
                {
                    echo '<div class="nav-tab-delete-img">
                          <img src="'.esc_url( WPVIVID_PLUGIN_URL.'/admin/partials/images/delete-tab.png' ).'" style="vertical-align:middle; cursor:pointer;">
                       </div>';
                }
            }
            else
            {
                echo esc_html($tab['title']);
            }
            echo '</a>';
        }
    }

    public function display_page()
    {
        $first=true;
        foreach ($this->tabs as $tab)
        {
            //delete
            $style='display: none;';
            if($first)
            {
                if(isset($tab['hide']))
                {

                }
                else
                {
                    $style='';
                    $first=false;
                }
            }

            $class=$this->container_id.'-content';

            echo '<div id="wpvivid_page_'.esc_attr($tab['slug']).'" class="'.esc_attr($class).'" style="'.esc_attr($style).'">';
            call_user_func($tab['page']);
            echo '</div>';
        }
    }
}includes/class-wpvivid.php000064400001175761151327705670011715 0ustar00<?php

/**
 * The file that defines the core plugin class
 *
 * A class definition that includes attributes and functions used across both the
 * public-facing side of the site and the admin area.
 *
 * @link       https://wpvivid.com
 * @since      0.9.1
 *
 * @package    wpvivid
 * @subpackage wpvivid/includes
 */

/**
 * The core plugin class.
 *
 * This is used to define internationalization, admin-specific hooks, and
 * public-facing site hooks.
 *
 * Also maintains the unique identifier of this plugin as well as the current
 * version of the plugin.
 *
 * @since      0.9.1
 * @package    wpvivid
 * @subpackage wpvivid/includes
 * @author     wpvivid team
 */
if (!defined('WPVIVID_PLUGIN_DIR')){
    die;
}

class WPvivid
{

	protected $plugin_name;

	protected $version;

    public $wpvivid_log;
    public $wpvivid_download_log;

    public $current_task;

    public $updater;

    public $remote_collection;

    public $function_realize;

    public $end_shutdown_function;

    public $restore_data;

    public $migrate;
    public $backup_uploader;

    public $admin;

    public $interface_mainwp;

    public $staging;

    public $backup2;

	public function __construct()
    {
        $this->version = WPVIVID_PLUGIN_VERSION;

		$this->plugin_name = WPVIVID_PLUGIN_SLUG;

		$this->end_shutdown_function = false;

		$this->restore_data=false;
		$this->remote_collection=false;
        add_action('plugins_loaded', array($this, 'load_remote_storage'),10);

        include_once WPVIVID_PLUGIN_DIR . '/includes/class-wpvivid-setting.php';
        include_once WPVIVID_PLUGIN_DIR . '/includes/class-wpvivid-tools.php';
        include_once WPVIVID_PLUGIN_DIR . '/includes/class-wpvivid-schedule.php';
        include_once WPVIVID_PLUGIN_DIR . '/includes/class-wpvivid-taskmanager.php';
        include_once WPVIVID_PLUGIN_DIR . '/includes/class-wpvivid-backuplist.php';
        include_once WPVIVID_PLUGIN_DIR . '/includes/staging/class-wpvivid-staging.php';
        include_once WPVIVID_PLUGIN_DIR . '/includes/class-wpvivid-public-interface.php';
        include_once WPVIVID_PLUGIN_DIR . '/includes/class-wpvivid-crypt.php';

        if(!$this->wpvivid_check_staging_pro_active())
        {
            $this->staging=new WPvivid_Staging_Free();
        }

        $this->init_cron();

        include_once WPVIVID_PLUGIN_DIR . '/includes/new_backup/class-wpvivid-backup2.php';
        $this->backup2=new WPvivid_Backup_2();

        include_once WPVIVID_PLUGIN_DIR . '/includes/new_backup/class-wpvivid-restore2.php';
        new WPvivid_Restore_2();

        include_once WPVIVID_PLUGIN_DIR . '/includes/customclass/class-wpvivid-send-to-site.php';
        new WPvivid_Send_to_site();

        include_once WPVIVID_PLUGIN_DIR . '/includes/class-wpvivid-log.php';
        include_once WPVIVID_PLUGIN_DIR . '/includes/class-wpvivid-error-log.php';
        include_once WPVIVID_PLUGIN_DIR . '/includes/class-wpvivid-zipclass.php';
        include_once WPVIVID_PLUGIN_DIR . '/includes/class-wpvivid-backup.php';
        include_once WPVIVID_PLUGIN_DIR . '/includes/class-wpvivid-upload.php';

        $this->wpvivid_log=new WPvivid_Log();
        $this->wpvivid_download_log=new WPvivid_Log();

        add_action('init', array($this, 'init_pclzip_tmp_folder'));

		if(is_admin())
        {
            $this->load_admin();
            $this->set_locale();
        }

        add_filter('wpvivid_get_oldest_backup_ids', array($this, 'get_oldest_backup_ids'), 10, 2);
        add_filter('wpvivid_check_backup_completeness', array($this, 'check_backup_completeness'), 10, 2);

		/*
        include_once WPVIVID_PLUGIN_DIR . '/includes/class-wpvivid-log.php';
        include_once WPVIVID_PLUGIN_DIR . '/includes/class-wpvivid-error-log.php';

        $this->load_dependencies();

        $this->wpvivid_log=new WPvivid_Log();
        $this->wpvivid_download_log=new WPvivid_Log();
		//Load dependent files


        //A flag to determine whether plugin had been initialized
		$init=get_option('wpvivid_init', 'not init');
		if($init=='not init')
        {
            //Initialization settings
            WPvivid_Setting::init_option();
            WPvivid_Setting::update_option('wpvivid_init','init');
        }

        $wpvivid_remote_init=get_option('wpvivid_remote_init', 'not init');
		if($wpvivid_remote_init=='not init'){
            $this->init_remote_option();
            WPvivid_Setting::update_option('wpvivid_remote_init','init');
        }
		*/

	}

	public function load_admin()
    {
        require_once WPVIVID_PLUGIN_DIR . '/admin/class-wpvivid-admin.php';
        include_once WPVIVID_PLUGIN_DIR . '/includes/class-wpvivid-tab-page-container.php' ;
        include_once WPVIVID_PLUGIN_DIR . '/includes/class-wpvivid-downloader.php';
        include_once WPVIVID_PLUGIN_DIR . '/includes/class-wpvivid-mail-report.php';
        include_once WPVIVID_PLUGIN_DIR . '/includes/class-wpvivid-function-realize.php';
        include_once WPVIVID_PLUGIN_DIR . '/includes/class-wpvivid-backup-uploader.php';
        include_once WPVIVID_PLUGIN_DIR . '/includes/class-wpvivid-migrate.php';
        include_once WPVIVID_PLUGIN_DIR . '/includes/class-wpvivid-db-method.php';
        include_once WPVIVID_PLUGIN_DIR . '/includes/class-wpvivid-additional-db-method.php';
        include_once WPVIVID_PLUGIN_DIR . '/includes/class-wpvivid-export-import.php';
        include_once WPVIVID_PLUGIN_DIR . '/includes/class-wpvivid-exporter.php';
        include_once WPVIVID_PLUGIN_DIR . '/includes/class-wpvivid-importer.php';
        include_once WPVIVID_PLUGIN_DIR . '/includes/class-wpvivid-interface-mainwp.php';
        include_once WPVIVID_PLUGIN_DIR . '/includes/upload-cleaner/class-wpvivid-uploads-cleaner.php';
        include_once WPVIVID_PLUGIN_DIR . '/includes/snapshot/class-wpvivid-snapshot.php';
        include_once WPVIVID_PLUGIN_DIR . '/includes/class-wpvivid-restore-data.php';

        $this->function_realize=new WPvivid_Function_Realize();
        $this->migrate=new WPvivid_Migrate();
        $this->backup_uploader=new Wpvivid_BackupUploader();
        $this->interface_mainwp = new WPvivid_Interface_MainWP();

        $export_import = new WPvivid_Export_Import();
        $cleaner=new WPvivid_Uploads_Cleaner();

        new WPvivid_Snapshot_Ex();

        $this->admin = new WPvivid_Admin($this->get_plugin_name(), $this->get_version());
        add_filter('wpvivid_pre_add_remote',array($this, 'pre_add_remote'),10,2);
        add_filter('wpvivid_get_log_list', array( $this, 'wpvivid_get_log_list' ), 10);
        add_action('wpvivid_get_log_list_output', array( $this, 'wpvivid_get_log_list_output' ));
        //add_action('plugins_loaded', array($this, 'load_remote_storage'),10);

        $this->load_ajax_hook_for_admin();

        add_filter('wpvivid_add_backup_list', array( $this, 'wpvivid_add_backup_list' ), 10, 3);
        add_action('wpvivid_add_backup_list_output', array( $this, 'wpvivid_add_backup_list_output' ), 10);
        add_filter('wpvivid_add_remote_storage_list', array( $this, 'wpvivid_add_remote_storage_list' ), 10);
        add_filter('wpvivid_schedule_add_remote_pic', array( $this, 'wpvivid_schedule_add_remote_pic' ), 10);
        add_filter('wpvivid_get_remote_directory', array( $this, 'wpvivid_get_remote_directory' ), 10);

        add_filter('wpvivid_get_last_backup_message', array( $this, 'wpvivid_get_last_backup_message' ), 10);
        add_action('wpvivid_get_last_backup_message_output', array( $this, 'wpvivid_get_last_backup_message_output' ), 10);
        add_filter('wpvivid_schedule_local_remote', array( $this, 'wpvivid_schedule_local_remote' ), 10);
        add_filter('wpvivid_remote_storage', array( $this, 'wpvivid_remote_storage'), 10);
        add_filter('wpvivid_add_remote_notice', array($this, 'wpvivid_add_remote_notice'), 10, 2);
        add_filter('wpvivid_set_general_setting', array($this, 'wpvivid_set_general_setting'), 10, 3);

        add_action('wpvivid_handle_backup_succeed',array($this,'wpvivid_handle_backup_succeed'),10);
        add_action('wpvivid_handle_upload_succeed',array($this,'wpvivid_handle_backup_succeed'),10);

        add_action('wpvivid_handle_upload_succeed',array($this,'wpvivid_mark_task'),20);
        add_action('wpvivid_handle_backup_succeed',array($this,'wpvivid_mark_task'),20);

        add_action('wpvivid_handle_backup_failed',array($this,'wpvivid_handle_backup_failed'),9, 2);

        add_action('wpvivid_handle_upload_succeed',array($this,'wpvivid_deal_upload_succeed'),9);

        add_action('wpvivid_handle_backup_failed',array($this,'wpvivid_mark_task'),20);

        add_action('wpvivid_before_setup_page',array($this,'clean_cache'));
        add_filter('wpvivid_check_type_database', array($this, 'wpvivid_check_type_database'), 10, 2);
        add_filter('wpvivid_set_mail_subject', array($this, 'set_mail_subject'), 10, 2);
        add_filter('wpvivid_set_mail_body', array($this, 'set_mail_body'), 10, 2);

        add_filter('wpvivid_get_mainwp_sync_data', array($this, 'get_mainwp_sync_data'), 10);
        //
        add_filter('wpvivid_get_zip_object_class_ex',array($this, 'get_zip_object_class'));
    }

	public function init_cron()
    {
        $schedule=new WPvivid_Schedule();
        //add_action(WPVIVID_MAIN_SCHEDULE_EVENT,array( $this,'main_schedule'));
        add_action(WPVIVID_RESUME_SCHEDULE_EVENT,array( $this,'resume_schedule'));
        add_action(WPVIVID_CLEAN_BACKING_UP_DATA_EVENT,array($this,'clean_backing_up_data_event'));
        add_action(WPVIVID_CLEAN_BACKUP_RECORD_EVENT,array($this,'clean_backup_record_event'));
        //add_clean_event
        add_action(WPVIVID_TASK_MONITOR_EVENT,array( $this,'task_monitor'));
        add_filter('cron_schedules',array( $schedule,'wpvivid_cron_schedules'),99);
        add_filter('wpvivid_schedule_time', array($schedule, 'output'));
    }

	private function load_dependencies()
    {

        include_once WPVIVID_PLUGIN_DIR . '/includes/class-wpvivid-taskmanager.php';

        include_once WPVIVID_PLUGIN_DIR . '/includes/class-wpvivid-downloader.php';

        include_once WPVIVID_PLUGIN_DIR . '/includes/class-wpvivid-mail-report.php';


        include_once WPVIVID_PLUGIN_DIR . '/includes/class-wpvivid-function-realize.php';
        include_once WPVIVID_PLUGIN_DIR . '/includes/class-wpvivid-upload.php';

        include_once WPVIVID_PLUGIN_DIR . '/includes/class-wpvivid-backup-uploader.php';
        include_once WPVIVID_PLUGIN_DIR . '/includes/class-wpvivid-crypt.php';
        include_once WPVIVID_PLUGIN_DIR . '/includes/class-wpvivid-migrate.php';

        include_once WPVIVID_PLUGIN_DIR . '/includes/class-wpvivid-db-method.php';

        include_once WPVIVID_PLUGIN_DIR . '/includes/class-wpvivid-public-interface.php';

        include_once WPVIVID_PLUGIN_DIR . '/includes/class-wpvivid-additional-db-method.php';

        include_once WPVIVID_PLUGIN_DIR . '/includes/class-wpvivid-export-import.php';
        include_once WPVIVID_PLUGIN_DIR . '/includes/class-wpvivid-exporter.php';
        include_once WPVIVID_PLUGIN_DIR . '/includes/class-wpvivid-importer.php';

        include_once WPVIVID_PLUGIN_DIR . '/includes/class-wpvivid-interface-mainwp.php';

        include_once WPVIVID_PLUGIN_DIR . '/includes/upload-cleaner/class-wpvivid-uploads-cleaner.php';
        include_once WPVIVID_PLUGIN_DIR . '/includes/staging/class-wpvivid-staging.php';

        include_once WPVIVID_PLUGIN_DIR . '/includes/snapshot/class-wpvivid-snapshot.php';

        $this->function_realize=new WPvivid_Function_Realize();
        $this->migrate=new WPvivid_Migrate();
        $this->backup_uploader=new Wpvivid_BackupUploader();
        $this->interface_mainwp = new WPvivid_Interface_MainWP();
        $send_to_site=new WPvivid_Send_to_site();
        $export_import = new WPvivid_Export_Import();
        $cleaner=new WPvivid_Uploads_Cleaner();

        if(!$this->wpvivid_check_staging_pro_active())
        {
            $this->staging=new WPvivid_Staging_Free();
        }

        new WPvivid_Snapshot_Ex();

        include_once WPVIVID_PLUGIN_DIR . '/includes/new_backup/class-wpvivid-backup2.php';
        $this->backup2=new WPvivid_Backup_2();

        include_once WPVIVID_PLUGIN_DIR . '/includes/new_backup/class-wpvivid-restore2.php';
        new WPvivid_Restore_2();
	}

	public function wpvivid_check_staging_pro_active()
    {
        if ( ! function_exists( 'is_plugin_active' ) )
        {
            include_once(ABSPATH.'wp-admin/includes/plugin.php');
        }
        if(is_plugin_active('wpvivid-staging/wpvivid-staging.php'))
        {
            return true;
        }
        else
        {
            return false;
        }
    }

	public function init_pclzip_tmp_folder()
    {
        if (!defined('PCLZIP_TEMPORARY_DIR')) {
            $backupdir=WPvivid_Setting::get_backupdir();
            define( 'PCLZIP_TEMPORARY_DIR', WP_CONTENT_DIR.DIRECTORY_SEPARATOR.$backupdir.DIRECTORY_SEPARATOR );
        }
    }

    public function load_remote_storage()
    {
        include_once WPVIVID_PLUGIN_DIR . '/includes/class-wpvivid-remote-collection.php';
        $this->remote_collection=new WPvivid_Remote_collection();
    }

	private function set_locale()
    {
        require_once WPVIVID_PLUGIN_DIR . '/includes/class-wpvivid-i18n.php';
		$plugin_i18n = new WPvivid_i18n();
        add_action('init',array( $plugin_i18n,'load_plugin_textdomain'));
	}

    public function pre_add_remote($remote,$id)
    {
        unset($remote['default']);
        return $remote;
    }

    public function wpjam_pre_update_option_cache($value, $option)
    {
        wp_cache_delete('notoptions', 'options');
        wp_cache_delete('alloptions', 'options');
        wp_cache_delete($option, 'options');
        return $value;
    }

    public function load_ajax_hook_for_admin()
    {
        //Add remote storage
        add_action('wp_ajax_wpvivid_add_remote',array( $this,'add_remote'));
        //Delete remote storage
        add_action('wp_ajax_wpvivid_delete_remote',array( $this,'delete_remote'));
        //Retrieve remote storage
        add_action('wp_ajax_wpvivid_retrieve_remote',array( $this,'retrieve_remote'));
        //Edit remote storage
        add_action('wp_ajax_wpvivid_edit_remote',array( $this,'edit_remote'));
        //List exist remote
        add_action('wp_ajax_wpvivid_list_remote',array( $this,'list_remote'));
        //Test remote connection
        add_action('wp_ajax_wpvivid_test_remote_connection',array( $this,'test_remote_connection'));
        //Start backup
        add_action('wp_ajax_wpvivid_prepare_backup',array( $this,'prepare_backup'));
        add_action('wp_ajax_wpvivid_delete_ready_task',array($this,'delete_ready_task'));
        add_action('wp_ajax_wpvivid_backup_now',array( $this,'backup_now'));
        //Cancel backup
        add_action('wp_ajax_wpvivid_backup_cancel',array( $this,'backup_cancel'));
        //List backup record
        add_action('wp_ajax_wpvivid_get_backup_list',array( $this,'get_backup_list'));
        //View backup record log file
        add_action('wp_ajax_wpvivid_view_backup_log',array( $this,'view_backup_log'));
        //View log file of the backup task
        add_action('wp_ajax_wpvivid_view_backup_task_log',array( $this,'view_backup_task_log'));
        //List all logs
        add_action('wp_ajax_wpvivid_get_log_list',array( $this,'get_log_list'));
        //View logs
        add_action('wp_ajax_wpvivid_view_log',array( $this,'view_log'));
        //Prepare download backup files
        add_action('wp_ajax_wpvivid_prepare_download_backup',array( $this,'prepare_download_backup'));
        //Get download progress
        add_action('wp_ajax_wpvivid_get_download_progress',array($this,'get_download_progress'));
        //Download backup from site
        add_action('wp_ajax_wpvivid_download_backup',array( $this,'download_backup'));
        //Delete backup record
        add_action('wp_ajax_wpvivid_delete_backup',array( $this,'delete_backup'));
        //Delete backup records
        add_action('wp_ajax_wpvivid_delete_backup_array',array( $this,'delete_backup_array'));
        //
        add_action('wp_ajax_wpvivid_init_download_page',array( $this,'init_download_page'));
        //Download backuplist change page
        add_action('wp_ajax_wpvivid_get_download_page_ex',array($this,'get_download_page_ex'));
        //Set security lock for backup record
        add_action('wp_ajax_wpvivid_set_security_lock',array( $this,'set_security_lock'));
        //Delete task
        add_action('wp_ajax_wpvivid_delete_task',array( $this,'delete_task'));
        //Get backup schedule data
        add_action('wp_ajax_wpvivid_get_schedule',array( $this,'get_schedule'));
        //Get last backup information
        add_action('wp_ajax_wpvivid_get_last_backup',array( $this,'get_last_backup'));
        //Get settings
        add_action('wp_ajax_wpvivid_get_setting',array( $this,'get_setting'));
        add_action('wp_ajax_wpvivid_get_general_setting',array( $this,'get_general_setting'));
        //Update settings
        add_action('wp_ajax_wpvivid_update_setting',array( $this,'update_setting'));
        add_action('wp_ajax_wpvivid_set_general_setting',array( $this,'set_general_setting'));
        add_action('wp_ajax_wpvivid_set_schedule',array( $this,'set_schedule' ));
        //Export settings
        add_action('wp_ajax_wpvivid_export_setting',array( $this,'export_setting'));
        //Import settings
        add_action('wp_ajax_wpvivid_import_setting',array( $this,'import_setting'));
        //Send test mail
        add_action('wp_ajax_wpvivid_test_send_mail',array( $this,'test_send_mail'));
        //Send debug mail
        add_action('wp_ajax_wpvivid_create_debug_package',array( $this,'create_debug_package'));
        //Get backup local storage path
        add_action('wp_ajax_wpvivid_get_dir',array( $this,'get_dir'));
        //Get Web-server disk space in use
        add_action('wp_ajax_wpvivid_junk_files_info',array( $this,'junk_files_info'));
        add_action('wp_ajax_wpvivid_clean_local_storage',array( $this,'clean_local_storage'));
        add_action('wp_ajax_wpvivid_get_out_of_date_info',array($this,'get_out_of_date_info'));
        add_action('wp_ajax_wpvivid_clean_out_of_date_backup',array($this,'clean_out_of_date_backup'));
        //Prepare backup files for restore
        add_action('wp_ajax_wpvivid_prepare_restore',array( $this,'prepare_restore'));
        //Download backup files for restore
        add_action('wp_ajax_wpvivid_download_restore',array( $this,'download_restore_file'));
        //
        add_action('wp_ajax_wpvivid_init_restore_page',array( $this,'init_restore_page'));
        //
        add_action('wp_ajax_wpvivid_delete_last_restore_data',array( $this,'delete_last_restore_data'));
        //
        //start restore
        add_action('wp_ajax_wpvivid_restore',array( $this,'restore'));
        add_action('wp_ajax_wpvivid_get_restore_progress',array( $this,'get_restore_progress'));
        add_action('wp_ajax_wpvivid_get_download_restore_progress',array( $this,'download_restore_progress'));
        add_action('wp_ajax_wpvivid_check_download_has_zero_date', array( $this, 'download_check_has_zero_date' ));
        //When restoring the database use wp_ajax_nopriv_
        add_action('wp_ajax_nopriv_wpvivid_restore',array( $this,'restore'));
        add_action('wp_ajax_nopriv_wpvivid_get_restore_progress',array( $this,'get_restore_progress'));
        add_action('wp_ajax_wpvivid_list_tasks',array( $this,'list_tasks'));
        //View last backup record log
        add_action('wp_ajax_wpvivid_read_last_backup_log',array( $this,'read_last_backup_log'));
        //Set default remote storage when backing up
        add_action('wp_ajax_wpvivid_set_default_remote_storage',array( $this,'set_default_remote_storage'));
        //Get default remote storage when backing up
        add_action('wp_ajax_wpvivid_get_default_remote_storage',array( $this,'get_default_remote_storage'));
        add_action('wp_ajax_wpvivid_need_review',array( $this,'need_review'));
        add_action('wp_ajax_wpvivid_send_debug_info',array($this,'wpvivid_send_debug_info'));
        add_action('wp_ajax_wpvivid_get_ini_memory_limit',array($this,'get_ini_memory_limit'));
        add_action('wp_ajax_wpvivid_get_restore_file_is_migrate', array($this, 'get_restore_file_is_migrate'));

        add_action('wp_ajax_wpvivid_check_remote_alias_exist', array($this, 'check_remote_alias_exist'));
        add_action('wp_ajax_wpvivid_task_monitor', array($this, 'task_monitor_ex'));
        add_action('wp_ajax_wpvivid_amazons3_notice', array($this, 'amazons3_notice'));

        add_action('wp_ajax_wpvivid_hide_mainwp_tab_page', array($this, 'hide_mainwp_tab_page'));
        add_action('wp_ajax_wpvivid_hide_wp_cron_notice', array($this, 'hide_wp_cron_notice'));
        //wpvivid_task_monitor

        //download backup by mainwp
        add_action('wp_ajax_wpvivid_download_backup_mainwp', array($this, 'download_backup_mainwp'));
    }

	public function get_plugin_name()
    {
		return $this->plugin_name;
	}

	public function get_version()
    {
        return $this->version;
    }

    /**
     * Prepare backup include what you want to backup,where you want to store.
     *
     *When prepare backup finished,you can use backup_now start a backup task.
     *
     * @since 0.9.1
     */
    public function prepare_backup()
    {
        check_ajax_referer( 'wpvivid_ajax', 'nonce' );
        $check=current_user_can('manage_options');
        $check=apply_filters('wpvivid_ajax_check_security',$check);
        if(!$check)
        {
            die();
        }
        $this->end_shutdown_function=false;
        register_shutdown_function(array($this,'deal_prepare_shutdown_error'));
        try
        {
            if(isset($_POST['backup'])&&!empty($_POST['backup']))
            {
                $json = sanitize_text_field($_POST['backup']);
                $json = stripslashes($json);
                $backup_options = json_decode($json, true);
                if (is_null($backup_options))
                {
                    $this->end_shutdown_function=true;
                    die();
                }

                $backup_options = apply_filters('wpvivid_custom_backup_options', $backup_options);

                if(!isset($backup_options['type']))
                {
                    $backup_options['type']='Manual';
                    $backup_options['action']='backup';
                }

                $ret = $this->check_backup_option($backup_options, $backup_options['type']);
                if($ret['result']!=WPVIVID_SUCCESS)
                {
                    $this->end_shutdown_function=true;
                    echo wp_json_encode($ret);
                    die();
                }

                $ret=$this->pre_backup($backup_options);
                if($ret['result']=='success')
                {
                    //Check the website data to be backed up
                    /*
                    $ret['check']=$this->check_backup($ret['task_id'],$backup_options);
                    if(isset($ret['check']['result']) && $ret['check']['result'] == WPVIVID_FAILED)
                    {
                        $this->end_shutdown_function=true;
                        echo wp_json_encode(array('result' => WPVIVID_FAILED,'error' => $ret['check']['error']));
                        die();
                    }*/

                    $html = '';
                    $html = apply_filters('wpvivid_add_backup_list', $html);
                    $ret['html'] = $html;
                }
                $this->end_shutdown_function=true;
                echo wp_json_encode($ret);
                die();
            }
        }
        catch (Exception $error)
        {
            $this->end_shutdown_function=true;
            $ret['result']='failed';
            $message = 'An exception has occurred. class:'.get_class($error).';msg:'.$error->getMessage().';code:'.$error->getCode().';line:'.$error->getLine().';in_file:'.$error->getFile().';';
            $ret['error'] = $message;
            $id=uniqid('wpvivid-');
            $log_file_name=$id.'_backup';

            if(!class_exists('WPvivid_Log'))
            {
                include_once WPVIVID_PLUGIN_DIR . '/includes/class-wpvivid-log.php';
            }
            $log=new WPvivid_Log();
            $log->CreateLogFile($log_file_name,'no_folder','backup');
            $log->WriteLog($message,'notice');
            $log->CloseFile();
            WPvivid_error_log::create_error_log($log->log_file);
            error_log($message);
            echo wp_json_encode($ret);
            die();
        }
    }

    public function deal_prepare_shutdown_error()
    {
        if($this->end_shutdown_function==false)
        {
            $last_error = error_get_last();
            if (!empty($last_error) && !in_array($last_error['type'], array(E_NOTICE,E_WARNING,E_USER_NOTICE,E_USER_WARNING,E_DEPRECATED), true)) {
                $error = $last_error;
            } else {
                $error = false;
            }
            $ret['result'] = 'failed';
            if ($error === false) {
                $ret['error'] = 'unknown Error';
            } else {
                $ret['error'] = 'type: '. $error['type'] . ', ' . $error['message'] . ' file:' . $error['file'] . ' line:' . $error['line'];
                error_log($ret['error']);
            }
            $id = uniqid('wpvivid-');
            $log_file_name = $id . '_backup';
            if(!class_exists('WPvivid_Log'))
            {
                include_once WPVIVID_PLUGIN_DIR . '/includes/class-wpvivid-log.php';
            }
            $log = new WPvivid_Log();
            $log->CreateLogFile($log_file_name, 'no_folder', 'backup');
            $log->WriteLog($ret['error'], 'notice');
            $log->CloseFile();
            WPvivid_error_log::create_error_log($log->log_file);
            echo wp_json_encode($ret);
            die();
        }
    }

    public function check_backup_option($data, $backup_method = 'Manual')
    {
        $ret['result']=WPVIVID_SUCCESS;
        add_filter('wpvivid_check_backup_options_valid',array($this, 'check_backup_options_valid'),10,3);
        $ret=apply_filters('wpvivid_check_backup_options_valid',$ret,$data,$backup_method);
        return $ret;
    }

    public function check_backup_options_valid($ret,$data,$backup_method)
    {
        $ret['result']=WPVIVID_FAILED;
        if(!isset($data['backup_files']))
        {
            $ret['error']=__('A backup type is required.', 'wpvivid-backuprestore');
            return $ret;
        }

        $data['backup_files']=sanitize_text_field($data['backup_files']);

        if(empty($data['backup_files']))
        {
            $ret['error']=__('A backup type is required.', 'wpvivid-backuprestore');
            return $ret;
        }

        if(!isset($data['local']) && !isset($data['remote']))
        {
            $ret['error']=__('Choose at least one storage location for backups.', 'wpvivid-backuprestore');
            return $ret;
        }

        $data['local']=sanitize_text_field($data['local']);
        $data['remote']=sanitize_text_field($data['remote']);

        if(empty($data['local']) && empty($data['remote']))
        {
            $ret['error']=__('Choose at least one storage location for backups.', 'wpvivid-backuprestore');
            return $ret;
        }

        if($backup_method == 'Manual')
        {
            if ($data['remote'] === '1')
            {
                $remote_storage = WPvivid_Setting::get_remote_options();
                if ($remote_storage == false)
                {
                    $ret['error'] = __('There is no default remote storage configured. Please set it up first.', 'wpvivid-backuprestore');
                    return $ret;
                }
            }
        }
        $ret['result']=WPVIVID_SUCCESS;
        return $ret;
    }

    /**
     * Delete tasks had [ready] status.
     *
     *When prepare backup go wrong,may retain some task we don't need.Delete them.
     *
     * @since 0.9.3
     */
    public function delete_ready_task()
    {
        check_ajax_referer( 'wpvivid_ajax', 'nonce' );
        $check=current_user_can('manage_options');
        $check=apply_filters('wpvivid_ajax_check_security',$check);
        if(!$check)
        {
            die();
        }
        try {
            WPvivid_taskmanager::delete_ready_task();
            $ret['result'] = 'success';
        }
        catch (Exception $error) {
            $message = 'An exception has occurred. class: '.get_class($error).';msg: '.$error->getMessage().';code: '.$error->getCode().';line: '.$error->getLine().';in_file: '.$error->getFile().';';
            error_log($message);
            echo wp_json_encode(array('result'=>'failed','error'=>$message));
            die();
        }
        echo wp_json_encode($ret);
        die();
    }
    /**
     * Start a backup task init by prepare_backup.
     *
     * @since 0.9.1
     */
    public function backup_now()
    {
        check_ajax_referer( 'wpvivid_ajax', 'nonce' );
        $check=current_user_can('manage_options');
        $check=apply_filters('wpvivid_ajax_check_security',$check);
        if(!$check)
        {
            die();
        }
        try {
            if (!isset($_POST['task_id']) || empty($_POST['task_id']) || !is_string($_POST['task_id'])) {
                $ret['result'] = 'failed';
                $ret['error'] = __('Error occurred while parsing the request data. Please try to run backup again.', 'wpvivid-backuprestore');
                echo wp_json_encode($ret);
                die();
            }
            $task_id = sanitize_key($_POST['task_id']);

            //Start backup site
            if (WPvivid_taskmanager::is_tasks_backup_running()) {
                $ret['result'] = 'failed';
                $ret['error'] = __('A task is already running. Please wait until the running task is complete, and try again.', 'wpvivid-backuprestore');
                echo wp_json_encode($ret);
                die();
            }
            //flush buffer
            $this->flush($task_id);

            $task_msg = WPvivid_taskmanager::get_task($task_id);
            $this->update_last_backup_time($task_msg);

            $this->backup($task_id);
        }
        catch (Exception $error) {
            $message = 'An exception has occurred. class: '.get_class($error).';msg: '.$error->getMessage().';code: '.$error->getCode().';line: '.$error->getLine().';in_file: '.$error->getFile().';';
            error_log($message);
            echo wp_json_encode(array('result'=>'failed','error'=>$message));
            die();
        }
        die();
    }
    /**
     * View backup record logs.
     *
     * @since 0.9.1
     */
    public function view_backup_log()
    {
        check_ajax_referer( 'wpvivid_ajax', 'nonce' );
        $check=current_user_can('manage_options');
        $check=apply_filters('wpvivid_ajax_check_security',$check);
        if(!$check)
        {
            die();
        }
        try
        {
            if (isset($_POST['id']) && !empty($_POST['id']) && is_string($_POST['id']))
            {
                $backup_id = sanitize_key($_POST['id']);
                $backup = WPvivid_Backuplist::get_backup_by_id($backup_id);
                if (!$backup)
                {
                    $json['result'] = 'failed';
                    $json['error'] = __('Retrieving the backup information failed while showing log. Please try again later.', 'wpvivid-backuprestore');
                    echo wp_json_encode($json);
                    die();
                }

                if (!file_exists($backup['log']))
                {
                    $json['result'] = 'failed';
                    $json['error'] = __('The log not found.', 'wpvivid-backuprestore');
                    echo wp_json_encode($json);
                    die();
                }

                $file = fopen($backup['log'], 'r');

                if (!$file)
                {
                    $json['result'] = 'failed';
                    $json['error'] = __('Unable to open the log file.', 'wpvivid-backuprestore');
                    echo wp_json_encode($json);
                    die();
                }

                $buffer = '';
                while (!feof($file))
                {
                    $buffer .= fread($file, 1024);
                }
                fclose($file);

                $json['result'] = 'success';
                $json['data'] = $buffer;
                echo wp_json_encode($json);
            } else {
                $json['result'] = 'failed';
                $json['error'] = __('Reading the log failed. Please try again.', 'wpvivid-backuprestore');
                echo wp_json_encode($json);
            }
        }
        catch (Exception $error)
        {
            $message = 'An exception has occurred. class: '.get_class($error).';msg: '.$error->getMessage().';code: '.$error->getCode().';line: '.$error->getLine().';in_file: '.$error->getFile().';';
            error_log($message);
            echo wp_json_encode(array('result'=>'failed','error'=>$message));
            die();
        }
        die();
    }
    /**
     * View last backup record logs.
     *
     * @since 0.9.1
     */
    public function read_last_backup_log()
    {
        check_ajax_referer( 'wpvivid_ajax', 'nonce' );
        $check=current_user_can('manage_options');
        $check=apply_filters('wpvivid_ajax_check_security',$check);
        if(!$check)
        {
            die();
        }
        try {
            if (!isset($_POST['log_file_name']) || empty($_POST['log_file_name']) || !is_string($_POST['log_file_name'])) {
                $json['result'] = 'failed';
                $json['error'] = __('Reading the log failed. Please try again.', 'wpvivid-backuprestore');
                echo wp_json_encode($json);
                die();
            }
            $option = sanitize_text_field($_POST['log_file_name']);
            $log_file_name = $this->wpvivid_log->GetSaveLogFolder() . $option . '_log.txt';

            if (!file_exists($log_file_name)) {
                $json['result'] = 'failed';
                $json['error'] = __('The log not found.', 'wpvivid-backuprestore');
                echo wp_json_encode($json);
                die();
            }

            $file = fopen($log_file_name, 'r');

            if (!$file) {
                $json['result'] = 'failed';
                $json['error'] = __('Unable to open the log file.', 'wpvivid-backuprestore');
                echo wp_json_encode($json);
                die();
            }

            $buffer = '';
            while (!feof($file)) {
                $buffer .= fread($file, 1024);
            }
            fclose($file);

            $json['result'] = 'success';
            $json['data'] = $buffer;
            echo wp_json_encode($json);
        }
        catch (Exception $error) {
            $message = 'An exception has occurred. class: '.get_class($error).';msg: '.$error->getMessage().';code: '.$error->getCode().';line: '.$error->getLine().';in_file: '.$error->getFile().';';
            error_log($message);
            echo wp_json_encode(array('result'=>'failed','error'=>$message));
            die();
        }
        die();
    }
    /**
     * View logs of the backup task.
     *
     * @since 0.9.1
     */
    public function view_backup_task_log()
    {
        check_ajax_referer( 'wpvivid_ajax', 'nonce' );
        $check=current_user_can('manage_options');
        $check=apply_filters('wpvivid_ajax_check_security',$check);
        if(!$check)
        {
            die();
        }
        try {
            if (isset($_POST['id']) && !empty($_POST['id']) && is_string($_POST['id'])) {
                $backup_task_id = sanitize_key($_POST['id']);
                $option = WPvivid_taskmanager::get_task_options($backup_task_id, 'log_file_name');
                if (!$option) {
                    $json['result'] = 'failed';
                    $json['error'] = __('Retrieving the backup information failed while showing log. Please try again later.', 'wpvivid-backuprestore');
                    echo wp_json_encode($json);
                    die();
                }

                $log_file_name = $this->wpvivid_log->GetSaveLogFolder() . $option . '_log.txt';

                if (!file_exists($log_file_name)) {
                    $json['result'] = 'failed';
                    $json['error'] = __('The log not found.', 'wpvivid-backuprestore');
                    echo wp_json_encode($json);
                    die();
                }

                $file = fopen($log_file_name, 'r');

                if (!$file) {
                    $json['result'] = 'failed';
                    $json['error'] = __('Unable to open the log file.', 'wpvivid-backuprestore');
                    echo wp_json_encode($json);
                    die();
                }

                $buffer = '';
                while (!feof($file)) {
                    $buffer .= fread($file, 1024);
                }
                fclose($file);

                $json['result'] = 'success';
                $json['data'] = $buffer;
                echo wp_json_encode($json);
            } else {
                $json['result'] = 'failed';
                $json['error'] = __('Reading the log failed. Please try again.', 'wpvivid-backuprestore');
                echo wp_json_encode($json);
            }
        }
        catch (Exception $error) {
            $message = 'An exception has occurred. class: '.get_class($error).';msg: '.$error->getMessage().';code: '.$error->getCode().';line: '.$error->getLine().';in_file: '.$error->getFile().';';
            error_log($message);
            echo wp_json_encode(array('result'=>'failed','error'=>$message));
            die();
        }
        die();
    }
    /**
     * Cancel a backup task.
     *
     * @since 0.9.1
     */
    public function backup_cancel()
    {
        check_ajax_referer( 'wpvivid_ajax', 'nonce' );
        $check=current_user_can('manage_options');
        $check=apply_filters('wpvivid_ajax_check_security',$check);
        if(!$check)
        {
            die();
        }
        try {
            /*if (isset($_POST['task_id']) && !empty($_POST['task_id']) && is_string($_POST['task_id'])) {
                $task_id = sanitize_key($_POST['task_id']);
                $json = $this->function_realize->_backup_cancel($task_id);
                echo wp_json_encode($json);
            }*/
            $json = $this->function_realize->_backup_cancel();
            echo wp_json_encode($json);
        }
        catch (Exception $error) {
            $message = 'An exception has occurred. class: '.get_class($error).';msg: '.$error->getMessage().';code: '.$error->getCode().';line: '.$error->getLine().';in_file: '.$error->getFile().';';
            error_log($message);
            echo wp_json_encode(array('result'=>'failed','error'=>$message));
            die();
        }
        die();
    }

    public function main_schedule($schedule_id='')
    {
        //get backup options
        do_action('wpvivid_set_current_schedule_id', $schedule_id);
        $this->end_shutdown_function=false;
        register_shutdown_function(array($this,'deal_prepare_shutdown_error'));
        $schedule_options=WPvivid_Schedule::get_schedule($schedule_id);
        if(empty($schedule_options))
        {
            $this->end_shutdown_function=true;
            die();
        }
        try
        {
            $schedule_options['backup']['local'] = strval($schedule_options['backup']['local']);
            $schedule_options['backup']['remote'] = strval($schedule_options['backup']['remote']);
            $schedule_options['backup']['ismerge'] = strval($schedule_options['backup']['ismerge']);
            $schedule_options['backup']['lock'] = strval($schedule_options['backup']['lock']);
            $ret = $this->check_backup_option($schedule_options['backup'], 'Cron');
            if ($ret['result'] != WPVIVID_SUCCESS)
            {
                $this->end_shutdown_function=true;
                //echo wp_json_encode($ret);
                die();
            }

            if(!isset($schedule_options['backup']['type']))
            {
                $schedule_options['backup']['type']='Cron';
                $schedule_options['backup']['action']='backup';
            }

            $ret = $this->pre_backup($schedule_options['backup']);
            if ($ret['result'] == 'success') {
                //Check the website data to be backed up.
                //$this->check_backup($ret['task_id'], $schedule_options['backup']);
                //flush buffer
                $this->flush($ret['task_id']);
                //start backup task.

                $task_msg = WPvivid_taskmanager::get_task($ret['task_id']);
                $this->update_last_backup_time($task_msg);

                $this->backup($ret['task_id']);
            }
            $this->end_shutdown_function=true;
            die();
        }
        catch (Exception $error)
        {
            $this->end_shutdown_function=true;
            $ret['result']='failed';
            $message = 'An exception has occurred. class:'.get_class($error).';msg:'.$error->getMessage().';code:'.$error->getCode().';line:'.$error->getLine().';in_file:'.$error->getFile().';';
            $ret['error'] = $message;
            $id=uniqid('wpvivid-');
            $log_file_name=$id.'_backup';
            if(!class_exists('WPvivid_Log'))
            {
                include_once WPVIVID_PLUGIN_DIR . '/includes/class-wpvivid-log.php';
            }
            $log=new WPvivid_Log();
            $log->CreateLogFile($log_file_name,'no_folder','backup');
            $log->WriteLog($message,'notice');
            $log->CloseFile();
            WPvivid_error_log::create_error_log($log->log_file);
            error_log($message);
            //echo wp_json_encode($ret);
            die();
        }
    }
    /**
     * Resume backup schedule.
     *
     * Resume a backup task.
     *
     * @var string $task_id backup task id
     *
     * @since 0.9.1
     */
    public function resume_schedule($task_id='0')
    {
        if($task_id=='0')
        {
            die();
        }

        $task=WPvivid_taskmanager::get_task($task_id);

        if(!$task)
        {
            die();
        }

        if (WPvivid_taskmanager::is_tasks_backup_running())
        {
            $ret['result'] = 'failed';
            $ret['error'] = __('A task is already running. Please wait until the running task is complete, and try again.', 'wpvivid-backuprestore');
            echo wp_json_encode($ret);
            die();
        }

        $doing=WPvivid_taskmanager::get_backup_main_task_progress($task_id);
        if($doing=='backup')
        {
            //flush buffer
            $this->flush($task_id);
            $this->backup($task_id);
        }
        else if($doing=='upload')
        {
            //flush buffer
            $this->flush($task_id);
            $this->upload($task_id);
        }
        //resume backup

        die();
    }

    /**
     * Clean backing up data schedule.
     *
     * @var string $task_id backup task id
     *
     * @since 0.9.1
     */
    public function clean_backing_up_data_event($task_id)
    {
        $tasks=WPvivid_Setting::get_option('clean_task');
        if(isset($tasks[$task_id])){
            $task=$tasks[$task_id];
            unset($tasks[$task_id]);
        }
        WPvivid_Setting::update_option('clean_task',$tasks);

        if(!empty($task))
        {
            $backup=new WPvivid_Backup(false,$task);
            $backup->clean_backup();

            $files=array();

            if($task['options']['remote_options']!==false)
            {
                $backup_files=$backup->task->get_need_cleanup_files(true);
                foreach ($backup_files as $file)
                {
                    $files[]=basename($file);
                }
                if(!empty($files))
                {
                    $upload=new WPvivid_Upload();
                    $upload->clean_remote_backup($task['options']['remote_options'],$files);
                }
            }
            //clean upload
        }
    }
    /**
     * Clean backup record schedule.
     *
     * @var string $task_id backup task id
     *
     * @since 0.9.1
     */
    public function clean_backup_record_event($backup_id)
    {
        $tasks=WPvivid_Setting::get_option('clean_task');
        $backup=$tasks[$backup_id];
        unset($tasks[$backup_id]);
        WPvivid_Setting::update_option('clean_task',$tasks);

        if(!empty($backup))
        {
            $backup_item=new WPvivid_Backup_Item($backup);
            $backup_item->cleanup_local_backup();
            $backup_item->cleanup_remote_backup();
        }
    }
    /**
     * Clean oldest backup record.
     *
     * @var string $task_id backup task id
     *
     * @since 0.9.1
     */
    public function clean_oldest_backup()
    {
        $oldest_ids=array();
        $oldest_ids=apply_filters('wpvivid_get_oldest_backup_ids',$oldest_ids,false);
        if($oldest_ids!==false)
        {
            foreach ($oldest_ids as $oldest_id)
            {
                $this->add_clean_backup_record_event($oldest_id);
                WPvivid_Backuplist::delete_backup($oldest_id);
            }
        }
    }

    public function get_oldest_backup_ids($oldest_ids,$multiple)
    {
        if($multiple)
        {
            $count=WPvivid_Setting::get_max_backup_count();

            $oldest_ids = WPvivid_Backuplist::get_out_of_date_backuplist($count);

            return $oldest_ids;
        }
        else
        {
            $count=WPvivid_Setting::get_max_backup_count();
            $oldest_id=WPvivid_Backuplist::check_backuplist_limit($count);
            $oldest_ids=array();
            $oldest_ids[]=$oldest_id;
            return $oldest_ids;
        }
    }

    public function check_backup_completeness($check_res, $task_id){
        $task=WPvivid_taskmanager::get_task($task_id);
        if(isset($task['setting']['is_merge']) && $task['setting']['is_merge'] == '1')
        {
            if(isset($task['jobs']))
            {
                foreach ($task['jobs'] as $job_info)
                {
                    if($job_info['backup_type'] === 'backup_merge')
                    {
                        if(isset($job_info['zip_file']) && !empty($job_info['zip_file']))
                        {
                            foreach ($job_info['zip_file'] as $zip_file_name => $zip_file_info)
                            {
                                if(!$this->check_backup_file_json($zip_file_name)){
                                    $check_res = false;
                                }
                            }
                        }
                    }
                }
            }
        }
        else
        {
            if(isset($task['jobs']))
            {
                foreach ($task['jobs'] as $job_info)
                {
                    if(isset($job_info['zip_file']) && !empty($job_info['zip_file']))
                    {
                        foreach ($job_info['zip_file'] as $zip_file_name => $zip_file_info)
                        {
                            if(!$this->check_backup_file_json($zip_file_name)){
                                $check_res = false;
                            }
                        }
                    }
                }
            }
        }
        return $check_res;
    }

    public function get_mainwp_sync_data($information)
    {
        $data['setting']['wpvivid_compress_setting']=get_option('wpvivid_compress_setting');
        $data['setting']['wpvivid_local_setting']=get_option('wpvivid_local_setting');
        $data['setting']['wpvivid_common_setting']=get_option('wpvivid_common_setting');
        $data['setting']['wpvivid_email_setting']=get_option('wpvivid_email_setting');
        $data['setting']['cron_backup_count']=get_option('cron_backup_count');
        $data['schedule']=get_option('wpvivid_schedule_setting');
        $data['remote']['upload']=get_option('wpvivid_upload_setting');
        $data['remote']['history']=get_option('wpvivid_user_history');

        $data['setting_addon'] = $data['setting'];
        $data['setting_addon']['wpvivid_staging_options']=array();
        $data['backup_custom_setting']=array();
        $data['menu_capability']=array();
        $data['white_label_setting']=array();
        $data['incremental_backup_setting']=array();
        $data['last_backup_report']=array();
        $data['schedule_addon']=array();
        $data['time_zone']=false;
        $data['is_pro']=false;
        $data['is_install']=false;
        $data['is_login']=false;
        $data['latest_version']='';
        $data['current_version']='';
        $data['dashboard_version'] = '';
        $data['addons_info'] = array();
        $data=apply_filters('wpvivid_get_wpvivid_info_addon_mainwp', $data);

        $information['syncWPvividSetting']=$data;
        return $information;
    }

    public function check_backup_file_json($file_name){
        $setting=get_option('wpvivid_common_setting',array());
        $zip_method=isset($setting['zip_method'])?$setting['zip_method']:'ziparchive';

        if($zip_method=='ziparchive'||empty($zip_method))
        {
            if(class_exists('ZipArchive'))
            {
                if(method_exists('ZipArchive', 'addFile'))
                {
                    $zip_method='ziparchive';
                }
                else
                {
                    $zip_method='pclzip';
                }
            }
            else
            {
                $zip_method='pclzip';
            }
        }
        else
        {
            $zip_method='pclzip';
        }

        if($zip_method=='ziparchive')
        {
            $general_setting=WPvivid_Setting::get_setting(true, "");
            $backup_folder = $general_setting['options']['wpvivid_local_setting']['path'];
            $backup_path=WP_CONTENT_DIR.DIRECTORY_SEPARATOR.$backup_folder.DIRECTORY_SEPARATOR;
            $file_path=$backup_path.$file_name;

            $zip_object=new ZipArchive();
            $zip_object->open($file_path);

            $json=$zip_object->getFromName('wpvivid_package_info.json');
            if($json !== false)
            {
                $json = json_decode($json, 1);
                if (is_null($json))
                {
                    return false;
                }
                else
                {
                    return $json;
                }
            }
            else
            {
                return false;
            }
        }
        else
        {
            if(!class_exists('WPvivid_ZipClass'))
                include_once WPVIVID_PLUGIN_DIR . '/includes/class-wpvivid-zipclass.php';
            $zip=new WPvivid_ZipClass();

            $general_setting=WPvivid_Setting::get_setting(true, "");
            $backup_folder = $general_setting['options']['wpvivid_local_setting']['path'];
            $backup_path=WP_CONTENT_DIR.DIRECTORY_SEPARATOR.$backup_folder.DIRECTORY_SEPARATOR;
            $file_path=$backup_path.$file_name;

            $ret=$zip->get_json_data($file_path);
            if($ret['result'] === WPVIVID_SUCCESS) {
                $json=$ret['json_data'];
                $json = json_decode($json, 1);
                if (is_null($json)) {
                    return false;
                } else {
                    return $json;
                }
            }
            elseif($ret['result'] === WPVIVID_FAILED){
                return false;
            }
        }
    }

    /**
     * Initialization backup task.
     *
     * @var array $backup_options
     * @var string $type
     * @var int $lock
     *
     * @return array
     *
     * @since 0.9.1
     */
    public function pre_backup($backup_options)
    {
        if(apply_filters('wpvivid_need_clean_oldest_backup',true,$backup_options))
        {
            $this->clean_oldest_backup();
        }
        do_action('wpvivid_clean_oldest_backup',$backup_options);

        if(WPvivid_taskmanager::is_tasks_backup_running())
        {
            $ret['result']='failed';
            $ret['error']=__('A task is already running. Please wait until the running task is complete, and try again.', 'wpvivid-backuprestore');
            return $ret;
        }

        $backup=new WPvivid_Backup_Task();
        $ret=$backup->new_backup_task($backup_options,$backup_options['type'],$backup_options['action']);
        return $ret;
    }

    /**
     * start or resume a backup task.
     *
     * @var string $task_id
     *
     * @since 0.9.1
     */
    public function backup($task_id)
    {
        //register shutdown function to catch php fatal error such as script time out and memory limit

        $common_setting = WPvivid_Setting::get_option('wpvivid_common_setting');
        if(isset($common_setting['memory_limit']) && !empty($common_setting['memory_limit'])){
            $memory_limit = $common_setting['memory_limit'];
        }
        else{
            $memory_limit = WPVIVID_MEMORY_LIMIT;
        }
        @ini_set('memory_limit', $memory_limit);
        $this->end_shutdown_function=false;
        register_shutdown_function(array($this,'deal_shutdown_error'),$task_id);
        @ignore_user_abort(true);
        WPvivid_taskmanager::update_backup_task_status($task_id,true,'running');
        $this->current_task=WPvivid_taskmanager::get_task($task_id);
        //start a watch task event
        $this->add_monitor_event($task_id);
        //flush buffer
        //$this->flush($task_id);
        $this->wpvivid_log->OpenLogFile(WPvivid_taskmanager::get_task_options($task_id,'log_file_name'));
        $this->wpvivid_log->WriteLog('Start backing up.','notice');
        $this->wpvivid_log->WriteLogHander();
        //start backup
        try
        {
            $this->wpvivid_check_add_litespeed_server($task_id);
            $backup=new WPvivid_Backup();

            //$backup->clearcache();
            $backup_ret=$backup->backup($task_id);
            $backup->clearcache();

            if($backup_ret['result'] != WPVIVID_SUCCESS)
            {
                $this->wpvivid_log->WriteLog('Backup ends with an error '. $backup_ret['error'], 'error');
            }
            else {
                $this->wpvivid_log->WriteLog('Backup completed.','notice');
            }

            if(!$this->finish_backup_task($task_id))
            {
                $this->end_shutdown_function=true;
                die();
            }
            if(WPvivid_taskmanager::get_task_options($task_id,'remote_options')!=false)
            {
                $this->upload($task_id,false);
            }
        }
        catch (Exception $e)
        {
            //catch error and stop task recording history
            $this->deal_task_error($task_id,'exception',$e);
            $this->wpvivid_log->CloseFile();
            $this->end_shutdown_function=true;
            die();
        }

        $this->end_shutdown_function=true;
        die();
    }

    /**
     * recording finished backup task.
     *
     * @var string $task_id
     *
     * @var array $backup_ret return data of backup
     *
     * @return boolean
     *
     * @since 0.9.1
     */
    private function finish_backup_task($task_id)
    {
        $status=WPvivid_taskmanager::get_backup_task_status($task_id);
        if($status['str']=='running')
        {
            $this->wpvivid_log->WriteLog('Backup succeeded.','notice');

            $check_res = apply_filters('wpvivid_check_backup_completeness', true, $task_id);
            if(!$check_res){
                $task=WPvivid_taskmanager::get_task($task_id);
                $task['status']['error'] = 'We have detected that this backup is either corrupted or incomplete. Please make sure your server disk space is sufficient then create a new backup. In order to successfully back up/restore a website, the amount of free server disk space needs to be at least twice the size of the website';
                do_action('wpvivid_handle_backup_failed',$task, false);
                return false;
            }

            $remote_options=WPvivid_taskmanager::get_task_options($task_id,'remote_options');
            if($remote_options===false)
            {
                $task=WPvivid_taskmanager::update_backup_task_status($task_id,false,'completed');
                do_action('wpvivid_handle_backup_succeed',$task);
            }
            return true;
        }
        else
        {
            $task=WPvivid_taskmanager::get_task($task_id);
            do_action('wpvivid_handle_backup_failed',$task, false);
            return false;
        }
    }

    public function wpvivid_analysis_backup($task)
    {
        if($task['type'] == 'Manual')
        {
            $need_review=WPvivid_Setting::get_option('wpvivid_need_review');
            if($need_review=='not')
            {
                $review_time=WPvivid_Setting::get_option('wpvivid_review_time', false);
                if($review_time === false || time() >= $review_time)
                {
                    WPvivid_Setting::update_option('wpvivid_need_review','show');
                    $msg = 'Backup successful! If you\'re happy with WPvivid Backup Plugin, a 5-star rating would mean the world to us and help us make it even better.';
                    WPvivid_Setting::update_option('wpvivid_review_msg',$msg);
                    WPvivid_Setting::update_option('wpvivid_review_type', 'manual');
                }
            }
        }
        else if($task['type'] == 'Cron')
        {
            $cron_backup_count = WPvivid_Setting::get_option('cron_backup_count');
            if(empty($cron_backup_count)){
                $cron_backup_count = 0;
            }
            $cron_backup_count++;
            WPvivid_Setting::update_option('cron_backup_count', $cron_backup_count);
            $common_setting = WPvivid_Setting::get_option('wpvivid_common_setting');
            $max_backup_count = $common_setting['max_backup_count'];
            if($cron_backup_count >= $max_backup_count){
                $need_review=WPvivid_Setting::get_option('wpvivid_need_review');
                if($need_review=='not')
                {
                    WPvivid_Setting::update_option('wpvivid_need_review','show');
                    $msg = 'Cheers, scheduled backups are running smoothly. If you find WPvivid Backup plugin helpful, a 5-star rating would help us continue improving.';
                    WPvivid_Setting::update_option('wpvivid_review_msg',$msg);
                    WPvivid_Setting::update_option('wpvivid_review_type', 'cron');
                }
            }
        }
    }
    /**
     * start upload files to remote.
     *
     * @var string $task_id
     * @var bool $restart
     * @since 0.9.10
     */
    public function upload($task_id,$restart=true)
    {
        $this->end_shutdown_function=false;
        register_shutdown_function(array($this,'deal_shutdown_error'),$task_id);
        @ignore_user_abort(true);
        WPvivid_taskmanager::update_backup_task_status($task_id,$restart,'running',false,0);
        $this->current_task=WPvivid_taskmanager::get_task($task_id);
        //start a watch task event
        $this->add_monitor_event($task_id);
        //flush buffer
        //$this->flush($task_id);
        $this->wpvivid_log->OpenLogFile(WPvivid_taskmanager::get_task_options($task_id,'log_file_name'));
        $this->wpvivid_log->WriteLog('Start upload.','notice');

        $this->set_time_limit($task_id);

        $upload=new WPvivid_Upload();
        $ret=$upload->upload($task_id);
        if($ret['result'] == WPVIVID_SUCCESS)
        {
            $task=WPvivid_taskmanager::update_backup_task_status($task_id,false,'completed');
            do_action('wpvivid_handle_upload_succeed',$task);
        }
        else
        {
            $backup = WPvivid_Backuplist::get_backup_by_id($task_id);
            if($backup!==false)
            {
                $backup['save_local']=1;
                WPvivid_Backuplist::update_backup_option($task_id, $backup);
            }

            $this->wpvivid_log->WriteLog('Uploading the file ends with an error '. $ret['error'], 'error');
            $task=WPvivid_taskmanager::get_task($task_id);
            do_action('wpvivid_handle_backup_failed',$task, false);
        }
        $this->end_shutdown_function=true;
        die();
    }

    function wpvivid_deal_upload_succeed($task)
    {
        $save_local=$task['options']['save_local'];
        if($save_local==0)
        {
            $retain_local = WPvivid_Setting::get_retain_local_status();
            if(!$retain_local) {
                $this->wpvivid_log->WriteLog('Cleaned up local files after uploading to remote storages.', 'notice');
                $backup = new WPvivid_Backup($task['id']);
                $backup->clean_backup();
            }
        }
        $this->wpvivid_log->WriteLog('Upload succeeded.','notice');
        $remote_options=$task['options']['remote_options'];
        $remote_options=apply_filters('wpvivid_set_backup_remote_options',$remote_options,$task['id']);
        WPvivid_Backuplist::update_backup($task['id'],'remote',$remote_options);
    }

    function wpvivid_handle_backup_succeed($task)
    {
        if($task['action'] === 'backup')
        {
            $backup_task=new WPvivid_Backup_Task($task['id']);
            $backup_task->add_new_backup();

            $remote_options = WPvivid_taskmanager::get_task_options($task['id'], 'remote_options');
            if($remote_options != false){
                WPvivid_Backuplist::update_backup($task['id'],'remote', $remote_options);
            }

            $backup_success_count = WPvivid_Setting::get_option('wpvivid_backup_success_count');
            if (empty($backup_success_count))
            {
                $backup_success_count = 0;
            }
            $backup_success_count++;
            WPvivid_Setting::update_option('wpvivid_backup_success_count', $backup_success_count);
            $this->wpvivid_analysis_backup($task);
            $task_msg = WPvivid_taskmanager::get_task($task['id']);
            $this->update_last_backup_task($task_msg);
        }
        WPvivid_Schedule::clear_monitor_schedule($task['id']);

        $backup_task=new WPvivid_Backup_Task($task['id']);
        $res=$backup_task->get_backup_result();
        if(!empty($res['files']))
        {
            if(!class_exists('WPvivid_mail_report'))
                include_once WPVIVID_PLUGIN_DIR . '/includes/class-wpvivid-mail-report.php';
            WPvivid_mail_report::send_report_mail($task);
        }
        $this->wpvivid_check_clear_litespeed_rule($task['id']);
    }

    function wpvivid_mark_task($task)
    {
        WPvivid_taskmanager::mark_task($task['id']);
    }

    function wpvivid_handle_backup_failed($task, $need_set_low_resource_mode)
    {
        if($task['action'] === 'backup')
        {
            $backup_error_array = WPvivid_Setting::get_option('wpvivid_backup_error_array');
            if (!isset($backup_error_array) || empty($backup_error_array))
            {
                $backup_error_array = array();
                $backup_error_array['bu_error']['task_id'] = '';
                $backup_error_array['bu_error']['error_msg'] = '';
            }
            if (!array_key_exists($task['id'], $backup_error_array['bu_error']))
            {
                $backup_error_array['bu_error']['task_id'] = $task['id'];
                $backup_error_array['bu_error']['error_msg'] = 'Unknown error.';

                $general_setting=WPvivid_Setting::get_setting(true, "");
                $need_notice = false;
                if(!isset($general_setting['options']['wpvivid_compress_setting']['subpackage_plugin_upload'])){
                    $need_notice = true;
                }
                else{
                    if($general_setting['options']['wpvivid_compress_setting']['subpackage_plugin_upload']){
                        $need_notice = false;
                    }
                    else{
                        $need_notice = true;
                    }
                }
                if($need_notice) {
                    if($need_set_low_resource_mode) {
                        $notice_msg1 = 'Backup failed, it seems due to insufficient server resource or hitting server limit. Please navigate to Settings > Advanced > ';
                        $notice_msg2 = 'optimization mode for web hosting/shared hosting';
                        $notice_msg3 = ' to enable it and try again';
                        $backup_error_array['bu_error']['error_msg']='<div class="notice notice-error inline"><p>'.$notice_msg1.'<strong>'.$notice_msg2.'</strong>'.$notice_msg3.'</p></div>';
                    }
                    else{
                        $notice_msg = 'Backup error: '.$task['status']['error'].', task id: '.$task['id'];
                        $backup_error_array['bu_error']['error_msg']='<div class="notice notice-error inline"><p>'.$notice_msg.', Please switch to <a href="#" onclick="wpvivid_click_switch_page(\'wrap\', \'wpvivid_tab_debug\', true);">Website Info</a> page to send us the debug information. </p></div>';
                    }
                }
                else{
                    if($need_set_low_resource_mode) {
                        $notice_msg = 'Backup failed, it seems due to insufficient server resource or hitting server limit.';
                        $backup_error_array['bu_error']['error_msg'] = '<div class="notice notice-error inline"><p>' . $notice_msg . ', Please switch to <a href="#" onclick="wpvivid_click_switch_page(\'wrap\', \'wpvivid_tab_debug\', true);">Website Info</a> page to send us the debug information. </p></div>';
                    }
                    else {
                        $notice_msg = 'Backup error: ' . $task['status']['error'] . ', task id: ' . $task['id'];
                        $backup_error_array['bu_error']['error_msg'] = '<div class="notice notice-error inline"><p>' . $notice_msg . ', Please switch to <a href="#" onclick="wpvivid_click_switch_page(\'wrap\', \'wpvivid_tab_debug\', true);">Website Info</a> page to send us the debug information. </p></div>';
                    }
                }
            }
            WPvivid_Setting::update_option('wpvivid_backup_error_array', $backup_error_array);
            $task_msg = WPvivid_taskmanager::get_task($task['id']);
            $this->update_last_backup_task($task_msg);
        }
        $this->wpvivid_log->WriteLog($task['status']['error'],'error');
        $this->wpvivid_log->CloseFile();
        WPvivid_error_log::create_error_log($this->wpvivid_log->log_file);
        WPvivid_Schedule::clear_monitor_schedule($task['id']);
        $this->add_clean_backing_up_data_event($task['id']);
        if(!class_exists('WPvivid_mail_report'))
            include_once WPVIVID_PLUGIN_DIR . '/includes/class-wpvivid-mail-report.php';
        WPvivid_mail_report::send_report_mail($task);
        $this->wpvivid_check_clear_litespeed_rule($task['id']);
    }

    public function deal_shutdown_error($task_id)
    {
        if($this->end_shutdown_function===false)
        {
            $last_error = error_get_last();
            if (!empty($last_error) && !in_array($last_error['type'], array(E_NOTICE,E_WARNING,E_USER_NOTICE,E_USER_WARNING,E_DEPRECATED), true))
            {
                $error = $last_error;
            } else {
                $error = false;
            }
            //$this->task_monitor($task_id,$error);
            if (WPvivid_taskmanager::get_task($task_id) !== false)
            {
                if ($this->wpvivid_log->log_file_handle == false)
                {
                    $this->wpvivid_log->OpenLogFile(WPvivid_taskmanager::get_task_options($task_id, 'log_file_name'));
                }

                $status = WPvivid_taskmanager::get_backup_task_status($task_id);

                if ($status['str'] == 'running' || $status['str'] == 'error' || $status['str'] == 'no_responds')
                {
                    $options=WPvivid_Setting::get_option('wpvivid_common_setting');
                    if(isset($options['max_execution_time']))
                    {
                        $limit=$options['max_execution_time'];
                    }
                    else
                    {
                        $limit=WPVIVID_MAX_EXECUTION_TIME;
                    }

                    if(isset($options['max_resume_count']))
                    {
                        $max_resume_count=$options['max_resume_count'];
                    }
                    else
                    {
                        $max_resume_count=WPVIVID_RESUME_RETRY_TIMES;
                    }
                    $time_spend = time() - $status['timeout'];
                    $time_start = time() - $status['start_time'];
                    $time_min=min($limit, 120);
                    if ($time_spend >= $limit)
                    {
                        //time out
                        $status['resume_count']++;

                        if ($status['resume_count'] > $max_resume_count)
                        {
                            $message = __('Too many resumption attempts.', 'wpvivid-backuprestore');
                            $task = WPvivid_taskmanager::update_backup_task_status($task_id, false, 'error', false, $status['resume_count'], $message);
                            do_action('wpvivid_handle_backup_failed', $task, true);
                        } else {
                            $this->check_cancel_backup($task_id);
                            $message = 'Task timed out.';
                            if ($this->add_resume_event($task_id))
                            {
                                WPvivid_taskmanager::update_backup_task_status($task_id, false, 'wait_resume', false, $status['resume_count']);
                            } else {
                                $task = WPvivid_taskmanager::update_backup_task_status($task_id, false, 'error', false, $status['resume_count'], $message);
                                do_action('wpvivid_handle_backup_failed', $task, true);
                            }
                        }
                        if ($this->wpvivid_log)
                            $this->wpvivid_log->WriteLog($message, 'error');
                    }
                    else if($time_start>=$time_min)
                    {
                        $status['resume_count']++;
                        if ($status['resume_count'] > $max_resume_count)
                        {
                            $message = __('Too many resumption attempts.', 'wpvivid-backuprestore');
                            if ($error !== false)
                            {
                                $message.= 'type: '. $error['type'] . ', ' . $error['message'] . ' file:' . $error['file'] . ' line:' . $error['line'];
                            }
                            $task = WPvivid_taskmanager::update_backup_task_status($task_id, false, 'error', false, $status['resume_count'], $message);
                            do_action('wpvivid_handle_backup_failed', $task ,true);
                        } else {
                            $this->check_cancel_backup($task_id);
                            $message = 'Task timed out (WebHosting).';
                            if ($this->add_resume_event($task_id))
                            {
                                WPvivid_taskmanager::update_backup_task_status($task_id, false, 'wait_resume', false, $status['resume_count']);
                            } else {
                                $task = WPvivid_taskmanager::update_backup_task_status($task_id, false, 'error', false, $status['resume_count'], $message);
                                do_action('wpvivid_handle_backup_failed', $task, true);
                            }
                        }
                        if ($this->wpvivid_log)
                            $this->wpvivid_log->WriteLog($message, 'error');
                    }
                    else
                    {
                        $status['resume_count']++;
                        if ($status['resume_count'] > $max_resume_count)
                        {
                            $message = __('Too many resumption attempts.', 'wpvivid-backuprestore');
                            if ($error !== false)
                            {
                                $message.= 'type: '. $error['type'] . ', ' . $error['message'] . ' file:' . $error['file'] . ' line:' . $error['line'];
                            }
                            $task = WPvivid_taskmanager::update_backup_task_status($task_id, false, 'error', false, $status['resume_count'], $message);
                            do_action('wpvivid_handle_backup_failed', $task ,true);
                        } else {
                            $this->check_cancel_backup($task_id);
                            $message = 'Task timed out (WebHosting).';
                            if ($this->add_resume_event($task_id))
                            {
                                WPvivid_taskmanager::update_backup_task_status($task_id, false, 'wait_resume', false, $status['resume_count']);
                            } else {
                                $task = WPvivid_taskmanager::update_backup_task_status($task_id, false, 'error', false, $status['resume_count'], $message);
                                do_action('wpvivid_handle_backup_failed', $task, true);
                            }
                        }
                        if ($this->wpvivid_log)
                            $this->wpvivid_log->WriteLog($message, 'error');
                    }
                    /*
                    else
                    {
                        if ($status['str'] != 'error')
                        {
                            if ($error !== false)
                            {
                                $message = 'type: '. $error['type'] . ', ' . $error['message'] . ' file:' . $error['file'] . ' line:' . $error['line'];
                            } else {
                                $message = __('Backup timed out. Please set the value of PHP script execution timeout to '.$time_start.' in plugin settings.', 'wpvivid-backuprestore');
                            }
                            WPvivid_taskmanager::update_backup_task_status($task_id, false, 'error', false, $status['resume_count'], $message);
                        }
                        $task = WPvivid_taskmanager::get_task($task_id);
                        do_action('wpvivid_handle_backup_failed', $task, false);
                    }
                    */
                }
            }
            die();
        }
    }
    public function deal_task_error($task_id,$error_type,$error)
    {
        $message = 'An '.$error_type.' has occurred. class:'.get_class($error).';msg:'.$error->getMessage().';code:'.$error->getCode().';line:'.$error->getLine().';in_file:'.$error->getFile().';';
        error_log($message);
        $task=WPvivid_taskmanager::update_backup_task_status($task_id,false,'error',false,false,$message);
        $this->wpvivid_log->WriteLog($message,'error');

        do_action('wpvivid_handle_backup_failed',$task, false);
    }
    /**
     * update time limit.
     *
     * @var string $task_id
     *
     * @var int $second
     *
     * @since 0.9.1
     */
    public function set_time_limit($task_id,$second=0)
    {
        if($second==0)
        {
            $options=WPvivid_Setting::get_option('wpvivid_common_setting');
            if(isset($options['max_execution_time']))
            {
                $second=$options['max_execution_time'];
            }
            else
            {
                $second=WPVIVID_MAX_EXECUTION_TIME;
            }
        }
        WPvivid_taskmanager::update_backup_task_status($task_id,false,'',true);
        @set_time_limit($second);
    }
    /**
     * Watch task status.
     *
     * @var string $task_id
     *
     * @var array|false $error
     *
     * @since 0.9.1
     */
    public function task_monitor($task_id)
    {
        if(WPvivid_taskmanager::get_task($task_id)!==false)
        {
            if($this->wpvivid_log->log_file_handle==false)
            {
                $this->wpvivid_log->OpenLogFile(WPvivid_taskmanager::get_task_options($task_id,'log_file_name'));
            }

            $status=WPvivid_taskmanager::get_backup_task_status($task_id);

            if($status['str']=='running'||$status['str']=='error'||$status['str']=='no_responds')
            {
                $options=WPvivid_Setting::get_option('wpvivid_common_setting');
                if(isset($options['max_execution_time']))
                {
                    $limit=$options['max_execution_time'];
                }
                else
                {
                    $limit=WPVIVID_MAX_EXECUTION_TIME;
                }
                $time_spend=time()-$status['timeout'];
                $last_active_time=time()-$status['run_time'];
                if($time_spend>=$limit&&$last_active_time>min(180,$limit))
                {
                    //time out
                    if(isset($options['max_resume_count']))
                    {
                        $max_resume_count=$options['max_resume_count'];
                    }
                    else
                    {
                        $max_resume_count=WPVIVID_RESUME_RETRY_TIMES;
                    }
                    $status['resume_count']++;
                    if($status['resume_count']>$max_resume_count)
                    {
                        $message=__('Too many resumption attempts.', 'wpvivid-backuprestore');
                        $task=WPvivid_taskmanager::update_backup_task_status($task_id,false,'error',false,$status['resume_count'],$message);
                        //WPvivid_error_log::create_error_log($this->wpvivid_log->log_file);
                        do_action('wpvivid_handle_backup_failed',$task, true);
                    }
                    else
                    {
                        $this->check_cancel_backup($task_id);
                        $message=__('Task timed out.', 'wpvivid-backuprestore');
                        if($this->add_resume_event($task_id))
                        {
                            WPvivid_taskmanager::update_backup_task_status($task_id,false,'wait_resume',false,$status['resume_count']);
                        }
                        else
                        {
                            $task=WPvivid_taskmanager::update_backup_task_status($task_id,false,'error',false,$status['resume_count'],$message);
                            do_action('wpvivid_handle_backup_failed',$task, true);
                        }
                    }
                    if($this->wpvivid_log)
                        $this->wpvivid_log->WriteLog($message,'error');
                }
                else {
                    $time_spend=time()-$status['run_time'];
                    if($time_spend>180)
                    {
                        $this->check_cancel_backup($task_id);
                        $this->wpvivid_log->WriteLog('Not responding for a long time.','notice');
                        WPvivid_taskmanager::update_backup_task_status($task_id,false,'no_responds',false,$status['resume_count']);
                        $this->add_monitor_event($task_id);
                    }
                    else {
                        $this->add_monitor_event($task_id);
                    }
                }
            }
            else if($status['str']=='wait_resume')
            {
                $timestamp = wp_next_scheduled(WPVIVID_RESUME_SCHEDULE_EVENT,array($task_id));
                if($timestamp===false)
                {
                    if($this->wpvivid_log)
                        $this->wpvivid_log->WriteLog('Missing resume task,so we create new one.','error');
                    $message = 'Task timed out (WebHosting).';
                    if ($this->add_resume_event($task_id))
                    {
                        WPvivid_taskmanager::update_backup_task_status($task_id, false, 'wait_resume', false, $status['resume_count']);
                    } else {
                        $task = WPvivid_taskmanager::update_backup_task_status($task_id, false, 'error', false, $status['resume_count'], $message);
                        do_action('wpvivid_handle_backup_failed', $task, true);
                    }
                }
            }
        }
    }

    public function task_monitor_ex()
    {
        check_ajax_referer( 'wpvivid_ajax', 'nonce' );
        $check=current_user_can('manage_options');
        $check=apply_filters('wpvivid_ajax_check_security',$check);
        if(!$check)
        {
            die();
        }

        $tasks=WPvivid_Setting::get_tasks();
        $task_id='';
        foreach ($tasks as $task)
        {
            if($task['action']=='backup')
            {
                $status=WPvivid_taskmanager::get_backup_tasks_status($task['id']);
                if($status['str']=='completed'||$status['str']=='error')
                {
                    continue;
                }
                else
                {
                    $task_id=$task['id'];
                    break;
                }
            }
        }

        if(empty($task_id))
        {
            die();
        }

        if(WPvivid_taskmanager::get_task($task_id)!==false)
        {
            if($this->wpvivid_log->log_file_handle==false)
            {
                $this->wpvivid_log->OpenLogFile(WPvivid_taskmanager::get_task_options($task_id,'log_file_name'));
            }

            $status=WPvivid_taskmanager::get_backup_task_status($task_id);

            if($status['str']=='running'||$status['str']=='error'||$status['str']=='no_responds')
            {
                $options=WPvivid_Setting::get_option('wpvivid_common_setting');
                if(isset($options['max_execution_time']))
                {
                    $limit=$options['max_execution_time'];
                }
                else
                {
                    $limit=WPVIVID_MAX_EXECUTION_TIME;
                }
                $time_spend=time()-$status['timeout'];
                $last_active_time=time()-$status['run_time'];
                if($time_spend>=$limit&&$last_active_time>min(180,$limit))
                {
                    //time out
                    if(isset($options['max_resume_count']))
                    {
                        $max_resume_count=$options['max_resume_count'];
                    }
                    else
                    {
                        $max_resume_count=WPVIVID_RESUME_RETRY_TIMES;
                    }
                    $status['resume_count']++;
                    if($status['resume_count']>$max_resume_count)
                    {
                        $message=__('Too many resumption attempts.', 'wpvivid-backuprestore');
                        $task=WPvivid_taskmanager::update_backup_task_status($task_id,false,'error',false,$status['resume_count'],$message);
                        //WPvivid_error_log::create_error_log($this->wpvivid_log->log_file);
                        do_action('wpvivid_handle_backup_failed',$task, true);
                    }
                    else
                    {
                        $this->check_cancel_backup($task_id);
                        $message=__('Task timed out.', 'wpvivid-backuprestore');
                        if($this->add_resume_event($task_id))
                        {
                            WPvivid_taskmanager::update_backup_task_status($task_id,false,'wait_resume',false,$status['resume_count']);
                        }
                        else
                        {
                            $task=WPvivid_taskmanager::update_backup_task_status($task_id,false,'error',false,$status['resume_count'],$message);
                            do_action('wpvivid_handle_backup_failed',$task, true);
                        }
                    }
                    if($this->wpvivid_log)
                        $this->wpvivid_log->WriteLog($message,'error');
                }
                else {
                    $time_spend=time()-$status['run_time'];
                    if($time_spend>180)
                    {
                        $this->check_cancel_backup($task_id);
                        $this->wpvivid_log->WriteLog('Not responding for a long time.','notice');
                        WPvivid_taskmanager::update_backup_task_status($task_id,false,'no_responds',false,$status['resume_count']);
                        $this->add_monitor_event($task_id);
                    }
                    else{
                        $this->add_monitor_event($task_id);
                    }
                }
            }
            else if($status['str']=='wait_resume')
            {
                $timestamp = wp_next_scheduled(WPVIVID_RESUME_SCHEDULE_EVENT,array($task_id));
                if($timestamp===false)
                {
                    if($this->wpvivid_log)
                        $this->wpvivid_log->WriteLog('Missing resume task,so we create new one.','error');
                    $message = 'Task timed out (WebHosting).';
                    if ($this->add_resume_event($task_id))
                    {
                        WPvivid_taskmanager::update_backup_task_status($task_id, false, 'wait_resume', false, $status['resume_count']);
                    } else {
                        $task = WPvivid_taskmanager::update_backup_task_status($task_id, false, 'error', false, $status['resume_count'], $message);
                        do_action('wpvivid_handle_backup_failed', $task, true);
                    }
                }
            }
        }
    }
    /**
     * Estimate the size of files, folder, database and backup time before backing up.
     *
     * @var string $task_id
     *
     * @var string $backup_files
     *
     * @var array $backup_option
     *
     * @return array
     *
     * @since 0.9.1
     */
    public function check_backup($task_id,$backup_option)
    {
        @set_time_limit(180);
        $options=WPvivid_Setting::get_option('wpvivid_common_setting');
        if(isset($options['estimate_backup']))
        {
            if($options['estimate_backup'] == false)
            {
                $ret['alert_db']=false;
                $ret['alter_files']=false;
                $ret['alter_fcgi']=false;
                $ret['alter_big_file']=false;
                return $ret;
            }
        }

        $file_size=false;

        $check['check_file']=false;
        $check['check_db']=false;
        add_filter('wpvivid_check_backup_size',array($this, 'check_backup_size'),10,2);
        $check = apply_filters('wpvivid_check_backup_size', $check,$backup_option);
        $check_file=$check['check_file'];
        $check_db=$check['check_db'];

        $sapi_type=php_sapi_name();

        if($sapi_type=='cgi-fcgi'||$sapi_type=='fpm-fcgi')
        {
            $alter_fcgi=true;
        }
        else
        {
            $alter_fcgi=false;
        }
        if($check_db)
        {
            include_once WPVIVID_PLUGIN_DIR . '/includes/class-wpvivid-db-method.php';
            $db_method=new WPvivid_DB_Method();
            $ret=$db_method->check_db($alter_fcgi);
            if($ret['result']==WPVIVID_FAILED)
            {
                return $ret;
            }
        }
        else
        {
            $ret['alert_db']=false;
            $ret['db_size']=false;
        }

        $ret['alter_files']=false;
        $ret['alter_big_file']=false;
        $ret['alter_fcgi']=false;

        if($check_file)
        {
            include_once WPVIVID_PLUGIN_DIR .'/includes/class-wpvivid-backup.php';
            $task=new WPvivid_Backup_Task($task_id);

            $file_size=$task->get_file_info();
            $sum_size=$file_size['sum_size'];
            $sum_count=$file_size['sum_count'];
            if($alter_fcgi)
            {
                $alter_sum_size=1024*1024*1024;
                $alter_sum_count=20000;
            }
            else
            {
                $alter_sum_size=4*1024*1024*1024;
                $alter_sum_count=8*10000;
            }

            if($sum_size>$alter_sum_size||$sum_count>$alter_sum_count)
            {
                $ret['alter_files']=true;
                $ret['sum_size']=$this->formatBytes($sum_size);
                $ret['sum_count']=$sum_count;
                $ret['file_size']=$file_size;
                if($alter_fcgi)
                    $ret['alter_fcgi']=true;
            }
            else{
                $ret['sum_count']=$sum_count;
            }
            $file_size['sum']=$this->formatBytes($sum_size);
        }

        $ret['file_size']=$file_size;
        if($task_id!==false)
        {
            $task=new WPvivid_Backup_Task($task_id);
            $task->set_file_and_db_info($ret['db_size'],$file_size);
        }
        return $ret;
    }

    public function check_backup_size($check,$backup_option)
    {
        if(isset($backup_option['backup_files']))
        {
            if($backup_option['backup_files']=='files+db')
            {
                $check['check_file']=true;
                $check['check_db']=true;
            }
            else if($backup_option['backup_files']=='files')
            {
                $check['check_file']=true;
            }
            else if($backup_option['backup_files']=='db')
            {
                $check['check_db']=true;
            }
        }
        return $check;
    }

    /**
     * Add a backup task resume schedule.
     *
     * @var string $task_id
     *
     * @return boolean
     *
     * @since 0.9.1
     */
    private function add_resume_event($task_id)
    {
        $resume_time=time()+WPVIVID_RESUME_INTERVAL;

        $b=wp_schedule_single_event($resume_time,WPVIVID_RESUME_SCHEDULE_EVENT,array($task_id));

        if($b===false)
        {
            $timestamp = wp_next_scheduled(WPVIVID_RESUME_SCHEDULE_EVENT,array($task_id));

            if($timestamp!==false)
            {
                $resume_time=max($resume_time,$timestamp+10*60+10);

                $b=wp_schedule_single_event($resume_time,WPVIVID_RESUME_SCHEDULE_EVENT,array($task_id));

                if($b===false)
                {
                    $this->wpvivid_log->WriteLog('Add and retry resume event failed.','notice');
                    return false;
                }
                $this->wpvivid_log->WriteLog('Retry resume event succeeded.','notice');
            }
            else
            {
                $this->wpvivid_log->WriteLog('Add resume event failed.','notice');
                return false;
            }
        }
        $this->wpvivid_log->WriteLog('Add resume event succeeded.. arg1:'.$resume_time.' arg2:'.WPVIVID_RESUME_SCHEDULE_EVENT.' arg3:'.$task_id,'notice');
        return true;
    }
    /**
     * Add a scheduled task to clear backup data.
     *
     * @var string $task_id
     *
     * @return boolean
     *
     * @since 0.9.1
     */
    public function add_clean_backing_up_data_event($task_id)
    {
        $task=WPvivid_taskmanager::get_task($task_id);
        $tasks=WPvivid_Setting::get_option('clean_task');
        $tasks[$task_id]=$task;
        WPvivid_Setting::update_option('clean_task',$tasks);

        $resume_time=time()+60;

        $b=wp_schedule_single_event($resume_time,WPVIVID_CLEAN_BACKING_UP_DATA_EVENT,array($task_id));

        if($b===false)
        {
            $timestamp = wp_next_scheduled(WPVIVID_CLEAN_BACKING_UP_DATA_EVENT,array($task_id));

            if($timestamp!==false)
            {
                $resume_time=max($resume_time,$timestamp+10*60+10);

                $b=wp_schedule_single_event($resume_time,WPVIVID_CLEAN_BACKING_UP_DATA_EVENT,array($task_id));

                if($b===false)
                {
                    return false;
                }
            }
            else
            {
                return false;
            }
        }
        return true;
    }
    /**
     * Add a scheduled task to clear backup record.
     *
     * @var string $task_id
     *
     * @return boolean
     *
     * @since 0.9.1
     */
    private function add_clean_backup_record_event($backup_id)
    {
        $backup=WPvivid_Backuplist::get_backup_by_id($backup_id);
        $tasks=WPvivid_Setting::get_option('clean_task');
        $tasks[$backup_id]=$backup;
        WPvivid_Setting::update_option('clean_task',$tasks);
        $resume_time=time()+60;

        $b=wp_schedule_single_event($resume_time,WPVIVID_CLEAN_BACKUP_RECORD_EVENT,array($backup_id));

        if($b===false)
        {
            $timestamp = wp_next_scheduled(WPVIVID_CLEAN_BACKUP_RECORD_EVENT,array($backup_id));

            if($timestamp!==false)
            {
                $resume_time=max($resume_time,$timestamp+10*60+10);

                $b=wp_schedule_single_event($resume_time,WPVIVID_CLEAN_BACKUP_RECORD_EVENT,array($backup_id));

                if($b===false)
                {
                    return false;
                }
            }
            else
            {
                return false;
            }
        }
        return true;
    }
    /**
     * Add a watch task scheduled event.
     *
     * @var string $task_id
     *
     * @var int $next_time
     *
     * @return boolean
     *
     * @since 0.9.1
     */
    public function add_monitor_event($task_id,$next_time=120)
    {
        $resume_time=time()+$next_time;

        $timestamp = wp_next_scheduled(WPVIVID_TASK_MONITOR_EVENT,array($task_id));

        if($timestamp===false)
        {
            $b = wp_schedule_single_event($resume_time, WPVIVID_TASK_MONITOR_EVENT, array($task_id));
            if ($b === false)
            {
                return false;
            } else {
                return true;
            }
        }
        return true;
    }

    public function formatBytes($bytes, $precision = 2)
    {
        $units = array('B', 'KB', 'MB', 'GB', 'TB');

        $bytes = max($bytes, 0);
        $pow = floor(($bytes ? log($bytes) : 0) / log(1024));
        $pow = min($pow, count($units) - 1);

        // Uncomment one of the following alternatives
        $bytes /= (1 << (10 * $pow));

        return round($bytes, $precision) . '' . $units[$pow];
    }
    /**
     * check backup task canceled or not
     *
     * @var string $task_id
     *
     * @since 0.9.1
     */
    public function check_cancel_backup($task_id)
    {
        if(WPvivid_taskmanager::get_task($task_id)!==false)
        {
            $task=new WPvivid_Backup_Task($task_id);

            if($task->is_cancel_file_exist())
            {
                if($this->wpvivid_log->log_file_handle==false)
                {
                    $this->wpvivid_log->OpenLogFile(WPvivid_taskmanager::get_task_options($task_id,'log_file_name'));
                }
                $this->wpvivid_log->WriteLog('Backup cancelled.','notice');

                $task->update_status('cancel');
                //WPvivid_taskmanager::update_backup_task_status($task_id,false,'cancel',false);
                $this->add_clean_backing_up_data_event($task_id);
                WPvivid_Schedule::clear_monitor_schedule($task_id);
                WPvivid_taskmanager::delete_task($task_id);
                die();
            }
        }
    }
    public function flush($txt, $from_mainwp=false) {
        if(!$from_mainwp) {
            $ret['result'] = 'success';
            $ret['task_id'] = $txt;
            $txt = wp_json_encode($ret);
        }
        else{
            $ret['result']='success';
            $txt = '<mainwp>' . base64_encode( serialize( $ret ) ) . '</mainwp>';
        }
        if(!headers_sent()){
            header('Content-Length: '.( ( ! empty( $txt ) ) ? strlen( $txt ) : '0' ));
            header('Connection: close');
            header('Content-Encoding: none');
        }
        if (session_id())
            session_write_close();
        echo wp_json_encode($ret);

        if(function_exists('fastcgi_finish_request'))
        {
            fastcgi_finish_request();
        }
        else
        {
            if(ob_get_level()>0)
                ob_flush();
            flush();
        }
    }

    /**
     * return initialization download page data
     *
     * @var string $task_id
     *
     * @since 0.9.48
     */
    public function init_download_page()
    {
        check_ajax_referer( 'wpvivid_ajax', 'nonce' );
        $check=current_user_can('manage_options');
        $check=apply_filters('wpvivid_ajax_check_security',$check);
        if(!$check)
        {
            die();
        }
        try {
            if (isset($_POST['backup_id']) && !empty($_POST['backup_id']) && is_string($_POST['backup_id'])) {
                $backup_id = sanitize_key($_POST['backup_id']);
                $backup = WPvivid_Backuplist::get_backup_by_id($backup_id);
                if ($backup === false) {
                    $ret['result'] = WPVIVID_FAILED;
                    $ret['error'] = 'backup id not found';
                    echo wp_json_encode($ret);
                    die();
                }

                $backup_item = new WPvivid_Backup_Item($backup);

                $backup_files = $backup_item->get_download_backup_files($backup_id);

                if ($backup_files['result'] == WPVIVID_SUCCESS) {
                    $ret['result'] = WPVIVID_SUCCESS;

                    $remote = $backup_item->get_remote();

                    foreach ($backup_files['files'] as $file) {
                        $path = $this->get_backup_path($backup_item, $file['file_name']);
                        //$path = $backup_item->get_local_path() . $file['file_name'];

                        if (file_exists($path)) {
                            if (filesize($path) == $file['size']) {
                                if (WPvivid_taskmanager::get_download_task_v2($file['file_name']))
                                    WPvivid_taskmanager::delete_download_task_v2($file['file_name']);
                                $ret['files'][$file['file_name']]['status'] = 'completed';
                                $ret['files'][$file['file_name']]['size'] = size_format(filesize($path), 2);
                                $ret['files'][$file['file_name']]['download_path'] = $path;
                                $download_url = $this->get_backup_url($backup_item, $file['file_name']);
                                $ret['files'][$file['file_name']]['download_url'] = $download_url;

                                continue;
                            }
                        }
                        $ret['files'][$file['file_name']]['size'] = size_format($file['size'], 2);

                        if (empty($remote)) {
                            $ret['files'][$file['file_name']]['status'] = 'file_not_found';
                        } else {
                            $task = WPvivid_taskmanager::get_download_task_v2($file['file_name']);
                            if ($task === false) {
                                $ret['files'][$file['file_name']]['status'] = 'need_download';
                            } else {
                                $ret['result'] = WPVIVID_SUCCESS;
                                if ($task['status'] === 'running') {
                                    $ret['files'][$file['file_name']]['status'] = 'running';
                                    $ret['files'][$file['file_name']]['progress_text'] = $task['progress_text'];
                                    if (file_exists($path)) {
                                        $ret['files'][$file['file_name']]['downloaded_size'] = size_format(filesize($path), 2);
                                    } else {
                                        $ret['files'][$file['file_name']]['downloaded_size'] = '0';
                                    }
                                } elseif ($task['status'] === 'timeout') {
                                    $ret['files'][$file['file_name']]['status'] = 'timeout';
                                    $ret['files'][$file['file_name']]['progress_text'] = $task['progress_text'];
                                    WPvivid_taskmanager::delete_download_task_v2($file['file_name']);
                                } elseif ($task['status'] === 'completed') {
                                    $ret['files'][$file['file_name']]['status'] = 'completed';
                                    WPvivid_taskmanager::delete_download_task_v2($file['file_name']);
                                } elseif ($task['status'] === 'error') {
                                    $ret['files'][$file['file_name']]['status'] = 'error';
                                    $ret['files'][$file['file_name']]['error'] = $task['error'];
                                    WPvivid_taskmanager::delete_download_task_v2($file['file_name']);
                                }
                            }
                        }
                    }
                } else {
                    $ret = $backup_files;
                }


                if (!class_exists('WPvivid_Files_List'))
                    include_once WPVIVID_PLUGIN_DIR .'/admin/partials/wpvivid-backup-restore-page-display.php';

                $files_list = new WPvivid_Files_List();

                $files_list->set_files_list($ret['files'], $backup_id);
                $files_list->prepare_items();
                ob_start();
                $files_list->display();
                $ret['html'] = ob_get_clean();

                echo wp_json_encode($ret);
            }
        }
        catch (Exception $error) {
            $message = 'An exception has occurred. class: '.get_class($error).';msg: '.$error->getMessage().';code: '.$error->getCode().';line: '.$error->getLine().';in_file: '.$error->getFile().';';
            error_log($message);
            echo wp_json_encode(array('result'=>'failed','error'=>$message));
        }
        die();
    }

    public function get_download_page_ex()
    {
        check_ajax_referer( 'wpvivid_ajax', 'nonce' );
        $check=current_user_can('manage_options');
        $check=apply_filters('wpvivid_ajax_check_security',$check);
        if(!$check)
        {
            die();
        }
        try {
            if (isset($_POST['backup_id']) && !empty($_POST['backup_id']) && is_string($_POST['backup_id'])) {
                if (isset($_POST['page'])) {
                    $page = sanitize_key($_POST['page']);
                } else {
                    $page = 1;
                }

                $backup_id = sanitize_key($_POST['backup_id']);
                $backup = WPvivid_Backuplist::get_backup_by_id($backup_id);
                if ($backup === false) {
                    $ret['result'] = WPVIVID_FAILED;
                    $ret['error'] = 'backup id not found';
                    echo wp_json_encode($ret);
                    die();
                }

                $backup_item = new WPvivid_Backup_Item($backup);

                $backup_files = $backup_item->get_download_backup_files($backup_id);

                if ($backup_files['result'] == WPVIVID_SUCCESS) {
                    $ret['result'] = WPVIVID_SUCCESS;

                    $remote = $backup_item->get_remote();

                    foreach ($backup_files['files'] as $file) {
                        $path = $this->get_backup_path($backup_item, $file['file_name']);
                        //$path = $backup_item->get_local_path() . $file['file_name'];
                        if (file_exists($path)) {
                            if (filesize($path) == $file['size']) {
                                if (WPvivid_taskmanager::get_download_task_v2($file['file_name']))
                                    WPvivid_taskmanager::delete_download_task_v2($file['file_name']);
                                $ret['files'][$file['file_name']]['status'] = 'completed';
                                $ret['files'][$file['file_name']]['size'] = size_format(filesize($path), 2);
                                $ret['files'][$file['file_name']]['download_path'] = $path;
                                $download_url = $this->get_backup_url($backup_item, $file['file_name']);
                                $ret['files'][$file['file_name']]['download_url'] = $download_url;

                                continue;
                            }
                        }
                        $ret['files'][$file['file_name']]['size'] = size_format($file['size'], 2);

                        if (empty($remote)) {
                            $ret['files'][$file['file_name']]['status'] = 'file_not_found';
                        } else {
                            $task = WPvivid_taskmanager::get_download_task_v2($file['file_name']);
                            if ($task === false) {
                                $ret['files'][$file['file_name']]['status'] = 'need_download';
                            } else {
                                $ret['result'] = WPVIVID_SUCCESS;
                                if ($task['status'] === 'running') {
                                    $ret['files'][$file['file_name']]['status'] = 'running';
                                    $ret['files'][$file['file_name']]['progress_text'] = $task['progress_text'];
                                    if (file_exists($path)) {
                                        $ret['files'][$file['file_name']]['downloaded_size'] = size_format(filesize($path), 2);
                                    } else {
                                        $ret['files'][$file['file_name']]['downloaded_size'] = '0';
                                    }
                                } elseif ($task['status'] === 'timeout') {
                                    $ret['files'][$file['file_name']]['status'] = 'timeout';
                                    $ret['files'][$file['file_name']]['progress_text'] = $task['progress_text'];
                                    WPvivid_taskmanager::delete_download_task_v2($file['file_name']);
                                } elseif ($task['status'] === 'completed') {
                                    $ret['files'][$file['file_name']]['status'] = 'completed';
                                    WPvivid_taskmanager::delete_download_task_v2($file['file_name']);
                                } elseif ($task['status'] === 'error') {
                                    $ret['files'][$file['file_name']]['status'] = 'error';
                                    $ret['files'][$file['file_name']]['error'] = $task['error'];
                                    WPvivid_taskmanager::delete_download_task_v2($file['file_name']);
                                }
                            }
                        }
                    }
                } else {
                    $ret = $backup_files;
                }

                if (!class_exists('WPvivid_Files_List'))
                    include_once WPVIVID_PLUGIN_DIR .'/admin/partials/wpvivid-backup-restore-page-display.php';

                $files_list = new WPvivid_Files_List();

                $files_list->set_files_list($ret['files'], $backup_id, $page);
                $files_list->prepare_items();
                ob_start();
                $files_list->display();
                $ret['html'] = ob_get_clean();

                echo wp_json_encode($ret);
            }
        }
        catch (Exception $error) {
            $message = 'An exception has occurred. class: '.get_class($error).';msg: '.$error->getMessage().';code: '.$error->getCode().';line: '.$error->getLine().';in_file: '.$error->getFile().';';
            error_log($message);
            echo wp_json_encode(array('result'=>'failed','error'=>$message));
        }
        die();
    }

    /**
     * prepare download backup
     *
     * Retrieve files from the server
     *
     * @var string $task_id
     *
     * @since 0.9.1
     */
    public function prepare_download_backup()
    {
        check_ajax_referer( 'wpvivid_ajax', 'nonce' );
        $check=current_user_can('manage_options');
        $check=apply_filters('wpvivid_ajax_check_security',$check);
        if(!$check)
        {
            die();
        }
        $this->end_shutdown_function=false;
        register_shutdown_function(array($this,'deal_prepare_download_shutdown_error'));
        $id=uniqid('wpvivid-');
        $log_file_name=$id.'_download';
        $this->wpvivid_download_log->OpenLogFile($log_file_name);
        $this->wpvivid_download_log->WriteLog('Prepare download backup.','notice');
        $this->wpvivid_download_log->WriteLogHander();
        try {
            if (!isset($_POST['backup_id']) || empty($_POST['backup_id']) || !is_string($_POST['backup_id']) || !isset($_POST['file_name']) || empty($_POST['file_name']) || !is_string($_POST['file_name'])) {
                $this->end_shutdown_function=true;
                die();
            }
            $download_info = array();
            $download_info['backup_id'] = sanitize_key($_POST['backup_id']);
            $download_info['file_name'] = sanitize_text_field($_POST['file_name']);
            @set_time_limit(600);
            if (session_id())
                session_write_close();

            $downloader = new WPvivid_downloader();
            $downloader->ready_download($download_info);

            $ret['result'] = 'success';
            echo wp_json_encode($ret);
        }
        catch (Exception $error) {
            $message = 'An exception has occurred. class: '.get_class($error).';msg: '.$error->getMessage().';code: '.$error->getCode().';line: '.$error->getLine().';in_file: '.$error->getFile().';';
            if($this->wpvivid_download_log){
                $this->wpvivid_download_log->WriteLog($message ,'error');
                $this->wpvivid_download_log->CloseFile();
                WPvivid_error_log::create_error_log($this->wpvivid_download_log->log_file);
            }
            else {
                $id = uniqid('wpvivid-');
                $log_file_name = $id . '_download';
                if(!class_exists('WPvivid_Log'))
                {
                    include_once WPVIVID_PLUGIN_DIR . '/includes/class-wpvivid-log.php';
                }
                $log = new WPvivid_Log();
                $log->CreateLogFile($log_file_name, 'no_folder', 'download');
                $log->WriteLog($message, 'error');
                $log->CloseFile();
                WPvivid_error_log::create_error_log($log->log_file);
            }
            error_log($message);
            $this->end_shutdown_function=true;
            echo wp_json_encode(array('result'=>'failed','error'=>$message));
            die();
        }
        $this->wpvivid_download_log->CloseFile();
        $this->end_shutdown_function=true;
        die();
    }

    public function get_download_progress()
    {
        check_ajax_referer( 'wpvivid_ajax', 'nonce' );
        $check=current_user_can('manage_options');
        $check=apply_filters('wpvivid_ajax_check_security',$check);
        if(!$check)
        {
            die();
        }
        try {
            if (isset($_POST['backup_id'])) {
                $backup_id = sanitize_key($_POST['backup_id']);
                $ret['result'] = WPVIVID_SUCCESS;
                $ret['files'] = array();
                $ret['need_update'] = false;

                $backup = WPvivid_Backuplist::get_backup_by_id($backup_id);
                if ($backup === false) {
                    $ret['result'] = WPVIVID_FAILED;
                    $ret['error'] = 'backup id not found';
                    return $ret;
                }

                $backup_item = new WPvivid_Backup_Item($backup);

                $backup_files = $backup_item->get_download_backup_files($backup_id);

                foreach ($backup_files['files'] as $file) {
                    $path = $this->get_backup_path($backup_item, $file['file_name']);
                    //$path = $backup_item->get_local_path() . $file['file_name'];
                    if (file_exists($path)) {
                        $downloaded_size = size_format(filesize($path), 2);
                    } else {
                        $downloaded_size = '0';
                    }
                    $file['size'] = size_format($file['size'], 2);

                    $task = WPvivid_taskmanager::get_download_task_v2($file['file_name']);
                    if ($task === false) {
                        $ret['files'][$file['file_name']]['status'] = 'need_download';
                        $ret['files'][$file['file_name']]['html'] = '<div class="wpvivid-element-space-bottom">
                                                                        <span class="wpvivid-element-space-right">Retriving (remote storage to web server)</span><span class="wpvivid-element-space-right">|</span><span>File Size: </span><span class="wpvivid-element-space-right">' . $file['size'] . '</span><span class="wpvivid-element-space-right">|</span><span>Downloaded Size: </span><span>0</span>
                                                                   </div>
                                                                   <div style="width:100%;height:10px; background-color:#dcdcdc;">
                                                                        <div style="background-color:#0085ba; float:left;width:0%;height:10px;"></div>
                                                                   </div>';
                        $ret['need_update'] = true;
                    } else {
                        if ($task['status'] === 'running') {
                            $ret['files'][$file['file_name']]['status'] = 'running';
                            $ret['files'][$file['file_name']]['progress_text'] = $task['progress_text'];
                            $ret['files'][$file['file_name']]['html'] = '<div class="wpvivid-element-space-bottom">
                                                                            <span class="wpvivid-element-space-right">Retriving (remote storage to web server)</span><span class="wpvivid-element-space-right">|</span><span>File Size: </span><span class="wpvivid-element-space-right">' . $file['size'] . '</span><span class="wpvivid-element-space-right">|</span><span>Downloaded Size: </span><span>' . $downloaded_size . '</span>
                                                                        </div>
                                                                        <div style="width:100%;height:10px; background-color:#dcdcdc;">
                                                                            <div style="background-color:#0085ba; float:left;width:' . $task['progress_text'] . '%;height:10px;"></div>
                                                                        </div>';
                            $ret['need_update'] = true;
                        } elseif ($task['status'] === 'timeout') {
                            $ret['files'][$file['file_name']]['status'] = 'timeout';
                            $ret['files'][$file['file_name']]['progress_text'] = $task['progress_text'];
                            $ret['files'][$file['file_name']]['html'] = '<div class="wpvivid-element-space-bottom">
                                                                            <span>Download timeout, please retry.</span>
                                                                         </div>
                                                                         <div>
                                                                            <span>' . __('File Size: ', 'wpvivid-backuprestore') . '</span><span class="wpvivid-element-space-right">' . $file['size'] . '</span><span class="wpvivid-element-space-right">|</span><span class="wpvivid-element-space-right"><a class="wpvivid-download" style="cursor: pointer;">'. __('Prepare to Download', 'wpvivid-backuprestore').'</a></span>
                                                                        </div>';
                            WPvivid_taskmanager::delete_download_task_v2($file['file_name']);
                        } elseif ($task['status'] === 'completed') {
                            $ret['files'][$file['file_name']]['status'] = 'completed';
                            $ret['files'][$file['file_name']]['html'] = '<span>' . __('File Size: ', 'wpvivid-backuprestore') . '</span><span class="wpvivid-element-space-right">' . $file['size'] . '</span><span class="wpvivid-element-space-right">|</span><span class="wpvivid-element-space-right wpvivid-ready-download"><a style="cursor: pointer;">'. __('Download', 'wpvivid-backuprestore').'</a></span>';
                            WPvivid_taskmanager::delete_download_task_v2($file['file_name']);
                        } elseif ($task['status'] === 'error') {
                            $ret['files'][$file['file_name']]['status'] = 'error';
                            $ret['files'][$file['file_name']]['error'] = $task['error'];
                            $ret['files'][$file['file_name']]['html'] = '<div class="wpvivid-element-space-bottom">
                                                                            <span>' . $task['error'] . '</span>
                                                                         </div>
                                                                         <div>
                                                                            <span>' . __('File Size: ', 'wpvivid-backuprestore') . '</span><span class="wpvivid-element-space-right">' . $file['size'] . '</span><span class="wpvivid-element-space-right">|</span><span class="wpvivid-element-space-right"><a class="wpvivid-download" style="cursor: pointer;">'. __('Prepare to Download', 'wpvivid-backuprestore').'</a></span>
                                                                         </div>';
                            WPvivid_taskmanager::delete_download_task_v2($file['file_name']);
                        }
                    }
                }
                echo wp_json_encode($ret);
            }
        }
        catch (Exception $error) {
            $message = 'An exception has occurred. class: '.get_class($error).';msg: '.$error->getMessage().';code: '.$error->getCode().';line: '.$error->getLine().';in_file: '.$error->getFile().';';
            error_log($message);
            echo wp_json_encode(array('result'=>'failed','error'=>$message));
        }
        die();
    }

    public function deal_prepare_download_shutdown_error()
    {
        if($this->end_shutdown_function==false) {
            $last_error = error_get_last();
            if (!empty($last_error) && !in_array($last_error['type'], array(E_NOTICE,E_WARNING,E_USER_NOTICE,E_USER_WARNING,E_DEPRECATED), true)) {
                $error = $last_error;
            } else {
                $error = false;
            }
            $ret['result'] = 'failed';
            if ($error === false) {
                $ret['error'] = 'unknown Error';
            } else {
                $ret['error'] = 'type: '. $error['type'] . ', ' . $error['message'] . ' file:' . $error['file'] . ' line:' . $error['line'];
                error_log($ret['error']);
            }
            if($this->wpvivid_download_log){
                $this->wpvivid_download_log->WriteLog($ret['error'] ,'error');
                $this->wpvivid_download_log->CloseFile();
                WPvivid_error_log::create_error_log($this->wpvivid_download_log->log_file);
            }
            else {
                $id = uniqid('wpvivid-');
                $log_file_name = $id . '_download';
                if(!class_exists('WPvivid_Log'))
                {
                    include_once WPVIVID_PLUGIN_DIR . '/includes/class-wpvivid-log.php';
                }
                $log = new WPvivid_Log();
                $log->CreateLogFile($log_file_name, 'no_folder', 'download');
                $log->WriteLog($ret['error'], 'notice');
                $log->CloseFile();
                WPvivid_error_log::create_error_log($log->log_file);
            }
            echo wp_json_encode($ret);
            die();
        }
    }

    public function init_download($backup_id)
    {
        if(empty($backup_id))
        {
            $ret['result']=WPVIVID_SUCCESS;
            $ret['data']=array();
            return $ret;
        }
        $ret['result']=WPVIVID_SUCCESS;
        $backup=WPvivid_Backuplist::get_backup_by_id($backup_id);

        if($backup===false)
        {
            $ret['result']=WPVIVID_FAILED;
            $ret['error']='backup id not found';
            return $ret;
        }

        $backup_item=new WPvivid_Backup_Item($backup);
        $ret=$backup_item->get_download_backup_files($backup_id);
        if($ret['result']==WPVIVID_SUCCESS){
            $ret=$backup_item->get_download_progress($backup_id, $ret['files']);
            WPvivid_taskmanager::update_download_cache($backup_id,$ret);
        }
        return $ret;
    }

    /**
     * download backup file
     *
     * @since 0.9.1
     */
    public function download_backup()
    {
        check_ajax_referer( 'wpvivid_ajax', 'nonce' );
        $check=current_user_can('manage_options');
        $check=apply_filters('wpvivid_ajax_check_security',$check);
        if(!$check)
        {
            die();
        }
        try {
            if (isset($_REQUEST['backup_id']) && isset($_REQUEST['file_name'])) {
                if (!empty($_REQUEST['backup_id']) && is_string($_REQUEST['backup_id'])) {
                    $backup_id = sanitize_key($_REQUEST['backup_id']);
                } else {
                    die();
                }

                if (!empty($_REQUEST['file_name']) && is_string($_REQUEST['file_name'])) {
                    //$file_name=sanitize_file_name($_REQUEST['file_name']);
                    $file_name = sanitize_text_field($_REQUEST['file_name']);
                } else {
                    die();
                }

                $cache = WPvivid_taskmanager::get_download_cache($backup_id);
                if ($cache === false) {
                    $this->init_download($backup_id);
                    $cache = WPvivid_taskmanager::get_download_cache($backup_id);
                }
                $path = false;
                if (array_key_exists($file_name, $cache['files'])) {
                    if ($cache['files'][$file_name]['status'] == 'completed') {
                        $path = $cache['files'][$file_name]['download_path'];
                    }
                }
                if ($path !== false) {
                    if (file_exists($path)) {
                        if (session_id())
                            session_write_close();

                        @ini_set( 'memory_limit', '1024M' );

                        $size = filesize($path);
                        if (!headers_sent()) {
                            header('Content-Description: File Transfer');
                            header('Content-Type: application/zip');
                            header('Content-Disposition: attachment; filename="' . basename($path) . '"');
                            header('Cache-Control: must-revalidate');
                            header('Content-Length: ' . $size);
                            header('Content-Transfer-Encoding: binary');
                        }

                        if ($size < 1024 * 1024 * 60) {
                            ob_end_clean();
                            readfile($path);
                            exit;
                        } else {
                            ob_end_clean();
                            $download_rate = 1024 * 10;
                            $file = fopen($path, "r");
                            while (!feof($file)) {
                                @set_time_limit(20);
                                // send the current file part to the browser
                                print fread($file, round($download_rate * 1024));
                                // flush the content to the browser
                                ob_flush();
                                flush();
                                // sleep one second
                                sleep(1);
                            }
                            fclose($file);
                            exit;
                        }
                    }
                }
            }
        }
        catch (Exception $error) {
            $message = 'An exception has occurred. class: '.get_class($error).';msg: '.$error->getMessage().';code: '.$error->getCode().';line: '.$error->getLine().';in_file: '.$error->getFile().';';
            error_log($message);
            echo wp_json_encode(array('result'=>'failed','error'=>$message));
            die();
        }
        $admin_url = admin_url();
        echo '<a href="'.esc_url($admin_url).'admin.php?page=WPvivid">file not found. please retry again.</a>';
        die();
    }

    /**
     * List backup record
     *
     * @since 0.9.1
     */
    public function get_backup_list()
    {
        check_ajax_referer( 'wpvivid_ajax', 'nonce' );
        $check=current_user_can('manage_options');
        $check=apply_filters('wpvivid_ajax_check_security',$check);
        if(!$check)
        {
            die();
        };
        try
        {
            $json['result'] = 'success';
            $html = '';
            $html = apply_filters('wpvivid_add_backup_list', $html);
            $json['html'] = $html;
            echo wp_json_encode($json);
        }
        catch (Exception $error)
        {
            $message = 'An exception has occurred. class: '.get_class($error).';msg: '.$error->getMessage().';code: '.$error->getCode().';line: '.$error->getLine().';in_file: '.$error->getFile().';';
            error_log($message);
            echo wp_json_encode(array('result'=>'failed','error'=>$message));
            die();
        }
        die();
    }
    /**
     * Delete backup record
     *
     * @since 0.9.1
     */
    public function delete_backup()
    {
        check_ajax_referer( 'wpvivid_ajax', 'nonce' );
        $check=current_user_can('manage_options');
        $check=apply_filters('wpvivid_ajax_check_security',$check);
        if(!$check)
        {
            die();
        }
        try
        {
            if (isset($_POST['backup_id']) && !empty($_POST['backup_id']) && is_string($_POST['backup_id']) && isset($_POST['force']))
            {
                if ($_POST['force'] == 0 || $_POST['force'] == 1)
                {
                    $force_del = sanitize_key($_POST['force']);
                } else {
                    $force_del = 0;
                }
                $backup_id = sanitize_key($_POST['backup_id']);
                $ret = $this->delete_backup_by_id($backup_id, $force_del);
                echo wp_json_encode($ret);
            }
        }
        catch (Exception $error) {
            $message = 'An exception has occurred. class: '.get_class($error).';msg: '.$error->getMessage().';code: '.$error->getCode().';line: '.$error->getLine().';in_file: '.$error->getFile().';';
            error_log($message);
            echo wp_json_encode(array('result'=>'failed','error'=>$message));
            die();
        }
        die();
    }
    /**
     * Delete backup records
     *
     * @since 0.9.1
     */
    public function delete_backup_array()
    {
        check_ajax_referer( 'wpvivid_ajax', 'nonce' );
        $check=current_user_can('manage_options');
        $check=apply_filters('wpvivid_ajax_check_security',$check);
        if(!$check)
        {
            die();
        }
        try
        {
            if (isset($_POST['backup_id']) && !empty($_POST['backup_id']) && is_array($_POST['backup_id']))
            {
                $backup_ids = $_POST['backup_id'];
                $ret = array();
                foreach ($backup_ids as $backup_id)
                {
                    $backup_id = sanitize_key($backup_id);
                    $ret = $this->delete_backup_by_id($backup_id);
                }
                $html = '';
                $html = apply_filters('wpvivid_add_backup_list', $html);
                $ret['html'] = $html;
                echo wp_json_encode($ret);
            }
        }
        catch (Exception $error)
        {
            $message = 'An exception has occurred. class: '.get_class($error).';msg: '.$error->getMessage().';code: '.$error->getCode().';line: '.$error->getLine().';in_file: '.$error->getFile().';';
            error_log($message);
            echo wp_json_encode(array('result'=>'failed','error'=>$message));
            die();
        }
        die();
    }

    public function delete_backup_by_id($backup_id,$force=0)
    {
        $backup=WPvivid_Backuplist::get_backup_by_id($backup_id);
        if(!$backup)
        {
            $ret['result']='failed';
            $ret['error']=__('Retrieving the backup(s) information failed while deleting the selected backup(s). Please try again later.', 'wpvivid-backuprestore');
            return $ret;
        }

        $backup_item=new WPvivid_Backup_Item($backup);
        if($backup_item->is_lock())
        {
            if($force==0)
            {
                $ret['result']='failed';
                $ret['error']=__('Unable to delete the locked backup. Please unlock it first and try again.', 'wpvivid-backuprestore');
                return $ret;
            }
        }
        WPvivid_Backuplist::delete_backup($backup_id);
        $backup_item->cleanup_local_backup();
        $backup_item->cleanup_remote_backup();

        $html = '';
        $html = apply_filters('wpvivid_add_backup_list', $html);
        $ret['html'] = $html;
        $ret['result']='success';
        return $ret;
    }

    public function delete_local_backup($backup_ids)
    {
        foreach ($backup_ids as $backup_id)
        {
            $backup=WPvivid_Backuplist::get_backup_by_id($backup_id);
            if(!$backup)
            {
               continue;
            }

            if(array_key_exists('lock',$backup))
            {
               continue;
            }

            $backup_item = new WPvivid_Backup_Item($backup);
            $backup_item->cleanup_local_backup();
        }
    }

    public function delete_task()
    {
        check_ajax_referer( 'wpvivid_ajax', 'nonce' );
        $check=current_user_can('manage_options');
        $check=apply_filters('wpvivid_ajax_check_security',$check);
        if(!$check)
        {
            die();
        };
        try {
            if (isset($_POST['task_id']) && !empty($_POST['task_id']) && is_string($_POST['task_id'])) {
                $task_id = sanitize_key($_POST['task_id']);
                WPvivid_taskmanager::delete_task($task_id);

                $json['result'] = 'success';
                echo wp_json_encode($json);
            }
        }
        catch (Exception $error) {
            $message = 'An exception has occurred. class: '.get_class($error).';msg: '.$error->getMessage().';code: '.$error->getCode().';line: '.$error->getLine().';in_file: '.$error->getFile().';';
            error_log($message);
            echo wp_json_encode(array('result'=>'failed','error'=>$message));
            die();
        }
        die();
    }
    /**
     * Add remote storage
     *
     * @since 0.9.1
     */
    public function add_remote()
    {
        check_ajax_referer( 'wpvivid_ajax', 'nonce' );
        $check=current_user_can('manage_options');
        $check=apply_filters('wpvivid_ajax_check_security',$check);
        if(!$check)
        {
            die();
        }
        try {
            if (empty($_POST) || !isset($_POST['remote']) || !is_string($_POST['remote']) || !isset($_POST['type']) || !is_string($_POST['type'])) {
                die();
            }
            $json = $_POST['remote'];
            $json = stripslashes($json);
            $remote_options = json_decode($json, true);
            if (is_null($remote_options)) {
                die();
            }

            $remote_options['type'] = sanitize_text_field($_POST['type']);
            if ($remote_options['type'] == 'amazons3')
            {
                if(isset($remote_options['s3Path']))
                    $remote_options['s3Path'] = rtrim($remote_options['s3Path'], "/");
            }
            $ret = $this->remote_collection->add_remote($remote_options);

            if ($ret['result'] == 'success') {
                $html = '';
                $html = apply_filters('wpvivid_add_remote_storage_list', $html);
                $ret['html'] = $html;
                $pic = '';
                $pic = apply_filters('wpvivid_schedule_add_remote_pic', $pic);
                $ret['pic'] = $pic;
                $dir = '';
                $dir = apply_filters('wpvivid_get_remote_directory', $dir);
                $ret['dir'] = $dir;
                $schedule_local_remote = '';
                $schedule_local_remote = apply_filters('wpvivid_schedule_local_remote', $schedule_local_remote);
                $ret['local_remote'] = $schedule_local_remote;
                $remote_storage = '';
                $remote_storage = apply_filters('wpvivid_remote_storage', $remote_storage);
                $ret['remote_storage'] = $remote_storage;
                $remote_select_part = '';
                $remote_select_part = apply_filters('wpvivid_remote_storage_select_part', $remote_select_part);
                $ret['remote_select_part'] = $remote_select_part;
                $default = array();
                $remote_array = apply_filters('wpvivid_archieve_remote_array', $default);
                $ret['remote_array'] = $remote_array;
                $success_msg = __('You have successfully added a remote storage.', 'wpvivid-backuprestore');
                $ret['notice'] = apply_filters('wpvivid_add_remote_notice', true, $success_msg);
            }
            else{
                $ret['notice'] = apply_filters('wpvivid_add_remote_notice', false, $ret['error']);
            }
        }
        catch (Exception $error) {
            $message = 'An exception has occurred. class: '.get_class($error).';msg: '.$error->getMessage().';code: '.$error->getCode().';line: '.$error->getLine().';in_file: '.$error->getFile().';';
            error_log($message);
            echo wp_json_encode(array('result'=>'failed','error'=>$message));
            die();
        }
        echo wp_json_encode($ret);
        die();
    }
    /**
     * Delete remote storage
     *
     * @since 0.9.1
     */
    public function delete_remote()
    {
        try {
            check_ajax_referer( 'wpvivid_ajax', 'nonce' );
            $check=current_user_can('manage_options');
            $check=apply_filters('wpvivid_ajax_check_security',$check);
            if(!$check)
            {
                die();
            };
            if (empty($_POST) || !isset($_POST['remote_id']) || !is_string($_POST['remote_id'])) {
                die();
            }
            $id = sanitize_key($_POST['remote_id']);
            if (WPvivid_Setting::delete_remote_option($id)) {
                $remote_selected = WPvivid_Setting::get_user_history('remote_selected');
                if (in_array($id, $remote_selected)) {
                    WPvivid_Setting::update_user_history('remote_selected', array());
                }
                $ret['result'] = 'success';
                $html = '';
                $html = apply_filters('wpvivid_add_remote_storage_list', $html);
                $ret['html'] = $html;
                $pic = '';
                $pic = apply_filters('wpvivid_schedule_add_remote_pic', $pic);
                $ret['pic'] = $pic;
                $dir = '';
                $dir = apply_filters('wpvivid_get_remote_directory', $dir);
                $ret['dir'] = $dir;
                $schedule_local_remote = '';
                $schedule_local_remote = apply_filters('wpvivid_schedule_local_remote', $schedule_local_remote);
                $ret['local_remote'] = $schedule_local_remote;
                $remote_storage = '';
                $remote_storage = apply_filters('wpvivid_remote_storage', $remote_storage);
                $ret['remote_storage'] = $remote_storage;
            } else {
                $ret['result'] = 'failed';
                $ret['error'] = __('Failed to delete the remote storage, can not retrieve the storage infomation. Please try again.', 'wpvivid-backuprestore');
            }
        }
        catch (Exception $error) {
            $message = 'An exception has occurred. class: '.get_class($error).';msg: '.$error->getMessage().';code: '.$error->getCode().';line: '.$error->getLine().';in_file: '.$error->getFile().';';
            error_log($message);
            echo wp_json_encode(array('result'=>'failed','error'=>$message));
            die();
        }
        echo wp_json_encode($ret);
        die();
    }

    /**
     * Retrieve remote storage
     *
     * @since 0.9.8
     */
    public function retrieve_remote()
    {
        try {
            check_ajax_referer( 'wpvivid_ajax', 'nonce' );
            $check=current_user_can('manage_options');
            $check=apply_filters('wpvivid_ajax_check_security',$check);
            if(!$check)
            {
                die();
            }
            if (empty($_POST) || !isset($_POST['remote_id']) || !is_string($_POST['remote_id'])) {
                die();
            }
            $id = sanitize_key($_POST['remote_id']);
            $remoteslist = WPvivid_Setting::get_all_remote_options();
            $ret['result'] = WPVIVID_FAILED;
            $ret['error'] = __('Failed to get the remote storage information. Please try again later.', 'wpvivid-backuprestore');
            foreach ($remoteslist as $key => $value) {
                if ($key == $id) {
                    if ($key === 'remote_selected') {
                        continue;
                    }
                    $value = apply_filters('wpvivid_encrypt_remote_password', $value);
                    $ret = $value;
                    $ret['result'] = WPVIVID_SUCCESS;
                    break;
                }
            }
        }
        catch (Exception $error) {
            $message = 'An exception has occurred. class: '.get_class($error).';msg: '.$error->getMessage().';code: '.$error->getCode().';line: '.$error->getLine().';in_file: '.$error->getFile().';';
            error_log($message);
            echo wp_json_encode(array('result'=>'failed','error'=>$message));
            die();
        }
        echo wp_json_encode($ret);
        die();
    }
    /**
     * Edit remote storage
     *
     * @since 0.9.8
     */
    public function edit_remote()
    {
        check_ajax_referer( 'wpvivid_ajax', 'nonce' );
        $check=current_user_can('manage_options');
        $check=apply_filters('wpvivid_ajax_check_security',$check);
        if(!$check)
        {
            die();
        }
        try {
            if (empty($_POST) || !isset($_POST['remote']) || !is_string($_POST['remote']) || !isset($_POST['id']) || !is_string($_POST['id']) || !isset($_POST['type']) || !is_string($_POST['type'])) {
                die();
            }
            $json = sanitize_text_field($_POST['remote']);
            $json = stripslashes($json);
            $remote_options = json_decode($json, true);
            if (is_null($remote_options)) {
                die();
            }
            $remote_options['type'] = sanitize_text_field($_POST['type']);
            if ($remote_options['type'] == 'amazons3')
            {
                if(isset($remote_options['s3Path']))
                    $remote_options['s3Path'] = rtrim($remote_options['s3Path'], "/");
            }
            $id=sanitize_key($_POST['id']);
            $old_remote=WPvivid_Setting::get_remote_option($id);
            foreach ($old_remote as $key=>$value)
            {
                if(isset($remote_options[$key]))
                    $old_remote[$key]=$remote_options[$key];
            }

            $ret = $this->remote_collection->update_remote($id, $old_remote);

            if ($ret['result'] == 'success') {
                $ret['result'] = WPVIVID_SUCCESS;
                $html = '';
                $html = apply_filters('wpvivid_add_remote_storage_list', $html);
                $ret['html'] = $html;
                $pic = '';
                $pic = apply_filters('wpvivid_schedule_add_remote_pic', $pic);
                $ret['pic'] = $pic;
                $dir = '';
                $dir = apply_filters('wpvivid_get_remote_directory', $dir);
                $ret['dir'] = $dir;
                $schedule_local_remote = '';
                $schedule_local_remote = apply_filters('wpvivid_schedule_local_remote', $schedule_local_remote);
                $ret['local_remote'] = $schedule_local_remote;
                $remote_storage = '';
                $remote_storage = apply_filters('wpvivid_remote_storage', $remote_storage);
                $ret['remote_storage'] = $remote_storage;
                $remote_select_part = '';
                $remote_select_part = apply_filters('wpvivid_remote_storage_select_part', $remote_select_part);
                $ret['remote_select_part'] = $remote_select_part;
                $default = array();
                $remote_array = apply_filters('wpvivid_archieve_remote_array', $default);
                $ret['remote_array'] = $remote_array;
                $success_msg = 'You have successfully updated the account information of your remote storage.';
                $ret['notice'] = apply_filters('wpvivid_add_remote_notice', true, $success_msg);
            }
            else{
                $ret['notice'] = apply_filters('wpvivid_add_remote_notice', false, $ret['error']);
            }
        }
        catch (Exception $error) {
            $message = 'An exception has occurred. class: '.get_class($error).';msg: '.$error->getMessage().';code: '.$error->getCode().';line: '.$error->getLine().';in_file: '.$error->getFile().';';
            error_log($message);
            echo wp_json_encode(array('result'=>'failed','error'=>$message));
            die();
        }
        echo wp_json_encode($ret);
        die();
    }
    /**
     * List exist remote
     *
     * @since 0.9.1
     */
    public function list_remote()
    {
        check_ajax_referer( 'wpvivid_ajax', 'nonce' );
        $check=current_user_can('manage_options');
        $check=apply_filters('wpvivid_ajax_check_security',$check);
        if(!$check)
        {
            die();
        };
        try {
            $ret['result'] = 'success';
            $html = '';
            $html = apply_filters('wpvivid_add_remote_storage_list', $html);
            $ret['html'] = $html;
            $pic = '';
            $pic = apply_filters('wpvivid_schedule_add_remote_pic', $pic);
            $ret['pic'] = $pic;
            $dir = '';
            $dir = apply_filters('wpvivid_get_remote_directory', $dir);
            $ret['dir'] = $dir;
            $schedule_local_remote = '';
            $schedule_local_remote = apply_filters('wpvivid_schedule_local_remote', $schedule_local_remote);
            $ret['local_remote'] = $schedule_local_remote;
            $remote_storage = '';
            $remote_storage = apply_filters('wpvivid_remote_storage', $remote_storage);
            $ret['remote_storage'] = $remote_storage;
        }
        catch (Exception $error) {
            $message = 'An exception has occurred. class: '.get_class($error).';msg: '.$error->getMessage().';code: '.$error->getCode().';line: '.$error->getLine().';in_file: '.$error->getFile().';';
            error_log($message);
            echo wp_json_encode(array('result'=>'failed','error'=>$message));
            die();
        }
        echo wp_json_encode($ret);
        die();
    }
    /**
     * Test remote connection
     *
     * @since 0.9.1
     */
    public function test_remote_connection()
    {
        check_ajax_referer( 'wpvivid_ajax', 'nonce' );
        $check=current_user_can('manage_options');
        $check=apply_filters('wpvivid_ajax_check_security',$check);
        if(!$check)
        {
            die();
        }
        try {
            if (empty($_POST) || !isset($_POST['remote']) || !is_string($_POST['remote']) || !isset($_POST['type']) || !is_string($_POST['type'])) {
                die();
            }
            $json = sanitize_text_field($_POST['remote']);
            $json = stripslashes($json);
            $remote_options = json_decode($json, true);
            if (is_null($remote_options)) {
                die();
            }

            $remote_options['type'] = sanitize_text_field($_POST['type']);
            $remote = $this->remote_collection->get_remote($remote_options);
            $ret = $remote->test_connect();
        }
        catch (Exception $error) {
            $message = 'An exception has occurred. class: '.get_class($error).';msg: '.$error->getMessage().';code: '.$error->getCode().';line: '.$error->getLine().';in_file: '.$error->getFile().';';
            error_log($message);
            echo wp_json_encode(array('result'=>'failed','error'=>$message));
            die();
        }
        echo wp_json_encode($ret);
        die();
    }
    /**
     * Get backup schedule data
     *
     * @since 0.9.1
     */
    public function get_schedule()
    {
        check_ajax_referer( 'wpvivid_ajax', 'nonce' );
        $check=current_user_can('manage_options');
        $check=apply_filters('wpvivid_ajax_check_security',$check);
        if(!$check)
        {
            die();
        };
        try {
            $schedule = WPvivid_Schedule::get_schedule();
            $schedule['next_start'] = gmdate("l, F d, Y H:i", $schedule['next_start']);
            $ret['result'] = 'success';
            $ret['data'] = $schedule;
            $ret['user_history'] = WPvivid_Setting::get_user_history('remote_selected');
            echo wp_json_encode($ret);
        }
        catch (Exception $error) {
            $message = 'An exception has occurred. class: '.get_class($error).';msg: '.$error->getMessage().';code: '.$error->getCode().';line: '.$error->getLine().';in_file: '.$error->getFile().';';
            error_log($message);
            echo wp_json_encode(array('result'=>'failed','error'=>$message));
            die();
        }
        die();
    }

    public function scan_last_restore()
    {
        try
        {
            $dir=WPvivid_Setting::get_backupdir();
            $restore_data_file=WP_CONTENT_DIR.DIRECTORY_SEPARATOR.$dir.DIRECTORY_SEPARATOR.'wpvivid_restoredata';
            if(file_exists($restore_data_file))
            {
                $ret['has_exist_restore']=1;
            }
            else
            {
                $ret['has_exist_restore']=0;
            }

            $ret['has_old_files']=0;
        }
        catch (Exception $error)
        {
            $message = 'An exception has occurred. class: '.get_class($error).';msg: '.$error->getMessage().';code: '.$error->getCode().';line: '.$error->getLine().';in_file: '.$error->getFile().';';

            $ret['has_exist_restore']=1;
            $ret['restore_error']=$message;
        }


        return $ret;
    }

    public function check_max_allow_packet_ex()
    {
        $max_all_packet_warning=false;
        global $wpdb;
        $wpdb->get_results('SET NAMES utf8mb4', ARRAY_A);
        $max_allowed_packet = $wpdb->get_results('SELECT @@session.max_allowed_packet;',ARRAY_N);
        if($max_allowed_packet)
        {
            if(is_array($max_allowed_packet)&&isset($max_allowed_packet[0])&&isset($max_allowed_packet[0][0]))
            {
                if($max_allowed_packet[0][0]<16777216){
                    $max_all_packet_warning = 'max_allowed_packet = '.size_format($max_allowed_packet[0][0]).' is too small. The recommended value is 16M or higher. Too small value could lead to a failure when importing a larger database.';
                }
            }
        }
        return $max_all_packet_warning;
    }

    public function init_restore_page()
    {
        check_ajax_referer( 'wpvivid_ajax', 'nonce' );
        $check=current_user_can('manage_options');
        $check=apply_filters('wpvivid_ajax_check_security',$check);
        if(!$check)
        {
            die();
        }
        try
        {
            if (!isset($_POST['backup_id']) || empty($_POST['backup_id']) || !is_string($_POST['backup_id']))
            {
                die();
            }

            //$this->restore_data = new WPvivid_restore_data();
            $ret_scan_last_restore = $this->scan_last_restore();

            $backup_id = sanitize_key($_POST['backup_id']);

            $backup = WPvivid_Backuplist::get_backup_by_id($backup_id);

            $backup_item = new WPvivid_Backup_Item($backup);

            $ret = $backup_item->check_backup_files();

            $ret['is_migrate'] = $backup_item->check_migrate_file();

            if ($backup_item->get_backup_type() == 'Upload' || $backup_item->get_backup_type() == 'Migration')
            {
                $is_display = $backup_item->is_display_migrate_option();
                if($is_display === true)
                {
                    $ret['is_migrate_ui'] = 1;
                }
                else {
                    $ret['is_migrate_ui'] = 0;
                }
            } else {
                $ret['is_migrate_ui'] = 0;
            }

            $ret['skip_backup_old_site'] = 1;
            $ret['skip_backup_old_database'] = 1;

            $ret = array_merge($ret, $ret_scan_last_restore);

            $ret['max_allow_packet_warning'] = $this->check_max_allow_packet_ex();

            $common_setting = WPvivid_Setting::get_option('wpvivid_common_setting');
            if(isset($common_setting['restore_memory_limit']) && !empty($common_setting['restore_memory_limit'])){
                $memory_limit = $common_setting['restore_memory_limit'];
            }
            else{
                $memory_limit = WPVIVID_RESTORE_MEMORY_LIMIT;
            }

            @ini_set('memory_limit', $memory_limit);

            $memory_limit = ini_get('memory_limit');
            $unit = strtoupper(substr($memory_limit, -1));
            if ($unit == 'K')
            {
                $memory_limit_tmp = intval($memory_limit) * 1024;
            }
            else if ($unit == 'M')
            {
                $memory_limit_tmp = intval($memory_limit) * 1024 * 1024;
            }
            else if ($unit == 'G')
            {
                $memory_limit_tmp = intval($memory_limit) * 1024 * 1024 * 1024;
            }
            else{
                $memory_limit_tmp = intval($memory_limit);
            }
            if ($memory_limit_tmp < 256 * 1024 * 1024)
            {
                $ret['memory_limit_warning'] = 'memory_limit = ' . $memory_limit . ' is too small. The recommended value is 256M or higher. Too small value could result in a failure of website restore.';
            } else {
                $ret['memory_limit_warning'] = false;
            }

            if ($ret['result'] == WPVIVID_FAILED)
            {
                //$this->wpvivid_handle_restore_error($ret['error'], 'Init restore page');
            }

            $has_zero_date=$backup_item->check_has_zero_date();
            if($has_zero_date)
            {
                include_once WPVIVID_PLUGIN_DIR . '/includes/class-wpvivid-db-method.php';
                $db_method = new WPvivid_DB_Method();
                $ret_sql_mode = $db_method->get_sql_mode();
                if(preg_match('/NO_ZERO_DATE/', $ret_sql_mode['mysql_mode']))
                {
                    $ret['has_zero_date']=1;
                }
            }

            $wp_version_check=$backup_item->check_wp_version();
            if($wp_version_check)
            {
                $ret['wp_version_check']=true;
            }
            else
            {
                $ret['wp_version_check']=false;
            }

            echo wp_json_encode($ret);
        }
        catch (Exception $error)
        {
            $message = 'An exception has occurred. class: '.get_class($error).';msg: '.$error->getMessage().';code: '.$error->getCode().';line: '.$error->getLine().';in_file: '.$error->getFile().';';
            error_log($message);
            echo wp_json_encode(array('result'=>'failed','error'=>$message));
            die();
        }
        die();
    }

    public function get_restore_file_is_migrate()
    {
        check_ajax_referer( 'wpvivid_ajax', 'nonce' );
        $check=current_user_can('manage_options');
        $check=apply_filters('wpvivid_ajax_check_security',$check);
        if(!$check)
        {
            die();
        }
        try {
            if (!isset($_POST['backup_id']) || empty($_POST['backup_id']) || !is_string($_POST['backup_id'])) {
                die();
            }

            $backup_id = sanitize_key($_POST['backup_id']);

            $backup = WPvivid_Backuplist::get_backup_by_id($backup_id);

            $backup_item = new WPvivid_Backup_Item($backup);

            $ret = $backup_item->check_backup_files();

            $ret['is_migrate'] =  $backup_item->check_migrate_file();

            if ($backup_item->get_backup_type() == 'Upload' || $backup_item->get_backup_type() == 'Migration')
            {
                $is_display = $backup_item->is_display_migrate_option();
                if($is_display === true){
                    $ret['is_migrate_ui'] = 1;
                }
                else{
                    $ret['is_migrate_ui'] = 0;
                }
                /*if( $ret['is_migrate']==0)
                    $ret['is_migrate_ui'] = 1;
                else
                    $ret['is_migrate_ui'] = 0;*/
            } else {
                $ret['is_migrate_ui'] = 0;
            }

            if ($ret['result'] == WPVIVID_FAILED) {
                //$this->wpvivid_handle_restore_error($ret['error'], 'Init restore page');
            }

            echo wp_json_encode($ret);
        }
        catch (Exception $error) {
            $message = 'An exception has occurred. class: '.get_class($error).';msg: '.$error->getMessage().';code: '.$error->getCode().';line: '.$error->getLine().';in_file: '.$error->getFile().';';
            error_log($message);
            echo wp_json_encode(array('result'=>'failed','error'=>$message));
            die();
        }
        die();
    }

    public function delete_last_restore_data()
    {
        check_ajax_referer( 'wpvivid_ajax', 'nonce' );
        $check=current_user_can('manage_options');
        $check=apply_filters('wpvivid_ajax_check_security',$check);
        if(!$check)
        {
            die();
        }
        try {
            $this->restore_data = new WPvivid_restore_data();
            $this->restore_data->clean_restore_data();
            $ret['result'] = 'success';
            echo wp_json_encode($ret);
        }
        catch (Exception $error) {
            $message = 'An exception has occurred. class: '.get_class($error).';msg: '.$error->getMessage().';code: '.$error->getCode().';line: '.$error->getLine().';in_file: '.$error->getFile().';';
            error_log($message);
            echo wp_json_encode(array('result'=>'failed','error'=>$message));
            die();
        }
        die();
    }

    /**
     * Prepare backup files for restore
     *
     * @since 0.9.1
     */
    public function prepare_restore()
    {
        check_ajax_referer( 'wpvivid_ajax', 'nonce' );
        $check=current_user_can('manage_options');
        $check=apply_filters('wpvivid_ajax_check_security',$check);
        if(!$check)
        {
            die();
        }
        try {
            if (!isset($_POST['backup_id']) || empty($_POST['backup_id']) || !is_string($_POST['backup_id'])) {
                die();
            }

            $backup_id = sanitize_key($_POST['backup_id']);

            $backup = WPvivid_Backuplist::get_backup_by_id($backup_id);

            $backup_item = new WPvivid_Backup_Item($backup);

            $ret = $backup_item->check_backup_files();

            if ($backup_item->get_backup_type() == 'Upload')
            {
                $ret['is_migrate'] = 1;
            } else {
                $ret['is_migrate'] = 0;
            }

            echo wp_json_encode($ret);
        }
        catch (Exception $error) {
            $message = 'An exception has occurred. class: '.get_class($error).';msg: '.$error->getMessage().';code: '.$error->getCode().';line: '.$error->getLine().';in_file: '.$error->getFile().';';
            error_log($message);
            echo wp_json_encode(array('result'=>'failed','error'=>$message));
            die();
        }
        die();
    }
    /**
     * Download backup files from remote server for restore
     *
     * @since 0.9.1
     */
    public function download_restore_file()
    {
        check_ajax_referer( 'wpvivid_ajax', 'nonce' );
        $check=current_user_can('manage_options');
        $check=apply_filters('wpvivid_ajax_check_security',$check);
        if(!$check)
        {
            die();
        }
        try {
            if (!isset($_POST['backup_id']) || empty($_POST['backup_id']) || !is_string($_POST['backup_id'])
                || !isset($_POST['file_name']) || empty($_POST['file_name']) || !is_string($_POST['file_name'])) {
                die();
            }

            @set_time_limit(600);

            $backup_id = sanitize_key($_POST['backup_id']);
            //$file_name=sanitize_file_name($_POST['file_name']);
            $file_name = sanitize_text_field($_POST['file_name']);

            $file['file_name'] = $file_name;
            $file['size'] = sanitize_key($_POST['size']);

            if(isset($_POST['md5']))
            {
                $file['md5'] = sanitize_key($_POST['md5']);
            }

            $backup = WPvivid_Backuplist::get_backup_by_id($backup_id);
            if (!$backup) {
                echo wp_json_encode(array('result' => WPVIVID_FAILED, 'error' => 'backup not found'));
                die();
            }

            $backup_item = new WPvivid_Backup_Item($backup);

            $remote_option = $backup_item->get_remote();

            if ($remote_option === false) {
                echo wp_json_encode(array('result' => WPVIVID_FAILED, 'error' => 'Retrieving the cloud storage information failed while downloading backups. Please try again later.'));
                die();
            }

            //$downloader = new WPvivid_downloader();
            //$ret = $downloader->download($file, $local_path, $remote_option);
            $download_info = array();
            $download_info['backup_id'] = sanitize_key($_POST['backup_id']);
            //$download_info['file_name']=sanitize_file_name($_POST['file_name']);
            $download_info['file_name'] = sanitize_text_field($_POST['file_name']);
            //set_time_limit(600);
            if (session_id())
                session_write_close();

            $downloader = new WPvivid_downloader();
            $downloader->ready_download($download_info);

            $ret['result'] = 'success';
            echo wp_json_encode($ret);
        }
        catch (Exception $error) {
            $message = 'An exception has occurred. class: '.get_class($error).';msg: '.$error->getMessage().';code: '.$error->getCode().';line: '.$error->getLine().';in_file: '.$error->getFile().';';
            error_log($message);
            echo wp_json_encode(array('result'=>'failed','error'=>$message));
            die();
        }
        die();
    }

    public function download_restore_progress()
    {
        check_ajax_referer( 'wpvivid_ajax', 'nonce' );
        $check=current_user_can('manage_options');
        $check=apply_filters('wpvivid_ajax_check_security',$check);
        if(!$check)
        {
            die();
        }
        try
        {
            if (!isset($_POST['file_name'])) {
                die();
            }

            $file_name=sanitize_text_field($_POST['file_name']);

            $file_size = sanitize_key($_POST['size']);

            $task = WPvivid_taskmanager::get_download_task_v2($file_name);

            if ($task === false)
            {
                $check_status = false;
                $backupdir=WPvivid_Setting::get_backupdir();
                $local_storage_dir = WP_CONTENT_DIR.DIRECTORY_SEPARATOR.$backupdir;
                $local_file=$local_storage_dir.DIRECTORY_SEPARATOR.$file_name;
                if(file_exists($local_file))
                {
                    if(filesize($local_file)==$file_size)
                    {
                        $check_status = true;
                    }
                }

                if($check_status){
                    $ret['result'] = WPVIVID_SUCCESS;
                    $ret['status'] = 'completed';
                }
                else {
                    $ret['result'] = WPVIVID_FAILED;
                    $ret['error'] = 'not found download file';
                    //$this->wpvivid_handle_restore_error($ret['error'], 'Downloading backup file');
                }
                echo wp_json_encode($ret);
            } else {
                $ret['result'] = WPVIVID_SUCCESS;
                $ret['status'] = $task['status'];
                if(isset($task['download_descript']))
                {
                    $ret['log'] = $task['download_descript'];
                }
                else
                {
                    $ret['log'] = '';
                }
                $ret['error'] = $task['error'];
                echo wp_json_encode($ret);
            }
        }
        catch (Exception $error) {
            $message = 'An exception has occurred. class: '.get_class($error).';msg: '.$error->getMessage().';code: '.$error->getCode().';line: '.$error->getLine().';in_file: '.$error->getFile().';';
            error_log($message);
            echo wp_json_encode(array('result'=>'failed','error'=>$message));
            die();
        }
        die();
    }

    public function download_check_has_zero_date()
    {
        try
        {
            check_ajax_referer( 'wpvivid_ajax', 'nonce' );
            $check=current_user_can('manage_options');
            $check=apply_filters('wpvivid_ajax_check_security',$check);
            if(!$check)
            {
                die();
            }
            if (!isset($_POST['backup_id'])) {
                die();
            }

            $backup_id = sanitize_key($_POST['backup_id']);

            $backup = WPvivid_Backuplist::get_backup_by_id($backup_id);

            $backup_item = new WPvivid_Backup_Item($backup);

            $has_zero_date=$backup_item->check_has_zero_date();
            if($has_zero_date)
            {
                include_once WPVIVID_PLUGIN_DIR . '/includes/class-wpvivid-db-method.php';
                $db_method = new WPvivid_DB_Method();
                $ret_sql_mode = $db_method->get_sql_mode();
                if(preg_match('/NO_ZERO_DATE/', $ret_sql_mode['mysql_mode']))
                {
                    $ret['has_zero_date']=1;
                }
            }

            $wp_version_check=$backup_item->check_wp_version();
            if($wp_version_check)
            {
                $ret['wp_version_check']=true;
            }
            else
            {
                $ret['wp_version_check']=false;
            }

            $ret['result']=WPVIVID_SUCCESS;
            echo wp_json_encode($ret);
        }
        catch (Exception $error) {
            $message = 'An exception has occurred. class: '.get_class($error).';msg: '.$error->getMessage().';code: '.$error->getCode().';line: '.$error->getLine().';in_file: '.$error->getFile().';';
            error_log($message);
            echo wp_json_encode(array('result'=>'failed','error'=>$message));
            die();
        }
        die();
    }

    public function wpvivid_handle_restore_error($error_message, $error_type)
    {
        $this->restore_data=new WPvivid_restore_data();
        $this->restore_data->delete_restore_log();
        $this->restore_data->write_log($error_type, 'error');
        $this->restore_data->write_log($error_message, 'error');
        $this->restore_data->save_error_log_to_debug();
    }

    public function wpvivid_handle_remote_storage_error($error_message, $error_type)
    {
        $id = uniqid('wpvivid-');
        $log_file_name = $id . '_add_remote';
        $log = new WPvivid_Log();
        $log->CreateLogFile($log_file_name, 'no_folder', 'Add Remote Test Connection');
        $log->WriteLog('Remote Type: '.$error_type, 'notice');
        if(isset($ret['error'])) {
            $log->WriteLog($error_message, 'notice');
        }
        $log->CloseFile();
        WPvivid_error_log::create_error_log($log->log_file);
    }

    /**
     * Start restore
     *
     * @since 0.9.1
     */
    public function restore()
    {
        check_ajax_referer( 'wpvivid_ajax', 'nonce' );
        $check=current_user_can('manage_options');
        if(!$check)
        {
            die();
        }
        $this->end_shutdown_function=false;
        register_shutdown_function(array($this,'deal_restore_shutdown_error'));
        if(!isset($_POST['backup_id'])||empty($_POST['backup_id'])||!is_string($_POST['backup_id']))
        {
            $this->end_shutdown_function=true;
            die();
        }

        $backup_id=sanitize_key($_POST['backup_id']);
        $backup=WPvivid_Backuplist::get_backup_by_id($backup_id);
        if($backup===false)
        {
            die();
        }

        $this->restore_data=new WPvivid_restore_data();

        $restore_options=array();
        if(isset($_POST['restore_options']))
        {
            $json=sanitize_text_field($_POST['restore_options']);
            $json = stripslashes($json);
            $restore_options = json_decode($json, 1);
            if(is_null($restore_options))
            {
                $restore_options=array();
            }
        }
        try
        {
            if ($this->restore_data->has_restore())
            {
                $status = $this->restore_data->get_restore_status();

                if ($status === WPVIVID_RESTORE_ERROR)
                {
                    $ret['result'] =WPVIVID_FAILED;
                    $ret['error'] = $this->restore_data->get_restore_error();
                    $this->restore_data->save_error_log_to_debug();
                    $this->restore_data->delete_temp_files();
                    $this->_disable_maintenance_mode();
                    echo wp_json_encode($ret);
                    $this->end_shutdown_function=true;
                    die();
                }
                else if ($status === WPVIVID_RESTORE_COMPLETED)
                {
                    $this->write_litespeed_rule(false);
                    $this->restore_data->write_log('disable maintenance mode', 'notice');
                    $this->restore_data->delete_temp_files();
                    $this->_disable_maintenance_mode();
                    echo wp_json_encode(array('result' => 'finished'));
                    $this->end_shutdown_function=true;
                    die();
                }
            }
            else {
                $this->restore_data->init_restore_data($backup_id,$restore_options);
                $this->restore_data->write_log('init restore data', 'notice');
            }
        }
        catch (Exception $error)
        {
            $message = 'An exception has occurred. class: '.get_class($error).';msg: '.$error->getMessage().';code: '.$error->getCode().';line: '.$error->getLine().';in_file: '.$error->getFile().';';
            echo esc_html($message);
            $this->end_shutdown_function=true;
            die();
        }

        try
        {
            $this->_enable_maintenance_mode();
            $this->write_litespeed_rule();
            $restore=new WPvivid_Restore();
            $common_setting = WPvivid_Setting::get_option('wpvivid_common_setting');
            if(isset($common_setting['restore_memory_limit']) && !empty($common_setting['restore_memory_limit'])){
                $memory_limit = $common_setting['restore_memory_limit'];
            }
            else{
                $memory_limit = WPVIVID_RESTORE_MEMORY_LIMIT;
            }

            @ini_set('memory_limit', $memory_limit);
            $ret=$restore->restore();
            if($ret['result']==WPVIVID_FAILED&&$ret['error']=='A restore task is already running.')
            {
                echo wp_json_encode(array('result'=> WPVIVID_SUCCESS));
                $this->end_shutdown_function=true;
                die();
            }
            $this->_disable_maintenance_mode();
        }
        catch (Exception $error)
        {
            $message = 'An exception has occurred. class: '.get_class($error).';msg: '.$error->getMessage().';code: '.$error->getCode().';line: '.$error->getLine().';in_file: '.$error->getFile().';';
            error_log($message);
            $this->restore_data->delete_temp_files();
            $this->restore_data->update_error($message);
            $this->restore_data->write_log($message,'error');
            $this->restore_data->save_error_log_to_debug();
            $this->_disable_maintenance_mode();
            echo wp_json_encode(array('result'=>WPVIVID_FAILED,'error'=>$message));
            $this->end_shutdown_function=true;
            die();
        }

        if($ret['result']==WPVIVID_FAILED)
        {
            $this->restore_data->delete_temp_files();
            $this->_disable_maintenance_mode();
        }

        echo wp_json_encode($ret);
        $this->end_shutdown_function=true;
        die();
    }

    public function write_litespeed_rule($open=true)
    {
        $litespeed=false;
        if ( isset( $_SERVER['HTTP_X_LSCACHE'] ) && $_SERVER['HTTP_X_LSCACHE'] )
        {
            $litespeed=true;
        }
        elseif ( isset( $_SERVER['LSWS_EDITION'] ) && strpos( $_SERVER['LSWS_EDITION'], 'Openlitespeed' ) === 0 ) {
            $litespeed=true;
        }
        elseif ( isset( $_SERVER['SERVER_SOFTWARE'] ) && $_SERVER['SERVER_SOFTWARE'] == 'LiteSpeed' ) {
            $litespeed=true;
        }

        if($litespeed)
        {
            if (function_exists('insert_with_markers'))
            {
                $home_path     = get_home_path();
                $htaccess_file = $home_path . '.htaccess';

                if ( ( ! file_exists( $htaccess_file ) && is_writable( $home_path ) ) || is_writable( $htaccess_file ) )
                {
                    if ( got_mod_rewrite() )
                    {
                        if($open)
                        {
                            $line=array();
                            $line[]='<IfModule Litespeed>';
                            $line[]='RewriteEngine On';
                            $line[]='RewriteRule .* - [E=noabort:1, E=noconntimeout:1]';
                            $line[]='</IfModule>';
                            insert_with_markers($htaccess_file,'WPvivid_Restore',$line);
                        }
                        else
                        {
                            insert_with_markers($htaccess_file,'WPvivid_Restore','');
                        }

                    }
                }
            }
        }
    }

    public function deal_restore_shutdown_error()
    {
        if($this->end_shutdown_function===false)
        {
            $last_error = error_get_last();
            if (!empty($last_error) && !in_array($last_error['type'], array(E_NOTICE,E_WARNING,E_USER_NOTICE,E_USER_WARNING,E_DEPRECATED), true))
            {
                $error = $last_error;
            } else {
                $error = false;
            }
            //$this->task_monitor($task_id,$error);

            if ($error !== false)
            {
                $message = 'type: '. $error['type'] . ', ' . $error['message'] . ' file:' . $error['file'] . ' line:' . $error['line'];
                $this->restore_data->delete_temp_files();
                $this->restore_data->update_error($message);
                $this->restore_data->write_log($message,'error');
                $this->restore_data->save_error_log_to_debug();
                //save_error_log_to_debug
                $this->_disable_maintenance_mode();
                echo wp_json_encode(array('result'=>WPVIVID_FAILED,'error'=>$message));
            }
            else {
                $message = __('restore failed error unknown', 'wpvivid-backuprestore');
                $this->restore_data->delete_temp_files();
                $this->restore_data->update_error($message);
                $this->restore_data->write_log($message,'error');
                $this->restore_data->save_error_log_to_debug();
                $this->_disable_maintenance_mode();
                echo wp_json_encode(array('result'=>WPVIVID_FAILED,'error'=>$message));
            }

            die();
        }
    }
    /**
     * Get restore progress
     *
     * @since 0.9.1
     */
    public function get_restore_progress()
    {
        try
        {
            check_ajax_referer( 'wpvivid_ajax', 'nonce' );
            $check=current_user_can('manage_options');
            if(!$check)
            {
                die();
            }
            if(!isset($_POST['backup_id'])||empty($_POST['backup_id'])||!is_string($_POST['backup_id']))
            {
                $this->end_shutdown_function=true;
                die();
            }

            $backup_id=sanitize_key($_POST['backup_id']);
            $backup=WPvivid_Backuplist::get_backup_by_id($backup_id);
            if($backup===false)
            {
                die();
            }

            $this->restore_data = new WPvivid_restore_data();

            if ($this->restore_data->has_restore())
            {
                $ret['result'] = 'success';
                $ret['status'] = $this->restore_data->get_restore_status();
                if ($ret['status'] == WPVIVID_RESTORE_ERROR) {
                    $this->restore_data->save_error_log_to_debug();
                }
                $ret['log'] = $this->restore_data->get_log_content();
                echo wp_json_encode($ret);
                die();
            } else {
                $ret['result'] = 'failed';
                $ret['error'] = __('The restore file not found. Please verify the file exists.', 'wpvivid-backuprestore');
                echo wp_json_encode($ret);
                die();
            }
        }
        catch (Exception $error) {
            $message = 'An exception has occurred. class: '.get_class($error).';msg: '.$error->getMessage().';code: '.$error->getCode().';line: '.$error->getLine().';in_file: '.$error->getFile().';';
            error_log($message);
            echo wp_json_encode(array('result'=>'failed','error'=>$message));
            die();
        }
    }

    public function init_filesystem()
    {
        $credentials = request_filesystem_credentials(wp_nonce_url(admin_url('admin.php')."?page=WPvivid", 'wpvivid-nonce'));

        if ( ! WP_Filesystem($credentials) )
        {
            return false;
        }
        return true;
    }

    public function _enable_maintenance_mode()
    {
        //enable maintenance mode by create the .maintenance file.
        //If your wordpress version is greater than 4.6, use the enable_maintenance_mode filter to make our ajax request pass
        $this->init_filesystem();
        global $wp_filesystem;
        $file = $wp_filesystem->abspath() . '.maintenance';
        $maintenance_string = '<?php $upgrading = ' . (time()+1200) . ';';
        $maintenance_string.='global $wp_version;';
        $maintenance_string.='$version_check=version_compare($wp_version,4.6,\'>\' );';
        $maintenance_string.='if($version_check)';
        $maintenance_string.='{';
        $maintenance_string.='if(!function_exists(\'enable_maintenance_mode_filter\'))';
        $maintenance_string.='{';
        $maintenance_string.='function enable_maintenance_mode_filter($enable_checks,$upgrading)';
        $maintenance_string.='{';
        $maintenance_string.='if(is_admin()&&isset($_POST[\'wpvivid_restore\']))';
        $maintenance_string.='{';
        $maintenance_string.='return false;';
        $maintenance_string.='}';
        $maintenance_string.='return $enable_checks;';
        $maintenance_string.='}';
        $maintenance_string.='}';
        $maintenance_string.='add_filter( \'enable_maintenance_mode\',\'enable_maintenance_mode_filter\',10, 2 );';
        $maintenance_string.='}';
        $maintenance_string.='else';
        $maintenance_string.='{';
        $maintenance_string.='if(is_admin()&&isset($_POST[\'wpvivid_restore\']))';
        $maintenance_string.='{';
        $maintenance_string.='global $upgrading;';
        $maintenance_string.='$upgrading=0;';
        $maintenance_string.='return 1;';
        $maintenance_string.='}';
        $maintenance_string.='}';
        if ($wp_filesystem->exists( $file ) )
        {
            $wp_filesystem->delete($file);
        }
        $wp_filesystem->put_contents($file, $maintenance_string, FS_CHMOD_FILE);
    }

    public function _disable_maintenance_mode()
    {
        $this->init_filesystem();
        global $wp_filesystem;
        $file = $wp_filesystem->abspath() . '.maintenance';
        if ($wp_filesystem->exists( $file ))
        {
            $wp_filesystem->delete($file);
        }
    }

    public function deal_restore_error($error_type,$error)
    {
        $message = 'A '.$error_type.' has occurred. class:'.get_class($error).';msg:'.$error->getMessage().';code:'.$error->getCode().';line:'.$error->getLine().';in_file:'.$error->getFile().';';
        error_log($message);
        echo esc_html($message);
    }

    public function update_last_backup_time($task)
    {
        WPvivid_Setting::update_option('wpvivid_last_msg',$task);
    }

    public function update_last_backup_task($task)
    {
        apply_filters('wpvivid_set_backup_report_addon_mainwp', $task);
    }
    /**
     * Get last backup information
     *
     * @since 0.9.1
     */
    public function get_last_backup()
    {
        check_ajax_referer( 'wpvivid_ajax', 'nonce' );
        $check=current_user_can('manage_options');
        $check=apply_filters('wpvivid_ajax_check_security',$check);
        if(!$check)
        {
            die();
        }
        try {
            $html = '';
            $html = apply_filters('wpvivid_get_last_backup_message', $html);
            $ret['data'] = $html;
            echo wp_json_encode($ret);
        }
        catch (Exception $error) {
            $message = 'An exception has occurred. class: '.get_class($error).';msg: '.$error->getMessage().';code: '.$error->getCode().';line: '.$error->getLine().';in_file: '.$error->getFile().';';
            error_log($message);
            echo wp_json_encode(array('result'=>'failed','error'=>$message));
            die();
        }
        die();
    }

    public function wpvivid_get_last_backup_message($html)
    {
        $html='';
        $message=WPvivid_Setting::get_last_backup_message('wpvivid_last_msg');
        if(empty($message)){
            $last_message=__('The last backup message not found.', 'wpvivid-backuprestore');
        }
        else{
            if(isset($message['status']['start_time']))
            {
                $message['status']['start_time'] = gmdate("l, F-d-Y H:i", strtotime($message['status']['start_time']));
                $last_message=$message['status']['start_time'];
            }
            else
            {
                $last_message=__('The last backup message not found.', 'wpvivid-backuprestore');
            }
        }
        $html .= '<strong>'.__('Last Backup: ', 'wpvivid-backuprestore').'</strong>'.$last_message;
        return $html;
    }

    public function wpvivid_get_last_backup_message_output()
    {
        $message=WPvivid_Setting::get_last_backup_message('wpvivid_last_msg');
        if(empty($message)){
            ?>
            <strong><?php esc_html_e('Last Backup: ', 'wpvivid-backuprestore'); ?></strong><?php esc_html_e('The last backup message not found.', 'wpvivid-backuprestore'); ?>
            <?php
        }
        else{
            if(isset($message['status']['start_time']))
            {
                if($message['status']['str'] == 'completed'){
                    $last_backup_status='Succeeded';
                    $span_style='color: #81d742;';
                }
                elseif($message['status']['str'] == 'error'){
                    $last_backup_status='Failed';
                    $span_style='color: #ff0000;';
                }
                elseif($message['status']['str'] == 'cancel'){
                    $last_backup_status='Failed';
                    $span_style='color: #ff0000;';
                }
                else{
                    $last_backup_status='Succeeded';
                    $span_style='color: #81d742;';
                }
                ?>
                <strong><?php esc_html_e('Last Backup: ', 'wpvivid-backuprestore'); ?></strong>
                <span style="<?php echo esc_attr($span_style); ?>">
                    <?php
                    echo esc_html(gmdate("F-d-Y H:i", strtotime($message['status']['start_time'])).' ('.$last_backup_status.')');
                    ?>
                </span>
                <?php
            }
            else
            {
                ?>
                <strong><?php esc_html_e('Last Backup: ', 'wpvivid-backuprestore'); ?></strong><?php esc_html_e('The last backup message not found.', 'wpvivid-backuprestore'); ?>
                <?php
            }
        }
    }

    public function list_tasks()
    {
        check_ajax_referer( 'wpvivid_ajax', 'nonce' );
        $check=current_user_can('manage_options');
        $check=apply_filters('wpvivid_ajax_check_security',$check);
        if(!$check)
        {
            die();
        };
        try
        {
            if (isset($_POST['backup_id']))
            {
                $backup_id=sanitize_key($_POST['backup_id']);
            }
            else{
                $backup_id=false;
            }
            $ret = $this->_list_tasks($backup_id);
            $backup_success_count=WPvivid_Setting::get_option('wpvivid_backup_success_count');
            if(!empty($backup_success_count)){
                WPvivid_Setting::delete_option('wpvivid_backup_success_count');
            }

            $backup_error_array=WPvivid_Setting::get_option('wpvivid_backup_error_array');
            if(!empty($backup_error_array)){
                WPvivid_Setting::delete_option('wpvivid_backup_error_array');
            }

            echo wp_json_encode($ret);
        }
        catch (Exception $error)
        {
            $message = 'An exception has occurred. class: '.get_class($error).';msg: '.$error->getMessage().';code: '.$error->getCode().';line: '.$error->getLine().';in_file: '.$error->getFile().';';
            error_log($message);
            echo wp_json_encode(array('result'=>'failed','error'=>$message));
            die();
        }
        die();
    }

    public function _list_tasks($backup_id)
    {
        $tasks=WPvivid_Setting::get_tasks();
        $ret=array();
        $list_tasks=array();
        foreach ($tasks as $task)
        {
            if($task['action']=='backup')
            {
                $backup=new WPvivid_Backup_Task($task['id']);
                $list_tasks[$task['id']]=$backup->get_backup_task_info($task['id']);
                if($list_tasks[$task['id']]['task_info']['need_next_schedule']===true){
                    $timestamp = wp_next_scheduled(WPVIVID_TASK_MONITOR_EVENT,array($task['id']));

                    if($timestamp===false)
                    {
                        $this->add_monitor_event($task['id'],20);
                    }
                }
                /*if($list_tasks[$task['id']]['task_info']['need_update_last_task']===true){
                    $task_msg = WPvivid_taskmanager::get_task($task['id']);
                    $this->update_last_backup_task($task_msg);
                    if($task['type'] === 'Cron') {
                        //update last backup time
                        //do_action('wpvivid_update_schedule_last_time_addon');
                    }
                }*/
                //<div id="wpvivid_estimate_backup_info" style="float:left; ' . $list_tasks[$task['id']]['task_info']['display_estimate_backup'] . '">
                //                                                <div class="backup-basic-info"><span class="wpvivid-element-space-right">' . __('Database Size:', 'wpvivid-backuprestore') . '</span><span id="wpvivid_backup_database_size">' . $list_tasks[$task['id']]['task_info']['db_size'] . '</span></div>
                //                                                <div class="backup-basic-info"><span class="wpvivid-element-space-right">' . __('File Size:', 'wpvivid-backuprestore') . '</span><span id="wpvivid_backup_file_size">' . $list_tasks[$task['id']]['task_info']['file_size'] . '</span></div>
                //                                             </div>
                $list_tasks[$task['id']]['progress_html'] = '<div class="action-progress-bar" id="wpvivid_action_progress_bar">
                                                <div class="action-progress-bar-percent" id="wpvivid_action_progress_bar_percent" style="height:24px;width:' . $list_tasks[$task['id']]['task_info']['backup_percent'] . '"></div>
                                             </div>
                                             <div id="wpvivid_estimate_upload_info" style="float: left;"> 
                                                <div class="backup-basic-info"><span class="wpvivid-element-space-right">' . __('Total Size:', 'wpvivid-backuprestore') . '</span><span>' . $list_tasks[$task['id']]['task_info']['total'] . '</span></div>
                                                <div class="backup-basic-info"><span class="wpvivid-element-space-right">' . __('Uploaded:', 'wpvivid-backuprestore') . '</span><span>' . $list_tasks[$task['id']]['task_info']['upload'] . '</span></div>
                                                <div class="backup-basic-info"><span class="wpvivid-element-space-right">' . __('Speed:', 'wpvivid-backuprestore') . '</span><span>' . $list_tasks[$task['id']]['task_info']['speed'] . '</span></div>
                                             </div>
                                             <div style="float: left;">
                                                <div class="backup-basic-info"><span class="wpvivid-element-space-right">' . __('Network Connection:', 'wpvivid-backuprestore') . '</span><span>' . $list_tasks[$task['id']]['task_info']['network_connection'] . '</span></div>
                                             </div>
                                             <div style="clear:both;"></div>
                                             <div style="margin-left:10px; float: left; width:100%;"><p id="wpvivid_current_doing">' . $list_tasks[$task['id']]['task_info']['descript'] . '</p></div>
                                             <div style="clear: both;"></div>
                                             <div>
                                                <div id="wpvivid_backup_cancel" class="backup-log-btn"><input class="button-primary" id="wpvivid_backup_cancel_btn" type="submit" value="' . esc_attr('Cancel', 'wpvivid-backuprestore') . '" style="' . $list_tasks[$task['id']]['task_info']['css_btn_cancel'] . '" /></div>
                                                <div id="wpvivid_backup_log" class="backup-log-btn"><input class="button-primary" id="wpvivid_backup_log_btn" type="submit" value="' . esc_attr('Log', 'wpvivid-backuprestore') . '" style="' . $list_tasks[$task['id']]['task_info']['css_btn_log'] . '" /></div>
                                             </div>
                                             <div style="clear: both;"></div>';
            }
        }
        WPvivid_taskmanager::delete_marked_task();

        $ret['backuplist_html'] = false;
        $backup_success_count=WPvivid_Setting::get_option('wpvivid_backup_success_count');
        if(!empty($backup_success_count)){
            //$notice_msg = $backup_success_count.' backup tasks have been completed. Please switch to <a href="#" onclick="wpvivid_click_switch_page(\'wrap\', \'wpvivid_tab_log\', true);">Log</a> page to check the details.';
            $notice_msg = sprintf('%d backup tasks have been completed. Please switch to <a href="#" onclick="wpvivid_click_switch_page(\'wrap\', \'wpvivid_tab_log\', true);">Log</a> page to check the details.', $backup_success_count);
            $success_notice_html='<div class="notice notice-success is-dismissible inline"><p>'.$notice_msg.'</p>
                                    <button type="button" class="notice-dismiss" onclick="click_dismiss_notice(this);">
                                    <span class="screen-reader-text">Dismiss this notice.</span>
                                    </button>
                                    </div>';
            //WPvivid_Setting::delete_option('wpvivid_backup_success_count');
            $html = '';
            $html = apply_filters('wpvivid_add_backup_list', $html);
            $ret['backuplist_html'] = $html;
        }
        else {
            $success_notice_html = false;
        }
        $ret['success_notice_html'] = $success_notice_html;

        $backup_error_array=WPvivid_Setting::get_option('wpvivid_backup_error_array');
        if(!empty($backup_error_array)){
            $error_notice_html = array();
            foreach ($backup_error_array as $key => $value){
                $error_notice_html['bu_error']['task_id']=$value['task_id'];
                $error_notice_html['bu_error']['error_msg']=$value['error_msg'];
            }
            //WPvivid_Setting::delete_option('wpvivid_backup_error_array');
            $html = '';
            $html = apply_filters('wpvivid_add_backup_list', $html);
            $ret['backuplist_html'] = $html;
        }
        else{
            $error_notice_html = false;
        }
        $ret['error_notice_html'] = $error_notice_html;

        $ret['backup']['result']='success';
        $ret['backup']['data']=$list_tasks;

        $ret['download']=array();
        if($backup_id !== false && !empty($backup_id)) {
            $backup=WPvivid_Backuplist::get_backup_by_id($backup_id);
            if($backup===false)
            {
                $ret['result']=WPVIVID_FAILED;
                $ret['error']='backup id not found';
                return $ret;
            }
            $backup_item=new WPvivid_Backup_Item($backup);
            $ret['download']=$backup_item->update_download_page($backup_id);
        }

        $html='';
        $html=apply_filters('wpvivid_get_last_backup_message', $html);
        $ret['last_msg_html']=$html;

        $html='';
        $html=apply_filters('wpvivid_get_log_list', $html);
        $ret['log_html'] = $html['html'];
        $ret['log_count'] = $html['log_count'];

        return $ret;
    }

    public function clean_cache()
    {
        delete_option('wpvivid_download_cache');
        delete_option('wpvivid_download_task');
        WPvivid_taskmanager::delete_out_of_date_finished_task();
        WPvivid_taskmanager::delete_ready_task();
    }
    /**
     * Get backup local storage path
     *
     * @since 0.9.1
     */
    public function get_dir()
    {
        check_ajax_referer( 'wpvivid_ajax', 'nonce' );
        $check=current_user_can('manage_options');
        $check=apply_filters('wpvivid_ajax_check_security',$check);
        if(!$check)
        {
            die();
        };
        try {
            $dir = WPvivid_Setting::get_option('wpvivid_local_setting');

            if (!isset($dir['path'])) {
                $dir = WPvivid_Setting::set_default_local_option();
            }

            if (!is_dir(WP_CONTENT_DIR . DIRECTORY_SEPARATOR . $dir['path'])) {
                @mkdir(WP_CONTENT_DIR . DIRECTORY_SEPARATOR . $dir['path'], 0777, true);
                @fopen(WP_CONTENT_DIR . DIRECTORY_SEPARATOR . $dir['path'] . '/index.html', 'x');
                $tempfile = @fopen(WP_CONTENT_DIR . DIRECTORY_SEPARATOR . $dir['path'] . '/.htaccess', 'x');
                if ($tempfile) {
                    //$text = "deny from all";
                    $text="<IfModule mod_rewrite.c>\r\nRewriteEngine On\r\nRewriteRule .* - [F,L]\r\n</IfModule>";
                    fwrite($tempfile, $text);
                    fclose($tempfile);
                } else {
                    $ret['result'] = 'failed';
                    $ret['error'] = __('Getting backup directory failed. Please try again later.', 'wpvivid-backuprestore');
                }

            }

            $ret['result'] = 'success';
            $ret['path'] = WP_CONTENT_DIR . DIRECTORY_SEPARATOR . $dir['path'];
            echo wp_json_encode($ret);
        }
        catch (Exception $error) {
            $message = 'An exception has occurred. class: '.get_class($error).';msg: '.$error->getMessage().';code: '.$error->getCode().';line: '.$error->getLine().';in_file: '.$error->getFile().';';
            error_log($message);
            echo wp_json_encode(array('result'=>'failed','error'=>$message));
            die();
        }
        die();
    }
    /**
     * Set security lock for backup record
     *
     * @since 0.9.1
     */
    public function set_security_lock()
    {
        check_ajax_referer( 'wpvivid_ajax', 'nonce' );
        $check=current_user_can('manage_options');
        $check=apply_filters('wpvivid_ajax_check_security',$check);
        if(!$check)
        {
            die();
        };
        try {
            if (isset($_POST['backup_id']) && !empty($_POST['backup_id']) && is_string($_POST['backup_id']) && isset($_POST['lock'])) {
                $backup_id = sanitize_key($_POST['backup_id']);
                if ($_POST['lock'] == 0 || $_POST['lock'] == 1) {
                    $lock = sanitize_key($_POST['lock']);
                } else {
                    $lock = 0;
                }

                $ret = WPvivid_Backuplist::set_security_lock($backup_id, $lock);
                echo wp_json_encode($ret);
            }
        }
        catch (Exception $error) {
            $message = 'An exception has occurred. class: '.get_class($error).';msg: '.$error->getMessage().';code: '.$error->getCode().';line: '.$error->getLine().';in_file: '.$error->getFile().';';
            error_log($message);
            echo wp_json_encode(array('result'=>'failed','error'=>$message));
            die();
        }
        die();
    }
    /**
     * Get Web-server disk space in use
     *
     * @since 0.9.1
     */
    public function junk_files_info()
    {
        check_ajax_referer( 'wpvivid_ajax', 'nonce' );
        $check=current_user_can('manage_options');
        $check=apply_filters('wpvivid_ajax_check_security',$check);
        if(!$check)
        {
            die();
        }
        try {
            $ret['result'] = 'success';
            $ret['data'] = $this->_junk_files_info_ex();
            echo wp_json_encode($ret);
        }
        catch (Exception $error) {
            $message = 'An exception has occurred. class: '.get_class($error).';msg: '.$error->getMessage().';code: '.$error->getCode().';line: '.$error->getLine().';in_file: '.$error->getFile().';';
            error_log($message);
            echo wp_json_encode(array('result'=>'failed','error'=>$message));
            die();
        }
        die();
    }

    public function check_is_a_wpvivid_backup($file_name)
    {
        $ret=WPvivid_Backup_Item::get_backup_file_info($file_name);
        if($ret['result'] === WPVIVID_SUCCESS){
            return true;
        }
        elseif($ret['result'] === WPVIVID_FAILED){
            return $ret['error'];
        }
    }

    public function check_file_is_a_wpvivid_backup($file_name,&$backup_id)
    {
        if(preg_match('/wpvivid-.*_.*_.*\.zip$/',$file_name))
        {
            if(preg_match('/wpvivid-(.*?)_/',$file_name,$matches))
            {
                $id= $matches[0];
                $id=substr($id,0,strlen($id)-1);

                $backup_id_list=WPvivid_Backuplist::get_has_remote_backuplist();
                if(in_array($id, $backup_id_list))
                {
                    return false;
                }
                return true;
            }
            else
            {
                return false;
            }
        }
        else
        {
            return false;
        }
    }

    public function get_wpvivid_backup_size()
    {
        $path=WP_CONTENT_DIR.DIRECTORY_SEPARATOR.WPvivid_Setting::get_backupdir().DIRECTORY_SEPARATOR;
        $backups=array();
        $count = 0;
        $ret_size = 0;
        if(is_dir($path))
        {
            $handler = opendir($path);
            if($handler!==false)
            {
                while (($filename = readdir($handler)) !== false)
                {
                    if ($filename != "." && $filename != "..")
                    {
                        $count++;

                        if (is_dir($path  . $filename))
                        {
                            continue;
                        } else {
                            if($this->check_file_is_a_wpvivid_backup($filename,$backup_id))
                            {
                                if($this->check_is_a_wpvivid_backup($path.$filename) === true)
                                {
                                    $backups[$backup_id]['files'][] = $filename;
                                }
                            }
                        }
                    }
                }
                if($handler)
                    @closedir($handler);
            }
            if(!empty($backups))
            {
                foreach ($backups as $backup_id =>$backup)
                {
                    $backup_data['result']='success';
                    $backup_data['files']=array();
                    if(empty($backup['files']))
                        continue;
                    foreach ($backup['files'] as $file)
                    {
                        $ret_size += filesize($path.$file);
                    }
                }
            }
        }
        else{
            $ret_size = 0;
        }
        return $ret_size;
    }

    public function _junk_files_info_ex()
    {
        try
        {
            $log_dir = WPvivid_tools::GetSaveLogFolder();
            $log_dir_byte = $this->GetDirectorySize($log_dir);
            $ret['log_dir_size'] = $this->formatBytes($log_dir_byte);


            $ret['backup_cache_size'] = 0;
            $path = WP_CONTENT_DIR.DIRECTORY_SEPARATOR.WPvivid_Setting::get_backupdir();
            $handler=opendir($path);
            if($handler===false)
            {
                $ret['backup_cache_size'] = 0;
            }
            while(($filename=readdir($handler))!==false)
            {
                if(preg_match('#pclzip-.*\.tmp#', $filename)){
                    $ret['backup_cache_size'] += filesize($path.DIRECTORY_SEPARATOR.$filename);
                }
                if(preg_match('#pclzip-.*\.gz#', $filename)){
                    $ret['backup_cache_size'] += filesize($path.DIRECTORY_SEPARATOR.$filename);
                }
            }
            @closedir($handler);
            $backup_id_list=WPvivid_Backuplist::get_has_remote_backuplist();
            foreach ($backup_id_list as $backup_id)
            {
                $backup=WPvivid_Backuplist::get_backup_by_id($backup_id);
                if(!$backup)
                {
                    continue;
                }

                if(array_key_exists('lock',$backup))
                {
                    continue;
                }

                $backup_item = new WPvivid_Backup_Item($backup);
                $file=$backup_item->get_files(false);
                foreach ($file as $filename)
                {
                    if(file_exists($path.DIRECTORY_SEPARATOR.$filename))
                    {
                        $ret['backup_cache_size'] += filesize($path.DIRECTORY_SEPARATOR.$filename);
                    }
                }
            }
            $ret['backup_cache_size'] = $this->formatBytes($ret['backup_cache_size']);


            $ret['junk_size'] = 0;
            $delete_files  = array();
            $delete_folder = array();
            $list=WPvivid_Backuplist::get_backuplist();
            $files=array();
            foreach ($list as $backup_id => $backup_value)
            {
                $backup=WPvivid_Backuplist::get_backup_by_id($backup_id);
                if($backup===false)
                {
                    continue;
                }
                $backup_item = new WPvivid_Backup_Item($backup);
                $file=$backup_item->get_files(false);
                foreach ($file as $filename){
                    $files[]=$filename;
                }
            }

            $dir=WPvivid_Setting::get_backupdir();
            $dir=WP_CONTENT_DIR.DIRECTORY_SEPARATOR. $dir;
            $path=str_replace('/',DIRECTORY_SEPARATOR,WPvivid_tools::GetSaveLogFolder());
            if(substr($path, -1) == DIRECTORY_SEPARATOR)
            {
                $path = substr($path, 0, -1);
            }
            $folder[]= $path;
            $except_regex['file'][]='&wpvivid-&';
            $except_regex['file'][]='&wpvivid_temp-&';
            //$except_regex['file'][]='&'.apply_filters('wpvivid_white_label_file_prefix', 'wpvivid').'-&';
            //$except_regex['file'][]='&'.apply_filters('wpvivid_white_label_file_prefix', 'wpvivid').'_temp-&';
            $this -> get_dir_files($delete_files,$delete_folder,$dir,$except_regex,$files,$folder,0,false);

            foreach ($delete_files as $file)
            {
                if(file_exists($file))
                {
                    $ret['junk_size'] += filesize($file);
                }
            }

            foreach ($delete_folder as $folder)
            {
                if(file_exists($folder))
                {
                    $ret['junk_size'] += $this->GetDirectorySize($folder);
                }
            }
            $ret['junk_size'] = $this->formatBytes($ret['junk_size']);


            $backup_dir_byte = $this->GetDirectorySize($dir);

            $ret['backup_size'] = $this->get_wpvivid_backup_size();
            $ret['backup_size'] = $this->formatBytes($ret['backup_size']);

            $ret['sum_size'] = $this->formatBytes($backup_dir_byte + $log_dir_byte);
        }
        catch (Exception $e)
        {
            $ret['log_path'] = $log_dir = WPvivid_tools::GetSaveLogFolder();
            $dir = WPvivid_Setting::get_backupdir();
            $ret['old_files_path'] = WP_CONTENT_DIR . DIRECTORY_SEPARATOR . $dir . DIRECTORY_SEPARATOR . WPVIVID_DEFAULT_ROLLBACK_DIR;
            $dir = WP_CONTENT_DIR . DIRECTORY_SEPARATOR . $dir;
            $ret['junk_path'] = $dir;
            $ret['sum_size'] = '0';
            $ret['log_dir_size'] = '0';
            $ret['backup_cache_size'] = '0';
            $ret['junk_size'] = '0';
            $ret['backup_size'] = '0';
        }
        return $ret;
    }

    public function get_out_of_date_info()
    {
        check_ajax_referer( 'wpvivid_ajax', 'nonce' );
        $check=current_user_can('manage_options');
        $check=apply_filters('wpvivid_ajax_check_security',$check);
        if(!$check)
        {
            die();
        }
        try {
            $dir = WPvivid_Setting::get_backupdir();
            $dir = WP_CONTENT_DIR . DIRECTORY_SEPARATOR . $dir;
            $ret['web_server'] = $dir;
            $ret['remote_options'] = WPvivid_Setting::get_remote_options();

            $info = WPvivid_Backuplist::get_out_of_date_backuplist_info(WPvivid_Setting::get_max_backup_count());
            $ret['info'] = $info;
            $ret['info']['size'] = $this->formatBytes($info['size']);

            echo wp_json_encode($ret);
        }
        catch (Exception $error) {
            $message = 'An exception has occurred. class: '.get_class($error).';msg: '.$error->getMessage().';code: '.$error->getCode().';line: '.$error->getLine().';in_file: '.$error->getFile().';';
            error_log($message);
            echo wp_json_encode(array('result'=>'failed','error'=>$message));
            die();
        }
        die();
    }

    public function _get_out_of_date_info()
    {
        $dir=WPvivid_Setting::get_backupdir();
        $dir=WP_CONTENT_DIR.DIRECTORY_SEPARATOR. $dir;
        $ret['web_server']=$dir;
        $ret['remote_options']=WPvivid_Setting::get_remote_options();

        $info=WPvivid_Backuplist::get_out_of_date_backuplist_info(WPvivid_Setting::get_max_backup_count());
        $ret['info']=$info;
        $ret['info']['size']=$this->formatBytes($info['size']);

        return $ret;
    }

    public function clean_out_of_date_backup()
    {
        check_ajax_referer( 'wpvivid_ajax', 'nonce' );
        $check=current_user_can('manage_options');
        $check=apply_filters('wpvivid_ajax_check_security',$check);
        if(!$check)
        {
            die();
        }
        try
        {
            $backup_ids=array();
            $backup_ids=apply_filters('wpvivid_get_oldest_backup_ids',$backup_ids,true);
            foreach ($backup_ids as $backup_id)
            {
                $this->delete_backup_by_id($backup_id);
            }
            $ret['result'] = 'success';
            $html = '';
            $html = apply_filters('wpvivid_add_backup_list', $html);
            $ret['html'] = $html;

            echo wp_json_encode($ret);
        }
        catch (Exception $error)
        {
            $message = 'An exception has occurred. class: '.get_class($error).';msg: '.$error->getMessage().';code: '.$error->getCode().';line: '.$error->getLine().';in_file: '.$error->getFile().';';
            error_log($message);
            echo wp_json_encode(array('result'=>'failed','error'=>$message));
        }
        die();
    }

    private function GetDirectorySize($path){
        $bytes_total = 0;
        $path = realpath($path);
        if($path!==false && $path!='' && file_exists($path))
        {
            foreach(new RecursiveIteratorIterator(new RecursiveDirectoryIterator($path, FilesystemIterator::SKIP_DOTS)) as $object){
                $bytes_total += $object->getSize();
            }
        }
        return $bytes_total;
    }

    public function clean_local_storage()
    {
        check_ajax_referer( 'wpvivid_ajax', 'nonce' );
        $check=current_user_can('manage_options');
        $check=apply_filters('wpvivid_ajax_check_security',$check);
        if(!$check)
        {
            die();
        }

        try
        {
            if(!isset($_POST['options'])||empty($_POST['options'])||!is_string($_POST['options']))
            {
                die();
            }
            $options=sanitize_text_field($_POST['options']);
            $options =stripslashes($options);
            $options=json_decode($options,true);
            if(is_null($options))
            {
                die();
            }
            if($options['log']=='0' && $options['backup_cache']=='0' && $options['junk_files']=='0' && $options['old_files']=='0')
            {
                $ret['result']=WPVIVID_FAILED;
                $ret['msg']=__('Choose at least one type of junk files for deleting.', 'wpvivid-backuprestore');
                echo wp_json_encode($ret);
                die();
            }
            $delete_files = array();
            $delete_folder=array();
            if($options['log']=='1')
            {
                $log_dir=$this->wpvivid_log->GetSaveLogFolder();
                $log_files=array();
                $temp=array();
                $this -> get_dir_files($log_files,$temp,$log_dir,array('file' => '&wpvivid-&'),array(),array(),0,false);

                foreach ($log_files as $file)
                {
                    $file_name=basename($file);
                    $id=substr ($file_name,0,21);
                    if(WPvivid_Backuplist::get_backup_by_id($id)===false)
                    {
                        $delete_files[]=$file;
                    }
                }
            }

            if($options['backup_cache']=='1')
            {
                $backup_id_list=WPvivid_Backuplist::get_has_remote_backuplist();
                $this->delete_local_backup($backup_id_list);
                WPvivid_tools::clean_junk_cache();
            }

            if($options['junk_files']=='1')
            {
                $list=WPvivid_Backuplist::get_backuplist();
                $files=array();
                foreach ($list as $backup_id => $backup_value)
                {
                    $backup=WPvivid_Backuplist::get_backup_by_id($backup_id);
                    if($backup===false)
                    {
                        continue;
                    }
                    $backup_item = new WPvivid_Backup_Item($backup);
                    $file=$backup_item->get_files(false);
                    foreach ($file as $filename){
                        $files[]=$filename;
                    }
                }

                $dir=WPvivid_Setting::get_backupdir();
                $dir=WP_CONTENT_DIR.DIRECTORY_SEPARATOR. $dir;
                $path=str_replace('/',DIRECTORY_SEPARATOR,$this->wpvivid_log->GetSaveLogFolder());
                if(substr($path, -1) == DIRECTORY_SEPARATOR) {
                    $path = substr($path, 0, -1);
                }
                $folder[]= $path;
                $except_regex['file'][]='&wpvivid-&';
                $except_regex['file'][]='&wpvivid_temp-&';
                $this -> get_dir_files($delete_files,$delete_folder,$dir,$except_regex,$files,$folder,0,false);
            }

            foreach ($delete_files as $file)
            {
                if(file_exists($file))
                    @wp_delete_file($file);
            }

            foreach ($delete_folder as $folder)
            {
                if(file_exists($folder))
                    WPvivid_tools::deldir($folder,'',true);
            }

            $ret['result']='success';
            $ret['msg']=__('The selected junk files have been deleted.', 'wpvivid-backuprestore');
            $ret['data']=$this->_junk_files_info_ex();
            $html = '';
            $html = apply_filters('wpvivid_get_log_list', $html);
            $ret['html'] = $html['html'];
            $ret['log_count'] = $html['log_count'];
            echo wp_json_encode($ret);
        }
        catch (Exception $error)
        {
            $message = 'An exception has occurred. class: '.get_class($error).';msg: '.$error->getMessage().';code: '.$error->getCode().';line: '.$error->getLine().';in_file: '.$error->getFile().';';
            error_log($message);
            echo wp_json_encode(array('result'=>'failed','error'=>$message));
            die();
        }

        die();
    }

    public function get_dir_files(&$files,&$folder,$path,$except_regex,$exclude_files=array(),$exclude_folder=array(),$exclude_file_size=0,$flag = true)
    {
        $handler=opendir($path);
        if($handler===false)
            return;
        while(($filename=readdir($handler))!==false)
        {
            if($filename != "." && $filename != "..")
            {
                $dir=str_replace('/',DIRECTORY_SEPARATOR,$path.DIRECTORY_SEPARATOR.$filename);


                if(in_array($dir,$exclude_folder))
                {
                    continue;
                }
                else if(is_dir($path.DIRECTORY_SEPARATOR.$filename))
                {
                    if($except_regex!==false)
                    {
                        if($this -> regex_match($except_regex['file'],$path.DIRECTORY_SEPARATOR.$filename,$flag)){
                            continue;
                        }
                        $folder[]=$path.DIRECTORY_SEPARATOR.$filename;
                    }else
                    {
                        $folder[]=$path.DIRECTORY_SEPARATOR.$filename;
                    }
                    $this->get_dir_files($files ,$folder, $path.DIRECTORY_SEPARATOR.$filename,$except_regex,$exclude_folder);
                }else {
                    if($except_regex===false||!$this -> regex_match($except_regex['file'] ,$path.DIRECTORY_SEPARATOR.$filename,$flag))
                    {
                        if(in_array($filename,$exclude_files))
                        {
                            continue;
                        }
                        if($exclude_file_size==0)
                        {
                            $files[] = $path.DIRECTORY_SEPARATOR.$filename;
                        }
                        else if(filesize($path.DIRECTORY_SEPARATOR.$filename)<$exclude_file_size*1024*1024)
                        {
                            $files[] = $path.DIRECTORY_SEPARATOR.$filename;
                        }
                    }
                }
            }
        }
        if($handler)
            @closedir($handler);

    }
    private function regex_match($regex_array,$filename,$flag){
        if($flag){
            if(empty($regex_array)){
                return false;
            }
            if(is_array($regex_array)){
                foreach ($regex_array as $regex)
                {
                    if(preg_match($regex,$filename))
                    {
                        return true;
                    }
                }
            }else{
                if(preg_match($regex_array,$filename))
                {
                    return true;
                }
            }
            return false;
        }else{
            if(empty($regex_array)){
                return true;
            }
            if(is_array($regex_array)){
                foreach ($regex_array as $regex)
                {
                    if(preg_match($regex,$filename))
                    {
                        return false;
                    }
                }
            }else{
                if(preg_match($regex_array,$filename))
                {
                    return false;
                }
            }
            return true;
        }
    }

    public function get_setting()
    {
        check_ajax_referer( 'wpvivid_ajax', 'nonce' );
        $check=current_user_can('manage_options');
        $check=apply_filters('wpvivid_ajax_check_security',$check);
        if(!$check)
        {
            die();
        };
        try {
            if (isset($_POST['all']) && is_bool($_POST['all'])) {
                $all = sanitize_key($_POST['all']);
                if (!$all) {
                    if (isset($_POST['options_name']) && is_array($_POST['options_name'])) {
                        $options_name = $_POST['options_name'];
                        $options_name_array=array();
                        foreach ($options_name as $option_name)
                        {
                            $options_name_array[]=sanitize_text_field($option_name);
                        }
                        $ret = WPvivid_Setting::get_setting($all, $options_name_array);
                        echo wp_json_encode($ret);
                    }
                } else {
                    $options_name = array();
                    $ret = WPvivid_Setting::get_setting($all, $options_name);
                    echo wp_json_encode($ret);
                }
            }
        }
        catch (Exception $error) {
            $message = 'An exception has occurred. class: '.get_class($error).';msg: '.$error->getMessage().';code: '.$error->getCode().';line: '.$error->getLine().';in_file: '.$error->getFile().';';
            error_log($message);
            echo wp_json_encode(array('result'=>'failed','error'=>$message));
            die();
        }
        die();
    }

    public function update_setting()
    {
        check_ajax_referer( 'wpvivid_ajax', 'nonce' );
        $check=current_user_can('manage_options');
        $check=apply_filters('wpvivid_ajax_check_security',$check);
        if(!$check)
        {
            die();
        };
        try {
            if (isset($_POST['options']) && !empty($_POST['options']) && is_string($_POST['options'])) {
                $json = sanitize_text_field($_POST['options']);
                $json = stripslashes($json);
                $options = json_decode($json, true);
                if (is_null($options)) {
                    die();
                }
                $ret = WPvivid_Setting::update_setting($options);
                echo wp_json_encode($ret);
            }
        }
        catch (Exception $error) {
            $message = 'An exception has occurred. class: '.get_class($error).';msg: '.$error->getMessage().';code: '.$error->getCode().';line: '.$error->getLine().';in_file: '.$error->getFile().';';
            error_log($message);
            echo wp_json_encode(array('result'=>'failed','error'=>$message));
            die();
        }
        die();
    }

    function set_default_remote_storage()
    {
        check_ajax_referer( 'wpvivid_ajax', 'nonce' );
        $check=current_user_can('manage_options');
        $check=apply_filters('wpvivid_ajax_check_security',$check);
        if(!$check)
        {
            die();
        };
        try {
            if (!isset($_POST['remote_storage']) || empty($_POST['remote_storage']) || !is_array($_POST['remote_storage'])) {
                $ret['result'] = WPVIVID_FAILED;
                $ret['error'] = __('Choose one storage from the list to be the default storage.', 'wpvivid-backuprestore');
                echo wp_json_encode($ret);
                die();
            }
            $remote_storage = $_POST['remote_storage'];
            $remote_storage = array_map( 'sanitize_text_field', $remote_storage );
            WPvivid_Setting::update_user_history('remote_selected', $remote_storage);
            $ret['result'] = 'success';
            $html = '';
            $html = apply_filters('wpvivid_add_remote_storage_list', $html);
            $ret['html'] = $html;
            $pic = '';
            $pic = apply_filters('wpvivid_schedule_add_remote_pic', $pic);
            $ret['pic'] = $pic;
            $dir = '';
            $dir = apply_filters('wpvivid_get_remote_directory', $dir);
            $ret['dir'] = $dir;
            $schedule_local_remote = '';
            $schedule_local_remote = apply_filters('wpvivid_schedule_local_remote', $schedule_local_remote);
            $ret['local_remote'] = $schedule_local_remote;
            $remote_storage = '';
            $remote_storage = apply_filters('wpvivid_remote_storage', $remote_storage);
            $ret['remote_storage'] = $remote_storage;
            $remote_select_part = '';
            $remote_select_part = apply_filters('wpvivid_remote_storage_select_part', $remote_select_part);
            $ret['remote_select_part'] = $remote_select_part;
            $default = array();
            $remote_array = apply_filters('wpvivid_archieve_remote_array', $default);
            $ret['remote_array'] = $remote_array;
            $success_msg = __('You have successfully changed your default remote storage.', 'wpvivid-backuprestore');
            $ret['notice'] = apply_filters('wpvivid_add_remote_notice', true, $success_msg);
            echo wp_json_encode($ret);
        }
        catch (Exception $error) {
            $message = 'An exception has occurred. class: '.get_class($error).';msg: '.$error->getMessage().';code: '.$error->getCode().';line: '.$error->getLine().';in_file: '.$error->getFile().';';
            error_log($message);
            echo wp_json_encode(array('result'=>'failed','error'=>$message));
            die();
        }
        die();
    }

    public function check_remote_alias_exist()
    {
        check_ajax_referer( 'wpvivid_ajax', 'nonce' );
        $check=current_user_can('manage_options');
        $check=apply_filters('wpvivid_ajax_check_security',$check);
        if(!$check)
        {
            die();
        };
        if (!isset($_POST['remote_alias']))
        {
            $remote_alias=sanitize_text_field($_POST['remote_alias']);
            $remoteslist=WPvivid_Setting::get_all_remote_options();
            foreach ($remoteslist as $key=>$value)
            {
                if(isset($value['name'])&&$value['name'] == $remote_alias)
                {
                    $ret['result']=WPVIVID_FAILED;
                    $ret['error']="Warning: The alias already exists in storage list.";
                    echo wp_json_encode($ret);
                    die();
                }
            }
            $ret['result']=WPVIVID_SUCCESS;
            echo wp_json_encode($ret);
            die();
        }

        die();
    }

    function get_default_remote_storage(){
        check_ajax_referer( 'wpvivid_ajax', 'nonce' );
        $check=current_user_can('manage_options');
        $check=apply_filters('wpvivid_ajax_check_security',$check);
        if(!$check)
        {
            die();
        };
        try {
            $ret['result'] = 'success';
            $ret['remote_storage'] = WPvivid_Setting::get_user_history('remote_selected');
            echo wp_json_encode($ret);
        }
        catch (Exception $error) {
            $message = 'An exception has occurred. class: '.get_class($error).';msg: '.$error->getMessage().';code: '.$error->getCode().';line: '.$error->getLine().';in_file: '.$error->getFile().';';
            error_log($message);
            echo wp_json_encode(array('result'=>'failed','error'=>$message));
            die();
        }
        die();
    }

    function get_default_remote_storage_ex(){
        $ret['result']='success';
        $ret['remote_storage']=WPvivid_Setting::get_user_history('remote_selected');
        return $ret;
    }

    public function get_general_setting()
    {
        check_ajax_referer( 'wpvivid_ajax', 'nonce' );
        $check=current_user_can('manage_options');
        $check=apply_filters('wpvivid_ajax_check_security',$check);
        if(!$check)
        {
            die();
        };
        try {
            if (isset($_POST['all']) && is_bool($_POST['all'])) {
                $all = sanitize_key($_POST['all']);
                if (!$all) {
                    if (isset($_POST['options_name']) && is_array($_POST['options_name'])) {
                        $options_name = $_POST['options_name'];
                        $options_name_array=array();
                        foreach ($options_name as $option_name)
                        {
                            $options_name_array[]=sanitize_text_field($option_name);
                        }
                        $ret['data']['setting'] = WPvivid_Setting::get_setting($all, $options_name_array);

                        $schedule = WPvivid_Schedule::get_schedule();
                        $schedule['next_start'] = gmdate("l, F d, Y H:i", $schedule['next_start']);
                        $ret['result'] = 'success';
                        $ret['data']['schedule'] = $schedule;
                        $ret['user_history'] = WPvivid_Setting::get_user_history('remote_selected');
                        echo wp_json_encode($ret);
                    }
                } else {
                    $options_name = array();
                    $ret['data']['setting'] = WPvivid_Setting::get_setting($all, $options_name);
                    $schedule = WPvivid_Schedule::get_schedule();
                    $schedule['next_start'] = gmdate("l, F d, Y H:i", $schedule['next_start']);
                    $ret['result'] = 'success';
                    $ret['data']['schedule'] = $schedule;
                    $ret['user_history'] = WPvivid_Setting::get_user_history('remote_selected');
                    echo wp_json_encode($ret);
                }
            }
        }
        catch (Exception $error) {
            $message = 'An exception has occurred. class: '.get_class($error).';msg: '.$error->getMessage().';code: '.$error->getCode().';line: '.$error->getLine().';in_file: '.$error->getFile().';';
            error_log($message);
            echo wp_json_encode(array('result'=>'failed','error'=>$message));
            die();
        }
        die();
    }

    public function wpvivid_set_general_setting($setting_data, $setting, $options)
    {
        $setting_data['wpvivid_common_setting']['backup_params'] = $setting['backup_params'];
        if($setting_data['wpvivid_common_setting']['backup_params'] === 'low')
        {
            $setting_data['wpvivid_common_setting']['compress_file_count'] = 500;
            $setting_data['wpvivid_common_setting']['max_file_size'] = '200';
            $setting_data['wpvivid_common_setting']['max_sql_file_size'] = 400;
            $setting_data['wpvivid_common_setting']['exclude_file_size'] = 0;
            $setting_data['wpvivid_common_setting']['max_execution_time'] = 300;
            $setting_data['wpvivid_common_setting']['memory_limit'] = '512M';
            $setting_data['wpvivid_common_setting']['migrate_size'] = '2048';
            $setting_data['wpvivid_common_setting']['max_resume_count'] = 6;
        }
        else if($setting_data['wpvivid_common_setting']['backup_params'] === 'mid')
        {
            $setting_data['wpvivid_common_setting']['compress_file_count'] = 2000;
            $setting_data['wpvivid_common_setting']['max_file_size'] = '1024';
            $setting_data['wpvivid_common_setting']['max_sql_file_size'] = 1024;
            $setting_data['wpvivid_common_setting']['exclude_file_size'] = 0;
            $setting_data['wpvivid_common_setting']['max_execution_time'] = 500;
            $setting_data['wpvivid_common_setting']['memory_limit'] = '512M';
            $setting_data['wpvivid_common_setting']['migrate_size'] = '2048';
            $setting_data['wpvivid_common_setting']['max_resume_count'] = 6;
        }
        else if($setting_data['wpvivid_common_setting']['backup_params'] === 'high')
        {
            $setting_data['wpvivid_common_setting']['compress_file_count'] = 10000;
            $setting_data['wpvivid_common_setting']['max_file_size'] = '4080';
            $setting_data['wpvivid_common_setting']['max_sql_file_size'] = 4080;
            $setting_data['wpvivid_common_setting']['exclude_file_size'] = 0;
            $setting_data['wpvivid_common_setting']['max_execution_time'] = 900;
            $setting_data['wpvivid_common_setting']['memory_limit'] = '512M';
            $setting_data['wpvivid_common_setting']['migrate_size'] = '2048';
            $setting_data['wpvivid_common_setting']['max_resume_count'] = 6;
        }
        else if($setting_data['wpvivid_common_setting']['backup_params'] === 'custom')
        {
            $setting_data['wpvivid_common_setting']['compress_file_count'] = intval($setting['compress_file_count']);
            $setting_data['wpvivid_common_setting']['max_file_size'] = $setting['max_file_size'];
            $setting_data['wpvivid_common_setting']['max_sql_file_size'] = intval($setting['max_sql_file_size']);
            $setting_data['wpvivid_common_setting']['exclude_file_size'] = intval($setting['exclude_file_size']);
            $setting_data['wpvivid_common_setting']['max_execution_time'] = intval($setting['max_execution_time']);
            $setting_data['wpvivid_common_setting']['memory_limit'] = $setting['memory_limit'].'M';
            $setting_data['wpvivid_common_setting']['migrate_size'] = $setting['migrate_size'];
            $setting_data['wpvivid_common_setting']['max_resume_count'] = $setting['max_resume_count'];
        }


        $setting['restore_max_execution_time'] = intval($setting['restore_max_execution_time']);
        $setting['max_backup_count'] = intval($setting['max_backup_count']);

        $setting_data['wpvivid_email_setting']['send_to'][] = $setting['send_to'];
        $setting_data['wpvivid_email_setting']['always'] = $setting['always'];
        if(isset($setting['email_enable'])) {
            $setting_data['wpvivid_email_setting']['email_enable'] = $setting['email_enable'];
        }

        $setting['path']=esc_attr($setting['path']);
        $setting_data['wpvivid_local_setting']['path'] = $setting['path'];

        if($options['options']['wpvivid_local_setting']['path'] !== $setting['path'])
        {
            if(file_exists(WP_CONTENT_DIR.DIRECTORY_SEPARATOR.$options['options']['wpvivid_local_setting']['path']))
            {
                @rename(WP_CONTENT_DIR.DIRECTORY_SEPARATOR.$options['options']['wpvivid_local_setting']['path'], WP_CONTENT_DIR.DIRECTORY_SEPARATOR.$setting['path']);
            }
        }

        $setting_data['wpvivid_local_setting']['save_local'] = $options['options']['wpvivid_local_setting']['save_local'];

        $setting_data['wpvivid_common_setting']['restore_max_execution_time'] = $setting['restore_max_execution_time'];
        $setting_data['wpvivid_common_setting']['log_save_location'] = $setting['path'].'/wpvivid_log';
        $setting_data['wpvivid_common_setting']['max_backup_count'] = $setting['max_backup_count'];
        $setting_data['wpvivid_common_setting']['show_admin_bar'] = $setting['show_admin_bar'];
        $setting_data['wpvivid_common_setting']['domain_include'] = $setting['domain_include'];
        $setting_data['wpvivid_common_setting']['estimate_backup'] = $setting['estimate_backup'];
        $setting_data['wpvivid_common_setting']['restore_memory_limit'] = $setting['restore_memory_limit'].'M';
        $setting_data['wpvivid_common_setting']['ismerge'] = $setting['ismerge'];
        $setting_data['wpvivid_common_setting']['db_connect_method'] = $setting['db_connect_method'];
        $setting_data['wpvivid_common_setting']['retain_local'] = $setting['retain_local'];
        $setting_data['wpvivid_common_setting']['uninstall_clear_folder'] = $setting['uninstall_clear_folder'];
        $setting_data['wpvivid_common_setting']['backup_symlink_folder'] = $setting['backup_symlink_folder'];

        //new

        $setting_data['wpvivid_common_setting']['replace_rows_pre_request'] = intval($setting['replace_rows_pre_request']);
        $setting_data['wpvivid_common_setting']['sql_file_buffer_pre_request'] = intval($setting['sql_file_buffer_pre_request']);
        $setting_data['wpvivid_common_setting']['use_index'] = intval($setting['use_index']);
        $setting_data['wpvivid_common_setting']['unzip_files_pre_request'] = intval($setting['unzip_files_pre_request']);

        $setting_data['wpvivid_common_setting']['zip_method'] = $setting['zip_method'];

		return $setting_data;
    }

    public function set_general_setting()
    {
        check_ajax_referer( 'wpvivid_ajax', 'nonce' );
        $check=current_user_can('manage_options');
        $check=apply_filters('wpvivid_ajax_check_security',$check);
        if(!$check)
        {
            die();
        };
        $ret=array();
        try
        {
            if(isset($_POST['setting'])&&!empty($_POST['setting']))
            {
                $json_setting = sanitize_text_field($_POST['setting']);
                $json_setting = stripslashes($json_setting);
                $setting = json_decode($json_setting, true);
                if (is_null($setting)){
                    die();
                }
                $ret = $this->check_setting_option($setting);
                if($ret['result']!=WPVIVID_SUCCESS)
                {
                    echo wp_json_encode($ret);
                    die();
                }
                $options=WPvivid_Setting::get_setting(true, "");
                $setting_data = array();
                $setting_data= apply_filters('wpvivid_set_general_setting',$setting_data, $setting, $options);
                $ret['setting']=WPvivid_Setting::update_setting($setting_data);
            }
        }
        catch (Exception $error)
        {
            $message = 'An exception has occurred. class: '.get_class($error).';msg: '.$error->getMessage().';code: '.$error->getCode().';line: '.$error->getLine().';in_file: '.$error->getFile().';';
            error_log($message);
            echo wp_json_encode(array('result'=>'failed','error'=>$message));
            die();
        }
        echo wp_json_encode($ret);
        die();
    }

    public function set_schedule(){
        check_ajax_referer( 'wpvivid_ajax', 'nonce' );
        $check=current_user_can('manage_options');
        $check=apply_filters('wpvivid_ajax_check_security',$check);
        if(!$check)
        {
            die();
        };

        $ret=array();

        try{
            if(isset($_POST['schedule'])&&!empty($_POST['schedule']))
            {
                $json = sanitize_text_field($_POST['schedule']);
                $json = stripslashes($json);
                $schedule = json_decode($json, true);
                if (is_null($schedule))
                {
                    die();
                }
                $ret = $this->check_schedule_option($schedule);
                if($ret['result']!=WPVIVID_SUCCESS)
                {
                    echo wp_json_encode($ret);
                    die();
                }
                //set_schedule_ex
                $ret=WPvivid_Schedule::set_schedule_ex($schedule);
                if($ret['result']!='success')
                {
                    echo wp_json_encode($ret);
                    die();
                }
            }
        }
        catch (Exception $error)
        {
            $message = 'An exception has occurred. class: '.get_class($error).';msg: '.$error->getMessage().';code: '.$error->getCode().';line: '.$error->getLine().';in_file: '.$error->getFile().';';
            error_log($message);
            echo wp_json_encode(array('result'=>'failed','error'=>$message));
            die();
        }
        echo wp_json_encode($ret);
        die();
    }

    public function check_setting_option($data)
    {
        $ret['result']=WPVIVID_FAILED;
        if(!isset($data['max_file_size']))
        {
            $ret['error']=__('The value of \'Compress file every\' can\'t be empty.', 'wpvivid-backuprestore');
            return $ret;
        }

        $data['max_file_size']=sanitize_text_field($data['max_file_size']);

        if(empty($data['max_file_size']) && $data['max_file_size'] != '0')
        {
            $ret['error']=__('The value of \'Compress file every\' can\'t be empty.', 'wpvivid-backuprestore');
            return $ret;
        }

        if(!isset($data['exclude_file_size']))
        {
            $ret['error']=__('The value of \'Exclude files which are larger than\' can\'t be empty.', 'wpvivid-backuprestore');
        }

        $data['exclude_file_size']=sanitize_text_field($data['exclude_file_size']);

        if(empty($data['exclude_file_size']) && $data['exclude_file_size'] != '0')
        {
            $ret['error']=__('The value of \'Exclude files which are larger than\' can\'t be empty.', 'wpvivid-backuprestore');
            return $ret;
        }

        if(!isset($data['max_execution_time']))
        {
            $ret['error']=__('The value of \'PHP scripts execution timeout for backup\' can\'t be empty.', 'wpvivid-backuprestore');
        }

        $data['max_execution_time']=sanitize_text_field($data['max_execution_time']);

        if(empty($data['max_execution_time']) && $data['max_execution_time'] != '0')
        {
            $ret['error']=__('The value of \'PHP scripts execution timeout for backup\' can\'t be empty.', 'wpvivid-backuprestore');
            return $ret;
        }

        //
        if(!isset($data['restore_max_execution_time']))
        {
            $ret['error']=__('The value of \'PHP scripts execution timeout for restore\' can\'t be empty.', 'wpvivid-backuprestore');
        }
        $data['restore_max_execution_time']=sanitize_text_field($data['restore_max_execution_time']);
        if(empty($data['restore_max_execution_time']) && $data['restore_max_execution_time'] != '0')
        {
            $ret['error']=__('The value of \'PHP scripts execution timeout for restore\' can\'t be empty.', 'wpvivid-backuprestore');
            return $ret;
        }

        if(!isset($data['memory_limit']))
        {
            $ret['error']=__('The value of \'PHP memory limit for backup\' can\'t be empty.', 'wpvivid-backuprestore');
        }
        $data['memory_limit']=sanitize_text_field($data['memory_limit']);
        if(empty($data['memory_limit']) && $data['memory_limit'] != '0')
        {
            $ret['error']=__('The value of \'PHP memory limit for backup\' can\'t be empty.', 'wpvivid-backuprestore');
            return $ret;
        }

        if(!isset($data['restore_memory_limit']))
        {
            $ret['error']=__('The value of \'PHP memory limit for restoration\' can\'t be empty.', 'wpvivid-backuprestore');
        }
        $data['restore_memory_limit']=sanitize_text_field($data['restore_memory_limit']);
        if(empty($data['restore_memory_limit']) && $data['restore_memory_limit'] != '0')
        {
            $ret['error']=__('The value of \'PHP memory limit for restoration\' can\'t be empty.', 'wpvivid-backuprestore');
            return $ret;
        }

        if(!isset($data['migrate_size']))
        {
            $ret['error']=__('The value of \'Chunk Size\' can\'t be empty.', 'wpvivid-backuprestore');
        }
        $data['migrate_size']=sanitize_text_field($data['migrate_size']);
        if(empty($data['migrate_size']) && $data['migrate_size'] != '0')
        {
            $ret['error']=__('The value of \'Chunk Size\' can\'t be empty.', 'wpvivid-backuprestore');
            return $ret;
        }

        $data['compress_file_count']=sanitize_text_field($data['compress_file_count']);
        if(!isset($data['compress_file_count']) || empty($data['compress_file_count']))
        {
            $ret['error']=__('The value of \'The number of files compressed to the backup zip each time\' can\'t be empty.', 'wpvivid-backuprestore');
            return $ret;
        }

        $data['max_sql_file_size']=sanitize_text_field($data['max_sql_file_size']);
        if(!isset($data['max_sql_file_size']) || empty($data['max_sql_file_size']))
        {
            $ret['error']=__('The value of \'Split a sql file every this size\' can\'t be empty.', 'wpvivid-backuprestore');
            return $ret;
        }

        $data['replace_rows_pre_request']=sanitize_text_field($data['replace_rows_pre_request']);
        if(!isset($data['replace_rows_pre_request']) || empty($data['replace_rows_pre_request']))
        {
            $ret['error']=__('The value of \'Maximum rows of data to be processed per request for restoration\' can\'t be empty.', 'wpvivid-backuprestore');
            return $ret;
        }

        $data['sql_file_buffer_pre_request']=sanitize_text_field($data['sql_file_buffer_pre_request']);
        if(!isset($data['sql_file_buffer_pre_request']) || empty($data['sql_file_buffer_pre_request']))
        {
            $ret['error']=__('The value of \'Maximum size of sql file to be imported per request for restoration\' can\'t be empty.', 'wpvivid-backuprestore');
            return $ret;
        }

        if(!isset($data['wpvivid_uc_scan_limit']))
        {
            $ret['error']=__('The value of \'Posts Quantity Processed Per Request\' can\'t be empty.', 'wpvivid-backuprestore');
        }
        $data['wpvivid_uc_scan_limit']=sanitize_text_field($data['wpvivid_uc_scan_limit']);
        if(empty($data['wpvivid_uc_scan_limit']) && $data['wpvivid_uc_scan_limit'] != '0')
        {
            $ret['error']=__('The value of \'Posts Quantity Processed Per Request\' can\'t be empty.', 'wpvivid-backuprestore');
            return $ret;
        }

        if(!isset($data['wpvivid_uc_files_limit']))
        {
            $ret['error']=__('The value of \'Media Files Quantity Processed Per Request\' can\'t be empty.', 'wpvivid-backuprestore');
        }
        $data['wpvivid_uc_files_limit']=sanitize_text_field($data['wpvivid_uc_files_limit']);
        if(empty($data['wpvivid_uc_files_limit']) && $data['wpvivid_uc_files_limit'] != '0')
        {
            $ret['error']=__('The value of \'Media Files Quantity Processed Per Request\' can\'t be empty.', 'wpvivid-backuprestore');
            return $ret;
        }

        if(!$this->wpvivid_check_staging_pro_active())
        {
            if(!isset($data['staging_db_insert_count']))
            {
                $ret['error']=__('The value of \'DB Copy Count\' can\'t be empty.', 'wpvivid-backuprestore');
            }
            $data['staging_db_insert_count']=sanitize_text_field($data['staging_db_insert_count']);
            if(empty($data['staging_db_insert_count']) && $data['staging_db_insert_count'] != '0')
            {
                $ret['error']=__('The value of \'DB Copy Count\' can\'t be empty.', 'wpvivid-backuprestore');
                return $ret;
            }

            if(!isset($data['staging_db_replace_count']))
            {
                $ret['error']=__('The value of \'DB Replace Count\' can\'t be empty.', 'wpvivid-backuprestore');
            }
            $data['staging_db_replace_count']=sanitize_text_field($data['staging_db_replace_count']);
            if(empty($data['staging_db_replace_count']) && $data['staging_db_replace_count'] != '0')
            {
                $ret['error']=__('The value of \'DB Replace Count\' can\'t be empty.', 'wpvivid-backuprestore');
                return $ret;
            }

            if(!isset($data['staging_file_copy_count']))
            {
                $ret['error']=__('The value of \'File Copy Count\' can\'t be empty.', 'wpvivid-backuprestore');
            }
            $data['staging_file_copy_count']=sanitize_text_field($data['staging_file_copy_count']);
            if(empty($data['staging_file_copy_count']) && $data['staging_file_copy_count'] != '0')
            {
                $ret['error']=__('The value of \'File Copy Count\' can\'t be empty.', 'wpvivid-backuprestore');
                return $ret;
            }

            if(!isset($data['staging_exclude_file_size']))
            {
                $ret['error']=__('The value of \'Max File Size\' can\'t be empty.', 'wpvivid-backuprestore');
            }
            $data['staging_exclude_file_size']=sanitize_text_field($data['staging_exclude_file_size']);
            if(empty($data['staging_exclude_file_size']) && $data['staging_exclude_file_size'] != '0')
            {
                $ret['error']=__('The value of \'Max File Size\' can\'t be empty.', 'wpvivid-backuprestore');
                return $ret;
            }

            if(!isset($data['staging_memory_limit']))
            {
                $ret['error']=__('The value of \'Staging Memory Limit\' can\'t be empty.', 'wpvivid-backuprestore');
            }
            $data['staging_memory_limit']=sanitize_text_field($data['staging_memory_limit']);
            if(empty($data['staging_memory_limit']) && $data['staging_memory_limit'] != '0')
            {
                $ret['error']=__('The value of \'Staging Memory Limit\' can\'t be empty.', 'wpvivid-backuprestore');
                return $ret;
            }

            if(!isset($data['staging_max_execution_time']))
            {
                $ret['error']=__('The value of \'PHP Scripts Execution Timeout\' can\'t be empty.', 'wpvivid-backuprestore');
            }
            $data['staging_max_execution_time']=sanitize_text_field($data['staging_max_execution_time']);
            if(empty($data['staging_max_execution_time']) && $data['staging_max_execution_time'] != '0')
            {
                $ret['error']=__('The value of \'PHP Scripts Execution Timeout\' can\'t be empty.', 'wpvivid-backuprestore');
                return $ret;
            }

            if(!isset($data['staging_request_timeout']))
            {
                $ret['error']=__('The value of \'Delay Between Requests\' can\'t be empty.', 'wpvivid-backuprestore');
            }
            $data['staging_request_timeout']=sanitize_text_field($data['staging_request_timeout']);
            if(empty($data['staging_request_timeout']) && $data['staging_request_timeout'] != '0')
            {
                $ret['error']=__('The value of \'Delay Between Requests\' can\'t be empty.', 'wpvivid-backuprestore');
                return $ret;
            }
        }

        //

        if(!isset($data['path']))
        {
            $ret['error']=__('The local storage path is required.', 'wpvivid-backuprestore');
        }

        $data['path']=sanitize_text_field($data['path']);

        if(empty($data['path']))
        {
            $ret['error']=__('The local storage path is required.', 'wpvivid-backuprestore');
            return $ret;
        }

        $data['email_enable']=sanitize_text_field($data['email_enable']);
        $data['send_to']=sanitize_text_field($data['send_to']);
        if($data['email_enable'] == '1')
        {
            if(empty($data['send_to']))
            {
                $ret['error']=__('An email address is required.', 'wpvivid-backuprestore');
                return $ret;
            }
        }

        if(isset($data['db_connect_method']) && $data['db_connect_method'] === 'pdo') {
            if (class_exists('PDO')) {
                $extensions = get_loaded_extensions();
                if (!array_search('pdo_mysql', $extensions)) {
                    $ret['error'] = __('The pdo_mysql extension is not detected. Please install the extension first or choose wpdb option for Database connection method.', 'wpvivid-backuprestore');
                    return $ret;
                }
            } else {
                $ret['error'] = __('The pdo_mysql extension is not detected. Please install the extension first or choose wpdb option for Database connection method.', 'wpvivid-backuprestore');
                return $ret;
            }
        }

        $ret['result']=WPVIVID_SUCCESS;
        return $ret;
    }

    public function check_schedule_option($data)
    {
        $ret['result']=WPVIVID_FAILED;

        $data['enable']=sanitize_text_field($data['enable']);
        $data['save_local_remote']=sanitize_text_field($data['save_local_remote']);

        if(!empty($data['enable'])){
            if($data['enable'] == '1'){
                if(!empty($data['save_local_remote'])){
                    if($data['save_local_remote'] == 'remote'){
                        $remote_storage=WPvivid_Setting::get_remote_options();
                        if($remote_storage == false) {
                            $ret['error']=__('There is no default remote storage configured. Please set it up first.', 'wpvivid-backuprestore');
                            return $ret;
                        }
                    }
                }
            }
        }

        $ret['result']=WPVIVID_SUCCESS;
        return $ret;
    }

    public function export_setting()
    {
        check_ajax_referer( 'wpvivid_ajax', 'nonce' );
        $check=current_user_can('manage_options');
        $check=apply_filters('wpvivid_ajax_check_security',$check);
        if(!$check)
        {
            die();
        };
        try {
            if (isset($_REQUEST['setting']) && !empty($_REQUEST['setting']) && isset($_REQUEST['history']) && !empty($_REQUEST['history']) && isset($_REQUEST['review'])) {
                $setting = sanitize_text_field($_REQUEST['setting']);
                $history = sanitize_text_field($_REQUEST['history']);
                $review = sanitize_text_field($_REQUEST['review']);

                if ($setting == '1') {
                    $setting = true;
                } else {
                    $setting = false;
                }

                if ($history == '1') {
                    $history = true;
                } else {
                    $history = false;
                }

                if ($review == '1') {
                    $review = true;
                } else {
                    $review = false;
                }

                $backup_list = false;

                $json = WPvivid_Setting::export_setting_to_json($setting, $history, $review, $backup_list);

                $parse = wp_parse_url(home_url());
                $path = '';
                if(isset($parse['path'])) {
                    $parse['path'] = str_replace('/', '_', $parse['path']);
                    $parse['path'] = str_replace('.', '_', $parse['path']);
                    $path = $parse['path'];
                }
                $parse['host'] = str_replace('/', '_', $parse['host']);
                $parse['host'] = str_replace('.', '_', $parse['host']);
                $domain_tran = $parse['host'].$path;
                $offset=get_option('gmt_offset');
                $date_format = gmdate("Ymd",time()+$offset*60*60);
                $time_format = gmdate("His",time()+$offset*60*60);
                $export_file_name = apply_filters('wpvivid_white_label_slug', 'wpvivid').'_setting-'.$domain_tran.'-'.$date_format.'-'.$time_format.'.json';
                if (!headers_sent()) {
                    header('Content-Disposition: attachment; filename='.$export_file_name);
                    //header('Content-type: application/json');
                    header('Content-Type: application/force-download');
                    header('Content-Description: File Transfer');
                    header('Cache-Control: must-revalidate');
                    header('Content-Transfer-Encoding: binary');
                }

                echo wp_json_encode($json);
            }
        }
        catch (Exception $error) {
            $message = 'An exception has occurred. class: '.get_class($error).';msg: '.$error->getMessage().';code: '.$error->getCode().';line: '.$error->getLine().';in_file: '.$error->getFile().';';
            error_log($message);
            echo wp_json_encode(array('result'=>'failed','error'=>$message));
            die();
        }
        exit;
    }

    public function import_setting()
    {
        check_ajax_referer( 'wpvivid_ajax', 'nonce' );
        $check=current_user_can('manage_options');
        $check=apply_filters('wpvivid_ajax_check_security',$check);
        if(!$check)
        {
            die();
        };
        try {
            if (isset($_POST['data']) && !empty($_POST['data']) && is_string($_POST['data'])) {
                $data = sanitize_text_field($_POST['data']);
                $data = stripslashes($data);
                $json = json_decode($data, true);
                if (is_null($json)) {
                    die();
                }
                if (json_last_error() === JSON_ERROR_NONE && is_array($json) && array_key_exists('plugin', $json) && $json['plugin'] == 'WPvivid') {
                    $json = apply_filters('wpvivid_trim_import_info', $json);
                    WPvivid_Setting::import_json_to_setting($json);
                    //WPvivid_Schedule::reset_schedule();
                    do_action('wpvivid_reset_schedule');
                    $ret['result'] = 'success';
                    $ret['slug'] = apply_filters('wpvivid_white_label_slug', 'WPvivid');
                    echo wp_json_encode($ret);
                } else {
                    $ret['result'] = 'failed';
                    $ret['error'] = __('The selected file is not the setting file for WPvivid. Please upload the right file.', 'wpvivid-backuprestore');
                    echo wp_json_encode($ret);
                }
            }
        }
        catch (Exception $error) {
            $message = 'An exception has occurred. class: '.get_class($error).';msg: '.$error->getMessage().';code: '.$error->getCode().';line: '.$error->getLine().';in_file: '.$error->getFile().';';
            error_log($message);
            echo wp_json_encode(array('result'=>'failed','error'=>$message));
            die();
        }
        die();
    }

    public function test_send_mail()
    {
        check_ajax_referer( 'wpvivid_ajax', 'nonce' );
        $check=current_user_can('manage_options');
        $check=apply_filters('wpvivid_ajax_check_security',$check);
        if(!$check)
        {
            die();
        }
        try {
            if (isset($_POST['send_to']) && !empty($_POST['send_to']) && is_string($_POST['send_to'])) {
                $send_to = sanitize_email($_POST['send_to']);
                if (empty($send_to)) {
                    $ret['result'] = 'failed';
                    $ret['error'] = __('Invalid email address', 'wpvivid-backuprestore');
                    echo wp_json_encode($ret);
                } else {
                    $subject = 'WPvivid Test Mail';
                    $body = 'This is a test mail from WPvivid backup plugin';
                    $headers = array('Content-Type: text/html; charset=UTF-8');
                    if (wp_mail($send_to, $subject, $body, $headers) === false) {
                        $ret['result'] = 'failed';
                        $ret['error'] = __('Unable to send email. Please check the configuration of email server.', 'wpvivid-backuprestore');
                    } else {
                        $ret['result'] = 'success';
                    }
                    echo wp_json_encode($ret);
                }
            }
        }
        catch (Exception $error) {
            $message = 'An exception has occurred. class: '.get_class($error).';msg: '.$error->getMessage().';code: '.$error->getCode().';line: '.$error->getLine().';in_file: '.$error->getFile().';';
            error_log($message);
            echo wp_json_encode(array('result'=>'failed','error'=>$message));
            die();
        }
        die();
    }

    public function create_debug_package()
    {
        check_ajax_referer( 'wpvivid_ajax', 'nonce' );
        $check=current_user_can('manage_options');
        $check=apply_filters('wpvivid_ajax_check_security',$check);
        if(!$check)
        {
            die();
        }
        try {
            $files = WPvivid_error_log::get_error_log();
            $staging_files = WPvivid_error_log::get_staging_error_log();

            if (!class_exists('WPvivid_PclZip'))
                include_once WPVIVID_PLUGIN_DIR . '/includes/zip/class-wpvivid-pclzip.php';

            $backup_path = WPvivid_Setting::get_backupdir();
            $path = WP_CONTENT_DIR . DIRECTORY_SEPARATOR . $backup_path . DIRECTORY_SEPARATOR . 'wpvivid_debug.zip';

            if (file_exists($path)) {
                @wp_delete_file($path);
            }
            $archive = new WPvivid_PclZip($path);

            if (!empty($files)) {
                if (!$archive->add($files, WPVIVID_PCLZIP_OPT_REMOVE_ALL_PATH)) {
                    echo esc_html($archive->errorInfo(true)) . ' <a href="' . esc_url(admin_url()) . 'admin.php?page=WPvivid">retry</a>.';
                    exit;
                }
            }

            if (!empty($staging_files)) {
                if (!$archive->add($staging_files, WPVIVID_PCLZIP_OPT_REMOVE_ALL_PATH)) {
                    echo esc_html($archive->errorInfo(true)) . ' <a href="' . esc_url(admin_url()) . 'admin.php?page=WPvivid">retry</a>.';
                    exit;
                }
            }

            $server_info = wp_json_encode($this->get_website_info());
            $server_file_path = WP_CONTENT_DIR . DIRECTORY_SEPARATOR . $backup_path . DIRECTORY_SEPARATOR . 'wpvivid_server_info.json';
            if (file_exists($server_file_path)) {
                @wp_delete_file($server_file_path);
            }
            $server_file = fopen($server_file_path, 'x');
            fclose($server_file);
            file_put_contents($server_file_path, $server_info);
            if (!$archive->add($server_file_path, WPVIVID_PCLZIP_OPT_REMOVE_ALL_PATH)) {
                echo esc_html($archive->errorInfo(true)) . ' <a href="' . esc_url(admin_url()) . 'admin.php?page=WPvivid">retry</a>.';
                exit;
            }
            @wp_delete_file($server_file_path);

            if (session_id())
                session_write_close();

            $size = filesize($path);
            if (!headers_sent()) {
                header('Content-Description: File Transfer');
                header('Content-Type: application/zip');
                header('Content-Disposition: attachment; filename="' . basename($path) . '"');
                header('Cache-Control: must-revalidate');
                header('Content-Length: ' . $size);
                header('Content-Transfer-Encoding: binary');
            }


            ob_end_clean();
            readfile($path);
            @wp_delete_file($path);
        }
        catch (Exception $error) {
            $message = 'An exception has occurred. class: '.get_class($error).';msg: '.$error->getMessage().';code: '.$error->getCode().';line: '.$error->getLine().';in_file: '.$error->getFile().';';
            error_log($message);
            echo wp_json_encode(array('result'=>'failed','error'=>$message));
            die();
        }
        exit;
    }

    public function get_log_list()
    {
        check_ajax_referer( 'wpvivid_ajax', 'nonce' );
        $check=current_user_can('manage_options');
        $check=apply_filters('wpvivid_ajax_check_security',$check);
        if(!$check)
        {
            die();
        }
        try {
            $ret['result'] = 'success';
            $html = '';
            $html = apply_filters('wpvivid_get_log_list', $html);
            $ret['html'] = $html['html'];
            $ret['log_count'] = $html['log_count'];
            echo wp_json_encode($ret);
        }
        catch (Exception $error) {
            $message = 'An exception has occurred. class: '.get_class($error).';msg: '.$error->getMessage().';code: '.$error->getCode().';line: '.$error->getLine().';in_file: '.$error->getFile().';';
            error_log($message);
            echo wp_json_encode(array('result'=>'failed','error'=>$message));
            die();
        }
        die();
    }

    public function wpvivid_get_log_list($html)
    {
        $loglist=$this->get_log_list_ex();
        $current_num=1;
        $max_log_diaplay=20;
        $log_index=0;
        $pic_log='/admin/partials/images/Log.png';
        if(!empty($loglist['log_list']['file'])) {
            foreach ($loglist['log_list']['file'] as $value) {
                if ($current_num <= $max_log_diaplay) {
                    $log_tr_display = '';
                } else {
                    $log_tr_display = 'display: none;';
                }
                if (empty($value['time'])) {
                    $value['time'] = 'N/A';
                }
                else{
                    $offset=get_option('gmt_offset');
                    $localtime = strtotime($value['time'])/* + $offset * 60 * 60*/;
                    $value['time'] = gmdate('F-d-Y H:i:s',$localtime);
                }
                if (empty($value['des'])) {
                    $value['des'] = 'N/A';
                }
                $value['path'] = str_replace('\\', '/', $value['path']);
                $html .= '<tr style="'.esc_attr($log_tr_display, 'wpvivid-backuprestore').'">
                <td class="row-title"><label for="tablecell">'.$value['time'].'</label>
                </td>
                <td>'.$value['des'].'</td>
                <td>'.$value['file_name'].'</td>
                <td>
                    <a onclick="wpvivid_read_log(\''.'wpvivid_view_log'.'\', \''.$value['id'].'\', \''.'backup'.'\')" style="cursor:pointer;">
                    <img src="'.esc_url(WPVIVID_PLUGIN_URL.$pic_log).'" style="vertical-align:middle;">Log
                    </a>
                </td>
                </tr>';
                $log_index++;
                $current_num++;
            }
        }
        $ret['log_count']=$log_index;
        $ret['html']=$html;
        return $ret;
    }

    public function wpvivid_get_log_list_output()
    {
        $loglist=$this->get_log_list_ex();
        $current_num=1;
        $max_log_diaplay=20;
        $pic_log='/admin/partials/images/Log.png';
        if(!empty($loglist['log_list']['file'])) {
            foreach ($loglist['log_list']['file'] as $value) {
                if ($current_num <= $max_log_diaplay) {
                    $log_tr_display = '';
                } else {
                    $log_tr_display = 'display: none;';
                }
                if (empty($value['time'])) {
                    $value['time'] = 'N/A';
                }
                else{
                    $localtime = strtotime($value['time']);
                    $value['time'] = gmdate('F-d-Y H:i:s',$localtime);
                }
                if (empty($value['des'])) {
                    $value['des'] = 'N/A';
                }
                $value['path'] = str_replace('\\', '/', $value['path']);
                ?>
                <tr style="<?php echo esc_attr($log_tr_display); ?>">
                    <td class="row-title"><label for="tablecell"><?php echo esc_html($value['time']); ?></label>
                    </td>
                    <td><?php echo esc_html($value['des']); ?></td>
                    <td><?php echo esc_html($value['file_name']); ?></td>
                    <td>
                        <a onclick="wpvivid_read_log('wpvivid_view_log', '<?php echo esc_js($value['id']); ?>', 'backup');" style="cursor:pointer;">
                            <img src="<?php echo esc_url(WPVIVID_PLUGIN_URL.$pic_log); ?>" style="vertical-align:middle;">Log
                        </a>
                    </td>
                </tr>
                <?php
                $current_num++;
            }
        }
    }

    public function get_log_list_ex()
    {
        $ret['log_list']['file']=array();

        if(!class_exists('WPvivid_Log'))
        {
            include_once WPVIVID_PLUGIN_DIR . '/includes/class-wpvivid-log.php';
        }

        $log=new WPvivid_Log();
        $dir=$log->GetSaveLogFolder();
        $files=array();
        $handler=opendir($dir);
        $regex='#^wpvivid.*_log.txt#';
        if($handler!==false)
        {
            while(($filename=readdir($handler))!==false)
            {
                if($filename != "." && $filename != "..")
                {
                    if(is_dir($dir.$filename))
                    {
                        continue;
                    }else{
                        if(preg_match($regex,$filename))
                        {
                            $files[$filename] = $dir.$filename;
                        }
                    }
                }
            }
            if($handler)
                @closedir($handler);
        }

        $dir.='error'.DIRECTORY_SEPARATOR;
        if(file_exists($dir))
        {
            $handler=opendir($dir);
            if($handler!==false)
            {
                while(($filename=readdir($handler))!==false)
                {
                    if($filename != "." && $filename != "..")
                    {
                        if(is_dir($dir.$filename))
                        {
                            continue;
                        }else {
                            if(preg_match($regex,$filename))
                            {
                                $files[$filename] = $dir.$filename;
                            }
                        }
                    }
                }
                if($handler)
                    @closedir($handler);
            }
        }


        foreach ($files as $file)
        {
            $handle = @fopen($file, "r");
            if ($handle)
            {
                $log_file['file_name']=basename($file);
                $log_file['id']='';
                if(preg_match('/wpvivid-(.*?)_/', basename($file), $matches))
                {
                    $id= $matches[0];
                    $id=substr($id,0,strlen($id)-1);
                    $log_file['id']=$id;
                }
                $log_file['path']=$file;
                $log_file['des']='';
                $log_file['time']='';
                if(preg_match('/error/', $file))
                {
                    $log_file['result']='failed';
                }
                else
                {
                    $log_file['result']='success';
                }
                $line = fgets($handle);
                if($line!==false)
                {
                    $pos=strpos($line,'Log created: ');
                    if($pos!==false)
                    {
                        $log_file['time']=substr ($line,$pos+strlen('Log created: '));
                    }
                }
                $line = fgets($handle);
                if($line!==false)
                {
                    $pos=strpos($line,'Type: ');
                    if($pos!==false)
                    {
                        $log_file['des']=substr ($line,$pos+strlen('Type: '));
                    }
                }

                fclose($handle);
                $ret['log_list']['file'][basename($file)]=$log_file;
            }
        }

        $ret['log_list']['file'] =$this->sort_list($ret['log_list']['file']);

        return $ret;
    }

    public function sort_list($list)
    {
        uasort ($list,function($a, $b)
        {
            if($a['time']>$b['time'])
            {
                return -1;
            }
            else if($a['time']===$b['time'])
            {
                return 0;
            }
            else
            {
                return 1;
            }
        });

        return $list;
    }

    public function view_log()
    {
        check_ajax_referer( 'wpvivid_ajax', 'nonce' );
        $check=current_user_can('manage_options');
        $check=apply_filters('wpvivid_ajax_check_security',$check);
        if(!$check)
        {
            die();
        }
        try {
            if (isset($_POST['id']) && !empty($_POST['id']) && is_string($_POST['id'])) {
                $id = sanitize_text_field($_POST['id']);

                $path = '';

                if(isset($_POST['log_type']))
                {
                    $log_type = sanitize_text_field($_POST['log_type']);
                }
                else
                {
                    $log_type = 'backup';
                }
                if($log_type === 'backup')
                {
                    $loglist=$this->get_log_list_ex();
                }
                else
                {
                    $log_page=new WPvivid_Staging_Log_Page_Free();
                    $loglist=$log_page->get_log_list('staging');
                }

                if(!empty($loglist['log_list']['file']))
                {
                    foreach ($loglist['log_list']['file'] as $value)
                    {
                        if($value['id'] === $id)
                        {
                            $path = str_replace('\\', '/', $value['path']);
                            break;
                        }
                    }
                }

                if (!file_exists($path)) {
                    $json['result'] = 'failed';
                    $json['error'] = __('The log not found.', 'wpvivid-backuprestore');
                    echo wp_json_encode($json);
                    die();
                }

                $file = fopen($path, 'r');

                if (!$file) {
                    $json['result'] = 'failed';
                    $json['error'] = __('Unable to open the log file.', 'wpvivid-backuprestore');
                    echo wp_json_encode($json);
                    die();
                }

                $buffer = '';
                while (!feof($file)) {
                    $buffer .= fread($file, 1024);
                }
                fclose($file);

                $json['result'] = 'success';
                $json['data'] = $buffer;
                echo wp_json_encode($json);
            } else {
                $json['result'] = 'failed';
                $json['error'] = __('Reading the log failed. Please try again.', 'wpvivid-backuprestore');
                echo wp_json_encode($json);
            }
        }
        catch (Exception $error) {
            $message = 'An exception has occurred. class: '.get_class($error).';msg: '.$error->getMessage().';code: '.$error->getCode().';line: '.$error->getLine().';in_file: '.$error->getFile().';';
            error_log($message);
            echo wp_json_encode(array('result'=>'failed','error'=>$message));
            die();
        }
        die();
    }

    public function get_website_info()
    {
        try {
            $version = $this->version;
            $version = apply_filters('wpvivid_display_pro_version', $version);
            $ret['result'] = 'success';
            $ret['data']['version'] = $version;
            $ret['data']['home_url'] = get_home_url();
            $ret['data']['abspath'] = ABSPATH;
            $ret['data']['wp_content_path'] = WP_CONTENT_DIR;
            $ret['data']['wp_plugin_path'] = WP_PLUGIN_DIR;
            $ret['data']['active_plugins'] = get_option('active_plugins');

            global $wp_version;
            $ret['wp_version'] = $wp_version;
            if (is_multisite()) {
                $ret['data']['multisite'] = 'enable';
            } else {
                $ret['data']['multisite'] = 'disable';
            }
            $ret['data']['web_server'] = $_SERVER["SERVER_SOFTWARE"];
            $ret['data']['php_version'] = phpversion();
            global $wpdb;
            $ret['data']['mysql_version'] = $wpdb->db_version();
            if (defined('WP_DEBUG')) {
                $ret['data']['wp_debug'] = WP_DEBUG;
            } else {
                $ret['wp_debug'] = false;
            }
            $ret['data']['language'] = get_bloginfo('language');
            $ret['data']['upload_max_filesize'] = ini_get("upload_max_filesize");

            $options = WPvivid_Setting::get_option('wpvivid_common_setting');
            if (isset($options['max_execution_time'])) {
                $limit = $options['max_execution_time'];
            } else {
                $limit = WPVIVID_MAX_EXECUTION_TIME;
            }
            ini_set('max_execution_time', $limit);

            $current_offset = get_option( 'gmt_offset' );
            $timezone       = get_option( 'timezone_string' );

            if ( false !== strpos( $timezone, 'Etc/GMT' ) ) {
                $timezone = '';
            }

            if ( empty( $timezone ) ) {
                if ( 0 == $current_offset ) {
                    $timezone = 'UTC+0';
                } elseif ( $current_offset < 0 ) {
                    $timezone = 'UTC' . $current_offset;
                } else {
                    $timezone = 'UTC+' . $current_offset;
                }
            }

            $ret['data']['max_execution_time'] = ini_get("max_execution_time");
            $ret['data']['max_input_vars'] = ini_get("max_input_vars");
            $ret['data']['max_input_vars'] = ini_get("max_input_vars");
            $ret['data']['timezone'] = $timezone;//date_default_timezone_get();
            if(function_exists('php_uname'))
            {
                $ret['data']['OS'] = php_uname();
            }
            $ret['data']['memory_current'] = $this->formatBytes(memory_get_usage());
            $ret['data']['memory_peak'] = $this->formatBytes(memory_get_peak_usage());
            $ret['data']['memory_limit'] = ini_get('memory_limit');
            $ret['data']['post_max_size'] = ini_get('post_max_size');
            $ret['data']['allow_url_fopen'] = ini_get('allow_url_fopen');
            $ret['data']['safe_mode'] = ini_get('safe_mode');
            $ret['data']['pcre.backtrack_limit'] = ini_get('pcre.backtrack_limit');
            $extensions = get_loaded_extensions();
            if (array_search('exif', $extensions)) {
                $ret['data']['exif'] = 'support';
            } else {
                $ret['data']['exif'] = 'not support';
            }

            if (array_search('xml', $extensions)) {
                $ret['data']['xml'] = 'support';
            } else {
                $ret['data']['xml'] = 'not support';
            }

            if (array_search('suhosin', $extensions)) {
                $ret['data']['suhosin'] = 'support';
            } else {
                $ret['data']['suhosin'] = 'not support';
            }

            if (array_search('gd', $extensions)) {
                $ret['data']['IPTC'] = 'support';
            } else {
                $ret['data']['IPTC'] = 'not support';
            }

            $ret['data']['extensions'] = $extensions;

            if (function_exists('apache_get_modules')) {
                $ret['data']['apache_modules'] = apache_get_modules();
            } else {
                $ret['data']['apache_modules'] = array();
            }

            if (array_search('pdo_mysql', $extensions)) {
                $ret['data']['pdo_mysql'] = 'support';
            } else {
                $ret['data']['pdo_mysql'] = 'not support';
            }

            if ($ret['data']['pdo_mysql'] == 'support') {
                include_once WPVIVID_PLUGIN_DIR . '/includes/class-wpvivid-db-method.php';
                $db_method = new WPvivid_DB_Method();
                $ret_sql_mode = $db_method->get_sql_mode();
                if ($ret_sql_mode['result'] == WPVIVID_FAILED) {
                    $ret['data']['mysql_mode'] = '';
                } else {
                    $ret['data']['mysql_mode'] = $ret_sql_mode['mysql_mode'];
                    $ret['mysql_mode'] = $ret_sql_mode['mysql_mode'];
                }
            } else {
                $ret['data']['mysql_mode'] = '';
            }
            if (!class_exists('PclZip')) include_once(ABSPATH . '/wp-admin/includes/class-pclzip.php');
            if (!class_exists('PclZip')) {
                $ret['data']['PclZip'] = 'not support';
            } else {
                $ret['data']['PclZip'] = 'support';
            }

            if (is_multisite() && !defined('MULTISITE')) {
                $prefix = $wpdb->base_prefix;
            } else {
                $prefix = $wpdb->get_blog_prefix(0);
            }

            $ret['data']['wp_prefix'] = $prefix;

            $sapi_type = php_sapi_name();

            if ($sapi_type == 'cgi-fcgi' || $sapi_type == ' fpm-fcgi') {
                $ret['data']['fast_cgi'] = 'On';
            } else {
                $ret['data']['fast_cgi'] = 'Off';
            }
        }
        catch (Exception $error) {
            $message = 'An exception has occurred. class: '.get_class($error).';msg: '.$error->getMessage().';code: '.$error->getCode().';line: '.$error->getLine().';in_file: '.$error->getFile().';';
            error_log($message);
            return array('result'=>'failed','error'=>$message);
        }
        return $ret;
    }

    public function wpvivid_add_backup_list($html, $list_name = 'wpvivid_backup_list', $tour = false)
    {
        $html = '';
        $backuplist=WPvivid_Backuplist::get_backuplist($list_name);
        $remote=array();
        $remote=apply_filters('wpvivid_remote_pic', $remote);
        $upload_title = '';
        foreach ($backuplist as $key=>$value) {
            if($value['type'] !== 'Rollback') {
                $row_style = '';
                if ($value['type'] == 'Migration' || $value['type'] == 'Upload') {
                    if ($value['type'] == 'Migration') {
                        $upload_title = 'Received Backup: ';
                    } else if ($value['type'] == 'Upload') {
                        $upload_title = __('Uploaded Backup: ', 'wpvivid-backuprestore');
                    }
                    $row_style = 'border: 2px solid #006799; box-sizing:border-box; -moz-box-sizing:border-box; -webkit-box-sizing:border-box;';
                } else if ($value['type'] == 'Manual' || $value['type'] == 'Cron') {
                    $row_style = '';
                    $upload_title = '';
                } else {
                    $upload_title = '';
                }

                if (empty($value['lock'])) {
                    $backup_lock = '/admin/partials/images/unlocked.png';
                    $lock_status = 'unlock';
                } else {
                    if ($value['lock'] == 0) {
                        $backup_lock = '/admin/partials/images/unlocked.png';
                        $lock_status = 'unlock';
                    } else {
                        $backup_lock = '/admin/partials/images/locked.png';
                        $lock_status = 'lock';
                    }
                }

                $backup_size=0;
                $backup_time=$value['create_time'];
                if(isset($value['backup']['files'])){
                    foreach ($value['backup']['files'] as $file_info){
                        $backup_size+=$file_info['size'];
                        if(preg_match('/[0-9]{4}-[0-9]{2}-[0-9]{2}-[0-9]{2}-[0-9]{2}/',$file_info['file_name'],$matches))
                        {
                            $backup_date=$matches[0];
                        }
                        else
                        {
                            $backup_date=$value['create_time'];
                        }

                        $time_array=explode('-',$backup_date);
                        if(sizeof($time_array)>4){
                            $time=$time_array[0].'-'.$time_array[1].'-'.$time_array[2].' '.$time_array[3].':'.$time_array[4];
                            $backup_time=strtotime($time);
                        }
                        //break;
                    }
                }

                $remote_pic_html = '';
                $save_local_pic_y = '/admin/partials/images/storage-local.png';
                $save_local_pic_n = '/admin/partials/images/storage-local(gray).png';
                $local_title = 'Localhost';
                if ($value['save_local'] == 1 || $value['type'] == 'Migration') {
                    $remote_pic_html .= '<img  src="' . esc_url(WPVIVID_PLUGIN_URL . $save_local_pic_y) . '" style="vertical-align:middle; " title="' . $local_title . '"/>';
                } else {
                    $remote_pic_html .= '<img  src="' . esc_url(WPVIVID_PLUGIN_URL . $save_local_pic_n) . '" style="vertical-align:middle; " title="' . $local_title . '"/>';
                }
                $b_has_remote = false;
                if (is_array($remote)) {
                    foreach ($remote as $key1 => $value1) {
                        foreach ($value['remote'] as $storage_type) {
                            $b_has_remote = true;
                            if ($key1 === $storage_type['type']) {
                                $pic = $value1['selected_pic'];
                            } else {
                                $pic = $value1['default_pic'];
                            }
                        }
                        if (!$b_has_remote) {
                            $pic = $value1['default_pic'];
                        }
                        $title = $value1['title'];
                        $remote_pic_html .= '<img  src="' . esc_url(WPVIVID_PLUGIN_URL . $pic) . '" style="vertical-align:middle; " title="' . $title . '"/>';
                    }
                }
                if ($tour) {
                    $tour = false;
                    $tour_message = '<div class="wpvivid-popuptext" id="wpvivid_popup_tour">' . esc_html__('Click the button to complete website restore or migration', 'wpvivid-backuprestore') . '</div>';
                    $tour_class = 'wpvivid-popup';
                } else {
                    $tour_message = '';
                    $tour_class = '';
                }

                $hide = 'hide';
                $html .= '<tr style="' . $row_style . '">
                <th class="check-column"><input name="check_backup" type="checkbox" id="' . esc_attr($key, 'wpvivid-backuprestore') . '" value="' . esc_attr($key, 'wpvivid-backuprestore') . '" onclick="wpvivid_click_check_backup(\'' . $key . '\', \'' . $list_name . '\');" /></th>
                <td class="tablelistcolumn">
                    <div style="float:left;padding:0 10px 10px 0;">
                        <div class="backuptime"><strong>' . $upload_title . '</strong>' . gmdate('M-d-Y H:i', $backup_time) . '</div>
                        <div class="common-table">
                            <span title="To lock the backup, the backup can only be deleted manually" id="wpvivid_lock_' . $key . '">
                            <img src="' . esc_url(WPVIVID_PLUGIN_URL . $backup_lock) . '" name="' . esc_attr($lock_status, 'wpvivid-backuprestore') . '" onclick="wpvivid_set_backup_lock(\'' . $key . '\', \'' . $lock_status . '\');" style="vertical-align:middle; cursor:pointer;"/>
                            </span>
                            <span style="margin:0;">|</span> <span>' . __('Type:', 'wpvivid-backuprestore') . '</span><span>' . $value['type'] . '</span>
                            <span style="margin:0;">|</span> <span title="Backup log"><a href="#" onclick="wpvivid_read_log(\'' . 'wpvivid_view_backup_log' . '\', \'' . $key . '\');"><img src="' . esc_url(WPVIVID_PLUGIN_URL . '/admin/partials/images/Log.png') . '" style="vertical-align:middle;cursor:pointer;"/><span style="margin:0;">' . __('Log', 'wpvivid-backuprestore') . '</span></a></span>
                        </div>
                    </div>
                </td>
                <td class="tablelistcolumn">
                    <div style="float:left;padding:10px 10px 10px 0;">' . $remote_pic_html . '</div>
                </td>
               
                <td class="tablelistcolumn" style="min-width:100px;">
                    <div id="wpvivid_file_part_' .$key . '" style="float:left;padding:10px 10px 10px 0;">
                        <div style="cursor:pointer;" onclick="wpvivid_initialize_download(\'' . $key . '\', \'' . $list_name . '\');" title="'. esc_html__('Prepare to download the backup', 'wpvivid-backuprestore') .'">
                            <img id="wpvivid_download_btn_' . $key . '" src="' . esc_url(WPVIVID_PLUGIN_URL . '/admin/partials/images/download.png') . '" style="vertical-align:middle;" /><span>' . __('Download', 'wpvivid-backuprestore') . ' (' . esc_html(size_format($backup_size, 2)) . ')' .'</span>
                            <div class="spinner" id="wpvivid_download_loading_' . $key . '" style="float:right;width:auto;height:auto;padding:10px 180px 10px 0;background-position:0 0;"></div>
                        </div>
                    </div>
                </td>
                <td class="tablelistcolumn" style="min-width:100px;">
                    <div class="' . $tour_class . '" onclick="wpvivid_popup_tour(\'' . $hide . '\');">
                        ' . $tour_message . '<div style="cursor:pointer;padding:10px 0 10px 0;" onclick="wpvivid_initialize_restore(\'' . $key . '\',\'' . gmdate('M-d-Y H:i', $backup_time) . '\',\'' . $value['type'] . '\');" style="float:left;padding:10px 10px 10px 0;">
                            <img src="' . esc_url(WPVIVID_PLUGIN_URL . '/admin/partials/images/Restore.png') . '" style="vertical-align:middle;" /><span>' . __('Restore', 'wpvivid-backuprestore') . '</span>
                        </div>
                    </div>
                </td>
                <td class="tablelistcolumn">
                    <div class="backuplist-delete-backup" style="padding:10px 0 10px 0;">
                        <img src="' . esc_url(WPVIVID_PLUGIN_URL . '/admin/partials/images/Delete.png') . '" style="vertical-align:middle; cursor:pointer;" title="'. __('Delete the backup', 'wpvivid-backuprestore') .'" onclick="wpvivid_delete_selected_backup(\'' . $key . '\', \'' . $list_name . '\');"/>
                    </div>
                </td>
            </tr>';
            }
        }
        return $html;
    }

    public function wpvivid_add_backup_list_output()
    {
        $list_name = 'wpvivid_backup_list';
        $backuplist=WPvivid_Backuplist::get_backuplist($list_name);
        $remote=array();
        $remote=apply_filters('wpvivid_remote_pic', $remote);
        $upload_title = '';
        foreach ($backuplist as $key=>$value) {
            if($value['type'] !== 'Rollback') {
                $row_style = '';
                if ($value['type'] == 'Migration' || $value['type'] == 'Upload') {
                    if ($value['type'] == 'Migration') {
                        $upload_title = 'Received Backup: ';
                    } else if ($value['type'] == 'Upload') {
                        $upload_title = __('Uploaded Backup: ', 'wpvivid-backuprestore');
                    }
                    $row_style = 'border: 2px solid #006799; box-sizing:border-box; -moz-box-sizing:border-box; -webkit-box-sizing:border-box;';
                } else if ($value['type'] == 'Manual' || $value['type'] == 'Cron') {
                    $row_style = '';
                    $upload_title = '';
                } else {
                    $upload_title = '';
                }

                if (empty($value['lock'])) {
                    $backup_lock = '/admin/partials/images/unlocked.png';
                    $lock_status = 'unlock';
                } else {
                    if ($value['lock'] == 0) {
                        $backup_lock = '/admin/partials/images/unlocked.png';
                        $lock_status = 'unlock';
                    } else {
                        $backup_lock = '/admin/partials/images/locked.png';
                        $lock_status = 'lock';
                    }
                }

                $backup_size=0;
                $backup_time=$value['create_time'];
                if(isset($value['backup']['files'])){
                    foreach ($value['backup']['files'] as $file_info){
                        $backup_size+=$file_info['size'];
                        if(preg_match('/[0-9]{4}-[0-9]{2}-[0-9]{2}-[0-9]{2}-[0-9]{2}/',$file_info['file_name'],$matches))
                        {
                            $backup_date=$matches[0];
                        }
                        else
                        {
                            $backup_date=$value['create_time'];
                        }

                        $time_array=explode('-',$backup_date);
                        if(sizeof($time_array)>4){
                            $time=$time_array[0].'-'.$time_array[1].'-'.$time_array[2].' '.$time_array[3].':'.$time_array[4];
                            $backup_time=strtotime($time);
                        }
                        //break;
                    }
                }

                $hide = 'hide';
                ?>
                <tr style="<?php echo esc_attr($row_style); ?>">
                    <th class="check-column"><input name="check_backup" type="checkbox" id="<?php echo esc_attr($key); ?>" value="<?php echo esc_attr($key); ?>" onclick="wpvivid_click_check_backup('<?php echo esc_js($key); ?>', '<?php echo esc_js($list_name); ?>');" /></th>
                    <td class="tablelistcolumn">
                        <div style="float:left;padding:0 10px 10px 0;">
                            <div class="backuptime"><strong><?php echo esc_html($upload_title); ?></strong><?php echo esc_html(gmdate('M-d-Y H:i', $backup_time)); ?></div>
                            <div class="common-table">
                            <span title="To lock the backup, the backup can only be deleted manually" id="wpvivid_lock_<?php echo esc_attr($key); ?>">
                            <img src="<?php echo esc_url(WPVIVID_PLUGIN_URL . $backup_lock); ?>" name="<?php echo esc_attr($lock_status); ?>" onclick="wpvivid_set_backup_lock('<?php echo esc_js($key); ?>', '<?php echo esc_js($lock_status); ?>');" style="vertical-align:middle; cursor:pointer;"/>
                            </span>
                                <span style="margin:0;">|</span> <span><?php esc_html_e('Type: ', 'wpvivid-backuprestore'); ?></span><span><?php echo esc_html($value['type']); ?></span>
                                <span style="margin:0;">|</span> <span title="Backup log"><a href="#" onclick="wpvivid_read_log('wpvivid_view_backup_log', '<?php echo esc_js($key); ?>');"><img src="<?php echo esc_url(WPVIVID_PLUGIN_URL.'/admin/partials/images/Log.png'); ?>" style="vertical-align:middle;cursor:pointer;"/><span style="margin:0;"><?php esc_html_e('Log', 'wpvivid-backuprestore'); ?></span></a></span>
                            </div>
                        </div>
                    </td>
                    <td class="tablelistcolumn">
                        <div style="float:left;padding:10px 10px 10px 0;">
                            <?php
                            $remote_pic_html = '';
                            $save_local_pic_y = '/admin/partials/images/storage-local.png';
                            $save_local_pic_n = '/admin/partials/images/storage-local(gray).png';
                            $local_title = 'Localhost';
                            if ($value['save_local'] == 1 || $value['type'] == 'Migration') {
                                echo '<img  src="'.esc_url(WPVIVID_PLUGIN_URL . $save_local_pic_y).'" style="vertical-align:middle; " title="'.esc_attr($local_title).'"/>';
                            } else {
                                echo '<img  src="'.esc_url(WPVIVID_PLUGIN_URL . $save_local_pic_n).'" style="vertical-align:middle; " title="'.esc_attr($local_title).'"/>';
                            }
                            $b_has_remote = false;
                            if (is_array($remote)) {
                                foreach ($remote as $key1 => $value1) {
                                    $pic = $value1['default_pic'];
                                    foreach ($value['remote'] as $storage_type) {
                                        $b_has_remote = true;
                                        if ($key1 === $storage_type['type']) {
                                            $pic = $value1['selected_pic'];
                                        } else {
                                            $pic = $value1['default_pic'];
                                        }
                                    }
                                    if (!$b_has_remote) {
                                        $pic = $value1['default_pic'];
                                    }
                                    $title = $value1['title'];
                                    echo '<img  src="'.esc_url(WPVIVID_PLUGIN_URL . $pic).'" style="vertical-align:middle; " title="'.esc_attr($title).'"/>';
                                }
                            }
                            ?>
                        </div>
                    </td>
                    <td class="tablelistcolumn" style="min-width:100px;">
                        <div id="wpvivid_file_part_<?php echo esc_attr($key); ?>" style="float:left;padding:10px 10px 10px 0;">
                            <div style="cursor:pointer;" onclick="wpvivid_initialize_download('<?php echo esc_js($key); ?>', '<?php echo esc_js($list_name); ?>');" title="<?php esc_attr_e('Prepare to download the backup', 'wpvivid-backuprestore'); ?>">
                                <img id="wpvivid_download_btn_<?php echo esc_attr($key); ?>" src="<?php echo esc_url(WPVIVID_PLUGIN_URL.'/admin/partials/images/download.png'); ?>" style="vertical-align:middle;" /><span><?php esc_html_e('Download', 'wpvivid-backuprestore'); echo ' ('.esc_html(size_format($backup_size, 2)).')'; ?></span>
                                <div class="spinner" id="wpvivid_download_loading_<?php esc_attr($key); ?>" style="float:right;width:auto;height:auto;padding:10px 180px 10px 0;background-position:0 0;"></div>
                            </div>
                        </div>
                    </td>
                    <td class="tablelistcolumn" style="min-width:100px;">
                        <div onclick="wpvivid_popup_tour('<?php echo esc_js($hide); ?>');">
                            <div style="cursor:pointer;padding:10px 0 10px 0;" onclick="wpvivid_initialize_restore('<?php echo esc_js($key); ?>','<?php echo esc_js(gmdate('M-d-Y H:i', $backup_time)); ?>','<?php echo esc_js($value['type']); ?>');" style="float:left;padding:10px 10px 10px 0;">
                                <img src="<?php echo esc_url(WPVIVID_PLUGIN_URL . '/admin/partials/images/Restore.png'); ?>" style="vertical-align:middle;" /><span><?php esc_html_e('Restore', 'wpvivid-backuprestore'); ?></span>
                            </div>
                        </div>
                    </td>
                    <td class="tablelistcolumn">
                        <div class="backuplist-delete-backup" style="padding:10px 0 10px 0;">
                            <img src="<?php echo esc_url(WPVIVID_PLUGIN_URL . '/admin/partials/images/Delete.png'); ?>" style="vertical-align:middle; cursor:pointer;" title="<?php esc_attr_e('Delete the backup', 'wpvivid-backuprestore'); ?>" onclick="wpvivid_delete_selected_backup('<?php echo esc_js($key); ?>', '<?php echo esc_js($list_name); ?>');"/>
                        </div>
                    </td>
                </tr>
                <?php
            }
        }
    }

    public function wpvivid_add_remote_storage_list($html)
    {
        $html = '';
        $remoteslist=WPvivid_Setting::get_all_remote_options();
        $default_remote_storage='';
        foreach ($remoteslist['remote_selected'] as $value) {
            $default_remote_storage=$value;
        }
        $i=1;
        foreach ($remoteslist as $key=>$value)
        {
            if($key === 'remote_selected')
            {
                continue;
            }
            if ($key === $default_remote_storage)
            {
                $check_status = 'checked';
            }
            else
            {
                $check_status='';
            }
            $storage_type = $value['type'];
            $storage_type=apply_filters('wpvivid_storage_provider_tran', $storage_type);
            $html .= '<tr>
                <td>'.$i++.'</td>
                <td><input type="checkbox" name="remote_storage" value="'.esc_attr($key).'" '.esc_attr($check_status).' /></td>
                <td>'.$storage_type.'</td>
                <td class="row-title"><label for="tablecell">'.$value['name'].'</label></td>
                <td>
                    <div style="float: left;"><img src="'.esc_url(WPVIVID_PLUGIN_URL.'/admin/partials/images/Edit.png').'" onclick="click_retrieve_remote_storage(\''.esc_attr($key).'\',\''.esc_attr($value['type']).'\',\''.esc_attr($value['name']).'\'
                    );" style="vertical-align:middle; cursor:pointer;" title="'. esc_html__('Edit the remote storage', 'wpvivid-backuprestore') .'"/></div>
                    <div><img src="'.esc_url(WPVIVID_PLUGIN_URL.'/admin/partials/images/Delete.png').'" onclick="wpvivid_delete_remote_storage(\''.esc_attr($key).'\'
                    );" style="vertical-align:middle; cursor:pointer;" title="'. esc_html__('Remove the remote storage', 'wpvivid-backuprestore') .'"/></div>
                </td>
                </tr>';
        }
        return $html;
    }

    public function wpvivid_remote_storage($remote_storage){
        $remote_id_array = WPvivid_Setting::get_user_history('remote_selected');
        $remote_storage = false;
        foreach ($remote_id_array as $value){
            $remote_storage = true;
        }
        return $remote_storage;
    }

    public function wpvivid_add_remote_notice($notice_type, $message){
        $html = '';
        if($notice_type) {
            $html .= '<div class="notice notice-success is-dismissible inline"><p>'.$message.'</p>
                                    <button type="button" class="notice-dismiss" onclick="click_dismiss_notice(this);">
                                    <span class="screen-reader-text">Dismiss this notice.</span>
                                    </button>
                                    </div>';

        }
        else{
            $html .= '<div class="notice notice-error"><p>' . $message . '</p></div>';
        }
        return $html;
    }

    public function wpvivid_schedule_add_remote_pic($html){
        $html = '';
        $remoteslist=WPvivid_Setting::get_all_remote_options();
        $default_remote_storage=array();
        foreach ($remoteslist['remote_selected'] as $value) {
            $default_remote_storage[]=$value;
        }
        $remote_storage_type=array();
        foreach ($remoteslist as $key=>$value)
        {
            if(in_array($key, $default_remote_storage))
            {
                $remote_storage_type[]=$value['type'];
            }
        }

        $remote=array();
        $remote=apply_filters('wpvivid_remote_pic', $remote);
        if(is_array($remote)) {
            foreach ($remote as $key => $value) {
                $title = $value['title'];
                if (in_array($key, $remote_storage_type)) {
                    $pic = $value['selected_pic'];
                } else {
                    $pic = $value['default_pic'];
                }
                $url = apply_filters('wpvivid_get_wpvivid_pro_url', WPVIVID_PLUGIN_URL, $key);
                $html .= '<img  src="' . esc_url($url . $pic) . '" style="vertical-align:middle; " title="' . $title . '"/>';
            }
            $html.='<img onclick="wpvivid_click_switch_page(\'wrap\', \'wpvivid_tab_remote_storage\', true);" src="'.esc_url(WPVIVID_PLUGIN_URL.'/admin/partials/images/add-storages.png').'" style="vertical-align:middle;" title="'.esc_attr__('Add a storage', 'wpvivid-backuprestore').'"/>';
        }
        return $html;
    }

    public function wpvivid_schedule_local_remote($html){
        $html = '';
        $schedule=WPvivid_Schedule::get_schedule();
        $backup_local = 'checked';
        $backup_remote = '';
        if($schedule['enable'] == true)
        {
            if($schedule['backup']['remote'] === 1)
            {
                $backup_local = '';
                $backup_remote = 'checked';
            }
            else{
                $backup_local = 'checked';
                $backup_remote = '';
            }
        }
        $html .= '<fieldset>
                   <label title="">
                        <input type="radio" option="schedule" name="save_local_remote" value="local" '.$backup_local.' />
                        <span>'.__( 'Save backups on localhost (web server)', 'wpvivid-backuprestore' ).'</span>
                   </label><br>
                   <label title="">
                        <input type="radio" option="schedule" name="save_local_remote" value="remote" '.$backup_remote.' />
                        <span>'.__( 'Send backups to remote storage (You can choose whether to keep the backup in localhost after it is uploaded to cloud storage in Settings.)', 'wpvivid-backuprestore' ).'</span>
                   </label>
                   <label style="display: none;">
                        <input type="checkbox" option="schedule" name="lock" value="0" />
                   </label>
                   </fieldset>';
        return $html;
    }

    public function wpvivid_get_remote_directory($out_of_date_remote){
        $out_of_date=$this->_get_out_of_date_info();
        $out_of_date_remote='There is no path for remote storage, please set it up first.';

        if($out_of_date['remote_options'] !== false)
        {
            $out_of_date_remote_temp = array();
            foreach ($out_of_date['remote_options'] as $value)
            {
                $out_of_date_remote=apply_filters('wpvivid_get_out_of_date_remote',$out_of_date_remote, $value);
                $value['type']=apply_filters('wpvivid_storage_provider_tran', $value['type']);
                $out_of_date_remote_temp[] = $value['type'].': '.$out_of_date_remote;
            }
            $out_of_date_remote = implode(',', $out_of_date_remote_temp);
        }
        return $out_of_date_remote;
    }

    public function init_remote_option()
    {
        $remoteslist=WPvivid_Setting::get_all_remote_options();
        foreach ($remoteslist as $key=>$value)
        {
            if(!array_key_exists('options',$value))
            {
                continue;
            }
            $remote = array();
            if($value['type'] === 'ftp')
            {
                $remote['host']=$value['options']['host'];
                $remote['username']=$value['options']['username'];
                $remote['password']=$value['options']['password'];
                $remote['path']=$value['options']['path'];
                $remote['name']=$value['options']['name'];
                $remote['passive']=$value['options']['passive'];
                $value['type'] = strtolower($value['type']);
                $remote['type']=$value['type'];
                $remoteslist[$key]=$remote;
            }
            elseif ($value['type'] === 'sftp')
            {
                $remote['host']=$value['options']['host'];
                $remote['username']=$value['options']['username'];
                $remote['password']=$value['options']['password'];
                $remote['path']=$value['options']['path'];
                $remote['name']=$value['options']['name'];
                $remote['port']=$value['options']['port'];
                $value['type'] = strtolower($value['type']);
                $remote['type']=$value['type'];
                $remoteslist[$key]=$remote;
            }
            elseif ($value['type'] === 'amazonS3')
            {
                $remote['classMode']='0';
                $remote['sse']='0';
                $remote['name']=$value['options']['name'];
                $remote['access']=$value['options']['access'];
                $remote['secret']=$value['options']['secret'];
                $remote['s3Path']=$value['options']['s3Path'];
                $value['type'] = strtolower($value['type']);
                $remote['type']=$value['type'];
                $remoteslist[$key]=$remote;
            }
        }
        WPvivid_Setting::update_option('wpvivid_upload_setting',$remoteslist);

        $backuplist=WPvivid_Backuplist::get_backuplist();
        foreach ($backuplist as $key=>$value)
        {
            if(is_array($value['remote']))
            {
                foreach ($value['remote'] as $remote_key=>$storage_type)
                {
                    if(!array_key_exists('options',$storage_type))
                    {
                        continue;
                    }
                    $remote = array();
                    if($storage_type['type'] === 'ftp')
                    {
                        $remote['host']=$storage_type['options']['host'];
                        $remote['username']=$storage_type['options']['username'];
                        $remote['password']=$storage_type['options']['password'];
                        $remote['path']=$storage_type['options']['path'];
                        $remote['name']=$storage_type['options']['name'];
                        $remote['passive']=$storage_type['options']['passive'];
                        $storage_type['type'] = strtolower($storage_type['type']);
                        $remote['type']=$storage_type['type'];
                    }
                    elseif ($storage_type['type'] === 'sftp')
                    {
                        $remote['host']=$storage_type['options']['host'];
                        $remote['username']=$storage_type['options']['username'];
                        $remote['password']=$storage_type['options']['password'];
                        $remote['path']=$storage_type['options']['path'];
                        $remote['name']=$storage_type['options']['name'];
                        $remote['port']=$storage_type['options']['port'];
                        $storage_type['type'] = strtolower($storage_type['type']);
                        $remote['type']=$storage_type['type'];
                    }
                    elseif ($storage_type['type'] === 'amazonS3')
                    {
                        $remote['classMode']='0';
                        $remote['sse']='0';
                        $remote['name']=$storage_type['options']['name'];
                        $remote['access']=$storage_type['options']['access'];
                        $remote['secret']=$storage_type['options']['secret'];
                        $remote['s3Path']=$storage_type['options']['s3Path'];
                        $storage_type['type'] = strtolower($storage_type['type']);
                        $remote['type']=$storage_type['type'];
                    }
                    $backuplist[$key]['remote'][$remote_key]=$remote;
                }
            }
        }
        WPvivid_Setting::update_option('wpvivid_backup_list',$backuplist);
    }

    public function need_review()
    {
        check_ajax_referer( 'wpvivid_ajax', 'nonce' );
        $check=current_user_can('manage_options');
        $check=apply_filters('wpvivid_ajax_check_security',$check);
        if(!$check)
        {
            die();
        }
        try {
            if (isset($_POST['review']) && !empty($_POST['review']) && is_string($_POST['review'])) {
                $review = sanitize_text_field($_POST['review']);
                if ($review == 'rate-now') {
                    $review_option = 'do_not_ask';
                    echo 'https://wordpress.org/support/plugin/wpvivid-backuprestore/reviews/?filter=5';
                } elseif ($review == 'never-ask') {
                    $review_option = 'do_not_ask';
                    echo '';
                } elseif ($review == 'already-done'){
                    $review_option = 'do_not_ask';
                    echo '';
                } elseif ($review == 'ask-later') {
                    $review_option = 'not';
                    $review_type = WPvivid_Setting::get_option('wpvivid_review_type', false);
                    if($review_type === 'manual' || $review_type === 'migration')
                    {
                        WPvivid_Setting::update_option('wpvivid_review_time', time()+ 7*24*60*60);
                    }
                    WPvivid_Setting::update_option('cron_backup_count', 0);
                    echo '';
                } elseif ($review == 'dismiss') {
                    $review_option = 'not';
                    $review_type = WPvivid_Setting::get_option('wpvivid_review_type', false);
                    if($review_type === 'manual' || $review_type === 'migration')
                    {
                        WPvivid_Setting::update_option('wpvivid_review_time', time()+ 14*24*60*60);
                        WPvivid_Setting::update_option('cron_backup_count', 0);
                    }
                    echo '';
                } else {
                    $review_option = 'not';
                    echo '';
                }
                WPvivid_Setting::update_option('wpvivid_need_review', $review_option);
            }
        }
        catch (Exception $error) {
            $message = 'An exception has occurred. class: '.get_class($error).';msg: '.$error->getMessage().';code: '.$error->getCode().';line: '.$error->getLine().';in_file: '.$error->getFile().';';
            error_log($message);
            echo wp_json_encode(array('result'=>'failed','error'=>$message));
            die();
        }
        die();
    }

    public function wpvivid_send_debug_info(){
        check_ajax_referer( 'wpvivid_ajax', 'nonce' );
        $check=current_user_can('manage_options');
        $check=apply_filters('wpvivid_ajax_check_security',$check);
        if(!$check)
        {
            die();
        }
        try {
            if (!isset($_POST['user_mail']) || empty($_POST['user_mail'])) {
                $ret['result'] = 'failed';
                $ret['error'] = __('User\'s email address is required.', 'wpvivid-backuprestore');
            } else {
                $pattern = '/^[a-z0-9]+([._-][a-z0-9]+)*@([0-9a-z-]+\.[a-z]{2,14}(\.[a-z]{2})?)$/i';
                if (!preg_match($pattern, $_POST['user_mail'])) {
                    $ret['result'] = 'failed';
                    $ret['error'] = __('Please enter a valid email address.', 'wpvivid-backuprestore');
                } else {
                    $user_mail=sanitize_email($_POST['user_mail']);
                    $server_type=sanitize_text_field($_POST['server_type']);
                    $host_provider=sanitize_text_field($_POST['host_provider']);
                    $comment=sanitize_text_field($_POST['comment']);
                    if(!class_exists('WPvivid_mail_report'))
                        include_once WPVIVID_PLUGIN_DIR . '/includes/class-wpvivid-mail-report.php';
                    $ret = WPvivid_mail_report::wpvivid_send_debug_info($user_mail,$server_type,$host_provider,$comment);
                }
            }
            echo wp_json_encode($ret);
        }
        catch (Exception $error) {
            $message = 'An exception has occurred. class: '.get_class($error).';msg: '.$error->getMessage().';code: '.$error->getCode().';line: '.$error->getLine().';in_file: '.$error->getFile().';';
            error_log($message);
            echo wp_json_encode(array('result'=>'failed','error'=>$message));
            die();
        }
        die();
    }

    public function get_ini_memory_limit(){
        check_ajax_referer( 'wpvivid_ajax', 'nonce' );
        $check=current_user_can('manage_options');
        $check=apply_filters('wpvivid_ajax_check_security',$check);
        if(!$check)
        {
            die();
        }
        try {
            $memory_limit = @ini_get('memory_limit');
            echo esc_html($memory_limit);
        }
        catch (Exception $error) {
            $message = 'An exception has occurred. class: '.get_class($error).';msg: '.$error->getMessage().';code: '.$error->getCode().';line: '.$error->getLine().';in_file: '.$error->getFile().';';
            error_log($message);
            echo wp_json_encode(array('result'=>'failed','error'=>$message));
            die();
        }
        die();
    }

    public function wpvivid_switch_domain_to_folder($domain){
        $parse = wp_parse_url($domain);
        $path = '';
        if(isset($parse['path'])) {
            $parse['path'] = str_replace('/', '_', $parse['path']);
            $parse['path'] = str_replace('.', '_', $parse['path']);
            $path = $parse['path'];
        }
        $parse['host'] = str_replace('/', '_', $parse['host']);
        $parse['host'] = str_replace('.', '_', $parse['host']);
        return $parse['host'].$path;
    }

    public function wpvivid_check_zip_valid()
    {
        return true;
    }

    public function amazons3_notice()
    {
        check_ajax_referer( 'wpvivid_ajax', 'nonce' );
        $check=current_user_can('manage_options');
        $check=apply_filters('wpvivid_ajax_check_security',$check);
        if(!$check)
        {
            die();
        }
        try {
            $notice_message = 'init';
            WPvivid_Setting::update_option('wpvivid_amazons3_notice', $notice_message);
        }
        catch (Exception $error) {
            $message = 'An exception has occurred. class: '.get_class($error).';msg: '.$error->getMessage().';code: '.$error->getCode().';line: '.$error->getLine().';in_file: '.$error->getFile().';';
            error_log($message);
            echo wp_json_encode(array('result'=>'failed','error'=>$message));
            die();
        }
        die();
    }

    public function wpvivid_check_type_database($is_type_db, $data){
        if(isset($data['dump_db'])){
            $is_type_db = true;
        }
        return $is_type_db;
    }

    public function hide_mainwp_tab_page(){
        check_ajax_referer( 'wpvivid_ajax', 'nonce' );
        $check=current_user_can('manage_options');
        $check=apply_filters('wpvivid_ajax_check_security',$check);
        if(!$check)
        {
            die();
        }
        WPvivid_Setting::update_option('wpvivid_hide_mwp_tab_page_v1', true);
        $ret['result']=WPVIVID_SUCCESS;
        echo wp_json_encode($ret);
        die();
    }

    public function hide_wp_cron_notice(){
        check_ajax_referer( 'wpvivid_ajax', 'nonce' );
        $check=current_user_can('manage_options');
        $check=apply_filters('wpvivid_ajax_check_security',$check);
        if(!$check)
        {
            die();
        }
        WPvivid_Setting::update_option('wpvivid_hide_wp_cron_notice', true);
        $ret['result']=WPVIVID_SUCCESS;
        echo wp_json_encode($ret);
        die();
    }

    public function download_backup_mainwp()
    {
        check_ajax_referer( 'wpvivid_ajax', 'nonce' );
        $check=current_user_can('manage_options');
        $check=apply_filters('wpvivid_ajax_check_security',$check);
        if(!$check)
        {
            die();
        }
        try {
            if (isset($_REQUEST['backup_id']) && isset($_REQUEST['file_name'])) {
                if (!empty($_REQUEST['backup_id']) && is_string($_REQUEST['backup_id'])) {
                    $backup_id = sanitize_key($_REQUEST['backup_id']);
                } else {
                    die();
                }

                if (!empty($_REQUEST['file_name']) && is_string($_REQUEST['file_name'])) {
                    //$file_name=sanitize_file_name($_REQUEST['file_name']);
                    $file_name = sanitize_text_field($_REQUEST['file_name']);
                } else {
                    die();
                }

                $cache = WPvivid_taskmanager::get_download_cache($backup_id);
                if ($cache === false) {
                    $this->init_download($backup_id);
                    $cache = WPvivid_taskmanager::get_download_cache($backup_id);
                }
                $path = false;
                if (array_key_exists($file_name, $cache['files'])) {
                    if ($cache['files'][$file_name]['status'] == 'completed') {
                        $path = $cache['files'][$file_name]['download_path'];
                    }
                }
                if ($path !== false) {
                    if (file_exists($path)) {
                        if (session_id())
                            session_write_close();

                        $size = filesize($path);
                        if (!headers_sent()) {
                            header('Content-Description: File Transfer');
                            header('Content-Type: application/zip');
                            header('Content-Disposition: attachment; filename="' . basename($path) . '"');
                            header('Cache-Control: must-revalidate');
                            header('Content-Length: ' . $size);
                            header('Content-Transfer-Encoding: binary');
                        }

                        if ($size < 1024 * 1024 * 60) {
                            ob_end_clean();
                            readfile($path);
                            exit;
                        } else {
                            ob_end_clean();
                            $download_rate = 1024 * 10;
                            $file = fopen($path, "r");
                            while (!feof($file)) {
                                @set_time_limit(20);
                                // send the current file part to the browser
                                print fread($file, round($download_rate * 1024));
                                // flush the content to the browser
                                ob_flush();
                                flush();

                                // sleep one second
                                sleep(1);
                            }
                            fclose($file);
                            exit;
                        }
                    }
                }
            }
        }
        catch (Exception $error) {
            $message = 'An exception has occurred. class: '.get_class($error).';msg: '.$error->getMessage().';code: '.$error->getCode().';line: '.$error->getLine().';in_file: '.$error->getFile().';';
            error_log($message);
            echo wp_json_encode(array('result'=>'failed','error'=>$message));
            die();
        }
        $admin_url = admin_url();
        echo '<a href="'.esc_url($admin_url).'admin.php?page=WPvivid">file not found. please retry again.</a>';
        die();
    }

    public function set_mail_subject($subject, $task){
        if(!class_exists('WPvivid_mail_report'))
            include_once WPVIVID_PLUGIN_DIR . '/includes/class-wpvivid-mail-report.php';
        $subject=WPvivid_mail_report::create_subject($task);
        return $subject;
    }

    public function set_mail_body($body, $task){
        if(!class_exists('WPvivid_mail_report'))
            include_once WPVIVID_PLUGIN_DIR . '/includes/class-wpvivid-mail-report.php';
        $body=WPvivid_mail_report::create_body($task);
        return $body;
    }

    public function wpvivid_handle_mainwp_action($data){
        $public_interface = new WPvivid_Public_Interface();
        $ret = $public_interface->mainwp_data($data);
        return $ret;
    }

    public function get_zip_object_class($class)
    {
        /*if(version_compare(phpversion(),'8.0.0','>='))
        {
            return 'WPvivid_PclZip_Class_Ex';
        }
        return $class;*/
        return 'WPvivid_PclZip_Class_Ex';
    }

    public function get_backup_path($backup_item, $file_name)
    {
        $path = $backup_item->get_local_path() . $file_name;

        if (file_exists($path)) {
            return $path;
        }
        else{
            $local_setting = get_option('wpvivid_local_setting', array());
            if(!empty($local_setting))
            {
                $path = WP_CONTENT_DIR . DIRECTORY_SEPARATOR . $local_setting['path'] . DIRECTORY_SEPARATOR . $file_name;
            }
            else {
                $path = WP_CONTENT_DIR . DIRECTORY_SEPARATOR . 'wpvividbackups' . DIRECTORY_SEPARATOR . $file_name;
            }
        }
        return $path;
    }

    public function get_backup_folder()
    {
        $local_setting = get_option('wpvivid_local_setting', array());
        if(!empty($local_setting))
        {
            $path = WP_CONTENT_DIR . DIRECTORY_SEPARATOR . $local_setting['path'] . DIRECTORY_SEPARATOR;
        }
        else {
            $path = WP_CONTENT_DIR . DIRECTORY_SEPARATOR . 'wpvividbackups' . DIRECTORY_SEPARATOR;
        }
        return $path;
    }

    public function get_backup_url($backup_item, $file_name)
    {
        $path = $backup_item->get_local_path() . $file_name;

        if (file_exists($path)) {
            return $backup_item->get_local_url() . $file_name;
        }
        else{
            $local_setting = get_option('wpvivid_local_setting', array());
            if(!empty($local_setting))
            {
                $url = content_url().DIRECTORY_SEPARATOR.$local_setting['path'].DIRECTORY_SEPARATOR.$file_name;
            }
            else {
                $url = content_url().DIRECTORY_SEPARATOR.'wpvividbackups'.DIRECTORY_SEPARATOR.$file_name;
            }
        }
        return $url;
    }

    public function wpvivid_check_add_litespeed_server($task_id)
    {
        $litespeed=false;
        if ( isset( $_SERVER['HTTP_X_LSCACHE'] ) && $_SERVER['HTTP_X_LSCACHE'] )
        {
            $litespeed=true;
        }
        elseif ( isset( $_SERVER['LSWS_EDITION'] ) && strpos( $_SERVER['LSWS_EDITION'], 'Openlitespeed' ) === 0 ) {
            $litespeed=true;
        }
        elseif ( isset( $_SERVER['SERVER_SOFTWARE'] ) && $_SERVER['SERVER_SOFTWARE'] == 'LiteSpeed' ) {
            $litespeed=true;
        }

        if($litespeed)
        {
            if($this->wpvivid_log->log_file_handle==false)
            {
                $this->wpvivid_log->OpenLogFile(WPvivid_taskmanager::get_task_options($task_id,'log_file_name'));
            }
            $this->wpvivid_log->WriteLog('LiteSpeed Server.','notice');

            if ( ! function_exists( 'got_mod_rewrite' ) )
            {
                require_once ABSPATH . 'wp-admin/includes/misc.php';
            }

            if(function_exists('insert_with_markers'))
            {
                if(!function_exists('get_home_path'))
                    require_once(ABSPATH . 'wp-admin/includes/file.php');
                $home_path     = get_home_path();
                $htaccess_file = $home_path . '.htaccess';

                if ( ( ! file_exists( $htaccess_file ) && is_writable( $home_path ) ) || is_writable( $htaccess_file ) )
                {
                    if ( got_mod_rewrite() )
                    {
                        $line[]='<IfModule Litespeed>';
                        $line[]='RewriteEngine On';
                        $line[]='RewriteRule .* - [E=noabort:1, E=noconntimeout:1]';
                        $line[]='</IfModule>';
                        insert_with_markers($htaccess_file,'WPvivid Rewrite Rule for LiteSpeed',$line);
                        $this->wpvivid_log->WriteLog('Add LiteSpeed Rule','notice');
                    }
                    else
                    {
                        $this->wpvivid_log->WriteLog('mod_rewrite not found.','notice');
                    }
                }
                else
                {
                    $this->wpvivid_log->WriteLog('.htaccess file not exists or not writable.','notice');
                }
            }
            else
            {
                $this->wpvivid_log->WriteLog('insert_with_markers function not exists.','notice');
            }
        }
    }

    public function wpvivid_check_clear_litespeed_rule($task_id)
    {
        $litespeed=false;
        if ( isset( $_SERVER['HTTP_X_LSCACHE'] ) && $_SERVER['HTTP_X_LSCACHE'] )
        {
            $litespeed=true;
        }
        elseif ( isset( $_SERVER['LSWS_EDITION'] ) && strpos( $_SERVER['LSWS_EDITION'], 'Openlitespeed' ) === 0 ) {
            $litespeed=true;
        }
        elseif ( isset( $_SERVER['SERVER_SOFTWARE'] ) && $_SERVER['SERVER_SOFTWARE'] == 'LiteSpeed' ) {
            $litespeed=true;
        }

        if($litespeed)
        {
            if($this->wpvivid_log->log_file_handle==false)
            {
                $this->wpvivid_log->OpenLogFile(WPvivid_taskmanager::get_task_options($task_id,'log_file_name'));
            }
            $this->wpvivid_log->WriteLog('LiteSpeed Server.','notice');

            if ( ! function_exists( 'got_mod_rewrite' ) )
            {
                require_once ABSPATH . 'wp-admin/includes/misc.php';
            }

            if(function_exists('insert_with_markers'))
            {
                if(!function_exists('get_home_path'))
                    require_once(ABSPATH . 'wp-admin/includes/file.php');
                $home_path     = get_home_path();
                $htaccess_file = $home_path . '.htaccess';

                if ( ( ! file_exists( $htaccess_file ) && is_writable( $home_path ) ) || is_writable( $htaccess_file ) )
                {
                    if ( got_mod_rewrite() )
                    {
                        insert_with_markers($htaccess_file,'WPvivid Rewrite Rule for LiteSpeed','');
                        $this->wpvivid_log->WriteLog('Clear LiteSpeed Rule','notice');
                    }
                    else
                    {
                        $this->wpvivid_log->WriteLog('mod_rewrite not found.','notice');
                    }
                }
                else
                {
                    $this->wpvivid_log->WriteLog('.htaccess file not exists or not writable.','notice');
                }
            }
            else
            {
                $this->wpvivid_log->WriteLog('insert_with_markers function not exists.','notice');
            }
        }
    }
}
includes/class-wpvivid-schedule.php000064400000032441151327705670013471 0ustar00<?php

if (!defined('WPVIVID_PLUGIN_DIR')){
    die;
}

class WPvivid_Schedule
{
    protected $schedule_type = array(
        'wpvivid_12hours'       =>  '12Hours',
        'twicedaily'             =>  '12Hours',
        'wpvivid_daily'         =>   'Daily',
        'daily'                  =>   'Daily',
        'onceday'                =>   'Daily',
        'wpvivid_weekly'        =>   'Weekly',
        'weekly'                 =>   'Weekly',
        'wpvivid_fortnightly'  =>   'Fortnightly',
        'fortnightly'           =>   'Fortnightly',
        'wpvivid_monthly'      =>   'Monthly',
        'monthly'               =>    'Monthly',
        'montly'                =>    'Monthly'
    );

    public function __construct()
    {
        add_action('wpvivid_reset_schedule', array($this, 'wpvivid_reset_schedule'), 10);
    }

    public function wpvivid_cron_schedules($schedules)
    {
        if(!isset($schedules["wpvivid_12hours"])){
            $schedules["wpvivid_12hours"] = array(
                'interval' => 3600*12,
                'display' => __('12 Hours', 'wpvivid-backuprestore'));
        }

        if(!isset($schedules["wpvivid_daily"])){
            $schedules["wpvivid_daily"] = array(
                'interval' => 86400 ,
                'display' => __('Daily', 'wpvivid-backuprestore'));
        }

        if(!isset($schedules["wpvivid_weekly"])){
            $schedules["wpvivid_weekly"] = array(
                'interval' => 604800 ,
                'display' => __('Weekly', 'wpvivid-backuprestore'));
        }

        if(!isset($schedules["wpvivid_fortnightly"])){
            $schedules["wpvivid_fortnightly"] = array(
                'interval' => 604800*2 ,
                'display' => __('Fortnightly', 'wpvivid-backuprestore'));
        }

        if(!isset($schedules["wpvivid_monthly"])){
            $schedules["wpvivid_monthly"] = array(
                'interval' => 2592000 ,
                'display' => __('Monthly', 'wpvivid-backuprestore'));
        }

        return $schedules;
    }

    public function check_schedule_type($display){
        $schedules = wp_get_schedules();
        $check_res = false;
        $ret = array();
        foreach ($this->schedule_type as $key => $value){
            if($value == $display){
                if(isset($schedules[$key])){
                    $check_res = true;
                    $ret['type']=$key;
                    break;
                }
            }
        }
        $ret['result']=$check_res;
        return $ret;
    }

    public function output($html)
    {
        $html='';

        $display_array = array("12Hours", "Daily", "Weekly", "Fortnightly", "Monthly");
        foreach($display_array as $display){
            $schedule_check = $this->check_schedule_type($display);
            if($schedule_check['result']){
                $html.=' <label><input type="radio" option="schedule" name="recurrence" value="'.$schedule_check['type'].'" />';
                if($display === '12Hours'){
                    $html.='<span>'.__('12Hours', 'wpvivid-backuprestore').'</span></label><br>';
                }
                if($display === 'Daily'){
                    $html.='<span>'.__('Daily', 'wpvivid-backuprestore').'</span></label><br>';
                }
                if($display === 'Weekly'){
                    $html.='<span>'.__('Weekly', 'wpvivid-backuprestore').'</span></label><br>';
                }
                if($display === 'Fortnightly'){
                    $html.='<span>'.__('Fortnightly', 'wpvivid-backuprestore').'</span></label><br>';
                }
                if($display === 'Monthly'){
                    $html.='<span>'.__('Monthly', 'wpvivid-backuprestore').'</span></label><br>';
                }
            }
            else{
                $html.='<p>Warning: Unable to set '.$display.' backup schedule</p>';
            }
        }
        $html.='<label>';
        $html.='<div style="float: left;">';
        $html.='<input type="radio" disabled />';
        $html.='<span class="wpvivid-element-space-right" style="color: #ddd;">'.__('Custom', 'wpvivid-backuprestore').'</span>';
        $html.='</div>';
        $html.='<div style="float: left; height: 32px; line-height: 32px;">';
        $html.='<span class="wpvivid-feature-pro">';
        $html.='<a href="https://docs.wpvivid.com/wpvivid-backup-pro-customize-start-time.html" style="text-decoration: none; margin-top: 10px;">'.__('Pro feature: learn more', 'wpvivid-backuprestore').'</a>';
        $html.='</span>';
        $html.='</div>';
        $html.='</label><br>';
        return $html;
    }

    public static function get_start_time($time)
    {
        if(!is_array( $time ) )
        {
            return false;
        }

        if(!isset($time['type']))
        {
            return false;
        }

        $week=$time['start_time']['week'];
        $day=$time['start_time']['day'];
        $current_day=$time['start_time']['current_day'];

        if(strtotime('now')>strtotime($current_day)){
            $daily_start_time = $current_day.' +1 day';
        }
        else{
            $daily_start_time = $current_day;
        }

        $weekly_tmp = $week.' '.$current_day;
        if(strtotime('now')>strtotime($weekly_tmp)) {
            $weekly_start_time = $week.' '.$weekly_tmp.' next week';
        }
        else{
            $weekly_start_time = $weekly_tmp;
        }

        $date_now = gmdate("Y-m-",time());
        $monthly_tmp = $date_now.$day.' '.$current_day;
        if(strtotime('now')>strtotime($monthly_tmp)){
            $date_now = gmdate("Y-m-",strtotime('+1 month'));
            $monthly_start_time = $date_now.$day.' '.$current_day;
        }
        else{
            $monthly_start_time = $monthly_tmp;
        }

        $schedule_type_ex = array(
            'wpvivid_12hours'       =>  '12Hours',
            'twicedaily'             =>  '12Hours',
            'wpvivid_daily'         =>   'Daily',
            'daily'                  =>   'Daily',
            'onceday'                =>   'Daily',
            'wpvivid_weekly'        =>   'Weekly',
            'weekly'                 =>   'Weekly',
            'wpvivid_fortnightly'  =>   'Fortnightly',
            'fortnightly'           =>   'Fortnightly',
            'wpvivid_monthly'      =>   'Monthly',
            'monthly'               =>    'Monthly',
            'montly'                =>    'Monthly'
        );

        $display_array = array(
            "12Hours"       =>  $daily_start_time,
            "Daily"         =>  $daily_start_time,
            "Weekly"        =>  $weekly_start_time,
            "Fortnightly"   =>  $weekly_start_time,
            "Monthly"       =>  $monthly_start_time
        );
        foreach ($schedule_type_ex as $key => $value){
            if($key == $time['type']){
                foreach ($display_array as $display_key => $display_value){
                    if($value == $display_key){
                        return strtotime($display_value);
                    }
                }
            }
        }
        return false;
    }

    public static function get_schedule($schedule_id = '')
    {
        add_filter('wpvivid_get_schedule', array('WPvivid_Schedule', 'get_schedule_ex'),10,2);
        $schedule=array();
        $schedule=apply_filters('wpvivid_get_schedule',$schedule,$schedule_id);
        return $schedule;
    }

    public static function get_schedule_ex($schedule,$schedule_id)
    {
        $schedule=WPvivid_Setting::get_option('wpvivid_schedule_setting');

        if(empty($schedule['backup']))
        {
            $schedule['backup']['backup_files']='files+db';
            $schedule['backup']['local']=1;
            $schedule['backup']['remote']=0;
            $schedule['backup']['ismerge']=1;
            $schedule['backup']['lock']=0;
        }

        $recurrence = wp_get_schedule(WPVIVID_MAIN_SCHEDULE_EVENT);

        if(!wp_get_schedule(WPVIVID_MAIN_SCHEDULE_EVENT))
        {
            $schedule['enable']=false;
            return $schedule;
        }

        $schedule['enable']=true;
        $schedule['recurrence']=$recurrence;
        $timestamp=wp_next_scheduled(WPVIVID_MAIN_SCHEDULE_EVENT);

        $schedule['next_start']=$timestamp;
        return $schedule;
    }

    public static function set_schedule($schedule_data,$schedule)
    {
        if($schedule['enable']==1)
        {
            $schedule_data['enable']=$schedule['enable'];

            $schedule_data['type']=$schedule['recurrence'];
            $schedule_data['event']=WPVIVID_MAIN_SCHEDULE_EVENT;
            $time['type']=$schedule['recurrence'];
            $time['start_time']['week']='mon';
            $time['start_time']['day']='01';
            $time['start_time']['current_day']="00:00";
            $timestamp=WPvivid_Schedule::get_start_time($time);
            $schedule_data['start_time']=$timestamp;

            $schedule_data['backup']['backup_files']=$schedule['backup_type'];
            if($schedule['save_local_remote']=='remote')
            {
                $schedule_data['backup']['local']=0;
                $schedule_data['backup']['remote']=1;
            }
            else
            {
                $schedule_data['backup']['local']=1;
                $schedule_data['backup']['remote']=0;
            }
            $schedule_data['backup']['ismerge']=1;
            $schedule_data['backup']['lock']=$schedule['lock'];
        }
        else
        {
            $schedule_data['enable']=$schedule['enable'];
        }

        return $schedule_data;
    }

    public static function set_schedule_ex($schedule)
    {
        add_filter('wpvivid_set_schedule', array('WPvivid_Schedule', 'set_schedule'),10,2);
        $schedule_data=array();
        $schedule_data= apply_filters('wpvivid_set_schedule',$schedule_data, $schedule);
        WPvivid_Setting::update_option('wpvivid_schedule_setting',$schedule_data);
        if($schedule_data===false)
        {
            $ret['result']='failed';
            $ret['error']=__('Creating scheduled tasks failed. Please try again later.', 'wpvivid-backuprestore');
            return $ret;
        }

        if($schedule_data['enable']==1)
        {
            if(wp_get_schedule($schedule_data['event']))
            {
                $timestamp = wp_next_scheduled($schedule_data['event']);
                wp_unschedule_event($timestamp,$schedule_data['event']);
            }
            if(wp_schedule_event($schedule_data['start_time'], $schedule_data['type'], $schedule_data['event'])===false)
            {
                $ret['result']='failed';
                $ret['error']=__('Creating scheduled tasks failed. Please try again later.', 'wpvivid-backuprestore');
                $ret['data']=$schedule_data;
                return $ret;
            }
            else
            {
                $ret['result']='success';
                $ret['data']=$schedule_data;
                return $ret;
            }
        }
        else
        {
            if(wp_get_schedule(WPVIVID_MAIN_SCHEDULE_EVENT))
            {
                wp_clear_scheduled_hook(WPVIVID_MAIN_SCHEDULE_EVENT);
                $timestamp = wp_next_scheduled(WPVIVID_MAIN_SCHEDULE_EVENT);
                wp_unschedule_event($timestamp,WPVIVID_MAIN_SCHEDULE_EVENT);
            }
            $ret['result']='success';
            $ret['data']=$schedule_data;
            return $ret;
        }

    }

    public function wpvivid_reset_schedule()
    {
        self::reset_schedule();
        return true;
    }

    public static function reset_schedule()
    {
        $schedule=WPvivid_Setting::get_option('wpvivid_schedule_setting');
        if(!empty($schedule))
        {
            if($schedule['enable'])
            {
                self::set_schedule_ex($schedule);
            }
            else
            {
                self::disable_schedule();
            }
        }
        else
        {
            self::disable_schedule();
        }

        return true;
    }

    public static function disable_schedule()
    {
        $schedule=WPvivid_Setting::get_option('wpvivid_schedule_setting');
        $schedule['enable']=false;
        WPvivid_Setting::update_option('wpvivid_schedule_setting',$schedule);
        if(wp_get_schedule(WPVIVID_MAIN_SCHEDULE_EVENT))
        {
            wp_clear_scheduled_hook(WPVIVID_MAIN_SCHEDULE_EVENT);
            $timestamp = wp_next_scheduled(WPVIVID_MAIN_SCHEDULE_EVENT);
            wp_unschedule_event($timestamp,WPVIVID_MAIN_SCHEDULE_EVENT);
        }
    }

    public static function clear_monitor_schedule($id)
    {
        $timestamp =wp_next_scheduled(WPVIVID_TASK_MONITOR_EVENT,array($id));
        if($timestamp!==false)
        {
            wp_unschedule_event($timestamp,WPVIVID_TASK_MONITOR_EVENT,array($id));
        }
    }

    public static function get_next_resume_time($id)
    {
        $timestamp=wp_next_scheduled(WPVIVID_RESUME_SCHEDULE_EVENT,array($id));
        if($timestamp!==false)
        {
            return $timestamp-time();
        }
        else
        {
            return false;
        }
    }
}
includes/class-wpvivid-remote-collection.php000064400000012157151327705670015323 0ustar00<?php
/**
 * Created by PhpStorm.
 * User: alienware`x
 * Date: 2019/1/22
 * Time: 9:19
 */

require_once WPVIVID_PLUGIN_DIR .'/includes/customclass/class-wpvivid-remote-default.php';
require_once WPVIVID_PLUGIN_DIR .'/includes/customclass/class-wpvivid-ftpclass.php';
require_once WPVIVID_PLUGIN_DIR. '/includes/customclass/class-wpvivid-sftpclass.php';
require_once WPVIVID_PLUGIN_DIR . '/includes/customclass/class-wpvivid-amazons3-plus.php';
require_once WPVIVID_PLUGIN_DIR . '/includes/customclass/class-wpvivid-google-drive.php';
require_once WPVIVID_PLUGIN_DIR . '/includes/customclass/class-wpvivid-dropbox.php';
require_once WPVIVID_PLUGIN_DIR . '/includes/customclass/class-wpvivid-one-drive.php';
require_once WPVIVID_PLUGIN_DIR . '/includes/customclass/class-wpvivid-s3compat.php';


class WPvivid_Remote_collection
{
    private $remote_collection=array();

    public function __construct()
    {
        add_filter('wpvivid_remote_register', array($this, 'init_remotes'),10);
        $this->remote_collection=apply_filters('wpvivid_remote_register',$this->remote_collection);
        $this->load_hooks();
    }

    public function get_remote($remote)
    {
        if(is_array($remote)&&array_key_exists('type',$remote)&&array_key_exists($remote['type'],$this->remote_collection))
        {
            $class_name =$this->remote_collection[$remote['type']];

            if(class_exists($class_name))
            {
                $object = new $class_name($remote);
                return $object;
            }
        }
        $object = new $this ->remote_collection['default']();
        return  $object;
    }

    public function add_remote($remote_option)
    {
        $remote=$this->get_remote($remote_option);

        $ret=$remote->sanitize_options();

        if($ret['result']=='success')
        {
            $remote_option=$ret['options'];
            $ret=$remote->test_connect();
            if($ret['result']=='success')
            {
                $ret=array();
                $default=$remote_option['default'];
                $id=WPvivid_Setting::add_remote_options($remote_option);
                if($default==1)
                {
                    $remote_ids[]=$id;
                    $remote_ids=apply_filters('wpvivid_before_add_user_history',$remote_ids);
                    WPvivid_Setting::update_user_history('remote_selected',$remote_ids);
                    $schedule_data = WPvivid_Setting::get_option('wpvivid_schedule_setting');
                    if(!empty($schedule_data['enable'])) {
                        if ($schedule_data['enable'] == 1) {
                            $schedule_data['backup']['local'] = 0;
                            $schedule_data['backup']['remote'] = 1;
                        }
                        WPvivid_Setting::update_option('wpvivid_schedule_setting', $schedule_data);
                    }
                }
                $ret['result']=WPVIVID_SUCCESS;
            }
            else {
                $id = uniqid('wpvivid-');
                $log_file_name = $id . '_add_remote';
                $log = new WPvivid_Log();
                $log->CreateLogFile($log_file_name, 'no_folder', 'Add Remote Test Connection');
                $log->WriteLog('Remote Type: '.$remote_option['type'], 'notice');
                if(isset($ret['error'])) {
                    $log->WriteLog($ret['error'], 'notice');
                }
                $log->CloseFile();
                WPvivid_error_log::create_error_log($log->log_file);
            }
        }

        return $ret;
    }

    public function update_remote($id,$remote_option)
    {
        $remote=$this->get_remote($remote_option);

        $old_remote=WPvivid_Setting::get_remote_option($id);

        $ret=$remote->sanitize_options($old_remote['name']);
        if($ret['result']=='success')
        {
            $remote_option=$ret['options'];
            $ret=$remote->test_connect();
            if($ret['result']=='success')
            {
                $ret=array();
                WPvivid_Setting::update_remote_option($id,$remote_option);
                $ret['result']=WPVIVID_SUCCESS;
            }
        }

        return $ret;
    }

    public function init_remotes($remote_collection)
    {
        $remote_collection['default'] = 'WPvivid_Remote_Defult';
        $remote_collection['sftp']='WPvivid_SFTPClass';
        $remote_collection['ftp']='WPvivid_FTPClass';
        $remote_collection['amazons3']='WPvivid_AMAZONS3Class';
        $remote_collection[WPVIVID_REMOTE_GOOGLEDRIVE] = 'Wpvivid_Google_drive';
        $remote_collection['dropbox']='WPvivid_Dropbox';
        $remote_collection[WPVIVID_REMOTE_ONEDRIVE] = 'Wpvivid_one_drive';
        $remote_collection[WPVIVID_REMOTE_S3COMPAT] = 'Wpvivid_S3Compat';
        $remote_collection[WPVIVID_REMOTE_SEND_TO_SITE] = 'WPvivid_Send_to_site';
        return $remote_collection;
    }

    public function load_hooks()
    {
        foreach ($this->remote_collection as $class_name)
        {
            $object = new $class_name();
        }
    }
}includes/class-wpvivid-mysqldump-method.php000064400000311513151327705670015206 0ustar00<?php

use PDO as PDO;
use PDOException as PDOException;

/**
 * Enum with all available compression methods
 *
 */

abstract class WPvividCompressMethod
{
    public static $enums = array(
        "None",
        "Gzip",
        "Bzip2"
    );

    /**
     * @param string $c
     * @return boolean
     */
    public static function isValid($c)
    {
        return in_array($c, self::$enums);
    }
}

abstract class WPvividCompressManagerFactory
{
    /**
     * @param string $c
     * @return WPvividCompressBzip2|WPvividCompressGzip|WPvividCompressNone
     */
    public static function create($c)
    {
        $c = ucfirst(strtolower($c));
        if (! WPvividCompressMethod::isValid($c)) {
            throw new Exception("Compression method (".esc_html($c).") is not defined yet");
        }

        $method =  __NAMESPACE__ . "\\" . "WPvividCompress" . $c;

        return new $method;
    }
}

class WPvividCompressBzip2 extends WPvividCompressManagerFactory
{
    private $fileHandler = null;

    public function __construct()
    {
        if (! function_exists("bzopen")) {
            throw new Exception("Compression is enabled, but bzip2 lib is not installed or configured properly");
        }
    }

    /**
     * @param string $filename
     */
    public function open($filename)
    {
        $this->fileHandler = bzopen($filename, "w");
        if (false === $this->fileHandler) {
            throw new Exception("Output file is not writable");
        }

        return true;
    }

    public function write($str)
    {
        if (false === ($bytesWritten = bzwrite($this->fileHandler, $str))) {
            throw new Exception("Writting to file failed! Probably, there is no more free space left?");
        }
        return $bytesWritten;
    }

    public function close()
    {
        return bzclose($this->fileHandler);
    }
}

class WPvividCompressGzip extends WPvividCompressManagerFactory
{
    private $fileHandler = null;

    public function __construct()
    {
        if (! function_exists("gzopen")) {
            throw new Exception("Compression is enabled, but gzip lib is not installed or configured properly");
        }
    }

    /**
     * @param string $filename
     */
    public function open($filename)
    {
        $this->fileHandler = gzopen($filename, "wb");
        if (false === $this->fileHandler) {
            throw new Exception("Output file is not writable");
        }

        return true;
    }

    public function write($str)
    {
        if (false === ($bytesWritten = gzwrite($this->fileHandler, $str))) {
            throw new Exception("Writting to file failed! Probably, there is no more free space left?");
        }
        return $bytesWritten;
    }

    public function close()
    {
        return gzclose($this->fileHandler);
    }
}

class WPvividCompressNone extends WPvividCompressManagerFactory
{
    private $fileHandler = null;

    /**
     * @param string $filename
     */
    public function open($filename)
    {
        $this->fileHandler = fopen($filename, "wb");
        if (false === $this->fileHandler) {
            throw new Exception("Output file is not writable");
        }

        return true;
    }

    public function write($str)
    {
        if (false === ($bytesWritten = fwrite($this->fileHandler, $str))) {
            throw new Exception("Writting to file failed! Probably, there is no more free space left?");
        }
        return $bytesWritten;
    }

    public function close()
    {
        return fclose($this->fileHandler);
    }
}

/**
 * Enum with all available TypeAdapter implementations
 *
 */
abstract class WPvividTypeAdapter
{
    public static $enums = array(
        "Sqlite",
        "Mysql",
        "Wpdb"
    );

    /**
     * @param string $c
     * @return boolean
     */
    public static function isValid($c)
    {
        return in_array($c, self::$enums);
    }
}


if(!class_exists('TypeAdapterFactory'))
{
    abstract class TypeAdapterFactory
    {
        /**
         * @param string $c Type of database factory to create (Mysql, Sqlite,...)
         * @param PDO $dbHandler
         */
        public static function create($c, $dbHandler = null)
        {
            $c = ucfirst(strtolower($c));
            if (! WPvividTypeAdapter::isValid($c)) {
                if($c === 'Mysql'){
                    $c = 'PDO_MySQL';
                }
                throw new Exception("Database type support for (".esc_html($c).") not yet available");
            }
            $method =  __NAMESPACE__ . "\\" . "WPvividTypeAdapter" . $c;
            return new $method($dbHandler);
        }

        /**
         * function databases Add sql to create and use database
         * @todo make it do something with sqlite
         */
        public function databases()
        {
            return "";
        }

        public function show_create_table($tableName)
        {
            return "SELECT tbl_name as 'Table', sql as 'Create Table' " .
                "FROM sqlite_master " .
                "WHERE type='table' AND tbl_name='$tableName'";
        }

        /**
         * function create_table Get table creation code from database
         * @todo make it do something with sqlite
         */
        public function create_table($row, $dumpSettings)
        {
            return "";
        }

        public function show_create_view($viewName)
        {
            return "SELECT tbl_name as 'View', sql as 'Create View' " .
                "FROM sqlite_master " .
                "WHERE type='view' AND tbl_name='$viewName'";
        }

        /**
         * function create_view Get view creation code from database
         * @todo make it do something with sqlite
         */
        public function create_view($row)
        {
            return "";
        }

        /**
         * function show_create_trigger Get trigger creation code from database
         * @todo make it do something with sqlite
         */
        public function show_create_trigger($triggerName)
        {
            return "";
        }

        /**
         * function create_trigger Modify trigger code, add delimiters, etc
         * @todo make it do something with sqlite
         */
        public function create_trigger($triggerName)
        {
            return "";
        }

        /**
         * function create_procedure Modify procedure code, add delimiters, etc
         * @todo make it do something with sqlite
         */
        public function create_procedure($procedureName, $dumpSettings)
        {
            return "";
        }

        public function show_tables()
        {
            return "SELECT tbl_name FROM sqlite_master WHERE type='table'";
        }

        public function show_views()
        {
            return "SELECT tbl_name FROM sqlite_master WHERE type='view'";
        }

        public function show_triggers()
        {
            return "SELECT name FROM sqlite_master WHERE type='trigger'";
        }

        public function show_columns()
        {
            if (func_num_args() != 1) {
                return "";
            }

            $args = func_get_args();

            return "pragma table_info({$args[0]})";
        }

        public function show_procedures()
        {
            return "";
        }

        public function show_events()
        {
            return "";
        }

        public function setup_transaction()
        {
            return "";
        }

        public function start_transaction()
        {
            return "BEGIN EXCLUSIVE";
        }

        public function commit_transaction()
        {
            return "COMMIT";
        }

        public function lock_table()
        {
            return "";
        }

        public function unlock_table()
        {
            return "";
        }

        public function start_add_lock_table()
        {
            return PHP_EOL;
        }

        public function end_add_lock_table()
        {
            return PHP_EOL;
        }

        public function start_add_disable_keys()
        {
            return PHP_EOL;
        }

        public function end_add_disable_keys()
        {
            return PHP_EOL;
        }

        public function start_disable_foreign_keys_check()
        {
            return PHP_EOL;
        }

        public function end_disable_foreign_keys_check()
        {
            return PHP_EOL;
        }

        public function add_drop_database()
        {
            return PHP_EOL;
        }

        public function add_drop_trigger()
        {
            return PHP_EOL;
        }

        public function drop_table()
        {
            return PHP_EOL;
        }

        public function drop_view()
        {
            return PHP_EOL;
        }

        /**
         * Decode column metadata and fill info structure.
         * type, is_numeric and is_blob will always be available.
         *
         * @param array $colType Array returned from "SHOW COLUMNS FROM tableName"
         * @return array
         */
        public function parseColumnType($colType)
        {
            return array();
        }

        public function backup_parameters()
        {
            return PHP_EOL;
        }

        public function restore_parameters()
        {
            return PHP_EOL;
        }
    }

    class TypeAdapterPgsql extends TypeAdapterFactory
    {
    }

    class TypeAdapterDblib extends TypeAdapterFactory
    {
    }

    class TypeAdapterSqlite extends TypeAdapterFactory
    {
    }



    class TypeAdapterMysql extends TypeAdapterFactory
    {

        private $dbHandler = null;

        private $dsn;
        private $user;
        private $pass;
        private $pdoSettings;
        private $reconnect_count;

        // Numerical Mysql types
        public $mysqlTypes = array(
            'numerical' => array(
                'bit',
                'tinyint',
                'smallint',
                'mediumint',
                'int',
                'integer',
                'bigint',
                'real',
                'double',
                'float',
                'decimal',
                'numeric'
            ),
            'blob' => array(
                'tinyblob',
                'blob',
                'mediumblob',
                'longblob',
                'binary',
                'varbinary',
                'bit',
                'geometry', /* http://bugs.mysql.com/bug.php?id=43544 */
                'point',
                'linestring',
                'polygon',
                'multipoint',
                'multilinestring',
                'multipolygon',
                'geometrycollection',
            )
        );

        public function __construct ($dbHandler)
        {
            $pdoSettingsDefault = array(
                PDO::ATTR_PERSISTENT => true,
                PDO::ATTR_ERRMODE => PDO::ERRMODE_WARNING,
                PDO::MYSQL_ATTR_USE_BUFFERED_QUERY => false
            );

            $this->dbHandler = $dbHandler;
            $this->dsn='';
            $this->user='';
            $this->pass='';
            $this->pdoSettings=$pdoSettingsDefault;
            $this->reconnect_count=0;
        }

        public function set_connect_info($dsn,$user,$pass,$pdoSetting)
        {
            $this->dsn=$dsn;
            $this->user=$user;
            $this->pass=$pass;
            $this->pdoSettings=$pdoSetting;
        }

        public function connect($host,$dbname,$user,$pass,$init_commands=array())
        {
            if(empty($host))
            {
                $host=DB_HOST;
            }
            $res = explode(':',$host);
            $db_host = $res[0];
            $db_port = empty($res[1])?'':$res[1];
            if(!empty($db_port)) {
                $this->dsn='mysql:host=' . $db_host . ';port=' . $db_port . ';dbname=' . $dbname;
            }
            else{
                $this->dsn='mysql:host=' . $db_host . ';dbname=' . $dbname;
            }
            $this->user=$user;
            $this->pass=$pass;
            $this->dbHandler = @new PDO(
                $this->dsn,
                $this->user,
                $this->pass,
                $this->pdoSettings
            );
            // Execute init commands once connected
            foreach($init_commands as $stmt)
            {
                $this->dbHandler->exec($stmt);
            }
            $this->dbHandler->setAttribute(PDO::ATTR_ORACLE_NULLS, PDO::NULL_NATURAL);
        }

        public function errorInfo()
        {
            $error=$this->dbHandler->errorInfo();
            return $error;
        }

        public function quote($colValue)
        {
            return $this->dbHandler->quote($colValue);
        }


        public function databases()
        {
            $this->check_parameters(func_num_args(), $expected_num_args = 1, __METHOD__);
            $args = func_get_args();
            $databaseName = $args[0];

            $resultSet = $this->query("SHOW VARIABLES LIKE 'character_set_database';");
            $characterSet = $resultSet->fetchColumn(1);
            $resultSet->closeCursor();

            $resultSet = $this->query("SHOW VARIABLES LIKE 'collation_database';");
            $collationDb = $resultSet->fetchColumn(1);
            $resultSet->closeCursor();
            $ret = "";

            $ret .= "CREATE DATABASE /*!32312 IF NOT EXISTS*/ `{$databaseName}`".
                " /*!40100 DEFAULT CHARACTER SET {$characterSet} " .
                " COLLATE {$collationDb} */;" . PHP_EOL . PHP_EOL .
                "USE `{$databaseName}`;" . PHP_EOL . PHP_EOL;

            return $ret;
        }

        public function show_create_table($tableName)
        {
            return "SHOW CREATE TABLE `$tableName`";
        }

        public function show_create_view($viewName)
        {
            return "SHOW CREATE VIEW `$viewName`";
        }

        public function show_create_trigger($triggerName)
        {
            return "SHOW CREATE TRIGGER `$triggerName`";
        }

        public function show_create_procedure($procedureName)
        {
            return "SHOW CREATE PROCEDURE `$procedureName`";
        }

        public function show_create_event($eventName)
        {
            return "SHOW CREATE EVENT `$eventName`";
        }

        public function create_table( $row, $dumpSettings )
        {

            if ( !isset($row['Create Table']) ) {
                throw new Exception("Error getting table code, unknown output");
            }

            //$createTable = str_replace('\'0000-00-00 00:00:00\'','\'1999-01-01 00:00:00\'',$row['Create Table']);
            $createTable = $row['Create Table'];

            if ( $dumpSettings['reset-auto-increment'] ) {
                $match = "/AUTO_INCREMENT=[0-9]+/s";
                $replace = "";
                $createTable = preg_replace($match, $replace, $createTable);
            }

            $ret = "/*!40101 SET @saved_cs_client     = @@character_set_client */;" . PHP_EOL .
                "/*!40101 SET character_set_client = " . $dumpSettings['default-character-set'] . " */;" . PHP_EOL .
                $createTable . ";" . PHP_EOL .
                "/*!40101 SET character_set_client = @saved_cs_client */;" . PHP_EOL .
                PHP_EOL;
            return $ret;
        }

        public function create_view($row)
        {
            $ret = "";
            if (!isset($row['Create View'])) {
                throw new Exception("Error getting view structure, unknown output");
            }

            $triggerStmt = $row['Create View'];

            $triggerStmtReplaced1 = str_replace(
                "CREATE ALGORITHM",
                "/*!50001 CREATE ALGORITHM",
                $triggerStmt
            );
            $triggerStmtReplaced2 = str_replace(
                " DEFINER=",
                " */" . PHP_EOL . "/*!50013 DEFINER=",
                $triggerStmtReplaced1
            );
            $triggerStmtReplaced3 = str_replace(
                " VIEW ",
                " */" . PHP_EOL . "/*!50001 VIEW ",
                $triggerStmtReplaced2
            );
            if (false === $triggerStmtReplaced1 ||
                false === $triggerStmtReplaced2 ||
                false === $triggerStmtReplaced3) {
                $triggerStmtReplaced = $triggerStmt;
            } else {
                $triggerStmtReplaced = $triggerStmtReplaced3 . " */;";
            }

            $ret .= $triggerStmtReplaced . PHP_EOL . PHP_EOL;
            return $ret;
        }

        public function create_trigger($row)
        {
            $ret = "";
            if (!isset($row['SQL Original Statement'])) {
                throw new Exception("Error getting trigger code, unknown output");
            }

            $triggerStmt = $row['SQL Original Statement'];
            $triggerStmtReplaced = str_replace(
                "CREATE DEFINER",
                "/*!50003 CREATE*/ /*!50017 DEFINER",
                $triggerStmt
            );
            $triggerStmtReplaced = str_replace(
                " TRIGGER",
                "*/ /*!50003 TRIGGER",
                $triggerStmtReplaced
            );
            if ( false === $triggerStmtReplaced ) {
                $triggerStmtReplaced = $triggerStmt . " /* ";
            }

            $ret .= "DELIMITER ;;" . PHP_EOL .
                $triggerStmtReplaced . " */ ;;" . PHP_EOL .
                "DELIMITER ;" . PHP_EOL . PHP_EOL;
            return $ret;
        }

        public function create_procedure($row, $dumpSettings)
        {
            $ret = "";
            if (!isset($row['Create Procedure'])) {
                throw new Exception("Error getting procedure code, unknown output. " .
                    "Please check 'https://bugs.mysql.com/bug.php?id=14564'");
            }
            $procedureStmt = $row['Create Procedure'];

            $ret .= "/*!50003 DROP PROCEDURE IF EXISTS `" .
                $row['Procedure'] . "` */;" . PHP_EOL .
                "/*!40101 SET @saved_cs_client     = @@character_set_client */;" . PHP_EOL .
                "/*!40101 SET character_set_client = " . $dumpSettings['default-character-set'] . " */;" . PHP_EOL .
                "DELIMITER ;;" . PHP_EOL .
                $procedureStmt . " ;;" . PHP_EOL .
                "DELIMITER ;" . PHP_EOL .
                "/*!40101 SET character_set_client = @saved_cs_client */;" . PHP_EOL . PHP_EOL;

            return $ret;
        }

        public function create_event($row)
        {
            $ret = "";
            if ( !isset($row['Create Event']) ) {
                throw new Exception("Error getting event code, unknown output. " .
                    "Please check 'http://stackoverflow.com/questions/10853826/mysql-5-5-create-event-gives-syntax-error'");
            }
            $eventName = $row['Event'];
            $eventStmt = $row['Create Event'];
            $sqlMode = $row['sql_mode'];

            $eventStmtReplaced = str_replace(
                "CREATE DEFINER",
                "/*!50106 CREATE*/ /*!50117 DEFINER",
                $eventStmt
            );
            $eventStmtReplaced = str_replace(
                " EVENT ",
                "*/ /*!50106 EVENT ",
                $eventStmtReplaced
            );

            if ( false === $eventStmtReplaced ) {
                $eventStmtReplaced = $eventStmt . " /* ";
            }

            $ret .= "/*!50106 SET @save_time_zone= @@TIME_ZONE */ ;" . PHP_EOL .
                "/*!50106 DROP EVENT IF EXISTS `" . $eventName . "` */;" . PHP_EOL .
                "DELIMITER ;;" . PHP_EOL .
                "/*!50003 SET @saved_cs_client      = @@character_set_client */ ;;" . PHP_EOL .
                "/*!50003 SET @saved_cs_results     = @@character_set_results */ ;;" . PHP_EOL .
                "/*!50003 SET @saved_col_connection = @@collation_connection */ ;;" . PHP_EOL .
                "/*!50003 SET character_set_client  = utf8 */ ;;" . PHP_EOL .
                "/*!50003 SET character_set_results = utf8 */ ;;" . PHP_EOL .
                "/*!50003 SET collation_connection  = utf8_general_ci */ ;;" . PHP_EOL .
                "/*!50003 SET @saved_sql_mode       = @@sql_mode */ ;;" . PHP_EOL .
                "/*!50003 SET sql_mode              = '" . $sqlMode . "' */ ;;" . PHP_EOL .
                "/*!50003 SET @saved_time_zone      = @@time_zone */ ;;" . PHP_EOL .
                "/*!50003 SET time_zone             = 'SYSTEM' */ ;;" . PHP_EOL .
                $eventStmtReplaced . " */ ;;" . PHP_EOL .
                "/*!50003 SET time_zone             = @saved_time_zone */ ;;" . PHP_EOL .
                "/*!50003 SET sql_mode              = @saved_sql_mode */ ;;" . PHP_EOL .
                "/*!50003 SET character_set_client  = @saved_cs_client */ ;;" . PHP_EOL .
                "/*!50003 SET character_set_results = @saved_cs_results */ ;;" . PHP_EOL .
                "/*!50003 SET collation_connection  = @saved_col_connection */ ;;" . PHP_EOL .
                "DELIMITER ;" . PHP_EOL .
                "/*!50106 SET TIME_ZONE= @save_time_zone */ ;" . PHP_EOL . PHP_EOL;
            // Commented because we are doing this in restore_parameters()
            // "/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;" . PHP_EOL . PHP_EOL;

            return $ret;
        }

        public function show_tables()
        {
            $this->check_parameters(func_num_args(), $expected_num_args = 1, __METHOD__);
            $args = func_get_args();
            return "SELECT TABLE_NAME AS tbl_name " .
                "FROM INFORMATION_SCHEMA.TABLES " .
                "WHERE TABLE_TYPE='BASE TABLE' AND TABLE_SCHEMA='{$args[0]}'";
        }

        public function show_views()
        {
            $this->check_parameters(func_num_args(), $expected_num_args = 1, __METHOD__);
            $args = func_get_args();
            return "SELECT TABLE_NAME AS tbl_name " .
                "FROM INFORMATION_SCHEMA.TABLES " .
                "WHERE TABLE_TYPE='VIEW' AND TABLE_SCHEMA='{$args[0]}'";
        }

        public function show_triggers()
        {
            $this->check_parameters(func_num_args(), $expected_num_args = 1, __METHOD__);
            $args = func_get_args();
            return "SHOW TRIGGERS FROM `{$args[0]}`;";
        }

        public function show_columns()
        {
            $this->check_parameters(func_num_args(), $expected_num_args = 1, __METHOD__);
            $args = func_get_args();
            return "SHOW COLUMNS FROM `{$args[0]}`;";
        }

        public function show_procedures()
        {
            $this->check_parameters(func_num_args(), $expected_num_args = 1, __METHOD__);
            $args = func_get_args();
            return "SELECT SPECIFIC_NAME AS procedure_name " .
                "FROM INFORMATION_SCHEMA.ROUTINES " .
                "WHERE ROUTINE_TYPE='PROCEDURE' AND ROUTINE_SCHEMA='{$args[0]}'";
        }

        /**
         * Get query string to ask for names of events from current database.
         *
         * @param string Name of database
         * @return string
         */
        public function show_events()
        {
            $this->check_parameters(func_num_args(), $expected_num_args = 1, __METHOD__);
            $args = func_get_args();
            return "SELECT EVENT_NAME AS event_name " .
                "FROM INFORMATION_SCHEMA.EVENTS " .
                "WHERE EVENT_SCHEMA='{$args[0]}'";
        }

        public function setup_transaction()
        {
            return "SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ";
        }

        public function start_transaction()
        {
            return "START TRANSACTION";
        }

        public function commit_transaction()
        {
            return "COMMIT";
        }

        public function lock_table()
        {
            $this->check_parameters(func_num_args(), $expected_num_args = 1, __METHOD__);
            $args = func_get_args();
            return $this->dbHandler->exec("LOCK TABLES `{$args[0]}` READ LOCAL");

        }

        public function unlock_table()
        {
            return $this->dbHandler->exec("UNLOCK TABLES");
        }

        public function start_add_lock_table()
        {
            $this->check_parameters(func_num_args(), $expected_num_args = 1, __METHOD__);
            $args = func_get_args();
            return "LOCK TABLES `{$args[0]}` WRITE;" . PHP_EOL;
        }

        public function end_add_lock_table()
        {
            return "UNLOCK TABLES;" . PHP_EOL;
        }

        public function start_add_disable_keys()
        {
            $this->check_parameters(func_num_args(), $expected_num_args = 1, __METHOD__);
            $args = func_get_args();
            return "/*!40000 ALTER TABLE `{$args[0]}` DISABLE KEYS */;" .
                PHP_EOL;
        }

        public function end_add_disable_keys()
        {
            $this->check_parameters(func_num_args(), $expected_num_args = 1, __METHOD__);
            $args = func_get_args();
            return "/*!40000 ALTER TABLE `{$args[0]}` ENABLE KEYS */;" .
                PHP_EOL;
        }

        public function start_disable_autocommit()
        {
            return "SET autocommit=0;" . PHP_EOL;
        }

        public function end_disable_autocommit()
        {
            return "COMMIT;" . PHP_EOL;
        }

        public function add_drop_database()
        {
            $this->check_parameters(func_num_args(), $expected_num_args = 1, __METHOD__);
            $args = func_get_args();
            return "/*!40000 DROP DATABASE IF EXISTS `{$args[0]}`*/;" .
                PHP_EOL . PHP_EOL;
        }

        public function add_drop_trigger()
        {
            $this->check_parameters(func_num_args(), $expected_num_args = 1, __METHOD__);
            $args = func_get_args();
            return "DROP TRIGGER IF EXISTS `{$args[0]}`;" . PHP_EOL;
        }

        public function drop_table()
        {
            $this->check_parameters(func_num_args(), $expected_num_args = 1, __METHOD__);
            $args = func_get_args();
            return "DROP TABLE IF EXISTS `{$args[0]}`;" . PHP_EOL;
        }

        public function drop_view()
        {
            $this->check_parameters(func_num_args(), $expected_num_args = 1, __METHOD__);
            $args = func_get_args();
            return "DROP TABLE IF EXISTS `{$args[0]}`;" . PHP_EOL .
                "/*!50001 DROP VIEW IF EXISTS `{$args[0]}`*/;" . PHP_EOL;
        }

        public function getDatabaseHeader()
        {
            $this->check_parameters(func_num_args(), $expected_num_args = 1, __METHOD__);
            $args = func_get_args();
            return "--" . PHP_EOL .
                "-- Current Database: `{$args[0]}`" . PHP_EOL .
                "--" . PHP_EOL . PHP_EOL;
        }

        /**
         * Decode column metadata and fill info structure.
         * type, is_numeric and is_blob will always be available.
         *
         * @param array $colType Array returned from "SHOW COLUMNS FROM tableName"
         * @return array
         */
        public function parseColumnType($colType)
        {
            $colInfo = array();
            $colParts = explode(" ", $colType['Type']);

            if($fparen = strpos($colParts[0], "("))
            {
                $colInfo['type'] = substr($colParts[0], 0, $fparen);
                $colInfo['length']  = str_replace(")", "", substr($colParts[0], $fparen+1));
                $colInfo['attributes'] = isset($colParts[1]) ? $colParts[1] : NULL;
            }
            else
            {
                $colInfo['type'] = $colParts[0];
            }
            $colInfo['is_numeric'] = in_array($colInfo['type'], $this->mysqlTypes['numerical']);
            $colInfo['is_blob'] = in_array($colInfo['type'], $this->mysqlTypes['blob']);
            // for virtual 'Extra' -> "STORED GENERATED"
            $colInfo['is_virtual'] = strpos($colType['Extra'], "STORED GENERATED") === false ? false : true;

            return $colInfo;
        }

        public function get_connection_charset($wpdb = null) {
            if (null === $wpdb) {
                global $wpdb;
            }

            $charset = (defined('DB_CHARSET') && DB_CHARSET) ? DB_CHARSET : 'utf8mb4';

            if (method_exists($wpdb, 'determine_charset')) {
                $charset_collate = $wpdb->determine_charset($charset, '');
                if (!empty($charset_collate['charset'])) $charset = $charset_collate['charset'];
            }

            return $charset;
        }

        public function backup_parameters()
        {
            global $wpdb;
            $this->check_parameters(func_num_args(), $expected_num_args = 1, __METHOD__);
            $args = func_get_args();
            $dumpSettings = $args[0];
            $ret = "/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;" . PHP_EOL .
                "/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;" . PHP_EOL .
                "/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;" . PHP_EOL .
                "/*!40101 SET NAMES " . $this->get_connection_charset($wpdb) . " */;" . PHP_EOL;

            if (false === $dumpSettings['skip-tz-utc']) {
                $ret .= "/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;" . PHP_EOL .
                    "/*!40103 SET TIME_ZONE='+00:00' */;" . PHP_EOL;
            }

            $ret .= "/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;" . PHP_EOL .
                "/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;" . PHP_EOL .
                "/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;" . PHP_EOL .
                "/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;" . PHP_EOL .PHP_EOL;

            return $ret;
        }

        public function restore_parameters()
        {
            $this->check_parameters(func_num_args(), $expected_num_args = 1, __METHOD__);
            $args = func_get_args();
            $dumpSettings = $args[0];
            $ret = "";

            if (false === $dumpSettings['skip-tz-utc']) {
                $ret .= "/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;" . PHP_EOL;
            }

            $ret .= "/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;" . PHP_EOL .
                "/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;" . PHP_EOL .
                "/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;" . PHP_EOL .
                "/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;" . PHP_EOL .
                "/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;" . PHP_EOL .
                "/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;" . PHP_EOL .
                "/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;" . PHP_EOL . PHP_EOL;

            return $ret;
        }

        /**
         * Check number of parameters passed to function, useful when inheriting.
         * Raise exception if unexpected.
         *
         * @param integer $num_args
         * @param integer $expected_num_args
         * @param string $method_name
         */
        private function check_parameters($num_args, $expected_num_args, $method_name)
        {
            if ( $num_args != $expected_num_args ) {
                throw new Exception("Unexpected parameter passed to ".esc_html($method_name));
            }
            return;
        }

        public function query($string)
        {
            $ret= $this->dbHandler->query($string);

            if($ret===false)
            {
                $info=$this->dbHandler->errorInfo();
                if($info[1] == 2006)
                {
                    if($this->reconnect_count>3)
                    {
                        throw new Exception("MySQL server has gone away. Too many reconnect.");
                    }
                    $this->reconnect();
                    $this->reconnect_count++;
                    $ret= $this->dbHandler->query($string);
                    if($ret!==false)
                    {
                        $ret->setFetchMode(PDO::FETCH_ASSOC);
                    }
                }
            }
            else
            {
                $ret->setFetchMode(PDO::FETCH_ASSOC);
            }

            return $ret;
        }

        public function exec($string)
        {
            $ret=$this->dbHandler->exec($string);

            if($ret===false)
            {
                $info=$this->dbHandler->errorInfo();
                if($info[1] == 2006)
                {
                    if($this->reconnect_count>3)
                    {
                        throw new Exception("MySQL server has gone away. Too many reconnect.");
                    }
                    $this->reconnect();
                    $this->reconnect_count++;
                    $ret= $this->dbHandler->exec($string);
                }
            }
            return $ret;
        }

        public function reconnect()
        {
            $this->dbHandler = @new PDO(
                $this->dsn,
                $this->user,
                $this->pass,
                $this->pdoSettings
            );
        }

        public function closeCursor($resultSet)
        {
            $resultSet->closeCursor();
        }
    }

    class TypeAdapterWpdb extends TypeAdapterFactory
    {

        private $dbHandler = null;

        // Numerical Mysql types
        public $mysqlTypes = array(
            'numerical' => array(
                'bit',
                'tinyint',
                'smallint',
                'mediumint',
                'int',
                'integer',
                'bigint',
                'real',
                'double',
                'float',
                'decimal',
                'numeric'
            ),
            'blob' => array(
                'tinyblob',
                'blob',
                'mediumblob',
                'longblob',
                'binary',
                'varbinary',
                'bit',
                'geometry', /* http://bugs.mysql.com/bug.php?id=43544 */
                'point',
                'linestring',
                'polygon',
                'multipoint',
                'multilinestring',
                'multipolygon',
                'geometrycollection',
            )
        );

        public function __construct ($dbHandler)
        {
            $this->dbHandler = $dbHandler;
        }

        public function connect($host,$dbname,$user,$pass,$init_commands=array())
        {
            global $wpdb;

            $this->dbHandler=$wpdb;
        }

        public function databases()
        {
            $this->check_parameters(func_num_args(), $expected_num_args = 1, __METHOD__);
            $args = func_get_args();
            $databaseName = $args[0];

            $resultSet = $this->query("SHOW VARIABLES LIKE 'character_set_database';");
            $characterSet = $resultSet->fetchColumn(1);
            $resultSet->closeCursor();

            $resultSet = $this->query("SHOW VARIABLES LIKE 'collation_database';");
            $collationDb = $resultSet->fetchColumn(1);
            $resultSet->closeCursor();
            $ret = "";

            $ret .= "CREATE DATABASE /*!32312 IF NOT EXISTS*/ `{$databaseName}`".
                " /*!40100 DEFAULT CHARACTER SET {$characterSet} " .
                " COLLATE {$collationDb} */;" . PHP_EOL . PHP_EOL .
                "USE `{$databaseName}`;" . PHP_EOL . PHP_EOL;

            return $ret;
        }

        public function show_create_table($tableName)
        {
            return "SHOW CREATE TABLE `$tableName`";
        }

        public function show_create_view($viewName)
        {
            return "SHOW CREATE VIEW `$viewName`";
        }

        public function show_create_trigger($triggerName)
        {
            return "SHOW CREATE TRIGGER `$triggerName`";
        }

        public function show_create_procedure($procedureName)
        {
            return "SHOW CREATE PROCEDURE `$procedureName`";
        }

        public function show_create_event($eventName)
        {
            return "SHOW CREATE EVENT `$eventName`";
        }

        public function create_table( $row, $dumpSettings )
        {

            if ( !isset($row['Create Table']) ) {
                throw new Exception("Error getting table code, unknown output");
            }

            //$createTable = str_replace('\'0000-00-00 00:00:00\'','\'1999-01-01 00:00:00\'',$row['Create Table']);
            $createTable = $row['Create Table'];

            if ( $dumpSettings['reset-auto-increment'] ) {
                $match = "/AUTO_INCREMENT=[0-9]+/s";
                $replace = "";
                $createTable = preg_replace($match, $replace, $createTable);
            }

            $ret = "/*!40101 SET @saved_cs_client     = @@character_set_client */;" . PHP_EOL .
                "/*!40101 SET character_set_client = " . $dumpSettings['default-character-set'] . " */;" . PHP_EOL .
                $createTable . ";" . PHP_EOL .
                "/*!40101 SET character_set_client = @saved_cs_client */;" . PHP_EOL .
                PHP_EOL;
            return $ret;
        }

        public function create_view($row)
        {
            $ret = "";
            if (!isset($row['Create View'])) {
                throw new Exception("Error getting view structure, unknown output");
            }

            $triggerStmt = $row['Create View'];

            $triggerStmtReplaced1 = str_replace(
                "CREATE ALGORITHM",
                "/*!50001 CREATE ALGORITHM",
                $triggerStmt
            );
            $triggerStmtReplaced2 = str_replace(
                " DEFINER=",
                " */" . PHP_EOL . "/*!50013 DEFINER=",
                $triggerStmtReplaced1
            );
            $triggerStmtReplaced3 = str_replace(
                " VIEW ",
                " */" . PHP_EOL . "/*!50001 VIEW ",
                $triggerStmtReplaced2
            );
            if (false === $triggerStmtReplaced1 ||
                false === $triggerStmtReplaced2 ||
                false === $triggerStmtReplaced3) {
                $triggerStmtReplaced = $triggerStmt;
            } else {
                $triggerStmtReplaced = $triggerStmtReplaced3 . " */;";
            }

            $ret .= $triggerStmtReplaced . PHP_EOL . PHP_EOL;
            return $ret;
        }

        public function create_trigger($row)
        {
            $ret = "";
            if (!isset($row['SQL Original Statement'])) {
                throw new Exception("Error getting trigger code, unknown output");
            }

            $triggerStmt = $row['SQL Original Statement'];
            $triggerStmtReplaced = str_replace(
                "CREATE DEFINER",
                "/*!50003 CREATE*/ /*!50017 DEFINER",
                $triggerStmt
            );
            $triggerStmtReplaced = str_replace(
                " TRIGGER",
                "*/ /*!50003 TRIGGER",
                $triggerStmtReplaced
            );
            if ( false === $triggerStmtReplaced ) {
                $triggerStmtReplaced = $triggerStmt . " /* ";
            }

            $ret .= "DELIMITER ;;" . PHP_EOL .
                $triggerStmtReplaced . " */ ;;" . PHP_EOL .
                "DELIMITER ;" . PHP_EOL . PHP_EOL;
            return $ret;
        }

        public function create_procedure($row, $dumpSettings)
        {
            $ret = "";
            if (!isset($row['Create Procedure'])) {
                throw new Exception("Error getting procedure code, unknown output. " .
                    "Please check 'https://bugs.mysql.com/bug.php?id=14564'");
            }
            $procedureStmt = $row['Create Procedure'];

            $ret .= "/*!50003 DROP PROCEDURE IF EXISTS `" .
                $row['Procedure'] . "` */;" . PHP_EOL .
                "/*!40101 SET @saved_cs_client     = @@character_set_client */;" . PHP_EOL .
                "/*!40101 SET character_set_client = " . $dumpSettings['default-character-set'] . " */;" . PHP_EOL .
                "DELIMITER ;;" . PHP_EOL .
                $procedureStmt . " ;;" . PHP_EOL .
                "DELIMITER ;" . PHP_EOL .
                "/*!40101 SET character_set_client = @saved_cs_client */;" . PHP_EOL . PHP_EOL;

            return $ret;
        }

        public function create_event($row)
        {
            $ret = "";
            if ( !isset($row['Create Event']) ) {
                throw new Exception("Error getting event code, unknown output. " .
                    "Please check 'http://stackoverflow.com/questions/10853826/mysql-5-5-create-event-gives-syntax-error'");
            }
            $eventName = $row['Event'];
            $eventStmt = $row['Create Event'];
            $sqlMode = $row['sql_mode'];

            $eventStmtReplaced = str_replace(
                "CREATE DEFINER",
                "/*!50106 CREATE*/ /*!50117 DEFINER",
                $eventStmt
            );
            $eventStmtReplaced = str_replace(
                " EVENT ",
                "*/ /*!50106 EVENT ",
                $eventStmtReplaced
            );

            if ( false === $eventStmtReplaced ) {
                $eventStmtReplaced = $eventStmt . " /* ";
            }

            $ret .= "/*!50106 SET @save_time_zone= @@TIME_ZONE */ ;" . PHP_EOL .
                "/*!50106 DROP EVENT IF EXISTS `" . $eventName . "` */;" . PHP_EOL .
                "DELIMITER ;;" . PHP_EOL .
                "/*!50003 SET @saved_cs_client      = @@character_set_client */ ;;" . PHP_EOL .
                "/*!50003 SET @saved_cs_results     = @@character_set_results */ ;;" . PHP_EOL .
                "/*!50003 SET @saved_col_connection = @@collation_connection */ ;;" . PHP_EOL .
                "/*!50003 SET character_set_client  = utf8 */ ;;" . PHP_EOL .
                "/*!50003 SET character_set_results = utf8 */ ;;" . PHP_EOL .
                "/*!50003 SET collation_connection  = utf8_general_ci */ ;;" . PHP_EOL .
                "/*!50003 SET @saved_sql_mode       = @@sql_mode */ ;;" . PHP_EOL .
                "/*!50003 SET sql_mode              = '" . $sqlMode . "' */ ;;" . PHP_EOL .
                "/*!50003 SET @saved_time_zone      = @@time_zone */ ;;" . PHP_EOL .
                "/*!50003 SET time_zone             = 'SYSTEM' */ ;;" . PHP_EOL .
                $eventStmtReplaced . " */ ;;" . PHP_EOL .
                "/*!50003 SET time_zone             = @saved_time_zone */ ;;" . PHP_EOL .
                "/*!50003 SET sql_mode              = @saved_sql_mode */ ;;" . PHP_EOL .
                "/*!50003 SET character_set_client  = @saved_cs_client */ ;;" . PHP_EOL .
                "/*!50003 SET character_set_results = @saved_cs_results */ ;;" . PHP_EOL .
                "/*!50003 SET collation_connection  = @saved_col_connection */ ;;" . PHP_EOL .
                "DELIMITER ;" . PHP_EOL .
                "/*!50106 SET TIME_ZONE= @save_time_zone */ ;" . PHP_EOL . PHP_EOL;
            // Commented because we are doing this in restore_parameters()
            // "/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;" . PHP_EOL . PHP_EOL;

            return $ret;
        }

        public function show_tables()
        {
            $this->check_parameters(func_num_args(), $expected_num_args = 1, __METHOD__);
            $args = func_get_args();
            return "SELECT TABLE_NAME AS tbl_name " .
                "FROM INFORMATION_SCHEMA.TABLES " .
                "WHERE TABLE_TYPE='BASE TABLE' AND TABLE_SCHEMA='{$args[0]}'";
        }

        public function show_views()
        {
            $this->check_parameters(func_num_args(), $expected_num_args = 1, __METHOD__);
            $args = func_get_args();
            return "SELECT TABLE_NAME AS tbl_name " .
                "FROM INFORMATION_SCHEMA.TABLES " .
                "WHERE TABLE_TYPE='VIEW' AND TABLE_SCHEMA='{$args[0]}'";
        }

        public function show_triggers()
        {
            $this->check_parameters(func_num_args(), $expected_num_args = 1, __METHOD__);
            $args = func_get_args();
            return "SHOW TRIGGERS FROM `{$args[0]}`;";
        }

        public function show_columns()
        {
            $this->check_parameters(func_num_args(), $expected_num_args = 1, __METHOD__);
            $args = func_get_args();
            return "SHOW COLUMNS FROM `{$args[0]}`;";
        }

        public function show_procedures()
        {
            $this->check_parameters(func_num_args(), $expected_num_args = 1, __METHOD__);
            $args = func_get_args();
            return "SELECT SPECIFIC_NAME AS procedure_name " .
                "FROM INFORMATION_SCHEMA.ROUTINES " .
                "WHERE ROUTINE_TYPE='PROCEDURE' AND ROUTINE_SCHEMA='{$args[0]}'";
        }

        /**
         * Get query string to ask for names of events from current database.
         *
         * @param string Name of database
         * @return string
         */
        public function show_events()
        {
            $this->check_parameters(func_num_args(), $expected_num_args = 1, __METHOD__);
            $args = func_get_args();
            return "SELECT EVENT_NAME AS event_name " .
                "FROM INFORMATION_SCHEMA.EVENTS " .
                "WHERE EVENT_SCHEMA='{$args[0]}'";
        }

        public function setup_transaction()
        {
            return "SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ";
        }

        public function start_transaction()
        {
            return "START TRANSACTION";
        }

        public function commit_transaction()
        {
            return "COMMIT";
        }

        public function lock_table()
        {
            $this->check_parameters(func_num_args(), $expected_num_args = 1, __METHOD__);
            $args = func_get_args();
            return $this->dbHandler->get_results("LOCK TABLES `{$args[0]}` READ LOCAL",ARRAY_A);

        }

        public function unlock_table()
        {
            return $this->dbHandler->get_results("UNLOCK TABLES",ARRAY_A);
        }

        public function start_add_lock_table()
        {
            $this->check_parameters(func_num_args(), $expected_num_args = 1, __METHOD__);
            $args = func_get_args();
            return "LOCK TABLES `{$args[0]}` WRITE;" . PHP_EOL;
        }

        public function end_add_lock_table()
        {
            return "UNLOCK TABLES;" . PHP_EOL;
        }

        public function start_add_disable_keys()
        {
            $this->check_parameters(func_num_args(), $expected_num_args = 1, __METHOD__);
            $args = func_get_args();
            return "/*!40000 ALTER TABLE `{$args[0]}` DISABLE KEYS */;" .
                PHP_EOL;
        }

        public function end_add_disable_keys()
        {
            $this->check_parameters(func_num_args(), $expected_num_args = 1, __METHOD__);
            $args = func_get_args();
            return "/*!40000 ALTER TABLE `{$args[0]}` ENABLE KEYS */;" .
                PHP_EOL;
        }

        public function start_disable_autocommit()
        {
            return "SET autocommit=0;" . PHP_EOL;
        }

        public function end_disable_autocommit()
        {
            return "COMMIT;" . PHP_EOL;
        }

        public function add_drop_database()
        {
            $this->check_parameters(func_num_args(), $expected_num_args = 1, __METHOD__);
            $args = func_get_args();
            return "/*!40000 DROP DATABASE IF EXISTS `{$args[0]}`*/;" .
                PHP_EOL . PHP_EOL;
        }

        public function add_drop_trigger()
        {
            $this->check_parameters(func_num_args(), $expected_num_args = 1, __METHOD__);
            $args = func_get_args();
            return "DROP TRIGGER IF EXISTS `{$args[0]}`;" . PHP_EOL;
        }

        public function drop_table()
        {
            $this->check_parameters(func_num_args(), $expected_num_args = 1, __METHOD__);
            $args = func_get_args();
            return "DROP TABLE IF EXISTS `{$args[0]}`;" . PHP_EOL;
        }

        public function drop_view()
        {
            $this->check_parameters(func_num_args(), $expected_num_args = 1, __METHOD__);
            $args = func_get_args();
            return "DROP TABLE IF EXISTS `{$args[0]}`;" . PHP_EOL .
                "/*!50001 DROP VIEW IF EXISTS `{$args[0]}`*/;" . PHP_EOL;
        }

        public function getDatabaseHeader()
        {
            $this->check_parameters(func_num_args(), $expected_num_args = 1, __METHOD__);
            $args = func_get_args();
            return "--" . PHP_EOL .
                "-- Current Database: `{$args[0]}`" . PHP_EOL .
                "--" . PHP_EOL . PHP_EOL;
        }

        /**
         * Decode column metadata and fill info structure.
         * type, is_numeric and is_blob will always be available.
         *
         * @param array $colType Array returned from "SHOW COLUMNS FROM tableName"
         * @return array
         */
        public function parseColumnType($colType)
        {
            $colInfo = array();
            $colParts = explode(" ", $colType['Type']);

            if($fparen = strpos($colParts[0], "("))
            {
                $colInfo['type'] = substr($colParts[0], 0, $fparen);
                $colInfo['length']  = str_replace(")", "", substr($colParts[0], $fparen+1));
                $colInfo['attributes'] = isset($colParts[1]) ? $colParts[1] : NULL;
            }
            else
            {
                $colInfo['type'] = $colParts[0];
            }
            $colInfo['is_numeric'] = in_array($colInfo['type'], $this->mysqlTypes['numerical']);
            $colInfo['is_blob'] = in_array($colInfo['type'], $this->mysqlTypes['blob']);
            // for virtual 'Extra' -> "STORED GENERATED"
            $colInfo['is_virtual'] = strpos($colType['Extra'], "STORED GENERATED") === false ? false : true;

            return $colInfo;
        }

        public function get_connection_charset($wpdb = null) {
            if (null === $wpdb) {
                global $wpdb;
            }

            $charset = (defined('DB_CHARSET') && DB_CHARSET) ? DB_CHARSET : 'utf8mb4';

            if (method_exists($wpdb, 'determine_charset')) {
                $charset_collate = $wpdb->determine_charset($charset, '');
                if (!empty($charset_collate['charset'])) $charset = $charset_collate['charset'];
            }

            return $charset;
        }

        public function backup_parameters()
        {
            global $wpdb;
            $this->check_parameters(func_num_args(), $expected_num_args = 1, __METHOD__);
            $args = func_get_args();
            $dumpSettings = $args[0];
            $ret = "/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;" . PHP_EOL .
                "/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;" . PHP_EOL .
                "/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;" . PHP_EOL .
                "/*!40101 SET NAMES " . $this->get_connection_charset($wpdb) . " */;" . PHP_EOL;

            if (false === $dumpSettings['skip-tz-utc']) {
                $ret .= "/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;" . PHP_EOL .
                    "/*!40103 SET TIME_ZONE='+00:00' */;" . PHP_EOL;
            }

            $ret .= "/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;" . PHP_EOL .
                "/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;" . PHP_EOL .
                "/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;" . PHP_EOL .
                "/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;" . PHP_EOL .PHP_EOL;

            return $ret;
        }

        public function restore_parameters()
        {
            $this->check_parameters(func_num_args(), $expected_num_args = 1, __METHOD__);
            $args = func_get_args();
            $dumpSettings = $args[0];
            $ret = "";

            if (false === $dumpSettings['skip-tz-utc']) {
                $ret .= "/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;" . PHP_EOL;
            }

            $ret .= "/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;" . PHP_EOL .
                "/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;" . PHP_EOL .
                "/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;" . PHP_EOL .
                "/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;" . PHP_EOL .
                "/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;" . PHP_EOL .
                "/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;" . PHP_EOL .
                "/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;" . PHP_EOL . PHP_EOL;

            return $ret;
        }

        /**
         * Check number of parameters passed to function, useful when inheriting.
         * Raise exception if unexpected.
         *
         * @param integer $num_args
         * @param integer $expected_num_args
         * @param string $method_name
         */
        private function check_parameters($num_args, $expected_num_args, $method_name)
        {
            if ( $num_args != $expected_num_args ) {
                throw new Exception("Unexpected parameter passed to ".esc_html($method_name));
            }
            return;
        }

        public function query($string)
        {
            return $this->dbHandler->get_results($string, ARRAY_A);
        }

        public function exec($string)
        {
            return $this->dbHandler->get_results($string, ARRAY_A);
        }

        public function quote($value)
        {
            $search = array("\x00", "\x0a", "\x0d", "\x1a");
            $replace = array('\0', '\n', '\r', '\Z');
            $value=str_replace('\\', '\\\\', $value);
            $value=str_replace('\'', '\\\'', $value);
            $value= "'" . str_replace($search, $replace, $value) . "'";
            return $value;
        }

        public function closeCursor($resultSet)
        {
            $this->dbHandler->flush();
        }
    }
}

/**
 * TypeAdapter Factory
 *
 */
abstract class WPvividTypeAdapterFactory
{
    /**
     * @param string $c Type of database factory to create (Mysql, Sqlite,...)
     * @param PDO $dbHandler
     */
    public static function create($c, $dbHandler = null)
    {
        $c = ucfirst(strtolower($c));
        if (! WPvividTypeAdapter::isValid($c)) {
            if($c === 'Mysql'){
                $c = 'PDO_MySQL';
            }
            throw new Exception("Database type support for (".esc_html($c).") not yet available");
        }
        $method =  __NAMESPACE__ . "\\" . "WPvividTypeAdapter" . $c;
        return new $method($dbHandler);
    }

    /**
     * function databases Add sql to create and use database
     * @todo make it do something with sqlite
     */
    public function databases()
    {
        return "";
    }

    public function show_create_table($tableName)
    {
        return "SELECT tbl_name as 'Table', sql as 'Create Table' " .
            "FROM sqlite_master " .
            "WHERE type='table' AND tbl_name='$tableName'";
    }

    /**
     * function create_table Get table creation code from database
     * @todo make it do something with sqlite
     */
    public function create_table($row, $dumpSettings)
    {
        return "";
    }

    public function show_create_view($viewName)
    {
        return "SELECT tbl_name as 'View', sql as 'Create View' " .
            "FROM sqlite_master " .
            "WHERE type='view' AND tbl_name='$viewName'";
    }

    /**
     * function create_view Get view creation code from database
     * @todo make it do something with sqlite
     */
    public function create_view($row)
    {
        return "";
    }

    /**
     * function show_create_trigger Get trigger creation code from database
     * @todo make it do something with sqlite
     */
    public function show_create_trigger($triggerName)
    {
        return "";
    }

    /**
     * function create_trigger Modify trigger code, add delimiters, etc
     * @todo make it do something with sqlite
     */
    public function create_trigger($triggerName)
    {
        return "";
    }

    /**
     * function create_procedure Modify procedure code, add delimiters, etc
     * @todo make it do something with sqlite
     */
    public function create_procedure($procedureName, $dumpSettings)
    {
        return "";
    }

    public function show_tables()
    {
        return "SELECT tbl_name FROM sqlite_master WHERE type='table'";
    }

    public function show_views()
    {
        return "SELECT tbl_name FROM sqlite_master WHERE type='view'";
    }

    public function show_triggers()
    {
        return "SELECT name FROM sqlite_master WHERE type='trigger'";
    }

    public function show_columns()
    {
        if (func_num_args() != 1) {
            return "";
        }

        $args = func_get_args();

        return "pragma table_info({$args[0]})";
    }

    public function show_procedures()
    {
        return "";
    }

    public function show_events()
    {
        return "";
    }

    public function setup_transaction()
    {
        return "";
    }

    public function start_transaction()
    {
        return "BEGIN EXCLUSIVE";
    }

    public function commit_transaction()
    {
        return "COMMIT";
    }

    public function lock_table()
    {
        return "";
    }

    public function unlock_table()
    {
        return "";
    }

    public function start_add_lock_table()
    {
        return PHP_EOL;
    }

    public function end_add_lock_table()
    {
        return PHP_EOL;
    }

    public function start_add_disable_keys()
    {
        return PHP_EOL;
    }

    public function end_add_disable_keys()
    {
        return PHP_EOL;
    }

    public function start_disable_foreign_keys_check()
    {
        return PHP_EOL;
    }

    public function end_disable_foreign_keys_check()
    {
        return PHP_EOL;
    }

    public function add_drop_database()
    {
        return PHP_EOL;
    }

    public function add_drop_trigger()
    {
        return PHP_EOL;
    }

    public function drop_table()
    {
        return PHP_EOL;
    }

    public function drop_view()
    {
        return PHP_EOL;
    }

    /**
     * Decode column metadata and fill info structure.
     * type, is_numeric and is_blob will always be available.
     *
     * @param array $colType Array returned from "SHOW COLUMNS FROM tableName"
     * @return array
     */
    public function parseColumnType($colType)
    {
        return array();
    }

    public function backup_parameters()
    {
        return PHP_EOL;
    }

    public function restore_parameters()
    {
        return PHP_EOL;
    }
}

class WPvividTypeAdapterPgsql extends WPvividTypeAdapterFactory
{
}

class WPvividTypeAdapterDblib extends WPvividTypeAdapterFactory
{
}

class WPvividTypeAdapterSqlite extends WPvividTypeAdapterFactory
{
}



class WPvividTypeAdapterMysql extends WPvividTypeAdapterFactory
{

    private $dbHandler = null;

    private $dsn;
    private $user;
    private $pass;
    private $pdoSettings;
    private $reconnect_count;

    // Numerical Mysql types
    public $mysqlTypes = array(
        'numerical' => array(
            'bit',
            'tinyint',
            'smallint',
            'mediumint',
            'int',
            'integer',
            'bigint',
            'real',
            'double',
            'float',
            'decimal',
            'numeric'
        ),
        'blob' => array(
            'tinyblob',
            'blob',
            'mediumblob',
            'longblob',
            'binary',
            'varbinary',
            'bit',
            'geometry', /* http://bugs.mysql.com/bug.php?id=43544 */
            'point',
            'linestring',
            'polygon',
            'multipoint',
            'multilinestring',
            'multipolygon',
            'geometrycollection',
        )
    );

    public function __construct ($dbHandler)
    {
        $pdoSettingsDefault = array(
            PDO::ATTR_PERSISTENT => true,
            PDO::ATTR_ERRMODE => PDO::ERRMODE_WARNING,
            PDO::MYSQL_ATTR_USE_BUFFERED_QUERY => false
        );

        $this->dbHandler = $dbHandler;
        $this->dsn='';
        $this->user='';
        $this->pass='';
        $this->pdoSettings=$pdoSettingsDefault;
        $this->reconnect_count=0;
    }

    public function set_connect_info($dsn,$user,$pass,$pdoSetting)
    {
        $this->dsn=$dsn;
        $this->user=$user;
        $this->pass=$pass;
        $this->pdoSettings=$pdoSetting;
    }

    public function connect($host,$dbname,$user,$pass,$init_commands=array())
    {
        if(empty($host))
        {
            $host=DB_HOST;
        }
        $res = explode(':',$host);
        $db_host = $res[0];
        $db_port = empty($res[1])?'':$res[1];
        if(!empty($db_port)) {
            $this->dsn='mysql:host=' . $db_host . ';port=' . $db_port . ';dbname=' . $dbname;
        }
        else{
            $this->dsn='mysql:host=' . $db_host . ';dbname=' . $dbname;
        }
        $this->user=$user;
        $this->pass=$pass;
        $this->dbHandler = @new PDO(
            $this->dsn,
            $this->user,
            $this->pass,
            $this->pdoSettings
        );
        // Execute init commands once connected
        foreach($init_commands as $stmt)
        {
            $this->dbHandler->exec($stmt);
        }
        $this->dbHandler->setAttribute(PDO::ATTR_ORACLE_NULLS, PDO::NULL_NATURAL);
    }

    public function errorInfo()
    {
        $error=$this->dbHandler->errorInfo();
        return $error;
    }

    public function quote($colValue)
    {
        return $this->dbHandler->quote($colValue);
    }


    public function databases()
    {
        $this->check_parameters(func_num_args(), $expected_num_args = 1, __METHOD__);
        $args = func_get_args();
        $databaseName = $args[0];

        $resultSet = $this->query("SHOW VARIABLES LIKE 'character_set_database';");
        $characterSet = $resultSet->fetchColumn(1);
        $resultSet->closeCursor();

        $resultSet = $this->query("SHOW VARIABLES LIKE 'collation_database';");
        $collationDb = $resultSet->fetchColumn(1);
        $resultSet->closeCursor();
        $ret = "";

        $ret .= "CREATE DATABASE /*!32312 IF NOT EXISTS*/ `{$databaseName}`".
            " /*!40100 DEFAULT CHARACTER SET {$characterSet} " .
            " COLLATE {$collationDb} */;" . PHP_EOL . PHP_EOL .
            "USE `{$databaseName}`;" . PHP_EOL . PHP_EOL;

        return $ret;
    }

    public function show_create_table($tableName)
    {
        return "SHOW CREATE TABLE `$tableName`";
    }

    public function show_create_view($viewName)
    {
        return "SHOW CREATE VIEW `$viewName`";
    }

    public function show_create_trigger($triggerName)
    {
        return "SHOW CREATE TRIGGER `$triggerName`";
    }

    public function show_create_procedure($procedureName)
    {
        return "SHOW CREATE PROCEDURE `$procedureName`";
    }

    public function show_create_event($eventName)
    {
        return "SHOW CREATE EVENT `$eventName`";
    }

    public function create_table( $row, $dumpSettings )
    {

        if ( !isset($row['Create Table']) ) {
            throw new Exception("Error getting table code, unknown output");
        }

        //$createTable = str_replace('\'0000-00-00 00:00:00\'','\'1999-01-01 00:00:00\'',$row['Create Table']);
        $createTable = $row['Create Table'];

        if ( $dumpSettings['reset-auto-increment'] ) {
            $match = "/AUTO_INCREMENT=[0-9]+/s";
            $replace = "";
            $createTable = preg_replace($match, $replace, $createTable);
        }

        $ret = "/*!40101 SET @saved_cs_client     = @@character_set_client */;" . PHP_EOL .
            "/*!40101 SET character_set_client = " . $dumpSettings['default-character-set'] . " */;" . PHP_EOL .
            $createTable . ";" . PHP_EOL .
            "/*!40101 SET character_set_client = @saved_cs_client */;" . PHP_EOL .
            PHP_EOL;
        return $ret;
    }

    public function create_view($row)
    {
        $ret = "";
        if (!isset($row['Create View'])) {
            throw new Exception("Error getting view structure, unknown output");
        }

        $triggerStmt = $row['Create View'];

        $triggerStmtReplaced1 = str_replace(
            "CREATE ALGORITHM",
            "/*!50001 CREATE ALGORITHM",
            $triggerStmt
        );
        $triggerStmtReplaced2 = str_replace(
            " DEFINER=",
            " */" . PHP_EOL . "/*!50013 DEFINER=",
            $triggerStmtReplaced1
        );
        $triggerStmtReplaced3 = str_replace(
            " VIEW ",
            " */" . PHP_EOL . "/*!50001 VIEW ",
            $triggerStmtReplaced2
        );
        if (false === $triggerStmtReplaced1 ||
            false === $triggerStmtReplaced2 ||
            false === $triggerStmtReplaced3) {
            $triggerStmtReplaced = $triggerStmt;
        } else {
            $triggerStmtReplaced = $triggerStmtReplaced3 . " */;";
        }

        $ret .= $triggerStmtReplaced . PHP_EOL . PHP_EOL;
        return $ret;
    }

    public function create_trigger($row)
    {
        $ret = "";
        if (!isset($row['SQL Original Statement'])) {
            throw new Exception("Error getting trigger code, unknown output");
        }

        $triggerStmt = $row['SQL Original Statement'];
        $triggerStmtReplaced = str_replace(
            "CREATE DEFINER",
            "/*!50003 CREATE*/ /*!50017 DEFINER",
            $triggerStmt
        );
        $triggerStmtReplaced = str_replace(
            " TRIGGER",
            "*/ /*!50003 TRIGGER",
            $triggerStmtReplaced
        );
        if ( false === $triggerStmtReplaced ) {
            $triggerStmtReplaced = $triggerStmt . " /* ";
        }

        $ret .= "DELIMITER ;;" . PHP_EOL .
            $triggerStmtReplaced . " */ ;;" . PHP_EOL .
            "DELIMITER ;" . PHP_EOL . PHP_EOL;
        return $ret;
    }

    public function create_procedure($row, $dumpSettings)
    {
        $ret = "";
        if (!isset($row['Create Procedure'])) {
            throw new Exception("Error getting procedure code, unknown output. " .
                "Please check 'https://bugs.mysql.com/bug.php?id=14564'");
        }
        $procedureStmt = $row['Create Procedure'];

        $ret .= "/*!50003 DROP PROCEDURE IF EXISTS `" .
            $row['Procedure'] . "` */;" . PHP_EOL .
            "/*!40101 SET @saved_cs_client     = @@character_set_client */;" . PHP_EOL .
            "/*!40101 SET character_set_client = " . $dumpSettings['default-character-set'] . " */;" . PHP_EOL .
            "DELIMITER ;;" . PHP_EOL .
            $procedureStmt . " ;;" . PHP_EOL .
            "DELIMITER ;" . PHP_EOL .
            "/*!40101 SET character_set_client = @saved_cs_client */;" . PHP_EOL . PHP_EOL;

        return $ret;
    }

    public function create_event($row)
    {
        $ret = "";
        if ( !isset($row['Create Event']) ) {
            throw new Exception("Error getting event code, unknown output. " .
                "Please check 'http://stackoverflow.com/questions/10853826/mysql-5-5-create-event-gives-syntax-error'");
        }
        $eventName = $row['Event'];
        $eventStmt = $row['Create Event'];
        $sqlMode = $row['sql_mode'];

        $eventStmtReplaced = str_replace(
            "CREATE DEFINER",
            "/*!50106 CREATE*/ /*!50117 DEFINER",
            $eventStmt
        );
        $eventStmtReplaced = str_replace(
            " EVENT ",
            "*/ /*!50106 EVENT ",
            $eventStmtReplaced
        );

        if ( false === $eventStmtReplaced ) {
            $eventStmtReplaced = $eventStmt . " /* ";
        }

        $ret .= "/*!50106 SET @save_time_zone= @@TIME_ZONE */ ;" . PHP_EOL .
            "/*!50106 DROP EVENT IF EXISTS `" . $eventName . "` */;" . PHP_EOL .
            "DELIMITER ;;" . PHP_EOL .
            "/*!50003 SET @saved_cs_client      = @@character_set_client */ ;;" . PHP_EOL .
            "/*!50003 SET @saved_cs_results     = @@character_set_results */ ;;" . PHP_EOL .
            "/*!50003 SET @saved_col_connection = @@collation_connection */ ;;" . PHP_EOL .
            "/*!50003 SET character_set_client  = utf8 */ ;;" . PHP_EOL .
            "/*!50003 SET character_set_results = utf8 */ ;;" . PHP_EOL .
            "/*!50003 SET collation_connection  = utf8_general_ci */ ;;" . PHP_EOL .
            "/*!50003 SET @saved_sql_mode       = @@sql_mode */ ;;" . PHP_EOL .
            "/*!50003 SET sql_mode              = '" . $sqlMode . "' */ ;;" . PHP_EOL .
            "/*!50003 SET @saved_time_zone      = @@time_zone */ ;;" . PHP_EOL .
            "/*!50003 SET time_zone             = 'SYSTEM' */ ;;" . PHP_EOL .
            $eventStmtReplaced . " */ ;;" . PHP_EOL .
            "/*!50003 SET time_zone             = @saved_time_zone */ ;;" . PHP_EOL .
            "/*!50003 SET sql_mode              = @saved_sql_mode */ ;;" . PHP_EOL .
            "/*!50003 SET character_set_client  = @saved_cs_client */ ;;" . PHP_EOL .
            "/*!50003 SET character_set_results = @saved_cs_results */ ;;" . PHP_EOL .
            "/*!50003 SET collation_connection  = @saved_col_connection */ ;;" . PHP_EOL .
            "DELIMITER ;" . PHP_EOL .
            "/*!50106 SET TIME_ZONE= @save_time_zone */ ;" . PHP_EOL . PHP_EOL;
        // Commented because we are doing this in restore_parameters()
        // "/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;" . PHP_EOL . PHP_EOL;

        return $ret;
    }

    public function show_tables()
    {
        $this->check_parameters(func_num_args(), $expected_num_args = 1, __METHOD__);
        $args = func_get_args();
        return "SELECT TABLE_NAME AS tbl_name " .
            "FROM INFORMATION_SCHEMA.TABLES " .
            "WHERE TABLE_TYPE='BASE TABLE' AND TABLE_SCHEMA='{$args[0]}'";
    }

    public function show_views()
    {
        $this->check_parameters(func_num_args(), $expected_num_args = 1, __METHOD__);
        $args = func_get_args();
        return "SELECT TABLE_NAME AS tbl_name " .
            "FROM INFORMATION_SCHEMA.TABLES " .
            "WHERE TABLE_TYPE='VIEW' AND TABLE_SCHEMA='{$args[0]}'";
    }

    public function show_triggers()
    {
        $this->check_parameters(func_num_args(), $expected_num_args = 1, __METHOD__);
        $args = func_get_args();
        return "SHOW TRIGGERS FROM `{$args[0]}`;";
    }

    public function show_columns()
    {
        $this->check_parameters(func_num_args(), $expected_num_args = 1, __METHOD__);
        $args = func_get_args();
        return "SHOW COLUMNS FROM `{$args[0]}`;";
    }

    public function show_procedures()
    {
        $this->check_parameters(func_num_args(), $expected_num_args = 1, __METHOD__);
        $args = func_get_args();
        return "SELECT SPECIFIC_NAME AS procedure_name " .
            "FROM INFORMATION_SCHEMA.ROUTINES " .
            "WHERE ROUTINE_TYPE='PROCEDURE' AND ROUTINE_SCHEMA='{$args[0]}'";
    }

    /**
     * Get query string to ask for names of events from current database.
     *
     * @param string Name of database
     * @return string
     */
    public function show_events()
    {
        $this->check_parameters(func_num_args(), $expected_num_args = 1, __METHOD__);
        $args = func_get_args();
        return "SELECT EVENT_NAME AS event_name " .
            "FROM INFORMATION_SCHEMA.EVENTS " .
            "WHERE EVENT_SCHEMA='{$args[0]}'";
    }

    public function setup_transaction()
    {
        return "SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ";
    }

    public function start_transaction()
    {
        return "START TRANSACTION";
    }

    public function commit_transaction()
    {
        return "COMMIT";
    }

    public function lock_table()
    {
        $this->check_parameters(func_num_args(), $expected_num_args = 1, __METHOD__);
        $args = func_get_args();
        return $this->dbHandler->exec("LOCK TABLES `{$args[0]}` READ LOCAL");

    }

    public function unlock_table()
    {
        return $this->dbHandler->exec("UNLOCK TABLES");
    }

    public function start_add_lock_table()
    {
        $this->check_parameters(func_num_args(), $expected_num_args = 1, __METHOD__);
        $args = func_get_args();
        return "LOCK TABLES `{$args[0]}` WRITE;" . PHP_EOL;
    }

    public function end_add_lock_table()
    {
        return "UNLOCK TABLES;" . PHP_EOL;
    }

    public function start_add_disable_keys()
    {
        $this->check_parameters(func_num_args(), $expected_num_args = 1, __METHOD__);
        $args = func_get_args();
        return "/*!40000 ALTER TABLE `{$args[0]}` DISABLE KEYS */;" .
            PHP_EOL;
    }

    public function end_add_disable_keys()
    {
        $this->check_parameters(func_num_args(), $expected_num_args = 1, __METHOD__);
        $args = func_get_args();
        return "/*!40000 ALTER TABLE `{$args[0]}` ENABLE KEYS */;" .
            PHP_EOL;
    }

    public function start_disable_autocommit()
    {
        return "SET autocommit=0;" . PHP_EOL;
    }

    public function end_disable_autocommit()
    {
        return "COMMIT;" . PHP_EOL;
    }

    public function add_drop_database()
    {
        $this->check_parameters(func_num_args(), $expected_num_args = 1, __METHOD__);
        $args = func_get_args();
        return "/*!40000 DROP DATABASE IF EXISTS `{$args[0]}`*/;" .
            PHP_EOL . PHP_EOL;
    }

    public function add_drop_trigger()
    {
        $this->check_parameters(func_num_args(), $expected_num_args = 1, __METHOD__);
        $args = func_get_args();
        return "DROP TRIGGER IF EXISTS `{$args[0]}`;" . PHP_EOL;
    }

    public function drop_table()
    {
        $this->check_parameters(func_num_args(), $expected_num_args = 1, __METHOD__);
        $args = func_get_args();
        return "DROP TABLE IF EXISTS `{$args[0]}`;" . PHP_EOL;
    }

    public function drop_view()
    {
        $this->check_parameters(func_num_args(), $expected_num_args = 1, __METHOD__);
        $args = func_get_args();
        return "DROP TABLE IF EXISTS `{$args[0]}`;" . PHP_EOL .
            "/*!50001 DROP VIEW IF EXISTS `{$args[0]}`*/;" . PHP_EOL;
    }

    public function getDatabaseHeader()
    {
        $this->check_parameters(func_num_args(), $expected_num_args = 1, __METHOD__);
        $args = func_get_args();
        return "--" . PHP_EOL .
            "-- Current Database: `{$args[0]}`" . PHP_EOL .
            "--" . PHP_EOL . PHP_EOL;
    }

    /**
     * Decode column metadata and fill info structure.
     * type, is_numeric and is_blob will always be available.
     *
     * @param array $colType Array returned from "SHOW COLUMNS FROM tableName"
     * @return array
     */
    public function parseColumnType($colType)
    {
        $colInfo = array();
        $colParts = explode(" ", $colType['Type']);

        if($fparen = strpos($colParts[0], "("))
        {
            $colInfo['type'] = substr($colParts[0], 0, $fparen);
            $colInfo['length']  = str_replace(")", "", substr($colParts[0], $fparen+1));
            $colInfo['attributes'] = isset($colParts[1]) ? $colParts[1] : NULL;
        }
        else
        {
            $colInfo['type'] = $colParts[0];
        }
        $colInfo['is_numeric'] = in_array($colInfo['type'], $this->mysqlTypes['numerical']);
        $colInfo['is_blob'] = in_array($colInfo['type'], $this->mysqlTypes['blob']);
        // for virtual 'Extra' -> "STORED GENERATED"
        $colInfo['is_virtual'] = strpos($colType['Extra'], "STORED GENERATED") === false ? false : true;

        return $colInfo;
    }

    public function get_connection_charset($wpdb = null) {
        if (null === $wpdb) {
            global $wpdb;
        }

        $charset = (defined('DB_CHARSET') && DB_CHARSET) ? DB_CHARSET : 'utf8mb4';

        if (method_exists($wpdb, 'determine_charset')) {
            $charset_collate = $wpdb->determine_charset($charset, '');
            if (!empty($charset_collate['charset'])) $charset = $charset_collate['charset'];
        }

        return $charset;
    }

    public function backup_parameters()
    {
        global $wpdb;
        $this->check_parameters(func_num_args(), $expected_num_args = 1, __METHOD__);
        $args = func_get_args();
        $dumpSettings = $args[0];
        $ret = "/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;" . PHP_EOL .
            "/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;" . PHP_EOL .
            "/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;" . PHP_EOL .
            "/*!40101 SET NAMES " . $this->get_connection_charset($wpdb) . " */;" . PHP_EOL;

        if (false === $dumpSettings['skip-tz-utc']) {
            $ret .= "/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;" . PHP_EOL .
                "/*!40103 SET TIME_ZONE='+00:00' */;" . PHP_EOL;
        }

        $ret .= "/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;" . PHP_EOL .
            "/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;" . PHP_EOL .
            "/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;" . PHP_EOL .
            "/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;" . PHP_EOL .PHP_EOL;

        return $ret;
    }

    public function restore_parameters()
    {
        $this->check_parameters(func_num_args(), $expected_num_args = 1, __METHOD__);
        $args = func_get_args();
        $dumpSettings = $args[0];
        $ret = "";

        if (false === $dumpSettings['skip-tz-utc']) {
            $ret .= "/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;" . PHP_EOL;
        }

        $ret .= "/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;" . PHP_EOL .
            "/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;" . PHP_EOL .
            "/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;" . PHP_EOL .
            "/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;" . PHP_EOL .
            "/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;" . PHP_EOL .
            "/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;" . PHP_EOL .
            "/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;" . PHP_EOL . PHP_EOL;

        return $ret;
    }

    /**
     * Check number of parameters passed to function, useful when inheriting.
     * Raise exception if unexpected.
     *
     * @param integer $num_args
     * @param integer $expected_num_args
     * @param string $method_name
     */
    private function check_parameters($num_args, $expected_num_args, $method_name)
    {
        if ( $num_args != $expected_num_args ) {
            throw new Exception("Unexpected parameter passed to ".esc_html($method_name));
        }
        return;
    }

    public function query($string)
    {
        $ret= $this->dbHandler->query($string);

        if($ret===false)
        {
            $info=$this->dbHandler->errorInfo();
            if($info[1] == 2006)
            {
                if($this->reconnect_count>3)
                {
                    throw new Exception("MySQL server has gone away. Too many reconnect.");
                }
                $this->reconnect();
                $this->reconnect_count++;
                $ret= $this->dbHandler->query($string);
                if($ret!==false)
                {
                    $ret->setFetchMode(PDO::FETCH_ASSOC);
                }
            }
        }
        else
        {
            $ret->setFetchMode(PDO::FETCH_ASSOC);
        }

        return $ret;
    }

    public function exec($string)
    {
        $ret=$this->dbHandler->exec($string);

        if($ret===false)
        {
            $info=$this->dbHandler->errorInfo();
            if($info[1] == 2006)
            {
                if($this->reconnect_count>3)
                {
                    throw new Exception("MySQL server has gone away. Too many reconnect.");
                }
                $this->reconnect();
                $this->reconnect_count++;
                $ret= $this->dbHandler->exec($string);
            }
        }
        return $ret;
    }

    public function reconnect()
    {
        $this->dbHandler = @new PDO(
            $this->dsn,
            $this->user,
            $this->pass,
            $this->pdoSettings
        );
    }

    public function closeCursor($resultSet)
    {
        $resultSet->closeCursor();
    }
}

class WPvividTypeAdapterWpdb extends WPvividTypeAdapterFactory
{

    private $dbHandler = null;

    // Numerical Mysql types
    public $mysqlTypes = array(
        'numerical' => array(
            'bit',
            'tinyint',
            'smallint',
            'mediumint',
            'int',
            'integer',
            'bigint',
            'real',
            'double',
            'float',
            'decimal',
            'numeric'
        ),
        'blob' => array(
            'tinyblob',
            'blob',
            'mediumblob',
            'longblob',
            'binary',
            'varbinary',
            'bit',
            'geometry', /* http://bugs.mysql.com/bug.php?id=43544 */
            'point',
            'linestring',
            'polygon',
            'multipoint',
            'multilinestring',
            'multipolygon',
            'geometrycollection',
        )
    );

    public function __construct ($dbHandler)
    {
        $this->dbHandler = $dbHandler;
    }

    public function connect($host,$dbname,$user,$pass,$init_commands=array())
    {
        global $wpdb;

        $this->dbHandler=$wpdb;
    }

    public function databases()
    {
        $this->check_parameters(func_num_args(), $expected_num_args = 1, __METHOD__);
        $args = func_get_args();
        $databaseName = $args[0];

        $resultSet = $this->query("SHOW VARIABLES LIKE 'character_set_database';");
        $characterSet = $resultSet->fetchColumn(1);
        $resultSet->closeCursor();

        $resultSet = $this->query("SHOW VARIABLES LIKE 'collation_database';");
        $collationDb = $resultSet->fetchColumn(1);
        $resultSet->closeCursor();
        $ret = "";

        $ret .= "CREATE DATABASE /*!32312 IF NOT EXISTS*/ `{$databaseName}`".
            " /*!40100 DEFAULT CHARACTER SET {$characterSet} " .
            " COLLATE {$collationDb} */;" . PHP_EOL . PHP_EOL .
            "USE `{$databaseName}`;" . PHP_EOL . PHP_EOL;

        return $ret;
    }

    public function show_create_table($tableName)
    {
        return "SHOW CREATE TABLE `$tableName`";
    }

    public function show_create_view($viewName)
    {
        return "SHOW CREATE VIEW `$viewName`";
    }

    public function show_create_trigger($triggerName)
    {
        return "SHOW CREATE TRIGGER `$triggerName`";
    }

    public function show_create_procedure($procedureName)
    {
        return "SHOW CREATE PROCEDURE `$procedureName`";
    }

    public function show_create_event($eventName)
    {
        return "SHOW CREATE EVENT `$eventName`";
    }

    public function create_table( $row, $dumpSettings )
    {

        if ( !isset($row['Create Table']) ) {
            throw new Exception("Error getting table code, unknown output");
        }

        //$createTable = str_replace('\'0000-00-00 00:00:00\'','\'1999-01-01 00:00:00\'',$row['Create Table']);
        $createTable = $row['Create Table'];

        if ( $dumpSettings['reset-auto-increment'] ) {
            $match = "/AUTO_INCREMENT=[0-9]+/s";
            $replace = "";
            $createTable = preg_replace($match, $replace, $createTable);
        }

        $ret = "/*!40101 SET @saved_cs_client     = @@character_set_client */;" . PHP_EOL .
            "/*!40101 SET character_set_client = " . $dumpSettings['default-character-set'] . " */;" . PHP_EOL .
            $createTable . ";" . PHP_EOL .
            "/*!40101 SET character_set_client = @saved_cs_client */;" . PHP_EOL .
            PHP_EOL;
        return $ret;
    }

    public function create_view($row)
    {
        $ret = "";
        if (!isset($row['Create View'])) {
            throw new Exception("Error getting view structure, unknown output");
        }

        $triggerStmt = $row['Create View'];

        $triggerStmtReplaced1 = str_replace(
            "CREATE ALGORITHM",
            "/*!50001 CREATE ALGORITHM",
            $triggerStmt
        );
        $triggerStmtReplaced2 = str_replace(
            " DEFINER=",
            " */" . PHP_EOL . "/*!50013 DEFINER=",
            $triggerStmtReplaced1
        );
        $triggerStmtReplaced3 = str_replace(
            " VIEW ",
            " */" . PHP_EOL . "/*!50001 VIEW ",
            $triggerStmtReplaced2
        );
        if (false === $triggerStmtReplaced1 ||
            false === $triggerStmtReplaced2 ||
            false === $triggerStmtReplaced3) {
            $triggerStmtReplaced = $triggerStmt;
        } else {
            $triggerStmtReplaced = $triggerStmtReplaced3 . " */;";
        }

        $ret .= $triggerStmtReplaced . PHP_EOL . PHP_EOL;
        return $ret;
    }

    public function create_trigger($row)
    {
        $ret = "";
        if (!isset($row['SQL Original Statement'])) {
            throw new Exception("Error getting trigger code, unknown output");
        }

        $triggerStmt = $row['SQL Original Statement'];
        $triggerStmtReplaced = str_replace(
            "CREATE DEFINER",
            "/*!50003 CREATE*/ /*!50017 DEFINER",
            $triggerStmt
        );
        $triggerStmtReplaced = str_replace(
            " TRIGGER",
            "*/ /*!50003 TRIGGER",
            $triggerStmtReplaced
        );
        if ( false === $triggerStmtReplaced ) {
            $triggerStmtReplaced = $triggerStmt . " /* ";
        }

        $ret .= "DELIMITER ;;" . PHP_EOL .
            $triggerStmtReplaced . " */ ;;" . PHP_EOL .
            "DELIMITER ;" . PHP_EOL . PHP_EOL;
        return $ret;
    }

    public function create_procedure($row, $dumpSettings)
    {
        $ret = "";
        if (!isset($row['Create Procedure'])) {
            throw new Exception("Error getting procedure code, unknown output. " .
                "Please check 'https://bugs.mysql.com/bug.php?id=14564'");
        }
        $procedureStmt = $row['Create Procedure'];

        $ret .= "/*!50003 DROP PROCEDURE IF EXISTS `" .
            $row['Procedure'] . "` */;" . PHP_EOL .
            "/*!40101 SET @saved_cs_client     = @@character_set_client */;" . PHP_EOL .
            "/*!40101 SET character_set_client = " . $dumpSettings['default-character-set'] . " */;" . PHP_EOL .
            "DELIMITER ;;" . PHP_EOL .
            $procedureStmt . " ;;" . PHP_EOL .
            "DELIMITER ;" . PHP_EOL .
            "/*!40101 SET character_set_client = @saved_cs_client */;" . PHP_EOL . PHP_EOL;

        return $ret;
    }

    public function create_event($row)
    {
        $ret = "";
        if ( !isset($row['Create Event']) ) {
            throw new Exception("Error getting event code, unknown output. " .
                "Please check 'http://stackoverflow.com/questions/10853826/mysql-5-5-create-event-gives-syntax-error'");
        }
        $eventName = $row['Event'];
        $eventStmt = $row['Create Event'];
        $sqlMode = $row['sql_mode'];

        $eventStmtReplaced = str_replace(
            "CREATE DEFINER",
            "/*!50106 CREATE*/ /*!50117 DEFINER",
            $eventStmt
        );
        $eventStmtReplaced = str_replace(
            " EVENT ",
            "*/ /*!50106 EVENT ",
            $eventStmtReplaced
        );

        if ( false === $eventStmtReplaced ) {
            $eventStmtReplaced = $eventStmt . " /* ";
        }

        $ret .= "/*!50106 SET @save_time_zone= @@TIME_ZONE */ ;" . PHP_EOL .
            "/*!50106 DROP EVENT IF EXISTS `" . $eventName . "` */;" . PHP_EOL .
            "DELIMITER ;;" . PHP_EOL .
            "/*!50003 SET @saved_cs_client      = @@character_set_client */ ;;" . PHP_EOL .
            "/*!50003 SET @saved_cs_results     = @@character_set_results */ ;;" . PHP_EOL .
            "/*!50003 SET @saved_col_connection = @@collation_connection */ ;;" . PHP_EOL .
            "/*!50003 SET character_set_client  = utf8 */ ;;" . PHP_EOL .
            "/*!50003 SET character_set_results = utf8 */ ;;" . PHP_EOL .
            "/*!50003 SET collation_connection  = utf8_general_ci */ ;;" . PHP_EOL .
            "/*!50003 SET @saved_sql_mode       = @@sql_mode */ ;;" . PHP_EOL .
            "/*!50003 SET sql_mode              = '" . $sqlMode . "' */ ;;" . PHP_EOL .
            "/*!50003 SET @saved_time_zone      = @@time_zone */ ;;" . PHP_EOL .
            "/*!50003 SET time_zone             = 'SYSTEM' */ ;;" . PHP_EOL .
            $eventStmtReplaced . " */ ;;" . PHP_EOL .
            "/*!50003 SET time_zone             = @saved_time_zone */ ;;" . PHP_EOL .
            "/*!50003 SET sql_mode              = @saved_sql_mode */ ;;" . PHP_EOL .
            "/*!50003 SET character_set_client  = @saved_cs_client */ ;;" . PHP_EOL .
            "/*!50003 SET character_set_results = @saved_cs_results */ ;;" . PHP_EOL .
            "/*!50003 SET collation_connection  = @saved_col_connection */ ;;" . PHP_EOL .
            "DELIMITER ;" . PHP_EOL .
            "/*!50106 SET TIME_ZONE= @save_time_zone */ ;" . PHP_EOL . PHP_EOL;
        // Commented because we are doing this in restore_parameters()
        // "/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;" . PHP_EOL . PHP_EOL;

        return $ret;
    }

    public function show_tables()
    {
        $this->check_parameters(func_num_args(), $expected_num_args = 1, __METHOD__);
        $args = func_get_args();
        return "SELECT TABLE_NAME AS tbl_name " .
            "FROM INFORMATION_SCHEMA.TABLES " .
            "WHERE TABLE_TYPE='BASE TABLE' AND TABLE_SCHEMA='{$args[0]}'";
    }

    public function show_views()
    {
        $this->check_parameters(func_num_args(), $expected_num_args = 1, __METHOD__);
        $args = func_get_args();
        return "SELECT TABLE_NAME AS tbl_name " .
            "FROM INFORMATION_SCHEMA.TABLES " .
            "WHERE TABLE_TYPE='VIEW' AND TABLE_SCHEMA='{$args[0]}'";
    }

    public function show_triggers()
    {
        $this->check_parameters(func_num_args(), $expected_num_args = 1, __METHOD__);
        $args = func_get_args();
        return "SHOW TRIGGERS FROM `{$args[0]}`;";
    }

    public function show_columns()
    {
        $this->check_parameters(func_num_args(), $expected_num_args = 1, __METHOD__);
        $args = func_get_args();
        return "SHOW COLUMNS FROM `{$args[0]}`;";
    }

    public function show_procedures()
    {
        $this->check_parameters(func_num_args(), $expected_num_args = 1, __METHOD__);
        $args = func_get_args();
        return "SELECT SPECIFIC_NAME AS procedure_name " .
            "FROM INFORMATION_SCHEMA.ROUTINES " .
            "WHERE ROUTINE_TYPE='PROCEDURE' AND ROUTINE_SCHEMA='{$args[0]}'";
    }

    /**
     * Get query string to ask for names of events from current database.
     *
     * @param string Name of database
     * @return string
     */
    public function show_events()
    {
        $this->check_parameters(func_num_args(), $expected_num_args = 1, __METHOD__);
        $args = func_get_args();
        return "SELECT EVENT_NAME AS event_name " .
            "FROM INFORMATION_SCHEMA.EVENTS " .
            "WHERE EVENT_SCHEMA='{$args[0]}'";
    }

    public function setup_transaction()
    {
        return "SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ";
    }

    public function start_transaction()
    {
        return "START TRANSACTION";
    }

    public function commit_transaction()
    {
        return "COMMIT";
    }

    public function lock_table()
    {
        $this->check_parameters(func_num_args(), $expected_num_args = 1, __METHOD__);
        $args = func_get_args();
        return $this->dbHandler->get_results("LOCK TABLES `{$args[0]}` READ LOCAL",ARRAY_A);

    }

    public function unlock_table()
    {
        return $this->dbHandler->get_results("UNLOCK TABLES",ARRAY_A);
    }

    public function start_add_lock_table()
    {
        $this->check_parameters(func_num_args(), $expected_num_args = 1, __METHOD__);
        $args = func_get_args();
        return "LOCK TABLES `{$args[0]}` WRITE;" . PHP_EOL;
    }

    public function end_add_lock_table()
    {
        return "UNLOCK TABLES;" . PHP_EOL;
    }

    public function start_add_disable_keys()
    {
        $this->check_parameters(func_num_args(), $expected_num_args = 1, __METHOD__);
        $args = func_get_args();
        return "/*!40000 ALTER TABLE `{$args[0]}` DISABLE KEYS */;" .
            PHP_EOL;
    }

    public function end_add_disable_keys()
    {
        $this->check_parameters(func_num_args(), $expected_num_args = 1, __METHOD__);
        $args = func_get_args();
        return "/*!40000 ALTER TABLE `{$args[0]}` ENABLE KEYS */;" .
            PHP_EOL;
    }

    public function start_disable_autocommit()
    {
        return "SET autocommit=0;" . PHP_EOL;
    }

    public function end_disable_autocommit()
    {
        return "COMMIT;" . PHP_EOL;
    }

    public function add_drop_database()
    {
        $this->check_parameters(func_num_args(), $expected_num_args = 1, __METHOD__);
        $args = func_get_args();
        return "/*!40000 DROP DATABASE IF EXISTS `{$args[0]}`*/;" .
            PHP_EOL . PHP_EOL;
    }

    public function add_drop_trigger()
    {
        $this->check_parameters(func_num_args(), $expected_num_args = 1, __METHOD__);
        $args = func_get_args();
        return "DROP TRIGGER IF EXISTS `{$args[0]}`;" . PHP_EOL;
    }

    public function drop_table()
    {
        $this->check_parameters(func_num_args(), $expected_num_args = 1, __METHOD__);
        $args = func_get_args();
        return "DROP TABLE IF EXISTS `{$args[0]}`;" . PHP_EOL;
    }

    public function drop_view()
    {
        $this->check_parameters(func_num_args(), $expected_num_args = 1, __METHOD__);
        $args = func_get_args();
        return "DROP TABLE IF EXISTS `{$args[0]}`;" . PHP_EOL .
            "/*!50001 DROP VIEW IF EXISTS `{$args[0]}`*/;" . PHP_EOL;
    }

    public function getDatabaseHeader()
    {
        $this->check_parameters(func_num_args(), $expected_num_args = 1, __METHOD__);
        $args = func_get_args();
        return "--" . PHP_EOL .
            "-- Current Database: `{$args[0]}`" . PHP_EOL .
            "--" . PHP_EOL . PHP_EOL;
    }

    /**
     * Decode column metadata and fill info structure.
     * type, is_numeric and is_blob will always be available.
     *
     * @param array $colType Array returned from "SHOW COLUMNS FROM tableName"
     * @return array
     */
    public function parseColumnType($colType)
    {
        $colInfo = array();
        $colParts = explode(" ", $colType['Type']);

        if($fparen = strpos($colParts[0], "("))
        {
            $colInfo['type'] = substr($colParts[0], 0, $fparen);
            $colInfo['length']  = str_replace(")", "", substr($colParts[0], $fparen+1));
            $colInfo['attributes'] = isset($colParts[1]) ? $colParts[1] : NULL;
        }
        else
        {
            $colInfo['type'] = $colParts[0];
        }
        $colInfo['is_numeric'] = in_array($colInfo['type'], $this->mysqlTypes['numerical']);
        $colInfo['is_blob'] = in_array($colInfo['type'], $this->mysqlTypes['blob']);
        // for virtual 'Extra' -> "STORED GENERATED"
        $colInfo['is_virtual'] = strpos($colType['Extra'], "STORED GENERATED") === false ? false : true;

        return $colInfo;
    }

    public function get_connection_charset($wpdb = null) {
        if (null === $wpdb) {
            global $wpdb;
        }

        $charset = (defined('DB_CHARSET') && DB_CHARSET) ? DB_CHARSET : 'utf8mb4';

        if (method_exists($wpdb, 'determine_charset')) {
            $charset_collate = $wpdb->determine_charset($charset, '');
            if (!empty($charset_collate['charset'])) $charset = $charset_collate['charset'];
        }

        return $charset;
    }

    public function backup_parameters()
    {
        global $wpdb;
        $this->check_parameters(func_num_args(), $expected_num_args = 1, __METHOD__);
        $args = func_get_args();
        $dumpSettings = $args[0];
        $ret = "/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;" . PHP_EOL .
            "/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;" . PHP_EOL .
            "/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;" . PHP_EOL .
            "/*!40101 SET NAMES " . $this->get_connection_charset($wpdb) . " */;" . PHP_EOL;

        if (false === $dumpSettings['skip-tz-utc']) {
            $ret .= "/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;" . PHP_EOL .
                "/*!40103 SET TIME_ZONE='+00:00' */;" . PHP_EOL;
        }

        $ret .= "/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;" . PHP_EOL .
            "/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;" . PHP_EOL .
            "/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;" . PHP_EOL .
            "/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;" . PHP_EOL .PHP_EOL;

        return $ret;
    }

    public function restore_parameters()
    {
        $this->check_parameters(func_num_args(), $expected_num_args = 1, __METHOD__);
        $args = func_get_args();
        $dumpSettings = $args[0];
        $ret = "";

        if (false === $dumpSettings['skip-tz-utc']) {
            $ret .= "/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;" . PHP_EOL;
        }

        $ret .= "/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;" . PHP_EOL .
            "/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;" . PHP_EOL .
            "/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;" . PHP_EOL .
            "/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;" . PHP_EOL .
            "/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;" . PHP_EOL .
            "/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;" . PHP_EOL .
            "/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;" . PHP_EOL . PHP_EOL;

        return $ret;
    }

    /**
     * Check number of parameters passed to function, useful when inheriting.
     * Raise exception if unexpected.
     *
     * @param integer $num_args
     * @param integer $expected_num_args
     * @param string $method_name
     */
    private function check_parameters($num_args, $expected_num_args, $method_name)
    {
        if ( $num_args != $expected_num_args ) {
            throw new Exception("Unexpected parameter passed to ".esc_html($method_name));
        }
        return;
    }

    public function query($string)
    {
        return $this->dbHandler->get_results($string, ARRAY_A);
    }

    public function exec($string)
    {
        return $this->dbHandler->get_results($string, ARRAY_A);
    }

    public function quote($value)
    {
        $search = array("\x00", "\x0a", "\x0d", "\x1a");
        $replace = array('\0', '\n', '\r', '\Z');
        $value=str_replace('\\', '\\\\', $value);
        $value=str_replace('\'', '\\\'', $value);
        $value= "'" . str_replace($search, $replace, $value) . "'";
        return $value;
    }

    public function closeCursor($resultSet)
    {
        $this->dbHandler->flush();
    }
}includes/class-wpvivid-backuplist.php000064400000032036151327705670014036 0ustar00<?php

if (!defined('WPVIVID_PLUGIN_DIR')){
    die;
}
class WPvivid_Backuplist
{
    public static function get_backup_by_id($id)
    {
        $lists[]='wpvivid_backup_list';
        $lists=apply_filters('wpvivid_get_backuplist_name',$lists);
        foreach ($lists as $list_name)
        {
            $list = WPvivid_Setting::get_option($list_name);
            if(!empty($list))
            {
                foreach ($list as $k=>$backup)
                {
                    if ($id == $k)
                    {
                        return $backup;
                    }
                }
            }
        }
        return false;
    }

    public static function update_backup_option($backup_id,$backup_new)
    {
        $lists[]='wpvivid_backup_list';
        $lists=apply_filters('wpvivid_get_backuplist_name',$lists);
        foreach ($lists as $list_name)
        {
            $list = WPvivid_Setting::get_option($list_name);
            if(!empty($list))
            {
                foreach ($list as $k=>$backup)
                {
                    if ($backup_id == $k)
                    {
                        $list[$backup_id]=$backup_new;
                        WPvivid_Setting::update_option($list_name,$list);
                        return ;
                    }
                }
            }
        }
    }

    public static function get_backuplist($list_name='')
    {
        $list=array();
        add_filter('wpvivid_get_backuplist',array('WPvivid_Backuplist','get_backup_list'),10,2);
        $list=apply_filters('wpvivid_get_backuplist',$list,$list_name);
        return $list;
    }

    public static function get_backup_list($list,$list_name)
    {
        $list =  WPvivid_Setting::get_option('wpvivid_backup_list');
        $list =self::sort_list($list);

        return $list;
    }

    public static function get_backuplist_by_id($id){
        $list = array();
        add_filter('wpvivid_get_backuplist_by_id',array('WPvivid_Backuplist','get_backup_list_by_id'), 10 , 2);
        $ret=apply_filters('wpvivid_get_backuplist_by_id',$list,$id);
        return $ret;
    }

    public static function get_backup_list_by_id($list, $id)
    {
        $list = WPvivid_Setting::get_option('wpvivid_backup_list');
        foreach ($list as $k=>$backup)
        {
            if ($id == $k)
            {
                $ret['list_name'] = 'wpvivid_backup_list';
                $ret['list_data'] = $list;
                return $ret;
            }
        }
        return false;
    }

    public static function get_backuplist_by_key($key)
    {
        add_filter('wpvivid_get_backuplist_item',array('WPvivid_Backuplist','get_backuplist_item'),10,2);
        $backup=false;
        $backup=apply_filters('wpvivid_get_backuplist_item',$backup,$key);
        return $backup;
    }

    public static function get_backuplist_item($backup,$key)
    {
        $list = WPvivid_Setting::get_option('wpvivid_backup_list');
        foreach ($list as $k=>$backup)
        {
            if ($key == $k)
            {
                return $backup;
            }
        }
        return false;
    }

    public static function update_backup($id,$key,$data)
    {
        add_action('wpvivid_update_backup',array('WPvivid_Backuplist', 'update_backup_item'),10,3);
        do_action('wpvivid_update_backup',$id,$key,$data);
    }

    public static function update_backup_item($id,$key,$data)
    {
        $list = WPvivid_Setting::get_option('wpvivid_backup_list');
        if(array_key_exists($id,$list))
        {
            $list[$id][$key]=$data;
            WPvivid_Setting::update_option('wpvivid_backup_list',$list);
        }
    }

    public static function add_new_upload_backup($task_id,$backup,$time,$log='')
    {
        $backup_data=array();
        $backup_data['type']='Upload';
        $backup_data['create_time']=$time;
        $backup_data['manual_delete']=0;
        $backup_data['local']['path']=WPvivid_Setting::get_backupdir();
        $backup_data['compress']['compress_type']='zip';
        $backup_data['save_local']=1;
        $backup_data['log']=$log;

        $backup_data['backup']=$backup;
        $backup_data['remote']=array();
        $backup_data['lock']=0;
        $backup_list='wpvivid_backup_list';

        $backup_list=apply_filters('get_wpvivid_backup_list_name',$backup_list,$task_id,$backup_data);

        $list = WPvivid_Setting::get_option($backup_list);
        $list[$task_id]=$backup_data;
        WPvivid_Setting::update_option($backup_list,$list);
    }

    public static function delete_backup($key)
    {
        $lists[]='wpvivid_backup_list';
        $lists=apply_filters('wpvivid_get_backuplist_name',$lists);
        foreach ($lists as $list_name)
        {
            $list = WPvivid_Setting::get_option($list_name);
            foreach ($list as $k=>$backup)
            {
                if ($key == $k)
                {
                    unset($list[$key]);
                    WPvivid_Setting::update_option($list_name, $list);
                    return;
                }
            }
        }
    }

    public static function sort_list($list)
    {
        if($list !== false)
        {
            uasort ($list,function($a, $b)
            {
                if($a['create_time']>$b['create_time'])
                {
                    return -1;
                }
                else if($a['create_time']===$b['create_time'])
                {
                    return 0;
                }
                else
                {
                    return 1;
                }
            });
        }
        return $list;
    }

    public static function get_oldest_backup_id($list)
    {
        $oldest_id='';
        $oldest=0;
        foreach ($list as $k=>$backup)
        {
            if(!array_key_exists('lock',$backup) || (isset($backup['lock']) && $backup['lock'] == '0'))
            {
                if ($oldest == 0)
                {
                    $oldest = $backup['create_time'];
                    $oldest_id = $k;
                } else {
                    if ($oldest > $backup['create_time'])
                    {
                        $oldest_id = $k;
                    }
                }
            }
        }
        return $oldest_id;
    }

    public static function check_backuplist_limit($max_count)
    {
        $list = WPvivid_Setting::get_option('wpvivid_backup_list');
        foreach ($list as $key=>$value)
        {
            if(isset($value['lock']) && $value['lock'] == 1)
            {
                unset($list[$key]);
            }
        }
        if($list !== false)
        {
            $size=sizeof($list);
        }
        else
        {
            $size = 0;
        }
        if($size>=$max_count)
        {
            $oldest_id=self::get_oldest_backup_id($list);
            if(empty($oldest_id))
            {
                return false;
            }
            else
            {
                return $oldest_id;
            }
        }
        else
        {
           return false;
        }
    }

    public static function get_out_of_date_backuplist($max_count)
    {
        $list = WPvivid_Setting::get_option('wpvivid_backup_list');
        foreach ($list as $key=>$value)
        {
            if(isset($value['lock']) && $value['lock'] == 1)
            {
                unset($list[$key]);
            }
        }
        if($list !== false)
        {
            $size=sizeof($list);
        }
        else
        {
            $size = 0;
        }
        $out_of_date_list=array();

        if($max_count==0)
            return $out_of_date_list;

        while($size>$max_count)
        {
            $oldest_id=self::get_oldest_backup_id($list);

            if(!empty($oldest_id))
            {
                $out_of_date_list[]=$oldest_id;
                unset($list[$oldest_id]);
            }
            $new_size=sizeof($list);
            if($new_size==$size)
            {
                break;
            }
            else
            {
                $size=$new_size;
            }
        }

        return $out_of_date_list;
    }

    public static function get_out_of_date_backuplist_info($max_count)
    {
        $list = WPvivid_Setting::get_option('wpvivid_backup_list');
        if($list !== false)
        {
            $size=sizeof($list);
        }
        else
        {
            $size = 0;
        }
        $out_of_date_list['size']=0;
        $out_of_date_list['count']=0;

        if($max_count==0)
            return $out_of_date_list;

        while($size>$max_count)
        {
            $oldest_id=self::get_oldest_backup_id($list);

            if(!empty($oldest_id))
            {
                $out_of_date_list['size']+=self::get_size($oldest_id);
                $out_of_date_list['count']++;
                unset($list[$oldest_id]);
            }
            $new_size=sizeof($list);
            if($new_size==$size)
            {
                break;
            }
            else
            {
                $size=$new_size;
            }
        }

        return $out_of_date_list;
    }

    public static function get_size($backup_id)
    {
        $size=0;
        $list = WPvivid_Setting::get_option('wpvivid_backup_list');
        $backup=$list[$backup_id];
        if(isset($backup['backup']['files'])){
            foreach ($backup['backup']['files'] as $file) {
                $size+=$file['size'];
            }
        }
        else{
            if(isset($backup['backup']['data']['type'])){
                foreach ($backup['backup']['data']['type'] as $type) {
                    foreach ($type['files'] as $file) {
                        $size+=$file['size'];
                    }
                }
            }
        }

        return $size;
    }
    public static function set_security_lock($backup_id,$lock)
    {
        //$list = WPvivid_Setting::get_option('wpvivid_backup_list');
        $ret = self::get_backuplist_by_id($backup_id);
        if($ret !== false) {
            $list = $ret['list_data'];
            if (array_key_exists($backup_id, $list)) {
                if ($lock == 1) {
                    $list[$backup_id]['lock'] = 1;
                }
                else {
                    if (array_key_exists('lock', $list[$backup_id])) {
                        unset($list[$backup_id]['lock']);
                    }
                }
            }
            WPvivid_Setting::update_option($ret['list_name'], $list);
        }

        $ret['result'] = 'success';
        $list = WPvivid_Setting::get_option($ret['list_name']);
        if (array_key_exists($backup_id, $list)) {
            if (isset($list[$backup_id]['lock'])) {
                if ($list[$backup_id]['lock'] == 1) {
                    $backup_lock = '/admin/partials/images/locked.png';
                    $lock_status = 'lock';
                    $ret['html'] = '<img src="' . esc_url(WPVIVID_PLUGIN_URL . $backup_lock) . '" name="' . esc_attr($lock_status, 'wpvivid-backuprestore') . '" onclick="wpvivid_set_backup_lock(\''.$backup_id.'\', \''.$lock_status.'\');" style="vertical-align:middle; cursor:pointer;"/>';
                } else {
                    $backup_lock = '/admin/partials/images/unlocked.png';
                    $lock_status = 'unlock';
                    $ret['html'] = '<img src="' . esc_url(WPVIVID_PLUGIN_URL . $backup_lock) . '" name="' . esc_attr($lock_status, 'wpvivid-backuprestore') . '" onclick="wpvivid_set_backup_lock(\''.$backup_id.'\', \''.$lock_status.'\');" style="vertical-align:middle; cursor:pointer;"/>';
                }
            } else {
                $backup_lock = '/admin/partials/images/unlocked.png';
                $lock_status = 'unlock';
                $ret['html'] = '<img src="' . esc_url(WPVIVID_PLUGIN_URL . $backup_lock) . '" name="' . esc_attr($lock_status, 'wpvivid-backuprestore') . '" onclick="wpvivid_set_backup_lock(\''.$backup_id.'\', \''.$lock_status.'\');" style="vertical-align:middle; cursor:pointer;"/>';
            }
        } else {
            $backup_lock = '/admin/partials/images/unlocked.png';
            $lock_status = 'unlock';
            $ret['html'] = '<img src="' . esc_url(WPVIVID_PLUGIN_URL . $backup_lock) . '" name="' . esc_attr($lock_status, 'wpvivid-backuprestore') . '" onclick="wpvivid_set_backup_lock(\''.$backup_id.'\', \''.$lock_status.'\');" style="vertical-align:middle; cursor:pointer;"/>';
        }
        return $ret;
    }

    public static function get_has_remote_backuplist()
    {
        $backup_id_list=array();
        $list = WPvivid_Setting::get_option('wpvivid_backup_list');
        foreach ($list as $k=>$backup)
        {
            if(!empty($backup['remote']))
            {
                $backup_id_list[]=$k;
            }
        }
        return $backup_id_list;
    }
}includes/lib2/google-api-php-client/src/WPvivid/Google/Exception.php000064400000001217151327705670021346 0ustar00<?php
/*
 * Copyright 2013 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

class WPvivid_Google_Exception extends Exception
{
}
includes/lib2/google-api-php-client/src/WPvivid/Google/Service/Resource.php000064400000024014151327705670022577 0ustar00<?php
/**
 * Copyright 2010 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

use WPvividGuzzleHttp\Psr7\Request;

/**
 * Implements the actual methods/resources of the discovered Google API using magic function
 * calling overloading (__call()), which on call will see if the method name (plus.activities.list)
 * is available in this service, and if so construct an apiHttpRequest representing it.
 *
 */
class WPvivid_Google_Service_Resource
{
  // Valid query parameters that work, but don't appear in discovery.
  private $stackParameters = array(
      'alt' => array('type' => 'string', 'location' => 'query'),
      'fields' => array('type' => 'string', 'location' => 'query'),
      'trace' => array('type' => 'string', 'location' => 'query'),
      'userIp' => array('type' => 'string', 'location' => 'query'),
      'quotaUser' => array('type' => 'string', 'location' => 'query'),
      'data' => array('type' => 'string', 'location' => 'body'),
      'mimeType' => array('type' => 'string', 'location' => 'header'),
      'uploadType' => array('type' => 'string', 'location' => 'query'),
      'mediaUpload' => array('type' => 'complex', 'location' => 'query'),
      'prettyPrint' => array('type' => 'string', 'location' => 'query'),
  );

  /** @var string $rootUrl */
  private $rootUrl;

  /** @var Google_Client $client */
  private $client;

  /** @var string $serviceName */
  private $serviceName;

  /** @var string $servicePath */
  private $servicePath;

  /** @var string $resourceName */
  private $resourceName;

  /** @var array $methods */
  private $methods;

  public function __construct($service, $serviceName, $resourceName, $resource)
  {
    $this->rootUrl = $service->rootUrl;
    $this->client = $service->getClient();
    $this->servicePath = $service->servicePath;
    $this->serviceName = $serviceName;
    $this->resourceName = $resourceName;
    $this->methods = is_array($resource) && isset($resource['methods']) ?
        $resource['methods'] :
        array($resourceName => $resource);
  }

  /**
   * TODO: This function needs simplifying.
   * @param $name
   * @param $arguments
   * @param $expectedClass - optional, the expected class name
   * @return WPvivid_Google_Http_Request|expectedClass
   * @throws WPvivid_Google_Exception
   */
  public function call($name, $arguments, $expectedClass = null)
  {
    if (! isset($this->methods[$name])) {
      $this->client->getLogger()->error(
          'Service method unknown',
          array(
              'service' => $this->serviceName,
              'resource' => $this->resourceName,
              'method' => $name
          )
      );

      throw new WPvivid_Google_Exception(
          "Unknown function: " .
          esc_html("{$this->serviceName}->{$this->resourceName}->{$name}()")
      );
    }
    $method = $this->methods[$name];
    $parameters = $arguments[0];

    // postBody is a special case since it's not defined in the discovery
    // document as parameter, but we abuse the param entry for storing it.
    $postBody = null;
    if (isset($parameters['postBody'])) {
      if ($parameters['postBody'] instanceof WPvivid_Google_Model) {
        // In the cases the post body is an existing object, we want
        // to use the smart method to create a simple object for
        // for JSONification.
        $parameters['postBody'] = $parameters['postBody']->toSimpleObject();
      } else if (is_object($parameters['postBody'])) {
        // If the post body is another kind of object, we will try and
        // wrangle it into a sensible format.
        $parameters['postBody'] =
            $this->convertToArrayAndStripNulls($parameters['postBody']);
      }
      $postBody = (array) $parameters['postBody'];
      unset($parameters['postBody']);
    }

    // TODO: optParams here probably should have been
    // handled already - this may well be redundant code.
    if (isset($parameters['optParams'])) {
      $optParams = $parameters['optParams'];
      unset($parameters['optParams']);
      $parameters = array_merge($parameters, $optParams);
    }

    if (!isset($method['parameters'])) {
      $method['parameters'] = array();
    }

    $method['parameters'] = array_merge(
        $this->stackParameters,
        $method['parameters']
    );

    foreach ($parameters as $key => $val) {
      if ($key != 'postBody' && ! isset($method['parameters'][$key])) {
        $this->client->getLogger()->error(
            'Service parameter unknown',
            array(
                'service' => $this->serviceName,
                'resource' => $this->resourceName,
                'method' => $name,
                'parameter' => $key
            )
        );
        throw new WPvivid_Google_Exception(esc_html("($name) unknown parameter: '$key'"));
      }
    }

    foreach ($method['parameters'] as $paramName => $paramSpec) {
      if (isset($paramSpec['required']) &&
          $paramSpec['required'] &&
          ! isset($parameters[$paramName])
      ) {
        $this->client->getLogger()->error(
            'Service parameter missing',
            array(
                'service' => $this->serviceName,
                'resource' => $this->resourceName,
                'method' => $name,
                'parameter' => $paramName
            )
        );
        throw new WPvivid_Google_Exception(esc_html("($name) missing required param: '$paramName'"));
      }
      if (isset($parameters[$paramName])) {
        $value = $parameters[$paramName];
        $parameters[$paramName] = $paramSpec;
        $parameters[$paramName]['value'] = $value;
        unset($parameters[$paramName]['required']);
      } else {
        // Ensure we don't pass nulls.
        unset($parameters[$paramName]);
      }
    }

    $this->client->getLogger()->info(
        'Service Call',
        array(
            'service' => $this->serviceName,
            'resource' => $this->resourceName,
            'method' => $name,
            'arguments' => $parameters,
        )
    );

    // build the service uri
    $url = $this->createRequestUri(
        $method['path'],
        $parameters
    );

    // NOTE: because we're creating the request by hand,
    // and because the service has a rootUrl property
    // the "base_uri" of the Http Client is not accounted for
    $request = new Request(
        $method['httpMethod'],
        $url,
        ['content-type' => 'application/json'],
        $postBody ? json_encode($postBody) : ''
    );

    // support uploads
    if (isset($parameters['data'])) {
      $mimeType = isset($parameters['mimeType'])
        ? $parameters['mimeType']['value']
        : 'application/octet-stream';
      $data = $parameters['data']['value'];
      $upload = new WPvivid_Google_Http_MediaFileUpload($this->client, $request, $mimeType, $data);

      // pull down the modified request
      $request = $upload->getRequest();
    }

    // if this is a media type, we will return the raw response
    // rather than using an expected class
    if (isset($parameters['alt']) && $parameters['alt']['value'] == 'media') {
      $expectedClass = null;
    }

    // if the client is marked for deferring, rather than
    // execute the request, return the response
    if ($this->client->shouldDefer()) {
      // @TODO find a better way to do this
      $request = $request
        ->withHeader('X-Php-Expected-Class', $expectedClass);

      return $request;
    }

    return $this->client->execute($request, $expectedClass);
  }

  protected function convertToArrayAndStripNulls($o)
  {
    $o = (array) $o;
    foreach ($o as $k => $v) {
      if ($v === null) {
        unset($o[$k]);
      } elseif (is_object($v) || is_array($v)) {
        $o[$k] = $this->convertToArrayAndStripNulls($o[$k]);
      }
    }
    return $o;
  }

  /**
   * Parse/expand request parameters and create a fully qualified
   * request uri.
   * @static
   * @param string $restPath
   * @param array $params
   * @return string $requestUrl
   */
  public function createRequestUri($restPath, $params)
  {
    // Override the default servicePath address if the $restPath use a /
    if ('/' == substr($restPath, 0, 1)) {
      $requestUrl = substr($restPath, 1);
    } else {
      $requestUrl = $this->servicePath . $restPath;
    }

    // code for leading slash
    if ($this->rootUrl) {
      if ('/' !== substr($this->rootUrl, -1) && '/' !== substr($requestUrl, 0, 1)) {
        $requestUrl = '/' . $requestUrl;
      }
      $requestUrl = $this->rootUrl . $requestUrl;
    }
    $uriTemplateVars = array();
    $queryVars = array();
    foreach ($params as $paramName => $paramSpec) {
      if ($paramSpec['type'] == 'boolean') {
        $paramSpec['value'] = $paramSpec['value'] ? 'true' : 'false';
      }
      if ($paramSpec['location'] == 'path') {
        $uriTemplateVars[$paramName] = $paramSpec['value'];
      } else if ($paramSpec['location'] == 'query') {
        if (is_array($paramSpec['value'])) {
          foreach ($paramSpec['value'] as $value) {
            $queryVars[] = $paramName . '=' . rawurlencode(rawurldecode($value));
          }
        } else {
          $queryVars[] = $paramName . '=' . rawurlencode(rawurldecode($paramSpec['value']));
        }
      }
    }

    if (count($uriTemplateVars)) {
      $uriTemplateParser = new WPvivid_Google_Utils_UriTemplate();
      $requestUrl = $uriTemplateParser->parse($requestUrl, $uriTemplateVars);
    }

    if (count($queryVars)) {
        if (version_compare(PHP_VERSION, '7.4', '>=')) {
            $requestUrl .= '?' . implode('&', $queryVars);
        }
        else{
            $requestUrl .= '?' . implode($queryVars, '&');
        }
    }

    return $requestUrl;
  }
}
includes/lib2/google-api-php-client/src/WPvivid/Google/Service/Exception.php000064400000003553151327705670022753 0ustar00<?php
/*
 * Copyright 2014 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

class WPvivid_Google_Service_Exception extends WPvivid_Google_Exception
{
  /**
   * Optional list of errors returned in a JSON body of an HTTP error response.
   */
  protected $errors = array();

  /**
   * Override default constructor to add the ability to set $errors and a retry
   * map.
   *
   * @param string $message
   * @param int $code
   * @param Exception|null $previous
   * @param [{string, string}] errors List of errors returned in an HTTP
   * response.  Defaults to [].
   * @param array|null $retryMap Map of errors with retry counts.
   */
  public function __construct(
      $message,
      $code = 0,
      Exception $previous = null,
      $errors = array()
  ) {
    if (version_compare(PHP_VERSION, '5.3.0') >= 0) {
      parent::__construct($message, $code, $previous);
    } else {
      parent::__construct($message, $code);
    }

    $this->errors = $errors;
  }

  /**
   * An example of the possible errors returned.
   *
   * {
   *   "domain": "global",
   *   "reason": "authError",
   *   "message": "Invalid Credentials",
   *   "locationType": "header",
   *   "location": "Authorization",
   * }
   *
   * @return [{string, string}] List of errors return in an HTTP response or [].
   */
  public function getErrors()
  {
    return $this->errors;
  }
}
includes/lib2/google-api-php-client/src/WPvivid/Google/Service/README.md000064400000000301151327705670021547 0ustar00# Google API Client Services

Google API Client Service classes have been moved to the 
[google-api-php-client-services](https://github.com/google/google-api-php-client-services) 
repository. 
includes/lib2/google-api-php-client/src/WPvivid/Google/Service/Drive/DriveFileVideoMediaMetadata.php000064400000002274151327705670027326 0ustar00<?php
/*
 * Copyright 2014 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

class WPvivid_Google_Service_Drive_DriveFileVideoMediaMetadata extends WPvivid_Google_Model
{
  public $durationMillis;
  public $height;
  public $width;

  public function setDurationMillis($durationMillis)
  {
    $this->durationMillis = $durationMillis;
  }
  public function getDurationMillis()
  {
    return $this->durationMillis;
  }
  public function setHeight($height)
  {
    $this->height = $height;
  }
  public function getHeight()
  {
    return $this->height;
  }
  public function setWidth($width)
  {
    $this->width = $width;
  }
  public function getWidth()
  {
    return $this->width;
  }
}
includes/lib2/google-api-php-client/src/WPvivid/Google/Service/Drive/DriveFile.php000064400000033030151327705670023730 0ustar00<?php
/*
 * Copyright 2014 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

class WPvivid_Google_Service_Drive_DriveFile extends WPvivid_Google_Collection
{
  protected $collection_key = 'spaces';
  public $appProperties;
  protected $capabilitiesType = 'WPvivid_Google_Service_Drive_DriveFileCapabilities';
  protected $capabilitiesDataType = '';
  protected $contentHintsType = 'WPvivid_Google_Service_Drive_DriveFileContentHints';
  protected $contentHintsDataType = '';
  public $copyRequiresWriterPermission;
  public $createdTime;
  public $description;
  public $explicitlyTrashed;
  public $exportLinks;
  public $fileExtension;
  public $folderColorRgb;
  public $fullFileExtension;
  public $hasAugmentedPermissions;
  public $hasThumbnail;
  public $headRevisionId;
  public $iconLink;
  public $id;
  protected $imageMediaMetadataType = 'WPvivid_Google_Service_Drive_DriveFileImageMediaMetadata';
  protected $imageMediaMetadataDataType = '';
  public $isAppAuthorized;
  public $kind;
  protected $lastModifyingUserType = 'WPvivid_Google_Service_Drive_User';
  protected $lastModifyingUserDataType = '';
  public $md5Checksum;
  public $mimeType;
  public $modifiedByMe;
  public $modifiedByMeTime;
  public $modifiedTime;
  public $name;
  public $originalFilename;
  public $ownedByMe;
  protected $ownersType = 'WPvivid_Google_Service_Drive_User';
  protected $ownersDataType = 'array';
  public $parents;
  public $permissionIds;
  protected $permissionsType = 'WPvivid_Google_Service_Drive_Permission';
  protected $permissionsDataType = 'array';
  public $properties;
  public $quotaBytesUsed;
  public $shared;
  public $sharedWithMeTime;
  protected $sharingUserType = 'WPvivid_Google_Service_Drive_User';
  protected $sharingUserDataType = '';
  public $size;
  public $spaces;
  public $starred;
  public $teamDriveId;
  public $thumbnailLink;
  public $thumbnailVersion;
  public $trashed;
  public $trashedTime;
  protected $trashingUserType = 'WPvivid_Google_Service_Drive_User';
  protected $trashingUserDataType = '';
  public $version;
  protected $videoMediaMetadataType = 'WPvivid_Google_Service_Drive_DriveFileVideoMediaMetadata';
  protected $videoMediaMetadataDataType = '';
  public $viewedByMe;
  public $viewedByMeTime;
  public $viewersCanCopyContent;
  public $webContentLink;
  public $webViewLink;
  public $writersCanShare;

  public function setAppProperties($appProperties)
  {
    $this->appProperties = $appProperties;
  }
  public function getAppProperties()
  {
    return $this->appProperties;
  }
  /**
   * @param WPvivid_Google_Service_Drive_DriveFileCapabilities
   */
  public function setCapabilities(WPvivid_Google_Service_Drive_DriveFileCapabilities $capabilities)
  {
    $this->capabilities = $capabilities;
  }
  /**
   * @return WPvivid_Google_Service_Drive_DriveFileCapabilities
   */
  public function getCapabilities()
  {
    return $this->capabilities;
  }
  /**
   * @param WPvivid_Google_Service_Drive_DriveFileContentHints
   */
  public function setContentHints(WPvivid_Google_Service_Drive_DriveFileContentHints $contentHints)
  {
    $this->contentHints = $contentHints;
  }
  /**
   * @return WPvivid_Google_Service_Drive_DriveFileContentHints
   */
  public function getContentHints()
  {
    return $this->contentHints;
  }
  public function setCopyRequiresWriterPermission($copyRequiresWriterPermission)
  {
    $this->copyRequiresWriterPermission = $copyRequiresWriterPermission;
  }
  public function getCopyRequiresWriterPermission()
  {
    return $this->copyRequiresWriterPermission;
  }
  public function setCreatedTime($createdTime)
  {
    $this->createdTime = $createdTime;
  }
  public function getCreatedTime()
  {
    return $this->createdTime;
  }
  public function setDescription($description)
  {
    $this->description = $description;
  }
  public function getDescription()
  {
    return $this->description;
  }
  public function setExplicitlyTrashed($explicitlyTrashed)
  {
    $this->explicitlyTrashed = $explicitlyTrashed;
  }
  public function getExplicitlyTrashed()
  {
    return $this->explicitlyTrashed;
  }
  public function setExportLinks($exportLinks)
  {
    $this->exportLinks = $exportLinks;
  }
  public function getExportLinks()
  {
    return $this->exportLinks;
  }
  public function setFileExtension($fileExtension)
  {
    $this->fileExtension = $fileExtension;
  }
  public function getFileExtension()
  {
    return $this->fileExtension;
  }
  public function setFolderColorRgb($folderColorRgb)
  {
    $this->folderColorRgb = $folderColorRgb;
  }
  public function getFolderColorRgb()
  {
    return $this->folderColorRgb;
  }
  public function setFullFileExtension($fullFileExtension)
  {
    $this->fullFileExtension = $fullFileExtension;
  }
  public function getFullFileExtension()
  {
    return $this->fullFileExtension;
  }
  public function setHasAugmentedPermissions($hasAugmentedPermissions)
  {
    $this->hasAugmentedPermissions = $hasAugmentedPermissions;
  }
  public function getHasAugmentedPermissions()
  {
    return $this->hasAugmentedPermissions;
  }
  public function setHasThumbnail($hasThumbnail)
  {
    $this->hasThumbnail = $hasThumbnail;
  }
  public function getHasThumbnail()
  {
    return $this->hasThumbnail;
  }
  public function setHeadRevisionId($headRevisionId)
  {
    $this->headRevisionId = $headRevisionId;
  }
  public function getHeadRevisionId()
  {
    return $this->headRevisionId;
  }
  public function setIconLink($iconLink)
  {
    $this->iconLink = $iconLink;
  }
  public function getIconLink()
  {
    return $this->iconLink;
  }
  public function setId($id)
  {
    $this->id = $id;
  }
  public function getId()
  {
    return $this->id;
  }
  /**
   * @param WPvivid_Google_Service_Drive_DriveFileImageMediaMetadata
   */
  public function setImageMediaMetadata(WPvivid_Google_Service_Drive_DriveFileImageMediaMetadata $imageMediaMetadata)
  {
    $this->imageMediaMetadata = $imageMediaMetadata;
  }
  /**
   * @return WPvivid_Google_Service_Drive_DriveFileImageMediaMetadata
   */
  public function getImageMediaMetadata()
  {
    return $this->imageMediaMetadata;
  }
  public function setIsAppAuthorized($isAppAuthorized)
  {
    $this->isAppAuthorized = $isAppAuthorized;
  }
  public function getIsAppAuthorized()
  {
    return $this->isAppAuthorized;
  }
  public function setKind($kind)
  {
    $this->kind = $kind;
  }
  public function getKind()
  {
    return $this->kind;
  }
  /**
   * @param WPvivid_Google_Service_Drive_User
   */
  public function setLastModifyingUser(WPvivid_Google_Service_Drive_User $lastModifyingUser)
  {
    $this->lastModifyingUser = $lastModifyingUser;
  }
  /**
   * @return WPvivid_Google_Service_Drive_User
   */
  public function getLastModifyingUser()
  {
    return $this->lastModifyingUser;
  }
  public function setMd5Checksum($md5Checksum)
  {
    $this->md5Checksum = $md5Checksum;
  }
  public function getMd5Checksum()
  {
    return $this->md5Checksum;
  }
  public function setMimeType($mimeType)
  {
    $this->mimeType = $mimeType;
  }
  public function getMimeType()
  {
    return $this->mimeType;
  }
  public function setModifiedByMe($modifiedByMe)
  {
    $this->modifiedByMe = $modifiedByMe;
  }
  public function getModifiedByMe()
  {
    return $this->modifiedByMe;
  }
  public function setModifiedByMeTime($modifiedByMeTime)
  {
    $this->modifiedByMeTime = $modifiedByMeTime;
  }
  public function getModifiedByMeTime()
  {
    return $this->modifiedByMeTime;
  }
  public function setModifiedTime($modifiedTime)
  {
    $this->modifiedTime = $modifiedTime;
  }
  public function getModifiedTime()
  {
    return $this->modifiedTime;
  }
  public function setName($name)
  {
    $this->name = $name;
  }
  public function getName()
  {
    return $this->name;
  }
  public function setOriginalFilename($originalFilename)
  {
    $this->originalFilename = $originalFilename;
  }
  public function getOriginalFilename()
  {
    return $this->originalFilename;
  }
  public function setOwnedByMe($ownedByMe)
  {
    $this->ownedByMe = $ownedByMe;
  }
  public function getOwnedByMe()
  {
    return $this->ownedByMe;
  }
  /**
   * @param WPvivid_Google_Service_Drive_User
   */
  public function setOwners($owners)
  {
    $this->owners = $owners;
  }
  /**
   * @return WPvivid_Google_Service_Drive_User
   */
  public function getOwners()
  {
    return $this->owners;
  }
  public function setParents($parents)
  {
    $this->parents = $parents;
  }
  public function getParents()
  {
    return $this->parents;
  }
  public function setPermissionIds($permissionIds)
  {
    $this->permissionIds = $permissionIds;
  }
  public function getPermissionIds()
  {
    return $this->permissionIds;
  }
  /**
   * @param WPvivid_Google_Service_Drive_Permission
   */
  public function setPermissions($permissions)
  {
    $this->permissions = $permissions;
  }
  /**
   * @return WPvivid_Google_Service_Drive_Permission
   */
  public function getPermissions()
  {
    return $this->permissions;
  }
  public function setProperties($properties)
  {
    $this->properties = $properties;
  }
  public function getProperties()
  {
    return $this->properties;
  }
  public function setQuotaBytesUsed($quotaBytesUsed)
  {
    $this->quotaBytesUsed = $quotaBytesUsed;
  }
  public function getQuotaBytesUsed()
  {
    return $this->quotaBytesUsed;
  }
  public function setShared($shared)
  {
    $this->shared = $shared;
  }
  public function getShared()
  {
    return $this->shared;
  }
  public function setSharedWithMeTime($sharedWithMeTime)
  {
    $this->sharedWithMeTime = $sharedWithMeTime;
  }
  public function getSharedWithMeTime()
  {
    return $this->sharedWithMeTime;
  }
  /**
   * @param WPvivid_Google_Service_Drive_User
   */
  public function setSharingUser(WPvivid_Google_Service_Drive_User $sharingUser)
  {
    $this->sharingUser = $sharingUser;
  }
  /**
   * @return WPvivid_Google_Service_Drive_User
   */
  public function getSharingUser()
  {
    return $this->sharingUser;
  }
  public function setSize($size)
  {
    $this->size = $size;
  }
  public function getSize()
  {
    return $this->size;
  }
  public function setSpaces($spaces)
  {
    $this->spaces = $spaces;
  }
  public function getSpaces()
  {
    return $this->spaces;
  }
  public function setStarred($starred)
  {
    $this->starred = $starred;
  }
  public function getStarred()
  {
    return $this->starred;
  }
  public function setTeamDriveId($teamDriveId)
  {
    $this->teamDriveId = $teamDriveId;
  }
  public function getTeamDriveId()
  {
    return $this->teamDriveId;
  }
  public function setThumbnailLink($thumbnailLink)
  {
    $this->thumbnailLink = $thumbnailLink;
  }
  public function getThumbnailLink()
  {
    return $this->thumbnailLink;
  }
  public function setThumbnailVersion($thumbnailVersion)
  {
    $this->thumbnailVersion = $thumbnailVersion;
  }
  public function getThumbnailVersion()
  {
    return $this->thumbnailVersion;
  }
  public function setTrashed($trashed)
  {
    $this->trashed = $trashed;
  }
  public function getTrashed()
  {
    return $this->trashed;
  }
  public function setTrashedTime($trashedTime)
  {
    $this->trashedTime = $trashedTime;
  }
  public function getTrashedTime()
  {
    return $this->trashedTime;
  }
  /**
   * @param WPvivid_Google_Service_Drive_User
   */
  public function setTrashingUser(WPvivid_Google_Service_Drive_User $trashingUser)
  {
    $this->trashingUser = $trashingUser;
  }
  /**
   * @return WPvivid_Google_Service_Drive_User
   */
  public function getTrashingUser()
  {
    return $this->trashingUser;
  }
  public function setVersion($version)
  {
    $this->version = $version;
  }
  public function getVersion()
  {
    return $this->version;
  }
  /**
   * @param WPvivid_Google_Service_Drive_DriveFileVideoMediaMetadata
   */
  public function setVideoMediaMetadata(WPvivid_Google_Service_Drive_DriveFileVideoMediaMetadata $videoMediaMetadata)
  {
    $this->videoMediaMetadata = $videoMediaMetadata;
  }
  /**
   * @return WPvivid_Google_Service_Drive_DriveFileVideoMediaMetadata
   */
  public function getVideoMediaMetadata()
  {
    return $this->videoMediaMetadata;
  }
  public function setViewedByMe($viewedByMe)
  {
    $this->viewedByMe = $viewedByMe;
  }
  public function getViewedByMe()
  {
    return $this->viewedByMe;
  }
  public function setViewedByMeTime($viewedByMeTime)
  {
    $this->viewedByMeTime = $viewedByMeTime;
  }
  public function getViewedByMeTime()
  {
    return $this->viewedByMeTime;
  }
  public function setViewersCanCopyContent($viewersCanCopyContent)
  {
    $this->viewersCanCopyContent = $viewersCanCopyContent;
  }
  public function getViewersCanCopyContent()
  {
    return $this->viewersCanCopyContent;
  }
  public function setWebContentLink($webContentLink)
  {
    $this->webContentLink = $webContentLink;
  }
  public function getWebContentLink()
  {
    return $this->webContentLink;
  }
  public function setWebViewLink($webViewLink)
  {
    $this->webViewLink = $webViewLink;
  }
  public function getWebViewLink()
  {
    return $this->webViewLink;
  }
  public function setWritersCanShare($writersCanShare)
  {
    $this->writersCanShare = $writersCanShare;
  }
  public function getWritersCanShare()
  {
    return $this->writersCanShare;
  }
}
includes/lib2/google-api-php-client/src/WPvivid/Google/Service/Drive/CommentList.php000064400000002657151327705670024330 0ustar00<?php
/*
 * Copyright 2014 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

class WPvivid_Google_Service_Drive_CommentList extends WPvivid_Google_Collection
{
  protected $collection_key = 'comments';
  protected $commentsType = 'WPvivid_Google_Service_Drive_Comment';
  protected $commentsDataType = 'array';
  public $kind;
  public $nextPageToken;

  /**
   * @param WPvivid_Google_Service_Drive_Comment
   */
  public function setComments($comments)
  {
    $this->comments = $comments;
  }
  /**
   * @return WPvivid_Google_Service_Drive_Comment
   */
  public function getComments()
  {
    return $this->comments;
  }
  public function setKind($kind)
  {
    $this->kind = $kind;
  }
  public function getKind()
  {
    return $this->kind;
  }
  public function setNextPageToken($nextPageToken)
  {
    $this->nextPageToken = $nextPageToken;
  }
  public function getNextPageToken()
  {
    return $this->nextPageToken;
  }
}
includes/lib2/google-api-php-client/src/WPvivid/Google/Service/Drive/TeamDriveList.php000064400000002711151327705670024575 0ustar00<?php
/*
 * Copyright 2014 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

class WPvivid_Google_Service_Drive_TeamDriveList extends WPvivid_Google_Collection
{
  protected $collection_key = 'teamDrives';
  public $kind;
  public $nextPageToken;
  protected $teamDrivesType = 'WPvivid_Google_Service_Drive_TeamDrive';
  protected $teamDrivesDataType = 'array';

  public function setKind($kind)
  {
    $this->kind = $kind;
  }
  public function getKind()
  {
    return $this->kind;
  }
  public function setNextPageToken($nextPageToken)
  {
    $this->nextPageToken = $nextPageToken;
  }
  public function getNextPageToken()
  {
    return $this->nextPageToken;
  }
  /**
   * @param WPvivid_Google_Service_Drive_TeamDrive
   */
  public function setTeamDrives($teamDrives)
  {
    $this->teamDrives = $teamDrives;
  }
  /**
   * @return WPvivid_Google_Service_Drive_TeamDrive
   */
  public function getTeamDrives()
  {
    return $this->teamDrives;
  }
}
includes/lib2/google-api-php-client/src/WPvivid/Google/Service/Drive/AboutTeamDriveThemes.php000064400000002321151327705670026077 0ustar00<?php
/*
 * Copyright 2014 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

class WPvivid_Google_Service_Drive_AboutTeamDriveThemes extends WPvivid_Google_Model
{
  public $backgroundImageLink;
  public $colorRgb;
  public $id;

  public function setBackgroundImageLink($backgroundImageLink)
  {
    $this->backgroundImageLink = $backgroundImageLink;
  }
  public function getBackgroundImageLink()
  {
    return $this->backgroundImageLink;
  }
  public function setColorRgb($colorRgb)
  {
    $this->colorRgb = $colorRgb;
  }
  public function getColorRgb()
  {
    return $this->colorRgb;
  }
  public function setId($id)
  {
    $this->id = $id;
  }
  public function getId()
  {
    return $this->id;
  }
}
includes/lib2/google-api-php-client/src/WPvivid/Google/Service/Drive/ChangeList.php000064400000003203151327705670024077 0ustar00<?php
/*
 * Copyright 2014 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

class WPvivid_Google_Service_Drive_ChangeList extends Google_Collection
{
  protected $collection_key = 'changes';
  protected $changesType = 'WPvivid_Google_Service_Drive_Change';
  protected $changesDataType = 'array';
  public $kind;
  public $newStartPageToken;
  public $nextPageToken;

  /**
   * @param WPvivid_Google_Service_Drive_Change
   */
  public function setChanges($changes)
  {
    $this->changes = $changes;
  }
  /**
   * @return WPvivid_Google_Service_Drive_Change
   */
  public function getChanges()
  {
    return $this->changes;
  }
  public function setKind($kind)
  {
    $this->kind = $kind;
  }
  public function getKind()
  {
    return $this->kind;
  }
  public function setNewStartPageToken($newStartPageToken)
  {
    $this->newStartPageToken = $newStartPageToken;
  }
  public function getNewStartPageToken()
  {
    return $this->newStartPageToken;
  }
  public function setNextPageToken($nextPageToken)
  {
    $this->nextPageToken = $nextPageToken;
  }
  public function getNextPageToken()
  {
    return $this->nextPageToken;
  }
}
includes/lib2/google-api-php-client/src/WPvivid/Google/Service/Drive/DriveFileContentHints.php000064400000002600151327705670026270 0ustar00<?php
/*
 * Copyright 2014 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

class WPvivid_Google_Service_Drive_DriveFileContentHints extends WPvivid_Google_Model
{
  public $indexableText;
  protected $thumbnailType = 'WPvivid_Google_Service_Drive_DriveFileContentHintsThumbnail';
  protected $thumbnailDataType = '';

  public function setIndexableText($indexableText)
  {
    $this->indexableText = $indexableText;
  }
  public function getIndexableText()
  {
    return $this->indexableText;
  }
  /**
   * @param WPvivid_Google_Service_Drive_DriveFileContentHintsThumbnail
   */
  public function setThumbnail(WPvivid_Google_Service_Drive_DriveFileContentHintsThumbnail $thumbnail)
  {
    $this->thumbnail = $thumbnail;
  }
  /**
   * @return WPvivid_Google_Service_Drive_DriveFileContentHintsThumbnail
   */
  public function getThumbnail()
  {
    return $this->thumbnail;
  }
}
includes/lib2/google-api-php-client/src/WPvivid/Google/Service/Drive/TeamDrive.php000064400000006572151327705670023752 0ustar00<?php
/*
 * Copyright 2014 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

class WPvivid_Google_Service_Drive_TeamDrive extends WPvivid_Google_Model
{
  protected $backgroundImageFileType = 'WPvivid_Google_Service_Drive_TeamDriveBackgroundImageFile';
  protected $backgroundImageFileDataType = '';
  public $backgroundImageLink;
  protected $capabilitiesType = 'WPvivid_Google_Service_Drive_TeamDriveCapabilities';
  protected $capabilitiesDataType = '';
  public $colorRgb;
  public $createdTime;
  public $id;
  public $kind;
  public $name;
  protected $restrictionsType = 'WPvivid_Google_Service_Drive_TeamDriveRestrictions';
  protected $restrictionsDataType = '';
  public $themeId;

  /**
   * @param WPvivid_Google_Service_Drive_TeamDriveBackgroundImageFile
   */
  public function setBackgroundImageFile(WPvivid_Google_Service_Drive_TeamDriveBackgroundImageFile $backgroundImageFile)
  {
    $this->backgroundImageFile = $backgroundImageFile;
  }
  /**
   * @return WPvivid_Google_Service_Drive_TeamDriveBackgroundImageFile
   */
  public function getBackgroundImageFile()
  {
    return $this->backgroundImageFile;
  }
  public function setBackgroundImageLink($backgroundImageLink)
  {
    $this->backgroundImageLink = $backgroundImageLink;
  }
  public function getBackgroundImageLink()
  {
    return $this->backgroundImageLink;
  }
  /**
   * @param WPvivid_Google_Service_Drive_TeamDriveCapabilities
   */
  public function setCapabilities(WPvivid_Google_Service_Drive_TeamDriveCapabilities $capabilities)
  {
    $this->capabilities = $capabilities;
  }
  /**
   * @return WPvivid_Google_Service_Drive_TeamDriveCapabilities
   */
  public function getCapabilities()
  {
    return $this->capabilities;
  }
  public function setColorRgb($colorRgb)
  {
    $this->colorRgb = $colorRgb;
  }
  public function getColorRgb()
  {
    return $this->colorRgb;
  }
  public function setCreatedTime($createdTime)
  {
    $this->createdTime = $createdTime;
  }
  public function getCreatedTime()
  {
    return $this->createdTime;
  }
  public function setId($id)
  {
    $this->id = $id;
  }
  public function getId()
  {
    return $this->id;
  }
  public function setKind($kind)
  {
    $this->kind = $kind;
  }
  public function getKind()
  {
    return $this->kind;
  }
  public function setName($name)
  {
    $this->name = $name;
  }
  public function getName()
  {
    return $this->name;
  }
  /**
   * @param WPvivid_Google_Service_Drive_TeamDriveRestrictions
   */
  public function setRestrictions(WPvivid_Google_Service_Drive_TeamDriveRestrictions $restrictions)
  {
    $this->restrictions = $restrictions;
  }
  /**
   * @return WPvivid_Google_Service_Drive_TeamDriveRestrictions
   */
  public function getRestrictions()
  {
    return $this->restrictions;
  }
  public function setThemeId($themeId)
  {
    $this->themeId = $themeId;
  }
  public function getThemeId()
  {
    return $this->themeId;
  }
}
lib2/google-api-php-client/src/WPvivid/Google/Service/Drive/TeamDriveBackgroundImageFile.php000064400000002513151327705670027425 0ustar00includes<?php
/*
 * Copyright 2014 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

class WPvivid_Google_Service_Drive_TeamDriveBackgroundImageFile extends WPvivid_Google_Model
{
  public $id;
  public $width;
  public $xCoordinate;
  public $yCoordinate;

  public function setId($id)
  {
    $this->id = $id;
  }
  public function getId()
  {
    return $this->id;
  }
  public function setWidth($width)
  {
    $this->width = $width;
  }
  public function getWidth()
  {
    return $this->width;
  }
  public function setXCoordinate($xCoordinate)
  {
    $this->xCoordinate = $xCoordinate;
  }
  public function getXCoordinate()
  {
    return $this->xCoordinate;
  }
  public function setYCoordinate($yCoordinate)
  {
    $this->yCoordinate = $yCoordinate;
  }
  public function getYCoordinate()
  {
    return $this->yCoordinate;
  }
}
includes/lib2/google-api-php-client/src/WPvivid/Google/Service/Drive/Change.php000064400000004576151327705670023261 0ustar00<?php
/*
 * Copyright 2014 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

class WPvivid_Google_Service_Drive_Change extends WPvivid_Google_Model
{
  protected $fileType = 'WPvivid_Google_Service_Drive_DriveFile';
  protected $fileDataType = '';
  public $fileId;
  public $kind;
  public $removed;
  protected $teamDriveType = 'WPvivid_Google_Service_Drive_TeamDrive';
  protected $teamDriveDataType = '';
  public $teamDriveId;
  public $time;
  public $type;

  /**
   * @param WPvivid_Google_Service_Drive_DriveFile
   */
  public function setFile(WPvivid_Google_Service_Drive_DriveFile $file)
  {
    $this->file = $file;
  }
  /**
   * @return WPvivid_Google_Service_Drive_DriveFile
   */
  public function getFile()
  {
    return $this->file;
  }
  public function setFileId($fileId)
  {
    $this->fileId = $fileId;
  }
  public function getFileId()
  {
    return $this->fileId;
  }
  public function setKind($kind)
  {
    $this->kind = $kind;
  }
  public function getKind()
  {
    return $this->kind;
  }
  public function setRemoved($removed)
  {
    $this->removed = $removed;
  }
  public function getRemoved()
  {
    return $this->removed;
  }
  /**
   * @param WPvivid_Google_Service_Drive_TeamDrive
   */
  public function setTeamDrive(WPvivid_Google_Service_Drive_TeamDrive $teamDrive)
  {
    $this->teamDrive = $teamDrive;
  }
  /**
   * @return WPvivid_Google_Service_Drive_TeamDrive
   */
  public function getTeamDrive()
  {
    return $this->teamDrive;
  }
  public function setTeamDriveId($teamDriveId)
  {
    $this->teamDriveId = $teamDriveId;
  }
  public function getTeamDriveId()
  {
    return $this->teamDriveId;
  }
  public function setTime($time)
  {
    $this->time = $time;
  }
  public function getTime()
  {
    return $this->time;
  }
  public function setType($type)
  {
    $this->type = $type;
  }
  public function getType()
  {
    return $this->type;
  }
}
includes/lib2/google-api-php-client/src/WPvivid/Google/Service/Drive/DriveFileCapabilities.php000064400000014236151327705670026251 0ustar00<?php
/*
 * Copyright 2014 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

class WPvivid_Google_Service_Drive_DriveFileCapabilities extends WPvivid_Google_Model
{
  public $canAddChildren;
  public $canChangeCopyRequiresWriterPermission;
  public $canChangeViewersCanCopyContent;
  public $canComment;
  public $canCopy;
  public $canDelete;
  public $canDeleteChildren;
  public $canDownload;
  public $canEdit;
  public $canListChildren;
  public $canMoveChildrenOutOfTeamDrive;
  public $canMoveChildrenWithinTeamDrive;
  public $canMoveItemIntoTeamDrive;
  public $canMoveItemOutOfTeamDrive;
  public $canMoveItemWithinTeamDrive;
  public $canMoveTeamDriveItem;
  public $canReadRevisions;
  public $canReadTeamDrive;
  public $canRemoveChildren;
  public $canRename;
  public $canShare;
  public $canTrash;
  public $canTrashChildren;
  public $canUntrash;

  public function setCanAddChildren($canAddChildren)
  {
    $this->canAddChildren = $canAddChildren;
  }
  public function getCanAddChildren()
  {
    return $this->canAddChildren;
  }
  public function setCanChangeCopyRequiresWriterPermission($canChangeCopyRequiresWriterPermission)
  {
    $this->canChangeCopyRequiresWriterPermission = $canChangeCopyRequiresWriterPermission;
  }
  public function getCanChangeCopyRequiresWriterPermission()
  {
    return $this->canChangeCopyRequiresWriterPermission;
  }
  public function setCanChangeViewersCanCopyContent($canChangeViewersCanCopyContent)
  {
    $this->canChangeViewersCanCopyContent = $canChangeViewersCanCopyContent;
  }
  public function getCanChangeViewersCanCopyContent()
  {
    return $this->canChangeViewersCanCopyContent;
  }
  public function setCanComment($canComment)
  {
    $this->canComment = $canComment;
  }
  public function getCanComment()
  {
    return $this->canComment;
  }
  public function setCanCopy($canCopy)
  {
    $this->canCopy = $canCopy;
  }
  public function getCanCopy()
  {
    return $this->canCopy;
  }
  public function setCanDelete($canDelete)
  {
    $this->canDelete = $canDelete;
  }
  public function getCanDelete()
  {
    return $this->canDelete;
  }
  public function setCanDeleteChildren($canDeleteChildren)
  {
    $this->canDeleteChildren = $canDeleteChildren;
  }
  public function getCanDeleteChildren()
  {
    return $this->canDeleteChildren;
  }
  public function setCanDownload($canDownload)
  {
    $this->canDownload = $canDownload;
  }
  public function getCanDownload()
  {
    return $this->canDownload;
  }
  public function setCanEdit($canEdit)
  {
    $this->canEdit = $canEdit;
  }
  public function getCanEdit()
  {
    return $this->canEdit;
  }
  public function setCanListChildren($canListChildren)
  {
    $this->canListChildren = $canListChildren;
  }
  public function getCanListChildren()
  {
    return $this->canListChildren;
  }
  public function setCanMoveChildrenOutOfTeamDrive($canMoveChildrenOutOfTeamDrive)
  {
    $this->canMoveChildrenOutOfTeamDrive = $canMoveChildrenOutOfTeamDrive;
  }
  public function getCanMoveChildrenOutOfTeamDrive()
  {
    return $this->canMoveChildrenOutOfTeamDrive;
  }
  public function setCanMoveChildrenWithinTeamDrive($canMoveChildrenWithinTeamDrive)
  {
    $this->canMoveChildrenWithinTeamDrive = $canMoveChildrenWithinTeamDrive;
  }
  public function getCanMoveChildrenWithinTeamDrive()
  {
    return $this->canMoveChildrenWithinTeamDrive;
  }
  public function setCanMoveItemIntoTeamDrive($canMoveItemIntoTeamDrive)
  {
    $this->canMoveItemIntoTeamDrive = $canMoveItemIntoTeamDrive;
  }
  public function getCanMoveItemIntoTeamDrive()
  {
    return $this->canMoveItemIntoTeamDrive;
  }
  public function setCanMoveItemOutOfTeamDrive($canMoveItemOutOfTeamDrive)
  {
    $this->canMoveItemOutOfTeamDrive = $canMoveItemOutOfTeamDrive;
  }
  public function getCanMoveItemOutOfTeamDrive()
  {
    return $this->canMoveItemOutOfTeamDrive;
  }
  public function setCanMoveItemWithinTeamDrive($canMoveItemWithinTeamDrive)
  {
    $this->canMoveItemWithinTeamDrive = $canMoveItemWithinTeamDrive;
  }
  public function getCanMoveItemWithinTeamDrive()
  {
    return $this->canMoveItemWithinTeamDrive;
  }
  public function setCanMoveTeamDriveItem($canMoveTeamDriveItem)
  {
    $this->canMoveTeamDriveItem = $canMoveTeamDriveItem;
  }
  public function getCanMoveTeamDriveItem()
  {
    return $this->canMoveTeamDriveItem;
  }
  public function setCanReadRevisions($canReadRevisions)
  {
    $this->canReadRevisions = $canReadRevisions;
  }
  public function getCanReadRevisions()
  {
    return $this->canReadRevisions;
  }
  public function setCanReadTeamDrive($canReadTeamDrive)
  {
    $this->canReadTeamDrive = $canReadTeamDrive;
  }
  public function getCanReadTeamDrive()
  {
    return $this->canReadTeamDrive;
  }
  public function setCanRemoveChildren($canRemoveChildren)
  {
    $this->canRemoveChildren = $canRemoveChildren;
  }
  public function getCanRemoveChildren()
  {
    return $this->canRemoveChildren;
  }
  public function setCanRename($canRename)
  {
    $this->canRename = $canRename;
  }
  public function getCanRename()
  {
    return $this->canRename;
  }
  public function setCanShare($canShare)
  {
    $this->canShare = $canShare;
  }
  public function getCanShare()
  {
    return $this->canShare;
  }
  public function setCanTrash($canTrash)
  {
    $this->canTrash = $canTrash;
  }
  public function getCanTrash()
  {
    return $this->canTrash;
  }
  public function setCanTrashChildren($canTrashChildren)
  {
    $this->canTrashChildren = $canTrashChildren;
  }
  public function getCanTrashChildren()
  {
    return $this->canTrashChildren;
  }
  public function setCanUntrash($canUntrash)
  {
    $this->canUntrash = $canUntrash;
  }
  public function getCanUntrash()
  {
    return $this->canUntrash;
  }
}
includes/lib2/google-api-php-client/src/WPvivid/Google/Service/Drive/Permission.php000064400000006257151327705670024222 0ustar00<?php
/*
 * Copyright 2014 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

class WPvivid_Google_Service_Drive_Permission extends WPvivid_Google_Collection
{
  protected $collection_key = 'teamDrivePermissionDetails';
  public $allowFileDiscovery;
  public $deleted;
  public $displayName;
  public $domain;
  public $emailAddress;
  public $expirationTime;
  public $id;
  public $kind;
  public $photoLink;
  public $role;
  protected $teamDrivePermissionDetailsType = 'WPvivid_Google_Service_Drive_PermissionTeamDrivePermissionDetails';
  protected $teamDrivePermissionDetailsDataType = 'array';
  public $type;

  public function setAllowFileDiscovery($allowFileDiscovery)
  {
    $this->allowFileDiscovery = $allowFileDiscovery;
  }
  public function getAllowFileDiscovery()
  {
    return $this->allowFileDiscovery;
  }
  public function setDeleted($deleted)
  {
    $this->deleted = $deleted;
  }
  public function getDeleted()
  {
    return $this->deleted;
  }
  public function setDisplayName($displayName)
  {
    $this->displayName = $displayName;
  }
  public function getDisplayName()
  {
    return $this->displayName;
  }
  public function setDomain($domain)
  {
    $this->domain = $domain;
  }
  public function getDomain()
  {
    return $this->domain;
  }
  public function setEmailAddress($emailAddress)
  {
    $this->emailAddress = $emailAddress;
  }
  public function getEmailAddress()
  {
    return $this->emailAddress;
  }
  public function setExpirationTime($expirationTime)
  {
    $this->expirationTime = $expirationTime;
  }
  public function getExpirationTime()
  {
    return $this->expirationTime;
  }
  public function setId($id)
  {
    $this->id = $id;
  }
  public function getId()
  {
    return $this->id;
  }
  public function setKind($kind)
  {
    $this->kind = $kind;
  }
  public function getKind()
  {
    return $this->kind;
  }
  public function setPhotoLink($photoLink)
  {
    $this->photoLink = $photoLink;
  }
  public function getPhotoLink()
  {
    return $this->photoLink;
  }
  public function setRole($role)
  {
    $this->role = $role;
  }
  public function getRole()
  {
    return $this->role;
  }
  /**
   * @param WPvivid_Google_Service_Drive_PermissionTeamDrivePermissionDetails
   */
  public function setTeamDrivePermissionDetails($teamDrivePermissionDetails)
  {
    $this->teamDrivePermissionDetails = $teamDrivePermissionDetails;
  }
  /**
   * @return WPvivid_Google_Service_Drive_PermissionTeamDrivePermissionDetails
   */
  public function getTeamDrivePermissionDetails()
  {
    return $this->teamDrivePermissionDetails;
  }
  public function setType($type)
  {
    $this->type = $type;
  }
  public function getType()
  {
    return $this->type;
  }
}
includes/lib2/google-api-php-client/src/WPvivid/Google/Service/Drive/Revision.php000064400000006445151327705670023667 0ustar00<?php
/*
 * Copyright 2014 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

class WPvivid_Google_Service_Drive_Revision extends WPvivid_Google_Model
{
  public $exportLinks;
  public $id;
  public $keepForever;
  public $kind;
  protected $lastModifyingUserType = 'WPvivid_Google_Service_Drive_User';
  protected $lastModifyingUserDataType = '';
  public $md5Checksum;
  public $mimeType;
  public $modifiedTime;
  public $originalFilename;
  public $publishAuto;
  public $published;
  public $publishedOutsideDomain;
  public $size;

  public function setExportLinks($exportLinks)
  {
    $this->exportLinks = $exportLinks;
  }
  public function getExportLinks()
  {
    return $this->exportLinks;
  }
  public function setId($id)
  {
    $this->id = $id;
  }
  public function getId()
  {
    return $this->id;
  }
  public function setKeepForever($keepForever)
  {
    $this->keepForever = $keepForever;
  }
  public function getKeepForever()
  {
    return $this->keepForever;
  }
  public function setKind($kind)
  {
    $this->kind = $kind;
  }
  public function getKind()
  {
    return $this->kind;
  }
  /**
   * @param WPvivid_Google_Service_Drive_User
   */
  public function setLastModifyingUser(WPvivid_Google_Service_Drive_User $lastModifyingUser)
  {
    $this->lastModifyingUser = $lastModifyingUser;
  }
  /**
   * @return WPvivid_Google_Service_Drive_User
   */
  public function getLastModifyingUser()
  {
    return $this->lastModifyingUser;
  }
  public function setMd5Checksum($md5Checksum)
  {
    $this->md5Checksum = $md5Checksum;
  }
  public function getMd5Checksum()
  {
    return $this->md5Checksum;
  }
  public function setMimeType($mimeType)
  {
    $this->mimeType = $mimeType;
  }
  public function getMimeType()
  {
    return $this->mimeType;
  }
  public function setModifiedTime($modifiedTime)
  {
    $this->modifiedTime = $modifiedTime;
  }
  public function getModifiedTime()
  {
    return $this->modifiedTime;
  }
  public function setOriginalFilename($originalFilename)
  {
    $this->originalFilename = $originalFilename;
  }
  public function getOriginalFilename()
  {
    return $this->originalFilename;
  }
  public function setPublishAuto($publishAuto)
  {
    $this->publishAuto = $publishAuto;
  }
  public function getPublishAuto()
  {
    return $this->publishAuto;
  }
  public function setPublished($published)
  {
    $this->published = $published;
  }
  public function getPublished()
  {
    return $this->published;
  }
  public function setPublishedOutsideDomain($publishedOutsideDomain)
  {
    $this->publishedOutsideDomain = $publishedOutsideDomain;
  }
  public function getPublishedOutsideDomain()
  {
    return $this->publishedOutsideDomain;
  }
  public function setSize($size)
  {
    $this->size = $size;
  }
  public function getSize()
  {
    return $this->size;
  }
}
includes/lib2/google-api-php-client/src/WPvivid/Google/Service/Drive/CommentQuotedFileContent.php000064400000001763151327705670027006 0ustar00<?php
/*
 * Copyright 2014 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

class WPvivid_Google_Service_Drive_CommentQuotedFileContent extends WPvivid_Google_Model
{
  public $mimeType;
  public $value;

  public function setMimeType($mimeType)
  {
    $this->mimeType = $mimeType;
  }
  public function getMimeType()
  {
    return $this->mimeType;
  }
  public function setValue($value)
  {
    $this->value = $value;
  }
  public function getValue()
  {
    return $this->value;
  }
}
includes/lib2/google-api-php-client/src/WPvivid/Google/Service/Drive/GeneratedIds.php000064400000002174151327705670024422 0ustar00<?php
/*
 * Copyright 2014 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

class WPvivid_Google_Service_Drive_GeneratedIds extends WPvivid_Google_Collection
{
  protected $collection_key = 'ids';
  public $ids;
  public $kind;
  public $space;

  public function setIds($ids)
  {
    $this->ids = $ids;
  }
  public function getIds()
  {
    return $this->ids;
  }
  public function setKind($kind)
  {
    $this->kind = $kind;
  }
  public function getKind()
  {
    return $this->kind;
  }
  public function setSpace($space)
  {
    $this->space = $space;
  }
  public function getSpace()
  {
    return $this->space;
  }
}
lib2/google-api-php-client/src/WPvivid/Google/Service/Drive/DriveFileImageMediaMetadataLocation.php000064400000002304151327705670030706 0ustar00includes<?php
/*
 * Copyright 2014 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

class WPvivid_Google_Service_Drive_DriveFileImageMediaMetadataLocation extends WPvivid_Google_Model
{
  public $altitude;
  public $latitude;
  public $longitude;

  public function setAltitude($altitude)
  {
    $this->altitude = $altitude;
  }
  public function getAltitude()
  {
    return $this->altitude;
  }
  public function setLatitude($latitude)
  {
    $this->latitude = $latitude;
  }
  public function getLatitude()
  {
    return $this->latitude;
  }
  public function setLongitude($longitude)
  {
    $this->longitude = $longitude;
  }
  public function getLongitude()
  {
    return $this->longitude;
  }
}
includes/lib2/google-api-php-client/src/WPvivid/Google/Service/Drive/PermissionList.php000064400000002726151327705670025053 0ustar00<?php
/*
 * Copyright 2014 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

class WPvivid_Google_Service_Drive_PermissionList extends WPvivid_Google_Collection
{
  protected $collection_key = 'permissions';
  public $kind;
  public $nextPageToken;
  protected $permissionsType = 'WPvivid_Google_Service_Drive_Permission';
  protected $permissionsDataType = 'array';

  public function setKind($kind)
  {
    $this->kind = $kind;
  }
  public function getKind()
  {
    return $this->kind;
  }
  public function setNextPageToken($nextPageToken)
  {
    $this->nextPageToken = $nextPageToken;
  }
  public function getNextPageToken()
  {
    return $this->nextPageToken;
  }
  /**
   * @param WPvivid_Google_Service_Drive_Permission
   */
  public function setPermissions($permissions)
  {
    $this->permissions = $permissions;
  }
  /**
   * @return WPvivid_Google_Service_Drive_Permission
   */
  public function getPermissions()
  {
    return $this->permissions;
  }
}
includes/lib2/google-api-php-client/src/WPvivid/Google/Service/Drive/StartPageToken.php000064400000002014151327705670024750 0ustar00<?php
/*
 * Copyright 2014 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

class WPvivid_Google_Service_Drive_StartPageToken extends WPvivid_Google_Model
{
  public $kind;
  public $startPageToken;

  public function setKind($kind)
  {
    $this->kind = $kind;
  }
  public function getKind()
  {
    return $this->kind;
  }
  public function setStartPageToken($startPageToken)
  {
    $this->startPageToken = $startPageToken;
  }
  public function getStartPageToken()
  {
    return $this->startPageToken;
  }
}
includes/lib2/google-api-php-client/src/WPvivid/Google/Service/Drive/TeamDriveCapabilities.php000064400000012226151327705670026255 0ustar00<?php
/*
 * Copyright 2014 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

class WPvivid_Google_Service_Drive_TeamDriveCapabilities extends WPvivid_Google_Model
{
  public $canAddChildren;
  public $canChangeCopyRequiresWriterPermissionRestriction;
  public $canChangeDomainUsersOnlyRestriction;
  public $canChangeTeamDriveBackground;
  public $canChangeTeamMembersOnlyRestriction;
  public $canComment;
  public $canCopy;
  public $canDeleteChildren;
  public $canDeleteTeamDrive;
  public $canDownload;
  public $canEdit;
  public $canListChildren;
  public $canManageMembers;
  public $canReadRevisions;
  public $canRemoveChildren;
  public $canRename;
  public $canRenameTeamDrive;
  public $canShare;
  public $canTrashChildren;

  public function setCanAddChildren($canAddChildren)
  {
    $this->canAddChildren = $canAddChildren;
  }
  public function getCanAddChildren()
  {
    return $this->canAddChildren;
  }
  public function setCanChangeCopyRequiresWriterPermissionRestriction($canChangeCopyRequiresWriterPermissionRestriction)
  {
    $this->canChangeCopyRequiresWriterPermissionRestriction = $canChangeCopyRequiresWriterPermissionRestriction;
  }
  public function getCanChangeCopyRequiresWriterPermissionRestriction()
  {
    return $this->canChangeCopyRequiresWriterPermissionRestriction;
  }
  public function setCanChangeDomainUsersOnlyRestriction($canChangeDomainUsersOnlyRestriction)
  {
    $this->canChangeDomainUsersOnlyRestriction = $canChangeDomainUsersOnlyRestriction;
  }
  public function getCanChangeDomainUsersOnlyRestriction()
  {
    return $this->canChangeDomainUsersOnlyRestriction;
  }
  public function setCanChangeTeamDriveBackground($canChangeTeamDriveBackground)
  {
    $this->canChangeTeamDriveBackground = $canChangeTeamDriveBackground;
  }
  public function getCanChangeTeamDriveBackground()
  {
    return $this->canChangeTeamDriveBackground;
  }
  public function setCanChangeTeamMembersOnlyRestriction($canChangeTeamMembersOnlyRestriction)
  {
    $this->canChangeTeamMembersOnlyRestriction = $canChangeTeamMembersOnlyRestriction;
  }
  public function getCanChangeTeamMembersOnlyRestriction()
  {
    return $this->canChangeTeamMembersOnlyRestriction;
  }
  public function setCanComment($canComment)
  {
    $this->canComment = $canComment;
  }
  public function getCanComment()
  {
    return $this->canComment;
  }
  public function setCanCopy($canCopy)
  {
    $this->canCopy = $canCopy;
  }
  public function getCanCopy()
  {
    return $this->canCopy;
  }
  public function setCanDeleteChildren($canDeleteChildren)
  {
    $this->canDeleteChildren = $canDeleteChildren;
  }
  public function getCanDeleteChildren()
  {
    return $this->canDeleteChildren;
  }
  public function setCanDeleteTeamDrive($canDeleteTeamDrive)
  {
    $this->canDeleteTeamDrive = $canDeleteTeamDrive;
  }
  public function getCanDeleteTeamDrive()
  {
    return $this->canDeleteTeamDrive;
  }
  public function setCanDownload($canDownload)
  {
    $this->canDownload = $canDownload;
  }
  public function getCanDownload()
  {
    return $this->canDownload;
  }
  public function setCanEdit($canEdit)
  {
    $this->canEdit = $canEdit;
  }
  public function getCanEdit()
  {
    return $this->canEdit;
  }
  public function setCanListChildren($canListChildren)
  {
    $this->canListChildren = $canListChildren;
  }
  public function getCanListChildren()
  {
    return $this->canListChildren;
  }
  public function setCanManageMembers($canManageMembers)
  {
    $this->canManageMembers = $canManageMembers;
  }
  public function getCanManageMembers()
  {
    return $this->canManageMembers;
  }
  public function setCanReadRevisions($canReadRevisions)
  {
    $this->canReadRevisions = $canReadRevisions;
  }
  public function getCanReadRevisions()
  {
    return $this->canReadRevisions;
  }
  public function setCanRemoveChildren($canRemoveChildren)
  {
    $this->canRemoveChildren = $canRemoveChildren;
  }
  public function getCanRemoveChildren()
  {
    return $this->canRemoveChildren;
  }
  public function setCanRename($canRename)
  {
    $this->canRename = $canRename;
  }
  public function getCanRename()
  {
    return $this->canRename;
  }
  public function setCanRenameTeamDrive($canRenameTeamDrive)
  {
    $this->canRenameTeamDrive = $canRenameTeamDrive;
  }
  public function getCanRenameTeamDrive()
  {
    return $this->canRenameTeamDrive;
  }
  public function setCanShare($canShare)
  {
    $this->canShare = $canShare;
  }
  public function getCanShare()
  {
    return $this->canShare;
  }
  public function setCanTrashChildren($canTrashChildren)
  {
    $this->canTrashChildren = $canTrashChildren;
  }
  public function getCanTrashChildren()
  {
    return $this->canTrashChildren;
  }
}
includes/lib2/google-api-php-client/src/WPvivid/Google/Service/Drive/User.php000064400000003252151327705670023000 0ustar00<?php
/*
 * Copyright 2014 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

class WPvivid_Google_Service_Drive_User extends WPvivid_Google_Model
{
  public $displayName;
  public $emailAddress;
  public $kind;
  public $me;
  public $permissionId;
  public $photoLink;

  public function setDisplayName($displayName)
  {
    $this->displayName = $displayName;
  }
  public function getDisplayName()
  {
    return $this->displayName;
  }
  public function setEmailAddress($emailAddress)
  {
    $this->emailAddress = $emailAddress;
  }
  public function getEmailAddress()
  {
    return $this->emailAddress;
  }
  public function setKind($kind)
  {
    $this->kind = $kind;
  }
  public function getKind()
  {
    return $this->kind;
  }
  public function setMe($me)
  {
    $this->me = $me;
  }
  public function getMe()
  {
    return $this->me;
  }
  public function setPermissionId($permissionId)
  {
    $this->permissionId = $permissionId;
  }
  public function getPermissionId()
  {
    return $this->permissionId;
  }
  public function setPhotoLink($photoLink)
  {
    $this->photoLink = $photoLink;
  }
  public function getPhotoLink()
  {
    return $this->photoLink;
  }
}
includes/lib2/google-api-php-client/src/WPvivid/Google/Service/Drive/TeamDriveRestrictions.php000064400000003267151327705670026361 0ustar00<?php
/*
 * Copyright 2014 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

class WPvivid_Google_Service_Drive_TeamDriveRestrictions extends WPvivid_Google_Model
{
  public $adminManagedRestrictions;
  public $copyRequiresWriterPermission;
  public $domainUsersOnly;
  public $teamMembersOnly;

  public function setAdminManagedRestrictions($adminManagedRestrictions)
  {
    $this->adminManagedRestrictions = $adminManagedRestrictions;
  }
  public function getAdminManagedRestrictions()
  {
    return $this->adminManagedRestrictions;
  }
  public function setCopyRequiresWriterPermission($copyRequiresWriterPermission)
  {
    $this->copyRequiresWriterPermission = $copyRequiresWriterPermission;
  }
  public function getCopyRequiresWriterPermission()
  {
    return $this->copyRequiresWriterPermission;
  }
  public function setDomainUsersOnly($domainUsersOnly)
  {
    $this->domainUsersOnly = $domainUsersOnly;
  }
  public function getDomainUsersOnly()
  {
    return $this->domainUsersOnly;
  }
  public function setTeamMembersOnly($teamMembersOnly)
  {
    $this->teamMembersOnly = $teamMembersOnly;
  }
  public function getTeamMembersOnly()
  {
    return $this->teamMembersOnly;
  }
}
includes/lib2/google-api-php-client/src/WPvivid/Google/Service/Drive/Channel.php000064400000004345151327705670023436 0ustar00<?php
/*
 * Copyright 2014 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

class WPvivid_Google_Service_Drive_Channel extends WPvivid_Google_Model
{
  public $address;
  public $expiration;
  public $id;
  public $kind;
  public $params;
  public $payload;
  public $resourceId;
  public $resourceUri;
  public $token;
  public $type;

  public function setAddress($address)
  {
    $this->address = $address;
  }
  public function getAddress()
  {
    return $this->address;
  }
  public function setExpiration($expiration)
  {
    $this->expiration = $expiration;
  }
  public function getExpiration()
  {
    return $this->expiration;
  }
  public function setId($id)
  {
    $this->id = $id;
  }
  public function getId()
  {
    return $this->id;
  }
  public function setKind($kind)
  {
    $this->kind = $kind;
  }
  public function getKind()
  {
    return $this->kind;
  }
  public function setParams($params)
  {
    $this->params = $params;
  }
  public function getParams()
  {
    return $this->params;
  }
  public function setPayload($payload)
  {
    $this->payload = $payload;
  }
  public function getPayload()
  {
    return $this->payload;
  }
  public function setResourceId($resourceId)
  {
    $this->resourceId = $resourceId;
  }
  public function getResourceId()
  {
    return $this->resourceId;
  }
  public function setResourceUri($resourceUri)
  {
    $this->resourceUri = $resourceUri;
  }
  public function getResourceUri()
  {
    return $this->resourceUri;
  }
  public function setToken($token)
  {
    $this->token = $token;
  }
  public function getToken()
  {
    return $this->token;
  }
  public function setType($type)
  {
    $this->type = $type;
  }
  public function getType()
  {
    return $this->type;
  }
}
includes/lib2/google-api-php-client/src/WPvivid/Google/Service/Drive/Resource/Changes.php000064400000013453151327705670025225 0ustar00<?php
/*
 * Copyright 2014 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

/**
 * The "changes" collection of methods.
 * Typical usage is:
 *  <code>
 *   $driveService = new Google_Service_Drive(...);
 *   $changes = $driveService->changes;
 *  </code>
 */
class WPvivid_Google_Service_Drive_Resource_Changes extends WPvivid_Google_Service_Resource
{
  /**
   * Gets the starting pageToken for listing future changes.
   * (changes.getStartPageToken)
   *
   * @param array $optParams Optional parameters.
   *
   * @opt_param bool supportsTeamDrives Whether the requesting application
   * supports Team Drives.
   * @opt_param string teamDriveId The ID of the Team Drive for which the starting
   * pageToken for listing future changes from that Team Drive will be returned.
   * @return WPvivid_Google_Service_Drive_StartPageToken
   */
  public function getStartPageToken($optParams = array())
  {
    $params = array();
    $params = array_merge($params, $optParams);
    return $this->call('getStartPageToken', array($params), "WPvivid_Google_Service_Drive_StartPageToken");
  }
  /**
   * Lists the changes for a user or Team Drive. (changes.listChanges)
   *
   * @param string $pageToken The token for continuing a previous list request on
   * the next page. This should be set to the value of 'nextPageToken' from the
   * previous response or to the response from the getStartPageToken method.
   * @param array $optParams Optional parameters.
   *
   * @opt_param bool includeCorpusRemovals Whether changes should include the file
   * resource if the file is still accessible by the user at the time of the
   * request, even when a file was removed from the list of changes and there will
   * be no further change entries for this file.
   * @opt_param bool includeRemoved Whether to include changes indicating that
   * items have been removed from the list of changes, for example by deletion or
   * loss of access.
   * @opt_param bool includeTeamDriveItems Whether Team Drive files or changes
   * should be included in results.
   * @opt_param int pageSize The maximum number of changes to return per page.
   * @opt_param bool restrictToMyDrive Whether to restrict the results to changes
   * inside the My Drive hierarchy. This omits changes to files such as those in
   * the Application Data folder or shared files which have not been added to My
   * Drive.
   * @opt_param string spaces A comma-separated list of spaces to query within the
   * user corpus. Supported values are 'drive', 'appDataFolder' and 'photos'.
   * @opt_param bool supportsTeamDrives Whether the requesting application
   * supports Team Drives.
   * @opt_param string teamDriveId The Team Drive from which changes will be
   * returned. If specified the change IDs will be reflective of the Team Drive;
   * use the combined Team Drive ID and change ID as an identifier.
   * @return WPvivid_Google_Service_Drive_ChangeList
   */
  public function listChanges($pageToken, $optParams = array())
  {
    $params = array('pageToken' => $pageToken);
    $params = array_merge($params, $optParams);
    return $this->call('list', array($params), "WPvivid_Google_Service_Drive_ChangeList");
  }
  /**
   * Subscribes to changes for a user. (changes.watch)
   *
   * @param string $pageToken The token for continuing a previous list request on
   * the next page. This should be set to the value of 'nextPageToken' from the
   * previous response or to the response from the getStartPageToken method.
   * @param WPvivid_Google_Service_Drive_Channel $postBody
   * @param array $optParams Optional parameters.
   *
   * @opt_param bool includeCorpusRemovals Whether changes should include the file
   * resource if the file is still accessible by the user at the time of the
   * request, even when a file was removed from the list of changes and there will
   * be no further change entries for this file.
   * @opt_param bool includeRemoved Whether to include changes indicating that
   * items have been removed from the list of changes, for example by deletion or
   * loss of access.
   * @opt_param bool includeTeamDriveItems Whether Team Drive files or changes
   * should be included in results.
   * @opt_param int pageSize The maximum number of changes to return per page.
   * @opt_param bool restrictToMyDrive Whether to restrict the results to changes
   * inside the My Drive hierarchy. This omits changes to files such as those in
   * the Application Data folder or shared files which have not been added to My
   * Drive.
   * @opt_param string spaces A comma-separated list of spaces to query within the
   * user corpus. Supported values are 'drive', 'appDataFolder' and 'photos'.
   * @opt_param bool supportsTeamDrives Whether the requesting application
   * supports Team Drives.
   * @opt_param string teamDriveId The Team Drive from which changes will be
   * returned. If specified the change IDs will be reflective of the Team Drive;
   * use the combined Team Drive ID and change ID as an identifier.
   * @return WPvivid_Google_Service_Drive_Channel
   */
  public function watch($pageToken, WPvivid_Google_Service_Drive_Channel $postBody, $optParams = array())
  {
    $params = array('pageToken' => $pageToken, 'postBody' => $postBody);
    $params = array_merge($params, $optParams);
    return $this->call('watch', array($params), "WPvivid_Google_Service_Drive_Channel");
  }
}
includes/lib2/google-api-php-client/src/WPvivid/Google/Service/Drive/Resource/Replies.php000064400000010744151327705670025260 0ustar00<?php
/*
 * Copyright 2014 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

/**
 * The "replies" collection of methods.
 * Typical usage is:
 *  <code>
 *   $driveService = new WPvivid_Google_Service_Drive(...);
 *   $replies = $driveService->replies;
 *  </code>
 */
class WPvivid_Google_Service_Drive_Resource_Replies extends WPvivid_Google_Service_Resource
{
  /**
   * Creates a new reply to a comment. (replies.create)
   *
   * @param string $fileId The ID of the file.
   * @param string $commentId The ID of the comment.
   * @param WPvivid_Google_Service_Drive_Reply $postBody
   * @param array $optParams Optional parameters.
   * @return WPvivid_Google_Service_Drive_Reply
   */
  public function create($fileId, $commentId, WPvivid_Google_Service_Drive_Reply $postBody, $optParams = array())
  {
    $params = array('fileId' => $fileId, 'commentId' => $commentId, 'postBody' => $postBody);
    $params = array_merge($params, $optParams);
    return $this->call('create', array($params), "WPvivid_Google_Service_Drive_Reply");
  }
  /**
   * Deletes a reply. (replies.delete)
   *
   * @param string $fileId The ID of the file.
   * @param string $commentId The ID of the comment.
   * @param string $replyId The ID of the reply.
   * @param array $optParams Optional parameters.
   */
  public function delete($fileId, $commentId, $replyId, $optParams = array())
  {
    $params = array('fileId' => $fileId, 'commentId' => $commentId, 'replyId' => $replyId);
    $params = array_merge($params, $optParams);
    return $this->call('delete', array($params));
  }
  /**
   * Gets a reply by ID. (replies.get)
   *
   * @param string $fileId The ID of the file.
   * @param string $commentId The ID of the comment.
   * @param string $replyId The ID of the reply.
   * @param array $optParams Optional parameters.
   *
   * @opt_param bool includeDeleted Whether to return deleted replies. Deleted
   * replies will not include their original content.
   * @return WPvivid_Google_Service_Drive_Reply
   */
  public function get($fileId, $commentId, $replyId, $optParams = array())
  {
    $params = array('fileId' => $fileId, 'commentId' => $commentId, 'replyId' => $replyId);
    $params = array_merge($params, $optParams);
    return $this->call('get', array($params), "WPvivid_Google_Service_Drive_Reply");
  }
  /**
   * Lists a comment's replies. (replies.listReplies)
   *
   * @param string $fileId The ID of the file.
   * @param string $commentId The ID of the comment.
   * @param array $optParams Optional parameters.
   *
   * @opt_param bool includeDeleted Whether to include deleted replies. Deleted
   * replies will not include their original content.
   * @opt_param int pageSize The maximum number of replies to return per page.
   * @opt_param string pageToken The token for continuing a previous list request
   * on the next page. This should be set to the value of 'nextPageToken' from the
   * previous response.
   * @return WPvivid_Google_Service_Drive_ReplyList
   */
  public function listReplies($fileId, $commentId, $optParams = array())
  {
    $params = array('fileId' => $fileId, 'commentId' => $commentId);
    $params = array_merge($params, $optParams);
    return $this->call('list', array($params), "WPvivid_Google_Service_Drive_ReplyList");
  }
  /**
   * Updates a reply with patch semantics. (replies.update)
   *
   * @param string $fileId The ID of the file.
   * @param string $commentId The ID of the comment.
   * @param string $replyId The ID of the reply.
   * @param WPvivid_Google_Service_Drive_Reply $postBody
   * @param array $optParams Optional parameters.
   * @return WPvivid_Google_Service_Drive_Reply
   */
  public function update($fileId, $commentId, $replyId, WPvivid_Google_Service_Drive_Reply $postBody, $optParams = array())
  {
    $params = array('fileId' => $fileId, 'commentId' => $commentId, 'replyId' => $replyId, 'postBody' => $postBody);
    $params = array_merge($params, $optParams);
    return $this->call('update', array($params), "WPvivid_Google_Service_Drive_Reply");
  }
}
includes/lib2/google-api-php-client/src/WPvivid/Google/Service/Drive/Resource/Permissions.php000064400000015261151327705670026167 0ustar00<?php
/*
 * Copyright 2014 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

/**
 * The "permissions" collection of methods.
 * Typical usage is:
 *  <code>
 *   $driveService = new Google_Service_Drive(...);
 *   $permissions = $driveService->permissions;
 *  </code>
 */
class WPvivid_Google_Service_Drive_Resource_Permissions extends WPvivid_Google_Service_Resource
{
  /**
   * Creates a permission for a file or Team Drive. (permissions.create)
   *
   * @param string $fileId The ID of the file or Team Drive.
   * @param WPvivid_Google_Service_Drive_Permission $postBody
   * @param array $optParams Optional parameters.
   *
   * @opt_param string emailMessage A plain text custom message to include in the
   * notification email.
   * @opt_param bool sendNotificationEmail Whether to send a notification email
   * when sharing to users or groups. This defaults to true for users and groups,
   * and is not allowed for other requests. It must not be disabled for ownership
   * transfers.
   * @opt_param bool supportsTeamDrives Whether the requesting application
   * supports Team Drives.
   * @opt_param bool transferOwnership Whether to transfer ownership to the
   * specified user and downgrade the current owner to a writer. This parameter is
   * required as an acknowledgement of the side effect.
   * @opt_param bool useDomainAdminAccess Issue the request as a domain
   * administrator; if set to true, then the requester will be granted access if
   * they are an administrator of the domain to which the item belongs.
   * @return WPvivid_Google_Service_Drive_Permission
   */
  public function create($fileId, WPvivid_Google_Service_Drive_Permission $postBody, $optParams = array())
  {
    $params = array('fileId' => $fileId, 'postBody' => $postBody);
    $params = array_merge($params, $optParams);
    return $this->call('create', array($params), "WPvivid_Google_Service_Drive_Permission");
  }
  /**
   * Deletes a permission. (permissions.delete)
   *
   * @param string $fileId The ID of the file or Team Drive.
   * @param string $permissionId The ID of the permission.
   * @param array $optParams Optional parameters.
   *
   * @opt_param bool supportsTeamDrives Whether the requesting application
   * supports Team Drives.
   * @opt_param bool useDomainAdminAccess Issue the request as a domain
   * administrator; if set to true, then the requester will be granted access if
   * they are an administrator of the domain to which the item belongs.
   */
  public function delete($fileId, $permissionId, $optParams = array())
  {
    $params = array('fileId' => $fileId, 'permissionId' => $permissionId);
    $params = array_merge($params, $optParams);
    return $this->call('delete', array($params));
  }
  /**
   * Gets a permission by ID. (permissions.get)
   *
   * @param string $fileId The ID of the file.
   * @param string $permissionId The ID of the permission.
   * @param array $optParams Optional parameters.
   *
   * @opt_param bool supportsTeamDrives Whether the requesting application
   * supports Team Drives.
   * @opt_param bool useDomainAdminAccess Issue the request as a domain
   * administrator; if set to true, then the requester will be granted access if
   * they are an administrator of the domain to which the item belongs.
   * @return WPvivid_Google_Service_Drive_Permission
   */
  public function get($fileId, $permissionId, $optParams = array())
  {
    $params = array('fileId' => $fileId, 'permissionId' => $permissionId);
    $params = array_merge($params, $optParams);
    return $this->call('get', array($params), "WPvivid_Google_Service_Drive_Permission");
  }
  /**
   * Lists a file's or Team Drive's permissions. (permissions.listPermissions)
   *
   * @param string $fileId The ID of the file or Team Drive.
   * @param array $optParams Optional parameters.
   *
   * @opt_param int pageSize The maximum number of permissions to return per page.
   * When not set for files in a Team Drive, at most 100 results will be returned.
   * When not set for files that are not in a Team Drive, the entire list will be
   * returned.
   * @opt_param string pageToken The token for continuing a previous list request
   * on the next page. This should be set to the value of 'nextPageToken' from the
   * previous response.
   * @opt_param bool supportsTeamDrives Whether the requesting application
   * supports Team Drives.
   * @opt_param bool useDomainAdminAccess Issue the request as a domain
   * administrator; if set to true, then the requester will be granted access if
   * they are an administrator of the domain to which the item belongs.
   * @return WPvivid_Google_Service_Drive_PermissionList
   */
  public function listPermissions($fileId, $optParams = array())
  {
    $params = array('fileId' => $fileId);
    $params = array_merge($params, $optParams);
    return $this->call('list', array($params), "WPvivid_Google_Service_Drive_PermissionList");
  }
  /**
   * Updates a permission with patch semantics. (permissions.update)
   *
   * @param string $fileId The ID of the file or Team Drive.
   * @param string $permissionId The ID of the permission.
   * @param WPvivid_Google_Service_Drive_Permission $postBody
   * @param array $optParams Optional parameters.
   *
   * @opt_param bool removeExpiration Whether to remove the expiration date.
   * @opt_param bool supportsTeamDrives Whether the requesting application
   * supports Team Drives.
   * @opt_param bool transferOwnership Whether to transfer ownership to the
   * specified user and downgrade the current owner to a writer. This parameter is
   * required as an acknowledgement of the side effect.
   * @opt_param bool useDomainAdminAccess Issue the request as a domain
   * administrator; if set to true, then the requester will be granted access if
   * they are an administrator of the domain to which the item belongs.
   * @return WPvivid_Google_Service_Drive_Permission
   */
  public function update($fileId, $permissionId, WPvivid_Google_Service_Drive_Permission $postBody, $optParams = array())
  {
    $params = array('fileId' => $fileId, 'permissionId' => $permissionId, 'postBody' => $postBody);
    $params = array_merge($params, $optParams);
    return $this->call('update', array($params), "WPvivid_Google_Service_Drive_Permission");
  }
}
includes/lib2/google-api-php-client/src/WPvivid/Google/Service/Drive/Resource/Revisions.php000064400000007111151327705670025630 0ustar00<?php
/*
 * Copyright 2014 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

/**
 * The "revisions" collection of methods.
 * Typical usage is:
 *  <code>
 *   $driveService = new WPvivid_Google_Service_Drive(...);
 *   $revisions = $driveService->revisions;
 *  </code>
 */
class WPvivid_Google_Service_Drive_Resource_Revisions extends WPvivid_Google_Service_Resource
{
  /**
   * Permanently deletes a revision. This method is only applicable to files with
   * binary content in Drive. (revisions.delete)
   *
   * @param string $fileId The ID of the file.
   * @param string $revisionId The ID of the revision.
   * @param array $optParams Optional parameters.
   */
  public function delete($fileId, $revisionId, $optParams = array())
  {
    $params = array('fileId' => $fileId, 'revisionId' => $revisionId);
    $params = array_merge($params, $optParams);
    return $this->call('delete', array($params));
  }
  /**
   * Gets a revision's metadata or content by ID. (revisions.get)
   *
   * @param string $fileId The ID of the file.
   * @param string $revisionId The ID of the revision.
   * @param array $optParams Optional parameters.
   *
   * @opt_param bool acknowledgeAbuse Whether the user is acknowledging the risk
   * of downloading known malware or other abusive files. This is only applicable
   * when alt=media.
   * @return WPvivid_Google_Service_Drive_Revision
   */
  public function get($fileId, $revisionId, $optParams = array())
  {
    $params = array('fileId' => $fileId, 'revisionId' => $revisionId);
    $params = array_merge($params, $optParams);
    return $this->call('get', array($params), "WPvivid_Google_Service_Drive_Revision");
  }
  /**
   * Lists a file's revisions. (revisions.listRevisions)
   *
   * @param string $fileId The ID of the file.
   * @param array $optParams Optional parameters.
   *
   * @opt_param int pageSize The maximum number of revisions to return per page.
   * @opt_param string pageToken The token for continuing a previous list request
   * on the next page. This should be set to the value of 'nextPageToken' from the
   * previous response.
   * @return WPvivid_Google_Service_Drive_RevisionList
   */
  public function listRevisions($fileId, $optParams = array())
  {
    $params = array('fileId' => $fileId);
    $params = array_merge($params, $optParams);
    return $this->call('list', array($params), "WPvivid_Google_Service_Drive_RevisionList");
  }
  /**
   * Updates a revision with patch semantics. (revisions.update)
   *
   * @param string $fileId The ID of the file.
   * @param string $revisionId The ID of the revision.
   * @param WPvivid_Google_Service_Drive_Revision $postBody
   * @param array $optParams Optional parameters.
   * @return WPvivid_Google_Service_Drive_Revision
   */
  public function update($fileId, $revisionId, WPvivid_Google_Service_Drive_Revision $postBody, $optParams = array())
  {
    $params = array('fileId' => $fileId, 'revisionId' => $revisionId, 'postBody' => $postBody);
    $params = array_merge($params, $optParams);
    return $this->call('update', array($params), "WPvivid_Google_Service_Drive_Revision");
  }
}
includes/lib2/google-api-php-client/src/WPvivid/Google/Service/Drive/Resource/Files.php000064400000025715151327705670024723 0ustar00<?php
/*
 * Copyright 2014 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

/**
 * The "files" collection of methods.
 * Typical usage is:
 *  <code>
 *   $driveService = new WPvivid_Google_Service_Drive(...);
 *   $files = $driveService->files;
 *  </code>
 */
class WPvivid_Google_Service_Drive_Resource_Files extends WPvivid_Google_Service_Resource
{
  /**
   * Creates a copy of a file and applies any requested updates with patch
   * semantics. (files.copy)
   *
   * @param string $fileId The ID of the file.
   * @param WPvivid_Google_Service_Drive_DriveFile $postBody
   * @param array $optParams Optional parameters.
   *
   * @opt_param bool ignoreDefaultVisibility Whether to ignore the domain's
   * default visibility settings for the created file. Domain administrators can
   * choose to make all uploaded files visible to the domain by default; this
   * parameter bypasses that behavior for the request. Permissions are still
   * inherited from parent folders.
   * @opt_param bool keepRevisionForever Whether to set the 'keepForever' field in
   * the new head revision. This is only applicable to files with binary content
   * in Drive.
   * @opt_param string ocrLanguage A language hint for OCR processing during image
   * import (ISO 639-1 code).
   * @opt_param bool supportsTeamDrives Whether the requesting application
   * supports Team Drives.
   * @return WPvivid_Google_Service_Drive_DriveFile
   */
  public function copy($fileId, WPvivid_Google_Service_Drive_DriveFile $postBody, $optParams = array())
  {
    $params = array('fileId' => $fileId, 'postBody' => $postBody);
    $params = array_merge($params, $optParams);
    return $this->call('copy', array($params), "WPvivid_Google_Service_Drive_DriveFile");
  }
  /**
   * Creates a new file. (files.create)
   *
   * @param WPvivid_Google_Service_Drive_DriveFile $postBody
   * @param array $optParams Optional parameters.
   *
   * @opt_param bool ignoreDefaultVisibility Whether to ignore the domain's
   * default visibility settings for the created file. Domain administrators can
   * choose to make all uploaded files visible to the domain by default; this
   * parameter bypasses that behavior for the request. Permissions are still
   * inherited from parent folders.
   * @opt_param bool keepRevisionForever Whether to set the 'keepForever' field in
   * the new head revision. This is only applicable to files with binary content
   * in Drive.
   * @opt_param string ocrLanguage A language hint for OCR processing during image
   * import (ISO 639-1 code).
   * @opt_param bool supportsTeamDrives Whether the requesting application
   * supports Team Drives.
   * @opt_param bool useContentAsIndexableText Whether to use the uploaded content
   * as indexable text.
   * @return WPvivid_Google_Service_Drive_DriveFile
   */
  public function create(WPvivid_Google_Service_Drive_DriveFile $postBody, $optParams = array())
  {
    $params = array('postBody' => $postBody);
    $params = array_merge($params, $optParams);
    return $this->call('create', array($params), "WPvivid_Google_Service_Drive_DriveFile");
  }
  /**
   * Permanently deletes a file owned by the user without moving it to the trash.
   * If the file belongs to a Team Drive the user must be an organizer on the
   * parent. If the target is a folder, all descendants owned by the user are also
   * deleted. (files.delete)
   *
   * @param string $fileId The ID of the file.
   * @param array $optParams Optional parameters.
   *
   * @opt_param bool supportsTeamDrives Whether the requesting application
   * supports Team Drives.
   */
  public function delete($fileId, $optParams = array())
  {
    $params = array('fileId' => $fileId);
    $params = array_merge($params, $optParams);
    return $this->call('delete', array($params));
  }
  /**
   * Permanently deletes all of the user's trashed files. (files.emptyTrash)
   *
   * @param array $optParams Optional parameters.
   */
  public function emptyTrash($optParams = array())
  {
    $params = array();
    $params = array_merge($params, $optParams);
    return $this->call('emptyTrash', array($params));
  }
  /**
   * Exports a Google Doc to the requested MIME type and returns the exported
   * content. Please note that the exported content is limited to 10MB.
   * (files.export)
   *
   * @param string $fileId The ID of the file.
   * @param string $mimeType The MIME type of the format requested for this
   * export.
   * @param array $optParams Optional parameters.
   */
  public function export($fileId, $mimeType, $optParams = array())
  {
    $params = array('fileId' => $fileId, 'mimeType' => $mimeType);
    $params = array_merge($params, $optParams);
    return $this->call('export', array($params));
  }
  /**
   * Generates a set of file IDs which can be provided in create requests.
   * (files.generateIds)
   *
   * @param array $optParams Optional parameters.
   *
   * @opt_param int count The number of IDs to return.
   * @opt_param string space The space in which the IDs can be used to create new
   * files. Supported values are 'drive' and 'appDataFolder'.
   * @return WPvivid_Google_Service_Drive_GeneratedIds
   */
  public function generateIds($optParams = array())
  {
    $params = array();
    $params = array_merge($params, $optParams);
    return $this->call('generateIds', array($params), "WPvivid_Google_Service_Drive_GeneratedIds");
  }
  /**
   * Gets a file's metadata or content by ID. (files.get)
   *
   * @param string $fileId The ID of the file.
   * @param array $optParams Optional parameters.
   *
   * @opt_param bool acknowledgeAbuse Whether the user is acknowledging the risk
   * of downloading known malware or other abusive files. This is only applicable
   * when alt=media.
   * @opt_param bool supportsTeamDrives Whether the requesting application
   * supports Team Drives.
   * @return WPvivid_Google_Service_Drive_DriveFile
   */
  public function get($fileId, $optParams = array())
  {
    $params = array('fileId' => $fileId);
    $params = array_merge($params, $optParams);
    return $this->call('get', array($params), "WPvivid_Google_Service_Drive_DriveFile");
  }
  /**
   * Lists or searches files. (files.listFiles)
   *
   * @param array $optParams Optional parameters.
   *
   * @opt_param string corpora Comma-separated list of bodies of items
   * (files/documents) to which the query applies. Supported bodies are 'user',
   * 'domain', 'teamDrive' and 'allTeamDrives'. 'allTeamDrives' must be combined
   * with 'user'; all other values must be used in isolation. Prefer 'user' or
   * 'teamDrive' to 'allTeamDrives' for efficiency.
   * @opt_param string corpus The source of files to list. Deprecated: use
   * 'corpora' instead.
   * @opt_param bool includeTeamDriveItems Whether Team Drive items should be
   * included in results.
   * @opt_param string orderBy A comma-separated list of sort keys. Valid keys are
   * 'createdTime', 'folder', 'modifiedByMeTime', 'modifiedTime', 'name',
   * 'name_natural', 'quotaBytesUsed', 'recency', 'sharedWithMeTime', 'starred',
   * and 'viewedByMeTime'. Each key sorts ascending by default, but may be
   * reversed with the 'desc' modifier. Example usage:
   * ?orderBy=folder,modifiedTime desc,name. Please note that there is a current
   * limitation for users with approximately one million files in which the
   * requested sort order is ignored.
   * @opt_param int pageSize The maximum number of files to return per page.
   * Partial or empty result pages are possible even before the end of the files
   * list has been reached.
   * @opt_param string pageToken The token for continuing a previous list request
   * on the next page. This should be set to the value of 'nextPageToken' from the
   * previous response.
   * @opt_param string q A query for filtering the file results. See the "Search
   * for Files" guide for supported syntax.
   * @opt_param string spaces A comma-separated list of spaces to query within the
   * corpus. Supported values are 'drive', 'appDataFolder' and 'photos'.
   * @opt_param bool supportsTeamDrives Whether the requesting application
   * supports Team Drives.
   * @opt_param string teamDriveId ID of Team Drive to search.
   * @return WPvivid_Google_Service_Drive_FileList
   */
  public function listFiles($optParams = array())
  {
    $params = array();
    $params = array_merge($params, $optParams);
    return $this->call('list', array($params), "WPvivid_Google_Service_Drive_FileList");
  }
  /**
   * Updates a file's metadata and/or content with patch semantics. (files.update)
   *
   * @param string $fileId The ID of the file.
   * @param WPvivid_Google_Service_Drive_DriveFile $postBody
   * @param array $optParams Optional parameters.
   *
   * @opt_param string addParents A comma-separated list of parent IDs to add.
   * @opt_param bool keepRevisionForever Whether to set the 'keepForever' field in
   * the new head revision. This is only applicable to files with binary content
   * in Drive.
   * @opt_param string ocrLanguage A language hint for OCR processing during image
   * import (ISO 639-1 code).
   * @opt_param string removeParents A comma-separated list of parent IDs to
   * remove.
   * @opt_param bool supportsTeamDrives Whether the requesting application
   * supports Team Drives.
   * @opt_param bool useContentAsIndexableText Whether to use the uploaded content
   * as indexable text.
   * @return WPvivid_Google_Service_Drive_DriveFile
   */
  public function update($fileId, WPvivid_Google_Service_Drive_DriveFile $postBody, $optParams = array())
  {
    $params = array('fileId' => $fileId, 'postBody' => $postBody);
    $params = array_merge($params, $optParams);
    return $this->call('update', array($params), "WPvivid_Google_Service_Drive_DriveFile");
  }
  /**
   * Subscribes to changes to a file (files.watch)
   *
   * @param string $fileId The ID of the file.
   * @param WPvivid_Google_Service_Drive_Channel $postBody
   * @param array $optParams Optional parameters.
   *
   * @opt_param bool acknowledgeAbuse Whether the user is acknowledging the risk
   * of downloading known malware or other abusive files. This is only applicable
   * when alt=media.
   * @opt_param bool supportsTeamDrives Whether the requesting application
   * supports Team Drives.
   * @return WPvivid_Google_Service_Drive_Channel
   */
  public function watch($fileId, WPvivid_Google_Service_Drive_Channel $postBody, $optParams = array())
  {
    $params = array('fileId' => $fileId, 'postBody' => $postBody);
    $params = array_merge($params, $optParams);
    return $this->call('watch', array($params), "WPvivid_Google_Service_Drive_Channel");
  }
}
includes/lib2/google-api-php-client/src/WPvivid/Google/Service/Drive/Resource/Comments.php000064400000010341151327705670025433 0ustar00<?php
/*
 * Copyright 2014 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

/**
 * The "comments" collection of methods.
 * Typical usage is:
 *  <code>
 *   $driveService = new WPvivid_Google_Service_Drive(...);
 *   $comments = $driveService->comments;
 *  </code>
 */
class WPvivid_Google_Service_Drive_Resource_Comments extends WPvivid_Google_Service_Resource
{
  /**
   * Creates a new comment on a file. (comments.create)
   *
   * @param string $fileId The ID of the file.
   * @param WPvivid_Google_Service_Drive_Comment $postBody
   * @param array $optParams Optional parameters.
   * @return WPvivid_Google_Service_Drive_Comment
   */
  public function create($fileId, WPvivid_Google_Service_Drive_Comment $postBody, $optParams = array())
  {
    $params = array('fileId' => $fileId, 'postBody' => $postBody);
    $params = array_merge($params, $optParams);
    return $this->call('create', array($params), "WPvivid_Google_Service_Drive_Comment");
  }
  /**
   * Deletes a comment. (comments.delete)
   *
   * @param string $fileId The ID of the file.
   * @param string $commentId The ID of the comment.
   * @param array $optParams Optional parameters.
   */
  public function delete($fileId, $commentId, $optParams = array())
  {
    $params = array('fileId' => $fileId, 'commentId' => $commentId);
    $params = array_merge($params, $optParams);
    return $this->call('delete', array($params));
  }
  /**
   * Gets a comment by ID. (comments.get)
   *
   * @param string $fileId The ID of the file.
   * @param string $commentId The ID of the comment.
   * @param array $optParams Optional parameters.
   *
   * @opt_param bool includeDeleted Whether to return deleted comments. Deleted
   * comments will not include their original content.
   * @return WPvivid_Google_Service_Drive_Comment
   */
  public function get($fileId, $commentId, $optParams = array())
  {
    $params = array('fileId' => $fileId, 'commentId' => $commentId);
    $params = array_merge($params, $optParams);
    return $this->call('get', array($params), "WPvivid_Google_Service_Drive_Comment");
  }
  /**
   * Lists a file's comments. (comments.listComments)
   *
   * @param string $fileId The ID of the file.
   * @param array $optParams Optional parameters.
   *
   * @opt_param bool includeDeleted Whether to include deleted comments. Deleted
   * comments will not include their original content.
   * @opt_param int pageSize The maximum number of comments to return per page.
   * @opt_param string pageToken The token for continuing a previous list request
   * on the next page. This should be set to the value of 'nextPageToken' from the
   * previous response.
   * @opt_param string startModifiedTime The minimum value of 'modifiedTime' for
   * the result comments (RFC 3339 date-time).
   * @return WPvivid_Google_Service_Drive_CommentList
   */
  public function listComments($fileId, $optParams = array())
  {
    $params = array('fileId' => $fileId);
    $params = array_merge($params, $optParams);
    return $this->call('list', array($params), "WPvivid_Google_Service_Drive_CommentList");
  }
  /**
   * Updates a comment with patch semantics. (comments.update)
   *
   * @param string $fileId The ID of the file.
   * @param string $commentId The ID of the comment.
   * @param WPvivid_Google_Service_Drive_Comment $postBody
   * @param array $optParams Optional parameters.
   * @return WPvivid_Google_Service_Drive_Comment
   */
  public function update($fileId, $commentId, WPvivid_Google_Service_Drive_Comment $postBody, $optParams = array())
  {
    $params = array('fileId' => $fileId, 'commentId' => $commentId, 'postBody' => $postBody);
    $params = array_merge($params, $optParams);
    return $this->call('update', array($params), "WPvivid_Google_Service_Drive_Comment");
  }
}
includes/lib2/google-api-php-client/src/WPvivid/Google/Service/Drive/Resource/About.php000064400000002413151327705670024721 0ustar00<?php
/*
 * Copyright 2014 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

/**
 * The "about" collection of methods.
 * Typical usage is:
 *  <code>
 *   $driveService = new Google_Service_Drive(...);
 *   $about = $driveService->about;
 *  </code>
 */
class WPvivid_Google_Service_Drive_Resource_About extends WPvivid_Google_Service_Resource
{
  /**
   * Gets information about the user, the user's Drive, and system capabilities.
   * (about.get)
   *
   * @param array $optParams Optional parameters.
   * @return WPvivid_Google_Service_Drive_About
   */
  public function get($optParams = array())
  {
    $params = array();
    $params = array_merge($params, $optParams);
    return $this->call('get', array($params), "WPvivid_Google_Service_Drive_About");
  }
}
includes/lib2/google-api-php-client/src/WPvivid/Google/Service/Drive/Resource/Teamdrives.php000064400000011176151327705670025760 0ustar00<?php
/*
 * Copyright 2014 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

/**
 * The "teamdrives" collection of methods.
 * Typical usage is:
 *  <code>
 *   $driveService = new WPvivid_Google_Service_Drive(...);
 *   $teamdrives = $driveService->teamdrives;
 *  </code>
 */
class WPvivid_Google_Service_Drive_Resource_Teamdrives extends WPvivid_Google_Service_Resource
{
  /**
   * Creates a new Team Drive. (teamdrives.create)
   *
   * @param string $requestId An ID, such as a random UUID, which uniquely
   * identifies this user's request for idempotent creation of a Team Drive. A
   * repeated request by the same user and with the same request ID will avoid
   * creating duplicates by attempting to create the same Team Drive. If the Team
   * Drive already exists a 409 error will be returned.
   * @param WPvivid_Google_Service_Drive_TeamDrive $postBody
   * @param array $optParams Optional parameters.
   * @return WPvivid_Google_Service_Drive_TeamDrive
   */
  public function create($requestId, WPvivid_Google_Service_Drive_TeamDrive $postBody, $optParams = array())
  {
    $params = array('requestId' => $requestId, 'postBody' => $postBody);
    $params = array_merge($params, $optParams);
    return $this->call('create', array($params), "WPvivid_Google_Service_Drive_TeamDrive");
  }
  /**
   * Permanently deletes a Team Drive for which the user is an organizer. The Team
   * Drive cannot contain any untrashed items. (teamdrives.delete)
   *
   * @param string $teamDriveId The ID of the Team Drive
   * @param array $optParams Optional parameters.
   */
  public function delete($teamDriveId, $optParams = array())
  {
    $params = array('teamDriveId' => $teamDriveId);
    $params = array_merge($params, $optParams);
    return $this->call('delete', array($params));
  }
  /**
   * Gets a Team Drive's metadata by ID. (teamdrives.get)
   *
   * @param string $teamDriveId The ID of the Team Drive
   * @param array $optParams Optional parameters.
   *
   * @opt_param bool useDomainAdminAccess Issue the request as a domain
   * administrator; if set to true, then the requester will be granted access if
   * they are an administrator of the domain to which the Team Drive belongs.
   * @return WPvivid_Google_Service_Drive_TeamDrive
   */
  public function get($teamDriveId, $optParams = array())
  {
    $params = array('teamDriveId' => $teamDriveId);
    $params = array_merge($params, $optParams);
    return $this->call('get', array($params), "WPvivid_Google_Service_Drive_TeamDrive");
  }
  /**
   * Lists the user's Team Drives. (teamdrives.listTeamdrives)
   *
   * @param array $optParams Optional parameters.
   *
   * @opt_param int pageSize Maximum number of Team Drives to return.
   * @opt_param string pageToken Page token for Team Drives.
   * @opt_param string q Query string for searching Team Drives.
   * @opt_param bool useDomainAdminAccess Issue the request as a domain
   * administrator; if set to true, then all Team Drives of the domain in which
   * the requester is an administrator are returned.
   * @return WPvivid_Google_Service_Drive_TeamDriveList
   */
  public function listTeamdrives($optParams = array())
  {
    $params = array();
    $params = array_merge($params, $optParams);
    return $this->call('list', array($params), "WPvivid_Google_Service_Drive_TeamDriveList");
  }
  /**
   * Updates a Team Drive's metadata (teamdrives.update)
   *
   * @param string $teamDriveId The ID of the Team Drive
   * @param WPvivid_Google_Service_Drive_TeamDrive $postBody
   * @param array $optParams Optional parameters.
   *
   * @opt_param bool useDomainAdminAccess Issue the request as a domain
   * administrator; if set to true, then the requester will be granted access if
   * they are an administrator of the domain to which the Team Drive belongs.
   * @return WPvivid_Google_Service_Drive_TeamDrive
   */
  public function update($teamDriveId, WPvivid_Google_Service_Drive_TeamDrive $postBody, $optParams = array())
  {
    $params = array('teamDriveId' => $teamDriveId, 'postBody' => $postBody);
    $params = array_merge($params, $optParams);
    return $this->call('update', array($params), "WPvivid_Google_Service_Drive_TeamDrive");
  }
}
includes/lib2/google-api-php-client/src/WPvivid/Google/Service/Drive/Resource/Channels.php000064400000002455151327705670025410 0ustar00<?php
/*
 * Copyright 2014 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

/**
 * The "channels" collection of methods.
 * Typical usage is:
 *  <code>
 *   $driveService = new WPvivid_Google_Service_Drive(...);
 *   $channels = $driveService->channels;
 *  </code>
 */
class WPvivid_Google_Service_Drive_Resource_Channels extends WPvivid_Google_Service_Resource
{
  /**
   * Stop watching resources through this channel (channels.stop)
   *
   * @param WPvivid_Google_Service_Drive_Channel $postBody
   * @param array $optParams Optional parameters.
   */
  public function stop(WPvivid_Google_Service_Drive_Channel $postBody, $optParams = array())
  {
    $params = array('postBody' => $postBody);
    $params = array_merge($params, $optParams);
    return $this->call('stop', array($params));
  }
}
includes/lib2/google-api-php-client/src/WPvivid/Google/Service/Drive/AboutStorageQuota.php000064400000002606151327705670025475 0ustar00<?php
/*
 * Copyright 2014 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

class WPvivid_Google_Service_Drive_AboutStorageQuota extends WPvivid_Google_Model
{
  public $limit;
  public $usage;
  public $usageInDrive;
  public $usageInDriveTrash;

  public function setLimit($limit)
  {
    $this->limit = $limit;
  }
  public function getLimit()
  {
    return $this->limit;
  }
  public function setUsage($usage)
  {
    $this->usage = $usage;
  }
  public function getUsage()
  {
    return $this->usage;
  }
  public function setUsageInDrive($usageInDrive)
  {
    $this->usageInDrive = $usageInDrive;
  }
  public function getUsageInDrive()
  {
    return $this->usageInDrive;
  }
  public function setUsageInDriveTrash($usageInDriveTrash)
  {
    $this->usageInDriveTrash = $usageInDriveTrash;
  }
  public function getUsageInDriveTrash()
  {
    return $this->usageInDriveTrash;
  }
}
includes/lib2/google-api-php-client/src/WPvivid/Google/Service/Drive/DriveFileImageMediaMetadata.php000064400000011346151327705670027302 0ustar00<?php
/*
 * Copyright 2014 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

class WPvivid_Google_Service_Drive_DriveFileImageMediaMetadata extends WPvivid_Google_Model
{
  public $aperture;
  public $cameraMake;
  public $cameraModel;
  public $colorSpace;
  public $exposureBias;
  public $exposureMode;
  public $exposureTime;
  public $flashUsed;
  public $focalLength;
  public $height;
  public $isoSpeed;
  public $lens;
  protected $locationType = 'WPvivid_Google_Service_Drive_DriveFileImageMediaMetadataLocation';
  protected $locationDataType = '';
  public $maxApertureValue;
  public $meteringMode;
  public $rotation;
  public $sensor;
  public $subjectDistance;
  public $time;
  public $whiteBalance;
  public $width;

  public function setAperture($aperture)
  {
    $this->aperture = $aperture;
  }
  public function getAperture()
  {
    return $this->aperture;
  }
  public function setCameraMake($cameraMake)
  {
    $this->cameraMake = $cameraMake;
  }
  public function getCameraMake()
  {
    return $this->cameraMake;
  }
  public function setCameraModel($cameraModel)
  {
    $this->cameraModel = $cameraModel;
  }
  public function getCameraModel()
  {
    return $this->cameraModel;
  }
  public function setColorSpace($colorSpace)
  {
    $this->colorSpace = $colorSpace;
  }
  public function getColorSpace()
  {
    return $this->colorSpace;
  }
  public function setExposureBias($exposureBias)
  {
    $this->exposureBias = $exposureBias;
  }
  public function getExposureBias()
  {
    return $this->exposureBias;
  }
  public function setExposureMode($exposureMode)
  {
    $this->exposureMode = $exposureMode;
  }
  public function getExposureMode()
  {
    return $this->exposureMode;
  }
  public function setExposureTime($exposureTime)
  {
    $this->exposureTime = $exposureTime;
  }
  public function getExposureTime()
  {
    return $this->exposureTime;
  }
  public function setFlashUsed($flashUsed)
  {
    $this->flashUsed = $flashUsed;
  }
  public function getFlashUsed()
  {
    return $this->flashUsed;
  }
  public function setFocalLength($focalLength)
  {
    $this->focalLength = $focalLength;
  }
  public function getFocalLength()
  {
    return $this->focalLength;
  }
  public function setHeight($height)
  {
    $this->height = $height;
  }
  public function getHeight()
  {
    return $this->height;
  }
  public function setIsoSpeed($isoSpeed)
  {
    $this->isoSpeed = $isoSpeed;
  }
  public function getIsoSpeed()
  {
    return $this->isoSpeed;
  }
  public function setLens($lens)
  {
    $this->lens = $lens;
  }
  public function getLens()
  {
    return $this->lens;
  }
  /**
   * @param WPvivid_Google_Service_Drive_DriveFileImageMediaMetadataLocation
   */
  public function setLocation(WPvivid_Google_Service_Drive_DriveFileImageMediaMetadataLocation $location)
  {
    $this->location = $location;
  }
  /**
   * @return WPvivid_Google_Service_Drive_DriveFileImageMediaMetadataLocation
   */
  public function getLocation()
  {
    return $this->location;
  }
  public function setMaxApertureValue($maxApertureValue)
  {
    $this->maxApertureValue = $maxApertureValue;
  }
  public function getMaxApertureValue()
  {
    return $this->maxApertureValue;
  }
  public function setMeteringMode($meteringMode)
  {
    $this->meteringMode = $meteringMode;
  }
  public function getMeteringMode()
  {
    return $this->meteringMode;
  }
  public function setRotation($rotation)
  {
    $this->rotation = $rotation;
  }
  public function getRotation()
  {
    return $this->rotation;
  }
  public function setSensor($sensor)
  {
    $this->sensor = $sensor;
  }
  public function getSensor()
  {
    return $this->sensor;
  }
  public function setSubjectDistance($subjectDistance)
  {
    $this->subjectDistance = $subjectDistance;
  }
  public function getSubjectDistance()
  {
    return $this->subjectDistance;
  }
  public function setTime($time)
  {
    $this->time = $time;
  }
  public function getTime()
  {
    return $this->time;
  }
  public function setWhiteBalance($whiteBalance)
  {
    $this->whiteBalance = $whiteBalance;
  }
  public function getWhiteBalance()
  {
    return $this->whiteBalance;
  }
  public function setWidth($width)
  {
    $this->width = $width;
  }
  public function getWidth()
  {
    return $this->width;
  }
}
includes/lib2/google-api-php-client/src/WPvivid/Google/Service/Drive/RevisionList.php000064400000002674151327705670024523 0ustar00<?php
/*
 * Copyright 2014 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

class WPvivid_Google_Service_Drive_RevisionList extends WPvivid_Google_Collection
{
  protected $collection_key = 'revisions';
  public $kind;
  public $nextPageToken;
  protected $revisionsType = 'WPvivid_Google_Service_Drive_Revision';
  protected $revisionsDataType = 'array';

  public function setKind($kind)
  {
    $this->kind = $kind;
  }
  public function getKind()
  {
    return $this->kind;
  }
  public function setNextPageToken($nextPageToken)
  {
    $this->nextPageToken = $nextPageToken;
  }
  public function getNextPageToken()
  {
    return $this->nextPageToken;
  }
  /**
   * @param WPvivid_Google_Service_Drive_Revision
   */
  public function setRevisions($revisions)
  {
    $this->revisions = $revisions;
  }
  /**
   * @return WPvivid_Google_Service_Drive_Revision
   */
  public function getRevisions()
  {
    return $this->revisions;
  }
}
includes/lib2/google-api-php-client/src/WPvivid/Google/Service/Drive/Comment.php000064400000006713151327705670023471 0ustar00<?php
/*
 * Copyright 2014 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

class WPvivid_Google_Service_Drive_Comment extends WPvivid_Google_Collection
{
  protected $collection_key = 'replies';
  public $anchor;
  protected $authorType = 'WPvivid_Google_Service_Drive_User';
  protected $authorDataType = '';
  public $content;
  public $createdTime;
  public $deleted;
  public $htmlContent;
  public $id;
  public $kind;
  public $modifiedTime;
  protected $quotedFileContentType = 'WPvivid_Google_Service_Drive_CommentQuotedFileContent';
  protected $quotedFileContentDataType = '';
  protected $repliesType = 'WPvivid_Google_Service_Drive_Reply';
  protected $repliesDataType = 'array';
  public $resolved;

  public function setAnchor($anchor)
  {
    $this->anchor = $anchor;
  }
  public function getAnchor()
  {
    return $this->anchor;
  }
  /**
   * @param WPvivid_Google_Service_Drive_User
   */
  public function setAuthor(WPvivid_Google_Service_Drive_User $author)
  {
    $this->author = $author;
  }
  /**
   * @return WPvivid_Google_Service_Drive_User
   */
  public function getAuthor()
  {
    return $this->author;
  }
  public function setContent($content)
  {
    $this->content = $content;
  }
  public function getContent()
  {
    return $this->content;
  }
  public function setCreatedTime($createdTime)
  {
    $this->createdTime = $createdTime;
  }
  public function getCreatedTime()
  {
    return $this->createdTime;
  }
  public function setDeleted($deleted)
  {
    $this->deleted = $deleted;
  }
  public function getDeleted()
  {
    return $this->deleted;
  }
  public function setHtmlContent($htmlContent)
  {
    $this->htmlContent = $htmlContent;
  }
  public function getHtmlContent()
  {
    return $this->htmlContent;
  }
  public function setId($id)
  {
    $this->id = $id;
  }
  public function getId()
  {
    return $this->id;
  }
  public function setKind($kind)
  {
    $this->kind = $kind;
  }
  public function getKind()
  {
    return $this->kind;
  }
  public function setModifiedTime($modifiedTime)
  {
    $this->modifiedTime = $modifiedTime;
  }
  public function getModifiedTime()
  {
    return $this->modifiedTime;
  }
  /**
   * @param WPvivid_Google_Service_Drive_CommentQuotedFileContent
   */
  public function setQuotedFileContent(WPvivid_Google_Service_Drive_CommentQuotedFileContent $quotedFileContent)
  {
    $this->quotedFileContent = $quotedFileContent;
  }
  /**
   * @return WPvivid_Google_Service_Drive_CommentQuotedFileContent
   */
  public function getQuotedFileContent()
  {
    return $this->quotedFileContent;
  }
  /**
   * @param WPvivid_Google_Service_Drive_Reply
   */
  public function setReplies($replies)
  {
    $this->replies = $replies;
  }
  /**
   * @return WPvivid_Google_Service_Drive_Reply
   */
  public function getReplies()
  {
    return $this->replies;
  }
  public function setResolved($resolved)
  {
    $this->resolved = $resolved;
  }
  public function getResolved()
  {
    return $this->resolved;
  }
}
lib2/google-api-php-client/src/WPvivid/Google/Service/Drive/PermissionTeamDrivePermissionDetails.php000064400000002737151327705670031322 0ustar00includes<?php
/*
 * Copyright 2014 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

class WPvivid_Google_Service_Drive_PermissionTeamDrivePermissionDetails extends WPvivid_Google_Model
{
  public $inherited;
  public $inheritedFrom;
  public $role;
  public $teamDrivePermissionType;

  public function setInherited($inherited)
  {
    $this->inherited = $inherited;
  }
  public function getInherited()
  {
    return $this->inherited;
  }
  public function setInheritedFrom($inheritedFrom)
  {
    $this->inheritedFrom = $inheritedFrom;
  }
  public function getInheritedFrom()
  {
    return $this->inheritedFrom;
  }
  public function setRole($role)
  {
    $this->role = $role;
  }
  public function getRole()
  {
    return $this->role;
  }
  public function setTeamDrivePermissionType($teamDrivePermissionType)
  {
    $this->teamDrivePermissionType = $teamDrivePermissionType;
  }
  public function getTeamDrivePermissionType()
  {
    return $this->teamDrivePermissionType;
  }
}
includes/lib2/google-api-php-client/src/WPvivid/Google/Service/Drive/About.php000064400000007173151327705670023142 0ustar00<?php
/*
 * Copyright 2014 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

class WPvivid_Google_Service_Drive_About extends WPvivid_Google_Collection
{
  protected $collection_key = 'teamDriveThemes';
  public $appInstalled;
  public $canCreateTeamDrives;
  public $exportFormats;
  public $folderColorPalette;
  public $importFormats;
  public $kind;
  public $maxImportSizes;
  public $maxUploadSize;
  protected $storageQuotaType = 'WPvivid_Google_Service_Drive_AboutStorageQuota';
  protected $storageQuotaDataType = '';
  protected $teamDriveThemesType = 'WPvivid_Google_Service_Drive_AboutTeamDriveThemes';
  protected $teamDriveThemesDataType = 'array';
  protected $userType = 'WPvivid_Google_Service_Drive_User';
  protected $userDataType = '';

  public function setAppInstalled($appInstalled)
  {
    $this->appInstalled = $appInstalled;
  }
  public function getAppInstalled()
  {
    return $this->appInstalled;
  }
  public function setCanCreateTeamDrives($canCreateTeamDrives)
  {
    $this->canCreateTeamDrives = $canCreateTeamDrives;
  }
  public function getCanCreateTeamDrives()
  {
    return $this->canCreateTeamDrives;
  }
  public function setExportFormats($exportFormats)
  {
    $this->exportFormats = $exportFormats;
  }
  public function getExportFormats()
  {
    return $this->exportFormats;
  }
  public function setFolderColorPalette($folderColorPalette)
  {
    $this->folderColorPalette = $folderColorPalette;
  }
  public function getFolderColorPalette()
  {
    return $this->folderColorPalette;
  }
  public function setImportFormats($importFormats)
  {
    $this->importFormats = $importFormats;
  }
  public function getImportFormats()
  {
    return $this->importFormats;
  }
  public function setKind($kind)
  {
    $this->kind = $kind;
  }
  public function getKind()
  {
    return $this->kind;
  }
  public function setMaxImportSizes($maxImportSizes)
  {
    $this->maxImportSizes = $maxImportSizes;
  }
  public function getMaxImportSizes()
  {
    return $this->maxImportSizes;
  }
  public function setMaxUploadSize($maxUploadSize)
  {
    $this->maxUploadSize = $maxUploadSize;
  }
  public function getMaxUploadSize()
  {
    return $this->maxUploadSize;
  }
  /**
   * @param WPvivid_Google_Service_Drive_AboutStorageQuota
   */
  public function setStorageQuota(WPvivid_Google_Service_Drive_AboutStorageQuota $storageQuota)
  {
    $this->storageQuota = $storageQuota;
  }
  /**
   * @return WPvivid_Google_Service_Drive_AboutStorageQuota
   */
  public function getStorageQuota()
  {
    return $this->storageQuota;
  }
  /**
   * @param WPvivid_Google_Service_Drive_AboutTeamDriveThemes
   */
  public function setTeamDriveThemes($teamDriveThemes)
  {
    $this->teamDriveThemes = $teamDriveThemes;
  }
  /**
   * @return WPvivid_Google_Service_Drive_AboutTeamDriveThemes
   */
  public function getTeamDriveThemes()
  {
    return $this->teamDriveThemes;
  }
  /**
   * @param WPvivid_Service_Drive_User
   */
  public function setUser(WPvivid_Google_Service_Drive_User $user)
  {
    $this->user = $user;
  }
  /**
   * @return WPvivid_Google_Service_Drive_User
   */
  public function getUser()
  {
    return $this->user;
  }
}
includes/lib2/google-api-php-client/src/WPvivid/Google/Service/Drive/ReplyList.php000064400000002636151327705670024016 0ustar00<?php
/*
 * Copyright 2014 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

class WPvivid_Google_Service_Drive_ReplyList extends WPvivid_Google_Collection
{
  protected $collection_key = 'replies';
  public $kind;
  public $nextPageToken;
  protected $repliesType = 'WPvivid_Google_Service_Drive_Reply';
  protected $repliesDataType = 'array';

  public function setKind($kind)
  {
    $this->kind = $kind;
  }
  public function getKind()
  {
    return $this->kind;
  }
  public function setNextPageToken($nextPageToken)
  {
    $this->nextPageToken = $nextPageToken;
  }
  public function getNextPageToken()
  {
    return $this->nextPageToken;
  }
  /**
   * @param WPvivid_Google_Service_Drive_Reply
   */
  public function setReplies($replies)
  {
    $this->replies = $replies;
  }
  /**
   * @return WPvivid_Google_Service_Drive_Reply
   */
  public function getReplies()
  {
    return $this->replies;
  }
}
includes/lib2/google-api-php-client/src/WPvivid/Google/Service/Drive/Reply.php000064400000004527151327705670023163 0ustar00<?php
/*
 * Copyright 2014 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

class WPvivid_Google_Service_Drive_Reply extends WPvivid_Google_Model
{
  public $action;
  protected $authorType = 'WPvivid_Google_Service_Drive_User';
  protected $authorDataType = '';
  public $content;
  public $createdTime;
  public $deleted;
  public $htmlContent;
  public $id;
  public $kind;
  public $modifiedTime;

  public function setAction($action)
  {
    $this->action = $action;
  }
  public function getAction()
  {
    return $this->action;
  }
  /**
   * @param WPvivid_Google_Service_Drive_User
   */
  public function setAuthor(WPvivid_Google_Service_Drive_User $author)
  {
    $this->author = $author;
  }
  /**
   * @return WPvivid_Google_Service_Drive_User
   */
  public function getAuthor()
  {
    return $this->author;
  }
  public function setContent($content)
  {
    $this->content = $content;
  }
  public function getContent()
  {
    return $this->content;
  }
  public function setCreatedTime($createdTime)
  {
    $this->createdTime = $createdTime;
  }
  public function getCreatedTime()
  {
    return $this->createdTime;
  }
  public function setDeleted($deleted)
  {
    $this->deleted = $deleted;
  }
  public function getDeleted()
  {
    return $this->deleted;
  }
  public function setHtmlContent($htmlContent)
  {
    $this->htmlContent = $htmlContent;
  }
  public function getHtmlContent()
  {
    return $this->htmlContent;
  }
  public function setId($id)
  {
    $this->id = $id;
  }
  public function getId()
  {
    return $this->id;
  }
  public function setKind($kind)
  {
    $this->kind = $kind;
  }
  public function getKind()
  {
    return $this->kind;
  }
  public function setModifiedTime($modifiedTime)
  {
    $this->modifiedTime = $modifiedTime;
  }
  public function getModifiedTime()
  {
    return $this->modifiedTime;
  }
}
lib2/google-api-php-client/src/WPvivid/Google/Service/Drive/DriveFileContentHintsThumbnail.php000064400000001771151327705670030065 0ustar00includes<?php
/*
 * Copyright 2014 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

class WPvivid_Google_Service_Drive_DriveFileContentHintsThumbnail extends WPvivid_Google_Model
{
  public $image;
  public $mimeType;

  public function setImage($image)
  {
    $this->image = $image;
  }
  public function getImage()
  {
    return $this->image;
  }
  public function setMimeType($mimeType)
  {
    $this->mimeType = $mimeType;
  }
  public function getMimeType()
  {
    return $this->mimeType;
  }
}
includes/lib2/google-api-php-client/src/WPvivid/Google/Service/Drive/FileList.php000064400000003212151327705670023571 0ustar00<?php
/*
 * Copyright 2014 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

class WPvivid_Google_Service_Drive_FileList extends WPvivid_Google_Collection
{
  protected $collection_key = 'files';
  protected $filesType = 'WPvivid_Google_Service_Drive_DriveFile';
  protected $filesDataType = 'array';
  public $incompleteSearch;
  public $kind;
  public $nextPageToken;
  public $files;

  /**
   * @param WPvivid_Google_Service_Drive_DriveFile
   */
  public function setFiles($files)
  {
    $this->files = $files;
  }
  /**
   * @return WPvivid_Google_Service_Drive_DriveFile
   */
  public function getFiles()
  {
    return $this->files;
  }
  public function setIncompleteSearch($incompleteSearch)
  {
    $this->incompleteSearch = $incompleteSearch;
  }
  public function getIncompleteSearch()
  {
    return $this->incompleteSearch;
  }
  public function setKind($kind)
  {
    $this->kind = $kind;
  }
  public function getKind()
  {
    return $this->kind;
  }
  public function setNextPageToken($nextPageToken)
  {
    $this->nextPageToken = $nextPageToken;
  }
  public function getNextPageToken()
  {
    return $this->nextPageToken;
  }
}
includes/lib2/google-api-php-client/src/WPvivid/Google/Service/Drive.php000064400000076743151327705670022101 0ustar00<?php
/*
 * Copyright 2014 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

/**
 * Service definition for Drive (v3).
 *
 * <p>
 * Manages files in Drive including uploading, downloading, searching, detecting
 * changes, and updating sharing permissions.</p>
 *
 * <p>
 * For more information about this service, see the API
 * <a href="https://developers.google.com/drive/" target="_blank">Documentation</a>
 * </p>
 *
 * @author Google, Inc.
 */
class WPvivid_Google_Service_Drive extends WPvivid_Google_Service
{
  /** See, edit, create, and delete all of your Google Drive files. */
  const DRIVE =
      "https://www.googleapis.com/auth/drive";
  /** View and manage its own configuration data in your Google Drive. */
  const DRIVE_APPDATA =
      "https://www.googleapis.com/auth/drive.appdata";
  /** View and manage Google Drive files and folders that you have opened or created with this app. */
  const DRIVE_FILE =
      "https://www.googleapis.com/auth/drive.file";
  /** View and manage metadata of files in your Google Drive. */
  const DRIVE_METADATA =
      "https://www.googleapis.com/auth/drive.metadata";
  /** View metadata for files in your Google Drive. */
  const DRIVE_METADATA_READONLY =
      "https://www.googleapis.com/auth/drive.metadata.readonly";
  /** View the photos, videos and albums in your Google Photos. */
  const DRIVE_PHOTOS_READONLY =
      "https://www.googleapis.com/auth/drive.photos.readonly";
  /** See and download all your Google Drive files. */
  const DRIVE_READONLY =
      "https://www.googleapis.com/auth/drive.readonly";
  /** Modify your Google Apps Script scripts' behavior. */
  const DRIVE_SCRIPTS =
      "https://www.googleapis.com/auth/drive.scripts";

  public $about;
  public $changes;
  public $channels;
  public $comments;
  public $files;
  public $permissions;
  public $replies;
  public $revisions;
  public $teamdrives;
  public $serviceName;

  /**
   * Constructs the internal representation of the Drive service.
   *
   * @param WPvivid_Google_Client $client
   */
  public function __construct(WPvivid_Google_Client $client)
  {
    parent::__construct($client);
    $this->rootUrl = 'https://www.googleapis.com/';
    $this->servicePath = 'drive/v3/';
    $this->version = 'v3';
    $this->serviceName = 'drive';

    $this->about = new WPvivid_Google_Service_Drive_Resource_About(
        $this,
        $this->serviceName,
        'about',
        array(
          'methods' => array(
            'get' => array(
              'path' => 'about',
              'httpMethod' => 'GET',
              'parameters' => array(),
            ),
          )
        )
    );
    $this->changes = new WPvivid_Google_Service_Drive_Resource_Changes(
        $this,
        $this->serviceName,
        'changes',
        array(
          'methods' => array(
            'getStartPageToken' => array(
              'path' => 'changes/startPageToken',
              'httpMethod' => 'GET',
              'parameters' => array(
                'supportsTeamDrives' => array(
                  'location' => 'query',
                  'type' => 'boolean',
                ),
                'teamDriveId' => array(
                  'location' => 'query',
                  'type' => 'string',
                ),
              ),
            ),'list' => array(
              'path' => 'changes',
              'httpMethod' => 'GET',
              'parameters' => array(
                'pageToken' => array(
                  'location' => 'query',
                  'type' => 'string',
                  'required' => true,
                ),
                'includeCorpusRemovals' => array(
                  'location' => 'query',
                  'type' => 'boolean',
                ),
                'includeRemoved' => array(
                  'location' => 'query',
                  'type' => 'boolean',
                ),
                'includeTeamDriveItems' => array(
                  'location' => 'query',
                  'type' => 'boolean',
                ),
                'pageSize' => array(
                  'location' => 'query',
                  'type' => 'integer',
                ),
                'restrictToMyDrive' => array(
                  'location' => 'query',
                  'type' => 'boolean',
                ),
                'spaces' => array(
                  'location' => 'query',
                  'type' => 'string',
                ),
                'supportsTeamDrives' => array(
                  'location' => 'query',
                  'type' => 'boolean',
                ),
                'teamDriveId' => array(
                  'location' => 'query',
                  'type' => 'string',
                ),
              ),
            ),'watch' => array(
              'path' => 'changes/watch',
              'httpMethod' => 'POST',
              'parameters' => array(
                'pageToken' => array(
                  'location' => 'query',
                  'type' => 'string',
                  'required' => true,
                ),
                'includeCorpusRemovals' => array(
                  'location' => 'query',
                  'type' => 'boolean',
                ),
                'includeRemoved' => array(
                  'location' => 'query',
                  'type' => 'boolean',
                ),
                'includeTeamDriveItems' => array(
                  'location' => 'query',
                  'type' => 'boolean',
                ),
                'pageSize' => array(
                  'location' => 'query',
                  'type' => 'integer',
                ),
                'restrictToMyDrive' => array(
                  'location' => 'query',
                  'type' => 'boolean',
                ),
                'spaces' => array(
                  'location' => 'query',
                  'type' => 'string',
                ),
                'supportsTeamDrives' => array(
                  'location' => 'query',
                  'type' => 'boolean',
                ),
                'teamDriveId' => array(
                  'location' => 'query',
                  'type' => 'string',
                ),
              ),
            ),
          )
        )
    );
    $this->channels = new WPvivid_Google_Service_Drive_Resource_Channels(
        $this,
        $this->serviceName,
        'channels',
        array(
          'methods' => array(
            'stop' => array(
              'path' => 'channels/stop',
              'httpMethod' => 'POST',
              'parameters' => array(),
            ),
          )
        )
    );
    $this->comments = new WPvivid_Google_Service_Drive_Resource_Comments(
        $this,
        $this->serviceName,
        'comments',
        array(
          'methods' => array(
            'create' => array(
              'path' => 'files/{fileId}/comments',
              'httpMethod' => 'POST',
              'parameters' => array(
                'fileId' => array(
                  'location' => 'path',
                  'type' => 'string',
                  'required' => true,
                ),
              ),
            ),'delete' => array(
              'path' => 'files/{fileId}/comments/{commentId}',
              'httpMethod' => 'DELETE',
              'parameters' => array(
                'fileId' => array(
                  'location' => 'path',
                  'type' => 'string',
                  'required' => true,
                ),
                'commentId' => array(
                  'location' => 'path',
                  'type' => 'string',
                  'required' => true,
                ),
              ),
            ),'get' => array(
              'path' => 'files/{fileId}/comments/{commentId}',
              'httpMethod' => 'GET',
              'parameters' => array(
                'fileId' => array(
                  'location' => 'path',
                  'type' => 'string',
                  'required' => true,
                ),
                'commentId' => array(
                  'location' => 'path',
                  'type' => 'string',
                  'required' => true,
                ),
                'includeDeleted' => array(
                  'location' => 'query',
                  'type' => 'boolean',
                ),
              ),
            ),'list' => array(
              'path' => 'files/{fileId}/comments',
              'httpMethod' => 'GET',
              'parameters' => array(
                'fileId' => array(
                  'location' => 'path',
                  'type' => 'string',
                  'required' => true,
                ),
                'includeDeleted' => array(
                  'location' => 'query',
                  'type' => 'boolean',
                ),
                'pageSize' => array(
                  'location' => 'query',
                  'type' => 'integer',
                ),
                'pageToken' => array(
                  'location' => 'query',
                  'type' => 'string',
                ),
                'startModifiedTime' => array(
                  'location' => 'query',
                  'type' => 'string',
                ),
              ),
            ),'update' => array(
              'path' => 'files/{fileId}/comments/{commentId}',
              'httpMethod' => 'PATCH',
              'parameters' => array(
                'fileId' => array(
                  'location' => 'path',
                  'type' => 'string',
                  'required' => true,
                ),
                'commentId' => array(
                  'location' => 'path',
                  'type' => 'string',
                  'required' => true,
                ),
              ),
            ),
          )
        )
    );
    $this->files = new WPvivid_Google_Service_Drive_Resource_Files(
        $this,
        $this->serviceName,
        'files',
        array(
          'methods' => array(
            'copy' => array(
              'path' => 'files/{fileId}/copy',
              'httpMethod' => 'POST',
              'parameters' => array(
                'fileId' => array(
                  'location' => 'path',
                  'type' => 'string',
                  'required' => true,
                ),
                'ignoreDefaultVisibility' => array(
                  'location' => 'query',
                  'type' => 'boolean',
                ),
                'keepRevisionForever' => array(
                  'location' => 'query',
                  'type' => 'boolean',
                ),
                'ocrLanguage' => array(
                  'location' => 'query',
                  'type' => 'string',
                ),
                'supportsTeamDrives' => array(
                  'location' => 'query',
                  'type' => 'boolean',
                ),
              ),
            ),'create' => array(
              'path' => 'files',
              'httpMethod' => 'POST',
              'parameters' => array(
                'ignoreDefaultVisibility' => array(
                  'location' => 'query',
                  'type' => 'boolean',
                ),
                'keepRevisionForever' => array(
                  'location' => 'query',
                  'type' => 'boolean',
                ),
                'ocrLanguage' => array(
                  'location' => 'query',
                  'type' => 'string',
                ),
                'supportsTeamDrives' => array(
                  'location' => 'query',
                  'type' => 'boolean',
                ),
                'useContentAsIndexableText' => array(
                  'location' => 'query',
                  'type' => 'boolean',
                ),
              ),
            ),'delete' => array(
              'path' => 'files/{fileId}',
              'httpMethod' => 'DELETE',
              'parameters' => array(
                'fileId' => array(
                  'location' => 'path',
                  'type' => 'string',
                  'required' => true,
                ),
                'supportsTeamDrives' => array(
                  'location' => 'query',
                  'type' => 'boolean',
                ),
              ),
            ),'emptyTrash' => array(
              'path' => 'files/trash',
              'httpMethod' => 'DELETE',
              'parameters' => array(),
            ),'export' => array(
              'path' => 'files/{fileId}/export',
              'httpMethod' => 'GET',
              'parameters' => array(
                'fileId' => array(
                  'location' => 'path',
                  'type' => 'string',
                  'required' => true,
                ),
                'mimeType' => array(
                  'location' => 'query',
                  'type' => 'string',
                  'required' => true,
                ),
              ),
            ),'generateIds' => array(
              'path' => 'files/generateIds',
              'httpMethod' => 'GET',
              'parameters' => array(
                'count' => array(
                  'location' => 'query',
                  'type' => 'integer',
                ),
                'space' => array(
                  'location' => 'query',
                  'type' => 'string',
                ),
              ),
            ),'get' => array(
              'path' => 'files/{fileId}',
              'httpMethod' => 'GET',
              'parameters' => array(
                'fileId' => array(
                  'location' => 'path',
                  'type' => 'string',
                  'required' => true,
                ),
                'acknowledgeAbuse' => array(
                  'location' => 'query',
                  'type' => 'boolean',
                ),
                'supportsTeamDrives' => array(
                  'location' => 'query',
                  'type' => 'boolean',
                ),
              ),
            ),'list' => array(
              'path' => 'files',
              'httpMethod' => 'GET',
              'parameters' => array(
                'corpora' => array(
                  'location' => 'query',
                  'type' => 'string',
                ),
                'corpus' => array(
                  'location' => 'query',
                  'type' => 'string',
                ),
                'includeTeamDriveItems' => array(
                  'location' => 'query',
                  'type' => 'boolean',
                ),
                'orderBy' => array(
                  'location' => 'query',
                  'type' => 'string',
                ),
                'pageSize' => array(
                  'location' => 'query',
                  'type' => 'integer',
                ),
                'pageToken' => array(
                  'location' => 'query',
                  'type' => 'string',
                ),
                'q' => array(
                  'location' => 'query',
                  'type' => 'string',
                ),
                'spaces' => array(
                  'location' => 'query',
                  'type' => 'string',
                ),
                'supportsTeamDrives' => array(
                  'location' => 'query',
                  'type' => 'boolean',
                ),
                'teamDriveId' => array(
                  'location' => 'query',
                  'type' => 'string',
                ),
              ),
            ),'update' => array(
              'path' => 'files/{fileId}',
              'httpMethod' => 'PATCH',
              'parameters' => array(
                'fileId' => array(
                  'location' => 'path',
                  'type' => 'string',
                  'required' => true,
                ),
                'addParents' => array(
                  'location' => 'query',
                  'type' => 'string',
                ),
                'keepRevisionForever' => array(
                  'location' => 'query',
                  'type' => 'boolean',
                ),
                'ocrLanguage' => array(
                  'location' => 'query',
                  'type' => 'string',
                ),
                'removeParents' => array(
                  'location' => 'query',
                  'type' => 'string',
                ),
                'supportsTeamDrives' => array(
                  'location' => 'query',
                  'type' => 'boolean',
                ),
                'useContentAsIndexableText' => array(
                  'location' => 'query',
                  'type' => 'boolean',
                ),
              ),
            ),'watch' => array(
              'path' => 'files/{fileId}/watch',
              'httpMethod' => 'POST',
              'parameters' => array(
                'fileId' => array(
                  'location' => 'path',
                  'type' => 'string',
                  'required' => true,
                ),
                'acknowledgeAbuse' => array(
                  'location' => 'query',
                  'type' => 'boolean',
                ),
                'supportsTeamDrives' => array(
                  'location' => 'query',
                  'type' => 'boolean',
                ),
              ),
            ),
          )
        )
    );
    $this->permissions = new WPvivid_Google_Service_Drive_Resource_Permissions(
        $this,
        $this->serviceName,
        'permissions',
        array(
          'methods' => array(
            'create' => array(
              'path' => 'files/{fileId}/permissions',
              'httpMethod' => 'POST',
              'parameters' => array(
                'fileId' => array(
                  'location' => 'path',
                  'type' => 'string',
                  'required' => true,
                ),
                'emailMessage' => array(
                  'location' => 'query',
                  'type' => 'string',
                ),
                'sendNotificationEmail' => array(
                  'location' => 'query',
                  'type' => 'boolean',
                ),
                'supportsTeamDrives' => array(
                  'location' => 'query',
                  'type' => 'boolean',
                ),
                'transferOwnership' => array(
                  'location' => 'query',
                  'type' => 'boolean',
                ),
                'useDomainAdminAccess' => array(
                  'location' => 'query',
                  'type' => 'boolean',
                ),
              ),
            ),'delete' => array(
              'path' => 'files/{fileId}/permissions/{permissionId}',
              'httpMethod' => 'DELETE',
              'parameters' => array(
                'fileId' => array(
                  'location' => 'path',
                  'type' => 'string',
                  'required' => true,
                ),
                'permissionId' => array(
                  'location' => 'path',
                  'type' => 'string',
                  'required' => true,
                ),
                'supportsTeamDrives' => array(
                  'location' => 'query',
                  'type' => 'boolean',
                ),
                'useDomainAdminAccess' => array(
                  'location' => 'query',
                  'type' => 'boolean',
                ),
              ),
            ),'get' => array(
              'path' => 'files/{fileId}/permissions/{permissionId}',
              'httpMethod' => 'GET',
              'parameters' => array(
                'fileId' => array(
                  'location' => 'path',
                  'type' => 'string',
                  'required' => true,
                ),
                'permissionId' => array(
                  'location' => 'path',
                  'type' => 'string',
                  'required' => true,
                ),
                'supportsTeamDrives' => array(
                  'location' => 'query',
                  'type' => 'boolean',
                ),
                'useDomainAdminAccess' => array(
                  'location' => 'query',
                  'type' => 'boolean',
                ),
              ),
            ),'list' => array(
              'path' => 'files/{fileId}/permissions',
              'httpMethod' => 'GET',
              'parameters' => array(
                'fileId' => array(
                  'location' => 'path',
                  'type' => 'string',
                  'required' => true,
                ),
                'pageSize' => array(
                  'location' => 'query',
                  'type' => 'integer',
                ),
                'pageToken' => array(
                  'location' => 'query',
                  'type' => 'string',
                ),
                'supportsTeamDrives' => array(
                  'location' => 'query',
                  'type' => 'boolean',
                ),
                'useDomainAdminAccess' => array(
                  'location' => 'query',
                  'type' => 'boolean',
                ),
              ),
            ),'update' => array(
              'path' => 'files/{fileId}/permissions/{permissionId}',
              'httpMethod' => 'PATCH',
              'parameters' => array(
                'fileId' => array(
                  'location' => 'path',
                  'type' => 'string',
                  'required' => true,
                ),
                'permissionId' => array(
                  'location' => 'path',
                  'type' => 'string',
                  'required' => true,
                ),
                'removeExpiration' => array(
                  'location' => 'query',
                  'type' => 'boolean',
                ),
                'supportsTeamDrives' => array(
                  'location' => 'query',
                  'type' => 'boolean',
                ),
                'transferOwnership' => array(
                  'location' => 'query',
                  'type' => 'boolean',
                ),
                'useDomainAdminAccess' => array(
                  'location' => 'query',
                  'type' => 'boolean',
                ),
              ),
            ),
          )
        )
    );
    $this->replies = new WPvivid_Google_Service_Drive_Resource_Replies(
        $this,
        $this->serviceName,
        'replies',
        array(
          'methods' => array(
            'create' => array(
              'path' => 'files/{fileId}/comments/{commentId}/replies',
              'httpMethod' => 'POST',
              'parameters' => array(
                'fileId' => array(
                  'location' => 'path',
                  'type' => 'string',
                  'required' => true,
                ),
                'commentId' => array(
                  'location' => 'path',
                  'type' => 'string',
                  'required' => true,
                ),
              ),
            ),'delete' => array(
              'path' => 'files/{fileId}/comments/{commentId}/replies/{replyId}',
              'httpMethod' => 'DELETE',
              'parameters' => array(
                'fileId' => array(
                  'location' => 'path',
                  'type' => 'string',
                  'required' => true,
                ),
                'commentId' => array(
                  'location' => 'path',
                  'type' => 'string',
                  'required' => true,
                ),
                'replyId' => array(
                  'location' => 'path',
                  'type' => 'string',
                  'required' => true,
                ),
              ),
            ),'get' => array(
              'path' => 'files/{fileId}/comments/{commentId}/replies/{replyId}',
              'httpMethod' => 'GET',
              'parameters' => array(
                'fileId' => array(
                  'location' => 'path',
                  'type' => 'string',
                  'required' => true,
                ),
                'commentId' => array(
                  'location' => 'path',
                  'type' => 'string',
                  'required' => true,
                ),
                'replyId' => array(
                  'location' => 'path',
                  'type' => 'string',
                  'required' => true,
                ),
                'includeDeleted' => array(
                  'location' => 'query',
                  'type' => 'boolean',
                ),
              ),
            ),'list' => array(
              'path' => 'files/{fileId}/comments/{commentId}/replies',
              'httpMethod' => 'GET',
              'parameters' => array(
                'fileId' => array(
                  'location' => 'path',
                  'type' => 'string',
                  'required' => true,
                ),
                'commentId' => array(
                  'location' => 'path',
                  'type' => 'string',
                  'required' => true,
                ),
                'includeDeleted' => array(
                  'location' => 'query',
                  'type' => 'boolean',
                ),
                'pageSize' => array(
                  'location' => 'query',
                  'type' => 'integer',
                ),
                'pageToken' => array(
                  'location' => 'query',
                  'type' => 'string',
                ),
              ),
            ),'update' => array(
              'path' => 'files/{fileId}/comments/{commentId}/replies/{replyId}',
              'httpMethod' => 'PATCH',
              'parameters' => array(
                'fileId' => array(
                  'location' => 'path',
                  'type' => 'string',
                  'required' => true,
                ),
                'commentId' => array(
                  'location' => 'path',
                  'type' => 'string',
                  'required' => true,
                ),
                'replyId' => array(
                  'location' => 'path',
                  'type' => 'string',
                  'required' => true,
                ),
              ),
            ),
          )
        )
    );
    $this->revisions = new WPvivid_Google_Service_Drive_Resource_Revisions(
        $this,
        $this->serviceName,
        'revisions',
        array(
          'methods' => array(
            'delete' => array(
              'path' => 'files/{fileId}/revisions/{revisionId}',
              'httpMethod' => 'DELETE',
              'parameters' => array(
                'fileId' => array(
                  'location' => 'path',
                  'type' => 'string',
                  'required' => true,
                ),
                'revisionId' => array(
                  'location' => 'path',
                  'type' => 'string',
                  'required' => true,
                ),
              ),
            ),'get' => array(
              'path' => 'files/{fileId}/revisions/{revisionId}',
              'httpMethod' => 'GET',
              'parameters' => array(
                'fileId' => array(
                  'location' => 'path',
                  'type' => 'string',
                  'required' => true,
                ),
                'revisionId' => array(
                  'location' => 'path',
                  'type' => 'string',
                  'required' => true,
                ),
                'acknowledgeAbuse' => array(
                  'location' => 'query',
                  'type' => 'boolean',
                ),
              ),
            ),'list' => array(
              'path' => 'files/{fileId}/revisions',
              'httpMethod' => 'GET',
              'parameters' => array(
                'fileId' => array(
                  'location' => 'path',
                  'type' => 'string',
                  'required' => true,
                ),
                'pageSize' => array(
                  'location' => 'query',
                  'type' => 'integer',
                ),
                'pageToken' => array(
                  'location' => 'query',
                  'type' => 'string',
                ),
              ),
            ),'update' => array(
              'path' => 'files/{fileId}/revisions/{revisionId}',
              'httpMethod' => 'PATCH',
              'parameters' => array(
                'fileId' => array(
                  'location' => 'path',
                  'type' => 'string',
                  'required' => true,
                ),
                'revisionId' => array(
                  'location' => 'path',
                  'type' => 'string',
                  'required' => true,
                ),
              ),
            ),
          )
        )
    );
    $this->teamdrives = new WPvivid_Google_Service_Drive_Resource_Teamdrives(
        $this,
        $this->serviceName,
        'teamdrives',
        array(
          'methods' => array(
            'create' => array(
              'path' => 'teamdrives',
              'httpMethod' => 'POST',
              'parameters' => array(
                'requestId' => array(
                  'location' => 'query',
                  'type' => 'string',
                  'required' => true,
                ),
              ),
            ),'delete' => array(
              'path' => 'teamdrives/{teamDriveId}',
              'httpMethod' => 'DELETE',
              'parameters' => array(
                'teamDriveId' => array(
                  'location' => 'path',
                  'type' => 'string',
                  'required' => true,
                ),
              ),
            ),'get' => array(
              'path' => 'teamdrives/{teamDriveId}',
              'httpMethod' => 'GET',
              'parameters' => array(
                'teamDriveId' => array(
                  'location' => 'path',
                  'type' => 'string',
                  'required' => true,
                ),
                'useDomainAdminAccess' => array(
                  'location' => 'query',
                  'type' => 'boolean',
                ),
              ),
            ),'list' => array(
              'path' => 'teamdrives',
              'httpMethod' => 'GET',
              'parameters' => array(
                'pageSize' => array(
                  'location' => 'query',
                  'type' => 'integer',
                ),
                'pageToken' => array(
                  'location' => 'query',
                  'type' => 'string',
                ),
                'q' => array(
                  'location' => 'query',
                  'type' => 'string',
                ),
                'useDomainAdminAccess' => array(
                  'location' => 'query',
                  'type' => 'boolean',
                ),
              ),
            ),'update' => array(
              'path' => 'teamdrives/{teamDriveId}',
              'httpMethod' => 'PATCH',
              'parameters' => array(
                'teamDriveId' => array(
                  'location' => 'path',
                  'type' => 'string',
                  'required' => true,
                ),
                'useDomainAdminAccess' => array(
                  'location' => 'query',
                  'type' => 'boolean',
                ),
              ),
            ),
          )
        )
    );
  }
}
includes/lib2/google-api-php-client/src/WPvivid/Google/Client.php000064400000077164151327705670020644 0ustar00<?php
/*
 * Copyright 2010 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

use WPvividGoogle\Auth\ApplicationDefaultCredentials;
use WPvividGoogle\Auth\Cache\MemoryCacheItemPool;
use WPvividGoogle\Auth\CredentialsLoader;
use WPvividGoogle\Auth\HttpHandler\HttpHandlerFactory;
use WPvividGoogle\Auth\OAuth2;
use WPvividGoogle\Auth\Credentials\ServiceAccountCredentials;
use WPvividGoogle\Auth\Credentials\UserRefreshCredentials;
use WPvividGuzzleHttp\Client;
use WPvividGuzzleHttp\ClientInterface;
use WPvividGuzzleHttp\Ring\Client\StreamHandler;
use WPvividPsr\Cache\CacheItemPoolInterface;
use WPvividPsr\Http\Message\RequestInterface;
use WPvividPsr\Log\LoggerInterface;
use WPvividMonolog\Logger;
use WPvividMonolog\Handler\StreamHandler as MonologStreamHandler;
use WPvividMonolog\Handler\SyslogHandler as MonologSyslogHandler;

/**
 * The Google API Client
 * https://github.com/google/google-api-php-client
 */
class WPvivid_Google_Client
{
  const LIBVER = "2.2.2";
  const USER_AGENT_SUFFIX = "google-api-php-client/";
  const OAUTH2_REVOKE_URI = 'https://accounts.google.com/o/oauth2/revoke';
  const OAUTH2_TOKEN_URI = 'https://www.googleapis.com/oauth2/v4/token';
  const OAUTH2_AUTH_URL = 'https://accounts.google.com/o/oauth2/auth';
  const API_BASE_PATH = 'https://www.googleapis.com';

  /**
   * @var WPvividGoogle\Auth\OAuth2 $auth
   */
  private $auth;

  /**
   * @var WPvividGuzzleHttp\ClientInterface $http
   */
  private $http;

  /**
   * @var WPvividPsr\Cache\CacheItemPoolInterface $cache
   */
  private $cache;

  /**
   * @var array access token
   */
  private $token;

  /**
   * @var array $config
   */
  private $config;

  /**
   * @var WPvividPsr\Log\LoggerInterface $logger
   */
  private $logger;

  /**
   * @var boolean $deferExecution
   */
  private $deferExecution = false;

  /** @var array $scopes */
  // Scopes requested by the client
  protected $requestedScopes = [];

  /**
   * Construct the Google Client.
   *
   * @param array $config
   */
  public function __construct(array $config = array())
  {
    $this->config = array_merge(
        [
          'application_name' => '',

          // Don't change these unless you're working against a special development
          // or testing environment.
          'base_path' => self::API_BASE_PATH,

          // https://developers.google.com/console
          'client_id' => '',
          'client_secret' => '',
          'redirect_uri' => null,
          'state' => null,

          // Simple API access key, also from the API console. Ensure you get
          // a Server key, and not a Browser key.
          'developer_key' => '',

          // For use with Google Cloud Platform
          // fetch the ApplicationDefaultCredentials, if applicable
          // @see https://developers.google.com/identity/protocols/application-default-credentials
          'use_application_default_credentials' => false,
          'signing_key' => null,
          'signing_algorithm' => null,
          'subject' => null,

          // Other OAuth2 parameters.
          'hd' => '',
          'prompt' => '',
          'openid.realm' => '',
          'include_granted_scopes' => null,
          'login_hint' => '',
          'request_visible_actions' => '',
          'access_type' => 'online',
          'approval_prompt' => 'auto',

          // Task Runner retry configuration
          // @see WPvivid_Google_Task_Runner
          'retry' => array(),

          // cache config for downstream auth caching
          'cache_config' => [],

          // function to be called when an access token is fetched
          // follows the signature function ($cacheKey, $accessToken)
          'token_callback' => null,

          // Service class used in WPvivid_Google_Client::verifyIdToken.
          // Explicitly pass this in to avoid setting JWT::$leeway
          'jwt' => null,
        ],
        $config
    );
  }

  /**
   * Get a string containing the version of the library.
   *
   * @return string
   */
  public function getLibraryVersion()
  {
    return self::LIBVER;
  }

  /**
   * For backwards compatibility
   * alias for fetchAccessTokenWithAuthCode
   *
   * @param $code string code from accounts.google.com
   * @return array access token
   * @deprecated
   */
  public function authenticate($code)
  {
    return $this->fetchAccessTokenWithAuthCode($code);
  }

  /**
   * Attempt to exchange a code for an valid authentication token.
   * Helper wrapped around the OAuth 2.0 implementation.
   *
   * @param $code string code from accounts.google.com
   * @return array access token
   */
  public function fetchAccessTokenWithAuthCode($code)
  {
    if (strlen($code) == 0) {
      throw new InvalidArgumentException("Invalid code");
    }

    $auth = $this->getOAuth2Service();
    $auth->setCode($code);
    $auth->setRedirectUri($this->getRedirectUri());

    $httpHandler = HttpHandlerFactory::build($this->getHttpClient());
    $creds = $auth->fetchAuthToken($httpHandler);
    if ($creds && isset($creds['access_token'])) {
      $creds['created'] = time();
      $this->setAccessToken($creds);
    }

    return $creds;
  }

  /**
   * For backwards compatibility
   * alias for fetchAccessTokenWithAssertion
   *
   * @return array access token
   * @deprecated
   */
  public function refreshTokenWithAssertion()
  {
    return $this->fetchAccessTokenWithAssertion();
  }

  /**
   * Fetches a fresh access token with a given assertion token.
   * @param ClientInterface $authHttp optional.
   * @return array access token
   */
  public function fetchAccessTokenWithAssertion(ClientInterface $authHttp = null)
  {
    if (!$this->isUsingApplicationDefaultCredentials()) {
      throw new DomainException(
          'set the JSON service account credentials using'
          . ' WPvivid_Client::setAuthConfig or set the path to your JSON file'
          . ' with the "GOOGLE_APPLICATION_CREDENTIALS" environment variable'
          . ' and call WPvivid_Google_Client::useApplicationDefaultCredentials to'
          . ' refresh a token with assertion.'
      );
    }

    $this->getLogger()->log(
        'info',
        'OAuth2 access token refresh with Signed JWT assertion grants.'
    );

    $credentials = $this->createApplicationDefaultCredentials();

    $httpHandler = HttpHandlerFactory::build($authHttp);
    $creds = $credentials->fetchAuthToken($httpHandler);
    if ($creds && isset($creds['access_token'])) {
      $creds['created'] = time();
      $this->setAccessToken($creds);
    }

    return $creds;
  }

  /**
   * For backwards compatibility
   * alias for fetchAccessTokenWithRefreshToken
   *
   * @param string $refreshToken
   * @return array access token
   */
  public function refreshToken($refreshToken)
  {
    return $this->fetchAccessTokenWithRefreshToken($refreshToken);
  }

  /**
   * Fetches a fresh OAuth 2.0 access token with the given refresh token.
   * @param string $refreshToken
   * @return array access token
   */
  public function fetchAccessTokenWithRefreshToken($refreshToken = null)
  {
    if (null === $refreshToken) {
      if (!isset($this->token['refresh_token'])) {
        throw new LogicException(
            'refresh token must be passed in or set as part of setAccessToken'
        );
      }
      $refreshToken = $this->token['refresh_token'];
    }
    $this->getLogger()->info('OAuth2 access token refresh');
    $auth = $this->getOAuth2Service();
    $auth->setRefreshToken($refreshToken);

    $httpHandler = HttpHandlerFactory::build($this->getHttpClient());
    $creds = $auth->fetchAuthToken($httpHandler);
    if ($creds && isset($creds['access_token'])) {
      $creds['created'] = time();
      if (!isset($creds['refresh_token'])) {
        $creds['refresh_token'] = $refreshToken;
      }
      $this->setAccessToken($creds);
    }

    return $creds;
  }

  /**
   * Create a URL to obtain user authorization.
   * The authorization endpoint allows the user to first
   * authenticate, and then grant/deny the access request.
   * @param string|array $scope The scope is expressed as an array or list of space-delimited strings.
   * @return string
   */
  public function createAuthUrl($scope = null)
  {
    if (empty($scope)) {
      $scope = $this->prepareScopes();
    }
    if (is_array($scope)) {
      $scope = implode(' ', $scope);
    }

    // only accept one of prompt or approval_prompt
    $approvalPrompt = $this->config['prompt']
      ? null
      : $this->config['approval_prompt'];

    // include_granted_scopes should be string "true", string "false", or null
    $includeGrantedScopes = $this->config['include_granted_scopes'] === null
      ? null
      : var_export($this->config['include_granted_scopes'], true);

    $params = array_filter(
        [
          'access_type' => $this->config['access_type'],
          'approval_prompt' => $approvalPrompt,
          'hd' => $this->config['hd'],
          'include_granted_scopes' => $includeGrantedScopes,
          'login_hint' => $this->config['login_hint'],
          'openid.realm' => $this->config['openid.realm'],
          'prompt' => $this->config['prompt'],
          'response_type' => 'code',
          'scope' => $scope,
          'state' => $this->config['state'],
        ]
    );

    // If the list of scopes contains plus.login, add request_visible_actions
    // to auth URL.
    $rva = $this->config['request_visible_actions'];
    if (strlen($rva) > 0 && false !== strpos($scope, 'plus.login')) {
        $params['request_visible_actions'] = $rva;
    }

    $auth = $this->getOAuth2Service();

    return (string) $auth->buildFullAuthorizationUri($params);
  }

  /**
   * Adds auth listeners to the HTTP client based on the credentials
   * set in the Google API Client object
   *
   * @param WPvividGuzzleHttp\ClientInterface $http the http client object.
   * @return WPvividGuzzleHttp\ClientInterface the http client object
   */
  public function authorize(ClientInterface $http = null)
  {
    $credentials = null;
    $token = null;
    $scopes = null;
    if (null === $http) {
      $http = $this->getHttpClient();
    }
    // These conditionals represent the decision tree for authentication
    //   1.  Check for Application Default Credentials
    //   2.  Check for API Key
    //   3a. Check for an Access Token
    //   3b. If access token exists but is expired, try to refresh it
    if ($this->isUsingApplicationDefaultCredentials()) {
      $credentials = $this->createApplicationDefaultCredentials();
    } elseif ($token = $this->getAccessToken()) {
      $scopes = $this->prepareScopes();
      // add refresh subscriber to request a new token
      if (isset($token['refresh_token']) && $this->isAccessTokenExpired()) {
        $credentials = $this->createUserRefreshCredentials(
            $scopes,
            $token['refresh_token']
        );
      }
    }

    $authHandler = $this->getAuthHandler();

    if ($credentials) {
      $callback = $this->config['token_callback'];
      $http = $authHandler->attachCredentials($http, $credentials, $callback);
    } elseif ($token) {
      $http = $authHandler->attachToken($http, $token, (array) $scopes);
    } elseif ($key = $this->config['developer_key']) {
      $http = $authHandler->attachKey($http, $key);
    }

    return $http;
  }

  /**
   * Set the configuration to use application default credentials for
   * authentication
   *
   * @see https://developers.google.com/identity/protocols/application-default-credentials
   * @param boolean $useAppCreds
   */
  public function useApplicationDefaultCredentials($useAppCreds = true)
  {
    $this->config['use_application_default_credentials'] = $useAppCreds;
  }

  /**
   * To prevent useApplicationDefaultCredentials from inappropriately being
   * called in a conditional
   *
   * @see https://developers.google.com/identity/protocols/application-default-credentials
   */
  public function isUsingApplicationDefaultCredentials()
  {
    return $this->config['use_application_default_credentials'];
  }

  /**
   * @param string|array $token
   * @throws InvalidArgumentException
   */
  public function setAccessToken($token)
  {
    if (is_string($token)) {
      if ($json = json_decode($token, true)) {
        $token = $json;
      } else {
        // assume $token is just the token string
        $token = array(
          'access_token' => $token,
        );
      }
    }
    if ($token == null) {
      throw new InvalidArgumentException('invalid json token');
    }
    if (!isset($token['access_token'])) {
      throw new InvalidArgumentException("Invalid token format");
    }
    $this->token = $token;
  }

  public function getAccessToken()
  {
    return $this->token;
  }

  /**
   * @return string|null
   */
  public function getRefreshToken()
  {
    if (isset($this->token['refresh_token'])) {
      return $this->token['refresh_token'];
    }

    return null;
  }

  /**
   * Returns if the access_token is expired.
   * @return bool Returns True if the access_token is expired.
   */
  public function isAccessTokenExpired()
  {
    if (!$this->token) {
      return true;
    }

    $created = 0;
    if (isset($this->token['created'])) {
      $created = $this->token['created'];
    } elseif (isset($this->token['id_token'])) {
      // check the ID token for "iat"
      // signature verification is not required here, as we are just
      // using this for convenience to save a round trip request
      // to the Google API server
      $idToken = $this->token['id_token'];
      if (substr_count($idToken, '.') == 2) {
        $parts = explode('.', $idToken);
        $payload = json_decode(base64_decode($parts[1]), true);
        if ($payload && isset($payload['iat'])) {
          $created = $payload['iat'];
        }
      }
    }

    // If the token is set to expire in the next 30 seconds.
    return ($created + ($this->token['expires_in'] - 30)) < time();
  }

  /**
   * @deprecated See UPGRADING.md for more information
   */
  public function getAuth()
  {
    throw new BadMethodCallException(
        'This function no longer exists. See UPGRADING.md for more information'
    );
  }

  /**
   * @deprecated See UPGRADING.md for more information
   */
  public function setAuth($auth)
  {
    throw new BadMethodCallException(
        'This function no longer exists. See UPGRADING.md for more information'
    );
  }

  /**
   * Set the OAuth 2.0 Client ID.
   * @param string $clientId
   */
  public function setClientId($clientId)
  {
    $this->config['client_id'] = $clientId;
  }

  public function getClientId()
  {
    return $this->config['client_id'];
  }

  /**
   * Set the OAuth 2.0 Client Secret.
   * @param string $clientSecret
   */
  public function setClientSecret($clientSecret)
  {
    $this->config['client_secret'] = $clientSecret;
  }

  public function getClientSecret()
  {
    return $this->config['client_secret'];
  }

  /**
   * Set the OAuth 2.0 Redirect URI.
   * @param string $redirectUri
   */
  public function setRedirectUri($redirectUri)
  {
    $this->config['redirect_uri'] = $redirectUri;
  }

  public function getRedirectUri()
  {
    return $this->config['redirect_uri'];
  }

  /**
   * Set OAuth 2.0 "state" parameter to achieve per-request customization.
   * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-22#section-3.1.2.2
   * @param string $state
   */
  public function setState($state)
  {
    $this->config['state'] = $state;
  }

  /**
   * @param string $accessType Possible values for access_type include:
   *  {@code "offline"} to request offline access from the user.
   *  {@code "online"} to request online access from the user.
   */
  public function setAccessType($accessType)
  {
    $this->config['access_type'] = $accessType;
  }

  /**
   * @param string $approvalPrompt Possible values for approval_prompt include:
   *  {@code "force"} to force the approval UI to appear.
   *  {@code "auto"} to request auto-approval when possible. (This is the default value)
   */
  public function setApprovalPrompt($approvalPrompt)
  {
    $this->config['approval_prompt'] = $approvalPrompt;
  }

  /**
   * Set the login hint, email address or sub id.
   * @param string $loginHint
   */
  public function setLoginHint($loginHint)
  {
    $this->config['login_hint'] = $loginHint;
  }

  /**
   * Set the application name, this is included in the User-Agent HTTP header.
   * @param string $applicationName
   */
  public function setApplicationName($applicationName)
  {
    $this->config['application_name'] = $applicationName;
  }

  /**
   * If 'plus.login' is included in the list of requested scopes, you can use
   * this method to define types of app activities that your app will write.
   * You can find a list of available types here:
   * @link https://developers.google.com/+/api/moment-types
   *
   * @param array $requestVisibleActions Array of app activity types
   */
  public function setRequestVisibleActions($requestVisibleActions)
  {
    if (is_array($requestVisibleActions)) {
      $requestVisibleActions = implode(" ", $requestVisibleActions);
    }
    $this->config['request_visible_actions'] = $requestVisibleActions;
  }

  /**
   * Set the developer key to use, these are obtained through the API Console.
   * @see http://code.google.com/apis/console-help/#generatingdevkeys
   * @param string $developerKey
   */
  public function setDeveloperKey($developerKey)
  {
    $this->config['developer_key'] = $developerKey;
  }

  /**
   * Set the hd (hosted domain) parameter streamlines the login process for
   * Google Apps hosted accounts. By including the domain of the user, you
   * restrict sign-in to accounts at that domain.
   * @param $hd string - the domain to use.
   */
  public function setHostedDomain($hd)
  {
    $this->config['hd'] = $hd;
  }

  /**
   * Set the prompt hint. Valid values are none, consent and select_account.
   * If no value is specified and the user has not previously authorized
   * access, then the user is shown a consent screen.
   * @param $prompt string
   */
  public function setPrompt($prompt)
  {
    $this->config['prompt'] = $prompt;
  }

  /**
   * openid.realm is a parameter from the OpenID 2.0 protocol, not from OAuth
   * 2.0. It is used in OpenID 2.0 requests to signify the URL-space for which
   * an authentication request is valid.
   * @param $realm string - the URL-space to use.
   */
  public function setOpenidRealm($realm)
  {
    $this->config['openid.realm'] = $realm;
  }

  /**
   * If this is provided with the value true, and the authorization request is
   * granted, the authorization will include any previous authorizations
   * granted to this user/application combination for other scopes.
   * @param $include boolean - the URL-space to use.
   */
  public function setIncludeGrantedScopes($include)
  {
    $this->config['include_granted_scopes'] = $include;
  }

  /**
   * sets function to be called when an access token is fetched
   * @param callable $tokenCallback - function ($cacheKey, $accessToken)
   */
  public function setTokenCallback(callable $tokenCallback)
  {
    $this->config['token_callback'] = $tokenCallback;
  }

  /**
   * Revoke an OAuth2 access token or refresh token. This method will revoke the current access
   * token, if a token isn't provided.
   *
   * @param string|null $token The token (access token or a refresh token) that should be revoked.
   * @return boolean Returns True if the revocation was successful, otherwise False.
   */
  public function revokeToken($token = null)
  {
    $tokenRevoker = new WPvivid_Google_AccessToken_Revoke(
        $this->getHttpClient()
    );

    return $tokenRevoker->revokeToken($token ?: $this->getAccessToken());
  }

  /**
   * Verify an id_token. This method will verify the current id_token, if one
   * isn't provided.
   *
   * @throws LogicException
   * @param string|null $idToken The token (id_token) that should be verified.
   * @return array|false Returns the token payload as an array if the verification was
   * successful, false otherwise.
   */
  public function verifyIdToken($idToken = null)
  {
    $tokenVerifier = new WPvivid_Google_AccessToken_Verify(
        $this->getHttpClient(),
        $this->getCache(),
        $this->config['jwt']
    );

    if (null === $idToken) {
      $token = $this->getAccessToken();
      if (!isset($token['id_token'])) {
        throw new LogicException(
            'id_token must be passed in or set as part of setAccessToken'
        );
      }
      $idToken = $token['id_token'];
    }

    return $tokenVerifier->verifyIdToken(
        $idToken,
        $this->getClientId()
    );
  }

  /**
   * Set the scopes to be requested. Must be called before createAuthUrl().
   * Will remove any previously configured scopes.
   * @param array $scopes, ie: array('https://www.googleapis.com/auth/plus.login',
   * 'https://www.googleapis.com/auth/moderator')
   */
  public function setScopes($scopes)
  {
    $this->requestedScopes = array();
    $this->addScope($scopes);
  }

  /**
   * This functions adds a scope to be requested as part of the OAuth2.0 flow.
   * Will append any scopes not previously requested to the scope parameter.
   * A single string will be treated as a scope to request. An array of strings
   * will each be appended.
   * @param $scope_or_scopes string|array e.g. "profile"
   */
  public function addScope($scope_or_scopes)
  {
    if (is_string($scope_or_scopes) && !in_array($scope_or_scopes, $this->requestedScopes)) {
      $this->requestedScopes[] = $scope_or_scopes;
    } else if (is_array($scope_or_scopes)) {
      foreach ($scope_or_scopes as $scope) {
        $this->addScope($scope);
      }
    }
  }

  /**
   * Returns the list of scopes requested by the client
   * @return array the list of scopes
   *
   */
  public function getScopes()
  {
     return $this->requestedScopes;
  }

  /**
   * @return string|null
   * @visible For Testing
   */
  public function prepareScopes()
  {
    if (empty($this->requestedScopes)) {
      return null;
    }

    return implode(' ', $this->requestedScopes);
  }

  /**
   * Helper method to execute deferred HTTP requests.
   *
   * @param $request WPvividPsr\Http\Message\RequestInterface|WPvivid_Google_Http_Batch
   * @throws  WPvivid_Google_Exception
   * @return array of the type of the expected class or Psr\Http\Message\ResponseInterface.
   */
  public function execute(RequestInterface $request, $expectedClass = null)
  {
    $request = $request->withHeader(
        'User-Agent',
        $this->config['application_name']
        . " " . self::USER_AGENT_SUFFIX
        . $this->getLibraryVersion()
    );

    // call the authorize method
    // this is where most of the grunt work is done
    $http = $this->authorize();

    return WPvivid_Google_Http_REST::execute($http, $request, $expectedClass, $this->config['retry']);
  }

  /**
   * Declare whether batch calls should be used. This may increase throughput
   * by making multiple requests in one connection.
   *
   * @param boolean $useBatch True if the batch support should
   * be enabled. Defaults to False.
   */
  public function setUseBatch($useBatch)
  {
    // This is actually an alias for setDefer.
    $this->setDefer($useBatch);
  }

  /**
   * Are we running in Google AppEngine?
   * return bool
   */
  public function isAppEngine()
  {
    return (isset($_SERVER['SERVER_SOFTWARE']) &&
        strpos($_SERVER['SERVER_SOFTWARE'], 'Google App Engine') !== false);
  }

  public function setConfig($name, $value)
  {
    $this->config[$name] = $value;
  }

  public function getConfig($name, $default = null)
  {
    return isset($this->config[$name]) ? $this->config[$name] : $default;
  }

  /**
   * For backwards compatibility
   * alias for setAuthConfig
   *
   * @param string $file the configuration file
   * @throws WPvivid_Google_Exception
   * @deprecated
   */
  public function setAuthConfigFile($file)
  {
    $this->setAuthConfig($file);
  }

  /**
   * Set the auth config from new or deprecated JSON config.
   * This structure should match the file downloaded from
   * the "Download JSON" button on in the Google Developer
   * Console.
   * @param string|array $config the configuration json
   * @throws WPvivid_Google_Exception
   */
  public function setAuthConfig($config)
  {
    if (is_string($config)) {
      if (!file_exists($config)) {
        throw new InvalidArgumentException('file does not exist');
      }

      $json = file_get_contents($config);

      if (!$config = json_decode($json, true)) {
        throw new LogicException('invalid json for auth config');
      }
    }

    $key = isset($config['installed']) ? 'installed' : 'web';
    if (isset($config['type']) && $config['type'] == 'service_account') {
      // application default credentials
      $this->useApplicationDefaultCredentials();

      // set the information from the config
      $this->setClientId($config['client_id']);
      $this->config['client_email'] = $config['client_email'];
      $this->config['signing_key'] = $config['private_key'];
      $this->config['signing_algorithm'] = 'HS256';
    } elseif (isset($config[$key])) {
      // old-style
      $this->setClientId($config[$key]['client_id']);
      $this->setClientSecret($config[$key]['client_secret']);
      if (isset($config[$key]['redirect_uris'])) {
        $this->setRedirectUri($config[$key]['redirect_uris'][0]);
      }
    } else {
      // new-style
      $this->setClientId($config['client_id']);
      $this->setClientSecret($config['client_secret']);
      if (isset($config['redirect_uris'])) {
        $this->setRedirectUri($config['redirect_uris'][0]);
      }
    }
  }

  /**
   * Use when the service account has been delegated domain wide access.
   *
   * @param string $subject an email address account to impersonate
   */
  public function setSubject($subject)
  {
    $this->config['subject'] = $subject;
  }

  /**
   * Declare whether making API calls should make the call immediately, or
   * return a request which can be called with ->execute();
   *
   * @param boolean $defer True if calls should not be executed right away.
   */
  public function setDefer($defer)
  {
    $this->deferExecution = $defer;
  }

  /**
   * Whether or not to return raw requests
   * @return boolean
   */
  public function shouldDefer()
  {
    return $this->deferExecution;
  }

  /**
   * @return  WPvividGoogle\Auth\OAuth2 implementation
   */
  public function getOAuth2Service()
  {
    if (!isset($this->auth)) {
      $this->auth = $this->createOAuth2Service();
    }

    return $this->auth;
  }

  /**
   * create a default google auth object
   */
  protected function createOAuth2Service()
  {
    $auth = new OAuth2(
        [
          'clientId'          => $this->getClientId(),
          'clientSecret'      => $this->getClientSecret(),
          'authorizationUri'   => self::OAUTH2_AUTH_URL,
          'tokenCredentialUri' => self::OAUTH2_TOKEN_URI,
          'redirectUri'       => $this->getRedirectUri(),
          'issuer'            => $this->config['client_id'],
          'signingKey'        => $this->config['signing_key'],
          'signingAlgorithm'  => $this->config['signing_algorithm'],
        ]
    );

    return $auth;
  }

  /**
   * Set the Cache object
   * @param WPvividPsr\Cache\CacheItemPoolInterface $cache
   */
  public function setCache(CacheItemPoolInterface $cache)
  {
    $this->cache = $cache;
  }

  /**
   * @return WPvividPsr\Cache\CacheItemPoolInterface Cache implementation
   */
  public function getCache()
  {
    if (!$this->cache) {
      $this->cache = $this->createDefaultCache();
    }

    return $this->cache;
  }

  /**
   * @param array $cacheConfig
   */
  public function setCacheConfig(array $cacheConfig)
  {
    $this->config['cache_config'] = $cacheConfig;
  }

  /**
   * Set the Logger object
   * @param WPvividPsr\Log\LoggerInterface $logger
   */
  public function setLogger(LoggerInterface $logger)
  {
    $this->logger = $logger;
  }

  /**
   * @return WPvividPsr\Log\LoggerInterface implementation
   */
  public function getLogger()
  {
    if (!isset($this->logger)) {
      $this->logger = $this->createDefaultLogger();
    }

    return $this->logger;
  }

  protected function createDefaultLogger()
  {
    $logger = new Logger('google-api-php-client');
    if ($this->isAppEngine()) {
      $handler = new MonologSyslogHandler('app', LOG_USER, Logger::NOTICE);
    } else {
      $handler = new MonologStreamHandler('php://stderr', Logger::NOTICE);
    }
    $logger->pushHandler($handler);

    return $logger;
  }

  protected function createDefaultCache()
  {
    return new MemoryCacheItemPool;
  }

  /**
   * Set the Http Client object
   * @param WPvividGuzzleHttp\ClientInterface $http
   */
  public function setHttpClient(ClientInterface $http)
  {
    $this->http = $http;
  }

  /**
   * @return WPvividGuzzleHttp\ClientInterface implementation
   */
  public function getHttpClient()
  {
    if (null === $this->http) {
      $this->http = $this->createDefaultHttpClient();
    }

    return $this->http;
  }

  protected function createDefaultHttpClient()
  {
    $options = ['exceptions' => false];

    $version = ClientInterface::VERSION;
    if ('5' === $version[0]) {
      $options = [
        'base_url' => $this->config['base_path'],
        'defaults' => $options,
      ];
      if ($this->isAppEngine()) {
        // set StreamHandler on AppEngine by default
        $options['handler']  = new StreamHandler();
        $options['defaults']['verify'] = '/etc/ca-certificates.crt';
      }
    } else {
      // guzzle 6
        $options['base_uri'] = $this->config['base_path'];
        $options['verify'] = WPVIVID_PLUGIN_DIR.'/vendor/guzzle/guzzle/src/Guzzle/Http/Resources/cacert.pem';
    }

    return new WPvividGuzzleHttp\Client($options);
  }

  private function createApplicationDefaultCredentials()
  {
    $scopes = $this->prepareScopes();
    $sub = $this->config['subject'];
    $signingKey = $this->config['signing_key'];

    // create credentials using values supplied in setAuthConfig
    if ($signingKey) {
      $serviceAccountCredentials = array(
        'client_id' => $this->config['client_id'],
        'client_email' => $this->config['client_email'],
        'private_key' => $signingKey,
        'type' => 'service_account',
      );
      $credentials = CredentialsLoader::makeCredentials($scopes, $serviceAccountCredentials);
    } else {
      $credentials = ApplicationDefaultCredentials::getCredentials($scopes);
    }

    // for service account domain-wide authority (impersonating a user)
    // @see https://developers.google.com/identity/protocols/OAuth2ServiceAccount
    if ($sub) {
      if (!$credentials instanceof ServiceAccountCredentials) {
        throw new DomainException('domain-wide authority requires service account credentials');
      }

      $credentials->setSub($sub);
    }

    return $credentials;
  }

  protected function getAuthHandler()
  {
    // Be very careful using the cache, as the underlying auth library's cache
    // implementation is naive, and the cache keys do not account for user
    // sessions.
    //
    // @see https://github.com/google/google-api-php-client/issues/821
    return WPvivid_Google_AuthHandler_AuthHandlerFactory::build(
        $this->getCache(),
        $this->config['cache_config']
    );
  }

  private function createUserRefreshCredentials($scope, $refreshToken)
  {
    $creds = array_filter(
        array(
          'client_id' => $this->getClientId(),
          'client_secret' => $this->getClientSecret(),
          'refresh_token' => $refreshToken,
        )
    );

    return new UserRefreshCredentials($scope, $creds);
  }
}
includes/lib2/google-api-php-client/src/WPvivid/Google/Service.php000064400000002471151327705670021013 0ustar00<?php
/*
 * Copyright 2010 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

class WPvivid_Google_Service
{
  public $batchPath;
  public $rootUrl;
  public $version;
  public $servicePath;
  public $availableScopes;
  public $resource;
  private $client;

  public function __construct(WPvivid_Google_Client $client)
  {
    $this->client = $client;
  }

  /**
   * Return the associated WPvivid_Google_Client class.
   * @return WPvivid_Google_Client
   */
  public function getClient()
  {
    return $this->client;
  }

  /**
   * Create a new HTTP Batch handler for this service
   *
   * @return WPvivid_Google_Http_Batch
   */
  public function createBatch()
  {
    return new WPvivid_Google_Http_Batch(
        $this->client,
        false,
        $this->rootUrl,
        $this->batchPath
    );
  }
}
includes/lib2/google-api-php-client/src/WPvivid/Google/Utils/UriTemplate.php000064400000022357151327705670022753 0ustar00<?php
/*
 * Copyright 2013 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * Implementation of levels 1-3 of the URI Template spec.
 * @see http://tools.ietf.org/html/rfc6570
 */
class WPvivid_Google_Utils_UriTemplate
{
  const TYPE_MAP = "1";
  const TYPE_LIST = "2";
  const TYPE_SCALAR = "4";

  /**
   * @var $operators array
   * These are valid at the start of a template block to
   * modify the way in which the variables inside are
   * processed.
   */
  private $operators = array(
      "+" => "reserved",
      "/" => "segments",
      "." => "dotprefix",
      "#" => "fragment",
      ";" => "semicolon",
      "?" => "form",
      "&" => "continuation"
  );

  /**
   * @var $reserved array
   * These are the characters which should not be URL encoded in reserved
   * strings.
   */
  private $reserved = array(
      "=", ",", "!", "@", "|", ":", "/", "?", "#",
      "[", "]",'$', "&", "'", "(", ")", "*", "+", ";"
  );
  private $reservedEncoded = array(
    "%3D", "%2C", "%21", "%40", "%7C", "%3A", "%2F", "%3F",
    "%23", "%5B", "%5D", "%24", "%26", "%27", "%28", "%29",
    "%2A", "%2B", "%3B"
  );

  public function parse($string, array $parameters)
  {
    return $this->resolveNextSection($string, $parameters);
  }

  /**
   * This function finds the first matching {...} block and
   * executes the replacement. It then calls itself to find
   * subsequent blocks, if any.
   */
  private function resolveNextSection($string, $parameters)
  {
    $start = strpos($string, "{");
    if ($start === false) {
      return $string;
    }
    $end = strpos($string, "}");
    if ($end === false) {
      return $string;
    }
    $string = $this->replace($string, $start, $end, $parameters);
    return $this->resolveNextSection($string, $parameters);
  }

  private function replace($string, $start, $end, $parameters)
  {
    // We know a data block will have {} round it, so we can strip that.
    $data = substr($string, $start + 1, $end - $start - 1);

    // If the first character is one of the reserved operators, it effects
    // the processing of the stream.
    if (isset($this->operators[$data[0]])) {
      $op = $this->operators[$data[0]];
      $data = substr($data, 1);
      $prefix = "";
      $prefix_on_missing = false;

      switch ($op) {
        case "reserved":
          // Reserved means certain characters should not be URL encoded
          $data = $this->replaceVars($data, $parameters, ",", null, true);
          break;
        case "fragment":
          // Comma separated with fragment prefix. Bare values only.
          $prefix = "#";
          $prefix_on_missing = true;
          $data = $this->replaceVars($data, $parameters, ",", null, true);
          break;
        case "segments":
          // Slash separated data. Bare values only.
          $prefix = "/";
          $data =$this->replaceVars($data, $parameters, "/");
          break;
        case "dotprefix":
          // Dot separated data. Bare values only.
          $prefix = ".";
          $prefix_on_missing = true;
          $data = $this->replaceVars($data, $parameters, ".");
          break;
        case "semicolon":
          // Semicolon prefixed and separated. Uses the key name
          $prefix = ";";
          $data = $this->replaceVars($data, $parameters, ";", "=", false, true, false);
          break;
        case "form":
          // Standard URL format. Uses the key name
          $prefix = "?";
          $data = $this->replaceVars($data, $parameters, "&", "=");
          break;
        case "continuation":
          // Standard URL, but with leading ampersand. Uses key name.
          $prefix = "&";
          $data = $this->replaceVars($data, $parameters, "&", "=");
          break;
      }

      // Add the initial prefix character if data is valid.
      if ($data || ($data !== false && $prefix_on_missing)) {
        $data = $prefix . $data;
      }

    } else {
      // If no operator we replace with the defaults.
      $data = $this->replaceVars($data, $parameters);
    }
    // This is chops out the {...} and replaces with the new section.
    return substr($string, 0, $start) . $data . substr($string, $end + 1);
  }

  private function replaceVars(
      $section,
      $parameters,
      $sep = ",",
      $combine = null,
      $reserved = false,
      $tag_empty = false,
      $combine_on_empty = true
  ) {
    if (strpos($section, ",") === false) {
      // If we only have a single value, we can immediately process.
      return $this->combine(
          $section,
          $parameters,
          $sep,
          $combine,
          $reserved,
          $tag_empty,
          $combine_on_empty
      );
    } else {
      // If we have multiple values, we need to split and loop over them.
      // Each is treated individually, then glued together with the
      // separator character.
      $vars = explode(",", $section);
      return $this->combineList(
          $vars,
          $sep,
          $parameters,
          $combine,
          $reserved,
          false, // Never emit empty strings in multi-param replacements
          $combine_on_empty
      );
    }
  }

  public function combine(
      $key,
      $parameters,
      $sep,
      $combine,
      $reserved,
      $tag_empty,
      $combine_on_empty
  ) {
    $length = false;
    $explode = false;
    $skip_final_combine = false;
    $value = false;

    // Check for length restriction.
    if (strpos($key, ":") !== false) {
      list($key, $length) = explode(":", $key);
    }

    // Check for explode parameter.
    if ($key[strlen($key) - 1] == "*") {
      $explode = true;
      $key = substr($key, 0, -1);
      $skip_final_combine = true;
    }

    // Define the list separator.
    $list_sep = $explode ? $sep : ",";

    if (isset($parameters[$key])) {
      $data_type = $this->getDataType($parameters[$key]);
      switch ($data_type) {
        case self::TYPE_SCALAR:
          $value = $this->getValue($parameters[$key], $length);
          break;
        case self::TYPE_LIST:
          $values = array();
          foreach ($parameters[$key] as $pkey => $pvalue) {
            $pvalue = $this->getValue($pvalue, $length);
            if ($combine && $explode) {
              $values[$pkey] = $key . $combine . $pvalue;
            } else {
              $values[$pkey] = $pvalue;
            }
          }
          $value = implode($list_sep, $values);
          if ($value == '') {
            return '';
          }
          break;
        case self::TYPE_MAP:
          $values = array();
          foreach ($parameters[$key] as $pkey => $pvalue) {
            $pvalue = $this->getValue($pvalue, $length);
            if ($explode) {
              $pkey = $this->getValue($pkey, $length);
              $values[] = $pkey . "=" . $pvalue; // Explode triggers = combine.
            } else {
              $values[] = $pkey;
              $values[] = $pvalue;
            }
          }
          $value = implode($list_sep, $values);
          if ($value == '') {
            return false;
          }
          break;
      }
    } else if ($tag_empty) {
      // If we are just indicating empty values with their key name, return that.
      return $key;
    } else {
      // Otherwise we can skip this variable due to not being defined.
      return false;
    }

    if ($reserved) {
      $value = str_replace($this->reservedEncoded, $this->reserved, $value);
    }

    // If we do not need to include the key name, we just return the raw
    // value.
    if (!$combine || $skip_final_combine) {
      return $value;
    }

    // Else we combine the key name: foo=bar, if value is not the empty string.
    return $key . ($value != '' || $combine_on_empty ? $combine . $value : '');
  }

  /**
   * Return the type of a passed in value
   */
  private function getDataType($data)
  {
    if (is_array($data)) {
      reset($data);
      if (key($data) !== 0) {
        return self::TYPE_MAP;
      }
      return self::TYPE_LIST;
    }
    return self::TYPE_SCALAR;
  }

  /**
   * Utility function that merges multiple combine calls
   * for multi-key templates.
   */
  private function combineList(
      $vars,
      $sep,
      $parameters,
      $combine,
      $reserved,
      $tag_empty,
      $combine_on_empty
  ) {
    $ret = array();
    foreach ($vars as $var) {
      $response = $this->combine(
          $var,
          $parameters,
          $sep,
          $combine,
          $reserved,
          $tag_empty,
          $combine_on_empty
      );
      if ($response === false) {
        continue;
      }
      $ret[] = $response;
    }
    return implode($sep, $ret);
  }

  /**
   * Utility function to encode and trim values
   */
  private function getValue($value, $length)
  {
    if ($length) {
      $value = substr($value, 0, $length);
    }
    $value = rawurlencode($value);
    return $value;
  }
}
includes/lib2/google-api-php-client/src/WPvivid/Google/AccessToken/Verify.php000064400000016550151327705670023064 0ustar00<?php

/*
 * Copyright 2008 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

use Firebase\JWT\ExpiredException as ExpiredExceptionV3;
use Firebase\JWT\SignatureInvalidException;
use WPvividGuzzleHttp\Client;
use WPvividGuzzleHttp\ClientInterface;
use WPvividPsr\Cache\CacheItemPoolInterface;
use WPvividGoogle\Auth\Cache\MemoryCacheItemPool;
use Stash\Driver\FileSystem;
use Stash\Pool;

/**
 * Wrapper around Google Access Tokens which provides convenience functions
 *
 */
class WPvivid_Google_AccessToken_Verify
{
  const FEDERATED_SIGNON_CERT_URL = 'https://www.googleapis.com/oauth2/v3/certs';
  const OAUTH2_ISSUER = 'accounts.google.com';
  const OAUTH2_ISSUER_HTTPS = 'https://accounts.google.com';

  /**
   * @var WPvividGuzzleHttp\ClientInterface The http client
   */
  private $http;

  /**
   * @var WPvividPsr\Cache\CacheItemPoolInterface cache class
   */
  private $cache;

  /**
   * Instantiates the class, but does not initiate the login flow, leaving it
   * to the discretion of the caller.
   */
  public function __construct(
      ClientInterface $http = null,
      CacheItemPoolInterface $cache = null,
      $jwt = null
  ) {
    if (null === $http) {
      $http = new Client();
    }

    if (null === $cache) {
      $cache = new MemoryCacheItemPool;
    }

    $this->http = $http;
    $this->cache = $cache;
    $this->jwt = $jwt ?: $this->getJwtService();
  }

  /**
   * Verifies an id token and returns the authenticated apiLoginTicket.
   * Throws an exception if the id token is not valid.
   * The audience parameter can be used to control which id tokens are
   * accepted.  By default, the id token must have been issued to this OAuth2 client.
   *
   * @param $audience
   * @return array the token payload, if successful
   */
  public function verifyIdToken($idToken, $audience = null)
  {
    if (empty($idToken)) {
      throw new LogicException('id_token cannot be null');
    }

    // set phpseclib constants if applicable
    $this->setPhpsecConstants();

    // Check signature
    $certs = $this->getFederatedSignOnCerts();
    foreach ($certs as $cert) {
      $bigIntClass = $this->getBigIntClass();
      $rsaClass = $this->getRsaClass();
      $modulus = new $bigIntClass($this->jwt->urlsafeB64Decode($cert['n']), 256);
      $exponent = new $bigIntClass($this->jwt->urlsafeB64Decode($cert['e']), 256);

      $rsa = new $rsaClass();
      $rsa->loadKey(array('n' => $modulus, 'e' => $exponent));

      try {
        $payload = $this->jwt->decode(
            $idToken,
            $rsa->getPublicKey(),
            array('RS256')
        );

        if (property_exists($payload, 'aud')) {
          if ($audience && $payload->aud != $audience) {
            return false;
          }
        }

        // support HTTP and HTTPS issuers
        // @see https://developers.google.com/identity/sign-in/web/backend-auth
        $issuers = array(self::OAUTH2_ISSUER, self::OAUTH2_ISSUER_HTTPS);
        if (!isset($payload->iss) || !in_array($payload->iss, $issuers)) {
          return false;
        }

        return (array) $payload;
      } catch (ExpiredException $e) {
        return false;
      } catch (ExpiredExceptionV3 $e) {
        return false;
      } catch (SignatureInvalidException $e) {
        // continue
      } catch (DomainException $e) {
        // continue
      }
    }

    return false;
  }

  private function getCache()
  {
    return $this->cache;
  }

  /**
   * Retrieve and cache a certificates file.
   *
   * @param $url string location
   * @throws WPvivid_Google_Exception
   * @return array certificates
   */
  private function retrieveCertsFromLocation($url)
  {
    // If we're retrieving a local file, just grab it.
    if (0 !== strpos($url, 'http')) {
      if (!$file = file_get_contents($url)) {
        throw new WPvivid_Google_Exception(
            "Failed to retrieve verification certificates: '" .
            esc_url($url) . "'."
        );
      }

      return json_decode($file, true);
    }

    $response = $this->http->get($url);

    if ($response->getStatusCode() == 200) {
      return json_decode((string) $response->getBody(), true);
    }
    throw new WPvivid_Google_Exception(
        sprintf(
            'Failed to retrieve verification certificates: "%s".',
            esc_html($response->getBody()->getContents())
        ),
        esc_attr($response->getStatusCode())
    );
  }

  // Gets federated sign-on certificates to use for verifying identity tokens.
  // Returns certs as array structure, where keys are key ids, and values
  // are PEM encoded certificates.
  private function getFederatedSignOnCerts()
  {
    $certs = null;
    if ($cache = $this->getCache()) {
      $cacheItem = $cache->getItem('federated_signon_certs_v3');
      $certs = $cacheItem->get();
    }


    if (!$certs) {
      $certs = $this->retrieveCertsFromLocation(
          self::FEDERATED_SIGNON_CERT_URL
      );

      if ($cache) {
        $cacheItem->expiresAt(new DateTime('+1 hour'));
        $cacheItem->set($certs);
        $cache->save($cacheItem);
      }
    }

    if (!isset($certs['keys'])) {
      throw new InvalidArgumentException(
          'federated sign-on certs expects "keys" to be set'
      );
    }

    return $certs['keys'];
  }

  private function getJwtService()
  {
    $jwtClass = 'JWT';
    if (class_exists('\Firebase\JWT\JWT')) {
      $jwtClass = 'Firebase\JWT\JWT';
    }

    if (property_exists($jwtClass, 'leeway') && $jwtClass::$leeway < 1) {
      // Ensures JWT leeway is at least 1
      // @see https://github.com/google/google-api-php-client/issues/827
      $jwtClass::$leeway = 1;
    }

    return new $jwtClass;
  }

  private function getRsaClass()
  {
    if (class_exists('phpseclib\Crypt\RSA')) {
      return 'phpseclib\Crypt\RSA';
    }

    return 'Crypt_RSA';
  }

  private function getBigIntClass()
  {
    if (class_exists('phpseclib\Math\BigInteger')) {
      return 'phpseclib\Math\BigInteger';
    }

    return 'Math_BigInteger';
  }

  private function getOpenSslConstant()
  {
    if (class_exists('phpseclib\Crypt\RSA')) {
      return 'phpseclib\Crypt\RSA::MODE_OPENSSL';
    }

    if (class_exists('Crypt_RSA')) {
      return 'CRYPT_RSA_MODE_OPENSSL';
    }

    throw new \Exception('Cannot find RSA class');
  }

  /**
   * phpseclib calls "phpinfo" by default, which requires special
   * whitelisting in the AppEngine VM environment. This function
   * sets constants to bypass the need for phpseclib to check phpinfo
   *
   * @see phpseclib/Math/BigInteger
   * @see https://github.com/GoogleCloudPlatform/getting-started-php/issues/85
   */
  private function setPhpsecConstants()
  {
    if (filter_var(getenv('GAE_VM'), FILTER_VALIDATE_BOOLEAN)) {
      if (!defined('MATH_BIGINTEGER_OPENSSL_ENABLED')) {
        define('MATH_BIGINTEGER_OPENSSL_ENABLED', true);
      }
      if (!defined('CRYPT_RSA_MODE')) {
        define('CRYPT_RSA_MODE', constant($this->getOpenSslConstant()));
      }
    }
  }
}
includes/lib2/google-api-php-client/src/WPvivid/Google/AccessToken/Revoke.php000064400000004275151327705670023054 0ustar00<?php

/*
 * Copyright 2008 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

use WPvividGoogle\Auth\HttpHandler\HttpHandlerFactory;
use WPvividGuzzleHttp\ClientInterface;
use WPvividGuzzleHttp\Psr7;
use WPvividGuzzleHttp\Psr7\Request;

/**
 * Wrapper around Google Access Tokens which provides convenience functions
 *
 */
class WPvivid_Google_AccessToken_Revoke
{
  /**
   * @var WPvividGuzzleHttp\ClientInterface The http client
   */
  private $http;

  /**
   * Instantiates the class, but does not initiate the login flow, leaving it
   * to the discretion of the caller.
   */
  public function __construct(ClientInterface $http = null)
  {
    $this->http = $http;
  }

  /**
   * Revoke an OAuth2 access token or refresh token. This method will revoke the current access
   * token, if a token isn't provided.
   *
   * @param string|array $token The token (access token or a refresh token) that should be revoked.
   * @return boolean Returns True if the revocation was successful, otherwise False.
   */
  public function revokeToken($token)
  {
    if (is_array($token)) {
      if (isset($token['refresh_token'])) {
        $token = $token['refresh_token'];
      } else {
        $token = $token['access_token'];
      }
    }

    $body = Psr7\stream_for(http_build_query(array('token' => $token)));
    $request = new Request(
        'POST',
        WPvivid_Google_Client::OAUTH2_REVOKE_URI,
        [
          'Cache-Control' => 'no-store',
          'Content-Type'  => 'application/x-www-form-urlencoded',
        ],
        $body
    );

    $httpHandler = HttpHandlerFactory::build($this->http);

    $response = $httpHandler($request);

    return $response->getStatusCode() == 200;
  }
}
includes/lib2/google-api-php-client/src/WPvivid/Google/Task/Runner.php000064400000016170151327705670021567 0ustar00<?php
/*
 * Copyright 2014 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * A task runner with exponential backoff support.
 *
 * @see https://developers.google.com/drive/web/handle-errors#implementing_exponential_backoff
 */
class WPvivid_Google_Task_Runner
{
  const TASK_RETRY_NEVER = 0;
  const TASK_RETRY_ONCE = 1;
  const TASK_RETRY_ALWAYS = -1;

  /**
   * @var integer $maxDelay The max time (in seconds) to wait before a retry.
   */
  private $maxDelay = 60;
  /**
   * @var integer $delay The previous delay from which the next is calculated.
   */
  private $delay = 1;

  /**
   * @var integer $factor The base number for the exponential back off.
   */
  private $factor = 2;
  /**
   * @var float $jitter A random number between -$jitter and $jitter will be
   * added to $factor on each iteration to allow for a better distribution of
   * retries.
   */
  private $jitter = 0.5;

  /**
   * @var integer $attempts The number of attempts that have been tried so far.
   */
  private $attempts = 0;
  /**
   * @var integer $maxAttempts The max number of attempts allowed.
   */
  private $maxAttempts = 1;

  /**
   * @var callable $action The task to run and possibly retry.
   */
  private $action;
  /**
   * @var array $arguments The task arguments.
   */
  private $arguments;

  /**
   * @var array $retryMap Map of errors with retry counts.
   */
  protected $retryMap = [
    '500' => self::TASK_RETRY_ALWAYS,
    '503' => self::TASK_RETRY_ALWAYS,
    'rateLimitExceeded' => self::TASK_RETRY_ALWAYS,
    'userRateLimitExceeded' => self::TASK_RETRY_ALWAYS,
    6  => self::TASK_RETRY_ALWAYS,  // CURLE_COULDNT_RESOLVE_HOST
    7  => self::TASK_RETRY_ALWAYS,  // CURLE_COULDNT_CONNECT
    28 => self::TASK_RETRY_ALWAYS,  // CURLE_OPERATION_TIMEOUTED
    35 => self::TASK_RETRY_ALWAYS,  // CURLE_SSL_CONNECT_ERROR
    52 => self::TASK_RETRY_ALWAYS   // CURLE_GOT_NOTHING
  ];

  /**
   * Creates a new task runner with exponential backoff support.
   *
   * @param array $config The task runner config
   * @param string $name The name of the current task (used for logging)
   * @param callable $action The task to run and possibly retry
   * @param array $arguments The task arguments
   * @throws WPvivid_Google_Task_Exception when misconfigured
   */
  public function __construct(
      $config,
      $name,
      $action,
      array $arguments = array()
  ) {
    if (isset($config['initial_delay'])) {
      if ($config['initial_delay'] < 0) {
        throw new WPvivid_Google_Task_Exception(
            'Task configuration `initial_delay` must not be negative.'
        );
      }

      $this->delay = $config['initial_delay'];
    }

    if (isset($config['max_delay'])) {
      if ($config['max_delay'] <= 0) {
        throw new WPvivid_Google_Task_Exception(
            'Task configuration `max_delay` must be greater than 0.'
        );
      }

      $this->maxDelay = $config['max_delay'];
    }

    if (isset($config['factor'])) {
      if ($config['factor'] <= 0) {
        throw new WPvivid_Google_Task_Exception(
            'Task configuration `factor` must be greater than 0.'
        );
      }

      $this->factor = $config['factor'];
    }

    if (isset($config['jitter'])) {
      if ($config['jitter'] <= 0) {
        throw new WPvivid_Google_Task_Exception(
            'Task configuration `jitter` must be greater than 0.'
        );
      }

      $this->jitter = $config['jitter'];
    }

    if (isset($config['retries'])) {
      if ($config['retries'] < 0) {
        throw new WPvivid_Google_Task_Exception(
            'Task configuration `retries` must not be negative.'
        );
      }
      $this->maxAttempts += $config['retries'];
    }

    if (!is_callable($action)) {
        throw new WPvivid_Google_Task_Exception(
            'Task argument `$action` must be a valid callable.'
        );
    }

    $this->action = $action;
    $this->arguments = $arguments;
  }

  /**
   * Checks if a retry can be attempted.
   *
   * @return boolean
   */
  public function canAttempt()
  {
    return $this->attempts < $this->maxAttempts;
  }

  /**
   * Runs the task and (if applicable) automatically retries when errors occur.
   *
   * @return mixed
   * @throws WPvivid_Google_Service_Exception on failure when no retries are available.
   */
  public function run()
  {
    while ($this->attempt()) {
      try {
        return call_user_func_array($this->action, $this->arguments);
      } catch (WPvivid_Google_Service_Exception $exception) {
        $allowedRetries = $this->allowedRetries(
            $exception->getCode(),
            $exception->getErrors()
        );

        if (!$this->canAttempt() || !$allowedRetries) {
          throw $exception;
        }

        if ($allowedRetries > 0) {
          $this->maxAttempts = min(
              $this->maxAttempts,
              $this->attempts + $allowedRetries
          );
        }
      }
    }
  }

  /**
   * Runs a task once, if possible. This is useful for bypassing the `run()`
   * loop.
   *
   * NOTE: If this is not the first attempt, this function will sleep in
   * accordance to the backoff configurations before running the task.
   *
   * @return boolean
   */
  public function attempt()
  {
    if (!$this->canAttempt()) {
      return false;
    }

    if ($this->attempts > 0) {
      $this->backOff();
    }

    $this->attempts++;
    return true;
  }

  /**
   * Sleeps in accordance to the backoff configurations.
   */
  private function backOff()
  {
    $delay = $this->getDelay();

    usleep($delay * 1000000);
  }

  /**
   * Gets the delay (in seconds) for the current backoff period.
   *
   * @return float
   */
  private function getDelay()
  {
    $jitter = $this->getJitter();
    $factor = $this->attempts > 1 ? $this->factor + $jitter : 1 + abs($jitter);

    return $this->delay = min($this->maxDelay, $this->delay * $factor);
  }

  /**
   * Gets the current jitter (random number between -$this->jitter and
   * $this->jitter).
   *
   * @return float
   */
  private function getJitter()
  {
    return $this->jitter * 2 * mt_rand() / mt_getrandmax() - $this->jitter;
  }

  /**
   * Gets the number of times the associated task can be retried.
   *
   * NOTE: -1 is returned if the task can be retried indefinitely
   *
   * @return integer
   */
  public function allowedRetries($code, $errors = array())
  {
    if (isset($this->retryMap[$code])) {
      return $this->retryMap[$code];
    }

    if (
        !empty($errors) &&
        isset($errors[0]['reason'], $this->retryMap[$errors[0]['reason']])
    ) {
      return $this->retryMap[$errors[0]['reason']];
    }

    return 0;
  }

  public function setRetryMap($retryMap)
  {
    $this->retryMap = $retryMap;
  }
}
includes/lib2/google-api-php-client/src/WPvivid/Google/Task/Retryable.php000064400000001353151327705670022244 0ustar00<?php
/*
 * Copyright 2014 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * Interface for checking how many times a given task can be retried following
 * a failure.
 */
interface WPvivid_Google_Task_Retryable
{
}
includes/lib2/google-api-php-client/src/WPvivid/Google/Task/Exception.php000064400000001243151327705670022247 0ustar00<?php
/*
 * Copyright 2014 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

class WPvivid_Google_Task_Exception extends WPvivid_Google_Exception
{
}
includes/lib2/google-api-php-client/src/WPvivid/Google/AuthHandler/AuthHandlerFactory.php000064400000002573151327705670025344 0ustar00<?php
/**
 * Copyright 2015 Google Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

use WPvividGuzzleHttp\Client;
use WPvividGuzzleHttp\ClientInterface;

class WPvivid_Google_AuthHandler_AuthHandlerFactory
{
  /**
   * Builds out a default http handler for the installed version of guzzle.
   *
   * @return WPvivid_Google_AuthHandler_Guzzle5AuthHandler|WPvivid_Google_AuthHandler_Guzzle6AuthHandler
   * @throws Exception
   */
  public static function build($cache = null, array $cacheConfig = [])
  {
    $version = ClientInterface::VERSION;

    switch ($version[0]) {
      case '5':
        return new WPvivid_Google_AuthHandler_Guzzle5AuthHandler($cache, $cacheConfig);
      case '6':
        return new WPvivid_Google_AuthHandler_Guzzle6AuthHandler($cache, $cacheConfig);
      default:
        throw new Exception('Version not supported');
    }
  }
}
includes/lib2/google-api-php-client/src/WPvivid/Google/AuthHandler/Guzzle5AuthHandler.php000064400000005060151327705670025274 0ustar00<?php

use WPvividGoogle\Auth\CredentialsLoader;
use WPvividGoogle\Auth\HttpHandler\HttpHandlerFactory;
use WPvividGoogle\Auth\FetchAuthTokenCache;
use WPvividGoogle\Auth\Subscriber\AuthTokenSubscriber;
use WPvividGoogle\Auth\Subscriber\ScopedAccessTokenSubscriber;
use WPvividGoogle\Auth\Subscriber\SimpleSubscriber;
use WPvividGuzzleHttp\Client;
use WPvividGuzzleHttp\ClientInterface;
use WPvividPsr\Cache\CacheItemPoolInterface;

/**
*
*/
class WPvivid_Google_AuthHandler_Guzzle5AuthHandler
{
  protected $cache;
  protected $cacheConfig;

  public function __construct(CacheItemPoolInterface $cache = null, array $cacheConfig = [])
  {
    $this->cache = $cache;
    $this->cacheConfig = $cacheConfig;
  }

  public function attachCredentials(
      ClientInterface $http,
      CredentialsLoader $credentials,
      callable $tokenCallback = null
  ) {
    // use the provided cache
    if ($this->cache) {
      $credentials = new FetchAuthTokenCache(
          $credentials,
          $this->cacheConfig,
          $this->cache
      );
    }
    // if we end up needing to make an HTTP request to retrieve credentials, we
    // can use our existing one, but we need to throw exceptions so the error
    // bubbles up.
    $authHttp = $this->createAuthHttp($http);
    $authHttpHandler = HttpHandlerFactory::build($authHttp);
    $subscriber = new AuthTokenSubscriber(
        $credentials,
        $authHttpHandler,
        $tokenCallback
    );

    $http->setDefaultOption('auth', 'google_auth');
    $http->getEmitter()->attach($subscriber);

    return $http;
  }

  public function attachToken(ClientInterface $http, array $token, array $scopes)
  {
    $tokenFunc = function ($scopes) use ($token) {
      return $token['access_token'];
    };

    $subscriber = new ScopedAccessTokenSubscriber(
        $tokenFunc,
        $scopes,
        $this->cacheConfig,
        $this->cache
    );

    $http->setDefaultOption('auth', 'scoped');
    $http->getEmitter()->attach($subscriber);

    return $http;
  }

  public function attachKey(ClientInterface $http, $key)
  {
    $subscriber = new SimpleSubscriber(['key' => $key]);

    $http->setDefaultOption('auth', 'simple');
    $http->getEmitter()->attach($subscriber);

    return $http;
  }

  private function createAuthHttp(ClientInterface $http)
  {
    return new Client(
        [
          'base_url' => $http->getBaseUrl(),
          'defaults' => [
            'exceptions' => true,
            'verify' => $http->getDefaultOption('verify'),
            'proxy' => $http->getDefaultOption('proxy'),
          ]
        ]
    );
  }
}
includes/lib2/google-api-php-client/src/WPvivid/Google/AuthHandler/Guzzle6AuthHandler.php000064400000005514151327705670025301 0ustar00<?php

use WPvividGoogle\Auth\CredentialsLoader;
use WPvividGoogle\Auth\HttpHandler\HttpHandlerFactory;
use WPvividGoogle\Auth\FetchAuthTokenCache;
use WPvividGoogle\Auth\Middleware\AuthTokenMiddleware;
use WPvividGoogle\Auth\Middleware\ScopedAccessTokenMiddleware;
use WPvividGoogle\Auth\Middleware\SimpleMiddleware;
use WPvividGuzzleHttp\Client;
use WPvividGuzzleHttp\ClientInterface;
use WPvividPsr\Cache\CacheItemPoolInterface;

/**
*
*/
class WPvivid_Google_AuthHandler_Guzzle6AuthHandler
{
  protected $cache;
  protected $cacheConfig;

  public function __construct(CacheItemPoolInterface $cache = null, array $cacheConfig = [])
  {
    $this->cache = $cache;
    $this->cacheConfig = $cacheConfig;
  }

  public function attachCredentials(
      ClientInterface $http,
      CredentialsLoader $credentials,
      callable $tokenCallback = null
  ) {
    // use the provided cache
    if ($this->cache) {
      $credentials = new FetchAuthTokenCache(
          $credentials,
          $this->cacheConfig,
          $this->cache
      );
    }
    // if we end up needing to make an HTTP request to retrieve credentials, we
    // can use our existing one, but we need to throw exceptions so the error
    // bubbles up.
    $authHttp = $this->createAuthHttp($http);
    $authHttpHandler = HttpHandlerFactory::build($authHttp);
    $middleware = new AuthTokenMiddleware(
        $credentials,
        $authHttpHandler,
        $tokenCallback
    );

    $config = $http->getConfig();
    $config['handler']->remove('google_auth');
    $config['handler']->push($middleware, 'google_auth');
    $config['auth'] = 'google_auth';
    $http = new Client($config);

    return $http;
  }

  public function attachToken(ClientInterface $http, array $token, array $scopes)
  {
    $tokenFunc = function ($scopes) use ($token) {
      return $token['access_token'];
    };

    $middleware = new ScopedAccessTokenMiddleware(
        $tokenFunc,
        $scopes,
        $this->cacheConfig,
        $this->cache
    );

    $config = $http->getConfig();
    $config['handler']->remove('google_auth');
    $config['handler']->push($middleware, 'google_auth');
    $config['auth'] = 'scoped';
    $http = new Client($config);

    return $http;
  }

  public function attachKey(ClientInterface $http, $key)
  {
    $middleware = new SimpleMiddleware(['key' => $key]);

    $config = $http->getConfig();
    $config['handler']->remove('google_auth');
    $config['handler']->push($middleware, 'google_auth');
    $config['auth'] = 'simple';
    $http = new Client($config);

    return $http;
  }

  private function createAuthHttp(ClientInterface $http)
  {
    return new Client(
        [
          'base_uri' => $http->getConfig('base_uri'),
          'exceptions' => true,
          'verify' => $http->getConfig('verify'),
          'proxy' => $http->getConfig('proxy'),
        ]
    );
  }
}
includes/lib2/google-api-php-client/src/WPvivid/Google/Model.php000064400000021371151327705670020453 0ustar00<?php
/*
 * Copyright 2011 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * This class defines attributes, valid values, and usage which is generated
 * from a given json schema.
 * http://tools.ietf.org/html/draft-zyp-json-schema-03#section-5
 *
 */
class WPvivid_Google_Model implements ArrayAccess
{
  /**
   * If you need to specify a NULL JSON value, use WPvivid_Google_Model::NULL_VALUE
   * instead - it will be replaced when converting to JSON with a real null.
   */
  const NULL_VALUE = "{}gapi-php-null";
  protected $internal_gapi_mappings = array();
  protected $modelData = array();
  protected $processed = array();

  /**
   * Polymorphic - accepts a variable number of arguments dependent
   * on the type of the model subclass.
   */
  final public function __construct()
  {
    if (func_num_args() == 1 && is_array(func_get_arg(0))) {
      // Initialize the model with the array's contents.
      $array = func_get_arg(0);
      $this->mapTypes($array);
    }
    $this->gapiInit();
  }

  /**
   * Getter that handles passthrough access to the data array, and lazy object creation.
   * @param string $key Property name.
   * @return mixed The value if any, or null.
   */
  public function __get($key)
  {
    $keyType = $this->keyType($key);
    $keyDataType = $this->dataType($key);
    if ($keyType && !isset($this->processed[$key])) {
      if (isset($this->modelData[$key])) {
        $val = $this->modelData[$key];
      } elseif ($keyDataType == 'array' || $keyDataType == 'map') {
        $val = array();
      } else {
        $val = null;
      }

      if ($this->isAssociativeArray($val)) {
        if ($keyDataType && 'map' == $keyDataType) {
          foreach ($val as $arrayKey => $arrayItem) {
              $this->modelData[$key][$arrayKey] =
                new $keyType($arrayItem);
          }
        } else {
          $this->modelData[$key] = new $keyType($val);
        }
      } else if (is_array($val)) {
        $arrayObject = array();
        foreach ($val as $arrayIndex => $arrayItem) {
          $arrayObject[$arrayIndex] = new $keyType($arrayItem);
        }
        $this->modelData[$key] = $arrayObject;
      }
      $this->processed[$key] = true;
    }

    return isset($this->modelData[$key]) ? $this->modelData[$key] : null;
  }

  /**
   * Initialize this object's properties from an array.
   *
   * @param array $array Used to seed this object's properties.
   * @return void
   */
  protected function mapTypes($array)
  {
    // Hard initialise simple types, lazy load more complex ones.
    foreach ($array as $key => $val) {
      if ($keyType = $this->keyType($key)) {
        $dataType = $this->dataType($key);
        if ($dataType == 'array' || $dataType == 'map') {
          $this->$key = array();
          foreach ($val as $itemKey => $itemVal) {
            if ($itemVal instanceof $keyType) {
              $this->{$key}[$itemKey] = $itemVal;
            } else {
              $this->{$key}[$itemKey] = new $keyType($itemVal);
            }
          }
        } elseif ($val instanceof $keyType) {
          $this->$key = $val;
        } else {
          $this->$key = new $keyType($val);
        }
        unset($array[$key]);
      } elseif (property_exists($this, $key)) {
          $this->$key = $val;
          unset($array[$key]);
      } elseif (property_exists($this, $camelKey = $this->camelCase($key))) {
          // This checks if property exists as camelCase, leaving it in array as snake_case
          // in case of backwards compatibility issues.
          $this->$camelKey = $val;
      }
    }
    $this->modelData = $array;
  }

  /**
   * Blank initialiser to be used in subclasses to do  post-construction initialisation - this
   * avoids the need for subclasses to have to implement the variadics handling in their
   * constructors.
   */
  protected function gapiInit()
  {
    return;
  }

  /**
   * Create a simplified object suitable for straightforward
   * conversion to JSON. This is relatively expensive
   * due to the usage of reflection, but shouldn't be called
   * a whole lot, and is the most straightforward way to filter.
   */
  public function toSimpleObject()
  {
    $object = new stdClass();

    // Process all other data.
    foreach ($this->modelData as $key => $val) {
      $result = $this->getSimpleValue($val);
      if ($result !== null) {
        $object->$key = $this->nullPlaceholderCheck($result);
      }
    }

    // Process all public properties.
    $reflect = new ReflectionObject($this);
    $props = $reflect->getProperties(ReflectionProperty::IS_PUBLIC);
    foreach ($props as $member) {
      $name = $member->getName();
      $result = $this->getSimpleValue($this->$name);
      if ($result !== null) {
        $name = $this->getMappedName($name);
        $object->$name = $this->nullPlaceholderCheck($result);
      }
    }

    return $object;
  }

  /**
   * Handle different types of values, primarily
   * other objects and map and array data types.
   */
  private function getSimpleValue($value)
  {
    if ($value instanceof WPvivid_Google_Model) {
      return $value->toSimpleObject();
    } else if (is_array($value)) {
      $return = array();
      foreach ($value as $key => $a_value) {
        $a_value = $this->getSimpleValue($a_value);
        if ($a_value !== null) {
          $key = $this->getMappedName($key);
          $return[$key] = $this->nullPlaceholderCheck($a_value);
        }
      }
      return $return;
    }
    return $value;
  }

  /**
   * Check whether the value is the null placeholder and return true null.
   */
  private function nullPlaceholderCheck($value)
  {
    if ($value === self::NULL_VALUE) {
      return null;
    }
    return $value;
  }

  /**
   * If there is an internal name mapping, use that.
   */
  private function getMappedName($key)
  {
    if (isset($this->internal_gapi_mappings, $this->internal_gapi_mappings[$key])) {
      $key = $this->internal_gapi_mappings[$key];
    }
    return $key;
  }

  /**
   * Returns true only if the array is associative.
   * @param array $array
   * @return bool True if the array is associative.
   */
  protected function isAssociativeArray($array)
  {
    if (!is_array($array)) {
      return false;
    }
    $keys = array_keys($array);
    foreach ($keys as $key) {
      if (is_string($key)) {
        return true;
      }
    }
    return false;
  }

  /**
   * Verify if $obj is an array.
   * @throws WPvivid_Google_Exception Thrown if $obj isn't an array.
   * @param array $obj Items that should be validated.
   * @param string $method Method expecting an array as an argument.
   */
  public function assertIsArray($obj, $method)
  {
    if ($obj && !is_array($obj)) {
      throw new WPvivid_Google_Exception(
          esc_html("Incorrect parameter type passed to $method(). Expected an array.")
      );
    }
  }

  #[\ReturnTypeWillChange]
  public function offsetExists($offset)
  {
    return isset($this->$offset) || isset($this->modelData[$offset]);
  }

  #[\ReturnTypeWillChange]
  public function offsetGet($offset)
  {
    return isset($this->$offset) ?
        $this->$offset :
        $this->__get($offset);
  }

  #[\ReturnTypeWillChange]
  public function offsetSet($offset, $value)
  {
    if (property_exists($this, $offset)) {
      $this->$offset = $value;
    } else {
      $this->modelData[$offset] = $value;
      $this->processed[$offset] = true;
    }
  }

  #[\ReturnTypeWillChange]
  public function offsetUnset($offset)
  {
    unset($this->modelData[$offset]);
  }

  protected function keyType($key)
  {
    $keyType = $key . "Type";

    // ensure keyType is a valid class
    if (property_exists($this, $keyType) && class_exists($this->$keyType)) {
      return $this->$keyType;
    }
  }

  protected function dataType($key)
  {
    $dataType = $key . "DataType";

    if (property_exists($this, $dataType)) {
      return $this->$dataType;
    }
  }

  public function __isset($key)
  {
    return isset($this->modelData[$key]);
  }

  public function __unset($key)
  {
    unset($this->modelData[$key]);
  }

  /**
   * Convert a string to camelCase
   * @param  string $value
   * @return string
   */
  private function camelCase($value)
  {
    $value = ucwords(str_replace(array('-', '_'), ' ', $value));
    $value = str_replace(' ', '', $value);
    $value[0] = strtolower($value[0]);
    return $value;
  }
}
includes/lib2/google-api-php-client/src/WPvivid/Google/Http/Batch.php000064400000015506151327705670021356 0ustar00<?php
/*
 * Copyright 2012 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

use WPvividGuzzleHttp\Psr7;
use WPvividGuzzleHttp\Psr7\Request;
use WPvividGuzzleHttp\Psr7\Response;
use WPvividPsr\Http\Message\RequestInterface;
use WPvividPsr\Http\Message\ResponseInterface;

/**
 * Class to handle batched requests to the Google API service.
 */
class WPvivid_Google_Http_Batch
{
  const BATCH_PATH = 'batch';

  private static $CONNECTION_ESTABLISHED_HEADERS = array(
    "HTTP/1.0 200 Connection established\r\n\r\n",
    "HTTP/1.1 200 Connection established\r\n\r\n",
  );

  /** @var string Multipart Boundary. */
  private $boundary;

  /** @var array service requests to be executed. */
  private $requests = array();

  /** @var WPvivid_Google_Client */
  private $client;

  private $rootUrl;

  private $batchPath;

  public function __construct(
      WPvivid_Google_Client $client,
      $boundary = false,
      $rootUrl = null,
      $batchPath = null
  ) {
    $this->client = $client;
    $this->boundary = $boundary ?: mt_rand();
    $this->rootUrl = rtrim($rootUrl ?: $this->client->getConfig('base_path'), '/');
    $this->batchPath = $batchPath ?: self::BATCH_PATH;
  }

  public function add(RequestInterface $request, $key = false)
  {
    if (false == $key) {
      $key = mt_rand();
    }

    $this->requests[$key] = $request;
  }

  public function execute()
  {
    $body = '';
    $classes = array();
    $batchHttpTemplate = <<<EOF
--%s
Content-Type: application/http
Content-Transfer-Encoding: binary
MIME-Version: 1.0
Content-ID: %s

%s
%s%s


EOF;

    /** @var WPvivid_Google_Http_Request $req */
    foreach ($this->requests as $key => $request) {
      $firstLine = sprintf(
          '%s %s HTTP/%s',
          $request->getMethod(),
          $request->getRequestTarget(),
          $request->getProtocolVersion()
      );

      $content = (string) $request->getBody();

      $headers = '';
      foreach ($request->getHeaders() as $name => $values) {
          $headers .= sprintf("%s:%s\r\n", $name, implode(', ', $values));
      }

      $body .= sprintf(
          $batchHttpTemplate,
          $this->boundary,
          $key,
          $firstLine,
          $headers,
          $content ? "\n".$content : ''
      );

      $classes['response-' . $key] = $request->getHeaderLine('X-Php-Expected-Class');
    }

    $body .= "--{$this->boundary}--";
    $body = trim($body);
    $url = $this->rootUrl . '/' . $this->batchPath;
    $headers = array(
      'Content-Type' => sprintf('multipart/mixed; boundary=%s', $this->boundary),
      'Content-Length' => strlen($body),
    );

    $request = new Request(
        'POST',
        $url,
        $headers,
        $body
    );

    $response = $this->client->execute($request);

    return $this->parseResponse($response, $classes);
  }

  public function parseResponse(ResponseInterface $response, $classes = array())
  {
    $contentType = $response->getHeaderLine('content-type');
    $contentType = explode(';', $contentType);
    $boundary = false;
    foreach ($contentType as $part) {
      $part = explode('=', $part, 2);
      if (isset($part[0]) && 'boundary' == trim($part[0])) {
        $boundary = $part[1];
      }
    }

    $body = (string) $response->getBody();
    if (!empty($body)) {
      $body = str_replace("--$boundary--", "--$boundary", $body);
      $parts = explode("--$boundary", $body);
      $responses = array();
      $requests = array_values($this->requests);

      foreach ($parts as $i => $part) {
        $part = trim($part);
        if (!empty($part)) {
          list($rawHeaders, $part) = explode("\r\n\r\n", $part, 2);
          $headers = $this->parseRawHeaders($rawHeaders);

          $status = substr($part, 0, strpos($part, "\n"));
          $status = explode(" ", $status);
          $status = $status[1];

          list($partHeaders, $partBody) = $this->parseHttpResponse($part, false);
          $response = new Response(
              $status,
              $partHeaders,
              Psr7\stream_for($partBody)
          );

          // Need content id.
          $key = $headers['content-id'];

          try {
            $response = WPvivid_Google_Http_REST::decodeHttpResponse($response, $requests[$i-1]);
          } catch (WPvivid_Google_Service_Exception $e) {
            // Store the exception as the response, so successful responses
            // can be processed.
            $response = $e;
          }

          $responses[$key] = $response;
        }
      }

      return $responses;
    }

    return null;
  }

  private function parseRawHeaders($rawHeaders)
  {
    $headers = array();
    $responseHeaderLines = explode("\r\n", $rawHeaders);
    foreach ($responseHeaderLines as $headerLine) {
      if ($headerLine && strpos($headerLine, ':') !== false) {
        list($header, $value) = explode(': ', $headerLine, 2);
        $header = strtolower($header);
        if (isset($headers[$header])) {
          $headers[$header] .= "\n" . $value;
        } else {
          $headers[$header] = $value;
        }
      }
    }
    return $headers;
  }

  /**
   * Used by the IO lib and also the batch processing.
   *
   * @param $respData
   * @param $headerSize
   * @return array
   */
  private function parseHttpResponse($respData, $headerSize)
  {
    // check proxy header
    foreach (self::$CONNECTION_ESTABLISHED_HEADERS as $established_header) {
      if (stripos($respData, $established_header) !== false) {
        // existed, remove it
        $respData = str_ireplace($established_header, '', $respData);
        // Subtract the proxy header size unless the cURL bug prior to 7.30.0
        // is present which prevented the proxy header size from being taken into
        // account.
        // @TODO look into this
        // if (!$this->needsQuirk()) {
        //   $headerSize -= strlen($established_header);
        // }
        break;
      }
    }

    if ($headerSize) {
      $responseBody = substr($respData, $headerSize);
      $responseHeaders = substr($respData, 0, $headerSize);
    } else {
      $responseSegments = explode("\r\n\r\n", $respData, 2);
      $responseHeaders = $responseSegments[0];
      $responseBody = isset($responseSegments[1]) ? $responseSegments[1] :
                                                    null;
    }

    $responseHeaders = $this->parseRawHeaders($responseHeaders);

    return array($responseHeaders, $responseBody);
  }
}
includes/lib2/google-api-php-client/src/WPvivid/Google/Http/MediaFileUpload.php000064400000022453151327705670023320 0ustar00<?php
/**
 * Copyright 2012 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

use WPvividGuzzleHttp\Psr7;
use WPvividGuzzleHttp\Psr7\Request;
use WPvividGuzzleHttp\Psr7\Uri;
use WPvividPsr\Http\Message\RequestInterface;

/**
 * Manage large file uploads, which may be media but can be any type
 * of sizable data.
 */
class WPvivid_Google_Http_MediaFileUpload
{
  const UPLOAD_MEDIA_TYPE = 'media';
  const UPLOAD_MULTIPART_TYPE = 'multipart';
  const UPLOAD_RESUMABLE_TYPE = 'resumable';

  /** @var string $mimeType */
  private $mimeType;

  /** @var string $data */
  private $data;

  /** @var bool $resumable */
  private $resumable;

  /** @var int $chunkSize */
  private $chunkSize;

  /** @var int $size */
  private $size;

  /** @var string $resumeUri */
  private $resumeUri;

  /** @var int $progress */
  private $progress;

  /** @var WPvivid_Google_Client */
  private $client;

  /** @var WPvividPsr\Http\Message\RequestInterface */
  private $request;

  /** @var string */
  private $boundary;

  /**
   * Result code from last HTTP call
   * @var int
   */
  private $httpResultCode;

  /**
   * @param $client WPvivid_Google_Client
   * @param $request RequestInterface
   * @param $mimeType string
   * @param $data string The bytes you want to upload.
   * @param $resumable bool
   * @param bool $chunkSize File will be uploaded in chunks of this many bytes.
   * only used if resumable=True
   */
  public function __construct(
      WPvivid_Google_Client $client,
      RequestInterface $request,
      $mimeType,
      $data,
      $resumable = false,
      $chunkSize = false
  ) {
    $this->client = $client;
    $this->request = $request;
    $this->mimeType = $mimeType;
    $this->data = $data;
    $this->resumable = $resumable;
    $this->chunkSize = $chunkSize;
    $this->progress = 0;

    $this->process();
  }

  /**
   * Set the size of the file that is being uploaded.
   * @param $size - int file size in bytes
   */
  public function setFileSize($size)
  {
    $this->size = $size;
  }

  /**
   * Return the progress on the upload
   * @return int progress in bytes uploaded.
   */
  public function getProgress()
  {
    return $this->progress;
  }

  /**
   * Send the next part of the file to upload.
   * @param [$chunk] the next set of bytes to send. If false will used $data passed
   * at construct time.
   */
  public function nextChunk($chunk = false)
  {
    $resumeUri = $this->getResumeUri();

    if (false == $chunk) {
      $chunk = substr($this->data, $this->progress, $this->chunkSize);
    }

    $lastBytePos = $this->progress + strlen($chunk) - 1;
    $headers = array(
      'content-range' => "bytes $this->progress-$lastBytePos/$this->size",
      'content-length' => strlen($chunk),
      'expect' => '',
    );

    $request = new Request(
        'PUT',
        $resumeUri,
        $headers,
        Psr7\stream_for($chunk)
    );

    return $this->makePutRequest($request);
  }

  /**
   * Return the HTTP result code from the last call made.
   * @return int code
   */
  public function getHttpResultCode()
  {
    return $this->httpResultCode;
  }

  /**
  * Sends a PUT-Request to google drive and parses the response,
  * setting the appropiate variables from the response()
  *
  * @param WPvivid_Google_Http_Request $httpRequest the Reuqest which will be send
  *
  * @return false|mixed false when the upload is unfinished or the decoded http response
  *
  */
  private function makePutRequest(RequestInterface $request)
  {
    $response = $this->client->execute($request);
    $this->httpResultCode = $response->getStatusCode();

    if (308 == $this->httpResultCode) {
      // Track the amount uploaded.
      $range = $response->getHeaderLine('range');
      if ($range) {
        $range_array = explode('-', $range);
        $this->progress = $range_array[1] + 1;
      }

      // Allow for changing upload URLs.
      $location = $response->getHeaderLine('location');
      if ($location) {
        $this->resumeUri = $location;
      }

      // No problems, but upload not complete.
      return false;
    }

    return WPvivid_Google_Http_REST::decodeHttpResponse($response, $this->request);
  }

  /**
   * Resume a previously unfinished upload
   * @param $resumeUri resume-URI of the unfinished, resumable upload.
   */
  public function resume($resumeUri)
  {
     $this->resumeUri = $resumeUri;
     $headers = array(
       'content-range' => "bytes */$this->size",
       'content-length' => 0,
     );
     $httpRequest = new Request(
         'PUT',
         $this->resumeUri,
         $headers
     );

     return $this->makePutRequest($httpRequest);
  }

    public function setResumeUri($resumeUri)
    {
        $this->resumeUri = $resumeUri;
    }
    public function setProgress($progress)
    {
        $this->progress = $progress;
    }

  /**
   * @return WPvividPsr\Http\Message\RequestInterface $request
   * @visible for testing
   */
  private function process()
  {
    $this->transformToUploadUrl();
    $request = $this->request;

    $postBody = '';
    $contentType = false;

    $meta = (string) $request->getBody();
    $meta = is_string($meta) ? json_decode($meta, true) : $meta;

    $uploadType = $this->getUploadType($meta);
    $request = $request->withUri(
        Uri::withQueryValue($request->getUri(), 'uploadType', $uploadType)
    );

    $mimeType = $this->mimeType ?: $request->getHeaderLine('content-type');

    if (self::UPLOAD_RESUMABLE_TYPE == $uploadType) {
      $contentType = $mimeType;
      $postBody = is_string($meta) ? $meta : json_encode($meta);
    } else if (self::UPLOAD_MEDIA_TYPE == $uploadType) {
      $contentType = $mimeType;
      $postBody = $this->data;
    } else if (self::UPLOAD_MULTIPART_TYPE == $uploadType) {
      // This is a multipart/related upload.
      $boundary = $this->boundary ?: mt_rand();
      $boundary = str_replace('"', '', $boundary);
      $contentType = 'multipart/related; boundary=' . $boundary;
      $related = "--$boundary\r\n";
      $related .= "Content-Type: application/json; charset=UTF-8\r\n";
      $related .= "\r\n" . json_encode($meta) . "\r\n";
      $related .= "--$boundary\r\n";
      $related .= "Content-Type: $mimeType\r\n";
      $related .= "Content-Transfer-Encoding: base64\r\n";
      $related .= "\r\n" . base64_encode($this->data) . "\r\n";
      $related .= "--$boundary--";
      $postBody = $related;
    }

    $request = $request->withBody(Psr7\stream_for($postBody));

    if (isset($contentType) && $contentType) {
      $request = $request->withHeader('content-type', $contentType);
    }

    return $this->request = $request;
  }

  /**
   * Valid upload types:
   * - resumable (UPLOAD_RESUMABLE_TYPE)
   * - media (UPLOAD_MEDIA_TYPE)
   * - multipart (UPLOAD_MULTIPART_TYPE)
   * @param $meta
   * @return string
   * @visible for testing
   */
  public function getUploadType($meta)
  {
    if ($this->resumable) {
      return self::UPLOAD_RESUMABLE_TYPE;
    }

    if (false == $meta && $this->data) {
      return self::UPLOAD_MEDIA_TYPE;
    }

    return self::UPLOAD_MULTIPART_TYPE;
  }

  public function getResumeUri()
  {
    if (null === $this->resumeUri) {
      $this->resumeUri = $this->fetchResumeUri();
    }

    return $this->resumeUri;
  }

  private function fetchResumeUri()
  {
    $body = $this->request->getBody();
    if ($body) {
      $headers = array(
        'content-type' => 'application/json; charset=UTF-8',
        'content-length' => $body->getSize(),
        'x-upload-content-type' => $this->mimeType,
        'x-upload-content-length' => $this->size,
        'expect' => '',
      );
      foreach ($headers as $key => $value) {
        $this->request = $this->request->withHeader($key, $value);
      }
    }

    $response = $this->client->execute($this->request, false);
    $location = $response->getHeaderLine('location');
    $code = $response->getStatusCode();

    if (200 == $code && true == $location) {
      return $location;
    }

    $message = $code;
    $body = json_decode((string) $this->request->getBody(), true);
    if (isset($body['error']['errors'])) {
      $message .= ': ';
      foreach ($body['error']['errors'] as $error) {
        $message .= "{$error[domain]}, {$error[message]};";
      }
      $message = rtrim($message, ';');
    }

    $error = "Failed to start the resumable upload (HTTP {$message})";
    $this->client->getLogger()->error($error);

    throw new WPvivid_Google_Exception(esc_html($error));
  }

  private function transformToUploadUrl()
  {
    $parts = parse_url((string) $this->request->getUri());
    if (!isset($parts['path'])) {
      $parts['path'] = '';
    }
    $parts['path'] = '/upload' . $parts['path'];
    $uri = Uri::fromParts($parts);
    $this->request = $this->request->withUri($uri);
  }

  public function setChunkSize($chunkSize)
  {
    $this->chunkSize = $chunkSize;
  }

  public function getRequest()
  {
    return $this->request;
  }
}
includes/lib2/google-api-php-client/src/WPvivid/Google/Http/REST.php000064400000013126151327705670021106 0ustar00<?php
/*
 * Copyright 2010 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

use WPvividGoogle\Auth\HttpHandler\HttpHandlerFactory;
use WPvividGuzzleHttp\ClientInterface;
use WPvividGuzzleHttp\Exception\RequestException;
use WPvividGuzzleHttp\Psr7\Response;
use WPvividPsr\Http\Message\RequestInterface;
use WPvividPsr\Http\Message\ResponseInterface;

/**
 * This class implements the RESTful transport of apiServiceRequest()'s
 */
class WPvivid_Google_Http_REST
{
  /**
   * Executes a Psr\Http\Message\RequestInterface and (if applicable) automatically retries
   * when errors occur.
   *
   * @param WPvivid_Google_Client $client
   * @param WpvividPsr\Http\Message\RequestInterface $req
   * @return array decoded result
   * @throws WPvivid_Google_Service_Exception on server side error (ie: not authenticated,
   *  invalid or malformed post body, invalid url)
   */
  public static function execute(
      ClientInterface $client,
      RequestInterface $request,
      $expectedClass = null,
      $config = array(),
      $retryMap = null
  ) {
    $runner = new WPvivid_Google_Task_Runner(
        $config,
        sprintf('%s %s', $request->getMethod(), (string) $request->getUri()),
        array(get_class(), 'doExecute'),
        array($client, $request, $expectedClass)
    );

    if (null !== $retryMap) {
      $runner->setRetryMap($retryMap);
    }

    return $runner->run();
  }

  /**
   * Executes a Psr\Http\Message\RequestInterface
   *
   * @param WPvivid_Google_Client $client
   * @param WPvividPsr\Http\Message\RequestInterface $request
   * @return array decoded result
   * @throws WPvivid_Google_Service_Exception on server side error (ie: not authenticated,
   *  invalid or malformed post body, invalid url)
   */
  public static function doExecute(ClientInterface $client, RequestInterface $request, $expectedClass = null)
  {
    try {
      $httpHandler = HttpHandlerFactory::build($client);
      $response = $httpHandler($request);
    } catch (RequestException $e) {
      // if Guzzle throws an exception, catch it and handle the response
      if (!$e->hasResponse()) {
        throw $e;
      }

      $response = $e->getResponse();
      // specific checking for Guzzle 5: convert to PSR7 response
      if ($response instanceof \WPvividGuzzleHttp\Message\ResponseInterface) {
        $response = new Response(
            $response->getStatusCode(),
            $response->getHeaders() ?: [],
            $response->getBody(),
            $response->getProtocolVersion(),
            $response->getReasonPhrase()
        );
      }
    }

    return self::decodeHttpResponse($response, $request, $expectedClass);
  }

  /**
   * Decode an HTTP Response.
   * @static
   * @throws WPvivid_Google_Service_Exception
   * @param WPvividPsr\Http\Message\RequestInterface $response The http response to be decoded.
   * @param WPvividPsr\Http\Message\ResponseInterface $response
   * @return mixed|null
   */
  public static function decodeHttpResponse(
      ResponseInterface $response,
      RequestInterface $request = null,
      $expectedClass = null
  ) {
    $code = $response->getStatusCode();

    // retry strategy
    if (intVal($code) >= 400) {
      // if we errored out, it should be safe to grab the response body
      $body = (string) $response->getBody();

      // Check if we received errors, and add those to the Exception for convenience
      throw new WPvivid_Google_Service_Exception(esc_html($body), esc_attr($code), null, esc_html(self::getResponseErrors($body)));
    }

    // Ensure we only pull the entire body into memory if the request is not
    // of media type
    $body = self::decodeBody($response, $request);

    if ($expectedClass = self::determineExpectedClass($expectedClass, $request)) {
      $json = json_decode($body, true);

      return new $expectedClass($json);
    }

    return $response;
  }

  private static function decodeBody(ResponseInterface $response, RequestInterface $request = null)
  {
    if (self::isAltMedia($request)) {
      // don't decode the body, it's probably a really long string
      return '';
    }

    return (string) $response->getBody();
  }

  private static function determineExpectedClass($expectedClass, RequestInterface $request = null)
  {
    // "false" is used to explicitly prevent an expected class from being returned
    if (false === $expectedClass) {
      return null;
    }

    // if we don't have a request, we just use what's passed in
    if (null === $request) {
      return $expectedClass;
    }

    // return what we have in the request header if one was not supplied
    return $expectedClass ?: $request->getHeaderLine('X-Php-Expected-Class');
  }

  private static function getResponseErrors($body)
  {
    $json = json_decode($body, true);

    if (isset($json['error']['errors'])) {
      return $json['error']['errors'];
    }

    return null;
  }

  private static function isAltMedia(RequestInterface $request = null)
  {
    if ($request && $qs = $request->getUri()->getQuery()) {
      parse_str($qs, $query);
      if (isset($query['alt']) && $query['alt'] == 'media') {
        return true;
      }
    }

    return false;
  }
}
includes/lib2/google-api-php-client/src/WPvivid/Google/Collection.php000064400000005007151327705670021504 0ustar00<?php

if (!class_exists('WPvivid_Google_Client')) {
  require_once __DIR__ . '/autoload.php';
}

/**
 * Extension to the regular WPvivid_Google_Model that automatically
 * exposes the items array for iteration, so you can just
 * iterate over the object rather than a reference inside.
 */
class WPvivid_Google_Collection extends WPvivid_Google_Model implements Iterator, Countable
{
  protected $collection_key = 'items';

  #[\ReturnTypeWillChange]
  public function rewind()
  {
    if (isset($this->{$this->collection_key})
        && is_array($this->{$this->collection_key})) {
      reset($this->{$this->collection_key});
    }
  }

  #[\ReturnTypeWillChange]
  public function current()
  {
    $this->coerceType($this->key());
    if (is_array($this->{$this->collection_key})) {
      return current($this->{$this->collection_key});
    }
  }

  #[\ReturnTypeWillChange]
  public function key()
  {
    if (isset($this->{$this->collection_key})
        && is_array($this->{$this->collection_key})) {
      return key($this->{$this->collection_key});
    }
  }

  #[\ReturnTypeWillChange]
  public function next()
  {
    return next($this->{$this->collection_key});
  }

  #[\ReturnTypeWillChange]
  public function valid()
  {
    $key = $this->key();
    return $key !== null && $key !== false;
  }

  #[\ReturnTypeWillChange]
  public function count()
  {
    if (!isset($this->{$this->collection_key})) {
      return 0;
    }
    return count($this->{$this->collection_key});
  }

  public function offsetExists($offset)
  {
    if (!is_numeric($offset)) {
      return parent::offsetExists($offset);
    }
    return isset($this->{$this->collection_key}[$offset]);
  }

  public function offsetGet($offset)
  {
    if (!is_numeric($offset)) {
      return parent::offsetGet($offset);
    }
    $this->coerceType($offset);
    return $this->{$this->collection_key}[$offset];
  }

  public function offsetSet($offset, $value)
  {
    if (!is_numeric($offset)) {
      return parent::offsetSet($offset, $value);
    }
    $this->{$this->collection_key}[$offset] = $value;
  }

  public function offsetUnset($offset)
  {
    if (!is_numeric($offset)) {
      return parent::offsetUnset($offset);
    }
    unset($this->{$this->collection_key}[$offset]);
  }

  private function coerceType($offset)
  {
    $keyType = $this->keyType($this->collection_key);
    if ($keyType && !is_object($this->{$this->collection_key}[$offset])) {
      $this->{$this->collection_key}[$offset] =
          new $keyType($this->{$this->collection_key}[$offset]);
    }
  }
}
includes/lib2/google-api-php-client/src/WPvivid/Google/autoload.php000064400000001345151327705670021222 0ustar00<?php

/**
 * THIS FILE IS FOR BACKWARDS COMPATIBLITY ONLY
 *
 * If you were not already including this file in your project, please ignore it
 */

$file = __DIR__ . '/../../vendor/autoload.php';

if (!file_exists($file)) {
  $exception = 'This library must be installed via composer or by downloading the full package.';
  $exception .= ' See the instructions at https://github.com/google/google-api-php-client#installation.';
  throw new Exception(esc_html($exception));
}

$error = 'google-api-php-client\'s autoloader was moved to vendor/autoload.php in 2.0.0. This ';
$error .= 'redirect will be removed in 2.1. Please adjust your code to use the new location.';
trigger_error(esc_html($error), E_USER_DEPRECATED);

require_once $file;
includes/lib/google-api-php-client/src/Google/Utils/UriTemplate.php000064400000022346151327705670021277 0ustar00<?php
/*
 * Copyright 2013 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * Implementation of levels 1-3 of the URI Template spec.
 * @see http://tools.ietf.org/html/rfc6570
 */
class Google_Utils_UriTemplate
{
  const TYPE_MAP = "1";
  const TYPE_LIST = "2";
  const TYPE_SCALAR = "4";

  /**
   * @var $operators array
   * These are valid at the start of a template block to
   * modify the way in which the variables inside are
   * processed.
   */
  private $operators = array(
      "+" => "reserved",
      "/" => "segments",
      "." => "dotprefix",
      "#" => "fragment",
      ";" => "semicolon",
      "?" => "form",
      "&" => "continuation"
  );

  /**
   * @var reserved array
   * These are the characters which should not be URL encoded in reserved
   * strings.
   */
  private $reserved = array(
      "=", ",", "!", "@", "|", ":", "/", "?", "#",
      "[", "]",'$', "&", "'", "(", ")", "*", "+", ";"
  );
  private $reservedEncoded = array(
    "%3D", "%2C", "%21", "%40", "%7C", "%3A", "%2F", "%3F",
    "%23", "%5B", "%5D", "%24", "%26", "%27", "%28", "%29",
    "%2A", "%2B", "%3B"
  );

  public function parse($string, array $parameters)
  {
    return $this->resolveNextSection($string, $parameters);
  }

  /**
   * This function finds the first matching {...} block and
   * executes the replacement. It then calls itself to find
   * subsequent blocks, if any.
   */
  private function resolveNextSection($string, $parameters)
  {
    $start = strpos($string, "{");
    if ($start === false) {
      return $string;
    }
    $end = strpos($string, "}");
    if ($end === false) {
      return $string;
    }
    $string = $this->replace($string, $start, $end, $parameters);
    return $this->resolveNextSection($string, $parameters);
  }

  private function replace($string, $start, $end, $parameters)
  {
    // We know a data block will have {} round it, so we can strip that.
    $data = substr($string, $start + 1, $end - $start - 1);

    // If the first character is one of the reserved operators, it effects
    // the processing of the stream.
    if (isset($this->operators[$data[0]])) {
      $op = $this->operators[$data[0]];
      $data = substr($data, 1);
      $prefix = "";
      $prefix_on_missing = false;

      switch ($op) {
        case "reserved":
          // Reserved means certain characters should not be URL encoded
          $data = $this->replaceVars($data, $parameters, ",", null, true);
          break;
        case "fragment":
          // Comma separated with fragment prefix. Bare values only.
          $prefix = "#";
          $prefix_on_missing = true;
          $data = $this->replaceVars($data, $parameters, ",", null, true);
          break;
        case "segments":
          // Slash separated data. Bare values only.
          $prefix = "/";
          $data =$this->replaceVars($data, $parameters, "/");
          break;
        case "dotprefix":
          // Dot separated data. Bare values only.
          $prefix = ".";
          $prefix_on_missing = true;
          $data = $this->replaceVars($data, $parameters, ".");
          break;
        case "semicolon":
          // Semicolon prefixed and separated. Uses the key name
          $prefix = ";";
          $data = $this->replaceVars($data, $parameters, ";", "=", false, true, false);
          break;
        case "form":
          // Standard URL format. Uses the key name
          $prefix = "?";
          $data = $this->replaceVars($data, $parameters, "&", "=");
          break;
        case "continuation":
          // Standard URL, but with leading ampersand. Uses key name.
          $prefix = "&";
          $data = $this->replaceVars($data, $parameters, "&", "=");
          break;
      }

      // Add the initial prefix character if data is valid.
      if ($data || ($data !== false && $prefix_on_missing)) {
        $data = $prefix . $data;
      }

    } else {
      // If no operator we replace with the defaults.
      $data = $this->replaceVars($data, $parameters);
    }
    // This is chops out the {...} and replaces with the new section.
    return substr($string, 0, $start) . $data . substr($string, $end + 1);
  }

  private function replaceVars(
      $section,
      $parameters,
      $sep = ",",
      $combine = null,
      $reserved = false,
      $tag_empty = false,
      $combine_on_empty = true
  ) {
    if (strpos($section, ",") === false) {
      // If we only have a single value, we can immediately process.
      return $this->combine(
          $section,
          $parameters,
          $sep,
          $combine,
          $reserved,
          $tag_empty,
          $combine_on_empty
      );
    } else {
      // If we have multiple values, we need to split and loop over them.
      // Each is treated individually, then glued together with the
      // separator character.
      $vars = explode(",", $section);
      return $this->combineList(
          $vars,
          $sep,
          $parameters,
          $combine,
          $reserved,
          false, // Never emit empty strings in multi-param replacements
          $combine_on_empty
      );
    }
  }

  public function combine(
      $key,
      $parameters,
      $sep,
      $combine,
      $reserved,
      $tag_empty,
      $combine_on_empty
  ) {
    $length = false;
    $explode = false;
    $skip_final_combine = false;
    $value = false;

    // Check for length restriction.
    if (strpos($key, ":") !== false) {
      list($key, $length) = explode(":", $key);
    }

    // Check for explode parameter.
    if ($key[strlen($key) - 1] == "*") {
      $explode = true;
      $key = substr($key, 0, -1);
      $skip_final_combine = true;
    }

    // Define the list separator.
    $list_sep = $explode ? $sep : ",";

    if (isset($parameters[$key])) {
      $data_type = $this->getDataType($parameters[$key]);
      switch ($data_type) {
        case self::TYPE_SCALAR:
          $value = $this->getValue($parameters[$key], $length);
          break;
        case self::TYPE_LIST:
          $values = array();
          foreach ($parameters[$key] as $pkey => $pvalue) {
            $pvalue = $this->getValue($pvalue, $length);
            if ($combine && $explode) {
              $values[$pkey] = $key . $combine . $pvalue;
            } else {
              $values[$pkey] = $pvalue;
            }
          }
          $value = implode($list_sep, $values);
          if ($value == '') {
            return '';
          }
          break;
        case self::TYPE_MAP:
          $values = array();
          foreach ($parameters[$key] as $pkey => $pvalue) {
            $pvalue = $this->getValue($pvalue, $length);
            if ($explode) {
              $pkey = $this->getValue($pkey, $length);
              $values[] = $pkey . "=" . $pvalue; // Explode triggers = combine.
            } else {
              $values[] = $pkey;
              $values[] = $pvalue;
            }
          }
          $value = implode($list_sep, $values);
          if ($value == '') {
            return false;
          }
          break;
      }
    } else if ($tag_empty) {
      // If we are just indicating empty values with their key name, return that.
      return $key;
    } else {
      // Otherwise we can skip this variable due to not being defined.
      return false;
    }

    if ($reserved) {
      $value = str_replace($this->reservedEncoded, $this->reserved, $value);
    }

    // If we do not need to include the key name, we just return the raw
    // value.
    if (!$combine || $skip_final_combine) {
      return $value;
    }

    // Else we combine the key name: foo=bar, if value is not the empty string.
    return $key . ($value != '' || $combine_on_empty ? $combine . $value : '');
  }

  /**
   * Return the type of a passed in value
   */
  private function getDataType($data)
  {
    if (is_array($data)) {
      reset($data);
      if (key($data) !== 0) {
        return self::TYPE_MAP;
      }
      return self::TYPE_LIST;
    }
    return self::TYPE_SCALAR;
  }

  /**
   * Utility function that merges multiple combine calls
   * for multi-key templates.
   */
  private function combineList(
      $vars,
      $sep,
      $parameters,
      $combine,
      $reserved,
      $tag_empty,
      $combine_on_empty
  ) {
    $ret = array();
    foreach ($vars as $var) {
      $response = $this->combine(
          $var,
          $parameters,
          $sep,
          $combine,
          $reserved,
          $tag_empty,
          $combine_on_empty
      );
      if ($response === false) {
        continue;
      }
      $ret[] = $response;
    }
    return implode($sep, $ret);
  }

  /**
   * Utility function to encode and trim values
   */
  private function getValue($value, $length)
  {
    if ($length) {
      $value = substr($value, 0, $length);
    }
    $value = rawurlencode($value);
    return $value;
  }
}
includes/lib/google-api-php-client/src/Google/AuthHandler/AuthHandlerFactory.php000064400000002523151327705670023665 0ustar00<?php
/**
 * Copyright 2015 Google Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

use WPvividGuzzleHttp\Client;
use WPvividGuzzleHttp\ClientInterface;

class Google_AuthHandler_AuthHandlerFactory
{
  /**
   * Builds out a default http handler for the installed version of guzzle.
   *
   * @return Google_AuthHandler_Guzzle5AuthHandler|Google_AuthHandler_Guzzle6AuthHandler
   * @throws Exception
   */
  public static function build($cache = null, array $cacheConfig = [])
  {
    $version = ClientInterface::VERSION;

    switch ($version[0]) {
      case '5':
        return new Google_AuthHandler_Guzzle5AuthHandler($cache, $cacheConfig);
      case '6':
        return new Google_AuthHandler_Guzzle6AuthHandler($cache, $cacheConfig);
      default:
        throw new Exception('Version not supported');
    }
  }
}
includes/lib/google-api-php-client/src/Google/AuthHandler/Guzzle5AuthHandler.php000064400000005050151327705670023621 0ustar00<?php

use WPvividGoogle\Auth\CredentialsLoader;
use WPvividGoogle\Auth\HttpHandler\HttpHandlerFactory;
use WPvividGoogle\Auth\FetchAuthTokenCache;
use WPvividGoogle\Auth\Subscriber\AuthTokenSubscriber;
use WPvividGoogle\Auth\Subscriber\ScopedAccessTokenSubscriber;
use WPvividGoogle\Auth\Subscriber\SimpleSubscriber;
use WPvividGuzzleHttp\Client;
use WPvividGuzzleHttp\ClientInterface;
use WPvividPsr\Cache\CacheItemPoolInterface;

/**
*
*/
class Google_AuthHandler_Guzzle5AuthHandler
{
  protected $cache;
  protected $cacheConfig;

  public function __construct(CacheItemPoolInterface $cache = null, array $cacheConfig = [])
  {
    $this->cache = $cache;
    $this->cacheConfig = $cacheConfig;
  }

  public function attachCredentials(
      ClientInterface $http,
      CredentialsLoader $credentials,
      callable $tokenCallback = null
  ) {
    // use the provided cache
    if ($this->cache) {
      $credentials = new FetchAuthTokenCache(
          $credentials,
          $this->cacheConfig,
          $this->cache
      );
    }
    // if we end up needing to make an HTTP request to retrieve credentials, we
    // can use our existing one, but we need to throw exceptions so the error
    // bubbles up.
    $authHttp = $this->createAuthHttp($http);
    $authHttpHandler = HttpHandlerFactory::build($authHttp);
    $subscriber = new AuthTokenSubscriber(
        $credentials,
        $authHttpHandler,
        $tokenCallback
    );

    $http->setDefaultOption('auth', 'google_auth');
    $http->getEmitter()->attach($subscriber);

    return $http;
  }

  public function attachToken(ClientInterface $http, array $token, array $scopes)
  {
    $tokenFunc = function ($scopes) use ($token) {
      return $token['access_token'];
    };

    $subscriber = new ScopedAccessTokenSubscriber(
        $tokenFunc,
        $scopes,
        $this->cacheConfig,
        $this->cache
    );

    $http->setDefaultOption('auth', 'scoped');
    $http->getEmitter()->attach($subscriber);

    return $http;
  }

  public function attachKey(ClientInterface $http, $key)
  {
    $subscriber = new SimpleSubscriber(['key' => $key]);

    $http->setDefaultOption('auth', 'simple');
    $http->getEmitter()->attach($subscriber);

    return $http;
  }

  private function createAuthHttp(ClientInterface $http)
  {
    return new Client(
        [
          'base_url' => $http->getBaseUrl(),
          'defaults' => [
            'exceptions' => true,
            'verify' => $http->getDefaultOption('verify'),
            'proxy' => $http->getDefaultOption('proxy'),
          ]
        ]
    );
  }
}
includes/lib/google-api-php-client/src/Google/AuthHandler/Guzzle6AuthHandler.php000064400000005504151327705670023626 0ustar00<?php

use WPvividGoogle\Auth\CredentialsLoader;
use WPvividGoogle\Auth\HttpHandler\HttpHandlerFactory;
use WPvividGoogle\Auth\FetchAuthTokenCache;
use WPvividGoogle\Auth\Middleware\AuthTokenMiddleware;
use WPvividGoogle\Auth\Middleware\ScopedAccessTokenMiddleware;
use WPvividGoogle\Auth\Middleware\SimpleMiddleware;
use WPvividGuzzleHttp\Client;
use WPvividGuzzleHttp\ClientInterface;
use WPvividPsr\Cache\CacheItemPoolInterface;

/**
*
*/
class Google_AuthHandler_Guzzle6AuthHandler
{
  protected $cache;
  protected $cacheConfig;

  public function __construct(CacheItemPoolInterface $cache = null, array $cacheConfig = [])
  {
    $this->cache = $cache;
    $this->cacheConfig = $cacheConfig;
  }

  public function attachCredentials(
      ClientInterface $http,
      CredentialsLoader $credentials,
      callable $tokenCallback = null
  ) {
    // use the provided cache
    if ($this->cache) {
      $credentials = new FetchAuthTokenCache(
          $credentials,
          $this->cacheConfig,
          $this->cache
      );
    }
    // if we end up needing to make an HTTP request to retrieve credentials, we
    // can use our existing one, but we need to throw exceptions so the error
    // bubbles up.
    $authHttp = $this->createAuthHttp($http);
    $authHttpHandler = HttpHandlerFactory::build($authHttp);
    $middleware = new AuthTokenMiddleware(
        $credentials,
        $authHttpHandler,
        $tokenCallback
    );

    $config = $http->getConfig();
    $config['handler']->remove('google_auth');
    $config['handler']->push($middleware, 'google_auth');
    $config['auth'] = 'google_auth';
    $http = new Client($config);

    return $http;
  }

  public function attachToken(ClientInterface $http, array $token, array $scopes)
  {
    $tokenFunc = function ($scopes) use ($token) {
      return $token['access_token'];
    };

    $middleware = new ScopedAccessTokenMiddleware(
        $tokenFunc,
        $scopes,
        $this->cacheConfig,
        $this->cache
    );

    $config = $http->getConfig();
    $config['handler']->remove('google_auth');
    $config['handler']->push($middleware, 'google_auth');
    $config['auth'] = 'scoped';
    $http = new Client($config);

    return $http;
  }

  public function attachKey(ClientInterface $http, $key)
  {
    $middleware = new SimpleMiddleware(['key' => $key]);

    $config = $http->getConfig();
    $config['handler']->remove('google_auth');
    $config['handler']->push($middleware, 'google_auth');
    $config['auth'] = 'simple';
    $http = new Client($config);

    return $http;
  }

  private function createAuthHttp(ClientInterface $http)
  {
    return new Client(
        [
          'base_uri' => $http->getConfig('base_uri'),
          'exceptions' => true,
          'verify' => $http->getConfig('verify'),
          'proxy' => $http->getConfig('proxy'),
        ]
    );
  }
}
includes/lib/google-api-php-client/src/Google/Collection.php000064400000004505151327705670020034 0ustar00<?php

if (!class_exists('Google_Client')) {
  require_once __DIR__ . '/autoload.php';
}

/**
 * Extension to the regular Google_Model that automatically
 * exposes the items array for iteration, so you can just
 * iterate over the object rather than a reference inside.
 */
class Google_Collection extends Google_Model implements Iterator, Countable
{
  protected $collection_key = 'items';

  public function rewind()
  {
    if (isset($this->{$this->collection_key})
        && is_array($this->{$this->collection_key})) {
      reset($this->{$this->collection_key});
    }
  }

  public function current()
  {
    $this->coerceType($this->key());
    if (is_array($this->{$this->collection_key})) {
      return current($this->{$this->collection_key});
    }
  }

  public function key()
  {
    if (isset($this->{$this->collection_key})
        && is_array($this->{$this->collection_key})) {
      return key($this->{$this->collection_key});
    }
  }

  public function next()
  {
    return next($this->{$this->collection_key});
  }

  public function valid()
  {
    $key = $this->key();
    return $key !== null && $key !== false;
  }

  public function count()
  {
    if (!isset($this->{$this->collection_key})) {
      return 0;
    }
    return count($this->{$this->collection_key});
  }

  public function offsetExists($offset)
  {
    if (!is_numeric($offset)) {
      return parent::offsetExists($offset);
    }
    return isset($this->{$this->collection_key}[$offset]);
  }

  public function offsetGet($offset)
  {
    if (!is_numeric($offset)) {
      return parent::offsetGet($offset);
    }
    $this->coerceType($offset);
    return $this->{$this->collection_key}[$offset];
  }

  public function offsetSet($offset, $value)
  {
    if (!is_numeric($offset)) {
      return parent::offsetSet($offset, $value);
    }
    $this->{$this->collection_key}[$offset] = $value;
  }

  public function offsetUnset($offset)
  {
    if (!is_numeric($offset)) {
      return parent::offsetUnset($offset);
    }
    unset($this->{$this->collection_key}[$offset]);
  }

  private function coerceType($offset)
  {
    $keyType = $this->keyType($this->collection_key);
    if ($keyType && !is_object($this->{$this->collection_key}[$offset])) {
      $this->{$this->collection_key}[$offset] =
          new $keyType($this->{$this->collection_key}[$offset]);
    }
  }
}
includes/lib/google-api-php-client/src/Google/Http/MediaFileUpload.php000064400000022241151327705670021641 0ustar00<?php
/**
 * Copyright 2012 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

use WPvividGuzzleHttp\Psr7;
use WPvividGuzzleHttp\Psr7\Request;
use WPvividGuzzleHttp\Psr7\Uri;
use WPvividPsr\Http\Message\RequestInterface;

/**
 * Manage large file uploads, which may be media but can be any type
 * of sizable data.
 */
class Google_Http_MediaFileUpload
{
  const UPLOAD_MEDIA_TYPE = 'media';
  const UPLOAD_MULTIPART_TYPE = 'multipart';
  const UPLOAD_RESUMABLE_TYPE = 'resumable';

  /** @var string $mimeType */
  private $mimeType;

  /** @var string $data */
  private $data;

  /** @var bool $resumable */
  private $resumable;

  /** @var int $chunkSize */
  private $chunkSize;

  /** @var int $size */
  private $size;

  /** @var string $resumeUri */
  private $resumeUri;

  /** @var int $progress */
  private $progress;

  /** @var Google_Client */
  private $client;

  /** @var Psr\Http\Message\RequestInterface */
  private $request;

  /** @var string */
  private $boundary;

  /**
   * Result code from last HTTP call
   * @var int
   */
  private $httpResultCode;

  /**
   * @param $mimeType string
   * @param $data string The bytes you want to upload.
   * @param $resumable bool
   * @param bool $chunkSize File will be uploaded in chunks of this many bytes.
   * only used if resumable=True
   */
  public function __construct(
      Google_Client $client,
      RequestInterface $request,
      $mimeType,
      $data,
      $resumable = false,
      $chunkSize = false
  ) {
    $this->client = $client;
    $this->request = $request;
    $this->mimeType = $mimeType;
    $this->data = $data;
    $this->resumable = $resumable;
    $this->chunkSize = $chunkSize;
    $this->progress = 0;

    $this->process();
  }

  /**
   * Set the size of the file that is being uploaded.
   * @param $size - int file size in bytes
   */
  public function setFileSize($size)
  {
    $this->size = $size;
  }

  /**
   * Return the progress on the upload
   * @return int progress in bytes uploaded.
   */
  public function getProgress()
  {
    return $this->progress;
  }

  /**
   * Send the next part of the file to upload.
   * @param [$chunk] the next set of bytes to send. If false will used $data passed
   * at construct time.
   */
  public function nextChunk($chunk = false)
  {
    $resumeUri = $this->getResumeUri();

    if (false == $chunk) {
      $chunk = substr($this->data, $this->progress, $this->chunkSize);
    }

    $lastBytePos = $this->progress + strlen($chunk) - 1;
    $headers = array(
      'content-range' => "bytes $this->progress-$lastBytePos/$this->size",
      'content-length' => strlen($chunk),
      'expect' => '',
    );

    $request = new Request(
        'PUT',
        $resumeUri,
        $headers,
        Psr7\stream_for($chunk)
    );

    return $this->makePutRequest($request);
  }

  /**
   * Return the HTTP result code from the last call made.
   * @return int code
   */
  public function getHttpResultCode()
  {
    return $this->httpResultCode;
  }

  /**
  * Sends a PUT-Request to google drive and parses the response,
  * setting the appropiate variables from the response()
  *
  * @param Google_Http_Request $httpRequest the Reuqest which will be send
  *
  * @return false|mixed false when the upload is unfinished or the decoded http response
  *
  */
  private function makePutRequest(RequestInterface $request)
  {
    $response = $this->client->execute($request);
    $this->httpResultCode = $response->getStatusCode();

    if (308 == $this->httpResultCode) {
      // Track the amount uploaded.
      $range = $response->getHeaderLine('range');
      if ($range) {
        $range_array = explode('-', $range);
        $this->progress = $range_array[1] + 1;
      }

      // Allow for changing upload URLs.
      $location = $response->getHeaderLine('location');
      if ($location) {
        $this->resumeUri = $location;
      }

      // No problems, but upload not complete.
      return false;
    }

    return Google_Http_REST::decodeHttpResponse($response, $this->request);
  }

  /**
   * Resume a previously unfinished upload
   * @param $resumeUri the resume-URI of the unfinished, resumable upload.
   */
  public function resume($resumeUri)
  {
     $this->resumeUri = $resumeUri;
     $headers = array(
       'content-range' => "bytes */$this->size",
       'content-length' => 0,
     );
     $httpRequest = new Request(
         'PUT',
         $this->resumeUri,
         $headers
     );

     return $this->makePutRequest($httpRequest);
  }

    public function setResumeUri($resumeUri)
    {
        $this->resumeUri = $resumeUri;
    }
    public function setProgress($progress)
    {
        $this->progress = $progress;
    }

  /**
   * @return Psr\Http\Message\RequestInterface $request
   * @visible for testing
   */
  private function process()
  {
    $this->transformToUploadUrl();
    $request = $this->request;

    $postBody = '';
    $contentType = false;

    $meta = (string) $request->getBody();
    $meta = is_string($meta) ? json_decode($meta, true) : $meta;

    $uploadType = $this->getUploadType($meta);
    $request = $request->withUri(
        Uri::withQueryValue($request->getUri(), 'uploadType', $uploadType)
    );

    $mimeType = $this->mimeType ?: $request->getHeaderLine('content-type');

    if (self::UPLOAD_RESUMABLE_TYPE == $uploadType) {
      $contentType = $mimeType;
      $postBody = is_string($meta) ? $meta : json_encode($meta);
    } else if (self::UPLOAD_MEDIA_TYPE == $uploadType) {
      $contentType = $mimeType;
      $postBody = $this->data;
    } else if (self::UPLOAD_MULTIPART_TYPE == $uploadType) {
      // This is a multipart/related upload.
      $boundary = $this->boundary ?: mt_rand();
      $boundary = str_replace('"', '', $boundary);
      $contentType = 'multipart/related; boundary=' . $boundary;
      $related = "--$boundary\r\n";
      $related .= "Content-Type: application/json; charset=UTF-8\r\n";
      $related .= "\r\n" . json_encode($meta) . "\r\n";
      $related .= "--$boundary\r\n";
      $related .= "Content-Type: $mimeType\r\n";
      $related .= "Content-Transfer-Encoding: base64\r\n";
      $related .= "\r\n" . base64_encode($this->data) . "\r\n";
      $related .= "--$boundary--";
      $postBody = $related;
    }

    $request = $request->withBody(Psr7\stream_for($postBody));

    if (isset($contentType) && $contentType) {
      $request = $request->withHeader('content-type', $contentType);
    }

    return $this->request = $request;
  }

  /**
   * Valid upload types:
   * - resumable (UPLOAD_RESUMABLE_TYPE)
   * - media (UPLOAD_MEDIA_TYPE)
   * - multipart (UPLOAD_MULTIPART_TYPE)
   * @param $meta
   * @return string
   * @visible for testing
   */
  public function getUploadType($meta)
  {
    if ($this->resumable) {
      return self::UPLOAD_RESUMABLE_TYPE;
    }

    if (false == $meta && $this->data) {
      return self::UPLOAD_MEDIA_TYPE;
    }

    return self::UPLOAD_MULTIPART_TYPE;
  }

  public function getResumeUri()
  {
    if (null === $this->resumeUri) {
      $this->resumeUri = $this->fetchResumeUri();
    }

    return $this->resumeUri;
  }

  private function fetchResumeUri()
  {
    $body = $this->request->getBody();
    if ($body) {
      $headers = array(
        'content-type' => 'application/json; charset=UTF-8',
        'content-length' => $body->getSize(),
        'x-upload-content-type' => $this->mimeType,
        'x-upload-content-length' => $this->size,
        'expect' => '',
      );
      foreach ($headers as $key => $value) {
        $this->request = $this->request->withHeader($key, $value);
      }
    }

    $response = $this->client->execute($this->request, false);
    $location = $response->getHeaderLine('location');
    $code = $response->getStatusCode();

    if (200 == $code && true == $location) {
      return $location;
    }

    $message = $code;
    $body = json_decode((string) $this->request->getBody(), true);
    if (isset($body['error']['errors'])) {
      $message .= ': ';
      foreach ($body['error']['errors'] as $error) {
        $message .= "{$error[domain]}, {$error[message]};";
      }
      $message = rtrim($message, ';');
    }

    $error = "Failed to start the resumable upload (HTTP {$message})";
    $this->client->getLogger()->error($error);

    throw new Google_Exception(esc_html($error));
  }

  private function transformToUploadUrl()
  {
    $parts = parse_url((string) $this->request->getUri());
    if (!isset($parts['path'])) {
      $parts['path'] = '';
    }
    $parts['path'] = '/upload' . $parts['path'];
    $uri = Uri::fromParts($parts);
    $this->request = $this->request->withUri($uri);
  }

  public function setChunkSize($chunkSize)
  {
    $this->chunkSize = $chunkSize;
  }

  public function getRequest()
  {
    return $this->request;
  }
}
includes/lib/google-api-php-client/src/Google/Http/Batch.php000064400000015426151327705670017705 0ustar00<?php
/*
 * Copyright 2012 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

use WPvividGuzzleHttp\Psr7;
use WPvividGuzzleHttp\Psr7\Request;
use WPvividGuzzleHttp\Psr7\Response;
use WPvividPsr\Http\Message\RequestInterface;
use WPvividPsr\Http\Message\ResponseInterface;

/**
 * Class to handle batched requests to the Google API service.
 */
class Google_Http_Batch
{
  const BATCH_PATH = 'batch';

  private static $CONNECTION_ESTABLISHED_HEADERS = array(
    "HTTP/1.0 200 Connection established\r\n\r\n",
    "HTTP/1.1 200 Connection established\r\n\r\n",
  );

  /** @var string Multipart Boundary. */
  private $boundary;

  /** @var array service requests to be executed. */
  private $requests = array();

  /** @var Google_Client */
  private $client;

  private $rootUrl;

  private $batchPath;

  public function __construct(
      Google_Client $client,
      $boundary = false,
      $rootUrl = null,
      $batchPath = null
  ) {
    $this->client = $client;
    $this->boundary = $boundary ?: mt_rand();
    $this->rootUrl = rtrim($rootUrl ?: $this->client->getConfig('base_path'), '/');
    $this->batchPath = $batchPath ?: self::BATCH_PATH;
  }

  public function add(RequestInterface $request, $key = false)
  {
    if (false == $key) {
      $key = mt_rand();
    }

    $this->requests[$key] = $request;
  }

  public function execute()
  {
    $body = '';
    $classes = array();
    $batchHttpTemplate = <<<EOF
--%s
Content-Type: application/http
Content-Transfer-Encoding: binary
MIME-Version: 1.0
Content-ID: %s

%s
%s%s


EOF;

    /** @var Google_Http_Request $req */
    foreach ($this->requests as $key => $request) {
      $firstLine = sprintf(
          '%s %s HTTP/%s',
          $request->getMethod(),
          $request->getRequestTarget(),
          $request->getProtocolVersion()
      );

      $content = (string) $request->getBody();

      $headers = '';
      foreach ($request->getHeaders() as $name => $values) {
          $headers .= sprintf("%s:%s\r\n", $name, implode(', ', $values));
      }

      $body .= sprintf(
          $batchHttpTemplate,
          $this->boundary,
          $key,
          $firstLine,
          $headers,
          $content ? "\n".$content : ''
      );

      $classes['response-' . $key] = $request->getHeaderLine('X-Php-Expected-Class');
    }

    $body .= "--{$this->boundary}--";
    $body = trim($body);
    $url = $this->rootUrl . '/' . $this->batchPath;
    $headers = array(
      'Content-Type' => sprintf('multipart/mixed; boundary=%s', $this->boundary),
      'Content-Length' => strlen($body),
    );

    $request = new Request(
        'POST',
        $url,
        $headers,
        $body
    );

    $response = $this->client->execute($request);

    return $this->parseResponse($response, $classes);
  }

  public function parseResponse(ResponseInterface $response, $classes = array())
  {
    $contentType = $response->getHeaderLine('content-type');
    $contentType = explode(';', $contentType);
    $boundary = false;
    foreach ($contentType as $part) {
      $part = explode('=', $part, 2);
      if (isset($part[0]) && 'boundary' == trim($part[0])) {
        $boundary = $part[1];
      }
    }

    $body = (string) $response->getBody();
    if (!empty($body)) {
      $body = str_replace("--$boundary--", "--$boundary", $body);
      $parts = explode("--$boundary", $body);
      $responses = array();
      $requests = array_values($this->requests);

      foreach ($parts as $i => $part) {
        $part = trim($part);
        if (!empty($part)) {
          list($rawHeaders, $part) = explode("\r\n\r\n", $part, 2);
          $headers = $this->parseRawHeaders($rawHeaders);

          $status = substr($part, 0, strpos($part, "\n"));
          $status = explode(" ", $status);
          $status = $status[1];

          list($partHeaders, $partBody) = $this->parseHttpResponse($part, false);
          $response = new Response(
              $status,
              $partHeaders,
              Psr7\stream_for($partBody)
          );

          // Need content id.
          $key = $headers['content-id'];

          try {
            $response = Google_Http_REST::decodeHttpResponse($response, $requests[$i-1]);
          } catch (Google_Service_Exception $e) {
            // Store the exception as the response, so successful responses
            // can be processed.
            $response = $e;
          }

          $responses[$key] = $response;
        }
      }

      return $responses;
    }

    return null;
  }

  private function parseRawHeaders($rawHeaders)
  {
    $headers = array();
    $responseHeaderLines = explode("\r\n", $rawHeaders);
    foreach ($responseHeaderLines as $headerLine) {
      if ($headerLine && strpos($headerLine, ':') !== false) {
        list($header, $value) = explode(': ', $headerLine, 2);
        $header = strtolower($header);
        if (isset($headers[$header])) {
          $headers[$header] .= "\n" . $value;
        } else {
          $headers[$header] = $value;
        }
      }
    }
    return $headers;
  }

  /**
   * Used by the IO lib and also the batch processing.
   *
   * @param $respData
   * @param $headerSize
   * @return array
   */
  private function parseHttpResponse($respData, $headerSize)
  {
    // check proxy header
    foreach (self::$CONNECTION_ESTABLISHED_HEADERS as $established_header) {
      if (stripos($respData, $established_header) !== false) {
        // existed, remove it
        $respData = str_ireplace($established_header, '', $respData);
        // Subtract the proxy header size unless the cURL bug prior to 7.30.0
        // is present which prevented the proxy header size from being taken into
        // account.
        // @TODO look into this
        // if (!$this->needsQuirk()) {
        //   $headerSize -= strlen($established_header);
        // }
        break;
      }
    }

    if ($headerSize) {
      $responseBody = substr($respData, $headerSize);
      $responseHeaders = substr($respData, 0, $headerSize);
    } else {
      $responseSegments = explode("\r\n\r\n", $respData, 2);
      $responseHeaders = $responseSegments[0];
      $responseBody = isset($responseSegments[1]) ? $responseSegments[1] :
                                                    null;
    }

    $responseHeaders = $this->parseRawHeaders($responseHeaders);

    return array($responseHeaders, $responseBody);
  }
}
includes/lib/google-api-php-client/src/Google/Http/REST.php000064400000012772151327705670017442 0ustar00<?php
/*
 * Copyright 2010 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

use WPvividGoogle\Auth\HttpHandler\HttpHandlerFactory;
use WPvividGuzzleHttp\ClientInterface;
use WPvividGuzzleHttp\Exception\RequestException;
use WPvividGuzzleHttp\Psr7\Response;
use WPvividPsr\Http\Message\RequestInterface;
use WPvividPsr\Http\Message\ResponseInterface;

/**
 * This class implements the RESTful transport of apiServiceRequest()'s
 */
class Google_Http_REST
{
  /**
   * Executes a Psr\Http\Message\RequestInterface and (if applicable) automatically retries
   * when errors occur.
   *
   * @param Google_Client $client
   * @param Psr\Http\Message\RequestInterface $req
   * @return array decoded result
   * @throws Google_Service_Exception on server side error (ie: not authenticated,
   *  invalid or malformed post body, invalid url)
   */
  public static function execute(
      ClientInterface $client,
      RequestInterface $request,
      $expectedClass = null,
      $config = array(),
      $retryMap = null
  ) {
    $runner = new Google_Task_Runner(
        $config,
        sprintf('%s %s', $request->getMethod(), (string) $request->getUri()),
        array(get_class(), 'doExecute'),
        array($client, $request, $expectedClass)
    );

    if (null !== $retryMap) {
      $runner->setRetryMap($retryMap);
    }

    return $runner->run();
  }

  /**
   * Executes a Psr\Http\Message\RequestInterface
   *
   * @param Google_Client $client
   * @param Psr\Http\Message\RequestInterface $request
   * @return array decoded result
   * @throws Google_Service_Exception on server side error (ie: not authenticated,
   *  invalid or malformed post body, invalid url)
   */
  public static function doExecute(ClientInterface $client, RequestInterface $request, $expectedClass = null)
  {
    try {
      $httpHandler = HttpHandlerFactory::build($client);
      $response = $httpHandler($request);
    } catch (RequestException $e) {
      // if Guzzle throws an exception, catch it and handle the response
      if (!$e->hasResponse()) {
        throw $e;
      }

      $response = $e->getResponse();
      // specific checking for Guzzle 5: convert to PSR7 response
      if ($response instanceof \WPvividGuzzleHttp\Message\ResponseInterface) {
        $response = new Response(
            $response->getStatusCode(),
            $response->getHeaders() ?: [],
            $response->getBody(),
            $response->getProtocolVersion(),
            $response->getReasonPhrase()
        );
      }
    }

    return self::decodeHttpResponse($response, $request, $expectedClass);
  }

  /**
   * Decode an HTTP Response.
   * @static
   * @throws Google_Service_Exception
   * @param Psr\Http\Message\RequestInterface $response The http response to be decoded.
   * @param Psr\Http\Message\ResponseInterface $response
   * @return mixed|null
   */
  public static function decodeHttpResponse(
      ResponseInterface $response,
      RequestInterface $request = null,
      $expectedClass = null
  ) {
    $code = $response->getStatusCode();

    // retry strategy
    if (intVal($code) >= 400) {
      // if we errored out, it should be safe to grab the response body
      $body = (string) $response->getBody();

      // Check if we received errors, and add those to the Exception for convenience
      throw new Google_Service_Exception(esc_html($body), esc_attr($code), null, esc_html(self::getResponseErrors($body)));
    }

    // Ensure we only pull the entire body into memory if the request is not
    // of media type
    $body = self::decodeBody($response, $request);

    if ($expectedClass = self::determineExpectedClass($expectedClass, $request)) {
      $json = json_decode($body, true);

      return new $expectedClass($json);
    }

    return $response;
  }

  private static function decodeBody(ResponseInterface $response, RequestInterface $request = null)
  {
    if (self::isAltMedia($request)) {
      // don't decode the body, it's probably a really long string
      return '';
    }

    return (string) $response->getBody();
  }

  private static function determineExpectedClass($expectedClass, RequestInterface $request = null)
  {
    // "false" is used to explicitly prevent an expected class from being returned
    if (false === $expectedClass) {
      return null;
    }

    // if we don't have a request, we just use what's passed in
    if (null === $request) {
      return $expectedClass;
    }

    // return what we have in the request header if one was not supplied
    return $expectedClass ?: $request->getHeaderLine('X-Php-Expected-Class');
  }

  private static function getResponseErrors($body)
  {
    $json = json_decode($body, true);

    if (isset($json['error']['errors'])) {
      return $json['error']['errors'];
    }

    return null;
  }

  private static function isAltMedia(RequestInterface $request = null)
  {
    if ($request && $qs = $request->getUri()->getQuery()) {
      parse_str($qs, $query);
      if (isset($query['alt']) && $query['alt'] == 'media') {
        return true;
      }
    }

    return false;
  }
}
includes/lib/google-api-php-client/src/Google/Task/Retryable.php000064400000001343151327705670020571 0ustar00<?php
/*
 * Copyright 2014 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * Interface for checking how many times a given task can be retried following
 * a failure.
 */
interface Google_Task_Retryable
{
}
includes/lib/google-api-php-client/src/Google/Task/Exception.php000064400000001223151327705670020573 0ustar00<?php
/*
 * Copyright 2014 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

class Google_Task_Exception extends Google_Exception
{
}
includes/lib/google-api-php-client/src/Google/Task/Runner.php000064400000016045151327705670020116 0ustar00<?php
/*
 * Copyright 2014 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * A task runner with exponential backoff support.
 *
 * @see https://developers.google.com/drive/web/handle-errors#implementing_exponential_backoff
 */
class Google_Task_Runner
{
  const TASK_RETRY_NEVER = 0;
  const TASK_RETRY_ONCE = 1;
  const TASK_RETRY_ALWAYS = -1;

  /**
   * @var integer $maxDelay The max time (in seconds) to wait before a retry.
   */
  private $maxDelay = 60;
  /**
   * @var integer $delay The previous delay from which the next is calculated.
   */
  private $delay = 1;

  /**
   * @var integer $factor The base number for the exponential back off.
   */
  private $factor = 2;
  /**
   * @var float $jitter A random number between -$jitter and $jitter will be
   * added to $factor on each iteration to allow for a better distribution of
   * retries.
   */
  private $jitter = 0.5;

  /**
   * @var integer $attempts The number of attempts that have been tried so far.
   */
  private $attempts = 0;
  /**
   * @var integer $maxAttempts The max number of attempts allowed.
   */
  private $maxAttempts = 1;

  /**
   * @var callable $action The task to run and possibly retry.
   */
  private $action;
  /**
   * @var array $arguments The task arguments.
   */
  private $arguments;

  /**
   * @var array $retryMap Map of errors with retry counts.
   */
  protected $retryMap = [
    '500' => self::TASK_RETRY_ALWAYS,
    '503' => self::TASK_RETRY_ALWAYS,
    'rateLimitExceeded' => self::TASK_RETRY_ALWAYS,
    'userRateLimitExceeded' => self::TASK_RETRY_ALWAYS,
    6  => self::TASK_RETRY_ALWAYS,  // CURLE_COULDNT_RESOLVE_HOST
    7  => self::TASK_RETRY_ALWAYS,  // CURLE_COULDNT_CONNECT
    28 => self::TASK_RETRY_ALWAYS,  // CURLE_OPERATION_TIMEOUTED
    35 => self::TASK_RETRY_ALWAYS,  // CURLE_SSL_CONNECT_ERROR
    52 => self::TASK_RETRY_ALWAYS   // CURLE_GOT_NOTHING
  ];

  /**
   * Creates a new task runner with exponential backoff support.
   *
   * @param array $config The task runner config
   * @param string $name The name of the current task (used for logging)
   * @param callable $action The task to run and possibly retry
   * @param array $arguments The task arguments
   * @throws Google_Task_Exception when misconfigured
   */
  public function __construct(
      $config,
      $name,
      $action,
      array $arguments = array()
  ) {
    if (isset($config['initial_delay'])) {
      if ($config['initial_delay'] < 0) {
        throw new Google_Task_Exception(
            'Task configuration `initial_delay` must not be negative.'
        );
      }

      $this->delay = $config['initial_delay'];
    }

    if (isset($config['max_delay'])) {
      if ($config['max_delay'] <= 0) {
        throw new Google_Task_Exception(
            'Task configuration `max_delay` must be greater than 0.'
        );
      }

      $this->maxDelay = $config['max_delay'];
    }

    if (isset($config['factor'])) {
      if ($config['factor'] <= 0) {
        throw new Google_Task_Exception(
            'Task configuration `factor` must be greater than 0.'
        );
      }

      $this->factor = $config['factor'];
    }

    if (isset($config['jitter'])) {
      if ($config['jitter'] <= 0) {
        throw new Google_Task_Exception(
            'Task configuration `jitter` must be greater than 0.'
        );
      }

      $this->jitter = $config['jitter'];
    }

    if (isset($config['retries'])) {
      if ($config['retries'] < 0) {
        throw new Google_Task_Exception(
            'Task configuration `retries` must not be negative.'
        );
      }
      $this->maxAttempts += $config['retries'];
    }

    if (!is_callable($action)) {
        throw new Google_Task_Exception(
            'Task argument `$action` must be a valid callable.'
        );
    }

    $this->action = $action;
    $this->arguments = $arguments;
  }

  /**
   * Checks if a retry can be attempted.
   *
   * @return boolean
   */
  public function canAttempt()
  {
    return $this->attempts < $this->maxAttempts;
  }

  /**
   * Runs the task and (if applicable) automatically retries when errors occur.
   *
   * @return mixed
   * @throws Google_Task_Retryable on failure when no retries are available.
   */
  public function run()
  {
    while ($this->attempt()) {
      try {
        return call_user_func_array($this->action, $this->arguments);
      } catch (Google_Service_Exception $exception) {
        $allowedRetries = $this->allowedRetries(
            $exception->getCode(),
            $exception->getErrors()
        );

        if (!$this->canAttempt() || !$allowedRetries) {
          throw $exception;
        }

        if ($allowedRetries > 0) {
          $this->maxAttempts = min(
              $this->maxAttempts,
              $this->attempts + $allowedRetries
          );
        }
      }
    }
  }

  /**
   * Runs a task once, if possible. This is useful for bypassing the `run()`
   * loop.
   *
   * NOTE: If this is not the first attempt, this function will sleep in
   * accordance to the backoff configurations before running the task.
   *
   * @return boolean
   */
  public function attempt()
  {
    if (!$this->canAttempt()) {
      return false;
    }

    if ($this->attempts > 0) {
      $this->backOff();
    }

    $this->attempts++;
    return true;
  }

  /**
   * Sleeps in accordance to the backoff configurations.
   */
  private function backOff()
  {
    $delay = $this->getDelay();

    usleep($delay * 1000000);
  }

  /**
   * Gets the delay (in seconds) for the current backoff period.
   *
   * @return float
   */
  private function getDelay()
  {
    $jitter = $this->getJitter();
    $factor = $this->attempts > 1 ? $this->factor + $jitter : 1 + abs($jitter);

    return $this->delay = min($this->maxDelay, $this->delay * $factor);
  }

  /**
   * Gets the current jitter (random number between -$this->jitter and
   * $this->jitter).
   *
   * @return float
   */
  private function getJitter()
  {
    return $this->jitter * 2 * mt_rand() / mt_getrandmax() - $this->jitter;
  }

  /**
   * Gets the number of times the associated task can be retried.
   *
   * NOTE: -1 is returned if the task can be retried indefinitely
   *
   * @return integer
   */
  public function allowedRetries($code, $errors = array())
  {
    if (isset($this->retryMap[$code])) {
      return $this->retryMap[$code];
    }

    if (
        !empty($errors) &&
        isset($errors[0]['reason'], $this->retryMap[$errors[0]['reason']])
    ) {
      return $this->retryMap[$errors[0]['reason']];
    }

    return 0;
  }

  public function setRetryMap($retryMap)
  {
    $this->retryMap = $retryMap;
  }
}
includes/lib/google-api-php-client/src/Google/Exception.php000064400000001207151327705670017673 0ustar00<?php
/*
 * Copyright 2013 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

class Google_Exception extends Exception
{
}
includes/lib/google-api-php-client/src/Google/autoload.php000064400000001345151327705670017550 0ustar00<?php

/**
 * THIS FILE IS FOR BACKWARDS COMPATIBLITY ONLY
 *
 * If you were not already including this file in your project, please ignore it
 */

$file = __DIR__ . '/../../vendor/autoload.php';

if (!file_exists($file)) {
  $exception = 'This library must be installed via composer or by downloading the full package.';
  $exception .= ' See the instructions at https://github.com/google/google-api-php-client#installation.';
  throw new Exception(esc_html($exception));
}

$error = 'google-api-php-client\'s autoloader was moved to vendor/autoload.php in 2.0.0. This ';
$error .= 'redirect will be removed in 2.1. Please adjust your code to use the new location.';
trigger_error(esc_html($error), E_USER_DEPRECATED);

require_once $file;
includes/lib/google-api-php-client/src/Google/Model.php000064400000021145151327705670017000 0ustar00<?php
/*
 * Copyright 2011 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * This class defines attributes, valid values, and usage which is generated
 * from a given json schema.
 * http://tools.ietf.org/html/draft-zyp-json-schema-03#section-5
 *
 */
class Google_Model implements ArrayAccess
{
  /**
   * If you need to specify a NULL JSON value, use Google_Model::NULL_VALUE
   * instead - it will be replaced when converting to JSON with a real null.
   */
  const NULL_VALUE = "{}gapi-php-null";
  protected $internal_gapi_mappings = array();
  protected $modelData = array();
  protected $processed = array();

  /**
   * Polymorphic - accepts a variable number of arguments dependent
   * on the type of the model subclass.
   */
  final public function __construct()
  {
    if (func_num_args() == 1 && is_array(func_get_arg(0))) {
      // Initialize the model with the array's contents.
      $array = func_get_arg(0);
      $this->mapTypes($array);
    }
    $this->gapiInit();
  }

  /**
   * Getter that handles passthrough access to the data array, and lazy object creation.
   * @param string $key Property name.
   * @return mixed The value if any, or null.
   */
  public function __get($key)
  {
    $keyType = $this->keyType($key);
    $keyDataType = $this->dataType($key);
    if ($keyType && !isset($this->processed[$key])) {
      if (isset($this->modelData[$key])) {
        $val = $this->modelData[$key];
      } elseif ($keyDataType == 'array' || $keyDataType == 'map') {
        $val = array();
      } else {
        $val = null;
      }

      if ($this->isAssociativeArray($val)) {
        if ($keyDataType && 'map' == $keyDataType) {
          foreach ($val as $arrayKey => $arrayItem) {
              $this->modelData[$key][$arrayKey] =
                new $keyType($arrayItem);
          }
        } else {
          $this->modelData[$key] = new $keyType($val);
        }
      } else if (is_array($val)) {
        $arrayObject = array();
        foreach ($val as $arrayIndex => $arrayItem) {
          $arrayObject[$arrayIndex] = new $keyType($arrayItem);
        }
        $this->modelData[$key] = $arrayObject;
      }
      $this->processed[$key] = true;
    }

    return isset($this->modelData[$key]) ? $this->modelData[$key] : null;
  }

  /**
   * Initialize this object's properties from an array.
   *
   * @param array $array Used to seed this object's properties.
   * @return void
   */
  protected function mapTypes($array)
  {
    // Hard initialise simple types, lazy load more complex ones.
    foreach ($array as $key => $val) {
      if ($keyType = $this->keyType($key)) {
        $dataType = $this->dataType($key);
        if ($dataType == 'array' || $dataType == 'map') {
          $this->$key = array();
          foreach ($val as $itemKey => $itemVal) {
            if ($itemVal instanceof $keyType) {
              $this->{$key}[$itemKey] = $itemVal;
            } else {
              $this->{$key}[$itemKey] = new $keyType($itemVal);
            }
          }
        } elseif ($val instanceof $keyType) {
          $this->$key = $val;
        } else {
          $this->$key = new $keyType($val);
        }
        unset($array[$key]);
      } elseif (property_exists($this, $key)) {
          $this->$key = $val;
          unset($array[$key]);
      } elseif (property_exists($this, $camelKey = $this->camelCase($key))) {
          // This checks if property exists as camelCase, leaving it in array as snake_case
          // in case of backwards compatibility issues.
          $this->$camelKey = $val;
      }
    }
    $this->modelData = $array;
  }

  /**
   * Blank initialiser to be used in subclasses to do  post-construction initialisation - this
   * avoids the need for subclasses to have to implement the variadics handling in their
   * constructors.
   */
  protected function gapiInit()
  {
    return;
  }

  /**
   * Create a simplified object suitable for straightforward
   * conversion to JSON. This is relatively expensive
   * due to the usage of reflection, but shouldn't be called
   * a whole lot, and is the most straightforward way to filter.
   */
  public function toSimpleObject()
  {
    $object = new stdClass();

    // Process all other data.
    foreach ($this->modelData as $key => $val) {
      $result = $this->getSimpleValue($val);
      if ($result !== null) {
        $object->$key = $this->nullPlaceholderCheck($result);
      }
    }

    // Process all public properties.
    $reflect = new ReflectionObject($this);
    $props = $reflect->getProperties(ReflectionProperty::IS_PUBLIC);
    foreach ($props as $member) {
      $name = $member->getName();
      $result = $this->getSimpleValue($this->$name);
      if ($result !== null) {
        $name = $this->getMappedName($name);
        $object->$name = $this->nullPlaceholderCheck($result);
      }
    }

    return $object;
  }

  /**
   * Handle different types of values, primarily
   * other objects and map and array data types.
   */
  private function getSimpleValue($value)
  {
    if ($value instanceof Google_Model) {
      return $value->toSimpleObject();
    } else if (is_array($value)) {
      $return = array();
      foreach ($value as $key => $a_value) {
        $a_value = $this->getSimpleValue($a_value);
        if ($a_value !== null) {
          $key = $this->getMappedName($key);
          $return[$key] = $this->nullPlaceholderCheck($a_value);
        }
      }
      return $return;
    }
    return $value;
  }

  /**
   * Check whether the value is the null placeholder and return true null.
   */
  private function nullPlaceholderCheck($value)
  {
    if ($value === self::NULL_VALUE) {
      return null;
    }
    return $value;
  }

  /**
   * If there is an internal name mapping, use that.
   */
  private function getMappedName($key)
  {
    if (isset($this->internal_gapi_mappings, $this->internal_gapi_mappings[$key])) {
      $key = $this->internal_gapi_mappings[$key];
    }
    return $key;
  }

  /**
   * Returns true only if the array is associative.
   * @param array $array
   * @return bool True if the array is associative.
   */
  protected function isAssociativeArray($array)
  {
    if (!is_array($array)) {
      return false;
    }
    $keys = array_keys($array);
    foreach ($keys as $key) {
      if (is_string($key)) {
        return true;
      }
    }
    return false;
  }

  /**
   * Verify if $obj is an array.
   * @throws Google_Exception Thrown if $obj isn't an array.
   * @param array $obj Items that should be validated.
   * @param string $method Method expecting an array as an argument.
   */
  public function assertIsArray($obj, $method)
  {
    if ($obj && !is_array($obj)) {
      throw new Google_Exception(
          esc_html("Incorrect parameter type passed to $method(). Expected an array.")
      );
    }
  }

  public function offsetExists($offset)
  {
    return isset($this->$offset) || isset($this->modelData[$offset]);
  }

  public function offsetGet($offset)
  {
    return isset($this->$offset) ?
        $this->$offset :
        $this->__get($offset);
  }

  public function offsetSet($offset, $value)
  {
    if (property_exists($this, $offset)) {
      $this->$offset = $value;
    } else {
      $this->modelData[$offset] = $value;
      $this->processed[$offset] = true;
    }
  }

  public function offsetUnset($offset)
  {
    unset($this->modelData[$offset]);
  }

  protected function keyType($key)
  {
    $keyType = $key . "Type";

    // ensure keyType is a valid class
    if (property_exists($this, $keyType) && class_exists($this->$keyType)) {
      return $this->$keyType;
    }
  }

  protected function dataType($key)
  {
    $dataType = $key . "DataType";

    if (property_exists($this, $dataType)) {
      return $this->$dataType;
    }
  }

  public function __isset($key)
  {
    return isset($this->modelData[$key]);
  }

  public function __unset($key)
  {
    unset($this->modelData[$key]);
  }

  /**
   * Convert a string to camelCase
   * @param  string $value
   * @return string
   */
  private function camelCase($value)
  {
    $value = ucwords(str_replace(array('-', '_'), ' ', $value));
    $value = str_replace(' ', '', $value);
    $value[0] = strtolower($value[0]);
    return $value;
  }
}
includes/lib/google-api-php-client/src/Google/Service/Exception.php000064400000003533151327705670021277 0ustar00<?php
/*
 * Copyright 2014 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

class Google_Service_Exception extends Google_Exception
{
  /**
   * Optional list of errors returned in a JSON body of an HTTP error response.
   */
  protected $errors = array();

  /**
   * Override default constructor to add the ability to set $errors and a retry
   * map.
   *
   * @param string $message
   * @param int $code
   * @param Exception|null $previous
   * @param [{string, string}] errors List of errors returned in an HTTP
   * response.  Defaults to [].
   * @param array|null $retryMap Map of errors with retry counts.
   */
  public function __construct(
      $message,
      $code = 0,
      Exception $previous = null,
      $errors = array()
  ) {
    if (version_compare(PHP_VERSION, '5.3.0') >= 0) {
      parent::__construct($message, $code, $previous);
    } else {
      parent::__construct($message, $code);
    }

    $this->errors = $errors;
  }

  /**
   * An example of the possible errors returned.
   *
   * {
   *   "domain": "global",
   *   "reason": "authError",
   *   "message": "Invalid Credentials",
   *   "locationType": "header",
   *   "location": "Authorization",
   * }
   *
   * @return [{string, string}] List of errors return in an HTTP response or [].
   */
  public function getErrors()
  {
    return $this->errors;
  }
}
includes/lib/google-api-php-client/src/Google/Service/README.md000064400000000301151327705670020075 0ustar00# Google API Client Services

Google API Client Service classes have been moved to the 
[google-api-php-client-services](https://github.com/google/google-api-php-client-services) 
repository. 
includes/lib/google-api-php-client/src/Google/Service/Resource.php000064400000023704151327705670021132 0ustar00<?php
/**
 * Copyright 2010 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

use WPvividGuzzleHttp\Psr7\Request;

/**
 * Implements the actual methods/resources of the discovered Google API using magic function
 * calling overloading (__call()), which on call will see if the method name (plus.activities.list)
 * is available in this service, and if so construct an apiHttpRequest representing it.
 *
 */
class Google_Service_Resource
{
  // Valid query parameters that work, but don't appear in discovery.
  private $stackParameters = array(
      'alt' => array('type' => 'string', 'location' => 'query'),
      'fields' => array('type' => 'string', 'location' => 'query'),
      'trace' => array('type' => 'string', 'location' => 'query'),
      'userIp' => array('type' => 'string', 'location' => 'query'),
      'quotaUser' => array('type' => 'string', 'location' => 'query'),
      'data' => array('type' => 'string', 'location' => 'body'),
      'mimeType' => array('type' => 'string', 'location' => 'header'),
      'uploadType' => array('type' => 'string', 'location' => 'query'),
      'mediaUpload' => array('type' => 'complex', 'location' => 'query'),
      'prettyPrint' => array('type' => 'string', 'location' => 'query'),
  );

  /** @var string $rootUrl */
  private $rootUrl;

  /** @var Google_Client $client */
  private $client;

  /** @var string $serviceName */
  private $serviceName;

  /** @var string $servicePath */
  private $servicePath;

  /** @var string $resourceName */
  private $resourceName;

  /** @var array $methods */
  private $methods;

  public function __construct($service, $serviceName, $resourceName, $resource)
  {
    $this->rootUrl = $service->rootUrl;
    $this->client = $service->getClient();
    $this->servicePath = $service->servicePath;
    $this->serviceName = $serviceName;
    $this->resourceName = $resourceName;
    $this->methods = is_array($resource) && isset($resource['methods']) ?
        $resource['methods'] :
        array($resourceName => $resource);
  }

  /**
   * TODO: This function needs simplifying.
   * @param $name
   * @param $arguments
   * @param $expectedClass - optional, the expected class name
   * @return Google_Http_Request|expectedClass
   * @throws Google_Exception
   */
  public function call($name, $arguments, $expectedClass = null)
  {
    if (! isset($this->methods[$name])) {
      $this->client->getLogger()->error(
          'Service method unknown',
          array(
              'service' => $this->serviceName,
              'resource' => $this->resourceName,
              'method' => $name
          )
      );

      throw new Google_Exception(
          "Unknown function: " .
          esc_html("{$this->serviceName}->{$this->resourceName}->{$name}()")
      );
    }
    $method = $this->methods[$name];
    $parameters = $arguments[0];

    // postBody is a special case since it's not defined in the discovery
    // document as parameter, but we abuse the param entry for storing it.
    $postBody = null;
    if (isset($parameters['postBody'])) {
      if ($parameters['postBody'] instanceof Google_Model) {
        // In the cases the post body is an existing object, we want
        // to use the smart method to create a simple object for
        // for JSONification.
        $parameters['postBody'] = $parameters['postBody']->toSimpleObject();
      } else if (is_object($parameters['postBody'])) {
        // If the post body is another kind of object, we will try and
        // wrangle it into a sensible format.
        $parameters['postBody'] =
            $this->convertToArrayAndStripNulls($parameters['postBody']);
      }
      $postBody = (array) $parameters['postBody'];
      unset($parameters['postBody']);
    }

    // TODO: optParams here probably should have been
    // handled already - this may well be redundant code.
    if (isset($parameters['optParams'])) {
      $optParams = $parameters['optParams'];
      unset($parameters['optParams']);
      $parameters = array_merge($parameters, $optParams);
    }

    if (!isset($method['parameters'])) {
      $method['parameters'] = array();
    }

    $method['parameters'] = array_merge(
        $this->stackParameters,
        $method['parameters']
    );

    foreach ($parameters as $key => $val) {
      if ($key != 'postBody' && ! isset($method['parameters'][$key])) {
        $this->client->getLogger()->error(
            'Service parameter unknown',
            array(
                'service' => $this->serviceName,
                'resource' => $this->resourceName,
                'method' => $name,
                'parameter' => $key
            )
        );
        throw new Google_Exception(esc_html("($name) unknown parameter: '$key'"));
      }
    }

    foreach ($method['parameters'] as $paramName => $paramSpec) {
      if (isset($paramSpec['required']) &&
          $paramSpec['required'] &&
          ! isset($parameters[$paramName])
      ) {
        $this->client->getLogger()->error(
            'Service parameter missing',
            array(
                'service' => $this->serviceName,
                'resource' => $this->resourceName,
                'method' => $name,
                'parameter' => $paramName
            )
        );
        throw new Google_Exception(esc_html("($name) missing required param: '$paramName'"));
      }
      if (isset($parameters[$paramName])) {
        $value = $parameters[$paramName];
        $parameters[$paramName] = $paramSpec;
        $parameters[$paramName]['value'] = $value;
        unset($parameters[$paramName]['required']);
      } else {
        // Ensure we don't pass nulls.
        unset($parameters[$paramName]);
      }
    }

    $this->client->getLogger()->info(
        'Service Call',
        array(
            'service' => $this->serviceName,
            'resource' => $this->resourceName,
            'method' => $name,
            'arguments' => $parameters,
        )
    );

    // build the service uri
    $url = $this->createRequestUri(
        $method['path'],
        $parameters
    );

    // NOTE: because we're creating the request by hand,
    // and because the service has a rootUrl property
    // the "base_uri" of the Http Client is not accounted for
    $request = new Request(
        $method['httpMethod'],
        $url,
        ['content-type' => 'application/json'],
        $postBody ? json_encode($postBody) : ''
    );

    // support uploads
    if (isset($parameters['data'])) {
      $mimeType = isset($parameters['mimeType'])
        ? $parameters['mimeType']['value']
        : 'application/octet-stream';
      $data = $parameters['data']['value'];
      $upload = new Google_Http_MediaFileUpload($this->client, $request, $mimeType, $data);

      // pull down the modified request
      $request = $upload->getRequest();
    }

    // if this is a media type, we will return the raw response
    // rather than using an expected class
    if (isset($parameters['alt']) && $parameters['alt']['value'] == 'media') {
      $expectedClass = null;
    }

    // if the client is marked for deferring, rather than
    // execute the request, return the response
    if ($this->client->shouldDefer()) {
      // @TODO find a better way to do this
      $request = $request
        ->withHeader('X-Php-Expected-Class', $expectedClass);

      return $request;
    }

    return $this->client->execute($request, $expectedClass);
  }

  protected function convertToArrayAndStripNulls($o)
  {
    $o = (array) $o;
    foreach ($o as $k => $v) {
      if ($v === null) {
        unset($o[$k]);
      } elseif (is_object($v) || is_array($v)) {
        $o[$k] = $this->convertToArrayAndStripNulls($o[$k]);
      }
    }
    return $o;
  }

  /**
   * Parse/expand request parameters and create a fully qualified
   * request uri.
   * @static
   * @param string $restPath
   * @param array $params
   * @return string $requestUrl
   */
  public function createRequestUri($restPath, $params)
  {
    // Override the default servicePath address if the $restPath use a /
    if ('/' == substr($restPath, 0, 1)) {
      $requestUrl = substr($restPath, 1);
    } else {
      $requestUrl = $this->servicePath . $restPath;
    }

    // code for leading slash
    if ($this->rootUrl) {
      if ('/' !== substr($this->rootUrl, -1) && '/' !== substr($requestUrl, 0, 1)) {
        $requestUrl = '/' . $requestUrl;
      }
      $requestUrl = $this->rootUrl . $requestUrl;
    }
    $uriTemplateVars = array();
    $queryVars = array();
    foreach ($params as $paramName => $paramSpec) {
      if ($paramSpec['type'] == 'boolean') {
        $paramSpec['value'] = $paramSpec['value'] ? 'true' : 'false';
      }
      if ($paramSpec['location'] == 'path') {
        $uriTemplateVars[$paramName] = $paramSpec['value'];
      } else if ($paramSpec['location'] == 'query') {
        if (is_array($paramSpec['value'])) {
          foreach ($paramSpec['value'] as $value) {
            $queryVars[] = $paramName . '=' . rawurlencode(rawurldecode($value));
          }
        } else {
          $queryVars[] = $paramName . '=' . rawurlencode(rawurldecode($paramSpec['value']));
        }
      }
    }

    if (count($uriTemplateVars)) {
      $uriTemplateParser = new Google_Utils_UriTemplate();
      $requestUrl = $uriTemplateParser->parse($requestUrl, $uriTemplateVars);
    }

    if (count($queryVars)) {
        if (version_compare(PHP_VERSION, '7.4', '>=')) {
            $requestUrl .= '?' . implode('&', $queryVars);
        }
        else{
            $requestUrl .= '?' . implode($queryVars, '&');
        }
    }

    return $requestUrl;
  }
}
includes/lib/google-api-php-client/src/Google/Service.php000064400000002411151327705670017333 0ustar00<?php
/*
 * Copyright 2010 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

class Google_Service
{
  public $batchPath;
  public $rootUrl;
  public $version;
  public $servicePath;
  public $availableScopes;
  public $resource;
  private $client;

  public function __construct(Google_Client $client)
  {
    $this->client = $client;
  }

  /**
   * Return the associated Google_Client class.
   * @return Google_Client
   */
  public function getClient()
  {
    return $this->client;
  }

  /**
   * Create a new HTTP Batch handler for this service
   *
   * @return Google_Http_Batch
   */
  public function createBatch()
  {
    return new Google_Http_Batch(
        $this->client,
        false,
        $this->rootUrl,
        $this->batchPath
    );
  }
}
includes/lib/google-api-php-client/src/Google/AccessToken/Verify.php000064400000016472151327705670021415 0ustar00<?php

/*
 * Copyright 2008 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

use Firebase\JWT\ExpiredException as ExpiredExceptionV3;
use Firebase\JWT\SignatureInvalidException;
use WPvividGuzzleHttp\Client;
use WPvividGuzzleHttp\ClientInterface;
use WPvividPsr\Cache\CacheItemPoolInterface;
use WPvividGoogle\Auth\Cache\MemoryCacheItemPool;
use Stash\Driver\FileSystem;
use Stash\Pool;

/**
 * Wrapper around Google Access Tokens which provides convenience functions
 *
 */
class Google_AccessToken_Verify
{
  const FEDERATED_SIGNON_CERT_URL = 'https://www.googleapis.com/oauth2/v3/certs';
  const OAUTH2_ISSUER = 'accounts.google.com';
  const OAUTH2_ISSUER_HTTPS = 'https://accounts.google.com';

  /**
   * @var GuzzleHttp\ClientInterface The http client
   */
  private $http;

  /**
   * @var Psr\Cache\CacheItemPoolInterface cache class
   */
  private $cache;

  /**
   * Instantiates the class, but does not initiate the login flow, leaving it
   * to the discretion of the caller.
   */
  public function __construct(
      ClientInterface $http = null,
      CacheItemPoolInterface $cache = null,
      $jwt = null
  ) {
    if (null === $http) {
      $http = new Client();
    }

    if (null === $cache) {
      $cache = new MemoryCacheItemPool;
    }

    $this->http = $http;
    $this->cache = $cache;
    $this->jwt = $jwt ?: $this->getJwtService();
  }

  /**
   * Verifies an id token and returns the authenticated apiLoginTicket.
   * Throws an exception if the id token is not valid.
   * The audience parameter can be used to control which id tokens are
   * accepted.  By default, the id token must have been issued to this OAuth2 client.
   *
   * @param $audience
   * @return array the token payload, if successful
   */
  public function verifyIdToken($idToken, $audience = null)
  {
    if (empty($idToken)) {
      throw new LogicException('id_token cannot be null');
    }

    // set phpseclib constants if applicable
    $this->setPhpsecConstants();

    // Check signature
    $certs = $this->getFederatedSignOnCerts();
    foreach ($certs as $cert) {
      $bigIntClass = $this->getBigIntClass();
      $rsaClass = $this->getRsaClass();
      $modulus = new $bigIntClass($this->jwt->urlsafeB64Decode($cert['n']), 256);
      $exponent = new $bigIntClass($this->jwt->urlsafeB64Decode($cert['e']), 256);

      $rsa = new $rsaClass();
      $rsa->loadKey(array('n' => $modulus, 'e' => $exponent));

      try {
        $payload = $this->jwt->decode(
            $idToken,
            $rsa->getPublicKey(),
            array('RS256')
        );

        if (property_exists($payload, 'aud')) {
          if ($audience && $payload->aud != $audience) {
            return false;
          }
        }

        // support HTTP and HTTPS issuers
        // @see https://developers.google.com/identity/sign-in/web/backend-auth
        $issuers = array(self::OAUTH2_ISSUER, self::OAUTH2_ISSUER_HTTPS);
        if (!isset($payload->iss) || !in_array($payload->iss, $issuers)) {
          return false;
        }

        return (array) $payload;
      } catch (ExpiredException $e) {
        return false;
      } catch (ExpiredExceptionV3 $e) {
        return false;
      } catch (SignatureInvalidException $e) {
        // continue
      } catch (DomainException $e) {
        // continue
      }
    }

    return false;
  }

  private function getCache()
  {
    return $this->cache;
  }

  /**
   * Retrieve and cache a certificates file.
   *
   * @param $url string location
   * @throws Google_Exception
   * @return array certificates
   */
  private function retrieveCertsFromLocation($url)
  {
    // If we're retrieving a local file, just grab it.
    if (0 !== strpos($url, 'http')) {
      if (!$file = file_get_contents($url)) {
        throw new Google_Exception(
            "Failed to retrieve verification certificates: '" .
            esc_url($url) . "'."
        );
      }

      return json_decode($file, true);
    }

    $response = $this->http->get($url);

    if ($response->getStatusCode() == 200) {
      return json_decode((string) $response->getBody(), true);
    }
    throw new Google_Exception(
        sprintf(
            'Failed to retrieve verification certificates: "%s".',
            esc_html($response->getBody()->getContents())
        ),
        esc_attr($response->getStatusCode())
    );
  }

  // Gets federated sign-on certificates to use for verifying identity tokens.
  // Returns certs as array structure, where keys are key ids, and values
  // are PEM encoded certificates.
  private function getFederatedSignOnCerts()
  {
    $certs = null;
    if ($cache = $this->getCache()) {
      $cacheItem = $cache->getItem('federated_signon_certs_v3');
      $certs = $cacheItem->get();
    }


    if (!$certs) {
      $certs = $this->retrieveCertsFromLocation(
          self::FEDERATED_SIGNON_CERT_URL
      );

      if ($cache) {
        $cacheItem->expiresAt(new DateTime('+1 hour'));
        $cacheItem->set($certs);
        $cache->save($cacheItem);
      }
    }

    if (!isset($certs['keys'])) {
      throw new InvalidArgumentException(
          'federated sign-on certs expects "keys" to be set'
      );
    }

    return $certs['keys'];
  }

  private function getJwtService()
  {
    $jwtClass = 'JWT';
    if (class_exists('\Firebase\JWT\JWT')) {
      $jwtClass = 'Firebase\JWT\JWT';
    }

    if (property_exists($jwtClass, 'leeway') && $jwtClass::$leeway < 1) {
      // Ensures JWT leeway is at least 1
      // @see https://github.com/google/google-api-php-client/issues/827
      $jwtClass::$leeway = 1;
    }

    return new $jwtClass;
  }

  private function getRsaClass()
  {
    if (class_exists('phpseclib\Crypt\RSA')) {
      return 'phpseclib\Crypt\RSA';
    }

    return 'Crypt_RSA';
  }

  private function getBigIntClass()
  {
    if (class_exists('phpseclib\Math\BigInteger')) {
      return 'phpseclib\Math\BigInteger';
    }

    return 'Math_BigInteger';
  }

  private function getOpenSslConstant()
  {
    if (class_exists('phpseclib\Crypt\RSA')) {
      return 'phpseclib\Crypt\RSA::MODE_OPENSSL';
    }

    if (class_exists('Crypt_RSA')) {
      return 'CRYPT_RSA_MODE_OPENSSL';
    }

    throw new \Exception('Cannot find RSA class');
  }

  /**
   * phpseclib calls "phpinfo" by default, which requires special
   * whitelisting in the AppEngine VM environment. This function
   * sets constants to bypass the need for phpseclib to check phpinfo
   *
   * @see phpseclib/Math/BigInteger
   * @see https://github.com/GoogleCloudPlatform/getting-started-php/issues/85
   */
  private function setPhpsecConstants()
  {
    if (filter_var(getenv('GAE_VM'), FILTER_VALIDATE_BOOLEAN)) {
      if (!defined('MATH_BIGINTEGER_OPENSSL_ENABLED')) {
        define('MATH_BIGINTEGER_OPENSSL_ENABLED', true);
      }
      if (!defined('CRYPT_RSA_MODE')) {
        define('CRYPT_RSA_MODE', constant($this->getOpenSslConstant()));
      }
    }
  }
}
includes/lib/google-api-php-client/src/Google/AccessToken/Revoke.php000064400000004246151327705670021400 0ustar00<?php

/*
 * Copyright 2008 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

use WPvividGoogle\Auth\HttpHandler\HttpHandlerFactory;
use WPvividGuzzleHttp\ClientInterface;
use WPvividGuzzleHttp\Psr7;
use WPvividGuzzleHttp\Psr7\Request;

/**
 * Wrapper around Google Access Tokens which provides convenience functions
 *
 */
class Google_AccessToken_Revoke
{
  /**
   * @var GuzzleHttp\ClientInterface The http client
   */
  private $http;

  /**
   * Instantiates the class, but does not initiate the login flow, leaving it
   * to the discretion of the caller.
   */
  public function __construct(ClientInterface $http = null)
  {
    $this->http = $http;
  }

  /**
   * Revoke an OAuth2 access token or refresh token. This method will revoke the current access
   * token, if a token isn't provided.
   *
   * @param string|array $token The token (access token or a refresh token) that should be revoked.
   * @return boolean Returns True if the revocation was successful, otherwise False.
   */
  public function revokeToken($token)
  {
    if (is_array($token)) {
      if (isset($token['refresh_token'])) {
        $token = $token['refresh_token'];
      } else {
        $token = $token['access_token'];
      }
    }

    $body = Psr7\stream_for(http_build_query(array('token' => $token)));
    $request = new Request(
        'POST',
        Google_Client::OAUTH2_REVOKE_URI,
        [
          'Cache-Control' => 'no-store',
          'Content-Type'  => 'application/x-www-form-urlencoded',
        ],
        $body
    );

    $httpHandler = HttpHandlerFactory::build($this->http);

    $response = $httpHandler($request);

    return $response->getStatusCode() == 200;
  }
}
includes/lib/google-api-php-client/src/Google/Client.php000064400000076714151327705670017172 0ustar00<?php
/*
 * Copyright 2010 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

use WPvividGoogle\Auth\ApplicationDefaultCredentials;
use WPvividGoogle\Auth\Cache\MemoryCacheItemPool;
use WPvividGoogle\Auth\CredentialsLoader;
use WPvividGoogle\Auth\HttpHandler\HttpHandlerFactory;
use WPvividGoogle\Auth\OAuth2;
use WPvividGoogle\Auth\Credentials\ServiceAccountCredentials;
use WPvividGoogle\Auth\Credentials\UserRefreshCredentials;
use WPvividGuzzleHttp\Client;
use WPvividGuzzleHttp\ClientInterface;
use WPvividGuzzleHttp\Ring\Client\StreamHandler;
use WPvividPsr\Cache\CacheItemPoolInterface;
use WPvividPsr\Http\Message\RequestInterface;
use WPvividPsr\Log\LoggerInterface;
use WPvividMonolog\Logger;
use WPvividMonolog\Handler\StreamHandler as MonologStreamHandler;
use WPvividMonolog\Handler\SyslogHandler as MonologSyslogHandler;

/**
 * The Google API Client
 * https://github.com/google/google-api-php-client
 */
class Google_Client
{
  const LIBVER = "2.2.2";
  const USER_AGENT_SUFFIX = "google-api-php-client/";
  const OAUTH2_REVOKE_URI = 'https://accounts.google.com/o/oauth2/revoke';
  const OAUTH2_TOKEN_URI = 'https://www.googleapis.com/oauth2/v4/token';
  const OAUTH2_AUTH_URL = 'https://accounts.google.com/o/oauth2/auth';
  const API_BASE_PATH = 'https://www.googleapis.com';

  /**
   * @var Google\Auth\OAuth2 $auth
   */
  private $auth;

  /**
   * @var GuzzleHttp\ClientInterface $http
   */
  private $http;

  /**
   * @var Psr\Cache\CacheItemPoolInterface $cache
   */
  private $cache;

  /**
   * @var array access token
   */
  private $token;

  /**
   * @var array $config
   */
  private $config;

  /**
   * @var Psr\Log\LoggerInterface $logger
   */
  private $logger;

  /**
   * @var boolean $deferExecution
   */
  private $deferExecution = false;

  /** @var array $scopes */
  // Scopes requested by the client
  protected $requestedScopes = [];

  /**
   * Construct the Google Client.
   *
   * @param array $config
   */
  public function __construct(array $config = array())
  {
    $this->config = array_merge(
        [
          'application_name' => '',

          // Don't change these unless you're working against a special development
          // or testing environment.
          'base_path' => self::API_BASE_PATH,

          // https://developers.google.com/console
          'client_id' => '',
          'client_secret' => '',
          'redirect_uri' => null,
          'state' => null,

          // Simple API access key, also from the API console. Ensure you get
          // a Server key, and not a Browser key.
          'developer_key' => '',

          // For use with Google Cloud Platform
          // fetch the ApplicationDefaultCredentials, if applicable
          // @see https://developers.google.com/identity/protocols/application-default-credentials
          'use_application_default_credentials' => false,
          'signing_key' => null,
          'signing_algorithm' => null,
          'subject' => null,

          // Other OAuth2 parameters.
          'hd' => '',
          'prompt' => '',
          'openid.realm' => '',
          'include_granted_scopes' => null,
          'login_hint' => '',
          'request_visible_actions' => '',
          'access_type' => 'online',
          'approval_prompt' => 'auto',

          // Task Runner retry configuration
          // @see Google_Task_Runner
          'retry' => array(),

          // cache config for downstream auth caching
          'cache_config' => [],

          // function to be called when an access token is fetched
          // follows the signature function ($cacheKey, $accessToken)
          'token_callback' => null,

          // Service class used in Google_Client::verifyIdToken.
          // Explicitly pass this in to avoid setting JWT::$leeway
          'jwt' => null,
        ],
        $config
    );
  }

  /**
   * Get a string containing the version of the library.
   *
   * @return string
   */
  public function getLibraryVersion()
  {
    return self::LIBVER;
  }

  /**
   * For backwards compatibility
   * alias for fetchAccessTokenWithAuthCode
   *
   * @param $code string code from accounts.google.com
   * @return array access token
   * @deprecated
   */
  public function authenticate($code)
  {
    return $this->fetchAccessTokenWithAuthCode($code);
  }

  /**
   * Attempt to exchange a code for an valid authentication token.
   * Helper wrapped around the OAuth 2.0 implementation.
   *
   * @param $code string code from accounts.google.com
   * @return array access token
   */
  public function fetchAccessTokenWithAuthCode($code)
  {
    if (strlen($code) == 0) {
      throw new InvalidArgumentException("Invalid code");
    }

    $auth = $this->getOAuth2Service();
    $auth->setCode($code);
    $auth->setRedirectUri($this->getRedirectUri());

    $httpHandler = HttpHandlerFactory::build($this->getHttpClient());
    $creds = $auth->fetchAuthToken($httpHandler);
    if ($creds && isset($creds['access_token'])) {
      $creds['created'] = time();
      $this->setAccessToken($creds);
    }

    return $creds;
  }

  /**
   * For backwards compatibility
   * alias for fetchAccessTokenWithAssertion
   *
   * @return array access token
   * @deprecated
   */
  public function refreshTokenWithAssertion()
  {
    return $this->fetchAccessTokenWithAssertion();
  }

  /**
   * Fetches a fresh access token with a given assertion token.
   * @param ClientInterface $authHttp optional.
   * @return array access token
   */
  public function fetchAccessTokenWithAssertion(ClientInterface $authHttp = null)
  {
    if (!$this->isUsingApplicationDefaultCredentials()) {
      throw new DomainException(
          'set the JSON service account credentials using'
          . ' Google_Client::setAuthConfig or set the path to your JSON file'
          . ' with the "GOOGLE_APPLICATION_CREDENTIALS" environment variable'
          . ' and call Google_Client::useApplicationDefaultCredentials to'
          . ' refresh a token with assertion.'
      );
    }

    $this->getLogger()->log(
        'info',
        'OAuth2 access token refresh with Signed JWT assertion grants.'
    );

    $credentials = $this->createApplicationDefaultCredentials();

    $httpHandler = HttpHandlerFactory::build($authHttp);
    $creds = $credentials->fetchAuthToken($httpHandler);
    if ($creds && isset($creds['access_token'])) {
      $creds['created'] = time();
      $this->setAccessToken($creds);
    }

    return $creds;
  }

  /**
   * For backwards compatibility
   * alias for fetchAccessTokenWithRefreshToken
   *
   * @param string $refreshToken
   * @return array access token
   */
  public function refreshToken($refreshToken)
  {
    return $this->fetchAccessTokenWithRefreshToken($refreshToken);
  }

  /**
   * Fetches a fresh OAuth 2.0 access token with the given refresh token.
   * @param string $refreshToken
   * @return array access token
   */
  public function fetchAccessTokenWithRefreshToken($refreshToken = null)
  {
    if (null === $refreshToken) {
      if (!isset($this->token['refresh_token'])) {
        throw new LogicException(
            'refresh token must be passed in or set as part of setAccessToken'
        );
      }
      $refreshToken = $this->token['refresh_token'];
    }
    $this->getLogger()->info('OAuth2 access token refresh');
    $auth = $this->getOAuth2Service();
    $auth->setRefreshToken($refreshToken);

    $httpHandler = HttpHandlerFactory::build($this->getHttpClient());
    $creds = $auth->fetchAuthToken($httpHandler);
    if ($creds && isset($creds['access_token'])) {
      $creds['created'] = time();
      if (!isset($creds['refresh_token'])) {
        $creds['refresh_token'] = $refreshToken;
      }
      $this->setAccessToken($creds);
    }

    return $creds;
  }

  /**
   * Create a URL to obtain user authorization.
   * The authorization endpoint allows the user to first
   * authenticate, and then grant/deny the access request.
   * @param string|array $scope The scope is expressed as an array or list of space-delimited strings.
   * @return string
   */
  public function createAuthUrl($scope = null)
  {
    if (empty($scope)) {
      $scope = $this->prepareScopes();
    }
    if (is_array($scope)) {
      $scope = implode(' ', $scope);
    }

    // only accept one of prompt or approval_prompt
    $approvalPrompt = $this->config['prompt']
      ? null
      : $this->config['approval_prompt'];

    // include_granted_scopes should be string "true", string "false", or null
    $includeGrantedScopes = $this->config['include_granted_scopes'] === null
      ? null
      : var_export($this->config['include_granted_scopes'], true);

    $params = array_filter(
        [
          'access_type' => $this->config['access_type'],
          'approval_prompt' => $approvalPrompt,
          'hd' => $this->config['hd'],
          'include_granted_scopes' => $includeGrantedScopes,
          'login_hint' => $this->config['login_hint'],
          'openid.realm' => $this->config['openid.realm'],
          'prompt' => $this->config['prompt'],
          'response_type' => 'code',
          'scope' => $scope,
          'state' => $this->config['state'],
        ]
    );

    // If the list of scopes contains plus.login, add request_visible_actions
    // to auth URL.
    $rva = $this->config['request_visible_actions'];
    if (strlen($rva) > 0 && false !== strpos($scope, 'plus.login')) {
        $params['request_visible_actions'] = $rva;
    }

    $auth = $this->getOAuth2Service();

    return (string) $auth->buildFullAuthorizationUri($params);
  }

  /**
   * Adds auth listeners to the HTTP client based on the credentials
   * set in the Google API Client object
   *
   * @param WPvividGuzzleHttp\ClientInterface $http the http client object.
   * @return WPvividGuzzleHttp\ClientInterface the http client object
   */
  public function authorize(ClientInterface $http = null)
  {
    $credentials = null;
    $token = null;
    $scopes = null;
    if (null === $http) {
      $http = $this->getHttpClient();
    }
    // These conditionals represent the decision tree for authentication
    //   1.  Check for Application Default Credentials
    //   2.  Check for API Key
    //   3a. Check for an Access Token
    //   3b. If access token exists but is expired, try to refresh it
    if ($this->isUsingApplicationDefaultCredentials()) {
      $credentials = $this->createApplicationDefaultCredentials();
    } elseif ($token = $this->getAccessToken()) {
      $scopes = $this->prepareScopes();
      // add refresh subscriber to request a new token
      if (isset($token['refresh_token']) && $this->isAccessTokenExpired()) {
        $credentials = $this->createUserRefreshCredentials(
            $scopes,
            $token['refresh_token']
        );
      }
    }

    $authHandler = $this->getAuthHandler();

    if ($credentials) {
      $callback = $this->config['token_callback'];
      $http = $authHandler->attachCredentials($http, $credentials, $callback);
    } elseif ($token) {
      $http = $authHandler->attachToken($http, $token, (array) $scopes);
    } elseif ($key = $this->config['developer_key']) {
      $http = $authHandler->attachKey($http, $key);
    }

    return $http;
  }

  /**
   * Set the configuration to use application default credentials for
   * authentication
   *
   * @see https://developers.google.com/identity/protocols/application-default-credentials
   * @param boolean $useAppCreds
   */
  public function useApplicationDefaultCredentials($useAppCreds = true)
  {
    $this->config['use_application_default_credentials'] = $useAppCreds;
  }

  /**
   * To prevent useApplicationDefaultCredentials from inappropriately being
   * called in a conditional
   *
   * @see https://developers.google.com/identity/protocols/application-default-credentials
   */
  public function isUsingApplicationDefaultCredentials()
  {
    return $this->config['use_application_default_credentials'];
  }

  /**
   * @param string|array $token
   * @throws InvalidArgumentException
   */
  public function setAccessToken($token)
  {
    if (is_string($token)) {
      if ($json = json_decode($token, true)) {
        $token = $json;
      } else {
        // assume $token is just the token string
        $token = array(
          'access_token' => $token,
        );
      }
    }
    if ($token == null) {
      throw new InvalidArgumentException('invalid json token');
    }
    if (!isset($token['access_token'])) {
      throw new InvalidArgumentException("Invalid token format");
    }
    $this->token = $token;
  }

  public function getAccessToken()
  {
    return $this->token;
  }

  /**
   * @return string|null
   */
  public function getRefreshToken()
  {
    if (isset($this->token['refresh_token'])) {
      return $this->token['refresh_token'];
    }

    return null;
  }

  /**
   * Returns if the access_token is expired.
   * @return bool Returns True if the access_token is expired.
   */
  public function isAccessTokenExpired()
  {
    if (!$this->token) {
      return true;
    }

    $created = 0;
    if (isset($this->token['created'])) {
      $created = $this->token['created'];
    } elseif (isset($this->token['id_token'])) {
      // check the ID token for "iat"
      // signature verification is not required here, as we are just
      // using this for convenience to save a round trip request
      // to the Google API server
      $idToken = $this->token['id_token'];
      if (substr_count($idToken, '.') == 2) {
        $parts = explode('.', $idToken);
        $payload = json_decode(base64_decode($parts[1]), true);
        if ($payload && isset($payload['iat'])) {
          $created = $payload['iat'];
        }
      }
    }

    // If the token is set to expire in the next 30 seconds.
    return ($created + ($this->token['expires_in'] - 30)) < time();
  }

  /**
   * @deprecated See UPGRADING.md for more information
   */
  public function getAuth()
  {
    throw new BadMethodCallException(
        'This function no longer exists. See UPGRADING.md for more information'
    );
  }

  /**
   * @deprecated See UPGRADING.md for more information
   */
  public function setAuth($auth)
  {
    throw new BadMethodCallException(
        'This function no longer exists. See UPGRADING.md for more information'
    );
  }

  /**
   * Set the OAuth 2.0 Client ID.
   * @param string $clientId
   */
  public function setClientId($clientId)
  {
    $this->config['client_id'] = $clientId;
  }

  public function getClientId()
  {
    return $this->config['client_id'];
  }

  /**
   * Set the OAuth 2.0 Client Secret.
   * @param string $clientSecret
   */
  public function setClientSecret($clientSecret)
  {
    $this->config['client_secret'] = $clientSecret;
  }

  public function getClientSecret()
  {
    return $this->config['client_secret'];
  }

  /**
   * Set the OAuth 2.0 Redirect URI.
   * @param string $redirectUri
   */
  public function setRedirectUri($redirectUri)
  {
    $this->config['redirect_uri'] = $redirectUri;
  }

  public function getRedirectUri()
  {
    return $this->config['redirect_uri'];
  }

  /**
   * Set OAuth 2.0 "state" parameter to achieve per-request customization.
   * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-22#section-3.1.2.2
   * @param string $state
   */
  public function setState($state)
  {
    $this->config['state'] = $state;
  }

  /**
   * @param string $accessType Possible values for access_type include:
   *  {@code "offline"} to request offline access from the user.
   *  {@code "online"} to request online access from the user.
   */
  public function setAccessType($accessType)
  {
    $this->config['access_type'] = $accessType;
  }

  /**
   * @param string $approvalPrompt Possible values for approval_prompt include:
   *  {@code "force"} to force the approval UI to appear.
   *  {@code "auto"} to request auto-approval when possible. (This is the default value)
   */
  public function setApprovalPrompt($approvalPrompt)
  {
    $this->config['approval_prompt'] = $approvalPrompt;
  }

  /**
   * Set the login hint, email address or sub id.
   * @param string $loginHint
   */
  public function setLoginHint($loginHint)
  {
    $this->config['login_hint'] = $loginHint;
  }

  /**
   * Set the application name, this is included in the User-Agent HTTP header.
   * @param string $applicationName
   */
  public function setApplicationName($applicationName)
  {
    $this->config['application_name'] = $applicationName;
  }

  /**
   * If 'plus.login' is included in the list of requested scopes, you can use
   * this method to define types of app activities that your app will write.
   * You can find a list of available types here:
   * @link https://developers.google.com/+/api/moment-types
   *
   * @param array $requestVisibleActions Array of app activity types
   */
  public function setRequestVisibleActions($requestVisibleActions)
  {
    if (is_array($requestVisibleActions)) {
      $requestVisibleActions = implode(" ", $requestVisibleActions);
    }
    $this->config['request_visible_actions'] = $requestVisibleActions;
  }

  /**
   * Set the developer key to use, these are obtained through the API Console.
   * @see http://code.google.com/apis/console-help/#generatingdevkeys
   * @param string $developerKey
   */
  public function setDeveloperKey($developerKey)
  {
    $this->config['developer_key'] = $developerKey;
  }

  /**
   * Set the hd (hosted domain) parameter streamlines the login process for
   * Google Apps hosted accounts. By including the domain of the user, you
   * restrict sign-in to accounts at that domain.
   * @param $hd string - the domain to use.
   */
  public function setHostedDomain($hd)
  {
    $this->config['hd'] = $hd;
  }

  /**
   * Set the prompt hint. Valid values are none, consent and select_account.
   * If no value is specified and the user has not previously authorized
   * access, then the user is shown a consent screen.
   * @param $prompt string
   */
  public function setPrompt($prompt)
  {
    $this->config['prompt'] = $prompt;
  }

  /**
   * openid.realm is a parameter from the OpenID 2.0 protocol, not from OAuth
   * 2.0. It is used in OpenID 2.0 requests to signify the URL-space for which
   * an authentication request is valid.
   * @param $realm string - the URL-space to use.
   */
  public function setOpenidRealm($realm)
  {
    $this->config['openid.realm'] = $realm;
  }

  /**
   * If this is provided with the value true, and the authorization request is
   * granted, the authorization will include any previous authorizations
   * granted to this user/application combination for other scopes.
   * @param $include boolean - the URL-space to use.
   */
  public function setIncludeGrantedScopes($include)
  {
    $this->config['include_granted_scopes'] = $include;
  }

  /**
   * sets function to be called when an access token is fetched
   * @param callable $tokenCallback - function ($cacheKey, $accessToken)
   */
  public function setTokenCallback(callable $tokenCallback)
  {
    $this->config['token_callback'] = $tokenCallback;
  }

  /**
   * Revoke an OAuth2 access token or refresh token. This method will revoke the current access
   * token, if a token isn't provided.
   *
   * @param string|null $token The token (access token or a refresh token) that should be revoked.
   * @return boolean Returns True if the revocation was successful, otherwise False.
   */
  public function revokeToken($token = null)
  {
    $tokenRevoker = new Google_AccessToken_Revoke(
        $this->getHttpClient()
    );

    return $tokenRevoker->revokeToken($token ?: $this->getAccessToken());
  }

  /**
   * Verify an id_token. This method will verify the current id_token, if one
   * isn't provided.
   *
   * @throws LogicException
   * @param string|null $idToken The token (id_token) that should be verified.
   * @return array|false Returns the token payload as an array if the verification was
   * successful, false otherwise.
   */
  public function verifyIdToken($idToken = null)
  {
    $tokenVerifier = new Google_AccessToken_Verify(
        $this->getHttpClient(),
        $this->getCache(),
        $this->config['jwt']
    );

    if (null === $idToken) {
      $token = $this->getAccessToken();
      if (!isset($token['id_token'])) {
        throw new LogicException(
            'id_token must be passed in or set as part of setAccessToken'
        );
      }
      $idToken = $token['id_token'];
    }

    return $tokenVerifier->verifyIdToken(
        $idToken,
        $this->getClientId()
    );
  }

  /**
   * Set the scopes to be requested. Must be called before createAuthUrl().
   * Will remove any previously configured scopes.
   * @param array $scopes, ie: array('https://www.googleapis.com/auth/plus.login',
   * 'https://www.googleapis.com/auth/moderator')
   */
  public function setScopes($scopes)
  {
    $this->requestedScopes = array();
    $this->addScope($scopes);
  }

  /**
   * This functions adds a scope to be requested as part of the OAuth2.0 flow.
   * Will append any scopes not previously requested to the scope parameter.
   * A single string will be treated as a scope to request. An array of strings
   * will each be appended.
   * @param $scope_or_scopes string|array e.g. "profile"
   */
  public function addScope($scope_or_scopes)
  {
    if (is_string($scope_or_scopes) && !in_array($scope_or_scopes, $this->requestedScopes)) {
      $this->requestedScopes[] = $scope_or_scopes;
    } else if (is_array($scope_or_scopes)) {
      foreach ($scope_or_scopes as $scope) {
        $this->addScope($scope);
      }
    }
  }

  /**
   * Returns the list of scopes requested by the client
   * @return array the list of scopes
   *
   */
  public function getScopes()
  {
     return $this->requestedScopes;
  }

  /**
   * @return string|null
   * @visible For Testing
   */
  public function prepareScopes()
  {
    if (empty($this->requestedScopes)) {
      return null;
    }

    return implode(' ', $this->requestedScopes);
  }

  /**
   * Helper method to execute deferred HTTP requests.
   *
   * @param $request Psr\Http\Message\RequestInterface|Google_Http_Batch
   * @throws Google_Exception
   * @return object of the type of the expected class or Psr\Http\Message\ResponseInterface.
   */
  public function execute(RequestInterface $request, $expectedClass = null)
  {
    $request = $request->withHeader(
        'User-Agent',
        $this->config['application_name']
        . " " . self::USER_AGENT_SUFFIX
        . $this->getLibraryVersion()
    );

    // call the authorize method
    // this is where most of the grunt work is done
    $http = $this->authorize();

    return Google_Http_REST::execute($http, $request, $expectedClass, $this->config['retry']);
  }

  /**
   * Declare whether batch calls should be used. This may increase throughput
   * by making multiple requests in one connection.
   *
   * @param boolean $useBatch True if the batch support should
   * be enabled. Defaults to False.
   */
  public function setUseBatch($useBatch)
  {
    // This is actually an alias for setDefer.
    $this->setDefer($useBatch);
  }

  /**
   * Are we running in Google AppEngine?
   * return bool
   */
  public function isAppEngine()
  {
    return (isset($_SERVER['SERVER_SOFTWARE']) &&
        strpos($_SERVER['SERVER_SOFTWARE'], 'Google App Engine') !== false);
  }

  public function setConfig($name, $value)
  {
    $this->config[$name] = $value;
  }

  public function getConfig($name, $default = null)
  {
    return isset($this->config[$name]) ? $this->config[$name] : $default;
  }

  /**
   * For backwards compatibility
   * alias for setAuthConfig
   *
   * @param string $file the configuration file
   * @throws Google_Exception
   * @deprecated
   */
  public function setAuthConfigFile($file)
  {
    $this->setAuthConfig($file);
  }

  /**
   * Set the auth config from new or deprecated JSON config.
   * This structure should match the file downloaded from
   * the "Download JSON" button on in the Google Developer
   * Console.
   * @param string|array $config the configuration json
   * @throws Google_Exception
   */
  public function setAuthConfig($config)
  {
    if (is_string($config)) {
      if (!file_exists($config)) {
        throw new InvalidArgumentException('file does not exist');
      }

      $json = file_get_contents($config);

      if (!$config = json_decode($json, true)) {
        throw new LogicException('invalid json for auth config');
      }
    }

    $key = isset($config['installed']) ? 'installed' : 'web';
    if (isset($config['type']) && $config['type'] == 'service_account') {
      // application default credentials
      $this->useApplicationDefaultCredentials();

      // set the information from the config
      $this->setClientId($config['client_id']);
      $this->config['client_email'] = $config['client_email'];
      $this->config['signing_key'] = $config['private_key'];
      $this->config['signing_algorithm'] = 'HS256';
    } elseif (isset($config[$key])) {
      // old-style
      $this->setClientId($config[$key]['client_id']);
      $this->setClientSecret($config[$key]['client_secret']);
      if (isset($config[$key]['redirect_uris'])) {
        $this->setRedirectUri($config[$key]['redirect_uris'][0]);
      }
    } else {
      // new-style
      $this->setClientId($config['client_id']);
      $this->setClientSecret($config['client_secret']);
      if (isset($config['redirect_uris'])) {
        $this->setRedirectUri($config['redirect_uris'][0]);
      }
    }
  }

  /**
   * Use when the service account has been delegated domain wide access.
   *
   * @param string $subject an email address account to impersonate
   */
  public function setSubject($subject)
  {
    $this->config['subject'] = $subject;
  }

  /**
   * Declare whether making API calls should make the call immediately, or
   * return a request which can be called with ->execute();
   *
   * @param boolean $defer True if calls should not be executed right away.
   */
  public function setDefer($defer)
  {
    $this->deferExecution = $defer;
  }

  /**
   * Whether or not to return raw requests
   * @return boolean
   */
  public function shouldDefer()
  {
    return $this->deferExecution;
  }

  /**
   * @return Google\Auth\OAuth2 implementation
   */
  public function getOAuth2Service()
  {
    if (!isset($this->auth)) {
      $this->auth = $this->createOAuth2Service();
    }

    return $this->auth;
  }

  /**
   * create a default google auth object
   */
  protected function createOAuth2Service()
  {
    $auth = new OAuth2(
        [
          'clientId'          => $this->getClientId(),
          'clientSecret'      => $this->getClientSecret(),
          'authorizationUri'   => self::OAUTH2_AUTH_URL,
          'tokenCredentialUri' => self::OAUTH2_TOKEN_URI,
          'redirectUri'       => $this->getRedirectUri(),
          'issuer'            => $this->config['client_id'],
          'signingKey'        => $this->config['signing_key'],
          'signingAlgorithm'  => $this->config['signing_algorithm'],
        ]
    );

    return $auth;
  }

  /**
   * Set the Cache object
   * @param Psr\Cache\CacheItemPoolInterface $cache
   */
  public function setCache(CacheItemPoolInterface $cache)
  {
    $this->cache = $cache;
  }

  /**
   * @return Psr\Cache\CacheItemPoolInterface Cache implementation
   */
  public function getCache()
  {
    if (!$this->cache) {
      $this->cache = $this->createDefaultCache();
    }

    return $this->cache;
  }

  /**
   * @param array $cacheConfig
   */
  public function setCacheConfig(array $cacheConfig)
  {
    $this->config['cache_config'] = $cacheConfig;
  }

  /**
   * Set the Logger object
   * @param Psr\Log\LoggerInterface $logger
   */
  public function setLogger(LoggerInterface $logger)
  {
    $this->logger = $logger;
  }

  /**
   * @return Psr\Log\LoggerInterface implementation
   */
  public function getLogger()
  {
    if (!isset($this->logger)) {
      $this->logger = $this->createDefaultLogger();
    }

    return $this->logger;
  }

  protected function createDefaultLogger()
  {
    $logger = new Logger('google-api-php-client');
    if ($this->isAppEngine()) {
      $handler = new MonologSyslogHandler('app', LOG_USER, Logger::NOTICE);
    } else {
      $handler = new MonologStreamHandler('php://stderr', Logger::NOTICE);
    }
    $logger->pushHandler($handler);

    return $logger;
  }

  protected function createDefaultCache()
  {
    return new MemoryCacheItemPool;
  }

  /**
   * Set the Http Client object
   * @param WPvividGuzzleHttp\ClientInterface $http
   */
  public function setHttpClient(ClientInterface $http)
  {
    $this->http = $http;
  }

  /**
   * @return WPvividGuzzleHttp\ClientInterface implementation
   */
  public function getHttpClient()
  {
    if (null === $this->http) {
      $this->http = $this->createDefaultHttpClient();
    }

    return $this->http;
  }

  protected function createDefaultHttpClient()
  {
    $options = ['exceptions' => false];

    $version = ClientInterface::VERSION;
    if ('5' === $version[0]) {
      $options = [
        'base_url' => $this->config['base_path'],
        'defaults' => $options,
      ];
      if ($this->isAppEngine()) {
        // set StreamHandler on AppEngine by default
        $options['handler']  = new StreamHandler();
        $options['defaults']['verify'] = '/etc/ca-certificates.crt';
      }
    } else {
      // guzzle 6
        $options['base_uri'] = $this->config['base_path'];
        $options['verify'] = WPVIVID_PLUGIN_DIR.'/vendor/guzzle/guzzle/src/Guzzle/Http/Resources/cacert.pem';
    }

    return new WPvividGuzzleHttp\Client($options);
  }

  private function createApplicationDefaultCredentials()
  {
    $scopes = $this->prepareScopes();
    $sub = $this->config['subject'];
    $signingKey = $this->config['signing_key'];

    // create credentials using values supplied in setAuthConfig
    if ($signingKey) {
      $serviceAccountCredentials = array(
        'client_id' => $this->config['client_id'],
        'client_email' => $this->config['client_email'],
        'private_key' => $signingKey,
        'type' => 'service_account',
      );
      $credentials = CredentialsLoader::makeCredentials($scopes, $serviceAccountCredentials);
    } else {
      $credentials = ApplicationDefaultCredentials::getCredentials($scopes);
    }

    // for service account domain-wide authority (impersonating a user)
    // @see https://developers.google.com/identity/protocols/OAuth2ServiceAccount
    if ($sub) {
      if (!$credentials instanceof ServiceAccountCredentials) {
        throw new DomainException('domain-wide authority requires service account credentials');
      }

      $credentials->setSub($sub);
    }

    return $credentials;
  }

  protected function getAuthHandler()
  {
    // Be very careful using the cache, as the underlying auth library's cache
    // implementation is naive, and the cache keys do not account for user
    // sessions.
    //
    // @see https://github.com/google/google-api-php-client/issues/821
    return Google_AuthHandler_AuthHandlerFactory::build(
        $this->getCache(),
        $this->config['cache_config']
    );
  }

  private function createUserRefreshCredentials($scope, $refreshToken)
  {
    $creds = array_filter(
        array(
          'client_id' => $this->getClientId(),
          'client_secret' => $this->getClientSecret(),
          'refresh_token' => $refreshToken,
        )
    );

    return new UserRefreshCredentials($scope, $creds);
  }
}
includes/lib/aws-sdk-php-2.8.31/src/Aws/Common/Enum/Size.php000064400000002636151327705670017131 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\Common\Enum;

use Aws\Common\Enum;

/**
 * Contains enumerable byte-size values
 */
class Size extends Enum
{
    const B         = 1;
    const BYTE      = 1;
    const BYTES     = 1;

    const KB        = 1024;
    const KILOBYTE  = 1024;
    const KILOBYTES = 1024;

    const MB        = 1048576;
    const MEGABYTE  = 1048576;
    const MEGABYTES = 1048576;

    const GB        = 1073741824;
    const GIGABYTE  = 1073741824;
    const GIGABYTES = 1073741824;

    const TB        = 1099511627776;
    const TERABYTE  = 1099511627776;
    const TERABYTES = 1099511627776;

    const PB        = 1125899906842624;
    const PETABYTE  = 1125899906842624;
    const PETABYTES = 1125899906842624;

    const EB        = 1152921504606846976;
    const EXABYTE   = 1152921504606846976;
    const EXABYTES  = 1152921504606846976;
}
includes/lib/aws-sdk-php-2.8.31/src/Aws/Common/Enum/UaString.php000064400000002507151327705670017750 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\Common\Enum;

use Aws\Common\Enum;

/**
 * User-Agent header strings for various high level operations
 */
class UaString extends Enum
{
    /**
     * @var string Name of the option used to add to the UA string
     */
    const OPTION = 'ua.append';

    /**
     * @var string Resource iterator
     */
    const ITERATOR = 'ITR';

    /**
     * @var string Resource waiter
     */
    const WAITER = 'WTR';

    /**
     * @var string Session handlers (e.g. Amazon DynamoDB session handler)
     */
    const SESSION = 'SES';

    /**
     * @var string Multipart upload helper for Amazon S3
     */
    const MULTIPART_UPLOAD = 'MUP';

    /**
     * @var string Command executed during a batch transfer
     */
    const BATCH = 'BAT';
}
includes/lib/aws-sdk-php-2.8.31/src/Aws/Common/Enum/DateFormat.php000064400000001655151327705670020245 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\Common\Enum;

use Aws\Common\Enum;

/**
 * Contains enumerable date format values used in the SDK
 */
class DateFormat extends Enum
{
    const ISO8601    = 'Ymd\THis\Z';
    const ISO8601_S3 = 'Y-m-d\TH:i:s\Z';
    const RFC1123    = 'D, d M Y H:i:s \G\M\T';
    const RFC2822    = \DateTime::RFC2822;
    const SHORT      = 'Ymd';
}
includes/lib/aws-sdk-php-2.8.31/src/Aws/Common/Enum/Time.php000064400000002105151327705670017104 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\Common\Enum;

use Aws\Common\Enum;

/**
 * Contains enumerable time values
 */
class Time extends Enum
{
    const SECOND  = 1;
    const SECONDS = 1;

    const MINUTE  = 60;
    const MINUTES = 60;

    const HOUR    = 3600;
    const HOURS   = 3600;

    const DAY     = 86400;
    const DAYS    = 86400;

    const WEEK    = 604800;
    const WEEKS   = 604800;

    const MONTH   = 2592000;
    const MONTHS  = 2592000;

    const YEAR    = 31557600;
    const YEARS   = 31557600;
}
includes/lib/aws-sdk-php-2.8.31/src/Aws/Common/Enum/ClientOptions.php000064400000011506151327705670021005 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\Common\Enum;

use Aws\Common\Enum;

/**
 * Contains enumerable default factory options that can be passed to a client's factory method
 */
class ClientOptions extends Enum
{
    /**
     * AWS Access Key ID
     *
     * @deprecated Use "credentials" instead.
     */
    const KEY = 'key';

    /**
     * AWS secret access key
     *
     * @deprecated Use "credentials" instead.
     */
    const SECRET = 'secret';

    /**
     * Custom AWS security token to use with request authentication.
     *
     * @deprecated Use "credentials" instead.
     */
    const TOKEN = 'token';

    /**
     * Provide an array of "key", "secret", and "token" or an instance of
     * `Aws\Common\Credentials\CredentialsInterface`.
     */
    const CREDENTIALS = 'credentials';

    /**
     * @var string Name of a credential profile to read from your ~/.aws/credentials file
     */
    const PROFILE = 'profile';

    /**
     * @var string UNIX timestamp for when the custom credentials expire
     */
    const TOKEN_TTD = 'token.ttd';

    /**
     * @var string Used to cache credentials when using providers that require HTTP requests. Set the trueto use the
     *             default APC cache or provide a `Guzzle\Cache\CacheAdapterInterface` object.
     */
    const CREDENTIALS_CACHE = 'credentials.cache';

    /**
     * @var string Optional custom cache key to use with the credentials
     */
    const CREDENTIALS_CACHE_KEY = 'credentials.cache.key';

    /**
     * @var string Pass this option to specify a custom `Guzzle\Http\ClientInterface` to use if your credentials require
     *             a HTTP request (e.g. RefreshableInstanceProfileCredentials)
     */
    const CREDENTIALS_CLIENT = 'credentials.client';

    /**
     * @var string Region name (e.g. 'us-east-1', 'us-west-1', 'us-west-2', 'eu-west-1', etc...)
     */
    const REGION = 'region';

    /**
     * @var string URI Scheme of the base URL (e.g. 'https', 'http').
     */
    const SCHEME = 'scheme';

    /**
     * @var string Specify the name of the service
     */
    const SERVICE = 'service';

    /**
     * Instead of using a `region` and `scheme`, you can specify a custom base
     * URL for the client.
     *
     * @deprecated Use the "endpoint" option instead.
     */
    const BASE_URL = 'base_url';

    /**
     * @var string You can optionally provide a custom signature implementation used to sign requests
     */
    const SIGNATURE = 'signature';

    /**
     * @var string Set to explicitly override the service name used in signatures
     */
    const SIGNATURE_SERVICE = 'signature.service';

    /**
     * @var string Set to explicitly override the region name used in signatures
     */
    const SIGNATURE_REGION = 'signature.region';

    /**
     * @var string Option key holding an exponential backoff plugin
     */
    const BACKOFF = 'client.backoff';

    /**
     * @var string Option key holding the exponential backoff retries
     */
    const BACKOFF_RETRIES = 'client.backoff.retries';

    /**
     * @var string `Guzzle\Log\LogAdapterInterface` object used to log backoff retries. Use 'debug' to emit PHP
     *             warnings when a retry is issued.
     */
    const BACKOFF_LOGGER = 'client.backoff.logger';

    /**
     * @var string Optional template to use for exponential backoff log messages. See
     *             `Guzzle\Plugin\Backoff\BackoffLogger` for formatting information.
     */
    const BACKOFF_LOGGER_TEMPLATE = 'client.backoff.logger.template';

    /**
     * Set to true to use the bundled CA cert or pass the full path to an SSL
     * certificate bundle. This option should be modified when you encounter
     * curl error code 60. Set to "system" to use the cacert bundle on your
     * system.
     */
    const SSL_CERT = 'ssl.certificate_authority';

    /**
     * @var string Service description to use with the client
     */
    const SERVICE_DESCRIPTION = 'service.description';

    /**
     * @var string Whether or not modeled responses have transformations applied to them
     */
    const MODEL_PROCESSING = 'command.model_processing';

    /**
     * @var bool Set to false to disable validation
     */
    const VALIDATION = 'validation';

    /**
     * @var string API version used by the client
     */
    const VERSION = 'version';
}
includes/lib/aws-sdk-php-2.8.31/src/Aws/Common/Enum/Region.php000064400000003776151327705670017450 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\Common\Enum;

use Aws\Common\Enum;

/**
 * Contains enumerable region code values. These should be useful in most cases,
 * with Amazon S3 being the most notable exception
 *
 * @link http://docs.aws.amazon.com/general/latest/gr/rande.html AWS Regions and Endpoints
 */
class Region extends Enum
{
    const US_EAST_1           = 'us-east-1';
    const VIRGINIA            = 'us-east-1';
    const NORTHERN_VIRGINIA   = 'us-east-1';

    const US_WEST_1           = 'us-west-1';
    const CALIFORNIA          = 'us-west-1';
    const NORTHERN_CALIFORNIA = 'us-west-1';

    const US_WEST_2           = 'us-west-2';
    const OREGON              = 'us-west-2';

    const EU_WEST_1           = 'eu-west-1';
    const IRELAND             = 'eu-west-1';
    
    const EU_CENTRAL_1        = 'eu-central-1';
    const FRANKFURT           = 'eu-central-1';

    const AP_SOUTHEAST_1      = 'ap-southeast-1';
    const SINGAPORE           = 'ap-southeast-1';

    const AP_SOUTHEAST_2      = 'ap-southeast-2';
    const SYDNEY              = 'ap-southeast-2';

    const AP_NORTHEAST_1      = 'ap-northeast-1';
    const TOKYO               = 'ap-northeast-1';

    const SA_EAST_1           = 'sa-east-1';
    const SAO_PAULO           = 'sa-east-1';

    const CN_NORTH_1          = 'cn-north-1';
    const BEIJING             = 'cn-north-1';

    const US_GOV_WEST_1       = 'us-gov-west-1';
    const GOV_CLOUD_US        = 'us-gov-west-1';
}
includes/lib/aws-sdk-php-2.8.31/src/Aws/Common/HostNameUtils.php000064400000004655151327705670020055 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\Common;

use Guzzle\Http\Url;

/**
 * Utility class for parsing regions and services from URLs
 */
class HostNameUtils
{
    const DEFAULT_REGION = 'us-east-1';
    const DEFAULT_GOV_REGION = 'us-gov-west-1';

    /**
     * Parse the AWS region name from a URL
     *
     *
     * @param Url $url HTTP URL
     *
     * @return string
     * @link http://docs.aws.amazon.com/general/latest/gr/rande.html
     */
    public static function parseRegionName(Url $url)
    {
        // If we don't recognize the domain, just return the default
        if (substr($url->getHost(), -14) != '.amazonaws.com') {
            return self::DEFAULT_REGION;
        }

        $serviceAndRegion = substr($url->getHost(), 0, -14);
        // Special handling for S3 regions
        $separator = strpos($serviceAndRegion, 's3') === 0 ? '-' : '.';
        $separatorPos = strpos($serviceAndRegion, $separator);

        // If don't detect a separator, then return the default region
        if ($separatorPos === false) {
            return self::DEFAULT_REGION;
        }

        $region = substr($serviceAndRegion, $separatorPos + 1);

        // All GOV regions currently use the default GOV region
        if ($region == 'us-gov') {
            return self::DEFAULT_GOV_REGION;
        }

        return $region;
    }

    /**
     * Parse the AWS service name from a URL
     *
     * @param Url $url HTTP URL
     *
     * @return string Returns a service name (or empty string)
     * @link http://docs.aws.amazon.com/general/latest/gr/rande.html
     */
    public static function parseServiceName(Url $url)
    {
        // The service name is the first part of the host
        $parts = explode('.', $url->getHost(), 2);

        // Special handling for S3
        if (stripos($parts[0], 's3') === 0) {
            return 's3';
        }

        return $parts[0];
    }
}
includes/lib/aws-sdk-php-2.8.31/src/Aws/Common/Command/JsonCommand.php000064400000003125151327705670021073 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\Common\Command;

use Guzzle\Service\Command\OperationCommand;
use Guzzle\Http\Curl\CurlHandle;

/**
 * Adds AWS JSON body functionality to dynamically generated HTTP requests
 */
class JsonCommand extends OperationCommand
{
    /**
     * {@inheritdoc}
     */
    protected function build()
    {
        parent::build();

        // Ensure that the body of the request ALWAYS includes some JSON. By default, this is an empty object.
        if (!$this->request->getBody()) {
            $this->request->setBody('{}');
        }

        // Never send the Expect header when interacting with a JSON query service
        $this->request->removeHeader('Expect');

        // Always send JSON requests as a raw string rather than using streams to avoid issues with
        // cURL error code 65: "necessary data rewind wasn't possible".
        // This could be removed after PHP addresses https://bugs.php.net/bug.php?id=47204
        $this->request->getCurlOptions()->set(CurlHandle::BODY_AS_STRING, true);
    }
}
includes/lib/aws-sdk-php-2.8.31/src/Aws/Common/Command/AwsQueryVisitor.php000064400000011104151327705670022017 0ustar00<?php

namespace Aws\Common\Command;

use Guzzle\Http\Message\RequestInterface;
use Guzzle\Service\Description\Parameter;
use Guzzle\Service\Command\CommandInterface;
use Guzzle\Service\Command\LocationVisitor\Request\AbstractRequestVisitor;

/**
 * Location visitor used to serialize AWS query parameters (e.g. EC2, SES, SNS, SQS, etc) as POST fields
 */
class AwsQueryVisitor extends AbstractRequestVisitor
{
    private $fqname;

    public function visit(CommandInterface $command, RequestInterface $request, Parameter $param, $value)
    {
        $this->fqname = $command->getName();
        $query = array();
        $this->customResolver($value, $param, $query, $param->getWireName());
        $request->addPostFields($query);
    }

    /**
     * Map nested parameters into the location_key based parameters
     *
     * @param array     $value  Value to map
     * @param Parameter $param  Parameter that holds information about the current key
     * @param array     $query  Built up query string values
     * @param string    $prefix String to prepend to sub query values
     */
    protected function customResolver($value, Parameter $param, array &$query, $prefix = '')
    {
        switch ($param->getType()) {
            case 'object':
                $this->resolveObject($param, $value, $prefix, $query);
                break;
            case 'array':
                $this->resolveArray($param, $value, $prefix, $query);
                break;
            default:
                $query[$prefix] = $param->filter($value);
        }
    }

    /**
     * Custom handling for objects
     *
     * @param Parameter $param  Parameter for the object
     * @param array     $value  Value that is set for this parameter
     * @param string    $prefix Prefix for the resulting key
     * @param array     $query  Query string array passed by reference
     */
    protected function resolveObject(Parameter $param, array $value, $prefix, array &$query)
    {
        // Maps are implemented using additional properties
        $hasAdditionalProperties = ($param->getAdditionalProperties() instanceof Parameter);
        $additionalPropertyCount = 0;

        foreach ($value as $name => $v) {
            if ($subParam = $param->getProperty($name)) {
                // if the parameter was found by name as a regular property
                $key = $prefix . '.' . $subParam->getWireName();
                $this->customResolver($v, $subParam, $query, $key);
            } elseif ($hasAdditionalProperties) {
                // Handle map cases like &Attribute.1.Name=<name>&Attribute.1.Value=<value>
                $additionalPropertyCount++;
                $data = $param->getData();
                $keyName = isset($data['keyName']) ? $data['keyName'] : 'key';
                $valueName = isset($data['valueName']) ? $data['valueName'] : 'value';
                $query["{$prefix}.{$additionalPropertyCount}.{$keyName}"] = $name;
                $newPrefix = "{$prefix}.{$additionalPropertyCount}.{$valueName}";
                if (is_array($v)) {
                    $this->customResolver($v, $param->getAdditionalProperties(), $query, $newPrefix);
                } else {
                    $query[$newPrefix] = $param->filter($v);
                }
            }
        }
    }

    /**
     * Custom handling for arrays
     *
     * @param Parameter $param  Parameter for the object
     * @param array     $value  Value that is set for this parameter
     * @param string    $prefix Prefix for the resulting key
     * @param array     $query  Query string array passed by reference
     */
    protected function resolveArray(Parameter $param, array $value, $prefix, array &$query)
    {
        static $serializeEmpty = array(
            'SetLoadBalancerPoliciesForBackendServer' => 1,
            'SetLoadBalancerPoliciesOfListener' => 1,
            'UpdateStack' => 1
        );

        // For BC, serialize empty lists for specific operations
        if (!$value) {
            if (isset($serializeEmpty[$this->fqname])) {
                if (substr($prefix, -7) === '.member') {
                    $prefix = substr($prefix, 0, -7);
                }
                $query[$prefix] = '';
            }
            return;
        }

        $offset = $param->getData('offset') ?: 1;
        foreach ($value as $index => $v) {
            $index += $offset;
            if (is_array($v) && $items = $param->getItems()) {
                $this->customResolver($v, $items, $query, $prefix . '.' . $index);
            } else {
                $query[$prefix . '.' . $index] = $param->filter($v);
            }
        }
    }
}
includes/lib/aws-sdk-php-2.8.31/src/Aws/Common/Command/XmlResponseLocationVisitor.php000064400000004143151327705670024214 0ustar00<?php

namespace Aws\Common\Command;

use Guzzle\Service\Description\Operation;
use Guzzle\Service\Command\CommandInterface;
use Guzzle\Http\Message\Response;
use Guzzle\Service\Description\Parameter;
use Guzzle\Service\Command\LocationVisitor\Response\XmlVisitor;

/**
 * Class used for custom AWS XML response parsing of query services
 */
class XmlResponseLocationVisitor extends XmlVisitor
{
    /**
     * {@inheritdoc}
     */
    public function before(CommandInterface $command, array &$result)
    {
        parent::before($command, $result);

        // Unwrapped wrapped responses
        $operation = $command->getOperation();
        if ($operation->getServiceDescription()->getData('resultWrapped')) {
            $wrappingNode = $operation->getName() . 'Result';
            if (isset($result[$wrappingNode])) {
                $result = $result[$wrappingNode] + $result;
                unset($result[$wrappingNode]);
            }
        }
    }

    /**
     * Accounts for wrapper nodes
     * {@inheritdoc}
     */
    public function visit(
        CommandInterface $command,
        Response $response,
        Parameter $param,
        &$value,
        $context =  null
    ) {
        parent::visit($command, $response, $param, $value, $context);

        // Account for wrapper nodes (e.g. RDS, ElastiCache, etc)
        if ($param->getData('wrapper')) {
            $wireName = $param->getWireName();
            $value += $value[$wireName];
            unset($value[$wireName]);
        }
    }

    /**
     * Filter used when converting XML maps into associative arrays in service descriptions
     *
     * @param array  $value     Value to filter
     * @param string $entryName Name of each entry
     * @param string $keyName   Name of each key
     * @param string $valueName Name of each value
     *
     * @return array Returns the map of the XML data
     */
    public static function xmlMap($value, $entryName, $keyName, $valueName)
    {
        $result = array();
        foreach ($value as $entry) {
            $result[$entry[$keyName]] = $entry[$valueName];
        }

        return $result;
    }
}
includes/lib/aws-sdk-php-2.8.31/src/Aws/Common/Command/QueryCommand.php000064400000002703151327705670021270 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\Common\Command;

use Guzzle\Service\Command\OperationCommand;

/**
 * Adds AWS Query service serialization
 */
class QueryCommand extends OperationCommand
{
    /**
     * @var AwsQueryVisitor
     */
    protected static $queryVisitor;

    /**
     * @var XmlResponseLocationVisitor
     */
    protected static $xmlVisitor;

    /**
     * Register the aws.query visitor
     */
    protected function init()
    {
        // @codeCoverageIgnoreStart
        if (!self::$queryVisitor) {
            self::$queryVisitor = new AwsQueryVisitor();
        }
        if (!self::$xmlVisitor) {
            self::$xmlVisitor = new XmlResponseLocationVisitor();
        }
        // @codeCoverageIgnoreEnd

        $this->getRequestSerializer()->addVisitor('aws.query', self::$queryVisitor);
        $this->getResponseParser()->addVisitor('xml', self::$xmlVisitor);
    }
}
includes/lib/aws-sdk-php-2.8.31/src/Aws/Common/InstanceMetadata/Waiter/ServiceAvailable.php000064400000002703151327705670025147 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\Common\InstanceMetadata\Waiter;

use Aws\Common\Waiter\AbstractResourceWaiter;
use Guzzle\Http\Exception\CurlException;

/**
 * Waits until the instance metadata service is responding.  Will send up to
 * 4 requests with a 5 second delay between each try.  Each try can last up to
 * 11 seconds to complete if the service is not responding.
 *
 * @codeCoverageIgnore
 */
class ServiceAvailable extends AbstractResourceWaiter
{
    protected $interval = 5;
    protected $maxAttempts = 4;

    /**
     * {@inheritdoc}
     */
    public function doWait()
    {
        $request = $this->client->get();
        try {
            $request->getCurlOptions()->set(CURLOPT_CONNECTTIMEOUT, 10)
                ->set(CURLOPT_TIMEOUT, 10);
            $request->send();

            return true;
        } catch (CurlException $e) {
            return false;
        }
    }
}
includes/lib/aws-sdk-php-2.8.31/src/Aws/Common/InstanceMetadata/InstanceMetadataClient.php000064400000007317151327705670025065 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\Common\InstanceMetadata;

use Aws\Common\Enum\ClientOptions as Options;
use Aws\Common\Exception\InstanceProfileCredentialsException;
use Aws\Common\Credentials\Credentials;
use Aws\Common\Client\AbstractClient;
use Guzzle\Common\Collection;
use Guzzle\Http\Message\RequestFactory;

/**
 * Client used for interacting with the Amazon EC2 instance metadata server
 */
class InstanceMetadataClient extends AbstractClient
{
    /**
     * Factory method to create a new InstanceMetadataClient using an array
     * of configuration options.
     *
     * The configuration options accepts the following array keys and values:
     * - base_url: Override the base URL of the instance metadata server
     * - version:  Version of the metadata server to interact with
     *
     * @param array|Collection $config Configuration options
     *
     * @return InstanceMetadataClient
     */
    public static function factory($config = array())
    {
        $config = Collection::fromConfig($config, array(
            Options::BASE_URL => 'http://169.254.169.254/{version}/',
            'version'         => 'latest',
            'request.options' => array(
                'connect_timeout' => 5,
                'timeout'         => 10
            )
        ), array('base_url', 'version'));

        return new self($config);
    }

    /**
     * Constructor override
     */
    public function __construct(Collection $config)
    {
        $this->setConfig($config);
        $this->setBaseUrl($config->get(Options::BASE_URL));
        $this->defaultHeaders = new Collection();
        $this->setRequestFactory(RequestFactory::getInstance());
    }

    /**
     * Get instance profile credentials
     *
     * @return Credentials
     * @throws InstanceProfileCredentialsException
     */
    public function getInstanceProfileCredentials()
    {
        try {
            $request = $this->get('meta-data/iam/security-credentials/');
            $credentials = trim($request->send()->getBody(true));
            $result = $this->get("meta-data/iam/security-credentials/{$credentials}")->send()->json();
        } catch (\Exception $e) {
            $message = sprintf('Error retrieving credentials from the instance profile metadata server. When you are'
                . ' not running inside of Amazon EC2, you must provide your AWS access key ID and secret access key in'
                . ' the "key" and "secret" options when creating a client or provide an instantiated'
                . ' Aws\\Common\\Credentials\\CredentialsInterface object. (%s)', $e->getMessage());
            throw new InstanceProfileCredentialsException(esc_html($message), esc_attr($e->getCode()));
        }

        // Ensure that the status code was successful
        if ($result['Code'] !== 'Success') {
            $e = new InstanceProfileCredentialsException('Unexpected response code: ' . $result['Code']);
            $e->setStatusCode($result['Code']);
            throw $e;
        }

        return new Credentials(
            $result['AccessKeyId'],
            $result['SecretAccessKey'],
            $result['Token'],
            strtotime($result['Expiration'])
        );
    }
}
includes/lib/aws-sdk-php-2.8.31/src/Aws/Common/Enum.php000064400000002621151327705670016211 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\Common;

/**
 * Represents an enumerable set of values
 */
abstract class Enum
{
    /**
     * @var array A cache of all enum values to increase performance
     */
    protected static $cache = array();

    /**
     * Returns the names (or keys) of all of constants in the enum
     *
     * @return array
     */
    public static function keys()
    {
        return array_keys(static::values());
    }

    /**
     * Return the names and values of all the constants in the enum
     *
     * @return array
     */
    public static function values()
    {
        $class = get_called_class();

        if (!isset(self::$cache[$class])) {
            $reflected = new \ReflectionClass($class);
            self::$cache[$class] = $reflected->getConstants();
        }

        return self::$cache[$class];
    }
}
includes/lib/aws-sdk-php-2.8.31/src/Aws/Common/Facade/facade-classes.php000064400000011600151327705670021303 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\Common\Facade;

/**
 * The following classes are used to implement the static client facades and are aliased into the global namespaced. We
 * discourage the use of these classes directly by their full namespace since they are not autoloaded and are considered
 * an implementation detail that could possibly be changed in the future.
 */

// @codeCoverageIgnoreStart

class AutoScaling extends Facade
{
    public static function getServiceBuilderKey()
    {
        return 'autoscaling';
    }
}

class CloudFormation extends Facade
{
    public static function getServiceBuilderKey()
    {
        return 'cloudformation';
    }
}

class CloudFront extends Facade
{
    public static function getServiceBuilderKey()
    {
        return 'cloudfront';
    }
}

class CloudSearch extends Facade
{
    public static function getServiceBuilderKey()
    {
        return 'cloudsearch';
    }
}

class CloudTrail extends Facade
{
    public static function getServiceBuilderKey()
    {
        return 'cloudtrail';
    }
}

class CloudWatch extends Facade
{
    public static function getServiceBuilderKey()
    {
        return 'cloudwatch';
    }
}

class DataPipeline extends Facade
{
    public static function getServiceBuilderKey()
    {
        return 'datapipeline';
    }
}

class DirectConnect extends Facade
{
    public static function getServiceBuilderKey()
    {
        return 'directconnect';
    }
}

class DynamoDb extends Facade
{
    public static function getServiceBuilderKey()
    {
        return 'dynamodb';
    }
}

class Ec2 extends Facade
{
    public static function getServiceBuilderKey()
    {
        return 'ec2';
    }
}

class ElastiCache extends Facade
{
    public static function getServiceBuilderKey()
    {
        return 'elasticache';
    }
}

class ElasticBeanstalk extends Facade
{
    public static function getServiceBuilderKey()
    {
        return 'elasticbeanstalk';
    }
}

class ElasticLoadBalancing extends Facade
{
    public static function getServiceBuilderKey()
    {
        return 'elasticloadbalancing';
    }
}

class ElasticTranscoder extends Facade
{
    public static function getServiceBuilderKey()
    {
        return 'elastictranscoder';
    }
}

class Emr extends Facade
{
    public static function getServiceBuilderKey()
    {
        return 'emr';
    }
}

class Glacier extends Facade
{
    public static function getServiceBuilderKey()
    {
        return 'glacier';
    }
}

class Iam extends Facade
{
    public static function getServiceBuilderKey()
    {
        return 'iam';
    }
}

class ImportExport extends Facade
{
    public static function getServiceBuilderKey()
    {
        return 'importexport';
    }
}

class Kinesis extends Facade
{
    public static function getServiceBuilderKey()
    {
        return 'kinesis';
    }
}

class OpsWorks extends Facade
{
    public static function getServiceBuilderKey()
    {
        return 'opsworks';
    }
}

class Rds extends Facade
{
    public static function getServiceBuilderKey()
    {
        return 'rds';
    }
}

class Redshift extends Facade
{
    public static function getServiceBuilderKey()
    {
        return 'redshift';
    }
}

class Route53 extends Facade
{
    public static function getServiceBuilderKey()
    {
        return 'route53';
    }
}

class S3 extends Facade
{
    public static function getServiceBuilderKey()
    {
        return 's3';
    }
}

class SimpleDb extends Facade
{
    public static function getServiceBuilderKey()
    {
        return 'sdb';
    }
}

class Ses extends Facade
{
    public static function getServiceBuilderKey()
    {
        return 'ses';
    }
}

class Sns extends Facade
{
    public static function getServiceBuilderKey()
    {
        return 'sns';
    }
}

class Sqs extends Facade
{
    public static function getServiceBuilderKey()
    {
        return 'sqs';
    }
}

class StorageGateway extends Facade
{
    public static function getServiceBuilderKey()
    {
        return 'storagegateway';
    }
}

class Sts extends Facade
{
    public static function getServiceBuilderKey()
    {
        return 'sts';
    }
}

class Support extends Facade
{
    public static function getServiceBuilderKey()
    {
        return 'support';
    }
}

class Swf extends Facade
{
    public static function getServiceBuilderKey()
    {
        return 'swf';
    }
}

// @codeCoverageIgnoreEnd
includes/lib/aws-sdk-php-2.8.31/src/Aws/Common/Facade/FacadeInterface.php000064400000002225151327705670021434 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\Common\Facade;

/**
 * Interface that defines a client facade. Facades are convenient static classes that allow you to run client methods
 * statically on a default instance from the service builder. The facades themselves are aliased into the global
 * namespace for ease of use.
 *
 * @deprecated "Facades" are being removed in version 3.0 of the SDK.
 */
interface FacadeInterface
{
    /**
     * Returns the key used to access the client instance from the Service Builder
     *
     * @return string
     */
    public static function getServiceBuilderKey();
}
includes/lib/aws-sdk-php-2.8.31/src/Aws/Common/Facade/Facade.php000064400000004513151327705670017615 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\Common\Facade;

use Aws\Common\Aws;

/**
 * Base facade class that handles the delegation logic
 *
 * @deprecated "Facades" are being removed in version 3.0 of the SDK.
 */
abstract class Facade implements FacadeInterface
{
    /** @var Aws */
    protected static $serviceBuilder;

    /**
     * Mounts the facades by extracting information from the service builder config and using creating class aliases
     *
     * @param string|null $targetNamespace Namespace that the facades should be mounted to. Defaults to global namespace
     *
     * @param Aws $serviceBuilder
     */
    public static function mountFacades(Aws $serviceBuilder, $targetNamespace = null)
    {
        self::$serviceBuilder = $serviceBuilder;
        require_once __DIR__ . '/facade-classes.php';
        foreach ($serviceBuilder->getConfig() as $service) {
            if (isset($service['alias'], $service['class'])) {
                $facadeClass = __NAMESPACE__ . '\\' . $service['alias'];
                $facadeAlias = ltrim($targetNamespace . '\\' . $service['alias'], '\\');
                if (!class_exists($facadeAlias) && class_exists($facadeClass)) {
                    // @codeCoverageIgnoreStart
                    class_alias($facadeClass, $facadeAlias);
                    // @codeCoverageIgnoreEnd
                }
            }
        }
    }

    /**
     * Returns the instance of the client that the facade operates on
     *
     * @return \Aws\Common\Client\AwsClientInterface
     */
    public static function getClient()
    {
        return self::$serviceBuilder->get(static::getServiceBuilderKey());
    }

    public static function __callStatic($method, $args)
    {
        return call_user_func_array(array(self::getClient(), $method), $args);
    }
}
includes/lib/aws-sdk-php-2.8.31/src/Aws/Common/Iterator/AwsResourceIterator.php000064400000014102151327705670023047 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\Common\Iterator;

use Aws\Common\Enum\UaString as Ua;
use Aws\Common\Exception\RuntimeException;
Use Guzzle\Service\Resource\Model;
use Guzzle\Service\Resource\ResourceIterator;

/**
 * Iterate over a client command
 */
class AwsResourceIterator extends ResourceIterator
{
    /**
     * @var Model Result of a command
     */
    protected $lastResult = null;

    /**
     * Provides access to the most recent result obtained by the iterator. This makes it easier to extract any
     * additional information from the result which you do not have access to from the values emitted by the iterator
     *
     * @return Model|null
     */
    public function getLastResult()
    {
        return $this->lastResult;
    }

    /**
     * {@inheritdoc}
     * This AWS specific version of the resource iterator provides a default implementation of the typical AWS iterator
     * process. It relies on configuration and extension to implement the operation-specific logic of handling results
     * and nextTokens. This method will loop until resources are acquired or there are no more iterations available.
     */
    protected function sendRequest()
    {
        do {
            // Prepare the request including setting the next token
            $this->prepareRequest();
            if ($this->nextToken) {
                $this->applyNextToken();
            }

            // Execute the request and handle the results
            $this->command->add(Ua::OPTION, Ua::ITERATOR);
            $this->lastResult = $this->command->getResult();
            $resources = $this->handleResults($this->lastResult);
            $this->determineNextToken($this->lastResult);

            // If no resources collected, prepare to reiterate before yielding
            if ($reiterate = empty($resources) && $this->nextToken) {
                $this->command = clone $this->originalCommand;
            }
        } while ($reiterate);

        return $resources;
    }

    protected function prepareRequest()
    {
        // Get the limit parameter key to set
        $limitKey = $this->get('limit_key');
        if ($limitKey && ($limit = $this->command->get($limitKey))) {
            $pageSize = $this->calculatePageSize();

            // If the limit of the command is different than the pageSize of the iterator, use the smaller value
            if ($limit && $pageSize) {
                $realLimit = min($limit, $pageSize);
                $this->command->set($limitKey, $realLimit);
            }
        }
    }

    protected function handleResults(Model $result)
    {
        $results = array();

        // Get the result key that contains the results
        if ($resultKey = $this->get('result_key')) {
            $results = $this->getValueFromResult($result, $resultKey) ?: array();
        }

        return $results;
    }

    protected function applyNextToken()
    {
        // Get the token parameter key to set
        if ($tokenParam = $this->get('input_token')) {
            // Set the next token. Works with multi-value tokens
            if (is_array($tokenParam)) {
                if (is_array($this->nextToken) && count($tokenParam) === count($this->nextToken)) {
                    foreach (array_combine($tokenParam, $this->nextToken) as $param => $token) {
                        $this->command->set($param, $token);
                    }
                } else {
                    throw new RuntimeException('The definition of the iterator\'s token parameter and the actual token '
                        . 'value are not compatible.');
                }
            } else {
                $this->command->set($tokenParam, $this->nextToken);
            }
        }
    }

    protected function determineNextToken(Model $result)
    {
        $this->nextToken = null;

        // If the value of "more_results" is true or there is no "more_results" to check, then try to get the next token
        $moreKey = $this->get('more_results');
        if ($moreKey === null || $this->getValueFromResult($result, $moreKey)) {
            // Get the token key to check
            if ($tokenKey = $this->get('output_token')) {
                // Get the next token's value. Works with multi-value tokens
                if (is_array($tokenKey)) {
                    $this->nextToken = array();
                    foreach ($tokenKey as $key) {
                        $this->nextToken[] = $this->getValueFromResult($result, $key);
                    }
                } else {
                    $this->nextToken = $this->getValueFromResult($result, $tokenKey);
                }
            }
        }
    }

    /**
     * Extracts the value from the result using Collection::getPath. Also adds some additional logic for keys that need
     * to access n-1 indexes (e.g., ImportExport, Kinesis). The n-1 logic only works for the known cases. We will switch
     * to a jmespath implementation in the future to cover all cases
     *
     * @param Model  $result
     * @param string $key
     *
     * @return mixed|null
     */
    protected function getValueFromResult(Model $result, $key)
    {
        // Special handling for keys that need to access n-1 indexes
        if (strpos($key, '#') !== false) {
            $keyParts = explode('#', $key, 2);
            $items = $result->getPath(trim($keyParts[0], '/'));
            if ($items && is_array($items)) {
                $index = count($items) - 1;
                $key = strtr($key, array('#' => $index));
            }
        }

        // Get the value
        return $result->getPath($key);
    }
}
includes/lib/aws-sdk-php-2.8.31/src/Aws/Common/Iterator/AwsResourceIteratorFactory.php000064400000007250151327705670024405 0ustar00<?php

namespace Aws\Common\Iterator;

use Aws\Common\Exception\InvalidArgumentException;
use Guzzle\Common\Collection;
use Guzzle\Service\Command\CommandInterface;
use Guzzle\Service\Resource\ResourceIteratorFactoryInterface;

/**
 * Resource iterator factory used to instantiate the default AWS resource iterator with the correct configuration or
 * use a concrete iterator class if one exists
 */
class AwsResourceIteratorFactory implements ResourceIteratorFactoryInterface
{
    /**
     * @var array Default configuration values for iterators
     */
    protected static $defaultIteratorConfig = array(
        'input_token'  => null,
        'output_token' => null,
        'limit_key'    => null,
        'result_key'   => null,
        'more_results' => null,
    );

    /**
     * @var array Legacy configuration options mapped to their new names
     */
    private static $legacyConfigOptions = array(
        'token_param' => 'input_token',
        'token_key'   => 'output_token',
        'limit_param' => 'limit_key',
        'more_key'    => 'more_results',
    );

    /**
     * @var array Iterator configuration for each iterable operation
     */
    protected $config;

    /**
     * @var ResourceIteratorFactoryInterface Another factory that will be used first to instantiate the iterator
     */
    protected $primaryIteratorFactory;

    /**
     * @param array                            $config                 An array of configuration values for the factory
     * @param ResourceIteratorFactoryInterface $primaryIteratorFactory Another factory to use for chain of command
     */
    public function __construct(array $config, ResourceIteratorFactoryInterface $primaryIteratorFactory = null)
    {
        $this->primaryIteratorFactory = $primaryIteratorFactory;
        $this->config = array();
        foreach ($config as $name => $operation) {
            $this->config[$name] = $operation + self::$defaultIteratorConfig;
        }
    }

    public function build(CommandInterface $command, array $options = array())
    {
        // Get the configuration data for the command
        $commandName = $command->getName();
        $commandSupported = isset($this->config[$commandName]);
        $options = $this->translateLegacyConfigOptions($options);
        $options += $commandSupported ? $this->config[$commandName] : array();

        // Instantiate the iterator using the primary factory (if one was provided)
        if ($this->primaryIteratorFactory && $this->primaryIteratorFactory->canBuild($command)) {
            $iterator = $this->primaryIteratorFactory->build($command, $options);
        } elseif (!$commandSupported) {
            throw new InvalidArgumentException(esc_html("Iterator was not found for {$commandName}."));
        } else {
            // Instantiate a generic AWS resource iterator
            $iterator = new AwsResourceIterator($command, $options);
        }

        return $iterator;
    }

    public function canBuild(CommandInterface $command)
    {
        if ($this->primaryIteratorFactory) {
            return $this->primaryIteratorFactory->canBuild($command);
        } else {
            return isset($this->config[$command->getName()]);
        }
    }

    /**
     * @param array $config The config for a single operation
     *
     * @return array The modified config with legacy options translated
     */
    private function translateLegacyConfigOptions($config)
    {
        foreach (self::$legacyConfigOptions as $legacyOption => $newOption) {
            if (isset($config[$legacyOption])) {
                $config[$newOption] = $config[$legacyOption];
                unset($config[$legacyOption]);
            }
        }

        return $config;
    }
}
includes/lib/aws-sdk-php-2.8.31/src/Aws/Common/Waiter/WaiterClassFactory.php000064400000005665151327705670022324 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\Common\Waiter;

use Aws\Common\Exception\InvalidArgumentException;
use Guzzle\Inflection\Inflector;
use Guzzle\Inflection\InflectorInterface;

/**
 * Factory for creating {@see WaiterInterface} objects using a convention of
 * storing waiter classes in the Waiter folder of a client class namespace using
 * a snake_case to CamelCase conversion (e.g. camel_case => CamelCase).
 */
class WaiterClassFactory implements WaiterFactoryInterface
{
    /**
     * @var array List of namespaces used to look for classes
     */
    protected $namespaces;

    /**
     * @var InflectorInterface Inflector used to inflect class names
     */
    protected $inflector;

    /**
     * @param array|string       $namespaces Namespaces of waiter objects
     * @param InflectorInterface $inflector  Inflector used to resolve class names
     */
    public function __construct($namespaces = array(), InflectorInterface $inflector = null)
    {
        $this->namespaces = (array) $namespaces;
        $this->inflector = $inflector ?: Inflector::getDefault();
    }

    /**
     * Registers a namespace to check for Waiters
     *
     * @param string $namespace Namespace which contains Waiter classes
     *
     * @return self
     */
    public function registerNamespace($namespace)
    {
        array_unshift($this->namespaces, $namespace);

        return $this;
    }

    /**
     * {@inheritdoc}
     */
    public function build($waiter)
    {
        if (!($className = $this->getClassName($waiter))) {
            throw new InvalidArgumentException(esc_html("Waiter was not found matching {$waiter}."));
        }

        return new $className();
    }

    /**
     * {@inheritdoc}
     */
    public function canBuild($waiter)
    {
        return $this->getClassName($waiter) !== null;
    }

    /**
     * Get the name of a waiter class
     *
     * @param string $waiter Waiter name
     *
     * @return string|null
     */
    protected function getClassName($waiter)
    {
        $waiterName = $this->inflector->camel($waiter);

        // Determine the name of the class to load
        $className = null;
        foreach ($this->namespaces as $namespace) {
            $potentialClassName = $namespace . '\\' . $waiterName;
            if (class_exists($potentialClassName)) {
                return $potentialClassName;
            }
        }

        return null;
    }
}
includes/lib/aws-sdk-php-2.8.31/src/Aws/Common/Waiter/CallableWaiter.php000064400000004110151327705670021406 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\Common\Waiter;

use Aws\Common\Exception\InvalidArgumentException;
use Aws\Common\Exception\RuntimeException;

/**
 * Callable wait implementation
 */
class CallableWaiter extends AbstractWaiter
{
    /**
     * @var callable Callable function
     */
    protected $callable;

    /**
     * @var array Additional context for the callable function
     */
    protected $context = array();

    /**
     * Set the callable function to call in each wait attempt
     *
     * @param callable $callable Callable function
     *
     * @return self
     * @throws InvalidArgumentException when the method is not callable
     */
    public function setCallable($callable)
    {
        if (!is_callable($callable)) {
            throw new InvalidArgumentException('Value is not callable');
        }

        $this->callable = $callable;

        return $this;
    }

    /**
     * Set additional context for the callable function. This data will be passed into the callable function as the
     * second argument
     *
     * @param array $context Additional context
     *
     * @return self
     */
    public function setContext(array $context)
    {
        $this->context = $context;

        return $this;
    }

    /**
     * {@inheritdoc}
     */
    public function doWait()
    {
        if (!$this->callable) {
            throw new RuntimeException('No callable was specified for the wait method');
        }

        return call_user_func($this->callable, $this->attempts, $this->context);
    }
}
includes/lib/aws-sdk-php-2.8.31/src/Aws/Common/Waiter/AbstractWaiter.php000064400000007233151327705670021463 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\Common\Waiter;

use Aws\Common\Exception\RuntimeException;
use Guzzle\Common\AbstractHasDispatcher;

/**
 * Abstract wait implementation
 */
abstract class AbstractWaiter extends AbstractHasDispatcher implements WaiterInterface
{
    protected $attempts = 0;
    protected $config = array();

    /**
     * {@inheritdoc}
     */
    public static function getAllEvents()
    {
        return array(
            // About to check if the waiter needs to wait
            'waiter.before_attempt',
            // About to sleep
            'waiter.before_wait',
        );
    }

    /**
     * The max attempts allowed by the waiter
     *
     * @return int
     */
    public function getMaxAttempts()
    {
        return isset($this->config[self::MAX_ATTEMPTS]) ? $this->config[self::MAX_ATTEMPTS] : 10;
    }

    /**
     * Get the amount of time in seconds to delay between attempts
     *
     * @return int
     */
    public function getInterval()
    {
        return isset($this->config[self::INTERVAL]) ? $this->config[self::INTERVAL] : 0;
    }

    /**
     * {@inheritdoc}
     */
    public function setMaxAttempts($maxAttempts)
    {
        $this->config[self::MAX_ATTEMPTS] = $maxAttempts;

        return $this;
    }

    /**
     * {@inheritdoc}
     */
    public function setInterval($interval)
    {
        $this->config[self::INTERVAL] = $interval;

        return $this;
    }

    /**
     * Set config options associated with the waiter
     *
     * @param array $config Options to set
     *
     * @return self
     */
    public function setConfig(array $config)
    {
        if (isset($config['waiter.before_attempt'])) {
            $this->getEventDispatcher()->addListener('waiter.before_attempt', $config['waiter.before_attempt']);
            unset($config['waiter.before_attempt']);
        }

        if (isset($config['waiter.before_wait'])) {
            $this->getEventDispatcher()->addListener('waiter.before_wait', $config['waiter.before_wait']);
            unset($config['waiter.before_wait']);
        }

        $this->config = $config;

        return $this;
    }

    /**
     * {@inheritdoc}
     */
    public function wait()
    {
        $this->attempts = 0;

        do {
            $this->dispatch('waiter.before_attempt', array(
                'waiter' => $this,
                'config' => $this->config,
            ));

            if ($this->doWait()) {
                break;
            }

            if (++$this->attempts >= $this->getMaxAttempts()) {
                throw new RuntimeException('Wait method never resolved to true after ' . esc_html($this->attempts) . ' attempts');
            }

            $this->dispatch('waiter.before_wait', array(
                'waiter' => $this,
                'config' => $this->config,
            ));

            if ($this->getInterval()) {
                usleep($this->getInterval() * 1000000);
            }

        } while (1);
    }

    /**
     * Method to implement in subclasses
     *
     * @return bool Return true when successful, false on failure
     */
    abstract protected function doWait();
}
includes/lib/aws-sdk-php-2.8.31/src/Aws/Common/Waiter/WaiterConfig.php000064400000004067151327705670021127 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\Common\Waiter;

use Guzzle\Common\Collection;

/**
 * Configuration info of a waiter object
 */
class WaiterConfig extends Collection
{
    const WAITER_NAME = 'name';
    const MAX_ATTEMPTS = 'max_attempts';
    const INTERVAL = 'interval';
    const OPERATION = 'operation';
    const IGNORE_ERRORS = 'ignore_errors';
    const DESCRIPTION = 'description';
    const SUCCESS_TYPE = 'success.type';
    const SUCCESS_PATH = 'success.path';
    const SUCCESS_VALUE = 'success.value';
    const FAILURE_TYPE = 'failure.type';
    const FAILURE_PATH = 'failure.path';
    const FAILURE_VALUE = 'failure.value';

    /**
     * @param array $data Array of configuration directives
     */
    public function __construct(array $data = array())
    {
        $this->data = $data;
        $this->extractConfig();
    }

    /**
     * Create the command configuration variables
     */
    protected function extractConfig()
    {
        // Populate success.* and failure.* if specified in acceptor.*
        foreach ($this->data as $key => $value) {
            if (substr($key, 0, 9) == 'acceptor.') {
                $name = substr($key, 9);
                if (!isset($this->data["success.{$name}"])) {
                    $this->data["success.{$name}"] = $value;
                }
                if (!isset($this->data["failure.{$name}"])) {
                    $this->data["failure.{$name}"] = $value;
                }
                unset($this->data[$key]);
            }
        }
    }
}
includes/lib/aws-sdk-php-2.8.31/src/Aws/Common/Waiter/AbstractResourceWaiter.php000064400000002472151327705670023173 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\Common\Waiter;

use Aws\Common\Client\AwsClientInterface;
use Aws\Common\Exception\RuntimeException;

/**
 * Abstract waiter implementation used to wait on resources
 */
abstract class AbstractResourceWaiter extends AbstractWaiter implements ResourceWaiterInterface
{
    /**
     * @var AwsClientInterface
     */
    protected $client;

    /**
     * {@inheritdoc}
     */
    public function setClient(AwsClientInterface $client)
    {
        $this->client = $client;

        return $this;
    }

    /**
     * {@inheritdoc}
     */
    public function wait()
    {
        if (!$this->client) {
            throw new RuntimeException('No client has been specified on the waiter');
        }

        parent::wait();
    }
}
includes/lib/aws-sdk-php-2.8.31/src/Aws/Common/Waiter/CompositeWaiterFactory.php000064400000004257151327705670023215 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\Common\Waiter;

use Aws\Common\Exception\InvalidArgumentException;

/**
 * Factory that utilizes multiple factories for creating waiters
 */
class CompositeWaiterFactory implements WaiterFactoryInterface
{
    /**
     * @var array Array of factories
     */
    protected $factories;

    /**
     * @param array $factories Array of factories used to instantiate waiters
     */
    public function __construct(array $factories)
    {
        $this->factories = $factories;
    }

    /**
     * {@inheritdoc}
     */
    public function build($waiter)
    {
        if (!($factory = $this->getFactory($waiter))) {
            throw new InvalidArgumentException(esc_html("Waiter was not found matching {$waiter}."));
        }

        return $factory->build($waiter);
    }

    /**
     * {@inheritdoc}
     */
    public function canBuild($waiter)
    {
        return (bool) $this->getFactory($waiter);
    }

    /**
     * Add a factory to the composite factory
     *
     * @param WaiterFactoryInterface $factory Factory to add
     *
     * @return self
     */
    public function addFactory(WaiterFactoryInterface $factory)
    {
        $this->factories[] = $factory;

        return $this;
    }

    /**
     * Get the factory that matches the waiter name
     *
     * @param string $waiter Name of the waiter
     *
     * @return WaiterFactoryInterface|bool
     */
    protected function getFactory($waiter)
    {
        foreach ($this->factories as $factory) {
            if ($factory->canBuild($waiter)) {
                return $factory;
            }
        }

        return false;
    }
}
includes/lib/aws-sdk-php-2.8.31/src/Aws/Common/Waiter/ResourceWaiterInterface.php000064400000002010151327705670023314 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\Common\Waiter;

use Aws\Common\Client\AwsClientInterface;

/**
 * Interface used in conjunction with clients to wait on a resource
 */
interface ResourceWaiterInterface extends WaiterInterface
{
    /**
     * Set the client associated with the waiter
     *
     * @param AwsClientInterface $client Client to use with the waiter
     *
     * @return self
     */
    public function setClient(AwsClientInterface $client);
}
includes/lib/aws-sdk-php-2.8.31/src/Aws/Common/Waiter/ConfigResourceWaiter.php000064400000016660151327705670022641 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\Common\Waiter;

use Aws\Common\Exception\InvalidArgumentException;
use Aws\Common\Exception\RuntimeException;
use Aws\Common\Exception\ServiceResponseException;
use Guzzle\Service\Resource\Model;
use Guzzle\Service\Exception\ValidationException;

/**
 * Resource waiter driven by configuration options
 */
class ConfigResourceWaiter extends AbstractResourceWaiter
{
    /**
     * @var WaiterConfig Waiter configuration
     */
    protected $waiterConfig;

    /**
     * @param WaiterConfig $waiterConfig Waiter configuration
     */
    public function __construct(WaiterConfig $waiterConfig)
    {
        $this->waiterConfig = $waiterConfig;
        $this->setInterval($waiterConfig->get(WaiterConfig::INTERVAL));
        $this->setMaxAttempts($waiterConfig->get(WaiterConfig::MAX_ATTEMPTS));
    }

    /**
     * {@inheritdoc}
     */
    public function setConfig(array $config)
    {
        foreach ($config as $key => $value) {
            if (substr($key, 0, 7) == 'waiter.') {
                $this->waiterConfig->set(substr($key, 7), $value);
            }
        }

        if (!isset($config[self::INTERVAL])) {
            $config[self::INTERVAL] = $this->waiterConfig->get(WaiterConfig::INTERVAL);
        }

        if (!isset($config[self::MAX_ATTEMPTS])) {
            $config[self::MAX_ATTEMPTS] = $this->waiterConfig->get(WaiterConfig::MAX_ATTEMPTS);
        }

        return parent::setConfig($config);
    }

    /**
     * Get the waiter's configuration data
     *
     * @return WaiterConfig
     */
    public function getWaiterConfig()
    {
        return $this->waiterConfig;
    }

    /**
     * {@inheritdoc}
     */
    protected function doWait()
    {
        $params = $this->config;
        // remove waiter settings from the operation's input
        foreach (array_keys($params) as $key) {
            if (substr($key, 0, 7) == 'waiter.') {
                unset($params[$key]);
            }
        }

        $operation = $this->client->getCommand($this->waiterConfig->get(WaiterConfig::OPERATION), $params);

        try {
            return $this->checkResult($this->client->execute($operation));
        } catch (ValidationException $e) {
            throw new InvalidArgumentException(
                esc_html($this->waiterConfig->get(WaiterConfig::WAITER_NAME)) . ' waiter validation failed:  ' . esc_html($e->getMessage()),
                esc_attr($e->getCode()),
                esc_attr($e)
            );
        } catch (ServiceResponseException $e) {

            // Check if this exception satisfies a success or failure acceptor
            $transition = $this->checkErrorAcceptor($e);
            if (null !== $transition) {
                return $transition;
            }

            // Check if this exception should be ignored
            foreach ((array) $this->waiterConfig->get(WaiterConfig::IGNORE_ERRORS) as $ignore) {
                if ($e->getExceptionCode() == $ignore) {
                    // This exception is ignored, so it counts as a failed attempt rather than a fast-fail
                    return false;
                }
            }

            // Allow non-ignore exceptions to bubble through
            throw $e;
        }
    }

    /**
     * Check if an exception satisfies a success or failure acceptor
     *
     * @param ServiceResponseException $e
     *
     * @return bool|null Returns true for success, false for failure, and null for no transition
     */
    protected function checkErrorAcceptor(ServiceResponseException $e)
    {
        if ($this->waiterConfig->get(WaiterConfig::SUCCESS_TYPE) == 'error') {
            if ($e->getExceptionCode() == $this->waiterConfig->get(WaiterConfig::SUCCESS_VALUE)) {
                // Mark as a success
                return true;
            }
        }

        // Mark as an attempt
        return null;
    }

    /**
     * Check to see if the response model satisfies a success or failure state
     *
     * @param Model $result Result model
     *
     * @return bool
     * @throws RuntimeException
     */
    protected function checkResult(Model $result)
    {
        // Check if the result evaluates to true based on the path and output model
        if ($this->waiterConfig->get(WaiterConfig::SUCCESS_TYPE) == 'output' &&
            $this->checkPath(
                $result,
                $this->waiterConfig->get(WaiterConfig::SUCCESS_PATH),
                $this->waiterConfig->get(WaiterConfig::SUCCESS_VALUE)
            )
        ) {
            return true;
        }

        // It did not finish waiting yet. Determine if we need to fail-fast based on the failure acceptor.
        if ($this->waiterConfig->get(WaiterConfig::FAILURE_TYPE) == 'output') {
            $failureValue = $this->waiterConfig->get(WaiterConfig::FAILURE_VALUE);
            if ($failureValue) {
                $key = $this->waiterConfig->get(WaiterConfig::FAILURE_PATH);
                if ($this->checkPath($result, $key, $failureValue, false)) {
                    // Determine which of the results triggered the failure
                    $triggered = array_intersect(
                        (array) $this->waiterConfig->get(WaiterConfig::FAILURE_VALUE),
                        array_unique((array) $result->getPath($key))
                    );
                    // fast fail because the failure case was satisfied
                    throw new RuntimeException(
                        'A resource entered into an invalid state of "'
                        . esc_html(implode(', ', $triggered)) . '" while waiting with the "'
                        . esc_html($this->waiterConfig->get(WaiterConfig::WAITER_NAME)) . '" waiter.'
                    );
                }
            }
        }

        return false;
    }

    /**
     * Check to see if the path of the output key is satisfied by the value
     *
     * @param Model  $model      Result model
     * @param string $key        Key to check
     * @param string $checkValue Compare the key to the value
     * @param bool   $all        Set to true to ensure all value match or false to only match one
     *
     * @return bool
     */
    protected function checkPath(Model $model, $key = null, $checkValue = array(), $all = true)
    {
        // If no key is set, then just assume true because the request succeeded
        if (!$key) {
            return true;
        }

        if (!($result = $model->getPath($key))) {
            return false;
        }

        $total = $matches = 0;
        foreach ((array) $result as $value) {
            $total++;
            foreach ((array) $checkValue as $check) {
                if ($value == $check) {
                    $matches++;
                    break;
                }
            }
        }

        // When matching all values, ensure that the match count matches the total count
        if ($all && $total != $matches) {
            return false;
        }

        return $matches > 0;
    }
}
includes/lib/aws-sdk-php-2.8.31/src/Aws/Common/Waiter/WaiterConfigFactory.php000064400000005650151327705670022456 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\Common\Waiter;

use Aws\Common\Exception\InvalidArgumentException;
use Guzzle\Inflection\Inflector;
use Guzzle\Inflection\InflectorInterface;

/**
 * Factory for creating {@see WaiterInterface} objects using a configuration DSL.
 */
class WaiterConfigFactory implements WaiterFactoryInterface
{
    /**
     * @var array Configuration directives
     */
    protected $config;

    /**
     * @var InflectorInterface Inflector used to inflect class names
     */
    protected $inflector;

    /**
     * @param array              $config    Array of configuration directives
     * @param InflectorInterface $inflector Inflector used to resolve class names
     */
    public function __construct(
        array $config,
        InflectorInterface $inflector = null
    ) {
        $this->config = $config;
        $this->inflector = $inflector ?: Inflector::getDefault();
    }

    /**
     * {@inheritdoc}
     */
    public function build($waiter)
    {
        return new ConfigResourceWaiter($this->getWaiterConfig($waiter));
    }

    /**
     * {@inheritdoc}
     */
    public function canBuild($waiter)
    {
        return isset($this->config[$waiter]) || isset($this->config[$this->inflector->camel($waiter)]);
    }

    /**
     * Get waiter configuration data, taking __default__ and extensions into account
     *
     * @param string $name Waiter name
     *
     * @return WaiterConfig
     * @throws InvalidArgumentException
     */
    protected function getWaiterConfig($name)
    {
        if (!$this->canBuild($name)) {
            throw new InvalidArgumentException(esc_html('No waiter found matching "' . $name . '"'));
        }

        // inflect the name if needed
        $name = isset($this->config[$name]) ? $name : $this->inflector->camel($name);
        $waiter = new WaiterConfig($this->config[$name]);
        $waiter['name'] = $name;

        // Always use __default__ as the basis if it's set
        if (isset($this->config['__default__'])) {
            $parentWaiter = new WaiterConfig($this->config['__default__']);
            $waiter = $parentWaiter->overwriteWith($waiter);
        }

        // Allow for configuration extensions
        if (isset($this->config[$name]['extends'])) {
            $waiter = $this->getWaiterConfig($this->config[$name]['extends'])->overwriteWith($waiter);
        }

        return $waiter;
    }
}
includes/lib/aws-sdk-php-2.8.31/src/Aws/Common/Waiter/WaiterInterface.php000064400000003105151327705670021612 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\Common\Waiter;

/**
 * WaiterInterface used to wait on something to be in a particular state
 */
interface WaiterInterface
{
    const INTERVAL = 'waiter.interval';
    const MAX_ATTEMPTS = 'waiter.max_attempts';

    /**
     * Set the maximum number of attempts to make when waiting
     *
     * @param int $maxAttempts Max number of attempts
     *
     * @return self
     */
    public function setMaxAttempts($maxAttempts);

    /**
     * Set the amount of time to interval between attempts
     *
     * @param int $interval Interval in seconds
     *
     * @return self
     */
    public function setInterval($interval);

    /**
     * Set configuration options associated with the waiter
     *
     * @param array $config Configuration options to set
     *
     * @return self
     */
    public function setConfig(array $config);

    /**
     * Begin the waiting loop
     *
     * @throw RuntimeException if the method never resolves to true
     */
    public function wait();
}
includes/lib/aws-sdk-php-2.8.31/src/Aws/Common/Waiter/WaiterFactoryInterface.php000064400000002152151327705670023143 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\Common\Waiter;

/**
 * Waiter factory used to create waiter objects by short names
 */
interface WaiterFactoryInterface
{
    /**
     * Create a waiter by name
     *
     * @param string $waiter Name of the waiter to create
     *
     * @return WaiterInterface
     */
    public function build($waiter);

    /**
     * Check if the factory can create a waiter by a specific name
     *
     * @param string $waiter Name of the waiter to check
     *
     * @return bool
     */
    public function canBuild($waiter);
}
includes/lib/aws-sdk-php-2.8.31/src/Aws/Common/Credentials/CredentialsInterface.php000064400000004650151327705670023624 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\Common\Credentials;

/**
 * Provides access to the AWS credentials used for accessing AWS services: AWS
 * access key ID, secret access key, and security token. These credentials are
 * used to securely sign requests to AWS services.
 */
interface CredentialsInterface extends \Serializable
{
    /**
     * Returns the AWS access key ID for this credentials object.
     *
     * @return string
     */
    public function getAccessKeyId();

    /**
     * Returns the AWS secret access key for this credentials object.
     *
     * @return string
     */
    public function getSecretKey();

    /**
     * Get the associated security token if available
     *
     * @return string|null
     */
    public function getSecurityToken();

    /**
     * Get the UNIX timestamp in which the credentials will expire
     *
     * @return int|null
     */
    public function getExpiration();

    /**
     * Set the AWS access key ID for this credentials object.
     *
     * @param string $key AWS access key ID
     *
     * @return self
     */
    public function setAccessKeyId($key);

    /**
     * Set the AWS secret access key for this credentials object.
     *
     * @param string $secret AWS secret access key
     *
     * @return CredentialsInterface
     */
    public function setSecretKey($secret);

    /**
     * Set the security token to use with this credentials object
     *
     * @param string $token Security token
     *
     * @return self
     */
    public function setSecurityToken($token);

    /**
     * Set the UNIX timestamp in which the credentials will expire
     *
     * @param int $timestamp UNIX timestamp expiration
     *
     * @return self
     */
    public function setExpiration($timestamp);

    /**
     * Check if the credentials are expired
     *
     * @return bool
     */
    public function isExpired();
}
includes/lib/aws-sdk-php-2.8.31/src/Aws/Common/Credentials/NullCredentials.php000064400000002332151327705670022631 0ustar00<?php
namespace Aws\Common\Credentials;

/**
 * A blank set of credentials. AWS clients must be provided credentials, but
 * there are some types of requests that do not need authentication. This class
 * can be used to pivot on that scenario, and also serve as a mock credentials
 * object when testing
 *
 * @codeCoverageIgnore
 */
class NullCredentials implements CredentialsInterface
{
    public function getAccessKeyId()
    {
        return '';
    }

    public function getSecretKey()
    {
        return '';
    }

    public function getSecurityToken()
    {
        return null;
    }

    public function getExpiration()
    {
        return null;
    }

    public function isExpired()
    {
        return false;
    }

    public function serialize()
    {
        return 'N;';
    }

    public function unserialize($serialized)
    {
        // Nothing to do here.
    }

    public function setAccessKeyId($key)
    {
        // Nothing to do here.
    }

    public function setSecretKey($secret)
    {
        // Nothing to do here.
    }

    public function setSecurityToken($token)
    {
        // Nothing to do here.
    }

    public function setExpiration($timestamp)
    {
        // Nothing to do here.
    }
}
includes/lib/aws-sdk-php-2.8.31/src/Aws/Common/Credentials/AbstractRefreshableCredentials.php000064400000004360151327705670025630 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\Common\Credentials;

/**
 * Abstract decorator to provide a foundation for refreshable credentials
 */
abstract class AbstractRefreshableCredentials extends AbstractCredentialsDecorator
{
    /**
     * Get the underlying credentials, refreshing if necessary.
     *
     * @return Credentials
     */
    public function getCredentials()
    {
        if ($this->credentials->isExpired()) {
            $this->refresh();
        }

        return new Credentials(
            $this->credentials->getAccessKeyId(),
            $this->credentials->getSecretKey(),
            $this->credentials->getSecurityToken(),
            $this->credentials->getExpiration()
        );
    }

    /**
     * {@inheritdoc}
     */
    public function getAccessKeyId()
    {
        if ($this->credentials->isExpired()) {
            $this->refresh();
        }

        return $this->credentials->getAccessKeyId();
    }

    /**
     * {@inheritdoc}
     */
    public function getSecretKey()
    {
        if ($this->credentials->isExpired()) {
            $this->refresh();
        }

        return $this->credentials->getSecretKey();
    }

    /**
     * {@inheritdoc}
     */
    public function getSecurityToken()
    {
        if ($this->credentials->isExpired()) {
            $this->refresh();
        }

        return $this->credentials->getSecurityToken();
    }

    /**
     * {@inheritdoc}
     */
    public function serialize()
    {
        if ($this->credentials->isExpired()) {
            $this->refresh();
        }

        return $this->credentials->serialize();
    }

    /**
     * Attempt to get new credentials
     */
    abstract protected function refresh();
}
includes/lib/aws-sdk-php-2.8.31/src/Aws/Common/Credentials/RefreshableInstanceProfileCredentials.php000064400000005673151327705670027162 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\Common\Credentials;

use Aws\Common\InstanceMetadata\InstanceMetadataClient;
use Aws\Common\Exception\InstanceProfileCredentialsException;

/**
 * Credentials decorator used to implement retrieving credentials from the
 * EC2 metadata server
 */
class RefreshableInstanceProfileCredentials extends AbstractRefreshableCredentials
{
    /**
     * @var InstanceMetadataClient
     */
    protected $client;
    /** @var bool */
    private $customClient;

    /**
     * Constructs a new instance profile credentials decorator
     *
     * @param CredentialsInterface   $credentials Credentials to adapt
     * @param InstanceMetadataClient $client      Client used to get new credentials
     */
    public function __construct(CredentialsInterface $credentials, InstanceMetadataClient $client = null)
    {
        parent::__construct($credentials);
        $this->setClient($client);
    }

    public function setClient(InstanceMetadataClient $client = null)
    {
        $this->customClient = null !== $client;
        $this->client = $client ?: InstanceMetadataClient::factory();
    }

    public function serialize()
    {
        $serializable = array(
            'credentials' => parent::serialize(),
            'customClient' => $this->customClient,
        );

        if ($this->customClient) {
            $serializable['client'] = serialize($this->client);
        }

        return json_encode($serializable);
    }

    public function unserialize($value)
    {
        $serialized = json_decode($value, true);
        parent::unserialize($serialized['credentials']);
        $this->customClient = $serialized['customClient'];
        $this->client = $this->customClient ?
            unserialize($serialized['client'])
            : InstanceMetadataClient::factory();
    }

    /**
     * Attempt to get new credentials from the instance profile
     *
     * @throws InstanceProfileCredentialsException On error
     */
    protected function refresh()
    {
        $credentials = $this->client->getInstanceProfileCredentials();
        // Expire the token 5 minutes early to pre-fetch before expiring.
        $this->credentials->setAccessKeyId($credentials->getAccessKeyId())
            ->setSecretKey($credentials->getSecretKey())
            ->setSecurityToken($credentials->getSecurityToken())
            ->setExpiration($credentials->getExpiration() - 300);
    }
}
includes/lib/aws-sdk-php-2.8.31/src/Aws/Common/Credentials/CacheableCredentials.php000064400000005201151327705670023544 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\Common\Credentials;

use Guzzle\Cache\CacheAdapterInterface;

/**
 * Credentials decorator used to implement caching credentials
 */
class CacheableCredentials extends AbstractRefreshableCredentials
{
    /**
     * @var CacheAdapterInterface Cache adapter used to store credentials
     */
    protected $cache;

    /**
     * @var string Cache key used to store the credentials
     */
    protected $cacheKey;

    /**
     * CacheableCredentials is a decorator that decorates other credentials
     *
     * @param CredentialsInterface  $credentials Credentials to adapt
     * @param CacheAdapterInterface $cache       Cache to use to store credentials
     * @param string                $cacheKey    Cache key of the credentials
     */
    public function __construct(CredentialsInterface $credentials, CacheAdapterInterface $cache, $cacheKey)
    {
        $this->cache = $cache;
        $this->cacheKey = $cacheKey;

        parent::__construct($credentials);
    }

    /**
     * Attempt to get new credentials from cache or from the adapted object
     */
    protected function refresh()
    {
        if (!$cache = $this->cache->fetch($this->cacheKey)) {
            // The credentials were not found, so try again and cache if new
            $this->credentials->getAccessKeyId();
            if (!$this->credentials->isExpired()) {
                // The credentials were updated, so cache them
                $this->cache->save($this->cacheKey, $this->credentials, $this->credentials->getExpiration() - time());
            }
        } else {
            // The credentials were found in cache, so update the adapter object
            // if the cached credentials are not expired
            if (!$cache->isExpired()) {
                $this->credentials->setAccessKeyId($cache->getAccessKeyId());
                $this->credentials->setSecretKey($cache->getSecretKey());
                $this->credentials->setSecurityToken($cache->getSecurityToken());
                $this->credentials->setExpiration($cache->getExpiration());
            }
        }
    }
}
includes/lib/aws-sdk-php-2.8.31/src/Aws/Common/Credentials/Credentials.php000064400000027476151327705670022016 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\Common\Credentials;

use Aws\Common\Enum\ClientOptions as Options;
use Aws\Common\Exception\InvalidArgumentException;
use Aws\Common\Exception\RequiredExtensionNotLoadedException;
use Aws\Common\Exception\RuntimeException;
use Guzzle\Common\FromConfigInterface;
use Guzzle\Cache\CacheAdapterInterface;
use Guzzle\Cache\DoctrineCacheAdapter;
use Guzzle\Common\Collection;

/**
 * Basic implementation of the AWSCredentials interface that allows callers to
 * pass in the AWS access key and secret access in the constructor.
 */
class Credentials implements CredentialsInterface, FromConfigInterface
{
    const ENV_KEY = 'AWS_ACCESS_KEY_ID';
    const ENV_SECRET = 'AWS_SECRET_KEY';
    const ENV_SECRET_ACCESS_KEY = 'AWS_SECRET_ACCESS_KEY';
    const ENV_PROFILE = 'AWS_PROFILE';

    /** @var string AWS Access Key ID */
    protected $key;

    /** @var string AWS Secret Access Key */
    protected $secret;

    /** @var string AWS Security Token */
    protected $token;

    /** @var int Time to die of token */
    protected $ttd;

    /**
     * Get the available keys for the factory method
     *
     * @return array
     */
    public static function getConfigDefaults()
    {
        return array(
            Options::KEY                   => null,
            Options::SECRET                => null,
            Options::TOKEN                 => null,
            Options::TOKEN_TTD             => null,
            Options::PROFILE               => null,
            Options::CREDENTIALS_CACHE     => null,
            Options::CREDENTIALS_CACHE_KEY => null,
            Options::CREDENTIALS_CLIENT    => null
        );
    }

    /**
     * Factory method for creating new credentials.  This factory method will
     * create the appropriate credentials object with appropriate decorators
     * based on the passed configuration options.
     *
     * @param array $config Options to use when instantiating the credentials
     *
     * @return CredentialsInterface
     * @throws InvalidArgumentException If the caching options are invalid
     * @throws RuntimeException         If using the default cache and APC is disabled
     */
    public static function factory($config = array())
    {
        // Add default key values
        foreach (self::getConfigDefaults() as $key => $value) {
            if (!isset($config[$key])) {
                $config[$key] = $value;
            }
        }

        // Set up the cache
        $cache = $config[Options::CREDENTIALS_CACHE];
        $cacheKey = $config[Options::CREDENTIALS_CACHE_KEY] ?:
            'credentials_' . ($config[Options::KEY] ?: crc32(gethostname()));

        if (
            $cacheKey &&
            $cache instanceof CacheAdapterInterface &&
            $cached = self::createFromCache($cache, $cacheKey)
        ) {
            return $cached;
        }

        // Create the credentials object
        if (!$config[Options::KEY] || !$config[Options::SECRET]) {
            $credentials = self::createFromEnvironment($config);
        } else {
            // Instantiate using short or long term credentials
            $credentials = new static(
                $config[Options::KEY],
                $config[Options::SECRET],
                $config[Options::TOKEN],
                $config[Options::TOKEN_TTD]
            );
        }

        // Check if the credentials are refreshable, and if so, configure caching
        $cache = $config[Options::CREDENTIALS_CACHE];
        if ($cacheKey && $cache) {
            $credentials = self::createCache($credentials, $cache, $cacheKey);
        }

        return $credentials;
    }

    /**
     * Create credentials from the credentials ini file in the HOME directory.
     *
     * @param string|null $profile  Pass a specific profile to use. If no
     *                              profile is specified we will attempt to use
     *                              the value specified in the AWS_PROFILE
     *                              environment variable. If AWS_PROFILE is not
     *                              set, the "default" profile is used.
     * @param string|null $filename Pass a string to specify the location of the
     *                              credentials files. If null is passed, the
     *                              SDK will attempt to find the configuration
     *                              file at in your HOME directory at
     *                              ~/.aws/credentials.
     * @return CredentialsInterface
     * @throws \RuntimeException if the file cannot be found, if the file is
     *                           invalid, or if the profile is invalid.
     */
    public static function fromIni($profile = null, $filename = null)
    {
        if (!$filename) {
            $filename = self::getHomeDir() . '/.aws/credentials';
        }

        if (!$profile) {
            $profile = self::getEnvVar(self::ENV_PROFILE) ?: 'default';
        }

        if (!is_readable($filename) || ($data = parse_ini_file($filename, true)) === false) {
            throw new \RuntimeException(esc_html("Invalid AWS credentials file: {$filename}."));
        }

        if (!isset($data[$profile]['aws_access_key_id']) || !isset($data[$profile]['aws_secret_access_key'])) {
            throw new \RuntimeException(esc_html("Invalid AWS credentials profile {$profile} in {$filename}."));
        }

        return new self(
            $data[$profile]['aws_access_key_id'],
            $data[$profile]['aws_secret_access_key'],
            isset($data[$profile]['aws_security_token'])
                ? $data[$profile]['aws_security_token']
                : null
        );
    }

    /**
     * Constructs a new BasicAWSCredentials object, with the specified AWS
     * access key and AWS secret key
     *
     * @param string $accessKeyId     AWS access key ID
     * @param string $secretAccessKey AWS secret access key
     * @param string $token           Security token to use
     * @param int    $expiration      UNIX timestamp for when credentials expire
     */
    public function __construct($accessKeyId, $secretAccessKey, $token = null, $expiration = null)
    {
        $this->key = trim($accessKeyId);
        $this->secret = trim($secretAccessKey);
        $this->token = $token;
        $this->ttd = $expiration;
    }

    public function serialize()
    {
        return json_encode(array(
            Options::KEY       => $this->key,
            Options::SECRET    => $this->secret,
            Options::TOKEN     => $this->token,
            Options::TOKEN_TTD => $this->ttd
        ));
    }

    public function unserialize($serialized)
    {
        $data = json_decode($serialized, true);
        $this->key    = $data[Options::KEY];
        $this->secret = $data[Options::SECRET];
        $this->token  = $data[Options::TOKEN];
        $this->ttd    = $data[Options::TOKEN_TTD];
    }

    public function getAccessKeyId()
    {
        return $this->key;
    }

    public function getSecretKey()
    {
        return $this->secret;
    }

    public function getSecurityToken()
    {
        return $this->token;
    }

    public function getExpiration()
    {
        return $this->ttd;
    }

    public function isExpired()
    {
        return $this->ttd !== null && time() >= $this->ttd;
    }

    public function setAccessKeyId($key)
    {
        $this->key = $key;

        return $this;
    }

    public function setSecretKey($secret)
    {
        $this->secret = $secret;

        return $this;
    }

    public function setSecurityToken($token)
    {
        $this->token = $token;

        return $this;
    }

    public function setExpiration($timestamp)
    {
        $this->ttd = $timestamp;

        return $this;
    }

    /**
     * When no keys are provided, attempt to create them based on the
     * environment or instance profile credentials.
     *
     * @param array|Collection $config
     *
     * @return CredentialsInterface
     */
    private static function createFromEnvironment($config)
    {
        // Get key and secret from ENV variables
        $envKey = self::getEnvVar(self::ENV_KEY);
        if (!($envSecret = self::getEnvVar(self::ENV_SECRET))) {
            // Use AWS_SECRET_ACCESS_KEY if AWS_SECRET_KEY was not set
            $envSecret = self::getEnvVar(self::ENV_SECRET_ACCESS_KEY);
        }

        // Use credentials from the environment variables if available
        if ($envKey && $envSecret) {
            return new static($envKey, $envSecret);
        }

        try {
            // Use credentials from the INI file in HOME directory if available
            return self::fromIni($config[Options::PROFILE]);
        } catch (\RuntimeException $e) {
            // Otherwise, try using instance profile credentials (available on EC2 instances)
            return new RefreshableInstanceProfileCredentials(
                new static('', '', '', 1),
                $config[Options::CREDENTIALS_CLIENT]
            );
        }
    }

    private static function createFromCache(CacheAdapterInterface $cache, $cacheKey)
    {
        $cached = $cache->fetch($cacheKey);
        if ($cached instanceof CredentialsInterface && !$cached->isExpired()) {
            return new CacheableCredentials($cached, $cache, $cacheKey);
        }

        return null;
    }

    private static function createCache(CredentialsInterface $credentials, $cache, $cacheKey)
    {
        if ($cache === 'true' || $cache === true) {
            // If no cache adapter was provided, then create one for the user
            // @codeCoverageIgnoreStart
            if (!extension_loaded('apc')) {
                throw new RequiredExtensionNotLoadedException('PHP has not been compiled with APC. Unable to cache '
                    . 'the credentials.');
            } elseif (!class_exists('Doctrine\Common\Cache\ApcCache')) {
                throw new RuntimeException(
                    'Cannot set ' . esc_html(Options::CREDENTIALS_CACHE) . ' to true because the Doctrine cache component is '
                    . 'not installed. Either install doctrine/cache or pass in an instantiated '
                    . 'Guzzle\Cache\CacheAdapterInterface object'
                );
            }
            // @codeCoverageIgnoreEnd
            $cache = new DoctrineCacheAdapter(new \Doctrine\Common\Cache\ApcCache());
        } elseif (!($cache instanceof CacheAdapterInterface)) {
            throw new InvalidArgumentException('Unable to utilize caching with the specified options');
        }

        // Decorate the credentials with a cache
        return new CacheableCredentials($credentials, $cache, $cacheKey);
    }

    private static function getHomeDir()
    {
        // On Linux/Unix-like systems, use the HOME environment variable
        if ($homeDir = self::getEnvVar('HOME')) {
            return $homeDir;
        }

        // Get the HOMEDRIVE and HOMEPATH values for Windows hosts
        $homeDrive = self::getEnvVar('HOMEDRIVE');
        $homePath = self::getEnvVar('HOMEPATH');

        return ($homeDrive && $homePath) ? $homeDrive . $homePath : null;
    }

    /**
     * Fetches the value of an environment variable by checking $_SERVER and getenv().
     *
     * @param string $var Name of the environment variable
     *
     * @return mixed|null
     */
    private static function getEnvVar($var)
    {
        return isset($_SERVER[$var]) ? $_SERVER[$var] : getenv($var);
    }
}
includes/lib/aws-sdk-php-2.8.31/src/Aws/Common/Credentials/AbstractCredentialsDecorator.php000064400000005437151327705670025336 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\Common\Credentials;

/**
 * Abstract credentials decorator
 */
class AbstractCredentialsDecorator implements CredentialsInterface
{
    /**
     * @var CredentialsInterface Wrapped credentials object
     */
    protected $credentials;

    /**
     * Constructs a new BasicAWSCredentials object, with the specified AWS
     * access key and AWS secret key
     *
     * @param CredentialsInterface $credentials
     */
    public function __construct(CredentialsInterface $credentials)
    {
        $this->credentials = $credentials;
    }

    /**
     * {@inheritdoc}
     */
    public function serialize()
    {
        return $this->credentials->serialize();
    }

    /**
     * {@inheritdoc}
     */
    public function unserialize($serialized)
    {
        $this->credentials = new Credentials('', '');
        $this->credentials->unserialize($serialized);
    }

    /**
     * {@inheritdoc}
     */
    public function getAccessKeyId()
    {
        return $this->credentials->getAccessKeyId();
    }

    /**
     * {@inheritdoc}
     */
    public function getSecretKey()
    {
        return $this->credentials->getSecretKey();
    }

    /**
     * {@inheritdoc}
     */
    public function getSecurityToken()
    {
        return $this->credentials->getSecurityToken();
    }

    /**
     * {@inheritdoc}
     */
    public function getExpiration()
    {
        return $this->credentials->getExpiration();
    }

    /**
     * {@inheritdoc}
     */
    public function isExpired()
    {
        return $this->credentials->isExpired();
    }

    /**
     * {@inheritdoc}
     */
    public function setAccessKeyId($key)
    {
        $this->credentials->setAccessKeyId($key);

        return $this;
    }

    /**
     * {@inheritdoc}
     */
    public function setSecretKey($secret)
    {
        $this->credentials->setSecretKey($secret);

        return $this;
    }

    /**
     * {@inheritdoc}
     */
    public function setSecurityToken($token)
    {
        $this->credentials->setSecurityToken($token);

        return $this;
    }

    /**
     * {@inheritdoc}
     */
    public function setExpiration($timestamp)
    {
        $this->credentials->setExpiration($timestamp);

        return $this;
    }
}
includes/lib/aws-sdk-php-2.8.31/src/Aws/Common/Exception/UnexpectedValueException.php000064400000001441151327705670024222 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\Common\Exception;

/**
 * AWS SDK namespaced version of the SPL UnexpectedValueException.
 */
class UnexpectedValueException extends \UnexpectedValueException implements AwsExceptionInterface {}
includes/lib/aws-sdk-php-2.8.31/src/Aws/Common/Exception/OverflowException.php000064400000001414151327705670022724 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\Common\Exception;

/**
 * AWS SDK namespaced version of the SPL OverflowException.
 */
class OverflowException extends \OverflowException implements AwsExceptionInterface {}
includes/lib/aws-sdk-php-2.8.31/src/Aws/Common/Exception/InvalidArgumentException.php000064400000001441151327705670024212 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\Common\Exception;

/**
 * AWS SDK namespaced version of the SPL InvalidArgumentException.
 */
class InvalidArgumentException extends \InvalidArgumentException implements AwsExceptionInterface {}
includes/lib/aws-sdk-php-2.8.31/src/Aws/Common/Exception/BadMethodCallException.php000064400000001433151327705670023545 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\Common\Exception;

/**
 * AWS SDK namespaced version of the SPL BadMethodCallException.
 */
class BadMethodCallException extends \BadMethodCallException implements AwsExceptionInterface {}
includes/lib/aws-sdk-php-2.8.31/src/Aws/Common/Exception/RuntimeException.php000064400000001411151327705670022541 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\Common\Exception;

/**
 * AWS SDK namespaced version of the SPL RuntimeException.
 */
class RuntimeException extends \RuntimeException implements AwsExceptionInterface {}
includes/lib/aws-sdk-php-2.8.31/src/Aws/Common/Exception/RequiredExtensionNotLoadedException.php000064400000001463151327705670026374 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\Common\Exception;

/**
 * Thrown when a particular PHP extension is required to execute the guarded logic, but the extension is not loaded
 */
class RequiredExtensionNotLoadedException extends RuntimeException {}
includes/lib/aws-sdk-php-2.8.31/src/Aws/Common/Exception/TransferException.php000064400000001423151327705670022705 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\Common\Exception;

use Guzzle\Http\Exception\CurlException;

/**
 * Transfer request exception
 */
class TransferException extends CurlException implements AwsExceptionInterface {}
includes/lib/aws-sdk-php-2.8.31/src/Aws/Common/Exception/Parser/JsonQueryExceptionParser.php000064400000002356151327705670025477 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\Common\Exception\Parser;

use Guzzle\Http\Message\Response;

/**
 * Parses JSON encoded exception responses from query services
 */
class JsonQueryExceptionParser extends AbstractJsonExceptionParser
{
    /**
     * {@inheritdoc}
     */
    protected function doParse(array $data, Response $response)
    {
        if ($json = $data['parsed']) {
            if (isset($json['__type'])) {
                $parts = explode('#', $json['__type']);
                $data['code'] = isset($parts[1]) ? $parts[1] : $parts[0];
            }
            $data['message'] = isset($json['message']) ? $json['message'] : null;
        }

        return $data;
    }
}
includes/lib/aws-sdk-php-2.8.31/src/Aws/Common/Exception/Parser/DefaultXmlExceptionParser.php000064400000007211151327705670025600 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\Common\Exception\Parser;

use Guzzle\Http\Message\RequestInterface;
use Guzzle\Http\Message\Response;

/**
 * Parses default XML exception responses
 */
class DefaultXmlExceptionParser implements ExceptionParserInterface
{
    public function parse(RequestInterface $request, Response $response)
    {
        $data = array(
            'code'       => null,
            'message'    => null,
            'type'       => $response->isClientError() ? 'client' : 'server',
            'request_id' => null,
            'parsed'     => null
        );

        $body = $response->getBody(true);

        if (!$body) {
            $this->parseHeaders($request, $response, $data);
            return $data;
        }

        try {
            $xml = new \SimpleXMLElement($body);
            $this->parseBody($xml, $data);
            return $data;
        } catch (\Exception $e) {
            // Gracefully handle parse errors. This could happen when the
            // server responds with a non-XML response (e.g., private beta
            // services).
            $data['code'] = 'PhpInternalXmlParseError';
            $data['message'] = 'A non-XML response was received';
            return $data;
        }
    }

    /**
     * Parses additional exception information from the response headers
     *
     * @param RequestInterface $request  Request that was issued
     * @param Response         $response The response from the request
     * @param array            $data     The current set of exception data
     */
    protected function parseHeaders(RequestInterface $request, Response $response, array &$data)
    {
        $data['message'] = $response->getStatusCode() . ' ' . $response->getReasonPhrase();
        if ($requestId = $response->getHeader('x-amz-request-id')) {
            $data['request_id'] = $requestId;
            $data['message'] .= " (Request-ID: $requestId)";
        }
    }

    /**
     * Parses additional exception information from the response body
     *
     * @param \SimpleXMLElement $body The response body as XML
     * @param array             $data The current set of exception data
     */
    protected function parseBody(\SimpleXMLElement $body, array &$data)
    {
        $data['parsed'] = $body;

        $namespaces = $body->getDocNamespaces();
        if (isset($namespaces[''])) {
            // Account for the default namespace being defined and PHP not being able to handle it :(
            $body->registerXPathNamespace('ns', $namespaces['']);
            $prefix = 'ns:';
        } else {
            $prefix = '';
        }

        if ($tempXml = $body->xpath("//{$prefix}Code[1]")) {
            $data['code'] = (string) $tempXml[0];
        }

        if ($tempXml = $body->xpath("//{$prefix}Message[1]")) {
            $data['message'] = (string) $tempXml[0];
        }

        $tempXml = $body->xpath("//{$prefix}RequestId[1]");
        if (empty($tempXml)) {
            $tempXml = $body->xpath("//{$prefix}RequestID[1]");
        }
        if (isset($tempXml[0])) {
            $data['request_id'] = (string) $tempXml[0];
        }
    }
}
includes/lib/aws-sdk-php-2.8.31/src/Aws/Common/Exception/Parser/JsonRestExceptionParser.php000064400000002720151327705670025302 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\Common\Exception\Parser;

use Guzzle\Http\Message\Response;

/**
 * Parses JSON encoded exception responses from REST services
 */
class JsonRestExceptionParser extends AbstractJsonExceptionParser
{
    /**
     * {@inheritdoc}
     */
    protected function doParse(array $data, Response $response)
    {
        // Merge in error data from the JSON body
        if ($json = $data['parsed']) {
            $data = array_replace($data, $json);
        }

        // Correct error type from services like Amazon Glacier
        if (!empty($data['type'])) {
            $data['type'] = strtolower($data['type']);
        }

        // Retrieve the error code from services like Amazon Elastic Transcoder
        if ($code = (string) $response->getHeader('x-amzn-ErrorType')) {
            $data['code'] = substr($code, 0, strpos($code, ':'));
        }

        return $data;
    }
}
includes/lib/aws-sdk-php-2.8.31/src/Aws/Common/Exception/Parser/ExceptionParserInterface.php000064400000002561151327705670025436 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\Common\Exception\Parser;

use Guzzle\Http\Message\RequestInterface;
use Guzzle\Http\Message\Response;

/**
 * Interface used to parse exceptions into an associative array of data
 */
interface ExceptionParserInterface
{
    /**
     * Parses an exception into an array of data containing at minimum the
     * following array keys:
     * - type:       Exception type
     * - code:       Exception code
     * - message:    Exception message
     * - request_id: Request ID
     * - parsed:     The parsed representation of the data (array, SimpleXMLElement, etc)
     *
     * @param RequestInterface $request
     * @param Response         $response Unsuccessful response
     *
     * @return array
     */
    public function parse(RequestInterface $request, Response $response);
}
includes/lib/aws-sdk-php-2.8.31/src/Aws/Common/Exception/Parser/AbstractJsonExceptionParser.php000064400000004220151327705670026125 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\Common\Exception\Parser;

use Guzzle\Http\Message\RequestInterface;
use Guzzle\Http\Message\Response;

/**
 * Parses JSON encoded exception responses
 */
abstract class AbstractJsonExceptionParser implements ExceptionParserInterface
{
    /**
     * {@inheritdoc}
     */
    public function parse(RequestInterface $request, Response $response)
    {
        // Build array of default error data
        $data = array(
            'code'       => null,
            'message'    => null,
            'type'       => $response->isClientError() ? 'client' : 'server',
            'request_id' => (string) $response->getHeader('x-amzn-RequestId'),
            'parsed'     => null
        );

        // Parse the json and normalize key casings
        if (null !== $json = json_decode($response->getBody(true), true)) {
            $data['parsed'] = array_change_key_case($json);
        }

        // Do additional, protocol-specific parsing and return the result
        $data = $this->doParse($data, $response);

        // Remove "Fault" suffix from exception names
        if (isset($data['code']) && strpos($data['code'], 'Fault')) {
            $data['code'] = preg_replace('/^([a-zA-Z]+)Fault$/', '$1', $data['code']);
        }

        return $data;
    }

    /**
     * Pull relevant exception data out of the parsed json
     *
     * @param array    $data     The exception data
     * @param Response $response The response from the service containing the error
     *
     * @return array
     */
    abstract protected function doParse(array $data, Response $response);
}
includes/lib/aws-sdk-php-2.8.31/src/Aws/Common/Exception/NamespaceExceptionFactory.php000064400000006771151327705670024360 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\Common\Exception;

use Aws\Common\Exception\Parser\ExceptionParserInterface;
use Guzzle\Http\Message\RequestInterface;
use Guzzle\Http\Message\Response;

/**
 * Attempts to create exceptions by inferring the name from the code and a base
 * namespace that contains exceptions.  Exception classes are expected to be in
 * upper camelCase and always end in 'Exception'. 'Exception' will be appended
 * if it is not present in the exception code.
 */
class NamespaceExceptionFactory implements ExceptionFactoryInterface
{
    /**
     * @var ExceptionParserInterface $parser Parser used to parse responses
     */
    protected $parser;

    /**
     * @var string Base namespace containing exception classes
     */
    protected $baseNamespace;

    /**
     * @var string Default class to instantiate if a match is not found
     */
    protected $defaultException;

    /**
     * @param ExceptionParserInterface $parser           Parser used to parse exceptions
     * @param string                   $baseNamespace    Namespace containing exceptions
     * @param string                   $defaultException Default class to use if one is not mapped
     */
    public function __construct(
        ExceptionParserInterface $parser,
        $baseNamespace,
        $defaultException = 'Aws\Common\Exception\ServiceResponseException'
    ) {
        $this->parser = $parser;
        $this->baseNamespace = $baseNamespace;
        $this->defaultException = $defaultException;
    }

    /**
     * {@inheritdoc}
     */
    public function fromResponse(RequestInterface $request, Response $response)
    {
        $parts = $this->parser->parse($request, $response);

        // Removing leading 'AWS.' and embedded periods
        $className = $this->baseNamespace . '\\' . str_replace(array('AWS.', '.'), '', $parts['code']);
        if (substr($className, -9) !== 'Exception') {
            $className .= 'Exception';
        }

        $className = class_exists($className) ? $className : $this->defaultException;

        return $this->createException($className, $request, $response, $parts);
    }

    /**
     * Create an prepare an exception object
     *
     * @param string           $className Name of the class to create
     * @param RequestInterface $request   Request
     * @param Response         $response  Response received
     * @param array            $parts     Parsed exception data
     *
     * @return \Exception
     */
    protected function createException($className, RequestInterface $request, Response $response, array $parts)
    {
        $class = new $className($parts['message']);

        if ($class instanceof ServiceResponseException) {
            $class->setExceptionCode($parts['code']);
            $class->setExceptionType($parts['type']);
            $class->setResponse($response);
            $class->setRequest($request);
            $class->setRequestId($parts['request_id']);
        }

        return $class;
    }
}
includes/lib/aws-sdk-php-2.8.31/src/Aws/Common/Exception/ExceptionListener.php000064400000003227151327705670022712 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\Common\Exception;

use Guzzle\Common\Event;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;

/**
 * Converts generic Guzzle response exceptions into AWS specific exceptions
 */
class ExceptionListener implements EventSubscriberInterface
{
    /**
     * @var ExceptionFactoryInterface Factory used to create new exceptions
     */
    protected $factory;

    /**
     * @param ExceptionFactoryInterface $factory Factory used to create exceptions
     */
    public function __construct(ExceptionFactoryInterface $factory)
    {
        $this->factory = $factory;
    }

    /**
     * {@inheritdoc}
     */
    public static function getSubscribedEvents()
    {
        return array('request.error' => array('onRequestError', -1));
    }

    /**
     * Throws a more meaningful request exception if available
     *
     * @param Event $event Event emitted
     */
    public function onRequestError(Event $event)
    {
        $e = $this->factory->fromResponse($event['request'], $event['response']);
        $event->stopPropagation();
        throw $e;
    }
}
includes/lib/aws-sdk-php-2.8.31/src/Aws/Common/Exception/InstanceProfileCredentialsException.php000064400000002416151327705670026367 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\Common\Exception;

use Aws\Common\Exception\RuntimeException;

/**
 * Exception thrown when an error occurs with instance profile credentials
 */
class InstanceProfileCredentialsException extends RuntimeException
{
    /**
     * @var string
     */
    protected $statusCode;

    /**
     * Set the error response code received from the instance metadata
     *
     * @param string $code Response code
     */
    public function setStatusCode($code)
    {
        $this->statusCode = $code;
    }

    /**
     * Get the error response code from the service
     *
     * @return string|null
     */
    public function getStatusCode()
    {
        return $this->statusCode;
    }
}
includes/lib/aws-sdk-php-2.8.31/src/Aws/Common/Exception/LogicException.php000064400000001403151327705670022154 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\Common\Exception;

/**
 * AWS SDK namespaced version of the SPL LogicException.
 */
class LogicException extends \LogicException implements AwsExceptionInterface {}
includes/lib/aws-sdk-php-2.8.31/src/Aws/Common/Exception/AwsExceptionInterface.php000064400000001643151327705670023500 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\Common\Exception;

/**
 * "Marker Interface" implemented by every exception in the AWS SDK
 */
interface AwsExceptionInterface
{
    public function getCode();
    public function getLine();
    public function getFile();
    public function getMessage();
    public function getPrevious();
    public function getTrace();
}
includes/lib/aws-sdk-php-2.8.31/src/Aws/Common/Exception/ServiceResponseException.php000064400000011504151327705670024241 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\Common\Exception;

use Guzzle\Http\Message\RequestInterface;
use Guzzle\Http\Message\Response;

/**
 * Default AWS exception
 */
class ServiceResponseException extends RuntimeException
{
    /**
     * @var Response Response
     */
    protected $response;

    /**
     * @var RequestInterface Request
     */
    protected $request;

    /**
     * @var string Request ID
     */
    protected $requestId;

    /**
     * @var string Exception type (client / server)
     */
    protected $exceptionType;

    /**
     * @var string Exception code
     */
    protected $exceptionCode;

    /**
     * Set the exception code
     *
     * @param string $code Exception code
     */
    public function setExceptionCode($code)
    {
        $this->exceptionCode = $code;
    }

    /**
     * Get the exception code
     *
     * @return string|null
     */
    public function getExceptionCode()
    {
        return $this->exceptionCode;
    }

    /**
     * Set the exception type
     *
     * @param string $type Exception type
     */
    public function setExceptionType($type)
    {
        $this->exceptionType = $type;
    }

    /**
     * Get the exception type (one of client or server)
     *
     * @return string|null
     */
    public function getExceptionType()
    {
        return $this->exceptionType;
    }

    /**
     * Set the request ID
     *
     * @param string $id Request ID
     */
    public function setRequestId($id)
    {
        $this->requestId = $id;
    }

    /**
     * Get the Request ID
     *
     * @return string|null
     */
    public function getRequestId()
    {
        return $this->requestId;
    }

    /**
     * Set the associated response
     *
     * @param Response $response Response
     */
    public function setResponse(Response $response)
    {
        $this->response = $response;
    }

    /**
     * Get the associated response object
     *
     * @return Response|null
     */
    public function getResponse()
    {
        return $this->response;
    }

    /**
     * Set the associated request
     *
     * @param RequestInterface $request
     */
    public function setRequest(RequestInterface $request)
    {
        $this->request = $request;
    }

    /**
     * Get the associated request object
     *
     * @return RequestInterface|null
     */
    public function getRequest()
    {
        return $this->request;
    }

    /**
     * Get the status code of the response
     *
     * @return int|null
     */
    public function getStatusCode()
    {
        return $this->response ? $this->response->getStatusCode() : null;
    }

    /**
     * Cast to a string
     *
     * @return string
     */
    public function __toString()
    {
        $message = get_class($this) . ': '
            . 'AWS Error Code: ' . $this->getExceptionCode() . ', '
            . 'Status Code: ' . $this->getStatusCode() . ', '
            . 'AWS Request ID: ' . $this->getRequestId() . ', '
            . 'AWS Error Type: ' . $this->getExceptionType() . ', '
            . 'AWS Error Message: ' . $this->getMessage();

        // Add the User-Agent if available
        if ($this->request) {
            $message .= ', ' . 'User-Agent: ' . $this->request->getHeader('User-Agent');
        }

        return $message;
    }

    /**
     * Get the request ID of the error. This value is only present if a
     * response was received, and is not present in the event of a networking
     * error.
     *
     * Same as `getRequestId()` method, but matches the interface for SDKv3.
     *
     * @return string|null Returns null if no response was received
     */
    public function getAwsRequestId()
    {
        return $this->requestId;
    }

    /**
     * Get the AWS error type.
     *
     * Same as `getExceptionType()` method, but matches the interface for SDKv3.
     *
     * @return string|null Returns null if no response was received
     */
    public function getAwsErrorType()
    {
        return $this->exceptionType;
    }

    /**
     * Get the AWS error code.
     *
     * Same as `getExceptionCode()` method, but matches the interface for SDKv3.
     *
     * @return string|null Returns null if no response was received
     */
    public function getAwsErrorCode()
    {
        return $this->exceptionCode;
    }
}
includes/lib/aws-sdk-php-2.8.31/src/Aws/Common/Exception/DomainException.php000064400000001406151327705670022331 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\Common\Exception;

/**
 * AWS SDK namespaced version of the SPL DomainException.
 */
class DomainException extends \DomainException implements AwsExceptionInterface {}
includes/lib/aws-sdk-php-2.8.31/src/Aws/Common/Exception/ExceptionFactoryInterface.php000064400000002172151327705670024353 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\Common\Exception;

use Guzzle\Http\Message\RequestInterface;
use Guzzle\Http\Message\Response;

/**
 * Interface used to create AWS exception
 */
interface ExceptionFactoryInterface
{
    /**
     * Returns an AWS service specific exception
     *
     * @param RequestInterface $request  Unsuccessful request
     * @param Response         $response Unsuccessful response that was encountered
     *
     * @return \Exception|AwsExceptionInterface
     */
    public function fromResponse(RequestInterface $request, Response $response);
}
includes/lib/aws-sdk-php-2.8.31/src/Aws/Common/Exception/MultipartUploadException.php000064400000003147151327705670024254 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\Common\Exception;

use Aws\Common\Model\MultipartUpload\TransferStateInterface;

/**
 * Thrown when a {@see Aws\Common\MultipartUpload\TransferInterface} object encounters an error during transfer
 */
class MultipartUploadException extends RuntimeException
{
    /**
     * @var TransferStateInterface State of the transfer when the error was encountered
     */
    protected $state;

    /**
     * @param TransferStateInterface $state     Transfer state
     * @param \Exception             $exception Last encountered exception
     */
    public function __construct(TransferStateInterface $state, \Exception $exception = null)
    {
        parent::__construct(
            'An error was encountered while performing a multipart upload: ' . $exception->getMessage(),
            0,
            $exception
        );

        $this->state = $state;
    }

    /**
     * Get the state of the transfer
     *
     * @return TransferStateInterface
     */
    public function getState()
    {
        return $this->state;
    }
}
includes/lib/aws-sdk-php-2.8.31/src/Aws/Common/Exception/OutOfBoundsException.php000064400000001422151327705670023327 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\Common\Exception;

/**
 * AWS SDK namespaced version of the SPL OverflowException.
 */
class OutOfBoundsException extends \OutOfBoundsException implements AwsExceptionInterface {}
includes/lib/aws-sdk-php-2.8.31/src/Aws/Common/Client/UserAgentListener.php000064400000003643151327705670022133 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\Common\Client;

use Guzzle\Common\Event;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;

/**
 * Listener used to append strings to the User-Agent header of a request based
 * on the `ua.append` option. `ua.append` can contain a string or array of values.
 */
class UserAgentListener implements EventSubscriberInterface
{
    /**
     * @var string Option used to store User-Agent modifiers
     */
    const OPTION = 'ua.append';

    /**
     * {@inheritdoc}
     */
    public static function getSubscribedEvents()
    {
        return array('command.before_send' => 'onBeforeSend');
    }

    /**
     * Adds strings to the User-Agent header using the `ua.append` parameter of a command
     *
     * @param Event $event Event emitted
     */
    public function onBeforeSend(Event $event)
    {
        $command = $event['command'];
        if ($userAgentAppends = $command->get(self::OPTION)) {
            $request = $command->getRequest();
            $userAgent = (string) $request->getHeader('User-Agent');
            foreach ((array) $userAgentAppends as $append) {
                $append = ' ' . $append;
                if (strpos($userAgent, $append) === false) {
                    $userAgent .= $append;
                }
            }
            $request->setHeader('User-Agent', $userAgent);
        }
    }
}
includes/lib/aws-sdk-php-2.8.31/src/Aws/Common/Client/AbstractClient.php000064400000022744151327705670021435 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\Common\Client;

use Aws\Common\Aws;
use Aws\Common\Credentials\CredentialsInterface;
use Aws\Common\Enum\ClientOptions as Options;
use Aws\Common\Exception\InvalidArgumentException;
use Aws\Common\Exception\TransferException;
use Aws\Common\RulesEndpointProvider;
use Aws\Common\Signature\EndpointSignatureInterface;
use Aws\Common\Signature\SignatureInterface;
use Aws\Common\Signature\SignatureListener;
use Aws\Common\Waiter\WaiterClassFactory;
use Aws\Common\Waiter\CompositeWaiterFactory;
use Aws\Common\Waiter\WaiterFactoryInterface;
use Aws\Common\Waiter\WaiterConfigFactory;
use Guzzle\Common\Collection;
use Guzzle\Http\Exception\CurlException;
use Guzzle\Http\QueryAggregator\DuplicateAggregator;
use Guzzle\Service\Client;
use Guzzle\Service\Description\ServiceDescriptionInterface;

/**
 * Abstract AWS client
 */
abstract class AbstractClient extends Client implements AwsClientInterface
{
    /** @var CredentialsInterface AWS credentials */
    protected $credentials;

    /** @var SignatureInterface Signature implementation of the service */
    protected $signature;

    /** @var WaiterFactoryInterface Factory used to create waiter classes */
    protected $waiterFactory;

    /** @var DuplicateAggregator Cached query aggregator*/
    protected $aggregator;

    /**
     * {@inheritdoc}
     */
    public static function getAllEvents()
    {
        return array_merge(Client::getAllEvents(), array(
            'client.region_changed',
            'client.credentials_changed',
        ));
    }

    /**
     * @param CredentialsInterface $credentials AWS credentials
     * @param SignatureInterface   $signature   Signature implementation
     * @param Collection           $config      Configuration options
     *
     * @throws InvalidArgumentException if an endpoint provider isn't provided
     */
    public function __construct(CredentialsInterface $credentials, SignatureInterface $signature, Collection $config)
    {
        // Bootstrap with Guzzle
        parent::__construct($config->get(Options::BASE_URL), $config);
        $this->credentials = $credentials;
        $this->signature = $signature;
        $this->aggregator = new DuplicateAggregator();

        // Make sure the user agent is prefixed by the SDK version
        $this->setUserAgent('aws-sdk-php2/' . Aws::VERSION, true);

        // Add the event listener so that requests are signed before they are sent
        $dispatcher = $this->getEventDispatcher();
        $dispatcher->addSubscriber(new SignatureListener($credentials, $signature));

        if ($backoff = $config->get(Options::BACKOFF)) {
            $dispatcher->addSubscriber($backoff, -255);
        }
    }

    public function __call($method, $args)
    {
        if (substr($method, 0, 3) === 'get' && substr($method, -8) === 'Iterator') {
            // Allow magic method calls for iterators (e.g. $client->get<CommandName>Iterator($params))
            $commandOptions = isset($args[0]) ? $args[0] : null;
            $iteratorOptions = isset($args[1]) ? $args[1] : array();
            return $this->getIterator(substr($method, 3, -8), $commandOptions, $iteratorOptions);
        } elseif (substr($method, 0, 9) == 'waitUntil') {
            // Allow magic method calls for waiters (e.g. $client->waitUntil<WaiterName>($params))
            return $this->waitUntil(substr($method, 9), isset($args[0]) ? $args[0]: array());
        } else {
            return parent::__call(ucfirst($method), $args);
        }
    }

    /**
     * Get an endpoint for a specific region from a service description
     * @deprecated This function will no longer be updated to work with new regions.
     */
    public static function getEndpoint(ServiceDescriptionInterface $description, $region, $scheme)
    {
        try {
            $service = $description->getData('endpointPrefix');
            $provider = RulesEndpointProvider::fromDefaults();
            $result = $provider(array(
                'service' => $service,
                'region'  => $region,
                'scheme'  => $scheme
            ));
            return $result['endpoint'];
        } catch (\InvalidArgumentException $e) {
            throw new InvalidArgumentException(esc_html($e->getMessage()), 0, esc_attr($e));
        }
    }

    public function getCredentials()
    {
        return $this->credentials;
    }

    public function setCredentials(CredentialsInterface $credentials)
    {
        $formerCredentials = $this->credentials;
        $this->credentials = $credentials;

        // Dispatch an event that the credentials have been changed
        $this->dispatch('client.credentials_changed', array(
            'credentials'        => $credentials,
            'former_credentials' => $formerCredentials,
        ));

        return $this;
    }

    public function getSignature()
    {
        return $this->signature;
    }

    public function getRegions()
    {
        return $this->serviceDescription->getData('regions');
    }

    public function getRegion()
    {
        return $this->getConfig(Options::REGION);
    }

    public function setRegion($region)
    {
        $config = $this->getConfig();
        $formerRegion = $config->get(Options::REGION);
        $global = $this->serviceDescription->getData('globalEndpoint');
        $provider = $config->get('endpoint_provider');

        if (!$provider) {
            throw new \RuntimeException('No endpoint provider configured');
        }

        // Only change the region if the service does not have a global endpoint
        if (!$global || $this->serviceDescription->getData('namespace') === 'S3') {

            $endpoint = call_user_func(
                $provider,
                array(
                    'scheme'  => $config->get(Options::SCHEME),
                    'region'  => $region,
                    'service' => $config->get(Options::SERVICE)
                )
            );

            $this->setBaseUrl($endpoint['endpoint']);
            $config->set(Options::BASE_URL, $endpoint['endpoint']);
            $config->set(Options::REGION, $region);

            // Update the signature if necessary
            $signature = $this->getSignature();
            if ($signature instanceof EndpointSignatureInterface) {
                /** @var EndpointSignatureInterface $signature */
                $signature->setRegionName($region);
            }

            // Dispatch an event that the region has been changed
            $this->dispatch('client.region_changed', array(
                'region'        => $region,
                'former_region' => $formerRegion,
            ));
        }

        return $this;
    }

    public function waitUntil($waiter, array $input = array())
    {
        $this->getWaiter($waiter, $input)->wait();

        return $this;
    }

    public function getWaiter($waiter, array $input = array())
    {
        return $this->getWaiterFactory()->build($waiter)
            ->setClient($this)
            ->setConfig($input);
    }

    public function setWaiterFactory(WaiterFactoryInterface $waiterFactory)
    {
        $this->waiterFactory = $waiterFactory;

        return $this;
    }

    public function getWaiterFactory()
    {
        if (!$this->waiterFactory) {
            $clientClass = get_class($this);
            // Use a composite factory that checks for classes first, then config waiters
            $this->waiterFactory = new CompositeWaiterFactory(array(
                new WaiterClassFactory(substr($clientClass, 0, strrpos($clientClass, '\\')) . '\\Waiter')
            ));
            if ($this->getDescription()) {
                $waiterConfig = $this->getDescription()->getData('waiters') ?: array();
                $this->waiterFactory->addFactory(new WaiterConfigFactory($waiterConfig));
            }
        }

        return $this->waiterFactory;
    }

    public function getApiVersion()
    {
        return $this->serviceDescription->getApiVersion();
    }

    /**
     * {@inheritdoc}
     * @throws \Aws\Common\Exception\TransferException
     */
    public function send($requests)
    {
        try {
            return parent::send($requests);
        } catch (CurlException $e) {
            $wrapped = new TransferException($e->getMessage(), null, $e);
            $wrapped->setCurlHandle($e->getCurlHandle())
                ->setCurlInfo($e->getCurlInfo())
                ->setError($e->getError(), $e->getErrorNo())
                ->setRequest($e->getRequest());
            throw $wrapped;
        }
    }

    /**
     * Ensures that the duplicate query string aggregator is used so that
     * query string values are sent over the wire as foo=bar&foo=baz.
     * {@inheritdoc}
     */
    public function createRequest(
        $method = 'GET',
        $uri = null,
        $headers = null,
        $body = null,
        array $options = array()
    ) {
        $request = parent::createRequest($method, $uri, $headers, $body, $options);
        $request->getQuery()->setAggregator($this->aggregator);
        return $request;
    }
}
includes/lib/aws-sdk-php-2.8.31/src/Aws/Common/Client/ClientBuilder.php000064400000043121151327705670021250 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\Common\Client;

use Aws\Common\Credentials\Credentials;
use Aws\Common\Credentials\CredentialsInterface;
use Aws\Common\Credentials\NullCredentials;
use Aws\Common\Enum\ClientOptions as Options;
use Aws\Common\Exception\ExceptionListener;
use Aws\Common\Exception\InvalidArgumentException;
use Aws\Common\Exception\NamespaceExceptionFactory;
use Aws\Common\Exception\Parser\DefaultXmlExceptionParser;
use Aws\Common\Exception\Parser\ExceptionParserInterface;
use Aws\Common\Iterator\AwsResourceIteratorFactory;
use Aws\Common\RulesEndpointProvider;
use Aws\Common\Signature\EndpointSignatureInterface;
use Aws\Common\Signature\SignatureInterface;
use Aws\Common\Signature\SignatureV2;
use Aws\Common\Signature\SignatureV3Https;
use Aws\Common\Signature\SignatureV4;
use Guzzle\Common\Collection;
use Guzzle\Plugin\Backoff\BackoffPlugin;
use Guzzle\Plugin\Backoff\CurlBackoffStrategy;
use Guzzle\Plugin\Backoff\ExponentialBackoffStrategy;
use Guzzle\Plugin\Backoff\HttpBackoffStrategy;
use Guzzle\Plugin\Backoff\TruncatedBackoffStrategy;
use Guzzle\Service\Description\ServiceDescription;
use Guzzle\Service\Resource\ResourceIteratorClassFactory;
use Guzzle\Log\LogAdapterInterface;
use Guzzle\Log\ClosureLogAdapter;
use Guzzle\Plugin\Backoff\BackoffLogger;

/**
 * Builder for creating AWS service clients
 */
class ClientBuilder
{
    /**
     * @var array Default client config
     */
    protected static $commonConfigDefaults = array('scheme' => 'https');

    /**
     * @var array Default client requirements
     */
    protected static $commonConfigRequirements = array(Options::SERVICE_DESCRIPTION);

    /**
     * @var string The namespace of the client
     */
    protected $clientNamespace;

    /**
     * @var array The config options
     */
    protected $config = array();

    /**
     * @var array The config defaults
     */
    protected $configDefaults = array();

    /**
     * @var array The config requirements
     */
    protected $configRequirements = array();

    /**
     * @var ExceptionParserInterface The Parser interface for the client
     */
    protected $exceptionParser;

    /**
     * @var array Array of configuration data for iterators available for the client
     */
    protected $iteratorsConfig = array();

    /** @var string */
    private $clientClass;

    /** @var string */
    private $serviceName;

    /**
     * Factory method for creating the client builder
     *
     * @param string $namespace The namespace of the client
     *
     * @return ClientBuilder
     */
    public static function factory($namespace = null)
    {
        return new static($namespace);
    }

    /**
     * Constructs a client builder
     *
     * @param string $namespace The namespace of the client
     */
    public function __construct($namespace = null)
    {
        $this->clientNamespace = $namespace;

        // Determine service and class name
        $this->clientClass = 'Aws\Common\Client\DefaultClient';

        if ($this->clientNamespace) {
            $this->serviceName = substr($this->clientNamespace, strrpos($this->clientNamespace, '\\') + 1);
            $this->clientClass = $this->clientNamespace . '\\' . $this->serviceName . 'Client';
        }
    }

    /**
     * Sets the config options
     *
     * @param array|Collection $config The config options
     *
     * @return ClientBuilder
     */
    public function setConfig($config)
    {
        $this->config = $this->processArray($config);

        return $this;
    }

    /**
     * Sets the config options' defaults
     *
     * @param array|Collection $defaults The default values
     *
     * @return ClientBuilder
     */
    public function setConfigDefaults($defaults)
    {
        $this->configDefaults = $this->processArray($defaults);

        return $this;
    }

    /**
     * Sets the required config options
     *
     * @param array|Collection $required The required config options
     *
     * @return ClientBuilder
     */
    public function setConfigRequirements($required)
    {
        $this->configRequirements = $this->processArray($required);

        return $this;
    }

    /**
     * Sets the exception parser. If one is not provided the builder will use
     * the default XML exception parser.
     *
     * @param ExceptionParserInterface $parser The exception parser
     *
     * @return ClientBuilder
     */
    public function setExceptionParser(ExceptionParserInterface $parser)
    {
        $this->exceptionParser = $parser;

        return $this;
    }

    /**
     * Set the configuration for the client's iterators
     *
     * @param array $config Configuration data for client's iterators
     *
     * @return ClientBuilder
     */
    public function setIteratorsConfig(array $config)
    {
        $this->iteratorsConfig = $config;

        return $this;
    }

    /**
     * Performs the building logic using all of the parameters that have been
     * set and falling back to default values. Returns an instantiate service
     * client with credentials prepared and plugins attached.
     *
     * @return AwsClientInterface
     * @throws InvalidArgumentException
     */
    public function build()
    {
        // Resolve configuration
        $config = Collection::fromConfig(
            $this->config,
            array_merge(self::$commonConfigDefaults, $this->configDefaults),
            (self::$commonConfigRequirements + $this->configRequirements)
        );

        if ($config[Options::VERSION] === 'latest') {
            $config[Options::VERSION] = constant("{$this->clientClass}::LATEST_API_VERSION");
        }

        if (!isset($config['endpoint_provider'])) {
            $config['endpoint_provider'] = RulesEndpointProvider::fromDefaults();
        }

        // Resolve the endpoint, signature, and credentials
        $description = $this->updateConfigFromDescription($config);
        $signature = $this->getSignature($description, $config);
        $credentials = $this->getCredentials($config);
        $this->extractHttpConfig($config);

        // Resolve exception parser
        if (!$this->exceptionParser) {
            $this->exceptionParser = new DefaultXmlExceptionParser();
        }

        // Resolve backoff strategy
        $backoff = $config->get(Options::BACKOFF);
        if ($backoff === null) {
            $retries = isset($config[Options::BACKOFF_RETRIES]) ? $config[Options::BACKOFF_RETRIES] : 3;
            $backoff = $this->createDefaultBackoff($retries);
            $config->set(Options::BACKOFF, $backoff);
        }

        if ($backoff) {
            $this->addBackoffLogger($backoff, $config);
        }

        /** @var AwsClientInterface $client */
        $client = new $this->clientClass($credentials, $signature, $config);
        $client->setDescription($description);

        // Add exception marshaling so that more descriptive exception are thrown
        if ($this->clientNamespace) {
            $exceptionFactory = new NamespaceExceptionFactory(
                $this->exceptionParser,
                "{$this->clientNamespace}\\Exception",
                "{$this->clientNamespace}\\Exception\\{$this->serviceName}Exception"
            );
            $client->addSubscriber(new ExceptionListener($exceptionFactory));
        }

        // Add the UserAgentPlugin to append to the User-Agent header of requests
        $client->addSubscriber(new UserAgentListener());

        // Filters used for the cache plugin
        $client->getConfig()->set(
            'params.cache.key_filter',
            'header=date,x-amz-date,x-amz-security-token,x-amzn-authorization'
        );

        // Set the iterator resource factory based on the provided iterators config
        $client->setResourceIteratorFactory(new AwsResourceIteratorFactory(
            $this->iteratorsConfig,
            new ResourceIteratorClassFactory($this->clientNamespace . '\\Iterator')
        ));

        // Disable parameter validation if needed
        if ($config->get(Options::VALIDATION) === false) {
            $params = $config->get('command.params') ?: array();
            $params['command.disable_validation'] = true;
            $config->set('command.params', $params);
        }

        return $client;
    }

    /**
     * Add backoff logging to the backoff plugin if needed
     *
     * @param BackoffPlugin $plugin Backoff plugin
     * @param Collection    $config Configuration settings
     *
     * @throws InvalidArgumentException
     */
    protected function addBackoffLogger(BackoffPlugin $plugin, Collection $config)
    {
        // The log option can be set to `debug` or an instance of a LogAdapterInterface
        if ($logger = $config->get(Options::BACKOFF_LOGGER)) {
            $format = $config->get(Options::BACKOFF_LOGGER_TEMPLATE);
            if ($logger === 'debug') {
                $logger = new ClosureLogAdapter(function ($message) {
                    trigger_error(esc_html($message) . "\n");
                });
            } elseif (!($logger instanceof LogAdapterInterface)) {
                throw new InvalidArgumentException(
                    esc_html(Options::BACKOFF_LOGGER) . ' must be set to `debug` or an instance of '
                        . 'Guzzle\\Common\\Log\\LogAdapterInterface'
                );
            }
            // Create the plugin responsible for logging exponential backoff retries
            $logPlugin = new BackoffLogger($logger);
            // You can specify a custom format or use the default
            if ($format) {
                $logPlugin->setTemplate($format);
            }
            $plugin->addSubscriber($logPlugin);
        }
    }

    /**
     * Ensures that an array (e.g. for config data) is actually in array form
     *
     * @param array|Collection $array The array data
     *
     * @return array
     * @throws InvalidArgumentException if the arg is not an array or Collection
     */
    protected function processArray($array)
    {
        if ($array instanceof Collection) {
            $array = $array->getAll();
        }

        if (!is_array($array)) {
            throw new InvalidArgumentException('The config must be provided as an array or Collection.');
        }

        return $array;
    }

    /**
     * Update a configuration object from a service description
     *
     * @param Collection $config Config to update
     *
     * @return ServiceDescription
     * @throws InvalidArgumentException
     */
    protected function updateConfigFromDescription(Collection $config)
    {
        $description = $config->get(Options::SERVICE_DESCRIPTION);
        if (!($description instanceof ServiceDescription)) {
            // Inject the version into the sprintf template if it is a string
            if (is_string($description)) {
                $description = sprintf($description, $config->get(Options::VERSION));
            }
            $description = ServiceDescription::factory($description);
            $config->set(Options::SERVICE_DESCRIPTION, $description);
        }

        if (!$config->get(Options::SERVICE)) {
            $config->set(Options::SERVICE, $description->getData('endpointPrefix'));
        }

        if ($iterators = $description->getData('iterators')) {
            $this->setIteratorsConfig($iterators);
        }

        $this->handleRegion($config);
        $this->handleEndpoint($config);

        return $description;
    }

    /**
     * Return an appropriate signature object for a a client based on the
     * "signature" configuration setting, or the default signature specified in
     * a service description. The signature can be set to a valid signature
     * version identifier string or an instance of Aws\Common\Signature\SignatureInterface.
     *
     * @param ServiceDescription $description Description that holds a signature option
     * @param Collection         $config      Configuration options
     *
     * @return SignatureInterface
     * @throws InvalidArgumentException
     */
    protected function getSignature(ServiceDescription $description, Collection $config)
    {
        // If a custom signature has not been provided, then use the default
        // signature setting specified in the service description.
        $signature = $config->get(Options::SIGNATURE) ?: $description->getData('signatureVersion');

        if (is_string($signature)) {
            if ($signature == 'v4') {
                $signature = new SignatureV4();
            } elseif ($signature == 'v2') {
                $signature = new SignatureV2();
            } elseif ($signature == 'v3https') {
                $signature = new SignatureV3Https();
            } else {
                throw new InvalidArgumentException(esc_html("Invalid signature type: {$signature}"));
            }
        } elseif (!($signature instanceof SignatureInterface)) {
            throw new InvalidArgumentException('The provided signature is not '
                . 'a signature version string or an instance of '
                . 'Aws\\Common\\Signature\\SignatureInterface');
        }

        // Allow a custom service name or region value to be provided
        if ($signature instanceof EndpointSignatureInterface) {

            // Determine the service name to use when signing
            $signature->setServiceName($config->get(Options::SIGNATURE_SERVICE)
                ?: $description->getData('signingName')
                ?: $description->getData('endpointPrefix'));

            // Determine the region to use when signing requests
            $signature->setRegionName($config->get(Options::SIGNATURE_REGION) ?: $config->get(Options::REGION));
        }

        return $signature;
    }

    protected function getCredentials(Collection $config)
    {
        $credentials = $config->get(Options::CREDENTIALS);

        if (is_array($credentials)) {
            $credentials = Credentials::factory($credentials);
        } elseif ($credentials === false) {
            $credentials = new NullCredentials();
        } elseif (!$credentials instanceof CredentialsInterface) {
            $credentials = Credentials::factory($config);
        }

        return $credentials;
    }

    private function handleRegion(Collection $config)
    {
        // Make sure a valid region is set
        $region = $config[Options::REGION];
        $description = $config[Options::SERVICE_DESCRIPTION];
        $global = $description->getData('globalEndpoint');

        if (!$global && !$region) {
            throw new InvalidArgumentException(
                'A region is required when using ' . esc_html($description->getData('serviceFullName'))
            );
        } elseif ($global && !$region) {
            $config[Options::REGION] = 'us-east-1';
        }
    }

    private function handleEndpoint(Collection $config)
    {
        // Alias "endpoint" with "base_url" for forwards compatibility.
        if ($config['endpoint']) {
            $config[Options::BASE_URL] = $config['endpoint'];
            return;
        }

        if ($config[Options::BASE_URL]) {
            return;
        }

        $endpoint = call_user_func(
            $config['endpoint_provider'],
            array(
                'scheme'  => $config[Options::SCHEME],
                'region'  => $config[Options::REGION],
                'service' => $config[Options::SERVICE]
            )
        );

        $config[Options::BASE_URL] = $endpoint['endpoint'];

        // Set a signature if one was not explicitly provided.
        if (!$config->hasKey(Options::SIGNATURE)
            && isset($endpoint['signatureVersion'])
        ) {
            $config->set(Options::SIGNATURE, $endpoint['signatureVersion']);
        }

        // The the signing region if endpoint rule specifies one.
        if (isset($endpoint['credentialScope'])) {
            $scope = $endpoint['credentialScope'];
            if (isset($scope['region'])) {
                $config->set(Options::SIGNATURE_REGION, $scope['region']);
            }
        }
    }

    private function createDefaultBackoff($retries = 3)
    {
        return new BackoffPlugin(
            // Retry failed requests up to 3 times if it is determined that the request can be retried
            new TruncatedBackoffStrategy($retries,
                // Retry failed requests with 400-level responses due to throttling
                new ThrottlingErrorChecker($this->exceptionParser,
                    // Retry failed requests due to transient network or cURL problems
                    new CurlBackoffStrategy(null,
                        // Retry failed requests with 500-level responses
                        new HttpBackoffStrategy(array(500, 503, 509),
                            // Retry requests that failed due to expired credentials
                            new ExpiredCredentialsChecker($this->exceptionParser,
                                new ExponentialBackoffStrategy()
                            )
                        )
                    )
                )
            )
        );
    }

    private function extractHttpConfig(Collection $config)
    {
        $http = $config['http'];

        if (!is_array($http)) {
            return;
        }

        if (isset($http['verify'])) {
            $config[Options::SSL_CERT] = $http['verify'];
        }
    }
}
includes/lib/aws-sdk-php-2.8.31/src/Aws/Common/Client/ThrottlingErrorChecker.php000064400000004532151327705670023163 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\Common\Client;

use Aws\Common\Exception\Parser\ExceptionParserInterface;
use Guzzle\Http\Exception\HttpException;
use Guzzle\Http\Message\RequestInterface;
use Guzzle\Http\Message\Response;
use Guzzle\Plugin\Backoff\BackoffStrategyInterface;
use Guzzle\Plugin\Backoff\AbstractBackoffStrategy;

/**
 * Backoff logic that handles throttling exceptions from services
 */
class ThrottlingErrorChecker extends AbstractBackoffStrategy
{
    /** @var array Whitelist of exception codes (as indexes) that indicate throttling */
    protected static $throttlingExceptions = array(
        'RequestLimitExceeded'                   => true,
        'Throttling'                             => true,
        'ThrottlingException'                    => true,
        'ProvisionedThroughputExceededException' => true,
        'RequestThrottled'                       => true,
    );

    /**
     * @var ExceptionParserInterface Exception parser used to parse exception responses
     */
    protected $exceptionParser;

    public function __construct(ExceptionParserInterface $exceptionParser, BackoffStrategyInterface $next = null)
    {
        $this->exceptionParser = $exceptionParser;
        if ($next) {
            $this->setNext($next);
        }
    }

    /**
     * {@inheritdoc}
     */
    public function makesDecision()
    {
        return true;
    }

    /**
     * {@inheritdoc}
     */
    protected function getDelay(
        $retries,
        RequestInterface $request,
        Response $response = null,
        HttpException $e = null
    ) {
        if ($response && $response->isClientError()) {
            $parts = $this->exceptionParser->parse($request, $response);
            return isset(self::$throttlingExceptions[$parts['code']]) ? true : null;
        }
    }
}
includes/lib/aws-sdk-php-2.8.31/src/Aws/Common/Client/AwsClientInterface.php000064400000006272151327705670022243 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\Common\Client;

use Aws\Common\Credentials\CredentialsInterface;
use Aws\Common\Signature\SignatureInterface;
use Aws\Common\Waiter\WaiterFactoryInterface;
use Aws\Common\Waiter\WaiterInterface;
use Guzzle\Service\ClientInterface;

/**
 * Interface that all AWS clients implement
 */
interface AwsClientInterface extends ClientInterface
{
    /**
     * Returns the AWS credentials associated with the client
     *
     * @return CredentialsInterface
     */
    public function getCredentials();

    /**
     * Sets the credentials object associated with the client
     *
     * @param CredentialsInterface $credentials Credentials object to use
     *
     * @return self
     */
    public function setCredentials(CredentialsInterface $credentials);

    /**
     * Returns the signature implementation used with the client
     *
     * @return SignatureInterface
     */
    public function getSignature();

    /**
     * Get a list of available regions and region data
     *
     * @return array
     */
    public function getRegions();

    /**
     * Get the name of the region to which the client is configured to send requests
     *
     * @return string
     */
    public function getRegion();

    /**
     * Change the region to which the client is configured to send requests
     *
     * @param string $region Name of the region
     *
     * @return self
     */
    public function setRegion($region);

    /**
     * Get the waiter factory being used by the client
     *
     * @return WaiterFactoryInterface
     */
    public function getWaiterFactory();

    /**
     * Set the waiter factory to use with the client
     *
     * @param WaiterFactoryInterface $waiterFactory Factory used to create waiters
     *
     * @return self
     */
    public function setWaiterFactory(WaiterFactoryInterface $waiterFactory);

    /**
     * Wait until a resource is available or an associated waiter returns true
     *
     * @param string $waiter Name of the waiter
     * @param array  $input  Values used as input for the underlying operation and to control the waiter
     *
     * @return self
     */
    public function waitUntil($waiter, array $input = array());

    /**
     * Get a named waiter object
     *
     * @param string $waiter Name of the waiter
     * @param array  $input  Values used as input for the underlying operation and to control the waiter
     *
     * @return WaiterInterface
     */
    public function getWaiter($waiter, array $input = array());

    /**
     * Get the API version of the client (e.g. 2006-03-01)
     *
     * @return string
     */
    public function getApiVersion();
}
includes/lib/aws-sdk-php-2.8.31/src/Aws/Common/Client/UploadBodyListener.php000064400000006505151327705670022300 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\Common\Client;

use Aws\Common\Exception\InvalidArgumentException;
use Guzzle\Common\Event;
use Guzzle\Http\EntityBody;
use Guzzle\Service\Command\AbstractCommand as Command;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;

/**
 * Prepares the body parameter of a command such that the parameter is more flexible (e.g. accepts file handles) with
 * the value it accepts but converts it to the correct format for the command. Also looks for a "Filename" parameter.
 */
class UploadBodyListener implements EventSubscriberInterface
{
    /**
     * @var array The names of the commands of which to modify the body parameter
     */
    protected $commands;

    /**
     * @var string The key for the upload body parameter
     */
    protected $bodyParameter;

    /**
     * @var string The key for the source file parameter
     */
    protected $sourceParameter;

    /**
     * @param array  $commands        The commands to modify
     * @param string $bodyParameter   The key for the body parameter
     * @param string $sourceParameter The key for the source file parameter
     */
    public function __construct(array $commands, $bodyParameter = 'Body', $sourceParameter = 'SourceFile')
    {
        $this->commands = $commands;
        $this->bodyParameter = (string) $bodyParameter;
        $this->sourceParameter = (string) $sourceParameter;
    }

    /**
     * {@inheritdoc}
     */
    public static function getSubscribedEvents()
    {
        return array('command.before_prepare' => array('onCommandBeforePrepare'));
    }

    /**
     * Converts filenames and file handles into EntityBody objects before the command is validated
     *
     * @param Event $event Event emitted
     * @throws InvalidArgumentException
     */
    public function onCommandBeforePrepare(Event $event)
    {
        /** @var Command $command */
        $command = $event['command'];
        if (in_array($command->getName(), $this->commands)) {
            // Get the interesting parameters
            $source = $command->get($this->sourceParameter);
            $body = $command->get($this->bodyParameter);

            // If a file path is passed in then get the file handle
            if (is_string($source) && file_exists($source)) {
                $body = fopen($source, 'r');
            }

            // Prepare the body parameter and remove the source file parameter
            if (null !== $body) {
                $command->remove($this->sourceParameter);
                $command->set($this->bodyParameter, EntityBody::factory($body));
            } else {
                throw new InvalidArgumentException(esc_html("You must specify a non-null value for the {$this->bodyParameter} or {$this->sourceParameter} parameters."));
            }
        }
    }
}
includes/lib/aws-sdk-php-2.8.31/src/Aws/Common/Client/DefaultClient.php000064400000006706151327705670021256 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\Common\Client;

use Aws\Common\Enum\ClientOptions as Options;
use Guzzle\Common\Collection;

/**
 * Generic client for interacting with an AWS service
 */
class DefaultClient extends AbstractClient
{
    /**
     * Factory method to create a default client using an array of configuration options.
     *
     * The following array keys and values are available options:
     *
     * Credential options ((`key`, `secret`, and optional `token`) OR `credentials` is required):
     *
     * - key: AWS Access Key ID
     * - secret: AWS secret access key
     * - credentials: You can optionally provide a custom `Aws\Common\Credentials\CredentialsInterface` object
     * - token: Custom AWS security token to use with request authentication. Please note that not all services accept temporary credentials. See http://docs.aws.amazon.com/STS/latest/UsingSTS/UsingTokens.html
     * - token.ttd: UNIX timestamp for when the custom credentials expire
     * - credentials.cache.key: Optional custom cache key to use with the credentials
     * - credentials.client: Pass this option to specify a custom `Guzzle\Http\ClientInterface` to use if your credentials require a HTTP request (e.g. RefreshableInstanceProfileCredentials)
     *
     * Region and endpoint options (Some services do not require a region while others do. Check the service specific user guide documentation for details):
     *
     * - region: Region name (e.g. 'us-east-1', 'us-west-1', 'us-west-2', 'eu-west-1', etc...)
     * - scheme: URI Scheme of the base URL (e.g. 'https', 'http') used when endpoint is not supplied
     * - endpoint: Allows you to specify a custom endpoint instead of building one from the region and scheme
     *
     * Generic client options:
     *
     * - signature: Overrides the signature used by the client. Clients will always choose an appropriate default signature. However, it can be useful to override this with a custom setting. This can be set to "v4", "v3https", "v2" or an instance of Aws\Common\Signature\SignatureInterface.
     * - ssl.certificate_authority: Set to true to use the bundled CA cert or pass the full path to an SSL certificate bundle
     * - curl.options: Associative of CURLOPT_* cURL options to add to each request
     * - client.backoff.logger: `Guzzle\Log\LogAdapterInterface` object used to log backoff retries. Use 'debug' to emit PHP warnings when a retry is issued.
     * - client.backoff.logger.template: Optional template to use for exponential backoff log messages. See `Guzzle\Plugin\Backoff\BackoffLogger` for formatting information.
     *
     * @param array|Collection $config Client configuration data
     *
     * @return self
     */
    public static function factory($config = array())
    {
        return ClientBuilder::factory()
            ->setConfig($config)
            ->setConfigDefaults(array(Options::SCHEME => 'https'))
            ->build();
    }
}
includes/lib/aws-sdk-php-2.8.31/src/Aws/Common/Client/ExpiredCredentialsChecker.php000064400000005224151327705670023570 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\Common\Client;

use Aws\Common\Credentials\AbstractRefreshableCredentials;
use Aws\Common\Client\AwsClientInterface;
use Aws\Common\Exception\Parser\ExceptionParserInterface;
use Guzzle\Http\Exception\HttpException;
use Guzzle\Http\Message\RequestInterface;
use Guzzle\Http\Message\Response;
use Guzzle\Plugin\Backoff\BackoffStrategyInterface;
use Guzzle\Plugin\Backoff\AbstractBackoffStrategy;

/**
 * Backoff logic that handles retrying requests when credentials expire
 */
class ExpiredCredentialsChecker extends AbstractBackoffStrategy
{
    /**
     * @var array Array of known retrying exception codes
     */
    protected $retryable = array(
        'RequestExpired' => true,
        'ExpiredTokenException' => true,
        'ExpiredToken' => true
    );

    /**
     * @var ExceptionParserInterface Exception parser used to parse exception responses
     */
    protected $exceptionParser;

    public function __construct(ExceptionParserInterface $exceptionParser, BackoffStrategyInterface $next = null) {
        $this->exceptionParser = $exceptionParser;
        $this->next = $next;
    }

    public function makesDecision()
    {
        return true;
    }

    protected function getDelay($retries, RequestInterface $request, Response $response = null, HttpException $e = null)
    {
        if ($response && $response->isClientError()) {

            $parts = $this->exceptionParser->parse($request, $response);
            if (!isset($this->retryable[$parts['code']]) || !$request->getClient()) {
                return null;
            }

            /** @var AwsClientInterface $client */
            $client = $request->getClient();
            // Only retry if the credentials can be refreshed
            if (!($client->getCredentials() instanceof AbstractRefreshableCredentials)) {
                return null;
            }

            // Resign the request using new credentials
            $client->getSignature()->signRequest($request, $client->getCredentials()->setExpiration(-1));

            // Retry immediately with no delay
            return 0;
        }
    }
}
includes/lib/aws-sdk-php-2.8.31/src/Aws/Common/Signature/SignatureListener.php000064400000005033151327705670022715 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\Common\Signature;

use Aws\Common\Credentials\AbstractRefreshableCredentials;
use Aws\Common\Credentials\CredentialsInterface;
use Aws\Common\Credentials\NullCredentials;
use Guzzle\Common\Event;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;

/**
 * Listener used to sign requests before they are sent over the wire
 */
class SignatureListener implements EventSubscriberInterface
{
    /**
     * @var CredentialsInterface
     */
    protected $credentials;

    /**
     * @var SignatureInterface
     */
    protected $signature;

    /**
     * Construct a new request signing plugin
     *
     * @param CredentialsInterface $credentials Credentials used to sign requests
     * @param SignatureInterface   $signature   Signature implementation
     */
    public function __construct(CredentialsInterface $credentials, SignatureInterface $signature)
    {
        $this->credentials = $credentials;
        $this->signature = $signature;
    }

    /**
     * {@inheritdoc}
     */
    public static function getSubscribedEvents()
    {
        return array(
            'request.before_send'        => array('onRequestBeforeSend', -255),
            'client.credentials_changed' => array('onCredentialsChanged')
        );
    }

    /**
     * Updates the listener with new credentials if the client is updated
     *
     * @param Event $event Event emitted
     */
    public function onCredentialsChanged(Event $event)
    {
        $this->credentials = $event['credentials'];
    }

    /**
     * Signs requests before they are sent
     *
     * @param Event $event Event emitted
     */
    public function onRequestBeforeSend(Event $event)
    {
        $creds = $this->credentials instanceof AbstractRefreshableCredentials
            ? $this->credentials->getCredentials()
            : $this->credentials;

        if(!$creds instanceof NullCredentials) {
            $this->signature->signRequest($event['request'], $creds);
        }
    }
}
includes/lib/aws-sdk-php-2.8.31/src/Aws/Common/Signature/SignatureV4.php000064400000037546151327705670021437 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\Common\Signature;

use Aws\Common\Credentials\CredentialsInterface;
use Aws\Common\Enum\DateFormat;
use Aws\Common\HostNameUtils;
use Guzzle\Http\Message\EntityEnclosingRequestInterface;
use Guzzle\Http\Message\RequestFactory;
use Guzzle\Http\Message\RequestInterface;
use Guzzle\Http\QueryString;
use Guzzle\Http\Url;
use Guzzle\Stream\Stream;

/**
 * Signature Version 4
 * @link http://docs.aws.amazon.com/general/latest/gr/signature-version-4.html
 */
class SignatureV4 extends AbstractSignature implements EndpointSignatureInterface
{
    /** @var string Cache of the default empty entity-body payload */
    const DEFAULT_PAYLOAD = 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855';

    /** @var string Explicitly set service name */
    protected $serviceName;

    /** @var string Explicitly set region name */
    protected $regionName;

    /** @var int Maximum number of hashes to cache */
    protected $maxCacheSize = 50;

    /** @var array Cache of previously signed values */
    protected $hashCache = array();

    /** @var int Size of the hash cache */
    protected $cacheSize = 0;

    /**
     * @param string $serviceName Bind the signing to a particular service name
     * @param string $regionName  Bind the signing to a particular region name
     */
    public function __construct($serviceName = null, $regionName = null)
    {
        $this->serviceName = $serviceName;
        $this->regionName = $regionName;
    }

    /**
     * Set the service name instead of inferring it from a request URL
     *
     * @param string $service Name of the service used when signing
     *
     * @return self
     */
    public function setServiceName($service)
    {
        $this->serviceName = $service;

        return $this;
    }

    /**
     * Set the region name instead of inferring it from a request URL
     *
     * @param string $region Name of the region used when signing
     *
     * @return self
     */
    public function setRegionName($region)
    {
        $this->regionName = $region;

        return $this;
    }

    /**
     * Set the maximum number of computed hashes to cache
     *
     * @param int $maxCacheSize Maximum number of hashes to cache
     *
     * @return self
     */
    public function setMaxCacheSize($maxCacheSize)
    {
        $this->maxCacheSize = $maxCacheSize;

        return $this;
    }

    public function signRequest(RequestInterface $request, CredentialsInterface $credentials)
    {
        $timestamp = $this->getTimestamp();
        $longDate = gmdate(DateFormat::ISO8601, $timestamp);
        $shortDate = substr($longDate, 0, 8);

        // Remove any previously set Authorization headers so that retries work
        $request->removeHeader('Authorization');

        // Requires a x-amz-date header or Date
        if ($request->hasHeader('x-amz-date') || !$request->hasHeader('Date')) {
            $request->setHeader('x-amz-date', $longDate);
        } else {
            $request->setHeader('Date', gmdate(DateFormat::RFC1123, $timestamp));
        }

        // Add the security token if one is present
        if ($credentials->getSecurityToken()) {
            $request->setHeader('x-amz-security-token', $credentials->getSecurityToken());
        }

        // Parse the service and region or use one that is explicitly set
        $region = $this->regionName;
        $service = $this->serviceName;
        if (!$region || !$service) {
            $url = Url::factory($request->getUrl());
            $region = $region ?: HostNameUtils::parseRegionName($url);
            $service = $service ?: HostNameUtils::parseServiceName($url);
        }

        $credentialScope = $this->createScope($shortDate, $region, $service);
        $payload = $this->getPayload($request);
        $signingContext = $this->createSigningContext($request, $payload);
        $signingContext['string_to_sign'] = $this->createStringToSign(
            $longDate,
            $credentialScope,
            $signingContext['canonical_request']
        );

        // Calculate the signing key using a series of derived keys
        $signingKey = $this->getSigningKey($shortDate, $region, $service, $credentials->getSecretKey());
        $signature = hash_hmac('sha256', $signingContext['string_to_sign'], $signingKey);

        $request->setHeader('Authorization', "AWS4-HMAC-SHA256 "
            . "Credential={$credentials->getAccessKeyId()}/{$credentialScope}, "
            . "SignedHeaders={$signingContext['signed_headers']}, Signature={$signature}");

        // Add debug information to the request
        $request->getParams()->set('aws.signature', $signingContext);
    }

    public function createPresignedUrl(
        RequestInterface $request,
        CredentialsInterface $credentials,
        $expires
    ) {
        $request = $this->createPresignedRequest($request, $credentials);
        $query = $request->getQuery();
        $httpDate = gmdate(DateFormat::ISO8601, $this->getTimestamp());
        $shortDate = substr($httpDate, 0, 8);
        $scope = $this->createScope(
            $shortDate,
            $this->regionName,
            $this->serviceName
        );
        $this->addQueryValues($scope, $request, $credentials, $expires);
        $payload = $this->getPresignedPayload($request);
        $context = $this->createSigningContext($request, $payload);
        $stringToSign = $this->createStringToSign(
            $httpDate,
            $scope,
            $context['canonical_request']
        );
        $key = $this->getSigningKey(
            $shortDate,
            $this->regionName,
            $this->serviceName,
            $credentials->getSecretKey()
        );
        $query['X-Amz-Signature'] = hash_hmac('sha256', $stringToSign, $key);

        return $request->getUrl();
    }

    /**
     * Converts a POST request to a GET request by moving POST fields into the
     * query string.
     *
     * Useful for pre-signing query protocol requests.
     *
     * @param EntityEnclosingRequestInterface $request Request to clone
     *
     * @return RequestInterface
     * @throws \InvalidArgumentException if the method is not POST
     */
    public static function convertPostToGet(EntityEnclosingRequestInterface $request)
    {
        if ($request->getMethod() !== 'POST') {
            throw new \InvalidArgumentException('Expected a POST request but '
                . 'received a ' . esc_html($request->getMethod()) . ' request.');
        }

        $cloned = RequestFactory::getInstance()
            ->cloneRequestWithMethod($request, 'GET');

        // Move POST fields to the query if they are present
        foreach ($request->getPostFields() as $name => $value) {
            $cloned->getQuery()->set($name, $value);
        }

        return $cloned;
    }

    /**
     * Get the payload part of a signature from a request.
     *
     * @param RequestInterface $request
     *
     * @return string
     */
    protected function getPayload(RequestInterface $request)
    {
        // Calculate the request signature payload
        if ($request->hasHeader('x-amz-content-sha256')) {
            // Handle streaming operations (e.g. Glacier.UploadArchive)
            return (string) $request->getHeader('x-amz-content-sha256');
        }

        if ($request instanceof EntityEnclosingRequestInterface) {
            if ($request->getMethod() == 'POST' && count($request->getPostFields())) {
                return hash('sha256', (string) $request->getPostFields());
            } elseif ($body = $request->getBody()) {
                return Stream::getHash($request->getBody(), 'sha256');
            }
        }

        return self::DEFAULT_PAYLOAD;
    }

    /**
     * Get the payload of a request for use with pre-signed URLs.
     *
     * @param RequestInterface $request
     *
     * @return string
     */
    protected function getPresignedPayload(RequestInterface $request)
    {
        return $this->getPayload($request);
    }

    protected function createCanonicalizedPath(RequestInterface $request)
    {
        $doubleEncoded = rawurlencode(ltrim($request->getPath(), '/'));

        return '/' . str_replace('%2F', '/', $doubleEncoded);
    }

    private function createStringToSign($longDate, $credentialScope, $creq)
    {
        return "AWS4-HMAC-SHA256\n{$longDate}\n{$credentialScope}\n"
            . hash('sha256', $creq);
    }

    private function createPresignedRequest(
        RequestInterface $request,
        CredentialsInterface $credentials
    ) {
        // POST requests can be sent as GET requests instead by moving the
        // POST fields into the query string.
        if ($request instanceof EntityEnclosingRequestInterface
            && $request->getMethod() === 'POST'
            && strpos($request->getHeader('Content-Type'), 'application/x-www-form-urlencoded') === 0
        ) {
            $sr = RequestFactory::getInstance()
                ->cloneRequestWithMethod($request, 'GET');
            // Move POST fields to the query if they are present
            foreach ($request->getPostFields() as $name => $value) {
                $sr->getQuery()->set($name, $value);
            }
        } else {
            $sr = clone $request;
        }

        // Make sure to handle temporary credentials
        if ($token = $credentials->getSecurityToken()) {
            $sr->setHeader('X-Amz-Security-Token', $token);
            $sr->getQuery()->set('X-Amz-Security-Token', $token);
        }

        $this->moveHeadersToQuery($sr);

        return $sr;
    }

    /**
     * Create the canonical representation of a request
     *
     * @param RequestInterface $request Request to canonicalize
     * @param string           $payload Request payload (typically the value
     *                                  of the x-amz-content-sha256 header.
     *
     * @return array Returns an array of context information including:
     *               - canonical_request
     *               - signed_headers
     */
    private function createSigningContext(RequestInterface $request, $payload)
    {
        $signable = array(
            'host'        => true,
            'date'        => true,
            'content-md5' => true
        );

        // Normalize the path as required by SigV4 and ensure it's absolute
        $canon = $request->getMethod() . "\n"
            . $this->createCanonicalizedPath($request) . "\n"
            . $this->getCanonicalizedQueryString($request) . "\n";

        $canonHeaders = array();

        foreach ($request->getHeaders()->getAll() as $key => $values) {
            $key = strtolower($key);
            if (isset($signable[$key]) || substr($key, 0, 6) === 'x-amz-') {
                $values = $values->toArray();
                if (count($values) == 1) {
                    $values = $values[0];
                } else {
                    sort($values);
                    $values = implode(',', $values);
                }
                $canonHeaders[$key] = $key . ':' . preg_replace('/\s+/', ' ', $values);
            }
        }

        ksort($canonHeaders);
        $signedHeadersString = implode(';', array_keys($canonHeaders));
        $canon .= implode("\n", $canonHeaders) . "\n\n"
            . $signedHeadersString . "\n"
            . $payload;

        return array(
            'canonical_request' => $canon,
            'signed_headers'    => $signedHeadersString
        );
    }

    /**
     * Get a hash for a specific key and value.  If the hash was previously
     * cached, return it
     *
     * @param string $shortDate Short date
     * @param string $region    Region name
     * @param string $service   Service name
     * @param string $secretKey Secret Access Key
     *
     * @return string
     */
    private function getSigningKey($shortDate, $region, $service, $secretKey)
    {
        $cacheKey = $shortDate . '_' . $region . '_' . $service . '_' . $secretKey;

        // Retrieve the hash form the cache or create it and add it to the cache
        if (!isset($this->hashCache[$cacheKey])) {
            // When the cache size reaches the max, then just clear the cache
            if (++$this->cacheSize > $this->maxCacheSize) {
                $this->hashCache = array();
                $this->cacheSize = 0;
            }
            $dateKey = hash_hmac('sha256', $shortDate, 'AWS4' . $secretKey, true);
            $regionKey = hash_hmac('sha256', $region, $dateKey, true);
            $serviceKey = hash_hmac('sha256', $service, $regionKey, true);
            $this->hashCache[$cacheKey] = hash_hmac('sha256', 'aws4_request', $serviceKey, true);
        }

        return $this->hashCache[$cacheKey];
    }

    /**
     * Get the canonicalized query string for a request
     *
     * @param  RequestInterface $request
     * @return string
     */
    private function getCanonicalizedQueryString(RequestInterface $request)
    {
        $queryParams = $request->getQuery()->getAll();
        unset($queryParams['X-Amz-Signature']);
        if (empty($queryParams)) {
            return '';
        }

        $qs = '';
        ksort($queryParams);
        foreach ($queryParams as $key => $values) {
            if (is_array($values)) {
                sort($values);
            } elseif ($values === 0) {
                $values = array('0');
            } elseif (!$values) {
                $values = array('');
            }

            foreach ((array) $values as $value) {
                if ($value === QueryString::BLANK) {
                    $value = '';
                }
                $qs .= rawurlencode($key) . '=' . rawurlencode($value) . '&';
            }
        }

        return substr($qs, 0, -1);
    }

    private function convertExpires($expires)
    {
        if ($expires instanceof \DateTime) {
            $expires = $expires->getTimestamp();
        } elseif (!is_numeric($expires)) {
            $expires = strtotime($expires);
        }

        $duration = $expires - time();

        // Ensure that the duration of the signature is not longer than a week
        if ($duration > 604800) {
            throw new \InvalidArgumentException('The expiration date of a '
                . 'signature version 4 presigned URL must be less than one '
                . 'week');
        }

        return $duration;
    }

    private function createScope($shortDate, $region, $service)
    {
        return $shortDate
            . '/' . $region
            . '/' . $service
            . '/aws4_request';
    }

    private function addQueryValues(
        $scope,
        RequestInterface $request,
        CredentialsInterface $credentials,
        $expires
    ) {
        $credential = $credentials->getAccessKeyId() . '/' . $scope;

        // Set query params required for pre-signed URLs
        $request->getQuery()
            ->set('X-Amz-Algorithm', 'AWS4-HMAC-SHA256')
            ->set('X-Amz-Credential', $credential)
            ->set('X-Amz-Date', gmdate('Ymd\THis\Z', $this->getTimestamp()))
            ->set('X-Amz-SignedHeaders', 'Host')
            ->set('X-Amz-Expires', $this->convertExpires($expires));
    }

    private function moveHeadersToQuery(RequestInterface $request)
    {
        $query = $request->getQuery();

        foreach ($request->getHeaders() as $name => $header) {
            if (substr($name, 0, 5) == 'x-amz') {
                $query[$header->getName()] = (string) $header;
            }
            if ($name !== 'host') {
                $request->removeHeader($name);
            }
        }
    }
}
includes/lib/aws-sdk-php-2.8.31/src/Aws/Common/Signature/SignatureV3Https.php000064400000004101151327705670022436 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\Common\Signature;

use Aws\Common\Credentials\CredentialsInterface;
use Aws\Common\Enum\DateFormat;
use Guzzle\Http\Message\RequestInterface;

/**
 * Implementation of Signature Version 3 HTTPS
 * @link http://docs.aws.amazon.com/Route53/latest/DeveloperGuide/RESTAuthentication.html
 */
class SignatureV3Https extends AbstractSignature
{
    public function signRequest(RequestInterface $request, CredentialsInterface $credentials)
    {
        // Add a date header if one is not set
        if (!$request->hasHeader('date') && !$request->hasHeader('x-amz-date')) {
            $request->setHeader('Date', gmdate(DateFormat::RFC1123, $this->getTimestamp()));
        }

        // Add the security token if one is present
        if ($credentials->getSecurityToken()) {
            $request->setHeader('x-amz-security-token', $credentials->getSecurityToken());
        }

        // Determine the string to sign
        $stringToSign = (string) ($request->getHeader('Date') ?: $request->getHeader('x-amz-date'));
        $request->getParams()->set('aws.string_to_sign', $stringToSign);

        // Calculate the signature
        $signature = base64_encode(hash_hmac('sha256', $stringToSign, $credentials->getSecretKey(), true));

        // Add the authorization header to the request
        $headerFormat = 'AWS3-HTTPS AWSAccessKeyId=%s,Algorithm=HmacSHA256,Signature=%s';
        $request->setHeader('X-Amzn-Authorization', sprintf($headerFormat, $credentials->getAccessKeyId(), $signature));
    }
}
includes/lib/aws-sdk-php-2.8.31/src/Aws/Common/Signature/AbstractSignature.php000064400000002352151327705670022674 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\Common\Signature;

use Aws\Common\Credentials\CredentialsInterface;
use Guzzle\Http\Message\RequestInterface;

abstract class AbstractSignature implements SignatureInterface
{
    /**
     * Provides the timestamp used for the class (used for mocking PHP's time() function)
     *
     * @return int
     */
    protected function getTimestamp()
    {
        return time();
    }

    /**
     * @codeCoverageIgnore
     */
    public function createPresignedUrl(
        RequestInterface $request,
        CredentialsInterface $credentials,
        $expires
    ) {
        throw new \BadMethodCallException(__METHOD__ . ' not implemented');
    }
}
includes/lib/aws-sdk-php-2.8.31/src/Aws/Common/Signature/EndpointSignatureInterface.php000064400000002360151327705670024531 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\Common\Signature;

/**
 * Interface for signatures that use specific region and service names when
 * signing requests.
 */
interface EndpointSignatureInterface extends SignatureInterface
{
    /**
     * Set the service name instead of inferring it from a request URL
     *
     * @param string $service Name of the service used when signing
     *
     * @return self
     */
    public function setServiceName($service);

    /**
     * Set the region name instead of inferring it from a request URL
     *
     * @param string $region Name of the region used when signing
     *
     * @return self
     */
    public function setRegionName($region);
}
includes/lib/aws-sdk-php-2.8.31/src/Aws/Common/Signature/SignatureInterface.php000064400000003552151327705670023034 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\Common\Signature;

use Aws\Common\Credentials\CredentialsInterface;
use Guzzle\Http\Message\RequestInterface;

/**
 * Interface used to provide interchangeable strategies for signing requests
 * using the various AWS signature protocols.
 */
interface SignatureInterface
{
    /**
     * Signs the specified request with an AWS signing protocol by using the
     * provided AWS account credentials and adding the required headers to the
     * request.
     *
     * @param RequestInterface     $request     Request to add a signature to
     * @param CredentialsInterface $credentials Signing credentials
     */
    public function signRequest(RequestInterface $request, CredentialsInterface $credentials);

    /**
     * Create a pre-signed URL
     *
     * @param RequestInterface     $request Request to sign
     * @param CredentialsInterface $credentials Credentials used to sign
     * @param int|string|\DateTime $expires The time at which the URL should expire. This can be a Unix timestamp, a
     *                                      PHP DateTime object, or a string that can be evaluated by strtotime
     * @return string
     */
    public function createPresignedUrl(
        RequestInterface $request,
        CredentialsInterface $credentials,
        $expires
    );
}
includes/lib/aws-sdk-php-2.8.31/src/Aws/Common/Signature/SignatureV2.php000064400000007065151327705670021426 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\Common\Signature;

use Aws\Common\Credentials\CredentialsInterface;
use Guzzle\Http\Message\RequestInterface;

/**
 * Implementation of Signature Version 2
 * @link http://aws.amazon.com/articles/1928
 */
class SignatureV2 extends AbstractSignature
{
    public function signRequest(RequestInterface $request, CredentialsInterface $credentials)
    {
        // refresh the cached timestamp
        $timestamp = $this->getTimestamp(true);

        // set values we need in CanonicalizedParameterString
        $this->addParameter($request, 'Timestamp', gmdate('c', $timestamp));
        $this->addParameter($request, 'SignatureVersion', '2');
        $this->addParameter($request, 'SignatureMethod', 'HmacSHA256');
        $this->addParameter($request, 'AWSAccessKeyId', $credentials->getAccessKeyId());

        if ($token = $credentials->getSecurityToken()) {
            $this->addParameter($request, 'SecurityToken', $token);
        }

        // Get the path and ensure it's absolute
        $path = '/' . ltrim($request->getUrl(true)->normalizePath()->getPath(), '/');

        // build string to sign
        $sign = $request->getMethod() . "\n"
            . $request->getHost() . "\n"
            . $path . "\n"
            . $this->getCanonicalizedParameterString($request);

        // Add the string to sign to the request for debugging purposes
        $request->getParams()->set('aws.string_to_sign', $sign);

        $signature = base64_encode(
            hash_hmac(
                'sha256',
                $sign,
                $credentials->getSecretKey(),
                true
            )
        );

        $this->addParameter($request, 'Signature', $signature);
    }

    /**
     * Add a parameter key and value to the request according to type
     *
     * @param RequestInterface $request The request
     * @param string           $key     The name of the parameter
     * @param string           $value   The value of the parameter
     */
    public function addParameter(RequestInterface $request, $key, $value)
    {
        if ($request->getMethod() == 'POST') {
            $request->setPostField($key, $value);
        } else {
            $request->getQuery()->set($key, $value);
        }
    }

    /**
     * Get the canonicalized query/parameter string for a request
     *
     * @param RequestInterface $request Request used to build canonicalized string
     *
     * @return string
     */
    private function getCanonicalizedParameterString(RequestInterface $request)
    {
        if ($request->getMethod() == 'POST') {
            $params = $request->getPostFields()->toArray();
        } else {
            $params = $request->getQuery()->toArray();
        }

        // Don't resign a previous signature value
        unset($params['Signature']);
        uksort($params, 'strcmp');

        $str = '';
        foreach ($params as $key => $val) {
            $str .= rawurlencode($key) . '=' . rawurlencode($val) . '&';
        }

        return substr($str, 0, -1);
    }
}
includes/lib/aws-sdk-php-2.8.31/src/Aws/Common/Aws.php000064400000007241151327705670016042 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\Common;

use Aws\Common\Facade\Facade;
use Guzzle\Service\Builder\ServiceBuilder;
use Guzzle\Service\Builder\ServiceBuilderLoader;

/**
 * Base class for interacting with web service clients
 */
class Aws extends ServiceBuilder
{
    /**
     * @var string Current version of the SDK
     */
    const VERSION = '2.8.31';

    /**
     * Create a new service locator for the AWS SDK
     *
     * You can configure the service locator is four different ways:
     *
     * 1. Use the default configuration file shipped with the SDK that wires class names with service short names and
     *    specify global parameters to add to every definition (e.g. key, secret, credentials, etc)
     *
     * 2. Use a custom configuration file that extends the default config and supplies credentials for each service.
     *
     * 3. Use a custom config file that wires services to custom short names for services.
     *
     * 4. If you are on Amazon EC2, you can use the default configuration file and not provide any credentials so that
     *    you are using InstanceProfile credentials.
     *
     * @param array|string $config           The full path to a .php or .js|.json file, or an associative array of data
     *                                       to use as global parameters to pass to each service.
     * @param array        $globalParameters Global parameters to pass to every service as it is instantiated.
     *
     * @return Aws
     */
    public static function factory($config = null, array $globalParameters = array())
    {
        if (!$config) {
            // If nothing is passed in, then use the default configuration file with credentials from the environment
            $config = self::getDefaultServiceDefinition();
        } elseif (is_array($config)) {
            // If an array was passed, then use the default configuration file with parameter overrides
            $globalParameters = $config;
            $config = self::getDefaultServiceDefinition();
        }

        $loader = new ServiceBuilderLoader();
        $loader->addAlias('_aws', self::getDefaultServiceDefinition())
            ->addAlias('_sdk1', __DIR__  . '/Resources/sdk1-config.php');

        return $loader->load($config, $globalParameters);
    }

    /**
     * Get the full path to the default service builder definition file
     *
     * @return string
     */
    public static function getDefaultServiceDefinition()
    {
        return __DIR__  . '/Resources/aws-config.php';
    }

    /**
     * Returns the configuration for the service builder
     *
     * @return array
     */
    public function getConfig()
    {
        return $this->builderConfig;
    }

    /**
     * Enables the facades for the clients defined in the service builder
     *
     * @param string|null $namespace The namespace that the facades should be mounted to. Defaults to global namespace
     *
     * @return Aws
     * @deprecated "Facades" are being removed in version 3.0 of the SDK.
     */
    public function enableFacades($namespace = null)
    {
        Facade::mountFacades($this, $namespace);

        return $this;
    }
}
includes/lib/aws-sdk-php-2.8.31/src/Aws/Common/Model/MultipartUpload/AbstractUploadPart.php000064400000004753151327705670025242 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\Common\Model\MultipartUpload;

use Aws\Common\Exception\InvalidArgumentException;

/**
 * An object that encapsulates the data for an upload part
 */
abstract class AbstractUploadPart implements UploadPartInterface
{
    /**
     * @var array A map of external array keys to internal property names
     */
    protected static $keyMap = array();

    /**
     * @var int The number of the upload part representing its order in the overall upload
     */
    protected $partNumber;

    /**
     * {@inheritdoc}
     */
    public static function fromArray($data)
    {
        $part = new static();
        $part->loadData($data);

        return $part;
    }

    /**
     * {@inheritdoc}
     */
    public function getPartNumber()
    {
        return $this->partNumber;
    }

    /**
     * {@inheritdoc}
     */
    public function toArray()
    {
        $array = array();
        foreach (static::$keyMap as $key => $property) {
            $array[$key] = $this->{$property};
        }

        return $array;
    }

    /**
     * {@inheritdoc}
     */
    public function serialize()
    {
        return serialize($this->toArray());
    }

    /**
     * {@inheritdoc}
     */
    public function unserialize($serialized)
    {
        $this->loadData(unserialize($serialized));
    }

    /**
     * Loads an array of data into the upload part by extracting only the needed keys
     *
     * @param array|\Traversable $data Data to load into the upload part value object
     *
     * @throws InvalidArgumentException if a required key is missing
     */
    protected function loadData($data)
    {
        foreach (static::$keyMap as $key => $property) {
            if (isset($data[$key])) {
                $this->{$property} = $data[$key];
            } else {
                throw new InvalidArgumentException(esc_html("A required key [$key] was missing from the upload part."));
            }
        }
    }
}
includes/lib/aws-sdk-php-2.8.31/src/Aws/Common/Model/MultipartUpload/AbstractUploadBuilder.php000064400000010337151327705670025715 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\Common\Model\MultipartUpload;

use Aws\Common\Client\AwsClientInterface;
use Aws\Common\Exception\InvalidArgumentException;
use Guzzle\Http\EntityBody;

/**
 * Easily create a multipart uploader used to quickly and reliably upload a
 * large file or data stream to Amazon S3 using multipart uploads
 */
abstract class AbstractUploadBuilder
{
    /**
     * @var AwsClientInterface Client used to transfer requests
     */
    protected $client;

    /**
     * @var TransferStateInterface State of the transfer
     */
    protected $state;

    /**
     * @var EntityBody Source of the data
     */
    protected $source;

    /**
     * @var array Array of headers to set on the object
     */
    protected $headers = array();

    /**
     * Return a new instance of the UploadBuilder
     *
     * @return static
     */
    public static function newInstance()
    {
        return new static;
    }

    /**
     * Set the client used to connect to the AWS service
     *
     * @param AwsClientInterface $client Client to use
     *
     * @return $this
     */
    public function setClient(AwsClientInterface $client)
    {
        $this->client = $client;

        return $this;
    }

    /**
     * Set the state of the upload. This is useful for resuming from a previously started multipart upload.
     * You must use a local file stream as the data source if you wish to resume from a previous upload.
     *
     * @param TransferStateInterface|string $state Pass a TransferStateInterface object or the ID of the initiated
     *                                             multipart upload. When an ID is passed, the builder will create a
     *                                             state object using the data from a ListParts API response.
     *
     * @return $this
     */
    public function resumeFrom($state)
    {
        $this->state = $state;

        return $this;
    }

    /**
     * Set the data source of the transfer
     *
     * @param resource|string|EntityBody $source Source of the transfer. Pass a string to transfer from a file on disk.
     *                                           You can also stream from a resource returned from fopen or a Guzzle
     *                                           {@see EntityBody} object.
     *
     * @return $this
     * @throws InvalidArgumentException when the source cannot be found or opened
     */
    public function setSource($source)
    {
        // Use the contents of a file as the data source
        if (is_string($source)) {
            if (!file_exists($source)) {
                throw new InvalidArgumentException(esc_html("File does not exist: {$source}"));
            }
            // Clear the cache so that we send accurate file sizes
            clearstatcache(true, $source);
            $source = fopen($source, 'r');
        }

        $this->source = EntityBody::factory($source);

        if ($this->source->isSeekable() && $this->source->getSize() == 0) {
            throw new InvalidArgumentException('Empty body provided to upload builder');
        }

        return $this;
    }

    /**
     * Specify the headers to set on the upload
     *
     * @param array $headers Headers to add to the uploaded object
     *
     * @return $this
     */
    public function setHeaders(array $headers)
    {
        $this->headers = $headers;

        return $this;
    }

    /**
     * Build the appropriate uploader based on the builder options
     *
     * @return TransferInterface
     */
    abstract public function build();

    /**
     * Initiate the multipart upload
     *
     * @return TransferStateInterface
     */
    abstract protected function initiateMultipartUpload();
}
includes/lib/aws-sdk-php-2.8.31/src/Aws/Common/Model/MultipartUpload/AbstractUploadId.php000064400000004370151327705670024663 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\Common\Model\MultipartUpload;

use Aws\Common\Exception\InvalidArgumentException;

/**
 * An object that encapsulates the data identifying an upload
 */
abstract class AbstractUploadId implements UploadIdInterface
{
    /**
     * @var array Expected values (with defaults)
     */
    protected static $expectedValues = array();

    /**
     * @var array Params representing the identifying information
     */
    protected $data = array();

    /**
     * {@inheritdoc}
     */
    public static function fromParams($data)
    {
        $uploadId = new static();
        $uploadId->loadData($data);

        return $uploadId;
    }

    /**
     * {@inheritdoc}
     */
    public function toParams()
    {
        return $this->data;
    }

    /**
     * {@inheritdoc}
     */
    public function serialize()
    {
        return serialize($this->data);
    }

    /**
     * {@inheritdoc}
     */
    public function unserialize($serialized)
    {
        $this->loadData(unserialize($serialized));
    }

    /**
     * Loads an array of data into the UploadId by extracting only the needed keys
     *
     * @param array $data Data to load
     *
     * @throws InvalidArgumentException if a required key is missing
     */
    protected function loadData($data)
    {
        $data = array_replace(static::$expectedValues, array_intersect_key($data, static::$expectedValues));
        foreach ($data as $key => $value) {
            if (isset($data[$key])) {
                $this->data[$key] = $data[$key];
            } else {
                throw new InvalidArgumentException(esc_html("A required key [$key] was missing from the UploadId."));
            }
        }
    }
}
includes/lib/aws-sdk-php-2.8.31/src/Aws/Common/Model/MultipartUpload/TransferInterface.php000064400000003517151327705670025105 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\Common\Model\MultipartUpload;

use Guzzle\Common\HasDispatcherInterface;
use Guzzle\Service\Resource\Model;

/**
 * Interface for transferring the contents of a data source to an AWS service via a multipart upload interface
 */
interface TransferInterface extends HasDispatcherInterface
{
    /**
     * Upload the source to using a multipart upload
     *
     * @return Model|null Result of the complete multipart upload command or null if uploading was stopped
     */
    public function upload();

    /**
     * Abort the upload
     *
     * @return Model Returns the result of the abort multipart upload command
     */
    public function abort();

    /**
     * Get the current state of the upload
     *
     * @return TransferStateInterface
     */
    public function getState();

    /**
     * Stop the transfer and retrieve the current state.
     *
     * This allows you to stop and later resume a long running transfer if needed.
     *
     * @return TransferStateInterface
     */
    public function stop();

    /**
     * Set an option on the transfer object
     *
     * @param string $option Option to set
     * @param mixed  $value  The value to set
     *
     * @return self
     */
    public function setOption($option, $value);
}
includes/lib/aws-sdk-php-2.8.31/src/Aws/Common/Model/MultipartUpload/UploadIdInterface.php000064400000002151151327705670025013 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\Common\Model\MultipartUpload;

/**
 * An object that encapsulates the data identifying an upload
 */
interface UploadIdInterface extends \Serializable
{
    /**
     * Create an UploadId from an array
     *
     * @param array $data Data representing the upload identification
     *
     * @return self
     */
    public static function fromParams($data);

    /**
     * Returns the array form of the upload identification for use as command params
     *
     * @return array
     */
    public function toParams();
}
includes/lib/aws-sdk-php-2.8.31/src/Aws/Common/Model/MultipartUpload/UploadPartInterface.php000064400000002351151327705670025367 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\Common\Model\MultipartUpload;

/**
 * An object that encapsulates the data for an upload part
 */
interface UploadPartInterface extends \Serializable
{
    /**
     * Create an upload part from an array
     *
     * @param array|\Traversable $data Data representing the upload part
     *
     * @return self
     */
    public static function fromArray($data);

    /**
     * Returns the part number of the upload part which is used as an identifier
     *
     * @return int
     */
    public function getPartNumber();

    /**
     * Returns the array form of the upload part
     *
     * @return array
     */
    public function toArray();
}
includes/lib/aws-sdk-php-2.8.31/src/Aws/Common/Model/MultipartUpload/TransferStateInterface.php000064400000004607151327705670026107 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\Common\Model\MultipartUpload;

use Aws\Common\Client\AwsClientInterface;

/**
 * State of a multipart upload
 */
interface TransferStateInterface extends \Countable, \IteratorAggregate, \Serializable
{
    /**
     * Create the transfer state from the results of list parts request
     *
     * @param AwsClientInterface $client   Client used to send the request
     * @param UploadIdInterface  $uploadId Params needed to identify the upload and form the request
     *
     * @return self
     */
    public static function fromUploadId(AwsClientInterface $client, UploadIdInterface $uploadId);

    /**
     * Get the params used to identify an upload part
     *
     * @return UploadIdInterface
     */
    public function getUploadId();

    /**
     * Get the part information of a specific part
     *
     * @param int $partNumber Part to retrieve
     *
     * @return UploadPartInterface
     */
    public function getPart($partNumber);

    /**
     * Add a part to the transfer state
     *
     * @param UploadPartInterface $part The part to add
     *
     * @return self
     */
    public function addPart(UploadPartInterface $part);

    /**
     * Check if a specific part has been uploaded
     *
     * @param int $partNumber Part to check
     *
     * @return bool
     */
    public function hasPart($partNumber);

    /**
     * Get a list of all of the uploaded part numbers
     *
     * @return array
     */
    public function getPartNumbers();

    /**
     * Set whether or not the transfer has been aborted
     *
     * @param bool $aborted Set to true to mark the transfer as aborted
     *
     * @return self
     */
    public function setAborted($aborted);

    /**
     * Check if the transfer has been marked as aborted
     *
     * @return bool
     */
    public function isAborted();
}
includes/lib/aws-sdk-php-2.8.31/src/Aws/Common/Model/MultipartUpload/AbstractTransfer.php000064400000015521151327705670024746 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\Common\Model\MultipartUpload;

use Aws\Common\Client\AwsClientInterface;
use Aws\Common\Exception\MultipartUploadException;
use Aws\Common\Exception\RuntimeException;
use Guzzle\Common\AbstractHasDispatcher;
use Guzzle\Http\EntityBody;
use Guzzle\Http\EntityBodyInterface;
use Guzzle\Service\Command\OperationCommand;
use Guzzle\Service\Resource\Model;

/**
 * Abstract class for transfer commonalities
 */
abstract class AbstractTransfer extends AbstractHasDispatcher implements TransferInterface
{
    const BEFORE_UPLOAD      = 'multipart_upload.before_upload';
    const AFTER_UPLOAD       = 'multipart_upload.after_upload';
    const BEFORE_PART_UPLOAD = 'multipart_upload.before_part_upload';
    const AFTER_PART_UPLOAD  = 'multipart_upload.after_part_upload';
    const AFTER_ABORT        = 'multipart_upload.after_abort';
    const AFTER_COMPLETE     = 'multipart_upload.after_complete';

    /**
     * @var AwsClientInterface Client used for the transfers
     */
    protected $client;

    /**
     * @var TransferStateInterface State of the transfer
     */
    protected $state;

    /**
     * @var EntityBody Data source of the transfer
     */
    protected $source;

    /**
     * @var array Associative array of options
     */
    protected $options;

    /**
     * @var int Size of each part to upload
     */
    protected $partSize;

    /**
     * @var bool Whether or not the transfer has been stopped
     */
    protected $stopped = false;

    /**
     * Construct a new transfer object
     *
     * @param AwsClientInterface     $client  Client used for the transfers
     * @param TransferStateInterface $state   State used to track transfer
     * @param EntityBody             $source  Data source of the transfer
     * @param array                  $options Array of options to apply
     */
    public function __construct(
        AwsClientInterface $client,
        TransferStateInterface $state,
        EntityBody $source,
        array $options = array()
    ) {
        $this->client  = $client;
        $this->state   = $state;
        $this->source  = $source;
        $this->options = $options;

        $this->init();

        $this->partSize = $this->calculatePartSize();
    }

    public function __invoke()
    {
        return $this->upload();
    }

    /**
     * {@inheritdoc}
     */
    public static function getAllEvents()
    {
        return array(
            self::BEFORE_PART_UPLOAD,
            self::AFTER_UPLOAD,
            self::BEFORE_PART_UPLOAD,
            self::AFTER_PART_UPLOAD,
            self::AFTER_ABORT,
            self::AFTER_COMPLETE
        );
    }

    /**
     * {@inheritdoc}
     */
    public function abort()
    {
        $command = $this->getAbortCommand();
        $result = $command->getResult();

        $this->state->setAborted(true);
        $this->stop();
        $this->dispatch(self::AFTER_ABORT, $this->getEventData($command));

        return $result;
    }

    /**
     * {@inheritdoc}
     */
    public function stop()
    {
        $this->stopped = true;

        return $this->state;
    }

    /**
     * {@inheritdoc}
     */
    public function getState()
    {
        return $this->state;
    }

    /**
     * Get the array of options associated with the transfer
     *
     * @return array
     */
    public function getOptions()
    {
        return $this->options;
    }

    /**
     * Set an option on the transfer
     *
     * @param string $option Name of the option
     * @param mixed  $value  Value to set
     *
     * @return self
     */
    public function setOption($option, $value)
    {
        $this->options[$option] = $value;

        return $this;
    }

    /**
     * Get the source body of the upload
     *
     * @return EntityBodyInterface
     */
    public function getSource()
    {
        return $this->source;
    }

    /**
     * {@inheritdoc}
     * @throws MultipartUploadException when an error is encountered. Use getLastException() to get more information.
     * @throws RuntimeException         when attempting to upload an aborted transfer
     */
    public function upload()
    {
        if ($this->state->isAborted()) {
            throw new RuntimeException('The transfer has been aborted and cannot be uploaded');
        }

        $this->stopped = false;
        $eventData = $this->getEventData();
        $this->dispatch(self::BEFORE_UPLOAD, $eventData);

        try {
            $this->transfer();
            $this->dispatch(self::AFTER_UPLOAD, $eventData);

            if ($this->stopped) {
                return null;
            } else {
                $result = $this->complete();
                $this->dispatch(self::AFTER_COMPLETE, $eventData);
            }
        } catch (\Exception $e) {
            throw new MultipartUploadException(esc_attr($this->state), esc_attr($e));
        }

        return $result;
    }

    /**
     * Get an array used for event notifications
     *
     * @param OperationCommand $command Command to include in event data
     *
     * @return array
     */
    protected function getEventData(OperationCommand $command = null)
    {
        $data = array(
            'transfer'  => $this,
            'source'    => $this->source,
            'options'   => $this->options,
            'client'    => $this->client,
            'part_size' => $this->partSize,
            'state'     => $this->state
        );

        if ($command) {
            $data['command'] = $command;
        }

        return $data;
    }

    /**
     * Hook to initialize the transfer
     */
    protected function init() {}

    /**
     * Determine the upload part size based on the size of the source data and
     * taking into account the acceptable minimum and maximum part sizes.
     *
     * @return int The part size
     */
    abstract protected function calculatePartSize();

    /**
     * Complete the multipart upload
     *
     * @return Model Returns the result of the complete multipart upload command
     */
    abstract protected function complete();

    /**
     * Hook to implement in subclasses to perform the actual transfer
     */
    abstract protected function transfer();

    /**
     * Fetches the abort command fom the concrete implementation
     *
     * @return OperationCommand
     */
    abstract protected function getAbortCommand();
}
includes/lib/aws-sdk-php-2.8.31/src/Aws/Common/Model/MultipartUpload/AbstractTransferState.php000064400000007077151327705670025756 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\Common\Model\MultipartUpload;

use Aws\Common\Exception\RuntimeException;

/**
 * State of a multipart upload
 */
abstract class AbstractTransferState implements TransferStateInterface
{
    /**
     * @var UploadIdInterface Object holding params used to identity the upload part
     */
    protected $uploadId;

    /**
     * @var array Array of parts where the part number is the index
     */
    protected $parts = array();

    /**
     * @var bool Whether or not the transfer was aborted
     */
    protected $aborted = false;

    /**
     * Construct a new transfer state object
     *
     * @param UploadIdInterface $uploadId Upload identifier object
     */
    public function __construct(UploadIdInterface $uploadId)
    {
        $this->uploadId = $uploadId;
    }

    /**
     * {@inheritdoc}
     */
    public function getUploadId()
    {
        return $this->uploadId;
    }

    /**
     * Get a data value from the transfer state's uploadId
     *
     * @param string $key Key to retrieve (e.g. Bucket, Key, UploadId, etc)
     *
     * @return string|null
     */
    public function getFromId($key)
    {
        $params = $this->uploadId->toParams();

        return isset($params[$key]) ? $params[$key] : null;
    }

    /**
     * {@inheritdoc}
     */
    public function getPart($partNumber)
    {
        return isset($this->parts[$partNumber]) ? $this->parts[$partNumber] : null;
    }

    /**
     * {@inheritdoc}
     */
    public function addPart(UploadPartInterface $part)
    {
        $partNumber = $part->getPartNumber();
        $this->parts[$partNumber] = $part;

        return $this;
    }

    /**
     * {@inheritdoc}
     */
    public function hasPart($partNumber)
    {
        return isset($this->parts[$partNumber]);
    }

    /**
     * {@inheritdoc}
     */
    public function getPartNumbers()
    {
        return array_keys($this->parts);
    }

    /**
     * {@inheritdoc}
     */
    public function setAborted($aborted)
    {
        $this->aborted = (bool) $aborted;

        return $this;
    }

    /**
     * {@inheritdoc}
     */
    public function isAborted()
    {
        return $this->aborted;
    }

    /**
     * {@inheritdoc}
     */
    public function count()
    {
        return count($this->parts);
    }

    /**
     * {@inheritdoc}
     */
    public function getIterator()
    {
        return new \ArrayIterator($this->parts);
    }

    /**
     * {@inheritdoc}
     */
    public function serialize()
    {
        return serialize(get_object_vars($this));
    }

    /**
     * {@inheritdoc}
     */
    public function unserialize($serialized)
    {
        $data = unserialize($serialized);
        foreach (get_object_vars($this) as $property => $oldValue) {
            if (array_key_exists($property, $data)) {
                $this->{$property} = $data[$property];
            } else {
                throw new RuntimeException(esc_html("The {$property} property could be restored during unserialization."));
            }
        }
    }
}
includes/lib/aws-sdk-php-2.8.31/src/Aws/Common/Resources/sdk1-config.php000064400000007050151327705670021365 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

return array(
    'includes' => array('_aws'),
    'services' => array(

        'sdk1_settings' => array(
            'extends' => 'default_settings',
            'params'  => array(
                'certificate_authority' => false
            )
        ),

        'v1.autoscaling' => array(
            'extends' => 'sdk1_settings',
            'class'   => 'AmazonAS'
        ),

        'v1.cloudformation' => array(
            'extends' => 'sdk1_settings',
            'class'   => 'AmazonCloudFormation'
        ),

        'v1.cloudfront' => array(
            'extends' => 'sdk1_settings',
            'class'   => 'AmazonCloudFront'
        ),

        'v1.cloudsearch' => array(
            'extends' => 'sdk1_settings',
            'class'   => 'AmazonCloudSearch'
        ),

        'v1.cloudwatch' => array(
            'extends' => 'sdk1_settings',
            'class'   => 'AmazonCloudWatch'
        ),

        'v1.dynamodb' => array(
            'extends' => 'sdk1_settings',
            'class'   => 'AmazonDynamoDB'
        ),

        'v1.ec2' => array(
            'extends' => 'sdk1_settings',
            'class'   => 'AmazonEC2'
        ),

        'v1.elasticache' => array(
            'extends' => 'sdk1_settings',
            'class'   => 'AmazonElastiCache'
        ),

        'v1.elasticbeanstalk' => array(
            'extends' => 'sdk1_settings',
            'class'   => 'AmazonElasticBeanstalk'
        ),

        'v1.elb' => array(
            'extends' => 'sdk1_settings',
            'class'   => 'AmazonELB'
        ),

        'v1.emr' => array(
            'extends' => 'sdk1_settings',
            'class'   => 'AmazonEMR'
        ),

        'v1.iam' => array(
            'extends' => 'sdk1_settings',
            'class'   => 'AmazonIAM'
        ),

        'v1.importexport'     => array(
            'extends' => 'sdk1_settings',
            'class'   => 'AmazonImportExport'
        ),

        'v1.rds' => array(
            'extends' => 'sdk1_settings',
            'class'   => 'AmazonRDS'
        ),

        'v1.s3'  => array(
            'extends' => 'sdk1_settings',
            'class'   => 'AmazonS3'
        ),

        'v1.sdb' => array(
            'extends' => 'sdk1_settings',
            'class'   => 'AmazonSDB'
        ),

        'v1.ses' => array(
            'extends' => 'sdk1_settings',
            'class'   => 'AmazonSES'
        ),

        'v1.sns' => array(
            'extends' => 'sdk1_settings',
            'class'   => 'AmazonSNS'
        ),

        'v1.sqs' => array(
            'extends' => 'sdk1_settings',
            'class'   => 'AmazonSQS'
        ),

        'v1.storagegateway'   => array(
            'extends' => 'sdk1_settings',
            'class'   => 'AmazonStorageGateway'
        ),

        'v1.sts' => array(
            'extends' => 'sdk1_settings',
            'class'   => 'AmazonSTS'
        ),

        'v1.swf' => array(
            'extends' => 'sdk1_settings',
            'class'   => 'AmazonSWF'
        )
    )
);
includes/lib/aws-sdk-php-2.8.31/src/Aws/Common/Resources/public-endpoints.php000064400000004612151327705670022540 0ustar00<?php
return array(
    'version' => 2,
    'endpoints' => array(
        '*/*' => array(
            'endpoint' => '{service}.{region}.amazonaws.com'
        ),
        'cn-north-1/*' => array(
            'endpoint' => '{service}.{region}.amazonaws.com.cn',
            'signatureVersion' => 'v4'
        ),
        'us-gov-west-1/iam' => array(
            'endpoint' => 'iam.us-gov.amazonaws.com'
        ),
        'us-gov-west-1/sts' => array(
            'endpoint' => 'sts.us-gov-west-1.amazonaws.com'
        ),
        'us-gov-west-1/s3' => array(
            'endpoint' => 's3-{region}.amazonaws.com'
        ),
        '*/cloudfront' => array(
            'endpoint' => 'cloudfront.amazonaws.com',
            'credentialScope' => array(
                'region' => 'us-east-1'
            )
        ),
        '*/iam' => array(
            'endpoint' => 'iam.amazonaws.com',
            'credentialScope' => array(
                'region' => 'us-east-1'
            )
        ),
        '*/importexport' => array(
            'endpoint' => 'importexport.amazonaws.com',
            'credentialScope' => array(
                'region' => 'us-east-1'
            )
        ),
        '*/route53' => array(
            'endpoint' => 'route53.amazonaws.com',
            'credentialScope' => array(
                'region' => 'us-east-1'
            )
        ),
        '*/sts' => array(
            'endpoint' => 'sts.amazonaws.com',
            'credentialScope' => array(
                'region' => 'us-east-1'
            )
        ),
        'us-east-1/sdb' => array(
            'endpoint' => 'sdb.amazonaws.com'
        ),
        'us-east-1/s3' => array(
            'endpoint' => 's3.amazonaws.com'
        ),
        'us-west-1/s3' => array(
            'endpoint' => 's3-{region}.amazonaws.com'
        ),
        'us-west-2/s3' => array(
            'endpoint' => 's3-{region}.amazonaws.com'
        ),
        'eu-west-1/s3' => array(
            'endpoint' => 's3-{region}.amazonaws.com'
        ),
        'ap-southeast-1/s3' => array(
            'endpoint' => 's3-{region}.amazonaws.com'
        ),
        'ap-southeast-2/s3' => array(
            'endpoint' => 's3-{region}.amazonaws.com'
        ),
        'ap-northeast-1/s3' => array(
            'endpoint' => 's3-{region}.amazonaws.com'
        ),
        'sa-east-1/s3' => array(
            'endpoint' => 's3-{region}.amazonaws.com'
        )
    )
);
includes/lib/aws-sdk-php-2.8.31/src/Aws/Common/Resources/aws-config.php000064400000025222151327705670021316 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

return array(
    'class' => 'Aws\Common\Aws',
    'services' => array(

        'default_settings' => array(
            'params' => array()
        ),

        'autoscaling' => array(
            'alias'   => 'AutoScaling',
            'extends' => 'default_settings',
            'class'   => 'Aws\AutoScaling\AutoScalingClient'
        ),

        'cloudformation' => array(
            'alias'   => 'CloudFormation',
            'extends' => 'default_settings',
            'class'   => 'Aws\CloudFormation\CloudFormationClient'
        ),

        'cloudfront' => array(
            'alias'   => 'CloudFront',
            'extends' => 'default_settings',
            'class'   => 'Aws\CloudFront\CloudFrontClient'
        ),

        'cloudfront_20120505' => array(
            'extends' => 'cloudfront',
            'params' => array(
                'version' => '2012-05-05'
            )
        ),

        'cloudhsm' => array(
            'alias'   => 'CloudHsm',
            'extends' => 'default_settings',
            'class'   => 'Aws\CloudHsm\CloudHsmClient'
        ),

        'cloudsearch' => array(
            'alias'   => 'CloudSearch',
            'extends' => 'default_settings',
            'class'   => 'Aws\CloudSearch\CloudSearchClient'
        ),

        'cloudsearch_20110201' => array(
            'extends' => 'cloudsearch',
            'params' => array(
                'version' => '2011-02-01'
            )
        ),

        'cloudsearchdomain' => array(
            'alias'   => 'CloudSearchDomain',
            'extends' => 'default_settings',
            'class'   => 'Aws\CloudSearchDomain\CloudSearchDomainClient'
        ),

        'cloudtrail' => array(
            'alias'   => 'CloudTrail',
            'extends' => 'default_settings',
            'class'   => 'Aws\CloudTrail\CloudTrailClient'
        ),

        'cloudwatch' => array(
            'alias'   => 'CloudWatch',
            'extends' => 'default_settings',
            'class'   => 'Aws\CloudWatch\CloudWatchClient'
        ),

        'cloudwatchlogs' => array(
            'alias'   => 'CloudWatchLogs',
            'extends' => 'default_settings',
            'class'   => 'Aws\CloudWatchLogs\CloudWatchLogsClient'
        ),

        'cognito-identity' => array(
            'alias'   => 'CognitoIdentity',
            'extends' => 'default_settings',
            'class'   => 'Aws\CognitoIdentity\CognitoIdentityClient'
        ),

        'cognitoidentity' => array('extends' => 'cognito-identity'),

        'cognito-sync' => array(
            'alias'   => 'CognitoSync',
            'extends' => 'default_settings',
            'class'   => 'Aws\CognitoSync\CognitoSyncClient'
        ),

        'cognitosync' => array('extends' => 'cognito-sync'),

        'codecommit' => array(
            'alias'   => 'CodeCommit',
            'extends' => 'default_settings',
            'class'   => 'Aws\CodeCommit\CodeCommitClient'
        ),

        'codedeploy' => array(
            'alias'   => 'CodeDeploy',
            'extends' => 'default_settings',
            'class'   => 'Aws\CodeDeploy\CodeDeployClient'
        ),

        'codepipeline' => array(
            'alias'   => 'CodePipeline',
            'extends' => 'default_settings',
            'class'   => 'Aws\CodePipeline\CodePipelineClient'
        ),

        'config' => array(
            'alias'   => 'ConfigService',
            'extends' => 'default_settings',
            'class'   => 'Aws\ConfigService\ConfigServiceClient'
        ),

        'datapipeline' => array(
            'alias'   => 'DataPipeline',
            'extends' => 'default_settings',
            'class'   => 'Aws\DataPipeline\DataPipelineClient'
        ),

        'devicefarm' => array(
            'alias'   => 'DeviceFarm',
            'extends' => 'default_settings',
            'class'   => 'Aws\DeviceFarm\DeviceFarmClient'
        ),

        'directconnect' => array(
            'alias'   => 'DirectConnect',
            'extends' => 'default_settings',
            'class'   => 'Aws\DirectConnect\DirectConnectClient'
        ),

        'ds' => array(
            'alias'   => 'DirectoryService',
            'extends' => 'default_settings',
            'class'   => 'Aws\DirectoryService\DirectoryServiceClient'
        ),

        'dynamodb' => array(
            'alias'   => 'DynamoDb',
            'extends' => 'default_settings',
            'class'   => 'Aws\DynamoDb\DynamoDbClient'
        ),

        'dynamodb_20111205' => array(
            'extends' => 'dynamodb',
            'params' => array(
                'version' => '2011-12-05'
            )
        ),

        'dynamodbstreams' => array(
            'alias'   => 'DynamoDbStreams',
            'extends' => 'default_settings',
            'class'   => 'Aws\DynamoDbStreams\DynamoDbStreamsClient'
        ),

        'ec2' => array(
            'alias'   => 'Ec2',
            'extends' => 'default_settings',
            'class'   => 'Aws\Ec2\Ec2Client'
        ),

        'ecs' => array(
            'alias'   => 'Ecs',
            'extends' => 'default_settings',
            'class'   => 'Aws\Ecs\EcsClient'
        ),

        'elasticache' => array(
            'alias'   => 'ElastiCache',
            'extends' => 'default_settings',
            'class'   => 'Aws\ElastiCache\ElastiCacheClient'
        ),

        'elasticbeanstalk' => array(
            'alias'   => 'ElasticBeanstalk',
            'extends' => 'default_settings',
            'class'   => 'Aws\ElasticBeanstalk\ElasticBeanstalkClient'
        ),

        'efs' => array(
            'alias'   => 'Efs',
            'extends' => 'default_settings',
            'class'   => 'Aws\Efs\EfsClient'
        ),

        'elasticloadbalancing' => array(
            'alias'   => 'ElasticLoadBalancing',
            'extends' => 'default_settings',
            'class'   => 'Aws\ElasticLoadBalancing\ElasticLoadBalancingClient'
        ),

        'elastictranscoder' => array(
            'alias'   => 'ElasticTranscoder',
            'extends' => 'default_settings',
            'class'   => 'Aws\ElasticTranscoder\ElasticTranscoderClient'
        ),

        'emr' => array(
            'alias'   => 'Emr',
            'extends' => 'default_settings',
            'class'   => 'Aws\Emr\EmrClient'
        ),

        'glacier' => array(
            'alias'   => 'Glacier',
            'extends' => 'default_settings',
            'class'   => 'Aws\Glacier\GlacierClient'
        ),

        'kinesis' => array(
            'alias'   => 'Kinesis',
            'extends' => 'default_settings',
            'class'   => 'Aws\Kinesis\KinesisClient'
        ),

        'kms' => array(
            'alias'   => 'Kms',
            'extends' => 'default_settings',
            'class'   => 'Aws\Kms\KmsClient'
        ),

        'lambda' => array(
            'alias'   => 'Lambda',
            'extends' => 'default_settings',
            'class'   => 'Aws\Lambda\LambdaClient'
        ),

        'iam' => array(
            'alias'   => 'Iam',
            'extends' => 'default_settings',
            'class'   => 'Aws\Iam\IamClient'
        ),

        'importexport' => array(
            'alias'   => 'ImportExport',
            'extends' => 'default_settings',
            'class'   => 'Aws\ImportExport\ImportExportClient'
        ),

        'machinelearning' => array(
            'alias'   => 'MachineLearning',
            'extends' => 'default_settings',
            'class'   => 'Aws\MachineLearning\MachineLearningClient'
        ),

        'opsworks' => array(
            'alias'   => 'OpsWorks',
            'extends' => 'default_settings',
            'class'   => 'Aws\OpsWorks\OpsWorksClient'
        ),

        'rds' => array(
            'alias'   => 'Rds',
            'extends' => 'default_settings',
            'class'   => 'Aws\Rds\RdsClient'
        ),

        'redshift' => array(
            'alias'   => 'Redshift',
            'extends' => 'default_settings',
            'class'   => 'Aws\Redshift\RedshiftClient'
        ),

        'route53' => array(
            'alias'   => 'Route53',
            'extends' => 'default_settings',
            'class'   => 'Aws\Route53\Route53Client'
        ),

        'route53domains' => array(
            'alias'   => 'Route53Domains',
            'extends' => 'default_settings',
            'class'   => 'Aws\Route53Domains\Route53DomainsClient'
        ),

        's3' => array(
            'alias'   => 'S3',
            'extends' => 'default_settings',
            'class'   => 'Aws\S3\S3Client'
        ),

        'sdb' => array(
            'alias'   => 'SimpleDb',
            'extends' => 'default_settings',
            'class'   => 'Aws\SimpleDb\SimpleDbClient'
        ),

        'ses' => array(
            'alias'   => 'Ses',
            'extends' => 'default_settings',
            'class'   => 'Aws\Ses\SesClient'
        ),

        'sns' => array(
            'alias'   => 'Sns',
            'extends' => 'default_settings',
            'class'   => 'Aws\Sns\SnsClient'
        ),

        'sqs' => array(
            'alias'   => 'Sqs',
            'extends' => 'default_settings',
            'class'   => 'Aws\Sqs\SqsClient'
        ),

        'ssm' => array(
            'alias'   => 'Ssm',
            'extends' => 'default_settings',
            'class'   => 'Aws\Ssm\SsmClient'
        ),

        'storagegateway' => array(
            'alias'   => 'StorageGateway',
            'extends' => 'default_settings',
            'class'   => 'Aws\StorageGateway\StorageGatewayClient'
        ),

        'sts' => array(
            'alias'   => 'Sts',
            'extends' => 'default_settings',
            'class'   => 'Aws\Sts\StsClient'
        ),

        'support' => array(
            'alias'   => 'Support',
            'extends' => 'default_settings',
            'class'   => 'Aws\Support\SupportClient'
        ),

        'swf' => array(
            'alias'   => 'Swf',
            'extends' => 'default_settings',
            'class'   => 'Aws\Swf\SwfClient'
        ),

        'workspaces' => array(
            'alias'   => 'WorkSpaces',
            'extends' => 'default_settings',
            'class'   => 'Aws\WorkSpaces\WorkSpacesClient'
        ),
    )
);
includes/lib/aws-sdk-php-2.8.31/src/Aws/Common/RulesEndpointProvider.php000064400000003437151327705670021621 0ustar00<?php
namespace Aws\Common;

/**
 * Provides endpoints based on a rules configuration file.
 */
class RulesEndpointProvider
{
    /** @var array */
    private $patterns;

    /**
     * @param array $patterns Hash of endpoint patterns mapping to endpoint
     *                        configurations.
     */
    public function __construct(array $patterns)
    {
        $this->patterns = $patterns;
    }

    /**
     * Creates and returns the default RulesEndpointProvider based on the
     * public rule sets.
     *
     * @return self
     */
    public static function fromDefaults()
    {
        return new self(require __DIR__ . '/Resources/public-endpoints.php');
    }

    public function __invoke(array $args = array())
    {
        if (!isset($args['service'])) {
            throw new \InvalidArgumentException('Requires a "service" value');
        }

        if (!isset($args['region'])) {
            throw new \InvalidArgumentException('Requires a "region" value');
        }

        foreach ($this->getKeys($args['region'], $args['service']) as $key) {
            if (isset($this->patterns['endpoints'][$key])) {
                return $this->expand($this->patterns['endpoints'][$key], $args);
            }
        }

        throw new \RuntimeException('Could not resolve endpoint');
    }

    private function expand(array $config, array $args)
    {
        $scheme = isset($args['scheme']) ? $args['scheme'] : 'https';
        $config['endpoint'] = $scheme . '://' . str_replace(
            array('{service}', '{region}'),
            array($args['service'], $args['region']),
            $config['endpoint']
        );

        return $config;
    }

    private function getKeys($region, $service)
    {
        return array("$region/$service", "$region/*", "*/$service", "*/*");
    }
}
includes/lib/aws-sdk-php-2.8.31/src/Aws/Common/Hash/TreeHash.php000064400000014031151327705670017671 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\Common\Hash;

use Aws\Common\Enum\Size;
use Aws\Common\Exception\InvalidArgumentException;
use Aws\Common\Exception\LogicException;
use Guzzle\Http\EntityBody;

/**
 * Encapsulates the creation of a tree hash from streamed chunks of data
 */
class TreeHash implements ChunkHashInterface
{
    /**
     * @var string The algorithm used for hashing
     */
    protected $algorithm;

    /**
     * @var array Set of binary checksums from which the tree hash is derived
     */
    protected $checksums = array();

    /**
     * @var string The resulting hash in hex form
     */
    protected $hash;

    /**
     * @var string The resulting hash in binary form
     */
    protected $hashRaw;

    /**
     * Create a tree hash from an array of existing tree hash checksums
     *
     * @param array  $checksums    Set of checksums
     * @param bool   $inBinaryForm Whether or not the checksums are already in binary form
     * @param string $algorithm    A valid hash algorithm name as returned by `hash_algos()`
     *
     * @return TreeHash
     */
    public static function fromChecksums(array $checksums, $inBinaryForm = false, $algorithm = self::DEFAULT_ALGORITHM)
    {
        $treeHash = new self($algorithm);

        // Convert checksums to binary form if provided in hex form and add them to the tree hash
        $treeHash->checksums = $inBinaryForm ? $checksums : array_map('Aws\Common\Hash\HashUtils::hexToBin', $checksums);

        // Pre-calculate hash
        $treeHash->getHash();

        return $treeHash;
    }

    /**
     * Create a tree hash from a content body
     *
     * @param string|resource|EntityBody $content   Content to create a tree hash for
     * @param string                     $algorithm A valid hash algorithm name as returned by `hash_algos()`
     *
     * @return TreeHash
     */
    public static function fromContent($content, $algorithm = self::DEFAULT_ALGORITHM)
    {
        $treeHash = new self($algorithm);

        // Read the data in 1MB chunks and add to tree hash
        $content = EntityBody::factory($content);
        while ($data = $content->read(Size::MB)) {
            $treeHash->addData($data);
        }

        // Pre-calculate hash
        $treeHash->getHash();

        return $treeHash;
    }

    /**
     * Validates an entity body with a tree hash checksum
     *
     * @param string|resource|EntityBody $content   Content to create a tree hash for
     * @param string                     $checksum  The checksum to use for validation
     * @param string                     $algorithm A valid hash algorithm name as returned by `hash_algos()`
     *
     * @return bool
     */
    public static function validateChecksum($content, $checksum, $algorithm = self::DEFAULT_ALGORITHM)
    {
        $treeHash = self::fromContent($content, $algorithm);

        return ($checksum === $treeHash->getHash());
    }

    /**
     * {@inheritdoc}
     */
    public function __construct($algorithm = self::DEFAULT_ALGORITHM)
    {
        HashUtils::validateAlgorithm($algorithm);
        $this->algorithm = $algorithm;
    }

    /**
     * {@inheritdoc}
     * @throws LogicException           if the root tree hash is already calculated
     * @throws InvalidArgumentException if the data is larger than 1MB
     */
    public function addData($data)
    {
        // Error if hash is already calculated
        if ($this->hash) {
            throw new LogicException('You may not add more data to a finalized tree hash.');
        }

        // Make sure that only 1MB chunks or smaller get passed in
        if (strlen($data) > Size::MB) {
            throw new InvalidArgumentException('The chunk of data added is too large for tree hashing.');
        }

        // Store the raw hash of this data segment
        $this->checksums[] = hash($this->algorithm, $data, true);

        return $this;
    }

    /**
     * Add a checksum to the tree hash directly
     *
     * @param string $checksum     The checksum to add
     * @param bool   $inBinaryForm Whether or not the checksum is already in binary form
     *
     * @return self
     * @throws LogicException if the root tree hash is already calculated
     */
    public function addChecksum($checksum, $inBinaryForm = false)
    {
        // Error if hash is already calculated
        if ($this->hash) {
            throw new LogicException('You may not add more checksums to a finalized tree hash.');
        }

        // Convert the checksum to binary form if necessary
        $this->checksums[] = $inBinaryForm ? $checksum : HashUtils::hexToBin($checksum);

        return $this;
    }

    /**
     * {@inheritdoc}
     */
    public function getHash($returnBinaryForm = false)
    {
        if (!$this->hash) {
            // Perform hashes up the tree to arrive at the root checksum of the tree hash
            $hashes = $this->checksums;
            while (count($hashes) > 1) {
                $sets = array_chunk($hashes, 2);
                $hashes = array();
                foreach ($sets as $set) {
                    $hashes[] = (count($set) === 1) ? $set[0] : hash($this->algorithm, $set[0] . $set[1], true);
                }
            }

            $this->hashRaw = $hashes[0];
            $this->hash = HashUtils::binToHex($this->hashRaw);
        }

        return $returnBinaryForm ? $this->hashRaw : $this->hash;
    }

    /**
     * @return array Array of raw checksums composing the tree hash
     */
    public function getChecksums()
    {
        return $this->checksums;
    }
}
includes/lib/aws-sdk-php-2.8.31/src/Aws/Common/Hash/ChunkHashInterface.php000064400000002714151327705670021670 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\Common\Hash;

/**
 * Interface for objects that encapsulate the creation of a hash from streamed chunks of data
 */
interface ChunkHashInterface
{
    const DEFAULT_ALGORITHM = 'sha256';

    /**
     * Constructs the chunk hash and sets the algorithm to use for hashing
     *
     * @param string $algorithm A valid hash algorithm name as returned by `hash_algos()`
     *
     * @return self
     */
    public function __construct($algorithm = 'sha256');

    /**
     * Add a chunk of data to be hashed
     *
     * @param string $data Data to be hashed
     *
     * @return self
     */
    public function addData($data);

    /**
     * Return the results of the hash
     *
     * @param bool $returnBinaryForm If true, returns the hash in binary form instead of hex form
     *
     * @return string
     */
    public function getHash($returnBinaryForm = false);
}
includes/lib/aws-sdk-php-2.8.31/src/Aws/Common/Hash/ChunkHash.php000064400000004120151327705670020040 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\Common\Hash;

use Aws\Common\Exception\LogicException;

/**
 * Encapsulates the creation of a hash from streamed chunks of data
 */
class ChunkHash implements ChunkHashInterface
{
    /**
     * @var resource The hash context as created by `hash_init()`
     */
    protected $context;

    /**
     * @var string The resulting hash in hex form
     */
    protected $hash;

    /**
     * @var string The resulting hash in binary form
     */
    protected $hashRaw;

    /**
     * {@inheritdoc}
     */
    public function __construct($algorithm = self::DEFAULT_ALGORITHM)
    {
        HashUtils::validateAlgorithm($algorithm);
        $this->context = hash_init($algorithm);
    }

    /**
     * {@inheritdoc}
     */
    public function addData($data)
    {
        if (!$this->context) {
            throw new LogicException('You may not add more data to a finalized chunk hash.');
        }

        hash_update($this->context, $data);

        return $this;
    }

    /**
     * {@inheritdoc}
     */
    public function getHash($returnBinaryForm = false)
    {
        if (!$this->hash) {
            $this->hashRaw = hash_final($this->context, true);
            $this->hash = HashUtils::binToHex($this->hashRaw);
            $this->context = null;
        }

        return $returnBinaryForm ? $this->hashRaw : $this->hash;
    }

    /**
     * {@inheritdoc}
     */
    public function __clone()
    {
        if ($this->context) {
            $this->context = hash_copy($this->context);
        }
    }
}
includes/lib/aws-sdk-php-2.8.31/src/Aws/Common/Hash/HashUtils.php000064400000004127151327705670020077 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\Common\Hash;

use Aws\Common\Exception\InvalidArgumentException;

/**
 * Contains hashing utilities
 */
class HashUtils
{
    /**
     * Converts a hash in hex form to binary form
     *
     * @param string $hash Hash in hex form
     *
     * @return string Hash in binary form
     */
    public static function hexToBin($hash)
    {
        // If using PHP 5.4, there is a native function to convert from hex to binary
        static $useNative;
        if ($useNative === null) {
            $useNative = function_exists('hex2bin');
        }

        if (!$useNative && strlen($hash) % 2 !== 0) {
            $hash = '0' . $hash;
        }

        return $useNative ? hex2bin($hash) : pack("H*", $hash);
    }

    /**
     * Converts a hash in binary form to hex form
     *
     * @param string $hash Hash in binary form
     *
     * @return string Hash in hex form
     */
    public static function binToHex($hash)
    {
        return bin2hex($hash);
    }

    /**
     * Checks if the algorithm specified exists and throws an exception if it does not
     *
     * @param string $algorithm Name of the algorithm to validate
     *
     * @return bool
     * @throws InvalidArgumentException if the algorithm doesn't exist
     */
    public static function validateAlgorithm($algorithm)
    {
        if (!in_array($algorithm, hash_algos(), true)) {
            throw new InvalidArgumentException(esc_html("The hashing algorithm specified ({$algorithm}) does not exist."));
        }

        return true;
    }
}
includes/lib/aws-sdk-php-2.8.31/src/Aws/S3/AcpListener.php000064400000004762151327705670016563 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\S3;

use Aws\Common\Exception\InvalidArgumentException;
use Aws\S3\Model\Acp;
use Guzzle\Common\Event;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;

/**
 * Listener used to add an Access Control Policy to a request
 */
class AcpListener implements EventSubscriberInterface
{
    /**
     * {@inheritdoc}
     */
    public static function getSubscribedEvents()
    {
        return array('command.before_prepare' => array('onCommandBeforePrepare', -255));
    }

    /**
     * An event handler for constructing ACP definitions.
     *
     * @param Event $event The event to respond to.
     *
     * @throws InvalidArgumentException
     */
    public function onCommandBeforePrepare(Event $event)
    {
        /** @var \Guzzle\Service\Command\AbstractCommand $command */
        $command = $event['command'];
        $operation = $command->getOperation();
        if ($operation->hasParam('ACP') && $command->hasKey('ACP')) {
            if ($acp = $command->get('ACP')) {
                // Ensure that the correct object was passed
                if (!($acp instanceof Acp)) {
                    throw new InvalidArgumentException('ACP must be an instance of Aws\S3\Model\Acp');
                }

                // Check if the user specified both an ACP and Grants
                if ($command->hasKey('Grants')) {
                    throw new InvalidArgumentException(
                        'Use either the ACP parameter or the Grants parameter. Do not use both.'
                    );
                }

                // Add the correct headers/body based parameters to the command
                if ($operation->hasParam('Grants')) {
                    $command->overwriteWith($acp->toArray());
                } else {
                    $acp->updateCommand($command);
                }
            }

            // Remove the ACP parameter
            $command->remove('ACP');
        }
    }
}
includes/lib/aws-sdk-php-2.8.31/src/Aws/S3/S3Signature.php000064400000022114151327705670016510 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\S3;

use Aws\Common\Credentials\CredentialsInterface;
use Guzzle\Http\Message\RequestInterface;
use Guzzle\Http\QueryString;
use Guzzle\Http\Url;

/**
 * Default Amazon S3 signature implementation
 * @link http://docs.aws.amazon.com/AmazonS3/latest/dev/RESTAuthentication.html
 */
class S3Signature implements S3SignatureInterface
{
    /**
     * @var array Query string values that must be signed
     */
    protected $signableQueryString = array (
        'acl',
        'cors',
        'delete',
        'lifecycle',
        'location',
        'logging',
        'notification',
        'partNumber',
        'policy',
        'requestPayment',
        'response-cache-control',
        'response-content-disposition',
        'response-content-encoding',
        'response-content-language',
        'response-content-type',
        'response-expires',
        'restore',
        'tagging',
        'torrent',
        'uploadId',
        'uploads',
        'versionId',
        'versioning',
        'versions',
        'website',
    );

    /** @var array Sorted headers that must be signed */
    private $signableHeaders = array('Content-MD5', 'Content-Type');

    public function signRequest(RequestInterface $request, CredentialsInterface $credentials)
    {
        // Ensure that the signable query string parameters are sorted
        sort($this->signableQueryString);

        // Add the security token header if one is being used by the credentials
        if ($token = $credentials->getSecurityToken()) {
            $request->setHeader('x-amz-security-token', $token);
        }

        $request->removeHeader('x-amz-date');
        $request->setHeader('Date', gmdate(\DateTime::RFC2822));

        $stringToSign = $this->createCanonicalizedString($request);
        $request->getParams()->set('aws.string_to_sign', $stringToSign);

        $request->setHeader(
            'Authorization',
            'AWS ' . $credentials->getAccessKeyId() . ':' . $this->signString($stringToSign, $credentials)
        );
    }

    public function createPresignedUrl(
        RequestInterface $request,
        CredentialsInterface $credentials,
        $expires
    ) {
        if ($expires instanceof \DateTime) {
            $expires = $expires->getTimestamp();
        } elseif (!is_numeric($expires)) {
            $expires = strtotime($expires);
        }

        // Operate on a clone of the request, so the original is not altered
        $request = clone $request;

        // URL encoding already occurs in the URI template expansion. Undo that and encode using the same encoding as
        // GET object, PUT object, etc.
        $path = S3Client::encodeKey(rawurldecode($request->getPath()));
        $request->setPath($path);

        // Make sure to handle temporary credentials
        if ($token = $credentials->getSecurityToken()) {
            $request->setHeader('x-amz-security-token', $token);
            $request->getQuery()->set('x-amz-security-token', $token);
        }

        // Set query params required for pre-signed URLs
        $request->getQuery()
            ->set('AWSAccessKeyId', $credentials->getAccessKeyId())
            ->set('Expires', $expires)
            ->set('Signature', $this->signString(
                $this->createCanonicalizedString($request, $expires),
                $credentials
            ));

        // Move X-Amz-* headers to the query string
        foreach ($request->getHeaders() as $name => $header) {
            $name = strtolower($name);
            if (strpos($name, 'x-amz-') === 0) {
                $request->getQuery()->set($name, (string) $header);
                $request->removeHeader($name);
            }
        }

        return $request->getUrl();
    }

    public function signString($string, CredentialsInterface $credentials)
    {
        return base64_encode(hash_hmac('sha1', $string, $credentials->getSecretKey(), true));
    }

    public function createCanonicalizedString(RequestInterface $request, $expires = null)
    {
        $buffer = $request->getMethod() . "\n";

        // Add the interesting headers
        foreach ($this->signableHeaders as $header) {
            $buffer .= (string) $request->getHeader($header) . "\n";
        }

        // Choose dates from left to right based on what's set
        $date = $expires ?: (string) $request->getHeader('date');

        $buffer .= "{$date}\n"
            . $this->createCanonicalizedAmzHeaders($request)
            . $this->createCanonicalizedResource($request);

        return $buffer;
    }

    /**
     * Create a canonicalized AmzHeaders string for a signature.
     *
     * @param RequestInterface $request Request from which to gather headers
     *
     * @return string Returns canonicalized AMZ headers.
     */
    private function createCanonicalizedAmzHeaders(RequestInterface $request)
    {
        $headers = array();
        foreach ($request->getHeaders() as $name => $header) {
            $name = strtolower($name);
            if (strpos($name, 'x-amz-') === 0) {
                $value = trim((string) $header);
                if ($value || $value === '0') {
                    $headers[$name] = $name . ':' . $value;
                }
            }
        }

        if (!$headers) {
            return '';
        }

        ksort($headers);

        return implode("\n", $headers) . "\n";
    }

    /**
     * Create a canonicalized resource for a request
     *
     * @param RequestInterface $request Request for the resource
     *
     * @return string
     */
    private function createCanonicalizedResource(RequestInterface $request)
    {
        $buffer = $request->getParams()->get('s3.resource');
        // When sending a raw HTTP request (e.g. $client->get())
        if (null === $buffer) {
            $bucket = $request->getParams()->get('bucket') ?: $this->parseBucketName($request);
            // Use any specified bucket name, the parsed bucket name, or no bucket name when interacting with GetService
            $buffer = $bucket ? "/{$bucket}" : '';
            // Remove encoding from the path and use the S3 specific encoding
            $path = S3Client::encodeKey(rawurldecode($request->getPath()));
            // if the bucket was path style, then ensure that the bucket wasn't duplicated in the resource
            $buffer .= preg_replace("#^/{$bucket}/{$bucket}#", "/{$bucket}", $path);
        }

        // Remove double slashes
        $buffer = str_replace('//', '/', $buffer);

        // Add sub resource parameters
        $query = $request->getQuery();
        $first = true;
        foreach ($this->signableQueryString as $key) {
            if ($query->hasKey($key)) {
                $value = $query[$key];
                $buffer .= $first ? '?' : '&';
                $first = false;
                $buffer .= $key;
                // Don't add values for empty sub-resources
                if ($value !== '' &&
                    $value !== false &&
                    $value !== null &&
                    $value !== QueryString::BLANK
                ) {
                    $buffer .= "={$value}";
                }
            }
        }

        return $buffer;
    }

    /**
     * Parse the bucket name from a request object
     *
     * @param RequestInterface $request Request to parse
     *
     * @return string
     */
    private function parseBucketName(RequestInterface $request)
    {
        $baseUrl = Url::factory($request->getClient()->getBaseUrl());
        $baseHost = $baseUrl->getHost();
        $host = $request->getHost();

        if (strpos($host, $baseHost) === false) {
            // Does not contain the base URL, so it's either a redirect, CNAME, or using a different region
            $baseHost = '';
            // For every known S3 host, check if that host is present on the request
            $regions = $request->getClient()->getDescription()->getData('regions');
            foreach ($regions as $region) {
                if (strpos($host, $region['hostname']) !== false) {
                    // This host matches the request host. Tells use the region and endpoint-- we can derive the bucket
                    $baseHost = $region['hostname'];
                    break;
                }
            }
            // If no matching base URL was found, then assume that this is a CNAME, and the CNAME is the bucket
            if (!$baseHost) {
                return $host;
            }
        }

        // Remove the baseURL from the host of the request to attempt to determine the bucket name
        return trim(str_replace($baseHost, '', $request->getHost()), ' .');
    }
}
includes/lib/aws-sdk-php-2.8.31/src/Aws/S3/Resources/s3-2006-03-01.php000064400000712626151327705670017701 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

return array (
    'apiVersion' => '2006-03-01',
    'endpointPrefix' => 's3',
    'serviceFullName' => 'Amazon Simple Storage Service',
    'serviceAbbreviation' => 'Amazon S3',
    'serviceType' => 'rest-xml',
    'timestampFormat' => 'rfc822',
    'globalEndpoint' => 's3.amazonaws.com',
    'signatureVersion' => 's3',
    'namespace' => 'S3',
    'regions' => array(
        'us-east-1' => array(
            'http' => true,
            'https' => true,
            'hostname' => 's3.amazonaws.com',
        ),
        'us-west-1' => array(
            'http' => true,
            'https' => true,
            'hostname' => 's3-us-west-1.amazonaws.com',
        ),
        'us-west-2' => array(
            'http' => true,
            'https' => true,
            'hostname' => 's3-us-west-2.amazonaws.com',
        ),
        'eu-west-1' => array(
            'http' => true,
            'https' => true,
            'hostname' => 's3-eu-west-1.amazonaws.com',
        ),
        'eu-central-1' => array(
            'http' => true,
            'https' => true,
            'hostname' => 's3-eu-central-1.amazonaws.com',
        ),
        'ap-northeast-1' => array(
            'http' => true,
            'https' => true,
            'hostname' => 's3-ap-northeast-1.amazonaws.com',
        ),
        'ap-southeast-1' => array(
            'http' => true,
            'https' => true,
            'hostname' => 's3-ap-southeast-1.amazonaws.com',
        ),
        'ap-southeast-2' => array(
            'http' => true,
            'https' => true,
            'hostname' => 's3-ap-southeast-2.amazonaws.com',
        ),
        'sa-east-1' => array(
            'http' => true,
            'https' => true,
            'hostname' => 's3-sa-east-1.amazonaws.com',
        ),
        'cn-north-1' => array(
            'http' => true,
            'https' => true,
            'hostname' => 's3.cn-north-1.amazonaws.com.cn',
        ),
        'us-gov-west-1' => array(
            'http' => true,
            'https' => true,
            'hostname' => 's3-us-gov-west-1.amazonaws.com',
        ),
    ),
    'operations' => array(
        'AbortMultipartUpload' => array(
            'httpMethod' => 'DELETE',
            'uri' => '/{Bucket}{/Key*}',
            'class' => 'Aws\\S3\\Command\\S3Command',
            'responseClass' => 'AbortMultipartUploadOutput',
            'responseType' => 'model',
            'documentationUrl' => 'http://docs.aws.amazon.com/AmazonS3/latest/API/mpUploadAbort.html',
            'parameters' => array(
                'Bucket' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'uri',
                ),
                'Key' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'uri',
                    'minLength' => 1,
                    'filters' => array(
                        'Aws\\S3\\S3Client::explodeKey',
                    ),
                ),
                'UploadId' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'query',
                    'sentAs' => 'uploadId',
                ),
                'RequestPayer' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-request-payer',
                ),
            ),
            'errorResponses' => array(
                array(
                    'reason' => 'The specified multipart upload does not exist.',
                    'class' => 'NoSuchUploadException',
                ),
            ),
        ),
        'CompleteMultipartUpload' => array(
            'httpMethod' => 'POST',
            'uri' => '/{Bucket}{/Key*}',
            'class' => 'Aws\\S3\\Command\\S3Command',
            'responseClass' => 'CompleteMultipartUploadOutput',
            'responseType' => 'model',
            'documentationUrl' => 'http://docs.aws.amazon.com/AmazonS3/latest/API/mpUploadComplete.html',
            'data' => array(
                'xmlRoot' => array(
                    'name' => 'CompleteMultipartUpload',
                    'namespaces' => array(
                        'http://s3.amazonaws.com/doc/2006-03-01/',
                    ),
                ),
            ),
            'parameters' => array(
                'Bucket' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'uri',
                ),
                'Key' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'uri',
                    'minLength' => 1,
                    'filters' => array(
                        'Aws\\S3\\S3Client::explodeKey',
                    ),
                ),
                'Parts' => array(
                    'type' => 'array',
                    'location' => 'xml',
                    'data' => array(
                        'xmlFlattened' => true,
                    ),
                    'items' => array(
                        'name' => 'CompletedPart',
                        'type' => 'object',
                        'sentAs' => 'Part',
                        'properties' => array(
                            'ETag' => array(
                                'type' => 'string',
                            ),
                            'PartNumber' => array(
                                'type' => 'numeric',
                            ),
                        ),
                    ),
                ),
                'UploadId' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'query',
                    'sentAs' => 'uploadId',
                ),
                'RequestPayer' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-request-payer',
                ),
                'command.expects' => array(
                    'static' => true,
                    'default' => 'application/xml',
                ),
            ),
        ),
        'CopyObject' => array(
            'httpMethod' => 'PUT',
            'uri' => '/{Bucket}{/Key*}',
            'class' => 'Aws\\S3\\Command\\S3Command',
            'responseClass' => 'CopyObjectOutput',
            'responseType' => 'model',
            'documentationUrl' => 'http://docs.aws.amazon.com/AmazonS3/latest/API/RESTObjectCOPY.html',
            'data' => array(
                'xmlRoot' => array(
                    'name' => 'CopyObjectRequest',
                    'namespaces' => array(
                        'http://s3.amazonaws.com/doc/2006-03-01/',
                    ),
                ),
            ),
            'parameters' => array(
                'ACL' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-acl',
                ),
                'Bucket' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'uri',
                ),
                'CacheControl' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'Cache-Control',
                ),
                'ContentDisposition' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'Content-Disposition',
                ),
                'ContentEncoding' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'Content-Encoding',
                ),
                'ContentLanguage' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'Content-Language',
                ),
                'ContentType' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'Content-Type',
                ),
                'CopySource' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-copy-source',
                ),
                'CopySourceIfMatch' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-copy-source-if-match',
                ),
                'CopySourceIfModifiedSince' => array(
                    'type' => array(
                        'object',
                        'string',
                        'integer',
                    ),
                    'format' => 'date-time-http',
                    'location' => 'header',
                    'sentAs' => 'x-amz-copy-source-if-modified-since',
                ),
                'CopySourceIfNoneMatch' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-copy-source-if-none-match',
                ),
                'CopySourceIfUnmodifiedSince' => array(
                    'type' => array(
                        'object',
                        'string',
                        'integer',
                    ),
                    'format' => 'date-time-http',
                    'location' => 'header',
                    'sentAs' => 'x-amz-copy-source-if-unmodified-since',
                ),
                'Expires' => array(
                    'type' => array(
                        'object',
                        'string',
                        'integer',
                    ),
                    'format' => 'date-time-http',
                    'location' => 'header',
                ),
                'GrantFullControl' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-grant-full-control',
                ),
                'GrantRead' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-grant-read',
                ),
                'GrantReadACP' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-grant-read-acp',
                ),
                'GrantWriteACP' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-grant-write-acp',
                ),
                'Key' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'uri',
                    'minLength' => 1,
                    'filters' => array(
                        'Aws\\S3\\S3Client::explodeKey',
                    ),
                ),
                'Metadata' => array(
                    'type' => 'object',
                    'location' => 'header',
                    'sentAs' => 'x-amz-meta-',
                    'additionalProperties' => array(
                        'type' => 'string',
                    ),
                ),
                'MetadataDirective' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-metadata-directive',
                ),
                'ServerSideEncryption' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-server-side-encryption',
                ),
                'StorageClass' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-storage-class',
                ),
                'WebsiteRedirectLocation' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-website-redirect-location',
                ),
                'SSECustomerAlgorithm' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-server-side-encryption-customer-algorithm',
                ),
                'SSECustomerKey' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-server-side-encryption-customer-key',
                ),
                'SSECustomerKeyMD5' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-server-side-encryption-customer-key-MD5',
                ),
                'SSEKMSKeyId' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-server-side-encryption-aws-kms-key-id',
                ),
                'CopySourceSSECustomerAlgorithm' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-copy-source-server-side-encryption-customer-algorithm',
                ),
                'CopySourceSSECustomerKey' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-copy-source-server-side-encryption-customer-key',
                ),
                'CopySourceSSECustomerKeyMD5' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-copy-source-server-side-encryption-customer-key-MD5',
                ),
                'RequestPayer' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-request-payer',
                ),
                'ACP' => array(
                    'type' => 'object',
                    'additionalProperties' => true,
                ),
                'command.expects' => array(
                    'static' => true,
                    'default' => 'application/xml',
                ),
            ),
            'errorResponses' => array(
                array(
                    'reason' => 'The source object of the COPY operation is not in the active tier and is only stored in Amazon Glacier.',
                    'class' => 'ObjectNotInActiveTierErrorException',
                ),
            ),
        ),
        'CreateBucket' => array(
            'httpMethod' => 'PUT',
            'uri' => '/{Bucket}',
            'class' => 'Aws\\S3\\Command\\S3Command',
            'responseClass' => 'CreateBucketOutput',
            'responseType' => 'model',
            'documentationUrl' => 'http://docs.aws.amazon.com/AmazonS3/latest/API/RESTBucketPUT.html',
            'data' => array(
                'xmlRoot' => array(
                    'name' => 'CreateBucketConfiguration',
                    'namespaces' => array(
                        'http://s3.amazonaws.com/doc/2006-03-01/',
                    ),
                ),
            ),
            'parameters' => array(
                'ACL' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-acl',
                ),
                'Bucket' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'uri',
                ),
                'LocationConstraint' => array(
                    'type' => 'string',
                    'location' => 'xml',
                ),
                'GrantFullControl' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-grant-full-control',
                ),
                'GrantRead' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-grant-read',
                ),
                'GrantReadACP' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-grant-read-acp',
                ),
                'GrantWrite' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-grant-write',
                ),
                'GrantWriteACP' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-grant-write-acp',
                ),
                'ACP' => array(
                    'type' => 'object',
                    'additionalProperties' => true,
                ),
            ),
            'errorResponses' => array(
                array(
                    'reason' => 'The requested bucket name is not available. The bucket namespace is shared by all users of the system. Please select a different name and try again.',
                    'class' => 'BucketAlreadyExistsException',
                ),
            ),
        ),
        'CreateMultipartUpload' => array(
            'httpMethod' => 'POST',
            'uri' => '/{Bucket}{/Key*}?uploads',
            'class' => 'Aws\\S3\\Command\\S3Command',
            'responseClass' => 'CreateMultipartUploadOutput',
            'responseType' => 'model',
            'documentationUrl' => 'http://docs.aws.amazon.com/AmazonS3/latest/API/mpUploadInitiate.html',
            'data' => array(
                'xmlRoot' => array(
                    'name' => 'CreateMultipartUploadRequest',
                    'namespaces' => array(
                        'http://s3.amazonaws.com/doc/2006-03-01/',
                    ),
                ),
            ),
            'parameters' => array(
                'ACL' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-acl',
                ),
                'Bucket' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'uri',
                ),
                'CacheControl' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'Cache-Control',
                ),
                'ContentDisposition' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'Content-Disposition',
                ),
                'ContentEncoding' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'Content-Encoding',
                ),
                'ContentLanguage' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'Content-Language',
                ),
                'ContentType' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'Content-Type',
                ),
                'Expires' => array(
                    'type' => array(
                        'object',
                        'string',
                        'integer',
                    ),
                    'format' => 'date-time-http',
                    'location' => 'header',
                ),
                'GrantFullControl' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-grant-full-control',
                ),
                'GrantRead' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-grant-read',
                ),
                'GrantReadACP' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-grant-read-acp',
                ),
                'GrantWriteACP' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-grant-write-acp',
                ),
                'Key' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'uri',
                    'minLength' => 1,
                    'filters' => array(
                        'Aws\\S3\\S3Client::explodeKey',
                    ),
                ),
                'Metadata' => array(
                    'type' => 'object',
                    'location' => 'header',
                    'sentAs' => 'x-amz-meta-',
                    'additionalProperties' => array(
                        'type' => 'string',
                    ),
                ),
                'ServerSideEncryption' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-server-side-encryption',
                ),
                'StorageClass' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-storage-class',
                ),
                'WebsiteRedirectLocation' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-website-redirect-location',
                ),
                'SSECustomerAlgorithm' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-server-side-encryption-customer-algorithm',
                ),
                'SSECustomerKey' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-server-side-encryption-customer-key',
                ),
                'SSECustomerKeyMD5' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-server-side-encryption-customer-key-MD5',
                ),
                'SSEKMSKeyId' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-server-side-encryption-aws-kms-key-id',
                ),
                'RequestPayer' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-request-payer',
                ),
                'ACP' => array(
                    'type' => 'object',
                    'additionalProperties' => true,
                ),
                'command.expects' => array(
                    'static' => true,
                    'default' => 'application/xml',
                ),
            ),
        ),
        'DeleteBucket' => array(
            'httpMethod' => 'DELETE',
            'uri' => '/{Bucket}',
            'class' => 'Aws\\S3\\Command\\S3Command',
            'responseClass' => 'DeleteBucketOutput',
            'responseType' => 'model',
            'documentationUrl' => 'http://docs.aws.amazon.com/AmazonS3/latest/API/RESTBucketDELETE.html',
            'parameters' => array(
                'Bucket' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'uri',
                ),
            ),
        ),
        'DeleteBucketCors' => array(
            'httpMethod' => 'DELETE',
            'uri' => '/{Bucket}?cors',
            'class' => 'Aws\\S3\\Command\\S3Command',
            'responseClass' => 'DeleteBucketCorsOutput',
            'responseType' => 'model',
            'documentationUrl' => 'http://docs.aws.amazon.com/AmazonS3/latest/API/RESTBucketDELETEcors.html',
            'parameters' => array(
                'Bucket' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'uri',
                ),
            ),
        ),
        'DeleteBucketLifecycle' => array(
            'httpMethod' => 'DELETE',
            'uri' => '/{Bucket}?lifecycle',
            'class' => 'Aws\\S3\\Command\\S3Command',
            'responseClass' => 'DeleteBucketLifecycleOutput',
            'responseType' => 'model',
            'documentationUrl' => 'http://docs.aws.amazon.com/AmazonS3/latest/API/RESTBucketDELETElifecycle.html',
            'parameters' => array(
                'Bucket' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'uri',
                ),
            ),
        ),
        'DeleteBucketPolicy' => array(
            'httpMethod' => 'DELETE',
            'uri' => '/{Bucket}?policy',
            'class' => 'Aws\\S3\\Command\\S3Command',
            'responseClass' => 'DeleteBucketPolicyOutput',
            'responseType' => 'model',
            'documentationUrl' => 'http://docs.aws.amazon.com/AmazonS3/latest/API/RESTBucketDELETEpolicy.html',
            'parameters' => array(
                'Bucket' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'uri',
                ),
            ),
        ),
        'DeleteBucketReplication' => array(
            'httpMethod' => 'DELETE',
            'uri' => '/{Bucket}?replication',
            'class' => 'Aws\\S3\\Command\\S3Command',
            'responseClass' => 'DeleteBucketReplicationOutput',
            'responseType' => 'model',
            'parameters' => array(
                'Bucket' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'uri',
                ),
            ),
        ),
        'DeleteBucketTagging' => array(
            'httpMethod' => 'DELETE',
            'uri' => '/{Bucket}?tagging',
            'class' => 'Aws\\S3\\Command\\S3Command',
            'responseClass' => 'DeleteBucketTaggingOutput',
            'responseType' => 'model',
            'documentationUrl' => 'http://docs.aws.amazon.com/AmazonS3/latest/API/RESTBucketDELETEtagging.html',
            'parameters' => array(
                'Bucket' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'uri',
                ),
            ),
        ),
        'DeleteBucketWebsite' => array(
            'httpMethod' => 'DELETE',
            'uri' => '/{Bucket}?website',
            'class' => 'Aws\\S3\\Command\\S3Command',
            'responseClass' => 'DeleteBucketWebsiteOutput',
            'responseType' => 'model',
            'documentationUrl' => 'http://docs.aws.amazon.com/AmazonS3/latest/API/RESTBucketDELETEwebsite.html',
            'parameters' => array(
                'Bucket' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'uri',
                ),
            ),
        ),
        'DeleteObject' => array(
            'httpMethod' => 'DELETE',
            'uri' => '/{Bucket}{/Key*}',
            'class' => 'Aws\\S3\\Command\\S3Command',
            'responseClass' => 'DeleteObjectOutput',
            'responseType' => 'model',
            'documentationUrl' => 'http://docs.aws.amazon.com/AmazonS3/latest/API/RESTObjectDELETE.html',
            'parameters' => array(
                'Bucket' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'uri',
                ),
                'Key' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'uri',
                    'minLength' => 1,
                    'filters' => array(
                        'Aws\\S3\\S3Client::explodeKey',
                    ),
                ),
                'MFA' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-mfa',
                ),
                'VersionId' => array(
                    'type' => 'string',
                    'location' => 'query',
                    'sentAs' => 'versionId',
                ),
                'RequestPayer' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-request-payer',
                ),
            ),
        ),
        'DeleteObjects' => array(
            'httpMethod' => 'POST',
            'uri' => '/{Bucket}?delete',
            'class' => 'Aws\\S3\\Command\\S3Command',
            'responseClass' => 'DeleteObjectsOutput',
            'responseType' => 'model',
            'documentationUrl' => 'http://docs.aws.amazon.com/AmazonS3/latest/API/multiobjectdeleteapi.html',
            'data' => array(
                'xmlRoot' => array(
                    'name' => 'Delete',
                    'namespaces' => array(
                        'http://s3.amazonaws.com/doc/2006-03-01/',
                    ),
                ),
                'contentMd5' => true,
            ),
            'parameters' => array(
                'Bucket' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'uri',
                ),
                'Objects' => array(
                    'required' => true,
                    'type' => 'array',
                    'location' => 'xml',
                    'data' => array(
                        'xmlFlattened' => true,
                    ),
                    'items' => array(
                        'name' => 'ObjectIdentifier',
                        'type' => 'object',
                        'sentAs' => 'Object',
                        'properties' => array(
                            'Key' => array(
                                'required' => true,
                                'type' => 'string',
                                'minLength' => 1,
                            ),
                            'VersionId' => array(
                                'type' => 'string',
                            ),
                        ),
                    ),
                ),
                'Quiet' => array(
                    'type' => 'boolean',
                    'format' => 'boolean-string',
                    'location' => 'xml',
                ),
                'MFA' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-mfa',
                ),
                'RequestPayer' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-request-payer',
                ),
                'command.expects' => array(
                    'static' => true,
                    'default' => 'application/xml',
                ),
            ),
        ),
        'GetBucketAcl' => array(
            'httpMethod' => 'GET',
            'uri' => '/{Bucket}?acl',
            'class' => 'Aws\\S3\\Command\\S3Command',
            'responseClass' => 'GetBucketAclOutput',
            'responseType' => 'model',
            'documentationUrl' => 'http://docs.aws.amazon.com/AmazonS3/latest/API/RESTBucketGETacl.html',
            'parameters' => array(
                'Bucket' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'uri',
                ),
                'command.expects' => array(
                    'static' => true,
                    'default' => 'application/xml',
                ),
            ),
        ),
        'GetBucketCors' => array(
            'httpMethod' => 'GET',
            'uri' => '/{Bucket}?cors',
            'class' => 'Aws\\S3\\Command\\S3Command',
            'responseClass' => 'GetBucketCorsOutput',
            'responseType' => 'model',
            'documentationUrl' => 'http://docs.aws.amazon.com/AmazonS3/latest/API/RESTBucketGETcors.html',
            'parameters' => array(
                'Bucket' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'uri',
                ),
                'command.expects' => array(
                    'static' => true,
                    'default' => 'application/xml',
                ),
            ),
        ),
        'GetBucketLifecycle' => array(
            'httpMethod' => 'GET',
            'uri' => '/{Bucket}?lifecycle',
            'class' => 'Aws\\S3\\Command\\S3Command',
            'responseClass' => 'GetBucketLifecycleOutput',
            'responseType' => 'model',
            'documentationUrl' => 'http://docs.aws.amazon.com/AmazonS3/latest/API/RESTBucketGETlifecycle.html',
            'parameters' => array(
                'Bucket' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'uri',
                ),
                'command.expects' => array(
                    'static' => true,
                    'default' => 'application/xml',
                ),
            ),
        ),
        'GetBucketLifecycleConfiguration' => array(
            'httpMethod' => 'GET',
            'uri' => '/{Bucket}?lifecycle',
            'class' => 'Aws\\S3\\Command\\S3Command',
            'responseClass' => 'GetBucketLifecycleConfigurationOutput',
            'responseType' => 'model',
            'parameters' => array(
                'Bucket' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'uri',
                ),
                'command.expects' => array(
                    'static' => true,
                    'default' => 'application/xml',
                ),
            ),
        ),
        'GetBucketLocation' => array(
            'httpMethod' => 'GET',
            'uri' => '/{Bucket}?location',
            'class' => 'Aws\\S3\\Command\\S3Command',
            'responseClass' => 'GetBucketLocationOutput',
            'responseType' => 'model',
            'documentationUrl' => 'http://docs.aws.amazon.com/AmazonS3/latest/API/RESTBucketGETlocation.html',
            'parameters' => array(
                'Bucket' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'uri',
                ),
            ),
        ),
        'GetBucketLogging' => array(
            'httpMethod' => 'GET',
            'uri' => '/{Bucket}?logging',
            'class' => 'Aws\\S3\\Command\\S3Command',
            'responseClass' => 'GetBucketLoggingOutput',
            'responseType' => 'model',
            'documentationUrl' => 'http://docs.aws.amazon.com/AmazonS3/latest/API/RESTBucketGETlogging.html',
            'parameters' => array(
                'Bucket' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'uri',
                ),
                'command.expects' => array(
                    'static' => true,
                    'default' => 'application/xml',
                ),
            ),
        ),
        'GetBucketNotification' => array(
            'httpMethod' => 'GET',
            'uri' => '/{Bucket}?notification',
            'class' => 'Aws\\S3\\Command\\S3Command',
            'responseClass' => 'NotificationConfigurationDeprecated',
            'responseType' => 'model',
            'documentationUrl' => 'http://docs.aws.amazon.com/AmazonS3/latest/API/RESTBucketGETnotification.html',
            'parameters' => array(
                'Bucket' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'uri',
                ),
                'command.expects' => array(
                    'static' => true,
                    'default' => 'application/xml',
                ),
            ),
        ),
        'GetBucketNotificationConfiguration' => array(
            'httpMethod' => 'GET',
            'uri' => '/{Bucket}?notification',
            'class' => 'Aws\\S3\\Command\\S3Command',
            'responseClass' => 'NotificationConfiguration',
            'responseType' => 'model',
            'parameters' => array(
                'Bucket' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'uri',
                ),
                'command.expects' => array(
                    'static' => true,
                    'default' => 'application/xml',
                ),
            ),
        ),
        'GetBucketPolicy' => array(
            'httpMethod' => 'GET',
            'uri' => '/{Bucket}?policy',
            'class' => 'Aws\\S3\\Command\\S3Command',
            'responseClass' => 'GetBucketPolicyOutput',
            'responseType' => 'model',
            'documentationUrl' => 'http://docs.aws.amazon.com/AmazonS3/latest/API/RESTBucketGETpolicy.html',
            'parameters' => array(
                'Bucket' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'uri',
                ),
            ),
        ),
        'GetBucketReplication' => array(
            'httpMethod' => 'GET',
            'uri' => '/{Bucket}?replication',
            'class' => 'Aws\\S3\\Command\\S3Command',
            'responseClass' => 'GetBucketReplicationOutput',
            'responseType' => 'model',
            'parameters' => array(
                'Bucket' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'uri',
                ),
                'command.expects' => array(
                    'static' => true,
                    'default' => 'application/xml',
                ),
            ),
        ),
        'GetBucketRequestPayment' => array(
            'httpMethod' => 'GET',
            'uri' => '/{Bucket}?requestPayment',
            'class' => 'Aws\\S3\\Command\\S3Command',
            'responseClass' => 'GetBucketRequestPaymentOutput',
            'responseType' => 'model',
            'documentationUrl' => 'http://docs.aws.amazon.com/AmazonS3/latest/API/RESTrequestPaymentGET.html',
            'parameters' => array(
                'Bucket' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'uri',
                ),
                'command.expects' => array(
                    'static' => true,
                    'default' => 'application/xml',
                ),
            ),
        ),
        'GetBucketTagging' => array(
            'httpMethod' => 'GET',
            'uri' => '/{Bucket}?tagging',
            'class' => 'Aws\\S3\\Command\\S3Command',
            'responseClass' => 'GetBucketTaggingOutput',
            'responseType' => 'model',
            'documentationUrl' => 'http://docs.aws.amazon.com/AmazonS3/latest/API/RESTBucketGETtagging.html',
            'parameters' => array(
                'Bucket' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'uri',
                ),
                'command.expects' => array(
                    'static' => true,
                    'default' => 'application/xml',
                ),
            ),
        ),
        'GetBucketVersioning' => array(
            'httpMethod' => 'GET',
            'uri' => '/{Bucket}?versioning',
            'class' => 'Aws\\S3\\Command\\S3Command',
            'responseClass' => 'GetBucketVersioningOutput',
            'responseType' => 'model',
            'documentationUrl' => 'http://docs.aws.amazon.com/AmazonS3/latest/API/RESTBucketGETversioningStatus.html',
            'parameters' => array(
                'Bucket' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'uri',
                ),
                'command.expects' => array(
                    'static' => true,
                    'default' => 'application/xml',
                ),
            ),
        ),
        'GetBucketWebsite' => array(
            'httpMethod' => 'GET',
            'uri' => '/{Bucket}?website',
            'class' => 'Aws\\S3\\Command\\S3Command',
            'responseClass' => 'GetBucketWebsiteOutput',
            'responseType' => 'model',
            'documentationUrl' => 'http://docs.aws.amazon.com/AmazonS3/latest/API/RESTBucketGETwebsite.html',
            'parameters' => array(
                'Bucket' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'uri',
                ),
                'command.expects' => array(
                    'static' => true,
                    'default' => 'application/xml',
                ),
            ),
        ),
        'GetObject' => array(
            'httpMethod' => 'GET',
            'uri' => '/{Bucket}{/Key*}',
            'class' => 'Aws\\S3\\Command\\S3Command',
            'responseClass' => 'GetObjectOutput',
            'responseType' => 'model',
            'documentationUrl' => 'http://docs.aws.amazon.com/AmazonS3/latest/API/RESTObjectGET.html',
            'parameters' => array(
                'Bucket' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'uri',
                ),
                'IfMatch' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'If-Match',
                ),
                'IfModifiedSince' => array(
                    'type' => array(
                        'object',
                        'string',
                        'integer',
                    ),
                    'format' => 'date-time-http',
                    'location' => 'header',
                    'sentAs' => 'If-Modified-Since',
                ),
                'IfNoneMatch' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'If-None-Match',
                ),
                'IfUnmodifiedSince' => array(
                    'type' => array(
                        'object',
                        'string',
                        'integer',
                    ),
                    'format' => 'date-time-http',
                    'location' => 'header',
                    'sentAs' => 'If-Unmodified-Since',
                ),
                'Key' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'uri',
                    'minLength' => 1,
                    'filters' => array(
                        'Aws\\S3\\S3Client::explodeKey',
                    ),
                ),
                'Range' => array(
                    'type' => 'string',
                    'location' => 'header',
                ),
                'ResponseCacheControl' => array(
                    'type' => 'string',
                    'location' => 'query',
                    'sentAs' => 'response-cache-control',
                ),
                'ResponseContentDisposition' => array(
                    'type' => 'string',
                    'location' => 'query',
                    'sentAs' => 'response-content-disposition',
                ),
                'ResponseContentEncoding' => array(
                    'type' => 'string',
                    'location' => 'query',
                    'sentAs' => 'response-content-encoding',
                ),
                'ResponseContentLanguage' => array(
                    'type' => 'string',
                    'location' => 'query',
                    'sentAs' => 'response-content-language',
                ),
                'ResponseContentType' => array(
                    'type' => 'string',
                    'location' => 'query',
                    'sentAs' => 'response-content-type',
                ),
                'ResponseExpires' => array(
                    'type' => array(
                        'object',
                        'string',
                        'integer',
                    ),
                    'format' => 'date-time-http',
                    'location' => 'query',
                    'sentAs' => 'response-expires',
                ),
                'VersionId' => array(
                    'type' => 'string',
                    'location' => 'query',
                    'sentAs' => 'versionId',
                ),
                'SSECustomerAlgorithm' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-server-side-encryption-customer-algorithm',
                ),
                'SSECustomerKey' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-server-side-encryption-customer-key',
                ),
                'SSECustomerKeyMD5' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-server-side-encryption-customer-key-MD5',
                ),
                'RequestPayer' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-request-payer',
                ),
                'SaveAs' => array(
                    'location' => 'response_body',
                ),
            ),
            'errorResponses' => array(
                array(
                    'reason' => 'The specified key does not exist.',
                    'class' => 'NoSuchKeyException',
                ),
            ),
        ),
        'GetObjectAcl' => array(
            'httpMethod' => 'GET',
            'uri' => '/{Bucket}{/Key*}?acl',
            'class' => 'Aws\\S3\\Command\\S3Command',
            'responseClass' => 'GetObjectAclOutput',
            'responseType' => 'model',
            'documentationUrl' => 'http://docs.aws.amazon.com/AmazonS3/latest/API/RESTObjectGETacl.html',
            'parameters' => array(
                'Bucket' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'uri',
                ),
                'Key' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'uri',
                    'minLength' => 1,
                    'filters' => array(
                        'Aws\\S3\\S3Client::explodeKey',
                    ),
                ),
                'VersionId' => array(
                    'type' => 'string',
                    'location' => 'query',
                    'sentAs' => 'versionId',
                ),
                'RequestPayer' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-request-payer',
                ),
                'command.expects' => array(
                    'static' => true,
                    'default' => 'application/xml',
                ),
            ),
            'errorResponses' => array(
                array(
                    'reason' => 'The specified key does not exist.',
                    'class' => 'NoSuchKeyException',
                ),
            ),
        ),
        'GetObjectTorrent' => array(
            'httpMethod' => 'GET',
            'uri' => '/{Bucket}{/Key*}?torrent',
            'class' => 'Aws\\S3\\Command\\S3Command',
            'responseClass' => 'GetObjectTorrentOutput',
            'responseType' => 'model',
            'documentationUrl' => 'http://docs.aws.amazon.com/AmazonS3/latest/API/RESTObjectGETtorrent.html',
            'parameters' => array(
                'Bucket' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'uri',
                ),
                'Key' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'uri',
                    'minLength' => 1,
                    'filters' => array(
                        'Aws\\S3\\S3Client::explodeKey',
                    ),
                ),
                'RequestPayer' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-request-payer',
                ),
            ),
        ),
        'HeadBucket' => array(
            'httpMethod' => 'HEAD',
            'uri' => '/{Bucket}',
            'class' => 'Aws\\S3\\Command\\S3Command',
            'responseClass' => 'HeadBucketOutput',
            'responseType' => 'model',
            'documentationUrl' => 'http://docs.aws.amazon.com/AmazonS3/latest/API/RESTBucketHEAD.html',
            'parameters' => array(
                'Bucket' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'uri',
                ),
            ),
            'errorResponses' => array(
                array(
                    'reason' => 'The specified bucket does not exist.',
                    'class' => 'NoSuchBucketException',
                ),
            ),
        ),
        'HeadObject' => array(
            'httpMethod' => 'HEAD',
            'uri' => '/{Bucket}{/Key*}',
            'class' => 'Aws\\S3\\Command\\S3Command',
            'responseClass' => 'HeadObjectOutput',
            'responseType' => 'model',
            'documentationUrl' => 'http://docs.aws.amazon.com/AmazonS3/latest/API/RESTObjectHEAD.html',
            'parameters' => array(
                'Bucket' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'uri',
                ),
                'IfMatch' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'If-Match',
                ),
                'IfModifiedSince' => array(
                    'type' => array(
                        'object',
                        'string',
                        'integer',
                    ),
                    'format' => 'date-time-http',
                    'location' => 'header',
                    'sentAs' => 'If-Modified-Since',
                ),
                'IfNoneMatch' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'If-None-Match',
                ),
                'IfUnmodifiedSince' => array(
                    'type' => array(
                        'object',
                        'string',
                        'integer',
                    ),
                    'format' => 'date-time-http',
                    'location' => 'header',
                    'sentAs' => 'If-Unmodified-Since',
                ),
                'Key' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'uri',
                    'minLength' => 1,
                    'filters' => array(
                        'Aws\\S3\\S3Client::explodeKey',
                    ),
                ),
                'Range' => array(
                    'type' => 'string',
                    'location' => 'header',
                ),
                'VersionId' => array(
                    'type' => 'string',
                    'location' => 'query',
                    'sentAs' => 'versionId',
                ),
                'SSECustomerAlgorithm' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-server-side-encryption-customer-algorithm',
                ),
                'SSECustomerKey' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-server-side-encryption-customer-key',
                ),
                'SSECustomerKeyMD5' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-server-side-encryption-customer-key-MD5',
                ),
                'RequestPayer' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-request-payer',
                ),
            ),
            'errorResponses' => array(
                array(
                    'reason' => 'The specified key does not exist.',
                    'class' => 'NoSuchKeyException',
                ),
            ),
        ),
        'ListBuckets' => array(
            'httpMethod' => 'GET',
            'uri' => '/',
            'class' => 'Aws\\S3\\Command\\S3Command',
            'responseClass' => 'ListBucketsOutput',
            'responseType' => 'model',
            'documentationUrl' => 'http://docs.aws.amazon.com/AmazonS3/latest/API/RESTServiceGET.html',
            'parameters' => array(
                'command.expects' => array(
                    'static' => true,
                    'default' => 'application/xml',
                ),
            ),
        ),
        'ListMultipartUploads' => array(
            'httpMethod' => 'GET',
            'uri' => '/{Bucket}?uploads',
            'class' => 'Aws\\S3\\Command\\S3Command',
            'responseClass' => 'ListMultipartUploadsOutput',
            'responseType' => 'model',
            'documentationUrl' => 'http://docs.aws.amazon.com/AmazonS3/latest/API/mpUploadListMPUpload.html',
            'parameters' => array(
                'Bucket' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'uri',
                ),
                'Delimiter' => array(
                    'type' => 'string',
                    'location' => 'query',
                    'sentAs' => 'delimiter',
                ),
                'EncodingType' => array(
                    'type' => 'string',
                    'location' => 'query',
                    'sentAs' => 'encoding-type',
                ),
                'KeyMarker' => array(
                    'type' => 'string',
                    'location' => 'query',
                    'sentAs' => 'key-marker',
                ),
                'MaxUploads' => array(
                    'type' => 'numeric',
                    'location' => 'query',
                    'sentAs' => 'max-uploads',
                ),
                'Prefix' => array(
                    'type' => 'string',
                    'location' => 'query',
                    'sentAs' => 'prefix',
                ),
                'UploadIdMarker' => array(
                    'type' => 'string',
                    'location' => 'query',
                    'sentAs' => 'upload-id-marker',
                ),
                'command.expects' => array(
                    'static' => true,
                    'default' => 'application/xml',
                ),
            ),
        ),
        'ListObjectVersions' => array(
            'httpMethod' => 'GET',
            'uri' => '/{Bucket}?versions',
            'class' => 'Aws\\S3\\Command\\S3Command',
            'responseClass' => 'ListObjectVersionsOutput',
            'responseType' => 'model',
            'documentationUrl' => 'http://docs.aws.amazon.com/AmazonS3/latest/API/RESTBucketGETVersion.html',
            'parameters' => array(
                'Bucket' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'uri',
                ),
                'Delimiter' => array(
                    'type' => 'string',
                    'location' => 'query',
                    'sentAs' => 'delimiter',
                ),
                'EncodingType' => array(
                    'type' => 'string',
                    'location' => 'query',
                    'sentAs' => 'encoding-type',
                ),
                'KeyMarker' => array(
                    'type' => 'string',
                    'location' => 'query',
                    'sentAs' => 'key-marker',
                ),
                'MaxKeys' => array(
                    'type' => 'numeric',
                    'location' => 'query',
                    'sentAs' => 'max-keys',
                ),
                'Prefix' => array(
                    'type' => 'string',
                    'location' => 'query',
                    'sentAs' => 'prefix',
                ),
                'VersionIdMarker' => array(
                    'type' => 'string',
                    'location' => 'query',
                    'sentAs' => 'version-id-marker',
                ),
                'command.expects' => array(
                    'static' => true,
                    'default' => 'application/xml',
                ),
            ),
        ),
        'ListObjects' => array(
            'httpMethod' => 'GET',
            'uri' => '/{Bucket}',
            'class' => 'Aws\\S3\\Command\\S3Command',
            'responseClass' => 'ListObjectsOutput',
            'responseType' => 'model',
            'documentationUrl' => 'http://docs.aws.amazon.com/AmazonS3/latest/API/RESTBucketGET.html',
            'parameters' => array(
                'Bucket' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'uri',
                ),
                'Delimiter' => array(
                    'type' => 'string',
                    'location' => 'query',
                    'sentAs' => 'delimiter',
                ),
                'EncodingType' => array(
                    'type' => 'string',
                    'location' => 'query',
                    'sentAs' => 'encoding-type',
                ),
                'Marker' => array(
                    'type' => 'string',
                    'location' => 'query',
                    'sentAs' => 'marker',
                ),
                'MaxKeys' => array(
                    'type' => 'numeric',
                    'location' => 'query',
                    'sentAs' => 'max-keys',
                ),
                'Prefix' => array(
                    'type' => 'string',
                    'location' => 'query',
                    'sentAs' => 'prefix',
                ),
                'command.expects' => array(
                    'static' => true,
                    'default' => 'application/xml',
                ),
            ),
            'errorResponses' => array(
                array(
                    'reason' => 'The specified bucket does not exist.',
                    'class' => 'NoSuchBucketException',
                ),
            ),
        ),
        'ListParts' => array(
            'httpMethod' => 'GET',
            'uri' => '/{Bucket}{/Key*}',
            'class' => 'Aws\\S3\\Command\\S3Command',
            'responseClass' => 'ListPartsOutput',
            'responseType' => 'model',
            'documentationUrl' => 'http://docs.aws.amazon.com/AmazonS3/latest/API/mpUploadListParts.html',
            'parameters' => array(
                'Bucket' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'uri',
                ),
                'Key' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'uri',
                    'minLength' => 1,
                    'filters' => array(
                        'Aws\\S3\\S3Client::explodeKey',
                    ),
                ),
                'MaxParts' => array(
                    'type' => 'numeric',
                    'location' => 'query',
                    'sentAs' => 'max-parts',
                ),
                'PartNumberMarker' => array(
                    'type' => 'numeric',
                    'location' => 'query',
                    'sentAs' => 'part-number-marker',
                ),
                'UploadId' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'query',
                    'sentAs' => 'uploadId',
                ),
                'RequestPayer' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-request-payer',
                ),
                'command.expects' => array(
                    'static' => true,
                    'default' => 'application/xml',
                ),
            ),
        ),
        'PutBucketAcl' => array(
            'httpMethod' => 'PUT',
            'uri' => '/{Bucket}?acl',
            'class' => 'Aws\\S3\\Command\\S3Command',
            'responseClass' => 'PutBucketAclOutput',
            'responseType' => 'model',
            'documentationUrl' => 'http://docs.aws.amazon.com/AmazonS3/latest/API/RESTBucketPUTacl.html',
            'data' => array(
                'xmlRoot' => array(
                    'name' => 'AccessControlPolicy',
                    'namespaces' => array(
                        'http://s3.amazonaws.com/doc/2006-03-01/',
                    ),
                ),
            ),
            'parameters' => array(
                'ACL' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-acl',
                ),
                'Grants' => array(
                    'type' => 'array',
                    'location' => 'xml',
                    'sentAs' => 'AccessControlList',
                    'items' => array(
                        'name' => 'Grant',
                        'type' => 'object',
                        'properties' => array(
                            'Grantee' => array(
                                'type' => 'object',
                                'properties' => array(
                                    'DisplayName' => array(
                                        'type' => 'string',
                                    ),
                                    'EmailAddress' => array(
                                        'type' => 'string',
                                    ),
                                    'ID' => array(
                                        'type' => 'string',
                                    ),
                                    'Type' => array(
                                        'required' => true,
                                        'type' => 'string',
                                        'sentAs' => 'xsi:type',
                                        'data' => array(
                                            'xmlAttribute' => true,
                                            'xmlNamespace' => 'http://www.w3.org/2001/XMLSchema-instance',
                                        ),
                                    ),
                                    'URI' => array(
                                        'type' => 'string',
                                    ),
                                ),
                            ),
                            'Permission' => array(
                                'type' => 'string',
                            ),
                        ),
                    ),
                ),
                'Owner' => array(
                    'type' => 'object',
                    'location' => 'xml',
                    'properties' => array(
                        'DisplayName' => array(
                            'type' => 'string',
                        ),
                        'ID' => array(
                            'type' => 'string',
                        ),
                    ),
                ),
                'Bucket' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'uri',
                ),
                'GrantFullControl' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-grant-full-control',
                ),
                'GrantRead' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-grant-read',
                ),
                'GrantReadACP' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-grant-read-acp',
                ),
                'GrantWrite' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-grant-write',
                ),
                'GrantWriteACP' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-grant-write-acp',
                ),
                'ACP' => array(
                    'type' => 'object',
                    'additionalProperties' => true,
                ),
            ),
        ),
        'PutBucketCors' => array(
            'httpMethod' => 'PUT',
            'uri' => '/{Bucket}?cors',
            'class' => 'Aws\\S3\\Command\\S3Command',
            'responseClass' => 'PutBucketCorsOutput',
            'responseType' => 'model',
            'documentationUrl' => 'http://docs.aws.amazon.com/AmazonS3/latest/API/RESTBucketPUTcors.html',
            'data' => array(
                'xmlRoot' => array(
                    'name' => 'CORSConfiguration',
                    'namespaces' => array(
                        'http://s3.amazonaws.com/doc/2006-03-01/',
                    ),
                ),
                'contentMd5' => true,
            ),
            'parameters' => array(
                'Bucket' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'uri',
                ),
                'CORSRules' => array(
                    'required' => true,
                    'type' => 'array',
                    'location' => 'xml',
                    'data' => array(
                        'xmlFlattened' => true,
                    ),
                    'items' => array(
                        'name' => 'CORSRule',
                        'type' => 'object',
                        'sentAs' => 'CORSRule',
                        'properties' => array(
                            'AllowedHeaders' => array(
                                'type' => 'array',
                                'data' => array(
                                    'xmlFlattened' => true,
                                ),
                                'items' => array(
                                    'name' => 'AllowedHeader',
                                    'type' => 'string',
                                    'sentAs' => 'AllowedHeader',
                                ),
                            ),
                            'AllowedMethods' => array(
                                'required' => true,
                                'type' => 'array',
                                'data' => array(
                                    'xmlFlattened' => true,
                                ),
                                'items' => array(
                                    'name' => 'AllowedMethod',
                                    'type' => 'string',
                                    'sentAs' => 'AllowedMethod',
                                ),
                            ),
                            'AllowedOrigins' => array(
                                'required' => true,
                                'type' => 'array',
                                'data' => array(
                                    'xmlFlattened' => true,
                                ),
                                'items' => array(
                                    'name' => 'AllowedOrigin',
                                    'type' => 'string',
                                    'sentAs' => 'AllowedOrigin',
                                ),
                            ),
                            'ExposeHeaders' => array(
                                'type' => 'array',
                                'data' => array(
                                    'xmlFlattened' => true,
                                ),
                                'items' => array(
                                    'name' => 'ExposeHeader',
                                    'type' => 'string',
                                    'sentAs' => 'ExposeHeader',
                                ),
                            ),
                            'MaxAgeSeconds' => array(
                                'type' => 'numeric',
                            ),
                        ),
                    ),
                ),
            ),
        ),
        'PutBucketLifecycle' => array(
            'httpMethod' => 'PUT',
            'uri' => '/{Bucket}?lifecycle',
            'class' => 'Aws\\S3\\Command\\S3Command',
            'responseClass' => 'PutBucketLifecycleOutput',
            'responseType' => 'model',
            'documentationUrl' => 'http://docs.aws.amazon.com/AmazonS3/latest/API/RESTBucketPUTlifecycle.html',
            'data' => array(
                'xmlRoot' => array(
                    'name' => 'LifecycleConfiguration',
                    'namespaces' => array(
                        'http://s3.amazonaws.com/doc/2006-03-01/',
                    ),
                ),
                'contentMd5' => true,
            ),
            'parameters' => array(
                'Bucket' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'uri',
                ),
                'Rules' => array(
                    'required' => true,
                    'type' => 'array',
                    'location' => 'xml',
                    'data' => array(
                        'xmlFlattened' => true,
                    ),
                    'items' => array(
                        'name' => 'Rule',
                        'type' => 'object',
                        'sentAs' => 'Rule',
                        'properties' => array(
                            'Expiration' => array(
                                'type' => 'object',
                                'properties' => array(
                                    'Date' => array(
                                        'type' => array(
                                            'object',
                                            'string',
                                            'integer',
                                        ),
                                        'format' => 'date-time',
                                    ),
                                    'Days' => array(
                                        'type' => 'numeric',
                                    ),
                                ),
                            ),
                            'ID' => array(
                                'type' => 'string',
                            ),
                            'Prefix' => array(
                                'required' => true,
                                'type' => 'string',
                            ),
                            'Status' => array(
                                'required' => true,
                                'type' => 'string',
                            ),
                            'Transition' => array(
                                'type' => 'object',
                                'properties' => array(
                                    'Date' => array(
                                        'type' => array(
                                            'object',
                                            'string',
                                            'integer',
                                        ),
                                        'format' => 'date-time',
                                    ),
                                    'Days' => array(
                                        'type' => 'numeric',
                                    ),
                                    'StorageClass' => array(
                                        'type' => 'string',
                                    ),
                                ),
                            ),
                            'NoncurrentVersionTransition' => array(
                                'type' => 'object',
                                'properties' => array(
                                    'NoncurrentDays' => array(
                                        'type' => 'numeric',
                                    ),
                                    'StorageClass' => array(
                                        'type' => 'string',
                                    ),
                                ),
                            ),
                            'NoncurrentVersionExpiration' => array(
                                'type' => 'object',
                                'properties' => array(
                                    'NoncurrentDays' => array(
                                        'type' => 'numeric',
                                    ),
                                ),
                            ),
                        ),
                    ),
                ),
            ),
        ),
        'PutBucketLifecycleConfiguration' => array(
            'httpMethod' => 'PUT',
            'uri' => '/{Bucket}?lifecycle',
            'class' => 'Aws\\S3\\Command\\S3Command',
            'responseClass' => 'PutBucketLifecycleConfigurationOutput',
            'responseType' => 'model',
            'data' => array(
                'xmlRoot' => array(
                    'name' => 'LifecycleConfiguration',
                    'namespaces' => array(
                        'http://s3.amazonaws.com/doc/2006-03-01/',
                    ),
                ),
            ),
            'parameters' => array(
                'Bucket' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'uri',
                ),
                'Rules' => array(
                    'required' => true,
                    'type' => 'array',
                    'location' => 'xml',
                    'data' => array(
                        'xmlFlattened' => true,
                    ),
                    'items' => array(
                        'name' => 'LifecycleRule',
                        'type' => 'object',
                        'sentAs' => 'Rule',
                        'properties' => array(
                            'Expiration' => array(
                                'type' => 'object',
                                'properties' => array(
                                    'Date' => array(
                                        'type' => array(
                                            'object',
                                            'string',
                                            'integer',
                                        ),
                                        'format' => 'date-time-http',
                                    ),
                                    'Days' => array(
                                        'type' => 'numeric',
                                    ),
                                ),
                            ),
                            'ID' => array(
                                'type' => 'string',
                            ),
                            'Prefix' => array(
                                'required' => true,
                                'type' => 'string',
                            ),
                            'Status' => array(
                                'required' => true,
                                'type' => 'string',
                            ),
                            'Transitions' => array(
                                'type' => 'array',
                                'data' => array(
                                    'xmlFlattened' => true,
                                ),
                                'items' => array(
                                    'name' => 'Transition',
                                    'type' => 'object',
                                    'sentAs' => 'Transition',
                                    'properties' => array(
                                        'Date' => array(
                                            'type' => array(
                                                'object',
                                                'string',
                                                'integer',
                                            ),
                                            'format' => 'date-time-http',
                                        ),
                                        'Days' => array(
                                            'type' => 'numeric',
                                        ),
                                        'StorageClass' => array(
                                            'type' => 'string',
                                        ),
                                    ),
                                ),
                            ),
                            'NoncurrentVersionTransitions' => array(
                                'type' => 'array',
                                'data' => array(
                                    'xmlFlattened' => true,
                                ),
                                'items' => array(
                                    'name' => 'NoncurrentVersionTransition',
                                    'type' => 'object',
                                    'sentAs' => 'NoncurrentVersionTransition',
                                    'properties' => array(
                                        'NoncurrentDays' => array(
                                            'type' => 'numeric',
                                        ),
                                        'StorageClass' => array(
                                            'type' => 'string',
                                        ),
                                    ),
                                ),
                            ),
                            'NoncurrentVersionExpiration' => array(
                                'type' => 'object',
                                'properties' => array(
                                    'NoncurrentDays' => array(
                                        'type' => 'numeric',
                                    ),
                                ),
                            ),
                        ),
                    ),
                ),
            ),
        ),
        'PutBucketLogging' => array(
            'httpMethod' => 'PUT',
            'uri' => '/{Bucket}?logging',
            'class' => 'Aws\\S3\\Command\\S3Command',
            'responseClass' => 'PutBucketLoggingOutput',
            'responseType' => 'model',
            'documentationUrl' => 'http://docs.aws.amazon.com/AmazonS3/latest/API/RESTBucketPUTlogging.html',
            'data' => array(
                'xmlRoot' => array(
                    'name' => 'BucketLoggingStatus',
                    'namespaces' => array(
                        'http://s3.amazonaws.com/doc/2006-03-01/',
                    ),
                ),
                'xmlAllowEmpty' => true,
            ),
            'parameters' => array(
                'Bucket' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'uri',
                ),
                'LoggingEnabled' => array(
                    'type' => 'object',
                    'location' => 'xml',
                    'properties' => array(
                        'TargetBucket' => array(
                            'type' => 'string',
                        ),
                        'TargetGrants' => array(
                            'type' => 'array',
                            'items' => array(
                                'name' => 'Grant',
                                'type' => 'object',
                                'properties' => array(
                                    'Grantee' => array(
                                        'type' => 'object',
                                        'properties' => array(
                                            'DisplayName' => array(
                                                'type' => 'string',
                                            ),
                                            'EmailAddress' => array(
                                                'type' => 'string',
                                            ),
                                            'ID' => array(
                                                'type' => 'string',
                                            ),
                                            'Type' => array(
                                                'required' => true,
                                                'type' => 'string',
                                                'sentAs' => 'xsi:type',
                                                'data' => array(
                                                    'xmlAttribute' => true,
                                                    'xmlNamespace' => 'http://www.w3.org/2001/XMLSchema-instance',
                                                ),
                                            ),
                                            'URI' => array(
                                                'type' => 'string',
                                            ),
                                        ),
                                    ),
                                    'Permission' => array(
                                        'type' => 'string',
                                    ),
                                ),
                            ),
                        ),
                        'TargetPrefix' => array(
                            'type' => 'string',
                        ),
                    ),
                ),
            ),
        ),
        'PutBucketNotification' => array(
            'httpMethod' => 'PUT',
            'uri' => '/{Bucket}?notification',
            'class' => 'Aws\\S3\\Command\\S3Command',
            'responseClass' => 'PutBucketNotificationOutput',
            'responseType' => 'model',
            'documentationUrl' => 'http://docs.aws.amazon.com/AmazonS3/latest/API/RESTBucketPUTnotification.html',
            'data' => array(
                'xmlRoot' => array(
                    'name' => 'NotificationConfiguration',
                    'namespaces' => array(
                        'http://s3.amazonaws.com/doc/2006-03-01/',
                    ),
                ),
                'xmlAllowEmpty' => true,
            ),
            'parameters' => array(
                'Bucket' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'uri',
                ),
                'TopicConfiguration' => array(
                    'type' => 'object',
                    'location' => 'xml',
                    'properties' => array(
                        'Id' => array(
                            'type' => 'string',
                        ),
                        'Events' => array(
                            'type' => 'array',
                            'data' => array(
                                'xmlFlattened' => true,
                            ),
                            'items' => array(
                                'name' => 'Event',
                                'type' => 'string',
                            ),
                        ),
                        'Event' => array(
                            'type' => 'string',
                        ),
                        'Topic' => array(
                            'type' => 'string',
                        ),
                    ),
                ),
                'QueueConfiguration' => array(
                    'type' => 'object',
                    'location' => 'xml',
                    'properties' => array(
                        'Id' => array(
                            'type' => 'string',
                        ),
                        'Event' => array(
                            'type' => 'string',
                        ),
                        'Events' => array(
                            'type' => 'array',
                            'data' => array(
                                'xmlFlattened' => true,
                            ),
                            'items' => array(
                                'name' => 'Event',
                                'type' => 'string',
                            ),
                        ),
                        'Queue' => array(
                            'type' => 'string',
                        ),
                    ),
                ),
                'CloudFunctionConfiguration' => array(
                    'type' => 'object',
                    'location' => 'xml',
                    'properties' => array(
                        'Id' => array(
                            'type' => 'string',
                        ),
                        'Event' => array(
                            'type' => 'string',
                        ),
                        'Events' => array(
                            'type' => 'array',
                            'data' => array(
                                'xmlFlattened' => true,
                            ),
                            'items' => array(
                                'name' => 'Event',
                                'type' => 'string',
                            ),
                        ),
                        'CloudFunction' => array(
                            'type' => 'string',
                        ),
                        'InvocationRole' => array(
                            'type' => 'string',
                        ),
                    ),
                ),
            ),
        ),
        'PutBucketNotificationConfiguration' => array(
            'httpMethod' => 'PUT',
            'uri' => '/{Bucket}?notification',
            'class' => 'Aws\\S3\\Command\\S3Command',
            'responseClass' => 'PutBucketNotificationConfigurationOutput',
            'responseType' => 'model',
            'data' => array(
                'xmlRoot' => array(
                    'name' => 'NotificationConfiguration',
                    'namespaces' => array(
                        'http://s3.amazonaws.com/doc/2006-03-01/',
                    ),
                ),
            ),
            'parameters' => array(
                'Bucket' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'uri',
                ),
                'TopicConfigurations' => array(
                    'type' => 'array',
                    'location' => 'xml',
                    'data' => array(
                        'xmlFlattened' => true,
                    ),
                    'items' => array(
                        'name' => 'TopicConfiguration',
                        'type' => 'object',
                        'sentAs' => 'TopicConfiguration',
                        'properties' => array(
                            'Id' => array(
                                'type' => 'string',
                            ),
                            'TopicArn' => array(
                                'required' => true,
                                'type' => 'string',
                                'sentAs' => 'Topic',
                            ),
                            'Events' => array(
                                'required' => true,
                                'type' => 'array',
                                'data' => array(
                                    'xmlFlattened' => true,
                                ),
                                'items' => array(
                                    'name' => 'Event',
                                    'type' => 'string',
                                    'sentAs' => 'Event',
                                ),
                            ),
                            'Filter' => array(
                                'type' => 'object',
                                'properties' => array(
                                    'Key' => array(
                                        'type' => 'object',
                                        'sentAs' => 'S3Key',
                                        'properties' => array(
                                            'FilterRules' => array(
                                                'type' => 'array',
                                                'data' => array(
                                                    'xmlFlattened' => true,
                                                ),
                                                'items' => array(
                                                    'name' => 'FilterRule',
                                                    'type' => 'object',
                                                    'sentAs' => 'FilterRule',
                                                    'properties' => array(
                                                        'Name' => array(
                                                            'type' => 'string',
                                                        ),
                                                        'Value' => array(
                                                            'type' => 'string',
                                                        ),
                                                    ),
                                                ),
                                            ),
                                        ),
                                    ),
                                ),
                            ),
                        ),
                    ),
                ),
                'QueueConfigurations' => array(
                    'type' => 'array',
                    'location' => 'xml',
                    'data' => array(
                        'xmlFlattened' => true,
                    ),
                    'items' => array(
                        'name' => 'QueueConfiguration',
                        'type' => 'object',
                        'sentAs' => 'QueueConfiguration',
                        'properties' => array(
                            'Id' => array(
                                'type' => 'string',
                            ),
                            'QueueArn' => array(
                                'required' => true,
                                'type' => 'string',
                                'sentAs' => 'Queue',
                            ),
                            'Events' => array(
                                'required' => true,
                                'type' => 'array',
                                'data' => array(
                                    'xmlFlattened' => true,
                                ),
                                'items' => array(
                                    'name' => 'Event',
                                    'type' => 'string',
                                    'sentAs' => 'Event',
                                ),
                            ),
                            'Filter' => array(
                                'type' => 'object',
                                'properties' => array(
                                    'Key' => array(
                                        'type' => 'object',
                                        'sentAs' => 'S3Key',
                                        'properties' => array(
                                            'FilterRules' => array(
                                                'type' => 'array',
                                                'data' => array(
                                                    'xmlFlattened' => true,
                                                ),
                                                'items' => array(
                                                    'name' => 'FilterRule',
                                                    'type' => 'object',
                                                    'sentAs' => 'FilterRule',
                                                    'properties' => array(
                                                        'Name' => array(
                                                            'type' => 'string',
                                                        ),
                                                        'Value' => array(
                                                            'type' => 'string',
                                                        ),
                                                    ),
                                                ),
                                            ),
                                        ),
                                    ),
                                ),
                            ),
                        ),
                    ),
                ),
                'LambdaFunctionConfigurations' => array(
                    'type' => 'array',
                    'location' => 'xml',
                    'data' => array(
                        'xmlFlattened' => true,
                    ),
                    'items' => array(
                        'name' => 'LambdaFunctionConfiguration',
                        'type' => 'object',
                        'sentAs' => 'CloudFunctionConfiguration',
                        'properties' => array(
                            'Id' => array(
                                'type' => 'string',
                            ),
                            'LambdaFunctionArn' => array(
                                'required' => true,
                                'type' => 'string',
                                'sentAs' => 'CloudFunction',
                            ),
                            'Events' => array(
                                'required' => true,
                                'type' => 'array',
                                'data' => array(
                                    'xmlFlattened' => true,
                                ),
                                'items' => array(
                                    'name' => 'Event',
                                    'type' => 'string',
                                    'sentAs' => 'Event',
                                ),
                            ),
                            'Filter' => array(
                                'type' => 'object',
                                'properties' => array(
                                    'Key' => array(
                                        'type' => 'object',
                                        'sentAs' => 'S3Key',
                                        'properties' => array(
                                            'FilterRules' => array(
                                                'type' => 'array',
                                                'data' => array(
                                                    'xmlFlattened' => true,
                                                ),
                                                'items' => array(
                                                    'name' => 'FilterRule',
                                                    'type' => 'object',
                                                    'sentAs' => 'FilterRule',
                                                    'properties' => array(
                                                        'Name' => array(
                                                            'type' => 'string',
                                                        ),
                                                        'Value' => array(
                                                            'type' => 'string',
                                                        ),
                                                    ),
                                                ),
                                            ),
                                        ),
                                    ),
                                ),
                            ),
                        ),
                    ),
                ),
            ),
        ),
        'PutBucketPolicy' => array(
            'httpMethod' => 'PUT',
            'uri' => '/{Bucket}?policy',
            'class' => 'Aws\\S3\\Command\\S3Command',
            'responseClass' => 'PutBucketPolicyOutput',
            'responseType' => 'model',
            'documentationUrl' => 'http://docs.aws.amazon.com/AmazonS3/latest/API/RESTBucketPUTpolicy.html',
            'data' => array(
                'xmlRoot' => array(
                    'name' => 'PutBucketPolicyRequest',
                    'namespaces' => array(
                        'http://s3.amazonaws.com/doc/2006-03-01/',
                    ),
                ),
            ),
            'parameters' => array(
                'Bucket' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'uri',
                ),
                'Policy' => array(
                    'required' => true,
                    'type' => array(
                        'string',
                        'object',
                    ),
                    'location' => 'body',
                ),
            ),
        ),
        'PutBucketReplication' => array(
            'httpMethod' => 'PUT',
            'uri' => '/{Bucket}?replication',
            'class' => 'Aws\\S3\\Command\\S3Command',
            'responseClass' => 'PutBucketReplicationOutput',
            'responseType' => 'model',
            'data' => array(
                'xmlRoot' => array(
                    'name' => 'ReplicationConfiguration',
                    'namespaces' => array(
                        'http://s3.amazonaws.com/doc/2006-03-01/',
                    ),
                ),
            ),
            'parameters' => array(
                'Bucket' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'uri',
                ),
                'Role' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'xml',
                ),
                'Rules' => array(
                    'required' => true,
                    'type' => 'array',
                    'location' => 'xml',
                    'data' => array(
                        'xmlFlattened' => true,
                    ),
                    'items' => array(
                        'name' => 'ReplicationRule',
                        'type' => 'object',
                        'sentAs' => 'Rule',
                        'properties' => array(
                            'ID' => array(
                                'type' => 'string',
                            ),
                            'Prefix' => array(
                                'required' => true,
                                'type' => 'string',
                            ),
                            'Status' => array(
                                'required' => true,
                                'type' => 'string',
                            ),
                            'Destination' => array(
                                'required' => true,
                                'type' => 'object',
                                'properties' => array(
                                    'Bucket' => array(
                                        'required' => true,
                                        'type' => 'string',
                                    ),
                                    'StorageClass' => array(
                                        'type' => 'string',
                                    ),
                                ),
                            ),
                        ),
                    ),
                ),
            ),
        ),
        'PutBucketRequestPayment' => array(
            'httpMethod' => 'PUT',
            'uri' => '/{Bucket}?requestPayment',
            'class' => 'Aws\\S3\\Command\\S3Command',
            'responseClass' => 'PutBucketRequestPaymentOutput',
            'responseType' => 'model',
            'documentationUrl' => 'http://docs.aws.amazon.com/AmazonS3/latest/API/RESTrequestPaymentPUT.html',
            'data' => array(
                'xmlRoot' => array(
                    'name' => 'RequestPaymentConfiguration',
                    'namespaces' => array(
                        'http://s3.amazonaws.com/doc/2006-03-01/',
                    ),
                ),
            ),
            'parameters' => array(
                'Bucket' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'uri',
                ),
                'Payer' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'xml',
                ),
            ),
        ),
        'PutBucketTagging' => array(
            'httpMethod' => 'PUT',
            'uri' => '/{Bucket}?tagging',
            'class' => 'Aws\\S3\\Command\\S3Command',
            'responseClass' => 'PutBucketTaggingOutput',
            'responseType' => 'model',
            'documentationUrl' => 'http://docs.aws.amazon.com/AmazonS3/latest/API/RESTBucketPUTtagging.html',
            'data' => array(
                'xmlRoot' => array(
                    'name' => 'Tagging',
                    'namespaces' => array(
                        'http://s3.amazonaws.com/doc/2006-03-01/',
                    ),
                ),
                'contentMd5' => true,
            ),
            'parameters' => array(
                'Bucket' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'uri',
                ),
                'TagSet' => array(
                    'required' => true,
                    'type' => 'array',
                    'location' => 'xml',
                    'items' => array(
                        'name' => 'Tag',
                        'type' => 'object',
                        'properties' => array(
                            'Key' => array(
                                'required' => true,
                                'type' => 'string',
                                'minLength' => 1,
                            ),
                            'Value' => array(
                                'required' => true,
                                'type' => 'string',
                            ),
                        ),
                    ),
                ),
            ),
        ),
        'PutBucketVersioning' => array(
            'httpMethod' => 'PUT',
            'uri' => '/{Bucket}?versioning',
            'class' => 'Aws\\S3\\Command\\S3Command',
            'responseClass' => 'PutBucketVersioningOutput',
            'responseType' => 'model',
            'documentationUrl' => 'http://docs.aws.amazon.com/AmazonS3/latest/API/RESTBucketPUTVersioningStatus.html',
            'data' => array(
                'xmlRoot' => array(
                    'name' => 'VersioningConfiguration',
                    'namespaces' => array(
                        'http://s3.amazonaws.com/doc/2006-03-01/',
                    ),
                ),
            ),
            'parameters' => array(
                'Bucket' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'uri',
                ),
                'MFA' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-mfa',
                ),
                'MFADelete' => array(
                    'type' => 'string',
                    'location' => 'xml',
                    'sentAs' => 'MfaDelete',
                ),
                'Status' => array(
                    'type' => 'string',
                    'location' => 'xml',
                ),
            ),
        ),
        'PutBucketWebsite' => array(
            'httpMethod' => 'PUT',
            'uri' => '/{Bucket}?website',
            'class' => 'Aws\\S3\\Command\\S3Command',
            'responseClass' => 'PutBucketWebsiteOutput',
            'responseType' => 'model',
            'documentationUrl' => 'http://docs.aws.amazon.com/AmazonS3/latest/API/RESTBucketPUTwebsite.html',
            'data' => array(
                'xmlRoot' => array(
                    'name' => 'WebsiteConfiguration',
                    'namespaces' => array(
                        'http://s3.amazonaws.com/doc/2006-03-01/',
                    ),
                ),
                'xmlAllowEmpty' => true,
            ),
            'parameters' => array(
                'Bucket' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'uri',
                ),
                'ErrorDocument' => array(
                    'type' => 'object',
                    'location' => 'xml',
                    'properties' => array(
                        'Key' => array(
                            'required' => true,
                            'type' => 'string',
                            'minLength' => 1,
                        ),
                    ),
                ),
                'IndexDocument' => array(
                    'type' => 'object',
                    'location' => 'xml',
                    'properties' => array(
                        'Suffix' => array(
                            'required' => true,
                            'type' => 'string',
                        ),
                    ),
                ),
                'RedirectAllRequestsTo' => array(
                    'type' => 'object',
                    'location' => 'xml',
                    'properties' => array(
                        'HostName' => array(
                            'required' => true,
                            'type' => 'string',
                        ),
                        'Protocol' => array(
                            'type' => 'string',
                        ),
                    ),
                ),
                'RoutingRules' => array(
                    'type' => 'array',
                    'location' => 'xml',
                    'items' => array(
                        'name' => 'RoutingRule',
                        'type' => 'object',
                        'properties' => array(
                            'Condition' => array(
                                'type' => 'object',
                                'properties' => array(
                                    'HttpErrorCodeReturnedEquals' => array(
                                        'type' => 'string',
                                    ),
                                    'KeyPrefixEquals' => array(
                                        'type' => 'string',
                                    ),
                                ),
                            ),
                            'Redirect' => array(
                                'required' => true,
                                'type' => 'object',
                                'properties' => array(
                                    'HostName' => array(
                                        'type' => 'string',
                                    ),
                                    'HttpRedirectCode' => array(
                                        'type' => 'string',
                                    ),
                                    'Protocol' => array(
                                        'type' => 'string',
                                    ),
                                    'ReplaceKeyPrefixWith' => array(
                                        'type' => 'string',
                                    ),
                                    'ReplaceKeyWith' => array(
                                        'type' => 'string',
                                    ),
                                ),
                            ),
                        ),
                    ),
                ),
            ),
        ),
        'PutObject' => array(
            'httpMethod' => 'PUT',
            'uri' => '/{Bucket}{/Key*}',
            'class' => 'Aws\\S3\\Command\\S3Command',
            'responseClass' => 'PutObjectOutput',
            'responseType' => 'model',
            'documentationUrl' => 'http://docs.aws.amazon.com/AmazonS3/latest/API/RESTObjectPUT.html',
            'data' => array(
                'xmlRoot' => array(
                    'name' => 'PutObjectRequest',
                    'namespaces' => array(
                        'http://s3.amazonaws.com/doc/2006-03-01/',
                    ),
                ),
            ),
            'parameters' => array(
                'ACL' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-acl',
                ),
                'Body' => array(
                    'type' => array(
                        'string',
                        'object',
                    ),
                    'location' => 'body',
                ),
                'Bucket' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'uri',
                ),
                'CacheControl' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'Cache-Control',
                ),
                'ContentDisposition' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'Content-Disposition',
                ),
                'ContentEncoding' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'Content-Encoding',
                ),
                'ContentLanguage' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'Content-Language',
                ),
                'ContentLength' => array(
                    'type' => 'numeric',
                    'location' => 'header',
                    'sentAs' => 'Content-Length',
                ),
                'ContentMD5' => array(
                    'type' => array(
                        'string',
                        'boolean',
                    ),
                    'location' => 'header',
                    'sentAs' => 'Content-MD5',
                ),
                'ContentType' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'Content-Type',
                ),
                'Expires' => array(
                    'type' => array(
                        'object',
                        'string',
                        'integer',
                    ),
                    'format' => 'date-time-http',
                    'location' => 'header',
                ),
                'GrantFullControl' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-grant-full-control',
                ),
                'GrantRead' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-grant-read',
                ),
                'GrantReadACP' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-grant-read-acp',
                ),
                'GrantWriteACP' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-grant-write-acp',
                ),
                'Key' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'uri',
                    'minLength' => 1,
                    'filters' => array(
                        'Aws\\S3\\S3Client::explodeKey',
                    ),
                ),
                'Metadata' => array(
                    'type' => 'object',
                    'location' => 'header',
                    'sentAs' => 'x-amz-meta-',
                    'additionalProperties' => array(
                        'type' => 'string',
                    ),
                ),
                'ServerSideEncryption' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-server-side-encryption',
                ),
                'StorageClass' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-storage-class',
                ),
                'WebsiteRedirectLocation' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-website-redirect-location',
                ),
                'SSECustomerAlgorithm' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-server-side-encryption-customer-algorithm',
                ),
                'SSECustomerKey' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-server-side-encryption-customer-key',
                ),
                'SSECustomerKeyMD5' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-server-side-encryption-customer-key-MD5',
                ),
                'SSEKMSKeyId' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-server-side-encryption-aws-kms-key-id',
                ),
                'RequestPayer' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-request-payer',
                ),
                'ACP' => array(
                    'type' => 'object',
                    'additionalProperties' => true,
                ),
            ),
        ),
        'PutObjectAcl' => array(
            'httpMethod' => 'PUT',
            'uri' => '/{Bucket}{/Key*}?acl',
            'class' => 'Aws\\S3\\Command\\S3Command',
            'responseClass' => 'PutObjectAclOutput',
            'responseType' => 'model',
            'documentationUrl' => 'http://docs.aws.amazon.com/AmazonS3/latest/API/RESTObjectPUTacl.html',
            'data' => array(
                'xmlRoot' => array(
                    'name' => 'AccessControlPolicy',
                    'namespaces' => array(
                        'http://s3.amazonaws.com/doc/2006-03-01/',
                    ),
                ),
            ),
            'parameters' => array(
                'ACL' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-acl',
                ),
                'Grants' => array(
                    'type' => 'array',
                    'location' => 'xml',
                    'sentAs' => 'AccessControlList',
                    'items' => array(
                        'name' => 'Grant',
                        'type' => 'object',
                        'properties' => array(
                            'Grantee' => array(
                                'type' => 'object',
                                'properties' => array(
                                    'DisplayName' => array(
                                        'type' => 'string',
                                    ),
                                    'EmailAddress' => array(
                                        'type' => 'string',
                                    ),
                                    'ID' => array(
                                        'type' => 'string',
                                    ),
                                    'Type' => array(
                                        'required' => true,
                                        'type' => 'string',
                                        'sentAs' => 'xsi:type',
                                        'data' => array(
                                            'xmlAttribute' => true,
                                            'xmlNamespace' => 'http://www.w3.org/2001/XMLSchema-instance',
                                        ),
                                    ),
                                    'URI' => array(
                                        'type' => 'string',
                                    ),
                                ),
                            ),
                            'Permission' => array(
                                'type' => 'string',
                            ),
                        ),
                    ),
                ),
                'Owner' => array(
                    'type' => 'object',
                    'location' => 'xml',
                    'properties' => array(
                        'DisplayName' => array(
                            'type' => 'string',
                        ),
                        'ID' => array(
                            'type' => 'string',
                        ),
                    ),
                ),
                'Bucket' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'uri',
                ),
                'GrantFullControl' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-grant-full-control',
                ),
                'GrantRead' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-grant-read',
                ),
                'GrantReadACP' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-grant-read-acp',
                ),
                'GrantWrite' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-grant-write',
                ),
                'GrantWriteACP' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-grant-write-acp',
                ),
                'Key' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'uri',
                    'minLength' => 1,
                    'filters' => array(
                        'Aws\\S3\\S3Client::explodeKey',
                    ),
                ),
                'RequestPayer' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-request-payer',
                ),
                'ACP' => array(
                    'type' => 'object',
                    'additionalProperties' => true,
                ),
            ),
            'errorResponses' => array(
                array(
                    'reason' => 'The specified key does not exist.',
                    'class' => 'NoSuchKeyException',
                ),
            ),
        ),
        'RestoreObject' => array(
            'httpMethod' => 'POST',
            'uri' => '/{Bucket}{/Key*}?restore',
            'class' => 'Aws\\S3\\Command\\S3Command',
            'responseClass' => 'RestoreObjectOutput',
            'responseType' => 'model',
            'documentationUrl' => 'http://docs.aws.amazon.com/AmazonS3/latest/API/RESTObjectRestore.html',
            'data' => array(
                'xmlRoot' => array(
                    'name' => 'RestoreRequest',
                    'namespaces' => array(
                        'http://s3.amazonaws.com/doc/2006-03-01/',
                    ),
                ),
            ),
            'parameters' => array(
                'Bucket' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'uri',
                ),
                'Key' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'uri',
                    'minLength' => 1,
                    'filters' => array(
                        'Aws\\S3\\S3Client::explodeKey',
                    ),
                ),
                'VersionId' => array(
                    'type' => 'string',
                    'location' => 'query',
                    'sentAs' => 'versionId',
                ),
                'Days' => array(
                    'required' => true,
                    'type' => 'numeric',
                    'location' => 'xml',
                ),
                'RequestPayer' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-request-payer',
                ),
            ),
            'errorResponses' => array(
                array(
                    'reason' => 'This operation is not allowed against this storage tier',
                    'class' => 'ObjectAlreadyInActiveTierErrorException',
                ),
            ),
        ),
        'UploadPart' => array(
            'httpMethod' => 'PUT',
            'uri' => '/{Bucket}{/Key*}',
            'class' => 'Aws\\S3\\Command\\S3Command',
            'responseClass' => 'UploadPartOutput',
            'responseType' => 'model',
            'documentationUrl' => 'http://docs.aws.amazon.com/AmazonS3/latest/API/mpUploadUploadPart.html',
            'data' => array(
                'xmlRoot' => array(
                    'name' => 'UploadPartRequest',
                    'namespaces' => array(
                        'http://s3.amazonaws.com/doc/2006-03-01/',
                    ),
                ),
            ),
            'parameters' => array(
                'Body' => array(
                    'type' => array(
                        'string',
                        'object',
                    ),
                    'location' => 'body',
                ),
                'Bucket' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'uri',
                ),
                'ContentLength' => array(
                    'type' => 'numeric',
                    'location' => 'header',
                    'sentAs' => 'Content-Length',
                ),
                'ContentMD5' => array(
                    'type' => array(
                        'string',
                        'boolean',
                    ),
                    'location' => 'header',
                    'sentAs' => 'Content-MD5',
                ),
                'Key' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'uri',
                    'minLength' => 1,
                    'filters' => array(
                        'Aws\\S3\\S3Client::explodeKey',
                    ),
                ),
                'PartNumber' => array(
                    'required' => true,
                    'type' => 'numeric',
                    'location' => 'query',
                    'sentAs' => 'partNumber',
                ),
                'UploadId' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'query',
                    'sentAs' => 'uploadId',
                ),
                'ServerSideEncryption' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-server-side-encryption',
                ),
                'SSECustomerAlgorithm' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-server-side-encryption-customer-algorithm',
                ),
                'SSECustomerKey' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-server-side-encryption-customer-key',
                ),
                'SSECustomerKeyMD5' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-server-side-encryption-customer-key-MD5',
                ),
                'RequestPayer' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-request-payer',
                ),
            ),
        ),
        'UploadPartCopy' => array(
            'httpMethod' => 'PUT',
            'uri' => '/{Bucket}{/Key*}',
            'class' => 'Aws\\S3\\Command\\S3Command',
            'responseClass' => 'UploadPartCopyOutput',
            'responseType' => 'model',
            'documentationUrl' => 'http://docs.aws.amazon.com/AmazonS3/latest/API/mpUploadUploadPartCopy.html',
            'data' => array(
                'xmlRoot' => array(
                    'name' => 'UploadPartCopyRequest',
                    'namespaces' => array(
                        'http://s3.amazonaws.com/doc/2006-03-01/',
                    ),
                ),
            ),
            'parameters' => array(
                'Bucket' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'uri',
                ),
                'CopySource' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-copy-source',
                ),
                'CopySourceIfMatch' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-copy-source-if-match',
                ),
                'CopySourceIfModifiedSince' => array(
                    'type' => array(
                        'object',
                        'string',
                        'integer',
                    ),
                    'format' => 'date-time-http',
                    'location' => 'header',
                    'sentAs' => 'x-amz-copy-source-if-modified-since',
                ),
                'CopySourceIfNoneMatch' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-copy-source-if-none-match',
                ),
                'CopySourceIfUnmodifiedSince' => array(
                    'type' => array(
                        'object',
                        'string',
                        'integer',
                    ),
                    'format' => 'date-time-http',
                    'location' => 'header',
                    'sentAs' => 'x-amz-copy-source-if-unmodified-since',
                ),
                'CopySourceRange' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-copy-source-range',
                ),
                'Key' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'uri',
                    'minLength' => 1,
                    'filters' => array(
                        'Aws\\S3\\S3Client::explodeKey',
                    ),
                ),
                'PartNumber' => array(
                    'required' => true,
                    'type' => 'numeric',
                    'location' => 'query',
                    'sentAs' => 'partNumber',
                ),
                'UploadId' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'query',
                    'sentAs' => 'uploadId',
                ),
                'SSECustomerAlgorithm' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-server-side-encryption-customer-algorithm',
                ),
                'SSECustomerKey' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-server-side-encryption-customer-key',
                ),
                'SSECustomerKeyMD5' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-server-side-encryption-customer-key-MD5',
                ),
                'CopySourceSSECustomerAlgorithm' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-copy-source-server-side-encryption-customer-algorithm',
                ),
                'CopySourceSSECustomerKey' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-copy-source-server-side-encryption-customer-key',
                ),
                'CopySourceSSECustomerKeyMD5' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-copy-source-server-side-encryption-customer-key-MD5',
                ),
                'RequestPayer' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-request-payer',
                ),
                'command.expects' => array(
                    'static' => true,
                    'default' => 'application/xml',
                ),
            ),
        ),
    ),
    'models' => array(
        'AbortMultipartUploadOutput' => array(
            'type' => 'object',
            'additionalProperties' => true,
            'properties' => array(
                'RequestCharged' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-request-charged',
                ),
                'RequestId' => array(
                    'location' => 'header',
                    'sentAs' => 'x-amz-request-id',
                ),
            ),
        ),
        'CompleteMultipartUploadOutput' => array(
            'type' => 'object',
            'additionalProperties' => true,
            'properties' => array(
                'Location' => array(
                    'type' => 'string',
                    'location' => 'xml',
                ),
                'Bucket' => array(
                    'type' => 'string',
                    'location' => 'xml',
                ),
                'Key' => array(
                    'type' => 'string',
                    'location' => 'xml',
                ),
                'Expiration' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-expiration',
                ),
                'ETag' => array(
                    'type' => 'string',
                    'location' => 'xml',
                ),
                'ServerSideEncryption' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-server-side-encryption',
                ),
                'VersionId' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-version-id',
                ),
                'SSEKMSKeyId' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-server-side-encryption-aws-kms-key-id',
                ),
                'RequestCharged' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-request-charged',
                ),
                'RequestId' => array(
                    'location' => 'header',
                    'sentAs' => 'x-amz-request-id',
                ),
            ),
        ),
        'CopyObjectOutput' => array(
            'type' => 'object',
            'additionalProperties' => true,
            'properties' => array(
                'ETag' => array(
                    'type' => 'string',
                    'location' => 'xml',
                ),
                'LastModified' => array(
                    'type' => 'string',
                    'location' => 'xml',
                ),
                'Expiration' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-expiration',
                ),
                'CopySourceVersionId' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-copy-source-version-id',
                ),
                'VersionId' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-version-id',
                ),
                'ServerSideEncryption' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-server-side-encryption',
                ),
                'SSECustomerAlgorithm' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-server-side-encryption-customer-algorithm',
                ),
                'SSECustomerKeyMD5' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-server-side-encryption-customer-key-MD5',
                ),
                'SSEKMSKeyId' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-server-side-encryption-aws-kms-key-id',
                ),
                'RequestCharged' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-request-charged',
                ),
                'RequestId' => array(
                    'location' => 'header',
                    'sentAs' => 'x-amz-request-id',
                ),
            ),
        ),
        'CreateBucketOutput' => array(
            'type' => 'object',
            'additionalProperties' => true,
            'properties' => array(
                'Location' => array(
                    'type' => 'string',
                    'location' => 'header',
                ),
                'RequestId' => array(
                    'location' => 'header',
                    'sentAs' => 'x-amz-request-id',
                ),
            ),
        ),
        'CreateMultipartUploadOutput' => array(
            'type' => 'object',
            'additionalProperties' => true,
            'properties' => array(
                'Bucket' => array(
                    'type' => 'string',
                    'location' => 'xml',
                    'sentAs' => 'Bucket',
                ),
                'Key' => array(
                    'type' => 'string',
                    'location' => 'xml',
                ),
                'UploadId' => array(
                    'type' => 'string',
                    'location' => 'xml',
                ),
                'ServerSideEncryption' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-server-side-encryption',
                ),
                'SSECustomerAlgorithm' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-server-side-encryption-customer-algorithm',
                ),
                'SSECustomerKeyMD5' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-server-side-encryption-customer-key-MD5',
                ),
                'SSEKMSKeyId' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-server-side-encryption-aws-kms-key-id',
                ),
                'RequestCharged' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-request-charged',
                ),
                'RequestId' => array(
                    'location' => 'header',
                    'sentAs' => 'x-amz-request-id',
                ),
            ),
        ),
        'DeleteBucketOutput' => array(
            'type' => 'object',
            'additionalProperties' => true,
            'properties' => array(
                'RequestId' => array(
                    'location' => 'header',
                    'sentAs' => 'x-amz-request-id',
                ),
            ),
        ),
        'DeleteBucketCorsOutput' => array(
            'type' => 'object',
            'additionalProperties' => true,
            'properties' => array(
                'RequestId' => array(
                    'location' => 'header',
                    'sentAs' => 'x-amz-request-id',
                ),
            ),
        ),
        'DeleteBucketLifecycleOutput' => array(
            'type' => 'object',
            'additionalProperties' => true,
            'properties' => array(
                'RequestId' => array(
                    'location' => 'header',
                    'sentAs' => 'x-amz-request-id',
                ),
            ),
        ),
        'DeleteBucketPolicyOutput' => array(
            'type' => 'object',
            'additionalProperties' => true,
            'properties' => array(
                'RequestId' => array(
                    'location' => 'header',
                    'sentAs' => 'x-amz-request-id',
                ),
            ),
        ),
        'DeleteBucketReplicationOutput' => array(
            'type' => 'object',
            'additionalProperties' => true,
            'properties' => array(
                'RequestId' => array(
                    'location' => 'header',
                    'sentAs' => 'x-amz-request-id',
                ),
            ),
        ),
        'DeleteBucketTaggingOutput' => array(
            'type' => 'object',
            'additionalProperties' => true,
            'properties' => array(
                'RequestId' => array(
                    'location' => 'header',
                    'sentAs' => 'x-amz-request-id',
                ),
            ),
        ),
        'DeleteBucketWebsiteOutput' => array(
            'type' => 'object',
            'additionalProperties' => true,
            'properties' => array(
                'RequestId' => array(
                    'location' => 'header',
                    'sentAs' => 'x-amz-request-id',
                ),
            ),
        ),
        'DeleteObjectOutput' => array(
            'type' => 'object',
            'additionalProperties' => true,
            'properties' => array(
                'DeleteMarker' => array(
                    'type' => 'boolean',
                    'location' => 'header',
                    'sentAs' => 'x-amz-delete-marker',
                ),
                'VersionId' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-version-id',
                ),
                'RequestCharged' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-request-charged',
                ),
                'RequestId' => array(
                    'location' => 'header',
                    'sentAs' => 'x-amz-request-id',
                ),
            ),
        ),
        'DeleteObjectsOutput' => array(
            'type' => 'object',
            'additionalProperties' => true,
            'properties' => array(
                'Deleted' => array(
                    'type' => 'array',
                    'location' => 'xml',
                    'data' => array(
                        'xmlFlattened' => true,
                    ),
                    'items' => array(
                        'name' => 'DeletedObject',
                        'type' => 'object',
                        'properties' => array(
                            'Key' => array(
                                'type' => 'string',
                            ),
                            'VersionId' => array(
                                'type' => 'string',
                            ),
                            'DeleteMarker' => array(
                                'type' => 'boolean',
                            ),
                            'DeleteMarkerVersionId' => array(
                                'type' => 'string',
                            ),
                        ),
                    ),
                ),
                'RequestCharged' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-request-charged',
                ),
                'Errors' => array(
                    'type' => 'array',
                    'location' => 'xml',
                    'sentAs' => 'Error',
                    'data' => array(
                        'xmlFlattened' => true,
                    ),
                    'items' => array(
                        'name' => 'Error',
                        'type' => 'object',
                        'sentAs' => 'Error',
                        'properties' => array(
                            'Key' => array(
                                'type' => 'string',
                            ),
                            'VersionId' => array(
                                'type' => 'string',
                            ),
                            'Code' => array(
                                'type' => 'string',
                            ),
                            'Message' => array(
                                'type' => 'string',
                            ),
                        ),
                    ),
                ),
                'RequestId' => array(
                    'location' => 'header',
                    'sentAs' => 'x-amz-request-id',
                ),
            ),
        ),
        'GetBucketAclOutput' => array(
            'type' => 'object',
            'additionalProperties' => true,
            'properties' => array(
                'Owner' => array(
                    'type' => 'object',
                    'location' => 'xml',
                    'properties' => array(
                        'DisplayName' => array(
                            'type' => 'string',
                        ),
                        'ID' => array(
                            'type' => 'string',
                        ),
                    ),
                ),
                'Grants' => array(
                    'type' => 'array',
                    'location' => 'xml',
                    'sentAs' => 'AccessControlList',
                    'items' => array(
                        'name' => 'Grant',
                        'type' => 'object',
                        'sentAs' => 'Grant',
                        'properties' => array(
                            'Grantee' => array(
                                'type' => 'object',
                                'properties' => array(
                                    'DisplayName' => array(
                                        'type' => 'string',
                                    ),
                                    'EmailAddress' => array(
                                        'type' => 'string',
                                    ),
                                    'ID' => array(
                                        'type' => 'string',
                                    ),
                                    'Type' => array(
                                        'type' => 'string',
                                        'sentAs' => 'xsi:type',
                                        'data' => array(
                                            'xmlAttribute' => true,
                                            'xmlNamespace' => 'http://www.w3.org/2001/XMLSchema-instance',
                                        ),
                                    ),
                                    'URI' => array(
                                        'type' => 'string',
                                    ),
                                ),
                            ),
                            'Permission' => array(
                                'type' => 'string',
                            ),
                        ),
                    ),
                ),
                'RequestId' => array(
                    'location' => 'header',
                    'sentAs' => 'x-amz-request-id',
                ),
            ),
        ),
        'GetBucketCorsOutput' => array(
            'type' => 'object',
            'additionalProperties' => true,
            'properties' => array(
                'CORSRules' => array(
                    'type' => 'array',
                    'location' => 'xml',
                    'sentAs' => 'CORSRule',
                    'data' => array(
                        'xmlFlattened' => true,
                    ),
                    'items' => array(
                        'name' => 'CORSRule',
                        'type' => 'object',
                        'sentAs' => 'CORSRule',
                        'properties' => array(
                            'AllowedHeaders' => array(
                                'type' => 'array',
                                'sentAs' => 'AllowedHeader',
                                'data' => array(
                                    'xmlFlattened' => true,
                                ),
                                'items' => array(
                                    'name' => 'AllowedHeader',
                                    'type' => 'string',
                                    'sentAs' => 'AllowedHeader',
                                ),
                            ),
                            'AllowedMethods' => array(
                                'type' => 'array',
                                'sentAs' => 'AllowedMethod',
                                'data' => array(
                                    'xmlFlattened' => true,
                                ),
                                'items' => array(
                                    'name' => 'AllowedMethod',
                                    'type' => 'string',
                                    'sentAs' => 'AllowedMethod',
                                ),
                            ),
                            'AllowedOrigins' => array(
                                'type' => 'array',
                                'sentAs' => 'AllowedOrigin',
                                'data' => array(
                                    'xmlFlattened' => true,
                                ),
                                'items' => array(
                                    'name' => 'AllowedOrigin',
                                    'type' => 'string',
                                    'sentAs' => 'AllowedOrigin',
                                ),
                            ),
                            'ExposeHeaders' => array(
                                'type' => 'array',
                                'sentAs' => 'ExposeHeader',
                                'data' => array(
                                    'xmlFlattened' => true,
                                ),
                                'items' => array(
                                    'name' => 'ExposeHeader',
                                    'type' => 'string',
                                    'sentAs' => 'ExposeHeader',
                                ),
                            ),
                            'MaxAgeSeconds' => array(
                                'type' => 'numeric',
                            ),
                        ),
                    ),
                ),
                'RequestId' => array(
                    'location' => 'header',
                    'sentAs' => 'x-amz-request-id',
                ),
            ),
        ),
        'GetBucketLifecycleOutput' => array(
            'type' => 'object',
            'additionalProperties' => true,
            'properties' => array(
                'Rules' => array(
                    'type' => 'array',
                    'location' => 'xml',
                    'sentAs' => 'Rule',
                    'data' => array(
                        'xmlFlattened' => true,
                    ),
                    'items' => array(
                        'name' => 'Rule',
                        'type' => 'object',
                        'sentAs' => 'Rule',
                        'properties' => array(
                            'Expiration' => array(
                                'type' => 'object',
                                'properties' => array(
                                    'Date' => array(
                                        'type' => 'string',
                                    ),
                                    'Days' => array(
                                        'type' => 'numeric',
                                    ),
                                ),
                            ),
                            'ID' => array(
                                'type' => 'string',
                            ),
                            'Prefix' => array(
                                'type' => 'string',
                            ),
                            'Status' => array(
                                'type' => 'string',
                            ),
                            'Transition' => array(
                                'type' => 'object',
                                'properties' => array(
                                    'Date' => array(
                                        'type' => 'string',
                                    ),
                                    'Days' => array(
                                        'type' => 'numeric',
                                    ),
                                    'StorageClass' => array(
                                        'type' => 'string',
                                    ),
                                ),
                            ),
                            'NoncurrentVersionTransition' => array(
                                'type' => 'object',
                                'properties' => array(
                                    'NoncurrentDays' => array(
                                        'type' => 'numeric',
                                    ),
                                    'StorageClass' => array(
                                        'type' => 'string',
                                    ),
                                ),
                            ),
                            'NoncurrentVersionExpiration' => array(
                                'type' => 'object',
                                'properties' => array(
                                    'NoncurrentDays' => array(
                                        'type' => 'numeric',
                                    ),
                                ),
                            ),
                        ),
                    ),
                ),
                'RequestId' => array(
                    'location' => 'header',
                    'sentAs' => 'x-amz-request-id',
                ),
            ),
        ),
        'GetBucketLifecycleConfigurationOutput' => array(
            'type' => 'object',
            'additionalProperties' => true,
            'properties' => array(
                'Rules' => array(
                    'type' => 'array',
                    'location' => 'xml',
                    'sentAs' => 'Rule',
                    'data' => array(
                        'xmlFlattened' => true,
                    ),
                    'items' => array(
                        'name' => 'LifecycleRule',
                        'type' => 'object',
                        'sentAs' => 'Rule',
                        'properties' => array(
                            'Expiration' => array(
                                'type' => 'object',
                                'properties' => array(
                                    'Date' => array(
                                        'type' => 'string',
                                    ),
                                    'Days' => array(
                                        'type' => 'numeric',
                                    ),
                                ),
                            ),
                            'ID' => array(
                                'type' => 'string',
                            ),
                            'Prefix' => array(
                                'type' => 'string',
                            ),
                            'Status' => array(
                                'type' => 'string',
                            ),
                            'Transitions' => array(
                                'type' => 'array',
                                'sentAs' => 'Transition',
                                'data' => array(
                                    'xmlFlattened' => true,
                                ),
                                'items' => array(
                                    'name' => 'Transition',
                                    'type' => 'object',
                                    'sentAs' => 'Transition',
                                    'properties' => array(
                                        'Date' => array(
                                            'type' => 'string',
                                        ),
                                        'Days' => array(
                                            'type' => 'numeric',
                                        ),
                                        'StorageClass' => array(
                                            'type' => 'string',
                                        ),
                                    ),
                                ),
                            ),
                            'NoncurrentVersionTransitions' => array(
                                'type' => 'array',
                                'sentAs' => 'NoncurrentVersionTransition',
                                'data' => array(
                                    'xmlFlattened' => true,
                                ),
                                'items' => array(
                                    'name' => 'NoncurrentVersionTransition',
                                    'type' => 'object',
                                    'sentAs' => 'NoncurrentVersionTransition',
                                    'properties' => array(
                                        'NoncurrentDays' => array(
                                            'type' => 'numeric',
                                        ),
                                        'StorageClass' => array(
                                            'type' => 'string',
                                        ),
                                    ),
                                ),
                            ),
                            'NoncurrentVersionExpiration' => array(
                                'type' => 'object',
                                'properties' => array(
                                    'NoncurrentDays' => array(
                                        'type' => 'numeric',
                                    ),
                                ),
                            ),
                        ),
                    ),
                ),
                'RequestId' => array(
                    'location' => 'header',
                    'sentAs' => 'x-amz-request-id',
                ),
            ),
        ),
        'GetBucketLocationOutput' => array(
            'type' => 'object',
            'additionalProperties' => true,
            'properties' => array(
                'Location' => array(
                    'type' => 'string',
                    'location' => 'body',
                    'filters' => array(
                        'strval',
                        'strip_tags',
                        'trim',
                    ),
                ),
            ),
        ),
        'GetBucketLoggingOutput' => array(
            'type' => 'object',
            'additionalProperties' => true,
            'properties' => array(
                'LoggingEnabled' => array(
                    'type' => 'object',
                    'location' => 'xml',
                    'properties' => array(
                        'TargetBucket' => array(
                            'type' => 'string',
                        ),
                        'TargetGrants' => array(
                            'type' => 'array',
                            'items' => array(
                                'name' => 'Grant',
                                'type' => 'object',
                                'sentAs' => 'Grant',
                                'properties' => array(
                                    'Grantee' => array(
                                        'type' => 'object',
                                        'properties' => array(
                                            'DisplayName' => array(
                                                'type' => 'string',
                                            ),
                                            'EmailAddress' => array(
                                                'type' => 'string',
                                            ),
                                            'ID' => array(
                                                'type' => 'string',
                                            ),
                                            'Type' => array(
                                                'type' => 'string',
                                                'sentAs' => 'xsi:type',
                                                'data' => array(
                                                    'xmlAttribute' => true,
                                                    'xmlNamespace' => 'http://www.w3.org/2001/XMLSchema-instance',
                                                ),
                                            ),
                                            'URI' => array(
                                                'type' => 'string',
                                            ),
                                        ),
                                    ),
                                    'Permission' => array(
                                        'type' => 'string',
                                    ),
                                ),
                            ),
                        ),
                        'TargetPrefix' => array(
                            'type' => 'string',
                        ),
                    ),
                ),
                'RequestId' => array(
                    'location' => 'header',
                    'sentAs' => 'x-amz-request-id',
                ),
            ),
        ),
        'NotificationConfigurationDeprecated' => array(
            'type' => 'object',
            'additionalProperties' => true,
            'properties' => array(
                'TopicConfiguration' => array(
                    'type' => 'object',
                    'location' => 'xml',
                    'properties' => array(
                        'Id' => array(
                            'type' => 'string',
                        ),
                        'Events' => array(
                            'type' => 'array',
                            'sentAs' => 'Event',
                            'data' => array(
                                'xmlFlattened' => true,
                            ),
                            'items' => array(
                                'name' => 'Event',
                                'type' => 'string',
                                'sentAs' => 'Event',
                            ),
                        ),
                        'Event' => array(
                            'type' => 'string',
                        ),
                        'Topic' => array(
                            'type' => 'string',
                        ),
                    ),
                ),
                'QueueConfiguration' => array(
                    'type' => 'object',
                    'location' => 'xml',
                    'properties' => array(
                        'Id' => array(
                            'type' => 'string',
                        ),
                        'Event' => array(
                            'type' => 'string',
                        ),
                        'Events' => array(
                            'type' => 'array',
                            'sentAs' => 'Event',
                            'data' => array(
                                'xmlFlattened' => true,
                            ),
                            'items' => array(
                                'name' => 'Event',
                                'type' => 'string',
                                'sentAs' => 'Event',
                            ),
                        ),
                        'Queue' => array(
                            'type' => 'string',
                        ),
                    ),
                ),
                'CloudFunctionConfiguration' => array(
                    'type' => 'object',
                    'location' => 'xml',
                    'properties' => array(
                        'Id' => array(
                            'type' => 'string',
                        ),
                        'Event' => array(
                            'type' => 'string',
                        ),
                        'Events' => array(
                            'type' => 'array',
                            'sentAs' => 'Event',
                            'data' => array(
                                'xmlFlattened' => true,
                            ),
                            'items' => array(
                                'name' => 'Event',
                                'type' => 'string',
                                'sentAs' => 'Event',
                            ),
                        ),
                        'CloudFunction' => array(
                            'type' => 'string',
                        ),
                        'InvocationRole' => array(
                            'type' => 'string',
                        ),
                    ),
                ),
                'RequestId' => array(
                    'location' => 'header',
                    'sentAs' => 'x-amz-request-id',
                ),
            ),
        ),
        'NotificationConfiguration' => array(
            'type' => 'object',
            'additionalProperties' => true,
            'properties' => array(
                'TopicConfigurations' => array(
                    'type' => 'array',
                    'location' => 'xml',
                    'sentAs' => 'TopicConfiguration',
                    'data' => array(
                        'xmlFlattened' => true,
                    ),
                    'items' => array(
                        'name' => 'TopicConfiguration',
                        'type' => 'object',
                        'sentAs' => 'TopicConfiguration',
                        'properties' => array(
                            'Id' => array(
                                'type' => 'string',
                            ),
                            'TopicArn' => array(
                                'type' => 'string',
                                'sentAs' => 'Topic',
                            ),
                            'Events' => array(
                                'type' => 'array',
                                'sentAs' => 'Event',
                                'data' => array(
                                    'xmlFlattened' => true,
                                ),
                                'items' => array(
                                    'name' => 'Event',
                                    'type' => 'string',
                                    'sentAs' => 'Event',
                                ),
                            ),
                            'Filter' => array(
                                'type' => 'object',
                                'properties' => array(
                                    'Key' => array(
                                        'type' => 'object',
                                        'sentAs' => 'S3Key',
                                        'properties' => array(
                                            'FilterRules' => array(
                                                'type' => 'array',
                                                'sentAs' => 'FilterRule',
                                                'data' => array(
                                                    'xmlFlattened' => true,
                                                ),
                                                'items' => array(
                                                    'name' => 'FilterRule',
                                                    'type' => 'object',
                                                    'sentAs' => 'FilterRule',
                                                    'properties' => array(
                                                        'Name' => array(
                                                            'type' => 'string',
                                                        ),
                                                        'Value' => array(
                                                            'type' => 'string',
                                                        ),
                                                    ),
                                                ),
                                            ),
                                        ),
                                    ),
                                ),
                            ),
                        ),
                    ),
                ),
                'QueueConfigurations' => array(
                    'type' => 'array',
                    'location' => 'xml',
                    'sentAs' => 'QueueConfiguration',
                    'data' => array(
                        'xmlFlattened' => true,
                    ),
                    'items' => array(
                        'name' => 'QueueConfiguration',
                        'type' => 'object',
                        'sentAs' => 'QueueConfiguration',
                        'properties' => array(
                            'Id' => array(
                                'type' => 'string',
                            ),
                            'QueueArn' => array(
                                'type' => 'string',
                                'sentAs' => 'Queue',
                            ),
                            'Events' => array(
                                'type' => 'array',
                                'sentAs' => 'Event',
                                'data' => array(
                                    'xmlFlattened' => true,
                                ),
                                'items' => array(
                                    'name' => 'Event',
                                    'type' => 'string',
                                    'sentAs' => 'Event',
                                ),
                            ),
                            'Filter' => array(
                                'type' => 'object',
                                'properties' => array(
                                    'Key' => array(
                                        'type' => 'object',
                                        'sentAs' => 'S3Key',
                                        'properties' => array(
                                            'FilterRules' => array(
                                                'type' => 'array',
                                                'sentAs' => 'FilterRule',
                                                'data' => array(
                                                    'xmlFlattened' => true,
                                                ),
                                                'items' => array(
                                                    'name' => 'FilterRule',
                                                    'type' => 'object',
                                                    'sentAs' => 'FilterRule',
                                                    'properties' => array(
                                                        'Name' => array(
                                                            'type' => 'string',
                                                        ),
                                                        'Value' => array(
                                                            'type' => 'string',
                                                        ),
                                                    ),
                                                ),
                                            ),
                                        ),
                                    ),
                                ),
                            ),
                        ),
                    ),
                ),
                'LambdaFunctionConfigurations' => array(
                    'type' => 'array',
                    'location' => 'xml',
                    'sentAs' => 'CloudFunctionConfiguration',
                    'data' => array(
                        'xmlFlattened' => true,
                    ),
                    'items' => array(
                        'name' => 'LambdaFunctionConfiguration',
                        'type' => 'object',
                        'sentAs' => 'CloudFunctionConfiguration',
                        'properties' => array(
                            'Id' => array(
                                'type' => 'string',
                            ),
                            'LambdaFunctionArn' => array(
                                'type' => 'string',
                                'sentAs' => 'CloudFunction',
                            ),
                            'Events' => array(
                                'type' => 'array',
                                'sentAs' => 'Event',
                                'data' => array(
                                    'xmlFlattened' => true,
                                ),
                                'items' => array(
                                    'name' => 'Event',
                                    'type' => 'string',
                                    'sentAs' => 'Event',
                                ),
                            ),
                            'Filter' => array(
                                'type' => 'object',
                                'properties' => array(
                                    'Key' => array(
                                        'type' => 'object',
                                        'sentAs' => 'S3Key',
                                        'properties' => array(
                                            'FilterRules' => array(
                                                'type' => 'array',
                                                'sentAs' => 'FilterRule',
                                                'data' => array(
                                                    'xmlFlattened' => true,
                                                ),
                                                'items' => array(
                                                    'name' => 'FilterRule',
                                                    'type' => 'object',
                                                    'sentAs' => 'FilterRule',
                                                    'properties' => array(
                                                        'Name' => array(
                                                            'type' => 'string',
                                                        ),
                                                        'Value' => array(
                                                            'type' => 'string',
                                                        ),
                                                    ),
                                                ),
                                            ),
                                        ),
                                    ),
                                ),
                            ),
                        ),
                    ),
                ),
                'RequestId' => array(
                    'location' => 'header',
                    'sentAs' => 'x-amz-request-id',
                ),
            ),
        ),
        'GetBucketPolicyOutput' => array(
            'type' => 'object',
            'additionalProperties' => true,
            'properties' => array(
                'Policy' => array(
                    'type' => 'string',
                    'instanceOf' => 'Guzzle\\Http\\EntityBody',
                    'location' => 'body',
                ),
                'RequestId' => array(
                    'location' => 'header',
                    'sentAs' => 'x-amz-request-id',
                ),
            ),
        ),
        'GetBucketReplicationOutput' => array(
            'type' => 'object',
            'additionalProperties' => true,
            'properties' => array(
                'Role' => array(
                    'type' => 'string',
                    'location' => 'xml',
                ),
                'Rules' => array(
                    'type' => 'array',
                    'location' => 'xml',
                    'sentAs' => 'Rule',
                    'data' => array(
                        'xmlFlattened' => true,
                    ),
                    'items' => array(
                        'name' => 'ReplicationRule',
                        'type' => 'object',
                        'sentAs' => 'Rule',
                        'properties' => array(
                            'ID' => array(
                                'type' => 'string',
                            ),
                            'Prefix' => array(
                                'type' => 'string',
                            ),
                            'Status' => array(
                                'type' => 'string',
                            ),
                            'Destination' => array(
                                'type' => 'object',
                                'properties' => array(
                                    'Bucket' => array(
                                        'type' => 'string',
                                    ),
                                    'StorageClass' => array(
                                        'type' => 'string',
                                    ),
                                ),
                            ),
                        ),
                    ),
                ),
                'RequestId' => array(
                    'location' => 'header',
                    'sentAs' => 'x-amz-request-id',
                ),
            ),
        ),
        'GetBucketRequestPaymentOutput' => array(
            'type' => 'object',
            'additionalProperties' => true,
            'properties' => array(
                'Payer' => array(
                    'type' => 'string',
                    'location' => 'xml',
                ),
                'RequestId' => array(
                    'location' => 'header',
                    'sentAs' => 'x-amz-request-id',
                ),
            ),
        ),
        'GetBucketTaggingOutput' => array(
            'type' => 'object',
            'additionalProperties' => true,
            'properties' => array(
                'TagSet' => array(
                    'type' => 'array',
                    'location' => 'xml',
                    'items' => array(
                        'name' => 'Tag',
                        'type' => 'object',
                        'sentAs' => 'Tag',
                        'properties' => array(
                            'Key' => array(
                                'type' => 'string',
                            ),
                            'Value' => array(
                                'type' => 'string',
                            ),
                        ),
                    ),
                ),
                'RequestId' => array(
                    'location' => 'header',
                    'sentAs' => 'x-amz-request-id',
                ),
            ),
        ),
        'GetBucketVersioningOutput' => array(
            'type' => 'object',
            'additionalProperties' => true,
            'properties' => array(
                'Status' => array(
                    'type' => 'string',
                    'location' => 'xml',
                ),
                'MFADelete' => array(
                    'type' => 'string',
                    'location' => 'xml',
                    'sentAs' => 'MfaDelete',
                ),
                'RequestId' => array(
                    'location' => 'header',
                    'sentAs' => 'x-amz-request-id',
                ),
            ),
        ),
        'GetBucketWebsiteOutput' => array(
            'type' => 'object',
            'additionalProperties' => true,
            'properties' => array(
                'RedirectAllRequestsTo' => array(
                    'type' => 'object',
                    'location' => 'xml',
                    'properties' => array(
                        'HostName' => array(
                            'type' => 'string',
                        ),
                        'Protocol' => array(
                            'type' => 'string',
                        ),
                    ),
                ),
                'IndexDocument' => array(
                    'type' => 'object',
                    'location' => 'xml',
                    'properties' => array(
                        'Suffix' => array(
                            'type' => 'string',
                        ),
                    ),
                ),
                'ErrorDocument' => array(
                    'type' => 'object',
                    'location' => 'xml',
                    'properties' => array(
                        'Key' => array(
                            'type' => 'string',
                        ),
                    ),
                ),
                'RoutingRules' => array(
                    'type' => 'array',
                    'location' => 'xml',
                    'items' => array(
                        'name' => 'RoutingRule',
                        'type' => 'object',
                        'sentAs' => 'RoutingRule',
                        'properties' => array(
                            'Condition' => array(
                                'type' => 'object',
                                'properties' => array(
                                    'HttpErrorCodeReturnedEquals' => array(
                                        'type' => 'string',
                                    ),
                                    'KeyPrefixEquals' => array(
                                        'type' => 'string',
                                    ),
                                ),
                            ),
                            'Redirect' => array(
                                'type' => 'object',
                                'properties' => array(
                                    'HostName' => array(
                                        'type' => 'string',
                                    ),
                                    'HttpRedirectCode' => array(
                                        'type' => 'string',
                                    ),
                                    'Protocol' => array(
                                        'type' => 'string',
                                    ),
                                    'ReplaceKeyPrefixWith' => array(
                                        'type' => 'string',
                                    ),
                                    'ReplaceKeyWith' => array(
                                        'type' => 'string',
                                    ),
                                ),
                            ),
                        ),
                    ),
                ),
                'RequestId' => array(
                    'location' => 'header',
                    'sentAs' => 'x-amz-request-id',
                ),
            ),
        ),
        'GetObjectOutput' => array(
            'type' => 'object',
            'additionalProperties' => true,
            'properties' => array(
                'Body' => array(
                    'type' => 'string',
                    'instanceOf' => 'Guzzle\\Http\\EntityBody',
                    'location' => 'body',
                ),
                'DeleteMarker' => array(
                    'type' => 'boolean',
                    'location' => 'header',
                    'sentAs' => 'x-amz-delete-marker',
                ),
                'AcceptRanges' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'accept-ranges',
                ),
                'Expiration' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-expiration',
                ),
                'Restore' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-restore',
                ),
                'LastModified' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'Last-Modified',
                ),
                'ContentLength' => array(
                    'type' => 'numeric',
                    'location' => 'header',
                    'sentAs' => 'Content-Length',
                ),
                'ETag' => array(
                    'type' => 'string',
                    'location' => 'header',
                ),
                'MissingMeta' => array(
                    'type' => 'numeric',
                    'location' => 'header',
                    'sentAs' => 'x-amz-missing-meta',
                ),
                'VersionId' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-version-id',
                ),
                'CacheControl' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'Cache-Control',
                ),
                'ContentDisposition' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'Content-Disposition',
                ),
                'ContentEncoding' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'Content-Encoding',
                ),
                'ContentLanguage' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'Content-Language',
                ),
                'ContentRange' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'Content-Range',
                ),
                'ContentType' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'Content-Type',
                ),
                'Expires' => array(
                    'type' => 'string',
                    'location' => 'header',
                ),
                'WebsiteRedirectLocation' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-website-redirect-location',
                ),
                'ServerSideEncryption' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-server-side-encryption',
                ),
                'Metadata' => array(
                    'type' => 'object',
                    'location' => 'header',
                    'sentAs' => 'x-amz-meta-',
                    'additionalProperties' => array(
                        'type' => 'string',
                    ),
                ),
                'SSECustomerAlgorithm' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-server-side-encryption-customer-algorithm',
                ),
                'SSECustomerKeyMD5' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-server-side-encryption-customer-key-MD5',
                ),
                'SSEKMSKeyId' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-server-side-encryption-aws-kms-key-id',
                ),
                'StorageClass' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-storage-class',
                ),
                'RequestCharged' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-request-charged',
                ),
                'ReplicationStatus' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-replication-status',
                ),
                'RequestId' => array(
                    'location' => 'header',
                    'sentAs' => 'x-amz-request-id',
                ),
            ),
        ),
        'GetObjectAclOutput' => array(
            'type' => 'object',
            'additionalProperties' => true,
            'properties' => array(
                'Owner' => array(
                    'type' => 'object',
                    'location' => 'xml',
                    'properties' => array(
                        'DisplayName' => array(
                            'type' => 'string',
                        ),
                        'ID' => array(
                            'type' => 'string',
                        ),
                    ),
                ),
                'Grants' => array(
                    'type' => 'array',
                    'location' => 'xml',
                    'sentAs' => 'AccessControlList',
                    'items' => array(
                        'name' => 'Grant',
                        'type' => 'object',
                        'sentAs' => 'Grant',
                        'properties' => array(
                            'Grantee' => array(
                                'type' => 'object',
                                'properties' => array(
                                    'DisplayName' => array(
                                        'type' => 'string',
                                    ),
                                    'EmailAddress' => array(
                                        'type' => 'string',
                                    ),
                                    'ID' => array(
                                        'type' => 'string',
                                    ),
                                    'Type' => array(
                                        'type' => 'string',
                                        'sentAs' => 'xsi:type',
                                        'data' => array(
                                            'xmlAttribute' => true,
                                            'xmlNamespace' => 'http://www.w3.org/2001/XMLSchema-instance',
                                        ),
                                    ),
                                    'URI' => array(
                                        'type' => 'string',
                                    ),
                                ),
                            ),
                            'Permission' => array(
                                'type' => 'string',
                            ),
                        ),
                    ),
                ),
                'RequestCharged' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-request-charged',
                ),
                'RequestId' => array(
                    'location' => 'header',
                    'sentAs' => 'x-amz-request-id',
                ),
            ),
        ),
        'GetObjectTorrentOutput' => array(
            'type' => 'object',
            'additionalProperties' => true,
            'properties' => array(
                'Body' => array(
                    'type' => 'string',
                    'instanceOf' => 'Guzzle\\Http\\EntityBody',
                    'location' => 'body',
                ),
                'RequestCharged' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-request-charged',
                ),
                'RequestId' => array(
                    'location' => 'header',
                    'sentAs' => 'x-amz-request-id',
                ),
            ),
        ),
        'HeadBucketOutput' => array(
            'type' => 'object',
            'additionalProperties' => true,
            'properties' => array(
                'RequestId' => array(
                    'location' => 'header',
                    'sentAs' => 'x-amz-request-id',
                ),
            ),
        ),
        'HeadObjectOutput' => array(
            'type' => 'object',
            'additionalProperties' => true,
            'properties' => array(
                'DeleteMarker' => array(
                    'type' => 'boolean',
                    'location' => 'header',
                    'sentAs' => 'x-amz-delete-marker',
                ),
                'AcceptRanges' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'accept-ranges',
                ),
                'Expiration' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-expiration',
                ),
                'Restore' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-restore',
                ),
                'LastModified' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'Last-Modified',
                ),
                'ContentLength' => array(
                    'type' => 'numeric',
                    'location' => 'header',
                    'sentAs' => 'Content-Length',
                ),
                'ETag' => array(
                    'type' => 'string',
                    'location' => 'header',
                ),
                'MissingMeta' => array(
                    'type' => 'numeric',
                    'location' => 'header',
                    'sentAs' => 'x-amz-missing-meta',
                ),
                'VersionId' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-version-id',
                ),
                'CacheControl' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'Cache-Control',
                ),
                'ContentDisposition' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'Content-Disposition',
                ),
                'ContentEncoding' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'Content-Encoding',
                ),
                'ContentLanguage' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'Content-Language',
                ),
                'ContentType' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'Content-Type',
                ),
                'Expires' => array(
                    'type' => 'string',
                    'location' => 'header',
                ),
                'WebsiteRedirectLocation' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-website-redirect-location',
                ),
                'ServerSideEncryption' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-server-side-encryption',
                ),
                'Metadata' => array(
                    'type' => 'object',
                    'location' => 'header',
                    'sentAs' => 'x-amz-meta-',
                    'additionalProperties' => array(
                        'type' => 'string',
                    ),
                ),
                'SSECustomerAlgorithm' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-server-side-encryption-customer-algorithm',
                ),
                'SSECustomerKeyMD5' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-server-side-encryption-customer-key-MD5',
                ),
                'SSEKMSKeyId' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-server-side-encryption-aws-kms-key-id',
                ),
                'StorageClass' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-storage-class',
                ),
                'RequestCharged' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-request-charged',
                ),
                'ReplicationStatus' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-replication-status',
                ),
                'RequestId' => array(
                    'location' => 'header',
                    'sentAs' => 'x-amz-request-id',
                ),
            ),
        ),
        'ListBucketsOutput' => array(
            'type' => 'object',
            'additionalProperties' => true,
            'properties' => array(
                'Buckets' => array(
                    'type' => 'array',
                    'location' => 'xml',
                    'items' => array(
                        'name' => 'Bucket',
                        'type' => 'object',
                        'sentAs' => 'Bucket',
                        'properties' => array(
                            'Name' => array(
                                'type' => 'string',
                            ),
                            'CreationDate' => array(
                                'type' => 'string',
                            ),
                        ),
                    ),
                ),
                'Owner' => array(
                    'type' => 'object',
                    'location' => 'xml',
                    'properties' => array(
                        'DisplayName' => array(
                            'type' => 'string',
                        ),
                        'ID' => array(
                            'type' => 'string',
                        ),
                    ),
                ),
                'RequestId' => array(
                    'location' => 'header',
                    'sentAs' => 'x-amz-request-id',
                ),
            ),
        ),
        'ListMultipartUploadsOutput' => array(
            'type' => 'object',
            'additionalProperties' => true,
            'properties' => array(
                'Bucket' => array(
                    'type' => 'string',
                    'location' => 'xml',
                ),
                'KeyMarker' => array(
                    'type' => 'string',
                    'location' => 'xml',
                ),
                'UploadIdMarker' => array(
                    'type' => 'string',
                    'location' => 'xml',
                ),
                'NextKeyMarker' => array(
                    'type' => 'string',
                    'location' => 'xml',
                ),
                'Prefix' => array(
                    'type' => 'string',
                    'location' => 'xml',
                ),
                'Delimiter' => array(
                    'type' => 'string',
                    'location' => 'xml',
                ),
                'NextUploadIdMarker' => array(
                    'type' => 'string',
                    'location' => 'xml',
                ),
                'MaxUploads' => array(
                    'type' => 'numeric',
                    'location' => 'xml',
                ),
                'IsTruncated' => array(
                    'type' => 'boolean',
                    'location' => 'xml',
                ),
                'Uploads' => array(
                    'type' => 'array',
                    'location' => 'xml',
                    'sentAs' => 'Upload',
                    'data' => array(
                        'xmlFlattened' => true,
                    ),
                    'items' => array(
                        'name' => 'MultipartUpload',
                        'type' => 'object',
                        'sentAs' => 'Upload',
                        'properties' => array(
                            'UploadId' => array(
                                'type' => 'string',
                            ),
                            'Key' => array(
                                'type' => 'string',
                            ),
                            'Initiated' => array(
                                'type' => 'string',
                            ),
                            'StorageClass' => array(
                                'type' => 'string',
                            ),
                            'Owner' => array(
                                'type' => 'object',
                                'properties' => array(
                                    'DisplayName' => array(
                                        'type' => 'string',
                                    ),
                                    'ID' => array(
                                        'type' => 'string',
                                    ),
                                ),
                            ),
                            'Initiator' => array(
                                'type' => 'object',
                                'properties' => array(
                                    'ID' => array(
                                        'type' => 'string',
                                    ),
                                    'DisplayName' => array(
                                        'type' => 'string',
                                    ),
                                ),
                            ),
                        ),
                    ),
                ),
                'CommonPrefixes' => array(
                    'type' => 'array',
                    'location' => 'xml',
                    'data' => array(
                        'xmlFlattened' => true,
                    ),
                    'items' => array(
                        'name' => 'CommonPrefix',
                        'type' => 'object',
                        'properties' => array(
                            'Prefix' => array(
                                'type' => 'string',
                            ),
                        ),
                    ),
                ),
                'EncodingType' => array(
                    'type' => 'string',
                    'location' => 'xml',
                ),
                'RequestId' => array(
                    'location' => 'header',
                    'sentAs' => 'x-amz-request-id',
                ),
            ),
        ),
        'ListObjectVersionsOutput' => array(
            'type' => 'object',
            'additionalProperties' => true,
            'properties' => array(
                'IsTruncated' => array(
                    'type' => 'boolean',
                    'location' => 'xml',
                ),
                'KeyMarker' => array(
                    'type' => 'string',
                    'location' => 'xml',
                ),
                'VersionIdMarker' => array(
                    'type' => 'string',
                    'location' => 'xml',
                ),
                'NextKeyMarker' => array(
                    'type' => 'string',
                    'location' => 'xml',
                ),
                'NextVersionIdMarker' => array(
                    'type' => 'string',
                    'location' => 'xml',
                ),
                'Versions' => array(
                    'type' => 'array',
                    'location' => 'xml',
                    'sentAs' => 'Version',
                    'data' => array(
                        'xmlFlattened' => true,
                    ),
                    'items' => array(
                        'name' => 'ObjectVersion',
                        'type' => 'object',
                        'sentAs' => 'Version',
                        'properties' => array(
                            'ETag' => array(
                                'type' => 'string',
                            ),
                            'Size' => array(
                                'type' => 'numeric',
                            ),
                            'StorageClass' => array(
                                'type' => 'string',
                            ),
                            'Key' => array(
                                'type' => 'string',
                            ),
                            'VersionId' => array(
                                'type' => 'string',
                            ),
                            'IsLatest' => array(
                                'type' => 'boolean',
                            ),
                            'LastModified' => array(
                                'type' => 'string',
                            ),
                            'Owner' => array(
                                'type' => 'object',
                                'properties' => array(
                                    'DisplayName' => array(
                                        'type' => 'string',
                                    ),
                                    'ID' => array(
                                        'type' => 'string',
                                    ),
                                ),
                            ),
                        ),
                    ),
                ),
                'DeleteMarkers' => array(
                    'type' => 'array',
                    'location' => 'xml',
                    'sentAs' => 'DeleteMarker',
                    'data' => array(
                        'xmlFlattened' => true,
                    ),
                    'items' => array(
                        'name' => 'DeleteMarkerEntry',
                        'type' => 'object',
                        'sentAs' => 'DeleteMarker',
                        'properties' => array(
                            'Owner' => array(
                                'type' => 'object',
                                'properties' => array(
                                    'DisplayName' => array(
                                        'type' => 'string',
                                    ),
                                    'ID' => array(
                                        'type' => 'string',
                                    ),
                                ),
                            ),
                            'Key' => array(
                                'type' => 'string',
                            ),
                            'VersionId' => array(
                                'type' => 'string',
                            ),
                            'IsLatest' => array(
                                'type' => 'boolean',
                            ),
                            'LastModified' => array(
                                'type' => 'string',
                            ),
                        ),
                    ),
                ),
                'Name' => array(
                    'type' => 'string',
                    'location' => 'xml',
                ),
                'Prefix' => array(
                    'type' => 'string',
                    'location' => 'xml',
                ),
                'Delimiter' => array(
                    'type' => 'string',
                    'location' => 'xml',
                ),
                'MaxKeys' => array(
                    'type' => 'numeric',
                    'location' => 'xml',
                ),
                'CommonPrefixes' => array(
                    'type' => 'array',
                    'location' => 'xml',
                    'data' => array(
                        'xmlFlattened' => true,
                    ),
                    'items' => array(
                        'name' => 'CommonPrefix',
                        'type' => 'object',
                        'properties' => array(
                            'Prefix' => array(
                                'type' => 'string',
                            ),
                        ),
                    ),
                ),
                'EncodingType' => array(
                    'type' => 'string',
                    'location' => 'xml',
                ),
                'RequestId' => array(
                    'location' => 'header',
                    'sentAs' => 'x-amz-request-id',
                ),
            ),
        ),
        'ListObjectsOutput' => array(
            'type' => 'object',
            'additionalProperties' => true,
            'properties' => array(
                'IsTruncated' => array(
                    'type' => 'boolean',
                    'location' => 'xml',
                ),
                'Marker' => array(
                    'type' => 'string',
                    'location' => 'xml',
                ),
                'NextMarker' => array(
                    'type' => 'string',
                    'location' => 'xml',
                ),
                'Contents' => array(
                    'type' => 'array',
                    'location' => 'xml',
                    'data' => array(
                        'xmlFlattened' => true,
                    ),
                    'items' => array(
                        'name' => 'Object',
                        'type' => 'object',
                        'properties' => array(
                            'Key' => array(
                                'type' => 'string',
                            ),
                            'LastModified' => array(
                                'type' => 'string',
                            ),
                            'ETag' => array(
                                'type' => 'string',
                            ),
                            'Size' => array(
                                'type' => 'numeric',
                            ),
                            'StorageClass' => array(
                                'type' => 'string',
                            ),
                            'Owner' => array(
                                'type' => 'object',
                                'properties' => array(
                                    'DisplayName' => array(
                                        'type' => 'string',
                                    ),
                                    'ID' => array(
                                        'type' => 'string',
                                    ),
                                ),
                            ),
                        ),
                    ),
                ),
                'Name' => array(
                    'type' => 'string',
                    'location' => 'xml',
                ),
                'Prefix' => array(
                    'type' => 'string',
                    'location' => 'xml',
                ),
                'Delimiter' => array(
                    'type' => 'string',
                    'location' => 'xml',
                ),
                'MaxKeys' => array(
                    'type' => 'numeric',
                    'location' => 'xml',
                ),
                'CommonPrefixes' => array(
                    'type' => 'array',
                    'location' => 'xml',
                    'data' => array(
                        'xmlFlattened' => true,
                    ),
                    'items' => array(
                        'name' => 'CommonPrefix',
                        'type' => 'object',
                        'properties' => array(
                            'Prefix' => array(
                                'type' => 'string',
                            ),
                        ),
                    ),
                ),
                'EncodingType' => array(
                    'type' => 'string',
                    'location' => 'xml',
                ),
                'RequestId' => array(
                    'location' => 'header',
                    'sentAs' => 'x-amz-request-id',
                ),
            ),
        ),
        'ListPartsOutput' => array(
            'type' => 'object',
            'additionalProperties' => true,
            'properties' => array(
                'Bucket' => array(
                    'type' => 'string',
                    'location' => 'xml',
                ),
                'Key' => array(
                    'type' => 'string',
                    'location' => 'xml',
                ),
                'UploadId' => array(
                    'type' => 'string',
                    'location' => 'xml',
                ),
                'PartNumberMarker' => array(
                    'type' => 'numeric',
                    'location' => 'xml',
                ),
                'NextPartNumberMarker' => array(
                    'type' => 'numeric',
                    'location' => 'xml',
                ),
                'MaxParts' => array(
                    'type' => 'numeric',
                    'location' => 'xml',
                ),
                'IsTruncated' => array(
                    'type' => 'boolean',
                    'location' => 'xml',
                ),
                'Parts' => array(
                    'type' => 'array',
                    'location' => 'xml',
                    'sentAs' => 'Part',
                    'data' => array(
                        'xmlFlattened' => true,
                    ),
                    'items' => array(
                        'name' => 'Part',
                        'type' => 'object',
                        'sentAs' => 'Part',
                        'properties' => array(
                            'PartNumber' => array(
                                'type' => 'numeric',
                            ),
                            'LastModified' => array(
                                'type' => 'string',
                            ),
                            'ETag' => array(
                                'type' => 'string',
                            ),
                            'Size' => array(
                                'type' => 'numeric',
                            ),
                        ),
                    ),
                ),
                'Initiator' => array(
                    'type' => 'object',
                    'location' => 'xml',
                    'properties' => array(
                        'ID' => array(
                            'type' => 'string',
                        ),
                        'DisplayName' => array(
                            'type' => 'string',
                        ),
                    ),
                ),
                'Owner' => array(
                    'type' => 'object',
                    'location' => 'xml',
                    'properties' => array(
                        'DisplayName' => array(
                            'type' => 'string',
                        ),
                        'ID' => array(
                            'type' => 'string',
                        ),
                    ),
                ),
                'StorageClass' => array(
                    'type' => 'string',
                    'location' => 'xml',
                ),
                'RequestCharged' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-request-charged',
                ),
                'RequestId' => array(
                    'location' => 'header',
                    'sentAs' => 'x-amz-request-id',
                ),
            ),
        ),
        'PutBucketAclOutput' => array(
            'type' => 'object',
            'additionalProperties' => true,
            'properties' => array(
                'RequestId' => array(
                    'location' => 'header',
                    'sentAs' => 'x-amz-request-id',
                ),
            ),
        ),
        'PutBucketCorsOutput' => array(
            'type' => 'object',
            'additionalProperties' => true,
            'properties' => array(
                'RequestId' => array(
                    'location' => 'header',
                    'sentAs' => 'x-amz-request-id',
                ),
            ),
        ),
        'PutBucketLifecycleOutput' => array(
            'type' => 'object',
            'additionalProperties' => true,
            'properties' => array(
                'RequestId' => array(
                    'location' => 'header',
                    'sentAs' => 'x-amz-request-id',
                ),
            ),
        ),
        'PutBucketLifecycleConfigurationOutput' => array(
            'type' => 'object',
            'additionalProperties' => true,
            'properties' => array(
                'RequestId' => array(
                    'location' => 'header',
                    'sentAs' => 'x-amz-request-id',
                ),
            ),
        ),
        'PutBucketLoggingOutput' => array(
            'type' => 'object',
            'additionalProperties' => true,
            'properties' => array(
                'RequestId' => array(
                    'location' => 'header',
                    'sentAs' => 'x-amz-request-id',
                ),
            ),
        ),
        'PutBucketNotificationOutput' => array(
            'type' => 'object',
            'additionalProperties' => true,
            'properties' => array(
                'RequestId' => array(
                    'location' => 'header',
                    'sentAs' => 'x-amz-request-id',
                ),
            ),
        ),
        'PutBucketNotificationConfigurationOutput' => array(
            'type' => 'object',
            'additionalProperties' => true,
            'properties' => array(
                'RequestId' => array(
                    'location' => 'header',
                    'sentAs' => 'x-amz-request-id',
                ),
            ),
        ),
        'PutBucketPolicyOutput' => array(
            'type' => 'object',
            'additionalProperties' => true,
            'properties' => array(
                'RequestId' => array(
                    'location' => 'header',
                    'sentAs' => 'x-amz-request-id',
                ),
            ),
        ),
        'PutBucketReplicationOutput' => array(
            'type' => 'object',
            'additionalProperties' => true,
            'properties' => array(
                'RequestId' => array(
                    'location' => 'header',
                    'sentAs' => 'x-amz-request-id',
                ),
            ),
        ),
        'PutBucketRequestPaymentOutput' => array(
            'type' => 'object',
            'additionalProperties' => true,
            'properties' => array(
                'RequestId' => array(
                    'location' => 'header',
                    'sentAs' => 'x-amz-request-id',
                ),
            ),
        ),
        'PutBucketTaggingOutput' => array(
            'type' => 'object',
            'additionalProperties' => true,
            'properties' => array(
                'RequestId' => array(
                    'location' => 'header',
                    'sentAs' => 'x-amz-request-id',
                ),
            ),
        ),
        'PutBucketVersioningOutput' => array(
            'type' => 'object',
            'additionalProperties' => true,
            'properties' => array(
                'RequestId' => array(
                    'location' => 'header',
                    'sentAs' => 'x-amz-request-id',
                ),
            ),
        ),
        'PutBucketWebsiteOutput' => array(
            'type' => 'object',
            'additionalProperties' => true,
            'properties' => array(
                'RequestId' => array(
                    'location' => 'header',
                    'sentAs' => 'x-amz-request-id',
                ),
            ),
        ),
        'PutObjectOutput' => array(
            'type' => 'object',
            'additionalProperties' => true,
            'properties' => array(
                'Expiration' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-expiration',
                ),
                'ETag' => array(
                    'type' => 'string',
                    'location' => 'header',
                ),
                'ServerSideEncryption' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-server-side-encryption',
                ),
                'VersionId' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-version-id',
                ),
                'SSECustomerAlgorithm' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-server-side-encryption-customer-algorithm',
                ),
                'SSECustomerKeyMD5' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-server-side-encryption-customer-key-MD5',
                ),
                'SSEKMSKeyId' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-server-side-encryption-aws-kms-key-id',
                ),
                'RequestCharged' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-request-charged',
                ),
                'RequestId' => array(
                    'location' => 'header',
                    'sentAs' => 'x-amz-request-id',
                ),
                'ObjectURL' => array(
                ),
            ),
        ),
        'PutObjectAclOutput' => array(
            'type' => 'object',
            'additionalProperties' => true,
            'properties' => array(
                'RequestCharged' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-request-charged',
                ),
                'RequestId' => array(
                    'location' => 'header',
                    'sentAs' => 'x-amz-request-id',
                ),
            ),
        ),
        'RestoreObjectOutput' => array(
            'type' => 'object',
            'additionalProperties' => true,
            'properties' => array(
                'RequestCharged' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-request-charged',
                ),
                'RequestId' => array(
                    'location' => 'header',
                    'sentAs' => 'x-amz-request-id',
                ),
            ),
        ),
        'UploadPartOutput' => array(
            'type' => 'object',
            'additionalProperties' => true,
            'properties' => array(
                'ServerSideEncryption' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-server-side-encryption',
                ),
                'ETag' => array(
                    'type' => 'string',
                    'location' => 'header',
                ),
                'SSECustomerAlgorithm' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-server-side-encryption-customer-algorithm',
                ),
                'SSECustomerKeyMD5' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-server-side-encryption-customer-key-MD5',
                ),
                'SSEKMSKeyId' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-server-side-encryption-aws-kms-key-id',
                ),
                'RequestCharged' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-request-charged',
                ),
                'RequestId' => array(
                    'location' => 'header',
                    'sentAs' => 'x-amz-request-id',
                ),
            ),
        ),
        'UploadPartCopyOutput' => array(
            'type' => 'object',
            'additionalProperties' => true,
            'properties' => array(
                'CopySourceVersionId' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-copy-source-version-id',
                ),
                'ETag' => array(
                    'type' => 'string',
                    'location' => 'xml',
                ),
                'LastModified' => array(
                    'type' => 'string',
                    'location' => 'xml',
                ),
                'ServerSideEncryption' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-server-side-encryption',
                ),
                'SSECustomerAlgorithm' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-server-side-encryption-customer-algorithm',
                ),
                'SSECustomerKeyMD5' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-server-side-encryption-customer-key-MD5',
                ),
                'SSEKMSKeyId' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-server-side-encryption-aws-kms-key-id',
                ),
                'RequestCharged' => array(
                    'type' => 'string',
                    'location' => 'header',
                    'sentAs' => 'x-amz-request-charged',
                ),
                'RequestId' => array(
                    'location' => 'header',
                    'sentAs' => 'x-amz-request-id',
                ),
            ),
        ),
    ),
    'iterators' => array(
        'ListBuckets' => array(
            'result_key' => 'Buckets',
        ),
        'ListMultipartUploads' => array(
            'limit_key' => 'MaxUploads',
            'more_results' => 'IsTruncated',
            'output_token' => array(
                'NextKeyMarker',
                'NextUploadIdMarker',
            ),
            'input_token' => array(
                'KeyMarker',
                'UploadIdMarker',
            ),
            'result_key' => array(
                'Uploads',
                'CommonPrefixes',
            ),
        ),
        'ListObjectVersions' => array(
            'more_results' => 'IsTruncated',
            'limit_key' => 'MaxKeys',
            'output_token' => array(
                'NextKeyMarker',
                'NextVersionIdMarker',
            ),
            'input_token' => array(
                'KeyMarker',
                'VersionIdMarker',
            ),
            'result_key' => array(
                'Versions',
                'DeleteMarkers',
                'CommonPrefixes',
            ),
        ),
        'ListObjects' => array(
            'more_results' => 'IsTruncated',
            'limit_key' => 'MaxKeys',
            'output_token' => 'NextMarker',
            'input_token' => 'Marker',
            'result_key' => array(
                'Contents',
                'CommonPrefixes',
            ),
        ),
        'ListParts' => array(
            'more_results' => 'IsTruncated',
            'limit_key' => 'MaxParts',
            'output_token' => 'NextPartNumberMarker',
            'input_token' => 'PartNumberMarker',
            'result_key' => 'Parts',
        ),
    ),
    'waiters' => array(
        '__default__' => array(
            'interval' => 5,
            'max_attempts' => 20,
        ),
        'BucketExists' => array(
            'operation' => 'HeadBucket',
            'success.type' => 'output',
            'ignore_errors' => array(
                'NoSuchBucket',
            ),
        ),
        'BucketNotExists' => array(
            'operation' => 'HeadBucket',
            'success.type' => 'error',
            'success.value' => 'NoSuchBucket',
        ),
        'ObjectExists' => array(
            'operation' => 'HeadObject',
            'success.type' => 'output',
            'ignore_errors' => array(
                'NoSuchKey',
            ),
        ),
        'ObjectNotExists' => array(
            'operation' => 'HeadObject',
            'success.type' => 'error',
            'success.value' => 'NoSuchKey'
        ),
    ),
);
includes/lib/aws-sdk-php-2.8.31/src/Aws/S3/Sync/DownloadSync.php000064400000006516151327705670017671 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\S3\Sync;

use Aws\Common\Exception\RuntimeException;
use Aws\S3\ResumableDownload;

/**
 * Downloads and Amazon S3 bucket to a local directory
 */
class DownloadSync extends AbstractSync
{
    protected function createTransferAction(\SplFileInfo $file)
    {
        $sourceFilename = $file->getPathname();
        list($bucket, $key) = explode('/', substr($sourceFilename, 5), 2);
        $filename = $this->options['source_converter']->convert($sourceFilename);
        $this->createDirectory($filename);

        // Some S3 buckets contains nested files under the same name as a directory
        if (is_dir($filename)) {
            return false;
        }

        // Allow a previously interrupted download to resume
        if (file_exists($filename) && $this->options['resumable']) {
            return new ResumableDownload($this->options['client'], $bucket, $key, $filename);
        }

        return $this->options['client']->getCommand('GetObject', array(
            'Bucket' => $bucket,
            'Key'    => $key,
            'SaveAs' => $filename
        ));
    }

    /**
     * @codeCoverageIgnore
     */
    protected function createDirectory($filename)
    {
        $directory = dirname($filename);
        // Some S3 clients create empty files to denote directories. Remove these so that we can create the directory.
        if (is_file($directory) && filesize($directory) == 0) {
            unlink($directory);
        }
        // Create the directory if it does not exist
        if (!is_dir($directory) && !mkdir($directory, 0777, true)) {
            $errors = error_get_last();
            throw new RuntimeException('Could not create directory: ' . esc_html($directory) . ' - ' . esc_html($errors['message']));
        }
    }

    protected function filterCommands(array $commands)
    {
        // Build a list of all of the directories in each command so that we don't attempt to create an empty dir in
        // the same parallel transfer as attempting to create a file in that dir
        $dirs = array();
        foreach ($commands as $command) {
            $parts = array_values(array_filter(explode('/', $command['SaveAs'])));
            for ($i = 0, $total = count($parts); $i < $total; $i++) {
                $dir = '';
                for ($j = 0; $j < $i; $j++) {
                    $dir .= '/' . $parts[$j];
                }
                if ($dir && !in_array($dir, $dirs)) {
                    $dirs[] = $dir;
                }
            }
        }

        return array_filter($commands, function ($command) use ($dirs) {
            return !in_array($command['SaveAs'], $dirs);
        });
    }

    protected function transferCommands(array $commands)
    {
        parent::transferCommands($this->filterCommands($commands));
    }
}
includes/lib/aws-sdk-php-2.8.31/src/Aws/S3/Sync/DownloadSyncBuilder.php000064400000010124151327705670021166 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\S3\Sync;

use Aws\Common\Exception\RuntimeException;
use Aws\S3\ResumableDownload;
use Guzzle\Common\Event;
use Guzzle\Http\EntityBodyInterface;
use Guzzle\Service\Command\CommandInterface;

class DownloadSyncBuilder extends AbstractSyncBuilder
{
    /** @var bool */
    protected $resumable = false;

    /** @var string */
    protected $directory;

    /** @var int Number of files that can be transferred concurrently */
    protected $concurrency = 5;

    /**
     * Set the directory where the objects from be downloaded to
     *
     * @param string $directory Directory
     *
     * @return $this
     */
    public function setDirectory($directory)
    {
        $this->directory = $directory;

        return $this;
    }

    /**
     * Call this function to allow partial downloads to be resumed if the download was previously interrupted
     *
     * @return self
     */
    public function allowResumableDownloads()
    {
        $this->resumable = true;

        return $this;
    }

    protected function specificBuild()
    {
        $sync = new DownloadSync(array(
            'client'           => $this->client,
            'bucket'           => $this->bucket,
            'iterator'         => $this->sourceIterator,
            'source_converter' => $this->sourceConverter,
            'target_converter' => $this->targetConverter,
            'concurrency'      => $this->concurrency,
            'resumable'        => $this->resumable,
            'directory'        => $this->directory
        ));

        return $sync;
    }

    protected function getTargetIterator()
    {
        if (!$this->directory) {
            throw new RuntimeException('A directory is required');
        }

        if (!is_dir($this->directory) && !mkdir($this->directory, 0777, true)) {
            // @codeCoverageIgnoreStart
            throw new RuntimeException('Unable to create root download directory: ' . esc_html($this->directory));
            // @codeCoverageIgnoreEnd
        }

        return $this->filterIterator(
            new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($this->directory))
        );
    }

    protected function getDefaultSourceConverter()
    {
        return new KeyConverter(
            "s3://{$this->bucket}/{$this->baseDir}",
            $this->directory . DIRECTORY_SEPARATOR, $this->delimiter
        );
    }

    protected function getDefaultTargetConverter()
    {
        return new KeyConverter("s3://{$this->bucket}/{$this->baseDir}", '', $this->delimiter);
    }

    protected function assertFileIteratorSet()
    {
        $this->sourceIterator = $this->sourceIterator ?: $this->createS3Iterator();
    }

    protected function addDebugListener(AbstractSync $sync, $resource)
    {
        $sync->getEventDispatcher()->addListener(UploadSync::BEFORE_TRANSFER, function (Event $e) use ($resource) {
            if ($e['command'] instanceof CommandInterface) {
                $from = $e['command']['Bucket'] . '/' . $e['command']['Key'];
                $to = $e['command']['SaveAs'] instanceof EntityBodyInterface
                    ? $e['command']['SaveAs']->getUri()
                    : $e['command']['SaveAs'];
                fwrite($resource, "Downloading {$from} -> {$to}\n");
            } elseif ($e['command'] instanceof ResumableDownload) {
                $from = $e['command']->getBucket() . '/' . $e['command']->getKey();
                $to = $e['command']->getFilename();
                fwrite($resource, "Resuming {$from} -> {$to}\n");
            }
        });
    }
}
includes/lib/aws-sdk-php-2.8.31/src/Aws/S3/Sync/AbstractSync.php000064400000007745151327705670017672 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\S3\Sync;

use Aws\S3\S3Client;
use Guzzle\Common\AbstractHasDispatcher;
use Guzzle\Common\Collection;
use Guzzle\Iterator\ChunkedIterator;
use Guzzle\Service\Command\CommandInterface;

abstract class AbstractSync extends AbstractHasDispatcher
{
    const BEFORE_TRANSFER = 's3.sync.before_transfer';
    const AFTER_TRANSFER = 's3.sync.after_transfer';

    /** @var Collection */
    protected $options;

    /**
     * @param array $options Associative array of options:
     *     - client: (S3Client) used to transfer requests
     *     - bucket: (string) Amazon S3 bucket
     *     - iterator: (\Iterator) Iterator that yields SplFileInfo objects to transfer
     *     - source_converter: (FilenameConverterInterface) Converter used to convert filenames
     *     - *: Any other options required by subclasses
     */
    public function __construct(array $options)
    {
        $this->options = Collection::fromConfig(
            $options,
            array('concurrency' => 10),
            array('client', 'bucket', 'iterator', 'source_converter')
        );
        $this->init();
    }

    public static function getAllEvents()
    {
        return array(self::BEFORE_TRANSFER, self::AFTER_TRANSFER);
    }

    /**
     * Begin transferring files
     */
    public function transfer()
    {
        // Pull out chunks of uploads to upload in parallel
        $iterator = new ChunkedIterator($this->options['iterator'], $this->options['concurrency']);
        foreach ($iterator as $files) {
            $this->transferFiles($files);
        }
    }

    /**
     * Create a command or special transfer action for the
     *
     * @param \SplFileInfo $file File used to build the transfer
     *
     * @return CommandInterface|callable
     */
    abstract protected function createTransferAction(\SplFileInfo $file);

    /**
     * Hook to initialize subclasses
     * @codeCoverageIgnore
     */
    protected function init() {}

    /**
     * Process and transfer a group of files
     *
     * @param array $files Files to transfer
     */
    protected function transferFiles(array $files)
    {
        // Create the base event data object
        $event = array('sync' => $this, 'client' => $this->options['client']);

        $commands = array();
        foreach ($files as $file) {
            if ($action = $this->createTransferAction($file)) {
                $event = array('command' => $action, 'file' => $file) + $event;
                $this->dispatch(self::BEFORE_TRANSFER, $event);
                if ($action instanceof CommandInterface) {
                    $commands[] = $action;
                } elseif (is_callable($action)) {
                    $action();
                    $this->dispatch(self::AFTER_TRANSFER, $event);
                }
            }
        }

        $this->transferCommands($commands);
    }

    /**
     * Transfer an array of commands in parallel
     *
     * @param array $commands Commands to transfer
     */
    protected function transferCommands(array $commands)
    {
        if ($commands) {
            $this->options['client']->execute($commands);
            // Notify listeners that each command finished
            $event = array('sync' => $this, 'client' => $this->options['client']);
            foreach ($commands as $command) {
                $event['command'] = $command;
                $this->dispatch(self::AFTER_TRANSFER, $event);
            }
        }
    }
}
includes/lib/aws-sdk-php-2.8.31/src/Aws/S3/Sync/KeyConverter.php000064400000004421151327705670017676 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\S3\Sync;

/**
 * Converts filenames from one system to another
 */
class KeyConverter implements FilenameConverterInterface
{
    /** @var string Directory separator for Amazon S3 keys */
    protected $delimiter;

    /** @var string Prefix to prepend to each Amazon S3 object key */
    protected $prefix;

    /** @var string Base directory to remove from each file path before converting to an object key */
    protected $baseDir;

    /**
     * @param string $baseDir   Base directory to remove from each converted name
     * @param string $prefix    Amazon S3 prefix
     * @param string $delimiter Directory separator used with generated names
     */
    public function __construct($baseDir = '', $prefix = '', $delimiter = '/')
    {
        $this->baseDir = (string) $baseDir;
        $this->prefix = $prefix;
        $this->delimiter = $delimiter;
    }

    public function convert($filename)
    {
        $key = $filename;

        // Remove base directory from the key (only the first occurrence)
        if ($this->baseDir && (false !== $pos = strpos($filename, $this->baseDir))) {
            $key = substr_replace($key, '', $pos, strlen($this->baseDir));
        }

        // Replace Windows directory separators to become Unix style, and convert that to the custom dir separator
        $key = str_replace('/', $this->delimiter, str_replace('\\', '/', $key));

        // Add the key prefix and remove double slashes that are not in the protocol (e.g. prefixed with ":")
        $delim = preg_quote($this->delimiter);
        $key = preg_replace(
            "#(?<!:){$delim}{$delim}#",
            $this->delimiter,
            $this->prefix . $key
        );

        return $key;
    }
}
includes/lib/aws-sdk-php-2.8.31/src/Aws/S3/Sync/UploadSync.php000064400000005704151327705670017344 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\S3\Sync;

use Aws\Common\Exception\RuntimeException;
use Aws\S3\Model\MultipartUpload\UploadBuilder;
use Aws\S3\Model\MultipartUpload\AbstractTransfer;
use Guzzle\Http\EntityBody;

/**
 * Uploads a local directory tree to Amazon S3
 */
class UploadSync extends AbstractSync
{
    const BEFORE_MULTIPART_BUILD = 's3.sync.before_multipart_build';

    protected function init()
    {
        if (null == $this->options['multipart_upload_size']) {
            $this->options['multipart_upload_size'] = AbstractTransfer::MIN_PART_SIZE;
        }
    }

    protected function createTransferAction(\SplFileInfo $file)
    {
        // Open the file for reading
        $filename = $file->getRealPath() ?: $file->getPathName();

        if (!($resource = fopen($filename, 'r'))) {
            // @codeCoverageIgnoreStart
            throw new RuntimeException('Could not open ' . esc_html($file->getPathname()) . ' for reading');
            // @codeCoverageIgnoreEnd
        }

        $key = $this->options['source_converter']->convert($filename);
        $body = EntityBody::factory($resource);

        // Determine how the ACL should be applied
        if ($acl = $this->options['acl']) {
            $aclType = is_string($this->options['acl']) ? 'ACL' : 'ACP';
        } else {
            $acl = 'private';
            $aclType = 'ACL';
        }

        // Use a multi-part upload if the file is larger than the cutoff size and is a regular file
        if ($body->getWrapper() == 'plainfile' && $file->getSize() >= $this->options['multipart_upload_size']) {
            $builder = UploadBuilder::newInstance()
                ->setBucket($this->options['bucket'])
                ->setKey($key)
                ->setMinPartSize($this->options['multipart_upload_size'])
                ->setOption($aclType, $acl)
                ->setClient($this->options['client'])
                ->setSource($body)
                ->setConcurrency($this->options['concurrency']);

            $this->dispatch(
                self::BEFORE_MULTIPART_BUILD,
                array('builder' => $builder, 'file' => $file)
            );

            return $builder->build();
        }

        return $this->options['client']->getCommand('PutObject', array(
            'Bucket' => $this->options['bucket'],
            'Key'    => $key,
            'Body'   => $body,
            $aclType => $acl
        ));
    }
}
includes/lib/aws-sdk-php-2.8.31/src/Aws/S3/Sync/FilenameConverterInterface.php000064400000001625151327705670022512 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\S3\Sync;

/**
 * Converts filenames from one system to another (e.g. local to Amazon S3)
 */
interface FilenameConverterInterface
{
    /**
     * Convert a filename
     *
     * @param string $filename Name of the file to convert
     *
     * @return string
     */
    public function convert($filename);
}
includes/lib/aws-sdk-php-2.8.31/src/Aws/S3/Sync/AbstractSyncBuilder.php000064400000030352151327705670021167 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\S3\Sync;

use Aws\Common\Exception\RuntimeException;
use Aws\Common\Exception\UnexpectedValueException;
use Aws\S3\S3Client;
use Aws\S3\Iterator\OpendirIterator;
use Guzzle\Common\Event;
use Guzzle\Common\HasDispatcherInterface;
use Guzzle\Iterator\FilterIterator;
use Guzzle\Service\Command\CommandInterface;

abstract class AbstractSyncBuilder
{
    /** @var \Iterator Iterator that returns SplFileInfo objects to upload */
    protected $sourceIterator;

    /** @var S3Client Amazon S3 client used to send requests */
    protected $client;

    /** @var string Bucket used with the transfer */
    protected $bucket;

    /** @var int Number of files that can be transferred concurrently */
    protected $concurrency = 10;

    /** @var array Custom parameters to add to each operation sent while transferring */
    protected $params = array();

    /** @var FilenameConverterInterface */
    protected $sourceConverter;

    /** @var FilenameConverterInterface */
    protected $targetConverter;

    /** @var string Prefix at prepend to each Amazon S3 object key */
    protected $keyPrefix = '';

    /** @var string Directory separator for Amazon S3 keys */
    protected $delimiter = '/';

    /** @var string Base directory to remove from each file path before converting to an object name or file name */
    protected $baseDir;

    /** @var bool Whether or not to only transfer modified or new files */
    protected $forcing = false;

    /** @var bool Whether or not debug output is enable */
    protected $debug;

    /**
     * @return static
     */
    public static function getInstance()
    {
        return new static();
    }

    /**
     * Set the bucket to use with the sync
     *
     * @param string $bucket Amazon S3 bucket name
     *
     * @return $this
     */
    public function setBucket($bucket)
    {
        $this->bucket = $bucket;

        return $this;
    }

    /**
     * Set the Amazon S3 client object that will send requests
     *
     * @param S3Client $client Amazon S3 client
     *
     * @return $this
     */
    public function setClient(S3Client $client)
    {
        $this->client = $client;

        return $this;
    }

    /**
     * Set a custom iterator that returns \SplFileInfo objects for the source data
     *
     * @param \Iterator $iterator
     *
     * @return $this
     */
    public function setSourceIterator(\Iterator $iterator)
    {
        $this->sourceIterator = $iterator;

        return $this;
    }

    /**
     * Set a custom object key provider instead of building one internally
     *
     * @param FileNameConverterInterface $converter Filename to object key provider
     *
     * @return $this
     */
    public function setSourceFilenameConverter(FilenameConverterInterface $converter)
    {
        $this->sourceConverter = $converter;

        return $this;
    }

    /**
     * Set a custom object key provider instead of building one internally
     *
     * @param FileNameConverterInterface $converter Filename to object key provider
     *
     * @return $this
     */
    public function setTargetFilenameConverter(FilenameConverterInterface $converter)
    {
        $this->targetConverter = $converter;

        return $this;
    }

    /**
     * Set the base directory of the files being transferred. The base directory is removed from each file path before
     * converting the file path to an object key or vice versa.
     *
     * @param string $baseDir Base directory, which will be deleted from each uploaded object key
     *
     * @return $this
     */
    public function setBaseDir($baseDir)
    {
        $this->baseDir = $baseDir;

        return $this;
    }

    /**
     * Specify a prefix to prepend to each Amazon S3 object key or the prefix where object are stored in a bucket
     *
     * Can be used to upload files to a pseudo sub-folder key or only download files from a pseudo sub-folder
     *
     * @param string $keyPrefix Prefix for each uploaded key
     *
     * @return $this
     */
    public function setKeyPrefix($keyPrefix)
    {
        // Removing leading slash
        $this->keyPrefix = ltrim($keyPrefix, '/');

        return $this;
    }

    /**
     * Specify the delimiter used for the targeted filesystem (default delimiter is "/")
     *
     * @param string $delimiter Delimiter to use to separate paths
     *
     * @return $this
     */
    public function setDelimiter($delimiter)
    {
        $this->delimiter = $delimiter;

        return $this;
    }

    /**
     * Specify an array of operation parameters to apply to each operation executed by the sync object
     *
     * @param array $params Associative array of PutObject (upload) GetObject (download) parameters
     *
     * @return $this
     */
    public function setOperationParams(array $params)
    {
        $this->params = $params;

        return $this;
    }

    /**
     * Set the number of files that can be transferred concurrently
     *
     * @param int $concurrency Number of concurrent transfers
     *
     * @return $this
     */
    public function setConcurrency($concurrency)
    {
        $this->concurrency = $concurrency;

        return $this;
    }

    /**
     * Set to true to force transfers even if a file already exists and has not changed
     *
     * @param bool $force Set to true to force transfers without checking if it has changed
     *
     * @return $this
     */
    public function force($force = false)
    {
        $this->forcing = (bool) $force;

        return $this;
    }

    /**
     * Enable debug mode
     *
     * @param bool|resource $enabledOrResource Set to true or false to enable or disable debug output. Pass an opened
     *                                         fopen resource to write to instead of writing to standard out.
     * @return $this
     */
    public function enableDebugOutput($enabledOrResource = true)
    {
        $this->debug = $enabledOrResource;

        return $this;
    }

    /**
     * Add a filename filter that uses a regular expression to filter out files that you do not wish to transfer.
     *
     * @param string $search Regular expression search (in preg_match format). Any filename that matches this regex
     *                       will not be transferred.
     * @return $this
     */
    public function addRegexFilter($search)
    {
        $this->assertFileIteratorSet();
        $this->sourceIterator = new FilterIterator($this->sourceIterator, function ($i) use ($search) {
            return !preg_match($search, (string) $i);
        });
        $this->sourceIterator->rewind();

        return $this;
    }

    /**
     * Builds a UploadSync or DownloadSync object
     *
     * @return AbstractSync
     */
    public function build()
    {
        $this->validateRequirements();
        $this->sourceConverter = $this->sourceConverter ?: $this->getDefaultSourceConverter();
        $this->targetConverter = $this->targetConverter ?: $this->getDefaultTargetConverter();

        // Only wrap the source iterator in a changed files iterator if we are not forcing the transfers
        if (!$this->forcing) {
            $this->sourceIterator->rewind();
            $this->sourceIterator = new ChangedFilesIterator(
                new \NoRewindIterator($this->sourceIterator),
                $this->getTargetIterator(),
                $this->sourceConverter,
                $this->targetConverter
            );
            $this->sourceIterator->rewind();
        }

        $sync = $this->specificBuild();

        if ($this->params) {
            $this->addCustomParamListener($sync);
        }

        if ($this->debug) {
            $this->addDebugListener($sync, is_bool($this->debug) ? STDOUT : $this->debug);
        }

        return $sync;
    }

    /**
     * Hook to implement in subclasses
     *
     * @return AbstractSync
     */
    abstract protected function specificBuild();

    /**
     * @return \Iterator
     */
    abstract protected function getTargetIterator();

    /**
     * @return FilenameConverterInterface
     */
    abstract protected function getDefaultSourceConverter();

    /**
     * @return FilenameConverterInterface
     */
    abstract protected function getDefaultTargetConverter();

    /**
     * Add a listener to the sync object to output debug information while transferring
     *
     * @param AbstractSync $sync     Sync object to listen to
     * @param resource     $resource Where to write debug messages
     */
    abstract protected function addDebugListener(AbstractSync $sync, $resource);

    /**
     * Validate that the builder has the minimal requirements
     *
     * @throws RuntimeException if the builder is not configured completely
     */
    protected function validateRequirements()
    {
        if (!$this->client) {
            throw new RuntimeException('No client was provided');
        }
        if (!$this->bucket) {
            throw new RuntimeException('No bucket was provided');
        }
        $this->assertFileIteratorSet();
    }

    /**
     * Ensure that the base file iterator has been provided
     *
     * @throws RuntimeException
     */
    protected function assertFileIteratorSet()
    {
        // Interesting... Need to use isset because: Object of class GlobIterator could not be converted to boolean
        if (!isset($this->sourceIterator)) {
            throw new RuntimeException('A source file iterator must be specified');
        }
    }

    /**
     * Wraps a generated iterator in a filter iterator that removes directories
     *
     * @param \Iterator $iterator Iterator to wrap
     *
     * @return \Iterator
     * @throws UnexpectedValueException
     */
    protected function filterIterator(\Iterator $iterator)
    {
        $f = new FilterIterator($iterator, function ($i) {
            if (!$i instanceof \SplFileInfo) {
                throw new UnexpectedValueException('All iterators for UploadSync must return SplFileInfo objects');
            }
            return $i->isFile();
        });

        $f->rewind();

        return $f;
    }

    /**
     * Add the custom param listener to a transfer object
     *
     * @param HasDispatcherInterface $sync
     */
    protected function addCustomParamListener(HasDispatcherInterface $sync)
    {
        $params = $this->params;
        $sync->getEventDispatcher()->addListener(
            UploadSync::BEFORE_TRANSFER,
            function (Event $e) use ($params) {
                if ($e['command'] instanceof CommandInterface) {
                    $e['command']->overwriteWith($params);
                }
            }
        );
    }

    /**
     * Create an Amazon S3 file iterator based on the given builder settings
     *
     * @return OpendirIterator
     */
    protected function createS3Iterator()
    {
        // Ensure that the stream wrapper is registered
        $this->client->registerStreamWrapper();

        // Calculate the opendir() bucket and optional key prefix location
        $dir = "s3://{$this->bucket}";
        if ($this->keyPrefix) {
            $dir .= '/' . ltrim($this->keyPrefix, '/ ');
        }

        // Use opendir so that we can pass stream context to the iterator
        $dh = opendir($dir, stream_context_create(array(
            's3' => array(
                'delimiter'  => '',
                'listFilter' => function ($obj) {
                    // Ensure that we do not try to download a glacier object.
                    return !isset($obj['StorageClass']) ||
                        $obj['StorageClass'] != 'GLACIER';
                }
            )
        )));

        // Add the trailing slash for the OpendirIterator concatenation
        if (!$this->keyPrefix) {
            $dir .= '/';
        }

        return $this->filterIterator(new \NoRewindIterator(new OpendirIterator($dh, $dir)));
    }
}
includes/lib/aws-sdk-php-2.8.31/src/Aws/S3/Sync/UploadSyncBuilder.php000064400000013221151327705670020644 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\S3\Sync;

use FilesystemIterator as FI;
use Aws\Common\Model\MultipartUpload\AbstractTransfer;
use Aws\S3\Model\Acp;
use Guzzle\Common\HasDispatcherInterface;
use Guzzle\Common\Event;
use Guzzle\Service\Command\CommandInterface;

class UploadSyncBuilder extends AbstractSyncBuilder
{
    /** @var string|Acp Access control policy to set on each object */
    protected $acp = 'private';

    /** @var int */
    protected $multipartUploadSize;

    /**
     * Set the path that contains files to recursively upload to Amazon S3
     *
     * @param string $path Path that contains files to upload
     *
     * @return $this
     */
    public function uploadFromDirectory($path)
    {
        $this->baseDir = realpath($path);
        $this->sourceIterator = $this->filterIterator(new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator(
            $path,
            FI::SKIP_DOTS | FI::UNIX_PATHS | FI::FOLLOW_SYMLINKS
        )));

        return $this;
    }

    /**
     * Set a glob expression that will match files to upload to Amazon S3
     *
     * @param string $glob Glob expression
     *
     * @return $this
     * @link http://www.php.net/manual/en/function.glob.php
     */
    public function uploadFromGlob($glob)
    {
        $this->sourceIterator = $this->filterIterator(
            new \GlobIterator($glob, FI::SKIP_DOTS | FI::UNIX_PATHS | FI::FOLLOW_SYMLINKS)
        );

        return $this;
    }

    /**
     * Set a canned ACL to apply to each uploaded object
     *
     * @param string $acl Canned ACL for each upload
     *
     * @return $this
     */
    public function setAcl($acl)
    {
        $this->acp = $acl;

        return $this;
    }

    /**
     * Set an Access Control Policy to apply to each uploaded object
     *
     * @param Acp $acp Access control policy
     *
     * @return $this
     */
    public function setAcp(Acp $acp)
    {
        $this->acp = $acp;

        return $this;
    }

    /**
     * Set the multipart upload size threshold. When the size of a file exceeds this value, the file will be uploaded
     * using a multipart upload.
     *
     * @param int $size Size threshold
     *
     * @return $this
     */
    public function setMultipartUploadSize($size)
    {
        $this->multipartUploadSize = $size;

        return $this;
    }

    protected function specificBuild()
    {
        $sync = new UploadSync(array(
            'client' => $this->client,
            'bucket' => $this->bucket,
            'iterator' => $this->sourceIterator,
            'source_converter' => $this->sourceConverter,
            'target_converter' => $this->targetConverter,
            'concurrency' => $this->concurrency,
            'multipart_upload_size' => $this->multipartUploadSize,
            'acl' => $this->acp
        ));

        return $sync;
    }

    protected function addCustomParamListener(HasDispatcherInterface $sync)
    {
        // Handle the special multi-part upload event
        parent::addCustomParamListener($sync);
        $params = $this->params;
        $sync->getEventDispatcher()->addListener(
            UploadSync::BEFORE_MULTIPART_BUILD,
            function (Event $e) use ($params) {
                foreach ($params as $k => $v) {
                    $e['builder']->setOption($k, $v);
                }
            }
        );
    }

    protected function getTargetIterator()
    {
        return $this->createS3Iterator();
    }

    protected function getDefaultSourceConverter()
    {
        return new KeyConverter($this->baseDir, $this->keyPrefix . $this->delimiter, $this->delimiter);
    }

    protected function getDefaultTargetConverter()
    {
        return new KeyConverter('s3://' . $this->bucket . '/', '', DIRECTORY_SEPARATOR);
    }

    protected function addDebugListener(AbstractSync $sync, $resource)
    {
        $sync->getEventDispatcher()->addListener(UploadSync::BEFORE_TRANSFER, function (Event $e) use ($resource) {

            $c = $e['command'];

            if ($c instanceof CommandInterface) {
                $uri = $c['Body']->getUri();
                $size = $c['Body']->getSize();
                fwrite($resource, "Uploading {$uri} -> {$c['Key']} ({$size} bytes)\n");
                return;
            }

            // Multipart upload
            $body = $c->getSource();
            $totalSize = $body->getSize();
            $progress = 0;
            fwrite($resource, "Beginning multipart upload: " . $body->getUri() . ' -> ');
            fwrite($resource, $c->getState()->getFromId('Key') . " ({$totalSize} bytes)\n");

            $c->getEventDispatcher()->addListener(
                AbstractTransfer::BEFORE_PART_UPLOAD,
                function ($e) use (&$progress, $totalSize, $resource) {
                    $command = $e['command'];
                    $size = $command['Body']->getContentLength();
                    $percentage = number_format(($progress / $totalSize) * 100, 2);
                    fwrite($resource, "- Part {$command['PartNumber']} ({$size} bytes, {$percentage}%)\n");
                    $progress += $size;
                }
            );
        });
    }
}
includes/lib/aws-sdk-php-2.8.31/src/Aws/S3/Sync/ChangedFilesIterator.php000064400000007557151327705670021321 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\S3\Sync;

/**
 * Iterator used to filter an internal iterator to only yield files that do not exist in the target iterator or files
 * that have changed
 */
class ChangedFilesIterator extends \FilterIterator
{
    /** @var \Iterator */
    protected $sourceIterator;

    /** @var \Iterator */
    protected $targetIterator;

    /** @var FilenameConverterInterface */
    protected $sourceConverter;

    /** @var FilenameConverterInterface */
    protected $targetConverter;

    /** @var array Previously loaded data */
    protected $cache = array();

    /**
     * @param \Iterator                  $sourceIterator  Iterator to wrap and filter
     * @param \Iterator                  $targetIterator  Iterator used to compare against the source iterator
     * @param FilenameConverterInterface $sourceConverter Key converter to convert source to target keys
     * @param FilenameConverterInterface $targetConverter Key converter to convert target to source keys
     */
    public function __construct(
        \Iterator $sourceIterator,
        \Iterator $targetIterator,
        FilenameConverterInterface $sourceConverter,
        FilenameConverterInterface $targetConverter
    ) {
        $this->targetIterator = $targetIterator;
        $this->sourceConverter = $sourceConverter;
        $this->targetConverter = $targetConverter;
        parent::__construct($sourceIterator);
    }

    public function accept()
    {
        $current = $this->current();
        $key = $this->sourceConverter->convert($this->normalize($current));

        if (!($data = $this->getTargetData($key))) {
            return true;
        }

        // Ensure the Content-Length matches and it hasn't been modified since the mtime
        return $current->getSize() != $data[0] || $current->getMTime() > $data[1];
    }

    /**
     * Returns an array of the files from the target iterator that were not found in the source iterator
     *
     * @return array
     */
    public function getUnmatched()
    {
        return array_keys($this->cache);
    }

    /**
     * Get key information from the target iterator for a particular filename
     *
     * @param string $key Target iterator filename
     *
     * @return array|bool Returns an array of data, or false if the key is not in the iterator
     */
    protected function getTargetData($key)
    {
        $key = $this->cleanKey($key);

        if (isset($this->cache[$key])) {
            $result = $this->cache[$key];
            unset($this->cache[$key]);
            return $result;
        }

        $it = $this->targetIterator;

        while ($it->valid()) {
            $value = $it->current();
            $data = array($value->getSize(), $value->getMTime());
            $filename = $this->targetConverter->convert($this->normalize($value));
            $filename = $this->cleanKey($filename);

            if ($filename == $key) {
                return $data;
            }

            $this->cache[$filename] = $data;
            $it->next();
        }

        return false;
    }

    private function normalize($current)
    {
        $asString = (string) $current;

        return strpos($asString, 's3://') === 0
            ? $asString
            : $current->getRealPath();
    }

    private function cleanKey($key)
    {
        return ltrim($key, '/');
    }
}
includes/lib/aws-sdk-php-2.8.31/src/Aws/S3/SocketTimeoutChecker.php000064400000004321151327705670020425 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\S3;

use Guzzle\Http\Exception\HttpException;
use Guzzle\Http\Message\RequestInterface;
use Guzzle\Http\Message\EntityEnclosingRequestInterface;
use Guzzle\Http\Message\Response;
use Guzzle\Plugin\Backoff\BackoffStrategyInterface;
use Guzzle\Plugin\Backoff\AbstractBackoffStrategy;

/**
 * Custom S3 exponential backoff checking use to retry 400 responses containing the following reason phrase:
 * "Your socket connection to the server was not read from or written to within the timeout period.".
 * This error has been reported as intermittent/random, and in most cases, seems to occur during the middle of a
 * transfer. This plugin will attempt to retry these failed requests, and if using a local file, will clear the
 * stat cache of the file and set a new content-length header on the upload.
 */
class SocketTimeoutChecker extends AbstractBackoffStrategy
{
    const ERR = 'Your socket connection to the server was not read from or written to within the timeout period';

    /**
     * {@inheridoc}
     */
    public function __construct(BackoffStrategyInterface $next = null)
    {
        if ($next) {
            $this->setNext($next);
        }
    }

    /**
     * {@inheridoc}
     */
    public function makesDecision()
    {
        return true;
    }

    /**
     * {@inheritdoc}
     */
    protected function getDelay(
        $retries,
        RequestInterface $request,
        Response $response = null,
        HttpException $e = null
    ) {
        if ($response
            && $response->getStatusCode() == 400
            && strpos($response->getBody(), self::ERR)
        ) {
            return true;
        }
    }
}
includes/lib/aws-sdk-php-2.8.31/src/Aws/S3/Exception/NoSuchLifecycleConfigurationException.php000064400000001347151327705670025732 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\S3\Exception;

/**
 * The lifecycle configuration does not exist.
 */
class NoSuchLifecycleConfigurationException extends S3Exception {}
includes/lib/aws-sdk-php-2.8.31/src/Aws/S3/Exception/UserKeyMustBeSpecifiedException.php000064400000001446151327705670024506 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\S3\Exception;

/**
 * The bucket POST must contain the specified field name. If it is specified, please check the order of the fields.
 */
class UserKeyMustBeSpecifiedException extends S3Exception {}
includes/lib/aws-sdk-php-2.8.31/src/Aws/S3/Exception/InvalidBucketNameException.php000064400000001323151327705670023502 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\S3\Exception;

/**
 * The specified bucket is not valid.
 */
class InvalidBucketNameException extends S3Exception {}
includes/lib/aws-sdk-php-2.8.31/src/Aws/S3/Exception/BucketAlreadyExistsException.php000064400000001507151327705670024100 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\S3\Exception;

/**
 * The requested bucket name is not available. The bucket namespace is shared by all users of the system. Please select a different name and try again.
 */
class BucketAlreadyExistsException extends S3Exception {}
includes/lib/aws-sdk-php-2.8.31/src/Aws/S3/Exception/AmbiguousGrantByEmailAddressException.php000064400000001405151327705670025656 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\S3\Exception;

/**
 * The e-mail address you provided is associated with more than one account.
 */
class AmbiguousGrantByEmailAddressException extends S3Exception {}
includes/lib/aws-sdk-php-2.8.31/src/Aws/S3/Exception/DeleteMultipleObjectsException.php000064400000002545151327705670024414 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\S3\Exception;

/**
 * Exception thrown when errors occur in a DeleteMultipleObjects request
 */
class DeleteMultipleObjectsException extends S3Exception
{
    /**
     * @var array Array of errors
     */
    protected $errors = array();

    /**
     * @param array $errors Array of errors
     */
    public function __construct(array $errors = array())
    {
        parent::__construct('Unable to delete certain keys when executing a DeleteMultipleObjects request');
        $this->errors = $errors;
    }

    /**
     * Get the errored objects
     *
     * @return array Returns an array of associative arrays, each containing
     *               a 'Code', 'Message', and 'Key' key.
     */
    public function getErrors()
    {
        return $this->errors;
    }
}
includes/lib/aws-sdk-php-2.8.31/src/Aws/S3/Exception/InlineDataTooLargeException.php000064400000001337151327705670023627 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\S3\Exception;

/**
 * Inline data exceeds the maximum allowed size.
 */
class InlineDataTooLargeException extends S3Exception {}
includes/lib/aws-sdk-php-2.8.31/src/Aws/S3/Exception/ServiceUnavailableException.php000064400000001322151327705670023720 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\S3\Exception;

/**
 * Please reduce your request rate.
 */
class ServiceUnavailableException extends S3Exception {}
includes/lib/aws-sdk-php-2.8.31/src/Aws/S3/Exception/NoSuchCORSConfigurationException.php000064400000001357151327705670024602 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\S3\Exception;

/**
 * The specified bucket does not have a CORs configuration.
 */
class NoSuchCORSConfigurationException extends S3Exception {}
includes/lib/aws-sdk-php-2.8.31/src/Aws/S3/Exception/CrossLocationLoggingProhibitedException.php000064400000001474151327705670026267 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\S3\Exception;

/**
 * Cross location logging not allowed. Buckets in one geographic location cannot log information to a bucket in another location.
 */
class CrossLocationLoggingProhibitedException extends S3Exception {}
includes/lib/aws-sdk-php-2.8.31/src/Aws/S3/Exception/MalformedXMLException.php000064400000001626151327705670022452 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\S3\Exception;

/**
 * This happens when the user sends a malformed xml (xml that doesn't conform to the published xsd) for the configuration. The error message is, "The XML you provided was not well-formed or did not validate against our published schema."
 */
class MalformedXMLException extends S3Exception {}
includes/lib/aws-sdk-php-2.8.31/src/Aws/S3/Exception/BadDigestException.php000064400000001346151327705670022010 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\S3\Exception;

/**
 * The Content-MD5 you specified did not match what we received.
 */
class BadDigestException extends S3Exception {}
includes/lib/aws-sdk-php-2.8.31/src/Aws/S3/Exception/ObjectAlreadyInActiveTierErrorException.php000064400000001365151327705670026154 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\S3\Exception;

/**
 * This operation is not allowed against this storage tier
 */
class ObjectAlreadyInActiveTierErrorException extends S3Exception {}
includes/lib/aws-sdk-php-2.8.31/src/Aws/S3/Exception/SlowDownException.php000064400000001310151327705670021725 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\S3\Exception;

/**
 * Please reduce your request rate.
 */
class SlowDownException extends S3Exception {}
includes/lib/aws-sdk-php-2.8.31/src/Aws/S3/Exception/InvalidRequestException.php000064400000001342151327705670023115 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\S3\Exception;

/**
 * SOAP requests must be made over an HTTPS connection.
 */
class InvalidRequestException extends S3Exception {}
includes/lib/aws-sdk-php-2.8.31/src/Aws/S3/Exception/NotImplementedException.php000064400000001362151327705670023104 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\S3\Exception;

/**
 * A header you provided implies functionality that is not implemented.
 */
class NotImplementedException extends S3Exception {}
includes/lib/aws-sdk-php-2.8.31/src/Aws/S3/Exception/MissingSecurityHeaderException.php000064400000001340151327705670024426 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\S3\Exception;

/**
 * Your request was missing a required header.
 */
class MissingSecurityHeaderException extends S3Exception {}
includes/lib/aws-sdk-php-2.8.31/src/Aws/S3/Exception/InvalidURIException.php000064400000001313151327705670022122 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\S3\Exception;

/**
 * Couldn't parse the specified URI.
 */
class InvalidURIException extends S3Exception {}
includes/lib/aws-sdk-php-2.8.31/src/Aws/S3/Exception/NoLoggingStatusForKeyException.php000064400000001367151327705670024374 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\S3\Exception;

/**
 * There is no such thing as a logging status sub-resource for a key.
 */
class NoLoggingStatusForKeyException extends S3Exception {}
includes/lib/aws-sdk-php-2.8.31/src/Aws/S3/Exception/InternalErrorException.php000064400000001340151327705670022742 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\S3\Exception;

/**
 * We encountered an internal error. Please try again.
 */
class InternalErrorException extends S3Exception {}
includes/lib/aws-sdk-php-2.8.31/src/Aws/S3/Exception/NoSuchBucketException.php000064400000001320151327705670022507 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\S3\Exception;

/**
 * The specified bucket does not exist.
 */
class NoSuchBucketException extends S3Exception {}
includes/lib/aws-sdk-php-2.8.31/src/Aws/S3/Exception/MalformedACLErrorException.php000064400000001413151327705670023415 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\S3\Exception;

/**
 * The XML you provided was not well-formed or did not validate against our published schema.
 */
class MalformedACLErrorException extends S3Exception {}
includes/lib/aws-sdk-php-2.8.31/src/Aws/S3/Exception/PermanentRedirectException.php000064400000001472151327705670023575 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\S3\Exception;

/**
 * The bucket you are attempting to access must be addressed using the specified endpoint. Please send all future requests to this endpoint.
 */
class PermanentRedirectException extends S3Exception {}
includes/lib/aws-sdk-php-2.8.31/src/Aws/S3/Exception/NotSignedUpException.php000064400000001521151327705670022354 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\S3\Exception;

/**
 * Your account is not signed up for the Amazon S3 service. You must sign up before you can use Amazon S3. You can sign up at the following URL: http://aws.amazon.com/s3
 */
class NotSignedUpException extends S3Exception {}
includes/lib/aws-sdk-php-2.8.31/src/Aws/S3/Exception/AccountProblemException.php000064400000001447151327705670023101 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\S3\Exception;

/**
 * There is a problem with your AWS account that prevents the operation from completing successfully. Please use Contact Us.
 */
class AccountProblemException extends S3Exception {}
includes/lib/aws-sdk-php-2.8.31/src/Aws/S3/Exception/InvalidTokenException.php000064400000001341151327705670022544 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\S3\Exception;

/**
 * The provided token is malformed or otherwise invalid.
 */
class InvalidTokenException extends S3Exception {}
includes/lib/aws-sdk-php-2.8.31/src/Aws/S3/Exception/UnresolvableGrantByEmailAddressException.php000064400000001404151327705670026363 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\S3\Exception;

/**
 * The e-mail address you provided does not match any account on record.
 */
class UnresolvableGrantByEmailAddressException extends S3Exception {}
includes/lib/aws-sdk-php-2.8.31/src/Aws/S3/Exception/NoSuchVersionException.php000064400000001407151327705670022725 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\S3\Exception;

/**
 * Indicates that the version ID specified in the request does not match an existing version.
 */
class NoSuchVersionException extends S3Exception {}
includes/lib/aws-sdk-php-2.8.31/src/Aws/S3/Exception/InvalidTargetBucketForLoggingException.php000064400000001503151327705670026026 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\S3\Exception;

/**
 * The target bucket for logging does not exist, is not owned by you, or does not have the appropriate grants for the log-delivery group.
 */
class InvalidTargetBucketForLoggingException extends S3Exception {}
includes/lib/aws-sdk-php-2.8.31/src/Aws/S3/Exception/MissingRequestBodyErrorException.php000064400000001452151327705670024772 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\S3\Exception;

/**
 * This happens when the user sends an empty xml document as a request. The error message is, "Request body is empty."
 */
class MissingRequestBodyErrorException extends S3Exception {}
includes/lib/aws-sdk-php-2.8.31/src/Aws/S3/Exception/InvalidStorageClassException.php000064400000001340151327705670024055 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\S3\Exception;

/**
 * The storage class you specified is not valid.
 */
class InvalidStorageClassException extends S3Exception {}
includes/lib/aws-sdk-php-2.8.31/src/Aws/S3/Exception/PreconditionFailedException.php000064400000001357151327705670023726 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\S3\Exception;

/**
 * At least one of the preconditions you specified did not hold.
 */
class PreconditionFailedException extends S3Exception {}
includes/lib/aws-sdk-php-2.8.31/src/Aws/S3/Exception/RequestTimeoutException.php000064400000001415151327705670023156 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\S3\Exception;

/**
 * Your socket connection to the server was not read from or written to within the timeout period.
 */
class RequestTimeoutException extends S3Exception {}
includes/lib/aws-sdk-php-2.8.31/src/Aws/S3/Exception/InvalidSecurityException.php000064400000001337151327705670023300 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\S3\Exception;

/**
 * The provided security credentials are not valid.
 */
class InvalidSecurityException extends S3Exception {}
includes/lib/aws-sdk-php-2.8.31/src/Aws/S3/Exception/OperationAbortedException.php000064400000001425151327705670023421 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\S3\Exception;

/**
 * A conflicting conditional operation is currently in progress against this resource. Please try again.
 */
class OperationAbortedException extends S3Exception {}
includes/lib/aws-sdk-php-2.8.31/src/Aws/S3/Exception/ExpiredTokenException.php000064400000001313151327705670022555 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\S3\Exception;

/**
 * The provided token has expired.
 */
class ExpiredTokenException extends S3Exception {}
includes/lib/aws-sdk-php-2.8.31/src/Aws/S3/Exception/MaxMessageLengthExceededException.php000064400000001321151327705670024776 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\S3\Exception;

/**
 * Your request was too big.
 */
class MaxMessageLengthExceededException extends S3Exception {}
includes/lib/aws-sdk-php-2.8.31/src/Aws/S3/Exception/InvalidRangeException.php000064400000001324151327705670022521 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\S3\Exception;

/**
 * The requested range cannot be satisfied.
 */
class InvalidRangeException extends S3Exception {}
lib/aws-sdk-php-2.8.31/src/Aws/S3/Exception/IncorrectNumberOfFilesInPostRequestException.php000064400000001365151327705670027163 0ustar00includes<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\S3\Exception;

/**
 * POST requires exactly one file upload per request.
 */
class IncorrectNumberOfFilesInPostRequestException extends S3Exception {}
includes/lib/aws-sdk-php-2.8.31/src/Aws/S3/Exception/TokenRefreshRequiredException.php000064400000001331151327705670024254 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\S3\Exception;

/**
 * The provided token must be refreshed.
 */
class TokenRefreshRequiredException extends S3Exception {}
includes/lib/aws-sdk-php-2.8.31/src/Aws/S3/Exception/MissingAttachmentException.php000064400000001345151327705670023603 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\S3\Exception;

/**
 * A SOAP attachment was expected, but none were found.
 */
class MissingAttachmentException extends S3Exception {}
includes/lib/aws-sdk-php-2.8.31/src/Aws/S3/Exception/UnexpectedContentException.php000064400000001327151327705670023620 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\S3\Exception;

/**
 * This request does not support content.
 */
class UnexpectedContentException extends S3Exception {}
includes/lib/aws-sdk-php-2.8.31/src/Aws/S3/Exception/BucketNotEmptyException.php000064400000001332151327705670023072 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\S3\Exception;

/**
 * The bucket you tried to delete is not empty.
 */
class BucketNotEmptyException extends S3Exception {}
includes/lib/aws-sdk-php-2.8.31/src/Aws/S3/Exception/ObjectNotInActiveTierErrorException.php000064400000001441151327705670025326 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\S3\Exception;

/**
 * The source object of the COPY operation is not in the active tier and is only stored in Amazon Glacier.
 */
class ObjectNotInActiveTierErrorException extends S3Exception {}
includes/lib/aws-sdk-php-2.8.31/src/Aws/S3/Exception/MissingSecurityElementException.php000064400000001351151327705670024631 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\S3\Exception;

/**
 * The SOAP 1.1 request is missing a security element.
 */
class MissingSecurityElementException extends S3Exception {}
includes/lib/aws-sdk-php-2.8.31/src/Aws/S3/Exception/InvalidArgumentException.php000064400000001277151327705670023256 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\S3\Exception;

/**
 * Invalid Argument
 */
class InvalidArgumentException extends S3Exception {}
includes/lib/aws-sdk-php-2.8.31/src/Aws/S3/Exception/NoSuchKeyException.php000064400000001312151327705670022023 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\S3\Exception;

/**
 * The specified key does not exist.
 */
class NoSuchKeyException extends S3Exception {}
includes/lib/aws-sdk-php-2.8.31/src/Aws/S3/Exception/EntityTooLargeException.php000064400000001353151327705670023071 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\S3\Exception;

/**
 * Your proposed upload exceeds the maximum allowed object size.
 */
class EntityTooLargeException extends S3Exception {}
includes/lib/aws-sdk-php-2.8.31/src/Aws/S3/Exception/CredentialsNotSupportedException.php000064400000001341151327705670025001 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\S3\Exception;

/**
 * This request does not support credentials.
 */
class CredentialsNotSupportedException extends S3Exception {}
includes/lib/aws-sdk-php-2.8.31/src/Aws/S3/Exception/NoSuchWebsiteConfigurationException.php000064400000001365151327705670025435 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\S3\Exception;

/**
 * The specified bucket does not have a website configuration.
 */
class NoSuchWebsiteConfigurationException extends S3Exception {}
includes/lib/aws-sdk-php-2.8.31/src/Aws/S3/Exception/S3Exception.php000064400000001402151327705670020440 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\S3\Exception;

use Aws\Common\Exception\ServiceResponseException;

/**
 * Default service exception class
 */
class S3Exception extends ServiceResponseException {}
includes/lib/aws-sdk-php-2.8.31/src/Aws/S3/Exception/InvalidAddressingHeaderException.php000064400000001333151327705670024661 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\S3\Exception;

/**
 * You must specify the Anonymous role.
 */
class InvalidAddressingHeaderException extends S3Exception {}
includes/lib/aws-sdk-php-2.8.31/src/Aws/S3/Exception/TooManyBucketsException.php000064400000001345151327705670023070 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\S3\Exception;

/**
 * You have attempted to create more buckets than allowed.
 */
class TooManyBucketsException extends S3Exception {}
includes/lib/aws-sdk-php-2.8.31/src/Aws/S3/Exception/TemporaryRedirectException.php000064400000001352151327705670023623 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\S3\Exception;

/**
 * You are being redirected to the bucket while DNS updates.
 */
class TemporaryRedirectException extends S3Exception {}
includes/lib/aws-sdk-php-2.8.31/src/Aws/S3/Exception/IllegalVersioningConfigurationException.php000064400000001416151327705670026325 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\S3\Exception;

/**
 * Indicates that the Versioning configuration specified in the request is invalid.
 */
class IllegalVersioningConfigurationException extends S3Exception {}
includes/lib/aws-sdk-php-2.8.31/src/Aws/S3/Exception/RequestTimeTooSkewedException.php000064400000001377151327705670024262 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\S3\Exception;

/**
 * The difference between the request time and the server's time is too large.
 */
class RequestTimeTooSkewedException extends S3Exception {}
includes/lib/aws-sdk-php-2.8.31/src/Aws/S3/Exception/IncompleteBodyException.php000064400000001401151327705670023067 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\S3\Exception;

/**
 * You did not provide the number of bytes specified by the Content-Length HTTP header
 */
class IncompleteBodyException extends S3Exception {}
includes/lib/aws-sdk-php-2.8.31/src/Aws/S3/Exception/InvalidLocationConstraintException.php000064400000001471151327705670025305 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\S3\Exception;

/**
 * The specified location constraint is not valid. For more information about Regions, see How to Select a Region for Your Buckets.
 */
class InvalidLocationConstraintException extends S3Exception {}
includes/lib/aws-sdk-php-2.8.31/src/Aws/S3/Exception/AccessDeniedException.php000064400000001271151327705670022471 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\S3\Exception;

/**
 * Access Denied
 */
class AccessDeniedException extends S3Exception {}
includes/lib/aws-sdk-php-2.8.31/src/Aws/S3/Exception/InvalidBucketStateException.php000064400000001360151327705670023703 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\S3\Exception;

/**
 * The request is not valid with the current state of the bucket.
 */
class InvalidBucketStateException extends S3Exception {}
includes/lib/aws-sdk-php-2.8.31/src/Aws/S3/Exception/NoSuchTagSetException.php000064400000001332151327705670022464 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\S3\Exception;

/**
 * There is no TagSet associated with the bucket.
 */
class NoSuchTagSetException extends S3Exception {}
includes/lib/aws-sdk-php-2.8.31/src/Aws/S3/Exception/InvalidTagErrorException.php000064400000001515151327705670023214 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\S3\Exception;

/**
 * The Tag provided was not a valid tag. This can occur if the Tag did not pass input validation. See the
 * CostAllocation docs for a description of valid tags.
 */
class InvalidTagErrorException extends S3Exception {}
includes/lib/aws-sdk-php-2.8.31/src/Aws/S3/Exception/MissingContentLengthException.php000064400000001344151327705670024266 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\S3\Exception;

/**
 * You must provide the Content-Length HTTP header.
 */
class MissingContentLengthException extends S3Exception {}
includes/lib/aws-sdk-php-2.8.31/src/Aws/S3/Exception/RedirectException.php000064400000001273151327705670021722 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\S3\Exception;

/**
 * Temporary redirect.
 */
class RedirectException extends S3Exception {}
includes/lib/aws-sdk-php-2.8.31/src/Aws/S3/Exception/NoSuchBucketPolicyException.php000064400000001335151327705670023675 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\S3\Exception;

/**
 * The specified bucket policy does not exist.
 */
class NoSuchBucketPolicyException extends S3Exception {}
includes/lib/aws-sdk-php-2.8.31/src/Aws/S3/Exception/InvalidPartOrderException.php000064400000001417151327705670023372 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\S3\Exception;

/**
 * The list of parts was not in ascending order.Parts list must specified in order by part number.
 */
class InvalidPartOrderException extends S3Exception {}
includes/lib/aws-sdk-php-2.8.31/src/Aws/S3/Exception/SignatureDoesNotMatchException.php000064400000001613151327705670024371 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\S3\Exception;

/**
 * The request signature we calculated does not match the signature you provided. Check your AWS Secret Access Key and signing method. For more information, see REST Authentication and SOAP Authentication for details.
 */
class SignatureDoesNotMatchException extends S3Exception {}
includes/lib/aws-sdk-php-2.8.31/src/Aws/S3/Exception/BucketAlreadyOwnedByYouException.php000064400000001411151327705670024657 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\S3\Exception;

/**
 * Your previous request to create the named bucket succeeded and you already own it.
 */
class BucketAlreadyOwnedByYouException extends S3Exception {}
includes/lib/aws-sdk-php-2.8.31/src/Aws/S3/Exception/MethodNotAllowedException.php000064400000001352151327705670023370 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\S3\Exception;

/**
 * The specified method is not allowed against this resource.
 */
class MethodNotAllowedException extends S3Exception {}
includes/lib/aws-sdk-php-2.8.31/src/Aws/S3/Exception/Parser/S3ExceptionParser.php000064400000004361151327705670023060 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\S3\Exception\Parser;

use Aws\Common\Exception\Parser\DefaultXmlExceptionParser;
use Guzzle\Http\Message\RequestInterface;
use Guzzle\Http\Message\Response;

/**
 * Parses S3 exception responses
 */
class S3ExceptionParser extends DefaultXmlExceptionParser
{
    /**
     * {@inheritdoc}
     */
    public function parse(RequestInterface $request, Response $response)
    {
        $data = parent::parse($request, $response);

        if ($response->getStatusCode() === 301) {
            $data['type'] = 'client';
            if (isset($data['message'], $data['parsed'])) {
                $data['message'] = rtrim($data['message'], '.') . ': "' . $data['parsed']->Endpoint . '".';
            }
        }

        return $data;
    }

    /**
     * {@inheritdoc}
     */
    protected function parseHeaders(RequestInterface $request, Response $response, array &$data)
    {
        parent::parseHeaders($request, $response, $data);

        // Get the request
        $status  = $response->getStatusCode();
        $method  = $request->getMethod();

        // Attempt to determine code for 403s and 404s
        if ($status === 403) {
            $data['code'] = 'AccessDenied';
        } elseif ($method === 'HEAD' && $status === 404) {
            $path   = explode('/', trim($request->getPath(), '/'));
            $host   = explode('.', $request->getHost());
            $bucket = (count($host) === 4) ? $host[0] : array_shift($path);
            $object = array_shift($path);

            if ($bucket && $object) {
                $data['code'] = 'NoSuchKey';
            } elseif ($bucket) {
                $data['code'] = 'NoSuchBucket';
            }
        }
    }
}
includes/lib/aws-sdk-php-2.8.31/src/Aws/S3/Exception/InvalidPayerException.php000064400000001330151327705670022542 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\S3\Exception;

/**
 * All access to this object has been disabled.
 */
class InvalidPayerException extends S3Exception {}
includes/lib/aws-sdk-php-2.8.31/src/Aws/S3/Exception/MetadataTooLargeException.php000064400000001357151327705670023341 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\S3\Exception;

/**
 * Your metadata headers exceed the maximum allowed metadata size.
 */
class MetadataTooLargeException extends S3Exception {}
includes/lib/aws-sdk-php-2.8.31/src/Aws/S3/Exception/InvalidPartException.php000064400000001522151327705670022373 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\S3\Exception;

/**
 * One or more of the specified parts could not be found. The part might not have been uploaded, or the specified entity tag might not have matched the part's entity tag.
 */
class InvalidPartException extends S3Exception {}
includes/lib/aws-sdk-php-2.8.31/src/Aws/S3/Exception/KeyTooLongException.php000064400000001277151327705670022217 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\S3\Exception;

/**
 * Your key is too long.
 */
class KeyTooLongException extends S3Exception {}
includes/lib/aws-sdk-php-2.8.31/src/Aws/S3/Exception/InvalidPolicyDocumentException.php000064400000001413151327705670024422 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\S3\Exception;

/**
 * The content of the form does not meet the conditions specified in the policy document.
 */
class InvalidPolicyDocumentException extends S3Exception {}
includes/lib/aws-sdk-php-2.8.31/src/Aws/S3/Exception/NotSuchBucketPolicyException.php000064400000001346151327705670024063 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\S3\Exception;

/**
 * The specified bucket does not have a bucket policy.
 */
class NotSuchBucketPolicyException extends S3Exception {}
includes/lib/aws-sdk-php-2.8.31/src/Aws/S3/Exception/RequestTorrentOfBucketErrorException.php000064400000001364151327705670025625 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\S3\Exception;

/**
 * Requesting the torrent file of a bucket is not permitted.
 */
class RequestTorrentOfBucketErrorException extends S3Exception {}
includes/lib/aws-sdk-php-2.8.31/src/Aws/S3/Exception/EntityTooSmallException.php000064400000001363151327705670023110 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\S3\Exception;

/**
 * Your proposed upload is smaller than the minimum allowed object size.
 */
class EntityTooSmallException extends S3Exception {}
includes/lib/aws-sdk-php-2.8.31/src/Aws/S3/Exception/InvalidSOAPRequestException.php000064400000001323151327705670023577 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\S3\Exception;

/**
 * The SOAP request body is invalid.
 */
class InvalidSOAPRequestException extends S3Exception {}
includes/lib/aws-sdk-php-2.8.31/src/Aws/S3/Exception/MalformedPOSTRequestException.php000064400000001371151327705670024145 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\S3\Exception;

/**
 * The body of your POST request is not well-formed multipart/form-data.
 */
class MalformedPOSTRequestException extends S3Exception {}
includes/lib/aws-sdk-php-2.8.31/src/Aws/S3/Exception/MaxPostPreDataLengthExceededErrorException.php000064400000001403151327705670026613 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\S3\Exception;

/**
 * Your POST request fields preceding the upload file were too large.
 */
class MaxPostPreDataLengthExceededErrorException extends S3Exception {}
includes/lib/aws-sdk-php-2.8.31/src/Aws/S3/Exception/InvalidAccessKeyIdException.php000064400000001363151327705670023617 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\S3\Exception;

/**
 * The AWS Access Key Id you provided does not exist in our records.
 */
class InvalidAccessKeyIdException extends S3Exception {}
includes/lib/aws-sdk-php-2.8.31/src/Aws/S3/Exception/NoSuchUploadException.php000064400000001332151327705670022521 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\S3\Exception;

/**
 * The specified multipart upload does not exist.
 */
class NoSuchUploadException extends S3Exception {}
includes/lib/aws-sdk-php-2.8.31/src/Aws/S3/Exception/InvalidDigestException.php000064400000001332151327705670022703 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\S3\Exception;

/**
 * The Content-MD5 you specified was an invalid.
 */
class InvalidDigestException extends S3Exception {}
includes/lib/aws-sdk-php-2.8.31/src/Aws/S3/Exception/RequestIsNotMultiPartContentException.php000064400000001372151327705670025763 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\S3\Exception;

/**
 * Bucket POST must be of the enclosure-type multipart/form-data.
 */
class RequestIsNotMultiPartContentException extends S3Exception {}
includes/lib/aws-sdk-php-2.8.31/src/Aws/S3/SseCpkListener.php000064400000004150151327705670017237 0ustar00<?php

namespace Aws\S3;

use Aws\Common\Exception\RuntimeException;
use Guzzle\Common\Event;
use Guzzle\Service\Command\CommandInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;

/**
 * This listener simplifies the SSE-C process by encoding and hashing the key.
 */
class SseCpkListener implements EventSubscriberInterface
{
    public static function getSubscribedEvents()
    {
        return array('command.before_prepare' => 'onCommandBeforePrepare');
    }

    public function onCommandBeforePrepare(Event $event)
    {
        /** @var CommandInterface $command */
        $command = $event['command'];

        // Allows only HTTPS connections when using SSE-C
        if ($command['SSECustomerKey'] ||
            $command['CopySourceSSECustomerKey']
        ) {
            $this->validateScheme($command);
        }

        // Prepare the normal SSE-CPK headers
        if ($command['SSECustomerKey']) {
            $this->prepareSseParams($command);
        }

        // If it's a copy operation, prepare the SSE-CPK headers for the source.
        if ($command['CopySourceSSECustomerKey']) {
            $this->prepareSseParams($command, true);
        }
    }

    private function validateScheme(CommandInterface $command)
    {
        if ($command->getClient()->getConfig('scheme') !== 'https') {
            throw new RuntimeException('You must configure your S3 client to '
                . 'use HTTPS in order to use the SSE-C features.');
        }
    }

    private function prepareSseParams(
        CommandInterface $command,
        $isCopy = false
    ) {
        $prefix = $isCopy ? 'CopySource' : '';

        // Base64 encode the provided key
        $key = $command[$prefix . 'SSECustomerKey'];
        $command[$prefix . 'SSECustomerKey'] = base64_encode($key);

        // Base64 the provided MD5 or, generate an MD5 if not provided
        if ($md5 = $command[$prefix . 'SSECustomerKeyMD5']) {
            $command[$prefix . 'SSECustomerKeyMD5'] = base64_encode($md5);
        } else {
            $command[$prefix . 'SSECustomerKeyMD5'] = base64_encode(md5($key, true));
        }
    }
}
includes/lib/aws-sdk-php-2.8.31/src/Aws/S3/S3Client.php000064400000076657151327705670016012 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\S3;

use Aws\Common\Client\AbstractClient;
use Aws\Common\Client\ClientBuilder;
use Aws\Common\Client\ExpiredCredentialsChecker;
use Aws\Common\Client\UploadBodyListener;
use Aws\Common\Enum\ClientOptions as Options;
use Aws\Common\Exception\RuntimeException;
use Aws\Common\Exception\InvalidArgumentException;
use Aws\Common\Signature\SignatureV4;
use Aws\Common\Model\MultipartUpload\AbstractTransfer;
use Aws\S3\Exception\AccessDeniedException;
use Aws\S3\Exception\Parser\S3ExceptionParser;
use Aws\S3\Exception\S3Exception;
use Aws\S3\Model\ClearBucket;
use Aws\S3\Model\MultipartUpload\AbstractTransfer as AbstractMulti;
use Aws\S3\Model\MultipartUpload\UploadBuilder;
use Aws\S3\Sync\DownloadSyncBuilder;
use Aws\S3\Sync\UploadSyncBuilder;
use Guzzle\Common\Collection;
use Guzzle\Http\EntityBody;
use Guzzle\Http\Message\RequestInterface;
use Guzzle\Iterator\FilterIterator;
use Guzzle\Plugin\Backoff\BackoffPlugin;
use Guzzle\Plugin\Backoff\CurlBackoffStrategy;
use Guzzle\Plugin\Backoff\ExponentialBackoffStrategy;
use Guzzle\Plugin\Backoff\HttpBackoffStrategy;
use Guzzle\Plugin\Backoff\TruncatedBackoffStrategy;
use Guzzle\Service\Command\CommandInterface;
use Guzzle\Service\Command\Factory\AliasFactory;
use Guzzle\Service\Command\Factory\CompositeFactory;
use Guzzle\Service\Resource\Model;
use Guzzle\Service\Resource\ResourceIteratorInterface;

/**
 * Client to interact with Amazon Simple Storage Service
 *
 * @method S3SignatureInterface getSignature() Returns the signature implementation used with the client
 * @method Model abortMultipartUpload(array $args = array()) {@command S3 AbortMultipartUpload}
 * @method Model completeMultipartUpload(array $args = array()) {@command S3 CompleteMultipartUpload}
 * @method Model copyObject(array $args = array()) {@command S3 CopyObject}
 * @method Model createBucket(array $args = array()) {@command S3 CreateBucket}
 * @method Model createMultipartUpload(array $args = array()) {@command S3 CreateMultipartUpload}
 * @method Model deleteBucket(array $args = array()) {@command S3 DeleteBucket}
 * @method Model deleteBucketCors(array $args = array()) {@command S3 DeleteBucketCors}
 * @method Model deleteBucketLifecycle(array $args = array()) {@command S3 DeleteBucketLifecycle}
 * @method Model deleteBucketPolicy(array $args = array()) {@command S3 DeleteBucketPolicy}
 * @method Model deleteBucketReplication(array $args = array()) {@command S3 DeleteBucketReplication}
 * @method Model deleteBucketTagging(array $args = array()) {@command S3 DeleteBucketTagging}
 * @method Model deleteBucketWebsite(array $args = array()) {@command S3 DeleteBucketWebsite}
 * @method Model deleteObject(array $args = array()) {@command S3 DeleteObject}
 * @method Model deleteObjects(array $args = array()) {@command S3 DeleteObjects}
 * @method Model getBucketAcl(array $args = array()) {@command S3 GetBucketAcl}
 * @method Model getBucketCors(array $args = array()) {@command S3 GetBucketCors}
 * @method Model getBucketLifecycle(array $args = array()) {@command S3 GetBucketLifecycle}
 * @method Model getBucketLifecycleConfiguration(array $args = array()) {@command S3 GetBucketLifecycleConfiguration}
 * @method Model getBucketLocation(array $args = array()) {@command S3 GetBucketLocation}
 * @method Model getBucketLogging(array $args = array()) {@command S3 GetBucketLogging}
 * @method Model getBucketNotification(array $args = array()) {@command S3 GetBucketNotification}
 * @method Model getBucketNotificationConfiguration(array $args = array()) {@command S3 GetBucketNotificationConfiguration}
 * @method Model getBucketPolicy(array $args = array()) {@command S3 GetBucketPolicy}
 * @method Model getBucketReplication(array $args = array()) {@command S3 GetBucketReplication}
 * @method Model getBucketRequestPayment(array $args = array()) {@command S3 GetBucketRequestPayment}
 * @method Model getBucketTagging(array $args = array()) {@command S3 GetBucketTagging}
 * @method Model getBucketVersioning(array $args = array()) {@command S3 GetBucketVersioning}
 * @method Model getBucketWebsite(array $args = array()) {@command S3 GetBucketWebsite}
 * @method Model getObject(array $args = array()) {@command S3 GetObject}
 * @method Model getObjectAcl(array $args = array()) {@command S3 GetObjectAcl}
 * @method Model getObjectTorrent(array $args = array()) {@command S3 GetObjectTorrent}
 * @method Model headBucket(array $args = array()) {@command S3 HeadBucket}
 * @method Model headObject(array $args = array()) {@command S3 HeadObject}
 * @method Model listBuckets(array $args = array()) {@command S3 ListBuckets}
 * @method Model listMultipartUploads(array $args = array()) {@command S3 ListMultipartUploads}
 * @method Model listObjectVersions(array $args = array()) {@command S3 ListObjectVersions}
 * @method Model listObjects(array $args = array()) {@command S3 ListObjects}
 * @method Model listParts(array $args = array()) {@command S3 ListParts}
 * @method Model putBucketAcl(array $args = array()) {@command S3 PutBucketAcl}
 * @method Model putBucketCors(array $args = array()) {@command S3 PutBucketCors}
 * @method Model putBucketLifecycle(array $args = array()) {@command S3 PutBucketLifecycle}
 * @method Model putBucketLifecycleConfiguration(array $args = array()) {@command S3 PutBucketLifecycleConfiguration}
 * @method Model putBucketLogging(array $args = array()) {@command S3 PutBucketLogging}
 * @method Model putBucketNotification(array $args = array()) {@command S3 PutBucketNotification}
 * @method Model putBucketNotificationConfiguration(array $args = array()) {@command S3 PutBucketNotificationConfiguration}
 * @method Model putBucketPolicy(array $args = array()) {@command S3 PutBucketPolicy}
 * @method Model putBucketReplication(array $args = array()) {@command S3 PutBucketReplication}
 * @method Model putBucketRequestPayment(array $args = array()) {@command S3 PutBucketRequestPayment}
 * @method Model putBucketTagging(array $args = array()) {@command S3 PutBucketTagging}
 * @method Model putBucketVersioning(array $args = array()) {@command S3 PutBucketVersioning}
 * @method Model putBucketWebsite(array $args = array()) {@command S3 PutBucketWebsite}
 * @method Model putObject(array $args = array()) {@command S3 PutObject}
 * @method Model putObjectAcl(array $args = array()) {@command S3 PutObjectAcl}
 * @method Model restoreObject(array $args = array()) {@command S3 RestoreObject}
 * @method Model uploadPart(array $args = array()) {@command S3 UploadPart}
 * @method Model uploadPartCopy(array $args = array()) {@command S3 UploadPartCopy}
 * @method waitUntilBucketExists(array $input) The input array uses the parameters of the HeadBucket operation and waiter specific settings
 * @method waitUntilBucketNotExists(array $input) The input array uses the parameters of the HeadBucket operation and waiter specific settings
 * @method waitUntilObjectExists(array $input) The input array uses the parameters of the HeadObject operation and waiter specific settings
 * @method ResourceIteratorInterface getListBucketsIterator(array $args = array()) The input array uses the parameters of the ListBuckets operation
 * @method ResourceIteratorInterface getListMultipartUploadsIterator(array $args = array()) The input array uses the parameters of the ListMultipartUploads operation
 * @method ResourceIteratorInterface getListObjectVersionsIterator(array $args = array()) The input array uses the parameters of the ListObjectVersions operation
 * @method ResourceIteratorInterface getListObjectsIterator(array $args = array()) The input array uses the parameters of the ListObjects operation
 * @method ResourceIteratorInterface getListPartsIterator(array $args = array()) The input array uses the parameters of the ListParts operation
 *
 * @link http://docs.aws.amazon.com/aws-sdk-php/v2/guide/service-s3.html User guide
 * @link http://docs.aws.amazon.com/aws-sdk-php/v2/api/class-Aws.S3.S3Client.html API docs
 */
class S3Client extends AbstractClient
{
    const LATEST_API_VERSION = '2006-03-01';

    /**
     * @var array Aliases for S3 operations
     */
    protected static $commandAliases = array(
        // REST API Docs Aliases
        'GetService' => 'ListBuckets',
        'GetBucket'  => 'ListObjects',
        'PutBucket'  => 'CreateBucket',

        // SDK 1.x Aliases
        'GetBucketHeaders'              => 'HeadBucket',
        'GetObjectHeaders'              => 'HeadObject',
        'SetBucketAcl'                  => 'PutBucketAcl',
        'CreateObject'                  => 'PutObject',
        'DeleteObjects'                 => 'DeleteMultipleObjects',
        'PutObjectCopy'                 => 'CopyObject',
        'SetObjectAcl'                  => 'PutObjectAcl',
        'GetLogs'                       => 'GetBucketLogging',
        'GetVersioningStatus'           => 'GetBucketVersioning',
        'SetBucketPolicy'               => 'PutBucketPolicy',
        'CreateBucketNotification'      => 'PutBucketNotification',
        'GetBucketNotifications'        => 'GetBucketNotification',
        'CopyPart'                      => 'UploadPartCopy',
        'CreateWebsiteConfig'           => 'PutBucketWebsite',
        'GetWebsiteConfig'              => 'GetBucketWebsite',
        'DeleteWebsiteConfig'           => 'DeleteBucketWebsite',
        'CreateObjectExpirationConfig'  => 'PutBucketLifecycle',
        'GetObjectExpirationConfig'     => 'GetBucketLifecycle',
        'DeleteObjectExpirationConfig'  => 'DeleteBucketLifecycle',
    );

    protected $directory = __DIR__;

    /**
     * Factory method to create a new Amazon S3 client using an array of configuration options.
     *
     * @param array|Collection $config Client configuration data
     *
     * @return S3Client
     * @link http://docs.aws.amazon.com/aws-sdk-php/v2/guide/configuration.html#client-configuration-options
     */
    public static function factory($config = array())
    {
        $exceptionParser = new S3ExceptionParser();

        // Configure the custom exponential backoff plugin for retrying S3 specific errors
        if (!isset($config[Options::BACKOFF])) {
            $retries = isset($config[Options::BACKOFF_RETRIES]) ? $config[Options::BACKOFF_RETRIES] : 3;
            $config[Options::BACKOFF] = static::createBackoffPlugin($exceptionParser, $retries);
        }

        $config[Options::SIGNATURE] = $signature = static::createSignature($config);

        $client = ClientBuilder::factory(__NAMESPACE__)
            ->setConfig($config)
            ->setConfigDefaults(array(
                Options::VERSION => self::LATEST_API_VERSION,
                Options::SERVICE_DESCRIPTION => __DIR__ . '/Resources/s3-%s.php'
            ))
            ->setExceptionParser($exceptionParser)
            ->setIteratorsConfig(array(
                'more_key' => 'IsTruncated',
                'operations' => array(
                    'ListBuckets',
                    'ListMultipartUploads' => array(
                        'limit_param' => 'MaxUploads',
                        'token_param' => array('KeyMarker', 'UploadIdMarker'),
                        'token_key'   => array('NextKeyMarker', 'NextUploadIdMarker'),
                    ),
                    'ListObjects' => array(
                        'limit_param' => 'MaxKeys',
                        'token_param' => 'Marker',
                        'token_key'   => 'NextMarker',
                    ),
                    'ListObjectVersions' => array(
                        'limit_param' => 'MaxKeys',
                        'token_param' => array('KeyMarker', 'VersionIdMarker'),
                        'token_key'   => array('nextKeyMarker', 'nextVersionIdMarker'),
                    ),
                    'ListParts' => array(
                        'limit_param' => 'MaxParts',
                        'result_key'  => 'Parts',
                        'token_param' => 'PartNumberMarker',
                        'token_key'   => 'NextPartNumberMarker',
                    ),
                )
            ))
            ->build();

        // Use virtual hosted buckets when possible
        $client->addSubscriber(new BucketStyleListener());
        // Ensure that ACP headers are applied when needed
        $client->addSubscriber(new AcpListener());
        // Validate and add required Content-MD5 hashes (e.g. DeleteObjects)
        $client->addSubscriber(new S3Md5Listener($signature));

        // Allow for specifying bodies with file paths and file handles
        $client->addSubscriber(new UploadBodyListener(array('PutObject', 'UploadPart')));

        // Ensures that if a SSE-CPK key is provided, the key and md5 are formatted correctly
        $client->addSubscriber(new SseCpkListener);

        // Add aliases for some S3 operations
        $default = CompositeFactory::getDefaultChain($client);
        $default->add(
            new AliasFactory($client, static::$commandAliases),
            'Guzzle\Service\Command\Factory\ServiceDescriptionFactory'
        );
        $client->setCommandFactory($default);

        return $client;
    }

    /**
     * Create an Amazon S3 specific backoff plugin
     *
     * @param S3ExceptionParser $exceptionParser
     *
     * @return BackoffPlugin
     */
    private static function createBackoffPlugin(S3ExceptionParser $exceptionParser, $retries = 3)
    {
        return new BackoffPlugin(
            new TruncatedBackoffStrategy($retries,
                new IncompleteMultipartUploadChecker(
                    new CurlBackoffStrategy(null,
                        new HttpBackoffStrategy(null,
                            new SocketTimeoutChecker(
                                new ExpiredCredentialsChecker($exceptionParser,
                                    new ExponentialBackoffStrategy()
                                )
                            )
                        )
                    )
                )
            )
        );
    }

    /**
     * Create an appropriate signature based on the configuration settings
     *
     * @param $config
     *
     * @return \Aws\Common\Signature\SignatureInterface
     * @throws InvalidArgumentException
     */
    private static function createSignature($config)
    {
        $currentValue = isset($config[Options::SIGNATURE]) ? $config[Options::SIGNATURE] : null;

        // Force v4 if no value is provided, a region is in the config, and
        // the region starts with "cn-" or "eu-central-".
        $requiresV4 = !$currentValue
            && isset($config['region'])
            && (strpos($config['region'], 'eu-central-') === 0
                || strpos($config['region'], 'cn-') === 0);

        // Use the Amazon S3 signature V4 when the value is set to "v4" or when
        // the value is not set and the region starts with "cn-".
        if ($currentValue == 'v4' || $requiresV4) {
            // Force SignatureV4 for specific regions or if specified in the config
            $currentValue = new S3SignatureV4('s3');
        } elseif (!$currentValue || $currentValue == 's3') {
            // Use the Amazon S3 signature by default
            $currentValue = new S3Signature();
        }

        // A region is require with v4
        if ($currentValue instanceof SignatureV4 && !isset($config['region'])) {
            throw new InvalidArgumentException('A region must be specified '
                . 'when using signature version 4');
        }

        return $currentValue;
    }

    /**
     * Determine if a string is a valid name for a DNS compatible Amazon S3
     * bucket, meaning the bucket can be used as a subdomain in a URL (e.g.,
     * "<bucket>.s3.amazonaws.com").
     *
     * @param string $bucket The name of the bucket to check.
     *
     * @return bool TRUE if the bucket name is valid or FALSE if it is invalid.
     */
    public static function isValidBucketName($bucket)
    {
        $bucketLen = strlen($bucket);
        if ($bucketLen < 3 || $bucketLen > 63 ||
            // Cannot look like an IP address
            preg_match('/(\d+\.){3}\d+$/', $bucket) ||
            // Cannot include special characters, must start and end with lower alnum
            !preg_match('/^[a-z0-9]([a-z0-9\-\.]*[a-z0-9])?$/', $bucket)
        ) {
            return false;
        }

        return true;
    }

    /**
     * Create a pre-signed URL for a request
     *
     * @param RequestInterface     $request Request to generate the URL for. Use the factory methods of the client to
     *                                      create this request object
     * @param int|string|\DateTime $expires The time at which the URL should expire. This can be a Unix timestamp, a
     *                                      PHP DateTime object, or a string that can be evaluated by strtotime
     *
     * @return string
     * @throws InvalidArgumentException if the request is not associated with this client object
     */
    public function createPresignedUrl(RequestInterface $request, $expires)
    {
        if ($request->getClient() !== $this) {
            throw new InvalidArgumentException('The request object must be associated with the client. Use the '
                . '$client->get(), $client->head(), $client->post(), $client->put(), etc. methods when passing in a '
                . 'request object');
        }

        return $this->signature->createPresignedUrl($request, $this->credentials, $expires);
    }

    /**
     * Returns the URL to an object identified by its bucket and key. If an expiration time is provided, the URL will
     * be signed and set to expire at the provided time.
     *
     * Note: This method does not ensure that the generated URL is valid. For example, the bucket referenced may not
     * exist, the key referenced may not exist, and the URL might include parameters that require it to be signed.
     * If you need to use parameters that require a signed URL (e.g., ResponseCacheControl), then you must sign the
     * URL either by providing an $expires argument or by signing the URL returned by this method in some other
     * manner.
     *
     * @param string $bucket  The name of the bucket where the object is located
     * @param string $key     The key of the object
     * @param mixed  $expires The time at which the URL should expire
     * @param array  $args    Arguments to the GetObject command. Additionally you can specify a "Scheme" if you would
     *                        like the URL to use a different scheme than what the client is configured to use
     *
     * @return string The URL to the object
     */
    public function getObjectUrl($bucket, $key, $expires = null, array $args = array())
    {
        $command = $this->getCommand('GetObject', $args + array('Bucket' => $bucket, 'Key' => $key));

        if ($command->hasKey('Scheme')) {
            $scheme = $command['Scheme'];
            $request = $command->remove('Scheme')->prepare()->setScheme($scheme)->setPort(null);
        } else {
            $request = $command->prepare();
        }

        return $expires ? $this->createPresignedUrl($request, $expires) : $request->getUrl();
    }

    /**
     * Helper used to clear the contents of a bucket. Use the {@see ClearBucket} object directly
     * for more advanced options and control.
     *
     * @param string $bucket Name of the bucket to clear.
     *
     * @return int Returns the number of deleted keys
     */
    public function clearBucket($bucket)
    {
        $clear = new ClearBucket($this, $bucket);

        return $clear->clear();
    }

    /**
     * Determines whether or not a bucket exists by name
     *
     * @param string $bucket    The name of the bucket
     * @param bool   $accept403 Set to true if 403s are acceptable
     * @param array  $options   Additional options to add to the executed command
     *
     * @return bool
     */
    public function doesBucketExist($bucket, $accept403 = true, array $options = array())
    {
        return $this->checkExistenceWithCommand(
            $this->getCommand('HeadBucket', array_merge($options, array(
                'Bucket' => $bucket
            ))), $accept403
        );
    }

    /**
     * Determines whether or not an object exists by name
     *
     * @param string $bucket  The name of the bucket
     * @param string $key     The key of the object
     * @param array  $options Additional options to add to the executed command
     *
     * @return bool
     */
    public function doesObjectExist($bucket, $key, array $options = array())
    {
        return $this->checkExistenceWithCommand(
            $this->getCommand('HeadObject', array_merge($options, array(
                'Bucket' => $bucket,
                'Key'    => $key
            )))
        );
    }

    /**
     * Determines whether or not a bucket policy exists for a bucket
     *
     * @param string $bucket  The name of the bucket
     * @param array  $options Additional options to add to the executed command
     *
     * @return bool
     */
    public function doesBucketPolicyExist($bucket, array $options = array())
    {
        return $this->checkExistenceWithCommand(
            $this->getCommand('GetBucketPolicy', array_merge($options, array(
                'Bucket' => $bucket
            )))
        );
    }

    /**
     * Raw URL encode a key and allow for '/' characters
     *
     * @param string $key Key to encode
     *
     * @return string Returns the encoded key
     */
    public static function encodeKey($key)
    {
        return str_replace('%2F', '/', rawurlencode($key));
    }

    /**
     * Explode a prefixed key into an array of values
     *
     * @param string $key Key to explode
     *
     * @return array Returns the exploded
     */
    public static function explodeKey($key)
    {
        // Remove a leading slash if one is found
        return explode('/', $key && $key[0] == '/' ? substr($key, 1) : $key);
    }

    /**
     * Register the Amazon S3 stream wrapper and associates it with this client object
     *
     * @return $this
     */
    public function registerStreamWrapper()
    {
        StreamWrapper::register($this);

        return $this;
    }

    /**
     * Upload a file, stream, or string to a bucket. If the upload size exceeds the specified threshold, the upload
     * will be performed using parallel multipart uploads.
     *
     * @param string $bucket  Bucket to upload the object
     * @param string $key     Key of the object
     * @param mixed  $body    Object data to upload. Can be a Guzzle\Http\EntityBodyInterface, stream resource, or
     *                        string of data to upload.
     * @param string $acl     ACL to apply to the object
     * @param array  $options Custom options used when executing commands:
     *     - params: Custom parameters to use with the upload. The parameters must map to a PutObject
     *       or InitiateMultipartUpload operation parameters.
     *     - min_part_size: Minimum size to allow for each uploaded part when performing a multipart upload.
     *     - concurrency: Maximum number of concurrent multipart uploads.
     *     - before_upload: Callback to invoke before each multipart upload. The callback will receive a
     *       Guzzle\Common\Event object with context.
     *
     * @see Aws\S3\Model\MultipartUpload\UploadBuilder for more options and customization
     * @return \Guzzle\Service\Resource\Model Returns the modeled result of the performed operation
     */
    public function upload($bucket, $key, $body, $acl = 'private', array $options = array())
    {
        $body = EntityBody::factory($body);
        $options = Collection::fromConfig(array_change_key_case($options), array(
            'min_part_size' => AbstractMulti::MIN_PART_SIZE,
            'params'        => array(),
            'concurrency'   => $body->getWrapper() == 'plainfile' ? 3 : 1
        ));

        if ($body->getSize() < $options['min_part_size']) {
            // Perform a simple PutObject operation
            return $this->putObject(array(
                'Bucket' => $bucket,
                'Key'    => $key,
                'Body'   => $body,
                'ACL'    => $acl
            ) + $options['params']);
        }

        // Perform a multipart upload if the file is large enough
        $transfer = UploadBuilder::newInstance()
            ->setBucket($bucket)
            ->setKey($key)
            ->setMinPartSize($options['min_part_size'])
            ->setConcurrency($options['concurrency'])
            ->setClient($this)
            ->setSource($body)
            ->setTransferOptions($options->toArray())
            ->addOptions($options['params'])
            ->setOption('ACL', $acl)
            ->build();

        if ($options['before_upload']) {
            $transfer->getEventDispatcher()->addListener(
                AbstractTransfer::BEFORE_PART_UPLOAD,
                $options['before_upload']
            );
        }

        return $transfer->upload();
    }

    /**
     * Recursively uploads all files in a given directory to a given bucket.
     *
     * @param string $directory Full path to a directory to upload
     * @param string $bucket    Name of the bucket
     * @param string $keyPrefix Virtual directory key prefix to add to each upload
     * @param array  $options   Associative array of upload options
     *     - params: Array of parameters to use with each PutObject operation performed during the transfer
     *     - base_dir: Base directory to remove from each object key
     *     - force: Set to true to upload every file, even if the file is already in Amazon S3 and has not changed
     *     - concurrency: Maximum number of parallel uploads (defaults to 10)
     *     - debug: Set to true or an fopen resource to enable debug mode to print information about each upload
     *     - multipart_upload_size: When the size of a file exceeds this value, the file will be uploaded using a
     *       multipart upload.
     *
     * @see Aws\S3\S3Sync\S3Sync for more options and customization
     */
    public function uploadDirectory($directory, $bucket, $keyPrefix = null, array $options = array())
    {
        $options = Collection::fromConfig(
            $options,
            array(
                'base_dir' => realpath($directory) ?: $directory
            )
        );

        $builder = $options['builder'] ?: UploadSyncBuilder::getInstance();
        $builder->uploadFromDirectory($directory)
            ->setClient($this)
            ->setBucket($bucket)
            ->setKeyPrefix($keyPrefix)
            ->setConcurrency($options['concurrency'] ?: 5)
            ->setBaseDir($options['base_dir'])
            ->force($options['force'])
            ->setOperationParams($options['params'] ?: array())
            ->enableDebugOutput($options['debug']);

        if ($options->hasKey('multipart_upload_size')) {
            $builder->setMultipartUploadSize($options['multipart_upload_size']);
        }

        $builder->build()->transfer();
    }

    /**
     * Downloads a bucket to the local filesystem
     *
     * @param string $directory Directory to download to
     * @param string $bucket    Bucket to download from
     * @param string $keyPrefix Only download objects that use this key prefix
     * @param array  $options   Associative array of download options
     *     - params: Array of parameters to use with each GetObject operation performed during the transfer
     *     - base_dir: Base directory to remove from each object key when storing in the local filesystem
     *     - force: Set to true to download every file, even if the file is already on the local filesystem and has not
     *       changed
     *     - concurrency: Maximum number of parallel downloads (defaults to 10)
     *     - debug: Set to true or a fopen resource to enable debug mode to print information about each download
     *     - allow_resumable: Set to true to allow previously interrupted downloads to be resumed using a Range GET
     */
    public function downloadBucket($directory, $bucket, $keyPrefix = '', array $options = array())
    {
        $options = new Collection($options);
        $builder = $options['builder'] ?: DownloadSyncBuilder::getInstance();
        $builder->setDirectory($directory)
            ->setClient($this)
            ->setBucket($bucket)
            ->setKeyPrefix($keyPrefix)
            ->setConcurrency($options['concurrency'] ?: 10)
            ->setBaseDir($options['base_dir'])
            ->force($options['force'])
            ->setOperationParams($options['params'] ?: array())
            ->enableDebugOutput($options['debug']);

        if ($options['allow_resumable']) {
            $builder->allowResumableDownloads();
        }

        $builder->build()->transfer();
    }

    /**
     * Deletes objects from Amazon S3 that match the result of a ListObjects operation. For example, this allows you
     * to do things like delete all objects that match a specific key prefix.
     *
     * @param string $bucket  Bucket that contains the object keys
     * @param string $prefix  Optionally delete only objects under this key prefix
     * @param string $regex   Delete only objects that match this regex
     * @param array  $options Options used when deleting the object:
     *     - before_delete: Callback to invoke before each delete. The callback will receive a
     *       Guzzle\Common\Event object with context.
     *
     * @see Aws\S3\S3Client::listObjects
     * @see Aws\S3\Model\ClearBucket For more options or customization
     * @return int Returns the number of deleted keys
     * @throws RuntimeException if no prefix and no regex is given
     */
    public function deleteMatchingObjects($bucket, $prefix = '', $regex = '', array $options = array())
    {
        if (!$prefix && !$regex) {
            throw new RuntimeException('A prefix or regex is required, or use S3Client::clearBucket().');
        }

        $clear = new ClearBucket($this, $bucket);
        $iterator = $this->getIterator('ListObjects', array('Bucket' => $bucket, 'Prefix' => $prefix));

        if ($regex) {
            $iterator = new FilterIterator($iterator, function ($current) use ($regex) {
                return preg_match($regex, $current['Key']);
            });
        }

        $clear->setIterator($iterator);
        if (isset($options['before_delete'])) {
            $clear->getEventDispatcher()->addListener(ClearBucket::BEFORE_CLEAR, $options['before_delete']);
        }

        return $clear->clear();
    }

    /**
     * Determines whether or not a resource exists using a command
     *
     * @param CommandInterface $command   Command used to poll for the resource
     * @param bool             $accept403 Set to true if 403s are acceptable
     *
     * @return bool
     * @throws S3Exception|\Exception if there is an unhandled exception
     */
    protected function checkExistenceWithCommand(CommandInterface $command, $accept403 = false)
    {
        try {
            $command->execute();
            $exists = true;
        } catch (AccessDeniedException $e) {
            $exists = (bool) $accept403;
        } catch (S3Exception $e) {
            $exists = false;
            if ($e->getResponse()->getStatusCode() >= 500) {
                // @codeCoverageIgnoreStart
                throw $e;
                // @codeCoverageIgnoreEnd
            }
        }

        return $exists;
    }
}
includes/lib/aws-sdk-php-2.8.31/src/Aws/S3/Model/DeleteObjectsBatch.php000064400000005331151327705670021061 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\S3\Model;

use Aws\Common\Client\AwsClientInterface;
use Aws\Common\Exception\InvalidArgumentException;
use Guzzle\Service\Command\AbstractCommand;
use Guzzle\Batch\BatchBuilder;
use Guzzle\Batch\BatchSizeDivisor;
use Guzzle\Batch\AbstractBatchDecorator;

/**
 * The DeleteObjectsBatch is a BatchDecorator for Guzzle that implements a
 * queue for deleting keys from an Amazon S3 bucket. You can add DeleteObject
 * or an array of [Key => %s, VersionId => %s] and call flush when the objects
 * should be deleted.
 */
class DeleteObjectsBatch extends AbstractBatchDecorator
{
    /**
     * Factory for creating a DeleteObjectsBatch
     *
     * @param AwsClientInterface $client Client used to transfer requests
     * @param string             $bucket Bucket that contains the objects to delete
     * @param string             $mfa    MFA token to use with the request
     *
     * @return static
     */
    public static function factory(AwsClientInterface $client, $bucket, $mfa = null)
    {
        $batch = BatchBuilder::factory()
            ->createBatchesWith(new BatchSizeDivisor(1000))
            ->transferWith(new DeleteObjectsTransfer($client, $bucket, $mfa))
            ->build();

        return new static($batch);
    }

    /**
     * Add an object to be deleted
     *
     * @param string $key       Key of the object
     * @param string $versionId VersionID of the object
     *
     * @return $this
     */
    public function addKey($key, $versionId = null)
    {
        return $this->add(array(
            'Key'       => $key,
            'VersionId' => $versionId
        ));
    }

    /**
     * {@inheritdoc}
     */
    public function add($item)
    {
        if ($item instanceof AbstractCommand && $item->getName() == 'DeleteObject') {
            $item = array(
                'Key'       => $item['Key'],
                'VersionId' => $item['VersionId']
            );
        }

        if (!is_array($item) || (!isset($item['Key']))) {
            throw new InvalidArgumentException('Item must be a DeleteObject command or array containing a Key and VersionId key.');
        }

        return parent::add($item);
    }
}
includes/lib/aws-sdk-php-2.8.31/src/Aws/S3/Model/Grantee.php000064400000014224151327705670016771 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\S3\Model;

use Aws\S3\Enum\Group;
use Aws\S3\Enum\GranteeType;
use Aws\Common\Exception\InvalidArgumentException;
use Aws\Common\Exception\UnexpectedValueException;
use Aws\Common\Exception\LogicException;
use Guzzle\Common\ToArrayInterface;

/**
 * Amazon S3 Grantee model
 */
class Grantee implements ToArrayInterface
{
    /**
     * @var array A map of grantee types to grant header value prefixes
     */
    protected static $headerMap = array(
        GranteeType::USER  => 'id',
        GranteeType::EMAIL => 'emailAddress',
        GranteeType::GROUP => 'uri'
    );

    /**
     * @var string The account ID, email, or URL identifying the grantee
     */
    protected $id;

    /**
     * @var string The display name of the grantee
     */
    protected $displayName;

    /**
     * @var string The type of the grantee (CanonicalUser or Group)
     */
    protected $type;

    /**
     * Constructs a Grantee
     *
     * @param string $id           Grantee identifier
     * @param string $displayName  Grantee display name
     * @param string $expectedType The expected type of the grantee
     */
    public function __construct($id, $displayName = null, $expectedType = null)
    {
        $this->type = GranteeType::USER;
        $this->setId($id, $expectedType);
        $this->setDisplayName($displayName);
    }

    /**
     * Sets the account ID, email, or URL identifying the grantee
     *
     * @param string $id           Grantee identifier
     * @param string $expectedType The expected type of the grantee
     *
     * @return Grantee
     *
     * @throws UnexpectedValueException if $expectedType is set and the grantee
     *     is not of that type after instantiation
     * @throws InvalidArgumentException when the ID provided is not a string
     */
    public function setId($id, $expectedType = null)
    {
        if (in_array($id, Group::values())) {
            $this->type = GranteeType::GROUP;
        } elseif (!is_string($id)) {
            throw new InvalidArgumentException('The grantee ID must be provided as a string value.');
        }

        if (strpos($id, '@') !== false) {
            $this->type = GranteeType::EMAIL;
        }

        if ($expectedType && $expectedType !== $this->type) {
            throw new UnexpectedValueException('The type of the grantee after '
                . 'setting the ID did not match the specified, expected type "'
                . esc_html($expectedType) . '" but received "' . esc_html($this->type) . '".');
        }

        $this->id = $id;

        return $this;
    }

    /**
     * Gets the grantee identifier
     *
     * @return string
     */
    public function getId()
    {
        return $this->id;
    }

    /**
     * Gets the grantee email address (if it is set)
     *
     * @return null|string
     */
    public function getEmailAddress()
    {
        return $this->isAmazonCustomerByEmail() ? $this->id : null;
    }

    /**
     * Gets the grantee URI (if it is set)
     *
     * @return null|string
     */
    public function getGroupUri()
    {
        return $this->isGroup() ? $this->id : null;
    }

    /**
     * Sets the display name of the grantee
     *
     * @param string $displayName Grantee name
     *
     * @return Grantee
     *
     * @throws LogicException when the grantee type not CanonicalUser
     */
    public function setDisplayName($displayName)
    {
        if ($this->type === GranteeType::USER) {
            if (empty($displayName) || !is_string($displayName)) {
                $displayName = $this->id;
            }
            $this->displayName = $displayName;
        } else {
            if ($displayName) {
                throw new LogicException('The display name can only be set '
                    . 'for grantees specified by ID.');
            }
        }

        return $this;
    }

    /**
     * Gets the grantee display name
     *
     * @return string
     */
    public function getDisplayName()
    {
        return $this->displayName;
    }

    /**
     * Gets the grantee type (determined by ID)
     *
     * @return string
     */
    public function getType()
    {
        return $this->type;
    }

    /**
     * Returns true if this grantee object represents a canonical user by ID
     *
     * @return bool
     */
    public function isCanonicalUser()
    {
        return ($this->type === GranteeType::USER);
    }

    /**
     * Returns true if this grantee object represents a customer by email
     *
     * @return bool
     */
    public function isAmazonCustomerByEmail()
    {
        return ($this->type === GranteeType::EMAIL);
    }

    /**
     * Returns true if this grantee object represents a group by URL
     *
     * @return bool
     */
    public function isGroup()
    {
        return ($this->type === GranteeType::GROUP);
    }

    /**
     * Returns the value used in headers to specify this grantee
     *
     * @return string
     */
    public function getHeaderValue()
    {
        $key = static::$headerMap[$this->type];

        return "{$key}=\"{$this->id}\"";
    }

    /**
     * {@inheritdoc}
     */
    public function toArray()
    {
        $result = array(
            'Type' => $this->type
        );

        switch ($this->type) {
            case GranteeType::USER:
                $result['ID'] = $this->id;
                $result['DisplayName'] = $this->displayName;
                break;
            case GranteeType::EMAIL:
                $result['EmailAddress'] = $this->id;
                break;
            case GranteeType::GROUP:
                $result['URI'] = $this->id;
        }

        return $result;
    }
}
includes/lib/aws-sdk-php-2.8.31/src/Aws/S3/Model/AcpBuilder.php000064400000006517151327705670017424 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\S3\Model;

use Aws\S3\Enum\GranteeType;

/**
 * Builder for creating Access Control Policies
 */
class AcpBuilder
{
    /**
     * @var Grantee The owner for the ACL
     */
    protected $owner;

    /**
     * @var array An array of Grant objects for the ACL
     */
    protected $grants = array();

    /**
     * Static method for chainable instantiation
     *
     * @return static
     */
    public static function newInstance()
    {
        return new static;
    }

    /**
     * Sets the owner to be set on the ACL
     *
     * @param string $id          Owner identifier
     * @param string $displayName Owner display name
     *
     * @return $this
     */
    public function setOwner($id, $displayName = null)
    {
        $this->owner = new Grantee($id, $displayName ?: $id, GranteeType::USER);

        return $this;
    }

    /**
     * Create and store a Grant with a CanonicalUser Grantee for the ACL
     *
     * @param string $permission  Permission for the Grant
     * @param string $id          Grantee identifier
     * @param string $displayName Grantee display name
     *
     * @return $this
     */
    public function addGrantForUser($permission, $id, $displayName = null)
    {
        $grantee = new Grantee($id, $displayName ?: $id, GranteeType::USER);
        $this->addGrant($permission, $grantee);

        return $this;
    }

    /**
     * Create and store a Grant with a AmazonCustomerByEmail Grantee for the ACL
     *
     * @param string $permission Permission for the Grant
     * @param string $email      Grantee email address
     *
     * @return $this
     */
    public function addGrantForEmail($permission, $email)
    {
        $grantee = new Grantee($email, null, GranteeType::EMAIL);
        $this->addGrant($permission, $grantee);

        return $this;
    }

    /**
     * Create and store a Grant with a Group Grantee for the ACL
     *
     * @param string $permission Permission for the Grant
     * @param string $group      Grantee group
     *
     * @return $this
     */
    public function addGrantForGroup($permission, $group)
    {
        $grantee = new Grantee($group, null, GranteeType::GROUP);
        $this->addGrant($permission, $grantee);

        return $this;
    }

    /**
     * Create and store a Grant for the ACL
     *
     * @param string  $permission Permission for the Grant
     * @param Grantee $grantee    The Grantee for the Grant
     *
     * @return $this
     */
    public function addGrant($permission, Grantee $grantee)
    {
        $this->grants[] = new Grant($grantee, $permission);

        return $this;
    }

    /**
     * Builds the ACP and returns it
     *
     * @return Acp
     */
    public function build()
    {
        return new Acp($this->owner, $this->grants);
    }
}
includes/lib/aws-sdk-php-2.8.31/src/Aws/S3/Model/MultipartUpload/UploadBuilder.php000064400000020721151327705670023264 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\S3\Model\MultipartUpload;

use Aws\Common\Enum\UaString as Ua;
use Aws\Common\Exception\InvalidArgumentException;
use Aws\Common\Model\MultipartUpload\AbstractUploadBuilder;
use Aws\S3\Model\Acp;

/**
 * Easily create a multipart uploader used to quickly and reliably upload a
 * large file or data stream to Amazon S3 using multipart uploads
 */
class UploadBuilder extends AbstractUploadBuilder
{
    /**
     * @var int Concurrency level to transfer the parts
     */
    protected $concurrency = 1;

    /**
     * @var int Minimum part size to upload
     */
    protected $minPartSize = AbstractTransfer::MIN_PART_SIZE;

    /**
     * @var string MD5 hash of the entire body to transfer
     */
    protected $md5;

    /**
     * @var bool Whether or not to calculate the entire MD5 hash of the object
     */
    protected $calculateEntireMd5 = false;

    /**
     * @var bool Whether or not to calculate MD5 hash of each part
     */
    protected $calculatePartMd5 = true;

    /**
     * @var array Array of initiate command options
     */
    protected $commandOptions = array();

    /**
     * @var array Array of transfer options
     */
    protected $transferOptions = array();

    /**
     * Set the bucket to upload the object to
     *
     * @param string $bucket Name of the bucket
     *
     * @return $this
     */
    public function setBucket($bucket)
    {
        return $this->setOption('Bucket', $bucket);
    }

    /**
     * Set the key of the object
     *
     * @param string $key Key of the object to upload
     *
     * @return $this
     */
    public function setKey($key)
    {
        return $this->setOption('Key', $key);
    }

    /**
     * Set the minimum acceptable part size
     *
     * @param int $minSize Minimum acceptable part size in bytes
     *
     * @return $this
     */
    public function setMinPartSize($minSize)
    {
        $this->minPartSize = (int) max((int) $minSize, AbstractTransfer::MIN_PART_SIZE);

        return $this;
    }

    /**
     * Set the concurrency level to use when uploading parts. This affects how
     * many parts are uploaded in parallel. You must use a local file as your
     * data source when using a concurrency greater than 1
     *
     * @param int $concurrency Concurrency level
     *
     * @return $this
     */
    public function setConcurrency($concurrency)
    {
        $this->concurrency = $concurrency;

        return $this;
    }

    /**
     * Explicitly set the MD5 hash of the entire body
     *
     * @param string $md5 MD5 hash of the entire body
     *
     * @return $this
     */
    public function setMd5($md5)
    {
        $this->md5 = $md5;

        return $this;
    }

    /**
     * Set to true to have the builder calculate the MD5 hash of the entire data
     * source before initiating a multipart upload (this could be an expensive
     * operation). This setting can ony be used with seekable data sources.
     *
     * @param bool $calculateMd5 Set to true to calculate the MD5 hash of the body
     *
     * @return $this
     */
    public function calculateMd5($calculateMd5)
    {
        $this->calculateEntireMd5 = (bool) $calculateMd5;

        return $this;
    }

    /**
     * Specify whether or not to calculate the MD5 hash of each uploaded part.
     * This setting defaults to true.
     *
     * @param bool $usePartMd5 Set to true to calculate the MD5 has of each part
     *
     * @return $this
     */
    public function calculatePartMd5($usePartMd5)
    {
        $this->calculatePartMd5 = (bool) $usePartMd5;

        return $this;
    }

    /**
     * Set the ACP to use on the object
     *
     * @param Acp $acp ACP to set on the object
     *
     * @return $this
     */
    public function setAcp(Acp $acp)
    {
        return $this->setOption('ACP', $acp);
    }

    /**
     * Set an option to pass to the initial CreateMultipartUpload operation
     *
     * @param string $name  Option name
     * @param string $value Option value
     *
     * @return $this
     */
    public function setOption($name, $value)
    {
        $this->commandOptions[$name] = $value;

        return $this;
    }

    /**
     * Add an array of options to pass to the initial CreateMultipartUpload operation
     *
     * @param array $options Array of CreateMultipartUpload operation parameters
     *
     * @return $this
     */
    public function addOptions(array $options)
    {
        $this->commandOptions = array_replace($this->commandOptions, $options);

        return $this;
    }

    /**
     * Set an array of transfer options to apply to the upload transfer object
     *
     * @param array $options Transfer options
     *
     * @return $this
     */
    public function setTransferOptions(array $options)
    {
        $this->transferOptions = $options;

        return $this;
    }

    /**
     * {@inheritdoc}
     * @throws InvalidArgumentException when attempting to resume a transfer using a non-seekable stream
     * @throws InvalidArgumentException when missing required properties (bucket, key, client, source)
     */
    public function build()
    {
        if ($this->state instanceof TransferState) {
            $this->commandOptions = array_replace($this->commandOptions, $this->state->getUploadId()->toParams());
        }

        if (!isset($this->commandOptions['Bucket']) || !isset($this->commandOptions['Key'])
            || !$this->client || !$this->source
        ) {
            throw new InvalidArgumentException('You must specify a Bucket, Key, client, and source.');
        }

        if ($this->state && !$this->source->isSeekable()) {
            throw new InvalidArgumentException('You cannot resume a transfer using a non-seekable source.');
        }

        // If no state was set, then create one by initiating or loading a multipart upload
        if (is_string($this->state)) {
            $this->state = TransferState::fromUploadId($this->client, UploadId::fromParams(array(
                'Bucket'   => $this->commandOptions['Bucket'],
                'Key'      => $this->commandOptions['Key'],
                'UploadId' => $this->state
            )));
        } elseif (!$this->state) {
            $this->state = $this->initiateMultipartUpload();
        }

        $options = array_replace(array(
            'min_part_size' => $this->minPartSize,
            'part_md5'      => (bool) $this->calculatePartMd5,
            'concurrency'   => $this->concurrency
        ), $this->transferOptions);

        return $this->concurrency > 1
            ? new ParallelTransfer($this->client, $this->state, $this->source, $options)
            : new SerialTransfer($this->client, $this->state, $this->source, $options);
    }

    /**
     * {@inheritdoc}
     */
    protected function initiateMultipartUpload()
    {
        // Determine Content-Type
        if (!isset($this->commandOptions['ContentType'])) {
            if ($mimeType = $this->source->getContentType()) {
                $this->commandOptions['ContentType'] = $mimeType;
            }
        }

        $params = array_replace(array(
            Ua::OPTION        => Ua::MULTIPART_UPLOAD,
            'command.headers' => $this->headers,
            'Metadata'        => array()
        ), $this->commandOptions);

        // Calculate the MD5 hash if none was set and it is asked of the builder
        if ($this->calculateEntireMd5) {
            $this->md5 = $this->source->getContentMd5();
        }

        // If an MD5 is specified, then add it to the custom headers of the request
        // so that it will be returned when downloading the object from Amazon S3
        if ($this->md5) {
            $params['Metadata']['x-amz-Content-MD5'] = $this->md5;
        }

        $result = $this->client->getCommand('CreateMultipartUpload', $params)->execute();
        // Create a new state based on the initiated upload
        $params['UploadId'] = $result['UploadId'];

        return new TransferState(UploadId::fromParams($params));
    }
}
includes/lib/aws-sdk-php-2.8.31/src/Aws/S3/Model/MultipartUpload/SerialTransfer.php000064400000006164151327705670023462 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\S3\Model\MultipartUpload;

use Aws\Common\Enum\DateFormat;
use Aws\Common\Enum\Size;
use Aws\Common\Enum\UaString as Ua;
use Guzzle\Http\EntityBody;
use Guzzle\Http\ReadLimitEntityBody;

/**
 * Transfers multipart upload parts serially
 */
class SerialTransfer extends AbstractTransfer
{
    /**
     * {@inheritdoc}
     */
    protected function transfer()
    {
        while (!$this->stopped && !$this->source->isConsumed()) {

            if ($this->source->getContentLength() && $this->source->isSeekable()) {
                // If the stream is seekable and the Content-Length known, then stream from the data source
                $body = new ReadLimitEntityBody($this->source, $this->partSize, $this->source->ftell());
            } else {
                // We need to read the data source into a temporary buffer before streaming
                $body = EntityBody::factory();
                while ($body->getContentLength() < $this->partSize
                    && $body->write(
                        $this->source->read(max(1, min(10 * Size::KB, $this->partSize - $body->getContentLength())))
                    ));
            }

            // @codeCoverageIgnoreStart
            if ($body->getContentLength() == 0) {
                break;
            }
            // @codeCoverageIgnoreEnd

            $params = $this->state->getUploadId()->toParams();
            $command = $this->client->getCommand('UploadPart', array_replace($params, array(
                'PartNumber' => count($this->state) + 1,
                'Body'       => $body,
                'ContentMD5' => (bool) $this->options['part_md5'],
                Ua::OPTION   => Ua::MULTIPART_UPLOAD
            )));

            // Notify observers that the part is about to be uploaded
            $eventData = $this->getEventData();
            $eventData['command'] = $command;
            $this->dispatch(self::BEFORE_PART_UPLOAD, $eventData);

            // Allow listeners to stop the transfer if needed
            if ($this->stopped) {
                break;
            }

            $response = $command->getResponse();

            $this->state->addPart(UploadPart::fromArray(array(
                'PartNumber'   => $command['PartNumber'],
                'ETag'         => $response->getEtag(),
                'Size'         => $body->getContentLength(),
                'LastModified' => gmdate(DateFormat::RFC2822)
            )));

            // Notify observers that the part was uploaded
            $this->dispatch(self::AFTER_PART_UPLOAD, $eventData);
        }
    }
}
includes/lib/aws-sdk-php-2.8.31/src/Aws/S3/Model/MultipartUpload/ParallelTransfer.php000064400000011027151327705670023771 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\S3\Model\MultipartUpload;

use Aws\Common\Exception\RuntimeException;
use Aws\Common\Enum\DateFormat;
use Aws\Common\Enum\UaString as Ua;
use Guzzle\Http\EntityBody;
use Guzzle\Http\ReadLimitEntityBody;

/**
 * Transfers multipart upload parts in parallel
 */
class ParallelTransfer extends AbstractTransfer
{
    /**
     * {@inheritdoc}
     */
    protected function init()
    {
        parent::init();

        if (!$this->source->isLocal() || $this->source->getWrapper() != 'plainfile') {
            throw new RuntimeException('The source data must be a local file stream when uploading in parallel.');
        }

        if (empty($this->options['concurrency'])) {
            throw new RuntimeException('The `concurrency` option must be specified when instantiating.');
        }
    }

    /**
     * {@inheritdoc}
     */
    protected function transfer()
    {
        $totalParts  = (int) ceil($this->source->getContentLength() / $this->partSize);
        $concurrency = min($totalParts, $this->options['concurrency']);
        $partsToSend = $this->prepareParts($concurrency);
        $eventData   = $this->getEventData();

        while (!$this->stopped && count($this->state) < $totalParts) {

            $currentTotal = count($this->state);
            $commands = array();

            for ($i = 0; $i < $concurrency && $i + $currentTotal < $totalParts; $i++) {

                // Move the offset to the correct position
                $partsToSend[$i]->setOffset(($currentTotal + $i) * $this->partSize);

                // @codeCoverageIgnoreStart
                if ($partsToSend[$i]->getContentLength() == 0) {
                    break;
                }
                // @codeCoverageIgnoreEnd

                $params = $this->state->getUploadId()->toParams();
                $eventData['command'] = $this->client->getCommand('UploadPart', array_replace($params, array(
                    'PartNumber' => count($this->state) + 1 + $i,
                    'Body'       => $partsToSend[$i],
                    'ContentMD5' => (bool) $this->options['part_md5'],
                    Ua::OPTION   => Ua::MULTIPART_UPLOAD
                )));
                $commands[] = $eventData['command'];
                // Notify any listeners of the part upload
                $this->dispatch(self::BEFORE_PART_UPLOAD, $eventData);
            }

            // Allow listeners to stop the transfer if needed
            if ($this->stopped) {
                break;
            }

            // Execute each command, iterate over the results, and add to the transfer state
            /** @var \Guzzle\Service\Command\OperationCommand $command */
            foreach ($this->client->execute($commands) as $command) {
                $this->state->addPart(UploadPart::fromArray(array(
                    'PartNumber'   => $command['PartNumber'],
                    'ETag'         => $command->getResponse()->getEtag(),
                    'Size'         => (int) $command->getRequest()->getBody()->getContentLength(),
                    'LastModified' => gmdate(DateFormat::RFC2822)
                )));
                $eventData['command'] = $command;
                // Notify any listeners the the part was uploaded
                $this->dispatch(self::AFTER_PART_UPLOAD, $eventData);
            }
        }
    }

    /**
     * Prepare the entity body handles to use while transferring
     *
     * @param int $concurrency Number of parts to prepare
     *
     * @return array Parts to send
     */
    protected function prepareParts($concurrency)
    {
        $url = $this->source->getUri();
        // Use the source EntityBody as the first part
        $parts = array(new ReadLimitEntityBody($this->source, $this->partSize));
        // Open EntityBody handles for each part to upload in parallel
        for ($i = 1; $i < $concurrency; $i++) {
            $parts[] = new ReadLimitEntityBody(new EntityBody(fopen($url, 'r')), $this->partSize);
        }

        return $parts;
    }
}
includes/lib/aws-sdk-php-2.8.31/src/Aws/S3/Model/MultipartUpload/AbstractTransfer.php000064400000006414151327705670024004 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\S3\Model\MultipartUpload;

use Aws\Common\Enum\UaString as Ua;
use Aws\Common\Exception\RuntimeException;
use Aws\Common\Model\MultipartUpload\AbstractTransfer as CommonAbstractTransfer;
use Guzzle\Service\Command\OperationCommand;

/**
 * Abstract class for transfer commonalities
 */
abstract class AbstractTransfer extends CommonAbstractTransfer
{
    // An S3 upload part can be anywhere from 5 MB to 5 GB, but you can only have 10000 parts per upload
    const MIN_PART_SIZE = 5242880;
    const MAX_PART_SIZE = 5368709120;
    const MAX_PARTS     = 10000;

    /**
     * {@inheritdoc}
     * @throws RuntimeException if the part size can not be calculated from the provided data
     */
    protected function init()
    {
        // Merge provided options onto the default option values
        $this->options = array_replace(array(
            'min_part_size' => self::MIN_PART_SIZE,
            'part_md5'      => true
        ), $this->options);

        // Make sure the part size can be calculated somehow
        if (!$this->options['min_part_size'] && !$this->source->getContentLength()) {
            throw new RuntimeException('The ContentLength of the data source could not be determined, and no '
                . 'min_part_size option was provided');
        }
    }

    /**
     * {@inheritdoc}
     */
    protected function calculatePartSize()
    {
        $partSize = $this->source->getContentLength()
            ? (int) ceil(($this->source->getContentLength() / self::MAX_PARTS))
            : self::MIN_PART_SIZE;
        $partSize = max($this->options['min_part_size'], $partSize);
        $partSize = min($partSize, self::MAX_PART_SIZE);
        $partSize = max($partSize, self::MIN_PART_SIZE);

        return $partSize;
    }

    /**
     * {@inheritdoc}
     */
    protected function complete()
    {
        /** @var UploadPart $part */
        $parts = array();
        foreach ($this->state as $part) {
            $parts[] = array(
                'PartNumber' => $part->getPartNumber(),
                'ETag'       => $part->getETag(),
            );
        }

        $params = $this->state->getUploadId()->toParams();
        $params[Ua::OPTION] = Ua::MULTIPART_UPLOAD;
        $params['Parts'] = $parts;
        $command = $this->client->getCommand('CompleteMultipartUpload', $params);

        return $command->getResult();
    }

    /**
     * {@inheritdoc}
     */
    protected function getAbortCommand()
    {
        $params = $this->state->getUploadId()->toParams();
        $params[Ua::OPTION] = Ua::MULTIPART_UPLOAD;

        /** @var OperationCommand $command */
        $command = $this->client->getCommand('AbortMultipartUpload', $params);

        return $command;
    }
}
includes/lib/aws-sdk-php-2.8.31/src/Aws/S3/Model/MultipartUpload/UploadPart.php000064400000003204151327705670022601 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\S3\Model\MultipartUpload;

use Aws\Common\Model\MultipartUpload\AbstractUploadPart;

/**
 * An object that encapsulates the data for a Glacier upload operation
 */
class UploadPart extends AbstractUploadPart
{
    /**
     * {@inheritdoc}
     */
    protected static $keyMap = array(
        'PartNumber'   => 'partNumber',
        'ETag'         => 'eTag',
        'LastModified' => 'lastModified',
        'Size'         => 'size'
    );

    /**
     * @var string The ETag for this part
     */
    protected $eTag;

    /**
     * @var string The last modified date
     */
    protected $lastModified;

    /**
     * @var int The size (or content-length) in bytes of the upload body
     */
    protected $size;

    /**
     * @return string
     */
    public function getETag()
    {
        return $this->eTag;
    }

    /**
     * @return string
     */
    public function getLastModified()
    {
        return $this->lastModified;
    }

    /**
     * @return int
     */
    public function getSize()
    {
        return $this->size;
    }
}
includes/lib/aws-sdk-php-2.8.31/src/Aws/S3/Model/MultipartUpload/UploadId.php000064400000001770151327705670022235 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\S3\Model\MultipartUpload;

use Aws\Common\Model\MultipartUpload\AbstractUploadId;

/**
 * An object that encapsulates the identification for a Glacier upload part
 * @codeCoverageIgnore
 */
class UploadId extends AbstractUploadId
{
    /**
     * {@inheritdoc}
     */
    protected static $expectedValues = array(
        'Bucket'   => false,
        'Key'      => false,
        'UploadId' => false
    );
}
includes/lib/aws-sdk-php-2.8.31/src/Aws/S3/Model/MultipartUpload/TransferState.php000064400000002371151327705670023317 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\S3\Model\MultipartUpload;

use Aws\Common\Client\AwsClientInterface;
use Aws\Common\Model\MultipartUpload\AbstractTransferState;
use Aws\Common\Model\MultipartUpload\UploadIdInterface;

/**
 * State of a multipart upload
 */
class TransferState extends AbstractTransferState
{
    /**
     * {@inheritdoc}
     */
    public static function fromUploadId(AwsClientInterface $client, UploadIdInterface $uploadId)
    {
        $transferState = new self($uploadId);

        foreach ($client->getIterator('ListParts', $uploadId->toParams()) as $part) {
            $transferState->addPart(UploadPart::fromArray($part));
        }

        return $transferState;
    }
}
includes/lib/aws-sdk-php-2.8.31/src/Aws/S3/Model/Acp.php000064400000014452151327705670016112 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\S3\Model;

use Aws\Common\Exception\InvalidArgumentException;
use Aws\Common\Exception\OverflowException;
use Guzzle\Common\ToArrayInterface;
use Guzzle\Service\Command\AbstractCommand;

/**
 * Amazon S3 Access Control Policy (ACP)
 */
class Acp implements ToArrayInterface, \IteratorAggregate, \Countable
{
    /**
     * @var \SplObjectStorage List of grants on the ACP
     */
    protected $grants = array();

    /**
     * @var Grantee The owner of the ACP
     */
    protected $owner;

    /**
     * Constructs an ACP
     *
     * @param Grantee            $owner  ACP policy owner
     * @param array|\Traversable $grants List of grants for the ACP
     */
    public function __construct(Grantee $owner, $grants = null)
    {
        $this->setOwner($owner);
        $this->setGrants($grants);
    }

    /**
     * Create an Acp object from an array. This can be used to create an ACP from a response to a GetObject/Bucket ACL
     * operation.
     *
     * @param array $data Array of ACP data
     *
     * @return Acp
     */
    public static function fromArray(array $data)
    {
        $builder = new AcpBuilder();
        $builder->setOwner((string) $data['Owner']['ID'], $data['Owner']['DisplayName']);

        // Add each Grantee to the ACP
        foreach ($data['Grants'] as $grant) {
            $permission = $grant['Permission'];

            // Determine the type for response bodies that are missing the Type parameter
            if (!isset($grant['Grantee']['Type'])) {
                if (isset($grant['Grantee']['ID'])) {
                    $grant['Grantee']['Type'] = 'CanonicalUser';
                } elseif (isset($grant['Grantee']['URI'])) {
                    $grant['Grantee']['Type'] = 'Group';
                } else {
                    $grant['Grantee']['Type'] = 'AmazonCustomerByEmail';
                }
            }

            switch ($grant['Grantee']['Type']) {
                case 'Group':
                    $builder->addGrantForGroup($permission, $grant['Grantee']['URI']);
                    break;
                case 'AmazonCustomerByEmail':
                    $builder->addGrantForEmail($permission, $grant['Grantee']['EmailAddress']);
                    break;
                case 'CanonicalUser':
                    $builder->addGrantForUser(
                        $permission,
                        $grant['Grantee']['ID'],
                        $grant['Grantee']['DisplayName']
                    );
            }
        }

        return $builder->build();
    }

    /**
     * Set the owner of the ACP policy
     *
     * @param Grantee $owner ACP policy owner
     *
     * @return $this
     *
     * @throws InvalidArgumentException if the grantee does not have an ID set
     */
    public function setOwner(Grantee $owner)
    {
        if (!$owner->isCanonicalUser()) {
            throw new InvalidArgumentException('The owner must have an ID set.');
        }

        $this->owner = $owner;

        return $this;
    }

    /**
     * Get the owner of the ACP policy
     *
     * @return Grantee
     */
    public function getOwner()
    {
        return $this->owner;
    }

    /**
     * Set the grants for the ACP
     *
     * @param array|\Traversable $grants List of grants for the ACP
     *
     * @return $this
     *
     * @throws InvalidArgumentException
     */
    public function setGrants($grants = array())
    {
        $this->grants = new \SplObjectStorage();

        if ($grants) {
            if (is_array($grants) || $grants instanceof \Traversable) {
                /** @var Grant $grant */
                foreach ($grants as $grant) {
                    $this->addGrant($grant);
                }
            } else {
                throw new InvalidArgumentException('Grants must be passed in as an array or Traversable object.');
            }
        }

        return $this;
    }

    /**
     * Get all of the grants
     *
     * @return \SplObjectStorage
     */
    public function getGrants()
    {
        return $this->grants;
    }

    /**
     * Add a Grant
     *
     * @param Grant $grant Grant to add
     *
     * @return $this
     */
    public function addGrant(Grant $grant)
    {
        if (count($this->grants) < 100) {
            $this->grants->attach($grant);
        } else {
            throw new OverflowException('An ACP may contain up to 100 grants.');
        }

        return $this;
    }

    /**
     * Get the total number of attributes
     *
     * @return int
     */
    public function count()
    {
        return count($this->grants);
    }

    /**
     * Returns the grants for iteration
     *
     * @return \SplObjectStorage
     */
    public function getIterator()
    {
        return $this->grants;
    }

    /**
     * Applies grant headers to a command's parameters
     *
     * @param AbstractCommand $command Command to be updated
     *
     * @return $this
     */
    public function updateCommand(AbstractCommand $command)
    {
        $parameters = array();
        foreach ($this->grants as $grant) {
            /** @var Grant $grant */
            $parameters = array_merge_recursive($parameters, $grant->getParameterArray());
        }

        foreach ($parameters as $name => $values) {
            $command->set($name, implode(', ', (array) $values));
        }

        return $this;
    }

    /**
     * {@inheritdoc}
     */
    public function toArray()
    {
        $grants = array();
        foreach ($this->grants as $grant) {
            $grants[] = $grant->toArray();
        }

        return array(
            'Owner' => array(
                'ID'          => $this->owner->getId(),
                'DisplayName' => $this->owner->getDisplayName()
            ),
            'Grants' => $grants
        );
    }
}
includes/lib/aws-sdk-php-2.8.31/src/Aws/S3/Model/DeleteObjectsTransfer.php000064400000007612151327705670021630 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\S3\Model;

use Aws\Common\Client\AwsClientInterface;
use Aws\Common\Exception\OverflowException;
use Aws\Common\Enum\UaString as Ua;
use Aws\S3\Exception\InvalidArgumentException;
use Aws\S3\Exception\DeleteMultipleObjectsException;
use Guzzle\Batch\BatchTransferInterface;
use Guzzle\Service\Command\CommandInterface;

/**
 * Transfer logic for deleting multiple objects from an Amazon S3 bucket in a
 * single request
 */
class DeleteObjectsTransfer implements BatchTransferInterface
{
    /**
     * @var AwsClientInterface The Amazon S3 client for doing transfers
     */
    protected $client;

    /**
     * @var string Bucket from which to delete the objects
     */
    protected $bucket;

    /**
     * @var string MFA token to apply to the request
     */
    protected $mfa;

    /**
     * Constructs a transfer using the injected client
     *
     * @param AwsClientInterface $client Client used to transfer the requests
     * @param string             $bucket Name of the bucket that stores the objects
     * @param string             $mfa    MFA token used when contacting the Amazon S3 API
     */
    public function __construct(AwsClientInterface $client, $bucket, $mfa = null)
    {
        $this->client = $client;
        $this->bucket = $bucket;
        $this->mfa = $mfa;
    }

    /**
     * Set a new MFA token value
     *
     * @param string $token MFA token
     *
     * @return $this
     */
    public function setMfa($token)
    {
        $this->mfa = $token;

        return $this;
    }

    /**
     * {@inheritdoc}
     * @throws OverflowException        if a batch has more than 1000 items
     * @throws InvalidArgumentException when an invalid batch item is encountered
     */
    public function transfer(array $batch)
    {
        if (empty($batch)) {
            return;
        }

        if (count($batch) > 1000) {
            throw new OverflowException('Batches should be divided into chunks of no larger than 1000 keys');
        }

        $del = array();
        $command = $this->client->getCommand('DeleteObjects', array(
            'Bucket'   => $this->bucket,
            Ua::OPTION => Ua::BATCH
        ));

        if ($this->mfa) {
            $command->getRequestHeaders()->set('x-amz-mfa', $this->mfa);
        }

        foreach ($batch as $object) {
            // Ensure that the batch item is valid
            if (!is_array($object) || !isset($object['Key'])) {
                throw new InvalidArgumentException('Invalid batch item encountered: ' . esc_html(var_export($batch, true)));
            }
            $del[] = array(
                'Key'       => $object['Key'],
                'VersionId' => isset($object['VersionId']) ? $object['VersionId'] : null
            );
        }

        $command['Objects'] = $del;

        $command->execute();
        $this->processResponse($command);
    }

    /**
     * Process the response of the DeleteMultipleObjects request
     *
     * @paramCommandInterface $command Command executed
     */
    protected function processResponse(CommandInterface $command)
    {
        $result = $command->getResult();

        // Ensure that the objects were deleted successfully
        if (!empty($result['Errors'])) {
            $errors = $result['Errors'];
            throw new DeleteMultipleObjectsException(esc_html($errors));
        }
    }
}
includes/lib/aws-sdk-php-2.8.31/src/Aws/S3/Model/ClearBucket.php000064400000012655151327705670017576 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\S3\Model;

use Aws\Common\Client\AwsClientInterface;
use Guzzle\Common\AbstractHasDispatcher;
use Guzzle\Batch\FlushingBatch;
use Guzzle\Batch\ExceptionBufferingBatch;
use Guzzle\Batch\NotifyingBatch;
use Guzzle\Common\Exception\ExceptionCollection;

/**
 * Class used to clear the contents of a bucket or the results of an iterator
 */
class ClearBucket extends AbstractHasDispatcher
{
    /**
     * @var string Event emitted when a batch request has completed
     */
    const AFTER_DELETE = 'clear_bucket.after_delete';

    /**
     * @var string Event emitted before the bucket is cleared
     */
    const BEFORE_CLEAR = 'clear_bucket.before_clear';

    /**
     * @var string Event emitted after the bucket is cleared
     */
    const AFTER_CLEAR = 'clear_bucket.after_clear';

    /**
     * @var AwsClientInterface Client used to execute the requests
     */
    protected $client;

    /**
     * @var AbstractS3ResourceIterator Iterator used to yield keys
     */
    protected $iterator;

    /**
     * @var string MFA used with each request
     */
    protected $mfa;

    /**
     * @param AwsClientInterface $client Client used to execute requests
     * @param string             $bucket Name of the bucket to clear
     */
    public function __construct(AwsClientInterface $client, $bucket)
    {
        $this->client = $client;
        $this->bucket = $bucket;
    }

    /**
     * {@inheritdoc}
     */
    public static function getAllEvents()
    {
        return array(self::AFTER_DELETE, self::BEFORE_CLEAR, self::AFTER_CLEAR);
    }

    /**
     * Set the bucket that is to be cleared
     *
     * @param string $bucket Name of the bucket to clear
     *
     * @return $this
     */
    public function setBucket($bucket)
    {
        $this->bucket = $bucket;

        return $this;
    }

    /**
     * Get the iterator used to yield the keys to be deleted. A default iterator
     * will be created and returned if no iterator has been explicitly set.
     *
     * @return \Iterator
     */
    public function getIterator()
    {
        if (!$this->iterator) {
            $this->iterator = $this->client->getIterator('ListObjectVersions', array(
                'Bucket' => $this->bucket
            ));
        }

        return $this->iterator;
    }

    /**
     * Sets a different iterator to use than the default iterator. This can be helpful when you wish to delete
     * only specific keys from a bucket (e.g. keys that match a certain prefix or delimiter, or perhaps keys that
     * pass through a filtered, decorated iterator).
     *
     * @param \Iterator $iterator Iterator used to yield the keys to be deleted
     *
     * @return $this
     */
    public function setIterator(\Iterator $iterator)
    {
        $this->iterator = $iterator;

        return $this;
    }

    /**
     * Set the MFA token to send with each request
     *
     * @param string $mfa MFA token to send with each request. The value is the concatenation of the authentication
     *                    device's serial number, a space, and the value displayed on your authentication device.
     *
     * @return $this
     */
    public function setMfa($mfa)
    {
        $this->mfa = $mfa;

        return $this;
    }

    /**
     * Clear the bucket
     *
     * @return int Returns the number of deleted keys
     * @throws ExceptionCollection
     */
    public function clear()
    {
        $that = $this;
        $batch = DeleteObjectsBatch::factory($this->client, $this->bucket, $this->mfa);
        $batch = new NotifyingBatch($batch, function ($items) use ($that) {
            $that->dispatch(ClearBucket::AFTER_DELETE, array('keys' => $items));
        });
        $batch = new FlushingBatch(new ExceptionBufferingBatch($batch), 1000);

        // Let any listeners know that the bucket is about to be cleared
        $this->dispatch(self::BEFORE_CLEAR, array(
            'iterator' => $this->getIterator(),
            'batch'    => $batch,
            'mfa'      => $this->mfa
        ));

        $deleted = 0;
        foreach ($this->getIterator() as $object) {
            if (isset($object['VersionId'])) {
                $versionId = $object['VersionId'] == 'null' ? null : $object['VersionId'];
            } else {
                $versionId = null;
            }
            $batch->addKey($object['Key'], $versionId);
            $deleted++;
        }
        $batch->flush();

        // If any errors were encountered, then throw an ExceptionCollection
        if (count($batch->getExceptions())) {
            $e = new ExceptionCollection();
            foreach ($batch->getExceptions() as $exception) {
                $e->add($exception->getPrevious());
            }
            throw $e;
        }

        // Let any listeners know that the bucket was cleared
        $this->dispatch(self::AFTER_CLEAR, array('deleted' => $deleted));

        return $deleted;
    }
}
includes/lib/aws-sdk-php-2.8.31/src/Aws/S3/Model/Grant.php000064400000006473151327705670016466 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\S3\Model;

use Aws\S3\Enum\Permission;
use Aws\Common\Exception\InvalidArgumentException;
use Guzzle\Common\ToArrayInterface;

/**
 * Amazon S3 Grant model
 */
class Grant implements ToArrayInterface
{
    /**
     * @var array A map of permissions to operation parameters
     */
    protected static $parameterMap = array(
        Permission::READ         => 'GrantRead',
        Permission::WRITE        => 'GrantWrite',
        Permission::READ_ACP     => 'GrantReadACP',
        Permission::WRITE_ACP    => 'GrantWriteACP',
        Permission::FULL_CONTROL => 'GrantFullControl'
    );

    /**
     * @var Grantee The grantee affected by the grant
     */
    protected $grantee;

    /**
     * @var string The permission set by the grant
     */
    protected $permission;

    /**
     * Constructs an ACL
     *
     * @param Grantee $grantee    Affected grantee
     * @param string  $permission Permission applied
     */
    public function __construct(Grantee $grantee, $permission)
    {
        $this->setGrantee($grantee);
        $this->setPermission($permission);
    }

    /**
     * Set the grantee affected by the grant
     *
     * @param Grantee $grantee Affected grantee
     *
     * @return $this
     */
    public function setGrantee(Grantee $grantee)
    {
        $this->grantee = $grantee;

        return $this;
    }

    /**
     * Get the grantee affected by the grant
     *
     * @return Grantee
     */
    public function getGrantee()
    {
        return $this->grantee;
    }

    /**
     * Set the permission set by the grant
     *
     * @param string $permission Permission applied
     *
     * @return $this
     *
     * @throws InvalidArgumentException
     */
    public function setPermission($permission)
    {
        $valid = Permission::values();
        if (!in_array($permission, $valid)) {
            throw new InvalidArgumentException('The permission must be one of '
                . 'the following: ' . esc_html(implode(', ', $valid)) . '.');
        }

        $this->permission = $permission;

        return $this;
    }

    /**
     * Get the permission set by the grant
     *
     * @return string
     */
    public function getPermission()
    {
        return $this->permission;
    }

    /**
     * Returns an array of the operation parameter and value to set on the operation
     *
     * @return array
     */
    public function getParameterArray()
    {
        return array(
            self::$parameterMap[$this->permission] => $this->grantee->getHeaderValue()
        );
    }

    /**
     * {@inheritdoc}
     */
    public function toArray()
    {
        return array(
            'Grantee'    => $this->grantee->toArray(),
            'Permission' => $this->permission
        );
    }
}
includes/lib/aws-sdk-php-2.8.31/src/Aws/S3/Model/PostObject.php000064400000021612151327705670017457 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\S3\Model;

use Aws\Common\Enum\DateFormat;
use Aws\S3\S3Client;
use Guzzle\Common\Collection;
use Guzzle\Http\Url;

/**
 * Encapsulates the logic for getting the data for an S3 object POST upload form
 */
class PostObject extends Collection
{
    /**
     * @var S3Client The S3 client being used to sign the policy
     */
    protected $client;

    /**
     * @var string The bucket name where the object will be posted
     */
    protected $bucket;

    /**
     * @var array The <form> tag attributes as an array
     */
    protected $formAttributes;

    /**
     * @var array The form's <input> elements as an array
     */
    protected $formInputs;

    /**
     * @var string The raw json policy
     */
    protected $jsonPolicy;

    /**
     * Constructs the PostObject
     *
     * The options array accepts the following keys:
     *
     * - acl:                          The access control setting to apply to the uploaded file. Accepts any of the
     *                                 CannedAcl constants
     * - Cache-Control:                The Cache-Control HTTP header value to apply to the uploaded file
     * - Content-Disposition:          The Content-Disposition HTTP header value to apply to the uploaded file
     * - Content-Encoding:             The Content-Encoding HTTP header value to apply to the uploaded file
     * - Content-Type:                 The Content-Type HTTP header value to apply to the uploaded file. The default
     *                                 value is `application/octet-stream`
     * - Expires:                      The Expires HTTP header value to apply to the uploaded file
     * - key:                          The location where the file should be uploaded to. The default value is
     *                                 `^${filename}` which will use the name of the uploaded file
     * - policy:                       A raw policy in JSON format. By default, the PostObject creates one for you
     * - policy_callback:              A callback used to modify the policy before encoding and signing it. The
     *                                 method signature for the callback should accept an array of the policy data as
     *                                 the 1st argument, (optionally) the PostObject as the 2nd argument, and return
     *                                 the policy data with the desired modifications.
     * - success_action_redirect:      The URI for Amazon S3 to redirect to upon successful upload
     * - success_action_status:        The status code for Amazon S3 to return upon successful upload
     * - ttd:                          The expiration time for the generated upload form data
     * - x-amz-meta-*:                 Any custom meta tag that should be set to the object
     * - x-amz-server-side-encryption: The server-side encryption mechanism to use
     * - x-amz-storage-class:          The storage setting to apply to the object
     * - x-amz-server-side​-encryption​-customer-algorithm: The SSE-C algorithm
     * - x-amz-server-side​-encryption​-customer-key:       The SSE-C customer secret key
     * - x-amz-server-side​-encryption​-customer-key-MD5:   The MD5 hash of the SSE-C customer secret key
     *
     * For the Cache-Control, Content-Disposition, Content-Encoding,
     * Content-Type, Expires, and key options, to use a "starts-with" comparison
     * instead of an equals comparison, prefix the value with a ^ (carat)
     * character
     *
     * @param S3Client $client
     * @param $bucket
     * @param array $options
     */
    public function __construct(S3Client $client, $bucket, array $options = array())
    {
        $this->setClient($client);
        $this->setBucket($bucket);
        parent::__construct($options);
    }

    /**
     * Analyzes the provided data and turns it into useful data that can be
     * consumed and used to build an upload form
     *
     * @return PostObject
     */
    public function prepareData()
    {
        // Validate required options
        $options = Collection::fromConfig($this->data, array(
            'ttd' => '+1 hour',
            'key' => '^${filename}',
        ));

        // Format ttd option
        $ttd = $options['ttd'];
        $ttd = is_numeric($ttd) ? (int) $ttd : strtotime($ttd);
        unset($options['ttd']);

        // If a policy or policy callback were provided, extract those from the options
        $rawJsonPolicy = $options['policy'];
        $policyCallback = $options['policy_callback'];
        unset($options['policy'], $options['policy_callback']);

        // Setup policy document
        $policy = array(
            'expiration' => gmdate(DateFormat::ISO8601_S3, $ttd),
            'conditions' => array(array('bucket' => $this->bucket))
        );

        // Configure the endpoint/action
        $url = Url::factory($this->client->getBaseUrl());
        if ($url->getScheme() === 'https' && strpos($this->bucket, '.') !== false) {
            // Use path-style URLs
            $url->setPath($this->bucket);
        } else {
            // Use virtual-style URLs
            $url->setHost($this->bucket . '.' . $url->getHost());
        }

        // Setup basic form
        $this->formAttributes = array(
            'action' => (string) $url,
            'method' => 'POST',
            'enctype' => 'multipart/form-data'
        );
        $this->formInputs = array(
            'AWSAccessKeyId' => $this->client->getCredentials()->getAccessKeyId()
        );

        // Add success action status
        $status = (int) $options->get('success_action_status');
        if ($status && in_array($status, array(200, 201, 204))) {
            $this->formInputs['success_action_status'] = (string) $status;
            $policy['conditions'][] = array(
                'success_action_status' => (string) $status
            );
            unset($options['success_action_status']);
        }

        // Add other options
        foreach ($options as $key => $value) {
            $value = (string) $value;
            if ($value[0] === '^') {
                $value = substr($value, 1);
                $this->formInputs[$key] = $value;
                $value = preg_replace('/\$\{(\w*)\}/', '', $value);
                $policy['conditions'][] = array('starts-with', '$' . $key, $value);
            } else {
                $this->formInputs[$key] = $value;
                $policy['conditions'][] = array($key => $value);
            }
        }

        // Handle the policy
        $policy = is_callable($policyCallback) ? $policyCallback($policy, $this) : $policy;
        $this->jsonPolicy = $rawJsonPolicy ?: json_encode($policy);
        $this->applyPolicy();

        return $this;
    }

    /**
     * Sets the S3 client
     *
     * @param S3Client $client
     *
     * @return PostObject
     */
    public function setClient(S3Client $client)
    {
        $this->client = $client;

        return $this;
    }

    /**
     * Gets the S3 client
     *
     * @return S3Client
     */
    public function getClient()
    {
        return $this->client;
    }

    /**
     * Sets the bucket and makes sure it is a valid bucket name
     *
     * @param string $bucket
     *
     * @return PostObject
     */
    public function setBucket($bucket)
    {
        $this->bucket = $bucket;

        return $this;
    }

    /**
     * Gets the bucket name
     *
     * @return string
     */
    public function getBucket()
    {
        return $this->bucket;
    }

    /**
     * Gets the form attributes as an array
     *
     * @return array
     */
    public function getFormAttributes()
    {
        return $this->formAttributes;
    }

    /**
     * Gets the form inputs as an array
     *
     * @return array
     */
    public function getFormInputs()
    {
        return $this->formInputs;
    }

    /**
     * Gets the raw JSON policy
     *
     * @return string
     */
    public function getJsonPolicy()
    {
        return $this->jsonPolicy;
    }

    /**
     * Handles the encoding, singing, and injecting of the policy
     */
    protected function applyPolicy()
    {
        $jsonPolicy64 = base64_encode($this->jsonPolicy);
        $this->formInputs['policy'] = $jsonPolicy64;

        $this->formInputs['signature'] = base64_encode(hash_hmac(
            'sha1',
            $jsonPolicy64,
            $this->client->getCredentials()->getSecretKey(),
            true
        ));
    }
}
includes/lib/aws-sdk-php-2.8.31/src/Aws/S3/Command/S3Command.php000064400000003732151327705670017510 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\S3\Command;

use Guzzle\Service\Command\OperationCommand;
use Guzzle\Service\Resource\Model;
use Guzzle\Common\Event;

/**
 * Adds functionality to Amazon S3 commands:
 * - Adds the PutObject URL to a response
 * - Allows creating a Pre-signed URL from any command
 */
class S3Command extends OperationCommand
{
    /**
     * Create a pre-signed URL for the operation
     *
     * @param int|string $expires The Unix timestamp to expire at or a string that can be evaluated by strtotime
     *
     * @return string
     */
    public function createPresignedUrl($expires)
    {
        return $this->client->createPresignedUrl($this->prepare(), $expires);
    }

    /**
     * {@inheritdoc}
     */
    protected function process()
    {
        $request = $this->getRequest();
        $response = $this->getResponse();

        // Dispatch an error if a 301 redirect occurred
        if ($response->getStatusCode() == 301) {
            $this->getClient()->getEventDispatcher()->dispatch('request.error', new Event(array(
                'request'  => $this->getRequest(),
                'response' => $response
            )));
        }

        parent::process();

        // Set the GetObject URL if using the PutObject operation
        if ($this->result instanceof Model && $this->getName() == 'PutObject') {
            $this->result->set('ObjectURL', $request->getUrl());
        }
    }
}
includes/lib/aws-sdk-php-2.8.31/src/Aws/S3/IncompleteMultipartUploadChecker.php000064400000003642151327705670023001 0ustar00<?php

namespace Aws\S3;

use Guzzle\Http\Exception\HttpException;
use Guzzle\Http\Message\RequestInterface;
use Guzzle\Http\Message\EntityEnclosingRequestInterface;
use Guzzle\Http\Message\Response;
use Guzzle\Plugin\Backoff\BackoffStrategyInterface;
use Guzzle\Plugin\Backoff\AbstractBackoffStrategy;

/**
 * Retries CompleteMultipartUpload requests in the case of failure.
 *
 * From the S3 API Documentation:
 *
 *     Processing of a Complete Multipart Upload request could take several
 *     minutes to complete. After Amazon S3 begins processing the request, it
 *     sends an HTTP response header that specifies a 200 OK response. While
 *     processing is in progress, Amazon S3 periodically sends whitespace
 *     characters to keep the connection from timing out. Because a request
 *     could fail after the initial 200 OK response has been sent, it is
 *     important that you check the response body to determine whether the
 *     request succeeded. Note that if Complete Multipart Upload fails,
 *     applications should be prepared to retry the failed requests.
 */
class IncompleteMultipartUploadChecker extends AbstractBackoffStrategy
{
    public function __construct(BackoffStrategyInterface $next = null)
    {
        if ($next) {
            $this->setNext($next);
        }
    }

    public function makesDecision()
    {
        return true;
    }

    protected function getDelay(
        $retries,
        RequestInterface $request,
        Response $response = null,
        HttpException $e = null
    ) {
        if ($response && $request->getMethod() === 'POST'
            && $request instanceof EntityEnclosingRequestInterface
            && $response->getStatusCode() == 200
            && strpos($request->getBody(), '<CompleteMultipartUpload xmlns') !== false
            && strpos($response->getBody(), '<CompleteMultipartUploadResult xmlns') === false
        ) {
            return true;
        }
    }
}
includes/lib/aws-sdk-php-2.8.31/src/Aws/S3/S3SignatureInterface.php000064400000001345151327705670020334 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\S3;

use Aws\Common\Signature\SignatureInterface;

/**
 * @deprecated
 */
interface S3SignatureInterface extends SignatureInterface {}
includes/lib/aws-sdk-php-2.8.31/src/Aws/S3/S3Md5Listener.php000064400000004453151327705670016710 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\S3;

use Aws\Common\Signature\SignatureV4;
use Aws\Common\Signature\SignatureInterface;
use Guzzle\Common\Event;
use Guzzle\Service\Command\CommandInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;

/**
 * Adds required and optional Content-MD5 headers
 */
class S3Md5Listener implements EventSubscriberInterface
{
    /** @var S3SignatureInterface */
    private $signature;

    public static function getSubscribedEvents()
    {
        return array('command.after_prepare' => 'onCommandAfterPrepare');
    }

    public function __construct(SignatureInterface $signature)
    {
        $this->signature = $signature;
    }

    public function onCommandAfterPrepare(Event $event)
    {
        $command = $event['command'];
        $operation = $command->getOperation();

        if ($operation->getData('contentMd5')) {
            // Add the MD5 if it is required for all signers
            $this->addMd5($command);
        } elseif ($operation->hasParam('ContentMD5')) {
            $value = $command['ContentMD5'];
            // Add a computed MD5 if the parameter is set to true or if
            // not using Signature V4 and the value is not set (null).
            if ($value === true ||
                ($value === null && !($this->signature instanceof SignatureV4))
            ) {
                $this->addMd5($command);
            }
        }
    }

    private function addMd5(CommandInterface $command)
    {
        $request = $command->getRequest();
        $body = $request->getBody();
        if ($body && $body->getSize() > 0) {
            if (false !== ($md5 = $body->getContentMd5(true, true))) {
                $request->setHeader('Content-MD5', $md5);
            }
        }
    }
}
includes/lib/aws-sdk-php-2.8.31/src/Aws/S3/StreamWrapper.php000064400000071021151327705670017136 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\S3;

use Aws\Common\Exception\RuntimeException;
use Aws\S3\Exception\S3Exception;
use Aws\S3\Exception\NoSuchKeyException;
use Aws\S3\Iterator\ListObjectsIterator;
use Guzzle\Http\EntityBody;
use Guzzle\Http\CachingEntityBody;
use Guzzle\Http\Mimetypes;
use Guzzle\Iterator\FilterIterator;
use Guzzle\Stream\PhpStreamRequestFactory;
use Guzzle\Service\Command\CommandInterface;

/**
 * Amazon S3 stream wrapper to use "s3://<bucket>/<key>" files with PHP streams, supporting "r", "w", "a", "x".
 *
 * # Supported stream related PHP functions:
 * - fopen, fclose, fread, fwrite, fseek, ftell, feof, fflush
 * - opendir, closedir, readdir, rewinddir
 * - copy, rename, unlink
 * - mkdir, rmdir, rmdir (recursive)
 * - file_get_contents, file_put_contents
 * - file_exists, filesize, is_file, is_dir
 *
 * # Opening "r" (read only) streams:
 *
 * Read only streams are truly streaming by default and will not allow you to seek. This is because data
 * read from the stream is not kept in memory or on the local filesystem. You can force a "r" stream to be seekable
 * by setting the "seekable" stream context option true. This will allow true streaming of data from Amazon S3, but
 * will maintain a buffer of previously read bytes in a 'php://temp' stream to allow seeking to previously read bytes
 * from the stream.
 *
 * You may pass any GetObject parameters as 's3' stream context options. These options will affect how the data is
 * downloaded from Amazon S3.
 *
 * # Opening "w" and "x" (write only) streams:
 *
 * Because Amazon S3 requires a Content-Length header, write only streams will maintain a 'php://temp' stream to buffer
 * data written to the stream until the stream is flushed (usually by closing the stream with fclose).
 *
 * You may pass any PutObject parameters as 's3' stream context options. These options will affect how the data is
 * uploaded to Amazon S3.
 *
 * When opening an "x" stream, the file must exist on Amazon S3 for the stream to open successfully.
 *
 * # Opening "a" (write only append) streams:
 *
 * Similar to "w" streams, opening append streams requires that the data be buffered in a "php://temp" stream. Append
 * streams will attempt to download the contents of an object in Amazon S3, seek to the end of the object, then allow
 * you to append to the contents of the object. The data will then be uploaded using a PutObject operation when the
 * stream is flushed (usually with fclose).
 *
 * You may pass any GetObject and/or PutObject parameters as 's3' stream context options. These options will affect how
 * the data is downloaded and uploaded from Amazon S3.
 *
 * Stream context options:
 *
 * - "seekable": Set to true to create a seekable "r" (read only) stream by using a php://temp stream buffer
 * - For "unlink" only: Any option that can be passed to the DeleteObject operation
 */
class StreamWrapper
{
    /**
     * @var resource|null Stream context (this is set by PHP when a context is used)
     */
    public $context;

    /**
     * @var S3Client Client used to send requests
     */
    protected static $client;

    /**
     * @var string Mode the stream was opened with
     */
    protected $mode;

    /**
     * @var EntityBody Underlying stream resource
     */
    protected $body;

    /**
     * @var array Current parameters to use with the flush operation
     */
    protected $params;

    /**
     * @var ListObjectsIterator Iterator used with opendir() and subsequent readdir() calls
     */
    protected $objectIterator;

    /**
     * @var string The bucket that was opened when opendir() was called
     */
    protected $openedBucket;

    /**
     * @var string The prefix of the bucket that was opened with opendir()
     */
    protected $openedBucketPrefix;

    /**
     * @var array The next key to retrieve when using a directory iterator. Helps for fast directory traversal.
     */
    protected static $nextStat = array();

    /**
     * Register the 's3://' stream wrapper
     *
     * @param S3Client $client Client to use with the stream wrapper
     */
    public static function register(S3Client $client)
    {
        if (in_array('s3', stream_get_wrappers())) {
            stream_wrapper_unregister('s3');
        }

        stream_wrapper_register('s3', get_called_class(), STREAM_IS_URL);
        static::$client = $client;
    }

    /**
     * Close the stream
     */
    public function stream_close()
    {
        $this->body = null;
    }

    /**
     * @param string $path
     * @param string $mode
     * @param int    $options
     * @param string $opened_path
     *
     * @return bool
     */
    public function stream_open($path, $mode, $options, &$opened_path)
    {
        // We don't care about the binary flag
        $this->mode = $mode = rtrim($mode, 'bt');
        $this->params = $params = $this->getParams($path);
        $errors = array();

        if (!$params['Key']) {
            $errors[] = 'Cannot open a bucket. You must specify a path in the form of s3://bucket/key';
        }

        if (strpos($mode, '+')) {
            $errors[] = 'The Amazon S3 stream wrapper does not allow simultaneous reading and writing.';
        }

        if (!in_array($mode, array('r', 'w', 'a', 'x'))) {
            $errors[] = "Mode not supported: {$mode}. Use one 'r', 'w', 'a', or 'x'.";
        }

        // When using mode "x" validate if the file exists before attempting to read
        if ($mode == 'x' && static::$client->doesObjectExist($params['Bucket'], $params['Key'], $this->getOptions())) {
            $errors[] = "{$path} already exists on Amazon S3";
        }

        if (!$errors) {
            if ($mode == 'r') {
                return $this->openReadStream($params, $errors);
            } elseif ($mode == 'a') {
                return $this->openAppendStream($params, $errors);
            } else {
                return $this->openWriteStream($params, $errors);
            }
        }

        return $this->triggerError($errors);
    }

    /**
     * @return bool
     */
    public function stream_eof()
    {
        return $this->body->feof();
    }

    /**
     * @return bool
     */
    public function stream_flush()
    {
        if ($this->mode == 'r') {
            return false;
        }

        $this->body->rewind();
        $params = $this->params;
        $params['Body'] = $this->body;

        // Attempt to guess the ContentType of the upload based on the
        // file extension of the key
        if (!isset($params['ContentType']) &&
            ($type = Mimetypes::getInstance()->fromFilename($params['Key']))
        ) {
            $params['ContentType'] = $type;
        }

        try {
            static::$client->putObject($params);
            return true;
        } catch (\Exception $e) {
            return $this->triggerError($e->getMessage());
        }
    }

    /**
     * Read data from the underlying stream
     *
     * @param int $count Amount of bytes to read
     *
     * @return string
     */
    public function stream_read($count)
    {
        return $this->body->read($count);
    }

    /**
     * Seek to a specific byte in the stream
     *
     * @param int $offset Seek offset
     * @param int $whence Whence (SEEK_SET, SEEK_CUR, SEEK_END)
     *
     * @return bool
     */
    public function stream_seek($offset, $whence = SEEK_SET)
    {
        return $this->body->seek($offset, $whence);
    }

    /**
     * Get the current position of the stream
     *
     * @return int Returns the current position in the stream
     */
    public function stream_tell()
    {
        return $this->body->ftell();
    }

    /**
     * Write data the to the stream
     *
     * @param string $data
     *
     * @return int Returns the number of bytes written to the stream
     */
    public function stream_write($data)
    {
        return $this->body->write($data);
    }

    /**
     * Delete a specific object
     *
     * @param string $path
     * @return bool
     */
    public function unlink($path)
    {
        try {
            $this->clearStatInfo($path);
            static::$client->deleteObject($this->getParams($path));
            return true;
        } catch (\Exception $e) {
            return $this->triggerError($e->getMessage());
        }
    }

    /**
     * @return array
     */
    public function stream_stat()
    {
        $stat = fstat($this->body->getStream());
        // Add the size of the underlying stream if it is known
        if ($this->mode == 'r' && $this->body->getSize()) {
            $stat[7] = $stat['size'] = $this->body->getSize();
        }

        return $stat;
    }

    /**
     * Provides information for is_dir, is_file, filesize, etc. Works on buckets, keys, and prefixes
     *
     * @param string $path
     * @param int    $flags
     *
     * @return array Returns an array of stat data
     * @link http://www.php.net/manual/en/streamwrapper.url-stat.php
     */
    public function url_stat($path, $flags)
    {
        // Check if this path is in the url_stat cache
        if (isset(static::$nextStat[$path])) {
            return static::$nextStat[$path];
        }

        $parts = $this->getParams($path);

        if (!$parts['Key']) {
            // Stat "directories": buckets, or "s3://"
            if (!$parts['Bucket'] || static::$client->doesBucketExist($parts['Bucket'])) {
                return $this->formatUrlStat($path);
            } else {
                return $this->triggerError("File or directory not found: {$path}", $flags);
            }
        }

        try {
            try {
                $result = static::$client->headObject($parts)->toArray();
                if (substr($parts['Key'], -1, 1) == '/' && $result['ContentLength'] == 0) {
                    // Return as if it is a bucket to account for console bucket objects (e.g., zero-byte object "foo/")
                    return $this->formatUrlStat($path);
                } else {
                    // Attempt to stat and cache regular object
                    return $this->formatUrlStat($result);
                }
            } catch (NoSuchKeyException $e) {
                // Maybe this isn't an actual key, but a prefix. Do a prefix listing of objects to determine.
                $result = static::$client->listObjects(array(
                    'Bucket'  => $parts['Bucket'],
                    'Prefix'  => rtrim($parts['Key'], '/') . '/',
                    'MaxKeys' => 1
                ));
                if (!$result['Contents'] && !$result['CommonPrefixes']) {
                    return $this->triggerError("File or directory not found: {$path}", $flags);
                }
                // This is a directory prefix
                return $this->formatUrlStat($path);
            }
        } catch (\Exception $e) {
            return $this->triggerError($e->getMessage(), $flags);
        }
    }

    /**
     * Support for mkdir().
     *
     * @param string $path    Directory which should be created.
     * @param int    $mode    Permissions. 700-range permissions map to ACL_PUBLIC. 600-range permissions map to
     *                        ACL_AUTH_READ. All other permissions map to ACL_PRIVATE. Expects octal form.
     * @param int    $options A bitwise mask of values, such as STREAM_MKDIR_RECURSIVE.
     *
     * @return bool
     * @link http://www.php.net/manual/en/streamwrapper.mkdir.php
     */
    public function mkdir($path, $mode, $options)
    {
        $params = $this->getParams($path);
        if (!$params['Bucket']) {
            return false;
        }

        if (!isset($params['ACL'])) {
            $params['ACL'] = $this->determineAcl($mode);
        }

        return !isset($params['Key']) || $params['Key'] === '/'
            ? $this->createBucket($path, $params)
            : $this->createPseudoDirectory($path, $params);
    }

    /**
     * Remove a bucket from Amazon S3
     *
     * @param string $path the directory path
     * @param int    $options A bitwise mask of values
     *
     * @return bool true if directory was successfully removed
     * @link http://www.php.net/manual/en/streamwrapper.rmdir.php
     */
    public function rmdir($path, $options)
    {
        $params = $this->getParams($path);
        if (!$params['Bucket']) {
            return $this->triggerError('You cannot delete s3://. Please specify a bucket.');
        }

        try {

            if (!$params['Key']) {
                static::$client->deleteBucket(array('Bucket' => $params['Bucket']));
                $this->clearStatInfo($path);
                return true;
            }

            // Use a key that adds a trailing slash if needed.
            $prefix = rtrim($params['Key'], '/') . '/';

            $result = static::$client->listObjects(array(
                'Bucket'  => $params['Bucket'],
                'Prefix'  => $prefix,
                'MaxKeys' => 1
            ));

            // Check if the bucket contains keys other than the placeholder
            if ($result['Contents']) {
                foreach ($result['Contents'] as $key) {
                    if ($key['Key'] == $prefix) {
                        continue;
                    }
                    return $this->triggerError('Psuedo folder is not empty');
                }
                return $this->unlink(rtrim($path, '/') . '/');
            }

            return $result['CommonPrefixes']
                ? $this->triggerError('Pseudo folder contains nested folders')
                : true;

        } catch (\Exception $e) {
            return $this->triggerError($e->getMessage());
        }
    }

    /**
     * Support for opendir().
     *
     * The opendir() method of the Amazon S3 stream wrapper supports a stream
     * context option of "listFilter". listFilter must be a callable that
     * accepts an associative array of object data and returns true if the
     * object should be yielded when iterating the keys in a bucket.
     *
     * @param string $path    The path to the directory (e.g. "s3://dir[</prefix>]")
     * @param string $options Whether or not to enforce safe_mode (0x04). Unused.
     *
     * @return bool true on success
     * @see http://www.php.net/manual/en/function.opendir.php
     */
    public function dir_opendir($path, $options)
    {
        // Reset the cache
        $this->clearStatInfo();
        $params = $this->getParams($path);
        $delimiter = $this->getOption('delimiter');
        $filterFn = $this->getOption('listFilter');

        if ($delimiter === null) {
            $delimiter = '/';
        }

        if ($params['Key']) {
            $params['Key'] = rtrim($params['Key'], $delimiter) . $delimiter;
        }

        $this->openedBucket = $params['Bucket'];
        $this->openedBucketPrefix = $params['Key'];
        $operationParams = array('Bucket' => $params['Bucket'], 'Prefix' => $params['Key']);

        if ($delimiter) {
            $operationParams['Delimiter'] = $delimiter;
        }

        $objectIterator = static::$client->getIterator('ListObjects', $operationParams, array(
            'return_prefixes' => true,
            'sort_results'    => true
        ));

        // Filter our "/" keys added by the console as directories, and ensure
        // that if a filter function is provided that it passes the filter.
        $this->objectIterator = new FilterIterator(
            $objectIterator,
            function ($key) use ($filterFn) {
                // Each yielded results can contain a "Key" or "Prefix"
                return (!$filterFn || call_user_func($filterFn, $key)) &&
                    (!isset($key['Key']) || substr($key['Key'], -1, 1) !== '/');
            }
        );

        $this->objectIterator->next();

        return true;
    }

    /**
     * Close the directory listing handles
     *
     * @return bool true on success
     */
    public function dir_closedir()
    {
        $this->objectIterator = null;

        return true;
    }

    /**
     * This method is called in response to rewinddir()
     *
     * @return boolean true on success
     */
    public function dir_rewinddir()
    {
        $this->clearStatInfo();
        $this->objectIterator->rewind();

        return true;
    }

    /**
     * This method is called in response to readdir()
     *
     * @return string Should return a string representing the next filename, or false if there is no next file.
     *
     * @link http://www.php.net/manual/en/function.readdir.php
     */
    public function dir_readdir()
    {
        // Skip empty result keys
        if (!$this->objectIterator->valid()) {
            return false;
        }

        $current = $this->objectIterator->current();
        if (isset($current['Prefix'])) {
            // Include "directories". Be sure to strip a trailing "/"
            // on prefixes.
            $prefix = rtrim($current['Prefix'], '/');
            $result = str_replace($this->openedBucketPrefix, '', $prefix);
            $key = "s3://{$this->openedBucket}/{$prefix}";
            $stat = $this->formatUrlStat($prefix);
        } else {
            // Remove the prefix from the result to emulate other
            // stream wrappers.
            $result = str_replace($this->openedBucketPrefix, '', $current['Key']);
            $key = "s3://{$this->openedBucket}/{$current['Key']}";
            $stat = $this->formatUrlStat($current);
        }

        // Cache the object data for quick url_stat lookups used with
        // RecursiveDirectoryIterator.
        static::$nextStat = array($key => $stat);
        $this->objectIterator->next();

        return $result;
    }

    /**
     * Called in response to rename() to rename a file or directory. Currently only supports renaming objects.
     *
     * @param string $path_from the path to the file to rename
     * @param string $path_to   the new path to the file
     *
     * @return bool true if file was successfully renamed
     * @link http://www.php.net/manual/en/function.rename.php
     */
    public function rename($path_from, $path_to)
    {
        $partsFrom = $this->getParams($path_from);
        $partsTo = $this->getParams($path_to);
        $this->clearStatInfo($path_from);
        $this->clearStatInfo($path_to);

        if (!$partsFrom['Key'] || !$partsTo['Key']) {
            return $this->triggerError('The Amazon S3 stream wrapper only supports copying objects');
        }

        try {
            // Copy the object and allow overriding default parameters if desired, but by default copy metadata
            static::$client->copyObject($this->getOptions() + array(
                'Bucket' => $partsTo['Bucket'],
                'Key' => $partsTo['Key'],
                'CopySource' => '/' . $partsFrom['Bucket'] . '/' . rawurlencode($partsFrom['Key']),
                'MetadataDirective' => 'COPY'
            ));
            // Delete the original object
            static::$client->deleteObject(array(
                'Bucket' => $partsFrom['Bucket'],
                'Key'    => $partsFrom['Key']
            ) + $this->getOptions());
        } catch (\Exception $e) {
            return $this->triggerError($e->getMessage());
        }

        return true;
    }

    /**
     * Cast the stream to return the underlying file resource
     *
     * @param int $cast_as STREAM_CAST_FOR_SELECT or STREAM_CAST_AS_STREAM
     *
     * @return resource
     */
    public function stream_cast($cast_as)
    {
        return $this->body->getStream();
    }

    /**
     * Get the stream context options available to the current stream
     *
     * @return array
     */
    protected function getOptions()
    {
        $context = $this->context ?: stream_context_get_default();
        $options = stream_context_get_options($context);

        return isset($options['s3']) ? $options['s3'] : array();
    }

    /**
     * Get a specific stream context option
     *
     * @param string $name Name of the option to retrieve
     *
     * @return mixed|null
     */
    protected function getOption($name)
    {
        $options = $this->getOptions();

        return isset($options[$name]) ? $options[$name] : null;
    }

    /**
     * Get the bucket and key from the passed path (e.g. s3://bucket/key)
     *
     * @param string $path Path passed to the stream wrapper
     *
     * @return array Hash of 'Bucket', 'Key', and custom params
     */
    protected function getParams($path)
    {
        $parts = explode('/', substr($path, 5), 2);

        $params = $this->getOptions();
        unset($params['seekable']);

        return array(
            'Bucket' => $parts[0],
            'Key'    => isset($parts[1]) ? $parts[1] : null
        ) + $params;
    }

    /**
     * Serialize and sign a command, returning a request object
     *
     * @param CommandInterface $command Command to sign
     *
     * @return RequestInterface
     */
    protected function getSignedRequest($command)
    {
        $request = $command->prepare();
        $request->dispatch('request.before_send', array('request' => $request));

        return $request;
    }

    /**
     * Initialize the stream wrapper for a read only stream
     *
     * @param array $params Operation parameters
     * @param array $errors Any encountered errors to append to
     *
     * @return bool
     */
    protected function openReadStream(array $params, array &$errors)
    {
        // Create the command and serialize the request
        $request = $this->getSignedRequest(static::$client->getCommand('GetObject', $params));
        // Create a stream that uses the EntityBody object
        $factory = $this->getOption('stream_factory') ?: new PhpStreamRequestFactory();
        $this->body = $factory->fromRequest($request, array(), array('stream_class' => 'Guzzle\Http\EntityBody'));

        // Headers are placed in the "wrapper_data" array. The array of headers
        // is simply an array of header lines of which the first line is the
        // status line of the HTTP response.
        $headers = $this->body->getMetaData('wrapper_data');

        if ($headers && isset($headers[0])) {
            $statusParts = explode(' ', $headers[0]);
            $status = $statusParts[1];
            if ($status != 200) {
                return $this->triggerError('Cannot open file: ' . $this->body);
            }
        }

        // Wrap the body in a caching entity body if seeking is allowed
        if ($this->getOption('seekable')) {
            $this->body = new CachingEntityBody($this->body);
        }

        return true;
    }

    /**
     * Initialize the stream wrapper for a write only stream
     *
     * @param array $params Operation parameters
     * @param array $errors Any encountered errors to append to
     *
     * @return bool
     */
    protected function openWriteStream(array $params, array &$errors)
    {
        $this->body = new EntityBody(fopen('php://temp', 'r+'));

        return true;
    }

    /**
     * Initialize the stream wrapper for an append stream
     *
     * @param array $params Operation parameters
     * @param array $errors Any encountered errors to append to
     *
     * @return bool
     */
    protected function openAppendStream(array $params, array &$errors)
    {
        try {
            // Get the body of the object
            $this->body = static::$client->getObject($params)->get('Body');
            $this->body->seek(0, SEEK_END);
        } catch (S3Exception $e) {
            // The object does not exist, so use a simple write stream
            $this->openWriteStream($params, $errors);
        }

        return true;
    }

    /**
     * Trigger one or more errors
     *
     * @param string|array $errors Errors to trigger
     * @param mixed        $flags  If set to STREAM_URL_STAT_QUIET, then no error or exception occurs
     *
     * @return bool Returns false
     * @throws RuntimeException if throw_errors is true
     */
    protected function triggerError($errors, $flags = null)
    {
        if ($flags & STREAM_URL_STAT_QUIET) {
          // This is triggered with things like file_exists()

          if ($flags & STREAM_URL_STAT_LINK) {
            // This is triggered for things like is_link()
            return $this->formatUrlStat(false);
          }
          return false;
        }

        // This is triggered when doing things like lstat() or stat()
        trigger_error(implode("\n", array_map('esc_html', (array) $errors)), E_USER_WARNING);

        return false;
    }

    /**
     * Prepare a url_stat result array
     *
     * @param string|array $result Data to add
     *
     * @return array Returns the modified url_stat result
     */
    protected function formatUrlStat($result = null)
    {
        static $statTemplate = array(
            0  => 0,  'dev'     => 0,
            1  => 0,  'ino'     => 0,
            2  => 0,  'mode'    => 0,
            3  => 0,  'nlink'   => 0,
            4  => 0,  'uid'     => 0,
            5  => 0,  'gid'     => 0,
            6  => -1, 'rdev'    => -1,
            7  => 0,  'size'    => 0,
            8  => 0,  'atime'   => 0,
            9  => 0,  'mtime'   => 0,
            10 => 0,  'ctime'   => 0,
            11 => -1, 'blksize' => -1,
            12 => -1, 'blocks'  => -1,
        );

        $stat = $statTemplate;
        $type = gettype($result);

        // Determine what type of data is being cached
        if ($type == 'NULL' || $type == 'string') {
            // Directory with 0777 access - see "man 2 stat".
            $stat['mode'] = $stat[2] = 0040777;
        } elseif ($type == 'array' && isset($result['LastModified'])) {
            // ListObjects or HeadObject result
            $stat['mtime'] = $stat[9] = $stat['ctime'] = $stat[10] = strtotime($result['LastModified']);
            $stat['size'] = $stat[7] = (isset($result['ContentLength']) ? $result['ContentLength'] : $result['Size']);
            // Regular file with 0777 access - see "man 2 stat".
            $stat['mode'] = $stat[2] = 0100777;
        }

        return $stat;
    }

    /**
     * Clear the next stat result from the cache
     *
     * @param string $path If a path is specific, clearstatcache() will be called
     */
    protected function clearStatInfo($path = null)
    {
        static::$nextStat = array();
        if ($path) {
            clearstatcache(true, $path);
        }
    }

    /**
     * Creates a bucket for the given parameters.
     *
     * @param string $path   Stream wrapper path
     * @param array  $params A result of StreamWrapper::getParams()
     *
     * @return bool Returns true on success or false on failure
     */
    private function createBucket($path, array $params)
    {
        if (static::$client->doesBucketExist($params['Bucket'])) {
            return $this->triggerError("Directory already exists: {$path}");
        }

        try {
            static::$client->createBucket($params);
            $this->clearStatInfo($path);
            return true;
        } catch (\Exception $e) {
            return $this->triggerError($e->getMessage());
        }
    }

    /**
     * Creates a pseudo-folder by creating an empty "/" suffixed key
     *
     * @param string $path   Stream wrapper path
     * @param array  $params A result of StreamWrapper::getParams()
     *
     * @return bool
     */
    private function createPseudoDirectory($path, array $params)
    {
        // Ensure the path ends in "/" and the body is empty.
        $params['Key'] = rtrim($params['Key'], '/') . '/';
        $params['Body'] = '';

        // Fail if this pseudo directory key already exists
        if (static::$client->doesObjectExist($params['Bucket'], $params['Key'])) {
            return $this->triggerError("Directory already exists: {$path}");
        }

        try {
            static::$client->putObject($params);
            $this->clearStatInfo($path);
            return true;
        } catch (\Exception $e) {
            return $this->triggerError($e->getMessage());
        }
    }

    /**
     * Determine the most appropriate ACL based on a file mode.
     *
     * @param int $mode File mode
     *
     * @return string
     */
    private function determineAcl($mode)
    {
        $mode = decoct($mode);

        if ($mode >= 700 && $mode <= 799) {
            return 'public-read';
        }

        if ($mode >= 600 && $mode <= 699) {
            return 'authenticated-read';
        }

        return 'private';
    }
}
includes/lib/aws-sdk-php-2.8.31/src/Aws/S3/Iterator/OpendirIterator.php000064400000004066151327705670021252 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\S3\Iterator;

/**
 * Provides an iterator around an opendir resource. This is useful when you need to provide context to an opendir so
 * you can't use RecursiveDirectoryIterator
 */
class OpendirIterator implements \Iterator
{
    /** @var resource */
    protected $dirHandle;

    /** @var \SplFileInfo */
    protected $currentFile;

    /** @var int */
    protected $key = -1;

    /** @var string */
    protected $filePrefix;

    /**
     * @param resource $dirHandle  Opened directory handled returned from opendir
     * @param string   $filePrefix Prefix to add to each filename
     */
    public function __construct($dirHandle, $filePrefix = '')
    {
        $this->filePrefix = $filePrefix;
        $this->dirHandle = $dirHandle;
        $this->next();
    }

    public function __destruct()
    {
        if ($this->dirHandle) {
            closedir($this->dirHandle);
        }
    }

    public function rewind()
    {
        $this->key = 0;
        rewinddir($this->dirHandle);
    }

    public function current()
    {
        return $this->currentFile;
    }

    public function next()
    {
        if ($file = readdir($this->dirHandle)) {
            $this->currentFile = new \SplFileInfo($this->filePrefix . $file);
        } else {
            $this->currentFile = false;
        }

        $this->key++;
    }

    public function key()
    {
        return $this->key;
    }

    public function valid()
    {
        return $this->currentFile !== false;
    }
}
includes/lib/aws-sdk-php-2.8.31/src/Aws/S3/Iterator/ListBucketsIterator.php000064400000002534151327705670022104 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\S3\Iterator;

use Aws\Common\Iterator\AwsResourceIterator;
use Guzzle\Service\Resource\Model;

/**
 * Iterator for the S3 ListBuckets command
 *
 * This iterator includes the following additional options:
 *
 * - names_only: Set to true to receive only the object/prefix names
 */
class ListBucketsIterator extends AwsResourceIterator
{
    /**
     * {@inheritdoc}
     */
    protected function handleResults(Model $result)
    {
        // Get the results
        $buckets = $result->get('Buckets') ?: array();

        // If only the names_only set, change arrays to a string
        if ($this->get('names_only')) {
            foreach ($buckets as &$bucket) {
                $bucket = $bucket['Name'];
            }
        }

        return $buckets;
    }
}
includes/lib/aws-sdk-php-2.8.31/src/Aws/S3/Iterator/ListObjectsIterator.php000064400000005234151327705670022075 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\S3\Iterator;

use Aws\Common\Iterator\AwsResourceIterator;
use Guzzle\Service\Resource\Model;

/**
 * Iterator for an S3 ListObjects command
 *
 * This iterator includes the following additional options:
 *
 * - return_prefixes: Set to true to receive both prefixes and objects in results
 * - sort_results: Set to true to sort mixed (object/prefix) results
 * - names_only: Set to true to receive only the object/prefix names
 */
class ListObjectsIterator extends AwsResourceIterator
{
    protected function handleResults(Model $result)
    {
        // Get the list of objects and record the last key
        $objects = $result->get('Contents') ?: array();
        $numObjects = count($objects);
        $lastKey = $numObjects ? $objects[$numObjects - 1]['Key'] : false;
        if ($lastKey && !$result->hasKey($this->get('output_token'))) {
            $result->set($this->get('output_token'), $lastKey);
        }

        // Closure for getting the name of an object or prefix
        $getName = function ($object) {
            return isset($object['Key']) ? $object['Key'] : $object['Prefix'];
        };

        // If common prefixes returned (i.e. a delimiter was set) and they need to be returned, there is more to do
        if ($this->get('return_prefixes') && $result->hasKey('CommonPrefixes')) {
            // Collect and format the prefixes to include with the objects
            $objects = array_merge($objects, $result->get('CommonPrefixes'));

            // Sort the objects and prefixes to maintain alphabetical order, but only if some of each were returned
            if ($this->get('sort_results') && $lastKey && $objects) {
                usort($objects, function ($object1, $object2) use ($getName) {
                    return strcmp($getName($object1), $getName($object2));
                });
            }
        }

        // If only the names are desired, iterate through the results and convert the arrays to the object/prefix names
        if ($this->get('names_only')) {
            $objects = array_map($getName, $objects);
        }

        return $objects;
    }
}
includes/lib/aws-sdk-php-2.8.31/src/Aws/S3/Iterator/ListMultipartUploadsIterator.php000064400000002611151327705670024011 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\S3\Iterator;

use Guzzle\Service\Resource\Model;
use Aws\Common\Iterator\AwsResourceIterator;

/**
 * Iterator for the S3 ListMultipartUploads command
 *
 * This iterator includes the following additional options:
 *
 * - return_prefixes: Set to true to return both prefixes and uploads
 */
class ListMultipartUploadsIterator extends AwsResourceIterator
{
    /**
     * {@inheritdoc}
     */
    protected function handleResults(Model $result)
    {
        // Get the list of uploads
        $uploads = $result->get('Uploads') ?: array();

        // If there are prefixes and we want them, merge them in
        if ($this->get('return_prefixes') && $result->hasKey('CommonPrefixes')) {
            $uploads = array_merge($uploads, $result->get('CommonPrefixes'));
        }

        return $uploads;
    }
}
includes/lib/aws-sdk-php-2.8.31/src/Aws/S3/Iterator/ListObjectVersionsIterator.php000064400000003035151327705670023440 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\S3\Iterator;

use Aws\Common\Iterator\AwsResourceIterator;
use Guzzle\Service\Resource\Model;

/**
 * Iterator for an S3 ListObjectVersions command
 *
 * This iterator includes the following additional options:
 *
 * - return_prefixes: Set to true to receive both prefixes and versions in results
 */
class ListObjectVersionsIterator extends AwsResourceIterator
{
    /**
     * {@inheritdoc}
     */
    protected function handleResults(Model $result)
    {
        // Get the list of object versions
        $versions = $result->get('Versions') ?: array();
        $deleteMarkers = $result->get('DeleteMarkers') ?: array();
        $versions = array_merge($versions, $deleteMarkers);

        // If there are prefixes and we want them, merge them in
        if ($this->get('return_prefixes') && $result->hasKey('CommonPrefixes')) {
            $versions = array_merge($versions, $result->get('CommonPrefixes'));
        }

        return $versions;
    }
}
includes/lib/aws-sdk-php-2.8.31/src/Aws/S3/S3SignatureV4.php000064400000003463151327705670016730 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\S3;

use Aws\Common\Signature\SignatureV4;
use Aws\Common\Credentials\CredentialsInterface;
use Guzzle\Http\Message\EntityEnclosingRequestInterface;
use Guzzle\Http\Message\RequestInterface;

/**
 * Amazon S3 signature version 4 overrides.
 */
class S3SignatureV4 extends SignatureV4 implements S3SignatureInterface
{
    /**
     * Always add a x-amz-content-sha-256 for data integrity.
     */
    public function signRequest(RequestInterface $request, CredentialsInterface $credentials)
    {
        if (!$request->hasHeader('x-amz-content-sha256')) {
            $request->setHeader(
                'x-amz-content-sha256',
                $this->getPayload($request)
            );
        }

        parent::signRequest($request, $credentials);
    }

    /**
     * Override used to allow pre-signed URLs to be created for an
     * in-determinate request payload.
     */
    protected function getPresignedPayload(RequestInterface $request)
    {
        return 'UNSIGNED-PAYLOAD';
    }

    /**
     * Amazon S3 does not double-encode the path component in the canonical req
     */
    protected function createCanonicalizedPath(RequestInterface $request)
    {
        return '/' . ltrim($request->getPath(), '/');
    }
}
includes/lib/aws-sdk-php-2.8.31/src/Aws/S3/ResumableDownload.php000064400000014131151327705670017750 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\S3;

use Aws\Common\Exception\RuntimeException;
use Aws\Common\Exception\UnexpectedValueException;
use Guzzle\Http\EntityBody;
use Guzzle\Http\ReadLimitEntityBody;
use Guzzle\Http\EntityBodyInterface;
use Guzzle\Service\Resource\Model;

/**
 * Allows you to resume the download of a partially downloaded object.
 *
 * Downloads objects from Amazon S3 in using "Range" downloads. This allows a partially downloaded object to be resumed
 * so that only the remaining portion of the object is downloaded.
 */
class ResumableDownload
{
    /** @var S3Client The S3 client to use to download objects and issue HEAD requests */
    protected $client;

    /** @var Model Model object returned when the initial HeadObject operation was called */
    protected $meta;

    /** @var array Array of parameters to pass to a GetObject operation */
    protected $params;

    /** @var EntityBody Where the object will be downloaded */
    protected $target;

    /**
     * @param S3Client                            $client Client to use when executing requests
     * @param string                              $bucket Bucket that holds the object
     * @param string                              $key    Key of the object
     * @param string|resource|EntityBodyInterface $target Where the object should be downloaded to. Pass a string to
     *                                                    save the object to a file, pass a resource returned by
     *                                                    fopen() to save the object to a stream resource, or pass a
     *                                                    Guzzle EntityBody object to save the contents to an
     *                                                    EntityBody.
     * @param array                               $params Any additional GetObject or HeadObject parameters to use
     *                                                    with each command issued by the client. (e.g. pass "Version"
     *                                                    to download a specific version of an object)
     * @throws RuntimeException if the target variable points to a file that cannot be opened
     */
    public function __construct(S3Client $client, $bucket, $key, $target, array $params = array())
    {
        $this->params = $params;
        $this->client = $client;
        $this->params['Bucket'] = $bucket;
        $this->params['Key'] = $key;

        // If a string is passed, then assume that the download should stream to a file on disk
        if (is_string($target)) {
            if (!($target = fopen($target, 'a+'))) {
                throw new RuntimeException(esc_html("Unable to open {$target} for writing"));
            }
            // Always append to the file
            fseek($target, 0, SEEK_END);
        }

        // Get the metadata and Content-MD5 of the object
        $this->target = EntityBody::factory($target);
    }

    /**
     * Get the bucket of the download
     *
     * @return string
     */
    public function getBucket()
    {
        return $this->params['Bucket'];
    }

    /**
     * Get the key of the download
     *
     * @return string
     */
    public function getKey()
    {
        return $this->params['Key'];
    }

    /**
     * Get the file to which the contents are downloaded
     *
     * @return string
     */
    public function getFilename()
    {
        return $this->target->getUri();
    }

    /**
     * Download the remainder of the object from Amazon S3
     *
     * Performs a message integrity check if possible
     *
     * @return Model
     */
    public function __invoke()
    {
        $command = $this->client->getCommand('HeadObject', $this->params);
        $this->meta = $command->execute();

        if ($this->target->ftell() >= $this->meta['ContentLength']) {
            return false;
        }

        $this->meta['ContentMD5'] = (string) $command->getResponse()->getHeader('Content-MD5');

        // Use a ReadLimitEntityBody so that rewinding the stream after an error does not cause the file pointer
        // to enter an inconsistent state with the data being downloaded
        $this->params['SaveAs'] = new ReadLimitEntityBody(
            $this->target,
            $this->meta['ContentLength'],
            $this->target->ftell()
        );

        $result = $this->getRemaining();
        $this->checkIntegrity();

        return $result;
    }

    /**
     * Send the command to get the remainder of the object
     *
     * @return Model
     */
    protected function getRemaining()
    {
        $current = $this->target->ftell();
        $targetByte = $this->meta['ContentLength'] - 1;
        $this->params['Range'] = "bytes={$current}-{$targetByte}";

        // Set the starting offset so that the body is never seeked to before this point in the event of a retry
        $this->params['SaveAs']->setOffset($current);
        $command = $this->client->getCommand('GetObject', $this->params);

        return $command->execute();
    }

    /**
     * Performs an MD5 message integrity check if possible
     *
     * @throws UnexpectedValueException if the message does not validate
     */
    protected function checkIntegrity()
    {
        if ($this->target->isReadable() && $expected = $this->meta['ContentMD5']) {
            $actual = $this->target->getContentMd5();
            if ($actual != $expected) {
                throw new UnexpectedValueException(
                    esc_html("Message integrity check failed. Expected {$expected} but got {$actual}.")
                );
            }
        }
    }
}
includes/lib/aws-sdk-php-2.8.31/src/Aws/S3/Enum/CannedAcl.php000064400000001772151327705670017064 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\S3\Enum;

use Aws\Common\Enum;

/**
 * Contains enumerable CannedAcl values
 */
class CannedAcl extends Enum
{
    const PRIVATE_ACCESS = 'private';
    const PUBLIC_READ = 'public-read';
    const PUBLIC_READ_WRITE = 'public-read-write';
    const AUTHENTICATED_READ = 'authenticated-read';
    const BUCKET_OWNER_READ = 'bucket-owner-read';
    const BUCKET_OWNER_FULL_CONTROL = 'bucket-owner-full-control';
}
includes/lib/aws-sdk-php-2.8.31/src/Aws/S3/Enum/Storage.php000064400000001501151327705670016646 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\S3\Enum;

use Aws\Common\Enum;

/**
 * Contains enumerable Amazon S3 storage options
 */
class Storage extends Enum
{
    const STANDARD = 'STANDARD';
    const REDUCED  = 'REDUCED_REDUNDANCY';
    const GLACIER = 'GLACIER';
}
includes/lib/aws-sdk-php-2.8.31/src/Aws/S3/Enum/GranteeType.php000064400000001473151327705670017501 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\S3\Enum;

use Aws\Common\Enum;

/**
 * Contains enumerable GranteeType values
 */
class GranteeType extends Enum
{
    const USER = 'CanonicalUser';
    const EMAIL = 'AmazonCustomerByEmail';
    const GROUP = 'Group';
}
includes/lib/aws-sdk-php-2.8.31/src/Aws/S3/Enum/MFADelete.php000064400000001417151327705670016776 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\S3\Enum;

use Aws\Common\Enum;

/**
 * Contains enumerable MFADelete values
 */
class MFADelete extends Enum
{
    const ENABLED = 'Enabled';
    const DISABLED = 'Disabled';
}
includes/lib/aws-sdk-php-2.8.31/src/Aws/S3/Enum/Status.php000064400000001413151327705670016527 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\S3\Enum;

use Aws\Common\Enum;

/**
 * Contains enumerable Status values
 */
class Status extends Enum
{
    const ENABLED = 'Enabled';
    const SUSPENDED = 'Suspended';
}
includes/lib/aws-sdk-php-2.8.31/src/Aws/S3/Enum/ServerSideEncryption.php000064400000001402151327705670021370 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\S3\Enum;

use Aws\Common\Enum;

/**
 * Contains enumerable ServerSideEncryption values
 */
class ServerSideEncryption extends Enum
{
    const AES256 = 'AES256';
}
includes/lib/aws-sdk-php-2.8.31/src/Aws/S3/Enum/EncodingType.php000064400000001354151327705670017640 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\S3\Enum;

use Aws\Common\Enum;

/**
 * Contains enumerable EncodingType values
 */
class EncodingType extends Enum
{
    const URL = 'url';
}
includes/lib/aws-sdk-php-2.8.31/src/Aws/S3/Enum/Permission.php000064400000001562151327705670017401 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\S3\Enum;

use Aws\Common\Enum;

/**
 * Contains enumerable Permission values
 */
class Permission extends Enum
{
    const FULL_CONTROL = 'FULL_CONTROL';
    const WRITE = 'WRITE';
    const WRITE_ACP = 'WRITE_ACP';
    const READ = 'READ';
    const READ_ACP = 'READ_ACP';
}
includes/lib/aws-sdk-php-2.8.31/src/Aws/S3/Enum/MetadataDirective.php000064400000001427151327705670020630 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\S3\Enum;

use Aws\Common\Enum;

/**
 * Contains enumerable MetadataDirective values
 */
class MetadataDirective extends Enum
{
    const COPY = 'COPY';
    const REPLACE = 'REPLACE';
}
includes/lib/aws-sdk-php-2.8.31/src/Aws/S3/Enum/Protocol.php000064400000001401151327705670017042 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\S3\Enum;

use Aws\Common\Enum;

/**
 * Contains enumerable Protocol values
 */
class Protocol extends Enum
{
    const HTTP = 'http';
    const HTTPS = 'https';
}
includes/lib/aws-sdk-php-2.8.31/src/Aws/S3/Enum/Group.php000064400000001745151327705670016350 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\S3\Enum;

use Aws\Common\Enum;

/**
 * Contains enumerable Amazon S3 group options for ACL grantees
 */
class Group extends Enum
{
    const AUTHENTICATED_USERS = 'http://acs.amazonaws.com/groups/global/AuthenticatedUsers';
    const ALL_USERS           = 'http://acs.amazonaws.com/groups/global/AllUsers';
    const LOG_DELIVERY        = 'http://acs.amazonaws.com/groups/s3/LogDelivery';
}
includes/lib/aws-sdk-php-2.8.31/src/Aws/S3/Enum/Payer.php000064400000001422151327705670016324 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\S3\Enum;

use Aws\Common\Enum;

/**
 * Contains enumerable Payer values
 */
class Payer extends Enum
{
    const REQUESTER = 'Requester';
    const BUCKET_OWNER = 'BucketOwner';
}
includes/lib/aws-sdk-php-2.8.31/src/Aws/S3/Enum/StorageClass.php000064400000001453151327705670017642 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\S3\Enum;

use Aws\Common\Enum;

/**
 * Contains enumerable StorageClass values
 */
class StorageClass extends Enum
{
    const STANDARD = 'STANDARD';
    const REDUCED_REDUNDANCY = 'REDUCED_REDUNDANCY';
}
includes/lib/aws-sdk-php-2.8.31/src/Aws/S3/Enum/Event.php000064400000001424151327705670016327 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\S3\Enum;

use Aws\Common\Enum;

/**
 * Contains enumerable Event values
 */
class Event extends Enum
{
    const REDUCED_REDUNDANCY_LOST_OBJECT = 's3:ReducedRedundancyLostObject';
}
includes/lib/aws-sdk-php-2.8.31/src/Aws/S3/BucketStyleListener.php000064400000006261151327705670020312 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\S3;

use Guzzle\Common\Event;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;

/**
 * Listener used to change the way in which buckets are referenced (path/virtual style) based on context
 */
class BucketStyleListener implements EventSubscriberInterface
{
    private static $exclusions = array('GetBucketLocation' => true);

    public static function getSubscribedEvents()
    {
        return array('command.after_prepare' => array('onCommandAfterPrepare', -255));
    }

    /**
     * Changes how buckets are referenced in the HTTP request
     *
     * @param Event $event Event emitted
     */
    public function onCommandAfterPrepare(Event $event)
    {
        $command = $event['command'];
        $bucket = $command['Bucket'];
        $request = $command->getRequest();
        $pathStyle = false;

        // Skip operations that do not need the bucket moved to the host.
        if (isset(self::$exclusions[$command->getName()])) {
            return;
        }

        if ($key = $command['Key']) {
            // Modify the command Key to account for the {/Key*} explosion into an array
            if (is_array($key)) {
                $command['Key'] = $key = implode('/', $key);
            }
        }

        // Set the key and bucket on the request
        $request->getParams()->set('bucket', $bucket)->set('key', $key);

        // Switch to virtual if PathStyle is disabled, or not a DNS compatible bucket name, or the scheme is
        // http, or the scheme is https and there are no dots in the host header (avoids SSL issues)
        if (!$command['PathStyle'] && $command->getClient()->isValidBucketName($bucket)
            && !($command->getRequest()->getScheme() == 'https' && strpos($bucket, '.'))
        ) {
            // Switch to virtual hosted bucket
            $request->setHost($bucket . '.' . $request->getHost());
            $request->setPath(preg_replace("#^/{$bucket}#", '', $request->getPath()));
        } else {
            $pathStyle = true;
        }

        if (!$bucket) {
            $request->getParams()->set('s3.resource', '/');
        } elseif ($pathStyle) {
            // Path style does not need a trailing slash
            $request->getParams()->set(
                's3.resource',
                '/' . rawurlencode($bucket) . ($key ? ('/' . S3Client::encodeKey($key)) : '')
            );
        } else {
            // Bucket style needs a trailing slash
            $request->getParams()->set(
                's3.resource',
                '/' . rawurlencode($bucket) . ($key ? ('/' . S3Client::encodeKey($key)) : '/')
            );
        }
    }
}
includes/lib/aws-sdk-php-2.8.31/src/Aws/Iam/IamClient.php000064400000037604151327705670016441 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\Iam;

use Aws\Common\Client\AbstractClient;
use Aws\Common\Client\ClientBuilder;
use Aws\Common\Enum\ClientOptions as Options;
use Guzzle\Common\Collection;
use Guzzle\Service\Resource\Model;
use Guzzle\Service\Resource\ResourceIteratorInterface;

/**
 * Client to interact with AWS Identity and Access Management
 *
 * @method Model addClientIDToOpenIDConnectProvider(array $args = array()) {@command Iam AddClientIDToOpenIDConnectProvider}
 * @method Model addRoleToInstanceProfile(array $args = array()) {@command Iam AddRoleToInstanceProfile}
 * @method Model addUserToGroup(array $args = array()) {@command Iam AddUserToGroup}
 * @method Model attachGroupPolicy(array $args = array()) {@command Iam AttachGroupPolicy}
 * @method Model attachRolePolicy(array $args = array()) {@command Iam AttachRolePolicy}
 * @method Model attachUserPolicy(array $args = array()) {@command Iam AttachUserPolicy}
 * @method Model changePassword(array $args = array()) {@command Iam ChangePassword}
 * @method Model createAccessKey(array $args = array()) {@command Iam CreateAccessKey}
 * @method Model createAccountAlias(array $args = array()) {@command Iam CreateAccountAlias}
 * @method Model createGroup(array $args = array()) {@command Iam CreateGroup}
 * @method Model createInstanceProfile(array $args = array()) {@command Iam CreateInstanceProfile}
 * @method Model createLoginProfile(array $args = array()) {@command Iam CreateLoginProfile}
 * @method Model createOpenIDConnectProvider(array $args = array()) {@command Iam CreateOpenIDConnectProvider}
 * @method Model createPolicy(array $args = array()) {@command Iam CreatePolicy}
 * @method Model createPolicyVersion(array $args = array()) {@command Iam CreatePolicyVersion}
 * @method Model createRole(array $args = array()) {@command Iam CreateRole}
 * @method Model createSAMLProvider(array $args = array()) {@command Iam CreateSAMLProvider}
 * @method Model createUser(array $args = array()) {@command Iam CreateUser}
 * @method Model createVirtualMFADevice(array $args = array()) {@command Iam CreateVirtualMFADevice}
 * @method Model deactivateMFADevice(array $args = array()) {@command Iam DeactivateMFADevice}
 * @method Model deleteAccessKey(array $args = array()) {@command Iam DeleteAccessKey}
 * @method Model deleteAccountAlias(array $args = array()) {@command Iam DeleteAccountAlias}
 * @method Model deleteAccountPasswordPolicy(array $args = array()) {@command Iam DeleteAccountPasswordPolicy}
 * @method Model deleteGroup(array $args = array()) {@command Iam DeleteGroup}
 * @method Model deleteGroupPolicy(array $args = array()) {@command Iam DeleteGroupPolicy}
 * @method Model deleteInstanceProfile(array $args = array()) {@command Iam DeleteInstanceProfile}
 * @method Model deleteLoginProfile(array $args = array()) {@command Iam DeleteLoginProfile}
 * @method Model deleteOpenIDConnectProvider(array $args = array()) {@command Iam DeleteOpenIDConnectProvider}
 * @method Model deletePolicy(array $args = array()) {@command Iam DeletePolicy}
 * @method Model deletePolicyVersion(array $args = array()) {@command Iam DeletePolicyVersion}
 * @method Model deleteRole(array $args = array()) {@command Iam DeleteRole}
 * @method Model deleteRolePolicy(array $args = array()) {@command Iam DeleteRolePolicy}
 * @method Model deleteSAMLProvider(array $args = array()) {@command Iam DeleteSAMLProvider}
 * @method Model deleteSSHPublicKey(array $args = array()) {@command Iam DeleteSSHPublicKey}
 * @method Model deleteServerCertificate(array $args = array()) {@command Iam DeleteServerCertificate}
 * @method Model deleteSigningCertificate(array $args = array()) {@command Iam DeleteSigningCertificate}
 * @method Model deleteUser(array $args = array()) {@command Iam DeleteUser}
 * @method Model deleteUserPolicy(array $args = array()) {@command Iam DeleteUserPolicy}
 * @method Model deleteVirtualMFADevice(array $args = array()) {@command Iam DeleteVirtualMFADevice}
 * @method Model detachGroupPolicy(array $args = array()) {@command Iam DetachGroupPolicy}
 * @method Model detachRolePolicy(array $args = array()) {@command Iam DetachRolePolicy}
 * @method Model detachUserPolicy(array $args = array()) {@command Iam DetachUserPolicy}
 * @method Model enableMFADevice(array $args = array()) {@command Iam EnableMFADevice}
 * @method Model generateCredentialReport(array $args = array()) {@command Iam GenerateCredentialReport}
 * @method Model getAccessKeyLastUsed(array $args = array()) {@command Iam GetAccessKeyLastUsed}
 * @method Model getAccountAuthorizationDetails(array $args = array()) {@command Iam GetAccountAuthorizationDetails}
 * @method Model getAccountPasswordPolicy(array $args = array()) {@command Iam GetAccountPasswordPolicy}
 * @method Model getAccountSummary(array $args = array()) {@command Iam GetAccountSummary}
 * @method Model getContextKeysForCustomPolicy(array $args = array()) {@command Iam GetContextKeysForCustomPolicy}
 * @method Model getContextKeysForPrincipalPolicy(array $args = array()) {@command Iam GetContextKeysForPrincipalPolicy}
 * @method Model getCredentialReport(array $args = array()) {@command Iam GetCredentialReport}
 * @method Model getGroup(array $args = array()) {@command Iam GetGroup}
 * @method Model getGroupPolicy(array $args = array()) {@command Iam GetGroupPolicy}
 * @method Model getInstanceProfile(array $args = array()) {@command Iam GetInstanceProfile}
 * @method Model getLoginProfile(array $args = array()) {@command Iam GetLoginProfile}
 * @method Model getOpenIDConnectProvider(array $args = array()) {@command Iam GetOpenIDConnectProvider}
 * @method Model getPolicy(array $args = array()) {@command Iam GetPolicy}
 * @method Model getPolicyVersion(array $args = array()) {@command Iam GetPolicyVersion}
 * @method Model getRole(array $args = array()) {@command Iam GetRole}
 * @method Model getRolePolicy(array $args = array()) {@command Iam GetRolePolicy}
 * @method Model getSAMLProvider(array $args = array()) {@command Iam GetSAMLProvider}
 * @method Model getSSHPublicKey(array $args = array()) {@command Iam GetSSHPublicKey}
 * @method Model getServerCertificate(array $args = array()) {@command Iam GetServerCertificate}
 * @method Model getUser(array $args = array()) {@command Iam GetUser}
 * @method Model getUserPolicy(array $args = array()) {@command Iam GetUserPolicy}
 * @method Model listAccessKeys(array $args = array()) {@command Iam ListAccessKeys}
 * @method Model listAccountAliases(array $args = array()) {@command Iam ListAccountAliases}
 * @method Model listAttachedGroupPolicies(array $args = array()) {@command Iam ListAttachedGroupPolicies}
 * @method Model listAttachedRolePolicies(array $args = array()) {@command Iam ListAttachedRolePolicies}
 * @method Model listAttachedUserPolicies(array $args = array()) {@command Iam ListAttachedUserPolicies}
 * @method Model listEntitiesForPolicy(array $args = array()) {@command Iam ListEntitiesForPolicy}
 * @method Model listGroupPolicies(array $args = array()) {@command Iam ListGroupPolicies}
 * @method Model listGroups(array $args = array()) {@command Iam ListGroups}
 * @method Model listGroupsForUser(array $args = array()) {@command Iam ListGroupsForUser}
 * @method Model listInstanceProfiles(array $args = array()) {@command Iam ListInstanceProfiles}
 * @method Model listInstanceProfilesForRole(array $args = array()) {@command Iam ListInstanceProfilesForRole}
 * @method Model listMFADevices(array $args = array()) {@command Iam ListMFADevices}
 * @method Model listOpenIDConnectProviders(array $args = array()) {@command Iam ListOpenIDConnectProviders}
 * @method Model listPolicies(array $args = array()) {@command Iam ListPolicies}
 * @method Model listPolicyVersions(array $args = array()) {@command Iam ListPolicyVersions}
 * @method Model listRolePolicies(array $args = array()) {@command Iam ListRolePolicies}
 * @method Model listRoles(array $args = array()) {@command Iam ListRoles}
 * @method Model listSAMLProviders(array $args = array()) {@command Iam ListSAMLProviders}
 * @method Model listSSHPublicKeys(array $args = array()) {@command Iam ListSSHPublicKeys}
 * @method Model listServerCertificates(array $args = array()) {@command Iam ListServerCertificates}
 * @method Model listSigningCertificates(array $args = array()) {@command Iam ListSigningCertificates}
 * @method Model listUserPolicies(array $args = array()) {@command Iam ListUserPolicies}
 * @method Model listUsers(array $args = array()) {@command Iam ListUsers}
 * @method Model listVirtualMFADevices(array $args = array()) {@command Iam ListVirtualMFADevices}
 * @method Model putGroupPolicy(array $args = array()) {@command Iam PutGroupPolicy}
 * @method Model putRolePolicy(array $args = array()) {@command Iam PutRolePolicy}
 * @method Model putUserPolicy(array $args = array()) {@command Iam PutUserPolicy}
 * @method Model removeClientIDFromOpenIDConnectProvider(array $args = array()) {@command Iam RemoveClientIDFromOpenIDConnectProvider}
 * @method Model removeRoleFromInstanceProfile(array $args = array()) {@command Iam RemoveRoleFromInstanceProfile}
 * @method Model removeUserFromGroup(array $args = array()) {@command Iam RemoveUserFromGroup}
 * @method Model resyncMFADevice(array $args = array()) {@command Iam ResyncMFADevice}
 * @method Model setDefaultPolicyVersion(array $args = array()) {@command Iam SetDefaultPolicyVersion}
 * @method Model simulateCustomPolicy(array $args = array()) {@command Iam SimulateCustomPolicy}
 * @method Model simulatePrincipalPolicy(array $args = array()) {@command Iam SimulatePrincipalPolicy}
 * @method Model updateAccessKey(array $args = array()) {@command Iam UpdateAccessKey}
 * @method Model updateAccountPasswordPolicy(array $args = array()) {@command Iam UpdateAccountPasswordPolicy}
 * @method Model updateAssumeRolePolicy(array $args = array()) {@command Iam UpdateAssumeRolePolicy}
 * @method Model updateGroup(array $args = array()) {@command Iam UpdateGroup}
 * @method Model updateLoginProfile(array $args = array()) {@command Iam UpdateLoginProfile}
 * @method Model updateOpenIDConnectProviderThumbprint(array $args = array()) {@command Iam UpdateOpenIDConnectProviderThumbprint}
 * @method Model updateSAMLProvider(array $args = array()) {@command Iam UpdateSAMLProvider}
 * @method Model updateSSHPublicKey(array $args = array()) {@command Iam UpdateSSHPublicKey}
 * @method Model updateServerCertificate(array $args = array()) {@command Iam UpdateServerCertificate}
 * @method Model updateSigningCertificate(array $args = array()) {@command Iam UpdateSigningCertificate}
 * @method Model updateUser(array $args = array()) {@command Iam UpdateUser}
 * @method Model uploadSSHPublicKey(array $args = array()) {@command Iam UploadSSHPublicKey}
 * @method Model uploadServerCertificate(array $args = array()) {@command Iam UploadServerCertificate}
 * @method Model uploadSigningCertificate(array $args = array()) {@command Iam UploadSigningCertificate}
 * @method ResourceIteratorInterface getGetGroupIterator(array $args = array()) The input array uses the parameters of the GetGroup operation
 * @method ResourceIteratorInterface getListAccessKeysIterator(array $args = array()) The input array uses the parameters of the ListAccessKeys operation
 * @method ResourceIteratorInterface getListAccountAliasesIterator(array $args = array()) The input array uses the parameters of the ListAccountAliases operation
 * @method ResourceIteratorInterface getListAttachedGroupPoliciesIterator(array $args = array()) The input array uses the parameters of the ListAttachedGroupPolicies operation
 * @method ResourceIteratorInterface getListAttachedRolePoliciesIterator(array $args = array()) The input array uses the parameters of the ListAttachedRolePolicies operation
 * @method ResourceIteratorInterface getListAttachedUserPoliciesIterator(array $args = array()) The input array uses the parameters of the ListAttachedUserPolicies operation
 * @method ResourceIteratorInterface getListEntitiesForPolicyIterator(array $args = array()) The input array uses the parameters of the ListEntitiesForPolicy operation
 * @method ResourceIteratorInterface getListGroupPoliciesIterator(array $args = array()) The input array uses the parameters of the ListGroupPolicies operation
 * @method ResourceIteratorInterface getListGroupsIterator(array $args = array()) The input array uses the parameters of the ListGroups operation
 * @method ResourceIteratorInterface getListGroupsForUserIterator(array $args = array()) The input array uses the parameters of the ListGroupsForUser operation
 * @method ResourceIteratorInterface getListInstanceProfilesIterator(array $args = array()) The input array uses the parameters of the ListInstanceProfiles operation
 * @method ResourceIteratorInterface getListInstanceProfilesForRoleIterator(array $args = array()) The input array uses the parameters of the ListInstanceProfilesForRole operation
 * @method ResourceIteratorInterface getListMFADevicesIterator(array $args = array()) The input array uses the parameters of the ListMFADevices operation
 * @method ResourceIteratorInterface getListPoliciesIterator(array $args = array()) The input array uses the parameters of the ListPolicies operation
 * @method ResourceIteratorInterface getListRolePoliciesIterator(array $args = array()) The input array uses the parameters of the ListRolePolicies operation
 * @method ResourceIteratorInterface getListRolesIterator(array $args = array()) The input array uses the parameters of the ListRoles operation
 * @method ResourceIteratorInterface getListSAMLProvidersIterator(array $args = array()) The input array uses the parameters of the ListSAMLProviders operation
 * @method ResourceIteratorInterface getListServerCertificatesIterator(array $args = array()) The input array uses the parameters of the ListServerCertificates operation
 * @method ResourceIteratorInterface getListSigningCertificatesIterator(array $args = array()) The input array uses the parameters of the ListSigningCertificates operation
 * @method ResourceIteratorInterface getListUserPoliciesIterator(array $args = array()) The input array uses the parameters of the ListUserPolicies operation
 * @method ResourceIteratorInterface getListUsersIterator(array $args = array()) The input array uses the parameters of the ListUsers operation
 * @method ResourceIteratorInterface getListVirtualMFADevicesIterator(array $args = array()) The input array uses the parameters of the ListVirtualMFADevices operation
 * @method ResourceIteratorInterface getGetAccountAuthorizationDetailsIterator(array $args = array()) The input array uses the parameters of the GetAccountAuthorizationDetails operation
 *
 * @link http://docs.aws.amazon.com/aws-sdk-php/v2/guide/service-iam.html User guide
 * @link http://docs.aws.amazon.com/aws-sdk-php/v2/api/class-Aws.Iam.IamClient.html API docs
 */
class IamClient extends AbstractClient
{
    const LATEST_API_VERSION = '2010-05-08';

    /**
     * Factory method to create a new AWS Identity and Access Management client using an array of configuration options.
     *
     * @param array|Collection $config Client configuration data
     *
     * @return self
     * @link http://docs.aws.amazon.com/aws-sdk-php/v2/guide/configuration.html#client-configuration-options
     */
    public static function factory($config = array())
    {
        return ClientBuilder::factory(__NAMESPACE__)
            ->setConfig($config)
            ->setConfigDefaults(array(
                Options::VERSION             => self::LATEST_API_VERSION,
                Options::SERVICE_DESCRIPTION => __DIR__ . '/Resources/iam-%s.php'
            ))
            ->build();
    }
}
includes/lib/aws-sdk-php-2.8.31/src/Aws/Iam/Enum/AssignmentStatusType.php000064400000001503151327705670021643 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\Iam\Enum;

use Aws\Common\Enum;

/**
 * Contains enumerable AssignmentStatusType values
 */
class AssignmentStatusType extends Enum
{
    const ASSIGNED = 'Assigned';
    const UNASSIGNED = 'Unassigned';
    const ANY = 'Any';
}
includes/lib/aws-sdk-php-2.8.31/src/Aws/Iam/Enum/StatusType.php000064400000001420151327705670017610 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\Iam\Enum;

use Aws\Common\Enum;

/**
 * Contains enumerable StatusType values
 */
class StatusType extends Enum
{
    const ACTIVE = 'Active';
    const INACTIVE = 'Inactive';
}
includes/lib/aws-sdk-php-2.8.31/src/Aws/Iam/Resources/iam-2010-05-08.php000064400001234744151327705670020350 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

return array (
    'apiVersion' => '2010-05-08',
    'endpointPrefix' => 'iam',
    'serviceFullName' => 'AWS Identity and Access Management',
    'serviceAbbreviation' => 'IAM',
    'serviceType' => 'query',
    'globalEndpoint' => 'iam.amazonaws.com',
    'resultWrapped' => true,
    'signatureVersion' => 'v4',
    'namespace' => 'Iam',
    'regions' => array(
        'us-east-1' => array(
            'http' => false,
            'https' => true,
            'hostname' => 'iam.amazonaws.com',
        ),
        'us-west-1' => array(
            'http' => false,
            'https' => true,
            'hostname' => 'iam.amazonaws.com',
        ),
        'us-west-2' => array(
            'http' => false,
            'https' => true,
            'hostname' => 'iam.amazonaws.com',
        ),
        'eu-west-1' => array(
            'http' => false,
            'https' => true,
            'hostname' => 'iam.amazonaws.com',
        ),
        'ap-northeast-1' => array(
            'http' => false,
            'https' => true,
            'hostname' => 'iam.amazonaws.com',
        ),
        'ap-southeast-1' => array(
            'http' => false,
            'https' => true,
            'hostname' => 'iam.amazonaws.com',
        ),
        'ap-southeast-2' => array(
            'http' => false,
            'https' => true,
            'hostname' => 'iam.amazonaws.com',
        ),
        'sa-east-1' => array(
            'http' => false,
            'https' => true,
            'hostname' => 'iam.amazonaws.com',
        ),
        'cn-north-1' => array(
            'http' => false,
            'https' => true,
            'hostname' => 'iam.cn-north-1.amazonaws.com.cn',
        ),
        'us-gov-west-1' => array(
            'http' => false,
            'https' => true,
            'hostname' => 'iam.us-gov.amazonaws.com',
        ),
    ),
    'operations' => array(
        'AddClientIDToOpenIDConnectProvider' => array(
            'httpMethod' => 'POST',
            'uri' => '/',
            'class' => 'Aws\\Common\\Command\\QueryCommand',
            'responseClass' => 'EmptyOutput',
            'responseType' => 'model',
            'parameters' => array(
                'Action' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => 'AddClientIDToOpenIDConnectProvider',
                ),
                'Version' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => '2010-05-08',
                ),
                'OpenIDConnectProviderArn' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 20,
                ),
                'ClientID' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 1,
                ),
            ),
            'errorResponses' => array(
                array(
                    'reason' => 'The request was rejected because an invalid or out-of-range value was supplied for an input parameter.',
                    'class' => 'InvalidInputException',
                ),
                array(
                    'reason' => 'The request was rejected because it referenced an entity that does not exist. The error message describes the entity.',
                    'class' => 'NoSuchEntityException',
                ),
                array(
                    'reason' => 'The request was rejected because it attempted to create resources beyond the current AWS account limits. The error message describes the limit exceeded.',
                    'class' => 'LimitExceededException',
                ),
                array(
                    'reason' => 'The request processing has failed because of an unknown error, exception or failure.',
                    'class' => 'ServiceFailureException',
                ),
            ),
        ),
        'AddRoleToInstanceProfile' => array(
            'httpMethod' => 'POST',
            'uri' => '/',
            'class' => 'Aws\\Common\\Command\\QueryCommand',
            'responseClass' => 'EmptyOutput',
            'responseType' => 'model',
            'parameters' => array(
                'Action' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => 'AddRoleToInstanceProfile',
                ),
                'Version' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => '2010-05-08',
                ),
                'InstanceProfileName' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 1,
                ),
                'RoleName' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 1,
                ),
            ),
            'errorResponses' => array(
                array(
                    'reason' => 'The request was rejected because it referenced an entity that does not exist. The error message describes the entity.',
                    'class' => 'NoSuchEntityException',
                ),
                array(
                    'reason' => 'The request was rejected because it attempted to create a resource that already exists.',
                    'class' => 'EntityAlreadyExistsException',
                ),
                array(
                    'reason' => 'The request was rejected because it attempted to create resources beyond the current AWS account limits. The error message describes the limit exceeded.',
                    'class' => 'LimitExceededException',
                ),
                array(
                    'reason' => 'The request processing has failed because of an unknown error, exception or failure.',
                    'class' => 'ServiceFailureException',
                ),
            ),
        ),
        'AddUserToGroup' => array(
            'httpMethod' => 'POST',
            'uri' => '/',
            'class' => 'Aws\\Common\\Command\\QueryCommand',
            'responseClass' => 'EmptyOutput',
            'responseType' => 'model',
            'parameters' => array(
                'Action' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => 'AddUserToGroup',
                ),
                'Version' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => '2010-05-08',
                ),
                'GroupName' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 1,
                ),
                'UserName' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 1,
                ),
            ),
            'errorResponses' => array(
                array(
                    'reason' => 'The request was rejected because it referenced an entity that does not exist. The error message describes the entity.',
                    'class' => 'NoSuchEntityException',
                ),
                array(
                    'reason' => 'The request was rejected because it attempted to create resources beyond the current AWS account limits. The error message describes the limit exceeded.',
                    'class' => 'LimitExceededException',
                ),
                array(
                    'reason' => 'The request processing has failed because of an unknown error, exception or failure.',
                    'class' => 'ServiceFailureException',
                ),
            ),
        ),
        'AttachGroupPolicy' => array(
            'httpMethod' => 'POST',
            'uri' => '/',
            'class' => 'Aws\\Common\\Command\\QueryCommand',
            'responseClass' => 'EmptyOutput',
            'responseType' => 'model',
            'parameters' => array(
                'Action' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => 'AttachGroupPolicy',
                ),
                'Version' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => '2010-05-08',
                ),
                'GroupName' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 1,
                ),
                'PolicyArn' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 20,
                ),
            ),
            'errorResponses' => array(
                array(
                    'reason' => 'The request was rejected because it referenced an entity that does not exist. The error message describes the entity.',
                    'class' => 'NoSuchEntityException',
                ),
                array(
                    'reason' => 'The request was rejected because it attempted to create resources beyond the current AWS account limits. The error message describes the limit exceeded.',
                    'class' => 'LimitExceededException',
                ),
                array(
                    'reason' => 'The request was rejected because an invalid or out-of-range value was supplied for an input parameter.',
                    'class' => 'InvalidInputException',
                ),
                array(
                    'reason' => 'The request processing has failed because of an unknown error, exception or failure.',
                    'class' => 'ServiceFailureException',
                ),
            ),
        ),
        'AttachRolePolicy' => array(
            'httpMethod' => 'POST',
            'uri' => '/',
            'class' => 'Aws\\Common\\Command\\QueryCommand',
            'responseClass' => 'EmptyOutput',
            'responseType' => 'model',
            'parameters' => array(
                'Action' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => 'AttachRolePolicy',
                ),
                'Version' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => '2010-05-08',
                ),
                'RoleName' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 1,
                ),
                'PolicyArn' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 20,
                ),
            ),
            'errorResponses' => array(
                array(
                    'reason' => 'The request was rejected because it referenced an entity that does not exist. The error message describes the entity.',
                    'class' => 'NoSuchEntityException',
                ),
                array(
                    'reason' => 'The request was rejected because it attempted to create resources beyond the current AWS account limits. The error message describes the limit exceeded.',
                    'class' => 'LimitExceededException',
                ),
                array(
                    'reason' => 'The request was rejected because an invalid or out-of-range value was supplied for an input parameter.',
                    'class' => 'InvalidInputException',
                ),
                array(
                    'reason' => 'The request processing has failed because of an unknown error, exception or failure.',
                    'class' => 'ServiceFailureException',
                ),
            ),
        ),
        'AttachUserPolicy' => array(
            'httpMethod' => 'POST',
            'uri' => '/',
            'class' => 'Aws\\Common\\Command\\QueryCommand',
            'responseClass' => 'EmptyOutput',
            'responseType' => 'model',
            'parameters' => array(
                'Action' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => 'AttachUserPolicy',
                ),
                'Version' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => '2010-05-08',
                ),
                'UserName' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 1,
                ),
                'PolicyArn' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 20,
                ),
            ),
            'errorResponses' => array(
                array(
                    'reason' => 'The request was rejected because it referenced an entity that does not exist. The error message describes the entity.',
                    'class' => 'NoSuchEntityException',
                ),
                array(
                    'reason' => 'The request was rejected because it attempted to create resources beyond the current AWS account limits. The error message describes the limit exceeded.',
                    'class' => 'LimitExceededException',
                ),
                array(
                    'reason' => 'The request was rejected because an invalid or out-of-range value was supplied for an input parameter.',
                    'class' => 'InvalidInputException',
                ),
                array(
                    'reason' => 'The request processing has failed because of an unknown error, exception or failure.',
                    'class' => 'ServiceFailureException',
                ),
            ),
        ),
        'ChangePassword' => array(
            'httpMethod' => 'POST',
            'uri' => '/',
            'class' => 'Aws\\Common\\Command\\QueryCommand',
            'responseClass' => 'EmptyOutput',
            'responseType' => 'model',
            'parameters' => array(
                'Action' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => 'ChangePassword',
                ),
                'Version' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => '2010-05-08',
                ),
                'OldPassword' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 1,
                ),
                'NewPassword' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 1,
                ),
            ),
            'errorResponses' => array(
                array(
                    'reason' => 'The request was rejected because it referenced an entity that does not exist. The error message describes the entity.',
                    'class' => 'NoSuchEntityException',
                ),
                array(
                    'reason' => 'The request was rejected because the type of user for the transaction was incorrect.',
                    'class' => 'InvalidUserTypeException',
                ),
                array(
                    'reason' => 'The request was rejected because it attempted to create resources beyond the current AWS account limits. The error message describes the limit exceeded.',
                    'class' => 'LimitExceededException',
                ),
                array(
                    'reason' => 'The request was rejected because it referenced an entity that is temporarily unmodifiable, such as a user name that was deleted and then recreated. The error indicates that the request is likely to succeed if you try again after waiting several minutes. The error message describes the entity.',
                    'class' => 'EntityTemporarilyUnmodifiableException',
                ),
                array(
                    'reason' => 'The request was rejected because the provided password did not meet the requirements imposed by the account password policy.',
                    'class' => 'PasswordPolicyViolationException',
                ),
                array(
                    'reason' => 'The request processing has failed because of an unknown error, exception or failure.',
                    'class' => 'ServiceFailureException',
                ),
            ),
        ),
        'CreateAccessKey' => array(
            'httpMethod' => 'POST',
            'uri' => '/',
            'class' => 'Aws\\Common\\Command\\QueryCommand',
            'responseClass' => 'CreateAccessKeyResponse',
            'responseType' => 'model',
            'parameters' => array(
                'Action' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => 'CreateAccessKey',
                ),
                'Version' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => '2010-05-08',
                ),
                'UserName' => array(
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 1,
                ),
            ),
            'errorResponses' => array(
                array(
                    'reason' => 'The request was rejected because it referenced an entity that does not exist. The error message describes the entity.',
                    'class' => 'NoSuchEntityException',
                ),
                array(
                    'reason' => 'The request was rejected because it attempted to create resources beyond the current AWS account limits. The error message describes the limit exceeded.',
                    'class' => 'LimitExceededException',
                ),
                array(
                    'reason' => 'The request processing has failed because of an unknown error, exception or failure.',
                    'class' => 'ServiceFailureException',
                ),
            ),
        ),
        'CreateAccountAlias' => array(
            'httpMethod' => 'POST',
            'uri' => '/',
            'class' => 'Aws\\Common\\Command\\QueryCommand',
            'responseClass' => 'EmptyOutput',
            'responseType' => 'model',
            'parameters' => array(
                'Action' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => 'CreateAccountAlias',
                ),
                'Version' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => '2010-05-08',
                ),
                'AccountAlias' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 3,
                ),
            ),
            'errorResponses' => array(
                array(
                    'reason' => 'The request was rejected because it attempted to create a resource that already exists.',
                    'class' => 'EntityAlreadyExistsException',
                ),
                array(
                    'reason' => 'The request was rejected because it attempted to create resources beyond the current AWS account limits. The error message describes the limit exceeded.',
                    'class' => 'LimitExceededException',
                ),
                array(
                    'reason' => 'The request processing has failed because of an unknown error, exception or failure.',
                    'class' => 'ServiceFailureException',
                ),
            ),
        ),
        'CreateGroup' => array(
            'httpMethod' => 'POST',
            'uri' => '/',
            'class' => 'Aws\\Common\\Command\\QueryCommand',
            'responseClass' => 'CreateGroupResponse',
            'responseType' => 'model',
            'parameters' => array(
                'Action' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => 'CreateGroup',
                ),
                'Version' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => '2010-05-08',
                ),
                'Path' => array(
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 1,
                ),
                'GroupName' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 1,
                ),
            ),
            'errorResponses' => array(
                array(
                    'reason' => 'The request was rejected because it attempted to create resources beyond the current AWS account limits. The error message describes the limit exceeded.',
                    'class' => 'LimitExceededException',
                ),
                array(
                    'reason' => 'The request was rejected because it attempted to create a resource that already exists.',
                    'class' => 'EntityAlreadyExistsException',
                ),
                array(
                    'reason' => 'The request was rejected because it referenced an entity that does not exist. The error message describes the entity.',
                    'class' => 'NoSuchEntityException',
                ),
                array(
                    'reason' => 'The request processing has failed because of an unknown error, exception or failure.',
                    'class' => 'ServiceFailureException',
                ),
            ),
        ),
        'CreateInstanceProfile' => array(
            'httpMethod' => 'POST',
            'uri' => '/',
            'class' => 'Aws\\Common\\Command\\QueryCommand',
            'responseClass' => 'CreateInstanceProfileResponse',
            'responseType' => 'model',
            'parameters' => array(
                'Action' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => 'CreateInstanceProfile',
                ),
                'Version' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => '2010-05-08',
                ),
                'InstanceProfileName' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 1,
                ),
                'Path' => array(
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 1,
                ),
            ),
            'errorResponses' => array(
                array(
                    'reason' => 'The request was rejected because it attempted to create a resource that already exists.',
                    'class' => 'EntityAlreadyExistsException',
                ),
                array(
                    'reason' => 'The request was rejected because it attempted to create resources beyond the current AWS account limits. The error message describes the limit exceeded.',
                    'class' => 'LimitExceededException',
                ),
                array(
                    'reason' => 'The request processing has failed because of an unknown error, exception or failure.',
                    'class' => 'ServiceFailureException',
                ),
            ),
        ),
        'CreateLoginProfile' => array(
            'httpMethod' => 'POST',
            'uri' => '/',
            'class' => 'Aws\\Common\\Command\\QueryCommand',
            'responseClass' => 'CreateLoginProfileResponse',
            'responseType' => 'model',
            'parameters' => array(
                'Action' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => 'CreateLoginProfile',
                ),
                'Version' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => '2010-05-08',
                ),
                'UserName' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 1,
                ),
                'Password' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 1,
                ),
                'PasswordResetRequired' => array(
                    'type' => 'boolean',
                    'format' => 'boolean-string',
                    'location' => 'aws.query',
                ),
            ),
            'errorResponses' => array(
                array(
                    'reason' => 'The request was rejected because it attempted to create a resource that already exists.',
                    'class' => 'EntityAlreadyExistsException',
                ),
                array(
                    'reason' => 'The request was rejected because it referenced an entity that does not exist. The error message describes the entity.',
                    'class' => 'NoSuchEntityException',
                ),
                array(
                    'reason' => 'The request was rejected because the provided password did not meet the requirements imposed by the account password policy.',
                    'class' => 'PasswordPolicyViolationException',
                ),
                array(
                    'reason' => 'The request was rejected because it attempted to create resources beyond the current AWS account limits. The error message describes the limit exceeded.',
                    'class' => 'LimitExceededException',
                ),
                array(
                    'reason' => 'The request processing has failed because of an unknown error, exception or failure.',
                    'class' => 'ServiceFailureException',
                ),
            ),
        ),
        'CreateOpenIDConnectProvider' => array(
            'httpMethod' => 'POST',
            'uri' => '/',
            'class' => 'Aws\\Common\\Command\\QueryCommand',
            'responseClass' => 'CreateOpenIDConnectProviderResponse',
            'responseType' => 'model',
            'parameters' => array(
                'Action' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => 'CreateOpenIDConnectProvider',
                ),
                'Version' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => '2010-05-08',
                ),
                'Url' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 1,
                ),
                'ClientIDList' => array(
                    'type' => 'array',
                    'location' => 'aws.query',
                    'sentAs' => 'ClientIDList.member',
                    'items' => array(
                        'name' => 'clientIDType',
                        'type' => 'string',
                        'minLength' => 1,
                    ),
                ),
                'ThumbprintList' => array(
                    'required' => true,
                    'type' => 'array',
                    'location' => 'aws.query',
                    'sentAs' => 'ThumbprintList.member',
                    'items' => array(
                        'name' => 'thumbprintType',
                        'type' => 'string',
                        'minLength' => 40,
                    ),
                ),
            ),
            'errorResponses' => array(
                array(
                    'reason' => 'The request was rejected because an invalid or out-of-range value was supplied for an input parameter.',
                    'class' => 'InvalidInputException',
                ),
                array(
                    'reason' => 'The request was rejected because it attempted to create a resource that already exists.',
                    'class' => 'EntityAlreadyExistsException',
                ),
                array(
                    'reason' => 'The request was rejected because it attempted to create resources beyond the current AWS account limits. The error message describes the limit exceeded.',
                    'class' => 'LimitExceededException',
                ),
                array(
                    'reason' => 'The request processing has failed because of an unknown error, exception or failure.',
                    'class' => 'ServiceFailureException',
                ),
            ),
        ),
        'CreatePolicy' => array(
            'httpMethod' => 'POST',
            'uri' => '/',
            'class' => 'Aws\\Common\\Command\\QueryCommand',
            'responseClass' => 'CreatePolicyResponse',
            'responseType' => 'model',
            'parameters' => array(
                'Action' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => 'CreatePolicy',
                ),
                'Version' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => '2010-05-08',
                ),
                'PolicyName' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 1,
                ),
                'Path' => array(
                    'type' => 'string',
                    'location' => 'aws.query',
                ),
                'PolicyDocument' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 1,
                ),
                'Description' => array(
                    'type' => 'string',
                    'location' => 'aws.query',
                ),
            ),
            'errorResponses' => array(
                array(
                    'reason' => 'The request was rejected because an invalid or out-of-range value was supplied for an input parameter.',
                    'class' => 'InvalidInputException',
                ),
                array(
                    'reason' => 'The request was rejected because it attempted to create resources beyond the current AWS account limits. The error message describes the limit exceeded.',
                    'class' => 'LimitExceededException',
                ),
                array(
                    'reason' => 'The request was rejected because it attempted to create a resource that already exists.',
                    'class' => 'EntityAlreadyExistsException',
                ),
                array(
                    'reason' => 'The request was rejected because the policy document was malformed. The error message describes the specific error.',
                    'class' => 'MalformedPolicyDocumentException',
                ),
                array(
                    'reason' => 'The request processing has failed because of an unknown error, exception or failure.',
                    'class' => 'ServiceFailureException',
                ),
            ),
        ),
        'CreatePolicyVersion' => array(
            'httpMethod' => 'POST',
            'uri' => '/',
            'class' => 'Aws\\Common\\Command\\QueryCommand',
            'responseClass' => 'CreatePolicyVersionResponse',
            'responseType' => 'model',
            'parameters' => array(
                'Action' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => 'CreatePolicyVersion',
                ),
                'Version' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => '2010-05-08',
                ),
                'PolicyArn' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 20,
                ),
                'PolicyDocument' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 1,
                ),
                'SetAsDefault' => array(
                    'type' => 'boolean',
                    'format' => 'boolean-string',
                    'location' => 'aws.query',
                ),
            ),
            'errorResponses' => array(
                array(
                    'reason' => 'The request was rejected because it referenced an entity that does not exist. The error message describes the entity.',
                    'class' => 'NoSuchEntityException',
                ),
                array(
                    'reason' => 'The request was rejected because the policy document was malformed. The error message describes the specific error.',
                    'class' => 'MalformedPolicyDocumentException',
                ),
                array(
                    'reason' => 'The request was rejected because an invalid or out-of-range value was supplied for an input parameter.',
                    'class' => 'InvalidInputException',
                ),
                array(
                    'reason' => 'The request was rejected because it attempted to create resources beyond the current AWS account limits. The error message describes the limit exceeded.',
                    'class' => 'LimitExceededException',
                ),
                array(
                    'reason' => 'The request processing has failed because of an unknown error, exception or failure.',
                    'class' => 'ServiceFailureException',
                ),
            ),
        ),
        'CreateRole' => array(
            'httpMethod' => 'POST',
            'uri' => '/',
            'class' => 'Aws\\Common\\Command\\QueryCommand',
            'responseClass' => 'CreateRoleResponse',
            'responseType' => 'model',
            'parameters' => array(
                'Action' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => 'CreateRole',
                ),
                'Version' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => '2010-05-08',
                ),
                'Path' => array(
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 1,
                ),
                'RoleName' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 1,
                ),
                'AssumeRolePolicyDocument' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 1,
                ),
            ),
            'errorResponses' => array(
                array(
                    'reason' => 'The request was rejected because it attempted to create resources beyond the current AWS account limits. The error message describes the limit exceeded.',
                    'class' => 'LimitExceededException',
                ),
                array(
                    'reason' => 'The request was rejected because it attempted to create a resource that already exists.',
                    'class' => 'EntityAlreadyExistsException',
                ),
                array(
                    'reason' => 'The request was rejected because the policy document was malformed. The error message describes the specific error.',
                    'class' => 'MalformedPolicyDocumentException',
                ),
                array(
                    'reason' => 'The request processing has failed because of an unknown error, exception or failure.',
                    'class' => 'ServiceFailureException',
                ),
            ),
        ),
        'CreateSAMLProvider' => array(
            'httpMethod' => 'POST',
            'uri' => '/',
            'class' => 'Aws\\Common\\Command\\QueryCommand',
            'responseClass' => 'CreateSAMLProviderResponse',
            'responseType' => 'model',
            'parameters' => array(
                'Action' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => 'CreateSAMLProvider',
                ),
                'Version' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => '2010-05-08',
                ),
                'SAMLMetadataDocument' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 1000,
                ),
                'Name' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 1,
                ),
            ),
            'errorResponses' => array(
                array(
                    'reason' => 'The request was rejected because an invalid or out-of-range value was supplied for an input parameter.',
                    'class' => 'InvalidInputException',
                ),
                array(
                    'reason' => 'The request was rejected because it attempted to create a resource that already exists.',
                    'class' => 'EntityAlreadyExistsException',
                ),
                array(
                    'reason' => 'The request was rejected because it attempted to create resources beyond the current AWS account limits. The error message describes the limit exceeded.',
                    'class' => 'LimitExceededException',
                ),
                array(
                    'reason' => 'The request processing has failed because of an unknown error, exception or failure.',
                    'class' => 'ServiceFailureException',
                ),
            ),
        ),
        'CreateUser' => array(
            'httpMethod' => 'POST',
            'uri' => '/',
            'class' => 'Aws\\Common\\Command\\QueryCommand',
            'responseClass' => 'CreateUserResponse',
            'responseType' => 'model',
            'parameters' => array(
                'Action' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => 'CreateUser',
                ),
                'Version' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => '2010-05-08',
                ),
                'Path' => array(
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 1,
                ),
                'UserName' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 1,
                ),
            ),
            'errorResponses' => array(
                array(
                    'reason' => 'The request was rejected because it attempted to create resources beyond the current AWS account limits. The error message describes the limit exceeded.',
                    'class' => 'LimitExceededException',
                ),
                array(
                    'reason' => 'The request was rejected because it attempted to create a resource that already exists.',
                    'class' => 'EntityAlreadyExistsException',
                ),
                array(
                    'reason' => 'The request was rejected because it referenced an entity that does not exist. The error message describes the entity.',
                    'class' => 'NoSuchEntityException',
                ),
                array(
                    'reason' => 'The request processing has failed because of an unknown error, exception or failure.',
                    'class' => 'ServiceFailureException',
                ),
            ),
        ),
        'CreateVirtualMFADevice' => array(
            'httpMethod' => 'POST',
            'uri' => '/',
            'class' => 'Aws\\Common\\Command\\QueryCommand',
            'responseClass' => 'CreateVirtualMFADeviceResponse',
            'responseType' => 'model',
            'parameters' => array(
                'Action' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => 'CreateVirtualMFADevice',
                ),
                'Version' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => '2010-05-08',
                ),
                'Path' => array(
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 1,
                ),
                'VirtualMFADeviceName' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 1,
                ),
            ),
            'errorResponses' => array(
                array(
                    'reason' => 'The request was rejected because it attempted to create resources beyond the current AWS account limits. The error message describes the limit exceeded.',
                    'class' => 'LimitExceededException',
                ),
                array(
                    'reason' => 'The request was rejected because it attempted to create a resource that already exists.',
                    'class' => 'EntityAlreadyExistsException',
                ),
                array(
                    'reason' => 'The request processing has failed because of an unknown error, exception or failure.',
                    'class' => 'ServiceFailureException',
                ),
            ),
        ),
        'DeactivateMFADevice' => array(
            'httpMethod' => 'POST',
            'uri' => '/',
            'class' => 'Aws\\Common\\Command\\QueryCommand',
            'responseClass' => 'EmptyOutput',
            'responseType' => 'model',
            'parameters' => array(
                'Action' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => 'DeactivateMFADevice',
                ),
                'Version' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => '2010-05-08',
                ),
                'UserName' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 1,
                ),
                'SerialNumber' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 9,
                ),
            ),
            'errorResponses' => array(
                array(
                    'reason' => 'The request was rejected because it referenced an entity that is temporarily unmodifiable, such as a user name that was deleted and then recreated. The error indicates that the request is likely to succeed if you try again after waiting several minutes. The error message describes the entity.',
                    'class' => 'EntityTemporarilyUnmodifiableException',
                ),
                array(
                    'reason' => 'The request was rejected because it referenced an entity that does not exist. The error message describes the entity.',
                    'class' => 'NoSuchEntityException',
                ),
                array(
                    'reason' => 'The request was rejected because it attempted to create resources beyond the current AWS account limits. The error message describes the limit exceeded.',
                    'class' => 'LimitExceededException',
                ),
                array(
                    'reason' => 'The request processing has failed because of an unknown error, exception or failure.',
                    'class' => 'ServiceFailureException',
                ),
            ),
        ),
        'DeleteAccessKey' => array(
            'httpMethod' => 'POST',
            'uri' => '/',
            'class' => 'Aws\\Common\\Command\\QueryCommand',
            'responseClass' => 'EmptyOutput',
            'responseType' => 'model',
            'parameters' => array(
                'Action' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => 'DeleteAccessKey',
                ),
                'Version' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => '2010-05-08',
                ),
                'UserName' => array(
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 1,
                ),
                'AccessKeyId' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 16,
                ),
            ),
            'errorResponses' => array(
                array(
                    'reason' => 'The request was rejected because it referenced an entity that does not exist. The error message describes the entity.',
                    'class' => 'NoSuchEntityException',
                ),
                array(
                    'reason' => 'The request was rejected because it attempted to create resources beyond the current AWS account limits. The error message describes the limit exceeded.',
                    'class' => 'LimitExceededException',
                ),
                array(
                    'reason' => 'The request processing has failed because of an unknown error, exception or failure.',
                    'class' => 'ServiceFailureException',
                ),
            ),
        ),
        'DeleteAccountAlias' => array(
            'httpMethod' => 'POST',
            'uri' => '/',
            'class' => 'Aws\\Common\\Command\\QueryCommand',
            'responseClass' => 'EmptyOutput',
            'responseType' => 'model',
            'parameters' => array(
                'Action' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => 'DeleteAccountAlias',
                ),
                'Version' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => '2010-05-08',
                ),
                'AccountAlias' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 3,
                ),
            ),
            'errorResponses' => array(
                array(
                    'reason' => 'The request was rejected because it referenced an entity that does not exist. The error message describes the entity.',
                    'class' => 'NoSuchEntityException',
                ),
                array(
                    'reason' => 'The request was rejected because it attempted to create resources beyond the current AWS account limits. The error message describes the limit exceeded.',
                    'class' => 'LimitExceededException',
                ),
                array(
                    'reason' => 'The request processing has failed because of an unknown error, exception or failure.',
                    'class' => 'ServiceFailureException',
                ),
            ),
        ),
        'DeleteAccountPasswordPolicy' => array(
            'httpMethod' => 'POST',
            'uri' => '/',
            'class' => 'Aws\\Common\\Command\\QueryCommand',
            'responseClass' => 'EmptyOutput',
            'responseType' => 'model',
            'parameters' => array(
                'Action' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => 'DeleteAccountPasswordPolicy',
                ),
                'Version' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => '2010-05-08',
                ),
            ),
            'errorResponses' => array(
                array(
                    'reason' => 'The request was rejected because it referenced an entity that does not exist. The error message describes the entity.',
                    'class' => 'NoSuchEntityException',
                ),
                array(
                    'reason' => 'The request was rejected because it attempted to create resources beyond the current AWS account limits. The error message describes the limit exceeded.',
                    'class' => 'LimitExceededException',
                ),
                array(
                    'reason' => 'The request processing has failed because of an unknown error, exception or failure.',
                    'class' => 'ServiceFailureException',
                ),
            ),
        ),
        'DeleteGroup' => array(
            'httpMethod' => 'POST',
            'uri' => '/',
            'class' => 'Aws\\Common\\Command\\QueryCommand',
            'responseClass' => 'EmptyOutput',
            'responseType' => 'model',
            'parameters' => array(
                'Action' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => 'DeleteGroup',
                ),
                'Version' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => '2010-05-08',
                ),
                'GroupName' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 1,
                ),
            ),
            'errorResponses' => array(
                array(
                    'reason' => 'The request was rejected because it referenced an entity that does not exist. The error message describes the entity.',
                    'class' => 'NoSuchEntityException',
                ),
                array(
                    'reason' => 'The request was rejected because it attempted to delete a resource that has attached subordinate entities. The error message describes these entities.',
                    'class' => 'DeleteConflictException',
                ),
                array(
                    'reason' => 'The request was rejected because it attempted to create resources beyond the current AWS account limits. The error message describes the limit exceeded.',
                    'class' => 'LimitExceededException',
                ),
                array(
                    'reason' => 'The request processing has failed because of an unknown error, exception or failure.',
                    'class' => 'ServiceFailureException',
                ),
            ),
        ),
        'DeleteGroupPolicy' => array(
            'httpMethod' => 'POST',
            'uri' => '/',
            'class' => 'Aws\\Common\\Command\\QueryCommand',
            'responseClass' => 'EmptyOutput',
            'responseType' => 'model',
            'parameters' => array(
                'Action' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => 'DeleteGroupPolicy',
                ),
                'Version' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => '2010-05-08',
                ),
                'GroupName' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 1,
                ),
                'PolicyName' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 1,
                ),
            ),
            'errorResponses' => array(
                array(
                    'reason' => 'The request was rejected because it referenced an entity that does not exist. The error message describes the entity.',
                    'class' => 'NoSuchEntityException',
                ),
                array(
                    'reason' => 'The request was rejected because it attempted to create resources beyond the current AWS account limits. The error message describes the limit exceeded.',
                    'class' => 'LimitExceededException',
                ),
                array(
                    'reason' => 'The request processing has failed because of an unknown error, exception or failure.',
                    'class' => 'ServiceFailureException',
                ),
            ),
        ),
        'DeleteInstanceProfile' => array(
            'httpMethod' => 'POST',
            'uri' => '/',
            'class' => 'Aws\\Common\\Command\\QueryCommand',
            'responseClass' => 'EmptyOutput',
            'responseType' => 'model',
            'parameters' => array(
                'Action' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => 'DeleteInstanceProfile',
                ),
                'Version' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => '2010-05-08',
                ),
                'InstanceProfileName' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 1,
                ),
            ),
            'errorResponses' => array(
                array(
                    'reason' => 'The request was rejected because it referenced an entity that does not exist. The error message describes the entity.',
                    'class' => 'NoSuchEntityException',
                ),
                array(
                    'reason' => 'The request was rejected because it attempted to delete a resource that has attached subordinate entities. The error message describes these entities.',
                    'class' => 'DeleteConflictException',
                ),
                array(
                    'reason' => 'The request was rejected because it attempted to create resources beyond the current AWS account limits. The error message describes the limit exceeded.',
                    'class' => 'LimitExceededException',
                ),
                array(
                    'reason' => 'The request processing has failed because of an unknown error, exception or failure.',
                    'class' => 'ServiceFailureException',
                ),
            ),
        ),
        'DeleteLoginProfile' => array(
            'httpMethod' => 'POST',
            'uri' => '/',
            'class' => 'Aws\\Common\\Command\\QueryCommand',
            'responseClass' => 'EmptyOutput',
            'responseType' => 'model',
            'parameters' => array(
                'Action' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => 'DeleteLoginProfile',
                ),
                'Version' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => '2010-05-08',
                ),
                'UserName' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 1,
                ),
            ),
            'errorResponses' => array(
                array(
                    'reason' => 'The request was rejected because it referenced an entity that is temporarily unmodifiable, such as a user name that was deleted and then recreated. The error indicates that the request is likely to succeed if you try again after waiting several minutes. The error message describes the entity.',
                    'class' => 'EntityTemporarilyUnmodifiableException',
                ),
                array(
                    'reason' => 'The request was rejected because it referenced an entity that does not exist. The error message describes the entity.',
                    'class' => 'NoSuchEntityException',
                ),
                array(
                    'reason' => 'The request was rejected because it attempted to create resources beyond the current AWS account limits. The error message describes the limit exceeded.',
                    'class' => 'LimitExceededException',
                ),
                array(
                    'reason' => 'The request processing has failed because of an unknown error, exception or failure.',
                    'class' => 'ServiceFailureException',
                ),
            ),
        ),
        'DeleteOpenIDConnectProvider' => array(
            'httpMethod' => 'POST',
            'uri' => '/',
            'class' => 'Aws\\Common\\Command\\QueryCommand',
            'responseClass' => 'EmptyOutput',
            'responseType' => 'model',
            'parameters' => array(
                'Action' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => 'DeleteOpenIDConnectProvider',
                ),
                'Version' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => '2010-05-08',
                ),
                'OpenIDConnectProviderArn' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 20,
                ),
            ),
            'errorResponses' => array(
                array(
                    'reason' => 'The request was rejected because an invalid or out-of-range value was supplied for an input parameter.',
                    'class' => 'InvalidInputException',
                ),
                array(
                    'reason' => 'The request was rejected because it referenced an entity that does not exist. The error message describes the entity.',
                    'class' => 'NoSuchEntityException',
                ),
                array(
                    'reason' => 'The request processing has failed because of an unknown error, exception or failure.',
                    'class' => 'ServiceFailureException',
                ),
            ),
        ),
        'DeletePolicy' => array(
            'httpMethod' => 'POST',
            'uri' => '/',
            'class' => 'Aws\\Common\\Command\\QueryCommand',
            'responseClass' => 'EmptyOutput',
            'responseType' => 'model',
            'parameters' => array(
                'Action' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => 'DeletePolicy',
                ),
                'Version' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => '2010-05-08',
                ),
                'PolicyArn' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 20,
                ),
            ),
            'errorResponses' => array(
                array(
                    'reason' => 'The request was rejected because it referenced an entity that does not exist. The error message describes the entity.',
                    'class' => 'NoSuchEntityException',
                ),
                array(
                    'reason' => 'The request was rejected because it attempted to create resources beyond the current AWS account limits. The error message describes the limit exceeded.',
                    'class' => 'LimitExceededException',
                ),
                array(
                    'reason' => 'The request was rejected because an invalid or out-of-range value was supplied for an input parameter.',
                    'class' => 'InvalidInputException',
                ),
                array(
                    'reason' => 'The request was rejected because it attempted to delete a resource that has attached subordinate entities. The error message describes these entities.',
                    'class' => 'DeleteConflictException',
                ),
                array(
                    'reason' => 'The request processing has failed because of an unknown error, exception or failure.',
                    'class' => 'ServiceFailureException',
                ),
            ),
        ),
        'DeletePolicyVersion' => array(
            'httpMethod' => 'POST',
            'uri' => '/',
            'class' => 'Aws\\Common\\Command\\QueryCommand',
            'responseClass' => 'EmptyOutput',
            'responseType' => 'model',
            'parameters' => array(
                'Action' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => 'DeletePolicyVersion',
                ),
                'Version' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => '2010-05-08',
                ),
                'PolicyArn' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 20,
                ),
                'VersionId' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'aws.query',
                ),
            ),
            'errorResponses' => array(
                array(
                    'reason' => 'The request was rejected because it referenced an entity that does not exist. The error message describes the entity.',
                    'class' => 'NoSuchEntityException',
                ),
                array(
                    'reason' => 'The request was rejected because it attempted to create resources beyond the current AWS account limits. The error message describes the limit exceeded.',
                    'class' => 'LimitExceededException',
                ),
                array(
                    'reason' => 'The request was rejected because an invalid or out-of-range value was supplied for an input parameter.',
                    'class' => 'InvalidInputException',
                ),
                array(
                    'reason' => 'The request was rejected because it attempted to delete a resource that has attached subordinate entities. The error message describes these entities.',
                    'class' => 'DeleteConflictException',
                ),
                array(
                    'reason' => 'The request processing has failed because of an unknown error, exception or failure.',
                    'class' => 'ServiceFailureException',
                ),
            ),
        ),
        'DeleteRole' => array(
            'httpMethod' => 'POST',
            'uri' => '/',
            'class' => 'Aws\\Common\\Command\\QueryCommand',
            'responseClass' => 'EmptyOutput',
            'responseType' => 'model',
            'parameters' => array(
                'Action' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => 'DeleteRole',
                ),
                'Version' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => '2010-05-08',
                ),
                'RoleName' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 1,
                ),
            ),
            'errorResponses' => array(
                array(
                    'reason' => 'The request was rejected because it referenced an entity that does not exist. The error message describes the entity.',
                    'class' => 'NoSuchEntityException',
                ),
                array(
                    'reason' => 'The request was rejected because it attempted to delete a resource that has attached subordinate entities. The error message describes these entities.',
                    'class' => 'DeleteConflictException',
                ),
                array(
                    'reason' => 'The request was rejected because it attempted to create resources beyond the current AWS account limits. The error message describes the limit exceeded.',
                    'class' => 'LimitExceededException',
                ),
                array(
                    'reason' => 'The request processing has failed because of an unknown error, exception or failure.',
                    'class' => 'ServiceFailureException',
                ),
            ),
        ),
        'DeleteRolePolicy' => array(
            'httpMethod' => 'POST',
            'uri' => '/',
            'class' => 'Aws\\Common\\Command\\QueryCommand',
            'responseClass' => 'EmptyOutput',
            'responseType' => 'model',
            'parameters' => array(
                'Action' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => 'DeleteRolePolicy',
                ),
                'Version' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => '2010-05-08',
                ),
                'RoleName' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 1,
                ),
                'PolicyName' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 1,
                ),
            ),
            'errorResponses' => array(
                array(
                    'reason' => 'The request was rejected because it referenced an entity that does not exist. The error message describes the entity.',
                    'class' => 'NoSuchEntityException',
                ),
                array(
                    'reason' => 'The request was rejected because it attempted to create resources beyond the current AWS account limits. The error message describes the limit exceeded.',
                    'class' => 'LimitExceededException',
                ),
                array(
                    'reason' => 'The request processing has failed because of an unknown error, exception or failure.',
                    'class' => 'ServiceFailureException',
                ),
            ),
        ),
        'DeleteSAMLProvider' => array(
            'httpMethod' => 'POST',
            'uri' => '/',
            'class' => 'Aws\\Common\\Command\\QueryCommand',
            'responseClass' => 'EmptyOutput',
            'responseType' => 'model',
            'parameters' => array(
                'Action' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => 'DeleteSAMLProvider',
                ),
                'Version' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => '2010-05-08',
                ),
                'SAMLProviderArn' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 20,
                ),
            ),
            'errorResponses' => array(
                array(
                    'reason' => 'The request was rejected because an invalid or out-of-range value was supplied for an input parameter.',
                    'class' => 'InvalidInputException',
                ),
                array(
                    'reason' => 'The request was rejected because it attempted to create resources beyond the current AWS account limits. The error message describes the limit exceeded.',
                    'class' => 'LimitExceededException',
                ),
                array(
                    'reason' => 'The request was rejected because it referenced an entity that does not exist. The error message describes the entity.',
                    'class' => 'NoSuchEntityException',
                ),
                array(
                    'reason' => 'The request processing has failed because of an unknown error, exception or failure.',
                    'class' => 'ServiceFailureException',
                ),
            ),
        ),
        'DeleteSSHPublicKey' => array(
            'httpMethod' => 'POST',
            'uri' => '/',
            'class' => 'Aws\\Common\\Command\\QueryCommand',
            'responseClass' => 'EmptyOutput',
            'responseType' => 'model',
            'parameters' => array(
                'Action' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => 'DeleteSSHPublicKey',
                ),
                'Version' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => '2010-05-08',
                ),
                'UserName' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 1,
                ),
                'SSHPublicKeyId' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 20,
                ),
            ),
            'errorResponses' => array(
                array(
                    'reason' => 'The request was rejected because it referenced an entity that does not exist. The error message describes the entity.',
                    'class' => 'NoSuchEntityException',
                ),
            ),
        ),
        'DeleteServerCertificate' => array(
            'httpMethod' => 'POST',
            'uri' => '/',
            'class' => 'Aws\\Common\\Command\\QueryCommand',
            'responseClass' => 'EmptyOutput',
            'responseType' => 'model',
            'parameters' => array(
                'Action' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => 'DeleteServerCertificate',
                ),
                'Version' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => '2010-05-08',
                ),
                'ServerCertificateName' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 1,
                ),
            ),
            'errorResponses' => array(
                array(
                    'reason' => 'The request was rejected because it referenced an entity that does not exist. The error message describes the entity.',
                    'class' => 'NoSuchEntityException',
                ),
                array(
                    'reason' => 'The request was rejected because it attempted to delete a resource that has attached subordinate entities. The error message describes these entities.',
                    'class' => 'DeleteConflictException',
                ),
                array(
                    'reason' => 'The request was rejected because it attempted to create resources beyond the current AWS account limits. The error message describes the limit exceeded.',
                    'class' => 'LimitExceededException',
                ),
                array(
                    'reason' => 'The request processing has failed because of an unknown error, exception or failure.',
                    'class' => 'ServiceFailureException',
                ),
            ),
        ),
        'DeleteSigningCertificate' => array(
            'httpMethod' => 'POST',
            'uri' => '/',
            'class' => 'Aws\\Common\\Command\\QueryCommand',
            'responseClass' => 'EmptyOutput',
            'responseType' => 'model',
            'parameters' => array(
                'Action' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => 'DeleteSigningCertificate',
                ),
                'Version' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => '2010-05-08',
                ),
                'UserName' => array(
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 1,
                ),
                'CertificateId' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 24,
                ),
            ),
            'errorResponses' => array(
                array(
                    'reason' => 'The request was rejected because it referenced an entity that does not exist. The error message describes the entity.',
                    'class' => 'NoSuchEntityException',
                ),
                array(
                    'reason' => 'The request was rejected because it attempted to create resources beyond the current AWS account limits. The error message describes the limit exceeded.',
                    'class' => 'LimitExceededException',
                ),
                array(
                    'reason' => 'The request processing has failed because of an unknown error, exception or failure.',
                    'class' => 'ServiceFailureException',
                ),
            ),
        ),
        'DeleteUser' => array(
            'httpMethod' => 'POST',
            'uri' => '/',
            'class' => 'Aws\\Common\\Command\\QueryCommand',
            'responseClass' => 'EmptyOutput',
            'responseType' => 'model',
            'parameters' => array(
                'Action' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => 'DeleteUser',
                ),
                'Version' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => '2010-05-08',
                ),
                'UserName' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 1,
                ),
            ),
            'errorResponses' => array(
                array(
                    'reason' => 'The request was rejected because it attempted to create resources beyond the current AWS account limits. The error message describes the limit exceeded.',
                    'class' => 'LimitExceededException',
                ),
                array(
                    'reason' => 'The request was rejected because it referenced an entity that does not exist. The error message describes the entity.',
                    'class' => 'NoSuchEntityException',
                ),
                array(
                    'reason' => 'The request was rejected because it attempted to delete a resource that has attached subordinate entities. The error message describes these entities.',
                    'class' => 'DeleteConflictException',
                ),
                array(
                    'reason' => 'The request processing has failed because of an unknown error, exception or failure.',
                    'class' => 'ServiceFailureException',
                ),
            ),
        ),
        'DeleteUserPolicy' => array(
            'httpMethod' => 'POST',
            'uri' => '/',
            'class' => 'Aws\\Common\\Command\\QueryCommand',
            'responseClass' => 'EmptyOutput',
            'responseType' => 'model',
            'parameters' => array(
                'Action' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => 'DeleteUserPolicy',
                ),
                'Version' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => '2010-05-08',
                ),
                'UserName' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 1,
                ),
                'PolicyName' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 1,
                ),
            ),
            'errorResponses' => array(
                array(
                    'reason' => 'The request was rejected because it referenced an entity that does not exist. The error message describes the entity.',
                    'class' => 'NoSuchEntityException',
                ),
                array(
                    'reason' => 'The request was rejected because it attempted to create resources beyond the current AWS account limits. The error message describes the limit exceeded.',
                    'class' => 'LimitExceededException',
                ),
                array(
                    'reason' => 'The request processing has failed because of an unknown error, exception or failure.',
                    'class' => 'ServiceFailureException',
                ),
            ),
        ),
        'DeleteVirtualMFADevice' => array(
            'httpMethod' => 'POST',
            'uri' => '/',
            'class' => 'Aws\\Common\\Command\\QueryCommand',
            'responseClass' => 'EmptyOutput',
            'responseType' => 'model',
            'parameters' => array(
                'Action' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => 'DeleteVirtualMFADevice',
                ),
                'Version' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => '2010-05-08',
                ),
                'SerialNumber' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 9,
                ),
            ),
            'errorResponses' => array(
                array(
                    'reason' => 'The request was rejected because it referenced an entity that does not exist. The error message describes the entity.',
                    'class' => 'NoSuchEntityException',
                ),
                array(
                    'reason' => 'The request was rejected because it attempted to delete a resource that has attached subordinate entities. The error message describes these entities.',
                    'class' => 'DeleteConflictException',
                ),
                array(
                    'reason' => 'The request was rejected because it attempted to create resources beyond the current AWS account limits. The error message describes the limit exceeded.',
                    'class' => 'LimitExceededException',
                ),
                array(
                    'reason' => 'The request processing has failed because of an unknown error, exception or failure.',
                    'class' => 'ServiceFailureException',
                ),
            ),
        ),
        'DetachGroupPolicy' => array(
            'httpMethod' => 'POST',
            'uri' => '/',
            'class' => 'Aws\\Common\\Command\\QueryCommand',
            'responseClass' => 'EmptyOutput',
            'responseType' => 'model',
            'parameters' => array(
                'Action' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => 'DetachGroupPolicy',
                ),
                'Version' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => '2010-05-08',
                ),
                'GroupName' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 1,
                ),
                'PolicyArn' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 20,
                ),
            ),
            'errorResponses' => array(
                array(
                    'reason' => 'The request was rejected because it referenced an entity that does not exist. The error message describes the entity.',
                    'class' => 'NoSuchEntityException',
                ),
                array(
                    'reason' => 'The request was rejected because it attempted to create resources beyond the current AWS account limits. The error message describes the limit exceeded.',
                    'class' => 'LimitExceededException',
                ),
                array(
                    'reason' => 'The request was rejected because an invalid or out-of-range value was supplied for an input parameter.',
                    'class' => 'InvalidInputException',
                ),
                array(
                    'reason' => 'The request processing has failed because of an unknown error, exception or failure.',
                    'class' => 'ServiceFailureException',
                ),
            ),
        ),
        'DetachRolePolicy' => array(
            'httpMethod' => 'POST',
            'uri' => '/',
            'class' => 'Aws\\Common\\Command\\QueryCommand',
            'responseClass' => 'EmptyOutput',
            'responseType' => 'model',
            'parameters' => array(
                'Action' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => 'DetachRolePolicy',
                ),
                'Version' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => '2010-05-08',
                ),
                'RoleName' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 1,
                ),
                'PolicyArn' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 20,
                ),
            ),
            'errorResponses' => array(
                array(
                    'reason' => 'The request was rejected because it referenced an entity that does not exist. The error message describes the entity.',
                    'class' => 'NoSuchEntityException',
                ),
                array(
                    'reason' => 'The request was rejected because it attempted to create resources beyond the current AWS account limits. The error message describes the limit exceeded.',
                    'class' => 'LimitExceededException',
                ),
                array(
                    'reason' => 'The request was rejected because an invalid or out-of-range value was supplied for an input parameter.',
                    'class' => 'InvalidInputException',
                ),
                array(
                    'reason' => 'The request processing has failed because of an unknown error, exception or failure.',
                    'class' => 'ServiceFailureException',
                ),
            ),
        ),
        'DetachUserPolicy' => array(
            'httpMethod' => 'POST',
            'uri' => '/',
            'class' => 'Aws\\Common\\Command\\QueryCommand',
            'responseClass' => 'EmptyOutput',
            'responseType' => 'model',
            'parameters' => array(
                'Action' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => 'DetachUserPolicy',
                ),
                'Version' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => '2010-05-08',
                ),
                'UserName' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 1,
                ),
                'PolicyArn' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 20,
                ),
            ),
            'errorResponses' => array(
                array(
                    'reason' => 'The request was rejected because it referenced an entity that does not exist. The error message describes the entity.',
                    'class' => 'NoSuchEntityException',
                ),
                array(
                    'reason' => 'The request was rejected because it attempted to create resources beyond the current AWS account limits. The error message describes the limit exceeded.',
                    'class' => 'LimitExceededException',
                ),
                array(
                    'reason' => 'The request was rejected because an invalid or out-of-range value was supplied for an input parameter.',
                    'class' => 'InvalidInputException',
                ),
                array(
                    'reason' => 'The request processing has failed because of an unknown error, exception or failure.',
                    'class' => 'ServiceFailureException',
                ),
            ),
        ),
        'EnableMFADevice' => array(
            'httpMethod' => 'POST',
            'uri' => '/',
            'class' => 'Aws\\Common\\Command\\QueryCommand',
            'responseClass' => 'EmptyOutput',
            'responseType' => 'model',
            'parameters' => array(
                'Action' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => 'EnableMFADevice',
                ),
                'Version' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => '2010-05-08',
                ),
                'UserName' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 1,
                ),
                'SerialNumber' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 9,
                ),
                'AuthenticationCode1' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 6,
                ),
                'AuthenticationCode2' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 6,
                ),
            ),
            'errorResponses' => array(
                array(
                    'reason' => 'The request was rejected because it attempted to create a resource that already exists.',
                    'class' => 'EntityAlreadyExistsException',
                ),
                array(
                    'reason' => 'The request was rejected because it referenced an entity that is temporarily unmodifiable, such as a user name that was deleted and then recreated. The error indicates that the request is likely to succeed if you try again after waiting several minutes. The error message describes the entity.',
                    'class' => 'EntityTemporarilyUnmodifiableException',
                ),
                array(
                    'reason' => 'The request was rejected because the authentication code was not recognized. The error message describes the specific error.',
                    'class' => 'InvalidAuthenticationCodeException',
                ),
                array(
                    'reason' => 'The request was rejected because it attempted to create resources beyond the current AWS account limits. The error message describes the limit exceeded.',
                    'class' => 'LimitExceededException',
                ),
                array(
                    'reason' => 'The request was rejected because it referenced an entity that does not exist. The error message describes the entity.',
                    'class' => 'NoSuchEntityException',
                ),
                array(
                    'reason' => 'The request processing has failed because of an unknown error, exception or failure.',
                    'class' => 'ServiceFailureException',
                ),
            ),
        ),
        'GenerateCredentialReport' => array(
            'httpMethod' => 'POST',
            'uri' => '/',
            'class' => 'Aws\\Common\\Command\\QueryCommand',
            'responseClass' => 'GenerateCredentialReportResponse',
            'responseType' => 'model',
            'parameters' => array(
                'Action' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => 'GenerateCredentialReport',
                ),
                'Version' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => '2010-05-08',
                ),
            ),
            'errorResponses' => array(
                array(
                    'reason' => 'The request was rejected because it attempted to create resources beyond the current AWS account limits. The error message describes the limit exceeded.',
                    'class' => 'LimitExceededException',
                ),
                array(
                    'reason' => 'The request processing has failed because of an unknown error, exception or failure.',
                    'class' => 'ServiceFailureException',
                ),
            ),
        ),
        'GetAccessKeyLastUsed' => array(
            'httpMethod' => 'POST',
            'uri' => '/',
            'class' => 'Aws\\Common\\Command\\QueryCommand',
            'responseClass' => 'GetAccessKeyLastUsedResponse',
            'responseType' => 'model',
            'parameters' => array(
                'Action' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => 'GetAccessKeyLastUsed',
                ),
                'Version' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => '2010-05-08',
                ),
                'AccessKeyId' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 16,
                ),
            ),
            'errorResponses' => array(
                array(
                    'reason' => 'The request was rejected because it referenced an entity that does not exist. The error message describes the entity.',
                    'class' => 'NoSuchEntityException',
                ),
            ),
        ),
        'GetAccountAuthorizationDetails' => array(
            'httpMethod' => 'POST',
            'uri' => '/',
            'class' => 'Aws\\Common\\Command\\QueryCommand',
            'responseClass' => 'GetAccountAuthorizationDetailsResponse',
            'responseType' => 'model',
            'parameters' => array(
                'Action' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => 'GetAccountAuthorizationDetails',
                ),
                'Version' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => '2010-05-08',
                ),
                'Filter' => array(
                    'type' => 'array',
                    'location' => 'aws.query',
                    'sentAs' => 'Filter.member',
                    'items' => array(
                        'name' => 'EntityType',
                        'type' => 'string',
                    ),
                ),
                'MaxItems' => array(
                    'type' => 'numeric',
                    'location' => 'aws.query',
                    'minimum' => 1,
                    'maximum' => 1000,
                ),
                'Marker' => array(
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 1,
                ),
            ),
            'errorResponses' => array(
                array(
                    'reason' => 'The request processing has failed because of an unknown error, exception or failure.',
                    'class' => 'ServiceFailureException',
                ),
            ),
        ),
        'GetAccountPasswordPolicy' => array(
            'httpMethod' => 'POST',
            'uri' => '/',
            'class' => 'Aws\\Common\\Command\\QueryCommand',
            'responseClass' => 'GetAccountPasswordPolicyResponse',
            'responseType' => 'model',
            'parameters' => array(
                'Action' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => 'GetAccountPasswordPolicy',
                ),
                'Version' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => '2010-05-08',
                ),
            ),
            'errorResponses' => array(
                array(
                    'reason' => 'The request was rejected because it referenced an entity that does not exist. The error message describes the entity.',
                    'class' => 'NoSuchEntityException',
                ),
                array(
                    'reason' => 'The request processing has failed because of an unknown error, exception or failure.',
                    'class' => 'ServiceFailureException',
                ),
            ),
        ),
        'GetAccountSummary' => array(
            'httpMethod' => 'POST',
            'uri' => '/',
            'class' => 'Aws\\Common\\Command\\QueryCommand',
            'responseClass' => 'GetAccountSummaryResponse',
            'responseType' => 'model',
            'parameters' => array(
                'Action' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => 'GetAccountSummary',
                ),
                'Version' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => '2010-05-08',
                ),
            ),
            'errorResponses' => array(
                array(
                    'reason' => 'The request processing has failed because of an unknown error, exception or failure.',
                    'class' => 'ServiceFailureException',
                ),
            ),
        ),
        'GetContextKeysForCustomPolicy' => array(
            'httpMethod' => 'POST',
            'uri' => '/',
            'class' => 'Aws\\Common\\Command\\QueryCommand',
            'responseClass' => 'GetContextKeysForPolicyResponse',
            'responseType' => 'model',
            'parameters' => array(
                'Action' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => 'GetContextKeysForCustomPolicy',
                ),
                'Version' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => '2010-05-08',
                ),
                'PolicyInputList' => array(
                    'required' => true,
                    'type' => 'array',
                    'location' => 'aws.query',
                    'sentAs' => 'PolicyInputList.member',
                    'items' => array(
                        'name' => 'policyDocumentType',
                        'type' => 'string',
                        'minLength' => 1,
                    ),
                ),
            ),
            'errorResponses' => array(
                array(
                    'reason' => 'The request was rejected because an invalid or out-of-range value was supplied for an input parameter.',
                    'class' => 'InvalidInputException',
                ),
            ),
        ),
        'GetContextKeysForPrincipalPolicy' => array(
            'httpMethod' => 'POST',
            'uri' => '/',
            'class' => 'Aws\\Common\\Command\\QueryCommand',
            'responseClass' => 'GetContextKeysForPolicyResponse',
            'responseType' => 'model',
            'parameters' => array(
                'Action' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => 'GetContextKeysForPrincipalPolicy',
                ),
                'Version' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => '2010-05-08',
                ),
                'PolicySourceArn' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 20,
                ),
                'PolicyInputList' => array(
                    'type' => 'array',
                    'location' => 'aws.query',
                    'sentAs' => 'PolicyInputList.member',
                    'items' => array(
                        'name' => 'policyDocumentType',
                        'type' => 'string',
                        'minLength' => 1,
                    ),
                ),
            ),
            'errorResponses' => array(
                array(
                    'reason' => 'The request was rejected because it referenced an entity that does not exist. The error message describes the entity.',
                    'class' => 'NoSuchEntityException',
                ),
                array(
                    'reason' => 'The request was rejected because an invalid or out-of-range value was supplied for an input parameter.',
                    'class' => 'InvalidInputException',
                ),
            ),
        ),
        'GetCredentialReport' => array(
            'httpMethod' => 'POST',
            'uri' => '/',
            'class' => 'Aws\\Common\\Command\\QueryCommand',
            'responseClass' => 'GetCredentialReportResponse',
            'responseType' => 'model',
            'parameters' => array(
                'Action' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => 'GetCredentialReport',
                ),
                'Version' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => '2010-05-08',
                ),
            ),
            'errorResponses' => array(
                array(
                    'reason' => 'The request was rejected because the credential report does not exist. To generate a credential report, use GenerateCredentialReport.',
                    'class' => 'CredentialReportNotPresentException',
                ),
                array(
                    'reason' => 'The request was rejected because the most recent credential report has expired. To generate a new credential report, use GenerateCredentialReport. For more information about credential report expiration, see Getting Credential Reports in the IAM User Guide.',
                    'class' => 'CredentialReportExpiredException',
                ),
                array(
                    'reason' => 'The request was rejected because the credential report is still being generated.',
                    'class' => 'CredentialReportNotReadyException',
                ),
                array(
                    'reason' => 'The request processing has failed because of an unknown error, exception or failure.',
                    'class' => 'ServiceFailureException',
                ),
            ),
        ),
        'GetGroup' => array(
            'httpMethod' => 'POST',
            'uri' => '/',
            'class' => 'Aws\\Common\\Command\\QueryCommand',
            'responseClass' => 'GetGroupResponse',
            'responseType' => 'model',
            'parameters' => array(
                'Action' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => 'GetGroup',
                ),
                'Version' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => '2010-05-08',
                ),
                'GroupName' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 1,
                ),
                'Marker' => array(
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 1,
                ),
                'MaxItems' => array(
                    'type' => 'numeric',
                    'location' => 'aws.query',
                    'minimum' => 1,
                    'maximum' => 1000,
                ),
            ),
            'errorResponses' => array(
                array(
                    'reason' => 'The request was rejected because it referenced an entity that does not exist. The error message describes the entity.',
                    'class' => 'NoSuchEntityException',
                ),
                array(
                    'reason' => 'The request processing has failed because of an unknown error, exception or failure.',
                    'class' => 'ServiceFailureException',
                ),
            ),
        ),
        'GetGroupPolicy' => array(
            'httpMethod' => 'POST',
            'uri' => '/',
            'class' => 'Aws\\Common\\Command\\QueryCommand',
            'responseClass' => 'GetGroupPolicyResponse',
            'responseType' => 'model',
            'parameters' => array(
                'Action' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => 'GetGroupPolicy',
                ),
                'Version' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => '2010-05-08',
                ),
                'GroupName' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 1,
                ),
                'PolicyName' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 1,
                ),
            ),
            'errorResponses' => array(
                array(
                    'reason' => 'The request was rejected because it referenced an entity that does not exist. The error message describes the entity.',
                    'class' => 'NoSuchEntityException',
                ),
                array(
                    'reason' => 'The request processing has failed because of an unknown error, exception or failure.',
                    'class' => 'ServiceFailureException',
                ),
            ),
        ),
        'GetInstanceProfile' => array(
            'httpMethod' => 'POST',
            'uri' => '/',
            'class' => 'Aws\\Common\\Command\\QueryCommand',
            'responseClass' => 'GetInstanceProfileResponse',
            'responseType' => 'model',
            'parameters' => array(
                'Action' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => 'GetInstanceProfile',
                ),
                'Version' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => '2010-05-08',
                ),
                'InstanceProfileName' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 1,
                ),
            ),
            'errorResponses' => array(
                array(
                    'reason' => 'The request was rejected because it referenced an entity that does not exist. The error message describes the entity.',
                    'class' => 'NoSuchEntityException',
                ),
                array(
                    'reason' => 'The request processing has failed because of an unknown error, exception or failure.',
                    'class' => 'ServiceFailureException',
                ),
            ),
        ),
        'GetLoginProfile' => array(
            'httpMethod' => 'POST',
            'uri' => '/',
            'class' => 'Aws\\Common\\Command\\QueryCommand',
            'responseClass' => 'GetLoginProfileResponse',
            'responseType' => 'model',
            'parameters' => array(
                'Action' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => 'GetLoginProfile',
                ),
                'Version' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => '2010-05-08',
                ),
                'UserName' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 1,
                ),
            ),
            'errorResponses' => array(
                array(
                    'reason' => 'The request was rejected because it referenced an entity that does not exist. The error message describes the entity.',
                    'class' => 'NoSuchEntityException',
                ),
                array(
                    'reason' => 'The request processing has failed because of an unknown error, exception or failure.',
                    'class' => 'ServiceFailureException',
                ),
            ),
        ),
        'GetOpenIDConnectProvider' => array(
            'httpMethod' => 'POST',
            'uri' => '/',
            'class' => 'Aws\\Common\\Command\\QueryCommand',
            'responseClass' => 'GetOpenIDConnectProviderResponse',
            'responseType' => 'model',
            'parameters' => array(
                'Action' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => 'GetOpenIDConnectProvider',
                ),
                'Version' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => '2010-05-08',
                ),
                'OpenIDConnectProviderArn' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 20,
                ),
            ),
            'errorResponses' => array(
                array(
                    'reason' => 'The request was rejected because an invalid or out-of-range value was supplied for an input parameter.',
                    'class' => 'InvalidInputException',
                ),
                array(
                    'reason' => 'The request was rejected because it referenced an entity that does not exist. The error message describes the entity.',
                    'class' => 'NoSuchEntityException',
                ),
                array(
                    'reason' => 'The request processing has failed because of an unknown error, exception or failure.',
                    'class' => 'ServiceFailureException',
                ),
            ),
        ),
        'GetPolicy' => array(
            'httpMethod' => 'POST',
            'uri' => '/',
            'class' => 'Aws\\Common\\Command\\QueryCommand',
            'responseClass' => 'GetPolicyResponse',
            'responseType' => 'model',
            'parameters' => array(
                'Action' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => 'GetPolicy',
                ),
                'Version' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => '2010-05-08',
                ),
                'PolicyArn' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 20,
                ),
            ),
            'errorResponses' => array(
                array(
                    'reason' => 'The request was rejected because it referenced an entity that does not exist. The error message describes the entity.',
                    'class' => 'NoSuchEntityException',
                ),
                array(
                    'reason' => 'The request was rejected because an invalid or out-of-range value was supplied for an input parameter.',
                    'class' => 'InvalidInputException',
                ),
                array(
                    'reason' => 'The request processing has failed because of an unknown error, exception or failure.',
                    'class' => 'ServiceFailureException',
                ),
            ),
        ),
        'GetPolicyVersion' => array(
            'httpMethod' => 'POST',
            'uri' => '/',
            'class' => 'Aws\\Common\\Command\\QueryCommand',
            'responseClass' => 'GetPolicyVersionResponse',
            'responseType' => 'model',
            'parameters' => array(
                'Action' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => 'GetPolicyVersion',
                ),
                'Version' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => '2010-05-08',
                ),
                'PolicyArn' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 20,
                ),
                'VersionId' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'aws.query',
                ),
            ),
            'errorResponses' => array(
                array(
                    'reason' => 'The request was rejected because it referenced an entity that does not exist. The error message describes the entity.',
                    'class' => 'NoSuchEntityException',
                ),
                array(
                    'reason' => 'The request was rejected because an invalid or out-of-range value was supplied for an input parameter.',
                    'class' => 'InvalidInputException',
                ),
                array(
                    'reason' => 'The request processing has failed because of an unknown error, exception or failure.',
                    'class' => 'ServiceFailureException',
                ),
            ),
        ),
        'GetRole' => array(
            'httpMethod' => 'POST',
            'uri' => '/',
            'class' => 'Aws\\Common\\Command\\QueryCommand',
            'responseClass' => 'GetRoleResponse',
            'responseType' => 'model',
            'parameters' => array(
                'Action' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => 'GetRole',
                ),
                'Version' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => '2010-05-08',
                ),
                'RoleName' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 1,
                ),
            ),
            'errorResponses' => array(
                array(
                    'reason' => 'The request was rejected because it referenced an entity that does not exist. The error message describes the entity.',
                    'class' => 'NoSuchEntityException',
                ),
                array(
                    'reason' => 'The request processing has failed because of an unknown error, exception or failure.',
                    'class' => 'ServiceFailureException',
                ),
            ),
        ),
        'GetRolePolicy' => array(
            'httpMethod' => 'POST',
            'uri' => '/',
            'class' => 'Aws\\Common\\Command\\QueryCommand',
            'responseClass' => 'GetRolePolicyResponse',
            'responseType' => 'model',
            'parameters' => array(
                'Action' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => 'GetRolePolicy',
                ),
                'Version' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => '2010-05-08',
                ),
                'RoleName' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 1,
                ),
                'PolicyName' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 1,
                ),
            ),
            'errorResponses' => array(
                array(
                    'reason' => 'The request was rejected because it referenced an entity that does not exist. The error message describes the entity.',
                    'class' => 'NoSuchEntityException',
                ),
                array(
                    'reason' => 'The request processing has failed because of an unknown error, exception or failure.',
                    'class' => 'ServiceFailureException',
                ),
            ),
        ),
        'GetSAMLProvider' => array(
            'httpMethod' => 'POST',
            'uri' => '/',
            'class' => 'Aws\\Common\\Command\\QueryCommand',
            'responseClass' => 'GetSAMLProviderResponse',
            'responseType' => 'model',
            'parameters' => array(
                'Action' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => 'GetSAMLProvider',
                ),
                'Version' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => '2010-05-08',
                ),
                'SAMLProviderArn' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 20,
                ),
            ),
            'errorResponses' => array(
                array(
                    'reason' => 'The request was rejected because it referenced an entity that does not exist. The error message describes the entity.',
                    'class' => 'NoSuchEntityException',
                ),
                array(
                    'reason' => 'The request was rejected because an invalid or out-of-range value was supplied for an input parameter.',
                    'class' => 'InvalidInputException',
                ),
                array(
                    'reason' => 'The request processing has failed because of an unknown error, exception or failure.',
                    'class' => 'ServiceFailureException',
                ),
            ),
        ),
        'GetSSHPublicKey' => array(
            'httpMethod' => 'POST',
            'uri' => '/',
            'class' => 'Aws\\Common\\Command\\QueryCommand',
            'responseClass' => 'GetSSHPublicKeyResponse',
            'responseType' => 'model',
            'parameters' => array(
                'Action' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => 'GetSSHPublicKey',
                ),
                'Version' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => '2010-05-08',
                ),
                'UserName' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 1,
                ),
                'SSHPublicKeyId' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 20,
                ),
                'Encoding' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'aws.query',
                ),
            ),
            'errorResponses' => array(
                array(
                    'reason' => 'The request was rejected because it referenced an entity that does not exist. The error message describes the entity.',
                    'class' => 'NoSuchEntityException',
                ),
                array(
                    'reason' => 'The request was rejected because the public key encoding format is unsupported or unrecognized.',
                    'class' => 'UnrecognizedPublicKeyEncodingException',
                ),
            ),
        ),
        'GetServerCertificate' => array(
            'httpMethod' => 'POST',
            'uri' => '/',
            'class' => 'Aws\\Common\\Command\\QueryCommand',
            'responseClass' => 'GetServerCertificateResponse',
            'responseType' => 'model',
            'parameters' => array(
                'Action' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => 'GetServerCertificate',
                ),
                'Version' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => '2010-05-08',
                ),
                'ServerCertificateName' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 1,
                ),
            ),
            'errorResponses' => array(
                array(
                    'reason' => 'The request was rejected because it referenced an entity that does not exist. The error message describes the entity.',
                    'class' => 'NoSuchEntityException',
                ),
                array(
                    'reason' => 'The request processing has failed because of an unknown error, exception or failure.',
                    'class' => 'ServiceFailureException',
                ),
            ),
        ),
        'GetUser' => array(
            'httpMethod' => 'POST',
            'uri' => '/',
            'class' => 'Aws\\Common\\Command\\QueryCommand',
            'responseClass' => 'GetUserResponse',
            'responseType' => 'model',
            'parameters' => array(
                'Action' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => 'GetUser',
                ),
                'Version' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => '2010-05-08',
                ),
                'UserName' => array(
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 1,
                ),
            ),
            'errorResponses' => array(
                array(
                    'reason' => 'The request was rejected because it referenced an entity that does not exist. The error message describes the entity.',
                    'class' => 'NoSuchEntityException',
                ),
                array(
                    'reason' => 'The request processing has failed because of an unknown error, exception or failure.',
                    'class' => 'ServiceFailureException',
                ),
            ),
        ),
        'GetUserPolicy' => array(
            'httpMethod' => 'POST',
            'uri' => '/',
            'class' => 'Aws\\Common\\Command\\QueryCommand',
            'responseClass' => 'GetUserPolicyResponse',
            'responseType' => 'model',
            'parameters' => array(
                'Action' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => 'GetUserPolicy',
                ),
                'Version' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => '2010-05-08',
                ),
                'UserName' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 1,
                ),
                'PolicyName' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 1,
                ),
            ),
            'errorResponses' => array(
                array(
                    'reason' => 'The request was rejected because it referenced an entity that does not exist. The error message describes the entity.',
                    'class' => 'NoSuchEntityException',
                ),
                array(
                    'reason' => 'The request processing has failed because of an unknown error, exception or failure.',
                    'class' => 'ServiceFailureException',
                ),
            ),
        ),
        'ListAccessKeys' => array(
            'httpMethod' => 'POST',
            'uri' => '/',
            'class' => 'Aws\\Common\\Command\\QueryCommand',
            'responseClass' => 'ListAccessKeysResponse',
            'responseType' => 'model',
            'parameters' => array(
                'Action' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => 'ListAccessKeys',
                ),
                'Version' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => '2010-05-08',
                ),
                'UserName' => array(
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 1,
                ),
                'Marker' => array(
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 1,
                ),
                'MaxItems' => array(
                    'type' => 'numeric',
                    'location' => 'aws.query',
                    'minimum' => 1,
                    'maximum' => 1000,
                ),
            ),
            'errorResponses' => array(
                array(
                    'reason' => 'The request was rejected because it referenced an entity that does not exist. The error message describes the entity.',
                    'class' => 'NoSuchEntityException',
                ),
                array(
                    'reason' => 'The request processing has failed because of an unknown error, exception or failure.',
                    'class' => 'ServiceFailureException',
                ),
            ),
        ),
        'ListAccountAliases' => array(
            'httpMethod' => 'POST',
            'uri' => '/',
            'class' => 'Aws\\Common\\Command\\QueryCommand',
            'responseClass' => 'ListAccountAliasesResponse',
            'responseType' => 'model',
            'parameters' => array(
                'Action' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => 'ListAccountAliases',
                ),
                'Version' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => '2010-05-08',
                ),
                'Marker' => array(
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 1,
                ),
                'MaxItems' => array(
                    'type' => 'numeric',
                    'location' => 'aws.query',
                    'minimum' => 1,
                    'maximum' => 1000,
                ),
            ),
            'errorResponses' => array(
                array(
                    'reason' => 'The request processing has failed because of an unknown error, exception or failure.',
                    'class' => 'ServiceFailureException',
                ),
            ),
        ),
        'ListAttachedGroupPolicies' => array(
            'httpMethod' => 'POST',
            'uri' => '/',
            'class' => 'Aws\\Common\\Command\\QueryCommand',
            'responseClass' => 'ListAttachedGroupPoliciesResponse',
            'responseType' => 'model',
            'parameters' => array(
                'Action' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => 'ListAttachedGroupPolicies',
                ),
                'Version' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => '2010-05-08',
                ),
                'GroupName' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 1,
                ),
                'PathPrefix' => array(
                    'type' => 'string',
                    'location' => 'aws.query',
                ),
                'Marker' => array(
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 1,
                ),
                'MaxItems' => array(
                    'type' => 'numeric',
                    'location' => 'aws.query',
                    'minimum' => 1,
                    'maximum' => 1000,
                ),
            ),
            'errorResponses' => array(
                array(
                    'reason' => 'The request was rejected because it referenced an entity that does not exist. The error message describes the entity.',
                    'class' => 'NoSuchEntityException',
                ),
                array(
                    'reason' => 'The request was rejected because an invalid or out-of-range value was supplied for an input parameter.',
                    'class' => 'InvalidInputException',
                ),
                array(
                    'reason' => 'The request processing has failed because of an unknown error, exception or failure.',
                    'class' => 'ServiceFailureException',
                ),
            ),
        ),
        'ListAttachedRolePolicies' => array(
            'httpMethod' => 'POST',
            'uri' => '/',
            'class' => 'Aws\\Common\\Command\\QueryCommand',
            'responseClass' => 'ListAttachedRolePoliciesResponse',
            'responseType' => 'model',
            'parameters' => array(
                'Action' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => 'ListAttachedRolePolicies',
                ),
                'Version' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => '2010-05-08',
                ),
                'RoleName' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 1,
                ),
                'PathPrefix' => array(
                    'type' => 'string',
                    'location' => 'aws.query',
                ),
                'Marker' => array(
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 1,
                ),
                'MaxItems' => array(
                    'type' => 'numeric',
                    'location' => 'aws.query',
                    'minimum' => 1,
                    'maximum' => 1000,
                ),
            ),
            'errorResponses' => array(
                array(
                    'reason' => 'The request was rejected because it referenced an entity that does not exist. The error message describes the entity.',
                    'class' => 'NoSuchEntityException',
                ),
                array(
                    'reason' => 'The request was rejected because an invalid or out-of-range value was supplied for an input parameter.',
                    'class' => 'InvalidInputException',
                ),
                array(
                    'reason' => 'The request processing has failed because of an unknown error, exception or failure.',
                    'class' => 'ServiceFailureException',
                ),
            ),
        ),
        'ListAttachedUserPolicies' => array(
            'httpMethod' => 'POST',
            'uri' => '/',
            'class' => 'Aws\\Common\\Command\\QueryCommand',
            'responseClass' => 'ListAttachedUserPoliciesResponse',
            'responseType' => 'model',
            'parameters' => array(
                'Action' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => 'ListAttachedUserPolicies',
                ),
                'Version' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => '2010-05-08',
                ),
                'UserName' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 1,
                ),
                'PathPrefix' => array(
                    'type' => 'string',
                    'location' => 'aws.query',
                ),
                'Marker' => array(
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 1,
                ),
                'MaxItems' => array(
                    'type' => 'numeric',
                    'location' => 'aws.query',
                    'minimum' => 1,
                    'maximum' => 1000,
                ),
            ),
            'errorResponses' => array(
                array(
                    'reason' => 'The request was rejected because it referenced an entity that does not exist. The error message describes the entity.',
                    'class' => 'NoSuchEntityException',
                ),
                array(
                    'reason' => 'The request was rejected because an invalid or out-of-range value was supplied for an input parameter.',
                    'class' => 'InvalidInputException',
                ),
                array(
                    'reason' => 'The request processing has failed because of an unknown error, exception or failure.',
                    'class' => 'ServiceFailureException',
                ),
            ),
        ),
        'ListEntitiesForPolicy' => array(
            'httpMethod' => 'POST',
            'uri' => '/',
            'class' => 'Aws\\Common\\Command\\QueryCommand',
            'responseClass' => 'ListEntitiesForPolicyResponse',
            'responseType' => 'model',
            'parameters' => array(
                'Action' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => 'ListEntitiesForPolicy',
                ),
                'Version' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => '2010-05-08',
                ),
                'PolicyArn' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 20,
                ),
                'EntityFilter' => array(
                    'type' => 'string',
                    'location' => 'aws.query',
                ),
                'PathPrefix' => array(
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 1,
                ),
                'Marker' => array(
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 1,
                ),
                'MaxItems' => array(
                    'type' => 'numeric',
                    'location' => 'aws.query',
                    'minimum' => 1,
                    'maximum' => 1000,
                ),
            ),
            'errorResponses' => array(
                array(
                    'reason' => 'The request was rejected because it referenced an entity that does not exist. The error message describes the entity.',
                    'class' => 'NoSuchEntityException',
                ),
                array(
                    'reason' => 'The request was rejected because an invalid or out-of-range value was supplied for an input parameter.',
                    'class' => 'InvalidInputException',
                ),
                array(
                    'reason' => 'The request processing has failed because of an unknown error, exception or failure.',
                    'class' => 'ServiceFailureException',
                ),
            ),
        ),
        'ListGroupPolicies' => array(
            'httpMethod' => 'POST',
            'uri' => '/',
            'class' => 'Aws\\Common\\Command\\QueryCommand',
            'responseClass' => 'ListGroupPoliciesResponse',
            'responseType' => 'model',
            'parameters' => array(
                'Action' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => 'ListGroupPolicies',
                ),
                'Version' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => '2010-05-08',
                ),
                'GroupName' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 1,
                ),
                'Marker' => array(
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 1,
                ),
                'MaxItems' => array(
                    'type' => 'numeric',
                    'location' => 'aws.query',
                    'minimum' => 1,
                    'maximum' => 1000,
                ),
            ),
            'errorResponses' => array(
                array(
                    'reason' => 'The request was rejected because it referenced an entity that does not exist. The error message describes the entity.',
                    'class' => 'NoSuchEntityException',
                ),
                array(
                    'reason' => 'The request processing has failed because of an unknown error, exception or failure.',
                    'class' => 'ServiceFailureException',
                ),
            ),
        ),
        'ListGroups' => array(
            'httpMethod' => 'POST',
            'uri' => '/',
            'class' => 'Aws\\Common\\Command\\QueryCommand',
            'responseClass' => 'ListGroupsResponse',
            'responseType' => 'model',
            'parameters' => array(
                'Action' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => 'ListGroups',
                ),
                'Version' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => '2010-05-08',
                ),
                'PathPrefix' => array(
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 1,
                ),
                'Marker' => array(
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 1,
                ),
                'MaxItems' => array(
                    'type' => 'numeric',
                    'location' => 'aws.query',
                    'minimum' => 1,
                    'maximum' => 1000,
                ),
            ),
            'errorResponses' => array(
                array(
                    'reason' => 'The request processing has failed because of an unknown error, exception or failure.',
                    'class' => 'ServiceFailureException',
                ),
            ),
        ),
        'ListGroupsForUser' => array(
            'httpMethod' => 'POST',
            'uri' => '/',
            'class' => 'Aws\\Common\\Command\\QueryCommand',
            'responseClass' => 'ListGroupsForUserResponse',
            'responseType' => 'model',
            'parameters' => array(
                'Action' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => 'ListGroupsForUser',
                ),
                'Version' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => '2010-05-08',
                ),
                'UserName' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 1,
                ),
                'Marker' => array(
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 1,
                ),
                'MaxItems' => array(
                    'type' => 'numeric',
                    'location' => 'aws.query',
                    'minimum' => 1,
                    'maximum' => 1000,
                ),
            ),
            'errorResponses' => array(
                array(
                    'reason' => 'The request was rejected because it referenced an entity that does not exist. The error message describes the entity.',
                    'class' => 'NoSuchEntityException',
                ),
                array(
                    'reason' => 'The request processing has failed because of an unknown error, exception or failure.',
                    'class' => 'ServiceFailureException',
                ),
            ),
        ),
        'ListInstanceProfiles' => array(
            'httpMethod' => 'POST',
            'uri' => '/',
            'class' => 'Aws\\Common\\Command\\QueryCommand',
            'responseClass' => 'ListInstanceProfilesResponse',
            'responseType' => 'model',
            'parameters' => array(
                'Action' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => 'ListInstanceProfiles',
                ),
                'Version' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => '2010-05-08',
                ),
                'PathPrefix' => array(
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 1,
                ),
                'Marker' => array(
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 1,
                ),
                'MaxItems' => array(
                    'type' => 'numeric',
                    'location' => 'aws.query',
                    'minimum' => 1,
                    'maximum' => 1000,
                ),
            ),
            'errorResponses' => array(
                array(
                    'reason' => 'The request processing has failed because of an unknown error, exception or failure.',
                    'class' => 'ServiceFailureException',
                ),
            ),
        ),
        'ListInstanceProfilesForRole' => array(
            'httpMethod' => 'POST',
            'uri' => '/',
            'class' => 'Aws\\Common\\Command\\QueryCommand',
            'responseClass' => 'ListInstanceProfilesForRoleResponse',
            'responseType' => 'model',
            'parameters' => array(
                'Action' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => 'ListInstanceProfilesForRole',
                ),
                'Version' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => '2010-05-08',
                ),
                'RoleName' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 1,
                ),
                'Marker' => array(
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 1,
                ),
                'MaxItems' => array(
                    'type' => 'numeric',
                    'location' => 'aws.query',
                    'minimum' => 1,
                    'maximum' => 1000,
                ),
            ),
            'errorResponses' => array(
                array(
                    'reason' => 'The request was rejected because it referenced an entity that does not exist. The error message describes the entity.',
                    'class' => 'NoSuchEntityException',
                ),
                array(
                    'reason' => 'The request processing has failed because of an unknown error, exception or failure.',
                    'class' => 'ServiceFailureException',
                ),
            ),
        ),
        'ListMFADevices' => array(
            'httpMethod' => 'POST',
            'uri' => '/',
            'class' => 'Aws\\Common\\Command\\QueryCommand',
            'responseClass' => 'ListMFADevicesResponse',
            'responseType' => 'model',
            'parameters' => array(
                'Action' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => 'ListMFADevices',
                ),
                'Version' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => '2010-05-08',
                ),
                'UserName' => array(
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 1,
                ),
                'Marker' => array(
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 1,
                ),
                'MaxItems' => array(
                    'type' => 'numeric',
                    'location' => 'aws.query',
                    'minimum' => 1,
                    'maximum' => 1000,
                ),
            ),
            'errorResponses' => array(
                array(
                    'reason' => 'The request was rejected because it referenced an entity that does not exist. The error message describes the entity.',
                    'class' => 'NoSuchEntityException',
                ),
                array(
                    'reason' => 'The request processing has failed because of an unknown error, exception or failure.',
                    'class' => 'ServiceFailureException',
                ),
            ),
        ),
        'ListOpenIDConnectProviders' => array(
            'httpMethod' => 'POST',
            'uri' => '/',
            'class' => 'Aws\\Common\\Command\\QueryCommand',
            'responseClass' => 'ListOpenIDConnectProvidersResponse',
            'responseType' => 'model',
            'parameters' => array(
                'Action' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => 'ListOpenIDConnectProviders',
                ),
                'Version' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => '2010-05-08',
                ),
            ),
            'errorResponses' => array(
                array(
                    'reason' => 'The request processing has failed because of an unknown error, exception or failure.',
                    'class' => 'ServiceFailureException',
                ),
            ),
        ),
        'ListPolicies' => array(
            'httpMethod' => 'POST',
            'uri' => '/',
            'class' => 'Aws\\Common\\Command\\QueryCommand',
            'responseClass' => 'ListPoliciesResponse',
            'responseType' => 'model',
            'parameters' => array(
                'Action' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => 'ListPolicies',
                ),
                'Version' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => '2010-05-08',
                ),
                'Scope' => array(
                    'type' => 'string',
                    'location' => 'aws.query',
                ),
                'OnlyAttached' => array(
                    'type' => 'boolean',
                    'format' => 'boolean-string',
                    'location' => 'aws.query',
                ),
                'PathPrefix' => array(
                    'type' => 'string',
                    'location' => 'aws.query',
                ),
                'Marker' => array(
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 1,
                ),
                'MaxItems' => array(
                    'type' => 'numeric',
                    'location' => 'aws.query',
                    'minimum' => 1,
                    'maximum' => 1000,
                ),
            ),
            'errorResponses' => array(
                array(
                    'reason' => 'The request processing has failed because of an unknown error, exception or failure.',
                    'class' => 'ServiceFailureException',
                ),
            ),
        ),
        'ListPolicyVersions' => array(
            'httpMethod' => 'POST',
            'uri' => '/',
            'class' => 'Aws\\Common\\Command\\QueryCommand',
            'responseClass' => 'ListPolicyVersionsResponse',
            'responseType' => 'model',
            'parameters' => array(
                'Action' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => 'ListPolicyVersions',
                ),
                'Version' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => '2010-05-08',
                ),
                'PolicyArn' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 20,
                ),
                'Marker' => array(
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 1,
                ),
                'MaxItems' => array(
                    'type' => 'numeric',
                    'location' => 'aws.query',
                    'minimum' => 1,
                    'maximum' => 1000,
                ),
            ),
            'errorResponses' => array(
                array(
                    'reason' => 'The request was rejected because it referenced an entity that does not exist. The error message describes the entity.',
                    'class' => 'NoSuchEntityException',
                ),
                array(
                    'reason' => 'The request was rejected because an invalid or out-of-range value was supplied for an input parameter.',
                    'class' => 'InvalidInputException',
                ),
                array(
                    'reason' => 'The request processing has failed because of an unknown error, exception or failure.',
                    'class' => 'ServiceFailureException',
                ),
            ),
        ),
        'ListRolePolicies' => array(
            'httpMethod' => 'POST',
            'uri' => '/',
            'class' => 'Aws\\Common\\Command\\QueryCommand',
            'responseClass' => 'ListRolePoliciesResponse',
            'responseType' => 'model',
            'parameters' => array(
                'Action' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => 'ListRolePolicies',
                ),
                'Version' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => '2010-05-08',
                ),
                'RoleName' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 1,
                ),
                'Marker' => array(
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 1,
                ),
                'MaxItems' => array(
                    'type' => 'numeric',
                    'location' => 'aws.query',
                    'minimum' => 1,
                    'maximum' => 1000,
                ),
            ),
            'errorResponses' => array(
                array(
                    'reason' => 'The request was rejected because it referenced an entity that does not exist. The error message describes the entity.',
                    'class' => 'NoSuchEntityException',
                ),
                array(
                    'reason' => 'The request processing has failed because of an unknown error, exception or failure.',
                    'class' => 'ServiceFailureException',
                ),
            ),
        ),
        'ListRoles' => array(
            'httpMethod' => 'POST',
            'uri' => '/',
            'class' => 'Aws\\Common\\Command\\QueryCommand',
            'responseClass' => 'ListRolesResponse',
            'responseType' => 'model',
            'parameters' => array(
                'Action' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => 'ListRoles',
                ),
                'Version' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => '2010-05-08',
                ),
                'PathPrefix' => array(
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 1,
                ),
                'Marker' => array(
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 1,
                ),
                'MaxItems' => array(
                    'type' => 'numeric',
                    'location' => 'aws.query',
                    'minimum' => 1,
                    'maximum' => 1000,
                ),
            ),
            'errorResponses' => array(
                array(
                    'reason' => 'The request processing has failed because of an unknown error, exception or failure.',
                    'class' => 'ServiceFailureException',
                ),
            ),
        ),
        'ListSAMLProviders' => array(
            'httpMethod' => 'POST',
            'uri' => '/',
            'class' => 'Aws\\Common\\Command\\QueryCommand',
            'responseClass' => 'ListSAMLProvidersResponse',
            'responseType' => 'model',
            'parameters' => array(
                'Action' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => 'ListSAMLProviders',
                ),
                'Version' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => '2010-05-08',
                ),
            ),
            'errorResponses' => array(
                array(
                    'reason' => 'The request processing has failed because of an unknown error, exception or failure.',
                    'class' => 'ServiceFailureException',
                ),
            ),
        ),
        'ListSSHPublicKeys' => array(
            'httpMethod' => 'POST',
            'uri' => '/',
            'class' => 'Aws\\Common\\Command\\QueryCommand',
            'responseClass' => 'ListSSHPublicKeysResponse',
            'responseType' => 'model',
            'parameters' => array(
                'Action' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => 'ListSSHPublicKeys',
                ),
                'Version' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => '2010-05-08',
                ),
                'UserName' => array(
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 1,
                ),
                'Marker' => array(
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 1,
                ),
                'MaxItems' => array(
                    'type' => 'numeric',
                    'location' => 'aws.query',
                    'minimum' => 1,
                    'maximum' => 1000,
                ),
            ),
            'errorResponses' => array(
                array(
                    'reason' => 'The request was rejected because it referenced an entity that does not exist. The error message describes the entity.',
                    'class' => 'NoSuchEntityException',
                ),
            ),
        ),
        'ListServerCertificates' => array(
            'httpMethod' => 'POST',
            'uri' => '/',
            'class' => 'Aws\\Common\\Command\\QueryCommand',
            'responseClass' => 'ListServerCertificatesResponse',
            'responseType' => 'model',
            'parameters' => array(
                'Action' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => 'ListServerCertificates',
                ),
                'Version' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => '2010-05-08',
                ),
                'PathPrefix' => array(
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 1,
                ),
                'Marker' => array(
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 1,
                ),
                'MaxItems' => array(
                    'type' => 'numeric',
                    'location' => 'aws.query',
                    'minimum' => 1,
                    'maximum' => 1000,
                ),
            ),
            'errorResponses' => array(
                array(
                    'reason' => 'The request processing has failed because of an unknown error, exception or failure.',
                    'class' => 'ServiceFailureException',
                ),
            ),
        ),
        'ListSigningCertificates' => array(
            'httpMethod' => 'POST',
            'uri' => '/',
            'class' => 'Aws\\Common\\Command\\QueryCommand',
            'responseClass' => 'ListSigningCertificatesResponse',
            'responseType' => 'model',
            'parameters' => array(
                'Action' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => 'ListSigningCertificates',
                ),
                'Version' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => '2010-05-08',
                ),
                'UserName' => array(
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 1,
                ),
                'Marker' => array(
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 1,
                ),
                'MaxItems' => array(
                    'type' => 'numeric',
                    'location' => 'aws.query',
                    'minimum' => 1,
                    'maximum' => 1000,
                ),
            ),
            'errorResponses' => array(
                array(
                    'reason' => 'The request was rejected because it referenced an entity that does not exist. The error message describes the entity.',
                    'class' => 'NoSuchEntityException',
                ),
                array(
                    'reason' => 'The request processing has failed because of an unknown error, exception or failure.',
                    'class' => 'ServiceFailureException',
                ),
            ),
        ),
        'ListUserPolicies' => array(
            'httpMethod' => 'POST',
            'uri' => '/',
            'class' => 'Aws\\Common\\Command\\QueryCommand',
            'responseClass' => 'ListUserPoliciesResponse',
            'responseType' => 'model',
            'parameters' => array(
                'Action' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => 'ListUserPolicies',
                ),
                'Version' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => '2010-05-08',
                ),
                'UserName' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 1,
                ),
                'Marker' => array(
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 1,
                ),
                'MaxItems' => array(
                    'type' => 'numeric',
                    'location' => 'aws.query',
                    'minimum' => 1,
                    'maximum' => 1000,
                ),
            ),
            'errorResponses' => array(
                array(
                    'reason' => 'The request was rejected because it referenced an entity that does not exist. The error message describes the entity.',
                    'class' => 'NoSuchEntityException',
                ),
                array(
                    'reason' => 'The request processing has failed because of an unknown error, exception or failure.',
                    'class' => 'ServiceFailureException',
                ),
            ),
        ),
        'ListUsers' => array(
            'httpMethod' => 'POST',
            'uri' => '/',
            'class' => 'Aws\\Common\\Command\\QueryCommand',
            'responseClass' => 'ListUsersResponse',
            'responseType' => 'model',
            'parameters' => array(
                'Action' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => 'ListUsers',
                ),
                'Version' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => '2010-05-08',
                ),
                'PathPrefix' => array(
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 1,
                ),
                'Marker' => array(
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 1,
                ),
                'MaxItems' => array(
                    'type' => 'numeric',
                    'location' => 'aws.query',
                    'minimum' => 1,
                    'maximum' => 1000,
                ),
            ),
            'errorResponses' => array(
                array(
                    'reason' => 'The request processing has failed because of an unknown error, exception or failure.',
                    'class' => 'ServiceFailureException',
                ),
            ),
        ),
        'ListVirtualMFADevices' => array(
            'httpMethod' => 'POST',
            'uri' => '/',
            'class' => 'Aws\\Common\\Command\\QueryCommand',
            'responseClass' => 'ListVirtualMFADevicesResponse',
            'responseType' => 'model',
            'parameters' => array(
                'Action' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => 'ListVirtualMFADevices',
                ),
                'Version' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => '2010-05-08',
                ),
                'AssignmentStatus' => array(
                    'type' => 'string',
                    'location' => 'aws.query',
                ),
                'Marker' => array(
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 1,
                ),
                'MaxItems' => array(
                    'type' => 'numeric',
                    'location' => 'aws.query',
                    'minimum' => 1,
                    'maximum' => 1000,
                ),
            ),
        ),
        'PutGroupPolicy' => array(
            'httpMethod' => 'POST',
            'uri' => '/',
            'class' => 'Aws\\Common\\Command\\QueryCommand',
            'responseClass' => 'EmptyOutput',
            'responseType' => 'model',
            'parameters' => array(
                'Action' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => 'PutGroupPolicy',
                ),
                'Version' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => '2010-05-08',
                ),
                'GroupName' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 1,
                ),
                'PolicyName' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 1,
                ),
                'PolicyDocument' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 1,
                ),
            ),
            'errorResponses' => array(
                array(
                    'reason' => 'The request was rejected because it attempted to create resources beyond the current AWS account limits. The error message describes the limit exceeded.',
                    'class' => 'LimitExceededException',
                ),
                array(
                    'reason' => 'The request was rejected because the policy document was malformed. The error message describes the specific error.',
                    'class' => 'MalformedPolicyDocumentException',
                ),
                array(
                    'reason' => 'The request was rejected because it referenced an entity that does not exist. The error message describes the entity.',
                    'class' => 'NoSuchEntityException',
                ),
                array(
                    'reason' => 'The request processing has failed because of an unknown error, exception or failure.',
                    'class' => 'ServiceFailureException',
                ),
            ),
        ),
        'PutRolePolicy' => array(
            'httpMethod' => 'POST',
            'uri' => '/',
            'class' => 'Aws\\Common\\Command\\QueryCommand',
            'responseClass' => 'EmptyOutput',
            'responseType' => 'model',
            'parameters' => array(
                'Action' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => 'PutRolePolicy',
                ),
                'Version' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => '2010-05-08',
                ),
                'RoleName' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 1,
                ),
                'PolicyName' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 1,
                ),
                'PolicyDocument' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 1,
                ),
            ),
            'errorResponses' => array(
                array(
                    'reason' => 'The request was rejected because it attempted to create resources beyond the current AWS account limits. The error message describes the limit exceeded.',
                    'class' => 'LimitExceededException',
                ),
                array(
                    'reason' => 'The request was rejected because the policy document was malformed. The error message describes the specific error.',
                    'class' => 'MalformedPolicyDocumentException',
                ),
                array(
                    'reason' => 'The request was rejected because it referenced an entity that does not exist. The error message describes the entity.',
                    'class' => 'NoSuchEntityException',
                ),
                array(
                    'reason' => 'The request processing has failed because of an unknown error, exception or failure.',
                    'class' => 'ServiceFailureException',
                ),
            ),
        ),
        'PutUserPolicy' => array(
            'httpMethod' => 'POST',
            'uri' => '/',
            'class' => 'Aws\\Common\\Command\\QueryCommand',
            'responseClass' => 'EmptyOutput',
            'responseType' => 'model',
            'parameters' => array(
                'Action' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => 'PutUserPolicy',
                ),
                'Version' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => '2010-05-08',
                ),
                'UserName' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 1,
                ),
                'PolicyName' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 1,
                ),
                'PolicyDocument' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 1,
                ),
            ),
            'errorResponses' => array(
                array(
                    'reason' => 'The request was rejected because it attempted to create resources beyond the current AWS account limits. The error message describes the limit exceeded.',
                    'class' => 'LimitExceededException',
                ),
                array(
                    'reason' => 'The request was rejected because the policy document was malformed. The error message describes the specific error.',
                    'class' => 'MalformedPolicyDocumentException',
                ),
                array(
                    'reason' => 'The request was rejected because it referenced an entity that does not exist. The error message describes the entity.',
                    'class' => 'NoSuchEntityException',
                ),
                array(
                    'reason' => 'The request processing has failed because of an unknown error, exception or failure.',
                    'class' => 'ServiceFailureException',
                ),
            ),
        ),
        'RemoveClientIDFromOpenIDConnectProvider' => array(
            'httpMethod' => 'POST',
            'uri' => '/',
            'class' => 'Aws\\Common\\Command\\QueryCommand',
            'responseClass' => 'EmptyOutput',
            'responseType' => 'model',
            'parameters' => array(
                'Action' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => 'RemoveClientIDFromOpenIDConnectProvider',
                ),
                'Version' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => '2010-05-08',
                ),
                'OpenIDConnectProviderArn' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 20,
                ),
                'ClientID' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 1,
                ),
            ),
            'errorResponses' => array(
                array(
                    'reason' => 'The request was rejected because an invalid or out-of-range value was supplied for an input parameter.',
                    'class' => 'InvalidInputException',
                ),
                array(
                    'reason' => 'The request was rejected because it referenced an entity that does not exist. The error message describes the entity.',
                    'class' => 'NoSuchEntityException',
                ),
                array(
                    'reason' => 'The request processing has failed because of an unknown error, exception or failure.',
                    'class' => 'ServiceFailureException',
                ),
            ),
        ),
        'RemoveRoleFromInstanceProfile' => array(
            'httpMethod' => 'POST',
            'uri' => '/',
            'class' => 'Aws\\Common\\Command\\QueryCommand',
            'responseClass' => 'EmptyOutput',
            'responseType' => 'model',
            'parameters' => array(
                'Action' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => 'RemoveRoleFromInstanceProfile',
                ),
                'Version' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => '2010-05-08',
                ),
                'InstanceProfileName' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 1,
                ),
                'RoleName' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 1,
                ),
            ),
            'errorResponses' => array(
                array(
                    'reason' => 'The request was rejected because it referenced an entity that does not exist. The error message describes the entity.',
                    'class' => 'NoSuchEntityException',
                ),
                array(
                    'reason' => 'The request was rejected because it attempted to create resources beyond the current AWS account limits. The error message describes the limit exceeded.',
                    'class' => 'LimitExceededException',
                ),
                array(
                    'reason' => 'The request processing has failed because of an unknown error, exception or failure.',
                    'class' => 'ServiceFailureException',
                ),
            ),
        ),
        'RemoveUserFromGroup' => array(
            'httpMethod' => 'POST',
            'uri' => '/',
            'class' => 'Aws\\Common\\Command\\QueryCommand',
            'responseClass' => 'EmptyOutput',
            'responseType' => 'model',
            'parameters' => array(
                'Action' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => 'RemoveUserFromGroup',
                ),
                'Version' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => '2010-05-08',
                ),
                'GroupName' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 1,
                ),
                'UserName' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 1,
                ),
            ),
            'errorResponses' => array(
                array(
                    'reason' => 'The request was rejected because it referenced an entity that does not exist. The error message describes the entity.',
                    'class' => 'NoSuchEntityException',
                ),
                array(
                    'reason' => 'The request was rejected because it attempted to create resources beyond the current AWS account limits. The error message describes the limit exceeded.',
                    'class' => 'LimitExceededException',
                ),
                array(
                    'reason' => 'The request processing has failed because of an unknown error, exception or failure.',
                    'class' => 'ServiceFailureException',
                ),
            ),
        ),
        'ResyncMFADevice' => array(
            'httpMethod' => 'POST',
            'uri' => '/',
            'class' => 'Aws\\Common\\Command\\QueryCommand',
            'responseClass' => 'EmptyOutput',
            'responseType' => 'model',
            'parameters' => array(
                'Action' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => 'ResyncMFADevice',
                ),
                'Version' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => '2010-05-08',
                ),
                'UserName' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 1,
                ),
                'SerialNumber' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 9,
                ),
                'AuthenticationCode1' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 6,
                ),
                'AuthenticationCode2' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 6,
                ),
            ),
            'errorResponses' => array(
                array(
                    'reason' => 'The request was rejected because the authentication code was not recognized. The error message describes the specific error.',
                    'class' => 'InvalidAuthenticationCodeException',
                ),
                array(
                    'reason' => 'The request was rejected because it referenced an entity that does not exist. The error message describes the entity.',
                    'class' => 'NoSuchEntityException',
                ),
                array(
                    'reason' => 'The request was rejected because it attempted to create resources beyond the current AWS account limits. The error message describes the limit exceeded.',
                    'class' => 'LimitExceededException',
                ),
                array(
                    'reason' => 'The request processing has failed because of an unknown error, exception or failure.',
                    'class' => 'ServiceFailureException',
                ),
            ),
        ),
        'SetDefaultPolicyVersion' => array(
            'httpMethod' => 'POST',
            'uri' => '/',
            'class' => 'Aws\\Common\\Command\\QueryCommand',
            'responseClass' => 'EmptyOutput',
            'responseType' => 'model',
            'parameters' => array(
                'Action' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => 'SetDefaultPolicyVersion',
                ),
                'Version' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => '2010-05-08',
                ),
                'PolicyArn' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 20,
                ),
                'VersionId' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'aws.query',
                ),
            ),
            'errorResponses' => array(
                array(
                    'reason' => 'The request was rejected because it referenced an entity that does not exist. The error message describes the entity.',
                    'class' => 'NoSuchEntityException',
                ),
                array(
                    'reason' => 'The request was rejected because an invalid or out-of-range value was supplied for an input parameter.',
                    'class' => 'InvalidInputException',
                ),
                array(
                    'reason' => 'The request was rejected because it attempted to create resources beyond the current AWS account limits. The error message describes the limit exceeded.',
                    'class' => 'LimitExceededException',
                ),
                array(
                    'reason' => 'The request processing has failed because of an unknown error, exception or failure.',
                    'class' => 'ServiceFailureException',
                ),
            ),
        ),
        'SimulateCustomPolicy' => array(
            'httpMethod' => 'POST',
            'uri' => '/',
            'class' => 'Aws\\Common\\Command\\QueryCommand',
            'responseClass' => 'SimulatePolicyResponse',
            'responseType' => 'model',
            'parameters' => array(
                'Action' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => 'SimulateCustomPolicy',
                ),
                'Version' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => '2010-05-08',
                ),
                'PolicyInputList' => array(
                    'required' => true,
                    'type' => 'array',
                    'location' => 'aws.query',
                    'sentAs' => 'PolicyInputList.member',
                    'items' => array(
                        'name' => 'policyDocumentType',
                        'type' => 'string',
                        'minLength' => 1,
                    ),
                ),
                'ActionNames' => array(
                    'required' => true,
                    'type' => 'array',
                    'location' => 'aws.query',
                    'sentAs' => 'ActionNames.member',
                    'items' => array(
                        'name' => 'ActionNameType',
                        'type' => 'string',
                        'minLength' => 3,
                    ),
                ),
                'ResourceArns' => array(
                    'type' => 'array',
                    'location' => 'aws.query',
                    'sentAs' => 'ResourceArns.member',
                    'items' => array(
                        'name' => 'ResourceNameType',
                        'type' => 'string',
                        'minLength' => 1,
                    ),
                ),
                'ResourcePolicy' => array(
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 1,
                ),
                'ResourceOwner' => array(
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 1,
                ),
                'CallerArn' => array(
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 1,
                ),
                'ContextEntries' => array(
                    'type' => 'array',
                    'location' => 'aws.query',
                    'sentAs' => 'ContextEntries.member',
                    'items' => array(
                        'name' => 'ContextEntry',
                        'type' => 'object',
                        'properties' => array(
                            'ContextKeyName' => array(
                                'type' => 'string',
                                'minLength' => 5,
                            ),
                            'ContextKeyValues' => array(
                                'type' => 'array',
                                'sentAs' => 'ContextKeyValues.member',
                                'items' => array(
                                    'name' => 'ContextKeyValueType',
                                    'type' => 'string',
                                ),
                            ),
                            'ContextKeyType' => array(
                                'type' => 'string',
                            ),
                        ),
                    ),
                ),
                'ResourceHandlingOption' => array(
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 1,
                ),
                'MaxItems' => array(
                    'type' => 'numeric',
                    'location' => 'aws.query',
                    'minimum' => 1,
                    'maximum' => 1000,
                ),
                'Marker' => array(
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 1,
                ),
            ),
            'errorResponses' => array(
                array(
                    'reason' => 'The request was rejected because an invalid or out-of-range value was supplied for an input parameter.',
                    'class' => 'InvalidInputException',
                ),
                array(
                    'reason' => 'The request failed because a provided policy could not be successfully evaluated. An additional detail message indicates the source of the failure.',
                    'class' => 'PolicyEvaluationException',
                ),
            ),
        ),
        'SimulatePrincipalPolicy' => array(
            'httpMethod' => 'POST',
            'uri' => '/',
            'class' => 'Aws\\Common\\Command\\QueryCommand',
            'responseClass' => 'SimulatePolicyResponse',
            'responseType' => 'model',
            'parameters' => array(
                'Action' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => 'SimulatePrincipalPolicy',
                ),
                'Version' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => '2010-05-08',
                ),
                'PolicySourceArn' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 20,
                ),
                'PolicyInputList' => array(
                    'type' => 'array',
                    'location' => 'aws.query',
                    'sentAs' => 'PolicyInputList.member',
                    'items' => array(
                        'name' => 'policyDocumentType',
                        'type' => 'string',
                        'minLength' => 1,
                    ),
                ),
                'ActionNames' => array(
                    'required' => true,
                    'type' => 'array',
                    'location' => 'aws.query',
                    'sentAs' => 'ActionNames.member',
                    'items' => array(
                        'name' => 'ActionNameType',
                        'type' => 'string',
                        'minLength' => 3,
                    ),
                ),
                'ResourceArns' => array(
                    'type' => 'array',
                    'location' => 'aws.query',
                    'sentAs' => 'ResourceArns.member',
                    'items' => array(
                        'name' => 'ResourceNameType',
                        'type' => 'string',
                        'minLength' => 1,
                    ),
                ),
                'ResourcePolicy' => array(
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 1,
                ),
                'ResourceOwner' => array(
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 1,
                ),
                'CallerArn' => array(
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 1,
                ),
                'ContextEntries' => array(
                    'type' => 'array',
                    'location' => 'aws.query',
                    'sentAs' => 'ContextEntries.member',
                    'items' => array(
                        'name' => 'ContextEntry',
                        'type' => 'object',
                        'properties' => array(
                            'ContextKeyName' => array(
                                'type' => 'string',
                                'minLength' => 5,
                            ),
                            'ContextKeyValues' => array(
                                'type' => 'array',
                                'sentAs' => 'ContextKeyValues.member',
                                'items' => array(
                                    'name' => 'ContextKeyValueType',
                                    'type' => 'string',
                                ),
                            ),
                            'ContextKeyType' => array(
                                'type' => 'string',
                            ),
                        ),
                    ),
                ),
                'ResourceHandlingOption' => array(
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 1,
                ),
                'MaxItems' => array(
                    'type' => 'numeric',
                    'location' => 'aws.query',
                    'minimum' => 1,
                    'maximum' => 1000,
                ),
                'Marker' => array(
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 1,
                ),
            ),
            'errorResponses' => array(
                array(
                    'reason' => 'The request was rejected because it referenced an entity that does not exist. The error message describes the entity.',
                    'class' => 'NoSuchEntityException',
                ),
                array(
                    'reason' => 'The request was rejected because an invalid or out-of-range value was supplied for an input parameter.',
                    'class' => 'InvalidInputException',
                ),
                array(
                    'reason' => 'The request failed because a provided policy could not be successfully evaluated. An additional detail message indicates the source of the failure.',
                    'class' => 'PolicyEvaluationException',
                ),
            ),
        ),
        'UpdateAccessKey' => array(
            'httpMethod' => 'POST',
            'uri' => '/',
            'class' => 'Aws\\Common\\Command\\QueryCommand',
            'responseClass' => 'EmptyOutput',
            'responseType' => 'model',
            'parameters' => array(
                'Action' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => 'UpdateAccessKey',
                ),
                'Version' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => '2010-05-08',
                ),
                'UserName' => array(
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 1,
                ),
                'AccessKeyId' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 16,
                ),
                'Status' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'aws.query',
                ),
            ),
            'errorResponses' => array(
                array(
                    'reason' => 'The request was rejected because it referenced an entity that does not exist. The error message describes the entity.',
                    'class' => 'NoSuchEntityException',
                ),
                array(
                    'reason' => 'The request was rejected because it attempted to create resources beyond the current AWS account limits. The error message describes the limit exceeded.',
                    'class' => 'LimitExceededException',
                ),
                array(
                    'reason' => 'The request processing has failed because of an unknown error, exception or failure.',
                    'class' => 'ServiceFailureException',
                ),
            ),
        ),
        'UpdateAccountPasswordPolicy' => array(
            'httpMethod' => 'POST',
            'uri' => '/',
            'class' => 'Aws\\Common\\Command\\QueryCommand',
            'responseClass' => 'EmptyOutput',
            'responseType' => 'model',
            'parameters' => array(
                'Action' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => 'UpdateAccountPasswordPolicy',
                ),
                'Version' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => '2010-05-08',
                ),
                'MinimumPasswordLength' => array(
                    'type' => 'numeric',
                    'location' => 'aws.query',
                    'minimum' => 6,
                    'maximum' => 128,
                ),
                'RequireSymbols' => array(
                    'type' => 'boolean',
                    'format' => 'boolean-string',
                    'location' => 'aws.query',
                ),
                'RequireNumbers' => array(
                    'type' => 'boolean',
                    'format' => 'boolean-string',
                    'location' => 'aws.query',
                ),
                'RequireUppercaseCharacters' => array(
                    'type' => 'boolean',
                    'format' => 'boolean-string',
                    'location' => 'aws.query',
                ),
                'RequireLowercaseCharacters' => array(
                    'type' => 'boolean',
                    'format' => 'boolean-string',
                    'location' => 'aws.query',
                ),
                'AllowUsersToChangePassword' => array(
                    'type' => 'boolean',
                    'format' => 'boolean-string',
                    'location' => 'aws.query',
                ),
                'MaxPasswordAge' => array(
                    'type' => 'numeric',
                    'location' => 'aws.query',
                    'minimum' => 1,
                    'maximum' => 1095,
                ),
                'PasswordReusePrevention' => array(
                    'type' => 'numeric',
                    'location' => 'aws.query',
                    'minimum' => 1,
                    'maximum' => 24,
                ),
                'HardExpiry' => array(
                    'type' => 'boolean',
                    'format' => 'boolean-string',
                    'location' => 'aws.query',
                ),
            ),
            'errorResponses' => array(
                array(
                    'reason' => 'The request was rejected because it referenced an entity that does not exist. The error message describes the entity.',
                    'class' => 'NoSuchEntityException',
                ),
                array(
                    'reason' => 'The request was rejected because the policy document was malformed. The error message describes the specific error.',
                    'class' => 'MalformedPolicyDocumentException',
                ),
                array(
                    'reason' => 'The request was rejected because it attempted to create resources beyond the current AWS account limits. The error message describes the limit exceeded.',
                    'class' => 'LimitExceededException',
                ),
                array(
                    'reason' => 'The request processing has failed because of an unknown error, exception or failure.',
                    'class' => 'ServiceFailureException',
                ),
            ),
        ),
        'UpdateAssumeRolePolicy' => array(
            'httpMethod' => 'POST',
            'uri' => '/',
            'class' => 'Aws\\Common\\Command\\QueryCommand',
            'responseClass' => 'EmptyOutput',
            'responseType' => 'model',
            'parameters' => array(
                'Action' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => 'UpdateAssumeRolePolicy',
                ),
                'Version' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => '2010-05-08',
                ),
                'RoleName' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 1,
                ),
                'PolicyDocument' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 1,
                ),
            ),
            'errorResponses' => array(
                array(
                    'reason' => 'The request was rejected because it referenced an entity that does not exist. The error message describes the entity.',
                    'class' => 'NoSuchEntityException',
                ),
                array(
                    'reason' => 'The request was rejected because the policy document was malformed. The error message describes the specific error.',
                    'class' => 'MalformedPolicyDocumentException',
                ),
                array(
                    'reason' => 'The request was rejected because it attempted to create resources beyond the current AWS account limits. The error message describes the limit exceeded.',
                    'class' => 'LimitExceededException',
                ),
                array(
                    'reason' => 'The request processing has failed because of an unknown error, exception or failure.',
                    'class' => 'ServiceFailureException',
                ),
            ),
        ),
        'UpdateGroup' => array(
            'httpMethod' => 'POST',
            'uri' => '/',
            'class' => 'Aws\\Common\\Command\\QueryCommand',
            'responseClass' => 'EmptyOutput',
            'responseType' => 'model',
            'parameters' => array(
                'Action' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => 'UpdateGroup',
                ),
                'Version' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => '2010-05-08',
                ),
                'GroupName' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 1,
                ),
                'NewPath' => array(
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 1,
                ),
                'NewGroupName' => array(
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 1,
                ),
            ),
            'errorResponses' => array(
                array(
                    'reason' => 'The request was rejected because it referenced an entity that does not exist. The error message describes the entity.',
                    'class' => 'NoSuchEntityException',
                ),
                array(
                    'reason' => 'The request was rejected because it attempted to create a resource that already exists.',
                    'class' => 'EntityAlreadyExistsException',
                ),
                array(
                    'reason' => 'The request was rejected because it attempted to create resources beyond the current AWS account limits. The error message describes the limit exceeded.',
                    'class' => 'LimitExceededException',
                ),
                array(
                    'reason' => 'The request processing has failed because of an unknown error, exception or failure.',
                    'class' => 'ServiceFailureException',
                ),
            ),
        ),
        'UpdateLoginProfile' => array(
            'httpMethod' => 'POST',
            'uri' => '/',
            'class' => 'Aws\\Common\\Command\\QueryCommand',
            'responseClass' => 'EmptyOutput',
            'responseType' => 'model',
            'parameters' => array(
                'Action' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => 'UpdateLoginProfile',
                ),
                'Version' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => '2010-05-08',
                ),
                'UserName' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 1,
                ),
                'Password' => array(
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 1,
                ),
                'PasswordResetRequired' => array(
                    'type' => 'boolean',
                    'format' => 'boolean-string',
                    'location' => 'aws.query',
                ),
            ),
            'errorResponses' => array(
                array(
                    'reason' => 'The request was rejected because it referenced an entity that is temporarily unmodifiable, such as a user name that was deleted and then recreated. The error indicates that the request is likely to succeed if you try again after waiting several minutes. The error message describes the entity.',
                    'class' => 'EntityTemporarilyUnmodifiableException',
                ),
                array(
                    'reason' => 'The request was rejected because it referenced an entity that does not exist. The error message describes the entity.',
                    'class' => 'NoSuchEntityException',
                ),
                array(
                    'reason' => 'The request was rejected because the provided password did not meet the requirements imposed by the account password policy.',
                    'class' => 'PasswordPolicyViolationException',
                ),
                array(
                    'reason' => 'The request was rejected because it attempted to create resources beyond the current AWS account limits. The error message describes the limit exceeded.',
                    'class' => 'LimitExceededException',
                ),
                array(
                    'reason' => 'The request processing has failed because of an unknown error, exception or failure.',
                    'class' => 'ServiceFailureException',
                ),
            ),
        ),
        'UpdateOpenIDConnectProviderThumbprint' => array(
            'httpMethod' => 'POST',
            'uri' => '/',
            'class' => 'Aws\\Common\\Command\\QueryCommand',
            'responseClass' => 'EmptyOutput',
            'responseType' => 'model',
            'parameters' => array(
                'Action' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => 'UpdateOpenIDConnectProviderThumbprint',
                ),
                'Version' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => '2010-05-08',
                ),
                'OpenIDConnectProviderArn' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 20,
                ),
                'ThumbprintList' => array(
                    'required' => true,
                    'type' => 'array',
                    'location' => 'aws.query',
                    'sentAs' => 'ThumbprintList.member',
                    'items' => array(
                        'name' => 'thumbprintType',
                        'type' => 'string',
                        'minLength' => 40,
                    ),
                ),
            ),
            'errorResponses' => array(
                array(
                    'reason' => 'The request was rejected because an invalid or out-of-range value was supplied for an input parameter.',
                    'class' => 'InvalidInputException',
                ),
                array(
                    'reason' => 'The request was rejected because it referenced an entity that does not exist. The error message describes the entity.',
                    'class' => 'NoSuchEntityException',
                ),
                array(
                    'reason' => 'The request processing has failed because of an unknown error, exception or failure.',
                    'class' => 'ServiceFailureException',
                ),
            ),
        ),
        'UpdateSAMLProvider' => array(
            'httpMethod' => 'POST',
            'uri' => '/',
            'class' => 'Aws\\Common\\Command\\QueryCommand',
            'responseClass' => 'UpdateSAMLProviderResponse',
            'responseType' => 'model',
            'parameters' => array(
                'Action' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => 'UpdateSAMLProvider',
                ),
                'Version' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => '2010-05-08',
                ),
                'SAMLMetadataDocument' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 1000,
                ),
                'SAMLProviderArn' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 20,
                ),
            ),
            'errorResponses' => array(
                array(
                    'reason' => 'The request was rejected because it referenced an entity that does not exist. The error message describes the entity.',
                    'class' => 'NoSuchEntityException',
                ),
                array(
                    'reason' => 'The request was rejected because an invalid or out-of-range value was supplied for an input parameter.',
                    'class' => 'InvalidInputException',
                ),
                array(
                    'reason' => 'The request was rejected because it attempted to create resources beyond the current AWS account limits. The error message describes the limit exceeded.',
                    'class' => 'LimitExceededException',
                ),
                array(
                    'reason' => 'The request processing has failed because of an unknown error, exception or failure.',
                    'class' => 'ServiceFailureException',
                ),
            ),
        ),
        'UpdateSSHPublicKey' => array(
            'httpMethod' => 'POST',
            'uri' => '/',
            'class' => 'Aws\\Common\\Command\\QueryCommand',
            'responseClass' => 'EmptyOutput',
            'responseType' => 'model',
            'parameters' => array(
                'Action' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => 'UpdateSSHPublicKey',
                ),
                'Version' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => '2010-05-08',
                ),
                'UserName' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 1,
                ),
                'SSHPublicKeyId' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 20,
                ),
                'Status' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'aws.query',
                ),
            ),
            'errorResponses' => array(
                array(
                    'reason' => 'The request was rejected because it referenced an entity that does not exist. The error message describes the entity.',
                    'class' => 'NoSuchEntityException',
                ),
            ),
        ),
        'UpdateServerCertificate' => array(
            'httpMethod' => 'POST',
            'uri' => '/',
            'class' => 'Aws\\Common\\Command\\QueryCommand',
            'responseClass' => 'EmptyOutput',
            'responseType' => 'model',
            'parameters' => array(
                'Action' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => 'UpdateServerCertificate',
                ),
                'Version' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => '2010-05-08',
                ),
                'ServerCertificateName' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 1,
                ),
                'NewPath' => array(
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 1,
                ),
                'NewServerCertificateName' => array(
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 1,
                ),
            ),
            'errorResponses' => array(
                array(
                    'reason' => 'The request was rejected because it referenced an entity that does not exist. The error message describes the entity.',
                    'class' => 'NoSuchEntityException',
                ),
                array(
                    'reason' => 'The request was rejected because it attempted to create a resource that already exists.',
                    'class' => 'EntityAlreadyExistsException',
                ),
                array(
                    'reason' => 'The request was rejected because it attempted to create resources beyond the current AWS account limits. The error message describes the limit exceeded.',
                    'class' => 'LimitExceededException',
                ),
                array(
                    'reason' => 'The request processing has failed because of an unknown error, exception or failure.',
                    'class' => 'ServiceFailureException',
                ),
            ),
        ),
        'UpdateSigningCertificate' => array(
            'httpMethod' => 'POST',
            'uri' => '/',
            'class' => 'Aws\\Common\\Command\\QueryCommand',
            'responseClass' => 'EmptyOutput',
            'responseType' => 'model',
            'parameters' => array(
                'Action' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => 'UpdateSigningCertificate',
                ),
                'Version' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => '2010-05-08',
                ),
                'UserName' => array(
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 1,
                ),
                'CertificateId' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 24,
                ),
                'Status' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'aws.query',
                ),
            ),
            'errorResponses' => array(
                array(
                    'reason' => 'The request was rejected because it referenced an entity that does not exist. The error message describes the entity.',
                    'class' => 'NoSuchEntityException',
                ),
                array(
                    'reason' => 'The request was rejected because it attempted to create resources beyond the current AWS account limits. The error message describes the limit exceeded.',
                    'class' => 'LimitExceededException',
                ),
                array(
                    'reason' => 'The request processing has failed because of an unknown error, exception or failure.',
                    'class' => 'ServiceFailureException',
                ),
            ),
        ),
        'UpdateUser' => array(
            'httpMethod' => 'POST',
            'uri' => '/',
            'class' => 'Aws\\Common\\Command\\QueryCommand',
            'responseClass' => 'EmptyOutput',
            'responseType' => 'model',
            'parameters' => array(
                'Action' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => 'UpdateUser',
                ),
                'Version' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => '2010-05-08',
                ),
                'UserName' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 1,
                ),
                'NewPath' => array(
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 1,
                ),
                'NewUserName' => array(
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 1,
                ),
            ),
            'errorResponses' => array(
                array(
                    'reason' => 'The request was rejected because it referenced an entity that does not exist. The error message describes the entity.',
                    'class' => 'NoSuchEntityException',
                ),
                array(
                    'reason' => 'The request was rejected because it attempted to create resources beyond the current AWS account limits. The error message describes the limit exceeded.',
                    'class' => 'LimitExceededException',
                ),
                array(
                    'reason' => 'The request was rejected because it attempted to create a resource that already exists.',
                    'class' => 'EntityAlreadyExistsException',
                ),
                array(
                    'reason' => 'The request was rejected because it referenced an entity that is temporarily unmodifiable, such as a user name that was deleted and then recreated. The error indicates that the request is likely to succeed if you try again after waiting several minutes. The error message describes the entity.',
                    'class' => 'EntityTemporarilyUnmodifiableException',
                ),
                array(
                    'reason' => 'The request processing has failed because of an unknown error, exception or failure.',
                    'class' => 'ServiceFailureException',
                ),
            ),
        ),
        'UploadSSHPublicKey' => array(
            'httpMethod' => 'POST',
            'uri' => '/',
            'class' => 'Aws\\Common\\Command\\QueryCommand',
            'responseClass' => 'UploadSSHPublicKeyResponse',
            'responseType' => 'model',
            'parameters' => array(
                'Action' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => 'UploadSSHPublicKey',
                ),
                'Version' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => '2010-05-08',
                ),
                'UserName' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 1,
                ),
                'SSHPublicKeyBody' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 1,
                ),
            ),
            'errorResponses' => array(
                array(
                    'reason' => 'The request was rejected because it attempted to create resources beyond the current AWS account limits. The error message describes the limit exceeded.',
                    'class' => 'LimitExceededException',
                ),
                array(
                    'reason' => 'The request was rejected because it referenced an entity that does not exist. The error message describes the entity.',
                    'class' => 'NoSuchEntityException',
                ),
                array(
                    'reason' => 'The request was rejected because the public key is malformed or otherwise invalid.',
                    'class' => 'InvalidPublicKeyException',
                ),
                array(
                    'reason' => 'The request was rejected because the SSH public key is already associated with the specified IAM user.',
                    'class' => 'DuplicateSSHPublicKeyException',
                ),
                array(
                    'reason' => 'The request was rejected because the public key encoding format is unsupported or unrecognized.',
                    'class' => 'UnrecognizedPublicKeyEncodingException',
                ),
            ),
        ),
        'UploadServerCertificate' => array(
            'httpMethod' => 'POST',
            'uri' => '/',
            'class' => 'Aws\\Common\\Command\\QueryCommand',
            'responseClass' => 'UploadServerCertificateResponse',
            'responseType' => 'model',
            'parameters' => array(
                'Action' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => 'UploadServerCertificate',
                ),
                'Version' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => '2010-05-08',
                ),
                'Path' => array(
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 1,
                ),
                'ServerCertificateName' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 1,
                ),
                'CertificateBody' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 1,
                ),
                'PrivateKey' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 1,
                ),
                'CertificateChain' => array(
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 1,
                ),
            ),
            'errorResponses' => array(
                array(
                    'reason' => 'The request was rejected because it attempted to create resources beyond the current AWS account limits. The error message describes the limit exceeded.',
                    'class' => 'LimitExceededException',
                ),
                array(
                    'reason' => 'The request was rejected because it attempted to create a resource that already exists.',
                    'class' => 'EntityAlreadyExistsException',
                ),
                array(
                    'reason' => 'The request was rejected because the certificate was malformed or expired. The error message describes the specific error.',
                    'class' => 'MalformedCertificateException',
                ),
                array(
                    'reason' => 'The request was rejected because the public key certificate and the private key do not match.',
                    'class' => 'KeyPairMismatchException',
                ),
                array(
                    'reason' => 'The request processing has failed because of an unknown error, exception or failure.',
                    'class' => 'ServiceFailureException',
                ),
            ),
        ),
        'UploadSigningCertificate' => array(
            'httpMethod' => 'POST',
            'uri' => '/',
            'class' => 'Aws\\Common\\Command\\QueryCommand',
            'responseClass' => 'UploadSigningCertificateResponse',
            'responseType' => 'model',
            'parameters' => array(
                'Action' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => 'UploadSigningCertificate',
                ),
                'Version' => array(
                    'static' => true,
                    'location' => 'aws.query',
                    'default' => '2010-05-08',
                ),
                'UserName' => array(
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 1,
                ),
                'CertificateBody' => array(
                    'required' => true,
                    'type' => 'string',
                    'location' => 'aws.query',
                    'minLength' => 1,
                ),
            ),
            'errorResponses' => array(
                array(
                    'reason' => 'The request was rejected because it attempted to create resources beyond the current AWS account limits. The error message describes the limit exceeded.',
                    'class' => 'LimitExceededException',
                ),
                array(
                    'reason' => 'The request was rejected because it attempted to create a resource that already exists.',
                    'class' => 'EntityAlreadyExistsException',
                ),
                array(
                    'reason' => 'The request was rejected because the certificate was malformed or expired. The error message describes the specific error.',
                    'class' => 'MalformedCertificateException',
                ),
                array(
                    'reason' => 'The request was rejected because the certificate is invalid.',
                    'class' => 'InvalidCertificateException',
                ),
                array(
                    'reason' => 'The request was rejected because the same certificate is associated with an IAM user in the account.',
                    'class' => 'DuplicateCertificateException',
                ),
                array(
                    'reason' => 'The request was rejected because it referenced an entity that does not exist. The error message describes the entity.',
                    'class' => 'NoSuchEntityException',
                ),
                array(
                    'reason' => 'The request processing has failed because of an unknown error, exception or failure.',
                    'class' => 'ServiceFailureException',
                ),
            ),
        ),
    ),
    'models' => array(
        'EmptyOutput' => array(
            'type' => 'object',
            'additionalProperties' => true,
        ),
        'CreateAccessKeyResponse' => array(
            'type' => 'object',
            'additionalProperties' => true,
            'properties' => array(
                'AccessKey' => array(
                    'type' => 'object',
                    'location' => 'xml',
                    'properties' => array(
                        'UserName' => array(
                            'type' => 'string',
                        ),
                        'AccessKeyId' => array(
                            'type' => 'string',
                        ),
                        'Status' => array(
                            'type' => 'string',
                        ),
                        'SecretAccessKey' => array(
                            'type' => 'string',
                        ),
                        'CreateDate' => array(
                            'type' => 'string',
                        ),
                    ),
                ),
            ),
        ),
        'CreateGroupResponse' => array(
            'type' => 'object',
            'additionalProperties' => true,
            'properties' => array(
                'Group' => array(
                    'type' => 'object',
                    'location' => 'xml',
                    'properties' => array(
                        'Path' => array(
                            'type' => 'string',
                        ),
                        'GroupName' => array(
                            'type' => 'string',
                        ),
                        'GroupId' => array(
                            'type' => 'string',
                        ),
                        'Arn' => array(
                            'type' => 'string',
                        ),
                        'CreateDate' => array(
                            'type' => 'string',
                        ),
                    ),
                ),
            ),
        ),
        'CreateInstanceProfileResponse' => array(
            'type' => 'object',
            'additionalProperties' => true,
            'properties' => array(
                'InstanceProfile' => array(
                    'type' => 'object',
                    'location' => 'xml',
                    'properties' => array(
                        'Path' => array(
                            'type' => 'string',
                        ),
                        'InstanceProfileName' => array(
                            'type' => 'string',
                        ),
                        'InstanceProfileId' => array(
                            'type' => 'string',
                        ),
                        'Arn' => array(
                            'type' => 'string',
                        ),
                        'CreateDate' => array(
                            'type' => 'string',
                        ),
                        'Roles' => array(
                            'type' => 'array',
                            'items' => array(
                                'name' => 'Role',
                                'type' => 'object',
                                'sentAs' => 'member',
                                'properties' => array(
                                    'Path' => array(
                                        'type' => 'string',
                                    ),
                                    'RoleName' => array(
                                        'type' => 'string',
                                    ),
                                    'RoleId' => array(
                                        'type' => 'string',
                                    ),
                                    'Arn' => array(
                                        'type' => 'string',
                                    ),
                                    'CreateDate' => array(
                                        'type' => 'string',
                                    ),
                                    'AssumeRolePolicyDocument' => array(
                                        'type' => 'string',
                                    ),
                                ),
                            ),
                        ),
                    ),
                ),
            ),
        ),
        'CreateLoginProfileResponse' => array(
            'type' => 'object',
            'additionalProperties' => true,
            'properties' => array(
                'LoginProfile' => array(
                    'type' => 'object',
                    'location' => 'xml',
                    'properties' => array(
                        'UserName' => array(
                            'type' => 'string',
                        ),
                        'CreateDate' => array(
                            'type' => 'string',
                        ),
                        'PasswordResetRequired' => array(
                            'type' => 'boolean',
                        ),
                    ),
                ),
            ),
        ),
        'CreateOpenIDConnectProviderResponse' => array(
            'type' => 'object',
            'additionalProperties' => true,
            'properties' => array(
                'OpenIDConnectProviderArn' => array(
                    'type' => 'string',
                    'location' => 'xml',
                ),
            ),
        ),
        'CreatePolicyResponse' => array(
            'type' => 'object',
            'additionalProperties' => true,
            'properties' => array(
                'Policy' => array(
                    'type' => 'object',
                    'location' => 'xml',
                    'properties' => array(
                        'PolicyName' => array(
                            'type' => 'string',
                        ),
                        'PolicyId' => array(
                            'type' => 'string',
                        ),
                        'Arn' => array(
                            'type' => 'string',
                        ),
                        'Path' => array(
                            'type' => 'string',
                        ),
                        'DefaultVersionId' => array(
                            'type' => 'string',
                        ),
                        'AttachmentCount' => array(
                            'type' => 'numeric',
                        ),
                        'IsAttachable' => array(
                            'type' => 'boolean',
                        ),
                        'Description' => array(
                            'type' => 'string',
                        ),
                        'CreateDate' => array(
                            'type' => 'string',
                        ),
                        'UpdateDate' => array(
                            'type' => 'string',
                        ),
                    ),
                ),
            ),
        ),
        'CreatePolicyVersionResponse' => array(
            'type' => 'object',
            'additionalProperties' => true,
            'properties' => array(
                'PolicyVersion' => array(
                    'type' => 'object',
                    'location' => 'xml',
                    'properties' => array(
                        'Document' => array(
                            'type' => 'string',
                        ),
                        'VersionId' => array(
                            'type' => 'string',
                        ),
                        'IsDefaultVersion' => array(
                            'type' => 'boolean',
                        ),
                        'CreateDate' => array(
                            'type' => 'string',
                        ),
                    ),
                ),
            ),
        ),
        'CreateRoleResponse' => array(
            'type' => 'object',
            'additionalProperties' => true,
            'properties' => array(
                'Role' => array(
                    'type' => 'object',
                    'location' => 'xml',
                    'properties' => array(
                        'Path' => array(
                            'type' => 'string',
                        ),
                        'RoleName' => array(
                            'type' => 'string',
                        ),
                        'RoleId' => array(
                            'type' => 'string',
                        ),
                        'Arn' => array(
                            'type' => 'string',
                        ),
                        'CreateDate' => array(
                            'type' => 'string',
                        ),
                        'AssumeRolePolicyDocument' => array(
                            'type' => 'string',
                        ),
                    ),
                ),
            ),
        ),
        'CreateSAMLProviderResponse' => array(
            'type' => 'object',
            'additionalProperties' => true,
            'properties' => array(
                'SAMLProviderArn' => array(
                    'type' => 'string',
                    'location' => 'xml',
                ),
            ),
        ),
        'CreateUserResponse' => array(
            'type' => 'object',
            'additionalProperties' => true,
            'properties' => array(
                'User' => array(
                    'type' => 'object',
                    'location' => 'xml',
                    'properties' => array(
                        'Path' => array(
                            'type' => 'string',
                        ),
                        'UserName' => array(
                            'type' => 'string',
                        ),
                        'UserId' => array(
                            'type' => 'string',
                        ),
                        'Arn' => array(
                            'type' => 'string',
                        ),
                        'CreateDate' => array(
                            'type' => 'string',
                        ),
                        'PasswordLastUsed' => array(
                            'type' => 'string',
                        ),
                    ),
                ),
            ),
        ),
        'CreateVirtualMFADeviceResponse' => array(
            'type' => 'object',
            'additionalProperties' => true,
            'properties' => array(
                'VirtualMFADevice' => array(
                    'type' => 'object',
                    'location' => 'xml',
                    'properties' => array(
                        'SerialNumber' => array(
                            'type' => 'string',
                        ),
                        'Base32StringSeed' => array(
                            'type' => 'string',
                        ),
                        'QRCodePNG' => array(
                            'type' => 'string',
                        ),
                        'User' => array(
                            'type' => 'object',
                            'properties' => array(
                                'Path' => array(
                                    'type' => 'string',
                                ),
                                'UserName' => array(
                                    'type' => 'string',
                                ),
                                'UserId' => array(
                                    'type' => 'string',
                                ),
                                'Arn' => array(
                                    'type' => 'string',
                                ),
                                'CreateDate' => array(
                                    'type' => 'string',
                                ),
                                'PasswordLastUsed' => array(
                                    'type' => 'string',
                                ),
                            ),
                        ),
                        'EnableDate' => array(
                            'type' => 'string',
                        ),
                    ),
                ),
            ),
        ),
        'GenerateCredentialReportResponse' => array(
            'type' => 'object',
            'additionalProperties' => true,
            'properties' => array(
                'State' => array(
                    'type' => 'string',
                    'location' => 'xml',
                ),
                'Description' => array(
                    'type' => 'string',
                    'location' => 'xml',
                ),
            ),
        ),
        'GetAccessKeyLastUsedResponse' => array(
            'type' => 'object',
            'additionalProperties' => true,
            'properties' => array(
                'UserName' => array(
                    'type' => 'string',
                    'location' => 'xml',
                ),
                'AccessKeyLastUsed' => array(
                    'type' => 'object',
                    'location' => 'xml',
                    'properties' => array(
                        'LastUsedDate' => array(
                            'type' => 'string',
                        ),
                        'ServiceName' => array(
                            'type' => 'string',
                        ),
                        'Region' => array(
                            'type' => 'string',
                        ),
                    ),
                ),
            ),
        ),
        'GetAccountAuthorizationDetailsResponse' => array(
            'type' => 'object',
            'additionalProperties' => true,
            'properties' => array(
                'UserDetailList' => array(
                    'type' => 'array',
                    'location' => 'xml',
                    'items' => array(
                        'name' => 'UserDetail',
                        'type' => 'object',
                        'sentAs' => 'member',
                        'properties' => array(
                            'Path' => array(
                                'type' => 'string',
                            ),
                            'UserName' => array(
                                'type' => 'string',
                            ),
                            'UserId' => array(
                                'type' => 'string',
                            ),
                            'Arn' => array(
                                'type' => 'string',
                            ),
                            'CreateDate' => array(
                                'type' => 'string',
                            ),
                            'UserPolicyList' => array(
                                'type' => 'array',
                                'items' => array(
                                    'name' => 'PolicyDetail',
                                    'type' => 'object',
                                    'sentAs' => 'member',
                                    'properties' => array(
                                        'PolicyName' => array(
                                            'type' => 'string',
                                        ),
                                        'PolicyDocument' => array(
                                            'type' => 'string',
                                        ),
                                    ),
                                ),
                            ),
                            'GroupList' => array(
                                'type' => 'array',
                                'items' => array(
                                    'name' => 'groupNameType',
                                    'type' => 'string',
                                    'sentAs' => 'member',
                                ),
                            ),
                            'AttachedManagedPolicies' => array(
                                'type' => 'array',
                                'items' => array(
                                    'name' => 'AttachedPolicy',
                                    'type' => 'object',
                                    'sentAs' => 'member',
                                    'properties' => array(
                                        'PolicyName' => array(
                                            'type' => 'string',
                                        ),
                                        'PolicyArn' => array(
                                            'type' => 'string',
                                        ),
                                    ),
                                ),
                            ),
                        ),
                    ),
                ),
                'GroupDetailList' => array(
                    'type' => 'array',
                    'location' => 'xml',
                    'items' => array(
                        'name' => 'GroupDetail',
                        'type' => 'object',
                        'sentAs' => 'member',
                        'properties' => array(
                            'Path' => array(
                                'type' => 'string',
                            ),
                            'GroupName' => array(
                                'type' => 'string',
                            ),
                            'GroupId' => array(
                                'type' => 'string',
                            ),
                            'Arn' => array(
                                'type' => 'string',
                            ),
                            'CreateDate' => array(
                                'type' => 'string',
                            ),
                            'GroupPolicyList' => array(
                                'type' => 'array',
                                'items' => array(
                                    'name' => 'PolicyDetail',
                                    'type' => 'object',
                                    'sentAs' => 'member',
                                    'properties' => array(
                                        'PolicyName' => array(
                                            'type' => 'string',
                                        ),
                                        'PolicyDocument' => array(
                                            'type' => 'string',
                                        ),
                                    ),
                                ),
                            ),
                            'AttachedManagedPolicies' => array(
                                'type' => 'array',
                                'items' => array(
                                    'name' => 'AttachedPolicy',
                                    'type' => 'object',
                                    'sentAs' => 'member',
                                    'properties' => array(
                                        'PolicyName' => array(
                                            'type' => 'string',
                                        ),
                                        'PolicyArn' => array(
                                            'type' => 'string',
                                        ),
                                    ),
                                ),
                            ),
                        ),
                    ),
                ),
                'RoleDetailList' => array(
                    'type' => 'array',
                    'location' => 'xml',
                    'items' => array(
                        'name' => 'RoleDetail',
                        'type' => 'object',
                        'sentAs' => 'member',
                        'properties' => array(
                            'Path' => array(
                                'type' => 'string',
                            ),
                            'RoleName' => array(
                                'type' => 'string',
                            ),
                            'RoleId' => array(
                                'type' => 'string',
                            ),
                            'Arn' => array(
                                'type' => 'string',
                            ),
                            'CreateDate' => array(
                                'type' => 'string',
                            ),
                            'AssumeRolePolicyDocument' => array(
                                'type' => 'string',
                            ),
                            'InstanceProfileList' => array(
                                'type' => 'array',
                                'items' => array(
                                    'name' => 'InstanceProfile',
                                    'type' => 'object',
                                    'sentAs' => 'member',
                                    'properties' => array(
                                        'Path' => array(
                                            'type' => 'string',
                                        ),
                                        'InstanceProfileName' => array(
                                            'type' => 'string',
                                        ),
                                        'InstanceProfileId' => array(
                                            'type' => 'string',
                                        ),
                                        'Arn' => array(
                                            'type' => 'string',
                                        ),
                                        'CreateDate' => array(
                                            'type' => 'string',
                                        ),
                                        'Roles' => array(
                                            'type' => 'array',
                                            'items' => array(
                                                'name' => 'Role',
                                                'type' => 'object',
                                                'sentAs' => 'member',
                                                'properties' => array(
                                                    'Path' => array(
                                                        'type' => 'string',
                                                    ),
                                                    'RoleName' => array(
                                                        'type' => 'string',
                                                    ),
                                                    'RoleId' => array(
                                                        'type' => 'string',
                                                    ),
                                                    'Arn' => array(
                                                        'type' => 'string',
                                                    ),
                                                    'CreateDate' => array(
                                                        'type' => 'string',
                                                    ),
                                                    'AssumeRolePolicyDocument' => array(
                                                        'type' => 'string',
                                                    ),
                                                ),
                                            ),
                                        ),
                                    ),
                                ),
                            ),
                            'RolePolicyList' => array(
                                'type' => 'array',
                                'items' => array(
                                    'name' => 'PolicyDetail',
                                    'type' => 'object',
                                    'sentAs' => 'member',
                                    'properties' => array(
                                        'PolicyName' => array(
                                            'type' => 'string',
                                        ),
                                        'PolicyDocument' => array(
                                            'type' => 'string',
                                        ),
                                    ),
                                ),
                            ),
                            'AttachedManagedPolicies' => array(
                                'type' => 'array',
                                'items' => array(
                                    'name' => 'AttachedPolicy',
                                    'type' => 'object',
                                    'sentAs' => 'member',
                                    'properties' => array(
                                        'PolicyName' => array(
                                            'type' => 'string',
                                        ),
                                        'PolicyArn' => array(
                                            'type' => 'string',
                                        ),
                                    ),
                                ),
                            ),
                        ),
                    ),
                ),
                'Policies' => array(
                    'type' => 'array',
                    'location' => 'xml',
                    'items' => array(
                        'name' => 'ManagedPolicyDetail',
                        'type' => 'object',
                        'sentAs' => 'member',
                        'properties' => array(
                            'PolicyName' => array(
                                'type' => 'string',
                            ),
                            'PolicyId' => array(
                                'type' => 'string',
                            ),
                            'Arn' => array(
                                'type' => 'string',
                            ),
                            'Path' => array(
                                'type' => 'string',
                            ),
                            'DefaultVersionId' => array(
                                'type' => 'string',
                            ),
                            'AttachmentCount' => array(
                                'type' => 'numeric',
                            ),
                            'IsAttachable' => array(
                                'type' => 'boolean',
                            ),
                            'Description' => array(
                                'type' => 'string',
                            ),
                            'CreateDate' => array(
                                'type' => 'string',
                            ),
                            'UpdateDate' => array(
                                'type' => 'string',
                            ),
                            'PolicyVersionList' => array(
                                'type' => 'array',
                                'items' => array(
                                    'name' => 'PolicyVersion',
                                    'type' => 'object',
                                    'sentAs' => 'member',
                                    'properties' => array(
                                        'Document' => array(
                                            'type' => 'string',
                                        ),
                                        'VersionId' => array(
                                            'type' => 'string',
                                        ),
                                        'IsDefaultVersion' => array(
                                            'type' => 'boolean',
                                        ),
                                        'CreateDate' => array(
                                            'type' => 'string',
                                        ),
                                    ),
                                ),
                            ),
                        ),
                    ),
                ),
                'IsTruncated' => array(
                    'type' => 'boolean',
                    'location' => 'xml',
                ),
                'Marker' => array(
                    'type' => 'string',
                    'location' => 'xml',
                ),
            ),
        ),
        'GetAccountPasswordPolicyResponse' => array(
            'type' => 'object',
            'additionalProperties' => true,
            'properties' => array(
                'PasswordPolicy' => array(
                    'type' => 'object',
                    'location' => 'xml',
                    'properties' => array(
                        'MinimumPasswordLength' => array(
                            'type' => 'numeric',
                        ),
                        'RequireSymbols' => array(
                            'type' => 'boolean',
                        ),
                        'RequireNumbers' => array(
                            'type' => 'boolean',
                        ),
                        'RequireUppercaseCharacters' => array(
                            'type' => 'boolean',
                        ),
                        'RequireLowercaseCharacters' => array(
                            'type' => 'boolean',
                        ),
                        'AllowUsersToChangePassword' => array(
                            'type' => 'boolean',
                        ),
                        'ExpirePasswords' => array(
                            'type' => 'boolean',
                        ),
                        'MaxPasswordAge' => array(
                            'type' => 'numeric',
                        ),
                        'PasswordReusePrevention' => array(
                            'type' => 'numeric',
                        ),
                        'HardExpiry' => array(
                            'type' => 'boolean',
                        ),
                    ),
                ),
            ),
        ),
        'GetAccountSummaryResponse' => array(
            'type' => 'object',
            'additionalProperties' => true,
            'properties' => array(
                'SummaryMap' => array(
                    'type' => 'array',
                    'location' => 'xml',
                    'data' => array(
                        'xmlMap' => array(
                            'Users',
                            'UsersQuota',
                            'Groups',
                            'GroupsQuota',
                            'ServerCertificates',
                            'ServerCertificatesQuota',
                            'UserPolicySizeQuota',
                            'GroupPolicySizeQuota',
                            'GroupsPerUserQuota',
                            'SigningCertificatesPerUserQuota',
                            'AccessKeysPerUserQuota',
                            'MFADevices',
                            'MFADevicesInUse',
                            'AccountMFAEnabled',
                            'AccountAccessKeysPresent',
                            'AccountSigningCertificatesPresent',
                            'AttachedPoliciesPerGroupQuota',
                            'AttachedPoliciesPerRoleQuota',
                            'AttachedPoliciesPerUserQuota',
                            'Policies',
                            'PoliciesQuota',
                            'PolicySizeQuota',
                            'PolicyVersionsInUse',
                            'PolicyVersionsInUseQuota',
                            'VersionsPerPolicyQuota',
                        ),
                    ),
                    'filters' => array(
                        array(
                            'method' => 'Aws\\Common\\Command\\XmlResponseLocationVisitor::xmlMap',
                            'args' => array(
                                '@value',
                                'entry',
                                'key',
                                'value',
                            ),
                        ),
                    ),
                    'items' => array(
                        'name' => 'entry',
                        'type' => 'object',
                        'sentAs' => 'entry',
                        'additionalProperties' => true,
                        'properties' => array(
                            'key' => array(
                                'type' => 'string',
                            ),
                            'value' => array(
                                'type' => 'numeric',
                            ),
                        ),
                    ),
                    'additionalProperties' => false,
                ),
            ),
        ),
        'GetContextKeysForPolicyResponse' => array(
            'type' => 'object',
            'additionalProperties' => true,
            'properties' => array(
                'ContextKeyNames' => array(
                    'type' => 'array',
                    'location' => 'xml',
                    'items' => array(
                        'name' => 'ContextKeyNameType',
                        'type' => 'string',
                        'sentAs' => 'member',
                    ),
                ),
            ),
        ),
        'GetCredentialReportResponse' => array(
            'type' => 'object',
            'additionalProperties' => true,
            'properties' => array(
                'Content' => array(
                    'type' => 'string',
                    'location' => 'xml',
                ),
                'ReportFormat' => array(
                    'type' => 'string',
                    'location' => 'xml',
                ),
                'GeneratedTime' => array(
                    'type' => 'string',
                    'location' => 'xml',
                ),
            ),
        ),
        'GetGroupResponse' => array(
            'type' => 'object',
            'additionalProperties' => true,
            'properties' => array(
                'Group' => array(
                    'type' => 'object',
                    'location' => 'xml',
                    'properties' => array(
                        'Path' => array(
                            'type' => 'string',
                        ),
                        'GroupName' => array(
                            'type' => 'string',
                        ),
                        'GroupId' => array(
                            'type' => 'string',
                        ),
                        'Arn' => array(
                            'type' => 'string',
                        ),
                        'CreateDate' => array(
                            'type' => 'string',
                        ),
                    ),
                ),
                'Users' => array(
                    'type' => 'array',
                    'location' => 'xml',
                    'items' => array(
                        'name' => 'User',
                        'type' => 'object',
                        'sentAs' => 'member',
                        'properties' => array(
                            'Path' => array(
                                'type' => 'string',
                            ),
                            'UserName' => array(
                                'type' => 'string',
                            ),
                            'UserId' => array(
                                'type' => 'string',
                            ),
                            'Arn' => array(
                                'type' => 'string',
                            ),
                            'CreateDate' => array(
                                'type' => 'string',
                            ),
                            'PasswordLastUsed' => array(
                                'type' => 'string',
                            ),
                        ),
                    ),
                ),
                'IsTruncated' => array(
                    'type' => 'boolean',
                    'location' => 'xml',
                ),
                'Marker' => array(
                    'type' => 'string',
                    'location' => 'xml',
                ),
            ),
        ),
        'GetGroupPolicyResponse' => array(
            'type' => 'object',
            'additionalProperties' => true,
            'properties' => array(
                'GroupName' => array(
                    'type' => 'string',
                    'location' => 'xml',
                ),
                'PolicyName' => array(
                    'type' => 'string',
                    'location' => 'xml',
                ),
                'PolicyDocument' => array(
                    'type' => 'string',
                    'location' => 'xml',
                ),
            ),
        ),
        'GetInstanceProfileResponse' => array(
            'type' => 'object',
            'additionalProperties' => true,
            'properties' => array(
                'InstanceProfile' => array(
                    'type' => 'object',
                    'location' => 'xml',
                    'properties' => array(
                        'Path' => array(
                            'type' => 'string',
                        ),
                        'InstanceProfileName' => array(
                            'type' => 'string',
                        ),
                        'InstanceProfileId' => array(
                            'type' => 'string',
                        ),
                        'Arn' => array(
                            'type' => 'string',
                        ),
                        'CreateDate' => array(
                            'type' => 'string',
                        ),
                        'Roles' => array(
                            'type' => 'array',
                            'items' => array(
                                'name' => 'Role',
                                'type' => 'object',
                                'sentAs' => 'member',
                                'properties' => array(
                                    'Path' => array(
                                        'type' => 'string',
                                    ),
                                    'RoleName' => array(
                                        'type' => 'string',
                                    ),
                                    'RoleId' => array(
                                        'type' => 'string',
                                    ),
                                    'Arn' => array(
                                        'type' => 'string',
                                    ),
                                    'CreateDate' => array(
                                        'type' => 'string',
                                    ),
                                    'AssumeRolePolicyDocument' => array(
                                        'type' => 'string',
                                    ),
                                ),
                            ),
                        ),
                    ),
                ),
            ),
        ),
        'GetLoginProfileResponse' => array(
            'type' => 'object',
            'additionalProperties' => true,
            'properties' => array(
                'LoginProfile' => array(
                    'type' => 'object',
                    'location' => 'xml',
                    'properties' => array(
                        'UserName' => array(
                            'type' => 'string',
                        ),
                        'CreateDate' => array(
                            'type' => 'string',
                        ),
                        'PasswordResetRequired' => array(
                            'type' => 'boolean',
                        ),
                    ),
                ),
            ),
        ),
        'GetOpenIDConnectProviderResponse' => array(
            'type' => 'object',
            'additionalProperties' => true,
            'properties' => array(
                'Url' => array(
                    'type' => 'string',
                    'location' => 'xml',
                ),
                'ClientIDList' => array(
                    'type' => 'array',
                    'location' => 'xml',
                    'items' => array(
                        'name' => 'clientIDType',
                        'type' => 'string',
                        'sentAs' => 'member',
                    ),
                ),
                'ThumbprintList' => array(
                    'type' => 'array',
                    'location' => 'xml',
                    'items' => array(
                        'name' => 'thumbprintType',
                        'type' => 'string',
                        'sentAs' => 'member',
                    ),
                ),
                'CreateDate' => array(
                    'type' => 'string',
                    'location' => 'xml',
                ),
            ),
        ),
        'GetPolicyResponse' => array(
            'type' => 'object',
            'additionalProperties' => true,
            'properties' => array(
                'Policy' => array(
                    'type' => 'object',
                    'location' => 'xml',
                    'properties' => array(
                        'PolicyName' => array(
                            'type' => 'string',
                        ),
                        'PolicyId' => array(
                            'type' => 'string',
                        ),
                        'Arn' => array(
                            'type' => 'string',
                        ),
                        'Path' => array(
                            'type' => 'string',
                        ),
                        'DefaultVersionId' => array(
                            'type' => 'string',
                        ),
                        'AttachmentCount' => array(
                            'type' => 'numeric',
                        ),
                        'IsAttachable' => array(
                            'type' => 'boolean',
                        ),
                        'Description' => array(
                            'type' => 'string',
                        ),
                        'CreateDate' => array(
                            'type' => 'string',
                        ),
                        'UpdateDate' => array(
                            'type' => 'string',
                        ),
                    ),
                ),
            ),
        ),
        'GetPolicyVersionResponse' => array(
            'type' => 'object',
            'additionalProperties' => true,
            'properties' => array(
                'PolicyVersion' => array(
                    'type' => 'object',
                    'location' => 'xml',
                    'properties' => array(
                        'Document' => array(
                            'type' => 'string',
                        ),
                        'VersionId' => array(
                            'type' => 'string',
                        ),
                        'IsDefaultVersion' => array(
                            'type' => 'boolean',
                        ),
                        'CreateDate' => array(
                            'type' => 'string',
                        ),
                    ),
                ),
            ),
        ),
        'GetRoleResponse' => array(
            'type' => 'object',
            'additionalProperties' => true,
            'properties' => array(
                'Role' => array(
                    'type' => 'object',
                    'location' => 'xml',
                    'properties' => array(
                        'Path' => array(
                            'type' => 'string',
                        ),
                        'RoleName' => array(
                            'type' => 'string',
                        ),
                        'RoleId' => array(
                            'type' => 'string',
                        ),
                        'Arn' => array(
                            'type' => 'string',
                        ),
                        'CreateDate' => array(
                            'type' => 'string',
                        ),
                        'AssumeRolePolicyDocument' => array(
                            'type' => 'string',
                        ),
                    ),
                ),
            ),
        ),
        'GetRolePolicyResponse' => array(
            'type' => 'object',
            'additionalProperties' => true,
            'properties' => array(
                'RoleName' => array(
                    'type' => 'string',
                    'location' => 'xml',
                ),
                'PolicyName' => array(
                    'type' => 'string',
                    'location' => 'xml',
                ),
                'PolicyDocument' => array(
                    'type' => 'string',
                    'location' => 'xml',
                ),
            ),
        ),
        'GetSAMLProviderResponse' => array(
            'type' => 'object',
            'additionalProperties' => true,
            'properties' => array(
                'SAMLMetadataDocument' => array(
                    'type' => 'string',
                    'location' => 'xml',
                ),
                'CreateDate' => array(
                    'type' => 'string',
                    'location' => 'xml',
                ),
                'ValidUntil' => array(
                    'type' => 'string',
                    'location' => 'xml',
                ),
            ),
        ),
        'GetSSHPublicKeyResponse' => array(
            'type' => 'object',
            'additionalProperties' => true,
            'properties' => array(
                'SSHPublicKey' => array(
                    'type' => 'object',
                    'location' => 'xml',
                    'properties' => array(
                        'UserName' => array(
                            'type' => 'string',
                        ),
                        'SSHPublicKeyId' => array(
                            'type' => 'string',
                        ),
                        'Fingerprint' => array(
                            'type' => 'string',
                        ),
                        'SSHPublicKeyBody' => array(
                            'type' => 'string',
                        ),
                        'Status' => array(
                            'type' => 'string',
                        ),
                        'UploadDate' => array(
                            'type' => 'string',
                        ),
                    ),
                ),
            ),
        ),
        'GetServerCertificateResponse' => array(
            'type' => 'object',
            'additionalProperties' => true,
            'properties' => array(
                'ServerCertificate' => array(
                    'type' => 'object',
                    'location' => 'xml',
                    'properties' => array(
                        'ServerCertificateMetadata' => array(
                            'type' => 'object',
                            'properties' => array(
                                'Path' => array(
                                    'type' => 'string',
                                ),
                                'ServerCertificateName' => array(
                                    'type' => 'string',
                                ),
                                'ServerCertificateId' => array(
                                    'type' => 'string',
                                ),
                                'Arn' => array(
                                    'type' => 'string',
                                ),
                                'UploadDate' => array(
                                    'type' => 'string',
                                ),
                                'Expiration' => array(
                                    'type' => 'string',
                                ),
                            ),
                        ),
                        'CertificateBody' => array(
                            'type' => 'string',
                        ),
                        'CertificateChain' => array(
                            'type' => 'string',
                        ),
                    ),
                ),
            ),
        ),
        'GetUserResponse' => array(
            'type' => 'object',
            'additionalProperties' => true,
            'properties' => array(
                'User' => array(
                    'type' => 'object',
                    'location' => 'xml',
                    'properties' => array(
                        'Path' => array(
                            'type' => 'string',
                        ),
                        'UserName' => array(
                            'type' => 'string',
                        ),
                        'UserId' => array(
                            'type' => 'string',
                        ),
                        'Arn' => array(
                            'type' => 'string',
                        ),
                        'CreateDate' => array(
                            'type' => 'string',
                        ),
                        'PasswordLastUsed' => array(
                            'type' => 'string',
                        ),
                    ),
                ),
            ),
        ),
        'GetUserPolicyResponse' => array(
            'type' => 'object',
            'additionalProperties' => true,
            'properties' => array(
                'UserName' => array(
                    'type' => 'string',
                    'location' => 'xml',
                ),
                'PolicyName' => array(
                    'type' => 'string',
                    'location' => 'xml',
                ),
                'PolicyDocument' => array(
                    'type' => 'string',
                    'location' => 'xml',
                ),
            ),
        ),
        'ListAccessKeysResponse' => array(
            'type' => 'object',
            'additionalProperties' => true,
            'properties' => array(
                'AccessKeyMetadata' => array(
                    'type' => 'array',
                    'location' => 'xml',
                    'items' => array(
                        'name' => 'AccessKeyMetadata',
                        'type' => 'object',
                        'sentAs' => 'member',
                        'properties' => array(
                            'UserName' => array(
                                'type' => 'string',
                            ),
                            'AccessKeyId' => array(
                                'type' => 'string',
                            ),
                            'Status' => array(
                                'type' => 'string',
                            ),
                            'CreateDate' => array(
                                'type' => 'string',
                            ),
                        ),
                    ),
                ),
                'IsTruncated' => array(
                    'type' => 'boolean',
                    'location' => 'xml',
                ),
                'Marker' => array(
                    'type' => 'string',
                    'location' => 'xml',
                ),
            ),
        ),
        'ListAccountAliasesResponse' => array(
            'type' => 'object',
            'additionalProperties' => true,
            'properties' => array(
                'AccountAliases' => array(
                    'type' => 'array',
                    'location' => 'xml',
                    'items' => array(
                        'name' => 'accountAliasType',
                        'type' => 'string',
                        'sentAs' => 'member',
                    ),
                ),
                'IsTruncated' => array(
                    'type' => 'boolean',
                    'location' => 'xml',
                ),
                'Marker' => array(
                    'type' => 'string',
                    'location' => 'xml',
                ),
            ),
        ),
        'ListAttachedGroupPoliciesResponse' => array(
            'type' => 'object',
            'additionalProperties' => true,
            'properties' => array(
                'AttachedPolicies' => array(
                    'type' => 'array',
                    'location' => 'xml',
                    'items' => array(
                        'name' => 'AttachedPolicy',
                        'type' => 'object',
                        'sentAs' => 'member',
                        'properties' => array(
                            'PolicyName' => array(
                                'type' => 'string',
                            ),
                            'PolicyArn' => array(
                                'type' => 'string',
                            ),
                        ),
                    ),
                ),
                'IsTruncated' => array(
                    'type' => 'boolean',
                    'location' => 'xml',
                ),
                'Marker' => array(
                    'type' => 'string',
                    'location' => 'xml',
                ),
            ),
        ),
        'ListAttachedRolePoliciesResponse' => array(
            'type' => 'object',
            'additionalProperties' => true,
            'properties' => array(
                'AttachedPolicies' => array(
                    'type' => 'array',
                    'location' => 'xml',
                    'items' => array(
                        'name' => 'AttachedPolicy',
                        'type' => 'object',
                        'sentAs' => 'member',
                        'properties' => array(
                            'PolicyName' => array(
                                'type' => 'string',
                            ),
                            'PolicyArn' => array(
                                'type' => 'string',
                            ),
                        ),
                    ),
                ),
                'IsTruncated' => array(
                    'type' => 'boolean',
                    'location' => 'xml',
                ),
                'Marker' => array(
                    'type' => 'string',
                    'location' => 'xml',
                ),
            ),
        ),
        'ListAttachedUserPoliciesResponse' => array(
            'type' => 'object',
            'additionalProperties' => true,
            'properties' => array(
                'AttachedPolicies' => array(
                    'type' => 'array',
                    'location' => 'xml',
                    'items' => array(
                        'name' => 'AttachedPolicy',
                        'type' => 'object',
                        'sentAs' => 'member',
                        'properties' => array(
                            'PolicyName' => array(
                                'type' => 'string',
                            ),
                            'PolicyArn' => array(
                                'type' => 'string',
                            ),
                        ),
                    ),
                ),
                'IsTruncated' => array(
                    'type' => 'boolean',
                    'location' => 'xml',
                ),
                'Marker' => array(
                    'type' => 'string',
                    'location' => 'xml',
                ),
            ),
        ),
        'ListEntitiesForPolicyResponse' => array(
            'type' => 'object',
            'additionalProperties' => true,
            'properties' => array(
                'PolicyGroups' => array(
                    'type' => 'array',
                    'location' => 'xml',
                    'items' => array(
                        'name' => 'PolicyGroup',
                        'type' => 'object',
                        'sentAs' => 'member',
                        'properties' => array(
                            'GroupName' => array(
                                'type' => 'string',
                            ),
                        ),
                    ),
                ),
                'PolicyUsers' => array(
                    'type' => 'array',
                    'location' => 'xml',
                    'items' => array(
                        'name' => 'PolicyUser',
                        'type' => 'object',
                        'sentAs' => 'member',
                        'properties' => array(
                            'UserName' => array(
                                'type' => 'string',
                            ),
                        ),
                    ),
                ),
                'PolicyRoles' => array(
                    'type' => 'array',
                    'location' => 'xml',
                    'items' => array(
                        'name' => 'PolicyRole',
                        'type' => 'object',
                        'sentAs' => 'member',
                        'properties' => array(
                            'RoleName' => array(
                                'type' => 'string',
                            ),
                        ),
                    ),
                ),
                'IsTruncated' => array(
                    'type' => 'boolean',
                    'location' => 'xml',
                ),
                'Marker' => array(
                    'type' => 'string',
                    'location' => 'xml',
                ),
            ),
        ),
        'ListGroupPoliciesResponse' => array(
            'type' => 'object',
            'additionalProperties' => true,
            'properties' => array(
                'PolicyNames' => array(
                    'type' => 'array',
                    'location' => 'xml',
                    'items' => array(
                        'name' => 'policyNameType',
                        'type' => 'string',
                        'sentAs' => 'member',
                    ),
                ),
                'IsTruncated' => array(
                    'type' => 'boolean',
                    'location' => 'xml',
                ),
                'Marker' => array(
                    'type' => 'string',
                    'location' => 'xml',
                ),
            ),
        ),
        'ListGroupsResponse' => array(
            'type' => 'object',
            'additionalProperties' => true,
            'properties' => array(
                'Groups' => array(
                    'type' => 'array',
                    'location' => 'xml',
                    'items' => array(
                        'name' => 'Group',
                        'type' => 'object',
                        'sentAs' => 'member',
                        'properties' => array(
                            'Path' => array(
                                'type' => 'string',
                            ),
                            'GroupName' => array(
                                'type' => 'string',
                            ),
                            'GroupId' => array(
                                'type' => 'string',
                            ),
                            'Arn' => array(
                                'type' => 'string',
                            ),
                            'CreateDate' => array(
                                'type' => 'string',
                            ),
                        ),
                    ),
                ),
                'IsTruncated' => array(
                    'type' => 'boolean',
                    'location' => 'xml',
                ),
                'Marker' => array(
                    'type' => 'string',
                    'location' => 'xml',
                ),
            ),
        ),
        'ListGroupsForUserResponse' => array(
            'type' => 'object',
            'additionalProperties' => true,
            'properties' => array(
                'Groups' => array(
                    'type' => 'array',
                    'location' => 'xml',
                    'items' => array(
                        'name' => 'Group',
                        'type' => 'object',
                        'sentAs' => 'member',
                        'properties' => array(
                            'Path' => array(
                                'type' => 'string',
                            ),
                            'GroupName' => array(
                                'type' => 'string',
                            ),
                            'GroupId' => array(
                                'type' => 'string',
                            ),
                            'Arn' => array(
                                'type' => 'string',
                            ),
                            'CreateDate' => array(
                                'type' => 'string',
                            ),
                        ),
                    ),
                ),
                'IsTruncated' => array(
                    'type' => 'boolean',
                    'location' => 'xml',
                ),
                'Marker' => array(
                    'type' => 'string',
                    'location' => 'xml',
                ),
            ),
        ),
        'ListInstanceProfilesResponse' => array(
            'type' => 'object',
            'additionalProperties' => true,
            'properties' => array(
                'InstanceProfiles' => array(
                    'type' => 'array',
                    'location' => 'xml',
                    'items' => array(
                        'name' => 'InstanceProfile',
                        'type' => 'object',
                        'sentAs' => 'member',
                        'properties' => array(
                            'Path' => array(
                                'type' => 'string',
                            ),
                            'InstanceProfileName' => array(
                                'type' => 'string',
                            ),
                            'InstanceProfileId' => array(
                                'type' => 'string',
                            ),
                            'Arn' => array(
                                'type' => 'string',
                            ),
                            'CreateDate' => array(
                                'type' => 'string',
                            ),
                            'Roles' => array(
                                'type' => 'array',
                                'items' => array(
                                    'name' => 'Role',
                                    'type' => 'object',
                                    'sentAs' => 'member',
                                    'properties' => array(
                                        'Path' => array(
                                            'type' => 'string',
                                        ),
                                        'RoleName' => array(
                                            'type' => 'string',
                                        ),
                                        'RoleId' => array(
                                            'type' => 'string',
                                        ),
                                        'Arn' => array(
                                            'type' => 'string',
                                        ),
                                        'CreateDate' => array(
                                            'type' => 'string',
                                        ),
                                        'AssumeRolePolicyDocument' => array(
                                            'type' => 'string',
                                        ),
                                    ),
                                ),
                            ),
                        ),
                    ),
                ),
                'IsTruncated' => array(
                    'type' => 'boolean',
                    'location' => 'xml',
                ),
                'Marker' => array(
                    'type' => 'string',
                    'location' => 'xml',
                ),
            ),
        ),
        'ListInstanceProfilesForRoleResponse' => array(
            'type' => 'object',
            'additionalProperties' => true,
            'properties' => array(
                'InstanceProfiles' => array(
                    'type' => 'array',
                    'location' => 'xml',
                    'items' => array(
                        'name' => 'InstanceProfile',
                        'type' => 'object',
                        'sentAs' => 'member',
                        'properties' => array(
                            'Path' => array(
                                'type' => 'string',
                            ),
                            'InstanceProfileName' => array(
                                'type' => 'string',
                            ),
                            'InstanceProfileId' => array(
                                'type' => 'string',
                            ),
                            'Arn' => array(
                                'type' => 'string',
                            ),
                            'CreateDate' => array(
                                'type' => 'string',
                            ),
                            'Roles' => array(
                                'type' => 'array',
                                'items' => array(
                                    'name' => 'Role',
                                    'type' => 'object',
                                    'sentAs' => 'member',
                                    'properties' => array(
                                        'Path' => array(
                                            'type' => 'string',
                                        ),
                                        'RoleName' => array(
                                            'type' => 'string',
                                        ),
                                        'RoleId' => array(
                                            'type' => 'string',
                                        ),
                                        'Arn' => array(
                                            'type' => 'string',
                                        ),
                                        'CreateDate' => array(
                                            'type' => 'string',
                                        ),
                                        'AssumeRolePolicyDocument' => array(
                                            'type' => 'string',
                                        ),
                                    ),
                                ),
                            ),
                        ),
                    ),
                ),
                'IsTruncated' => array(
                    'type' => 'boolean',
                    'location' => 'xml',
                ),
                'Marker' => array(
                    'type' => 'string',
                    'location' => 'xml',
                ),
            ),
        ),
        'ListMFADevicesResponse' => array(
            'type' => 'object',
            'additionalProperties' => true,
            'properties' => array(
                'MFADevices' => array(
                    'type' => 'array',
                    'location' => 'xml',
                    'items' => array(
                        'name' => 'MFADevice',
                        'type' => 'object',
                        'sentAs' => 'member',
                        'properties' => array(
                            'UserName' => array(
                                'type' => 'string',
                            ),
                            'SerialNumber' => array(
                                'type' => 'string',
                            ),
                            'EnableDate' => array(
                                'type' => 'string',
                            ),
                        ),
                    ),
                ),
                'IsTruncated' => array(
                    'type' => 'boolean',
                    'location' => 'xml',
                ),
                'Marker' => array(
                    'type' => 'string',
                    'location' => 'xml',
                ),
            ),
        ),
        'ListOpenIDConnectProvidersResponse' => array(
            'type' => 'object',
            'additionalProperties' => true,
            'properties' => array(
                'OpenIDConnectProviderList' => array(
                    'type' => 'array',
                    'location' => 'xml',
                    'items' => array(
                        'name' => 'OpenIDConnectProviderListEntry',
                        'type' => 'object',
                        'sentAs' => 'member',
                        'properties' => array(
                            'Arn' => array(
                                'type' => 'string',
                            ),
                        ),
                    ),
                ),
            ),
        ),
        'ListPoliciesResponse' => array(
            'type' => 'object',
            'additionalProperties' => true,
            'properties' => array(
                'Policies' => array(
                    'type' => 'array',
                    'location' => 'xml',
                    'items' => array(
                        'name' => 'Policy',
                        'type' => 'object',
                        'sentAs' => 'member',
                        'properties' => array(
                            'PolicyName' => array(
                                'type' => 'string',
                            ),
                            'PolicyId' => array(
                                'type' => 'string',
                            ),
                            'Arn' => array(
                                'type' => 'string',
                            ),
                            'Path' => array(
                                'type' => 'string',
                            ),
                            'DefaultVersionId' => array(
                                'type' => 'string',
                            ),
                            'AttachmentCount' => array(
                                'type' => 'numeric',
                            ),
                            'IsAttachable' => array(
                                'type' => 'boolean',
                            ),
                            'Description' => array(
                                'type' => 'string',
                            ),
                            'CreateDate' => array(
                                'type' => 'string',
                            ),
                            'UpdateDate' => array(
                                'type' => 'string',
                            ),
                        ),
                    ),
                ),
                'IsTruncated' => array(
                    'type' => 'boolean',
                    'location' => 'xml',
                ),
                'Marker' => array(
                    'type' => 'string',
                    'location' => 'xml',
                ),
            ),
        ),
        'ListPolicyVersionsResponse' => array(
            'type' => 'object',
            'additionalProperties' => true,
            'properties' => array(
                'Versions' => array(
                    'type' => 'array',
                    'location' => 'xml',
                    'items' => array(
                        'name' => 'PolicyVersion',
                        'type' => 'object',
                        'sentAs' => 'member',
                        'properties' => array(
                            'Document' => array(
                                'type' => 'string',
                            ),
                            'VersionId' => array(
                                'type' => 'string',
                            ),
                            'IsDefaultVersion' => array(
                                'type' => 'boolean',
                            ),
                            'CreateDate' => array(
                                'type' => 'string',
                            ),
                        ),
                    ),
                ),
                'IsTruncated' => array(
                    'type' => 'boolean',
                    'location' => 'xml',
                ),
                'Marker' => array(
                    'type' => 'string',
                    'location' => 'xml',
                ),
            ),
        ),
        'ListRolePoliciesResponse' => array(
            'type' => 'object',
            'additionalProperties' => true,
            'properties' => array(
                'PolicyNames' => array(
                    'type' => 'array',
                    'location' => 'xml',
                    'items' => array(
                        'name' => 'policyNameType',
                        'type' => 'string',
                        'sentAs' => 'member',
                    ),
                ),
                'IsTruncated' => array(
                    'type' => 'boolean',
                    'location' => 'xml',
                ),
                'Marker' => array(
                    'type' => 'string',
                    'location' => 'xml',
                ),
            ),
        ),
        'ListRolesResponse' => array(
            'type' => 'object',
            'additionalProperties' => true,
            'properties' => array(
                'Roles' => array(
                    'type' => 'array',
                    'location' => 'xml',
                    'items' => array(
                        'name' => 'Role',
                        'type' => 'object',
                        'sentAs' => 'member',
                        'properties' => array(
                            'Path' => array(
                                'type' => 'string',
                            ),
                            'RoleName' => array(
                                'type' => 'string',
                            ),
                            'RoleId' => array(
                                'type' => 'string',
                            ),
                            'Arn' => array(
                                'type' => 'string',
                            ),
                            'CreateDate' => array(
                                'type' => 'string',
                            ),
                            'AssumeRolePolicyDocument' => array(
                                'type' => 'string',
                            ),
                        ),
                    ),
                ),
                'IsTruncated' => array(
                    'type' => 'boolean',
                    'location' => 'xml',
                ),
                'Marker' => array(
                    'type' => 'string',
                    'location' => 'xml',
                ),
            ),
        ),
        'ListSAMLProvidersResponse' => array(
            'type' => 'object',
            'additionalProperties' => true,
            'properties' => array(
                'SAMLProviderList' => array(
                    'type' => 'array',
                    'location' => 'xml',
                    'items' => array(
                        'name' => 'SAMLProviderListEntry',
                        'type' => 'object',
                        'sentAs' => 'member',
                        'properties' => array(
                            'Arn' => array(
                                'type' => 'string',
                            ),
                            'ValidUntil' => array(
                                'type' => 'string',
                            ),
                            'CreateDate' => array(
                                'type' => 'string',
                            ),
                        ),
                    ),
                ),
            ),
        ),
        'ListSSHPublicKeysResponse' => array(
            'type' => 'object',
            'additionalProperties' => true,
            'properties' => array(
                'SSHPublicKeys' => array(
                    'type' => 'array',
                    'location' => 'xml',
                    'items' => array(
                        'name' => 'SSHPublicKeyMetadata',
                        'type' => 'object',
                        'sentAs' => 'member',
                        'properties' => array(
                            'UserName' => array(
                                'type' => 'string',
                            ),
                            'SSHPublicKeyId' => array(
                                'type' => 'string',
                            ),
                            'Status' => array(
                                'type' => 'string',
                            ),
                            'UploadDate' => array(
                                'type' => 'string',
                            ),
                        ),
                    ),
                ),
                'IsTruncated' => array(
                    'type' => 'boolean',
                    'location' => 'xml',
                ),
                'Marker' => array(
                    'type' => 'string',
                    'location' => 'xml',
                ),
            ),
        ),
        'ListServerCertificatesResponse' => array(
            'type' => 'object',
            'additionalProperties' => true,
            'properties' => array(
                'ServerCertificateMetadataList' => array(
                    'type' => 'array',
                    'location' => 'xml',
                    'items' => array(
                        'name' => 'ServerCertificateMetadata',
                        'type' => 'object',
                        'sentAs' => 'member',
                        'properties' => array(
                            'Path' => array(
                                'type' => 'string',
                            ),
                            'ServerCertificateName' => array(
                                'type' => 'string',
                            ),
                            'ServerCertificateId' => array(
                                'type' => 'string',
                            ),
                            'Arn' => array(
                                'type' => 'string',
                            ),
                            'UploadDate' => array(
                                'type' => 'string',
                            ),
                            'Expiration' => array(
                                'type' => 'string',
                            ),
                        ),
                    ),
                ),
                'IsTruncated' => array(
                    'type' => 'boolean',
                    'location' => 'xml',
                ),
                'Marker' => array(
                    'type' => 'string',
                    'location' => 'xml',
                ),
            ),
        ),
        'ListSigningCertificatesResponse' => array(
            'type' => 'object',
            'additionalProperties' => true,
            'properties' => array(
                'Certificates' => array(
                    'type' => 'array',
                    'location' => 'xml',
                    'items' => array(
                        'name' => 'SigningCertificate',
                        'type' => 'object',
                        'sentAs' => 'member',
                        'properties' => array(
                            'UserName' => array(
                                'type' => 'string',
                            ),
                            'CertificateId' => array(
                                'type' => 'string',
                            ),
                            'CertificateBody' => array(
                                'type' => 'string',
                            ),
                            'Status' => array(
                                'type' => 'string',
                            ),
                            'UploadDate' => array(
                                'type' => 'string',
                            ),
                        ),
                    ),
                ),
                'IsTruncated' => array(
                    'type' => 'boolean',
                    'location' => 'xml',
                ),
                'Marker' => array(
                    'type' => 'string',
                    'location' => 'xml',
                ),
            ),
        ),
        'ListUserPoliciesResponse' => array(
            'type' => 'object',
            'additionalProperties' => true,
            'properties' => array(
                'PolicyNames' => array(
                    'type' => 'array',
                    'location' => 'xml',
                    'items' => array(
                        'name' => 'policyNameType',
                        'type' => 'string',
                        'sentAs' => 'member',
                    ),
                ),
                'IsTruncated' => array(
                    'type' => 'boolean',
                    'location' => 'xml',
                ),
                'Marker' => array(
                    'type' => 'string',
                    'location' => 'xml',
                ),
            ),
        ),
        'ListUsersResponse' => array(
            'type' => 'object',
            'additionalProperties' => true,
            'properties' => array(
                'Users' => array(
                    'type' => 'array',
                    'location' => 'xml',
                    'items' => array(
                        'name' => 'User',
                        'type' => 'object',
                        'sentAs' => 'member',
                        'properties' => array(
                            'Path' => array(
                                'type' => 'string',
                            ),
                            'UserName' => array(
                                'type' => 'string',
                            ),
                            'UserId' => array(
                                'type' => 'string',
                            ),
                            'Arn' => array(
                                'type' => 'string',
                            ),
                            'CreateDate' => array(
                                'type' => 'string',
                            ),
                            'PasswordLastUsed' => array(
                                'type' => 'string',
                            ),
                        ),
                    ),
                ),
                'IsTruncated' => array(
                    'type' => 'boolean',
                    'location' => 'xml',
                ),
                'Marker' => array(
                    'type' => 'string',
                    'location' => 'xml',
                ),
            ),
        ),
        'ListVirtualMFADevicesResponse' => array(
            'type' => 'object',
            'additionalProperties' => true,
            'properties' => array(
                'VirtualMFADevices' => array(
                    'type' => 'array',
                    'location' => 'xml',
                    'items' => array(
                        'name' => 'VirtualMFADevice',
                        'type' => 'object',
                        'sentAs' => 'member',
                        'properties' => array(
                            'SerialNumber' => array(
                                'type' => 'string',
                            ),
                            'Base32StringSeed' => array(
                                'type' => 'string',
                            ),
                            'QRCodePNG' => array(
                                'type' => 'string',
                            ),
                            'User' => array(
                                'type' => 'object',
                                'properties' => array(
                                    'Path' => array(
                                        'type' => 'string',
                                    ),
                                    'UserName' => array(
                                        'type' => 'string',
                                    ),
                                    'UserId' => array(
                                        'type' => 'string',
                                    ),
                                    'Arn' => array(
                                        'type' => 'string',
                                    ),
                                    'CreateDate' => array(
                                        'type' => 'string',
                                    ),
                                    'PasswordLastUsed' => array(
                                        'type' => 'string',
                                    ),
                                ),
                            ),
                            'EnableDate' => array(
                                'type' => 'string',
                            ),
                        ),
                    ),
                ),
                'IsTruncated' => array(
                    'type' => 'boolean',
                    'location' => 'xml',
                ),
                'Marker' => array(
                    'type' => 'string',
                    'location' => 'xml',
                ),
            ),
        ),
        'SimulatePolicyResponse' => array(
            'type' => 'object',
            'additionalProperties' => true,
            'properties' => array(
                'EvaluationResults' => array(
                    'type' => 'array',
                    'location' => 'xml',
                    'items' => array(
                        'name' => 'EvaluationResult',
                        'type' => 'object',
                        'sentAs' => 'member',
                        'properties' => array(
                            'EvalActionName' => array(
                                'type' => 'string',
                            ),
                            'EvalResourceName' => array(
                                'type' => 'string',
                            ),
                            'EvalDecision' => array(
                                'type' => 'string',
                            ),
                            'MatchedStatements' => array(
                                'type' => 'array',
                                'items' => array(
                                    'name' => 'Statement',
                                    'type' => 'object',
                                    'sentAs' => 'member',
                                    'properties' => array(
                                        'SourcePolicyId' => array(
                                            'type' => 'string',
                                        ),
                                        'SourcePolicyType' => array(
                                            'type' => 'string',
                                        ),
                                        'StartPosition' => array(
                                            'type' => 'object',
                                            'properties' => array(
                                                'Line' => array(
                                                    'type' => 'numeric',
                                                ),
                                                'Column' => array(
                                                    'type' => 'numeric',
                                                ),
                                            ),
                                        ),
                                        'EndPosition' => array(
                                            'type' => 'object',
                                            'properties' => array(
                                                'Line' => array(
                                                    'type' => 'numeric',
                                                ),
                                                'Column' => array(
                                                    'type' => 'numeric',
                                                ),
                                            ),
                                        ),
                                    ),
                                ),
                            ),
                            'MissingContextValues' => array(
                                'type' => 'array',
                                'items' => array(
                                    'name' => 'ContextKeyNameType',
                                    'type' => 'string',
                                    'sentAs' => 'member',
                                ),
                            ),
                            'EvalDecisionDetails' => array(
                                'type' => 'array',
                                'filters' => array(
                                    array(
                                        'method' => 'Aws\\Common\\Command\\XmlResponseLocationVisitor::xmlMap',
                                        'args' => array(
                                            '@value',
                                            'entry',
                                            'key',
                                            'value',
                                        ),
                                    ),
                                ),
                                'items' => array(
                                    'name' => 'entry',
                                    'type' => 'object',
                                    'sentAs' => 'entry',
                                    'additionalProperties' => true,
                                    'properties' => array(
                                        'key' => array(
                                            'type' => 'string',
                                        ),
                                        'value' => array(
                                            'type' => 'string',
                                        ),
                                    ),
                                ),
                                'additionalProperties' => false,
                            ),
                            'ResourceSpecificResults' => array(
                                'type' => 'array',
                                'items' => array(
                                    'name' => 'ResourceSpecificResult',
                                    'type' => 'object',
                                    'sentAs' => 'member',
                                    'properties' => array(
                                        'EvalResourceName' => array(
                                            'type' => 'string',
                                        ),
                                        'EvalResourceDecision' => array(
                                            'type' => 'string',
                                        ),
                                        'MatchedStatements' => array(
                                            'type' => 'array',
                                            'items' => array(
                                                'name' => 'Statement',
                                                'type' => 'object',
                                                'sentAs' => 'member',
                                                'properties' => array(
                                                    'SourcePolicyId' => array(
                                                        'type' => 'string',
                                                    ),
                                                    'SourcePolicyType' => array(
                                                        'type' => 'string',
                                                    ),
                                                    'StartPosition' => array(
                                                        'type' => 'object',
                                                        'properties' => array(
                                                            'Line' => array(
                                                                'type' => 'numeric',
                                                            ),
                                                            'Column' => array(
                                                                'type' => 'numeric',
                                                            ),
                                                        ),
                                                    ),
                                                    'EndPosition' => array(
                                                        'type' => 'object',
                                                        'properties' => array(
                                                            'Line' => array(
                                                                'type' => 'numeric',
                                                            ),
                                                            'Column' => array(
                                                                'type' => 'numeric',
                                                            ),
                                                        ),
                                                    ),
                                                ),
                                            ),
                                        ),
                                        'MissingContextValues' => array(
                                            'type' => 'array',
                                            'items' => array(
                                                'name' => 'ContextKeyNameType',
                                                'type' => 'string',
                                                'sentAs' => 'member',
                                            ),
                                        ),
                                        'EvalDecisionDetails' => array(
                                            'type' => 'array',
                                            'filters' => array(
                                                array(
                                                    'method' => 'Aws\\Common\\Command\\XmlResponseLocationVisitor::xmlMap',
                                                    'args' => array(
                                                        '@value',
                                                        'entry',
                                                        'key',
                                                        'value',
                                                    ),
                                                ),
                                            ),
                                            'items' => array(
                                                'name' => 'entry',
                                                'type' => 'object',
                                                'sentAs' => 'entry',
                                                'additionalProperties' => true,
                                                'properties' => array(
                                                    'key' => array(
                                                        'type' => 'string',
                                                    ),
                                                    'value' => array(
                                                        'type' => 'string',
                                                    ),
                                                ),
                                            ),
                                            'additionalProperties' => false,
                                        ),
                                    ),
                                ),
                            ),
                        ),
                    ),
                ),
                'IsTruncated' => array(
                    'type' => 'boolean',
                    'location' => 'xml',
                ),
                'Marker' => array(
                    'type' => 'string',
                    'location' => 'xml',
                ),
            ),
        ),
        'UpdateSAMLProviderResponse' => array(
            'type' => 'object',
            'additionalProperties' => true,
            'properties' => array(
                'SAMLProviderArn' => array(
                    'type' => 'string',
                    'location' => 'xml',
                ),
            ),
        ),
        'UploadSSHPublicKeyResponse' => array(
            'type' => 'object',
            'additionalProperties' => true,
            'properties' => array(
                'SSHPublicKey' => array(
                    'type' => 'object',
                    'location' => 'xml',
                    'properties' => array(
                        'UserName' => array(
                            'type' => 'string',
                        ),
                        'SSHPublicKeyId' => array(
                            'type' => 'string',
                        ),
                        'Fingerprint' => array(
                            'type' => 'string',
                        ),
                        'SSHPublicKeyBody' => array(
                            'type' => 'string',
                        ),
                        'Status' => array(
                            'type' => 'string',
                        ),
                        'UploadDate' => array(
                            'type' => 'string',
                        ),
                    ),
                ),
            ),
        ),
        'UploadServerCertificateResponse' => array(
            'type' => 'object',
            'additionalProperties' => true,
            'properties' => array(
                'ServerCertificateMetadata' => array(
                    'type' => 'object',
                    'location' => 'xml',
                    'properties' => array(
                        'Path' => array(
                            'type' => 'string',
                        ),
                        'ServerCertificateName' => array(
                            'type' => 'string',
                        ),
                        'ServerCertificateId' => array(
                            'type' => 'string',
                        ),
                        'Arn' => array(
                            'type' => 'string',
                        ),
                        'UploadDate' => array(
                            'type' => 'string',
                        ),
                        'Expiration' => array(
                            'type' => 'string',
                        ),
                    ),
                ),
            ),
        ),
        'UploadSigningCertificateResponse' => array(
            'type' => 'object',
            'additionalProperties' => true,
            'properties' => array(
                'Certificate' => array(
                    'type' => 'object',
                    'location' => 'xml',
                    'properties' => array(
                        'UserName' => array(
                            'type' => 'string',
                        ),
                        'CertificateId' => array(
                            'type' => 'string',
                        ),
                        'CertificateBody' => array(
                            'type' => 'string',
                        ),
                        'Status' => array(
                            'type' => 'string',
                        ),
                        'UploadDate' => array(
                            'type' => 'string',
                        ),
                    ),
                ),
            ),
        ),
    ),
    'iterators' => array(
        'GetGroup' => array(
            'input_token' => 'Marker',
            'output_token' => 'Marker',
            'more_results' => 'IsTruncated',
            'limit_key' => 'MaxItems',
            'result_key' => 'Users',
        ),
        'ListAccessKeys' => array(
            'input_token' => 'Marker',
            'output_token' => 'Marker',
            'more_results' => 'IsTruncated',
            'limit_key' => 'MaxItems',
            'result_key' => 'AccessKeyMetadata',
        ),
        'ListAccountAliases' => array(
            'input_token' => 'Marker',
            'output_token' => 'Marker',
            'more_results' => 'IsTruncated',
            'limit_key' => 'MaxItems',
            'result_key' => 'AccountAliases',
        ),
        'ListAttachedGroupPolicies' => array(
            'input_token' => 'Marker',
            'output_token' => 'Marker',
            'more_results' => 'IsTruncated',
            'limit_key' => 'MaxItems',
            'result_key' => 'AttachedPolicies',
        ),
        'ListAttachedRolePolicies' => array(
            'input_token' => 'Marker',
            'output_token' => 'Marker',
            'more_results' => 'IsTruncated',
            'limit_key' => 'MaxItems',
            'result_key' => 'AttachedPolicies',
        ),
        'ListAttachedUserPolicies' => array(
            'input_token' => 'Marker',
            'output_token' => 'Marker',
            'more_results' => 'IsTruncated',
            'limit_key' => 'MaxItems',
            'result_key' => 'AttachedPolicies',
        ),
        'ListEntitiesForPolicy' => array(
            'input_token' => 'Marker',
            'output_token' => 'Marker',
            'more_results' => 'IsTruncated',
            'limit_key' => 'MaxItems',
            'result_key' => array(
                'PolicyGroups',
                'PolicyUsers',
                'PolicyRoles',
            ),
        ),
        'ListGroupPolicies' => array(
            'input_token' => 'Marker',
            'output_token' => 'Marker',
            'more_results' => 'IsTruncated',
            'limit_key' => 'MaxItems',
            'result_key' => 'PolicyNames',
        ),
        'ListGroups' => array(
            'input_token' => 'Marker',
            'output_token' => 'Marker',
            'more_results' => 'IsTruncated',
            'limit_key' => 'MaxItems',
            'result_key' => 'Groups',
        ),
        'ListGroupsForUser' => array(
            'input_token' => 'Marker',
            'output_token' => 'Marker',
            'more_results' => 'IsTruncated',
            'limit_key' => 'MaxItems',
            'result_key' => 'Groups',
        ),
        'ListInstanceProfiles' => array(
            'input_token' => 'Marker',
            'output_token' => 'Marker',
            'more_results' => 'IsTruncated',
            'limit_key' => 'MaxItems',
            'result_key' => 'InstanceProfiles',
        ),
        'ListInstanceProfilesForRole' => array(
            'input_token' => 'Marker',
            'output_token' => 'Marker',
            'more_results' => 'IsTruncated',
            'limit_key' => 'MaxItems',
            'result_key' => 'InstanceProfiles',
        ),
        'ListMFADevices' => array(
            'input_token' => 'Marker',
            'output_token' => 'Marker',
            'more_results' => 'IsTruncated',
            'limit_key' => 'MaxItems',
            'result_key' => 'MFADevices',
        ),
        'ListPolicies' => array(
            'input_token' => 'Marker',
            'output_token' => 'Marker',
            'more_results' => 'IsTruncated',
            'limit_key' => 'MaxItems',
            'result_key' => 'Policies',
        ),
        'ListRolePolicies' => array(
            'input_token' => 'Marker',
            'output_token' => 'Marker',
            'more_results' => 'IsTruncated',
            'limit_key' => 'MaxItems',
            'result_key' => 'PolicyNames',
        ),
        'ListRoles' => array(
            'input_token' => 'Marker',
            'output_token' => 'Marker',
            'more_results' => 'IsTruncated',
            'limit_key' => 'MaxItems',
            'result_key' => 'Roles',
        ),
        'ListSAMLProviders' => array(
            'result_key' => 'SAMLProviderList',
        ),
        'ListServerCertificates' => array(
            'input_token' => 'Marker',
            'output_token' => 'Marker',
            'more_results' => 'IsTruncated',
            'limit_key' => 'MaxItems',
            'result_key' => 'ServerCertificateMetadataList',
        ),
        'ListSigningCertificates' => array(
            'input_token' => 'Marker',
            'output_token' => 'Marker',
            'more_results' => 'IsTruncated',
            'limit_key' => 'MaxItems',
            'result_key' => 'Certificates',
        ),
        'ListUserPolicies' => array(
            'input_token' => 'Marker',
            'output_token' => 'Marker',
            'more_results' => 'IsTruncated',
            'limit_key' => 'MaxItems',
            'result_key' => 'PolicyNames',
        ),
        'ListUsers' => array(
            'input_token' => 'Marker',
            'output_token' => 'Marker',
            'more_results' => 'IsTruncated',
            'limit_key' => 'MaxItems',
            'result_key' => 'Users',
        ),
        'ListVirtualMFADevices' => array(
            'input_token' => 'Marker',
            'output_token' => 'Marker',
            'more_results' => 'IsTruncated',
            'limit_key' => 'MaxItems',
            'result_key' => 'VirtualMFADevices',
        ),
        'GetAccountAuthorizationDetails' => array(
            'input_token' => 'Marker',
            'output_token' => 'Marker',
            'more_results' => 'IsTruncated',
            'limit_key' => 'MaxItems',
        ),
    ),
);
includes/lib/aws-sdk-php-2.8.31/src/Aws/Iam/Exception/NoSuchEntityException.php000064400000001443151327705670022775 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\Iam\Exception;

/**
 * The request was rejected because it referenced an entity that does not exist. The error message describes the entity.
 */
class NoSuchEntityException extends IamException {}
includes/lib/aws-sdk-php-2.8.31/src/Aws/Iam/Exception/MalformedPolicyDocumentException.php000064400000001454151327705670025170 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\Iam\Exception;

/**
 * The request was rejected because the policy document was malformed. The error message describes the specific error.
 */
class MalformedPolicyDocumentException extends IamException {}
includes/lib/aws-sdk-php-2.8.31/src/Aws/Iam/Exception/InvalidCertificateException.php000064400000001360151327705670024130 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\Iam\Exception;

/**
 * The request was rejected because the certificate is invalid.
 */
class InvalidCertificateException extends IamException {}
includes/lib/aws-sdk-php-2.8.31/src/Aws/Iam/Exception/EntityAlreadyExistsException.php000064400000001414151327705670024355 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\Iam\Exception;

/**
 * The request was rejected because it attempted to create a resource that already exists.
 */
class EntityAlreadyExistsException extends IamException {}
includes/lib/aws-sdk-php-2.8.31/src/Aws/Iam/Exception/InvalidUserTypeException.php000064400000001405151327705670023466 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\Iam\Exception;

/**
 * The request was rejected because the type of user for the transaction was incorrect.
 */
class InvalidUserTypeException extends IamException {}
includes/lib/aws-sdk-php-2.8.31/src/Aws/Iam/Exception/LimitExceededException.php000064400000001507151327705670023107 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\Iam\Exception;

/**
 * The request was rejected because it attempted to create resources beyond the current AWS account limits. The error message describes the limit exceeded.
 */
class LimitExceededException extends IamException {}
includes/lib/aws-sdk-php-2.8.31/src/Aws/Iam/Exception/InvalidAuthenticationCodeException.php000064400000001467151327705670025470 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\Iam\Exception;

/**
 * The request was rejected because the authentication code was not recognized. The error message describes the specific error.
 */
class InvalidAuthenticationCodeException extends IamException {}
includes/lib/aws-sdk-php-2.8.31/src/Aws/Iam/Exception/DuplicateCertificateException.php000064400000001434151327705670024456 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\Iam\Exception;

/**
 * The request was rejected because the same certificate is associated to another user under the account.
 */
class DuplicateCertificateException extends IamException {}
includes/lib/aws-sdk-php-2.8.31/src/Aws/Iam/Exception/EntityTemporarilyUnmodifiableException.php000064400000001744151327705670026430 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\Iam\Exception;

/**
 * The request was rejected because it referenced an entity that is temporarily unmodifiable, such as a user name that was deleted and then recreated. The error indicates that the request is likely to succeed if you try again after waiting several minutes. The error message describes the entity.
 */
class EntityTemporarilyUnmodifiableException extends IamException {}
includes/lib/aws-sdk-php-2.8.31/src/Aws/Iam/Exception/DeleteConflictException.php000064400000001506151327705670023265 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\Iam\Exception;

/**
 * The request was rejected because it attempted to delete a resource that has attached subordinate entities. The error message describes these entities.
 */
class DeleteConflictException extends IamException {}
includes/lib/aws-sdk-php-2.8.31/src/Aws/Iam/Exception/IamException.php000064400000001404151327705670021104 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\Iam\Exception;

use Aws\Common\Exception\ServiceResponseException;

/**
 * Default service exception class
 */
class IamException extends ServiceResponseException {}
includes/lib/aws-sdk-php-2.8.31/src/Aws/Iam/Exception/InvalidInputException.php000064400000001365151327705670023012 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\Iam\Exception;

/**
 * Exception that occurs when a InvalidInputException error is encountered
 */
class InvalidInputException extends IamException {}
includes/lib/aws-sdk-php-2.8.31/src/Aws/Iam/Exception/PasswordPolicyViolationException.php000064400000001465151327705670025254 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\Iam\Exception;

/**
 * The request was rejected because the provided password did not meet the requirements imposed by the account password policy.
 */
class PasswordPolicyViolationException extends IamException {}
includes/lib/aws-sdk-php-2.8.31/src/Aws/Iam/Exception/MalformedCertificateException.php000064400000001460151327705670024451 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\Iam\Exception;

/**
 * The request was rejected because the certificate was malformed or expired. The error message describes the specific error.
 */
class MalformedCertificateException extends IamException {}
includes/lib/aws-sdk-php-2.8.31/src/Aws/Iam/Exception/KeyPairMismatchException.php000064400000001416151327705670023433 0ustar00<?php
/**
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

namespace Aws\Iam\Exception;

/**
 * The request was rejected because the public key certificate and the private key do not match.
 */
class KeyPairMismatchException extends IamException {}
includes/class-wpvivid-backup.php000064400000347261151327705670013153 0ustar00<?php

if (!defined('WPVIVID_PLUGIN_DIR'))
{
    die;
}

define('WPVIVID_BACKUP_TYPE_DB','backup_db');
define('WPVIVID_BACKUP_TYPE_THEMES','backup_themes');
define('WPVIVID_BACKUP_TYPE_PLUGIN','backup_plugin');
define('WPVIVID_BACKUP_TYPE_UPLOADS','backup_uploads');
define('WPVIVID_BACKUP_TYPE_UPLOADS_FILES','backup_uploads_files');
//define('WPVIVID_BACKUP_TYPE_UPLOADS_FILES_OTHER','backup_uploads_files_other');
define('WPVIVID_BACKUP_TYPE_CONTENT','backup_content');
define('WPVIVID_BACKUP_TYPE_CORE','backup_core');
define('WPVIVID_BACKUP_TYPE_OTHERS','backup_others');
define('WPVIVID_BACKUP_TYPE_MERGE','backup_merge');

define('WPVIVID_BACKUP_ROOT_WP_CONTENT','wp-content');
define('WPVIVID_BACKUP_ROOT_CUSTOM','custom');
define('WPVIVID_BACKUP_ROOT_WP_ROOT','root');
define('WPVIVID_BACKUP_ROOT_WP_UPLOADS','uploads');
class WPvivid_Backup_Task
{
    protected $task;

    public $backup_type_collect;

    public function __construct($task_id=false,$task=false)
    {
        if($task_id!==false)
        {
            $this->task=WPvivid_taskmanager::get_task($task_id);
        }

        if($task!==false)
        {
            $this->task=$task;
        }

        $this->backup_type_collect[WPVIVID_BACKUP_TYPE_DB]=1;
        $this->backup_type_collect[WPVIVID_BACKUP_TYPE_THEMES]=1;
        $this->backup_type_collect[WPVIVID_BACKUP_TYPE_PLUGIN]=1;
        $this->backup_type_collect[WPVIVID_BACKUP_TYPE_UPLOADS]=1;
        $this->backup_type_collect[WPVIVID_BACKUP_TYPE_UPLOADS_FILES]=1;
        //$this->backup_type_collect[WPVIVID_BACKUP_TYPE_UPLOADS_FILES_OTHER]=1;
        $this->backup_type_collect[WPVIVID_BACKUP_TYPE_CONTENT]=1;
        $this->backup_type_collect[WPVIVID_BACKUP_TYPE_CORE]=1;
        $this->backup_type_collect[WPVIVID_BACKUP_TYPE_OTHERS]=1;
        $this->backup_type_collect[WPVIVID_BACKUP_TYPE_MERGE]=1;

        add_filter('wpvivid_set_backup', array($this, 'wpvivid_set_backup'),10);
        add_filter('wpvivid_exclude_plugins',array($this,'exclude_plugins'),20);
        add_filter('wpvivid_get_backup_exclude_regex',array($this, 'get_backup_exclude_regex'),10,2);
    }

    public function get_backup_exclude_regex($exclude_regex,$backup_type)
    {
        if($backup_type==WPVIVID_BACKUP_TYPE_UPLOADS||$backup_type==WPVIVID_BACKUP_TYPE_UPLOADS_FILES)
        {
            $upload_dir = wp_upload_dir();
            $backup_data['files_root']=$this -> transfer_path($upload_dir['basedir']);
            $exclude_regex[]='#^'.preg_quote($this -> transfer_path($upload_dir['basedir']).DIRECTORY_SEPARATOR.'backwpup', '/').'#';  // BackWPup backup directory
            $exclude_regex[]='#^'.preg_quote($this -> transfer_path($upload_dir['basedir']).DIRECTORY_SEPARATOR.'ShortpixelBackups', '/').'#';//ShortpixelBackups
            $exclude_regex[]='#^'.preg_quote($this -> transfer_path($upload_dir['basedir']).DIRECTORY_SEPARATOR.'backup', '/').'#';
            $exclude_regex[]='#^'.preg_quote($this -> transfer_path($upload_dir['basedir']).DIRECTORY_SEPARATOR.'backwpup', '/').'#';  // BackWPup backup directory
            $exclude_regex[]='#^'.preg_quote($this -> transfer_path($upload_dir['basedir']).DIRECTORY_SEPARATOR.'backup-guard', '/').'#';  // Wordpress Backup and Migrate Plugin backup directory
        }
        else if($backup_type==WPVIVID_BACKUP_TYPE_CONTENT)
        {
            $exclude_regex[]='#^'.preg_quote($this -> transfer_path(WP_CONTENT_DIR).DIRECTORY_SEPARATOR.'updraft', '/').'#';   // Updraft Plus backup directory
            $exclude_regex[]='#^'.preg_quote($this -> transfer_path(WP_CONTENT_DIR).DIRECTORY_SEPARATOR.'ai1wm-backups', '/').'#'; // All-in-one WP migration backup directory
            $exclude_regex[]='#^'.preg_quote($this -> transfer_path(WP_CONTENT_DIR).DIRECTORY_SEPARATOR.'backups', '/').'#'; // Xcloner backup directory
            $exclude_regex[]='#^'.preg_quote($this -> transfer_path(WP_CONTENT_DIR).DIRECTORY_SEPARATOR.'upgrade', '/').'#';
            $exclude_regex[]='#^'.preg_quote($this -> transfer_path(WP_CONTENT_DIR).DIRECTORY_SEPARATOR.'wpvivid', '/').'#';
            $exclude_regex[]='#^'.preg_quote($this -> transfer_path(WP_CONTENT_DIR).DIRECTORY_SEPARATOR.WPvivid_Setting::get_backupdir(), '/').'#';
            $exclude_regex[]='#^'.preg_quote($this -> transfer_path(WP_CONTENT_DIR).DIRECTORY_SEPARATOR.'plugins', '/').'#';
            $exclude_regex[]='#^'.preg_quote($this -> transfer_path(WP_CONTENT_DIR).DIRECTORY_SEPARATOR.'cache', '/').'#';
            $exclude_regex[]='#^'.preg_quote($this -> transfer_path(WP_CONTENT_DIR).DIRECTORY_SEPARATOR.'wphb-cache', '/').'#';
            $exclude_regex[]='#^'.preg_quote($this -> transfer_path(WP_CONTENT_DIR).DIRECTORY_SEPARATOR.'backup', '/').'#';
            $exclude_regex[]='#^'.preg_quote($this -> transfer_path(WP_CONTENT_DIR).DIRECTORY_SEPARATOR.'Dropbox_Backup', '/').'#';
            $exclude_regex[]='#^'.preg_quote($this -> transfer_path(WP_CONTENT_DIR).DIRECTORY_SEPARATOR.'mysql.sql', '/').'#';//mysql

            $exclude_regex[]='#^'.preg_quote($this -> transfer_path(WP_CONTENT_DIR).DIRECTORY_SEPARATOR.'backup-migration', '/').'#';
            $exclude_regex[]='#^'.preg_quote($this -> transfer_path(WP_CONTENT_DIR).DIRECTORY_SEPARATOR.'backups-dup-lite', '/').'#';

            if(defined('WPVIVID_UPLOADS_ISO_DIR'))
            {
                $exclude_regex[]='#^'.preg_quote($this -> transfer_path(WP_CONTENT_DIR).DIRECTORY_SEPARATOR.WPVIVID_UPLOADS_ISO_DIR, '/').'#';
            }
            $upload_dir = wp_upload_dir();
            $exclude_regex[]='#^'.preg_quote($this -> transfer_path($upload_dir['basedir']), '/').'$#';
            $exclude_regex[]='#^'.preg_quote($this->transfer_path(get_theme_root()), '/').'#';
        }

        return $exclude_regex;
    }

    public function exclude_plugins($exclude_plugins)
    {
        if(in_array('wpvivid-backuprestore',$exclude_plugins))
        {
            $exclude_plugins[]='wpvivid-backuprestore';
        }

        if(in_array('wp-cerber',$exclude_plugins))
        {
            $exclude_plugins[]='wp-cerber';
        }

        if(in_array('.',$exclude_plugins))
        {
            $exclude_plugins[]='.';
        }

        if(in_array('wpvivid-backup-pro',$exclude_plugins))
        {
            $exclude_plugins[]='wpvivid-backup-pro';
        }

        return $exclude_plugins;
    }

    public function get_id()
    {
        return $this->task['id'];
    }

    public function new_backup_task($options,$type,$action='backup')
    {
        $id=uniqid('wpvivid-');
        $this->task=false;
        $this->task['id']=$id;
        $this->task['action']=$action;
        $this->task['type']=$type;

        $this->task['status']['start_time']=time();
        $this->task['status']['run_time']=time();
        $this->task['status']['timeout']=time();
        $this->task['status']['str']='ready';
        $this->task['status']['resume_count']=0;

        if(isset($options['remote']))
        {
            if($options['remote']=='1')
            {
                if(isset($options['remote_options']))
                {
                    $this->task['options']['remote_options']=$options['remote_options'];
                }
                else
                {
                    $this->task['options']['remote_options']=WPvivid_Setting::get_remote_options();
                }

            }
            else {
                $this->task['options']['remote_options']=false;
            }
        }
        else
        {
            $this->task['options']['remote_options']=false;
        }

        $this->task['options']['remote_options'] = apply_filters('wpvivid_set_remote_options', $this->task['options']['remote_options'],$options);

        if(isset($options['local']))
        {
            if($options['local']=='1')
            {
                $this->task['options']['save_local']=1;
            }
            else
            {
                $this->task['options']['save_local']=0;
            }
        }
        else
        {
            $this->task['options']['save_local']=1;
        }

        if(isset($options['lock']))
        {
            $this->task['options']['lock']=$options['lock'];
        }
        else
        {
            $this->task['options']['lock']=0;
        }

        $general_setting=WPvivid_Setting::get_setting(true, "");

        if(isset($options['backup_prefix']) && !empty($options['backup_prefix']))
        {
            $backup_prefix=$options['backup_prefix'];
        }
        else
        {
            if(isset($general_setting['options']['wpvivid_common_setting']['domain_include'])&&$general_setting['options']['wpvivid_common_setting']['domain_include'])
            {
                $check_addon = apply_filters('wpvivid_check_setting_addon', 'not_addon');
                if (isset($general_setting['options']['wpvivid_common_setting']['backup_prefix']) && $check_addon == 'addon')
                {
                    $backup_prefix = $general_setting['options']['wpvivid_common_setting']['backup_prefix'];
                }
                else {
                    $home_url_prefix = get_home_url();
                    $home_url_prefix = $this->parse_url_all($home_url_prefix);
                    $backup_prefix = $home_url_prefix;
                }
            }
            else
            {
                $backup_prefix='';
            }
        }
        $this->task['options']['backup_prefix']=$backup_prefix;
        $offset=get_option('gmt_offset');
        if(empty($backup_prefix))
            $this->task['options']['file_prefix'] = $this->task['id'] . '_' . gmdate('Y-m-d-H-i', $this->task['status']['start_time']+$offset*60*60);
        else
            $this->task['options']['file_prefix'] = $backup_prefix . '_' . $this->task['id'] . '_' . gmdate('Y-m-d-H-i', $this->task['status']['start_time']+$offset*60*60);

        $this->task['options']['file_prefix'] = apply_filters('wpvivid_backup_file_prefix',$this->task['options']['file_prefix'],$backup_prefix,$this->task['id'],$this->task['status']['start_time']);

        if(isset($general_setting['options']['wpvivid_common_setting']['ismerge']))
        {
            if($general_setting['options']['wpvivid_common_setting']['ismerge']==1)
            {
                $this->task['options']['backup_options']['ismerge']=1;
            }
            else {
                $this->task['options']['backup_options']['ismerge']=0;
            }
        }
        else {
            $this->task['options']['backup_options']['ismerge']=1;
        }
        $this->task['options']['backup_options']['ismerge']=apply_filters('wpvivid_set_backup_ismerge',$this->task['options']['backup_options']['ismerge'],$options);

        $this->task['options']['log_file_name']=$id.'_backup';
        $log=new WPvivid_Log();
        $log->CreateLogFile($this->task['options']['log_file_name'],'no_folder','backup');
        //$log->WriteLog(get_home_path(),'test');
        $this->task['options']['backup_options']['prefix']=$this->task['options']['file_prefix'];
        $this->task['options']['backup_options']['compress']=WPvivid_Setting::get_option('wpvivid_compress_setting');
        $this->task['options']['backup_options']['dir']=WPvivid_Setting::get_backupdir();
        $this->task['options']['backup_options']['backup']=array();

        if(isset($options['backup_files']))
        {
            if($options['backup_files']=='files+db')
            {
                $this->set_backup(WPVIVID_BACKUP_TYPE_DB);
                $this->set_backup(WPVIVID_BACKUP_TYPE_THEMES);
                $this->set_backup(WPVIVID_BACKUP_TYPE_PLUGIN);
                $general_setting=WPvivid_Setting::get_setting(true, "");
                if(isset($general_setting['options']['wpvivid_compress_setting']['subpackage_plugin_upload']) && !empty($general_setting['options']['wpvivid_compress_setting']['subpackage_plugin_upload'])){
                    if($general_setting['options']['wpvivid_compress_setting']['subpackage_plugin_upload']){
                        $this->set_backup(WPVIVID_BACKUP_TYPE_UPLOADS_FILES);
                        //$this->set_backup(WPVIVID_BACKUP_TYPE_UPLOADS_FILES_OTHER);
                    }
                    else{
                        $this->set_backup(WPVIVID_BACKUP_TYPE_UPLOADS);
                    }
                }
                else{
                    $this->set_backup(WPVIVID_BACKUP_TYPE_UPLOADS);
                }
                $this->set_backup(WPVIVID_BACKUP_TYPE_CONTENT);
                $this->set_backup(WPVIVID_BACKUP_TYPE_CORE);
            }
            else if($options['backup_files']=='files')
            {
                $this->set_backup(WPVIVID_BACKUP_TYPE_THEMES);
                $this->set_backup(WPVIVID_BACKUP_TYPE_PLUGIN);
                $general_setting=WPvivid_Setting::get_setting(true, "");
                if(isset($general_setting['options']['wpvivid_compress_setting']['subpackage_plugin_upload']) && !empty($general_setting['options']['wpvivid_compress_setting']['subpackage_plugin_upload'])){
                    if($general_setting['options']['wpvivid_compress_setting']['subpackage_plugin_upload']){
                        $this->set_backup(WPVIVID_BACKUP_TYPE_UPLOADS_FILES);
                        //$this->set_backup(WPVIVID_BACKUP_TYPE_UPLOADS_FILES_OTHER);
                    }
                    else{
                        $this->set_backup(WPVIVID_BACKUP_TYPE_UPLOADS);
                    }
                }
                else{
                    $this->set_backup(WPVIVID_BACKUP_TYPE_UPLOADS);
                }
                $this->set_backup(WPVIVID_BACKUP_TYPE_CONTENT);
                $this->set_backup(WPVIVID_BACKUP_TYPE_CORE);
            }
            else if($options['backup_files']=='db')
            {
                $this->set_backup(WPVIVID_BACKUP_TYPE_DB);
            }
        }
        else
        {
            $this->task['options']['backup_options'] = apply_filters('wpvivid_set_backup_type', $this->task['options']['backup_options'],$options);
        }

        $this->task['data']['doing']='backup';
        $this->task['data']['backup']['doing']='';
        $this->task['data']['backup']['finished']=0;
        $this->task['data']['backup']['progress']=0;
        $this->task['data']['backup']['job_data']=array();
        $this->task['data']['backup']['sub_job']=array();
        $this->task['data']['backup']['db_size']='0';
        $this->task['data']['backup']['files_size']['sum']='0';
        $this->task['data']['upload']['doing']='';
        $this->task['data']['upload']['finished']=0;
        $this->task['data']['upload']['progress']=0;
        $this->task['data']['upload']['job_data']=array();
        $this->task['data']['upload']['sub_job']=array();
        WPvivid_Setting::update_task($id,$this->task);
        $ret['result']='success';
        $ret['task_id']=$this->task['id'];
        $log->CloseFile();
        return $ret;
    }

    protected function parse_url_all($url)
    {
        $parse = wp_parse_url($url);
        //$path=str_replace('/','_',$parse['path']);
        $path = '';
        if(isset($parse['path'])) {
            $parse['path'] = str_replace('/', '_', $parse['path']);
            $path = $parse['path'];
        }
        return $parse['host'].$path;
    }

    public function set_backup($backup, $task_type='')
    {
        if(is_string($backup))
        {
            if(!function_exists('get_home_path'))
                require_once(ABSPATH . 'wp-admin/includes/file.php');
            $backup_data['key']=$backup;
            $backup_data['result']=false;
            $backup_data['compress']=$this->task['options']['backup_options']['compress'];
            $backup_data['finished']=0;
            $backup_data['path']=WP_CONTENT_DIR.DIRECTORY_SEPARATOR. $this->task['options']['backup_options']['dir'].DIRECTORY_SEPARATOR;
            if($backup==WPVIVID_BACKUP_TYPE_DB)
            {
                //$backup_data['root_path']=WP_CONTENT_DIR.DIRECTORY_SEPARATOR.$this->task['options']['backup_options']['dir'];
                $backup_data['root_flag']=WPVIVID_BACKUP_ROOT_CUSTOM;
                $backup_data['dump_db']=1;
                $backup_data['sql_file_name']=WP_CONTENT_DIR.DIRECTORY_SEPARATOR.$this->task['options']['backup_options']['dir'].DIRECTORY_SEPARATOR.$this->get_prefix().'_backup_db.sql';
                $backup_data['json_info']['dump_db']=1;
                $backup_data['json_info']['home_url']=home_url();
                $backup_data['json_info']['file_type']='databases';
                if(is_multisite())
                {
                    $backup_data['json_info']['is_mu']=1;
                }
                $backup_data['prefix']=$this->get_prefix().'_backup_db';
            }
            else if($backup==WPVIVID_BACKUP_TYPE_THEMES)
            {
                //$backup_data['root_path']=WP_CONTENT_DIR;
                $backup_data['root_flag']=WPVIVID_BACKUP_ROOT_WP_CONTENT;
                $backup_data['prefix']=$this->get_prefix().'_backup_themes';
                $backup_data['files_root']=$this->transfer_path(get_theme_root());
                $backup_data['exclude_regex']=array();
                $backup_data['include_regex']=array();
                $backup_data['json_info']['file_type']='themes';
                $backup_data['json_info']['themes']=$this->get_themes_list();
                $this->task['options']['backup_options']['backup'][$backup_data['key']]=$backup_data;
            }
            else if($backup==WPVIVID_BACKUP_TYPE_PLUGIN)
            {
                //$backup_data['root_path']=WP_CONTENT_DIR;
                $backup_data['root_flag']=WPVIVID_BACKUP_ROOT_WP_CONTENT;
                $backup_data['prefix']=$this->get_prefix().'_backup_plugin';
                if(isset($backup_data['compress']['subpackage_plugin_upload'])&&$backup_data['compress']['subpackage_plugin_upload'])
                {
                    $backup_data['plugin_subpackage']=1;
                }
                $backup_data['files_root']=$this->transfer_path(WP_PLUGIN_DIR);

                $exclude_plugins=array();
                $exclude_plugins=apply_filters('wpvivid_exclude_plugins',$exclude_plugins);
                $exclude_regex=array();
                foreach ($exclude_plugins as $exclude_plugin)
                {
                    $exclude_regex[]='#^'.preg_quote($this -> transfer_path(WP_PLUGIN_DIR.DIRECTORY_SEPARATOR.$exclude_plugin), '/').'#';
                }
                $backup_data['exclude_regex']=$exclude_regex;
                $backup_data['include_regex']=array();
                $backup_data['json_info']['file_type']='plugin';
                $backup_data['json_info']['plugin']=$this->get_plugins_list();
            }
            else if($backup==WPVIVID_BACKUP_TYPE_UPLOADS)
            {
                //$backup_data['root_path']=WP_CONTENT_DIR;
                $backup_data['root_flag']=WPVIVID_BACKUP_ROOT_WP_CONTENT;
                $backup_data['prefix']=$this->get_prefix().'_backup_uploads';
                $upload_dir = wp_upload_dir();
                $backup_data['files_root']=$this -> transfer_path($upload_dir['basedir']);

                $exclude_regex=array();
                $exclude_regex=apply_filters('wpvivid_get_backup_exclude_regex',$exclude_regex,WPVIVID_BACKUP_TYPE_UPLOADS);
                $backup_data['exclude_regex']=$exclude_regex;
                $backup_data['include_regex']=array();
                $backup_data['json_info']['file_type']='upload';
            }
            else if($backup==WPVIVID_BACKUP_TYPE_UPLOADS_FILES)
            {
                //$backup_data['root_path']=WP_CONTENT_DIR;
                $backup_data['root_flag']=WPVIVID_BACKUP_ROOT_WP_CONTENT;
                $backup_data['prefix']=$this->get_prefix().'_backup_uploads';
                $backup_data['uploads_subpackage']=1;
                $upload_dir = wp_upload_dir();
                $backup_data['files_root']=$this -> transfer_path($upload_dir['basedir']);
                $exclude_regex=array();
                $exclude_regex=apply_filters('wpvivid_get_backup_exclude_regex',$exclude_regex,WPVIVID_BACKUP_TYPE_UPLOADS_FILES);
                $backup_data['include_regex']=array();
                $backup_data['exclude_regex']=$exclude_regex;
                //$backup_data['include_regex'][]='#^'.preg_quote($this -> transfer_path($upload_dir['basedir']).DIRECTORY_SEPARATOR, '/').'[0-9]{4}#';
                $backup_data['json_info']['file_type']='upload';
            }
            else if($backup==WPVIVID_BACKUP_TYPE_CONTENT)
            {
                //$backup_data['root_path']=get_home_path();
                $backup_data['root_flag']=WPVIVID_BACKUP_ROOT_WP_ROOT;
                $backup_data['prefix']=$this->get_prefix().'_backup_content';
                $backup_data['files_root']=$this -> transfer_path(WP_CONTENT_DIR);
                $exclude_regex=array();
                $exclude_regex=apply_filters('wpvivid_get_backup_exclude_regex',$exclude_regex,WPVIVID_BACKUP_TYPE_CONTENT);
                $backup_data['exclude_regex']=$exclude_regex;
                $backup_data['include_regex']=array();
                $backup_data['json_info']['file_type']='wp-content';
            }
            else if($backup==WPVIVID_BACKUP_TYPE_CORE)
            {
                //$backup_data['root_path']=ABSPATH;
                $backup_data['root_flag']=WPVIVID_BACKUP_ROOT_WP_ROOT;
                $backup_data['prefix']=$this->get_prefix().'_backup_core';
                $backup_data['files_root']=$this -> transfer_path(ABSPATH);
                $backup_data['json_info']['include_path'][]='wp-includes';
                $backup_data['json_info']['include_path'][]='wp-admin';
                $backup_data['json_info']['wp_core']=1;
                $backup_data['json_info']['home_url']=home_url();
                $include_regex[]='#^'.preg_quote($this -> transfer_path(ABSPATH.DIRECTORY_SEPARATOR.'wp-admin'), '/').'#';
                $include_regex[]='#^'.preg_quote($this->transfer_path(ABSPATH.DIRECTORY_SEPARATOR.'wp-includes'), '/').'#';
                $exclude_regex[]='#^'.preg_quote($this->transfer_path(ABSPATH.DIRECTORY_SEPARATOR.'wp-admin'.DIRECTORY_SEPARATOR), '/').'pclzip-.*\.tmp#';
                $exclude_regex[]='#^'.preg_quote($this->transfer_path(ABSPATH.DIRECTORY_SEPARATOR.'wp-admin'.DIRECTORY_SEPARATOR), '/').'pclzip-.*\.gz#';
                $exclude_regex[]='#session_mm_cgi-fcgi#';
                $exclude_regex=apply_filters('wpvivid_get_backup_exclude_regex',$exclude_regex,WPVIVID_BACKUP_TYPE_CORE);
                $backup_data['exclude_regex']=$exclude_regex;
                $backup_data['include_regex']=$include_regex;
                $backup_data['json_info']['file_type']='wp-core';
            }
            else if($backup==WPVIVID_BACKUP_TYPE_MERGE)
            {
                //$backup_data['root_path']=WP_CONTENT_DIR.DIRECTORY_SEPARATOR.$this->task['options']['backup_options']['dir'];
                $backup_data['root_flag']=WPVIVID_BACKUP_ROOT_CUSTOM;
                $file_name = $this->get_prefix().'_backup_all';
                $file_name = apply_filters('wpvivid_set_incremental_backup_file_name', $file_name, $this->get_prefix(), $task_type);
                $backup_data['prefix']=$file_name;
                $backup_data['files']=array();
                $backup_data['json_info']['has_child']=1;
                foreach ($this->task['options']['backup_options']['backup'] as $backup_finished_data)
                {
                    $backup_data['files']=array_merge($backup_data['files'],$this->get_backup_file($backup_finished_data['key']));
                }
                $backup_data['json_info']['home_url']=home_url();
            }
            else
            {
                $backup_data=false;
            }
            if($backup_data!==false)
            {
                $backup_data=apply_filters('wpvivid_set_backup',$backup_data);
                $this->task['options']['backup_options']['backup'][$backup_data['key']]=$backup_data;
            }
        }
    }

    public function get_themes_list()
    {
        $themes_list=array();
        $list=wp_get_themes();
        foreach ($list as $key=>$item)
        {
            $themes_list[$key]['slug']=$key;
            $themes_list[$key]['size']=$this->get_folder_size(get_theme_root().DIRECTORY_SEPARATOR.$key,0);
        }
        return $themes_list;
    }

    public function get_plugins_list()
    {
        $plugins_list=array();

        if(!function_exists('get_plugins'))
            require_once(ABSPATH . 'wp-admin/includes/plugin.php');

        $list=get_plugins();

        $exclude_plugins[]='wpvivid-backuprestore';
        $exclude_plugins[]='wp-cerber';
        $exclude_plugins[]='.';
        $exclude_plugins=apply_filters('wpvivid_exclude_plugins',$exclude_plugins);

        foreach ($list as $key=>$item)
        {
            if(in_array(dirname($key),$exclude_plugins))
            {
                continue;
            }

            $plugins_list[dirname($key)]['slug']=dirname($key);
            $plugins_list[dirname($key)]['size']=$this->get_folder_size(WP_PLUGIN_DIR.DIRECTORY_SEPARATOR.dirname($key),0);
            //
        }
        return $plugins_list;
    }

    public function get_folder_size($root,$size)
    {
        $count = 0;
        if(is_dir($root))
        {
            $handler = opendir($root);
            if($handler!==false)
            {
                while (($filename = readdir($handler)) !== false)
                {
                    if ($filename != "." && $filename != "..") {
                        $count++;

                        if (is_dir($root . DIRECTORY_SEPARATOR . $filename))
                        {
                            $size=$this->get_folder_size($root . DIRECTORY_SEPARATOR . $filename,$size);
                        } else {
                            $size+=filesize($root . DIRECTORY_SEPARATOR . $filename);
                        }
                    }
                }
                if($handler)
                    @closedir($handler);
            }

        }
        return $size;
    }

    public function wpvivid_set_backup($backup_data)
    {
        if(isset($backup_data['uploads_subpackage'])||isset($backup_data['plugin_subpackage']))
        {
            $backup_data['resume_packages'] = 1;
        }
        else if($backup_data['key']==WPVIVID_BACKUP_TYPE_MERGE)
        {
            $backup_data['resume_packages'] = 1;
        }

        return $backup_data;
    }

    public function get_need_backup_files($backup_data)
    {
        if(isset($backup_data['uploads_subpackage']))
        {
            return $backup_data['files']=$this->get_need_uploads_backup_folder($backup_data);
        }

        if(isset($backup_data['plugin_subpackage']))
        {
            return $backup_data['files']=$this->get_need_backup_folder($backup_data);
        }

        if(isset($backup_data['files'])&&!empty($backup_data['files']))
        {
            return $backup_data['files'];
        }
        else
        {
            return $this->get_file_list($backup_data['files_root'],$backup_data['exclude_regex'],$backup_data['include_regex'],$this->task['options']['backup_options']['compress']['exclude_file_size']);
        }
    }

    public function get_backup_file($key)
    {
        $files=array();
        if(array_key_exists($key,$this->task['options']['backup_options']['backup']))
        {
            $backup=$this->task['options']['backup_options']['backup'][$key];
            if($backup['finished'])
            {
                if($backup['result']!=false)
                {
                    foreach ($backup['result']['files'] as $file_data)
                    {
                        $files[]=WP_CONTENT_DIR.DIRECTORY_SEPARATOR.$this->task['options']['backup_options']['dir'].DIRECTORY_SEPARATOR.$file_data['file_name'];
                    }
                }
            }
        }
        return $files;
    }

    public function get_need_backup()
    {
        $this->task=WPvivid_taskmanager::get_task($this->task['id']);
        $backup=false;
        $i_finished_backup_count=0;
        $i_count_of_backup=sizeof($this->task['options']['backup_options']['backup']);
        foreach ($this->task['options']['backup_options']['backup'] as $key=>$backup_data)
        {
            if($backup_data['result']!==false)
            {
                $ret=$backup_data['result'];
                if($ret['result']!==WPVIVID_SUCCESS)
                {
                    return false;
                }
            }

            if( $backup_data['finished']==0)
            {
                if($backup===false)
                {
                    add_filter('wpvivid_get_need_backup_files', array($this, 'wpvivid_get_need_backup_files'),10);
                    $backup_data=apply_filters('wpvivid_get_need_backup_files',$backup_data);

                    $backup=$backup_data;
                }
            }
            else {
                $i_finished_backup_count++;
            }
        }

        if($i_count_of_backup>0)
        {
            $i_progress=intval(1/$i_count_of_backup*100);
            WPvivid_taskmanager::update_backup_main_task_progress($this->task['id'],'backup',$i_progress*$i_finished_backup_count,0);

            if($i_finished_backup_count>=$i_count_of_backup)
            {
                if($this->task['options']['backup_options']['ismerge']==1)
                {
                    if(!array_key_exists(WPVIVID_BACKUP_TYPE_MERGE,$this->task['options']['backup_options']['backup']))
                    {
                        $this->set_backup(WPVIVID_BACKUP_TYPE_MERGE, $this->task['type']);
                        WPvivid_Setting::update_task($this->task['id'],$this->task);
                        $backup=$this->task['options']['backup_options']['backup'][WPVIVID_BACKUP_TYPE_MERGE];
                    }
                }
            }
        }
        return $backup;
    }

    public function get_need_backup_files_size($backup_data)
    {
        if(isset($backup_data['files'])&&!empty($backup_data['files']))
        {
            return $backup_data['files'];
        }
        else
        {
            return $this->get_file_list($backup_data['files_root'],$backup_data['exclude_regex'],$backup_data['include_regex'],$this->task['options']['backup_options']['compress']['exclude_file_size']);
        }
    }

    public function wpvivid_get_need_backup_files($backup_data)
    {
        if(!isset($backup_data['dump_db']))
        {
            if(array_key_exists($backup_data['key'],$this->backup_type_collect))
            {
                $backup_data['files'] = $this->get_need_backup_files($backup_data);
            }
            else
            {
                if(!isset($backup_data['files']))
                {
                    $backup_data['files']=array();
                }
                $backup_data['files'] =apply_filters('wpvivid_get_custom_need_backup_files', $backup_data['files'],$backup_data,$this->task['options']['backup_options']['compress']);
            }

            $need=false;
            add_filter('wpvivid_need_backup_files_update', array($this, 'need_backup_files_update'), 10, 2);
            if(apply_filters('wpvivid_need_backup_files_update',$need,$backup_data))
            {
                $this->task['options']['backup_options']['backup'][$backup_data['key']]=$backup_data;
                WPvivid_Setting::update_task($this->task['id'],$this->task);
            }
        }
        return $backup_data;
    }

    public function need_backup_files_update($need,$backup_data)
    {
        if(isset($backup_data['uploads_subpackage'])||isset($backup_data['plugin_subpackage']))
        {
            return true;
        }
        else
        {
            return $need;
        }
    }

    public function update_backup_result($backup_data,$result)
    {
        $this->task=WPvivid_taskmanager::get_task($this->task['id']);

        if(array_key_exists($backup_data['key'],$this->task['options']['backup_options']['backup']))
        {
            $this->task['options']['backup_options']['backup'][$backup_data['key']]['finished']=1;

            add_filter('wpvivid_backup_update_result', array($this, 'wpvivid_backup_update_result'),10,2);
            $result=apply_filters('wpvivid_backup_update_result',$result,$backup_data);
            $this->task['options']['backup_options']['backup'][$backup_data['key']]['result']=$result;

            WPvivid_taskmanager::update_task_options($this->task['id'],'backup_options', $this->task['options']['backup_options']);

            if($result['result']==WPVIVID_FAILED)
            {
                WPvivid_taskmanager::update_backup_task_status($this->task['id'],false,'error',false,false,$result['error']);
                return ;
            }
        }

        $i_finished_backup_count=0;
        $i_count_of_backup=sizeof($this->task['options']['backup_options']['backup']);

        foreach ($this->task['options']['backup_options']['backup'] as $backup_data)
        {
            if( $backup_data['finished']==1)
            {
                $i_finished_backup_count++;
            }
        }

        if($i_finished_backup_count>=$i_count_of_backup)
        {
            WPvivid_taskmanager::update_backup_main_task_progress($this->task['id'],'backup',100,1);
        }
        //WPvivid_Setting::update_task($this->task['id'],$this->task);
    }

    public function wpvivid_backup_update_result($result,$backup_data)
    {
        if($result['result']==WPVIVID_SUCCESS)
        {
            $exist_files_name=array();
            foreach ($result['files'] as $temp_file_data)
            {
                $exist_files_name[$temp_file_data['file_name']]=$temp_file_data['file_name'];
            }

            if(isset($backup_data['resume_packages'])&&$backup_data['resume_packages'])
            {
                $packages_files_info = $this->get_packages_files_info($backup_data['key']);
                if ($packages_files_info !== false)
                {
                    foreach ($packages_files_info as $file_data)
                    {
                        if(!array_key_exists($file_data['file_name'],$exist_files_name))
                        {
                            $result['files'][]=$file_data;
                        }
                    }
                }
            }
        }

        return $result;
    }

    public function get_backup_result()
    {
        $ret['result']=WPVIVID_SUCCESS;
        $ret['files']=array();
        foreach ($this->task['options']['backup_options']['backup'] as $backup_data)
        {
            if($this->task['options']['backup_options']['ismerge']==1)
            {
                if(WPVIVID_BACKUP_TYPE_MERGE==$backup_data['key'])
                {
                    $ret=$backup_data['result'];
                    if($ret['result']!==WPVIVID_SUCCESS)
                    {
                        return $ret;
                    }
                }
            }
            else
            {
                $ret['files']=array_merge($ret['files'],$backup_data['result']['files']);
            }
        }

        return $ret;
    }

    private function transfer_path($path)
    {
        $path = str_replace('\\','/',$path);
        $values = explode('/',$path);
        return implode(DIRECTORY_SEPARATOR,$values);
    }

    public function get_file_list($root,$exclude_regex,$include_regex,$exclude_file_size)
    {
        $files=array();
        $this->getFileLoop($files,$root,$exclude_regex,$include_regex,$exclude_file_size);
        return $files;
    }

    public function getFileLoop(&$files,$path,$exclude_regex=array(),$include_regex=array(),$exclude_file_size=0,$include_dir = true)
    {
        $count = 0;
        if(is_dir($path))
        {
            $handler = opendir($path);
            if($handler!==false)
            {
                while (($filename = readdir($handler)) !== false)
                {
                    if ($filename != "." && $filename != "..") {
                        $count++;

                        if (is_dir($path . DIRECTORY_SEPARATOR . $filename))
                        {
                            if ($this->regex_match($exclude_regex, $path . DIRECTORY_SEPARATOR . $filename, 0)) {
                                if ($this->regex_match($include_regex, $path . DIRECTORY_SEPARATOR . $filename, 1)) {
                                    $this->getFileLoop($files, $path . DIRECTORY_SEPARATOR . $filename, $exclude_regex, $include_regex, $exclude_file_size, $include_dir);
                                }
                            }
                        } else {
                            if($this->regex_match($exclude_regex, $path . DIRECTORY_SEPARATOR . $filename, 0)){
                                if ($exclude_file_size == 0)
                                {
                                    if(is_readable($path . DIRECTORY_SEPARATOR . $filename))
                                    {
                                        $files[] = $path . DIRECTORY_SEPARATOR . $filename;
                                    }
                                } else {
                                    if(is_readable($path . DIRECTORY_SEPARATOR . $filename))
                                    {
                                        if (filesize($path . DIRECTORY_SEPARATOR . $filename) < $exclude_file_size * 1024 * 1024) {
                                            $files[] = $path . DIRECTORY_SEPARATOR . $filename;
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
                if($handler)
                    @closedir($handler);
            }

        }
        if($include_dir && $count == 0){
            $files[] = $path;
        }
    }

    private function regex_match($regex_array,$string,$mode)
    {
        if(empty($regex_array))
        {
            return true;
        }

        if($mode==0)
        {
            foreach ($regex_array as $regex)
            {
                if(preg_match($regex,$string))
                {
                    return false;
                }
            }

            return true;
        }

        if($mode==1)
        {
            foreach ($regex_array as $regex)
            {
                if(preg_match($regex,$string))
                {
                    return true;
                }
            }

            return false;
        }

        return true;
    }

    public function get_need_cleanup_files($all=false)
    {
        $files=array();
        if($this->task['options']['backup_options']['ismerge']==1)
        {
            foreach ($this->task['options']['backup_options']['backup'] as $backup_finished_data)
            {
                if($all===false)
                {
                    if(WPVIVID_BACKUP_TYPE_MERGE==$backup_finished_data['key'])
                        continue;
                }

                $files=array_merge($files,$this->get_backup_file($backup_finished_data['key']));
                add_filter('wpvivid_get_need_cleanup_files', array($this, 'wpvivid_get_need_cleanup_files'),10,2);
                $files=apply_filters('wpvivid_get_need_cleanup_files',$files,$backup_finished_data);
            }
        }

        return $files;
    }

    public function wpvivid_get_need_cleanup_files($files,$backup_finished_data)
    {
        if(WPVIVID_BACKUP_TYPE_PLUGIN==$backup_finished_data['key'])
        {
            $general_setting=WPvivid_Setting::get_setting(true, "");
            if(isset($general_setting['options']['wpvivid_compress_setting']['subpackage_plugin_upload']) && !empty($general_setting['options']['wpvivid_compress_setting']['subpackage_plugin_upload'])){
                if($general_setting['options']['wpvivid_compress_setting']['subpackage_plugin_upload']){
                    $packages_files_info = $this->get_packages_files_info(WPVIVID_BACKUP_TYPE_PLUGIN);
                    if ($packages_files_info !== false) {
                        foreach ($packages_files_info as $file_data) {
                            $path = WP_CONTENT_DIR . DIRECTORY_SEPARATOR . $this->task['options']['backup_options']['dir'] . DIRECTORY_SEPARATOR . $file_data['file_name'];
                            if (!in_array($path, $files))
                                $files[] = $path;
                        }
                    }
                }
            }
        }
        else if(WPVIVID_BACKUP_TYPE_UPLOADS_FILES==$backup_finished_data['key'])
        {
            $general_setting=WPvivid_Setting::get_setting(true, "");
            if(isset($general_setting['options']['wpvivid_compress_setting']['subpackage_plugin_upload']) && !empty($general_setting['options']['wpvivid_compress_setting']['subpackage_plugin_upload'])){
                if($general_setting['options']['wpvivid_compress_setting']['subpackage_plugin_upload']){
                    $packages_files_info = $this->get_packages_files_info(WPVIVID_BACKUP_TYPE_UPLOADS_FILES);
                    if ($packages_files_info !== false) {
                        foreach ($packages_files_info as $file_data) {
                            $path = WP_CONTENT_DIR . DIRECTORY_SEPARATOR . $this->task['options']['backup_options']['dir'] . DIRECTORY_SEPARATOR . $file_data['file_name'];
                            global $wpvivid_plugin;
                            $wpvivid_plugin->wpvivid_log->WriteLog('test:' . $path, 'notice');
                            if (!in_array($path, $files))
                                $files[] = $path;
                        }
                    }
                }
            }
        }
        return $files;
    }

    public function get_prefix()
    {
        return  $this->task['options']['backup_options']['prefix'];
    }

    public function set_file_and_db_info($db_size,$file_size)
    {
        $this->task['data']['backup']['db_size']=$db_size;
        $this->task['data']['backup']['files_size']=$file_size;

        WPvivid_Setting::update_task($this->task['id'],$this->task);
    }

    public function get_file_info()
    {
        $file_size['sum_size']=0;
        $file_size['sum_count']=0;

        $memory_limit = ini_get('memory_limit');
        $ret['memory_limit']=$memory_limit;
        $memory_limit = trim($memory_limit);
        $memory_limit_int = (int) $memory_limit;
        $last = strtolower(substr($memory_limit, -1));

        if($last == 'g')
            $memory_limit_int = $memory_limit_int*1024*1024*1024;
        if($last == 'm')
            $memory_limit_int = $memory_limit_int*1024*1024;
        if($last == 'k')
            $memory_limit_int = $memory_limit_int*1024;

        $files=array();

        foreach ($this->task['options']['backup_options']['backup'] as $backup_data)
        {
            if(!isset($backup_data['dump_db']))
            {
                if(array_key_exists($backup_data['key'],$this->backup_type_collect))
                {
                    $backup_files = $this->get_need_backup_files_size($backup_data);
                }
                else
                {
                    $backup_data['files']=array();
                    $backup_files =apply_filters('wpvivid_get_custom_need_backup_files_size', $backup_data['files'],$backup_data,$this->task['options']['backup_options']['compress']);
                }
                $files=array_merge($backup_files,$files);
            }
        }

        foreach ($files as $file)
        {
            $size=0;
            $_file_size=filesize($file);
            if($_file_size>($memory_limit_int*0.9))
            {
                $ret['alter_big_file']=true;
                $ret['alter_files']=true;
            }
            $size+=$_file_size;
            $file_size['sum_count']++;
            $file_size['sum_size']+=$size;
        }
        return $file_size;
    }

    public function is_cancel_file_exist()
    {
        $file_name=$this->task['options']['file_prefix'];

        $file=WP_CONTENT_DIR.DIRECTORY_SEPARATOR.$this->task['options']['backup_options']['dir'].DIRECTORY_SEPARATOR.$file_name.'_cancel';

        if(file_exists($file))
        {
            return true;
        }
        else
        {
            return false;
        }
    }

    public function update_status($status)
    {
        $this->task['status']['str']=$status;
        WPvivid_Setting::update_task($this->task['id'],$this->task);
    }

    public function get_backup_task_info($task_id){
        $list_tasks['status']=WPvivid_taskmanager::get_backup_tasks_status($task_id);
        $list_tasks['is_canceled']=WPvivid_taskmanager::is_task_canceled($task_id);
        $list_tasks['size']=WPvivid_taskmanager::get_backup_size($task_id);
        $list_tasks['data']=WPvivid_taskmanager::get_backup_tasks_progress($task_id);
        //
        $list_tasks['task_info']['need_next_schedule']=false;
        if($list_tasks['status']['str']=='running'||$list_tasks['status']['str']=='no_responds')
        {
            if($list_tasks['data']['running_stamp']>180) {
                $list_tasks['task_info']['need_next_schedule'] = true;
            }
            else{
                $list_tasks['task_info']['need_next_schedule'] = false;
            }
        }
        //
        $general_setting=WPvivid_Setting::get_setting(true, "");
        if($general_setting['options']['wpvivid_common_setting']['estimate_backup'] == 0){
            $list_tasks['task_info']['display_estimate_backup'] = 'display: none';
        }
        else{
            $list_tasks['task_info']['display_estimate_backup'] = '';
        }
        //
        $list_tasks['task_info']['backup_percent']=$list_tasks['data']['progress'].'%';
        //
        if($list_tasks['size']['db_size'] == false){
            $list_tasks['task_info']['db_size']=0;
        }
        else{
            $list_tasks['task_info']['db_size']=$list_tasks['size']['db_size'];
        }
        if($list_tasks['size']['files_size'] == false){
            $list_tasks['task_info']['file_size']=0;
        }
        else{
            $list_tasks['task_info']['file_size']=$list_tasks['size']['files_size']['sum'];
        }
        //
        $list_tasks['task_info']['descript']='';
        $list_tasks['task_info']['css_btn_cancel']='pointer-events: auto; opacity: 1;';
        $list_tasks['task_info']['css_btn_log']='pointer-events: auto; opacity: 1;';
        $list_tasks['task_info']['total'] = 'N/A';
        $list_tasks['task_info']['upload'] = 'N/A';
        $list_tasks['task_info']['speed'] = 'N/A';
        $list_tasks['task_info']['network_connection'] = 'N/A';

        $list_tasks['task_info']['need_update_last_task']=false;
        if($list_tasks['status']['str']=='ready')
        {
            $list_tasks['task_info']['descript']=__('Ready to backup. Progress: 0%, running time: 0second.','wpvivid-backuprestore');
            $list_tasks['task_info']['css_btn_cancel']='pointer-events: none; opacity: 0.4;';
            $list_tasks['task_info']['css_btn_log']='pointer-events: none; opacity: 0.4;';
        }
        else if($list_tasks['status']['str']=='running')
        {
            if($list_tasks['is_canceled'] == false)
            {
                if($list_tasks['data']['type'] == 'upload')
                {
                    if(isset($list_tasks['data']['upload_data']) && !empty($list_tasks['data']['upload_data'])) {
                        $descript = $list_tasks['data']['upload_data']['descript'];
                        $offset = $list_tasks['data']['upload_data']['offset'];
                        $current_size = $list_tasks['data']['upload_data']['current_size'];
                        $last_time = $list_tasks['data']['upload_data']['last_time'];
                        $last_size = $list_tasks['data']['upload_data']['last_size'];
                        $speed = ($offset - $last_size) / (time() - $last_time);
                        $speed /= 1000;
                        $speed = round($speed, 2);
                        $speed .= 'kb/s';
                        if(!empty($current_size)) {
                            $list_tasks['task_info']['total'] = size_format($current_size,2);
                        }
                        if(!empty($offset)) {
                            $list_tasks['task_info']['upload'] = size_format($offset, 2);
                        }
                    }
                    else{
                        $descript = 'Start uploading.';
                        $speed = '0kb/s';
                        $list_tasks['task_info']['total'] = 'N/A';
                        $list_tasks['task_info']['upload'] = 'N/A';
                    }

                    $list_tasks['task_info']['speed'] = $speed;
                    $list_tasks['task_info']['descript'] = $descript.' '.__('Progress: ', 'wpvivid-backuprestore') . $list_tasks['task_info']['backup_percent'] . ', ' . __('running time: ', 'wpvivid-backuprestore') . $list_tasks['data']['running_time'];

                    $time_spend=time()-$list_tasks['status']['run_time'];
                    if($time_spend>30)
                    {
                        $list_tasks['task_info']['network_connection']='Retrying';
                    }
                    else
                    {
                        $list_tasks['task_info']['network_connection']='OK';
                    }
                }
                else {
                    $list_tasks['task_info']['descript'] = $list_tasks['data']['descript'] . ' '. __('Progress: ', 'wpvivid-backuprestore') . $list_tasks['task_info']['backup_percent'] . ', '. __('running time: ', 'wpvivid-backuprestore') . $list_tasks['data']['running_time'];
                }
                $list_tasks['task_info']['css_btn_cancel']='pointer-events: auto; opacity: 1;';
                $list_tasks['task_info']['css_btn_log']='pointer-events: auto; opacity: 1;';
            }
            else{
                $list_tasks['task_info']['descript']=__('The backup will be canceled after backing up the current chunk ends.','wpvivid-backuprestore');
                $list_tasks['task_info']['css_btn_cancel']='pointer-events: none; opacity: 0.4;';
                $list_tasks['task_info']['css_btn_log']='pointer-events: auto; opacity: 1;';
            }
        }
        else if($list_tasks['status']['str']=='wait_resume'){
            $list_tasks['task_info']['descript']='Task '.$task_id.' timed out, backup task will retry in '.$list_tasks['data']['next_resume_time'].' seconds, retry times: '.$list_tasks['status']['resume_count'].'.';
            $list_tasks['task_info']['css_btn_cancel']='pointer-events: auto; opacity: 1;';
            $list_tasks['task_info']['css_btn_log']='pointer-events: auto; opacity: 1;';
        }
        else if($list_tasks['status']['str']=='no_responds'){
            if($list_tasks['is_canceled'] == false){
                $list_tasks['task_info']['descript']='Task , '.$list_tasks['data']['doing'].' is not responding. Progress: '.$list_tasks['task_info']['backup_percent'].', running time: '.$list_tasks['data']['running_time'];
                $list_tasks['task_info']['css_btn_cancel']='pointer-events: auto; opacity: 1;';
                $list_tasks['task_info']['css_btn_log']='pointer-events: auto; opacity: 1;';
            }
            else{
                $list_tasks['task_info']['descript']=__('The backup will be canceled after backing up the current chunk ends.','wpvivid-backuprestore');
                $list_tasks['task_info']['css_btn_cancel']='pointer-events: none; opacity: 0.4;';
                $list_tasks['task_info']['css_btn_log']='pointer-events: auto; opacity: 1;';
            }
        }
        else if($list_tasks['status']['str']=='completed'){
            $list_tasks['task_info']['descript']='Task '.$task_id.' completed.';
            $list_tasks['task_info']['css_btn_cancel']='pointer-events: auto; opacity: 1;';
            $list_tasks['task_info']['css_btn_log']='pointer-events: auto; opacity: 1;';
            $list_tasks['task_info']['need_update_last_task']=true;
        }
        else if($list_tasks['status']['str']=='error'){
            $list_tasks['task_info']['descript']='Backup error: '.$list_tasks['status']['error'];
            $list_tasks['task_info']['css_btn_cancel']='pointer-events: auto; opacity: 1;';
            $list_tasks['task_info']['css_btn_log']='pointer-events: auto; opacity: 1;';
            $list_tasks['task_info']['need_update_last_task']=true;
        }

        return $list_tasks;
    }

    public function get_packages_info($key)
    {
        if(isset($this->task['data']['backup']['sub_job'][$key]))
        {
            if(empty($this->task['data']['backup']['sub_job'][$key]['job_data']))
            {
                return false;
            }
            else
            {
                $packages=array();
                foreach ($this->task['data']['backup']['sub_job'][$key]['job_data'] as $key=>$package)
                {
                    if($key=='files')
                    {
                        continue;
                    }

                    $packages[]=$package;
                }
                if(empty($packages))
                {
                    return false;
                }
                else
                {
                    return $packages;
                }
            }
        }
        else
        {
            return false;
        }
    }

    public function get_packages_files_info($key)
    {
        if(isset($this->task['data']['backup']['sub_job'][$key]))
        {
            if(empty($this->task['data']['backup']['sub_job'][$key]['job_data']))
            {
                return false;
            }
            else
            {
                if(isset($this->task['data']['backup']['sub_job'][$key]['job_data']['files']))
                {
                    return $this->task['data']['backup']['sub_job'][$key]['job_data']['files'];
                }
                else
                {
                    return false;
                }
            }
        }
        else
        {
            return false;
        }
    }

    public function set_packages_info($key,$packages)
    {
        $this->task=WPvivid_taskmanager::get_task($this->task['id']);
        $job_data=array();
        foreach ($packages as $package)
        {
            $package['backup']=false;
            $job_data[basename($package['path'])]=$package;
        }
        $this->task['data']['backup']['sub_job'][$key]['job_data']=$job_data;
        WPvivid_Setting::update_task($this->task['id'],$this->task);
        return $job_data;
    }

    public function update_packages_info($key,$package,$file_data=false)
    {
        $this->task=WPvivid_taskmanager::get_task($this->task['id']);
        $this->task['data']['backup']['sub_job'][$key]['job_data'][basename($package['path'])]=$package;
        if($file_data!==false)
        {
            $this->task['data']['backup']['sub_job'][$key]['job_data']['files'][]=$file_data;
        }
        WPvivid_Setting::update_task($this->task['id'],$this->task);
    }

    public function update_sub_task_progress($key,$finished,$progress)
    {
        $this->task=WPvivid_taskmanager::get_task($this->task['id']);
        $this->task['status']['run_time']=time();
        $this->task['status']['str']='running';
        $this->task['data']['doing']='backup';
        $sub_job_name=$key;
        $this->task['data']['backup']['doing']=$key;
        $this->task['data']['backup']['sub_job'][$sub_job_name]['finished']=$finished;
        $this->task['data']['backup']['sub_job'][$sub_job_name]['progress']=$progress;
        if(!isset( $this->task['data']['backup']['sub_job'][$sub_job_name]['job_data']))
        {
            $this->task['data']['backup']['sub_job'][$sub_job_name]['job_data']=array();
        }
        WPvivid_Setting::update_task($this->task['id'],$this->task);
    }

    public function get_need_backup_folder($backup_data)
    {
        if(isset($backup_data['files'])&&empty($backup_data['files']))
        {
            return $backup_data['files'];
        }
        else
        {
            return $this->get_folder_list($backup_data['files_root'],$backup_data['exclude_regex'],$backup_data['include_regex'],$this->task['options']['backup_options']['compress']['exclude_file_size']);
        }
    }

    public function get_folder_list($root,$exclude_regex,$include_regex,$exclude_file_size)
    {
        $files=array();
        $this->getFolder($files,$root,$exclude_regex,$include_regex,$exclude_file_size);
        return $files;
    }

    public function getFolder(&$files,$path,$exclude_regex=array(),$include_regex=array(),$exclude_file_size=0,$include_dir = true)
    {
        $count = 0;
        if(is_dir($path))
        {
            $handler = opendir($path);
            if($handler!==false)
            {
                while (($filename = readdir($handler)) !== false)
                {
                    if ($filename != "." && $filename != "..")
                    {
                        $count++;

                        if (is_dir($path . DIRECTORY_SEPARATOR . $filename))
                        {
                            if ($this->regex_match($exclude_regex, $path . DIRECTORY_SEPARATOR . $filename, 0))
                            {
                                if ($this->regex_match($include_regex, $path . DIRECTORY_SEPARATOR . $filename, 1))
                                {
                                    $files[] = $path . DIRECTORY_SEPARATOR . $filename;
                                }
                            }
                        } else {
                            if($this->regex_match($exclude_regex, $path . DIRECTORY_SEPARATOR . $filename, 0)){
                                if ($exclude_file_size == 0)
                                {
                                    if(is_readable($path . DIRECTORY_SEPARATOR . $filename))
                                    {
                                        $files[] = $path . DIRECTORY_SEPARATOR . $filename;
                                    }
                                } else {
                                    if(is_readable($path . DIRECTORY_SEPARATOR . $filename))
                                    {
                                        if (filesize($path . DIRECTORY_SEPARATOR . $filename) < $exclude_file_size * 1024 * 1024)
                                        {
                                            $files[] = $path . DIRECTORY_SEPARATOR . $filename;
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
                if($handler)
                    @closedir($handler);
            }
        }
        if($include_dir && $count == 0)
        {
            $files[] = $path;
        }
    }

    public function get_need_uploads_backup_folder($backup_data)
    {
        if(isset($backup_data['files'])&&empty($backup_data['files']))
        {
            return $backup_data['files'];
        }
        else
        {
            return $this->get_uploads_folder_list($backup_data['files_root'],$backup_data['exclude_regex'],$backup_data['include_regex'],$this->task['options']['backup_options']['compress']['exclude_file_size']);
        }
    }

    public function get_uploads_folder_list($root,$exclude_regex,$include_regex,$exclude_file_size)
    {
        $files=array();
        $files[] = $root;
        $this->getUploadsFolder($files,$root,$exclude_regex,$include_regex,$exclude_file_size);
        return $files;
    }

    public function getUploadsFolder(&$files,$path,$exclude_regex=array(),$include_regex=array(),$exclude_file_size=array(),$include_dir = true)
    {
        $count = 0;
        if(is_dir($path))
        {
            $handler = opendir($path);
            if($handler===false)
                return;
            while (($filename = readdir($handler)) !== false)
            {
                if ($filename != "." && $filename != "..")
                {
                    $count++;

                    if (is_dir($path . DIRECTORY_SEPARATOR . $filename))
                    {
                        if ($this->regex_match($exclude_regex, $path . DIRECTORY_SEPARATOR . $filename, 0))
                        {
                            if ($this->regex_match($include_regex, $path . DIRECTORY_SEPARATOR . $filename, 1))
                            {
                                $this->getUploadsFolder($files,$path . DIRECTORY_SEPARATOR . $filename,$exclude_regex,$include_regex,$exclude_file_size,$include_dir);
                            }
                        }
                    }
                }
            }
            if($handler)
                @closedir($handler);
        }
        if($include_dir && $count == 0)
        {
            $files[] = $path;
        }
    }

    public function add_new_backup()
    {
        $this->task=WPvivid_taskmanager::get_task($this->task['id']);
        $backup_data=array();
        $backup_data['type']=$this->task['type'];
        $status=WPvivid_taskmanager::get_backup_task_status($this->task['id']);
        $backup_data['create_time']=$status['start_time'];
        $backup_data['manual_delete']=0;
        $backup_options=WPvivid_taskmanager::get_task_options($this->task['id'],'backup_options');
        $lock=WPvivid_taskmanager::get_task_options($this->task['id'],'lock');
        $backup_data['local']['path']=$backup_options['dir'];
        $backup_data['compress']['compress_type']=$backup_options['compress']['compress_type'];
        $backup_data['save_local']=$this->task['options']['save_local'];
        if(isset($this->task['options']['backup_prefix']))
        {
            $backup_data['backup_prefix'] = $this->task['options']['backup_prefix'];
        }

        global $wpvivid_plugin;
        $backup_data['log']=$wpvivid_plugin->wpvivid_log->log_file;
        $backup_data['backup']=$this->get_backup_result();
        $backup_data['remote']=array();
        if($lock==1)
            $backup_data['lock']=1;

        $backup_list='wpvivid_backup_list';

        $backup_list=apply_filters('get_wpvivid_backup_list_name',$backup_list,$this->task['id']);

        $list = WPvivid_Setting::get_option($backup_list);
        $list[$this->task['id']]=$backup_data;
        WPvivid_Setting::update_option($backup_list,$list);
    }

    public function get_backup_files()
    {
        $files=array();
        foreach ($this->task['options']['backup_options']['backup'] as $backup_data)
        {
            if($this->task['options']['backup_options']['ismerge']==1)
            {
                if(WPVIVID_BACKUP_TYPE_MERGE==$backup_data['key'])
                {
                    if($backup_data['result']!==false)
                    {
                        $ret=$backup_data['result'];
                        if($ret['result']===WPVIVID_SUCCESS)
                        {
                            foreach ($ret['files'] as $file)
                            {
                                $files[]=WP_CONTENT_DIR.DIRECTORY_SEPARATOR.$this->task['options']['backup_options']['dir'].DIRECTORY_SEPARATOR.$file['file_name'];
                            }
                        }
                    }
                }
            }
            else
            {
                if($backup_data['result']!==false)
                {
                    $ret=$backup_data['result'];
                    if($ret['result']===WPVIVID_SUCCESS)
                    {
                        foreach ($ret['files'] as $file)
                        {
                            $files[]=WP_CONTENT_DIR.DIRECTORY_SEPARATOR.$this->task['options']['backup_options']['dir'].DIRECTORY_SEPARATOR.$file['file_name'];
                        }
                    }
                }
            }
        }

        return $files;
    }
}

class WPvivid_Backup_Item
{
    private $config;

    public function __construct($options)
    {
        $this->config=$options;
    }

    public function get_backup_type()
    {
        return $this->config['type'];
    }

    public function get_backup_path($file_name)
    {
        $path = $this->get_local_path() . $file_name;

        if (file_exists($path)) {
            return $path;
        }
        else{
            $local_setting = get_option('wpvivid_local_setting', array());
            if(!empty($local_setting))
            {
                $path = WP_CONTENT_DIR . DIRECTORY_SEPARATOR . $local_setting['path'] . DIRECTORY_SEPARATOR . $file_name;
            }
            else {
                $path = WP_CONTENT_DIR . DIRECTORY_SEPARATOR . 'wpvividbackups' . DIRECTORY_SEPARATOR . $file_name;
            }
        }
        return $path;
    }

    public function get_files($has_dir=true)
    {
        global $wpvivid_plugin;
        $files=array();
        if(isset($this->config['backup']['files']))
        {
            //file_name
            foreach ($this->config['backup']['files'] as $file)
            {
                if($has_dir)
                    $files[]=$this->get_backup_path($file['file_name']);//$this->get_local_path().$file['file_name'];
                else
                    $files[]=$file['file_name'];
            }
        }
        else{
            if(isset($this->config['backup']['data']['meta']['files']))
            {
                foreach ($this->config['backup']['data']['meta']['files'] as $file)
                {
                    if($has_dir)
                        $files[]=$this->get_backup_path($file['file_name']);//$this->get_local_path().$file['file_name'];
                    else
                        $files[]=$file['file_name'];
                }
            }
        }
        return $files;
    }

    public function is_lock()
    {
        if(isset($this->config['lock']))
        {
            return $this->config['lock'];
        }
        else{
            return false;
        }
    }

    public function check_backup_files()
    {
        global $wpvivid_plugin;

        $b_has_data=false;
        $tmp_data=array();
        if(isset($this->config['backup']['files']))
        {
            $b_has_data = true;
            $tmp_data = $this->config['backup']['files'];
        }
        else if(isset($this->config['backup']['data']['meta']['files'])){
            $b_has_data = true;
            $tmp_data = $this->config['backup']['data']['meta']['files'];
        }

        if($b_has_data)
        {
            $b_need_download=false;
            $b_not_found=false;
            $b_test=false;
            foreach ($tmp_data as $file)
            {
                $need_download=false;
                $path=$this->get_backup_path($file['file_name']);//$this->get_local_path().$file['file_name'];
                if(file_exists($path))
                {
                    if(filesize($path) == $file['size'])
                    {
                        if($wpvivid_plugin->wpvivid_check_zip_valid())
                        {
                            $res = TRUE;
                        }
                        else{
                            $res = FALSE;
                        }
                    }
                    else {
                        $res = FALSE;
                    }
                    if ($res !== TRUE)
                    {
                        $need_download=true;
                    }
                }
                else
                {
                    $b_test=true;
                    $need_download=true;
                }

                if($need_download)
                {
                    if(empty($this->config['remote']))
                    {
                        $b_not_found=true;
                        $ret['files'][$file['file_name']]['status']='file_not_found';
                        $ret['files'][$file['file_name']]['size']=$file['size'];
                        //$ret['files'][$file['file_name']]['md5']=$file['md5'];
                    }
                    else
                    {
                        $b_need_download=true;
                        WPvivid_taskmanager::delete_download_task_v2($file['file_name']);
                        $ret['files'][$file['file_name']]['status']='need_download';
                        $ret['files'][$file['file_name']]['size']=$file['size'];
                        //$ret['files'][$file['file_name']]['md5']=$file['md5'];
                    }
                }
            }

            if($b_not_found)
            {
                $ret['result']=WPVIVID_FAILED;
                if($b_test)
                    $ret['error']='Backup files doesn\'t exist. Restore failed.';
                else
                    $ret['error']='Backup doesn\'t exist in both web server and remote storage. Restore failed.';
            }
            else if($b_need_download)
            {
                $ret['result']='need_download';
            }
            else
            {
                $ret['result']=WPVIVID_SUCCESS;
            }
        }
        else
        {
            $ret['result']=WPVIVID_FAILED;
            $ret['error']='Unknown error.';
        }

        return $ret;
    }

    public function check_migrate_file()
    {
        if(isset($this->config['backup']['files']))
        {
            $tmp_data = $this->config['backup']['files'];
            if(!class_exists('WPvivid_ZipClass'))
                include_once WPVIVID_PLUGIN_DIR . '/includes/class-wpvivid-zipclass.php';
            $zip=new WPvivid_ZipClass();

            foreach ($tmp_data as $file)
            {
                $path=$this->get_backup_path($file['file_name']);//$this->get_local_path().$file['file_name'];
                if(file_exists($path))
                {
                    $ret=$zip->get_json_data($path);
                    if($ret['result'] === WPVIVID_SUCCESS) {
                        $json=$ret['json_data'];
                        $json = json_decode($json, 1);
                        if (!is_null($json)) {
                            if (isset($json['home_url']) && home_url() != $json['home_url']) {
                                return 1;
                            }
                        }
                        else{
                            return 0;
                        }
                    }
                    elseif($ret['result'] === WPVIVID_FAILED){
                        return 0;
                    }
                }
            }
            return 0;
        }
        else
        {
            return 0;
        }

    }

    public function is_display_migrate_option(){
        if(isset($this->config['backup']['files']))
        {
            $tmp_data = $this->config['backup']['files'];
            if(!class_exists('WPvivid_ZipClass'))
                include_once WPVIVID_PLUGIN_DIR . '/includes/class-wpvivid-zipclass.php';
            $zip=new WPvivid_ZipClass();

            foreach ($tmp_data as $file)
            {
                $path=$this->get_backup_path($file['file_name']);//$this->get_local_path().$file['file_name'];
                if(file_exists($path))
                {
                    $ret=$zip->get_json_data($path);
                    if($ret['result'] === WPVIVID_SUCCESS) {
                        $json=$ret['json_data'];
                        $json = json_decode($json, 1);
                        if (!is_null($json)) {
                            if (isset($json['home_url'])){
                                return false;
                            }
                            else{
                                return true;
                            }
                        }
                        else{
                            return true;
                        }
                    }
                    elseif($ret['result'] === WPVIVID_FAILED){
                        return true;
                    }
                }
            }
            return true;
        }
        else
        {
            return true;
        }
    }

    public function get_local_path()
    {
        $path=WP_CONTENT_DIR.DIRECTORY_SEPARATOR.$this->config['local']['path'].DIRECTORY_SEPARATOR;
        $path=apply_filters('wpvivid_get_site_wpvivid_path',$path,$this->config['local']['path']);
        return $path;
    }

    public function get_local_url()
    {
        $url=content_url().DIRECTORY_SEPARATOR.$this->config['local']['path'].DIRECTORY_SEPARATOR;
        $url=apply_filters('wpvivid_get_site_wpvivid_url',$url,$this->config['local']['path']);
        return $url;
    }

    public function get_remote()
    {
        $remote_option=array_shift($this->config['remote']);

        if(is_null($remote_option))
        {
            return false;
        }
        else
        {
            return $remote_option;
        }
    }

    public function get_backup_packages()
    {
        $packages=array();
        $index=0;

        if(isset($this->config['backup']['files']))
        {
            $db_package=array();
            $file_added=array();
            //file_name
            foreach ($this->config['backup']['files'] as $file)
            {
                if(isset($file_added[$file['file_name']]))
                {
                    continue;
                }

                if (preg_match('/wpvivid-.*_.*_.*\.part[0-9]+\.zip$/', $file['file_name'],$matches))
                {
                    $this->get_all_part_files($file['file_name'],$this->config['backup']['files'],$packages[$index],$file_added);
                }
                else
                {
                    if($this->check_file_is_a_db_package($file['file_name']))
                    {
                        $db_package['files'][]=$file['file_name'];
                    }
                    else
                    {
                        $packages[$index]['files'][]=$file['file_name'];
                    }
                    $file_added[$file['file_name']]=1;
                }
                $index++;
            }

            $file_added=array();
            $child_packages=array();

            foreach ($packages as $key=>$package)
            {
                $files=array();

                foreach ($package['files'] as $package_files)
                {
                    $files=array_merge($files,$this->get_child_files($package_files));
                }

                if(empty($files))
                {
                    continue;
                }

                foreach ($files as $file)
                {
                    if (isset($file_added[$file['file_name']]))
                    {
                        continue;
                    }

                    if (preg_match('/wpvivid-.*_.*_.*\.part[0-9]+\.zip$/', $file['file_name'],$matches))
                    {
                        $this->get_all_part_files($file['file_name'],$files,$child_packages[$index],$file_added);
                    }
                    else
                    {
                        if($this->check_file_is_a_db_package($file['file_name']))
                        {
                            $db_package['files'][]=$file['file_name'];
                        }
                        else
                        {
                            $child_packages[$index]['files'][]=$file['file_name'];
                        }
                        $file_added[$file['file_name']]=1;
                    }
                    $index++;
                }
            }

            $packages=array_merge($packages,$child_packages);
            if(!empty($db_package))
            {
                $packages[$index]=$db_package;
            }
        }
        else if(isset($this->config['backup']['data']))
        {
            if(isset($this->config['backup']['ismerge'])&&$this->config['backup']['ismerge']==1)
            {
                $packages[$index]['option']['has_child']=1;
                //$packages[$index]['option']['root']='wp-content';
                $packages[$index]['option']['root_flag']=WPVIVID_BACKUP_ROOT_WP_CONTENT;
                foreach ($this->config['backup']['data']['meta']['files'] as $file)
                {
                    $packages[$index]['files'][]=$file['file_name'];
                }
                $index++;
            }

            foreach ($this->config['backup']['data']['type'] as $type)
            {
                if($type['type_name']=='backup_db')
                {
                    $packages[$index]['option']['dump_db']=1;
                    //$packages[$index]['option']['root']='wp-content\\'.$this->config['local']['path'];
                    $packages[$index]['option']['root_flag']=WPVIVID_BACKUP_ROOT_CUSTOM;
                }
                else if($type['type_name']=='backup_themes')
                {
                    //$packages[$index]['option']['root']='wp-content';
                    $packages[$index]['option']['root_flag']=WPVIVID_BACKUP_ROOT_WP_CONTENT;
                }
                else if($type['type_name']=='backup_plugin')
                {
                    //$packages[$index]['option']['root']='wp-content';
                    $packages[$index]['option']['root_flag']=WPVIVID_BACKUP_ROOT_WP_CONTENT;
                }
                else if($type['type_name']=='backup_uploads')
                {
                    //$packages[$index]['option']['root']='wp-content';
                    $packages[$index]['option']['root_flag']=WPVIVID_BACKUP_ROOT_WP_CONTENT;
                }
                else if($type['type_name']=='backup_content')
                {
                    //$packages[$index]['option']['root']='';
                    $packages[$index]['option']['root_flag']=WPVIVID_BACKUP_ROOT_WP_ROOT;
                }
                else if($type['type_name']=='backup_core')
                {
                    //$packages[$index]['option']['root']='';
                    $packages[$index]['option']['root_flag']=WPVIVID_BACKUP_ROOT_WP_ROOT;
                    $packages[$index]['option']['include_path'][]='wp-includes';
                    $packages[$index]['option']['include_path'][]='wp-admin';
                }

                foreach ($type['files'] as $file)
                {
                    $packages[$index]['files'][]=$file['file_name'];
                }
                $index++;
            }
        }
        return $packages;
    }

    public function check_file_is_a_db_package($file_name)
    {
        //backup_db.zip
        if (preg_match('#.*_backup_db.zip?#', $file_name, $matches))
        {
            return true;
        }
        else
        {
            return false;
        }
    }

    public function get_all_part_files($file_name,$files,&$package,&$file_added)
    {
        if (preg_match('#\.part[0-9]+\.zip$#',$file_name,$matches))
        {
            $prefix=$matches[0];
            $file_prefix=substr($file_name,0,strlen($file_name)-strlen($prefix));
            foreach ($files as $file)
            {
                if(isset($file_added[$file['file_name']]))
                {
                    continue;
                }

                if (strpos($file['file_name'], $file_prefix) !== false)
                {
                    $package['files'][]=$file['file_name'];
                    $file_added[$file['file_name']]=1;
                }
            }
        }
    }

    public function get_child_files($file_name)
    {
        if(!class_exists('WPvivid_ZipClass'))
            include_once WPVIVID_PLUGIN_DIR . '/includes/class-wpvivid-zipclass.php';
        $zip=new WPvivid_ZipClass();

        $path=$this->get_backup_path($file_name);//$this->get_local_path().$file_name;

        $files = array();

        $ret=$zip->get_json_data($path);

        if($ret['result'] === WPVIVID_SUCCESS) {
            $json=$ret['json_data'];
            $json = json_decode($json, 1);
            if (isset($json['has_child'])) {
                $files = $zip->list_file($path);
            }
        }
        return $files;
    }

    public function get_file_info($file_name)
    {
        if(!class_exists('WPvivid_ZipClass'))
            include_once WPVIVID_PLUGIN_DIR . '/includes/class-wpvivid-zipclass.php';
        $zip=new WPvivid_ZipClass();

        $path=$this->get_backup_path($file_name);//$this->get_local_path().$file_name;

        $ret=$zip->get_json_data($path);
        if($ret['result'] === WPVIVID_SUCCESS) {
            $json=$ret['json_data'];
            $json = json_decode($json, 1);
            if (is_null($json)) {
                return false;
            } else {
                return $json;
            }
        }
        elseif($ret['result'] === WPVIVID_FAILED){
            return false;
        }
    }

    static public function get_backup_file_info($file_name)
    {
        if(!class_exists('WPvivid_ZipClass'))
            include_once WPVIVID_PLUGIN_DIR . '/includes/class-wpvivid-zipclass.php';
        $zip=new WPvivid_ZipClass();
        $ret=$zip->get_json_data($file_name);
        if($ret['result'] === WPVIVID_SUCCESS)
        {
            $json=$ret['json_data'];
            $json = json_decode($json, 1);
            if (is_null($json)) {
                return array('result'=>WPVIVID_FAILED,'error'=>'Failed to decode json');
            } else {
                return array('result'=>WPVIVID_SUCCESS,'json_data'=>$json);
            }
        }
        elseif($ret['result'] === WPVIVID_FAILED){
            return $ret;
        }
    }

    public function get_sql_file($file_name)
    {
        if(!class_exists('WPvivid_ZipClass'))
            include_once WPVIVID_PLUGIN_DIR . '/includes/class-wpvivid-zipclass.php';
        $zip=new WPvivid_ZipClass();
        $path=$this->get_backup_path($file_name);//$this->get_local_path().$file_name;
        $files=$zip->list_file($path);
        return $files[0]['file_name'];
    }

    public static function get_backup_files($backup){
        $files=array();
        if(isset($backup['backup']['files'])){
            $files=$backup['backup']['files'];
        }
        else{
            if(isset($backup['backup']['ismerge'])) {
                if ($backup['backup']['ismerge'] == 1) {
                    if(isset($backup['backup']['data']['meta']['files'])){
                        $files=$backup['backup']['data']['meta']['files'];
                    }
                }
            }
        }
        asort($files);
        uasort($files, function ($a, $b) {
            $file_name_1 = $a['file_name'];
            $file_name_2 = $b['file_name'];
            $index_1 = 0;
            if(preg_match('/wpvivid-.*_.*_.*\.part.*\.zip$/', $file_name_1)) {
                if (preg_match('/part.*$/', $file_name_1, $matches)) {
                    $index_1 = $matches[0];
                    $index_1 = preg_replace("/part/","", $index_1);
                    $index_1 = preg_replace("/.zip/","", $index_1);
                }
            }
            $index_2 = 0;
            if(preg_match('/wpvivid-.*_.*_.*\.part.*\.zip$/', $file_name_2)) {
                if (preg_match('/part.*$/', $file_name_2, $matches)) {
                    $index_2 = $matches[0];
                    $index_2 = preg_replace("/part/", "", $index_2);
                    $index_2 = preg_replace("/.zip/", "", $index_2);
                }
            }
            if($index_1 !== 0 && $index_2 === 0){
                return -1;
            }
            if($index_1 === 0 && $index_2 !== 0){
                return 1;
            }
        });
        return $files;
    }

    public function get_download_backup_files($backup_id){
        $ret['result']=WPVIVID_FAILED;
        $data=array();
        $backup=WPvivid_Backuplist::get_backup_by_id($backup_id);
        if(!$backup)
        {
            $ret['error']='Backup id not found.';
            return $ret;
        }

        $files=array();
        $files = self::get_backup_files($backup);
        if(empty($files)){
            $ret['error']='Failed to get backup files.';
        }
        else{
            $ret['result']=WPVIVID_SUCCESS;
            $ret['files']=$files;
        }
        return $ret;
    }

    public function get_download_progress($backup_id, $files){
        global $wpvivid_plugin;
        $b_need_download=false;
        $b_not_found=false;
        $file_count=0;
        $file_part_num=1;
        $check_type='';
        foreach ($files as $file)
        {
            $need_download=false;
            $path=$this->get_backup_path($file['file_name']);//$this->get_local_path().$file['file_name'];
            $download_url=content_url().DIRECTORY_SEPARATOR.$this->config['local']['path'].DIRECTORY_SEPARATOR.$file['file_name'];
            if(file_exists($path)) {
                if(filesize($path) == $file['size']){
                    if($wpvivid_plugin->wpvivid_check_zip_valid()) {
                        $res = TRUE;
                    }
                    else{
                        $res = FALSE;
                    }
                }
                else{
                    $res = FALSE;
                }
                if ($res !== TRUE)
                {
                    $need_download=true;
                }
            }
            else {
                $need_download=true;
            }
            if($file_part_num < 10){
                $format_part=sprintf("%02d", $file_part_num);
            }
            else{
                $format_part=$file_part_num;
            }
            if($need_download) {
                if(empty($this->config['remote'])) {
                    $b_not_found=true;
                    $ret['result'] = WPVIVID_SUCCESS;
                    $ret['files'][$file['file_name']]['status']='file_not_found';
                }
                else{
                    $task = WPvivid_taskmanager::get_download_task_v2($file['file_name']);
                    $ret['task']=$task;
                    if ($task === false) {
                        $ret['result'] = WPVIVID_SUCCESS;
                        $ret['files'][$file['file_name']]['status']='need_download';
                        $ret['files'][$file['file_name']]['html']='<div style="float:left;margin:10px 10px 10px 0;text-align:center; width:180px;">
                                                                  <span>Part'.$format_part.'</span></br>
                                                                  <span id=\''.$backup_id.'-text-part-'.$file_part_num.'\'><a onclick="wpvivid_prepare_download(\''.$file_part_num.'\', \''.$backup_id.'\', \''.$file['file_name'].'\');" style="cursor: pointer;">Prepare to Download</a></span></br>
                                                                  <div style="width:100%;height:5px; background-color:#dcdcdc;"><div id=\''.$backup_id.'-progress-part-'.$file_part_num.'\' style="background-color:#0085ba; float:left;width:0;height:5px;"></div></div>
                                                                  <span>size:</span><span>'.$wpvivid_plugin->formatBytes($file['size']).'</span>
                                                                  </div>';
                    } else {
                        $ret['result'] = WPVIVID_SUCCESS;
                        if($task['status'] === 'running'){
                            $ret['files'][$file['file_name']]['status'] = 'running';
                            $ret['files'][$file['file_name']]['html']='<div style="float:left;margin:10px 10px 10px 0;text-align:center; width:180px;">
                                                                            <span>Part'.$format_part.'</span></br>
                                                                            <span id=\''.$backup_id.'-text-part-'.$file_part_num.'\'><a >Retriving(remote storage to web server)</a></span></br>
                                                                            <div style="width:100%;height:5px; background-color:#dcdcdc;"><div id=\''.$backup_id.'-progress-part-'.$file_part_num.'\' style="background-color:#0085ba; float:left;width:'.$task['progress_text'].'%;height:5px;"></div></div>
                                                                            <span>size:</span><span>'.$wpvivid_plugin->formatBytes($file['size']).'</span>
                                                                            </div>';
                            $ret['files'][$file['file_name']]['progress_text']=$task['progress_text'];
                        }
                        elseif($task['status'] === 'timeout'){
                            $ret['files'][$file['file_name']]['status']='timeout';
                            $ret['files'][$file['file_name']]['html']='<div style="float:left;margin:10px 10px 10px 0;text-align:center; width:180px;">
                                                                            <span>Part'.$format_part.'</span></br>
                                                                            <span id=\''.$backup_id.'-text-part-'.$file_part_num.'\'><a onclick="wpvivid_prepare_download(\''.$file_part_num.'\', \''.$backup_id.'\', \''.$file['file_name'].'\');" style="cursor: pointer;">Prepare to Download</a></span></br>
                                                                            <div style="width:100%;height:5px; background-color:#dcdcdc;"><div id=\''.$backup_id.'-progress-part-'.$file_part_num.'\' style="background-color:#0085ba; float:left;width:'.$task['progress_text'].'%;height:5px;"></div></div>
                                                                            <span>size:</span><span>'.$wpvivid_plugin->formatBytes($file['size']).'</span>
                                                                            </div>';
                            $ret['files'][$file['file_name']]['progress_text']=$task['progress_text'];
                            WPvivid_taskmanager::delete_download_task_v2($file['file_name']);
                        }
                        elseif($task['status'] === 'completed'){
                            $ret['files'][$file['file_name']]['status']='completed';
                            $ret['files'][$file['file_name']]['html']='<div style="float:left;margin:10px 10px 10px 0;text-align:center; width:180px;">
                                                                 <span>Part'.$format_part.'</span></br>
                                                                 <span id=\''.$backup_id.'-text-part-'.$file_part_num.'\'><a onclick="wpvivid_download(\''.$backup_id.'\', \''.$check_type.'\', \''.$file['file_name'].'\');" style="cursor: pointer;">Download</a></span></br>
                                                                 <div style="width:100%;height:5px; background-color:#dcdcdc;"><div id=\''.$backup_id.'-progress-part-'.$file_part_num.'\' style="background-color:#0085ba; float:left;width:100%;height:5px;"></div></div>
                                                                 <span>size:</span><span>'.$wpvivid_plugin->formatBytes($file['size']).'</span>
                                                                 </div>';
                            WPvivid_taskmanager::delete_download_task_v2($file['file_name']);
                        }
                        elseif($task['status'] === 'error'){
                            $ret['files'][$file['file_name']]['status']='error';
                            $ret['files'][$file['file_name']]['html']='<div style="float:left;margin:10px 10px 10px 0;text-align:center; width:180px;">
                                                                        <span>Part'.$format_part.'</span></br>
                                                                        <span id=\''.$backup_id.'-text-part-'.$file_part_num.'\'><a onclick="wpvivid_prepare_download(\''.$file_part_num.'\', \''.$backup_id.'\', \''.$file['file_name'].'\');" style="cursor: pointer;">Prepare to Download</a></span></br>
                                                                        <div style="width:100%;height:5px; background-color:#dcdcdc;"><div id=\''.$backup_id.'-progress-part-'.$file_part_num.'\' style="background-color:#0085ba; float:left;width:0;height:5px;"></div></div>
                                                                        <span>size:</span><span>'.$wpvivid_plugin->formatBytes($file['size']).'</span>
                                                                        </div>';
                            $ret['files'][$file['file_name']]['error'] = $task['error'];
                            WPvivid_taskmanager::delete_download_task_v2($file['file_name']);
                        }
                    }
                }
            }
            else{
                $ret['result'] = WPVIVID_SUCCESS;
                if(WPvivid_taskmanager::get_download_task_v2($file['file_name']))
                    WPvivid_taskmanager::delete_download_task_v2($file['file_name']);
                $ret['files'][$file['file_name']]['status']='completed';
                $ret['files'][$file['file_name']]['download_path']=$path;
                $ret['files'][$file['file_name']]['download_url']=$download_url;
                $ret['files'][$file['file_name']]['html']='<div style="float:left;margin:10px 10px 10px 0;text-align:center; width:180px;">
                                                                 <span>Part'.$format_part.'</span></br>
                                                                 <span id=\''.$backup_id.'-text-part-'.$file_part_num.'\'><a onclick="wpvivid_download(\''.$backup_id.'\', \''.$check_type.'\', \''.$file['file_name'].'\');" style="cursor: pointer;">Download</a></span></br>
                                                                 <div style="width:100%;height:5px; background-color:#dcdcdc;"><div id=\''.$backup_id.'-progress-part-'.$file_part_num.'\' style="background-color:#0085ba; float:left;width:100%;height:5px;"></div></div>
                                                                 <span>size:</span><span>'.$wpvivid_plugin->formatBytes($file['size']).'</span>
                                                                 </div>';
            }
            $ret['files'][$file['file_name']]['size']=$wpvivid_plugin->formatBytes($file['size']);
            $file_count++;
            $file_part_num++;
        }
        if ($file_count % 2 != 0) {
            $file_count++;
            if($file_count < 10){
                $format_part=sprintf("%02d", $file_count);
            }
            else{
                $format_part=$file_count;
            }
            $ret['place_html']='<div style="float:left;margin:10px 10px 10px 0;text-align:center; width:180px; color:#cccccc;">
                                   <span>Part'.$format_part.'</span></br>
                                   <span>Download</span></br>
                                   <div style="width:100%;height:5px; background-color:#dcdcdc;"><div style="background-color:#0085ba; float:left;width:0;height:5px;"></div></div>
                                   <span>size:</span><span>0</span>
                                   </div>';
        }
        else{
            $ret['place_html']='';
        }
        return $ret;
    }

    public function update_download_page($backup_id){
        $ret=$this->get_download_backup_files($backup_id);
        if($ret['result']==WPVIVID_SUCCESS){
            $ret=$this->get_download_progress($backup_id, $ret['files']);
            WPvivid_taskmanager::update_download_cache($backup_id,$ret);
        }
        return $ret;
    }

    public function cleanup_local_backup()
    {
        $files=array();
        $download_dir=$this->config['local']['path'];
        $file=$this->get_files(false);

        foreach ($file as $filename)
        {
            $files[] = $filename;
        }

        foreach ($files as $file)
        {
            $download_path = WP_CONTENT_DIR .DIRECTORY_SEPARATOR . $download_dir . DIRECTORY_SEPARATOR . $file;
            if (file_exists($download_path))
            {
                @wp_delete_file($download_path);
            }
            else{
                $backup_dir=WPvivid_Setting::get_backupdir();
                $download_path = WP_CONTENT_DIR .DIRECTORY_SEPARATOR . $backup_dir . DIRECTORY_SEPARATOR . $file;
                if (file_exists($download_path))
                {
                    @wp_delete_file($download_path);
                }
            }
        }
    }

    public function cleanup_remote_backup()
    {
        if(!empty($this->config['remote']))
        {
            $files=$this->get_files(false);
            foreach($this->config['remote'] as $remote)
            {
                if(!class_exists('WPvivid_downloader'))
                    include_once WPVIVID_PLUGIN_DIR . '/includes/class-wpvivid-downloader.php';
                WPvivid_downloader::delete($remote,$files);
            }
        }
    }

    public function check_has_zero_date()
    {
        if(isset($this->config['backup']['files']))
        {
            $tmp_data = $this->config['backup']['files'];

            if(!class_exists('WPvivid_ZipClass'))
                include_once WPVIVID_PLUGIN_DIR . '/includes/class-wpvivid-zipclass.php';
            $zip=new WPvivid_ZipClass();
            foreach ($tmp_data as $file)
            {
                $path=$this->get_backup_path($file['file_name']);
                if(file_exists($path))
                {
                    $ret=$zip->get_json_data($path);
                    if($ret['result'] === WPVIVID_SUCCESS)
                    {
                        $json=$ret['json_data'];
                        $json = json_decode($json, 1);
                        if (!is_null($json))
                        {
                            if (isset($json['has_child']))
                            {
                                if (isset($json['child_file']))
                                {
                                    foreach ($json['child_file'] as $child_file_name => $child_file_info)
                                    {
                                        if(isset($child_file_info['find_zero_date']) && $child_file_info['find_zero_date'] == 1)
                                        {
                                            return true;
                                        }
                                    }
                                }
                            }
                            else
                            {
                                if(isset($json['find_zero_date']) && $json['find_zero_date'] == 1)
                                {
                                    return true;
                                }
                            }
                        }
                        else {
                            return false;
                        }
                    }
                    elseif($ret['result'] === WPVIVID_FAILED)
                    {
                        return false;
                    }
                }
            }
            return false;
        }
        else
        {
            return false;
        }
    }

    public function check_wp_version()
    {
        if(isset($this->config['backup']['files']))
        {
            $tmp_data = $this->config['backup']['files'];

            if(!class_exists('WPvivid_ZipClass'))
                include_once WPVIVID_PLUGIN_DIR . '/includes/class-wpvivid-zipclass.php';
            $zip=new WPvivid_ZipClass();
            foreach ($tmp_data as $file)
            {
                $path=$this->get_backup_path($file['file_name']);
                if(file_exists($path))
                {
                    $ret=$zip->get_json_data($path);
                    if($ret['result'] === WPVIVID_SUCCESS)
                    {
                        $json=$ret['json_data'];
                        $json = json_decode($json, 1);
                        if (!is_null($json))
                        {
                            if (isset($json['has_child']))
                            {
                                if (isset($json['child_file']))
                                {
                                    foreach ($json['child_file'] as $child_file_name => $child_file_info)
                                    {
                                        if(isset($child_file_info['wp_version']))
                                        {
                                            $backup_wp_version=$child_file_info['wp_version'];
                                            $wp_version = get_bloginfo( 'version' );
                                            if (version_compare($wp_version,$backup_wp_version,'>'))
                                            {
                                                return false;
                                            }
                                            else
                                            {
                                                return true;
                                            }
                                        }
                                        else
                                        {
                                            return true;
                                        }
                                    }
                                }
                            }
                            else
                            {
                                if(isset($json['wp_version']))
                                {
                                    $backup_wp_version=$json['wp_version'];
                                    $wp_version = get_bloginfo( 'version' );
                                    if (version_compare($wp_version,$backup_wp_version,'>'))
                                    {
                                        return false;
                                    }
                                    else
                                    {
                                        return true;
                                    }
                                }
                                else
                                {
                                    return true;
                                }
                            }
                        }
                        else {
                            return true;
                        }
                    }
                    elseif($ret['result'] === WPVIVID_FAILED)
                    {
                        return true;
                    }
                }
            }
            return true;
        }
        else
        {
            return true;
        }
    }
}

class WPvivid_Backup
{
    public $task;
    public $backup_type_report = '';
    //public $config;

    public function __construct($task_id=false,$task=false)
    {
        if($task_id!==false)
        {
            $this->task=new WPvivid_Backup_Task($task_id);
        }
        else if($task!==false)
        {
            $this->task=new WPvivid_Backup_Task(false,$task);
        }
        else
        {
            $this->task=new WPvivid_Backup_Task();
        }
        //$this->config=$config;
    }

	public function init_options($task_id)
    {
        $this->task=new WPvivid_Backup_Task($task_id);
    }

	public function backup($task_id)
    {
        $this->init_options($task_id);
        $next_backup=$this->task->get_need_backup();

        $this->backup_type_report = '';

        while($next_backup!==false)
        {
            global $wpvivid_plugin;
            $wpvivid_plugin->set_time_limit($this->task->get_id());
            $this->task->update_sub_task_progress($next_backup['key'],0, sprintf('Start backing up %s.', $next_backup['key']));
            $wpvivid_plugin->wpvivid_log->WriteLog('Prepare to backup '.$next_backup['key'].' files.','notice');
            $this->backup_type_report .= $next_backup['key'].',';
            if(isset($next_backup['files'])) {
                $wpvivid_plugin->wpvivid_log->WriteLog('File number: ' . sizeof($next_backup['files']), 'notice');
            }
            $result = $this->_backup($next_backup);
            $wpvivid_plugin->wpvivid_log->WriteLog('Backing up '.$next_backup['key'].' completed.','notice');
            $this->task->update_sub_task_progress($next_backup['key'],1, sprintf('Backing up %s finished.', $next_backup['key']));
            $this->task->update_backup_result($next_backup,$result);
            $wpvivid_plugin->check_cancel_backup($task_id);
            unset($next_backup);
            $next_backup=$this->task->get_need_backup();
        }

        WPvivid_Setting::update_option('wpvivid_backup_report', $this->backup_type_report);
        $this->cleanup();

        $ret=$this->task->get_backup_result();
        return $ret;
	}

    private function _backup($data)
    {
        global $wpvivid_plugin;
        $result['result']=WPVIVID_FAILED;
        $result['error']='test error';

        $is_type_db = false;
        $is_type_db = apply_filters('wpvivid_check_type_database', $is_type_db, $data);
        if($is_type_db)
        {
            include_once WPVIVID_PLUGIN_DIR .'/includes/class-wpvivid-backup-database.php';
            $wpvivid_plugin->wpvivid_log->WriteLog('Start exporting database.','notice');
            $backup_database = new WPvivid_Backup_Database();
            $result = $backup_database -> backup_database($data,$this->task->get_id());
            $wpvivid_plugin->wpvivid_log->WriteLog('Exporting database finished.','notice');
            if($result['result']==WPVIVID_SUCCESS)
            {
                $data['files']=$result['files'];
            }
            else
            {
                return $result;
            }
        }

        if(!class_exists('WPvivid_ZipClass'))
            include_once WPVIVID_PLUGIN_DIR . '/includes/class-wpvivid-zipclass.php';
        $zip = new WPvivid_ZipClass();
        if(is_array($zip->last_error))
        {
            return $zip->last_error;
        }

        if(isset($data['resume_packages']))
        {
            global $wpvivid_plugin;
            $wpvivid_plugin->wpvivid_log->WriteLog('Start compressing '.$data['key'],'notice');

            $packages=$this->task->get_packages_info($data['key']);
            if($packages===false)
            {
                if(isset($data['plugin_subpackage']))
                {
                    $ret =$zip->get_plugin_packages($data);
                }
                else if(isset($data['uploads_subpackage']))
                {
                    $ret =$zip->get_upload_packages($data);
                }
                else
                {
                    if($data['key']==WPVIVID_BACKUP_TYPE_MERGE)
                        $ret =$zip->get_packages($data,true);
                    else
                        $ret =$zip->get_packages($data);
                }

                $packages=$this->task->set_packages_info($data['key'],$ret['packages']);
            }

            $temp_dir = $data['path'].'temp-'.$data['prefix'].DIRECTORY_SEPARATOR;
            define(PCLZIP_TEMPORARY_DIR,$temp_dir);

            $result['result']=WPVIVID_SUCCESS;
            $result['files']=array();
            foreach ($packages as $package)
            {
                $wpvivid_plugin->set_time_limit($this->task->get_id());
                if(!empty($package['files'])&&$package['backup']==false)
                {
                    if(isset($data['uploads_subpackage']))
                    {
                        $files=$zip->get_upload_files_from_cache($package['files']);
                    }
                    else
                    {
                        $files=$package['files'];
                    }

                    if(empty($files))
                        continue;

                    $zip_ret=$zip->_zip($package['path'],$files, $data,$package['json']);
                    if($zip_ret['result']==WPVIVID_SUCCESS)
                    {
                        if(isset($data['uploads_subpackage']))
                        {
                            if(file_exists($package['files']))
                            {
                                @wp_delete_file($package['files']);
                            }
                        }

                        $result['files'][] = $zip_ret['file_data'];
                        $package['backup']=true;
                        $this->task->update_packages_info($data['key'],$package,$zip_ret['file_data']);
                        if($data['key']==WPVIVID_BACKUP_TYPE_MERGE)
                        {
                            $this->cleanup_finished_package($package['files']);
                        }
                    }
                    else
                    {
                        $result=$zip_ret;
                        break;
                    }
                }else {
                    continue;
                }
            }
            $wpvivid_plugin->wpvivid_log->WriteLog('Compressing '.$data['key'].' completed','notice');
            return $result;
        }
        else
        {
            $is_additional_db = false;
            $is_additional_db = apply_filters('wpvivid_check_additional_database', $is_additional_db, $data);
            if($is_additional_db){
                $result =$zip->compress_additional_database($data);
            }
            else {
                $result =$zip->compress($data);
            }

            if($is_type_db)
            {
                foreach ($data['files'] as $sql_file)
                {
                    @wp_delete_file($sql_file);
                }
            }
        }

        return $result;
    }

    public function cleanup()
    {
        $files=$this->task->get_need_cleanup_files();

        foreach ($files as $file)
        {
            if(file_exists($file)) {
                global $wpvivid_plugin;
                $wpvivid_plugin->wpvivid_log->WriteLog('Cleaned up file, filename: '.$file,'notice');
                @wp_delete_file($file);
            }
        }
    }

    public function cleanup_finished_package($files)
    {
        foreach ($files as $file)
        {
            if(file_exists($file))
            {
                global $wpvivid_plugin;
                $wpvivid_plugin->wpvivid_log->WriteLog('Cleaned up file, filename: '.$file,'notice');
                @wp_delete_file($file);
            }
        }
    }

    public function clean_backup()
    {
        $path = WP_CONTENT_DIR.DIRECTORY_SEPARATOR.WPvivid_Setting::get_backupdir();
        $handler=opendir($path);
        if($handler!==false)
        {
            while(($filename=readdir($handler))!==false)
            {
                if(preg_match('#'.$this->task->get_id().'#',$filename) || preg_match('#'.apply_filters('wpvivid_fix_wpvivid_free', $this->task->get_id()).'#',$filename))
                {
                    @wp_delete_file($path.DIRECTORY_SEPARATOR.$filename);
                }
            }
            @closedir($handler);
        }
    }

    public function clearcache()
    {
        $task_id=$this->task->get_prefix();
        $path = WP_CONTENT_DIR.DIRECTORY_SEPARATOR.WPvivid_Setting::get_backupdir();
        $handler=opendir($path);
        if($handler!==false)
        {
            while(($filename=readdir($handler))!==false)
            {
                if(is_dir($path.DIRECTORY_SEPARATOR.$filename) && preg_match('#temp-'.$task_id.'#',$filename))
                {
                    $this->deldir($path.DIRECTORY_SEPARATOR.$filename,'',true);
                }
                if(is_dir($path.DIRECTORY_SEPARATOR.$filename) && preg_match('#temp-'.$task_id.'#',$filename))
                {
                    $this->deldir($path.DIRECTORY_SEPARATOR.$filename,'',true);
                }
                if(preg_match('#pclzip-.*\.tmp#', $filename)){
                    @wp_delete_file($path.DIRECTORY_SEPARATOR.$filename);
                }
                if(preg_match('#pclzip-.*\.gz#', $filename)){
                    @wp_delete_file($path.DIRECTORY_SEPARATOR.$filename);
                }
            }
            @closedir($handler);
        }

    }

    public function deldir($path,$exclude=array(),$flag = false)
    {
        if(!is_dir($path))
        {
            return ;
        }
        $handler=opendir($path);
        if(empty($handler))
            return ;
        while(($filename=readdir($handler))!==false)
        {
            if($filename != "." && $filename != "..")
            {
                if(is_dir($path.DIRECTORY_SEPARATOR.$filename)){
                    if(empty($exclude)||$this->regex_match($exclude['directory'],$path.DIRECTORY_SEPARATOR.$filename ,0)){
                        $this->deldir( $path.DIRECTORY_SEPARATOR.$filename ,$exclude, $flag);
                        @rmdir( $path.DIRECTORY_SEPARATOR.$filename );
                    }
                }else{
                    if(empty($exclude)||$this->regex_match($exclude['file'],$path.DIRECTORY_SEPARATOR.$filename ,0)){
                        @wp_delete_file($path.DIRECTORY_SEPARATOR.$filename);
                    }
                }
            }
        }
        if($handler)
            @closedir($handler);
        if($flag)
            @rmdir($path);
    }

    public function regex_match($regex_array,$string,$mode)
    {
        if(empty($regex_array))
        {
            return true;
        }

        if($mode==0)
        {
            foreach ($regex_array as $regex)
            {
                if(preg_match($regex,$string))
                {
                    return false;
                }
            }

            return true;
        }

        if($mode==1)
        {
            foreach ($regex_array as $regex)
            {
                if(preg_match($regex,$string))
                {
                    return true;
                }
            }

            return false;
        }

        return true;
    }

    public function clean_remote_backup($remotes,$files)
    {
        $remote_option=array_shift($remotes);

        if(!is_null($remote_option))
        {
            global $wpvivid_plugin;
            if(!class_exists('WPvivid_Remote_collection'))
            {
                include_once WPVIVID_PLUGIN_DIR . '/includes/class-wpvivid-remote-collection.php';
                $wpvivid_plugin->remote_collection=new WPvivid_Remote_collection();
            }
            $remote=$wpvivid_plugin->remote_collection->get_remote($remote_option);
            $remote ->cleanup($files);
        }
    }
}includes/class-wpvivid-log.php000064400000013075151327705670012460 0ustar00<?php

if (!defined('WPVIVID_PLUGIN_DIR')){
    die;
}
class WPvivid_Log
{
    public $log_file;
    public $log_file_handle;

    public function __construct()
    {
        $this->log_file_handle=false;
    }

    public function CreateLogFile($file_name,$type,$describe)
    {
        if($type=='has_folder')
        {
            $this->log_file=$file_name;
        }
        else
        {
            $this->log_file=$this->GetSaveLogFolder().$file_name.'_log.txt';
        }
        if(file_exists($this->log_file))
        {
            @wp_delete_file( $this->log_file);
        }
        $this->log_file_handle = fopen($this->log_file, 'a');
        $offset=get_option('gmt_offset');
        $time =gmdate("Y-m-d H:i:s",time()+$offset*60*60);
        $text='Log created: '.$time."\n";
        $text.='Type: '.$describe."\n";
        fwrite($this->log_file_handle,$text);

        return $this->log_file;
    }

    public function OpenLogFile($file_name,$type='no_folder',$delete=0)
    {
        if($type=='has_folder')
        {
            $this->log_file=$file_name;
        }
        else
        {
            $this->log_file=$this->GetSaveLogFolder().$file_name.'_log.txt';
        }
        if($delete==1)
        {
            wp_delete_file( $this->log_file);
        }
        $this->log_file_handle = fopen($this->log_file, 'a');

        return $this->log_file;
    }

    public function WriteLog($log,$type)
    {
        if ($this->log_file_handle)
        {
            $offset=get_option('gmt_offset');
            $time =gmdate("Y-m-d H:i:s",time()+$offset*60*60);
            $text='['.$time.']'.'['.$type.']'.$log."\n";
            fwrite($this->log_file_handle,$text );
        }
    }

    public function CloseFile()
    {
        if ($this->log_file_handle)
        {
            fclose($this->log_file_handle);
            $this->log_file_handle=false;
        }
    }

    public function GetSaveLogFolder()
    {
        include_once plugin_dir_path( dirname( __FILE__ ) ) . 'includes/class-wpvivid-setting.php';

        $options = WPvivid_Setting::get_option('wpvivid_common_setting');

        if(!isset($options['log_save_location']))
        {
            //WPvivid_Setting::set_default_common_option();
            $options['log_save_location']=WPVIVID_DEFAULT_LOG_DIR;
            update_option('wpvivid_common_setting', $options, 'no');

            $options = WPvivid_Setting::get_option('wpvivid_common_setting');
        }

        if(!is_dir(WP_CONTENT_DIR.DIRECTORY_SEPARATOR.$options['log_save_location']))
        {
            @mkdir(WP_CONTENT_DIR.DIRECTORY_SEPARATOR.$options['log_save_location'],0777,true);
            //@fopen(WP_CONTENT_DIR.DIRECTORY_SEPARATOR.$options['log_save_location'].DIRECTORY_SEPARATOR.'index.html', 'x');
            $tempfile=@fopen(WP_CONTENT_DIR.DIRECTORY_SEPARATOR.$options['log_save_location'].DIRECTORY_SEPARATOR.'.htaccess', 'x');
            if($tempfile)
            {
                //$text="deny from all";
                $text="<IfModule mod_rewrite.c>\r\nRewriteEngine On\r\nRewriteRule .* - [F,L]\r\n</IfModule>";
                fwrite($tempfile,$text );
            }
        }

        return WP_CONTENT_DIR.DIRECTORY_SEPARATOR.$options['log_save_location'].DIRECTORY_SEPARATOR;
    }

    public function WriteLogHander()
    {
        if ($this->log_file_handle)
        {
            global $wp_version;
            global $wpdb;

            $sapi_type=php_sapi_name();
            if($sapi_type=='cgi-fcgi'||$sapi_type==' fpm-fcgi') {
                $fcgi='On';
            }
            else {
                $fcgi='Off';
            }

            $options=WPvivid_Setting::get_option('wpvivid_common_setting');
            if(isset($options['max_execution_time'])) {
                $max_execution_time=$options['max_execution_time'];
            }
            else {
                $max_execution_time=WPVIVID_MAX_EXECUTION_TIME;
            }

            $log='server info fcgi:'.$fcgi.' max execution time: '.$max_execution_time.' wp version:'.$wp_version.' php version:'.phpversion().' db version:'.$wpdb->db_version().' php ini:safe_mode:'.ini_get('safe_mode').' ';
            $log.='memory_limit:'.ini_get('memory_limit').' memory_get_usage:'.size_format(memory_get_usage(),2).' memory_get_peak_usage:'.size_format(memory_get_peak_usage(),2);
            $log.=' extensions:';
            $loaded_extensions = get_loaded_extensions();
            if(!in_array('PDO', $loaded_extensions))
            {
                $log.='PDO not enabled ';
            }
            else
            {
                $log.='PDO enabled ';
            }
            if(!in_array('curl', $loaded_extensions))
            {
                $log.='curl not enabled ';
            }
            else
            {
                $log.='curl enabled ';
            }

            if(!in_array('zlib', $loaded_extensions)) {
                $log .= 'zlib not enabled ';
            }
            else
            {
                $log.='zlib enabled ';
            }

            $log.=' ';
            if(is_multisite())
            {
                $log.=' is_multisite:1';
            }
            else
            {
                $log.=' is_multisite:0';
            }

            $offset=get_option('gmt_offset');
            $time =gmdate("Y-m-d H:i:s",time()+$offset*60*60);
            $text='['.$time.']'.'[notice]'.$log."\n";
            fwrite($this->log_file_handle,$text );
        }
    }
}includes/class-wpvivid-backup-database.php000064400000012464151327705670014707 0ustar00<?php

if (!defined('WPVIVID_PLUGIN_DIR')){
    die;
}
define('NECESSARY','1');
define('OPTION','0');
class WPvivid_Backup_Database
{
    private $task_id;

    public function __construct()
    {
    }

    public function wpvivid_archieve_database_info($databases, $data)
    {
        if(isset($data['dump_db']))
        {
            $sql_info['file_name'] =$data['sql_file_name'];
            $sql_info['database'] = DB_NAME;
            $sql_info['host'] = DB_HOST;
            $sql_info['user'] = DB_USER;
            $sql_info['pass'] = DB_PASSWORD;
            $databases[] = $sql_info;
        }
        return $databases;
    }

    public function backup_database($data,$task_id = '')
    {
        global $wpvivid_plugin;
        $dump=null;

        try
        {
            $this->task_id=$task_id;

            //$backup_file =$data['sql_file_name'];

            require_once 'class-wpvivid-mysqldump-method.php';
            require_once 'class-wpvivid-mysqldump.php';
            include_once WPVIVID_PLUGIN_DIR . '/includes/class-wpvivid-db-method.php';

            $db_method=new WPvivid_DB_Method();
            $version =$db_method->get_mysql_version();

            if(version_compare('4.1.0',$version) > 0)
            {
                return array('result'=>WPVIVID_FAILED,'error'=>'Your MySQL version is too old. Please upgrade at least to MySQL 4.1.0.');
            }

            if(version_compare('5.3.0',phpversion()) > 0){
                return array('result'=>WPVIVID_FAILED,'error'=>'Your PHP version is too old. Please upgrade at least to PHP 5.3.0.');
            }

            $db_method->check_max_allowed_packet();
            add_filter('wpvivid_exclude_db_table', array($this, 'exclude_table'),10,2);
            $exclude=array();
            $exclude = apply_filters('wpvivid_exclude_db_table',$exclude, $data);
            $include=array();
            $include = apply_filters('wpvivid_include_db_table',$include, $data);

            $site_url = apply_filters('wpvivid_dump_set_site_url',get_site_url(), $data);
            $home_url = apply_filters('wpvivid_dump_set_home_url',get_home_url(), $data);
            $content_url=apply_filters('wpvivid_dump_set_content_url',content_url(), $data);

            global $wpdb;
            if (is_multisite() && !defined('MULTISITE'))
            {
                $prefix = $wpdb->base_prefix;
            } else {
                $prefix = $wpdb->get_blog_prefix(0);
            }
            $prefix=apply_filters('wpvivid_dump_set_prefix',$prefix, $data);

            add_filter('wpvivid_archieve_database_info', array($this, 'wpvivid_archieve_database_info'), 10, 2);
            $databases=array();
            $databases = apply_filters('wpvivid_archieve_database_info', $databases, $data);
            $is_additional_db = false;
            if($data['key'] === 'backup_additional_db')
            {
                $is_additional_db = true;
            }

            $backup_files = array();
            foreach ($databases as $sql_info)
            {
                $database_name = $sql_info['database'];
                $backup_file = $sql_info['file_name'];
                $backup_files[] = $backup_file;
                $host = $sql_info['host'];
                $user = $sql_info['user'];
                $pass = $sql_info['pass'];

                $dumpSettings=array();
                $dumpSettings['exclude-tables']=$exclude;
                $dumpSettings['include-tables']=$include;
                $dumpSettings['add-drop-table']=true;
                $dumpSettings['extended-insert']=false;

                $dumpSettings['site_url']=$site_url;
                $dumpSettings['home_url']=$home_url;
                $dumpSettings['content_url']=$content_url;
                $dumpSettings['prefix']=$prefix;

                $dump = new WPvivid_Mysqldump($host, $database_name, $user, $pass, $is_additional_db, $dumpSettings);

                if (file_exists($backup_file))
                    @wp_delete_file($backup_file);

                $dump->task_id=$task_id;
                $dump->start($backup_file);
            }

            unset($pdo);
        }
        catch (Exception $e)
        {
            $str_last_query_string='';
            if(!is_null($dump))
            {
                $str_last_query_string=$dump->last_query_string;
            }
            if(!empty($str_last_query_string))
            {
                $wpvivid_plugin->wpvivid_log->WriteLog('last query string:'.$str_last_query_string,'error');
            }
            $message = 'A exception ('.get_class($e).') occurred '.$e->getMessage().' (Code: '.$e->getCode().', line '.$e->getLine().' in '.$e->getFile().')';
            return array('result'=>WPVIVID_FAILED,'error'=>$message);
        }

	    $files = array();
        $files = $backup_files;
        return array('result'=>WPVIVID_SUCCESS,'files'=>$files);
    }

    public function exclude_table($exclude,$data)
    {
        global $wpdb;
        if (is_multisite() && !defined('MULTISITE'))
        {
            $prefix = $wpdb->base_prefix;
        } else {
            $prefix = $wpdb->get_blog_prefix(0);
        }
        $exclude = array('/^(?!' . $prefix . ')/i');
        return $exclude;
    }
}includes/class-wpvivid-public-interface.php000064400000037502151327705670015114 0ustar00<?php

class WPvivid_Public_Interface
{
    public function __construct()
    {

    }

    public function mainwp_data($data){
        $action = sanitize_text_field($data['mwp_action']);
        if (has_filter($action)) {
            $ret = apply_filters($action, $data);
        } else {
            $ret['result'] = WPVIVID_FAILED;
            $ret['error'] = 'Unknown function';
        }
        return $ret;
    }

    public function prepare_backup($backup_options){
        global $wpvivid_plugin;
        if(isset($backup_options)&&!empty($backup_options)){
            if (is_null($backup_options)) {
                $ret['error']='Invalid parameter param:'.$backup_options;
                return $ret;
            }
            $backup_options = apply_filters('wpvivid_custom_backup_options', $backup_options);

            if(!isset($backup_options['type']))
            {
                $backup_options['type']=__('Manual', 'wpvivid-backuprestore');
                $backup_options['action']='backup';
            }
            $ret = $wpvivid_plugin->check_backup_option($backup_options, $backup_options['type']);
            if($ret['result']!='success') {
                return $ret;
            }
            $ret=$wpvivid_plugin->pre_backup($backup_options);
            if($ret['result']=='success') {
                //Check the website data to be backed up
                $ret['check']=$wpvivid_plugin->check_backup($ret['task_id'],$backup_options);
                if(isset($ret['check']['result']) && $ret['check']['result'] == 'failed') {
                    $ret['error']=$ret['check']['error'];
                    return $ret;
                }
            }
        }
        else{
            $ret['error']='Error occurred while parsing the request data. Please try to run backup again.';
            return $ret;
        }
        return $ret;
    }

    public function get_status(){
        $ret['result']='success';
        $list_tasks=array();
        $tasks=WPvivid_Setting::get_tasks();
        foreach ($tasks as $task)
        {
            $backup = new WPvivid_Backup_Task($task['id']);
            $list_tasks[$task['id']]=$backup->get_backup_task_info($task['id']);
            if($list_tasks[$task['id']]['task_info']['need_update_last_task']===true){
                $task_msg = WPvivid_taskmanager::get_task($task['id']);
                WPvivid_Setting::update_option('wpvivid_last_msg',$task_msg);
                apply_filters('wpvivid_set_backup_report_addon_mainwp', $task_msg);
            }
        }
        $ret['wpvivid']['task']=$list_tasks;
        $backuplist=WPvivid_Backuplist::get_backuplist();
        $schedule=WPvivid_Schedule::get_schedule();
        $ret['wpvivid']['backup_list']=$backuplist;
        $ret['wpvivid']['schedule']=$schedule;
        $ret['wpvivid']['schedule']['last_message']=WPvivid_Setting::get_last_backup_message('wpvivid_last_msg');
        WPvivid_taskmanager::delete_marked_task();
        return $ret;
    }

    public function get_backup_schedule(){
        $schedule=WPvivid_Schedule::get_schedule();
        $ret['result']='success';
        $ret['wpvivid']['schedule']=$schedule;
        $ret['wpvivid']['schedule']['last_message']=WPvivid_Setting::get_last_backup_message('wpvivid_last_msg');
        return $ret;
    }

    public function get_backup_list(){
        $backuplist=WPvivid_Backuplist::get_backuplist();
        $ret['result']='success';
        $ret['wpvivid']['backup_list']=$backuplist;
        return $ret;
    }

    public function get_default_remote(){
        global $wpvivid_plugin;
        $ret['result']='success';
        $ret['remote_storage_type']=$wpvivid_plugin->function_realize->_get_default_remote_storage();
        return $ret;
    }

    public function delete_backup($backup_id, $force_del){
        global $wpvivid_plugin;
        if(!isset($backup_id)||empty($backup_id)||!is_string($backup_id)) {
            $ret['error']='Invalid parameter param: backup_id.';
            return $ret;
        }
        if(!isset($force_del)){
            $ret['error']='Invalid parameter param: force.';
            return $ret;
        }
        if($force_del==0||$force_del==1) {
        }
        else {
            $force_del=0;
        }
        $backup_id=sanitize_key($backup_id);
        $ret=$wpvivid_plugin->delete_backup_by_id($backup_id, $force_del);
        $backuplist=WPvivid_Backuplist::get_backuplist();
        $ret['wpvivid']['backup_list']=$backuplist;
        return $ret;
    }

    public function delete_backup_array($backup_id_array){
        global $wpvivid_plugin;
        if(!isset($backup_id_array)||empty($backup_id_array)||!is_array($backup_id_array)) {
            $ret['error']='Invalid parameter param: backup_id';
            return $ret;
        }
        $ret=array();
        foreach($backup_id_array as $backup_id)
        {
            $backup_id=sanitize_key($backup_id);
            $ret=$wpvivid_plugin->delete_backup_by_id($backup_id);
        }
        $backuplist=WPvivid_Backuplist::get_backuplist();
        $ret['wpvivid']['backup_list']=$backuplist;
        return $ret;
    }

    public function set_security_lock($backup_id, $lock){
        if(!isset($backup_id)||empty($backup_id)||!is_string($backup_id)){
            $ret['error']='Backup id not found';
            return $ret;
        }
        if(!isset($lock)){
            $ret['error']='Invalid parameter param: lock';
            return $ret;
        }
        $backup_id=sanitize_key($backup_id);
        if($lock==0||$lock==1) {
        }
        else {
            $lock=0;
        }
        WPvivid_Backuplist::set_security_lock($backup_id,$lock);
        $backuplist=WPvivid_Backuplist::get_backuplist();
        $ret['wpvivid']['backup_list']=$backuplist;
        return $ret;
    }

    public function view_log($backup_id){
        global $wpvivid_plugin;
        if (!isset($backup_id)||empty($backup_id)||!is_string($backup_id)){
            $ret['error']='Backup id not found';
            return $ret;
        }
        $backup_id=sanitize_key($backup_id);
        $ret=$wpvivid_plugin->function_realize->_get_log_file('backuplist', $backup_id);
        if($ret['result'] == 'success') {
            $file = fopen($ret['log_file'], 'r');
            if (!$file) {
                $ret['result'] = 'failed';
                $ret['error'] = __('Unable to open the log file.', 'wpvivid-backuprestore');
                return $ret;
            }
            $buffer = '';
            while (!feof($file)) {
                $buffer .= fread($file, 1024);
            }
            fclose($file);
            $ret['data'] = $buffer;
        }
        else{
            $ret['error']='Unknown error';
        }
        return $ret;
    }

    public function read_last_backup_log($log_file_name){
        global $wpvivid_plugin;
        if(!isset($log_file_name)||empty($log_file_name)||!is_string($log_file_name))
        {
            $ret['result']='failed';
            $ret['error']=__('Reading the log failed. Please try again.', 'wpvivid-backuprestore');
            return $ret;
        }
        $log_file_name=sanitize_text_field($log_file_name);
        $ret=$wpvivid_plugin->function_realize->_get_log_file('lastlog', $log_file_name);
        if($ret['result'] == 'success') {
            $file = fopen($ret['log_file'], 'r');
            if (!$file) {
                $ret['result'] = 'failed';
                $ret['error'] = __('Unable to open the log file.', 'wpvivid-backuprestore');
                return $ret;
            }
            $buffer = '';
            while (!feof($file)) {
                $buffer .= fread($file, 1024);
            }
            fclose($file);
            $ret['result'] = 'success';
            $ret['data'] = $buffer;
        }
        else{
            $ret['error']='Unknown error';
        }
        return $ret;
    }

    public function view_backup_task_log($backup_task_id){
        global $wpvivid_plugin;
        if (!isset($backup_task_id)||empty($backup_task_id)||!is_string($backup_task_id)){
            $ret['error']='Reading the log failed. Please try again.';
            return $ret;
        }
        $backup_task_id = sanitize_key($backup_task_id);
        $ret=$wpvivid_plugin->function_realize->_get_log_file('tasklog', $backup_task_id);
        if($ret['result'] == 'success') {
            $file = fopen($ret['log_file'], 'r');
            if (!$file) {
                $ret['result'] = 'failed';
                $ret['error'] = __('Unable to open the log file.', 'wpvivid-backuprestore');
                return $ret;
            }
            $buffer = '';
            while (!feof($file)) {
                $buffer .= fread($file, 1024);
            }
            fclose($file);
            $ret['result'] = 'success';
            $ret['data'] = $buffer;
        }
        else{
            $ret['error']='Unknown error';
        }
        return $ret;
    }

    public function backup_cancel($task_id = ''){
        global $wpvivid_plugin;
        $ret=$wpvivid_plugin->function_realize->_backup_cancel();
        return $ret;
    }

    public function init_download_page($backup_id){
        global $wpvivid_plugin;
        if(!isset($backup_id)||empty($backup_id)||!is_string($backup_id)) {
            $ret['error']='Invalid parameter param:'.$backup_id;
            return $ret;
        }
        else {
            $backup_id=sanitize_key($backup_id);
            return $wpvivid_plugin->init_download($backup_id);
        }
    }

    public function prepare_download_backup($backup_id, $file_name){
        if(!isset($backup_id)||empty($backup_id)||!is_string($backup_id))
        {
            $ret['error']='Invalid parameter param:'.$backup_id;
            return $ret;
        }
        if(!isset($file_name)||empty($file_name)||!is_string($file_name))
        {
            $ret['error']='Invalid parameter param:'.$file_name;
            return $ret;
        }
        $download_info=array();
        $download_info['backup_id']=sanitize_key($backup_id);
        $download_info['file_name'] = $file_name;

        @set_time_limit(600);
        if (session_id())
            session_write_close();
        try
        {
            $downloader=new WPvivid_downloader();
            $downloader->ready_download($download_info);
        }
        catch (Exception $e)
        {
            $message = 'A exception ('.get_class($e).') occurred '.$e->getMessage().' (Code: '.$e->getCode().', line '.$e->getLine().' in '.$e->getFile().')';
            error_log($message);
            return array('error'=>$message);
        }
        catch (Error $e)
        {
            $message = 'A error ('.get_class($e).') has occurred: '.$e->getMessage().' (Code: '.$e->getCode().', line '.$e->getLine().' in '.$e->getFile().')';
            error_log($message);
            return array('error'=>$message);
        }

        $ret['result']='success';
        return $ret;
    }

    public function get_download_task($backup_id){
        if(!isset($backup_id)||empty($backup_id)||!is_string($backup_id)) {
            $ret['error']='Invalid parameter param:'.$backup_id;
            return $ret;
        }
        else {
            $backup = WPvivid_Backuplist::get_backup_by_id($backup_id);
            if ($backup === false) {
                $ret['result'] = WPVIVID_FAILED;
                $ret['error'] = 'backup id not found';
                return $ret;
            }
            $backup_item = new WPvivid_Backup_Item($backup);
            $ret = $backup_item->update_download_page($backup_id);
            return $ret;
        }
    }

    public function download_backup($backup_id, $file_name){
        global $wpvivid_plugin;
        if(!isset($backup_id)||empty($backup_id)||!is_string($backup_id)) {
            $ret['error']='Invalid parameter param: backup_id';
            return $ret;
        }
        if(!isset($file_name)||empty($file_name)||!is_string($file_name)) {
            $ret['error']='Invalid parameter param: file_name';
            return $ret;
        }
        $backup_id=sanitize_key($backup_id);
        $cache=WPvivid_taskmanager::get_download_cache($backup_id);
        if($cache===false) {
            $wpvivid_plugin->init_download($backup_id);
            $cache=WPvivid_taskmanager::get_download_cache($backup_id);
        }
        $path=false;
        if(array_key_exists($file_name,$cache['files'])) {
            if($cache['files'][$file_name]['status']=='completed') {
                $path=$cache['files'][$file_name]['download_path'];
                $download_url = $cache['files'][$file_name]['download_url'];
            }
        }
        if($path!==false) {
            if (file_exists($path)) {
                $ret['download_url'] = $download_url;
                $ret['size'] = filesize($path);
            }
        }
        return $ret;
    }

    public function set_general_setting($setting){
        $ret=array();
        try {
            if(isset($setting)&&!empty($setting)) {
                $json_setting = $setting;
                $json_setting = stripslashes($json_setting);
                $setting = json_decode($json_setting, true);
                if (is_null($setting)) {
                    $ret['error']='bad parameter';
                    return $ret;
                }
                WPvivid_Setting::update_setting($setting);
            }

            $ret['result']='success';
        }
        catch (Exception $error) {
            $message = 'An exception has occurred. class: '.get_class($error).';msg: '.$error->getMessage().';code: '.$error->getCode().';line: '.$error->getLine().';in_file: '.$error->getFile().';';
            error_log($message);
            return array('error'=>$message);
        }
        return $ret;
    }

    public function set_schedule($schedule){
        $ret=array();
        try {
            if(isset($schedule)&&!empty($schedule)) {
                $json = $schedule;
                $json = stripslashes($json);
                $schedule = json_decode($json, true);
                if (is_null($schedule)) {
                    $ret['error']='bad parameter';
                    return $ret;
                }
                $ret=WPvivid_Schedule::set_schedule_ex($schedule);
                if($ret['result']!='success') {
                    return $ret;
                }
            }
            $ret['result']='success';
        }
        catch (Exception $error) {
            $message = 'An exception has occurred. class: '.get_class($error).';msg: '.$error->getMessage().';code: '.$error->getCode().';line: '.$error->getLine().';in_file: '.$error->getFile().';';
            error_log($message);
            return array('error'=>$message);
        }
        return $ret;
    }

    public function set_remote($remote){
        global $wpvivid_plugin;
        $ret=array();
        try {
            if(isset($remote)&&!empty($remote)) {
                $json = $remote;
                $json = stripslashes($json);
                $remote = json_decode($json, true);
                if (is_null($remote)) {
                    $ret['error']='bad parameter';
                    return $ret;
                }
                $wpvivid_plugin->function_realize->_set_remote($remote);
            }
            $ret['result']='success';
        }
        catch (Exception $error) {
            $message = 'An exception has occurred. class: '.get_class($error).';msg: '.$error->getMessage().';code: '.$error->getCode().';line: '.$error->getLine().';in_file: '.$error->getFile().';';
            error_log($message);
            return array('error'=>$message);
        }
        return $ret;
    }
}includes/index.php000064400000000032151327705670010202 0ustar00<?php // Silence is goldenincludes/staging/class-wpvivid-staging-log.php000064400000023660151327705670015547 0ustar00<?php

if (!defined('WPVIVID_PLUGIN_DIR'))
{
    die;
}

class WPvivid_Staging_Log_Free
{
    public $log_file;
    public $log_file_handle;

    public function __construct()
    {
        $this->log_file_handle=false;
    }

    public function CreateLogFile($file_name,$type,$describe)
    {
        if($type=='has_folder')
        {
            $this->log_file=$file_name;
        }
        else
        {
            $this->log_file=$this->GetSaveLogFolder().$file_name.'_log.txt';
        }
        if(file_exists($this->log_file))
        {
            @wp_delete_file( $this->log_file);
        }
        $this->log_file_handle = fopen($this->log_file, 'a');
        $offset=get_option('gmt_offset');
        $time =gmdate("Y-m-d H:i:s",time()+$offset*60*60);
        $text='Log created: '.$time."\n";
        $text.='Type: '.$describe."\n";
        fwrite($this->log_file_handle,$text);

        return $this->log_file;
    }

    public function OpenLogFile($file_name,$type='no_folder',$delete=0)
    {
        if($type=='has_folder')
        {
            $this->log_file=$file_name;
        }
        else
        {
            $this->log_file=$this->GetSaveLogFolder().$file_name.'_log.txt';
        }
        if($delete==1)
        {
            wp_delete_file( $this->log_file);
        }
        $this->log_file_handle = fopen($this->log_file, 'a');

        return $this->log_file;
    }

    public function WriteLog($log,$type)
    {
        if ($this->log_file_handle)
        {
            $offset=get_option('gmt_offset');
            $time =gmdate("Y-m-d H:i:s",time()+$offset*60*60);
            $text='['.$time.']'.'['.$type.']'.$log."\n";
            fwrite($this->log_file_handle,$text );
        }
    }

    public function CloseFile()
    {
        if ($this->log_file_handle)
        {
            fclose($this->log_file_handle);
            $this->log_file_handle=false;
        }
    }

    public function GetSaveLogFolder()
    {
        if(!is_dir(WP_CONTENT_DIR.DIRECTORY_SEPARATOR.'wpvivid_staging'))
        {
            @mkdir(WP_CONTENT_DIR.DIRECTORY_SEPARATOR.'wpvivid_staging',0777,true);
            @fopen(WP_CONTENT_DIR.DIRECTORY_SEPARATOR.'wpvivid_staging'.DIRECTORY_SEPARATOR.'index.html', 'x');
            $tempfile=@fopen(WP_CONTENT_DIR.DIRECTORY_SEPARATOR.'wpvivid_staging'.DIRECTORY_SEPARATOR.'.htaccess', 'x');
            if($tempfile)
            {
                //$text="deny from all";
                $text="<IfModule mod_rewrite.c>\r\nRewriteEngine On\r\nRewriteRule .* - [F,L]\r\n</IfModule>";
                fwrite($tempfile,$text );
            }
        }

        return WP_CONTENT_DIR.DIRECTORY_SEPARATOR.'wpvivid_staging'.DIRECTORY_SEPARATOR;
    }

    public function WriteLogHander()
    {
        if ($this->log_file_handle)
        {
            global $wp_version;
            global $wpdb;

            $sapi_type=php_sapi_name();
            if($sapi_type=='cgi-fcgi'||$sapi_type==' fpm-fcgi') {
                $fcgi='On';
            }
            else {
                $fcgi='Off';
            }

            $options=get_option('wpvivid_staging_options',array());

            if(isset($options['max_execution_time']))
            {
                $max_execution_time=$options['staging_max_execution_time'];
            }
            else {
                $max_execution_time=900;
            }

            $log='server info fcgi:'.$fcgi.' max execution time: '.$max_execution_time.' wp version:'.$wp_version.' php version:'.phpversion().' db version:'.$wpdb->db_version().' php ini:safe_mode:'.ini_get('safe_mode').' ';
            $log.='memory_limit:'.ini_get('memory_limit').' memory_get_usage:'.size_format(memory_get_usage(),2).' memory_get_peak_usage:'.size_format(memory_get_peak_usage(),2);
            $log.=' extensions:';
            $loaded_extensions = get_loaded_extensions();
            if(!in_array('PDO', $loaded_extensions))
            {
                $log.='PDO not enabled ';
            }
            else
            {
                $log.='PDO enabled ';
            }
            if(!in_array('curl', $loaded_extensions))
            {
                $log.='curl not enabled ';
            }
            else
            {
                $log.='curl enabled ';
            }

            if(!in_array('zlib', $loaded_extensions)) {
                $log .= 'zlib not enabled ';
            }
            else
            {
                $log.='zlib enabled ';
            }

            $log.=' ';
            if(is_multisite())
            {
                $log.=' is_multisite:1';
            }
            else
            {
                $log.=' is_multisite:0';
            }

            $offset=get_option('gmt_offset');
            $time =gmdate("Y-m-d H:i:s",time()+$offset*60*60);
            $text='['.$time.']'.'[notice]'.$log."\n";
            fwrite($this->log_file_handle,$text );
        }
    }
}

class WPvivid_Staging_error_log_free
{
    public static function create_error_log($log_file_name)
    {
        $dir=dirname($log_file_name);
        $file=basename($log_file_name);
        if(!is_dir($dir.DIRECTORY_SEPARATOR.'error'))
        {
            @mkdir($dir.DIRECTORY_SEPARATOR.'error',0777,true);
            @fopen($dir.DIRECTORY_SEPARATOR.'error'.'/index.html', 'x');
            $tempfile=@fopen($dir.DIRECTORY_SEPARATOR.'error'.'/.htaccess', 'x');
            if($tempfile)
            {
                //$text="deny from all";
                $text="<IfModule mod_rewrite.c>\r\nRewriteEngine On\r\nRewriteRule .* - [F,L]\r\n</IfModule>";
                fwrite($tempfile,$text );
                @fclose($tempfile);
            }
        }

        if(!file_exists($log_file_name))
        {
            return ;
        }

        if(file_exists($dir.DIRECTORY_SEPARATOR.'error'.DIRECTORY_SEPARATOR.$file))
        {
            @wp_delete_file($dir.DIRECTORY_SEPARATOR.'error'.DIRECTORY_SEPARATOR.$file);
        }

        @rename($log_file_name,$dir.DIRECTORY_SEPARATOR.'error'.DIRECTORY_SEPARATOR.$file);

        //self::delete_oldest_error_log();
    }

    public static function create_restore_error_log($log_file_name)
    {
        $dir=dirname($log_file_name);
        if(!is_dir($dir.DIRECTORY_SEPARATOR.'wpvivid_log'.DIRECTORY_SEPARATOR.'error'))
        {
            @mkdir($dir.DIRECTORY_SEPARATOR.'wpvivid_log'.DIRECTORY_SEPARATOR.'error',0777,true);
            @fopen($dir.DIRECTORY_SEPARATOR.'wpvivid_log'.DIRECTORY_SEPARATOR.'error'.'/index.html', 'x');
            $tempfile=@fopen($dir.DIRECTORY_SEPARATOR.'wpvivid_log'.DIRECTORY_SEPARATOR.'error'.'/.htaccess', 'x');
            if($tempfile)
            {
                //$text="deny from all";
                $text="<IfModule mod_rewrite.c>\r\nRewriteEngine On\r\nRewriteRule .* - [F,L]\r\n</IfModule>";
                fwrite($tempfile,$text );
                @fclose($tempfile);
            }
        }
        $id = uniqid('wpvivid-');
        $file=$id.'_restore_log.txt';
        if(file_exists($dir.DIRECTORY_SEPARATOR.'error'.DIRECTORY_SEPARATOR.$file))
        {
            @wp_delete_file($dir.DIRECTORY_SEPARATOR.'error'.DIRECTORY_SEPARATOR.$file);
        }

        @copy($log_file_name,$dir.DIRECTORY_SEPARATOR.'wpvivid_log'.DIRECTORY_SEPARATOR.'error'.DIRECTORY_SEPARATOR.$file);
        //self::delete_oldest_error_log();
    }

    public static function delete_oldest_error_log()
    {
        $files=array();
        $log=new WPvivid_Staging_Log_Free();
        $dir=$log->GetSaveLogFolder();
        $dir=$dir.'error';
        @$handler=opendir($dir);
        if($handler===false)
            return;
        $regex='#^wpvivid.*_log.txt#';
        while(($filename=readdir($handler))!==false)
        {
            if($filename != "." && $filename != "..")
            {
                if(is_dir($dir.DIRECTORY_SEPARATOR.$filename))
                {
                    continue;
                }else{
                    if(preg_match($regex,$filename))
                    {
                        $files[$filename] = $dir.DIRECTORY_SEPARATOR.$filename;
                    }
                }
            }
        }
        if($handler)
            @closedir($handler);
        $oldest=0;
        $oldest_filename='';
        $max_count=5;
        if(sizeof($files)>$max_count)
        {
            foreach ($files as $file)
            {
                if($oldest==0)
                {
                    $oldest=filemtime($file);
                    $oldest_filename=$file;
                }
                else
                {
                    if($oldest>filemtime($file))
                    {
                        $oldest=filemtime($file);
                        $oldest_filename=$file;
                    }
                }
            }

            if($oldest_filename!='')
            {
                @wp_delete_file($oldest_filename);
            }
        }
    }

    public static function get_error_log()
    {
        $log=new WPvivid_Staging_Log_Free();
        $dir=$log->GetSaveLogFolder();
        $dir=$dir.'error';
        $files=array();
        $handler=opendir($dir);
        if($handler === false){
            return $files;
        }
        $regex='#^wpvivid.*_log.txt#';
        while(($filename=readdir($handler))!==false)
        {
            if($filename != "." && $filename != "..")
            {
                if(is_dir($dir.$filename))
                {
                    continue;
                }
                else{
                    if(preg_match($regex,$filename))
                    {
                        $files[] = $dir.DIRECTORY_SEPARATOR.$filename;
                    }
                }
            }
        }
        if($handler)
            @closedir($handler);
        return $files;
    }
}includes/staging/class-wpvivid-staging-copy-db-ex.php000064400000167474151327705670016751 0ustar00<?php
if (!defined('WPVIVID_PLUGIN_DIR'))
{
    die;
}
class WPvivid_Staging_Copy_DB
{
    public $task;

    public $db_src_instance;
    public $db_des_instance;

    public $old_prefix;
    public $replace_prefix;
    public $new_prefix;

    public $old_site_url;
    public $old_home_url;

    public $new_site_url;
    public $new_home_url;

    public $replacing_table;

    public $placeholder;
    public $path_site;


    public function __construct($task_id)
    {
        $this->task=new WPvivid_Staging_Task($task_id);

        $this->db_src_instance=false;
        $this->db_des_instance=false;
        $this->placeholder=array();

        add_filter('wpvivid_restore_db_skip_replace_tables', array($this, 'skip_tables'),10,2);
        add_filter('wpvivid_restore_db_skip_replace_rows', array($this, 'skip_rows'),10,3);
    }

    public function skip_tables($skip_table,$table_name)
    {
        $skip_tables[]='adrotate_stats';
        $skip_tables[]='login_security_solution_fail';
        $skip_tables[]='icl_strings';
        $skip_tables[]='icl_string_positions';
        $skip_tables[]='icl_string_translations';
        $skip_tables[]='icl_languages_translations';
        $skip_tables[]='slim_stats';
        $skip_tables[]='slim_stats_archive';
        $skip_tables[]='es_online';
        $skip_tables[]='ahm_download_stats';
        $skip_tables[]='woocommerce_order_items';
        $skip_tables[]='woocommerce_sessions';
        $skip_tables[]='redirection_404';
        $skip_tables[]='redirection_logs';
        $skip_tables[]='wbz404_logs';
        $skip_tables[]='wbz404_redirects';
        $skip_tables[]='Counterize';
        $skip_tables[]='Counterize_UserAgents';
        $skip_tables[]='Counterize_Referers';
        $skip_tables[]='et_bloom_stats';
        $skip_tables[]='term_relationships';
        $skip_tables[]='lbakut_activity_log';
        $skip_tables[]='simple_feed_stats';
        $skip_tables[]='svisitor_stat';
        $skip_tables[]='itsec_log';
        $skip_tables[]='relevanssi_log';
        $skip_tables[]='wysija_email_user_stat';
        $skip_tables[]='wponlinebackup_generations';
        $skip_tables[]='blc_instances';
        $skip_tables[]='wp_rp_tags';
        $skip_tables[]='statpress';
        $skip_tables[]='wfHits';
        $skip_tables[]='wp_wfFileMods';
        $skip_tables[]='tts_trafficstats';
        $skip_tables[]='tts_referrer_stats';
        $skip_tables[]='dmsguestbook';
        $skip_tables[]='relevanssi';
        $skip_tables[]='wfFileMods';
        $skip_tables[]='learnpress_sessions';
        $skip_tables[]='icl_string_pages';
        $skip_tables[]='webarx_event_log';
        $skip_tables[]='duplicator_packages';
        $skip_tables[]='wsal_metadata';
        $skip_tables[]='wsal_occurrences';
        if(in_array(substr($table_name, strlen($this->new_prefix)),$skip_tables))
        {
            $skip_table=true;
        }
        else
        {
            $skip_table=false;
        }

        return $skip_table;
    }

    public function skip_rows($skip_rows,$table_name,$column_name)
    {
        $row['table_name']='posts';
        $row['column_name']='guid';
        $rows[]=$row;
        $row['table_name']='options';
        $row['column_name']='mainwp_child_subpages';
        $rows[]=$row;
        foreach ($rows as $row)
        {
            if($column_name==$row['column_name']&&$table_name==$this->new_prefix.$row['table_name'])
            {
                $skip_rows=true;
                break;
            }
        }

        return $skip_rows;
    }

    public function do_copy_db()
    {
        global $wpvivid_plugin;

        $this->db_src_instance=false;
        $this->db_des_instance=false;

        if($this->task->is_restore())
            $this->new_prefix=$this->task->get_temp_prefix();
        else
            $this->new_prefix=$this->task->get_db_prefix(true);
        $this->old_prefix=$this->task->get_db_prefix();

        if(!$this->init_copy_tables_list())
            return false;
        $table=$this->task->get_start('db');
        $this->task->update_calc_db_size('db');
        while($table&&!$table['finished'])
        {
            $ret=$this->copy_table($table,$this->task->get_db_insert_count());

            if($ret['result']=='failed')
            {
                $this->task->set_error($ret['error']);
                return false;
            }

            if(!$table['finished'])
            {
                $wpvivid_plugin->staging->log->WriteLog('Copying '.$this->task->get_db_insert_count().' queries is completed.','notice');
                $wpvivid_plugin->staging->log->WriteLog('The next copying table info: '.wp_json_encode($table),'notice');
            }

            $this->task->update_table('db',$table);
        }

        if($this->task->get_start('db')===false)
        {
            $wpvivid_plugin->staging->log->WriteLog('Copying '.$table['name'].' is completed.','notice');
            $this->task->update_calc_db_finish_size('db');
            $wpvivid_plugin->staging->log->WriteLog('Database copying is completed.','notice');
            $this->task->update_job_finished('db');
        }
        else
        {
            $wpvivid_plugin->staging->log->WriteLog('Copying '.$table['name'].' is completed.','notice');
            $this->task->update_table_finished('db',$table);
            $this->task->update_calc_db_finish_size('db');
        }

        return true;
    }

    public function do_replace_db()
    {
        global $wpvivid_plugin;

        $this->replace_prefix=$this->task->get_temp_prefix();
        $this->new_prefix=$this->task->get_db_prefix(true);
        $this->old_prefix=$this->task->get_db_prefix();

        $this->old_site_url= $this->task->get_site_url();
        $this->old_home_url= $this->task->get_home_url();

        $this->new_site_url=$this->task->get_site_url(true);
        $this->new_home_url=$this->task->get_home_url(true);

        if(!$this->init_replace_tables_list())
            return false;

        $table=$this->task->get_start('db_replace');
        $this->task->update_calc_db_size('db_replace');
        while($table&&!$table['finished'])
        {
            $ret=$this->replace_table($table,$this->task->get_db_replace_count());
            if($ret['result']=='failed')
            {
                $this->task->set_error($ret['error']);
                return false;
            }

            if(!$table['finished'])
            {
                $wpvivid_plugin->staging->log->WriteLog($this->task->get_db_replace_count().' queries of '.$table['name'].' is replaced.','notice');
                $wpvivid_plugin->staging->log->WriteLog('The next replacing table info: '.wp_json_encode($table),'notice');
            }

            $this->task->update_table('db_replace',$table);
        }

        $wpvivid_plugin->staging->log->WriteLog('Replacing '.$table['name'].' is completed.','notice');
        $this->task->update_table_finished('db_replace',$table);
        $this->task->update_calc_db_finish_size('db_replace');

        if($this->task->get_start('db_replace')===false)
        {
            if(!$this->task->is_restore())
            {
                $this->set_staging_site_data();
            }
            //$this->set_staging_site_data();
            $wpvivid_plugin->staging->log->WriteLog('Replacing database is completed.','notice');
            $this->task->update_job_finished('db_replace');
        }

        return true;
    }

    public function do_rename_db()
    {
        global $wpvivid_plugin;

        $this->replace_prefix=$this->task->get_temp_prefix();
        $this->new_prefix=$this->task->get_db_prefix(true);
        $this->old_prefix=$this->task->get_db_prefix();

        $this->old_site_url= $this->task->get_site_url();
        $this->old_home_url= $this->task->get_home_url();

        $this->new_site_url=$this->task->get_site_url(true);
        $this->new_home_url=$this->task->get_home_url(true);

        if(!$this->init_rename_tables_list())
            return false;

        $table=$this->task->get_start('db_rename');
        $this->task->update_calc_db_size('db_rename');
        while($table)
        {
            $ret= $this->rename_table($table);
            if($ret['result']=='failed')
            {
                $this->task->set_error($ret['error']);
                return false;
            }

            $wpvivid_plugin->staging->log->WriteLog('Rename '.$table['name'].' is completed.','notice');
            $this->task->update_table_finished('db_rename',$table);
            $table=$this->task->get_start('db_rename');
            $this->task->update_calc_db_finish_size('db_rename');
        }

        $this->set_staging_site_data();
        $wpvivid_plugin->staging->log->WriteLog('Rename tables is completed.','notice');
        $this->task->update_job_finished('db_rename');

        return true;
    }

    public function set_staging_site_data()
    {
        global $wpvivid_plugin;

        $db=$this->get_db_instance(true);

        $prefix=$this->new_prefix;

        $query=$db->prepare("UPDATE {$prefix}options SET option_value = %s WHERE option_name = 'siteurl' or option_name='home'",$this->new_site_url);

        if ($db->get_results($query)===false)
        {
            $error=$db->last_error;
            $wpvivid_plugin->staging->log->WriteLog($error, 'Warning');
        }

        $update_query=$db->prepare("UPDATE {$prefix}options SET option_value = %s WHERE option_name = 'rewrite_rules'", '');
        $wpvivid_plugin->staging->log->WriteLog($update_query, 'notice');
        if ($db->get_results($update_query)===false)
        {
            $error=$db->last_error;
            $wpvivid_plugin->staging->log->WriteLog($error, 'Warning');
        }

        $update_query=$db->prepare("INSERT INTO {$prefix}options (option_name,option_value) VALUES ('wpvivid_staging_finish',%d)", 1);
        $wpvivid_plugin->staging->log->WriteLog($update_query, 'notice');
        if ($db->get_results($update_query)===false)
        {
            $error=$db->last_error;
            $wpvivid_plugin->staging->log->WriteLog($error, 'Warning');
        }

        $is_overwrite_permalink_structure = $this->task->get_is_overwrite_permalink_structure();
        if($is_overwrite_permalink_structure == 0)
        {
            if(!$this->task->is_restore()){
                $update_query = $db->prepare("INSERT INTO {$prefix}options (option_name,option_value) VALUES ('wpvivid_staging_init',%d)", 1);
            }
            else{
                $permalink_structure = $this->task->get_permalink_structure();
                $update_query = $db->prepare("INSERT INTO {$prefix}options (option_name,option_value) VALUES ('wpvivid_staging_init',%s)", $permalink_structure);
            }
            $wpvivid_plugin->staging->log->WriteLog($update_query, 'notice');
            if ($db->get_results($update_query) === false) {
                $error = $db->last_error;
                $wpvivid_plugin->staging->log->WriteLog($error, 'Warning');
            }
        }

        if($this->task->is_restore())
        {
            delete_option('wpvivid_staging_data');
            update_option('blog_public','1');

            $push_staging_history = $this->task->get_push_staging_history();
            update_option('wpvivid_push_staging_history', $push_staging_history,'no');

            if($this->task->get_site_mu_single())
            {
                switch_to_blog( $this->task->get_site_mu_single_site_id());
                delete_option('wpvivid_staging_data');
                delete_option('wpvivid_staging_finish');
                delete_option('wpvivid_staging_init');
                restore_current_blog();
            }
        }
        else
        {
            $data['id']=$this->task->get_id();
            $data['name']=$this->task->get_path(true);
            $data['prefix']= $prefix;
            $admin_url = apply_filters('wpvividstg_get_admin_url', '');
            $admin_url .= 'admin.php?page='.apply_filters('wpvivid_white_label_slug', 'WPvivid');
            $data['parent_admin_url']=$admin_url;
            $data['live_site_url']=home_url();
            $data['live_site_staging_url']=apply_filters('wpvividstg_get_admin_url', '').'admin.php?page='.apply_filters('wpvivid_white_label_plugin_name', 'WPvivid_Staging');
            $data=serialize($data);
            $update_query = $db->prepare("INSERT INTO {$prefix}options (option_name,option_value) VALUES ('wpvivid_staging_data',%s)", $data);
            $wpvivid_plugin->staging->log->WriteLog($update_query, 'notice');
            if ($db->get_results($update_query)===false)
            {
                $error=$db->last_error;
                $wpvivid_plugin->staging->log->WriteLog($error, 'Warning');
            }

            $update_query =$db->prepare("UPDATE {$prefix}options SET option_value = %s WHERE option_name = 'blog_public'", '0');
            $wpvivid_plugin->staging->log->WriteLog($update_query, 'notice');
            if ($db->get_results($update_query)===false)
            {
                $error=$db->last_error;
                $wpvivid_plugin->staging->log->WriteLog($error, 'Warning');
            }

            $delete_query = $db->prepare("DELETE FROM {$prefix}options WHERE option_name = %s", 'wpvivid_backup_list');
            $wpvivid_plugin->staging->log->WriteLog($delete_query, 'notice');
            if ($db->get_results($delete_query)===false)
            {
                $error=$db->last_error;
                $wpvivid_plugin->staging->log->WriteLog($error, 'Warning');
            }
        }

        if(!$this->task->is_restore())
        {
            $permalink = get_option( 'permalink_structure','');

            $update_query = $db->prepare("UPDATE {$prefix}options SET option_value = %s WHERE option_name = 'permalink_structure'", $permalink);
            $wpvivid_plugin->staging->log->WriteLog($update_query, 'notice');
            if ($db->get_results($update_query)===false)
            {
                $error=$db->last_error;
                $wpvivid_plugin->staging->log->WriteLog($error, 'Warning');
            }
        }


        $update_query =$db->prepare("UPDATE {$prefix}options SET option_value = %s WHERE option_name = 'upload_path'", "");
        $wpvivid_plugin->staging->log->WriteLog($update_query, 'notice');
        if ($db->get_results($update_query)===false)
        {
            $error=$db->last_error;
            $wpvivid_plugin->staging->log->WriteLog($error, 'Warning');
        }

        if($this->task->is_mu_single()&&!$this->task->is_restore())
        {
            if($this->task->is_mu_single())
            {
                switch_to_blog($this->task->get_mu_single_site_id());
                $current   = get_option( 'active_plugins', array() );
                restore_current_blog();
            }
            else
            {
                $current   = get_option('active_plugins',array());
            }

            if(!in_array('wpvivid-backuprestore/wpvivid-backuprestore.php',$current))
                $current[] = 'wpvivid-backuprestore/wpvivid-backuprestore.php.php';
            sort( $current );
            $value=serialize($current);
            $update_query = $db->prepare("UPDATE {$prefix}options SET option_value=%s WHERE option_name='active_plugins'" , $value);
            $wpvivid_plugin->staging->log->WriteLog($update_query, 'notice');
            if ($db->get_results($update_query)===false)
            {
                $error=$db->last_error;
                $wpvivid_plugin->staging->log->WriteLog($error, 'Warning');
            }
        }
    }

    public function init_replace_tables_list()
    {
        if(empty($this->task->get_tables('db_replace')))
        {
            global $wpvivid_plugin;
            $wpvivid_plugin->staging->log->WriteLog('Retrieve the tables required to replace.','notice');

            $tables=array();

            $db=$this->get_db_instance(true);

            $copyed_tables=$this->task->get_tables('db');

            foreach ($copyed_tables as $table)
            {
                global $wpdb;
                if($this->task->is_mu_single()&&($table['name']==$wpdb->base_prefix.'users'||$table['name']==$wpdb->base_prefix.'usermeta'))
                {
                    $og_table_name=$this->str_replace_limit($wpdb->base_prefix,'',$table['name'], 1);
                    $new_table_name=$this->replace_prefix.$og_table_name;
                }
                else
                {
                    $og_table_name=$this->str_replace_limit($this->old_prefix,'',$table['name'], 1);
                    $new_table_name=$this->replace_prefix.$og_table_name;
                }
                //$og_table_name=$this->str_replace_limit($this->old_prefix,'',$table['name'], 1);
                //$new_table_name=$this->replace_prefix.$og_table_name;
                if(!$this->task->is_tables_exclude($new_table_name,$this->replace_prefix))
                {
                    $table['name']=$new_table_name;
                    $table['start']=0;
                    $table['finished']=0;
                    $tables[$new_table_name]=$table;
                }
            }

            /*
            $sql=$db->prepare("SHOW TABLES LIKE %s;", $db->esc_like($this->replace_prefix) . '%');

            $result = $db->get_results($sql, OBJECT_K);

            if($result===false)
            {
                $error='Failed to retrieve database tables, error:'.$db->last_error;
                $wpvivid_staging->log->WriteLog($error,'error');
                $this->task->set_error($error);
                return false;
            }
            if(empty($result))
            {
                $error='Tables not found in database.';
                $wpvivid_staging->log->WriteLog($error,'error');
                $this->task->set_error($error);
                return false;
            }

            foreach ($result as $table_name=>$value)
            {
                if(!$this->task->is_tables_exclude($table_name,$this->replace_prefix))
                {
                    $table['name']=$table_name;
                    $table['start']=0;
                    $table['finished']=0;
                    $tables[$table_name]=$table;
                }
            }*/
            //$wpvivid_staging->log->WriteLog(wp_json_encode($tables),'test');
            $this->task->update_tables('db_replace',$tables);
        }
        return true;
    }

    public function init_copy_tables_list()
    {
        if(empty($this->task->get_tables('db')))
        {
            global $wpvivid_plugin;
            $wpvivid_plugin->staging->log->WriteLog('Retrieve the tables required to copy.','notice');

            $tables=array();
            $db=$this->get_db_instance(false);

            $sql=$db->prepare("SHOW TABLES LIKE %s;", $db->esc_like($this->old_prefix) . '%');

            $result = $db->get_results($sql, OBJECT_K);

            if($result===false)
            {
                $error='Failed to retrieve database tables, error:'.$db->last_error;
                $wpvivid_plugin->staging->log->WriteLog($error,'error');
                $this->task->set_error($error);
                return false;
            }
            if(empty($result))
            {
                $error='Tables not found in database.';
                $wpvivid_plugin->staging->log->WriteLog($error,'error');
                $this->task->set_error($error);
                return false;
            }

            foreach ($result as $table_name=>$value)
            {
                if(!$this->task->is_tables_exclude($table_name))
                {
                    $table['name']=$table_name;
                    $table['create']=0;
                    $table['start']=0;
                    $table['finished']=0;
                    $tables[$table_name]=$table;
                }
            }

            if($this->task->is_mu_single())
            {
                global $wpdb;
                $sql=$db->prepare("SHOW TABLES LIKE %s;", $db->esc_like($wpdb->base_prefix) . '%');
                $result = $db->get_results($sql, OBJECT_K);
                foreach ($result as $table_name=>$value)
                {
                    if(!$this->task->is_tables_exclude($table_name))
                    {
                        $table['name']=$table_name;
                        $table['create']=0;
                        $table['start']=0;
                        $table['finished']=0;
                        $tables[$table_name]=$table;
                    }
                }
            }

            global $wpdb;
            $all_tables = (array) $wpdb->get_results( "SHOW FULL TABLES", ARRAY_N );
            if(!empty($all_tables) && !empty($tables)){
                foreach ($tables as $table_name => $table){
                    foreach ($all_tables as $table_arr){
                        if($table_name === $table_arr[0] && $table_arr[1] === 'VIEW'){
                            unset($tables[$table_name]);
                        }
                    }
                }
            }

            $this->task->update_tables('db',$tables);
        }
        return true;
    }

    public function init_rename_tables_list()
    {
        if(empty($this->task->get_tables('db_rename')))
        {
            global $wpvivid_plugin;
            $wpvivid_plugin->staging->log->WriteLog('Retrieve the tables required to rename.','notice');

            $tables=array();

            $db=$this->get_db_instance(true);

            $sql=$db->prepare("SHOW TABLES LIKE %s;", $db->esc_like($this->replace_prefix) . '%');

            $result = $db->get_results($sql, OBJECT_K);

            if($result===false)
            {
                $error='Failed to retrieve database tables, error:'.$db->last_error;
                $wpvivid_plugin->staging->log->WriteLog($error,'error');
                $this->task->set_error($error);
                return false;
            }
            if(empty($result))
            {
                $error='Tables not found in database.';
                $wpvivid_plugin->staging->log->WriteLog($error,'error');
                $this->task->set_error($error);
                return false;
            }

            foreach ($result as $table_name=>$value)
            {
                $table['name']=$table_name;
                $table['start']=0;
                $table['finished']=0;
                $tables[$table_name]=$table;
            }

            $this->task->update_tables('db_rename',$tables);
        }
        return true;
    }

    public function get_db_instance($des=false)
    {
        $db=$this->task->get_db_connect();
        if($des)
        {
            if( $this->db_des_instance===false)
            {
                if($db['des_use_additional_db']===false)
                {
                    global $wpdb;
                    $this->db_des_instance=$wpdb;
                    return $this->db_des_instance;
                }
                else
                {
                    $this->db_des_instance=new wpdb($db['des_dbuser'],$db['des_dbpassword'],$db['des_dbname'],$db['des_dbhost']);
                    return $this->db_des_instance;
                }
            }
            else
            {
                return $this->db_des_instance;
            }
        }
        else
        {
            if( $this->db_src_instance===false)
            {
                if($db['src_use_additional_db']===false)
                {
                    global $wpdb;
                    $this->db_src_instance=$wpdb;
                    return  $this->db_src_instance;
                }
                else
                {
                    $this->db_src_instance=new wpdb($db['src_dbuser'],$db['src_dbpassword'],$db['src_dbname'],$db['src_dbhost']);
                    return $this->db_src_instance;
                }
            }
            else
            {
                return $this->db_src_instance;
            }
        }
    }

    public function is_same_database()
    {
        $db=$this->task->get_db_connect();
        if( $db['des_use_additional_db']||$db['src_use_additional_db'])
        {
            return false;
        }
        else
        {
            return true;
        }

    }

    public function copy_table(&$table,$count)
    {
        $ret['result']='success';
        if($table==false)
        {
            return $ret;
        }

        if($table['create']==0)
        {
            $ret=$this->create_table($table['name']);
            if($ret['result']=='success')
            {
                $table['create']=1;
            }
            else
            {
                return $ret;
            }
        }

        return $this->copy_table_data($table,$count);
    }

    function str_replace_limit($search, $replace, $subject, $limit=-1)
    {

        if (is_array($search)) {
            foreach ($search as $k=>$v) {
                $search[$k] = '`' . preg_quote($search[$k],'`') . '`';
            }
        }
        else {
            $search = '`' . preg_quote($search,'`') . '`';
        }

        return preg_replace($search, $replace, $subject, $limit);
    }

    public function create_table($table_name)
    {
        global $wpvivid_plugin,$wpdb;

        if($this->task->is_mu_single()&&($table_name==$wpdb->base_prefix.'users'||$table_name==$wpdb->base_prefix.'usermeta'))
        {
            $og_table_name=$this->str_replace_limit($wpdb->base_prefix,'',$table_name, 1);
            $new_table_name=$this->new_prefix.$og_table_name;
        }
        else
        {
            $og_table_name=$this->str_replace_limit($this->old_prefix,'',$table_name, 1);
            $new_table_name=$this->new_prefix.$og_table_name;
        }

        $new_db=$this->get_db_instance(true);
        $old_db=$this->get_db_instance();

        $query = $old_db->prepare( 'SHOW TABLES LIKE %s', $new_db->esc_like( $new_table_name ) );

        if ( $new_db->get_var( $query ) == $new_table_name )
        {
            $new_db->query('SET foreign_key_checks = 0');
            $new_db->query("DROP TABLE IF EXISTS {$new_table_name}");
        }

        $result = $old_db->get_results( "SHOW CREATE TABLE `{$table_name}`", ARRAY_A );
        if( isset($result[0]['Create Table']))
        {
            $query=$result[0]['Create Table'];

            $query = str_replace( "CREATE TABLE `{$table_name}`", "CREATE TABLE `{$new_table_name}`", $query );

            $query = preg_replace_callback( "/CONSTRAINT\s`(\w+)`/", function()
            {
                $new="CONSTRAINT `" . uniqid() . "`";
                return $new;
            }, $query );

            $query = preg_replace_callback( "/REFERENCES\s`(\w+)`/", function($matches)
            {
                $new=str_replace($this->old_prefix,$this->new_prefix,$matches[0]);
                return $new;
            }, $query );

            $new_db->query('SET FOREIGN_KEY_CHECKS=0;');

            if( false === $new_db->query( $query ) )
            {
                $error='Failed to create a table. Error:'.$new_db->last_error.', query:'.$query;
                $wpvivid_plugin->staging->log->WriteLog($error,'error');
                $ret['result']='failed';
                $ret['error']=$error;
            }
            else
            {
                $ret['result']='success';
            }
        }
        else
        {
            $error='Failed to retrieve the table structure. Table name: '.$table_name;
            $wpvivid_plugin->staging->log->WriteLog($error,'error');
            $ret['result']='failed';
            $ret['error']=$error;
        }
        return $ret;
    }

    public function copy_table_data(&$table,$count)
    {
        $ret['result']='success';

        $new_db=$this->get_db_instance(true);
        $old_db=$this->get_db_instance();

        global $wpdb;
        if($this->task->is_mu_single()&&($table['name']==$wpdb->base_prefix.'users'||$table['name']==$wpdb->base_prefix.'usermeta'))
        {
            $og_table_name=$this->str_replace_limit($wpdb->base_prefix,'',$table['name'], 1);
            $new_table_name=$this->new_prefix.$og_table_name;
        }
        else
        {
            $og_table_name=$this->str_replace_limit($this->old_prefix,'',$table['name'], 1);
            $new_table_name=$this->new_prefix.$og_table_name;
        }

        $old_table_name=$table['name'];

        $sum =$old_db->get_var("SELECT COUNT(1) FROM `{$old_table_name}`");
        if($sum==0)
        {
            $table['finished']=1;
            return $ret;
        }


        $limit = " LIMIT {$count} OFFSET {$table['start']}";

        $new_db->query('SET FOREIGN_KEY_CHECKS=0;');

        if($this->is_same_database())
        {
            $select =  "SELECT * FROM `{$old_table_name}` {$limit}";
            if($new_db->query( "INSERT INTO `{$new_table_name}` ".$select )===false)
            {
                $error='Failed to insert '.$new_table_name.', error: '.$new_db->last_error;
                global $wpvivid_plugin;
                $wpvivid_plugin->staging->log->WriteLog($error,'warning');

                $start =$new_db->get_var("SELECT COUNT(1) FROM `{$new_table_name}`");
                if($start===false)
                {
                    $ret['result']='failed';
                    $ret['error']=$error;
                    return $ret;
                }
                global $wpvivid_plugin;
                $wpvivid_plugin->staging->log->WriteLog('new start offset '.$start,'warning');
                $limit = " LIMIT {$count} OFFSET {$start}";
                $select =  "SELECT * FROM `{$old_table_name}` {$limit}";
                if($new_db->query( "INSERT INTO `{$new_table_name}` ".$select )===false)
                {
                    $error='Failed to insert '.$new_table_name.', error: '.$new_db->last_error;
                    global $wpvivid_plugin;
                    $wpvivid_plugin->staging->log->WriteLog($error,'error');
                    $ret['result']='failed';
                    $ret['error']=$error;
                    return $ret;
                }
                else
                {
                    $table['start']=$start;
                }
            }
        }
        else
        {
            $rows = $old_db->get_results( "SELECT * FROM `{$old_table_name}` {$limit}", ARRAY_A );

            foreach ( $rows as $row )
            {
                if($new_db->insert($new_table_name,$row)===false)
                {
                    //global $wpvivid_staging;
                    //$error='Failed to insert '.$new_table_name.', error: '.$new_db->last_error;
                }
            }
        }

        $table['start'] += $count;

        if( $table['start'] > $sum )
        {
            $table['finished']=1;
        }

        return $ret;
    }

    public function replace_table(&$table,$count)
    {
        $ret['result']='success';

        global $wpvivid_plugin;
        $this->replacing_table=$table['name'];
        $db=$this->get_db_instance(true);
        if(substr($table['name'], strlen($this->replace_prefix))=='usermeta')
        {
            if($this->old_prefix!=$this->new_prefix)
            {
                $update_query ='UPDATE '.$table['name'].' SET meta_key=REPLACE(meta_key,"'.$this->old_prefix.'","'.$this->new_prefix.'") WHERE meta_key LIKE "'.str_replace('_','\_',$this->old_prefix).'%";';
                $wpvivid_plugin->staging->log->WriteLog($update_query, 'notice');
                $wpvivid_plugin->staging->log->WriteLog('The length of UPDATE statement: '.strlen($update_query), 'notice');
                if ($db->get_results($update_query)===false)
                {
                    $error=$db->last_error;
                    $wpvivid_plugin->staging->log->WriteLog($error, 'Warning');
                }
                $table['finished']=1;

                if($this->task->is_mu_single())
                {
                    global $wpdb;
                    $update_query ='UPDATE '.$table['name'].' SET meta_key=REPLACE(meta_key,"'.$wpdb->base_prefix.'","'.$this->new_prefix.'") WHERE meta_key LIKE "'.str_replace('_','\_',$wpdb->base_prefix).'%";';
                    $wpvivid_plugin->staging->log->WriteLog($update_query, 'notice');
                    $wpvivid_plugin->staging->log->WriteLog('The length of UPDATE statement: '.strlen($update_query), 'notice');
                    if ($db->get_results($update_query)===false)
                    {
                        $error=$db->last_error;
                        $wpvivid_plugin->staging->log->WriteLog($error, 'Warning');
                    }
                }
                return $ret;
            }
        }

        if(is_multisite())
        {
            if(substr($table['name'], strlen($this->replace_prefix))=='blogs')
            {
                $wpvivid_plugin->staging->log->WriteLog('update mu blogs', 'notice');

                if((preg_match('#^https?://([^/]+)#i', $this->new_home_url, $matches) || preg_match('#^https?://([^/]+)#i', $this->new_site_url, $matches)) && (preg_match('#^https?://([^/]+)#i', $this->old_home_url, $old_matches) || preg_match('#^https?://([^/]+)#i', $this->old_site_url, $old_matches)))
                {
                    $new_string = strtolower($matches[1]);
                    $old_string = strtolower($old_matches[1]);

                    $query = 'SELECT * FROM `'.$table['name'].'`';
                    $result=$db->get_results($query,ARRAY_A);
                    if($result && sizeof($result)>0)
                    {
                        $rows = $result;
                        $mu_option=$this->task->get_mu_option();
                        $wpvivid_plugin->staging->log->WriteLog(wp_json_encode($mu_option), 'notice');
                        foreach ($rows as $row)
                        {
                            $update=array();
                            $where=array();

                            $old_domain_data = $row['domain'];
                            $new_domain_data=str_replace($old_string,$new_string,$old_domain_data);

                            $temp_where='`blog_id` = "' . $row['blog_id'] . '"';
                            if (is_callable(array($db, 'remove_placeholder_escape')))
                                $temp_where = $db->remove_placeholder_escape($temp_where);
                            $where[] = $temp_where;
                            $update[] = '`domain` = "' . $new_domain_data . '"';

                            $new_path_data=$mu_option['site'][$row['blog_id']]['path_site'];
                            $update[] = '`path` = "' . $new_path_data . '"';

                            if(!empty($update)&&!empty($where))
                            {
                                $update_query = 'UPDATE `'.$table['name'].'` SET '.implode(', ', $update).' WHERE '.implode(' AND ', array_filter($where)).';';
                                $wpvivid_plugin->staging->log->WriteLog($update_query, 'notice');
                                $db->get_results($update_query);
                            }
                        }
                    }
                }
            }
        }

        $skip_table=false;
        if(apply_filters('wpvivid_restore_db_skip_replace_tables',$skip_table,$table['name']))
        {
            $wpvivid_plugin->staging->log->WriteLog('Ignore table '.$table['name'], 'Warning');
            $table['finished']=1;
            return $ret;
        }

        if(preg_match('/aiowps_audit_log/i', $table['name']) || preg_match('/aiowps_debug_log/i', $table['name']))
        {
            $wpvivid_plugin->staging->log->WriteLog('Skip replace table '.$table['name'], 'Warning');
            $table['finished']=1;
            return $ret;
        }

        $sum =$db->get_var("SELECT COUNT(1) FROM `{$table['name']}`");

        if($sum>0)
        {
            $query='DESCRIBE `'.$table['name'].'`';
            $result=$db->get_results($query,ARRAY_A);
            if($result===false)
            {
                $error=$db->last_error;
                $wpvivid_plugin->staging->log->WriteLog($error, 'Warning');
                $table['finished']=1;
                return $ret;
            }
            $columns=array();
            foreach ($result as $data)
            {
                $column['Field']=$data['Field'];
                if($data['Key']=='PRI')
                    $column['PRI']=1;
                else
                    $column['PRI']=0;

                if($data['Type']=='mediumblob')
                {
                    $column['skip']=1;
                }
                $columns[]=$column;
            }
            $update_query='';

            $start_row=$table['start'];
            $wpvivid_plugin->staging->log->WriteLog('Start replacing the table prefix from '.$start_row. ' row.', 'notice');
            $end_row=$count;
            $query = 'SELECT * FROM `'.$table['name'].'` LIMIT '.$start_row.', '.$end_row;
            $result=$db->get_results($query,ARRAY_A);
            if($result && sizeof($result)>0)
            {
                $rows = $result;
                foreach ($rows as $row)
                {
                    if( isset( $row['option_value'] ) && strlen( $row['option_value'] ) >= 5000000 )
                    {
                        continue;
                    }
                    $update=array();
                    $where=array();
                    foreach ($columns as $column)
                    {
                        if(isset($column['skip']))
                        {
                            //$wpvivid_plugin->staging->log->WriteLog('Skip MEDIUMBLOB data.', 'notice');
                            continue;
                        }
                        if($column['Field']=='option_name'&&$row[$column['Field']]=='mainwp_child_subpages')
                        {
                            break;
                        }
                        $old_data = $row[$column['Field']];
                        $size = strlen( $old_data );
                        if( $size >= 5000000 )
                        {
                            continue;
                        }
                        if($column['PRI']==1)
                        {
                            $db->escape_by_ref($old_data);
                            $temp_where='`'.$column['Field'].'` = "' . $old_data . '"';
                            if (is_callable(array($db, 'remove_placeholder_escape')))
                                $temp_where = $db->remove_placeholder_escape($temp_where);
                            $where[] = $temp_where;
                        }
                        $skip_row=false;
                        if(apply_filters('wpvivid_restore_db_skip_replace_rows',$skip_row,$table['name'],$column['Field']))
                        {
                            continue;
                        }
                        $new_data=$this->replace_row_data($old_data);
                        if($new_data==$old_data)
                            continue;
                        $db->escape_by_ref($new_data);
                        if (is_callable(array($db, 'remove_placeholder_escape')))
                            $new_data = $db->remove_placeholder_escape($new_data);
                        $update[] = '`'.$column['Field'].'` = "' . $new_data . '"';
                    }
                    if(!empty($update)&&!empty($where))
                    {
                        $temp_query = 'UPDATE `'.$table['name'].'` SET '.implode(', ', $update).' WHERE '.implode(' AND ', array_filter($where)).';';
                        $update_query=$temp_query;

                        if ($db->get_results($update_query)===false)
                        {
                            $error=$db->last_error;
                            $wpvivid_plugin->staging->log->WriteLog($error, 'Warning');
                        }
                        $update_query='';
                    }
                }
            }
            if(!empty($update_query))
            {
                $wpvivid_plugin->staging->log->WriteLog($update_query, 'notice');
                if ($db->get_results($update_query)===false)
                {
                    $error=$db->last_error;
                    $wpvivid_plugin->staging->log->WriteLog($error, 'Warning');
                }
            }
        }
        $wpvivid_plugin->staging->log->WriteLog('Replacing database tables is completed.', 'notice');
        $table['start'] += $count;
        if( $table['start'] > $sum )
        {
            $table['finished']=1;
        }
        if($table['finished'])
        {
            if(substr($table['name'], strlen($this->replace_prefix))=='options')
            {
                $update_query ='UPDATE '.$table['name'].' SET option_name="'.$this->new_prefix.'user_roles" WHERE option_name="'.$this->old_prefix.'user_roles";';
                $wpvivid_plugin->staging->log->WriteLog($update_query, 'notice');
                $wpvivid_plugin->staging->log->WriteLog('The length of UPDATE statement: '.strlen($update_query), 'notice');
                if ($db->get_results($update_query)===false)
                {
                    $error=$db->last_error;
                    $wpvivid_plugin->staging->log->WriteLog($error, 'Warning');
                }
            }
        }
        return $ret;
    }

    public function replace_row_data($old_data)
    {
        $unserialize_data = @unserialize($old_data, array('allowed_classes' => false));
        if($unserialize_data===false)
        {
            $old_data=$this->replace_string_v2($old_data);
        }
        else
        {
            $old_data=$this->replace_serialize_data($unserialize_data);
            $old_data=serialize($old_data);
        }

        return $old_data;
    }

    private function replace_serialize_data($data,$serialized = false)
    {
        if(is_serialized( $data ) && ( $serialize_data = @unserialize( $data, array('allowed_classes' => false) ) ) !== false)
        {
            $data=$this->replace_serialize_data($serialize_data,true);
        }
        else if(is_array($data))
        {
            foreach ($data as $key => $value)
            {
                $data[$key]=$this->replace_serialize_data($value);
            }
        }
        else if(is_object($data))
        {
            $temp = $data; // new $data_class();
            if (is_a($data, '__PHP_Incomplete_Class'))
            {

            }
            else
            {
                $props = get_object_vars($data);
                foreach ($props as $key => $value)
                {
                    if (strpos($key, "\0")===0)
                        continue;
                    $temp->$key = $this->replace_serialize_data($value);
                }
            }
            $data = $temp;
            unset($temp);
        }
        else if(is_string($data))
        {
            $data=$this->replace_string_v2($data);
        }
        if($serialized)
            $data=serialize($data);
        return $data;
    }

    public function get_mix_link($url)
    {
        if (0 === stripos($url, 'https://'))
        {
            $mix_link = 'http://'.substr($url, 8);
        } elseif (0 === stripos($url, 'http://')) {
            $mix_link = 'https://'.substr($url, 7);
        }
        else
        {
            $mix_link=false;
        }
        return $mix_link;
    }

    public function get_remove_http_link($url)
    {
        if (0 === stripos($url, 'https://'))
        {
            $mix_link = '//'.substr($url, 8);
        } elseif (0 === stripos($url, 'http://')) {
            $mix_link = '//'.substr($url, 7);
        }
        else
        {
            $mix_link=false;
        }
        return $mix_link;
    }

    public function get_remove_http_link_ex($url)
    {
        if (0 === stripos($url, 'https://'))
        {
            $mix_link = '\/\/'.substr($url, 8);
        } elseif (0 === stripos($url, 'http://')) {
            $mix_link = '\/\/'.substr($url, 7);
        }
        else
        {
            $mix_link=false;
        }
        return $mix_link;
    }

    public function replace_string_v2($old_string)
    {
        if(!is_string($old_string))
        {
            return $old_string;
        }

        $from=array();
        $to=array();

        $new_url_use_https=false;
        if (0 === stripos($this->new_site_url, 'https://')|| stripos($this->new_site_url, 'https:\/\/'))
        {
            $new_url_use_https=true;
        }
        else if (0 === stripos($this->new_site_url, 'http://')|| stripos($this->new_site_url, 'http:\/\/'))
        {
            $new_url_use_https=false;
        }

        $prefix=$this->replace_prefix;

        if($this->old_site_url!=$this->new_site_url)
        {
            if(substr($this->replacing_table, strlen($prefix))=='posts'||substr($this->replacing_table, strlen($prefix))=='postmeta'||substr($this->replacing_table, strlen($prefix))=='options')
            {
                $remove_http_link=$this->get_remove_http_link($this->old_site_url);
                if($remove_http_link!==false)
                {
                    $new_remove_http_link=$this->get_remove_http_link($this->new_site_url);
                    $from[]=$remove_http_link;
                    $to[]=$new_remove_http_link;

                    if($new_url_use_https)
                    {
                        $from[]='http:'.$new_remove_http_link;
                        $to[]='https:'.$new_remove_http_link;
                    }
                    else
                    {
                        $from[]='https:'.$new_remove_http_link;
                        $to[]='http:'.$new_remove_http_link;
                    }

                    $quote_old_site_url=$this->get_http_link_at_quote($remove_http_link);
                    $quote_new_site_url=$this->get_http_link_at_quote($new_remove_http_link);
                    $from[]=$quote_old_site_url;
                    $to[]=$quote_new_site_url;
                    if($new_url_use_https)
                    {
                        $from[]='http:'.$quote_new_site_url;
                        $to[]='https:'.$quote_new_site_url;
                    }
                    else
                    {
                        $from[]='https:'.$quote_new_site_url;
                        $to[]='http:'.$quote_new_site_url;
                    }
                }
                else
                {
                    $remove_http_link=$this->get_remove_http_link_ex($this->old_site_url);
                    if($remove_http_link!==false)
                    {
                        $new_remove_http_link=$this->get_remove_http_link_ex($this->new_site_url);
                        $from[]=$remove_http_link;
                        $to[]=$new_remove_http_link;

                        if($new_url_use_https)
                        {
                            $from[]='http:'.$new_remove_http_link;
                            $to[]='https:'.$new_remove_http_link;
                        }
                        else
                        {
                            $from[]='https:'.$new_remove_http_link;
                            $to[]='http:'.$new_remove_http_link;
                        }
                    }
                }
            }
            else
            {
                $from[]=$this->old_site_url;
                $to[]=$this->new_site_url;
            }
        }


        if($this->old_home_url!=$this->old_site_url&&$this->old_home_url!=$this->new_home_url)
        {
            if(substr($this->replacing_table, strlen($prefix))=='posts'||substr($this->replacing_table, strlen($prefix))=='postmeta'||substr($this->replacing_table, strlen($prefix))=='options')
            {
                $remove_http_link=$this->get_remove_http_link($this->old_home_url);
                if($remove_http_link!==false)
                {
                    $new_remove_http_link=$this->get_remove_http_link($this->new_home_url);
                    $from[]=$remove_http_link;
                    $to[]=$new_remove_http_link;

                    if($new_url_use_https)
                    {
                        $from[]='http:'.$new_remove_http_link;
                        $to[]='https:'.$new_remove_http_link;
                    }
                    else
                    {
                        $from[]='https:'.$new_remove_http_link;
                        $to[]='http:'.$new_remove_http_link;
                    }

                    $quote_old_site_url=$this->get_http_link_at_quote($remove_http_link);
                    $quote_new_site_url=$this->get_http_link_at_quote($new_remove_http_link);
                    $from[]=$quote_old_site_url;
                    $to[]=$quote_new_site_url;
                    if($new_url_use_https)
                    {
                        $from[]='http:'.$quote_new_site_url;
                        $to[]='https:'.$quote_new_site_url;
                    }
                    else
                    {
                        $from[]='https:'.$quote_new_site_url;
                        $to[]='http:'.$quote_new_site_url;
                    }
                }
                else
                {
                    $remove_http_link=$this->get_remove_http_link_ex($this->old_home_url);
                    if($remove_http_link!==false)
                    {
                        $new_remove_http_link=$this->get_remove_http_link_ex($this->new_home_url);
                        $from[]=$remove_http_link;
                        $to[]=$new_remove_http_link;

                        if($new_url_use_https)
                        {
                            $from[]='http:'.$new_remove_http_link;
                            $to[]='https:'.$new_remove_http_link;
                        }
                        else
                        {
                            $from[]='https:'.$new_remove_http_link;
                            $to[]='http:'.$new_remove_http_link;
                        }
                    }
                }
            }
            else
            {
                $from[]=$this->old_home_url;
                $to[]=$this->new_home_url;
            }
        }


        if(!empty($from)&&!empty($to))
        {
            $old_string=str_replace($from,$to,$old_string);
        }

        return $old_string;
    }

    public function replace_string($old_string)
    {
        if(!is_string($old_string))
        {
            return $old_string;
        }

        $from=array();
        $to=array();

        if($this->old_site_url!=$this->new_site_url)
        {
            $remove_http_link=$this->get_remove_http_link($this->old_site_url);
            $new_remove_http_link=$this->get_remove_http_link($this->new_site_url);
            if(strpos($new_remove_http_link,$remove_http_link)!==false)
            {
                return $this->replace_string_ex($old_string);
            }
        }

        if($this->old_site_url!=$this->new_site_url)
        {
            $from[]=$this->old_site_url;
            $to[]=$this->new_site_url;
            $old_mix_link=$this->get_mix_link($this->old_site_url);
            if($old_mix_link!==false)
            {
                $from[]=$old_mix_link;
                $to[]=$this->new_site_url;
            }
            if(substr($this->replacing_table, strlen($this->new_prefix))=='posts'||substr($this->replacing_table, strlen($this->new_prefix))=='postmeta'||substr($this->replacing_table, strlen($this->new_prefix))=='options')
            {
                $remove_http_link=$this->get_remove_http_link($this->old_site_url);
                if($remove_http_link!==false)
                {
                    $new_remove_http_link=$this->get_remove_http_link($this->new_site_url);
                    $from[]=$remove_http_link;
                    $to[]=$new_remove_http_link;
                    $quote_old_site_url=$this->get_http_link_at_quote($remove_http_link);
                    $quote_new_site_url=$this->get_http_link_at_quote($new_remove_http_link);
                    $from[]=$quote_old_site_url;
                    $to[]=$quote_new_site_url;
                }

                $remove_http_link=$this->get_remove_http_link_ex($this->old_site_url);
                if($remove_http_link!==false)
                {
                    $new_remove_http_link=$this->get_remove_http_link_ex($this->new_site_url);
                    $from[]=$remove_http_link;
                    $to[]=$new_remove_http_link;
                    $quote_old_site_url=$this->get_http_link_at_quote($remove_http_link);
                    $quote_new_site_url=$this->get_http_link_at_quote($new_remove_http_link);
                    $from[]=$quote_old_site_url;
                    $to[]=$quote_new_site_url;
                }
            }
            $quote_old_site_url=$this->get_http_link_at_quote($this->old_site_url);
            $quote_new_site_url=$this->get_http_link_at_quote($this->new_site_url);
            $from[]=$quote_old_site_url;
            $to[]=$quote_new_site_url;
        }

        if($this->old_home_url!=$this->new_home_url)
        {
            $from[]=$this->old_home_url;
            $to[]=$this->new_home_url;

            $old_mix_link=$this->get_mix_link($this->old_home_url);
            if($old_mix_link!==false)
            {
                $from[]=$old_mix_link;
                $to[]=$this->new_home_url;
            }
        }

        if(!empty($from)&&!empty($to))
        {
            $old_string=str_replace($from,$to,$old_string);
        }


        return $old_string;
    }

    public function replace_string_ex($old_string)
    {
        if(!is_string($old_string))
        {
            return $old_string;
        }

        $from=array();
        $place_holder=array();
        $to=array();

        if($this->old_site_url!=$this->new_site_url)
        {
            $remove_http_link=$this->get_remove_http_link($this->old_site_url);
            if($remove_http_link!==false)
            {
                $new_remove_http_link=$this->get_remove_http_link($this->new_site_url);
                $from[]=$remove_http_link;
                //$place_holder[]=$this->get_place_holder(0);
                $to[]=$new_remove_http_link;

                $quote_old_site_url=$this->get_http_link_at_quote($remove_http_link);
                $quote_new_site_url=$this->get_http_link_at_quote($new_remove_http_link);
                $from[]=$quote_old_site_url;
                //$place_holder[]=$this->get_place_holder(3);
                $to[]=$quote_new_site_url;
            }

            $old_mix_link=$this->get_mix_link($this->old_site_url);
            if($old_mix_link!==false)
            {
                $from[]=$old_mix_link;
                //$place_holder[]=$this->get_place_holder(1);
                $to[]=$this->new_site_url;
            }
            if(substr($this->replacing_table, strlen($this->new_prefix))=='posts'||substr($this->replacing_table, strlen($this->new_prefix))=='postmeta'||substr($this->replacing_table, strlen($this->new_prefix))=='options')
            {
                $remove_http_link=$this->get_remove_http_link_ex($this->old_site_url);
                if($remove_http_link!==false)
                {
                    $new_remove_http_link=$this->get_remove_http_link_ex($this->new_site_url);
                    $from[]=$remove_http_link;
                    //$place_holder[]=$this->get_place_holder(2);
                    $to[]=$new_remove_http_link;

                    $quote_old_site_url=$this->get_http_link_at_quote($remove_http_link);
                    $quote_new_site_url=$this->get_http_link_at_quote($new_remove_http_link);
                    $from[]=$quote_old_site_url;
                    //$place_holder[]=$this->get_place_holder(3);
                    $to[]=$quote_new_site_url;
                }
            }
            $quote_old_site_url=$this->get_http_link_at_quote($this->old_site_url);
            $quote_new_site_url=$this->get_http_link_at_quote($this->new_site_url);
            $from[]=$quote_old_site_url;
            //$place_holder[]=$this->get_place_holder(3);
            $to[]=$quote_new_site_url;
        }

        if($this->old_home_url!=$this->new_home_url&&$this->old_home_url!=$this->old_site_url)
        {
            $from[]=$this->old_home_url;
            //$place_holder[]=$this->get_place_holder(4);
            $to[]=$this->new_home_url;

            $old_mix_link=$this->get_mix_link($this->old_home_url);
            if($old_mix_link!==false)
            {
                $from[]=$old_mix_link;
                //$place_holder[]=$this->get_place_holder(5);
                $to[]=$this->new_home_url;
            }
        }

        if(!empty($from)&&!empty($to))
        {
            //$old_string=str_replace($from,$place_holder,$old_string);
            $old_string=str_replace($from,$to,$old_string);
        }


        return $old_string;
    }

    public function get_place_holder($index)
    {
        if ( empty($this->placeholder) )
        {
            for($i=0;$i<6;$i++)
            {
                // If ext/hash is not present, compat.php's hash_hmac() does not support sha256.
                $algo = function_exists( 'hash' ) ? 'sha256' : 'sha1';
                // Old WP installs may not have AUTH_SALT defined.
                $salt = defined( 'AUTH_SALT' ) && AUTH_SALT ? AUTH_SALT : (string) wp_rand();

                $this->placeholder[$i] = '{' . hash_hmac( $algo, uniqid( $salt, true ), $salt ) . '}';
            }
        }

        return $this->placeholder[$index];
    }

    public function get_http_link_at_quote($url)
    {
        return str_replace('/','\/',$url);
    }

    private function rename_table(&$table)
    {
        global $wpvivid_plugin;
        $table_name=$table['name'];
        $tasks=array();
        $og_table=$this->new_prefix.substr($table_name, strlen($this->replace_prefix));
        $db=$this->get_db_instance(true);
        $db->query('SET FOREIGN_KEY_CHECKS=0;');

        if(substr($table_name, strlen($this->replace_prefix))=='options')
        {
            $tasks = get_option('wpvivid_staging_task_list', array());
        }

        $wpvivid_plugin->staging->log->WriteLog('DROP TABLE '.$og_table,'notice');
        if($db->query("DROP TABLE IF EXISTS {$og_table}")===false)
        {
            $wpvivid_plugin->staging->log->WriteLog('Failed to drop table '.$og_table.', error:'.$db->last_error,'notice');
        }
        $wpvivid_plugin->staging->log->WriteLog('Rename a table named '.$table_name.' to '.$og_table.' ','notice');

        if ($db->query("RENAME TABLE {$table_name} TO {$og_table}")===false)
        {
            $wpvivid_plugin->staging->log->WriteLog('Failed to rename a table named '.$table_name.' to '.$og_table.', error: '.$db->last_error,'notice');
        }

        if(substr($table_name, strlen($this->replace_prefix))=='options')
        {
            wp_cache_delete ( 'alloptions', 'options' );
            update_option('wpvivid_staging_task_list',$tasks,'no');
        }

        $table['finished']=1;

        $ret['result']='success';
        return $ret;
    }
}includes/staging/class-wpvivid-staging-setting.php000064400000075742151327705670016453 0ustar00<?php

if (!defined('WPVIVID_PLUGIN_DIR'))
{
    die;
}

class WPvivid_Staging_Setting_Free
{
    public $main_tab;

    public function __construct()
    {
        add_action('wp_ajax_wpvividstg_save_setting', array($this, 'save_setting'));
        add_filter('wpvivid_add_setting_tab_page', array($this, 'add_setting_tab_page'), 10);
        add_action('wpvivid_setting_add_staging_cell',array($this, 'output_staging_setting_cell'),10);
        add_filter('wpvivid_set_general_setting', array($this, 'set_general_setting'), 12, 3);
    }

    public function add_setting_tab_page($setting_array)
    {
        $setting_array['staging_setting'] = array('index' => '3', 'tab_func' =>  array($this, 'add_tab_staging'), 'page_func' => array($this, 'add_page_staging'));
        return $setting_array;
    }

    public function add_tab_staging()
    {
        ?>
        <a href="#" id="wpvivid_tab_staging_setting" class="nav-tab setting-nav-tab" onclick="switchsettingTabs(event,'page-staging-setting')"><?php esc_html_e('Staging Settings', 'wpvivid-backuprestore'); ?></a>
        <?php
    }

    public function add_page_staging()
    {
        ?>
        <div class="setting-tab-content wpvivid_tab_staging_setting" id="page-staging-setting" style="margin-top: 10px; display: none;">
            <?php do_action('wpvivid_setting_add_staging_cell'); ?>
        </div>
        <?php
    }

    public function output_staging_setting_cell()
    {
        $options=get_option('wpvivid_staging_options',array());

        $staging_db_insert_count   = isset($options['staging_db_insert_count']) ? $options['staging_db_insert_count'] : 10000;
        $staging_db_replace_count  = isset($options['staging_db_replace_count']) ? $options['staging_db_replace_count'] : 5000;
        $staging_file_copy_count   = isset($options['staging_file_copy_count']) ? $options['staging_file_copy_count'] : 500;
        $staging_exclude_file_size = isset($options['staging_exclude_file_size']) ? $options['staging_exclude_file_size'] : 30;
        $staging_memory_limit      = isset($options['staging_memory_limit']) ? $options['staging_memory_limit'] : '256M';
        $staging_memory_limit      = str_replace('M', '', $staging_memory_limit);
        $staging_max_execution_time= isset($options['staging_max_execution_time']) ? $options['staging_max_execution_time'] : 900;
        $staging_resume_count      = isset($options['staging_resume_count']) ? $options['staging_resume_count'] : 6;
        $staging_request_timeout      = isset($options['staging_request_timeout']) ? $options['staging_request_timeout'] : 1500;

        $staging_keep_setting      = isset($options['staging_keep_setting']) ? $options['staging_keep_setting'] : true;


        $staging_not_need_login=isset($options['not_need_login']) ? $options['not_need_login'] : true;
        if($staging_not_need_login)
        {
            $staging_not_need_login_check='checked';
        }
        else
        {
            $staging_not_need_login_check='';
        }
        $staging_overwrite_permalink = isset($options['staging_overwrite_permalink']) ? $options['staging_overwrite_permalink'] : true;
        if($staging_overwrite_permalink){
            $staging_overwrite_permalink_check = 'checked';
        }
        else{
            $staging_overwrite_permalink_check = '';
        }

        if($staging_keep_setting)
        {
            $staging_keep_setting='checked';
        }
        else
        {
            $staging_keep_setting='';
        }
        ?>
        <div style="margin-top: 10px;">
            <div class="postbox schedule-tab-block wpvivid-setting-addon" style="margin-bottom: 10px; padding-bottom: 0;">
                <div class="wpvivid-element-space-bottom"><strong><?php esc_html_e('DB Copy Count', 'wpvivid-backuprestore'); ?></strong></div>
                <div class="wpvivid-element-space-bottom">
                    <input type="text" class="all-options" option="setting" name="staging_db_insert_count" value="<?php echo esc_attr($staging_db_insert_count); ?>"
                           placeholder="10000" onkeyup="value=value.replace(/\D/g,'')" />
                </div>
                <div class="wpvivid-element-space-bottom">
                    <?php esc_html_e( 'Number of DB rows, that are copied within one ajax query. The higher value makes the database copy process faster. 
                Please try a high value to find out the highest possible value. If you encounter timeout errors, try lower values until no 
                more errors occur.', 'wpvivid-backuprestore' ); ?>
                </div>

                <div class="wpvivid-element-space-bottom"><strong><?php esc_html_e('DB Replace Count', 'wpvivid-backuprestore'); ?></strong></div>
                <div class="wpvivid-element-space-bottom">
                    <input type="text" class="all-options" option="setting" name="staging_db_replace_count" value="<?php echo esc_attr($staging_db_replace_count); ?>"
                           placeholder="5000" onkeyup="value=value.replace(/\D/g,'')" />
                </div>
                <div class="wpvivid-element-space-bottom">
                    <?php esc_html_e( 'Number of DB rows, that are processed within one ajax query. The higher value makes the DB replacement process faster. 
                If timeout erros occur, decrease the value because this process consumes a lot of memory.', 'wpvivid-backuprestore' ); ?>
                </div>

                <div class="wpvivid-element-space-bottom"><strong><?php esc_html_e('File Copy Count', 'wpvivid-backuprestore'); ?></strong></div>
                <div class="wpvivid-element-space-bottom">
                    <input type="text" class="all-options" option="setting" name="staging_file_copy_count" value="<?php echo esc_attr($staging_file_copy_count); ?>"
                           placeholder="500" onkeyup="value=value.replace(/\D/g,'')" />
                </div>
                <div class="wpvivid-element-space-bottom">
                    <?php esc_html_e( 'Number of files to copy that will be copied within one ajax request. The higher value makes the file copy process faster. 
                Please try a high value to find out the highest possible value. If you encounter timeout errors, try lower values until no more errors occur.', 'wpvivid-backuprestore' ); ?>
                </div>

                <div class="wpvivid-element-space-bottom"><strong><?php esc_html_e('Max File Size', 'wpvivid-backuprestore'); ?></strong></div>
                <div class="wpvivid-element-space-bottom">
                    <input type="text" class="all-options" option="setting" name="staging_exclude_file_size" value="<?php echo esc_attr($staging_exclude_file_size); ?>"
                           placeholder="30" onkeyup="value=value.replace(/\D/g,'')" />MB
                </div>
                <div class="wpvivid-element-space-bottom">
                    <?php esc_html_e( 'Maximum size of the files copied to a staging site. All files larger than this value will be ignored. If you set the value of 0 MB, all files will be copied to a staging site.', 'wpvivid-backuprestore' ); ?>
                </div>

                <div class="wpvivid-element-space-bottom"><strong><?php esc_html_e('Staging Memory Limit', 'wpvivid-backuprestore'); ?></strong></div>
                <div class="wpvivid-element-space-bottom">
                    <input type="text" class="all-options" option="setting" name="staging_memory_limit" value="<?php echo esc_attr($staging_memory_limit); ?>"
                           placeholder="256" onkeyup="value=value.replace(/\D/g,'')" />MB
                </div>
                <div class="wpvivid-element-space-bottom">
                    <?php esc_html_e('Adjust this value to apply for a temporary PHP memory limit for the plugin to create a staging site. 
                We set this value to 256M by default. Increase the value if you encounter a memory exhausted error. Note: some 
                web hosting providers may not support this.', 'wpvivid-backuprestore'); ?>
                </div>

                <div class="wpvivid-element-space-bottom"><strong><?php esc_html_e('PHP Script Execution Timeout', 'wpvivid-backuprestore'); ?></strong></div>
                <div class="wpvivid-element-space-bottom">
                    <input type="text" class="all-options" option="setting" name="staging_max_execution_time" value="<?php echo esc_attr($staging_max_execution_time); ?>"
                           placeholder="900" onkeyup="value=value.replace(/\D/g,'')" />
                </div>
                <div class="wpvivid-element-space-bottom">
                    <?php esc_html_e( 'The time-out is not your server PHP time-out. With the execution time exhausted, our plugin will shut down the progress of 
                creating a staging site. If the progress  encounters a time-out, that means you have a medium or large sized website. Please try to 
                scale the value bigger.', 'wpvivid-backuprestore' ); ?>
                </div>

                <div class="wpvivid-element-space-bottom"><strong><?php esc_html_e('Delay Between Requests', 'wpvivid-backuprestore'); ?></strong></div>
                <div class="wpvivid-element-space-bottom">
                    <input type="text" class="all-options" option="setting" name="staging_request_timeout" value="<?php echo esc_attr($staging_request_timeout); ?>"
                           placeholder="1500" onkeyup="value=value.replace(/\D/g,'')" />ms
                </div>
                <div class="wpvivid-element-space-bottom">
                    <?php esc_html_e( 'A lower value will help speed up the process of creating a staging site. However, if your server has a limit on the number of requests, a higher value is recommended.', 'wpvivid-backuprestore' ); ?>
                </div>

                <div class="wpvivid-element-space-bottom">
                    <strong><?php esc_html_e('Retrying', 'wpvivid-backuprestore'); ?> </strong>
                    <select option="setting" name="staging_resume_count">
                        <?php
                        for($resume_count=3; $resume_count<10; $resume_count++){
                            if($resume_count === $staging_resume_count){
                                echo '<option selected="selected" value="'.esc_attr($resume_count).'">'.esc_html($resume_count).'</option>';
                            }
                            else{
                                echo '<option value="'.esc_attr($resume_count).'">'.esc_html($resume_count).'</option>';
                            }
                        }
                        ?>
                    </select><strong><?php esc_html_e(' times when encountering a time-out error', 'wpvivid-backuprestore'); ?></strong>
                </div>

                <div class="wpvivid-element-space-bottom">
                    <label>
                        <input type="checkbox" option="setting" name="not_need_login" <?php echo esc_attr($staging_not_need_login_check); ?> />
                        <span><strong><?php esc_html_e('Anyone can visit the staging site', 'wpvivid-backuprestore'); ?></strong></span>
                    </label>
                </div>

                <div class="wpvivid-element-space-bottom">
                    <span><?php esc_html_e('When the option is checked, anyone will be able to visit the staging site without the need to login. Uncheck it to request a login to visit the staging site.', 'wpvivid-backuprestore'); ?></span>
                </div>

                <div class="wpvivid-element-space-bottom">
                    <label>
                        <input type="checkbox" option="setting" name="staging_overwrite_permalink" <?php echo esc_attr($staging_overwrite_permalink_check); ?> />
                        <span><strong><?php esc_html_e('Keep permalink when transferring website', 'wpvivid-backuprestore'); ?></strong></span>
                    </label>
                </div>

                <div class="wpvivid-element-space-bottom">
                    <span><?php esc_html_e('When checked, this option allows you to keep the current permalink structure when you create a staging site or push a staging site to live.', 'wpvivid-backuprestore'); ?></span>
                </div>

                <div class="wpvivid-element-space-bottom">
                    <label>
                        <input type="checkbox" option="setting" name="staging_keep_setting" <?php echo esc_attr($staging_keep_setting); ?> />
                        <span><strong><?php esc_html_e('Keep staging sites when deleting the plugin', 'wpvivid-backuprestore'); ?></strong></span>
                    </label>
                </div>

                <div class="wpvivid-element-space-bottom">
                    <span><?php esc_html_e('With this option checked, all staging sites you have created will be retained when the plugin is deleted, just in case you still need them later. The sites will show up again after the plugin is reinstalled.', 'wpvivid-backuprestore'); ?></span>
                </div>
            </div>
        </div>
        <?php
    }

    public function output_staging_setting()
    {
        ?>
        <div style="margin-top: 10px;">
            <?php
            $this->wpvivid_setting_add_staging_cell_addon();
            ?>
        </div>
        <?php
    }

    public function wpvivid_setting_add_staging_cell_addon()
    {
        $options=get_option('wpvivid_staging_options',array());

        $staging_db_insert_count   = isset($options['staging_db_insert_count']) ? $options['staging_db_insert_count'] : 10000;
        $staging_db_replace_count  = isset($options['staging_db_replace_count']) ? $options['staging_db_replace_count'] : 5000;
        $staging_file_copy_count   = isset($options['staging_file_copy_count']) ? $options['staging_file_copy_count'] : 500;
        $staging_exclude_file_size = isset($options['staging_exclude_file_size']) ? $options['staging_exclude_file_size'] : 30;
        $staging_memory_limit      = isset($options['staging_memory_limit']) ? $options['staging_memory_limit'] : '256M';
        $staging_memory_limit      = str_replace('M', '', $staging_memory_limit);
        $staging_max_execution_time= isset($options['staging_max_execution_time']) ? $options['staging_max_execution_time'] : 900;
        $staging_resume_count      = isset($options['staging_resume_count']) ? $options['staging_resume_count'] : 6;
        $staging_request_timeout      = isset($options['staging_request_timeout']) ? $options['staging_request_timeout'] : 1500;

        $staging_keep_setting      = isset($options['staging_keep_setting']) ? $options['staging_keep_setting'] : true;


        $staging_not_need_login=isset($options['not_need_login']) ? $options['not_need_login'] : true;
        if($staging_not_need_login)
        {
            $staging_not_need_login_check='checked';
        }
        else
        {
            $staging_not_need_login_check='';
        }
        $staging_overwrite_permalink = isset($options['staging_overwrite_permalink']) ? $options['staging_overwrite_permalink'] : true;
        if($staging_overwrite_permalink){
            $staging_overwrite_permalink_check = 'checked';
        }
        else{
            $staging_overwrite_permalink_check = '';
        }

        if($staging_keep_setting)
        {
            $staging_keep_setting='checked';
        }
        else
        {
            $staging_keep_setting='';
        }
        ?>
        <div class="postbox schedule-tab-block wpvivid-setting-addon" style="margin-bottom: 10px; padding-bottom: 0;">
            <div class="wpvivid-element-space-bottom"><strong><?php esc_html_e('DB Copy Count', 'wpvivid-backuprestore'); ?></strong></div>
            <div class="wpvivid-element-space-bottom">
                <input type="text" class="all-options" option="setting" name="staging_db_insert_count" value="<?php echo esc_attr($staging_db_insert_count); ?>"
                       placeholder="10000" onkeyup="value=value.replace(/\D/g,'')" />
            </div>
            <div class="wpvivid-element-space-bottom">
                <?php esc_html_e( 'Number of DB rows, that are copied within one ajax query. The higher value makes the database copy process faster. 
                Please try a high value to find out the highest possible value. If you encounter timeout errors, try lower values until no 
                more errors occur.', 'wpvivid-backuprestore' ); ?>
            </div>

            <div class="wpvivid-element-space-bottom"><strong><?php esc_html_e('DB Replace Count', 'wpvivid-backuprestore'); ?></strong></div>
            <div class="wpvivid-element-space-bottom">
                <input type="text" class="all-options" option="setting" name="staging_db_replace_count" value="<?php echo esc_attr($staging_db_replace_count); ?>"
                       placeholder="5000" onkeyup="value=value.replace(/\D/g,'')" />
            </div>
            <div class="wpvivid-element-space-bottom">
                <?php esc_html_e( 'Number of DB rows, that are processed within one ajax query. The higher value makes the DB replacement process faster. 
                If timeout erros occur, decrease the value because this process consumes a lot of memory.', 'wpvivid-backuprestore' ); ?>
            </div>

            <div class="wpvivid-element-space-bottom"><strong><?php esc_html_e('File Copy Count', 'wpvivid-backuprestore'); ?></strong></div>
            <div class="wpvivid-element-space-bottom">
                <input type="text" class="all-options" option="setting" name="staging_file_copy_count" value="<?php echo esc_attr($staging_file_copy_count); ?>"
                       placeholder="500" onkeyup="value=value.replace(/\D/g,'')" />
            </div>
            <div class="wpvivid-element-space-bottom">
                <?php esc_html_e( 'Number of files to copy that will be copied within one ajax request. The higher value makes the file copy process faster. 
                Please try a high value to find out the highest possible value. If you encounter timeout errors, try lower values until no more errors occur.', 'wpvivid-backuprestore' ); ?>
            </div>

            <div class="wpvivid-element-space-bottom"><strong><?php esc_html_e('Max File Size', 'wpvivid-backuprestore'); ?></strong></div>
            <div class="wpvivid-element-space-bottom">
                <input type="text" class="all-options" option="setting" name="staging_exclude_file_size" value="<?php echo esc_attr($staging_exclude_file_size); ?>"
                       placeholder="30" onkeyup="value=value.replace(/\D/g,'')" />MB
            </div>
            <div class="wpvivid-element-space-bottom">
                <?php esc_html_e( 'Maximum size of the files copied to a staging site. All files larger than this value will be ignored. If you set the value of 0 MB, all files will be copied to a staging site.', 'wpvivid-backuprestore' ); ?>
            </div>

            <div class="wpvivid-element-space-bottom"><strong><?php esc_html_e('Staging Memory Limit', 'wpvivid-backuprestore'); ?></strong></div>
            <div class="wpvivid-element-space-bottom">
                <input type="text" class="all-options" option="setting" name="staging_memory_limit" value="<?php echo esc_attr($staging_memory_limit); ?>"
                       placeholder="256" onkeyup="value=value.replace(/\D/g,'')" />MB
            </div>
            <div class="wpvivid-element-space-bottom">
                <?php esc_html_e('Adjust this value to apply for a temporary PHP memory limit for the plugin to create a staging site. 
                We set this value to 256M by default. Increase the value if you encounter a memory exhausted error. Note: some 
                web hosting providers may not support this.', 'wpvivid-backuprestore'); ?>
            </div>

            <div class="wpvivid-element-space-bottom"><strong><?php esc_html_e('PHP Script Execution Timeout', 'wpvivid-backuprestore'); ?></strong></div>
            <div class="wpvivid-element-space-bottom">
                <input type="text" class="all-options" option="setting" name="staging_max_execution_time" value="<?php echo esc_attr($staging_max_execution_time); ?>"
                       placeholder="900" onkeyup="value=value.replace(/\D/g,'')" />
            </div>
            <div class="wpvivid-element-space-bottom">
                <?php esc_html_e( 'The time-out is not your server PHP time-out. With the execution time exhausted, our plugin will shut down the progress of 
                creating a staging site. If the progress  encounters a time-out, that means you have a medium or large sized website. Please try to 
                scale the value bigger.', 'wpvivid-backuprestore' ); ?>
            </div>

            <div class="wpvivid-element-space-bottom"><strong><?php esc_html_e('Delay Between Requests', 'wpvivid-backuprestore'); ?></strong></div>
            <div class="wpvivid-element-space-bottom">
                <input type="text" class="all-options" option="setting" name="staging_request_timeout" value="<?php echo esc_attr($staging_request_timeout); ?>"
                       placeholder="1500" onkeyup="value=value.replace(/\D/g,'')" />ms
            </div>
            <div class="wpvivid-element-space-bottom">
                <?php esc_html_e( 'A lower value will help speed up the process of creating a staging site. However, if your server has a limit on the number of requests, a higher value is recommended.', 'wpvivid-backuprestore' ); ?>
            </div>

            <div class="wpvivid-element-space-bottom">
                <strong><?php esc_html_e('Retrying', 'wpvivid-backuprestore'); ?></strong>
                <select option="setting" name="staging_resume_count">
                    <?php
                    for($resume_count=3; $resume_count<10; $resume_count++){
                        if($resume_count === $staging_resume_count){
                           echo '<option selected="selected" value="'.esc_attr($resume_count).'">'.esc_html($resume_count).'</option>';
                        }
                        else{
                            echo'<option value="'.esc_attr($resume_count).'">'.esc_html($resume_count).'</option>';
                        }
                    }
                    ?>
                </select><strong><?php esc_html_e(' times when encountering a time-out error', 'wpvivid-backuprestore'); ?></strong>
            </div>

            <div class="wpvivid-element-space-bottom">
                <label>
                    <input type="checkbox" option="setting" name="not_need_login" <?php echo esc_attr($staging_not_need_login_check); ?> />
                    <span><strong><?php esc_html_e('Anyone can visit the staging site', 'wpvivid-backuprestore'); ?></strong></span>
                </label>
            </div>

            <div class="wpvivid-element-space-bottom">
                <span><?php esc_html_e('When the option is checked, anyone will be able to visit the staging site without the need to login. Uncheck it to request a login to visit the staging site.', 'wpvivid-backuprestore'); ?></span>
            </div>

            <div class="wpvivid-element-space-bottom">
                <label>
                    <input type="checkbox" option="setting" name="staging_overwrite_permalink" <?php echo esc_attr($staging_overwrite_permalink_check); ?> />
                    <span><strong><?php esc_html_e('Keep permalink when transferring website', 'wpvivid-backuprestore'); ?></strong></span>
                </label>
            </div>

            <div class="wpvivid-element-space-bottom">
                <span><?php esc_html_e('When checked, this option allows you to keep the current permalink structure when you create a staging site or push a staging site to live.', 'wpvivid-backuprestore'); ?></span>
            </div>

            <div class="wpvivid-element-space-bottom">
                <label>
                    <input type="checkbox" option="setting" name="staging_keep_setting" <?php echo esc_attr($staging_keep_setting); ?> />
                    <span><strong><?php esc_html_e('Keep staging sites when deleting the plugin', 'wpvivid-backuprestore'); ?></strong></span>
                </label>
            </div>

            <div class="wpvivid-element-space-bottom">
                <span><?php esc_html_e('With this option checked, all staging sites you have created will be retained when the plugin is deleted, just in case you still need them later. The sites will show up again after the plugin is reinstalled.', 'wpvivid-backuprestore'); ?></span>
            </div>
        </div>
        <div><input class="button-primary wpvividstg_save_setting" type="submit" value="<?php echo esc_attr( 'Save Changes'); ?>" /></div>
        <script>
            jQuery('.wpvividstg_save_setting').click(function()
            {
                wpvividstg_save_setting();
            });

            function wpvividstg_save_setting()
            {
                var setting_data = wpvivid_ajax_data_transfer('setting');
                var ajax_data = {
                    'action': 'wpvividstg_save_setting',
                    'setting': setting_data,
                };
                jQuery('.wpvividstg_save_setting').css({'pointer-events': 'none', 'opacity': '0.4'});
                wpvivid_post_request(ajax_data, function (data)
                {
                    try
                    {
                        var jsonarray = jQuery.parseJSON(data);

                        jQuery('.wpvividstg_save_setting').css({'pointer-events': 'auto', 'opacity': '1'});
                        if (jsonarray.result === 'success')
                        {
                            location.href='<?php echo esc_url(apply_filters('wpvividstg_get_admin_url', '')) . 'admin.php?page=wpvividstg-setting'; ?>';
                        }
                        else {
                            alert(jsonarray.error);
                        }
                    }
                    catch (err)
                    {
                        alert(err);
                        jQuery('.wpvividstg_save_setting').css({'pointer-events': 'auto', 'opacity': '1'});
                    }
                }, function (XMLHttpRequest, textStatus, errorThrown) {
                    jQuery('.wpvividstg_save_setting').css({'pointer-events': 'auto', 'opacity': '1'});
                    var error_message = wpvivid_output_ajaxerror('changing base settings', textStatus, errorThrown);
                    alert(error_message);
                });
            }
        </script>
        <?php
    }

    public function set_general_setting($setting_data, $setting, $options)
    {
        $options=get_option('wpvivid_staging_options',array());

        if(isset($setting['staging_db_insert_count']))
            $options['staging_db_insert_count'] = intval($setting['staging_db_insert_count']);
        if(isset($setting['staging_db_replace_count']))
            $options['staging_db_replace_count'] = intval($setting['staging_db_replace_count']);
        if(isset($setting['staging_file_copy_count']))
            $options['staging_file_copy_count'] = intval($setting['staging_file_copy_count']);
        if(isset($setting['staging_exclude_file_size']))
        $options['staging_exclude_file_size'] = intval($setting['staging_exclude_file_size']);
        if(isset($setting['staging_memory_limit']))
            $options['staging_memory_limit'] = $setting['staging_memory_limit'].'M';
        if(isset($setting['staging_max_execution_time']))
            $options['staging_max_execution_time'] = intval($setting['staging_max_execution_time']);
        if(isset($setting['staging_resume_count']))
            $options['staging_resume_count'] = intval($setting['staging_resume_count']);
        if(isset($setting['not_need_login']))
            $options['not_need_login']= intval($setting['not_need_login']);
        if(isset($setting['staging_overwrite_permalink']))
            $options['staging_overwrite_permalink'] = intval($setting['staging_overwrite_permalink']);
        if(isset($setting['staging_request_timeout']))
            $options['staging_request_timeout']= intval($setting['staging_request_timeout']);
        if(isset($setting['staging_keep_setting']))
            $options['staging_keep_setting']= intval($setting['staging_keep_setting']);

        update_option('wpvivid_staging_options',$options,'no');

        return $setting_data;
    }

    public function save_setting()
    {
        check_ajax_referer( 'wpvivid_ajax', 'nonce' );
        $check=current_user_can('manage_options');
        $check=apply_filters('wpvivid_ajax_check_security',$check);
        if(!$check)
        {
            die();
        }
        $ret=array();
        try
        {
            if(isset($_POST['setting'])&&!empty($_POST['setting']))
            {
                $json_setting = sanitize_text_field($_POST['setting']);
                $json_setting = stripslashes($json_setting);
                $setting = json_decode($json_setting, true);
                if (is_null($setting))
                {
                    echo 'json decode failed';
                    die();
                }

                $options=get_option('wpvivid_staging_options',array());

                $options['staging_db_insert_count'] = intval($setting['staging_db_insert_count']);
                $options['staging_db_replace_count'] = intval($setting['staging_db_replace_count']);
                $options['staging_file_copy_count'] = intval($setting['staging_file_copy_count']);
                $options['staging_exclude_file_size'] = intval($setting['staging_exclude_file_size']);
                $options['staging_memory_limit'] = $setting['staging_memory_limit'].'M';
                $options['staging_max_execution_time'] = intval($setting['staging_max_execution_time']);
                $options['staging_resume_count'] = intval($setting['staging_resume_count']);
                $options['not_need_login']= intval($setting['not_need_login']);
                $options['staging_overwrite_permalink'] = intval($setting['staging_overwrite_permalink']);

                $options['staging_request_timeout']= intval($setting['staging_request_timeout']);
                $options['staging_keep_setting']= intval($setting['staging_keep_setting']);

                update_option('wpvivid_staging_options',$options,'no');

                $ret['result']='success';
            }
        }
        catch (Exception $error)
        {
            $message = 'An exception has occurred. class: '.get_class($error).';msg: '.$error->getMessage().';code: '.$error->getCode().';line: '.$error->getLine().';in_file: '.$error->getFile().';';
            error_log($message);
            echo wp_json_encode(array('result'=>'failed','error'=>$message));
            die();
        }
        echo wp_json_encode($ret);
        die();
    }
}includes/staging/class-wpvivid-staging-copy-files-ex.php000064400000066570151327705670017461 0ustar00<?php
if (!defined('WPVIVID_PLUGIN_DIR'))
{
    die;
}
class WPvivid_Staging_Copy_Files
{
    public $task;
    public $cache_file;
    public $cache_file_name;

    public function __construct($task_id)
    {
        $this->task=new WPvivid_Staging_Task($task_id);
        $this->cache_file_name=WP_CONTENT_DIR.DIRECTORY_SEPARATOR.'wpvivid_staging'.DIRECTORY_SEPARATOR.$this->task->get_id().'_staging_cache.txt';
        $this->cache_file=false;

    }

    public function do_copy_file($key)
    {
        global $wpvivid_plugin;

        $src_path=$des_path='';
        $wpvivid_plugin->staging->log->WriteLog('Retrieve the files required to copy.','notice');
        $list=$this->get_copy_dir_list($key,$src_path,$des_path);
        if(!file_exists($this->cache_file_name))
        {
            $wpvivid_plugin->staging->log->WriteLog('Create a cache file.','notice');
            $this->create_cache_file($list);
        }

        $start=$this->task->get_start($key);
        $wpvivid_plugin->staging->log->WriteLog('Copying files starts from: '.$start,'notice');
        $wpvivid_plugin->staging->log->WriteLog('Copying files from '.$src_path.' to '.$des_path,'notice');

        while($this->copy_files($start,$this->task->get_files_copy_count(),$src_path,$des_path))
        {
            $wpvivid_plugin->staging->log->WriteLog('The count of copied files: '.$this->task->get_files_copy_count(),'notice');
            $wpvivid_plugin->staging->log->WriteLog('The next copying files starts from:'.$start,'notice');
            $this->task->update_start($key,$start);
        }
        $wpvivid_plugin->staging->log->WriteLog('Copying '.$key.' files is completed.','notice');

        if($key=='core')
        {
            $this->check_wp_config();

            if(is_multisite()&&$this->task->is_restore())
            {

            }
            else
            {
                $this->change_wp_config();
            }

            if(is_multisite()&&!$this->task->is_restore())
            {
                $this->change_htaccess();
            }
        }
        $this->task->update_job_finished($key);

        $this->clean_up();

        return true;
    }

    public function check_wp_config()
    {
        $des_path=$this->task->get_path();
        $des=$des_path.DIRECTORY_SEPARATOR.'wp-config.php';
        if(file_exists($des))
        {
            return;
        }
        else
        {
            if ( file_exists( ABSPATH . 'wp-config.php' ) )
            {
                $src=ABSPATH . 'wp-config.php';
            }
            else if ( @file_exists( dirname( ABSPATH ) . '/wp-config.php' ))
            {
                $src=dirname( ABSPATH ) . '/wp-config.php';
            }
            else
            {
                global $wpvivid_plugin;
                $wpvivid_plugin->staging->log->WriteLog('not found wp-config.php file','notice');
                return ;
            }
            if(copy($src,$des))
            {
                @chmod($des,0755);
            }
        }
    }

    public function change_wp_config()
    {
        global $wpvivid_plugin;

        $des_path=$this->task->get_path();
        $path=$des_path.DIRECTORY_SEPARATOR.'wp-config.php';
        $data=file_get_contents($path);
        if( $data === false )
        {
            $wpvivid_plugin->staging->log->WriteLog('wp-config.php not found in '.$path,'notice');
            return false;
        }

        $pattern     = '/\$table_prefix\s*=\s*(.*)/';
        $replacement = '$table_prefix = \'' . $this->task->get_db_prefix(true) . '\';';
        $data     = preg_replace( $pattern, $replacement, $data );


        if( $data===null )
        {
            $wpvivid_plugin->staging->log->WriteLog('table_prefix not found in wp-config.php','notice');
            return false;
        }

        preg_match( "/define\s*\(\s*['\"]WP_HOME['\"]\s*,\s*(.*)\s*\);/", $data, $matches );
        if( !empty( $matches[1] ) )
        {
            $wpvivid_plugin->staging->log->WriteLog('WP_HOME found in wp-config.php','notice');
            $pattern = "/define\s*\(\s*['\"]WP_HOME['\"]\s*,\s*(.*)\s*\);.*/";
            $replace = "define('WP_HOME','" . $this->task->get_home_url(true) . "'); //";
            $data = preg_replace( array($pattern), $replace, $data );
            if( null === ($data) )
            {
                $wpvivid_plugin->staging->log->WriteLog('WP_HOME not replace in wp-config.php','notice');
                return false;
            }
        }

        preg_match( "/define\s*\(\s*['\"]WP_SITEURL['\"]\s*,\s*(.*)\s*\);/", $data, $matches );
        if( !empty( $matches[1] ) )
        {
            $wpvivid_plugin->staging->log->WriteLog('WP_SITEURL found in wp-config.php','notice');
            $pattern = "/define\s*\(\s*['\"]WP_SITEURL['\"]\s*,\s*(.*)\s*\);.*/";
            $replace = "define('WP_SITEURL','" . $this->task->get_site_url(true) . "'); //";
            $data = preg_replace( array($pattern), $replace, $data );
            if( null === ($data) )
            {
                $wpvivid_plugin->staging->log->WriteLog('WP_SITEURL not replace in wp-config.php','notice');
                return false;
            }
        }

        if(is_multisite()&&!$this->task->is_restore())
        {
            preg_match( "/define\s*\(\s*['\"]PATH_CURRENT_SITE['\"]\s*,\s*(.*)\s*\);/", $data, $matches );
            if( !empty( $matches[1] ) )
            {
                $mu_option=$this->task->get_mu_option();
                $wpvivid_plugin->staging->log->WriteLog('PATH_CURRENT_SITE found in wp-config.php','notice');
                $pattern = "/define\s*\(\s*['\"]PATH_CURRENT_SITE['\"]\s*,\s*(.*)\s*\);.*/";
                $replace = "define('PATH_CURRENT_SITE','" .$mu_option['path_current_site'] . "'); //";
                $data = preg_replace( array($pattern), $replace, $data );
                if( null === ($data) )
                {
                    $wpvivid_plugin->staging->log->WriteLog('PATH_CURRENT_SITE not replace in wp-config.php','notice');
                    return false;
                }
            }

            if($this->task->is_mu_single())
            {
                preg_match( "/define\s*\(\s*['\"]WP_ALLOW_MULTISITE['\"]\s*,\s*(.*)\s*\);/", $data, $matches );
                if( !empty( $matches[1] ) )
                {
                    $wpvivid_plugin->staging->log->WriteLog('WP_ALLOW_MULTISITE found in wp-config.php','notice');
                    $pattern = "/define\s*\(\s*['\"]WP_ALLOW_MULTISITE['\"]\s*,\s*(.*)\s*\);.*/";
                    $replace = "define('WP_ALLOW_MULTISITE',false); //";
                    $data = preg_replace( array($pattern), $replace, $data );
                    if( null === ($data) )
                    {
                        $wpvivid_plugin->staging->log->WriteLog('WP_ALLOW_MULTISITE not replace in wp-config.php','notice');
                        return false;
                    }
                }

                preg_match( "/define\s*\(\s*['\"]MULTISITE['\"]\s*,\s*(.*)\s*\);/", $data, $matches );
                if( !empty( $matches[1] ) )
                {
                    $wpvivid_plugin->staging->log->WriteLog('MULTISITE found in wp-config.php','notice');
                    $pattern = "/define\s*\(\s*['\"]MULTISITE['\"]\s*,\s*(.*)\s*\);.*/";
                    $replace = "define('MULTISITE',false); //";
                    $data = preg_replace( array($pattern), $replace, $data );
                    if( null === ($data) )
                    {
                        $wpvivid_plugin->staging->log->WriteLog('MULTISITE not replace in wp-config.php','notice');
                        return false;
                    }
                }

                preg_match( "/define\s*\(\s*['\"]UPLOADS['\"]\s*,\s*(.*)\s*\);/", $data, $matches );
                if( !empty( $matches[1] ) )
                {
                    $wpvivid_plugin->staging->log->WriteLog('UPLOADS found in wp-config.php','notice');
                    $pattern = "/define\s*\(\s*['\"]UPLOADS['\"]\s*,\s*(.*)\s*\);.*/";
                    $replace = "define('UPLOADS','".$this->task->get_mu_single_upload()."'); //";
                    $data = preg_replace( array($pattern), $replace, $data );
                    if( null === ($data) )
                    {
                        $wpvivid_plugin->staging->log->WriteLog('MULTISITE not replace in wp-config.php','notice');
                        return false;
                    }
                }
                else
                {
                    preg_match("/if\s*\(\s*\s*!\s*defined\s*\(\s*['\"]ABSPATH['\"]\s*(.*)\s*\)\s*\)/", $data, $matches);
                    if (!empty($matches[0]))
                    {
                        $matches[0];
                        $pattern = "/if\s*\(\s*\s*!\s*defined\s*\(\s*['\"]ABSPATH['\"]\s*(.*)\s*\)\s*\)/";
                        $replace = "define('UPLOADS', '".$this->task->get_mu_single_upload()."'); \n".
                            "if ( ! defined( 'ABSPATH' ) )";
                        $data = preg_replace( array($pattern), $replace, $data );
                        if (null === ($data))
                        {
                            $wpvivid_plugin->staging->log->WriteLog('UPLOADS not replace in wp-config.php','notice');
                            return false;
                        }
                    }
                }
            }
        }

        $db=$this->task->get_db_connect();

        if($this->task->is_restore())
        {
            $wpvivid_plugin->staging->log->WriteLog('Edit wp-config.php','notice');
            if( $db['src_use_additional_db'])
            {

                $pattern     = "/define\s*\(\s*'DB_NAME'\s*,\s*(.*)\s*\);.*/";
                $replacement = "define( 'DB_NAME', '".DB_NAME."');";
                $data     = preg_replace( $pattern, $replacement, $data );

                $pattern     = "/define\s*\(\s*'DB_USER'\s*,\s*(.*)\s*\);.*/";
                $replacement = "define( 'DB_USER', '".DB_USER."');";
                $data     = preg_replace( $pattern, $replacement, $data );

                $pattern     = "/define\s*\(\s*'DB_PASSWORD'\s*,\s*(.*)\s*\);.*/";
                $replacement = "define( 'DB_PASSWORD', '".DB_PASSWORD."');";
                $data     = preg_replace( $pattern, $replacement, $data );

                $pattern     = "/define\s*\(\s*'DB_HOST'\s*,\s*(.*)\s*\);.*/";
                $replacement = "define( 'DB_HOST', '".DB_HOST."');";
                $data     = preg_replace( $pattern, $replacement, $data );
            }
        }
        else
        {
            if( $db['des_use_additional_db'])
            {
                $pattern     = "/define\s*\(\s*'DB_NAME'\s*,\s*(.*)\s*\);.*/";
                $replacement = "define( 'DB_NAME', '{$db['des_dbname']}');";
                $data     = preg_replace( $pattern, $replacement, $data );

                $pattern     = "/define\s*\(\s*'DB_USER'\s*,\s*(.*)\s*\);.*/";
                $replacement = "define( 'DB_USER', '{$db['des_dbuser']}');";
                $data     = preg_replace( $pattern, $replacement, $data );

                $pattern     = "/define\s*\(\s*'DB_PASSWORD'\s*,\s*(.*)\s*\);.*/";
                $replacement = "define( 'DB_PASSWORD', '{$db['des_dbpassword']}');";
                $data     = preg_replace( $pattern, $replacement, $data );

                $pattern     = "/define\s*\(\s*'DB_HOST'\s*,\s*(.*)\s*\);.*/";
                $replacement = "define( 'DB_HOST', '{$db['des_dbhost']}');";
                $data     = preg_replace( $pattern, $replacement, $data );
            }
        }

        file_put_contents($path,$data);

        $wpvivid_plugin->staging->log->WriteLog('Replacing table_prefix in wp-config.php is completed.','notice');
        return true;
    }

    public function change_htaccess()
    {
        global $wpvivid_plugin;
        $des_path=$this->task->get_path();
        $path=$des_path.DIRECTORY_SEPARATOR.'.htaccess';
        if(file_exists($path))
        {
            if(is_multisite()&&!$this->task->is_restore())
            {
                $mu_option=$this->task->get_mu_option();
                $data=file_get_contents($path);
                //$data = str_replace(PATH_CURRENT_SITE,$mu_option['path_current_site'],$data);
                preg_match( "/RewriteBase\s*(.*)\s*/", $data, $matches );
                if( !empty( $matches[1] ) )
                {
                    $new_rewrite_base = $mu_option['path_current_site'];
                    $wpvivid_plugin->staging->log->WriteLog('RewriteBase found in .htaccess','notice');
                    $pattern = "/RewriteBase\s*(.*)\s*.*/";
                    $replace = "RewriteBase $new_rewrite_base";
                    $data = preg_replace( array($pattern), $replace, $data );
                    if( null === ($data) )
                    {
                        $wpvivid_plugin->staging->log->WriteLog('WP_HOME not replace in wp-config.php','notice');
                    }
                }
                file_put_contents($path,$data);
            }
        }
    }

    public function get_copy_dir_list($key,&$src_path,&$des_path)
    {
        $list=array();
        if($key=='core')
        {
            $src_path=$this->task->get_path(false);
            $des_path=$this->task->get_path(true);

            $dir_info['root']=$this -> transfer_path($src_path);
            $dir_info['recursive']=false;
            if($this->task->is_restore()&&is_multisite())
            {
                $exclude_files_regex[]='#.htaccess#';
                $exclude_files_regex[]='#wp-config.php#';
                $dir_info['exclude_files_regex']=$exclude_files_regex;
            }
            $list[]=$dir_info;
            $dir_info['root']=$src_path.DIRECTORY_SEPARATOR.'wp-admin';
            $dir_info['recursive']=true;
            $list[]=$dir_info;
            $dir_info['root']=$src_path.DIRECTORY_SEPARATOR.'wp-includes';
            $list[]=$dir_info;
        }
        else if($key=='wp-content')
        {
            $des_path=$this->get_content_dir(true);
            $src_path=untrailingslashit($this->get_content_dir());
            $dir_info['root']=$this -> transfer_path($src_path);
            $dir_info['recursive']=true;
            $exclude_regex=$this->task->get_job_option($key,'exclude_regex');
            $exclude_files_regex=$this->task->get_job_option($key,'exclude_files_regex');
            $exclude_regex[]='#^'.preg_quote($this -> transfer_path($this->get_content_dir()).DIRECTORY_SEPARATOR.'updraft', '/').'#';   // Updraft Plus backup directory
            $exclude_regex[]='#^'.preg_quote($this -> transfer_path($this->get_content_dir()).DIRECTORY_SEPARATOR.'ai1wm-backups', '/').'#'; // All-in-one WP migration backup directory
            $exclude_regex[]='#^'.preg_quote($this -> transfer_path($this->get_content_dir()).DIRECTORY_SEPARATOR.'backups', '/').'#'; // Xcloner backup directory
            $exclude_regex[]='#^'.preg_quote($this -> transfer_path($this->get_content_dir()).DIRECTORY_SEPARATOR.'upgrade', '/').'#';
            $exclude_regex[]='#^'.preg_quote($this -> transfer_path($this->get_content_dir()).DIRECTORY_SEPARATOR.'wpvivid', '/').'#';
            $exclude_regex[]='#^'.preg_quote($this -> transfer_path($this->get_content_dir()).DIRECTORY_SEPARATOR.'wpvivid_staging', '/').'#';
            //$exclude_regex[]='#^'.preg_quote($this->transfer_path($this->get_content_dir()), '/').'#';
            $exclude_regex[]='#^'.preg_quote($this -> transfer_path($this->get_content_dir()).DIRECTORY_SEPARATOR.'cache', '/').'#';
            $exclude_regex[]='#^'.preg_quote($this -> transfer_path($this->get_content_dir()).DIRECTORY_SEPARATOR.'w3tc-config', '/').'#';
            $exclude_regex[]='#^'.preg_quote($this -> transfer_path($this->get_content_dir()).DIRECTORY_SEPARATOR.'Dropbox_Backup', '/').'#';
            $exclude_regex[]='#^'.preg_quote($this -> transfer_path($this->get_upload_dir()), '/').'#';
            $exclude_regex[]='#^'.preg_quote($this->transfer_path($this->get_theme_dir()), '/').'#';
            $exclude_regex[]='#^'.preg_quote($this->transfer_path($this->get_plugin_dir()), '/').'#';

            //$self_dir = str_replace($src_path, '', $this -> transfer_path($des_path));
            //$self_dir = str_replace('wp-content', '', $self_dir);
            //$self_dir = str_replace('\\', '', $self_dir);
            //$exclude_regex[]='#^'.preg_quote($this -> transfer_path($this->get_content_dir()).DIRECTORY_SEPARATOR.$self_dir, '/').'#';
            $staging_list = get_option('wpvivid_staging_task_list',array());
            if(!empty($staging_list))
            {
                foreach ($staging_list as $key => $value)
                {
                    $exclude_regex[]='#^'.preg_quote($this -> transfer_path($value['path']['des_path']), '/').'$#';
                }
            }

            $dir_info['exclude_regex']=$exclude_regex;
            $dir_info['exclude_files_regex']=$exclude_files_regex;
            $list[]=$dir_info;
        }
        else if($key=='plugins')
        {
            $des_path=$this->get_plugin_dir(true);
            $src_path=untrailingslashit($this->get_plugin_dir());

            $dir_info['root']=$this -> transfer_path($src_path);
            $exclude_regex=$this->task->get_job_option($key,'exclude_regex');

            if($this->task->is_restore())
            {
                $exclude_regex[]='#^'.preg_quote($this -> transfer_path($this->get_plugin_dir().DIRECTORY_SEPARATOR.'wpvivid-backuprestore'), '/').'#';
                $exclude_regex[]='#^'.preg_quote($this -> transfer_path($this->get_plugin_dir().DIRECTORY_SEPARATOR.'wpvivid-staging'), '/').'#';
                $exclude_regex[]='#^'.preg_quote($this -> transfer_path($this->get_plugin_dir().DIRECTORY_SEPARATOR.'wpvivid-backup-pro'), '/').'#';
            }

            $dir_info['exclude_regex']=$exclude_regex;

            $dir_info['recursive']=true;
            $list[]=$dir_info;
        }
        else if($key=='theme')
        {
            $des_path=$this->get_theme_dir(true);
            $src_path=$this->get_theme_dir();
            $dir_info['root']=$this -> transfer_path($src_path);
            $dir_info['exclude_regex']=$this->task->get_job_option($key,'exclude_regex');
            $dir_info['recursive']=true;
            $list[]=$dir_info;
        }
        else if($key=='upload')
        {
            $des_path=$this->get_upload_dir(true);
            $src_path=$this->get_upload_dir();
            $dir_info['root']=$this -> transfer_path($src_path);
            $dir_info['exclude_regex']=$this->task->get_job_option($key,'exclude_regex');
            $exclude_files_regex=$this->task->get_job_option($key,'exclude_files_regex');
            if($this->task->get_job_option($key,'include_regex'))
            {
                $dir_info['include_regex']=$this->task->get_job_option($key,'include_regex');
            }
            $dir_info['exclude_files_regex']=$exclude_files_regex;

            $dir_info['recursive']=true;
            $list[]=$dir_info;
        }
        else
        {
            $src_path=$this->task->get_path(false);
            $des_path=$this->task->get_path();
            $path=$this->task->get_job_option($key,'root');
            $dir_info['root']=$this -> transfer_path($src_path.DIRECTORY_SEPARATOR.$path);
            $dir_info['exclude_regex']=$this->task->get_job_option($key,'exclude_regex');
            $exclude_files_regex=$this->task->get_job_option($key,'exclude_files_regex');
            $dir_info['exclude_files_regex']=$exclude_files_regex;
            $dir_info['recursive']=true;
            $list[]=$dir_info;
        }

        $src_path=$this -> transfer_path($src_path);
        $des_path=$this -> transfer_path($des_path);

        return $list;
    }

    public function get_content_dir($des=false)
    {
        $dir = str_replace( ABSPATH, '', WP_CONTENT_DIR );
        $src_path=$this->task->get_path($des);
        return $src_path.DIRECTORY_SEPARATOR.$dir;
    }

    public function get_upload_dir($des=false)
    {
        $upload_dir = wp_upload_dir();
        $dir = str_replace( ABSPATH, '', $upload_dir['basedir'] );
        $src_path=$this->task->get_path($des);
        return $src_path.DIRECTORY_SEPARATOR.$dir;
    }

    public function get_theme_dir($des=false)
    {
        $dir = str_replace( ABSPATH, '',get_theme_root() );
        $src_path=$this->task->get_path($des);
        return $src_path.DIRECTORY_SEPARATOR.$dir;
    }

    public function get_plugin_dir($des=false)
    {
        $dir = str_replace( ABSPATH, '',WP_PLUGIN_DIR );
        $src_path=$this->task->get_path($des);
        return $src_path.DIRECTORY_SEPARATOR.$dir;
    }

    private function transfer_path($path)
    {
        $path = str_replace('\\','/',$path);
        $values = explode('/',$path);
        return implode(DIRECTORY_SEPARATOR,$values);
    }

    public function create_cache_file($list)
    {
        if(file_exists($this->cache_file_name))
            @wp_delete_file($this->cache_file_name);
        $this->cache_file=fopen($this->cache_file_name,'a');
        foreach ($list as $item)
        {
            $exclude_regex=array();
            $exclude_files_regex=array();
            if(isset($item['exclude_regex'])&&$item['exclude_regex']!=false)
            {
                $exclude_regex=$item['exclude_regex'];
            }
            if(isset($item['exclude_files_regex'])&&$item['exclude_files_regex']!=false)
            {
                $exclude_files_regex=$item['exclude_files_regex'];
            }
            //
            if(isset($item['include_regex'])&&$item['include_regex']!=false)
            {
                $include_regex=$item['include_regex'];
            }
            else
            {
                $include_regex=array();
            }

            $this->create_cache_from_folder($item['root'],$item['recursive'],$exclude_regex,$exclude_files_regex,$include_regex);
        }
    }

    public function copy_files(&$start,$count,$src_path,$des_path)
    {
        global $wpvivid_plugin;
        $file = new SplFileObject($this->cache_file_name);

        if($start==0)
            $file->seek($start);
        else
            $file->seek($start-1);

        $file->setFlags( \SplFileObject::SKIP_EMPTY | \SplFileObject::READ_AHEAD );

        for ( $i = 0; $i < $count; $i++ )
        {
            if( $file->eof() )
            {
                return false;
            }
            $src = $file->fgets();

            $src=trim($src,PHP_EOL);

            if(empty($src))
                continue;

            $start++;

            if(!file_exists($src))
            {
                continue;
            }
            $src=$this -> transfer_path($src);
            $des=str_replace($src_path,$des_path,$src);

            if(is_dir($src))
            {
                @mkdir($des,0755,true);
            }
            else
            {
                if(copy($src,$des))
                {
                    @chmod($des,0755);
                }
                else
                {
                    $wpvivid_plugin->staging->log->WriteLog('Failed to copy files from '.$src.' to '.$des.'.','warning');
                }
            }
        }

        $file = null;
        return true;
    }

    public function create_cache_from_folder($folder,$recursive=false,$exclude_regex=array(),$exclude_files_regex=array(),$include_regex=array())
    {
        $this->getFolder($folder,$recursive,$exclude_regex,$exclude_files_regex,$include_regex);
    }

    public function getFolder($path,$recursive,$exclude_regex,$exclude_files_regex,$include_regex)
    {
        if($this->cache_file==false)
            $this->cache_file=fopen($this->cache_file_name,'a');

        if(is_dir($path))
        {
            $line = $path.PHP_EOL;
            fwrite($this->cache_file, $line);

            $handler = opendir($path);
            if($handler!==false)
            {
                while (($filename = readdir($handler)) !== false)
                {
                    if ($filename != "." && $filename != "..")
                    {
                        if (is_dir($path . DIRECTORY_SEPARATOR . $filename))
                        {
                            if($recursive&&$this->regex_match($exclude_regex, $path . DIRECTORY_SEPARATOR . $filename, 0))
                            {
                                if(!empty($include_regex))
                                {
                                    if($recursive&&$this->regex_match($include_regex, $path . DIRECTORY_SEPARATOR . $filename, 1))
                                    {
                                        $this->getFolder($path . DIRECTORY_SEPARATOR . $filename,$recursive,$exclude_regex,$exclude_files_regex,$include_regex);
                                    }
                                }
                                else
                                {
                                    $this->getFolder($path . DIRECTORY_SEPARATOR . $filename,$recursive,$exclude_regex,$exclude_files_regex,$include_regex);
                                }
                            }
                        } else {

                            if($this->regex_match($exclude_files_regex, $filename, 0))
                            {
                                if ($this->regex_match($exclude_regex, $path . DIRECTORY_SEPARATOR . $filename, 0))
                                {
                                    if(is_readable($path . DIRECTORY_SEPARATOR . $filename))
                                    {
                                        if (filesize($path . DIRECTORY_SEPARATOR . $filename) < $this->task->get_exclude_file_size() * 1024 * 1024 || $this->task->get_exclude_file_size() === 0)
                                        {
                                            $line = $path . DIRECTORY_SEPARATOR . $filename.PHP_EOL;
                                            fwrite($this->cache_file, $line);
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
                if($handler)
                    @closedir($handler);
            }
        }
    }

    private function regex_match($regex_array,$string,$mode)
    {
        if(empty($regex_array))
        {
            return true;
        }

        if($mode==0)
        {
            foreach ($regex_array as $regex)
            {
                if(preg_match($regex,$string))
                {
                    return false;
                }
            }

            return true;
        }

        if($mode==1)
        {
            foreach ($regex_array as $regex)
            {
                if(preg_match($regex,$string))
                {
                    return true;
                }
            }

            return false;
        }

        return true;
    }

    public function clean_up()
    {
        if($this->cache_file)
            fclose($this->cache_file);
        @wp_delete_file($this->cache_file_name);
    }
}includes/staging/class-wpvivid-staging-list-ui-display.php000064400000222044151327705670020014 0ustar00<?php
if (!defined('WPVIVID_PLUGIN_DIR'))
{
    die;
}

class WPvivid_Staging_List_UI_Display_Free
{
    public function __construct()
    {

    }

    public function get_staging_site_data()
    {
        if(is_multisite())
        {
            switch_to_blog(get_main_site_id());
            $staging=get_option('wpvivid_staging_data',false);
            restore_current_blog();
        }
        else
        {
            $staging=get_option('wpvivid_staging_data',false);
        }

        return $staging;
    }

    public function output_staging_sites_list_page()
    {
        ?>
        <div class="postbox quickstaging">
            <div class="wpvivid-one-coloum" style="border:1px solid #f1f1f1;padding-top:0em;padding-bottom:0em;">
                <div class="wpvivid-two-col">
                    <ul class="">
                        <li>
                            <input type="button" class="button button-primary" id="wpvivid_switch_create_staging_page" value="Create A Staging Site">
                            <p>Click to start creating a staging site.
                        </li>
                    </ul>
                </div>

                <?php
                if(!is_multisite()){
                    ?>
                    <div class="wpvivid-two-col">
                        <ul class="">
                            <li>
                                <input type="button" class="button button-primary" id="wpvivid_switch_create_fresh_install_page" value="Create A Fresh WP Site">
                                <p>Click to start creating a fresh WP install.
                            </li>
                        </ul>
                    </div>
                    <?php
                }
                ?>
                <div style="clear: both;"></div>
            </div>


            <div id="wpvivid_staging_list">
                <?php
                $list = get_option('wpvivid_staging_task_list',array());
                if(!empty($list))
                {
                    foreach ($list as $id => $staging)
                    {
                        if(isset($staging['site']['path']) && !empty($staging['site']['path']))
                        {
                            $staging_site_name = basename($staging['site']['path']);
                        }
                        else{
                            $staging_site_name = 'N/A';
                        }

                        $home_url = home_url();
                        global $wpdb;
                        $home_url_sql = $wpdb->get_results( $wpdb->prepare( "SELECT * FROM $wpdb->options WHERE option_name = %s", 'home' ) );
                        foreach ( $home_url_sql as $home ){
                            $home_url = $home->option_value;
                        }
                        $home_url = untrailingslashit($home_url);
                        $admin_url  = apply_filters('wpvividstg_get_admin_url', '');

                        //$admin_name = str_replace($home_url, '', $admin_url);
                        //$admin_name = trim($admin_name, '/');

                        if(!isset($staging['login_url']))
                        {
                            $admin_name = str_replace($home_url, '', $admin_url);
                            $admin_name = trim($admin_name, '/');
                            $admin_url_descript = 'Admin URL';
                        }
                        else
                        {
                            $login_url = $staging['login_url'];
                            $login_name = str_replace($home_url, '', $login_url);
                            $login_name = trim($login_name, '/');
                            if($login_name !== 'wp-login.php' && !isset($staging['site']['fresh_install']))
                            {
                                $admin_name = $login_name;
                                $admin_url_descript = 'Login URL';
                            }
                            else
                            {
                                $admin_name = str_replace($home_url, '', $admin_url);
                                $admin_name = trim($admin_name, '/');
                                $admin_url_descript = 'Admin URL';
                            }
                        }

                        if(isset($staging['site']['home_url']) && !empty($staging['site']['home_url']))
                        {
                            $site_url = esc_url($staging['site']['home_url']);
                            $admin_url = esc_url($staging['site']['home_url'].'/'.$admin_name.'/');
                        }
                        else{
                            $site_url = 'N/A';
                            $admin_url = 'N/A';
                        }

                        if(isset($staging['site']['prefix']) && !empty($staging['site']['prefix']))
                        {
                            $prefix = $staging['site']['prefix'];
                            if(isset($staging['site']['db_connect']['dbname']) && !empty($staging['site']['db_connect']['dbname'])){
                                $db_name = $staging['site']['db_connect']['dbname'];
                            }
                            else{
                                $db_name = DB_NAME;
                            }
                        }
                        else{
                            $prefix = 'N/A';
                            $db_name = 'N/A';
                        }
                        if(isset($staging['site']['path']) && !empty($staging['site']['path'])){
                            $site_dir = $staging['site']['path'];
                        }
                        else{
                            $site_dir = 'N/A';
                        }

                        if(isset($staging['site']['fresh_install']))
                        {
                            $copy_btn='Copy the Fresh Install to Live(pro feature)';
                            $update_btn='Update the Fresh Install(pro feature)';
                            $class_btn='fresh-install';
                        }
                        else
                        {
                            $copy_btn='Copy the Staging Site to Live(pro feature)';
                            $update_btn='Update the Staging Site(pro feature)';
                            $class_btn='staging-site';
                        }

                        if(isset($staging['create_time']))
                        {
                            $staging_create_time = $staging['create_time'];
                            $offset=get_option('gmt_offset');
                            $utc_time = $staging_create_time + $offset * 60 * 60;
                            $staging_create_time = gmdate('M-d-Y H:i', $utc_time);
                        }
                        else
                        {
                            $staging_create_time = 'N/A';
                        }

                        if(isset($staging['copy_time']))
                        {
                            $staging_copy_time = $staging['copy_time'];
                            $offset=get_option('gmt_offset');
                            $utc_time = $staging_copy_time + $offset * 60 * 60;
                            $staging_copy_time = gmdate('M-d-Y H:i', $utc_time);
                        }
                        else
                        {
                            $staging_copy_time = 'N/A';
                        }
                        ?>
                        <div class="wpvivid-one-coloum" style="border:1px solid #f1f1f1;padding-top:0em; margin-top:1em;" id="<?php echo esc_attr($id); ?>">
                            <div class="wpvivid-two-col">
                                <p><span class="dashicons dashicons-awards wpvivid-dashicons-blue"></span><span><strong>Site Name: </strong></span><span><?php echo esc_html($staging_site_name); ?></span></p>
                                <p><span class="dashicons dashicons-admin-home wpvivid-dashicons-blue"></span><span><strong>Home URL: </strong></span><span><a href="<?php echo esc_url($site_url); ?>"><?php echo esc_url($site_url); ?></a></span></p>
                                <p><span class="dashicons dashicons-rest-api wpvivid-dashicons-blue"></span><span><strong><?php echo esc_html($admin_url_descript); ?>: </strong></span><span><a href="<?php echo esc_url($admin_url); ?>"><?php echo esc_url($admin_url); ?></a></span></p>
                                <p><span class="dashicons dashicons-clock wpvivid-dashicons-blue"></span><span><strong>Create Time: </strong></span><span><?php echo esc_html($staging_create_time); ?></span></p>
                            </div>

                            <div class="wpvivid-two-col">
                                <p><span class="dashicons dashicons-admin-site-alt3 wpvivid-dashicons-blue"></span><span><strong>Database Name: </strong></span><span><?php echo esc_html($db_name); ?></span></p>
                                <p><span class="dashicons dashicons-list-view wpvivid-dashicons-blue"></span><span><strong>Table Prefix: </strong></span><span><?php echo esc_html($prefix); ?></span></p>
                                <p><span class="dashicons dashicons-portfolio wpvivid-dashicons-blue"></span><span><strong>Directory: </strong></span><span><?php echo esc_html($site_dir); ?></span></p>
                                <p><span class="dashicons dashicons-clock wpvivid-dashicons-blue"></span><span><strong>Update Time: </strong></span><span><?php echo esc_html($staging_copy_time); ?></span></p>
                            </div>

                            <div class="wpvivid-copy-staging-to-live-block <?php echo esc_attr($class_btn); ?>" name="<?php echo esc_attr($id); ?>" style="padding:1em 1em 0 0;">
                                <?php
                                if($staging['status']['str'] === 'completed')
                                {
                                    ?>
                                    <span class="wpvivid-rectangle wpvivid-grey-light wpvivid-hover-blue wpvivid-staging-operate wpvivid-update-live-to-staging"><?php echo esc_html($update_btn); ?></span>
                                    <span class="wpvivid-rectangle wpvivid-grey-light wpvivid-hover-blue wpvivid-staging-operate wpvivid-copy-staging-to-live"><?php echo esc_html($copy_btn); ?></span>
                                    <?php
                                }
                                ?>
                                <span class="wpvivid-rectangle wpvivid-grey-light wpvivid-hover-blue wpvivid-staging-operate wpvivid-delete-staging-site">Delete</span>
                                <?php
                                if($staging['status']['str'] === 'ready')
                                {
                                    ?>
                                    <span class="wpvivid-rectangle wpvivid-grey-light wpvivid-hover-blue wpvivid-staging-operate wpvivid-restart-staging-site">Resume</span>
                                    <span class="dashicons dashicons-editor-help wpvivid-dashicons-editor-help wpvivid-tooltip" style="margin-top: 4px;">
                                    <div class="wpvivid-bottom">
                                        <!-- The content you need -->
                                        <p>The staging site is not fully created yet due to an interruption. Click the Resume button to continue the creation.</p>
                                        <i></i> <!-- do not delete this line -->
                                    </div>
                                </span>
                                    <?php
                                }
                                ?>
                            </div>
                            <div style="clear: both;"></div>
                        </div>
                        <?php
                    }
                }
                ?>
            </div>

            <script>
                <?php
                $upload_dir = wp_upload_dir();
                $upload_path = $upload_dir['basedir'];
                $upload_path = str_replace('\\','/',$upload_path);
                $upload_path = $upload_path.'/';
                $content_dir = WP_CONTENT_DIR;
                $content_path = str_replace('\\','/',$content_dir);
                $content_path = $content_path.'/';
                $home_path = str_replace('\\','/', get_home_path());
                $theme_path = str_replace('\\','/', get_theme_root());
                $theme_path = $theme_path.'/';
                $plugin_path = str_replace('\\','/', WP_PLUGIN_DIR);
                $plugin_path = $plugin_path.'/';
                ?>
                var path_arr = {};
                path_arr['core'] = '<?php echo esc_attr($home_path); ?>';
                path_arr['content'] = '<?php echo esc_attr($content_path); ?>';
                path_arr['uploads'] = '<?php echo esc_attr($upload_path); ?>';
                path_arr['themes'] = '<?php echo esc_attr($theme_path); ?>';
                path_arr['plugins'] = '<?php echo esc_attr($plugin_path); ?>';

                var push_staging_site_id='';
                var wpvivid_ajax_lock=false;

                function wpvivid_create_standard_json(){
                    var json = {};
                    json['database_check_ex'] = '1';
                    json['folder_check_ex'] = '1';
                    json['exclude_custom'] = '0';
                    json['core_list'] = Array();
                    json['core_check'] = '0';
                    json['database_list'] = Array();
                    json['database_check'] = '1';
                    json['themes_list'] = {};
                    json['themes_check'] = '0';
                    json['themes_extension']= Array();
                    json['plugins_list'] = {};
                    json['plugins_check'] = '0';
                    json['plugins_extension']= Array();
                    json['uploads_list'] = {};
                    json['uploads_check'] = '1';
                    json['upload_extension']= Array();
                    json['content_list'] = {};
                    json['content_check'] = '0';
                    json['content_extension']= Array();
                    json['additional_file_list'] = {};
                    json['additional_file_check'] = '0';
                    json['additional_file_extension']= Array();
                    return json;
                }

                function wpvivid_lock_unlock_push_ui(action){
                    if(action === 'lock'){
                        jQuery('#wpvivid_staging_list').find('a').css({'pointer-events': 'none', 'opacity': '0.4'});
                        jQuery('#wpvivid_staging_list').find('input').attr('disabled', true);
                        jQuery('#wpvivid_staging_list').find('div.wpvivid-delete-staging-site').css({'pointer-events': 'none', 'opacity': '0.4'});
                        jQuery('#wpvivid_staging_list').find('div#wpvivid_custom_staging_site').css({'pointer-events': 'none', 'opacity': '0.4'});
                    }
                    else{
                        jQuery('#wpvivid_staging_list').find('a').css({'pointer-events': 'auto', 'opacity': '1'});
                        jQuery('#wpvivid_staging_list').find('input').attr('disabled', false);
                        jQuery('#wpvivid_staging_list').find('div.wpvivid-delete-staging-site').css({'pointer-events': 'auto', 'opacity': '1'});
                        jQuery('#wpvivid_staging_list').find('div#wpvivid_custom_staging_site').css({'pointer-events': 'auto', 'opacity': '1'});
                    }
                }

                function wpvivid_delete_staging_site_lock_unlock(id, action){
                    if(action === 'lock'){
                        jQuery('#wpvivid_staging_list').css({'pointer-events': 'none', 'opacity': '0.4'});
                    }
                    else{
                        jQuery('#wpvivid_staging_list').css({'pointer-events': 'auto', 'opacity': '1'});
                    }
                }

                function wpvivid_staging_js_fix(parent_id, is_staging, themes_path, plugins_path, uploads_path, content_path, home_path, staging_site_id){
                    var tree_path = themes_path;

                    var path_arr = {};
                    path_arr['core'] = home_path;
                    path_arr['content'] = content_path;
                    path_arr['uploads'] = uploads_path;
                    path_arr['themes'] = themes_path;
                    path_arr['plugins'] = plugins_path;

                    jQuery('#'+parent_id).on('click', '.wpvivid-handle-additional-folder-detail', function(){
                        wpvivid_init_custom_include_tree(home_path, is_staging, parent_id);
                    });

                    jQuery('#'+parent_id).on('click', '.wpvivid-refresh-include-tree', function(){
                        wpvivid_init_custom_include_tree(home_path, is_staging, parent_id, 1);
                    });

                    jQuery('#'+parent_id).on('click', '.wpvivid-handle-tree-detail', function(){
                        var value = jQuery('#'+parent_id).find('.wpvivid-custom-tree-selector').val();
                        if(value === 'themes'){
                            tree_path = themes_path;
                        }
                        else if(value === 'plugins'){
                            tree_path = plugins_path;
                        }
                        else if(value === 'content'){
                            tree_path = content_path;
                        }
                        else if(value === 'uploads'){
                            tree_path = uploads_path;
                        }
                        wpvivid_init_custom_exclude_tree(tree_path, is_staging, parent_id);
                    });

                    jQuery('#'+parent_id).on('change', '.wpvivid-custom-tree-selector', function(){
                        var value = jQuery('#'+parent_id).find('.wpvivid-custom-tree-selector').val();
                        if(value === 'themes'){
                            tree_path = themes_path;
                        }
                        else if(value === 'plugins'){
                            tree_path = plugins_path;
                        }
                        else if(value === 'content'){
                            tree_path = content_path;
                        }
                        else if(value === 'uploads'){
                            tree_path = uploads_path;
                        }
                        jQuery('#'+parent_id).find('.wpvivid-custom-exclude-tree-info').jstree("destroy").empty();
                        wpvivid_init_custom_exclude_tree(tree_path, is_staging, parent_id);
                    });

                    jQuery('#'+parent_id).on('click', '.wpvivid-refresh-exclude-tree', function(){
                        var value = jQuery('#'+parent_id).find('.wpvivid-custom-tree-selector').val();
                        if(value === 'themes'){
                            tree_path = themes_path;
                        }
                        else if(value === 'plugins'){
                            tree_path = plugins_path;
                        }
                        else if(value === 'content'){
                            tree_path = content_path;
                        }
                        else if(value === 'uploads'){
                            tree_path = uploads_path;
                        }
                        wpvivid_init_custom_exclude_tree(tree_path, is_staging, parent_id, 1);
                    });

                    jQuery('#'+parent_id).on('click', '.wpvivid-custom-tree-exclude-btn', function(){
                        var select_folders = jQuery('#'+parent_id).find('.wpvivid-custom-exclude-tree-info').jstree(true).get_selected(true);
                        var tree_type = jQuery('#'+parent_id).find('.wpvivid-custom-tree-selector').val();
                        var tree_path = path_arr[tree_type];
                        if(tree_type === 'themes'){
                            var list_obj = jQuery('#'+parent_id).find('.wpvivid-custom-exclude-themes-list');
                        }
                        else if(tree_type === 'plugins'){
                            var list_obj = jQuery('#'+parent_id).find('.wpvivid-custom-exclude-plugins-list');
                        }
                        else if(tree_type === 'content'){
                            var list_obj = jQuery('#'+parent_id).find('.wpvivid-custom-exclude-content-list');
                        }
                        else if(tree_type === 'uploads'){
                            var list_obj = jQuery('#'+parent_id).find('.wpvivid-custom-exclude-uploads-list');
                        }

                        jQuery.each(select_folders, function (index, select_item) {
                            if (select_item.id !== tree_path) {
                                var value = select_item.id;
                                value = value.replace(tree_path, '');
                                if (!wpvivid_check_tree_repeat(tree_type, value, parent_id)) {
                                    var class_name = select_item.icon;
                                    if(class_name === 'dashicons dashicons-category wpvivid-dashicons-orange wpvivid-icon-16px-nopointer'){
                                        var type = 'folder';
                                    }
                                    else{
                                        var type = 'file';
                                    }
                                    var tr = "<div class='wpvivid-text-line' type='"+type+"'>" +
                                        "<span class='dashicons dashicons-trash wpvivid-icon-16px wpvivid-remove-custom-exlcude-tree'></span>" +
                                        "<span class='"+class_name+"'></span>" +
                                        "<span class='wpvivid-text-line'>" + value + "</span>" +
                                        "</div>";
                                    list_obj.append(tr);
                                }
                            }
                        });
                    });

                    if(is_staging){
                        is_staging = '1';
                    }
                    else{
                        is_staging = '0';
                    }
                    wpvivid_get_custom_database_tables_info(parent_id, is_staging, staging_site_id);
                }

                function wpvivid_load_mu_staging_js(parent_id){
                    function wpvivid_handle_custom_open_close_ex(handle_obj, obj, parent_id){
                        if(obj.is(":hidden")) {
                            handle_obj.each(function(){
                                if(jQuery(this).hasClass('dashicons-arrow-down-alt2')){
                                    jQuery(this).removeClass('dashicons-arrow-down-alt2');
                                    jQuery(this).addClass('dashicons-arrow-up-alt2');
                                }
                            });
                            obj.show();
                        }
                        else{
                            handle_obj.each(function(){
                                if(jQuery(this).hasClass('dashicons-arrow-up-alt2')){
                                    jQuery(this).removeClass('dashicons-arrow-up-alt2');
                                    jQuery(this).addClass('dashicons-arrow-down-alt2');
                                }
                            });
                            obj.hide();
                        }
                    }

                    function wpvivid_change_custom_exclude_info(type, parent_id){
                        jQuery('#'+parent_id).find('.wpvivid-custom-exclude-module').hide();
                        if(type === 'themes'){
                            jQuery('#'+parent_id).find('.wpvivid-custom-exclude-themes-module').show();
                        }
                        else if(type === 'plugins'){
                            jQuery('#'+parent_id).find('.wpvivid-custom-exclude-plugins-module').show();
                        }
                        else if(type === 'content'){
                            jQuery('#'+parent_id).find('.wpvivid-custom-exclude-content-module').show();
                        }
                        else if(type === 'uploads'){
                            jQuery('#'+parent_id).find('.wpvivid-custom-exclude-uploads-module').show();
                        }
                    }

                    function wpvivid_check_tree_repeat(tree_type, value, parent_id) {
                        if(tree_type === 'themes'){
                            var list = 'wpvivid-custom-exclude-themes-list';
                        }
                        else if(tree_type === 'plugins'){
                            var list = 'wpvivid-custom-exclude-plugins-list';
                        }
                        else if(tree_type === 'content'){
                            var list = 'wpvivid-custom-exclude-content-list';
                        }
                        else if(tree_type === 'uploads'){
                            var list = 'wpvivid-custom-exclude-uploads-list';
                        }
                        else if(tree_type === 'additional-folder'){
                            var list = 'wpvivid-custom-include-additional-folder-list';
                        }

                        var brepeat = false;
                        jQuery('#'+parent_id).find('.'+list+' div').find('span:eq(2)').each(function (){
                            if (value === this.innerHTML) {
                                brepeat = true;
                            }
                        });
                        return brepeat;
                    }

                    jQuery('#'+parent_id).on('click', '.wpvivid-handle-database-detail', function(){
                        var handle_obj = jQuery('#'+parent_id).find('.wpvivid-handle-database-detail');
                        var obj = jQuery('#'+parent_id).find('.wpvivid-database-detail');
                        wpvivid_handle_custom_open_close_ex(handle_obj, obj, parent_id);
                    });

                    jQuery('#'+parent_id).on('click', '.wpvivid-handle-base-database-detail', function(){
                        var handle_obj = jQuery('#'+parent_id).find('.wpvivid-handle-base-database-detail');
                        var obj = jQuery('#'+parent_id).find('.wpvivid-base-database-detail');
                        wpvivid_handle_custom_open_close_ex(handle_obj, obj, parent_id);
                    });

                    jQuery('#'+parent_id).on('click', '.wpvivid-handle-file-detail', function(){
                        var handle_obj = jQuery('#'+parent_id).find('.wpvivid-handle-file-detail');
                        var obj = jQuery('#'+parent_id).find('.wpvivid-file-detail');
                        wpvivid_handle_custom_open_close_ex(handle_obj, obj, parent_id);
                    });

                    jQuery('#'+parent_id).on('click', '.wpvivid-handle-additional-folder-detail', function(){
                        var handle_obj = jQuery('#'+parent_id).find('.wpvivid-handle-additional-folder-detail');
                        var obj = jQuery('#'+parent_id).find('.wpvivid-additional-folder-detail');
                        wpvivid_handle_custom_open_close_ex(handle_obj, obj, parent_id);
                    });

                    jQuery('#'+parent_id).on('click', '.wpvivid-handle-tree-detail', function(){
                        var handle_obj = jQuery('#'+parent_id).find('.wpvivid-handle-tree-detail');
                        var obj = jQuery('#'+parent_id).find('.wpvivid-tree-detail');
                        var value = jQuery('#'+parent_id).find('.wpvivid-custom-tree-selector').val();
                        wpvivid_handle_custom_open_close_ex(handle_obj, obj, parent_id);
                    });

                    jQuery('#'+parent_id).on('change', '.wpvivid-custom-tree-selector', function(){
                        var value = jQuery('#'+parent_id).find('.wpvivid-custom-tree-selector').val();
                        jQuery('#'+parent_id).find('.wpvivid-custom-exclude-tree-info').jstree("destroy").empty();
                        wpvivid_change_custom_exclude_info(value, parent_id);
                    });

                    jQuery('#'+parent_id).on('click', '.wpvivid-include-additional-folder-btn', function(){
                        var select_folders = jQuery('#'+parent_id).find('.wpvivid-custom-additional-folder-tree-info').jstree(true).get_selected(true);
                        var tree_path = '<?php echo esc_attr($home_path); ?>';
                        var list_obj = jQuery('#'+parent_id).find('.wpvivid-custom-include-additional-folder-list');
                        var tree_type = 'additional-folder';

                        jQuery.each(select_folders, function (index, select_item) {
                            if (select_item.id !== tree_path) {
                                var value = select_item.id;
                                value = value.replace(tree_path, '');
                                if (!wpvivid_check_tree_repeat(tree_type, value, parent_id)) {
                                    var class_name = select_item.icon;
                                    if(class_name === 'dashicons dashicons-category wpvivid-dashicons-orange wpvivid-icon-16px-nopointer'){
                                        var type = 'folder';
                                    }
                                    else{
                                        var type = 'file';
                                    }
                                    var tr = "<div class='wpvivid-text-line' type='"+type+"'>" +
                                        "<span class='dashicons dashicons-trash wpvivid-icon-16px wpvivid-remove-custom-exlcude-tree'></span>" +
                                        "<span class='"+class_name+"'></span>" +
                                        "<span class='wpvivid-text-line'>" + value + "</span>" +
                                        "</div>";
                                    list_obj.append(tr);
                                }
                            }
                        });
                    });

                    jQuery('#'+parent_id).on('click', '.wpvivid-custom-tree-exclude-btn', function(){
                        var select_folders = jQuery('#'+parent_id).find('.wpvivid-custom-exclude-tree-info').jstree(true).get_selected(true);
                        var tree_type = jQuery('#'+parent_id).find('.wpvivid-custom-tree-selector').val();
                        var tree_path = path_arr[tree_type];
                        if(tree_type === 'themes'){
                            var list_obj = jQuery('#'+parent_id).find('.wpvivid-custom-exclude-themes-list');
                        }
                        else if(tree_type === 'plugins'){
                            var list_obj = jQuery('#'+parent_id).find('.wpvivid-custom-exclude-plugins-list');
                        }
                        else if(tree_type === 'content'){
                            var list_obj = jQuery('#'+parent_id).find('.wpvivid-custom-exclude-content-list');
                        }
                        else if(tree_type === 'uploads'){
                            var list_obj = jQuery('#'+parent_id).find('.wpvivid-custom-exclude-uploads-list');
                        }

                        jQuery.each(select_folders, function (index, select_item) {
                            if (select_item.id !== tree_path) {
                                var value = select_item.id;
                                value = value.replace(tree_path, '');
                                if (!wpvivid_check_tree_repeat(tree_type, value, parent_id)) {
                                    var class_name = select_item.icon;
                                    if(class_name === 'dashicons dashicons-category wpvivid-dashicons-orange wpvivid-icon-16px-nopointer'){
                                        var type = 'folder';
                                    }
                                    else{
                                        var type = 'file';
                                    }
                                    var tr = "<div class='wpvivid-text-line' type='"+type+"'>" +
                                        "<span class='dashicons dashicons-trash wpvivid-icon-16px wpvivid-remove-custom-exlcude-tree'></span>" +
                                        "<span class='"+class_name+"'></span>" +
                                        "<span class='wpvivid-text-line'>" + value + "</span>" +
                                        "</div>";
                                    list_obj.append(tr);
                                }
                            }
                        });
                    });

                    jQuery('#'+parent_id).on('click', '.wpvivid-remove-custom-exlcude-tree', function(){
                        jQuery(this).parent().remove();
                    });

                    jQuery('#'+parent_id).on('click', '.wpvivid-clear-custom-include-list', function(){
                        jQuery('#'+parent_id).find('.wpvivid-custom-include-additional-folder-list').html('');
                    });

                    jQuery('#'+parent_id).on('click', '.wpvivid-clear-custom-exclude-list', function(){
                        var tree_type = jQuery('#'+parent_id).find('.wpvivid-custom-tree-selector').val();
                        if(tree_type === 'themes'){
                            var list = 'wpvivid-custom-exclude-themes-list';
                        }
                        else if(tree_type === 'plugins'){
                            var list = 'wpvivid-custom-exclude-plugins-list';
                        }
                        else if(tree_type === 'content'){
                            var list = 'wpvivid-custom-exclude-content-list';
                        }
                        else if(tree_type === 'uploads'){
                            var list = 'wpvivid-custom-exclude-uploads-list';
                        }
                        jQuery('#'+parent_id).find('.'+list).html('');
                    });

                    jQuery('#'+parent_id).on('click', '.wpvivid-database-table-check', function(){
                        if(jQuery(this).prop('checked')){
                            if(jQuery(this).hasClass('wpvivid-database-base-table-check')){
                                jQuery('#'+parent_id).find('input:checkbox[option=base_db][name=Database]').prop('checked', true);
                            }
                            else if(jQuery(this).hasClass('wpvivid-database-other-table-check')){
                                jQuery('#'+parent_id).find('input:checkbox[option=other_db][name=Database]').prop('checked', true);
                            }
                            else if(jQuery(this).hasClass('wpvivid-database-diff-prefix-table-check')){
                                jQuery('#'+parent_id).find('input:checkbox[option=diff_prefix_db][name=Database]').prop('checked', true);
                            }
                        }
                        else{
                            var check_status = false;
                            if (jQuery(this).hasClass('wpvivid-database-base-table-check')) {
                                jQuery('#'+parent_id).find('input:checkbox[option=other_db][name=Database]').each(function(){
                                    if(jQuery(this).prop('checked')){
                                        check_status = true;
                                    }
                                });
                                jQuery('#'+parent_id).find('input:checkbox[option=diff_prefix_db][name=Database]').each(function(){
                                    if(jQuery(this).prop('checked')){
                                        check_status = true;
                                    }
                                });
                                if(check_status) {
                                    jQuery('#'+parent_id).find('input:checkbox[option=base_db][name=Database]').prop('checked', false);
                                }
                                else{
                                    jQuery(this).prop('checked', true);
                                    alert('Please select at least one table type under the Database option, or deselect the option.');
                                }
                            }
                            else if (jQuery(this).hasClass('wpvivid-database-other-table-check')) {
                                jQuery('#'+parent_id).find('input:checkbox[option=base_db][name=Database]').each(function(){
                                    if(jQuery(this).prop('checked')){
                                        check_status = true;
                                    }
                                });
                                jQuery('#'+parent_id).find('input:checkbox[option=diff_prefix_db][name=Database]').each(function(){
                                    if(jQuery(this).prop('checked')){
                                        check_status = true;
                                    }
                                });
                                if(check_status) {
                                    jQuery('#'+parent_id).find('input:checkbox[option=other_db][name=Database]').prop('checked', false);
                                }
                                else{
                                    jQuery(this).prop('checked', true);
                                    alert('Please select at least one table type under the Database option, or deselect the option.');
                                }
                            }
                            else if (jQuery(this).hasClass('wpvivid-database-diff-prefix-table-check')) {
                                jQuery('#'+parent_id).find('input:checkbox[option=base_db][name=Database]').each(function(){
                                    if(jQuery(this).prop('checked')){
                                        check_status = true;
                                    }
                                });
                                jQuery('#'+parent_id).find('input:checkbox[option=other_db][name=Database]').each(function(){
                                    if(jQuery(this).prop('checked')){
                                        check_status = true;
                                    }
                                });
                                if(check_status) {
                                    jQuery('#'+parent_id).find('input:checkbox[option=diff_prefix_db][name=Database]').prop('checked', false);
                                }
                                else{
                                    jQuery(this).prop('checked', true);
                                    alert('Please select at least one table type under the Database option, or deselect the option.');
                                }
                            }
                        }
                    });

                    jQuery('#'+parent_id).on("click", 'input:checkbox[option=base_db][name=Database]', function(){
                        if(jQuery(this).prop('checked')){
                            var all_check = true;
                            jQuery('#'+parent_id).find('input:checkbox[option=base_db][name=Database]').each(function(){
                                if(!jQuery(this).prop('checked')){
                                    all_check = false;
                                }
                            });
                            if(all_check){
                                jQuery('#'+parent_id).find('.wpvivid-database-base-table-check').prop('checked', true);
                            }
                        }
                        else{
                            var check_status = false;
                            jQuery('#'+parent_id).find('input:checkbox[name=Database]').each(function(){
                                if(jQuery(this).prop('checked')){
                                    check_status = true;
                                }
                            });
                            if(check_status){
                                jQuery('#'+parent_id).find('.wpvivid-database-base-table-check').prop('checked', false);
                            }
                            else{
                                jQuery(this).prop('checked', true);
                                alert('Please select at least one table type under the Database option, or deselect the option.');
                            }
                        }
                    });

                    jQuery('#'+parent_id).on("click", 'input:checkbox[option=other_db][name=Database]', function(){
                        if(jQuery(this).prop('checked')){
                            var all_check = true;
                            jQuery('#'+parent_id).find('input:checkbox[option=other_db][name=Database]').each(function(){
                                if(!jQuery(this).prop('checked')){
                                    all_check = false;
                                }
                            });
                            if(all_check){
                                jQuery('#'+parent_id).find('.wpvivid-database-other-table-check').prop('checked', true);
                            }
                        }
                        else{
                            var check_status = false;
                            jQuery('#'+parent_id).find('input:checkbox[name=Database]').each(function(){
                                if(jQuery(this).prop('checked')){
                                    check_status = true;
                                }
                            });
                            if(check_status){
                                jQuery('#'+parent_id).find('.wpvivid-database-other-table-check').prop('checked', false);
                            }
                            else{
                                jQuery(this).prop('checked', true);
                                alert('Please select at least one table type under the Database option, or deselect the option.');
                            }
                        }
                    });

                    jQuery('#'+parent_id).on("click", 'input:checkbox[option=diff_prefix_db][name=Database]', function(){
                        if(jQuery(this).prop('checked')){
                            var all_check = true;
                            jQuery('#'+parent_id).find('input:checkbox[option=diff_prefix_db][name=Database]').each(function(){
                                if(!jQuery(this).prop('checked')){
                                    all_check = false;
                                }
                            });
                            if(all_check){
                                jQuery('#'+parent_id).find('.wpvivid-database-diff-prefix-table-check').prop('checked', true);
                            }
                        }
                        else{
                            var check_status = false;
                            jQuery('#'+parent_id).find('input:checkbox[name=Database]').each(function(){
                                if(jQuery(this).prop('checked')){
                                    check_status = true;
                                }
                            });
                            if(check_status){
                                jQuery('#'+parent_id).find('.wpvivid-database-diff-prefix-table-check').prop('checked', false);
                            }
                            else{
                                jQuery(this).prop('checked', true);
                                alert('Please select at least one table type under the Database option, or deselect the option.');
                            }
                        }
                    });

                    jQuery('#'+parent_id).on("click", '.wpvivid-custom-database-part', function(){
                        if(jQuery(this).prop('checked')){
                            jQuery('#'+parent_id).find('.wpvivid-custom-database-check').prop('checked', true);
                        }
                        else{
                            var check_status = false;
                            if(jQuery('#'+parent_id).find('.wpvivid-custom-file-part').prop('checked')){
                                check_status = true;
                            }
                            if(check_status){
                                jQuery('#'+parent_id).find('.wpvivid-custom-database-check').prop('checked', false);
                            }
                            else{
                                jQuery(this).prop('checked', true);
                                alert('Please select at least one item under Custom Backup option.');
                            }
                        }
                    });

                    jQuery('#'+parent_id).on("click", '.wpvivid-custom-database-check', function(){
                        if(jQuery(this).prop('checked')){
                            jQuery('#'+parent_id).find('.wpvivid-custom-database-part').prop('checked', true);
                        }
                        else{
                            var check_status = false;
                            if(jQuery('#'+parent_id).find('.wpvivid-custom-file-part').prop('checked')){
                                check_status = true;
                            }
                            if(check_status){
                                jQuery('#'+parent_id).find('.wpvivid-custom-database-part').prop('checked', false);
                            }
                            else{
                                jQuery(this).prop('checked', true);
                                alert('Please select at least one item under Custom Backup option.');
                            }
                        }
                    });

                    jQuery('#'+parent_id).on("click", '.wpvivid-custom-file-part', function(){
                        if(jQuery(this).prop('checked')){
                            jQuery('#'+parent_id).find('.wpvivid-custom-core-check').prop('checked', true);
                            jQuery('#'+parent_id).find('.wpvivid-custom-themes-check').prop('checked', true);
                            jQuery('#'+parent_id).find('.wpvivid-custom-plugins-check').prop('checked', true);
                            jQuery('#'+parent_id).find('.wpvivid-custom-content-check').prop('checked', true);
                            jQuery('#'+parent_id).find('.wpvivid-custom-uploads-check').prop('checked', true);
                            jQuery('#'+parent_id).find('.wpvivid-custom-additional-folder-check').prop('checked', true);
                        }
                        else{
                            var check_status = false;
                            if(jQuery('#'+parent_id).find('.wpvivid-custom-database-part').prop('checked')){
                                check_status = true;
                            }
                            if(check_status){
                                jQuery('#'+parent_id).find('.wpvivid-custom-core-check').prop('checked', false);
                                jQuery('#'+parent_id).find('.wpvivid-custom-themes-check').prop('checked', false);
                                jQuery('#'+parent_id).find('.wpvivid-custom-plugins-check').prop('checked', false);
                                jQuery('#'+parent_id).find('.wpvivid-custom-content-check').prop('checked', false);
                                jQuery('#'+parent_id).find('.wpvivid-custom-uploads-check').prop('checked', false);
                                jQuery('#'+parent_id).find('.wpvivid-custom-additional-folder-check').prop('checked', false);
                            }
                            else{
                                jQuery(this).prop('checked', true);
                                alert('Please select at least one item under Custom Backup option.');
                            }
                        }
                    });

                    //core
                    jQuery('#'+parent_id).on("click", '.wpvivid-custom-core-check', function(){
                        if(jQuery(this).prop('checked')){
                            jQuery('#'+parent_id).find('.wpvivid-custom-file-part').prop('checked', true);
                        }
                        else{
                            var check_status = false;
                            if(jQuery('#'+parent_id).find('.wpvivid-custom-database-part').prop('checked')){
                                check_status = true;
                            }
                            if(check_status){
                                if(!jQuery('#'+parent_id).find('.wpvivid-custom-themes-check').prop('checked') &&
                                    !jQuery('#'+parent_id).find('.wpvivid-custom-plugins-check').prop('checked') &&
                                    !jQuery('#'+parent_id).find('.wpvivid-custom-content-check').prop('checked') &&
                                    !jQuery('#'+parent_id).find('.wpvivid-custom-uploads-check').prop('checked') &&
                                    !jQuery('#'+parent_id).find('.wpvivid-custom-additional-folder-check').prop('checked')){
                                    jQuery('#'+parent_id).find('.wpvivid-custom-file-part').prop('checked', false);
                                }
                            }
                            else{
                                if(!jQuery('#'+parent_id).find('.wpvivid-custom-themes-check').prop('checked') &&
                                    !jQuery('#'+parent_id).find('.wpvivid-custom-plugins-check').prop('checked') &&
                                    !jQuery('#'+parent_id).find('.wpvivid-custom-content-check').prop('checked') &&
                                    !jQuery('#'+parent_id).find('.wpvivid-custom-uploads-check').prop('checked') &&
                                    !jQuery('#'+parent_id).find('.wpvivid-custom-additional-folder-check').prop('checked')){
                                    jQuery(this).prop('checked', true);
                                    alert('Please select at least one item under Custom Backup option.');
                                }
                            }
                        }
                    });

                    //themes
                    jQuery('#'+parent_id).on("click", '.wpvivid-custom-themes-check', function(){
                        if(jQuery(this).prop('checked')){
                            jQuery('#'+parent_id).find('.wpvivid-custom-file-part').prop('checked', true);
                        }
                        else{
                            var check_status = false;
                            if(jQuery('#'+parent_id).find('.wpvivid-custom-database-part').prop('checked')){
                                check_status = true;
                            }
                            if(check_status){
                                if(!jQuery('#'+parent_id).find('.wpvivid-custom-core-check').prop('checked') &&
                                    !jQuery('#'+parent_id).find('.wpvivid-custom-plugins-check').prop('checked') &&
                                    !jQuery('#'+parent_id).find('.wpvivid-custom-content-check').prop('checked') &&
                                    !jQuery('#'+parent_id).find('.wpvivid-custom-uploads-check').prop('checked') &&
                                    !jQuery('#'+parent_id).find('.wpvivid-custom-additional-folder-check').prop('checked')){
                                    jQuery('#'+parent_id).find('.wpvivid-custom-file-part').prop('checked', false);
                                }
                            }
                            else{
                                if(!jQuery('#'+parent_id).find('.wpvivid-custom-core-check').prop('checked') &&
                                    !jQuery('#'+parent_id).find('.wpvivid-custom-plugins-check').prop('checked') &&
                                    !jQuery('#'+parent_id).find('.wpvivid-custom-content-check').prop('checked') &&
                                    !jQuery('#'+parent_id).find('.wpvivid-custom-uploads-check').prop('checked') &&
                                    !jQuery('#'+parent_id).find('.wpvivid-custom-additional-folder-check').prop('checked')){
                                    jQuery(this).prop('checked', true);
                                    alert('Please select at least one item under Custom Backup option.');
                                }
                            }
                        }
                    });

                    //plugins
                    jQuery('#'+parent_id).on("click", '.wpvivid-custom-plugins-check', function(){
                        if(jQuery(this).prop('checked')){
                            jQuery('#'+parent_id).find('.wpvivid-custom-file-part').prop('checked', true);
                        }
                        else{
                            var check_status = false;
                            if(jQuery('#'+parent_id).find('.wpvivid-custom-database-part').prop('checked')){
                                check_status = true;
                            }
                            if(check_status){
                                if(!jQuery('#'+parent_id).find('.wpvivid-custom-themes-check').prop('checked') &&
                                    !jQuery('#'+parent_id).find('.wpvivid-custom-core-check').prop('checked') &&
                                    !jQuery('#'+parent_id).find('.wpvivid-custom-content-check').prop('checked') &&
                                    !jQuery('#'+parent_id).find('.wpvivid-custom-uploads-check').prop('checked') &&
                                    !jQuery('#'+parent_id).find('.wpvivid-custom-additional-folder-check').prop('checked')){
                                    jQuery('#'+parent_id).find('.wpvivid-custom-file-part').prop('checked', false);
                                }
                            }
                            else{
                                if(!jQuery('#'+parent_id).find('.wpvivid-custom-themes-check').prop('checked') &&
                                    !jQuery('#'+parent_id).find('.wpvivid-custom-core-check').prop('checked') &&
                                    !jQuery('#'+parent_id).find('.wpvivid-custom-content-check').prop('checked') &&
                                    !jQuery('#'+parent_id).find('.wpvivid-custom-uploads-check').prop('checked') &&
                                    !jQuery('#'+parent_id).find('.wpvivid-custom-additional-folder-check').prop('checked')){
                                    jQuery(this).prop('checked', true);
                                    alert('Please select at least one item under Custom Backup option.');
                                }
                            }
                        }
                    });

                    //content
                    jQuery('#'+parent_id).on("click", '.wpvivid-custom-content-check', function(){
                        if(jQuery(this).prop('checked')){
                            jQuery('#'+parent_id).find('.wpvivid-custom-file-part').prop('checked', true);
                        }
                        else{
                            var check_status = false;
                            if(jQuery('#'+parent_id).find('.wpvivid-custom-database-part').prop('checked')){
                                check_status = true;
                            }
                            if(check_status){
                                if(!jQuery('#'+parent_id).find('.wpvivid-custom-themes-check').prop('checked') &&
                                    !jQuery('#'+parent_id).find('.wpvivid-custom-plugins-check').prop('checked') &&
                                    !jQuery('#'+parent_id).find('.wpvivid-custom-core-check').prop('checked') &&
                                    !jQuery('#'+parent_id).find('.wpvivid-custom-uploads-check').prop('checked') &&
                                    !jQuery('#'+parent_id).find('.wpvivid-custom-additional-folder-check').prop('checked')){
                                    jQuery('#'+parent_id).find('.wpvivid-custom-file-part').prop('checked', false);
                                }
                            }
                            else{
                                if(!jQuery('#'+parent_id).find('.wpvivid-custom-themes-check').prop('checked') &&
                                    !jQuery('#'+parent_id).find('.wpvivid-custom-plugins-check').prop('checked') &&
                                    !jQuery('#'+parent_id).find('.wpvivid-custom-core-check').prop('checked') &&
                                    !jQuery('#'+parent_id).find('.wpvivid-custom-uploads-check').prop('checked') &&
                                    !jQuery('#'+parent_id).find('.wpvivid-custom-additional-folder-check').prop('checked')){
                                    jQuery(this).prop('checked', true);
                                    alert('Please select at least one item under Custom Backup option.');
                                }
                            }
                        }
                    });

                    //uploads
                    jQuery('#'+parent_id).on("click", '.wpvivid-custom-uploads-check', function(){
                        if(jQuery(this).prop('checked')){
                            jQuery('#'+parent_id).find('.wpvivid-custom-file-part').prop('checked', true);
                        }
                        else{
                            var check_status = false;
                            if(jQuery('#'+parent_id).find('.wpvivid-custom-database-part').prop('checked')){
                                check_status = true;
                            }
                            if(check_status){
                                if(!jQuery('#'+parent_id).find('.wpvivid-custom-themes-check').prop('checked') &&
                                    !jQuery('#'+parent_id).find('.wpvivid-custom-plugins-check').prop('checked') &&
                                    !jQuery('#'+parent_id).find('.wpvivid-custom-content-check').prop('checked') &&
                                    !jQuery('#'+parent_id).find('.wpvivid-custom-core-check').prop('checked') &&
                                    !jQuery('#'+parent_id).find('.wpvivid-custom-additional-folder-check').prop('checked')){
                                    jQuery('#'+parent_id).find('.wpvivid-custom-file-part').prop('checked', false);
                                }
                            }
                            else{
                                if(!jQuery('#'+parent_id).find('.wpvivid-custom-themes-check').prop('checked') &&
                                    !jQuery('#'+parent_id).find('.wpvivid-custom-plugins-check').prop('checked') &&
                                    !jQuery('#'+parent_id).find('.wpvivid-custom-content-check').prop('checked') &&
                                    !jQuery('#'+parent_id).find('.wpvivid-custom-core-check').prop('checked') &&
                                    !jQuery('#'+parent_id).find('.wpvivid-custom-additional-folder-check').prop('checked')){
                                    jQuery(this).prop('checked', true);
                                    alert('Please select at least one item under Custom Backup option.');
                                }
                            }
                        }
                    });

                    //additional_folder
                    jQuery('#'+parent_id).on("click", '.wpvivid-custom-additional-folder-check', function(){
                        if(jQuery(this).prop('checked')){
                            jQuery('#'+parent_id).find('.wpvivid-custom-file-part').prop('checked', true);
                        }
                        else{
                            var check_status = false;
                            if(jQuery('#'+parent_id).find('.wpvivid-custom-database-part').prop('checked')){
                                check_status = true;
                            }
                            if(check_status){
                                if(!jQuery('#'+parent_id).find('.wpvivid-custom-themes-check').prop('checked') &&
                                    !jQuery('#'+parent_id).find('.wpvivid-custom-plugins-check').prop('checked') &&
                                    !jQuery('#'+parent_id).find('.wpvivid-custom-content-check').prop('checked') &&
                                    !jQuery('#'+parent_id).find('.wpvivid-custom-uploads-check').prop('checked') &&
                                    !jQuery('#'+parent_id).find('.wpvivid-custom-core-check').prop('checked')){
                                    jQuery('#'+parent_id).find('.wpvivid-custom-file-part').prop('checked', false);
                                }
                            }
                            else{
                                if(!jQuery('#'+parent_id).find('.wpvivid-custom-themes-check').prop('checked') &&
                                    !jQuery('#'+parent_id).find('.wpvivid-custom-plugins-check').prop('checked') &&
                                    !jQuery('#'+parent_id).find('.wpvivid-custom-content-check').prop('checked') &&
                                    !jQuery('#'+parent_id).find('.wpvivid-custom-uploads-check').prop('checked') &&
                                    !jQuery('#'+parent_id).find('.wpvivid-custom-core-check').prop('checked')){
                                    jQuery(this).prop('checked', true);
                                    alert('Please select at least one item under Custom Backup option.');
                                }
                            }
                        }
                    });
                }

                function wpvivid_get_mu_site_info(id,copy){
                    var ajax_data = {
                        'action':'wpvividstg_get_mu_site_info_free',
                        'id': id,
                        'copy':copy
                    };
                    wpvivid_lock_unlock_push_ui('lock');
                    wpvivid_post_request(ajax_data, function(data){
                        wpvivid_lock_unlock_push_ui('unlock');
                        var jsonarray = jQuery.parseJSON(data);
                        if (jsonarray.result === 'success') {
                            push_staging_site_id=id;
                            jQuery('#wpvividstg_select_mu_staging_site').html(jsonarray.html);
                            jQuery('#'+id).find('.wpvivid-push-content').after(jQuery('#wpvividstg_select_mu_staging_site'));
                            jQuery('#wpvividstg_select_mu_staging_site').show();
                            wpvivid_load_mu_staging_js('wpvivid_custom_mu_staging_site');
                            if(copy == 'true' || copy == true){
                                //wpvivid_load_staging_tree('wpvivid_custom_mu_staging_site', true);
                                wpvivid_staging_js_fix('wpvivid_custom_mu_staging_site', true, jsonarray.theme_path, jsonarray.plugin_path, jsonarray.uploads_path, jsonarray.content_path, jsonarray.home_path, id);
                            }
                            else{
                                //wpvivid_load_staging_tree('wpvivid_custom_mu_staging_site', false);
                                wpvivid_staging_js_fix('wpvivid_custom_mu_staging_site', false, jsonarray.theme_path, jsonarray.plugin_path, jsonarray.uploads_path, jsonarray.content_path, jsonarray.home_path, id);
                            }
                            jQuery('#wpvivid_mu_copy_staging_site_list').find('input:checkbox').each(function(){
                                jQuery(this).prop('checked', true);
                            });
                        }
                        else if (jsonarray.result === 'failed') {
                            alert(jsonarray.error);
                        }

                        jQuery('#wpvivid_staging_list').find('.wpvivid-copy-staging-to-live-block').each(function() {
                            var tmp_id = jQuery(this).attr('name');
                            if(id !== tmp_id) {
                                if(jQuery(this).hasClass('staging-site')){
                                    var class_btn = 'staging-site';
                                    var copy_btn = 'Copy the Staging Site to Live(pro feature)';
                                    var update_btn = 'Update the Staging Site(pro feature)';
                                    var tip_text = 'Tips: Click the \'Copy the Staging Site to Live\' button above to migrate the staging site to your live site. Click the \'Update the Staging Site\' button to update the live site to the staging site.';
                                }
                                else{
                                    var class_btn = 'fresh-install';
                                    var copy_btn = 'Copy the Fresh Install to Live(pro feature)';
                                    var update_btn = 'Update the Fresh Install(pro feature)';
                                    var tip_text = 'Tips: Click the \'Copy the Fresh Install to Live\' button above to migrate the fresh install to your live site. Click the \'Update the Fresh Install\' button to update the live site to the fresh install.';
                                }

                                if(jQuery(this).hasClass('mu-single')){
                                    var mu_single_class = 'mu-single';
                                }
                                else{
                                    var mu_single_class = '';
                                }

                                var tmp_html = '<span class="wpvivid-rectangle wpvivid-grey-light wpvivid-hover-blue wpvivid-staging-operate wpvivid-update-live-to-staging">Update the Staging Site(pro feature)</span>' +
                                    '<span class="wpvivid-rectangle wpvivid-grey-light wpvivid-hover-blue wpvivid-staging-operate wpvivid-copy-staging-to-live">Copy the Staging Site to Live(pro feature)</span>' +
                                    '<span class="wpvivid-rectangle wpvivid-grey-light wpvivid-hover-blue wpvivid-staging-operate wpvivid-delete-staging-site">Delete</span>';
                                jQuery(this).html(tmp_html);
                            }
                        });
                    }, function(XMLHttpRequest, textStatus, errorThrown)
                    {
                        wpvivid_lock_unlock_push_ui('unlock');
                        var error_message = wpvivid_output_ajaxerror('export the previously-exported settings', textStatus, errorThrown);
                        alert(error_message);
                    });
                }

                jQuery('#wpvivid_switch_create_staging_page').click(function(){
                    switch_staging_tab('create_staging');
                });

                jQuery('#wpvivid_switch_create_fresh_install_page').click(function(){
                    switch_staging_tab('create_fresh_install');
                });

                jQuery('#wpvivid_staging_list').on("click", '.wpvivid-delete-staging-site', function(){
                    var descript = '<?php esc_html_e('Are you sure to delete this staging site?', 'wpvivid-backuprestore'); ?>';
                    var ret = confirm(descript);
                    if (ret === true) {
                        var id = jQuery(this).parent().attr('name');
                        var ajax_data = {
                            'action': 'wpvividstg_delete_site_free',
                            'id': id
                        };
                        wpvivid_delete_staging_site_lock_unlock(id, 'lock');
                        wpvivid_post_request(ajax_data, function (data) {
                            wpvivid_delete_staging_site_lock_unlock(id, 'unlock');
                            var jsonarray = jQuery.parseJSON(data);
                            if (jsonarray.result === 'success') {
                                location.href='<?php echo esc_url(apply_filters('wpvividstg_get_admin_url', '') ). 'admin.php?page=wpvivid-staging'; ?>';
                            }
                            else if (jsonarray.result === 'failed') {
                                alert(jsonarray.error);
                            }
                        }, function (XMLHttpRequest, textStatus, errorThrown) {
                            wpvivid_delete_staging_site_lock_unlock(id, 'unlock');
                            var error_message = wpvivid_output_ajaxerror('export the previously-exported settings', textStatus, errorThrown);
                            alert(error_message);
                        });
                    }
                });

                jQuery('#wpvivid_staging_list').on("click", '.wpvivid-restart-staging-site', function(){
                    var descript = '<?php esc_html_e('Are you sure to restart this staging site?', 'wpvivid-backuprestore'); ?>';
                    var ret = confirm(descript);
                    if (ret === true) {
                        var id = jQuery(this).parent().attr('name');
                        var ajax_data = {
                            'action':'wpvividstg_set_restart_staging_id_free',
                            'id': id
                        };
                        wpvivid_post_request(ajax_data, function (data) {
                            var jsonarray = jQuery.parseJSON(data);
                            if (jsonarray.result === 'success') {
                                jQuery('#wpvivid_choose_staging_content').hide();
                                jQuery('#wpvivid_create_btn').hide();
                                jQuery('#wpvivid_create_staging_step2').show();
                                switch_staging_tab('create_staging');
                                wpvivid_restart_staging();
                            }
                            else if (jsonarray.result === 'failed') {
                                alert(jsonarray.error);
                            }
                        }, function (XMLHttpRequest, textStatus, errorThrown) {
                            var error_message = wpvivid_output_ajaxerror('setting restart staging id', textStatus, errorThrown);
                            alert(error_message);
                        });
                    }
                });
            </script>
        </div>
        <?php
    }

    public function output_staging(){
        $data=$this->get_staging_site_data();
        $data['live_site_staging_url'] = str_replace('wpvivid-staging', 'WPvivid_Staging', $data['live_site_staging_url']);
        $live_site_url = $data['live_site_url'];
        $push_site_url = $data['live_site_staging_url'];
        ?>
        <div class="wpvivid-one-coloum" style="border:1px solid #f1f1f1;padding-top:0em;">
            <div class="wpvivid-two-col">
                <p><span class="dashicons dashicons-awards wpvivid-dashicons-blue"></span><span><strong>Site Name: </strong></span><span><?php echo esc_html(basename(get_home_path())); ?></span></p>
                <p><span class="dashicons dashicons-admin-home wpvivid-dashicons-blue"></span><span><strong>Live Site URL: </strong></span><span><?php echo esc_url($live_site_url); ?></span></p>
                <p><span class="dashicons dashicons-rest-api wpvivid-dashicons-blue"></span><span><strong>Live Site Staging: </strong></span><span><?php echo esc_url($push_site_url); ?></span></p>
            </div>

            <div class="wpvivid-two-col">
                <p><span class="dashicons dashicons-admin-site-alt3 wpvivid-dashicons-blue"></span><span><strong>Database Name: </strong></span><span><?php echo esc_html(DB_NAME); ?></span></p>
                <p><span class="dashicons dashicons-list-view wpvivid-dashicons-blue"></span><span><strong>Table Prefix: </strong></span><span><?php echo esc_html($data['prefix']); ?></span></p>
                <p><span class="dashicons dashicons-portfolio wpvivid-dashicons-blue"></span><span><strong>Directory: </strong></span><span><?php echo esc_html(get_home_path()); ?></span></p>
            </div>
            <div style="clear: both;"></div>
        </div>
        <?php
    }
}includes/staging/class-wpvivid-fresh-install-create-ui-display.php000064400000145045151327705670021430 0ustar00<?php
if (!defined('WPVIVID_PLUGIN_DIR'))
{
    die;
}

class WPvivid_Fresh_Install_Create_UI_Display_Free
{
    public function __construct()
    {

    }

    public function output_themes_plugins_info($type)
    {
        $html = '';
        if($type === 'theme')
        {
            $themes_path = get_theme_root();
            $has_themes = false;
            $themes_table = '';
            $themes_table_html = '';
            $themes_info = array();

            $themes = wp_get_themes();

            if (!empty($themes))
            {
                $has_themes = true;
            }
            foreach ($themes as $theme)
            {
                $file = $theme->get_stylesheet();
                $parent=$theme->parent();

                $themes_info[$file] = $this->get_theme_plugin_info($themes_path . DIRECTORY_SEPARATOR . $file);
                $themes_info[$file]['parent']=$parent;
                $themes_info[$file]['parent_file']=$theme->get_template();
                $themes_info[$file]['child']=array();
                $current_theme=wp_get_theme();
                if($current_theme->get_stylesheet()==$file)
                {
                    $themes_info[$file]['active'] = 1;
                }
                else
                {
                    $themes_info[$file]['active'] = 0;
                }
            }

            foreach ($themes_info as $file => $info)
            {
                if($info['active']&&$info['parent']!=false)
                {
                    $themes_info[$info['parent_file']]['active']=1;
                    $themes_info[$info['parent_file']]['child'][]=$file;
                }
            }

            $themes_all_check = 'checked';
            foreach ($themes_info as $file => $info)
            {
                $checked = '';

                if ($info['active'] == 1)
                {
                    $checked = 'checked';
                }
                if (empty($checked)) {
                    $themes_all_check = '';
                }

                echo '<div class="wpvivid-text-line"><input type="checkbox" option="create_wp" name="Themes" value="' . esc_attr($file) . '" '. esc_html($checked) .'>' . esc_html($file) . '</div>';
            }
        }
        else{
            $has_plugins = false;
            $plugins_table = '';
            $plugins_table_html = '';
            $path = WP_PLUGIN_DIR;
            $plugin_info = array();

            if (!function_exists('get_plugins'))
                require_once(ABSPATH . 'wp-admin/includes/plugin.php');
            $plugins = get_plugins();

            if (!empty($plugins))
            {
                $has_plugins = true;
            }
            foreach ($plugins as $key => $plugin)
            {
                $slug = dirname($key);
                if ($slug == '.')
                    continue;
                $plugin_info[$slug] = $this->get_theme_plugin_info($path . DIRECTORY_SEPARATOR . $slug);
                $plugin_info[$slug]['Name'] = $plugin['Name'];
                $plugin_info[$slug]['slug'] = $slug;
                if($slug=='wpvivid-backuprestore')
                {
                    $plugin_info[$slug]['active'] = 1;
                    $plugin_info[$slug]['disable'] = 1;
                }
                else
                {
                    $plugin_info[$slug]['active'] = 0;
                    $plugin_info[$slug]['disable'] = 0;
                }

            }

            $plugins_all_check='checked';

            foreach ($plugin_info as $slug => $info)
            {
                $disable_check = '';
                if ($info['disable']==1)
                {
                    $disable_check = 'disabled';
                }
                $checked = '';

                if ($info['active'] == 1)
                {
                    $checked = 'checked';
                }

                if (empty($checked)) {
                    $plugins_all_check = '';
                }

                echo '<div class="wpvivid-text-line"><input type="checkbox" option="create_wp" name="Plugins" value="' . esc_attr($info['slug']) . '" '. esc_html($checked) .'>' . esc_html($info['Name']) . '</div>';
            }
        }
    }

    public function get_theme_plugin_info($root)
    {
        //$theme_info['size']=$this->get_folder_size($root,0);
        $theme_info['size']=0;
        return $theme_info;
    }

    public function get_folder_size($root,$size)
    {
        $count = 0;
        if(is_dir($root))
        {
            $handler = opendir($root);
            if($handler!==false)
            {
                while (($filename = readdir($handler)) !== false)
                {
                    if ($filename != "." && $filename != "..") {
                        $count++;

                        if (is_dir($root . DIRECTORY_SEPARATOR . $filename))
                        {
                            $size=$this->get_folder_size($root . DIRECTORY_SEPARATOR . $filename,$size);
                        } else {
                            $size+=filesize($root . DIRECTORY_SEPARATOR . $filename);
                        }
                    }
                }
                if($handler)
                    @closedir($handler);
            }

        }
        return $size;
    }

    public function get_database_home_url()
    {
        $home_url = home_url();
        global $wpdb;
        $home_url_sql = $wpdb->get_results( $wpdb->prepare( "SELECT * FROM $wpdb->options WHERE option_name = %s", 'home' ) );
        foreach ( $home_url_sql as $home ){
            $home_url = $home->option_value;
        }
        return untrailingslashit($home_url);
    }

    public function output_create_wp_page()
    {
        $options=get_option('wpvivid_staging_options',array());
        if(isset( $options['staging_request_timeout']))
        {
            $request_timeout=$options['staging_request_timeout'];
        }
        else
        {
            $request_timeout=1500;
        }

        update_option('wpvivid_current_running_staging_task','','no');
        update_option('wpvivid_staging_task_cancel', false,'no');
        $home_url   = $this->get_database_home_url();
        $admin_url  = admin_url();
        $admin_name = basename($admin_url);
        $admin_name = trim($admin_name, '/');

        $home_path = get_home_path();
        $staging_num = 1;
        $staging_dir = 'myfreshinstall01';
        $staging_content_dir = 'myfreshinstall01';
        $default_fresh_install_site = 'myfreshinstall01';
        while(1){
            $default_fresh_install_site = 'myfreshinstall'.sprintf("%02d", $staging_num);
            $staging_dir = $home_path.$default_fresh_install_site;
            if(!file_exists($staging_dir)){
                break;
            }
            $staging_num++;
        }

        $content_dir = WP_CONTENT_DIR;
        $content_dir = str_replace('\\','/',$content_dir);
        $content_path = $content_dir.'/';
        $staging_num = 1;
        $default_content_fresh_install_site='myfreshinstall01';
        while(1){
            $default_content_fresh_install_site = 'myfreshinstall'.sprintf("%02d", $staging_num);
            $staging_dir = $content_path.$default_content_fresh_install_site;
            if(!file_exists($staging_dir)){
                break;
            }
            $staging_num++;
        }

        global $wpdb;
        $prefix='';
        $site_id=1;
        $base_prefix=$wpdb->base_prefix;
        while(1)
        {
            if($site_id<10)
            {
                $prefix='wpvividfresh0'.$site_id.'_';
            }
            else
            {
                $prefix='wpvividfresh'.$site_id.'_';
            }

            $sql=$wpdb->prepare("SHOW TABLES LIKE %s;", $wpdb->esc_like($prefix) . '%');
            $result = $wpdb->get_results($sql, OBJECT_K);
            if(empty($result))
            {
                break;
            }
            $site_id++;
        }
        $themes_plugins_descript = 'The activated plugins and themes will be copied to a fresh site by default. A Child theme must be copied if it exists.';
        ?>
        <div class="postbox quickstaging">
            <div id="wpvivid_create_new_wp_content">
                <div class="wpvivid-one-coloum" style="border:1px solid #f1f1f1;padding-bottom:0em; margin-top:0em;margin-bottom:1em;">
                    <div class="wpvivid-one-coloum" style="background:#f5f5f5;padding-top:0em;padding-bottom:0em;display: none;">
                        <div class="wpvivid-two-col">
                            <p><span class="dashicons dashicons-awards wpvivid-dashicons-blue"></span><span><strong>Site Name: </strong></span><span class="wpvivid-fresh-install-staging-site-name"><?php echo esc_html($default_fresh_install_site); ?></span></p>
                            <p><span class="dashicons dashicons-admin-site-alt3 wpvivid-dashicons-blue"></span><span><strong>Database Name: </strong></span><span class="wpvivid-staging-additional-database-name-display"><?php echo esc_html(DB_NAME); ?></span></p>
                            <p><span class="dashicons dashicons-list-view wpvivid-dashicons-blue"></span><span><strong>Table Prefix: </strong></span><span class="wpvivid-staging-table-prefix-display"><?php echo esc_html($prefix); ?></span></p>
                        </div>
                        <div class="wpvivid-two-col">
                            <!--<p><span class="dashicons dashicons-admin-site-alt3 wpvivid-dashicons-blue"></span><span><strong>Database Name:</strong></span><span>admin06</span></p>-->
                            <p><span class="dashicons dashicons-admin-home wpvivid-dashicons-blue"></span><span><strong>Home URL: </strong></span><span class="wpvivid-fresh-install-home-url"><?php echo esc_url($home_url); ?>/</span><span class="wpvivid-fresh-install-staging-site-name"><?php echo esc_html($default_fresh_install_site); ?></span></p>
                            <p><span class="dashicons  dashicons-rest-api wpvivid-dashicons-blue"></span><span><strong>Admin URL: </strong></span><span class="wpvivid-fresh-install-home-url"><?php echo esc_url($home_url); ?>/</span><span class="wpvivid-fresh-install-staging-site-name"><?php echo esc_html($default_fresh_install_site); ?></span><span>/<?php echo esc_html($admin_name); ?></span></p>
                        </div>
                    </div>

                    <div>
                        <div>
                            <h2 style="padding-left:1em;padding-top:0.6em; background:#f1f1f1;">
                                <span class="dashicons dashicons-portfolio wpvivid-dashicons-orange"></span>
                                <span>Directory to Install the Fresh Install</span>
                            </h2>
                            <?php
                            $server_type = $_SERVER['SERVER_SOFTWARE'];
                            if(preg_match('/nginx/i', $server_type))
                            {
                                ?>
                                <div style="border:1px solid #ccc; padding:0 1em;margin-top:1em; border-radius:0.5em;">
                                    <p>
                                        <span>We detected that your web server is Nginx, please add specific rewrite rules to the Nginx config file for the staging site working properly. <a href="https://docs.wpvivid.com/add-rewrite-rules-to-nginx.html">How to</a></span>
                                    <p>
                                    <div style="clear:both;"></div>
                                </div>
                                <?php
                            }
                            ?>
                            <p>
                                <label>
                                    <input type="radio" option="create_wp" name="choose_create_staging_dir" value="0" checked="checked">
                                    <span>website root</span>
                                </label>
                                <label>
                                    <input type="radio" option="create_wp" name="choose_create_staging_dir" value="1">
                                    <span>/wp-content/</span>
                                </label>
                                <label>
                                    <input type="radio" option="create_wp" name="choose_create_staging_dir" value="2" disabled>
                                    <span>subdomain(pro feature)</span>
                                </label>
                            </p>

                            <div id="wpvivid_fresh_install_path_part" style="border-left: 4px solid #007cba;padding-left:1em;">
                                <p>
                                    <input type="text" option="create_wp" name="path" id="wpvivid_fresh_install_staging_path" placeholder="<?php echo esc_attr($default_fresh_install_site); ?>" value="<?php echo esc_attr($default_fresh_install_site); ?>" onkeyup="value=value.replace(/[^a-zA-Z0-9]/g,'')" onpaste="value=value.replace(/[^\a-\z\A-\Z0-9]/g,'')"><span> Custom directory</span>
                                </p>
                                <p>
                                    <span class="dashicons dashicons-admin-home wpvivid-dashicons-blue"></span><span>Home Url: </span><span class="wpvivid-fresh-install-home-url"><?php echo esc_url($home_url); ?>/</span><span class="wpvivid-fresh-install-staging-site-name"><?php echo esc_html($default_fresh_install_site); ?></span>
                                    <span style="margin-left:1em;" class="dashicons dashicons-portfolio wpvivid-dashicons-blue"></span><span><strong>Directory:</strong></span>
                                    <span><?php echo esc_html(untrailingslashit(ABSPATH)); ?>/</span><span class="wpvivid-fresh-install-staging-site-name"><?php echo esc_html($default_fresh_install_site); ?></span>
                                </p>
                            </div>
                        </div>

                        <h2 style="padding-left:1em;padding-top:0.6em;background:#f1f1f1;">
                            <span class="dashicons dashicons-cloud wpvivid-dashicons-blue"></span>
                            <span>Choose Database to Install the Fresh Install</span>
                        </h2>
                        <p>
                            <input type="text" option="create_wp" name="prefix" id="wpvivid_fresh_install_staging_table_prefix" placeholder="<?php echo esc_attr($prefix); ?>" value="<?php echo esc_attr($prefix); ?>" onkeyup="value=value.replace(/[^a-zA-Z0-9_]/g,'')" onpaste="value=value.replace(/[^\a-\z\A-\Z0-9_]/g,'')" title="Table Prefix"> Custom Table Prefix, By default: <?php echo esc_html($prefix); ?>
                        </p>

                        <p>
                            <label>
                                <input type="radio" option="create_wp" name="choose_create_staging_db" value="0" checked="">
                                <span>Install the staging site to the live site's database (Easy setup)</span>
                            </label>
                        </p>
                        <p>
                            <label>
                                <input type="radio" option="create_wp" name="choose_create_staging_db" value="1">
                                <span>Install the staging site to a separate database (Expert setup)</span>
                            </label>
                        </p>
                        <p></p>
                        <div class="" id="wpvivid_fresh_install_additional_database_account" style="display: none;">
                            <form>
                                <p><label><input type="text" option="create_wp" name="database-name" autocomplete="off" placeholder="DB Name" title="DB Name" readonly></label>
                                    <label><input type="text" option="create_wp" name="database-user" autocomplete="off" placeholder="DB Username" title="DB Username" readonly></label></p>
                                <p><label><input type="password" option="create_wp" name="database-pass" autocomplete="off" placeholder="Password" title="The Password of the Database Username" readonly></label>
                                    <label><input type="text" option="create_wp" name="database-host" autocomplete="off" placeholder="localhost" title="Database Host" readonly></label></p>
                                <p><label><input class="button-primary wpvivid_setting_general_save" name="test-fresh-install-additional-db-btn" type="button" onclick="wpvivid_additional_database_connect_test_ex();" value="Test Connection" readonly></label></p>
                            </form>
                        </div>
                        <div style="clear: both;"></div>
                    </div>
                </div>
                <div style="clear: both;"></div>

                <div style="background:#f1f1f1;">
                    <h2 style="padding-left:1em;padding-top:0.6em;">
                        <span class="dashicons dashicons-grid-view wpvivid-dashicons-blue"></span>
                        <span>Themes And Plugins</span>
                    </h2>
                </div>
                <div>
                    <div class="wpvivid-two-col" style="padding:0.2em;">
                        <div style="padding:0 0 0.5em 0.2em;">
                            <span><span>Check All </span><input type="checkbox" name="wpvivid_check_all_fresh_install_themes"></span>
                        </div>
                        <div style="padding:0.3em;height:300px;overflow-y:auto; border:1px solid #ccc;">
                            <?php $this->output_themes_plugins_info('theme'); ?>
                        </div>
                    </div>
                    <div class="wpvivid-two-col" style="padding:0.2em;">
                        <div style="padding:0 0 0.5em 0.2em;">
                            <span><span>Check All </span><input type="checkbox" name="wpvivid_check_all_fresh_install_plugins"></span>
                        </div>
                        <div style="padding:0.3em;height:300px;overflow-y:auto;border:1px solid #ccc;">
                            <?php $this->output_themes_plugins_info('plugin'); ?>
                        </div>
                    </div>
                </div>

                <div style="clear: both;"></div>
                <div style="padding:1em 1em 0 0;">
                    <input class="button-primary wpvivid_setting_general_save" id="wpvivid_create_new_wp" type="submit" value="Create Now"><span> Note: Please don't refresh the page while creating a fresh install.</span>
                </div>
                <div style="padding:1em 1em 0 0;">
                    <span>Tips: Please temporarily deactivate all cache, firewall and redirect plugins before creating a staging site to rule out possibilities of unknown failures.</span>
                </div>
            </div>

            <div id="wpvivid_create_new_wp_progress" style="display: none;">
                <div class="wpvivid-element-space-bottom">
                    <input class="button button-primary" type="button" id="wpvivid_staging_cancel" value="Cancel" />
                </div>
                <div class="postbox wpvivid-staging-log wpvivid-element-space-bottom" id="wpvivid_fresh_install_staging_log" style="margin-bottom: 0;"></div>
                <div class="action-progress-bar" style="margin: 10px 0 0 0; !important;">
                    <div class="action-progress-bar-percent" id="wpvivid_fresh_install_staging_progress_bar" style="height:24px;line-height:24px;width:0;">
                        <div style="float: left; margin-left: 4px;">0</div>
                        <div style="clear: both;"></div>
                    </div>
                </div>
            </div>
            <script>
                var home_url="<?php echo esc_url($home_url).'/'; ?>";
                var content_url="<?php echo esc_url($home_url).'/wp-content/'; ?>";
                var staging_requet_timeout=<?php echo esc_attr($request_timeout) ?>;

                var default_fresh_install_site = '<?php echo esc_attr($default_fresh_install_site); ?>';
                var default_content_fresh_install_site = '<?php echo esc_attr($default_content_fresh_install_site); ?>';

                jQuery('input:checkbox[name=wpvivid_check_all_fresh_install_themes]').on("click", function(){
                    if(jQuery(this).prop('checked'))
                    {
                        jQuery('input:checkbox[option=create_wp][name=Themes]').prop('checked', true);
                    }
                    else
                    {
                        jQuery('input:checkbox[option=create_wp][name=Themes]').prop('checked', false);
                    }
                });

                jQuery('input:checkbox[name=wpvivid_check_all_fresh_install_plugins]').on("click", function(){
                    if(jQuery(this).prop('checked'))
                    {
                        jQuery('input:checkbox[option=create_wp][name=Plugins]').prop('checked', true);
                    }
                    else
                    {
                        jQuery('input:checkbox[option=create_wp][name=Plugins]').prop('checked', false);
                    }
                });

                jQuery('input:checkbox[option=create_wp][name=Themes]').on("click", function(){
                    if(jQuery(this).prop('checked'))
                    {
                        var all_check = true;
                        jQuery('input:checkbox[option=create_wp][name=Themes]').each(function(){
                            if(!jQuery(this).prop('checked')){
                                all_check = false;
                            }
                        });
                        if(all_check) {
                            jQuery('input:checkbox[name=wpvivid_check_all_fresh_install_themes]').prop('checked', true);
                        }
                        else {
                            jQuery('input:checkbox[name=wpvivid_check_all_fresh_install_themes]').prop('checked', false);
                        }
                    }
                    else
                    {
                        jQuery('input:checkbox[name=wpvivid_check_all_fresh_install_themes]').prop('checked', false);
                    }
                });

                jQuery('input:checkbox[option=create_wp][name=Plugins]').on("click", function(){
                    if(jQuery(this).prop('checked'))
                    {
                        var all_check = true;
                        jQuery('input:checkbox[option=create_wp][name=Plugins]').each(function(){
                            if(!jQuery(this).prop('checked')){
                                all_check = false;
                            }
                        });
                        if(all_check) {
                            jQuery('input:checkbox[name=wpvivid_check_all_fresh_install_plugins]').prop('checked', true);
                        }
                        else {
                            jQuery('input:checkbox[name=wpvivid_check_all_fresh_install_plugins]').prop('checked', false);
                        }
                    }
                    else
                    {
                        jQuery('input:checkbox[name=wpvivid_check_all_fresh_install_plugins]').prop('checked', false);
                    }
                });

                jQuery('#wpvivid_create_new_wp_content').on("click", 'input:radio[name=choose_create_staging_db]', function(){
                    if(jQuery(this).prop('checked')){
                        var value = jQuery(this).val();
                        if(value === '0'){
                            jQuery('#wpvivid_fresh_install_additional_database_account').hide();
                            jQuery('#wpvivid_fresh_install_additional_database_account').find('input[name=database-name]').attr('readonly', true);
                            jQuery('#wpvivid_fresh_install_additional_database_account').find('input[name=database-user]').attr('readonly', true);
                            jQuery('#wpvivid_fresh_install_additional_database_account').find('input[name=database-pass]').attr('readonly', true);
                            jQuery('#wpvivid_fresh_install_additional_database_account').find('input[name=database-host]').attr('readonly', true);
                            jQuery('#wpvivid_create_new_wp_content').find('.wpvivid-staging-additional-database-name-display').html('<?php echo esc_html(DB_NAME); ?>');
                        }
                        else{
                            jQuery('#wpvivid_fresh_install_additional_database_account').show();
                            jQuery('#wpvivid_fresh_install_additional_database_account').find('input[name=database-name]').attr('readonly', false);
                            jQuery('#wpvivid_fresh_install_additional_database_account').find('input[name=database-user]').attr('readonly', false);
                            jQuery('#wpvivid_fresh_install_additional_database_account').find('input[name=database-pass]').attr('readonly', false);
                            jQuery('#wpvivid_fresh_install_additional_database_account').find('input[name=database-host]').attr('readonly', false);
                            var additional_db_name = jQuery('.wpvivid-additional-database-name').val();
                            if(additional_db_name !== ''){
                                jQuery('#wpvivid_create_new_wp_content').find('.wpvivid-staging-additional-database-name-display').html(additional_db_name);
                            }
                            else{
                                jQuery('#wpvivid_create_new_wp_content').find('.wpvivid-staging-additional-database-name-display').html('*');
                            }
                            wpvivid_fresh_install_additional_database_table_prefix();
                        }
                    }
                });

                jQuery('#wpvivid_create_new_wp_content').on("click", 'input:radio[name=choose_create_staging_dir]', function()
                {
                    if(jQuery(this).prop('checked'))
                    {
                        var value = jQuery(this).val();

                        if(value === '0')
                        {
                            jQuery('.wpvivid-fresh-install-home-url').show();
                            jQuery('#wpvivid_fresh_install_path_part').show();
                            jQuery('#wpvivid_fresh_install_staging_path').val(default_fresh_install_site);
                            var staging_path = jQuery('#wpvivid_fresh_install_staging_path').val();
                            if(staging_path !== '')
                            {
                                jQuery('.wpvivid-fresh-install-staging-site-name').html(staging_path);
                            }
                            else{
                                jQuery('.wpvivid-fresh-install-staging-site-name').html('*');
                            }
                        }
                        else
                        {
                            jQuery('.wpvivid-fresh-install-home-url').show();
                            jQuery('#wpvivid_fresh_install_path_part').show();
                            jQuery('#wpvivid_fresh_install_staging_path').val(default_content_fresh_install_site);
                            var staging_path = jQuery('#wpvivid_fresh_install_staging_path').val();
                            if(staging_path !== '')
                            {
                                jQuery('.wpvivid-fresh-install-staging-site-name').html('wp-content/'+staging_path);
                            }
                            else{
                                jQuery('.wpvivid-fresh-install-staging-site-name').html('wp-content/*');
                            }
                        }
                    }
                });

                jQuery('#wpvivid_create_new_wp_content').on("keyup", '#wpvivid_fresh_install_staging_table_prefix', function(){
                    wpvivid_fresh_install_additional_database_table_prefix();
                });

                jQuery('#wpvivid_create_new_wp_content').on("keyup", '#wpvivid_fresh_install_staging_path', function() {
                    var value = jQuery('input:radio[name=choose_create_staging_dir]:checked').val();
                    if(value === '0')
                    {
                        var staging_path = jQuery('#wpvivid_fresh_install_staging_path').val();
                        if(staging_path !== ''){
                            jQuery('.wpvivid-fresh-install-staging-site-name').html(staging_path);
                        }
                        else{
                            jQuery('.wpvivid-fresh-install-staging-site-name').html('*');
                        }
                    }
                    else if(value === '1'){
                        var staging_path = jQuery('#wpvivid_fresh_install_staging_path').val();
                        if(staging_path !== ''){
                            jQuery('.wpvivid-fresh-install-staging-site-name').html('wp-content/'+staging_path);
                        }
                        else{
                            jQuery('.wpvivid-fresh-install-staging-site-name').html('wp-content/*');
                        }
                    }
                });

                jQuery('#wpvivid_create_new_wp').click(function() {
                    var descript = '<?php esc_html_e('Click OK to start creating fresh WordPress install.', 'wpvivid-backuprestore'); ?>';
                    var ret = confirm(descript);
                    if(ret === true)
                    {
                        wpvivid_create_new_wp();
                    }
                });

                function wpvivid_fresh_install_additional_database_table_prefix(){
                    var additional_db_prefix = jQuery('#wpvivid_create_new_wp_content').find('#wpvivid_fresh_install_staging_table_prefix').val();
                    if(additional_db_prefix !== ''){
                        jQuery('#wpvivid_create_new_wp_content').find('.wpvivid-staging-table-prefix-display').html(additional_db_prefix);
                    }
                    else{
                        jQuery('#wpvivid_create_new_wp_content').find('.wpvivid-staging-table-prefix-display').html('*');
                    }
                }

                function wpvivid_create_new_wp() {
                    var staging_root_dir='0';
                    jQuery('input[option=create_wp][name=choose_create_staging_dir]').each(function ()
                    {
                        if (jQuery(this).prop('checked'))
                        {
                            staging_root_dir = jQuery(this).val();
                        }
                    });

                    var table_prefix=jQuery('input[option=create_wp][name=prefix]').val();

                    if(table_prefix=='')
                    {
                        alert('Table Prefix is required.');
                        return ;
                    }

                    var path='';

                    var path=jQuery('input[option=create_wp][name=path]').val();

                    if(path === '')
                    {
                        alert('A site name is required.');
                        return;
                    }

                    var additional_database_json = {};

                    var additional_database_option = '0';
                    jQuery('input[option=create_wp][name=choose_create_staging_db]').each(function ()
                    {
                        if (jQuery(this).prop('checked'))
                        {
                            additional_database_option = jQuery(this).val();
                        }
                    });

                    if (additional_database_option === '1')
                    {
                        additional_database_json['additional_database_check'] = '1';
                        additional_database_json['additional_database_info'] = {};
                        additional_database_json['additional_database_info']['db_user'] = jQuery('input[option=create_wp][name=database-user]').val();
                        additional_database_json['additional_database_info']['db_pass'] = jQuery('input[option=create_wp][name=database-pass]').val();
                        additional_database_json['additional_database_info']['db_host'] = jQuery('input[option=create_wp][name=database-host]').val();
                        additional_database_json['additional_database_info']['db_name'] = jQuery('input[option=create_wp][name=database-name]').val();
                        if (additional_database_json['additional_database_info']['db_name'] === '')
                        {
                            alert('Database Name is required.');
                            return;
                        }
                        if (additional_database_json['additional_database_info']['db_user'] === '')
                        {
                            alert('Database User is required.');
                            return;
                        }
                        if (additional_database_json['additional_database_info']['db_host'] === '')
                        {
                            alert('Database Host is required.');
                            return;
                        }
                    }
                    else {
                        additional_database_json['additional_database_check'] = '0';
                    }
                    var additional_database_info=JSON.stringify(additional_database_json);

                    var ajax_data =
                        {
                            'action': 'wpvividstg_check_staging_dir_free',
                            'root_dir':staging_root_dir,
                            'path': path,
                            'table_prefix': table_prefix,
                            'additional_db': additional_database_info
                        };
                    wpvivid_post_request(ajax_data, function (data)
                    {
                        var jsonarray = jQuery.parseJSON(data);
                        if (jsonarray.result === 'failed')
                        {
                            alert(jsonarray.error);
                        }
                        else
                        {
                            var ajax_data =
                                {
                                    'action': 'wpvividstg_check_filesystem_permissions_free',
                                    'root_dir':staging_root_dir,
                                    'path': path
                                };
                            wpvivid_post_request(ajax_data, function (data)
                            {
                                var jsonarray = jQuery.parseJSON(data);
                                if (jsonarray.result === 'failed')
                                {
                                    alert(jsonarray.error);
                                }
                                else
                                {
                                    var custom_dir_json = wpvivid_get_custom_create_new_wp_option();
                                    var custom_dir = JSON.stringify(custom_dir_json);

                                    var ajax_data = {
                                        'action': 'wpvividstg_start_staging_free',
                                        'create_new_wp':true,
                                        'path': path,
                                        'table_prefix': table_prefix,
                                        'custom_dir': custom_dir,
                                        'additional_db': additional_database_info,
                                        'root_dir':staging_root_dir,
                                    };


                                    jQuery('#wpvivid_create_new_wp_content').hide();
                                    jQuery('#wpvivid_create_new_wp_progress').show();

                                    wpvivid_post_request(ajax_data, function (data)
                                    {
                                        setTimeout(function ()
                                        {
                                            wpvivid_get_create_new_wp_progress();
                                        }, staging_requet_timeout);
                                    }, function (XMLHttpRequest, textStatus, errorThrown)
                                    {
                                        jQuery('#wpvivid_create_new_wp_content').hide();
                                        jQuery('#wpvivid_create_new_wp_progress').show();
                                        setTimeout(function () {
                                            wpvivid_get_create_new_wp_progress();
                                        }, staging_requet_timeout);
                                    });
                                }
                            }, function (XMLHttpRequest, textStatus, errorThrown) {
                                var error_message = wpvivid_output_ajaxerror('creating staging site', textStatus, errorThrown);
                                alert(error_message);
                            });
                        }
                    }, function (XMLHttpRequest, textStatus, errorThrown) {
                        var error_message = wpvivid_output_ajaxerror('creating staging site', textStatus, errorThrown);
                        alert(error_message);
                    });
                }

                function wpvivid_get_create_new_wp_progress() {
                    var ajax_data = {
                        'action':'wpvividstg_get_staging_progress_free',
                    };

                    wpvivid_post_request(ajax_data, function(data)
                    {
                        try
                        {
                            var jsonarray = jQuery.parseJSON(data);
                            if (jsonarray.result === 'success')
                            {
                                var log_data = jsonarray.log;
                                jQuery('#wpvivid_fresh_install_staging_log').html("");
                                while (log_data.indexOf('\n') >= 0)
                                {
                                    var iLength = log_data.indexOf('\n');
                                    var log = log_data.substring(0, iLength);
                                    log_data = log_data.substring(iLength + 1);
                                    var insert_log = "<div style=\"clear:both;\">" + log + "</div>";
                                    jQuery('#wpvivid_fresh_install_staging_log').append(insert_log);
                                    var div = jQuery('#wpvivid_fresh_install_staging_log');
                                    div[0].scrollTop = div[0].scrollHeight;
                                }
                                jQuery('#wpvivid_fresh_install_staging_progress_bar').css('width', jsonarray.percent + '%');
                                jQuery('#wpvivid_fresh_install_staging_progress_bar').find('div').eq(0).html(jsonarray.percent + '%');
                                if(jsonarray.continue)
                                {
                                    if(jsonarray.need_restart)
                                    {
                                        wpvivid_restart_create_new_wp();
                                    }
                                    else
                                    {
                                        setTimeout(function()
                                        {
                                            wpvivid_get_create_new_wp_progress();
                                        }, staging_requet_timeout);
                                    }
                                }
                                else
                                {
                                    if(typeof jsonarray.completed !== 'undefined' && jsonarray.completed)
                                    {
                                        jQuery('#wpvivid_staging_cancel').css({'pointer-events': 'auto', 'opacity': '1'});
                                        var percent = 100;
                                        jQuery('#wpvivid_fresh_install_staging_progress_bar').css('width', percent + '%');
                                        jQuery('#wpvivid_fresh_install_staging_progress_bar').find('div').eq(0).html(percent + '%');
                                        setTimeout(function()
                                        {
                                            alert('Creating a fresh WordPress install completed successfully.');
                                            location.href='<?php echo esc_url(apply_filters('wpvividstg_get_admin_url', '')) . 'admin.php?page=wpvivid-staging'; ?>';
                                        }, 1000);
                                    }
                                    else if(typeof jsonarray.error !== 'undefined' && jsonarray.error)
                                    {
                                        alert(jsonarray.error);
                                        location.href='<?php echo esc_url(apply_filters('wpvividstg_get_admin_url', '')) . 'admin.php?page=wpvivid-staging'; ?>';
                                    }
                                    else if(typeof jsonarray.is_cancel !== 'undefined' && jsonarray.is_cancel)
                                    {
                                        var staging_site_info = {};
                                        staging_site_info['staging_path'] = jsonarray.staging_path;
                                        staging_site_info['staging_additional_db'] = jsonarray.staging_additional_db;
                                        staging_site_info['staging_additional_db_user'] = jsonarray.staging_additional_db_user;
                                        staging_site_info['staging_additional_db_pass'] = jsonarray.staging_additional_db_pass;
                                        staging_site_info['staging_additional_db_host'] = jsonarray.staging_additional_db_host;
                                        staging_site_info['staging_additional_db_name'] = jsonarray.staging_additional_db_name;
                                        staging_site_info['staging_table_prefix'] = jsonarray.staging_table_prefix;
                                        staging_site_info = JSON.stringify(staging_site_info);
                                        ajax_data = {
                                            'action': 'wpvividstg_delete_cancel_staging_site_free',
                                            'staging_site_info': staging_site_info
                                        };
                                        wpvivid_post_request(ajax_data, function (data)
                                        {
                                            location.href='<?php echo esc_url(apply_filters('wpvividstg_get_admin_url', '')) . 'admin.php?page=wpvivid-staging'; ?>';
                                        }, function (XMLHttpRequest, textStatus, errorThrown)
                                        {
                                            var error_message = wpvivid_output_ajaxerror('deleting fresh site', textStatus, errorThrown);
                                            alert(error_message);
                                            location.href='<?php echo esc_url(apply_filters('wpvividstg_get_admin_url', '')) . 'admin.php?page=wpvivid-staging'; ?>';
                                        });
                                    }
                                    else{
                                        location.href='<?php echo esc_url(apply_filters('wpvividstg_get_admin_url', '')) . 'admin.php?page=wpvivid-staging'; ?>';
                                    }
                                }
                            }
                            else if (jsonarray.result === 'failed')
                            {
                                jQuery('#wpvivid_create_new_wp_content').show();
                                jQuery('#wpvivid_create_new_wp_progress').hide();
                                alert(jsonarray.error);
                            }
                        }
                        catch(err)
                        {
                            setTimeout(function()
                            {
                                wpvivid_get_create_new_wp_progress();
                            }, 3000);
                        }

                    }, function(XMLHttpRequest, textStatus, errorThrown)
                    {
                        setTimeout(function()
                        {
                            wpvivid_get_create_new_wp_progress();
                        }, 3000);
                    });
                }

                function wpvivid_restart_create_new_wp() {
                    var ajax_data = {
                        'action':'wpvividstg_start_staging_free',
                    };

                    wpvivid_post_request(ajax_data, function(data)
                    {
                        setTimeout(function()
                        {
                            wpvivid_get_create_new_wp_progress();
                        }, staging_requet_timeout);
                    }, function(XMLHttpRequest, textStatus, errorThrown)
                    {
                        setTimeout(function()
                        {
                            wpvivid_get_create_new_wp_progress();
                        }, staging_requet_timeout);
                    });
                }

                function wpvivid_get_custom_create_new_wp_option() {
                    var json = {};
                    json['themes_list'] = Array();
                    json['plugins_list'] = Array();
                    json['themes_check'] = '0';
                    json['plugins_check'] = '0';
                    jQuery('input:checkbox[option=create_wp][name=Themes]').each(function()
                    {
                        if(jQuery(this).prop('checked'))
                        {
                            json['themes_check'] = '1';
                        }
                        else{
                            json['themes_list'].push(jQuery(this).val());
                        }
                    });
                    jQuery('input:checkbox[option=create_wp][name=Plugins]').each(function()
                    {
                        if(jQuery(this).prop('checked'))
                        {
                            json['plugins_check'] = '1';
                        }
                        else{
                            json['plugins_list'].push(jQuery(this).val());
                        }
                    });
                    return json;
                }

                function wpvivid_additional_database_connect_test_ex()
                {
                    var db_user =jQuery('input[option=create_wp][name=database-user]').val();
                    var db_pass =jQuery('input[option=create_wp][name=database-pass]').val();
                    var db_host =jQuery('input[option=create_wp][name=database-host]').val();
                    var db_name =jQuery('input[option=create_wp][name=database-name]').val();
                    if(db_name == '')
                    {
                        alert('Database Name is required.');
                        return;
                    }

                    if(db_user == '')
                    {
                        alert('Database User is required.');
                        return;
                    }

                    if(db_pass == '')
                    {
                        alert('Database Password is required.');
                        return;
                    }

                    if(db_host == '')
                    {
                        alert('Database Host is required.');
                        return ;
                    }

                    var db_json = {};
                    db_json['db_user'] = db_user;
                    db_json['db_pass'] = db_pass;
                    db_json['db_host'] = db_host;
                    db_json['db_name'] = db_name;
                    var db_connect_info = JSON.stringify(db_json);
                    var ajax_data = {
                        'action': 'wpvividstg_test_additional_database_connect_free',
                        'database_info': db_connect_info
                    };

                    wpvivid_post_request(ajax_data, function (data)
                    {
                        try
                        {
                            var jsonarray = jQuery.parseJSON(data);
                            if (jsonarray !== null)
                            {
                                if (jsonarray.result === 'success')
                                {
                                    alert('Connection success.')
                                }
                                else
                                {
                                    alert(jsonarray.error);
                                }
                            }
                            else
                            {
                                alert('Connection Failed. Please check the credentials you entered and try again.');
                            }
                        }
                        catch (e)
                        {
                            alert('Connection Failed. Please check the credentials you entered and try again.');
                        }
                    }, function (XMLHttpRequest, textStatus, errorThrown)
                    {
                        var error_message = wpvivid_output_ajaxerror('connecting database', textStatus, errorThrown);
                        alert(error_message);
                    });
                }
            </script>
        </div>
        <?php
    }
}includes/staging/class-wpvivid-staging-create-new-wp.php000064400000022120151327705670017432 0ustar00<?php
if (!defined('WPVIVID_PLUGIN_DIR'))
{
    die;
}

if ( ! class_exists( 'WP_List_Table' ) )
{
    require_once( ABSPATH . 'wp-admin/includes/class-wp-list-table.php' );
}

class WPvivid_Staging_Install_Wordpress_Free
{
    public $task;

    public $db_src_instance;
    public $db_des_instance;
    public $new_site_url;
    public $new_home_url;

    public function __construct($task_id)
    {
        $this->task=new WPvivid_Staging_Task($task_id);
    }

    public function do_install_wordpress()
    {
        global $wpvivid_plugin,$wpdb,$current_user;

        $this->db_src_instance=false;
        $this->db_des_instance=false;
        $this->new_site_url=$this->task->get_site_url(true);
        $this->new_home_url=$this->task->get_home_url(true);


        $wpvivid_plugin->staging->log->WriteLog('Start install new wordpress.','notice');

        if (!function_exists('wp_install'))
        {
            require ABSPATH . 'wp-admin/includes/upgrade.php';
        }

        $username=$current_user->user_login;
        $email=$current_user->user_email;
        $title= get_option('blogname');
        $userpassword=$current_user->user_pass;
        $prefix=$this->task->get_db_prefix(true);
        $old_prefix=$this->task->get_db_prefix();
        $permalink_structure = $this->task->get_permalink_structure();
        $is_overwrite_permalink_structure = $this->task->get_is_overwrite_permalink_structure();

        $data['id']=$this->task->get_id();
        $data['name']=$this->task->get_path(true);
        $data['prefix']= $prefix;
        $admin_url = apply_filters('wpvividstg_get_admin_url', '');
        $admin_url .= 'admin.php?page='.apply_filters('wpvivid_white_label_slug', 'WPvivid');
        $data['parent_admin_url']=$admin_url;
        $data['live_site_url']=home_url();
        $data['live_site_staging_url']=apply_filters('wpvividstg_get_admin_url', '').'admin.php?page='.apply_filters('wpvivid_white_label_plugin_name', 'WPvivid_Staging');
        $data=serialize($data);

        $old_wpdb=$wpdb;

        $wpdb=$this->get_db_instance(true);

        $wpdb->set_prefix($prefix);

        $hook_name = 'update_option_blogname';
        if(has_action($hook_name))
        {
            remove_all_actions($hook_name);
        }

        $result = @wp_install($title, $username, $email, 0, '', md5(wp_rand()));
        $wpvivid_plugin->staging->log->WriteLog(wp_json_encode($result), 'notice');
        $user_id = $result['user_id'];

        $db_field = 'ID';

        if ( ! $wpdb->get_row(
            $wpdb->prepare(
                "SELECT * FROM $wpdb->users WHERE $db_field = %s LIMIT 1",
                $user_id
            )
        ) )
        {
            $wpvivid_plugin->staging->log->WriteLog('User not create ,so we create it.', 'notice');
            $user_id       = wp_create_user( $username, md5(wp_rand()), $email );
            update_user_option( $user_id, 'default_password_nag', true, true );
        }

        $query = $wpdb->prepare("UPDATE {$prefix}users SET user_pass = %s, user_activation_key = '' WHERE ID = %d LIMIT 1", array($userpassword, $user_id));
        $wpvivid_plugin->staging->log->WriteLog($query, 'notice');
        $wpdb->query($query);

        $update_query ="UPDATE {$prefix}options SET option_value = '{$this->new_site_url}' WHERE option_name = 'siteurl'";
        $wpvivid_plugin->staging->log->WriteLog($update_query, 'notice');
        if ($wpdb->get_results($update_query)===false)
        {
            $error=$wpdb->last_error;
            $wpvivid_plugin->staging->log->WriteLog($error, 'Warning');
        }

        $update_query ="UPDATE {$prefix}options SET option_value = '{$this->new_home_url}' WHERE option_name = 'home'";
        $wpvivid_plugin->staging->log->WriteLog($update_query, 'notice');
        if ($wpdb->get_results($update_query)===false)
        {
            $error=$wpdb->last_error;
            $wpvivid_plugin->staging->log->WriteLog($error, 'Warning');
        }

        $update_query ="UPDATE {$prefix}options SET option_name='{$prefix}user_roles' WHERE option_name='{$old_prefix}user_roles'";
        $wpvivid_plugin->staging->log->WriteLog($update_query, 'notice');
        if ($wpdb->get_results($update_query)===false)
        {
            $error=$wpdb->last_error;
            $wpvivid_plugin->staging->log->WriteLog($error, 'Warning');
        }

        $update_query=$wpdb->prepare("INSERT INTO {$prefix}options (option_name,option_value) VALUES ('wpvivid_staging_finish',%d)", 1);
        $wpvivid_plugin->staging->log->WriteLog($update_query, 'notice');
        if ($wpdb->get_results($update_query)===false)
        {
            $error=$wpdb->last_error;
            $wpvivid_plugin->staging->log->WriteLog($error, 'Warning');
        }

        if($is_overwrite_permalink_structure == 0)
        {
            $update_query = "INSERT INTO {$prefix}options (option_name,option_value) VALUES ('wpvivid_staging_init','{$permalink_structure}')";

            $wpvivid_plugin->staging->log->WriteLog($update_query, 'notice');
            if ($wpdb->get_results($update_query) === false)
            {
                $error = $wpdb->last_error;
                $wpvivid_plugin->staging->log->WriteLog($error, 'Warning');
            }
        }

        $update_query = $wpdb->prepare("INSERT INTO {$prefix}options (option_name,option_value) VALUES ('wpvivid_staging_data',%s)", $data);
        $wpvivid_plugin->staging->log->WriteLog($update_query, 'notice');
        if ($wpdb->get_results($update_query)===false)
        {
            $error=$wpdb->last_error;
            $wpvivid_plugin->staging->log->WriteLog($error, 'Warning');
        }

        $current   = array();
        $current[] = 'wpvivid-backuprestore/wpvivid-backuprestore.php';
        sort( $current );
        $value=serialize($current);
        $update_query = $wpdb->prepare("UPDATE {$prefix}options SET option_value=%s WHERE option_name='active_plugins'" , $value);
        $wpvivid_plugin->staging->log->WriteLog($update_query, 'notice');
        if ($wpdb->get_results($update_query)===false)
        {
            $error=$wpdb->last_error;
            $wpvivid_plugin->staging->log->WriteLog($error, 'Warning');
        }

        $wpdb=$old_wpdb;
        $wpvivid_plugin->staging->log->WriteLog('prefix:'.$old_prefix,'notice');
        $wpdb->set_prefix($old_prefix);

        $des_path=$this->task->get_path();
        $path=$des_path.DIRECTORY_SEPARATOR.'wp-config.php';
        $data=file_get_contents($path);
        if( $data === false )
        {
            $wpvivid_plugin->staging->log->WriteLog('wp-config.php not found in '.$path,'notice');
        }
        else
        {
            preg_match( "/define\s*\(\s*['\"]MULTISITE['\"]\s*,\s*(.*)\s*\);/", $data, $matches );
            if( !empty( $matches[1] ) )
            {
                $wpvivid_plugin->staging->log->WriteLog('MULTISITE found in wp-config.php','notice');
                $pattern = "/define\s*\(\s*['\"]MULTISITE['\"]\s*,\s*(.*)\s*\);.*/";
                $replace = "define('MULTISITE',false); //";
                $data = preg_replace( array($pattern), $replace, $data );
                if( null === ($data) )
                {
                    $wpvivid_plugin->staging->log->WriteLog('MULTISITE not replace in wp-config.php','notice');
                }
            }
            file_put_contents($path,$data);
        }

        $wpvivid_plugin->staging->log->WriteLog('finished install new wordpress.','notice');
        $this->task->update_job_finished('create_new_wp');



        return true;
    }

    public function get_db_instance($des=false)
    {
        $db=$this->task->get_db_connect();
        if($des)
        {
            if( $this->db_des_instance===false)
            {
                if($db['des_use_additional_db']===false)
                {
                    global $wpdb;
                    $this->db_des_instance=$wpdb;
                    return $this->db_des_instance;
                }
                else
                {
                    $this->db_des_instance=new wpdb($db['des_dbuser'],$db['des_dbpassword'],$db['des_dbname'],$db['des_dbhost']);
                    return $this->db_des_instance;
                }
            }
            else
            {
                return $this->db_des_instance;
            }
        }
        else
        {
            if( $this->db_src_instance===false)
            {
                if($db['src_use_additional_db']===false)
                {
                    global $wpdb;
                    $this->db_src_instance=$wpdb;
                    return  $this->db_src_instance;
                }
                else
                {
                    $this->db_src_instance=new wpdb($db['src_dbuser'],$db['src_dbpassword'],$db['src_dbname'],$db['src_dbhost']);
                    return $this->db_src_instance;
                }
            }
            else
            {
                return $this->db_src_instance;
            }
        }
    }
}includes/staging/class-wpvivid-staging-log-page.php000064400000125404151327705670016460 0ustar00<?php
if (!defined('WPVIVID_PLUGIN_DIR'))
{
    die;
}

class WPvivid_Staging_Log_List_Free extends WP_List_Table
{
    public $page_num;
    public $log_list;

    public function __construct( $args = array() )
    {
        parent::__construct(
            array(
                'plural' => 'log',
                'screen' => 'log'
            )
        );
    }

    public function get_columns()
    {
        $columns = array();
        $columns['wpvivid_date'] = __( 'Date', 'wpvivid-backuprestore' );
        $columns['wpvivid_log_type'] = __( 'Log Type', 'wpvivid-backuprestore' );
        $columns['wpvivid_log_file_name'] =__( 'Log File Name	', 'wpvivid-backuprestore'  );
        $columns['wpvivid_log_action'] = __( 'Action	', 'wpvivid-backuprestore'  );
        $columns['wpvivid_download'] = __( 'Download', 'wpvivid-backuprestore'  );

        return $columns;
    }

    public function set_log_list($log_list,$page_num=1)
    {
        $this->log_list=$log_list;
        $this->page_num=$page_num;
    }

    public function get_pagenum()
    {
        if($this->page_num=='first')
        {
            $this->page_num=1;
        }
        else if($this->page_num=='last')
        {
            $this->page_num=$this->_pagination_args['total_pages'];
        }
        $pagenum = $this->page_num ? $this->page_num : 0;

        if ( isset( $this->_pagination_args['total_pages'] ) && $pagenum > $this->_pagination_args['total_pages'] )
        {
            $pagenum = $this->_pagination_args['total_pages'];
        }

        return max( 1, $pagenum );
    }

    public function prepare_items()
    {
        $columns = $this->get_columns();
        $hidden = array();
        $sortable = array();
        $this->_column_headers = array($columns, $hidden, $sortable);

        $total_items =sizeof($this->log_list);

        $this->set_pagination_args(
            array(
                'total_items' => $total_items,
                'per_page'    => 10,
            )
        );
    }

    public function has_items()
    {
        return !empty($this->log_list);
    }

    public function _column_wpvivid_date( $log )
    {
        $offset=get_option('gmt_offset');
        $localtime = strtotime($log['time']) + $offset * 60 * 60;
        echo '<td><label for="tablecell">'.esc_html(gmdate('F-d-Y H:i:s',$localtime)).'</label></td>';
    }

    protected function column_wpvivid_log_type($log)
    {
        if($log['error'])
        {
            echo '<span>Error</span>';
        }
        else
        {
            echo '<span>'.esc_html($log['des']).'</span>';
        }
    }

    public function column_wpvivid_log_file_name( $log )
    {
        echo '<span>'.esc_html($log['file_name']).'</span>';
    }

    public function column_wpvivid_log_action( $log )
    {
        echo '<a class="open-log" log="'.esc_attr($log['file_name']).'" style="cursor:pointer;">
                  <img src="'.esc_url(WPVIVID_PLUGIN_IMAGES_URL.'Log.png').'" style="vertical-align:middle;">Log
               </a>';
    }

    public function column_wpvivid_download( $log )
    {
        echo '<a class="download-log" log="'.esc_attr($log['file_name']).'" style="cursor:pointer;">
                    <img src="' . esc_url(WPVIVID_PLUGIN_IMAGES_URL . 'staging/download.png') . '" style="vertical-align:middle;" />Download
               </a>';
    }

    public function display_rows()
    {
        $this->_display_rows( $this->log_list );
    }

    private function _display_rows($log_list)
    {
        $page=$this->get_pagenum();

        $page_log_list=array();
        $count=0;
        while ( $count<$page )
        {
            $page_log_list = array_splice( $log_list, 0, 10);
            $count++;
        }
        foreach ( $page_log_list as $log)
        {
            $this->single_row($log);
        }
    }

    public function single_row($log)
    {
        ?>
        <tr>
            <?php $this->single_row_columns( $log ); ?>
        </tr>
        <?php
    }

    protected function pagination( $which )
    {
        if ( empty( $this->_pagination_args ) )
        {
            return;
        }

        $total_items     = $this->_pagination_args['total_items'];
        $total_pages     = $this->_pagination_args['total_pages'];
        $infinite_scroll = false;
        if ( isset( $this->_pagination_args['infinite_scroll'] ) )
        {
            $infinite_scroll = $this->_pagination_args['infinite_scroll'];
        }

        if ( 'top' === $which && $total_pages > 1 )
        {
            $this->screen->render_screen_reader_content( 'heading_pagination' );
        }

        $output = '<span class="displaying-num">' . sprintf( _n( '%s item', '%s items', $total_items, 'wpvivid-backuprestore' ), number_format_i18n( $total_items ) ) . '</span>';

        $current              = $this->get_pagenum();

        $page_links = array();

        $total_pages_before = '<span class="paging-input">';
        $total_pages_after  = '</span></span>';

        $disable_first = $disable_last = $disable_prev = $disable_next = false;

        if ( $current == 1 ) {
            $disable_first = true;
            $disable_prev  = true;
        }
        if ( $current == 2 ) {
            $disable_first = true;
        }
        if ( $current == $total_pages ) {
            $disable_last = true;
            $disable_next = true;
        }
        if ( $current == $total_pages - 1 ) {
            $disable_last = true;
        }

        if ( $disable_first ) {
            $page_links[] = '<span class="tablenav-pages-navspan button disabled" aria-hidden="true">&laquo;</span>';
        } else {
            $page_links[] = sprintf(
                "<div class='first-page button'><span class='screen-reader-text'>%s</span><span aria-hidden='true'>%s</span></div>",
                __( 'First page', 'wpvivid-backuprestore' ),
                '&laquo;'
            );
        }

        if ( $disable_prev ) {
            $page_links[] = '<span class="tablenav-pages-navspan button disabled" aria-hidden="true">&lsaquo;</span>';
        } else {
            $page_links[] = sprintf(
                "<div class='prev-page button' value='%s'><span class='screen-reader-text'>%s</span><span aria-hidden='true'>%s</span></div>",
                $current,
                __( 'Previous page', 'wpvivid-backuprestore' ),
                '&lsaquo;'
            );
        }

        if ( 'bottom' === $which ) {
            $html_current_page  = $current;
            $total_pages_before = '<span class="screen-reader-text">' . __( 'Current Page', 'wpvivid-backuprestore' ) . '</span><span id="table-paging" class="paging-input"><span class="tablenav-paging-text">';
        } else {
            $html_current_page = sprintf(
                "%s<input class='current-page' type='text' name='paged' value='%s' size='%d' aria-describedby='table-paging' /><span class='tablenav-paging-text'>",
                '<label class="screen-reader-text">' . __( 'Current Page', 'wpvivid-backuprestore' ) . '</label>',
                $current,
                strlen( $total_pages )
            );
        }
        $html_total_pages = sprintf( "<span class='total-pages'>%s</span>", number_format_i18n( $total_pages ) );
        $page_links[]     = $total_pages_before . sprintf( _x( '%1$s of %2$s', 'paging', 'wpvivid-backuprestore' ), $html_current_page, $html_total_pages ) . $total_pages_after;

        if ( $disable_next ) {
            $page_links[] = '<span class="tablenav-pages-navspan button disabled" aria-hidden="true">&rsaquo;</span>';
        } else {
            $page_links[] = sprintf(
                "<div class='next-page button' value='%s'><span class='screen-reader-text'>%s</span><span aria-hidden='true'>%s</span></div>",
                $current,
                __( 'Next page', 'wpvivid-backuprestore' ),
                '&rsaquo;'
            );
        }

        if ( $disable_last ) {
            $page_links[] = '<span class="tablenav-pages-navspan button disabled" aria-hidden="true">&raquo;</span>';
        } else {
            $page_links[] = sprintf(
                "<div class='last-page button'><span class='screen-reader-text'>%s</span><span aria-hidden='true'>%s</span></div>",
                __( 'Last page', 'wpvivid-backuprestore' ),
                '&raquo;'
            );
        }

        $pagination_links_class = 'pagination-links';
        if ( ! empty( $infinite_scroll ) ) {
            $pagination_links_class .= ' hide-if-js';
        }
        $output .= "\n<span class='$pagination_links_class'>" . join( "\n", $page_links ) . '</span>';

        if ( $total_pages ) {
            $page_class = $total_pages < 2 ? ' one-page' : '';
        } else {
            $page_class = ' no-pages';
        }
        $this->_pagination = "<div class='tablenav-pages{$page_class}'>$output</div>";

        echo $this->_pagination;
    }

    protected function display_tablenav( $which ) {
        $css_type = '';
        if ( 'top' === $which ) {
            wp_nonce_field( 'bulk-' . $this->_args['plural'] );
            $css_type = 'margin: 0 0 10px 0';
        }
        else if( 'bottom' === $which ) {
            $css_type = 'margin: 10px 0 0 0';
        }
        ?>
        <div class="tablenav <?php echo esc_attr( $which ); ?>" style="<?php echo esc_attr($css_type); ?>">
            <?php
            $this->extra_tablenav( $which );
            $this->pagination( $which );
            ?>

            <br class="clear" />
        </div>
        <?php
    }
}

class WPvivid_Staging_Log_Page_Free
{
    public $main_tab;
    public function __construct()
    {
        add_action('wp_ajax_wpvividstg_get_log_list_page', array($this, 'get_log_list_page'));
        add_action('wp_ajax_wpvividstg_view_log_ex', array($this, 'view_log_ex'));
        add_action('wp_ajax_wpvividstg_download_log', array($this, 'download_log'));

        //add_filter('wpvivid_get_staging_admin_menus', array($this, 'get_menu'), 10);
        add_filter('wpvivid_add_log_tab_page', array($this, 'add_log_tab_page'), 11);
        add_filter('wpvivid_get_staging_log_list', array($this, 'get_staging_log_list'), 10);
    }

    public function add_log_tab_page($setting_array)
    {
        $setting_array['staging_log_page'] = array('index' => '2', 'tab_func' =>  array($this, 'add_tab_log'), 'page_func' => array($this, 'add_page_log'));
        return $setting_array;
    }

    public function add_tab_log(){
        ?>
        <a href="#" id="wpvivid_tab_staging_log" class="nav-tab log-nav-tab" onclick="switchlogTabs(event,'staging-logs-page')"><?php esc_html_e('Staging Logs', 'wpvivid-backuprestore'); ?></a>
        <?php
    }

    public function add_page_log()
    {
        global $wpvivid_plugin;
        $display_log_count=array(0=>"10",1=>"20",2=>"30",3=>"40",4=>"50");
        $max_log_diaplay=20;
        $loglist=$this->get_log_list('staging');
        ?>
        <div id="staging-logs-page" class="log-tab-content wpvivid_tab_log" name="tab-logs" style="display: none">
            <table class="wp-list-table widefat plugins">
                <thead class="log-head">
                <tr>
                    <th class="row-title"><?php esc_html_e( 'Date', 'wpvivid-backuprestore' ); ?></th>
                    <th><?php esc_html_e( 'Log Type', 'wpvivid-backuprestore' ); ?></th>
                    <th><?php esc_html_e( 'Log File Name', 'wpvivid-backuprestore' ); ?></th>
                    <th><?php esc_html_e( 'Action', 'wpvivid-backuprestore' ); ?></th>
                </tr>
                </thead>
                <tbody class="wpvivid-loglist" id="wpvivid_staging_loglist">
                <?php
                $this->get_staging_log_list();
                ?>
                </tbody>
            </table>
            <div style="padding-top: 10px; text-align: center;">
                <input class="button-secondary log-page" id="wpvivid_staging_pre_log_page" type="submit" value="<?php esc_attr_e( ' < Pre page ', 'wpvivid-backuprestore' ); ?>" />
                <div style="font-size: 12px; display: inline-block; padding-left: 10px;">
                                <span id="wpvivid_staging_log_page_info" style="line-height: 35px;">
                                    <?php
                                    $current_page=1;
                                    $max_page=ceil(sizeof($loglist['log_list']['file'])/$max_log_diaplay);
                                    if($max_page == 0) $max_page = 1;
                                    echo esc_html($current_page.' / '.$max_page);
                                    ?>
                                </span>
                </div>
                <input class="button-secondary log-page" id="wpvivid_staging_next_log_page" type="submit" value="<?php esc_attr_e( ' Next page > ', 'wpvivid-backuprestore' ); ?>" />
                <div style="float: right;">
                    <select name="" id="wpvivid_staging_display_log_count">
                        <?php
                        foreach ($display_log_count as $value){
                            if($value == $max_log_diaplay){
                                echo '<option selected="selected" value="' . esc_attr($value) . '">' . esc_html($value) . '</option>';
                            }
                            else {
                                echo '<option value="' . esc_attr($value) . '">' . esc_html($value) . '</option>';
                            }
                        }
                        ?>
                    </select>
                </div>
            </div>
        </div>
        <script>
            var wpvivid_cur_staging_log_page = 1;
            var wpvivid_staging_log_count = '<?php
                echo esc_attr(sizeof($loglist['log_list']['file']));
                ?>';
            jQuery('#wpvivid_staging_display_log_count').on("change", function(){
                wpvivid_staging_display_log_page();
            });

            jQuery('#wpvivid_staging_pre_log_page').click(function(){
                wpvivid_staging_pre_log_page();
            });

            jQuery('#wpvivid_staging_next_log_page').click(function(){
                wpvivid_staging_next_log_page();
            });

            function wpvivid_staging_pre_log_page(){
                if(wpvivid_cur_staging_log_page > 1){
                    wpvivid_cur_staging_log_page--;
                }
                wpvivid_staging_display_log_page();
            }

            function wpvivid_staging_next_log_page(){
                var display_count = jQuery("#wpvivid_staging_display_log_count option:selected").val();
                var max_pages=Math.ceil(wpvivid_staging_log_count/display_count);
                if(wpvivid_cur_staging_log_page < max_pages){
                    wpvivid_cur_staging_log_page++;
                }
                wpvivid_staging_display_log_page();
            }

            function wpvivid_staging_display_log_page(){
                var display_count = jQuery("#wpvivid_staging_display_log_count option:selected").val();
                var max_pages=Math.ceil(wpvivid_staging_log_count/display_count);
                if(max_pages == 0) max_pages = 1;
                jQuery('#wpvivid_staging_log_page_info').html(wpvivid_cur_staging_log_page+ " / "+max_pages);

                var begin = (wpvivid_cur_staging_log_page - 1) * display_count;
                var end = parseInt(begin) + parseInt(display_count);
                jQuery("#wpvivid_staging_loglist tr").hide();
                jQuery('#wpvivid_staging_loglist tr').each(function(i){
                    if (i >= begin && i < end)
                    {
                        jQuery(this).show();
                    }
                });
            }
        </script>
        <?php
    }

    public function get_staging_log_list()
    {
        $loglist=$this->get_log_list('staging');
        $current_num=1;
        $max_log_diaplay=20;
        $log_index=0;
        $pic_log='/admin/partials/images/Log.png';
        if(!empty($loglist['log_list']['file']))
        {
            foreach ($loglist['log_list']['file'] as $value)
            {
                if ($current_num <= $max_log_diaplay) {
                    $log_tr_display = '';
                } else {
                    $log_tr_display = 'display: none;';
                }
                if (empty($value['time'])) {
                    $value['time'] = 'N/A';
                }
                else{
                    $offset=get_option('gmt_offset');
                    $localtime = strtotime($value['time'])/* + $offset * 60 * 60*/;
                    $value['time'] = gmdate('F-d-Y H:i:s',$localtime);
                }
                if (empty($value['des'])) {
                    $value['des'] = 'N/A';
                }
                $value['path'] = str_replace('\\', '/', $value['path']);
                echo  '<tr style="'.esc_attr($log_tr_display).'">
                <td class="row-title"><label for="tablecell">'.esc_html($value['time']).'</label>
                </td>
                <td>'.esc_html($value['des']).'</td>
                <td>'.esc_html($value['file_name']).'</td>
                <td>
                    <a onclick="wpvivid_read_log(\'wpvivid_view_log\', \''.esc_attr($value['id']).'\', \'staging\')" style="cursor:pointer;">
                    <img src="'.esc_url(WPVIVID_PLUGIN_URL.$pic_log).'" style="vertical-align:middle;">Log
                    </a>
                </td>
                </tr>';
                $log_index++;
                $current_num++;
            }
        }
    }

    public function output_log()
    {
        ?>
        <div class="postbox restore_log" id="wpvivid_read_log_content">

        </div>
        <?php
    }

    public function output_staging_log_list()
    {
        $this->output_log_list('staging','staging_log','wpvivid_staging_log_list');
    }

    public function output_log_list($type,$slug,$id)
    {
        ?>
        <div class="wpvivid-log-list" id="<?php echo esc_attr($id)?>">
            <?php
            $loglist=$this->get_log_list($type);
            $table = new WPvivid_Staging_Log_List_Free();
            $table->set_log_list($loglist['log_list']['file']);
            $table->prepare_items();
            $table->display();
            ?>
        </div>
        <script>
            jQuery('#<?php echo esc_attr($id)?>').on("click",'.first-page',function()
            {
                wpvivid_log_change_page('first','<?php echo esc_attr($type);?>','<?php echo esc_attr($id);?>');
            });

            jQuery('#<?php echo esc_attr($id)?>').on("click",'.prev-page',function()
            {
                var page=parseInt(jQuery(this).attr('value'));
                wpvivid_log_change_page(page-1,'<?php echo esc_attr($type);?>','<?php echo esc_attr($id)?>');
            });

            jQuery('#<?php echo esc_attr($id)?>').on("click",'.next-page',function()
            {
                var page=parseInt(jQuery(this).attr('value'));
                wpvivid_log_change_page(page+1,'<?php echo esc_attr($type);?>','<?php echo esc_attr($id)?>');
            });

            jQuery('#<?php echo esc_attr($id)?>').on("click",'.last-page',function()
            {
                wpvivid_log_change_page('last','<?php echo esc_attr($type);?>','<?php echo esc_attr($id)?>');
            });

            jQuery('#<?php echo esc_attr($id)?>').on("keypress", '.current-page', function()
            {
                if(event.keyCode === 13)
                {
                    var page = jQuery(this).val();
                    wpvivid_log_change_page(page,'<?php echo esc_attr($type);?>','<?php echo esc_attr($id)?>');
                }
            });

            jQuery('#<?php echo esc_attr($id)?>').on("click",'.open-log',function()
            {
                var log=jQuery(this).attr("log");
                wpvivid_open_log(log,'<?php echo esc_attr($slug);?>');
            });

            jQuery('#<?php echo esc_attr($id)?>').on("click",'.download-log',function()
            {
                var log=jQuery(this).attr("log");
                wpvivid_download_log(log);
            });

        </script>
        <?php
    }

    public function get_log_list($type='backup')
    {
        $ret['log_list']['file']=array();
        $log=new WPvivid_Staging_Log_Free();
        $dir=$log->GetSaveLogFolder();
        $files=array();
        $error_files=array();
        $handler=opendir($dir);
        $regex='#^wpvivid.*_log.txt#';
        if($handler!==false)
        {
            while(($filename=readdir($handler))!==false)
            {
                if($filename != "." && $filename != "..")
                {
                    if(is_dir($dir.$filename))
                    {
                        continue;
                    }else{
                        if(preg_match($regex,$filename))
                        {
                            $files[$filename] = $dir.$filename;
                        }
                    }
                }
            }
            if($handler)
                @closedir($handler);
        }

        $dir.='error'.DIRECTORY_SEPARATOR;
        if(file_exists($dir))
        {
            $handler=opendir($dir);
            if($handler!==false)
            {
                while(($filename=readdir($handler))!==false)
                {
                    if($filename != "." && $filename != "..")
                    {
                        if(is_dir($dir.$filename))
                        {
                            continue;
                        }else{
                            if(preg_match($regex,$filename))
                            {
                                $error_files[$filename] = $dir.$filename;
                            }
                        }
                    }
                }
                if($handler)
                    @closedir($handler);
            }
        }


        foreach ($files as $file)
        {
            $handle = @fopen($file, "r");
            if ($handle)
            {
                $log_file=array();
                $log_file['file_name']=basename($file);
                $log_file['id']='';
                if(preg_match('/wpvivid-(.*?)_/', basename($file), $matches))
                {
                    $id= $matches[0];
                    $id=substr($id,0,strlen($id)-1);
                    $log_file['id']=$id;
                }
                $log_file['path']=$file;
                $log_file['des']='';
                $log_file['time']='';
                $log_file['error']=false;
                if(preg_match('/error/', $file))
                {
                    $log_file['result']='failed';
                }
                else
                {
                    $log_file['result']='success';
                }
                $line = fgets($handle);
                if($line!==false)
                {
                    $pos=strpos($line,'Log created: ');
                    if($pos!==false)
                    {
                        $log_file['time']=substr ($line,$pos+strlen('Log created: '));
                    }
                }
                $line = fgets($handle);
                if($line!==false)
                {
                    $pos=strpos($line,'Type: ');
                    if($pos!==false)
                    {
                        $log_file['des']=substr ($line,$pos+strlen('Type: '));
                    }
                    else
                    {
                        $log_file['des']='other';
                    }
                }
                fclose($handle);
                if(preg_match('#'.$type.'#',$log_file['des']))
                {
                    $ret['log_list']['file'][basename($file)]=$log_file;
                }
                else if($type=='other')
                {
                    if(preg_match('#scan#',$log_file['des']))
                    {
                        $ret['log_list']['file'][basename($file)]=$log_file;
                    }
                    else if(preg_match('#export#',$log_file['des']))
                    {
                        $ret['log_list']['file'][basename($file)]=$log_file;
                    }
                    else if(preg_match('#Add Remote Test Connection	#',$log_file['des']))
                    {
                        $ret['log_list']['file'][basename($file)]=$log_file;
                    }
                    else if(preg_match('#upload#',$log_file['des']))
                    {
                        $ret['log_list']['file'][basename($file)]=$log_file;
                    }
                    else if(preg_match('#import#',$log_file['des']))
                    {
                        $ret['log_list']['file'][basename($file)]=$log_file;
                    }
                    else if(preg_match('#transfer#',$log_file['des']))
                    {
                        $ret['log_list']['file'][basename($file)]=$log_file;
                    }
                    else if(preg_match('#other#',$log_file['des']))
                    {
                        $ret['log_list']['file'][basename($file)]=$log_file;
                    }
                }
            }
        }

        foreach ($error_files as $file)
        {
            $handle = @fopen($file, "r");
            if ($handle)
            {
                $log_file=array();
                $log_file['file_name']=basename($file);
                $log_file['id']='';
                if(preg_match('/wpvivid-(.*?)_/', basename($file), $matches))
                {
                    $id= $matches[0];
                    $id=substr($id,0,strlen($id)-1);
                    $log_file['id']=$id;
                }
                $log_file['path']=$file;
                $log_file['des']='';
                $log_file['time']='';
                $log_file['error']=true;
                $line = fgets($handle);
                if($line!==false)
                {
                    $pos=strpos($line,'Log created: ');
                    if($pos!==false)
                    {
                        $log_file['time']=substr ($line,$pos+strlen('Log created: '));
                    }
                }
                $line = fgets($handle);
                if($line!==false)
                {
                    $pos=strpos($line,'Type: ');
                    if($pos!==false)
                    {
                        $log_file['des']=substr ($line,$pos+strlen('Type: '));
                    }
                    else
                    {
                        $log_file['des']='other';
                    }
                }
                fclose($handle);
                if(preg_match('#'.$type.'#',$log_file['des']))
                {
                    $ret['log_list']['file'][basename($file)]=$log_file;
                }
                else if($type=='other')
                {
                    if(preg_match('#scan#',$log_file['des']))
                    {
                        $ret['log_list']['file'][basename($file)]=$log_file;
                    }
                    else if(preg_match('#export#',$log_file['des']))
                    {
                        $ret['log_list']['file'][basename($file)]=$log_file;
                    }
                    else if(preg_match('#Add Remote Test Connection	#',$log_file['des']))
                    {
                        $ret['log_list']['file'][basename($file)]=$log_file;
                    }
                    else if(preg_match('#upload#',$log_file['des']))
                    {
                        $ret['log_list']['file'][basename($file)]=$log_file;
                    }
                    else if(preg_match('#import#',$log_file['des']))
                    {
                        $ret['log_list']['file'][basename($file)]=$log_file;
                    }
                    else if(preg_match('#transfer#',$log_file['des']))
                    {
                        $ret['log_list']['file'][basename($file)]=$log_file;
                    }
                    else if(preg_match('#other#',$log_file['des']))
                    {
                        $ret['log_list']['file'][basename($file)]=$log_file;
                    }
                }
            }
        }

        $ret['log_list']['file'] =$this->sort_list($ret['log_list']['file']);

        return $ret;
    }

    public function sort_list($list)
    {
        uasort ($list,function($a, $b)
        {
            if($a['error']>$b['error'])
            {
                return -1;
            }
            else if($a['error']<$b['error'])
            {
                return 1;
            }

            if($a['time']>$b['time'])
            {
                return -1;
            }
            else if($a['time']===$b['time'])
            {
                return 0;
            }
            else
            {
                return 1;
            }
        });

        return $list;
    }

    public function get_log_list_page()
    {
        global $wpvivid_plugin;
        check_ajax_referer( 'wpvivid_ajax', 'nonce' );
        $check=current_user_can('manage_options');
        $check=apply_filters('wpvivid_ajax_check_security',$check);
        if(!$check)
        {
            die();
        }
        try
        {
            $page = sanitize_key($_POST['page']);
            $type=sanitize_text_field($_POST['type']);
            $loglist = $this->get_log_list($type);
            $table = new WPvivid_Staging_Log_List_Free();
            $table->set_log_list($loglist['log_list']['file'], $page);
            $table->prepare_items();
            ob_start();
            $table->display();
            $rows = ob_get_clean();

            $ret['result'] = 'success';
            $ret['rows'] = $rows;
            echo wp_json_encode($ret);
        }
        catch (Exception $error) {
            $message = 'An exception has occurred. class: '.get_class($error).';msg: '.$error->getMessage().';code: '.$error->getCode().';line: '.$error->getLine().';in_file: '.$error->getFile().';';
            error_log($message);
            echo wp_json_encode(array('result'=>'failed','error'=>$message));
        }
        die();
    }

    public function view_log_ex()
    {
        global $wpvivid_plugin;
        check_ajax_referer( 'wpvivid_ajax', 'nonce' );
        $check=current_user_can('manage_options');
        $check=apply_filters('wpvivid_ajax_check_security',$check);
        if(!$check)
        {
            die();
        }
        try
        {
            if (isset($_POST['log']) && !empty($_POST['log']) && is_string($_POST['log']))
            {
                $log = sanitize_text_field($_POST['log']);
                $loglist=$this->get_log_list_ex();

                if(isset($loglist['log_list']['file'][$log]))
                {
                    $log=$loglist['log_list']['file'][$log];
                }
                else
                {
                    $json['result'] = 'failed';
                    $json['error'] = __('The log not found.', 'wpvivid-backuprestore');
                    echo wp_json_encode($json);
                    die();
                }

                $path=$log['path'];

                if (!file_exists($path))
                {
                    $json['result'] = 'failed';
                    $json['error'] = __('The log not found.', 'wpvivid-backuprestore');
                    echo wp_json_encode($json);
                    die();
                }

                $file = fopen($path, 'r');

                if (!$file) {
                    $json['result'] = 'failed';
                    $json['error'] = __('Unable to open the log file.', 'wpvivid-backuprestore');
                    echo wp_json_encode($json);
                    die();
                }

                $buffer = '';
                while (!feof($file)) {
                    $buffer .= fread($file, 1024);
                }
                fclose($file);

                $json['result'] = 'success';
                $json['data'] = $buffer;
                echo wp_json_encode($json);
            } else {
                $json['result'] = 'failed';
                $json['error'] = __('Reading the log failed. Please try again.', 'wpvivid-backuprestore');
                echo wp_json_encode($json);
            }
        }
        catch (Exception $error)
        {
            $message = 'An exception has occurred. class: '.get_class($error).';msg: '.$error->getMessage().';code: '.$error->getCode().';line: '.$error->getLine().';in_file: '.$error->getFile().';';
            error_log($message);
            echo wp_json_encode(array('result'=>'failed','error'=>$message));
        }
        die();
    }

    public function get_log_list_ex()
    {
        $ret['log_list']['file']=array();
        $log=new WPvivid_Staging_Log_Free();
        $dir=$log->GetSaveLogFolder();
        $files=array();
        $error_files=array();
        $handler=opendir($dir);
        $regex='#^wpvivid.*_log.txt#';
        if($handler!==false)
        {
            while(($filename=readdir($handler))!==false)
            {
                if($filename != "." && $filename != "..")
                {
                    if(is_dir($dir.$filename))
                    {
                        continue;
                    }else{
                        if(preg_match($regex,$filename))
                        {
                            $files[$filename] = $dir.$filename;
                        }
                    }
                }
            }
            if($handler)
                @closedir($handler);
        }

        $dir.='error'.DIRECTORY_SEPARATOR;
        if(file_exists($dir))
        {
            $handler=opendir($dir);
            if($handler!==false)
            {
                while(($filename=readdir($handler))!==false)
                {
                    if($filename != "." && $filename != "..")
                    {
                        if(is_dir($dir.$filename))
                        {
                            continue;
                        }else{
                            if(preg_match($regex,$filename))
                            {
                                $error_files[$filename] = $dir.$filename;
                            }
                        }
                    }
                }
                if($handler)
                    @closedir($handler);
            }
        }


        foreach ($files as $file)
        {
            $handle = @fopen($file, "r");
            if ($handle)
            {
                $log_file=array();
                $log_file['file_name']=basename($file);
                $log_file['path']=$file;
                $log_file['des']='';
                $log_file['time']='';
                $log_file['error']=false;
                $line = fgets($handle);
                if($line!==false)
                {
                    $pos=strpos($line,'Log created: ');
                    if($pos!==false)
                    {
                        $log_file['time']=substr ($line,$pos+strlen('Log created: '));
                    }
                }
                $line = fgets($handle);
                if($line!==false)
                {
                    $pos=strpos($line,'Type: ');
                    if($pos!==false)
                    {
                        $log_file['des']=substr ($line,$pos+strlen('Type: '));
                    }
                    else
                    {
                        $log_file['des']='other';
                    }
                }
                $ret['log_list']['file'][basename($file)]=$log_file;
                fclose($handle);
            }
        }

        foreach ($error_files as $file)
        {
            $handle = @fopen($file, "r");
            if ($handle)
            {
                $log_file=array();
                $log_file['file_name']=basename($file);
                $log_file['path']=$file;
                $log_file['des']='';
                $log_file['time']='';
                $log_file['error']=true;
                $line = fgets($handle);
                if($line!==false)
                {
                    $pos=strpos($line,'Log created: ');
                    if($pos!==false)
                    {
                        $log_file['time']=substr ($line,$pos+strlen('Log created: '));
                    }
                }
                $line = fgets($handle);
                if($line!==false)
                {
                    $pos=strpos($line,'Type: ');
                    if($pos!==false)
                    {
                        $log_file['des']=substr ($line,$pos+strlen('Type: '));
                    }
                    else
                    {
                        $log_file['des']='other';
                    }
                }
                $ret['log_list']['file'][basename($file)]=$log_file;
                fclose($handle);
            }
        }

        $ret['log_list']['file'] =$this->sort_list($ret['log_list']['file']);

        return $ret;
    }

    public function download_log()
    {
        global $wpvivid_plugin;
        check_ajax_referer( 'wpvivid_ajax', 'nonce' );
        $check=current_user_can('manage_options');
        $check=apply_filters('wpvivid_ajax_check_security',$check);
        if(!$check)
        {
            die();
        }

        $admin_url=apply_filters('wpvividstg_get_admin_url', '') . 'admin.php?page=wpvividstg-log';
        try
        {
            if (isset($_REQUEST['log']))
            {
                $log = sanitize_text_field($_REQUEST['log']);
                $loglist=$this->get_log_list_ex();

                if(isset($loglist['log_list']['file'][$log]))
                {
                    $log=$loglist['log_list']['file'][$log];
                }
                else
                {
                    $message= __('The log not found.', 'wpvivid-backuprestore');
                    echo sprintf(
                        esc_html($message). '%1$stry again%2$s.',
                        '<a href="' . esc_url($admin_url) . '">',
                        '</a>'
                    );
                    //echo __($message.' <a href="'.$admin_url.'">retry</a> again.');
                    die();
                }

                $path=$log['path'];

                if (!file_exists($path))
                {
                    $message= __('The log not found.', 'wpvivid-backuprestore');
                    echo sprintf(
                        esc_html($message). '%1$stry again%2$s.',
                        '<a href="' . esc_url($admin_url) . '">',
                        '</a>'
                    );
                    //echo __($message.' <a href="'.$admin_url.'">retry</a> again.');
                    die();
                }

                if (file_exists($path))
                {
                    if (session_id())
                        session_write_close();

                    $size = filesize($path);
                    if (!headers_sent())
                    {
                        header('Content-Description: File Transfer');
                        header('Content-Type: text');
                        header('Content-Disposition: attachment; filename="' . basename($path) . '"');
                        header('Cache-Control: must-revalidate');
                        header('Content-Length: ' . $size);
                        header('Content-Transfer-Encoding: binary');
                    }

                    if ($size < 1024 * 1024 * 60) {
                        ob_end_clean();
                        readfile($path);
                        exit;
                    } else {
                        ob_end_clean();
                        $download_rate = 1024 * 10;
                        $file = fopen($path, "r");
                        while (!feof($file)) {
                            @set_time_limit(20);
                            // send the current file part to the browser
                            print fread($file, round($download_rate * 1024));
                            // flush the content to the browser
                            flush();
                            if (ob_get_level())
                            {
                                ob_end_clean();
                            }
                            // sleep one second
                            sleep(1);
                        }
                        fclose($file);
                        exit;
                    }
                }
                else
                {
                    echo sprintf(
                        'File not found. Please %1$stry again%2$s.',
                        '<a href="' . esc_url($admin_url) . '">',
                        '</a>'
                    );
                    //echo __(' file not found. please <a href="'.$admin_url.'">retry</a> again.');
                    die();
                }

            } else {
                $message = __('Reading the log failed. Please try again.', 'wpvivid-backuprestore');
                echo sprintf(
                    esc_html($message). '%1$stry again%2$s.',
                    '<a href="' . esc_url($admin_url) . '">',
                    '</a>'
                );
                //echo __($message.' <a href="'.$admin_url.'">retry</a> again.');
                die();
            }
        }
        catch (Exception $error)
        {
            $message = 'An exception has occurred. class: '.get_class($error).';msg: '.$error->getMessage().';code: '.$error->getCode().';line: '.$error->getLine().';in_file: '.$error->getFile().';';
            error_log($message);
            echo sprintf(
                'An exception has occurred. class: %1$s; msg: %2$s; code: %3$s; line: %4$s; in_file: %5$s. Please %6$stry again%7$s.',
                esc_html(get_class($error)),
                esc_html($error->getMessage()),
                esc_html($error->getCode()),
                esc_html($error->getLine()),
                esc_html($error->getFile()),
                '<a href="' . esc_url($admin_url) . '">',
                '</a>'
            );
            //echo __($message.' <a href="'.$admin_url.'">retry</a> again.');
            die();
        }
    }
}includes/staging/class-wpvivid-staging-ui-display.php000064400000270222151327705670017044 0ustar00<?php

if (!defined('WPVIVID_PLUGIN_DIR'))
{
    die;
}

if ( ! class_exists( 'WP_List_Table' ) )
{
    require_once( ABSPATH . 'wp-admin/includes/class-wp-list-table.php' );
}

class WPvivid_Staging_Custom_Select_List_Free
{
    public $parent_id;
    public $is_staging_site   = false;
    public $staging_home_path = false;
    public $custom_core_path;
    public $custom_theme_path;
    public $custom_plugin_path;
    public $custom_uploads_path;
    public $custom_content_path;
    public $custom_additional_file_path;

    public function __construct(){

    }

    public function set_parent_id($parent_id){
        $this->parent_id = $parent_id;
    }

    public function set_staging_home_path($is_staging_site=false, $staging_home_path=false){
        $this->is_staging_site   = $is_staging_site;
        $this->staging_home_path = $staging_home_path;
    }

    public function display_rows(){
        $core_check = 'checked';
        $database_check = 'checked';
        $themes_check = 'checked';
        $plugins_check = 'checked';
        $uploads_check = 'checked';
        $content_check = 'checked';
        $additional_folder_check = '';

        $theme_exclude_extension = '';
        $plugin_exclude_extension = '';
        $upload_exclude_extension = '';
        $content_exclude_extension = '';
        $additional_folder_exclude_extension = '';

        $database_part_check = 'checked="checked"';
        $file_part_check = 'checked="checked"';
        $exclude_part_check = 'checked="checked"';

        if($this->is_staging_site){
            $border_css = 'border: 1px solid #f1f1f1;';
            $checkbox_disable = '';
            $core_descript = 'If the staging site and the live site have the same version of WordPress. Then it is not necessary to copy the WordPress core files to the live site.';
            $db_descript = 'It is recommended to copy all tables of the database to the live site.';
            $themes_plugins_descript = 'The activated plugins and themes will be copied to the live site by default. The Child theme must be copied if it exists';
            $uploads_descript = 'Images and media files are stored in the Uploads directory by default. All files are copied to the live site by default. You can exclude folders you do not want to copy.';
            $contents_descript = '<strong style="text-decoration:underline;"><i>Exclude</i></strong> folders you do not want to copy to the live site, except for the wp-content/uploads folder.';
            $additional_file_descript = '<strong style="text-decoration:underline;"><i>Include</i></strong> additional files or folders you want to copy to the live site.';
        }
        else{
            $border_css = 'border: none;';
            $checkbox_disable = ' disabled';
            $core_descript = 'These are the essential files for creating a staging site.';
            $db_descript = 'The tables created by WordPress are required for the staging site. Database tables created by themes or plugins are optional.';
            $themes_plugins_descript = 'The activated plugins and themes will be copied to a staging site by default. A Child theme must be copied if it exists.';
            $uploads_descript = 'Images and media files are stored in the Uploads directory by default. All files are copied to the staging site by default. You can exclude folders you do not want to copy.';
            $contents_descript = '<strong style="text-decoration:underline;"><i>Exclude</i></strong> folders you do not want to copy to the staging site, except for the wp-content/uploads folder.';
            $additional_file_descript = '<strong style="text-decoration:underline;"><i>Include</i></strong> additional files or folders you want to copy to the staging site.';
            $options = get_option('wpvivid_staging_history', array());
            if(isset($options['additional_file_check'])) {
                $additional_folder_check = $options['additional_file_check'] == '1' ? 'checked' : '';
            }
            if(isset($options['upload_extension']) && !empty($options['upload_extension'])){
                $upload_exclude_extension = implode(",", $options['upload_extension']);
            }
            if(isset($options['content_extension']) && !empty($options['content_extension'])){
                $content_exclude_extension = implode(",", $options['content_extension']);
            }
            if(isset($options['additional_file_extension']) && !empty($options['additional_file_extension'])){
                $additional_folder_exclude_extension = implode(",", $options['additional_file_extension']);
            }
        }

        ?>
        <div>
            <span><input type="checkbox" class="wpvivid-custom-database-part" <?php echo esc_attr($database_part_check.$checkbox_disable); ?>></span>
            <span class="dashicons dashicons-admin-site-alt3 wpvivid-dashicons-blue"></span>
            <span class="wpvivid-handle-database-detail" style="cursor:pointer;"><strong>Database Will Be Copied</strong></span>
            <span class="dashicons dashicons-editor-help wpvivid-dashicons-editor-help wpvivid-tooltip">
                <div class="wpvivid-bottom">
                    <!-- The content you need -->
                    <p>Won't back up any tables or additional databases if uncheck this.</p>
                    <i></i> <!-- do not delete this line -->
                </div>
            </span>
        </div>

        <!--  files begin  -->
        <div style="margin-top:1em;">
            <span><input type="checkbox" class="wpvivid-custom-file-part" <?php echo esc_attr($file_part_check.$checkbox_disable); ?>></span>
            <span class="dashicons dashicons-portfolio wpvivid-dashicons-orange"></span>
            <span class="wpvivid-handle-file-detail" style="cursor:pointer;"><strong>Files & Folders Will Be Copied</strong></span>
            <span class="wpvivid-handle-file-detail" style="cursor:pointer;"> (</span><span class="wpvivid-total-file-size">calculating</span><span>)</span>
            <span class="dashicons dashicons-editor-help wpvivid-dashicons-editor-help wpvivid-tooltip">
                <div class="wpvivid-bottom">
                    <!-- The content you need -->
                    <p>Won't back up any files or folders if uncheck this.</p>
                    <i></i> <!-- do not delete this line -->
                </div>
            </span>
            <span class="dashicons dashicons-arrow-down-alt2 wpvivid-dashicons-grey wpvivid-handle-file-detail" style="cursor:pointer;"></span>
        </div>
        <div class="wpvivid-file-detail" style="padding-left:2em; display: none;">
            <p><span><input class="wpvivid-custom-core-check" type="checkbox" <?php echo esc_attr($core_check.$checkbox_disable); ?>><span><strong>Wordpress Core<span> (</span><span class="wpvivid-core-size">calculating</span><span>)</span>: </strong>includes <code>wp-admin</code> folder,<code>wp-includes</code> folder and all other essential files.</span></span></p>
            <p><span><input class="wpvivid-custom-themes-check" type="checkbox" <?php echo esc_attr($themes_check.$checkbox_disable); ?>><span><strong>Themes<span> (</span><span class="wpvivid-themes-size">calculating</span><span>)</span>: </strong>includes all folders of themes.</span></p>
            <p><span><input class="wpvivid-custom-plugins-check" type="checkbox" <?php echo esc_attr($plugins_check.$checkbox_disable); ?>><span><strong>Plugins<span> (</span><span class="wpvivid-plugins-size">calculating</span><span>)</span>: </strong>includes all folders of plugins.</span></p>
            <p><span><input class="wpvivid-custom-content-check" type="checkbox" <?php echo esc_attr($content_check.$checkbox_disable); ?>><span><strong>Wp-content<span> (</span><span class="wpvivid-content-size">calculating</span><span>)</span>: </strong>everything in <code>wp-content</code> <strong>except for</strong> <code>themes</code>, <code>plugins</code> and <code>uploads</code> folders.</span></span></p>
            <p><span><input class="wpvivid-custom-uploads-check" type="checkbox" <?php echo esc_attr($uploads_check.$checkbox_disable); ?>><span><strong>Uploads<span> (</span><span class="wpvivid-uploads-size">calculating</span><span>)</span>: </strong>includes images, videos, and any other files such as PDF documents, MS Word docs, and GIFs.</span></span></p>
            <p>
                <span><input class="wpvivid-custom-additional-folder-check" type="checkbox" disabled><span><strong>Additional Files/Folders<span> (</span><span>pro feature</span><span>)</span>: </strong>all folders/files in root directory of your website except for Wordpress core folders/files.</span></span>
            </p>

            <p></p>

            <div class="wpvivid-additional-folder-detail" style="display: none;">
                <div style="padding-left:2em;margin-top:1em;">
                    <div style="border-bottom:1px solid #eee;border-top:1px solid #eee;">
                        <p><span class="dashicons dashicons-lightbulb wpvivid-dashicons-orange"></span><span><code>CTRL</code> + <code>Left Click</code> to select multiple files or folders.</span></p>
                    </div>
                </div>
                <div style="width:30%;float:left;box-sizing:border-box;padding-right:0.5em;padding-left:2em;">
                    <div>
                        <p>
                            <span class="dashicons dashicons-networking wpvivid-dashicons-blue"></span>
                            <span><strong>Tree View</strong></span>
                            <span class="wpvivid-rectangle wpvivid-grey-light wpvivid-hover-blue wpvivid-refresh-include-tree">Refresh<span>
                        </p>
                    </div>
                    <div class="wpvivid-custom-additional-folder-tree-info" style="margin-top:10px;height:250px;border:1px solid #eee;padding:0.2em 0.5em;overflow:auto;">Tree Viewer</div>
                    <div style="clear:both;"></div>
                    <div style="padding:1em 0 0 0;"><input class="button-primary wpvivid-include-additional-folder-btn" type="submit" value="Include Files/Folders"></div>
                </div>
                <div style="width:70%; float:left;box-sizing:border-box;padding-left:0.5em;">
                    <div>
                        <p>
                            <span class="dashicons dashicons-portfolio wpvivid-dashicons-orange"></span>
                            <span><strong>Additional Files/Folders Will Be Backed Up</strong></span>
                        </p>
                    </div>
                    <div class="wpvivid-custom-include-additional-folder-list" style="height:250px;border:1px solid #eee;padding:0.2em 0.5em;overflow-y:auto;">
                        <?php $this->wpvivid_load_custom_exclude_list('additional-folder'); ?>
                    </div>
                    <div style="padding:1em 0 0 0;"><span class="wpvivid-rectangle wpvivid-grey-light wpvivid-hover-blue wpvivid-clear-custom-include-list" style="float:right;">Empty Included Files/Folders</span></div>
                </div>
            </div>
        </div>
        <div style="clear:both;"></div>
        <!--  files end  -->

        <div style="box-sizing:border-box; margin-top:1em;">
            <!--  exclude tree begin  -->
            <div style="margin-top:1em;">
                <span><input type="checkbox" class="wpvivid-custom-exclude-part" disabled></span>
                <span class="dashicons dashicons-portfolio wpvivid-dashicons-grey"></span>
                <span class="wpvivid-handle-tree-detail" style="cursor:pointer;"><strong>Exclude Additional Files/Folders (pro feature)</strong></span>
            </div>
            <div class="wpvivid-tree-detail" style="display: none;">
                <div style="padding-left:2em;margin-top:1em;">
                    <div style="border-bottom:1px solid #eee;border-top:1px solid #eee;">
                        <p><span class="dashicons dashicons-lightbulb wpvivid-dashicons-orange"></span><span><code>CTRL</code> + <code>Left Click</code> to select multiple files or folders.</span></p>
                    </div>
                </div>

                <div style="width:30%;float:left;box-sizing:border-box;padding-right:0.5em;padding-left:2em;">
                    <div>
                        <p>
                            <span class="dashicons dashicons-networking wpvivid-dashicons-blue"></span>
                            <span><strong>Folder Tree View</strong></span>
                            <span class="wpvivid-rectangle wpvivid-grey-light wpvivid-hover-blue wpvivid-refresh-exclude-tree">Refresh<span>
                        </p>
                    </div>
                    <div style="height:250px;">
                        <div>
                            <select name="action" class="wpvivid-custom-tree-selector" style="width:100%;border:1px solid #aaa;">
                                <option value="themes" selected>themes</option>
                                <option value="plugins">plugins</option>
                                <option value="content">wp-content</option>
                                <option value="uploads">uploads</option>
                            </select>
                        </div>
                        <div class="wpvivid-custom-exclude-tree-info" style="margin-top:10px;height:210px;border:1px solid #eee;padding:0.2em 0.5em;overflow:auto;">Tree Viewer
                        </div>
                    </div>
                    <div style="clear:both;"></div>
                    <div style="padding:1.5em 0 0 0;"><input class="button-primary wpvivid-custom-tree-exclude-btn" type="submit" value="Exclude Files/Folders"></div>
                </div>
                <div style="width:70%; float:left;box-sizing:border-box;padding-left:0.5em;">
                    <div>
                        <p>
                            <span class="dashicons dashicons-portfolio wpvivid-dashicons-orange"></span>
                            <span><strong>Excluded Files/Folders/File Types</strong></span>
                        </p>
                    </div>

                    <!-- themes -->
                    <div class="wpvivid-custom-exclude-module wpvivid-custom-exclude-themes-module">
                        <input type="text" class="wpvivid-themes-extension" style="width:100%; border:1px solid #aaa;" value="<?php echo esc_attr($theme_exclude_extension); ?>" placeholder="Exclude file types, separate by comma - for example: gif, jpg, webp, pdf" />
                    </div>
                    <div class="wpvivid-custom-exclude-module wpvivid-custom-exclude-themes-module wpvivid-custom-exclude-themes-list" style="margin-top:10px;height:210px;border:1px solid #eee;padding:0.2em 0.5em;overflow-y:auto;">
                        <?php $this->wpvivid_load_custom_exclude_list('themes'); ?>
                    </div>
                    <div class="wpvivid-custom-exclude-module wpvivid-custom-exclude-themes-module" style="padding:1em 0 0 0;"><span class="wpvivid-rectangle wpvivid-grey-light wpvivid-hover-blue wpvivid-clear-custom-exclude-list" style="float:right;">Empty Excluded Files/Folders</span></div>

                    <!-- plugins -->
                    <div class="wpvivid-custom-exclude-module wpvivid-custom-exclude-plugins-module" style="display: none;">
                        <input type="text" class="wpvivid-plugins-extension" style="width:100%; border:1px solid #aaa;" value="<?php echo esc_attr($plugin_exclude_extension); ?>" placeholder="Exclude file types, separate by comma - for example: gif, jpg, webp, pdf" />
                    </div>
                    <div class="wpvivid-custom-exclude-module wpvivid-custom-exclude-plugins-module wpvivid-custom-exclude-plugins-list" style="margin-top:10px;height:210px;border:1px solid #eee;padding:0.2em 0.5em;overflow-y:auto;display: none;">
                        <?php $this->wpvivid_load_custom_exclude_list('plugins'); ?>
                    </div>
                    <div class="wpvivid-custom-exclude-module wpvivid-custom-exclude-plugins-module" style="padding:1em 0 0 0;display: none;"><span class="wpvivid-rectangle wpvivid-grey-light wpvivid-hover-blue wpvivid-clear-custom-exclude-list" style="float:right;">Empty Excluded Files/Folders</span></div>

                    <!-- content -->
                    <div class="wpvivid-custom-exclude-module wpvivid-custom-exclude-content-module" style="display: none;">
                        <input type="text" class="wpvivid-content-extension" style="width:100%; border:1px solid #aaa;" value="<?php echo esc_attr($content_exclude_extension); ?>" placeholder="Exclude file types, separate by comma - for example: gif, jpg, webp, pdf" />
                    </div>
                    <div class="wpvivid-custom-exclude-module wpvivid-custom-exclude-content-module wpvivid-custom-exclude-content-list" style="margin-top:10px;height:210px;border:1px solid #eee;padding:0.2em 0.5em;overflow-y:auto;display: none;">
                        <?php $this->wpvivid_load_custom_exclude_list('content'); ?>
                    </div>
                    <div class="wpvivid-custom-exclude-module wpvivid-custom-exclude-content-module" style="padding:1em 0 0 0;display: none;"><span class="wpvivid-rectangle wpvivid-grey-light wpvivid-hover-blue wpvivid-clear-custom-exclude-list" style="float:right;">Empty Excluded Files/Folders</span></div>

                    <!-- uploads -->
                    <div class="wpvivid-custom-exclude-module wpvivid-custom-exclude-uploads-module" style="display: none;">
                        <input type="text" class="wpvivid-uploads-extension" style="width:100%; border:1px solid #aaa;" value="<?php echo esc_attr($upload_exclude_extension); ?>" placeholder="Exclude file types, separate by comma - for example: gif, jpg, webp, pdf" />
                    </div>
                    <div class="wpvivid-custom-exclude-module wpvivid-custom-exclude-uploads-module wpvivid-custom-exclude-uploads-list" style="margin-top:10px;height:210px;border:1px solid #eee;padding:0.2em 0.5em;overflow-y:auto;display: none;">
                        <?php $this->wpvivid_load_custom_exclude_list('uploads'); ?>
                    </div>
                    <div class="wpvivid-custom-exclude-module wpvivid-custom-exclude-uploads-module" style="padding:1em 0 0 0;display: none;"><span class="wpvivid-rectangle wpvivid-grey-light wpvivid-hover-blue wpvivid-clear-custom-exclude-list" style="float:right;">Empty Excluded Files/Folders</span></div>
                </div>

            </div>
            <div style="clear:both;"></div>
            <!--  exculde tree end  -->
        </div>
        <?php
    }

    public function wpvivid_load_custom_exclude_list($backup_type){
        $list_type = 'themes_list';
        if($backup_type == 'themes'){
            $list_type = 'themes_list';
        }
        else if($backup_type == 'plugins'){
            $list_type = 'plugins_list';
        }
        else if($backup_type == 'content'){
            $list_type = 'content_list';
        }
        else if($backup_type == 'uploads'){
            $list_type = 'uploads_list';
        }
        else if($backup_type == 'additional-folder'){
            $list_type = 'additional_file_list';
        }

        $options = get_option('wpvivid_staging_history', array());
        $ret = '';

        //fix old data
        $need_fix = false;
        if($backup_type == 'themes'){
            if(isset($options['themes_list']) && !empty($options['themes_list'])){
                foreach ($options['themes_list'] as $index => $value) {
                    if(!isset($value['type'])){
                        $need_fix = true;
                        $options['themes_list'][$value]['name'] = $value;
                        $options['themes_list'][$value]['type'] = 'dashicons dashicons-category wpvivid-dashicons-orange wpvivid-icon-16px-nopointer';
                        unset($options['themes_list'][$index]);
                    }
                }
            }
        }
        else if($backup_type == 'plugins'){
            if(isset($options['plugins_list']) && !empty($options['plugins_list'])){
                foreach ($options['plugins_list'] as $index => $value) {
                    if(!isset($value['type'])){
                        $need_fix = true;
                        $options['plugins_list'][$value]['name'] = $value;
                        $options['plugins_list'][$value]['type'] = 'dashicons dashicons-category wpvivid-dashicons-orange wpvivid-icon-16px-nopointer';
                        unset($options['plugins_list'][$index]);
                    }
                }
            }
        }

        if($need_fix){
            update_option('wpvivid_staging_history', $options, 'no');
        }

        if(isset($options[$list_type]) && !empty($options[$list_type])) {
            foreach ($options[$list_type] as $index => $value) {
                if(isset($value['type'])){
                    if($value['type'] === 'wpvivid-custom-li-folder-icon'){
                        $value['type'] = 'dashicons dashicons-category wpvivid-dashicons-orange wpvivid-icon-16px-nopointer';
                    }
                    else if($value['type'] === 'wpvivid-custom-li-file-icon'){
                        $value['type'] = 'dashicons dashicons-media-default wpvivid-dashicons-grey wpvivid-icon-16px-nopointer';
                    }

                    $class_type = $value['type'];
                    $exclude_name = $value['name'];
                    if($value['type'] === 'dashicons dashicons-category wpvivid-dashicons-orange wpvivid-icon-16px-nopointer'){
                        $type = 'folder';
                    }
                    else{
                        $type = 'file';
                    }
                }
                else{
                    $class_type = 'dashicons dashicons-category wpvivid-dashicons-orange wpvivid-icon-16px-nopointer';
                    $exclude_name = $value;
                    $type = 'folder';
                }
                echo '<div class="wpvivid-text-line" type="'.esc_attr($type).'">
                            <span class="dashicons dashicons-trash wpvivid-icon-16px wpvivid-remove-custom-exlcude-tree"></span><span class="'.esc_attr($class_type).'"></span><span class="wpvivid-text-line">'.esc_html($exclude_name).'</span>
                         </div>';
            }
        }
        return $ret;
    }

    public function load_js(){
        $core_dir = $this->is_staging_site === false ? str_replace('\\','/',get_home_path()) : str_replace('\\','/',$this->staging_home_path);
        $this->custom_core_path = $core_dir;

        $upload_dir = wp_upload_dir();
        $upload_path = $this->is_staging_site === false ?  $upload_dir['basedir'] : $this->staging_home_path.'/wp-content/uploads';
        $upload_path = str_replace('\\','/',$upload_path);
        $upload_path = $upload_path.'/';
        $this->custom_uploads_path = $upload_path;

        $content_dir = $this->is_staging_site === false ? WP_CONTENT_DIR : $this->staging_home_path.'/wp-content';
        $content_path = str_replace('\\','/',$content_dir);
        $content_path = $content_path.'/';
        $this->custom_content_path = $content_path;

        $additional_file_path = $this->is_staging_site === false ? str_replace('\\','/',get_home_path()) : str_replace('\\','/',$this->staging_home_path);
        $this->custom_additional_file_path = $additional_file_path;

        $theme_dir = $this->is_staging_site === false ? str_replace('\\','/', get_theme_root()) : str_replace('\\','/', $this->staging_home_path.'/wp-content/themes');
        $this->custom_theme_path = $theme_dir.'/';

        $plugin_dir = $this->is_staging_site === false ? str_replace('\\','/', WP_PLUGIN_DIR) : str_replace('\\','/', $this->staging_home_path.'/wp-content/plugins');
        $this->custom_plugin_path = $plugin_dir.'/';
        ?>
        <script>
            var path_arr = {};
            path_arr['core'] = '<?php echo esc_attr($this->custom_core_path); ?>';
            path_arr['content'] = '<?php echo esc_attr($this->custom_content_path); ?>';
            path_arr['uploads'] = '<?php echo esc_attr($this->custom_uploads_path); ?>';
            path_arr['themes'] = '<?php echo esc_attr($this->custom_theme_path); ?>';
            path_arr['plugins'] = '<?php echo esc_attr($this->custom_plugin_path); ?>';

            function wpvivid_handle_custom_open_close_ex(handle_obj, obj, parent_id){
                if(obj.is(":hidden")) {
                    handle_obj.each(function(){
                        if(jQuery(this).hasClass('dashicons-arrow-down-alt2')){
                            jQuery(this).removeClass('dashicons-arrow-down-alt2');
                            jQuery(this).addClass('dashicons-arrow-up-alt2');
                        }
                    });
                    obj.show();
                }
                else{
                    handle_obj.each(function(){
                        if(jQuery(this).hasClass('dashicons-arrow-up-alt2')){
                            jQuery(this).removeClass('dashicons-arrow-up-alt2');
                            jQuery(this).addClass('dashicons-arrow-down-alt2');
                        }
                    });
                    obj.hide();
                }
            }

            function wpvivid_change_custom_exclude_info(type, parent_id){
                jQuery('#'+parent_id).find('.wpvivid-custom-exclude-module').hide();
                if(type === 'themes'){
                    jQuery('#'+parent_id).find('.wpvivid-custom-exclude-themes-module').show();
                }
                else if(type === 'plugins'){
                    jQuery('#'+parent_id).find('.wpvivid-custom-exclude-plugins-module').show();
                }
                else if(type === 'content'){
                    jQuery('#'+parent_id).find('.wpvivid-custom-exclude-content-module').show();
                }
                else if(type === 'uploads'){
                    jQuery('#'+parent_id).find('.wpvivid-custom-exclude-uploads-module').show();
                }
            }

            function wpvivid_check_tree_repeat(tree_type, value, parent_id) {
                if(tree_type === 'themes'){
                    var list = 'wpvivid-custom-exclude-themes-list';
                }
                else if(tree_type === 'plugins'){
                    var list = 'wpvivid-custom-exclude-plugins-list';
                }
                else if(tree_type === 'content'){
                    var list = 'wpvivid-custom-exclude-content-list';
                }
                else if(tree_type === 'uploads'){
                    var list = 'wpvivid-custom-exclude-uploads-list';
                }
                else if(tree_type === 'additional-folder'){
                    var list = 'wpvivid-custom-include-additional-folder-list';
                }

                var brepeat = false;
                jQuery('#'+parent_id).find('.'+list+' div').find('span:eq(2)').each(function (){
                    if (value === this.innerHTML) {
                        brepeat = true;
                    }
                });
                return brepeat;
            }

            jQuery('#<?php echo esc_attr($this->parent_id); ?>').on('click', '.wpvivid-handle-base-database-detail', function(){
                var handle_obj = jQuery('#<?php echo esc_attr($this->parent_id); ?>').find('.wpvivid-handle-base-database-detail');
                var obj = jQuery('#<?php echo esc_attr($this->parent_id); ?>').find('.wpvivid-base-database-detail');
                wpvivid_handle_custom_open_close_ex(handle_obj, obj, '<?php echo esc_attr($this->parent_id); ?>');
            });

            jQuery('#<?php echo esc_attr($this->parent_id); ?>').on('click', '.wpvivid-handle-file-detail', function(){
                var handle_obj = jQuery('#<?php echo esc_attr($this->parent_id); ?>').find('.wpvivid-handle-file-detail');
                var obj = jQuery('#<?php echo esc_attr($this->parent_id); ?>').find('.wpvivid-file-detail');
                wpvivid_handle_custom_open_close_ex(handle_obj, obj, '<?php echo esc_attr($this->parent_id); ?>');
            });

            jQuery('#<?php echo esc_attr($this->parent_id); ?>').on('change', '.wpvivid-custom-tree-selector', function(){
                var value = jQuery('#<?php echo esc_attr($this->parent_id); ?>').find('.wpvivid-custom-tree-selector').val();
                jQuery('#<?php echo esc_attr($this->parent_id); ?>').find('.wpvivid-custom-exclude-tree-info').jstree("destroy").empty();
                wpvivid_change_custom_exclude_info(value, '<?php echo esc_attr($this->parent_id); ?>');
            });

            jQuery('#<?php echo esc_attr($this->parent_id); ?>').on('click', '.wpvivid-refresh-include-tree', function(){

            });

            jQuery('#<?php echo esc_attr($this->parent_id); ?>').on('click', '.wpvivid-refresh-exclude-tree', function(){

            });

            jQuery('#<?php echo esc_attr($this->parent_id); ?>').on('click', '.wpvivid-include-additional-folder-btn', function(){
                var select_folders = jQuery('#<?php echo esc_attr($this->parent_id); ?>').find('.wpvivid-custom-additional-folder-tree-info').jstree(true).get_selected(true);
                var tree_path = '<?php echo esc_attr($this->custom_additional_file_path); ?>';
                var list_obj = jQuery('#<?php echo esc_attr($this->parent_id); ?>').find('.wpvivid-custom-include-additional-folder-list');
                var tree_type = 'additional-folder';

                jQuery.each(select_folders, function (index, select_item) {
                    if (select_item.id !== tree_path) {
                        var value = select_item.id;
                        value = value.replace(tree_path, '');
                        if (!wpvivid_check_tree_repeat(tree_type, value, '<?php echo esc_attr($this->parent_id); ?>')) {
                            var class_name = select_item.icon;
                            if(class_name === 'dashicons dashicons-category wpvivid-dashicons-orange wpvivid-icon-16px-nopointer'){
                                var type = 'folder';
                            }
                            else{
                                var type = 'file';
                            }
                            var tr = "<div class='wpvivid-text-line' type='"+type+"'>" +
                                "<span class='dashicons dashicons-trash wpvivid-icon-16px wpvivid-remove-custom-exlcude-tree'></span>" +
                                "<span class='"+class_name+"'></span>" +
                                "<span class='wpvivid-text-line'>" + value + "</span>" +
                                "</div>";
                            list_obj.append(tr);
                        }
                    }
                });
            });

            jQuery('#<?php echo esc_attr($this->parent_id); ?>').on('click', '.wpvivid-remove-custom-exlcude-tree', function(){
                jQuery(this).parent().remove();
            });

            jQuery('#<?php echo esc_attr($this->parent_id); ?>').on('click', '.wpvivid-clear-custom-include-list', function(){
                jQuery('#<?php echo esc_attr($this->parent_id); ?>').find('.wpvivid-custom-include-additional-folder-list').html('');
            });

            jQuery('#<?php echo esc_attr($this->parent_id); ?>').on('click', '.wpvivid-clear-custom-exclude-list', function(){
                var tree_type = jQuery('#<?php echo esc_attr($this->parent_id); ?>').find('.wpvivid-custom-tree-selector').val();
                if(tree_type === 'themes'){
                    var list = 'wpvivid-custom-exclude-themes-list';
                }
                else if(tree_type === 'plugins'){
                    var list = 'wpvivid-custom-exclude-plugins-list';
                }
                else if(tree_type === 'content'){
                    var list = 'wpvivid-custom-exclude-content-list';
                }
                else if(tree_type === 'uploads'){
                    var list = 'wpvivid-custom-exclude-uploads-list';
                }
                jQuery('#<?php echo esc_attr($this->parent_id); ?>').find('.'+list).html('');
            });

            jQuery('#<?php echo esc_attr($this->parent_id); ?>').on('click', '.wpvivid-database-table-check', function(){
                if(jQuery(this).prop('checked')){
                    if(jQuery(this).hasClass('wpvivid-database-base-table-check')){
                        jQuery('#<?php echo esc_attr($this->parent_id); ?>').find('input:checkbox[option=base_db][name=Database]').prop('checked', true);
                    }
                    else if(jQuery(this).hasClass('wpvivid-database-other-table-check')){
                        jQuery('#<?php echo esc_attr($this->parent_id); ?>').find('input:checkbox[option=other_db][name=Database]').prop('checked', true);
                    }
                    else if(jQuery(this).hasClass('wpvivid-database-diff-prefix-table-check')){
                        jQuery('#<?php echo esc_attr($this->parent_id); ?>').find('input:checkbox[option=diff_prefix_db][name=Database]').prop('checked', true);
                    }
                }
                else{
                    if (jQuery(this).hasClass('wpvivid-database-base-table-check')) {
                        jQuery('#<?php echo esc_attr($this->parent_id); ?>').find('input:checkbox[option=base_db][name=Database]').prop('checked', false);
                    }
                    else if (jQuery(this).hasClass('wpvivid-database-other-table-check')) {
                        jQuery('#<?php echo esc_attr($this->parent_id); ?>').find('input:checkbox[option=other_db][name=Database]').prop('checked', false);
                    }
                    else if (jQuery(this).hasClass('wpvivid-database-diff-prefix-table-check')) {
                        jQuery('#<?php echo esc_attr($this->parent_id); ?>').find('input:checkbox[option=diff_prefix_db][name=Database]').prop('checked', false);
                    }
                }
            });

            jQuery('#<?php echo esc_attr($this->parent_id); ?>').on("click", 'input:checkbox[option=base_db][name=Database]', function(){
                if(jQuery(this).prop('checked')){
                    var all_check = true;
                    jQuery('#<?php echo esc_attr($this->parent_id); ?>').find('input:checkbox[option=base_db][name=Database]').each(function(){
                        if(!jQuery(this).prop('checked')){
                            all_check = false;
                        }
                    });
                    if(all_check){
                        jQuery('#<?php echo esc_attr($this->parent_id); ?>').find('.wpvivid-database-base-table-check').prop('checked', true);
                    }
                }
                else{
                    jQuery('#<?php echo esc_attr($this->parent_id); ?>').find('.wpvivid-database-base-table-check').prop('checked', false);
                }
            });

            jQuery('#<?php echo esc_attr($this->parent_id); ?>').on("click", 'input:checkbox[option=other_db][name=Database]', function(){
                if(jQuery(this).prop('checked')){
                    var all_check = true;
                    jQuery('#<?php echo esc_attr($this->parent_id); ?>').find('input:checkbox[option=other_db][name=Database]').each(function(){
                        if(!jQuery(this).prop('checked')){
                            all_check = false;
                        }
                    });
                    if(all_check){
                        jQuery('#<?php echo esc_attr($this->parent_id); ?>').find('.wpvivid-database-other-table-check').prop('checked', true);
                    }
                }
                else{
                    jQuery('#<?php echo esc_attr($this->parent_id); ?>').find('.wpvivid-database-other-table-check').prop('checked', false);
                }
            });

            jQuery('#<?php echo esc_attr($this->parent_id); ?>').on("click", 'input:checkbox[option=diff_prefix_db][name=Database]', function(){
                if(jQuery(this).prop('checked')){
                    var all_check = true;
                    jQuery('#<?php echo esc_attr($this->parent_id); ?>').find('input:checkbox[option=diff_prefix_db][name=Database]').each(function(){
                        if(!jQuery(this).prop('checked')){
                            all_check = false;
                        }
                    });
                    if(all_check){
                        jQuery('#<?php echo esc_attr($this->parent_id); ?>').find('.wpvivid-database-diff-prefix-table-check').prop('checked', true);
                    }
                }
                else{
                    jQuery('#<?php echo esc_attr($this->parent_id); ?>').find('.wpvivid-database-diff-prefix-table-check').prop('checked', false);
                }
            });

            function wpvivid_get_database_size(){
                var ajax_data = {
                    'action': 'wpvividstg_get_custom_database_size_free'
                };
                wpvivid_post_request(ajax_data, function (data) {
                    var jsonarray = jQuery.parseJSON(data);
                    if (jsonarray.result == 'success') {
                        jQuery('.wpvivid-database-size').html(jsonarray.database_size);
                    }
                    else {
                        alert(jsonarray.error);
                    }
                }, function (XMLHttpRequest, textStatus, errorThrown) {
                    var error_message = wpvivid_output_ajaxerror('retrieving the last backup log', textStatus, errorThrown);
                    alert(error_message);
                });
            }

            function wpvivid_get_files_size(){
                var ajax_data = {
                    'action': 'wpvividstg_get_custom_files_size_free'
                };
                wpvivid_post_request(ajax_data, function (data) {
                    var jsonarray = jQuery.parseJSON(data);
                    if (jsonarray.result == 'success') {
                        jQuery('.wpvivid-core-size').html(jsonarray.core_size);
                        jQuery('.wpvivid-themes-size').html(jsonarray.themes_size);
                        jQuery('.wpvivid-plugins-size').html(jsonarray.plugins_size);
                        jQuery('.wpvivid-uploads-size').html(jsonarray.uploads_size);
                        jQuery('.wpvivid-content-size').html(jsonarray.content_size);
                        jQuery('.wpvivid-additional-folder-size').html(jsonarray.additional_size);
                        jQuery('.wpvivid-total-file-size').html(jsonarray.total_file_size);
                    }
                    else {
                        alert(jsonarray.error);
                    }
                }, function (XMLHttpRequest, textStatus, errorThrown) {
                    var error_message = wpvivid_output_ajaxerror('retrieving the last backup log', textStatus, errorThrown);
                    alert(error_message);
                });
            }

            jQuery(document).ready(function () {
                wpvivid_get_database_size();
                wpvivid_get_files_size();
            });
        </script>
        <?php
    }
}

class WPvivid_Staging_Custom_MU_Select_List_Free{
    public $parent_id;
    public $is_staging_site   = false;
    public $is_sync_site      = false;
    public $staging_home_path = false;
    public $custom_core_path;
    public $custom_theme_path;
    public $custom_plugin_path;
    public $custom_uploads_path;
    public $custom_content_path;
    public $custom_additional_file_path;

    public function __construct(){

    }

    public function set_parent_id($parent_id){
        $this->parent_id = $parent_id;
    }

    public function set_staging_home_path($is_staging_site=false, $is_sync_site=false, $staging_home_path=false){
        $this->is_staging_site   = $is_staging_site;
        $this->is_sync_site      = $is_sync_site;
        $this->staging_home_path = $staging_home_path;
    }

    public function display_rows(){
        $core_check = 'checked';
        $database_check = 'checked';
        $themes_check = 'checked';
        $plugins_check = 'checked';
        $uploads_check = 'checked';
        $content_check = 'checked';
        $additional_folder_check = '';

        $theme_exclude_extension = '';
        $plugin_exclude_extension = '';
        $upload_exclude_extension = '';
        $content_exclude_extension = '';
        $additional_folder_exclude_extension = '';

        $database_part_check = 'checked="checked"';
        $file_part_check = 'checked="checked"';
        $exclude_part_check = 'checked="checked"';

        $db_descript = 'All the tables in the WordPress MU database except for subsites tables.';
        $uploads_descript = 'The folder where images and media files of the main site are stored by default. All files will be copied to the staging site by default. You can exclude folders you do not want to copy.';
        $core_descript = 'These are the essential files for creating a staging site.';
        $themes_plugins_descript = 'All the plugins and themes files used by the MU network. The activated plugins and themes will be copied to the staging site by default. A child theme must be copied if it exists.';
        $contents_descript = '<strong style="text-decoration:underline;"><i>Exclude</i></strong> folders you do not want to copy to the staging site, except for the wp-content/uploads folder.';
        $additional_file_descript = '<strong style="text-decoration:underline;"><i>Include</i></strong> additional files or folders you want to copy to the staging site.';

        ?>
        <div>
            <span><input type="checkbox" class="wpvivid-custom-database-part" <?php echo esc_attr($database_part_check); ?> disabled></span>
            <span class="dashicons dashicons-admin-site-alt3 wpvivid-dashicons-blue"></span>
            <span class="wpvivid-handle-database-detail" style="cursor:pointer;"><strong>Database Will Be Copied</strong></span>
            <span class="wpvivid-handle-database-detail" style="cursor:pointer;"> (</span><span class="wpvivid-database-size">calculating</span><span>)</span>
            <span class="dashicons dashicons-editor-help wpvivid-dashicons-editor-help wpvivid-tooltip">
                <div class="wpvivid-bottom">
                    <!-- The content you need -->
                    <p>Won't back up any tables or additional databases if uncheck this.</p>
                    <i></i> <!-- do not delete this line -->
                </div>
            </span>
        </div>

        <div class="wpvivid-database-detail" style="display: none;">
            <!--  database begin  -->
            <div style="padding-left:2em;">
                <p><span><input type="checkbox" class="wpvivid-custom-database-check" <?php echo esc_attr($database_check); ?> disabled><span class="wpvivid-handle-base-database-detail" style="cursor:pointer;"><strong>Tables In The Wordpress Database</strong></span></span></p>
            </div>
            <div style="clear:both;"></div>
            <!--  database end  -->
        </div>

        <!--  files begin  -->
        <div style="margin-top:1em;">
            <span><input type="checkbox" class="wpvivid-custom-file-part" <?php echo esc_attr($file_part_check); ?> disabled></span>
            <span class="dashicons dashicons-portfolio wpvivid-dashicons-orange"></span>
            <span class="wpvivid-handle-file-detail" style="cursor:pointer;"><strong>Files & Folders Will Be Copied</strong></span>
            <span class="wpvivid-handle-file-detail" style="cursor:pointer;"> (</span><span class="wpvivid-total-file-size">calculating</span><span>)</span>
            <span class="dashicons dashicons-editor-help wpvivid-dashicons-editor-help wpvivid-tooltip">
                <div class="wpvivid-bottom">
                    <!-- The content you need -->
                    <p>Won't back up any files or folders if uncheck this.</p>
                    <i></i> <!-- do not delete this line -->
                </div>
            </span>
            <span class="dashicons dashicons-arrow-down-alt2 wpvivid-dashicons-grey wpvivid-handle-file-detail" style="cursor:pointer;"></span>
        </div>
        <div class="wpvivid-file-detail" style="padding-left:2em; display: none;">
            <p><span><input class="wpvivid-custom-core-check" type="checkbox" <?php echo esc_attr($core_check); ?> disabled><span><strong>Wordpress Core<span> (</span><span class="wpvivid-core-size">calculating</span><span>)</span>: </strong>includes <code>wp-admin</code> folder,<code>wp-includes</code> folder and all other essential files.</span></span></p>
            <p><span><input class="wpvivid-custom-themes-check" type="checkbox" <?php echo esc_attr($themes_check); ?> disabled><span><strong>Themes<span> (</span><span class="wpvivid-themes-size">calculating</span><span>)</span>: </strong>includes all folders of themes.</span></p>
            <p><span><input class="wpvivid-custom-plugins-check" type="checkbox" <?php echo esc_attr($plugins_check); ?> disabled><span><strong>Plugins<span> (</span><span class="wpvivid-plugins-size">calculating</span><span>)</span>: </strong>includes all folders of plugins.</span></p>
            <p><span><input class="wpvivid-custom-content-check" type="checkbox" <?php echo esc_attr($content_check); ?> disabled><span><strong>Wp-content<span> (</span><span class="wpvivid-content-size">calculating</span><span>)</span>: </strong>everything in <code>wp-content</code> <strong>except for</strong> <code>themes</code>, <code>plugins</code> and <code>uploads</code> folders.</span></span></p>
            <p><span><input class="wpvivid-custom-uploads-check" type="checkbox" <?php echo esc_attr($uploads_check); ?> disabled><span><strong>Uploads<span> (</span><span class="wpvivid-uploads-size">calculating</span><span>)</span>: </strong>includes images, videos, and any other files such as PDF documents, MS Word docs, and GIFs.</span></span></p>
            <p>
                <span><input class="wpvivid-custom-additional-folder-check" type="checkbox" disabled><span><strong>Additional Files/Folders<span> (</span><span class="wpvivid-additional-folder-size">calculating</span><span>)</span>: </strong>all folders/files in root directory of your website except for Wordpress core folders/files.</span></span>
            </p>

            <p></p>

            <div class="wpvivid-additional-folder-detail" style="display: none;">
                <div style="padding-left:2em;margin-top:1em;">
                    <div style="border-bottom:1px solid #eee;border-top:1px solid #eee;">
                        <p><span class="dashicons dashicons-lightbulb wpvivid-dashicons-orange"></span><span><code>CTRL</code> + <code>Left Click</code> to select multiple files or folders.</span></p>
                    </div>
                </div>
                <div style="width:30%;float:left;box-sizing:border-box;padding-right:0.5em;padding-left:2em;">
                    <div>
                        <p>
                            <span class="dashicons dashicons-networking wpvivid-dashicons-blue"></span>
                            <span><strong>Tree View</strong></span>
                            <span class="wpvivid-rectangle wpvivid-grey-light wpvivid-hover-blue wpvivid-refresh-include-tree">Refresh<span>
                        </p>
                    </div>
                    <div class="wpvivid-custom-additional-folder-tree-info" style="margin-top:10px;height:250px;border:1px solid #eee;padding:0.2em 0.5em;overflow:auto;">Tree Viewer</div>
                    <div style="clear:both;"></div>
                    <div style="padding:1em 0 0 0;"><input class="button-primary wpvivid-include-additional-folder-btn" type="submit" value="Include Files/Folders"></div>
                </div>
                <div style="width:70%; float:left;box-sizing:border-box;padding-left:0.5em;">
                    <div>
                        <p>
                            <span class="dashicons dashicons-portfolio wpvivid-dashicons-orange"></span>
                            <span><strong>Additional Files/Folders Will Be Backed Up</strong></span>
                        </p>
                    </div>
                    <div class="wpvivid-custom-include-additional-folder-list" style="height:250px;border:1px solid #eee;padding:0.2em 0.5em;overflow-y:auto;"></div>
                    <div style="padding:1em 0 0 0;"><span class="wpvivid-rectangle wpvivid-grey-light wpvivid-hover-blue wpvivid-clear-custom-include-list" style="float:right;">Empty Included Files/Folders</span></div>
                </div>
            </div>
        </div>
        <div style="clear:both;"></div>
        <!--  files end  -->

        <div style="box-sizing:border-box; margin-top:1em;">
            <!--  exclude tree begin  -->
            <div style="margin-top:1em;">
                <span><input type="checkbox" class="wpvivid-custom-exclude-part" disabled></span>
                <span class="dashicons dashicons-portfolio wpvivid-dashicons-grey"></span>
                <span class="wpvivid-handle-tree-detail" style="cursor:pointer;"><strong>Exclude Additional Files/Folders (pro feature)</strong></span>
            </div>
            <div class="wpvivid-tree-detail" style="display: none;">
                <div style="padding-left:2em;margin-top:1em;">
                    <div style="border-bottom:1px solid #eee;border-top:1px solid #eee;">
                        <p><span class="dashicons dashicons-lightbulb wpvivid-dashicons-orange"></span><span><code>CTRL</code> + <code>Left Click</code> to select multiple files or folders.</span></p>
                    </div>
                </div>

                <div style="width:30%;float:left;box-sizing:border-box;padding-right:0.5em;padding-left:2em;">
                    <div>
                        <p>
                            <span class="dashicons dashicons-networking wpvivid-dashicons-blue"></span>
                            <span><strong>Folder Tree View</strong></span>
                            <span class="wpvivid-rectangle wpvivid-grey-light wpvivid-hover-blue wpvivid-refresh-exclude-tree">Refresh<span>
                        </p>
                    </div>
                    <div style="height:250px;">
                        <div>
                            <select name="action" class="wpvivid-custom-tree-selector" style="width:100%;border:1px solid #aaa;">
                                <option value="themes" selected>themes</option>
                                <option value="plugins">plugins</option>
                                <option value="content">wp-content</option>
                                <option value="uploads">uploads</option>
                            </select>
                        </div>
                        <div class="wpvivid-custom-exclude-tree-info" style="margin-top:10px;height:210px;border:1px solid #eee;padding:0.2em 0.5em;overflow:auto;">Tree Viewer
                        </div>
                    </div>
                    <div style="clear:both;"></div>
                    <div style="padding:1.5em 0 0 0;"><input class="button-primary wpvivid-custom-tree-exclude-btn" type="submit" value="Exclude Files/Folders"></div>
                </div>
                <div style="width:70%; float:left;box-sizing:border-box;padding-left:0.5em;">
                    <div>
                        <p>
                            <span class="dashicons dashicons-portfolio wpvivid-dashicons-orange"></span>
                            <span><strong>Excluded Files/Folders/File Types</strong></span>
                        </p>
                    </div>

                    <!-- themes -->
                    <div class="wpvivid-custom-exclude-module wpvivid-custom-exclude-themes-module">
                        <input type="text" class="wpvivid-themes-extension" style="width:100%; border:1px solid #aaa;" value="<?php echo esc_attr($theme_exclude_extension); ?>" placeholder="Exclude file types, separate by comma - for example: gif, jpg, webp, pdf" />
                    </div>
                    <div class="wpvivid-custom-exclude-module wpvivid-custom-exclude-themes-module wpvivid-custom-exclude-themes-list" style="margin-top:10px;height:210px;border:1px solid #eee;padding:0.2em 0.5em;overflow-y:auto;"></div>
                    <div class="wpvivid-custom-exclude-module wpvivid-custom-exclude-themes-module" style="padding:1em 0 0 0;"><span class="wpvivid-rectangle wpvivid-grey-light wpvivid-hover-blue wpvivid-clear-custom-exclude-list" style="float:right;">Empty Excluded Files/Folders</span></div>

                    <!-- plugins -->
                    <div class="wpvivid-custom-exclude-module wpvivid-custom-exclude-plugins-module" style="display: none;">
                        <input type="text" class="wpvivid-plugins-extension" style="width:100%; border:1px solid #aaa;" value="<?php echo esc_attr($plugin_exclude_extension); ?>" placeholder="Exclude file types, separate by comma - for example: gif, jpg, webp, pdf" />
                    </div>
                    <div class="wpvivid-custom-exclude-module wpvivid-custom-exclude-plugins-module wpvivid-custom-exclude-plugins-list" style="margin-top:10px;height:210px;border:1px solid #eee;padding:0.2em 0.5em;overflow-y:auto;display: none;"></div>
                    <div class="wpvivid-custom-exclude-module wpvivid-custom-exclude-plugins-module" style="padding:1em 0 0 0;display: none;"><span class="wpvivid-rectangle wpvivid-grey-light wpvivid-hover-blue wpvivid-clear-custom-exclude-list" style="float:right;">Empty Excluded Files/Folders</span></div>

                    <!-- content -->
                    <div class="wpvivid-custom-exclude-module wpvivid-custom-exclude-content-module" style="display: none;">
                        <input type="text" class="wpvivid-content-extension" style="width:100%; border:1px solid #aaa;" value="<?php echo esc_attr($content_exclude_extension); ?>" placeholder="Exclude file types, separate by comma - for example: gif, jpg, webp, pdf" />
                    </div>
                    <div class="wpvivid-custom-exclude-module wpvivid-custom-exclude-content-module wpvivid-custom-exclude-content-list" style="margin-top:10px;height:210px;border:1px solid #eee;padding:0.2em 0.5em;overflow-y:auto;display: none;"></div>
                    <div class="wpvivid-custom-exclude-module wpvivid-custom-exclude-content-module" style="padding:1em 0 0 0;display: none;"><span class="wpvivid-rectangle wpvivid-grey-light wpvivid-hover-blue wpvivid-clear-custom-exclude-list" style="float:right;">Empty Excluded Files/Folders</span></div>

                    <!-- uploads -->
                    <div class="wpvivid-custom-exclude-module wpvivid-custom-exclude-uploads-module" style="display: none;">
                        <input type="text" class="wpvivid-uploads-extension" style="width:100%; border:1px solid #aaa;" value="<?php echo esc_attr($upload_exclude_extension); ?>" placeholder="Exclude file types, separate by comma - for example: gif, jpg, webp, pdf" />
                    </div>
                    <div class="wpvivid-custom-exclude-module wpvivid-custom-exclude-uploads-module wpvivid-custom-exclude-uploads-list" style="margin-top:10px;height:210px;border:1px solid #eee;padding:0.2em 0.5em;overflow-y:auto;display: none;"></div>
                    <div class="wpvivid-custom-exclude-module wpvivid-custom-exclude-uploads-module" style="padding:1em 0 0 0;display: none;"><span class="wpvivid-rectangle wpvivid-grey-light wpvivid-hover-blue wpvivid-clear-custom-exclude-list" style="float:right;">Empty Excluded Files/Folders</span></div>
                </div>

            </div>
            <div style="clear:both;"></div>
            <!--  exculde tree end  -->
        </div>
        <?php
    }

    public function load_js(){
        $core_dir = $this->is_staging_site === false ? str_replace('\\','/',get_home_path()) : str_replace('\\','/',$this->staging_home_path);
        $this->custom_core_path = $core_dir;

        $upload_dir = wp_upload_dir();
        $upload_path = $this->is_staging_site === false ?  $upload_dir['basedir'] : $this->staging_home_path.'/wp-content/uploads';
        $upload_path = str_replace('\\','/',$upload_path);
        $upload_path = $upload_path.'/';
        $this->custom_uploads_path = $upload_path;

        $content_dir = $this->is_staging_site === false ? WP_CONTENT_DIR : $this->staging_home_path.'/wp-content';
        $content_path = str_replace('\\','/',$content_dir);
        $content_path = $content_path.'/';
        $this->custom_content_path = $content_path;

        $additional_file_path = $this->is_staging_site === false ? str_replace('\\','/',get_home_path()) : str_replace('\\','/',$this->staging_home_path);
        $this->custom_additional_file_path = $additional_file_path;

        $theme_dir = $this->is_staging_site === false ? str_replace('\\','/', get_theme_root()) : str_replace('\\','/', $this->staging_home_path.'/wp-content/themes');
        $this->custom_theme_path = $theme_dir.'/';

        $plugin_dir = $this->is_staging_site === false ? str_replace('\\','/', WP_PLUGIN_DIR) : str_replace('\\','/', $this->staging_home_path.'/wp-content/plugins');
        $this->custom_plugin_path = $plugin_dir.'/';
        ?>
        <script>
            var path_arr = {};
            path_arr['core'] = '<?php echo esc_attr($this->custom_core_path); ?>';
            path_arr['content'] = '<?php echo esc_attr($this->custom_content_path); ?>';
            path_arr['uploads'] = '<?php echo esc_attr($this->custom_uploads_path); ?>';
            path_arr['themes'] = '<?php echo esc_attr($this->custom_theme_path); ?>';
            path_arr['plugins'] = '<?php echo esc_attr($this->custom_plugin_path); ?>';

            function wpvivid_handle_custom_open_close_ex(handle_obj, obj, parent_id){
                if(obj.is(":hidden")) {
                    handle_obj.each(function(){
                        if(jQuery(this).hasClass('dashicons-arrow-down-alt2')){
                            jQuery(this).removeClass('dashicons-arrow-down-alt2');
                            jQuery(this).addClass('dashicons-arrow-up-alt2');
                        }
                    });
                    obj.show();
                }
                else{
                    handle_obj.each(function(){
                        if(jQuery(this).hasClass('dashicons-arrow-up-alt2')){
                            jQuery(this).removeClass('dashicons-arrow-up-alt2');
                            jQuery(this).addClass('dashicons-arrow-down-alt2');
                        }
                    });
                    obj.hide();
                }
            }

            function wpvivid_change_custom_exclude_info(type, parent_id){
                jQuery('#'+parent_id).find('.wpvivid-custom-exclude-module').hide();
                if(type === 'themes'){
                    jQuery('#'+parent_id).find('.wpvivid-custom-exclude-themes-module').show();
                }
                else if(type === 'plugins'){
                    jQuery('#'+parent_id).find('.wpvivid-custom-exclude-plugins-module').show();
                }
                else if(type === 'content'){
                    jQuery('#'+parent_id).find('.wpvivid-custom-exclude-content-module').show();
                }
                else if(type === 'uploads'){
                    jQuery('#'+parent_id).find('.wpvivid-custom-exclude-uploads-module').show();
                }
            }

            function wpvivid_check_tree_repeat(tree_type, value, parent_id) {
                if(tree_type === 'themes'){
                    var list = 'wpvivid-custom-exclude-themes-list';
                }
                else if(tree_type === 'plugins'){
                    var list = 'wpvivid-custom-exclude-plugins-list';
                }
                else if(tree_type === 'content'){
                    var list = 'wpvivid-custom-exclude-content-list';
                }
                else if(tree_type === 'uploads'){
                    var list = 'wpvivid-custom-exclude-uploads-list';
                }
                else if(tree_type === 'additional-folder'){
                    var list = 'wpvivid-custom-include-additional-folder-list';
                }

                var brepeat = false;
                jQuery('#'+parent_id).find('.'+list+' div').find('span:eq(2)').each(function (){
                    if (value === this.innerHTML) {
                        brepeat = true;
                    }
                });
                return brepeat;
            }

            jQuery('#<?php echo esc_attr($this->parent_id); ?>').on('click', '.wpvivid-handle-base-database-detail', function(){
                var handle_obj = jQuery('#<?php echo esc_attr($this->parent_id); ?>').find('.wpvivid-handle-base-database-detail');
                var obj = jQuery('#<?php echo esc_attr($this->parent_id); ?>').find('.wpvivid-base-database-detail');
                wpvivid_handle_custom_open_close_ex(handle_obj, obj, '<?php echo esc_attr($this->parent_id); ?>');
            });

            jQuery('#<?php echo esc_attr($this->parent_id); ?>').on('click', '.wpvivid-handle-file-detail', function(){
                var handle_obj = jQuery('#<?php echo esc_attr($this->parent_id); ?>').find('.wpvivid-handle-file-detail');
                var obj = jQuery('#<?php echo esc_attr($this->parent_id); ?>').find('.wpvivid-file-detail');
                wpvivid_handle_custom_open_close_ex(handle_obj, obj, '<?php echo esc_attr($this->parent_id); ?>');
            });

            jQuery('#<?php echo esc_attr($this->parent_id); ?>').on('change', '.wpvivid-custom-tree-selector', function(){
                var value = jQuery('#<?php echo esc_attr($this->parent_id); ?>').find('.wpvivid-custom-tree-selector').val();
                jQuery('#<?php echo esc_attr($this->parent_id); ?>').find('.wpvivid-custom-exclude-tree-info').jstree("destroy").empty();
                wpvivid_change_custom_exclude_info(value, '<?php echo esc_attr($this->parent_id); ?>');
            });

            jQuery('#<?php echo esc_attr($this->parent_id); ?>').on('click', '.wpvivid-refresh-include-tree', function(){

            });

            jQuery('#<?php echo esc_attr($this->parent_id); ?>').on('click', '.wpvivid-refresh-exclude-tree', function(){

            });

            jQuery('#<?php echo esc_attr($this->parent_id); ?>').on('click', '.wpvivid-include-additional-folder-btn', function(){
                var select_folders = jQuery('#<?php echo esc_attr($this->parent_id); ?>').find('.wpvivid-custom-additional-folder-tree-info').jstree(true).get_selected(true);
                var tree_path = '<?php echo esc_attr($this->custom_additional_file_path); ?>';
                var list_obj = jQuery('#<?php echo esc_attr($this->parent_id); ?>').find('.wpvivid-custom-include-additional-folder-list');
                var tree_type = 'additional-folder';

                jQuery.each(select_folders, function (index, select_item) {
                    if (select_item.id !== tree_path) {
                        var value = select_item.id;
                        value = value.replace(tree_path, '');
                        if (!wpvivid_check_tree_repeat(tree_type, value, '<?php echo esc_attr($this->parent_id); ?>')) {
                            var class_name = select_item.icon;
                            if(class_name === 'dashicons dashicons-category wpvivid-dashicons-orange wpvivid-icon-16px-nopointer'){
                                var type = 'folder';
                            }
                            else{
                                var type = 'file';
                            }
                            var tr = "<div class='wpvivid-text-line' type='"+type+"'>" +
                                "<span class='dashicons dashicons-trash wpvivid-icon-16px wpvivid-remove-custom-exlcude-tree'></span>" +
                                "<span class='"+class_name+"'></span>" +
                                "<span class='wpvivid-text-line'>" + value + "</span>" +
                                "</div>";
                            list_obj.append(tr);
                        }
                    }
                });
            });

            jQuery('#<?php echo esc_attr($this->parent_id); ?>').on('click', '.wpvivid-custom-tree-exclude-btn', function(){
                var select_folders = jQuery('#<?php echo esc_attr($this->parent_id); ?>').find('.wpvivid-custom-exclude-tree-info').jstree(true).get_selected(true);
                var tree_type = jQuery('#<?php echo esc_attr($this->parent_id); ?>').find('.wpvivid-custom-tree-selector').val();
                var tree_path = path_arr[tree_type];
                if(tree_type === 'themes'){
                    var list_obj = jQuery('#<?php echo esc_attr($this->parent_id); ?>').find('.wpvivid-custom-exclude-themes-list');
                }
                else if(tree_type === 'plugins'){
                    var list_obj = jQuery('#<?php echo esc_attr($this->parent_id); ?>').find('.wpvivid-custom-exclude-plugins-list');
                }
                else if(tree_type === 'content'){
                    var list_obj = jQuery('#<?php echo esc_attr($this->parent_id); ?>').find('.wpvivid-custom-exclude-content-list');
                }
                else if(tree_type === 'uploads'){
                    var list_obj = jQuery('#<?php echo esc_attr($this->parent_id); ?>').find('.wpvivid-custom-exclude-uploads-list');
                }

                jQuery.each(select_folders, function (index, select_item) {
                    if (select_item.id !== tree_path) {
                        var value = select_item.id;
                        value = value.replace(tree_path, '');
                        if (!wpvivid_check_tree_repeat(tree_type, value, '<?php echo esc_attr($this->parent_id); ?>')) {
                            var class_name = select_item.icon;
                            if(class_name === 'dashicons dashicons-category wpvivid-dashicons-orange wpvivid-icon-16px-nopointer'){
                                var type = 'folder';
                            }
                            else{
                                var type = 'file';
                            }
                            var tr = "<div class='wpvivid-text-line' type='"+type+"'>" +
                                "<span class='dashicons dashicons-trash wpvivid-icon-16px wpvivid-remove-custom-exlcude-tree'></span>" +
                                "<span class='"+class_name+"'></span>" +
                                "<span class='wpvivid-text-line'>" + value + "</span>" +
                                "</div>";
                            list_obj.append(tr);
                        }
                    }
                });
            });

            jQuery('#<?php echo esc_attr($this->parent_id); ?>').on('click', '.wpvivid-remove-custom-exlcude-tree', function(){
                jQuery(this).parent().remove();
            });

            jQuery('#<?php echo esc_attr($this->parent_id); ?>').on('click', '.wpvivid-clear-custom-include-list', function(){
                jQuery('#<?php echo esc_attr($this->parent_id); ?>').find('.wpvivid-custom-include-additional-folder-list').html('');
            });

            jQuery('#<?php echo esc_attr($this->parent_id); ?>').on('click', '.wpvivid-clear-custom-exclude-list', function(){
                var tree_type = jQuery('#<?php echo esc_attr($this->parent_id); ?>').find('.wpvivid-custom-tree-selector').val();
                if(tree_type === 'themes'){
                    var list = 'wpvivid-custom-exclude-themes-list';
                }
                else if(tree_type === 'plugins'){
                    var list = 'wpvivid-custom-exclude-plugins-list';
                }
                else if(tree_type === 'content'){
                    var list = 'wpvivid-custom-exclude-content-list';
                }
                else if(tree_type === 'uploads'){
                    var list = 'wpvivid-custom-exclude-uploads-list';
                }
                jQuery('#<?php echo esc_attr($this->parent_id); ?>').find('.'+list).html('');
            });

            jQuery('#<?php echo esc_attr($this->parent_id); ?>').on('click', '.wpvivid-database-table-check', function(){
                if(jQuery(this).prop('checked')){
                    if(jQuery(this).hasClass('wpvivid-database-base-table-check')){
                        jQuery('#<?php echo esc_attr($this->parent_id); ?>').find('input:checkbox[option=base_db][name=Database]').prop('checked', true);
                    }
                    else if(jQuery(this).hasClass('wpvivid-database-other-table-check')){
                        jQuery('#<?php echo esc_attr($this->parent_id); ?>').find('input:checkbox[option=other_db][name=Database]').prop('checked', true);
                    }
                    else if(jQuery(this).hasClass('wpvivid-database-diff-prefix-table-check')){
                        jQuery('#<?php echo esc_attr($this->parent_id); ?>').find('input:checkbox[option=diff_prefix_db][name=Database]').prop('checked', true);
                    }
                }
                else{
                    if (jQuery(this).hasClass('wpvivid-database-base-table-check')) {
                        jQuery('#<?php echo esc_attr($this->parent_id); ?>').find('input:checkbox[option=base_db][name=Database]').prop('checked', false);
                    }
                    else if (jQuery(this).hasClass('wpvivid-database-other-table-check')) {
                        jQuery('#<?php echo esc_attr($this->parent_id); ?>').find('input:checkbox[option=other_db][name=Database]').prop('checked', false);
                    }
                    else if (jQuery(this).hasClass('wpvivid-database-diff-prefix-table-check')) {
                        jQuery('#<?php echo esc_attr($this->parent_id); ?>').find('input:checkbox[option=diff_prefix_db][name=Database]').prop('checked', false);
                    }
                }
            });

            jQuery('#<?php echo esc_attr($this->parent_id); ?>').on("click", 'input:checkbox[option=base_db][name=Database]', function(){
                if(jQuery(this).prop('checked')){
                    var all_check = true;
                    jQuery('#<?php echo esc_attr($this->parent_id); ?>').find('input:checkbox[option=base_db][name=Database]').each(function(){
                        if(!jQuery(this).prop('checked')){
                            all_check = false;
                        }
                    });
                    if(all_check){
                        jQuery('#<?php echo esc_attr($this->parent_id); ?>').find('.wpvivid-database-base-table-check').prop('checked', true);
                    }
                }
                else{
                    jQuery('#<?php echo esc_attr($this->parent_id); ?>').find('.wpvivid-database-base-table-check').prop('checked', false);
                }
            });

            jQuery('#<?php echo esc_attr($this->parent_id); ?>').on("click", 'input:checkbox[option=other_db][name=Database]', function(){
                if(jQuery(this).prop('checked')){
                    var all_check = true;
                    jQuery('#<?php echo esc_attr($this->parent_id); ?>').find('input:checkbox[option=other_db][name=Database]').each(function(){
                        if(!jQuery(this).prop('checked')){
                            all_check = false;
                        }
                    });
                    if(all_check){
                        jQuery('#<?php echo esc_attr($this->parent_id); ?>').find('.wpvivid-database-other-table-check').prop('checked', true);
                    }
                }
                else{
                    jQuery('#<?php echo esc_attr($this->parent_id); ?>').find('.wpvivid-database-other-table-check').prop('checked', false);
                }
            });

            jQuery('#<?php echo esc_attr($this->parent_id); ?>').on("click", 'input:checkbox[option=diff_prefix_db][name=Database]', function(){
                if(jQuery(this).prop('checked')){
                    var all_check = true;
                    jQuery('#<?php echo esc_attr($this->parent_id); ?>').find('input:checkbox[option=diff_prefix_db][name=Database]').each(function(){
                        if(!jQuery(this).prop('checked')){
                            all_check = false;
                        }
                    });
                    if(all_check){
                        jQuery('#<?php echo esc_attr($this->parent_id); ?>').find('.wpvivid-database-diff-prefix-table-check').prop('checked', true);
                    }
                }
                else{
                    jQuery('#<?php echo esc_attr($this->parent_id); ?>').find('.wpvivid-database-diff-prefix-table-check').prop('checked', false);
                }
            });

            function wpvivid_get_database_size(){
                var ajax_data = {
                    'action': 'wpvividstg_get_custom_database_size_free'
                };
                wpvivid_post_request(ajax_data, function (data) {
                    var jsonarray = jQuery.parseJSON(data);
                    if (jsonarray.result == 'success') {
                        jQuery('.wpvivid-database-size').html(jsonarray.database_size);
                    }
                    else {
                        alert(jsonarray.error);
                    }
                }, function (XMLHttpRequest, textStatus, errorThrown) {
                    var error_message = wpvivid_output_ajaxerror('retrieving the last backup log', textStatus, errorThrown);
                    alert(error_message);
                });
            }

            function wpvivid_get_files_size(){
                var ajax_data = {
                    'action': 'wpvividstg_get_custom_files_size_free'
                };
                wpvivid_post_request(ajax_data, function (data) {
                    var jsonarray = jQuery.parseJSON(data);
                    if (jsonarray.result == 'success') {
                        jQuery('.wpvivid-core-size').html(jsonarray.core_size);
                        jQuery('.wpvivid-themes-size').html(jsonarray.themes_size);
                        jQuery('.wpvivid-plugins-size').html(jsonarray.plugins_size);
                        jQuery('.wpvivid-uploads-size').html(jsonarray.uploads_size);
                        jQuery('.wpvivid-content-size').html(jsonarray.content_size);
                        jQuery('.wpvivid-additional-folder-size').html(jsonarray.additional_size);
                        jQuery('.wpvivid-total-file-size').html(jsonarray.total_file_size);
                    }
                    else {
                        alert(jsonarray.error);
                    }
                }, function (XMLHttpRequest, textStatus, errorThrown) {
                    var error_message = wpvivid_output_ajaxerror('retrieving the last backup log', textStatus, errorThrown);
                    alert(error_message);
                });
            }

            jQuery(document).ready(function () {
                wpvivid_get_database_size();
                wpvivid_get_files_size();
            });
        </script>
        <?php
    }
}

class WPvivid_Staging_UI_Display_Free
{
    public $main_tab;

    public $staging_list_ui;
    public $staging_create_ui;
    public $fresh_install_ui;
    public $log_page;

    public function __construct()
    {
        include_once WPVIVID_PLUGIN_DIR . '/includes/staging/class-wpvivid-staging-list-ui-display.php';
        include_once WPVIVID_PLUGIN_DIR . '/includes/staging/class-wpvivid-staging-create-ui-display.php';
        include_once WPVIVID_PLUGIN_DIR . '/includes/staging/class-wpvivid-fresh-install-create-ui-display.php';

        $this->staging_list_ui=new WPvivid_Staging_List_UI_Display_Free();
        $this->staging_create_ui=new WPvivid_Staging_Create_UI_Display_Free();
        $this->fresh_install_ui=new WPvivid_Fresh_Install_Create_UI_Display_Free();
        $this->log_page=new WPvivid_Staging_Log_Page_Free();
    }

    public function get_staging_site_data()
    {
        if(is_multisite())
        {
            switch_to_blog(get_main_site_id());
            $staging=get_option('wpvivid_staging_data',false);
            restore_current_blog();
        }
        else
        {
            $staging=get_option('wpvivid_staging_data',false);
        }

        return $staging;
    }

    public static function wpvivid_check_site_url()
    {
        $site_url = site_url();
        $home_url = home_url();
        $site_url = untrailingslashit($site_url);
        $home_url = untrailingslashit($home_url);
        $db_site_url = '';
        $db_home_url = '';
        global $wpdb;
        $site_url_sql = $wpdb->get_results( $wpdb->prepare( "SELECT * FROM $wpdb->options WHERE option_name = %s", 'siteurl' ) );
        $home_url_sql = $wpdb->get_results( $wpdb->prepare( "SELECT * FROM $wpdb->options WHERE option_name = %s", 'home' ) );
        foreach ( $site_url_sql as $site ){
            $db_site_url = $site->option_value;
        }
        foreach ( $home_url_sql as $home ){
            $db_home_url = $home->option_value;
        }
        if($site_url !== $db_site_url || $home_url !== $db_home_url){
           echo '<div class="notice notice-warning inline"><p><strong>Warning:</strong> An inconsistency was detected between the site url, home url of the database and the actual website url. 
                                        This can cause inappropriate staging site url issues. Please change the site url and home url in the Options table of the database to the actual 
                                        url of your website. For example, if the site url and home url of the database is http://test.com, but the actual url of your website is https://test.com. 
                                        You’ll need to change the http to https.
                                                                  </p></div>';
        }
    }

    public static function wpvivid_check_login_url()
    {
        $home_url = home_url();
        global $wpdb;
        $home_url_sql = $wpdb->get_results( $wpdb->prepare( "SELECT * FROM $wpdb->options WHERE option_name = %s", 'home' ) );
        foreach ( $home_url_sql as $home ){
            $home_url = $home->option_value;
        }
        $home_url = untrailingslashit($home_url);
        $login_url = wp_login_url();
        $login_name = str_replace($home_url, '', $login_url);
        $login_name = trim($login_name, '/');
        if($login_name !== 'wp-login.php')
        {
            ?>
            <div class="notice notice-warning inline is-dismissible">
                <p>
                    <strong>Warning:</strong> We detected that the login url of your live site is not the default '/wp-login.php'. <a href="https://docs.wpvivid.com/wpvivid-staging-site-login-issue.html" target="_blank">Learn more</a>
                </p>
            </div>
            <?php
        }
    }

    public function init_page()
    {
        $options=get_option('wpvivid_staging_options',array());
        if(isset( $options['staging_request_timeout']))
        {
            $request_timeout=$options['staging_request_timeout'];
        }
        else
        {
            $request_timeout=1500;
        }
        ?>
        <div class="wrap" style="max-width:1720px;">
            <h1>
                <?php esc_html_e('WPvivid Plugins - Staging', 'wpvivid-backuprestore' ); ?>
            </h1>
            <?php self::wpvivid_check_site_url(); ?>
            <?php self::wpvivid_check_login_url(); ?>
            <script>
                function wpvivid_include_exclude_folder(type, parent_id, tree_path)
                {
                    var select_folders = '';
                    if (type === 'uploads')
                    {
                        select_folders = jQuery('#' + parent_id).find('.wpvivid-custom-uploads-tree-info').jstree(true).get_selected(true);
                        var list_obj = jQuery('#' + parent_id).find('.wpvivid-custom-exclude-uploads-list');
                    }
                    if (type === 'content')
                    {
                        select_folders = jQuery('#' + parent_id).find('.wpvivid-custom-content-tree-info').jstree(true).get_selected(true);
                        var list_obj = jQuery('#' + parent_id).find('.wpvivid-custom-exclude-content-list');
                    }
                    if (type === 'additional_file')
                    {
                        select_folders = jQuery('#' + parent_id).find('.wpvivid-custom-additional-file-tree-info').jstree(true).get_selected(true);
                        var list_obj = jQuery('#' + parent_id).find('.wpvivid-custom-include-additional-file-list');
                    }
                    jQuery.each(select_folders, function (index, select_item)
                    {
                        if (select_item.id !== tree_path)
                        {
                            var value = select_item.id;
                            value = value.replace(tree_path, '');
                            if (!wpvivid_check_custom_tree_repeat(type, value, parent_id))
                            {
                                var class_name = select_item.icon === 'jstree-folder' ? 'wpvivid-custom-li-folder-icon' : 'wpvivid-custom-li-file-icon';
                                var tr = "<ul style='margin: 0;'>" +
                                    "<li>" +
                                    "<div class='" + class_name + "'></div>" +
                                    "<div class='wpvivid-custom-li-font'>" + value + "</div>" +
                                    "<div class='wpvivid-custom-li-close' onclick='wpvivid_remove_custom_tree(this);' title='Remove' style='cursor: pointer;'>X</div>" +
                                    "</li>" +
                                    "</ul>";
                                list_obj.append(tr);
                            }
                        }
                    });
                }

                function wpvivid_check_custom_tree_repeat(type, value, parent_id)
                {
                    var brepeat = false;
                    var list_class = 'wpvivid-custom-exclude-uploads-list';
                    if (type === 'uploads')
                    {
                        list_class = 'wpvivid-custom-exclude-uploads-list';
                    }
                    if (type === 'content')
                    {
                        list_class = 'wpvivid-custom-exclude-content-list';
                    }
                    if (type === 'additional_file')
                    {
                        list_class = 'wpvivid-custom-include-additional-file-list';
                    }
                    jQuery('#' + parent_id).find('.' + list_class + ' ul').find('li div:eq(1)').each(function ()
                    {
                        if (value === this.innerHTML)
                        {
                            brepeat = true;
                        }
                    });
                    return brepeat;
                }

                function wpvivid_remove_custom_tree(obj)
                {
                    jQuery(obj).parent().parent().remove();
                }

                var staging_requet_timeout=<?php echo esc_attr($request_timeout) ?>;

                var archieve_info = {};
                archieve_info.src_db_retry    = 0;
                archieve_info.src_theme_retry = 0;
                archieve_info.des_db_retry    = 0;
                archieve_info.des_theme_retry = 0;

                function wpvivid_refresh_staging_database(parent_id, is_staging, staging_site_id) {
                    if(is_staging == '1')
                    {
                        archieve_info.des_db_retry = 0;
                    }
                    else
                    {
                        archieve_info.src_db_retry = 0;
                    }
                    var custom_database_loading = '<div class="spinner" style="margin: 0 5px 10px 0; float: left;"></div>' +
                        '<div style="float: left;">Archieving database tables</div>' +
                        '<div style="clear: both;"></div>';
                    jQuery('#' + parent_id).find('.wpvivid-custom-database-info').html('');
                    jQuery('#' + parent_id).find('.wpvivid-custom-database-info').html(custom_database_loading);
                    wpvivid_get_custom_database_tables_info(parent_id, is_staging, staging_site_id);
                }

                function wpvivid_get_custom_database_tables_info(parent_id, is_staging, staging_site_id) {
                    var id = staging_site_id;

                    var ajax_data = {
                        'action': 'wpvividstg_get_custom_database_tables_info_free',
                        'id': id,
                        'is_staging': is_staging
                    };
                    wpvivid_post_request(ajax_data, function (data)
                    {
                        var jsonarray = jQuery.parseJSON(data);
                        if (jsonarray.result === 'success')
                        {
                            jQuery('#' + parent_id).find('.wpvivid-custom-database-info').html('');
                            jQuery('#' + parent_id).find('.wpvivid-custom-database-info').html(jsonarray.html);
                        }
                    }, function (XMLHttpRequest, textStatus, errorThrown)
                    {
                        var need_retry_custom_database = false;
                        var retry_times=0;
                        if(is_staging == '1')
                        {
                            archieve_info.des_db_retry++;
                            retry_times = archieve_info.des_db_retry;
                        }
                        else{
                            archieve_info.src_db_retry++;
                            retry_times = archieve_info.src_db_retry;
                        }
                        if(retry_times < 10){
                            need_retry_custom_database = true;
                        }
                        if(need_retry_custom_database)
                        {
                            setTimeout(function()
                            {
                                wpvivid_get_custom_database_tables_info(parent_id, is_staging, staging_site_id);
                            }, 3000);
                        }
                        else{
                            var refresh_btn = '<input type="submit" class="button-primary" value="Refresh" onclick="wpvivid_refresh_staging_database(\''+parent_id+'\', \''+is_staging+'\', \''+staging_site_id+'\');">';
                            jQuery('#' + parent_id).find('.wpvivid-custom-database-info').html('');
                            jQuery('#' + parent_id).find('.wpvivid-custom-database-info').html(refresh_btn);
                        }
                    });
                }

                function load_js(parent_id, is_staging, theme_path, plugin_path, upload_path, content_path, home_path, staging_site_id = '')
                {
                    var tree_path = theme_path;

                    var path_arr = {};
                    path_arr['core'] = home_path;
                    path_arr['content'] = content_path;
                    path_arr['uploads'] = upload_path;
                    path_arr['themes'] = theme_path;
                    path_arr['plugins'] = plugin_path;

                    jQuery('#'+parent_id).on('click', '.wpvivid-custom-tree-exclude-btn', function(){
                        var select_folders = jQuery('#'+parent_id).find('.wpvivid-custom-exclude-tree-info').jstree(true).get_selected(true);
                        var tree_type = jQuery('#'+parent_id).find('.wpvivid-custom-tree-selector').val();
                        var tree_path = path_arr[tree_type];
                        if(tree_type === 'themes'){
                            var list_obj = jQuery('#'+parent_id).find('.wpvivid-custom-exclude-themes-list');
                        }
                        else if(tree_type === 'plugins'){
                            var list_obj = jQuery('#'+parent_id).find('.wpvivid-custom-exclude-plugins-list');
                        }
                        else if(tree_type === 'content'){
                            var list_obj = jQuery('#'+parent_id).find('.wpvivid-custom-exclude-content-list');
                        }
                        else if(tree_type === 'uploads'){
                            var list_obj = jQuery('#'+parent_id).find('.wpvivid-custom-exclude-uploads-list');
                        }

                        jQuery.each(select_folders, function (index, select_item) {
                            if (select_item.id !== tree_path) {
                                var value = select_item.id;
                                value = value.replace(tree_path, '');
                                if (!wpvivid_check_tree_repeat(tree_type, value, parent_id)) {
                                    var class_name = select_item.icon;
                                    if(class_name === 'dashicons dashicons-category wpvivid-dashicons-orange wpvivid-icon-16px-nopointer'){
                                        var type = 'folder';
                                    }
                                    else{
                                        var type = 'file';
                                    }
                                    var tr = "<div class='wpvivid-text-line' type='"+type+"'>" +
                                        "<span class='dashicons dashicons-trash wpvivid-icon-16px wpvivid-remove-custom-exlcude-tree'></span>" +
                                        "<span class='"+class_name+"'></span>" +
                                        "<span class='wpvivid-text-line'>" + value + "</span>" +
                                        "</div>";
                                    list_obj.append(tr);
                                }
                            }
                        });
                    });

                    if(is_staging){
                        is_staging = '1';
                    }
                    else{
                        is_staging = '0';
                    }
                    wpvivid_get_custom_database_tables_info(parent_id, is_staging, staging_site_id);
                }
            </script>
            <?php
            $data=$this->get_staging_site_data();

            $args['is_parent_tab']=1;
            $this->main_tab=new WPvivid_Tab_Page_Container();
            if($data===false)
            {
                $this->main_tab->add_tab('Staging Sites','staging_sites',array($this->staging_list_ui, 'output_staging_sites_list_page'), $args);
                $this->main_tab->add_tab('Create A Staging Site','create_staging',array($this->staging_create_ui, 'output_create_staging_site_page'), $args);

                if(!is_multisite())
                {
                    $this->main_tab->add_tab('Create A Fresh WP Install','create_fresh_install',array($this->fresh_install_ui, 'output_create_wp_page'), $args);
                }
            }
            else
            {
                $this->main_tab->add_tab('Staging Sites','staging_sites',array($this->staging_list_ui, 'output_staging'), $args);
            }
            $this->main_tab->display();
            ?>
            <script>
                function switch_staging_tab(id)
                {
                    jQuery( document ).trigger( '<?php echo esc_attr($this->main_tab->container_id) ?>-show',id);
                }
            </script>
        </div>
        <?php
    }
}includes/staging/class-wpvivid-staging-create-ui-display.php000064400000143416151327705670020311 0ustar00<?php
if (!defined('WPVIVID_PLUGIN_DIR'))
{
    die;
}

class WPvivid_Staging_Create_UI_Display_Free
{
    public function __construct()
    {

    }

    public function get_database_home_url()
    {
        $home_url = home_url();
        global $wpdb;
        $home_url_sql = $wpdb->get_results( $wpdb->prepare( "SELECT * FROM $wpdb->options WHERE option_name = %s", 'home' ) );
        foreach ( $home_url_sql as $home ){
            $home_url = $home->option_value;
        }
        return untrailingslashit($home_url);
    }

    public function output_create_staging_site_page()
    {
        update_option('wpvivid_current_running_staging_task','','no');
        update_option('wpvivid_staging_task_cancel', false,'no');
        $home_url   = $this->get_database_home_url();
        $admin_url  = admin_url();
        $admin_name = basename($admin_url);
        $admin_name = trim($admin_name, '/');

        $home_path = get_home_path();
        $staging_num = 1;
        $staging_dir = 'mystaging01';
        $staging_content_dir = 'mystaging01';
        $default_staging_site = 'mystaging01';
        while(1){
            $default_staging_site = 'mystaging'.sprintf("%02d", $staging_num);
            $staging_dir = $home_path.$default_staging_site;
            if(!file_exists($staging_dir)){
                break;
            }
            $staging_num++;
        }

        $content_dir = WP_CONTENT_DIR;
        $content_dir = str_replace('\\','/',$content_dir);
        $content_path = $content_dir.'/';
        $staging_num = 1;
        $default_content_staging_site='mystaging01';
        while(1){
            $default_content_staging_site = 'mystaging'.sprintf("%02d", $staging_num);
            $staging_dir = $content_path.$default_content_staging_site;
            if(!file_exists($staging_dir)){
                break;
            }
            $staging_num++;
        }

        global $wpdb;
        $prefix='';
        $site_id=1;
        $base_prefix=$wpdb->base_prefix;
        while(1)
        {
            if($site_id<10)
            {
                $prefix='wpvividstg0'.$site_id.'_';
            }
            else
            {
                $prefix='wpvividstg'.$site_id.'_';
            }

            $sql=$wpdb->prepare("SHOW TABLES LIKE %s;", $wpdb->esc_like($prefix) . '%');
            $result = $wpdb->get_results($sql, OBJECT_K);
            if(empty($result))
            {
                break;
            }
            $site_id++;
        }
        ?>
        <div class="postbox quickstaging">
            <div class="wpvivid-one-coloum" id="wpvivid_create_staging_step1" style="border:1px solid #f1f1f1;padding-bottom:0em; margin-top:0em;margin-bottom:1em;">
                <div class="wpvivid-one-coloum" style="background:#f5f5f5;padding-top:0em;padding-bottom:0em;display: none;">
                    <div class="wpvivid-two-col">
                        <p><span class="dashicons dashicons-awards wpvivid-dashicons-blue"></span><span><strong>Site Name: </strong></span><span class="wpvivid-staging-site-name"><?php echo esc_html($default_staging_site); ?></span></p>
                        <p><span class="dashicons dashicons-admin-site-alt3 wpvivid-dashicons-blue"></span><span><strong>Database Name: </strong></span><span class="wpvivid-staging-additional-database-name-display"><?php echo esc_html(DB_NAME); ?></span></p>
                        <p><span class="dashicons dashicons-list-view wpvivid-dashicons-blue"></span><span><strong>Table Prefix: </strong></span><span class="wpvivid-staging-table-prefix-display"><?php echo esc_html($prefix); ?></span></p>
                    </div>
                    <div class="wpvivid-two-col">
                        <!--<p><span class="dashicons dashicons-admin-site-alt3 wpvivid-dashicons-blue"></span><span><strong>Database Name:</strong></span><span>admin06</span></p>-->
                        <p><span class="dashicons dashicons-admin-home wpvivid-dashicons-blue"></span><span><strong>Home URL: </strong></span><span class="wpvivid-staging-home-url"><?php echo esc_url($home_url); ?>/</span><span class="wpvivid-staging-site-name"><?php echo esc_html($default_staging_site); ?></span></p>
                        <p><span class="dashicons  dashicons-rest-api wpvivid-dashicons-blue"></span><span><strong>Admin URL: </strong></span><span class="wpvivid-staging-home-url"><?php echo esc_url($home_url); ?>/</span><span class="wpvivid-staging-site-name"><?php echo esc_html($default_staging_site); ?></span><span>/<?php echo esc_html($admin_name); ?></span></p>
                    </div>
                </div>

                <div>
                    <div>
                        <h2 style="padding-left:1em;padding-top:0.6em; background:#f1f1f1;">
                            <span class="dashicons dashicons-portfolio wpvivid-dashicons-orange"></span>
                            <span>Directory to Install the Staging Site</span>
                        </h2>
                        <?php
                        $server_type = $_SERVER['SERVER_SOFTWARE'];
                        if(preg_match('/nginx/i', $server_type))
                        {
                            ?>
                            <div style="border:1px solid #ccc; padding:0 1em;margin-top:1em; border-radius:0.5em;">
                                <p>
                                    <span>We detected that your web server is Nginx, please add specific rewrite rules to the Nginx config file for the staging site working properly. <a href="https://docs.wpvivid.com/add-rewrite-rules-to-nginx.html">How to</a></span>
                                <p>
                                <div style="clear:both;"></div>
                            </div>
                            <?php
                        }
                        ?>
                        <p>
                            <label>
                                <input type="radio" name="choose_staging_dir" value="0" checked="checked">
                                <span>website root</span>
                            </label>
                            <label>
                                <input type="radio" name="choose_staging_dir" value="1">
                                <span>/wp-content/</span>
                            </label>
                            <label>
                                <input type="radio" name="choose_staging_dir" value="2" disabled>
                                <span>subdomain(pro feature)</span>
                            </label>
                        </p>

                        <div id="wpvivid_staging_part" style="border-left: 4px solid #007cba;padding-left:1em;">
                            <p>
                                <input type="text" id="wpvivid_staging_path" placeholder="<?php echo esc_attr($default_staging_site); ?>" value="<?php echo esc_attr($default_staging_site); ?>" onkeyup="value=value.replace(/[^a-zA-Z0-9]/g,'')" onpaste="value=value.replace(/[^\a-\z\A-\Z0-9]/g,'')"><span> Custom directory</span>
                            </p>
                            <p>
                                <span class="dashicons dashicons-admin-home wpvivid-dashicons-blue"></span><span>Home Url: </span><span class="wpvivid-staging-home-url"><?php echo esc_url($home_url); ?>/</span><span class="wpvivid-staging-site-name"><?php echo esc_html($default_staging_site); ?></span>
                                <span style="margin-left:1em;" class="dashicons dashicons-portfolio wpvivid-dashicons-blue"></span><span><strong>Directory:</strong></span>
                                <span><?php echo esc_html(untrailingslashit(ABSPATH)); ?>/</span><span class="wpvivid-staging-site-name"><?php echo esc_html($default_staging_site); ?></span>
                            </p>
                        </div>
                    </div>

                    <h2 style="padding-left:1em;padding-top:0.6em;background:#f1f1f1;">
                        <span class="dashicons dashicons-cloud wpvivid-dashicons-blue"></span>
                        <span>Choose Database to Install the Staging Site</span>
                    </h2>
                    <p>
                        <input type="text" id="wpvivid_staging_table_prefix" placeholder="<?php echo esc_attr($prefix); ?>" value="<?php echo esc_attr($prefix); ?>" onkeyup="value=value.replace(/[^a-zA-Z0-9_]/g,'')" onpaste="value=value.replace(/[^\a-\z\A-\Z0-9_]/g,'')" title="Table Prefix"> Custom Table Prefix, By default: <?php echo esc_html($prefix); ?>
                    </p>

                    <p>
                        <label>
                            <input type="radio" name="choose_staging_db" value="0" checked="">
                            <span>Install the staging site to the live site's database (Easy setup)</span>
                        </label>
                    </p>
                    <p>
                        <label>
                            <input type="radio" name="choose_staging_db" value="1">
                            <span>Install the staging site to a separate database (Expert setup)</span>
                        </label>
                    </p>
                    <p></p>
                    <div class="" id="wpvivid_additional_database_account" style="display: none;">
                        <form>
                            <p><label><input type="text" class="wpvivid-additional-database-name" autocomplete="off" placeholder="DB Name" title="DB Name" readonly></label>
                                <label><input type="text" class="wpvivid-additional-database-user" autocomplete="off" placeholder="DB Username" title="DB Username" readonly></label></p>
                            <p><label><input type="password" class="wpvivid-additional-database-pass" autocomplete="off" placeholder="Password" title="The Password of the Database Username" readonly></label>
                                <label><input type="text" class="wpvivid-additional-database-host" autocomplete="off" placeholder="localhost" title="Database Host" readonly></label></p>
                            <p><label><input class="button-primary wpvivid_setting_general_save" type="button" id="wpvivid_connect_additional_database" onclick="wpvivid_additional_database_connect_test();" value="Test Connection" readonly></label></p>
                        </form>
                    </div>
                    <div style="clear: both;"></div>
                </div>
            </div>

            <div id="wpvivid_choose_staging_content" class="wpvivid-one-coloum" style="border:1px solid #f1f1f1;padding-bottom:1em; margin-top:1em;margin-bottom:1em;">
                <h2 style="padding-left:0em;">
                    <span class="dashicons dashicons-admin-page wpvivid-dashicons-orange"></span>
                    <span>Choose What to Copy to The Staging Site</span>
                </h2>
                <p></p>
                <div>
                    <div id="wpvividstg_custom_backup_content">
                        <div id="wpvivid_custom_staging_list">
                            <?php
                            $custom_staging_list = new WPvivid_Staging_Custom_Select_List_Free();
                            $custom_staging_list ->set_parent_id('wpvivid_custom_staging_list');
                            $custom_staging_list ->set_staging_home_path();
                            $custom_staging_list ->display_rows();
                            $custom_staging_list ->load_js();
                            ?>
                        </div>
                    </div>
                    <div style="clear: both;"></div>
                </div>
            </div>

            <div id="wpvivid_create_btn" style="padding:1em 1em 0 0;">
                <div id="wpvivid_create_staging_content">
                    <input class="button-primary wpvivid_setting_general_save" id="wpvivid_create_staging" type="submit" value="Create Now" /><span> Note: Please don't refresh the page while creating a staging site.</span>
                </div>
                <div style="padding:1em 1em 0 0;">
                    <span>Tips: Please temporarily deactivate all cache, firewall and redirect plugins before creating a staging site to rule out possibilities of unknown failures.</span>
                </div>
            </div>

            <div id="wpvivid_create_staging_step2" style="display: none;">
                <div class="wpvivid-element-space-bottom">
                    <input class="button button-primary" type="button" id="wpvivid_staging_cancel" value="Cancel" />
                </div>
                <div class="postbox wpvivid-staging-log wpvivid-element-space-bottom" id="wpvivid_staging_log" style="margin-bottom: 0;"></div>
                <div class="action-progress-bar" style="margin: 10px 0 0 0; !important;">
                    <div class="action-progress-bar-percent" id="wpvivid_staging_progress_bar" style="height:24px;line-height:24px;width:0;">
                        <div style="float: left; margin-left: 4px;">0</div>
                        <div style="clear: both;"></div>
                    </div>
                </div>
            </div>
        </div>


        <script>
            <?php
            $upload_dir = wp_upload_dir();
            $upload_path = $upload_dir['basedir'];
            $upload_path = str_replace('\\','/',$upload_path);
            $upload_path = $upload_path.'/';
            $content_dir = WP_CONTENT_DIR;
            $content_path = str_replace('\\','/',$content_dir);
            $content_path = $content_path.'/';
            $home_path = str_replace('\\','/', get_home_path());
            $theme_path = str_replace('\\','/', get_theme_root());
            $theme_path = $theme_path.'/';
            $plugin_path = str_replace('\\','/', WP_PLUGIN_DIR);
            $plugin_path = $plugin_path.'/';
            ?>

            jQuery('#wpvivid_create_staging_step1').on("keyup", '#wpvivid_staging_path', function()
            {
                var value = jQuery('#wpvivid_create_staging_step1').find('input:radio[name=choose_staging_dir]:checked').val();
                if(value === '0')
                {
                    var staging_path = jQuery('#wpvivid_staging_path').val();
                    if(staging_path !== ''){
                        jQuery('.wpvivid-staging-site-name').html(staging_path);
                    }
                    else{
                        jQuery('.wpvivid-staging-site-name').html('*');
                    }
                }
                else if(value === '1')
                {
                    var staging_path = jQuery('#wpvivid_staging_path').val();
                    if(staging_path !== '')
                    {
                        jQuery('.wpvivid-staging-site-name').html('wp-content/'+staging_path);
                    }
                    else{
                        jQuery('.wpvivid-staging-site-name').html('wp-content/*');
                    }
                }
            });


            jQuery('#wpvivid_create_staging_step1').on("click", 'input:radio[name=choose_staging_db]', function(){
                if(jQuery(this).prop('checked')){
                    var value = jQuery(this).val();
                    if(value === '0'){
                        jQuery('#wpvivid_additional_database_account').hide();
                        jQuery('#wpvivid_additional_database_account').find('.wpvivid-additional-database-name').attr('readonly', true);
                        jQuery('#wpvivid_additional_database_account').find('.wpvivid-additional-database-user').attr('readonly', true);
                        jQuery('#wpvivid_additional_database_account').find('.wpvivid-additional-database-pass').attr('readonly', true);
                        jQuery('#wpvivid_additional_database_account').find('.wpvivid-additional-database-host').attr('readonly', true);
                        jQuery('.wpvivid-staging-additional-database-name-display').html('<?php echo esc_html(DB_NAME); ?>');
                    }
                    else{
                        jQuery('#wpvivid_additional_database_account').show();
                        jQuery('#wpvivid_additional_database_account').find('.wpvivid-additional-database-name').attr('readonly', false);
                        jQuery('#wpvivid_additional_database_account').find('.wpvivid-additional-database-user').attr('readonly', false);
                        jQuery('#wpvivid_additional_database_account').find('.wpvivid-additional-database-pass').attr('readonly', false);
                        jQuery('#wpvivid_additional_database_account').find('.wpvivid-additional-database-host').attr('readonly', false);
                        var additional_db_name = jQuery('.wpvivid-additional-database-name').val();
                        if(additional_db_name !== ''){
                            jQuery('.wpvivid-staging-additional-database-name-display').html(additional_db_name);
                        }
                        else{
                            jQuery('.wpvivid-staging-additional-database-name-display').html('*');
                        }
                        wpvivid_additional_database_table_prefix();
                    }
                }
            });

            var default_staging_site = '<?php echo esc_attr($default_staging_site); ?>';
            var default_content_staging_site = '<?php echo esc_attr($default_content_staging_site); ?>';
            var is_mu='<?php echo esc_attr(is_multisite()); ?>';
            jQuery('#wpvivid_create_staging_step1').on("click", 'input:radio[name=choose_staging_dir]', function() {
                if(jQuery(this).prop('checked'))
                {
                    var value = jQuery(this).val();

                    if(value === '0')
                    {
                        jQuery('.wpvivid-staging-home-url').show();
                        jQuery('#wpvivid_staging_path_part').show();
                        jQuery('#wpvivid_staging_path').val(default_staging_site);
                        var staging_path = jQuery('#wpvivid_staging_path').val();
                        if(staging_path !== '')
                        {
                            jQuery('.wpvivid-staging-site-name').html(staging_path);
                        }
                        else{
                            jQuery('.wpvivid-staging-site-name').html('*');
                        }
                    }
                    else
                    {
                        jQuery('.wpvivid-staging-home-url').show();
                        jQuery('#wpvivid_staging_path_part').show();
                        jQuery('#wpvivid_staging_path').val(default_content_staging_site);
                        var staging_path = jQuery('#wpvivid_staging_path').val();
                        if(staging_path !== '')
                        {
                            jQuery('.wpvivid-staging-site-name').html('wp-content/'+staging_path);
                        }
                        else{
                            jQuery('.wpvivid-staging-site-name').html('wp-content/*');
                        }
                    }
                }
            });

            jQuery('#wpvivid_create_staging_step1').on("keyup", '.wpvivid-additional-database-name', function(){
                var additional_db_name = jQuery(this).val();
                if(additional_db_name !== ''){
                    jQuery('.wpvivid-staging-additional-database-name-display').html(additional_db_name);
                }
                else{
                    jQuery('.wpvivid-staging-additional-database-name-display').html('*');
                }
            });

            jQuery('#wpvivid_create_staging_step1').on("keyup", '#wpvivid_staging_table_prefix', function(){
                wpvivid_additional_database_table_prefix();
            });

            function wpvivid_additional_database_table_prefix(){
                var additional_db_prefix = jQuery('#wpvivid_create_staging_step1').find('#wpvivid_staging_table_prefix').val();
                if(additional_db_prefix !== ''){
                    jQuery('#wpvivid_create_staging_step1').find('.wpvivid-staging-table-prefix-display').html(additional_db_prefix);
                }
                else{
                    jQuery('#wpvivid_create_staging_step1').find('.wpvivid-staging-table-prefix-display').html('*');
                }
            }

            jQuery('#wpvivid_create_staging_step2').on("click", '#wpvivid_staging_cancel', function(){
                wpvivid_staging_cancel();
            });

            function wpvivid_staging_cancel(){
                var ajax_data = {
                    'action': 'wpvividstg_cancel_staging_free'
                };
                jQuery('#wpvivid_staging_cancel').css({'pointer-events': 'none', 'opacity': '0.4'});
                wpvivid_post_request(ajax_data, function(data){

                }, function(XMLHttpRequest, textStatus, errorThrown) {
                    jQuery('#wpvivid_staging_cancel').css({'pointer-events': 'auto', 'opacity': '1'});
                    var error_message = wpvivid_output_ajaxerror('cancelling the staging', textStatus, errorThrown);
                    alert(error_message);
                });
            }

            jQuery('#wpvivid_create_staging').click(function() {
                var descript = '<?php esc_html_e('Click OK to start creating the staging site.', 'wpvivid-backuprestore'); ?>';
                var ret = confirm(descript);
                if(ret === true){
                    jQuery('#wpvivid_staging_notice').hide();
                    wpvivid_start_staging();
                }
            });

            jQuery('#wpvivid_mu_create_staging').click(function() {
                var descript = '<?php esc_html_e('Click OK to start creating the staging site.', 'wpvivid-backuprestore'); ?>';
                var ret = confirm(descript);
                if(ret === true){
                    jQuery('#wpvivid_staging_notice').hide();
                    wpvivid_start_staging();
                }
            });

            jQuery('#wpvivid_mu_single_create_staging').click(function() {
                var descript = '<?php esc_html_e('Click OK to start creating the staging site.', 'wpvivid-backuprestore'); ?>';
                var ret = confirm(descript);
                if(ret === true){
                    jQuery('#wpvivid_staging_notice').hide();
                    wpvivid_start_staging();
                }
            });

            function wpvivid_recreate_staging(){
                jQuery('#wpvivid_choose_staging_content').show();
                jQuery('#wpvivid_create_btn').show();
                jQuery('#wpvivid_create_staging_step2').hide();
            }

            function wpvivid_create_custom_json(parent_id){
                var json = {};
                //exclude
                json['exclude_custom'] = '0';
                json['folder_check_ex'] = '0';
                //core
                json['core_check'] = '1';
                json['core_list'] = Array();

                //themes
                json['themes_check'] = '1';
                json['themes_list'] = {};
                json['themes_extension'] = '';

                //plugins
                json['plugins_check'] = '1';
                json['plugins_list'] = {};
                json['plugins_extension'] = '';

                //content
                json['content_check'] = '1';
                json['content_list'] = {};
                json['content_extension'] = '';

                //uploads
                json['uploads_check'] = '1';
                json['uploads_list'] = {};
                json['upload_extension'] = '';

                //additional folders/files
                json['additional_file_check'] = '0';
                json['additional_file_list'] = {};

                //database
                json['database_list'] = Array();
                json['database_check'] = '1';

                return json;
            }

            function wpvivid_create_staging_lock_unlock(action){
                if(action === 'lock'){
                    jQuery('#wpvivid_create_staging_step1').find('input').attr('disabled', true);
                    jQuery('#wpvivid_staging_list').find('div.wpvivid-delete-staging-site').css({'pointer-events': 'none', 'opacity': '0.4'});
                }
                else{
                    jQuery('#wpvivid_create_staging_step1').find('input').attr('disabled', false);
                    jQuery('#wpvivid_staging_list').find('div.wpvivid-delete-staging-site').css({'pointer-events': 'auto', 'opacity': '1'});
                }
            }

            function wpvivid_check_staging_additional_folder_valid(parent_id){
                var check_status = false;
                if(jQuery('#'+parent_id).find('.wpvivid-custom-additional-file-check').prop('checked')){
                    jQuery('#'+parent_id).find('.wpvivid-custom-include-additional-file-list ul').find('li div:eq(1)').each(function () {
                        check_status = true;
                    });
                    if(check_status === false){
                        alert('Please select at least one item under the additional files/folder option, or deselect the option.');
                    }
                }
                else{
                    check_status = true;
                }
                return check_status;
            }

            function wpvivid_check_backup_option_avail(parent_id, check_database_item)
            {
                var check_status = true;

                //check is backup db or files
                var has_select_db_file = false;
                if(jQuery('#'+parent_id).find('.wpvivid-custom-database-part').prop('checked')){
                    has_select_db_file = true;
                    var has_db_item = false;
                    if(jQuery('#'+parent_id).find('.wpvivid-custom-database-check').prop('checked')){
                        has_db_item = true;
                        var has_local_table_item = false;
                        if(!check_database_item){
                            has_local_table_item = true;
                        }
                        jQuery('#'+parent_id).find('input:checkbox[name=Database]').each(function(index, value){
                            if(jQuery(this).prop('checked')){
                                has_local_table_item = true;
                            }
                        });
                        if(!has_local_table_item){
                            check_status = false;
                            alert('Please select at least one table to back up. Or, deselect the option \'Tables In The Wordpress Database\' under the option \'Databases Will Be Backed up\'.');
                            return check_status;
                        }
                    }
                    if(!has_db_item){
                        check_status = false;
                        alert('Please select at least one option from \'Tables In The Wordpress Database\' and \'Additional Databases\' under the option \'Databases Will Be Backed up\'. Or, deselect the option \'Databases Will Be Backed up\'.');
                        return check_status;
                    }
                }
                if(jQuery('#'+parent_id).find('.wpvivid-custom-file-part').prop('checked')){
                    has_select_db_file = true;
                    var has_file_item = false;
                    if(jQuery('#'+parent_id).find('.wpvivid-custom-core-check').prop('checked')){
                        has_file_item = true;
                    }
                    if(jQuery('#'+parent_id).find('.wpvivid-custom-themes-check').prop('checked')){
                        has_file_item = true;
                    }
                    if(jQuery('#'+parent_id).find('.wpvivid-custom-plugins-check').prop('checked')){
                        has_file_item = true;
                    }
                    if(jQuery('#'+parent_id).find('.wpvivid-custom-content-check').prop('checked')){
                        has_file_item = true;
                    }
                    if(jQuery('#'+parent_id).find('.wpvivid-custom-uploads-check').prop('checked')){
                        has_file_item = true;
                    }
                    if(jQuery('#'+parent_id).find('.wpvivid-custom-additional-folder-check').prop('checked')){
                        has_file_item = true;
                        var has_additional_folder = false;
                        jQuery('#'+parent_id).find('.wpvivid-custom-include-additional-folder-list div').find('span:eq(2)').each(function(){
                            has_additional_folder = true;
                        });
                        if(!has_additional_folder){
                            check_status = false;
                            alert('Please select at least one additional file or folder under the option \'Files/Folders Will Be Backed up\', Or, deselect the option \'Additional Files/Folders\'.');
                            return check_status;
                        }
                    }
                    if(!has_file_item){
                        check_status = false;
                        alert('Please select at least one option under the option \'Files/Folders Will Be Backed up\'. Or, deselect the option \'Files/Folders Will Be Backed up\'.');
                        return check_status;
                    }
                }
                if(!has_select_db_file){
                    check_status = false;
                    alert('Please select at least one option from \'Databases Will Be Backed up\' and \'Files/Folders Will Be Backed up\'.');
                    return check_status;
                }

                return check_status;
            }

            function wpvivid_start_staging()
            {
                var staging_root_dir='0';
                jQuery('#wpvivid_create_staging_step1').find('input:radio[name=choose_staging_dir]').each(function ()
                {
                    if (jQuery(this).prop('checked'))
                    {
                        staging_root_dir = jQuery(this).val();
                    }
                });

                var path='';

                path=jQuery('#wpvivid_staging_path').val();

                if(path === '')
                {
                    alert('A site name is required.');
                    return;
                }

                var table_prefix=jQuery('#wpvivid_staging_table_prefix').val();

                if(table_prefix === '')
                {
                    alert('Table Prefix is required.');
                    return;
                }

                var additional_database_json = {};

                var additional_database_option = '0';
                jQuery('#wpvivid_create_staging_step1').find('input:radio[name=choose_staging_db]').each(function ()
                {
                    if (jQuery(this).prop('checked')) {
                        additional_database_option = jQuery(this).val();
                    }
                });

                if (additional_database_option === '1')
                {
                    additional_database_json['additional_database_check'] = '1';
                    additional_database_json['additional_database_info'] = {};
                    additional_database_json['additional_database_info']['db_user'] = jQuery('.wpvivid-additional-database-user').val();
                    additional_database_json['additional_database_info']['db_pass'] = jQuery('.wpvivid-additional-database-pass').val();
                    additional_database_json['additional_database_info']['db_host'] = jQuery('.wpvivid-additional-database-host').val();
                    additional_database_json['additional_database_info']['db_name'] = jQuery('.wpvivid-additional-database-name').val();
                    if (additional_database_json['additional_database_info']['db_name'] === '') {
                        alert('Database Name is required.');
                        return;
                    }
                    if (additional_database_json['additional_database_info']['db_user'] === '') {
                        alert('Database User is required.');
                        return;
                    }
                    if (additional_database_json['additional_database_info']['db_host'] === '') {
                        alert('Database Host is required.');
                        return;
                    }
                }
                else {
                    additional_database_json['additional_database_check'] = '0';
                }
                var additional_database_info = JSON.stringify(additional_database_json);

                var ajax_data =
                    {
                        'action': 'wpvividstg_check_staging_dir_free',
                        'root_dir':staging_root_dir,
                        'path': path,
                        'table_prefix': table_prefix,
                        'additional_db': additional_database_info
                    };
                wpvivid_post_request(ajax_data, function (data)
                {
                    var jsonarray = jQuery.parseJSON(data);
                    if (jsonarray.result === 'failed')
                    {
                        alert(jsonarray.error);
                    }
                    else
                    {
                        var ajax_data =
                            {
                                'action': 'wpvividstg_check_filesystem_permissions_free',
                                'root_dir':staging_root_dir,
                                'path': path
                            };
                        wpvivid_post_request(ajax_data, function (data)
                        {
                            var jsonarray = jQuery.parseJSON(data);
                            if (jsonarray.result === 'failed')
                            {
                                alert(jsonarray.error);
                            }
                            else
                            {
                                jQuery('#wpvivid_staging_log').html("");
                                jQuery('#wpvivid_staging_progress_bar').css('width', '0%');
                                jQuery('#wpvivid_staging_progress_bar').find('div').eq(0).html('0%');
                                var custom_dir_json = wpvivid_create_custom_json('wpvivid_custom_staging_list');
                                var custom_dir = JSON.stringify(custom_dir_json);
                                var check_select = true;

                                wpvivid_create_staging_lock_unlock('lock');

                                var ajax_data = {
                                    'action': 'wpvividstg_start_staging_free',
                                    'path': path,
                                    'table_prefix': table_prefix,
                                    'custom_dir': custom_dir,
                                    'additional_db': additional_database_info,
                                    'root_dir':staging_root_dir
                                };

                                jQuery('#wpvivid_choose_staging_content').hide();
                                jQuery('#wpvivid_create_btn').hide();
                                jQuery('#wpvivid_create_staging_step2').show();
                                wpvivid_post_request(ajax_data, function (data)
                                {
                                    setTimeout(function () {
                                        wpvivid_get_staging_progress();
                                    }, staging_requet_timeout);
                                }, function (XMLHttpRequest, textStatus, errorThrown)
                                {
                                    jQuery('#wpvivid_choose_staging_content').hide();
                                    jQuery('#wpvivid_create_btn').hide();
                                    jQuery('#wpvivid_create_staging_step2').show();
                                    setTimeout(function () {
                                        wpvivid_get_staging_progress();
                                    }, staging_requet_timeout);
                                });
                            }
                        }, function (XMLHttpRequest, textStatus, errorThrown) {
                            var error_message = wpvivid_output_ajaxerror('creating staging site', textStatus, errorThrown);
                            alert(error_message);
                        });
                    }
                }, function (XMLHttpRequest, textStatus, errorThrown) {
                    var error_message = wpvivid_output_ajaxerror('creating staging site', textStatus, errorThrown);
                    alert(error_message);
                });
            }

            function wpvivid_restart_staging() {
                var ajax_data = {
                    'action':'wpvividstg_start_staging_free',
                };

                wpvivid_post_request(ajax_data, function(data)
                {
                    setTimeout(function()
                    {
                        wpvivid_get_staging_progress();
                    }, staging_requet_timeout);
                }, function(XMLHttpRequest, textStatus, errorThrown)
                {
                    setTimeout(function()
                    {
                        wpvivid_get_staging_progress();
                    }, staging_requet_timeout);
                });
            }

            function wpvivid_get_staging_progress() {
                console.log(staging_requet_timeout);
                var ajax_data = {
                    'action':'wpvividstg_get_staging_progress_free',
                };

                wpvivid_post_request(ajax_data, function(data)
                {
                    try
                    {
                        var jsonarray = jQuery.parseJSON(data);
                        if (jsonarray.result === 'success')
                        {
                            var log_data = jsonarray.log;
                            jQuery('#wpvivid_staging_log').html("");
                            while (log_data.indexOf('\n') >= 0)
                            {
                                var iLength = log_data.indexOf('\n');
                                var log = log_data.substring(0, iLength);
                                log_data = log_data.substring(iLength + 1);
                                var insert_log = "<div style=\"clear:both;\">" + log + "</div>";
                                jQuery('#wpvivid_staging_log').append(insert_log);
                                var div = jQuery('#wpvivid_staging_log');
                                div[0].scrollTop = div[0].scrollHeight;
                            }
                            jQuery('#wpvivid_staging_progress_bar').css('width', jsonarray.percent + '%');
                            jQuery('#wpvivid_staging_progress_bar').find('div').eq(0).html(jsonarray.percent + '%');
                            if(jsonarray.continue)
                            {
                                if(jsonarray.need_restart)
                                {
                                    wpvivid_restart_staging();
                                }
                                else
                                {
                                    setTimeout(function()
                                    {
                                        wpvivid_get_staging_progress();
                                    }, staging_requet_timeout);
                                }
                            }
                            else{
                                if(typeof jsonarray.completed !== 'undefined' && jsonarray.completed){
                                    jQuery('#wpvivid_staging_cancel').css({'pointer-events': 'auto', 'opacity': '1'});
                                    wpvivid_create_staging_lock_unlock('unlock');
                                    jQuery('#wpvivid_create_staging_step2').hide();
                                    location.href='<?php echo esc_url(apply_filters('wpvividstg_get_admin_url', '')) . 'admin.php?page=wpvivid-staging'; ?>';
                                }
                                else if(typeof jsonarray.error !== 'undefined' && jsonarray.error){
                                    wpvivid_create_staging_lock_unlock('unlock');
                                    var insert_log = "<div style=\"clear:both;\"><a style=\"cursor: pointer;\" onclick=\"wpvivid_recreate_staging();\">Create a staging site</a></div>";
                                    jQuery('#wpvivid_staging_log').append(insert_log);
                                    var div = jQuery('#wpvivid_staging_log');
                                    div[0].scrollTop = div[0].scrollHeight;
                                }
                                else if(typeof jsonarray.is_cancel !== 'undefined' && jsonarray.is_cancel){
                                    var staging_site_info = {};
                                    staging_site_info['staging_path'] = jsonarray.staging_path;
                                    staging_site_info['staging_additional_db'] = jsonarray.staging_additional_db;
                                    staging_site_info['staging_additional_db_user'] = jsonarray.staging_additional_db_user;
                                    staging_site_info['staging_additional_db_pass'] = jsonarray.staging_additional_db_pass;
                                    staging_site_info['staging_additional_db_host'] = jsonarray.staging_additional_db_host;
                                    staging_site_info['staging_additional_db_name'] = jsonarray.staging_additional_db_name;
                                    staging_site_info['staging_table_prefix'] = jsonarray.staging_table_prefix;
                                    staging_site_info = JSON.stringify(staging_site_info);
                                    ajax_data = {
                                        'action': 'wpvividstg_delete_cancel_staging_site_free',
                                        'staging_site_info': staging_site_info
                                    };
                                    wpvivid_post_request(ajax_data, function (data) {
                                        jQuery('#wpvivid_staging_cancel').css({'pointer-events': 'auto', 'opacity': '1'});
                                        wpvivid_create_staging_lock_unlock('unlock');
                                        jQuery('#wpvivid_choose_staging_content').show();
                                        jQuery('#wpvivid_create_btn').show();
                                        jQuery('#wpvivid_create_staging_step2').hide();
                                        try {
                                            var jsonarray = jQuery.parseJSON(data);
                                            if (jsonarray !== null) {
                                                if (jsonarray.result === 'success') {
                                                }
                                                else {
                                                    alert(jsonarray.error);
                                                }
                                            }
                                            else {
                                            }
                                        }
                                        catch (e) {
                                        }
                                    }, function (XMLHttpRequest, textStatus, errorThrown) {
                                        wpvivid_create_staging_lock_unlock('unlock');
                                        jQuery('#wpvivid_choose_staging_content').show();
                                        jQuery('#wpvivid_create_btn').show();
                                        jQuery('#wpvivid_create_staging_step2').hide();
                                        var error_message = wpvivid_output_ajaxerror('deleting staging site', textStatus, errorThrown);
                                        alert(error_message);
                                    });
                                }
                                else{
                                    jQuery('#wpvivid_staging_cancel').css({'pointer-events': 'auto', 'opacity': '1'});
                                    wpvivid_create_staging_lock_unlock('unlock');
                                    jQuery('#wpvivid_choose_staging_content').show();
                                    jQuery('#wpvivid_create_btn').show();
                                    jQuery('#wpvivid_create_staging_step2').hide();
                                }
                            }
                        }
                        else if (jsonarray.result === 'failed')
                        {
                            wpvivid_create_staging_lock_unlock('unlock');
                            jQuery('#wpvivid_choose_staging_content').show();
                            jQuery('#wpvivid_create_btn').show();
                            jQuery('#wpvivid_create_staging_step2').hide();
                            alert(jsonarray.error);
                        }
                    }
                    catch(err){
                        setTimeout(function()
                        {
                            wpvivid_get_staging_progress();
                        }, 3000);
                    }

                }, function(XMLHttpRequest, textStatus, errorThrown)
                {
                    setTimeout(function()
                    {
                        wpvivid_get_staging_progress();
                    }, 3000);
                });
            }

            function wpvivid_additional_database_connect_test(){
                var db_user = jQuery('.wpvivid-additional-database-user').val();
                var db_pass = jQuery('.wpvivid-additional-database-pass').val();
                var db_host = jQuery('.wpvivid-additional-database-host').val();
                var db_name = jQuery('.wpvivid-additional-database-name').val();
                if(db_name !== ''){
                    if(db_user !== ''){
                        if(db_host !== ''){
                            var db_json = {};
                            db_json['db_user'] = db_user;
                            db_json['db_pass'] = db_pass;
                            db_json['db_host'] = db_host;
                            db_json['db_name'] = db_name;
                            var db_connect_info = JSON.stringify(db_json);
                            var ajax_data = {
                                'action': 'wpvividstg_test_additional_database_connect_free',
                                'database_info': db_connect_info
                            };
                            jQuery('#wpvivid_connect_additional_database').css({
                                'pointer-events': 'none',
                                'opacity': '0.4'
                            });
                            wpvivid_post_request(ajax_data, function (data) {
                                jQuery('#wpvivid_connect_additional_database').css({
                                    'pointer-events': 'auto',
                                    'opacity': '1'
                                });
                                try {
                                    var jsonarray = jQuery.parseJSON(data);
                                    if (jsonarray !== null) {
                                        if (jsonarray.result === 'success') {
                                            alert('Connection success.')
                                        }
                                        else {
                                            alert(jsonarray.error);
                                        }
                                    }
                                    else {
                                        alert('Connection Failed. Please check the credentials you entered and try again.');
                                    }
                                }
                                catch (e) {
                                    alert('Connection Failed. Please check the credentials you entered and try again.');
                                }
                            }, function (XMLHttpRequest, textStatus, errorThrown) {
                                jQuery('#wpvivid_connect_additional_database').css({
                                    'pointer-events': 'auto',
                                    'opacity': '1'
                                });
                                jQuery(obj).css({'pointer-events': 'auto', 'opacity': '1'});
                                var error_message = wpvivid_output_ajaxerror('connecting database', textStatus, errorThrown);
                                alert(error_message);
                            });
                        }
                        else{
                            alert('Database Host is required.');
                        }
                    }
                    else{
                        alert('Database User is required.');
                    }
                }
                else{
                    alert('Database Name is required.');
                }
            }
        </script>
        <?php
    }
}includes/staging/class-wpvivid-staging-sites-list.php000064400000406014151327705670017064 0ustar00<?php

if (!defined('WPVIVID_PLUGIN_DIR'))
{
    die;
}

class WPvivid_Staging_List extends WP_List_Table
{
    public $list;
    public $page_num;
    public $parent;

    public function __construct( $args = array() )
    {
        global $wpdb;
        parent::__construct(
            array(
                'plural' => 'staging',
                'screen' => 'staging',
            )
        );
    }

    public function set_parent($parent)
    {
        $this->parent=$parent;
    }

    public function set_list($list)
    {
        $this->list=$list;
    }

    protected function get_table_classes() {
        return array( 'widefat', 'plugins', $this->_args['plural'] );
    }

    public function get_columns()
    {
        $posts_columns = array();

        $posts_columns['pic']  ='';
        $posts_columns['info'] = '';

        return $posts_columns;
    }

    function prepare_items()
    {
        $columns = $this->get_columns();
        $hidden = array('pic', 'info');
        $sortable = array();
        $this->_column_headers = array($columns, $hidden, $sortable);
        $total_items =sizeof($this->list);
    }

    public function has_items()
    {
        return !empty($this->list);
    }

    protected function _column_pic( $item, $classes, $data, $primary )
    {
        if(isset($item['site']['fresh_install']))
        {
            $url=esc_url(WPVIVID_PLUGIN_IMAGES_URL.'staging/Fresh-list.png');
        }
        else
        {
            $url=esc_url(WPVIVID_PLUGIN_IMAGES_URL.'staging/living-site.png');
        }

        echo '<td class="column-primary" style="margin: 10px;">
                    <div>
                          <div style="margin:auto; width:100px; height:100px; right:50%;">
                            <img src="'.esc_url($url).'">
                          </div>
                          <div class="'.esc_attr($item['id']).'" style="margin-top:10px;">
                            <div class="wpvivid-delete-staging-site" style="margin: auto;width: 70px;background-color:#f1f1f1; padding-top:4px;padding-bottom:4px; cursor:pointer;text-align:center;" title="Delete the stating site">Delete</div>
                          </div>           
                     </div>
              </td>';
    }

    protected function _column_info( $item, $classes, $data, $primary ){
        $home_url = home_url();
        global $wpdb;
        $home_url_sql = $wpdb->get_results( $wpdb->prepare( "SELECT * FROM $wpdb->options WHERE option_name = %s", 'home' ) );
        foreach ( $home_url_sql as $home ){
            $home_url = $home->option_value;
        }
        $home_url = untrailingslashit($home_url);

        $admin_url  = apply_filters('wpvividstg_get_admin_url', '');
        if(isset($item['site']['mu_single']))
        {
            $admin_url =admin_url();
        }
        $admin_name = str_replace($home_url, '', $admin_url);
        $admin_name = trim($admin_name, '/');

        if(isset($item['site']['prefix']) && !empty($item['site']['prefix'])){
            $prefix = $item['site']['prefix'];
            if(isset($item['site']['db_connect']['dbname']) && !empty($item['site']['db_connect']['dbname'])){
                $db_name = $item['site']['db_connect']['dbname'];
            }
            else{
                $db_name = DB_NAME;
            }
        }
        else{
            $prefix = 'N/A';
            $db_name = 'N/A';
        }
        if(isset($item['site']['path']) && !empty($item['site']['path'])){
            $site_dir = $item['site']['path'];
        }
        else{
            $site_dir = 'N/A';
        }
        if(isset($item['site']['home_url']) && !empty($item['site']['home_url'])){
            $site_url = esc_url($item['site']['home_url']);
            $admin_url = esc_url($item['site']['home_url'].'/'.$admin_name.'/');
            $site_url_link = '<a href="'.esc_url($site_url).'" target="_blank">'.$site_url.'</a>';
            $admin_url_link = '<a href="'.esc_url($admin_url).'" target="_blank">'.$admin_url.'</a>';
        }
        else{
            $site_url_link = 'N/A';
            $admin_url_link = 'N/A';
        }

        if(isset($item['site']['fresh_install']))
        {
            $copy_btn='Copy the Fresh Install to Live';
            $update_btn='Update the Fresh Install';
            $site_url='Fresh Install URL';
            $admin_url='Fresh Install Admin URL';
            $tip_text='Tips: Click the \'Copy the Fresh Install to Live\' button above to migrate the fresh install to your live site. Click the \'Update the Fresh Install\' button to update the live site to the fresh install.';
            $class_btn='fresh-install';
        }
        else
        {
            $copy_btn='Copy the Staging Site to Live';
            $update_btn='Update the Staging Site';
            $site_url='Staging Site URL';
            $admin_url='Staging Site Admin URL';
            $tip_text='Tips: Click the \'Copy the Staging Site to Live\' button above to migrate the staging site to your live site. Click the \'Update the Staging Site\' button to update the live site to the staging site.';
            $class_btn='staging-site';
        }

        if(isset($item['site']['mu_single']) && $item['site']['mu_single'] == true){
            $mu_single_class = 'mu-single';
        }
        else{
            $mu_single_class = '';
        }

        echo '<td class="column-description desc" colspan="2">
                        <div style="border-left:4px solid #00a0d2;padding-left:10px;float:left;">
                            <div style="height:20px;display:block;float:left;"><span class="wpvivid-element-space-right"><strong>'.esc_url($site_url).':</strong></span><span class="wpvivid-element-space-right">';
        if($site_url_link=="'N/A'")
        {
            echo esc_html($site_url_link);
        }
        else
        {
            $site_url = esc_url($item['site']['home_url']);
            echo '<a href="'.esc_url($site_url).'" target="_blank">'.esc_html($site_url).'</a>';
        }
                            echo '</span></div>
                            <div style="height:20px;display:block;float:left;"><span class="wpvivid-element-space-right"><strong>'.esc_url($admin_url).':</strong></span><span class="wpvivid-element-space-right">';
        if($admin_url_link=="'N/A'")
        {
            echo esc_html($admin_url_link);
        }
        else
        {
            $admin_url = esc_url($item['site']['home_url'].'/'.$admin_name.'/');
            $admin_url_link = '<a href="'.esc_url($admin_url).'" target="_blank">'.esc_html($admin_url).'</a>';
        }
        echo '</span></div>
                        </div>
                        <div style="clear:both"></div>
                        <div style="border-left:4px solid #00a0d2;padding-left:10px;float:left;">
                            <div style="height:20px;display:block;float:left;"><span class="wpvivid-element-space-right"><strong>Database:</strong></span><span class="wpvivid-element-space-right">'.esc_html($db_name).'</span></div>
                            <div style="height:20px;display:block;float:left;"><span class="wpvivid-element-space-right"><strong>Table Prefix:</strong></span><span class="wpvivid-element-space-right">'.esc_html($prefix).'</span></div>
                            <div style="height:20px;display:block;float:left;"><span class="wpvivid-element-space-right"><strong>Site Directory:</strong></span><span class="wpvivid-element-space-right">'.esc_html($site_dir).'</span></div>
                        </div>
                        <div style="clear:both"></div>
                        <div class="wpvivid-copy-staging-to-live-block '.esc_attr($class_btn).' '.esc_attr($mu_single_class).'" style="margin-top: 10px;">
                            <div>
                                <input class="button-primary wpvivid-copy-staging-to-live '.esc_attr($class_btn).' '.esc_attr($mu_single_class).'" type="button" value="'.esc_attr($copy_btn).'" style="margin-right: 10px;" />
                                <input class="button-primary wpvivid-update-live-to-staging '.esc_attr($class_btn).' '.esc_attr($mu_single_class).'" type="button" value="'.esc_attr($update_btn).'" />
                            </div>
                            <div style="border: 1px solid #f1f1f1; border-radius: 6px; margin-top: 10px;padding:5px;"><span>'.esc_html($tip_text).'</span></div>
                        </div>
                    </td>';
    }

    public function display_rows()
    {
        $this->_display_rows( $this->list );
    }

    private function _display_rows( $list )
    {
        foreach ( $list as $key=>$item)
        {
            $item['id']=$key;
            $this->single_row($item);
        }
    }

    public function single_row($item)
    {
        if(isset($item['site']['path']) && !empty($item['site']['path'])){
            $staging_site_name = basename($item['site']['path']);
        }
        else{
            $staging_site_name = 'N/A';
        }

        if(isset($item['site']['fresh_install']))
        {
            $text='Fresh Install Name';
        }
        else
        {
            $text='Staging Site Name';
        }

        if(isset($item['db_connect']['old_site_url']))
        {
            $live_domain = $item['db_connect']['old_site_url'];
        }
        else{
            $live_domain = 'N/A';
        }

        ?>
        <tr class="<?php echo esc_attr($item['id']); ?>">
            <td class="column-primary" style="border-top:1px solid #f1f1f1; border-bottom:1px solid #f1f1f1;" colspan="3" >
                <span><strong><?php echo esc_html($text); ?>: </strong></span><span><?php echo esc_html($staging_site_name); ?></span>
                <?php
                if(isset($item['site']['mu_single']))
                {
                    $site_id=$item['site']['mu_single_site_id'];
                    $site_url=get_site_url($site_id);
                    ?>
                    <span style="margin-left: 20px;"><strong>Live Site: </strong></span><span><?php echo esc_html($site_url); ?></span>
                    <?php
                }
                else{
                    ?>
                    <span style="margin-left: 20px;"><strong>Live Site: </strong></span><span><?php echo esc_html($live_domain); ?></span>
                    <?php
                }
                ?>
            </td>
        </tr>
        <tr id="<?php echo esc_attr($item['id']); ?>" class="<?php echo esc_attr($item['id']); ?>">
            <?php $this->single_row_columns( $item ); ?>
        </tr>
        <?php
    }

    public function display() {
        $singular = $this->_args['singular'];

        $this->screen->render_screen_reader_content( 'heading_list' );
        ?>
        <table class="wp-list-table <?php echo esc_attr(implode( ' ', $this->get_table_classes() )); ?>" style="border: 1px solid #f1f1f1; border-top: none;">
            <thead>
            <tr>
                <?php $this->print_column_headers(); ?>
            </tr>
            </thead>

            <tbody id="the-list"
                <?php
                if ( $singular ) {
                    echo esc_attr(" data-wp-lists='list:$singular'");
                }
                ?>
            >
            <?php $this->display_rows_or_placeholder(); ?>
            </tbody>

            <tfoot>
            <tr>
                <?php $this->print_column_headers( false ); ?>
            </tr>
            </tfoot>

        </table>
        <?php
    }

    public function display_js()
    {
        ?>
        <script>

        </script>
        <?php
    }
}

class WPvivid_Staging_MU_Site_List_Free extends WP_List_Table
{
    public $list;
    public $type;
    public $page_num;
    public $parent;

    public function __construct( $args = array() )
    {
        global $wpdb;
        parent::__construct(
            array(
                'plural' => 'staging_mu_site',
                'screen' => 'staging_mu_site',
            )
        );
    }

    public function set_parent($parent)
    {
        $this->parent=$parent;
    }

    public function set_list($list,$type,$page_num=1)
    {
        $this->list=$list;
        $this->type=$type;
        $this->page_num=$page_num;
    }

    protected function get_table_classes()
    {
        return array( 'widefat striped' );
    }

    public function get_columns()
    {
        $sites_columns = array(
            'cb'          => '<input type="checkbox" />',
            'blogname'    => __( 'Subsite URL', 'wpvivid-backuprestore' ),
            'tables_folders'=>__( 'Subsite Tables/Folders', 'wpvivid-backuprestore' ),
            'title' => __( 'Subsite Title', 'wpvivid-backuprestore' ),
            'description'  => __( 'Subsite Description', 'wpvivid-backuprestore')
        );

        return $sites_columns;
    }

    public function get_pagenum()
    {
        if($this->page_num=='first')
        {
            $this->page_num=1;
        }
        else if($this->page_num=='last')
        {
            $this->page_num=$this->_pagination_args['total_pages'];
        }
        $pagenum = $this->page_num ? $this->page_num : 0;

        if ( isset( $this->_pagination_args['total_pages'] ) && $pagenum > $this->_pagination_args['total_pages'] )
        {
            $pagenum = $this->_pagination_args['total_pages'];
        }

        return max( 1, $pagenum );
    }

    public function column_cb( $subsite )
    {
        $subsite_id = get_object_vars($subsite)["blog_id"];
        $blogname = get_object_vars($subsite)["domain"].get_object_vars($subsite)["path"];
        ?>
        <label class="screen-reader-text" for="blog_<?php echo esc_attr($subsite_id); ?>">
            <?php
            printf( 'Select %s', esc_html($blogname) );
            ?>
        </label>
        <input type="checkbox" name="<?php echo esc_attr( $this->type ); ?>" value="<?php echo esc_attr( $subsite_id ); ?>" checked />
        <?php
    }

    public function column_id( $subsite )
    {
        $subsite_id = get_object_vars($subsite)["blog_id"];
        echo esc_attr($subsite_id);
    }

    public function column_blogname( $subsite )
    {
        $subsite_id = get_object_vars($subsite)["blog_id"];
        $blogname    = untrailingslashit( get_object_vars($subsite)['domain'] . get_object_vars($subsite)['path'] );
        ?>
        <strong>
            <a href="<?php echo esc_url( network_admin_url( 'site-info.php?id=' .$subsite_id ) ); ?>" class="edit"><?php echo esc_html($blogname); ?></a>
        </strong>
        <?php
    }

    public function column_tables_folders( $subsite )
    {
        $subsite_id = get_object_vars($subsite)["blog_id"];
        $disable='';
        /*if( $this->type=='copy_mu_site')
        {
            $disable='';
        }
        else
        {
            $disable='disabled';
        }*/
        ?>
        <label>
            <input type="checkbox" name="<?php echo esc_attr( $this->type ); ?>_tables" value="<?php echo esc_attr( $subsite_id ); ?>" checked <?php echo esc_attr( $disable ); ?>/>
            Tables /
        </label>
        <label>
            <input type="checkbox" name="<?php echo esc_attr( $this->type ); ?>_folders" value="<?php echo esc_attr( $subsite_id ); ?>" checked <?php echo esc_attr( $disable ); ?>/>
            Folders
        </label>
        <?php
    }

    public function column_title( $subsite )
    {
        switch_to_blog( get_object_vars($subsite)["blog_id"] );
        echo esc_html( get_option( 'blogname' ) ) ;
        restore_current_blog();
    }

    public function column_description( $subsite ) {
        switch_to_blog( get_object_vars($subsite)["blog_id"] );
        echo esc_html(  get_option( 'blogdescription ' ) ) ;
        restore_current_blog();
    }

    public function has_items()
    {
        return !empty($this->list);
    }

    public function prepare_items()
    {
        $columns = $this->get_columns();
        $hidden = array();
        $sortable = array();
        $this->_column_headers = array($columns, $hidden, $sortable);

        $total_items =sizeof($this->list);

        $this->set_pagination_args(
            array(
                'total_items' => $total_items,
                'per_page'    => 10,
            )
        );
    }

    public function display_rows()
    {
        $this->_display_rows( $this->list );
    }

    private function _display_rows( $list )
    {
        $page=$this->get_pagenum();

        $page_list=$list;
        $temp_page_list=array();

        $count=0;
        while ( $count<$page )
        {
            $temp_page_list = array_splice( $page_list, 0, 10);
            $count++;
        }

        foreach ( $temp_page_list as $key=>$item)
        {
            $this->single_row($item);
        }
    }

    public function single_row($item)
    {
        ?>
        <tr>
            <?php $this->single_row_columns( $item ); ?>
        </tr>
        <?php
    }

    protected function pagination( $which )
    {
        if ( empty( $this->_pagination_args ) )
        {
            return;
        }

        $total_items     = $this->_pagination_args['total_items'];
        $total_pages     = $this->_pagination_args['total_pages'];
        $infinite_scroll = false;
        if ( isset( $this->_pagination_args['infinite_scroll'] ) )
        {
            $infinite_scroll = $this->_pagination_args['infinite_scroll'];
        }

        if ( 'top' === $which && $total_pages > 1 )
        {
            $this->screen->render_screen_reader_content( 'heading_pagination' );
        }

        $output = '<span class="displaying-num">' . sprintf( _n( '%s item', '%s items', $total_items, 'wpvivid-backuprestore' ), number_format_i18n( $total_items ) ) . '</span>';

        $current              = $this->get_pagenum();

        $page_links = array();

        $total_pages_before = '<span class="paging-input">';
        $total_pages_after  = '</span></span>';

        $disable_first = $disable_last = $disable_prev = $disable_next = false;

        if ( $current == 1 ) {
            $disable_first = true;
            $disable_prev  = true;
        }
        if ( $current == 2 ) {
            $disable_first = true;
        }
        if ( $current == $total_pages ) {
            $disable_last = true;
            $disable_next = true;
        }
        if ( $current == $total_pages - 1 ) {
            $disable_last = true;
        }

        if ( $disable_first ) {
            $page_links[] = '<span class="tablenav-pages-navspan button disabled" aria-hidden="true">&laquo;</span>';
        } else {
            $page_links[] = sprintf(
                "<div class='first-page button'><span class='screen-reader-text'>%s</span><span aria-hidden='true'>%s</span></div>",
                __( 'First page', 'wpvivid-backuprestore' ),
                '&laquo;'
            );
        }

        if ( $disable_prev ) {
            $page_links[] = '<span class="tablenav-pages-navspan button disabled" aria-hidden="true">&lsaquo;</span>';
        } else {
            $page_links[] = sprintf(
                "<div class='prev-page button' value='%s'><span class='screen-reader-text'>%s</span><span aria-hidden='true'>%s</span></div>",
                $current,
                __( 'Previous page', 'wpvivid-backuprestore' ),
                '&lsaquo;'
            );
        }

        if ( 'bottom' === $which ) {
            $html_current_page  = $current;
            $total_pages_before = '<span class="screen-reader-text">' . __( 'Current Page', 'wpvivid-backuprestore' ) . '</span><span id="table-paging" class="paging-input"><span class="tablenav-paging-text">';
        } else {
            $html_current_page = sprintf(
                "%s<input class='current-page' type='text' name='paged' value='%s' size='%d' aria-describedby='table-paging' /><span class='tablenav-paging-text'>",
                '<label class="screen-reader-text">' . __( 'Current Page', 'wpvivid-backuprestore' ) . '</label>',
                $current,
                strlen( $total_pages )
            );
        }
        $html_total_pages = sprintf( "<span class='total-pages'>%s</span>", number_format_i18n( $total_pages ) );
        $page_links[]     = $total_pages_before . sprintf( _x( '%1$s of %2$s', 'paging', 'wpvivid-backuprestore' ), $html_current_page, $html_total_pages ) . $total_pages_after;

        if ( $disable_next ) {
            $page_links[] = '<span class="tablenav-pages-navspan button disabled" aria-hidden="true">&rsaquo;</span>';
        } else {
            $page_links[] = sprintf(
                "<div class='next-page button' value='%s'><span class='screen-reader-text'>%s</span><span aria-hidden='true'>%s</span></div>",
                $current,
                __( 'Next page', 'wpvivid-backuprestore' ),
                '&rsaquo;'
            );
        }

        if ( $disable_last ) {
            $page_links[] = '<span class="tablenav-pages-navspan button disabled" aria-hidden="true">&raquo;</span>';
        } else {
            $page_links[] = sprintf(
                "<div class='last-page button'><span class='screen-reader-text'>%s</span><span aria-hidden='true'>%s</span></div>",
                __( 'Last page', 'wpvivid-backuprestore' ),
                '&raquo;'
            );
        }

        $pagination_links_class = 'pagination-links';
        if ( ! empty( $infinite_scroll ) ) {
            $pagination_links_class .= ' hide-if-js';
        }
        $output .= "\n<span class='$pagination_links_class'>" . join( "\n", $page_links ) . '</span>';

        if ( $total_pages ) {
            $page_class = $total_pages < 2 ? ' one-page' : '';
        } else {
            $page_class = ' no-pages';
        }
        $this->_pagination = "<div class='tablenav-pages{$page_class}'>$output</div>";

        echo $this->_pagination;
    }

    protected function display_tablenav( $which ) {
        $css_type = '';
        if ( 'top' === $which ) {
            wp_nonce_field( 'bulk-' . $this->_args['plural'] );
            $css_type = 'margin: 0 0 10px 0';
        }
        else if( 'bottom' === $which ) {
            $css_type = 'margin: 10px 0 0 0';
        }

        $total_pages     = $this->_pagination_args['total_pages'];
        if ( $total_pages >1)
        {
            ?>
            <div class="tablenav <?php echo esc_attr( $which ); ?>" style="<?php echo esc_attr($css_type); ?>">
                <?php
                $this->extra_tablenav( $which );
                $this->pagination( $which );
                ?>

                <br class="clear" />
            </div>
            <?php
        }
    }

    public function display() {
        $singular = $this->_args['singular'];

        $this->display_tablenav( 'top' );

        $this->screen->render_screen_reader_content( 'heading_list' );
        ?>
        <table class="wp-list-table <?php echo esc_attr(implode( ' ', $this->get_table_classes() )); ?>" >
            <thead>
            <tr>
                <?php $this->print_column_headers(); ?>
            </tr>
            </thead>

            <tbody id="the-list"
                <?php
                if ( $singular ) {
                    echo esc_attr(" data-wp-lists='list:$singular'");
                }
                ?>
            >
            <?php $this->display_rows_or_placeholder(); ?>
            </tbody>

            <tfoot>
            <tr>
                <?php $this->print_column_headers( false ); ?>
            </tr>
            </tfoot>

        </table>
        <?php
    }
}

class WPvivid_Staging_MU_Single_Site_List_Free extends WP_List_Table
{
    public $list;
    public $type;
    public $page_num;
    public $parent;

    public function __construct( $args = array() )
    {
        global $wpdb;
        parent::__construct(
            array(
                'plural' => 'staging_mu_site',
                'screen' => 'staging_mu_site',
            )
        );
    }

    public function set_parent($parent)
    {
        $this->parent=$parent;
    }

    public function set_list($list,$type,$page_num=1)
    {
        $this->list=$list;
        $this->type=$type;
        $this->page_num=$page_num;
    }

    protected function get_table_classes()
    {
        return array( 'widefat striped' );
    }

    public function get_columns()
    {
        $sites_columns = array(
            'cb'          => ' ',
            'blogname'    => __( 'Subsite URL', 'wpvivid-backuprestore' ),
            //'tables_folders'=>__( 'Subsite Tables/Folders', 'wpvivid-backuprestore' ),
            'title' => __( 'Subsite Title', 'wpvivid-backuprestore' ),
            'description'  => __( 'Subsite Description', 'wpvivid-backuprestore' )
        );

        return $sites_columns;
    }

    public function get_pagenum()
    {
        if($this->page_num=='first')
        {
            $this->page_num=1;
        }
        else if($this->page_num=='last')
        {
            $this->page_num=$this->_pagination_args['total_pages'];
        }
        $pagenum = $this->page_num ? $this->page_num : 0;

        if ( isset( $this->_pagination_args['total_pages'] ) && $pagenum > $this->_pagination_args['total_pages'] )
        {
            $pagenum = $this->_pagination_args['total_pages'];
        }

        return max( 1, $pagenum );
    }

    public function column_cb( $subsite )
    {
        $subsite_id = get_object_vars($subsite)["blog_id"];
        $blogname = get_object_vars($subsite)["domain"].get_object_vars($subsite)["path"];
        ?>
        <label class="screen-reader-text" for="blog_<?php echo esc_attr($subsite_id); ?>">
            <?php
            printf( 'Select %s', esc_html($blogname) );
            ?>
        </label>
        <input type="checkbox" name="<?php echo esc_attr( $this->type ); ?>" value="<?php echo esc_attr( $subsite_id ); ?>" />
        <?php
    }

    public function column_id( $subsite )
    {
        $subsite_id = get_object_vars($subsite)["blog_id"];
        echo esc_html($subsite_id);
    }

    public function column_blogname( $subsite )
    {
        $subsite_id = get_object_vars($subsite)["blog_id"];
        $blogname    = untrailingslashit( get_object_vars($subsite)['domain'] . get_object_vars($subsite)['path'] );
        ?>
        <strong>
            <a href="<?php echo esc_url( network_admin_url( 'site-info.php?id=' .$subsite_id ) ); ?>" class="edit"><?php echo esc_html($blogname); ?></a>
        </strong>
        <?php
    }

    public function column_tables_folders( $subsite )
    {
        $subsite_id = get_object_vars($subsite)["blog_id"];
        $disable='';
        /*if( $this->type=='copy_mu_site')
        {
            $disable='';
        }
        else
        {
            $disable='disabled';
        }*/
        ?>
        <label>
            <input type="checkbox" name="<?php echo esc_attr( $this->type ); ?>_tables" value="<?php echo esc_attr( $subsite_id ); ?>" <?php echo esc_attr( $disable ); ?>/>
            Tables /
        </label>
        <label>
            <input type="checkbox" name="<?php echo esc_attr( $this->type ); ?>_folders" value="<?php echo esc_attr( $subsite_id ); ?>" <?php echo esc_attr( $disable ); ?>/>
            Folders
        </label>
        <?php
    }

    public function column_title( $subsite )
    {
        switch_to_blog( get_object_vars($subsite)["blog_id"] );
        echo esc_html( get_option( 'blogname' ) ) ;
        restore_current_blog();
    }

    public function column_description( $subsite ) {
        switch_to_blog( get_object_vars($subsite)["blog_id"] );
        echo esc_html(  get_option( 'blogdescription ' ) ) ;
        restore_current_blog();
    }

    public function has_items()
    {
        return !empty($this->list);
    }

    public function prepare_items()
    {
        $columns = $this->get_columns();
        $hidden = array();
        $sortable = array();
        $this->_column_headers = array($columns, $hidden, $sortable);

        $total_items =sizeof($this->list);

        $this->set_pagination_args(
            array(
                'total_items' => $total_items,
                'per_page'    => 10,
            )
        );
    }

    public function display_rows()
    {
        $this->_display_rows( $this->list );
    }

    private function _display_rows( $list )
    {
        $page=$this->get_pagenum();

        $page_list=$list;
        $temp_page_list=array();

        $count=0;
        while ( $count<$page )
        {
            $temp_page_list = array_splice( $page_list, 0, 10);
            $count++;
        }

        foreach ( $temp_page_list as $key=>$item)
        {
            $this->single_row($item);
        }
    }

    public function single_row($item)
    {
        ?>
        <tr>
            <?php $this->single_row_columns( $item ); ?>
        </tr>
        <?php
    }

    protected function pagination( $which )
    {
        if ( empty( $this->_pagination_args ) )
        {
            return;
        }

        $total_items     = $this->_pagination_args['total_items'];
        $total_pages     = $this->_pagination_args['total_pages'];
        $infinite_scroll = false;
        if ( isset( $this->_pagination_args['infinite_scroll'] ) )
        {
            $infinite_scroll = $this->_pagination_args['infinite_scroll'];
        }

        if ( 'top' === $which && $total_pages > 1 )
        {
            $this->screen->render_screen_reader_content( 'heading_pagination' );
        }

        $output = '<span class="displaying-num">' . sprintf( _n( '%s item', '%s items', $total_items, 'wpvivid-backuprestore' ), number_format_i18n( $total_items ) ) . '</span>';

        $current              = $this->get_pagenum();

        $page_links = array();

        $total_pages_before = '<span class="paging-input">';
        $total_pages_after  = '</span></span>';

        $disable_first = $disable_last = $disable_prev = $disable_next = false;

        if ( $current == 1 ) {
            $disable_first = true;
            $disable_prev  = true;
        }
        if ( $current == 2 ) {
            $disable_first = true;
        }
        if ( $current == $total_pages ) {
            $disable_last = true;
            $disable_next = true;
        }
        if ( $current == $total_pages - 1 ) {
            $disable_last = true;
        }

        if ( $disable_first ) {
            $page_links[] = '<span class="tablenav-pages-navspan button disabled" aria-hidden="true">&laquo;</span>';
        } else {
            $page_links[] = sprintf(
                "<div class='first-page button'><span class='screen-reader-text'>%s</span><span aria-hidden='true'>%s</span></div>",
                __( 'First page', 'wpvivid-backuprestore' ),
                '&laquo;'
            );
        }

        if ( $disable_prev ) {
            $page_links[] = '<span class="tablenav-pages-navspan button disabled" aria-hidden="true">&lsaquo;</span>';
        } else {
            $page_links[] = sprintf(
                "<div class='prev-page button' value='%s'><span class='screen-reader-text'>%s</span><span aria-hidden='true'>%s</span></div>",
                $current,
                __( 'Previous page', 'wpvivid-backuprestore' ),
                '&lsaquo;'
            );
        }

        if ( 'bottom' === $which ) {
            $html_current_page  = $current;
            $total_pages_before = '<span class="screen-reader-text">' . __( 'Current Page', 'wpvivid-backuprestore' ) . '</span><span id="table-paging" class="paging-input"><span class="tablenav-paging-text">';
        } else {
            $html_current_page = sprintf(
                "%s<input class='current-page'  type='text' name='paged' value='%s' size='%d' aria-describedby='table-paging' /><span class='tablenav-paging-text'>",
                '<label  class="screen-reader-text">' . __( 'Current Page', 'wpvivid-backuprestore' ) . '</label>',
                $current,
                strlen( $total_pages )
            );
        }
        $html_total_pages = sprintf( "<span class='total-pages'>%s</span>", number_format_i18n( $total_pages ) );
        $page_links[]     = $total_pages_before . sprintf( _x( '%1$s of %2$s', 'paging', 'wpvivid-backuprestore' ), $html_current_page, $html_total_pages ) . $total_pages_after;

        if ( $disable_next ) {
            $page_links[] = '<span class="tablenav-pages-navspan button disabled" aria-hidden="true">&rsaquo;</span>';
        } else {
            $page_links[] = sprintf(
                "<div class='next-page button' value='%s'><span class='screen-reader-text'>%s</span><span aria-hidden='true'>%s</span></div>",
                $current,
                __( 'Next page', 'wpvivid-backuprestore' ),
                '&rsaquo;'
            );
        }

        if ( $disable_last ) {
            $page_links[] = '<span class="tablenav-pages-navspan button disabled" aria-hidden="true">&raquo;</span>';
        } else {
            $page_links[] = sprintf(
                "<div class='last-page button'><span class='screen-reader-text'>%s</span><span aria-hidden='true'>%s</span></div>",
                __( 'Last page', 'wpvivid-backuprestore' ),
                '&raquo;'
            );
        }

        $pagination_links_class = 'pagination-links';
        if ( ! empty( $infinite_scroll ) ) {
            $pagination_links_class .= ' hide-if-js';
        }
        $output .= "\n<span class='$pagination_links_class'>" . join( "\n", $page_links ) . '</span>';

        if ( $total_pages ) {
            $page_class = $total_pages < 2 ? ' one-page' : '';
        } else {
            $page_class = ' no-pages';
        }
        $this->_pagination = "<div class='tablenav-pages{$page_class}'>$output</div>";

        echo $this->_pagination;
    }

    protected function display_tablenav( $which ) {
        $css_type = '';
        if ( 'top' === $which ) {
            wp_nonce_field( 'bulk-' . $this->_args['plural'] );
            $css_type = 'margin: 0 0 10px 0';
        }
        else if( 'bottom' === $which ) {
            $css_type = 'margin: 10px 0 0 0';
        }

        $total_pages     = $this->_pagination_args['total_pages'];
        if ( $total_pages >1)
        {
            ?>
            <div class="tablenav <?php echo esc_attr( $which ); ?>" style="<?php echo esc_attr($css_type); ?>">
                <?php
                $this->extra_tablenav( $which );
                $this->pagination( $which );
                ?>

                <br class="clear" />
            </div>
            <?php
        }
    }

    public function display() {
        $singular = $this->_args['singular'];

        $this->display_tablenav( 'top' );

        $this->screen->render_screen_reader_content( 'heading_list' );
        ?>
        <table class="wp-list-table <?php echo esc_attr(implode( ' ', $this->get_table_classes() )); ?>" >
            <thead>
            <tr>
                <?php $this->print_column_headers(); ?>
            </tr>
            </thead>

            <tbody id="the-list"
                <?php
                if ( $singular ) {
                    echo esc_attr(" data-wp-lists='list:$singular'");
                }
                ?>
            >
            <?php $this->display_rows_or_placeholder(); ?>
            </tbody>

            <tfoot>
            <tr>
                <?php $this->print_column_headers( false ); ?>
            </tr>
            </tfoot>

        </table>
        <?php
    }
}

class WPvivid_Custom_MU_Staging_List
{
    public $parent_id;
    public $is_staging_site   = false;
    public $is_sync_site      = false;
    public $staging_home_path = false;
    public $custom_uploads_path;
    public $custom_content_path;
    public $custom_additional_file_path;

    public function __construct(){

    }

    public function set_parent_id($parent_id){
        $this->parent_id = $parent_id;
    }

    public function set_staging_home_path($is_staging_site=false, $is_sync_site=false, $staging_home_path=false){
        $this->is_staging_site   = $is_staging_site;
        $this->is_sync_site      = $is_sync_site;
        $this->staging_home_path = $staging_home_path;
    }

    public function display_rows()
    {
        $core_check = 'checked';
        $database_check = 'checked';
        $database_text_style = 'pointer-events: auto; opacity: 1;';
        $themes_check = 'checked';
        $plugins_check = 'checked';
        $themes_plugins_check = 'checked';
        $themes_plugins_text_style = 'pointer-events: auto; opacity: 1;';
        $uploads_check = 'checked';
        $uploads_text_style = 'pointer-events: auto; opacity: 1;';
        $content_check = 'checked';
        $content_text_style = 'pointer-events: auto; opacity: 1;';
        $additional_file_check = '';
        $additional_file_text_style = 'pointer-events: none; opacity: 0.4;';
        $upload_extension = '';
        $content_extension = '';
        $additional_file_extension = '';

        $db_descript = 'All the tables in the WordPress MU database except for subsites tables.';
        $uploads_descript = 'The folder where images and media files of the main site are stored by default. All files will be copied to the staging site by default. You can exclude folders you do not want to copy.';
        $core_descript = 'These are the essential files for creating a staging site.';
        $themes_plugins_descript = 'All the plugins and themes files used by the MU network. The activated plugins and themes will be copied to the staging site by default. A child theme must be copied if it exists.';
        $contents_descript = '<strong style="text-decoration:underline;"><i>Exclude</i></strong> folders you do not want to copy to the staging site, except for the wp-content/uploads folder.';
        $additional_file_descript = '<strong style="text-decoration:underline;"><i>Include</i></strong> additional files or folders you want to copy to the staging site.';

        ?>
        <table class="wp-list-table widefat plugins wpvivid-custom-table">
            <tbody>
            <!-------- core -------->
            <tr>
                <th class="check-column" scope="row" style="padding-left: 6px;">
                    <label class="screen-reader-text" for=""></label>
                    <input type="checkbox" checked disabled/>
                </th>
                <td class="plugin-title column-primary wpvivid-backup-to-font wpvivid-wordpress-core">WordPress Core</td>
                <td class="column-description desc"><?php echo esc_html($core_descript); ?></td>
            </tr>
            <!-------- database -------->
            <tr style="cursor:pointer;">
                <th class="check-column" scope="row" style="padding-left: 6px;">
                    <label class="screen-reader-text" for=""></label>
                    <input type="checkbox" name="checked[]" checked disabled/>
                </th>
                <td class="plugin-title column-primary wpvivid-backup-to-font wpvivid-handle-database-detail">Database</td>
                <td class="column-description desc wpvivid-handle-database-detail database-desc">
                    <?php echo esc_html($db_descript); ?>
                </td>
            </tr>
            <!-------- uploads -------->
            <tr style="cursor:pointer">
                <th class="check-column" scope="row" style="padding-left: 6px;">
                    <label class="screen-reader-text" for=""></label>
                    <input type="checkbox" name="checked[]" class="wpvivid-custom-check wpvivid-custom-uploads-check" checked disabled/>
                </th>
                <td class="plugin-title column-primary wpvivid-backup-to-font wpvivid-handle-uploads-detail">wp-content/uploads</td>
                <td class="column-description desc wpvivid-handle-uploads-detail uploads-desc"><?php echo esc_html($uploads_descript); ?></td>
                <th class="wpvivid-handle-uploads-detail">
                    <details class="primer" onclick="return false;" style="display: inline-block; width: 100%;">
                        <summary title="Show detail" style="float: right; color: #a0a5aa;"></summary>
                    </details>
                </th>
            </tr>
            <tr class="wpvivid-custom-detail wpvivid-uploads-detail wpvivid-close" style="<?php echo esc_attr($uploads_text_style); ?> display: none;">
                <th class="check-column"></th>
                <td colspan="3" class="plugin-title column-primary">
                    <table class="wp-list-table widefat plugins" style="width:100%;">
                        <thead>
                        <tr>
                            <th class="manage-column column-name column-primary" style="border-bottom: 1px solid #e1e1e1 !important;">
                                <label class="wpvivid-refresh-tree wpvivid-refresh-uploads-tree" style="margin-bottom: 0; font-size: 13px;">Click Here to Refresh Folder Tree</label>
                            </th>
                            <th class="manage-column column-description" style="font-size: 13px; border-bottom: 1px solid #e1e1e1 !important;">Checked Folders or Files to Transfer</th>
                        </tr>
                        </thead>
                        <tbody>
                        <tr>
                            <td class="wpvivid-custom-uploads-left" style="padding-right: 0;">
                                <div class="wpvivid-custom-uploads-tree">
                                    <div class="wpvivid-custom-tree wpvivid-custom-uploads-tree-info"></div>
                                </div>
                            </td>
                            <td class="wpvivid-custom-uploads-right">
                                <div class="wpvivid-custom-uploads-table wpvivid-custom-exclude-uploads-list">
                                </div>
                            </td>
                        </tr>
                        </tbody>
                        <tfoot>
                        <tr>
                            <td colspan="2">
                                <div>
                                    <div style="float: left; margin-right: 10px;">
                                        <input class="button-primary wpvivid-exclude-uploads-folder-btn" type="submit" value="Exclude Folders" disabled />
                                    </div>
                                    <small>
                                        <div class="wpvivid_tooltip" style="margin-top: 8px; float: left; line-height: 100%; white-space: normal;">?
                                            <div class="wpvivid_tooltiptext">Double click to open the folder tree, press Ctrl + left-click to select multiple items.</div>
                                        </div>
                                    </small>
                                    <div style="clear: both;"></div>
                                </div>
                            </td>
                        </tr>
                        </tfoot>
                        <div style="clear:both;"></div>
                    </table>
                    <div style="margin-top: 10px;">
                        <div style="float: left; margin-right: 10px;">
                            <input type="text" class="regular-text wpvivid-uploads-extension" placeholder="Exclude file types, for example: gif,jpg,webp" value="<?php echo esc_attr($upload_extension); ?>" onkeyup="value=value.replace(/[^a-zA-Z0-9\-_,]/g,'')"/>
                            <input type="button" class="wpvivid-uploads-extension-rule-btn" value="Save" />
                        </div>
                        <small>
                            <div class="wpvivid_tooltip" style="margin-top: 8px; float: left; line-height: 100%; white-space: normal;">?
                                <div class="wpvivid_tooltiptext">Exclude file types from the copy. All file types are separated by commas, for example: jpg, gif, tmp etc (without a dot before the file type).</div>
                            </div>
                        </small>
                        <div style="clear: both;"></div>
                    </div>
                </td>
            </tr>
            <!-------- themes and plugins -------->
            <tr style="cursor:pointer">
                <th class="check-column" scope="row" style="padding-left: 6px;">
                    <label class="screen-reader-text" for=""></label>
                    <input type="checkbox" name="checked[]" class="wpvivid-custom-check wpvivid-custom-themes-plugins-check" checked disabled/>
                </th>
                <td class="plugin-title column-primary wpvivid-backup-to-font wpvivid-handle-themes-plugins-detail">Themes and Plugins</td>
                <td class="column-description desc wpvivid-handle-themes-plugins-detail themes-plugins-desc">
                    <?php echo esc_html($themes_plugins_descript); ?>
                </td>
                <th class="wpvivid-handle-themes-plugins-detail">
                    <details class="primer" onclick="return false;" style="display: inline-block; width: 100%;">
                        <summary title="Show detail" style="float: right; color: #a0a5aa;"></summary>
                    </details>
                </th>
            </tr>
            <tr class="wpvivid-custom-detail wpvivid-themes-plugins-detail wpvivid-close" style="pointer-events: auto; opacity: 1; display: none;">
                <th class="check-column"></th>
                <td colspan="3" class="plugin-title column-primary wpvivid-custom-themes-plugins-info">
                    <div class="spinner" style="margin: 0 5px 10px 0; float: left;"></div>
                    <div style="float: left;">Archieving themes and plugins</div>
                    <div style="clear: both;"></div>
                </td>
            </tr>
            <!-------- content -------->
            <tr style="cursor:pointer">
                <th class="check-column" scope="row" style="padding-left: 6px;">
                    <label class="screen-reader-text" for=""></label>
                    <input type="checkbox" name="checked[]" class="wpvivid-custom-check wpvivid-custom-content-check" checked disabled/>
                </th>
                <td class="plugin-title column-primary wpvivid-backup-to-font wpvivid-handle-content-detail">wp-content</td>
                <td class="column-description desc wpvivid-handle-content-detail content-desc"><?php echo esc_html($contents_descript); ?></td>
                <th class="wpvivid-handle-content-detail">
                    <details class="primer" onclick="return false;" style="display: inline-block; width: 100%;">
                        <summary title="Show detail" style="float: right; color: #a0a5aa;"></summary>
                    </details>
                </th>
            </tr>
            <tr class="wpvivid-custom-detail wpvivid-content-detail wpvivid-close" style="<?php echo esc_attr($content_text_style); ?> display: none;">
                <th class="check-column"></th>
                <td colspan="3" class="plugin-title column-primary">
                    <table class="wp-list-table widefat plugins" style="width:100%;">
                        <thead>
                        <tr>
                            <th class="manage-column column-name column-primary" style="border-bottom: 1px solid #e1e1e1 !important;">
                                <label class="wpvivid-refresh-tree wpvivid-refresh-content-tree" style="margin-bottom: 0; font-size: 13px;">Click Here to Refresh Folder Tree</label>
                            </th>
                            <th class="manage-column column-description" style="font-size: 13px; border-bottom: 1px solid #e1e1e1 !important;">Checked Folders or Files to Transfer</th>
                        </tr>
                        </thead>
                        <tbody>
                        <tr>
                            <td class="wpvivid-custom-uploads-left" style="padding-right: 0;">
                                <div class="wpvivid-custom-uploads-tree">
                                    <div class="wpvivid-custom-tree wpvivid-custom-content-tree-info"></div>
                                </div>
                            </td>
                            <td class="wpvivid-custom-uploads-right">
                                <div class="wpvivid-custom-uploads-table wpvivid-custom-exclude-content-list">
                                </div>
                            </td>
                        </tr>
                        </tbody>
                        <tfoot>
                        <tr>
                            <td colspan="2">
                                <div style="float: left; margin-right: 10px;">
                                    <input class="button-primary wpvivid-exclude-content-folder-btn" type="submit" value="Exclude Folders" disabled />
                                </div>
                                <small>
                                    <div class="wpvivid_tooltip" style="margin-top: 8px; float: left; line-height: 100%; white-space: normal;">?
                                        <div class="wpvivid_tooltiptext">Double click to open the folder tree, press Ctrl + left-click to select multiple items.</div>
                                    </div>
                                </small>
                                <div style="clear: both;"></div>
                            </td>
                        </tr>
                        </tfoot>
                        <div style="clear:both;"></div>
                    </table>
                    <div style="margin-top: 10px;">
                        <div style="float: left; margin-right: 10px;">
                            <input type="text" class="regular-text wpvivid-content-extension" placeholder="Exclude file types, for example: gif,jpg,webp" value="<?php echo esc_attr($content_extension); ?>" onkeyup="value=value.replace(/[^a-zA-Z0-9\-_,]/g,'')"/>
                            <input type="button" class="wpvivid-content-extension-rule-btn" value="Save" />
                        </div>
                        <small>
                            <div class="wpvivid_tooltip" style="margin-top: 8px; float: left; line-height: 100%; white-space: normal;">?
                                <div class="wpvivid_tooltiptext">Exclude file types from the copy. All file types are separated by commas, for example: jpg, gif, tmp etc (without a dot before the file type).</div>
                            </div>
                        </small>
                        <div style="clear: both;"></div>
                    </div>
                </td>
            </tr>
            <!-------- additional files -------->
            <tr style="cursor:pointer">
                <th class="check-column" scope="row" style="padding-left: 6px;">
                    <label class="screen-reader-text" for=""></label>
                    <input type="checkbox" name="checked[]" class="wpvivid-custom-check wpvivid-custom-additional-file-check" <?php echo esc_attr($additional_file_check); ?> />
                </th>
                <td class="plugin-title column-primary wpvivid-backup-to-font wpvivid-handle-additional-file-detail">Additional Files/Folder</td>
                <td class="column-description desc wpvivid-handle-additional-file-detail additional-file-desc"><?php echo esc_html($additional_file_descript); ?></td>
                <th class="wpvivid-handle-additional-file-detail">
                    <details class="primer" onclick="return false;" style="display: inline-block; width: 100%;">
                        <summary title="Show detail" style="float: right; color: #a0a5aa;"></summary>
                    </details>
                </th>
            </tr>
            <tr class="wpvivid-custom-detail wpvivid-additional-file-detail wpvivid-close" style="<?php echo esc_attr($additional_file_text_style); ?> display: none;">
                <th class="check-column"></th>
                <td colspan="3" class="plugin-title column-primary">
                    <table class="wp-list-table widefat plugins" style="width:100%;">
                        <thead>
                        <tr>
                            <th class="manage-column column-name column-primary" style="border-bottom: 1px solid #e1e1e1 !important;">
                                <label class="wpvivid-refresh-tree wpvivid-refresh-additional-file-tree" style="margin-bottom: 0; font-size: 13px;">Click Here to Refresh Folder/File Tree</label>
                            </th>
                            <th class="manage-column column-description" style="font-size: 13px; border-bottom: 1px solid #e1e1e1 !important;">Checked Folders or Files to Transfer</th>
                        </tr>
                        </thead>
                        <tbody>
                        <tr>
                            <td class="wpvivid-custom-uploads-left" style="padding-right: 0;">
                                <div class="wpvivid-custom-uploads-tree">
                                    <div class="wpvivid-custom-tree wpvivid-custom-additional-file-tree-info"></div>
                                </div>
                            </td>
                            <td class="wpvivid-custom-uploads-right">
                                <div class="wpvivid-custom-uploads-table wpvivid-custom-include-additional-file-list">
                                </div>
                            </td>
                        </tr>
                        </tbody>
                        <tfoot>
                        <tr>
                            <td colspan="2">
                                <div style="float: left; margin-right: 10px;">
                                    <input class="button-primary wpvivid-include-additional-file-btn" type="submit" value="Include folders/files" disabled />
                                </div>
                                <small>
                                    <div class="wpvivid_tooltip" style="margin-top: 8px; float: left; line-height: 100%; white-space: normal;">?
                                        <div class="wpvivid_tooltiptext">Double click to open the folder tree, press Ctrl + left-click to select multiple items.</div>
                                    </div>
                                </small>
                                <div style="clear: both;"></div>
                            </td>
                        </tr>
                        </tfoot>
                        <div style="clear:both;"></div>
                    </table>
                    <div style="margin-top: 10px;">
                        <div style="float: left; margin-right: 10px;">
                            <input type="text" class="regular-text wpvivid-additional-file-extension" placeholder="Exclude file types, for example: gif,jpg,webp" value="<?php echo esc_attr($additional_file_extension); ?>" onkeyup="value=value.replace(/[^a-zA-Z0-9\-_,]/g,'')"/>
                            <input type="button" class="wpvivid-additional-file-extension-rule-btn" value="Save" />
                        </div>
                        <small>
                            <div class="wpvivid_tooltip" style="margin-top: 8px; float: left; line-height: 100%; white-space: normal;">?
                                <div class="wpvivid_tooltiptext">Exclude file types from the copy. All file types are separated by commas, for example: jpg, gif, tmp etc (without a dot before the file type).</div>
                            </div>
                        </small>
                        <div style="clear: both;"></div>
                    </div>
                </td>
            </tr>
            </tbody>
        </table>
        <?php
    }

    public function load_js(){
        $upload_dir = wp_upload_dir();
        $upload_path = $this->is_staging_site === false ?  $upload_dir['basedir'] : $this->staging_home_path.'/wp-content/uploads';
        $upload_path = str_replace('\\','/',$upload_path);
        $upload_path = $upload_path.'/';
        $this->custom_uploads_path = $upload_path;

        $content_dir = $this->is_staging_site === false ? WP_CONTENT_DIR : $this->staging_home_path.'/wp-content';
        $content_path = str_replace('\\','/',$content_dir);
        $content_path = $content_path.'/';
        $this->custom_content_path = $content_path;

        $additional_file_path = $this->is_staging_site === false ? str_replace('\\','/',get_home_path()) : str_replace('\\','/',$this->staging_home_path);
        $this->custom_additional_file_path = $additional_file_path;
        ?>
        <script>
            function wpvivid_handle_custom_open_close(obj, sub_obj){
                if(obj.hasClass('wpvivid-close')) {
                    sub_obj.hide();
                    sub_obj.prev().find('details').prop('open', false);
                    sub_obj.removeClass('wpvivid-open');
                    sub_obj.addClass('wpvivid-close');
                    sub_obj.prev().css('background-color', '#fff');
                    obj.prev().css('background-color', '#f1f1f1');
                    obj.prev().find('details').prop('open', true);
                    obj.show();
                    obj.removeClass('wpvivid-close');
                    obj.addClass('wpvivid-open');
                }
                else{
                    obj.hide();
                    obj.prev().css('background-color', '#fff');
                    obj.prev().find('details').prop('open', false);
                    obj.removeClass('wpvivid-open');
                    obj.addClass('wpvivid-close');
                }
            }

            jQuery('#<?php echo esc_attr($this->parent_id); ?>').on("click", '.wpvivid-handle-database-detail', function() {
                var obj = jQuery('#<?php echo esc_attr($this->parent_id); ?>').find('.wpvivid-database-detail');
                var sub_obj = jQuery('#<?php echo esc_attr($this->parent_id); ?>').find('.wpvivid-custom-detail');
                wpvivid_handle_custom_open_close(obj, sub_obj);
            });

            jQuery('#<?php echo esc_attr($this->parent_id); ?>').on("click", '.wpvivid-handle-themes-plugins-detail', function() {
                var obj = jQuery('#<?php echo esc_attr($this->parent_id); ?>').find('.wpvivid-themes-plugins-detail');
                var sub_obj = jQuery('#<?php echo esc_attr($this->parent_id); ?>').find('.wpvivid-custom-detail');
                wpvivid_handle_custom_open_close(obj, sub_obj);
            });

            jQuery('#<?php echo esc_attr($this->parent_id); ?>').on("click", '.wpvivid-handle-uploads-detail', function() {
                var obj = jQuery('#<?php echo esc_attr($this->parent_id); ?>').find('.wpvivid-uploads-detail');
                var sub_obj = jQuery('#<?php echo esc_attr($this->parent_id); ?>').find('.wpvivid-custom-detail');
                wpvivid_handle_custom_open_close(obj, sub_obj);
            });

            jQuery('#<?php echo esc_attr($this->parent_id); ?>').on("click", '.wpvivid-handle-content-detail', function() {
                var obj = jQuery('#<?php echo esc_attr($this->parent_id); ?>').find('.wpvivid-content-detail');
                var sub_obj = jQuery('#<?php echo esc_attr($this->parent_id); ?>').find('.wpvivid-custom-detail');
                wpvivid_handle_custom_open_close(obj, sub_obj);
            });

            jQuery('#<?php echo esc_attr($this->parent_id); ?>').on("click", '.wpvivid-handle-additional-file-detail', function() {
                var obj = jQuery('#<?php echo esc_attr($this->parent_id); ?>').find('.wpvivid-additional-file-detail');
                var sub_obj = jQuery('#<?php echo esc_attr($this->parent_id); ?>').find('.wpvivid-custom-detail');
                wpvivid_handle_custom_open_close(obj, sub_obj);
            });

            jQuery('#<?php echo esc_attr($this->parent_id); ?>').on("click", '.wpvivid-custom-check', function() {
                if (jQuery(this).prop('checked')) {
                    if(!jQuery(this).hasClass('wpvivid-custom-core-check')) {
                        jQuery(jQuery(this).parents('tr').next().get(0)).css({'pointer-events': 'auto', 'opacity': '1'});
                    }
                }
                else{
                    var check_status = false;
                    jQuery('#<?php echo esc_attr($this->parent_id); ?>').find('.wpvivid-custom-check').each(function(){
                        if(jQuery(this).prop('checked')){
                            check_status = true;
                        }
                    });
                    if(check_status) {
                        if (!jQuery(this).hasClass('wpvivid-custom-core-check')) {
                            jQuery(jQuery(this).parents('tr').next().get(0)).css({'pointer-events': 'none', 'opacity': '0.4'});
                        }
                    }
                    else{
                        jQuery(this).prop('checked', true);
                        alert('Please select at least one item under Custom option.');
                    }
                }
            });

            jQuery('#<?php echo esc_attr($this->parent_id); ?>').on("click", '.wpvivid-database-table-check', function() {
                if(jQuery(this).prop('checked')){
                    if(jQuery(this).hasClass('wpvivid-database-base-table-check')){
                        jQuery('#<?php echo esc_attr($this->parent_id); ?>').find('input:checkbox[option=base_db][name=Database]').prop('checked', true);
                    }
                    else if(jQuery(this).hasClass('wpvivid-database-woo-table-check')){
                        jQuery('#<?php echo esc_attr($this->parent_id); ?>').find('input:checkbox[option=woo_db][name=Database]').prop('checked', true);
                    }
                    else if(jQuery(this).hasClass('wpvivid-database-other-table-check')){
                        jQuery('#<?php echo esc_attr($this->parent_id); ?>').find('input:checkbox[option=other_db][name=Database]').prop('checked', true);
                    }
                }
                else{
                    var check_status = false;
                    if (jQuery(this).hasClass('wpvivid-database-base-table-check')) {
                        jQuery('#<?php echo esc_attr($this->parent_id); ?>').find('input:checkbox[option=other_db][name=Database]').each(function(){
                            if(jQuery(this).prop('checked')){
                                check_status = true;
                            }
                        });
                        jQuery('#<?php echo esc_attr($this->parent_id); ?>').find('input:checkbox[option=woo_db][name=Database]').each(function(){
                            if(jQuery(this).prop('checked')){
                                check_status = true;
                            }
                        });
                        if(check_status) {
                            jQuery('#<?php echo esc_attr($this->parent_id); ?>').find('input:checkbox[option=base_db][name=Database]').prop('checked', false);
                        }
                        else{
                            jQuery(this).prop('checked', true);
                            alert('Please select at least one table type under the Database option, or deselect the option.');
                        }
                    }
                    else if (jQuery(this).hasClass('wpvivid-database-woo-table-check')) {
                        jQuery('#<?php echo esc_attr($this->parent_id); ?>').find('input:checkbox[option=base_db][name=Database]').each(function(){
                            if(jQuery(this).prop('checked')){
                                check_status = true;
                            }
                        });
                        jQuery('#<?php echo esc_attr($this->parent_id); ?>').find('input:checkbox[option=other_db][name=Database]').each(function(){
                            if(jQuery(this).prop('checked')){
                                check_status = true;
                            }
                        });
                        if(check_status) {
                            jQuery('#<?php echo esc_attr($this->parent_id); ?>').find('input:checkbox[option=woo_db][name=Database]').prop('checked', false);
                        }
                        else{
                            jQuery(this).prop('checked', true);
                            alert('Please select at least one table type under the Database option, or deselect the option.');
                        }
                    }
                    else if (jQuery(this).hasClass('wpvivid-database-other-table-check')) {
                        jQuery('#<?php echo esc_attr($this->parent_id); ?>').find('input:checkbox[option=base_db][name=Database]').each(function(){
                            if(jQuery(this).prop('checked')){
                                check_status = true;
                            }
                        });
                        jQuery('#<?php echo esc_attr($this->parent_id); ?>').find('input:checkbox[option=woo_db][name=Database]').each(function(){
                            if(jQuery(this).prop('checked')){
                                check_status = true;
                            }
                        });
                        if(check_status) {
                            jQuery('#<?php echo esc_attr($this->parent_id); ?>').find('input:checkbox[option=other_db][name=Database]').prop('checked', false);
                        }
                        else{
                            jQuery(this).prop('checked', true);
                            alert('Please select at least one table type under the Database option, or deselect the option.');
                        }
                    }
                }
            });

            jQuery('#<?php echo esc_attr($this->parent_id); ?>').on("click", 'input:checkbox[option=base_db][name=Database]', function(){
                if(jQuery(this).prop('checked')){
                    var all_check = true;
                    jQuery('#<?php echo esc_attr($this->parent_id); ?>').find('input:checkbox[option=base_db][name=Database]').each(function(){
                        if(!jQuery(this).prop('checked')){
                            all_check = false;
                        }
                    });
                    if(all_check){
                        jQuery('#<?php echo esc_attr($this->parent_id); ?>').find('.wpvivid-database-base-table-check').prop('checked', true);
                    }
                }
                else{
                    var check_status = false;
                    jQuery('#<?php echo esc_attr($this->parent_id); ?>').find('input:checkbox[name=Database]').each(function(){
                        if(jQuery(this).prop('checked')){
                            check_status = true;
                        }
                    });
                    if(check_status){
                        jQuery('#<?php echo esc_attr($this->parent_id); ?>').find('.wpvivid-database-base-table-check').prop('checked', false);
                    }
                    else{
                        jQuery(this).prop('checked', true);
                        alert('Please select at least one table type under the Database option, or deselect the option.');
                    }
                }
            });

            jQuery('#<?php echo esc_attr($this->parent_id); ?>').on("click", 'input:checkbox[option=woo_db][name=Database]', function(){
                if(jQuery(this).prop('checked')){
                    var all_check = true;
                    jQuery('#<?php echo esc_attr($this->parent_id); ?>').find('input:checkbox[option=woo_db][name=Database]').each(function(){
                        if(!jQuery(this).prop('checked')){
                            all_check = false;
                        }
                    });
                    if(all_check){
                        jQuery('#<?php echo esc_attr($this->parent_id); ?>').find('.wpvivid-database-woo-table-check').prop('checked', true);
                    }
                }
                else{
                    var check_status = false;
                    jQuery('#<?php echo esc_attr($this->parent_id); ?>').find('input:checkbox[name=Database]').each(function(){
                        if(jQuery(this).prop('checked')){
                            check_status = true;
                        }
                    });
                    if(check_status){
                        jQuery('#<?php echo esc_attr($this->parent_id); ?>').find('.wpvivid-database-woo-table-check').prop('checked', false);
                    }
                    else{
                        jQuery(this).prop('checked', true);
                        alert('Please select at least one table type under the Database option, or deselect the option.');
                    }
                }
            });

            jQuery('#<?php echo esc_attr($this->parent_id); ?>').on("click", 'input:checkbox[option=other_db][name=Database]', function(){
                if(jQuery(this).prop('checked')){
                    var all_check = true;
                    jQuery('#<?php echo esc_attr($this->parent_id); ?>').find('input:checkbox[option=other_db][name=Database]').each(function(){
                        if(!jQuery(this).prop('checked')){
                            all_check = false;
                        }
                    });
                    if(all_check){
                        jQuery('#<?php echo esc_attr($this->parent_id); ?>').find('.wpvivid-database-other-table-check').prop('checked', true);
                    }
                }
                else{
                    var check_status = false;
                    jQuery('#<?php echo esc_attr($this->parent_id); ?>').find('input:checkbox[name=Database]').each(function(){
                        if(jQuery(this).prop('checked')){
                            check_status = true;
                        }
                    });
                    if(check_status){
                        jQuery('#<?php echo esc_attr($this->parent_id); ?>').find('.wpvivid-database-other-table-check').prop('checked', false);
                    }
                    else{
                        jQuery(this).prop('checked', true);
                        alert('Please select at least one table type under the Database option, or deselect the option.');
                    }
                }
            });

            jQuery('#<?php echo esc_attr($this->parent_id); ?>').on("click", '.wpvivid-themes-plugins-table-check', function(){
                if(jQuery(this).prop('checked')){
                    if(jQuery(this).hasClass('wpvivid-themes-table-check')){
                        jQuery('#<?php echo esc_attr($this->parent_id); ?>').find('input:checkbox[option=themes][name=Themes]').prop('checked', true);
                    }
                    else if(jQuery(this).hasClass('wpvivid-plugins-table-check')){
                        jQuery('#<?php echo esc_attr($this->parent_id); ?>').find('input:checkbox[option=plugins][name=Plugins]').prop('checked', true);
                    }
                }
                else{
                    var check_status = false;
                    if (jQuery(this).hasClass('wpvivid-themes-table-check')) {
                        jQuery('#<?php echo esc_attr($this->parent_id); ?>').find('input:checkbox[option=plugins][name=Plugins]').each(function(){
                            if(jQuery(this).prop('checked')){
                                check_status = true;
                            }
                        });
                        if(check_status) {
                            jQuery('#<?php echo esc_attr($this->parent_id); ?>').find('input:checkbox[option=themes][name=Themes]').prop('checked', false);
                        }
                        else{
                            jQuery(this).prop('checked', true);
                            alert('Please select at least one item under the Themes and Plugins option, or deselect the option.');
                        }
                    }
                    else if (jQuery(this).hasClass('wpvivid-plugins-table-check')) {
                        jQuery('#<?php echo esc_attr($this->parent_id); ?>').find('input:checkbox[option=themes][name=Themes]').each(function(){
                            if(jQuery(this).prop('checked')){
                                check_status = true;
                            }
                        });
                        if(check_status) {
                            jQuery('#<?php echo esc_attr($this->parent_id); ?>').find('input:checkbox[option=plugins][name=Plugins]').each(function(){
                                if(jQuery(this).val() !== 'wpvivid-backuprestore' && jQuery(this).val() !== 'wpvivid-backup-pro'){
                                    jQuery(this).prop('checked', false);
                                }
                            });
                        }
                        else{
                            jQuery(this).prop('checked', true);
                            alert('Please select at least one item under the Themes and Plugins option, or deselect the option.');
                        }
                    }
                }
            });

            jQuery('#<?php echo esc_attr($this->parent_id); ?>').on("click", 'input:checkbox[option=themes][name=Themes]', function(){
                if(jQuery(this).prop('checked')){
                    var all_check = true;
                    jQuery('#<?php echo esc_attr($this->parent_id); ?>').find('input:checkbox[option=themes][name=Themes]').each(function(){
                        if(!jQuery(this).prop('checked')){
                            all_check = false;
                        }
                    });
                    if(all_check){
                        jQuery('#<?php echo esc_attr($this->parent_id); ?>').find('.wpvivid-themes-table-check').prop('checked', true);
                    }
                }
                else{
                    var check_status = false;
                    jQuery('#<?php echo esc_attr($this->parent_id); ?>').find('input:checkbox[option=themes][name=Themes]').each(function(){
                        if(jQuery(this).prop('checked')){
                            check_status = true;
                        }
                    });
                    if(!check_status){
                        jQuery('#<?php echo esc_attr($this->parent_id); ?>').find('input:checkbox[option=plugins][name=Plugins]').each(function(){
                            if(jQuery(this).prop('checked')){
                                check_status = true;
                            }
                        });
                    }
                    if(check_status){
                        jQuery('#<?php echo esc_attr($this->parent_id); ?>').find('.wpvivid-themes-table-check').prop('checked', false);
                    }
                    else{
                        jQuery(this).prop('checked', true);
                        alert('Please select at least one item under the Themes and Plugins option, or deselect the option.');
                    }
                }
            });

            jQuery('#<?php echo esc_attr($this->parent_id); ?>').on("click", 'input:checkbox[option=plugins][name=Plugins]', function(){
                if(jQuery(this).prop('checked')){
                    var all_check = true;
                    jQuery('#<?php echo esc_attr($this->parent_id); ?>').find('input:checkbox[option=plugins][name=Plugins]').each(function(){
                        if(!jQuery(this).prop('checked')){
                            all_check = false;
                        }
                    });
                    if(all_check){
                        jQuery('#<?php echo esc_attr($this->parent_id); ?>').find('.wpvivid-plugins-table-check').prop('checked', true);
                    }
                }
                else{
                    var check_status = false;
                    jQuery('#<?php echo esc_attr($this->parent_id); ?>').find('input:checkbox[option=plugins][name=Plugins]').each(function(){
                        if(jQuery(this).prop('checked')){
                            check_status = true;
                        }
                    });
                    if(!check_status){
                        jQuery('#<?php echo esc_attr($this->parent_id); ?>').find('input:checkbox[option=themes][name=Themes]').each(function(){
                            if(jQuery(this).prop('checked')){
                                check_status = true;
                            }
                        });
                    }
                    if(check_status){
                        jQuery('#<?php echo esc_attr($this->parent_id); ?>').find('.wpvivid-plugins-table-check').prop('checked', false);
                    }
                    else{
                        jQuery(this).prop('checked', true);
                        alert('Please select at least one item under the Themes and Plugins option, or deselect the option.');
                    }
                }
            });

            jQuery('#<?php echo esc_attr($this->parent_id); ?>').on("click", '.wpvivid-uploads-extension-rule-btn', function(){
                var value = jQuery(this).prev().val();
                if(value!=='') {
                    wpvivid_update_staging_exclude_extension('upload', value);
                }
            });

            jQuery('#<?php echo esc_attr($this->parent_id); ?>').on("click", '.wpvivid-content-extension-rule-btn', function(){
                var value = jQuery(this).prev().val();
                if(value!=='') {
                    wpvivid_update_staging_exclude_extension('content', value);
                }
            });

            jQuery('#<?php echo esc_attr($this->parent_id); ?>').on("click", '.wpvivid-additional-file-extension-rule-btn', function(){
                var value = jQuery(this).prev().val();
                if(value!=='') {
                    wpvivid_update_staging_exclude_extension('additional_file', value);
                }
            });

            function wpvivid_update_staging_exclude_extension(type, value){
                var ajax_data = {
                    'action': 'wpvividstg_update_staging_exclude_extension_free',
                    'type': type,
                    'exclude_content': value
                };
                jQuery(this).css({'pointer-events': 'none', 'opacity': '0.4'});
                wpvivid_post_request(ajax_data, function (data) {
                    jQuery(this).css({'pointer-events': 'auto', 'opacity': '1'});
                    try {
                        var jsonarray = jQuery.parseJSON(data);
                        if (jsonarray.result === 'success') {
                        }
                    }
                    catch (err) {
                        alert(err);
                    }
                }, function (XMLHttpRequest, textStatus, errorThrown) {
                    jQuery(this).css({'pointer-events': 'auto', 'opacity': '1'});
                    var error_message = wpvivid_output_ajaxerror('saving staging extension', textStatus, errorThrown);
                    alert(error_message);
                });
            }

            jQuery('#<?php echo esc_attr($this->parent_id); ?>').on("click", '.wpvivid-custom-li-close', function(){
                jQuery(this).parent().parent().remove();
            });
        </script>
        <?php
    }
}

class WPvivid_Custom_Staging_List
{
    public $parent_id;
    public $is_staging_site   = false;
    public $staging_home_path = false;
    public $custom_uploads_path;
    public $custom_content_path;
    public $custom_additional_file_path;

    public function __construct(){

    }

    public function set_parent_id($parent_id){
        $this->parent_id = $parent_id;
    }

    public function set_staging_home_path($is_staging_site=false, $staging_home_path=false){
        $this->is_staging_site   = $is_staging_site;
        $this->staging_home_path = $staging_home_path;
    }

    public function display_rows(){
        $core_check = 'checked';
        $database_check = 'checked';
        $database_text_style = 'pointer-events: auto; opacity: 1;';
        $themes_check = 'checked';
        $plugins_check = 'checked';
        $themes_plugins_check = 'checked';
        $themes_plugins_text_style = 'pointer-events: auto; opacity: 1;';
        $uploads_check = 'checked';
        $uploads_text_style = 'pointer-events: auto; opacity: 1;';
        $content_check = 'checked';
        $content_text_style = 'pointer-events: auto; opacity: 1;';
        $additional_file_check = '';
        $additional_file_text_style = 'pointer-events: none; opacity: 0.4;';
        $upload_extension = '';
        $content_extension = '';
        $additional_file_extension = '';
        if($this->is_staging_site){
            $border_css = 'border: 1px solid #f1f1f1;';
            $checkbox_disable = '';
            $core_descript = 'If the staging site and the live site have the same version of WordPress. Then it is not necessary to copy the WordPress core files to the live site.';
            $db_descript = 'It is recommended to copy all tables of the database to the live site.';
            $themes_plugins_descript = 'The activated plugins and themes will be copied to the live site by default. The Child theme must be copied if it exists';
            $uploads_descript = 'Images and media files are stored in the Uploads directory by default. All files are copied to the live site by default. You can exclude folders you do not want to copy.';
            $contents_descript = '<strong style="text-decoration:underline;"><i>Exclude</i></strong> folders you do not want to copy to the live site, except for the wp-content/uploads folder.';
            $additional_file_descript = '<strong style="text-decoration:underline;"><i>Include</i></strong> additional files or folders you want to copy to the live site.';
        }
        else{
            $border_css = 'border: none;';
            $checkbox_disable = ' disabled';
            $core_descript = 'These are the essential files for creating a staging site.';
            $db_descript = 'The tables created by WordPress are required for the staging site. Database tables created by themes or plugins are optional.';
            $themes_plugins_descript = 'The activated plugins and themes will be copied to a staging site by default. A Child theme must be copied if it exists.';
            $uploads_descript = 'Images and media files are stored in the Uploads directory by default. All files are copied to the staging site by default. You can exclude folders you do not want to copy.';
            $contents_descript = '<strong style="text-decoration:underline;"><i>Exclude</i></strong> folders you do not want to copy to the staging site, except for the wp-content/uploads folder.';
            $additional_file_descript = '<strong style="text-decoration:underline;"><i>Include</i></strong> additional files or folders you want to copy to the staging site.';
            $options = get_option('wpvivid_staging_history', array());
            if(isset($options['additional_file_check'])) {
                $additional_file_check = $options['additional_file_check'] == '1' ? 'checked' : '';
                $additional_file_text_style = $options['additional_file_check'] == '1' ? 'pointer-events: auto; opacity: 1;' : 'pointer-events: none; opacity: 0.4;';
            }
            if(isset($options['upload_extension']) && !empty($options['upload_extension'])){
                $upload_extension = implode(",", $options['upload_extension']);
            }
            if(isset($options['content_extension']) && !empty($options['content_extension'])){
                $content_extension = implode(",", $options['content_extension']);
            }
            if(isset($options['additional_file_extension']) && !empty($options['additional_file_extension'])){
                $additional_file_extension = implode(",", $options['additional_file_extension']);
            }
        }
        ?>
        <table class="wp-list-table widefat plugins wpvivid-custom-table" style="<?php echo esc_attr($border_css); ?>">
            <tbody>
            <!-------- core -------->
            <tr>
                <th class="check-column" scope="row" style="padding-left: 6px;">
                    <label class="screen-reader-text" for=""></label>
                    <input type="checkbox" name="checked[]" class="wpvivid-custom-check wpvivid-custom-core-check" <?php echo esc_attr($core_check.$checkbox_disable); ?> />
                </th>
                <td class="plugin-title column-primary wpvivid-backup-to-font wpvivid-wordpress-core">Wordpress Core</td>
                <td class="column-description desc core-desc"><?php echo esc_html($core_descript); ?></td>
            </tr>
            <!-------- database -------->
            <tr style="cursor:pointer;">
                <th class="check-column" scope="row" style="padding-left: 6px;">
                    <label class="screen-reader-text" for=""></label>
                    <input type="checkbox" name="checked[]" class="wpvivid-custom-check wpvivid-custom-database-check" <?php echo esc_attr($database_check.$checkbox_disable); ?> />
                </th>
                <td class="plugin-title column-primary wpvivid-backup-to-font wpvivid-handle-database-detail">Database</td>
                <td class="column-description desc wpvivid-handle-database-detail database-desc">
                    <?php echo esc_html($db_descript); ?>
                </td>
                <th class="wpvivid-handle-database-detail">
                    <details class="primer" onclick="return false;" style="display: inline-block; width: 100%;">
                        <summary title="Show detail" style="float: right; color: #a0a5aa;"></summary>
                    </details>
                </th>
            </tr>
            <tr class="wpvivid-custom-detail wpvivid-database-detail wpvivid-close" style="<?php echo esc_attr($database_text_style); ?> display: none;">
                <th class="check-column"></th>
                <td colspan="3" class="plugin-title column-primary wpvivid-custom-database-info">
                    <div class="spinner" style="margin: 0 5px 10px 0; float: left;"></div>
                    <div style="float: left;">Archieving database tables</div>
                    <div style="clear: both;"></div>
                </td>
            </tr>
            <!-------- themes and plugins -------->
            <tr style="cursor:pointer">
                <th class="check-column" scope="row" style="padding-left: 6px;">
                    <label class="screen-reader-text" for=""></label>
                    <input type="checkbox" name="checked[]" class="wpvivid-custom-check wpvivid-custom-themes-plugins-check" <?php echo esc_attr($themes_plugins_check.$checkbox_disable); ?> />
                </th>
                <td class="plugin-title column-primary wpvivid-backup-to-font wpvivid-handle-themes-plugins-detail">Themes and Plugins</td>
                <td class="column-description desc wpvivid-handle-themes-plugins-detail themes-plugins-desc">
                    <?php echo esc_html($themes_plugins_descript); ?>
                </td>
                <th class="wpvivid-handle-themes-plugins-detail">
                    <details class="primer" onclick="return false;" style="display: inline-block; width: 100%;">
                        <summary title="Show detail" style="float: right; color: #a0a5aa;"></summary>
                    </details>
                </th>
            </tr>
            <tr class="wpvivid-custom-detail wpvivid-themes-plugins-detail wpvivid-close" style="<?php echo esc_attr($themes_plugins_text_style); ?> display: none;">
                <th class="check-column"></th>
                <td colspan="3" class="plugin-title column-primary wpvivid-custom-themes-plugins-info">
                    <div class="spinner" style="margin: 0 5px 10px 0; float: left;"></div>
                    <div style="float: left;">Archieving themes and plugins</div>
                    <div style="clear: both;"></div>
                </td>
            </tr>
            <!-------- uploads -------->
            <tr style="cursor:pointer">
                <th class="check-column" scope="row" style="padding-left: 6px;">
                    <label class="screen-reader-text" for=""></label>
                    <input type="checkbox" name="checked[]" class="wpvivid-custom-check wpvivid-custom-uploads-check" <?php echo esc_attr($uploads_check.$checkbox_disable); ?> />
                </th>
                <td class="plugin-title column-primary wpvivid-backup-to-font wpvivid-handle-uploads-detail">wp-content/uploads</td>
                <td class="column-description desc wpvivid-handle-uploads-detail uploads-desc"><?php echo esc_html($uploads_descript); ?></td>
                <th class="wpvivid-handle-uploads-detail">
                    <details class="primer" onclick="return false;" style="display: inline-block; width: 100%;">
                        <summary title="Show detail" style="float: right; color: #a0a5aa;"></summary>
                    </details>
                </th>
            </tr>
            <tr class="wpvivid-custom-detail wpvivid-uploads-detail wpvivid-close" style="<?php echo esc_attr($uploads_text_style); ?> display: none;">
                <th class="check-column"></th>
                <td colspan="3" class="plugin-title column-primary">
                    <table class="wp-list-table widefat plugins" style="width:100%;">
                        <thead>
                        <tr>
                            <th class="manage-column column-name column-primary" style="border-bottom: 1px solid #e1e1e1 !important;">
                                <label class="wpvivid-refresh-tree wpvivid-refresh-uploads-tree" style="margin-bottom: 0; font-size: 13px;">Click Here to Refresh Folder Tree</label>
                            </th>
                            <th class="manage-column column-description" style="font-size: 13px; border-bottom: 1px solid #e1e1e1 !important;">Checked Folders or Files to Transfer</th>
                        </tr>
                        </thead>
                        <tbody>
                        <tr>
                            <td class="wpvivid-custom-uploads-left" style="padding-right: 0;">
                                <div class="wpvivid-custom-uploads-tree">
                                    <div class="wpvivid-custom-tree wpvivid-custom-uploads-tree-info"></div>
                                </div>
                            </td>
                            <td class="wpvivid-custom-uploads-right">
                                <div class="wpvivid-custom-uploads-table wpvivid-custom-exclude-uploads-list">
                                    <?php
                                    if(!$this->is_staging_site)
                                    {
                                        $this->wpvivid_load_custom_upload();
                                    }
                                    ?>
                                </div>
                            </td>
                        </tr>
                        </tbody>
                        <tfoot>
                        <tr>
                            <td colspan="2">
                                <div>
                                    <div style="float: left; margin-right: 10px;">
                                        <input class="button-primary wpvivid-exclude-uploads-folder-btn" type="submit" value="Exclude Folders" disabled />
                                    </div>
                                    <small>
                                        <div class="wpvivid_tooltip" style="margin-top: 8px; float: left; line-height: 100%; white-space: normal;">?
                                            <div class="wpvivid_tooltiptext">Double click to open the folder tree, press Ctrl + left-click to select multiple items.</div>
                                        </div>
                                    </small>
                                    <div style="clear: both;"></div>
                                </div>
                            </td>
                        </tr>
                        </tfoot>
                        <div style="clear:both;"></div>
                    </table>
                    <div style="margin-top: 10px;">
                        <div style="float: left; margin-right: 10px;">
                            <input type="text" class="regular-text wpvivid-uploads-extension" placeholder="Exclude file types, for example: gif,jpg,webp" value="<?php echo esc_attr($upload_extension); ?>" onkeyup="value=value.replace(/[^a-zA-Z0-9\-_,]/g,'')"/>
                            <input type="button" class="wpvivid-uploads-extension-rule-btn" value="Save" />
                        </div>
                        <small>
                            <div class="wpvivid_tooltip" style="margin-top: 8px; float: left; line-height: 100%; white-space: normal;">?
                                <div class="wpvivid_tooltiptext">Exclude file types from the copy. All file types are separated by commas, for example: jpg, gif, tmp etc (without a dot before the file type).</div>
                            </div>
                        </small>
                        <div style="clear: both;"></div>
                    </div>
                </td>
            </tr>
            <!-------- content -------->
            <tr style="cursor:pointer">
                <th class="check-column" scope="row" style="padding-left: 6px;">
                    <label class="screen-reader-text" for=""></label>
                    <input type="checkbox" name="checked[]" class="wpvivid-custom-check wpvivid-custom-content-check" <?php echo esc_attr($content_check.$checkbox_disable); ?> />
                </th>
                <td class="plugin-title column-primary wpvivid-backup-to-font wpvivid-handle-content-detail">wp-content</td>
                <td class="column-description desc wpvivid-handle-content-detail content-desc"><?php echo esc_html($contents_descript); ?></td>
                <th class="wpvivid-handle-content-detail">
                    <details class="primer" onclick="return false;" style="display: inline-block; width: 100%;">
                        <summary title="Show detail" style="float: right; color: #a0a5aa;"></summary>
                    </details>
                </th>
            </tr>
            <tr class="wpvivid-custom-detail wpvivid-content-detail wpvivid-close" style="<?php echo esc_attr($content_text_style); ?> display: none;">
                <th class="check-column"></th>
                <td colspan="3" class="plugin-title column-primary">
                    <table class="wp-list-table widefat plugins" style="width:100%;">
                        <thead>
                        <tr>
                            <th class="manage-column column-name column-primary" style="border-bottom: 1px solid #e1e1e1 !important;">
                                <label class="wpvivid-refresh-tree wpvivid-refresh-content-tree" style="margin-bottom: 0; font-size: 13px;">Click Here to Refresh Folder Tree</label>
                            </th>
                            <th class="manage-column column-description" style="font-size: 13px; border-bottom: 1px solid #e1e1e1 !important;">Checked Folders or Files to Transfer</th>
                        </tr>
                        </thead>
                        <tbody>
                        <tr>
                            <td class="wpvivid-custom-uploads-left" style="padding-right: 0;">
                                <div class="wpvivid-custom-uploads-tree">
                                    <div class="wpvivid-custom-tree wpvivid-custom-content-tree-info"></div>
                                </div>
                            </td>
                            <td class="wpvivid-custom-uploads-right">
                                <div class="wpvivid-custom-uploads-table wpvivid-custom-exclude-content-list">
                                    <?php
                                    if(!$this->is_staging_site){
                                        $this->wpvivid_load_custom_content();
                                    }
                                    ?>
                                </div>
                            </td>
                        </tr>
                        </tbody>
                        <tfoot>
                        <tr>
                            <td colspan="2">
                                <div style="float: left; margin-right: 10px;">
                                    <input class="button-primary wpvivid-exclude-content-folder-btn" type="submit" value="Exclude Folders" disabled />
                                </div>
                                <small>
                                    <div class="wpvivid_tooltip" style="margin-top: 8px; float: left; line-height: 100%; white-space: normal;">?
                                        <div class="wpvivid_tooltiptext">Double click to open the folder tree, press Ctrl + left-click to select multiple items.</div>
                                    </div>
                                </small>
                                <div style="clear: both;"></div>
                            </td>
                        </tr>
                        </tfoot>
                        <div style="clear:both;"></div>
                    </table>
                    <div style="margin-top: 10px;">
                        <div style="float: left; margin-right: 10px;">
                            <input type="text" class="regular-text wpvivid-content-extension" placeholder="Exclude file types, for example: gif,jpg,webp" value="<?php echo esc_attr($content_extension); ?>" onkeyup="value=value.replace(/[^a-zA-Z0-9\-_,]/g,'')"/>
                            <input type="button" class="wpvivid-content-extension-rule-btn" value="Save" />
                        </div>
                        <small>
                            <div class="wpvivid_tooltip" style="margin-top: 8px; float: left; line-height: 100%; white-space: normal;">?
                                <div class="wpvivid_tooltiptext">Exclude file types from the copy. All file types are separated by commas, for example: jpg, gif, tmp etc (without a dot before the file type).</div>
                            </div>
                        </small>
                        <div style="clear: both;"></div>
                    </div>
                </td>
            </tr>
            <!-------- additional files -------->
            <tr style="cursor:pointer">
                <th class="check-column" scope="row" style="padding-left: 6px;">
                    <label class="screen-reader-text" for=""></label>
                    <input type="checkbox" name="checked[]" class="wpvivid-custom-check wpvivid-custom-additional-file-check" <?php echo esc_attr($additional_file_check); ?> />
                </th>
                <td class="plugin-title column-primary wpvivid-backup-to-font wpvivid-handle-additional-file-detail">Additional Files/Folder</td>
                <td class="column-description desc wpvivid-handle-additional-file-detail additional-file-desc"><?php echo esc_html($additional_file_descript); ?></td>
                <th class="wpvivid-handle-additional-file-detail">
                    <details class="primer" onclick="return false;" style="display: inline-block; width: 100%;">
                        <summary title="Show detail" style="float: right; color: #a0a5aa;"></summary>
                    </details>
                </th>
            </tr>
            <tr class="wpvivid-custom-detail wpvivid-additional-file-detail wpvivid-close" style="<?php echo esc_attr($additional_file_text_style); ?> display: none;">
                <th class="check-column"></th>
                <td colspan="3" class="plugin-title column-primary">
                    <table class="wp-list-table widefat plugins" style="width:100%;">
                        <thead>
                        <tr>
                            <th class="manage-column column-name column-primary" style="border-bottom: 1px solid #e1e1e1 !important;">
                                <label class="wpvivid-refresh-tree wpvivid-refresh-additional-file-tree" style="margin-bottom: 0; font-size: 13px;">Click Here to Refresh Folder/File Tree</label>
                            </th>
                            <th class="manage-column column-description" style="font-size: 13px; border-bottom: 1px solid #e1e1e1 !important;">Checked Folders or Files to Transfer</th>
                        </tr>
                        </thead>
                        <tbody>
                        <tr>
                            <td class="wpvivid-custom-uploads-left" style="padding-right: 0;">
                                <div class="wpvivid-custom-uploads-tree">
                                    <div class="wpvivid-custom-tree wpvivid-custom-additional-file-tree-info"></div>
                                </div>
                            </td>
                            <td class="wpvivid-custom-uploads-right">
                                <div class="wpvivid-custom-uploads-table wpvivid-custom-include-additional-file-list">
                                    <?php
                                    if(!$this->is_staging_site){
                                        $this->wpvivid_load_additional_file();
                                    }
                                    ?>
                                </div>
                            </td>
                        </tr>
                        </tbody>
                        <tfoot>
                        <tr>
                            <td colspan="2">
                                <div style="float: left; margin-right: 10px;">
                                    <input class="button-primary wpvivid-include-additional-file-btn" type="submit" value="Include folders/files" disabled />
                                </div>
                                <small>
                                    <div class="wpvivid_tooltip" style="margin-top: 8px; float: left; line-height: 100%; white-space: normal;">?
                                        <div class="wpvivid_tooltiptext">Double click to open the folder tree, press Ctrl + left-click to select multiple items.</div>
                                    </div>
                                </small>
                                <div style="clear: both;"></div>
                            </td>
                        </tr>
                        </tfoot>
                        <div style="clear:both;"></div>
                    </table>
                    <div style="margin-top: 10px;">
                        <div style="float: left; margin-right: 10px;">
                            <input type="text" class="regular-text wpvivid-additional-file-extension" placeholder="Exclude file types, for example: gif,jpg,webp" value="<?php echo esc_attr($additional_file_extension); ?>" onkeyup="value=value.replace(/[^a-zA-Z0-9\-_,]/g,'')"/>
                            <input type="button" class="wpvivid-additional-file-extension-rule-btn" value="Save" />
                        </div>
                        <small>
                            <div class="wpvivid_tooltip" style="margin-top: 8px; float: left; line-height: 100%; white-space: normal;">?
                                <div class="wpvivid_tooltiptext">Exclude file types from the copy. All file types are separated by commas, for example: jpg, gif, tmp etc (without a dot before the file type).</div>
                            </div>
                        </small>
                        <div style="clear: both;"></div>
                    </div>
                </td>
            </tr>
            </tbody>
        </table>
        <?php
    }

    public function wpvivid_load_custom_upload(){
        $options = get_option('wpvivid_staging_history', array());
        $ret = '';
        if(isset($options['uploads_list']) && !empty($options['uploads_list']))
        {
            foreach ($options['uploads_list'] as $index => $value) {
                echo '<ul style=\'margin: 0;\'>
                            <li>
                                <div class="'.esc_attr($value['type']).'"></div>
                                <div class="wpvivid-custom-li-font">'.esc_html($value['name']).'</div>
                                <div class="wpvivid-custom-li-close" onclick="wpvivid_remove_custom_tree(this);" title="Remove" style="cursor: pointer;">X</div>
                            </li>
                         </ul>';
            }
        }
    }

    public function wpvivid_load_custom_content(){
        $options = get_option('wpvivid_staging_history', array());
        $ret = '';
        if(isset($options['content_list']) && !empty($options['content_list'])) {
            foreach ($options['content_list'] as $index => $value) {
                echo '<ul style=\'margin: 0;\'>
                            <li>
                                <div class="'.esc_attr($value['type']).'"></div>
                                <div class="wpvivid-custom-li-font">'.esc_html($value['name']).'</div>
                                <div class="wpvivid-custom-li-close" onclick="wpvivid_remove_custom_tree(this);" title="Remove" style="cursor: pointer;">X</div>
                            </li>
                         </ul>';
            }
        }
    }

    public function wpvivid_load_additional_file(){
        $options = get_option('wpvivid_staging_history', array());
        $ret = '';
        if(isset($options['additional_file_list']) && !empty($options['additional_file_list'])) {
            foreach ($options['additional_file_list'] as $index => $value) {
                echo '<ul style=\'margin: 0;\'>
                            <li>
                                <div class="'.esc_attr($value['type']).'"></div>
                                <div class="wpvivid-custom-li-font">'.esc_html($value['name']).'</div>
                                <div class="wpvivid-custom-li-close" onclick="wpvivid_remove_custom_tree(this);" title="Remove" style="cursor: pointer;">X</div>
                            </li>
                         </ul>';
            }
        }
    }

    public function load_js(){
        $upload_dir = wp_upload_dir();
        $upload_path = $this->is_staging_site === false ?  $upload_dir['basedir'] : $this->staging_home_path.'/wp-content/uploads';
        $upload_path = str_replace('\\','/',$upload_path);
        $upload_path = $upload_path.'/';
        $this->custom_uploads_path = $upload_path;

        $content_dir = $this->is_staging_site === false ? WP_CONTENT_DIR : $this->staging_home_path.'/wp-content';
        $content_path = str_replace('\\','/',$content_dir);
        $content_path = $content_path.'/';
        $this->custom_content_path = $content_path;

        $additional_file_path = $this->is_staging_site === false ? str_replace('\\','/',get_home_path()) : str_replace('\\','/',$this->staging_home_path);
        $this->custom_additional_file_path = $additional_file_path;
        ?>
        <script>
            function wpvivid_handle_custom_open_close(obj, sub_obj){
                if(obj.hasClass('wpvivid-close')) {
                    sub_obj.hide();
                    sub_obj.prev().find('details').prop('open', false);
                    sub_obj.removeClass('wpvivid-open');
                    sub_obj.addClass('wpvivid-close');
                    sub_obj.prev().css('background-color', '#fff');
                    obj.prev().css('background-color', '#f1f1f1');
                    obj.prev().find('details').prop('open', true);
                    obj.show();
                    obj.removeClass('wpvivid-close');
                    obj.addClass('wpvivid-open');
                }
                else{
                    obj.hide();
                    obj.prev().css('background-color', '#fff');
                    obj.prev().find('details').prop('open', false);
                    obj.removeClass('wpvivid-open');
                    obj.addClass('wpvivid-close');
                }
            }

            jQuery('#<?php echo esc_attr($this->parent_id); ?>').on("click", '.wpvivid-handle-database-detail', function() {
                var obj = jQuery('#<?php echo esc_attr($this->parent_id); ?>').find('.wpvivid-database-detail');
                var sub_obj = jQuery('#<?php echo esc_attr($this->parent_id); ?>').find('.wpvivid-custom-detail');
                wpvivid_handle_custom_open_close(obj, sub_obj);
            });

            jQuery('#<?php echo esc_attr($this->parent_id); ?>').on("click", '.wpvivid-handle-themes-plugins-detail', function() {
                var obj = jQuery('#<?php echo esc_attr($this->parent_id); ?>').find('.wpvivid-themes-plugins-detail');
                var sub_obj = jQuery('#<?php echo esc_attr($this->parent_id); ?>').find('.wpvivid-custom-detail');
                wpvivid_handle_custom_open_close(obj, sub_obj);
            });

            jQuery('#<?php echo esc_attr($this->parent_id); ?>').on("click", '.wpvivid-handle-uploads-detail', function() {
                var obj = jQuery('#<?php echo esc_attr($this->parent_id); ?>').find('.wpvivid-uploads-detail');
                var sub_obj = jQuery('#<?php echo esc_attr($this->parent_id); ?>').find('.wpvivid-custom-detail');
                wpvivid_handle_custom_open_close(obj, sub_obj);
            });

            jQuery('#<?php echo esc_attr($this->parent_id); ?>').on("click", '.wpvivid-handle-content-detail', function() {
                var obj = jQuery('#<?php echo esc_attr($this->parent_id); ?>').find('.wpvivid-content-detail');
                var sub_obj = jQuery('#<?php echo esc_attr($this->parent_id); ?>').find('.wpvivid-custom-detail');
                wpvivid_handle_custom_open_close(obj, sub_obj);
            });

            jQuery('#<?php echo esc_attr($this->parent_id); ?>').on("click", '.wpvivid-handle-additional-file-detail', function() {
                var obj = jQuery('#<?php echo esc_attr($this->parent_id); ?>').find('.wpvivid-additional-file-detail');
                var sub_obj = jQuery('#<?php echo esc_attr($this->parent_id); ?>').find('.wpvivid-custom-detail');
                wpvivid_handle_custom_open_close(obj, sub_obj);
            });

            jQuery('#<?php echo esc_attr($this->parent_id); ?>').on("click", '.wpvivid-custom-check', function() {
                if (jQuery(this).prop('checked')) {
                    if(!jQuery(this).hasClass('wpvivid-custom-core-check')) {
                        jQuery(jQuery(this).parents('tr').next().get(0)).css({'pointer-events': 'auto', 'opacity': '1'});
                    }
                }
                else{
                    var check_status = false;
                    jQuery('#<?php echo esc_attr($this->parent_id); ?>').find('.wpvivid-custom-check').each(function(){
                        if(jQuery(this).prop('checked')){
                            check_status = true;
                        }
                    });
                    if(check_status) {
                        if (!jQuery(this).hasClass('wpvivid-custom-core-check')) {
                            jQuery(jQuery(this).parents('tr').next().get(0)).css({'pointer-events': 'none', 'opacity': '0.4'});
                        }
                    }
                    else{
                        jQuery(this).prop('checked', true);
                        alert('Please select at least one item under Custom option.');
                    }
                }
            });

            jQuery('#<?php echo esc_attr($this->parent_id); ?>').on("click", '.wpvivid-database-table-check', function() {
                if(jQuery(this).prop('checked')){
                    if(jQuery(this).hasClass('wpvivid-database-base-table-check')){
                        jQuery('#<?php echo esc_attr($this->parent_id); ?>').find('input:checkbox[option=base_db][name=Database]').prop('checked', true);
                    }
                    else if(jQuery(this).hasClass('wpvivid-database-woo-table-check')){
                        jQuery('#<?php echo esc_attr($this->parent_id); ?>').find('input:checkbox[option=woo_db][name=Database]').prop('checked', true);
                    }
                    else if(jQuery(this).hasClass('wpvivid-database-other-table-check')){
                        jQuery('#<?php echo esc_attr($this->parent_id); ?>').find('input:checkbox[option=other_db][name=Database]').prop('checked', true);
                    }
                }
                else{
                    var check_status = false;
                    if (jQuery(this).hasClass('wpvivid-database-base-table-check')) {
                        jQuery('#<?php echo esc_attr($this->parent_id); ?>').find('input:checkbox[option=other_db][name=Database]').each(function(){
                            if(jQuery(this).prop('checked')){
                                check_status = true;
                            }
                        });
                        jQuery('#<?php echo esc_attr($this->parent_id); ?>').find('input:checkbox[option=woo_db][name=Database]').each(function(){
                            if(jQuery(this).prop('checked')){
                                check_status = true;
                            }
                        });
                        if(check_status) {
                            jQuery('#<?php echo esc_attr($this->parent_id); ?>').find('input:checkbox[option=base_db][name=Database]').prop('checked', false);
                        }
                        else{
                            jQuery(this).prop('checked', true);
                            alert('Please select at least one table type under the Database option, or deselect the option.');
                        }
                    }
                    else if (jQuery(this).hasClass('wpvivid-database-woo-table-check')) {
                        jQuery('#<?php echo esc_attr($this->parent_id); ?>').find('input:checkbox[option=base_db][name=Database]').each(function(){
                            if(jQuery(this).prop('checked')){
                                check_status = true;
                            }
                        });
                        jQuery('#<?php echo esc_attr($this->parent_id); ?>').find('input:checkbox[option=other_db][name=Database]').each(function(){
                            if(jQuery(this).prop('checked')){
                                check_status = true;
                            }
                        });
                        if(check_status) {
                            jQuery('#<?php echo esc_attr($this->parent_id); ?>').find('input:checkbox[option=woo_db][name=Database]').prop('checked', false);
                        }
                        else{
                            jQuery(this).prop('checked', true);
                            alert('Please select at least one table type under the Database option, or deselect the option.');
                        }
                    }
                    else if (jQuery(this).hasClass('wpvivid-database-other-table-check')) {
                        jQuery('#<?php echo esc_attr($this->parent_id); ?>').find('input:checkbox[option=base_db][name=Database]').each(function(){
                            if(jQuery(this).prop('checked')){
                                check_status = true;
                            }
                        });
                        jQuery('#<?php echo esc_attr($this->parent_id); ?>').find('input:checkbox[option=woo_db][name=Database]').each(function(){
                            if(jQuery(this).prop('checked')){
                                check_status = true;
                            }
                        });
                        if(check_status) {
                            jQuery('#<?php echo esc_attr($this->parent_id); ?>').find('input:checkbox[option=other_db][name=Database]').prop('checked', false);
                        }
                        else{
                            jQuery(this).prop('checked', true);
                            alert('Please select at least one table type under the Database option, or deselect the option.');
                        }
                    }
                }
            });

            jQuery('#<?php echo esc_attr($this->parent_id); ?>').on("click", 'input:checkbox[option=base_db][name=Database]', function(){
                if(jQuery(this).prop('checked')){
                    var all_check = true;
                    jQuery('#<?php echo esc_attr($this->parent_id); ?>').find('input:checkbox[option=base_db][name=Database]').each(function(){
                        if(!jQuery(this).prop('checked')){
                            all_check = false;
                        }
                    });
                    if(all_check){
                        jQuery('#<?php echo esc_attr($this->parent_id); ?>').find('.wpvivid-database-base-table-check').prop('checked', true);
                    }
                }
                else{
                    var check_status = false;
                    jQuery('#<?php echo esc_attr($this->parent_id); ?>').find('input:checkbox[name=Database]').each(function(){
                        if(jQuery(this).prop('checked')){
                            check_status = true;
                        }
                    });
                    if(check_status){
                        jQuery('#<?php echo esc_attr($this->parent_id); ?>').find('.wpvivid-database-base-table-check').prop('checked', false);
                    }
                    else{
                        jQuery(this).prop('checked', true);
                        alert('Please select at least one table type under the Database option, or deselect the option.');
                    }
                }
            });

            jQuery('#<?php echo esc_attr($this->parent_id); ?>').on("click", 'input:checkbox[option=woo_db][name=Database]', function(){
                if(jQuery(this).prop('checked')){
                    var all_check = true;
                    jQuery('#<?php echo esc_attr($this->parent_id); ?>').find('input:checkbox[option=woo_db][name=Database]').each(function(){
                        if(!jQuery(this).prop('checked')){
                            all_check = false;
                        }
                    });
                    if(all_check){
                        jQuery('#<?php echo esc_attr($this->parent_id); ?>').find('.wpvivid-database-woo-table-check').prop('checked', true);
                    }
                }
                else{
                    var check_status = false;
                    jQuery('#<?php echo esc_attr($this->parent_id); ?>').find('input:checkbox[name=Database]').each(function(){
                        if(jQuery(this).prop('checked')){
                            check_status = true;
                        }
                    });
                    if(check_status){
                        jQuery('#<?php echo esc_attr($this->parent_id); ?>').find('.wpvivid-database-woo-table-check').prop('checked', false);
                    }
                    else{
                        jQuery(this).prop('checked', true);
                        alert('Please select at least one table type under the Database option, or deselect the option.');
                    }
                }
            });

            jQuery('#<?php echo esc_attr($this->parent_id); ?>').on("click", 'input:checkbox[option=other_db][name=Database]', function(){
                if(jQuery(this).prop('checked')){
                    var all_check = true;
                    jQuery('#<?php echo esc_attr($this->parent_id); ?>').find('input:checkbox[option=other_db][name=Database]').each(function(){
                        if(!jQuery(this).prop('checked')){
                            all_check = false;
                        }
                    });
                    if(all_check){
                        jQuery('#<?php echo esc_attr($this->parent_id); ?>').find('.wpvivid-database-other-table-check').prop('checked', true);
                    }
                }
                else{
                    var check_status = false;
                    jQuery('#<?php echo esc_attr($this->parent_id); ?>').find('input:checkbox[name=Database]').each(function(){
                        if(jQuery(this).prop('checked')){
                            check_status = true;
                        }
                    });
                    if(check_status){
                        jQuery('#<?php echo esc_attr($this->parent_id); ?>').find('.wpvivid-database-other-table-check').prop('checked', false);
                    }
                    else{
                        jQuery(this).prop('checked', true);
                        alert('Please select at least one table type under the Database option, or deselect the option.');
                    }
                }
            });

            jQuery('#<?php echo esc_attr($this->parent_id); ?>').on("click", '.wpvivid-themes-plugins-table-check', function(){
                if(jQuery(this).prop('checked')){
                    if(jQuery(this).hasClass('wpvivid-themes-table-check')){
                        jQuery('#<?php echo esc_attr($this->parent_id); ?>').find('input:checkbox[option=themes][name=Themes]').prop('checked', true);
                    }
                    else if(jQuery(this).hasClass('wpvivid-plugins-table-check')){
                        jQuery('#<?php echo esc_attr($this->parent_id); ?>').find('input:checkbox[option=plugins][name=Plugins]').prop('checked', true);
                    }
                }
                else{
                    var check_status = false;
                    if (jQuery(this).hasClass('wpvivid-themes-table-check')) {
                        jQuery('#<?php echo esc_attr($this->parent_id); ?>').find('input:checkbox[option=plugins][name=Plugins]').each(function(){
                            if(jQuery(this).prop('checked')){
                                check_status = true;
                            }
                        });
                        if(check_status) {
                            jQuery('#<?php echo esc_attr($this->parent_id); ?>').find('input:checkbox[option=themes][name=Themes]').prop('checked', false);
                        }
                        else{
                            jQuery(this).prop('checked', true);
                            alert('Please select at least one item under the Themes and Plugins option, or deselect the option.');
                        }
                    }
                    else if (jQuery(this).hasClass('wpvivid-plugins-table-check')) {
                        jQuery('#<?php echo esc_attr($this->parent_id); ?>').find('input:checkbox[option=themes][name=Themes]').each(function(){
                            if(jQuery(this).prop('checked')){
                                check_status = true;
                            }
                        });
                        if(check_status) {
                            jQuery('#<?php echo esc_attr($this->parent_id); ?>').find('input:checkbox[option=plugins][name=Plugins]').each(function(){
                                if(jQuery(this).val() !== 'wpvivid-backuprestore' && jQuery(this).val() !== 'wpvivid-backup-pro'){
                                    jQuery(this).prop('checked', false);
                                }
                            });
                        }
                        else{
                            jQuery(this).prop('checked', true);
                            alert('Please select at least one item under the Themes and Plugins option, or deselect the option.');
                        }
                    }
                }
            });

            jQuery('#<?php echo esc_attr($this->parent_id); ?>').on("click", 'input:checkbox[option=themes][name=Themes]', function(){
                if(jQuery(this).prop('checked')){
                    var all_check = true;
                    jQuery('#<?php echo esc_attr($this->parent_id); ?>').find('input:checkbox[option=themes][name=Themes]').each(function(){
                        if(!jQuery(this).prop('checked')){
                            all_check = false;
                        }
                    });
                    if(all_check){
                        jQuery('#<?php echo esc_attr($this->parent_id); ?>').find('.wpvivid-themes-table-check').prop('checked', true);
                    }
                }
                else{
                    var check_status = false;
                    jQuery('#<?php echo esc_attr($this->parent_id); ?>').find('input:checkbox[option=themes][name=Themes]').each(function(){
                        if(jQuery(this).prop('checked')){
                            check_status = true;
                        }
                    });
                    if(!check_status){
                        jQuery('#<?php echo esc_attr($this->parent_id); ?>').find('input:checkbox[option=plugins][name=Plugins]').each(function(){
                            if(jQuery(this).prop('checked')){
                                check_status = true;
                            }
                        });
                    }
                    if(check_status){
                        jQuery('#<?php echo esc_attr($this->parent_id); ?>').find('.wpvivid-themes-table-check').prop('checked', false);
                    }
                    else{
                        jQuery(this).prop('checked', true);
                        alert('Please select at least one item under the Themes and Plugins option, or deselect the option.');
                    }
                }
            });

            jQuery('#<?php echo esc_attr($this->parent_id); ?>').on("click", 'input:checkbox[option=plugins][name=Plugins]', function(){
                if(jQuery(this).prop('checked')){
                    var all_check = true;
                    jQuery('#<?php echo esc_attr($this->parent_id); ?>').find('input:checkbox[option=plugins][name=Plugins]').each(function(){
                        if(!jQuery(this).prop('checked')){
                            all_check = false;
                        }
                    });
                    if(all_check){
                        jQuery('#<?php echo esc_attr($this->parent_id); ?>').find('.wpvivid-plugins-table-check').prop('checked', true);
                    }
                }
                else{
                    var check_status = false;
                    jQuery('#<?php echo esc_attr($this->parent_id); ?>').find('input:checkbox[option=plugins][name=Plugins]').each(function(){
                        if(jQuery(this).prop('checked')){
                            check_status = true;
                        }
                    });
                    if(!check_status){
                        jQuery('#<?php echo esc_attr($this->parent_id); ?>').find('input:checkbox[option=themes][name=Themes]').each(function(){
                            if(jQuery(this).prop('checked')){
                                check_status = true;
                            }
                        });
                    }
                    if(check_status){
                        jQuery('#<?php echo esc_attr($this->parent_id); ?>').find('.wpvivid-plugins-table-check').prop('checked', false);
                    }
                    else{
                        jQuery(this).prop('checked', true);
                        alert('Please select at least one item under the Themes and Plugins option, or deselect the option.');
                    }
                }
            });

            jQuery('#<?php echo esc_attr($this->parent_id); ?>').on("click", '.wpvivid-uploads-extension-rule-btn', function(){
                var value = jQuery(this).prev().val();
                if(value!=='') {
                    wpvivid_update_staging_exclude_extension('upload', value);
                }
            });

            jQuery('#<?php echo esc_attr($this->parent_id); ?>').on("click", '.wpvivid-content-extension-rule-btn', function(){
                var value = jQuery(this).prev().val();
                if(value!=='') {
                    wpvivid_update_staging_exclude_extension('content', value);
                }
            });

            jQuery('#<?php echo esc_attr($this->parent_id); ?>').on("click", '.wpvivid-additional-file-extension-rule-btn', function(){
                var value = jQuery(this).prev().val();
                if(value!=='') {
                    wpvivid_update_staging_exclude_extension('additional_file', value);
                }
            });

            function wpvivid_update_staging_exclude_extension(type, value){
                var ajax_data = {
                    'action': 'wpvividstg_update_staging_exclude_extension_free',
                    'type': type,
                    'exclude_content': value
                };
                jQuery(this).css({'pointer-events': 'none', 'opacity': '0.4'});
                wpvivid_post_request(ajax_data, function (data) {
                    jQuery(this).css({'pointer-events': 'auto', 'opacity': '1'});
                    try {
                        var jsonarray = jQuery.parseJSON(data);
                        if (jsonarray.result === 'success') {
                        }
                    }
                    catch (err) {
                        alert(err);
                    }
                }, function (XMLHttpRequest, textStatus, errorThrown) {
                    jQuery(this).css({'pointer-events': 'auto', 'opacity': '1'});
                    var error_message = wpvivid_output_ajaxerror('saving staging extension', textStatus, errorThrown);
                    alert(error_message);
                });
            }

            jQuery('#<?php echo esc_attr($this->parent_id); ?>').on("click", '.wpvivid-custom-li-close', function(){
                jQuery(this).parent().parent().remove();
            });
        </script>
        <?php
    }
}

class WPvivid_Staging_Sites_List_Free
{
    public function __construct()
    {

    }
}includes/staging/class-wpvivid-staging.php000064400000346061151327705670014773 0ustar00<?php
if (!defined('WPVIVID_PLUGIN_DIR'))
{
    die;
}

if ( ! class_exists( 'WP_List_Table' ) )
{
    require_once( ABSPATH . 'wp-admin/includes/class-wp-list-table.php' );
}

class WPvivid_Staging_Free
{
    public $main_tab;
    public $end_shutdown_function;
    public $screen_ids;

    public $log;
    public $log_page;
    public $new_wp_page;
    public $ui_display;
    public $setting;

    public function __construct()
    {
        if(is_admin())
        {
            include_once WPVIVID_PLUGIN_DIR . '/includes/staging/class-wpvivid-staging-copy-db-ex.php';
            include_once WPVIVID_PLUGIN_DIR . '/includes/staging/class-wpvivid-staging-copy-files-ex.php';
            include_once WPVIVID_PLUGIN_DIR . '/includes/staging/class-wpvivid-staging-task-ex.php';
            include_once WPVIVID_PLUGIN_DIR . '/includes/staging/class-wpvivid-staging-log.php';
            include_once WPVIVID_PLUGIN_DIR . '/includes/staging/class-wpvivid-staging-log-page.php';
            include_once WPVIVID_PLUGIN_DIR . '/includes/staging/class-wpvivid-staging-create-new-wp.php';
            include_once WPVIVID_PLUGIN_DIR . '/includes/staging/class-wpvivid-staging-ui-display.php';
            include_once WPVIVID_PLUGIN_DIR . '/includes/staging/class-wpvivid-staging-setting.php';
            include_once WPVIVID_PLUGIN_DIR . '/includes/staging/class-wpvivid-staging-sites-list.php';

            $this->log=new WPvivid_Staging_Log_Free();
            $this->log_page=new WPvivid_Staging_Log_Page_Free();
            $this->ui_display=new WPvivid_Staging_UI_Display_Free();
            $this->setting=new WPvivid_Staging_Setting_Free();


            add_action('admin_enqueue_scripts',array( $this,'enqueue_styles'));
            add_action('admin_enqueue_scripts',array( $this,'enqueue_scripts'));

            //add_filter('wpvivid_add_side_bar', array($this, 'add_side_bar'), 11, 2);
            add_action('wpvivid_add_staging_side_bar_ex', array($this, 'add_side_bar'), 11, 2);

            add_filter('wpvivid_get_toolbar_menus',array($this,'get_toolbar_menus'),22);
            add_filter('wpvivid_get_admin_menus',array($this,'get_admin_menus'),22);
            add_filter('wpvivid_get_screen_ids',array($this,'get_screen_ids'),12);

            $this->load_ajax();
        }

        add_filter('wpvividstg_get_admin_url',array($this,'get_admin_url'),10);
        add_filter('wpvivid_add_staging_side_bar', array($this, 'wpvivid_add_staging_side_bar'), 11, 2);

        add_action( "init",array($this,'staging_site'));
    }

    public function add_side_bar($html, $show_schedule = false)
    {
        if(get_current_screen()->id=='wpvivid-backup_page_wpvivid-staging')
        {
            $wpvivid_version = WPVIVID_PLUGIN_VERSION;
            $wpvivid_version = apply_filters('wpvivid_display_pro_version', $wpvivid_version);

            ?>
            <div class="postbox">
                <h2>
                    <div style="float: left; margin-right: 5px;"><span style="margin: 0; padding: 0"><?php esc_html_e('Current Version: ', 'wpvivid-backuprestore'); ?><?php echo esc_html($wpvivid_version); ?></span></div>
                    <div style="float: left; margin-right: 5px;"><span style="margin: 0; padding: 0">|</span></div>
                    <div style="float: left; margin-left: 0;">
                        <span style="margin: 0; padding: 0"><a href="https://wordpress.org/plugins/wpvivid-backuprestore/#developers" target="_blank" style="text-decoration: none;"><?php esc_html_e('ChangeLog', 'wpvivid-backuprestore'); ?></a></span>
                    </div>
                    <div style="clear: both;"></div>
                </h2>
            </div>
            <div id="wpvivid_backup_schedule_part"></div>
            <div class="postbox">
                <h2><span><?php esc_html_e('Troubleshooting', 'wpvivid-backuprestore'); ?></span></h2>
                <div class="inside">
                    <table class="widefat" cellpadding="0">
                        <tbody>
                        <tr class="alternate">
                            <td class="row-title">'Read <a href="https://docs.wpvivid.com/troubleshooting-issues-wpvivid-backup-plugin.html" target="_blank">Troubleshooting page</a> for faster solutions.'</td>
                        </tr>
                        </tbody>
                    </table>
                </div>
            </div>
            <div class="postbox">
                <h2><span><?php esc_html_e('How-to', 'wpvivid-backuprestore'); ?></span></h2>
                <div class="inside">
                    <table class="widefat" cellpadding="0">
                        <tbody>
                        <tr class="alternate"><td class="row-title"><a href="https://docs.wpvivid.com/wpvivid-backup-pro-create-staging-site.html" target="_blank"><?php esc_html_e('Create A Staging Site', 'wpvivid-backuprestore'); ?></a></td></tr>
                        <tr><td class="row-title"><a href="https://docs.wpvivid.com/wpvivid-staging-pro-create-fresh-install.html" target="_blank"><?php esc_html_e('Create A Fresh WordPress Install', 'wpvivid-backuprestore'); ?></a></td></tr>
                        </tbody>
                    </table>
                </div>
            </div>
            <?php
            /*$html = '<div class="postbox">
                <h2>
                    <div style="float: left; margin-right: 5px;"><span style="margin: 0; padding: 0">'.__('Current Version:', 'wpvivid-backuprestore').' '.$wpvivid_version.'</span></div>
                    <div style="float: left; margin-right: 5px;"><span style="margin: 0; padding: 0">|</span></div>
                    <div style="float: left; margin-left: 0;">
                        <span style="margin: 0; padding: 0"><a href="https://wordpress.org/plugins/wpvivid-backuprestore/#developers" target="_blank" style="text-decoration: none;">'.__('ChangeLog', 'wpvivid-backuprestore').'</a></span>
                    </div>
                    <div style="clear: both;"></div>
                </h2>
             </div>
             <div id="wpvivid_backup_schedule_part"></div>
             <div class="postbox">
                <h2><span>'.__('Troubleshooting', 'wpvivid-backuprestore').'</span></h2>
                <div class="inside">
                    <table class="widefat" cellpadding="0">
                        <tbody>
                        <tr class="alternate">
                            <td class="row-title">'.__('Read <a href="https://docs.wpvivid.com/troubleshooting-issues-wpvivid-backup-plugin.html" target="_blank">Troubleshooting page</a> for faster solutions.', 'wpvivid-backuprestore').'</td>
                        </tr>             
                        </tbody>
                    </table>
                </div>
             </div>
             <div class="postbox">
                <h2><span>'.__('How-to', 'wpvivid-backuprestore').'</span></h2>
                <div class="inside">
                    <table class="widefat" cellpadding="0">
                        <tbody>
                            <tr class="alternate"><td class="row-title"><a href="https://docs.wpvivid.com/wpvivid-backup-pro-create-staging-site.html" target="_blank">'.__('Create A Staging Site', 'wpvivid-backuprestore').'</a></td></tr>
                            <tr><td class="row-title"><a href="https://docs.wpvivid.com/wpvivid-staging-pro-create-fresh-install.html" target="_blank">'.__('Create A Fresh WordPress Install', 'wpvivid-backuprestore').'</a></td></tr>
                        </tbody>
                    </table>
                </div>
             </div>';*/
        }

        //return $html;
    }

    public function get_admin_url($admin_url)
    {
        if(is_multisite())
        {
            $admin_url = network_admin_url();
        }
        else
        {
            $admin_url =admin_url();
        }

        return $admin_url;
    }

    public function wpvivid_add_staging_side_bar($html, $show_schedule)
    {
        $html = '<h2 style="margin-top:0.5em;">
                     <span class="dashicons dashicons-sticky wpvivid-dashicons-orange"></span>
                     <span>Troubleshooting</span>
                 </h2>
                 <div class="inside" style="padding-top:0;">
                     <ul class="" >
                        <li style="border-top:1px solid #f1f1f1;"><span class="dashicons dashicons-editor-help wpvivid-dashicons-orange" ></span>
                            <a href="https://docs.wpvivid.com/troubleshooting-issues-wpvivid-staging-pro.html"><b>Troubleshooting</b></a>
                            <small><span style="float: right;"><a href="https://wpvivid.com/troubleshooting-issues-wpvivid-staging-pro" style="text-decoration: none;" target="_blank"><span class="dashicons dashicons-migrate wpvivid-dashicons-grey"></span></a></span></small><br>
                        </li>
                     </ul>
                 </div>
                 
                 <h2>
                     <span class="dashicons dashicons-book-alt wpvivid-dashicons-orange" ></span>
                     <span>Documentation</span>
                 </h2>
                 <div class="inside" style="padding-top:0;">
                     <ul class="">
                        <li style="border-top:1px solid #f1f1f1;"><span class="dashicons dashicons-migrate wpvivid-dashicons-blue"></span>
                            <a href="https://docs.wpvivid.com/wpvivid-backup-pro-create-staging-site.html"><b>Create A Staging Site</b></a>
                            <small><span style="float: right;"><a href="https://wpvivid.com/wpvivid-backup-pro-create-staging-site" style="text-decoration: none;"><span class="dashicons dashicons-migrate wpvivid-dashicons-grey"></span></a></span></small><br>
                        </li>
                        <li><span class="dashicons dashicons-migrate wpvivid-dashicons-blue"></span>
                            <a href="https://docs.wpvivid.com/wpvivid-staging-pro-publish-staging-to-live.html"><b>Publish A Staging Site</b></a>
                            <small><span style="float: right;"><a href="https://wpvivid.com/wpvivid-backup-pro-publish-staging-to-live" style="text-decoration: none;"><span class="dashicons dashicons-migrate wpvivid-dashicons-grey"></span></a></span></small><br>
                        </li>
                        <li><span class="dashicons dashicons-migrate wpvivid-dashicons-blue"></span>
                            <a href="https://docs.wpvivid.com/wpvivid-staging-pro-create-staging-site-for-wordpress-multisite.html"><b>Create A MU Staging</b></a>
                            <small><span style="float: right;"><a href="https://wpvivid.com/wpvivid-staging-pro-create-staging-site-for-wordpress-multisite" style="text-decoration: none;"><span class="dashicons dashicons-migrate wpvivid-dashicons-grey"></span></a></span></small><br>
                        </li>
                     </ul>
                 </div>';
        return $html;
    }

    public function load_ajax()
    {
        add_action('wp_ajax_wpvividstg_start_staging_free', array($this, 'start_staging'));
        //add_action('wp_ajax_nopriv_wpvividstg_start_staging_free', array($this, 'start_staging'));
        add_action('wp_ajax_wpvividstg_set_restart_staging_id_free', array($this, 'set_restart_staging_id'));
        add_action('wp_ajax_wpvividstg_get_staging_progress_free', array($this, 'get_staging_progress'));
        //add_action('wp_ajax_nopriv_wpvividstg_get_staging_progress_free', array($this, 'get_staging_progress'));
        add_action('wp_ajax_wpvividstg_delete_site_free', array($this, 'delete_site'));
        add_action('wp_ajax_wpvividstg_delete_cancel_staging_site_free', array($this, 'delete_cancel_staging_site'));
        add_action('wp_ajax_wpvividstg_check_staging_dir_free', array($this, 'check_staging_dir'));
        add_action('wp_ajax_wpvividstg_check_filesystem_permissions_free', array($this, 'check_filesystem_permissions'));
        //
        add_action('wp_ajax_wpvividstg_get_custom_database_tables_info_free',array($this, 'get_custom_database_tables_info'));

        add_action('wp_ajax_wpvividstg_cancel_staging_free', array($this, 'cancel_staging'));
        add_action('wp_ajax_wpvividstg_test_additional_database_connect_free', array($this, 'test_additional_database_connect'));
        add_action('wp_ajax_wpvividstg_update_staging_exclude_extension_free', array($this, 'update_staging_exclude_extension'));
        //

        add_action('wp_ajax_wpvividstg_get_custom_database_size_free', array($this, 'get_custom_database_size'));
        add_action('wp_ajax_wpvividstg_get_custom_files_size_free', array($this, 'get_custom_files_size'));
    }

    public function enqueue_styles()
    {
        $this->screen_ids=apply_filters('wpvivid_get_screen_ids',$this->screen_ids);
        if(get_current_screen()->id=='wpvivid-backup_page_wpvivid-staging')
        {
            wp_enqueue_style(WPVIVID_PLUGIN_SLUG.'jstree', WPVIVID_PLUGIN_DIR_URL . 'js/jstree/dist/themes/default/style.min.css', array(), WPVIVID_PLUGIN_VERSION, 'all');
            wp_enqueue_style(WPVIVID_PLUGIN_SLUG.'staging', WPVIVID_PLUGIN_DIR_URL . 'css/wpvivid-staging-custom.css', array(), WPVIVID_PLUGIN_VERSION, 'all');
        }
    }

    public function enqueue_scripts()
    {
        $this->screen_ids=apply_filters('wpvivid_get_screen_ids',$this->screen_ids);
        if(in_array(get_current_screen()->id,$this->screen_ids))
        {
            wp_enqueue_script(WPVIVID_PLUGIN_SLUG.'jstree', WPVIVID_PLUGIN_DIR_URL . 'js/jstree/dist/jstree.min.js', array('jquery'), WPVIVID_PLUGIN_VERSION, false);
            wp_enqueue_script('plupload-all');
        }
    }

    public function get_screen_ids($screen_ids)
    {
        $screen_ids[]=apply_filters('wpvivid_white_label_screen_id', 'wpvivid-backup_page_wpvivid-staging');
        return $screen_ids;
    }

    public function get_toolbar_menus($toolbar_menus)
    {
        $admin_url = apply_filters('wpvivid_get_admin_url', '');

        $menu['id']='wpvivid_admin_menu_staging';
        $menu['parent']='wpvivid_admin_menu';
        $menu['title']=__('Staging', 'wpvivid-backuprestore');
        $menu['tab']= 'admin.php?page='.apply_filters('wpvivid_white_label_plugin_name', 'wpvivid-staging');
        $menu['href']=$admin_url . 'admin.php?page='.apply_filters('wpvivid_white_label_plugin_name', 'wpvivid-staging');
        $menu['capability']='administrator';
        $menu['index']=3;
        $toolbar_menus[$menu['parent']]['child'][$menu['id']]=$menu;
        return $toolbar_menus;
    }

    public function get_admin_menus($submenus)
    {
        $submenu['parent_slug']=apply_filters('wpvivid_white_label_slug', WPVIVID_PLUGIN_SLUG);
        $submenu['page_title']= apply_filters('wpvivid_white_label_display', 'WPvivid Backup');
        $submenu['menu_title']=__('Staging', 'wpvivid-backuprestore');
        $submenu['capability']='administrator';
        $submenu['menu_slug']=strtolower(sprintf('%s-staging', apply_filters('wpvivid_white_label_slug', 'wpvivid')));
        $submenu['index']=3;
        $submenu['function']=array($this, 'display_plugin_setup_page');
        $submenus[$submenu['menu_slug']]=$submenu;
        return $submenus;
    }

    public function display_plugin_setup_page()
    {
        ?>
        <?php
        $this->ui_display->init_page();
        ?>
        <?php
    }

    public function get_database_site_url()
    {
        $site_url = site_url();
        global $wpdb;
        $site_url_sql = $wpdb->get_results( $wpdb->prepare( "SELECT * FROM $wpdb->options WHERE option_name = %s", 'siteurl' ) );
        foreach ( $site_url_sql as $site ){
            $site_url = $site->option_value;
        }
        return untrailingslashit($site_url);
    }

    public function get_database_home_url()
    {
        $home_url = home_url();
        global $wpdb;
        $home_url_sql = $wpdb->get_results( $wpdb->prepare( "SELECT * FROM $wpdb->options WHERE option_name = %s", 'home' ) );
        foreach ( $home_url_sql as $home ){
            $home_url = $home->option_value;
        }
        return untrailingslashit($home_url);
    }

    public function get_custom_database_size(){
        global $wpvivid_plugin;
        check_ajax_referer( 'wpvivid_ajax', 'nonce' );
        $check=current_user_can('manage_options');
        $check=apply_filters('wpvivid_ajax_check_security',$check);
        if(!$check)
        {
            die();
        }
        try
        {
            $ret['result']='success';

            global $wpdb;
            $tables = $wpdb->get_results('SHOW TABLE STATUS', ARRAY_A);
            if (is_null($tables)) {
                $ret['result'] = 'failed';
                $ret['error'] = 'Failed to retrieve the table information for the database. Please try again.';
                return $ret;
            }

            $db_size = 0;

            $base_table_size = 0;
            foreach ($tables as $row) {
                $base_table_size += ($row["Data_length"] + $row["Index_length"]);
            }

            $db_size = size_format($base_table_size, 2);

            $ret['database_size'] = $db_size;
            echo wp_json_encode($ret);
        }
        catch (Exception $error)
        {
            $message = 'An exception has occurred. class: '.get_class($error).';msg: '.$error->getMessage().';code: '.$error->getCode().';line: '.$error->getLine().';in_file: '.$error->getFile().';';
            error_log($message);
            echo wp_json_encode(array('result'=>'failed','error'=>$message));
        }
        die();
    }

    public static function get_custom_path_size($type, $path, $size=0){
        if(!function_exists('get_home_path'))
            require_once(ABSPATH . 'wp-admin/includes/file.php');
        $home_path = str_replace('\\','/', get_home_path());
        $core_file_arr = array('.htaccess', 'index', 'license.txt', 'readme.html', 'wp-activate.php', 'wp-blog-header.php', 'wp-comments-post.php', 'wp-config.php', 'wp-config-sample.php',
            'wp-cron.php', 'wp-links-opml.php', 'wp-load.php', 'wp-login.php', 'wp-mail.php', 'wp-settings.php', 'wp-signup.php', 'wp-trackback.php', 'xmlrpc.php');
        if(is_dir($path))
        {
            $handler = opendir($path);
            if($handler!==false)
            {
                while (($filename = readdir($handler)) !== false)
                {
                    if ($filename != "." && $filename != "..") {
                        if (is_dir($path . DIRECTORY_SEPARATOR . $filename))
                        {
                            if($type === 'content'){
                                if($filename !== 'plugins' && $filename !== 'themes' && $filename !== 'uploads'){
                                    $size=self::get_custom_path_size($type, $path . DIRECTORY_SEPARATOR . $filename, $size);
                                }
                            }
                            else if($type === 'core' && $home_path === $path){
                                if($filename === 'wp-admin' || $filename === 'wp-includes'){
                                    $size=self::get_custom_path_size($type, $path . DIRECTORY_SEPARATOR . $filename, $size);
                                }
                            }
                            else if($type === 'additional'){
                                if($filename !== 'wp-admin' && $filename !== 'wp-content' && $filename !== 'wp-includes'){
                                    $size=self::get_custom_path_size($type, $path . DIRECTORY_SEPARATOR . $filename, $size);
                                }
                            }
                            else{
                                $size=self::get_custom_path_size($type, $path . DIRECTORY_SEPARATOR . $filename, $size);
                            }
                        } else {
                            if($type === 'core'){
                                if($home_path === $path){
                                    if(in_array($filename, $core_file_arr)){
                                        $size+=filesize($path . DIRECTORY_SEPARATOR . $filename);
                                    }
                                }
                                else{
                                    $size+=filesize($path . DIRECTORY_SEPARATOR . $filename);
                                }
                            }
                            else if($type === 'additional'){
                                if($home_path === $path){
                                    if(!in_array($filename, $core_file_arr)){
                                        $size+=filesize($path . DIRECTORY_SEPARATOR . $filename);
                                    }
                                }
                                else{
                                    $size+=filesize($path . DIRECTORY_SEPARATOR . $filename);
                                }
                            }
                            else{
                                $size+=filesize($path . DIRECTORY_SEPARATOR . $filename);
                            }
                        }
                    }
                }
                if($handler)
                    @closedir($handler);
            }

        }
        return $size;
    }

    public function get_custom_files_size(){
        global $wpvivid_plugin;
        check_ajax_referer( 'wpvivid_ajax', 'nonce' );
        $check=current_user_can('manage_options');
        $check=apply_filters('wpvivid_ajax_check_security',$check);
        if(!$check)
        {
            die();
        }
        try
        {
            $upload_dir = wp_upload_dir();
            $path = $upload_dir['basedir'];
            $path = str_replace('\\','/',$path);
            $uploads_path = $path.'/';

            $content_dir = WP_CONTENT_DIR;
            $path = str_replace('\\','/',$content_dir);
            $content_path = $path.'/';

            if(!function_exists('get_home_path'))
                require_once(ABSPATH . 'wp-admin/includes/file.php');
            $home_path = str_replace('\\','/', get_home_path());

            $themes_path = str_replace('\\','/', get_theme_root());
            $themes_path = $themes_path.'/';

            $plugins_path = str_replace('\\','/', WP_PLUGIN_DIR);
            $plugins_path = $plugins_path.'/';

            $ret['result']='success';
            $core_size = self::get_custom_path_size('core', $home_path);
            $themes_size = self::get_custom_path_size('themes', $themes_path);
            $plugins_size = self::get_custom_path_size('plugins', $plugins_path);
            $uploads_size = self::get_custom_path_size('uploads', $uploads_path);
            $content_size = self::get_custom_path_size('content', $content_path);
            $additional_size = self::get_custom_path_size('additional', $home_path);
            $ret['core_size'] = size_format($core_size, 2);
            $ret['themes_size'] = size_format($themes_size, 2);
            $ret['plugins_size'] = size_format($plugins_size, 2);
            $ret['uploads_size'] = size_format($uploads_size, 2);
            $ret['content_size'] = size_format($content_size, 2);
            $ret['additional_size'] = size_format($additional_size, 2);
            $ret['total_file_size'] = size_format($core_size+$themes_size+$plugins_size+$uploads_size+$content_size+$additional_size, 2);
            echo wp_json_encode($ret);
        }
        catch (Exception $error)
        {
            $message = 'An exception has occurred. class: '.get_class($error).';msg: '.$error->getMessage().';code: '.$error->getCode().';line: '.$error->getLine().';in_file: '.$error->getFile().';';
            error_log($message);
            echo wp_json_encode(array('result'=>'failed','error'=>$message));
        }
        die();
    }

    public function get_staging_site_data()
    {
        if(is_multisite())
        {
            switch_to_blog(get_main_site_id());
            $staging=get_option('wpvivid_staging_data',false);
            restore_current_blog();
        }
        else
        {
            $staging=get_option('wpvivid_staging_data',false);
        }

        return $staging;
    }

    public function get_custom_database_tables_info()
    {
        global $wpvivid_plugin;
        check_ajax_referer( 'wpvivid_ajax', 'nonce' );
        $check=current_user_can('manage_options');
        $check=apply_filters('wpvivid_ajax_check_security',$check);
        if(!$check)
        {
            die();
        }
        try {
            global $wpdb;
            $db = array();
            $use_additional_db = false;
            $staging_site_id = sanitize_key($_POST['id']);
            if(empty($_POST['id']))
            {
                $get_site_mu_single=false;
            }
            else
            {
                $task = new WPvivid_Staging_Task($staging_site_id);
                $site_id=$task->get_site_mu_single_site_id();
                $get_site_mu_single=$task->get_site_mu_single();
            }


            if (isset($_POST['is_staging']) && !empty($_POST['is_staging']) && is_string($_POST['is_staging'])&&$_POST['is_staging'] == '1')
            {
                $base_prefix = $task->get_site_prefix();
            }
            else
            {
                $base_prefix=$wpdb->base_prefix;
            }

            if (isset($_POST['is_staging']) && !empty($_POST['is_staging']) && is_string($_POST['is_staging']))
            {
                if ($_POST['is_staging'] == '1')
                {
                    $is_staging_site = true;

                    $prefix = $task->get_site_prefix();

                    $db = $task->get_site_db_connect();
                    if ($db['use_additional_db'] !== false)
                    {
                        $use_additional_db = true;
                    } else {
                        $use_additional_db = false;
                    }
                } else {
                    $is_staging_site = false;
                    $prefix = $wpdb->get_blog_prefix(0);
                }
            } else {
                $is_staging_site = false;
                $prefix = $wpdb->get_blog_prefix(0);
            }

            $ret['result'] = 'success';
            $ret['html'] = '';
            if (empty($prefix)) {
                echo wp_json_encode($ret);
                die();
            }

            $base_table = '';
            $woo_table = '';
            $other_table = '';
            $default_table = array($prefix . 'commentmeta', $prefix . 'comments', $prefix . 'links', $prefix . 'options', $prefix . 'postmeta', $prefix . 'posts', $prefix . 'term_relationships',
                $prefix . 'term_taxonomy', $prefix . 'termmeta', $prefix . 'terms', $prefix . 'usermeta', $prefix . 'users');
            $woo_table_arr = array($prefix.'actionscheduler_actions', $prefix.'actionscheduler_claims', $prefix.'actionscheduler_groups', $prefix.'actionscheduler_logs', $prefix.'aelia_dismissed_messages',
                $prefix.'aelia_exchange_rates_history', $prefix.'automatewoo_abandoned_carts', $prefix.'automatewoo_customer_meta', $prefix.'automatewoo_customers', $prefix.'automatewoo_events',
                $prefix.'automatewoo_guest_meta', $prefix.'automatewoo_guests', $prefix.'automatewoo_log_meta', $prefix.'automatewoo_logs', $prefix.'automatewoo_queue', $prefix.'automatewoo_queue_meta',
                $prefix.'automatewoo_unsubscribes', $prefix.'wc_admin_note_actions', $prefix.'wc_admin_notes', $prefix.'wc_am_api_activation', $prefix.'wc_am_api_resource', $prefix.'wc_am_associated_api_key',
                $prefix.'wc_am_secure_hash', $prefix.'wc_category_lookup', $prefix.'wc_customer_lookup', $prefix.'wc_download_log', $prefix.'wc_order_coupon_lookup', $prefix.'wc_order_product_lookup',
                $prefix.'wc_order_stats', $prefix.'wc_order_tax_lookup', $prefix.'wc_product_meta_lookup', $prefix.'wc_reserved_stock', $prefix.'wc_tax_rate_classes', $prefix.'wc_webhooks',
                $prefix.'woocommerce_api_keys', $prefix.'woocommerce_attribute_taxonomies', $prefix.'woocommerce_downloadable_product_permissions', $prefix.'woocommerce_log', $prefix.'woocommerce_order_itemmeta',
                $prefix.'woocommerce_order_items', $prefix.'woocommerce_payment_tokenmeta', $prefix.'woocommerce_payment_tokens', $prefix.'woocommerce_sessions', $prefix.'woocommerce_shipping_zone_locations',
                $prefix.'woocommerce_shipping_zone_methods', $prefix.'woocommerce_shipping_zones', $prefix.'woocommerce_tax_rate_locations', $prefix.'woocommerce_tax_rates');

            if ($is_staging_site) {
                $staging_option = self::wpvivid_get_push_staging_history();
                if (empty($staging_option)) {
                    $staging_option = array();
                }
                if ($use_additional_db) {
                    $handle = new wpdb($db['dbuser'], $db['dbpassword'], $db['dbname'], $db['dbhost']);
                    $tables = $handle->get_results('SHOW TABLE STATUS', ARRAY_A);
                } else {
                    $tables = $wpdb->get_results('SHOW TABLE STATUS', ARRAY_A);
                }
            } else {
                $staging_option = self::wpvivid_get_staging_history();
                if (empty($staging_option)) {
                    $staging_option = array();
                }
                $tables = $wpdb->get_results('SHOW TABLE STATUS', ARRAY_A);
            }

            if (is_null($tables)) {
                $ret['result'] = 'failed';
                $ret['error'] = 'Failed to retrieve the table information for the database. Please try again.';
                echo wp_json_encode($ret);
                die();
            }

            $tables_info = array();
            $has_base_table = false;
            $has_woo_table = false;
            $has_other_table = false;
            $base_count = 0;
            $woo_count = 0;
            $other_count = 0;
            $base_table_all_check = true;
            $woo_table_all_check = true;
            $other_table_all_check = true;
            foreach ($tables as $row)
            {
                if (preg_match('/^(?!' . $base_prefix . ')/', $row["Name"]) == 1)
                {
                    continue;
                }

                if($get_site_mu_single)
                {
                    $site_id=$task->get_site_mu_single_site_id();

                    if(!is_main_site($site_id))
                    {
                        if ( 1 == preg_match('/^' . $prefix . '/', $row["Name"]) )
                        {
                        }
                        else if ( 1 == preg_match('/^' . $base_prefix . '\d+_/', $row["Name"]) )
                        {
                            continue;
                        }
                        else
                        {
                            if($row["Name"]==$base_prefix.'users'||$row["Name"]==$base_prefix.'usermeta')
                            {

                            }
                            else
                            {
                                continue;
                            }
                        }
                    }
                    else
                    {
                        if ( 1 == preg_match('/^' . $base_prefix . '\d+_/', $row["Name"]) )
                        {
                            continue;
                        }
                        else
                        {
                            if($row["Name"]==$base_prefix.'blogs')
                                continue;
                            if($row["Name"]==$base_prefix.'blogmeta')
                                continue;
                            if($row["Name"]==$base_prefix.'sitemeta')
                                continue;
                            if($row["Name"]==$base_prefix.'site')
                                continue;
                        }
                    }
                }


                $tables_info[$row["Name"]]["Rows"] = $row["Rows"];
                $tables_info[$row["Name"]]["Data_length"] = size_format($row["Data_length"] + $row["Index_length"], 2);

                $checked = 'checked';
                if (!empty($staging_option['database_list'])) {
                    if ($is_staging_site) {
                        $tmp_row = $row["Name"];

                        $tmp_row = str_replace($base_prefix, $wpdb->base_prefix, $tmp_row);
                        if (in_array($tmp_row, $staging_option['database_list'])) {
                            $checked = '';
                        }
                    }
                    else if (in_array($row["Name"], $staging_option['database_list'])) {
                        $checked = '';
                    }
                }

                if (in_array($row["Name"], $default_table)) {
                    if ($checked == '') {
                        $base_table_all_check = false;
                    }
                    $has_base_table = true;

                    $base_table .= '<div class="wpvivid-text-line">
                                        <input type="checkbox" option="base_db" name="Database" value="'.esc_html($row["Name"]).'" '.esc_html($checked).' />
                                        <span class="wpvivid-text-line">'.esc_html($row["Name"]).'|Rows:'.$row["Rows"].'|Size:'.$tables_info[$row["Name"]]["Data_length"].'</span>
                                    </div>';
                    $base_count++;
                } else if(in_array($row['Name'], $woo_table_arr)){
                    if ($checked == '') {
                        $woo_table_all_check = false;
                    }
                    $has_woo_table = true;

                    $woo_table .= '<div class="wpvivid-text-line">
                                        <input type="checkbox" option="woo_db" name="Database" value="'.esc_html($row["Name"]).'" '.esc_html($checked).' />
                                        <span class="wpvivid-text-line">'.esc_html($row["Name"]).'|Rows:'.$row["Rows"].'|Size:'.$tables_info[$row["Name"]]["Data_length"].'</span>
                                   </div>';
                    $woo_count++;
                }
                else {
                    if ($checked == '') {
                        $other_table_all_check = false;
                    }
                    $has_other_table = true;

                    $other_table .= '<div class="wpvivid-text-line">
                                        <input type="checkbox" option="other_db" name="Database" value="'.esc_html($row["Name"]).'" '.esc_html($checked).' />
                                        <span class="wpvivid-text-line">'.esc_html($row["Name"]).'|Rows:'.$row["Rows"].'|Size:'.$tables_info[$row["Name"]]["Data_length"].'</span>
                                     </div>';
                    $other_count++;
                }
            }

            $ret['html'] = '<div style="padding-left:4em;margin-top:1em;">
                                        <div style="border-bottom:1px solid #eee;"></div>
                                     </div>';

            $base_table_html = '';
            $woo_table_html = '';
            $other_table_html = '';
            if ($has_base_table) {
                $base_all_check = '';
                if ($base_table_all_check) {
                    $base_all_check = 'checked';
                }
                $base_table_html .= '<div style="width:30%;float:left;box-sizing:border-box;padding-right:0.5em;padding-left:4em;">
                                        <div>
                                            <p>
                                                <span class="dashicons dashicons-list-view wpvivid-dashicons-blue"></span>
                                                <span><input type="checkbox" class="wpvivid-database-table-check wpvivid-database-base-table-check" '.esc_attr($base_all_check).'></span>
                                                <span><strong>Wordpress Default Tables</strong></span>
                                            </p>
                                        </div>
                                        <div style="height:250px;border:1px solid #eee;padding:0.2em 0.5em;overflow:auto;">
                                            '.$base_table.'
                                        </div>
                                        <div style="clear:both;"></div>
                                    </div>';
            }

            if ($has_other_table) {
                $other_all_check = '';
                if ($other_table_all_check) {
                    $other_all_check = 'checked';
                }

                if($has_woo_table){
                    $other_table_width = '40%';
                }
                else{
                    $other_table_width = '70%';
                }

                $other_table_html .= '<div style="width:'.$other_table_width.'; float:left;box-sizing:border-box;padding-left:0.5em;">
                                        <div>
                                            <p>
                                                <span class="dashicons dashicons-list-view wpvivid-dashicons-green"></span>
                                                <span><input type="checkbox" class="wpvivid-database-table-check wpvivid-database-other-table-check" '.esc_attr($other_all_check).'></span>
                                                <span><strong>Other Tables</strong></span>
                                            </p>
                                        </div>
                                        <div style="height:250px;border:1px solid #eee;padding:0.2em 0.5em;overflow-y:auto;">
                                            '.$other_table.'
                                        </div>
                                     </div>';
            }

            if($has_woo_table) {
                $woo_all_check = '';
                if ($woo_table_all_check) {
                    $woo_all_check = 'checked';
                }
                $woo_table_html .= '<div style="width:30%; float:left;box-sizing:border-box;padding-left:0.5em;">
                                        <div>
										    <p><span class="dashicons dashicons-list-view wpvivid-dashicons-orange"></span>
												<span><input type="checkbox" class="wpvivid-database-table-check wpvivid-database-woo-table-check" '.esc_attr($woo_all_check).'></span>
												<span><strong>WooCommerce Tables</strong></span>
											</p>
										</div>
										<div style="height:250px;border:1px solid #eee;padding:0.2em 0.5em;overflow:auto;">
											'.$woo_table.'
                                        </div>
                                    </div>';
            }

            $ret['html'] .= $base_table_html . $other_table_html . $woo_table_html;
            $ret['tables_info'] = $tables_info;
            echo wp_json_encode($ret);
        }
        catch (Exception $error) {
            $message = 'An exception has occurred. class: '.get_class($error).';msg: '.$error->getMessage().';code: '.$error->getCode().';line: '.$error->getLine().';in_file: '.$error->getFile().';';
            error_log($message);
            echo wp_json_encode(array('result'=>'failed','error'=>$message));
        }
        die();
    }

    public function wpvivid_replace_directory( $path ) {
        return preg_replace( '/[\\\\]+/', '/', $path );
    }

    public function getPath( $path, $wpcontentDir, $directory ) {
        $realPath = $this->wpvivid_replace_directory($directory->getRealPath());
        if( false === strpos( $realPath, $path ) ) {
            return false;
        }

        $path = str_replace( $wpcontentDir . '/', null, $this->wpvivid_replace_directory($directory->getRealPath()) );
        // Using strpos() for symbolic links as they could create nasty stuff in nix stuff for directory structures
        if( !$directory->isDir() ||
            strlen( $path ) < 1 ||
            (strpos( $this->wpvivid_replace_directory($directory->getRealPath()), $wpcontentDir . '/' . 'plugins' ) !== 0 &&
                strpos( $this->wpvivid_replace_directory($directory->getRealPath()), $wpcontentDir . '/' . 'themes' ) !== 0 &&
                strpos( $this->wpvivid_replace_directory($directory->getRealPath()), $wpcontentDir . '/' . 'uploads' ) !== 0 )
        ) {
            return false;
        }

        return $path;
    }

    public function wpvivid_search_staging_theme_directories($wpvivid_staging_themes_dir){
        if ( empty( $wpvivid_staging_themes_dir ) ) {
            return false;
        }
        $found_themes = array();
        $wpvivid_staging_themes_dir = (array) $wpvivid_staging_themes_dir;
        foreach ( $wpvivid_staging_themes_dir as $theme_root ) {
            $dirs = @ scandir( $theme_root );
            if ( ! $dirs ) {
                continue;
            }
            foreach ( $dirs as $dir ) {
                if ( ! is_dir( $theme_root . '/' . $dir ) || $dir[0] == '.' || $dir == 'CVS' ) {
                    continue;
                }
                if ( file_exists( $theme_root . '/' . $dir . '/style.css' ) ) {
                    $found_themes[ $dir ] = array(
                        'theme_file' => $dir . '/style.css',
                        'theme_root' => $theme_root,
                    );
                }
                else {
                    $found_theme = false;
                    $sub_dirs = @ scandir( $theme_root . '/' . $dir );
                    if ( ! $sub_dirs ) {
                        continue;
                    }
                    foreach ( $sub_dirs as $sub_dir ) {
                        if ( ! is_dir( $theme_root . '/' . $dir . '/' . $sub_dir ) || $dir[0] == '.' || $dir == 'CVS' ) {
                            continue;
                        }
                        if ( ! file_exists( $theme_root . '/' . $dir . '/' . $sub_dir . '/style.css' ) ) {
                            continue;
                        }
                        $found_themes[ $dir . '/' . $sub_dir ] = array(
                            'theme_file' => $dir . '/' . $sub_dir . '/style.css',
                            'theme_root' => $theme_root,
                        );
                        $found_theme = true;
                    }
                    if ( ! $found_theme ) {
                        $found_themes[ $dir ] = array(
                            'theme_file' => $dir . '/style.css',
                            'theme_root' => $theme_root,
                        );
                    }
                }
            }
        }
        asort( $found_themes );
        return $found_themes;
    }

    public function get_staging_themes_info($wpvivid_staging_themes_dir){
        $themes = array();
        $theme_directories = $this->wpvivid_search_staging_theme_directories($wpvivid_staging_themes_dir);
        if ( !empty( $theme_directories ) ) {
            foreach ( $theme_directories as $theme => $theme_root ) {
                $themes[ $theme ] = $theme_root['theme_root'] . '/' . $theme;
                $themes[ $theme ] = new WP_Theme( $theme, $theme_root['theme_root'] );
            }
        }
        return $themes;
    }

    public function get_staging_plugins_info($wpvivid_stating_plugins_dir){
        $wp_plugins  = array();
        $plugin_root = $wpvivid_stating_plugins_dir;
        $plugins_dir  = @ opendir( $plugin_root );
        $plugin_files = array();
        if ( $plugins_dir ) {
            while ( ( $file = readdir( $plugins_dir ) ) !== false ) {
                if ( substr( $file, 0, 1 ) == '.' ) {
                    continue;
                }
                if ( is_dir( $plugin_root . '/' . $file ) ) {
                    $plugins_subdir = @ opendir( $plugin_root . '/' . $file );
                    if ( $plugins_subdir ) {
                        while ( ( $subfile = readdir( $plugins_subdir ) ) !== false ) {
                            if ( substr( $subfile, 0, 1 ) == '.' ) {
                                continue;
                            }
                            if ( substr( $subfile, -4 ) == '.php' ) {
                                $plugin_files[] = "$file/$subfile";
                            }
                        }
                        closedir( $plugins_subdir );
                    }
                } else {
                    if ( substr( $file, -4 ) == '.php' ) {
                        $plugin_files[] = $file;
                    }
                }
            }
            closedir( $plugins_dir );
        }
        if ( !empty( $plugin_files ) ) {
            foreach ( $plugin_files as $plugin_file ) {
                if ( ! is_readable( "$plugin_root/$plugin_file" ) ) {
                    continue;
                }

                $plugin_data = get_plugin_data( "$plugin_root/$plugin_file", false, false );

                if ( empty( $plugin_data['Name'] ) ) {
                    continue;
                }

                $wp_plugins[ plugin_basename( $plugin_file ) ] = $plugin_data;
            }
        }
        return $wp_plugins;
    }

    public function get_staging_directory_info($path){
        $wpcontentDir = $path.DIRECTORY_SEPARATOR.'wp-content';
        $wpcontentDir = str_replace('\\', '/', $wpcontentDir);
        $tmp_path = str_replace('\\', '/', $path);
        if(!file_exists($wpcontentDir)){
            //return error
        }
        else {
            $directories = new \DirectoryIterator($wpcontentDir);
        }
        $wpvivid_staging_themes_dir  = '';
        $wpvivid_stating_plugins_dir = '';
        foreach ( $directories as $directory ) {
            if( false === ($path = $this->getPath( $tmp_path, $wpcontentDir, $directory )) ) {
                continue;
            }
            if($directory == 'themes'){
                $wpvivid_staging_themes_dir  = $wpcontentDir . '/' . 'themes';
            }
            if($directory == 'plugins'){
                $wpvivid_stating_plugins_dir = $wpcontentDir . '/' . 'plugins';
            }
        }
        $ret['themes_list']  = $this->get_staging_themes_info($wpvivid_staging_themes_dir);
        $ret['plugins_list'] = $this->get_staging_plugins_info($wpvivid_stating_plugins_dir);
        return $ret;
    }

    public function get_theme_plugin_info($root)
    {
        $theme_info['size']=$this->get_folder_size($root,0);
        return $theme_info;
    }

    public function get_folder_size($root,$size)
    {
        $count = 0;
        if(is_dir($root))
        {
            $handler = opendir($root);
            if($handler!==false)
            {
                while (($filename = readdir($handler)) !== false)
                {
                    if ($filename != "." && $filename != "..") {
                        $count++;

                        if (is_dir($root . DIRECTORY_SEPARATOR . $filename))
                        {
                            $size=$this->get_folder_size($root . DIRECTORY_SEPARATOR . $filename,$size);
                        } else {
                            $size+=filesize($root . DIRECTORY_SEPARATOR . $filename);
                        }
                    }
                }
                if($handler)
                    @closedir($handler);
            }

        }
        return $size;
    }

    public function staging_site()
    {
        $redirect=false;
        if(is_multisite())
        {
            switch_to_blog(get_main_site_id());
            $staging_init=get_option('wpvivid_staging_init', false);
            $staging_finish=get_option('wpvivid_staging_finish', false);
            restore_current_blog();
        }
        else
        {
            $staging_init=get_option('wpvivid_staging_init', false);
            $staging_finish=get_option('wpvivid_staging_finish', false);
        }

        if($staging_finish)
        {
            delete_transient( 'wp_core_block_css_files' );

            if ( function_exists( 'save_mod_rewrite_rules' ) ) {
                save_mod_rewrite_rules();
            }
            else{
                if(file_exists(ABSPATH . 'wp-admin/includes/misc.php')) {
                    require_once ABSPATH . 'wp-admin/includes/misc.php';
                }
                if ( function_exists( 'save_mod_rewrite_rules' ) ) {
                    save_mod_rewrite_rules();
                }
            }
            flush_rewrite_rules(true);
            delete_option('wpvivid_staging_finish');
            if(!$this->check_theme_exist())
            {
                $redirect=true;
            }
        }

        if($staging_init)
        {
            global $wp_rewrite;

            if($staging_init == 1){
                //create staging site
                $wp_rewrite->set_permalink_structure( null );
            }
            else{
                //push to live site
                $wp_rewrite->set_permalink_structure( $staging_init );
            }

            delete_option('wpvivid_staging_init');
        }

        $data=$this->get_staging_site_data();

        if($data!==false)
        {
            $user = wp_get_current_user();
            if($user->exists())
            {
                wp_enqueue_style( "wpvivid-admin-bar", WPVIVID_PLUGIN_DIR_URL . "css/wpvivid-admin-bar.css", array(), WPVIVID_PLUGIN_VERSION );
            }

            if(!$this->is_login_page())
            {
                if(is_multisite())
                {
                    switch_to_blog(get_main_site_id());
                    $options=get_option('wpvivid_staging_options', false);
                    restore_current_blog();
                }
                else
                {
                    $options=get_option('wpvivid_staging_options',array());
                }

                $staging_not_need_login=isset($options['not_need_login']) ? $options['not_need_login'] : true;

                if(!$staging_not_need_login)
                {
                    if(!current_user_can('manage_options'))
                    {
                        $this->output_login_page();
                    }
                }
            }
        }

        if($redirect)
        {
            ?>
            <script>
                location.reload();
            </script>
            <?php
        }
    }

    public function check_theme_exist()
    {
        global $wp_theme_directories;
        $stylesheet = get_stylesheet();
        $theme_root = get_raw_theme_root( $stylesheet );
        if ( false === $theme_root ) {
            $theme_root = WP_CONTENT_DIR . '/themes';
        }
        elseif ( ! in_array( $theme_root, (array) $wp_theme_directories ) )
        {
            $theme_root = WP_CONTENT_DIR . $theme_root;
        }

        $theme_dir = $stylesheet;

        // Correct a situation where the theme is 'some-directory/some-theme' but 'some-directory' was passed in as part of the theme root instead.
        if ( ! in_array( $theme_root, (array) $wp_theme_directories ) && in_array( dirname( $theme_root ), (array) $wp_theme_directories ) ) {
            $stylesheet = basename( $theme_root ) . '/' .$theme_dir;
            $theme_root = dirname( $theme_root );
        }

        $theme_file       = $stylesheet . '/style.css';

        if( ! file_exists( $theme_root . '/' . $theme_file ) )
        {
            $themes=wp_get_themes();
            foreach ($themes as $theme)
            {
                switch_theme($theme->get_stylesheet());
                return false;
            }
        }
        return true;
    }

    public function is_login_page()
    {
        return in_array($GLOBALS['pagenow'], array('wp-login.php', 'wp-register.php'));
    }

    public function wpvivid_logout_redirect()
    {
        $redirectTo = get_site_url();
        wp_logout();
        ?>
        <script>
            location.href='<?php echo esc_url($redirectTo); ?>';
        </script>
        <?php
    }

    public function output_login_page()
    {
        if(is_user_logged_in())
        {
            if(current_user_can( 'manage_options' ))
            {
                return false;
            }
            else
            {
                $this->wpvivid_logout_redirect();
            }
        }
        if( !isset( $_POST['log'] ) || !isset( $_POST['pwd'] ) )
        {

        }
        else
        {
            $user_data = get_user_by( 'login', sanitize_user($_POST['log']) );
            if( !$user_data )
            {
                $user_data = get_user_by( 'email', sanitize_email($_POST['log']) );
                $log=sanitize_email($_POST['log']);
            }
            else
            {
                $log=sanitize_user($_POST['log']);
            }

            if( $user_data )
            {
                if( wp_check_password( $_POST['pwd'], $user_data->user_pass, $user_data->ID ) )
                {

                    $rememberme = isset( $_POST['rememberme'] ) ? true : false;

                    wp_set_auth_cookie( $user_data->ID, $rememberme );
                    wp_set_current_user( $user_data->ID, $log );
                    do_action( 'wp_login', $log, get_userdata( $user_data->ID ) );

                    $redirect_to = get_site_url() . '/wp-admin/';

                    if( !empty( $_POST['redirect_to'] ) )
                    {
                        $url=sanitize_url($_POST['redirect_to']);
                        $redirectTo = wp_safe_redirect($url);
                    }

                    header( 'Location:' . $redirectTo );
                }
            }
        }

        require_once( ABSPATH . 'wp-login.php' );

        ?>
        <script>
            jQuery(document).ready(function ()
            {
                jQuery('#loginform').prop('action', '');
            });
        </script>
        <?php

        die();
    }

    public function delete_site()
    {
        global $wpvivid_plugin;
        check_ajax_referer( 'wpvivid_ajax', 'nonce' );
        $check=current_user_can('manage_options');
        $check=apply_filters('wpvivid_ajax_check_security',$check);
        if(!$check)
        {
            die();
        }
        try {
            if (isset($_POST['id'])) {
                $id = sanitize_key($_POST['id']);
            } else {
                die();
            }

            $ret = $this->_delete_site($id);

            $html = '';
            $list = get_option('wpvivid_staging_task_list', array());
            if (!empty($list)) {
                $display_list = new WPvivid_Staging_List();
                $display_list->set_parent('wpvivid_staging_list');
                $display_list->set_list($list);
                $display_list->prepare_items();
                ob_start();
                $display_list->display();
                $html = ob_get_clean();
            }
            $ret['html'] = $html;
            echo wp_json_encode($ret);
        }
        catch (Exception $error) {
            $message = 'An exception has occurred. class: '.get_class($error).';msg: '.$error->getMessage().';code: '.$error->getCode().';line: '.$error->getLine().';in_file: '.$error->getFile().';';
            error_log($message);
            echo wp_json_encode(array('result'=>'failed','error'=>$message));
        }
        die();
    }

    public function wpvivid_get_staging_database_object($use_additional_db, $db_user, $db_pass, $db_name, $db_host){
        if($use_additional_db){
            return new wpdb($db_user, $db_pass, $db_name, $db_host);
        }
        else{
            global $wpdb;
            return $wpdb;
        }
    }

    public function delete_cancel_staging_site(){
        global $wpvivid_plugin;
        check_ajax_referer( 'wpvivid_ajax', 'nonce' );
        $check=current_user_can('manage_options');
        $check=apply_filters('wpvivid_ajax_check_security',$check);
        if(!$check)
        {
            die();
        }
        try {
            if (isset($_POST['staging_site_info'])) {
                $json = sanitize_text_field($_POST['staging_site_info']);
                $json = stripslashes($json);
                $staging_site_info = json_decode($json, true);
                $site_path = $staging_site_info['staging_path'];
                $use_additional_db = $staging_site_info['staging_additional_db'];
                $db_user = $staging_site_info['staging_additional_db_user'];
                $db_pass = $staging_site_info['staging_additional_db_pass'];
                $db_name = $staging_site_info['staging_additional_db_name'];
                $db_host = $staging_site_info['staging_additional_db_host'];
                if (!empty($site_path)) {
                    $home_path = untrailingslashit(ABSPATH);
                    if ($home_path != $site_path) {
                        if (file_exists($site_path)) {
                            if (!class_exists('WP_Filesystem_Base')) include_once(ABSPATH . '/wp-admin/includes/class-wp-filesystem-base.php');
                            if (!class_exists('WP_Filesystem_Direct')) include_once(ABSPATH . '/wp-admin/includes/class-wp-filesystem-direct.php');

                            $fs = new WP_Filesystem_Direct(false);
                            $fs->rmdir($site_path, true);
                        }
                    }
                }

                $prefix = $staging_site_info['staging_table_prefix'];
                if (!empty($prefix)) {
                    $db = $this->wpvivid_get_staging_database_object($use_additional_db, $db_user, $db_pass, $db_name, $db_host);
                    $sql = $db->prepare("SHOW TABLES LIKE %s;", $db->esc_like($prefix) . '%');
                    $result = $db->get_results($sql, OBJECT_K);

                    if (!empty($result)) {
                        foreach ($result as $table_name => $value) {
                            $table['name'] = $table_name;
                            $db->query("DROP TABLE IF EXISTS {$table_name}");
                        }
                    }
                }

                $ret['result'] = 'success';
                echo wp_json_encode($ret);
            }
        }
        catch (Exception $error) {
            $message = 'An exception has occurred. class: '.get_class($error).';msg: '.$error->getMessage().';code: '.$error->getCode().';line: '.$error->getLine().';in_file: '.$error->getFile().';';
            error_log($message);
            echo wp_json_encode(array('result'=>'failed','error'=>$message));
        }
        die();
    }

    public function _delete_site($site_id,$unfinished=false)
    {
        try
        {
            set_time_limit(900);
            $task=new WPvivid_Staging_Task($site_id);

            if($unfinished)
            {
                if($task->is_restore()||$task->is_copy())
                {
                    $ret['result']='success';
                    return $ret;
                }
                $site_path=$task->get_path(true);
                $prefix=$task->get_db_prefix(true);
                $copy_db=new WPvivid_Staging_Copy_DB($site_id);
                $db=$copy_db->get_db_instance(true);
            }
            else
            {
                $site_path=$task->get_site_path();
                $prefix=$task->get_site_prefix();
                $db=$task->get_site_db_instance();
            }

            if(empty($site_path))
            {
                $ret['result']='success';
                $default = array();
                $tasks = get_option('wpvivid_staging_task_list', $default);
                unset($tasks[$site_id]);
                update_option('wpvivid_staging_task_list',$tasks,'no');
                return $ret;
            }

            $home_path=untrailingslashit(ABSPATH);
            if($home_path!=$site_path)
            {
                if (file_exists($site_path))
                {
                    if (!class_exists('WP_Filesystem_Base')) include_once(ABSPATH . '/wp-admin/includes/class-wp-filesystem-base.php');
                    if (!class_exists('WP_Filesystem_Direct')) include_once(ABSPATH . '/wp-admin/includes/class-wp-filesystem-direct.php');

                    $fs = new WP_Filesystem_Direct(false);
                    $fs->rmdir($site_path, true);
                }
            }

            if(empty($prefix)||empty($db))
            {
                $ret['result']='success';
                $default = array();
                $tasks = get_option('wpvivid_staging_task_list', $default);
                unset($tasks[$site_id]);
                update_option('wpvivid_staging_task_list',$tasks,'no');
                return $ret;
            }

            $sql=$db->prepare("SHOW TABLES LIKE %s;", $db->esc_like($prefix) . '%');
            $result = $db->get_results($sql, OBJECT_K);
            if(!empty($result))
            {
                $db->query( "SET foreign_key_checks = 0" );
                foreach ($result as $table_name=>$value)
                {
                    $table['name']=$table_name;
                    $db->query( "DROP TABLE IF EXISTS {$table_name}" );
                }
                $db->query( "SET foreign_key_checks = 1" );
            }

            $default = array();
            $tasks = get_option('wpvivid_staging_task_list', $default);
            unset($tasks[$site_id]);
            update_option('wpvivid_staging_task_list',$tasks,'no');

            $ret['result']='success';
        }
        catch (Exception $error)
        {
            $ret['result']='failed';
            $ret['error']=$error->getMessage();
        }

        return $ret;
    }

    public function check_staging_dir()
    {
        global $wpvivid_plugin;
        check_ajax_referer( 'wpvivid_ajax', 'nonce' );
        $check=current_user_can('manage_options');
        $check=apply_filters('wpvivid_ajax_check_security',$check);
        if(!$check)
        {
            die();
        }
        try
        {
            $ret['result'] = 'success';
            if(!isset($_POST['path']) || empty($_POST['path']) || !is_string($_POST['path']))
            {
                $ret['result']='failed';
                $ret['error']='A site path is required.';
                echo wp_json_encode($ret);
                die();
            }

            $path = sanitize_text_field($_POST['path']);
            $path = sanitize_file_name($path);

            if(!isset($_POST['table_prefix']) || empty($_POST['table_prefix']) || !is_string($_POST['table_prefix']))
            {
                $ret['result']='failed';
                $ret['error']='A table prefix is required.';
                echo wp_json_encode($ret);
                die();
            }

            $table_prefix = sanitize_text_field($_POST['table_prefix']);

            if (isset($_POST['root_dir']) && $_POST['root_dir'] == 0)
            {
                $path = untrailingslashit(ABSPATH) . DIRECTORY_SEPARATOR. $path;
            }
            else if(isset($_POST['root_dir']) && $_POST['root_dir'] == 1)
            {
                $path = WP_CONTENT_DIR . DIRECTORY_SEPARATOR . $path;
            }
            else
            {
                $ret['result'] = 'failed';
                $ret['error'] = 'We are not able to authenticate the staging directory, please contact us.';
                echo wp_json_encode($ret);
                die();
            }

            if (file_exists($path))
            {
                $ret['result'] = 'failed';
                $ret['error'] = 'A folder with the same name already exists in website\'s root directory.';
            }
            else
            {
                if (mkdir($path, 0755, true))
                {
                    rmdir($path);
                } else {
                    $ret['result'] = 'failed';
                    $ret['error'] = 'Create directory is not allowed in ' . $path . '.Please check the directory permissions and try again';
                }
            }

            if(isset($_POST['additional_db']))
            {
                $additional_db_json = sanitize_text_field($_POST['additional_db']);
                $additional_db_json = stripslashes($additional_db_json);
                $additional_db_options = json_decode($additional_db_json, true);
                if($additional_db_options['additional_database_check'] === '1')
                {
                    $db_user = sanitize_text_field($additional_db_options['additional_database_info']['db_user']);
                    $db_pass = sanitize_text_field($additional_db_options['additional_database_info']['db_pass']);
                    $db_host = sanitize_text_field($additional_db_options['additional_database_info']['db_host']);
                    $db_name = sanitize_text_field($additional_db_options['additional_database_info']['db_name']);
                    $db = new wpdb($db_user, $db_pass, $db_name, $db_host);
                    $sql = $db->prepare("SHOW TABLES LIKE %s;", $db->esc_like($table_prefix) . '%');
                    $result = $db->get_results($sql, OBJECT_K);
                    if (!empty($result))
                    {
                        $ret['result'] = 'failed';
                        $ret['error'] = 'The table prefix already exists.';
                    }
                }
                else
                {
                    global $wpdb;
                    $sql = $wpdb->prepare("SHOW TABLES LIKE %s;", $wpdb->esc_like($table_prefix) . '%');
                    $result = $wpdb->get_results($sql, OBJECT_K);
                    if (!empty($result))
                    {
                        $ret['result'] = 'failed';
                        $ret['error'] = 'The table prefix already exists.';
                    }
                }
            }
            else
            {
                global $wpdb;
                $sql = $wpdb->prepare("SHOW TABLES LIKE %s;", $wpdb->esc_like($table_prefix) . '%');
                $result = $wpdb->get_results($sql, OBJECT_K);
                if (!empty($result))
                {
                    $ret['result'] = 'failed';
                    $ret['error'] = 'The table prefix already exists.';
                }
            }
            echo wp_json_encode($ret);
        }
        catch (Exception $error)
        {
            $message = 'An exception has occurred. class: '.get_class($error).';msg: '.$error->getMessage().';code: '.$error->getCode().';line: '.$error->getLine().';in_file: '.$error->getFile().';';
            error_log($message);
            echo wp_json_encode(array('result'=>'failed','error'=>$message));
        }
        die();
    }

    public function check_filesystem_permissions()
    {
        global $wpvivid_plugin;
        check_ajax_referer( 'wpvivid_ajax', 'nonce' );
        $check=current_user_can('manage_options');
        $check=apply_filters('wpvivid_ajax_check_security',$check);
        if(!$check)
        {
            die();
        }
        try{
            if(!isset($_POST['path']) || empty($_POST['path']) || !is_string($_POST['path']))
            {
                $ret['result']='failed';
                $ret['error']='A site path is required.';
                echo wp_json_encode($ret);
                die();
            }

            $path = sanitize_text_field($_POST['path']);
            $src_path = untrailingslashit(ABSPATH);

            if(isset($_POST['root_dir'])&&$_POST['root_dir']==0)
            {
                $des_path = untrailingslashit(ABSPATH) . '/' . $path;
            }
            else if (isset($_POST['root_dir'])&&$_POST['root_dir']==1)
            {
                $des_path = WP_CONTENT_DIR . DIRECTORY_SEPARATOR . $path;
            }
            else
            {
                $test_dir = 'wpvividstg_testfolder';
                $des_path = untrailingslashit($path) . '/' . $test_dir;
            }

            $mk_res = mkdir($des_path,0755,true);
            if (!$mk_res)
            {
                $ret['result']='failed';
                $ret['error']='The directory where the staging site will be installed is not writable. Please set the permissions of the directory to 755 then try it again.';
                echo wp_json_encode($ret);
                die();
            }

            $test_file_name = 'wpvividstg_test_file.txt';
            $test_file_path = $des_path.DIRECTORY_SEPARATOR.$test_file_name;
            $mk_res = fopen($test_file_path, 'wb');
            if (!$mk_res)
            {
                if(file_exists($des_path))
                    @rmdir($des_path);
                $ret['result']='failed';
                $ret['error']='The directory where the staging site will be installed is not writable. Please set the permissions of the directory to 755 then try it again.';
                echo wp_json_encode($ret);
                die();
            }

            fclose($mk_res);
            @wp_delete_file($test_file_path);
            if(file_exists($des_path))
                @rmdir($des_path);

            $ret['result'] = 'success';
            echo wp_json_encode($ret);
        }
        catch (Exception $error)
        {
            $message = 'An exception has occurred. class: '.get_class($error).';msg: '.$error->getMessage().';code: '.$error->getCode().';line: '.$error->getLine().';in_file: '.$error->getFile().';';
            error_log($message);
            echo wp_json_encode(array('result'=>'failed','error'=>$message));
        }
        die();
    }

    public function get_recent_post()
    {
        //set_prefix
        $post_type='post';
        $args = array(
            'orderby' => 'modified',
            'ignore_sticky_posts' => '1',
            'page_id' => 0,
            'posts_per_page' => 1,
            'post_type' => $post_type
        );

        $loop = new WP_Query( $args );
        echo '<ul>';
        while( $loop->have_posts())
        {
            $loop->the_post();
            echo '<li><a href="' . esc_url(get_permalink( $loop->post->ID )) . '"> ' .esc_html(get_the_title( $loop->post->ID ) ). '</a> ( '. esc_url(get_the_modified_date()) .') </li>';
        }
        echo '</ul>';
        echo'<input id="wpvivid_update_post" type="button" class="button button-primary" value="Update">';

    }

    public function get_staging_progress()
    {
        global $wpvivid_plugin;
        check_ajax_referer( 'wpvivid_ajax', 'nonce' );
        $check=current_user_can('manage_options');
        $check=apply_filters('wpvivid_ajax_check_security',$check);
        if(!$check)
        {
            die();
        }

        $task_id=get_option('wpvivid_current_running_staging_task','');
        if(empty($task_id))
        {
            $list = get_option('wpvivid_staging_task_list',array());
            if(!empty($list))
            {
                foreach ($list as $key => $value)
                {
                    if($value['status']['str'] === 'running' || $value['status']['str'] === 'ready')
                    {
                        $task_id = $value['id'];
                        update_option('wpvivid_current_running_staging_task', $task_id, 'no');
                        break;
                    }
                }
            }
            if(empty($task_id))
            {
                $ret['result']='success';
                $ret['log']='';
                $ret['continue']=0;
                echo wp_json_encode($ret);
                die();
            }
        }

        try
        {
            $task=new WPvivid_Staging_Task($task_id);
            $b_delete=false;
            if($task->get_status()=='completed')
            {
                if($task->is_restore()){
                    $ret['completed_msg'] = 'Pushing the staging site to the live site completed successfully.';
                }
                else{
                    $ret['completed_msg'] = 'Updating the staging site completed successfully.';

                    $db_connect = $task->get_db_connect();
                    $url = $db_connect['new_site_url'];
                    $options=array();
                    $options['timeout']=30;
                    $response = wp_remote_request( $url,$options);
                    if(!is_wp_error($response) && ($response['response']['code'] == 200))
                    {
                        $this->log->OpenLogFile($task->get_log_file_name());
                        $this->log->WriteLog('Access staging site successfully.', 'notice');
                    }
                    else
                    {
                        $this->log->OpenLogFile($task->get_log_file_name());
                        $this->log->WriteLog('Access staging site failed.', 'notice');
                    }
                }
                update_option('wpvivid_current_running_staging_task','','no');
                $ret['continue']=0;
                $ret['completed']=1;
            }
            else if($task->get_status()=='ready')
            {
                $ret['continue']=1;
                $ret['need_restart']=1;
            }
            else if($task->get_status()=='error')
            {
                update_option('wpvivid_current_running_staging_task','','no');
                $ret['continue']=0;
                $ret['error']=1;
                $ret['error_msg']=$task->get_error();
                $b_delete=true;
            }
            else if($task->get_status()=='cancel')
            {
                update_option('wpvivid_current_running_staging_task','','no');
                $ret['continue']=0;
                $ret['need_restart']=0;
                $ret['is_cancel']=1;
                foreach ($task as $value){
                    $ret['staging_path']=$value['path']['des_path'];
                    if($value['db_connect']['des_use_additional_db']){
                        $ret['staging_additional_db']=1;
                        $ret['staging_additional_db_user']=$value['db_connect']['des_dbuser'];
                        $ret['staging_additional_db_pass']=$value['db_connect']['des_dbpassword'];
                        $ret['staging_additional_db_host']=$value['db_connect']['des_dbhost'];
                        $ret['staging_additional_db_name']=$value['db_connect']['des_dbname'];
                        $ret['staging_table_prefix']=$value['db_connect']['new_prefix'];
                    }
                    else{
                        $ret['staging_additional_db']=0;
                        $ret['staging_additional_db_user']=null;
                        $ret['staging_additional_db_pass']=null;
                        $ret['staging_additional_db_host']=null;
                        $ret['staging_additional_db_name']=null;
                        $ret['staging_table_prefix']=$value['db_connect']['new_prefix'];
                    }
                }
                update_option('wpvivid_staging_task_cancel', false, 'no');
                $b_delete=true;
            }
            else
            {
                if($task->check_timeout())
                {
                    if($task->get_status()=='ready')
                    {
                        $ret['continue']=1;
                        $ret['need_restart']=1;
                    }
                    else
                    {
                        update_option('wpvivid_current_running_staging_task','','no');
                        $ret['continue']=0;
                        $b_delete=true;
                    }
                }
                else
                {
                    $ret['continue']=1;
                    $ret['need_restart']=0;
                }
            }
            $staging_percent = $task->get_progress();
            $file_name=$this->log->GetSaveLogFolder(). $task->get_log_file_name().'_log.txt';
            $file =fopen($file_name,'r');
            $buffer='';
            if(!$file)
            {
                $buffer='open log file failed';
            }
            else
            {
                if(filesize($file_name)<=1*1024*1024)
                {
                    while(!feof($file))
                    {
                        $buffer .= fread($file,1024);
                    }
                }
                else
                {
                    $pos=-2;
                    $eof='';
                    $n=50;
                    $buffer_array = array();
                    while($n>0)
                    {
                        while($eof!=="\n")
                        {
                            if(!fseek($file, $pos, SEEK_END))
                            {
                                $eof=fgetc($file);
                                $pos--;
                            }
                            else
                            {
                                break;
                            }
                        }
                        $buffer_array[].=fgets($file);
                        $eof='';
                        $n--;
                    }

                    if(!empty($buffer_array))
                    {
                        $buffer_array = array_reverse($buffer_array);
                        foreach($buffer_array as $value)
                        {
                            $buffer.=$value;
                        }
                    }
                }

                fclose($file);
            }

            if($b_delete)
            {
                $this->_delete_site($task_id,true);
            }
            $ret['log']=$buffer;
            $ret['percent']=$staging_percent;
            $ret['result']='success';
            echo wp_json_encode($ret);
        }
        catch (Exception $error)
        {
            $ret['result']='failed';
            $ret['error']=$error->getMessage();
            echo wp_json_encode($ret);
        }

        die();
    }

    public function cancel_staging()
    {
        global $wpvivid_plugin;
        check_ajax_referer( 'wpvivid_ajax', 'nonce' );
        $check=current_user_can('manage_options');
        $check=apply_filters('wpvivid_ajax_check_security',$check);
        if(!$check)
        {
            die();
        }
        $task_id=get_option('wpvivid_current_running_staging_task','');
        if(empty($task_id))
        {
            $ret['result']='success';
            $ret['log']='';
            $ret['continue']=0;
            echo wp_json_encode($ret);
            die();
        }

        try
        {
            $task=new WPvivid_Staging_Task($task_id);
            $task->cancel_staging();

            $ret['result']='success';
            echo wp_json_encode($ret);
        }
        catch (Exception $error)
        {
            $ret['result']='failed';
            $ret['error']=$error->getMessage();
            echo wp_json_encode($ret);
        }
        die();
    }

    public function test_additional_database_connect(){
        global $wpvivid_plugin;
        check_ajax_referer( 'wpvivid_ajax', 'nonce' );
        $check=current_user_can('manage_options');
        $check=apply_filters('wpvivid_ajax_check_security',$check);
        if(!$check)
        {
            die();
        }
        try {
            if (isset($_POST['database_info']) && !empty($_POST['database_info']) && is_string($_POST['database_info'])) {
                $data = sanitize_text_field($_POST['database_info']);
                $data = stripslashes($data);
                $json = json_decode($data, true);
                $db_user = sanitize_text_field($json['db_user']);
                $db_pass = sanitize_text_field($json['db_pass']);
                $db_host = sanitize_text_field($json['db_host']);
                $db_name = sanitize_text_field($json['db_name']);

                $db = new wpdb($db_user, $db_pass, $db_name, $db_host);
                // Can not connect to mysql
                if (!empty($db->error->errors['db_connect_fail']['0'])) {
                    $ret['result'] = 'failed';
                    $ret['error'] = 'Failed to connect to MySQL server. Please try again later.';
                    echo wp_json_encode($ret);
                    die();
                }

                // Can not connect to database
                $db->select($db_name);
                if (!$db->ready) {
                    $ret['result'] = 'failed';
                    $ret['error'] = 'Unable to connect to MySQL database. Please try again later.';
                    echo wp_json_encode($ret);
                    die();
                }
                $ret['result'] = 'success';

                echo wp_json_encode($ret);
            }
        }
        catch (Exception $error) {
            $message = 'An exception has occurred. class: '.get_class($error).';msg: '.$error->getMessage().';code: '.$error->getCode().';line: '.$error->getLine().';in_file: '.$error->getFile().';';
            error_log($message);
            echo wp_json_encode(array('result'=>'failed','error'=>$message));
        }
        die();
    }

    public function update_staging_exclude_extension(){
        global $wpvivid_plugin;
        check_ajax_referer( 'wpvivid_ajax', 'nonce' );
        $check=current_user_can('manage_options');
        $check=apply_filters('wpvivid_ajax_check_security',$check);
        if(!$check)
        {
            die();
        }
        try {
            if (isset($_POST['type']) && !empty($_POST['type']) && is_string($_POST['type']) &&
                isset($_POST['exclude_content']) && !empty($_POST['exclude_content']) && is_string($_POST['exclude_content'])) {
                $type = sanitize_text_field($_POST['type']);
                $value = sanitize_text_field($_POST['exclude_content']);

                $staging_option = self::wpvivid_get_staging_history();
                if (empty($staging_option)) {
                    $staging_option = array();
                }

                if ($type === 'upload') {
                    $staging_option['upload_extension'] = array();
                    $str_tmp = explode(',', $value);
                    for ($index = 0; $index < count($str_tmp); $index++) {
                        if (!empty($str_tmp[$index])) {
                            $staging_option['upload_extension'][] = $str_tmp[$index];
                        }
                    }
                } else if ($type === 'content') {
                    $staging_option['content_extension'] = array();
                    $str_tmp = explode(',', $value);
                    for ($index = 0; $index < count($str_tmp); $index++) {
                        if (!empty($str_tmp[$index])) {
                            $staging_option['content_extension'][] = $str_tmp[$index];
                        }
                    }
                } else if ($type === 'additional_file') {
                    $staging_option['additional_file_extension'] = array();
                    $str_tmp = explode(',', $value);
                    for ($index = 0; $index < count($str_tmp); $index++) {
                        if (!empty($str_tmp[$index])) {
                            $staging_option['additional_file_extension'][] = $str_tmp[$index];
                        }
                    }
                }

                self::wpvivid_set_staging_history($staging_option);

                $ret['result'] = 'success';
                echo wp_json_encode($ret);
            }
        }
        catch (Exception $error) {
            $message = 'An exception has occurred. class: '.get_class($error).';msg: '.$error->getMessage().';code: '.$error->getCode().';line: '.$error->getLine().';in_file: '.$error->getFile().';';
            error_log($message);
            echo wp_json_encode(array('result'=>'failed','error'=>$message));
        }
        die();
    }

    public function deal_shutdown_error($task_id)
    {
        if($this->end_shutdown_function===false)
        {
            $task=false;

            $last_error = error_get_last();
            if (!empty($last_error) && !in_array($last_error['type'], array(E_NOTICE,E_WARNING,E_USER_NOTICE,E_USER_WARNING,E_DEPRECATED), true))
            {
                $error = $last_error;
            } else {
                $error = false;
            }

            try
            {
                $task=new WPvivid_Staging_Task($task_id);
                if($error==false)
                {
                    $message='Create staging site end with a error.';
                }
                else
                {
                    $message=$error;
                }
                $task->finished_task_with_error($message);
                $this->log->WriteLog($message,'error');
            }
            catch (Exception $error)
            {
                $message = 'An Error has occurred. class:'.get_class($error).';msg:'.$error->getMessage().';code:'.$error->getCode().';line:'.$error->getLine().';in_file:'.$error->getFile().';';
                error_log($message);
                if($task!==false)
                    $task->finished_task_with_error($message);
                $this->log->WriteLog($message,'error');
            }

            die();
        }
    }

    public function start_staging()
    {
        global $wpvivid_plugin;
        check_ajax_referer( 'wpvivid_ajax', 'nonce' );
        $check=current_user_can('manage_options');
        $check=apply_filters('wpvivid_ajax_check_security',$check);
        if(!$check)
        {
            die();
        }

        $this->end_shutdown_function=false;
        register_shutdown_function(array($this,'deal_staging_shutdown_error'));
        $task=false;
        try
        {
            $task_id=get_option('wpvivid_current_running_staging_task','');
            if(!empty($task_id))
            {
                $task=new WPvivid_Staging_Task($task_id);
                if($task->get_status()==='running')
                {
                    $this->end_shutdown_function=true;
                    die();
                }
                $this->log->OpenLogFile($task->get_log_file_name());
            }
            else
            {
                if(isset($_POST['path']) && isset($_POST['table_prefix']) && isset($_POST['custom_dir']) && isset($_POST['additional_db']))
                {
                    $json = sanitize_text_field($_POST['custom_dir']);
                    $json = stripslashes($json);
                    $staging_options = json_decode($json, true);

                    $additional_db_json = sanitize_text_field($_POST['additional_db']);
                    $additional_db_json = stripslashes($additional_db_json);
                    $additional_db_options = json_decode($additional_db_json, true);

                    $option['options'] = $this->set_staging_option();

                    $src_path = untrailingslashit(ABSPATH);
                    $path = sanitize_text_field($_POST['path']);
                    if(isset($_POST['root_dir'])&&$_POST['root_dir']==0)
                    {
                        $url_path=$path;

                        $new_site_url = untrailingslashit($this->get_database_site_url()). '/' . $url_path;
                        $new_home_url = untrailingslashit($this->get_database_home_url()). '/' . $url_path;
                        $des_path = untrailingslashit(ABSPATH) . '/' . $path;
                    }
                    else
                    {
                        $url_path=str_replace(ABSPATH,'',WP_CONTENT_DIR).'/' . $path;

                        $new_site_url = untrailingslashit($this->get_database_site_url()). '/' . $url_path;
                        $new_home_url = untrailingslashit($this->get_database_home_url()). '/' . $url_path;

                        $des_path = WP_CONTENT_DIR . DIRECTORY_SEPARATOR . $path;
                    }

                    $option['data']['path']['src_path'] = $src_path;
                    $option['data']['path']['des_path'] = $des_path;

                    $table_prefix = sanitize_text_field($_POST['table_prefix']);

                    $option['data']['restore'] = false;
                    $option['data']['copy']=false;

                    $this->set_create_staging_option($option,$staging_options,$additional_db_options,$new_site_url,$new_home_url,$table_prefix);


                    $task = new WPvivid_Staging_Task();
                    $task->setup_task($option);
                    $task->set_memory_limit();
                    $task->update_action_time('create_time');
                    $this->log->CreateLogFile($task->get_log_file_name(), 'no_folder', 'staging');
                    $this->log->WriteLog('Start creating staging site.', 'notice');
                    $this->log->WriteLogHander();
                }
            }

            $task_id=$task->get_id();
            update_option('wpvivid_current_running_staging_task',$task_id,'no');
            register_shutdown_function(array($this,'deal_shutdown_error'),$task_id);

            $doing=$task->get_doing_task();
            if($doing===false)
            {
                $doing=$task->get_start_next_task();
            }

            $task->set_time_limit();
            if(!$task->do_task($doing))
            {
                $task->finished_task_with_error();
                $this->end_shutdown_function=true;
                die();
            }

            $doing=$task->get_start_next_task();
            if($doing==false)
            {
                $this->log->WriteLog('Creating staging site is completed.','notice');
                $task->finished_task();
            }
        }
        catch (Exception $error)
        {
            $message = 'An Error has occurred. class:'.get_class($error).';msg:'.$error->getMessage().';code:'.$error->getCode().';line:'.$error->getLine().';in_file:'.$error->getFile().';';
            error_log($message);
            if($task!==false)
                $task->finished_task_with_error($message);
            $this->log->WriteLog($message,'error');
        }

        $this->end_shutdown_function=true;
        die();
    }

    public function set_restart_staging_id()
    {
        global $wpvivid_plugin;
        check_ajax_referer( 'wpvivid_ajax', 'nonce' );
        $check=current_user_can('manage_options');
        $check=apply_filters('wpvivid_ajax_check_security',$check);
        if(!$check)
        {
            die();
        }
        try {
            if(isset($_POST['id']))
            {
                $task_id = sanitize_key($_POST['id']);
                update_option('wpvivid_current_running_staging_task', $task_id, 'no');
                $ret['result'] = 'success';
                echo wp_json_encode($ret);
            }
        }
        catch (Exception $error) {
            $message = 'An exception has occurred. class: '.get_class($error).';msg: '.$error->getMessage().';code: '.$error->getCode().';line: '.$error->getLine().';in_file: '.$error->getFile().';';
            error_log($message);
            echo wp_json_encode(array('result'=>'failed','error'=>$message));
        }
        die();
    }

    public function set_db_connect_option($new_site_url,$new_home_url,$additional_db_options,$table_prefix,$mu_single_site_id='')
    {
        global $wpdb;
        if(!empty($mu_single_site_id))
        {
            $prefix=$wpdb->get_blog_prefix($mu_single_site_id);
            $db_connect['old_prefix'] = $prefix;
            $db_connect['old_site_url'] = get_site_url($mu_single_site_id);
            $db_connect['old_home_url'] = get_home_url($mu_single_site_id);
        }
        else
        {
            $db_connect['old_prefix'] = $wpdb->base_prefix;
            $db_connect['old_site_url'] = untrailingslashit($this->get_database_site_url());
            $db_connect['old_home_url'] = untrailingslashit($this->get_database_home_url());
        }

        $db_connect['new_site_url'] = $new_site_url;
        $db_connect['new_home_url'] = $new_home_url;
        $db_connect['src_use_additional_db'] = false;
        $db_connect['des_use_additional_db'] = false;
        $db_connect['new_prefix'] = $table_prefix;
        if(isset($additional_db_options['additional_database_check']) && $additional_db_options['additional_database_check'] == '1')
        {
            /*$option['data']['db_connect']['des_use_additional_db'] = true;
            $option['data']['db_connect']['des_dbuser'] = $additional_db_options['additional_database_info']['db_user'];
            $option['data']['db_connect']['des_dbpassword'] = $additional_db_options['additional_database_info']['db_pass'];
            $option['data']['db_connect']['des_dbname'] = $additional_db_options['additional_database_info']['db_name'];
            $option['data']['db_connect']['des_dbhost'] = $additional_db_options['additional_database_info']['db_host'];*/
            $db_connect['des_use_additional_db'] = true;
            $db_connect['des_dbuser'] = $additional_db_options['additional_database_info']['db_user'];
            $db_connect['des_dbpassword'] = $additional_db_options['additional_database_info']['db_pass'];
            $db_connect['des_dbname'] = $additional_db_options['additional_database_info']['db_name'];
            $db_connect['des_dbhost'] = $additional_db_options['additional_database_info']['db_host'];
        }

        return $db_connect;
    }

    public function set_create_staging_option(&$option,$staging_options,$additional_db_options,$new_site_url,$new_home_url,$table_prefix)
    {
        global $wpdb;

        if(isset($_POST['create_new_wp']))
        {
            $option['data']['core'] = true;

            if($staging_options['themes_check'] == '1')
            {
                $option['data']['theme']['exclude_regex'] = array();
                foreach ($staging_options['themes_list'] as $theme)
                {
                    $option['data']['theme']['exclude_regex'][] = '#^'.preg_quote($this -> transfer_path(get_theme_root().DIRECTORY_SEPARATOR.$theme), '/').'$#';
                }
            }

            if($staging_options['plugins_check'] == '1')
            {
                $option['data']['plugins']['exclude_regex'] = array();
                foreach ($staging_options['plugins_list'] as $plugin)
                {
                    $option['data']['plugins']['exclude_regex'][] = '#^'.preg_quote($this -> transfer_path(WP_PLUGIN_DIR.DIRECTORY_SEPARATOR.$plugin), '/').'#';
                }
            }

            $option['data']['db_connect']=$this->set_db_connect_option($new_site_url,$new_home_url,$additional_db_options,$table_prefix);
            $option['data']['create_new_wp']=true;
            //$option['data']['db']['exclude_tables'] = array();
            //$option['data']['db']['exclude_tables'][] = $wpdb->base_prefix.'hw_blocks';
            //foreach ($staging_options['database_list'] as $table)
            //{
            //    $option['data']['db']['exclude_tables'][] = $table;
            //}
        }
        else
        {
            if($staging_options['database_check'] == '1')
            {
                $option['data']['db_connect']=$this->set_db_connect_option($new_site_url,$new_home_url,$additional_db_options,$table_prefix);

                $option['data']['db']['exclude_tables'] = array();
                $option['data']['db']['exclude_tables'][] = $wpdb->base_prefix.'hw_blocks';
                foreach ($staging_options['database_list'] as $table)
                {
                    $option['data']['db']['exclude_tables'][] = $table;
                }
            }

            $option['data']['theme']['exclude_regex'] = array();
            if($staging_options['themes_check'] == '1')
            {
                foreach ($staging_options['themes_list'] as $key => $value)
                {
                    $option['data']['theme']['exclude_regex'][] = '#^'.preg_quote($this -> transfer_path(get_theme_root().DIRECTORY_SEPARATOR.$key), '/').'#';
                }
                $option['data']['theme']['exclude_files_regex']=array();
                $theme_extension_tmp = array();
                if(isset($staging_options['themes_extension']) && !empty($staging_options['themes_extension']))
                {
                    $str_tmp = explode(',', $staging_options['themes_extension']);
                    for($index=0; $index<count($str_tmp); $index++)
                    {
                        if(!empty($str_tmp[$index]))
                        {
                            $option['data']['theme']['exclude_files_regex'][] = '#' . '.*\.' . $str_tmp[$index] . '$' . '#';
                            $theme_extension_tmp[] = $str_tmp[$index];
                        }
                    }
                    $staging_options['themes_extension'] = $theme_extension_tmp;
                }
            }

            $option['data']['plugins']['exclude_regex'] = array();
            if($staging_options['plugins_check'] == '1')
            {
                foreach ($staging_options['plugins_list'] as $key => $value)
                {
                    $option['data']['plugins']['exclude_regex'][] = '#^'.preg_quote($this -> transfer_path(WP_PLUGIN_DIR.DIRECTORY_SEPARATOR.$key), '/').'#';
                }
                $option['data']['plugins']['exclude_files_regex']=array();
                $plugin_extension_tmp = array();
                if(isset($staging_options['plugins_extension']) && !empty($staging_options['plugins_extension']))
                {
                    $str_tmp = explode(',', $staging_options['plugins_extension']);
                    for($index=0; $index<count($str_tmp); $index++)
                    {
                        if(!empty($str_tmp[$index]))
                        {
                            $option['data']['plugins']['exclude_files_regex'][] = '#' . '.*\.' . $str_tmp[$index] . '$' . '#';
                            $plugin_extension_tmp[] = $str_tmp[$index];
                        }
                    }
                    $staging_options['plugins_extension'] = $plugin_extension_tmp;
                }
            }

            if($staging_options['uploads_check'] == '1')
            {
                $upload_dir = wp_upload_dir();
                $option['data']['upload']['exclude_regex'] = array();
                foreach ($staging_options['uploads_list'] as $key => $value)
                {
                    $option['data']['upload']['exclude_regex'][] = '#^'.preg_quote($this -> transfer_path($upload_dir['basedir'].DIRECTORY_SEPARATOR.$key), '/').'#';
                }
                $option['data']['upload']['exclude_files_regex']=array();
                $upload_extension_tmp = array();
                if(isset($staging_options['upload_extension']) && !empty($staging_options['upload_extension']))
                {
                    $str_tmp = explode(',', $staging_options['upload_extension']);
                    for($index=0; $index<count($str_tmp); $index++)
                    {
                        if(!empty($str_tmp[$index]))
                        {
                            $option['data']['upload']['exclude_files_regex'][] = '#' . '.*\.' . $str_tmp[$index] . '$' . '#';
                            $upload_extension_tmp[] = $str_tmp[$index];
                        }
                    }
                    $staging_options['upload_extension'] = $upload_extension_tmp;
                }
            }

            if($staging_options['content_check'] == '1')
            {
                $option['data']['wp-content']['exclude_regex'] = array();
                foreach ($staging_options['content_list'] as $key => $value)
                {
                    $option['data']['wp-content']['exclude_regex'][] = '#^'.preg_quote($this -> transfer_path(WP_CONTENT_DIR.DIRECTORY_SEPARATOR.$key), '/').'#';
                }
                $option['data']['wp-content']['exclude_files_regex']=array();
                $content_extension_tmp = array();
                if(isset($staging_options['content_extension']) && !empty($staging_options['content_extension']))
                {
                    $str_tmp = explode(',', $staging_options['content_extension']);
                    for($index=0; $index<count($str_tmp); $index++)
                    {
                        if(!empty($str_tmp[$index]))
                        {
                            $option['data']['wp-content']['exclude_files_regex'][] = '#' . '.*\.' . $str_tmp[$index] . '$' . '#';
                            $content_extension_tmp[] = $str_tmp[$index];
                        }
                    }
                    $staging_options['content_extension'] = $content_extension_tmp;
                }
            }

            if($staging_options['core_check'] == '1')
            {
                $option['data']['core'] = true;
            }

            if($staging_options['additional_file_check'] == '1')
            {
                $custom['exclude_regex'] = array();
                $custom['exclude_files_regex']=array();
                $additional_file_extension_tmp = array();
                if(isset($staging_options['additional_file_extension']) && !empty($staging_options['additional_file_extension']))
                {
                    $str_tmp = explode(',', $staging_options['additional_file_extension']);
                    for($index=0; $index<count($str_tmp); $index++)
                    {
                        if(!empty($str_tmp[$index]))
                        {
                            $custom['exclude_files_regex'][] = '#' . '.*\.' . $str_tmp[$index] . '$' . '#';
                            $additional_file_extension_tmp[] = $str_tmp[$index];
                        }
                    }
                    $staging_options['additional_file_extension'] = $additional_file_extension_tmp;
                }
                foreach ($staging_options['additional_file_list'] as $key => $value)
                {
                    $custom['root'] = $key;
                    $option['data']['custom'][] = $custom;
                }
            }

            self::wpvivid_set_staging_history($staging_options);
        }
    }

    public function get_table_list($prefix,&$mu_exclude_table,$task=false,$exculude_user=true)
    {

        global $wpdb;

        if($task===false)
        {
            $db=$wpdb;
        }
        else
        {
            $db=$task->get_site_db_instance();
        }

        $sql=$db->prepare("SHOW TABLES LIKE %s;", $wpdb->esc_like($prefix) . '%');
        $result = $db->get_results($sql, OBJECT_K);
        foreach ($result as $table_name=>$value)
        {
            if($prefix==$db->base_prefix)
            {
                if ( 1 == preg_match('/^' . $db->base_prefix . '\d+_/', $table_name) )
                {

                }
                else
                {
                    if($table_name==$db->base_prefix.'blogs'&&$exculude_user!==false)
                        continue;
                    if($exculude_user===false)
                    {
                        if($table_name==$db->base_prefix.'users'||$table_name==$db->base_prefix.'usermeta')
                            continue;
                    }
                    $mu_exclude_table[]=$table_name;
                }
            }
            else
            {
                $mu_exclude_table[]=$table_name;
            }
        }
    }

    public function get_upload_exclude_folder($site_id,$des=false,$task=false)
    {
        if($des)
        {
            $upload_dir = wp_upload_dir();
            $dir = str_replace( ABSPATH, '', $upload_dir['basedir'] );
            $src_path=$task->get_site_path();
            $upload_basedir=$src_path.DIRECTORY_SEPARATOR.$dir;
            if ( defined( 'MULTISITE' ) )
            {
                $upload_basedir = $upload_basedir.'/sites/' . $site_id;
            } else {
                $upload_basedir = $upload_basedir.'/' . $site_id;
            }
            return $upload_basedir;
        }
        else
        {
            $upload= $this->get_site_upload_dir($site_id);
            return $upload['basedir'];
        }
    }

    public function get_site_upload_dir($site_id, $time = null, $create_dir = true, $refresh_cache = false)
    {
        static $cache = array(), $tested_paths = array();

        $key = sprintf( '%d-%s',$site_id, (string) $time );

        if ( $refresh_cache || empty( $cache[ $key ] ) ) {
            $cache[ $key ] = $this->_wp_upload_dir( $site_id,$time );
        }

        /**
         * Filters the uploads directory data.
         *
         * @since 2.0.0
         *
         * @param array $uploads Array of upload directory data with keys of 'path',
         *                       'url', 'subdir, 'basedir', and 'error'.
         */
        $uploads = apply_filters( 'upload_dir', $cache[ $key ] );

        if ( $create_dir ) {
            $path = $uploads['path'];

            if ( array_key_exists( $path, $tested_paths ) ) {
                $uploads['error'] = $tested_paths[ $path ];
            } else {
                if ( ! wp_mkdir_p( $path ) ) {
                    if ( 0 === strpos( $uploads['basedir'], ABSPATH ) ) {
                        $error_path = str_replace( ABSPATH, '', $uploads['basedir'] ) . $uploads['subdir'];
                    } else {
                        $error_path = basename( $uploads['basedir'] ) . $uploads['subdir'];
                    }

                    $uploads['error'] = sprintf(
                    /* translators: %s: directory path */
                        __( 'Unable to create directory %s. Is its parent directory writable by the server?', 'wpvivid-backuprestore' ),
                        esc_html( $error_path )
                    );
                }

                $tested_paths[ $path ] = $uploads['error'];
            }
        }

        return $uploads;
    }

    public function _wp_upload_dir($site_id, $time = null ) {
        $siteurl     = get_option( 'siteurl' );
        $upload_path = trim( get_option( 'upload_path' ) );

        if ( empty( $upload_path ) || 'wp-content/uploads' == $upload_path ) {
            $dir = WP_CONTENT_DIR . '/uploads';
        } elseif ( 0 !== strpos( $upload_path, ABSPATH ) ) {
            // $dir is absolute, $upload_path is (maybe) relative to ABSPATH
            $dir = path_join( ABSPATH, $upload_path );
        } else {
            $dir = $upload_path;
        }

        if ( ! $url = get_option( 'upload_url_path' ) ) {
            if ( empty( $upload_path ) || ( 'wp-content/uploads' == $upload_path ) || ( $upload_path == $dir ) ) {
                $url = WP_CONTENT_URL . '/uploads';
            } else {
                $url = trailingslashit( $siteurl ) . $upload_path;
            }
        }

        /*
         * Honor the value of UPLOADS. This happens as long as ms-files rewriting is disabled.
         * We also sometimes obey UPLOADS when rewriting is enabled -- see the next block.
         */
        if ( defined( 'UPLOADS' ) && ! ( is_multisite() && get_site_option( 'ms_files_rewriting' ) ) ) {
            $dir = ABSPATH . UPLOADS;
            $url = trailingslashit( $siteurl ) . UPLOADS;
        }

        // If multisite (and if not the main site in a post-MU network)
        if ( is_multisite() && ! ( is_main_network() && is_main_site($site_id) && defined( 'MULTISITE' ) ) ) {
            if ( ! get_site_option( 'ms_files_rewriting' ) ) {
                /*
                 * If ms-files rewriting is disabled (networks created post-3.5), it is fairly
                 * straightforward: Append sites/%d if we're not on the main site (for post-MU
                 * networks). (The extra directory prevents a four-digit ID from conflicting with
                 * a year-based directory for the main site. But if a MU-era network has disabled
                 * ms-files rewriting manually, they don't need the extra directory, as they never
                 * had wp-content/uploads for the main site.)
                 */

                if ( defined( 'MULTISITE' ) ) {
                    $ms_dir = '/sites/' . $site_id;
                } else {
                    $ms_dir = '/' . $site_id;
                }

                $dir .= $ms_dir;
                $url .= $ms_dir;
            } elseif ( defined( 'UPLOADS' ) && ! ms_is_switched() ) {
                /*
                 * Handle the old-form ms-files.php rewriting if the network still has that enabled.
                 * When ms-files rewriting is enabled, then we only listen to UPLOADS when:
                 * 1) We are not on the main site in a post-MU network, as wp-content/uploads is used
                 *    there, and
                 * 2) We are not switched, as ms_upload_constants() hardcodes these constants to reflect
                 *    the original blog ID.
                 *
                 * Rather than UPLOADS, we actually use BLOGUPLOADDIR if it is set, as it is absolute.
                 * (And it will be set, see ms_upload_constants().) Otherwise, UPLOADS can be used, as
                 * as it is relative to ABSPATH. For the final piece: when UPLOADS is used with ms-files
                 * rewriting in multisite, the resulting URL is /files. (#WP22702 for background.)
                 */

                if ( defined( 'BLOGUPLOADDIR' ) ) {
                    $dir = untrailingslashit( BLOGUPLOADDIR );
                } else {
                    $dir = ABSPATH . UPLOADS;
                }
                $url = trailingslashit( $siteurl ) . 'files';
            }
        }

        $basedir = $dir;
        $baseurl = $url;

        $subdir = '';
        if ( get_option( 'uploads_use_yearmonth_folders' ) ) {
            // Generate the yearly and monthly dirs
            if ( ! $time ) {
                $time = current_time( 'mysql' );
            }
            $y      = substr( $time, 0, 4 );
            $m      = substr( $time, 5, 2 );
            $subdir = "/$y/$m";
        }

        $dir .= $subdir;
        $url .= $subdir;

        return array(
            'path'    => $dir,
            'url'     => $url,
            'subdir'  => $subdir,
            'basedir' => $basedir,
            'baseurl' => $baseurl,
            'error'   => false,
        );
    }

    public function deal_staging_shutdown_error()
    {
        if($this->end_shutdown_function===false)
        {
            $last_error = error_get_last();
            if (!empty($last_error) && !in_array($last_error['type'], array(E_NOTICE,E_WARNING,E_USER_NOTICE,E_USER_WARNING,E_DEPRECATED), true))
            {
                $error = $last_error;
            } else {
                $error = false;
            }

            if ($error === false)
            {
                $error = 'Task interrupted. Please hold on. We are starting a retry.';
            } else {
                $error = 'type: '. $error['type'] . ', ' . $error['message'] . ' file:' . $error['file'] . ' line:' . $error['line'];
                error_log($error);
            }
            $this->log->WriteLog($error,'error');

            $list = get_option('wpvivid_staging_task_list',array());
            if(!empty($list))
            {
                foreach ($list as $key => $value)
                {
                    if($value['status']['str'] === 'running')
                    {
                        $list[$key]['status']['str']='ready';
                        update_option('wpvivid_staging_task_list', $list, 'no');
                        break;
                    }
                }
            }

            die();
        }
    }

    public static function wpvivid_set_staging_history($option){
        update_option('wpvivid_staging_history', $option, 'no');
    }

    public static function wpvivid_get_staging_history(){
        $options = get_option('wpvivid_staging_history', array());
        return $options;
    }

    public static function wpvivid_get_push_staging_history(){
        $options = get_option('wpvivid_push_staging_history', array());
        return $options;
    }

    private function transfer_path($path)
    {
        $path = str_replace('\\','/',$path);
        $values = explode('/',$path);
        return implode(DIRECTORY_SEPARATOR,$values);
    }

    public function set_staging_option()
    {
        $options=get_option('wpvivid_staging_options');

        if(isset($options['staging_db_insert_count']))
            $option['staging_db_insert_count']=$options['staging_db_insert_count'];
        else
            $option['staging_db_insert_count']=10000;

        if(isset($options['staging_db_replace_count']))
            $option['staging_db_replace_count']=$options['staging_db_replace_count'];
        else
            $option['staging_db_replace_count']=5000;

        if(isset($options['staging_memory_limit']))
            $option['staging_memory_limit']=$options['staging_memory_limit'];
        else
            $option['staging_memory_limit']='256M';

        if(isset($options['staging_file_copy_count']))
            $option['staging_file_copy_count']=$options['staging_file_copy_count'];
        else
            $option['staging_file_copy_count']=500;

        if(isset($options['staging_exclude_file_size'])) {
            $option['staging_exclude_file_size'] = $options['staging_exclude_file_size'];
        }
        else {
            $option['staging_exclude_file_size'] = 30;
        }

        if(isset($options['staging_max_execution_time']))
            $option['staging_max_execution_time']=$options['staging_max_execution_time'];
        else
            $option['staging_max_execution_time']=900;

        if(isset($options['staging_resume_count']))
            $option['staging_resume_count']=$options['staging_resume_count'];
        else
            $option['staging_resume_count']=6;

        if(isset($options['staging_overwrite_permalink']))
            $option['staging_overwrite_permalink']=$options['staging_overwrite_permalink'];
        else
            $option['staging_overwrite_permalink']=1;

        return $option;
    }

    public function export_setting_addon($json)
    {
        $default = array();
        $wpvivid_staging_history = get_option('wpvivid_staging_history', $default);
        $wpvivid_push_staging_history = get_option('wpvivid_push_staging_history', $default);
        $json['data']['wpvivid_staging_history'] = $wpvivid_staging_history;
        $json['data']['wpvivid_push_staging_history'] = $wpvivid_push_staging_history;
        return $json;
    }
}includes/staging/class-wpvivid-staging-task-ex.php000064400000111056151327705670016337 0ustar00<?php
if (!defined('WPVIVID_PLUGIN_DIR'))
{
    die;
}
class WPvivid_Staging_Task
{
    public $task;

    public function __construct($task_id=false)
    {
        if($task_id===false)
        {
            $this->create_new_task();
        }
        else
        {
            $default = array();
            $tasks = get_option('wpvivid_staging_task_list', $default);
            if(isset($tasks[$task_id]))
            {
                $this->task=$tasks[$task_id];
            }
            else
            {
                $error = 'Staging task not found.';
                throw new Exception(esc_html($error));
            }
        }
    }

    public function get_id()
    {
        return $this->task['id'];
    }

    public function create_new_task()
    {
        $task_id=uniqid('wpvivid-');

        $task['id']=$task_id;

        $task['status']['start_time']=time();
        $task['status']['run_time']=time();
        $task['status']['timeout']=time();
        $task['status']['str']='ready';
        $task['status']['resume_count']=0;
        $task['job']=array();
        $task['doing']=false;
        $task['timeout_limit']=900;
        $task['log_file_name']=$task_id.'_staging';
        $log=new WPvivid_Staging_Log_Free();
        $log->CreateLogFile($task['log_file_name'],'no_folder','staging');
        $log->CloseFile();

        $this->task=$task;

        $this->update_task();
    }

    public function get_log_file_name()
    {
        return $this->task['log_file_name'];
    }

    public function set_time_limit()
    {
        $this->get_task();
        $this->task['status']['timeout']=time();
        if(isset($this->task['options']['staging_max_execution_time'])) {
            $this->task['timeout_limit'] = $this->task['options']['staging_max_execution_time'];
        }
        else{
            $this->task['timeout_limit'] = 900;
        }
        set_time_limit($this->task['timeout_limit']);
        $this->update_task();
    }

    public function update_task($task=false)
    {
        $default = array();
        $tasks = get_option('wpvivid_staging_task_list', $default);

        if($task===false)
        {
            $this->task['status']['run_time']=time();
            $tasks[$this->task['id']]=$this->task;
        }
        else
        {
            $this->task=$task;
            $this->task['status']['run_time']=time();
            $tasks[$this->task['id']]=$this->task;
        }
        update_option('wpvivid_staging_task_list',$tasks,'no');
    }

    public function get_task()
    {
        $default = array();
        $tasks = get_option('wpvivid_staging_task_list', $default);
        $this->task=$tasks[$this->task['id']];
        return $this->task;
    }

    public function get_mu_option()
    {
        if(isset($this->task['mu']))
        {
            return $this->task['mu'];
        }
        else
        {
            return false;
        }
    }

    public function setup_task($option)
    {
        global $wpvivid_plugin;
        $this->task['options']=$option['options'];

        if($option['data']['restore'])
        {
            $this->task['options']['restore']=true;

            $this->task['path']['src_path']=$this->task['site']['path'];
            $this->task['path']['des_path']=untrailingslashit(ABSPATH);

            global $wpdb;
            $this->task['db_connect']['old_prefix']=$this->task['site']['prefix'];
            if($this->get_site_mu_single())
            {
                $this->task['db_connect']['new_prefix']=$wpdb->get_blog_prefix($this->get_site_mu_single_site_id());
            }
            else
            {
                $this->task['db_connect']['new_prefix']=$wpdb->base_prefix;
            }

            $this->task['db_connect']['temp_prefix']=$wpdb->base_prefix.'temp_';
            $this->task['db_connect']['old_site_url']=$this->task['site']['site_url'];
            $this->task['db_connect']['old_home_url']=$this->task['site']['home_url'];

            if($this->get_site_mu_single())
            {
                $this->task['db_connect']['new_site_url']=get_site_url($this->get_site_mu_single_site_id());
                $this->task['db_connect']['new_home_url']=get_home_url($this->get_site_mu_single_site_id());
            }
            else
            {
                $this->task['db_connect']['new_site_url']=untrailingslashit($wpvivid_plugin->staging->get_database_site_url());
                $this->task['db_connect']['new_home_url']=untrailingslashit($wpvivid_plugin->staging->get_database_home_url());
            }

            $this->task['db_connect']['src_use_additional_db']=$this->task['site']['db_connect']['use_additional_db'];

            if($this->task['db_connect']['src_use_additional_db'])
            {
                $this->task['db_connect']['src_dbuser']= $this->task['site']['db_connect']['dbuser'];
                $this->task['db_connect']['src_dbpassword']= $this->task['site']['db_connect']['dbpassword'];
                $this->task['db_connect']['src_dbname']=$this->task['site']['db_connect']['dbname'];
                $this->task['db_connect']['src_dbhost']=$this->task['site']['db_connect']['dbhost'];
            }

            $this->task['db_connect']['des_use_additional_db']=false;
            $this->task['permalink_structure'] = get_option( 'permalink_structure','');

            if(isset($option['data']['mu']))
            {
                $this->task['mu']= $option['data']['mu'];
            }
        }
        else
        {
            $this->task['options']['restore']=false;

            if(isset($option['data']['copy'])&&$option['data']['copy'])
            {
                $this->task['options']['copy']=true;
                $this->task['path']['src_path']=untrailingslashit(ABSPATH);
                $this->task['path']['des_path']=$this->task['site']['path'];

                global $wpdb;
                if($this->get_site_mu_single())
                {
                    $prefix=$wpdb->get_blog_prefix($this->get_site_mu_single_site_id());
                }
                else
                {
                    $prefix=$wpdb->base_prefix;
                }

                $this->task['db_connect']['old_prefix']=$prefix;
                $this->task['db_connect']['new_prefix']=$this->task['site']['prefix'];

                if($this->get_site_mu_single())
                {
                    $this->task['db_connect']['old_site_url'] = get_site_url($this->get_site_mu_single_site_id());
                    $this->task['db_connect']['old_home_url'] = get_home_url($this->get_site_mu_single_site_id());
                }
                else
                {
                    $this->task['db_connect']['old_site_url']=untrailingslashit($wpvivid_plugin->staging->get_database_site_url());
                    $this->task['db_connect']['old_home_url']=untrailingslashit($wpvivid_plugin->staging->get_database_home_url());
                }

                $this->task['db_connect']['new_site_url']=$this->task['site']['site_url'];
                $this->task['db_connect']['new_home_url']=$this->task['site']['home_url'];
                $this->task['db_connect']['src_use_additional_db']=false;
                $this->task['db_connect']['des_use_additional_db']=$this->task['site']['db_connect']['use_additional_db'];
                if($this->task['db_connect']['des_use_additional_db'])
                {
                    $this->task['db_connect']['des_dbuser']= $this->task['site']['db_connect']['dbuser'];
                    $this->task['db_connect']['des_dbpassword']= $this->task['site']['db_connect']['dbpassword'];
                    $this->task['db_connect']['des_dbname']=$this->task['site']['db_connect']['dbname'];
                    $this->task['db_connect']['des_dbhost']=$this->task['site']['db_connect']['dbhost'];
                }
                if(isset($option['data']['mu']))
                {
                    $this->task['mu']= $option['data']['mu'];
                }
            }
            else
            {
                $this->task['options']['copy']=false;
                if(isset($option['data']['db_connect']))
                    $this->task['db_connect']=$option['data']['db_connect'];

                $this->task['path']=$option['data']['path'];

                if(isset($option['data']['mu']))
                {
                    $this->task['mu']= $option['data']['mu'];
                }
            }
            $this->task['permalink_structure'] = get_option( 'permalink_structure','');
            $this->task['login_url'] = wp_login_url();
        }

        if(isset($option['data']['core']))
        {
            $this->task['job']['core']['exclude_files_regex']='#\.htaccess#';
            $this->task['job']['core']['finished']=0;
            $this->task['job']['core']['start']=0;
            $this->task['job']['core']['type']='file';
        }
        if(isset($option['data']['wp-content']))
        {
            $this->task['job']['wp-content']['exclude_regex']=$option['data']['wp-content']['exclude_regex'];
            $this->task['job']['wp-content']['exclude_files_regex']=$option['data']['wp-content']['exclude_files_regex'];
            $this->task['job']['wp-content']['finished']=0;
            $this->task['job']['wp-content']['start']=0;
            $this->task['job']['wp-content']['type']='file';
        }
        if(isset($option['data']['plugins']))
        {
            $this->task['job']['plugins']['exclude_regex']=$option['data']['plugins']['exclude_regex'];
            $this->task['job']['plugins']['finished']=0;
            $this->task['job']['plugins']['start']=0;
            $this->task['job']['plugins']['type']='file';
        }
        if(isset($option['data']['theme']))
        {
            $this->task['job']['theme']['exclude_regex']=$option['data']['theme']['exclude_regex'];
            $this->task['job']['theme']['finished']=0;
            $this->task['job']['theme']['start']=0;
            $this->task['job']['theme']['type']='file';
        }
        if(isset($option['data']['upload']))
        {
            if(isset($option['data']['upload']['include_regex']))
                $this->task['job']['upload']['include_regex']=$option['data']['upload']['include_regex'];
            $this->task['job']['upload']['exclude_regex']=$option['data']['upload']['exclude_regex'];
            $this->task['job']['upload']['exclude_files_regex']=$option['data']['upload']['exclude_files_regex'];
            $this->task['job']['upload']['finished']=0;
            $this->task['job']['upload']['start']=0;
            $this->task['job']['upload']['type']='file';
        }

        if(isset($option['data']['custom']))
        {
            foreach ($option['data']['custom'] as $custom)
            {
                $this->task['job'][$custom['root']]['root']=$custom['root'];
                $this->task['job'][$custom['root']]['exclude_regex']=$custom['exclude_regex'];
                $this->task['job'][$custom['root']]['exclude_files_regex']=$custom['exclude_files_regex'];
                $this->task['job'][$custom['root']]['finished']=0;
                $this->task['job'][$custom['root']]['start']=0;
                $this->task['job'][$custom['root']]['type']='file';
            }
        }

        if(isset($option['data']['db']))
        {
            $this->task['job']['db']['exclude_tables']=$option['data']['db']['exclude_tables'];
            $this->task['job']['db']['finished']=0;
            $this->task['job']['db']['tables']=array();
            $this->task['job']['db']['type']='db';

            $this->task['job']['db_replace']['exclude_tables']=$option['data']['db']['exclude_tables'];
            $this->task['job']['db_replace']['finished']=0;
            $this->task['job']['db_replace']['tables']=array();
            $this->task['job']['db_replace']['type']='db_replace';

            if($option['data']['restore'])
            {
                $this->task['job']['db_rename']['exclude_tables']=$option['data']['db']['exclude_tables'];
                $this->task['job']['db_rename']['finished']=0;
                $this->task['job']['db_rename']['tables']=array();
                $this->task['job']['db_rename']['type']='db_rename';
            }

        }

        if(isset($option['data']['mu_single']))
        {
            $this->task['options']['mu_single']=true;
            $this->task['options']['mu_single_upload']=$option['data']['mu_single_upload'];
            $this->task['options']['mu_single_site_id']=$option['data']['mu_single_site_id'];
        }

        if(isset($option['data']['create_new_wp']))
        {
            $this->task['options']['fresh_install']=true;
            $this->task['job']['create_new_wp']['type']='install_wordpress';
            $this->task['job']['create_new_wp']['finished']=0;
        }

        $this->update_task();
    }

    public function update_action_time($action_type)
    {
        $this->get_task();
        $this->task[$action_type]=time();
        $this->update_task();
    }

    public function is_mu_single()
    {
        if(isset($this->task['options']['mu_single']))
            return true;
        else
            return false;
    }

    public function get_mu_single_upload()
    {
        if(isset($this->task['options']['mu_single_upload']))
            return $this->task['options']['mu_single_upload'];
        else
            return false;
    }

    public function get_mu_single_site_id()
    {
        if(isset($this->task['options']['mu_single_site_id']))
            return $this->task['options']['mu_single_site_id'];
        else
            return false;
    }

    public function get_permalink_structure(){
        $this->get_task();
        return $this->task['permalink_structure'];
    }

    public function get_is_overwrite_permalink_structure(){
        $this->get_task();
        return $this->task['options']['staging_overwrite_permalink'];
    }

    public function is_tables_exclude($table,$prefix=false)
    {
        if(isset($this->task['job']['db'])&&isset($this->task['job']['db']['exclude_tables']))
        {
            $arr=$this->task['job']['db']['exclude_tables'];

            if(empty($arr))
                return false;

            if($prefix===false)
            {
                return in_array($table, $arr);
            }
            else
            {
                $og_table=substr($table, strlen($prefix));
                $old_table=$this->get_db_prefix().$og_table;
                return in_array($old_table, $arr);
            }
        }
        else
        {
            return false;
        }
    }

    public function get_doing_task()
    {
        if($this->get_status()=='error')
            return false;

        $this->get_task();
        $doing=$this->task['doing'];

        if(isset($this->task['job'][$doing]))
        {
            if($this->task['job'][$doing]['finished'])
            {
                $this->task['doing']=false;
                $this->update_task();
                return $this->task['doing'];
            }
            else
            {
                return $doing;
            }
        }
        else
        {
            return false;
        }
    }

    public function get_start_next_task()
    {
        if($this->get_status()=='error')
            return false;

        $this->get_task();
        foreach ($this->task['job'] as $key=>$job)
        {
            if($job['finished'])
                continue;
            return $key;
        }
        return false;
    }

    public function do_task($key)
    {
        global $wpvivid_plugin;
        $this->get_task();
        $wpvivid_plugin->staging->log->WriteLog('Start processing '.$key.'.','notice');
        if($key==false)
            return true;

        $cancel_status = get_option('wpvivid_staging_task_cancel', false);
        //if($this->task['status']['str']=='cancel')
        if($cancel_status)
        {
            return false;
        }

        $job=$this->task['job'][$key];
        $this->task['doing']=$key;
        $this->task['status']['str']='running';
        $this->update_task();
        $this->flush();
        if($job['type']=='file')
        {
            $wpvivid_plugin->staging->log->WriteLog('Prepare to copy '.$key.' files.','notice');
            $task_id=$this->task['id'];
            $file=new WPvivid_Staging_Copy_Files($task_id);
            return $file->do_copy_file($key);
        }
        else if($job['type']=='db')
        {
            $wpvivid_plugin->staging->log->WriteLog('Prepare to copy database.','notice');
            $task_id=$this->task['id'];
            $file=new WPvivid_Staging_Copy_DB($task_id);
            return $file->do_copy_db();
        }
        else if($job['type']=='db_replace')
        {
            $wpvivid_plugin->staging->log->WriteLog('Prepare to replace database.','notice');
            $task_id=$this->task['id'];
            $file=new WPvivid_Staging_Copy_DB($task_id);
            return $file->do_replace_db();
        }
        else if($job['type']=='db_rename')
        {
            $wpvivid_plugin->staging->log->WriteLog('Prepare to rename tables.','notice');
            $task_id=$this->task['id'];
            $file=new WPvivid_Staging_Copy_DB($task_id);
            return $file->do_rename_db();
        }
        else if($job['type']=='install_wordpress')
        {
            $wpvivid_plugin->staging->log->WriteLog('Prepare to install wordpress.','notice');
            $task_id=$this->task['id'];
            $install=new WPvivid_Staging_Install_Wordpress_Free($task_id);
            return $install->do_install_wordpress();
        }
        return false;
    }

    private function flush()
    {
        $ret['result']='success';
        $ret['task_id']=$this->task['id'];
        $json=wp_json_encode($ret);
        if(!headers_sent())
        {
            header('Content-Length: '.strlen($json));
            header('Connection: close');
            header('Content-Encoding: none');
        }

        if (session_id())
            session_write_close();
        echo wp_json_encode($ret);

        if(function_exists('fastcgi_finish_request'))
        {
            fastcgi_finish_request();
        }
        else
        {
            ob_flush();
            flush();
        }
    }

    public function get_start($key)
    {
        $this->get_task();

        if($key=='db'||$key=='db_replace'||$key=='db_rename')
        {
            foreach ($this->task['job'][$key]['tables'] as $table)
            {
                if($table['finished']==0)
                {
                    return $table;
                }
            }
            return false;
        }
        else
        {
            return $this->task['job'][$key]['start'];
        }
    }

    public function update_start($key,$start)
    {
        $this->get_task();
        $this->task['job'][$key]['start']=$start;
        $this->update_task();
    }

    public function get_path($des=true)
    {
        $this->get_task();
        if($des)
        {
            return $this->task['path']['des_path'];
        }
        else
        {
            return $this->task['path']['src_path'];
        }
    }

    public function get_db_connect()
    {
        $this->get_task();
        return $this->task['db_connect'];
    }

    public function get_db_prefix($new=false)
    {
        $this->get_task();
        if($new)
        {
            return $this->task['db_connect']['new_prefix'];
        }
        else
        {
            return $this->task['db_connect']['old_prefix'];
        }
    }

    public function get_temp_prefix()
    {
        $this->get_task();
        if($this->is_restore())
            return $this->task['db_connect']['temp_prefix'];
        else
            return $this->task['db_connect']['new_prefix'];
    }

    public function get_site_url($new=false)
    {
        $this->get_task();
        if($new)
        {
            return $this->task['db_connect']['new_site_url'];
        }
        else
        {
            return $this->task['db_connect']['old_site_url'];
        }
    }

    public function get_home_url($new=false)
    {
        $this->get_task();
        if($new)
        {
            return $this->task['db_connect']['new_home_url'];
        }
        else
        {
            return $this->task['db_connect']['old_home_url'];
        }
    }

    public function get_job_option($key,$option_name)
    {
        $this->get_task();
        if(isset($this->task['job'][$key])&&isset($this->task['job'][$key][$option_name]))
        {
            return $this->task['job'][$key][$option_name];
        }
        else
        {
            return false;
        }
    }

    public function update_job_finished($key)
    {
        $this->get_task();
        $this->task['job'][$key]['finished']=1;
        $this->task['status']['str']='ready';
        $this->task['status']['resume_count']=0;
        $this->task['doing']=false;
        $this->update_task();
    }

    public function get_tables($key)
    {
        $this->get_task();
        return $this->task['job'][$key]['tables'];
    }

    public function update_tables($key,$tables)
    {
        $this->get_task();
        $this->task['job'][$key]['tables']=$tables;
        $this->update_task();
    }

    public function update_table($key,$table)
    {
        $this->get_task();
        $this->task['job'][$key]['tables'][$table['name']]=$table;
        $this->update_task();
    }

    public function update_table_finished($key,$table)
    {
        $this->get_task();
        $this->task['job'][$key]['tables'][$table['name']]=$table;
        $this->task['status']['str']='ready';
        $this->update_task();
    }

    public function finished_task()
    {
        $this->get_task();
        $default = array();
        $tasks = get_option('wpvivid_staging_task_list', $default);
        $this->task['status']['run_time']=time();
        $this->task['status']['str']='completed';
        if($this->is_restore()|| $this->task['options']['copy']==true)
        {
        }
        else {
            if(isset($this->task['options']['fresh_install']))
            {
                $this->task['site']['fresh_install']=true;
            }
            if(isset($this->task['options']['mu_single']))
            {
                $this->task['site']['mu_single']=true;
                $this->task['site']['mu_single_site_id']=$this->task['options']['mu_single_site_id'];
            }
            $this->task['site']['path']=$this->task['path']['des_path'];
            $this->task['site']['site_url']=$this->task['db_connect']['new_site_url'];
            $this->task['site']['home_url']=$this->task['db_connect']['new_home_url'];
            $this->task['site']['prefix']=$this->task['db_connect']['new_prefix'];

            $this->task['site']['db_connect']['use_additional_db']=$this->task['db_connect']['des_use_additional_db'];

            if($this->task['site']['db_connect']['use_additional_db'])
            {
                $this->task['site']['db_connect']['dbuser']=$this->task['db_connect']['des_dbuser'];
                $this->task['site']['db_connect']['dbpassword']=$this->task['db_connect']['des_dbpassword'];
                $this->task['site']['db_connect']['dbname']=$this->task['db_connect']['des_dbname'];
                $this->task['site']['db_connect']['dbhost']=$this->task['db_connect']['des_dbhost'];
            }

            if(isset($this->task['mu'])&&is_multisite())
            {
                $this->task['site']['path_current_site']=$this->task['mu']['path_current_site'];
                $this->task['site']['main_site_id']=$this->task['mu']['main_site_id'];
            }
        }


        $this->task['job']=array();
        $this->task['doing']=false;
        $tasks[$this->task['id']]=$this->task;
        update_option('wpvivid_staging_task_list',$tasks,'no');
    }

    public function get_site_mu_single()
    {
        if(isset($this->task['site']['mu_single']))
            return $this->task['site']['mu_single'];
        else
            return false;
    }

    public function get_site_mu_single_site_id()
    {
        if(isset($this->task['site']['mu_single']))
            return $this->task['site']['mu_single_site_id'];
        else
            return false;
    }

    public function get_site_path()
    {
        if(isset($this->task['site']))
            return $this->task['site']['path'];
        else
            return false;
    }

    public function get_site_db_connect()
    {
        if(isset($this->task['site']))
            return $this->task['site']['db_connect'];
        else
            return false;
    }

    public function get_site_db_instance()
    {
        if(isset($this->task['site']))
        {
            $db=$this->get_site_db_connect();
            if($db['use_additional_db']===false)
            {
                global $wpdb;
                return $wpdb;
            }
            else {
                return new wpdb($db['dbuser'],$db['dbpassword'],$db['dbname'],$db['dbhost']);
            }
        }
        else
        {
            return false;
        }
    }

    public function get_site_prefix()
    {
        if(isset($this->task['site']))
            return $this->task['site']['prefix'];
        else
            return false;
    }

    public function finished_task_with_error($error='')
    {
        global $wpvivid_plugin;
        $this->get_task();
        if(empty($error))
        {
            $cancel_status = get_option('wpvivid_staging_task_cancel', false);
            //if($this->task['status']['str']=='cancel')
            if($cancel_status)
            {
                $default = array();
                $tasks = get_option('wpvivid_staging_task_list', $default);
                $this->task['status']['run_time']=time();
                $this->task['status']['str']='error';
                $this->task['status']['error']='task canceled';
                $tasks[$this->task['id']]=$this->task;
                update_option('wpvivid_staging_task_list',$tasks,'no');
            }
            else
            {
                $default = array();
                $error = $this->get_error();
                $tasks = get_option('wpvivid_staging_task_list', $default);
                $this->task['status']['run_time']=time();
                $this->task['status']['str']='error';
                $this->task['status']['error']=$error;
                $tasks[$this->task['id']]=$this->task;
                update_option('wpvivid_staging_task_list',$tasks,'no');
                $wpvivid_plugin->staging->log->WriteLog('Error: '.$this->task['status']['error'],'error');
                WPvivid_Staging_error_log_free::create_error_log($wpvivid_plugin->staging->log->log_file);
            }
        }
        else
        {
            $default = array();
            $tasks = get_option('wpvivid_staging_task_list', $default);
            $this->task['status']['run_time']=time();
            $this->task['status']['str']='error';
            $this->task['status']['error']=$error;
            $tasks[$this->task['id']]=$this->task;
            update_option('wpvivid_staging_task_list',$tasks,'no');
            $wpvivid_plugin->staging->log->WriteLog('Error: '.$this->task['status']['error'],'error');
            WPvivid_Staging_error_log_free::create_error_log($wpvivid_plugin->staging->log->log_file);
        }
    }

    public function set_error($error)
    {
        $this->get_task();
        $this->task['status']['str']='error';
        $this->task['status']['error']=$error;
        $this->update_task();
    }

    public function get_error()
    {
        $this->get_task();
        if($this->task['status']['str']=='error')
        {
            return $this->task['status']['error'];
        }
        else
        {
            return '';
        }
    }

    public function get_status()
    {
        $this->get_task();
        $cancel_status = get_option('wpvivid_staging_task_cancel', false);
        if($cancel_status)
        {
            return 'cancel';
        }
        else {
            return $this->task['status']['str'];
        }
    }

    public function check_timeout()
    {
        $this->get_task();

        $time_spend = time() - $this->task['status']['timeout'];
        $limit=$this->task['timeout_limit'];
        $max_resume_count=$this->task['options']['staging_resume_count'];

        if ($time_spend >= $limit)
        {
            $this->task['status']['resume_count']++;

            global $wpvivid_plugin;
            $wpvivid_plugin->staging->log->OpenLogFile($this->get_log_file_name());
            $wpvivid_plugin->staging->log->WriteLog('Task time out. Resumption times: '.$this->task['status']['resume_count'],'notice');

            if($this->task['status']['resume_count']>$max_resume_count)
            {
                $wpvivid_plugin->staging->log->WriteLog('Task time out.','error');
                $this->task['status']['str']='error';
                $this->task['status']['error']='task time out.';
            }
            else
            {
                $this->task['status']['str']='ready';
            }

            $this->update_task();
            return true;
        }
        else
        {
            $no_response_time=time()-$this->task['status']['run_time'];
            if($no_response_time>180)
            {
                $next_timeout_time = $limit-$time_spend;
                global $wpvivid_plugin;
                $wpvivid_plugin->staging->log->OpenLogFile($this->get_log_file_name());
                $wpvivid_plugin->staging->log->WriteLog('Task is not responding and will time out in '.$next_timeout_time,'notice');
                $this->task['status']['str']='no_reponse';
                $this->update_task();
            }
        }

        return false;
    }

    public function set_memory_limit()
    {
        if(isset($this->task['options']['staging_memory_limit']))
            $memory_limit=$this->task['options']['staging_memory_limit'];
        else
            $memory_limit='256M';
        @ini_set('memory_limit', $memory_limit);
    }

    public function get_exclude_file_size()
    {
        if(isset($this->task['options']['staging_exclude_file_size'])) {
            $exclude_file_size = $this->task['options']['staging_exclude_file_size'];
        }
        else {
            $exclude_file_size = 30;
        }
        return $exclude_file_size;
    }

    public function get_files_copy_count()
    {
        if(isset($this->task['options']['staging_file_copy_count']))
            $files_copy_count=$this->task['options']['staging_file_copy_count'];
        else
            $files_copy_count=500;
        return $files_copy_count;
    }

    public function get_db_insert_count()
    {
        if(isset($this->task['options']['staging_db_insert_count']))
            $db_insert_count=$this->task['options']['staging_db_insert_count'];
        else
            $db_insert_count=10000;
        return $db_insert_count;
    }

    public function get_db_replace_count()
    {
        if(isset($this->task['options']['staging_db_replace_count']))
            $db_replace_count=$this->task['options']['staging_db_replace_count'];
        else
            $db_replace_count=5000;
        return $db_replace_count;
    }

    public function is_restore()
    {
        return $this->task['options']['restore'];
    }

    public function is_copy()
    {
        return $this->task['options']['copy'];
    }

    public function cancel_staging()
    {
        $this->get_task();
        //$default = array();
        //$tasks = get_option('wpvivid_staging_task_list', $default);
        if( $this->task['status']['str']=='running' || $this->task['status']['str']=='ready' )
        {
            //$this->task['status']['str']='cancel';
            update_option('wpvivid_staging_task_cancel', true, 'no');
        }

        //$tasks[$this->task['id']]=$this->task;
        //update_option('wpvivid_staging_task_list',$tasks);
    }

    public function update_calc_db_size($key)
    {
        $this->get_task();
        $size = 0;
        if($key=='db'||$key=='db_replace'||$key=='db_rename')
        {
            $size = count($this->task['job'][$key]['tables']);
        }
        $this->task['job'][$key]['copy_size']=$size;
        $this->update_task();
    }

    public function update_calc_db_finish_size($key)
    {
        $this->get_task();
        if($key=='db'||$key=='db_replace'||$key=='db_rename')
        {
            if(!isset($this->task['job'][$key]['finish_size'])){
                $this->task['job'][$key]['finish_size'] = 1;
            }
            else{
                $this->task['job'][$key]['finish_size']++;
            }
        }
        $this->update_task();
    }

    public function get_progress()
    {
        $job_count=sizeof($this->task['job']);

        if($job_count>0)
        {
            $job_finished=0;
            foreach ($this->task['job'] as $job)
            {
                if($job['type']=='db'||$job['type']=='db_replace'||$job['type']=='db_rename'){
                    if(isset($this->task['job'][$job['type']]['finish_size']) && $this->task['job'][$job['type']]['finish_size'] != 0 &&
                        isset($this->task['job'][$job['type']]['copy_size']) && $this->task['job'][$job['type']]['copy_size'] != 0) {
                        $percent_db = floatval($this->task['job'][$job['type']]['finish_size'] / $this->task['job'][$job['type']]['copy_size']);
                        $job_finished = floatval($job_finished + $percent_db);
                    }
                }
                else if($job['finished'])
                {
                    $job_finished++;
                }
            }
            $progress=intval(($job_finished/$job_count)*100);
            if($progress == 0){
                $progress = 5;
            }
            return $progress;
        }
        else
        {
            return 100;
        }
    }

    public function get_mu_sites($args=array())
    {
        global $wpdb;
        $db=$this->get_site_db_connect();

        if($db['use_additional_db']===false)
        {
            $old_prefix=$wpdb->base_prefix;
            $wpdb->set_prefix($this->get_site_prefix());
            $subsites=get_sites($args);
            $wpdb->set_prefix($old_prefix);

        }
        else
        {
            $old_wpdb=$wpdb;
            $wpdb=new wpdb($db['dbuser'],$db['dbpassword'],$db['dbname'],$db['dbhost']);
            $wpdb->set_prefix($this->get_site_prefix());
            $subsites=get_sites($args);
            $wpdb=$old_wpdb;
        }

        /*
        if($db['use_additional_db']===false)
        {
            $db_instance=$wpdb;
        }
        else
        {
            $db_instance=new wpdb($db['dbuser'],$db['dbpassword'],$db['dbname'],$db['dbhost']);
        }
        $sql='SELECT * FROM '.$this->get_site_prefix().'blogs';
        $subsites=$db_instance->get_results($sql,OBJECT_K);
        */

        return $subsites;
    }

    public function get_mu_path_current_site()
    {
        if(isset($this->task['site']['path_current_site']))
        {
            return $this->task['site']['path_current_site'];
        }
        else
        {
            return false;
        }
    }

    public function get_mu_main_site_id()
    {
        if(isset($this->task['site']['main_site_id']))
        {
            return $this->task['site']['main_site_id'];
        }
        else
        {
            return false;
        }
    }

    public function set_push_staging_history($option)
    {
        global $wpdb;
        $site_prefix=$this->get_site_prefix();
        foreach ($option['database_list'] as $index => $table)
        {
            $option['database_list'][$index] = str_replace($site_prefix, $wpdb->base_prefix, $table);
        }
        $this->task['push_staging_history'] = $option;
    }

    public function get_push_staging_history()
    {
        $option = $this->task['push_staging_history'];
        return $option;
    }
}includes/class-wpvivid-exporter.php000064400000221513151327705670013545 0ustar00<?php
/**
 * WPvivid addon: yes
 * Addon Name: wpvivid-backup-pro-all-in-one
 * Description: Pro
 * Version: 1.9.1
 */

if ( ! class_exists( 'WP_List_Table' ) )
{
    require_once( ABSPATH . 'wp-admin/includes/class-wp-list-table.php' );
}

class WPvivid_Post_List extends WP_List_Table
{
    public $post_ids;
    public $page_num;

    public function __construct( $args = array() ) {
        global $post_type_object, $wpdb;

        parent::__construct(
            array(
                'plural' => 'posts',
                'screen' => isset( $args['screen'] ) ? $args['screen'] : null,
            )
        );

        $post_type        = $this->screen->post_type;
        $post_type_object = get_post_type_object( $post_type );

        $exclude_states         = get_post_stati(
            array(
                'show_in_admin_all_list' => false,
            )
        );
        $this->user_posts_count = intval(
            $wpdb->get_var(
                $wpdb->prepare(
                    "
			SELECT COUNT( 1 )
			FROM $wpdb->posts
			WHERE post_type = %s
			AND post_status NOT IN ( '" . implode( "','", $exclude_states ) . "' )
			AND post_author = %d
		",
                    $post_type,
                    get_current_user_id()
                )
            )
        );

        if ( $this->user_posts_count && ! current_user_can( $post_type_object->cap->edit_others_posts ) && empty( $_REQUEST['post_status'] ) && empty( $_REQUEST['all_posts'] ) && empty( $_REQUEST['author'] ) && empty( $_REQUEST['show_sticky'] ) ) {
            $_GET['author'] = get_current_user_id();
        }

        if ( 'post' === $post_type && $sticky_posts = get_option( 'sticky_posts' ) ) {
            $sticky_posts             = implode( ', ', array_map( 'absint', (array) $sticky_posts ) );
            $this->sticky_posts_count = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT( 1 ) FROM $wpdb->posts WHERE post_type = %s AND post_status NOT IN ('trash', 'auto-draft') AND ID IN ($sticky_posts)", $post_type ) );
        }
    }

    public function get_columns()
    {
        $post_type = $this->screen->post_type;

        $posts_columns = array();

        $posts_columns['cb'] = '<input type="checkbox"/>';
        /* translators: manage posts column name */
        $posts_columns['wpvivid_id'] = 'ID';

        $posts_columns['title'] = _x( 'Title', 'column name', 'wpvivid-backuprestore' );

        if ( post_type_supports( $post_type, 'author' ) ) {
            $posts_columns['author'] = __( 'Author', 'wpvivid-backuprestore' );
        }

        $taxonomies = get_object_taxonomies( $post_type, 'objects' );
        $taxonomies = wp_filter_object_list( $taxonomies, array( 'show_admin_column' => true ), 'and', 'name' );

        /**
         * Filters the taxonomy columns in the Posts list table.
         *
         * The dynamic portion of the hook name, `$post_type`, refers to the post
         * type slug.
         *
         * @since 3.5.0
         *
         * @param string[] $taxonomies Array of taxonomy names to show columns for.
         * @param string   $post_type  The post type.
         */
        $taxonomies = apply_filters( "manage_taxonomies_for_{$post_type}_columns", $taxonomies, $post_type );
        $taxonomies = array_filter( $taxonomies, 'taxonomy_exists' );

        foreach ( $taxonomies as $taxonomy ) {
            if ( 'category' === $taxonomy ) {
                $column_key = 'categories';
            } elseif ( 'post_tag' === $taxonomy ) {
                $column_key = 'tags';
            } else {
                $column_key = 'taxonomy-' . $taxonomy;
            }

            $posts_columns[ $column_key ] = get_taxonomy( $taxonomy )->labels->name;
        }

        $posts_columns['comments'] =__( 'Comments', 'wpvivid-backuprestore' );

        $posts_columns['date'] = __( 'Date', 'wpvivid-backuprestore' );

        return $posts_columns;
    }

    function set_post_ids($post_ids,$page_num=1)
    {
        $this->post_ids=$post_ids;
        $this->page_num=$page_num;
    }

    public function get_pagenum()
    {
        if($this->page_num=='first')
        {
            $this->page_num=1;
        }
        else if($this->page_num=='last')
        {
            $this->page_num=$this->_pagination_args['total_pages'];
        }
        $pagenum = $this->page_num ? $this->page_num : 0;

        if ( isset( $this->_pagination_args['total_pages'] ) && $pagenum > $this->_pagination_args['total_pages'] )
        {
            $pagenum = $this->_pagination_args['total_pages'];
        }

        return max( 1, $pagenum );
    }

    function prepare_items()
    {
        $columns = $this->get_columns();
        $hidden = array();
        $sortable = array();
        $this->_column_headers = array($columns, $hidden, $sortable);

        $total_items =sizeof($this->post_ids);

        $this->set_pagination_args(
            array(
                'total_items' => $total_items,
                'per_page'    => 30,
            )
        );
    }

    public function has_items()
    {
        return !empty($this->post_ids);
    }

    public function column_cb( $post )
    {
        $checked='';
        if($post->checked)
        {
            $checked='checked';
        }
        ?>
        <input id="cb-select-<?php echo esc_attr($post->ID); ?>" type="checkbox" name="post[]" value="<?php echo esc_attr($post->ID); ?>" <?php echo esc_attr($checked) ?>/>
        <?php
    }

    /**
     * @since 4.3.0
     *
     * @param WP_Post $post
     * @param string  $classes
     * @param string  $data
     * @param string  $primary
     */
    protected function _column_title( $post, $classes, $data, $primary ) {
        echo '<td class="' . esc_attr($classes) . ' page-title" ', esc_attr($data), '>';
        echo esc_html($this->column_title( $post ));
        echo '</td>';
    }

    public function column_wpvivid_id( $post )
    {
        echo '<span>'.esc_attr($post->ID).'</span>';
    }
    /**
     * Handles the title column output.
     *
     * @since 4.3.0
     *
     * @global string $mode List table view mode.
     *
     * @param WP_Post $post The current WP_Post object.
     */
    public function column_title( $post ) {
        echo '<strong>';
        $title = $post->post_title;
        echo esc_html($title);
        echo "</strong>\n";
    }

    /**
     * Handles the post date column output.
     *
     * @since 4.3.0
     *
     * @global string $mode List table view mode.
     *
     * @param WP_Post $post The current WP_Post object.
     */
    public function column_date( $post )
    {
        global $mode;

        if ( '0000-00-00 00:00:00' === $post->post_date ) {
            $t_time    = $h_time = __( 'Unpublished', 'wpvivid-backuprestore' );
            $time_diff = 0;
        } else {
            $t_time = get_the_time( 'Y/m/d g:i:s a' );
            $m_time = $post->post_date;
            $time   = get_post_time( 'G', true, $post );

            $time_diff = time() - $time;

            if ( $time_diff > 0 && $time_diff < DAY_IN_SECONDS ) {
                $h_time = sprintf( '%s ago', human_time_diff( $time ) );
            } else {
                $h_time = mysql2date( 'Y/m/d', $m_time );
            }
        }

        if ( 'publish' === $post->post_status ) {
            $status = __( 'Published', 'wpvivid-backuprestore' );
        } elseif ( 'future' === $post->post_status ) {
            if ( $time_diff > 0 ) {
                $status = '<strong class="error-message">' . __( 'Missed schedule', 'wpvivid-backuprestore' ) . '</strong>';
            } else {
                $status = __( 'Scheduled', 'wpvivid-backuprestore' );
            }
        } else {
            $status = __( 'Last Modified', 'wpvivid-backuprestore' );
        }

        /**
         * Filters the status text of the post.
         *
         * @since 4.8.0
         *
         * @param string  $status      The status text.
         * @param WP_Post $post        Post object.
         * @param string  $column_name The column name.
         * @param string  $mode        The list display mode ('excerpt' or 'list').
         */
        $status = apply_filters( 'post_date_column_status', $status, $post, 'date', $mode );

        if ( $status ) {
            echo esc_html($status) . '<br />';
        }

        if ( 'excerpt' === $mode ) {
            /**
             * Filters the published time of the post.
             *
             * If `$mode` equals 'excerpt', the published time and date are both displayed.
             * If `$mode` equals 'list' (default), the publish date is displayed, with the
             * time and date together available as an abbreviation definition.
             *
             * @since 2.5.1
             *
             * @param string  $t_time      The published time.
             * @param WP_Post $post        Post object.
             * @param string  $column_name The column name.
             * @param string  $mode        The list display mode ('excerpt' or 'list').
             */
            echo esc_html(apply_filters( 'post_date_column_time', $t_time, $post, 'date', $mode ));
        } else {

            /** This filter is documented in wp-admin/includes/class-wp-posts-list-table.php */
            echo '<abbr title="' . esc_attr($t_time) . '">' . esc_html(apply_filters( 'post_date_column_time', $h_time, $post, 'date', $mode )) . '</abbr>';
        }
    }

    /**
     * Handles the comments column output.
     *
     * @since 4.3.0
     *
     * @param WP_Post $post The current WP_Post object.
     */
    public function column_comments( $post ) {
        ?>
        <div class="post-com-count-wrapper">
            <?php
            echo '<span style="text-align:center">'.esc_html(get_comments_number($post->ID)).'</span>'
            ?>
        </div>
        <?php
    }

    /**
     * Handles the post author column output.
     *
     * @since 4.3.0
     *
     * @param WP_Post $post The current WP_Post object.
     */
    public function column_author( $post ) {
        $user_data = get_userdata($post->post_author );

        echo '<span>'.esc_html($user_data->display_name).'</span>';
    }

    /**
     * Handles the default column output.
     *
     * @since 4.3.0
     *
     * @param WP_Post $post        The current WP_Post object.
     * @param string  $column_name The current column name.
     */
    public function column_default( $post, $column_name ) {
        if ( 'categories' === $column_name )
        {
            $taxonomy = 'category';
        } elseif ( 'tags' === $column_name )
        {
            $taxonomy = 'post_tag';
        } elseif ( 0 === strpos( $column_name, 'taxonomy-' ) )
        {
            $taxonomy = substr( $column_name, 9 );
        } else {
            $taxonomy = false;
        }
        if ( $taxonomy ) {
            $taxonomy_object = get_taxonomy( $taxonomy );
            $terms           = get_the_terms( $post->ID, $taxonomy );
            if ( is_array( $terms ) ) {
                $out = array();
                foreach ( $terms as $t ) {
                    $posts_in_term_qv = array();
                    if ( 'post' != $post->post_type ) {
                        $posts_in_term_qv['post_type'] = $post->post_type;
                    }
                    if ( $taxonomy_object->query_var ) {
                        $posts_in_term_qv[ $taxonomy_object->query_var ] = $t->slug;
                    } else {
                        $posts_in_term_qv['taxonomy'] = $taxonomy;
                        $posts_in_term_qv['term']     = $t->slug;
                    }

                    $label = esc_html( sanitize_term_field( 'name', $t->name, $t->term_id, $taxonomy, 'display' ) );

                    $out[] = $label;
                }
                /* translators: used between list items, there is a space after the comma */
                echo esc_html(join(  ', ', $out ));
            } else {
                echo '<span aria-hidden="true">&#8212;</span><span class="screen-reader-text">' . esc_html($taxonomy_object->labels->no_terms) . '</span>';
            }
            return;
        }

        if ( is_post_type_hierarchical( $post->post_type ) ) {

            /**
             * Fires in each custom column on the Posts list table.
             *
             * This hook only fires if the current post type is hierarchical,
             * such as pages.
             *
             * @since 2.5.0
             *
             * @param string $column_name The name of the column to display.
             * @param int    $post_id     The current post ID.
             */
            do_action( 'manage_pages_custom_column', $column_name, $post->ID );
        } else {

            /**
             * Fires in each custom column in the Posts list table.
             *
             * This hook only fires if the current post type is non-hierarchical,
             * such as posts.
             *
             * @since 1.5.0
             *
             * @param string $column_name The name of the column to display.
             * @param int    $post_id     The current post ID.
             */
            do_action( 'manage_posts_custom_column', $column_name, $post->ID );
        }

        /**
         * Fires for each custom column of a specific post type in the Posts list table.
         *
         * The dynamic portion of the hook name, `$post->post_type`, refers to the post type.
         *
         * @since 3.1.0
         *
         * @param string $column_name The name of the column to display.
         * @param int    $post_id     The current post ID.
         */
        do_action( "manage_{$post->post_type}_posts_custom_column", $column_name, $post->ID );
    }

    public function display_rows()
    {
        $this->_display_rows( $this->post_ids );
    }

    private function _display_rows($post_ids)
    {
        $page_post_ids=$post_ids;
        $page=$this->get_pagenum();
        $count=0;
        while ( $count<$page )
        {
            $page_post_ids = array_splice( $post_ids, 0, 30);
            $count++;
        }
        foreach ( $page_post_ids as $post_id)
        {
            $this->single_row($post_id);
        }
    }

    public function single_row($post_id)
    {
        $post = get_post($post_id['id']);
        $post->checked=$post_id['checked'];
        $classes = 'iedit author-' . ( get_current_user_id() == $post->post_author ? 'self' : 'other' );

        ?>
        <tr id="post-<?php echo esc_attr($post->ID); ?>" class="<?php echo esc_attr(implode( ' ', get_post_class( $classes, $post->ID ) )); ?>">
            <?php $this->single_row_columns( $post ); ?>
        </tr>
        <?php
    }

    protected function pagination( $which ) {
        if ( empty( $this->_pagination_args ) ) {
            return;
        }

        $total_items     = $this->_pagination_args['total_items'];
        $total_pages     = $this->_pagination_args['total_pages'];
        $infinite_scroll = false;
        if ( isset( $this->_pagination_args['infinite_scroll'] ) ) {
            $infinite_scroll = $this->_pagination_args['infinite_scroll'];
        }

        if ( 'top' === $which && $total_pages > 1 ) {
            $this->screen->render_screen_reader_content( 'heading_pagination' );
        }

        $output = '<span class="displaying-num">' . sprintf( _n( '%s item', '%s items', $total_items, 'wpvivid-backuprestore' ), number_format_i18n( $total_items ) ) . '</span>';

        $current              = $this->get_pagenum();
        $removable_query_args = wp_removable_query_args();

        $current_url = set_url_scheme( 'http://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'] );

        $current_url = remove_query_arg( $removable_query_args, $current_url );

        $page_links = array();

        $total_pages_before = '<span class="paging-input">';
        $total_pages_after  = '</span></span>';

        $disable_first = $disable_last = $disable_prev = $disable_next = false;

        if ( $current == 1 ) {
            $disable_first = true;
            $disable_prev  = true;
        }
        if ( $current == 2 ) {
            $disable_first = true;
        }
        if ( $current == $total_pages ) {
            $disable_last = true;
            $disable_next = true;
        }
        if ( $current == $total_pages - 1 ) {
            $disable_last = true;
        }

        if ( $disable_first ) {
            $page_links[] = '<span class="tablenav-pages-navspan button disabled" aria-hidden="true">&laquo;</span>';
        } else {
            $page_links[] = sprintf(
                "<div class='first-page button'><span class='screen-reader-text'>%s</span><span aria-hidden='true'>%s</span></div>",
                __( 'First page', 'wpvivid-backuprestore' ),
                '&laquo;'
            );
        }

        if ( $disable_prev ) {
            $page_links[] = '<span class="tablenav-pages-navspan button disabled" aria-hidden="true">&lsaquo;</span>';
        } else {
            $page_links[] = sprintf(
                "<div class='prev-page button' value='%s'><span class='screen-reader-text'>%s</span><span aria-hidden='true'>%s</span></div>",
                $current,
                __( 'Previous page', 'wpvivid-backuprestore' ),
                '&lsaquo;'
            );
        }

        if ( 'bottom' === $which ) {
            $html_current_page  = $current;
            $total_pages_before = '<span class="screen-reader-text">' . __( 'Current Page', 'wpvivid-backuprestore' ) . '</span><span id="table-paging" class="paging-input"><span class="tablenav-paging-text">';
        } else {
            $html_current_page = sprintf(
                "%s<input class='current-page' id='current-page-selector-export' type='text' name='paged' value='%s' size='%d' aria-describedby='table-paging' /><span class='tablenav-paging-text'>",
                '<label for="current-page-selector-export" class="screen-reader-text">' . __( 'Current Page', 'wpvivid-backuprestore' ) . '</label>',
                $current,
                strlen( $total_pages )
            );
        }
        $html_total_pages = sprintf( "<span class='total-pages'>%s</span>", number_format_i18n( $total_pages ) );
        $page_links[]     = $total_pages_before . sprintf( _x( '%1$s of %2$s', 'paging', 'wpvivid-backuprestore' ), $html_current_page, $html_total_pages ) . $total_pages_after;

        if ( $disable_next ) {
            $page_links[] = '<span class="tablenav-pages-navspan button disabled" aria-hidden="true">&rsaquo;</span>';
        } else {
            $page_links[] = sprintf(
                "<div class='next-page button' value='%s'><span class='screen-reader-text'>%s</span><span aria-hidden='true'>%s</span></div>",
                $current,
                __( 'Next page', 'wpvivid-backuprestore' ),
                '&rsaquo;'
            );
        }

        if ( $disable_last ) {
            $page_links[] = '<span class="tablenav-pages-navspan button disabled" aria-hidden="true">&raquo;</span>';
        } else {
            $page_links[] = sprintf(
                "<div class='last-page button'><span class='screen-reader-text'>%s</span><span aria-hidden='true'>%s</span></div>",
                __( 'Last page', 'wpvivid-backuprestore' ),
                '&raquo;'
            );
        }

        $pagination_links_class = 'pagination-links';
        if ( ! empty( $infinite_scroll ) ) {
            $pagination_links_class .= ' hide-if-js';
        }
        $output .= "\n<span class='$pagination_links_class'>" . join( "\n", $page_links ) . '</span>';

        if ( $total_pages ) {
            $page_class = $total_pages < 2 ? ' one-page' : '';
        } else {
            $page_class = ' no-pages';
        }
        $this->_pagination = "<div class='tablenav-pages{$page_class}'>$output</div>";

        echo $this->_pagination;
    }

    /**
     * Generate the table navigation above or below the table
     *
     * @since 3.1.0
     * @param string $which
     */
    protected function display_tablenav( $which ) {
        $css_type = '';
        if ( 'top' === $which ) {
            wp_nonce_field( 'bulk-' . $this->_args['plural'] );
            $css_type = 'margin: 0 0 10px 0';
        }
        else if( 'bottom' === $which ) {
            $css_type = 'margin: 10px 0 0 0';
        }
        ?>
        <div class="tablenav <?php echo esc_attr( $which ); ?>" style="<?php echo esc_attr($css_type); ?>">
            <div class="alignleft actions bulkactions">
                <?php echo '<input class="button-primary" id="wpvivid-post-research-submit" type="submit" name="post" value="' . esc_attr__('Reset Filters', 'wpvivid-backuprestore') .'">'; ?>
            </div>
            <?php
            $this->extra_tablenav( $which );
            $this->pagination( $which );
            ?>

            <br class="clear" />
        </div>
        <?php
    }
}

class WPvivid_Exporter_taskmanager
{
    public static function get_task($task_id)
    {
        $default = array();
        $tasks = get_option('wpvivid_exporter_task_list', $default);

        if(array_key_exists ($task_id,$tasks))
        {
            return $tasks[$task_id];
        }
        else
        {
            return false;
        }
    }

    public static function update_task($task_id,$task)
    {
        $default = array();
        $options = get_option('wpvivid_exporter_task_list', $default);
        $options[$task_id]=$task;
        WPvivid_Setting::update_option('wpvivid_exporter_task_list',$options);
    }

    public static function get_tasks()
    {
        $default = array();
        return $options = get_option('wpvivid_exporter_task_list', $default);
    }

    public static function get_backup_task_status($task_id)
    {
        $tasks=self::get_tasks();
        if(array_key_exists ($task_id,$tasks))
        {
            $task = $tasks[$task_id];
            return $task['status'];
        }
        else
        {
            return false;
        }
    }

    public static function delete_task($task_id)
    {
        $options = get_option('wpvivid_exporter_task_list', array());
        unset($options[$task_id]);
        WPvivid_Setting::update_option('wpvivid_exporter_task_list',$options);
    }

    public static function update_backup_task_status($task_id,$reset_start_time=false,$status='',$reset_timeout=false,$resume_count=false,$error='')
    {
        $tasks=self::get_tasks();
        if(array_key_exists ($task_id,$tasks))
        {
            $task = $tasks[$task_id];
            $task['status']['run_time']=time();
            if($reset_start_time)
                $task['status']['start_time']=time();
            if(!empty($status))
            {
                $task['status']['str']=$status;
            }
            if($reset_timeout)
                $task['status']['timeout']=time();
            if($resume_count!==false)
            {
                $task['status']['resume_count']=$resume_count;
            }

            if(!empty($error))
            {
                $task['status']['error']=$error;
            }
            self::update_task($task_id,$task);
            return $task;
        }
        else
        {
            return false;
        }
    }

    public static function get_task_options($task_id,$option_names)
    {
        $tasks=self::get_tasks();
        if(array_key_exists ($task_id,$tasks))
        {
            $task=$tasks[$task_id];

            if(is_array($option_names))
            {
                $options=array();
                foreach ($option_names as $name)
                {
                    $options[$name]=$task['options'][$name];
                }
                return $options;
            }
            else
            {
                return $task['options'][$option_names];
            }
        }
        else
        {
            return false;
        }
    }

    public static function is_tasks_running()
    {
        $tasks=self::get_tasks();
        foreach ($tasks as $task)
        {
            if ($task['status']['str']=='running'||$task['status']['str']=='no_responds')
            {
                return true;
            }
        }
        return false;
    }

    public static function update_main_task_progress($task_id,$job_name,$progress,$finished,$job_data=array())
    {
        $task=self::get_task($task_id);
        if($task!==false)
        {
            $task['status']['run_time']=time();
            $task['status']['str']='running';
            $task['data']['doing']=$job_name;
            $task['data'][$job_name]['finished']=$finished;
            $task['data'][$job_name]['progress']=$progress;
            $task['data'][$job_name]['job_data']=$job_data;
            self::update_task($task_id,$task);
        }
    }

    public static function get_backup_tasks_progress($task_id)
    {
        $tasks=self::get_tasks();
        if(array_key_exists ($task_id,$tasks))
        {
            $task = $tasks[$task_id];
            $current_time=gmdate("Y-m-d H:i:s");
            $create_time=gmdate("Y-m-d H:i:s",$task['status']['start_time']);
            $time_diff=strtotime($current_time)-strtotime($create_time);
            $running_time='';
            if(gmdate("G",$time_diff) > 0){
                $running_time .= gmdate("G",$time_diff).'hour';
            }
            if(intval(gmdate("i",$time_diff)) > 0){
                $running_time .= intval(gmdate("i",$time_diff)).'min';
            }
            if(intval(gmdate("s",$time_diff)) > 0){
                $running_time .= intval(gmdate("s",$time_diff)).'second';
            }

            $ret['type']=$task['data']['doing'];
            $ret['progress']=$task['data'][$ret['type']]['progress'];
            $ret['doing']=$task['data'][$ret['type']]['doing'];
            if(isset($task['data'][$ret['type']]['sub_job'][$ret['doing']]['progress']))
                $ret['descript']=$task['data'][$ret['type']]['sub_job'][$ret['doing']]['progress'];
            else
                $ret['descript']='';
            if(isset($task['data'][$ret['type']]['sub_job'][$ret['doing']]['upload_data']))
                $ret['upload_data']=$task['data'][$ret['type']]['sub_job'][$ret['doing']]['upload_data'];
            $task['data'][$ret['type']]['sub_job'][$ret['doing']]['upload_data']=false;
            $ret['running_time']=$running_time;
            $ret['running_stamp']=$time_diff;

            return $ret;
        }
        else
        {
            return false;
        }
    }
}

class WPvivid_Exporter_task
{
    private $task;

    public function __construct($task_id=false,$task=false)
    {
        if($task_id!==false)
        {
            $this->task=WPvivid_Exporter_taskmanager::get_task($task_id);
        }

        if($task!==false)
        {
            $this->task=$task;
        }
    }

    public function get_id()
    {
        return $this->task['id'];
    }

    public function new_backup_task($options)
    {
        $id=uniqid('wpvivid-');
        $this->task=false;
        $this->task['id']=$id;

        $this->task['status']['start_time']=time();
        $this->task['status']['run_time']=time();
        $this->task['status']['timeout']=time();
        $this->task['status']['str']='ready';
        $this->task['status']['resume_count']=0;

        if(isset($options['remote'])) {
            if($options['remote']=='1') {
                $this->task['options']['remote_options'] = isset($options['remote_options']) ? $options['remote_options'] : WPvivid_Setting::get_remote_options();
            }
            else {
                $this->task['options']['remote_options']=false;
            }
        }
        else {
            $this->task['options']['remote_options']=false;
        }

        $this->task['options']['remote_options'] = apply_filters('wpvivid_set_remote_options', $this->task['options']['remote_options'],$options);

        if(isset($options['local'])) {
            $this->task['options']['save_local'] = $options['local']=='1' ? 1 : 0;
        }
        else {
            $this->task['options']['save_local']=1;
        }

        $this->task['options']['post_comment'] = $options['post_comment'];

        if(empty($backup_prefix))
            $this->task['options']['file_prefix'] = $this->task['id'] . '_' . gmdate('Y-m-d-H-i', $this->task['status']['start_time']);
        else
            $this->task['options']['file_prefix'] = $backup_prefix . '_' . $this->task['id'] . '_' . gmdate('Y-m-d-H-i', $this->task['status']['start_time']);

        $this->task['options']['log_file_name']=$id.'_export';
        $log=new WPvivid_Log();
        $log->CreateLogFile($this->task['options']['log_file_name'],'no_folder','export');
        $this->task['options']['backup_options']['prefix']=$this->task['options']['file_prefix'];
        $this->task['options']['backup_options']['compress']=WPvivid_Setting::get_option('wpvivid_compress_setting');
        $this->task['options']['backup_options']['dir']=WPvivid_Setting::get_backupdir();
        $this->task['options']['backup_options']['post_ids']=$options['post_ids'];
        $this->task['options']['backup_options']['post_type']=$options['post_type'];
        $export_data['json_info']['post_type']=$options['post_type'];
        //$export_data['json_info']['post_ids']=$options['post_ids'];
        $export_data['json_info']['post_comment']=$options['post_comment'];
        $this->task['options']['backup_options']['backup'][$options['post_type']]=$export_data;
        $this->task['data']['doing']='export';
        $this->task['data']['export']['doing']='';
        $this->task['data']['export']['finished']=0;
        $this->task['data']['export']['progress']=0;
        if(sizeof($options['post_ids'])>50) {
            $this->task['data']['export']['pre_progress']=(50/sizeof($options['post_ids']))*100;
        }
        else {
            $this->task['data']['export']['pre_progress']=100;
        }
        $this->task['data']['export']['job_data']=array();
        $this->task['data']['export']['sub_job']=array();
        $this->task['data']['export']['export_info']['post_count']=sizeof($options['post_ids']);
        $this->task['data']['upload']['doing']='';
        $this->task['data']['upload']['finished']=0;
        $this->task['data']['upload']['progress']=0;
        $this->task['data']['upload']['job_data']=array();
        $this->task['data']['upload']['sub_job']=array();
        WPvivid_Exporter_taskmanager::update_task($id,$this->task);
        $ret['result']='success';
        $ret['task_id']=$this->task['id'];
        $log->CloseFile();
        return $ret;
    }

    private function parse_url_all($url)
    {
        $parse = wp_parse_url($url);
        $path=str_replace('/','_',$parse['path']);
        return $parse['host'].$path;
    }

    public function update_sub_task_progress($key,$finished,$progress)
    {
        $this->task=WPvivid_Exporter_taskmanager::get_task($this->get_id());
        $this->task['status']['run_time']=time();
        $this->task['status']['str']='running';
        $this->task['data']['doing']='export';
        $sub_job_name=$key;
        $this->task['data']['export']['doing']=$key;
        $this->task['data']['export']['sub_job'][$sub_job_name]['finished']=$finished;
        $this->task['data']['export']['sub_job'][$sub_job_name]['progress']=$progress;
        if(!isset( $this->task['data']['export']['sub_job'][$sub_job_name]['job_data']))
        {
            $this->task['data']['export']['sub_job'][$sub_job_name]['job_data']=array();
        }
        WPvivid_Exporter_taskmanager::update_task($this->get_id(),$this->task);
    }

    public function get_next_posts()
    {
        asort($this->task['options']['backup_options']['post_ids']);
        WPvivid_Exporter_taskmanager::update_task($this->get_id(),$this->task);
        $post_ids=$this->task['options']['backup_options']['post_ids'];
        if(empty($post_ids))
        {
            return false;
        }
        /*if(sizeof($post_ids)>50)
        {
            $next_post_ids = array_splice( $post_ids, 0, 50 );
        }
        else
        {
            $next_post_ids=$post_ids;
        }*/
        $next_post_ids=$post_ids;
        $ret=$this->get_post_contain_attachment_ids($next_post_ids);

        $next_post_ids = array_splice( $this->task['options']['backup_options']['post_ids'], 0, $ret['post_count'] );
        $ret['next_post_ids']=$next_post_ids;

        $post_type = $this->task['options']['backup_options']['post_type'];
        $ret['json_info'] = $this->task['options']['backup_options']['backup'][$post_type]['json_info'];

        $first=reset($next_post_ids);
        $last=end($next_post_ids);

        $post_comment = !empty($this->task['options']['post_comment']) ? $this->task['options']['post_comment'].'_' : '';
        $ret['file_name']=$post_comment.self::get_id().'_'.gmdate('Y-m-d-H-i', $this->task['status']['start_time']);
        $ret['export_type']=$this->task['options']['backup_options']['post_type'];
        return $ret;
    }

    public function update_finished_posts($finished_posts)
    {
        $this->task=WPvivid_Exporter_taskmanager::get_task( $this->get_id());
        array_splice( $this->task['options']['backup_options']['post_ids'], 0, $finished_posts['post_count'] );
        $this->task['data']['export']['progress']=$this->task['data']['export']['progress']+$this->task['data']['export']['pre_progress'];
        if($this->task['data']['export']['progress']>100)
        {
            $this->task['data']['export']['progress']=100;
        }
        WPvivid_Exporter_taskmanager::update_task($this->get_id(),$this->task);
    }

    public function update_export_files($file_data)
    {
        $this->task=WPvivid_Exporter_taskmanager::get_task( $this->get_id());

        $this->task['data']['file_data'][]=$file_data;

        $this->task['data']['export']['export_info']['file_name']=$file_data['file_name'];
        $this->task['data']['export']['export_info']['size']=$file_data['size'];

        WPvivid_Exporter_taskmanager::update_task($this->get_id(),$this->task);
    }

    public function get_export_files()
    {
        $this->task=WPvivid_Exporter_taskmanager::get_task( $this->get_id());

        if(isset($this->task['data']['file_data']))
        {
            $file_data=$this->task['data']['file_data'];
            return $file_data;
        }
        else
        {
            return array();
        }
    }

    public function get_post_contain_attachment_ids($post_ids)
    {
        $max_size=1024*1024*100;
        $current_size=0;
        $count=0;
        $sum_attachment_ids=array();
        $attachment_added_ids=array();
        $files=array();
        foreach ($post_ids as $id)
        {
            $count++;

            $attachment_ids=array();
            $post   = get_post( $id );
            if (preg_match_all( '/<img [^>]+>/', $post->post_content, $matches ) )
            {
                foreach( $matches[0] as $image )
                {
                    if ( preg_match( '/wp-image-([0-9]+)/i', $image, $class_id ) && ( $attachment_id = absint( $class_id[1] ) ) )
                    {
                        if(!in_array($attachment_id,$attachment_added_ids))
                        {
                            if(!is_null(get_post($attachment_id)))
                            {
                                $attachment_ids[] = $attachment_id;
                                $attachment_added_ids[]=$attachment_id;
                            }
                            else
                            {
                                $ret_attachment=$this->get_image_from_post_content($image);
                                $current_size+=$ret_attachment['size'];
                                $files=array_merge($files,$ret_attachment['files']);
                            }
                        }
                    }
                    else
                    {
                        $ret_attachment=$this->get_image_from_post_content($image);
                        $current_size+=$ret_attachment['size'];
                        $files=array_merge($files,$ret_attachment['files']);
                    }
                }
            }

            $_elementor_meta=get_post_meta($id,'_elementor_data',true);
            if($_elementor_meta!=false)
            {
                if ( is_string( $_elementor_meta ) && ! empty( $_elementor_meta ) )
                {
                    $_elementor_meta = json_decode( $_elementor_meta, true );
                }
                if ( empty( $_elementor_meta ) )
                {
                    $_elementor_meta = array();
                }
                $elements_data=$_elementor_meta;
                foreach ( $elements_data as $element_data )
                {
                    $element_image=$this->get_element_image($element_data,$attachment_added_ids);
                    $attachment_ids=array_merge($attachment_ids,$element_image);
                }
            }

            //_thumbnail_id
            $_thumbnail_id=get_post_meta($id,'_thumbnail_id',true);
            if($_thumbnail_id!=false)
            {
                if(!in_array($_thumbnail_id,$attachment_added_ids))
                {
                    if(!is_null(get_post($_thumbnail_id)))
                    {
                        $attachment_ids[] = $_thumbnail_id;
                        $attachment_added_ids[]=$_thumbnail_id;
                    }
                }
            }

            $sum_attachment_ids=array_merge($sum_attachment_ids,$attachment_ids);

            foreach ($attachment_ids as $attachment_id)
            {
                $ret_attachment=$this->get_attachment_size($attachment_id);
                $current_size+=$ret_attachment['size'];
                $files=array_merge($files,$ret_attachment['files']);
            }

            if($current_size>$max_size)
            {
                break;
            }
        }

        $ret['attachment_ids']=$sum_attachment_ids;
        $ret['post_count']=$count;
        $ret['files']=$files;
        return $ret;
    }

    public function get_image_from_post_content($image)
    {
        $ret['size']=0;
        $ret['files']=array();

        if(class_exists('DOMDocument'))
        {
            $doc = new DOMDocument();
            $doc->loadHTML($image);
            $xpath = new DOMXPath($doc);
            $src = $xpath->evaluate("string(//img/@src)");
        }
        else
        {
            preg_match('/src="([^"]+)/i',$image, $src);
            $src= str_ireplace( 'src="', '',  $src[0]);
        }

        $src=str_replace('https://','',$src);
        $src=str_replace('http://','',$src);

        $upload=wp_upload_dir();

        $upload['baseurl']=str_replace('https://','',$upload['baseurl']);
        $upload['baseurl']=str_replace('http://','',$upload['baseurl']);


        $path=str_replace($upload['baseurl'],$upload['basedir'],$src);
        if(file_exists($path))
        {
            $ret['size']+=filesize($path);
            $ret['files'][]=$path;
        }

        return $ret;
    }

    public function get_attachment_size($attachment_id)
    {
        $files=array();
        global $wpdb;

        $postmeta = $wpdb->get_results( $wpdb->prepare( "SELECT * FROM $wpdb->postmeta WHERE post_id = %d", $attachment_id ) );

        foreach ( $postmeta as $meta )
        {
            $upload_dir = wp_upload_dir();

            if ( $upload_dir['error'] !== false )
            {
                continue;
            }

            $dir=$upload_dir['basedir'];
            if ( apply_filters( 'wxr_export_skip_postmeta', false, $meta->meta_key, $meta ) ) {
                continue;
            }
            if($meta->meta_key=='_wp_attached_file')
            {
                $bfound=false;
                $name=$dir.DIRECTORY_SEPARATOR.$meta->meta_value;
                if(!in_array($name,$files)&&file_exists($name))
                {
                    $files[]=$name;
                    $bfound=true;
                }
                if($bfound)
                {
                    $attach_meta      = wp_get_attachment_metadata( $attachment_id );
                    if($attach_meta!=false)
                    {
                        if(isset($attach_meta['sizes']))
                        {
                            foreach ($attach_meta['sizes'] as $key=>$value)
                            {
                                $data=image_get_intermediate_size($attachment_id,$key);
                                $data['path']=ltrim($data['path'], './');
                                $name=$dir.DIRECTORY_SEPARATOR.$data['path'];
                                if(!in_array($name,$files)&&file_exists($name))
                                {
                                    $files[]=$dir.DIRECTORY_SEPARATOR.$data['path'];
                                }
                            }
                        }
                        else
                        {
                            global $wpvivid_plugin;
                            $wpvivid_plugin->wpvivid_log->WriteLog('attach_meta size not found id:'.$attachment_id,'notice');
                        }
                    }
                }
            }

        }

        $size=0;

        if(!empty($files))
        {
            foreach ($files as $file)
            {
                $size+=filesize($file);
            }
        }

        $ret['size']=$size;
        $ret['files']=$files;

        return $ret;
    }

    public function get_element_image($element_data,&$attachment_added_ids)
    {
        $element_image=array();

        if(!empty($element_data['settings']))
        {
            $settings=$element_data['settings'];
            if(isset($settings['image']))
            {
                if(!in_array($settings['image']['id'],$attachment_added_ids))
                {
                    $element_image[]=$settings['image']['id'];
                    $attachment_added_ids[]=$settings['image']['id'];
                }

            }
        }

        if(!empty($element_data['elements']))
        {
            foreach ($element_data['elements'] as $element)
            {
                $temp=$this->get_element_image($element,$attachment_added_ids);
                $element_image=array_merge($element_image,$temp);
            }
        }

        return $element_image;
    }

    public function add_new_export()
    {
        $files=$this->get_export_files();

        $backup_data=array();
        $status=WPvivid_Exporter_taskmanager::get_backup_task_status($this->task['id']);
        $backup_data['create_time']=$status['start_time'];

        global $wpvivid_plugin;
        $backup_data['log']=$wpvivid_plugin->wpvivid_log->log_file;
        $backup_data['export']=$files;
        $backup_data['id']=$this->task['id'];
        $list = get_option('wpvivid_export_list',array());
        $list[$this->task['id']]=$backup_data;
        WPvivid_Setting::update_option('wpvivid_export_list',$list);
    }
}

class WPvivid_Exporter_Item{
    private $config;

    public function __construct($options){
        $this->config=$options;
    }

    public function get_download_export_files(){
        $files = isset($this->config['export']) ? $this->config['export'] : array();
        if(empty($files)){
            $ret['result'] = WPVIVID_FAILED;
            $ret['error']='Failed to get export files.';
        }
        else{
            $ret['result'] = WPVIVID_SUCCESS;
            $ret['files']=$files;
        }
        return $ret;
    }

    public function get_download_progress($backup_id, $files){
        $this->config['local']['path'] = 'wpvividbackups';
        foreach ($files as $file){
            $need_download = false;
            $file_path     = WP_CONTENT_DIR.DIRECTORY_SEPARATOR.$this->config['local']['path'].DIRECTORY_SEPARATOR.$file;
            $download_url  = content_url().DIRECTORY_SEPARATOR.$this->config['local']['path'].DIRECTORY_SEPARATOR.$file;
            if(file_exists($file_path)){
                //need calc file size, then compare is need download

            }
            else{
                $need_download = true;
            }

            if($need_download){

            }
            else{
                $ret['result'] = WPVIVID_SUCCESS;
                $ret['files'][$file]['status'] = 'completed';
                $ret['files'][$file]['download_path'] = $file_path;
                $ret['files'][$file]['download_url'] = $download_url;
                ob_start();
                ?>
                <div style="float:left;margin:10px 10px 10px 0;text-align:center; width:180px;">
                    <span>Part01</span><br>
                    <span><a class="wpvivid-download-export" id="trtr" name="<?php echo esc_attr($file); ?>" style="cursor: pointer;">Download</a></span><br>
                    <div style="width:100%;height:5px; background-color:#dcdcdc;">
                        <div style="background-color:#0085ba; float:left;width:100%;height:5px;"></div>
                    </div>
                    <span>size: </span><span>1K</span>
                </div>
                <?php
                $html = ob_get_clean();
                $ret['html']=$html;
            }
        }
        return $ret;
    }
}

class WPvivid_Exporter
{
    public $task;
    //public $config;

    public function __construct($task_id=false,$task=false)
    {
        if($task_id!==false)
        {
            $this->task=new WPvivid_Exporter_task($task_id);
        }
        else if($task!==false)
        {
            $this->task=new WPvivid_Exporter_task(false,$task);
        }
        else
        {
            $this->task=new WPvivid_Exporter_task();
        }
    }

    public function init_options($task_id)
    {
        $this->task=new WPvivid_Exporter_task($task_id);
    }

    public function export($task_id)
    {
        $this->init_options($task_id);

        global $wpvivid_plugin;

        $next=$this->task->get_next_posts();

        $ret['result']='success';
        WPvivid_Exporter_taskmanager::update_main_task_progress($task_id, 'export', 5, 0);
        while($next!==false)
        {
            @set_time_limit(900);
            $wpvivid_plugin->wpvivid_log->WriteLog('Prepare to export post '.$next['file_name'],'notice');

            $this->task->update_sub_task_progress($next['file_name'],0,'Start export file '.$next['file_name']);
            $ret=$this->export_post_to_xml($next['next_post_ids'], $next['attachment_ids'],$next['file_name'],$next['export_type']);
            $wpvivid_plugin->wpvivid_log->WriteLog('Finished to export post '.$next['file_name'],'notice');
            if($ret['result']=='success')
            {
                $wpvivid_plugin->wpvivid_log->WriteLog('Prepare to zip file '.$next['file_name'],'notice');
                $next['json_info']['posts_count']=sizeof($next['next_post_ids']);
                $ret=$this->zip_media_files($ret['xml_file_name'],$next['files'],$next['file_name'],$next['export_type'],$next['json_info']);
                $wpvivid_plugin->wpvivid_log->WriteLog('Finished to zip file '.$next['file_name'],'notice');
                if($ret['result']!='success')
                {
                    $wpvivid_plugin->wpvivid_log->WriteLog('Failed to zip post '.$next['file_name'].' '.wp_json_encode($ret),'notice');
                    return $ret;
                }
                $this->task->update_sub_task_progress($next['file_name'],1,'Backing up '.$next['file_name'].' finished');
                $this->task->update_finished_posts($next);
                $this->task->update_export_files($ret['file_data']);
            }
            else
            {
                $wpvivid_plugin->wpvivid_log->WriteLog('Failed to export post '.$next['file_name'].' '.wp_json_encode($ret),'notice');
                return $ret;
            }
            $next=$this->task->get_next_posts();
        }
        WPvivid_Exporter_taskmanager::update_main_task_progress($task_id, 'export', 100, 1);

        return $ret;
    }

    public function export_post_to_xml($posts_ids,$attachment_ids,$file_name,$export_type)
    {
        $all_ids=array_merge($posts_ids,$attachment_ids);
        //$xml_file_name=$file_name.'.xml';
        $xml_file_name=$file_name.'_'.$export_type.'.xml';
        //$files=array();
        $export_folder = WP_CONTENT_DIR.DIRECTORY_SEPARATOR.WPvivid_Setting::get_backupdir().DIRECTORY_SEPARATOR.WPVIVID_IMPORT_EXPORT_DIR;
        if(!file_exists($export_folder)) {
            @mkdir($export_folder);
        }
        $path=$export_folder.DIRECTORY_SEPARATOR.$xml_file_name;
        $ret['xml_file_name']=$path;
        if(file_exists($path))
        {
            @wp_delete_file($path);
        }

        $this->write_header_to_file($path);

        $this->write_authors_list_to_file($path,$all_ids);

        $this->write_cat_to_file($path,$posts_ids);

        global $wp_query,$wpdb;

        // Fake being in the loop.
        $wp_query->in_the_loop = true;

        $task_id = $this->task->get_id();
        while ( $next_posts = array_splice( $posts_ids, 0, 20 ) )
        {
            $where = 'WHERE ID IN (' . join( ',', $next_posts ) . ')';
            $posts = $wpdb->get_results( "SELECT * FROM {$wpdb->posts} $where" );
            // Begin Loop.
            foreach ( $posts as $post )
            {
                $this->write_post_to_file($path,$post);
            }
        }
        WPvivid_Exporter_taskmanager::update_main_task_progress($task_id, 'export', 25, 0);
        while ( $next_posts = array_splice( $attachment_ids, 0, 20 ) )
        {
            $where = 'WHERE ID IN (' . join( ',', $next_posts ) . ')';
            $posts = $wpdb->get_results( "SELECT * FROM {$wpdb->posts} $where" );
            // Begin Loop.
            foreach ( $posts as $post )
            {
                $this->write_media_post_to_file($path,$post);
                //$post_files=$this->write_media_post_to_file($path,$post);
                //$files=array_merge($post_files,$files);
            }
        }
        WPvivid_Exporter_taskmanager::update_main_task_progress($task_id, 'export', 50, 0);
        $this->write_footer_to_file($path);

        //$ret['files']=$files;
        $ret['result']='success';
        return $ret;
    }

    private function zip_media_files($xml_file,$files,$file_name,$export_type,$json_info=false)
    {
        if (!class_exists('WPvivid_PclZip'))
            include_once WPVIVID_PLUGIN_DIR . '/includes/zip/class-wpvivid-pclzip.php';
        $path=WP_CONTENT_DIR.DIRECTORY_SEPARATOR.WPvivid_Setting::get_backupdir().DIRECTORY_SEPARATOR.WPVIVID_IMPORT_EXPORT_DIR.DIRECTORY_SEPARATOR.$file_name.'_export_'.$export_type.'.zip';
        $options['compress']['no_compress']=1;
        $options['compress']['use_temp_file']=1;
        $options['compress']['use_temp_size']=16;
        $options['root_flag']=WPVIVID_BACKUP_ROOT_WP_CONTENT;

        if(file_exists($path))
            @wp_delete_file($path);
        $archive = new WPvivid_PclZip($path);
        if($json_info!==false) {
            $temp_path = dirname($path).DIRECTORY_SEPARATOR.'wpvivid_export_package_info.json';
            if(file_exists($temp_path)) {
                @wp_delete_file($temp_path);
            }
            $json_info['create_time']=time();
            $json_info['xml_file']=basename($xml_file);
            $json_info['media_size']=0;
            foreach ($files as $file)
            {
                $json_info['media_size']+=@filesize($file);
            }
            file_put_contents($temp_path,print_r(wp_json_encode($json_info),true));
            $archive -> add($temp_path,WPVIVID_PCLZIP_OPT_REMOVE_PATH,dirname($temp_path));
            @wp_delete_file($temp_path);
        }

        $ret =$archive -> add($xml_file,WPVIVID_PCLZIP_OPT_REMOVE_PATH,dirname($xml_file));
        @wp_delete_file($xml_file);
        if(!$ret)
        {
            return array('result'=>WPVIVID_FAILED,'error'=>$archive->errorInfo(true));
        }

        if(!empty($files)) {
            $ret = $archive->add($files, WPVIVID_PCLZIP_OPT_REMOVE_PATH, WP_CONTENT_DIR, WPVIVID_PCLZIP_OPT_TEMP_FILE_THRESHOLD, 16);
        }

        if(!$ret)
        {
            return array('result'=>WPVIVID_FAILED,'error'=>$archive->errorInfo(true));
        }

        $file_data = array();
        $file_data['file_name'] = basename($path);
        $file_data['size'] = filesize($path);

        return array('result'=>WPVIVID_SUCCESS,'file_data'=>$file_data);
    }

    public function write_header_to_file($file)
    {
        $wxr_version=1.2;

        $line='<?xml version="1.0" encoding="' . get_bloginfo( 'charset' ) . "\" ?>\n";
        $line.='<!-- This is a WordPress eXtended RSS file generated by WordPress as an export of your site. -->
<!-- It contains information about your site\'s posts, pages, comments, categories, and other content. -->
<!-- You may use this file to transfer that content from one site to another. -->
<!-- This file is not intended to serve as a complete backup of your site. -->

<!-- To import this information into a WordPress site follow these steps: -->
<!-- 1. Log in to that site as an administrator. -->
<!-- 2. Go to Tools: Import in the WordPress admin panel. -->
<!-- 3. Install the "WordPress" importer from the list. -->
<!-- 4. Activate & Run Importer. -->
<!-- 5. Upload this file using the form provided on that page. -->
<!-- 6. You will first be asked to map the authors in this export file to users -->
<!--    on the site. For each author, you may choose to map to an -->
<!--    existing user on the site or to create a new user. -->
<!-- 7. WordPress will then import each of the posts, pages, comments, categories, etc. -->
<!--    contained in this file into your site. -->';
        $line.=apply_filters( 'the_generator', get_the_generator( 'export' ),  'export' ) . "\n";
        $line.='
<rss version="2.0"
	xmlns:excerpt="http://wordpress.org/export/'.$wxr_version.'/excerpt/"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:wp="http://wordpress.org/export/'.$wxr_version.'/"
>      ';
        $line.='
<channel>
    <title>'.apply_filters( 'bloginfo_rss', get_bloginfo_rss( 'name' ), 'name' ).'</title>
    <link>'.apply_filters( 'bloginfo_rss', get_bloginfo_rss( 'url' ), 'url' ).'</link>
    <description>'.apply_filters( 'bloginfo_rss', get_bloginfo_rss( 'description' ), 'description' ).'</description>
    <pubDate>'.gmdate( 'D, d M Y H:i:s +0000' ).'</pubDate>
    <language>'.apply_filters( 'bloginfo_rss', get_bloginfo_rss( 'language' ), 'language' ).'</language>
    <wp:wxr_version>'.$wxr_version.'</wp:wxr_version>
    <wp:base_site_url>'.$this->wxr_site_url().'</wp:base_site_url>
    <wp:base_blog_url>'.apply_filters( 'bloginfo_rss', get_bloginfo_rss( 'url' ), 'url' ).'</wp:base_blog_url>
    ';
        file_put_contents($file,$line);
    }

    public function write_authors_list_to_file($file,$post_ids)
    {
        $line=$this->wxr_authors_list( $post_ids );
        file_put_contents($file,$line,FILE_APPEND);
    }

    public function write_footer_to_file($file)
    {
        $line='
</channel>
</rss> ';
        file_put_contents($file,$line,FILE_APPEND);
    }

    public function write_post_header_to_file($file,$post)
    {
        $is_sticky = is_sticky( $post->ID ) ? 1 : 0;
        $post = get_post( $post );

        $guid = isset( $post->guid ) ? get_the_guid( $post ) : '';
        $id   = isset( $post->ID ) ? $post->ID : 0;

        $guid= apply_filters( 'the_guid', $guid, $id );
        $item_header_line='
        <item>
            <title>
                '.apply_filters( 'the_title_rss', $post->post_title ).'
            </title>
            <link>'.esc_url( apply_filters( 'the_permalink_rss', get_permalink($post->ID) ) ).'</link>
            <pubDate>'.mysql2date( 'D, d M Y H:i:s +0000', get_post_time( 'Y-m-d H:i:s', true,$post ), false ).'</pubDate>
            <dc:creator>'.$this->wxr_cdata( get_the_author_meta( 'login' ) ).'</dc:creator>
            <guid isPermaLink="false">'.$guid.'</guid>
		    <description></description>
		    <content:encoded>'.$this->wxr_cdata( apply_filters( 'the_content_export', $post->post_content ) ).' </content:encoded>
		    <excerpt:encoded>'.$this->wxr_cdata( apply_filters( 'the_excerpt_export', $post->post_excerpt ) ).'</excerpt:encoded>
		    <wp:post_id>'.intval( $post->ID ).'</wp:post_id>
		    <wp:post_date>'.$this->wxr_cdata( $post->post_date ).'</wp:post_date>
		    <wp:post_date_gmt>'.$this->wxr_cdata( $post->post_date_gmt ).'</wp:post_date_gmt>
		    <wp:comment_status>'.$this->wxr_cdata( $post->comment_status ).'</wp:comment_status>
		    <wp:ping_status>'.$this->wxr_cdata( $post->ping_status ).'</wp:ping_status>
		    <wp:post_name>'.$this->wxr_cdata( $post->post_name ).'</wp:post_name>
		    <wp:status>'.$this->wxr_cdata( $post->post_status ).'</wp:status>
		    <wp:post_parent>'.intval( $post->post_parent ).'</wp:post_parent>
		    <wp:menu_order>'.intval( $post->menu_order ).'</wp:menu_order>
		    <wp:post_type>'.$this->wxr_cdata( $post->post_type ).'</wp:post_type>
		    <wp:post_password>'.$this->wxr_cdata( $post->post_password ).'</wp:post_password>
		    <wp:is_sticky>'.intval( $is_sticky ).'</wp:is_sticky>
		    ';
        if ( $post->post_type == 'attachment' )
            $item_header_line.='<wp:attachment_url>'.$this->wxr_cdata( wp_get_attachment_url( $post->ID ) ).'</wp:attachment_url>';
        file_put_contents($file,$item_header_line,FILE_APPEND);

        $line=$this->wxr_post_taxonomy($post);
        file_put_contents($file,$line,FILE_APPEND);
    }

    public function write_media_post_to_file($file,$post)
    {
        global $wpdb;

        $postmeta = $wpdb->get_results( $wpdb->prepare( "SELECT * FROM $wpdb->postmeta WHERE post_id = %d", $post->ID ) );
        $post_meta_line='';
        $added_meta_key=array();
        foreach ( $postmeta as $meta )
        {
            if(in_array($meta->meta_key,$added_meta_key))
                continue;
            $added_meta_key[]=$meta->meta_key;

            $post_meta_line.='
                <wp:postmeta>
                <wp:meta_key>'.$this->wxr_cdata( $meta->meta_key ).'</wp:meta_key>
		        <wp:meta_value>'.$this->wxr_cdata( $meta->meta_value ).'</wp:meta_value>
		        </wp:postmeta>';
        }

        $this->write_post_header_to_file($file,$post);

        file_put_contents($file,$post_meta_line,FILE_APPEND);

        $_comments = $wpdb->get_results( $wpdb->prepare( "SELECT * FROM $wpdb->comments WHERE comment_post_ID = %d AND comment_approved <> 'spam'", $post->ID ) );
        $comments  = array_map( 'get_comment', $_comments );
        $line='';
        foreach ( $comments as $c )
        {
            $line.='
                    <wp:comment>
                    <wp:comment_id>'.intval( $c->comment_ID ).'</wp:comment_id>
                    <wp:comment_author>'.$this->wxr_cdata( $c->comment_author ).'</wp:comment_author>
                    <wp:comment_author_email>'.$this->wxr_cdata( $c->comment_author_email ).'</wp:comment_author_email>
			        <wp:comment_author_url>'.esc_url_raw( $c->comment_author_url ).'</wp:comment_author_url>
			        <wp:comment_author_IP>'.$this->wxr_cdata( $c->comment_author_IP ).'</wp:comment_author_IP>
			        <wp:comment_date>'.$this->wxr_cdata( $c->comment_date ).'</wp:comment_date>
			        <wp:comment_date_gmt>'.$this->wxr_cdata( $c->comment_date_gmt ).'</wp:comment_date_gmt>
			        <wp:comment_content>'.$this->wxr_cdata( $c->comment_content ).'</wp:comment_content>
			        <wp:comment_approved>'.$this->wxr_cdata( $c->comment_approved ).'</wp:comment_approved>
			        <wp:comment_type>'.$this->wxr_cdata( $c->comment_type ).'</wp:comment_type>
			        <wp:comment_parent>'.intval( $c->comment_parent ).'</wp:comment_parent>
			        <wp:comment_user_id>'.intval( $c->user_id ).'</wp:comment_user_id>';
            $c_meta = $wpdb->get_results( $wpdb->prepare( "SELECT * FROM $wpdb->commentmeta WHERE comment_id = %d", $c->comment_ID ) );
            foreach ( $c_meta as $meta )
            {
                if ( apply_filters( 'wxr_export_skip_commentmeta', false, $meta->meta_key, $meta ) )
                {
                    continue;
                }
                $line.='
                        <wp:commentmeta>
                            <wp:meta_key>'.$this->wxr_cdata( $meta->meta_key ).'</wp:meta_key>
			                <wp:meta_value>'.$this->wxr_cdata( $meta->meta_value ).'</wp:meta_value>
			            </wp:commentmeta>';
            }
            $line.='
                    </wp:comment>';
        }
        file_put_contents($file,$line,FILE_APPEND);
        $line='
        </item>';
        file_put_contents($file,$line,FILE_APPEND);
    }

    public function write_post_to_file($file,$post)
    {
        global $wpdb;

        setup_postdata( $post );

        $postmeta = $wpdb->get_results( $wpdb->prepare( "SELECT * FROM $wpdb->postmeta WHERE post_id = %d", $post->ID ) );
        $post_meta_line='';

        $added_meta_key=array();
        foreach ( $postmeta as $meta )
        {
            //if ( apply_filters( 'wxr_export_skip_postmeta', false, $meta->meta_key, $meta ) ) {
            //    continue;
            //}
            if(in_array($meta->meta_key,$added_meta_key))
                continue;
            $added_meta_key[]=$meta->meta_key;
            $post_meta_line.='
                <wp:postmeta>
                <wp:meta_key>'.$this->wxr_cdata( $meta->meta_key ).'</wp:meta_key>
		        <wp:meta_value>'.$this->wxr_cdata( $meta->meta_value ).'</wp:meta_value>
		        </wp:postmeta>';
        }

        $this->write_post_header_to_file($file,$post);

        file_put_contents($file,$post_meta_line,FILE_APPEND);

        $_comments = $wpdb->get_results( $wpdb->prepare( "SELECT * FROM $wpdb->comments WHERE comment_post_ID = %d AND comment_approved <> 'spam'", $post->ID ) );
        $comments  = array_map( 'get_comment', $_comments );
        $line='';
        foreach ( $comments as $c )
        {
            $line.='
                    <wp:comment>
                    <wp:comment_id>'.intval( $c->comment_ID ).'</wp:comment_id>
                    <wp:comment_author>'.$this->wxr_cdata( $c->comment_author ).'</wp:comment_author>
                    <wp:comment_author_email>'.$this->wxr_cdata( $c->comment_author_email ).'</wp:comment_author_email>
			        <wp:comment_author_url>'.esc_url_raw( $c->comment_author_url ).'</wp:comment_author_url>
			        <wp:comment_author_IP>'.$this->wxr_cdata( $c->comment_author_IP ).'</wp:comment_author_IP>
			        <wp:comment_date>'.$this->wxr_cdata( $c->comment_date ).'</wp:comment_date>
			        <wp:comment_date_gmt>'.$this->wxr_cdata( $c->comment_date_gmt ).'</wp:comment_date_gmt>
			        <wp:comment_content>'.$this->wxr_cdata( $c->comment_content ).'</wp:comment_content>
			        <wp:comment_approved>'.$this->wxr_cdata( $c->comment_approved ).'</wp:comment_approved>
			        <wp:comment_type>'.$this->wxr_cdata( $c->comment_type ).'</wp:comment_type>
			        <wp:comment_parent>'.intval( $c->comment_parent ).'</wp:comment_parent>
			        <wp:comment_user_id>'.intval( $c->user_id ).'</wp:comment_user_id>';
            $c_meta = $wpdb->get_results( $wpdb->prepare( "SELECT * FROM $wpdb->commentmeta WHERE comment_id = %d", $c->comment_ID ) );
            foreach ( $c_meta as $meta )
            {
                if ( apply_filters( 'wxr_export_skip_commentmeta', false, $meta->meta_key, $meta ) )
                {
                    continue;
                }
                $line.='
                        <wp:commentmeta>
                            <wp:meta_key>'.$this->wxr_cdata( $meta->meta_key ).'</wp:meta_key>
			                <wp:meta_value>'.$this->wxr_cdata( $meta->meta_value ).'</wp:meta_value>
			            </wp:commentmeta>';
            }
            $line.='
                    </wp:comment>';
        }
        file_put_contents($file,$line,FILE_APPEND);
        $line='
        </item>';
        file_put_contents($file,$line,FILE_APPEND);
        return true;
    }

    public function write_cat_to_file($file,$post_ids)
    {
        $cats = $tags = $terms = array();

        $categories = (array) get_categories( array( 'object_ids' => $post_ids ) );
        $tags       = (array) get_tags( array( 'object_ids' => $post_ids ) );

        $custom_taxonomies = get_taxonomies( array( '_builtin' => false ) );
        $custom_terms      = (array) get_terms( $custom_taxonomies,array( 'object_ids' => $post_ids ) );

        // Put categories in order with no child going before its parent.
        while ( $cat = array_shift( $categories ) ) {
            if ( $cat->parent == 0 || isset( $cats[ $cat->parent ] ) ) {
                $cats[ $cat->term_id ] = $cat;
            } else {
                $categories[] = $cat;
            }
        }

        // Put terms in order with no child going before its parent.
        while ( $t = array_shift( $custom_terms ) ) {
            if ( $t->parent == 0 || isset( $terms[ $t->parent ] ) ) {
                $terms[ $t->term_id ] = $t;
            } else {
                $custom_terms[] = $t;
            }
        }

        unset( $categories, $custom_taxonomies, $custom_terms );

        $line='';
        foreach ($cats as $c)
        {
            $line.='<wp:category>
            <wp:term_id>'.intval( $c->term_id ).'</wp:term_id>
            <wp:category_nicename>'.$this->wxr_cdata( $c->slug ).'</wp:category_nicename>
            <wp:category_parent>'.$this->wxr_cdata( $c->parent ? $cats[ $c->parent ]->slug : '' ).'</wp:category_parent>
            '.$this->wxr_cat_name( $c ).'
            '.$this->wxr_category_description( $c ).'
            '.$this->wxr_term_meta( $c ).'
            </wp:category>';
        }
        file_put_contents($file,$line,FILE_APPEND);
        $line='';
        foreach ( $tags as $t )
        {
            $line.='<wp:tag>
            <wp:term_id>'.intval( $t->term_id ).'</wp:term_id>
            <wp:tag_slug>'.$this->wxr_cdata( $t->slug ).'</wp:tag_slug>
            '.$this->wxr_tag_name( $t ).'
            '.$this->wxr_tag_description( $t ).'
            '.$this->wxr_term_meta( $t ).'
            </wp:tag>';
        }
        file_put_contents($file,$line,FILE_APPEND);

        $line='';

        foreach ( $terms as $t)
        {
            $line.='<wp:term>
            <wp:term_id>'.$this->wxr_cdata( $t->term_id ).'</wp:term_id>
            <wp:term_taxonomy>'.$this->wxr_cdata( $t->taxonomy ).'</wp:term_taxonomy>
            <wp:term_slug>'.$this->wxr_cdata( $t->slug ).'</wp:term_slug>
            <wp:term_parent>'.$this->wxr_cdata( $t->parent ? $terms[ $t->parent ]->slug : '' ).'</wp:term_parent>          
            '.$this->wxr_term_name( $t ).'
            '.$this->wxr_term_description( $t ).'
            '.$this->wxr_term_meta( $t ).'          
        </wp:term>';
        }
    }

    private function wxr_cat_name( $category )
    {
        if ( empty( $category->name ) )
        {
            return '';
        }

        return '<wp:cat_name>' . $this->wxr_cdata( $category->name ) . "</wp:cat_name>";
    }

    private function wxr_category_description( $category ) {
        if ( empty( $category->description ) ) {
            return '<wp:category_description></wp:category_description>\n';
        }

        return '<wp:category_description>' . $this->wxr_cdata( $category->description ) . "</wp:category_description>";
    }

    private function wxr_tag_name( $tag ) {
        if ( empty( $tag->name ) ) {
            return '';
        }

        return '<wp:tag_name>' . $this->wxr_cdata( $tag->name ) . "</wp:tag_name>";
    }

    private function wxr_tag_description( $tag ) {
        if ( empty( $tag->description ) ) {
            return '';
        }

        return '<wp:tag_description>' . $this->wxr_cdata( $tag->description ) . "</wp:tag_description>";
    }

    private function wxr_term_name( $term ) {
        if ( empty( $term->name ) ) {
            return '';
        }

        return '<wp:term_name>' . $this->wxr_cdata( $term->name ) . "</wp:term_name>";
    }

    private function wxr_term_description( $term ) {
        if ( empty( $term->description ) ) {
            return '';
        }

        return "\t\t<wp:term_description>" . $this->wxr_cdata( $term->description ) . "</wp:term_description>";
    }

    private function wxr_term_meta( $term ) {
        global $wpdb;

        $termmeta = $wpdb->get_results( $wpdb->prepare( "SELECT * FROM $wpdb->termmeta WHERE term_id = %d", $term->term_id ) );

        $line='';
        foreach ( $termmeta as $meta )
        {
            /**
             * Filters whether to selectively skip term meta used for WXR exports.
             *
             * Returning a truthy value to the filter will skip the current meta
             * object from being exported.
             *
             * @since 4.6.0
             *
             * @param bool   $skip     Whether to skip the current piece of term meta. Default false.
             * @param string $meta_key Current meta key.
             * @param object $meta     Current meta object.
             */
            if ( ! apply_filters( 'wxr_export_skip_termmeta', false, $meta->meta_key, $meta ) )
            {
                $line.="\t\t<wp:termmeta>\n\t\t\t<wp:meta_key>".$this->wxr_cdata( $meta->meta_key )."</wp:meta_key>\n\t\t\t<wp:meta_value>".$this->wxr_cdata( $meta->meta_value )."</wp:meta_value>\n\t\t</wp:termmeta>\n";
            }
        }
        return $line;
    }

    private function wxr_cdata( $str )
    {
        if ( ! seems_utf8( $str ) ) {
            $str = utf8_encode( $str );
        }
        // $str = ent2ncr(esc_html($str));
        $str = '<![CDATA[' . str_replace( ']]>', ']]]]><![CDATA[>', $str ) . ']]>';

        return $str;
    }

    private function wxr_site_url() {
        if ( is_multisite() ) {
            // Multisite: the base URL.
            return network_home_url();
        } else {
            // WordPress (single site): the blog URL.
            return get_bloginfo_rss( 'url' );
        }
    }

    private function wxr_authors_list( array $post_ids = null )
    {
        global $wpdb;

        if ( ! empty( $post_ids ) ) {
            $post_ids = array_map( 'absint', $post_ids );
            $and      = 'AND ID IN ( ' . implode( ', ', $post_ids ) . ')';
        } else {
            $and = '';
        }

        $authors = array();
        $results = $wpdb->get_results( "SELECT DISTINCT post_author FROM $wpdb->posts WHERE post_status != 'auto-draft' $and" );
        foreach ( (array) $results as $result )
        {
            $authors[] = get_userdata( $result->post_author );
        }

        $authors = array_filter( $authors );

        $line='';
        foreach ( $authors as $author )
        {
            $line.= "\t<wp:author>";
            $line.= '<wp:author_id>' . intval( $author->ID ) . '</wp:author_id>';
            $line.= '<wp:author_login>' . $this->wxr_cdata( $author->user_login ) . '</wp:author_login>';
            $line.= '<wp:author_email>' . $this->wxr_cdata( $author->user_email ) . '</wp:author_email>';
            $line.= '<wp:author_display_name>' . $this->wxr_cdata( $author->display_name ) . '</wp:author_display_name>';
            $line.= '<wp:author_first_name>' . $this->wxr_cdata( $author->first_name ) . '</wp:author_first_name>';
            $line.= '<wp:author_last_name>' . $this->wxr_cdata( $author->last_name ) . '</wp:author_last_name>';
            $line.= "</wp:author>\n";
        }
        return $line;
    }

    private function wxr_post_taxonomy($post)
    {
        $taxonomies = get_object_taxonomies( $post->post_type );
        if ( empty( $taxonomies ) ) {
            return;
        }
        $terms = wp_get_object_terms( $post->ID, $taxonomies );
        $line='';
        foreach ( (array) $terms as $term )
        {
            $line.= "\t\t<category domain=\"{$term->taxonomy}\" nicename=\"{$term->slug}\">" . $this->wxr_cdata( $term->name ) . "</category>\n";
        }
        return $line;
    }
}

includes/class-wpvivid-crypt.php000064400000014734151327705670013043 0ustar00<?php

if (!defined('WPVIVID_PLUGIN_DIR')){
    die;
}

class WPvivid_crypt
{
    private $public_key;
    private $sym_key;

    private $rij;
    private $rsa;

    public function __construct($public_key)
    {
        $this->public_key=$public_key;
        include_once WPVIVID_PLUGIN_DIR . '/vendor/autoload.php';
        $this->rij= new Crypt_Rijndael();
        $this->rsa= new Crypt_RSA();
    }

    public function generate_key()
    {
        $this->sym_key = crypt_random_string(32);
        $this->rij->setKey($this->sym_key);
    }

    public function encrypt_message($message)
    {
        $this->generate_key();
        $key=$this->encrypt_key();
        $len=str_pad(dechex(strlen($key)),3,'0', STR_PAD_LEFT);
        $message=$this->rij->encrypt($message);
        if($message===false)
            return false;
        $message_len = str_pad(dechex(strlen($message)), 16, '0', STR_PAD_LEFT);
        return $len.$key.$message_len.$message;
    }

    public function encrypt_key()
    {
        $this->rsa->loadKey($this->public_key);
        return $this->rsa->encrypt($this->sym_key);
    }

    public function decrypt_message($message)
    {
        $len = substr($message, 0, 3);
        $len = hexdec($len);
        $key = substr($message, 3, $len);

        $cipherlen = substr($message, ($len + 3), 16);
        $cipherlen = hexdec($cipherlen);

        $data = substr($message, ($len + 19), $cipherlen);
        $rsa = new Crypt_RSA();
        $rsa->loadKey($this->public_key);
        $key=$rsa->decrypt($key);
        $rij = new Crypt_Rijndael();
        $rij->setKey($key);
        return $rij->decrypt($data);
    }

    public function encrypt_user_info($user,$pw)
    {
        $user_info['user']=$user;
        $user_info['pw']=$pw;
        $info=wp_json_encode($user_info);
        $this->rsa->loadKey($this->public_key);
        return $this->rsa->encrypt($info);
    }

    public function encrypt_user_token($user,$token)
    {
        $user_info['user']=$user;
        $user_info['token']=$token;
        $info=wp_json_encode($user_info);
        $this->rsa->loadKey($this->public_key);
        return $this->rsa->encrypt($info);
    }

    public function encrypt_token($token)
    {
        $this->rsa->loadKey($this->public_key);
        return $this->rsa->encrypt($token);
    }
}

class WPvivid_Crypt_File
{
    private $key;
    private $rij;

    public function __construct($key)
    {
        include_once WPVIVID_PLUGIN_DIR . '/vendor/autoload.php';
        $this->rij= new Crypt_Rijndael();
        $this->key=$key;
    }

    public function encrypt($file)
    {
        $encrypted_path = dirname($file).'/encrypt_'.basename($file).'.tmp';

        $data_encrypted = 0;
        $buffer_size = 2097152;

        $file_size = filesize($file);

        $this->rij->setKey($this->key);
        $this->rij->disablePadding();
        $this->rij->enableContinuousBuffer();

        if (file_exists($encrypted_path))
        {
            @wp_delete_file($encrypted_path);
        }
        $encrypted_handle = fopen($encrypted_path, 'wb+');

        $file_handle = fopen($file, 'rb');

        if($file_handle===false)
        {
            $ret['result']='failed';
            $ret['error']=$file.' file not found';
            return $ret;
        }

        while ($data_encrypted < $file_size)
        {
            $file_part = fread($file_handle, $buffer_size);

            $length = strlen($file_part);
            if (0 != $length % 16)
            {
                $pad = 16 - ($length % 16);
                $file_part = str_pad($file_part, $length + $pad, chr($pad));
            }

            $encrypted_data = $this->rij->encrypt($file_part);

            fwrite($encrypted_handle, $encrypted_data);

            $data_encrypted += $buffer_size;
        }

        fclose($encrypted_handle);
        fclose($file_handle);

        $result_path = $file.'.crypt';

        @rename($encrypted_path, $result_path);

        $ret['result']='success';
        $ret['file_path']=$result_path;
        return $ret;
    }

    public function decrypt($file)
    {
        $file_handle = fopen($file, 'rb');

        if($file_handle===false)
        {
            $ret['result']='failed';
            $ret['error']=$file.' file not found';
            return $ret;
        }

        $decrypted_path = dirname($file).'/decrypt_'.basename($file).'.tmp';

        $decrypted_handle = fopen($decrypted_path, 'wb+');

        $this->rij->setKey($this->key);
        $this->rij->disablePadding();
        $this->rij->enableContinuousBuffer();

        $file_size = filesize($file);
        $bytes_decrypted = 0;
        $buffer_size =2097152;

        while ($bytes_decrypted < $file_size)
        {
            $file_part = fread($file_handle, $buffer_size);

            $length = strlen($file_part);
            if (0 != $length % 16) {
                $pad = 16 - ($length % 16);
                $file_part = str_pad($file_part, $length + $pad, chr($pad));
            }

            $decrypted_data = $this->rij->decrypt($file_part);

            $is_last_block = ($bytes_decrypted + strlen($decrypted_data) >= $file_size);

            $write_bytes = min($file_size - $bytes_decrypted, strlen($decrypted_data));
            if ($is_last_block)
            {
                $is_padding = false;
                $last_byte = ord(substr($decrypted_data, -1, 1));
                if ($last_byte < 16)
                {
                    $is_padding = true;
                    for ($j = 1; $j<=$last_byte; $j++)
                    {
                        if (substr($decrypted_data, -$j, 1) != chr($last_byte))
                            $is_padding = false;
                    }
                }
                if ($is_padding)
                {
                    $write_bytes -= $last_byte;
                }
            }

            fwrite($decrypted_handle, $decrypted_data, $write_bytes);
            $bytes_decrypted += $buffer_size;
        }

        // close the main file handle
        fclose($decrypted_handle);
        // close original file
        fclose($file_handle);

        $fullpath_new = preg_replace('/\.crypt$/', '', $file, 1).'.decrypted.zip';

        @rename($decrypted_path, $fullpath_new);
        $ret['result']='success';
        $ret['file_path']=$fullpath_new;

        return $ret;
    }
}includes/class-wpvivid-function-realize.php000064400000022741151327705670015155 0ustar00<?php

class WPvivid_Function_Realize
{
    public function __construct()
    {

    }

    public function _backup_cancel($task_id = '')
    {
        global $wpvivid_plugin;

        try
        {
            $tasks = WPvivid_taskmanager::get_tasks();
            $no_responds=false;
            $task_id='';
            foreach ($tasks as $task)
            {
                $task_id = $task['id'];
                $backup_task=new WPvivid_Backup_Task_2($task['id']);
                $status=$backup_task->get_status();

                $file_name=$backup_task->task['options']['file_prefix'];
                $path=$backup_task->task['options']['dir'];
                $file =$path. DIRECTORY_SEPARATOR . $file_name . '_cancel';
                touch($file);

                $last_active_time=time()-$status['run_time'];
                if($last_active_time>180)
                {
                    $no_responds=true;
                }

                $timestamp = wp_next_scheduled('wpvivid_task_monitor_event_2', array($task_id));

                if ($timestamp === false)
                {
                    $wpvivid_plugin->backup2->add_monitor_event($task_id);
                }
            }

            if($no_responds)
            {
                $ret['result'] = 'success';
                $ret['no_response'] = true;
                $ret['task_id'] = $task_id;
                $ret['msg'] = __('The backup is not responding for a while, do you want to force cancel it?', 'wpvivid-backuprestore');
            }
            else
            {
                $ret['result'] = 'success';
                $ret['no_response'] = false;
                $ret['task_id'] = $task_id;
                $ret['msg'] = __('The backup will be canceled after backing up the current chunk ends.', 'wpvivid-backuprestore');
            }

        }
        catch (Exception $error)
        {
            $message = 'An exception has occurred. class: '.get_class($error).';msg: '.$error->getMessage().';code: '.$error->getCode().';line: '.$error->getLine().';in_file: '.$error->getFile().';';
            error_log($message);
            return array('result'=>'failed','error'=>$message);
        }

        /*
        try
        {
            $tasks = WPvivid_taskmanager::get_tasks();
            foreach ($tasks as $task)
            {
                $task_id = $task['id'];
                $status=WPvivid_taskmanager::get_backup_task_status($task_id);
                $time_spend=$status['run_time']-$status['start_time'];
                $options=WPvivid_Setting::get_option('wpvivid_common_setting');
                if(isset($options['max_execution_time']))
                {
                    $limit=$options['max_execution_time'];
                }
                else
                {
                    $limit=WPVIVID_MAX_EXECUTION_TIME;
                }

                if($time_spend > $limit * 2)
                {
                    $file_name = WPvivid_taskmanager::get_task_options($task_id, 'file_prefix');
                    $backup_options = WPvivid_taskmanager::get_task_options($task_id, 'backup_options');
                    $file = WP_CONTENT_DIR . DIRECTORY_SEPARATOR . $backup_options['dir'] . DIRECTORY_SEPARATOR . $file_name . '_cancel';
                    touch($file);

                    if($wpvivid_plugin->wpvivid_log->log_file_handle==false)
                    {
                        $wpvivid_plugin->wpvivid_log->OpenLogFile(WPvivid_taskmanager::get_task_options($task_id,'log_file_name'));
                    }
                    $wpvivid_plugin->wpvivid_log->WriteLog('Backup cancelled. Twice the setting time.','notice');
                    $task=new WPvivid_Backup_Task($task_id);
                    $task->update_status('cancel');
                    $wpvivid_plugin->clean_backing_up_data_event($task_id);
                    WPvivid_Schedule::clear_monitor_schedule($task_id);
                    WPvivid_taskmanager::delete_task($task_id);
                }
                else
                {
                    $file_name = WPvivid_taskmanager::get_task_options($task_id, 'file_prefix');
                    $backup_options = WPvivid_taskmanager::get_task_options($task_id, 'backup_options');
                    $file = WP_CONTENT_DIR . DIRECTORY_SEPARATOR . $backup_options['dir'] . DIRECTORY_SEPARATOR . $file_name . '_cancel';
                    touch($file);

                    $timestamp = wp_next_scheduled(WPVIVID_TASK_MONITOR_EVENT, array($task_id));

                    if ($timestamp === false) {
                        $wpvivid_plugin->add_monitor_event($task_id, 10);
                    }
                }
                $wpvivid_plugin->wpvivid_check_clear_litespeed_rule($task_id);
            }

            if (WPvivid_taskmanager::get_task($task_id) !== false) {
                $file_name = WPvivid_taskmanager::get_task_options($task_id, 'file_prefix');
                $backup_options = WPvivid_taskmanager::get_task_options($task_id, 'backup_options');
                $file = WP_CONTENT_DIR . DIRECTORY_SEPARATOR . $backup_options['dir'] . DIRECTORY_SEPARATOR . $file_name . '_cancel';
                touch($file);
            }

            $timestamp = wp_next_scheduled(WPVIVID_TASK_MONITOR_EVENT, array($task_id));

            if ($timestamp === false) {
                $wpvivid_plugin->add_monitor_event($task_id, 10);
            }
            $ret['result'] = 'success';
            $ret['msg'] = __('The backup will be canceled after backing up the current chunk ends.', 'wpvivid-backuprestore');
        }
        catch (Exception $error) {
            $message = 'An exception has occurred. class: '.get_class($error).';msg: '.$error->getMessage().';code: '.$error->getCode().';line: '.$error->getLine().';in_file: '.$error->getFile().';';
            error_log($message);
            return array('result'=>'failed','error'=>$message);
        }
        catch (Error $error) {
            $message = 'An exception has occurred. class: '.get_class($error).';msg: '.$error->getMessage().';code: '.$error->getCode().';line: '.$error->getLine().';in_file: '.$error->getFile().';';
            error_log($message);
            return array('result'=>'failed','error'=>$message);
        }*/
        return $ret;
    }

    public function _get_log_file($read_type, $param){
        global $wpvivid_plugin;
        $ret['result']='failed';
        if($read_type == 'backuplist'){
            $backup_id = $param;
            $backup = WPvivid_Backuplist::get_backup_by_id($backup_id);
            if(!$backup) {
                $ret['result']='failed';
                $ret['error']=__('Retrieving the backup information failed while showing log. Please try again later.', 'wpvivid-backuprestore');
                return $ret;
            }
            if(!file_exists($backup['log'])) {
                $ret['result']='failed';
                $ret['error']=__('The log not found.', 'wpvivid-backuprestore');
                return $ret;
            }
            $ret['result']='success';
            $ret['log_file']=$backup['log'];
        }
        else if($read_type == 'lastlog'){
            $option = $param;
            $log_file_name= $wpvivid_plugin->wpvivid_log->GetSaveLogFolder().$option.'_log.txt';
            if(!file_exists($log_file_name))
            {
                $information['result']='failed';
                $information['error']=__('The log not found.', 'wpvivid-backuprestore');
                return $information;
            }
            $ret['result']='success';
            $ret['log_file']=$log_file_name;
        }
        else if($read_type == 'tasklog'){
            $backup_task_id = $param;
            $option=WPvivid_taskmanager::get_task_options($backup_task_id,'log_file_name');
            if(!$option) {
                $information['result']='failed';
                $information['error']=__('Retrieving the backup information failed while showing log. Please try again later.', 'wpvivid-backuprestore');
                return $information;
            }
            $log_file_name= $wpvivid_plugin->wpvivid_log->GetSaveLogFolder().$option.'_log.txt';
            if(!file_exists($log_file_name)) {
                $information['result']='failed';
                $information['error']=__('The log not found.', 'wpvivid-backuprestore');
                return $information;
            }
            $ret['result']='success';
            $ret['log_file']=$log_file_name;
        }
        return $ret;
    }

    public function _set_remote($remote){
        WPvivid_Setting::update_option('wpvivid_upload_setting',$remote['upload']);
        $history=WPvivid_Setting::get_option('wpvivid_user_history');
        $history['remote_selected']=$remote['history']['remote_selected'];
        WPvivid_Setting::update_option('wpvivid_user_history',$history);
    }

    public function _get_default_remote_storage(){
        $remote_storage_type = '';
        $remoteslist=WPvivid_Setting::get_all_remote_options();
        $default_remote_storage='';
        foreach ($remoteslist['remote_selected'] as $value) {
            $default_remote_storage=$value;
        }
        foreach ($remoteslist as $key=>$value)
        {
            if($key === $default_remote_storage)
            {
                $remote_storage_type=$value['type'];
            }
        }
        return $remote_storage_type;
    }
}includes/class-wpvivid-db-method.php000064400000021771151327705670013544 0ustar00<?php

class WPvivid_DB_Method
{
    public $db_handle;
    public $type;

    public function connect_db()
    {
        $common_setting = WPvivid_Setting::get_setting(false, 'wpvivid_common_setting');
        $db_connect_method = isset($common_setting['options']['wpvivid_common_setting']['db_connect_method']) ? $common_setting['options']['wpvivid_common_setting']['db_connect_method'] : 'wpdb';
        if($db_connect_method === 'wpdb'){
            global $wpdb;
            $this->db_handle=$wpdb;
            $this->type='wpdb';
            return array('result'=>WPVIVID_SUCCESS);
        }
        else{
            if(class_exists('PDO')) {
                $extensions=get_loaded_extensions();
                if(array_search('pdo_mysql',$extensions)) {
                    $res = explode(':',DB_HOST);
                    $db_host = $res[0];
                    $db_port = empty($res[1])?'':$res[1];

                    if(!empty($db_port)) {
                        $dsn='mysql:host=' . $db_host . ';port=' . $db_port . ';dbname=' . DB_NAME;
                    }
                    else{
                        $dsn='mysql:host=' . $db_host . ';dbname=' . DB_NAME;
                    }

                    $this->db_handle=new PDO($dsn, DB_USER, DB_PASSWORD);

                    $this->type='pdo_mysql';
                    return array('result'=>WPVIVID_SUCCESS);
                }
                else{
                    return array('result'=>WPVIVID_FAILED, 'error'=>'The pdo_mysql extension is not detected. Please install the extension first or choose wpdb option for Database connection method.');
                }
            }
            else{
                return array('result'=>WPVIVID_FAILED, 'error'=>'The pdo_mysql extension is not detected. Please install the extension first or choose wpdb option for Database connection method.');
            }
        }
    }

    public function check_db($fcgi)
    {
        $ret=$this->connect_db();

        if($ret['result']==WPVIVID_FAILED)
        {
            return $ret;
        }

        if($this->type=='pdo_mysql')
        {
            return $this->check_db_pdo($fcgi);
        }
        else if($this->type=='wpdb')
        {
            return $this->check_db_wpdb($fcgi);
        }

        return array('result' => WPVIVID_FAILED,'error' => 'db handle type not found.');
    }

    public function check_db_pdo($fcgi)
    {
        $ret['alert_db']=false;
        $ret['result']=WPVIVID_SUCCESS;
        $ret['big_tables']=array();
        $db_info=array();

        $sth = $this->db_handle->query('SHOW TABLE STATUS');
        $dbSize = 0;
        $sum_rows=0;
        $rows = $sth->fetchAll();
        foreach ($rows as $row)
        {
            global $wpdb;
            if (is_multisite() && !defined('MULTISITE'))
            {
                $prefix = $wpdb->base_prefix;
            } else {
                $prefix = $wpdb->get_blog_prefix(0);
            }
            if(preg_match('/^(?!'.$prefix.')/', $row["Name"]) == 1){
                continue;
            }

            $db_info[$row["Name"]]["Rows"]=$row["Rows"];
            $db_info[$row["Name"]]["Data_length"]=size_format($row["Data_length"]+$row["Index_length"],2);
            if($row["Rows"]>1000000)
            {
                $ret['big_tables'][$row["Name"]]['Rows']=$row["Rows"];
                $ret['big_tables'][$row["Name"]]['Data_length']=size_format($row["Data_length"]+$row["Index_length"],2);
            }

            $sum_rows+=$row["Rows"];
            $dbSize+=$row["Data_length"]+$row["Index_length"];
        }
        if($fcgi)
        {
            $alter_sum_rows=4000000;
        }
        else
        {
            $alter_sum_rows=4000000*3;
        }

        $memory_limit = ini_get('memory_limit');
        $ret['memory_limit']=$memory_limit;
        $memory_limit = trim($memory_limit);
        $memory_limit_int = (int) $memory_limit;
        $last = strtolower(substr($memory_limit, -1));

        if($last == 'g')
            $memory_limit_int = $memory_limit_int*1024*1024*1024;
        if($last == 'm')
            $memory_limit_int = $memory_limit_int*1024*1024;
        if($last == 'k')
            $memory_limit_int = $memory_limit_int*1024;

        if($dbSize>($memory_limit_int*0.9))
        {
            $max_rows=0;
        }
        else
        {
            $max_rows=(($memory_limit_int*0.9)-$dbSize)/49;
        }

        $max_rows=max($max_rows,1048576);

        if($sum_rows>$alter_sum_rows||$sum_rows>$max_rows)
        {
            //big db alert
            $ret['alert_db']=true;
            $ret['sum_rows']=$sum_rows;
            $ret['db_size']=size_format($dbSize,2);
            if($fcgi)
                $ret['alter_fcgi']=true;
        }

        $ret['db_size']=size_format($dbSize,2);
        return $ret;
    }

    public function check_db_wpdb($fcgi)
    {
        $ret['alert_db']=false;
        $ret['result']=WPVIVID_SUCCESS;
        $ret['big_tables']=array();
        $db_info=array();

        global $wpdb;
        $result=$wpdb->get_results('SHOW TABLE STATUS',ARRAY_A);

        //$sth = $this->db_handle->query('SHOW TABLE STATUS');
        $dbSize = 0;
        $sum_rows=0;
        //$rows = $sth->fetchAll();
        foreach ($result as $row)
        {
            global $wpdb;
            if (is_multisite() && !defined('MULTISITE'))
            {
                $prefix = $wpdb->base_prefix;
            } else {
                $prefix = $wpdb->get_blog_prefix(0);
            }
            if(preg_match('/^(?!'.$prefix.')/', $row["Name"]) == 1){
                continue;
            }

            $db_info[$row["Name"]]["Rows"]=$row["Rows"];
            $db_info[$row["Name"]]["Data_length"]=size_format($row["Data_length"]+$row["Index_length"],2);
            if($row["Rows"]>1000000)
            {
                $ret['big_tables'][$row["Name"]]['Rows']=$row["Rows"];
                $ret['big_tables'][$row["Name"]]['Data_length']=size_format($row["Data_length"]+$row["Index_length"],2);
            }

            $sum_rows+=$row["Rows"];
            $dbSize+=$row["Data_length"]+$row["Index_length"];
        }
        if($fcgi)
        {
            $alter_sum_rows=4000000;
        }
        else
        {
            $alter_sum_rows=4000000*3;
        }

        $memory_limit = ini_get('memory_limit');
        $ret['memory_limit']=$memory_limit;
        $memory_limit = trim($memory_limit);
        $memory_limit_int = (int) $memory_limit;
        $last = strtolower(substr($memory_limit, -1));

        if($last == 'g')
            $memory_limit_int = $memory_limit_int*1024*1024*1024;
        if($last == 'm')
            $memory_limit_int = $memory_limit_int*1024*1024;
        if($last == 'k')
            $memory_limit_int = $memory_limit_int*1024;

        if($dbSize>($memory_limit_int*0.9))
        {
            $max_rows=0;
        }
        else
        {
            $max_rows=(($memory_limit_int*0.9)-$dbSize)/49;
        }

        $max_rows=max($max_rows,1048576);

        if($sum_rows>$alter_sum_rows||$sum_rows>$max_rows)
        {
            //big db alert
            $ret['alert_db']=true;
            $ret['sum_rows']=$sum_rows;
            $ret['db_size']=size_format($dbSize,2);
            if($fcgi)
                $ret['alter_fcgi']=true;
        }

        $ret['db_size']=size_format($dbSize,2);
        return $ret;
    }

    public function get_sql_mode()
    {
        try {
            $ret['result'] = WPVIVID_SUCCESS;
            $ret['mysql_mode'] = '';

            global $wpdb;
            $result = $wpdb->get_results('SELECT @@SESSION.sql_mode', ARRAY_A);
            foreach ($result as $row) {
                $ret['mysql_mode'] = $row["@@SESSION.sql_mode"];
            }
        }
        catch (Exception $error) {
            $message = 'An exception has occurred. class: '.get_class($error).';msg: '.$error->getMessage().';code: '.$error->getCode().';line: '.$error->getLine().';in_file: '.$error->getFile().';';
            error_log($message);
            return array('result'=>'failed','error'=>$message);
        }

        return $ret;
    }

    public function get_mysql_version()
    {
        global $wpdb;

        $mysql_version = $wpdb->db_version();

        return $mysql_version;
    }

    public function check_max_allowed_packet()
    {
        global $wpvivid_plugin,$wpdb;

        $max_allowed_packet = (int) $wpdb->get_var("SELECT @@session.max_allowed_packet");

        if($max_allowed_packet<1048576)
        {
            $wpvivid_plugin->wpvivid_log->WriteLog('warning: max_allowed_packet less than 1M :'.size_format($max_allowed_packet,2),'notice');
        }
        else if($max_allowed_packet<33554432)
        {
            $wpvivid_plugin->wpvivid_log->WriteLog('max_allowed_packet less than 32M :'.size_format($max_allowed_packet,2),'notice');
        }
    }
}includes/class-wpvivid-taskmanager.php000064400000055220151327705670014172 0ustar00<?php

if (!defined('WPVIVID_PLUGIN_DIR')){
    die;
}
class WPvivid_taskmanager
{
    public static function new_backup_task($option,$type,$action='backup')
    {
        $id=uniqid('wpvivid-');
        $task['id']=$id;
        $task['action']=$action;
        $task['type']=$type;

        $task['status']['start_time']=time();
        $task['status']['run_time']=time();
        $task['status']['timeout']=time();
        $task['status']['str']='ready';
        $task['status']['resume_count']=0;

        $task['options']=$option;
        $task['options']['file_prefix']=$task['id'].'_'.gmdate('Y-m-d-H-i',$task['status']['start_time']);
        $task['options']['log_file_name']=$id.'_backup';
        $log=new WPvivid_Log();
        $log->CreateLogFile($task['options']['log_file_name'],'no_folder','backup');
        $log->CloseFile();

        $task['data']['doing']='backup';
        $task['data']['backup']['doing']='';
        $task['data']['backup']['finished']=0;
        $task['data']['backup']['progress']=0;
        $task['data']['backup']['job_data']=array();
        $task['data']['backup']['sub_job']=array();
        $task['data']['backup']['db_size']='0';
        $task['data']['backup']['files_size']['sum']='0';
        $task['data']['upload']['doing']='';
        $task['data']['upload']['finished']=0;
        $task['data']['upload']['progress']=0;
        $task['data']['upload']['job_data']=array();
        $task['data']['upload']['sub_job']=array();
        WPvivid_Setting::update_task($id,$task);
        $ret['result']='success';
        $ret['task_id']=$task['id'];
        return $ret;
    }

    public static function get_backup_task_prefix($task_id)
    {
        $tasks=WPvivid_Setting::get_tasks();
        if(array_key_exists ($task_id,$tasks))
        {
            $task = $tasks[$task_id];
            return  $task['options']['file_prefix'];
        }
        else
        {
            return false;
        }
    }

    public static function new_upload_task($option,$type)
    {
        $id=uniqid('wpvivid-');
        $task['id']=$id;
        $task['action']='upload';
        $task['type']='Manual';
        $task['type']=$type;

        $task['status']['start_time']=time();
        $task['status']['run_time']=time();
        $task['status']['timeout']=time();
        $task['status']['str']='ready';
        $task['status']['resume_count']=0;

        $task['options']=$option;
        $task['options']['file_prefix']=$task['id'].'_'.gmdate('Y-m-d-H-i',$task['status']['start_time']);
        $task['options']['log_file_name']=$id.'_backup';
        $log=new WPvivid_Log();
        $log->CreateLogFile($task['options']['log_file_name'],'no_folder','backup');
        $log->CloseFile();

        $task['data']['doing']='upload';
        $task['data']['doing']='backup';
        $task['data']['backup']['doing']='';
        $task['data']['backup']['finished']=1;
        $task['data']['backup']['progress']=100;
        $task['data']['backup']['job_data']=array();
        $task['data']['backup']['sub_job']=array();
        $task['data']['backup']['db_size']='0';
        $task['data']['backup']['files_size']['sum']='0';
        $task['data']['upload']['doing']='';
        $task['data']['upload']['finished']=0;
        $task['data']['upload']['progress']=0;
        $task['data']['upload']['job_data']=array();
        $task['data']['upload']['sub_job']=array();
        WPvivid_Setting::update_task($id,$task);
        $ret['result']='success';
        $ret['task_id']=$task['id'];
        return $ret;
    }

    public static function delete_ready_task()
    {
        $tasks=WPvivid_Setting::get_tasks();
        $delete_ids=array();

        if(!empty($tasks))
        {
            foreach ($tasks as $task)
            {
                if($task['status']['str']=='ready')
                {
                    $delete_ids[]=$task['id'];
                }
            }
        }

        foreach ($delete_ids as $id)
        {
            unset($tasks[$id]);
        }
        if(!empty($delete_ids))
            WPvivid_Setting::update_option('wpvivid_task_list',$tasks);
    }

    public static function is_task_canceled($task_id)
    {
        if(self::get_task($task_id)!==false)
        {
            $file_name = self::get_task_options($task_id, 'file_prefix');
            $backup_options = self::get_task_options($task_id, 'backup_options');

            $file = WP_CONTENT_DIR . DIRECTORY_SEPARATOR . $backup_options['dir'] . DIRECTORY_SEPARATOR . $file_name . '_cancel';

            if (file_exists($file))
            {
                return true;
            }
        }
        return false;
    }

    public static function get_backup_tasks_info($action){
        $tasks=WPvivid_Setting::get_tasks();
        $ret=array();
        foreach ($tasks as $task)
        {
            if($task['action']==$action)
            {
                $ret[$task['id']]['status']=self::get_backup_tasks_status($task['id']);
                $ret[$task['id']]['is_canceled']=self::is_task_canceled($task['id']);
                $ret[$task['id']]['size']=self::get_backup_size($task['id']);
                $ret[$task['id']]['data']=self::get_backup_tasks_progress($task['id']);
            }
        }
        return $ret;
    }

    public static function get_backup_tasks_status($task_id){
        $tasks=WPvivid_Setting::get_tasks();
        if(array_key_exists ($task_id,$tasks))
        {
            $task = $tasks[$task_id];
            return $task['status'];
        }
        else
        {
            return false;
        }
    }

    public static function get_backup_size($task_id){
        $tasks=WPvivid_Setting::get_tasks();
        if(array_key_exists ($task_id,$tasks))
        {
            $task = $tasks[$task_id];

            if(array_key_exists('db_size',$task['data']['backup']))
            {
                $ret['db_size']=$task['data']['backup']['db_size'];
                $ret['files_size']=$task['data']['backup']['files_size'];
                return $ret;
            }
            else
            {
                return false;
            }

        }
        else
        {
            return false;
        }
    }

    public static function get_backup_tasks_progress($task_id){
        $tasks=WPvivid_Setting::get_tasks();
        if(array_key_exists ($task_id,$tasks))
        {
            $task = $tasks[$task_id];
            $current_time=gmdate("Y-m-d H:i:s");
            $create_time=gmdate("Y-m-d H:i:s",$task['status']['start_time']);
            $time_diff=strtotime($current_time)-strtotime($create_time);
            $running_time='';
            if(gmdate("G",$time_diff) > 0){
                $running_time .= gmdate("G",$time_diff).' hour(s)';
            }
            if(intval(gmdate("i",$time_diff)) > 0){
                $running_time .= intval(gmdate("i",$time_diff)).' min(s)';
            }
            if(intval(gmdate("s",$time_diff)) > 0){
                $running_time .= intval(gmdate("s",$time_diff)).' second(s)';
            }
            $next_resume_time=WPvivid_Schedule::get_next_resume_time($task['id']);

            $ret['type']=$task['data']['doing'];
            $ret['progress']=$task['data'][$ret['type']]['progress'];
            $ret['doing']=$task['data'][$ret['type']]['doing'];
            if(isset($task['data'][$ret['type']]['sub_job'][$ret['doing']]['progress']))
                $ret['descript']=$task['data'][$ret['type']]['sub_job'][$ret['doing']]['progress'];
            else
                $ret['descript']='';
            if(isset($task['data'][$ret['type']]['sub_job'][$ret['doing']]['upload_data']))
                $ret['upload_data']=$task['data'][$ret['type']]['sub_job'][$ret['doing']]['upload_data'];
            $task['data'][$ret['type']]['sub_job'][$ret['doing']]['upload_data']=false;
            $ret['running_time']=$running_time;
            $ret['running_stamp']=$time_diff;
            $ret['next_resume_time']=$next_resume_time;
            return $ret;
        }
        else
        {
            return false;
        }
    }

    public static function get_backup_task_status($task_id)
    {
        $tasks=WPvivid_Setting::get_tasks();
        if(array_key_exists ($task_id,$tasks))
        {
            $task = $tasks[$task_id];
            return $task['status'];
        }
        else
        {
            return false;
        }
    }

    public static function update_backup_task_status($task_id,$reset_start_time=false,$status='',$reset_timeout=false,$resume_count=false,$error='')
    {
        $tasks=WPvivid_Setting::get_tasks();
        if(array_key_exists ($task_id,$tasks))
        {
            $task = $tasks[$task_id];
            $task['status']['run_time']=time();
            if($reset_start_time)
                $task['status']['start_time']=time();
            if(!empty($status))
            {
                $task['status']['str']=$status;
            }
            if($reset_timeout)
                $task['status']['timeout']=time();
           if($resume_count!==false)
           {
               $task['status']['resume_count']=$resume_count;
           }

            if(!empty($error))
           {
               $task['status']['error']=$error;
           }
            WPvivid_Setting::update_task($task_id,$task);
            return $task;
        }
        else
        {
            return false;
        }
    }

    public static function get_backup_task_error($task_id)
    {
        $tasks=WPvivid_Setting::get_tasks();
        if(array_key_exists ($task_id,$tasks))
        {
            $task = $tasks[$task_id];
            return $task['status']['error'];
        }
        else
        {
            return false;
        }
    }

    public static function get_task_options($task_id,$option_names)
    {
        $tasks=WPvivid_Setting::get_tasks();
        if(array_key_exists ($task_id,$tasks))
        {
            $task=$tasks[$task_id];

            if(is_array($option_names))
            {
                $options=array();
                foreach ($option_names as $name)
                {
                    $options[$name]=$task['options'][$name];
                }
                return $options;
            }
            else
            {
                return $task['options'][$option_names];
            }
        }
        else
        {
            return false;
        }
    }

    public static function update_task_options($task_id,$option_name,$option)
    {
        $tasks=WPvivid_Setting::get_tasks();
        if(array_key_exists ($task_id,$tasks))
        {
            $task=$tasks[$task_id];
            $task['options'][$option_name]=$option;

            WPvivid_Setting::update_task($task_id,$task);
            return true;
        }
        else
        {
            return false;
        }
    }

    public static function update_backup_main_task_progress($task_id,$job_name,$progress,$finished,$job_data=array())
    {
        $task=self::get_task($task_id);
        if($task!==false)
        {
            $task['status']['run_time']=time();
            $task['status']['str']='running';
            $task['data']['doing']=$job_name;
            $task['data'][$job_name]['finished']=$finished;
            $task['data'][$job_name]['progress']=$progress;
            $task['data'][$job_name]['job_data']=$job_data;
            WPvivid_Setting::update_task($task_id,$task);
        }
    }

    public static function update_backup_sub_task_progress($task_id,$job_name,$sub_job_name,$finished,$progress,$job_data=array(),$upload_data=array())
    {
        $task=self::get_task($task_id);
        if($task!==false)
        {
            $task['status']['run_time']=time();
            $task['status']['str']='running';
            $task['data']['doing']=$job_name;
            if(empty($sub_job_name))
            {
                $sub_job_name=$task['data'][$job_name]['doing'];
            }
            $task['data'][$job_name]['doing']=$sub_job_name;
            $task['data'][$job_name]['sub_job'][$sub_job_name]['finished']=$finished;
            if(!empty($progress))
                $task['data'][$job_name]['sub_job'][$sub_job_name]['progress']=$progress;
            if(!empty($job_data))
            {
                $task['data'][$job_name]['sub_job'][$sub_job_name]['job_data']=$job_data;
            }
            else
            {
                if(!isset($task['data'][$job_name]['sub_job'][$sub_job_name]['job_data']))
                {
                    $task['data'][$job_name]['sub_job'][$sub_job_name]['job_data']=array();
                }
            }
            if(!empty($upload_data)){
                $task['data'][$job_name]['sub_job'][$sub_job_name]['upload_data']=$upload_data;
            }
            else{
                if(!isset($task['data'][$job_name]['sub_job'][$sub_job_name]['upload_data'])){
                    $task['data'][$job_name]['sub_job'][$sub_job_name]['upload_data']=array();
                }
            }
            WPvivid_Setting::update_task($task_id,$task);
        }
    }

    public static function get_backup_main_task_progress($task_id,$job_name='')
    {
        $task=self::get_task($task_id);

        if(empty($job_name))
        {
            $job_name=$task['data']['doing'];
            return $job_name;
        }

        if(array_key_exists($job_name,$task['data']))
        {
            return $task['data'][$job_name];
        }
        return false;
    }

    public static function get_backup_sub_task_progress($task_id,$job_name,$sub_job_name)
    {
        $task=self::get_task($task_id);
        if(array_key_exists($job_name,$task['data']))
        {
            if(array_key_exists($sub_job_name,$task['data'][$job_name]['sub_job']))
            {
                return $task['data'][$job_name]['sub_job'][$sub_job_name];
            }
        }
        return false;
    }

    public static function update_backup_db_task_info($task_id,$db_info)
    {
        $task=self::get_task($task_id);
        $task['data']['backup']['sub_job']['backup_db']['db_info']=$db_info;
        WPvivid_Setting::update_task($task_id,$task);
    }

    public static function update_file_and_db_info($task_id,$db_size,$files_size)
    {
        $task=self::get_task($task_id);
        $task['data']['backup']['db_size']=$db_size;
        $task['data']['backup']['files_size']=$files_size;
        WPvivid_Setting::update_task($task_id,$task);
    }

    public static function update_download_cache($backup_id,$cache)
    {
        $default = array();

        $options = get_option('wpvivid_download_cache', $default);
        $options[$backup_id]['cache']=$cache;
        WPvivid_Setting::update_option('wpvivid_download_cache',$options);
    }

    public static function get_download_cache($backup_id)
    {
        $default = array();

        $options = get_option('wpvivid_download_cache', $default);

        if(array_key_exists($backup_id,$options))
        {
            return $options[$backup_id]['cache'];
        }

        return false;
    }

    public static function get_task($id)
    {
        $tasks=WPvivid_Setting::get_tasks();
        if(array_key_exists ($id,$tasks))
        {
            $task=$tasks[$id];
            return $task;
        }
        else
        {
            return false;
        }
    }

    public static function get_tasks()
    {
        $tasks=WPvivid_Setting::get_tasks();
        return $tasks;
    }

    public static function get_tasks_by_action($action)
    {
        $tasks=WPvivid_Setting::get_tasks();
        $ret=array();
        foreach ($tasks as $task)
        {
            if($task['action']==$action)
            {
                $ret[$task['id']]=$task;
            }
        }
        return $ret;
    }

    public static function is_tasks_backup_running()
    {
        $tasks=WPvivid_Setting::get_tasks();
        foreach ($tasks as $task)
        {
            if ($task['status']['str']=='running'||$task['status']['str']=='no_responds')
            {
                return true;
            }
        }
        return false;
    }

    public static function get_tasks_backup_running()
    {
        $tasks=WPvivid_Setting::get_tasks();
        $ret=array();
        foreach ($tasks as $task)
        {
            if($task['action']=='backup')
            {
                if ($task['status']['str']=='running')
                {
                    $ret[$task['id']]=$task;
                }
            }
        }
        return $ret;
    }

    public static function update_task($task)
    {
        WPvivid_Setting::update_task($task['id'],$task);
    }

    public static function delete_task($id)
    {
        WPvivid_Setting::delete_task($id);
    }

    public static function mark_task($id)
    {
        $tasks=WPvivid_Setting::get_tasks();
        if(array_key_exists ($id,$tasks))
        {
            $task=$tasks[$id];
            $task['marked']=1;
            WPvivid_Setting::update_task($id,$task);
            return true;
        }
        else
        {
            return false;
        }
    }

    public static function delete_marked_task()
    {
        $has_marked = 0;
        $tasks=WPvivid_Setting::get_tasks();
        $delete_ids=array();
        foreach ($tasks as $task)
        {
            if(isset($task['marked']))
            {
                $delete_ids[]=$task['id'];
            }
        }
        foreach ($delete_ids as $id)
        {
            unset($tasks[$id]);
            $has_marked = 1;
        }
        WPvivid_Setting::update_option('wpvivid_task_list',$tasks);
        return $has_marked;
    }

    public static function delete_out_of_date_finished_task()
    {
        $tasks=WPvivid_Setting::get_tasks();
        $delete_ids=array();

        if(!empty($tasks))
        {
            foreach ($tasks as $task)
            {
                if($task['status']['str']=='error'||$task['status']['str']=='completed')
                {
                    if(time()-$task['status']['run_time']>60)
                    {
                        $delete_ids[]=$task['id'];
                    }
                }
            }
        }

        foreach ($delete_ids as $id)
        {
            unset($tasks[$id]);
        }

        WPvivid_Setting::update_option('wpvivid_task_list',$tasks);
    }

    public static function delete_all_task()
    {
        WPvivid_Setting::delete_option('wpvivid_task_list');
    }

    public static function is_backup_task_timeout($task)
    {
        $current_time=gmdate("Y-m-d H:i:s");
        $run_time=gmdate("Y-m-d H:i:s",  $task['data']['run_time']);
        $running_time=strtotime($current_time)-strtotime($run_time);
        if($running_time>$task['data']['options']['max_execution_time'])
        {
            return true;
        }
        else
        {
            return false;
        }
    }

    public static function new_download_task_v2($file_name)
    {
        $default = array();
        wp_cache_delete('notoptions', 'options');
        wp_cache_delete('alloptions', 'options');
        wp_cache_delete('wpvivid_download_task_v2', 'options');

        $options = get_option('wpvivid_download_task_v2', $default);

        $task['file_name']=$file_name;
        $task['start_time']=time();
        $task['run_time']=time();
        $task['progress_text']='start download file:'.$file_name;
        $task['status']='running';
        $task['error']='';
        $options[$file_name]=$task;

        WPvivid_Setting::update_option('wpvivid_download_task_v2',$options);
        return $task;
    }

    public static function is_download_task_running_v2($file_name)
    {
        $default = array();
        $options = get_option('wpvivid_download_task_v2', $default);

        if(empty($options))
        {
            return false;
        }
        if(array_key_exists($file_name,$options))
        {
            $task=$options[$file_name];

            if($task['status'] === 'error')
            {
                return false;
            }

            if(time()-$task['run_time']>60)
            {
                return false;
            }
            else {
                return true;
            }
        }
        return false;
    }

    public static function update_download_task_v2(&$task,$progress_text,$status='',$error='')
    {
        $default = array();
        wp_cache_delete('notoptions', 'options');
        wp_cache_delete('alloptions', 'options');
        wp_cache_delete('wpvivid_download_task_v2', 'options');

        $options = get_option('wpvivid_download_task_v2', $default);

        $file_name=$task['file_name'];
        $task['run_time']=time();
        $task['progress_text']=$progress_text;
        if($status!='')
        {
            $task['status']=$status;
            if($error!='')
            {
                $task['error']=$error;
            }
        }

        $options[$file_name]=$task;

        WPvivid_Setting::update_option('wpvivid_download_task_v2',$options);
    }

    public static function get_download_task_v2($file_name)
    {
        $default = array();
        $options = get_option('wpvivid_download_task_v2', $default);

        if(empty($options))
        {
            return false;
        }
        if(array_key_exists($file_name,$options))
        {
            if(time()-$options[$file_name]['run_time']>60)
            {
                $options[$file_name]['status']='timeout';
                $options[$file_name]['error']='time out';
            }

            return $options[$file_name];
        }
        return false;
    }

    public static function delete_download_task_v2($file_name)
    {
        $default = array();
        $options = get_option('wpvivid_download_task_v2', $default);

        if(empty($options))
        {
            return false;
        }
        if(array_key_exists($file_name,$options))
        {
            unset($options[$file_name]);
            WPvivid_Setting::update_option('wpvivid_download_task_v2',$options);
            return true;
        }
        return false;
    }

    public static function wpvivid_reset_backup_retry_times($task_id)
    {
        $task=self::get_task($task_id);
        if($task!==false)
        {
            $task['status']['resume_count']=0;
            WPvivid_Setting::update_task($task_id,$task);
        }
    }
}includes/class-wpvivid-tools.php000064400000011061151327705670013030 0ustar00<?php

if (!defined('WPVIVID_PLUGIN_DIR')){
    die;
}

class WPvivid_tools
{
    public static function clean_junk_cache(){
        $home_url_prefix=get_home_url();
        $parse = wp_parse_url($home_url_prefix);
        $tmppath='';
        if(isset($parse['path'])) {
            $tmppath=str_replace('/','_',$parse['path']);
        }
        $home_url_prefix = $parse['host'].$tmppath;
        $path = WP_CONTENT_DIR.DIRECTORY_SEPARATOR.WPvivid_Setting::get_backupdir();
        $handler=opendir($path);
        if($handler===false)
        {
            return ;
        }
        while(($filename=readdir($handler))!==false)
        {
            /*if(is_dir($path.DIRECTORY_SEPARATOR.$filename) && preg_match('#temp-'.$home_url_prefix.'_'.'#',$filename))
            {
                WPvivid_tools::deldir($path.DIRECTORY_SEPARATOR.$filename,'',true);
            }
            if(is_dir($path.DIRECTORY_SEPARATOR.$filename) && preg_match('#temp-'.'#',$filename))
            {
                WPvivid_tools::deldir($path.DIRECTORY_SEPARATOR.$filename,'',true);
            }*/
            if(preg_match('#pclzip-.*\.tmp#', $filename)){
                @wp_delete_file($path.DIRECTORY_SEPARATOR.$filename);
            }
            if(preg_match('#pclzip-.*\.gz#', $filename)){
                @wp_delete_file($path.DIRECTORY_SEPARATOR.$filename);
            }
        }
        @closedir($handler);
    }

    public static function deldir($path,$exclude='',$flag = false)
    {
        if(!is_dir($path))
        {
            return ;
        }
        $handler=opendir($path);
        if(empty($handler))
            return ;
        while(($filename=readdir($handler))!==false)
        {
            if($filename != "." && $filename != "..")
            {
                if(is_dir($path.DIRECTORY_SEPARATOR.$filename)){
                    if(empty($exclude)||WPvivid_tools::regex_match($exclude['directory'],$path.DIRECTORY_SEPARATOR.$filename ,0)){
                        self::deldir( $path.DIRECTORY_SEPARATOR.$filename ,$exclude, $flag);
                        @rmdir( $path.DIRECTORY_SEPARATOR.$filename );
                    }
                }else{
                    if(empty($exclude)||WPvivid_tools::regex_match($exclude['file'],$path.DIRECTORY_SEPARATOR.$filename ,0)){
                        @wp_delete_file($path.DIRECTORY_SEPARATOR.$filename);
                    }
                }
            }
        }
        if($handler)
            @closedir($handler);
        if($flag)
            @rmdir($path);
    }

    public static function regex_match($regex_array,$string,$mode)
    {
        if(empty($regex_array))
        {
            return true;
        }

        if($mode==0)
        {
            foreach ($regex_array as $regex)
            {
                if(preg_match($regex,$string))
                {
                    return false;
                }
            }

            return true;
        }

        if($mode==1)
        {
            foreach ($regex_array as $regex)
            {
                if(preg_match($regex,$string))
                {
                    return true;
                }
            }

            return false;
        }

        return true;
    }

    public static function GetSaveLogFolder()
    {
        $options = get_option('wpvivid_common_setting',array());

        if(!isset($options['log_save_location']))
        {
            //WPvivid_Setting::set_default_common_option();
            $options['log_save_location']=WPVIVID_DEFAULT_LOG_DIR;
            update_option('wpvivid_common_setting', $options, 'no');

            $options = get_option('wpvivid_common_setting',array());
        }

        if(!is_dir(WP_CONTENT_DIR.DIRECTORY_SEPARATOR.$options['log_save_location']))
        {
            @mkdir(WP_CONTENT_DIR.DIRECTORY_SEPARATOR.$options['log_save_location'],0777,true);
            //@fopen(WP_CONTENT_DIR.DIRECTORY_SEPARATOR.$options['log_save_location'].DIRECTORY_SEPARATOR.'index.html', 'x');
            $tempfile=@fopen(WP_CONTENT_DIR.DIRECTORY_SEPARATOR.$options['log_save_location'].DIRECTORY_SEPARATOR.'.htaccess', 'x');
            if($tempfile)
            {
                //$text="deny from all";
                $text="<IfModule mod_rewrite.c>\r\nRewriteEngine On\r\nRewriteRule .* - [F,L]\r\n</IfModule>";
                fwrite($tempfile,$text );
            }
        }

        return WP_CONTENT_DIR.DIRECTORY_SEPARATOR.$options['log_save_location'].DIRECTORY_SEPARATOR;
    }
}includes/class-wpvivid-backup-uploader.php000064400000130010151327705670014742 0ustar00<?php

if (!defined('WPVIVID_PLUGIN_DIR'))
{
    die;
}

class Wpvivid_BackupUploader
{
    public function __construct()
    {
        add_action('wp_ajax_wpvivid_cancel_upload_backup_free', array($this, 'cancel_upload_backup_free'));
        add_action('wp_ajax_wpvivid_is_backup_file_free', array($this, 'is_backup_file_free'));
        add_action('wp_ajax_wpvivid_upload_files_finish_free', array($this, 'upload_files_finish_free'));
        add_action('wp_ajax_wpvivid_get_file_id',array($this,'get_file_id'));
        add_action('wp_ajax_wpvivid_upload_files',array($this,'upload_files'));
        add_action('wp_ajax_wpvivid_upload_files_finish',array($this,'upload_files_finish'));
        add_action('wp_ajax_wpvivid_delete_upload_incomplete_backup_free', array($this, 'delete_upload_incomplete_backup'));

        add_action('wp_ajax_wpvivid_rescan_local_folder',array($this,'rescan_local_folder_set_backup'));
        add_action('wp_ajax_wpvivid_get_backup_count',array($this,'get_backup_count'));
        add_action('wpvivid_rebuild_backup_list', array($this, 'wpvivid_rebuild_backup_list'), 10);
    }

    function cancel_upload_backup_free()
    {
        check_ajax_referer( 'wpvivid_ajax', 'nonce' );
        $check=current_user_can('manage_options');
        $check=apply_filters('wpvivid_ajax_check_security',$check);
        if(!$check)
        {
            die();
        }
        try{
            $path=WP_CONTENT_DIR.DIRECTORY_SEPARATOR.WPvivid_Setting::get_backupdir().DIRECTORY_SEPARATOR;
            if(is_dir($path))
            {
                $handler = opendir($path);
                if($handler!==false)
                {
                    while (($filename = readdir($handler)) !== false)
                    {
                        if ($filename != "." && $filename != "..")
                        {
                            if (is_dir($path  . $filename))
                            {
                                continue;
                            }
                            else
                            {
                                if (preg_match('/.*\.tmp$/', $filename))
                                {
                                    @wp_delete_file($path  . $filename);
                                }

                                if (preg_match('/.*\.part$/', $filename))
                                {
                                    @wp_delete_file($path  . $filename);
                                }
                            }
                        }
                    }
                    if($handler)
                        @closedir($handler);
                }
            }
        }
        catch (Exception $error) {
            $message = 'An exception has occurred. class: '.get_class($error).';msg: '.$error->getMessage().';code: '.$error->getCode().';line: '.$error->getLine().';in_file: '.$error->getFile().';';
            error_log($message);
            echo wp_json_encode(array('result'=>'failed','error'=>$message));
        }
        die();
    }

    public function is_wpvivid_backup($file_name)
    {
        if (preg_match('/wpvivid-.*_.*_.*\.zip$/', $file_name))
        {
            return true;
        }
        else {
            return false;
        }
    }

    function is_backup_file_free()
    {
        check_ajax_referer( 'wpvivid_ajax', 'nonce' );
        $check=current_user_can('manage_options');
        $check=apply_filters('wpvivid_ajax_check_security',$check);
        if(!$check)
        {
            die();
        }

        try
        {
            $file_name=sanitize_text_field($_POST['file_name']);
            if (isset($file_name))
            {
                if ($this->is_wpvivid_backup($file_name))
                {
                    $ret['result'] = WPVIVID_SUCCESS;

                    $backupdir=WPvivid_Setting::get_backupdir();
                    $filePath = WP_CONTENT_DIR.DIRECTORY_SEPARATOR.$backupdir.DIRECTORY_SEPARATOR.$file_name;
                    if(file_exists($filePath))
                    {
                        $ret['is_exists']=true;
                    }
                    else
                    {
                        $ret['is_exists']=false;
                    }
                }
                else
                {
                    $ret['result'] = WPVIVID_FAILED;
                    $ret['error'] = $file_name . ' is not created by WPvivid backup plugin.';
                }
            }
            else
            {
                $ret['result'] = WPVIVID_FAILED;
                $ret['error'] = 'Failed to post file name.';
            }

            echo wp_json_encode($ret);
        }
        catch (Exception $error)
        {
            $message = 'An exception has occurred. class: '.get_class($error).';msg: '.$error->getMessage().';code: '.$error->getCode().';line: '.$error->getLine().';in_file: '.$error->getFile().';';
            echo wp_json_encode(array('result'=>'failed','error'=>$message));
        }

        die();
    }

    function upload_files_finish_free()
    {
        check_ajax_referer( 'wpvivid_ajax', 'nonce' );
        $check=current_user_can('manage_options');
        $check=apply_filters('wpvivid_ajax_check_security',$check);
        if(!$check)
        {
            die();
        }

        try {
            $ret = $this->_rescan_local_folder_set_backup();
            echo wp_json_encode($ret);
        }
        catch (Exception $error)
        {
            $message = 'An exception has occurred. class: '.get_class($error).';msg: '.$error->getMessage().';code: '.$error->getCode().';line: '.$error->getLine().';in_file: '.$error->getFile().';';
            error_log($message);
            echo wp_json_encode(array('result'=>'failed','error'=>$message));
        }
        die();
    }

    function delete_upload_incomplete_backup()
    {
        check_ajax_referer( 'wpvivid_ajax', 'nonce' );
        $check=current_user_can('manage_options');
        $check=apply_filters('wpvivid_ajax_check_security',$check);
        if(!$check)
        {
            die();
        }

        try {
            if(isset($_POST['incomplete_backup'])&&!empty($_POST['incomplete_backup']))
            {
                $json = sanitize_text_field($_POST['incomplete_backup']);
                $json = stripslashes($json);
                $incomplete_backup = json_decode($json, true);

                if(is_array($incomplete_backup) && !empty($incomplete_backup))
                {
                    $path=WP_CONTENT_DIR.DIRECTORY_SEPARATOR.WPvivid_Setting::get_backupdir().DIRECTORY_SEPARATOR;
                    foreach ($incomplete_backup as $backup)
                    {
                        $backup = basename($backup);
                        if (preg_match('/wpvivid-.*_.*_.*\.zip$/', $backup))
                        {
                            @wp_delete_file($path.$backup);
                        }
                        else if(preg_match('/'.apply_filters('wpvivid_white_label_file_prefix', 'wpvivid').'-.*_.*_.*\.zip$/', $backup))
                        {
                            @wp_delete_file($path.$backup);
                        }
                    }
                }

                $ret['result']='success';
                echo wp_json_encode($ret);
            }
        }
        catch (Exception $error)
        {
            $message = 'An exception has occurred. class: '.get_class($error).';msg: '.$error->getMessage().';code: '.$error->getCode().';line: '.$error->getLine().';in_file: '.$error->getFile().';';
            error_log($message);
            echo wp_json_encode(array('result'=>'failed','error'=>$message));
        }
        die();
    }

    function get_file_id()
    {
        check_ajax_referer( 'wpvivid_ajax', 'nonce' );
        $check=current_user_can('manage_options');
        $check=apply_filters('wpvivid_ajax_check_security',$check);
        if(!$check)
        {
            die();
        }

        $file_name=sanitize_text_field($_POST['file_name']);
        if(isset($file_name))
        {
            if(preg_match('/wpvivid-.*_.*_.*\.zip$/',$file_name))
            {
                if(preg_match('/wpvivid-(.*?)_/',$file_name,$matches))
                {
                    $id= $matches[0];
                    $id=substr($id,0,strlen($id)-1);
                    if(WPvivid_Backuplist::get_backup_by_id($id)===false)
                    {
                        $ret['result']=WPVIVID_SUCCESS;
                        $ret['id']=$id;
                    }
                    else
                    {
                        $ret['result']=WPVIVID_FAILED;
                        $ret['error']='The uploading backup already exists in Backups list.';
                    }
                }
                else
                {
                    $ret['result']=WPVIVID_FAILED;
                    $ret['error']=$file_name . ' is not created by WPvivid backup plugin.';
                }
            }
            else
            {
                $ret['result']=WPVIVID_FAILED;
                $ret['error']=$file_name . ' is not created by WPvivid backup plugin.';
            }
        }
        else
        {
            $ret['result']=WPVIVID_FAILED;
            $ret['error']='Failed to post file name.';
        }

        echo wp_json_encode($ret);
        die();
    }

    function check_file_is_a_wpvivid_backup($file_name,&$backup_id)
    {
        if(preg_match('/wpvivid-.*_.*_.*\.zip$/',$file_name))
        {
            if(preg_match('/wpvivid-(.*?)_/',$file_name,$matches))
            {
                $id= $matches[0];
                $id=substr($id,0,strlen($id)-1);


                if(WPvivid_Backuplist::get_backup_by_id($id)===false)
                {
                    $backup_id=$id;
                    return true;
                }
                else
                {
                    return false;
                }
            }
            else
            {
                return false;
            }
        }
        else
        {
            return false;
        }
    }

    function upload_files()
    {
        check_ajax_referer( 'wpvivid_ajax', 'nonce' );
        $check=current_user_can('manage_options');
        $check=apply_filters('wpvivid_ajax_check_security',$check);
        if(!$check)
        {
            die();
        }

        try
        {
            $chunk = isset($_REQUEST["chunk"]) ? intval(sanitize_key($_REQUEST["chunk"])) : 0;
            $chunks = isset($_REQUEST["chunks"]) ? intval(sanitize_key($_REQUEST["chunks"])) : 0;

            $fileName = isset($_REQUEST["name"]) ? sanitize_text_field($_REQUEST["name"]) : $_FILES["file"]["name"];
            $validate = wp_check_filetype( $fileName );
            if ( $validate['type'] == false )
            {
                echo wp_json_encode(array('result'=>'failed','error'=>"File type is not allowed."));
                die();
            }

            $backupdir=WPvivid_Setting::get_backupdir();
            $filePath = WP_CONTENT_DIR.DIRECTORY_SEPARATOR.$backupdir.DIRECTORY_SEPARATOR.$fileName;

            $out = @fopen("{$filePath}.part", $chunk == 0 ? "wb" : "ab");

            if ($out)
            {
                // Read binary input stream and append it to temp file
                $options['test_form'] =true;
                $options['action'] ='wpvivid_upload_files';
                $options['test_type'] = false;
                $options['ext'] = 'zip';
                $options['type'] = 'application/zip';

                add_filter('upload_dir', array($this, 'upload_dir'));

                $status = wp_handle_upload($_FILES['async-upload'],$options);

                remove_filter('upload_dir', array($this, 'upload_dir'));

                $in = @fopen($status['file'], "rb");

                if ($in)
                {
                    while ($buff = fread($in, 4096))
                        fwrite($out, $buff);
                }
                else
                {
                    echo wp_json_encode(array('result'=>'failed','error'=>"Failed to open tmp file.path:".$status['file']));
                    die();
                }

                @fclose($in);
                @fclose($out);

                @wp_delete_file($status['file']);
            }
            else
            {
                echo wp_json_encode(array('result'=>'failed','error'=>"Failed to open input stream.path:{$filePath}.part"));
                die();
            }

            if (!$chunks || $chunk == $chunks - 1)
            {
                // Strip the temp .part suffix off
                rename("{$filePath}.part", $filePath);
            }

            echo wp_json_encode(array('result' => WPVIVID_SUCCESS));
        }
        catch (Exception $error)
        {
            $message = 'An exception has occurred. class: '.get_class($error).';msg: '.$error->getMessage().';code: '.$error->getCode().';line: '.$error->getLine().';in_file: '.$error->getFile().';';
            error_log($message);
            echo wp_json_encode(array('result'=>'failed','error'=>$message));
        }
        die();
    }

    function upload_files_finish()
    {
        check_ajax_referer( 'wpvivid_ajax', 'nonce' );
        $check=current_user_can('manage_options');
        $check=apply_filters('wpvivid_ajax_check_security',$check);
        if(!$check)
        {
            die();
        }

        $ret['html']=false;
        if(isset($_POST['files']))
        {
            $files=sanitize_text_field($_POST['files']);
            $files =stripslashes($files);
            $files =json_decode($files,true);
            if(is_null($files))
            {
                die();
            }

            $path=WP_CONTENT_DIR.DIRECTORY_SEPARATOR.WPvivid_Setting::get_backupdir().DIRECTORY_SEPARATOR;

            $backup_data['result']='success';
            $backup_data['files']=array();
            if(preg_match('/wpvivid-.*_.*_.*\.zip$/',$files[0]['name']))
            {
                if(preg_match('/wpvivid-(.*?)_/',$files[0]['name'],$matches_id))
                {
                    if(preg_match('/[0-9]{4}-[0-9]{2}-[0-9]{2}-[0-9]{2}-[0-9]{2}/',$files[0]['name'],$matches))
                    {
                        $backup_time=$matches[0];
                        $time_array=explode('-',$backup_time);
                        if(sizeof($time_array)>4)
                            $time=$time_array[0].'-'.$time_array[1].'-'.$time_array[2].' '.$time_array[3].':'.$time_array[4];
                        else
                            $time=$backup_time;
                        $time=strtotime($time);
                    }
                    else
                    {
                        $time=time();
                    }
                    $id= $matches_id[0];
                    $id=substr($id,0,strlen($id)-1);
                    $unlinked_file = '';
                    $check_result=true;
                    foreach ($files as $file)
                    {
                        $res=$this->check_is_a_wpvivid_backup($path.$file['name']);
                        if($res === true)
                        {
                            $add_file['file_name']=$file['name'];
                            $add_file['size']=filesize($path.$file['name']);
                            $backup_data['files'][]=$add_file;
                        }
                        else
                        {
                            $check_result=false;
                            $unlinked_file .= 'file name: '.$file['name'].', error: '.$res;
                        }
                    }
                    if($check_result === true){
                        WPvivid_Backuplist::add_new_upload_backup($id,$backup_data,$time,'');
                        $html = '';
                        $html = apply_filters('wpvivid_add_backup_list', $html);
                        $ret['result']=WPVIVID_SUCCESS;
                        $ret['html'] = $html;
                    }
                    else{
                        foreach ($files as $file) {
                            $this->clean_tmp_files($path, $file['name']);
                            @wp_delete_file($path . $file['name']);
                        }
                        $ret['result']=WPVIVID_FAILED;
                        $ret['error']='Upload file failed.';
                        $ret['unlinked']=$unlinked_file;
                    }
                }
                else
                {
                    $ret['result']=WPVIVID_FAILED;
                    $ret['error']='The backup is not created by WPvivid backup plugin.';
                }
            }
            else
            {
                $ret['result']=WPVIVID_FAILED;
                $ret['error']='The backup is not created by WPvivid backup plugin.';
            }
        }
        else{
            $ret['result']=WPVIVID_FAILED;
            $ret['error']='Failed to post file name.';
        }
        echo wp_json_encode($ret);
        die();
    }

    function clean_tmp_files($path, $filename){
        $handler=opendir($path);
        if($handler===false)
            return;
        while(($file=readdir($handler))!==false) {
            if (!is_dir($path.$file) && preg_match('/wpvivid-.*_.*_.*\.tmp$/', $file)) {
                $iPos = strrpos($file, '_');
                $file_temp = substr($file, 0, $iPos);
                if($file_temp === $filename) {
                    @wp_delete_file($path.$file);
                }
            }
        }
        @closedir($handler);
    }

    function wpvivid_check_remove_update_backup($path){
        $backup_list = WPvivid_Setting::get_option('wpvivid_backup_list');
        $remove_backup_array = array();
        $update_backup_array = array();
        $tmp_file_array = array();
        $remote_backup_list=WPvivid_Backuplist::get_has_remote_backuplist();
        foreach ($backup_list as $key => $value){
            if(!in_array($key, $remote_backup_list)) {
                $need_remove = true;
                $need_update = false;
                if (is_dir($path)) {
                    $handler = opendir($path);
                    if($handler===false)
                        return true;
                    while (($filename = readdir($handler)) !== false) {
                        if ($filename != "." && $filename != "..") {
                            if (!is_dir($path . $filename)) {
                                if ($this->check_wpvivid_file_info($filename, $backup_id, $need_update)) {
                                    if ($key === $backup_id) {
                                        $need_remove = false;
                                    }
                                    if ($need_update) {
                                        if ($this->check_is_a_wpvivid_backup($path . $filename) === true) {
                                            if (!in_array($filename, $tmp_file_array)) {
                                                $add_file['file_name'] = $filename;
                                                $add_file['size'] = filesize($path . $filename);
                                                $tmp_file_array[] = $filename;
                                                $update_backup_array[$backup_id]['files'][] = $add_file;
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                    if ($handler) {
                        @closedir($handler);
                    }
                }
                if ($need_remove) {
                    $remove_backup_array[] = $key;
                }
            }
        }
        $this->wpvivid_remove_update_local_backup_list($remove_backup_array, $update_backup_array);
        return true;
    }

    function check_wpvivid_file_info($file_name, &$backup_id, &$need_update=false){
        if(preg_match('/wpvivid-.*_.*_.*\.zip$/',$file_name))
        {
            if(preg_match('/wpvivid-(.*?)_/',$file_name,$matches))
            {
                $id= $matches[0];
                $id=substr($id,0,strlen($id)-1);
                $backup_id=$id;

                if(WPvivid_Backuplist::get_backup_by_id($id)===false)
                {
                    $need_update = false;
                    return true;
                }
                else
                {
                    $need_update = true;
                    return true;
                }
            }
            else
            {
                return false;
            }
        }
        else
        {
            return false;
        }
    }

    function wpvivid_remove_update_local_backup_list($remove_backup_array, $update_backup_array){
        $backup_list = WPvivid_Setting::get_option('wpvivid_backup_list');
        foreach ($remove_backup_array as $remove_backup_id){
            unset($backup_list[$remove_backup_id]);
        }
        foreach ($update_backup_array as $update_backup_id => $data){
            $backup_list[$update_backup_id]['backup']['files'] = $data['files'];
        }
        WPvivid_Setting::update_option('wpvivid_backup_list', $backup_list);
    }

    function _rescan_local_folder_set_backup(){
        $path=WP_CONTENT_DIR.DIRECTORY_SEPARATOR.WPvivid_Setting::get_backupdir().DIRECTORY_SEPARATOR;

        $this->wpvivid_check_remove_update_backup($path);

        $backups=array();
        $count = 0;
        if(is_dir($path))
        {
            $handler = opendir($path);
            if($handler!==false)
            {
                while (($filename = readdir($handler)) !== false)
                {
                    if ($filename != "." && $filename != "..")
                    {
                        $count++;

                        if (is_dir($path  . $filename))
                        {
                            continue;
                        } else {
                            if($this->check_file_is_a_wpvivid_backup($filename,$backup_id))
                            {
                                if($this->zip_check_sum($path . $filename))
                                {
                                    if($this->check_is_a_wpvivid_backup($path.$filename) === true)
                                    {
                                        $backups[$backup_id]['files'][]=$filename;
                                    }
                                    else
                                    {
                                        $ret['incomplete_backup'][] = $filename;
                                    }
                                }
                            }
                        }
                    }
                }
                if($handler)
                    @closedir($handler);
            }
        }
        else{
            $ret['result']=WPVIVID_FAILED;
            $ret['error']='Failed to get local storage directory.';
        }
        if(!empty($backups))
        {
            foreach ($backups as $backup_id =>$backup)
            {
                $backup_data['result']='success';
                $backup_data['files']=array();
                if(empty($backup['files']))
                    continue;
                $time=false;
                foreach ($backup['files'] as $file)
                {
                    if($time===false)
                    {
                        if(preg_match('/[0-9]{4}-[0-9]{2}-[0-9]{2}-[0-9]{2}-[0-9]{2}/',$file,$matches))
                        {
                            $backup_time=$matches[0];
                            $time_array=explode('-',$backup_time);
                            if(sizeof($time_array)>4)
                                $time=$time_array[0].'-'.$time_array[1].'-'.$time_array[2].' '.$time_array[3].':'.$time_array[4];
                            else
                                $time=$backup_time;
                            $time=strtotime($time);
                        }
                        else
                        {
                            $time=time();
                        }
                    }

                    $add_file['file_name']=$file;
                    $add_file['size']=filesize($path.$file);
                    $backup_data['files'][]=$add_file;
                }

                WPvivid_Backuplist::add_new_upload_backup($backup_id,$backup_data,$time,'');
            }
        }
        $ret['result']=WPVIVID_SUCCESS;
        $html = '';
        $tour = true;
        $html = apply_filters('wpvivid_add_backup_list', $html, 'wpvivid_backup_list', $tour);
        $ret['html'] = $html;
        return $ret;
    }

    function rescan_local_folder_set_backup()
    {
        check_ajax_referer( 'wpvivid_ajax', 'nonce' );
        $check=current_user_can('manage_options');
        $check=apply_filters('wpvivid_ajax_check_security',$check);
        if(!$check)
        {
            die();
        }

        $ret = $this->_rescan_local_folder_set_backup();
        echo wp_json_encode($ret);
        die();
    }

    public function wpvivid_rebuild_backup_list(){
        $this->_rescan_local_folder_set_backup();
    }

    static function rescan_local_folder()
    {
        $backupdir=WPvivid_Setting::get_backupdir();
        ?>
        <div style="padding-top: 10px;">
            <span><?php esc_html_e('Tips: Click the button below to scan all uploaded or received backups in directory', 'wpvivid-backuprestore'); ?>&nbsp<?php echo esc_html(WP_CONTENT_DIR.'/'.$backupdir); ?></span>
        </div>
        <div style="padding-top: 10px;">
            <input type="submit" class="button-primary" value="<?php esc_attr_e('Scan uploaded backup or received backup', 'wpvivid-backuprestore'); ?>" onclick="wpvivid_rescan_local_folder();" />
        </div>
        <script type="text/javascript">
            function wpvivid_rescan_local_folder()
            {
                var ajax_data = {
                    'action': 'wpvivid_rescan_local_folder'
                };
                wpvivid_post_request(ajax_data, function (data)
                {
                    try {
                        var jsonarray = jQuery.parseJSON(data);
                        if(typeof jsonarray.incomplete_backup !== 'undefined' && jsonarray.incomplete_backup.length > 0)
                        {
                            var incomplete_count = jsonarray.incomplete_backup.length;
                            alert('Failed to scan '+incomplete_count+' backup zips, the zips can be corrupted during creation or download process. Please check the zips.');
                        }
                        if(jsonarray.html !== false)
                        {
                            jQuery('#wpvivid_backup_list').html('');
                            jQuery('#wpvivid_backup_list').append(jsonarray.html);
                            wpvivid_popup_tour('show');
                        }
                    }
                    catch(err) {
                        alert(err);
                    }
                }, function (XMLHttpRequest, textStatus, errorThrown)
                {
                    var error_message = wpvivid_output_ajaxerror('scanning backup list', textStatus, errorThrown);
                    alert(error_message);
                });
            }
        </script>
        <?php
    }

    function get_backup_count()
    {
        check_ajax_referer( 'wpvivid_ajax', 'nonce' );
        $check=current_user_can('manage_options');
        $check=apply_filters('wpvivid_ajax_check_security',$check);
        if(!$check)
        {
            die();
        }

        $backuplist=WPvivid_Backuplist::get_backuplist();
        echo esc_attr(sizeof($backuplist));
        die();
    }

    static function upload_meta_box()
    {
        ?>
        <div id="wpvivid_plupload-upload-ui" class="hide-if-no-js" style="margin-bottom: 10px;">
            <div id="drag-drop-area">
                <div class="drag-drop-inside">
                    <p class="drag-drop-info"><?php esc_html_e('Drop files here', 'wpvivid-backuprestore'); ?></p>
                    <p><?php esc_html_x('or', 'Uploader: Drop files here - or - Select Files', 'wpvivid-backuprestore'); ?></p>
                    <p class="drag-drop-buttons"><input id="wpvivid_select_file_button" type="button" value="<?php esc_attr_e('Select Files', 'wpvivid-backuprestore'); ?>" class="button" /></p>
                </div>
            </div>
        </div>
        <div id="wpvivid_uploaded_file_list" class="hide-if-no-js" style="margin-bottom: 10px;"></div>
        <div id="wpvivid_upload_file_list" class="hide-if-no-js" style="margin-bottom: 10px;"></div>
        <div style="margin-bottom: 10px;">
            <input type="submit" class="button-primary" id="wpvivid_upload_submit_btn" value="Upload" onclick="wpvivid_submit_upload();" />
            <input type="submit" class="button-primary" id="wpvivid_stop_upload_btn" value="Cancel" onclick="wpvivid_cancel_upload();" />
        </div>
        <div style="clear: both;"></div>
        <?php
        $chunk_size = min(wp_max_upload_size(), 1048576*2);
        $plupload_init = array(
            'browse_button'       => 'wpvivid_select_file_button',
            'container'           => 'wpvivid_plupload-upload-ui',
            'drop_element'        => 'drag-drop-area',
            'file_data_name'      => 'async-upload',
            'max_retries'		    => 3,
            'multiple_queues'     => true,
            'max_file_size'       => '10Gb',
            'chunk_size'        => $chunk_size.'b',
            'url'                 => admin_url('admin-ajax.php'),
            'multipart'           => true,
            'urlstream_upload'    => true,
            // additional post data to send to our ajax hook
            'multipart_params'    => array(
                '_ajax_nonce' => wp_create_nonce('wpvivid_ajax'),
                'action'      => 'wpvivid_upload_files',            // the ajax action name
            ),
        );

        // we should probably not apply this filter, plugins may expect wp's media uploader...
        $plupload_init = apply_filters('plupload_init', $plupload_init);
        $upload_file_image = includes_url( '/images/media/archive.png' );
        ?>

        <script type="text/javascript">
            var uploader;

            function wpvivid_stop_upload()
            {
                var ajax_data = {
                    'action': 'wpvivid_cancel_upload_backup_free',
                };
                wpvivid_post_request(ajax_data, function (data)
                {
                    jQuery("#wpvivid_select_file_button").prop('disabled', false);
                    jQuery('#wpvivid_upload_file_list').html("");
                    jQuery('#wpvivid_upload_submit_btn').hide();
                    jQuery('#wpvivid_stop_upload_btn').hide();
                    wpvivid_init_upload_list();
                }, function (XMLHttpRequest, textStatus, errorThrown)
                {
                    var error_message = wpvivid_output_ajaxerror('cancelling upload backups', textStatus, errorThrown);
                    alert(error_message);
                    jQuery('#wpvivid_upload_file_list').html("");
                    jQuery('#wpvivid_upload_submit_btn').hide();
                    jQuery('#wpvivid_stop_upload_btn').hide();
                    wpvivid_init_upload_list();
                });
            }

            function wpvivid_check_plupload_added_files(up,files)
            {
                var repeat_files = '';
                var exist_files = '';
                var file_count=files.length;
                var current_scan=0;
                var exist_count=0;
                plupload.each(files, function(file)
                {
                    var brepeat=false;
                    var file_list = jQuery('#wpvivid_upload_file_list span');
                    file_list.each(function (index, value)
                    {
                        if (value.innerHTML === file.name)
                        {
                            brepeat=true;
                        }
                    });

                    if(!brepeat)
                    {
                        var ajax_data = {
                            'action': 'wpvivid_is_backup_file_free',
                            'file_name':file.name
                        };
                        wpvivid_post_request(ajax_data, function (data)
                        {
                            current_scan++;
                            var jsonarray = jQuery.parseJSON(data);
                            if (jsonarray.result === "success")
                            {
                                if(jsonarray.is_exists==true)
                                {
                                    exist_count++;
                                    if(exist_files === '') {
                                        exist_files += file.name;
                                    }
                                    else {
                                        exist_files += ', ' + file.name;
                                    }
                                    wpvivid_file_uploaded_queued(file);
                                    uploader.removeFile(file);
                                }
                                else
                                {
                                    wpvivid_fileQueued( file );
                                }
                                if(file_count === exist_count)
                                {
                                    alert("The backup already exists in target folder.");
                                    exist_files = '';
                                }
                                else if((file_count === current_scan) && (exist_files !== ''))
                                {
                                    alert(exist_files + " already exist in target folder.");
                                    exist_files = '';
                                }
                            }
                            else if(jsonarray.result === "failed")
                            {
                                uploader.removeFile(file);
                                alert(jsonarray.error);
                            }
                        }, function (XMLHttpRequest, textStatus, errorThrown)
                        {
                            current_scan++;
                            var error_message = wpvivid_output_ajaxerror('uploading backups', textStatus, errorThrown);
                            uploader.removeFile(file);
                            alert(error_message);
                        });
                    }
                    else{
                        current_scan++;
                        if(repeat_files === ''){
                            repeat_files += file.name;
                        }
                        else{
                            repeat_files += ', ' + file.name;
                        }
                    }
                });
                if(repeat_files !== ''){
                    alert(repeat_files + " already exists in upload list.");
                    repeat_files = '';
                }
            }

            function wpvivid_fileQueued(file)
            {
                jQuery('#wpvivid_upload_file_list').append(
                    '<div id="' + file.id + '" style="width: 100%; height: 36px; background: #fff; margin-bottom: 1px;">' +
                    '<img src=" <?php echo esc_attr($upload_file_image); ?> " alt="" style="float: left; margin: 2px 10px 0 3px; max-width: 40px; max-height: 32px;">' +
                    '<div style="line-height: 36px; float: left; margin-left: 5px;"><span>' + file.name + '</span></div>' +
                    '<div class="fileprogress" style="line-height: 36px; float: right; margin-right: 5px;"></div>' +
                    '</div>' +
                    '<div style="clear: both;"></div>'
                );
                jQuery('#wpvivid_upload_submit_btn').show();
                jQuery('#wpvivid_stop_upload_btn').show();
                jQuery("#wpvivid_upload_submit_btn").prop('disabled', false);
            }

            function wpvivid_file_uploaded_queued(file)
            {
                jQuery('#'+file.id).remove();
            }

            function wpvivid_delete_incomplete_backups(incomplete_backup)
            {
                var ajax_data = {
                    'action': 'wpvivid_delete_upload_incomplete_backup_free',
                    'incomplete_backup': incomplete_backup
                };
                wpvivid_post_request(ajax_data, function (data)
                {
                }, function (XMLHttpRequest, textStatus, errorThrown)
                {
                });
            }

            function wpvivid_init_upload_list()
            {
                uploader = new plupload.Uploader(<?php echo wp_json_encode($plupload_init); ?>);

                // checks if browser supports drag and drop upload, makes some css adjustments if necessary
                uploader.bind('Init', function(up)
                {
                    var uploaddiv = jQuery('#wpvivid_plupload-upload-ui');

                    if(up.features.dragdrop){
                        uploaddiv.addClass('drag-drop');
                        jQuery('#drag-drop-area')
                            .bind('dragover.wp-uploader', function(){ uploaddiv.addClass('drag-over'); })
                            .bind('dragleave.wp-uploader, drop.wp-uploader', function(){ uploaddiv.removeClass('drag-over'); });

                    }else{
                        uploaddiv.removeClass('drag-drop');
                        jQuery('#drag-drop-area').unbind('.wp-uploader');
                    }
                });

                uploader.init();
                // a file was added in the queue

                uploader.bind('FilesAdded', wpvivid_check_plupload_added_files);

                uploader.bind('Error', function(up, error)
                {
                    alert('Upload ' + error.file.name +' error, error code: ' + error.code + ', ' + error.message);
                    wpvivid_stop_upload();
                });

                uploader.bind('FileUploaded', function(up, file, response)
                {
                    var jsonarray = jQuery.parseJSON(response.response);
                    if(jsonarray.result == 'failed')
                    {
                        alert('upload ' + file.name + ' failed, ' + jsonarray.error);

                        uploader.stop();
                        wpvivid_stop_upload();
                    }
                    else
                    {
                        wpvivid_file_uploaded_queued(file);
                    }
                });

                uploader.bind('UploadProgress', function(up, file)
                {
                    jQuery('#' + file.id + " .fileprogress").html(file.percent + "%");
                });

                uploader.bind('UploadComplete',function(up, files)
                {
                    jQuery('#wpvivid_upload_file_list').html("");
                    jQuery('#wpvivid_upload_submit_btn').hide();
                    jQuery('#wpvivid_stop_upload_btn').hide();
                    jQuery("#wpvivid_select_file_button").prop('disabled', false);
                    var ajax_data = {
                        'action': 'wpvivid_upload_files_finish_free'
                    };
                    wpvivid_post_request(ajax_data, function (data)
                    {
                        try
                        {
                            var jsonarray = jQuery.parseJSON(data);
                            if(jsonarray.result === 'success')
                            {
                                if(typeof jsonarray.incomplete_backup !== 'undefined' && jsonarray.incomplete_backup.length > 0)
                                {
                                    var incomplete_count = jsonarray.incomplete_backup.length;
                                    var incomplete_backup = JSON.stringify(jsonarray.incomplete_backup);
                                    wpvivid_delete_incomplete_backups(incomplete_backup);
                                    alert('Failed to scan '+incomplete_count+' backup zips, the zips can be corrupted during creation or download process. Please check the zips.');
                                }
                                else
                                {
                                    alert('The upload has completed.');
                                }
                                jQuery('#wpvivid_backup_list').html('');
                                jQuery('#wpvivid_backup_list').append(jsonarray.html);
                                wpvivid_click_switch_page('backup', 'wpvivid_tab_backup', true);
                                location.href = '<?php echo esc_url(admin_url()) . 'admin.php?page=WPvivid'; ?>';
                            }
                            else
                            {
                                alert(jsonarray.error);
                            }
                        }
                        catch(err)
                        {
                            alert(err);
                        }
                    }, function (XMLHttpRequest, textStatus, errorThrown)
                    {
                        var error_message = wpvivid_output_ajaxerror('refreshing backup list', textStatus, errorThrown);
                        alert(error_message);
                    });
                    plupload.each(files, function(file)
                    {
                        if(typeof file === 'undefined')
                        {

                        }
                        else
                        {
                            uploader.removeFile(file.id);
                        }
                    });
                });

                uploader.bind('Destroy', function(up, file)
                {
                    wpvivid_stop_upload();
                });
            }

            jQuery(document).ready(function($)
            {
                // create the uploader and pass the config from above
                jQuery('#wpvivid_upload_submit_btn').hide();
                jQuery('#wpvivid_stop_upload_btn').hide();
                wpvivid_init_upload_list();
            });

            function wpvivid_submit_upload()
            {
                jQuery("#wpvivid_upload_submit_btn").prop('disabled', true);
                jQuery("#wpvivid_select_file_button").prop('disabled', true);
                uploader.refresh();
                uploader.start();
            }

            function wpvivid_cancel_upload()
            {
                uploader.destroy();
            }
        </script>
        <?php
    }

    public function upload_dir($uploads)
    {
        $uploads['path'] = WP_CONTENT_DIR.DIRECTORY_SEPARATOR.WPvivid_Setting::get_backupdir();
        return $uploads;
    }

    private function check_is_a_wpvivid_backup($file_name)
    {
        $ret=WPvivid_Backup_Item::get_backup_file_info($file_name);
        if($ret['result'] === WPVIVID_SUCCESS){
            return true;
        }
        elseif($ret['result'] === WPVIVID_FAILED){
            return $ret['error'];
        }
    }

    private function zip_check_sum($file_name)
    {
        return true;
    }
}includes/class-wpvivid-backup-site.php000064400000003235151327705670014103 0ustar00<?php

if (!defined('WPVIVID_PLUGIN_DIR')){
    die;
}
require_once WPVIVID_PLUGIN_DIR . '/includes/class-wpvivid-zipclass.php';
class WPvivid_Backup_Site
{
    private $tools_collection=array();

    public function __construct()
    {
        add_filter('wpvivid_tools_register', array($this, 'init_tools'),10);
        $this->tools_collection=apply_filters('wpvivid_tools_register',$this->tools_collection);
        $this->load_hooks();
    }

    public function init_tools($tools_collection){
        $tools_collection['zip'][WPVIVID_COMPRESS_ZIPCLASS] = 'WPvivid_ZipClass';
        return $tools_collection;
    }

    public function get_tools($type){
        if(array_key_exists($type,$this->tools_collection))
        {
            foreach ($this -> tools_collection[$type] as $class_name){
                if(class_exists($class_name)){
                    $object = new $class_name();
                    $last_error = $object -> getLastError();
                    if(empty($last_error))
                        return $object;
                }
            }
        }
        $class_name = $this -> tools_collection['zip'][WPVIVID_COMPRESS_ZIPCLASS];
        $object = new $class_name();
        $last_error = $object -> getLastError();
        if(empty($last_error)){
            return $object;
        }else{
            return array('result' => WPVIVID_FAILED,'error' => $last_error);
        }
    }

    public function load_hooks(){
        foreach ($this -> tools_collection as $compressType){
            foreach ($compressType as $className){
                $object = new $className();
            }
        }
    }
}includes/class-wpvivid-backuptoremote.php000064400000003616151327705670014723 0ustar00<?php

if (!defined('WPVIVID_PLUGIN_DIR')){
    die;
}

require_once WPVIVID_PLUGIN_DIR . '/includes/customclass/class-wpvivid-ftpclass.php';
require_once WPVIVID_PLUGIN_DIR . '/includes/customclass/class-wpvivid-sftpclass.php';
require_once WPVIVID_PLUGIN_DIR . '/includes/customclass/class-wpvivid-amazons3-plus.php';
class WPvivid_Backup_Remote
{

	public function backup_with_ftp($data = array())
    {
		$host = $data['options']['host'];
		$username = $data['options']['username'];
		$password = $data['options']['password'];
		$path = $data['options']['path'];
        $passive = $data['options']['passive'];
		$port = empty($data['options']['port'])?21:$data['options']['port'];

		$ftpclass = new WPvivid_FTPClass();
		$res = $ftpclass -> upload($host,$username,$password,$path,$data['files'],$data['task_id'],$passive,$port,$data['log']);
        return $res;
	}

	public function backup_with_sftp($data)
    {
	    if(empty($data['port']))
	        $data['options']['port'] = 22;
	    $host = $data['options']['host'];
	    $username = $data['options']['username'];
	    $password = $data['options']['password'];
	    $path = $data['options']['path'];
        $port = $data['options']['port'];
        $scp = $data['options']['scp'];

        $sftpclass = new WPvivid_SFTPClass();
		$result = $sftpclass -> upload($host,$username,$password,$path,$data['files'],$data['task_id'],$port,$scp,$data['log']);
        return $result;
	}

	public function backup_with_amazonS3($data = array())
    {
		$files = $data['files'];
		$access = $data['options']['access'];
		$secret = $data['options']['secret'];
		$s3Path = $data['options']['s3Path'];
		$region = $data['options']['region'];
		$amazonS3 = new WPvivid_AMAZONS3Class();
		$amazonS3 ->init($access,$secret,$region);
        $res =  $amazonS3 -> upload($files,$s3Path,$data['task_id'],$data['log']);
		return $res;
	}
}includes/class-wpvivid-mail-report.php000064400000200432151327705670014125 0ustar00<?php

if (!defined('WPVIVID_PLUGIN_DIR')){
    die;
}
class WPvivid_mail_report
{
    public static function send_report_mail($task,$log=false)
    {
        $option=WPvivid_Setting::get_option('wpvivid_email_setting');

        $option=apply_filters('wpvivid_get_mail_option_addon', $option);
        if(empty($option))
        {
            return true;
        }

        if($option['email_enable'] == 0){
            return true;
        }

        if(empty($option['send_to']))
        {
            return true;
        }

        if($task['status']['str']=='completed'&&$option['always']==false)
        {
            return true;
        }

        $headers = array('Content-Type: text/html; charset=UTF-8');

        $subject = '';
        $subject = apply_filters('wpvivid_set_mail_subject', $subject, $task);

        $body = '';
        $body = apply_filters('wpvivid_set_mail_body', $body, $task);

        $task_log=$task['options']['log_file_name'];

        if(isset($option['email_attach_log'])){
            if($option['email_attach_log'] == '1'){
                $attach_log = true;
            }
            else{
                $attach_log = false;
            }
        }
        else{
            $attach_log = true;
        }
        if($attach_log){
            $wpvivid_log=new WPvivid_Log();
            $log_file_name= $wpvivid_log->GetSaveLogFolder().$task_log.'_log.txt';
            $attachments[] = $log_file_name;
        }
        else{
            $attachments = array();
        }

        foreach ($option['send_to'] as $send_to)
        {
            if(wp_mail( $send_to, $subject, $body,$headers,$attachments)===false)
            {
                if($log!==false)
                {
                    $message=get_error_messages('wp_mail_failed');
                    $log->WriteLog($message,'error');
                }
            }
        }

        return true;
    }

    public static function send_report_mail_ex($task_id,$log=false)
    {
        $task= new WPvivid_Backup_Task_2($task_id);

        $option=WPvivid_Setting::get_option('wpvivid_email_setting');

        $option=apply_filters('wpvivid_get_mail_option_addon', $option);
        if(empty($option))
        {
            return true;
        }

        if($option['email_enable'] == 0){
            return true;
        }

        if(empty($option['send_to']))
        {
            return true;
        }

        $status=$task->get_status();
        if($status['str']!=='error'&&$option['always']==false)
        {
            return true;
        }


        $headers = array('Content-Type: text/html; charset=UTF-8');

        //$subject = '';
        //$subject = apply_filters('wpvivid_set_mail_subject', $subject, $task);
        $subject =self::set_mail_subject($task_id);

        //$body = '';
        //$body = apply_filters('wpvivid_set_mail_body', $body, $task);
        $body = self::set_mail_body($task_id);

        $task_log=$task->task['options']['log_file_name'];

        if(isset($option['email_attach_log'])){
            if($option['email_attach_log'] == '1'){
                $attach_log = true;
            }
            else{
                $attach_log = false;
            }
        }
        else{
            $attach_log = true;
        }
        if($attach_log){
            $wpvivid_log=new WPvivid_Log();
            if($status['str']==='error')
            {
                $log_file_name= $wpvivid_log->GetSaveLogFolder().'error'.DIRECTORY_SEPARATOR.$task_log.'_log.txt';
            }
            else
            {
                $log_file_name= $wpvivid_log->GetSaveLogFolder().$task_log.'_log.txt';
            }
            $attachments[] = $log_file_name;
        }
        else{
            $attachments = array();
        }

        foreach ($option['send_to'] as $send_to)
        {
            if(wp_mail( $send_to, $subject, $body,$headers,$attachments)===false)
            {
                if($log!==false)
                {
                    $message=get_error_messages('wp_mail_failed');
                    $log->WriteLog($message,'error');
                }
            }
        }

        return true;
    }

    public static function set_mail_subject($task_id)
    {
        $task= new WPvivid_Backup_Task_2($task_id);
        $task_status=$task->get_status();
        if($task_status['str']!=='error')
        {
            $status='Succeeded';
        }
        else
        {
            $status='Failed';
        }

        $offset=get_option('gmt_offset');
        $localtime=gmdate('m-d-Y H:i:s', $task->get_start_time()+$offset*60*60);
        $header='[Backup '.$status.']'.$localtime.' - By WPvivid Backup Plugin';
        return $header;
    }

    public static function set_mail_body($task_id)
    {
        $task= new WPvivid_Backup_Task_2($task_id);
        $task_status=$task->get_status();
        if($task_status['str']!=='error')
        {
            $status='Succeeded';
        }
        else
        {
            $status='Failed. '.$task_status['error'];
        }

        $type=$task->task['type'];
        if($type === 'Cron')
        {
            $type = 'Cron-Schedule';
        }
        $offset=get_option('gmt_offset');
        $start_time=gmdate("m-d-Y H:i:s",$task->get_start_time()+$offset*60*60);
        $end_time=gmdate("m-d-Y H:i:s",time()+$offset*60*60);
        $running_time=($task->get_end_time()-$task->get_start_time()).'s';
        $remote_options= $task->task['options']['remote_options'];
        if($remote_options!==false)
        {
            $remote_option=array_shift($remote_options);
            $remote=apply_filters('wpvivid_storage_provider_tran', $remote_option['type']);
        }
        else
        {
            $remote='Localhost';
        }
        $content='';

        $backup_content=isset($task->task['options']['backup_files'])?$task->task['options']['backup_files']:'';
        if($backup_content==='files')
        {
            $content.='All Files (Exclude Database)';
        }
        else if($backup_content==='files+db')
        {
            $content.='Entire Website';
        }
        else if($backup_content==='db')
        {
            $content.='Database';
        }
        else
        {
            $content='Upload';
        }

        $body='
        <table width="100%" cellpadding="0" cellspacing="0" bgcolor="#F5F7F8">
            <tbody>
            <tr>
                <td style="padding-bottom:20px">
                <div style="max-width:600px;margin-top:0;margin-bottom:0;margin-right:auto;margin-left:auto;padding-left:20px;padding-right:20px">
                    <table align="center" style="border-spacing:0;color:#111111;Margin:0 auto;width:100%;max-width:600px" bgcolor="#F5F7F8">
                        <tbody>
				        <tr>
                            <td bgcolor="#F5F7F8" style="padding-top:0;padding-bottom:0;padding-right:0;padding-left:0">
                                <table width="73%" style="border-spacing:0;color:#111111" bgcolor="#F5F7F8">
                                    <tbody>
			                        <tr>
                                        <td style="padding-top:20px;padding-bottom:0px;padding-left:10px;padding-right:40px;width:100%;text-align:center;font-size:32px;color:#2ea3f2;line-height:32px;font-weight:bold;">
                                            <span><img src="https://wpvivid.com/wp-content/uploads/2019/02/wpvivid-logo.png" title="WPvivid.com"></span>            
                                        </td>
                                    </tr>
                                    </tbody>
		                        </table>
                            </td>
                            <td width="100%" bgcolor="#F5F7F8" style="padding-top:0;padding-bottom:0;padding-right:0;padding-left:0">
                                <table width="100%" style="border-spacing:0;color:#111111" bgcolor="#F5F7F8">
                                    <tbody>
                                    <tr>
                                        <td style="padding-top:10px;padding-bottom:0px;padding-left:10px;padding-right:0px;background-color:#f5f7f8;width:100%;text-align:right">
                                            <p style="Margin-top:0px;margin-bottom:0px;font-size:13px;line-height:16px"><strong><a href="https://twitter.com/wpvividcom" style="text-decoration:none;color:#111111" target="_blank">24/7 Support: <u></u>Twitter<u></u></a></strong></p>
                                        </td>
                                    </tr>
                                    <tr>
                                        <td style="padding-top:0px;padding-bottom:0px;padding-left:10px;padding-right:0px;background-color:#f5f7f8;width:100%;text-align:right">
                                            <p class="m_764812426175198487customerinfo" style="Margin-top:5px;margin-bottom:0px;font-size:13px;line-height:16px">Or <u></u><a href="https://wpvivid.com/contact-us">Email Us</a><u></u></p>
                                        </td>
                                    </tr>
                                    </tbody>
                                </table>
                            </td>
                        </tr>
                        </tbody>
                    </table>
                </div>
                </td>
            </tr>
            </tbody>
        </table>
        
        <table width="100%" border="0" cellspacing="0" cellpadding="0" bgcolor="#F5F7F8">
            <tbody>
            <tr>
                <td bgcolor="#F5F7F8" style="padding-top:0px;padding-bottom:0px">
                    <div style="max-width:600px;margin-top:0;margin-bottom:0;margin-right:auto;margin-left:auto;padding-left:20px;padding-right:20px">
                        <table bgcolor="#FFFFFF" align="center" style="border-spacing:0;color:#111111;margin:0 auto;width:100%;max-width:600px">
                            <tbody>
                            <tr>
                                <td style="padding-top:0;padding-bottom:0;padding-right:0;padding-left:0">
                                    <table width="100%" style="border-spacing:0;color:#111111">
                                        <tbody>
                                        <tr>
                                            <td style="padding-top:40px;padding-bottom:0px;padding-left:40px;padding-right:40px;background-color:#ffffff;width:100%;text-align:center;font-size:32px;line-height:42px;font-weight:bold;">
                                                <span>Wordpress Backup Report</span>            
                                            </td>
                                        </tr>
                                        </tbody>
                                    </table>
                                </td>
                            </tr>
                            </tbody>
                        </table>            
                    </div>
                </td>
            </tr>
            </tbody>
        </table>
        
        <table width="100%" border="0" cellspacing="0" cellpadding="0" bgcolor="#F5F7F8">
            <tbody>
            <tr>
                <td bgcolor="#F5F7F8" style="padding-top:0px;padding-bottom:0px">
                    <div style="max-width:600px;margin-top:0;margin-bottom:0;margin-right:auto;margin-left:auto;padding-left:20px;padding-right:20px">
                        <table bgcolor="#FFFFFF" align="center" style="border-spacing:0;color:#111111;margin:0 auto;width:100%;max-width:600px">
                            <tbody>
                            <tr>
                                <td style="padding-top:0;padding-bottom:0;padding-right:0;padding-left:0">
                                    <table width="100%" style="border-spacing:0;color:#111111">
                                        <tbody>
                                        <tr>
                                            <td style="padding-top:20px;padding-bottom:0px;padding-left:0px;padding-right:0px;background-color:#ffffff;width:100%;text-align:left">
                                                <p style="margin-top:0px;line-height:0px;margin-bottom:0px;font-size:4px"> </p>
                                            </td>
                                        </tr>
                                        </tbody>
                                    </table>
                                </td>
                                <td width="80" style="padding-top:0;padding-bottom:0;padding-right:0;padding-left:0">
                                    <table width="80" style="border-spacing:0;color:#111111;border-bottom-color:#ffcca8;border-bottom-width:2px;border-bottom-style:solid">
                                        <tbody>
                                        <tr>
                                            <td style="padding-top:20px;padding-bottom:0px;padding-left:0px;padding-right:0px;background-color:#ffffff;width:100%;text-align:left">
                                                <p style="margin-top:0px;line-height:0px;margin-bottom:0px;font-size:4px"></p>
                                            </td>
                                        </tr>
                                        </tbody>
                                    </table>
                                </td>
                                <td style="padding-top:0;padding-bottom:0;padding-right:0;padding-left:0">
                                    <table width="100%" style="border-spacing:0;color:#111111">
                                        <tbody>
                                        <tr>
                                            <td style="padding-top:20px;padding-bottom:0px;padding-left:0px;padding-right:0px;background-color:#ffffff;width:100%;text-align:left">
                                                <p style="margin-top:0px;line-height:0px;margin-bottom:0px;font-size:4px"> </p>
                                            </td>
                                        </tr>
                                        </tbody>
                                    </table>
                                </td>
                            </tr>
                            </tbody>
                        </table>
                    </div>
                </td>
            </tr>
            </tbody>
        </table>
        
        <table width="100%" border="0" cellspacing="0" cellpadding="0" bgcolor="#F5F7F8">
            <tbody>
            <tr>
                <td bgcolor="#F5F7F8" style="padding-top:0px;padding-bottom:0px">
                    <div style="max-width:600px;margin-top:0;margin-bottom:0;margin-right:auto;margin-left:auto;padding-left:20px;padding-right:20px">
                        <table bgcolor="#FFFFFF" align="center" style="border-spacing:0;color:#111111;margin:0 auto;width:100%;max-width:600px">
                            <tbody>
                            <tr>
                                <td style="padding-top:0;padding-bottom:0;padding-right:0;padding-left:0">
                                    <table width="100%" style="border-spacing:0;color:#111111">
                                        <tbody>
                                        <tr>
                                            <td style="padding-top:40px;padding-bottom:0px;padding-left:40px;padding-right:40px;background-color:#ffffff;width:100%;text-align:left">
                                                <p style="gdsherpa-regular;margin-top:0px;font-size:14px;line-height:24px;margin-bottom:0px">
                                                    You receive this email because you have enabled the email notification feature in WPvivid plugin. Backup Details:
                                                </p>
                                            </td>
                                        </tr>
                                        </tbody>
                                    </table>
                                </td>
                            </tr>
                            </tbody>
                        </table>   
                    </div>
                </td>
            </tr>
            </tbody>
        </table>
        
        <table width="100%" border="0" cellspacing="0" cellpadding="0" bgcolor="#F5F7F8">
            <tbody>
            <tr>
                <td bgcolor="#F5F7F8" style="background-color:#f5f7f8;padding-top:0;padding-right:0;padding-left:0;padding-bottom:0">
                    <div style="max-width:600px;margin-top:0;margin-bottom:0;margin-right:auto;margin-left:auto;padding-left:20px;padding-right:20px">		
                        <table bgcolor="#ffffff" width="100%" align="center" border="0" cellspacing="0" cellpadding="0" style="color:#111111;max-width:600px">
                            <tbody>
                            <tr>
                                <td bgcolor="#ffffff" align="left" style="padding-top:10px;padding-bottom:0;padding-right:40px;padding-left:40px;background-color:#ffffff">
                                    <table border="0" cellpadding="0" cellspacing="0" align="left" width="100%">
                                        <tbody>
                                        <tr>
                                            <td style="padding-top:10px;padding-right:0;padding-bottom:0;padding-left:20px">
                                                <table border="0" cellpadding="0" cellspacing="0" align="left">
                                                    <tbody>
                                                    <tr>
                                                        <td valign="top" align="left" style="padding-top:0px;padding-right:0px;padding-bottom:0px;padding-left:0px">
                                                            <p style="text-align:left;Margin-top:0px;Margin-bottom:0px;gdsherpa-regular;font-size:14px;line-height:24px"><label>Backup: </label><label>'.$status.'</label></p>
                                                        </td>
                                                    </tr>
                                                    <tr>
                                                        <td valign="top" align="left" style="padding-top:0px;padding-right:0px;padding-bottom:0px;padding-left:0px">
                                                            <p style="text-align:left;Margin-top:0px;Margin-bottom:0px;gdsherpa-regular;font-size:14px;line-height:24px"><label>Backup Type: </label><label>'.$type.'</label></p>
                                                        </td>
                                                    </tr>
                                                    <tr>
                                                        <td valign="top" align="left" style="padding-top:0px;padding-right:0px;padding-bottom:0px;padding-left:0px">
                                                            <p style="text-align:left;Margin-top:0px;Margin-bottom:0px;gdsherpa-regular;font-size:14px;line-height:24px"><label>Start Time: </label><label>'.$start_time.'</label></p>
                                                        </td>
                                                    </tr>
                                                    <tr>
                                                        <td valign="top" align="left" style="padding-top:0px;padding-right:0px;padding-bottom:0px;padding-left:0px">
                                                            <p style="text-align:left;Margin-top:0px;Margin-bottom:0px;gdsherpa-regular;font-size:14px;line-height:24px"><label>End Time: </label><label>'.$end_time.'</label></p>
                                                        </td>
                                                    </tr>
                                                    <tr>
                                                        <td valign="top" align="left" style="padding-top:0px;padding-right:0px;padding-bottom:0px;padding-left:0px">
                                                            <p style="text-align:left;Margin-top:0px;Margin-bottom:0px;gdsherpa-regular;font-size:14px;line-height:24px"><label>Running Time: </label><label>'.$running_time.'</label></p>
                                                        </td>
                                                    </tr>
                                                    <tr>
                                                        <td valign="top" align="left" style="padding-top:0px;padding-right:0px;padding-bottom:0px;padding-left:0px">
                                                            <p style="text-align:left;Margin-top:0px;Margin-bottom:0px;gdsherpa-regular;font-size:14px;line-height:24px"><label>Backed up to: </label><label>'.$remote.'</label></p>
                                                        </td>
                                                    </tr>
                                                    <tr>
                                                        <td valign="top" align="left" style="padding-top:0px;padding-right:0px;padding-bottom:0px;padding-left:0px">
                                                            <p style="text-align:left;Margin-top:0px;Margin-bottom:0px;gdsherpa-regular;font-size:14px;line-height:24px"><label>Backup Content: </label><label>'.$content.'</label></p>
                                                        </td>
                                                    </tr>
                                                    </tbody>
                                                </table>
                                            </td>
                                        </tr>
                                        </tbody>
                                    </table>                     
                                </td>
                            </tr>
                            </tbody>
                        </table>
                    </div>
                </td>
            </tr>
            </tbody>
        </table>     
          
        <table width="100%" border="0" cellspacing="0" cellpadding="0" bgcolor="#F5F7F8">
            <tbody>
            <tr>
                <td bgcolor="#F5F7F8" style="padding-top:0px;padding-bottom:0px">
                    <div style="max-width:600px;margin-top:0;margin-bottom:0;margin-right:auto;margin-left:auto;padding-left:20px;padding-right:20px">             
                        <table bgcolor="#FFFFFF" align="center" style="border-spacing:0;color:#111111;margin:0 auto;width:100%;max-width:600px">
                            <tbody>
                            <tr>
                                <td style="padding-top:0;padding-bottom:0;padding-right:0;padding-left:0">
                                    <table width="100%" style="border-spacing:0;color:#757575">
                                        <tbody>
                                        <tr>
                                            <td style="padding-top:20px;padding-bottom:0px;padding-left:40px;padding-right:40px;background-color:#ffffff;width:100%;text-align:left">
                                                <p style="gdsherpa-regular;margin-top:0px;font-size:14px;line-height:24px;margin-bottom:0px">
                                                    *WPvivid Backup plugin is a Wordpress plugin that it will help you back up your site to the leading cloud storage providers like Dropbox, Google Drive, Amazon S3, Microsoft OneDrive, FTP and SFTP.
                                                </p>
                                                <p style="gdsherpa-regular;margin-top:0px;font-size:14px;line-height:24px;margin-bottom:0px">
                                                    Plugin Page: <a href="https://wordpress.org/plugins/wpvivid-backuprestore/">https://wordpress.org/plugins/wpvivid-backuprestore/</a>
                                                </p>
                                            </td>
                                        </tr>
                                        </tbody>
                                    </table>
                                </td>
                            </tr>
                            </tbody>
                        </table>     
                    </div>
                </td>
            </tr>
            </tbody>
        </table>
        
        <table width="100%" border="0" cellspacing="0" cellpadding="0" bgcolor="#F5F7F8">
            <tbody>
            <tr>
                <td bgcolor="#F5F7F8" style="padding-top:0px;padding-bottom:0px">
                    <div style="max-width:600px;margin-top:0;margin-bottom:0;margin-right:auto;margin-left:auto;padding-left:20px;padding-right:20px">
                        <table bgcolor="#FFFFFF" align="center" style="border-spacing:0;color:#111111;margin:0 auto;width:100%;max-width:600px">
                            <tbody>
                                <tr>
                                    <td style="padding-top:0;padding-bottom:0;padding-right:0;padding-left:0">
                                        <table width="100%" style="border-spacing:0;color:#111111">
                                            <tbody>
                                            <tr>
                                                <td style="padding-top:40px;padding-bottom:0px;padding-left:40px;padding-right:40px;background-color:#ffffff;width:100%;text-align:left">
                                                    <p style="margin-top:0px;line-height:0px;margin-bottom:0px;font-size:4px"></p>
                                                </td>
                                            </tr>
                                            </tbody>
                                        </table>
                                    </td>
                                </tr>
                            </tbody>
                        </table>
                    </div>
                </td>
            </tr>
            </tbody>
        </table>
        
        <table width="100%" border="0" cellspacing="0" cellpadding="0" bgcolor="#F5F7F8">
            <tbody>
                <tr>
                    <td bgcolor="#F5F7F8" style="background-color:#f5f7f8;padding-top:0;padding-right:0;padding-left:0;padding-bottom:0">
                        <div style="max-width:600px;margin-top:0;margin-bottom:0;margin-right:auto;margin-left:auto;padding-left:20px;padding-right:20px">
                            <table width="100%" align="center" border="0" cellspacing="0" cellpadding="0" style="color:#111111">
                                <tbody>
                                <tr>
                                    <td align="center" style="padding-top:40px;padding-bottom:0;padding-right:0px;padding-left:0px">
                                        <table border="0" cellpadding="0" cellspacing="0" width="100%">
                                            <tbody>
                                            <tr>
                                                <td align="left" valign="bottom">
                                                    <img src="https://wpvivid.com/wp-content/uploads/2019/03/report-background.png" width="270" height="60" style="display:block;width:100%;max-width:270px;min-width:10px;height:60px" class="CToWUd">
                                                </td>
                                                <td width="60" valign="bottom">
                                                    <img src="https://wpvivid.com/wp-content/uploads/2019/03/female.png" width="60" height="60" style="display:block" class="CToWUd">
                                                </td>
                                                <td align="right" valign="bottom">
                                                    <img src="https://wpvivid.com/wp-content/uploads/2019/03/report-background.png" width="270" height="60" style="display:block;width:100%;max-width:270px;min-width:10px;height:60px" class="CToWUd">
                                                </td>
                                            </tr>
                                            </tbody>
                                        </table>  
                                    </td>
                                </tr>
                                </tbody>
                            </table>
                            <table bgcolor="#FFFFFF" width="100%" align="left" border="0" cellspacing="0" cellpadding="0" style="color:#111111">
                                <tbody>
                                <tr>
                                    <td bgcolor="#FFFFFF" align="left" style="padding-top:20px;padding-bottom:40px;padding-right:40px;padding-left:40px;background-color:#ffffff">     
                                        <table border="0" cellpadding="0" cellspacing="0" width="100%" align="center">
                                            <tbody>
                                            <tr>
                                                <td align="center" style="padding-top:0px;padding-bottom:10px;padding-right:0;padding-left:0;text-align:center;font-size:18px;line-height:28px;font-weight:bold;">
                                                    <span>We\'re here to help you do your thing.</span>
                                                </td>
                                            </tr>
                                            <tr>
                                                <td align="center" style="padding-top:0px;padding-bottom:0px;padding-right:0;padding-left:0;text-align:center">
                                                    <p style="text-align:center;margin-top:0px;margin-bottom:0px;gdsherpa-regular;;font-size:14px;line-height:24px">
                                                        <a href="https://wpvivid.com/contact-us">Contact Us</a> or <a href="https://twitter.com/wpvividcom">Twitter</a>
                                                    </p>
                                                </td>
                                            </tr>
                                            </tbody>
                                        </table>        
                                    </td>
                                </tr>
                                </tbody>
                            </table>
                            <table border="0" cellpadding="0" cellspacing="0" width="100%">
                                <tbody>
                                    <tr>
                                        <td valign="top" style="font-size:0px;line-height:0px;padding-top:0px;padding-right:0px;padding-bottom:0px;padding-left:0px">
                                            <img src="https://wpvivid.com/wp-content/uploads/2019/03/unnamed6.jpg" width="600" height="5" style="display:block;width:100%;max-width:600px;min-width:10px;height:5px">
                                        </td>
                                    </tr>
                                </tbody>
                            </table>        
                        </div>
                    </td>
                </tr>
            </tbody>
        </table>
        
        <table width="100%" border="0" cellspacing="0" cellpadding="0" bgcolor="#F5F7F8">
            <tbody>
            <tr>
                <td bgcolor="#F5F7F8" style="padding-top:0px;padding-bottom:0px">
                    <div style="max-width:600px;margin-top:0;margin-bottom:0;margin-right:auto;margin-left:auto;padding-left:20px;padding-right:20px">
                        <table bgcolor="#F5F7F8" align="center" style="border-spacing:0;color:#111111;margin:0 auto;width:100%;max-width:600px">
                            <tbody>
                            <tr>
                                <td style="padding-top:0;padding-bottom:0;padding-right:0;padding-left:0">
                                    <table width="100%" style="border-spacing:0;color:#111111">
                                        <tbody>
                                        <tr>
                                            <td style="padding-top:40px;padding-bottom:0px;padding-left:40px;padding-right:40px;background-color:#f5f7f8;width:100%;text-align:left">
                                                <p style="margin-top:0px;line-height:0px;margin-bottom:0px;font-size:4px">&nbsp;</p>
                                            </td>
                                        </tr>
                                        </tbody>
                                    </table>
                                </td>
                            </tr>
                            </tbody>
                        </table>   
                    </div>
                </td>
            </tr>
            </tbody>
        </table>';
        return $body;
    }

    public static function create_subject($task)
    {
        $status=$task['status']['str'];
        if($status=='completed')
        {
            $status='Succeeded';
        }
        else
        {
            $status='Failed';
        }

        $offset=get_option('gmt_offset');
        $localtime=gmdate('m-d-Y H:i:s', $task['status']['start_time']+$offset*60*60);
        $header='[Backup '.$status.']'.$localtime.' - By WPvivid Backup Plugin';
        return $header;
    }

    public static function create_body($task)
    {
        $status=$task['status']['str'];
        if($status=='completed')
        {
            $status='Succeeded';
        }
        else
        {
            $status='Failed. '.$task['status']['error'];
        }
        $type=$task['type'];
        if($type === 'Cron')
        {
            $type = 'Cron-Schedule';
        }
        $offset=get_option('gmt_offset');
        $start_time=gmdate("m-d-Y H:i:s",$task['status']['start_time']+$offset*60*60);
        $end_time=gmdate("m-d-Y H:i:s",time()+$offset*60*60);
        $running_time=($task['status']['run_time']-$task['status']['start_time']).'s';
        $remote_options= $task['options']['remote_options'];
        if($remote_options!==false)
        {
            $remote_option=array_shift($remote_options);
            $remote=apply_filters('wpvivid_storage_provider_tran', $remote_option['type']);
        }
        else
        {
            $remote='Localhost';
        }
        $content='';

        $backup_options=$task['options']['backup_options'];
        if($backup_options!==false)
        {
            if(isset($backup_options['backup'][WPVIVID_BACKUP_TYPE_DB])&&isset($backup_options['backup'][WPVIVID_BACKUP_TYPE_THEMES]))
            {
                $content.='Entire Website';
            }
            else if(isset($backup_options['backup'][WPVIVID_BACKUP_TYPE_DB]))
            {
                $content.='Database';
            }
            else if(isset($backup_options['backup'][WPVIVID_BACKUP_TYPE_THEMES]))
            {
                $content.='All Files (Exclude Database)';
            }
        }
        else
        {
            $content='Upload';
        }

        $body='
        <table width="100%" cellpadding="0" cellspacing="0" bgcolor="#F5F7F8">
            <tbody>
            <tr>
                <td style="padding-bottom:20px">
                <div style="max-width:600px;margin-top:0;margin-bottom:0;margin-right:auto;margin-left:auto;padding-left:20px;padding-right:20px">
                    <table align="center" style="border-spacing:0;color:#111111;Margin:0 auto;width:100%;max-width:600px" bgcolor="#F5F7F8">
                        <tbody>
				        <tr>
                            <td bgcolor="#F5F7F8" style="padding-top:0;padding-bottom:0;padding-right:0;padding-left:0">
                                <table width="73%" style="border-spacing:0;color:#111111" bgcolor="#F5F7F8">
                                    <tbody>
			                        <tr>
                                        <td style="padding-top:20px;padding-bottom:0px;padding-left:10px;padding-right:40px;width:100%;text-align:center;font-size:32px;color:#2ea3f2;line-height:32px;font-weight:bold;">
                                            <span><img src="https://wpvivid.com/wp-content/uploads/2019/02/wpvivid-logo.png" title="WPvivid.com"></span>            
                                        </td>
                                    </tr>
                                    </tbody>
		                        </table>
                            </td>
                            <td width="100%" bgcolor="#F5F7F8" style="padding-top:0;padding-bottom:0;padding-right:0;padding-left:0">
                                <table width="100%" style="border-spacing:0;color:#111111" bgcolor="#F5F7F8">
                                    <tbody>
                                    <tr>
                                        <td style="padding-top:10px;padding-bottom:0px;padding-left:10px;padding-right:0px;background-color:#f5f7f8;width:100%;text-align:right">
                                            <p style="Margin-top:0px;margin-bottom:0px;font-size:13px;line-height:16px"><strong><a href="https://twitter.com/wpvividcom" style="text-decoration:none;color:#111111" target="_blank">24/7 Support: <u></u>Twitter<u></u></a></strong></p>
                                        </td>
                                    </tr>
                                    <tr>
                                        <td style="padding-top:0px;padding-bottom:0px;padding-left:10px;padding-right:0px;background-color:#f5f7f8;width:100%;text-align:right">
                                            <p class="m_764812426175198487customerinfo" style="Margin-top:5px;margin-bottom:0px;font-size:13px;line-height:16px">Or <u></u><a href="https://wpvivid.com/contact-us">Email Us</a><u></u></p>
                                        </td>
                                    </tr>
                                    </tbody>
                                </table>
                            </td>
                        </tr>
                        </tbody>
                    </table>
                </div>
                </td>
            </tr>
            </tbody>
        </table>
        
        <table width="100%" border="0" cellspacing="0" cellpadding="0" bgcolor="#F5F7F8">
            <tbody>
            <tr>
                <td bgcolor="#F5F7F8" style="padding-top:0px;padding-bottom:0px">
                    <div style="max-width:600px;margin-top:0;margin-bottom:0;margin-right:auto;margin-left:auto;padding-left:20px;padding-right:20px">
                        <table bgcolor="#FFFFFF" align="center" style="border-spacing:0;color:#111111;margin:0 auto;width:100%;max-width:600px">
                            <tbody>
                            <tr>
                                <td style="padding-top:0;padding-bottom:0;padding-right:0;padding-left:0">
                                    <table width="100%" style="border-spacing:0;color:#111111">
                                        <tbody>
                                        <tr>
                                            <td style="padding-top:40px;padding-bottom:0px;padding-left:40px;padding-right:40px;background-color:#ffffff;width:100%;text-align:center;font-size:32px;line-height:42px;font-weight:bold;">
                                                <span>Wordpress Backup Report</span>            
                                            </td>
                                        </tr>
                                        </tbody>
                                    </table>
                                </td>
                            </tr>
                            </tbody>
                        </table>            
                    </div>
                </td>
            </tr>
            </tbody>
        </table>
        
        <table width="100%" border="0" cellspacing="0" cellpadding="0" bgcolor="#F5F7F8">
            <tbody>
            <tr>
                <td bgcolor="#F5F7F8" style="padding-top:0px;padding-bottom:0px">
                    <div style="max-width:600px;margin-top:0;margin-bottom:0;margin-right:auto;margin-left:auto;padding-left:20px;padding-right:20px">
                        <table bgcolor="#FFFFFF" align="center" style="border-spacing:0;color:#111111;margin:0 auto;width:100%;max-width:600px">
                            <tbody>
                            <tr>
                                <td style="padding-top:0;padding-bottom:0;padding-right:0;padding-left:0">
                                    <table width="100%" style="border-spacing:0;color:#111111">
                                        <tbody>
                                        <tr>
                                            <td style="padding-top:20px;padding-bottom:0px;padding-left:0px;padding-right:0px;background-color:#ffffff;width:100%;text-align:left">
                                                <p style="margin-top:0px;line-height:0px;margin-bottom:0px;font-size:4px"> </p>
                                            </td>
                                        </tr>
                                        </tbody>
                                    </table>
                                </td>
                                <td width="80" style="padding-top:0;padding-bottom:0;padding-right:0;padding-left:0">
                                    <table width="80" style="border-spacing:0;color:#111111;border-bottom-color:#ffcca8;border-bottom-width:2px;border-bottom-style:solid">
                                        <tbody>
                                        <tr>
                                            <td style="padding-top:20px;padding-bottom:0px;padding-left:0px;padding-right:0px;background-color:#ffffff;width:100%;text-align:left">
                                                <p style="margin-top:0px;line-height:0px;margin-bottom:0px;font-size:4px"></p>
                                            </td>
                                        </tr>
                                        </tbody>
                                    </table>
                                </td>
                                <td style="padding-top:0;padding-bottom:0;padding-right:0;padding-left:0">
                                    <table width="100%" style="border-spacing:0;color:#111111">
                                        <tbody>
                                        <tr>
                                            <td style="padding-top:20px;padding-bottom:0px;padding-left:0px;padding-right:0px;background-color:#ffffff;width:100%;text-align:left">
                                                <p style="margin-top:0px;line-height:0px;margin-bottom:0px;font-size:4px"> </p>
                                            </td>
                                        </tr>
                                        </tbody>
                                    </table>
                                </td>
                            </tr>
                            </tbody>
                        </table>
                    </div>
                </td>
            </tr>
            </tbody>
        </table>
        
        <table width="100%" border="0" cellspacing="0" cellpadding="0" bgcolor="#F5F7F8">
            <tbody>
            <tr>
                <td bgcolor="#F5F7F8" style="padding-top:0px;padding-bottom:0px">
                    <div style="max-width:600px;margin-top:0;margin-bottom:0;margin-right:auto;margin-left:auto;padding-left:20px;padding-right:20px">
                        <table bgcolor="#FFFFFF" align="center" style="border-spacing:0;color:#111111;margin:0 auto;width:100%;max-width:600px">
                            <tbody>
                            <tr>
                                <td style="padding-top:0;padding-bottom:0;padding-right:0;padding-left:0">
                                    <table width="100%" style="border-spacing:0;color:#111111">
                                        <tbody>
                                        <tr>
                                            <td style="padding-top:40px;padding-bottom:0px;padding-left:40px;padding-right:40px;background-color:#ffffff;width:100%;text-align:left">
                                                <p style="gdsherpa-regular;margin-top:0px;font-size:14px;line-height:24px;margin-bottom:0px">
                                                    You receive this email because you have enabled the email notification feature in WPvivid plugin. Backup Details:
                                                </p>
                                            </td>
                                        </tr>
                                        </tbody>
                                    </table>
                                </td>
                            </tr>
                            </tbody>
                        </table>   
                    </div>
                </td>
            </tr>
            </tbody>
        </table>
        
        <table width="100%" border="0" cellspacing="0" cellpadding="0" bgcolor="#F5F7F8">
            <tbody>
            <tr>
                <td bgcolor="#F5F7F8" style="background-color:#f5f7f8;padding-top:0;padding-right:0;padding-left:0;padding-bottom:0">
                    <div style="max-width:600px;margin-top:0;margin-bottom:0;margin-right:auto;margin-left:auto;padding-left:20px;padding-right:20px">		
                        <table bgcolor="#ffffff" width="100%" align="center" border="0" cellspacing="0" cellpadding="0" style="color:#111111;max-width:600px">
                            <tbody>
                            <tr>
                                <td bgcolor="#ffffff" align="left" style="padding-top:10px;padding-bottom:0;padding-right:40px;padding-left:40px;background-color:#ffffff">
                                    <table border="0" cellpadding="0" cellspacing="0" align="left" width="100%">
                                        <tbody>
                                        <tr>
                                            <td style="padding-top:10px;padding-right:0;padding-bottom:0;padding-left:20px">
                                                <table border="0" cellpadding="0" cellspacing="0" align="left">
                                                    <tbody>
                                                    <tr>
                                                        <td valign="top" align="left" style="padding-top:0px;padding-right:0px;padding-bottom:0px;padding-left:0px">
                                                            <p style="text-align:left;Margin-top:0px;Margin-bottom:0px;gdsherpa-regular;font-size:14px;line-height:24px"><label>Backup: </label><label>'.$status.'</label></p>
                                                        </td>
                                                    </tr>
                                                    <tr>
                                                        <td valign="top" align="left" style="padding-top:0px;padding-right:0px;padding-bottom:0px;padding-left:0px">
                                                            <p style="text-align:left;Margin-top:0px;Margin-bottom:0px;gdsherpa-regular;font-size:14px;line-height:24px"><label>Backup Type: </label><label>'.$type.'</label></p>
                                                        </td>
                                                    </tr>
                                                    <tr>
                                                        <td valign="top" align="left" style="padding-top:0px;padding-right:0px;padding-bottom:0px;padding-left:0px">
                                                            <p style="text-align:left;Margin-top:0px;Margin-bottom:0px;gdsherpa-regular;font-size:14px;line-height:24px"><label>Start Time: </label><label>'.$start_time.'</label></p>
                                                        </td>
                                                    </tr>
                                                    <tr>
                                                        <td valign="top" align="left" style="padding-top:0px;padding-right:0px;padding-bottom:0px;padding-left:0px">
                                                            <p style="text-align:left;Margin-top:0px;Margin-bottom:0px;gdsherpa-regular;font-size:14px;line-height:24px"><label>End Time: </label><label>'.$end_time.'</label></p>
                                                        </td>
                                                    </tr>
                                                    <tr>
                                                        <td valign="top" align="left" style="padding-top:0px;padding-right:0px;padding-bottom:0px;padding-left:0px">
                                                            <p style="text-align:left;Margin-top:0px;Margin-bottom:0px;gdsherpa-regular;font-size:14px;line-height:24px"><label>Running Time: </label><label>'.$running_time.'</label></p>
                                                        </td>
                                                    </tr>
                                                    <tr>
                                                        <td valign="top" align="left" style="padding-top:0px;padding-right:0px;padding-bottom:0px;padding-left:0px">
                                                            <p style="text-align:left;Margin-top:0px;Margin-bottom:0px;gdsherpa-regular;font-size:14px;line-height:24px"><label>Backed up to: </label><label>'.$remote.'</label></p>
                                                        </td>
                                                    </tr>
                                                    <tr>
                                                        <td valign="top" align="left" style="padding-top:0px;padding-right:0px;padding-bottom:0px;padding-left:0px">
                                                            <p style="text-align:left;Margin-top:0px;Margin-bottom:0px;gdsherpa-regular;font-size:14px;line-height:24px"><label>Backup Content: </label><label>'.$content.'</label></p>
                                                        </td>
                                                    </tr>
                                                    </tbody>
                                                </table>
                                            </td>
                                        </tr>
                                        </tbody>
                                    </table>                     
                                </td>
                            </tr>
                            </tbody>
                        </table>
                    </div>
                </td>
            </tr>
            </tbody>
        </table>     
          
        <table width="100%" border="0" cellspacing="0" cellpadding="0" bgcolor="#F5F7F8">
            <tbody>
            <tr>
                <td bgcolor="#F5F7F8" style="padding-top:0px;padding-bottom:0px">
                    <div style="max-width:600px;margin-top:0;margin-bottom:0;margin-right:auto;margin-left:auto;padding-left:20px;padding-right:20px">             
                        <table bgcolor="#FFFFFF" align="center" style="border-spacing:0;color:#111111;margin:0 auto;width:100%;max-width:600px">
                            <tbody>
                            <tr>
                                <td style="padding-top:0;padding-bottom:0;padding-right:0;padding-left:0">
                                    <table width="100%" style="border-spacing:0;color:#757575">
                                        <tbody>
                                        <tr>
                                            <td style="padding-top:20px;padding-bottom:0px;padding-left:40px;padding-right:40px;background-color:#ffffff;width:100%;text-align:left">
                                                <p style="gdsherpa-regular;margin-top:0px;font-size:14px;line-height:24px;margin-bottom:0px">
                                                    *WPvivid Backup plugin is a Wordpress plugin that it will help you back up your site to the leading cloud storage providers like Dropbox, Google Drive, Amazon S3, Microsoft OneDrive, FTP and SFTP.
                                                </p>
                                                <p style="gdsherpa-regular;margin-top:0px;font-size:14px;line-height:24px;margin-bottom:0px">
                                                    Plugin Page: <a href="https://wordpress.org/plugins/wpvivid-backuprestore/">https://wordpress.org/plugins/wpvivid-backuprestore/</a>
                                                </p>
                                            </td>
                                        </tr>
                                        </tbody>
                                    </table>
                                </td>
                            </tr>
                            </tbody>
                        </table>     
                    </div>
                </td>
            </tr>
            </tbody>
        </table>
        
        <table width="100%" border="0" cellspacing="0" cellpadding="0" bgcolor="#F5F7F8">
            <tbody>
            <tr>
                <td bgcolor="#F5F7F8" style="padding-top:0px;padding-bottom:0px">
                    <div style="max-width:600px;margin-top:0;margin-bottom:0;margin-right:auto;margin-left:auto;padding-left:20px;padding-right:20px">
                        <table bgcolor="#FFFFFF" align="center" style="border-spacing:0;color:#111111;margin:0 auto;width:100%;max-width:600px">
                            <tbody>
                                <tr>
                                    <td style="padding-top:0;padding-bottom:0;padding-right:0;padding-left:0">
                                        <table width="100%" style="border-spacing:0;color:#111111">
                                            <tbody>
                                            <tr>
                                                <td style="padding-top:40px;padding-bottom:0px;padding-left:40px;padding-right:40px;background-color:#ffffff;width:100%;text-align:left">
                                                    <p style="margin-top:0px;line-height:0px;margin-bottom:0px;font-size:4px"></p>
                                                </td>
                                            </tr>
                                            </tbody>
                                        </table>
                                    </td>
                                </tr>
                            </tbody>
                        </table>
                    </div>
                </td>
            </tr>
            </tbody>
        </table>
        
        <table width="100%" border="0" cellspacing="0" cellpadding="0" bgcolor="#F5F7F8">
            <tbody>
                <tr>
                    <td bgcolor="#F5F7F8" style="background-color:#f5f7f8;padding-top:0;padding-right:0;padding-left:0;padding-bottom:0">
                        <div style="max-width:600px;margin-top:0;margin-bottom:0;margin-right:auto;margin-left:auto;padding-left:20px;padding-right:20px">
                            <table width="100%" align="center" border="0" cellspacing="0" cellpadding="0" style="color:#111111">
                                <tbody>
                                <tr>
                                    <td align="center" style="padding-top:40px;padding-bottom:0;padding-right:0px;padding-left:0px">
                                        <table border="0" cellpadding="0" cellspacing="0" width="100%">
                                            <tbody>
                                            <tr>
                                                <td align="left" valign="bottom">
                                                    <img src="https://wpvivid.com/wp-content/uploads/2019/03/report-background.png" width="270" height="60" style="display:block;width:100%;max-width:270px;min-width:10px;height:60px" class="CToWUd">
                                                </td>
                                                <td width="60" valign="bottom">
                                                    <img src="https://wpvivid.com/wp-content/uploads/2019/03/female.png" width="60" height="60" style="display:block" class="CToWUd">
                                                </td>
                                                <td align="right" valign="bottom">
                                                    <img src="https://wpvivid.com/wp-content/uploads/2019/03/report-background.png" width="270" height="60" style="display:block;width:100%;max-width:270px;min-width:10px;height:60px" class="CToWUd">
                                                </td>
                                            </tr>
                                            </tbody>
                                        </table>  
                                    </td>
                                </tr>
                                </tbody>
                            </table>
                            <table bgcolor="#FFFFFF" width="100%" align="left" border="0" cellspacing="0" cellpadding="0" style="color:#111111">
                                <tbody>
                                <tr>
                                    <td bgcolor="#FFFFFF" align="left" style="padding-top:20px;padding-bottom:40px;padding-right:40px;padding-left:40px;background-color:#ffffff">     
                                        <table border="0" cellpadding="0" cellspacing="0" width="100%" align="center">
                                            <tbody>
                                            <tr>
                                                <td align="center" style="padding-top:0px;padding-bottom:10px;padding-right:0;padding-left:0;text-align:center;font-size:18px;line-height:28px;font-weight:bold;">
                                                    <span>We\'re here to help you do your thing.</span>
                                                </td>
                                            </tr>
                                            <tr>
                                                <td align="center" style="padding-top:0px;padding-bottom:0px;padding-right:0;padding-left:0;text-align:center">
                                                    <p style="text-align:center;margin-top:0px;margin-bottom:0px;gdsherpa-regular;;font-size:14px;line-height:24px">
                                                        <a href="https://wpvivid.com/contact-us">Contact Us</a> or <a href="https://twitter.com/wpvividcom">Twitter</a>
                                                    </p>
                                                </td>
                                            </tr>
                                            </tbody>
                                        </table>        
                                    </td>
                                </tr>
                                </tbody>
                            </table>
                            <table border="0" cellpadding="0" cellspacing="0" width="100%">
                                <tbody>
                                    <tr>
                                        <td valign="top" style="font-size:0px;line-height:0px;padding-top:0px;padding-right:0px;padding-bottom:0px;padding-left:0px">
                                            <img src="https://wpvivid.com/wp-content/uploads/2019/03/unnamed6.jpg" width="600" height="5" style="display:block;width:100%;max-width:600px;min-width:10px;height:5px">
                                        </td>
                                    </tr>
                                </tbody>
                            </table>        
                        </div>
                    </td>
                </tr>
            </tbody>
        </table>
        
        <table width="100%" border="0" cellspacing="0" cellpadding="0" bgcolor="#F5F7F8">
            <tbody>
            <tr>
                <td bgcolor="#F5F7F8" style="padding-top:0px;padding-bottom:0px">
                    <div style="max-width:600px;margin-top:0;margin-bottom:0;margin-right:auto;margin-left:auto;padding-left:20px;padding-right:20px">
                        <table bgcolor="#F5F7F8" align="center" style="border-spacing:0;color:#111111;margin:0 auto;width:100%;max-width:600px">
                            <tbody>
                            <tr>
                                <td style="padding-top:0;padding-bottom:0;padding-right:0;padding-left:0">
                                    <table width="100%" style="border-spacing:0;color:#111111">
                                        <tbody>
                                        <tr>
                                            <td style="padding-top:40px;padding-bottom:0px;padding-left:40px;padding-right:40px;background-color:#f5f7f8;width:100%;text-align:left">
                                                <p style="margin-top:0px;line-height:0px;margin-bottom:0px;font-size:4px">&nbsp;</p>
                                            </td>
                                        </tr>
                                        </tbody>
                                    </table>
                                </td>
                            </tr>
                            </tbody>
                        </table>   
                    </div>
                </td>
            </tr>
            </tbody>
        </table>';
        return $body;
    }

    public static function wpvivid_send_debug_info($user_email,$server_type,$host_provider,$comment)
    {
        $send_to = 'support@wpvivid.com';
        $subject = 'Debug Information';
        $body = '<div>User\'s email: '.$user_email.'.</div>';
        $body .= '<div>Server type: '.$server_type.'.</div>';
        $body .= '<div>Host provider: '.$host_provider.'.</div>';
        $body .= '<div>Comment: '.$comment.'.</div>';
        $headers = array('Content-Type: text/html; charset=UTF-8');

        $files=WPvivid_error_log::get_error_log();

        if (!class_exists('WPvivid_PclZip'))
            include_once WPVIVID_PLUGIN_DIR . '/includes/zip/class-wpvivid-pclzip.php';

        $backup_path=WPvivid_Setting::get_backupdir();
        $path=WP_CONTENT_DIR.DIRECTORY_SEPARATOR.$backup_path.DIRECTORY_SEPARATOR.'wpvivid_debug.zip';

        if(file_exists($path))
        {
            @wp_delete_file( $path);
        }
        $archive = new WPvivid_PclZip($path);

        if(!empty($files))
        {
            if(!$archive->add($files,WPVIVID_PCLZIP_OPT_REMOVE_ALL_PATH))
            {
                echo esc_html($archive->errorInfo(true)).' <a href="'.esc_url(admin_url()).'admin.php?page=WPvivid">retry</a>.';
                exit;
            }
        }

        global $wpvivid_plugin;
        $server_info=wp_json_encode($wpvivid_plugin->get_website_info());
        $server_file_path=WP_CONTENT_DIR.DIRECTORY_SEPARATOR.$backup_path.DIRECTORY_SEPARATOR.'wpvivid_server_info.json';
        if(file_exists($server_file_path))
        {
            @wp_delete_file( $server_file_path);
        }
        $server_file = fopen($server_file_path, 'x');
        fclose($server_file);
        file_put_contents($server_file_path,$server_info);
        if(!$archive->add($server_file_path,WPVIVID_PCLZIP_OPT_REMOVE_ALL_PATH))
        {
            echo esc_html($archive->errorInfo(true)).' <a href="'.esc_url(admin_url()).'admin.php?page=WPvivid">retry</a>.';
            exit;
        }
        @wp_delete_file( $server_file_path);

        $attachments[] = $path;

        if(wp_mail( $send_to, $subject, $body,$headers,$attachments)===false)
        {
            $ret['result']='failed';
            $ret['error']=__('Unable to send email. Please check the configuration of email server.', 'wpvivid-backuprestore');
        }
        else
        {
            $ret['result']='success';
        }

        @wp_delete_file($path);
        return $ret;
    }
}includes/class-wpvivid-mysqldump.php000064400000127337151327705670013741 0ustar00<?php
/**
 * Mysqldump File Doc Comment
 *
 * PHP version 5
 *
 * @category Library
 * @package  Ifsnop\Mysqldump
 * @author   Michael J. Calkins <clouddueling@github.com>
 * @author   Diego Torres <ifsnop@github.com>
 * @license  http://www.gnu.org/copyleft/gpl.html GNU General Public License
 * @link     https://github.com/ifsnop/mysqldump-php
 *
 */



use Exception as Exception;


/**
 * Mysqldump Class Doc Comment
 *
 * @category Library
 * @package  Ifsnop\Mysqldump
 * @author   Michael J. Calkins <clouddueling@github.com>
 * @author   Diego Torres <ifsnop@github.com>
 * @license  http://www.gnu.org/copyleft/gpl.html GNU General Public License
 * @link     https://github.com/ifsnop/mysqldump-php
 *
 */
class WPvivid_Mysqldump
{

    // Same as mysqldump
    const MAXLINESIZE = 1000000;

    // Available compression methods as constants
    const GZIP = 'Gzip';
    const BZIP2 = 'Bzip2';
    const NONE = 'None';

    // Available connection strings
    const UTF8 = 'utf8';
    const UTF8MB4 = 'utf8mb4';

    /**
    * Database username
    * @var string
    */
    public $user;
    /**
    * Database password
    * @var string
    */
    public $pass;
    /**
    * Destination filename, defaults to stdout
    * @var string
    */
    public $fileName = 'php://output';

    // Internal stuff
    private $tables = array();
    private $views = array();
    private $triggers = array();
    private $procedures = array();
    private $events = array();
    //private $dbHandler = null;
    private $dbType;
    private $compressManager;
    private $typeAdapter;
    private $dumpSettings = array();
    private $version;
    private $tableColumnTypes = array();
    public $log=false;
    public $task_id='';
    /**
    * database name, parsed from dsn
    * @var string
    */
    private $dbName;
    /**
    * host name, parsed from dsn
    * @var string
    */
    private $host;
    /**
    * dsn string parsed as an array
    * @var array
    */
    private $dsnArray = array();

    public $last_query_string='';

    public function __construct(
        $host = '',
        $dbname='',
        $user = '',
        $pass = '',
        $is_additional_db = false,
        $dumpSettings = array()
    ) {
        $dumpSettingsDefault = array(
            'include-tables' => array(),
            'exclude-tables' => array(),
            'compress' => WPvivid_Mysqldump::NONE,
            'init_commands' => array(),
            'no-data' => array(),
            'reset-auto-increment' => false,
            'add-drop-database' => false,
            'add-drop-table' => false,
            'add-drop-trigger' => true,
            'add-locks' => true,
            'complete-insert' => false,
            'databases' => false,
            'default-character-set' => WPvivid_Mysqldump::UTF8,
            'disable-keys' => true,
            'extended-insert' => false,
            'events' => false,
            'hex-blob' => true, /* faster than escaped content */
            'net_buffer_length' => self::MAXLINESIZE,
            'no-autocommit' => false,
            'no-create-info' => false,
            'lock-tables' => false,
            'routines' => false,
            'single-transaction' => true,
            'skip-triggers' => false,
            'skip-tz-utc' => false,
            'skip-comments' => false,
            'skip-dump-date' => false,
            'where' => '',
            /* deprecated */
            'disable-foreign-keys-check' => true,
            'site_url'=>'',
            'home_url'=>'',
            'content_url'=>'',
            'prefix'=>''
        );

        if(defined('DB_CHARSET'))
        {
            $dumpSettingsDefault['default-character-set']=DB_CHARSET;
        }

        $this->dbType=$this->get_db_type($is_additional_db);
        $this->user = $user;
        $this->pass = $pass;
        $this->host=$host;
        $this->dbName=$dbname;
        $this->dumpSettings = self::array_replace_recursive($dumpSettingsDefault, $dumpSettings);

        $this->dumpSettings['init_commands'][] = "SET NAMES " . WPvivid_Mysqldump::UTF8MB4;

        if (false === $this->dumpSettings['skip-tz-utc'])
        {
            $this->dumpSettings['init_commands'][] = "SET TIME_ZONE='+00:00'";
        }

        $diff = array_diff(array_keys($this->dumpSettings), array_keys($dumpSettingsDefault));
        if (count($diff)>0) {
            throw new Exception("Unexpected value in dumpSettings: (" . esc_html(implode(",", $diff)) . ")");
        }

        if ( !is_array($this->dumpSettings['include-tables']) ||
            !is_array($this->dumpSettings['exclude-tables']) ) {
            throw new Exception("Include-tables and exclude-tables should be arrays");
        }

        // Dump the same views as tables, mimic mysqldump behaviour
        $this->dumpSettings['include-views'] = $this->dumpSettings['include-tables'];

        // Create a new compressManager to manage compressed output
        $this->compressManager = WPvividCompressManagerFactory::create($this->dumpSettings['compress']);
    }

    public function get_db_type($is_additional_db)
    {
        if($is_additional_db){
            return 'mysql';
        }
        else {
            $common_setting = WPvivid_Setting::get_setting(false, 'wpvivid_common_setting');
            $db_connect_method = isset($common_setting['options']['wpvivid_common_setting']['db_connect_method']) ? $common_setting['options']['wpvivid_common_setting']['db_connect_method'] : 'wpdb';
            if ($db_connect_method === 'wpdb') {
                return 'wpdb';
            } else {
                return 'mysql';
            }
        }
    }

    /**
     * Destructor of Mysqldump. Unsets dbHandlers and database objects.
     *
     */
    public function __destruct()
    {
        //$this->dbHandler = null;
    }

    /**
     * Custom array_replace_recursive to be used if PHP < 5.3
     * Replaces elements from passed arrays into the first array recursively
     *
     * @param array $array1 The array in which elements are replaced
     * @param array $array2 The array from which elements will be extracted
     *
     * @return array Returns an array, or NULL if an error occurs.
     */
    public static function array_replace_recursive($array1, $array2)
    {
        if (function_exists('array_replace_recursive')) {
            return array_replace_recursive($array1, $array2);
        }

        foreach ($array2 as $key => $value) {
            if (is_array($value)) {
                $array1[$key] = self::array_replace_recursive($array1[$key], $value);
            } else {
                $array1[$key] = $value;
            }
        }
        return $array1;
    }

    /**
     * Parse DSN string and extract dbname value
     * Several examples of a DSN string
     *   mysql:host=localhost;dbname=testdb
     *   mysql:host=localhost;port=3307;dbname=testdb
     *   mysql:unix_socket=/tmp/mysql.sock;dbname=testdb
     *
     * @param string $dsn dsn string to parse
     */
    private function parseDsn($dsn)
    {
        if (empty($dsn) || (false === ($pos = strpos($dsn, ":")))) {
            throw new Exception("Empty DSN string");
        }

        $this->dsn = $dsn;
        $this->dbType = strtolower(substr($dsn, 0, $pos));

        if (empty($this->dbType)) {
            throw new Exception("Missing database type from DSN string");
        }

        $dsn = substr($dsn, $pos + 1);

        foreach(explode(";", $dsn) as $kvp) {
            $kvpArr = explode("=", $kvp);
            $this->dsnArray[strtolower($kvpArr[0])] = $kvpArr[1];
        }

        if (empty($this->dsnArray['host']) &&
            empty($this->dsnArray['unix_socket'])) {
            throw new Exception("Missing host from DSN string");
        }
        $this->host = (!empty($this->dsnArray['host'])) ?
            $this->dsnArray['host'] :
            $this->dsnArray['unix_socket'];

        if (empty($this->dsnArray['dbname'])) {
            throw new Exception("Missing database name from DSN string");
        }

        $this->dbName = $this->dsnArray['dbname'];

        return true;
    }

    /**
     * Connect with PDO
     *
     * @return null
     */
    private function connect()
    {
        // Connecting with PDO
        /*
        try {
            switch ($this->dbType) {
                case 'sqlite':
                    $this->dbHandler = @new PDO("sqlite:" . $this->dbName, null, null, $this->pdoSettings);
                    break;
                case 'mysql':
                case 'pgsql':
                case 'dblib':
                    $this->dbHandler = @new PDO(
                        $this->dsn,
                        $this->user,
                        $this->pass,
                        $this->pdoSettings
                    );
                    // Execute init commands once connected
                    foreach($this->dumpSettings['init_commands'] as $stmt) {
                        $this->dbHandler->exec($stmt);
                    }
                    // Store server version
                    $this->version = $this->dbHandler->getAttribute(PDO::ATTR_SERVER_VERSION);
                    break;
                default:
                    throw new Exception("Unsupported database type (" . $this->dbType . ")");
            }
        } catch (PDOException $e) {
            throw new Exception(
                "Connection to " . $this->dbType . " failed with message: " .
                $e->getMessage()
            );
        }*/

        $this->typeAdapter = WPvividTypeAdapterFactory::create($this->dbType, null);
        $this->typeAdapter->connect($this->host,$this->dbName,$this->user,$this->pass,$this->dumpSettings['init_commands']);
    }

    /**
     * Main call
     *
     * @param string $filename  Name of file to write sql dump to
     * @return null
     */
    public function start($filename = '')
    {
        // Output file can be redefined here
        if (!empty($filename)) {
            $this->fileName = $filename;
        }

        // Connect to database
        $this->connect();

        // Create output file
        $this->compressManager->open($this->fileName);

        // Write some basic info to output file
        $this->compressManager->write($this->getDumpFileHeader());

        $this->compressManager->write('/* # site_url: '.$this->dumpSettings['site_url'].' */;'.PHP_EOL);
        $this->compressManager->write('/* # home_url: '.$this->dumpSettings['home_url'].' */;'.PHP_EOL);
        $this->compressManager->write('/* # content_url: '.$this->dumpSettings['content_url'].' */;'.PHP_EOL);
        $upload_dir  = wp_upload_dir();
        $this->compressManager->write('/* # upload_url: '.$upload_dir['baseurl'].' */;'.PHP_EOL);
        $this->compressManager->write('/* # table_prefix: '.$this->dumpSettings['prefix'].' */;'.PHP_EOL.PHP_EOL.PHP_EOL);

        // Store server settings and use sanner defaults to dump
        $this->compressManager->write(
            $this->typeAdapter->backup_parameters($this->dumpSettings)
        );

        if ($this->dumpSettings['databases']) {
            $this->compressManager->write(
                $this->typeAdapter->getDatabaseHeader($this->dbName)
            );
            if ($this->dumpSettings['add-drop-database']) {
                $this->compressManager->write(
                    $this->typeAdapter->add_drop_database($this->dbName)
                );
            }
        }

        // Get table, view and trigger structures from database
        $this->getDatabaseStructure();

        if ($this->dumpSettings['databases']) {
            $this->compressManager->write(
                $this->typeAdapter->databases($this->dbName)
            );
        }

        // If there still are some tables/views in include-tables array,
        // that means that some tables or views weren't found.
        // Give proper error and exit.
        // This check will be removed once include-tables supports regexps
        if (0 < count($this->dumpSettings['include-tables'])) {
            $name = implode(",", $this->dumpSettings['include-tables']);
            throw new Exception("Table (" . esc_html($name) . ") not found in database");
        }
        $this->exportTables();
        /*
        global $wpvivid_plugin;

        $this->exportTables();
        if($this -> privileges['SHOW VIEW'] == 0){
            $wpvivid_plugin->wpvivid_log->WriteLog('The lack of SHOW VIEW privilege, the backup will skip exportViews() to continue.','notice');
        }else{
            $this->exportViews();
        }

        if($this -> privileges['TRIGGER'] == 0){
            $wpvivid_plugin->wpvivid_log->WriteLog('The lack of TRIGGER privilege, the backup will skip exportTriggers() to continue.','notice');
        }else{
            $this->exportTriggers();
        }

        if($this -> privileges['CREATE ROUTINE'] == 0){
            $wpvivid_plugin->wpvivid_log->WriteLog('The lack of CREATE ROUTINE privilege, the backup will skip exportProcedures() to continue.','notice');
        }else{
            $this->exportProcedures();
        }

        if($this -> privileges['EVENT'] == 0){
            $wpvivid_plugin->wpvivid_log->WriteLog('The lack of EVENT privilege, the backup will skip exportEvents() to continue.','notice');
        }else{
            $this->exportEvents();
        }
        */

        // Restore saved parameters
        $this->compressManager->write(
            $this->typeAdapter->restore_parameters($this->dumpSettings)
        );
        // Write some stats to output file
        $this->compressManager->write($this->getDumpFileFooter());
        // Close output file
        $this->compressManager->close();
    }

    /**
     * Returns header for dump file
     *
     * @return string
     */
    private function getDumpFileHeader()
    {
        $header = '';
        if ( !$this->dumpSettings['skip-comments'] ) {
            // Some info about software, source and time
            $header = "-- mysqldump-php https://github.com/ifsnop/mysqldump-php" . PHP_EOL .
                    "--" . PHP_EOL .
                    "-- Host: {$this->host}\tDatabase: {$this->dbName}" . PHP_EOL .
                    "-- ------------------------------------------------------" . PHP_EOL;

            if ( !empty($this->version) ) {
                $header .= "-- Server version \t" . $this->version . PHP_EOL;
            }

            if ( !$this->dumpSettings['skip-dump-date'] ) {
                $header .= "-- Date: " . gmdate('r') . PHP_EOL . PHP_EOL;
            }
        }
        return $header;
    }

    /**
     * Returns footer for dump file
     *
     * @return string
     */
    private function getDumpFileFooter()
    {
        $footer = '';
        if (!$this->dumpSettings['skip-comments']) {
            $footer .= '-- Dump completed';
            if (!$this->dumpSettings['skip-dump-date']) {
                $footer .= ' on: ' . gmdate('r');
            }
            $footer .= PHP_EOL;
        }

        return $footer;
    }

    /**
     * Reads table and views names from database.
     * Fills $this->tables array so they will be dumped later.
     *
     * @return null
     */
    private function getDatabaseStructure()
    {
        // Listing all tables from database
        if (empty($this->dumpSettings['include-tables'])) {
            // include all tables for now, blacklisting happens later

            foreach ( $this->query($this->typeAdapter->show_tables($this->dbName)) as $row) {
                array_push($this->tables, current($row));
            }
        } else {
            // include only the tables mentioned in include-tables
            foreach ($this->query($this->typeAdapter->show_tables($this->dbName)) as $row) {
                if (in_array(current($row), $this->dumpSettings['include-tables'], true)) {
                    array_push($this->tables, current($row));
                    $elem = array_search(
                        current($row),
                        $this->dumpSettings['include-tables']
                    );
                    unset($this->dumpSettings['include-tables'][$elem]);
                }
            }
        }

        // Listing all views from database
        if (empty($this->dumpSettings['include-views'])) {
            // include all views for now, blacklisting happens later
            foreach ($this->query($this->typeAdapter->show_views($this->dbName)) as $row) {
                array_push($this->views, current($row));
            }
        } else {
            // include only the tables mentioned in include-tables
            foreach ($this->query($this->typeAdapter->show_views($this->dbName)) as $row) {
                if (in_array(current($row), $this->dumpSettings['include-views'], true)) {
                    array_push($this->views, current($row));
                    $elem = array_search(
                        current($row),
                        $this->dumpSettings['include-views']
                    );
                    unset($this->dumpSettings['include-views'][$elem]);
                }
            }
        }

        // Listing all triggers from database
        if (false === $this->dumpSettings['skip-triggers']) {
            foreach ($this->query($this->typeAdapter->show_triggers($this->dbName)) as $row) {
                array_push($this->triggers, $row['Trigger']);
            }
        }

        // Listing all procedures from database
        if ($this->dumpSettings['routines']) {
            foreach ($this->query($this->typeAdapter->show_procedures($this->dbName)) as $row) {
                array_push($this->procedures, $row['procedure_name']);
            }
        }

        // Listing all events from database
        if ($this->dumpSettings['events']) {
            foreach ($this->query($this->typeAdapter->show_events($this->dbName)) as $row) {
                array_push($this->events, $row['event_name']);
            }
        }
    }

    /**
     * Compare if $table name matches with a definition inside $arr
     * @param $table string
     * @param $arr array with strings or patterns
     * @return bool
     */
    private function matches($table, $arr) {
        $match = false;

        foreach ($arr as $pattern) {
            if ( '/' != $pattern[0] ) {
                continue;
            }
            if ( 1 == preg_match($pattern, $table) ) {
                $match = true;
            }
        }

        return in_array($table, $arr) || $match;
    }

    /**
     * Exports all the tables selected from database
     *
     * @return null
     */
    private function exportTables()
    {
        // Exporting tables one by one
        $i=0;
        $i_step=0;
        if($this->task_id!=='')
        {
            $options_name[]='backup_options';
            //$options_name[]='ismerge';
            $options=WPvivid_taskmanager::get_task_options($this->task_id,$options_name);
            if($options['backup_options']['ismerge'])
            {
                if(isset($options['backup_options']['backup']['backup_type'])) {
                    $i_step = intval(1 / (sizeof($options['backup_options']['backup']['backup_type']) + 1) * 100);
                }
                else{
                    $i_step = intval(1 / (sizeof($options['backup_options']['backup']) + 1) * 100);
                }
            }
            else
            {
                if(isset($options['backup_options']['backup']['backup_type'])) {
                    $i_step = intval(1 / sizeof($options['backup_options']['backup']['backup_type']) * 100);
                }
                else{
                    $i_step = intval(1 / sizeof($options['backup_options']['backup']) * 100);
                }
            }
        }

        foreach ($this->tables as $table)
        {
            if ( $this->matches($table, $this->dumpSettings['exclude-tables']) )
            {
                continue;
            }

            if($this->task_id!=='')
            {
                $message='Preparing to dump table '.$table;
                global $wpvivid_plugin;
                $wpvivid_plugin->wpvivid_log->WriteLog($message,'notice');
                WPvivid_taskmanager::update_backup_sub_task_progress($this->task_id,'backup',WPVIVID_BACKUP_TYPE_DB,0,$message);
            }

            $this->getTableStructure($table);
            if($this->tableColumnTypes[$table]===false)
            {
                global $wpvivid_plugin;
                $message='get Table Structure failed. table:'.$table;
                $wpvivid_plugin->wpvivid_log->WriteLog($message,'notice');
                continue;
            }
            if ( false === $this->dumpSettings['no-data'] ) { // don't break compatibility with old trigger
                $this->listValues($table);
            } else if ( true === $this->dumpSettings['no-data']
                 || $this->matches($table, $this->dumpSettings['no-data']) ) {
                continue;
            } else {
                $this->listValues($table);
            }
            $i++;
            if($this->task_id!=='')
            {
                $i_progress=intval($i/sizeof($this->tables)*$i_step);
                WPvivid_taskmanager::update_backup_main_task_progress($this->task_id,'backup',$i_progress,0);
            }
        }
        return ;
    }

    /**
     * Exports all the views found in database
     *
     * @return null
     */
    private function exportViews()
    {
        if (false === $this->dumpSettings['no-create-info']) {
            // Exporting views one by one
            foreach ($this->views as $view) {
                if ( $this->matches($view, $this->dumpSettings['exclude-tables']) ) {
                    continue;
                }
                $this->tableColumnTypes[$view] = $this->getTableColumnTypes($view);
                $this->getViewStructureTable($view);
            }
            foreach ($this->views as $view) {
                if ( $this->matches($view, $this->dumpSettings['exclude-tables']) ) {
                    continue;
                }
                $this->getViewStructureView($view);
            }
        }
    }

    /**
     * Exports all the triggers found in database
     *
     * @return null
     */
    private function exportTriggers()
    {
        // Exporting triggers one by one
        foreach ($this->triggers as $trigger) {
            $this->getTriggerStructure($trigger);
        }
    }

    /**
     * Exports all the procedures found in database
     *
     * @return null
     */
    private function exportProcedures()
    {
        // Exporting triggers one by one
        foreach ($this->procedures as $procedure) {
            $this->getProcedureStructure($procedure);
        }
    }

    /**
     * Exports all the events found in database
     *
     * @return null
     */
    private function exportEvents()
    {
        // Exporting triggers one by one
        foreach ($this->events as $event) {
            $this->getEventStructure($event);
        }
    }

    /**
     * Table structure extractor
     *
     * @todo move specific mysql code to typeAdapter
     * @param string $tableName  Name of table to export
     * @return null
     */
    private function getTableStructure($tableName)
    {
        if (!$this->dumpSettings['no-create-info']) {
            $ret = '';
            if (!$this->dumpSettings['skip-comments']) {
                $ret = "--" . PHP_EOL .
                    "-- Table structure for table `$tableName`" . PHP_EOL .
                    "--" . PHP_EOL . PHP_EOL;
            }
            $stmt = $this->typeAdapter->show_create_table($tableName);

            foreach ($this->query($stmt) as $r)
            {
                $this->compressManager->write($ret);
                if ($this->dumpSettings['add-drop-table']) {
                    $this->compressManager->write(
                        $this->typeAdapter->drop_table($tableName)
                    );
                }

                $this->compressManager->write(
                    $this->typeAdapter->create_table($r, $this->dumpSettings)
                );
                break;
            }
        }
        $this->tableColumnTypes[$tableName] = $this->getTableColumnTypes($tableName);
        return;
    }

    /**
     * Store column types to create data dumps and for Stand-In tables
     *
     * @param string $tableName  Name of table to export
     * @return array type column types detailed
     */

    private function getTableColumnTypes($tableName) {
        $columnTypes = array();
        $columns = $this->query(
            $this->typeAdapter->show_columns($tableName)
        );
        if($columns===false)
        {
            global $wpvivid_plugin;
            $error=$this->typeAdapter->errorInfo();
            if(isset($error[2])){
                $error = 'Error: '.$error[2];
            }
            else{
                $error = '';
            }
            $message='Show columns failed. '.$error;
            $wpvivid_plugin->wpvivid_log->WriteLog($message, 'warning');
            $columns = $this->query(
                'DESCRIBE '.$tableName
            );
            if($columns===false)
            {
                $error=$this->typeAdapter->errorInfo();
                if(isset($error[2])){
                    $error = 'Error: '.$error[2];
                }
                else{
                    $error = '';
                }
                $message='DESCRIBE failed. '.$error;
                $wpvivid_plugin->wpvivid_log->WriteLog($message, 'warning');
                return false;
            }
        }

        foreach($columns as $key => $col) {
            $types = $this->typeAdapter->parseColumnType($col);
            $columnTypes[$col['Field']] = array(
                'is_numeric'=> $types['is_numeric'],
                'is_blob' => $types['is_blob'],
                'type' => $types['type'],
                'type_sql' => $col['Type'],
                'is_virtual' => $types['is_virtual']
            );
        }

        return $columnTypes;
    }

    /**
     * View structure extractor, create table (avoids cyclic references)
     *
     * @todo move mysql specific code to typeAdapter
     * @param string $viewName  Name of view to export
     * @return null
     */
    private function getViewStructureTable($viewName)
    {
        if (!$this->dumpSettings['skip-comments']) {
            $ret = "--" . PHP_EOL .
                "-- Stand-In structure for view `${viewName}`" . PHP_EOL .
                "--" . PHP_EOL . PHP_EOL;
            $this->compressManager->write($ret);
        }
        $stmt = $this->typeAdapter->show_create_view($viewName);

        // create views as tables, to resolve dependencies
        foreach ($this->query($stmt) as $r) {
            if ($this->dumpSettings['add-drop-table']) {
                $this->compressManager->write(
                    $this->typeAdapter->drop_view($viewName)
                );
            }

            $this->compressManager->write(
                $this->createStandInTable($viewName)
            );
            break;
        }
    }

    /**
     * Write a create table statement for the table Stand-In, show create
     * table would return a create algorithm when used on a view
     *
     * @param string $viewName  Name of view to export
     * @return string create statement
     */
    function createStandInTable($viewName) {
        $ret = array();
        foreach($this->tableColumnTypes[$viewName] as $k => $v) {
            $ret[] = "`${k}` ${v['type_sql']}";
        }

        $ret = implode(PHP_EOL . ",", $ret);

        $ret = "CREATE TABLE IF NOT EXISTS `$viewName` (" .
            PHP_EOL . $ret . PHP_EOL . ");" . PHP_EOL;

        return $ret;
    }

    /**
     * View structure extractor, create view
     *
     * @todo move mysql specific code to typeAdapter
     * @param string $viewName  Name of view to export
     * @return null
     */
    private function getViewStructureView($viewName)
    {
        if (!$this->dumpSettings['skip-comments']) {
            $ret = "--" . PHP_EOL .
                "-- View structure for view `${viewName}`" . PHP_EOL .
                "--" . PHP_EOL . PHP_EOL;
            $this->compressManager->write($ret);
        }
        $stmt = $this->typeAdapter->show_create_view($viewName);

        // create views, to resolve dependencies
        // replacing tables with views
        foreach ($this->query($stmt) as $r) {
            // because we must replace table with view, we should delete it
            $this->compressManager->write(
                $this->typeAdapter->drop_view($viewName)
            );
            $this->compressManager->write(
                $this->typeAdapter->create_view($r)
            );
            break;
        }
    }

    /**
     * Trigger structure extractor
     *
     * @param string $triggerName  Name of trigger to export
     * @return null
     */
    private function getTriggerStructure($triggerName)
    {
        $stmt = $this->typeAdapter->show_create_trigger($triggerName);
        foreach ($this->query($stmt) as $r) {
            if ($this->dumpSettings['add-drop-trigger']) {
                $this->compressManager->write(
                    $this->typeAdapter->add_drop_trigger($triggerName)
                );
            }
            $this->compressManager->write(
                $this->typeAdapter->create_trigger($r)
            );
            return;
        }
    }

    /**
     * Procedure structure extractor
     *
     * @param string $procedureName  Name of procedure to export
     * @return null
     */
    private function getProcedureStructure($procedureName)
    {
        if (!$this->dumpSettings['skip-comments']) {
            $ret = "--" . PHP_EOL .
                "-- Dumping routines for database '" . $this->dbName . "'" . PHP_EOL .
                "--" . PHP_EOL . PHP_EOL;
            $this->compressManager->write($ret);
        }
        $stmt = $this->typeAdapter->show_create_procedure($procedureName);
        foreach ($this->query($stmt) as $r) {
            $this->compressManager->write(
                $this->typeAdapter->create_procedure($r, $this->dumpSettings)
            );
            return;
        }
    }

    /**
     * Event structure extractor
     *
     * @param string $eventName  Name of event to export
     * @return null
     */
    private function getEventStructure($eventName)
    {
        if (!$this->dumpSettings['skip-comments']) {
            $ret = "--" . PHP_EOL .
                "-- Dumping events for database '" . $this->dbName . "'" . PHP_EOL .
                "--" . PHP_EOL . PHP_EOL;
            $this->compressManager->write($ret);
        }
        $stmt = $this->typeAdapter->show_create_event($eventName);
        foreach ($this->query($stmt) as $r) {
            $this->compressManager->write(
                $this->typeAdapter->create_event($r, $this->dumpSettings)
            );
            return;
        }
    }

    /**
     * Escape values with quotes when needed
     *
     * @param string $tableName Name of table which contains rows
     * @param array $row Associative array of column names and values to be quoted
     *
     * @return string
     */
    private function escape($tableName, $row)
    {
        $ret = array();
        $columnTypes = $this->tableColumnTypes[$tableName];
        foreach ($row as $colName => $colValue) {
            if (is_null($colValue)) {
                $ret[] = "NULL";
            } elseif ($this->dumpSettings['hex-blob'] && $columnTypes[$colName]['is_blob']) {
                if ($columnTypes[$colName]['type'] == 'bit' || !empty($colValue)) {
                    $ret[] = "0x${colValue}";
                } else {
                    $ret[] = "''";
                }
            } elseif ($columnTypes[$colName]['is_numeric']) {
                $ret[] = $colValue;
            } else {
                $ret[] = $this->typeAdapter->quote($colValue);
            }
        }
        return $ret;
    }

    /**
     * Table rows extractor
     *
     * @param string $tableName  Name of table to export
     *
     * @return null
     */
    private function listValues($tableName)
    {
        $this->prepareListValues($tableName);

        $onlyOnce = true;
        $lineSize = 0;

        $colStmt = $this->getColumnStmt($tableName);

        global $wpdb;
        $prefix=$wpdb->base_prefix;

        if($this->dbType=='wpdb')
        {
            $start=0;
            $limit_count=5000;
            $sum =$wpdb->get_var("SELECT COUNT(1) FROM `{$tableName}`");
            if(substr($tableName, strlen($prefix))=='options')
            {
                $stmt = "SELECT " . implode(",", $colStmt) . " FROM `$tableName` WHERE option_name !='wpvivid_task_list'";
            }
            else
            {
                $stmt = "SELECT " . implode(",", $colStmt) . " FROM `$tableName`";
            }

            if ($this->dumpSettings['where']) {
                $stmt .= " WHERE {$this->dumpSettings['where']}";
            }

            $i=0;
            $i_check_cancel=0;
            $count=0;

            while($sum > $start)
            {
                $limit = " LIMIT {$limit_count} OFFSET {$start}";

                $query=$stmt.$limit;
                $resultSet = $this->query($query);

                if($resultSet===false)
                {
                    global $wpvivid_plugin;
                    $error=$this->typeAdapter->errorInfo();
                    if(isset($error[2])){
                        $error = 'Error: '.$error[2];
                    }
                    else{
                        $error = '';
                    }

                    $message='listValues failed. '.$error;
                    $wpvivid_plugin->wpvivid_log->WriteLog($message, 'warning');

                    $this->endListValues($tableName);
                    return ;
                }

                foreach ($resultSet as $row)
                {
                    $i++;
                    $vals = $this->escape($tableName, $row);

                    foreach($vals as $key => $value){
                        if($value === '\'0000-00-00 00:00:00\'')
                            $vals[$key] = '\'1999-01-01 00:00:00\'';
                    }

                    if ($onlyOnce || !$this->dumpSettings['extended-insert'])
                    {

                        if ($this->dumpSettings['complete-insert'])
                        {
                            $lineSize += $this->compressManager->write(
                                "INSERT INTO `$tableName` (" .
                                implode(", ", $colStmt) .
                                ") VALUES (" . implode(",", $vals) . ")"
                            );
                        } else {
                            $lineSize += $this->compressManager->write(
                                "INSERT INTO `$tableName` VALUES (" . implode(",", $vals) . ")"
                            );
                        }
                        $onlyOnce = false;
                    } else {
                        $lineSize += $this->compressManager->write(",(" . implode(",", $vals) . ")");
                    }
                    if (($lineSize > $this->dumpSettings['net_buffer_length']) ||
                        !$this->dumpSettings['extended-insert']) {
                        $onlyOnce = true;
                        $lineSize = $this->compressManager->write(";" . PHP_EOL);
                    }

                    if($i>=200000)
                    {
                        $count+=$i;
                        $i=0;
                        if($this->task_id!=='')
                        {
                            $i_check_cancel++;
                            if($i_check_cancel>5)
                            {
                                $i_check_cancel=0;
                                global $wpvivid_plugin;
                                $wpvivid_plugin->check_cancel_backup($this->task_id);
                            }
                            $message='Dumping table '.$tableName.', rows dumped: '.$count.' rows.';
                            WPvivid_taskmanager::update_backup_sub_task_progress($this->task_id,'backup',WPVIVID_BACKUP_TYPE_DB,0,$message);
                        }
                    }
                }

                $this->typeAdapter->closeCursor($resultSet);

                $start += $limit_count;
            }

            if (!$onlyOnce) {
                $this->compressManager->write(";" . PHP_EOL);
            }

            $this->endListValues($tableName);
        }
        else
        {
            if(substr($tableName, strlen($prefix))=='options')
            {
                $stmt = "SELECT " . implode(",", $colStmt) . " FROM `$tableName` WHERE option_name !='wpvivid_task_list'";
            }
            else
            {
                $stmt = "SELECT " . implode(",", $colStmt) . " FROM `$tableName`";
            }

            if ($this->dumpSettings['where']) {
                $stmt .= " WHERE {$this->dumpSettings['where']}";
            }

            $resultSet = $this->query($stmt);

            if($resultSet===false)
            {
                global $wpvivid_plugin;
                $error=$this->typeAdapter->errorInfo();
                if(isset($error[2])){
                    $error = 'Error: '.$error[2];
                }
                else{
                    $error = '';
                }

                $message='listValues failed. '.$error;
                $wpvivid_plugin->wpvivid_log->WriteLog($message, 'warning');

                $this->endListValues($tableName);
                return ;
            }

            $i=0;
            $i_check_cancel=0;
            $count=0;
            foreach ($resultSet as $row)
            {
                $i++;
                $vals = $this->escape($tableName, $row);

                foreach($vals as $key => $value){
                    if($value === '\'0000-00-00 00:00:00\'')
                        $vals[$key] = '\'1999-01-01 00:00:00\'';
                }

                if ($onlyOnce || !$this->dumpSettings['extended-insert'])
                {

                    if ($this->dumpSettings['complete-insert'])
                    {
                        $lineSize += $this->compressManager->write(
                            "INSERT INTO `$tableName` (" .
                            implode(", ", $colStmt) .
                            ") VALUES (" . implode(",", $vals) . ")"
                        );
                    } else {
                        $lineSize += $this->compressManager->write(
                            "INSERT INTO `$tableName` VALUES (" . implode(",", $vals) . ")"
                        );
                    }
                    $onlyOnce = false;
                } else {
                    $lineSize += $this->compressManager->write(",(" . implode(",", $vals) . ")");
                }
                if (($lineSize > $this->dumpSettings['net_buffer_length']) ||
                    !$this->dumpSettings['extended-insert']) {
                    $onlyOnce = true;
                    $lineSize = $this->compressManager->write(";" . PHP_EOL);
                }

                if($i>=200000)
                {
                    $count+=$i;
                    $i=0;
                    if($this->task_id!=='')
                    {
                        $i_check_cancel++;
                        if($i_check_cancel>5)
                        {
                            $i_check_cancel=0;
                            global $wpvivid_plugin;
                            $wpvivid_plugin->check_cancel_backup($this->task_id);
                        }
                        $message='Dumping table '.$tableName.', rows dumped: '.$count.' rows.';
                        WPvivid_taskmanager::update_backup_sub_task_progress($this->task_id,'backup',WPVIVID_BACKUP_TYPE_DB,0,$message);
                    }
                }
            }

            $this->typeAdapter->closeCursor($resultSet);
            //$resultSet->closeCursor();

            if (!$onlyOnce) {
                $this->compressManager->write(";" . PHP_EOL);
            }

            $this->endListValues($tableName);
        }
    }

    /**
     * Table rows extractor, append information prior to dump
     *
     * @param string $tableName  Name of table to export
     *
     * @return null
     */
    function prepareListValues($tableName)
    {
        if (!$this->dumpSettings['skip-comments']) {
            $this->compressManager->write(
                "--" . PHP_EOL .
                "-- Dumping data for table `$tableName`" .  PHP_EOL .
                "--" . PHP_EOL . PHP_EOL
            );
        }

        if ($this->dumpSettings['single-transaction']) {
            $this->exec($this->typeAdapter->setup_transaction());
            $this->exec($this->typeAdapter->start_transaction());
        }

        if ($this->dumpSettings['lock-tables'])
        {
            $this->typeAdapter->lock_table($tableName);

            //if($this -> privileges['LOCK TABLES'] == 0)
            //{
            //global $wpvivid_plugin;
            //    $wpvivid_plugin->wpvivid_log->WriteLog('The lack of LOCK TABLES privilege, the backup will skip lock_tables() to continue.','notice');
            //}else{
            //    $this->typeAdapter->lock_table($tableName);
            //}
        }

        if ($this->dumpSettings['add-locks']) {
            $this->compressManager->write(
                $this->typeAdapter->start_add_lock_table($tableName)
            );
        }

        if ($this->dumpSettings['disable-keys']) {
            $this->compressManager->write(
                $this->typeAdapter->start_add_disable_keys($tableName)
            );
        }

        // Disable autocommit for faster reload
        if ($this->dumpSettings['no-autocommit']) {
            $this->compressManager->write(
                $this->typeAdapter->start_disable_autocommit()
            );
        }

        return;
    }

    /**
     * Table rows extractor, close locks and commits after dump
     *
     * @param string $tableName  Name of table to export
     *
     * @return null
     */
    function endListValues($tableName)
    {
        if ($this->dumpSettings['disable-keys']) {
            $this->compressManager->write(
                $this->typeAdapter->end_add_disable_keys($tableName)
            );
        }

        if ($this->dumpSettings['add-locks']) {
            $this->compressManager->write(
                $this->typeAdapter->end_add_lock_table($tableName)
            );
        }

        if ($this->dumpSettings['single-transaction']) {
            $this->exec($this->typeAdapter->commit_transaction());
        }

        if ($this->dumpSettings['lock-tables']) {
            $this->typeAdapter->unlock_table($tableName);
        }

        // Commit to enable autocommit
        if ($this->dumpSettings['no-autocommit']) {
            $this->compressManager->write(
                $this->typeAdapter->end_disable_autocommit()
            );
        }

        $this->compressManager->write(PHP_EOL);

        return;
    }

    /**
     * Build SQL List of all columns on current table
     *
     * @param string $tableName  Name of table to get columns
     *
     * @return string SQL sentence with columns
     */
    function getColumnStmt($tableName)
    {
        $colStmt = array();
        foreach($this->tableColumnTypes[$tableName] as $colName => $colType) {
            if ($colType['type'] == 'bit' && $this->dumpSettings['hex-blob']) {
                $colStmt[] = "LPAD(HEX(`${colName}`),2,'0') AS `${colName}`";
            } else if ($colType['is_blob'] && $this->dumpSettings['hex-blob']) {
                $colStmt[] = "HEX(`${colName}`) AS `${colName}`";
            } else if ($colType['is_virtual']) {
                $this->dumpSettings['complete-insert'] = true;
                continue;
            } else {
                $colStmt[] = "`${colName}`";
            }
        }

        return $colStmt;
    }

    public function query($query_string)
    {
        $this->last_query_string=$query_string;
        return  $this->typeAdapter->query($query_string);
    }

    private function exec($query_string)
    {
        $this->last_query_string=$query_string;
        return  $this->typeAdapter->query($query_string);
    }
}


includes/class-wpvivid-restore-data.php000064400000003102151327705670014257 0ustar00<?php

if (!defined('WPVIVID_PLUGIN_DIR')){
    die;
}
class WPvivid_restore_data
{
    public $restore_data_file;
    public $restore_log_file;
    public $restore_log=false;
    public $restore_cache=false;


    public function __construct()
    {
        $dir=WPvivid_Setting::get_backupdir();
        $this->restore_data_file= WP_CONTENT_DIR.DIRECTORY_SEPARATOR.$dir.DIRECTORY_SEPARATOR.'wpvivid_restoredata';
        $this->restore_log_file= WP_CONTENT_DIR.DIRECTORY_SEPARATOR.$dir.DIRECTORY_SEPARATOR.'wpvivid_restore_log.txt';
    }

    public function write_log($message,$type)
    {
        if($this->restore_log===false)
        {
            $this->restore_log=new WPvivid_Log();
            $this->restore_log->OpenLogFile($this->restore_log_file,'has_folder');
        }

        clearstatcache();
        if(filesize($this->restore_log_file)>4*1024*1024)
        {
            $this->restore_log->CloseFile();
            wp_delete_file($this->restore_log_file);
            $this->restore_log=null;
            $this->restore_log=new WPvivid_Log();
            $this->restore_log->OpenLogFile($this->restore_log_file,'has_folder');
        }
        $this->restore_log->WriteLog($message,$type);
    }

    public function get_log_content()
    {
        $file =fopen($this->restore_log_file,'r');

        if(!$file)
        {
            return '';
        }

        $buffer='';
        while(!feof($file))
        {
            $buffer .= fread($file,1024);
        }
        fclose($file);

        return $buffer;
    }
}includes/class-wpvivid-migrate.php000064400000231012151327705670013320 0ustar00<?php

if (!defined('WPVIVID_PLUGIN_DIR'))
{
    die;
}

class WPvivid_Migrate
{
    public function __construct()
    {
        add_filter('wpvivid_add_tab_page', array($this, 'wpvivid_add_migrate_tab_page'));
        add_action('wp_ajax_wpvivid_generate_url',array( $this,'generate_url'));
        add_action('wp_ajax_wpvivid_generate_url_ex', array($this, 'generate_url_ex'));
        add_action('wp_ajax_wpvivid_send_backup_to_site',array( $this,'send_backup_to_site'));
        add_action('wp_ajax_wpvivid_migrate_now',array( $this,'migrate_now'));
        add_filter('wpvivid_backuppage_load_backuplist', array($this, 'wpvivid_backuppage_load_backuplist'));

        add_action('wp_ajax_wpvivid_export_download_backup',array( $this,'export_download_backup'));
        add_action('wp_ajax_wpvivid_list_upload_tasks',array( $this,'list_tasks'));
        add_action('wp_ajax_wpvivid_test_connect_site',array( $this,'test_connect_site'));
        add_action('wp_ajax_wpvivid_delete_transfer_key',array($this, 'delete_transfer_key'));

        add_filter('wpvivid_put_transfer_key', array($this, 'wpvivid_put_transfer_key'));
        add_action('wpvivid_put_transfer_key_output', array($this, 'wpvivid_put_transfer_key_output'));
        add_action('wpvivid_handle_backup_failed',array($this,'wpvivid_handle_backup_failed'),9);

        add_action('wpvivid_rescan_backup_list', array($this, 'wpvivid_rescan_backup_list'));
        add_action('wpvivid_handle_upload_succeed',array($this,'wpvivid_deal_upload_succeed'),11);

        add_action('wpvivid_add_migrate_type_output', array($this, 'wpvivid_add_migrate_type_output'),10,1);
        add_filter('wpvivid_add_migrate_type', array($this, 'wpvivid_add_migrate_type'), 11, 2);
        add_filter('wpvivid_migrate_descript', array($this, 'wpvivid_migrate_descript'));
        add_filter('wpvivid_migrate_part_type', array($this, 'wpvivid_migrate_part_type'));
        add_action('wpvivid_migrate_part_exec', array($this, 'wpvivid_migrate_part_exec'));
        add_filter('wpvivid_migrate_part_note', array($this, 'wpvivid_migrate_part_note'));
        add_filter('wpvivid_migrate_part_tip', array($this, 'wpvivid_migrate_part_tip'));

        add_filter('wpvivid_load_migrate_js', array($this, 'wpvivid_load_migrate_js'));
        add_action('wpvivid_add_migrate_js', array($this, 'wpvivid_add_migrate_js'));
    }

    public function wpvivid_add_migrate_tab_page($page_array){
        $page_array['migrate'] = array('index' => '3', 'tab_func' => array($this, 'wpvivid_add_tab_migrate'), 'page_func' => array($this, 'wpvivid_add_page_migrate'));
        $page_array['key'] = array('index' => '8', 'tab_func' => array($this, 'wpvivid_add_tab_key'), 'page_func' => array($this, 'wpvivid_add_page_key'));
        return $page_array;
    }

    public function wpvivid_add_tab_migrate()
    {
        ?>
        <a href="#" id="wpvivid_tab_migrate" class="nav-tab wrap-nav-tab" onclick="switchTabs(event,'migrate-page')"><?php esc_html_e('Auto-Migration', 'wpvivid-backuprestore'); ?></a>
        <?php
    }

    public function wpvivid_add_tab_key()
    {
        ?>
        <a href="#" id="wpvivid_tab_key" class="nav-tab wrap-nav-tab" onclick="switchTabs(event,'key-page')"><?php esc_html_e('Key', 'wpvivid-backuprestore'); ?></a>
        <?php
    }

    public function wpvivid_load_migrate_js($html)
    {
        do_action('wpvivid_add_migrate_js');
        return $html;
    }

    public function wpvivid_add_migrate_js()
    {
        ?>
        <script>
            var wpvivid_home_url = '<?php
                $wpvivid_siteurl = array();
                $wpvivid_siteurl=WPvivid_Admin::wpvivid_get_siteurl();
                echo esc_url($wpvivid_siteurl['home_url']);
                ?>';

            jQuery('input:radio[option=migrate][name=transfer]').click(function(){
                var value = jQuery(this).prop('value');
                if(value === 'transfer'){
                    jQuery('#wpvivid_transfer_btn').show();
                    jQuery('#wpvivid_export_download_btn').hide();
                }
                else if(value === 'export'){
                    jQuery('#wpvivid_transfer_btn').hide();
                    jQuery('#wpvivid_export_download_btn').show();
                }
            });
            //wpvivid_edit_url_button
            jQuery('#wpvivid_add_remote_site_url').show();
            jQuery('#wpvivid_upload_backup_percent').hide();

            var wpvivid_transfer_id = '';



            function wpvivid_control_transfer_lock(){
                jQuery('#wpvivid_quickbackup_btn').css({'pointer-events': 'none', 'opacity': '0.4'});
                jQuery('#wpvivid_transfer_btn').css({'pointer-events': 'none', 'opacity': '0.4'});
                jQuery("#wpvivid_delete_key_button").css({'pointer-events': 'none', 'opacity': '0.4'});
            }

            function wpvivid_control_transfer_unlock(){
                jQuery('#wpvivid_quickbackup_btn').css({'pointer-events': 'auto', 'opacity': '1'});
                jQuery('#wpvivid_transfer_btn').css({'pointer-events': 'auto', 'opacity': '1'});
                jQuery("#wpvivid_delete_key_button").css({'pointer-events': 'auto', 'opacity': '1'});
            }



            function wpvivid_click_export_backup()
            {
                var option_data = wpvivid_ajax_data_transfer('migrate');
                var ajax_data = {
                    'action': 'wpvivid_export_download_backup',
                    'backup_options':option_data
                };
                migrate_task_need_update=true;
                jQuery('#wpvivid_export_download_btn').css({'pointer-events': 'none', 'opacity': '0.4'});
                wpvivid_post_request(ajax_data, function (data)
                {
                }, function (XMLHttpRequest, textStatus, errorThrown)
                {
                    var error_message = wpvivid_output_ajaxerror('test generate url', textStatus, errorThrown);
                    alert(error_message);
                });
            }

            var wpvivid_display_get_key = false;



            function wpvivid_transfer_cancel_flow()
            {
                jQuery('#wpvivid_transfer_cancel_btn').click(function(){
                    wpvivid_cancel_transfer();
                });
            }

            function wpvivid_cancel_transfer()
            {
                var ajax_data= {
                    'action': 'wpvivid_backup_cancel',
                    'task_id': wpvivid_transfer_id
                };
                jQuery('#wpvivid_transfer_cancel_btn').css({'pointer-events': 'none', 'opacity': '0.4'});
                wpvivid_post_request(ajax_data, function(data)
                {
                    try
                    {
                        var jsonarray = jQuery.parseJSON(data);
                        if(jsonarray.no_response)
                        {
                            var ret = confirm(jsonarray.msg);
                            if(ret === true)
                            {
                                wpvivid_termination_backup_task_ex(jsonarray.task_id);
                            }
                        }
                        else
                        {
                            jQuery('#wpvivid_current_doing').html(jsonarray.msg);
                        }
                    }
                    catch(err)
                    {
                        alert(err);
                    }
                }, function(XMLHttpRequest, textStatus, errorThrown)
                {
                    jQuery('#wpvivid_transfer_cancel_btn').css({'pointer-events': 'auto', 'opacity': '1'});
                    var error_message = wpvivid_output_ajaxerror('cancelling the backup', textStatus, errorThrown);
                    wpvivid_add_notice('Backup', 'Error', error_message);
                });
            }

            function wpvivid_termination_backup_task_ex(task_id)
            {
                var ajax_data= {
                    'action': 'wpvivid_shutdown_backup',
                    'task_id': task_id
                };
                wpvivid_post_request(ajax_data, function(data)
                {
                    try
                    {
                    }
                    catch(err)
                    {
                        alert(err);
                    }
                }, function(XMLHttpRequest, textStatus, errorThrown)
                {
                    var error_message = wpvivid_output_ajaxerror('terminationing the backup', textStatus, errorThrown);
                    wpvivid_add_notice('Backup', 'Error', error_message);
                });
            }

            var migrate_task_need_update=true;
            var task_recheck_times=0;
            function wpvivid_check_upload_runningtask()
            {
                var ajax_data = {
                    'action': 'wpvivid_list_upload_tasks',
                };
                if(wpvivid_restoring === false) {
                    wpvivid_post_request(ajax_data, function (data) {
                        setTimeout(function () {
                            wpvivid_manage_upload_task();
                        }, 3000);
                        try {
                            var jsonarray = jQuery.parseJSON(data);
                            if (jsonarray.transfer_succeed_notice != false) {
                                jQuery('#wpvivid_backup_notice').show();
                                jQuery('#wpvivid_backup_notice').append(jsonarray.transfer_succeed_notice);
                            }
                            if (jsonarray.transfer_error_notice != false) {
                                jQuery('#wpvivid_backup_notice').show();
                                jQuery.each(jsonarray.transfer_error_notice, function (index, value) {
                                    jQuery('#wpvivid_backup_notice').append(value.error_msg);
                                });
                            }
                            var b_need_show = false;
                            if (jsonarray.transfer.data.length !== 0) {
                                b_need_show = true;
                                task_recheck_times = 0;
                                if (jsonarray.transfer.result === 'success') {
                                    jQuery.each(jsonarray.transfer.data, function (index, value) {
                                        if (value.status.str === 'ready') {
                                            wpvivid_control_transfer_lock();
                                            jQuery('#wpvivid_upload_backup_percent').show();
                                            jQuery('#wpvivid_upload_backup_percent').html(value.progress_html);
                                            migrate_task_need_update = true;
                                        }
                                        else if (value.status.str === 'running') {
                                            wpvivid_control_transfer_lock();
                                            jQuery('#wpvivid_upload_backup_percent').show();
                                            jQuery('#wpvivid_upload_backup_percent').html(value.progress_html);
                                            migrate_task_need_update = true;
                                        }
                                        else if (value.status.str === 'wait_resume') {
                                            wpvivid_control_transfer_lock();
                                            jQuery('#wpvivid_upload_backup_percent').show();
                                            jQuery('#wpvivid_upload_backup_percent').html(value.progress_html);
                                            if (value.data.next_resume_time !== 'get next resume time failed.') {
                                                wpvivid_resume_transfer(index, value.data.next_resume_time);
                                            }
                                            else {
                                                wpvivid_delete_backup_task(index);
                                            }
                                        }
                                        else if (value.status.str === 'no_responds') {
                                            wpvivid_control_transfer_lock();
                                            jQuery('#wpvivid_upload_backup_percent').show();
                                            jQuery('#wpvivid_upload_backup_percent').html(value.progress_html);
                                            migrate_task_need_update = true;
                                        }
                                        else if (value.status.str === 'completed') {
                                            wpvivid_control_transfer_unlock();
                                            jQuery('#wpvivid_upload_backup_percent').html(value.progress_html);
                                            jQuery('#wpvivid_upload_backup_percent').hide();
                                            migrate_task_need_update = true;
                                            alert('Transfer succeeded. Please scan the backup list on the destination site to display the backup, then restore the backup.');
                                        }
                                        else if (value.status.str === 'error') {
                                            wpvivid_control_transfer_unlock();
                                            jQuery('#wpvivid_upload_backup_percent').html(value.progress_html);
                                            jQuery('#wpvivid_upload_backup_percent').hide();
                                            migrate_task_need_update = true;
                                        }
                                    });
                                }
                                wpvivid_transfer_cancel_flow();
                            }
                            else{
                                if(wpvivid_transfer_id != '') {
                                    jQuery('#wpvivid_transfer_cancel_btn').css({'pointer-events': 'auto', 'opacity': '1'});
                                    wpvivid_control_transfer_unlock();
                                    jQuery('#wpvivid_upload_backup_percent').hide();
                                    wpvivid_transfer_id = '';
                                }
                            }
                        }
                        catch (err) {
                            alert(err);
                        }
                        if (!b_need_show) {
                            task_recheck_times++;
                            if (task_recheck_times < 5) {
                                migrate_task_need_update = true;
                            }
                        }

                    }, function (XMLHttpRequest, textStatus, errorThrown) {
                        migrate_task_need_update = true;
                        setTimeout(function () {
                            wpvivid_manage_upload_task();
                        }, 3000);
                    });
                }
            }

            function wpvivid_resume_transfer(backup_id, next_resume_time){
                if(next_resume_time < 0){
                    next_resume_time = 0;
                }
                next_resume_time = next_resume_time * 1000;
                setTimeout("wpvivid_activate_migrate_cron()", next_resume_time);
                setTimeout(function(){
                    task_recheck_times = 0;
                    migrate_task_need_update=true;
                }, next_resume_time);
            }

            function wpvivid_manage_upload_task()
            {
                if(migrate_task_need_update){
                    migrate_task_need_update=false;
                    wpvivid_check_upload_runningtask();
                }
                else {
                    setTimeout(function () {
                        wpvivid_manage_upload_task();
                    }, 3000);
                }
            }

            function wpvivid_activate_migrate_cron(){
                var next_get_time = 3 * 60 * 1000;
                jQuery.get(wpvivid_home_url+'/wp-cron.php');
                setTimeout("wpvivid_activate_migrate_cron()", next_get_time);
                setTimeout(function(){
                    migrate_task_need_update=true;
                }, 10000);
            }

            function switchmigrateTabs(evt,contentName,storage_page_id) {
                // Declare all variables
                var i, tabcontent, tablinks;

                // Get all elements with class="table-list-content" and hide them
                tabcontent = document.getElementsByClassName("migrate-tab-content");
                for (i = 0; i < tabcontent.length; i++) {
                    tabcontent[i].style.display = "none";
                }

                // Get all elements with class="table-nav-tab" and remove the class "nav-tab-active"
                tablinks = document.getElementsByClassName("migrate-nav-tab");
                for (i = 0; i < tablinks.length; i++) {
                    tablinks[i].className = tablinks[i].className.replace(" nav-tab-active", "");
                }

                // Show the current tab, and add an "storage-menu-active" class to the button that opened the tab
                document.getElementById(contentName).style.display = "block";
                evt.currentTarget.className += " nav-tab-active";

                var top = jQuery('#'+storage_page_id).offset().top-jQuery('#'+storage_page_id).height();
                jQuery('html, body').animate({scrollTop:top}, 'slow');
            }

            jQuery(document).ready(function ()
            {
                <?php
                $default_task_type = array();
                $default_task_type = apply_filters('wpvivid_get_task_type', $default_task_type);
                if(empty($default_task_type)){
                ?>
                //wpvivid_activate_migrate_cron();
                //wpvivid_manage_upload_task();
                <?php
                }
                ?>
            });
        </script>
        <?php
    }


    public function wpvivid_add_page_migrate(){
        $migrate_descript = '';
        $migrate_key = '';
        $migrate_part_type = '';
        $migrate_part_exec = '';
        $migrate_part_note = '';
        $migrate_part_tip = '';
        ?>
        <div id="migrate-page" class="wrap-tab-content wpvivid_tab_migrate" name="tab-migrate" style="display: none;">
            <div class="postbox wpvivid-element-space-bottom" style="padding: 10px;">
                <?php
                echo '<div style="padding: 0 0 10px 0;">
                    '.esc_html__('The feature can help you transfer a Wordpress site to a new domain(site). It would be a convenient way to migrate your WP site from dev environment to live server or from old server to the new.', 'wpvivid-backuprestore').'
                  </div>';
                do_action('wpvivid_put_transfer_key_output');
                ?>
            </div>

            <div class="postbox wpvivid-element-space-bottom" id="wpvivid_upload_backup_percent" style="display: none;">
                <div class="action-progress-bar" id="wpvivid_upload_progress_bar">
                    <div class="action-progress-bar-percent" id="wpvivid_upload_progress_bar_percent" style="height:24px;width:0"></div>
                </div>
                <div style="margin-left:10px; float: left; width:100%;"><p id="wpvivid_upload_current_doing"></p></div>
                <div style="clear: both;"></div>
                <div>
                    <div id="wpvivid_transfer_cancel" class="backup-log-btn"><input class="button-primary" id="wpvivid_transfer_cancel_btn" type="submit" value="<?php esc_attr_e( 'Cancel', 'wpvivid-backuprestore' ); ?>"  /></div>
                </div>
            </div>

            <div style="padding: 0 0 10px 0;">

                <?php
                $migrate_type = '';
                $type_name = 'transfer_type';
                echo '<div class="postbox quicktransfer">
                    <div class="wpvivid-element-space-bottom">
                        <h2 style="padding: 0;"><span>'.esc_html__( 'Choose the content you want to transfer', 'wpvivid-backuprestore').'</span></h2>
                    </div>
                    <div class="quickstart-archive-block">
                        <fieldset>
                            <legend class="screen-reader-text"><span>input type="radio"</span></legend>
                                ';
                do_action('wpvivid_add_migrate_type_output', $type_name);
                echo '
                        </fieldset>
                    </div>
                </div>';
                ?>

                <p><?php echo esc_html__('Note: ', 'wpvivid-backuprestore'); ?></p>
                <p>1. In order to successfully complete the migration, you'd better deactivate <a href="https://wpvivid.com/best-redirect-plugins.html" target="_blank" style="text-decoration: none;">301 redirect plugin</a>, <a href="https://wpvivid.com/8-best-wordpress-firewall-plugins.html" target="_blank" style="text-decoration: none;">firewall and security plugin</a>, and <a href="https://wpvivid.com/best-free-wordpress-caching-plugins.html" target="_blank" style="text-decoration: none;">caching plugin</a> (if they exist) before transferring website.</p>
                <p>2. Please migrate website with the manual way when using <strong>Local by Flywheel</strong> environment.</p>

                <div style="padding: 0 0 10px 0;">
                    <?php do_action('wpvivid_migrate_part_exec'); ?>
                </div>
                <div style="clear: both;"></div>
                <div style="padding: 10px 0 10px 0;">
                    <?php
                    $backupdir=WPvivid_Setting::get_backupdir();
                    echo '<p><strong>Tips: </strong>Some web hosts may restrict the connection between the two sites, so you may get a 403 error or unstable connection issue when performing auto migration. In that case, it is recommended to manually transfer the site</p>
                    <p><strong>'.esc_html__('How to migrate Wordpress site manually to a new domain(site) with WPvivid backup plugin?', 'wpvivid-backuprestore').'</strong></p>
                    <p>'.esc_html__('1. Download a backup in backups list to your computer.', 'wpvivid-backuprestore').'</p>
                    <p>'.esc_html__('2. Upload the backup to destination site. There are two ways available to use:', 'wpvivid-backuprestore').'</p>
                    <p style="margin-left: 20px;">'.esc_html__('2.1 Upload the backup to the upload section of WPvivid backup plugin in destination site.', 'wpvivid-backuprestore').'</p>
                    <p style="margin-left: 20px;">'.sprintf('2.2 Upload the backup with FTP client to backup directory %s in destination site, then click <strong>Scan uploaded backup or received backup</strong> button.', esc_html(WP_CONTENT_DIR.DIRECTORY_SEPARATOR.$backupdir)).'</p>
                    <p>'.esc_html__('3. Once done, the backup appears in backups list. Then, restore the backup.', 'wpvivid-backuprestore').'</p>';
                    ?>
                </div>
            </div>
        </div>
        <?php
        $js = '';
        apply_filters('wpvivid_load_migrate_js', $js);
        ?>
        <?php
    }

    public function wpvivid_add_page_key(){
        ?>
        <div id="key-page" class="wrap-tab-content wpvivid_tab_key" name="tab-key" style="display: none;">
            <div style="padding: 0 0 0 10px">
                <div style="padding: 0 0 10px 0">
                    <span><?php esc_html_e('In order to allow another site to send a backup to this site, please generate a key below. Once the key is generated, this site is ready to receive a backup from another site. Then, please copy and paste the key in sending site and save it.', 'wpvivid-backuprestore'); ?></span>
                </div>
                <strong><?php esc_html_e('The key will expire in ', 'wpvivid-backuprestore'); ?></strong>
                <select id="wpvivid_generate_url_expires" style="margin-bottom: 2px;">
                    <option value="2 hour"><?php esc_html_e('2 hours', 'wpvivid-backuprestore'); ?></option>
                    <option selected="selected" value="8 hour"><?php esc_html_e('8 hours', 'wpvivid-backuprestore'); ?></option>
                    <option value="24 hour"><?php esc_html_e('24 hours', 'wpvivid-backuprestore'); ?></option>
                    <!--<option value="Never">Never</option>-->
                </select>
                <p><?php esc_html_e('Tips: For security reason, please choose an appropriate expiration time for the key.', 'wpvivid-backuprestore'); ?></p>
                <div>
                    <input class="button-primary" id="wpvivid_generate_url" type="submit" value="<?php esc_attr_e( 'Generate', 'wpvivid-backuprestore' ); ?>" onclick="wpvivid_click_generate_url();" />
                </div>
                <div id="wpvivid_test_generate_url" style="padding-top: 10px;">
                    <textarea id="wpvivid_test_remote_site_url_text" style="width: 100%; height: 140px;"></textarea>
                </div>
            </div>
        </div>
        <script>
            jQuery("#wpvivid_test_remote_site_url_text").focus(function() {
                jQuery(this).select();
                jQuery(this).mouseup(function() {
                    jQuery(this).unbind("mouseup");
                    return false;
                });
            });

            function wpvivid_click_generate_url()
            {
                var expires=jQuery('#wpvivid_generate_url_expires').val();
                var ajax_data = {
                    'action': 'wpvivid_generate_url_ex',
                    'expires':expires
                };
                wpvivid_post_request(ajax_data, function (data)
                {
                    try
                    {
                        var jsonarray = jQuery.parseJSON(data);
                        if (jsonarray.result === 'success')
                        {
                            jQuery('#wpvivid_test_remote_site_url_text').val(jsonarray.url);
                        }
                        else
                        {
                            alert('Failed to generating key.');
                        }
                    }
                    catch (err)
                    {
                        alert(err);
                    }
                }, function (XMLHttpRequest, textStatus, errorThrown)
                {
                    var error_message = wpvivid_output_ajaxerror('generating key', textStatus, errorThrown);
                    alert(error_message);
                });
            }
        </script>
        <?php
    }

    public function test_connect_site()
    {
        if(isset($_POST['url']))
        {
            global $wpvivid_plugin;
            check_ajax_referer( 'wpvivid_ajax', 'nonce' );
            $check=current_user_can('manage_options');
            $check=apply_filters('wpvivid_ajax_check_security',$check);
            if(!$check)
            {
                die();
            }

            $url=strtok(sanitize_url($_POST['url']),'?');

            if (filter_var($url, FILTER_VALIDATE_URL) === FALSE)
            {
                $ret['result']=WPVIVID_FAILED;
                $ret['error']='The key is invalid.';
                echo wp_json_encode($ret);
                die();
            }

            if($url==home_url())
            {
                $ret['result']=WPVIVID_FAILED;
                $ret['error']='The key generated by this site cannot be added into this site.';
                echo wp_json_encode($ret);
                die();
            }

            $query=wp_parse_url (sanitize_url($_POST['url']),PHP_URL_QUERY);
            if($query===null)
            {
                $query=strtok('?');
            }
            parse_str($query,$query_arr);
            $token=$query_arr['token'];
            $expires=$query_arr['expires'];
            $domain=$query_arr['domain'];

            if ($expires != 0 && time() > $expires) {
                $ret['result'] = 'failed';
                $ret['error'] = 'The key has expired.';
                echo wp_json_encode($ret);
                die();
            }

            $json['test_connect']=1;
            $json=wp_json_encode($json);
            $crypt=new WPvivid_crypt(base64_decode($token));
            $data=$crypt->encrypt_message($json);
            if($data===false)
            {
                $ret['result'] = 'failed';
                $ret['error'] = 'Data encryption failed.';
                echo wp_json_encode($ret);
                die();
            }
            $data=base64_encode($data);
            
            $args['body']=array('wpvivid_content'=>$data,'wpvivid_action'=>'send_to_site_connect');
            $args['timeout']=30;
            $response=wp_remote_post($url,$args);

            if ( is_wp_error( $response ) )
            {
                $ret['result']=WPVIVID_FAILED;
                $ret['error']= $response->get_error_message();
            }
            else
            {
                if($response['response']['code']==200)
                {
                    $res=json_decode($response['body'],1);
                    if($res!=null)
                    {
                        if($res['result']==WPVIVID_SUCCESS)
                        {
                            $ret['result']=WPVIVID_SUCCESS;

                            $options=WPvivid_Setting::get_option('wpvivid_saved_api_token');

                            $options[$url]['token']=$token;
                            $options[$url]['url']=$url;
                            $options[$url]['expires']=$expires;
                            $options[$url]['domain']=$domain;

                            delete_option('wpvivid_saved_api_token');
                            WPvivid_Setting::update_option('wpvivid_saved_api_token',$options);

                            $html='';
                            $i=0;
                            foreach ($options as $key=>$site)
                            {
                                $check_status='';
                                if($key==$url)
                                {
                                    $check_status='checked';
                                }

                                if($site['expires']>time())
                                {
                                    $date=gmdate("l, F d, Y H:i", $site['expires']);
                                }
                                else
                                {
                                    $date='Token has expired';
                                }

                                $i++;
                                $html = apply_filters('wpvivid_put_transfer_key', $html);
                            }
                            $ret['html']= $html;

                        }
                        else
                        {
                            $ret['result']=WPVIVID_FAILED;
                            $ret['error']= $res['error'];
                        }
                    }
                    else
                    {
                        $ret['result']=WPVIVID_FAILED;
                        $ret['error']= $response['body'];
                        //$ret['error']= 'failed to parse returned data. Unable to retrieve the correct authorization data via HTTP request.';
                    }
                }
                else
                {
                    $ret['result']=WPVIVID_FAILED;
                    $ret['error']= 'upload error '.$response['response']['code'].' '.$response['body'];
                    //$response['body']
                }
            }

            echo wp_json_encode($ret);
        }
        die();
    }

    public function delete_transfer_key()
    {
        global $wpvivid_plugin;
        check_ajax_referer( 'wpvivid_ajax', 'nonce' );
        $check=current_user_can('manage_options');
        $check=apply_filters('wpvivid_ajax_check_security',$check);
        if(!$check)
        {
            die();
        }
        $ret['result']=WPVIVID_SUCCESS;
        delete_option('wpvivid_saved_api_token');
        $html='';
        $html = apply_filters('wpvivid_put_transfer_key', $html);
        $ret['html']=$html;
        echo wp_json_encode($ret);
        die();
    }

    public function send_backup_to_site()
    {
        try {
            global $wpvivid_plugin;
            check_ajax_referer( 'wpvivid_ajax', 'nonce' );
            $check=current_user_can('manage_options');
            $check=apply_filters('wpvivid_ajax_check_security',$check);
            if(!$check)
            {
                die();
            }

            $options = WPvivid_Setting::get_option('wpvivid_saved_api_token');

            if (empty($options)) {
                $ret['result'] = 'failed';
                $ret['error'] = __('A key is required.', 'wpvivid-backuprestore');
                echo wp_json_encode($ret);
                die();
            }

            $url = '';
            foreach ($options as $key => $value) {
                $url = $value['url'];
            }

            if ($url === '') {
                $ret['result'] = 'failed';
                $ret['error'] = __('The key is invalid.', 'wpvivid-backuprestore');
                echo wp_json_encode($ret);
                die();
            }

            if ($options[$url]['expires'] != 0 && $options[$url]['expires'] < time()) {
                $ret['result'] = 'failed';
                $ret['error'] =  __('The key has expired.', 'wpvivid-backuprestore');
                echo wp_json_encode($ret);
                die();
            }

            $json['test_connect']=1;
            $json=wp_json_encode($json);
            $crypt=new WPvivid_crypt(base64_decode($options[$url]['token']));
            $data=$crypt->encrypt_message($json);
            $data=base64_encode($data);
            $args['body']=array('wpvivid_content'=>$data,'wpvivid_action'=>'send_to_site_connect');
            $response=wp_remote_post($url,$args);

            if ( is_wp_error( $response ) )
            {
                $ret['result']=WPVIVID_FAILED;
                $ret['error']= $response->get_error_message();
                echo wp_json_encode($ret);
                die();
            }
            else
            {
                if($response['response']['code']==200) {
                    $res=json_decode($response['body'],1);
                    if($res!=null) {
                        if($res['result']==WPVIVID_SUCCESS) {
                        }
                        else {
                            $ret['result']=WPVIVID_FAILED;
                            $ret['error']= $res['error'];
                            echo wp_json_encode($ret);
                            die();
                        }
                    }
                    else {
                        $ret['result']=WPVIVID_FAILED;
                        $ret['error']= 'failed to parse returned data, unable to establish connection with the target site.';
                        $ret['response']=$response;
                        echo wp_json_encode($ret);
                        die();
                    }
                }
                else {
                    $ret['result']=WPVIVID_FAILED;
                    $ret['error']= 'upload error '.$response['response']['code'].' '.$response['body'];
                    echo wp_json_encode($ret);
                    die();
                }
            }

            if (WPvivid_taskmanager::is_tasks_backup_running()) {
                $ret['result'] = 'failed';
                $ret['error'] = __('A task is already running. Please wait until the running task is complete, and try again.', 'wpvivid-backuprestore');
                echo wp_json_encode($ret);
                die();
            }

            $remote_option['url'] = $options[$url]['url'];
            $remote_option['token'] = $options[$url]['token'];
            $remote_option['type'] = WPVIVID_REMOTE_SEND_TO_SITE;
            $remote_options['temp'] = $remote_option;

            $backup_options = stripslashes(sanitize_text_field($_POST['backup_options']));
            $backup_options = json_decode($backup_options, true);
            $backup['backup_files'] = $backup_options['transfer_type'];
            $backup['local'] = 0;
            $backup['remote'] = 1;
            $backup['ismerge'] = 1;
            $backup['lock'] = 0;
            $backup['remote_options'] = $remote_options;

            $backup_task = new WPvivid_Backup_Task();
            $ret = $backup_task->new_backup_task($backup, 'Manual', 'transfer');

            $task_id = $ret['task_id'];

            global $wpvivid_plugin;
            $wpvivid_plugin->check_backup($task_id, $backup);
            echo wp_json_encode($ret);
            die();
        }
        catch (Exception $e){
            $ret['result'] = 'failed';
            $ret['error'] = $e->getMessage();
            echo wp_json_encode($ret);
            die();
        }
    }

    public function migrate_now()
    {
        global $wpvivid_plugin;
        check_ajax_referer( 'wpvivid_ajax', 'nonce' );
        $check=current_user_can('manage_options');
        $check=apply_filters('wpvivid_ajax_check_security',$check);
        if(!$check)
        {
            die();
        }

        if (!isset($_POST['task_id'])||empty($_POST['task_id'])||!is_string($_POST['task_id']))
        {
            $ret['result']='failed';
            $ret['error']=__('Error occurred while parsing the request data. Please try to run backup again.', 'wpvivid-backuprestore');
            echo wp_json_encode($ret);
            die();
        }
        $task_id=sanitize_key($_POST['task_id']);

        //flush buffer
        $wpvivid_plugin->flush($task_id);
        $wpvivid_plugin->backup($task_id);
        die();
    }

    function export_download_backup()
    {
        global $wpvivid_plugin;
        check_ajax_referer( 'wpvivid_ajax', 'nonce' );
        $check=current_user_can('manage_options');
        $check=apply_filters('wpvivid_ajax_check_security',$check);
        if(!$check)
        {
            die();
        }

        $schedule_options=WPvivid_Schedule::get_schedule();
        if(empty($schedule_options))
        {
            die();
        }
        $backup_options = stripslashes(sanitize_text_field($_POST['backup_options']));
        $backup_options = json_decode($backup_options, true);
        $backup['backup_files']= $backup_options['transfer_type'];
        $backup['local']=1;
        $backup['remote']=0;
        $backup['ismerge']=1;
        $backup['lock']=0;
        //$backup['remote_options']='';

        $backup_task=new WPvivid_Backup_Task();
        $task=$backup_task->new_backup_task($backup,'Manual', 'export');

        $task_id=$task['task_id'];
        //add_action('wpvivid_handle_upload_succeed',array($this,'wpvivid_deal_upload_succeed'),11);
        $wpvivid_plugin->check_backup($task_id,$backup['backup_files']);
        $wpvivid_plugin->flush($task_id);
        $wpvivid_plugin->backup($task_id);
        //}
        die();
    }

    function wpvivid_handle_backup_failed($task)
    {
        global $wpvivid_plugin;
        if($task['action'] === 'transfer') {
            $backup_error_array = WPvivid_Setting::get_option('wpvivid_transfer_error_array');
            if (empty($backup_error_array)) {
                $backup_error_array = array();
            }
            if (!array_key_exists($task['id'], $backup_error_array['bu_error'])) {
                $backup_error_array['bu_error']['task_id'] = $task['id'];
                $backup_error_array['bu_error']['error_msg'] = $task['status']['error'];
                WPvivid_Setting::update_option('wpvivid_transfer_error_array', $backup_error_array);
            }
            $backup=new WPvivid_Backup($task['id']);
            $backup->clean_backup();
            $wpvivid_plugin->wpvivid_log->WriteLog('Upload failed. Delete task '.$task['id'], 'notice');
            WPvivid_Backuplist::delete_backup($task['id']);
        }
    }

    public function wpvivid_deal_upload_succeed($task)
    {
        global $wpvivid_plugin;
        if($task['action'] === 'transfer')
        {
            $backup_success_count = WPvivid_Setting::get_option('wpvivid_transfer_success_count');
            if (empty($backup_success_count))
            {
                $backup_success_count = 0;
            }
            $backup_success_count++;
            WPvivid_Setting::update_option('wpvivid_transfer_success_count', $backup_success_count);

            $wpvivid_plugin->wpvivid_log->WriteLog('Upload finished. Delete task '.$task['id'], 'notice');
            WPvivid_Backuplist::delete_backup($task['id']);
        }
    }

    public function generate_url()
    {
        global $wpvivid_plugin;
        check_ajax_referer( 'wpvivid_ajax', 'nonce' );
        $check=current_user_can('manage_options');
        $check=apply_filters('wpvivid_ajax_check_security',$check);
        if(!$check)
        {
            die();
        }

        include_once WPVIVID_PLUGIN_DIR . '/vendor/autoload.php';

        $expires=time()+3600;

        if(isset($_POST['expires']))
        {
            $expires_display=sanitize_text_field($_POST['expires']);
            if($expires_display=='1 month')
            {
                $expires=time()+2592000;
            }
            else if($expires_display=='1 day')
            {
                $expires=time()+86400;
            }
            else if($expires_display=='2 hour')
            {
                $expires=time()+7200;
            }
            else if($expires_display=='8 hour')
            {
                $expires=time()+28800;
            }
            else if($expires_display=='24 hour')
            {
                $expires=time()+86400;
            }
            else if($expires_display=='Never')
            {
                $expires=0;
            }
        }

        $key_size = 2048;
        $rsa = new Crypt_RSA();
        $keys = $rsa->createKey($key_size);
        $options['public_key']=base64_encode($keys['publickey']);
        $options['private_key']=base64_encode($keys['privatekey']);
        $options['expires']=$expires;
        $options['domain']=home_url();

        WPvivid_Setting::update_option('wpvivid_api_token',$options);

        $url= $options['domain'];
        $url=$url.'?domain='.$options['domain'].'&token='.$options['public_key'].'&expires='.$expires;
        echo $url;
        die();
    }

    public function generate_url_ex()
    {
        global $wpvivid_plugin;
        check_ajax_referer( 'wpvivid_ajax', 'nonce' );
        $check=current_user_can('manage_options');
        $check=apply_filters('wpvivid_ajax_check_security',$check);
        if(!$check)
        {
            die();
        }

        include_once WPVIVID_PLUGIN_DIR . '/vendor/autoload.php';

        $expires=time()+3600;

        if(isset($_POST['expires']))
        {
            $expires_display=sanitize_text_field($_POST['expires']);
            if($expires_display=='1 month')
            {
                $expires=time()+2592000;
            }
            else if($expires_display=='1 day')
            {
                $expires=time()+86400;
            }
            else if($expires_display=='2 hour')
            {
                $expires=time()+7200;
            }
            else if($expires_display=='8 hour')
            {
                $expires=time()+28800;
            }
            else if($expires_display=='24 hour')
            {
                $expires=time()+86400;
            }
            else if($expires_display=='Never')
            {
                $expires=0;
            }
        }

        $key_size = 2048;
        $rsa = new Crypt_RSA();
        $keys = $rsa->createKey($key_size);
        $options['public_key']=base64_encode($keys['publickey']);
        $options['private_key']=base64_encode($keys['privatekey']);
        $options['expires']=$expires;
        $options['domain']=home_url();

        WPvivid_Setting::update_option('wpvivid_api_token',$options);

        $url= $options['domain'];
        $url=$url.'?domain='.$options['domain'].'&token='.$options['public_key'].'&expires='.$expires;

        $ret['result']='success';
        $ret['url']=$url;
        echo wp_json_encode($ret);
        die();
    }

    public function wpvivid_put_transfer_key_output()
    {
        echo  '<div id="wpvivid_transfer_key">';
        $options=WPvivid_Setting::get_option('wpvivid_saved_api_token');
        if(empty($options))
        {
            echo '<div style="padding: 0 0 10px 0;"><strong>'.esc_html__('Please paste the key below.', 'wpvivid-backuprestore').'</strong><a href="#" style="margin-left: 5px; text-decoration: none;" onclick="wpvivid_click_how_to_get_key();">'.esc_html__('How to get a site key?', 'wpvivid-backuprestore').'</a></div>
            <div id="wpvivid_how_to_get_key"></div>
            <div class="wpvivid-element-space-bottom"><textarea type="text" id="wpvivid_transfer_key_text" onKeyUp="wpvivid_check_key(this.value)" style="width: 100%; height: 140px;"/></textarea></div>
            <div><input class="button-primary" id="wpvivid_save_url_button" type="submit" value="'.esc_attr__( 'Save', 'wpvivid-backuprestore' ).'" onclick="wpvivid_click_save_site_url();" /></div>';
        }
        else{
            foreach ($options as $key => $value)
            {
                $token = $value['token'];
                $source_dir=home_url();
                $target_dir=$value['domain'];
                $expires=$value['expires'];

                if ($expires != 0 && time() > $expires) {
                    $key_status='The key has expired. Please delete it first and generate a new one.';
                }
                else{
                    $time_diff = $expires - time();
                    $key_status = 'The key will expire in: '.gmdate("H:i:s",$time_diff).'. Once the key expires, you need to generate a new key.';
                }
            }
            echo '<div style="padding: 0 0 10px 0;">
                        <span>Key:</span>
                        <input type="text" id="wpvivid_send_remote_site_url_text" value="'.esc_attr($token).'" readonly="readonly" />
                        <input class="button-primary" id="wpvivid_delete_key_button" type="submit" value="'.esc_attr__( 'Delete', 'wpvivid-backuprestore' ).'" onclick="wpvivid_click_delete_transfer_key();" />
                       </div>
                       <div class="wpvivid-element-space-bottom">'.esc_html($key_status).'</div>
                       <div>The connection is ok. Now you can transfer the site <strong>'.esc_html($source_dir).'</strong> to the site <strong>'.esc_html($target_dir).'</strong></div>';
        }
        ?>
        </div>
        <script>
         var source_site = "<?php echo esc_url(admin_url('admin-ajax.php'))?>";
        function wpvivid_check_key(value){
                var pos = value.indexOf("?");
                var site_url = value.substring(0, pos);
                if(site_url == source_site){
                    alert("The key generated by this site cannot be added into this site.");
                    jQuery('#wpvivid_save_url_button').prop('disabled', true);
                }
                else{
                    jQuery("#wpvivid_save_url_button").prop('disabled', false);
                }
            }

            function wpvivid_click_save_site_url()
            {
                var url= jQuery('#wpvivid_transfer_key_text').val();
                var ajax_data = {
                    'action': 'wpvivid_test_connect_site',
                    'url':url
                };

                jQuery("#wpvivid_save_url_button").prop('disabled', true);
                wpvivid_post_request(ajax_data, function (data)
                {
                    jQuery("#wpvivid_save_url_button").prop('disabled', false);
                    try
                    {
                        var jsonarray = jQuery.parseJSON(data);
                        if(jsonarray.result==='success')
                        {
                            jQuery('#wpvivid_transfer_key').html(jsonarray.html);
                        }
                        else
                        {
                            alert(jsonarray.error);
                        }
                    }
                    catch(err)
                    {
                        alert(err);
                    }
                }, function (XMLHttpRequest, textStatus, errorThrown)
                {
                    jQuery("#wpvivid_save_url_button").prop('disabled', false);
                    var error_message = wpvivid_output_ajaxerror('saving key', textStatus, errorThrown);
                    alert(error_message);
                });
            }

            function wpvivid_click_delete_transfer_key()
            {
                var ajax_data = {
                    'action': 'wpvivid_delete_transfer_key'
                };

                jQuery("#wpvivid_delete_key_button").css({'pointer-events': 'none', 'opacity': '0.4'});
                wpvivid_post_request(ajax_data, function (data)
                {
                    jQuery("#wpvivid_delete_key_button").css({'pointer-events': 'none', 'opacity': '0.4'});
                    try
                    {
                        var jsonarray = jQuery.parseJSON(data);
                        if(jsonarray.result==='success')
                        {
                            jQuery('#wpvivid_transfer_key').html(jsonarray.html);
                        }
                    }
                    catch(err)
                    {
                        alert(err);
                    }
                }, function (XMLHttpRequest, textStatus, errorThrown)
                {
                    jQuery("#wpvivid_delete_key_button").css({'pointer-events': 'auto', 'opacity': '1'});
                    var error_message = wpvivid_output_ajaxerror('deleting key', textStatus, errorThrown);
                    alert(error_message);
                });
            }

            function click_dismiss_key_notice(obj){
                wpvivid_display_get_key = false;
                jQuery(obj).parent().remove();
            }

            function wpvivid_click_how_to_get_key(){
                if(!wpvivid_display_get_key) {
                    wpvivid_display_get_key = true;
                    var div = "<div class='notice notice-info is-dismissible inline'>" +
                        "<p>" + wpvividlion.get_key_step1 + "</p>" +
                        "<p>" + wpvividlion.get_key_step2 + "</p>" +
                        "<p>" + wpvividlion.get_key_step3 + "</p>" +
                        "<button type='button' class='notice-dismiss' onclick='click_dismiss_key_notice(this);'>" +
                        "<span class='screen-reader-text'>Dismiss this notice.</span>" +
                        "</button>" +
                        "</div>";
                    jQuery('#wpvivid_how_to_get_key').append(div);
                }
            }
        </script>
        <?php
    }

    public function wpvivid_put_transfer_key($html){
        $html='<div id="wpvivid_transfer_key">';
        $options=WPvivid_Setting::get_option('wpvivid_saved_api_token');
        if(empty($options)){
            $html .= '<div style="padding: 0 0 10px 0;"><strong>'.__('Please paste the key below.', 'wpvivid-backuprestore').'</strong><a href="#" style="margin-left: 5px; text-decoration: none;" onclick="wpvivid_click_how_to_get_key();">'.__('How to get a site key?', 'wpvivid-backuprestore').'</a></div>
            <div id="wpvivid_how_to_get_key"></div>
            <div class="wpvivid-element-space-bottom"><textarea type="text" id="wpvivid_transfer_key_text" onKeyUp="wpvivid_check_key(this.value)" style="width: 100%; height: 140px;"/></textarea></div>
            <div><input class="button-primary" id="wpvivid_save_url_button" type="submit" value="'.esc_attr__( 'Save', 'wpvivid-backuprestore' ).'" onclick="wpvivid_click_save_site_url();" /></div>';
        }
        else{
            foreach ($options as $key => $value)
            {
                $token = $value['token'];
                $source_dir=home_url();
                $target_dir=$value['domain'];
                $expires=$value['expires'];

                if ($expires != 0 && time() > $expires) {
                    $key_status='The key has expired. Please delete it first and generate a new one.';
                }
                else{
                    $time_diff = $expires - time();
                    $key_status = 'The key will expire in: '.gmdate("H:i:s",$time_diff).'. Once the key expires, you need to generate a new key.';
                }
            }
            $html .= '<div style="padding: 0 0 10px 0;">
                        <span>Key:</span>
                        <input type="text" id="wpvivid_send_remote_site_url_text" value="'.$token.'" readonly="readonly" />
                        <input class="button-primary" id="wpvivid_delete_key_button" type="submit" value="'.esc_attr__( 'Delete', 'wpvivid-backuprestore' ).'" onclick="wpvivid_click_delete_transfer_key();" />
                       </div>
                       <div class="wpvivid-element-space-bottom">'.$key_status.'</div>
                       <div>The connection is ok. Now you can transfer the site <strong>'.$source_dir.'</strong> to the site <strong>'.$target_dir.'</strong></div>';
        }
        $html.='</div>
        <script>
         var source_site = \''.admin_url('admin-ajax.php').'\';
        function wpvivid_check_key(value){
                var pos = value.indexOf(\'?\');
                var site_url = value.substring(0, pos);
                if(site_url == source_site){
                    alert(\'The key generated by this site cannot be added into this site.\');
                    jQuery(\'#wpvivid_save_url_button\').prop(\'disabled\', true);
                }
                else{
                    jQuery("#wpvivid_save_url_button").prop(\'disabled\', false);
                }
            }

            function wpvivid_click_save_site_url()
            {
                var url= jQuery(\'#wpvivid_transfer_key_text\').val();
                var ajax_data = {
                    \'action\': \'wpvivid_test_connect_site\',
                    \'url\':url
                };

                jQuery("#wpvivid_save_url_button").prop(\'disabled\', true);
                wpvivid_post_request(ajax_data, function (data)
                {
                    jQuery("#wpvivid_save_url_button").prop(\'disabled\', false);
                    try
                    {
                        var jsonarray = jQuery.parseJSON(data);
                        if(jsonarray.result===\'success\')
                        {
                            jQuery(\'#wpvivid_transfer_key\').html(jsonarray.html);
                        }
                        else
                        {
                            alert(jsonarray.error);
                        }
                    }
                    catch(err)
                    {
                        alert(err);
                    }
                }, function (XMLHttpRequest, textStatus, errorThrown)
                {
                    jQuery("#wpvivid_save_url_button").prop(\'disabled\', false);
                    var error_message = wpvivid_output_ajaxerror(\'saving key\', textStatus, errorThrown);
                    alert(error_message);
                });
            }

            function wpvivid_click_delete_transfer_key()
            {
                var ajax_data = {
                    \'action\': \'wpvivid_delete_transfer_key\'
                };

                jQuery("#wpvivid_delete_key_button").css({\'pointer-events\': \'none\', \'opacity\': \'0.4\'});
                wpvivid_post_request(ajax_data, function (data)
                {
                    jQuery("#wpvivid_delete_key_button").css({\'pointer-events\': \'none\', \'opacity\': \'0.4\'});
                    try
                    {
                        var jsonarray = jQuery.parseJSON(data);
                        if(jsonarray.result===\'success\')
                        {
                            jQuery(\'#wpvivid_transfer_key\').html(jsonarray.html);
                        }
                    }
                    catch(err)
                    {
                        alert(err);
                    }
                }, function (XMLHttpRequest, textStatus, errorThrown)
                {
                    jQuery("#wpvivid_delete_key_button").css({\'pointer-events\': \'auto\', \'opacity\': \'1\'});
                    var error_message = wpvivid_output_ajaxerror(\'deleting key\', textStatus, errorThrown);
                    alert(error_message);
                });
            }

            function click_dismiss_key_notice(obj){
                wpvivid_display_get_key = false;
                jQuery(obj).parent().remove();
            }

            function wpvivid_click_how_to_get_key(){
                if(!wpvivid_display_get_key) {
                    wpvivid_display_get_key = true;
                    var div = "<div class=\'notice notice-info is-dismissible inline\'>" +
                        "<p>" + wpvividlion.get_key_step1 + "</p>" +
                        "<p>" + wpvividlion.get_key_step2 + "</p>" +
                        "<p>" + wpvividlion.get_key_step3 + "</p>" +
                        "<button type=\'button\' class=\'notice-dismiss\' onclick=\'click_dismiss_key_notice(this);\'>" +
                        "<span class=\'screen-reader-text\'>Dismiss this notice.</span>" +
                        "</button>" +
                        "</div>";
                    jQuery(\'#wpvivid_how_to_get_key\').append(div);
                }
            }
        </script>';
        return $html;
    }

    public function wpvivid_migrate_descript($html){
        $html .= '<div style="padding: 0 0 10px 0;">
                    '.__('The feature can help you transfer a Wordpress site to a new domain(site). It would be a convenient way to migrate your WP site from dev environment to live server or from old server to the new.', 'wpvivid-backuprestore').'
                  </div>';
        return $html;
    }

    public function wpvivid_migrate_part_type($html){
        $migrate_type = '';
        $type_name = 'transfer_type';
        $html = '<div class="postbox quicktransfer">
                    <div class="wpvivid-element-space-bottom">
                        <h2 style="padding: 0;"><span>'.__( 'Choose the content you want to transfer', 'wpvivid-backuprestore').'</span></h2>
                    </div>
                    <div class="quickstart-archive-block">
                        <fieldset>
                            <legend class="screen-reader-text"><span>input type="radio"</span></legend>
                                '.apply_filters('wpvivid_add_migrate_type', $migrate_type, $type_name).'
                        </fieldset>
                    </div>
                </div>';
        return $html;
    }

    public function wpvivid_migrate_part_exec($html)
    {
        ?>
        <div id="wpvivid_transfer_btn" style="float: left;">
            <input class="button-primary quicktransfer-btn" type="submit" value="<?php esc_attr_e( 'Clone then Transfer', 'wpvivid-backuprestore'); ?>" onclick="wpvivid_click_send_backup();" />
        </div>
        <script>
            function wpvivid_click_send_backup()
            {
                //send_to_remote
                var option_data = wpvivid_ajax_data_transfer('migrate');
                var ajax_data = {
                    'action': 'wpvivid_send_backup_to_site_2',
                    'backup_options':option_data
                };
                migrate_task_need_update=true;
                wpvivid_clear_notice('wpvivid_backup_notice');
                wpvivid_control_transfer_lock();
                wpvivid_post_request(ajax_data, function (data)
                {
                    try
                    {
                        var jsonarray = jQuery.parseJSON(data);
                        if(jsonarray.result==='failed')
                        {
                            wpvivid_delete_transfer_ready_task(jsonarray.error);
                        }
                        else{
                            wpvivid_transfer_id = jsonarray.task_id;
                            wpvivid_migrate_now(jsonarray.task_id);
                        }
                    }
                    catch(err)
                    {
                        wpvivid_delete_transfer_ready_task(err);
                    }
                }, function (XMLHttpRequest, textStatus, errorThrown)
                {
                    var error_message = wpvivid_output_ajaxerror('trying to establish communication with your server', textStatus, errorThrown);
                    wpvivid_delete_transfer_ready_task(error_message);
                });
            }

            function wpvivid_migrate_now(task_id)
            {
                var ajax_data = {
                    'action': 'wpvivid_migrate_now_2',
                    'task_id': task_id
                };
                task_recheck_times = 0;
                m_need_update_2=true;
                wpvivid_post_request(ajax_data, function(data){
                }, function(XMLHttpRequest, textStatus, errorThrown) {
                });
            }

            function wpvivid_delete_transfer_ready_task(error){
                var ajax_data={
                    'action': 'wpvivid_delete_ready_task'
                };
                wpvivid_post_request(ajax_data, function (data) {
                    try {
                        var jsonarray = jQuery.parseJSON(data);
                        if (jsonarray.result === 'success') {
                            wpvivid_add_notice('Backup', 'Error', error);
                            wpvivid_control_transfer_unlock();
                            jQuery('#wpvivid_upload_backup_percent').hide();
                        }
                    }
                    catch(err){
                        wpvivid_add_notice('Backup', 'Error', err);
                        wpvivid_control_transfer_unlock();
                        jQuery('#wpvivid_upload_backup_percent').hide();
                    }
                }, function (XMLHttpRequest, textStatus, errorThrown) {
                    setTimeout(function () {
                        wpvivid_delete_transfer_ready_task(error);
                    }, 3000);
                });
            }
        </script>
        <?php
    }

    public function wpvivid_migrate_part_note($html){
        $html .= '<p>'.__('Note: ', 'wpvivid-backuprestore').'</p>
                <p>'.__('1. In order to successfully complete the migration, you\'d better deactivate <a href="https://wpvivid.com/best-redirect-plugins.html" target="_blank" style="text-decoration: none;">301 redirect plugin</a>, <a href="https://wpvivid.com/8-best-wordpress-firewall-plugins.html" target="_blank" style="text-decoration: none;">firewall and security plugin</a>, and <a href="https://wpvivid.com/best-free-wordpress-caching-plugins.html" target="_blank" style="text-decoration: none;">caching plugin</a> (if they exist) before transferring website.', 'wpvivid-backuprestore').'</p>
                <p>'.__('2. Please migrate website with the manual way when using <strong>Local by Flywheel</strong> environment.', 'wpvivid-backuprestore').'</p>';
        return $html;
    }

    public function wpvivid_migrate_part_tip($html){
        $backupdir=WPvivid_Setting::get_backupdir();
        $html .= '<p>'.__('<strong>Tips: </strong>Some web hosts may restrict the connection between the two sites, so you may get a 403 error or unstable connection issue when performing auto migration. In that case, it is recommended to manually transfer the site.', 'wpvivid-backuprestore').'</p>
                    <p><strong>'.__('How to migrate Wordpress site manually to a new domain(site) with WPvivid backup plugin?', 'wpvivid-backuprestore').'</strong></p>
                    <p>'.__('1. Download a backup in backups list to your computer.', 'wpvivid-backuprestore').'</p>
                    <p>'.__('2. Upload the backup to destination site. There are two ways available to use:', 'wpvivid-backuprestore').'</p>
                    <p style="margin-left: 20px;">'.__('2.1 Upload the backup to the upload section of WPvivid backup plugin in destination site.', 'wpvivid-backuprestore').'</p>
                    <p style="margin-left: 20px;">'.sprintf('2.2 Upload the backup with FTP client to backup directory %s in destination site, then click <strong>Scan uploaded backup or received backup</strong> button.', WP_CONTENT_DIR.DIRECTORY_SEPARATOR.$backupdir).'</p>
                    <p>'.__('3. Once done, the backup appears in backups list. Then, restore the backup.', 'wpvivid-backuprestore').'</p>';
        return $html;
    }

    public function wpvivid_add_migrate_type_output($name_type)
    {
        echo '<label>
                    <input type="radio" option="migrate" name="'.esc_attr($name_type).'" value="files+db" checked />
                    <span>'.esc_html__( 'Database + Files (WordPress Files)', 'wpvivid-backuprestore' ).'</span>
                  </label><br>
                  <label>
                    <input type="radio" option="migrate" name="'.esc_attr($name_type).'" value="files" />
                    <span>'.esc_html__( 'WordPress Files (Exclude Database)', 'wpvivid-backuprestore' ).'</span>
                  </label><br>
                  <label>
                    <input type="radio" option="migrate" name="'.esc_attr($name_type).'" value="db" />
                    <span>'.esc_html__( 'Only Database', 'wpvivid-backuprestore' ).'</span>
                  </label><br>
                  <label>
                   <div style="float: left;">
                        <input type="radio" disabled />
                        <span class="wpvivid-element-space-right" style="color: #ddd;">'.esc_html__('Choose what to migrate', 'wpvivid-backuprestore').'</span>
                    </div>
                    <span class="wpvivid-feature-pro">
                        <a href="https://docs.wpvivid.com/custom-migration-overview.html" style="text-decoration: none;">'.esc_html__('Pro feature: learn more', 'wpvivid-backuprestore').'</a>
                    </span>
                  </label><br>';
    }

    public function wpvivid_add_migrate_type($html, $name_type){
        $html .= '<label>
                    <input type="radio" option="migrate" name="'.$name_type.'" value="files+db" checked />
                    <span>'.__( 'Database + Files (WordPress Files)', 'wpvivid-backuprestore' ).'</span>
                  </label><br>
                  <label>
                    <input type="radio" option="migrate" name="'.$name_type.'" value="files" />
                    <span>'.__( 'WordPress Files (Exclude Database)', 'wpvivid-backuprestore' ).'</span>
                  </label><br>
                  <label>
                    <input type="radio" option="migrate" name="'.$name_type.'" value="db" />
                    <span>'.__( 'Only Database', 'wpvivid-backuprestore' ).'</span>
                  </label><br>
                  <label>
                   <div style="float: left;">
                        <input type="radio" disabled />
                        <span class="wpvivid-element-space-right" style="color: #ddd;">'.__('Choose what to migrate', 'wpvivid-backuprestore').'</span>
                    </div>
                    <span class="wpvivid-feature-pro">
                        <a href="https://docs.wpvivid.com/custom-migration-overview.html" style="text-decoration: none;">'.__('Pro feature: learn more', 'wpvivid-backuprestore').'</a>
                    </span>
                  </label><br>';
        return $html;
    }

    public function list_tasks()
    {
        global $wpvivid_plugin;
        check_ajax_referer( 'wpvivid_ajax', 'nonce' );
        $check=current_user_can('manage_options');
        $check=apply_filters('wpvivid_ajax_check_security',$check);
        if(!$check)
        {
            die();
        }
        $tasks=WPvivid_Setting::get_tasks();
        $ret=array();
        $list_tasks=array();
        foreach ($tasks as $task)
        {
            if($task['action']=='transfer')
            {
                $backup=new WPvivid_Backup_Task($task['id']);
                $list_tasks[$task['id']]=$backup->get_backup_task_info($task['id']);
                if($list_tasks[$task['id']]['task_info']['need_next_schedule']===true){
                    $timestamp = wp_next_scheduled(WPVIVID_TASK_MONITOR_EVENT,array($task['id']));

                    if($timestamp===false)
                    {
                        $wpvivid_plugin->add_monitor_event($task['id'],20);
                    }
                }
                if($list_tasks[$task['id']]['task_info']['need_update_last_task']===true){
                    $task_msg = WPvivid_taskmanager::get_task($task['id']);
                    $wpvivid_plugin->update_last_backup_task($task_msg);
                }
                //<div id="wpvivid_estimate_backup_info" style="float:left;">
                //                            <div class="backup-basic-info"><span class="wpvivid-element-space-right">' . __('Database Size:', 'wpvivid-backuprestore') . '</span><span>' . $list_tasks[$task['id']]['task_info']['db_size'] . '</span></div>
                //                            <div class="backup-basic-info"><span class="wpvivid-element-space-right">' . __('File Size:', 'wpvivid-backuprestore') . '</span><span>' . $list_tasks[$task['id']]['task_info']['file_size'] . '</span></div>
                //                         </div>
                $list_tasks[$task['id']]['progress_html'] = '<div class="action-progress-bar" id="wpvivid_upload_progress_bar">
                            <div class="action-progress-bar-percent" id="wpvivid_upload_progress_bar_percent" style="height:24px;width:' . $list_tasks[$task['id']]['task_info']['backup_percent'] . '"></div>
                         </div>
                         <div id="wpvivid_estimate_upload_info" style="float: left;"> 
                            <div class="backup-basic-info"><span class="wpvivid-element-space-right">' . __('Total Size:', 'wpvivid-backuprestore') . '</span><span>' . $list_tasks[$task['id']]['task_info']['total'] . '</span></div>
                            <div class="backup-basic-info"><span class="wpvivid-element-space-right">' . __('Uploaded:', 'wpvivid-backuprestore') . '</span><span>' . $list_tasks[$task['id']]['task_info']['upload'] . '</span></div>
                            <div class="backup-basic-info"><span class="wpvivid-element-space-right">' . __('Speed:', 'wpvivid-backuprestore') . '</span><span>' . $list_tasks[$task['id']]['task_info']['speed'] . '</span></div>
                         </div>
                         <div style="float: left;">
                            <div class="backup-basic-info"><span class="wpvivid-element-space-right">' . __('Network Connection:', 'wpvivid-backuprestore') . '</span><span>' . $list_tasks[$task['id']]['task_info']['network_connection'] . '</span></div>
                         </div>
                         <div style="clear:both;"></div>
                         <div style="margin-left:10px; float: left; width:100%;"><p id="wpvivid_upload_current_doing">' . $list_tasks[$task['id']]['task_info']['descript'] . '</p></div>
                         <div style="clear: both;"></div>
                         <div>
                            <div id="wpvivid_transfer_cancel" class="backup-log-btn"><input class="button-primary" id="wpvivid_transfer_cancel_btn" type="submit" value="'.esc_attr__( 'Cancel', 'wpvivid-backuprestore' ).'"  /></div>
                         </div>';
            }
        }
        WPvivid_taskmanager::delete_marked_task();

        $backup_success_count=WPvivid_Setting::get_option('wpvivid_transfer_success_count');
        if(!empty($backup_success_count)){
            $notice_msg = __('Transfer succeeded. Please scan the backup list on the destination site to display the backup, then restore the backup.', 'wpvivid-backuprestore');
            $success_notice_html='<div class="notice notice-success is-dismissible inline"><p>'.$notice_msg.'</p>
                                    <button type="button" class="notice-dismiss" onclick="click_dismiss_notice(this);">
                                    <span class="screen-reader-text">Dismiss this notice.</span>
                                    </button>
                                    </div>';
            WPvivid_Setting::delete_option('wpvivid_transfer_success_count');
        }
        else {
            $success_notice_html = false;
        }
        $ret['transfer_succeed_notice'] = $success_notice_html;

        $backup_error_array=WPvivid_Setting::get_option('wpvivid_transfer_error_array');
        if(!empty($backup_error_array)){
            $error_notice_html = array();
            foreach ($backup_error_array as $key => $value){
                $notice_msg = 'Transfer failed, '.$value['error_msg'];
                $error_notice_html['bu_error']['task_id']=$value['task_id'];
                $error_notice_html['bu_error']['error_msg']='<div class="notice notice-error inline"><p>'.$notice_msg.'</p></div>';
            }
            WPvivid_Setting::delete_option('wpvivid_transfer_error_array');
        }
        else{
            $error_notice_html = false;
        }
        $ret['transfer_error_notice'] = $error_notice_html;

        $ret['transfer']['result']='success';
        $ret['transfer']['data']=$list_tasks;

        if(!empty($task_ids))
        {
            foreach ($task_ids as $id)
            {
                WPvivid_Setting::delete_task($id);
            }
        }

        echo wp_json_encode($ret);
        die();
    }

    function wpvivid_rescan_backup_list(){
        ?>
        <div style="padding: 0 0 10px 0;">
            <?php
            Wpvivid_BackupUploader::rescan_local_folder();
            ?>
        </div>
        <?php
    }

    public function wpvivid_backuppage_load_backuplist($backuplist_array){
        $backuplist_array['list_upload'] = array('index' => '2', 'tab_func' => array($this, 'wpvivid_add_tab_upload'), 'page_func' => array($this, 'wpvivid_add_page_upload'));
        return $backuplist_array;
    }

    function wpvivid_add_tab_upload(){
        ?>
        <a href="#" id="wpvivid_tab_upload" class="nav-tab backup-nav-tab" onclick="switchrestoreTabs(event,'page-upload')"><?php esc_html_e('Upload', 'wpvivid-backuprestore'); ?></a>
        <?php
    }

    function wpvivid_add_page_upload(){
        $backupdir=WPvivid_Setting::get_backupdir();
        ?>
        <div class="backup-tab-content wpvivid_tab_upload" id="page-upload" style="display:none;">
            <div style="padding: 10px 0 10px 0;">
                <div style="padding-bottom: 10px;">
                    <span><?php echo esc_html(sprintf('The backups will be uploaded to %s directory.', WP_CONTENT_DIR.DIRECTORY_SEPARATOR.$backupdir)); ?></span>
                </div>
                <div style="padding-bottom: 10px;">
                    <span><?php esc_html_e('Note: The files you want to upload must be a backup created by WPvivid backup plugin. Make sure that uploading every part of a backup to the directory if the backup is split into many parts', 'wpvivid-backuprestore'); ?></span>
                </div>
                <?php
                Wpvivid_BackupUploader::upload_meta_box();
                ?>
            </div>
        </div>
        <?php
    }
}includes/class-wpvivid-upload.php000064400000013757151327705670013172 0ustar00<?php

if (!defined('WPVIVID_PLUGIN_DIR'))
{
    die;
}

class WPvivid_Upload
{
    public $task_id;

    public function upload($task_id,$remote_option=null)
    {
        global $wpvivid_plugin;
        $this->task_id=$task_id;
        $task=new WPvivid_Backup_Task($task_id);
        $files=$task->get_backup_files();
        WPvivid_taskmanager::update_backup_main_task_progress($this->task_id,'upload',0,0);

        if(is_null($remote_option))
        {
            $remote_options=WPvivid_taskmanager::get_task_options($this->task_id,'remote_options');

            if(sizeof($remote_options)>1)
            {
                $result=array('result' => WPVIVID_FAILED , 'error' => 'not support multi remote storage');
                $result= apply_filters('wpvivid_upload_files_to_multi_remote',$result,$task_id);

                if($result['result']==WPVIVID_SUCCESS)
                {
                    WPvivid_taskmanager::update_backup_main_task_progress($this->task_id,'upload',100,1);
                    WPvivid_taskmanager::update_backup_task_status($task_id,false,'completed');
                    return array('result' => WPVIVID_SUCCESS);
                }
                else
                {
                    WPvivid_taskmanager::update_backup_task_status($this->task_id,false,'error',false,false,$result['error']);
                    return array('result' => WPVIVID_FAILED , 'error' => $result['error']);
                }
            }
            else
            {
                $remote_option=array_shift($remote_options);

                if(is_null($remote_option))
                {
                    return array('result' => WPVIVID_FAILED , 'error' => 'not select remote storage');
                }

                if(!class_exists('WPvivid_Remote_collection'))
                {
                    include_once WPVIVID_PLUGIN_DIR . '/includes/class-wpvivid-remote-collection.php';
                    $wpvivid_plugin->remote_collection=new WPvivid_Remote_collection();
                }
                $remote=$wpvivid_plugin->remote_collection->get_remote($remote_option);

                $result=$remote->upload($this->task_id,$files,array($this,'upload_callback'));

                if($result['result']==WPVIVID_SUCCESS)
                {
                    WPvivid_taskmanager::update_backup_main_task_progress($this->task_id,'upload',100,1);
                    WPvivid_taskmanager::update_backup_task_status($task_id,false,'completed');
                    return array('result' => WPVIVID_SUCCESS);
                }
                else
                {
                    $remote ->cleanup($files);

                    WPvivid_taskmanager::update_backup_task_status($this->task_id,false,'error',false,false,$result['error']);
                    return array('result' => WPVIVID_FAILED , 'error' => $result['error']);
                }
            }
        }
        else
        {
            if(!class_exists('WPvivid_Remote_collection'))
            {
                include_once WPVIVID_PLUGIN_DIR . '/includes/class-wpvivid-remote-collection.php';
                $wpvivid_plugin->remote_collection=new WPvivid_Remote_collection();
            }
            $remote=$wpvivid_plugin->remote_collection->get_remote($remote_option);

            $result=$remote->upload($this->task_id,$files,array($this,'upload_callback'));

            if($result['result']==WPVIVID_SUCCESS)
            {
                WPvivid_taskmanager::update_backup_main_task_progress($this->task_id,'upload',100,1);
                WPvivid_taskmanager::update_backup_task_status($task_id,false,'completed');
                return array('result' => WPVIVID_SUCCESS);
            }
            else
            {
                $remote ->cleanup($files);

                WPvivid_taskmanager::update_backup_task_status($this->task_id,false,'error',false,false,$result['error']);
                return array('result' => WPVIVID_FAILED , 'error' => $result['error']);
            }
        }
    }

    public function upload_callback($offset,$current_name,$current_size,$last_time,$last_size)
    {
        $job_data=array();
        $upload_data=array();
        $upload_data['offset']=$offset;
        $upload_data['current_name']=$current_name;
        $upload_data['current_size']=$current_size;
        $upload_data['last_time']=$last_time;
        $upload_data['last_size']=$last_size;
        $upload_data['descript']='Uploading '.$current_name;
        $v =( $offset - $last_size ) / (time() - $last_time);
        $v /= 1000;
        $v=round($v,2);

        global $wpvivid_plugin;
        $wpvivid_plugin->check_cancel_backup($this->task_id);

        $message='Uploading '.$current_name.' Total size: '.size_format($current_size,2).' Uploaded: '.size_format($offset,2).' speed:'.$v.'kb/s';
        $wpvivid_plugin->wpvivid_log->WriteLog($message,'notice');
        $progress=intval(($offset/$current_size)*100);
        WPvivid_taskmanager::update_backup_main_task_progress($this->task_id,'upload',$progress,0);
        WPvivid_taskmanager::update_backup_sub_task_progress($this->task_id,'upload','',WPVIVID_UPLOAD_UNDO,$message, $job_data, $upload_data);
    }

    public function get_backup_files($backup)
    {
        $backup_item=new WPvivid_Backup_Item($backup);

        return $backup_item->get_files();
    }

    public function clean_remote_backup($remotes,$files)
    {
        $remote_option=array_shift($remotes);

        if(!is_null($remote_option))
        {
            global $wpvivid_plugin;
            if(!class_exists('WPvivid_Remote_collection'))
            {
                include_once WPVIVID_PLUGIN_DIR . '/includes/class-wpvivid-remote-collection.php';
                $wpvivid_plugin->remote_collection=new WPvivid_Remote_collection();
            }
            $remote=$wpvivid_plugin->remote_collection->get_remote($remote_option);
            $remote ->cleanup($files);
        }
    }
}includes/class-wpvivid-compress-default.php000064400000004773151327705670015161 0ustar00<?php
abstract class Wpvivid_Compress_Default{
    public $last_error = '';

    abstract public function compress($data);
    abstract public function extract($files,$path = '');
    abstract public function extract_by_files($files,$zip,$path = '');
    abstract public function get_include_zip($files,$allpackages);
    abstract public function listcontent($path);
    abstract public function listnum($path , $includeFolder = false);

    public function getLastError(){
        return $this -> last_error;
    }
    public function getBasename($basename){
        $basename = basename($basename);
        $arr = explode('.',$basename);
        return $arr[0];
    }
    public function _in_array($file,$lists){
        foreach ($lists as $item){
            if(strstr($file,$item)){
                return true;
            }
        }
        return false;
    }
    public function filesplit($max_size,$files){
        $packages = array();
        if($max_size == 0 || $max_size === '0M' || empty($max_size)){
            $packages[] = $files;
        }else{
            $sizenum = 0;
            $max_size = str_replace('M', '', $max_size);
            $size = $max_size * 1024 * 1024;
            $package = array();
            $flag = false;

            usort($files, function ($a, $b)
            {
                $a_size=filesize($a);
                $b_size=filesize($b);
                if ($a_size == $b_size)
                    return 0;

                if ($a_size < $b_size)
                    return 1;
                else
                    return -1;
            });

            foreach ($files as $file)
            {
                $sizenum += filesize($file);
                if($sizenum > $size)
                {
                    if(empty($package))
                    {
                        $package[] = $file;
                        $packages[] = $package;
                        $package = array();
                        $sizenum = 0;
                    }
                    else
                    {
                        $packages[] = $package;
                        $package = array();
                        $package[] = $file;
                        $sizenum = filesize($file);
                    }

                }else{
                    $package[] = $file;
                }
            }
            if(!empty($package))
                $packages[] = $package;
        }
        return $packages;
    }
}includes/customclass/class-wpvivid-base-dropbox.php000064400000031321151327705670016616 0ustar00<?php
if (!defined('WPVIVID_PLUGIN_DIR')){
    die;
}

class Dropbox_Base{

	const API_URL_V2  = 'https://api.dropboxapi.com/';
    const CONTENT_URL_V2 = 'https://content.dropboxapi.com/2/';
    const API_ID = 'cwn4z5jg8wy7b4u';

    private $access_token;
    private $created;
    private $expires_in;
    private $refresh_token;
    private $option;

    public function __construct($option)
    {
        $this -> option = $option;
    	$this -> access_token = $option['access_token'];

        $this -> created = $option['created'];
        $this -> expires_in = $option['expires_in'];
        $this -> refresh_token = $option['refresh_token'];
    }

    public function check_token()
    {
        if(!isset($this->option['refresh_token']))
        {
            return array('result' => WPVIVID_FAILED,'error' => 'Invalid or expired token. Please remove '.$this -> option['name'].' from the storage list and re-authenticate it.');
        }
        $now=time()-10;
        if ($now>$this->option['created']+$this->option['expires_in'])
        {
            $result=$this->getRefreshToken();
            if($result['result']=='failed')
            {
                return array('result' => WPVIVID_FAILED,'error' => $result['error']);
            }
            else
            {
                $remote_options=WPvivid_Setting::get_remote_option($this->option['id']);
                if($remote_options!==false)
                {
                    $remote_options['access_token']= base64_encode($result['data']['access_token']);
                    if(!isset($remote_options['is_encrypt']))
                    {
                        $remote_options['refresh_token']=base64_encode($remote_options['refresh_token']);
                        $this -> refresh_token = base64_encode($this->option['refresh_token']);
                    }
                    else
                    {
                        $this -> refresh_token = $this->option['refresh_token'];
                    }
                    $remote_options['expires_in'] = $result['data']['expires_in'];
                    $remote_options['created'] = time();
                    $remote_options['is_encrypt']=1;
                    WPvivid_Setting::update_remote_option($this->option['id'],$remote_options);
                    $this -> access_token = $remote_options['access_token'];
                    $this -> created = $remote_options['created'];
                    $this -> expires_in = $remote_options['expires_in'];
                    $this -> option['is_encrypt']=1;

                    $ret['result']='success';
                    return $ret;
                }
                else
                {
                    return array('result' => WPVIVID_FAILED,'error'=>'get refresh token failed');
                }
            }
        }
        else
        {
            $ret['result']='success';
            return $ret;
        }
    }

    public function upload($target_path, $file_data, $mode = "add")
    {
        $endpoint = self::CONTENT_URL_V2."files/upload";
        $headers = array(
            "Content-Type: application/octet-stream",
            "Dropbox-API-Arg: {\"path\": \"$target_path\", \"mode\": \"$mode\"}"       
        );
        
        if (file_exists($file_data))
            $postdata = file_get_contents($file_data);
        else
            $postdata = $file_data;

        $returnData = $this ->postRequest($endpoint, $headers, $postdata);
        if(isset($returnData['error_summary']) && preg_match( "/Invalid or expired token. Please remove .* from the storage list and re-authenticate it/", $returnData['error_summary'], $matches ))
        {
            $ret=$this->check_token();
            if($ret['result']=='failed')
            {
                return $returnData;
            }
            else
            {
                $returnData = $this ->postRequest($endpoint, $headers, $postdata);
            }
        }
        return $returnData;
    }

    public function upload_session_start()
    {
        $endpoint = self::CONTENT_URL_V2."files/upload_session/start";
        $headers = array(
            "Content-Type: application/octet-stream",
            "Dropbox-API-Arg: {\"close\": false}"
        );

        $returnData = $this ->postRequest($endpoint, $headers,null);
        if(isset($returnData['error_summary']) && preg_match( "/Invalid or expired token. Please remove .* from the storage list and re-authenticate it/", $returnData['error_summary'], $matches ))
        {
            $ret=$this->check_token();
            if($ret['result']=='failed')
            {
                return $returnData;
            }
            else
            {
                $returnData = $this ->postRequest($endpoint, $headers,null);
            }
        }
        return $returnData;
    }

    public function upload_session_append_v2($session_id, $offset, $postdata)
    {
        $endpoint = self::CONTENT_URL_V2."files/upload_session/append_v2";
        $headers = array(
            "Content-Type: application/octet-stream",
            "Dropbox-API-Arg: {\"cursor\": {\"session_id\": \"$session_id\",\"offset\": $offset},\"close\": false}"
        );

        $returnData = $this ->postRequest($endpoint, $headers, $postdata);
        if(isset($returnData['error_summary']) && preg_match( "/Invalid or expired token. Please remove .* from the storage list and re-authenticate it/", $returnData['error_summary'], $matches ))
        {
            $ret=$this->check_token();
            if($ret['result']=='failed')
            {
                return $returnData;
            }
            else
            {
                $returnData = $this ->postRequest($endpoint, $headers,null);
            }
        }
        return $returnData;
    }

    public function upload_session_finish($session_id, $filesize, $path, $mode = 'add') {
        $endpoint = self::CONTENT_URL_V2."files/upload_session/finish";
        $entry = array(
        	'cursor' => array(
        		'session_id' => $session_id,
        		'offset' => $filesize,
        	),
        	'commit' => array(
        		'path' => $path,
        		'mode' => $mode,

        	),
        );
        $headers = array(
            "Content-Type: application/octet-stream",
            "Dropbox-API-Arg: " . wp_json_encode($entry),
        );

        $returnData = $this ->postRequest($endpoint, $headers,null);
        return $returnData;
    }

    public function download($path,$header = array()) {
        $endpoint = "https://content.dropboxapi.com/2/files/download";
        $headers = array(
            "Content-Type: text/plain; charset=utf-8",
            "Dropbox-API-Arg: {\"path\": \"$path\"}"
        );
        $headers = array_merge ($headers,$header);
        $data = $this ->postRequest($endpoint, $headers,null,false);
        return $data;
    }

    public function delete($path) {
        $endpoint = self::API_URL_V2."2/files/delete";
        $headers = array(
            "Content-Type: application/json"
        );
        $postdata = wp_json_encode(array( "path" => $path ));
        $returnData = $this -> postRequest($endpoint, $headers, $postdata);
        return $returnData;
    }

    public function revoke() {
        $endpoint = self::API_URL_V2."2/auth/token/revoke";
        $headers = array();
        $this -> postRequest($endpoint, $headers);
    }

    public function getUsage(){
        $endpoint = self::API_URL_V2."2/users/get_space_usage";
        $headers = array(
            "Content-Type: application/json"
        );
        $postdata = "null";
        $returnData = $this -> postRequest($endpoint, $headers,$postdata);
        return $returnData;
    }

	public static function getUrl($url,$state = '')
    {
		$params = array(
                    'client_id' => self::API_ID,
                    'response_type' => 'code',
                    'redirect_uri' => $url,
                    'token_access_type'=>'offline',
                    'state' => $state,
                );
	    $url = 'https://www.dropbox.com/oauth2/authorize?';
	    $url .= http_build_query($params,'','&');
	    return $url;
	}

    public function postRequest($endpoint, $headers, $data = null,$returnjson = true) {
        if(isset($this->option['is_encrypt']) && $this->option['is_encrypt'] == 1) {
            $access_token=base64_decode($this -> access_token);
        }
        else{
            $access_token=$this -> access_token;
        }

        $ch = curl_init($endpoint);
        array_push($headers, "Authorization: Bearer " . $access_token);

        curl_setopt($ch, CURLOPT_POST, TRUE);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);

        //todo delete this code
//        curl_setopt($ch,CURLOPT_PROXY, '127.0.0.1:1080');
        $r = curl_exec($ch);
        $chinfo = curl_getinfo($ch);
        $error = curl_error($ch);
        curl_close($ch);

        if($r === false){
            $r['error_summary'] = $error;
        }else{
            if($chinfo['http_code'] === 401)
            {
                $ret=$this->check_token();
                if($ret['result']=='success')
                {
                    $ch = curl_init($endpoint);
                    array_push($headers, "Authorization: Bearer " . $access_token);

                    curl_setopt($ch, CURLOPT_POST, TRUE);
                    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
                    curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
                    curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
                    $r = curl_exec($ch);
                    $chinfo = curl_getinfo($ch);
                    $error = curl_error($ch);
                    curl_close($ch);
                    if($r === false)
                    {
                        $r['error_summary'] = $error;
                    }
                    else
                    {
                        if($chinfo['http_code'] === 401)
                        {
                            $r = array();
                            $r['error_summary'] = 'Invalid or expired token. Please remove '.$this -> option['name'].' from the storage list and re-authenticate it.';
                        }
                        elseif($chinfo['http_code'] !== 200 && $chinfo['http_code'] !== 206)
                        {
                            $r = json_decode($r,true);
                        }
                    }
                }
                else
                {
                    $r = array();
                    $r['error_summary'] = 'Invalid or expired token. Please remove '.$this -> option['name'].' from the storage list and re-authenticate it.';
                }

            }elseif($chinfo['http_code'] !== 200 && $chinfo['http_code'] !== 206){
                $r = json_decode($r,true);
            }
        }
        if($returnjson && !is_array($r))
            $r = json_decode($r,true);

        return $r;
    }
    public function setAccessToken($access_token){
    	$this -> access_token = $access_token;
    }

    public function getRefreshToken()
    {
        if(isset($this->option['is_encrypt']) && $this->option['is_encrypt'] == 1) {
            $refresh_token=base64_decode($this -> refresh_token);
        }
        else{
            $refresh_token=$this -> refresh_token;
        }

        $options=array();
        $options['timeout']=30;
        $options['sslverify']=FALSE;
        $params = array(
            'client_id' => self::API_ID,
            'refresh_token' => $refresh_token,
            'version'=>1
        );
        $url = 'https://auth.wpvivid.com/dropbox_v3/?';
        $url .= http_build_query($params,'','&');

        $request = wp_remote_request( $url,$options);

        if(!is_wp_error($request) && ($request['response']['code'] == 200))
        {
            $json= wp_remote_retrieve_body($request);
            $body=json_decode($json,true);
            if(is_null($body))
            {
                $ret['result']='failed';
                $ret['error']='Get refresh token failed';
                return $ret;
            }

            if($body['result']=='success')
            {
                return $body;
            }
            else
            {
                return $body;
            }
        }
        else
        {
            $ret['result']='failed';
            $ret['error']='Get refresh token failed';
            return $ret;
        }
    }
}includes/customclass/class-wpvivid-one-drive.php000064400000203305151327705670016124 0ustar00<?php

if (!defined('WPVIVID_PLUGIN_DIR')){
    die;
}

require_once WPVIVID_PLUGIN_DIR . '/includes/customclass/class-wpvivid-remote.php';

if(!defined('WPVIVID_REMOTE_ONEDRIVE'))
{
    define('WPVIVID_REMOTE_ONEDRIVE','onedrive');
}

if(!defined('WPVIVID_ONEDRIVE_DEFAULT_FOLDER'))
{
    define('WPVIVID_ONEDRIVE_DEFAULT_FOLDER','wpvivid_backup');
}

if(!defined('WPVIVID_ONEDRIVE_UPLOAD_SIZE'))
{
    define('WPVIVID_ONEDRIVE_UPLOAD_SIZE',1024*1024*2);
}

if(!defined('WPVIVID_ONEDRIVE_DOWNLOAD_SIZE'))
{
    define('WPVIVID_ONEDRIVE_DOWNLOAD_SIZE',1024*1024*2);
}

if(!defined('WPVIVID_ONEDRIVE_RETRY_TIMES'))
{
    define('WPVIVID_ONEDRIVE_RETRY_TIMES','3');
}

class WPvivid_one_drive extends WPvivid_Remote
{
    public $options;
    public $callback;
    public $add_remote;
    public function __construct($options=array())
    {
        if(empty($options))
        {
            if(!defined('WPVIVID_INIT_STORAGE_TAB_ONE_DRIVE'))
            {
                add_action('init', array($this, 'handle_auth_actions'));
                //wpvivid_one_drive_add_remote
                add_action('wp_ajax_wpvivid_one_drive_add_remote',array( $this,'finish_add_remote'));

                add_action('wpvivid_add_storage_tab',array($this,'wpvivid_add_storage_tab_one_drive'), 12);
                add_action('wpvivid_add_storage_page',array($this,'wpvivid_add_storage_page_one_drive'), 12);
                add_action('wpvivid_edit_remote_page',array($this,'wpvivid_edit_storage_page_one_drive'), 12);
                add_filter('wpvivid_remote_pic',array($this,'wpvivid_remote_pic_one_drive'),10);
                add_filter('wpvivid_get_out_of_date_remote',array($this,'wpvivid_get_out_of_date_one_drive'),10,2);
                add_filter('wpvivid_storage_provider_tran',array($this,'wpvivid_storage_provider_one_drive'),10);
                add_filter('wpvivid_get_root_path',array($this,'wpvivid_get_root_path_one_drive'),10);
                add_filter('wpvivid_pre_add_remote',array($this, 'pre_add_remote'),10,2);
                define('WPVIVID_INIT_STORAGE_TAB_ONE_DRIVE',1);
            }
        }
        else
        {
            $this->options=$options;
        }
        $this->add_remote=false;
    }

    public function pre_add_remote($remote,$id)
    {
        if($remote['type']==WPVIVID_REMOTE_ONEDRIVE)
        {
            $remote['id']=$id;
        }

        return $remote;
    }

    public function handle_auth_actions()
    {
        if (isset($_GET['action']) && isset($_GET['page']))
        {
            if($_GET['page'] === 'WPvivid')
            {
                if($_GET['action']=='wpvivid_one_drive_auth')
                {
                    $check=current_user_can('manage_options');
                    if(!$check)
                    {
                        return;
                    }
                    try {
                        $rand_id = substr(md5(time().rand()), 0,13);
                        $auth_id = 'wpvivid-auth-'.$rand_id;
                        $remote_options['auth_id']=$auth_id;
                        set_transient('onedrive_auth_id', $remote_options, 900);
                        $url = 'https://login.microsoftonline.com/common/oauth2/v2.0/authorize'
                            . '?client_id=' . urlencode('37668be9-b55f-458f-b6a3-97e6f8aa10c9')
                            . '&scope=' . urlencode('offline_access files.readwrite.all')
                            . '&response_type=code'
                            . '&redirect_uri=' . urlencode('https://auth.wpvivid.com/onedrive_v2/')
                            . '&state=' . urlencode(admin_url() . 'admin.php?page=WPvivid' . '&action=wpvivid_one_drive_finish_auth&main_tab=storage&sub_tab=one_drive&sub_page=storage_account_one_drive&auth_id='.$auth_id)
                            . '&display=popup'
                            . '&locale=en';
                        header('Location: ' . esc_url_raw($url));
                    }
                    catch (Exception $e){
                        echo '<div class="notice notice-error"><p>'.esc_html($e->getMessage()).'</p></div>';
                    }
                }
                else if($_GET['action']=='wpvivid_one_drive_finish_auth')
                {
                    $tmp_options = get_transient('onedrive_auth_id');
                    if($tmp_options === false)
                    {
                        return;
                    }
                    else if($tmp_options['auth_id'] !== $_GET['auth_id'])
                    {
                        delete_transient('onedrive_auth_id');
                        return;
                    }
                    try
                    {
                        if (isset($_GET['auth_error']))
                        {
                            $error = urldecode($_GET['auth_error']);
                            header('Location: ' . admin_url() . 'admin.php?page=' . WPVIVID_PLUGIN_SLUG . '&action=wpvivid_one_drive&result=error&resp_msg=' . $error);
                            return;
                        }

                        $remoteslist = WPvivid_Setting::get_all_remote_options();
                        foreach ($remoteslist as $key => $value)
                        {
                            if (isset($value['auth_id']) && isset($_GET['auth_id']) && $value['auth_id'] == sanitize_text_field($_GET['auth_id']))
                            {
                                echo '<div class="notice notice-success is-dismissible"><p>';
                                esc_html_e('You have authenticated the Microsoft OneDrive account as your remote storage.', 'wpvivid-backuprestore');
                                echo '</p></div>';
                                return;
                            }
                        }

                        if(empty($_POST['refresh_token']))
                        {
                            if(empty($tmp_options['token']['refresh_token']))
                            {
                                $err = 'No refresh token was received from OneDrive, which means that you entered client secret incorrectly, or that you did not re-authenticated yet after you corrected it. Please authenticate again.';
                                header('Location: ' . admin_url() . 'admin.php?page=' . WPVIVID_PLUGIN_SLUG . '&action=wpvivid_one_drive&result=error&resp_msg='.$err);

                                return;
                            }
                        }
                        else
                        {
                            $tmp_options['type'] = WPVIVID_REMOTE_ONEDRIVE;
                            $tmp_options['token']['access_token']=base64_encode(sanitize_text_field($_POST['access_token']));
                            $tmp_options['token']['refresh_token']=base64_encode(sanitize_text_field($_POST['refresh_token']));
                            $tmp_options['token']['expires']=time()+$_POST['expires_in'];
                            $tmp_options['is_encrypt'] = 1;
                            set_transient('onedrive_auth_id', $tmp_options, 900);
                        }
                        $this->add_remote=true;
                    }
                    catch (Exception $e){
                        echo '<div class="notice notice-error"><p>'.esc_html($e->getMessage()).'</p></div>';
                    }
                }
                else if($_GET['action']=='wpvivid_one_drive')
                {
                    try {
                        if (isset($_GET['result'])) {
                            if ($_GET['result'] == 'success') {
                                add_action('show_notice', array($this, 'wpvivid_show_notice_add_onedrive_success'));
                            } else if ($_GET['result'] == 'error') {
                                add_action('show_notice', array($this, 'wpvivid_show_notice_add_onedrive_error'));
                            }
                        }
                    }
                    catch (Exception $e){
                        echo '<div class="notice notice-error"><p>'.esc_html($e->getMessage()).'</p></div>';
                    }
                }
            }
        }
    }
    public function wpvivid_show_notice_add_onedrive_success(){
        echo '<div class="notice notice-success is-dismissible"><p>';
        esc_html_e('You have authenticated the Microsoft OneDrive account as your remote storage.', 'wpvivid-backuprestore');
        echo '</p></div>';
    }
    public function wpvivid_show_notice_add_onedrive_error(){
        global $wpvivid_plugin;
        $wpvivid_plugin->wpvivid_handle_remote_storage_error($_GET['resp_msg'], 'Add OneDrive Remote');
        echo '<div class="notice notice-error"><p>'.esc_html($_GET['resp_msg']).'</p></div>';
    }

    public function wpvivid_add_storage_tab_one_drive()
    {
        ?>
        <div class="storage-providers" remote_type="one_drive" onclick="select_remote_storage(event, 'storage_account_one_drive');">
            <img src="<?php echo esc_url(WPVIVID_PLUGIN_URL.'/admin/partials/images/storage-microsoft-onedrive.png'); ?>" style="vertical-align:middle;"/><?php esc_html_e('Microsoft OneDrive', 'wpvivid-backuprestore'); ?>
        </div>
        <?php
    }

    public function wpvivid_add_storage_page_one_drive()
    {
        global $wpvivid_plugin;
        $root_path=apply_filters('wpvivid_get_root_path', WPVIVID_REMOTE_ONEDRIVE);
        if($this->add_remote)
        {
            ?>
            <div id="storage_account_one_drive" class="storage-account-page" style="display:none;">
                <div style="background-color:#f1f1f1; padding: 10px;">
                    Please read <a target="_blank" href="https://wpvivid.com/privacy-policy" style="text-decoration: none;">this privacy policy</a> for use of our Microsoft OneDrive authorization app (none of your backup data is sent to us).
                </div>
                <div style="color:#8bc34a; padding: 10px 10px 10px 0;">
                    <strong><?php esc_html_e('Authentication is done, please continue to enter the storage information, then click \'Add Now\' button to save it.', 'wpvivid-backuprestore'); ?></strong>
                </div>
                <div style="padding: 10px 10px 10px 0;">
                    <strong><?php esc_html_e('Enter Your Microsoft OneDrive Information', 'wpvivid-backuprestore'); ?></strong>
                </div>
                <table class="wp-list-table widefat plugins" style="width:100%;">
                    <tbody>
                    <tr>
                        <td class="plugin-title column-primary">
                            <div class="wpvivid-storage-form">
                                <input type="text" class="regular-text" autocomplete="off" option="one_drive" name="name" placeholder="<?php esc_attr_e('Enter a unique alias: e.g. OneDrive-001', 'wpvivid-backuprestore'); ?>" onkeyup="value=value.replace(/[^a-zA-Z0-9\-_]/g,'')" />
                            </div>
                        </td>
                        <td class="column-description desc">
                            <div class="wpvivid-storage-form-desc">
                                <i><?php esc_html_e('A name to help you identify the storage if you have multiple remote storage connected.', 'wpvivid-backuprestore'); ?></i>
                            </div>
                        </td>
                    </tr>
                    <tr>
                        <td class="plugin-title column-primary">
                            <div class="wpvivid-storage-form">
                                <input type="text" class="regular-text" autocomplete="off" option="one_drive" name="path" value="<?php echo esc_attr($root_path.WPVIVID_ONEDRIVE_DEFAULT_FOLDER); ?>" readonly="readonly" />
                            </div>
                        </td>
                        <td class="column-description desc">
                            <div class="wpvivid-storage-form-desc">
                                <i><?php esc_html_e('All backups will be uploaded to this directory.', 'wpvivid-backuprestore'); ?></i>
                            </div>
                        </td>
                    </tr>
                    <tr>
                        <td class="plugin-title column-primary">
                            <div class="wpvivid-storage-form">
                                <input type="text" class="regular-text" autocomplete="off" value="mywebsite01" readonly="readonly" />
                            </div>
                        </td>
                        <td class="column-description desc">
                            <div class="wpvivid-storage-form-desc">
                                <a href="https://docs.wpvivid.com/wpvivid-backup-pro-microsoft-onedrive-custom-folder-name.html"><?php esc_html_e('Pro feature: Create a directory for storing the backups of the site', 'wpvivid-backuprestore'); ?></a>
                            </div>
                        </td>
                    </tr>
                    <tr>
                        <td class="plugin-title column-primary">
                            <div class="wpvivid-storage-select">
                                <label>
                                    <input type="checkbox" option="one_drive" name="default" checked /><?php esc_html_e('Set as the default remote storage.', 'wpvivid-backuprestore'); ?>
                                </label>
                            </div>
                        </td>
                        <td class="column-description desc">
                            <div class="wpvivid-storage-form-desc">
                                <i><?php esc_html_e('Once checked, all this sites backups sent to a remote storage destination will be uploaded to this storage by default.', 'wpvivid-backuprestore'); ?></i>
                            </div>
                        </td>
                    </tr>
                    <tr>
                        <td class="plugin-title column-primary">
                            <div class="wpvivid-storage-form">
                                <input id="wpvivid_one_drive_auth" class="button-primary" type="submit" value="<?php esc_attr_e('Add Now', 'wpvivid-backuprestore'); ?>">
                            </div>
                        </td>
                        <td class="column-description desc">
                            <div class="wpvivid-storage-form-desc">
                                <i><?php esc_html_e('Click the button to add the storage.', 'wpvivid-backuprestore'); ?></i>
                            </div>
                        </td>
                    </tr>
                    </tbody>
                </table>
            </div>
            <script>
                function wpvivid_check_onedrive_storage_alias(storage_alias)
                {
                    var find = 1;
                    jQuery('#wpvivid_remote_storage_list tr').each(function (i) {
                        jQuery(this).children('td').each(function (j) {
                            if (j == 3) {
                                if (jQuery(this).text() == storage_alias) {
                                    find = -1;
                                    return false;
                                }
                            }
                        });
                    });
                    return find;
                }

                jQuery('#wpvivid_one_drive_auth').click(function()
                {
                    wpvivid_one_drive_auth();
                });

                function wpvivid_one_drive_auth()
                {
                    wpvivid_settings_changed = false;
                    var name='';
                    var path='';
                    jQuery('input:text[option=one_drive]').each(function()
                    {
                        var key = jQuery(this).prop('name');
                        if(key==='name')
                        {
                            name = jQuery(this).val();
                        }
                    });

                    var remote_default='0';

                    jQuery('input:checkbox[option=one_drive]').each(function()
                    {
                        if(jQuery(this).prop('checked')) {
                            remote_default='1';
                        }
                        else {
                            remote_default='0';
                        }
                    });
                    if(name == ''){
                        alert(wpvividlion.remotealias);
                    }
                    else if(wpvivid_check_onedrive_storage_alias(name) === -1){
                        alert(wpvividlion.remoteexist);
                    }
                    else {
                        var ajax_data;
                        var remote_from = wpvivid_ajax_data_transfer('one_drive');
                        ajax_data = {
                            'action': 'wpvivid_one_drive_add_remote',
                            'remote': remote_from
                        };
                        jQuery('#wpvivid_one_drive_auth').css({'pointer-events': 'none', 'opacity': '0.4'});
                        jQuery('#wpvivid_remote_notice').html('');
                        wpvivid_post_request(ajax_data, function (data)
                        {
                            try
                            {
                                var jsonarray = jQuery.parseJSON(data);
                                if (jsonarray.result === 'success')
                                {
                                    jQuery('#wpvivid_one_drive_auth').css({'pointer-events': 'auto', 'opacity': '1'});
                                    jQuery('input:text[option=one_drive]').each(function(){
                                        jQuery(this).val('');
                                    });
                                    jQuery('input:password[option=one_drive]').each(function(){
                                        jQuery(this).val('');
                                    });
                                    wpvivid_handle_remote_storage_data(data);
                                    location.href='admin.php?page=WPvivid&action=wpvivid_one_drive&main_tab=storage&sub_tab=one_drive&sub_page=storage_account_one_drive&result=success';
                                }
                                else if (jsonarray.result === 'failed')
                                {
                                    jQuery('#wpvivid_remote_notice').html(jsonarray.notice);
                                    jQuery('input[option=add-remote]').css({'pointer-events': 'auto', 'opacity': '1'});
                                }
                            }
                            catch (err)
                            {
                                alert(err);
                                jQuery('input[option=add-remote]').css({'pointer-events': 'auto', 'opacity': '1'});
                            }

                        }, function (XMLHttpRequest, textStatus, errorThrown)
                        {
                            var error_message = wpvivid_output_ajaxerror('adding the remote storage', textStatus, errorThrown);
                            alert(error_message);
                            jQuery('#wpvivid_one_drive_auth').css({'pointer-events': 'auto', 'opacity': '1'});
                        });
                    }
                }
            </script>
            <?php
        }
        else
        {
            ?>
            <div id="storage_account_one_drive" class="storage-account-page" style="display:none;">
                <div style="background-color:#f1f1f1; padding: 10px;">
                    Please read <a target="_blank" href="https://wpvivid.com/privacy-policy" style="text-decoration: none;">this privacy policy</a> for use of our Microsoft OneDrive authorization app (none of your backup data is sent to us).
                </div>
                <div style="padding: 10px 10px 10px 0;">
                    <strong><?php esc_html_e('To add OneDrive, please get Microsoft authentication first. Once authenticated, you will be redirected to this page, then you can add storage information and save it', 'wpvivid-backuprestore'); ?></strong>
                </div>
                <table class="wp-list-table widefat plugins" style="width:100%;">
                    <tbody>
                    <tr>
                        <td class="plugin-title column-primary">
                            <div class="wpvivid-storage-form">
                                <input onclick="wpvivid_one_drive_auth();" class="button-primary" type="submit" value="<?php esc_attr_e('Authenticate with Microsoft OneDrive', 'wpvivid-backuprestore'); ?>">
                            </div>
                        </td>
                        <td class="column-description desc">
                            <div class="wpvivid-storage-form-desc">
                                <i><?php esc_html_e('Click to get Microsoft authentication.', 'wpvivid-backuprestore'); ?></i>
                            </div>
                        </td>
                    </tr>
                    </tbody>
                </table>
                <div style="padding: 10px 0 0 0;">
                    <span>Tip: Get a 404 or 403 error after authorization? Please read this <a href="https://docs.wpvivid.com/http-403-error-authorizing-cloud-storage.html">doc</a>.</span>
                </div>
            </div>
            <script>
                function wpvivid_one_drive_auth()
                {
                    location.href = '<?php echo esc_url(admin_url()) . 'admin.php?page=WPvivid' . '&action=wpvivid_one_drive_auth'?>';
                }
            </script>
            <?php
        }
    }

    public function wpvivid_edit_storage_page_one_drive()
    {
        ?>
        <div id="remote_storage_edit_onedrive" class="postbox storage-account-block remote-storage-edit" style="display:none;">
            <div style="padding: 0 10px 10px 0;">
                <strong><?php esc_html_e('Enter Your Microsoft OneDrive Information', 'wpvivid-backuprestore'); ?></strong>
            </div>
            <table class="wp-list-table widefat plugins" style="width:100%;">
                <tbody>
                <tr>
                    <td class="plugin-title column-primary">
                        <div class="wpvivid-storage-form">
                            <input type="text" class="regular-text" autocomplete="off" option="edit-onedrive" name="name" placeholder="<?php esc_attr_e('Enter a unique alias: e.g. OneDrive-001', 'wpvivid-backuprestore'); ?>" onkeyup="value=value.replace(/[^a-zA-Z0-9\-_]/g,'')" />
                        </div>
                    </td>
                    <td class="column-description desc">
                        <div class="wpvivid-storage-form-desc">
                            <i><?php esc_html_e('A name to help you identify the storage if you have multiple remote storage connected.', 'wpvivid-backuprestore'); ?></i>
                        </div>
                    </td>
                </tr>
                <tr>
                    <td class="plugin-title column-primary">
                        <div class="wpvivid-storage-form">
                            <input class="button-primary" type="submit" option="edit-remote" value="<?php esc_attr_e('Save Changes', 'wpvivid-backuprestore'); ?>">
                        </div>
                    </td>
                    <td class="column-description desc">
                        <div class="wpvivid-storage-form-desc">
                            <i><?php esc_html_e('Click the button to save the changes.', 'wpvivid-backuprestore'); ?></i>
                        </div>
                    </td>
                </tr>
                </tbody>
            </table>
        </div>
        <script>
            function wpvivid_one_drive_update_auth()
            {
                var name='';
                jQuery('input:text[option=edit-onedrive]').each(function()
                {
                    var key = jQuery(this).prop('name');
                    if(key==='name')
                    {
                        name = jQuery(this).val();
                    }
                });
                if(name == ''){
                    alert(wpvividlion.remotealias);
                }
                else if(wpvivid_check_onedrive_storage_alias(name) === -1){
                    alert(wpvividlion.remoteexist);
                }
                else {
                    location.href = '<?php echo esc_url(admin_url()) . 'admin.php?page=WPvivid' . '&action=wpvivid_one_drive_update_auth&name='?>' + name + '&id=' + wpvivid_editing_storage_id;
                }
            }
        </script>
        <?php
    }

    public function sanitize_options($skip_name='')
    {
        $ret['result']=WPVIVID_SUCCESS;

        if(!isset($this->options['name']))
        {
            $ret['error']="Warning: An alias for remote storage is required.";
            return $ret;
        }

        $ret['options']=$this->options;
        return $ret;
    }

    public function test_connect()
    {
        return array('result' => WPVIVID_SUCCESS);
    }

    public function upload($task_id, $files, $callback = '')
    {
        global $wpvivid_plugin;

        if($this->need_refresh())
        {
            $wpvivid_plugin->wpvivid_log->WriteLog('The token expired and will go to the server to refresh the token.','notice');
            $ret=$this->refresh_token();
            if($ret['result']===WPVIVID_FAILED)
            {
                return $ret;
            }
        }

        $path=$this->options['path'];
        $wpvivid_plugin->wpvivid_log->WriteLog('Check upload folder '.$path,'notice');
        $ret=$this->check_folder($path);

        if($ret['result']===WPVIVID_FAILED)
        {
            return $ret;
        }

        $upload_job=WPvivid_taskmanager::get_backup_sub_task_progress($task_id,'upload',WPVIVID_REMOTE_ONEDRIVE);
        if(empty($upload_job))
        {
            $job_data=array();
            foreach ($files as $file)
            {
                $file_data['size']=filesize($file);
                $file_data['uploaded']=0;
                $file_data['uploadUrl']='';
                $job_data[basename($file)]=$file_data;
            }
            WPvivid_taskmanager::update_backup_sub_task_progress($task_id,'upload',WPVIVID_REMOTE_ONEDRIVE,WPVIVID_UPLOAD_UNDO,'Start uploading',$job_data);
            $upload_job=WPvivid_taskmanager::get_backup_sub_task_progress($task_id,'upload',WPVIVID_REMOTE_ONEDRIVE);
        }

        foreach ($files as $file)
        {
            if(is_array($upload_job['job_data'])&&array_key_exists(basename($file),$upload_job['job_data']))
            {
                if($upload_job['job_data'][basename($file)]['uploaded']==1)
                    continue;
            }

            $this -> last_time = time();
            $this -> last_size = 0;

            if(!file_exists($file))
                return array('result' =>WPVIVID_FAILED,'error' =>$file.' not found. The file might has been moved, renamed or deleted. Please reload the list and verify the file exists.');
            $wpvivid_plugin->wpvivid_log->WriteLog('Start uploading '.basename($file),'notice');
            $wpvivid_plugin->set_time_limit($task_id);
            $result=$this->_upload($task_id, $file,$callback);
            if($result['result'] !==WPVIVID_SUCCESS)
            {
                return $result;
            }
            else
            {
                WPvivid_taskmanager::wpvivid_reset_backup_retry_times($task_id);
            }
            if($this->need_refresh())
            {
                $wpvivid_plugin->wpvivid_log->WriteLog('The token expired and will go to the server to refresh the token.','notice');
                $ret=$this->refresh_token();
                if($ret['result']===WPVIVID_FAILED)
                {
                    return $ret;
                }
            }
        }
        return array('result' =>WPVIVID_SUCCESS);
    }

    public function cleanup($files)
    {
        @set_time_limit(120);

        $path=$this->options['path'];
        if($this->need_refresh())
        {
            $ret=$this->refresh_token();
            if($ret['result']===WPVIVID_FAILED)
            {
                return $ret;
            }
        }

        $ret=$this->get_files_id($files,$path);
        if($ret['result']==WPVIVID_SUCCESS)
        {
            $ids=$ret['ids'];
            foreach ($ids as $id)
            {
                $this->delete_file($id);
            }
        }
        else
        {
            return $ret;
        }

        return array('result' =>WPVIVID_SUCCESS);
    }

    public function set_token()
    {
        $remote_options=WPvivid_Setting::get_remote_option($this->options['id']);
        if($remote_options!==false)
        {
            $this->options['token']=$remote_options['token'];
            if(isset($remote_options['is_encrypt']))
            {
                $this->options['is_encrypt']=$remote_options['is_encrypt'];
            }
        }
    }

    public function download($file, $local_path, $callback = '')
    {
        try {
            $this->current_file_name = $file['file_name'];
            $this->current_file_size = $file['size'];
            $this->callback=$callback;
            global $wpvivid_plugin;
            $wpvivid_plugin->wpvivid_download_log->WriteLog('Remote type: OneDrive.','notice');
            $this->set_token();
            if ($this->need_refresh()) {
                $ret = $this->refresh_token();
                if ($ret['result'] === WPVIVID_FAILED) {
                    return $ret;
                }
            }

            $path = $this->options['path'];
            $ret = $this->check_file($file['file_name'], $path);

            if ($ret['result'] === WPVIVID_FAILED) {
                return $ret;
            }

            $file_path = $local_path . $file['file_name'];
            $wpvivid_plugin->wpvivid_download_log->WriteLog('Create local file.','notice');
            $fh = fopen($file_path, 'a');
            $wpvivid_plugin->wpvivid_download_log->WriteLog('Downloading file ' . $file['file_name'] . ', Size: ' . $file['size'] ,'notice');
            $downloaded_start = filesize($file_path);
            $url = 'https://graph.microsoft.com/v1.0/me/drive/root:/' . $this->options['path'] . '/' . $file['file_name'] . ':/content';
            $download_size = WPVIVID_ONEDRIVE_DOWNLOAD_SIZE;
            $size = $file['size'];
            while ($downloaded_start < $size) {
                $ret = $this->download_loop($url, $downloaded_start, $download_size, $size);
                if ($ret['result'] != WPVIVID_SUCCESS) {
                    return $ret;
                }

                fwrite($fh, $ret['body']);
            }

            fclose($fh);
            return array('result' => WPVIVID_SUCCESS);
        }
        catch (Exception $error){
            $message = 'An exception has occurred. class: '.get_class($error).';msg: '.$error->getMessage().';code: '.$error->getCode().';line: '.$error->getLine().';in_file: '.$error->getFile().';';
            error_log($message);
            return array('result'=>WPVIVID_FAILED, 'error'=>$message);
        }
    }

    private function download_loop($url,&$downloaded_start,$download_size,$file_size,$retry_count=0)
    {
        global $wpvivid_plugin;

        $downloaded_end=min($downloaded_start+$download_size-1,$file_size-1);
        $response=$this->remote_get_download_backup($url,$downloaded_start,$downloaded_end,false,30);
        if ((time() - $this->last_time) > 3) {
            if (is_callable($this->callback)) {
                call_user_func_array($this->callback, array($downloaded_start, $this->current_file_name,
                    $this->current_file_size, $this->last_time, $this->last_size));
            }
            $this->last_size = $downloaded_start;
            $this->last_time = time();
        }
        if($response['result']==WPVIVID_SUCCESS)
        {
            $downloaded_start=$downloaded_end+1;
            $ret['result']=WPVIVID_SUCCESS;
            $ret['body']=$response['body'];
            return $ret;
        }
        else
        {
            if($retry_count<WPVIVID_ONEDRIVE_RETRY_TIMES)
            {
                $retry_count++;
                return $this->download_loop($url,$downloaded_start,$download_size,$file_size,$retry_count);
            }
            else
            {
                return $response;
            }
        }
    }

    public function remote_get_download_backup($url,$downloaded_start,$downloaded_end,$decode=true,$timeout=30,$except_code=array())
    {
        if(isset($this->options['is_encrypt']) && $this->options['is_encrypt'] == 1) {
            $access_token=base64_decode($this->options['token']['access_token']);
        }
        else{
            $access_token=$this->options['token']['access_token'];
        }

        if(empty($except_code))
        {
            $except_code=array(200,201,202,204,206);
        }

        $curl = curl_init();
        $curl_options = array(
            CURLOPT_URL			=> $url,
            CURLOPT_HTTPHEADER 	=> array(
                'Authorization: Bearer ' . $access_token,
                'Range: '."bytes=$downloaded_start-$downloaded_end"
            ),
            CURLOPT_CONNECTTIMEOUT => $timeout,
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_AUTOREFERER    => true,
            CURLOPT_SSL_VERIFYPEER => true
        );
        $curl_options[CURLOPT_CAINFO] = WPVIVID_PLUGIN_DIR.'/vendor/guzzle/guzzle/src/Guzzle/Http/Resources/cacert.pem';
        $curl_options[CURLOPT_FOLLOWLOCATION] = true;
        curl_setopt_array($curl, $curl_options);
        $result = curl_exec($curl);
        $http_info = curl_getinfo($curl);
        $http_code = array_key_exists('http_code', $http_info) ? (int) $http_info['http_code'] : null;
        if($result !== false)
        {
            curl_close($curl);
            if($http_code==401)
            {
                $this->refresh_token();
                $ret=$this->remote_get_download_backup($url,$downloaded_start,$downloaded_end,$decode,$timeout,$except_code);
                return $ret;
            }
            else
            {
                if(in_array($http_code,$except_code))
                {
                    $ret['result']=WPVIVID_SUCCESS;
                    if($decode)
                        $ret['body']=json_decode($result,1);
                    else
                        $ret['body']=$result;
                    return $ret;
                }
                else
                {
                    $ret['result']='failed';
                    $ret['error']='Download files failed, error code: '.$http_code;
                    return $ret;
                }
            }
        }
        else
        {
            $ret['result']='failed';
            $ret['error']=curl_error($curl);
            curl_close($curl);
            return $ret;
        }
    }

    private function need_refresh()
    {
        if(time()-120> $this->options['token']['expires'])
        {
            return true;
        }
        else
        {
            return false;
        }
    }

    private function refresh_token()
    {
        if(isset($this->options['is_encrypt']) && $this->options['is_encrypt'] == 1) {
            $refresh_token=base64_decode($this->options['token']['refresh_token']);
        }
        else{
            $refresh_token=$this->options['token']['refresh_token'];
        }

        $args['method']='POST';
        $args['wpvivid_refresh_token']=1;
        $args['timeout']=15;
        $args['sslverify']=FALSE;
        $args['body']=array( 'wpvivid_refresh_token' => '1', 'refresh_token' => $refresh_token);
        $response=wp_remote_post('https://auth.wpvivid.com/onedrive_v2/',$args);
        if(!is_wp_error($response) && ($response['response']['code'] == 200))
        {
            $json =stripslashes($response['body']);
            $json_ret =json_decode($json,true);
            if($json_ret['result']=='success')
            {
                $remote_options=WPvivid_Setting::get_remote_option($this->options['id']);
                $json_ret['token']['access_token']=base64_encode($json_ret['token']['access_token']);
                $json_ret['token']['refresh_token']=base64_encode($json_ret['token']['refresh_token']);
                $this->options['token']=$json_ret['token'];
                $this->options['is_encrypt']=1;
                $this->options['token']['expires']=time()+ $json_ret['token']['expires_in'];
                if($remote_options!==false)
                {
                    $remote_options['is_encrypt']=1;
                    $remote_options['token']=$json_ret['token'];
                    $remote_options['token']['expires']=time()+ $json_ret['token']['expires_in'];
                    WPvivid_Setting::update_remote_option($this->options['id'],$remote_options);
                }
                $ret['result']=WPVIVID_SUCCESS;
                return $ret;
            }
            else{
                $ret['result']=WPVIVID_FAILED;
                $ret['error']=$json_ret['error'];
                return $ret;
            }
        }
        else
        {
            $ret['result']=WPVIVID_FAILED;
            if ( is_wp_error( $response ) )
            {
                $ret['error']= $response->get_error_message();
            }
            else
            {
                $ret['error']=$response['response']['message'];
            }
            return $ret;
        }
    }

    private function check_folder($folder)
    {
        $url='https://graph.microsoft.com/v1.0/me/drive/root:/'.$folder.'?$select=id,name,folder';
        $response=$this->remote_get($url);
        if($response['result']==WPVIVID_SUCCESS)
        {
            $ret['result']=WPVIVID_SUCCESS;
            return $ret;
        }
        else
        {
            if(isset($response['code'])&&$response['code'] ==404)
            {
                $body=array( 'name' => $folder, 'folder' => array("childCount" => '0'));
                $body=wp_json_encode($body);
                $url='https://graph.microsoft.com/v1.0/me/drive/root/children';

                $response=$this->remote_post($url,array(),$body);
                if($response['result']==WPVIVID_SUCCESS)
                {
                    $ret['result']=WPVIVID_SUCCESS;
                    return $ret;
                }
                else
                {
                    return $response;
                }
            }
            else
            {
                return $response;
            }
        }
    }

    private function check_file($file,$folder)
    {
        $url='https://graph.microsoft.com/v1.0/me/drive/root:/'.$folder.'/'.$file.'?$select=id,name,size';

        $response=$this->remote_get($url);
        if($response['result']==WPVIVID_SUCCESS)
        {
            $ret['result']=WPVIVID_SUCCESS;
            return $ret;
        }
        else
        {
            return $response;
        }
    }

    private function _upload($task_id,$local_file,$callback)
    {
        global $wpvivid_plugin;

        $this -> current_file_size = filesize($local_file);
        $this -> current_file_name = basename($local_file);

        $wpvivid_plugin->wpvivid_log->WriteLog('Check if the server already has the same name file.','notice');

        $this->delete_file_by_name($this->options['path'],basename($local_file));

        $file_size=filesize($local_file);

        //small file
        if($file_size<1024*1024*4)
        {
            $wpvivid_plugin->wpvivid_log->WriteLog('Uploaded files are less than 4M.','notice');
            $ret=$this->upload_small_file($local_file,$task_id);
            return $ret;
        }
        else
        {
            $upload_job=WPvivid_taskmanager::get_backup_sub_task_progress($task_id,'upload',WPVIVID_REMOTE_ONEDRIVE);
            if(empty( $upload_job['job_data'][basename($local_file)]['uploadUrl']))
            {
                $wpvivid_plugin->wpvivid_log->WriteLog('Creating upload session.','notice');
                //big file
                $ret=$this->create_upload_session(basename($local_file));

                if($ret['result']===WPVIVID_FAILED)
                {
                    return $ret;
                }

                $upload_job['job_data'][basename($local_file)]['uploadUrl']=$ret['session_url'];
                $session_url=$ret['session_url'];

                WPvivid_taskmanager::update_backup_sub_task_progress($task_id,'upload',WPVIVID_REMOTE_ONEDRIVE,WPVIVID_UPLOAD_UNDO,'Created upload session',$upload_job['job_data']);
            }
            else
            {
                $session_url=$upload_job['job_data'][basename($local_file)]['uploadUrl'];
            }

            $wpvivid_plugin->wpvivid_log->WriteLog('Ready to start uploading files.','notice');
            $ret=$this->upload_resume($session_url,$local_file,$task_id,$callback);

            return $ret;
        }
    }

    private function upload_small_file($file,$task_id)
    {
        global $wpvivid_plugin;
        $upload_job=WPvivid_taskmanager::get_backup_sub_task_progress($task_id,'upload',WPVIVID_REMOTE_ONEDRIVE);

        if(isset($this->options['is_encrypt']) && $this->options['is_encrypt'] == 1) {
            $access_token=base64_decode($this->options['token']['access_token']);
        }
        else{
            $access_token=$this->options['token']['access_token'];
        }

        $path=$this->options['path'].'/'.basename($file);
        $args['method']='PUT';
        $args['headers']=array( 'Authorization' => 'bearer '.$access_token,'content-type' => 'application/zip');
        $args['timeout']=15;

        $data=file_get_contents($file);
        $args['body']=$data;

        WPvivid_taskmanager::update_backup_sub_task_progress($task_id,'upload',WPVIVID_REMOTE_ONEDRIVE,WPVIVID_UPLOAD_UNDO,'Start uploading '.basename($file).'.',$upload_job['job_data']);

        $response=wp_remote_post('https://graph.microsoft.com/v1.0/me/drive/root:/'.$path.':/content',$args);

        if(!is_wp_error($response) && ($response['response']['code'] == 200||$response['response']['code'] == 201))
        {
            $upload_job['job_data'][basename($file)]['uploaded']=1;
            $wpvivid_plugin->wpvivid_log->WriteLog('Finished uploading '.basename($file),'notice');
            WPvivid_taskmanager::update_backup_sub_task_progress($task_id,'upload',WPVIVID_REMOTE_ONEDRIVE,WPVIVID_UPLOAD_SUCCESS,'Uploading '.basename($file).' completed.',$upload_job['job_data']);
            return array('result' =>WPVIVID_SUCCESS);
        }
        else
        {
            $ret['result']=WPVIVID_FAILED;
            if ( is_wp_error( $response ) )
            {
                $ret['error']= $response->get_error_message();
            }
            else
            {
                $error=json_decode($response['body'],1);
                $ret['error']=$error['error']['message'];
            }
            return $ret;
        }
    }

    private function upload_resume($session_url,$file,$task_id,$callback)
    {
        global $wpvivid_plugin;
        $upload_job=WPvivid_taskmanager::get_backup_sub_task_progress($task_id,'upload',WPVIVID_REMOTE_ONEDRIVE);

        $ret=$this->get_upload_offset($session_url);

        if($ret['result']=='failed')
        {
            return $ret;
        }

        $offset=$ret['offset'];
        $wpvivid_plugin->wpvivid_log->WriteLog('offset '.$offset,'notice');

        WPvivid_taskmanager::update_backup_sub_task_progress($task_id,'upload',WPVIVID_REMOTE_ONEDRIVE,WPVIVID_UPLOAD_UNDO,'Start uploading '.basename($file).'.',$upload_job['job_data']);

        $file_size=filesize($file);
        $handle=fopen($file,'rb');
        $upload_size=WPVIVID_ONEDRIVE_UPLOAD_SIZE;
        $upload_end=min($offset+$upload_size-1,$file_size-1);
        while(true)
        {
            $ret=$this->upload_loop($session_url,$handle,$offset,$upload_end,$upload_size,$file_size,$task_id,$callback);

            if($ret['result']==WPVIVID_SUCCESS)
            {
                if((time() - $this -> last_time) >3)
                {
                    if(is_callable($callback))
                    {
                        call_user_func_array($callback,array($offset,$this -> current_file_name,
                            $this->current_file_size,$this -> last_time,$this -> last_size));
                    }
                    $this -> last_size = $offset;
                    $this -> last_time = time();
                }

                if($ret['op']=='continue')
                {
                    continue;
                }
                else
                {
                    break;
                }
            }
            else
            {
                return $ret;
            }
        }

        fclose($handle);
        $upload_job['job_data'][basename($file)]['uploaded']=1;
        $wpvivid_plugin->wpvivid_log->WriteLog('Finished uploading '.basename($file),'notice');
        WPvivid_taskmanager::update_backup_sub_task_progress($task_id,'upload',WPVIVID_REMOTE_ONEDRIVE,WPVIVID_UPLOAD_SUCCESS,'Uploading '.basename($file).' completed.',$upload_job['job_data']);
        return array('result' =>WPVIVID_SUCCESS);
    }

    private function get_upload_offset($uploadUrl)
    {
        global $wpvivid_plugin;
        $wpvivid_plugin->wpvivid_log->WriteLog('uploadUrl: '.$uploadUrl,'notice');

        $url=$uploadUrl;
        $response=$this->remote_get_ex($url);
        if($response['result']==WPVIVID_SUCCESS)
        {
            if($response['code']==200)
            {
                $ranges=$response['body']['nextExpectedRanges'];

                if (is_array($ranges))
                {
                    $range = $ranges[0];
                } else {
                    $range=$ranges;
                }

                if (preg_match('/^(\d+)/', $range, $matches))
                {
                    $uploaded = $matches[1];
                    $ret['result']='success';
                    $ret['offset']=$uploaded;
                    return $ret;
                }
                else
                {
                    $ret['result']='failed';
                    $ret['error']='get offset failed';
                    return $ret;
                }
            }
            else
            {
                $ret['result']='failed';
                $ret['error']='get offset failed';
                return $ret;
            }
        }
        else
        {
            return $response;
        }
    }

    private function create_upload_session($file)
    {
        $path=$this->options['path'].'/'.basename($file);
        $url='https://graph.microsoft.com/v1.0/me/drive/root:/'.$path.':/createUploadSession';
        $response=$this->remote_post($url);

        if($response['result']==WPVIVID_SUCCESS)
        {
            $upload_session=$response['body']['uploadUrl'];

            $ret['result']=WPVIVID_SUCCESS;
            $ret['session_url']=$upload_session;
            return $ret;
        }
        else
        {
            return $response;
        }
    }

    private function upload_loop($url,$file_handle,&$uploaded,&$upload_end,$upload_size,$file_size,$task_id,$callback,$retry_count=0)
    {
        $curl = curl_init();

        curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($curl, CURLOPT_CONNECTTIMEOUT, 60);
        curl_setopt($curl, CURLOPT_TIMEOUT, 60);
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);

        $upload_size=min($upload_size,$file_size-$uploaded);

        if ($uploaded)
            fseek($file_handle, $uploaded);

        if(isset($this->options['is_encrypt']) && $this->options['is_encrypt'] == 1) {
            $access_token=base64_decode($this->options['token']['access_token']);
        }
        else{
            $access_token=$this->options['token']['access_token'];
        }

        $headers = array(
            "Content-Length: $upload_size",
            "Content-Range: bytes $uploaded-$upload_end/".$file_size,
        );
        //$headers[] = 'Authorization: Bearer ' . $access_token;

        $options = array(
            CURLOPT_URL        => $url,
            CURLOPT_HTTPHEADER => $headers,
            CURLOPT_PUT        => true,
            CURLOPT_INFILE     => $file_handle,
            CURLOPT_INFILESIZE => $upload_size,
            CURLOPT_RETURNTRANSFER=>true,
        );

        curl_setopt_array($curl, $options);

        global $wpvivid_plugin;

        $response=curl_exec($curl);

        $http_code = curl_getinfo($curl,CURLINFO_HTTP_CODE);

        if($response!=false)
        {
            curl_close($curl);
            if($http_code==202)
            {
                $json=json_decode($response,1);
                $ranges=$json['nextExpectedRanges'];

                if (is_array($ranges))
                {
                    $range = $ranges[0];
                } else {
                    $range=$ranges;
                }

                if (preg_match('/^(\d+)/', $range, $matches))
                {
                    $uploaded = $matches[1];
                    $upload_end=min($uploaded+$upload_size-1,$file_size-1);
                }

                $ret['result']=WPVIVID_SUCCESS;
                $ret['op']='continue';
                return $ret;
            }
            else if($http_code==200||$http_code==201)
            {
                $ret['result']=WPVIVID_SUCCESS;
                $ret['op']='finished';
                return $ret;
            }
            else
            {
                if($retry_count<WPVIVID_ONEDRIVE_RETRY_TIMES)
                {
                    $error=json_decode($response,1);
                    $wpvivid_plugin->wpvivid_log->WriteLog('http code is not 200, start retry. http code :'.$http_code.', error: '.wp_json_encode($error),'notice');
                    $ret=$this->get_upload_offset($url);

                    if($ret['result']=='failed')
                    {
                        return $ret;
                    }

                    $uploaded=$ret['offset'];
                    $upload_end=min($uploaded+$upload_size-1,$file_size-1);
                    $wpvivid_plugin->wpvivid_log->WriteLog('offset '.$uploaded,'notice');
                    $retry_count++;
                    return $this->upload_loop($url,$file_handle,$uploaded,$upload_end,$upload_size,$file_size,$task_id,$callback,$retry_count);
                }
                else
                {
                    $ret['result']=WPVIVID_FAILED;
                    $error=json_decode($response,1);
                    $ret['error']=$error['error']['message'];
                    return $ret;
                }
            }
        }
        else
        {
            if($retry_count<WPVIVID_ONEDRIVE_RETRY_TIMES)
            {
                $wpvivid_plugin->wpvivid_log->WriteLog('http no response, start retry. http code :'.$http_code,'notice');
                $ret=$this->get_upload_offset($url);

                if($ret['result']=='failed')
                {
                    return $ret;
                }

                $uploaded=$ret['offset'];
                $upload_end=min($uploaded+$upload_size-1,$file_size-1);
                $wpvivid_plugin->wpvivid_log->WriteLog('offset '.$uploaded,'notice');
                if($http_code === 202)
                {
                    WPvivid_taskmanager::wpvivid_reset_backup_retry_times($task_id);
                    $retry_count=0;
                    if(is_callable($callback))
                    {
                        call_user_func_array($callback,array($uploaded,$this -> current_file_name,
                            $this->current_file_size,$this -> last_time,$this -> last_size));
                    }
                    $this -> last_size = $uploaded;
                    $this -> last_time = time();
                }
                else
                {
                    $retry_count++;
                }
                return $this->upload_loop($url,$file_handle,$uploaded,$upload_end,$upload_size,$file_size,$task_id,$callback,$retry_count);
            }
            else
            {
                $wpvivid_plugin->wpvivid_log->WriteLog('retry times: '.$retry_count.', http code :'.$http_code,'notice');
                $ret['result']=WPVIVID_FAILED;
                $ret['error']=curl_error($curl);
                curl_close($curl);
                return $ret;
            }
        }
    }

    private function get_files_id($files,$path)
    {
        $ret['ids']=array();
        foreach ($files as $file)
        {
            $url='https://graph.microsoft.com/v1.0/me/drive/root:/'.$path.'/'.$file.'?$select=id';
            $response=$this->remote_get($url);
            if($response['result']==WPVIVID_SUCCESS)
            {
                if($response['code']==200)
                {
                    $ret['ids'][]=$response['body']['id'];
                }
            }
            else
            {
                continue;
            }
        }

        if(sizeof($ret['ids'])==0)
        {
            $ret['result']=WPVIVID_FAILED;
            $ret['error']='file not found';
        }
        else
        {
            $ret['result']=WPVIVID_SUCCESS;
        }

        return $ret;
    }

    private function delete_file($id)
    {
        if(isset($this->options['is_encrypt']) && $this->options['is_encrypt'] == 1) {
            $access_token=base64_decode($this->options['token']['access_token']);
        }
        else{
            $access_token=$this->options['token']['access_token'];
        }

        $args['method']='DELETE';
        $args['headers']=array( 'Authorization' => 'bearer '.$access_token);
        $args['timeout']=15;

        $response = wp_remote_request( 'https://graph.microsoft.com/v1.0/me/drive/items/'.$id,$args);

        if(!is_wp_error($response) && ($response['response']['code'] == 204))
        {
            $ret['result']=WPVIVID_SUCCESS;
            return $ret;
        }
        else
        {
            $ret['result']=WPVIVID_FAILED;
            if ( is_wp_error( $response ) )
            {
                $ret['error']= $response->get_error_message();
            }
            else
            {
                $ret['error']= $response['body'];
            }
            return $ret;
        }
    }

    private function remote_get($url,$header=array(),$decode=true,$timeout=15,$except_code=array())
    {
        if(isset($this->options['is_encrypt']) && $this->options['is_encrypt'] == 1) {
            $access_token=base64_decode($this->options['token']['access_token']);
        }
        else{
            $access_token=$this->options['token']['access_token'];
        }

        if(empty($except_code))
        {
            $except_code=array(200,201,202,204,206);
        }
        $args['timeout']=$timeout;
        $args['headers']['Authorization']= 'bearer '.$access_token;
        $args['headers']= $args['headers']+$header;
        $response=wp_remote_get($url,$args);

        if(!is_wp_error($response))
        {
            $ret['code']=$response['response']['code'];
            if(in_array($response['response']['code'],$except_code))
            {
                $ret['result']=WPVIVID_SUCCESS;
                if($decode)
                    $ret['body']=json_decode($response['body'],1);
                else
                    $ret['body']=$response['body'];
            }
            else
            {
                $ret['result']=WPVIVID_FAILED;
                $error=json_decode($response['body'],1);
                $ret['error']=$error['error']['message'].' http code:'.$response['response']['code'];
            }
            return $ret;
        }
        else
        {
            $ret['result']=WPVIVID_FAILED;
            $ret['error']=$response->get_error_message();
            return $ret;
        }
    }

    public function remote_get_ex($url,$header=array(),$decode=true,$timeout=30,$except_code=array())
    {
        if(isset($this->options['is_encrypt']) && $this->options['is_encrypt'] == 1) {
            $access_token=base64_decode($this->options['token']['access_token']);
        }
        else{
            $access_token=$this->options['token']['access_token'];
        }

        if(empty($except_code))
        {
            $except_code=array(200,201,202,204,206);
        }
        $args['timeout']=$timeout;
        $args['headers']=$header;
        $response=wp_remote_get($url,$args);

        if(!is_wp_error($response))
        {
            if($response['response']['code']==401)
            {
                $this->refresh_token();

                if(isset($this->options['is_encrypt']) && $this->options['is_encrypt'] == 1) {
                    $access_token=base64_decode($this->options['token']['access_token']);
                }
                else{
                    $access_token=$this->options['token']['access_token'];
                }

                $args=array();
                $args['timeout']=$timeout;
                $args['headers']['Authorization']= 'bearer '.$access_token;
                $args['headers']= $args['headers']+$header;
                $response=wp_remote_get($url,$args);
            }

            $ret['code']=$response['response']['code'];
            if(in_array($response['response']['code'],$except_code))
            {
                $ret['result']=WPVIVID_SUCCESS;
                if($decode)
                    $ret['body']=json_decode($response['body'],1);
                else
                    $ret['body']=$response['body'];
            }
            else
            {
                $ret['result']=WPVIVID_FAILED;
                $error=json_decode($response['body'],1);
                $ret['error']=$error['error']['message'].' http code:'.$response['response']['code'];
            }
            return $ret;
        }
        else
        {
            $ret['result']=WPVIVID_FAILED;
            $ret['error']=$response->get_error_message();
            return $ret;
        }
    }

    private function remote_post($url,$header=array(),$body=null,$except_code=array())
    {
        if(isset($this->options['is_encrypt']) && $this->options['is_encrypt'] == 1) {
            $access_token=base64_decode($this->options['token']['access_token']);
        }
        else{
            $access_token=$this->options['token']['access_token'];
        }

        if(empty($except_code))
        {
            $except_code=array(200,201,202,204,206);
        }

        $args['method']='POST';
        $args['headers']=array( 'Authorization' => 'bearer '.$access_token,'content-type' => 'application/json');
        $args['headers']=$args['headers']+$header;
        if(!is_null($body))
        {
            $args['body']=$body;
        }
            $args['timeout']=15;

        $response=wp_remote_post($url,$args);

        if(!is_wp_error($response))
        {
            $ret['code']=$response['response']['code'];
            if(in_array($response['response']['code'],$except_code))
            {
                $ret['result']=WPVIVID_SUCCESS;
                $ret['body']=json_decode($response['body'],1);
            }
            else
            {
                $ret['result']=WPVIVID_FAILED;
                $error=json_decode($response['body'],1);
                $ret['error']=$error['error']['message'];
            }
            return $ret;
        }
        else
        {
            $ret['result']=WPVIVID_FAILED;
            $ret['error']=$response->get_error_message();
            return $ret;
        }
    }

    private function delete_file_by_name($folder,$file_name)
    {
        $files[]=$file_name;
        $ret=$this->get_files_id($files,$folder);

        if($ret['result']==WPVIVID_SUCCESS)
        {
            $ids=$ret['ids'];
            foreach ($ids as $id)
            {
                $ret=$this->delete_file($id);
                if($ret['result']==WPVIVID_FAILED)
                {
                    return $ret;
                }
            }
        }
        else
        {
            return $ret;
        }

        return array('result' =>WPVIVID_SUCCESS);
    }

    public function wpvivid_remote_pic_one_drive($remote)
    {
        $remote['onedrive']['default_pic'] = '/admin/partials/images/storage-microsoft-onedrive(gray).png';
        $remote['onedrive']['selected_pic'] = '/admin/partials/images/storage-microsoft-onedrive.png';
        $remote['onedrive']['title'] = 'Microsoft OneDrive';
        return $remote;
    }

    public function wpvivid_get_out_of_date_one_drive($out_of_date_remote, $remote)
    {
        if($remote['type'] == WPVIVID_REMOTE_ONEDRIVE){
            $root_path=apply_filters('wpvivid_get_root_path', $remote['type']);
            $out_of_date_remote = $root_path.$remote['path'];
        }
        return $out_of_date_remote;
    }

    public function wpvivid_storage_provider_one_drive($storage_type)
    {
        if($storage_type == WPVIVID_REMOTE_ONEDRIVE){
            $storage_type = 'Microsoft OneDrive';
        }
        return $storage_type;
    }
    public function wpvivid_get_root_path_one_drive($storage_type){
        if($storage_type == WPVIVID_REMOTE_ONEDRIVE){
            $storage_type = 'root/';
        }
        return $storage_type;
    }

    public function finish_add_remote()
    {
        global $wpvivid_plugin;
        check_ajax_referer( 'wpvivid_ajax', 'nonce' );
        $check=current_user_can('manage_options');
        $check=apply_filters('wpvivid_ajax_check_security',$check);
        if(!$check)
        {
            die();
        }
        try {
            if (empty($_POST) || !isset($_POST['remote']) || !is_string($_POST['remote'])) {
                die();
            }

            $tmp_remote_options = get_transient('onedrive_auth_id');
            if($tmp_remote_options === false)
            {
                die();
            }
            delete_transient('onedrive_auth_id');
            if(empty($tmp_remote_options)||$tmp_remote_options['type']!==WPVIVID_REMOTE_ONEDRIVE)
            {
                die();
            }

            $json = sanitize_text_field($_POST['remote']);
            $json = stripslashes($json);
            $remote_options = json_decode($json, true);
            if (is_null($remote_options)) {
                die();
            }

            $remote_options['path'] = WPVIVID_ONEDRIVE_DEFAULT_FOLDER;
            $remote_options=array_merge($remote_options,$tmp_remote_options);
            if(!class_exists('WPvivid_Remote_collection'))
            {
                include_once WPVIVID_PLUGIN_DIR . '/includes/class-wpvivid-remote-collection.php';
                $wpvivid_plugin->remote_collection=new WPvivid_Remote_collection();
            }
            $ret = $wpvivid_plugin->remote_collection->add_remote($remote_options);

            if ($ret['result'] == 'success') {
                $html = '';
                $html = apply_filters('wpvivid_add_remote_storage_list', $html);
                $ret['html'] = $html;
                $pic = '';
                $pic = apply_filters('wpvivid_schedule_add_remote_pic', $pic);
                $ret['pic'] = $pic;
                $dir = '';
                $dir = apply_filters('wpvivid_get_remote_directory', $dir);
                $ret['dir'] = $dir;
                $schedule_local_remote = '';
                $schedule_local_remote = apply_filters('wpvivid_schedule_local_remote', $schedule_local_remote);
                $ret['local_remote'] = $schedule_local_remote;
                $remote_storage = '';
                $remote_storage = apply_filters('wpvivid_remote_storage', $remote_storage);
                $ret['remote_storage'] = $remote_storage;
                $remote_select_part = '';
                $remote_select_part = apply_filters('wpvivid_remote_storage_select_part', $remote_select_part);
                $ret['remote_select_part'] = $remote_select_part;
                $default = array();
                $remote_array = apply_filters('wpvivid_archieve_remote_array', $default);
                $ret['remote_array'] = $remote_array;
                $success_msg = __('You have successfully added a remote storage.', 'wpvivid-backuprestore');
                $ret['notice'] = apply_filters('wpvivid_add_remote_notice', true, $success_msg);
            }
            else{
                $ret['notice'] = apply_filters('wpvivid_add_remote_notice', false, $ret['error']);
            }

        }
        catch (Exception $error) {
            $message = 'An exception has occurred. class: '.get_class($error).';msg: '.$error->getMessage().';code: '.$error->getCode().';line: '.$error->getLine().';in_file: '.$error->getFile().';';
            error_log($message);
            echo wp_json_encode(array('result'=>'failed','error'=>$message));
            die();
        }
        echo wp_json_encode($ret);
        die();
    }
}includes/customclass/class-wpvivid-base-s3.php000064400000026310151327705670015470 0ustar00<?php
if (!defined('WPVIVID_PLUGIN_DIR')){
    die;
}

include_once 'class-wpvivid-s3.php';

class WPvivid_Base_S3 extends Wpvivid_S3{
	var $signVer = 'v2';

    /**
     * Set Signature Version
     *
     * @param string $version
     * @return void
     */
    public function setSignatureVersion($version = 'v2') {
        $this->signVer = $version;
    }

    public function setServerSideEncryption($value = self::SSE_AES256) {
        $this->_serverSideEncryption = $value;
    }
    public function setStorageClass($value = self::STORAGE_CLASS_STANDARD_IA){
        $this -> _storageClass = $value;
    }

    public function initiateMultipartUpload ($bucket, $uri, $acl = self::ACL_PRIVATE, $metaHeaders = array(), $requestHeaders = array(), $storageClass = self::STORAGE_CLASS_STANDARD) {

        $rest = new WPvivid_S3Request('POST', $bucket, $uri, $this->endpoint, $this);
        $rest->setParameter('uploads','');

        if (is_array($requestHeaders) && !empty($requestHeaders))
            foreach ($requestHeaders as $h => $v) $rest->setHeader($h, $v);
        if(is_array($metaHeaders) && !empty($metaHeaders))
            foreach ($metaHeaders as $h => $v) $rest->setAmzHeader('x-amz-meta-'.$h, $v);

        if ($this -> _storageClass !== self::STORAGE_CLASS_STANDARD) // Storage class
            $rest->setAmzHeader('x-amz-storage-class', $this -> _storageClass);
        if ($this -> _serverSideEncryption !== self::SSE_NONE) // Server-side encryption
            $rest->setAmzHeader('x-amz-server-side-encryption', $this -> _serverSideEncryption);

        $rest->setAmzHeader('x-amz-acl', $acl);

        $rest->getResponse();
        if (false === $rest->response->error && 200 !== $rest->response->code) {
            $rest->response->error = array('code' => $rest->response->code, 'message' => 'Unexpected HTTP status');
        }

        if (false !== $rest->response->error) {
            $this->__triggerError(sprintf("WPvivid_S3::initiateMultipartUpload(): [%s] %s",
                $rest->response->error['code'], $rest->response->error['message']), __FILE__, __LINE__);
            return false;
        } elseif (isset($rest->response->body)) {
            if (is_a($rest->response->body, 'SimpleXMLElement')) {
                $body = $rest->response->body;
            } else {
                $body = new SimpleXMLElement($rest->response->body);
            }
            return (string) $body->UploadId;
        }
        return false;
    }

	public function uploadPart ($bucket, $uri, $uploadId, $filePath, $partNumber, $partSize = 5242880) {
		$rest = new WPvivid_S3Request('PUT', $bucket, $uri, $this->endpoint, $this);
		$rest->setParameter('partNumber', $partNumber);
		$rest->setParameter('uploadId', $uploadId);

		$fileOffset = ($partNumber - 1 ) * $partSize;
		$fileBytes = min(filesize($filePath) - $fileOffset, $partSize);
		if ($fileBytes < 0) $fileBytes = 0;

		$rest->setHeader('Content-Type', 'application/octet-stream');
		$rest->data = "";

		if ($handle = fopen($filePath, "rb")) {
			if ($fileOffset >0) fseek($handle, $fileOffset);
			$bytes_read = 0;
			while ($fileBytes>0 && $read = fread($handle, max($fileBytes, 131072))) { //128kb
				$fileBytes = $fileBytes - strlen($read);
				$bytes_read += strlen($read);
				$rest->data = $rest->data . $read;
			}
			fclose($handle);
		} else {
			return false;
		}

 		$rest->setHeader('Content-MD5', base64_encode(md5($rest->data, true)));
		$rest->size = $bytes_read;

		$rest = $rest->getResponse();
		if (false === $rest->error && 200 !== $rest->code) {
			$rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status');
		}

		if (false !== $rest->error) {
			$this->__triggerError(sprintf("S3::uploadPart(): [%s] %s",
			$rest->error['code'], $rest->error['message']), __FILE__, __LINE__);
			return false;
		}
		return $rest->headers['hash'];
	}

	public function completeMultipartUpload ($bucket, $uri, $uploadId, $parts) {
		$rest = new WPvivid_S3Request('POST', $bucket, $uri, $this->endpoint, $this);
		$rest->setParameter('uploadId', $uploadId);

		$xml = "<CompleteMultipartUpload>\n";
		$partno = 1;
		foreach ($parts as $etag) {
			$xml .= "<Part><PartNumber>$partno</PartNumber><ETag>$etag</ETag></Part>\n";
			$partno++;
		}
		$xml .= "</CompleteMultipartUpload>";

		$rest->data = $xml;
		$rest->size = strlen($rest->data);
		$rest->setHeader('Content-Type', 'application/xml');

		$rest = $rest->getResponse();
		if (false === $rest->error && 200 !== $rest->code) {
			$rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status');
		}

		if (false !== $rest->error) {
			if ('InternalError' == $rest->error['code'] && 'This multipart completion is already in progress' == $rest->error['message']) {
				return true;
			}
			$this->__triggerError(sprintf("S3::completeMultipartUpload(): [%s] %s",
			$rest->error['code'], $rest->error['message']), __FILE__, __LINE__);
			return false;
		}
		return true;

	}

	public function abortMultipartUpload ($bucket, $uri, $uploadId) {
		$rest = new WPvivid_S3Request('DELETE', $bucket, $uri, $this->endpoint, $this);
		$rest->setParameter('uploadId', $uploadId);

		$rest = $rest->getResponse();
		if (false === $rest->error && 204 !== $rest->code) {
			$rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status');
		}

		if (false !== $rest->error) {
			$this->__triggerError(sprintf("S3::abortMultipartUpload(): [%s] %s",
			$rest->error['code'], $rest->error['message']), __FILE__, __LINE__);
			return false;
		}
		return true;
	}

    public function getObject($bucket, $uri, $saveTo = false, $resume = false) {
        $rest = new WPvivid_S3Request('GET', $bucket, $uri, $this->endpoint, $this);
        if (false !== $saveTo) {
            if (is_resource($saveTo)) {
                $rest->fp = $saveTo;
                if (!is_bool($resume)) $rest->setHeader('Range', $resume);
            } else {
                if ($resume && file_exists($saveTo)) {
                    if (false !== ($rest->fp = @fopen($saveTo, 'ab'))) {
                        $rest->setHeader('Range', "bytes=".filesize($saveTo).'-');
                        $rest->file = realpath($saveTo);
                    } else {
                        $rest->response->error = array('code' => 0, 'message' => 'Unable to open save file for writing: '.$saveTo);
                    }
                } else {
                    if (false !== ($rest->fp = @fopen($saveTo, 'wb')))
                        $rest->file = realpath($saveTo);
                    else
                        $rest->response->error = array('code' => 0, 'message' => 'Unable to open save file for writing: '.$saveTo);
                }
            }
        }
        if (false === $rest->response->error) $rest->getResponse();

        if (false === $rest->response->error && ( !$resume && 200 != $rest->response->code) || ( $resume && 206 != $rest->response->code && 200 != $rest->response->code))
            $rest->response->error = array('code' => $rest->response->code, 'message' => 'Unexpected HTTP status');
        if (false !== $rest->response->error) {
            $this->__triggerError(sprintf("WPvivid_S3::getObject({$bucket}, {$uri}): [%s] %s",
                $rest->response->error['code'], $rest->response->error['message']), __FILE__, __LINE__);
            return false;
        }
        return $rest->response;
    }

    /*public function listObject($bucket, $path)
    {
        $rest = new WPvivid_S3Request('GET', $bucket, '', $this->endpoint, $this);
        $rest->setParameter('list-type', 2);
        //$rest->setParameter('max-keys', 2000);
        $rest->setParameter('prefix', $path);
        $rest->setParameter('start-after', 'wpvividbackuppro/pestcontrolcanberraarea_com_au\/2020_08_03_to_2020_08_10/pestcontrolcanberraarea.com.au_wpvivid-5f28895122706_2020-08-04-08-00_incremental_backup_all.zip');
        //$rest->setParameter('delimiter', $path);
        $response = $rest->getResponse();
        if ($response->error === false && $response->code !== 200)
        {
            //$response->error = array('code' => $response->code, 'message' => 'Unexpected HTTP status');
            $ret['result']='failed';
            $ret['error']=$response['message'].' '.$response->code;
            return $ret;
        }

        if ($response->error !== false)
        {
            $ret['result']='failed';
            $ret['error']=sprintf("S3::getBucket(): [%s] %s", $response->error['code'], $response->error['message']);
            return $ret;
        }

        $results = array();

        if (isset($response->body, $response->body->Contents))
        {
            foreach ($response->body->Contents as $c)
            {
                $results[] = array(
                    'name' => (string)$c->Key,
                    'size' => (int)$c->Size,
                );
            }
        }

        $ret['result']='success';
        $ret['data']=$results;
        return $ret;
    }*/

    public function listObject($bucket, $path)
    {
        $ret['result']='success';
        $results = array();
        $bcheck = true;
        $bcontinue = false;
        $continue_token = '';
        $start_after = '';
        $rest = new WPvivid_S3Request('GET', $bucket, '', $this->endpoint, $this);
        while($bcheck){
            $rest->unsetParameter($bucket);
            $rest->setParameter('list-type', 2);
            if($bcontinue) {
                $rest->setParameter('start-after', $start_after);
            }
            else{
                $rest->setParameter('prefix', $path);
            }
            $response = $rest->getResponse();
            if ($response->error === false && $response->code !== 200)
            {
                $ret['result']='failed';
                $ret['error']=$response['message'].' '.$response->code;
                return $ret;
            }

            if ($response->error !== false)
            {
                $ret['result']='failed';
                $ret['error']=sprintf("S3::getBucket(): [%s] %s", $response->error['code'], $response->error['message']);
                return $ret;
            }

            if (isset($response->body, $response->body->Contents))
            {
                foreach ($response->body->Contents as $c)
                {
                    $results[] = array(
                        'name' => (string)$c->Key,
                        'size' => (int)$c->Size,
                    );
                    $start_after = (string)$c->Key;
                }
            }

            if(isset($response->body->NextContinuationToken)){
                $bcontinue = true;
                $continue_token = $response->body->NextContinuationToken;
                $start_after = $start_after;
                $bcheck = true;
            }
            else{
                $bcontinue = false;
                $continue_token = '';
                $bcheck = false;
            }
            $ret['result']='success';
            $ret['data']=$results;
            if(!$bcheck){
                break;
            }
        }
        return $ret;
    }
}includes/customclass/class-wpvivid-send-to-site.php000064400000121242151327705670016546 0ustar00<?php

if (!defined('WPVIVID_PLUGIN_DIR'))
{
    die;
}

if(!defined('WPVIVID_REMOTE_SEND_TO_SITE'))
    define('WPVIVID_REMOTE_SEND_TO_SITE','send_to_site');
include_once WPVIVID_PLUGIN_DIR . '/includes/customclass/class-wpvivid-remote.php';

if(!defined('WPVIVID_SEND_TO_SITE_UPLOAD_SIZE'))
    define('WPVIVID_SEND_TO_SITE_UPLOAD_SIZE', 2);

class WPvivid_Send_to_site extends WPvivid_Remote
{
    public $options;

    public function __construct($options=array())
    {
        if(empty($options))
        {
            if(!defined('WPVIVID_INIT_SEND_TO_SITE'))
            {
                add_action('plugins_loaded', array($this, 'plugins_loaded'));

                define('WPVIVID_INIT_SEND_TO_SITE',1);
            }
        }
        else
        {
            $this->options=$options;
        }
    }

    public function plugins_loaded()
    {
        if (!empty($_POST) &&isset($_POST['wpvivid_action']))
        {
            @ini_set( 'display_errors', 0 );
            if($_POST['wpvivid_action']=='send_to_site_connect')
            {
                $this->send_to_site_connect();
            }
            else if($_POST['wpvivid_action']=='send_to_site_finish')
            {
                $this->send_to_site_finish();
            }
            else if($_POST['wpvivid_action']=='send_to_site')
            {
                $this->send_to_site();
            }
            else if($_POST['wpvivid_action']=='send_to_site_file_status')
            {
                $this->send_to_site_file_status();
            }
            else if($_POST['wpvivid_action']=='clear_backup_cache')
            {
                $this->clear_backup_cache();
            }
            die();
        }
    }

    public function init_remotes($remote_collection)
    {
        $remote_collection[WPVIVID_REMOTE_SEND_TO_SITE] = 'WPvivid_Send_to_site';
        return $remote_collection;
    }

    public function test_connect()
    {
        return array('result' => WPVIVID_SUCCESS,'test'=>$this->options['url']);
    }

    public function upload($task_id, $files, $callback = '')
    {
        include_once WPVIVID_PLUGIN_DIR . '/includes/class-wpvivid-crypt.php';

        global $wpvivid_plugin;
        $wpvivid_plugin->wpvivid_log->WriteLog('Connect site ','notice');
        $ret=$this->connect_site($task_id);
        if($ret['result']==WPVIVID_FAILED)
        {
            if($ret['error']=='The uploading backup already exists in Backups list.')
            {
                return array('result' =>WPVIVID_SUCCESS);
            }
            else
            {
                return $ret;
            }
        }
        $upload_job=WPvivid_taskmanager::get_backup_sub_task_progress($task_id,'upload',WPVIVID_REMOTE_SEND_TO_SITE);
        if(empty($upload_job))
        {
            $job_data=array();
            foreach ($files as $file)
            {
                $file_data['size']=filesize($file);
                $file_data['uploaded']=0;
                $job_data[basename($file)]=$file_data;
            }
            WPvivid_taskmanager::update_backup_sub_task_progress($task_id,'upload',WPVIVID_REMOTE_SEND_TO_SITE,WPVIVID_UPLOAD_UNDO,'Start uploading',$job_data);
            $upload_job=WPvivid_taskmanager::get_backup_sub_task_progress($task_id,'upload',WPVIVID_REMOTE_SEND_TO_SITE);
        }

        foreach ($files as $file)
        {
            $wpvivid_plugin->set_time_limit($task_id);
            if(array_key_exists(basename($file),$upload_job['job_data']))
            {
                if($upload_job['job_data'][basename($file)]['uploaded']==1)
                    continue;
            }

            $this -> last_time = time();
            $this -> last_size = 0;

            if(!file_exists($file))
                return array('result' =>WPVIVID_FAILED,'error' =>$file.' not found. The file might has been moved, renamed or deleted. Please reload the list and verify the file exists.');
            $result=$this->_upload($task_id, $file,$callback);
            if($result['result'] !==WPVIVID_SUCCESS)
            {
                $this->wpvivid_clear_backup_cache($task_id);
                return $result;
            }
        }
        $result=$this->upload_finish($task_id);
        return $result;
        //return array('result' =>WPVIVID_SUCCESS);
    }

    public function _upload($task_id, $file,$callback)
    {
        $this -> current_file_size = filesize($file);
        $this -> current_file_name = basename($file);

        $upload_job=WPvivid_taskmanager::get_backup_sub_task_progress($task_id,'upload',WPVIVID_REMOTE_SEND_TO_SITE);

        global $wpvivid_plugin;
        $wpvivid_plugin->wpvivid_log->WriteLog('Start uploading '.basename($file),'notice');

        WPvivid_taskmanager::update_backup_sub_task_progress($task_id,'upload',WPVIVID_REMOTE_SEND_TO_SITE,WPVIVID_UPLOAD_UNDO,'Start uploading '.basename($file).'.',$upload_job['job_data']);

        $file_size=filesize($file);
        $md5=md5_file($file);
        $handle=fopen($file,'rb');

        $ret=$this->get_file_status($task_id,basename($file),$file_size,$md5);

        $wpvivid_plugin->wpvivid_log->WriteLog(wp_json_encode($ret),'notice');

        if($ret['result']==WPVIVID_SUCCESS)
        {
            if($ret['file_status']['status']=='finished')
            {
                $wpvivid_plugin->wpvivid_log->WriteLog('upload finished','notice');
                fclose($handle);
                $upload_job['job_data'][basename($file)]['uploaded']=1;
                WPvivid_taskmanager::update_backup_sub_task_progress($task_id,'upload',WPVIVID_REMOTE_SEND_TO_SITE,WPVIVID_UPLOAD_SUCCESS,'Uploading '.basename($file).' completed.',$upload_job['job_data']);
                return array('result' =>WPVIVID_SUCCESS);
            }
            else if($ret['file_status']['status']=='continue')
            {
                $offset=$ret['file_status']['offset'];
            }
            else
            {
                $offset=0;
            }
        }
        else
        {
            return $ret;
        }

        $retry_count=0;
        while (!feof($handle))
        {
            $general_setting=WPvivid_Setting::get_setting(true, "");
            if(!isset($general_setting['options']['wpvivid_common_setting']['migrate_size']) || empty($general_setting['options']['wpvivid_common_setting']['migrate_size'])){
                $general_setting['options']['wpvivid_common_setting']['migrate_size']=WPVIVID_SEND_TO_SITE_UPLOAD_SIZE;
            }
            $upload_size = $general_setting['options']['wpvivid_common_setting']['migrate_size'];
            $upload_size = intval($upload_size) * 1024;

            $ret=$this->send_chunk($task_id,$handle,basename($file),$offset,$upload_size,$file_size,$md5);
            if($ret['result']==WPVIVID_SUCCESS)
            {
                $status = WPvivid_taskmanager::get_backup_task_status($task_id);
                $status['resume_count']=0;
                WPvivid_taskmanager::update_backup_task_status($task_id, false, 'running', false, $status['resume_count']);
                if((time() - $this -> last_time) >3)
                {
                    if(is_callable($callback))
                    {
                        call_user_func_array($callback,array($offset,$this -> current_file_name,
                            $this->current_file_size,$this -> last_time,$this -> last_size));
                    }
                    $this -> last_size = $offset;
                    $this -> last_time = time();
                }

                if($ret['op']=='continue')
                {
                    continue;
                }
                else
                {
                    break;
                }
            }
            else
            {
                if($retry_count>3)
                {
                    if(isset($ret['http_code']))
                    {
                        if($ret['http_code']==413)
                        {
                            $ret['error']='Site migration failed. The receiving site can\'t receive the oversized data chunk. Please set the value of Chunk size to 512 KB in plugin settings. Then try again.';
                        }
                    }

                    return $ret;
                }
                else
                {
                    if(isset($ret['http_code']))
                    {
                        if($ret['http_code']==413)
                        {
                            $wpvivid_plugin->wpvivid_log->WriteLog('Site migration failed. The receiving site can\'t receive the oversized data chunk. Please set the value of Chunk size to 512 KB in plugin settings. Then try again. Chunk size: '.size_format($offset),'warning');
                        }
                        else
                        {
                            $wpvivid_plugin->wpvivid_log->WriteLog('upload file error offset:'.size_format($offset).' http error:'.$ret['http_code'],'warning');
                        }
                    }
                    else
                    {
                        $wpvivid_plugin->wpvivid_log->WriteLog('upload file error offset:'.size_format($offset).' error:'.$ret['error'],'warning');
                    }
                    $retry_count++;
                }
            }
        }
        $wpvivid_plugin->wpvivid_log->WriteLog('upload finished','notice');
        fclose($handle);
        $upload_job['job_data'][basename($file)]['uploaded']=1;
        WPvivid_taskmanager::update_backup_sub_task_progress($task_id,'upload',WPVIVID_REMOTE_SEND_TO_SITE,WPVIVID_UPLOAD_SUCCESS,'Uploading '.basename($file).' completed.',$upload_job['job_data']);
        return array('result' =>WPVIVID_SUCCESS);
    }

    public function wpvivid_clear_backup_cache($task_id)
    {
        global $wpvivid_plugin;
        $json=array();

        $json['backup_id']=$task_id;
        $json=wp_json_encode($json);
        $crypt=new WPvivid_crypt(base64_decode($this->options['token']));
        $data=$crypt->encrypt_message($json);

        $data=base64_encode($data);

        $wpvivid_plugin->wpvivid_log->WriteLog('Failed upload backup, clear backup cache.','notice');

        global $wp_version;
        $args['user-agent'] ='WordPress/' . $wp_version . '; ' . get_bloginfo('url');
        $args['body']=array('wpvivid_content'=>$data,'wpvivid_action'=>'clear_backup_cache');
        $args['timeout']=30;
        $response=wp_remote_post($this->options['url'],$args);

        if ( is_wp_error( $response ) )
        {
            $ret['result']=WPVIVID_FAILED;
            $ret['error']= $response->get_error_message();
            $wpvivid_plugin->wpvivid_log->WriteLog( $ret['error'],'notice');
        }
        else
        {
            if($response['response']['code']==200)
            {
                $res=json_decode($response['body'],1);
                if($res!=null)
                {
                    if($res['result']==WPVIVID_SUCCESS)
                    {
                        $ret['result']=WPVIVID_SUCCESS;
                    }
                    else
                    {
                        $ret['result']=WPVIVID_FAILED;
                        $ret['error']= $res['error'];
                        $wpvivid_plugin->wpvivid_log->WriteLog( $ret['error'],'notice');
                    }
                }
                else
                {
                    $ret['result']=WPVIVID_FAILED;
                    $ret['error']= 'Failed to parse returned data, unable to clear target site backup cache.';
                    $wpvivid_plugin->wpvivid_log->WriteLog( $ret['error'],'notice');
                }
            }
            else
            {
                $ret['result']=WPVIVID_FAILED;
                $ret['error']= 'Clear backup cache error '.$response['response']['code'].' '.$response['body'];
                $wpvivid_plugin->wpvivid_log->WriteLog( $ret['error'],'notice');
            }
        }
        return $ret;
    }

    public function connect_site($task_id)
    {
        $json=array();

        $json['backup_id']=$task_id;
        $json=wp_json_encode($json);
        $crypt=new WPvivid_crypt(base64_decode($this->options['token']));
        $data=$crypt->encrypt_message($json);

        $data=base64_encode($data);
        global $wp_version;
        $args['user-agent'] ='WordPress/' . $wp_version . '; ' . get_bloginfo('url');
        $args['body']=array('wpvivid_content'=>$data,'wpvivid_action'=>'send_to_site_connect');
        $args['timeout']=30;
        $response=wp_remote_post($this->options['url'],$args);

        if ( is_wp_error( $response ) )
        {
            $ret['result']=WPVIVID_FAILED;
            $ret['error']= $response->get_error_message();
        }
        else
        {
            if($response['response']['code']==200)
            {
                global $wpvivid_plugin;

                $res=json_decode($response['body'],1);
                if($res!=null)
                {
                    if($res['result']==WPVIVID_SUCCESS)
                    {
                        $ret['result']=WPVIVID_SUCCESS;
                    }
                    else
                    {
                        $ret['result']=WPVIVID_FAILED;
                        $ret['error']= $res['error'];
                    }
                }
                else
                {
                    $ret['result']=WPVIVID_FAILED;
                    $ret['error']= 'Failed to parse returned data, unable to establish connection with the target site.';
                }
            }
            else
            {
                $ret['result']=WPVIVID_FAILED;
                $ret['error']= 'Upload error '.$response['response']['code'].' '.$response['body'];
            }
        }

        return $ret;
    }

    public function send_chunk($task_id,$file_handle,$file_name,&$offset,$size,$file_size,$md5)
    {
        $upload_size=min($size,$file_size-$offset);

        if ($offset)
            fseek($file_handle, $offset);

        $data=fread($file_handle,$upload_size);

        if($data===false)
        {
            $ret['result']=WPVIVID_FAILED;
            $ret['error']='Read file error at:'.$offset;
            return $ret;
        }

        $json['backup_id']=$task_id;
        $json['name']=$file_name;
        $json['offset']=$offset;
        $json['size']=$upload_size;
        $json['file_size']=$file_size;
        $json['md5']=$md5;
        $json['data']=base64_encode($data);
        $json=wp_json_encode($json);

        $crypt=new WPvivid_crypt(base64_decode($this->options['token']));
        $data=$crypt->encrypt_message($json);

        $data=base64_encode($data);

        global $wp_version;
        $args['user-agent'] ='WordPress/' . $wp_version . '; ' . get_bloginfo('url');
        $args['body']=array('wpvivid_content'=>$data,'wpvivid_action'=>'send_to_site');
        $args['timeout']=30;

        global $wpvivid_plugin;
        $wpvivid_plugin->wpvivid_log->WriteLog('send chunk '.basename($file_name).' offset '.$offset,'notice');

        $response=wp_remote_post($this->options['url'],$args);

        $wpvivid_plugin->wpvivid_log->WriteLog('finished send chunk','notice');

        if ( is_wp_error( $response ) )
        {
            $ret['result']=WPVIVID_FAILED;
            $ret['error']= $response->get_error_message();
        }
        else
        {
            if($response['response']['code']==200)
            {
                $res=json_decode($response['body'],1);
                if($res!=null)
                {
                    if($res['result']==WPVIVID_SUCCESS)
                    {
                        $offset=$offset+$upload_size;
                        $ret['result']=WPVIVID_SUCCESS;
                        $ret['op']=$res['op'];
                    }
                    else
                    {
                        $ret['result']=WPVIVID_FAILED;
                        $ret['error']= $res['error'];
                        $wpvivid_plugin->wpvivid_log->WriteLog( $ret['error'],'notice');
                    }

                }
                else
                {
                    $ret['result']=WPVIVID_FAILED;
                    $ret['error']= 'Failed to parse returned data, chunk transfer failed.';
                    $wpvivid_plugin->wpvivid_log->WriteLog('error send chunk failed','notice');
                }
            }
            else
            {
                $ret['result']=WPVIVID_FAILED;
                $ret['http_code']=$response['response']['code'];
                $ret['error']= 'http error, error code:'.$response['response']['code'];
            }
        }
        return $ret;
    }

    public function upload_finish($task_id)
    {
        $task= new WPvivid_Backup_Task_2($task_id);
        $task->update_backup_result();
        $task=WPvivid_taskmanager::get_task($task_id);
        $json=array();
        $json['backup']=$task;
        $json['backup_id']=$task_id;
        $json=wp_json_encode($json);

        $crypt=new WPvivid_crypt(base64_decode($this->options['token']));
        $data=$crypt->encrypt_message($json);

        $data=base64_encode($data);
        global $wp_version;
        $args['user-agent'] ='WordPress/' . $wp_version . '; ' . get_bloginfo('url');
        $args['body']=array('wpvivid_content'=>$data,'wpvivid_action'=>'send_to_site_finish');
        $args['timeout']=30;
        $response=wp_remote_post($this->options['url'],$args);
        if ( is_wp_error( $response ) )
        {
            $ret['result']=WPVIVID_FAILED;
            $ret['error']= $response->get_error_message();
        }
        else
        {
            if($response['response']['code']==200)
            {
                $res=json_decode($response['body'],1);
                if($res!=null)
                {
                    if($res['result']==WPVIVID_SUCCESS)
                    {
                        $ret['result']=WPVIVID_SUCCESS;
                    }
                    else
                    {
                        $ret['result']=WPVIVID_FAILED;
                        $ret['error']= $res['error'];
                    }
                }
                else
                {
                    $ret['result']=WPVIVID_FAILED;
                    $ret['error']= 'Failed to parse returned data, uploading backup chunks failed.';
                }
            }
            else
            {
                $ret['result']=WPVIVID_FAILED;
                //$ret['error']= 'Upload error '.$response['response']['code'].' '.$response['body'];
                $ret['error']= 'Upload error '.$response['response']['code'];
            }
        }

        return $ret;
    }

    public function send_to_site_connect()
    {
        include_once WPVIVID_PLUGIN_DIR . '/includes/class-wpvivid-crypt.php';
        try {
            if (isset($_POST['wpvivid_content'])) {
                $default = array();
                $option = get_option('wpvivid_api_token', $default);
                if (empty($option)) {
                    die();
                }
                if ($option['expires'] != 0 && $option['expires'] < time()) {
                    die();
                }

                $crypt = new WPvivid_crypt(base64_decode($option['private_key']));
                $body = base64_decode($_POST['wpvivid_content']);
                $data = $crypt->decrypt_message($body);
                if (!is_string($data)) {
                    $ret['result'] = WPVIVID_FAILED;
                    $ret['error'] = 'Data decryption failed.';
                    echo wp_json_encode($ret);
                    die();
                }

                $params = json_decode($data, 1);
                if (is_null($params)) {
                    $ret['result'] = WPVIVID_FAILED;
                    $ret['error'] = 'Data decode failed.';
                    echo wp_json_encode($ret);
                    die();
                }

                if (isset($params['backup_id'])) {
                    if (WPvivid_Backuplist::get_backup_by_id($params['backup_id']) !== false) {
                        $ret['result'] = WPVIVID_FAILED;
                        $ret['error'] = 'The uploading backup already exists in Backups list.';
                        echo wp_json_encode($ret);
                    } else {
                        global $wpvivid_plugin;
                        $wpvivid_plugin->wpvivid_log = new WPvivid_Log();
                        if (!file_exists($wpvivid_plugin->wpvivid_log->GetSaveLogFolder() . $params['backup_id'] . '_backup_log.txt')) {
                            $wpvivid_plugin->wpvivid_log->CreateLogFile($params['backup_id'] . '_backup', 'no_folder', 'transfer');
                            $wpvivid_plugin->wpvivid_log->WriteLogHander();
                        } else {
                            $wpvivid_plugin->wpvivid_log->OpenLogFile($params['backup_id'] . '_backup', 'no_folder');
                        }


                        $wpvivid_plugin->wpvivid_log->WriteLog('Connect site success', 'notice');
                        $ret['result'] = WPVIVID_SUCCESS;
                        echo wp_json_encode($ret);
                    }
                } else {
                    $ret['result'] = WPVIVID_SUCCESS;
                    echo wp_json_encode($ret);
                }
            }
        }
        catch (Exception $e) {
            $ret['result']=WPVIVID_FAILED;
            $ret['error']=$e->getMessage();
            echo wp_json_encode($ret);
            die();
        }
        die();
    }

    public function send_to_site()
    {
        include_once WPVIVID_PLUGIN_DIR . '/includes/class-wpvivid-crypt.php';
        $test_log=new WPvivid_Log();
        $test_log->CreateLogFile('test_backup','no_folder','transfer');
        $test_log->WriteLog('test upload.','notice');
        try
        {
            if(isset($_POST['wpvivid_content']))
            {
                global $wpvivid_plugin;
                $wpvivid_plugin->wpvivid_log=new WPvivid_Log();

                $default=array();
                $option=get_option('wpvivid_api_token',$default);
                if(empty($option))
                {
                    die();
                }
                if($option['expires'] !=0 && $option['expires']<time())
                {
                    die();
                }
                $crypt=new WPvivid_crypt(base64_decode($option['private_key']));
                $body=base64_decode($_POST['wpvivid_content']);
                $data=$crypt->decrypt_message($body);
                if (!is_string($data))
                {
                    $ret['result']=WPVIVID_FAILED;
                    $ret['error']='The key is invalid.';
                    echo wp_json_encode($ret);
                    die();
                }

                $params=json_decode($data,1);
                if(is_null($params))
                {
                    $ret['result']=WPVIVID_FAILED;
                    $ret['error']='The key is invalid.';
                    echo wp_json_encode($ret);
                    die();
                }

                $wpvivid_plugin->wpvivid_log->OpenLogFile($params['backup_id'].'_backup','no_folder','backup');
                $wpvivid_plugin->wpvivid_log->WriteLog('start upload.','notice');
                $dir=WPvivid_Setting::get_backupdir();

                $file_path=WP_CONTENT_DIR.DIRECTORY_SEPARATOR.$dir.DIRECTORY_SEPARATOR.str_replace('wpvivid','wpvivid_temp',$params['name']);

                if(!file_exists($file_path))
                {
                    $handle=fopen($file_path,'w');
                    fclose($handle);
                }

                $handle=fopen($file_path,'rb+');
                $offset=$params['offset'];
                $wpvivid_plugin->wpvivid_log->WriteLog('Write file:'.$file_path.' offset:'.size_format($offset),'notice');
                if($offset)
                {
                    if(fseek($handle, $offset)===-1)
                    {
                        $wpvivid_plugin->wpvivid_log->WriteLog('Seek file offset failed:'.size_format($offset),'notice');
                    }
                }

                if (fwrite($handle,base64_decode($params['data'])) === FALSE)
                {
                    $wpvivid_plugin->wpvivid_log->WriteLog('Write file :'.$file_path.' failed size:'.filesize($file_path),'notice');
                }
                else
                {
                    $wpvivid_plugin->wpvivid_log->WriteLog('Write file:'.$file_path.' success size:'.filesize($file_path),'notice');
                }

                fclose($handle);


                if(filesize($file_path)>=$params['file_size'])
                {
                    if (md5_file($file_path) == $params['md5'])
                    {
                        $wpvivid_plugin->wpvivid_log->WriteLog('rename temp file:'.$file_path.' to new name:'.WP_CONTENT_DIR.DIRECTORY_SEPARATOR.$dir.DIRECTORY_SEPARATOR.$params['name'],'notice');
                        rename($file_path,WP_CONTENT_DIR.DIRECTORY_SEPARATOR.$dir.DIRECTORY_SEPARATOR.$params['name']);

                        $ret['result']=WPVIVID_SUCCESS;
                        $ret['op']='finished';
                    } else {
                        $wpvivid_plugin->wpvivid_log->WriteLog('file md5 not match','notice');
                        $ret['result']=WPVIVID_FAILED;
                        $ret['error']='File md5 is not matched.';
                    }
                }
                else
                {
                    $wpvivid_plugin->wpvivid_log->WriteLog('continue size:'.filesize($file_path).' size1:'.$params['file_size'],'notice');
                    $ret['result']=WPVIVID_SUCCESS;
                    $ret['op']='continue';
                    //
                }

                echo wp_json_encode($ret);
            }
        }
        catch (Exception $e)
        {
            $ret['result']=WPVIVID_FAILED;
            $ret['error']=$e->getMessage();
            //$wpvivid_plugin->wpvivid_log->WriteLog($e->getMessage(),'error');
            echo wp_json_encode($ret);
            die();
        }

        die();
    }

    public function send_to_site_finish()
    {
        include_once WPVIVID_PLUGIN_DIR . '/includes/class-wpvivid-crypt.php';
        try {
            if (isset($_POST['wpvivid_content'])) {
                $default = array();
                $option = get_option('wpvivid_api_token', $default);
                if (empty($option)) {
                    die();
                }
                if ($option['expires'] != 0 && $option['expires'] < time()) {
                    die();
                }
                $crypt = new WPvivid_crypt(base64_decode($option['private_key']));
                $body = base64_decode($_POST['wpvivid_content']);
                $data = $crypt->decrypt_message($body);
                if (!is_string($data)) {
                    $ret['result'] = WPVIVID_FAILED;
                    $ret['error'] = 'The key is invalid.';
                    echo wp_json_encode($ret);
                    die();
                }
                $params = json_decode($data, 1);
                if (is_null($params)) {
                    $ret['result'] = WPVIVID_FAILED;
                    $ret['error'] = 'The key is invalid.';
                    echo wp_json_encode($ret);
                    die();
                }
                global $wpvivid_plugin;
                $wpvivid_plugin->wpvivid_log = new WPvivid_Log();
                $wpvivid_plugin->wpvivid_log->OpenLogFile($params['backup_id'] . '_backup', 'no_folder', 'backup');
                $wpvivid_plugin->wpvivid_log->WriteLog('upload finished', 'notice');
                if (isset($params['backup']) && isset($params['backup_id']))
                {
                    $list = WPvivid_Setting::get_option('wpvivid_backup_list');
                    $backup_data = $this->get_backup_data_by_task($params['backup']);
                    $list[$params['backup_id']] = $backup_data;
                    WPvivid_Setting::update_option('wpvivid_backup_list', $list);
                }
                $ret['result'] = WPVIVID_SUCCESS;
                echo wp_json_encode($ret);
            }
        }
        catch (Exception $e) {
            $ret['result']=WPVIVID_FAILED;
            $ret['error']=$e->getMessage();
            echo wp_json_encode($ret);
            die();
        }
        die();
    }

    public function get_backup_data_by_task($task)
    {
        global $wpvivid_plugin;
        $backup_data=array();
        $backup_data['type']='Migration';
        $backup_data['create_time']=$task['status']['start_time'];
        $backup_data['manual_delete']=0;
        $backup_data['local']['path']=WPvivid_Setting::get_backupdir();
        $backup_data['compress']['compress_type']=$task['options']['backup_options']['compress']['compress_type'];
        $backup_data['save_local']=$task['options']['save_local'];
        $backup_data['log']=$wpvivid_plugin->wpvivid_log->log_file;
        $backup_data['backup']=$this->get_backup_result_by_task($task);
        $backup_data['remote']=array();
        $backup_data['lock']=0;
        $backup_data=apply_filters('wpvivid_get_backup_data_by_task',$backup_data,$task);
        return $backup_data;
    }

    public function get_backup_result_by_task($task)
    {
        $ret['result']=WPVIVID_SUCCESS;
        $ret['files']=array();
        foreach ($task['options']['backup_options']['backup'] as $backup_data)
        {
            if($task['options']['backup_options']['ismerge']==1)
            {
                if (!defined('WPVIVID_BACKUP_TYPE_MERGE'))
                    define('WPVIVID_BACKUP_TYPE_MERGE','backup_merge');
                if(WPVIVID_BACKUP_TYPE_MERGE==$backup_data['key'])
                {
                    $ret=$backup_data['result'];
                    if($ret['result']!==WPVIVID_SUCCESS)
                    {
                        return $ret;
                    }
                }
            }
            else
            {
                $ret['files']=array_merge($ret['files'],$backup_data['result']['files']);
            }
        }
        return $ret;
    }

    public function cleanup($files)
    {
        return array('result' => WPVIVID_SUCCESS);
    }

    public function download($file, $local_path, $callback = '')
    {
        return array('result' => WPVIVID_SUCCESS);
    }

    public function get_file_status($task_id,$file,$file_size,$md5)
    {
        $json=array();

        $json['backup_id']=$task_id;
        $json['name']=$file;
        $json['file_size']=$file_size;
        $json['md5']=$md5;
        $json=wp_json_encode($json);
        $crypt=new WPvivid_crypt(base64_decode($this->options['token']));
        $data=$crypt->encrypt_message($json);
        $data=base64_encode($data);
        global $wp_version;
        $args['user-agent'] ='WordPress/' . $wp_version . '; ' . get_bloginfo('url');
        $args['body']=array('wpvivid_content'=>$data,'wpvivid_action'=>'send_to_site_file_status');
        $args['timeout']=30;
        $response=wp_remote_post($this->options['url'],$args);
        if ( is_wp_error( $response ) )
        {
            $ret['result']=WPVIVID_FAILED;
            $ret['error']= $response->get_error_message();
        }
        else
        {
            if($response['response']['code']==200)
            {
                global $wpvivid_plugin;

                $res=json_decode($response['body'],1);
                if($res!=null)
                {
                    if($res['result']==WPVIVID_SUCCESS)
                    {
                        $ret['result']=WPVIVID_SUCCESS;
                        $ret['file_status']=$res['file_status'];
                    }
                    else
                    {
                        $ret['result']=WPVIVID_FAILED;
                        $ret['error']= $res['error'];
                    }
                }
                else
                {
                    $ret['result']=WPVIVID_FAILED;
                    $ret['error']= 'Failed to parse returned data, unable to retrieve file status of target site.';
                }
            }
            else
            {
                $ret['result']=WPVIVID_FAILED;
                $ret['error']= 'Upload error '.$response['response']['code'].' '.$response['body'];
            }
        }
        return $ret;
    }

    public function send_to_site_file_status()
    {
        include_once WPVIVID_PLUGIN_DIR . '/includes/class-wpvivid-crypt.php';
        try {
            if (isset($_POST['wpvivid_content'])) {
                $default = array();
                $option = get_option('wpvivid_api_token', $default);
                if (empty($option)) {
                    die();
                }
                if ($option['expires'] != 0 && $option['expires'] < time()) {
                    die();
                }

                $crypt = new WPvivid_crypt(base64_decode($option['private_key']));
                $body = base64_decode($_POST['wpvivid_content']);
                $data = $crypt->decrypt_message($body);
                if (!is_string($data)) {
                    $ret['result'] = WPVIVID_FAILED;
                    $ret['error'] = 'The key is invalid.';
                    echo wp_json_encode($ret);
                    die();
                }

                $params = json_decode($data, 1);
                if (is_null($params)) {
                    $ret['result'] = WPVIVID_FAILED;
                    $ret['error'] = 'The key is invalid.';
                    echo wp_json_encode($ret);
                    die();
                }

                $dir = WPvivid_Setting::get_backupdir();
                $file_path = WP_CONTENT_DIR . DIRECTORY_SEPARATOR . $dir . DIRECTORY_SEPARATOR . str_replace('wpvivid', 'wpvivid_temp', $params['name']);

                $rename = true;

                if (!file_exists($file_path))
                {
                    $file_path = WP_CONTENT_DIR . DIRECTORY_SEPARATOR . $dir . DIRECTORY_SEPARATOR . $params['name'];
                    $rename = false;
                    $offset=false;
                }
                else
                {
                    $offset = filesize($file_path);
                }

                if (!$offset) {
                    $ret['result'] = WPVIVID_SUCCESS;
                    $ret['file_status']['status'] = 'start';
                    echo wp_json_encode($ret);
                    die();
                }

                if (filesize($file_path) >= $params['file_size']) {
                    if (md5_file($file_path) == $params['md5']) {
                        if ($rename)
                            rename($file_path, WP_CONTENT_DIR . DIRECTORY_SEPARATOR . $dir . DIRECTORY_SEPARATOR . $params['name']);
                        $ret['result'] = WPVIVID_SUCCESS;
                        $ret['file_status']['status'] = 'finished';
                    } else {
                        $ret['result'] = WPVIVID_FAILED;
                        $ret['error'] = 'File md5 is not matched.';
                    }
                } else {
                    $ret['result'] = WPVIVID_SUCCESS;
                    $ret['file_status']['status'] = 'continue';
                    $ret['file_status']['offset'] = filesize($file_path);
                }
                echo wp_json_encode($ret);
            }
        }
        catch (Exception $e) {
            $ret['result']=WPVIVID_FAILED;
            $ret['error']=$e->getMessage();
            echo wp_json_encode($ret);
            die();
        }
        die();
    }

    public function clear_backup_cache()
    {
        include_once WPVIVID_PLUGIN_DIR . '/includes/class-wpvivid-crypt.php';
        try {
            if (isset($_POST['wpvivid_content'])) {
                $default = array();
                $option = get_option('wpvivid_api_token', $default);
                if (empty($option)) {
                    die();
                }
                if ($option['expires'] != 0 && $option['expires'] < time()) {
                    die();
                }

                $crypt = new WPvivid_crypt(base64_decode($option['private_key']));
                $body = base64_decode($_POST['wpvivid_content']);
                $data = $crypt->decrypt_message($body);

                if (!is_string($data)) {
                    $ret['result'] = WPVIVID_FAILED;
                    $ret['error'] = 'The key is invalid.';
                    echo wp_json_encode($ret);
                    die();
                }
                $params = json_decode($data, 1);
                if (is_null($params)) {
                    $ret['result'] = WPVIVID_FAILED;
                    $ret['error'] = 'The key is invalid.';
                    echo wp_json_encode($ret);
                    die();
                }

                global $wpvivid_plugin;
                $wpvivid_plugin->wpvivid_log = new WPvivid_Log();
                $wpvivid_plugin->wpvivid_log->OpenLogFile($params['backup_id'] . '_backup', 'no_folder', 'backup');

                $dir=WPvivid_Setting::get_backupdir();


                $backup_path=WP_CONTENT_DIR.DIRECTORY_SEPARATOR.$dir.DIRECTORY_SEPARATOR;
                if(is_dir($backup_path))
                {
                    $handler = opendir($backup_path);
                    if($handler!==false)
                    {
                        while (($filename = readdir($handler)) !== false)
                        {
                            if ($filename != "." && $filename != "..")
                            {
                                if (is_dir($backup_path  . $filename))
                                {
                                    continue;
                                }
                                else {
                                    if (self::is_wpvivid_backup($filename))
                                    {
                                        if ($id =self::get_wpvivid_backup_id($filename))
                                        {
                                            $white_label_id = str_replace(apply_filters('wpvivid_white_label_file_prefix', 'wpvivid'), 'wpvivid', $id);
                                            if(isset($params['backup_id']))
                                            {
                                                $clear_backup_id = sanitize_text_field($params['backup_id']);

                                                if($id === $clear_backup_id || $white_label_id === $clear_backup_id)
                                                {
                                                    $wpvivid_plugin->wpvivid_log->WriteLog('Clear backup file: '.$backup_path.$filename, 'notice');
                                                    @wp_delete_file($backup_path.$filename);
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                        if($handler)
                            @closedir($handler);
                    }
                    $ret['result'] = WPVIVID_SUCCESS;
                }
                else{
                    $ret['result']='failed';
                    $ret['error']='Failed to get local storage directory.';
                }
                echo wp_json_encode($ret);
            }
        }
        catch (Exception $e) {
            $ret['result']=WPVIVID_FAILED;
            $ret['error']=$e->getMessage();
            echo wp_json_encode($ret);
            die();
        }
        die();
    }

    public static function is_wpvivid_backup($file_name)
    {
        if (preg_match('/wpvivid-.*_.*_.*\.zip$/', $file_name))
        {
            return true;
        }
        else {
            return false;
        }
    }

    public static function get_wpvivid_backup_id($file_name)
    {
        if (preg_match('/wpvivid-(.*?)_/', $file_name, $matches))
        {
            $id = $matches[0];
            $id = substr($id, 0, strlen($id) - 1);
            return $id;
        }
        else {
            return false;
        }
    }
}includes/customclass/class-wpvivid-ftpclass.php000064400000101377151327705670016061 0ustar00<?php

if (!defined('WPVIVID_PLUGIN_DIR')){
    die;
}

if(!defined('WPVIVID_REMOTE_FTP'))
    define('WPVIVID_REMOTE_FTP','ftp');

require_once WPVIVID_PLUGIN_DIR .'/includes/customclass/class-wpvivid-remote.php';
class WPvivid_FTPClass extends WPvivid_Remote{
    private $time_out = 20;
    private $callback;
    private $options=array();

    public function __construct($options=array())
    {
        if(empty($options))
        {
            add_action('wpvivid_add_storage_tab',array($this,'wpvivid_add_storage_tab_ftp'), 15);
            add_action('wpvivid_add_storage_page',array($this,'wpvivid_add_storage_page_ftp'), 15);
            add_action('wpvivid_edit_remote_page',array($this,'wpvivid_edit_storage_page_ftp'), 15);
            add_filter('wpvivid_remote_pic',array($this,'wpvivid_remote_pic_ftp'),9);
            add_filter('wpvivid_get_out_of_date_remote',array($this,'wpvivid_get_out_of_date_ftp'),10,2);
            add_filter('wpvivid_storage_provider_tran',array($this,'wpvivid_storage_provider_ftp'),10);

        }else{
            $this->options = $options;
        }
    }

    public function wpvivid_add_storage_tab_ftp()
    {
        ?>
        <div class="storage-providers" remote_type="ftp" onclick="select_remote_storage(event, 'storage_account_ftp');">
            <img src="<?php echo esc_url(WPVIVID_PLUGIN_URL.'/admin/partials/images/storage-ftp.png'); ?>" style="vertical-align:middle;"/><?php esc_html_e('FTP', 'wpvivid-backuprestore'); ?>
        </div>
        <?php
    }

    public function wpvivid_add_storage_page_ftp()
    {
        ?>
        <div id="storage_account_ftp" class="storage-account-page" style="display:none;">
            <div style="padding: 0 10px 10px 0;"><strong><?php esc_html_e('Enter Your FTP Account', 'wpvivid-backuprestore'); ?></strong></div>
            <table class="wp-list-table widefat plugins" style="width:100%;">
                <tbody>
                <form>
                <tr>
                    <td class="plugin-title column-primary">
                        <div class="wpvivid-storage-form">
                            <input type="text" autocomplete="off" option="ftp" name="name" placeholder="<?php esc_attr_e('Enter an unique alias: e.g. FTP-001', 'wpvivid-backuprestore'); ?>" class="regular-text" onkeyup="value=value.replace(/[^a-zA-Z0-9\-_]/g,'')" />
                        </div>
                    </td>
                    <td class="column-description desc">
                        <div class="wpvivid-storage-form-desc">
                            <i><?php esc_html_e('A name to help you identify the storage if you have multiple remote storage connected.', 'wpvivid-backuprestore'); ?></i>
                        </div>
                    </td>
                </tr>
                <tr>
                    <td class="plugin-title column-primary">
                        <div class="wpvivid-storage-form">
                            <input type="text" autocomplete="off" option="ftp" name="server" placeholder="<?php esc_attr_e('FTP server (server\'s port 21)','wpvivid-backuprestore'); ?>" class="regular-text"/>
                        </div>
                    </td>
                    <td class="column-description desc">
                        <div class="wpvivid-storage-form-desc">
                            <i style="margin-right: 10px;"><?php esc_html_e('Enter the FTP server.', 'wpvivid-backuprestore'); ?></i>
                        </div>
                    </td>
                </tr>
                <tr>
                    <td class="plugin-title column-primary">
                        <div class="wpvivid-storage-form">
                            <input type="text" autocomplete="off" class="regular-text" value="21" readonly="readonly" />
                        </div>
                    </td>
                    <td class="column-description desc">
                        <div class="wpvivid-storage-form-desc">
                            <a href="https://docs.wpvivid.com/wpvivid-backup-pro-ftp-change-ftp-default-port.html"><?php esc_html_e('Pro feature: Change the FTP default port number', 'wpvivid-backuprestore'); ?></a>
                        </div>
                    </td>
                </tr>
                <tr>
                    <td class="plugin-title column-primary">
                        <div class="wpvivid-storage-form">
                            <input type="text" class="regular-text" autocomplete="off" option="ftp" name="username" placeholder="<?php esc_attr_e('FTP login', 'wpvivid-backuprestore'); ?>" />
                        </div>
                    </td>
                    <td class="column-description desc">
                        <div class="wpvivid-storage-form-desc">
                            <i><?php esc_html_e('Enter your FTP server user name.', 'wpvivid-backuprestore'); ?></i>
                        </div>
                    </td>
                </tr>
                <tr>
                    <td class="plugin-title column-primary">
                        <div class="wpvivid-storage-form">
                            <input type="password" class="regular-text" autocomplete="new-password" option="ftp" name="password" placeholder="<?php esc_attr_e('FTP password', 'wpvivid-backuprestore'); ?>" />
                        </div>
                    </td>
                    <td class="column-description desc">
                        <div class="wpvivid-storage-form-desc">
                            <i><?php esc_html_e('Enter the FTP server password.', 'wpvivid-backuprestore'); ?></i>
                        </div>
                    </td>
                </tr>
                <tr>
                    <td class="plugin-title column-primary">
                        <div class="wpvivid-storage-form">
                            <input type="text" autocomplete="off" option="ftp" name="path" placeholder="<?php esc_attr_e('Absolute path must exist(e.g. /home/username)', 'wpvivid-backuprestore'); ?>" class="regular-text"/>
                        </div>
                    </td>
                    <td class="column-description desc">
                        <div class="wpvivid-storage-form-desc">
                            <i><?php esc_html_e('Enter an absolute path and a custom subdirectory (optional) for holding the backups of current website. For example, /home/username/customfolder', 'wpvivid-backuprestore'); ?></i>
                        </div>
                    </td>
                </tr>
                <tr>
                    <td class="plugin-title column-primary">
                        <div class="wpvivid-storage-select">
                            <label>
                                <input type="checkbox" option="ftp" name="default" checked /><?php esc_html_e('Set as the default remote storage.', 'wpvivid-backuprestore'); ?>
                            </label>
                        </div>
                    </td>
                    <td class="column-description desc">
                        <div class="wpvivid-storage-form-desc">
                            <i><?php esc_html_e('Once checked, all this sites backups sent to a remote storage destination will be uploaded to this storage by default.', 'wpvivid-backuprestore'); ?></i>
                        </div>
                    </td>
                </tr>
                <tr>
                    <td class="plugin-title column-primary">
                        <div class="wpvivid-storage-select">
                            <label>
                                <input type="checkbox" option="ftp" name="passive" checked /><?php esc_html_e('Uncheck this to enable FTP active mode.', 'wpvivid-backuprestore'); ?>
                            </label>
                        </div>
                    </td>
                    <td class="column-description desc">
                        <div class="wpvivid-storage-form-desc">
                            <i><?php esc_html_e('Uncheck the option to use FTP active mode when transferring files. Make sure the FTP server you are configuring supports the active FTP mode.', 'wpvivid-backuprestore'); ?></i>
                        </div>
                    </td>
                </tr>
                </form>
                <tr>
                    <td class="plugin-title column-primary">
                        <div class="wpvivid-storage-form">
                            <input class="button-primary" type="submit" option="add-remote" value="<?php esc_attr_e('Test and Add', 'wpvivid-backuprestore'); ?>">
                        </div>
                    </td>
                    <td class="column-description desc">
                        <div class="wpvivid-storage-form-desc">
                            <i><?php esc_html_e('Click the button to connect to FTP server and add it to the storage list below.', 'wpvivid-backuprestore'); ?></i>
                        </div>
                    </td>
                </tr>
                </tbody>
            </table>
        </div>
        <?php
    }

    public function wpvivid_edit_storage_page_ftp()
    {
        ?>
        <div id="remote_storage_edit_ftp" class="postbox storage-account-block remote-storage-edit" style="display:none;">
            <div style="padding: 0 10px 10px 0;"><strong><?php esc_html_e('Enter Your FTP Account', 'wpvivid-backuprestore'); ?></strong></div>
            <table class="wp-list-table widefat plugins" style="width:100%;">
                <tbody>
                <form>
                <tr>
                    <td class="plugin-title column-primary">
                        <div class="wpvivid-storage-form">
                            <input type="text" autocomplete="off" option="edit-ftp" name="name" placeholder="<?php esc_attr_e('Enter an unique alias: e.g. FTP-001', 'wpvivid-backuprestore'); ?>" class="regular-text" onkeyup="value=value.replace(/[^a-zA-Z0-9\-_]/g,'')" />
                        </div>
                    </td>
                    <td class="column-description desc">
                        <div class="wpvivid-storage-form-desc">
                            <i><?php esc_html_e('A name to help you identify the storage if you have multiple remote storage connected.', 'wpvivid-backuprestore'); ?></i>
                        </div>
                    </td>
                </tr>
                <tr>
                    <td class="plugin-title column-primary">
                        <div class="wpvivid-storage-form">
                            <input type="text" autocomplete="off" option="edit-ftp" name="server" placeholder="<?php esc_attr_e('FTP server (server\'s port 21)', 'wpvivid-backuprestore'); ?>" class="regular-text"/>
                        </div>
                    </td>
                    <td class="column-description desc">
                        <div class="wpvivid-storage-form-desc">
                            <i style="margin-right: 10px;"><?php esc_html_e('Enter the FTP server.', 'wpvivid-backuprestore'); ?></i>
                        </div>
                    </td>
                </tr>
                <tr>
                    <td class="plugin-title column-primary">
                        <div class="wpvivid-storage-form">
                            <input type="text" class="regular-text" autocomplete="off" option="edit-ftp" name="username" placeholder="<?php esc_attr_e('FTP login', 'wpvivid-backuprestore'); ?>" />
                        </div>
                    </td>
                    <td class="column-description desc">
                        <div class="wpvivid-storage-form-desc">
                            <i><?php esc_html_e('Enter your FTP server user name.', 'wpvivid-backuprestore'); ?></i>
                        </div>
                    </td>
                </tr>
                <tr>
                    <td class="plugin-title column-primary">
                        <div class="wpvivid-storage-form">
                            <input type="password" class="regular-text" autocomplete="new-password" option="edit-ftp" name="password" placeholder="<?php esc_attr_e('FTP password', 'wpvivid-backuprestore'); ?>" />
                        </div>
                    </td>
                    <td class="column-description desc">
                        <div class="wpvivid-storage-form-desc">
                            <i><?php esc_html_e('Enter the FTP server password.', 'wpvivid-backuprestore'); ?></i>
                        </div>
                    </td>
                </tr>
                <tr>
                    <td class="plugin-title column-primary">
                        <div class="wpvivid-storage-form">
                            <input type="text" autocomplete="off" option="edit-ftp" name="path" placeholder="<?php esc_attr_e('Absolute path must exist(e.g. /home/username)', 'wpvivid-backuprestore'); ?>" class="regular-text"/>
                        </div>
                    </td>
                    <td class="column-description desc">
                        <div class="wpvivid-storage-form-desc">
                            <i><?php esc_html_e('Enter an absolute path and a custom subdirectory (optional) for holding the backups of current website. For example, /home/username/customfolder', 'wpvivid-backuprestore'); ?></i>
                        </div>
                    </td>
                </tr>
                <tr>
                    <td class="plugin-title column-primary">
                        <div class="wpvivid-storage-select">
                            <label>
                                <input type="checkbox" option="edit-ftp" name="passive" checked /><?php esc_html_e('Uncheck this to enable FTP active mode.', 'wpvivid-backuprestore'); ?>
                            </label>
                        </div>
                    </td>
                    <td class="column-description desc">
                        <div class="wpvivid-storage-form-desc">
                            <i><?php esc_html_e('Uncheck the option to use FTP active mode when transferring files. Make sure the FTP server you are configuring supports the active FTP mode.', 'wpvivid-backuprestore'); ?></i>
                        </div>
                    </td>
                </tr>
                </form>
                <tr>
                    <td class="plugin-title column-primary">
                        <div class="wpvivid-storage-form">
                            <input class="button-primary" type="submit" option="edit-remote" value="<?php esc_attr_e('Save Changes', 'wpvivid-backuprestore'); ?>">
                        </div>
                    </td>
                    <td class="column-description desc">
                        <div class="wpvivid-storage-form-desc">
                            <i><?php esc_html_e('Click the button to save the changes.', 'wpvivid-backuprestore'); ?></i>
                        </div>
                    </td>
                </tr>
                </tbody>
            </table>
        </div>
        <?php
    }

    public function wpvivid_remote_pic_ftp($remote){
        $remote['ftp']['default_pic'] = '/admin/partials/images/storage-ftp(gray).png';
        $remote['ftp']['selected_pic'] = '/admin/partials/images/storage-ftp.png';
        $remote['ftp']['title'] = 'FTP';
        return $remote;
    }

    public function test_connect()
    {
        $passive =$this->options['passive'];
        $host = $this->options['host'];
        $username = $this->options['username'];
        if(isset($this->options['is_encrypt']) && $this->options['is_encrypt'] == 1){
            $password = base64_decode($this->options['password']);
        }
        else {
            $password = $this->options['password'];
        }
        $path = $this->options['path'];
        $port = empty($this->options['port'])?21:$this->options['port'];
        $conn = $this -> do_connect($host,$username,$password,$port);
        if(is_array($conn) && array_key_exists('result',$conn))
            return $conn;
        ftp_pasv($conn,$passive);
        return $this->do_chdir($conn,$path);
    }


    public function sanitize_options($skip_name='')
    {
        $ret['result']=WPVIVID_FAILED;
        if(!isset($this->options['name']))
        {
            $ret['error']=__('Warning: An alias for remote storage is required.','wpvivid-backuprestore');
            return $ret;
        }

        $this->options['name']=sanitize_text_field($this->options['name']);

        if(empty($this->options['name']))
        {
            $ret['error']=__('Warning: An alias for remote storage is required.','wpvivid-backuprestore');
            return $ret;
        }

        $remoteslist=WPvivid_Setting::get_all_remote_options();
        foreach ($remoteslist as $key=>$value)
        {
            if(isset($value['name'])&&$value['name'] == $this->options['name']&&$skip_name!=$value['name'])
            {
                $ret['error']="Warning: The alias already exists in storage list.";
                return $ret;
            }
        }

        $this->options['server']=sanitize_text_field($this->options['server']);

        if(empty($this->options['server']))
        {
            $ret['error']="Warning: The FTP server is required.";
            return $ret;
        }
        $res = explode(':',$this -> options['server']);
        if(sizeof($res) > 1){
            $this ->options['host'] = $res[0];
            if($res[1] != 21){
                $ret['error']='Currently, only port 21 is supported.';
                return $ret;
            }

        }else{
            $this -> options['host'] = $res[0];
        }


        if(!isset($this->options['username']))
        {
            $ret['error']="Warning: The FTP login is required.";
            return $ret;
        }

        $this->options['username']=sanitize_text_field($this->options['username']);

        if(empty($this->options['username']))
        {
            $ret['error']="Warning: The FTP login is required.";
            return $ret;
        }

        if(!isset($this->options['password'])||empty($this->options['password']))
        {
            $ret['error']="Warning: The FTP password is required.";
            return $ret;
        }

        $this->options['password']=sanitize_text_field($this->options['password']);

        if(empty($this->options['password']))
        {
            $ret['error']="Warning: The FTP password is required.";
            return $ret;
        }
        $this->options['password'] = base64_encode($this->options['password']);
        $this->options['is_encrypt'] = 1;

        if(!isset($this->options['path'])||empty($this->options['path']))
        {
            $ret['error']="Warning: The storage path is required.";
            return $ret;
        }

        $this->options['path']=sanitize_text_field($this->options['path']);

        if(empty($this->options['path']))
        {
            $ret['error']="Warning: The storage path is required.";
            return $ret;
        }

        if($this->options['path']=='/')
        {
            $ret['error']= __('Warning: Root directory is forbidden to set to \'/\'.', 'wpvivid-backuprestore');
            return $ret;
        }

        $ret['result']=WPVIVID_SUCCESS;
        $ret['options']=$this->options;
        return $ret;
    }

    public function do_connect($server,$username,$password,$port = 21)
    {
        $conn = ftp_connect( $server, $port, $this ->time_out );

        if($conn)
        {
            if(ftp_login($conn,$username,$password))
            {
                return $conn;
            }
            else
            {
                return array('result'=>WPVIVID_FAILED,'error'=>'Login failed. You have entered the incorrect credential(s). Please try again.');
            }
        }
        else{
            return array('result'=>WPVIVID_FAILED,'error'=>'Login failed. The connection has timed out. Please try again later.');
        }
	}
    public function do_chdir($conn,$path){
        @ftp_chdir($conn,'/');
        if(!@ftp_chdir($conn,$path))
        {
            $parts = explode('/',$path);
            foreach($parts as $part){
                if($part !== '') {
                    if (!@ftp_chdir($conn, $part)) {
                        if (!ftp_mkdir($conn, $part)) {
                            return array('result' => WPVIVID_FAILED, 'error' => 'Failed to create a backup. Make sure you have sufficient privileges to perform the operation.');
                        }

                        if (!@ftp_chdir($conn, $part)) {
                            return array('result' => WPVIVID_FAILED, 'error' => 'Failed to create a backup. Make sure you have sufficient privileges to perform the operation.');
                        }
                    }
                }
            }

            /*if ( ! ftp_mkdir( $conn, $path ) )
            {
                return array('result'=>WPVIVID_FAILED,'error'=>'Failed to create a backup. Make sure you have sufficient privileges to perform the operation.');
            }
            if (!@ftp_chdir($conn,$path))
            {
                return array('result'=>WPVIVID_FAILED,'error'=>'Failed to create a backup. Make sure you have sufficient privileges to perform the operation.');
            }*/
        }
        $temp_file = md5(wp_rand());
        $temp_path = trailingslashit(WP_CONTENT_DIR).WPvivid_Setting::get_backupdir().DIRECTORY_SEPARATOR.$temp_file;
        file_put_contents($temp_path,print_r($temp_file,true));
        if(! ftp_put($conn,trailingslashit($path).$temp_file,$temp_path,FTP_BINARY)){
            return array('result'=>WPVIVID_FAILED,'error'=>'Failed to add FTP storage. It can be because the FTP folder permissions are insufficient, or calling PHP ftp_put function of your web server failed. Please make sure the folder has write permission and the ftp_put function works properly.');
        }
        @wp_delete_file($temp_path);
        @ftp_delete($conn,trailingslashit($path).$temp_file);
        return array('result'=>WPVIVID_SUCCESS);
    }

	public function upload($task_id,$files,$callback = '')
    {
        global $wpvivid_plugin;
        $this -> callback = $callback;

        $passive =$this->options['passive'];
        $host = $this->options['host'];
        $username = $this->options['username'];
        if(isset($this->options['is_encrypt']) && $this->options['is_encrypt'] == 1){
            $password = base64_decode($this->options['password']);
        }
        else {
            $password = $this->options['password'];
        }
        $path = $this->options['path'];
        $port = empty($this->options['port'])?21:$this->options['port'];

        $upload_job=WPvivid_taskmanager::get_backup_sub_task_progress($task_id,'upload',WPVIVID_REMOTE_FTP);
        if(empty($upload_job))
        {
            $job_data=array();
            foreach ($files as $file)
            {
                if(!file_exists($file))
                    return array('result'=>WPVIVID_FAILED,'error'=>$file.' not found. The file might has been moved, renamed or deleted. Please back it up again.');
                $file_data['size']=filesize($file);
                $file_data['uploaded']=0;
                $job_data[basename($file)]=$file_data;
            }
            WPvivid_taskmanager::update_backup_sub_task_progress($task_id,'upload',WPVIVID_REMOTE_FTP,WPVIVID_UPLOAD_UNDO,'Start uploading.',$job_data);
            $upload_job=WPvivid_taskmanager::get_backup_sub_task_progress($task_id,'upload',WPVIVID_REMOTE_FTP);
        }
        $wpvivid_plugin->wpvivid_log->WriteLog('Connecting to server '.$host,'notice');
        $conn = $this -> do_connect($host,$username,$password,$port);
		if(is_array($conn) && array_key_exists('result',$conn))
			return $conn;
        ftp_pasv($conn,$passive);
        $wpvivid_plugin->wpvivid_log->WriteLog('chdir '.$path,'notice');
		$str = $this -> do_chdir($conn , $path);
		if($str['result'] !== WPVIVID_SUCCESS)
			return $str;

		$flag = true;
		$error = '';
		foreach ($files as $key => $file)
		{
            if(is_array($upload_job['job_data']) && array_key_exists(basename($file),$upload_job['job_data']))
            {
                if($upload_job['job_data'][basename($file)]['uploaded']==1)
                    continue;
            }
            $this ->last_time = time();
            $this -> last_size = 0;
            $wpvivid_plugin->wpvivid_log->WriteLog('Start uploading '.basename($file),'notice');
			$remote_file = trailingslashit($path).basename($file);
            if(!file_exists($file))
                return array('result'=>WPVIVID_FAILED,'error'=>$file.' not found. The file might has been moved, renamed or deleted. Please back it up again.');

            $wpvivid_plugin->set_time_limit($task_id);

            for($i =0;$i <WPVIVID_REMOTE_CONNECT_RETRY_TIMES;$i ++)
			{
                $this -> current_file_name = basename($file);
                $this -> current_file_size = filesize($file);
                $this -> last_time = time();
                $this -> last_size = 0;
                $local_handle = fopen($file,'rb');
                if(!$local_handle)
                {
                    return array('result'=>WPVIVID_FAILED,'error'=>'Failed to open '.$this->current_file_name.'.');
                }
                $status = ftp_nb_fput($conn,$remote_file,$local_handle,FTP_BINARY,0);
                while ($status == FTP_MOREDATA)
                {
                    $status = ftp_nb_continue($conn);
                    if((time() - $this -> last_time) >3)
                    {
                        if(is_callable($callback)){
                            call_user_func_array($callback,array(ftell($local_handle),$this -> current_file_name,
                                $this->current_file_size,$this -> last_time,$this -> last_size));
                        }
                        $this -> last_size = ftell($local_handle);
                        $this -> last_time = time();
                    }
                }
                if ($status != FTP_FINISHED)
                {
                    return array('result'=>WPVIVID_FAILED,'error'=>'Uploading '.$remote_file.' to FTP server failed. '.$remote_file.' might be deleted or network doesn\'t work properly. Please verify the file and confirm the network connection and try again later.');
                }

                if($status == FTP_FINISHED)
                {
                    WPvivid_taskmanager::wpvivid_reset_backup_retry_times($task_id);
                    $wpvivid_plugin->wpvivid_log->WriteLog('Finished uploading '.basename($file),'notice');
                    $upload_job['job_data'][basename($file)]['uploaded']=1;
                    WPvivid_taskmanager::update_backup_sub_task_progress($task_id,'upload',WPVIVID_REMOTE_FTP,WPVIVID_UPLOAD_SUCCESS,'Uploading '.basename($file).' completed.',$upload_job['job_data']);
                    break;
                }

                if($status != FTP_FINISHED && $i == (WPVIVID_REMOTE_CONNECT_RETRY_TIMES - 1))
                {
                   $flag = false;
                   $error = 'Uploading '.basename($file).' to FTP server failed. '.basename($file).' might be deleted or network doesn\'t work properly. Please verify the file and confirm the network connection and try again later.';
                   break 2;
                }
                sleep(WPVIVID_REMOTE_CONNECT_RETRY_INTERVAL);
            }
		}

		if($flag){
            return array('result'=>WPVIVID_SUCCESS);
        }else{
            return array('result'=>WPVIVID_FAILED,'error'=>$error);
        }
	}

    public function download($file,$local_path,$callback = '')
    {
        try {
            global $wpvivid_plugin;
            $passive = $this->options['passive'];
            $host = $this->options['host'];
            $username = $this->options['username'];
            if(isset($this->options['is_encrypt']) && $this->options['is_encrypt'] == 1){
                $password = base64_decode($this->options['password']);
            }
            else {
                $password = $this->options['password'];
            }
            $path = $this->options['path'];
            $port = empty($this->options['port']) ? 21 : $this->options['port'];

            $local_path = trailingslashit($local_path) . $file['file_name'];
            $remote_file = trailingslashit($path) . $file['file_name'];

            $this->current_file_name = $file['file_name'];
            $this->current_file_size = $file['size'];

            $wpvivid_plugin->wpvivid_download_log->WriteLog('Connecting FTP server.','notice');
            $conn = $this->do_connect($host, $username, $password, $port);
            if (is_array($conn) && array_key_exists('result', $conn)) {
                return $conn;
            }

            ftp_pasv($conn, $passive);
            $wpvivid_plugin->wpvivid_download_log->WriteLog('Create local file.','notice');
            $local_handle = fopen($local_path, 'ab');
            if (!$local_handle) {
                return array('result' => WPVIVID_FAILED, 'error' => 'Unable to create the local file. Please make sure the folder is writable and try again.');
            }

            $stat = fstat($local_handle);
            $offset = $stat['size'];
            $wpvivid_plugin->wpvivid_download_log->WriteLog('Downloading file ' . $file['file_name'] . ', Size: ' . $file['size'] ,'notice');
            $status = ftp_nb_fget($conn, $local_handle, $remote_file, FTP_BINARY, $offset);
            while ($status == FTP_MOREDATA) {
                $status = ftp_nb_continue($conn);
                if ((time() - $this->last_time) > 3) {
                    if (is_callable($callback)) {
                        call_user_func_array($callback, array(ftell($local_handle), $this->current_file_name,
                            $this->current_file_size, $this->last_time, $this->last_size));
                    }
                    $this->last_size = ftell($local_handle);
                    $this->last_time = time();
                }
            }

            if(filesize($local_path) == $file['size']){
                if($wpvivid_plugin->wpvivid_check_zip_valid()) {
                    $res = TRUE;
                }
                else{
                    $res = FALSE;
                }
            }
            else{
                $res = FALSE;
            }

            if ($status != FTP_FINISHED || $res !== TRUE) {
                @wp_delete_file($local_path);
                return array('result' => WPVIVID_FAILED, 'error' => 'Downloading ' . $remote_file . ' failed. ' . $remote_file . ' might be deleted or network doesn\'t work properly. Please verify the file and confirm the network connection and try again later.');
            }

            ftp_close($conn);
            fclose($local_handle);
            return array('result' => WPVIVID_SUCCESS);
        }
        catch (Exception $error){
            $message = 'An exception has occurred. class: '.get_class($error).';msg: '.$error->getMessage().';code: '.$error->getCode().';line: '.$error->getLine().';in_file: '.$error->getFile().';';
            error_log($message);
            return array('result'=>WPVIVID_FAILED, 'error'=>$message);
        }
    }

	public function cleanup($file){
        $host = $this->options['host'];
        $username = $this->options['username'];
        if(isset($this->options['is_encrypt']) && $this->options['is_encrypt'] == 1){
            $password = base64_decode($this->options['password']);
        }
        else {
            $password = $this->options['password'];
        }
        $path = $this->options['path'];
        $port = empty($this->options['port'])?21:$this->options['port'];

        $conn = $this -> do_connect($host,$username,$password,$port);
        if(is_array($conn) && array_key_exists('result',$conn))
            return $conn;

        foreach ($file as $value){
            @ftp_delete($conn,trailingslashit($path).$value);
        }
        return array('result'=>WPVIVID_SUCCESS);
	}

	public function init_remotes($remote_collection){
        $remote_collection[WPVIVID_REMOTE_FTP] = 'WPvivid_FTPClass';
        return $remote_collection;
    }

    public function wpvivid_get_out_of_date_ftp($out_of_date_remote, $remote)
    {
        if($remote['type'] == WPVIVID_REMOTE_FTP){
            $out_of_date_remote = $remote['path'];
        }
        return $out_of_date_remote;
    }

    public function wpvivid_storage_provider_ftp($storage_type)
    {
        if($storage_type == WPVIVID_REMOTE_FTP){
            $storage_type = 'FTP';
        }
        return $storage_type;
    }
}includes/customclass/class-wpvivid-dropbox.php000064400000126303151327705670015713 0ustar00<?php

if (!defined('WPVIVID_PLUGIN_DIR')){
    die;
}
if(!defined('WPVIVID_REMOTE_DROPBOX')){
    define('WPVIVID_REMOTE_DROPBOX','dropbox');
}
if(!defined('WPVIVID_DROPBOX_DEFAULT_FOLDER'))
    define('WPVIVID_DROPBOX_DEFAULT_FOLDER','/');
require_once WPVIVID_PLUGIN_DIR . '/includes/customclass/class-wpvivid-base-dropbox.php';
require_once WPVIVID_PLUGIN_DIR . '/includes/customclass/class-wpvivid-remote.php';
class WPvivid_Dropbox extends WPvivid_Remote
{

    private $options;
    private $upload_chunk_size = 2097152;
    private $download_chunk_size = 2097152;
    private $redirect_url = 'https://auth.wpvivid.com/dropbox_v3/';
    public $add_remote;
    public function __construct($options = array())
    {
        if(empty($options))
        {
            if(!defined('WPVIVID_INIT_STORAGE_TAB_DROPBOX'))
            {
                add_action('init', array($this, 'handle_auth_actions'));
                //wpvivid_dropbox_add_remote
                add_action('wp_ajax_wpvivid_dropbox_add_remote',array( $this,'finish_add_remote'));

                add_action('wpvivid_delete_remote_token',array($this,'revoke'));

                add_filter('wpvivid_remote_register', array($this, 'init_remotes'),10);
                add_action('wpvivid_add_storage_tab',array($this,'wpvivid_add_storage_tab_dropbox'), 11);
                add_action('wpvivid_add_storage_page',array($this,'wpvivid_add_storage_page_dropbox'), 11);
                add_action('wpvivid_edit_remote_page',array($this,'wpvivid_edit_storage_page_dropbox'), 11);
                add_filter('wpvivid_remote_pic',array($this,'wpvivid_remote_pic_dropbox'),10);
                add_filter('wpvivid_get_out_of_date_remote',array($this,'wpvivid_get_out_of_date_dropbox'),10,2);
                add_filter('wpvivid_storage_provider_tran',array($this,'wpvivid_storage_provider_dropbox'),10);
                add_filter('wpvivid_get_root_path',array($this,'wpvivid_get_root_path_dropbox'),10);
                add_filter('wpvivid_pre_add_remote',array($this, 'pre_add_remote'),10,2);
                define('WPVIVID_INIT_STORAGE_TAB_DROPBOX',1);
            }
        }else{
            $this -> options = $options;
        }
        $this->add_remote=false;
    }

    public function pre_add_remote($remote,$id)
    {
        if($remote['type']==WPVIVID_REMOTE_DROPBOX)
        {
            $remote['id']=$id;
        }

        return $remote;
    }

    public function test_connect()
    {
        return array('result' => WPVIVID_SUCCESS);
    }

    public function sanitize_options($skip_name='')
    {
        $ret['result']=WPVIVID_SUCCESS;

        if(!isset($this->options['name']))
        {
            $ret['error']="Warning: An alias for remote storage is required.";
            return $ret;
        }

        $this->options['name']=sanitize_text_field($this->options['name']);

        if(empty($this->options['name']))
        {
            $ret['error']="Warning: An alias for remote storage is required.";
            return $ret;
        }

        $remoteslist=WPvivid_Setting::get_all_remote_options();
        foreach ($remoteslist as $key=>$value)
        {
            if(isset($value['name'])&&$value['name'] == $this->options['name']&&$skip_name!=$value['name'])
            {
                $ret['error']="Warning: The alias already exists in storage list.";
                return $ret;
            }
        }

        $ret['options']=$this->options;
        return $ret;
    }

    public function upload($task_id, $files, $callback = '')
    {
        global $wpvivid_plugin;

        $options = $this -> options;
        $dropbox = new Dropbox_Base($options);
        $ret=$dropbox->check_token();
        if($ret['result']=='failed')
        {
            return $ret;
        }
        $upload_job=WPvivid_taskmanager::get_backup_sub_task_progress($task_id,'upload',WPVIVID_REMOTE_DROPBOX);
        if(empty($upload_job))
        {
            $job_data=array();
            foreach ($files as $file)
            {
                $file_data['size']=filesize($file);
                $file_data['uploaded']=0;
                $file_data['session_id']='';
                $file_data['offset']=0;
                $job_data[basename($file)]=$file_data;
            }
            WPvivid_taskmanager::update_backup_sub_task_progress($task_id,'upload',WPVIVID_REMOTE_DROPBOX,WPVIVID_UPLOAD_UNDO,'Start uploading',$job_data);
            $upload_job=WPvivid_taskmanager::get_backup_sub_task_progress($task_id,'upload',WPVIVID_REMOTE_DROPBOX);
        }

        foreach ($files as $file)
        {
            if(is_array($upload_job['job_data']) &&array_key_exists(basename($file),$upload_job['job_data']))
            {
                if($upload_job['job_data'][basename($file)]['uploaded']==1)
                    continue;
            }
            $ret=$dropbox->check_token();
            if($ret['result']=='failed')
            {
                return $ret;
            }

            $this -> last_time = time();
            $this -> last_size = 0;
            $wpvivid_plugin->wpvivid_log->WriteLog('Start uploading '.basename($file),'notice');
            $wpvivid_plugin->set_time_limit($task_id);
            if(!file_exists($file))
                return array('result' =>WPVIVID_FAILED,'error' =>$file.' not found. The file might has been moved, renamed or deleted. Please reload the list and verify the file exists.');
            $result = $this -> _put($task_id,$dropbox,$file,$callback);
            if($result['result'] !==WPVIVID_SUCCESS){
                $wpvivid_plugin->wpvivid_log->WriteLog('Uploading '.basename($file).' failed.','notice');
                return $result;
            }
            else
            {
                WPvivid_taskmanager::wpvivid_reset_backup_retry_times($task_id);
            }
            $wpvivid_plugin->wpvivid_log->WriteLog('Finished uploading '.basename($file),'notice');
            $upload_job['job_data'][basename($file)]['uploaded'] = 1;
            WPvivid_taskmanager::update_backup_sub_task_progress($task_id, 'upload', WPVIVID_REMOTE_DROPBOX, WPVIVID_UPLOAD_SUCCESS, 'Uploading ' . basename($file) . ' completed.', $upload_job['job_data']);
        }
        return array('result' =>WPVIVID_SUCCESS);
    }
    private function _put($task_id,$dropbox,$file,$callback){
        global $wpvivid_plugin;
        $options = $this -> options;
        $path = trailingslashit($options['path']).basename($file);
        $upload_job=WPvivid_taskmanager::get_backup_sub_task_progress($task_id,'upload',WPVIVID_REMOTE_DROPBOX);
        $this -> current_file_size = filesize($file);
        $this -> current_file_name = basename($file);

        if($this -> current_file_size > $this -> upload_chunk_size)
        {

            if(empty($upload_job['job_data'][basename($file)]['session_id']))
            {
                $wpvivid_plugin->wpvivid_log->WriteLog('Creating upload session.','notice');
                //WPvivid_taskmanager::update_backup_sub_task_progress($task_id,'upload',WPVIVID_REMOTE_DROPBOX,WPVIVID_UPLOAD_UNDO,'Start uploading '.basename($file).'.',$upload_job['job_data']);
                $result = $dropbox -> upload_session_start();
                if(isset($result['error_summary']))
                {
                    return array('result'=>WPVIVID_FAILED,'error'=>$result['error_summary']);
                }

                $upload_job['job_data'][basename($file)]['session_id']= $result['session_id'];
                WPvivid_taskmanager::update_backup_sub_task_progress($task_id,'upload',WPVIVID_REMOTE_DROPBOX,WPVIVID_UPLOAD_UNDO,'Start uploading '.basename($file).'.',$upload_job['job_data']);

                $build_id = $result['session_id'];
            }
            else
            {
                $build_id = $upload_job['job_data'][basename($file)]['session_id'];
            }

            $result = $this -> large_file_upload($task_id,$build_id,$file,$dropbox,$callback);
        }else{
            $wpvivid_plugin->wpvivid_log->WriteLog('Uploaded files are less than 2M.','notice');
            $result = $dropbox -> upload($path,$file);
            if(isset($result['error_summary'])){
                WPvivid_taskmanager::update_backup_sub_task_progress($task_id,'upload',WPVIVID_REMOTE_DROPBOX,WPVIVID_UPLOAD_FAILED,'Uploading '.basename($file).' failed.',$upload_job['job_data']);
                $result = array('result' => WPVIVID_FAILED,'error' => $result['error_summary']);
            }else{
                WPvivid_taskmanager::update_backup_sub_task_progress($task_id,'upload',WPVIVID_REMOTE_DROPBOX,WPVIVID_UPLOAD_SUCCESS,'Uploading '.basename($file).' completed.',$upload_job['job_data']);
                $result = array('result'=> WPVIVID_SUCCESS);
            }
        }
        return $result;
    }

    public function large_file_upload($task_id,$session_id,$file,$dropbox,$callback)
    {
        global $wpvivid_plugin;
        $fh = fopen($file,'rb');

        $upload_job=WPvivid_taskmanager::get_backup_sub_task_progress($task_id,'upload',WPVIVID_REMOTE_DROPBOX);

        $offset = $upload_job['job_data'][basename($file)]['offset'];
        $wpvivid_plugin->wpvivid_log->WriteLog('offset:'.size_format($offset,2),'notice');
        if ($offset > 0)
        {
            fseek($fh, $offset);
        }

        while($data =fread($fh,$this -> upload_chunk_size))
        {
            $ret = $this -> _upload_loop($session_id,$offset,$data,$dropbox);
            if($ret['result'] !== WPVIVID_SUCCESS)
            {
                return $ret;
            }

            if((time() - $this -> last_time) >3)
            {
                if(is_callable($callback))
                {
                    call_user_func_array($callback,array(min($offset + $this -> upload_chunk_size,$this -> current_file_size),$this -> current_file_name,
                        $this->current_file_size,$this -> last_time,$this -> last_size));
                }
                $this -> last_size = $offset;
                $this -> last_time = time();
            }

            if(isset($ret['correct_offset']))
            {
                $offset = $ret['correct_offset'];
                fseek($fh, $offset);
                $wpvivid_plugin->wpvivid_log->WriteLog('correct_offset:'.size_format($offset,2),'notice');
            }
            else
            {
                $offset = ftell($fh);
            }

            $upload_job['job_data'][basename($file)]['offset']=$offset;
            $wpvivid_plugin->wpvivid_log->WriteLog('offset:'.size_format($offset,2),'notice');
            WPvivid_taskmanager::update_backup_sub_task_progress($task_id,'upload',WPVIVID_REMOTE_DROPBOX,WPVIVID_UPLOAD_UNDO,'Uploading '.basename($file),$upload_job['job_data']);
        }

        $options = $this -> options;
        $path = trailingslashit($options['path']).basename($file);
        $result = $dropbox -> upload_session_finish($session_id,$offset,$path);
        if(isset($result['error_summary']))
        {
            $wpvivid_plugin->wpvivid_log->WriteLog('offset:'.$offset,'notice');
            $wpvivid_plugin->wpvivid_log->WriteLog('result:'.wp_json_encode($result),'notice');
            $ret = array('result' => WPVIVID_FAILED,'error' => $result['error_summary']);
        }else{
            $ret = array('result'=> WPVIVID_SUCCESS);
        }

        fclose($fh);
        return $ret;
    }
    public function _upload_loop($session_id,$offset,$data,$dropbox)
    {
        $result['result']=WPVIVID_SUCCESS;
        for($i =0;$i <WPVIVID_REMOTE_CONNECT_RETRY_TIMES; $i ++)
        {
            $result = $dropbox -> upload_session_append_v2($session_id,$offset,$data);
            if(isset($result['error_summary']))
            {
                if(strstr($result['error_summary'],'incorrect_offset'))
                {
                    $result['result']=WPVIVID_SUCCESS;
                    $result['correct_offset']=$result['error']['correct_offset'];
                    return $result;
                }
                else
                {
                    $result = array('result' => WPVIVID_FAILED,'error' => 'Uploading '.$this -> current_file_name.' to Dropbox server failed. '.$result['error_summary']);
                }
            }
            else
            {
                return array('result' => WPVIVID_SUCCESS);
            }
        }
        return $result;
    }

    public function download($file, $local_path, $callback = '')
    {
        try {
            global $wpvivid_plugin;
            $wpvivid_plugin->wpvivid_download_log->WriteLog('Remote type: Dropbox.','notice');
            $this->current_file_name = $file['file_name'];
            $this->current_file_size = $file['size'];
            $options = $this->options;
            $dropbox = new Dropbox_Base($options);
            $ret=$dropbox->check_token();
            if($ret['result']=='failed')
            {
                return $ret;
            }
            $file_path = trailingslashit($local_path) . $this->current_file_name;
            $start_offset = file_exists($file_path) ? filesize($file_path) : 0;
            $wpvivid_plugin->wpvivid_download_log->WriteLog('Create local file.','notice');
            $fh = fopen($file_path, 'a');
            $wpvivid_plugin->wpvivid_download_log->WriteLog('Downloading file ' . $file['file_name'] . ', Size: ' . $file['size'] ,'notice');
            while ($start_offset < $this->current_file_size) {
                $last_byte = min($start_offset + $this->download_chunk_size - 1, $this->current_file_size - 1);
                $headers = array("Range: bytes=$start_offset-$last_byte");
                $response = $dropbox->download(trailingslashit($options['path']) . $this->current_file_name, $headers);
                if (isset($response['error_summary'])) {
                    return array('result' => WPVIVID_FAILED, 'error' => 'Downloading ' . trailingslashit($options['path']) . $this->current_file_name . ' failed.' . $response['error_summary']);
                }
                if (!fwrite($fh, $response)) {
                    return array('result' => WPVIVID_FAILED, 'error' => 'Downloading ' . trailingslashit($options['path']) . $this->current_file_name . ' failed.');
                }
                clearstatcache();
                $state = stat($file_path);
                $start_offset = $state['size'];

                if ((time() - $this->last_time) > 3) {
                    if (is_callable($callback)) {
                        call_user_func_array($callback, array($start_offset, $this->current_file_name,
                            $this->current_file_size, $this->last_time, $this->last_size));
                    }
                    $this->last_size = $start_offset;
                    $this->last_time = time();
                }
            }
            @fclose($fh);

            if(filesize($file_path) == $file['size']){
                if($wpvivid_plugin->wpvivid_check_zip_valid()) {
                    $res = TRUE;
                }
                else{
                    $res = FALSE;
                }
            }
            else{
                $res = FALSE;
            }

            if ($res !== TRUE) {
                @wp_delete_file($file_path);
                return array('result' => WPVIVID_FAILED, 'error' => 'Downloading ' . $file['file_name'] . ' failed. ' . $file['file_name'] . ' might be deleted or network doesn\'t work properly. Please verify the file and confirm the network connection and try again later.');
            }
            return array('result' => WPVIVID_SUCCESS);
        }
        catch (Exception $error){
            $message = 'An exception has occurred. class: '.get_class($error).';msg: '.$error->getMessage().';code: '.$error->getCode().';line: '.$error->getLine().';in_file: '.$error->getFile().';';
            error_log($message);
            return array('result'=>WPVIVID_FAILED, 'error'=>$message);
        }
    }

    public function cleanup($files)
    {
        $options = $this -> options;
        $dropbox = new Dropbox_Base($options);
        $ret=$dropbox->check_token();
        if($ret['result']=='failed')
        {
            return $ret;
        }
        foreach ($files as $file){
            $dropbox -> delete(trailingslashit($options['path']).$file);
        }
        return array('result'=>WPVIVID_SUCCESS);
    }

    public function init_remotes($remote_collection){
        $remote_collection[WPVIVID_REMOTE_DROPBOX] = 'WPvivid_Dropbox';
        return $remote_collection;
    }

    public function handle_auth_actions()
    {
        if(isset($_GET['action']) && isset($_GET['page']))
        {
            if($_GET['page'] === 'WPvivid')
            {
                if($_GET['action'] === 'wpvivid_dropbox_auth')
                {
                    $check=current_user_can('manage_options');
                    if(!$check)
                    {
                        return;
                    }
                    try {
                        $rand_id = substr(md5(time().rand()), 0,13);
                        $auth_id = 'wpvivid-auth-'.$rand_id;
                        $state = admin_url() . 'admin.php?page=WPvivid' . '&action=wpvivid_dropbox_finish_auth&main_tab=storage&sub_tab=dropbox&sub_page=storage_account_dropbox&auth_id='.$auth_id;
                        $remote_options['auth_id']=$auth_id;
                        set_transient('dropbox_auth_id', $remote_options, 900);
                        $url = Dropbox_Base::getUrl($this->redirect_url, $state);
                        header('Location: ' . filter_var($url, FILTER_SANITIZE_URL));
                    }
                    catch (Exception $e){
                        echo '<div class="notice notice-error"><p>'.esc_html($e->getMessage()).'</p></div>';
                    }
                }
                else if($_GET['action'] === 'wpvivid_dropbox_finish_auth')
                {
                    $tmp_options = get_transient('dropbox_auth_id');
                    if($tmp_options === false)
                    {
                        return;
                    }
                    else if($tmp_options['auth_id'] !== $_GET['auth_id'])
                    {
                        delete_transient('dropbox_auth_id');
                        return;
                    }
                    try {
                        $remoteslist = WPvivid_Setting::get_all_remote_options();
                        foreach ($remoteslist as $key => $value)
                        {
                            if (isset($value['auth_id']) && isset($_GET['auth_id']) && $value['auth_id'] == sanitize_text_field($_GET['auth_id']))
                            {
                                echo '<div class="notice notice-success is-dismissible"><p>';
                                esc_html_e('You have authenticated the Dropbox account as your remote storage.', 'wpvivid-backuprestore');
                                echo '</p></div>';
                                return;
                            }
                        }
                        if(empty($_POST['code']))
                        {
                            if(empty($tmp_options['access_token']))
                            {
                                header('Location: ' . admin_url() . 'admin.php?page=' . WPVIVID_PLUGIN_SLUG . '&action=wpvivid_dropbox_drive&result=error&resp_msg=' . 'Get Dropbox token failed.');
                                return;
                            }
                        }
                        else
                        {
                            $tmp_options['type'] = WPVIVID_REMOTE_DROPBOX;
                            $tmp_options['access_token']= base64_encode(sanitize_text_field($_POST['code']));
                            $tmp_options['expires_in'] = sanitize_text_field($_POST['expires_in']);
                            $tmp_options['refresh_token'] = base64_encode(sanitize_text_field($_POST['refresh_token']));
                            $tmp_options['is_encrypt'] = 1;
                            set_transient('dropbox_auth_id', $tmp_options, 900);
                        }
                        $this->add_remote=true;
                    }
                    catch (Exception $e){
                        echo '<div class="notice notice-error"><p>'.esc_html($e->getMessage()).'</p></div>';
                    }
                }
                else if($_GET['action']=='wpvivid_dropbox_drive')
                {
                    try {
                        if (isset($_GET['result'])) {
                            if ($_GET['result'] == 'success') {
                                add_action('show_notice', array($this, 'wpvivid_show_notice_add_dropbox_success'));
                            } else if ($_GET['result'] == 'error') {
                                add_action('show_notice', array($this, 'wpvivid_show_notice_add_dropbox_error'));
                            }
                        }
                    }
                    catch (Exception $e){
                        echo '<div class="notice notice-error"><p>'.esc_html($e->getMessage()).'</p></div>';
                    }
                }
            }
        }
    }
    public function wpvivid_show_notice_add_dropbox_success(){
        echo '<div class="notice notice-success is-dismissible"><p>';
            esc_html_e('You have authenticated the Dropbox account as your remote storage.', 'wpvivid-backuprestore');
            echo '</p></div>';
    }
    public function wpvivid_show_notice_add_dropbox_error(){
        global $wpvivid_plugin;
        $wpvivid_plugin->wpvivid_handle_remote_storage_error($_GET['resp_msg'], 'Add Dropbox Remote');
        echo '<div class="notice notice-error"><p>'.esc_html($_GET['resp_msg']).'</p></div>';
    }

    public function wpvivid_add_storage_tab_dropbox(){
        ?>
        <div class="storage-providers" remote_type="dropbox" onclick="select_remote_storage(event, 'storage_account_dropbox');">
            <img src="<?php echo esc_url(WPVIVID_PLUGIN_URL.'/admin/partials/images/storage-dropbox.png'); ?>" style="vertical-align:middle;"/><?php esc_html_e('Dropbox', 'wpvivid-backuprestore'); ?>
        </div>
        <?php
    }
    public function wpvivid_add_storage_page_dropbox(){
        global $wpvivid_plugin;
        $root_path=apply_filters('wpvivid_get_root_path', WPVIVID_REMOTE_DROPBOX);

        $remote = get_option('wpvivid_upload_setting');
        if($this->add_remote)
        {
            ?>
            <div id="storage_account_dropbox" class="storage-account-page" style="display:none;">
                <div style="background-color:#f1f1f1; padding: 10px;">
                    Please read<a target="_blank" href="https://wpvivid.com/privacy-policy" style="text-decoration: none;">this privacy policy</a> for use of our Dropbox authorization app (none of your backup data is sent to us).
                </div>
                <div style="color:#8bc34a; padding: 10px 10px 10px 0;">
                    <strong><?php esc_html_e('Authentication is done, please continue to enter the storage information, then click \'Add Now\' button to save it.', 'wpvivid-backuprestore'); ?></strong>
                </div>
                <div style="padding: 10px 10px 10px 0;">
                    <strong><?php esc_html_e('Enter Your Dropbox Information', 'wpvivid-backuprestore'); ?></strong>
                </div>
                <table class="wp-list-table widefat plugins" style="width:100%;">
                    <tbody>
                    <tr>
                        <td class="plugin-title column-primary">
                            <div class="wpvivid-storage-form">
                                <input type="text" class="regular-text" autocomplete="off" option="dropbox" name="name" placeholder="<?php esc_attr_e('Enter a unique alias: e.g. Dropbox-001', 'wpvivid-backuprestore'); ?>" onkeyup="value=value.replace(/[^a-zA-Z0-9\-_]/g,'')" />
                            </div>
                        </td>
                        <td class="column-description desc">
                            <div class="wpvivid-storage-form-desc">
                                <i><?php esc_html_e('A name to help you identify the storage if you have multiple remote storage connected.', 'wpvivid-backuprestore'); ?></i>
                            </div>
                        </td>
                    </tr>
                    <tr>
                        <td class="plugin-title column-primary">
                            <div class="wpvivid-storage-form">
                                <input type="text" class="regular-text" autocomplete="off" option="dropbox" name="path" value="<?php echo esc_attr($root_path.WPVIVID_DROPBOX_DEFAULT_FOLDER); ?>" readonly="readonly" />
                            </div>
                        </td>
                        <td class="column-description desc">
                            <div class="wpvivid-storage-form-desc">
                                <i><?php esc_html_e('All backups will be uploaded to this directory.', 'wpvivid-backuprestore'); ?></i>
                            </div>
                        </td>
                    </tr>
                    <tr>
                        <td class="plugin-title column-primary">
                            <div class="wpvivid-storage-form">
                                <input type="text" class="regular-text" autocomplete="off" value="mywebsite01" readonly="readonly" />
                            </div>
                        </td>
                        <td class="column-description desc">
                            <div class="wpvivid-storage-form-desc">
                                <a href="https://docs.wpvivid.com/wpvivid-backup-pro-dropbox-custom-folder-name.html"><?php esc_html_e('Pro feature: Create a directory for storing the backups of the site', 'wpvivid-backuprestore'); ?></a>
                            </div>
                        </td>
                    </tr>
                    <tr>
                        <td class="plugin-title column-primary">
                            <div class="wpvivid-storage-select">
                                <label>
                                    <input type="checkbox" option="dropbox" name="default" checked /><?php esc_html_e('Set as the default remote storage.', 'wpvivid-backuprestore'); ?>
                                </label>
                            </div>
                        </td>
                        <td class="column-description desc">
                            <div class="wpvivid-storage-form-desc">
                                <i><?php esc_html_e('Once checked, all this sites backups sent to a remote storage destination will be uploaded to this storage by default.', 'wpvivid-backuprestore'); ?></i>
                            </div>
                        </td>
                    </tr>
                    <tr>
                        <td class="plugin-title column-primary">
                            <div class="wpvivid-storage-form">
                                <input id="wpvivid_dropbox_auth" class="button-primary" type="submit" value="<?php esc_attr_e('Add Now', 'wpvivid-backuprestore'); ?>">
                            </div>
                        </td>
                        <td class="column-description desc">
                            <div class="wpvivid-storage-form-desc">
                                <i><?php esc_html_e('Click the button to add the storage.', 'wpvivid-backuprestore'); ?></i>
                            </div>
                        </td>
                    </tr>
                    </tbody>
                </table>
            </div>
            <script>
                function wpvivid_check_dropbox_storage_alias(storage_alias)
                {
                    var find = 1;
                    jQuery('#wpvivid_remote_storage_list tr').each(function (i) {
                        jQuery(this).children('td').each(function (j) {
                            if (j == 3) {
                                if (jQuery(this).text() == storage_alias) {
                                    find = -1;
                                    return false;
                                }
                            }
                        });
                    });
                    return find;
                }

                jQuery('#wpvivid_dropbox_auth').click(function()
                {
                    wpvivid_dropbox_auth();
                });

                function wpvivid_dropbox_auth()
                {
                    wpvivid_settings_changed = false;
                    var name='';
                    var path = '';
                    var bdefault = '0';
                    jQuery("input:checkbox[option=dropbox]").each(function(){
                        var key = jQuery(this).prop('name');
                        if(jQuery(this).prop('checked')) {
                            bdefault = '1';
                        }
                        else {
                            bdefault = '0';
                        }
                    });
                    jQuery('input:text[option=dropbox]').each(function()
                    {
                        var type = jQuery(this).prop('name');
                        if(type == 'name'){
                            name = jQuery(this).val();
                        }
                    });
                    if(name == ''){
                        alert(wpvividlion.remotealias);
                    }
                    else if(wpvivid_check_dropbox_storage_alias(name) === -1){
                        alert(wpvividlion.remoteexist);
                    }
                    else{
                        var ajax_data;
                        var remote_from = wpvivid_ajax_data_transfer('dropbox');
                        ajax_data = {
                            'action': 'wpvivid_dropbox_add_remote',
                            'remote': remote_from
                        };
                        jQuery('#wpvivid_dropbox_auth').css({'pointer-events': 'none', 'opacity': '0.4'});
                        jQuery('#wpvivid_remote_notice').html('');
                        wpvivid_post_request(ajax_data, function (data)
                        {
                            try
                            {
                                var jsonarray = jQuery.parseJSON(data);
                                if (jsonarray.result === 'success')
                                {
                                    jQuery('#wpvivid_dropbox_auth').css({'pointer-events': 'auto', 'opacity': '1'});
                                    jQuery('input:text[option=dropbox]').each(function(){
                                        jQuery(this).val('');
                                    });
                                    jQuery('input:password[option=dropbox]').each(function(){
                                        jQuery(this).val('');
                                    });
                                    wpvivid_handle_remote_storage_data(data);
                                    location.href='admin.php?page=WPvivid&action=wpvivid_dropbox_drive&main_tab=storage&sub_tab=dropbox&sub_page=storage_account_dropbox&result=success';
                                }
                                else if (jsonarray.result === 'failed')
                                {
                                    jQuery('#wpvivid_remote_notice').html(jsonarray.notice);
                                    jQuery('input[option=add-remote]').css({'pointer-events': 'auto', 'opacity': '1'});
                                }
                            }
                            catch (err)
                            {
                                alert(err);
                                jQuery('input[option=add-remote]').css({'pointer-events': 'auto', 'opacity': '1'});
                            }

                        }, function (XMLHttpRequest, textStatus, errorThrown)
                        {
                            var error_message = wpvivid_output_ajaxerror('adding the remote storage', textStatus, errorThrown);
                            alert(error_message);
                            jQuery('#wpvivid_dropbox_auth').css({'pointer-events': 'auto', 'opacity': '1'});
                        });
                    }
                }
            </script>
            <?php
        }
        else
        {
            ?>
            <div id="storage_account_dropbox" class="storage-account-page" style="display:none;">
                <div style="background-color:#f1f1f1; padding: 10px;">
                    Please read <a target="_blank" href="https://wpvivid.com/privacy-policy" style="text-decoration: none;">this privacy policy</a> for use of our Dropbox authorization app (none of your backup data is sent to us).
                </div>
                <div style="padding: 10px 10px 10px 0;">
                    <strong><?php esc_html_e('To add Dropbox, please get Dropbox authentication first. Once authenticated, you will be redirected to this page, then you can add storage information and save it.', 'wpvivid-backuprestore'); ?></strong>
                </div>
                <table class="wp-list-table widefat plugins" style="width:100%;">
                    <tbody>
                    <tr>
                        <td class="plugin-title column-primary">
                            <div class="wpvivid-storage-form">
                                <input onclick="wpvivid_dropbox_auth();" class="button-primary" type="submit" value="<?php esc_attr_e('Authenticate with Dropbox', 'wpvivid-backuprestore'); ?>">
                            </div>
                        </td>
                        <td class="column-description desc">
                            <div class="wpvivid-storage-form-desc">
                                <i><?php esc_html_e('Click to get Dropbox authentication.', 'wpvivid-backuprestore'); ?></i>
                            </div>
                        </td>
                    </tr>
                    </tbody>
                </table>
                <div style="padding: 10px 0 0 0;">
                    <span>Tip: Get a 404 or 403 error after authorization? Please read this <a href="https://docs.wpvivid.com/http-403-error-authorizing-cloud-storage.html">doc</a>.</span>
                </div>
            </div>
            <script>
                function wpvivid_dropbox_auth()
                {
                    location.href ='<?php echo esc_url(admin_url()).'admin.php?page=WPvivid'.'&action=wpvivid_dropbox_auth'?>';
                }
            </script>
            <?php
        }
    }
    public function wpvivid_edit_storage_page_dropbox()
    {
        do_action('wpvivid_remote_storage_js');
        ?>
        <div id="remote_storage_edit_dropbox" class="postbox storage-account-block remote-storage-edit" style="display:none;">
            <div style="padding: 0 10px 10px 0;">
                <strong><?php esc_html_e('To add Dropbox, please get Dropbox authentication first. Once authenticated, you will be redirected to this page, then you can add storage information and save it', 'wpvivid-backuprestore'); ?></strong>
            </div>
            <table class="wp-list-table widefat plugins" style="width:100%;">
                <tbody>
                <tr>
                    <td class="plugin-title column-primary">
                        <div class="wpvivid-storage-form">
                            <input type="text" class="regular-text" autocomplete="off" option="edit-dropbox" name="name" placeholder="<?php esc_attr_e('Enter a unique alias: e.g. Dropbox-001', 'wpvivid-backuprestore'); ?>" onkeyup="value=value.replace(/[^a-zA-Z0-9\-_]/g,'')" />
                        </div>
                    </td>
                    <td class="column-description desc">
                        <div class="wpvivid-storage-form-desc">
                            <i><?php esc_html_e('A name to help you identify the storage if you have multiple remote storage connected.', 'wpvivid-backuprestore'); ?></i>
                        </div>
                    </td>
                </tr>
                <tr>
                    <td class="plugin-title column-primary">
                        <div class="wpvivid-storage-form">
                            <input class="button-primary" type="submit" option="edit-remote" value="<?php esc_attr_e('Save Changes', 'wpvivid-backuprestore'); ?>">
                        </div>
                    </td>
                    <td class="column-description desc">
                        <div class="wpvivid-storage-form-desc">
                            <i><?php esc_html_e('Click the button to save the changes.', 'wpvivid-backuprestore'); ?></i>
                        </div>
                    </td>
                </tr>
                </tbody>
            </table>
        </div>
        <script>
            function wpvivid_dropbox_update_auth()
            {
                var name='';
                jQuery('input:text[option=edit-dropbox]').each(function()
                {
                    var key = jQuery(this).prop('name');
                    if(key==='name')
                    {
                        name = jQuery(this).val();
                    }
                });
                if(name == ''){
                    alert(wpvividlion.remotealias);
                }
                else if(wpvivid_check_onedrive_storage_alias(name) === -1){
                    alert(wpvividlion.remoteexist);
                }
                else {
                    location.href = '<?php echo esc_url(admin_url()) . 'admin.php?page=WPvivid' . '&action=wpvivid_dropbox_update_auth&name='?>' + name + '&id=' + wpvivid_editing_storage_id;
                }
            }
        </script>
        <?php
    }
    public function wpvivid_remote_pic_dropbox($remote)
    {
        $remote['dropbox']['default_pic'] = '/admin/partials/images/storage-dropbox(gray).png';
        $remote['dropbox']['selected_pic'] = '/admin/partials/images/storage-dropbox.png';
        $remote['dropbox']['title'] = 'Dropbox';
        return $remote;
    }

    public function revoke($id){
        $upload_options = WPvivid_Setting::get_option('wpvivid_upload_setting');
        if(array_key_exists($id,$upload_options) && $upload_options[$id] == WPVIVID_REMOTE_DROPBOX){
            $dropbox = new Dropbox_Base($upload_options);
            $dropbox -> revoke();
        }
    }

    public function wpvivid_get_out_of_date_dropbox($out_of_date_remote, $remote)
    {
        if($remote['type'] == WPVIVID_REMOTE_DROPBOX){
            $root_path=apply_filters('wpvivid_get_root_path', $remote['type']);
            $out_of_date_remote = $root_path.$remote['path'];
        }
        return $out_of_date_remote;
    }

    public function wpvivid_storage_provider_dropbox($storage_type)
    {
        if($storage_type == WPVIVID_REMOTE_DROPBOX){
            $storage_type = 'Dropbox';
        }
        return $storage_type;
    }

    public function wpvivid_get_root_path_dropbox($storage_type){
        if($storage_type == WPVIVID_REMOTE_DROPBOX){
            $storage_type = 'apps/Wpvivid backup restore';
        }
        return $storage_type;
    }

    public function finish_add_remote()
    {
        global $wpvivid_plugin;
        check_ajax_referer( 'wpvivid_ajax', 'nonce' );
        $check=current_user_can('manage_options');
        $check=apply_filters('wpvivid_ajax_check_security',$check);
        if(!$check)
        {
            die();
        }
        try {
            if (empty($_POST) || !isset($_POST['remote']) || !is_string($_POST['remote'])) {
                die();
            }

            $tmp_remote_options = get_transient('dropbox_auth_id');
            if($tmp_remote_options === false)
            {
                die();
            }
            delete_transient('dropbox_auth_id');
            if(empty($tmp_remote_options)||$tmp_remote_options['type']!==WPVIVID_REMOTE_DROPBOX)
            {
                die();
            }

            $json = sanitize_text_field($_POST['remote']);
            $json = stripslashes($json);
            $remote_options = json_decode($json, true);
            if (is_null($remote_options)) {
                die();
            }

            $remote_options['created']=time();
            $remote_options['path'] = WPVIVID_DROPBOX_DEFAULT_FOLDER;
            $remote_options=array_merge($remote_options,$tmp_remote_options);
            if(!class_exists('WPvivid_Remote_collection'))
            {
                include_once WPVIVID_PLUGIN_DIR . '/includes/class-wpvivid-remote-collection.php';
                $wpvivid_plugin->remote_collection=new WPvivid_Remote_collection();
            }
            $ret = $wpvivid_plugin->remote_collection->add_remote($remote_options);

            if ($ret['result'] == 'success') {
                $html = '';
                $html = apply_filters('wpvivid_add_remote_storage_list', $html);
                $ret['html'] = $html;
                $pic = '';
                $pic = apply_filters('wpvivid_schedule_add_remote_pic', $pic);
                $ret['pic'] = $pic;
                $dir = '';
                $dir = apply_filters('wpvivid_get_remote_directory', $dir);
                $ret['dir'] = $dir;
                $schedule_local_remote = '';
                $schedule_local_remote = apply_filters('wpvivid_schedule_local_remote', $schedule_local_remote);
                $ret['local_remote'] = $schedule_local_remote;
                $remote_storage = '';
                $remote_storage = apply_filters('wpvivid_remote_storage', $remote_storage);
                $ret['remote_storage'] = $remote_storage;
                $remote_select_part = '';
                $remote_select_part = apply_filters('wpvivid_remote_storage_select_part', $remote_select_part);
                $ret['remote_select_part'] = $remote_select_part;
                $default = array();
                $remote_array = apply_filters('wpvivid_archieve_remote_array', $default);
                $ret['remote_array'] = $remote_array;
                $success_msg = __('You have successfully added a remote storage.', 'wpvivid-backuprestore');
                $ret['notice'] = apply_filters('wpvivid_add_remote_notice', true, $success_msg);
            }
            else{
                $ret['notice'] = apply_filters('wpvivid_add_remote_notice', false, $ret['error']);
            }

        }
        catch (Exception $error) {
            $message = 'An exception has occurred. class: '.get_class($error).';msg: '.$error->getMessage().';code: '.$error->getCode().';line: '.$error->getLine().';in_file: '.$error->getFile().';';
            error_log($message);
            echo wp_json_encode(array('result'=>'failed','error'=>$message));
            die();
        }
        echo wp_json_encode($ret);
        die();
    }
}includes/customclass/class-wpvivid-remote-default.php000064400000002015151327705670017144 0ustar00<?php
if (!defined('WPVIVID_PLUGIN_DIR')){
    die;
}

if(!defined('WPVIVID_UPLOAD_SUCCESS'))
{
    define('WPVIVID_UPLOAD_SUCCESS',1);
}

if(!defined('WPVIVID_UPLOAD_FAILED'))
{
    define('WPVIVID_UPLOAD_FAILED',2);
}

if(!defined('WPVIVID_UPLOAD_UNDO'))
{
    define('WPVIVID_UPLOAD_UNDO',0);
}

require_once WPVIVID_PLUGIN_DIR .'/includes/customclass/class-wpvivid-remote.php';
class WPvivid_Remote_Defult extends WPvivid_Remote{
    public function test_connect()
    {
        return array('result' => WPVIVID_FAILED,'error'=> 'Type incorrect.');
    }

    public function upload($task_id, $files, $callback = '')
    {
        return array('result' => WPVIVID_FAILED,'error'=> 'Type incorrect.');
    }

    public function download( $file, $local_path, $callback = '')
    {
        return array('result' => WPVIVID_FAILED,'error'=> 'Type incorrect.');
    }

    public function cleanup($files)
    {
        return array('result' => WPVIVID_FAILED,'error'=> 'Type incorrect.');
    }
}includes/customclass/class-wpvivid-amazons3-plus.php000064400000116423151327705670016754 0ustar00<?php
if (!defined('WPVIVID_PLUGIN_DIR')){
    die;
}
if(!defined('WPVIVID_REMOTE_AMAZONS3'))
    define('WPVIVID_REMOTE_AMAZONS3','amazons3');
if(!defined('WPVIVID_AMAZONS3_DEFAULT_FOLDER'))
    define('WPVIVID_AMAZONS3_DEFAULT_FOLDER','/wpvivid_backup');

require_once WPVIVID_PLUGIN_DIR . '/includes/customclass/class-wpvivid-remote.php';
require_once 'class-wpvivid-base-s3.php';
class WPvivid_AMAZONS3Class extends WPvivid_Remote{

    public $options;
    public $bucket='';

    private $upload_chunk_size = 5242880;
    private $download_chunk_size = 5242880;

    public $current_file_size;
    public $current_file_name;

    public function __construct($options=array())
    {
        if(empty($options))
        {
            add_action('wpvivid_add_storage_tab',array($this,'wpvivid_add_storage_tab_amazons3'), 13);
            add_action('wpvivid_add_storage_page',array($this,'wpvivid_add_storage_page_amazons3'), 13);
            add_action('wpvivid_edit_remote_page',array($this,'wpvivid_edit_storage_page_amazons3'), 13);
            add_filter('wpvivid_remote_pic',array($this,'wpvivid_remote_pic_amazons3'),11);
            add_filter('wpvivid_get_out_of_date_remote',array($this,'wpvivid_get_out_of_date_amazons3'),10,2);
            add_filter('wpvivid_storage_provider_tran',array($this,'wpvivid_storage_provider_amazons3'),10);
        }
        else
        {
            $this->options=$options;
        }
    }

    public function wpvivid_add_storage_tab_amazons3()
    {
        ?>
        <div class="storage-providers" remote_type="amazons3" onclick="select_remote_storage(event, 'storage_account_amazons3');">
            <img src="<?php echo esc_url(WPVIVID_PLUGIN_URL.'/admin/partials/images/storage-amazon-s3.png'); ?>" style="vertical-align:middle;"/><?php esc_html_e('Amazon S3', 'wpvivid-backuprestore'); ?>
        </div>
        <?php
    }

    public function wpvivid_add_storage_page_amazons3()
    {
        if(!function_exists('simplexml_load_string')){
            $need_extension = true;
            $add_btn_style = 'pointer-events: none; opacity: 0.4;';
        }
        else{
            $need_extension = false;
            $add_btn_style = 'pointer-events: auto; opacity: 1;';
        }
        ?>
        <div id="storage_account_amazons3"  class="storage-account-page" style="display:none;">
            <div style="padding: 0 10px 10px 0;">
                <strong><?php esc_html_e('Enter Your Amazon S3 Account', 'wpvivid-backuprestore'); ?></strong>
            </div>
            <table class="wp-list-table widefat plugins" style="width:100%;">
                <tbody>
                <form>
                <tr>
                    <td class="plugin-title column-primary">
                        <div class="wpvivid-storage-form">
                            <input type="text" class="regular-text" autocomplete="off" option="amazons3" name="name" placeholder="<?php esc_attr_e('Enter a unique alias: e.g. Amazon S3-001', 'wpvivid-backuprestore'); ?>" onkeyup="value=value.replace(/[^a-zA-Z0-9\-_]/g,'')" />
                        </div>
                    </td>
                    <td class="column-description desc">
                        <div class="wpvivid-storage-form-desc">
                            <i><?php esc_html_e('A name to help you identify the storage if you have multiple remote storage connected.', 'wpvivid-backuprestore'); ?></i>
                        </div>
                    </td>
                </tr>
                <tr>
                    <td class="plugin-title column-primary">
                        <div class="wpvivid-storage-form">
                            <input type="text" class="regular-text" autocomplete="off" option="amazons3" name="access" placeholder="<?php esc_attr_e('Amazon S3 access key', 'wpvivid-backuprestore'); ?>" />
                        </div>
                    </td>
                    <td class="column-description desc">
                        <div class="wpvivid-storage-form-desc">
                            <i><?php esc_html_e('Enter your Amazon S3 access key.', 'wpvivid-backuprestore'); ?></i><a href="https://wpvivid.com/get-amazon-access-secret-key.html" target="_blank"> <?php esc_html_e('How to get an AmazonS3 access key.', 'wpvivid-backuprestore'); ?></a>
                        </div>
                    </td>
                </tr>
                <tr>
                    <td class="plugin-title column-primary">
                        <div class="wpvivid-storage-form">
                            <input type="password" class="regular-text" autocomplete="new-password" option="amazons3" name="secret" placeholder="<?php esc_attr_e('Amazon S3 secret key', 'wpvivid-backuprestore'); ?>" />
                        </div>
                    </td>
                    <td class="column-description desc">
                        <div class="wpvivid-storage-form-desc">
                            <i><?php esc_html_e('Enter your Amazon S3 secret key.', 'wpvivid-backuprestore'); ?></i><a href="https://wpvivid.com/get-amazon-access-secret-key.html" target="_blank"> <?php esc_html_e('How to get an Amazon S3 secret key.', 'wpvivid-backuprestore'); ?></a>
                        </div>
                    </td>
                </tr>
                <tr>
                    <td class="plugin-title column-primary">
                        <div class="wpvivid-storage-form">
                            <input type="text" class="regular-text" autocomplete="off" option="amazons3" name="bucket" placeholder="<?php esc_attr_e('Amazon S3 Bucket Name(e.g. test)', 'wpvivid-backuprestore'); ?>" />
                        </div>
                    </td>
                    <td class="column-description desc">
                        <div class="wpvivid-storage-form-desc">
                            <i><span><?php esc_html_e('Enter an existed Bucket to create a custom backup storage directory.', 'wpvivid-backuprestore'); ?></span></i>
                        </div>
                    </td>
                </tr>
                <tr>
                    <td class="plugin-title column-primary">
                        <div class="wpvivid-storage-form">
                            <input type="text" class="regular-text" autocomplete="off" option="amazons3" name="path" placeholder="<?php esc_attr_e('Custom Path', 'wpvivid-backuprestore'); ?>" />
                        </div>
                    </td>
                    <td class="column-description desc">
                        <div class="wpvivid-storage-form-desc">
                            <i><span><?php esc_html_e('Customize the directory where you want to store backups within the Bucket.', 'wpvivid-backuprestore'); ?></span></i>
                        </div>
                    </td>
                </tr>
                <tr>
                    <td class="plugin-title column-primary">
                        <div class="wpvivid-storage-select">
                            <label>
                                <input type="checkbox" option="amazons3" name="default" checked /><?php esc_html_e('Set as the default remote storage.', 'wpvivid-backuprestore'); ?>
                            </label>
                        </div>
                    </td>
                    <td class="column-description desc">
                        <div class="wpvivid-storage-form-desc">
                            <i><?php esc_html_e('Once checked, all this sites backups sent to a remote storage destination will be uploaded to this storage by default.', 'wpvivid-backuprestore'); ?></i>
                        </div>
                    </td>
                </tr>
                <tr>
                    <td class="plugin-title column-primary">
                        <div class="wpvivid-storage-select">
                            <label>
                                <input type="checkbox" option="amazons3" name="classMode" checked /><?php esc_html_e('Storage class: Standard (infrequent access).', 'wpvivid-backuprestore'); ?>
                            </label>
                        </div>
                    </td>
                    <td class="column-description desc">
                        <div class="wpvivid-storage-form-desc">
                            <i><?php esc_html_e('Check the option to use Amazon S3 Standard-Infrequent Access (S3 Standard-IA) storage class for data transfer.', 'wpvivid-backuprestore'); ?></i>
                        </div>
                    </td>
                </tr>
                <tr>
                    <td class="plugin-title column-primary">
                        <div class="wpvivid-storage-select">
                            <label>
                                <input type="checkbox" option="amazons3" name="sse" checked /><?php esc_html_e('Server-side encryption.', 'wpvivid-backuprestore'); ?>
                            </label>
                        </div>
                    </td>
                    <td class="column-description desc">
                        <div class="wpvivid-storage-form-desc">
                            <i><?php esc_html_e('Check the option to use Amazon S3 server-side encryption to protect data.', 'wpvivid-backuprestore'); ?></i>
                        </div>
                    </td>
                </tr>
                </form>
                <tr>
                    <td class="plugin-title column-primary">
                        <div class="wpvivid-storage-form">
                            <input class="button-primary" option="add-remote" type="submit" value="<?php esc_attr_e('Test and Add', 'wpvivid-backuprestore'); ?>" />
                        </div>
                    </td>
                    <td class="column-description desc">
                        <div class="wpvivid-storage-form-desc">
                            <i><?php esc_html_e('Click the button to connect to Amazon S3 storage and add it to the storage list below.', 'wpvivid-backuprestore'); ?></i>
                        </div>
                    </td>
                </tr>
                </tbody>
            </table>
            <?php
            if($need_extension){
                ?>
                <p style="padding-left: 10px;"><?php esc_html_e('The simplexml extension is not detected. Please install the extension first.', 'wpvivid-backuprestore'); ?></p>
                <?php
            }
            ?>
        </div>
        <?php
    }

    public function wpvivid_edit_storage_page_amazons3()
    {
        ?>
        <div id="remote_storage_edit_amazons3" class="postbox storage-account-block remote-storage-edit" style="display:none;">
            <div style="padding: 0 10px 10px 0;">
                <strong><?php esc_html_e('Enter Your Amazon S3 Account', 'wpvivid-backuprestore'); ?></strong>
            </div>
            <table class="wp-list-table widefat plugins" style="width:100%;">
                <tbody>
                <form>
                <tr>
                    <td class="plugin-title column-primary">
                        <div class="wpvivid-storage-form">
                            <input type="text" class="regular-text" autocomplete="off" option="edit-amazons3" name="name" placeholder="<?php esc_attr_e('Enter a unique alias: e.g. Amazon S3-001', 'wpvivid-backuprestore'); ?>" onkeyup="value=value.replace(/[^a-zA-Z0-9\-_]/g,'')" />
                        </div>
                    </td>
                    <td class="column-description desc">
                        <div class="wpvivid-storage-form-desc">
                            <i><?php esc_html_e('A name to help you identify the storage if you have multiple remote storage connected.', 'wpvivid-backuprestore'); ?></i>
                        </div>
                    </td>
                </tr>
                <tr>
                    <td class="plugin-title column-primary">
                        <div class="wpvivid-storage-form">
                            <input type="text" class="regular-text" autocomplete="off" option="edit-amazons3" name="access" placeholder="<?php esc_attr_e('Amazon S3 access key', 'wpvivid-backuprestore'); ?>" />
                        </div>
                    </td>
                    <td class="column-description desc">
                        <div class="wpvivid-storage-form-desc">
                            <i><?php esc_html_e('Enter your Amazon S3 access key.', 'wpvivid-backuprestore'); ?></i><a href="https://wpvivid.com/get-amazon-access-secret-key.html" target="_blank"> <?php esc_html_e('How to get an AmazonS3 access key.', 'wpvivid-backuprestore'); ?></a>
                        </div>
                    </td>
                </tr>
                <tr>
                    <td class="plugin-title column-primary">
                        <div class="wpvivid-storage-form">
                            <input type="password" class="regular-text" autocomplete="new-password" option="edit-amazons3" name="secret" placeholder="<?php esc_attr_e('Amazon S3 secret key', 'wpvivid-backuprestore'); ?>" />
                        </div>
                    </td>
                    <td class="column-description desc">
                        <div class="wpvivid-storage-form-desc">
                            <i><?php esc_html_e('Enter your Amazon S3 secret key.', 'wpvivid-backuprestore'); ?></i><a href="https://wpvivid.com/get-amazon-access-secret-key.html" target="_blank"> <?php esc_html_e('How to get an Amazon S3 secret key.', 'wpvivid-backuprestore'); ?></a>
                        </div>
                    </td>
                </tr>
                <tr>
                    <td class="plugin-title column-primary">
                        <div class="wpvivid-storage-form">
                            <input type="text" class="regular-text" autocomplete="off" option="edit-amazons3" name="bucket" placeholder="<?php esc_attr_e('Amazon S3 Bucket Name(e.g. test)', 'wpvivid-backuprestore'); ?>" />
                        </div>
                    </td>
                    <td class="column-description desc">
                        <div class="wpvivid-storage-form-desc">
                            <i><span><?php esc_html_e('Enter an existed Bucket to create a custom backup storage directory.', 'wpvivid-backuprestore'); ?></span></i>
                        </div>
                    </td>
                </tr>
                <tr>
                    <td class="plugin-title column-primary">
                        <div class="wpvivid-storage-form">
                            <input type="text" class="regular-text" autocomplete="off" option="edit-amazons3" name="path" placeholder="<?php esc_attr_e('Custom Path', 'wpvivid-backuprestore'); ?>" />
                        </div>
                    </td>
                    <td class="column-description desc">
                        <div class="wpvivid-storage-form-desc">
                            <i><span><?php esc_html_e('Customize the directory where you want to store backups within the Bucket.', 'wpvivid-backuprestore'); ?></span></i>
                        </div>
                    </td>
                </tr>
                <tr>
                    <td class="plugin-title column-primary">
                        <div class="wpvivid-storage-select">
                            <label>
                                <input type="checkbox" option="edit-amazons3" name="classMode" /><?php esc_html_e('Storage class: Standard (infrequent access).', 'wpvivid-backuprestore'); ?>
                            </label>
                        </div>
                    </td>
                    <td class="column-description desc">
                        <div class="wpvivid-storage-form-desc">
                            <i><?php esc_html_e('Check the option to use Amazon S3 Standard-Infrequent Access (S3 Standard-IA) storage class for data transfer.', 'wpvivid-backuprestore'); ?></i>
                        </div>
                    </td>
                </tr>
                <tr>
                    <td class="plugin-title column-primary">
                        <div class="wpvivid-storage-select">
                            <label>
                                <input type="checkbox" option="edit-amazons3" name="sse" /><?php esc_html_e('Server-side encryption.', 'wpvivid-backuprestore'); ?>
                            </label>
                        </div>
                    </td>
                    <td class="column-description desc">
                        <div class="wpvivid-storage-form-desc">
                            <i><?php esc_html_e('Check the option to use Amazon S3 server-side encryption to protect data.', 'wpvivid-backuprestore'); ?></i>
                        </div>
                    </td>
                </tr>
                </form>
                <tr>
                    <td class="plugin-title column-primary">
                        <div class="wpvivid-storage-form">
                            <input class="button-primary" option="edit-remote" type="submit" value="<?php esc_attr_e('Save Changes', 'wpvivid-backuprestore'); ?>" />
                        </div>
                    </td>
                    <td class="column-description desc">
                        <div class="wpvivid-storage-form-desc">
                            <i><?php esc_html_e('Click the button to save the changes.', 'wpvivid-backuprestore'); ?></i>
                        </div>
                    </td>
                </tr>
                </tbody>
            </table>
        </div>
        <?php
    }

    public function wpvivid_remote_pic_amazons3($remote){
        $remote['amazons3']['default_pic'] = '/admin/partials/images/storage-amazon-s3(gray).png';
        $remote['amazons3']['selected_pic'] = '/admin/partials/images/storage-amazon-s3.png';
        $remote['amazons3']['title'] = 'Amazon S3';
        return $remote;
    }

    public function test_connect()
    {
        $amazons3 = $this -> getS3();
        if(is_array($amazons3) && $amazons3['result'] === WPVIVID_FAILED)
            return $amazons3;
        $temp_file = md5(wp_rand());
        try
        {
            if(isset($this->options['s3Path']))
            {
                $url=$this->options['s3Path'].$temp_file;
            }
            else
            {
                $url=$this->options['path'].'/'.$temp_file;
            }

            if(!$amazons3 -> putObjectString($temp_file,$this -> bucket,$url))
            {
                return array('result'=>WPVIVID_FAILED,'error'=>'We successfully accessed the bucket, but create test file failed.');
            }
            if(!$amazons3 -> deleteObject($this -> bucket,$url))
            {
                return array('result'=>WPVIVID_FAILED,'error'=>'We successfully accessed the bucket, and create test file succeed, but delete test file failed.');
            }
        }catch(Exception $e){
            return array('result'=>WPVIVID_FAILED,'error'=>$e -> getMessage());
        }
        return array('result'=>WPVIVID_SUCCESS);
    }

    public function sanitize_options($skip_name='')
    {
        $ret['result']=WPVIVID_FAILED;
        if(!isset($this->options['name']))
        {
            $ret['error']="Warning: An alias for remote storage is required.";
            return $ret;
        }

        $this->options['name']=sanitize_text_field($this->options['name']);

        if(empty($this->options['name']))
        {
            $ret['error']="Warning: An alias for remote storage is required.";
            return $ret;
        }

        $remoteslist=WPvivid_Setting::get_all_remote_options();
        foreach ($remoteslist as $key=>$value)
        {
            if(isset($value['name'])&&$value['name'] == $this->options['name']&&$skip_name!=$value['name'])
            {
                $ret['error']="Warning: The alias already exists in storage list.";
                return $ret;
            }
        }

        if(!isset($this->options['access']))
        {
            $ret['error']="Warning: The access key for Amazon S3 is required.";
            return $ret;
        }

        $this->options['access']=sanitize_text_field($this->options['access']);

        if(empty($this->options['access']))
        {
            $ret['error']="Warning: The access key for Amazon S3 is required.";
            return $ret;
        }

        if(!isset($this->options['secret']))
        {
            $ret['error']="Warning: The storage secret key is required.";
            return $ret;
        }

        $this->options['secret']=sanitize_text_field($this->options['secret']);

        if(empty($this->options['secret']))
        {
            $ret['error']="Warning: The storage secret key is required.";
            return $ret;
        }
        $this->options['secret'] = base64_encode($this->options['secret']);
        $this->options['is_encrypt'] = 1;

        if(!isset($this->options['bucket']))
        {
            $ret['error']="Warning: A Bucket name is required.";
            return $ret;
        }

        $this->options['bucket']=sanitize_text_field($this->options['bucket']);

        if(empty($this->options['bucket']))
        {
            $ret['error']="Warning: A Bucket name is required.";
            return $ret;
        }

        if(!isset($this->options['path']))
        {
            $ret['error']="Warning: A directory name is required.";
            return $ret;
        }

        $this->options['path']=sanitize_text_field($this->options['path']);

        if(empty($this->options['path'])){
            $ret['error']="Warning: A directory name is required.";
            return $ret;
        }

        $ret['result']=WPVIVID_SUCCESS;
        $ret['options']=$this->options;
        return $ret;
    }

    public function upload($task_id,$files,$callback='')
    {
        global $wpvivid_plugin;
        $amazons3 = $this -> getS3();

        if(is_array($amazons3) && $amazons3['result'] == WPVIVID_FAILED)
            return $amazons3;

        $upload_job=WPvivid_taskmanager::get_backup_sub_task_progress($task_id,'upload',WPVIVID_REMOTE_AMAZONS3);
        if(empty($upload_job))
        {
            $job_data=array();
            foreach ($files as $file)
            {
                $file_data['size']=filesize($file);
                $file_data['uploaded']=0;
                $job_data[basename($file)]=$file_data;
            }
            WPvivid_taskmanager::update_backup_sub_task_progress($task_id,'upload',WPVIVID_REMOTE_AMAZONS3,WPVIVID_UPLOAD_UNDO,'Start uploading',$job_data);
            $upload_job=WPvivid_taskmanager::get_backup_sub_task_progress($task_id,'upload',WPVIVID_REMOTE_AMAZONS3);
        }

        foreach ($files as $file){
            if(is_array($upload_job['job_data']) &&array_key_exists(basename($file),$upload_job['job_data']))
            {
                if($upload_job['job_data'][basename($file)]['uploaded']==1)
                    continue;
            }

            $this -> last_time = time();
            $this -> last_size = 0;
            $wpvivid_plugin->wpvivid_log->WriteLog('Start uploading '.basename($file),'notice');
            $wpvivid_plugin->set_time_limit($task_id);
            if(!file_exists($file))
                return array('result' =>WPVIVID_FAILED,'error' =>$file.' not found. The file might has been moved, renamed or deleted. Please reload the list and verify the file exists.');
            $result = $this -> _put($task_id,$amazons3,$file,$callback);
            if($result['result'] !==WPVIVID_SUCCESS){
                $wpvivid_plugin->wpvivid_log->WriteLog('Uploading '.basename($file).' failed.','notice');
                return $result;
            }
            else
            {
                WPvivid_taskmanager::wpvivid_reset_backup_retry_times($task_id);
            }
            $wpvivid_plugin->wpvivid_log->WriteLog('Finished uploading '.basename($file),'notice');
            $upload_job['job_data'][basename($file)]['uploaded']=1;
            WPvivid_taskmanager::update_backup_sub_task_progress($task_id,'upload',WPVIVID_REMOTE_AMAZONS3,WPVIVID_UPLOAD_SUCCESS,'Uploading '.basename($file).' completed.',$upload_job['job_data']);
        }
        return array('result' =>WPVIVID_SUCCESS);
    }
    private function _put($task_id,$amazons3,$file,$callback)
    {
        global $wpvivid_plugin;
        $upload_job=WPvivid_taskmanager::get_backup_sub_task_progress($task_id,'upload',WPVIVID_REMOTE_AMAZONS3);
        $this -> current_file_size = filesize($file);
        $this -> current_file_name = basename($file);

        if(isset($this->options['s3Path']))
        {
            $url=$this->options['s3Path'].$this -> current_file_name;
        }
        else
        {
            $url=$this->options['path'].'/'.$this -> current_file_name;
        }
        $wpvivid_plugin->wpvivid_log->WriteLog($url,'notice');

        $chunk_num = floor($this -> current_file_size / $this -> upload_chunk_size);
        if($this -> current_file_size % $this -> upload_chunk_size > 0) $chunk_num ++;

        for($i =0;$i <WPVIVID_REMOTE_CONNECT_RETRY_TIMES;$i ++)
        {
            try
            {
                WPvivid_taskmanager::update_backup_sub_task_progress($task_id,'upload',WPVIVID_REMOTE_AMAZONS3,WPVIVID_UPLOAD_UNDO,'Start uploading '.basename($file).'.',$upload_job['job_data']);

                if(true)
                {
                    $wpvivid_plugin->wpvivid_log->WriteLog('Creating Multipart Upload.','notice');
                    if(!empty($upload_job['job_data'][basename($file)]['upload_id']))
                    {
                        $build_id = $upload_job['job_data'][basename($file)]['upload_id'];
                    }else{
                        $build_id = $amazons3 -> initiateMultipartUpload($this -> bucket,$url);
                        $upload_job['job_data'][basename($file)]['upload_id'] = $build_id;
                        WPvivid_taskmanager::update_backup_sub_task_progress($task_id,'upload',WPVIVID_REMOTE_AMAZONS3,WPVIVID_UPLOAD_UNDO,'InitiateMultipartUpload, created build id of '.basename($file).'.',$upload_job['job_data']);
                    }
                    if(!empty($upload_job['job_data'][basename($file)]['upload_chunks']))
                    {
                        $chunks = $upload_job['job_data'][basename($file)]['upload_chunks'];
                    }else{
                        $chunks = array();
                        $upload_job['job_data'][basename($file)]['upload_chunks'] = $chunks;
                        WPvivid_taskmanager::update_backup_sub_task_progress($task_id,'upload',WPVIVID_REMOTE_AMAZONS3,WPVIVID_UPLOAD_UNDO,'Start multipartupload of '.basename($file).'.',$upload_job['job_data']);
                    }

                    for($i =sizeof($chunks);$i <$chunk_num;$i ++)
                    {
                        $chunk_id = $amazons3 -> uploadPart($this -> bucket,$url,$build_id,$file,$i+1,$this ->upload_chunk_size);
                        if(!$chunk_id){
                            $chunks = array();
                            $upload_job['job_data'][basename($file)]['upload_chunks'] = $chunks;
                            WPvivid_taskmanager::update_backup_sub_task_progress($task_id,'upload',WPVIVID_REMOTE_AMAZONS3,WPVIVID_UPLOAD_UNDO,'Start multipartupload of '.basename($file).'.',$upload_job['job_data']);
                            return array('result' => WPVIVID_FAILED,'error' => 'upload '.$file.' failed.');
                        }
                        $chunks[] = $chunk_id;
                        $upload_job['job_data'][basename($file)]['upload_chunks'] = $chunks;
                        WPvivid_taskmanager::update_backup_sub_task_progress($task_id,'upload',WPVIVID_REMOTE_AMAZONS3,WPVIVID_UPLOAD_UNDO,'Uploading '.basename($file).'.',$upload_job['job_data']);

                        $offset = (($i + 1) * $this -> upload_chunk_size) > $this -> current_file_size ? $this -> current_file_size : (($i + 1) * $this -> upload_chunk_size);
                        if((time() - $this -> last_time) >3)
                        {
                            if(is_callable($callback))
                            {
                                call_user_func_array($callback,array($offset,$this -> current_file_name,
                                    $this->current_file_size,$this -> last_time,$this -> last_size));
                            }
                            $this -> last_size = $offset;
                            $this -> last_time = time();
                        }
                    }
                    $result = $amazons3 -> completeMultipartUpload($this -> bucket,$url,$build_id,$chunks);
                }else{
                    $wpvivid_plugin->wpvivid_log->WriteLog('Uploaded files are less than 5M.','notice');
                    $input = $amazons3 -> inputFile($file);
                    $wpvivid_plugin->wpvivid_log->WriteLog('putObject input:'.wp_json_encode($input).' bucket:'.$this->bucket.' url:'.$url,'notice');
                    $result = $amazons3 -> putObject($input,$this ->bucket,$url);
                    $wpvivid_plugin->wpvivid_log->WriteLog('putObject end:'.$result,'notice');
                }
            }catch(Exception $e)
            {
                if(strstr($e -> getMessage(), 'upload ID may be invalid'))
                {
                    $upload_job['job_data'][basename($file)]['upload_id'] = '';
                    $upload_job['job_data'][basename($file)]['upload_chunks'] = '';
                    continue;
                }
                return array('result' => WPVIVID_FAILED,'error'=>$e -> getMessage());
            }
            if($result){
                $upload_job['job_data'][basename($file)]['uploaded']=1;
                WPvivid_taskmanager::update_backup_sub_task_progress($task_id,'upload',WPVIVID_REMOTE_AMAZONS3,WPVIVID_UPLOAD_SUCCESS,'Uploading '.basename($file).' completed.',$upload_job['job_data']);
                break;
            }
            if(!$result && $i == (WPVIVID_REMOTE_CONNECT_RETRY_TIMES - 1))
            {
                return array('result'=>WPVIVID_FAILED,'error'=>'Uploading '.$file.' to Amazon S3 server failed. '.$file.' might be deleted or network doesn\'t work properly. Please verify the file and confirm the network connection and try again later.');
            }
            sleep(WPVIVID_REMOTE_CONNECT_RETRY_INTERVAL);
        }
        return array('result' =>WPVIVID_SUCCESS);
    }

    public function download($file,$local_path,$callback = '')
    {
        try {
            global $wpvivid_plugin;
            $this->current_file_name = $file['file_name'];
            $this->current_file_size = $file['size'];
            $wpvivid_plugin->wpvivid_download_log->WriteLog('Get amazons3 client.','notice');
            $amazons3 = $this->getS3();
            if (is_array($amazons3) && $amazons3['result'] === WPVIVID_FAILED) {
                return $amazons3;
            }
            if(isset($this->options['s3Path']))
            {
                $url=$this->options['s3Path']. $this -> current_file_name;
            }
            else
            {
                $url=$this->options['path'].'/'. $this -> current_file_name;
            }
            $file_path = trailingslashit($local_path) . $this->current_file_name;
            $start_offset = file_exists($file_path) ? filesize($file_path) : 0;
            $wpvivid_plugin->wpvivid_download_log->WriteLog('Create local file.','notice');
            $fh = fopen($file_path, 'a');
            $wpvivid_plugin->wpvivid_download_log->WriteLog('Downloading file ' . $file['file_name'] . ', Size: ' . $file['size'] ,'notice');
            while ($start_offset < $this->current_file_size) {
                $last_byte = min($start_offset + $this->download_chunk_size - 1, $this->current_file_size - 1);
                $headers['Range'] = "bytes=$start_offset-$last_byte";
                $response = $amazons3->getObject($this->bucket,$url, $fh, $headers['Range']);
                if (!$response)
                    return array('result' => WPVIVID_FAILED, 'error' => 'download ' . $url. ' failed.');
                clearstatcache();
                $state = stat($file_path);
                $start_offset = $state['size'];

                if ((time() - $this->last_time) > 3) {
                    if (is_callable($callback)) {
                        call_user_func_array($callback, array($start_offset, $this->current_file_name,
                            $this->current_file_size, $this->last_time, $this->last_size));
                    }
                    $this->last_size = $start_offset;
                    $this->last_time = time();
                }
            }
            @fclose($fh);

            if(filesize($file_path) == $file['size']){
                if($wpvivid_plugin->wpvivid_check_zip_valid()) {
                    $res = TRUE;
                }
                else{
                    $res = FALSE;
                }
            }
            else{
                $res = FALSE;
            }

            if ($res !== TRUE) {
                @wp_delete_file($file_path);
                return array('result' => WPVIVID_FAILED, 'error' => 'Downloading ' . $file['file_name'] . ' failed. ' . $file['file_name'] . ' might be deleted or network doesn\'t work properly. Please verify the file and confirm the network connection and try again later.');
            }

            return array('result' => WPVIVID_SUCCESS);
        }
        catch (Exception $error){
            $message = 'An exception has occurred. class: '.get_class($error).';msg: '.$error->getMessage().';code: '.$error->getCode().';line: '.$error->getLine().';in_file: '.$error->getFile().';';
            error_log($message);
            return array('result'=>WPVIVID_FAILED, 'error'=>$message);
        }
    }

    public function cleanup($files)
    {
        $amazons3 = $this -> getS3();

        if(is_array($amazons3) && $amazons3['result'] === WPVIVID_FAILED)
            return $amazons3;
        foreach ($files as $file){

            if(isset($this->options['s3Path']))
            {
                $url=$this->options['s3Path'].$file;
            }
            else
            {
                $url=$this->options['path'].'/'.$file;
            }

            $amazons3 -> deleteObject($this -> bucket , $url);
        }
        return array('result' => WPVIVID_SUCCESS);
    }

    private function getS3()
    {
        if(isset($this->options['s3Path']))
        {
            $path_temp = str_replace('s3://','',$this->options['s3Path']);
            if (preg_match("#^/*([^/]+)/(.*)$#", $path_temp, $bmatches))
            {
                $this->bucket = $bmatches[1];
                if(empty($bmatches[2])){
                    $this->options['s3Path'] = '';
                }else{
                    $this->options['s3Path'] = trailingslashit($bmatches[2]);
                }
            } else {
                $this->bucket = $path_temp;
                $this->options['s3Path'] = '';
            }

            if(isset($this->options['is_encrypt']) && $this->options['is_encrypt'] == 1){
                $secret = base64_decode($this->options['secret']);
            }
            else {
                $secret = $this->options['secret'];
            }
            $amazons3 = new WPvivid_Base_S3($this->options['access'],$secret);

            $amazons3 -> setExceptions();
            if($this->options['classMode'])
                $amazons3 -> setStorageClass();
            if($this->options['sse'])
                $amazons3 -> setServerSideEncryption();

            try{
                $region = $amazons3 -> getBucketLocation($this->bucket);
            }catch(Exception $e){
                return array('result' => WPVIVID_FAILED,'error' => $e -> getMessage());
            }
            $endpoint = $this -> getEndpoint($region);
            if(!empty($endpoint))
                $amazons3 -> setEndpoint($endpoint);
            return $amazons3;
        }
        else
        {
            $this->bucket= $this->options['bucket'];
            if(isset($this->options['is_encrypt']) && $this->options['is_encrypt'] == 1){
                $secret = base64_decode($this->options['secret']);
            }
            else {
                $secret = $this->options['secret'];
            }
            $amazons3 = new WPvivid_Base_S3($this->options['access'],$secret);
            $amazons3 -> setExceptions();
            if($this->options['classMode'])
                $amazons3 -> setStorageClass();
            if($this->options['sse'])
                $amazons3 -> setServerSideEncryption();

            try{
                $region = $amazons3 -> getBucketLocation($this->bucket);
            }catch(Exception $e){
                return array('result' => WPVIVID_FAILED,'error' => $e -> getMessage());
            }

            $amazons3->setSignatureVersion('v4');
            $amazons3->setRegion($region);

            $endpoint = $this -> getEndpoint($region);
            if(!empty($endpoint))
                $amazons3 -> setEndpoint($endpoint);
            return $amazons3;
        }

    }
    private function getEndpoint($region){
        switch ($region) {
            case 'EU':
            case 'eu-west-1':
                $endpoint = 's3-eu-west-1.amazonaws.com';
                break;
            case 'US':
            case 'us-east-1':
                $endpoint = 's3.amazonaws.com';
                break;
            case 'us-west-1':
            case 'us-east-2':
            case 'us-west-2':
            case 'eu-west-2':
            case 'eu-west-3':
            case 'ap-southeast-1':
            case 'ap-southeast-2':
            case 'ap-northeast-2':
            case 'sa-east-1':
            case 'ca-central-1':
            case 'us-gov-west-1':
            case 'eu-north-1':
            case 'eu-central-1':
                $endpoint = 's3-'.$region.'.amazonaws.com';
                break;
            case 'ap-northeast-1':
                $endpoint = 's3.'.$region.'.amazonaws.com';
                break;
            case 'ap-south-1':
                $endpoint = 's3.'.$region.'.amazonaws.com';
                break;
            case 'cn-north-1':
                $endpoint = 's3.'.$region.'.amazonaws.com.cn';
                break;
            case 'af-south-1':
                $endpoint = 's3.'.$region.'.amazonaws.com';
                break;
            default:
                $endpoint = 's3.amazonaws.com';
                break;
        }
        return $endpoint;
    }

    public function wpvivid_get_out_of_date_amazons3($out_of_date_remote, $remote)
    {
        if($remote['type'] == WPVIVID_REMOTE_AMAZONS3)
        {
            if(isset($remote['s3Path']))
                $out_of_date_remote = $remote['s3Path'];
            else
                $out_of_date_remote = $remote['path'];
        }
        return $out_of_date_remote;
    }

    public function wpvivid_storage_provider_amazons3($storage_type)
    {
        if($storage_type == WPVIVID_REMOTE_AMAZONS3){
            $storage_type = 'Amazon S3';
        }
        return $storage_type;
    }
}includes/customclass/class-wpvivid-s3compat.php000064400000105275151327705670015774 0ustar00<?php
if (!defined('WPVIVID_PLUGIN_DIR')){
    die;
}
if(!defined('WPVIVID_REMOTE_S3COMPAT')){
    define('WPVIVID_REMOTE_S3COMPAT','s3compat');
}
if(!defined('WPVIVID_S3COMPAT_DEFAULT_FOLDER'))
    define('WPVIVID_S3COMPAT_DEFAULT_FOLDER','/wpvivid_backup');
if(!defined('WPVIVID_S3COMPAT_NEED_PHP_VERSION'))
    define('WPVIVID_S3COMPAT_NEED_PHP_VERSION','5.3.9');
require_once WPVIVID_PLUGIN_DIR . '/includes/customclass/class-wpvivid-remote.php';
use Aws\S3\S3Client;
use Aws\S3\Exception\S3Exception;
class Wpvivid_S3Compat extends WPvivid_Remote{
    private $options;
    private $bucket;
    private $region;

    private $upload_chunk_size = 5242880; // All parts except the last part must be no smaller than 5MB
    private $download_chunk_size = 2097152;

    public function __construct($options = array())
    {
        if(empty($options)){
            add_action('wpvivid_add_storage_tab',array($this,'wpvivid_add_storage_tab_s3compat'), 14);
            add_action('wpvivid_add_storage_page',array($this,'wpvivid_add_storage_page_s3compat'), 14);
            add_action('wpvivid_edit_remote_page',array($this,'wpvivid_edit_storage_page_s3compat'), 14);
            add_filter('wpvivid_remote_pic',array($this,'wpvivid_remote_pic_s3compat'),11);
            add_filter('wpvivid_get_out_of_date_remote',array($this,'wpvivid_get_out_of_date_s3compat'),10,2);
            add_filter('wpvivid_storage_provider_tran',array($this,'wpvivid_storage_provider_s3compat'),10);

        }else{
            $this -> options = $options;
        }
    }

    public function getClient(){
        $res = $this -> compare_php_version();
        if($res['result'] == WPVIVID_FAILED)
            return $res;

        if(isset($this->options['s3directory']))
        {
            $path_temp = str_replace('s3generic://','',$this->options['s3directory'].$this -> options['path']);
            if (preg_match("#^/*([^/]+)/(.*)$#", $path_temp, $bmatches))
            {
                $this->bucket = $bmatches[1];
            } else {
                $this->bucket = $path_temp;
            }
            $this->options['path']=ltrim($this -> options['path'],'/');
            $endpoint_temp = str_replace('https://','',$this->options['endpoint']);
            $explodes = explode('.',$endpoint_temp);
            $this -> region = $explodes[0];
            $this -> options['endpoint'] = 'https://'.trailingslashit($endpoint_temp);
        }
        else
        {
            $endpoint_temp = str_replace('https://','',$this->options['endpoint']);
            $explodes = explode('.',$endpoint_temp);
            $this -> region = $explodes[0];
            $this -> options['endpoint'] = 'https://'.trailingslashit($endpoint_temp);
            $this -> bucket=$this->options['bucket'];
        }

        if(isset($this->options['is_encrypt']) && $this->options['is_encrypt'] == 1){
            $secret = base64_decode($this->options['secret']);
        }
        else {
            $secret = $this->options['secret'];
        }
        include_once WPVIVID_PLUGIN_DIR.'/vendor/autoload.php';
        $s3compat = S3Client::factory(
            array(
                'credentials' => array(
                    'key' => $this -> options['access'],
                    'secret' => $secret,
                ),
                'version' => 'latest',
                'region'  => $this -> region,
                'endpoint' => $this -> options['endpoint'],
            )
        );
        return $s3compat;
    }

    public function test_connect()
    {
        $s3compat = $this -> getClient();
        if(is_array($s3compat) && $s3compat['result'] == WPVIVID_FAILED)
        {
            return $s3compat;
        }

        $temp_file = md5(wp_rand());

        try
        {
            $result = $s3compat->putObject(
                array(
                    'Bucket'=>$this->bucket,
                    'Key' =>  $this->options['path'].'/'.$temp_file,
                    'Body' => $temp_file,
                )
            );
            $etag = $result->get('ETag');
            if(!isset($etag))
            {
                return array('result'=>WPVIVID_FAILED,'error'=>'We successfully accessed the bucket, but create test file failed.');
            }
            $result = $s3compat->deleteObject(array(
                'Bucket' => $this -> bucket,
                'Key'    => $this -> options['path'].'/'.$temp_file,
            ));
            if(empty($result))
            {
                return array('result'=>WPVIVID_FAILED,'error'=>'We successfully accessed the bucket, and create test file succeed, but delete test file failed.');
            }
        }
        catch(S3Exception $e)
        {
            return array('result' => WPVIVID_FAILED,'error' => $e -> getAwsErrorCode().$e -> getMessage());
        }
        catch(Exception $e)
        {
            return array('result' => WPVIVID_FAILED,'error' => $e -> getMessage());
        }
        return array('result' => WPVIVID_SUCCESS);
    }

    public function upload($task_id, $files, $callback = '')
    {
        global $wpvivid_plugin;
        $s3compat = $this -> getClient();
        if(is_array($s3compat) && $s3compat['result'] == WPVIVID_FAILED)
        {
            return $s3compat;
        }

        $upload_job=WPvivid_taskmanager::get_backup_sub_task_progress($task_id,'upload',WPVIVID_REMOTE_S3COMPAT);
        if(empty($upload_job))
        {
            $job_data=array();
            foreach ($files as $file)
            {
                $file_data['size']=filesize($file);
                $file_data['uploaded']=0;
                $job_data[basename($file)]=$file_data;
            }
            WPvivid_taskmanager::update_backup_sub_task_progress($task_id,'upload',WPVIVID_REMOTE_S3COMPAT,WPVIVID_UPLOAD_UNDO,'Start uploading',$job_data);
            $upload_job=WPvivid_taskmanager::get_backup_sub_task_progress($task_id,'upload',WPVIVID_REMOTE_S3COMPAT);
        }

        foreach ($files as $file)
        {
            if(is_array($upload_job['job_data'])&&array_key_exists(basename($file),$upload_job['job_data']))
            {
                if($upload_job['job_data'][basename($file)]['uploaded']==1)
                    continue;
            }
            $this->last_time = time();
            $this->last_size = 0;
            $wpvivid_plugin->wpvivid_log->WriteLog('Start uploading '.basename($file),'notice');
            $wpvivid_plugin->set_time_limit($task_id);
            if(!file_exists($file)){
                $wpvivid_plugin->wpvivid_log->WriteLog('Uploading '.basename($file).' failed.','notice');
                return array('result' =>WPVIVID_FAILED,'error' =>$file.' not found. The file might has been moved, renamed or deleted. Please reload the list and verify the file exists.');
            }
            $result = $this->_put($task_id,$s3compat,$file,$callback);
            if($result['result'] !==WPVIVID_SUCCESS)
            {
                $wpvivid_plugin->wpvivid_log->WriteLog('Uploading '.basename($file).' failed.','notice');
                return $result;
            }
            else
            {
                WPvivid_taskmanager::wpvivid_reset_backup_retry_times($task_id);
            }
            $upload_job['job_data'][basename($file)]['uploaded']=1;
            $wpvivid_plugin->wpvivid_log->WriteLog('Finished uploading '.basename($file),'notice');
            WPvivid_taskmanager::update_backup_sub_task_progress($task_id,'upload',WPVIVID_REMOTE_S3COMPAT,WPVIVID_UPLOAD_SUCCESS,'Uploading '.basename($file).' completed.',$upload_job['job_data']);
        }
        return array('result' => WPVIVID_SUCCESS);
    }
    public function _put($task_id,$s3compat,$file,$callback){
        $path = $this->options['path'].'/'.basename($file);
        $this->current_file_size = filesize($file);
        $this->current_file_name = basename($file);

        try
        {
            if($this->current_file_size > $this->upload_chunk_size)
            {
                $result = $s3compat ->createMultipartUpload(array(
                    'Bucket'       => $this -> bucket,
                    'Key'          => $path,
                ));
                if(!isset($result['UploadId']))
                    return array('result' => WPVIVID_FAILED, 'error' => 'Creating upload task failed. Please try again.');

                $uploadId = $result['UploadId'];
                $fh = fopen($file,'rb');
                $partNumber = 1;
                $parts = array();
                $offset = 0;
                while(!feof($fh))
                {
                    $data = fread($fh,$this -> upload_chunk_size);
                    $result = $this -> _upload_loop($s3compat,$uploadId,$path,$data,$partNumber,$parts);
                    if($result['result'] === WPVIVID_FAILED)
                        break;

                    $partNumber ++;
                    $offset += $this -> upload_chunk_size;
                    if((time() - $this -> last_time) >3)
                    {
                        if(is_callable($callback))
                        {
                            call_user_func_array($callback,array(min($offset,$this -> current_file_size),$this -> current_file_name,
                                $this->current_file_size,$this -> last_time,$this -> last_size));
                        }
                        $this -> last_size = $offset;
                        $this -> last_time = time();
                    }
                }
                fclose($fh);

                if($result['result'] === WPVIVID_SUCCESS)
                {
                    $ret = $s3compat ->completeMultipartUpload
                    (
                        array(
                            'Bucket' => $this -> bucket,
                            'Key' => $path,
                            'Parts' => $parts,
                            'UploadId' => $uploadId,
                        )
                    );
                    if(!isset($ret['Location']))
                    {
                        $result = array('result' => WPVIVID_FAILED, 'error' => 'Merging multipart failed. File name: '.$this -> current_file_name);
                    }
                }
            }
            else {
                $res = $s3compat ->putObject(
                    array(
                        'Bucket'=>$this -> bucket,
                        'Key' =>  $path,
                        'SourceFile' => $file,
                    )
                );
                $etag = $res -> get('ETag');
                if(isset($etag))
                {
                    $result = array('result' => WPVIVID_SUCCESS);
                }else {
                    $result = array('result' => WPVIVID_FAILED , 'error' => 'upload '.$this -> current_file_name.' failed.');
                }
            }
        }
        catch(S3Exception $e)
        {
            return array('result' => WPVIVID_FAILED,'error' => $e -> getAwsErrorCode().$e -> getMessage());
        }
        catch(Exception $e)
        {
            return array('result' => WPVIVID_FAILED,'error' => $e -> getMessage());
        }
        return $result;
    }
    public function _upload_loop($s3compat,$uploadId,$path,$data,$partNumber,&$parts){
        for($i =0;$i <WPVIVID_REMOTE_CONNECT_RETRY_TIMES;$i ++)
        {
            $ret = $s3compat ->uploadPart(array(
                'Bucket'     => $this ->bucket,
                'Key'        => $path,
                'UploadId'   => $uploadId,
                'PartNumber' => $partNumber,
                'Body'       => $data,
            ));
            if(isset($ret['ETag']))
            {
                $parts[] = array(
                    'ETag' => $ret['ETag'],
                    'PartNumber' => $partNumber,
                );
                return array('result' => WPVIVID_SUCCESS);
            }
        }
        return array('result' => WPVIVID_FAILED,'error' =>'Multipart upload failed. File name: '.$this -> current_file_name);
    }

    public function download($file, $local_path, $callback = '')
    {
        try {
            global $wpvivid_plugin;
            $this->current_file_name = $file['file_name'];
            $this->current_file_size = $file['size'];
            $file_path = trailingslashit($local_path) . $this->current_file_name;
            $wpvivid_plugin->wpvivid_download_log->WriteLog('Get s3compat client.','notice');
            $s3compat = $this->getClient();
            if (is_array($s3compat) && $s3compat['result'] == WPVIVID_FAILED) {
                return $s3compat;
            }

            $start_offset = file_exists($file_path) ? filesize($file_path) : 0;
            $wpvivid_plugin->wpvivid_download_log->WriteLog('Create local file.','notice');
            $fh = fopen($file_path, 'a');
            $wpvivid_plugin->wpvivid_download_log->WriteLog('Downloading file ' . $file['file_name'] . ', Size: ' . $file['size'] ,'notice');
            while ($start_offset < $this->current_file_size)
            {
                $last_byte = min($start_offset + $this->download_chunk_size - 1, $this->current_file_size - 1);
                $range = "bytes=$start_offset-$last_byte";
                $response = $this->_download_loop($s3compat, $range, $fh);
                if ($response['result'] === WPVIVID_FAILED)
                {
                    return $response;
                }

                clearstatcache();
                $state = stat($file_path);
                $start_offset = $state['size'];
                if ((time() - $this->last_time) > 3)
                {
                    if (is_callable($callback)) {
                        call_user_func_array($callback, array($start_offset, $this->current_file_name,
                            $this->current_file_size, $this->last_time, $this->last_size));
                    }
                    $this->last_size = $start_offset;
                    $this->last_time = time();
                }
            }
            @fclose($fh);

            if(filesize($file_path) == $file['size'])
            {
                if($wpvivid_plugin->wpvivid_check_zip_valid())
                {
                    $res = TRUE;
                }
                else{
                    $res = FALSE;
                }
            }
            else{
                $res = FALSE;
            }

            if ($res !== TRUE) {
                @wp_delete_file($file_path);
                return array('result' => WPVIVID_FAILED, 'error' => 'Downloading ' . $file['file_name'] . ' failed. ' . $file['file_name'] . ' might be deleted or network doesn\'t work properly. Please verify the file and confirm the network connection and try again later.');
            }

            return array('result' => WPVIVID_SUCCESS);
        }
        catch (S3Exception $e) {
            return array('result' => WPVIVID_FAILED, 'error' => $e->getAwsErrorCode() . $e->getMessage());
        }
        catch (Exception $error){
            $message = 'An exception has occurred. class: '.get_class($error).';msg: '.$error->getMessage().';code: '.$error->getCode().';line: '.$error->getLine().';in_file: '.$error->getFile().';';
            error_log($message);
            return array('result'=>WPVIVID_FAILED, 'error'=>$message);
        }
    }

    public function _download_loop($s3compat,$range,$fh){
        try{
            for($i =0;$i <WPVIVID_REMOTE_CONNECT_RETRY_TIMES;$i ++){
                $response = $s3compat -> getObject(array(
                    'Bucket' => $this -> bucket,
                    'Key'    => $this -> options['path'].'/'.$this -> current_file_name,
                    'Range'  => $range
                ));
                if(isset($response['Body']) && fwrite($fh,$response['Body'])) {
                    return array('result' => WPVIVID_SUCCESS);
                }
            }
            return array('result'=>WPVIVID_FAILED, 'error' => 'download '.$this -> current_file_name.' failed.');
        }catch(S3Exception $e){
            return array('result' => WPVIVID_FAILED,'error' => $e -> getAwsErrorCode().$e -> getMessage());
        }catch(Exception $e){
            return array('result' => WPVIVID_FAILED,'error' => $e -> getMessage());
        }
    }

    public function cleanup($files)
    {
        $s3compat = $this -> getClient();
        if(is_array($s3compat) && $s3compat['result'] == WPVIVID_FAILED){
            return $s3compat;
        }

        $keys = array();
        foreach ($files as $file){
            $keys[] = array('Key' => $this -> options['path'].'/'.basename($file));
        }
        try{
            $result = $s3compat -> deleteObjects(array(
                // Bucket is required
                'Bucket' => $this -> bucket,
                // Objects is required
                'Objects' => $keys
            ));
        }catch (S3Exception $e){}catch (Exception $e){}
        return array('result'=>WPVIVID_SUCCESS);
    }

    public function wpvivid_add_storage_tab_s3compat(){
        ?>
        <div class="storage-providers" remote_type="s3compat" onclick="select_remote_storage(event, 'storage_account_s3compat');">
            <img src="<?php echo esc_url(WPVIVID_PLUGIN_URL.'/admin/partials/images/storage-digitalocean.png'); ?>" style="vertical-align:middle;"/><?php esc_html_e('DigitalOcean Spaces', 'wpvivid-backuprestore'); ?>
        </div>
        <?php
    }

    public function wpvivid_add_storage_page_s3compat(){
        ?>
        <div id="storage_account_s3compat"  class="storage-account-page" style="display:none;">
            <div style="padding: 0 10px 10px 0;">
                <strong><?php esc_html_e('Enter Your DigitalOcean Spaces Account', 'wpvivid-backuprestore'); ?></strong>
            </div>
            <table class="wp-list-table widefat plugins" style="width:100%;">
                <tbody>
                <form>
                <tr>
                    <td class="plugin-title column-primary">
                        <div class="wpvivid-storage-form">
                            <input type="text" class="regular-text" autocomplete="off" option="s3compat" name="name" placeholder="<?php esc_attr_e('Enter a unique alias: e.g. DOS-001', 'wpvivid-backuprestore'); ?>" onkeyup="value=value.replace(/[^a-zA-Z0-9\-_]/g,'')" />
                        </div>
                    </td>
                    <td class="column-description desc">
                        <div class="wpvivid-storage-form-desc">
                            <i><?php esc_html_e('A name to help you identify the storage if you have multiple remote storage connected.', 'wpvivid-backuprestore'); ?></i>
                        </div>
                    </td>
                </tr>
                <tr>
                    <td class="plugin-title column-primary">
                        <div class="wpvivid-storage-form">
                            <input type="text" class="regular-text" autocomplete="off" option="s3compat" name="access" placeholder="<?php esc_attr_e('DigitalOcean Spaces access key', 'wpvivid-backuprestore'); ?>" />
                        </div>
                    </td>
                    <td class="column-description desc">
                        <div class="wpvivid-storage-form-desc">
                            <i><?php esc_html_e('Enter your DigitalOcean Spaces access key', 'wpvivid-backuprestore'); ?></i>
                        </div>
                    </td>
                </tr>
                <tr>
                    <td class="plugin-title column-primary">
                        <div class="wpvivid-storage-form">
                            <input type="password" class="regular-text" autocomplete="new-password" option="s3compat" name="secret" placeholder="<?php esc_attr_e('DigitalOcean Spaces secret key', 'wpvivid-backuprestore'); ?>" />
                        </div>
                    </td>
                    <td class="column-description desc">
                        <div class="wpvivid-storage-form-desc">
                            <i><?php esc_html_e('Enter your DigitalOcean Spaces secret key', 'wpvivid-backuprestore'); ?></i>
                        </div>
                    </td>
                </tr>
                <tr>
                    <td class="plugin-title column-primary">
                        <div class="wpvivid-storage-form">
                            <input type="text" class="regular-text" autocomplete="off" option="s3compat" name="bucket" placeholder="<?php esc_attr_e('Space Name(e.g. test)', 'wpvivid-backuprestore'); ?>" />
                        </div>
                    </td>
                    <td class="column-description desc">
                        <div class="wpvivid-storage-form-desc">
                            <i><span><?php esc_html_e('Enter an existed Space to create a custom backup storage directory.', 'wpvivid-backuprestore'); ?></span></i>
                        </div>
                    </td>
                </tr>
                <tr>
                    <td class="plugin-title column-primary">
                        <div class="wpvivid-storage-form">
                            <input type="text" class="regular-text" autocomplete="off" option="s3compat" name="path" placeholder="<?php esc_attr_e('Custom Path', 'wpvivid-backuprestore'); ?>" />
                        </div>
                    </td>
                    <td class="column-description desc">
                        <div class="wpvivid-storage-form-desc">
                            <i><span><?php esc_html_e('Customize the directory where you want to store backups within the Space.', 'wpvivid-backuprestore'); ?></span></i>
                        </div>
                    </td>
                </tr>
                <tr>
                    <td class="plugin-title column-primary">
                        <div class="wpvivid-storage-form">
                            <input type="text" class="regular-text" autocomplete="off" option="s3compat" name="endpoint" placeholder="<?php esc_attr_e('region.digitaloceanspaces.com', 'wpvivid-backuprestore'); ?>" />
                        </div>
                    </td>
                    <td class="column-description desc">
                        <div class="wpvivid-storage-form-desc">
                            <i><?php esc_html_e('Enter the DigitalOcean Endpoint for the storage', 'wpvivid-backuprestore'); ?></i>
                        </div>
                    </td>
                </tr>
                <tr>
                    <td class="plugin-title column-primary">
                        <div class="wpvivid-storage-select">
                            <label>
                                <input type="checkbox" option="s3compat" name="default" checked /><?php esc_html_e('Set as the default remote storage.', 'wpvivid-backuprestore'); ?>
                            </label>
                        </div>
                    </td>
                    <td class="column-description desc">
                        <div class="wpvivid-storage-form-desc">
                            <i><?php esc_html_e('Once checked, all this sites backups sent to a remote storage destination will be uploaded to this storage by default.', 'wpvivid-backuprestore'); ?></i>
                        </div>
                    </td>
                </tr>
                </form>
                <tr>
                    <td class="plugin-title column-primary">
                        <div class="wpvivid-storage-form">
                            <input class="button-primary" option="add-remote" type="submit" value="<?php esc_attr_e('Test and Add', 'wpvivid-backuprestore'); ?>" />
                        </div>
                    </td>
                    <td class="column-description desc">
                        <div class="wpvivid-storage-form-desc">
                            <i><?php esc_html_e('Click the button to connect to DigitalOcean Spaces storage and add it to the storage list below.', 'wpvivid-backuprestore'); ?></i>
                        </div>
                    </td>
                </tr>
                </tbody>
            </table>
        </div>
        <?php
    }

    public function wpvivid_edit_storage_page_s3compat()
    {
        ?>
        <div id="remote_storage_edit_s3compat" class="postbox storage-account-block remote-storage-edit" style="display:none;">
            <div style="padding: 0 10px 10px 0;">
                <strong><?php esc_html_e('Enter Your DigitalOcean Spaces Account', 'wpvivid-backuprestore'); ?></strong>
            </div>
            <table class="wp-list-table widefat plugins" style="width:100%;">
                <tbody>
                <form>
                <tr>
                    <td class="plugin-title column-primary">
                        <div class="wpvivid-storage-form">
                            <input type="text" class="regular-text" autocomplete="off" option="edit-s3compat" name="name" placeholder="<?php esc_attr_e('Enter a unique alias: e.g. DOS-001', 'wpvivid-backuprestore'); ?>" onkeyup="value=value.replace(/[^a-zA-Z0-9\-_]/g,'')" />
                        </div>
                    </td>
                    <td class="column-description desc">
                        <div class="wpvivid-storage-form-desc">
                            <i><?php esc_html_e('A name to help you identify the storage if you have multiple remote storage connected.', 'wpvivid-backuprestore'); ?></i>
                        </div>
                    </td>
                </tr>
                <tr>
                    <td class="plugin-title column-primary">
                        <div class="wpvivid-storage-form">
                            <input type="text" class="regular-text" autocomplete="off" option="edit-s3compat" name="access" placeholder="<?php esc_attr_e('DigitalOcean Spaces access key', 'wpvivid-backuprestore'); ?>" />
                        </div>
                    </td>
                    <td class="column-description desc">
                        <div class="wpvivid-storage-form-desc">
                            <i><?php esc_html_e('Enter your DigitalOcean Spaces access key', 'wpvivid-backuprestore'); ?></i>
                        </div>
                    </td>
                </tr>
                <tr>
                    <td class="plugin-title column-primary">
                        <div class="wpvivid-storage-form">
                            <input type="password" class="regular-text" autocomplete="new-password" option="edit-s3compat" name="secret" placeholder="<?php esc_attr_e('DigitalOcean Spaces secret key', 'wpvivid-backuprestore'); ?>" />
                        </div>
                    </td>
                    <td class="column-description desc">
                        <div class="wpvivid-storage-form-desc">
                            <i><?php esc_html_e('Enter your DigitalOcean Spaces secret key', 'wpvivid-backuprestore'); ?></i>
                        </div>
                    </td>
                </tr>
                <tr>
                    <td class="plugin-title column-primary">
                        <div class="wpvivid-storage-form">
                            <input type="text" class="regular-text" autocomplete="off" option="edit-s3compat" name="bucket" placeholder="<?php esc_attr_e('Space Name(e.g. test)', 'wpvivid-backuprestore'); ?>" />
                        </div>
                    </td>
                    <td class="column-description desc">
                        <div class="wpvivid-storage-form-desc">
                            <i><span><?php esc_html_e('Enter an existed Space to create a custom backup storage directory.', 'wpvivid-backuprestore'); ?></span></i>
                        </div>
                    </td>
                </tr>
                <tr>
                    <td class="plugin-title column-primary">
                        <div class="wpvivid-storage-form">
                            <input type="text" class="regular-text" autocomplete="off" option="edit-s3compat" name="path" placeholder="<?php esc_attr_e('Custom Path', 'wpvivid-backuprestore'); ?>" />
                        </div>
                    </td>
                    <td class="column-description desc">
                        <div class="wpvivid-storage-form-desc">
                            <i><span><?php esc_html_e('Customize the directory where you want to store backups within the Space.', 'wpvivid-backuprestore'); ?></span></i>
                        </div>
                    </td>
                </tr>
                <tr>
                    <td class="plugin-title column-primary">
                        <div class="wpvivid-storage-form">
                            <input type="text" class="regular-text" autocomplete="off" option="edit-s3compat" name="endpoint" placeholder="<?php esc_attr_e('region.digitaloceanspaces.com', 'wpvivid-backuprestore'); ?>" />
                        </div>
                    </td>
                    <td class="column-description desc">
                        <div class="wpvivid-storage-form-desc">
                            <i><?php esc_html_e('Enter the DigitalOcean Endpoint for the storage', 'wpvivid-backuprestore'); ?></i>
                        </div>
                    </td>
                </tr>
                </form>
                <tr>
                    <td class="plugin-title column-primary">
                        <div class="wpvivid-storage-form">
                            <input class="button-primary" option="edit-remote" type="submit" value="<?php esc_attr_e('Save Changes', 'wpvivid-backuprestore'); ?>" />
                        </div>
                    </td>
                    <td class="column-description desc">
                        <div class="wpvivid-storage-form-desc">
                            <i><?php esc_html_e('Click the button to save the changes.', 'wpvivid-backuprestore'); ?></i>
                        </div>
                    </td>
                </tr>
                </tbody>
            </table>
        </div>
        <script>
            jQuery("input:text[option=edit-s3compat][name=s3directory]").keyup(function(){
                var value = jQuery(this).val();
                if(value == ''){
                    value = '*';
                }
                value = value + '/wpvivid_backup';
                jQuery('#wpvivid_edit_dos_root_path').html(value);
            });
        </script>
        <?php
    }

    public function wpvivid_remote_pic_s3compat($remote){
        $remote['s3compat']['default_pic'] = '/admin/partials/images/storage-digitalocean(gray).png';
        $remote['s3compat']['selected_pic'] = '/admin/partials/images/storage-digitalocean.png';
        $remote['s3compat']['title'] = 'DigitalOcean Spaces';
        return $remote;
    }

    public function sanitize_options($skip_name='')
    {
        $ret['result']=WPVIVID_FAILED;
        if(!isset($this->options['name']))
        {
            $ret['error']="Warning: An alias for remote storage is required.";
            return $ret;
        }

        $this->options['name']=sanitize_text_field($this->options['name']);

        if(empty($this->options['name']))
        {
            $ret['error']="Warning: An alias for remote storage is required.";
            return $ret;
        }

        $remoteslist=WPvivid_Setting::get_all_remote_options();
        foreach ($remoteslist as $key=>$value)
        {
            if(isset($value['name'])&&$value['name'] == $this->options['name']&&$skip_name!=$value['name'])
            {
                $ret['error']="Warning: The alias already exists in storage list.";
                return $ret;
            }
        }

        if(!isset($this->options['access']))
        {
            $ret['error']="Warning: The access key for S3-Compatible is required.";
            return $ret;
        }

        $this->options['access']=sanitize_text_field($this->options['access']);

        if(empty($this->options['access']))
        {
            $ret['error']="Warning: The access key for S3-Compatible is required.";
            return $ret;
        }

        if(!isset($this->options['secret']))
        {
            $ret['error']="Warning: The storage secret key is required.";
            return $ret;
        }

        $this->options['secret']=sanitize_text_field($this->options['secret']);

        if(empty($this->options['secret']))
        {
            $ret['error']="Warning: The storage secret key is required.";
            return $ret;
        }
        $this->options['secret'] = base64_encode($this->options['secret']);
        $this->options['is_encrypt'] = 1;

        if(empty($this->options['bucket']))
        {
            $ret['error']="Warning: A Digital Space is required.";
            return $ret;
        }

        if(!isset($this->options['path']))
        {
            $ret['error']="Warning: A directory name is required.";
            return $ret;
        }

        $this->options['path']=sanitize_text_field($this->options['path']);

        if(empty($this->options['path'])){
            $ret['error']="Warning: A directory name is required.";
            return $ret;
        }

        if(!isset($this->options['endpoint']))
        {
            $ret['error']="Warning: The end-point is required.";
            return $ret;
        }

        $this->options['endpoint']=sanitize_text_field($this->options['endpoint']);

        $ret['result']=WPVIVID_SUCCESS;
        $ret['options']=$this->options;
        return $ret;
    }

    public function wpvivid_get_out_of_date_s3compat($out_of_date_remote, $remote)
    {
        if($remote['type'] == WPVIVID_REMOTE_S3COMPAT)
        {
            if(isset($remote['s3directory']))
                $out_of_date_remote = $remote['s3directory'].$remote['path'];
            else
                $out_of_date_remote = $remote['path'];
        }
        return $out_of_date_remote;
    }

    public function wpvivid_storage_provider_s3compat($storage_type)
    {
        if($storage_type == WPVIVID_REMOTE_S3COMPAT){
            $storage_type = 'DigitalOcean Spaces';
        }
        return $storage_type;
    }
    private function compare_php_version(){
        if(version_compare(WPVIVID_GOOGLE_NEED_PHP_VERSION,phpversion()) > 0){
            return array('result' => WPVIVID_FAILED,'error' => 'The required PHP version is higher than '.WPVIVID_S3COMPAT_NEED_PHP_VERSION.'. After updating your PHP version, please try again.');
        }
        return array('result' => WPVIVID_SUCCESS);
    }


}includes/customclass/class-wpvivid-remote.php000064400000001715151327705670015530 0ustar00<?php
if (!defined('WPVIVID_PLUGIN_DIR')){
    die;
}
abstract class WPvivid_Remote{
    public $current_file_name = '';
    public $current_file_size = '';
    public $last_time = 0;
    public $last_size = 0;

    public $object;
    public $remote;

    abstract public function test_connect();
    abstract public function upload($task_id,$files,$callback = '');  // $files = array();
    abstract public function download($file,$local_path,$callback = ''); // $file = array('file_name' => ,'size' =>,'md5' =>)
    abstract public function cleanup($files);  // $files = array();

    public function formatBytes($bytes, $precision = 2)
    {
        $units = array('B', 'KB', 'MB', 'GB', 'TB');
        $bytes = max($bytes, 0);
        $pow = floor(($bytes ? log($bytes) : 0) / log(1024));
        $pow = min($pow, count($units) - 1);
        $bytes /= (1 << (10 * $pow));
        return round($bytes, $precision) . '' . $units[$pow];
    }
}includes/customclass/class-wpvivid-extend-sftp.php000064400000010702151327705670016472 0ustar00<?php

if (!defined('WPVIVID_PLUGIN_DIR')){
    die;
}
include_once WPVIVID_PLUGIN_DIR.'/vendor/autoload.php';
class WPvivid_Net_SFTP extends Net_SFTP
{
    function get($remote_file, $local_file = false, $offset = 0, $length = -1, $callback = null)
    {
        if (!($this->bitmap & NET_SSH2_MASK_LOGIN)) {
            return false;
        }

        $remote_file = $this->_realpath($remote_file);
        if ($remote_file === false) {
            return false;
        }

        $packet = pack('Na*N2', strlen($remote_file), $remote_file, NET_SFTP_OPEN_READ, 0);
        if (!$this->_send_sftp_packet(NET_SFTP_OPEN, $packet)) {
            return false;
        }

        $response = $this->_get_sftp_packet();
        switch ($this->packet_type) {
            case NET_SFTP_HANDLE:
                $handle = substr($response, 4);
                break;
            case NET_SFTP_STATUS:
                $this->_logError($response);
                return false;
            default:
                user_error('Expected SSH_FXP_HANDLE or SSH_FXP_STATUS');
                return false;
        }

        if (is_resource($local_file)) {
            $fp = $local_file;
            $stat = fstat($fp);
            $res_offset = $stat['size'];
        } else {
            $res_offset = 0;
            if ($local_file !== false) {
                $fp = fopen($local_file, 'wb');
                if (!$fp) {
                    return false;
                }
            } else {
                $content = '';
            }
        }

        $fclose_check = $local_file !== false && !is_resource($local_file);

        $start = $offset;
        $read = 0;
        while (true) {
            $i = 0;

            while ($i < NET_SFTP_QUEUE_SIZE && ($length < 0 || $read < $length)) {
                $tempoffset = $start + $read;

                $packet_size = $length > 0 ? min($this->max_sftp_packet, $length - $read) : $this->max_sftp_packet;
                $packet = pack('Na*N3', strlen($handle), $handle, $tempoffset / 4294967296, $tempoffset, $packet_size);
                if (!$this->_send_sftp_packet(NET_SFTP_READ, $packet)) {
                    if ($fclose_check) {
                        fclose($fp);
                    }
                    return false;
                }
                $packet = null;
                $read+= $packet_size;
                $i++;
            }

            if (!$i) {
                break;
            }

            $clear_responses = false;
            while ($i > 0) {
                $i--;

                if ($clear_responses) {
                    $this->_get_sftp_packet();
                    continue;
                } else {
                    $response = $this->_get_sftp_packet();
                }

                switch ($this->packet_type) {
                    case NET_SFTP_DATA:
                        $temp = substr($response, 4);
                        $offset+= strlen($temp);
                        if ($local_file === false) {
                            $content.= $temp;
                        } else {
                            fputs($fp, $temp);
                        }

                        if( is_callable($callback)){
                            call_user_func_array($callback,array($offset));
                        }

                        $temp = null;
                        break;
                    case NET_SFTP_STATUS:
                        $this->_logError($response);
                        $clear_responses = true;
                        break;
                    default:
                        if ($fclose_check) {
                            fclose($fp);
                        }
                        user_error('Expected SSH_FX_DATA or SSH_FXP_STATUS');
                }
                $response = null;
            }

            if ($clear_responses) {
                break;
            }
        }

        if ($length > 0 && $length <= $offset - $start) {
            if ($local_file === false) {
                $content = substr($content, 0, $length);
            } else {
                ftruncate($fp, $length + $res_offset);
            }
        }

        if ($fclose_check) {
            fclose($fp);
        }

        if (!$this->_close_handle($handle)) {
            return false;
        }

        return isset($content) ? $content : true;
    }
}includes/customclass/class-wpvivid-s3.php000064400000255367151327705670014600 0ustar00<?php
if (!defined('WPVIVID_PLUGIN_DIR')){
    die;
}

/**
* $Id$
*
* Copyright (c) 2013, Donovan Schönknecht.  All rights reserved.
* Portions copyright (c) 2012-2022, David Anderson (https://david.dw-perspective.org.uk). All rights reserved
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above copyright notice,
*   this list of conditions and the following disclaimer.
* - Redistributions in binary form must reproduce the above copyright
*   notice, this list of conditions and the following disclaimer in the
*   documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* Amazon S3 is a trademark of Amazon.com, Inc. or its affiliates.
*/

/**
* Amazon S3 PHP class
*
* @link http://undesigned.org.za/2007/10/22/amazon-s3-php-class
* @version 0.5.1
*/
class WPvivid_S3
{
	// ACL flags
	const ACL_PRIVATE = 'private';
	const ACL_PUBLIC_READ = 'public-read';
	const ACL_PUBLIC_READ_WRITE = 'public-read-write';
	const ACL_AUTHENTICATED_READ = 'authenticated-read';

	const STORAGE_CLASS_STANDARD = 'STANDARD';
	const STORAGE_CLASS_RRS = 'REDUCED_REDUNDANCY';
	const STORAGE_CLASS_STANDARD_IA = 'STANDARD_IA';

	const SSE_NONE = '';
	const SSE_AES256 = 'AES256';

	/**
	 * The AWS Access key
	 *
	 * @var string
	 * @access private
	 * @static
	 */
	private $__accessKey = null;

	/**
	 * AWS Secret Key
	 *
	 * @var string
	 * @access private
	 * @static
	 */
	private $__secretKey = null;

	/**
	 * SSL Client key
	 *
	 * @var string
	 * @access private
	 * @static
	 */
	private $__sslKey = null;

	/**
	 * Default delimiter to be used, for example while getBucket().
	 * @var string
	 * @access public
	 * @
	 */
	public $defDelimiter = null;

	/**
	 * AWS URI
	 *
	 * @var string
	 * @acess public
	 * @static
	 */
	public $endpoint = 's3.amazonaws.com';

	/**
	 * Proxy information
	 *
	 * @var null|array
	 * @access public
	 * @static
	 */
	public $proxy = null;

	/**
	 * Connect using SSL?
	 *
	 * @var bool
	 * @access public
	 * @static
	 */
	public $useSSL = false;

	/**
	 * Use SSL validation?
	 *
	 * @var bool
	 * @access public
	 * @static
	 */
	public $useSSLValidation = true;

	/**
	 * Use SSL version
	 *
	 * @var const
	 * @access public
	 * @static
	 */
	public $useSSLVersion = CURL_SSLVERSION_TLSv1;

	/**
	 * Use PHP exceptions?
	 *
	 * @var bool
	 * @access public
	 * @static
	 */
	public $useExceptions = false;

	/**
	 * Time offset applied to time()
	 * @access private
	 * @static
	 */
	private $__timeOffset = 0;

	/**
	 * SSL client key
	 *
	 * @var bool
	 * @access public
	 * @static
	 */
	public $sslKey = null;
	
	/**
	 * SSL client certfificate
	 *
	 * @var string
	 * @acess public
	 * @static
	 */
	public $sslCert = null;
	
	/**
	 * SSL CA cert (only required if you are having problems with your system CA cert)
	 *
	 * @var string
	 * @access public
	 * @static
	 */
	public $sslCACert = null;
	
	/**
	 * AWS Key Pair ID
	 *
	 * @var string
	 * @access private
	 * @static
	 */
	private $__signingKeyPairId = null;
	
	/**
	 * Key resource, freeSigningKey() must be called to clear it from memory
	 *
	 * @var bool
	 * @access private
	 * @
	 */
	private $__signingKeyResource = false;

    var $_serverSideEncryption = self::SSE_NONE;
    var $_storageClass = self::STORAGE_CLASS_STANDARD;
	/**
	* Constructor - if you're not using the class statically
	*
	* @param string $accessKey Access key
	* @param string $secretKey Secret key
	* @param boolean $useSSL Enable SSL
	* @param string $endpoint Amazon URI
	* @return void
	*/
	var $region;
	public function __construct($accessKey = null, $secretKey = null, $useSSL = false, $endpoint = 's3.amazonaws.com')
	{
		if ($accessKey !== null && $secretKey !== null)
			$this -> setAuth($accessKey, $secretKey);
		$this -> useSSL = $useSSL;
        $this -> endpoint = $endpoint;
        $this->region = 'us-east-1';
	}


	/**
	* Set the service endpoint
	*
	* @param string $host Hostname
	* @return void
	*/
	public function setEndpoint($host)
	{
        $this -> endpoint = $host;
	}

    /**
     * Set the service region
     *
     * @param string $region Region
     * @return void
     */
    public function setRegion($region) {
        $this->region = $region;
    }

    /**
     * Get the service region
     * Note: Region calculation will be done in methods/s3.php file
     *
     * @return string Region
     */
    public function getRegion() {
        return $this->region;
    }

	/**
	* Set AWS access key and secret key
	*
	* @param string $accessKey Access key
	* @param string $secretKey Secret key
	* @return void
	*/
	public function setAuth($accessKey, $secretKey)
	{
        $this -> __accessKey = $accessKey;
        $this -> __secretKey = $secretKey;
	}


	/**
	* Check if AWS keys have been set
	*
	* @return boolean
	*/
	public function hasAuth() {
		return ($this -> __accessKey !== null && $this -> __secretKey !== null);
	}


	/**
	* Set SSL on or off
	*
	* @param boolean $enabled SSL enabled
	* @param boolean $validate SSL certificate validation
	* @return void
	*/
	public function setSSL($enabled, $validate = true)
	{
        $this -> useSSL = $enabled;
        $this -> useSSLValidation = $validate;
	}


	/**
	* Set SSL client certificates (experimental)
	*
	* @param string $sslCert SSL client certificate
	* @param string $sslKey SSL client key
	* @param string $sslCACert SSL CA cert (only required if you are having problems with your system CA cert)
	* @return void
	*/
	public function setSSLAuth($sslCert = null, $sslKey = null, $sslCACert = null)
	{
        $this -> sslCert = $sslCert;
        $this -> sslKey = $sslKey;
        $this -> sslCACert = $sslCACert;
	}


	/**
	* Set proxy information
	*
	* @param string $host Proxy hostname and port (localhost:1234)
	* @param string $user Proxy username
	* @param string $pass Proxy password
	* @param constant $type CURL proxy type
	* @return void
	*/
	public function setProxy($host, $user = null, $pass = null, $type = CURLPROXY_SOCKS5)
	{
        $this -> proxy = array('host' => $host, 'type' => $type, 'user' => $user, 'pass' => $pass);
	}


	/**
	* Set the error mode to exceptions
	*
	* @param boolean $enabled Enable exceptions
	* @return void
	*/
	public function setExceptions($enabled = true)
	{
        $this -> useExceptions = $enabled;
	}


	/**
	* Set AWS time correction offset (use carefully)
	*
	* This can be used when an inaccurate system time is generating
	* invalid request signatures.  It should only be used as a last
	* resort when the system time cannot be changed.
	*
	* @param string $offset Time offset (set to zero to use AWS server time)
	* @return void
	*/
	public function setTimeCorrectionOffset($offset = 0)
	{
		if ($offset == 0)
		{
			$rest = new WPvivid_S3Request('HEAD','','',$this -> endpoint,$this);
			$rest = $rest->getResponse();
			$awstime = $rest->headers['date'];
			$systime = time();			
			$offset = $systime > $awstime ? -($systime - $awstime) : ($awstime - $systime);
		}
		$this -> __timeOffset = $offset;
	}


	/**
	* Set signing key
	*
	* @param string $keyPairId AWS Key Pair ID
	* @param string $signingKey Private Key
	* @param boolean $isFile Load private key from file, set to false to load string
	* @return boolean
	*/
	public function setSigningKey($keyPairId, $signingKey, $isFile = true)
	{
        $this -> __signingKeyPairId = $keyPairId;
		if (($this -> __signingKeyResource = openssl_pkey_get_private($isFile ?
		file_get_contents($signingKey) : $signingKey)) !== false) return true;
        $this -> __triggerError('S3::setSigningKey(): Unable to open load private key: '.$signingKey, __FILE__, __LINE__);
		return false;
	}


	/**
	* Free signing key from memory, MUST be called if you are using setSigningKey()
	*
	* @return void
	*/
	public function freeSigningKey()
	{
		if ($this -> __signingKeyResource !== false)
			openssl_free_key($this -> __signingKeyResource);
	}


	/**
	* Internal error handler
	*
	* @internal Internal error handler
	* @param string $message Error message
	* @param string $file Filename
	* @param integer $line Line number
	* @param integer $code Error code
	* @return void
	*/
	function __triggerError($message, $file, $line, $code = 0)
	{
		if ($this -> useExceptions)
			throw new WPvivid_S3Exception(esc_html($message), esc_attr($file), esc_attr($line), esc_attr($code));
		else
			trigger_error(esc_html($message), E_USER_WARNING);
	}


	/**
	* Get a list of buckets
	*
	* @param boolean $detailed Returns detailed bucket list when true
	* @return array | false
	*/
	public function listBuckets($detailed = false)
	{
		$rest = new WPvivid_S3Request('GET', '', '', $this -> endpoint,$this);
		$rest = $rest->getResponse();
		if ($rest->error === false && $rest->code !== 200)
			$rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status');
		if ($rest->error !== false)
		{
            $this -> __triggerError(sprintf("S3::listBuckets(): [%s] %s", $rest->error['code'],
			$rest->error['message']), __FILE__, __LINE__);
			return false;
		}
		$results = array();
		if (!isset($rest->body->Buckets)) return $results;

		if ($detailed)
		{
			if (isset($rest->body->Owner, $rest->body->Owner->ID, $rest->body->Owner->DisplayName))
			$results['owner'] = array(
				'id' => (string)$rest->body->Owner->ID, 'name' => (string)$rest->body->Owner->DisplayName
			);
			$results['buckets'] = array();
			foreach ($rest->body->Buckets->Bucket as $b)
				$results['buckets'][] = array(
					'name' => (string)$b->Name, 'time' => strtotime((string)$b->CreationDate)
				);
		} else
			foreach ($rest->body->Buckets->Bucket as $b) $results[] = (string)$b->Name;

		return $results;
	}


	/**
	* Get contents for a bucket
	*
	* If maxKeys is null this method will loop through truncated result sets
	*
	* @param string $bucket Bucket name
	* @param string $prefix Prefix
	* @param string $marker Marker (last file listed)
	* @param string $maxKeys Max keys (maximum number of keys to return)
	* @param string $delimiter Delimiter
	* @param boolean $returnCommonPrefixes Set to true to return CommonPrefixes
	* @return array | false
	*/
	public function getBucket($bucket, $prefix = null, $marker = null, $maxKeys = null, $delimiter = null, $returnCommonPrefixes = false)
	{
		$rest = new WPvivid_S3Request('GET', $bucket, '', $this -> endpoint,$this);
		if ($maxKeys == 0) $maxKeys = null;
		if ($prefix !== null && $prefix !== '') $rest->setParameter('prefix', $prefix);
		if ($marker !== null && $marker !== '') $rest->setParameter('marker', $marker);
		if ($maxKeys !== null && $maxKeys !== '') $rest->setParameter('max-keys', $maxKeys);
		if ($delimiter !== null && $delimiter !== '') $rest->setParameter('delimiter', $delimiter);
		else if (!empty($this -> defDelimiter)) $rest->setParameter('delimiter', $this -> defDelimiter);
		$response = $rest->getResponse();
		if ($response->error === false && $response->code !== 200)
			$response->error = array('code' => $response->code, 'message' => 'Unexpected HTTP status');
		if ($response->error !== false)
		{
            $this -> __triggerError(sprintf("S3::getBucket(): [%s] %s",
			$response->error['code'], $response->error['message']), __FILE__, __LINE__);
			return false;
		}

		$results = array();

		$nextMarker = null;
		if (isset($response->body, $response->body->Contents))
		foreach ($response->body->Contents as $c)
		{
			$results[(string)$c->Key] = array(
				'name' => (string)$c->Key,
				'time' => strtotime((string)$c->LastModified),
				'size' => (int)$c->Size,
				'hash' => substr((string)$c->ETag, 1, -1)
			);
			$nextMarker = (string)$c->Key;
		}

		if ($returnCommonPrefixes && isset($response->body, $response->body->CommonPrefixes))
			foreach ($response->body->CommonPrefixes as $c)
				$results[(string)$c->Prefix] = array('prefix' => (string)$c->Prefix);

		if (isset($response->body, $response->body->IsTruncated) &&
		(string)$response->body->IsTruncated == 'false') return $results;

		if (isset($response->body, $response->body->NextMarker))
			$nextMarker = (string)$response->body->NextMarker;

		// Loop through truncated results if maxKeys isn't specified
		if ($maxKeys == null && $nextMarker !== null && (string)$response->body->IsTruncated == 'true')
		do
		{
			$rest = new WPvivid_S3Request('GET', $bucket, '', $this -> endpoint,$this);
			if ($prefix !== null && $prefix !== '') $rest->setParameter('prefix', $prefix);
			$rest->setParameter('marker', $nextMarker);
			if ($delimiter !== null && $delimiter !== '') $rest->setParameter('delimiter', $delimiter);

			if (($response = $rest->getResponse()) == false || $response->code !== 200) break;

			if (isset($response->body, $response->body->Contents))
			foreach ($response->body->Contents as $c)
			{
				$results[(string)$c->Key] = array(
					'name' => (string)$c->Key,
					'time' => strtotime((string)$c->LastModified),
					'size' => (int)$c->Size,
					'hash' => substr((string)$c->ETag, 1, -1)
				);
				$nextMarker = (string)$c->Key;
			}

			if ($returnCommonPrefixes && isset($response->body, $response->body->CommonPrefixes))
				foreach ($response->body->CommonPrefixes as $c)
					$results[(string)$c->Prefix] = array('prefix' => (string)$c->Prefix);

			if (isset($response->body, $response->body->NextMarker))
				$nextMarker = (string)$response->body->NextMarker;

		} while ($response !== false && (string)$response->body->IsTruncated == 'true');

		return $results;
	}


	/**
	* Put a bucket
	*
	* @param string $bucket Bucket name
	* @param constant $acl ACL flag
	* @param string $location Set as "EU" to create buckets hosted in Europe
	* @return boolean
	*/
	public function putBucket($bucket, $acl = self::ACL_PRIVATE, $location = false)
	{
		$rest = new WPvivid_S3Request('PUT', $bucket, '', $this -> endpoint,$this);
		$rest->setAmzHeader('x-amz-acl', $acl);

		if ($location !== false)
		{
			$dom = new DOMDocument;
			$createBucketConfiguration = $dom->createElement('CreateBucketConfiguration');
			$locationConstraint = $dom->createElement('LocationConstraint', $location);
			$createBucketConfiguration->appendChild($locationConstraint);
			$dom->appendChild($createBucketConfiguration);
			$rest->data = $dom->saveXML();
			$rest->size = strlen($rest->data);
			$rest->setHeader('Content-Type', 'application/xml');
		}
		$rest = $rest->getResponse();

		if ($rest->error === false && $rest->code !== 200)
			$rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status');
		if ($rest->error !== false)
		{
            $this -> __triggerError(sprintf("S3::putBucket({$bucket}, {$acl}, {$location}): [%s] %s",
			$rest->error['code'], $rest->error['message']), __FILE__, __LINE__);
			return false;
		}
		return true;
	}


	/**
	* Delete an empty bucket
	*
	* @param string $bucket Bucket name
	* @return boolean
	*/
	public function deleteBucket($bucket)
	{
		$rest = new WPvivid_S3Request('DELETE', $bucket, '', $this -> endpoint,$this);
		$rest = $rest->getResponse();
		if ($rest->error === false && $rest->code !== 204)
			$rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status');
		if ($rest->error !== false)
		{
            $this -> __triggerError(sprintf("S3::deleteBucket({$bucket}): [%s] %s",
			$rest->error['code'], $rest->error['message']), __FILE__, __LINE__);
			return false;
		}
		return true;
	}


	/**
	* Create input info array for putObject()
	*
	* @param string $file Input file
	* @param mixed $md5sum Use MD5 hash (supply a string if you want to use your own)
	* @return array | false
	*/
	public function inputFile($file, $md5sum = true)
	{
		if (!file_exists($file) || !is_file($file) || !is_readable($file))
		{
            $this -> __triggerError('S3::inputFile(): Unable to open input file: '.$file, __FILE__, __LINE__);
			return false;
		}
		clearstatcache(false, $file);
		return array('file' => $file, 'size' => filesize($file), 'md5sum' => $md5sum !== false ?
		(is_string($md5sum) ? $md5sum : base64_encode(md5_file($file, true))) : '');
	}


	/**
	* Create input array info for putObject() with a resource
	*
	* @param string $resource Input resource to read from
	* @param integer $bufferSize Input byte size
	* @param string $md5sum MD5 hash to send (optional)
	* @return array | false
	*/
	public function inputResource(&$resource, $bufferSize = false, $md5sum = '')
	{
		if (!is_resource($resource) || (int)$bufferSize < 0)
		{
            $this -> __triggerError('S3::inputResource(): Invalid resource or buffer size', __FILE__, __LINE__);
			return false;
		}

		// Try to figure out the bytesize
		if ($bufferSize === false)
		{
			if (fseek($resource, 0, SEEK_END) < 0 || ($bufferSize = ftell($resource)) === false)
			{
				$this -> __triggerError('S3::inputResource(): Unable to obtain resource size', __FILE__, __LINE__);
				return false;
			}
			fseek($resource, 0);
		}

		$input = array('size' => $bufferSize, 'md5sum' => $md5sum);
		$input['fp'] =& $resource;
		return $input;
	}


	/**
	* Put an object
	*
	* @param mixed $input Input data
	* @param string $bucket Bucket name
	* @param string $uri Object URI
	* @param constant $acl ACL constant
	* @param array $metaHeaders Array of x-amz-meta-* headers
	* @param array $requestHeaders Array of request headers or content type as a string
	* @param constant $storageClass Storage class constant
	* @param constant $serverSideEncryption Server-side encryption
	* @return boolean
	*/
	public function putObject($input, $bucket, $uri, $acl = self::ACL_PRIVATE, $metaHeaders = array(), $requestHeaders = array(), $storageClass = self::STORAGE_CLASS_STANDARD, $serverSideEncryption = self::SSE_NONE)
	{
		if ($input === false) return false;
		$rest = new WPvivid_S3Request('PUT', $bucket, $uri, $this -> endpoint,$this);

		if (!is_array($input)) $input = array(
			'data' => $input, 'size' => strlen($input),
			'md5sum' => base64_encode(md5($input, true))
		);

		// Data
		if (isset($input['fp']))
			$rest->fp =& $input['fp'];
		elseif (isset($input['file']))
			$rest->fp = @fopen($input['file'], 'rb');
		elseif (isset($input['data']))
			$rest->data = $input['data'];

		// Content-Length (required)
		if (isset($input['size']) && $input['size'] >= 0)
			$rest->size = $input['size'];
		else {
			if (isset($input['file'])) {
				clearstatcache(false, $input['file']);
				$rest->size = filesize($input['file']);
			}
			elseif (isset($input['data']))
				$rest->size = strlen($input['data']);
		}

		// Custom request headers (Content-Type, Content-Disposition, Content-Encoding)
		if (is_array($requestHeaders))
			foreach ($requestHeaders as $h => $v)
				strpos($h, 'x-amz-') === 0 ? $rest->setAmzHeader($h, $v) : $rest->setHeader($h, $v);
		elseif (is_string($requestHeaders)) // Support for legacy contentType parameter
			$input['type'] = $requestHeaders;

		// Content-Type
		if (!isset($input['type']))
		{
			if (isset($requestHeaders['Content-Type']))
				$input['type'] =& $requestHeaders['Content-Type'];
			elseif (isset($input['file']))
				$input['type'] = $this -> __getMIMEType($input['file']);
			else
				$input['type'] = 'application/octet-stream';
		}

		if ($this -> _storageClass !== self::STORAGE_CLASS_STANDARD) // Storage class
			$rest->setAmzHeader('x-amz-storage-class', $this -> _storageClass);

		if ($this -> _serverSideEncryption !== self::SSE_NONE) // Server-side encryption
			$rest->setAmzHeader('x-amz-server-side-encryption', $this -> _serverSideEncryption);

		// We need to post with Content-Length and Content-Type, MD5 is optional
		if ($rest->size >= 0 && ($rest->fp !== false || $rest->data !== false))
		{
			$rest->setHeader('Content-Type', $input['type']);
			if (isset($input['md5sum'])) $rest->setHeader('Content-MD5', $input['md5sum']);

			$rest->setAmzHeader('x-amz-acl', $acl);
			foreach ($metaHeaders as $h => $v) $rest->setAmzHeader('x-amz-meta-'.$h, $v);
			$rest->getResponse();
		} else
			$rest->response->error = array('code' => 0, 'message' => 'Missing input parameters');

		if ($rest->response->error === false && $rest->response->code !== 200)
			$rest->response->error = array('code' => $rest->response->code, 'message' => 'Unexpected HTTP status');
		if ($rest->response->error !== false)
		{
			$this -> __triggerError(sprintf("S3::putObject(): [%s] %s",
			$rest->response->error['code'], $rest->response->error['message']), __FILE__, __LINE__);
			return false;
		}
		return true;
	}


	/**
	* Put an object from a file (legacy function)
	*
	* @param string $file Input file path
	* @param string $bucket Bucket name
	* @param string $uri Object URI
	* @param constant $acl ACL constant
	* @param array $metaHeaders Array of x-amz-meta-* headers
	* @param string $contentType Content type
	* @return boolean
	*/
	public function putObjectFile($file, $bucket, $uri, $acl = self::ACL_PRIVATE, $metaHeaders = array(), $contentType = null)
	{
		return $this -> putObject($this -> inputFile($file), $bucket, $uri, $acl, $metaHeaders, $contentType);
	}


	/**
	* Put an object from a string (legacy function)
	*
	* @param string $string Input data
	* @param string $bucket Bucket name
	* @param string $uri Object URI
	* @param constant $acl ACL constant
	* @param array $metaHeaders Array of x-amz-meta-* headers
	* @param string $contentType Content type
	* @return boolean
	*/
	public function putObjectString($string, $bucket, $uri, $acl = self::ACL_PRIVATE, $metaHeaders = array(), $contentType = 'text/plain')
	{
		return $this -> putObject($string, $bucket, $uri, $acl, $metaHeaders, $contentType);
	}


	/**
	* Get an object
	*
	* @param string $bucket Bucket name
	* @param string $uri Object URI
	* @param mixed $saveTo Filename or resource to write to
	* @return mixed
	*/
	public function getObject($bucket, $uri, $saveTo = false)
	{
		$rest = new WPvivid_S3Request('GET', $bucket, $uri, $this -> endpoint,$this);
		if ($saveTo !== false)
		{
			if (is_resource($saveTo))
				$rest->fp =& $saveTo;
			else
				if (($rest->fp = @fopen($saveTo, 'wb')) !== false)
					$rest->file = realpath($saveTo);
				else
					$rest->response->error = array('code' => 0, 'message' => 'Unable to open save file for writing: '.$saveTo);
		}
		if ($rest->response->error === false) $rest->getResponse();

		if ($rest->response->error === false && $rest->response->code !== 200)
			$rest->response->error = array('code' => $rest->response->code, 'message' => 'Unexpected HTTP status');
		if ($rest->response->error !== false)
		{
			$this -> __triggerError(sprintf("S3::getObject({$bucket}, {$uri}): [%s] %s",
			$rest->response->error['code'], $rest->response->error['message']), __FILE__, __LINE__);
			return false;
		}
		return $rest->response;
	}


	/**
	* Get object information
	*
	* @param string $bucket Bucket name
	* @param string $uri Object URI
	* @param boolean $returnInfo Return response information
	* @return mixed | false
	*/
	public function getObjectInfo($bucket, $uri, $returnInfo = true)
	{
		$rest = new WPvivid_S3Request('HEAD', $bucket, $uri, $this -> endpoint,$this);
		$rest = $rest->getResponse();
		if ($rest->error === false && ($rest->code !== 200 && $rest->code !== 404))
			$rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status');
		if ($rest->error !== false)
		{
			$this -> __triggerError(sprintf("S3::getObjectInfo({$bucket}, {$uri}): [%s] %s",
			$rest->error['code'], $rest->error['message']), __FILE__, __LINE__);
			return false;
		}
		return $rest->code == 200 ? $returnInfo ? $rest->headers : true : false;
	}


	/**
	* Copy an object
	*
	* @param string $srcBucket Source bucket name
	* @param string $srcUri Source object URI
	* @param string $bucket Destination bucket name
	* @param string $uri Destination object URI
	* @param constant $acl ACL constant
	* @param array $metaHeaders Optional array of x-amz-meta-* headers
	* @param array $requestHeaders Optional array of request headers (content type, disposition, etc.)
	* @param constant $storageClass Storage class constant
	* @return mixed | false
	*/
	public function copyObject($srcBucket, $srcUri, $bucket, $uri, $acl = self::ACL_PRIVATE, $metaHeaders = array(), $requestHeaders = array(), $storageClass = self::STORAGE_CLASS_STANDARD)
	{
		$rest = new WPvivid_S3Request('PUT', $bucket, $uri, $this -> endpoint,$this);
		$rest->setHeader('Content-Length', 0);
		foreach ($requestHeaders as $h => $v)
				strpos($h, 'x-amz-') === 0 ? $rest->setAmzHeader($h, $v) : $rest->setHeader($h, $v);
		foreach ($metaHeaders as $h => $v) $rest->setAmzHeader('x-amz-meta-'.$h, $v);
		if ($storageClass !== self::STORAGE_CLASS_STANDARD) // Storage class
			$rest->setAmzHeader('x-amz-storage-class', $storageClass);
		$rest->setAmzHeader('x-amz-acl', $acl);
		$rest->setAmzHeader('x-amz-copy-source', sprintf('/%s/%s', $srcBucket, rawurlencode($srcUri)));
		if (sizeof($requestHeaders) > 0 || sizeof($metaHeaders) > 0)
			$rest->setAmzHeader('x-amz-metadata-directive', 'REPLACE');

		$rest = $rest->getResponse();
		if ($rest->error === false && $rest->code !== 200)
			$rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status');
		if ($rest->error !== false)
		{
			$this -> __triggerError(sprintf("S3::copyObject({$srcBucket}, {$srcUri}, {$bucket}, {$uri}): [%s] %s",
			$rest->error['code'], $rest->error['message']), __FILE__, __LINE__);
			return false;
		}
		return isset($rest->body->LastModified, $rest->body->ETag) ? array(
			'time' => strtotime((string)$rest->body->LastModified),
			'hash' => substr((string)$rest->body->ETag, 1, -1)
		) : false;
	}


	/**
	* Set up a bucket redirection
	*
	* @param string $bucket Bucket name
	* @param string $location Target host name
	* @return boolean
	*/
	public function setBucketRedirect($bucket = NULL, $location = NULL)
	{
		$rest = new WPvivid_S3Request('PUT', $bucket, '', $this -> endpoint,$this);

		if( empty($bucket) || empty($location) ) {
			$this -> __triggerError("S3::setBucketRedirect({$bucket}, {$location}): Empty parameter.", __FILE__, __LINE__);
			return false;
		}

		$dom = new DOMDocument;
		$websiteConfiguration = $dom->createElement('WebsiteConfiguration');
		$redirectAllRequestsTo = $dom->createElement('RedirectAllRequestsTo');
		$hostName = $dom->createElement('HostName', $location);
		$redirectAllRequestsTo->appendChild($hostName);
		$websiteConfiguration->appendChild($redirectAllRequestsTo);
		$dom->appendChild($websiteConfiguration);
		$rest->setParameter('website', null);
		$rest->data = $dom->saveXML();
		$rest->size = strlen($rest->data);
		$rest->setHeader('Content-Type', 'application/xml');
		$rest = $rest->getResponse();

		if ($rest->error === false && $rest->code !== 200)
			$rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status');
		if ($rest->error !== false)
		{
			$this -> __triggerError(sprintf("S3::setBucketRedirect({$bucket}, {$location}): [%s] %s",
			$rest->error['code'], $rest->error['message']), __FILE__, __LINE__);
			return false;
		}
		return true;
	}


	/**
	* Set logging for a bucket
	*
	* @param string $bucket Bucket name
	* @param string $targetBucket Target bucket (where logs are stored)
	* @param string $targetPrefix Log prefix (e,g; domain.com-)
	* @return boolean
	*/
	public function setBucketLogging($bucket, $targetBucket, $targetPrefix = null)
	{
		// The S3 log delivery group has to be added to the target bucket's ACP
		if ($targetBucket !== null && ($acp = $this -> getAccessControlPolicy($targetBucket, '')) !== false)
		{
			// Only add permissions to the target bucket when they do not exist
			$aclWriteSet = false;
			$aclReadSet = false;
			foreach ($acp['acl'] as $acl)
			if ($acl['type'] == 'Group' && $acl['uri'] == 'http://acs.amazonaws.com/groups/s3/LogDelivery')
			{
				if ($acl['permission'] == 'WRITE') $aclWriteSet = true;
				elseif ($acl['permission'] == 'READ_ACP') $aclReadSet = true;
			}
			if (!$aclWriteSet) $acp['acl'][] = array(
				'type' => 'Group', 'uri' => 'http://acs.amazonaws.com/groups/s3/LogDelivery', 'permission' => 'WRITE'
			);
			if (!$aclReadSet) $acp['acl'][] = array(
				'type' => 'Group', 'uri' => 'http://acs.amazonaws.com/groups/s3/LogDelivery', 'permission' => 'READ_ACP'
			);
			if (!$aclReadSet || !$aclWriteSet) $this -> setAccessControlPolicy($targetBucket, '', $acp);
		}

		$dom = new DOMDocument;
		$bucketLoggingStatus = $dom->createElement('BucketLoggingStatus');
		$bucketLoggingStatus->setAttribute('xmlns', 'http://s3.amazonaws.com/doc/2006-03-01/');
		if ($targetBucket !== null)
		{
			if ($targetPrefix == null) $targetPrefix = $bucket . '-';
			$loggingEnabled = $dom->createElement('LoggingEnabled');
			$loggingEnabled->appendChild($dom->createElement('TargetBucket', $targetBucket));
			$loggingEnabled->appendChild($dom->createElement('TargetPrefix', $targetPrefix));
			// TODO: Add TargetGrants?
			$bucketLoggingStatus->appendChild($loggingEnabled);
		}
		$dom->appendChild($bucketLoggingStatus);

		$rest = new WPvivid_S3Request('PUT', $bucket, '', $this -> endpoint,$this);
		$rest->setParameter('logging', null);
		$rest->data = $dom->saveXML();
		$rest->size = strlen($rest->data);
		$rest->setHeader('Content-Type', 'application/xml');
		$rest = $rest->getResponse();
		if ($rest->error === false && $rest->code !== 200)
			$rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status');
		if ($rest->error !== false)
		{
			$this -> __triggerError(sprintf("S3::setBucketLogging({$bucket}, {$targetBucket}): [%s] %s",
			$rest->error['code'], $rest->error['message']), __FILE__, __LINE__);
			return false;
		}
		return true;
	}


	/**
	* Get logging status for a bucket
	*
	* This will return false if logging is not enabled.
	* Note: To enable logging, you also need to grant write access to the log group
	*
	* @param string $bucket Bucket name
	* @return array | false
	*/
	public function getBucketLogging($bucket)
	{
		$rest = new WPvivid_S3Request('GET', $bucket, '', $this -> endpoint,$this);
		$rest->setParameter('logging', null);
		$rest = $rest->getResponse();
		if ($rest->error === false && $rest->code !== 200)
			$rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status');
		if ($rest->error !== false)
		{
			$this -> __triggerError(sprintf("S3::getBucketLogging({$bucket}): [%s] %s",
			$rest->error['code'], $rest->error['message']), __FILE__, __LINE__);
			return false;
		}
		if (!isset($rest->body->LoggingEnabled)) return false; // No logging
		return array(
			'targetBucket' => (string)$rest->body->LoggingEnabled->TargetBucket,
			'targetPrefix' => (string)$rest->body->LoggingEnabled->TargetPrefix,
		);
	}


	/**
	* Disable bucket logging
	*
	* @param string $bucket Bucket name
	* @return boolean
	*/
	public function disableBucketLogging($bucket)
	{
		return $this -> setBucketLogging($bucket, null);
	}


	/**
	* Get a bucket's location
	*
	* @param string $bucket Bucket name
	* @return string | false
	*/
	public function getBucketLocation($bucket)
	{
		$rest = new WPvivid_S3Request('GET', $bucket, '', $this -> endpoint,$this);
		$rest->setParameter('location', null);
		$rest = $rest->getResponse();
		if ($rest->error === false && $rest->code !== 200)
			$rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status');
		if ($rest->error !== false)
		{
			$this -> __triggerError(sprintf("S3::getBucketLocation({$bucket}): [%s] %s",
			$rest->error['code'], $rest->error['message']), __FILE__, __LINE__);
			return false;
		}
		return (isset($rest->body[0]) && (string)$rest->body[0] !== '') ? (string)$rest->body[0] : 'us-east-1';
	}


	/**
	* Set object or bucket Access Control Policy
	*
	* @param string $bucket Bucket name
	* @param string $uri Object URI
	* @param array $acp Access Control Policy Data (same as the data returned from getAccessControlPolicy)
	* @return boolean
	*/
	public function setAccessControlPolicy($bucket, $uri = '', $acp = array())
	{
		$dom = new DOMDocument;
		$dom->formatOutput = true;
		$accessControlPolicy = $dom->createElement('AccessControlPolicy');
		$accessControlList = $dom->createElement('AccessControlList');

		// It seems the owner has to be passed along too
		$owner = $dom->createElement('Owner');
		$owner->appendChild($dom->createElement('ID', $acp['owner']['id']));
		$owner->appendChild($dom->createElement('DisplayName', $acp['owner']['name']));
		$accessControlPolicy->appendChild($owner);

		foreach ($acp['acl'] as $g)
		{
			$grant = $dom->createElement('Grant');
			$grantee = $dom->createElement('Grantee');
			$grantee->setAttribute('xmlns:xsi', 'http://www.w3.org/2001/XMLSchema-instance');
			if (isset($g['id']))
			{ // CanonicalUser (DisplayName is omitted)
				$grantee->setAttribute('xsi:type', 'CanonicalUser');
				$grantee->appendChild($dom->createElement('ID', $g['id']));
			}
			elseif (isset($g['email']))
			{ // AmazonCustomerByEmail
				$grantee->setAttribute('xsi:type', 'AmazonCustomerByEmail');
				$grantee->appendChild($dom->createElement('EmailAddress', $g['email']));
			}
			elseif ($g['type'] == 'Group')
			{ // Group
				$grantee->setAttribute('xsi:type', 'Group');
				$grantee->appendChild($dom->createElement('URI', $g['uri']));
			}
			$grant->appendChild($grantee);
			$grant->appendChild($dom->createElement('Permission', $g['permission']));
			$accessControlList->appendChild($grant);
		}

		$accessControlPolicy->appendChild($accessControlList);
		$dom->appendChild($accessControlPolicy);

		$rest = new WPvivid_S3Request('PUT', $bucket, $uri, $this -> endpoint,$this);
		$rest->setParameter('acl', null);
		$rest->data = $dom->saveXML();
		$rest->size = strlen($rest->data);
		$rest->setHeader('Content-Type', 'application/xml');
		$rest = $rest->getResponse();
		if ($rest->error === false && $rest->code !== 200)
			$rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status');
		if ($rest->error !== false)
		{
			$this -> __triggerError(sprintf("S3::setAccessControlPolicy({$bucket}, {$uri}): [%s] %s",
			$rest->error['code'], $rest->error['message']), __FILE__, __LINE__);
			return false;
		}
		return true;
	}


	/**
	* Get object or bucket Access Control Policy
	*
	* @param string $bucket Bucket name
	* @param string $uri Object URI
	* @return mixed | false
	*/
	public function getAccessControlPolicy($bucket, $uri = '')
	{
		$rest = new WPvivid_S3Request('GET', $bucket, $uri, $this -> endpoint,$this);
		$rest->setParameter('acl', null);
		$rest = $rest->getResponse();
		if ($rest->error === false && $rest->code !== 200)
			$rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status');
		if ($rest->error !== false)
		{
			$this -> __triggerError(sprintf("S3::getAccessControlPolicy({$bucket}, {$uri}): [%s] %s",
			$rest->error['code'], $rest->error['message']), __FILE__, __LINE__);
			return false;
		}

		$acp = array();
		if (isset($rest->body->Owner, $rest->body->Owner->ID, $rest->body->Owner->DisplayName))
			$acp['owner'] = array(
				'id' => (string)$rest->body->Owner->ID, 'name' => (string)$rest->body->Owner->DisplayName
			);

		if (isset($rest->body->AccessControlList))
		{
			$acp['acl'] = array();
			foreach ($rest->body->AccessControlList->Grant as $grant)
			{
				foreach ($grant->Grantee as $grantee)
				{
					if (isset($grantee->ID, $grantee->DisplayName)) // CanonicalUser
						$acp['acl'][] = array(
							'type' => 'CanonicalUser',
							'id' => (string)$grantee->ID,
							'name' => (string)$grantee->DisplayName,
							'permission' => (string)$grant->Permission
						);
					elseif (isset($grantee->EmailAddress)) // AmazonCustomerByEmail
						$acp['acl'][] = array(
							'type' => 'AmazonCustomerByEmail',
							'email' => (string)$grantee->EmailAddress,
							'permission' => (string)$grant->Permission
						);
					elseif (isset($grantee->URI)) // Group
						$acp['acl'][] = array(
							'type' => 'Group',
							'uri' => (string)$grantee->URI,
							'permission' => (string)$grant->Permission
						);
					else continue;
				}
			}
		}
		return $acp;
	}


	/**
	* Delete an object
	*
	* @param string $bucket Bucket name
	* @param string $uri Object URI
	* @return boolean
	*/
	public function deleteObject($bucket, $uri)
	{
		$rest = new WPvivid_S3Request('DELETE', $bucket, $uri, $this -> endpoint,$this);
		$rest = $rest->getResponse();
		if ($rest->error === false && $rest->code !== 204)
			$rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status');
		if ($rest->error !== false)
		{
			$this -> __triggerError(sprintf("S3::deleteObject(): [%s] %s",
			$rest->error['code'], $rest->error['message']), __FILE__, __LINE__);
			return false;
		}
		return true;
	}


	/**
	* Get a query string authenticated URL
	*
	* @param string $bucket Bucket name
	* @param string $uri Object URI
	* @param integer $lifetime Lifetime in seconds
	* @param boolean $hostBucket Use the bucket name as the hostname
	* @param boolean $https Use HTTPS ($hostBucket should be false for SSL verification)
	* @return string
	*/
	public function getAuthenticatedURL($bucket, $uri, $lifetime, $hostBucket = false, $https = false)
	{
		$expires = $this -> __getTime() + $lifetime;
		$uri = str_replace(array('%2F', '%2B'), array('/', '+'), rawurlencode($uri));
		return sprintf(($https ? 'https' : 'http').'://%s/%s?AWSAccessKeyId=%s&Expires=%u&Signature=%s',
		// $hostBucket ? $bucket : $bucket.'.s3.amazonaws.com', $uri, self::$__accessKey, $expires,
		$hostBucket ? $bucket : $this -> endpoint.'/'.$bucket, $uri, $this -> __accessKey, $expires,
		urlencode($this -> __getHash("GET\n\n\n{$expires}\n/{$bucket}/{$uri}")));
	}


	/**
	* Get a CloudFront signed policy URL
	*
	* @param array $policy Policy
	* @return string
	*/
	public function getSignedPolicyURL($policy)
	{
		$data = wp_json_encode($policy);
		$signature = '';
		if (!openssl_sign($data, $signature, $this -> __signingKeyResource)) return false;

		$encoded = str_replace(array('+', '='), array('-', '_', '~'), base64_encode($data));
		$signature = str_replace(array('+', '='), array('-', '_', '~'), base64_encode($signature));

		$url = $policy['Statement'][0]['Resource'] . '?';
		foreach (array('Policy' => $encoded, 'Signature' => $signature, 'Key-Pair-Id' => $this -> __signingKeyPairId) as $k => $v)
			$url .= $k.'='.str_replace('%2F', '/', rawurlencode($v)).'&';
		return substr($url, 0, -1);
	}


	/**
	* Get a CloudFront canned policy URL
	*
	* @param string $url URL to sign
	* @param integer $lifetime URL lifetime
	* @return string
	*/
	public function getSignedCannedURL($url, $lifetime)
	{
		return $this -> getSignedPolicyURL(array(
			'Statement' => array(
				array('Resource' => $url, 'Condition' => array(
					'DateLessThan' => array('AWS:EpochTime' => $this -> __getTime() + $lifetime)
				))
			)
		));
	}


	/**
	* Get upload POST parameters for form uploads
	*
	* @param string $bucket Bucket name
	* @param string $uriPrefix Object URI prefix
	* @param constant $acl ACL constant
	* @param integer $lifetime Lifetime in seconds
	* @param integer $maxFileSize Maximum filesize in bytes (default 5MB)
	* @param string $successRedirect Redirect URL or 200 / 201 status code
	* @param array $amzHeaders Array of x-amz-meta-* headers
	* @param array $headers Array of request headers or content type as a string
	* @param boolean $flashVars Includes additional "Filename" variable posted by Flash
	* @return object
	*/
	public function getHttpUploadPostParams($bucket, $uriPrefix = '', $acl = self::ACL_PRIVATE, $lifetime = 3600,
	$maxFileSize = 5242880, $successRedirect = "201", $amzHeaders = array(), $headers = array(), $flashVars = false)
	{
		// Create policy object
		$policy = new stdClass;
		$policy->expiration = gmdate('Y-m-d\TH:i:s\Z', ($this -> __getTime() + $lifetime));
		$policy->conditions = array();
		$obj = new stdClass; $obj->bucket = $bucket; array_push($policy->conditions, $obj);
		$obj = new stdClass; $obj->acl = $acl; array_push($policy->conditions, $obj);

		$obj = new stdClass; // 200 for non-redirect uploads
		if (is_numeric($successRedirect) && in_array((int)$successRedirect, array(200, 201)))
			$obj->success_action_status = (string)$successRedirect;
		else // URL
			$obj->success_action_redirect = $successRedirect;
		array_push($policy->conditions, $obj);

		if ($acl !== self::ACL_PUBLIC_READ)
			array_push($policy->conditions, array('eq', '$acl', $acl));

		array_push($policy->conditions, array('starts-with', '$key', $uriPrefix));
		if ($flashVars) array_push($policy->conditions, array('starts-with', '$Filename', ''));
		foreach (array_keys($headers) as $headerKey)
			array_push($policy->conditions, array('starts-with', '$'.$headerKey, ''));
		foreach ($amzHeaders as $headerKey => $headerVal)
		{
			$obj = new stdClass;
			$obj->{$headerKey} = (string)$headerVal;
			array_push($policy->conditions, $obj);
		}
		array_push($policy->conditions, array('content-length-range', 0, $maxFileSize));
		$policy = base64_encode(str_replace('\/', '/', wp_json_encode($policy)));

		// Create parameters
		$params = new stdClass;
		$params->AWSAccessKeyId = $this -> __accessKey;
		$params->key = $uriPrefix.'${filename}';
		$params->acl = $acl;
		$params->policy = $policy; unset($policy);
		$params->signature = $this -> __getHash($params->policy);
		if (is_numeric($successRedirect) && in_array((int)$successRedirect, array(200, 201)))
			$params->success_action_status = (string)$successRedirect;
		else
			$params->success_action_redirect = $successRedirect;
		foreach ($headers as $headerKey => $headerVal) $params->{$headerKey} = (string)$headerVal;
		foreach ($amzHeaders as $headerKey => $headerVal) $params->{$headerKey} = (string)$headerVal;
		return $params;
	}


	/**
	* Create a CloudFront distribution
	*
	* @param string $bucket Bucket name
	* @param boolean $enabled Enabled (true/false)
	* @param array $cnames Array containing CNAME aliases
	* @param string $comment Use the bucket name as the hostname
	* @param string $defaultRootObject Default root object
	* @param string $originAccessIdentity Origin access identity
	* @param array $trustedSigners Array of trusted signers
	* @return array | false
	*/
	public function createDistribution($bucket, $enabled = true, $cnames = array(), $comment = null, $defaultRootObject = null, $originAccessIdentity = null, $trustedSigners = array())
	{
		if (!extension_loaded('openssl'))
		{
			$this -> __triggerError(sprintf("S3::createDistribution({$bucket}, ".(int)$enabled.", [], '$comment'): %s",
			"CloudFront functionality requires SSL"), __FILE__, __LINE__);
			return false;
		}
		$useSSL = $this -> useSSL;

        $this -> useSSL = true; // CloudFront requires SSL
		$rest = new WPvivid_S3Request('POST', '', '2010-11-01/distribution', 'cloudfront.amazonaws.com',$this);
		$rest->data = $this -> __getCloudFrontDistributionConfigXML(
			$bucket.'.s3.amazonaws.com',
			$enabled,
			(string)$comment,
			(string)microtime(true),
			$cnames,
			$defaultRootObject,
			$originAccessIdentity,
			$trustedSigners
		);

		$rest->size = strlen($rest->data);
		$rest->setHeader('Content-Type', 'application/xml');
		$rest = $this -> __getCloudFrontResponse($rest);

        $this -> useSSL = $useSSL;

		if ($rest->error === false && $rest->code !== 201)
			$rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status');
		if ($rest->error !== false)
		{
			$this -> __triggerError(sprintf("S3::createDistribution({$bucket}, ".(int)$enabled.", [], '$comment'): [%s] %s",
			$rest->error['code'], $rest->error['message']), __FILE__, __LINE__);
			return false;
		} elseif ($rest->body instanceof SimpleXMLElement)
			return $this -> __parseCloudFrontDistributionConfig($rest->body);
		return false;
	}


	/**
	* Get CloudFront distribution info
	*
	* @param string $distributionId Distribution ID from listDistributions()
	* @return array | false
	*/
	public function getDistribution($distributionId)
	{
		if (!extension_loaded('openssl'))
		{
			$this -> __triggerError(sprintf("S3::getDistribution($distributionId): %s",
			"CloudFront functionality requires SSL"), __FILE__, __LINE__);
			return false;
		}
		$useSSL = $this -> useSSL;

        $this -> useSSL = true; // CloudFront requires SSL
		$rest = new WPvivid_S3Request('GET', '', '2010-11-01/distribution/'.$distributionId, 'cloudfront.amazonaws.com',$this);
		$rest = $this -> __getCloudFrontResponse($rest);

        $this -> useSSL = $useSSL;

		if ($rest->error === false && $rest->code !== 200)
			$rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status');
		if ($rest->error !== false)
		{
			$this -> __triggerError(sprintf("S3::getDistribution($distributionId): [%s] %s",
			$rest->error['code'], $rest->error['message']), __FILE__, __LINE__);
			return false;
		}
		elseif ($rest->body instanceof SimpleXMLElement)
		{
			$dist = $this -> __parseCloudFrontDistributionConfig($rest->body);
			$dist['hash'] = $rest->headers['hash'];
			$dist['id'] = $distributionId;
			return $dist;
		}
		return false;
	}


	/**
	* Update a CloudFront distribution
	*
	* @param array $dist Distribution array info identical to output of getDistribution()
	* @return array | false
	*/
	public function updateDistribution($dist)
	{
		if (!extension_loaded('openssl'))
		{
			$this -> __triggerError(sprintf("S3::updateDistribution({$dist['id']}): %s",
			"CloudFront functionality requires SSL"), __FILE__, __LINE__);
			return false;
		}

		$useSSL = $this -> useSSL;

        $this -> useSSL = true; // CloudFront requires SSL
		$rest = new WPvivid_S3Request('PUT', '', '2010-11-01/distribution/'.$dist['id'].'/config', 'cloudfront.amazonaws.com',$this);
		$rest->data = $this -> __getCloudFrontDistributionConfigXML(
			$dist['origin'],
			$dist['enabled'],
			$dist['comment'],
			$dist['callerReference'],
			$dist['cnames'],
			$dist['defaultRootObject'],
			$dist['originAccessIdentity'],
			$dist['trustedSigners']
		);

		$rest->size = strlen($rest->data);
		$rest->setHeader('If-Match', $dist['hash']);
		$rest = $this -> __getCloudFrontResponse($rest);

        $this -> useSSL = $useSSL;

		if ($rest->error === false && $rest->code !== 200)
			$rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status');
		if ($rest->error !== false)
		{
			$this -> __triggerError(sprintf("S3::updateDistribution({$dist['id']}): [%s] %s",
			$rest->error['code'], $rest->error['message']), __FILE__, __LINE__);
			return false;
		} else {
			$dist = $this -> __parseCloudFrontDistributionConfig($rest->body);
			$dist['hash'] = $rest->headers['hash'];
			return $dist;
		}
		return false;
	}


	/**
	* Delete a CloudFront distribution
	*
	* @param array $dist Distribution array info identical to output of getDistribution()
	* @return boolean
	*/
	public function deleteDistribution($dist)
	{
		if (!extension_loaded('openssl'))
		{
			$this -> __triggerError(sprintf("S3::deleteDistribution({$dist['id']}): %s",
			"CloudFront functionality requires SSL"), __FILE__, __LINE__);
			return false;
		}

		$useSSL = $this -> useSSL;

        $this -> useSSL = true; // CloudFront requires SSL
		$rest = new WPvivid_S3Request('DELETE', '', '2008-06-30/distribution/'.$dist['id'], 'cloudfront.amazonaws.com',$this);
		$rest->setHeader('If-Match', $dist['hash']);
		$rest = $this -> __getCloudFrontResponse($rest);

        $this -> useSSL = $useSSL;

		if ($rest->error === false && $rest->code !== 204)
			$rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status');
		if ($rest->error !== false)
		{
			$this -> __triggerError(sprintf("S3::deleteDistribution({$dist['id']}): [%s] %s",
			$rest->error['code'], $rest->error['message']), __FILE__, __LINE__);
			return false;
		}
		return true;
	}


	/**
	* Get a list of CloudFront distributions
	*
	* @return array
	*/
	public function listDistributions()
	{
		if (!extension_loaded('openssl'))
		{
			$this -> __triggerError(sprintf("S3::listDistributions(): [%s] %s",
			"CloudFront functionality requires SSL"), __FILE__, __LINE__);
			return false;
		}

		$useSSL =$this -> useSSL;
		$this -> useSSL = true; // CloudFront requires SSL
		$rest = new WPvivid_S3Request('GET', '', '2010-11-01/distribution', 'cloudfront.amazonaws.com',$this);
		$rest = self::__getCloudFrontResponse($rest);
		$this -> useSSL = $useSSL;

		if ($rest->error === false && $rest->code !== 200)
			$rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status');
		if ($rest->error !== false)
		{
			$this -> __triggerError(sprintf("S3::listDistributions(): [%s] %s",
			$rest->error['code'], $rest->error['message']), __FILE__, __LINE__);
			return false;
		}
		elseif ($rest->body instanceof SimpleXMLElement && isset($rest->body->DistributionSummary))
		{
			$list = array();
			if (isset($rest->body->Marker, $rest->body->MaxItems, $rest->body->IsTruncated))
			{
				//$info['marker'] = (string)$rest->body->Marker;
				//$info['maxItems'] = (int)$rest->body->MaxItems;
				//$info['isTruncated'] = (string)$rest->body->IsTruncated == 'true' ? true : false;
			}
			foreach ($rest->body->DistributionSummary as $summary)
				$list[(string)$summary->Id] = self::__parseCloudFrontDistributionConfig($summary);

			return $list;
		}
		return array();
	}

	/**
	* List CloudFront Origin Access Identities
	*
	* @return array
	*/
	public function listOriginAccessIdentities()
	{
		if (!extension_loaded('openssl'))
		{
			$this -> __triggerError(sprintf("S3::listOriginAccessIdentities(): [%s] %s",
			"CloudFront functionality requires SSL"), __FILE__, __LINE__);
			return false;
		}

		$this -> useSSL = true; // CloudFront requires SSL
		$rest = new WPvivid_S3Request('GET', '', '2010-11-01/origin-access-identity/cloudfront', 'cloudfront.amazonaws.com',$this);
		$rest = self::__getCloudFrontResponse($rest);
		$useSSL = $this -> useSSL;

		if ($rest->error === false && $rest->code !== 200)
			$rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status');
		if ($rest->error !== false)
		{
			trigger_error(sprintf("S3::listOriginAccessIdentities(): [%s] %s",
			esc_attr($rest->error['code']), esc_html($rest->error['message'])), E_USER_WARNING);
			return false;
		}

		if (isset($rest->body->CloudFrontOriginAccessIdentitySummary))
		{
			$identities = array();
			foreach ($rest->body->CloudFrontOriginAccessIdentitySummary as $identity)
				if (isset($identity->S3CanonicalUserId))
					$identities[(string)$identity->Id] = array('id' => (string)$identity->Id, 's3CanonicalUserId' => (string)$identity->S3CanonicalUserId);
			return $identities;
		}
		return false;
	}


	/**
	* Invalidate objects in a CloudFront distribution
	*
	* Thanks to Martin Lindkvist for S3::invalidateDistribution()
	*
	* @param string $distributionId Distribution ID from listDistributions()
	* @param array $paths Array of object paths to invalidate
	* @return boolean
	*/
	public function invalidateDistribution($distributionId, $paths)
	{
		if (!extension_loaded('openssl'))
		{
			$this -> __triggerError(sprintf("S3::invalidateDistribution(): [%s] %s",
			"CloudFront functionality requires SSL"), __FILE__, __LINE__);
			return false;
		}

		$useSSL = $this -> useSSL;
		$this -> useSSL = true; // CloudFront requires SSL
		$rest = new WPvivid_S3Request('POST', '', '2010-08-01/distribution/'.$distributionId.'/invalidation', 'cloudfront.amazonaws.com',$this);
		$rest->data = self::__getCloudFrontInvalidationBatchXML($paths, (string)microtime(true));
		$rest->size = strlen($rest->data);
		$rest = self::__getCloudFrontResponse($rest);
		$this -> useSSL = $useSSL;

		if ($rest->error === false && $rest->code !== 201)
			$rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status');
		if ($rest->error !== false)
		{
			trigger_error(esc_html(sprintf("S3::invalidate('{$distributionId}',{$paths}): [%s] %s",
                $rest->error['code'], $rest->error['message'])), E_USER_WARNING);
			return false;
		}
		return true;
	}


	/**
	* Get a InvalidationBatch DOMDocument
	*
	* @internal Used to create XML in invalidateDistribution()
	* @param array $paths Paths to objects to invalidateDistribution
	* @param int $callerReference
	* @return string
	*/
	private function __getCloudFrontInvalidationBatchXML($paths, $callerReference = '0')
	{
		$dom = new DOMDocument('1.0', 'UTF-8');
		$dom->formatOutput = true;
		$invalidationBatch = $dom->createElement('InvalidationBatch');
		foreach ($paths as $path)
			$invalidationBatch->appendChild($dom->createElement('Path', $path));

		$invalidationBatch->appendChild($dom->createElement('CallerReference', $callerReference));
		$dom->appendChild($invalidationBatch);
		return $dom->saveXML();
	}


	/**
	* List your invalidation batches for invalidateDistribution() in a CloudFront distribution
	*
	* http://docs.amazonwebservices.com/AmazonCloudFront/latest/APIReference/ListInvalidation.html
	* returned array looks like this:
	*	Array
	*	(
	*		[I31TWB0CN9V6XD] => InProgress
	*		[IT3TFE31M0IHZ] => Completed
	*		[I12HK7MPO1UQDA] => Completed
	*		[I1IA7R6JKTC3L2] => Completed
	*	)
	*
	* @param string $distributionId Distribution ID from listDistributions()
	* @return array
	*/
	public function getDistributionInvalidationList($distributionId)
	{
		if (!extension_loaded('openssl'))
		{
			$this -> __triggerError(sprintf("S3::getDistributionInvalidationList(): [%s] %s",
			"CloudFront functionality requires SSL"), __FILE__, __LINE__);
			return false;
		}

		$useSSL = $this -> useSSL;
		$this -> useSSL = true; // CloudFront requires SSL
		$rest = new WPvivid_S3Request('GET', '', '2010-11-01/distribution/'.$distributionId.'/invalidation', 'cloudfront.amazonaws.com',$this);
		$rest = $this -> __getCloudFrontResponse($rest);
		$this -> useSSL = $useSSL;

		if ($rest->error === false && $rest->code !== 200)
			$rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status');
		if ($rest->error !== false)
		{
			trigger_error(esc_html(sprintf("S3::getDistributionInvalidationList('{$distributionId}'): [%s]",
                $rest->error['code'], $rest->error['message'])), E_USER_WARNING);
			return false;
		}
		elseif ($rest->body instanceof SimpleXMLElement && isset($rest->body->InvalidationSummary))
		{
			$list = array();
			foreach ($rest->body->InvalidationSummary as $summary)
				$list[(string)$summary->Id] = (string)$summary->Status;

			return $list;
		}
		return array();
	}


	/**
	* Get a DistributionConfig DOMDocument
	*
	* http://docs.amazonwebservices.com/AmazonCloudFront/latest/APIReference/index.html?PutConfig.html
	*
	* @internal Used to create XML in createDistribution() and updateDistribution()
	* @param string $bucket S3 Origin bucket
	* @param boolean $enabled Enabled (true/false)
	* @param string $comment Comment to append
	* @param string $callerReference Caller reference
	* @param array $cnames Array of CNAME aliases
	* @param string $defaultRootObject Default root object
	* @param string $originAccessIdentity Origin access identity
	* @param array $trustedSigners Array of trusted signers
	* @return string
	*/
	private function __getCloudFrontDistributionConfigXML($bucket, $enabled, $comment, $callerReference = '0', $cnames = array(), $defaultRootObject = null, $originAccessIdentity = null, $trustedSigners = array())
	{
		$dom = new DOMDocument('1.0', 'UTF-8');
		$dom->formatOutput = true;
		$distributionConfig = $dom->createElement('DistributionConfig');
		$distributionConfig->setAttribute('xmlns', 'http://cloudfront.amazonaws.com/doc/2010-11-01/');

		$origin = $dom->createElement('S3Origin');
		$origin->appendChild($dom->createElement('DNSName', $bucket));
		if ($originAccessIdentity !== null) $origin->appendChild($dom->createElement('OriginAccessIdentity', $originAccessIdentity));
		$distributionConfig->appendChild($origin);

		if ($defaultRootObject !== null) $distributionConfig->appendChild($dom->createElement('DefaultRootObject', $defaultRootObject));

		$distributionConfig->appendChild($dom->createElement('CallerReference', $callerReference));
		foreach ($cnames as $cname)
			$distributionConfig->appendChild($dom->createElement('CNAME', $cname));
		if ($comment !== '') $distributionConfig->appendChild($dom->createElement('Comment', $comment));
		$distributionConfig->appendChild($dom->createElement('Enabled', $enabled ? 'true' : 'false'));

		$trusted = $dom->createElement('TrustedSigners');
		foreach ($trustedSigners as $id => $type)
			$trusted->appendChild($id !== '' ? $dom->createElement($type, $id) : $dom->createElement($type));
		$distributionConfig->appendChild($trusted);

		$dom->appendChild($distributionConfig);
		//var_dump($dom->saveXML());
		return $dom->saveXML();
	}


	/**
	* Parse a CloudFront distribution config
	*
	* See http://docs.amazonwebservices.com/AmazonCloudFront/latest/APIReference/index.html?GetDistribution.html
	*
	* @internal Used to parse the CloudFront DistributionConfig node to an array
	* @param object &$node DOMNode
	* @return array
	*/
	private function __parseCloudFrontDistributionConfig(&$node)
	{
		if (isset($node->DistributionConfig))
			return $this -> __parseCloudFrontDistributionConfig($node->DistributionConfig);

		$dist = array();
		if (isset($node->Id, $node->Status, $node->LastModifiedTime, $node->DomainName))
		{
			$dist['id'] = (string)$node->Id;
			$dist['status'] = (string)$node->Status;
			$dist['time'] = strtotime((string)$node->LastModifiedTime);
			$dist['domain'] = (string)$node->DomainName;
		}

		if (isset($node->CallerReference))
			$dist['callerReference'] = (string)$node->CallerReference;

		if (isset($node->Enabled))
			$dist['enabled'] = (string)$node->Enabled == 'true' ? true : false;

		if (isset($node->S3Origin))
		{
			if (isset($node->S3Origin->DNSName))
				$dist['origin'] = (string)$node->S3Origin->DNSName;

			$dist['originAccessIdentity'] = isset($node->S3Origin->OriginAccessIdentity) ?
			(string)$node->S3Origin->OriginAccessIdentity : null;
		}

		$dist['defaultRootObject'] = isset($node->DefaultRootObject) ? (string)$node->DefaultRootObject : null;

		$dist['cnames'] = array();
		if (isset($node->CNAME))
			foreach ($node->CNAME as $cname)
				$dist['cnames'][(string)$cname] = (string)$cname;

		$dist['trustedSigners'] = array();
		if (isset($node->TrustedSigners))
			foreach ($node->TrustedSigners as $signer)
			{
				if (isset($signer->Self))
					$dist['trustedSigners'][''] = 'Self';
				elseif (isset($signer->KeyPairId))
					$dist['trustedSigners'][(string)$signer->KeyPairId] = 'KeyPairId';
				elseif (isset($signer->AwsAccountNumber))
					$dist['trustedSigners'][(string)$signer->AwsAccountNumber] = 'AwsAccountNumber';
			}

		$dist['comment'] = isset($node->Comment) ? (string)$node->Comment : null;
		return $dist;
	}


	/**
	* Grab CloudFront response
	*
	* @internal Used to parse the CloudFront WPvivid_S3Request::getResponse() output
	* @param object &$rest WPvivid_S3Request instance
	* @return object
	*/
	private function __getCloudFrontResponse(&$rest)
	{
		$rest->getResponse();
		if ($rest->response->error === false && isset($rest->response->body) &&
		is_string($rest->response->body) && substr($rest->response->body, 0, 5) == '<?xml')
		{
			$rest->response->body = simplexml_load_string($rest->response->body);
			// Grab CloudFront errors
			if (isset($rest->response->body->Error, $rest->response->body->Error->Code,
			$rest->response->body->Error->Message))
			{
				$rest->response->error = array(
					'code' => (string)$rest->response->body->Error->Code,
					'message' => (string)$rest->response->body->Error->Message
				);
				unset($rest->response->body);
			}
		}
		return $rest->response;
	}


	/**
	* Get MIME type for file
	*
	* To override the putObject() Content-Type, add it to $requestHeaders
	*
	* To use fileinfo, ensure the MAGIC environment variable is set
	*
	* @internal Used to get mime types
	* @param string &$file File path
	* @return string
	*/
	private function __getMIMEType(&$file)
	{
		$exts = array(
			'jpg' => 'image/jpeg', 'jpeg' => 'image/jpeg', 'gif' => 'image/gif',
			'png' => 'image/png', 'ico' => 'image/x-icon', 'pdf' => 'application/pdf',
			'tif' => 'image/tiff', 'tiff' => 'image/tiff', 'svg' => 'image/svg+xml',
			'svgz' => 'image/svg+xml', 'swf' => 'application/x-shockwave-flash', 
			'zip' => 'application/zip', 'gz' => 'application/x-gzip',
			'tar' => 'application/x-tar', 'bz' => 'application/x-bzip',
			'bz2' => 'application/x-bzip2',  'rar' => 'application/x-rar-compressed',
			'exe' => 'application/x-msdownload', 'msi' => 'application/x-msdownload',
			'cab' => 'application/vnd.ms-cab-compressed', 'txt' => 'text/plain',
			'asc' => 'text/plain', 'htm' => 'text/html', 'html' => 'text/html',
			'css' => 'text/css', 'js' => 'text/javascript',
			'xml' => 'text/xml', 'xsl' => 'application/xsl+xml',
			'ogg' => 'application/ogg', 'mp3' => 'audio/mpeg', 'wav' => 'audio/x-wav',
			'avi' => 'video/x-msvideo', 'mpg' => 'video/mpeg', 'mpeg' => 'video/mpeg',
			'mov' => 'video/quicktime', 'flv' => 'video/x-flv', 'php' => 'text/x-php'
		);

		$ext = strtolower(pathinfo($file, PATHINFO_EXTENSION));
		if (isset($exts[$ext])) return $exts[$ext];

		// Use fileinfo if available
		if (extension_loaded('fileinfo') && isset($_ENV['MAGIC']) &&
		($finfo = finfo_open(FILEINFO_MIME, $_ENV['MAGIC'])) !== false)
		{
			if (($type = finfo_file($finfo, $file)) !== false)
			{
				// Remove the charset and grab the last content-type
				$type = explode(' ', str_replace('; charset=', ';charset=', $type));
				$type = array_pop($type);
				$type = explode(';', $type);
				$type = trim(array_shift($type));
			}
			finfo_close($finfo);
			if ($type !== false && strlen($type) > 0) return $type;
		}

		return 'application/octet-stream';
	}


	/**
	* Get the current time
	*
	* @internal Used to apply offsets to sytem time
	* @return integer
	*/
	public function __getTime()
	{
		return time() + $this -> __timeOffset;
	}


	/**
	* Generate the auth string: "AWS AccessKey:Signature"
	*
	* @internal Used by WPvivid_S3Request::getResponse()
	* @param string $string String to sign
	* @return string
	*/
	public function __getSignature($string)
	{
		return 'AWS '.$this -> __accessKey.':'.$this -> __getHash($string);
	}


	/**
	* Creates a HMAC-SHA1 hash
	*
	* This uses the hash extension if loaded
	*
	* @internal Used by __getSignature()
	* @param string $string String to sign
	* @return string
	*/
	private function __getHash($string)
	{
		return base64_encode(extension_loaded('hash') ?
		hash_hmac('sha1', $string, $this -> __secretKey, true) : pack('H*', sha1(
		(str_pad($this -> __secretKey, 64, chr(0x00)) ^ (str_repeat(chr(0x5c), 64))) .
		pack('H*', sha1((str_pad($this -> __secretKey, 64, chr(0x00)) ^
		(str_repeat(chr(0x36), 64))) . $string)))));
	}

    /**
     * Generate the headers for AWS Signature V4
     *
     * @internal Used by UpdraftPlus_S3Request::getResponse()
     * @param array $aHeaders amzHeaders
     * @param array $headers
     * @param string $method
     * @param string $uri
     * @param string $data
     *
     * @return array $headers
     */
    public function __getSignatureV4($aHeaders, $headers, $method = 'GET', $uri = '', $data = '') {// phpcs:ignore PHPCompatibility.FunctionNameRestrictions.ReservedFunctionNames.MethodDoubleUnderscore -- Method name "UpdraftPlus_S3Request::__responseHeaderCallback" is discouraged; PHP has reserved all method names with a double underscore prefix for future use.
        $service = 's3';
        $region = $this->getRegion();

        $algorithm   = 'AWS4-HMAC-SHA256';
        $amzHeaders  = array();
        $amzRequests = array();

        $amzDate = gmdate('Ymd\THis\Z');
        $amzDateStamp = gmdate('Ymd');

        // amz-date ISO8601 format? for aws request
        $amzHeaders['x-amz-date'] = $amzDate;

        // CanonicalHeaders
        foreach ($headers as $k => $v) {
            $amzHeaders[strtolower($k)] = trim($v);
        }

        foreach ($aHeaders as $k => $v) {
            $amzHeaders[strtolower($k)] = trim($v);
        }
        uksort($amzHeaders, 'strcmp');

        // payload
        $payloadHash = isset($amzHeaders['x-amz-content-sha256']) ? $amzHeaders['x-amz-content-sha256'] : hash('sha256', $data);

        // parameters
        $parameters = array();
        if (strpos($uri, '?')) {
            list($uri, $query_str) = @explode('?', $uri);
            parse_str($query_str, $parameters);
        }

        // Canonical Requests
        $amzRequests[] = $method;
        $uriQmPos = strpos($uri, '?');
        $amzRequests[] = (false === $uriQmPos ? $uri : substr($uri, 0, $uriQmPos));
        $amzRequests[] = http_build_query($parameters);

        // add headers as string to requests
        foreach ($amzHeaders as $k => $v) {
            $amzRequests[] = $k . ':' . $v;
        }

        // add a blank entry so we end up with an extra line break
        $amzRequests[] = '';

        // SignedHeaders
        $amzRequests[] = implode(';', array_keys($amzHeaders));

        // payload hash
        $amzRequests[] = $payloadHash;

        // request as string
        $amzRequestStr = implode("\n", $amzRequests);

        // CredentialScope
        $credentialScope = array();
        $credentialScope[] = $amzDateStamp;
        $credentialScope[] = $region;
        $credentialScope[] = $service;
        $credentialScope[] = 'aws4_request';

        // stringToSign
        $stringToSign = array();
        $stringToSign[] = $algorithm;
        $stringToSign[] = $amzDate;
        $stringToSign[] = implode('/', $credentialScope);
        $stringToSign[] = hash('sha256', $amzRequestStr);

        // as string
        $stringToSignStr = implode("\n", $stringToSign);

        // Make Signature
        $kSecret = 'AWS4' . $this->__secretKey;
        $kDate = hash_hmac('sha256', $amzDateStamp, $kSecret, true);
        $kRegion = hash_hmac('sha256', $region, $kDate, true);
        $kService = hash_hmac('sha256', $service, $kRegion, true);
        $kSigning = hash_hmac('sha256', 'aws4_request', $kService, true);
        $signature = hash_hmac('sha256', $stringToSignStr, $kSigning);

        $authorization = array(
            'Credential=' . $this->__accessKey . '/' . implode('/', $credentialScope),
            'SignedHeaders=' . implode(';', array_keys($amzHeaders)),
            'Signature=' . $signature,
        );
        $authorizationStr = $algorithm . ' ' . implode(',', $authorization);

        $resultHeaders = array(
            'X-AMZ-DATE' => $amzDate,
            'Authorization' => $authorizationStr
        );
        if (!isset($aHeaders['x-amz-content-sha256'])) {
            $resultHeaders['x-amz-content-sha256'] = $payloadHash;
        }

        return $resultHeaders;
    }
}

/**
 * S3 Request class 
 *
 * @link http://undesigned.org.za/2007/10/22/amazon-s3-php-class
 * @version 0.5.0-dev
 */
final class WPvivid_S3Request
{
	/**
	 * AWS URI
	 *
	 * @var string
	 * @access private
	 */
	private $endpoint;
	
	/**
	 * Verb
	 *
	 * @var string
	 * @access private
	 */
	private $verb;
	
	/**
	 * S3 bucket name
	 *
	 * @var string
	 * @access private
	 */
	private $bucket;
	
	/**
	 * Object URI
	 *
	 * @var string
	 * @access private
	 */
	private $uri;
	
	/**
	 * Final object URI
	 *
	 * @var string
	 * @access private
	 */
	private $resource = '';
	
	/**
	 * Additional request parameters
	 *
	 * @var array
	 * @access private
	 */
	private $parameters = array();
	
	/**
	 * Amazon specific request headers
	 *
	 * @var array
	 * @access private
	 */
	private $amzHeaders = array();

	/**
	 * HTTP request headers
	 *
	 * @var array
	 * @access private
	 */
	private $headers = array(
		'Host' => '', 'Date' => '', 'Content-MD5' => '', 'Content-Type' => ''
	);

	/**
	 * Use HTTP PUT?
	 *
	 * @var bool
	 * @access public
	 */
	public $fp = false;

	/**
	 * PUT file size
	 *
	 * @var int
	 * @access public
	 */
	public $size = 0;

	/**
	 * PUT post fields
	 *
	 * @var array
	 * @access public
	 */
	public $data = false;

	/**
	 * S3 request respone
	 *
	 * @var object
	 * @access public
	 */
	public $response;

    private $s3;

	/**
	* Constructor
	*
	* @param string $verb Verb
	* @param string $bucket Bucket name
	* @param string $uri Object URI
	* @param string $endpoint AWS endpoint URI
	* @return mixed
	*/
	function __construct($verb, $bucket = '', $uri = '', $endpoint = 's3.amazonaws.com', $s3 = null)
	{
		
		$this->endpoint = $endpoint;
		$this->verb = $verb;
		$this->bucket = $bucket;
		$this->uri = $uri !== '' ? '/'.str_replace('%2F', '/', rawurlencode($uri)) : '/';
		$this -> s3 = $s3;

		if ($this->bucket !== '')
		{
            $this->headers['Host'] = $this->endpoint;
            $this->uri = $this->uri;
            if ($this->bucket !== '') $this->uri = '/'.$this->bucket.$this->uri;
            $this->bucket = '';
            $this->resource = $this->uri;
		}
		else
		{
			$this->headers['Host'] = $this->endpoint;
			$this->resource = $this->uri;
		}

		$this->headers['Date'] = gmdate('D, d M Y H:i:s T');
		$this->response = new STDClass;
		$this->response->error = false;
		$this->response->body = null;
		$this->response->headers = array();
	}


	/**
	* Set request parameter
	*
	* @param string $key Key
	* @param string $value Value
	* @return void
	*/
	public function setParameter($key, $value)
	{
		$this->parameters[$key] = $value;
	}

	public function unsetParameter($bucket)
    {
        if (sizeof($this->parameters) > 0) {
            foreach ($this->parameters as $key => $value){
                unset($this->parameters[$key]);
            }
        }
        $this->uri = '/';
        if ($bucket !== '')
        {
            $this->uri = '/'.$bucket.$this->uri;
        }
    }


	/**
	* Set request header
	*
	* @param string $key Key
	* @param string $value Value
	* @return void
	*/
	public function setHeader($key, $value)
	{
		$this->headers[$key] = $value;
	}


	/**
	* Set x-amz-meta-* header
	*
	* @param string $key Key
	* @param string $value Value
	* @return void
	*/
	public function setAmzHeader($key, $value)
	{
		$this->amzHeaders[$key] = $value;
	}


	/**
	* Get the S3 response
	*
	* @return object | false
	*/
    /*public function getResponse() {
        $query = '';
        if (sizeof($this->parameters) > 0) {
            $query = ('?' !== substr($this->uri, -1)) ? '?' : '&';
            foreach ($this->parameters as $var => $value)
                if (null == $value || '' == $value) $query .= $var.'&';
                else $query .= $var.'='.rawurlencode($value).'&';
            $query = substr($query, 0, -1);
            $this->uri .= $query;

            if (array_key_exists('acl', $this->parameters) ||
                array_key_exists('location', $this->parameters) ||
                array_key_exists('torrent', $this->parameters) ||
                array_key_exists('logging', $this->parameters) ||
                array_key_exists('partNumber', $this->parameters) ||
                array_key_exists('uploads', $this->parameters) ||
                array_key_exists('uploadId', $this->parameters))
                $this->resource .= $query;
        }

        $url = ($this->s3->useSSL ? 'https://' : 'http://') . ('' !== $this->headers['Host'] ? $this->headers['Host'] : $this->endpoint) . $this->uri;
        //var_dump('bucket: ' . $this->bucket, 'uri: ' . $this->uri, 'resource: ' . $this->resource, 'url: ' . $url);

        $curl = curl_init();
        curl_setopt($curl, CURLOPT_USERAGENT, 'S3/php');

        if ($this->s3->useSSL) {
            // SSL Validation can now be optional for those with broken OpenSSL installations
            curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, $this->s3->useSSLValidation ? 2 : 0);
            curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, $this->s3->useSSLValidation ? 1 : 0);

            if (null !== $this->s3->sslKey) curl_setopt($curl, CURLOPT_SSLKEY, $this->s3->sslKey);
            if (null !== $this->s3->sslCert) curl_setopt($curl, CURLOPT_SSLCERT, $this->s3->sslCert);
            if (null !== $this->s3->sslCACert) curl_setopt($curl, CURLOPT_CAINFO, $this->s3->sslCACert);
        }

        curl_setopt($curl, CURLOPT_URL, $url);

        // Headers
        $headers = array(); $amz = array();
        foreach ($this->amzHeaders as $header => $value)
            if (strlen($value) > 0) $headers[] = $header.': '.$value;
        foreach ($this->headers as $header => $value)
            if (strlen($value) > 0) $headers[] = $header . ': ' . $value;

        // Collect AMZ headers for signature
        foreach ($this->amzHeaders as $header => $value)
            if (strlen($value) > 0) $amz[] = strtolower($header).':'.$value;

        // AMZ headers must be sorted
        if (sizeof($amz) > 0) {
            //sort($amz);
            usort($amz, array(&$this, '__sortMetaHeadersCmp'));
            $amz = "\n".implode("\n", $amz);
        } else {
            $amz = '';
        }

        if ($this->s3->hasAuth()) {
            // Authorization string (CloudFront stringToSign should only contain a date)
            if ('cloudfront.amazonaws.com' == $this->headers['Host']) {
                $headers[] = 'Authorization: ' . $this->s3->__getSignature($this->headers['Date']);
            } else {
                if ('v2' === $this->s3->signVer) {
                    $headers[] = 'Authorization: ' . $this->s3->__getSignature(
                            $this->verb."\n".
                            $this->headers['Content-MD5']."\n".
                            $this->headers['Content-Type']."\n".
                            $this->headers['Date'].$amz."\n".
                            $this->resource
                        );
                } else {
                    $amzHeaders = $this->s3->__getSignatureV4(
                        $this->amzHeaders,
                        $this->headers,
                        $this->verb,
                        $this->uri,
                        $this->data
                    );
                    foreach ($amzHeaders as $k => $v) {
                        $headers[] = $k . ': ' . $v;
                    }
                }
            }
        }
//        if (false !== $this->s3->port) curl_setopt($curl, CURLOPT_PORT, $this->s3->port);
        curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
        curl_setopt($curl, CURLOPT_HEADER, false);
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, false);
        curl_setopt($curl, CURLOPT_WRITEFUNCTION, array(&$this, '__responseWriteCallback'));
        curl_setopt($curl, CURLOPT_HEADERFUNCTION, array(&$this, '__responseHeaderCallback'));
        @curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true);

        // Request types
        switch ($this->verb) {
            case 'GET': break;
            case 'PUT': case 'POST':
            if (false !== $this->fp) {
                curl_setopt($curl, CURLOPT_PUT, true);
                curl_setopt($curl, CURLOPT_INFILE, $this->fp);
                if ($this->size >= 0) {
                    curl_setopt($curl, CURLOPT_INFILESIZE, $this->size);
                }
            } elseif (false !== $this->data) {
                curl_setopt($curl, CURLOPT_CUSTOMREQUEST, $this->verb);
                curl_setopt($curl, CURLOPT_POSTFIELDS, $this->data);
                curl_setopt($curl, CURLOPT_INFILESIZE, strlen($this->data));
            } else {
                curl_setopt($curl, CURLOPT_CUSTOMREQUEST, $this->verb);
            }
            break;
            case 'HEAD':
                curl_setopt($curl, CURLOPT_CUSTOMREQUEST, 'HEAD');
                curl_setopt($curl, CURLOPT_NOBODY, true);
                break;
            case 'DELETE':
                curl_setopt($curl, CURLOPT_CUSTOMREQUEST, 'DELETE');
                break;
            default: break;
        }

        // Execute, grab errors
        if (curl_exec($curl))
            $this->response->code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
        else
            $this->response->error = array(
                'code' => curl_errno($curl),
                'message' => curl_error($curl),
                'resource' => $this->resource
            );

        @curl_close($curl);

        // Parse body into XML
        // The case in which there is not application/xml content-type header is to support a DreamObjects case seen, April 2018
        if (false === $this->response->error && isset($this->response->body) && ((isset($this->response->headers['type']) && 'application/xml' == $this->response->headers['type']) || (!isset($this->response->headers['type']) && 0 === strpos($this->response->body, '<?xml')))) {
            $this->response->body = simplexml_load_string($this->response->body);

            // Grab S3 errors
            if (!in_array($this->response->code, array(200, 204, 206)) &&
                isset($this->response->body->Code)) {
                $this->response->error = array(
                    'code' => (string)$this->response->body->Code,
                );
                $this->response->error['message'] = isset($this->response->body->Message) ? $this->response->body->Message : '';
                if (isset($this->response->body->Resource))
                    $this->response->error['resource'] = (string)$this->response->body->Resource;
                unset($this->response->body);
            }
        }

        // Clean up file resources
//        if (false !== $this->fp && is_resource($this->fp)) fclose($this->fp);
        return $this->response;
    }*/
    public function getResponse() {
        $query = '';
        if (sizeof($this->parameters) > 0) {
            $query = ('?' !== substr($this->uri, -1)) ? '?' : '&';
            foreach ($this->parameters as $var => $value)
                if (null == $value || '' == $value) $query .= $var.'&';
                else if($var !== 'continuation-token'){
                    $query .= $var.'='.rawurlencode($value).'&';
                }
                else{
                    $query .= $var.'='.rawurlencode($value).'&';
                    //$query .= $var.'='.$value.'&';
                }
            $query = substr($query, 0, -1);
            $this->uri .= $query;

            if (array_key_exists('acl', $this->parameters) ||
                array_key_exists('location', $this->parameters) ||
                array_key_exists('torrent', $this->parameters) ||
                array_key_exists('logging', $this->parameters) ||
                array_key_exists('partNumber', $this->parameters) ||
                array_key_exists('uploads', $this->parameters) ||
                array_key_exists('uploadId', $this->parameters))
                $this->resource .= $query;
        }
        $url = ($this->s3->useSSL ? 'https://' : 'http://') . ('' !== $this->headers['Host'] ? $this->headers['Host'] : $this->endpoint) . $this->uri;
        //var_dump('bucket: ' . $this->bucket, 'uri: ' . $this->uri, 'resource: ' . $this->resource, 'url: ' . $url);

        $curl = curl_init();
        curl_setopt($curl, CURLOPT_USERAGENT, 'S3/php');

        if ($this->s3->useSSL) {
            // SSL Validation can now be optional for those with broken OpenSSL installations
            curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, $this->s3->useSSLValidation ? 2 : 0);
            curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, $this->s3->useSSLValidation ? 1 : 0);

            if (null !== $this->s3->sslKey) curl_setopt($curl, CURLOPT_SSLKEY, $this->s3->sslKey);
            if (null !== $this->s3->sslCert) curl_setopt($curl, CURLOPT_SSLCERT, $this->s3->sslCert);
            if (null !== $this->s3->sslCACert) curl_setopt($curl, CURLOPT_CAINFO, $this->s3->sslCACert);
        }

        curl_setopt($curl, CURLOPT_URL, $url);

        // Headers
        $headers = array(); $amz = array();
        foreach ($this->amzHeaders as $header => $value)
            if (strlen($value) > 0) $headers[] = $header.': '.$value;
        foreach ($this->headers as $header => $value)
            if (strlen($value) > 0) $headers[] = $header . ': ' . $value;

        // Collect AMZ headers for signature
        foreach ($this->amzHeaders as $header => $value)
            if (strlen($value) > 0) $amz[] = strtolower($header).':'.$value;

        // AMZ headers must be sorted
        if (sizeof($amz) > 0) {
            //sort($amz);
            usort($amz, array(&$this, '__sortMetaHeadersCmp'));
            $amz = "\n".implode("\n", $amz);
        } else {
            $amz = '';
        }

        if ($this->s3->hasAuth()) {
            // Authorization string (CloudFront stringToSign should only contain a date)
            if ('cloudfront.amazonaws.com' == $this->headers['Host']) {
                $headers[] = 'Authorization: ' . $this->s3->__getSignature($this->headers['Date']);
            } else {
                if ('v2' === $this->s3->signVer) {
                    $headers[] = 'Authorization: ' . $this->s3->__getSignature(
                            $this->verb."\n".
                            $this->headers['Content-MD5']."\n".
                            $this->headers['Content-Type']."\n".
                            $this->headers['Date'].$amz."\n".
                            $this->resource
                        );
                } else {
                    $amzHeaders = $this->s3->__getSignatureV4(
                        $this->amzHeaders,
                        $this->headers,
                        $this->verb,
                        $this->uri,
                        $this->data
                    );
                    foreach ($amzHeaders as $k => $v) {
                        $headers[] = $k . ': ' . $v;
                    }
                }
            }
        }
//        if (false !== $this->s3->port) curl_setopt($curl, CURLOPT_PORT, $this->s3->port);
        curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
        curl_setopt($curl, CURLOPT_HEADER, false);
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, false);
        curl_setopt($curl, CURLOPT_WRITEFUNCTION, array(&$this, '__responseWriteCallback'));
        curl_setopt($curl, CURLOPT_HEADERFUNCTION, array(&$this, '__responseHeaderCallback'));
        @curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true);

        // Request types
        switch ($this->verb) {
            case 'GET': break;
            case 'PUT': case 'POST':
            if (false !== $this->fp) {
                curl_setopt($curl, CURLOPT_PUT, true);
                curl_setopt($curl, CURLOPT_INFILE, $this->fp);
                if ($this->size >= 0) {
                    curl_setopt($curl, CURLOPT_INFILESIZE, $this->size);
                }
            } elseif (false !== $this->data) {
                curl_setopt($curl, CURLOPT_CUSTOMREQUEST, $this->verb);
                curl_setopt($curl, CURLOPT_POSTFIELDS, $this->data);
                curl_setopt($curl, CURLOPT_INFILESIZE, strlen($this->data));
            } else {
                curl_setopt($curl, CURLOPT_CUSTOMREQUEST, $this->verb);
            }
            break;
            case 'HEAD':
                curl_setopt($curl, CURLOPT_CUSTOMREQUEST, 'HEAD');
                curl_setopt($curl, CURLOPT_NOBODY, true);
                break;
            case 'DELETE':
                curl_setopt($curl, CURLOPT_CUSTOMREQUEST, 'DELETE');
                break;
            default: break;
        }

        // Execute, grab errors
        if (curl_exec($curl))
            $this->response->code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
        else
            $this->response->error = array(
                'code' => curl_errno($curl),
                'message' => curl_error($curl),
                'resource' => $this->resource
            );

        @curl_close($curl);

        // Parse body into XML
        // The case in which there is not application/xml content-type header is to support a DreamObjects case seen, April 2018
        if (false === $this->response->error && isset($this->response->body) && ((isset($this->response->headers['type']) && 'application/xml' == $this->response->headers['type']) || (!isset($this->response->headers['type']) && 0 === strpos($this->response->body, '<?xml')))) {
            $this->response->body = simplexml_load_string($this->response->body);

            // Grab S3 errors
            if (!in_array($this->response->code, array(200, 204, 206)) &&
                isset($this->response->body->Code)) {
                $this->response->error = array(
                    'code' => (string)$this->response->body->Code,
                );
                $this->response->error['message'] = isset($this->response->body->Message) ? $this->response->body->Message : '';
                if (isset($this->response->body->Resource))
                    $this->response->error['resource'] = (string)$this->response->body->Resource;
                unset($this->response->body);
            }
        }

        // Clean up file resources
//        if (false !== $this->fp && is_resource($this->fp)) fclose($this->fp);
        return $this->response;
    }

	/**
	* Sort compare for meta headers
	*
	* @internal Used to sort x-amz meta headers
	* @param string $a String A
	* @param string $b String B
	* @return integer
	*/
	private function __sortMetaHeadersCmp($a, $b)
	{
		$lenA = strpos($a, ':');
		$lenB = strpos($b, ':');
		$minLen = min($lenA, $lenB);
		$ncmp = strncmp($a, $b, $minLen);
		if ($lenA == $lenB) return $ncmp;
		if (0 == $ncmp) return $lenA < $lenB ? -1 : 1;
		return $ncmp;
	}

	/**
	* CURL write callback
	*
	* @param resource &$curl CURL resource
	* @param string &$data Data
	* @return integer
	*/
	private function __responseWriteCallback($curl, $data)
	{
		if (in_array($this->response->code, array(200, 206)) && $this->fp !== false)
			return fwrite($this->fp, $data);
		else
			$this->response->body .= $data;
		return strlen($data);
	}


//	/**
//	* Check DNS conformity
//	*
//	* @param string $bucket Bucket name
//	* @return boolean
//	*/
//	private function __dnsBucketName($bucket)
//	{
//		if (strlen($bucket) > 63 || preg_match("/[^a-z0-9\.-]/", $bucket)) return false;
//		if ($this -> s3 -> useSSL && strstr($bucket, '.') !== false) return false;
//		if (strstr($bucket, '-.') !== false) return false;
//		if (strstr($bucket, '..') !== false) return false;
//		if (!preg_match("/^[0-9a-z]/", $bucket)) return false;
//		if (!preg_match("/[0-9a-z]$/", $bucket)) return false;
//		return true;
//	}


	/**
	* CURL header callback
	*
	* @param resource $curl CURL resource
	* @param string $data Data
	* @return integer
	*/
	private function __responseHeaderCallback($curl, $data)
	{
		if (($strlen = strlen($data)) <= 2) return $strlen;
		if (substr($data, 0, 4) == 'HTTP')
			$this->response->code = (int)substr($data, 9, 3);
		else
		{
			$data = trim($data);
			if (strpos($data, ': ') === false) return $strlen;
			list($header, $value) = explode(': ', $data, 2);
			if ($header == 'Last-Modified')
				$this->response->headers['time'] = strtotime($value);
			elseif ($header == 'Date')
				$this->response->headers['date'] = strtotime($value);
			elseif ($header == 'Content-Length')
				$this->response->headers['size'] = (int)$value;
			elseif ($header == 'Content-Type')
				$this->response->headers['type'] = $value;
			elseif ($header == 'ETag')
				$this->response->headers['hash'] = $value[0] == '"' ? substr($value, 1, -1) : $value;
			elseif (preg_match('/^x-amz-meta-.*$/', $header))
				$this->response->headers[$header] = $value;
		}
		return $strlen;
	}

}

/**
 * S3 exception class
 *
 * @link http://undesigned.org.za/2007/10/22/amazon-s3-php-class
 * @version 0.5.0-dev
 */

class WPvivid_S3Exception extends Exception {
	/**
	 * Class constructor
	 *
	 * @param string $message Exception message
	 * @param string $file File in which exception was created
	 * @param string $line Line number on which exception was created
	 * @param int $code Exception code
	 */
	function __construct($message, $file, $line, $code = 0)
	{
		parent::__construct($message, $code);
		$this->file = $file;
		$this->line = $line;
	}
}
includes/customclass/class-wpvivid-google-drive.php000064400000170243151327705670016623 0ustar00<?php

if (!defined('WPVIVID_PLUGIN_DIR')){
    die;
}

require_once WPVIVID_PLUGIN_DIR . '/includes/customclass/class-wpvivid-remote.php';

if(!defined('WPVIVID_REMOTE_GOOGLEDRIVE'))
    define('WPVIVID_REMOTE_GOOGLEDRIVE','googledrive');
if(!defined('WPVIVID_GOOGLEDRIVE_DEFAULT_FOLDER'))
    define('WPVIVID_GOOGLEDRIVE_DEFAULT_FOLDER','wpvivid_backup');
if(!defined('WPVIVID_GOOGLEDRIVE_UPLOAD_SIZE'))
    define('WPVIVID_GOOGLEDRIVE_UPLOAD_SIZE',1024*1024*2);
if(!defined('WPVIVID_GOOGLE_NEED_PHP_VERSION'))
    define('WPVIVID_GOOGLE_NEED_PHP_VERSION','5.5');
class Wpvivid_Google_drive extends WPvivid_Remote
{
    public $options;

    public $google_drive_secrets;

    public $add_remote;

    public function __construct($options=array())
    {
        if(empty($options))
        {
            if(!defined('WPVIVID_INIT_STORAGE_TAB_GOOGLE_DRIVE'))
            {
                add_action('init', array($this, 'handle_auth_actions'));
                //wpvivid_google_drive_add_remote
                add_action('wp_ajax_wpvivid_google_drive_add_remote',array( $this,'finish_add_remote'));

                add_action('wpvivid_add_storage_tab',array($this,'wpvivid_add_storage_tab_google_drive'), 10);
                add_action('wpvivid_add_storage_page',array($this,'wpvivid_add_storage_page_google_drive'), 10);
                add_filter('wpvivid_pre_add_remote',array($this, 'pre_add_remote'),10,2);
                add_action('wpvivid_edit_remote_page',array($this,'wpvivid_edit_storage_page_google_drive'), 10);
                add_filter('wpvivid_remote_pic',array($this,'wpvivid_remote_pic_google_drive'),10);
                add_filter('wpvivid_get_out_of_date_remote',array($this,'wpvivid_get_out_of_date_google_drive'),10,2);
                add_filter('wpvivid_storage_provider_tran',array($this,'wpvivid_storage_provider_google_drive'),10);
                add_filter('wpvivid_get_root_path',array($this,'wpvivid_get_root_path_google_drive'),10);
                define('WPVIVID_INIT_STORAGE_TAB_GOOGLE_DRIVE',1);
            }

        }
        else
        {
            $this->options=$options;
        }
        $this->add_remote=false;
        $this->google_drive_secrets = array("web"=>array(
            "client_id"=>"134809148507-32crusepgace4h6g47ota99jjrvf4j1u.apps.googleusercontent.com",
            "project_id"=>"wpvivid-auth",
            "auth_uri"=>"https://accounts.google.com/o/oauth2/auth",
            "token_uri"=>"https://oauth2.googleapis.com/token",
            "auth_provider_x509_cert_url"=>"https://www.googleapis.com/oauth2/v1/certs",
            "client_secret"=>"",
            "redirect_uris"=>array("https://auth.wpvivid.com/google_drive_v2/")
        ));
    }

    public function pre_add_remote($remote,$id)
    {
        if($remote['type']==WPVIVID_REMOTE_GOOGLEDRIVE)
        {
            $remote['id']=$id;
        }

        return $remote;
    }

    public function handle_auth_actions()
    {
        if(isset($_GET['action']) && isset($_GET['page']))
        {
            if($_GET['page'] === 'WPvivid')
            {
                if($_GET['action']=='wpvivid_google_drive_auth')
                {
                    $check=current_user_can('manage_options');
                    if(!$check)
                    {
                        return;
                    }

                    $rand_id = substr(md5(time().rand()), 0,13);
                    $auth_id = 'wpvivid-auth-'.$rand_id;
                    $res = $this -> compare_php_version();
                    if($res['result'] == WPVIVID_FAILED){
                        echo '<div class="notice notice-warning is-dismissible"><p>'.esc_html($res['error']).'</p></div>';
                        return ;
                    }
                    try {
                        include_once WPVIVID_PLUGIN_DIR . '/vendor/autoload.php';
                        $client = new WPvivid_Google_Client();
                        $client->setAuthConfig($this->google_drive_secrets);
                        $client->setApprovalPrompt('force');
                        $client->addScope(WPvivid_Google_Service_Drive::DRIVE_FILE);
                        $client->setAccessType('offline');
                        $client->setState(admin_url() . 'admin.php?page=WPvivid' . '&action=wpvivid_google_drive_finish_auth&main_tab=storage&sub_tab=googledrive&sub_page=storage_account_google_drive&auth_id='.$auth_id);
                        $auth_url = $client->createAuthUrl();
                        $remote_options['auth_id']=$auth_id;
                        set_transient('google_drive_auth_id', $remote_options, 900);
                        header('Location: ' . filter_var($auth_url, FILTER_SANITIZE_URL));
                    }
                    catch (Exception $e){
                        if($e->getMessage() === 'file does not exist'){
                            $error_msg = __('Authentication failed, the client_secrets.json file is missing. Please make sure the client_secrets.json file is in wpvivid-backuprestore\includes\customclass directory.', 'wpvivid-backuprestore');
                            echo '<div class="notice notice-error"><p>'.esc_html($error_msg).'</p></div>';
                        }
                        else if($e->getMessage() === 'invalid json for auth config'){
                            $error_msg = __('Authentication failed, the format of the client_secrets.json file is incorrect. Please delete and re-install the plugin to recreate the file.', 'wpvivid-backuprestore');
                            echo '<div class="notice notice-error"><p>'.esc_html($error_msg).'</p></div>';
                        }
                        else{
                            echo '<div class="notice notice-error"><p>'.esc_html($e->getMessage()).'</p></div>';
                        }
                    }
                }
                else if($_GET['action']=='wpvivid_google_drive_finish_auth')
                {
                    $tmp_options = get_transient('google_drive_auth_id');
                    if($tmp_options === false)
                    {
                        return;
                    }
                    else if($tmp_options['auth_id'] !== $_GET['auth_id'])
                    {
                        delete_transient('google_drive_auth_id');
                        return;
                    }

                    try
                    {
                        if(isset($_GET['error']))
                        {
                            header('Location: '.admin_url().'admin.php?page='.WPVIVID_PLUGIN_SLUG.'&action=wpvivid_google_drive&main_tab=storage&sub_tab=googledrive&sub_page=storage_account_google_drive&result=error&resp_msg='.sanitize_text_field($_GET['error']));
                            return;
                        }

                        $remoteslist = WPvivid_Setting::get_all_remote_options();
                        foreach ($remoteslist as $key => $value)
                        {
                            if (isset($value['auth_id']) && isset($_GET['auth_id']) && $value['auth_id'] == sanitize_text_field($_GET['auth_id']))
                            {
                                echo '<div class="notice notice-success is-dismissible"><p>';
                                esc_html_e('You have authenticated the Google Drive account as your remote storage.', 'wpvivid-backuprestore');
                                echo '</p></div>';
                                return;
                            }
                        }

                        if(empty($_POST['refresh_token']))
                        {
                            if(empty($tmp_options['token']['refresh_token']))
                            {
                                $err = 'No refresh token was received from Google, which means that you entered client secret incorrectly, or that you did not re-authenticated yet after you corrected it. Please authenticate again.';
                                header('Location: '.admin_url().'admin.php?page='.WPVIVID_PLUGIN_SLUG.'&action=wpvivid_google_drive&main_tab=storage&sub_tab=googledrive&sub_page=storage_account_google_drive&result=error&resp_msg='.$err);

                                return;
                            }
                        }
                        else
                        {
                            $tmp_options['type'] = WPVIVID_REMOTE_GOOGLEDRIVE;
                            $tmp_options['token']['access_token'] = base64_encode(sanitize_text_field($_POST['access_token']));
                            $tmp_options['token']['expires_in'] = sanitize_text_field($_POST['expires_in']);
                            $tmp_options['token']['refresh_token'] = base64_encode(sanitize_text_field($_POST['refresh_token']));
                            $tmp_options['token']['scope'] = sanitize_text_field($_POST['scope']);
                            $tmp_options['token']['token_type'] = sanitize_text_field($_POST['token_type']);
                            $tmp_options['token']['created'] = sanitize_text_field($_POST['created']);
                            $tmp_options['is_encrypt'] = 1;
                            set_transient('google_drive_auth_id', $tmp_options, 900);
                        }
                        $this->add_remote=true;
                    }
                    catch (Exception $e){
                        echo '<div class="notice notice-error"><p>'.esc_html($e->getMessage()).'</p></div>';
                    }
                }
                else if($_GET['action']=='wpvivid_google_drive')
                {
                    try {
                        if (isset($_GET['result'])) {
                            if ($_GET['result'] == 'success') {
                                add_action('show_notice', array($this, 'wpvivid_show_notice_add_google_drive_success'));
                            } else if ($_GET['result'] == 'error') {
                                add_action('show_notice', array($this, 'wpvivid_show_notice_add_google_drive_error'));
                            }
                        }
                    }
                    catch (Exception $e){
                        echo '<div class="notice notice-error"><p>'.esc_html($e->getMessage()).'</p></div>';
                    }
                }
            }
        }
    }

    public function wpvivid_show_notice_add_google_drive_success(){
        echo '<div class="notice notice-success is-dismissible"><p>';
        esc_html_e('You have authenticated the Google Drive account as your remote storage.', 'wpvivid-backuprestore');
        echo '</p></div>';
    }
    public function wpvivid_show_notice_add_google_drive_error(){
        global $wpvivid_plugin;
        $wpvivid_plugin->wpvivid_handle_remote_storage_error($_GET['resp_msg'], 'Add Google Drive Remote');
        echo '<div class="notice notice-error"><p>'.esc_html($_GET['resp_msg']).'</p></div>';
    }

    public function wpvivid_add_storage_tab_google_drive()
    {
        ?>
        <div class="storage-providers storage-providers-active" remote_type="googledrive" onclick="select_remote_storage(event, 'storage_account_google_drive');">
            <img src="<?php echo esc_url(WPVIVID_PLUGIN_URL.'/admin/partials/images/stroage-google-drive.png'); ?>" style="vertical-align:middle;"/><?php esc_html_e('Google Drive', 'wpvivid-backuprestore'); ?>
        </div>
        <?php
    }

    public function wpvivid_add_storage_page_google_drive()
    {
        global $wpvivid_plugin;
        $root_path=apply_filters('wpvivid_get_root_path', WPVIVID_REMOTE_GOOGLEDRIVE);
        if($this->add_remote)
        {
            ?>
            <div id="storage_account_google_drive" class="storage-account-page">
                <div style="background-color:#f1f1f1; padding: 10px;">
                    Please read <a target="_blank" href="https://wpvivid.com/privacy-policy" style="text-decoration: none;">this privacy policy</a> for use of our Google Drive authorization app (none of your backup data is sent to us).
                </div>
                <div style="color:#8bc34a; padding: 10px 10px 10px 0;">
                    <strong>Authentication is done, please continue to enter the storage information, then click 'Add Now' button to save it.</strong>
                </div>
                <div style="padding: 10px 10px 10px 0;">
                    <strong><?php esc_html_e('Enter Your Google Drive Information', 'wpvivid-backuprestore'); ?></strong>
                </div>
                <table class="wp-list-table widefat plugins" style="width:100%;">
                    <tbody>
                    <tr>
                        <td class="plugin-title column-primary">
                            <div class="wpvivid-storage-form">
                                <input type="text" class="regular-text" autocomplete="off" option="googledrive" name="name" placeholder="<?php esc_attr_e('Enter a unique alias: e.g. Google Drive-001', 'wpvivid-backuprestore'); ?>" onkeyup="value=value.replace(/[^a-zA-Z0-9\-_]/g,'')" />
                            </div>
                        </td>
                        <td class="column-description desc">
                            <div class="wpvivid-storage-form-desc">
                                <i><?php esc_html_e('A name to help you identify the storage if you have multiple remote storage connected.', 'wpvivid-backuprestore'); ?></i>
                            </div>
                        </td>
                    </tr>
                    <tr>
                        <td class="plugin-title column-primary">
                            <div class="wpvivid-storage-form">
                                <input type="text" class="regular-text" autocomplete="off" name="path" value="<?php echo esc_attr($root_path.WPVIVID_GOOGLEDRIVE_DEFAULT_FOLDER); ?>" readonly="readonly" />
                            </div>
                        </td>
                        <td class="column-description desc">
                            <div class="wpvivid-storage-form-desc">
                                <i><?php esc_html_e('All backups will be uploaded to this directory.', 'wpvivid-backuprestore'); ?></i>
                            </div>
                        </td>
                    </tr>
                    <tr>
                        <td class="plugin-title column-primary">
                            <div class="wpvivid-storage-form">
                                <input type="text" class="regular-text" autocomplete="off" value="mywebsite01" readonly="readonly" />
                            </div>
                        </td>
                        <td class="column-description desc">
                            <div class="wpvivid-storage-form-desc">
                                <a href="https://docs.wpvivid.com/wpvivid-backup-pro-google-drive-custom-folder-name.html"><?php esc_html_e('Pro feature: Create a directory for storing the backups of the site', 'wpvivid-backuprestore'); ?></a>
                            </div>
                        </td>
                    </tr>
                    <tr>
                        <td class="plugin-title column-primary">
                            <div class="wpvivid-storage-select">
                                <label>
                                    <input type="checkbox" option="googledrive" name="default" checked /><?php esc_html_e('Set as the default remote storage.', 'wpvivid-backuprestore'); ?>
                                </label>
                            </div>
                        </td>
                        <td class="column-description desc">
                            <div class="wpvivid-storage-form-desc">
                                <i><?php esc_html_e('Once checked, all this sites backups sent to a remote storage destination will be uploaded to this storage by default.', 'wpvivid-backuprestore'); ?></i>
                            </div>
                        </td>
                    </tr>
                    <tr>
                        <td class="plugin-title column-primary">
                            <div class="wpvivid-storage-form">
                                <input id="wpvivid_google_drive_auth" class="button-primary" type="submit" value="<?php esc_attr_e('Add Now', 'wpvivid-backuprestore'); ?>" />
                            </div>
                        </td>
                        <td class="column-description desc">
                            <div class="wpvivid-storage-form-desc">
                                <i><?php esc_html_e('Click the button to add the storage.', 'wpvivid-backuprestore'); ?></i>
                            </div>
                        </td>
                    </tr>
                    </tbody>
                </table>
            </div>
            <script>
                function wpvivid_check_google_drive_storage_alias(storage_alias){
                    var find = 1;
                    jQuery('#wpvivid_remote_storage_list tr').each(function (i) {
                        jQuery(this).children('td').each(function (j) {
                            if (j == 3) {
                                if (jQuery(this).text() == storage_alias) {
                                    find = -1;
                                    return false;
                                }
                            }
                        });
                    });
                    return find;
                }
                jQuery('#wpvivid_google_drive_auth').click(function()
                {
                    wpvivid_google_drive_auth();
                });

                function wpvivid_google_drive_auth()
                {
                    wpvivid_settings_changed = false;
                    var name='';
                    var path='';
                    jQuery('input:text[option=googledrive]').each(function()
                    {
                        var key = jQuery(this).prop('name');
                        if(key==='name')
                        {
                            name = jQuery(this).val();
                        }
                    });

                    var remote_default='0';

                    jQuery('input:checkbox[option=googledrive]').each(function()
                    {
                        if(jQuery(this).prop('checked')) {
                            remote_default='1';
                        }
                        else {
                            remote_default='0';
                        }
                    });
                    if(name == ''){
                        alert(wpvividlion.remotealias);
                    }
                    else if(wpvivid_check_google_drive_storage_alias(name) === -1)
                    {
                        alert(wpvividlion.remoteexist);
                    }
                    else
                    {
                        var ajax_data;
                        var remote_from = wpvivid_ajax_data_transfer('googledrive');
                        ajax_data = {
                            'action': 'wpvivid_google_drive_add_remote',
                            'remote': remote_from
                        };
                        jQuery('#wpvivid_google_drive_auth').css({'pointer-events': 'none', 'opacity': '0.4'});
                        jQuery('#wpvivid_remote_notice').html('');
                        wpvivid_post_request(ajax_data, function (data)
                        {
                            try
                            {
                                var jsonarray = jQuery.parseJSON(data);
                                if (jsonarray.result === 'success')
                                {
                                    jQuery('#wpvivid_google_drive_auth').css({'pointer-events': 'auto', 'opacity': '1'});
                                    jQuery('input:text[option=googledrive]').each(function(){
                                        jQuery(this).val('');
                                    });
                                    jQuery('input:password[option=googledrive]').each(function(){
                                        jQuery(this).val('');
                                    });
                                    wpvivid_handle_remote_storage_data(data);
                                    location.href='admin.php?page=WPvivid&action=wpvivid_google_drive&main_tab=storage&sub_tab=googledrive&sub_page=storage_account_google_drive&result=success';
                                }
                                else if (jsonarray.result === 'failed')
                                {
                                    jQuery('#wpvivid_remote_notice').html(jsonarray.notice);
                                    jQuery('input[option=add-remote]').css({'pointer-events': 'auto', 'opacity': '1'});
                                }
                            }
                            catch (err)
                            {
                                alert(err);
                                jQuery('input[option=add-remote]').css({'pointer-events': 'auto', 'opacity': '1'});
                            }

                        }, function (XMLHttpRequest, textStatus, errorThrown)
                        {
                            var error_message = wpvivid_output_ajaxerror('adding the remote storage', textStatus, errorThrown);
                            alert(error_message);
                            jQuery('#wpvivid_google_drive_auth').css({'pointer-events': 'auto', 'opacity': '1'});
                        });
                    }
                }
            </script>
            <?php
        }
        else
        {
            ?>
            <div id="storage_account_google_drive" class="storage-account-page">
                <div style="background-color:#f1f1f1; padding: 10px;">
                    Please read <a target="_blank" href="https://wpvivid.com/privacy-policy" style="text-decoration: none;">this privacy policy</a> for use of our Google Drive authorization app (none of your backup data is sent to us).
                </div>
                <div style="padding: 10px 10px 10px 0;">
                    <strong><?php esc_html_e('To add Google Drive, please get Google authentication first. Once authenticated, you will be redirected to this page, then you can add storage information and save it', 'wpvivid-backuprestore'); ?></strong>
                </div>
                <table class="wp-list-table widefat plugins" style="width:100%;">
                    <tbody>
                    <tr>
                        <td class="plugin-title column-primary">
                            <div class="wpvivid-storage-form">
                                <input onclick="wpvivid_google_drive_auth();" class="button-primary" type="submit" value="<?php esc_attr_e('Authenticate with Google Drive', 'wpvivid-backuprestore'); ?>" />
                            </div>
                        </td>
                        <td class="column-description desc">
                            <div class="wpvivid-storage-form-desc">
                                <i><?php esc_html_e('Click to get Google authentication.', 'wpvivid-backuprestore'); ?></i>
                            </div>
                        </td>
                    </tr>
                    </tbody>
                </table>
                <div style="padding: 10px 0 0 0;">
                    <span>Tip: Get a 404 or 403 error after authorization? Please read this <a href="https://docs.wpvivid.com/http-403-error-authorizing-cloud-storage.html">doc</a>.</span>
                </div>
            </div>
            <script>
                function wpvivid_google_drive_auth()
                {
                    location.href = '<?php echo esc_url(admin_url()) . 'admin.php?page=WPvivid' . '&action=wpvivid_google_drive_auth'?>';
                }
            </script>
            <?php
        }

    }

    public function wpvivid_edit_storage_page_google_drive()
    {
        ?>
        <div id="remote_storage_edit_googledrive" class="postbox storage-account-block remote-storage-edit" style="display:none;">
            <div style="padding: 0 10px 10px 0;">
                <strong><?php esc_html_e('Enter Your Google Drive Information', 'wpvivid-backuprestore'); ?></strong>
            </div>
            <table class="wp-list-table widefat plugins" style="width:100%;">
                <tbody>
                <tr>
                    <td class="plugin-title column-primary">
                        <div class="wpvivid-storage-form">
                            <input type="text" class="regular-text" autocomplete="off" option="edit-googledrive" name="name" placeholder="<?php esc_attr_e('Enter a unique alias: e.g. Google Drive-001', 'wpvivid-backuprestore'); ?>" onkeyup="value=value.replace(/[^a-zA-Z0-9\-_]/g,'')" />
                        </div>
                    </td>
                    <td class="column-description desc">
                        <div class="wpvivid-storage-form-desc">
                            <i><?php esc_html_e('A name to help you identify the storage if you have multiple remote storage connected.', 'wpvivid-backuprestore'); ?></i>
                        </div>
                    </td>
                </tr>
                <tr>
                    <td class="plugin-title column-primary">
                        <div class="wpvivid-storage-form">
                            <input class="button-primary" type="submit" option="edit-remote" value="<?php esc_attr_e('Save Changes', 'wpvivid-backuprestore'); ?>" />
                        </div>
                    </td>
                    <td class="column-description desc">
                        <div class="wpvivid-storage-form-desc">
                            <i><?php esc_html_e('Click the button to save the changes.', 'wpvivid-backuprestore');?></i>
                        </div>
                    </td>
                </tr>
                </tbody>
            </table>
        </div>
        <script>
            function wpvivid_google_drive_update_auth()
            {
                var name='';
                jQuery('input:text[option=edit-googledrive]').each(function()
                {
                    var key = jQuery(this).prop('name');
                    if(key==='name')
                    {
                        name = jQuery(this).val();
                    }
                });

                if(name == ''){
                    alert(wpvividlion.remotealias);
                }
                else if(wpvivid_check_onedrive_storage_alias(name) === -1){
                    alert(wpvividlion.remoteexist);
                }
                else {
                    location.href = '<?php echo esc_url(admin_url()) . 'admin.php?page=WPvivid' . '&action=wpvivid_google_drive_update_auth&name='?>' + name + '&id=' + wpvivid_editing_storage_id;
                }
            }
        </script>
        <?php
    }

    public function wpvivid_remote_pic_google_drive($remote)
    {
        $remote['googledrive']['default_pic'] = '/admin/partials/images/stroage-google-drive(gray).png';
        $remote['googledrive']['selected_pic'] = '/admin/partials/images/stroage-google-drive.png';
        $remote['googledrive']['title'] = 'Google Drive';
        return $remote;
    }

    public function sanitize_options($skip_name='')
    {
        $ret['result']=WPVIVID_SUCCESS;

        if(!isset($this->options['name']))
        {
            $ret['error']="Warning: An alias for remote storage is required.";
            return $ret;
        }

        $this->options['name']=sanitize_text_field($this->options['name']);

        if(empty($this->options['name']))
        {
            $ret['error']="Warning: An alias for remote storage is required.";
            return $ret;
        }

        $remoteslist=WPvivid_Setting::get_all_remote_options();
        foreach ($remoteslist as $key=>$value)
        {
            if(isset($value['name'])&&$value['name'] == $this->options['name']&&$skip_name!=$value['name'])
            {
                $ret['error']="Warning: The alias already exists in storage list.";
                return $ret;
            }
        }

        $ret['options']=$this->options;
        return $ret;
    }

    public function test_connect()
    {
        return array('result' => WPVIVID_SUCCESS);
    }

    public function upload($task_id, $files, $callback = '')
    {
        global $wpvivid_plugin;


        $client=$this->get_client();
        if($client['result'] == WPVIVID_FAILED){
            return $client;
        }
        $client = $client['data'];

        if($client===false)
        {
            return array('result' => WPVIVID_FAILED,'error'=> 'Token refresh failed.');
        }

        $service = new WPvivid_Google_Service_Drive($client);
        $path=$this->options['path'];
        $wpvivid_plugin->wpvivid_log->WriteLog('Check upload folder '.$path,'notice');
        $folder_id=$this->get_folder($service,$path);

        if($folder_id==false)
        {
            return array('result' => WPVIVID_FAILED,'error'=> 'Unable to create the local file. Please make sure the folder is writable and try again.');
        }

        $upload_job=WPvivid_taskmanager::get_backup_sub_task_progress($task_id,'upload',WPVIVID_REMOTE_GOOGLEDRIVE);
        if(empty($upload_job))
        {
            $job_data=array();
            foreach ($files as $file)
            {
                $file_data['size']=filesize($file);
                $file_data['uploaded']=0;
                $file_data['resumeUri']=false;
                $file_data['progress']=false;
                $job_data[basename($file)]=$file_data;
            }
            WPvivid_taskmanager::update_backup_sub_task_progress($task_id,'upload',WPVIVID_REMOTE_GOOGLEDRIVE,WPVIVID_UPLOAD_UNDO,'Start uploading',$job_data);
            $upload_job=WPvivid_taskmanager::get_backup_sub_task_progress($task_id,'upload',WPVIVID_REMOTE_GOOGLEDRIVE);
        }

        foreach ($files as $file)
        {
            if(is_array($upload_job['job_data'])&&array_key_exists(basename($file),$upload_job['job_data']))
            {
                if($upload_job['job_data'][basename($file)]['uploaded']==1)
                    continue;
            }

            $this -> last_time = time();
            $this -> last_size = 0;

            if(!file_exists($file))
                return array('result' =>WPVIVID_FAILED,'error' =>$file.' not found. The file might has been moved, renamed or deleted. Please reload the list and verify the file exists.');
            $wpvivid_plugin->wpvivid_log->WriteLog('Start uploading '.basename($file),'notice');
            $wpvivid_plugin->set_time_limit($task_id);
            $result=$this->_upload($task_id, $file,$client,$service,$folder_id, $callback);
            if($result['result'] !==WPVIVID_SUCCESS){
                return $result;
            }
            else
            {
                WPvivid_taskmanager::wpvivid_reset_backup_retry_times($task_id);
            }
            $ref=$this->check_token($client, $service);
            if($ref['result']=!WPVIVID_SUCCESS)
            {
                return $ref;
            }
        }
        return array('result' =>WPVIVID_SUCCESS);
    }

    public function check_token(&$client, &$service)
    {
        if ($client->isAccessTokenExpired())
        {
            // Refresh the token if possible, else fetch a new one.
            global $wpvivid_plugin;
            $wpvivid_plugin->wpvivid_log->WriteLog('Refresh the token.','notice');
            if ($client->getRefreshToken())
            {
                $tmp_refresh_token = $client->getRefreshToken();
                /*
                $client->fetchAccessTokenWithRefreshToken($client->getRefreshToken());
                $token=$client->getAccessToken();
                */

                if(isset($this->options['is_encrypt']) && $this->options['is_encrypt'] == 1) {
                    $tmp_refresh_token = base64_decode($tmp_refresh_token);
                }

                $args = array(
                    'refresh_token' => $tmp_refresh_token
                );

                $result = wp_remote_post("https://auth.wpvivid.com/google_drive_v2/", array(
                    'timeout' => 60,
                    'body' => $args
                ));

                if (is_wp_error($result))
                {
                    return array('result' => WPVIVID_SUCCESS,'data' => false);
                }
                else
                {
                    $token = wp_remote_retrieve_body($result);
                    $token = json_decode($token, true);
                    if(!is_null($token))
                    {
                        $client->setAccessToken($token);
                    }
                    else
                    {
                        return array('result' => WPVIVID_SUCCESS,'data' => false);
                    }
                }

                $remote_options=WPvivid_Setting::get_remote_option($this->options['id']);
                $this->options['token']=json_decode(wp_json_encode($token),1);
                $this->options['token']['access_token']=base64_encode($this->options['token']['access_token']);
                $this->options['is_encrypt']=1;
                if($remote_options!==false)
                {
                    if(!isset($this->options['token']['refresh_token'])){
                        $this->options['token']['refresh_token'] = base64_encode($tmp_refresh_token);
                    }
                    else{
                        $this->options['token']['refresh_token']=base64_encode($this->options['token']['refresh_token']);
                    }
                    $remote_options['token']=$this->options['token'];
                    $remote_options['is_encrypt']=1;
                    WPvivid_Setting::update_remote_option($this->options['id'],$remote_options);

                    $client=$this->get_client();
                    if($client['result'] == WPVIVID_FAILED){
                        return $client;
                    }
                    $client = $client['data'];

                    if($client===false)
                    {
                        return array('result' => WPVIVID_FAILED,'error'=> 'Token refresh failed.');
                    }
                    $service = new WPvivid_Google_Service_Drive($client);
                }
                return array('result' => WPVIVID_SUCCESS);
            }
            else
            {
                return array('result' => WPVIVID_FAILED,'error'=>'get refresh token failed');
            }
        }
        else
        {
            return array('result' => WPVIVID_SUCCESS);
        }
    }

    public function _upload($task_id, $file,$client,$service,$folder_id, $callback = '', $retry_times=0)
    {
        global $wpvivid_plugin;
        $wpvivid_plugin->wpvivid_log->WriteLog('Check if the server already has the same name file.','notice');
        try{
            if(!$this->delete_exist_file($folder_id,basename($file),$service))
            {
                return array('result' =>WPVIVID_FAILED,'error'=>'Uploading '.$file.' to Google Drive server failed. '.$file.' might be deleted or network doesn\'t work properly . Please verify the file and confirm the network connection and try again later.');
            }

            $upload_job=WPvivid_taskmanager::get_backup_sub_task_progress($task_id,'upload',WPVIVID_REMOTE_GOOGLEDRIVE);
            $this -> current_file_size = filesize($file);
            $this -> current_file_name = basename($file);


            $fileMetadata = new WPvivid_Google_Service_Drive_DriveFile(array(
                'name' => basename($file),
                'parents' => array($folder_id)));
            $chunk_size = 1 * 1024 * 1024;
            $client->setDefer(true);
            $request = $service->files->create($fileMetadata);
            $media = new WPvivid_Google_Http_MediaFileUpload(
                $client,
                $request,
                'text/plain',
                null,
                true,
                $chunk_size
            );
            $media->setFileSize(filesize($file));

            $status = false;
            $handle = fopen($file, "rb");

            if(!empty($upload_job['job_data'][basename($file)]['resumeUri']))
            {
                $media->resume( $upload_job['job_data'][basename($file)]['resumeUri'] );

                $media->setResumeUri($upload_job['job_data'][basename($file)]['resumeUri'] );
                $media->setProgress($upload_job['job_data'][basename($file)]['progress'] );

                $wpvivid_plugin->wpvivid_log->WriteLog('Resume uploading '.basename($file).'.','notice');
                $wpvivid_plugin->wpvivid_log->WriteLog('resumeUri:'.$media->getResumeUri().'.','notice');
                $wpvivid_plugin->wpvivid_log->WriteLog('progress:'.$media->getProgress().'.','notice');

                $offset = $upload_job['job_data'][basename($file)]['progress'];
                fseek($handle, $offset);
                WPvivid_taskmanager::update_backup_sub_task_progress($task_id,'upload',WPVIVID_REMOTE_GOOGLEDRIVE,WPVIVID_UPLOAD_UNDO,'Resume uploading '.basename($file).'.',$upload_job['job_data']);
            }
            else
            {
                $wpvivid_plugin->wpvivid_log->WriteLog('Initiate a resumable upload session.','notice');
                $offset=0;
                WPvivid_taskmanager::update_backup_sub_task_progress($task_id,'upload',WPVIVID_REMOTE_GOOGLEDRIVE,WPVIVID_UPLOAD_UNDO,'Start uploading '.basename($file).'.',$upload_job['job_data']);
            }


            while (!$status && !feof($handle))
            {
                $chunk = fread($handle, $chunk_size);

                $status = $media->nextChunk($chunk);

                $offset+=strlen($chunk);
                $retry_times=0;

                if((time() - $this -> last_time) >3)
                {
                    if(is_callable($callback))
                    {
                        call_user_func_array($callback,array($offset,$this -> current_file_name,
                            $this->current_file_size,$this -> last_time,$this -> last_size));
                    }
                    $this -> last_size = $offset;
                    $this -> last_time = time();
                }

                $upload_job['job_data'][basename($file)]['resumeUri']=$media->getResumeUri();
                $upload_job['job_data'][basename($file)]['progress']=$media->getProgress();

                //$wpvivid_plugin->wpvivid_log->WriteLog('resumeUri:'.$media->getResumeUri().'.','notice');
                $wpvivid_plugin->wpvivid_log->WriteLog('progress:'.$media->getProgress().'.','notice');
                WPvivid_taskmanager::update_backup_sub_task_progress($task_id,'upload',WPVIVID_REMOTE_GOOGLEDRIVE,WPVIVID_UPLOAD_SUCCESS,'Uploading '.basename($file),$upload_job['job_data']);
            }

            fclose($handle);
            $client->setDefer(false);
            if ($status != false)
            {
                $wpvivid_plugin->wpvivid_log->WriteLog('Finished uploading '.basename($file),'notice');
                $upload_job['job_data'][basename($file)]['uploaded']=1;
                WPvivid_taskmanager::update_backup_sub_task_progress($task_id,'upload',WPVIVID_REMOTE_GOOGLEDRIVE,WPVIVID_UPLOAD_SUCCESS,'Uploading '.basename($file).' completed.',$upload_job['job_data']);
                $wpvivid_plugin->wpvivid_log->WriteLog('Upload success.','notice');
                return array('result' =>WPVIVID_SUCCESS);
            }
            else
            {
                $wpvivid_plugin->wpvivid_log->WriteLog('Upload failed.','notice');
                return array('result' =>WPVIVID_FAILED,'error'=>'Uploading '.$file.' to Google Drive server failed. '.$file.' might be deleted or network doesn\'t work properly. Please verify the file and confirm the network connection and try again later.');
            }
        }
        catch (WPvivid_Google_Service_Exception $e)
        {
            $retry_times++;
            fclose($handle);
            $client->setDefer(false);
            $message = 'A exception ('.get_class($e).') occurred '.esc_html($e->getMessage()).' (Code: '.$e->getCode().', line '.$e->getLine().' in '.$e->getFile().') ';
            if($retry_times < 15)
            {
                $wpvivid_plugin->wpvivid_log->WriteLog('Upload Google_Service_Exception, '.$message.', retry times: '.$retry_times,'notice');
                return $this->_upload($task_id, $file,$client,$service,$folder_id, $callback, $retry_times);
            }
            else
            {
                $wpvivid_plugin->wpvivid_log->WriteLog('Upload Google_Service_Exception, retry times: '.$retry_times,'notice');
                return array('result' =>WPVIVID_FAILED,'error'=>$message);
            }
        }
        catch (InvalidArgumentException $e)
        {
            //need refresh token
            $retry_times++;
            $message = 'A exception ('.get_class($e).') occurred '.esc_html($e->getMessage()).' (Code: '.$e->getCode().', line '.$e->getLine().' in '.$e->getFile().') ';
            $wpvivid_plugin->wpvivid_log->WriteLog('Upload InvalidArgumentException, '.$message.', refresh token','notice');
            $ref=$this->check_token($client, $service);
            if($ref['result']=!WPVIVID_SUCCESS)
            {
                $wpvivid_plugin->wpvivid_log->WriteLog('refresh token failed, error: '.json_encode($ref),'notice');
                return $ref;
            }

            fclose($handle);
            $client->setDefer(false);
            if($retry_times < 15)
            {
                $wpvivid_plugin->wpvivid_log->WriteLog('Refresh token completed, continue upload.','notice');
                return $this->_upload($task_id, $file,$client,$service,$folder_id, $callback, $retry_times);
            }
            else
            {
                return array('result' =>WPVIVID_FAILED,'error'=>$message);
            }
        }
    }

    public function get_client()
    {
        $res = $this -> compare_php_version();
        if($res['result'] == WPVIVID_FAILED){
            return $res;
        }

        $token=$this->options['token'];
        if(isset($this->options['is_encrypt']) && $this->options['is_encrypt'] == 1) {
            $token['access_token'] = base64_decode($this->options['token']['access_token']);
        }

        include_once WPVIVID_PLUGIN_DIR.'/vendor/autoload.php';
        $client = new WPvivid_Google_Client();
        $client->setConfig('access_type','offline');
        $client->setAuthConfig($this->google_drive_secrets);
        $client->addScope(WPvivid_Google_Service_Drive::DRIVE_FILE);//
        $client->setAccessToken($token);

        if ($client->isAccessTokenExpired())
        {
            // Refresh the token if possible, else fetch a new one.
            if ($client->getRefreshToken())
            {
                $tmp_refresh_token = $client->getRefreshToken();
                /*
                $client->fetchAccessTokenWithRefreshToken($client->getRefreshToken());
                $token=$client->getAccessToken();
                */

                if(isset($this->options['is_encrypt']) && $this->options['is_encrypt'] == 1) {
                    $tmp_refresh_token = base64_decode($tmp_refresh_token);
                }

                $args = array(
                    'refresh_token' => $tmp_refresh_token
                );

                $result = wp_remote_post("https://auth.wpvivid.com/google_drive_v2/", array(
                    'timeout' => 60,
                    'body' => $args
                ));

                if (is_wp_error($result))
                {
                    return array('result' => WPVIVID_SUCCESS,'data' => false);
                }
                else
                {
                    $token = wp_remote_retrieve_body($result);
                    $token = json_decode($token, true);
                    if(!is_null($token))
                    {
                        $client->setAccessToken($token);
                    }
                    else
                    {
                        return array('result' => WPVIVID_SUCCESS,'data' => false);
                    }
                }

                $this->options['token']=json_decode(wp_json_encode($token),1);
                $this->options['token']['access_token']=base64_encode($this->options['token']['access_token']);
                $this->options['is_encrypt']=1;
                if(!isset($this->options['token']['refresh_token'])){
                    $this->options['token']['refresh_token'] = base64_encode($tmp_refresh_token);
                }
                else{
                    $this->options['token']['refresh_token']=base64_encode($this->options['token']['refresh_token']);
                }
                WPvivid_Setting::update_remote_option($this->options['id'],$this->options);
                return array('result' => WPVIVID_SUCCESS,'data' => $client);
            }
            else
            {
                return array('result' => WPVIVID_SUCCESS,'data' => false);
            }
        }
        else
        {
            return array('result' => WPVIVID_SUCCESS,'data' => $client);
        }
    }

    private function get_folder($service,$path)
    {
        $response = $service->files->listFiles(array(
            'q' => "name ='".$path."' and 'root' in parents and mimeType = 'application/vnd.google-apps.folder'",
            'fields' => 'nextPageToken, files(id, name,mimeType)',
        ));
        if(sizeof($response->getFiles())==0)
        {
            $fileMetadata = new WPvivid_Google_Service_Drive_DriveFile(array(
                'name' => $path,
                'mimeType' => 'application/vnd.google-apps.folder'));
            $file = $service->files->create($fileMetadata, array(
                'fields' => 'id'));

            return $file->id;
        }
        else
        {
            foreach ($response->getFiles() as $file)
            {
                return $file->getId();
            }
        }

        return false;
    }

    public function set_token()
    {
        $remote_options=WPvivid_Setting::get_remote_option($this->options['id']);
        if($remote_options!==false)
        {
            $this->options['token']=$remote_options['token'];
            if(isset($remote_options['is_encrypt']))
            {
                $this->options['is_encrypt']=$remote_options['is_encrypt'];
            }
        }
    }

    public function download( $file, $local_path, $callback = '')
    {
        try
        {
            global $wpvivid_plugin;
            $this -> current_file_name = $file['file_name'];
            $this -> current_file_size = $file['size'];
            $this->set_token();
            $wpvivid_plugin->wpvivid_download_log->WriteLog('Google Drive get client.','notice');
            $client=$this->get_client();
            if($client['result'] == WPVIVID_FAILED) {
                return $client;
            }
            $client = $client['data'];

            if($client===false)
            {
                return array('result' => WPVIVID_FAILED,'error'=> 'Token refresh failed.');
            }

            $service = new WPvivid_Google_Service_Drive($client);

            $path=$this->options['path'];
            $wpvivid_plugin->wpvivid_download_log->WriteLog('Create local file.','notice');
            $folder_id=$this->get_folder($service,$path);

            if($folder_id==false)
            {
                return array('result' => WPVIVID_FAILED,'error'=> 'Unable to create the local file. Please make sure the folder is writable and try again.');
            }

            $response = $service->files->listFiles(array(
                'q' => "name='".$file['file_name']."' and '".$folder_id."' in parents",
                'fields' => 'files(id,size,webContentLink)'
            ));

            if(sizeof($response->getFiles())==0)
            {
                return array('result' => WPVIVID_FAILED,'error'=> 'Downloading file failed. The file might be deleted or network doesn\'t work properly. Please verify the file and confirm the network connection and try again later.');
            }
            else
            {
                $fileSize=$file['size'];
                $file_id='';
                foreach ($response->getFiles() as $file)
                {
                    $file_id=$file->getId();
                    break;
                }
                $wpvivid_plugin->wpvivid_download_log->WriteLog('Get download url.','notice');
                $download_url=$this->get_download_url($client,$file_id);

                if(!empty($file_id)||!empty($download_url))
                {
                    $file_path = trailingslashit($local_path).$this -> current_file_name;

                    if(file_exists($file_path))
                    {
                        $offset = filesize($file_path);
                    }
                    else
                    {
                        $offset=0;
                    }

                    $fh = fopen($file_path, 'a');
                    $upload_size = WPVIVID_GOOGLEDRIVE_UPLOAD_SIZE;
                    $http = $client->authorize();
                    $wpvivid_plugin->wpvivid_download_log->WriteLog('Downloading file ' . $file['file_name'] . ', Size: ' . $file['size'] ,'notice');
                    while ($offset < $fileSize)
                    {
                        $upload_end=min($offset+$upload_size-1,$fileSize-1);

                        if ($offset > 0)
                        {
                            $options['headers']['Range']='bytes='.$offset.'-'.$upload_end;
                        } else {
                            $options['headers']['Range']='bytes=0-'.$upload_end;
                        }
                        $request = new WPvividGuzzleHttp\Psr7\Request('GET', $download_url,$options['headers']);
                        $http_request = $http->send($request);
                        $http_response=$http_request->getStatusCode();
                        if (200 == $http_response || 206 == $http_response)
                        {
                            fwrite($fh, $http_request->getBody()->getContents(),$upload_size);
                            $offset=$upload_end + 1;
                        }
                        else
                        {
                            throw new Exception('Failed to obtain any new data at size: '.$offset.' http code:'.$http_response);
                        }

                        if((time() - $this -> last_time) >3)
                        {
                            if(is_callable($callback))
                            {
                                call_user_func_array($callback,array($offset,$this -> current_file_name,
                                    $this->current_file_size,$this -> last_time,$this -> last_size));
                            }
                            $this -> last_size = $offset;
                            $this -> last_time = time();
                        }
                    }
                    fclose($fh);
                }
                else
                {
                    return array('result' => WPVIVID_FAILED,'error'=> 'Downloading file failed. The file might be deleted or network doesn\'t work properly. Please verify the file and confirm the network connection and try again later.');
                }
            }
        }catch(Exception $e)
        {
            return array('result' => WPVIVID_FAILED,'error' => $e -> getMessage());
        }
        return array('result' => WPVIVID_SUCCESS);
    }

    public function get_download_url($client,$file_id)
    {
        $http = $client->authorize();
        $url='https://www.googleapis.com/drive/v2/files/'.$file_id;
        $request = new WPvividGuzzleHttp\Psr7\Request('GET', $url);
        $http_request = $http->send($request);

        $http_response=$http_request->getStatusCode();
        if (200 == $http_response)
        {
            $json=$http_request->getBody()->getContents();
            $json=json_decode($json,1);
            $download_url=$json['downloadUrl'];
            return $download_url;
        }
        else
        {
            throw new Exception('Failed to use v2 api');
        }
    }

    public function delete_exist_file($folder_id,$file,$service)
    {
        global $wpvivid_plugin;

        $client=$this->get_client();
        if($client['result'] == WPVIVID_FAILED)
            return false;
        $client = $client['data'];

        if($client===false)
        {
            return false;
        }

        try{
            $delete_files = $service->files->listFiles(array(
                'q' => "name='".$file."' and '".$folder_id."' in parents",
                'fields' => 'nextPageToken, files(id, name,mimeType)',
            ));
            if(sizeof($delete_files->getFiles())==0)
            {
                return true;
            }
            else
            {
                foreach ($delete_files->getFiles() as $file_google_drive)
                {
                    $file_id=$file_google_drive->getId();
                    $service->files->delete($file_id);
                    return true;
                }
            }
        }
        catch(Exception $error)
        {
            $wpvivid_plugin->wpvivid_log->WriteLog('listFiles exception.','notice');
            return true;
        }

        return false;
    }

    public function cleanup($files)
    {
        $client=$this->get_client();
        if($client['result'] == WPVIVID_FAILED)
            return $client;
        $client = $client['data'];

        if($client===false)
        {
            return array('result' => WPVIVID_FAILED,'error'=> 'Token refresh failed.');
        }

        $service = new WPvivid_Google_Service_Drive($client);

        $path=$this->options['path'];
        $folder_id=$this->get_folder($service,$path);

        if($folder_id==false)
        {
            return array('result' => WPVIVID_FAILED,'error'=> 'Unable to create the local file. Please make sure the folder is writable and try again.');
        }

        foreach ($files as $file)
        {
            $delete_files = $service->files->listFiles(array(
                'q' => "name='".$file."' and '".$folder_id."' in parents",
                'fields' => 'nextPageToken, files(id, name,mimeType)',
            ));

            if(sizeof($delete_files->getFiles())==0)
            {
                continue;
            }
            else
            {
                foreach ($delete_files->getFiles() as $file_google_drive)
                {
                    $file_id=$file_google_drive->getId();
                    $service->files->delete($file_id);
                }
            }
        }
        return array('result' =>WPVIVID_SUCCESS);
    }

    public function wpvivid_get_out_of_date_google_drive($out_of_date_remote, $remote)
    {
        if($remote['type'] == WPVIVID_REMOTE_GOOGLEDRIVE){
            $root_path=apply_filters('wpvivid_get_root_path', $remote['type']);
            $out_of_date_remote = $root_path.$remote['path'];
        }
        return $out_of_date_remote;
    }

    public function wpvivid_storage_provider_google_drive($storage_type)
    {
        if($storage_type == WPVIVID_REMOTE_GOOGLEDRIVE){
            $storage_type = 'Google Drive';
        }
        return $storage_type;
    }

    public function wpvivid_get_root_path_google_drive($storage_type){
        if($storage_type == WPVIVID_REMOTE_GOOGLEDRIVE){
            $storage_type = 'root/';
        }
        return $storage_type;
    }
    private function compare_php_version(){
        if(version_compare(WPVIVID_GOOGLE_NEED_PHP_VERSION,phpversion()) > 0){
            return array('result' => WPVIVID_FAILED,error => 'The required PHP version is higher than '.WPVIVID_GOOGLE_NEED_PHP_VERSION.'. After updating your PHP version, please try again.');
        }
        return array('result' => WPVIVID_SUCCESS);
    }

    public function finish_add_remote()
    {
        global $wpvivid_plugin;
        check_ajax_referer( 'wpvivid_ajax', 'nonce' );
        $check=current_user_can('manage_options');
        $check=apply_filters('wpvivid_ajax_check_security',$check);
        if(!$check)
        {
            die();
        }
        try {
            if (empty($_POST) || !isset($_POST['remote']) || !is_string($_POST['remote'])) {
                die();
            }

            $tmp_remote_options = get_transient('google_drive_auth_id');
            if($tmp_remote_options === false)
            {
                die();
            }
            delete_transient('google_drive_auth_id');
            if(empty($tmp_remote_options)||$tmp_remote_options['type']!==WPVIVID_REMOTE_GOOGLEDRIVE)
            {
                die();
            }

            $json = sanitize_text_field($_POST['remote']);
            $json = stripslashes($json);
            $remote_options = json_decode($json, true);
            if (is_null($remote_options)) {
                die();
            }

            $remote_options['path'] = WPVIVID_GOOGLEDRIVE_DEFAULT_FOLDER;
            $remote_options=array_merge($remote_options,$tmp_remote_options);
            if(!class_exists('WPvivid_Remote_collection'))
            {
                include_once WPVIVID_PLUGIN_DIR . '/includes/class-wpvivid-remote-collection.php';
                $wpvivid_plugin->remote_collection=new WPvivid_Remote_collection();
            }
            $ret = $wpvivid_plugin->remote_collection->add_remote($remote_options);

            if ($ret['result'] == 'success') {
                $html = '';
                $html = apply_filters('wpvivid_add_remote_storage_list', $html);
                $ret['html'] = $html;
                $pic = '';
                $pic = apply_filters('wpvivid_schedule_add_remote_pic', $pic);
                $ret['pic'] = $pic;
                $dir = '';
                $dir = apply_filters('wpvivid_get_remote_directory', $dir);
                $ret['dir'] = $dir;
                $schedule_local_remote = '';
                $schedule_local_remote = apply_filters('wpvivid_schedule_local_remote', $schedule_local_remote);
                $ret['local_remote'] = $schedule_local_remote;
                $remote_storage = '';
                $remote_storage = apply_filters('wpvivid_remote_storage', $remote_storage);
                $ret['remote_storage'] = $remote_storage;
                $remote_select_part = '';
                $remote_select_part = apply_filters('wpvivid_remote_storage_select_part', $remote_select_part);
                $ret['remote_select_part'] = $remote_select_part;
                $default = array();
                $remote_array = apply_filters('wpvivid_archieve_remote_array', $default);
                $ret['remote_array'] = $remote_array;
                $success_msg = __('You have successfully added a remote storage.', 'wpvivid-backuprestore');
                $ret['notice'] = apply_filters('wpvivid_add_remote_notice', true, $success_msg);
            }
            else{
                $ret['notice'] = apply_filters('wpvivid_add_remote_notice', false, $ret['error']);
            }

        }
        catch (Exception $error) {
            $message = 'An exception has occurred. class: '.get_class($error).';msg: '.$error->getMessage().';code: '.$error->getCode().';line: '.$error->getLine().';in_file: '.$error->getFile().';';
            error_log($message);
            echo wp_json_encode(array('result'=>'failed','error'=>$message));
            die();
        }
        echo wp_json_encode($ret);
        die();
    }
}includes/customclass/class-wpvivid-sftpclass.php000064400000073040151327705670016237 0ustar00<?php

if (!defined('WPVIVID_PLUGIN_DIR')){
    die;
}
if(!defined('WPVIVID_REMOTE_SFTP'))
    define('WPVIVID_REMOTE_SFTP','sftp');
require_once WPVIVID_PLUGIN_DIR .'/includes/customclass/class-wpvivid-remote.php';

class WPvivid_SFTPClass extends WPvivid_Remote{
    private $package_size = 10;
    private $timeout = 20;
    private $error_str=false;
    private $callback;
    private $options=array();

    public function __construct($options=array())
    {
        if(empty($options))
        {
            add_action('wpvivid_add_storage_tab',array($this,'wpvivid_add_storage_tab_sftp'), 16);
            add_action('wpvivid_add_storage_page',array($this,'wpvivid_add_storage_page_sftp'), 16);
            add_action('wpvivid_edit_remote_page',array($this,'wpvivid_edit_storage_page_sftp'), 16);
            add_filter('wpvivid_remote_pic',array($this,'wpvivid_remote_pic_sftp'),10);
            add_filter('wpvivid_get_out_of_date_remote',array($this,'wpvivid_get_out_of_date_sftp'),10,2);
            add_filter('wpvivid_storage_provider_tran',array($this,'wpvivid_storage_provider_sftp'),10);
        }
        else
        {
            $this->options=$options;
        }
    }

    public function wpvivid_add_storage_tab_sftp()
    {
        ?>
        <div class="storage-providers" remote_type="sftp" onclick="select_remote_storage(event, 'storage_account_sftp');">
            <img src="<?php echo esc_url(WPVIVID_PLUGIN_URL.'/admin/partials/images/storage-sftp.png'); ?>" style="vertical-align:middle;"/><?php esc_html_e('SFTP', 'wpvivid-backuprestore'); ?>
        </div>
        <?php
    }

    public function wpvivid_add_storage_page_sftp()
    {
        ?>
        <div id="storage_account_sftp" class="storage-account-page" style="display:none;">
            <div style="padding: 0 10px 10px 0;">
                <strong><?php esc_html_e('Enter Your SFTP Account', 'wpvivid-backuprestore'); ?></strong>
            </div>
            <table class="wp-list-table widefat plugins" style="width:100%;">
                <tbody>
                <form>
                <tr>
                    <td class="plugin-title column-primary">
                        <div class="wpvivid-storage-form">
                            <input type="text" class="regular-text" autocomplete="off" option="sftp" name="name" placeholder="<?php esc_attr_e('Enter a unique alias: e.g. SFTP-001', 'wpvivid-backuprestore'); ?>" onkeyup="value=value.replace(/[^a-zA-Z0-9\-_]/g,'')" />
                        </div>
                    </td>
                    <td class="column-description desc">
                        <div class="wpvivid-storage-form-desc">
                            <i><?php esc_html_e('A name to help you identify the storage if you have multiple remote storage connected.', 'wpvivid-backuprestore'); ?></i>
                        </div>
                    </td>
                </tr>
                <tr>
                    <td class="plugin-title column-primary">
                        <div class="wpvivid-storage-form">
                            <input type="text" class="regular-text" autocomplete="off" option="sftp" name="host" placeholder="<?php esc_attr_e('Server Address', 'wpvivid-backuprestore'); ?>" />
                        </div>
                    </td>
                    <td class="column-description desc">
                        <div class="wpvivid-storage-form-desc">
                            <i><?php esc_html_e('Enter the server address.', 'wpvivid-backuprestore'); ?></i>
                        </div>
                    </td>
                </tr>
                <tr>
                    <td class="plugin-title column-primary">
                        <div class="wpvivid-storage-form">
                            <input type="text" class="regular-text" autocomplete="off" option="sftp" name="username" placeholder="<?php esc_attr_e('User Name', 'wpvivid-backuprestore'); ?>" />
                        </div>
                    </td>
                    <td class="column-description desc">
                        <div class="wpvivid-storage-form-desc">
                            <i><?php esc_html_e('Enter the user name.', 'wpvivid-backuprestore'); ?></i>
                        </div>
                    </td>
                </tr>
                <tr>
                    <td class="plugin-title column-primary">
                        <div class="wpvivid-storage-form">
                            <input type="password" class="regular-text" autocomplete="new-password" option="sftp" name="password" placeholder="<?php esc_attr_e('User Password', 'wpvivid-backuprestore'); ?>" />
                        </div>
                    </td>
                    <td class="column-description desc">
                        <div class="wpvivid-storage-form-desc">
                            <i><?php esc_html_e('Enter the user password.', 'wpvivid-backuprestore'); ?></i>
                        </div>
                    </td>
                </tr>
                <tr>
                    <td class="plugin-title column-primary">
                        <div class="wpvivid-storage-form">
                            <input type="text" class="regular-text" autocomplete="off" option="sftp" name="port" placeholder="<?php esc_attr_e('Port', 'wpvivid-backuprestore'); ?>" onkeyup="value=value.replace(/\D/g,'')" />
                        </div>
                    </td>
                    <td class="column-description desc">
                        <div class="wpvivid-storage-form-desc">
                            <i><?php esc_html_e('Enter the server port.', 'wpvivid-backuprestore'); ?></i>
                        </div>
                    </td>
                </tr>
                <tr>
                    <td class="plugin-title column-primary">
                        <div class="wpvivid-storage-form">
                            <input type="text" class="regular-text" autocomplete="off" option="sftp" name="path" placeholder="<?php esc_attr_e('Absolute path must exist(e.g. /var)', 'wpvivid-backuprestore'); ?>" />
                        </div>
                    </td>
                    <td class="column-description desc">
                        <div class="wpvivid-storage-form-desc">
                            <i><?php esc_html_e('Enter an absolute path and a custom subdirectory (optional) for holding the backups of current website. For example, /var/customfolder/', 'wpvivid-backuprestore'); ?></i>
                        </div>
                    </td>
                </tr>
                <tr>
                    <td class="plugin-title column-primary">
                        <div class="wpvivid-storage-select">
                            <label>
                                <input type="checkbox" option="sftp" name="default" checked /><?php esc_html_e('Set as the default remote storage.', 'wpvivid-backuprestore'); ?>
                            </label>
                        </div>
                    </td>
                    <td class="column-description desc">
                        <div class="wpvivid-storage-form-desc">
                            <i><?php esc_html_e('Once checked, all this sites backups sent to a remote storage destination will be uploaded to this storage by default.', 'wpvivid-backuprestore'); ?></i>
                        </div>
                    </td>
                </tr>
                </form>
                <tr>
                    <td class="plugin-title column-primary">
                        <div class="wpvivid-storage-form">
                            <input class="button-primary" option="add-remote" type="submit" value="<?php esc_attr_e('Test and Add', 'wpvivid-backuprestore'); ?>" />
                        </div>
                    </td>
                    <td class="column-description desc">
                        <div class="wpvivid-storage-form-desc">
                            <i><?php esc_html_e('Click the button to connect to SFTP server and add it to the storage list below.', 'wpvivid-backuprestore'); ?></i>
                        </div>
                    </td>
                </tr>
                </tbody>
            </table>
        </div>
        <?php
    }

    public function wpvivid_edit_storage_page_sftp()
    {
        ?>
        <div id="remote_storage_edit_sftp" class="postbox storage-account-block remote-storage-edit" style="display:none;">
            <div style="padding: 0 10px 10px 0;">
                <strong><?php esc_html_e('Enter Your SFTP Account', 'wpvivid-backuprestore'); ?></strong>
            </div>
            <table class="wp-list-table widefat plugins" style="width:100%;">
                <tbody>
                <form>
                <tr>
                    <td class="plugin-title column-primary">
                        <div class="wpvivid-storage-form">
                            <input type="text" class="regular-text" autocomplete="off" option="edit-sftp" name="name" placeholder="<?php esc_attr_e('Enter a unique alias: e.g. SFTP-001', 'wpvivid-backuprestore'); ?>" onkeyup="value=value.replace(/[^a-zA-Z0-9\-_]/g,'')" />
                        </div>
                    </td>
                    <td class="column-description desc">
                        <div class="wpvivid-storage-form-desc">
                            <i><?php esc_html_e('A name to help you identify the storage if you have multiple remote storage connected.', 'wpvivid-backuprestore'); ?></i>
                        </div>
                    </td>
                </tr>
                <tr>
                    <td class="plugin-title column-primary">
                        <div class="wpvivid-storage-form">
                            <input type="text" class="regular-text" autocomplete="off" option="edit-sftp" name="host" placeholder="<?php esc_attr_e('Server Address', 'wpvivid-backuprestore'); ?>" />
                        </div>
                    </td>
                    <td class="column-description desc">
                        <div class="wpvivid-storage-form-desc">
                            <i><?php esc_html_e('Enter the server address.', 'wpvivid-backuprestore'); ?></i>
                        </div>
                    </td>
                </tr>
                <tr>
                    <td class="plugin-title column-primary">
                        <div class="wpvivid-storage-form">
                            <input type="text" class="regular-text" autocomplete="off" option="edit-sftp" name="username" placeholder="<?php esc_attr_e('User Name', 'wpvivid-backuprestore'); ?>" />
                        </div>
                    </td>
                    <td class="column-description desc">
                        <div class="wpvivid-storage-form-desc">
                            <i><?php esc_html_e('Enter the user name.', 'wpvivid-backuprestore'); ?></i>
                        </div>
                    </td>
                </tr>
                <tr>
                    <td class="plugin-title column-primary">
                        <div class="wpvivid-storage-form">
                            <input type="password" class="regular-text" autocomplete="new-password" option="edit-sftp" name="password" placeholder="<?php esc_attr_e('User Password', 'wpvivid-backuprestore'); ?>" />
                        </div>
                    </td>
                    <td class="column-description desc">
                        <div class="wpvivid-storage-form-desc">
                            <i><?php esc_html_e('Enter the user password.', 'wpvivid-backuprestore'); ?></i>
                        </div>
                    </td>
                </tr>
                <tr>
                    <td class="plugin-title column-primary">
                        <div class="wpvivid-storage-form">
                            <input type="text" class="regular-text" autocomplete="off" option="edit-sftp" name="port" placeholder="<?php esc_attr_e('Port', 'wpvivid-backuprestore'); ?>" onkeyup="value=value.replace(/\D/g,'')" />
                        </div>
                    </td>
                    <td class="column-description desc">
                        <div class="wpvivid-storage-form-desc">
                            <i><?php esc_html_e('Enter the server port.', 'wpvivid-backuprestore'); ?></i>
                        </div>
                    </td>
                </tr>
                <tr>
                    <td class="plugin-title column-primary">
                        <div class="wpvivid-storage-form">
                            <input type="text" class="regular-text" autocomplete="off" option="edit-sftp" name="path" placeholder="<?php esc_attr_e('Absolute path must exist(e.g. /var)', 'wpvivid-backuprestore'); ?>" />
                        </div>
                    </td>
                    <td class="column-description desc">
                        <div class="wpvivid-storage-form-desc">
                            <i><?php esc_html_e('Enter an absolute path and a custom subdirectory (optional) for holding the backups of current website. For example, /var/customfolder/', 'wpvivid-backuprestore'); ?></i>
                        </div>
                    </td>
                </tr>
                </form>
                <tr>
                    <td class="plugin-title column-primary">
                        <div class="wpvivid-storage-form">
                            <input class="button-primary" option="edit-remote" type="submit" value="<?php esc_attr_e('Save Changes', 'wpvivid-backuprestore'); ?>" />
                        </div>
                    </td>
                    <td class="column-description desc">
                        <div class="wpvivid-storage-form-desc">
                            <i><?php esc_html_e('Click the button to save the changes.', 'wpvivid-backuprestore'); ?></i>
                        </div>
                    </td>
                </tr>
                </tbody>
            </table>
        </div>
        <?php
    }

    public function wpvivid_remote_pic_sftp($remote)
    {
        $remote['sftp']['default_pic'] = '/admin/partials/images/storage-sftp(gray).png';
        $remote['sftp']['selected_pic'] = '/admin/partials/images/storage-sftp.png';
        $remote['sftp']['title'] = 'SFTP';
        return $remote;
    }

    public function test_connect()
    {
        $host = $this->options['host'];
        $username = $this->options['username'];
        if(isset($this->options['is_encrypt']) && $this->options['is_encrypt'] == 1){
            $password = base64_decode($this->options['password']);
        }
        else {
            $password = $this->options['password'];
        }
        $path = $this->options['path'];

        $port = empty($this->options['port'])?22:$this->options['port'];

        $conn = $this->do_connect($host,$username,$password,$port);
        if(!is_subclass_of($conn,'Net_SSH2'))
        {
            return $conn;
        }
        $str = $this->do_chdir($conn,$path);
        if($str['result'] == WPVIVID_SUCCESS)
        {
            if($conn->put(trailingslashit($path) . 'testfile', 'test data', NET_SFTP_STRING))
            {
                $this -> _delete($conn ,trailingslashit($path) . 'testfile');
                return array('result'=>WPVIVID_SUCCESS);
            }
            return array('result'=>WPVIVID_FAILED,'error'=>'Failed to create a test file. Please try again later.');
        }else{
            return $str;
        }
    }

    public function sanitize_options($skip_name='')
    {
        $ret['result']=WPVIVID_FAILED;
        if(!isset($this->options['name']))
        {
            $ret['error']="Warning: An alias for remote storage is required.";
            return $ret;
        }

        $this->options['name']=sanitize_text_field($this->options['name']);

        if(empty($this->options['name']))
        {
            $ret['error']="Warning: An alias for remote storage is required.";
            return $ret;
        }

        $remoteslist=WPvivid_Setting::get_all_remote_options();
        foreach ($remoteslist as $key=>$value)
        {
            if(isset($value['name'])&&$value['name'] == $this->options['name']&&$skip_name!=$value['name'])
            {
                $ret['error']="Warning: The alias already exists in storage list.";
                return $ret;
            }
        }

        if(!isset($this->options['host']))
        {
            $ret['error']="Warning: The IP Address is required.";
            return $ret;
        }

        $this->options['host']=sanitize_text_field($this->options['host']);

        if(empty($this->options['host']))
        {
            $ret['error']="Warning: The IP Address is required.";
            return $ret;
        }

        if(!isset($this->options['username']))
        {
            $ret['error']="Warning: The username is required.";
            return $ret;
        }

        $this->options['username']=sanitize_text_field($this->options['username']);

        if(empty($this->options['username']))
        {
            $ret['error']="Warning: The username is required.";
            return $ret;
        }

        if(!isset($this->options['password'])||empty($this->options['password']))
        {
            $ret['error']="Warning: The password is required.";
            return $ret;
        }

        //$this->options['password']=sanitize_text_field($this->options['password']);

        if(empty($this->options['password']))
        {
            $ret['error']="Warning: The password is required.";
            return $ret;
        }
        $this->options['password'] = base64_encode($this->options['password']);
        $this->options['is_encrypt'] = 1;

        if(!isset($this->options['port']))
        {
            $ret['error']="Warning: The port number is required.";
            return $ret;
        }

        $this->options['port']=sanitize_text_field($this->options['port']);

        if(empty($this->options['port']))
        {
            $ret['error']="Warning: The port number is required.";
            return $ret;
        }

        if(!isset($this->options['path'])||empty($this->options['path']))
        {
            $ret['error']="Warning: The storage path is required.";
            return $ret;
        }

        $this->options['path']=sanitize_text_field($this->options['path']);

        if(empty($this->options['path']))
        {
            $ret['error']="Warning: The storage path is required.";
            return $ret;
        }

        $ret['result']=WPVIVID_SUCCESS;
        $ret['options']=$this->options;
        return $ret;
    }

	function do_connect($host,$username,$password,$port)
    {
        include_once WPVIVID_PLUGIN_DIR . '/includes/customclass/class-wpvivid-extend-sftp.php';
        $conn = new WPvivid_Net_SFTP($host,$port,$this -> timeout);
        $conn -> setTimeout($this->timeout);
        $ret = $conn->login($username,$password);
        if(!$ret)
        {
            return array('result'=>WPVIVID_FAILED,'error'=>'The connection failed because of incorrect credentials or server connection timeout. Please try again.');
        }

		return $conn;
	}

	function do_chdir($conn,$path)
    {
        @$conn->mkdir($path);
        // See if the directory now exists
        if (!$conn->chdir($path))
        {
            @$conn->disconnect();
            return array('result'=>WPVIVID_FAILED,'error'=>'Failed to create a backup. Make sure you have sufficient privileges to perform the operation.');
        }

		return array('result'=>WPVIVID_SUCCESS);
	}

	function _delete($conn , $file)
    {
        $result = $conn ->delete($file , true);
		return $result;
	}

    public function upload($task_id,$files,$callback='')
    {
        global $wpvivid_plugin;
        $this -> callback = $callback;
        if(empty($this->options['port']))
            $this->options['port'] = 22;
        $host = $this->options['host'];
        $username = $this->options['username'];
        if(isset($this->options['is_encrypt']) && $this->options['is_encrypt'] == 1){
            $password = base64_decode($this->options['password']);
        }
        else {
            $password = $this->options['password'];
        }
        $path = $this->options['path'];
        $port = $this->options['port'];

        $upload_job=WPvivid_taskmanager::get_backup_sub_task_progress($task_id,'upload',WPVIVID_REMOTE_SFTP);

        if(empty($upload_job))
        {
            $job_data=array();
            foreach ($files as $file)
            {
                $file_data['size']=filesize($file);
                $file_data['uploaded']=0;
                $job_data[basename($file)]=$file_data;
            }
            WPvivid_taskmanager::update_backup_sub_task_progress($task_id,'upload',WPVIVID_REMOTE_SFTP,WPVIVID_UPLOAD_UNDO,'Start uploading',$job_data);
            $upload_job=WPvivid_taskmanager::get_backup_sub_task_progress($task_id,'upload',WPVIVID_REMOTE_SFTP);
        }

        $wpvivid_plugin->wpvivid_log->WriteLog('Connecting to server '.$host,'notice');
        $conn = $this->do_connect($host,$username,$password,$port);

        if(is_array($conn) && $conn['result'] ==WPVIVID_FAILED)
        {
            return $conn;
        }
        $wpvivid_plugin->wpvivid_log->WriteLog('chdir '.$path,'notice');
		$str = $this->do_chdir($conn,$path);
		if($str['result'] == WPVIVID_FAILED)
		    return $str;

		foreach ($files as $key => $file)
		{
		    if(is_array($upload_job['job_data']) &&array_key_exists(basename($file),$upload_job['job_data']))
            {
                if($upload_job['job_data'][basename($file)]['uploaded']==1)
                    continue;
            }
            $wpvivid_plugin->wpvivid_log->WriteLog('Start uploading '.basename($file),'notice');
		    $this -> last_time = time();
		    $this -> last_size = 0;

			if(!file_exists($file))
				return array('result'=>WPVIVID_FAILED,'error'=>$file.' not found. The file might has been moved, renamed or deleted. Please back it up again.');

            $wpvivid_plugin->set_time_limit($task_id);

			for($i =0;$i <WPVIVID_REMOTE_CONNECT_RETRY_TIMES;$i ++)
			{
                $this -> last_time = time();
                $this->current_file_name=basename($file);
                $this -> current_file_size = filesize($file);

                WPvivid_taskmanager::update_backup_sub_task_progress($task_id,'upload',WPVIVID_REMOTE_SFTP,WPVIVID_UPLOAD_UNDO,'Start uploading '.basename($file).'.',$upload_job['job_data']);

                $result = $conn->put(trailingslashit($path) . basename($file), $file, NET_SFTP_LOCAL_FILE| NET_SFTP_RESUME_START, -1, -1, array($this , 'upload_callback'));

                if($result)
                {
                    WPvivid_taskmanager::wpvivid_reset_backup_retry_times($task_id);
                    $wpvivid_plugin->wpvivid_log->WriteLog('Finished uploading '.basename($file),'notice');
                    $upload_job['job_data'][basename($file)]['uploaded']=1;
                    WPvivid_taskmanager::update_backup_sub_task_progress($task_id,'upload',WPVIVID_REMOTE_SFTP,WPVIVID_UPLOAD_SUCCESS,'Uploading '.basename($file).' completed.',$upload_job['job_data']);
                    break;
                }

                if(!$result && $i == (WPVIVID_REMOTE_CONNECT_RETRY_TIMES - 1))
                {
                    $conn -> disconnect();
                    return array('result'=>WPVIVID_FAILED,'error'=>'Uploading '.$file.' to SFTP server failed. '.$file.' might be deleted or network doesn\'t work properly. Please verify the file and confirm the network connection and try again later.');
                }
                sleep(WPVIVID_REMOTE_CONNECT_RETRY_INTERVAL);
            }
		}
		$conn -> disconnect();
		return array('result'=>WPVIVID_SUCCESS);
	}

    public function download($file,$local_path,$callback = '')
    {
        try {
            global $wpvivid_plugin;
            $this->callback = $callback;
            $this->current_file_name = $file['file_name'];
            $this->current_file_size = $file['size'];

            $host = $this->options['host'];
            $username = $this->options['username'];
            if(isset($this->options['is_encrypt']) && $this->options['is_encrypt'] == 1){
                $password = base64_decode($this->options['password']);
            }
            else {
                $password = $this->options['password'];
            }
            $path = $this->options['path'];
            $port = empty($this->options['port']) ? 22 : $this->options['port'];
            $local_path = trailingslashit($local_path) . $file['file_name'];
            $file_size = $file['size'];
            $remote_file_name = trailingslashit($path) . $file['file_name'];

            $wpvivid_plugin->wpvivid_download_log->WriteLog('Connecting SFTP server.','notice');
            $conn = $this->do_connect($host, $username, $password, $port);
            $progress = 0;
            if (!is_subclass_of($conn, 'Net_SSH2')) {
                return $conn;
            }
            $wpvivid_plugin->wpvivid_download_log->WriteLog('Create local file.','notice');
            $local_file = fopen($local_path, 'ab');
            if (!$local_file) {
                return array('result' => WPVIVID_FAILED, 'error' => 'Unable to create the local file. Please make sure the folder is writable and try again.');
            }
            $stat = fstat($local_file);
            $offset = $stat['size'];
            $progress = floor(($offset / $file_size) * 100);

            $wpvivid_plugin->wpvivid_download_log->WriteLog('Downloading file ' . $file['file_name'] . ', Size: ' . $file['size'] ,'notice');
            $result = $conn->get($remote_file_name, $local_file, $offset, -1, array($this, 'download_callback'));
            @fclose($local_file);

            if(filesize($local_path) == $file['size']){
                if($wpvivid_plugin->wpvivid_check_zip_valid()) {
                    $res = TRUE;
                }
                else{
                    $res = FALSE;
                }
            }
            else{
                $res = FALSE;
            }

            if ($result && $res) {
                return array('result' => WPVIVID_SUCCESS);
            } else {
                return array('result' => WPVIVID_FAILED, 'error' => 'Downloading ' . $remote_file_name . ' failed. ' . $remote_file_name . ' might be deleted or network doesn\'t work properly. Please verify the file and confirm the network connection and try again later.');
            }
        }
        catch (Exception $error){
            $message = 'An exception has occurred. class: '.get_class($error).';msg: '.$error->getMessage().';code: '.$error->getCode().';line: '.$error->getLine().';in_file: '.$error->getFile().';';
            error_log($message);
            return array('result'=>WPVIVID_FAILED, 'error'=>$message);
        }
    }

    public function delete($remote,$files){
        $host = $remote['options']['host'];
        $username = $remote['options']['username'];
        if(isset($remote['options']['is_encrypt']) && $remote['options']['is_encrypt'] == 1){
            $password = base64_decode($remote['options']['password']);
        }
        else {
            $password = $remote['options']['password'];
        }
        $path = $remote['options']['path'];
        $port = empty($remote['options']['port'])?22:$remote['options']['port'];

	    $conn = $this->do_connect($host,$username,$password,$port);
	    if(!is_subclass_of($conn,'Net_SSH2')){
		    return $conn;
	    }
	    foreach ($files as $file)
	    {
            $file=trailingslashit($path).$file;
            $this -> _delete($conn , $file);
        }
	    return array('result'=>WPVIVID_SUCCESS);
    }
    public function get_last_error()
    {
        if($this->error_str===false)
        {
            $this->error_str='connection time out.';
        }
        return $this->error_str;
    }
    public function upload_callback($offset){
        if((time() - $this -> last_time) >3)
        {
            if(is_callable($this -> callback)){
                call_user_func_array($this -> callback,array($offset,$this -> current_file_name,
                    $this->current_file_size,$this -> last_time,$this -> last_size));
            }
            $this -> last_size = $offset;
            $this -> last_time = time();
        }
    }
    public function download_callback($offset){
        if((time() - $this -> last_time) >3){
            if(is_callable($this -> callback)){
                call_user_func_array($this -> callback,array($offset,$this -> current_file_name,
                    $this->current_file_size,$this -> last_time,$this -> last_size));
            }
            $this -> last_size = $offset;
            $this -> last_time = time();
        }
    }

    public function cleanup($files)
    {
        $remote['options'] = $this -> options;
        return $this -> delete($remote,$files);
    }

    public function wpvivid_get_out_of_date_sftp($out_of_date_remote, $remote)
    {
        if($remote['type'] == WPVIVID_REMOTE_SFTP){
            $out_of_date_remote = $remote['path'];
        }
        return $out_of_date_remote;
    }

    public function wpvivid_storage_provider_sftp($storage_type)
    {
        if($storage_type == WPVIVID_REMOTE_SFTP){
            $storage_type = 'SFTP';
        }
        return $storage_type;
    }
}includes/class-wpvivid-importer.php000064400000306350151327705670013541 0ustar00<?php
/**
 * WPvivid addon: yes
 * Addon Name: wpvivid-backup-pro-all-in-one
 * Description: Pro
 * Version: 1.9.1
 * Need_init: yes
 * Interface Name: WPvivid_media_importer
 */

if ( ! class_exists( 'WP_List_Table' ) )
{
    require_once( ABSPATH . 'wp-admin/includes/class-wp-list-table.php' );
}

class WPvivid_Export_List extends WP_List_Table
{
    public $list;
    public $page_num;
    public $parent;

    public function __construct( $args = array() )
    {
        global $wpdb;
        parent::__construct(
            array(
                'plural' => 'import',
                'screen' => 'import',
            )
        );
    }

    public function set_parent($parent)
    {
        $this->parent=$parent;
    }

    public function set_list($list, $page_num=1)
    {
        $this->list=$list;
        $this->page_num=$page_num;
    }

    public function get_columns()
    {
        $posts_columns = array();

        $posts_columns['file_name'] = __( 'File Name', 'wpvivid-backuprestore' );
        $posts_columns['export_type'] = __( 'Post Types', 'wpvivid-backuprestore' );
        $posts_columns['posts_count'] = __( 'Count', 'wpvivid-backuprestore' );
        $posts_columns['media_size'] = __( 'Media Files Size', 'wpvivid-backuprestore' );
        $posts_columns['import'] = __( 'Action', 'wpvivid-backuprestore' );

        return $posts_columns;
    }

    protected function display_tablenav( $which ) {
        $total_items =sizeof($this->list);
        if($total_items > 10) {
            if ('top' === $which) {
                wp_nonce_field('bulk-' . $this->_args['plural']);
            }
            ?>
            <div class="tablenav <?php echo esc_attr($which); ?>">

                <?php if ($this->has_items()) : ?>
                    <div class="alignleft actions bulkactions">
                        <?php $this->bulk_actions($which); ?>
                    </div>
                <?php
                endif;
                $this->extra_tablenav($which);
                $this->pagination($which);
                ?>

                <br class="clear"/>
            </div>
            <?php
        }
    }

    function prepare_items()
    {
        $columns = $this->get_columns();
        $hidden = array();
        $sortable = array();
        $this->_column_headers = array($columns, $hidden, $sortable);

        $total_items =sizeof($this->list);
        if($total_items > 10) {
            $this->set_pagination_args(
                array(
                    'total_items' => $total_items,
                    'per_page' => 10,
                )
            );
        }
    }

    public function has_items()
    {
        return !empty($this->list);
    }

    public function column_cb( $item )
    {
        ?>
        <input id="cb-select-<?php echo esc_attr($item['id']); ?>" type="checkbox" name="export[]" value="<?php echo esc_attr($item['id']); ?>"/>
        <?php
    }

    public function _column_file_name( $item, $classes, $data, $primary )
    {
        echo '<td>                 
                    <div>
                        '.esc_html($item['file_name']).'
                    </div>
                     <div style="padding-bottom: 5px;">
                        <div class="backuptime">Data Modified: ' .esc_html(gmdate('M-d-Y H:i', $item['time'])) . '</div>              
                    </div>
                </td>';
    }

    public function _column_export_type( $item, $classes, $data, $primary )
    {
        $export = $item['export_type'] === 'page' ? 'Page' : 'Post';
        echo '<td style="color: #000;">              
                    <div>
                        <div style="float:left;padding:10px 10px 10px 0;">';
        esc_html_e('Type: ', 'wpvivid-backuprestore');
        echo esc_html($export).'</div>
                    </div> 
              </td>';
    }

    public function _column_posts_count( $item, $classes, $data, $primary )
    {
        echo '<td style="min-width:100px;">
                    <div style="float:left;padding:10px 10px 10px 0;">
                        '.esc_html($item['posts_count']).'
                    </div>
                </td>';
    }

    public function _column_media_size( $item, $classes, $data, $primary )
    {
        echo '<td style="min-width:100px;">
                    <div style="float:left;padding:10px 10px 10px 0;">
                        '.esc_html($item['media_size']).'
                    </div>
                </td>';
    }

    public function _column_import( $item )
    {
        echo '<td style="min-width:100px;">
                   <div class="export-list-import" style="cursor:pointer;padding:10px 0 10px 0;">
                        <img src="' . esc_url(WPVIVID_PLUGIN_URL . '/admin/partials/images/Restore.png') . '" style="vertical-align:middle;" /><span>' ; esc_html_e('Import', 'wpvivid-backuprestore') ;
                        echo '</span>
                   </div>                
               </td>';
    }

    public function display_rows()
    {
        $this->_display_rows( $this->list );
    }

    private function _display_rows($lists)
    {
        $page_lists=$lists;
        $page=$this->get_pagenum();
        $count=0;
        while ( $count<$page )
        {
            $page_lists = array_splice( $lists, 0, 10);
            $count++;
        }
        foreach ( $page_lists as $key=>$item )
        {
            $item['id']=$key;
            $this->single_row($item);
        }
        ?>
        <?php
    }

    public function get_pagenum()
    {
        if($this->page_num=='first')
        {
            $this->page_num=1;
        }
        else if($this->page_num=='last')
        {
            $this->page_num=$this->_pagination_args['total_pages'];
        }
        $pagenum = $this->page_num ? $this->page_num : 0;

        if ( isset( $this->_pagination_args['total_pages'] ) && $pagenum > $this->_pagination_args['total_pages'] )
        {
            $pagenum = $this->_pagination_args['total_pages'];
        }

        return max( 1, $pagenum );
    }

    public function single_row($item)
    {
        ?>
        <tr id="<?php echo esc_attr($item['id']) ?>" class="wpvivid-export-list-item">
            <?php $this->single_row_columns( $item ); ?>
        </tr>
        <?php
    }

    protected function pagination( $which ) {
        if ( empty( $this->_pagination_args ) ) {
            return;
        }

        $total_items     = $this->_pagination_args['total_items'];
        $total_pages     = $this->_pagination_args['total_pages'];
        $infinite_scroll = false;
        if ( isset( $this->_pagination_args['infinite_scroll'] ) ) {
            $infinite_scroll = $this->_pagination_args['infinite_scroll'];
        }

        if ( 'top' === $which && $total_pages > 1 ) {
            $this->screen->render_screen_reader_content( 'heading_pagination' );
        }

        $output = '<span class="displaying-num">' . sprintf( _n( '%s item', '%s items', $total_items, 'wpvivid-backuprestore' ), number_format_i18n( $total_items ) ) . '</span>';

        $current              = $this->get_pagenum();
        $removable_query_args = wp_removable_query_args();

        $current_url = set_url_scheme( 'http://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'] );

        $current_url = remove_query_arg( $removable_query_args, $current_url );

        $page_links = array();

        $total_pages_before = '<span class="paging-input">';
        $total_pages_after  = '</span></span>';

        $disable_first = $disable_last = $disable_prev = $disable_next = false;

        if ( $current == 1 ) {
            $disable_first = true;
            $disable_prev  = true;
        }
        if ( $current == 2 ) {
            $disable_first = true;
        }
        if ( $current == $total_pages ) {
            $disable_last = true;
            $disable_next = true;
        }
        if ( $current == $total_pages - 1 ) {
            $disable_last = true;
        }

        if ( $disable_first ) {
            $page_links[] = '<span class="tablenav-pages-navspan button disabled" aria-hidden="true">&laquo;</span>';
        } else {
            $page_links[] = sprintf(
                "<div class='first-page button'><span class='screen-reader-text'>%s</span><span aria-hidden='true'>%s</span></div>",
                __( 'First page', 'wpvivid-backuprestore' ),
                '&laquo;'
            );
        }

        if ( $disable_prev ) {
            $page_links[] = '<span class="tablenav-pages-navspan button disabled" aria-hidden="true">&lsaquo;</span>';
        } else {
            $page_links[] = sprintf(
                "<div class='prev-page button' value='%s'><span class='screen-reader-text'>%s</span><span aria-hidden='true'>%s</span></div>",
                $current,
                __( 'Previous page', 'wpvivid-backuprestore' ),
                '&lsaquo;'
            );
        }

        if ( 'bottom' === $which ) {
            $html_current_page  = $current;
            $total_pages_before = '<span class="screen-reader-text">' . __( 'Current Page', 'wpvivid-backuprestore' ) . '</span><span id="table-paging" class="paging-input"><span class="tablenav-paging-text">';
        } else {
            $html_current_page = sprintf(
                "%s<input class='current-page' id='current-page-selector-import' type='text' name='paged' value='%s' size='%d' aria-describedby='table-paging' /><span class='tablenav-paging-text'>",
                '<label for="current-page-selector-import" class="screen-reader-text">' . __( 'Current Page', 'wpvivid-backuprestore' ) . '</label>',
                $current,
                strlen( $total_pages )
            );
        }
        $html_total_pages = sprintf( "<span class='total-pages'>%s</span>", number_format_i18n( $total_pages ) );
        $page_links[]     = $total_pages_before . sprintf( _x( '%1$s of %2$s', 'paging', 'wpvivid-backuprestore' ), $html_current_page, $html_total_pages ) . $total_pages_after;

        if ( $disable_next ) {
            $page_links[] = '<span class="tablenav-pages-navspan button disabled" aria-hidden="true">&rsaquo;</span>';
        } else {
            $page_links[] = sprintf(
                "<div class='next-page button' value='%s'><span class='screen-reader-text'>%s</span><span aria-hidden='true'>%s</span></div>",
                $current,
                __( 'Next page', 'wpvivid-backuprestore' ),
                '&rsaquo;'
            );
        }

        if ( $disable_last ) {
            $page_links[] = '<span class="tablenav-pages-navspan button disabled" aria-hidden="true">&raquo;</span>';
        } else {
            $page_links[] = sprintf(
                "<div class='last-page button'><span class='screen-reader-text'>%s</span><span aria-hidden='true'>%s</span></div>",
                __( 'Last page', 'wpvivid-backuprestore' ),
                '&raquo;'
            );
        }

        $pagination_links_class = 'pagination-links';
        if ( ! empty( $infinite_scroll ) ) {
            $pagination_links_class .= ' hide-if-js';
        }
        $output .= "\n<span class='$pagination_links_class'>" . join( "\n", $page_links ) . '</span>';

        if ( $total_pages ) {
            $page_class = $total_pages < 2 ? ' one-page' : '';
        } else {
            $page_class = ' no-pages';
        }
        $this->_pagination = "<div class='tablenav-pages{$page_class}'>$output</div>";

        echo $this->_pagination;
    }
}

class WPvivid_Impoter_taskmanager
{
    public static function new_task($task_id,$files,$options)
    {
        $task['id']=$task_id;
        $task['status']['start_time']=time();
        $task['status']['run_time']=time();
        $task['status']['timeout']=time();
        $task['status']['str']='ready';
        $task['status']['resume_count']=0;
        $task['options']=$options;
        $task['data']['files']=$files;
        self::update_task($task_id,$task);
    }

    public static function get_files($task_id)
    {
        $task=self::get_task($task_id);
        return  $task['data']['files'];
    }

    public static function get_options($task_id)
    {
        $task=self::get_task($task_id);
        return  $task['options'];
    }

    public static function get_tasks(){
        $default = array();
        return $options = get_option('wpvivid_importer_task_list', $default);
    }

    public static function get_task($task_id)
    {
        $default = array();
        $tasks = get_option('wpvivid_importer_task_list', $default);
        if(array_key_exists ($task_id, $tasks)) {
            return $tasks[$task_id];
        }
        else {
            return false;
        }
    }

    public static function update_task($task_id, $task)
    {
        $default = array();
        $options = get_option('wpvivid_importer_task_list', $default);
        $options[$task_id]=$task;
        WPvivid_Setting::update_option('wpvivid_importer_task_list', $options);
    }

    public static function delete_task($task_id){
        $options = get_option('wpvivid_importer_task_list', array());
        unset($options[$task_id]);
        WPvivid_Setting::update_option('wpvivid_importer_task_list', $options);
    }

    public static function get_import_task_status($task_id){
        $tasks=self::get_tasks();
        if(array_key_exists ($task_id, $tasks)) {
            $task = $tasks[$task_id];
            return $task['status']['str'];
        }
        else {
            return false;
        }
    }

    public static function update_import_task_status($task_id, $status, $reset_start_time=false, $reset_timeout=false, $resume_count=false, $error=''){
        $tasks=self::get_tasks();
        if(array_key_exists ($task_id, $tasks))
        {
            $task = $tasks[$task_id];
            $task['status']['run_time']=time();
            if($reset_start_time)
                $task['status']['start_time']=time();
            if(!empty($status)) {
                $task['status']['str']=$status;
            }
            if($reset_timeout)
                $task['status']['timeout']=time();
            if($resume_count!==false) {
                $task['status']['resume_count']=$resume_count;
            }

            if(!empty($error)) {
                $task['status']['error']=$error;
            }
            self::update_task($task_id, $task);
            return $task;
        }
        else {
            return false;
        }
    }
}

class WPvivid_import_data
{
    public $import_log = false;
    public $import_log_file;

    public function __construct()
    {
        $this->import_log_file = WP_CONTENT_DIR.DIRECTORY_SEPARATOR.WPvivid_Setting::get_backupdir().DIRECTORY_SEPARATOR.WPVIVID_IMPORT_EXPORT_DIR.DIRECTORY_SEPARATOR.'wpvivid_import_log.txt';
    }

    public function wpvivid_create_import_log()
    {
        $this->import_log=new WPvivid_Log();
        $this->import_log->CreateLogFile($this->import_log_file, 'has_folder', 'import');
    }

    public function wpvivid_write_import_log($message, $type)
    {
        if($this->import_log===false)
        {
            $this->import_log=new WPvivid_Log();
            $this->import_log->OpenLogFile($this->import_log_file,'has_folder');
        }

        clearstatcache();
        if(filesize($this->import_log_file)>4*1024*1024)
        {
            $this->import_log->CloseFile();
            wp_delete_file($this->import_log_file);
            $this->import_log=null;
            $this->import_log=new WPvivid_Log();
            $this->import_log->OpenLogFile($this->import_log_file,'has_folder');
        }
        $this->import_log->WriteLog($message, $type);
    }

    public function get_log_content()
    {
        $buffer = '';
        if(file_exists($this->import_log_file)){
            $file = fopen($this->import_log_file, 'r');

            if (!$file) {
                return '';
            }

            while (!feof($file)) {
                $buffer .= fread($file, 1024);
            }
            fclose($file);
        }
        return $buffer;
    }
}


global $xml_file_name;
class WPvivid_media_importer
{
    var $max_wxr_version = 1.2; // max. supported WXR version

    var $id; // WXR attachment ID
    var $default_user;
    // information to import from WXR file
    var $version;
    var $authors = array();
    var $posts = array();
    var $terms = array();
    var $categories = array();
    var $tags = array();
    var $base_url = '';
    var $new_site_url='';

    // mappings from old information to new
    var $processed_authors = array();
    var $author_mapping = array();
    var $processed_terms = array();
    var $processed_posts = array();
    var $post_orphans = array();
    var $processed_menu_items = array();
    var $menu_item_orphans = array();
    var $missing_menu_items = array();

    var $fetch_attachments = false;
    var $url_remap = array();
    var $featured_images = array();

    public $import_log;

    public function __construct()
    {
    }

    public function import($id)
    {
        if (!class_exists('WPvivid_PclZip'))
            include_once WPVIVID_PLUGIN_DIR . '/includes/zip/class-wpvivid-pclzip.php';

        $this->import_log = new WPvivid_import_data();

        @set_time_limit(900);

        $path=WP_CONTENT_DIR.DIRECTORY_SEPARATOR.WPvivid_Setting::get_backupdir().DIRECTORY_SEPARATOR.WPVIVID_IMPORT_EXPORT_DIR;

        $files=WPvivid_Impoter_taskmanager::get_files($id);

        define(PCLZIP_TEMPORARY_DIR,dirname($path));

        global $xml_file_name;
        foreach ($files as $file)
        {
            $file_path=$path.DIRECTORY_SEPARATOR.$file;
            $this->import_log->wpvivid_write_import_log('Prepare to retrieve file info, file name: '.$file_path, 'notice');
            $archive = new WPvivid_PclZip($file_path);
            $ret=$this->get_file_info($file_path);
            if($ret['result']=='failed')
            {
                $this->import_log->wpvivid_write_import_log('Failed to retrieve file info, error: '.$ret['error'], 'notice');
                WPvivid_Impoter_taskmanager::update_import_task_status($id, 'error', true, false, false, $ret['error']);
                return $ret;
            }
            $this->import_log->wpvivid_write_import_log('Retrieving file info is completed.', 'notice');
            $xml_file=$ret['json_data']['xml_file'];
            $xml_file_name = $ret['json_data']['xml_file'];
            $this->import_log->wpvivid_write_import_log('Prepare to extract, file name: '.$xml_file, 'notice');
            $zip_ret = $archive->extract(WPVIVID_PCLZIP_OPT_BY_NAME,basename($xml_file),WPVIVID_PCLZIP_OPT_PATH,$path,WPVIVID_PCLZIP_OPT_REPLACE_NEWER,WPVIVID_PCLZIP_OPT_TEMP_FILE_THRESHOLD,16);
            if(!$zip_ret)
            {
                $this->import_log->wpvivid_write_import_log('Failed to extract, error: '.$archive->errorInfo(true), 'notice');
                WPvivid_Impoter_taskmanager::update_import_task_status($id, 'error', true, false, false, $archive->errorInfo(true));
                $ret['result']='failed';
                $ret['error'] = $archive->errorInfo(true);
                return $ret;
            }
            $this->import_log->wpvivid_write_import_log('The file extracton is completed, file name: '.$xml_file, 'notice');
            $this->import_log->wpvivid_write_import_log('Prepare to extract, file name: '.$file_path, 'notice');
            $zip_ret = $archive->extract(WPVIVID_PCLZIP_OPT_PATH, WP_CONTENT_DIR, WPVIVID_PCLZIP_OPT_REPLACE_NEWER, WPVIVID_PCLZIP_CB_PRE_EXTRACT, 'wpvivid_function_pre_extract_import_callback', WPVIVID_PCLZIP_OPT_TEMP_FILE_THRESHOLD,16);
            if(!$zip_ret)
            {
                $this->import_log->wpvivid_write_import_log('Failed to extract, error: '.$archive->errorInfo(true), 'notice');
                WPvivid_Impoter_taskmanager::update_import_task_status($id, 'error', true, false, false, $archive->errorInfo(true));
                $ret['result']='failed';
                $ret['error'] = $archive->errorInfo(true);
                return $ret;
            }
            $this->import_log->wpvivid_write_import_log('The file extracton is completed, file name: '.$file_path, 'notice');

            @set_time_limit(900);
            $file_path=$path.DIRECTORY_SEPARATOR.$xml_file;
            $this->import_log->wpvivid_write_import_log('Prepare import, file name: '.$file_path, 'notice');
            $ret=$this->_import($file_path, WPvivid_Impoter_taskmanager::get_options($id));
            if($ret['result']=='failed')
            {
                $this->import_log->wpvivid_write_import_log('Failed to import, error: '.$ret['error'], 'notice');
                WPvivid_Impoter_taskmanager::update_import_task_status($id, 'error', true, false, false, $ret['error']);
                return $ret;
            }
            $this->import_log->wpvivid_write_import_log('Import task is completed, file name: '.$file_path, 'notice');
            @wp_delete_file($file_path);
        }

        $this->replace_domain();

        $ret['result']='success';
        $ret['files']=$files;
        $this->import_log->wpvivid_write_import_log('Import task succeeded.', 'notice');
        WPvivid_Impoter_taskmanager::update_import_task_status($id, 'completed', false);
        return $ret;
    }

    public function get_file_info($file_name)
    {
        if(!class_exists('WPvivid_ZipClass'))
            include_once WPVIVID_PLUGIN_DIR . '/includes/class-wpvivid-zipclass.php';
        $zip=new WPvivid_ZipClass();
        $ret=$zip->get_json_data($file_name, 'export');
        if($ret['result'] === WPVIVID_SUCCESS)
        {
            $json=$ret['json_data'];
            $json = json_decode($json, 1);
            if (is_null($json))
            {
                return array('result'=>WPVIVID_FAILED,'error'=>'Failed to decode json');
            } else {
                return array('result'=>WPVIVID_SUCCESS,'json_data'=>$json);
            }
        }
        else {
            return $ret;
        }
    }

    public function _import($file,$options)
    {
        if(isset($options['user']))
        {
            $this->default_user=$options['user'];
        }
        else
        {
            $this->default_user=get_current_user_id();
        }

        if(isset($options['update_exist']))
        {
            $update_exist=$options['update_exist'];
        }
        else
        {
            $update_exist=false;
        }

        $ret=$this->import_start( $file );

        if($ret['result']=='failed')
        {
            return $ret;
        }

        $ret=$this->get_author_mapping();

        if($ret['result']=='failed')
        {
            return $ret;
        }

        wp_suspend_cache_invalidation( true );
        $ret=$this->process_categories();
        if($ret['result']=='failed')
        {
            return $ret;
        }
        $ret=$this->process_tags();
        if($ret['result']=='failed')
        {
            return $ret;
        }
        $ret=$this->process_terms();
        if($ret['result']=='failed')
        {
            return $ret;
        }
        $ret=$this->process_posts_ex($update_exist);
        if($ret['result']=='failed')
        {
            return $ret;
        }
        wp_suspend_cache_invalidation( false );
        $ret=$this->import_end();

        return $ret;
    }

    private function import_start( $file )
    {
        $this->import_log->wpvivid_write_import_log('Analyze the imported file, file name: '.$file, 'notice');
        $import_data = $this->parse( $file );
        if( is_wp_error( $import_data ) )
        {
            $this->import_log->wpvivid_write_import_log('Failed to analyze a file, file name: '.$file, 'notice');
            $ret['result']='failed';
            $ret['error']=$import_data->get_error_message();
            return $ret;
        }

        $this->version = $import_data['version'];
        $this->get_authors_from_import( $import_data );
        $this->posts = $import_data['posts'];
        $this->terms = $import_data['terms'];
        $this->categories = $import_data['categories'];
        $this->tags = $import_data['tags'];
        $this->base_url = esc_url( $import_data['base_url'] );
        $this->import_log->wpvivid_write_import_log('The file analysis is completed, file name: '.$file, 'notice');
        $ret['result']='success';
        return $ret;
    }

    private function get_author_mapping()
    {
        $ret['result']='success';

        return $ret;

        /*
        $create_users = false;

        foreach ( (array) $_POST['imported_authors'] as $i => $old_login )
        {
            // Multisite adds strtolower to sanitize_user. Need to sanitize here to stop breakage in process_posts.
            $santized_old_login = sanitize_user( $old_login, true );
            $old_id = isset( $this->authors[$old_login]['author_id'] ) ? intval($this->authors[$old_login]['author_id']) : false;

            if ( ! empty( $_POST['user_map'][$i] ) )
            {
                $user = get_userdata( intval($_POST['user_map'][$i]) );
                if ( isset( $user->ID ) ) {
                    if ( $old_id )
                        $this->processed_authors[$old_id] = $user->ID;
                    $this->author_mapping[$santized_old_login] = $user->ID;
                }
            } else if ( $create_users )
            {
                if ( ! empty($_POST['user_new'][$i]) )
                {
                    $user_id = wp_create_user( $_POST['user_new'][$i], wp_generate_password() );
                } else if ( $this->version != '1.0' )
                {
                    $user_data = array(
                        'user_login' => $old_login,
                        'user_pass' => wp_generate_password(),
                        'user_email' => isset( $this->authors[$old_login]['author_email'] ) ? $this->authors[$old_login]['author_email'] : '',
                        'display_name' => $this->authors[$old_login]['author_display_name'],
                        'first_name' => isset( $this->authors[$old_login]['author_first_name'] ) ? $this->authors[$old_login]['author_first_name'] : '',
                        'last_name' => isset( $this->authors[$old_login]['author_last_name'] ) ? $this->authors[$old_login]['author_last_name'] : '',
                    );
                    $user_id = wp_insert_user( $user_data );
                }

                if ( ! is_wp_error( $user_id ) )
                {
                    if ( $old_id )
                        $this->processed_authors[$old_id] = $user_id;
                    $this->author_mapping[$santized_old_login] = $user_id;
                } else {
                    printf( __( 'Failed to create new user for %s. Their posts will be attributed to the current user.', 'wordpress-importer' ), esc_html($this->authors[$old_login]['author_display_name']) );
                    if ( defined('IMPORT_DEBUG') && IMPORT_DEBUG )
                        echo ' ' . $user_id->get_error_message();
                    echo '<br />';
                }
            }

            // failsafe: if the user_id was invalid, default to the current user
            if ( ! isset( $this->author_mapping[$santized_old_login] ) )
            {
                if ( $old_id )
                    $this->processed_authors[$old_id] = (int) get_current_user_id();
                $this->author_mapping[$santized_old_login] = (int) get_current_user_id();
            }
        }
        */
    }

    private function process_categories()
    {
        $ret['result']='success';
        $this->categories = apply_filters( 'wp_import_categories', $this->categories );
        $this->import_log->wpvivid_write_import_log('Start importing categories.', 'notice');
        if ( empty( $this->categories ) ) {
            $this->import_log->wpvivid_write_import_log('Categories import is completed.', 'notice');
            return $ret;
        }

        foreach ( $this->categories as $cat )
        {
            // if the category already exists leave it alone
            $term_id = term_exists( $cat['category_nicename'], 'category' );
            if ( $term_id )
            {
                if ( is_array($term_id) ) $term_id = $term_id['term_id'];
                if ( isset($cat['term_id']) )
                    $this->processed_terms[intval($cat['term_id'])] = (int) $term_id;
                continue;
            }

            $category_parent = empty( $cat['category_parent'] ) ? 0 : category_exists( $cat['category_parent'] );
            $category_description = isset( $cat['category_description'] ) ? $cat['category_description'] : '';
            $catarr = array(
                'category_nicename' => $cat['category_nicename'],
                'category_parent' => $category_parent,
                'cat_name' => $cat['cat_name'],
                'category_description' => $category_description
            );
            $catarr = wp_slash( $catarr );

            $id = wp_insert_category( $catarr );
            if ( ! is_wp_error( $id ) )
            {
                if ( isset($cat['term_id']) )
                    $this->processed_terms[intval($cat['term_id'])] = $id;
            } else {

                if ( defined('IMPORT_DEBUG') && IMPORT_DEBUG )
                {
                    $this->import_log->wpvivid_write_import_log('Failed to import categories, error: '.$id->get_error_message(), 'notice');
                    $ret['result']='failed';
                    $ret['error']='Failed to import category '.$cat['category_nicename'].' '.$id->get_error_message();
                    return $ret;
                }
                continue;
            }

            $this->process_termmeta( $cat, $id['term_id'] );
        }

        unset( $this->categories );
        $this->import_log->wpvivid_write_import_log('Categories import is completed.', 'notice');
        return $ret;
    }

    private function process_termmeta( $term, $term_id )
    {
        if ( ! isset( $term['termmeta'] ) )
        {
            $term['termmeta'] = array();
        }

        $term['termmeta'] = apply_filters( 'wp_import_term_meta', $term['termmeta'], $term_id, $term );

        if ( empty( $term['termmeta'] ) ) {
            return;
        }

        foreach ( $term['termmeta'] as $meta )
        {
            $key = apply_filters( 'import_term_meta_key', $meta['key'], $term_id, $term );
            if ( ! $key ) {
                continue;
            }

            // Export gets meta straight from the DB so could have a serialized string
            $value = maybe_unserialize( $meta['value'] );

            add_term_meta( $term_id, $key, $value );

            do_action( 'import_term_meta', $term_id, $key, $value );
        }
    }

    private function process_tags()
    {
        $ret['result']='success';
        $this->tags = apply_filters( 'wp_import_tags', $this->tags );
        $this->import_log->wpvivid_write_import_log('Start importing tags.', 'notice');
        if ( empty( $this->tags ) ){
            $this->import_log->wpvivid_write_import_log('Tags import is completed.', 'notice');
            return $ret;
        }

        foreach ( $this->tags as $tag )
        {
            $term_id = term_exists( $tag['tag_slug'], 'post_tag' );
            if ( $term_id )
            {
                if ( is_array($term_id) ) $term_id = $term_id['term_id'];
                if ( isset($tag['term_id']) )
                    $this->processed_terms[intval($tag['term_id'])] = (int) $term_id;
                continue;
            }

            $tag = wp_slash( $tag );
            $tag_desc = isset( $tag['tag_description'] ) ? $tag['tag_description'] : '';
            $tagarr = array( 'slug' => $tag['tag_slug'], 'description' => $tag_desc );

            $id = wp_insert_term( $tag['tag_name'], 'post_tag', $tagarr );
            if ( ! is_wp_error( $id ) )
            {
                if ( isset($tag['term_id']) )
                    $this->processed_terms[intval($tag['term_id'])] = $id['term_id'];
            } else {
                if ( defined('IMPORT_DEBUG') && IMPORT_DEBUG )
                {
                    $this->import_log->wpvivid_write_import_log('Failed to import tags, error: '.$id->get_error_message(), 'notice');
                    $ret['result']='failed';
                    $ret['error']='Failed to import post tag '.$tag['tag_name'].' '.$id->get_error_message();
                    return $ret;
                }
                continue;
            }

            $this->process_termmeta( $tag, $id['term_id'] );
        }

        unset( $this->tags );
        $this->import_log->wpvivid_write_import_log('Tags import is completed.', 'notice');
        return $ret;
    }

    private function process_terms()
    {
        $ret['result']='success';
        $this->terms = apply_filters( 'wp_import_terms', $this->terms );
        $this->import_log->wpvivid_write_import_log('Start importing terms.', 'notice');
        if ( empty( $this->terms ) ) {
            $this->import_log->wpvivid_write_import_log('Terms import is completed.', 'notice');
            return $ret;
        }

        foreach ( $this->terms as $term )
        {
            // if the term already exists in the correct taxonomy leave it alone
            $term_id = term_exists( $term['slug'], $term['term_taxonomy'] );
            if ( $term_id ) {
                if ( is_array($term_id) ) $term_id = $term_id['term_id'];
                if ( isset($term['term_id']) )
                    $this->processed_terms[intval($term['term_id'])] = (int) $term_id;
                continue;
            }

            if ( empty( $term['term_parent'] ) ) {
                $parent = 0;
            } else {
                $parent = term_exists( $term['term_parent'], $term['term_taxonomy'] );
                if ( is_array( $parent ) ) $parent = $parent['term_id'];
            }
            $term = wp_slash( $term );
            $description = isset( $term['term_description'] ) ? $term['term_description'] : '';
            $termarr = array( 'slug' => $term['slug'], 'description' => $description, 'parent' => intval($parent) );

            $id = wp_insert_term( $term['term_name'], $term['term_taxonomy'], $termarr );
            if ( ! is_wp_error( $id ) ) {
                if ( isset($term['term_id']) )
                    $this->processed_terms[intval($term['term_id'])] = $id['term_id'];
            } else {
                if ( defined('IMPORT_DEBUG') && IMPORT_DEBUG )
                {
                    $this->import_log->wpvivid_write_import_log('Failed to import terms, error: '.$id->get_error_message(), 'notice');
                    $ret['result']='failed';
                    $ret['error']='Failed to import '.$term['term_taxonomy'].' '.$term['term_name'].' '.$id->get_error_message();
                    return $ret;
                }

                continue;
            }

            $this->process_termmeta( $term, $id['term_id'] );
        }

        unset( $this->terms );
        $this->import_log->wpvivid_write_import_log('Terms import is completed.', 'notice');
        return $ret;
    }

    private function process_posts_ex($update_exist=false)
    {
        $this->import_log->wpvivid_write_import_log('Start importing posts.', 'notice');
        $ret['result']='success';
        $this->posts = apply_filters( 'wp_import_posts', $this->posts );

        foreach ( $this->posts as $post )
        {
            $this->import_log->wpvivid_write_import_log('Post id: '.$post['post_id'], 'notice');
            $post = apply_filters( 'wp_import_post_data_raw', $post );
            $post_type_object = get_post_type_object( $post['post_type'] );
            $post_exists = post_exists( $post['post_title'], '', $post['post_date'] );
            $post_exists = apply_filters( 'wp_import_existing_post', $post_exists, $post );
            if ( $post_exists && get_post_type( $post_exists ) == $post['post_type'] )
            {
                $this->import_log->wpvivid_write_import_log('The post already exists.', 'notice');
                $comment_post_ID=$post_id = $post_exists;
                $this->processed_posts[ intval( $post['post_id'] ) ] = intval( $post_exists );

                if($update_exist)
                {
                    $post_parent = (int) $post['post_parent'];
                    if ( $post_parent )
                    {
                        // if we already know the parent, map it to the new local ID
                        if ( isset( $this->processed_posts[$post_parent] ) )
                        {
                            $post_parent = $this->processed_posts[$post_parent];
                            // otherwise record the parent for later
                        } else {
                            $this->post_orphans[intval($post['post_id'])] = $post_parent;
                            $post_parent = 0;
                        }
                    }
                    $author = sanitize_user( $post['post_author'], true );
                    if ( isset( $this->author_mapping[$author] ) ) {
                        $author = $this->author_mapping[$author];
                    }
                    else {
                        $author = (int)$this->default_user;
                    }

                    $postdata = array(
                        'ID' => $post['post_id'], 'post_author' => $author, 'post_date' => $post['post_date'],
                        'post_date_gmt' => $post['post_date_gmt'], 'post_content' => $post['post_content'],
                        'post_excerpt' => $post['post_excerpt'], 'post_title' => $post['post_title'],
                        'post_status' => $post['status'], 'post_name' => $post['post_name'],
                        'comment_status' => $post['comment_status'], 'ping_status' => $post['ping_status'],
                        'guid' => $post['guid'], 'post_parent' => $post_parent, 'menu_order' => $post['menu_order'],
                        'post_type' => $post['post_type'], 'post_password' => $post['post_password']
                    );

                    wp_update_post($postdata);

                    if ( ! empty( $post['postmeta'] ) )
                    {
                        foreach ( $post['postmeta'] as $meta )
                        {
                            $key = apply_filters( 'import_post_meta_key', $meta['key'], $post_id, $post );
                            $value = false;

                            if ( '_edit_last' == $key )
                            {
                                if ( isset( $this->processed_authors[intval($meta['value'])] ) )
                                    $value = $this->processed_authors[intval($meta['value'])];
                                else
                                    $key = false;
                            }

                            if ( $key )
                            {
                                // export gets meta straight from the DB so could have a serialized string
                                if ( ! $value )
                                    $value = maybe_unserialize( $meta['value'] );
                                if(metadata_exists('post', $post_id, $key))
                                {
                                    //update_post_meta($post_id,$key,$value);
                                    update_post_meta($post_id, wp_slash( $key ), wp_slash_strings_only( $value ));
                                }
                                else
                                {
                                    //add_post_meta( $post_id, $key, $value );
                                    add_post_meta( $post_id, wp_slash( $key ), wp_slash_strings_only( $value ) );
                                }


                                do_action( 'import_post_meta', $post_id, $key, $value );

                                // if the post has a featured image, take note of this in case of remap
                                if ( '_thumbnail_id' == $key )
                                    $this->featured_images[$post_id] = (int) $value;
                            }
                        }
                    }
                }

            } else {
                $post_parent = (int) $post['post_parent'];
                if ( $post_parent )
                {
                    // if we already know the parent, map it to the new local ID
                    if ( isset( $this->processed_posts[$post_parent] ) )
                    {
                        $post_parent = $this->processed_posts[$post_parent];
                        // otherwise record the parent for later
                    } else {
                        $this->post_orphans[intval($post['post_id'])] = $post_parent;
                        $post_parent = 0;
                    }
                }
                // map the post author
                $author = sanitize_user( $post['post_author'], true );
                if ( isset( $this->author_mapping[$author] ) ) {
                    $author = $this->author_mapping[$author];
                }
                else {
                    $author = (int)$this->default_user;
                }
                $postdata = array(
                    'import_id' => $post['post_id'], 'post_author' => $author, 'post_date' => $post['post_date'],
                    'post_date_gmt' => $post['post_date_gmt'], 'post_content' => $post['post_content'],
                    'post_excerpt' => $post['post_excerpt'], 'post_title' => $post['post_title'],
                    'post_status' => $post['status'], 'post_name' => $post['post_name'],
                    'comment_status' => $post['comment_status'], 'ping_status' => $post['ping_status'],
                    'guid' => $post['guid'], 'post_parent' => $post_parent, 'menu_order' => $post['menu_order'],
                    'post_type' => $post['post_type'], 'post_password' => $post['post_password']
                );
                $original_post_ID = $post['post_id'];
                $postdata = apply_filters( 'wp_import_post_data_processed', $postdata, $post );
                $postdata = wp_slash( $postdata );
                if ( 'attachment' == $postdata['post_type'] )
                {
                    $remote_url = ! empty($post['attachment_url']) ? $post['attachment_url'] : $post['guid'];
                    // try to use _wp_attached file for upload folder placement to ensure the same location as the export site
                    // e.g. location is 2003/05/image.jpg but the attachment post_date is 2010/09, see media_handle_upload()
                    $postdata['upload_date'] = $post['post_date'];
                    if ( isset( $post['postmeta'] ) )
                    {
                        foreach( $post['postmeta'] as $meta )
                        {
                            if ( $meta['key'] == '_wp_attached_file' ) {
                                if ( preg_match( '%^[0-9]{4}/[0-9]{2}%', $meta['value'], $matches ) )
                                    $postdata['upload_date'] = $matches[0];
                                break;
                            }
                        }
                        $postmeta=$post['postmeta'];
                    }
                    else
                    {
                        $postmeta=false;
                    }

                    $comment_post_ID = $post_id = $this->process_attachment_ex( $postdata, $remote_url ,$postmeta);
                } else {
                    $comment_post_ID =$post_id = wp_insert_post( $postdata, true );
                    do_action( 'wp_import_insert_post', $post_id, $original_post_ID, $postdata, $post );
                }
                if ( is_wp_error( $post_id ) )
                {
                    if ( defined('IMPORT_DEBUG') && IMPORT_DEBUG )
                    {
                        $ret['result']='failed';
                        $ret['error']='Failed to import '.$post_type_object->labels->singular_name.' '.$post['post_title'].' '.$post_id->get_error_message();
                        return $ret;
                    }
                    continue;
                }
                if ( $post['is_sticky'] == 1 )
                    stick_post( $post_id );
                // map pre-import ID to local ID
                $this->processed_posts[intval($post['post_id'])] = (int) $post_id;
            }

            if($post_exists)
                continue;

            if ( ! isset( $post['terms'] ) )
                $post['terms'] = array();

            $post['terms'] = apply_filters( 'wp_import_post_terms', $post['terms'], $post_id, $post );

            // add categories, tags and other terms
            if ( ! empty( $post['terms'] ) )
            {
                $terms_to_set = array();
                foreach ( $post['terms'] as $term )
                {
                    // back compat with WXR 1.0 map 'tag' to 'post_tag'
                    $taxonomy = ( 'tag' == $term['domain'] ) ? 'post_tag' : $term['domain'];
                    $term_exists = term_exists( $term['slug'], $taxonomy );
                    $term_id = is_array( $term_exists ) ? $term_exists['term_id'] : $term_exists;
                    if ( ! $term_id )
                    {
                        $t = wp_insert_term( $term['name'], $taxonomy, array( 'slug' => $term['slug'] ) );
                        if ( ! is_wp_error( $t ) )
                        {
                            $term_id = $t['term_id'];
                            do_action( 'wp_import_insert_term', $t, $term, $post_id, $post );
                        } else {
                            if ( defined('IMPORT_DEBUG') && IMPORT_DEBUG )
                            {
                                $this->import_log->wpvivid_write_import_log('Failed to import post, error: '.$post_id->get_error_message(), 'notice');
                                $ret['result']='failed';
                                $ret['error']='Failed to import '.esc_html($taxonomy).' '.esc_html($term['name']).' '.$post_id->get_error_message();
                                return $ret;
                            }
                            continue;
                        }
                    }
                    $terms_to_set[$taxonomy][] = intval( $term_id );
                }

                foreach ( $terms_to_set as $tax => $ids )
                {
                    $tt_ids = wp_set_post_terms( $post_id, $ids, $tax );
                    do_action( 'wp_import_set_post_terms', $tt_ids, $ids, $tax, $post_id, $post );
                }
                unset( $post['terms'], $terms_to_set );
            }

            if ( ! isset( $post['comments'] ) )
                $post['comments'] = array();

            $post['comments'] = apply_filters( 'wp_import_post_comments', $post['comments'], $post_id, $post );

            // add/update comments
            if ( ! empty( $post['comments'] ) )
            {
                $num_comments = 0;
                $inserted_comments = array();
                foreach ( $post['comments'] as $comment )
                {
                    $comment_id	= $comment['comment_id'];
                    $newcomments[$comment_id]['comment_post_ID']      = $comment_post_ID;
                    $newcomments[$comment_id]['comment_author']       = $comment['comment_author'];
                    $newcomments[$comment_id]['comment_author_email'] = $comment['comment_author_email'];
                    $newcomments[$comment_id]['comment_author_IP']    = $comment['comment_author_IP'];
                    $newcomments[$comment_id]['comment_author_url']   = $comment['comment_author_url'];
                    $newcomments[$comment_id]['comment_date']         = $comment['comment_date'];
                    $newcomments[$comment_id]['comment_date_gmt']     = $comment['comment_date_gmt'];
                    $newcomments[$comment_id]['comment_content']      = $comment['comment_content'];
                    $newcomments[$comment_id]['comment_approved']     = $comment['comment_approved'];
                    $newcomments[$comment_id]['comment_type']         = $comment['comment_type'];
                    $newcomments[$comment_id]['comment_parent'] 	  = $comment['comment_parent'];
                    $newcomments[$comment_id]['commentmeta']          = isset( $comment['commentmeta'] ) ? $comment['commentmeta'] : array();
                    if ( isset( $this->processed_authors[$comment['comment_user_id']] ) )
                        $newcomments[$comment_id]['user_id'] = $this->processed_authors[$comment['comment_user_id']];
                }
                ksort( $newcomments );

                foreach ( $newcomments as $key => $comment )
                {
                    // if this is a new post we can skip the comment_exists() check
                    if ( ! $post_exists || ! comment_exists( $comment['comment_author'], $comment['comment_date'] ) )
                    {
                        if ( isset( $inserted_comments[$comment['comment_parent']] ) )
                            $comment['comment_parent'] = $inserted_comments[$comment['comment_parent']];
                        $comment = wp_slash( $comment );
                        $comment = wp_filter_comment( $comment );
                        $inserted_comments[$key] = wp_insert_comment( $comment );
                        do_action( 'wp_import_insert_comment', $inserted_comments[$key], $comment, $comment_post_ID, $post );

                        foreach( $comment['commentmeta'] as $meta ) {
                            $value = maybe_unserialize( $meta['value'] );
                            add_comment_meta( $inserted_comments[$key], $meta['key'], $value );
                        }

                        $num_comments++;
                    }
                }
                unset( $newcomments, $inserted_comments, $post['comments'] );
            }

            if ( ! isset( $post['postmeta'] ) )
                $post['postmeta'] = array();

            $post['postmeta'] = apply_filters( 'wp_import_post_meta', $post['postmeta'], $post_id, $post );

            // add/update post meta
            if ( ! empty( $post['postmeta'] ) )
            {
                foreach ( $post['postmeta'] as $meta )
                {
                    $key = apply_filters( 'import_post_meta_key', $meta['key'], $post_id, $post );
                    $value = false;

                    if ( '_edit_last' == $key )
                    {
                        if ( isset( $this->processed_authors[intval($meta['value'])] ) )
                            $value = $this->processed_authors[intval($meta['value'])];
                        else
                            $key = false;
                    }

                    if ( $key )
                    {
                        // export gets meta straight from the DB so could have a serialized string
                        if ( ! $value )
                            $value = maybe_unserialize( $meta['value'] );
                        if(metadata_exists('post', $post_id, $key))
                        {
                            //update_post_meta($post_id,$key,$value);
                            update_post_meta($post_id, wp_slash( $key ), wp_slash_strings_only( $value ));
                        }
                        else
                        {
                            //add_post_meta( $post_id, $key, $value );
                            add_post_meta( $post_id, wp_slash( $key ), wp_slash_strings_only( $value ) );
                        }


                        do_action( 'import_post_meta', $post_id, $key, $value );

                        // if the post has a featured image, take note of this in case of remap
                        if ( '_thumbnail_id' == $key )
                            $this->featured_images[$post_id] = (int) $value;
                    }
                }
            }
        }

        unset( $this->posts );
        $this->import_log->wpvivid_write_import_log('Posts import is completed.', 'notice');
        return $ret;
    }

    public function replace_domain()
    {
        $this->new_site_url= untrailingslashit(site_url());
        $this->import_log->wpvivid_write_import_log('The original domain name: '.$this->base_url, 'notice');
        $this->import_log->wpvivid_write_import_log('The current domain name: '.$this->new_site_url, 'notice');
        if(empty($this->base_url))
        {
            $this->import_log->wpvivid_write_import_log('Failed to retrieve the original domain name: '.$this->base_url, 'notice');
            return ;
        }

        if(empty($this->processed_posts))
        {
            $this->import_log->wpvivid_write_import_log('The unimported posts', 'notice');
            return ;
        }

        if($this->base_url===$this->new_site_url)
        {
            $this->import_log->wpvivid_write_import_log('Replacing domain name is not required.', 'notice');
            return ;
        }


        global $wp_query,$wpdb;
        $this->import_log->wpvivid_write_import_log('Start replacing domain name.', 'notice');
        $wp_query->in_the_loop = true;
        while ( $next_posts = array_splice( $this->processed_posts, 0, 20 ) )
        {
            $where = 'WHERE ID IN (' . join(',', $next_posts) . ')';
            $posts = $wpdb->get_results("SELECT * FROM {$wpdb->posts} $where");

            foreach ( $posts as $post )
            {
                $old_data=$post->post_content;
                $new_data=$this->replace_row_data($old_data);
                if($new_data==$old_data)
                {
                    $this->import_log->wpvivid_write_import_log('Post ID '.$post->ID.' is not changed.', 'notice');
                    continue;
                }
                else
                {
                    $this->import_log->wpvivid_write_import_log('Post ID '.$post->ID.' is changed.', 'notice');
                }
                $post->post_content=$new_data;
                wp_update_post($post);
            }
        }
    }

    private function replace_row_data($old_data)
    {
        $unserialize_data = @unserialize($old_data, array('allowed_classes' => false));
        if($unserialize_data===false)
        {
            $old_data=$this->replace_string($old_data);
        }
        else
        {
            $old_data=$this->replace_serialize_data($unserialize_data);
            $old_data=serialize($old_data);
            /*if(is_array($unserialize_data))
            {
                $temp_data = array();
                foreach ($unserialize_data as $key => $value)
                {
                    $temp_data[$key]=$this->replace_string($value);
                }

                $old_data = $temp_data;
                unset($temp_data);
                $old_data=$this->replace_serialize_data($unserialize_data);
                $old_data=serialize($old_data);
            }
            else if(is_object($unserialize_data))
            {
                $temp_data = $unserialize_data;
                $props = get_object_vars($unserialize_data);
                foreach ($props as $key => $value)
                {
                    $temp_data->$key =$this->replace_string($value);
                }
                $old_data = $temp_data;
                unset($temp_data);
                $old_data=serialize($old_data);
            }*/
        }

        return $old_data;
    }

    private function replace_serialize_data($data)
    {
        if(is_string($data))
        {
            $serialize_data =@unserialize($data, array('allowed_classes' => false));
            if($serialize_data===false)
            {
                $data=$this->replace_string($data);
            }
            else
            {
                $data=serialize($this->replace_serialize_data($serialize_data));
            }
        }
        else if(is_array($data))
        {
            foreach ($data as $key => $value)
            {
                if(is_string($value))
                {
                    $data[$key]=$this->replace_string($value);
                }
                else if(is_array($value))
                {
                    $data[$key]=$this->replace_serialize_data($value);
                }
                else if(is_object($value))
                {
                    if (is_a($value, '__PHP_Incomplete_Class'))
                    {
                        //
                    }
                    else
                    {
                        $data[$key]=$this->replace_serialize_data($value);
                    }
                }
            }
        }
        else if(is_object($data))
        {
            $temp = $data; // new $data_class();
            if (is_a($data, '__PHP_Incomplete_Class'))
            {

            }
            else
            {
                $props = get_object_vars($data);
                foreach ($props as $key => $value)
                {
                    if(is_string($value))
                    {
                        $temp->$key =$this->replace_string($value);
                    }
                    else if(is_array($value))
                    {
                        $temp->$key=$this->replace_serialize_data($value);
                    }
                    else if(is_object($value))
                    {
                        $temp->$key=$this->replace_serialize_data($value);
                    }
                }
            }
            $data = $temp;
            unset($temp);
        }

        return $data;
    }

    private function replace_string($old_string)
    {
        if(!is_string($old_string))
        {
            return $old_string;
        }

        if($this->base_url!=$this->new_site_url)
        {
            $remove_http_link=$this->get_remove_http_link($this->base_url);
            $new_remove_http_link=$this->get_remove_http_link($this->new_site_url);
            if(strpos($new_remove_http_link,$remove_http_link)!==false)
            {
                return $this->replace_string_ex($old_string);
            }
        }

        if($this->base_url!=$this->new_site_url)
        {
            $old_string=str_replace($this->base_url,$this->new_site_url,$old_string);
            $old_mix_link=$this->get_mix_link($this->base_url);
            if($old_mix_link!==false)
            {
                $old_string=str_replace($old_mix_link,$this->new_site_url,$old_string);
            }
            $remove_http_link=$this->get_remove_http_link($this->base_url);
            if($remove_http_link!==false)
            {
                $new_remove_http_link=$this->get_remove_http_link($this->new_site_url);
                $old_string=str_replace($remove_http_link,$new_remove_http_link,$old_string);
            }

            $remove_http_link=$this->get_remove_http_link_ex($this->base_url);
            if($remove_http_link!==false)
            {
                $new_remove_http_link=$this->get_remove_http_link_ex($this->new_site_url);
                $old_string=str_replace($remove_http_link,$new_remove_http_link,$old_string);
            }
        }

        return $old_string;
    }

    private function replace_string_ex($old_string)
    {
        if(!is_string($old_string))
        {
            return $old_string;
        }

        if($this->base_url!=$this->new_site_url)
        {
            $remove_http_link=$this->get_remove_http_link($this->base_url);
            if($remove_http_link!==false)
            {
                $new_remove_http_link=$this->get_remove_http_link($this->new_site_url);
                $old_string=str_replace($remove_http_link,$new_remove_http_link,$old_string);
            }

            $new_mix_link=$this->get_mix_link($this->new_site_url);
            if($new_mix_link!==false)
            {
                $old_string=str_replace($new_mix_link,$this->new_site_url,$old_string);
            }

            $remove_http_link=$this->get_remove_http_link_ex($this->base_url);
            if($remove_http_link!==false)
            {
                $new_remove_http_link=$this->get_remove_http_link_ex($this->new_site_url);
                $old_string=str_replace($remove_http_link,$new_remove_http_link,$old_string);
            }
        }

        return $old_string;
    }

    private function get_remove_http_link($url)
    {
        if (0 === stripos($url, 'https://'))
        {
            $mix_link = '//'.substr($url, 8);
        } elseif (0 === stripos($url, 'http://')) {
            $mix_link = '//'.substr($url, 7);
        }
        else
        {
            $mix_link=false;
        }
        return $mix_link;
    }

    private function get_remove_http_link_ex($url)
    {
        if (0 === stripos($url, 'https://'))
        {
            $mix_link = '\/\/'.substr($url, 8);
        } elseif (0 === stripos($url, 'http://')) {
            $mix_link = '\/\/'.substr($url, 7);
        }
        else
        {
            $mix_link=false;
        }
        return $mix_link;
    }

    private function get_mix_link($url)
    {
        if (0 === stripos($url, 'https://'))
        {
            $mix_link = 'http://'.substr($url, 8);
        } elseif (0 === stripos($url, 'http://')) {
            $mix_link = 'https://'.substr($url, 7);
        }
        else
        {
            $mix_link=false;
        }
        return $mix_link;
    }

    function process_attachment_ex( $post, $url,$postmeta )
    {
        // if the URL is absolute, but does not contain address, then upload it assuming base_site_url
        if ( preg_match( '|^/[\w\W]+$|', $url ) )
            $url = rtrim( $this->base_url, '/' ) . $url;

        $upload = $this->fetch_local_file_ex( $url, $post ,$postmeta);
        if ( is_wp_error( $upload ) )
            return $upload;
        $post['post_mime_type']=$upload['type'];

        $post['guid'] = $upload['url'];

        // as per wp-admin/includes/upload.php
        $post_id = wp_insert_attachment( $post, $upload['file'] );

        if ( is_wp_error( $post_id ) )
        {
            return $post_id;
        }

        if ( preg_match( '!^image/!',$upload['type'] ) )
        {
            $parts = pathinfo( $url );
            $name = basename( $parts['basename'], ".{$parts['extension']}" ); // PATHINFO_FILENAME in PHP 5.2

            $parts_new = pathinfo( $upload['url'] );
            $name_new = basename( $parts_new['basename'], ".{$parts_new['extension']}" );

            $this->url_remap[$parts['dirname'] . '/' . $name] = $parts_new['dirname'] . '/' . $name_new;
        }

        return $post_id;
    }

    function fetch_local_file_ex($url,$post,$postmeta)
    {
        $file_name = basename( $url );

        $upload = wp_upload_dir( $post['upload_date'] );

        $new_file='';
        if($postmeta!==false)
        {
            foreach( $postmeta as $meta )
            {
                if ( $meta['key'] == '_wp_attached_file' )
                {
                    $new_file=$upload['basedir'].'/'.$meta['meta_value'];
                    $url = $upload['baseurl'].'/'.$meta['meta_value'];
                }
            }
        }

        if(empty($new_file))
        {
            $new_file = $upload['path'] . "/$file_name";
            $url = $upload['url'] . "/$file_name";
        }

        if(!file_exists($new_file))
        {
            return new WP_Error( 'import_file_error', 'File not exist, file:'.$new_file );
        }

        $wp_filetype = wp_check_filetype( $file_name );

        if ( ! $wp_filetype['ext'] && ! current_user_can( 'unfiltered_upload' ) )
        {
            return new WP_Error( 'import_file_error', __( 'Sorry, this file type is not permitted for security reasons.', 'wpvivid-backuprestore' ) );
        }

        return apply_filters(
            'wp_handle_upload',
            array(
                'file'  => $new_file,
                'url'   => $url,
                'type'  => $wp_filetype['type'],
                'error' => false,
            ),
            'sideload'
        );
    }

    function parse( $file ) {
        $parser = new WPvivid_WXR_Parser();
        return $parser->parse( $file );
    }

    function get_authors_from_import( $import_data )
    {
        if ( ! empty( $import_data['authors'] ) )
        {
            $this->authors = $import_data['authors'];
            // no author information, grab it from the posts
        } else {
            foreach ( $import_data['posts'] as $post )
            {
                $login = sanitize_user( $post['post_author'], true );
                if ( empty( $login ) )
                {
                    continue;
                }

                if ( ! isset($this->authors[$login]) )
                    $this->authors[$login] = array(
                        'author_login' => $login,
                        'author_display_name' => $post['post_author']
                    );
            }
        }
    }

    function process_attachment( $post, $url )
    {
        if ( ! $this->fetch_attachments )
            return new WP_Error( 'attachment_processing_error',
                __( 'Fetching attachments is not enabled', 'wpvivid-backuprestore' ) );

        // if the URL is absolute, but does not contain address, then upload it assuming base_site_url
        if ( preg_match( '|^/[\w\W]+$|', $url ) )
            $url = rtrim( $this->base_url, '/' ) . $url;

        $upload = $this->fetch_local_file( $url, $post );
        if ( is_wp_error( $upload ) )
            return $upload;

        if ( $info = wp_check_filetype( $upload['file'] ) )
            $post['post_mime_type'] = $info['type'];
        else
            return new WP_Error( 'attachment_processing_error', __('Invalid file type', 'wpvivid-backuprestore') );

        $post['guid'] = $upload['url'];

        // as per wp-admin/includes/upload.php
        $post_id = wp_insert_attachment( $post, $upload['file'] );

        if ( is_wp_error( $post_id ) )
        {
            echo 'error file:'.esc_html($upload['file']);
        }

        //$metadata=wp_generate_attachment_metadata( $post_id, $upload['file'] );
        //wp_update_attachment_metadata( $post_id,$metadata  );

        // remap resized image URLs, works by stripping the extension and remapping the URL stub.
        if ( preg_match( '!^image/!', $info['type'] ) ) {
            $parts = pathinfo( $url );
            $name = basename( $parts['basename'], ".{$parts['extension']}" ); // PATHINFO_FILENAME in PHP 5.2

            $parts_new = pathinfo( $upload['url'] );
            $name_new = basename( $parts_new['basename'], ".{$parts_new['extension']}" );

            $this->url_remap[$parts['dirname'] . '/' . $name] = $parts_new['dirname'] . '/' . $name_new;
        }

        return $post_id;
    }

    function fetch_local_file($url,$post)
    {
        $file_name = basename( $url );

        $upload = wp_upload_dir( $post['upload_date'] );
        $new_file = $upload['path'] . "/$file_name";
        $url = $upload['url'] . "/$file_name";

        $wp_filetype = wp_check_filetype( $file_name );

        if ( ! $wp_filetype['ext'] && ! current_user_can( 'unfiltered_upload' ) ) {
            return array( 'error' => __( 'Sorry, this file type is not permitted for security reasons.', 'wpvivid-backuprestore' ) );
        }

        if(!file_exists($new_file))
        {
            return new WP_Error( 'import_file_error', 'File not exist, file:'.$new_file );
        }

        return apply_filters(
            'wp_handle_upload',
            array(
                'file'  => $new_file,
                'url'   => $url,
                'type'  => $wp_filetype['type'],
                'error' => false,
            ),
            'sideload'
        );
    }

    /**
     * Performs post-import cleanup of files and the cache
     */
    function import_end()
    {
        wp_import_cleanup( $this->id );

        wp_cache_flush();
        foreach ( get_taxonomies() as $tax ) {
            delete_option( "{$tax}_children" );
            _get_term_hierarchy( $tax );
        }

        wp_defer_term_counting( false );
        wp_defer_comment_counting( false );

        $ret['result']='success';

        do_action( 'import_end' );
        return $ret;
    }
}

function wpvivid_function_pre_extract_import_callback($p_event, &$p_header)
{
    global $xml_file_name;

    if(strpos($p_header['filename'],$xml_file_name)!==false)
    {
        return 0;
    }

    if(strpos($p_header['filename'],'wpvivid_export_package_info.json')!==false)
    {
        return 0;
    }

    return 1;
}

/**
 * WordPress Importer class for managing parsing of WXR files.
 */
class WPvivid_WXR_Parser
{
    function parse( $file )
    {
        // Attempt to use proper XML parsers first
        if ( extension_loaded( 'simplexml' ) )
        {
            $parser = new WPvivid_WXR_Parser_SimpleXML;
            $result = $parser->parse( $file );

            // If SimpleXML succeeds or this is an invalid WXR file then return the results
            if ( ! is_wp_error( $result ) || 'SimpleXML_parse_error' != $result->get_error_code() )
                return $result;
        }
        else if ( extension_loaded( 'xml' ) )
        {
            $parser = new WPvivid_WXR_Parser_XML;
            $result = $parser->parse( $file );

            // If XMLParser succeeds or this is an invalid WXR file then return the results
            if ( ! is_wp_error( $result ) || 'XML_parse_error' != $result->get_error_code() )
                return $result;
        }

        // We have a malformed XML file, so display the error and fallthrough to regex
        if ( isset($result) && defined('IMPORT_DEBUG') && IMPORT_DEBUG )
        {
            $msg='';
            if ( 'SimpleXML_parse_error' == $result->get_error_code() )
            {
                foreach  ( $result->get_error_data() as $error )
                    $msg.= $error->line . ':' . $error->column . ' ' . esc_html( $error->message ) . "\n";
            } else if ( 'XML_parse_error' == $result->get_error_code() )
            {
                $error = $result->get_error_data();
                $msg.= $error[0] . ':' . $error[1] . ' ' . esc_html( $error[2] );
            }
            $msg.=__( 'There was an error when reading this WXR file', 'wpvivid-backuprestore' ) ;
            $msg.=__( 'Details are shown above. The importer will now try again with a different parser...', 'wpvivid-backuprestore' );

            return new WP_Error( 'WXR_Parser_error', $msg,'' );
        }

        // use regular expressions if nothing else available or this is bad XML
        $parser = new WPvivid_WXR_Parser_Regex;
        return $parser->parse( $file );
    }
}

/**
 * WXR Parser that makes use of the SimpleXML PHP extension.
 */
class WPvivid_WXR_Parser_SimpleXML
{
    function parse( $file )
    {
        $authors = $posts = $categories = $tags = $terms = array();

        $internal_errors = libxml_use_internal_errors(true);

        $dom = new DOMDocument;
        $old_value = null;
        if ( function_exists( 'libxml_disable_entity_loader' ) ) {
            $old_value = libxml_disable_entity_loader( true );
        }
        //$success = $dom->loadXML( file_get_contents( $file ), LIBXML_PARSEHUGE );
        $success = $dom->loadXML( file_get_contents( $file ) );
        if ( ! is_null( $old_value ) )
        {
            libxml_disable_entity_loader( $old_value );
        }

        if ( ! $success || isset( $dom->doctype ) )
        {
            return new WP_Error( 'SimpleXML_parse_error', __( 'There was an error when reading this WXR file', 'wpvivid-backuprestore' ), libxml_get_errors() );
        }

        $xml = simplexml_import_dom( $dom );
        unset( $dom );

        // halt if loading produces an error
        if ( ! $xml )
            return new WP_Error( 'SimpleXML_parse_error', __( 'There was an error when reading this WXR file', 'wpvivid-backuprestore' ), libxml_get_errors() );

        $wxr_version = $xml->xpath('/rss/channel/wp:wxr_version');
        if ( ! $wxr_version )
            return new WP_Error( 'WXR_parse_error', __( 'This does not appear to be a WXR file, missing/invalid WXR version number', 'wpvivid-backuprestore' ) );

        $wxr_version = (string) trim( $wxr_version[0] );
        // confirm that we are dealing with the correct file format
        if ( ! preg_match( '/^\d+\.\d+$/', $wxr_version ) )
            return new WP_Error( 'WXR_parse_error', __( 'This does not appear to be a WXR file, missing/invalid WXR version number', 'wpvivid-backuprestore' ) );

        $base_url = $xml->xpath('/rss/channel/wp:base_site_url');
        $base_url = (string) trim( $base_url[0] );

        $namespaces = $xml->getDocNamespaces();
        if ( ! isset( $namespaces['wp'] ) )
            $namespaces['wp'] = 'http://wordpress.org/export/1.1/';
        if ( ! isset( $namespaces['excerpt'] ) )
            $namespaces['excerpt'] = 'http://wordpress.org/export/1.1/excerpt/';

        // grab authors
        foreach ( $xml->xpath('/rss/channel/wp:author') as $author_arr )
        {
            $a = $author_arr->children( $namespaces['wp'] );
            $login = (string) $a->author_login;
            $authors[$login] = array(
                'author_id' => (int) $a->author_id,
                'author_login' => $login,
                'author_email' => (string) $a->author_email,
                'author_display_name' => (string) $a->author_display_name,
                'author_first_name' => (string) $a->author_first_name,
                'author_last_name' => (string) $a->author_last_name
            );
        }

        // grab cats, tags and terms
        foreach ( $xml->xpath('/rss/channel/wp:category') as $term_arr )
        {
            $t = $term_arr->children( $namespaces['wp'] );
            $category = array(
                'term_id' => (int) $t->term_id,
                'category_nicename' => (string) $t->category_nicename,
                'category_parent' => (string) $t->category_parent,
                'cat_name' => (string) $t->cat_name,
                'category_description' => (string) $t->category_description
            );

            foreach ( $t->termmeta as $meta ) {
                $category['termmeta'][] = array(
                    'key' => (string) $meta->meta_key,
                    'value' => (string) $meta->meta_value
                );
            }

            $categories[] = $category;
        }

        foreach ( $xml->xpath('/rss/channel/wp:tag') as $term_arr )
        {
            $t = $term_arr->children( $namespaces['wp'] );
            $tag = array(
                'term_id' => (int) $t->term_id,
                'tag_slug' => (string) $t->tag_slug,
                'tag_name' => (string) $t->tag_name,
                'tag_description' => (string) $t->tag_description
            );

            foreach ( $t->termmeta as $meta ) {
                $tag['termmeta'][] = array(
                    'key' => (string) $meta->meta_key,
                    'value' => (string) $meta->meta_value
                );
            }

            $tags[] = $tag;
        }

        foreach ( $xml->xpath('/rss/channel/wp:term') as $term_arr )
        {
            $t = $term_arr->children( $namespaces['wp'] );
            $term = array(
                'term_id' => (int) $t->term_id,
                'term_taxonomy' => (string) $t->term_taxonomy,
                'slug' => (string) $t->term_slug,
                'term_parent' => (string) $t->term_parent,
                'term_name' => (string) $t->term_name,
                'term_description' => (string) $t->term_description
            );

            foreach ( $t->termmeta as $meta ) {
                $term['termmeta'][] = array(
                    'key' => (string) $meta->meta_key,
                    'value' => (string) $meta->meta_value
                );
            }

            $terms[] = $term;
        }

        // grab posts
        foreach ( $xml->channel->item as $item )
        {
            $post = array(
                'post_title' => (string) $item->title,
                'guid' => (string) $item->guid,
            );

            $dc = $item->children( 'http://purl.org/dc/elements/1.1/' );
            $post['post_author'] = (string) $dc->creator;

            $content = $item->children( 'http://purl.org/rss/1.0/modules/content/' );
            $excerpt = $item->children( $namespaces['excerpt'] );
            $post['post_content'] = (string) $content->encoded;
            $post['post_excerpt'] = (string) $excerpt->encoded;

            $wp = $item->children( $namespaces['wp'] );
            $post['post_id'] = (int) $wp->post_id;
            $post['post_date'] = (string) $wp->post_date;
            $post['post_date_gmt'] = (string) $wp->post_date_gmt;
            $post['comment_status'] = (string) $wp->comment_status;
            $post['ping_status'] = (string) $wp->ping_status;
            $post['post_name'] = (string) $wp->post_name;
            $post['status'] = (string) $wp->status;
            $post['post_parent'] = (int) $wp->post_parent;
            $post['menu_order'] = (int) $wp->menu_order;
            $post['post_type'] = (string) $wp->post_type;
            $post['post_password'] = (string) $wp->post_password;
            $post['is_sticky'] = (int) $wp->is_sticky;

            if ( isset($wp->attachment_url) )
                $post['attachment_url'] = (string) $wp->attachment_url;

            foreach ( $item->category as $c )
            {
                $att = $c->attributes();
                if ( isset( $att['nicename'] ) )
                    $post['terms'][] = array(
                        'name' => (string) $c,
                        'slug' => (string) $att['nicename'],
                        'domain' => (string) $att['domain']
                    );
            }

            foreach ( $wp->postmeta as $meta )
            {
                $post['postmeta'][] = array(
                    'key' => (string) $meta->meta_key,
                    'value' => (string) $meta->meta_value
                );
            }

            foreach ( $wp->comment as $comment )
            {
                $meta = array();
                if ( isset( $comment->commentmeta ) ) {
                    foreach ( $comment->commentmeta as $m ) {
                        $meta[] = array(
                            'key' => (string) $m->meta_key,
                            'value' => (string) $m->meta_value
                        );
                    }
                }

                $post['comments'][] = array(
                    'comment_id' => (int) $comment->comment_id,
                    'comment_author' => (string) $comment->comment_author,
                    'comment_author_email' => (string) $comment->comment_author_email,
                    'comment_author_IP' => (string) $comment->comment_author_IP,
                    'comment_author_url' => (string) $comment->comment_author_url,
                    'comment_date' => (string) $comment->comment_date,
                    'comment_date_gmt' => (string) $comment->comment_date_gmt,
                    'comment_content' => (string) $comment->comment_content,
                    'comment_approved' => (string) $comment->comment_approved,
                    'comment_type' => (string) $comment->comment_type,
                    'comment_parent' => (string) $comment->comment_parent,
                    'comment_user_id' => (int) $comment->comment_user_id,
                    'commentmeta' => $meta,
                );
            }

            $posts[] = $post;
        }

        return array(
            'authors' => $authors,
            'posts' => $posts,
            'categories' => $categories,
            'tags' => $tags,
            'terms' => $terms,
            'base_url' => $base_url,
            'version' => $wxr_version
        );
    }
}

/**
 * WXR Parser that makes use of the XML Parser PHP extension.
 */
class WPvivid_WXR_Parser_XML {
    var $wp_tags = array(
        'wp:post_id', 'wp:post_date', 'wp:post_date_gmt', 'wp:comment_status', 'wp:ping_status', 'wp:attachment_url',
        'wp:status', 'wp:post_name', 'wp:post_parent', 'wp:menu_order', 'wp:post_type', 'wp:post_password',
        'wp:is_sticky', 'wp:term_id', 'wp:category_nicename', 'wp:category_parent', 'wp:cat_name', 'wp:category_description',
        'wp:tag_slug', 'wp:tag_name', 'wp:tag_description', 'wp:term_taxonomy', 'wp:term_parent',
        'wp:term_name', 'wp:term_description', 'wp:author_id', 'wp:author_login', 'wp:author_email', 'wp:author_display_name',
        'wp:author_first_name', 'wp:author_last_name',
    );
    var $wp_sub_tags = array(
        'wp:comment_id', 'wp:comment_author', 'wp:comment_author_email', 'wp:comment_author_url',
        'wp:comment_author_IP',	'wp:comment_date', 'wp:comment_date_gmt', 'wp:comment_content',
        'wp:comment_approved', 'wp:comment_type', 'wp:comment_parent', 'wp:comment_user_id',
    );

    function parse( $file ) {
        $this->wxr_version = $this->in_post = $this->cdata = $this->data = $this->sub_data = $this->in_tag = $this->in_sub_tag = false;
        $this->authors = $this->posts = $this->term = $this->category = $this->tag = array();

        $xml = xml_parser_create( 'UTF-8' );
        xml_parser_set_option( $xml, XML_OPTION_SKIP_WHITE, 1 );
        xml_parser_set_option( $xml, XML_OPTION_CASE_FOLDING, 0 );
        xml_set_object( $xml, $this );
        xml_set_character_data_handler( $xml, 'cdata' );
        xml_set_element_handler( $xml, 'tag_open', 'tag_close' );

        if ( ! xml_parse( $xml, file_get_contents( $file ), true ) ) {
            $current_line = xml_get_current_line_number( $xml );
            $current_column = xml_get_current_column_number( $xml );
            $error_code = xml_get_error_code( $xml );
            $error_string = xml_error_string( $error_code );
            return new WP_Error( 'XML_parse_error', 'There was an error when reading this WXR file', array( $current_line, $current_column, $error_string ) );
        }
        xml_parser_free( $xml );

        if ( ! preg_match( '/^\d+\.\d+$/', $this->wxr_version ) )
            return new WP_Error( 'WXR_parse_error', __( 'This does not appear to be a WXR file, missing/invalid WXR version number', 'wpvivid-backuprestore' ) );

        return array(
            'authors' => $this->authors,
            'posts' => $this->posts,
            'categories' => $this->category,
            'tags' => $this->tag,
            'terms' => $this->term,
            'base_url' => $this->base_url,
            'version' => $this->wxr_version
        );
    }

    function tag_open( $parse, $tag, $attr ) {
        if ( in_array( $tag, $this->wp_tags ) ) {
            $this->in_tag = substr( $tag, 3 );
            return;
        }

        if ( in_array( $tag, $this->wp_sub_tags ) ) {
            $this->in_sub_tag = substr( $tag, 3 );
            return;
        }

        switch ( $tag ) {
            case 'category':
                if ( isset($attr['domain'], $attr['nicename']) ) {
                    $this->sub_data['domain'] = $attr['domain'];
                    $this->sub_data['slug'] = $attr['nicename'];
                }
                break;
            case 'item': $this->in_post = true;
            case 'title': if ( $this->in_post ) $this->in_tag = 'post_title'; break;
            case 'guid': $this->in_tag = 'guid'; break;
            case 'dc:creator': $this->in_tag = 'post_author'; break;
            case 'content:encoded': $this->in_tag = 'post_content'; break;
            case 'excerpt:encoded': $this->in_tag = 'post_excerpt'; break;

            case 'wp:term_slug': $this->in_tag = 'slug'; break;
            case 'wp:meta_key': $this->in_sub_tag = 'key'; break;
            case 'wp:meta_value': $this->in_sub_tag = 'value'; break;
        }
    }

    function cdata( $parser, $cdata ) {
        if ( ! trim( $cdata ) )
            return;

        if ( false !== $this->in_tag || false !== $this->in_sub_tag ) {
            $this->cdata .= $cdata;
        } else {
            $this->cdata .= trim( $cdata );
        }
    }

    function tag_close( $parser, $tag ) {
        switch ( $tag ) {
            case 'wp:comment':
                unset( $this->sub_data['key'], $this->sub_data['value'] ); // remove meta sub_data
                if ( ! empty( $this->sub_data ) )
                    $this->data['comments'][] = $this->sub_data;
                $this->sub_data = false;
                break;
            case 'wp:commentmeta':
                $this->sub_data['commentmeta'][] = array(
                    'key' => $this->sub_data['key'],
                    'value' => $this->sub_data['value']
                );
                break;
            case 'category':
                if ( ! empty( $this->sub_data ) ) {
                    $this->sub_data['name'] = $this->cdata;
                    $this->data['terms'][] = $this->sub_data;
                }
                $this->sub_data = false;
                break;
            case 'wp:postmeta':
                if ( ! empty( $this->sub_data ) )
                    $this->data['postmeta'][] = $this->sub_data;
                $this->sub_data = false;
                break;
            case 'item':
                $this->posts[] = $this->data;
                $this->data = false;
                break;
            case 'wp:category':
            case 'wp:tag':
            case 'wp:term':
                $n = substr( $tag, 3 );
                array_push( $this->$n, $this->data );
                $this->data = false;
                break;
            case 'wp:author':
                if ( ! empty($this->data['author_login']) )
                    $this->authors[$this->data['author_login']] = $this->data;
                $this->data = false;
                break;
            case 'wp:base_site_url':
                $this->base_url = $this->cdata;
                break;
            case 'wp:wxr_version':
                $this->wxr_version = $this->cdata;
                break;

            default:
                if ( $this->in_sub_tag ) {
                    $this->sub_data[$this->in_sub_tag] = ! empty( $this->cdata ) ? $this->cdata : '';
                    $this->in_sub_tag = false;
                } else if ( $this->in_tag ) {
                    $this->data[$this->in_tag] = ! empty( $this->cdata ) ? $this->cdata : '';
                    $this->in_tag = false;
                }
        }

        $this->cdata = false;
    }
}

/**
 * WXR Parser that uses regular expressions. Fallback for installs without an XML parser.
 */
class WPvivid_WXR_Parser_Regex {
    var $authors = array();
    var $posts = array();
    var $categories = array();
    var $tags = array();
    var $terms = array();
    var $base_url = '';

    function __construct() {
        $this->has_gzip = is_callable( 'gzopen' );
    }

    function parse( $file ) {
        $wxr_version = $in_multiline = false;

        $multiline_content = '';

        $multiline_tags = array(
            'item'        => array( 'posts', array( $this, 'process_post' ) ),
            'wp:category' => array( 'categories', array( $this, 'process_category' ) ),
            'wp:tag'      => array( 'tags', array( $this, 'process_tag' ) ),
            'wp:term'     => array( 'terms', array( $this, 'process_term' ) ),
        );

        $fp = $this->fopen( $file, 'r' );
        if ( $fp ) {
            while ( ! $this->feof( $fp ) ) {
                $importline = rtrim( $this->fgets( $fp ) );

                if ( ! $wxr_version && preg_match( '|<wp:wxr_version>(\d+\.\d+)</wp:wxr_version>|', $importline, $version ) )
                    $wxr_version = $version[1];

                if ( false !== strpos( $importline, '<wp:base_site_url>' ) ) {
                    preg_match( '|<wp:base_site_url>(.*?)</wp:base_site_url>|is', $importline, $url );
                    $this->base_url = $url[1];
                    continue;
                }

                if ( false !== strpos( $importline, '<wp:author>' ) ) {
                    preg_match( '|<wp:author>(.*?)</wp:author>|is', $importline, $author );
                    $a = $this->process_author( $author[1] );
                    $this->authors[$a['author_login']] = $a;
                    continue;
                }

                foreach ( $multiline_tags as $tag => $handler ) {
                    // Handle multi-line tags on a singular line
                    if ( preg_match( '|<' . $tag . '>(.*?)</' . $tag . '>|is', $importline, $matches ) ) {
                        $this->{$handler[0]}[] = call_user_func( $handler[1], $matches[1] );

                    } elseif ( false !== ( $pos = strpos( $importline, "<$tag>" ) ) ) {
                        // Take note of any content after the opening tag
                        $multiline_content = trim( substr( $importline, $pos + strlen( $tag ) + 2 ) );

                        // We don't want to have this line added to `$is_multiline` below.
                        $importline        = '';
                        $in_multiline      = $tag;

                    } elseif ( false !== ( $pos = strpos( $importline, "</$tag>" ) ) ) {
                        $in_multiline          = false;
                        $multiline_content    .= trim( substr( $importline, 0, $pos ) );

                        $this->{$handler[0]}[] = call_user_func( $handler[1], $multiline_content );
                    }
                }

                if ( $in_multiline && $importline ) {
                    $multiline_content .= $importline . "\n";
                }
            }

            $this->fclose($fp);
        }

        if ( ! $wxr_version )
            return new WP_Error( 'WXR_parse_error', __( 'This does not appear to be a WXR file, missing/invalid WXR version number', 'wpvivid-backuprestore' ) );

        return array(
            'authors' => $this->authors,
            'posts' => $this->posts,
            'categories' => $this->categories,
            'tags' => $this->tags,
            'terms' => $this->terms,
            'base_url' => $this->base_url,
            'version' => $wxr_version
        );
    }

    function get_tag( $string, $tag ) {
        preg_match( "|<$tag.*?>(.*?)</$tag>|is", $string, $return );
        if ( isset( $return[1] ) ) {
            if ( substr( $return[1], 0, 9 ) == '<![CDATA[' ) {
                if ( strpos( $return[1], ']]]]><![CDATA[>' ) !== false ) {
                    preg_match_all( '|<!\[CDATA\[(.*?)\]\]>|s', $return[1], $matches );
                    $return = '';
                    foreach( $matches[1] as $match )
                        $return .= $match;
                } else {
                    $return = preg_replace( '|^<!\[CDATA\[(.*)\]\]>$|s', '$1', $return[1] );
                }
            } else {
                $return = $return[1];
            }
        } else {
            $return = '';
        }
        return $return;
    }

    function process_category( $c ) {
        return array(
            'term_id' => $this->get_tag( $c, 'wp:term_id' ),
            'cat_name' => $this->get_tag( $c, 'wp:cat_name' ),
            'category_nicename'	=> $this->get_tag( $c, 'wp:category_nicename' ),
            'category_parent' => $this->get_tag( $c, 'wp:category_parent' ),
            'category_description' => $this->get_tag( $c, 'wp:category_description' ),
        );
    }

    function process_tag( $t ) {
        return array(
            'term_id' => $this->get_tag( $t, 'wp:term_id' ),
            'tag_name' => $this->get_tag( $t, 'wp:tag_name' ),
            'tag_slug' => $this->get_tag( $t, 'wp:tag_slug' ),
            'tag_description' => $this->get_tag( $t, 'wp:tag_description' ),
        );
    }

    function process_term( $t ) {
        return array(
            'term_id' => $this->get_tag( $t, 'wp:term_id' ),
            'term_taxonomy' => $this->get_tag( $t, 'wp:term_taxonomy' ),
            'slug' => $this->get_tag( $t, 'wp:term_slug' ),
            'term_parent' => $this->get_tag( $t, 'wp:term_parent' ),
            'term_name' => $this->get_tag( $t, 'wp:term_name' ),
            'term_description' => $this->get_tag( $t, 'wp:term_description' ),
        );
    }

    function process_author( $a ) {
        return array(
            'author_id' => $this->get_tag( $a, 'wp:author_id' ),
            'author_login' => $this->get_tag( $a, 'wp:author_login' ),
            'author_email' => $this->get_tag( $a, 'wp:author_email' ),
            'author_display_name' => $this->get_tag( $a, 'wp:author_display_name' ),
            'author_first_name' => $this->get_tag( $a, 'wp:author_first_name' ),
            'author_last_name' => $this->get_tag( $a, 'wp:author_last_name' ),
        );
    }

    function process_post( $post ) {
        $post_id        = $this->get_tag( $post, 'wp:post_id' );
        $post_title     = $this->get_tag( $post, 'title' );
        $post_date      = $this->get_tag( $post, 'wp:post_date' );
        $post_date_gmt  = $this->get_tag( $post, 'wp:post_date_gmt' );
        $comment_status = $this->get_tag( $post, 'wp:comment_status' );
        $ping_status    = $this->get_tag( $post, 'wp:ping_status' );
        $status         = $this->get_tag( $post, 'wp:status' );
        $post_name      = $this->get_tag( $post, 'wp:post_name' );
        $post_parent    = $this->get_tag( $post, 'wp:post_parent' );
        $menu_order     = $this->get_tag( $post, 'wp:menu_order' );
        $post_type      = $this->get_tag( $post, 'wp:post_type' );
        $post_password  = $this->get_tag( $post, 'wp:post_password' );
        $is_sticky      = $this->get_tag( $post, 'wp:is_sticky' );
        $guid           = $this->get_tag( $post, 'guid' );
        $post_author    = $this->get_tag( $post, 'dc:creator' );

        $post_excerpt = $this->get_tag( $post, 'excerpt:encoded' );
        $post_excerpt = preg_replace_callback( '|<(/?[A-Z]+)|', array( &$this, '_normalize_tag' ), $post_excerpt );
        $post_excerpt = str_replace( '<br>', '<br />', $post_excerpt );
        $post_excerpt = str_replace( '<hr>', '<hr />', $post_excerpt );

        $post_content = $this->get_tag( $post, 'content:encoded' );
        $post_content = preg_replace_callback( '|<(/?[A-Z]+)|', array( &$this, '_normalize_tag' ), $post_content );
        $post_content = str_replace( '<br>', '<br />', $post_content );
        $post_content = str_replace( '<hr>', '<hr />', $post_content );

        $postdata = compact( 'post_id', 'post_author', 'post_date', 'post_date_gmt', 'post_content', 'post_excerpt',
            'post_title', 'status', 'post_name', 'comment_status', 'ping_status', 'guid', 'post_parent',
            'menu_order', 'post_type', 'post_password', 'is_sticky'
        );

        $attachment_url = $this->get_tag( $post, 'wp:attachment_url' );
        if ( $attachment_url )
            $postdata['attachment_url'] = $attachment_url;

        preg_match_all( '|<category domain="([^"]+?)" nicename="([^"]+?)">(.+?)</category>|is', $post, $terms, PREG_SET_ORDER );
        foreach ( $terms as $t ) {
            $post_terms[] = array(
                'slug' => $t[2],
                'domain' => $t[1],
                'name' => str_replace( array( '<![CDATA[', ']]>' ), '', $t[3] ),
            );
        }
        if ( ! empty( $post_terms ) ) $postdata['terms'] = $post_terms;

        preg_match_all( '|<wp:comment>(.+?)</wp:comment>|is', $post, $comments );
        $comments = $comments[1];
        if ( $comments ) {
            foreach ( $comments as $comment ) {
                preg_match_all( '|<wp:commentmeta>(.+?)</wp:commentmeta>|is', $comment, $commentmeta );
                $commentmeta = $commentmeta[1];
                $c_meta = array();
                foreach ( $commentmeta as $m ) {
                    $c_meta[] = array(
                        'key' => $this->get_tag( $m, 'wp:meta_key' ),
                        'value' => $this->get_tag( $m, 'wp:meta_value' ),
                    );
                }

                $post_comments[] = array(
                    'comment_id' => $this->get_tag( $comment, 'wp:comment_id' ),
                    'comment_author' => $this->get_tag( $comment, 'wp:comment_author' ),
                    'comment_author_email' => $this->get_tag( $comment, 'wp:comment_author_email' ),
                    'comment_author_IP' => $this->get_tag( $comment, 'wp:comment_author_IP' ),
                    'comment_author_url' => $this->get_tag( $comment, 'wp:comment_author_url' ),
                    'comment_date' => $this->get_tag( $comment, 'wp:comment_date' ),
                    'comment_date_gmt' => $this->get_tag( $comment, 'wp:comment_date_gmt' ),
                    'comment_content' => $this->get_tag( $comment, 'wp:comment_content' ),
                    'comment_approved' => $this->get_tag( $comment, 'wp:comment_approved' ),
                    'comment_type' => $this->get_tag( $comment, 'wp:comment_type' ),
                    'comment_parent' => $this->get_tag( $comment, 'wp:comment_parent' ),
                    'comment_user_id' => $this->get_tag( $comment, 'wp:comment_user_id' ),
                    'commentmeta' => $c_meta,
                );
            }
        }
        if ( ! empty( $post_comments ) ) $postdata['comments'] = $post_comments;

        preg_match_all( '|<wp:postmeta>(.+?)</wp:postmeta>|is', $post, $postmeta );
        $postmeta = $postmeta[1];
        if ( $postmeta ) {
            foreach ( $postmeta as $p ) {
                $post_postmeta[] = array(
                    'key' => $this->get_tag( $p, 'wp:meta_key' ),
                    'value' => $this->get_tag( $p, 'wp:meta_value' ),
                );
            }
        }
        if ( ! empty( $post_postmeta ) ) $postdata['postmeta'] = $post_postmeta;

        return $postdata;
    }

    function _normalize_tag( $matches ) {
        return '<' . strtolower( $matches[1] );
    }

    function fopen( $filename, $mode = 'r' ) {
        if ( $this->has_gzip )
            return gzopen( $filename, $mode );
        return fopen( $filename, $mode );
    }

    function feof( $fp ) {
        if ( $this->has_gzip )
            return gzeof( $fp );
        return feof( $fp );
    }

    function fgets( $fp, $len = 8192 ) {
        if ( $this->has_gzip )
            return gzgets( $fp, $len );
        return fgets( $fp, $len );
    }

    function fclose( $fp ) {
        if ( $this->has_gzip )
            return gzclose( $fp );
        return fclose( $fp );
    }
}includes/class-wpvivid-setting.php000064400000056406151327705670013361 0ustar00<?php

if (!defined('WPVIVID_PLUGIN_DIR')){
    die;
}
class WPvivid_Setting
{
    public static function init_option()
    {
        $ret=self::get_option('wpvivid_email_setting');
        if(empty($ret))
        {
            self::set_default_email_option();
        }

        $ret=self::get_option('wpvivid_compress_setting');
        if(empty($ret))
        {
            self::set_default_compress_option();
        }

        $ret=self::get_option('wpvivid_local_setting');
        if(empty($ret))
        {
            self::set_default_local_option();
        }

        $ret=self::get_option('wpvivid_upload_setting');
        if(empty($ret))
        {
            self::set_default_upload_option();
        }

        $ret=self::get_option('wpvivid_common_setting');
        if(empty($ret))
        {
            self::set_default_common_option();
        }
    }

    public static function get_default_option($option_name)
    {
        $options=array();

        switch ($option_name)
        {
            case 'wpvivid_compress_setting':
                $options=self::set_default_compress_option();
                break;
            case 'wpvivid_local_setting':
                $options=self::set_default_local_option();
                break;
            case 'wpvivid_upload_setting':
                $options=self::set_default_upload_option();
                break;
            case 'wpvivid_common_setting':
                $options=self::set_default_common_option();
                break;
        }
        return $options;
    }

    public static function set_default_option()
    {
        self::set_default_compress_option();
        self::set_default_local_option();
        self::set_default_upload_option();
        self::set_default_common_option();
    }

    public static function set_default_compress_option()
    {
        $compress_option['compress_type']=WPVIVID_DEFAULT_COMPRESS_TYPE;
        $compress_option['max_file_size']=WPVIVID_DEFAULT_MAX_FILE_SIZE;
        $compress_option['no_compress']=WPVIVID_DEFAULT_NO_COMPRESS;
        $compress_option['use_temp_file']=WPVIVID_DEFAULT_USE_TEMP;
        $compress_option['use_temp_size']=WPVIVID_DEFAULT_USE_TEMP_SIZE;
        $compress_option['exclude_file_size']=WPVIVID_DEFAULT_EXCLUDE_FILE_SIZE;
        $compress_option['subpackage_plugin_upload']=WPVIVID_DEFAULT_SUBPACKAGE_PLUGIN_UPLOAD;
        self::update_option('wpvivid_compress_setting',$compress_option);
        return $compress_option;
    }

    public static function set_default_local_option()
    {
        $local_option['path']=WPVIVID_DEFAULT_BACKUP_DIR;
        $local_option['save_local']=1;
        self::update_option('wpvivid_local_setting',$local_option);
        return $local_option;
    }

    public static function set_default_upload_option()
    {
        $upload_option=array();
        self::update_option('wpvivid_upload_setting',$upload_option);
        return $upload_option;
    }

    public static function set_default_email_option()
    {
        $email_option['send_to']=array();
        $email_option['always']=true;
        $email_option['email_enable']=false;
        self::update_option('wpvivid_email_setting',$email_option);
        return $email_option;
    }

    public static function set_default_common_option()
    {
        $sapi_type=php_sapi_name();

        if($sapi_type=='cgi-fcgi'||$sapi_type==' fpm-fcgi')
        {
            $common_option['max_execution_time']=WPVIVID_MAX_EXECUTION_TIME_FCGI;
        }
        else
        {
            $common_option['max_execution_time']=WPVIVID_MAX_EXECUTION_TIME;
        }

        $common_option['log_save_location']=WPVIVID_DEFAULT_LOG_DIR;
        $common_option['max_backup_count']=WPVIVID_DEFAULT_BACKUP_COUNT;
        $common_option['show_admin_bar']=WPVIVID_DEFAULT_ADMIN_BAR;
        //$common_option['show_tab_menu']=WPVIVID_DEFAULT_TAB_MENU;
        $common_option['domain_include']=WPVIVID_DEFAULT_DOMAIN_INCLUDE;
        $common_option['estimate_backup']=WPVIVID_DEFAULT_ESTIMATE_BACKUP;
        $common_option['max_resume_count']=WPVIVID_RESUME_RETRY_TIMES;
        $common_option['memory_limit']=WPVIVID_MEMORY_LIMIT;
        $common_option['restore_memory_limit']=WPVIVID_RESTORE_MEMORY_LIMIT;
        $common_option['migrate_size']=WPVIVID_MIGRATE_SIZE;
        self::update_option('wpvivid_common_setting',$common_option);
        return $common_option;
    }

    public static function get_option($option_name, $default = array())
    {
        $ret = get_option($option_name, $default);
        if(empty($ret))
        {
            self::get_default_option($option_name);
        }
        return $ret;
    }

    public static function get_last_backup_message($option_name, $default = array()){
        $message = self::get_option($option_name, $default);
        $ret = array();
        if(!empty($message['id'])) {
            $ret['id'] = $message['id'];
            $ret['status'] = $message['status'];
            $ret['status']['start_time'] = gmdate("M d, Y H:i", $ret['status']['start_time']);
            $ret['status']['run_time'] = gmdate("M d, Y H:i", $ret['status']['run_time']);
            $ret['status']['timeout'] = gmdate("M d, Y H:i", $ret['status']['timeout']);
            if(isset($message['options']['log_file_name']))
                $ret['log_file_name'] = $message['options']['log_file_name'];
            else
                $ret['log_file_name'] ='';
        }
        return $ret;
    }

    public static function get_backupdir()
    {
        $dir=self::get_option('wpvivid_local_setting');

        if(!isset($dir['path']))
        {
            $dir=self::set_default_local_option();
        }
        if(!is_dir(WP_CONTENT_DIR.DIRECTORY_SEPARATOR.$dir['path']))
        {
            @mkdir(WP_CONTENT_DIR.DIRECTORY_SEPARATOR.$dir['path'],0777,true);
            //@fopen(WP_CONTENT_DIR.DIRECTORY_SEPARATOR.$dir['path'].DIRECTORY_SEPARATOR.'index.html', 'x');
            $tempfile=@fopen(WP_CONTENT_DIR.DIRECTORY_SEPARATOR.$dir['path'].DIRECTORY_SEPARATOR.'.htaccess', 'x');
            if($tempfile)
            {
                //$text="deny from all";
                $text="<IfModule mod_rewrite.c>\r\nRewriteEngine On\r\nRewriteRule .* - [F,L]\r\n</IfModule>";
                fwrite($tempfile,$text );
                fclose($tempfile);
            }
            else
            {
                return false;
            }

        }

        return $dir['path'];
    }
    
    public static function wpvivid_remove_directory($directory)
    {
        if(file_exists($directory))
        {
            if($dir_handle=@opendir($directory))
            {
                while($filename=readdir($dir_handle))
                {
                    if($filename!='.' && $filename!='..')
                    {
                        $subFile=$directory."/".$filename;
                        if(is_dir($subFile))
                        {
                            self::wpvivid_remove_directory($subFile);
                        }
                        if(is_file($subFile))
                        {
                            wp_delete_file($subFile);
                        }
                    }
                }
                closedir($dir_handle);
                rmdir($directory);
            }
        }
    }

    public static function wpvivid_write_htaccess_rule($wpvivid_backup_dir_htaccess)
    {
        $tempfile=@fopen($wpvivid_backup_dir_htaccess, 'x');
        if($tempfile)
        {
            $text="<IfModule mod_rewrite.c>\r\nRewriteEngine On\r\nRewriteRule .* - [F,L]\r\n</IfModule>";
            fwrite($tempfile,$text );
            fclose($tempfile);
        }
    }

    public static function get_save_local()
    {
        $local=self::get_option('wpvivid_local_setting');

        if(!isset($local['save_local']))
        {
            $local=self::set_default_local_option();
        }

        return $local['save_local'];
    }

    public static function update_option($option_name,$options)
    {
        update_option($option_name,$options,'no');
    }

    public static function delete_option($option_name)
    {
        delete_option($option_name);
    }

    public static function get_tasks()
    {
        $default = array();
        return $options = get_option('wpvivid_task_list', $default);
    }

    public static function update_task($id,$task)
    {
        $default = array();
        $options = get_option('wpvivid_task_list', $default);
        $options[$id]=$task;
        self::update_option('wpvivid_task_list',$options);
    }

    public static function delete_task($id)
    {
        $default = array();
        $options = get_option('wpvivid_task_list', $default);
        unset($options[$id]);
        self::update_option('wpvivid_task_list',$options);
    }

    public static function check_compress_options()
    {
        $options =self::get_option('wpvivid_compress_setting');

        if(!isset($options['compress_type'])||!isset($options['max_file_size'])||
            !isset($options['no_compress'])||!isset($options['exclude_file_size'])||
            !isset($options['use_temp_file'])||!isset($options['use_temp_size']))
        {
            self::set_default_compress_option();
        }
    }

    public static function check_local_options()
    {
        $options =self::get_option('wpvivid_local_setting');

        if(!isset($options['path'])||!isset($options['save_local']))
        {
            self::set_default_local_option();
        }

        return true;
    }

    /*public static function get_backup_options($post)
    {
        self::check_compress_options();
        self::check_local_options();

        if($post=='files+db')
        {
            $backup_options['backup']['backup_type'][WPVIVID_BACKUP_TYPE_DB]=0;
            $backup_options['backup']['backup_type'][WPVIVID_BACKUP_TYPE_THEMES]=0;
            $backup_options['backup']['backup_type'][WPVIVID_BACKUP_TYPE_PLUGIN]=0;
            $backup_options['backup']['backup_type'][WPVIVID_BACKUP_TYPE_UPLOADS]=0;
            $backup_options['backup']['backup_type'][WPVIVID_BACKUP_TYPE_CONTENT]=0;
            $backup_options['backup']['backup_type'][WPVIVID_BACKUP_TYPE_CORE]=0;
        }
        else if($post=='files')
        {
            $backup_options['backup']['backup_type'][WPVIVID_BACKUP_TYPE_THEMES]=0;
            $backup_options['backup']['backup_type'][WPVIVID_BACKUP_TYPE_PLUGIN]=0;
            $backup_options['backup']['backup_type'][WPVIVID_BACKUP_TYPE_UPLOADS]=0;
            $backup_options['backup']['backup_type'][WPVIVID_BACKUP_TYPE_CONTENT]=0;
            $backup_options['backup']['backup_type'][WPVIVID_BACKUP_TYPE_CORE]=0;
        }
        else if($post=='db')
        {
            $backup_options['backup']['backup_type'][WPVIVID_BACKUP_TYPE_DB]=0;
        }
        else
        {
            //return false;
        }

        $backup_options['compress']=self::get_option('wpvivid_compress_setting');
        $backup_options['dir']=self::get_backupdir();
        return $backup_options;
    }*/

    public static function get_remote_option($id)
    {
        $upload_options=self::get_option('wpvivid_upload_setting');
        if(array_key_exists($id,$upload_options))
        {
            return $upload_options[$id];
        }
        else
        {
            return false;
        }
    }

    public static function get_remote_options($remote_ids=array())
    {
        if(empty($remote_ids))
        {
            $remote_ids=WPvivid_Setting::get_user_history('remote_selected');
        }

        if(empty($remote_ids))
        {
            return false;
        }

        $options=array();
        $upload_options=WPvivid_Setting::get_option('wpvivid_upload_setting');
        foreach ($remote_ids as $id)
        {
            if(array_key_exists($id,$upload_options))
            {
                $options[$id]=$upload_options[$id];
            }
        }
        if(empty($options))
            return false;
        else
            return $options;
    }

    public static function get_all_remote_options()
    {
        $upload_options=self::get_option('wpvivid_upload_setting');
        $upload_options['remote_selected']=WPvivid_Setting::get_user_history('remote_selected');
        return $upload_options;
    }

    public static function add_remote_options($remote)
    {
        $upload_options=self::get_option('wpvivid_upload_setting');
        $id=uniqid('wpvivid-remote-');

        $remote=apply_filters('wpvivid_pre_add_remote',$remote,$id);

        $upload_options[$id]=$remote;
        self::update_option('wpvivid_upload_setting',$upload_options);
        return $id;
    }

    public static function delete_remote_option($id)
    {
        do_action('wpvivid_delete_remote_token',$id);

        $upload_options=self::get_option('wpvivid_upload_setting');

        if(array_key_exists($id,$upload_options))
        {
            unset( $upload_options[$id]);

            self::update_option('wpvivid_upload_setting',$upload_options);
            return true;
        }
        else
        {
            return false;
        }
    }

    public static function update_remote_option($remote_id,$remote)
    {
        $upload_options=self::get_option('wpvivid_upload_setting');

        if(array_key_exists($remote_id,$upload_options))
        {
            $remote=apply_filters('wpvivid_pre_add_remote',$remote,$remote_id);
            $upload_options[$remote_id]=$remote;
            self::update_option('wpvivid_upload_setting',$upload_options);
            return true;
        }
        else
        {
            return false;
        }
    }

    public static function get_setting($all,$options_name)
    {
        $get_options=array();
        if($all==true)
        {
            $get_options[]='wpvivid_email_setting';
            $get_options[]='wpvivid_compress_setting';
            $get_options[]='wpvivid_local_setting';
            $get_options[]='wpvivid_common_setting';
            $get_options = apply_filters('wpvivid_get_setting_addon', $get_options);
        }
        else
        {
            $get_options[]=$options_name;
        }

        $ret['result']='success';
        $ret['options']=array();

        foreach ($get_options as $option_name)
        {
            $ret['options'][$option_name]=self::get_option($option_name);
        }

        return $ret;
    }

    public static function update_setting($options)
    {
        foreach ($options as $option_name=>$option)
        {
            self::update_option($option_name,$option);
        }
        $ret['result']='success';
        return $ret;
    }

    public static function export_setting_to_json($setting=true,$history=true,$review=true,$backup_list=true)
    {
        global $wpvivid_plugin;
        $json['plugin']=$wpvivid_plugin->get_plugin_name();
        $json['version']=WPVIVID_PLUGIN_VERSION;
        $json['setting']=$setting;
        $json['history']=$history;
        $json['data']['wpvivid_init']=self::get_option('wpvivid_init');

        if($setting)
        {
            $json['data']['wpvivid_schedule_setting']=self::get_option('wpvivid_schedule_setting');
            if(!empty( $json['data']['wpvivid_schedule_setting']))
            {
                if(isset($json['data']['wpvivid_schedule_setting']['backup']['backup_files']))
                    $json['data']['wpvivid_schedule_setting']['backup_type']=$json['data']['wpvivid_schedule_setting']['backup']['backup_files'];
                if(isset($json['data']['wpvivid_schedule_setting']['backup']['local']))
                {
                    if($json['data']['wpvivid_schedule_setting']['backup']['local'] == 1){
                        $json['data']['wpvivid_schedule_setting']['save_local_remote']='local';
                    }
                    else{
                        $json['data']['wpvivid_schedule_setting']['save_local_remote']='remote';
                    }
                }

                $json['data']['wpvivid_schedule_setting']['lock']=0;
                if(wp_get_schedule(WPVIVID_MAIN_SCHEDULE_EVENT))
                {
                    $recurrence = wp_get_schedule(WPVIVID_MAIN_SCHEDULE_EVENT);
                    $timestamp = wp_next_scheduled(WPVIVID_MAIN_SCHEDULE_EVENT);
                    $json['data']['wpvivid_schedule_setting']['recurrence']=$recurrence;
                    $json['data']['wpvivid_schedule_setting']['next_start']=$timestamp;
                }
            }
            else
            {
                $json['data']['wpvivid_schedule_setting']=array();
            }
            $json['data']['wpvivid_compress_setting']=self::get_option('wpvivid_compress_setting');
            $json['data']['wpvivid_local_setting']=self::get_option('wpvivid_local_setting');
            $json['data']['wpvivid_upload_setting']=self::get_option('wpvivid_upload_setting');
            $json['data']['wpvivid_common_setting']=self::get_option('wpvivid_common_setting');
            $json['data']['wpvivid_email_setting']=self::get_option('wpvivid_email_setting');
            $json['data']['wpvivid_saved_api_token']=self::get_option('wpvivid_saved_api_token');
            $json = apply_filters('wpvivid_export_setting_addon', $json);
            /*if(isset($json['data']['wpvivid_local_setting']['path'])){
                unset($json['data']['wpvivid_local_setting']['path']);
            }*/
            if(isset($json['data']['wpvivid_common_setting']['log_save_location'])){
                unset($json['data']['wpvivid_common_setting']['log_save_location']);
            }
            if(isset($json['data']['wpvivid_common_setting']['backup_prefix'])){
                unset($json['data']['wpvivid_common_setting']['backup_prefix']);
            }
        }

        if($history)
        {
            $json['data']['wpvivid_task_list']=self::get_option('wpvivid_task_list');
            $json['data']['wpvivid_last_msg']=self::get_option('wpvivid_last_msg');
            $json['data']['wpvivid_user_history']=self::get_option('wpvivid_user_history');
            $json = apply_filters('wpvivid_history_addon', $json);
        }

        if($backup_list){
            $json['data']['wpvivid_backup_list']=self::get_option('wpvivid_backup_list');
            $json = apply_filters('wpvivid_backup_list_addon', $json);
        }
        else{
            if(isset($json['data']['wpvivid_new_remote_list']))
            {
                unset($json['data']['wpvivid_new_remote_list']);
            }
        }

        if($review)
        {
            $json['data']['wpvivid_need_review']=self::get_option('wpvivid_need_review');
            $json['data']['cron_backup_count']=self::get_option('cron_backup_count');
            $json['data']['wpvivid_review_msg']=self::get_option('wpvivid_review_msg');
            $json['data']['wpvivid_review_time']=self::get_option('wpvivid_review_time', false);
            $json['data']['wpvivid_review_type']=self::get_option('wpvivid_review_type', false);
            $json = apply_filters('wpvivid_review_addon', $json);
        }
        return $json;
    }

    public static function import_json_to_setting($json)
    {
        wp_cache_delete('notoptions', 'options');
        wp_cache_delete('alloptions', 'options');
        foreach ($json['data'] as $option_name=>$option)
        {
            wp_cache_delete($option_name, 'options');
            delete_option($option_name);
            self::update_option($option_name,$option);
        }
    }

    public static function set_max_backup_count($count)
    {
        $options=self::get_option('wpvivid_common_setting');
        $options['max_backup_count']=$count;
        self::update_option('wpvivid_common_setting',$options);
    }

    public static function get_max_backup_count()
    {
        $options=self::get_option('wpvivid_common_setting');
        if(isset($options['max_backup_count']))
        {
            return $options['max_backup_count'];
        }
        else
        {
            return WPVIVID_MAX_BACKUP_COUNT;
        }
    }

    public static function get_mail_setting()
    {
        return self::get_option('wpvivid_email_setting');
    }

    public static function get_admin_bar_setting(){
        $options=self::get_option('wpvivid_common_setting');
        if(isset($options['show_admin_bar']))
        {
            if($options['show_admin_bar']){
                return true;
            }
            else{
                return false;
            }
        }
        else
        {
            return true;
        }
    }

    public static function update_user_history($action,$value)
    {
        $options=self::get_option('wpvivid_user_history');
        $options[$action]=$value;
        self::update_option('wpvivid_user_history',$options);
    }

    public static function get_user_history($action)
    {
        $options=self::get_option('wpvivid_user_history');
        if(array_key_exists($action,$options))
        {
            return $options[$action];
        }
        else
        {
            return array();
        }
    }

    public static function get_retain_local_status()
    {
        $options=self::get_option('wpvivid_common_setting');
        if(isset($options['retain_local']))
        {
            if($options['retain_local']){
                return true;
            }
            else{
                return false;
            }
        }
        else
        {
            return false;
        }
    }

    public static function get_sync_data()
    {
        $data['setting']['wpvivid_compress_setting']=self::get_option('wpvivid_compress_setting');
        $data['setting']['wpvivid_local_setting']=self::get_option('wpvivid_local_setting');
        $data['setting']['wpvivid_common_setting']=self::get_option('wpvivid_common_setting');
        $data['setting']['wpvivid_email_setting']=self::get_option('wpvivid_email_setting');
        $data['setting']['cron_backup_count']=self::get_option('cron_backup_count');
        $data['schedule']=self::get_option('wpvivid_schedule_setting');
        $data['remote']['upload']=self::get_option('wpvivid_upload_setting');
        $data['remote']['history']=self::get_option('wpvivid_user_history');
        $data['last_backup_report'] = get_option('wpvivid_backup_reports');

        $data['setting_addon'] = $data['setting'];
        $data['setting_addon']['wpvivid_staging_options']=array();
        $data['backup_custom_setting']=array();
        $data['menu_capability']=array();
        $data['white_label_setting']=array();
        $data['incremental_backup_setting']=array();
        $data['schedule_addon']=array();
        $data['time_zone']=false;
        $data['is_pro']=false;
        $data['is_install']=false;
        $data['is_login']=false;
        $data['latest_version']='';
        $data['current_version']='';
        $data['dashboard_version'] = '';
        $data['addons_info'] = array();
        $data=apply_filters('wpvivid_get_wpvivid_info_addon_mainwp_ex', $data);
        return $data;
    }
}includes/upload-cleaner/class-wpvivid-upload-cleaner-setting.php000064400000114566151327705670021147 0ustar00<?php
if (!defined('WPVIVID_PLUGIN_DIR'))
{
    die;
}

if ( ! class_exists( 'WP_List_Table' ) )
{
    require_once( ABSPATH . 'wp-admin/includes/class-wp-list-table.php' );
}

class WPvivid_Exclude_Files_List extends WP_List_Table
{
    public $list;
    public $type;
    public $page_num;
    public $parent;

    public function __construct( $args = array() )
    {
        global $wpdb;
        parent::__construct(
            array(
                'plural' => 'upload_files',
                'screen' => 'upload_files',
            )
        );
    }

    public function set_parent($parent)
    {
        $this->parent=$parent;
    }

    public function set_list($list,$page_num=1)
    {
        $this->list=$list;
        $this->page_num=$page_num;
    }

    protected function get_table_classes()
    {
        return array( 'widefat striped' );
    }

    public function get_columns()
    {
        $sites_columns = array(
            'cb'          => ' ',
            'file_regex'    => __( 'File Regex', 'wpvivid-backuprestore' )
        );

        return $sites_columns;
    }

    public function get_pagenum()
    {
        if($this->page_num=='first')
        {
            $this->page_num=1;
        }
        else if($this->page_num=='last')
        {
            $this->page_num=$this->_pagination_args['total_pages'];
        }
        $pagenum = $this->page_num ? $this->page_num : 0;

        if ( isset( $this->_pagination_args['total_pages'] ) && $pagenum > $this->_pagination_args['total_pages'] )
        {
            $pagenum = $this->_pagination_args['total_pages'];
        }

        return max( 1, $pagenum );
    }

    public function column_cb( $item )
    {
        echo '<input type="checkbox" name="regex_list" />';

    }

    public function column_file_regex( $item )
    {
        echo esc_html($item);
    }

    public function has_items()
    {
        return !empty($this->list);
    }

    public function prepare_items()
    {
        $columns = $this->get_columns();
        $hidden = array();
        $sortable = array();
        $this->_column_headers = array($columns, $hidden, $sortable);

        $total_items =sizeof($this->list);

        $this->set_pagination_args(
            array(
                'total_items' => $total_items,
                'per_page'    => 10,
            )
        );
    }

    public function display_rows()
    {
        $this->_display_rows( $this->list );
    }

    private function _display_rows( $list )
    {
        $page=$this->get_pagenum();

        $page_list=$list;
        $temp_page_list=array();

        $count=0;
        while ( $count<$page )
        {
            $temp_page_list = array_splice( $page_list, 0, 10);
            $count++;
        }

        foreach ( $temp_page_list as $key=>$item)
        {
            $this->single_row($item);
        }
    }

    public function single_row($item)
    {
        ?>
        <tr file_regex="<?php echo esc_attr($item)?>">
            <?php $this->single_row_columns( $item ); ?>
        </tr>
        <?php
    }

    protected function pagination( $which )
    {
        if ( empty( $this->_pagination_args ) )
        {
            return;
        }

        $total_items     = $this->_pagination_args['total_items'];
        $total_pages     = $this->_pagination_args['total_pages'];
        $infinite_scroll = false;
        if ( isset( $this->_pagination_args['infinite_scroll'] ) )
        {
            $infinite_scroll = $this->_pagination_args['infinite_scroll'];
        }

        if ( 'top' === $which && $total_pages > 1 )
        {
            $this->screen->render_screen_reader_content( 'heading_pagination' );
        }

        $output = '<span class="displaying-num">' . sprintf( _n( '%s item', '%s items', $total_items, 'wpvivid-backuprestore' ), number_format_i18n( $total_items ) ) . '</span>';

        $current              = $this->get_pagenum();

        $page_links = array();

        $total_pages_before = '<span class="paging-input">';
        $total_pages_after  = '</span></span>';

        $disable_first = $disable_last = $disable_prev = $disable_next = false;

        if ( $current == 1 ) {
            $disable_first = true;
            $disable_prev  = true;
        }
        if ( $current == 2 ) {
            $disable_first = true;
        }
        if ( $current == $total_pages ) {
            $disable_last = true;
            $disable_next = true;
        }
        if ( $current == $total_pages - 1 ) {
            $disable_last = true;
        }

        if ( $disable_first ) {
            $page_links[] = '<span class="tablenav-pages-navspan button disabled" aria-hidden="true">&laquo;</span>';
        } else {
            $page_links[] = sprintf(
                "<div class='first-page button'><span class='screen-reader-text'>%s</span><span aria-hidden='true'>%s</span></div>",
                __( 'First page', 'wpvivid-backuprestore' ),
                '&laquo;'
            );
        }

        if ( $disable_prev ) {
            $page_links[] = '<span class="tablenav-pages-navspan button disabled" aria-hidden="true">&lsaquo;</span>';
        } else {
            $page_links[] = sprintf(
                "<div class='prev-page button' value='%s'><span class='screen-reader-text'>%s</span><span aria-hidden='true'>%s</span></div>",
                $current,
                __( 'Previous page', 'wpvivid-backuprestore' ),
                '&lsaquo;'
            );
        }

        if ( 'bottom' === $which ) {
            $html_current_page  = $current;
            $total_pages_before = '<span class="screen-reader-text">' . __( 'Current Page', 'wpvivid-backuprestore' ) . '</span><span id="table-paging" class="paging-input"><span class="tablenav-paging-text">';
        } else {
            $html_current_page = sprintf(
                "%s<input class='current-page'  type='text' name='paged' value='%s' size='%d' aria-describedby='table-paging' /><span class='tablenav-paging-text'>",
                '<label  class="screen-reader-text">' . __( 'Current Page', 'wpvivid-backuprestore' ) . '</label>',
                $current,
                strlen( $total_pages )
            );
        }
        $html_total_pages = sprintf( "<span class='total-pages'>%s</span>", number_format_i18n( $total_pages ) );
        $page_links[]     = $total_pages_before . sprintf( _x( '%1$s of %2$s', 'paging', 'wpvivid-backuprestore' ), $html_current_page, $html_total_pages ) . $total_pages_after;

        if ( $disable_next ) {
            $page_links[] = '<span class="tablenav-pages-navspan button disabled" aria-hidden="true">&rsaquo;</span>';
        } else {
            $page_links[] = sprintf(
                "<div class='next-page button' value='%s'><span class='screen-reader-text'>%s</span><span aria-hidden='true'>%s</span></div>",
                $current,
                __( 'Next page', 'wpvivid-backuprestore' ),
                '&rsaquo;'
            );
        }

        if ( $disable_last ) {
            $page_links[] = '<span class="tablenav-pages-navspan button disabled" aria-hidden="true">&raquo;</span>';
        } else {
            $page_links[] = sprintf(
                "<div class='last-page button'><span class='screen-reader-text'>%s</span><span aria-hidden='true'>%s</span></div>",
                __( 'Last page', 'wpvivid-backuprestore' ),
                '&raquo;'
            );
        }

        $pagination_links_class = 'pagination-links';
        if ( ! empty( $infinite_scroll ) ) {
            $pagination_links_class .= ' hide-if-js';
        }
        $output .= "\n<span class='$pagination_links_class'>" . join( "\n", $page_links ) . '</span>';

        if ( $total_pages ) {
            $page_class = $total_pages < 2 ? ' one-page' : '';
        } else {
            $page_class = ' no-pages';
        }
        $this->_pagination = "<div class='tablenav-pages{$page_class}'>$output</div>";

        echo $this->_pagination;
    }

    protected function display_tablenav( $which ) {
        $css_type = '';
        if ( 'top' === $which ) {
            wp_nonce_field( 'bulk-' . $this->_args['plural'] );
            $css_type = 'margin: 0 0 10px 0';
        }
        else if( 'bottom' === $which ) {
            $css_type = 'margin: 10px 0 0 0';
        }

        $total_pages     = $this->_pagination_args['total_pages'];
        if ( $total_pages >1)
        {
            ?>
            <div class="tablenav <?php echo esc_attr( $which ); ?>" style="<?php echo esc_attr($css_type); ?>">
                <div class="alignleft actions bulkactions">
                    <label for="wpvivid_uc_exclude_regex_bulk_action" class="screen-reader-text">Select bulk action</label>
                    <select name="action" id="wpvivid_uc_exclude_regex_bulk_action">
                        <option value="remove_exclude_regex">Remove</option>
                    </select>
                    <input type="submit" class="button action" value="Apply">
                </div>
                <?php
                $this->extra_tablenav( $which );
                $this->pagination( $which );
                ?>
                <br class="clear" />
            </div>
            <?php
        }
        else
        {
            ?>
            <div class="tablenav <?php echo esc_attr( $which ); ?>" style="<?php echo esc_attr($css_type); ?>">
                <div class="alignleft actions bulkactions">
                    <label for="wpvivid_uc_exclude_regex_bulk_action" class="screen-reader-text">Select bulk action</label>
                    <select name="action" id="wpvivid_uc_exclude_regex_bulk_action">
                        <option value="remove_exclude_regex">Remove</option>
                    </select>
                    <input type="submit" class="button action" value="Apply">
                </div>
                <br class="clear" />
            </div>
            <?php
        }
    }

    public function display()
    {
        $singular = $this->_args['singular'];

        $this->display_tablenav( 'top' );

        $this->screen->render_screen_reader_content( 'heading_list' );
        ?>
        <table class="wp-list-table <?php echo esc_attr(implode( ' ', $this->get_table_classes() )); ?>" >
            <thead>
            <tr>
                <?php $this->print_column_headers(); ?>
            </tr>
            </thead>

            <tbody id="the-list"
                <?php
                if ( $singular ) {
                    echo esc_attr(" data-wp-lists='list:$singular'");
                }
                ?>
            >
            <?php $this->display_rows_or_placeholder(); ?>
            </tbody>

            <tfoot>
            <tr>
                <?php $this->print_column_headers( false ); ?>
            </tr>
            </tfoot>

        </table>
        <?php
    }
}

class WPvivid_Post_Type_List extends WP_List_Table
{
    public $list;
    public $type;
    public $page_num;
    public $parent;

    public function __construct( $args = array() )
    {
        global $wpdb;
        parent::__construct(
            array(
                'plural' => 'upload_files',
                'screen' => 'upload_files',
            )
        );
    }

    public function set_parent($parent)
    {
        $this->parent=$parent;
    }

    public function set_list($list,$page_num=1)
    {
        $this->list=$list;
        $this->page_num=$page_num;
    }

    protected function get_table_classes()
    {
        return array( 'widefat striped' );
    }

    public function get_columns()
    {
        $sites_columns = array(
            'cb'          => ' ',
            'post_type'    => __( 'Post Type', 'wpvivid-backuprestore' )
        );

        return $sites_columns;
    }

    public function get_pagenum()
    {
        if($this->page_num=='first')
        {
            $this->page_num=1;
        }
        else if($this->page_num=='last')
        {
            $this->page_num=$this->_pagination_args['total_pages'];
        }
        $pagenum = $this->page_num ? $this->page_num : 0;

        if ( isset( $this->_pagination_args['total_pages'] ) && $pagenum > $this->_pagination_args['total_pages'] )
        {
            $pagenum = $this->_pagination_args['total_pages'];
        }

        return max( 1, $pagenum );
    }

    public function column_cb( $item )
    {
        echo '<input type="checkbox" name="post_type" />';

    }

    public function column_post_type( $item )
    {
        echo esc_attr($item);
    }

    public function has_items()
    {
        return !empty($this->list);
    }

    public function prepare_items()
    {
        $columns = $this->get_columns();
        $hidden = array();
        $sortable = array();
        $this->_column_headers = array($columns, $hidden, $sortable);

        $total_items =sizeof($this->list);

        $this->set_pagination_args(
            array(
                'total_items' => $total_items,
                'per_page'    => 10,
            )
        );
    }

    public function display_rows()
    {
        $this->_display_rows( $this->list );
    }

    private function _display_rows( $list )
    {
        $page=$this->get_pagenum();

        $page_list=$list;
        $temp_page_list=array();

        $count=0;
        while ( $count<$page )
        {
            $temp_page_list = array_splice( $page_list, 0, 10);
            $count++;
        }

        foreach ( $temp_page_list as $key=>$item)
        {
            $this->single_row($item);
        }
    }

    public function single_row($item)
    {
        ?>
        <tr post_type="<?php echo esc_attr($item)?>">
            <?php $this->single_row_columns( $item ); ?>
        </tr>
        <?php
    }

    protected function pagination( $which )
    {
        if ( empty( $this->_pagination_args ) )
        {
            return;
        }

        $total_items     = $this->_pagination_args['total_items'];
        $total_pages     = $this->_pagination_args['total_pages'];
        $infinite_scroll = false;
        if ( isset( $this->_pagination_args['infinite_scroll'] ) )
        {
            $infinite_scroll = $this->_pagination_args['infinite_scroll'];
        }

        if ( 'top' === $which && $total_pages > 1 )
        {
            $this->screen->render_screen_reader_content( 'heading_pagination' );
        }

        $output = '<span class="displaying-num">' . sprintf( _n( '%s item', '%s items', $total_items, 'wpvivid-backuprestore' ), number_format_i18n( $total_items ) ) . '</span>';

        $current              = $this->get_pagenum();

        $page_links = array();

        $total_pages_before = '<span class="paging-input">';
        $total_pages_after  = '</span></span>';

        $disable_first = $disable_last = $disable_prev = $disable_next = false;

        if ( $current == 1 ) {
            $disable_first = true;
            $disable_prev  = true;
        }
        if ( $current == 2 ) {
            $disable_first = true;
        }
        if ( $current == $total_pages ) {
            $disable_last = true;
            $disable_next = true;
        }
        if ( $current == $total_pages - 1 ) {
            $disable_last = true;
        }

        if ( $disable_first ) {
            $page_links[] = '<span class="tablenav-pages-navspan button disabled" aria-hidden="true">&laquo;</span>';
        } else {
            $page_links[] = sprintf(
                "<div class='first-page button'><span class='screen-reader-text'>%s</span><span aria-hidden='true'>%s</span></div>",
                __( 'First page', 'wpvivid-backuprestore' ),
                '&laquo;'
            );
        }

        if ( $disable_prev ) {
            $page_links[] = '<span class="tablenav-pages-navspan button disabled" aria-hidden="true">&lsaquo;</span>';
        } else {
            $page_links[] = sprintf(
                "<div class='prev-page button' value='%s'><span class='screen-reader-text'>%s</span><span aria-hidden='true'>%s</span></div>",
                $current,
                __( 'Previous page', 'wpvivid-backuprestore' ),
                '&lsaquo;'
            );
        }

        if ( 'bottom' === $which ) {
            $html_current_page  = $current;
            $total_pages_before = '<span class="screen-reader-text">' . __( 'Current Page', 'wpvivid-backuprestore' ) . '</span><span id="table-paging" class="paging-input"><span class="tablenav-paging-text">';
        } else {
            $html_current_page = sprintf(
                "%s<input class='current-page'  type='text' name='paged' value='%s' size='%d' aria-describedby='table-paging' /><span class='tablenav-paging-text'>",
                '<label  class="screen-reader-text">' . __( 'Current Page', 'wpvivid-backuprestore' ) . '</label>',
                $current,
                strlen( $total_pages )
            );
        }
        $html_total_pages = sprintf( "<span class='total-pages'>%s</span>", number_format_i18n( $total_pages ) );
        $page_links[]     = $total_pages_before . sprintf( _x( '%1$s of %2$s', 'paging', 'wpvivid-backuprestore' ), $html_current_page, $html_total_pages ) . $total_pages_after;

        if ( $disable_next ) {
            $page_links[] = '<span class="tablenav-pages-navspan button disabled" aria-hidden="true">&rsaquo;</span>';
        } else {
            $page_links[] = sprintf(
                "<div class='next-page button' value='%s'><span class='screen-reader-text'>%s</span><span aria-hidden='true'>%s</span></div>",
                $current,
                __( 'Next page', 'wpvivid-backuprestore' ),
                '&rsaquo;'
            );
        }

        if ( $disable_last ) {
            $page_links[] = '<span class="tablenav-pages-navspan button disabled" aria-hidden="true">&raquo;</span>';
        } else {
            $page_links[] = sprintf(
                "<div class='last-page button'><span class='screen-reader-text'>%s</span><span aria-hidden='true'>%s</span></div>",
                __( 'Last page', 'wpvivid-backuprestore' ),
                '&raquo;'
            );
        }

        $pagination_links_class = 'pagination-links';
        if ( ! empty( $infinite_scroll ) ) {
            $pagination_links_class .= ' hide-if-js';
        }
        $output .= "\n<span class='$pagination_links_class'>" . join( "\n", $page_links ) . '</span>';

        if ( $total_pages ) {
            $page_class = $total_pages < 2 ? ' one-page' : '';
        } else {
            $page_class = ' no-pages';
        }
        $this->_pagination = "<div class='tablenav-pages{$page_class}'>$output</div>";

        echo $this->_pagination;
    }

    protected function display_tablenav( $which ) {
        $css_type = '';
        if ( 'top' === $which ) {
            wp_nonce_field( 'bulk-' . $this->_args['plural'] );
            $css_type = 'margin: 0 0 10px 0';
        }
        else if( 'bottom' === $which ) {
            $css_type = 'margin: 10px 0 0 0';
        }

        $total_pages     = $this->_pagination_args['total_pages'];
        if ( $total_pages >1)
        {
            ?>
            <div class="tablenav <?php echo esc_attr( $which ); ?>" style="<?php echo esc_attr($css_type); ?>">
                <div class="alignleft actions bulkactions">
                    <label for="wpvivid_uc_post_type_bulk_action" class="screen-reader-text">Select bulk action</label>
                    <select name="action" id="wpvivid_uc_post_type_bulk_action">
                        <option value="remove_post_type">Remove</option>
                    </select>
                    <input type="submit" class="button action" value="Apply">
                </div>
                <?php
                $this->extra_tablenav( $which );
                $this->pagination( $which );
                ?>
                <br class="clear" />
            </div>
            <?php
        }
        else
        {
            ?>
            <div class="tablenav <?php echo esc_attr( $which ); ?>" style="<?php echo esc_attr($css_type); ?>">
                <div class="alignleft actions bulkactions">
                    <label for="wpvivid_uc_post_type_bulk_action" class="screen-reader-text">Select bulk action</label>
                    <select name="action" id="wpvivid_uc_post_type_bulk_action">
                        <option value="remove_post_type">Remove</option>
                    </select>
                    <input type="submit" class="button action" value="Apply">
                </div>
                <br class="clear" />
            </div>
            <?php
        }
    }

    public function display()
    {
        $singular = $this->_args['singular'];

        $this->display_tablenav( 'top' );

        $this->screen->render_screen_reader_content( 'heading_list' );
        ?>
        <table class="wp-list-table <?php echo esc_attr(implode( ' ', $this->get_table_classes() )); ?>" >
            <thead>
            <tr>
                <?php $this->print_column_headers(); ?>
            </tr>
            </thead>

            <tbody id="the-list"
                <?php
                if ( $singular ) {
                    echo esc_attr(" data-wp-lists='list:$singular'");
                }
                ?>
            >
            <?php $this->display_rows_or_placeholder(); ?>
            </tbody>

            <tfoot>
            <tr>
                <?php $this->print_column_headers( false ); ?>
            </tr>
            </tfoot>

        </table>
        <?php
    }
}

class WPvivid_Uploads_Cleaner_Setting
{
    public function __construct()
    {
        add_filter('wpvivid_add_setting_tab_page', array($this, 'add_setting_tab_page'), 10);
        add_action('wpvivid_setting_add_uc_cell',array($this, 'add_uc_cell'),13);
        add_filter('wpvivid_set_general_setting', array($this, 'set_general_setting'), 11, 3);

        add_filter('wpvivid_pro_setting_tab', array($this, 'setting_tab'), 13);

        add_action('wp_ajax_wpvivid_get_exclude_files_list',array($this, 'get_exclude_files_list'));
        add_action('wp_ajax_wpvivid_delete_exclude_files',array($this, 'delete_exclude_files'));

        add_action('wp_ajax_wpvivid_get_post_type_list',array($this, 'get_post_type_list'));
        add_action('wp_ajax_wpvivid_delete_post_type',array($this, 'delete_post_type'));
    }

    public function setting_tab($tabs)
    {
        if(current_user_can('manage_options'))
        {
            $tab['title']='Media Cleaner Settings';
            $tab['slug']='upload_cleaner';
            $tab['callback']= array($this, 'output_setting');
            $args['is_parent_tab']=0;
            $args['transparency']=1;
            $tab['args']=$args;
            $tabs[]=$tab;
        }
        return $tabs;
    }

    public function set_general_setting($setting_data, $setting, $options)
    {
        if(isset($setting['wpvivid_uc_scan_limit']))
            $setting_data['wpvivid_uc_scan_limit'] = intval($setting['wpvivid_uc_scan_limit']);

        if(isset($setting['wpvivid_uc_files_limit']))
            $setting_data['wpvivid_uc_files_limit'] = intval($setting['wpvivid_uc_files_limit']);

        if(isset($setting['wpvivid_uc_scan_file_types'])&&is_array($setting['wpvivid_uc_scan_file_types']))
            $setting_data['wpvivid_uc_scan_file_types'] = $setting['wpvivid_uc_scan_file_types'];

        if(isset($setting['wpvivid_uc_post_types'])&&is_array($setting['wpvivid_uc_post_types']))
            $setting_data['wpvivid_uc_post_types'] = $setting['wpvivid_uc_post_types'];

        if(isset($setting['wpvivid_uc_quick_scan']))
            $setting_data['wpvivid_uc_quick_scan'] = boolval($setting['wpvivid_uc_quick_scan']);

        if(isset($setting['wpvivid_uc_delete_media_when_delete_file']))
            $setting_data['wpvivid_uc_delete_media_when_delete_file'] = boolval($setting['wpvivid_uc_delete_media_when_delete_file']);

        if(isset($setting['wpvivid_uc_exclude_files_regex']))
            $setting_data['wpvivid_uc_exclude_files_regex'] = $setting['wpvivid_uc_exclude_files_regex'];

        return $setting_data;
    }

    public function add_setting_tab_page($setting_array)
    {
        $setting_array['uc_setting'] = array('index' => '3', 'tab_func' =>  array($this, 'wpvivid_settingpage_add_tab_uc'), 'page_func' => array($this, 'wpvivid_settingpage_add_page_uc'));
        return $setting_array;
    }

    public function wpvivid_settingpage_add_tab_uc()
    {
        ?>
        <a href="#" id="wpvivid_tab_uc_setting" class="nav-tab setting-nav-tab" onclick="switchsettingTabs(event,'page-uc-setting')"><?php esc_html_e('Media Cleaner Settings', 'wpvivid-backuprestore'); ?></a>
        <?php
    }

    public function wpvivid_settingpage_add_page_uc()
    {
        ?>
        <div class="setting-tab-content wpvivid_tab_uc_setting" id="page-uc-setting" style="margin-top: 10px; display: none;">
            <?php do_action('wpvivid_setting_add_uc_cell'); ?>
        </div>
        <?php
    }

    public function output_setting()
    {
        ?>
        <div style="margin-top: 10px;">
            <?php
            $this->add_uc_cell();
            ?>
            <div><input class="button-primary wpvivid_setting_general_save" type="submit" value="<?php esc_attr_e( 'Save Changes', 'wpvivid-backuprestore' ); ?>" /></div>
        </div>
        <?php
    }

    public function add_uc_cell()
    {
        $scan_limit=get_option('wpvivid_uc_scan_limit',20);
        $files_limit=get_option('wpvivid_uc_files_limit',100);

        $default_file_types=array();
        $default_file_types[]='png';
        $default_file_types[]='jpg';
        $default_file_types[]='jpeg';
        $scan_file_types=get_option('wpvivid_uc_scan_file_types',$default_file_types);

        $quick_scan=get_option('wpvivid_uc_quick_scan',false);

        if($quick_scan)
        {
            $quick_scan='checked';
        }
        else
        {
            $quick_scan='';
        }

        //$default_post_types=array();
        //$default_post_types[]='attachment';
        //$default_post_types[]='revision';
        //$default_post_types[]='auto-draft';
        //$default_post_types[]='nav_menu_item';
        //$default_post_types[]='shop_order';
        //$default_post_types[]='shop_order_refund';
        //$default_post_types[]='oembed_cache';
        //$post_types=get_option('wpvivid_uc_post_types',$default_post_types);

        $delete_media_when_delete_file=get_option('wpvivid_uc_delete_media_when_delete_file',true);

        if($delete_media_when_delete_file)
        {
            $delete_media_when_delete_file='checked';
        }
        else
        {
            $delete_media_when_delete_file='';
        }

        $exclude_path=get_option('wpvivid_uc_exclude_files_regex', '');
        ?>
        <div class="postbox schedule-tab-block setting-page-content">
            <div class="wpvivid-element-space-bottom">
                <label for="wpvivid_uc_scan_file_types">
                    <input style="margin: 4px;" id="wpvivid_uc_quick_scan" type="checkbox" option="setting" name="wpvivid_uc_quick_scan" <?php echo esc_attr($quick_scan); ?> />
                    <span><strong><?php esc_html_e('Enable Quick Scan', 'wpvivid-backuprestore'); ?></strong></span>
                </label>
            </div>
            <div class="wpvivid-element-space-bottom">
                <span><?php esc_html_e('Checking this option will speed up your scans but may produce lower accuracy.', 'wpvivid-backuprestore'); ?></span>
            </div>
            <div class="wpvivid-element-space-bottom">
                <label for="wpvivid_uc_delete_media_when_delete_file">
                    <input style="margin: 4px;" id="wpvivid_uc_delete_media_when_delete_file" style="margin-right: 4px;" type="checkbox" option="setting" name="wpvivid_uc_delete_media_when_delete_file" <?php echo esc_attr($delete_media_when_delete_file); ?> />
                    <span><strong><?php esc_html_e('Delete Image URL', 'wpvivid-backuprestore'); ?></strong></span>
                </label>
            </div>
            <div class="wpvivid-element-space-bottom">
                <span><?php esc_html_e('With this option checked, when the image is deleted, the corresponding image url in the database that is not used anywhere on your website will also be deleted.', 'wpvivid-backuprestore'); ?></span>
            </div>
        </div>

        <div class="postbox schedule-tab-block setting-page-content">
            <div class="wpvivid-element-space-bottom"><strong><?php esc_html_e('Posts Quantity Processed Per Request', 'wpvivid-backuprestore'); ?></strong></div>
            <div class="wpvivid-element-space-bottom">
                <input style="margin: 0px;" type="text" placeholder="20" option="setting" name="wpvivid_uc_scan_limit" id="wpvivid_uc_scan_limit" class="all-options" value="<?php echo esc_attr($scan_limit); ?>" onkeyup="value=value.replace(/\D/g,'')" />
            </div>
            <div class="wpvivid-element-space-bottom">
                <?php esc_html_e( 'Set how many posts to process per request. The value should be set depending on your server performance and the recommended value is 20.', 'wpvivid-backuprestore' ); ?>
            </div>
            <div class="wpvivid-element-space-bottom"><strong><?php esc_html_e('Media Files Quantity Processed Per Request', 'wpvivid-backuprestore'); ?></strong></div>
            <div class="wpvivid-element-space-bottom">
                <input style="margin: 0px;" type="text" placeholder="100" option="setting" name="wpvivid_uc_files_limit" id="wpvivid_uc_files_limit" class="all-options" value="<?php echo esc_attr($files_limit); ?>" onkeyup="value=value.replace(/\D/g,'')" />
            </div>
            <div class="wpvivid-element-space-bottom">
                <?php esc_html_e( 'Set how many media files to process per request. The value should be set depending on your server performance and the recommended value is 100.', 'wpvivid-backuprestore' ); ?>
            </div>

            <div class="wpvivid-element-space-bottom"><strong><?php esc_html_e('Exclude images by folder path', 'wpvivid-backuprestore'); ?></strong></div>
            <div class="wpvivid-element-space-bottom">
                <textarea placeholder="Example:&#10;/wp-content/uploads/19/03/&#10;/wp-content/uploads/19/04/" option="setting" name="wpvivid_uc_exclude_files_regex" style="width:100%; height:200px; overflow-x:auto;"><?php echo esc_html($exclude_path); ?></textarea>
            </div>
        </div>
        <?php
    }

    public function get_exclude_files_list()
    {
        check_ajax_referer( 'wpvivid_ajax', 'nonce' );
        $check=current_user_can('manage_options');
        $check=apply_filters('wpvivid_ajax_check_security',$check);
        if(!$check)
        {
            die();
        }

        try
        {
            $file_exclude=array_map( 'sanitize_text_field', $_POST['file_exclude']);
            if(isset($file_exclude)&&!empty($file_exclude))
            {
                $white_list=get_option('wpvivid_uc_exclude_files_regex',array());
                $white_list[]=$file_exclude;
                update_option('wpvivid_uc_exclude_files_regex',$white_list,'no');
            }

            $white_list=get_option('wpvivid_uc_exclude_files_regex',array());
            $list=new WPvivid_Exclude_Files_List();

            if(isset($_POST['page']))
            {
                $list->set_list($white_list,sanitize_text_field($_POST['page']));
            }
            else
            {
                $list->set_list($white_list);
            }

            $list->prepare_items();
            ob_start();
            $list->display();
            $html = ob_get_clean();

            $ret['result']='success';
            $ret['html']=$html;
            echo wp_json_encode($ret);
        }
        catch (Exception $error)
        {
            $message = 'An exception has occurred. class: '.get_class($error).';msg: '.$error->getMessage().';code: '.$error->getCode().';line: '.$error->getLine().';in_file: '.$error->getFile().';';
            error_log($message);
            echo wp_json_encode(array('result'=>'failed','error'=>$message));
        }
        die();
    }

    public function delete_exclude_files()
    {
        check_ajax_referer( 'wpvivid_ajax', 'nonce' );
        $check=current_user_can('manage_options');
        $check=apply_filters('wpvivid_ajax_check_security',$check);
        if(!$check)
        {
            die();
        }

        try
        {
            $json = sanitize_text_field($_POST['selected']);
            $json = stripslashes($json);
            $json = json_decode($json, true);

            $files=$json['selected'];

            $white_list=get_option('wpvivid_uc_exclude_files_regex',array());
            $white_list = array_diff($white_list, $files);

            update_option('wpvivid_uc_exclude_files_regex',$white_list,'no');

            $white_list=get_option('wpvivid_uc_exclude_files_regex',array());
            $list=new WPvivid_Exclude_Files_List();

            if(isset($_POST['page']))
            {
                $list->set_list($white_list,sanitize_key($_POST['page']));
            }
            else
            {
                $list->set_list($white_list);
            }

            $list->prepare_items();
            ob_start();
            $list->display();
            $html = ob_get_clean();

            $ret['result']='success';
            $ret['html']=$html;
            echo wp_json_encode($ret);
        }
        catch (Exception $error)
        {
            $message = 'An exception has occurred. class: '.get_class($error).';msg: '.$error->getMessage().';code: '.$error->getCode().';line: '.$error->getLine().';in_file: '.$error->getFile().';';
            error_log($message);
            echo wp_json_encode(array('result'=>'failed','error'=>$message));
        }
        die();
    }

    public function get_post_type_list()
    {
        check_ajax_referer( 'wpvivid_ajax', 'nonce' );
        $check=current_user_can('manage_options');
        $check=apply_filters('wpvivid_ajax_check_security',$check);
        if(!$check)
        {
            die();
        }

        try
        {
            $default_post_types=array();
            $default_post_types[]='attachment';
            $default_post_types[]='revision';
            $default_post_types[]='auto-draft';
            $default_post_types[]='nav_menu_item';
            $default_post_types[]='shop_order';
            $default_post_types[]='shop_order_refund';
            $default_post_types[]='oembed_cache';
            $post_type=sanitize_text_field($_POST['post_type']);
            if(isset($post_type)&&!empty($post_type))
            {
                $file_exclude=$post_type;

                $post_types=get_option('wpvivid_uc_post_types',$default_post_types);
                $post_types[]=$file_exclude;
                update_option('wpvivid_uc_post_types',$post_types,'no');
            }

            $post_types=get_option('wpvivid_uc_post_types',array());
            $list=new WPvivid_Post_Type_List();

            if(isset($_POST['page']))
            {
                $list->set_list($post_types,sanitize_key($_POST['page']));
            }
            else
            {
                $list->set_list($post_types);
            }

            $list->prepare_items();
            ob_start();
            $list->display();
            $html = ob_get_clean();

            $ret['result']='success';
            $ret['html']=$html;
            echo wp_json_encode($ret);
        }
        catch (Exception $error)
        {
            $message = 'An exception has occurred. class: '.get_class($error).';msg: '.$error->getMessage().';code: '.$error->getCode().';line: '.$error->getLine().';in_file: '.$error->getFile().';';
            error_log($message);
            echo wp_json_encode(array('result'=>'failed','error'=>$message));
        }
        die();
    }

    public function delete_post_type()
    {
        check_ajax_referer( 'wpvivid_ajax', 'nonce' );
        $check=current_user_can('manage_options');
        $check=apply_filters('wpvivid_ajax_check_security',$check);
        if(!$check)
        {
            die();
        }

        try
        {
            $default_post_types=array();
            $default_post_types[]='attachment';
            $default_post_types[]='revision';
            $default_post_types[]='auto-draft';
            $default_post_types[]='nav_menu_item';
            $default_post_types[]='shop_order';
            $default_post_types[]='shop_order_refund';
            $default_post_types[]='oembed_cache';

            $json = sanitize_text_field($_POST['selected']);
            $json = stripslashes($json);
            $json = json_decode($json, true);

            $files=$json['selected'];

            $post_types=get_option('wpvivid_uc_post_types',$default_post_types);
            $post_types = array_diff($post_types, $files);

            update_option('wpvivid_uc_post_types',$post_types,'no');

            $post_types=get_option('wpvivid_uc_post_types',$default_post_types);
            $list=new WPvivid_Post_Type_List();

            if(isset($_POST['page']))
            {
                $list->set_list($post_types,sanitize_key($_POST['page']));
            }
            else
            {
                $list->set_list($post_types);
            }

            $list->prepare_items();
            ob_start();
            $list->display();
            $html = ob_get_clean();

            $ret['result']='success';
            $ret['html']=$html;
            echo wp_json_encode($ret);
        }
        catch (Exception $error)
        {
            $message = 'An exception has occurred. class: '.get_class($error).';msg: '.$error->getMessage().';code: '.$error->getCode().';line: '.$error->getLine().';in_file: '.$error->getFile().';';
            error_log($message);
            echo wp_json_encode(array('result'=>'failed','error'=>$message));
        }
        die();
    }
}includes/upload-cleaner/class-wpvivid-uploads-scanner.php000064400000234706151327705670017676 0ustar00<?php

if (!defined('WPVIVID_PLUGIN_DIR'))
{
    die;
}

class WPvivid_Uploads_Scanner
{
    public $upload_url;
    public $upload_base_url;

    public $file_found_cache;

    public function __construct()
    {
        $upload_dir=wp_upload_dir();
        $this->upload_url=$upload_dir['baseurl'];
        $this->upload_base_url = substr($upload_dir['baseurl'],1+strlen(get_site_url()));

        $this->file_found_cache=array();
    }

    public function init_scan_task()
    {
        $this->check_table();

        $task['start_time']=time();
        $task['running_time']=time();
        $task['status']='running';
        $task['progress']=0;
        $task['offset']=0;

        update_option('scan_unused_files_task',$task,'no');
    }

    public function check_table_exist()
    {
        global $wpdb;
        require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
        $charset_collate = $wpdb->get_charset_collate();

        $table_name = $wpdb->prefix . "wpvivid_scan_result";
        if($wpdb->get_var("SHOW TABLES LIKE '$table_name'") != $table_name)
        {
            $sql = "CREATE TABLE $table_name (
                id BIGINT(20) NOT NULL AUTO_INCREMENT,
                path text NOT NULL,
                from_post INT NOT NULL,
                PRIMARY KEY (id)
                ) ". $charset_collate . ";";
            //reference to upgrade.php file
            dbDelta( $sql );
        }
    }

    public function check_unused_uploads_files_table_exist()
    {
        global $wpdb;
        require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
        $charset_collate = $wpdb->get_charset_collate();

        $table_name = $wpdb->prefix . "wpvivid_unused_uploads_files";
        if($wpdb->get_var("SHOW TABLES LIKE '$table_name'") != $table_name)
        {
            $sql = "CREATE TABLE $table_name (
                id BIGINT(20) NOT NULL AUTO_INCREMENT,
                path text NOT NULL,
                folder text NOT NULL,
                PRIMARY KEY (id)
                )". $charset_collate . ";";
            //reference to upgrade.php file
            dbDelta( $sql );
        }
    }

    public function check_table()
    {
        global $wpdb;
        require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
        $charset_collate = $wpdb->get_charset_collate();

        $table_name = $wpdb->prefix . "wpvivid_scan_result";
        if($wpdb->get_var("SHOW TABLES LIKE '$table_name'") != $table_name)
        {
            $sql = "CREATE TABLE $table_name (
                id BIGINT(20) NOT NULL AUTO_INCREMENT,
                path text NOT NULL,
                from_post INT NOT NULL,
                PRIMARY KEY (id)
                ) ". $charset_collate . ";";
            //reference to upgrade.php file
            dbDelta( $sql );
        }

        $wpdb->query("TRUNCATE TABLE $table_name");
    }

    public function init_unused_uploads_task($folders)
    {
        $this->check_unused_uploads_files_table();
        update_option('unused_uploads_task',array(),'no');
        $task['start_time']=time();
        $task['running_time']=time();
        $task['status']='running';
        $task['progress']=0;
        $task['size']=0;

        $upload_folder = wp_upload_dir();

        $root_path =$upload_folder['basedir'];

        foreach ($folders as $folder)
        {
            $task['folder'][$folder]['finished']=0;
            $task['folder'][$folder]['offset']=0;
            if($folder=='.')
            {
                $task['folder'][$folder]['total']=0;
            }
            else
            {
                $path=$root_path.DIRECTORY_SEPARATOR.$folder;
                if(file_exists($path))
                {
                    $fi = new FilesystemIterator($path, FilesystemIterator::SKIP_DOTS);
                    $task['folder'][$folder]['total']=iterator_count($fi);
                }
                else {
                    $task['folder'][$folder]['total']=0;
                }
            }
        }

        update_option('unused_uploads_task',$task,'no');
    }

    public function check_unused_uploads_files_table()
    {
        global $wpdb;
        require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
        $charset_collate = $wpdb->get_charset_collate();

        $table_name = $wpdb->prefix . "wpvivid_unused_uploads_files";
        if($wpdb->get_var("SHOW TABLES LIKE '$table_name'") != $table_name)
        {
            $sql = "CREATE TABLE $table_name (
                id BIGINT(20) NOT NULL AUTO_INCREMENT,
                path text NOT NULL,
                folder text NOT NULL,
                PRIMARY KEY (id)
                )". $charset_collate . ";";
            //reference to upgrade.php file
            dbDelta( $sql );
        }

        $wpdb->query("TRUNCATE TABLE $table_name");
    }

    public function scan_sidebars_widgets()
    {
        global $wp_registered_widgets;
        $syswidgets = $wp_registered_widgets;
        $active_widgets = get_option( 'sidebars_widgets' );

        $files=array();

        foreach ( $active_widgets as $sidebar_name => $widgets )
        {
            if ( $sidebar_name != 'wp_inactive_widgets' && !empty( $widgets ) && is_array( $widgets ) )
            {
                foreach ( $widgets as $key => $widget )
                {
                    $files=array_merge($files,$this->get_images_from_widget($syswidgets[$widget]));

                    //do_action( 'wpmc_scan_widget', $syswidgets[$widget] );
                    //$acfwidget = $syswidgets[$widget]['callback'][0]->id;
                    //if ( strlen($acfwidget)>11 && substr($acfwidget,0,11)=='acf_widget_' )
                    //{
                        //$this->get_images_from_acfwidgets ( $acfwidget );
                    //}
                }
            }
        }

        return $files;
    }

    public function scan_divi_options()
    {
        $files=array();
        $options=get_option('et_divi',false);

        if($options!==false)
        {
            if(isset($options['divi_logo']))
            {
                $files[]=$this->get_src($options['divi_logo']);
            }
        }

        $options=get_option('widget_text',false);

        if($options!==false)
        {
            foreach ($options as $option)
            {
                if(isset($option['title']))
                {
                    $this->get_img_from_divi($option['title'],$files);
                }

                if(isset($option['text']))
                {
                    $this->get_img_from_divi($option['text'],$files);
                }
            }
        }


        $options=get_option('theme_mods_Divi',false);

        if($options!==false)
        {
            if(isset($options['background_image']))
            {
                $files[]=$this->get_src($options['background_image']);
            }
        }

        return $files;
    }

    public function get_images_from_widget($widget)
    {
        $widget_class = $widget['callback'][0]->option_name;
        $instance_id = $widget['params'][0]['number'];
        $widget_data = get_option( $widget_class );

        $files=array();

        $ids=array();

        if ( !empty( $widget_data[$instance_id]['text'] ) )
        {
            $html = $widget_data[$instance_id]['text']; // mm change
            $media=$this->get_media_from_html($html);

            if(!empty($media))
            {
                $files=$media;
            }
        }
        if ( !empty( $widget_data[$instance_id]['attachment_id'] ) )
        {
            $id = $widget_data[$instance_id]['attachment_id'];
            array_push( $ids, $id );
        }


        if ( !empty( $widget_data[$instance_id]['url'] ) )
        {
            $url = $widget_data[$instance_id]['url'];
            if ( $this->is_url( $url ) )
            {
                $src=$this->get_src($url);
                array_push( $files, $src );
            }
        }
        if ( !empty( $widget_data[$instance_id]['ids'] ) )
        {
            $newIds = $widget_data[$instance_id]['ids'];
            if(is_array($newIds))
            {
                $ids = array_merge( $ids, $newIds );
            }
            else
            {
                $ids = array_merge( $ids, array($newIds) );
            }
        }
        // Recent Blog Posts
        if ( !empty( $widget_data[$instance_id]['thumbnail'] ) )
        {
            $id = $widget_data[$instance_id]['thumbnail'];
            array_push( $ids, $id );
        }

        foreach ($ids as $id)
        {
            $files=array_merge($files,$this->get_img_from_id($id));
        }

        return $files;
    }

    public function scan_termmeta_thumbnail()
    {
        global $wpdb;
        $query = "SELECT meta_value FROM $wpdb->termmeta WHERE meta_key LIKE '%thumbnail_id%'";
        $metas = $wpdb->get_col( $query );

        $files=array();
        if(count($metas)>0)
        {
            $ids=array();
            foreach ( $metas as $id )
            {
                if ( is_numeric( $id ) && $id > 0 )
                    $ids[]=$id;
            }

            foreach ($ids as $id)
            {
                $files=array_merge($files,$this->get_img_from_id($id));
            }
        }

        $placeholder_id = get_option( 'woocommerce_placeholder_image', null, true );
        if ( !empty( $placeholder_id ) )
            $files=array_merge($files,$this->get_img_from_id($placeholder_id));
        return $files;
    }

    public function array_to_file($exploded)
    {
        $file='';
        foreach ($exploded as $key=>$value)
        {
            $file=$value;
        }
        return $file;
    }

    public function scan_image_from_nextend()
    {
        global $wpdb;

        $file_array=array();
        if($wpdb->get_var( $wpdb->prepare( 'SHOW TABLES LIKE %s', $wpdb->prefix."nextend2_image_storage" ) ) !== null)
        {
            $query = "SELECT image FROM ".$wpdb->prefix."nextend2_image_storage";
            $metas = $wpdb->get_col( $query );
            $upload_dir = wp_upload_dir();
            $upload_path = $upload_dir['basedir'];
            foreach ($metas as $meta)
            {
                $exploded = explode( ',', $meta );
                if ( is_array( $exploded ) )
                {
                    $file_tmp = $this->array_to_file($exploded);
                    $file = str_replace('$upload$', $upload_path, $file_tmp);
                    if(file_exists($file))
                    {
                        $file_array[] = str_replace('$upload$'.'/', '', $file_tmp);
                    }
                    continue;
                }
            }
        }
        return $file_array;
    }

    /*
    public function get_images_from_acfwidgets( $widget)
    {
        global $wpdb;
        $result=array();
        // $widget starts with: acf_widget_ and looks like this: acf_widget_15011-2
        $LikeKey = 'widget_' . $widget . '_%'; // Example: option_name starts with widget_acf_widget_15216-3_
        $q = "SELECT option_name, option_value FROM {$wpdb->options} where option_name like %s;";
        $OptionRows = $wpdb->get_results( $wpdb->prepare( $q, $LikeKey ) , ARRAY_N );
        if ( $wpdb->last_error )
        {
            $result['result']='failed';
            $result['error']=$wpdb->last_error;
            return $result;
        }
        if ( count( $OptionRows ) > 0 )
        {
            $ACFWidget_ids = array();
            $ACFWidget_urls = array();
            foreach( $OptionRows as $row )
            {
                //$row[0] = option_name from wp_options
                //$row[1] = option_value from wp_options
                // Three if statements in priority order (image ids, link fields, text fields)
                // *** An image field containing a post id for the image or is it???
                if ( strpos($row[0], 'image') || strpos($row[0], 'icon') !== false )
                {
                    if ( is_numeric( $row[1] ) ) {
                        array_push( $ACFWidget_ids, $row[1] );
                    }
                }

                // No else here because sometimes image or icon is present in the option_name and link is also present
                // Example: widget_acf_widget_15011-2_link_1_link_icon
                // Example: widget_acf_widget_15216-3_widget_image_link

                // *** A link field may contain a link or be empty
                if ( strpos( $row[0], 'link' ) || strpos( $row[0], 'url' ) !== false )
                {
                    if ( $this->is_url($row[1]) ) {
                        $url = $this->clean_url($row[1]);
                        if (!empty($url)) {
                            array_push($ACFWidget_urls, $url);
                        }
                    }
                }

                // *** A text field may contain HTML
                if (strpos($row[0], 'text') || strpos($row[0], 'html') !== false)
                {
                    if (!empty($row[1])) {
                        $ACFWidget_urls = array_merge($ACFWidget_urls, $this->get_urls_from_html($row[1]));  // mm change
                    }
                }
            }
        }
    }
    */

    public function get_post_count()
    {
        global $wpdb;

        $post_types=apply_filters('wpvivid_scan_post_types', array());

        $post_types="post_type NOT IN ('".implode("','",$post_types)."')";

        $post_status="post_status NOT IN ('inherit', 'trash', 'auto-draft')";

        $query="SELECT COUNT(*) FROM $wpdb->posts WHERE $post_types AND $post_status";
        $result=$wpdb->get_results($query,ARRAY_N);

        if($result && sizeof($result)>0)
        {
            $count = $result[0][0];
        }
        else
        {
            $count=0;
        }

        return $count;
    }

    public function get_posts($start,$limit)
    {
        global $wpdb;

        $post_types=apply_filters('wpvivid_scan_post_types', array());

        $post_types="post_type NOT IN ('".implode("','",$post_types)."')";

        $post_status="post_status NOT IN ('inherit', 'trash', 'auto-draft')";

        $query=$wpdb->prepare("SELECT ID FROM $wpdb->posts WHERE $post_types AND $post_status LIMIT %d, %d",$start,$limit);

        $posts = $wpdb->get_col( $query );

        return $posts;
    }

    public function get_media_from_html($html)
    {
        $html = mb_convert_encoding( $html, 'HTML-ENTITIES', 'UTF-8' );
        $html = do_shortcode( $html );
        $html = wp_filter_content_tags( $html );

        if ( !class_exists("DOMDocument") )
        {
            echo 'The DOM extension for PHP is not installed.';
            return array();
        }

        libxml_use_internal_errors(true);
        $dom = new DOMDocument();
        @$dom->loadHTML( $html );
        libxml_clear_errors();
        $results = array();

        $this->get_img_from_tag_img($dom,$results);

        $this->get_img_from_meta($dom,$results);

        $this->get_img_from_tag_a($dom,$results);

        $this->get_img_from_tag_a($dom,$results,'link');

        $this->get_img_from_bk($html,$results);

        $this->get_img_from_wp_image($html,$results);

        return $results;
    }

    public function get_media_from_post_content($post)
    {
        $html = get_post_field( 'post_content', $post );

        $html = mb_convert_encoding( $html, 'HTML-ENTITIES', 'UTF-8' );
        ob_start();
        $html = do_shortcode( $html );
        ob_clean();
        ob_end_flush();
        $html = wp_filter_content_tags( $html );

        if ( !class_exists("DOMDocument") )
        {
            echo 'The DOM extension for PHP is not installed.';
            return array();
        }

        if(empty($html))
        {
            return array();
        }

        libxml_use_internal_errors(true);
        $dom = new DOMDocument();
        @$dom->loadHTML( $html );
        libxml_clear_errors();
        $results = array();

        $this->get_img_from_tag_img($dom,$results);

        $this->get_img_from_meta($dom,$results);

        $this->get_img_from_tag_a($dom,$results);

        $this->get_img_from_tag_a($dom,$results,'link');

        $this->get_img_from_bk($html,$results);

        $this->get_img_from_wp_image($html,$results);

        $this->get_img_from_divi($html,$results);

        $galleries = get_post_galleries_images( $post );
        foreach ( $galleries as $gallery )
        {
            foreach ( $gallery as $image )
            {
                $src=$this->get_src($image);
                if($src!==false)
                {
                    array_push( $results, $src );
                }
            }
        }

        return $results;
    }

    public function get_img_from_tag_img($dom,&$results)
    {
        $imgs = $dom->getElementsByTagName( 'img' );
        foreach ( $imgs as $img )
        {
            $url = $img->getAttribute('src');
            $src=$this->get_src($url);
            if($src!==false)
            {
                array_push( $results, $src );
            }

            $srcset = $img->getAttribute('srcset');
            if ( !empty( $srcset ) )
            {
                $setImgs = explode( ',', trim( $srcset ) );
                foreach ( $setImgs as $setImg )
                {
                    $urls = explode( ' ', trim( $setImg ) );
                    if ( is_array( $urls ) )
                    {
                        $src=$this->get_src($urls[0]);
                        if($src!==false)
                        {
                            array_push( $results, $src );
                        }
                    }
                }
            }
        }
    }

    public function get_img_from_meta($dom,&$results)
    {
        $metas = $dom->getElementsByTagName( 'meta' );
        foreach ( $metas as $meta )
        {
            $property = $meta->getAttribute( 'property' );
            if ( $property == 'og:image' || $property == 'og:image:secure_url' || $property == 'twitter:image' )
            {
                $url = $meta->getAttribute( 'content' );
                $src=$this->get_src($url);
                if($src!==false)
                {
                    array_push( $results, $src );
                }
            }
        }
    }

    public function get_img_from_tag_a($dom,&$results,$tag='a')
    {
        $urls = $dom->getElementsByTagName($tag);
        foreach ( $urls as $url )
        {
            $url_href = $url->getAttribute('href'); // mm change

            $src=$this->get_src($url_href);
            if($src!==false)
            {
                if ( !empty( $src ) )
                {
                    array_push( $results, $src );
                }
            }
        }
    }

    public function get_img_from_bk($html,&$results)
    {
        preg_match_all( "/url\(\'?\"?((https?:\/\/)?[^\\&\#\[\] \"\?]+\.(jpe?g|gif|png))\'?\"?/", $html, $res );
        if ( !empty( $res ) && isset( $res[1] ) && count( $res[1] ) > 0 )
        {
            foreach ( $res[1] as $url )
            {
                $src=$this->get_src($url);
                if($src!==false)
                {
                    array_push( $results, $src );
                }
            }
        }
    }

    public function get_img_from_wp_image($html,&$results)
    {
        $posts_images_ids=array();
        preg_match_all( "/wp-image-([0-9]+)/", $html, $res );
        if ( !empty( $res ) && isset( $res[1] ) && count( $res[1] ) > 0 )
        {
            $posts_images_ids = array_merge( $posts_images_ids, $res[1] );
        }

        preg_match_all('/\[gallery.*ids=.(.*).\]/', $html, $res );
        if ( !empty( $res ) && isset( $res[1] ) && count( $res[1] ) > 0 )
        {
            foreach ( $res[1] as $id )
            {
                $ids = explode( ',', $id );
                $posts_images_ids = array_merge( $posts_images_ids, $ids );
            }
        }

        if(!empty($posts_images_ids))
        {
            foreach ($posts_images_ids as $id)
            {
                $files=$this->get_attachment_size($id);
                if(!empty($files))
                {
                    $results=array_merge( $results, $files );
                }
            }
        }
    }

    public function get_img_from_divi( $html, &$results )
    {
        $galleries_images_et = array();

        // Single Image
        preg_match_all( "/src=\"((https?:\/\/)?[^\\&\#\[\] \"\?]+\.(jpe?g|gif|png|ico|tif?f|bmp))\"/", $html, $res );
        if ( !empty( $res ) && isset( $res[1] ) && count( $res[1] ) > 0 )
        {
            foreach ( $res[1] as $url )
            {
                $src=$this->get_src($url);
                if($src!==false)
                {
                    array_push( $results, $src );
                }
            }
        }

        preg_match_all( "/image=\"((https?:\/\/)?[^\\&\#\[\] \"\?]+\.(jpe?g|gif|png|ico|tif?f|bmp))\"/", $html, $res );
        if ( !empty( $res ) && isset( $res[1] ) && count( $res[1] ) > 0 )
        {
            foreach ( $res[1] as $url )
            {
                $src=$this->get_src($url);
                if($src!==false)
                {
                    array_push( $results, $src );
                }
            }
        }
        // Background Image
        preg_match_all( "/background_image=\"((https?:\/\/)?[^\\&\#\[\] \"\?]+\.(jpe?g|gif|png|ico|tif?f|bmp))\"/", $html, $res );
        if ( !empty( $res ) && isset( $res[1] ) && count( $res[1] ) > 0 )
        {
            foreach ( $res[1] as $url )
            {
                $src=$this->get_src($url);
                if($src!==false)
                {
                    array_push( $results, $src );
                }
            }
        }

        // Modules with URL (like the Person module)
        preg_match_all( "/url=\"((https?:\/\/)?[^\\&\#\[\] \"\?]+\.(jpe?g|gif|png|ico|tif?f|bmp))\"/", $html, $res );
        if ( !empty( $res ) && isset( $res[1] ) )
        {
            foreach ( $res[1] as $url )
            {
                $src=$this->get_src($url);
                if($src!==false)
                {
                    array_push( $results, $src );
                }
            }
        }

        // Galleries
        preg_match_all( "/gallery_ids=\"([0-9,]+)/", $html, $res );
        if ( !empty( $res ) && isset( $res[1] ) )
        {
            foreach ( $res[1] as $r )
            {
                $ids = explode( ',', $r );
                $galleries_images_et = array_merge( $galleries_images_et, $ids );
            }
        }

        foreach ($galleries_images_et as $id)
        {
            $results=array_merge($results,$this->get_img_from_id($id));
        }

    }

    public function get_attachment_size($attachment_id)
    {
        $files=array();
        global $wpdb;
        $meta_key="(meta_key = '_wp_attached_file')";
        $postmeta = $wpdb->get_results( $wpdb->prepare( "SELECT * FROM $wpdb->postmeta WHERE post_id = %d AND $meta_key", $attachment_id ) );

        foreach ( $postmeta as $meta )
        {
            if($meta->meta_key=='_wp_attached_file')
            {
                $files[]=$meta->meta_value;

                $attach_meta      = wp_get_attachment_metadata( $attachment_id );
                if($attach_meta!=false)
                {
                    if(isset($attach_meta['sizes']))
                    {
                        foreach ($attach_meta['sizes'] as $key=>$value)
                        {
                            $data=image_get_intermediate_size($attachment_id,$key);
                            $files[]=$data['path'];
                        }
                    }
                }
            }
        }

        return $files;
    }

    public function get_media_from_post_meta($post)
    {
        global $wpdb;
        $meta_key="(meta_key = '_thumbnail_id')";
        $query=$wpdb->prepare("SELECT meta_value FROM {$wpdb->postmeta} WHERE post_id = %d AND $meta_key",$post);

        $metas = $wpdb->get_col($query);

        $postmeta_images_ids = array();
        $postmeta_images_urls = array();

        foreach ($metas as $meta)
        {
            if ( is_numeric( $meta ) )
            {
                if ( $meta > 0 )
                    array_push( $postmeta_images_ids, $meta );
                continue;
            }
            else if ( is_serialized( $meta ) )
            {
                $decoded = @unserialize( $meta, array('allowed_classes' => false) );
                if ( is_array( $decoded ) )
                {
                    $this->array_to_ids_or_urls( $decoded, $postmeta_images_ids, $postmeta_images_urls );
                    continue;
                }
            }
            else {
                $exploded = explode( ',', $meta );
                if ( is_array( $exploded ) )
                {
                    $this->array_to_ids_or_urls( $exploded, $postmeta_images_ids, $postmeta_images_urls );
                    continue;
                }
            }
        }

        //
        $meta_key="(meta_key = '_product_image_gallery')";
        $query=$wpdb->prepare("SELECT meta_value FROM {$wpdb->postmeta} WHERE post_id = %d AND $meta_key",$post);
        $metas = $wpdb->get_col($query);
        foreach ($metas as $meta)
        {
            if ( is_numeric( $meta ) )
            {
                if ( $meta > 0 )
                    array_push( $postmeta_images_ids, $meta );
                continue;
            }
            else if ( is_serialized( $meta ) )
            {
                $decoded = @unserialize( $meta, array('allowed_classes' => false) );
                if ( is_array( $decoded ) )
                {
                    $this->array_to_ids_or_urls( $decoded, $postmeta_images_ids, $postmeta_images_urls );
                    continue;
                }
            }
            else {
                $exploded = explode( ',', $meta );
                if ( is_array( $exploded ) )
                {
                    $this->array_to_ids_or_urls( $exploded, $postmeta_images_ids, $postmeta_images_urls );
                    continue;
                }
            }
        }
        //

        //
        $meta_key="(meta_key = 'image')";
        $query=$wpdb->prepare("SELECT meta_value FROM {$wpdb->postmeta} WHERE post_id = %d AND $meta_key",$post);
        $metas = $wpdb->get_col($query);
        foreach ($metas as $meta)
        {
            if ( is_numeric( $meta ) )
            {
                if ( $meta > 0 )
                    array_push( $postmeta_images_ids, $meta );
                continue;
            }
            else if ( is_serialized( $meta ) )
            {
                $decoded = @unserialize( $meta, array('allowed_classes' => false) );
                if ( is_array( $decoded ) )
                {
                    $this->array_to_ids_or_urls( $decoded, $postmeta_images_ids, $postmeta_images_urls );
                    continue;
                }
            }
            else {
                $exploded = explode( ',', $meta );
                if ( is_array( $exploded ) )
                {
                    $this->array_to_ids_or_urls( $exploded, $postmeta_images_ids, $postmeta_images_urls );
                    continue;
                }
            }
        }

        $meta_key="(meta_key = 'imagem')";
        $query=$wpdb->prepare("SELECT meta_value FROM {$wpdb->postmeta} WHERE post_id = %d AND $meta_key",$post);
        $metas = $wpdb->get_col($query);
        foreach ($metas as $meta)
        {
            if ( is_numeric( $meta ) )
            {
                if ( $meta > 0 )
                    array_push( $postmeta_images_ids, $meta );
                continue;
            }
            else if ( is_serialized( $meta ) )
            {
                $decoded = @unserialize( $meta, array('allowed_classes' => false) );
                if ( is_array( $decoded ) )
                {
                    $this->array_to_ids_or_urls( $decoded, $postmeta_images_ids, $postmeta_images_urls );
                    continue;
                }
            }
            else {
                $exploded = explode( ',', $meta );
                if ( is_array( $exploded ) )
                {
                    $this->array_to_ids_or_urls( $exploded, $postmeta_images_ids, $postmeta_images_urls );
                    continue;
                }
            }
        }

        $meta_key="(meta_key = 'bild1')";
        $query=$wpdb->prepare("SELECT meta_value FROM {$wpdb->postmeta} WHERE post_id = %d AND $meta_key",$post);
        $metas = $wpdb->get_col($query);
        foreach ($metas as $meta)
        {
            if ( is_numeric( $meta ) )
            {
                if ( $meta > 0 )
                    array_push( $postmeta_images_ids, $meta );
                continue;
            }
            else if ( is_serialized( $meta ) )
            {
                $decoded = @unserialize( $meta, array('allowed_classes' => false) );
                if ( is_array( $decoded ) )
                {
                    $this->array_to_ids_or_urls( $decoded, $postmeta_images_ids, $postmeta_images_urls );
                    continue;
                }
            }
            else {
                $exploded = explode( ',', $meta );
                if ( is_array( $exploded ) )
                {
                    $this->array_to_ids_or_urls( $exploded, $postmeta_images_ids, $postmeta_images_urls );
                    continue;
                }
            }
        }

        $meta_key="(meta_key = 'bild2')";
        $query=$wpdb->prepare("SELECT meta_value FROM {$wpdb->postmeta} WHERE post_id = %d AND $meta_key",$post);
        $metas = $wpdb->get_col($query);
        foreach ($metas as $meta)
        {
            if ( is_numeric( $meta ) )
            {
                if ( $meta > 0 )
                    array_push( $postmeta_images_ids, $meta );
                continue;
            }
            else if ( is_serialized( $meta ) )
            {
                $decoded = @unserialize( $meta, array('allowed_classes' => false) );
                if ( is_array( $decoded ) )
                {
                    $this->array_to_ids_or_urls( $decoded, $postmeta_images_ids, $postmeta_images_urls );
                    continue;
                }
            }
            else {
                $exploded = explode( ',', $meta );
                if ( is_array( $exploded ) )
                {
                    $this->array_to_ids_or_urls( $exploded, $postmeta_images_ids, $postmeta_images_urls );
                    continue;
                }
            }
        }

        $meta_key="(meta_key = 'bild3')";
        $query=$wpdb->prepare("SELECT meta_value FROM {$wpdb->postmeta} WHERE post_id = %d AND $meta_key",$post);
        $metas = $wpdb->get_col($query);
        foreach ($metas as $meta)
        {
            if ( is_numeric( $meta ) )
            {
                if ( $meta > 0 )
                    array_push( $postmeta_images_ids, $meta );
                continue;
            }
            else if ( is_serialized( $meta ) )
            {
                $decoded = @unserialize( $meta, array('allowed_classes' => false) );
                if ( is_array( $decoded ) )
                {
                    $this->array_to_ids_or_urls( $decoded, $postmeta_images_ids, $postmeta_images_urls );
                    continue;
                }
            }
            else {
                $exploded = explode( ',', $meta );
                if ( is_array( $exploded ) )
                {
                    $this->array_to_ids_or_urls( $exploded, $postmeta_images_ids, $postmeta_images_urls );
                    continue;
                }
            }
        }

        $meta_key="(meta_key = 'bild4')";
        $query=$wpdb->prepare("SELECT meta_value FROM {$wpdb->postmeta} WHERE post_id = %d AND $meta_key",$post);
        $metas = $wpdb->get_col($query);
        foreach ($metas as $meta)
        {
            if ( is_numeric( $meta ) )
            {
                if ( $meta > 0 )
                    array_push( $postmeta_images_ids, $meta );
                continue;
            }
            else if ( is_serialized( $meta ) )
            {
                $decoded = @unserialize( $meta,array('allowed_classes' => false) );
                if ( is_array( $decoded ) )
                {
                    $this->array_to_ids_or_urls( $decoded, $postmeta_images_ids, $postmeta_images_urls );
                    continue;
                }
            }
            else {
                $exploded = explode( ',', $meta );
                if ( is_array( $exploded ) )
                {
                    $this->array_to_ids_or_urls( $exploded, $postmeta_images_ids, $postmeta_images_urls );
                    continue;
                }
            }
        }
        //

        $files=array();

        foreach ($postmeta_images_ids as $id)
        {
            $files=array_merge($files,$this->get_img_from_id($id));
        }

        return $files;
    }

    public function get_media_from_post_meta_elementor( $post )
    {
        $postmeta_images_ids = array();
        $files=array();

        $_elementor_meta = get_post_meta( $post, '_elementor_data',true);
        if($_elementor_meta!=false)
        {
            if ( is_string( $_elementor_meta ) && ! empty( $_elementor_meta ) )
            {
                $_elementor_meta = json_decode( $_elementor_meta, true );
            }
            if ( empty( $_elementor_meta ) )
            {
                $_elementor_meta = array();
            }
            $elements_data=$_elementor_meta;
            foreach ( $elements_data as $element_data )
            {
                $element_image=$this->get_element_image($element_data,$postmeta_images_ids);
                $postmeta_images_ids=array_merge($postmeta_images_ids,$element_image);
            }


            foreach ($postmeta_images_ids as $id)
            {
                $files=array_merge($files,$this->get_img_from_id($id));
            }
        }
        return $files;
    }

    public function get_media_from_post_custom_meta( $post )
    {
        $custom_fields=get_post_custom($post);
        $files=array();


        if($custom_fields!=false)
        {
            if(isset($custom_fields['essb_cached_image']))
            {
                if ( is_string( $custom_fields['essb_cached_image'] ) && ! empty( $custom_fields['essb_cached_image'] ) )
                {
                    $files[]=$this->get_src($custom_fields['essb_cached_image']);
                }
                else if(is_array( $custom_fields['essb_cached_image'] )&& ! empty( $custom_fields['essb_cached_image'] ))
                {
                    foreach ($custom_fields['essb_cached_image'] as $essb_cached_image)
                    {
                        $files[]=$this->get_src($essb_cached_image);
                    }
                }

            }

            if(isset($custom_fields['picture']))
            {
                if ( is_string( $custom_fields['picture'] ) && ! empty( $custom_fields['picture'] ) )
                {
                    $id=$custom_fields['picture'];
                    $files=array_merge($files,$this->get_img_from_id($id));
                }
                else if(is_array( $custom_fields['picture'] )&& ! empty( $custom_fields['picture'] ))
                {
                    foreach ($custom_fields['picture'] as $id)
                    {
                        $files=array_merge($files,$this->get_img_from_id($id));
                    }
                }
            }
        }

        return $files;
    }

    public function get_media_from_wpresidence( $post )
    {
        $files=array();

        $image_to_attach = get_post_meta( $post, 'image_to_attach',true);
        $image_to_attach_array = explode(",", $image_to_attach);
        if(!empty($image_to_attach_array))
        {
            foreach ($image_to_attach_array as $image_post_id)
            {
                if(!empty($image_post_id))
                {
                    $_wp_attachment_metadata = get_post_meta( $image_post_id, '_wp_attachment_metadata',true);
                    if(isset($_wp_attachment_metadata['file']))
                    {
                        $files[] = $_wp_attachment_metadata['file'];
                        $iPos = strripos($_wp_attachment_metadata['file'], '/');
                        $relative_path = substr($_wp_attachment_metadata['file'], 0, $iPos+1);
                        if(isset($_wp_attachment_metadata['original_image']))
                        {
                            $files[] = $relative_path.$_wp_attachment_metadata['original_image'];
                        }
                        if(isset($_wp_attachment_metadata['sizes']))
                        {
                            foreach ($_wp_attachment_metadata['sizes'] as $type)
                            {
                                if(isset($type['file']))
                                {
                                    $files[] = $relative_path.$type['file'];
                                }
                            }
                        }
                    }
                }
            }
        }
        return $files;
    }

    public function get_media_from_breakdance( $post )
    {
        $files=array();
        $array_key='tree_json_string';
        $image_to_attach=get_post_meta( $post, 'breakdance_data',true);
        if (is_string($image_to_attach))
        {
            $decoded_value = json_decode($image_to_attach, true);
            if ($decoded_value !== null)
            {
                if (is_array($decoded_value) && array_key_exists($array_key, $decoded_value))
                {
                    if(isset($decoded_value[$array_key]))
                    {
                        $tree = json_decode($decoded_value[$array_key], true);
                        if(isset($tree['root']['children']))
                        {
                            foreach ($tree['root']['children'] as $info)
                            {
                                if(isset($info['children']))
                                {
                                    foreach ($info['children'] as $info1)
                                    {
                                        if(isset($info1['children']))
                                        {
                                            foreach ($info1['children'] as $info2)
                                            {
                                                if(isset($info2['children']))
                                                {
                                                    foreach ($info2['children'] as $info3)
                                                    {
                                                        if(isset($info3['data']['properties']['content']['content']['image']['id']))
                                                        {
                                                            $image_post_id=$info3['data']['properties']['content']['content']['image']['id'];
                                                            $_wp_attachment_metadata = get_post_meta( $image_post_id, '_wp_attachment_metadata',true);
                                                            if(isset($_wp_attachment_metadata['file']))
                                                            {
                                                                $files[] = $_wp_attachment_metadata['file'];
                                                                $iPos = strripos($_wp_attachment_metadata['file'], '/');
                                                                $relative_path = substr($_wp_attachment_metadata['file'], 0, $iPos+1);
                                                                if(isset($_wp_attachment_metadata['original_image']))
                                                                {
                                                                    $files[] = $relative_path.$_wp_attachment_metadata['original_image'];
                                                                }
                                                                if(isset($_wp_attachment_metadata['sizes']))
                                                                {
                                                                    foreach ($_wp_attachment_metadata['sizes'] as $type)
                                                                    {
                                                                        if(isset($type['file']))
                                                                        {
                                                                            $files[] = $relative_path.$type['file'];
                                                                        }
                                                                    }
                                                                }
                                                            }
                                                        }
                                                    }
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
        return $files;
    }

    public function get_media_from_oxygen( $post )
    {
        $files=array();
        $image_post_id_array=array();
        $image_to_attach=get_post_meta( $post, '_ct_builder_json',true);
        if (is_string($image_to_attach))
        {
            $metadata_array = json_decode($image_to_attach, true);
            if(isset( $metadata_array['children'] ) && is_array( $metadata_array['children'] ) && count( $metadata_array['children'] ) > 0 )
            {
                foreach($metadata_array['children'] as $index=>$value)
                {
                    if(isset($value['children']))
                    {
                        foreach ($value['children'] as $index1 => $value1)
                        {
                            if(isset($value1['options']['original']['image_ids']))
                            {
                                $tmp_array = explode(',', $value1['options']['original']['image_ids']);
                                foreach ($tmp_array as $image_post_id)
                                {
                                    $image_post_id_array[]=$image_post_id;
                                }
                            }
                            if(isset($value1['options']['original']['src']))
                            {
                                $src=$this->get_src($value1['options']['original']['src']);
                                array_push( $files, $src );
                            }
                            if(isset($value1['options']['original']['background-image']))
                            {
                                $src=$this->get_src($value1['options']['original']['background-image']);
                                array_push( $files, $src );
                            }
                            if(isset($value1['children']))
                            {
                                foreach ($value1['children'] as $index2 => $value2)
                                {
                                    if(isset($value2['options']['original']['image_ids']))
                                    {
                                        $tmp_array = explode(',', $value2['options']['original']['image_ids']);
                                        foreach ($tmp_array as $image_post_id)
                                        {
                                            $image_post_id_array[]=$image_post_id;
                                        }
                                    }
                                    if(isset($value2['options']['original']['src']))
                                    {
                                        $src=$this->get_src($value2['options']['original']['src']);
                                        array_push( $files, $src );
                                    }
                                    if(isset($value2['options']['original']['background-image']))
                                    {
                                        $src=$this->get_src($value2['options']['original']['background-image']);
                                        array_push( $files, $src );
                                    }
                                    if(isset($value2['children']))
                                    {
                                        foreach ($value2['children'] as $index3 => $value3)
                                        {
                                            if(isset($value3['options']['original']['image_ids']))
                                            {
                                                $tmp_array = explode(',', $value3['options']['original']['image_ids']);
                                                foreach ($tmp_array as $image_post_id)
                                                {
                                                    $image_post_id_array[]=$image_post_id;
                                                }
                                            }
                                            if(isset($value3['options']['original']['src']))
                                            {
                                                $src=$this->get_src($value3['options']['original']['src']);
                                                array_push( $files, $src );
                                            }
                                            if(isset($value3['options']['original']['background-image']))
                                            {
                                                $src=$this->get_src($value3['options']['original']['background-image']);
                                                array_push( $files, $src );
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }

        if(!empty($image_post_id_array))
        {
            foreach ($image_post_id_array as $image_post_id)
            {
                $_wp_attachment_metadata = get_post_meta( $image_post_id, '_wp_attachment_metadata',true);
                if(isset($_wp_attachment_metadata['file']))
                {
                    $files[] = $_wp_attachment_metadata['file'];
                    $iPos = strripos($_wp_attachment_metadata['file'], '/');
                    $relative_path = substr($_wp_attachment_metadata['file'], 0, $iPos+1);
                    if(isset($_wp_attachment_metadata['original_image']))
                    {
                        $files[] = $relative_path.$_wp_attachment_metadata['original_image'];
                    }
                    if(isset($_wp_attachment_metadata['sizes']))
                    {
                        foreach ($_wp_attachment_metadata['sizes'] as $type)
                        {
                            if(isset($type['file']))
                            {
                                $files[] = $relative_path.$type['file'];
                            }
                        }
                    }
                }
            }
        }
        return $files;
    }

    public function get_element_image($element_data,&$attachment_added_ids)
    {
        $element_image=array();

        if(!empty($element_data['settings']))
        {
            $settings=$element_data['settings'];
            if(isset($settings['image']))
            {
                if(isset($settings['image']['id']))
                {
                    if(!in_array($settings['image']['id'],$attachment_added_ids))
                    {
                        $element_image[]=$settings['image']['id'];
                        $attachment_added_ids[]=$settings['image']['id'];
                    }
                }
            }

            if(isset($settings['logo_items']))
            {
                foreach ($settings['logo_items'] as $item)
                {
                    if(isset($item['logo_image']))
                    {
                        if(!in_array($item['logo_image']['id'],$attachment_added_ids))
                        {
                            $element_image[]=$item['logo_image']['id'];
                            $attachment_added_ids[]=$item['logo_image']['id'];
                        }
                    }
                }
            }

            if(isset($settings['gallery']))
            {
                foreach ($settings['gallery'] as $item)
                {
                    if(isset($item['id']))
                    {
                        if(!in_array($item['id'],$attachment_added_ids))
                        {
                            $element_image[]=$item['id'];
                            $attachment_added_ids[]=$item['id'];
                        }
                    }
                }
            }

            if(isset($settings['background_image']))
            {
                if(isset($settings['background_image']['id']))
                {
                    if(!in_array($settings['background_image']['id'],$attachment_added_ids))
                    {
                        $element_image[]=$settings['background_image']['id'];
                        $attachment_added_ids[]=$settings['background_image']['id'];
                    }
                }
            }

            if(isset($settings['background_a_image']))
            {
                if(isset($settings['background_a_image']['id']))
                {
                    if(!in_array($settings['background_a_image']['id'],$attachment_added_ids))
                    {
                        $element_image[]=$settings['background_a_image']['id'];
                        $attachment_added_ids[]=$settings['background_a_image']['id'];
                    }
                }
            }

            if(isset($settings['carousel']) && !empty($settings['carousel']))
            {
                foreach ($settings['carousel'] as $item)
                {
                    if(isset($item['id']))
                    {
                        if(!in_array($item['id'],$attachment_added_ids))
                        {
                            $element_image[]=$item['id'];
                            $attachment_added_ids[]=$item['id'];
                        }
                    }
                }
            }

            if(isset($settings['slides']) && !empty($settings['slides']))
            {
                foreach ($settings['slides'] as $item)
                {
                    if(isset($item['image']['id']))
                    {
                        if(!in_array($item['image']['id'],$attachment_added_ids))
                        {
                            $element_image[]=$item['image']['id'];
                            $attachment_added_ids[]=$item['image']['id'];
                        }
                    }
                }
            }

            if(isset($settings['poster']) && !empty($settings['poster']))
            {
                if(isset($settings['poster']['id']))
                {
                    if(!in_array($settings['poster']['id'],$attachment_added_ids))
                    {
                        $element_image[]=$settings['poster']['id'];
                        $attachment_added_ids[]=$settings['poster']['id'];
                    }
                }
            }

            if(isset($settings['image_overlay']) && !empty($settings['image_overlay']))
            {
                if(isset($settings['image_overlay']['id']))
                {
                    if(!in_array($settings['image_overlay']['id'],$attachment_added_ids))
                    {
                        $element_image[]=$settings['image_overlay']['id'];
                        $attachment_added_ids[]=$settings['image_overlay']['id'];
                    }
                }
            }

            if(isset($settings['background_video_fallback']))
            {
                if(isset($settings['background_video_fallback']['id']))
                {
                    if(!in_array($settings['background_video_fallback']['id'],$attachment_added_ids))
                    {
                        $element_image[]=$settings['background_video_fallback']['id'];
                        $attachment_added_ids[]=$settings['background_video_fallback']['id'];
                    }
                }
            }

            if(isset($settings['background_slideshow_gallery']) && !empty($settings['background_slideshow_gallery']))
            {
                foreach ($settings['background_slideshow_gallery'] as $item)
                {
                    if(isset($item['id']))
                    {
                        if(!in_array($item['id'],$attachment_added_ids))
                        {
                            $element_image[]=$item['id'];
                            $attachment_added_ids[]=$item['id'];
                        }
                    }
                }
            }

            if(isset($settings['column_bg_image_new']) && !empty($settings['column_bg_image_new']))
            {
                if(isset($settings['column_bg_image_new']['id']))
                {
                    if(!in_array($settings['column_bg_image_new']['id'],$attachment_added_ids))
                    {
                        $element_image[]=$settings['column_bg_image_new']['id'];
                        $attachment_added_ids[]=$settings['column_bg_image_new']['id'];
                    }
                }
            }
        }

        if(!empty($element_data['elements']))
        {
            foreach ($element_data['elements'] as $element)
            {
                $temp=$this->get_element_image($element,$attachment_added_ids);
                $element_image=array_merge($element_image,$temp);
            }
        }

        return $element_image;
    }

    public function get_from_meta( $meta, $lookFor, &$ids, &$urls )
    {
        foreach ( $meta as $key => $value ) {
            if ( is_object( $value ) || is_array( $value ) )
                $this->get_from_meta( $value, $lookFor, $ids, $urls );
            else if ( in_array( $key, $lookFor ) ) {
                if ( empty( $value ) )
                    continue;
                else if ( is_numeric( $value ) ) {
                    // It this an ID?
                    array_push( $ids, $value );
                }
                else {
                    if ( $this->is_url( $value ) ) {
                        // Is this an URL?
                        array_push( $urls, $this->clean_url( $value ) );
                    }
                    else {
                        // Is this an array of IDs, encoded as a string? (like "20,13")
                        $pieces = explode( ',', $value );
                        foreach ( $pieces as $pval ) {
                            if ( is_numeric( $pval ) ) {
                                array_push( $ids, $pval );
                            }
                        }
                    }
                }
            }
        }
    }

    public function get_img_from_id($attachment_id)
    {
        $files=array();
        $attach_meta      = wp_get_attachment_metadata( $attachment_id );
        if($attach_meta!=false)
        {
            if(isset($attach_meta['sizes']))
            {
                foreach ($attach_meta['sizes'] as $key=>$value)
                {
                    $data=image_get_intermediate_size($attachment_id,$key);
                    $data['path']=ltrim($data['path'], './');
                    $name=$data['path'];
                    if(!in_array($name,$files))
                    {
                        $files[]=$name;
                    }
                }
            }

            if(isset($attach_meta['file'])&&is_string($attach_meta['file']))
            {
                if(!in_array($attach_meta['file'],$files))
                {
                    $files[]=$attach_meta['file'];
                }
            }
        }
        return $files;
    }

    public function get_src($url)
    {
        if(empty($url)||!is_string( $url ))
        {
            return false;
        }

        if(strlen($url)>4&&strtolower( substr( $url, 0, 4) ) == 'http')
        {
            $tmp_url = str_replace('https://', '', $url);
            $tmp_url = str_replace('http://', '', $tmp_url);

            $tmp_upload_url = str_replace('https://', '', $this->upload_url);
            $tmp_upload_url = str_replace('http://', '', $tmp_upload_url);

            $ipos = strpos( $tmp_url, $tmp_upload_url );
            if ($ipos === false)
            {
                return false;
            }

            $str=substr( $tmp_url, 1 + strlen( $tmp_upload_url ) + $ipos );

            return $str;
        }
        else if($url[0] == '/')
        {
            $ipos = strpos( $url, $this->upload_base_url );
            if ($ipos === false)
                return false;
            return substr( $url, 1 + strlen( $this->upload_base_url ) + $ipos );
        }
        else
        {
            return false;
        }
    }

    function is_url( $url ) {
        return ( (
            !empty( $url ) ) &&
            is_string( $url ) &&
            strlen( $url ) > 4 && (
                strtolower( substr( $url, 0, 4) ) == 'http' || $url[0] == '/'
            )
        );
    }

    function array_to_ids_or_urls( &$meta, &$ids, &$urls )
    {
        $regex_file = '/[A-Za-z0-9-_,.\(\)\s]+[.]{1}(jpg|jpeg|jpe|gif|png|tiff|bmp|csv|pdf|xls|xlsx|doc|docx|odt|wpd|rtf|tiff|mp3|mp4|wav|lua)/';
        foreach ( $meta as $k => $m )
        {
            if ( is_numeric( $m ) ) {
                // Probably a Media ID
                if ( $m > 0 )
                    array_push( $ids, $m );
            }
            else if ( is_array( $m ) )
            {
                // If it's an array with a width, probably that the index is the Media ID
                if ( isset( $m['width'] ) && is_numeric( $k ) ) {
                    if ( $k > 0 )
                        array_push( $ids, $k );
                }
            }
            else if ( !empty( $m ) )
            {
                // If it's a string, maybe it's a file (with an extension)
                if ( preg_match( $regex_file, $m ) )
                    array_push( $urls, $m );
            }
        }
    }

    private function transfer_path($path)
    {
        $path = str_replace('\\','/',$path);
        $values = explode('/',$path);
        return implode(DIRECTORY_SEPARATOR,$values);
    }

    public function get_folders()
    {
        $upload_folder = wp_upload_dir();

        $root_path =$upload_folder['basedir'];

        $regex=apply_filters('wpvivid_uc_scan_include_files_regex',array());

        $exclude_regex=apply_filters('wpvivid_uc_scan_exclude_files_regex',array());

        $result=$this->get_folder_list($root_path,$regex,$exclude_regex);

        return $result;
    }

    public function get_files($folder)
    {
        $upload_folder = wp_upload_dir();

        $root_path =$upload_folder['basedir'];

        $files =array();

        $regex=apply_filters('wpvivid_uc_scan_include_files_regex',array());

        $exclude_regex=apply_filters('wpvivid_uc_scan_exclude_files_regex',array());

        if($folder === '.')
        {
            $this->scan_root_uploaded_files($files, $root_path.DIRECTORY_SEPARATOR.$folder,$root_path,$regex,$exclude_regex);
        }
        else
        {
            $this->scan_list_uploaded_files($files, $root_path.DIRECTORY_SEPARATOR.$folder,$root_path,$regex,$exclude_regex);
        }

        return $files;
    }

    private function regex_match($regex_array,$string,$mode)
    {
        if(empty($regex_array))
        {
            return true;
        }

        if($mode==0)
        {
            foreach ($regex_array as $regex)
            {
                if(preg_match($regex,$string))
                {
                    return false;
                }
            }

            return true;
        }

        if($mode==1)
        {
            foreach ($regex_array as $regex)
            {
                if(preg_match($regex,$string))
                {
                    return true;
                }
            }

            return false;
        }

        return true;
    }

    private function get_folder_list($root_path,$regex=array(),$exclude_regex=array())
    {
        $result['folders']=array();
        $result['files']=array();
        $result['size']=0;
        $handler = opendir($root_path);
        if($handler!==false)
        {
            while (($filename = readdir($handler)) !== false)
            {
                if ($filename != "." && $filename != "..")
                {
                    if(empty($exclude_regex) || (!empty($exclude_regex) && $this->regex_match($exclude_regex,$this -> transfer_path($root_path . DIRECTORY_SEPARATOR . $filename),0)))
                    {
                        if (is_dir($root_path . DIRECTORY_SEPARATOR . $filename))
                        {
                            if(preg_match('#^\d{4}$#',$filename) ||
                                preg_match('/listing-uploads/', $filename))  //add listing-uploads\gallery
                            {
                                $result['folders']=array_merge( $result['folders'],$this->get_sub_folder($root_path . DIRECTORY_SEPARATOR . $filename,$filename,$exclude_regex));
                            }
                            else
                            {
                                $result['folders'][]=$filename;
                            }

                        }
                        else
                        {
                            if ($this->regex_match($exclude_regex, $filename, 0))
                            {
                                if($this->regex_match($regex, $filename, 1))
                                {
                                    $result['files'][] = $filename;
                                    $result['size']+=filesize($root_path . DIRECTORY_SEPARATOR . $filename);
                                }
                            }
                        }
                    }
                }
            }
            if($handler)
                @closedir($handler);
        }

        $result['folders'][]='.';
        return $result;
    }

    function get_sub_folder($path,$root,$exclude_regex=array())
    {
        $folders=array();
        $handler = opendir($path);
        if($handler!==false)
        {
            while (($filename = readdir($handler)) !== false)
            {
                if ($filename != "." && $filename != "..")
                {
                    if(empty($exclude_regex) || (!empty($exclude_regex) && $this->regex_match($exclude_regex,$this -> transfer_path($path . DIRECTORY_SEPARATOR . $filename),0)))
                    {
                        if (is_dir($path . DIRECTORY_SEPARATOR . $filename))
                        {
                            $folders[]=$root.DIRECTORY_SEPARATOR.$filename;
                        }
                    }
                }
            }
            if($handler)
                @closedir($handler);
        }
        return $folders;
    }

    function scan_root_uploaded_files( &$files,$path,$root,$regex=array(),$exclude_regex=array())
    {
        $count = 0;
        if(is_dir($path))
        {
            $handler = opendir($path);
            if($handler!==false)
            {
                while (($filename = readdir($handler)) !== false)
                {
                    if ($filename != "." && $filename != "..")
                    {
                        $count++;
                        if ($this->regex_match($exclude_regex, $this -> transfer_path($root . DIRECTORY_SEPARATOR . $filename), 0))
                        {
                            if($this->regex_match($regex, $filename, 1))
                            {
                                $result['files'][] = $filename;
                                $files[] = str_replace($path . DIRECTORY_SEPARATOR,'',$path . DIRECTORY_SEPARATOR . $filename);
                            }
                        }
                    }
                }
                if($handler)
                    @closedir($handler);
            }
        }
        return $files;
    }

    function scan_list_uploaded_files( &$files,$path,$root,$regex=array(),$exclude_regex=array())
    {
        $count = 0;
        if(is_dir($path))
        {
            $handler = opendir($path);
            if($handler!==false)
            {
                while (($filename = readdir($handler)) !== false)
                {
                    if ($filename != "." && $filename != "..")
                    {
                        $count++;

                        if (is_dir($path . DIRECTORY_SEPARATOR . $filename))
                        {
                            $this->scan_list_uploaded_files($files, $path . DIRECTORY_SEPARATOR . $filename,$root,$regex);
                        }
                        else
                        {
                            if ($this->regex_match($exclude_regex, $this -> transfer_path($path . DIRECTORY_SEPARATOR . $filename), 0))
                            {
                                if($this->regex_match($regex, $filename, 1))
                                {
                                    $result['files'][] = $filename;
                                    $files[] = str_replace($root . DIRECTORY_SEPARATOR,'',$path . DIRECTORY_SEPARATOR . $filename);
                                }
                            }
                        }
                    }
                }
                if($handler)
                    @closedir($handler);
            }
        }

        return $files;
    }

    public function update_scan_task($uploads_files,$offset,$status='running',$progress=0)
    {
        $task=get_option('scan_unused_files_task',array());

        $task['running_time']=time();
        $task['status']=$status;
        $task['progress']=$progress;
        $task['offset']=$offset;

        $this->insert_scan_result($uploads_files);
        update_option('scan_unused_files_task',$task,'no');
    }

    public function update_unused_uploads_task($uploads_files,$folder,$finished,$offset,$status='running',$progress=0,$size=0)
    {
        $task=get_option('unused_uploads_task',array());

        $task['running_time']=time();
        $task['status']=$status;
        $task['progress']=$progress;
        $task['size']+=$size;
        $task['folder'][$folder]['finished']=$finished;
        $task['folder'][$folder]['offset']=$offset;
        if(!empty($uploads_files))
            $this->insert_unused_uploads_files($folder,$uploads_files);
        update_option('unused_uploads_task',$task,'no');
    }

    public function get_unused_uploads_progress()
    {
        $task=get_option('unused_uploads_task',array());

        if(isset($task['folder']))
        {
            $i=0;
            foreach ($task['folder'] as $folder=>$item)
            {
                if($item['finished'])
                    $i++;
            }

            $progress=intval(($i/sizeof($task['folder']))*100);

            $ret['percent']=$progress;
            $ret['total_folders']=sizeof($task['folder']);
            $ret['scanned_folders']=$i;
            return $ret;
        }
        else
        {
            $ret['percent']=0;
            $ret['total_folders']=0;
            $ret['scanned_folders']=0;
            return $ret;
        }
    }

    public function get_unfinished_folder()
    {
        $task=get_option('unused_uploads_task',array());

        foreach ($task['folder'] as $folder=>$data)
        {
            if(!$data['finished'])
            {
                $result['folder']=$folder;
                $result['offset']=$data['offset'];
                $result['total']=$data['total'];
                return $result;
            }
        }

        return false;
    }

    public function insert_scan_result($uploads_files)
    {
        global $wpdb;
        $table_name = $wpdb->prefix . "wpvivid_scan_result";

        $query = "INSERT INTO $table_name (id,path,from_post) VALUES ";
        $values = array();
        $place_holders=array();
        foreach ( $uploads_files as $id=>$files )
        {
            if(empty($files))
                continue;
            foreach ($files as $path)
            {
                array_push( $values, $path );
                array_push( $values, $id );
                $place_holders[] = "(NULL,'%s',%d)";
            }
        }
        if ( !empty( $values ) )
        {
            $query .= implode( ', ', $place_holders );
            $prepared = $wpdb->prepare( "$query ", $values );
            $wpdb->query( $prepared );
        }
    }

    public function insert_unused_uploads_files($folder,$uploads_files)
    {
        global $wpdb;
        $table_name = $wpdb->prefix . "wpvivid_unused_uploads_files";

        $query = "INSERT INTO $table_name (id,path,folder) VALUES ";
        $values = array();
        $place_holders=array();
        foreach ( $uploads_files as $path )
        {
            array_push( $values, $path );
            array_push( $values, $folder );
            $place_holders[] = "(NULL,'%s','%s')";
        }

        if ( !empty( $values ) )
        {
            $query .= implode( ', ', $place_holders );
            $prepared = $wpdb->prepare( "$query ", $values );
            $wpdb->query( $prepared );
        }
    }

    public function is_uploads_files_exist($file)
    {
        global $wpdb;

        $file=str_replace('\\','/',$file);

        $table = $wpdb->prefix . "wpvivid_scan_result";
        $row = $wpdb->get_row( "SELECT * FROM $table WHERE path = '$file'" );
        if (empty($row))
        {
            $quick_scan=get_option('wpvivid_uc_quick_scan',false);

            if(!$quick_scan)
            {
                $attachment_id=$this->find_media_id_from_file($file);
                if($attachment_id)
                {
                    if(isset($this->file_found_cache[$attachment_id]))
                    {
                        if($this->file_found_cache[$attachment_id])
                        {
                            return true;
                        }
                        else
                        {
                            return false;
                        }
                    }

                    $files=$this->get_img_from_id($attachment_id);

                    if(!empty($files))
                    {
                        $files = implode("','",$files);
                        $sql= "SELECT * FROM $table WHERE path IN ('$files')";
                        $row = $wpdb->get_row($sql);

                        if (!empty($row))
                        {
                            $this->file_found_cache[$attachment_id]=1;
                            return true;
                        }
                        else
                        {
                            $this->file_found_cache[$attachment_id]=0;
                        }
                    }
                }
            }

            return false;
        }
        return true;
    }

    public function find_media_id_from_file( $file )
    {
        global $wpdb;

        $file=basename($file);

        $sql = "SELECT post_id
			FROM {$wpdb->postmeta}
			WHERE meta_key = '_wp_attachment_metadata'
			AND meta_value LIKE '%$file%'";

        $ret = $wpdb->get_var( $sql );

        if(!$ret)
        {
            $sql = $wpdb->prepare( "SELECT post_id
			FROM {$wpdb->postmeta}
			WHERE meta_key = '_wp_attached_file'
			AND meta_value = %s", $file
            );
            $ret = $wpdb->get_var( $sql );
        }
        return $ret;
    }

    public function get_scan_result($search,$folder)
    {
        global $wpdb;

        $where='';
        if(!empty($search)||!empty($folder))
        {
            $where='WHERE ';
            if(!empty($search))
            {
                $where.="`path` LIKE '%$search%'";
            }

            if(!empty($search)&&!empty($folder))
            {
                $where.=' AND ';
            }

            if(!empty($folder))
            {
                $where.="`folder` = '$folder'";
            }
        }

        $table = $wpdb->prefix . "wpvivid_unused_uploads_files";

        $sql="SELECT * FROM `$table` ".$where;

        return $wpdb->get_results($sql,ARRAY_A);
    }

    public function get_scan_result_count()
    {
        global $wpdb;

        $table = $wpdb->prefix . "wpvivid_unused_uploads_files";
        $sql="SELECT COUNT(*) FROM $table";

        $result=$wpdb->get_results($sql,ARRAY_N);
        if($result)
        {
            return $count=$result[0][0];
        }
        else
        {
            return false;
        }
    }

    public function get_scan_result_size()
    {
        $task=get_option('unused_uploads_task',array());

        if(empty($task))
        {
            return false;
        }
        else if(isset($task['size']))
        {
            return size_format($task['size'],2);
        }
        else
        {
            return false;
        }
    }

    public function get_all_folder()
    {
        global $wpdb;

        $table = $wpdb->prefix . "wpvivid_unused_uploads_files";
        $sql="SELECT * FROM $table GROUP BY `folder`";

        $result=$wpdb->get_results($sql,ARRAY_A);

        if($result)
        {
            $folders=array();
            foreach ($result as $item)
            {
                if($item['folder']=='.')
                {
                    $folders[]='root';
                }
                else
                {
                    $folders[]=$item['folder'];
                }

            }
           return $folders;
        }
        else
        {
            return false;
        }
    }

    public function get_selected_files_list($selected_list)
    {
        global $wpdb;

        $ids=implode(",",$selected_list);

        $table = $wpdb->prefix . "wpvivid_unused_uploads_files";
        $sql="SELECT * FROM $table WHERE `id` IN ($ids)";
        $result=$wpdb->get_results($sql,ARRAY_A);
        if($result)
        {
            $files=array();
            foreach ($result as $item)
            {
                $files[]=$item['path'];
            }
            return $files;
        }
        else
        {
            return false;
        }
    }

    public function delete_selected_files_list($selected_list)
    {
        global $wpdb;

        $table = $wpdb->prefix . "wpvivid_unused_uploads_files";

        $ids=implode(",",$selected_list);

        $sql="DELETE FROM $table WHERE `id` IN ($ids)";

        $result=$wpdb->query($sql);
        if($result)
        {
            return true;
        }
        else
        {
            return false;
        }
    }

    public function get_all_files_list($search,$folder,$offset,$count)
    {
        global $wpdb;

        $where='';
        if(!empty($search)||!empty($folder))
        {
            $where='WHERE ';
            if(!empty($search))
            {
                $where.="`path` LIKE '%$search%'";
            }

            if(!empty($search)&&!empty($folder))
            {
                $where.=' AND ';
            }

            if(!empty($folder))
            {
                $where.="`folder` = '$folder'";
            }
        }
        $where.=" LIMIT $offset,$count";
        //LIMIT

        $table = $wpdb->prefix . "wpvivid_unused_uploads_files";
        $sql="SELECT * FROM $table ".$where;
        $result=$wpdb->get_results($sql,ARRAY_A);
        if($result)
        {
            $files=array();
            foreach ($result as $item)
            {
                $files[]=$item['path'];
            }
            return $files;
        }
        else
        {
            return false;
        }
    }

    public function delete_all_files_list($search,$folder,$count)
    {
        global $wpdb;

        $where='';
        if(!empty($search)||!empty($folder))
        {
            $where='WHERE ';
            if(!empty($search))
            {
                $where.="`path` LIKE '%$search%'";
            }

            if(!empty($search)&&!empty($folder))
            {
                $where.=' AND ';
            }

            if(!empty($folder))
            {
                $where.="`folder` = '$folder'";
            }
        }
        $where.=" LIMIT $count";
        //LIMIT

        $table = $wpdb->prefix . "wpvivid_unused_uploads_files";
        $sql="DELETE FROM $table ".$where;

        $result=$wpdb->query($sql);
        if($result)
        {
            return true;
        }
        else
        {
            return false;
        }
    }
}includes/upload-cleaner/class-wpvivid-uploads-cleaner.php000064400000420466151327705670017656 0ustar00<?php
if (!defined('WPVIVID_PLUGIN_DIR'))
{
    die;
}

define('WPVIVID_UPLOADS_ISO_DIR','wpvivid_uploads'.DIRECTORY_SEPARATOR.'Isolate');

if ( ! class_exists( 'WP_List_Table' ) )
{
    require_once( ABSPATH . 'wp-admin/includes/class-wp-list-table.php' );
}

class WPvivid_Unused_Upload_Files_List extends WP_List_Table
{
    public $list;
    public $type;
    public $page_num;
    public $parent;

    public function __construct( $args = array() )
    {
        global $wpdb;
        parent::__construct(
            array(
                'plural' => 'upload_files',
                'screen' => 'upload_files',
            )
        );
    }

    public function set_parent($parent)
    {
        $this->parent=$parent;
    }

    public function set_list($list,$page_num=1)
    {
        $this->list=$list;
        $this->page_num=$page_num;
    }

    protected function get_table_classes()
    {
        return array( 'widefat striped' );
    }

    public function get_columns()
    {
        $sites_columns = array(
            'cb'          => ' ',
            'thumb'    =>__( 'Thumbnail', 'wpvivid-backuprestore' ),
            'path'    => __( 'Path', 'wpvivid-backuprestore' ),
            //'folder' => __( 'Folder', 'wpvivid-backuprestore' ),
            'size'=>__( 'Size', 'wpvivid-backuprestore' )
        );

        return $sites_columns;
    }

    public function get_pagenum()
    {
        if($this->page_num=='first')
        {
            $this->page_num=1;
        }
        else if($this->page_num=='last')
        {
            $this->page_num=$this->_pagination_args['total_pages'];
        }
        $pagenum = $this->page_num ? $this->page_num : 0;

        if ( isset( $this->_pagination_args['total_pages'] ) && $pagenum > $this->_pagination_args['total_pages'] )
        {
            $pagenum = $this->_pagination_args['total_pages'];
        }

        return max( 1, $pagenum );
    }

    public function column_cb( $item )
    {
        echo '<input type="checkbox" name="uploads" value="'.esc_attr($item['id']).'" />';
    }

    public function column_thumb($item)
    {
        $supported_image = array(
            'gif',
            'jpg',
            'jpeg',
            'png'
        );
        $item['path'] = str_replace('\\', '/', $item['path']);
        $upload_dir=wp_upload_dir();

        $path=$upload_dir['basedir'].DIRECTORY_SEPARATOR.$item['path'];
        $ext = strtolower(pathinfo($item['path'], PATHINFO_EXTENSION));
        if (in_array($ext, $supported_image)&&file_exists( $path ))
        {
            echo "<a target='_blank' href='" . esc_url($upload_dir['baseurl'].'/'.$item['path'] ).
                "'><img style='max-width: 48px; max-height: 48px;' src='" .
                esc_url($upload_dir['baseurl'].'/'.$item['path']) . "' />";
        }
        else {
            echo '<span class="dashicons dashicons-no-alt"></span>';
        }

    }

    public function column_path( $item )
    {
        $item['path']=esc_html($item['path']);
        echo esc_html('...\uploads\\'.$item['path']);
    }

    public function column_folder( $item )
    {
        if($item['folder']=='.')
        {
            echo 'Uploads root';
        }
        else
        {
            echo esc_html($item['folder']);
        }
    }

    public function column_size( $item )
    {
        $upload_dir=wp_upload_dir();
        $file_name=$upload_dir['basedir'].DIRECTORY_SEPARATOR.$item['path'];

        if(file_exists($file_name))
        {
            echo esc_html(size_format(filesize($file_name),2));
        }
        else
        {
            echo 'file not found';
        }

    }

    public function has_items()
    {
        return !empty($this->list);
    }

    /*
    public function no_items()
    {
        _e( '<a class="wpvivid-no-item" style="cursor:pointer">No items found. Click here to reset</a>' );
    }*/

    public function prepare_items()
    {
        $columns = $this->get_columns();
        $hidden = array();
        $sortable = array();
        $this->_column_headers = array($columns, $hidden, $sortable);

        $total_items =sizeof($this->list);

        $this->set_pagination_args(
            array(
                'total_items' => $total_items,
                'per_page'    => 20,
            )
        );
    }

    public function display_rows()
    {
        $this->_display_rows( $this->list );
    }

    private function _display_rows( $list )
    {
        $page=$this->get_pagenum();

        $page_list=$list;
        $temp_page_list=array();

        $count=0;
        while ( $count<$page )
        {
            $temp_page_list = array_splice( $page_list, 0, 20);
            $count++;
        }

        foreach ( $temp_page_list as $key=>$item)
        {
            $this->single_row($item);
        }
    }

    public function single_row($item)
    {
        ?>
        <tr>
            <?php $this->single_row_columns( $item ); ?>
        </tr>
        <?php
    }

    protected function pagination( $which )
    {
        if ( empty( $this->_pagination_args ) )
        {
            return;
        }

        $total_items     = $this->_pagination_args['total_items'];
        $total_pages     = $this->_pagination_args['total_pages'];
        $infinite_scroll = false;
        if ( isset( $this->_pagination_args['infinite_scroll'] ) )
        {
            $infinite_scroll = $this->_pagination_args['infinite_scroll'];
        }

        if ( 'top' === $which && $total_pages > 1 )
        {
            $this->screen->render_screen_reader_content( 'heading_pagination' );
        }

        $output = '<span class="displaying-num">' . sprintf( _n( '%s item', '%s items', $total_items, 'wpvivid-backuprestore' ), number_format_i18n( $total_items ) ) . '</span>';

        $current              = $this->get_pagenum();

        $page_links = array();

        $total_pages_before = '<span class="paging-input">';
        $total_pages_after  = '</span></span>';

        $disable_first = $disable_last = $disable_prev = $disable_next = false;

        if ( $current == 1 ) {
            $disable_first = true;
            $disable_prev  = true;
        }
        if ( $current == 2 ) {
            $disable_first = true;
        }
        if ( $current == $total_pages ) {
            $disable_last = true;
            $disable_next = true;
        }
        if ( $current == $total_pages - 1 ) {
            $disable_last = true;
        }

        if ( $disable_first ) {
            $page_links[] = '<span class="tablenav-pages-navspan button disabled" aria-hidden="true">&laquo;</span>';
        } else {
            $page_links[] = sprintf(
                "<div class='first-page button'><span class='screen-reader-text'>%s</span><span aria-hidden='true'>%s</span></div>",
                __( 'First page', 'wpvivid-backuprestore' ),
                '&laquo;'
            );
        }

        if ( $disable_prev ) {
            $page_links[] = '<span class="tablenav-pages-navspan button disabled" aria-hidden="true">&lsaquo;</span>';
        } else {
            $page_links[] = sprintf(
                "<div class='prev-page button' value='%s'><span class='screen-reader-text'>%s</span><span aria-hidden='true'>%s</span></div>",
                $current,
                __( 'Previous page', 'wpvivid-backuprestore' ),
                '&lsaquo;'
            );
        }

        if ( 'bottom' === $which ) {
            $html_current_page  = $current;
            $total_pages_before = '<span class="screen-reader-text">' . __( 'Current Page', 'wpvivid-backuprestore' ) . '</span><span id="table-paging" class="paging-input"><span class="tablenav-paging-text">';
        } else {
            $html_current_page = sprintf(
                "%s<input class='current-page'  type='text' name='paged' value='%s' size='%d' aria-describedby='table-paging' /><span class='tablenav-paging-text'>",
                '<label  class="screen-reader-text">' . __( 'Current Page', 'wpvivid-backuprestore' ) . '</label>',
                $current,
                strlen( $total_pages )
            );
        }
        $html_total_pages = sprintf( "<span class='total-pages'>%s</span>", number_format_i18n( $total_pages ) );
        $page_links[]     = $total_pages_before . sprintf( _x( '%1$s of %2$s', 'paging', 'wpvivid-backuprestore' ), $html_current_page, $html_total_pages ) . $total_pages_after;

        if ( $disable_next ) {
            $page_links[] = '<span class="tablenav-pages-navspan button disabled" aria-hidden="true">&rsaquo;</span>';
        } else {
            $page_links[] = sprintf(
                "<div class='next-page button' value='%s'><span class='screen-reader-text'>%s</span><span aria-hidden='true'>%s</span></div>",
                $current,
                __( 'Next page', 'wpvivid-backuprestore' ),
                '&rsaquo;'
            );
        }

        if ( $disable_last ) {
            $page_links[] = '<span class="tablenav-pages-navspan button disabled" aria-hidden="true">&raquo;</span>';
        } else {
            $page_links[] = sprintf(
                "<div class='last-page button'><span class='screen-reader-text'>%s</span><span aria-hidden='true'>%s</span></div>",
                __( 'Last page', 'wpvivid-backuprestore' ),
                '&raquo;'
            );
        }

        $pagination_links_class = 'pagination-links';
        if ( ! empty( $infinite_scroll ) ) {
            $pagination_links_class .= ' hide-if-js';
        }
        $output .= "\n<span class='$pagination_links_class'>" . join( "\n", $page_links ) . '</span>';

        if ( $total_pages ) {
            $page_class = $total_pages < 2 ? ' one-page' : '';
        } else {
            $page_class = ' no-pages';
        }
        $this->_pagination = "<div class='tablenav-pages{$page_class}'>$output</div>";

        echo $this->_pagination;
    }

    protected function display_tablenav( $which ) {
        $css_type = '';
        if ( 'top' === $which ) {
            wp_nonce_field( 'bulk-' . $this->_args['plural'] );
            $css_type = 'margin: 0 0 10px 0';
        }
        else if( 'bottom' === $which ) {
            $css_type = 'margin: 10px 0 0 0';
        }

        $total_pages     = $this->_pagination_args['total_pages'];
        if ( $total_pages >1)
        {
            ?>
            <div class="tablenav <?php echo esc_attr( $which ); ?>" style="<?php echo esc_attr($css_type); ?>">
                <div class="alignleft actions bulkactions">
                    <label for="wpvivid_uc_bulk_action" class="screen-reader-text"><?php esc_html_e( 'Select bulk action', 'wpvivid-backuprestore' ); ?></label>
                    <select name="action" id="wpvivid_uc_bulk_action">
                        <option value="-1"><?php esc_html_e( 'Bulk Actions', 'wpvivid-backuprestore' ); ?></option>
                        <option value="wpvivid_isolate_selected_image"><?php esc_html_e( 'Isolate selected images', 'wpvivid-backuprestore' ); ?></option>
                        <option value="wpvivid_isolate_list_image"><?php esc_html_e( 'Isolate all images', 'wpvivid-backuprestore' ); ?></option>
                    </select>
                    <input type="submit" class="button action" value="<?php esc_attr_e( 'Apply', 'wpvivid-backuprestore' ); ?>">
                </div>
                <div id="wpvivid_isolate_progress" style="margin-top: 4px; display: none;">
                    <div class="spinner is-active" style="margin: 0 5px 10px 0; float: left;"></div>
                    <div style="float: left; margin-top: 2px;"><?php esc_html_e( 'Isolating images...', 'wpvivid-backuprestore' ); ?></div>
                    <div style="clear: both;"></div>
                </div>
                <?php
                $this->extra_tablenav( $which );
                $this->pagination( $which );
                ?>

                <br class="clear" />
            </div>
            <?php
        }
        else
        {
            ?>
            <div class="tablenav <?php echo esc_attr( $which ); ?>" style="<?php echo esc_attr($css_type); ?>">
                <div class="alignleft actions bulkactions">
                    <label for="wpvivid_uc_bulk_action" class="screen-reader-text"><?php esc_html_e( 'Select bulk action', 'wpvivid-backuprestore' ); ?></label>
                    <select name="action" id="wpvivid_uc_bulk_action">
                        <option value="-1"><?php esc_html_e( 'Bulk Actions', 'wpvivid-backuprestore' ); ?></option>
                        <option value="wpvivid_isolate_selected_image"><?php esc_html_e( 'Isolate selected images', 'wpvivid-backuprestore' ); ?></option>
                        <option value="wpvivid_isolate_list_image"><?php esc_html_e( 'Isolate all images', 'wpvivid-backuprestore' ); ?></option>
                    </select>
                    <input type="submit" class="button action" value="<?php esc_attr_e( 'Apply', 'wpvivid-backuprestore' ); ?>">
                </div>
                <div id="wpvivid_isolate_progress" style="margin-top: 4px; display: none;">
                    <div class="spinner is-active" style="margin: 0 5px 10px 0; float: left;"></div>
                    <div style="float: left; margin-top: 2px;"><?php esc_html_e( 'Isolating images...', 'wpvivid-backuprestore' ); ?></div>
                    <div style="clear: both;"></div>
                </div>
                <br class="clear" />
            </div>
            <?php
        }
    }

    public function display() {
        $singular = $this->_args['singular'];

        $this->display_tablenav( 'top' );

        $this->screen->render_screen_reader_content( 'heading_list' );
        ?>
        <table class="wp-list-table <?php echo esc_attr(implode( ' ', $this->get_table_classes() )); ?>" >
            <thead>
            <tr>
                <?php $this->print_column_headers(); ?>
            </tr>
            </thead>

            <tbody id="the-list"
                <?php
                if ( $singular ) {
                    echo esc_attr(" data-wp-lists='list:$singular'");
                }
                ?>
            >
            <?php $this->display_rows_or_placeholder(); ?>
            </tbody>

            <tfoot>
            <tr>
                <?php $this->print_column_headers( false ); ?>
            </tr>
            </tfoot>

        </table>
        <?php
    }
}

class WPvivid_Isolate_Files_List extends WP_List_Table
{
    public $list;
    public $type;
    public $page_num;
    public $parent;

    public function __construct( $args = array() )
    {
        global $wpdb;
        parent::__construct(
            array(
                'plural' => 'upload_files',
                'screen' => 'upload_files',
            )
        );
    }

    public function set_parent($parent)
    {
        $this->parent=$parent;
    }

    public function set_list($list,$page_num=1)
    {
        $this->list=$list;
        $this->page_num=$page_num;
    }

    protected function get_table_classes()
    {
        return array( 'widefat striped' );
    }

    public function get_columns()
    {
        $sites_columns = array(
            'cb'          => ' ',
            'thumb'    =>__( 'Thumbnail', 'wpvivid-backuprestore' ),
            'path'    => __( 'Path', 'wpvivid-backuprestore' ),
            //'folder' => __( 'Folder', 'wpvivid-backuprestore' ),
            'size'=>__( 'Size', 'wpvivid-backuprestore' )
        );

        return $sites_columns;
    }

    public function get_pagenum()
    {
        if($this->page_num=='first')
        {
            $this->page_num=1;
        }
        else if($this->page_num=='last')
        {
            $this->page_num=$this->_pagination_args['total_pages'];
        }
        $pagenum = $this->page_num ? $this->page_num : 0;

        if ( isset( $this->_pagination_args['total_pages'] ) && $pagenum > $this->_pagination_args['total_pages'] )
        {
            $pagenum = $this->_pagination_args['total_pages'];
        }

        return max( 1, $pagenum );
    }

    public function column_cb( $item )
    {
        echo '<input type="checkbox" name="uploads" />';
    }

    public function column_thumb($item)
    {
        $supported_image = array(
            'gif',
            'jpg',
            'jpeg',
            'png'
        );


        $item['path'] = str_replace('\\', '/', $item['path']);
        $wpvivid_uploads_iso_dir = str_replace('\\', '/', WPVIVID_UPLOADS_ISO_DIR);
        $path=WP_CONTENT_DIR.DIRECTORY_SEPARATOR.WPVIVID_UPLOADS_ISO_DIR.DIRECTORY_SEPARATOR.$item['path'];

        $ext = strtolower(pathinfo($item['path'], PATHINFO_EXTENSION));
        if (in_array($ext, $supported_image)&&file_exists( $path ))
        {
            echo "<a target='_blank' href='" . esc_url(WP_CONTENT_URL.'/'.$wpvivid_uploads_iso_dir.'/'.$item['path']) .
                "'><img style='max-width: 48px; max-height: 48px;' src='" .
                esc_url(WP_CONTENT_URL.'/'.$wpvivid_uploads_iso_dir.'/'.$item['path'] ). "' />";
        }
        else {
            echo '<span class="dashicons dashicons-no-alt"></span>';
        }

    }

    public function column_path( $item )
    {
        $item['path']=esc_html($item['path']);
        echo esc_html('...\uploads\\'.$item['path']);
    }

    public function column_folder( $item )
    {
        if($item['folder']=='.')
        {
            echo 'Uploads root';
        }
        else
        {
            echo esc_html($item['folder']);
        }
    }

    public function column_size( $item )
    {
        $file_name=WP_CONTENT_DIR.DIRECTORY_SEPARATOR.WPVIVID_UPLOADS_ISO_DIR.DIRECTORY_SEPARATOR.$item['path'];

        if(file_exists($file_name))
        {
            echo esc_html(size_format(filesize($file_name),2));
        }
        else
        {
            echo 'file not found';
        }

    }

    public function has_items()
    {
        return !empty($this->list);
    }

    /*
    public function no_items()
    {
        _e( '<a class="wpvivid-no-item" style="cursor:pointer">No items found. Click here to reset</a>' );
    }
    */

    public function prepare_items()
    {
        $columns = $this->get_columns();
        $hidden = array();
        $sortable = array();
        $this->_column_headers = array($columns, $hidden, $sortable);

        $total_items =sizeof($this->list);

        $this->set_pagination_args(
            array(
                'total_items' => $total_items,
                'per_page'    => 20,
            )
        );
    }

    public function display_rows()
    {
        $this->_display_rows( $this->list );
    }

    private function _display_rows( $list )
    {
        $page=$this->get_pagenum();

        $page_list=$list;
        $temp_page_list=array();

        $count=0;
        while ( $count<$page )
        {
            $temp_page_list = array_splice( $page_list, 0, 20);
            $count++;
        }

        foreach ( $temp_page_list as $key=>$item)
        {
            $this->single_row($item);
        }
    }

    public function single_row($item)
    {
        ?>
        <tr path="<?php echo esc_attr($item['path'])?>">
            <?php $this->single_row_columns( $item ); ?>
        </tr>
        <?php
    }

    protected function pagination( $which )
    {
        if ( empty( $this->_pagination_args ) )
        {
            return;
        }

        $total_items     = $this->_pagination_args['total_items'];
        $total_pages     = $this->_pagination_args['total_pages'];
        $infinite_scroll = false;
        if ( isset( $this->_pagination_args['infinite_scroll'] ) )
        {
            $infinite_scroll = $this->_pagination_args['infinite_scroll'];
        }

        if ( 'top' === $which && $total_pages > 1 )
        {
            $this->screen->render_screen_reader_content( 'heading_pagination' );
        }

        $output = '<span class="displaying-num">' . sprintf( _n( '%s item', '%s items', $total_items, 'wpvivid-backuprestore' ), number_format_i18n( $total_items ) ) . '</span>';

        $current              = $this->get_pagenum();

        $page_links = array();

        $total_pages_before = '<span class="paging-input">';
        $total_pages_after  = '</span></span>';

        $disable_first = $disable_last = $disable_prev = $disable_next = false;

        if ( $current == 1 ) {
            $disable_first = true;
            $disable_prev  = true;
        }
        if ( $current == 2 ) {
            $disable_first = true;
        }
        if ( $current == $total_pages ) {
            $disable_last = true;
            $disable_next = true;
        }
        if ( $current == $total_pages - 1 ) {
            $disable_last = true;
        }

        if ( $disable_first ) {
            $page_links[] = '<span class="tablenav-pages-navspan button disabled" aria-hidden="true">&laquo;</span>';
        } else {
            $page_links[] = sprintf(
                "<div class='first-page button'><span class='screen-reader-text'>%s</span><span aria-hidden='true'>%s</span></div>",
                __( 'First page', 'wpvivid-backuprestore' ),
                '&laquo;'
            );
        }

        if ( $disable_prev ) {
            $page_links[] = '<span class="tablenav-pages-navspan button disabled" aria-hidden="true">&lsaquo;</span>';
        } else {
            $page_links[] = sprintf(
                "<div class='prev-page button' value='%s'><span class='screen-reader-text'>%s</span><span aria-hidden='true'>%s</span></div>",
                $current,
                __( 'Previous page', 'wpvivid-backuprestore' ),
                '&lsaquo;'
            );
        }

        if ( 'bottom' === $which ) {
            $html_current_page  = $current;
            $total_pages_before = '<span class="screen-reader-text">' . __( 'Current Page', 'wpvivid-backuprestore' ) . '</span><span id="table-paging" class="paging-input"><span class="tablenav-paging-text">';
        } else {
            $html_current_page = sprintf(
                "%s<input class='current-page'  type='text' name='paged' value='%s' size='%d' aria-describedby='table-paging' /><span class='tablenav-paging-text'>",
                '<label  class="screen-reader-text">' . __( 'Current Page', 'wpvivid-backuprestore' ) . '</label>',
                $current,
                strlen( $total_pages )
            );
        }
        $html_total_pages = sprintf( "<span class='total-pages'>%s</span>", number_format_i18n( $total_pages ) );
        $page_links[]     = $total_pages_before . sprintf( _x( '%1$s of %2$s', 'paging', 'wpvivid-backuprestore' ), $html_current_page, $html_total_pages ) . $total_pages_after;

        if ( $disable_next ) {
            $page_links[] = '<span class="tablenav-pages-navspan button disabled" aria-hidden="true">&rsaquo;</span>';
        } else {
            $page_links[] = sprintf(
                "<div class='next-page button' value='%s'><span class='screen-reader-text'>%s</span><span aria-hidden='true'>%s</span></div>",
                $current,
                __( 'Next page', 'wpvivid-backuprestore' ),
                '&rsaquo;'
            );
        }

        if ( $disable_last ) {
            $page_links[] = '<span class="tablenav-pages-navspan button disabled" aria-hidden="true">&raquo;</span>';
        } else {
            $page_links[] = sprintf(
                "<div class='last-page button'><span class='screen-reader-text'>%s</span><span aria-hidden='true'>%s</span></div>",
                __( 'Last page', 'wpvivid-backuprestore' ),
                '&raquo;'
            );
        }

        $pagination_links_class = 'pagination-links';
        if ( ! empty( $infinite_scroll ) ) {
            $pagination_links_class .= ' hide-if-js';
        }
        $output .= "\n<span class='$pagination_links_class'>" . join( "\n", $page_links ) . '</span>';

        if ( $total_pages ) {
            $page_class = $total_pages < 2 ? ' one-page' : '';
        } else {
            $page_class = ' no-pages';
        }
        $this->_pagination = "<div class='tablenav-pages{$page_class}'>$output</div>";

        echo $this->_pagination;
    }

    protected function display_tablenav( $which ) {
        $css_type = '';
        if ( 'top' === $which ) {
            wp_nonce_field( 'bulk-' . $this->_args['plural'] );
            $css_type = 'margin: 0 0 10px 0';
        }
        else if( 'bottom' === $which ) {
            $css_type = 'margin: 10px 0 0 0';
        }

        $total_pages     = $this->_pagination_args['total_pages'];

        $admin_url = apply_filters('wpvivid_get_admin_url', '');

        if ( $total_pages >1)
        {
            ?>
            <div class="tablenav <?php echo esc_attr( $which ); ?>" style="<?php echo esc_attr($css_type); ?>">
                <div class="alignleft actions bulkactions">
                    <label for="wpvivid_uc_iso_bulk_action" class="screen-reader-text"><?php esc_html_e( 'Select bulk action', 'wpvivid-backuprestore' ); ?></label>
                    <select name="action" id="wpvivid_uc_iso_bulk_action">
                        <option value="-1"><?php esc_html_e( 'Bulk Actions', 'wpvivid-backuprestore' ); ?></option>
                        <option value="wpvivid_restore_selected_image"><?php esc_html_e( 'Restore selected images', 'wpvivid-backuprestore' ); ?></option>
                        <option value="wpvivid_restore_list_image"><?php esc_html_e( 'Restore all images', 'wpvivid-backuprestore' ); ?></option>
                        <option value="wpvivid_delete_selected_image"><?php esc_html_e( 'Delete selected images', 'wpvivid-backuprestore' ); ?></option>
                        <option value="wpvivid_delete_list_image"><?php esc_html_e( 'Delete all images', 'wpvivid-backuprestore' ); ?></option>
                    </select>
                    <input type="submit" class="button action" value="<?php esc_attr_e( 'Apply', 'wpvivid-backuprestore' ); ?>">
                </div>
                <div id="wpvivid_restore_delete_progress" style="margin-top: 4px; display: none;">
                    <div class="spinner is-active" style="margin: 0 5px 10px 0; float: left;"></div>
                    <div id="wpvivid_restore_delete_text" style="float: left; margin-top: 2px;"><?php esc_html_e( 'Restoring images...', 'wpvivid-backuprestore' ); ?></div>
                    <div style="clear: both;"></div>
                </div>
                <div class="wpvivid-backup-tips" style="background: #fff; border: 1px solid #f1f1f1; border-radius: 6px; margin-top: 10px;margin-bottom: 10px">
                    <div style="float: left;">
                        <div style="padding: 10px;">
                            <strong><?php esc_html_e('Note: ', 'wpvivid-backuprestore'); ?></strong>
                            <?php echo sprintf('Once deleted, images will be lost permanently. The action cannot be undone, unless you have %1$sa backup%2$s in place.', '<a href="'. esc_url($admin_url) . 'admin.php?page=WPvivid'.'">', '</a>'); ?>
                        </div>
                    </div>
                    <div style="clear: both;"></div>
                </div>

                <?php
                $this->extra_tablenav( $which );
                $this->pagination( $which );
                ?>
                <br class="clear" />
            </div>
            <?php
        }
        else
        {
            ?>
            <div class="tablenav <?php echo esc_attr( $which ); ?>" style="<?php echo esc_attr($css_type); ?>">
                <div class="alignleft actions bulkactions">
                    <label for="wpvivid_uc_iso_bulk_action" class="screen-reader-text"><?php esc_html_e( 'Select bulk action', 'wpvivid-backuprestore' ); ?></label>
                    <select name="action" id="wpvivid_uc_iso_bulk_action">
                        <option value="-1"><?php esc_html_e( 'Bulk Actions', 'wpvivid-backuprestore' ); ?></option>
                        <option value="wpvivid_restore_selected_image"><?php esc_html_e( 'Restore selected images', 'wpvivid-backuprestore' ); ?></option>
                        <option value="wpvivid_restore_list_image"><?php esc_html_e( 'Restore all images', 'wpvivid-backuprestore' ); ?></option>
                        <option value="wpvivid_delete_selected_image"><?php esc_html_e( 'Delete selected images', 'wpvivid-backuprestore' ); ?></option>
                        <option value="wpvivid_delete_list_image"><?php esc_html_e( 'Delete all images', 'wpvivid-backuprestore' ); ?></option>
                    </select>
                    <input type="submit" class="button action" value="<?php esc_attr_e( 'Apply', 'wpvivid-backuprestore' ); ?>">
                </div>
                <div id="wpvivid_restore_delete_progress" style="margin-top: 4px; display: none;">
                    <div class="spinner is-active" style="margin: 0 5px 10px 0; float: left;"></div>
                    <div id="wpvivid_restore_delete_text" style="float: left; margin-top: 2px;"><?php esc_html_e( 'Restoring images...', 'wpvivid-backuprestore' ); ?></div>
                    <div style="clear: both;"></div>
                </div>
                <div class="wpvivid-backup-tips" style="background: #fff; border: 1px solid #f1f1f1; border-radius: 6px; margin-top: 10px;margin-bottom: 10px">
                    <div style="float: left;">
                        <div style="padding: 10px;">
                            <strong><?php esc_html_e('Note: ', 'wpvivid-backuprestore'); ?></strong>
                            <?php echo sprintf('Once deleted, images will be lost permanently. The action cannot be undone, unless you have %1$sa backup%2$s in place.', '<a href="'. esc_url($admin_url) . 'admin.php?page=WPvivid'.'">', '</a>'); ?>
                        </div>
                    </div>
                    <div style="clear: both;"></div>
                </div>
                <br class="clear" />
            </div>
            <?php
        }
    }

    public function display() {
        $singular = $this->_args['singular'];

        $this->display_tablenav( 'top' );

        $this->screen->render_screen_reader_content( 'heading_list' );
        ?>
        <table class="wp-list-table <?php echo esc_attr(implode( ' ', $this->get_table_classes() )); ?>" >
            <thead>
            <tr>
                <?php $this->print_column_headers(); ?>
            </tr>
            </thead>

            <tbody id="the-list"
                <?php
                if ( $singular ) {
                    echo esc_attr(" data-wp-lists='list:$singular'");
                }
                ?>
            >
            <?php $this->display_rows_or_placeholder(); ?>
            </tbody>

            <tfoot>
            <tr>
                <?php $this->print_column_headers( false ); ?>
            </tr>
            </tfoot>

        </table>
        <?php
    }
}

class WPvivid_Uploads_Cleaner
{
    public $main_tab;


    //public $screen_ids;
    //public $version;
    //public $plugin_name;

    public function __construct()
    {
        //$this->version = WPVIVID_UPLOADS_CLEANER_VERSION;
        //$this->plugin_name = WPVIVID_UPLOADS_CLEANER_SLUG;
        //$this->screen_ids=array();
        //$this->screen_ids[]='toplevel_page_'. $this->plugin_name;
        //add_action('admin_enqueue_scripts',array( $this,'enqueue_styles'));
        //add_action('admin_enqueue_scripts',array( $this,'enqueue_scripts'));
        //add_action('admin_menu',array( $this,'add_plugin_admin_menu'));
        //$plugin_basename = plugin_basename( plugin_dir_path( __DIR__ ) . 'wpvivid-uploads-cleaner.php' );
        //add_filter('plugin_action_links_' . $plugin_basename, array( $this,'add_action_links'));

        add_filter('wpvivid_scan_post_types',array($this,'scan_post_types'),10);

        add_action('wp_ajax_wpvivid_start_scan_uploads_files_task', array($this, 'start_scan_uploads_files_task'));
        add_action('wp_ajax_wpvivid_scan_uploads_files_from_post',array($this, 'scan_uploads_files_from_post'));

        add_action('wp_ajax_wpvivid_start_unused_files_task',array($this, 'start_unused_files_task'));
        add_action('wp_ajax_wpvivid_unused_files_task',array($this, 'unused_files_task'));

        add_action('wp_ajax_wpvivid_get_result_list',array($this, 'get_result_list'));

        add_action('wp_ajax_wpvivid_isolate_selected_image',array($this, 'isolate_selected_image'));
        add_action('wp_ajax_wpvivid_start_isolate_all_image',array($this, 'start_isolate_all_image'));
        add_action('wp_ajax_wpvivid_isolate_all_image',array($this, 'isolate_all_image'));
        //
        add_action('wp_ajax_wpvivid_get_iso_list',array($this, 'get_iso_list'));

        add_action('wp_ajax_wpvivid_delete_selected_image',array($this, 'delete_selected_image'));
        add_action('wp_ajax_wpvivid_start_delete_all_image',array($this, 'delete_all_image'));
        add_action('wp_ajax_wpvivid_delete_all_image',array($this, 'delete_all_image'));

        add_action('wp_ajax_wpvivid_restore_selected_image',array($this, 'restore_selected_image'));
        add_action('wp_ajax_wpvivid_start_restore_all_image',array($this, 'restore_all_image'));
        add_action('wp_ajax_wpvivid_restore_all_image',array($this, 'restore_all_image'));

        add_action('wp_ajax_wpvivid_uc_add_exclude_files',array($this, 'add_exclude_files'));
        //
        add_filter('wpvivid_uc_scan_include_files_regex',array($this,'scan_include_files_regex'),10);
        add_filter('wpvivid_uc_scan_exclude_files_regex',array($this,'scan_exclude_files_regex'),10);


        include_once WPVIVID_PLUGIN_DIR . '/includes/upload-cleaner/class-wpvivid-uploads-scanner.php';
        include_once WPVIVID_PLUGIN_DIR . '/includes/upload-cleaner/class-wpvivid-isolate-files.php';
        include_once WPVIVID_PLUGIN_DIR. '/includes/upload-cleaner/class-wpvivid-upload-cleaner-setting.php';

        $iso=new WPvivid_Isolate_Files();
        $iso->check_folder();

        $setting=new WPvivid_Uploads_Cleaner_Setting();


        add_filter('wpvivid_get_toolbar_menus',array($this,'get_toolbar_menus'),22);
        add_filter('wpvivid_get_admin_menus',array($this,'get_admin_menus'),22);
        add_filter('wpvivid_get_screen_ids',array($this,'get_screen_ids'),12);
    }

    public function get_screen_ids($screen_ids)
    {
        $screen_ids[]=apply_filters('wpvivid_white_label_screen_id', 'wpvivid-backup_page_wpvivid-cleaner');
        return $screen_ids;
    }

    public function get_toolbar_menus($toolbar_menus)
    {
        $admin_url = apply_filters('wpvivid_get_admin_url', '');

        $menu['id']='wpvivid_admin_menu_cleaner';
        $menu['parent']='wpvivid_admin_menu';
        $menu['title']=__('Image Cleaner', 'wpvivid-backuprestore');
        $menu['tab']= 'admin.php?page='.apply_filters('wpvivid_white_label_plugin_name', 'wpvivid-cleaner');
        $menu['href']=$admin_url . 'admin.php?page='.apply_filters('wpvivid_white_label_plugin_name', 'wpvivid-cleaner');
        $menu['capability']='administrator';
        $menu['index']=4;
        $toolbar_menus[$menu['parent']]['child'][$menu['id']]=$menu;
        return $toolbar_menus;
    }

    public function get_admin_menus($submenus)
    {
        $submenu['parent_slug']=apply_filters('wpvivid_white_label_slug', WPVIVID_PLUGIN_SLUG);
        $submenu['page_title']= apply_filters('wpvivid_white_label_display', 'WPvivid Backup');
        $submenu['menu_title']=__('Image Cleaner', 'wpvivid-backuprestore');
        $submenu['capability']='administrator';
        $submenu['menu_slug']=strtolower(sprintf('%s-cleaner', apply_filters('wpvivid_white_label_slug', 'wpvivid')));
        $submenu['index']=4;
        $submenu['function']=array($this, 'display');
        $submenus[$submenu['menu_slug']]=$submenu;
        return $submenus;
    }


    private function transfer_path($path)
    {
        $path = str_replace('\\','/',$path);
        $values = explode('/',$path);
        return implode(DIRECTORY_SEPARATOR,$values);
    }

    public function wpvivid_check_jet_engine()
    {
        if (!function_exists('get_plugins'))
        {
            require_once ABSPATH . 'wp-admin/includes/plugin.php';
        }

        $active_plugins = get_option('active_plugins');
        $plugins=get_plugins();
        $jet_engine_slug='jet-engine/jet-engine.php';

        if(!empty($plugins))
        {
            if(isset($plugins[$jet_engine_slug]))
            {
                if(in_array($jet_engine_slug, $active_plugins))
                {
                    echo '<div class="notice notice-warning inline" style="margin: 10px 0 0 0;"><p><strong>Warning:</strong> We detected that you use Jet Engine plugin on this site, 
                                                        it may have compatibility issues with our plugin, which can result in an inaccuracy of the scan result, 
                                                        so we recommend not using this feature yet.
                                                          </p></div>';
                }
            }
        }
    }

    public function display()
    {
        $scan=new WPvivid_Uploads_Scanner();
        $scan->check_table_exist();
        $scan->check_unused_uploads_files_table_exist();

        $upload_dir=wp_upload_dir();

        $path=$this->transfer_path($upload_dir['basedir']);

        $path1=$this->transfer_path(WP_CONTENT_DIR.DIRECTORY_SEPARATOR.'uploads');

        if($path!==$path1)
        {
            echo '<div class="notice notice-error inline"><p>The current version does not support custom uploads directory</p></div>';
            return;
        }

        ?>
        <div class="wrap" style="max-width:1720px;">
            <h1>
                <?php
                esc_html_e('WPvivid Image Cleaner', 'wpvivid-backuprestore');
                ?>
            </h1>

            <?php $this->wpvivid_check_jet_engine(); ?>

            <?php

            if(!class_exists('WPvivid_Tab_Page_Container'))
                include_once WPVIVID_PLUGIN_DIR . '/includes/class-wpvivid-tab-page-container.php';

            $args['is_parent_tab']=1;
            $this->main_tab=new WPvivid_Tab_Page_Container();
            $this->main_tab->add_tab(__('Scan Media', 'wpvivid-backuprestore'),'scan',array($this, 'output_scan'), $args);
            $this->main_tab->add_tab(__('Isolated Media', 'wpvivid-backuprestore'),'isolate',array($this, 'output_isolate'), $args);
            //$this->main_tab->add_tab('Database','database',array($this, 'output_database'), $args);
            $this->main_tab->display();
            if (isset($_GET['tab']))
            {
                $tab=esc_html($_GET['tab']);
                ?>
                <script>
                    jQuery(document).ready(function($)
                    {
                        jQuery( document ).trigger( '<?php echo esc_attr($this->main_tab->container_id); ?>-show','<?php echo esc_attr($tab); ?>');
                    });
                </script>
                <?php
            }
            ?>
        </div>
        <?php
    }

    public function output_scan()
    {

        $scanner=new WPvivid_Uploads_Scanner();

        $count=$scanner->get_scan_result_count();
        $size=$scanner->get_scan_result_size();


        $upload_dir=wp_upload_dir();

        $path=$this->transfer_path($upload_dir['basedir']);
        $abs=$this->transfer_path(ABSPATH);

        $path=str_replace($abs,'...'.DIRECTORY_SEPARATOR,$path);

        $folders=$scanner->get_all_folder();
        $admin_url = apply_filters('wpvivid_get_admin_url', '');
        //Before running a scan, it is recommended to <a style="cursor:pointer;" href="<?php echo $admin_url . 'admin.php?page=WPvivid';">[make a full website backup]</a> to avoid losing images.
        $progress_bar='<div class="action-progress-bar"><div class="action-progress-bar-percent" style="height:24px;width:0%"></div></div>    <div style="clear:both;"></div><div style="margin-left:10px; float: left; width:100%;"><p>Ready to scan</p></div> <div style="clear: both;"></div><div><div class="backup-log-btn"><input class="button-primary" id="wpvivid_uc_cancel" type="submit" value="Cancel" /></div></div><div style="clear: both;"></div>';
        ?>
        <div class="postbox quickbackup-addon">
            <div style="margin-top: 10px;margin-bottom: 10px;">
                <?php esc_html_e('In the tab, you can scan your media folder (uploads) to find unused images and isolate specific or all unused images.', 'wpvivid-backuprestore'); ?>
            </div>
            <div id="wpvivid_uc_scan">
                <div style="margin-top: 10px;margin-bottom: 10px;">
                    <?php esc_html_e('Media path: ', 'wpvivid-backuprestore'); ?><a><?php echo esc_html($path)?></a>
                </div>
                <input class="button-primary" style="width: 200px; height: 50px; font-size: 20px;" id="wpvivid_start_scan" type="submit" value="<?php esc_attr_e('Scan', 'wpvivid-backuprestore'); ?>">
                <div style="clear: both;"></div>
                <div style="margin-top: 10px">
                    <span>
                        <?php esc_html_e('Clicking the \'Scan\' button to find unused images in your media folder. Currently it only scans JPG and PNG images.', 'wpvivid-backuprestore'); ?>
                    </span>
                </div>
                <?php
                if($count===false)
                {
                }
                else
                {
                    echo "<p style=\"margin-top: 10px; margin-bottom: 0px;\">Last Scan: Unused media file(s) found: <strong>".esc_html($count)."</strong>. ";
                    if($size!==false)
                    {
                        echo 'Total size: '.esc_html($size).' .';
                    }
                    echo "</p>";
                }
                ?>
                <div class="wpvivid-backup-tips" style="background: #fff; border: 1px solid #f1f1f1; border-radius: 6px; margin-top: 10px;">
                    <div style="float: left;">
                        <div style="padding: 10px;">
                            <strong><?php esc_html_e('Note: ', 'wpvivid-backuprestore'); ?></strong>
                            <?php esc_html_e('Please don\'t refresh the page while running a scan.', 'wpvivid-backuprestore'); ?>
                        </div>
                    </div>
                    <div style="clear: both;"></div>
                </div>
            </div>
            <div id="wpvivid_uc_progress" style="display: none;">
                <?php
                echo '<div class="action-progress-bar"><div class="action-progress-bar-percent" style="height:24px;width:0%"></div></div>    <div style="clear:both;"></div><div style="margin-left:10px; float: left; width:100%;"><p>Ready to scan</p></div> <div style="clear: both;"></div><div><div class="backup-log-btn"><input class="button-primary" id="wpvivid_uc_cancel" type="submit" value="Cancel" /></div></div><div style="clear: both;"></div>';
                ?>
            </div>
            <br/>
        </div>
        <div class="postbox quickbackup-addon">
            <p>
                <input id="wpvivid_result_list_search" type="search" name="s" value="" placeholder="<?php esc_attr_e('Search', 'wpvivid-backuprestore'); ?>">
                <select id="wpvivid_result_list_folder" style="margin-top: -5px;">
                    <option selected="selected" value="0"><?php esc_html_e('All Folders', 'wpvivid-backuprestore'); ?></option>
                    <?php
                    if(!empty($folders))
                    {
                        asort($folders);
                        foreach ($folders as $folder)
                        {
                            echo "<option value='".esc_attr($folder)."'>".esc_html($folder)."</option>";
                        }
                    }
                    ?>
                </select>
                <input id="wpvivid_result_list_search_btn" type="submit" class="button" value="<?php esc_attr_e('Search', 'wpvivid-backuprestore'); ?>">
            </p>
        </div>
        <div class="postbox">

            <div id="wpvivid_scan_result_list" style="margin: 10px;">
                <?php

                $result=$scanner->get_scan_result('','');

                $list = new WPvivid_Unused_Upload_Files_List();

                $list->set_list($result);
                $list->prepare_items();
                $list ->display();
                ?>
            </div>
        </div>
        <script>
            var wpvivid_result_list_search='';
            var wpvivid_result_list_folder='';

            jQuery('#wpvivid_result_list_search_btn').click(function()
            {
                wpvivid_result_list_search=jQuery('#wpvivid_result_list_search').val();
                wpvivid_result_list_folder=jQuery('#wpvivid_result_list_folder').val();
                if(wpvivid_result_list_folder=='0')
                {
                    wpvivid_result_list_folder='';
                }

                if(wpvivid_result_list_folder=='root')
                {
                    wpvivid_result_list_folder='.';
                }

                wpvivid_get_result_list('first');
            });

            function wpvivid_get_result_list(page)
            {
                var ajax_data = {
                    'action': 'wpvivid_get_result_list',
                    'page':page,
                    'search':wpvivid_result_list_search,
                    'folder':wpvivid_result_list_folder
                };
                wpvivid_post_request(ajax_data, function (data)
                {
                    //var old_html= jQuery('#wpvivid_scan_result_list').html();
                    //jQuery('#wpvivid_scan_result_list').html('');
                    try
                    {
                        var jsonarray = jQuery.parseJSON(data);
                        if (jsonarray.result === 'success')
                        {
                            if(jsonarray.empty)
                            {
                                jQuery('#wpvivid_result_list_search').val('');
                                wpvivid_result_list_search='';
                                alert('No items found.');
                                //jQuery('#wpvivid_scan_result_list').html(old_html);
                            }
                            else
                            {
                                jQuery('#wpvivid_scan_result_list').html(jsonarray.html);
                            }
                        }
                        else
                        {
                            alert(jsonarray.error);
                        }
                    }
                    catch (err)
                    {
                        alert(err);
                    }
                }, function (XMLHttpRequest, textStatus, errorThrown)
                {
                    var error_message = wpvivid_output_ajaxerror('get list', textStatus, errorThrown);
                    alert(error_message);
                });
            }

            jQuery('#wpvivid_scan_result_list').on("click",'.wpvivid-no-item',function()
            {
                wpvivid_result_list_search='';
                jQuery('#wpvivid_result_list_search').val('');
                wpvivid_get_result_list('first');
            });

            jQuery('#wpvivid_scan_result_list').on("click",'.first-page',function()
            {
                wpvivid_get_result_list('first');
            });

            jQuery('#wpvivid_scan_result_list').on("click",'.prev-page',function()
            {
                var page=parseInt(jQuery(this).attr('value'));
                wpvivid_get_result_list(page-1);
            });

            jQuery('#wpvivid_scan_result_list').on("click",'.next-page',function()
            {
                var page=parseInt(jQuery(this).attr('value'));
                wpvivid_get_result_list(page+1);
            });

            jQuery('#wpvivid_scan_result_list').on("click",'.last-page',function()
            {
                wpvivid_get_result_list('last');
            });

            jQuery('#wpvivid_scan_result_list').on("keypress", '.current-page', function()
            {
                if(event.keyCode === 13){
                    var page = jQuery(this).val();
                    wpvivid_get_result_list(page);
                }
            });

            jQuery('#wpvivid_scan_result_list').on("click",'.action',function()
            {
                var selected=jQuery('#wpvivid_uc_bulk_action').val();

                if(selected=='wpvivid_isolate_selected_image')
                {
                    wpvivid_isolate_selected_image();
                }
                else if(selected=='wpvivid_isolate_list_image')
                {
                    wpvivid_start_isolate_all_image();
                }
                else if(selected=='wpvivid_ignore_selected_image')
                {
                    wpvivid_ignore_selected_image();
                }


            });

            function wpvivid_ignore_selected_image()
            {
                var json = {};
                json['selected']=Array();
                jQuery('input[name=uploads][type=checkbox]').each(function(index, value)
                {
                    if(jQuery(value).prop('checked'))
                    {
                        json['selected'].push(jQuery(value).val())
                    }
                });
                var selected= JSON.stringify(json);

                jQuery('#wpvivid_scan_result_list').find('.action').prop('disabled', true);
                //jQuery('#wpvivid_isolate_selected_image').prop('disabled', true);
                //jQuery('#wpvivid_isolate_list_image').prop('disabled', true);
                var ajax_data = {
                    'action': 'wpvivid_uc_add_exclude_files',
                    'selected':selected,
                    'search':wpvivid_result_list_search,
                    'folder':wpvivid_result_list_folder
                };
                wpvivid_post_request(ajax_data, function (data)
                {
                    jQuery('#wpvivid_scan_result_list').find('.action').prop('disabled', false);
                    //jQuery('#wpvivid_isolate_selected_image').prop('disabled', false);
                    //jQuery('#wpvivid_isolate_list_image').prop('disabled', false);

                    jQuery('#wpvivid_scan_result_list').html('');
                    try
                    {
                        var jsonarray = jQuery.parseJSON(data);
                        if (jsonarray.result === 'success')
                        {
                            jQuery('#wpvivid_scan_result_list').html(jsonarray.html);
                        }
                        else
                        {
                            alert(jsonarray.error);
                        }
                    }
                    catch (err)
                    {
                        alert(err);
                    }
                }, function (XMLHttpRequest, textStatus, errorThrown)
                {
                    jQuery('#wpvivid_scan_result_list').find('.action').prop('disabled', false);
                    //jQuery('#wpvivid_isolate_selected_image').prop('disabled', false);
                    //jQuery('#wpvivid_isolate_list_image').prop('disabled', false);
                    var error_message = wpvivid_output_ajaxerror('add options', textStatus, errorThrown);
                    alert(error_message);
                });
            }

            function wpvivid_isolate_selected_image()
            {
                var json = {};
                json['selected']=Array();
                jQuery('input[name=uploads][type=checkbox]').each(function(index, value)
                {
                    if(jQuery(value).prop('checked'))
                    {
                        json['selected'].push(jQuery(value).val())
                    }
                });
                var selected= JSON.stringify(json);

                //jQuery('#wpvivid_isolate_selected_image').prop('disabled', true);
                //jQuery('#wpvivid_isolate_list_image').prop('disabled', true);
                jQuery('#wpvivid_scan_result_list').find('.action').prop('disabled', true);
                var ajax_data = {
                    'action': 'wpvivid_isolate_selected_image',
                    'selected':selected,
                    'search':wpvivid_result_list_search,
                    'folder':wpvivid_result_list_folder
                };
                jQuery('#wpvivid_isolate_progress').show();
                wpvivid_post_request(ajax_data, function (data)
                {
                    jQuery('#wpvivid_isolate_progress').hide();
                    //jQuery('#wpvivid_isolate_selected_image').prop('disabled', false);
                    //jQuery('#wpvivid_isolate_list_image').prop('disabled', false);
                    jQuery('#wpvivid_scan_result_list').find('.action').prop('disabled', false);
                    jQuery('#wpvivid_scan_result_list').html('');
                    try
                    {
                        var jsonarray = jQuery.parseJSON(data);
                        if (jsonarray.result === 'success')
                        {
                            jQuery('#wpvivid_scan_result_list').html(jsonarray.html);
                            jQuery('#wpvivid_iso_files_list').html(jsonarray.iso);
                        }
                        else
                        {
                            alert(jsonarray.error);
                        }
                    }
                    catch (err)
                    {
                        alert(err);
                    }
                }, function (XMLHttpRequest, textStatus, errorThrown)
                {
                    jQuery('#wpvivid_isolate_progress').hide();
                    jQuery('#wpvivid_scan_result_list').find('.action').prop('disabled', false);
                    //jQuery('#wpvivid_isolate_selected_image').prop('disabled', false);
                    //jQuery('#wpvivid_isolate_list_image').prop('disabled', false);
                    var error_message = wpvivid_output_ajaxerror('add isolate files', textStatus, errorThrown);
                    alert(error_message);
                });
            }

            function wpvivid_start_isolate_all_image()
            {
                var ajax_data = {
                    'action': 'wpvivid_start_isolate_all_image',
                    'search':wpvivid_result_list_search,
                    'folder':wpvivid_result_list_folder
                };
                jQuery('#wpvivid_isolate_progress').show();
                //jQuery('#wpvivid_isolate_selected_image').prop('disabled', true);
                //jQuery('#wpvivid_isolate_list_image').prop('disabled', true);
                jQuery('#wpvivid_scan_result_list').find('.action').prop('disabled', true);
                wpvivid_post_request(ajax_data, function (data)
                {
                    jQuery('#wpvivid_isolate_progress').hide();
                    try
                    {
                        var jsonarray = jQuery.parseJSON(data);
                        if (jsonarray.result === 'success')
                        {
                            if(jsonarray.continue)
                            {
                                wpvivid_isolate_all_image();
                            }
                            else
                            {
                                location.href = '<?php echo esc_url(admin_url()) . 'admin.php?page='.esc_html(apply_filters('wpvivid_white_label_plugin_name', 'wpvivid-cleaner'));?>';
                            }
                        }
                        else if (jsonarray.result === 'failed')
                        {
                            alert(jsonarray.error);
                            jQuery('#wpvivid_scan_result_list').find('.action').prop('disabled', false);
                            //jQuery('#wpvivid_isolate_selected_image').prop('disabled', false);
                            //jQuery('#wpvivid_isolate_list_image').prop('disabled', false);
                        }
                    }
                    catch(err)
                    {
                        alert(err);
                        jQuery('#wpvivid_scan_result_list').find('.action').prop('disabled', false);
                        //jQuery('#wpvivid_isolate_selected_image').prop('disabled', false);
                        //jQuery('#wpvivid_isolate_list_image').prop('disabled', false);
                    }
                }, function (XMLHttpRequest, textStatus, errorThrown)
                {
                    jQuery('#wpvivid_isolate_progress').hide();
                    jQuery('#wpvivid_scan_result_list').find('.action').prop('disabled', false);
                    //jQuery('#wpvivid_isolate_selected_image').prop('disabled', false);
                    //jQuery('#wpvivid_isolate_list_image').prop('disabled', false);

                    var error_message = wpvivid_output_ajaxerror('add isolate files', textStatus, errorThrown);
                    alert(error_message);
                });
            }

            function wpvivid_isolate_all_image()
            {
                var ajax_data = {
                    'action': 'wpvivid_isolate_all_image',
                    'search':wpvivid_result_list_search,
                    'folder':wpvivid_result_list_folder
                };
                wpvivid_post_request(ajax_data, function(data)
                {
                    try
                    {
                        var jsonarray = jQuery.parseJSON(data);
                        if (jsonarray.result === 'success')
                        {
                            if(jsonarray.continue)
                            {
                                wpvivid_isolate_all_image();
                            }
                            else
                            {
                                location.href = '<?php echo esc_url(admin_url()) . 'admin.php?page='.esc_html(apply_filters('wpvivid_white_label_plugin_name', 'wpvivid-cleaner'));?>';
                            }
                        }
                        else if (jsonarray.result === 'failed')
                        {
                            alert(jsonarray.error);
                            jQuery('#wpvivid_scan_result_list').find('.action').prop('disabled', false);
                            //jQuery('#wpvivid_isolate_selected_image').prop('disabled', false);
                            //jQuery('#wpvivid_isolate_list_image').prop('disabled', false);
                        }
                    }
                    catch(err)
                    {
                        alert(err);
                        jQuery('#wpvivid_scan_result_list').find('.action').prop('disabled', false);
                        //jQuery('#wpvivid_isolate_selected_image').prop('disabled', false);
                        //jQuery('#wpvivid_isolate_list_image').prop('disabled', false);
                    }

                }, function(XMLHttpRequest, textStatus, errorThrown)
                {
                    var error_message = wpvivid_output_ajaxerror('add isolate files', textStatus, errorThrown);
                    alert(error_message);
                    jQuery('#wpvivid_scan_result_list').find('.action').prop('disabled', false);
                    //jQuery('#wpvivid_isolate_selected_image').prop('disabled', false);
                    //jQuery('#wpvivid_isolate_list_image').prop('disabled', false);
                });
            }

            jQuery('#wpvivid_rescan').click(function()
            {
                jQuery( document ).trigger( '<?php echo esc_attr($this->main_tab->container_id) ?>-show','scan');
            });

        </script>
        <script>
            var wpvivid_cancel=false;
            jQuery('#wpvivid_start_scan').click(function()
            {
                wpvivid_start_scan();
                //wpvivid_start_unused_files_task();
            });

            jQuery('#wpvivid_uc_progress').on("click",'#wpvivid_uc_cancel',function()
            {
                wpvivid_cancel_scan();
            });

            function wpvivid_cancel_scan()
            {
                wpvivid_cancel=true;
                jQuery('#wpvivid_uc_cancel').prop('disabled', true);
            }

            function wpvivid_start_scan()
            {
                jQuery('#wpvivid_uc_progress').show();

                jQuery('#wpvivid_uc_progress').html('<?php echo '<div class="action-progress-bar"><div class="action-progress-bar-percent" style="height:24px;width:0%"></div></div>    <div style="clear:both;"></div><div style="margin-left:10px; float: left; width:100%;"><p>Ready to scan</p></div> <div style="clear: both;"></div><div><div class="backup-log-btn"><input class="button-primary" id="wpvivid_uc_cancel" type="submit" value="Cancel" /></div></div><div style="clear: both;"></div>';?>');
                jQuery('#wpvivid_uc_scan').hide();
                jQuery('#wpvivid_uc_cancel').prop('disabled', false);

                wpvivid_cancel=false;

                var ajax_data = {
                    'action': 'wpvivid_start_scan_uploads_files_task'
                };
                wpvivid_post_request(ajax_data, function(data)
                {
                    try
                    {
                        var jsonarray = jQuery.parseJSON(data);
                        if (jsonarray.result === 'success')
                        {
                            jQuery('#wpvivid_uc_progress').html(jsonarray.progress_html);
                            if(jsonarray.continue)
                            {
                                scan_uploads_files(jsonarray.start);
                            }
                            else
                            {
                                wpvivid_start_unused_files_task();
                            }
                        }
                        else if (jsonarray.result === 'failed')
                        {
                            jQuery('#wpvivid_uc_progress').hide();
                            jQuery('#wpvivid_uc_scan').show();
                        }
                    }
                    catch(err)
                    {
                        alert(err);
                        jQuery('#wpvivid_uc_progress').hide();
                        jQuery('#wpvivid_uc_scan').show();
                    }

                }, function(XMLHttpRequest, textStatus, errorThrown)
                {
                    var error_message = wpvivid_output_ajaxerror('scan files', textStatus, errorThrown);
                    alert(error_message);

                    jQuery('#wpvivid_uc_progress').hide();
                    jQuery('#wpvivid_uc_scan').show();
                });
            }

            function scan_uploads_files(start)
            {
                if(wpvivid_cancel)
                {
                    jQuery('#wpvivid_uc_progress').hide();
                    jQuery('#wpvivid_uc_scan').show();
                    jQuery('#wpvivid_uc_cancel').prop('disabled', false);
                    return;
                }

                var ajax_data = {
                    'action': 'wpvivid_scan_uploads_files_from_post',
                    'start':start
                };
                wpvivid_post_request(ajax_data, function(data)
                {
                    try
                    {
                        var jsonarray = jQuery.parseJSON(data);
                        if (jsonarray.result === 'success')
                        {
                            jQuery('#wpvivid_uc_progress').html(jsonarray.progress_html);
                            if(jsonarray.continue)
                            {
                                scan_uploads_files(jsonarray.start);
                            }
                            else
                            {
                                wpvivid_start_unused_files_task();
                            }
                        }
                        else if (jsonarray.result === 'failed')
                        {
                            alert(jsonarray.error);
                            jQuery('#wpvivid_uc_progress').hide();
                            jQuery('#wpvivid_uc_scan').show();
                        }
                    }
                    catch(err)
                    {
                        alert(err);
                        jQuery('#wpvivid_uc_progress').hide();
                        jQuery('#wpvivid_uc_scan').show();
                    }

                }, function(XMLHttpRequest, textStatus, errorThrown)
                {
                    var error_message = wpvivid_output_ajaxerror('scan files', textStatus, errorThrown);
                    alert(error_message);

                    jQuery('#wpvivid_uc_progress').hide();
                    jQuery('#wpvivid_uc_scan').show();
                });
            }

            function wpvivid_start_unused_files_task()
            {
                if(wpvivid_cancel)
                {
                    jQuery('#wpvivid_uc_progress').hide();
                    jQuery('#wpvivid_uc_scan').show();
                    jQuery('#wpvivid_uc_cancel').prop('disabled', false);
                    return;
                }

                var ajax_data = {
                    'action': 'wpvivid_start_unused_files_task'
                };
                wpvivid_post_request(ajax_data, function(data)
                {
                    try
                    {
                        var jsonarray = jQuery.parseJSON(data);
                        if (jsonarray.result === 'success')
                        {
                            jQuery('#wpvivid_uc_progress').html(jsonarray.progress_html);
                            if(jsonarray.continue)
                            {
                                wpvivid_unused_files_task();
                            }
                            else
                            {
                                location.href = '<?php echo esc_url(admin_url()) . 'admin.php?page='.esc_html(apply_filters('wpvivid_white_label_plugin_name', 'wpvivid-cleaner'));?>';
                            }
                        }
                        else if (jsonarray.result === 'failed')
                        {
                            alert(jsonarray.error);
                            jQuery('#wpvivid_uc_progress').hide();
                            jQuery('#wpvivid_uc_scan').show();
                        }
                    }
                    catch(err)
                    {
                        alert(err);
                        jQuery('#wpvivid_uc_progress').hide();
                        jQuery('#wpvivid_uc_scan').show();
                    }

                }, function(XMLHttpRequest, textStatus, errorThrown)
                {
                    var error_message = wpvivid_output_ajaxerror('scan files', textStatus, errorThrown);
                    alert(error_message);

                    jQuery('#wpvivid_uc_progress').hide();
                    jQuery('#wpvivid_uc_scan').show();
                });
            }

            function wpvivid_unused_files_task()
            {
                if(wpvivid_cancel)
                {
                    jQuery('#wpvivid_uc_progress').hide();
                    jQuery('#wpvivid_uc_scan').show();
                    jQuery('#wpvivid_uc_cancel').prop('disabled', false);
                    jQuery('#wpvivid_uc_scan_log').html("");
                    return;
                }

                var ajax_data = {
                    'action': 'wpvivid_unused_files_task'
                };
                wpvivid_post_request(ajax_data, function(data)
                {
                    try
                    {
                        var jsonarray = jQuery.parseJSON(data);
                        if (jsonarray.result === 'success')
                        {
                            jQuery('#wpvivid_uc_progress').html(jsonarray.progress_html);
                            if(jsonarray.continue)
                            {
                                wpvivid_unused_files_task();
                            }
                            else
                            {
                                location.href = '<?php echo esc_url(admin_url()) . 'admin.php?page='.esc_html(apply_filters('wpvivid_white_label_plugin_name', 'wpvivid-cleaner'));?>';
                            }
                        }
                        else if (jsonarray.result === 'failed')
                        {
                            alert(jsonarray.error);
                            jQuery('#wpvivid_uc_progress').hide();
                            jQuery('#wpvivid_uc_scan').show();
                        }
                    }
                    catch(err)
                    {
                        alert(err);
                        jQuery('#wpvivid_uc_progress').hide();
                        jQuery('#wpvivid_uc_scan').show();
                    }

                }, function(XMLHttpRequest, textStatus, errorThrown)
                {
                    var error_message = wpvivid_output_ajaxerror('scan files', textStatus, errorThrown);
                    alert(error_message);

                    jQuery('#wpvivid_uc_progress').hide();
                    jQuery('#wpvivid_uc_scan').show();
                });
            }

            jQuery(document).ready(function($)
            {
                jQuery('#wpvivid_uc_scan').show();
                jQuery('#wpvivid_uc_progress').hide();
            });
        </script>
        <?php
    }

    public function output_isolate()
    {
        $iso=new WPvivid_Isolate_Files();

        $path=$this->transfer_path(WP_CONTENT_DIR.DIRECTORY_SEPARATOR.WPVIVID_UPLOADS_ISO_DIR);
        $abs=$this->transfer_path(ABSPATH);

        $path=str_replace($abs,'...'.DIRECTORY_SEPARATOR,$path);
        $result=$iso->get_isolate_folder();
        ?>
        <div class="postbox quickbackup-addon">
            <div style="margin-top: 10px;margin-bottom: 10px;">
                <?php esc_html_e('This tab displays the isolated images and their locations. You can choose to restore or delete specific isolated images.', 'wpvivid-backuprestore'); ?>
            </div>
            <div style="margin-top: 10px;margin-bottom: 10px;">
                <?php esc_html_e('lsolated Folder Path: ', 'wpvivid-backuprestore'); ?><a><?php echo esc_html($path)?></a>
            </div>
        </div>
        <div class="postbox quickbackup-addon">
            <p>
                <input id="wpvivid_iso_list_search" type="search" name="s" value="" placeholder="<?php esc_attr_e('Search', 'wpvivid-backuprestore'); ?>">
                <select id="wpvivid_iso_list_folder" style="margin-top: -5px;">
                    <option selected="selected" value="0"><?php esc_html_e('All Folders', 'wpvivid-backuprestore'); ?></option>
                    <?php
                    asort($result['folders']);
                    foreach ($result['folders'] as $folder)
                    {
                        echo "<option value='".esc_attr($folder)."'>".esc_html($folder)."</option>";
                    }
                    ?>
                </select>
                <input id="wpvivid_iso_list_search_btn" type="submit" class="button" value="<?php esc_attr_e('Search', 'wpvivid-backuprestore'); ?>">
            </p>
        </div>
        <div class="postbox">
            <div id="wpvivid_iso_files_list" style="margin: 10px;">
                <?php
                $files=$iso->get_isolate_files();
                $list = new WPvivid_Isolate_Files_List();

                $list->set_list($files);
                $list->prepare_items();
                $list ->display();
                ?>
            </div>
        </div>
        <script>
            var wpvivid_iso_list_search='';
            var wpvivid_iso_list_folder='';

            jQuery('#wpvivid_iso_list_search_btn').click(function()
            {
                wpvivid_iso_list_search=jQuery('#wpvivid_iso_list_search').val();
                wpvivid_iso_list_folder=jQuery('#wpvivid_iso_list_folder').val();
                if(wpvivid_iso_list_folder=='0')
                {
                    wpvivid_iso_list_folder='';
                }

                if(wpvivid_iso_list_folder=='root')
                {
                    wpvivid_iso_list_folder='.';
                }

                wpvivid_get_iso_list('first');
            });

            function wpvivid_get_iso_list(page)
            {
                var ajax_data = {
                    'action': 'wpvivid_get_iso_list',
                    'page':page,
                    'search':wpvivid_iso_list_search,
                    'folder':wpvivid_iso_list_folder
                };

                wpvivid_post_request(ajax_data, function (data)
                {
                    //jQuery('#wpvivid_iso_files_list').html('');
                    try
                    {
                        var jsonarray = jQuery.parseJSON(data);
                        if (jsonarray.result === 'success')
                        {
                            if(jsonarray.empty)
                            {
                                jQuery('#wpvivid_iso_list_search').val('');
                                wpvivid_iso_list_search='';
                                alert('No items found.');
                            }
                            else
                            {
                                jQuery('#wpvivid_iso_files_list').html(jsonarray.html);
                            }

                        }
                        else
                        {
                            alert(jsonarray.error);
                        }
                    }
                    catch (err)
                    {
                        alert(err);
                    }
                }, function (XMLHttpRequest, textStatus, errorThrown)
                {
                    var error_message = wpvivid_output_ajaxerror('get list', textStatus, errorThrown);
                    alert(error_message);
                });
            }

            jQuery('#wpvivid_iso_files_list').on("click",'.first-page',function()
            {
                wpvivid_get_iso_list('first');
            });

            jQuery('#wpvivid_iso_files_list').on("click",'.wpvivid-no-item',function()
            {
                wpvivid_iso_list_search='';
                jQuery('#wpvivid_iso_files_list').val('');
                wpvivid_get_iso_list('first');
            });

            jQuery('#wpvivid_iso_files_list').on("click",'.prev-page',function()
            {
                var page=parseInt(jQuery(this).attr('value'));
                wpvivid_get_iso_list(page-1);
            });

            jQuery('#wpvivid_iso_files_list').on("click",'.next-page',function()
            {
                var page=parseInt(jQuery(this).attr('value'));
                wpvivid_get_iso_list(page+1);
            });

            jQuery('#wpvivid_iso_files_list').on("click",'.last-page',function()
            {
                wpvivid_get_iso_list('last');
            });

            jQuery('#wpvivid_iso_files_list').on("keypress", '.current-page', function()
            {
                if(event.keyCode === 13){
                    var page = jQuery(this).val();
                    wpvivid_get_iso_list(page);
                }
            });

            jQuery('#wpvivid_iso_files_list').on("click",'.action',function()
            {
                var selected=jQuery('#wpvivid_uc_iso_bulk_action').val();

                if(selected=='wpvivid_delete_selected_image')
                {
                    wpvivid_delete_selected_image();
                }
                else if(selected=='wpvivid_delete_list_image')
                {
                    wpvivid_start_delete_all_image();
                }
                else if(selected=='wpvivid_restore_selected_image')
                {
                    wpvivid_restore_selected_image();
                }
                else if(selected=='wpvivid_restore_list_image')
                {
                    wpvivid_start_restore_all_image();
                }

            });

            function wpvivid_delete_selected_image()
            {
                var json = {};
                json['selected']=Array();
                jQuery('input[name=uploads][type=checkbox]').each(function(index, value)
                {
                    if(jQuery(value).prop('checked'))
                    {
                        jQuery(value).closest('tr');
                        var path = jQuery(this).closest('tr').attr('path');
                        json['selected'].push(path)
                    }
                });
                var selected= JSON.stringify(json);
                jQuery('#wpvivid_iso_files_list').find('.action').prop('disabled', true);

                var ajax_data = {
                    'action': 'wpvivid_delete_selected_image',
                    'selected':selected,
                    'search':wpvivid_iso_list_search,
                    'folder':wpvivid_iso_list_folder
                };
                jQuery('#wpvivid_restore_delete_progress').show();
                jQuery('#wpvivid_restore_delete_text').html('Deleting images...');
                wpvivid_post_request(ajax_data, function (data)
                {
                    jQuery('#wpvivid_restore_delete_progress').hide();
                    jQuery('#wpvivid_iso_files_list').find('.action').prop('disabled', false);

                    jQuery('#wpvivid_iso_files_list').html('');
                    try
                    {
                        var jsonarray = jQuery.parseJSON(data);
                        if (jsonarray.result === 'success')
                        {
                            jQuery('#wpvivid_iso_files_list').html(jsonarray.html);
                        }
                        else
                        {
                            alert(jsonarray.error);
                        }
                    }
                    catch (err)
                    {
                        alert(err);
                    }
                }, function (XMLHttpRequest, textStatus, errorThrown)
                {
                    jQuery('#wpvivid_restore_delete_progress').hide();
                    jQuery('#wpvivid_iso_files_list').find('.action').prop('disabled', false);
                    //jQuery('#wpvivid_delete_selected_image').prop('disabled', false);
                    //jQuery('#wpvivid_delete_list_image').prop('disabled', false);
                    //jQuery('#wpvivid_restore_selected_image').prop('disabled', false);
                    //jQuery('#wpvivid_restore_list_image').prop('disabled', false);

                    var error_message = wpvivid_output_ajaxerror('delete files', textStatus, errorThrown);
                    alert(error_message);
                });
            }

            function wpvivid_start_delete_all_image()
            {
                var ajax_data = {
                    'action': 'wpvivid_start_delete_all_image',
                    'search':wpvivid_iso_list_search,
                    'folder':wpvivid_iso_list_folder
                };
                jQuery('#wpvivid_iso_files_list').find('.action').prop('disabled', true);
                jQuery('#wpvivid_restore_delete_progress').show();
                jQuery('#wpvivid_restore_delete_text').html('Deleting images...');
                wpvivid_post_request(ajax_data, function (data)
                {
                    jQuery('#wpvivid_restore_delete_progress').hide();
                    try
                    {
                        var jsonarray = jQuery.parseJSON(data);
                        if (jsonarray.result === 'success')
                        {
                            if(jsonarray.continue)
                            {
                                wpvivid_delete_all_image();
                            }
                            else
                            {
                                location.href = '<?php echo esc_url(admin_url()) . 'admin.php?page=' .esc_html(apply_filters('wpvivid_white_label_plugin_name', 'wpvivid-cleaner')). '&tab=isolate'?>';
                            }
                        }
                        else if (jsonarray.result === 'failed')
                        {
                            alert(jsonarray.error);
                            jQuery('#wpvivid_iso_files_list').find('.action').prop('disabled', false);
                        }
                    }
                    catch(err)
                    {
                        alert(err);
                        jQuery('#wpvivid_iso_files_list').find('.action').prop('disabled', false);
                        //jQuery('#wpvivid_delete_selected_image').prop('disabled', false);
                        //jQuery('#wpvivid_delete_list_image').prop('disabled', false);
                        //jQuery('#wpvivid_restore_selected_image').prop('disabled', false);
                        //jQuery('#wpvivid_restore_list_image').prop('disabled', false);
                    }
                }, function (XMLHttpRequest, textStatus, errorThrown)
                {
                    jQuery('#wpvivid_restore_delete_progress').hide();
                    jQuery('#wpvivid_iso_files_list').find('.action').prop('disabled', false);
                    //jQuery('#wpvivid_delete_selected_image').prop('disabled', false);
                    //jQuery('#wpvivid_delete_list_image').prop('disabled', false);
                    //jQuery('#wpvivid_restore_selected_image').prop('disabled', false);
                    //jQuery('#wpvivid_restore_list_image').prop('disabled', false);

                    var error_message = wpvivid_output_ajaxerror('delete files', textStatus, errorThrown);
                    alert(error_message);
                });
            }

            function wpvivid_delete_all_image()
            {
                var ajax_data = {
                    'action': 'wpvivid_delete_all_image',
                    'search':wpvivid_iso_list_search,
                    'folder':wpvivid_iso_list_folder
                };
                jQuery('#wpvivid_iso_files_list').find('.action').prop('disabled', true);

                wpvivid_post_request(ajax_data, function(data)
                {
                    try
                    {
                        var jsonarray = jQuery.parseJSON(data);
                        if (jsonarray.result === 'success')
                        {
                            if(jsonarray.continue)
                            {
                                wpvivid_delete_all_image();
                            }
                            else
                            {
                                location.href = '<?php echo  esc_url(admin_url()) . 'admin.php?page=' .esc_html(apply_filters('wpvivid_white_label_plugin_name', 'wpvivid-cleaner')). '&tab=isolate'?>';
                            }
                        }
                        else if (jsonarray.result === 'failed')
                        {
                            alert(jsonarray.error);
                            jQuery('#wpvivid_iso_files_list').find('.action').prop('disabled', false);
                        }
                    }
                    catch(err)
                    {
                        alert(err);
                        jQuery('#wpvivid_iso_files_list').find('.action').prop('disabled', false);
                    }

                }, function(XMLHttpRequest, textStatus, errorThrown)
                {
                    var error_message = wpvivid_output_ajaxerror('delete files', textStatus, errorThrown);
                    alert(error_message);
                    jQuery('#wpvivid_iso_files_list').find('.action').prop('disabled', false);
                });
            }

            function wpvivid_restore_selected_image()
            {
                var json = {};
                json['selected']=Array();
                jQuery('input[name=uploads][type=checkbox]').each(function(index, value)
                {
                    if(jQuery(value).prop('checked'))
                    {
                        jQuery(value).closest('tr');
                        var path = jQuery(this).closest('tr').attr('path');
                        json['selected'].push(path)
                    }
                });
                var selected= JSON.stringify(json);
                jQuery('#wpvivid_iso_files_list').find('.action').prop('disabled', true);

                var ajax_data = {
                    'action': 'wpvivid_restore_selected_image',
                    'selected':selected,
                    'search':wpvivid_iso_list_search,
                    'folder':wpvivid_iso_list_folder
                };
                jQuery('#wpvivid_restore_delete_progress').show();
                jQuery('#wpvivid_restore_delete_text').html('Restoring images...');
                wpvivid_post_request(ajax_data, function (data)
                {
                    jQuery('#wpvivid_restore_delete_progress').hide();
                    jQuery('#wpvivid_iso_files_list').find('.action').prop('disabled', false);

                    jQuery('#wpvivid_iso_files_list').html('');
                    try
                    {

                        var jsonarray = jQuery.parseJSON(data);
                        if (jsonarray.result === 'success')
                        {
                            jQuery('#wpvivid_iso_files_list').html(jsonarray.html);
                        }
                        else
                        {
                            alert(jsonarray.error);
                        }
                    }
                    catch (err)
                    {
                        alert(err);
                    }
                }, function (XMLHttpRequest, textStatus, errorThrown)
                {
                    jQuery('#wpvivid_restore_delete_progress').hide();
                    jQuery('#wpvivid_iso_files_list').find('.action').prop('disabled', false);
                    //jQuery('#wpvivid_delete_selected_image').prop('disabled', false);
                    //jQuery('#wpvivid_delete_list_image').prop('disabled', false);
                    //jQuery('#wpvivid_restore_selected_image').prop('disabled', false);
                    //jQuery('#wpvivid_restore_list_image').prop('disabled', false);

                    var error_message = wpvivid_output_ajaxerror('restore files', textStatus, errorThrown);
                    alert(error_message);
                });
            }

            function wpvivid_start_restore_all_image()
            {
                var ajax_data = {
                    'action': 'wpvivid_start_restore_all_image',
                    'search':wpvivid_iso_list_search,
                    'folder':wpvivid_iso_list_folder
                };
                jQuery('#wpvivid_restore_delete_progress').show();
                jQuery('#wpvivid_restore_delete_text').html('Restoring images...');
                wpvivid_post_request(ajax_data, function (data)
                {
                    jQuery('#wpvivid_restore_delete_progress').hide();
                    try
                    {
                        var jsonarray = jQuery.parseJSON(data);
                        if (jsonarray.result === 'success')
                        {
                            if(jsonarray.continue)
                            {
                                wpvivid_restore_all_image();
                            }
                            else
                            {
                                location.href = '<?php echo  esc_url(admin_url()) . 'admin.php?page=' .esc_html(apply_filters('wpvivid_white_label_plugin_name', 'wpvivid-cleaner')). '&tab=isolate'?>';
                            }
                        }
                        else if (jsonarray.result === 'failed')
                        {
                            alert(jsonarray.error);
                            jQuery('#wpvivid_iso_files_list').find('.action').prop('disabled', false);
                        }
                    }
                    catch(err)
                    {
                        alert(err);
                        jQuery('#wpvivid_iso_files_list').find('.action').prop('disabled', false);
                        //jQuery('#wpvivid_delete_selected_image').prop('disabled', false);
                        //jQuery('#wpvivid_delete_list_image').prop('disabled', false);
                        //jQuery('#wpvivid_restore_selected_image').prop('disabled', false);
                        //jQuery('#wpvivid_restore_list_image').prop('disabled', false);
                    }
                }, function (XMLHttpRequest, textStatus, errorThrown)
                {
                    jQuery('#wpvivid_restore_delete_progress').hide();
                    jQuery('#wpvivid_iso_files_list').find('.action').prop('disabled', false);
                    //jQuery('#wpvivid_delete_selected_image').prop('disabled', false);
                    //jQuery('#wpvivid_delete_list_image').prop('disabled', false);
                    //jQuery('#wpvivid_restore_selected_image').prop('disabled', false);
                    //jQuery('#wpvivid_restore_list_image').prop('disabled', false);

                    var error_message = wpvivid_output_ajaxerror('restore files', textStatus, errorThrown);
                    alert(error_message);
                });
            }

            function wpvivid_restore_all_image()
            {
                var ajax_data = {
                    'action': 'wpvivid_restore_all_image',
                    'search':wpvivid_iso_list_search,
                    'folder':wpvivid_iso_list_folder
                };
                wpvivid_post_request(ajax_data, function(data)
                {
                    try
                    {
                        var jsonarray = jQuery.parseJSON(data);
                        if (jsonarray.result === 'success')
                        {
                            if(jsonarray.continue)
                            {
                                wpvivid_restore_all_image();
                            }
                            else
                            {
                                location.href = '<?php echo  esc_url(admin_url()) . 'admin.php?page=' .esc_html(apply_filters('wpvivid_white_label_plugin_name', 'wpvivid-cleaner')). '&tab=isolate'?>';
                            }
                        }
                        else if (jsonarray.result === 'failed')
                        {
                            alert(jsonarray.error);
                            jQuery('#wpvivid_iso_files_list').find('.action').prop('disabled', false);
                            //jQuery('#wpvivid_delete_selected_image').prop('disabled', false);
                            //jQuery('#wpvivid_delete_list_image').prop('disabled', false);
                            //jQuery('#wpvivid_restore_selected_image').prop('disabled', false);
                            //jQuery('#wpvivid_restore_list_image').prop('disabled', false);
                        }
                    }
                    catch(err)
                    {
                        alert(err);
                        jQuery('#wpvivid_iso_files_list').find('.action').prop('disabled', false);
                    }

                }, function(XMLHttpRequest, textStatus, errorThrown)
                {
                    var error_message = wpvivid_output_ajaxerror('restore files', textStatus, errorThrown);
                    alert(error_message);
                    jQuery('#wpvivid_iso_files_list').find('.action').prop('disabled', false);
                    //jQuery('#wpvivid_delete_selected_image').prop('disabled', false);
                    //jQuery('#wpvivid_delete_list_image').prop('disabled', false);
                    //jQuery('#wpvivid_restore_selected_image').prop('disabled', false);
                    //jQuery('#wpvivid_restore_list_image').prop('disabled', false);
                });
            }
        </script>
        <?php
    }

    public function output_database()
    {
        ?>
        <div class="postbox quickbackup-addon">
            <h1>Coming soon</h1>
        </div>
        <?php
    }

    public function start_scan_uploads_files_task()
    {
        check_ajax_referer( 'wpvivid_ajax', 'nonce' );
        $check=current_user_can('manage_options');
        $check=apply_filters('wpvivid_ajax_check_security',$check);
        if(!$check)
        {
            die();
        }

        set_time_limit(30);

        $uploads_scanner=new WPvivid_Uploads_Scanner();
        $uploads_scanner->init_scan_task();
        $uploads_files=array();

        $uploads_files[0]=$uploads_scanner->scan_sidebars_widgets();

        $files=$uploads_scanner->scan_termmeta_thumbnail();
        $uploads_files[0]=array_merge($uploads_files[0],$files);
        $files=$uploads_scanner->scan_divi_options();
        $uploads_files[0]=array_merge($uploads_files[0],$files);

        $site_icon_id = (int) get_option( 'site_icon' );
        if($site_icon_id)
        {
            $files=$uploads_scanner->get_attachment_size($site_icon_id);
            $uploads_files[0]=array_merge($uploads_files[0],$files);
        }
        $site_logo_id = get_option( 'site_logo' );
        if($site_logo_id)
        {
            $files=$uploads_scanner->get_attachment_size($site_logo_id);
            $uploads_files[0]=array_merge($uploads_files[0],$files);
        }

        $count=$uploads_scanner->get_post_count();

        $start=0;
        $limit=min(get_option('wpvivid_uc_scan_limit',20),$count);

        $posts=$uploads_scanner->get_posts($start,$limit);



        foreach ($posts as $post)
        {
            $media=$uploads_scanner->get_media_from_post_content($post);
            //$uploads_files['post_id']=$post;
            //$uploads_files['uploads_files']=$media;
            //$uploads_files=array_merge($uploads_files,$media);

            if(!empty($media))
            {
                $uploads_files[$post]=$media;
            }

            $media=$uploads_scanner->get_media_from_post_meta($post);

            if(!empty($media))
            {
                if(isset($uploads_files[$post]))
                    $uploads_files[$post]=array_merge($uploads_files[$post],$media);
                else
                    $uploads_files[$post]=$media;
            }

            $media=$uploads_scanner->get_media_from_post_meta_elementor($post);

            if(!empty($media))
            {
                if(isset($uploads_files[$post]))
                    $uploads_files[$post]=array_merge($uploads_files[$post],$media);
                else
                    $uploads_files[$post]=$media;
            }
            //$uploads_ids=array_merge($uploads_ids,$media);
            $media=$uploads_scanner->get_media_from_post_custom_meta($post);

            if(!empty($media))
            {
                if(isset($uploads_files[$post]))
                    $uploads_files[$post]=array_merge($uploads_files[$post],$media);
                else
                    $uploads_files[$post]=$media;
            }

            //fix theme WpResidence
            $media=$uploads_scanner->get_media_from_wpresidence($post);

            if(!empty($media))
            {
                if(isset($uploads_files[$post]))
                    $uploads_files[$post]=array_merge($uploads_files[$post],$media);
                else
                    $uploads_files[$post]=$media;
            }

            //fix breakdance page builder
            $media=$uploads_scanner->get_media_from_breakdance($post);

            if(!empty($media))
            {
                if(isset($uploads_files[$post]))
                    $uploads_files[$post]=array_merge($uploads_files[$post],$media);
                else
                    $uploads_files[$post]=$media;
            }

            //oxygen images
            $media=$uploads_scanner->get_media_from_oxygen($post);

            if(!empty($media))
            {
                if(isset($uploads_files[$post]))
                    $uploads_files[$post]=array_merge($uploads_files[$post],$media);
                else
                    $uploads_files[$post]=$media;
            }
        }

        $start+=$limit;

        $result['result']='success';
        if($count == 0){
            $result['percent']=0;
        }
        else{
            $result['percent']=intval(($start/$count)*100);
        }
        $result['total_posts']=$start;
        $result['scanned_posts']=$count;
        $result['descript']='Scanning files from posts';
        $result['progress_html']='
        <div class="action-progress-bar">
            <div class="action-progress-bar-percent" style="height:24px;width:' . $result['percent'] . '%"></div>
        </div>
        <div style="float:left;">
            <div class="backup-basic-info">
                <span class="wpvivid-element-space-right">' . __('Total Posts:', 'wpvivid-backuprestore') . '</span>
                <span>' . $result['total_posts'] . '</span>
            </div>
            <div class="backup-basic-info">
                <span class="wpvivid-element-space-right">' . __('Scanned:', 'wpvivid-backuprestore') . '</span>
                <span>' . $result['scanned_posts'] . '</span>
            </div>
        </div>       
        <div style="clear:both;"></div>
        <div style="margin-left:10px; float: left; width:100%;">
            <p>' .  $result['descript'] . '</p>
        </div>
        <div style="clear: both;"></div>
        <div>
             <div class="backup-log-btn">
                <input class="button-primary" id="wpvivid_uc_cancel" type="submit" value="' . esc_attr('Cancel', 'wpvivid-backuprestore') . '" />
             </div>          
        </div>
        <div style="clear: both;"></div>';

        if($start>=$count)
        {
            $uploads_scanner->update_scan_task($uploads_files,$start,'finished',100);
            $result['start']=$start;
            $result['status']='finished';
            $result['continue']=0;
            $result['log']='scan upload files finished'.PHP_EOL;
        }
        else
        {
            $uploads_scanner->update_scan_task($uploads_files,$start,'running');
            $result['start']=$start;
            $result['status']='running';
            $result['continue']=1;
            $result['log']='scanned posts:'.$start.PHP_EOL.'total posts:'.$count.PHP_EOL;
        }


        //$uploads_files=$uploads_scanner->get_files();
        //$result['count']=$count;
        //$result['files']=$uploads_files;

        echo wp_json_encode($result);
        die();
    }

    public function scan_uploads_files_from_post()
    {
        check_ajax_referer( 'wpvivid_ajax', 'nonce' );
        $check=current_user_can('manage_options');
        $check=apply_filters('wpvivid_ajax_check_security',$check);
        if(!$check)
        {
            die();
        }

        if(!isset($_POST['start']))
        {
            die();
        }

        $start=intval($_POST['start']);

        if(!is_int($start))
        {
            die();
        }

        set_time_limit(30);

        $uploads_scanner=new WPvivid_Uploads_Scanner();

        $count=$uploads_scanner->get_post_count();

        $limit=min(get_option('wpvivid_uc_scan_limit',20),$count);

        $posts=$uploads_scanner->get_posts($start,$limit);

        $uploads_files=array();

        foreach ($posts as $post)
        {
            $media=$uploads_scanner->get_media_from_post_content($post);
            //$uploads_files['post_id']=$post;
            //$uploads_files['uploads_files']=$media;
            //$uploads_files=array_merge($uploads_files,$media);

            if(!empty($media))
            {
                $uploads_files[$post]=$media;
            }

            $media=$uploads_scanner->get_media_from_post_meta($post);

            if(!empty($media))
            {
                if(isset($uploads_files[$post]))
                    $uploads_files[$post]=array_merge($uploads_files[$post],$media);
                else
                    $uploads_files[$post]=$media;
            }

            $media=$uploads_scanner->get_media_from_post_meta_elementor($post);

            if(!empty($media))
            {
                if(isset($uploads_files[$post]))
                    $uploads_files[$post]=array_merge($uploads_files[$post],$media);
                else
                    $uploads_files[$post]=$media;
            }

            $media=$uploads_scanner->get_media_from_post_custom_meta($post);

            if(!empty($media))
            {
                if(isset($uploads_files[$post]))
                    $uploads_files[$post]=array_merge($uploads_files[$post],$media);
                else
                    $uploads_files[$post]=$media;
            }

            //fix theme WpResidence
            $media=$uploads_scanner->get_media_from_wpresidence($post);

            if(!empty($media))
            {
                if(isset($uploads_files[$post]))
                    $uploads_files[$post]=array_merge($uploads_files[$post],$media);
                else
                    $uploads_files[$post]=$media;
            }

            //fix breakdance page builder
            $media=$uploads_scanner->get_media_from_breakdance($post);

            if(!empty($media))
            {
                if(isset($uploads_files[$post]))
                    $uploads_files[$post]=array_merge($uploads_files[$post],$media);
                else
                    $uploads_files[$post]=$media;
            }

            //oxygen images
            $media=$uploads_scanner->get_media_from_oxygen($post);

            if(!empty($media))
            {
                if(isset($uploads_files[$post]))
                    $uploads_files[$post]=array_merge($uploads_files[$post],$media);
                else
                    $uploads_files[$post]=$media;
            }
        }

        $start+=$limit;

        $result['result']='success';
        $result['percent']=intval(($start/$count)*100);
        $result['total_posts']=$start;
        $result['scanned_posts']=$count;
        $result['descript']='Scanning files from posts';
        $result['progress_html']='
        <div class="action-progress-bar">
            <div class="action-progress-bar-percent" style="height:24px;width:' . $result['percent'] . '%"></div>
        </div>
        <div style="float:left;">
            <div class="backup-basic-info">
                <span class="wpvivid-element-space-right">' . __('Total Posts:', 'wpvivid-backuprestore') . '</span>
                <span>' . $result['total_posts'] . '</span>
            </div>
            <div class="backup-basic-info">
                <span class="wpvivid-element-space-right">' . __('Scanned:', 'wpvivid-backuprestore') . '</span>
                <span>' . $result['scanned_posts'] . '</span>
            </div>
        </div>       
        <div style="clear:both;"></div>
        <div style="margin-left:10px; float: left; width:100%;">
            <p>' .  $result['descript'] . '</p>
        </div>
        <div style="clear: both;"></div>
        <div>
             <div class="backup-log-btn">
                <input class="button-primary" id="wpvivid_uc_cancel" type="submit" value="' . esc_attr('Cancel', 'wpvivid-backuprestore') . '" />
             </div>          
        </div>
        <div style="clear: both;"></div>';

        if($start>=$count)
        {
            $uploads_scanner->update_scan_task($uploads_files,$start,'finished',100);
            $result['start']=$start;
            $result['status']='finished';
            $result['continue']=0;
            $result['log']='scan upload files finished'.PHP_EOL;
        }
        else
        {
            $uploads_scanner->update_scan_task($uploads_files,$start,'running');
            $result['start']=$start;
            $result['status']='running';
            $result['continue']=1;
            $result['log']='scanned posts:'.$start.PHP_EOL.'total posts:'.$count.PHP_EOL;
        }

        $ret=$uploads_scanner->get_unused_uploads_progress();
        $result['total_folders']=$ret['total_folders'];
        $result['scanned_folders']=$ret['scanned_folders'];
        $result['percent']=$ret['percent'];

        echo wp_json_encode($result);
        die();
    }

    public function start_unused_files_task()
    {
        check_ajax_referer( 'wpvivid_ajax', 'nonce' );
        $check=current_user_can('manage_options');
        $check=apply_filters('wpvivid_ajax_check_security',$check);
        if(!$check)
        {
            die();
        }

        set_time_limit(30);

        $uploads_scanner=new WPvivid_Uploads_Scanner();

        $result=$uploads_scanner->get_folders();

        $uploads_scanner->init_unused_uploads_task($result['folders']);

        $files=array();
        foreach ($result['files'] as $file)
        {

            if(!$uploads_scanner->is_uploads_files_exist($file))
            {
                $files[]=$file;
            }
        }

        $uploads_scanner->update_unused_uploads_task($files,'.',1,0,'running',0,$result['size']);

        $result['result']='success';
        $result['status']='running';
        $result['continue']=1;
        $result['log']='scanning files'.PHP_EOL;

        $ret=$uploads_scanner->get_unused_uploads_progress();
        $result['total_folders']=$ret['total_folders'];
        $result['scanned_folders']=$ret['scanned_folders'];
        $result['percent']=$ret['percent'];
        $result['descript']='Scanning upload folder.';
        $result['progress_html']='
        <div class="action-progress-bar">
            <div class="action-progress-bar-percent" style="height:24px;width:' . $result['percent'] . '%"></div>
        </div>
        <div style="float:left;">
            <div class="backup-basic-info">
                <span class="wpvivid-element-space-right">' . __('Total Folders:', 'wpvivid-backuprestore') . '</span>
                <span>' . $result['total_folders'] . '</span>
            </div>
            <div class="backup-basic-info">
                <span class="wpvivid-element-space-right">' . __('Scanned:', 'wpvivid-backuprestore') . '</span>
                <span>' . $result['scanned_folders'] . '</span>
            </div>
        </div>       
        <div style="clear:both;"></div>
        <div style="margin-left:10px; float: left; width:100%;">
            <p>' .  $result['descript'] . '</p>
        </div>
        <div style="clear: both;"></div>
        <div>
             <div class="backup-log-btn">
                <input class="button-primary" id="wpvivid_uc_cancel" type="submit" value="' . esc_attr('Cancel', 'wpvivid-backuprestore') . '" />
             </div>          
        </div>
        <div style="clear: both;"></div>';
        $result['.']=$files;
        echo wp_json_encode($result);
        die();
    }

    public function unused_files_task()
    {
        check_ajax_referer( 'wpvivid_ajax', 'nonce' );
        $check=current_user_can('manage_options');
        $check=apply_filters('wpvivid_ajax_check_security',$check);
        if(!$check)
        {
            die();
        }

        set_time_limit(30);

        $uploads_scanner=new WPvivid_Uploads_Scanner();

        $ret=$uploads_scanner->get_unfinished_folder();

        if($ret===false)
        {
            $uploads_scanner->update_unused_uploads_task(array(),'.',1,0,'finished',100);
            $result['result']='success';
            $result['status']='finished';
            $result['log']='scanning files finished'.PHP_EOL;
            $result['percent']=100;
            $result['continue']=0;

        }
        else
        {
            $size=0;
            $folder=$ret['folder'];
            $offset=$ret['offset'];
            $total=$ret['total'];
            $files=$uploads_scanner->get_files($folder);

            $upload_folder = wp_upload_dir();

            $root_path =$upload_folder['basedir'];

            $start=0;
            $count=0;
            $limit=get_option('wpvivid_uc_files_limit',100);

            $unused_files=array();
            foreach ($files as $file)
            {
                if($count>$limit)
                {
                    $uploads_scanner->update_unused_uploads_task($unused_files,$folder,0,$start,'running',0,$size);

                    $result['result']='success';
                    $result['status']='running';
                    $result['continue']=1;
                    $task=get_option('unused_uploads_task',array());
                    $result['task']=$task;
                    $result[$folder]=$unused_files;
                    $result['log']='scanning folder '.$folder.PHP_EOL.'scanned files:'.$start.PHP_EOL;
                    $ret=$uploads_scanner->get_unused_uploads_progress();
                    $result['total_folders']=$ret['total_folders'];
                    $result['scanned_folders']=$ret['scanned_folders'];
                    $result['percent']=$ret['percent'];

                    $result['descript']='Scanning upload folder:'.$folder.'<br>'.$start.' files have been scanned in '.$total.' files';
                    $result['progress_html']='
        <div class="action-progress-bar">
            <div class="action-progress-bar-percent" style="height:24px;width:' . $result['percent'] . '%"></div>
        </div>
        <div style="float:left;">
            <div class="backup-basic-info">
                <span class="wpvivid-element-space-right">' . __('Total Folders:', 'wpvivid-backuprestore') . '</span>
                <span>' . $result['total_folders'] . '</span>
            </div>
            <div class="backup-basic-info">
                <span class="wpvivid-element-space-right">' . __('Scanned:', 'wpvivid-backuprestore') . '</span>
                <span>' . $result['scanned_folders'] . '</span>
            </div>
        </div>       
        <div style="clear:both;"></div>
        <div style="margin-left:10px; float: left; width:100%;">
            <p>' .  $result['descript'] . '</p>
        </div>
        <div style="clear: both;"></div>
        <div>
             <div class="backup-log-btn">
                <input class="button-primary" id="wpvivid_uc_cancel" type="submit" value="' . esc_attr('Cancel', 'wpvivid-backuprestore') . '" />
             </div>          
        </div>
        <div style="clear: both;"></div>';
                    echo wp_json_encode($result);
                    die();
                }

                if($start>=$offset)
                {
                    if(!$uploads_scanner->is_uploads_files_exist($file))
                    {
                        $unused_files[]=$file;
                        $size+=filesize($root_path.DIRECTORY_SEPARATOR . $file);
                    }
                    $count++;
                }
                $start++;
            }

            $uploads_scanner->update_unused_uploads_task($unused_files,$folder,1,0,'running',0,$size);

            $result['result']='success';
            $result['status']='running';
            $result['continue']=1;
            $result[$folder]=$unused_files;
            $result['log']='scanning folder '.$folder.PHP_EOL.'scanned files:'.$start.PHP_EOL;
            $ret=$uploads_scanner->get_unused_uploads_progress();
            $result['total_folders']=$ret['total_folders'];
            $result['scanned_folders']=$ret['scanned_folders'];
            $result['percent']=$ret['percent'];

            $upload_folder = wp_upload_dir();

            $result['descript']='Scanning upload folder:'.$folder.'<br>'.$start.' files have been scanned in '.$total.' files';
            $result['progress_html']='
        <div class="action-progress-bar">
            <div class="action-progress-bar-percent" style="height:24px;width:' . $result['percent'] . '%"></div>
        </div>
        <div style="float:left;">
            <div class="backup-basic-info">
                <span class="wpvivid-element-space-right">' . __('Total Folders:', 'wpvivid-backuprestore') . '</span>
                <span>' . $result['total_folders'] . '</span>
            </div>
            <div class="backup-basic-info">
                <span class="wpvivid-element-space-right">' . __('Scanned:', 'wpvivid-backuprestore') . '</span>
                <span>' . $result['scanned_folders'] . '</span>
            </div>
        </div>       
        <div style="clear:both;"></div>
        <div style="margin-left:10px; float: left; width:100%;">
            <p>' .  $result['descript'] . '</p>
        </div>
        <div style="clear: both;"></div>
        <div>
             <div class="backup-log-btn">
                <input class="button-primary" id="wpvivid_uc_cancel" type="submit" value="' . esc_attr('Cancel', 'wpvivid-backuprestore') . '" />
             </div>          
        </div>
        <div style="clear: both;"></div>';
        }
        echo wp_json_encode($result);
        die();
    }

    public function scan_post_types($post_types)
    {
        $default_post_types=array();
        $default_post_types[]='attachment';
        $default_post_types[]='revision';
        $default_post_types[]='auto-draft';
        $default_post_types[]='nav_menu_item';
        $default_post_types[]='shop_order';
        $default_post_types[]='shop_order_refund';
        $default_post_types[]='oembed_cache';
        $post_types=get_option('wpvivid_uc_post_types',$default_post_types);
        return $post_types;
    }

    public function scan_exclude_files_regex($regex)
    {
        $files=get_option('wpvivid_uc_exclude_files_regex','');
        if(empty($files))
        {
            return $regex;
        }
        $files=explode("\n", $files);
        foreach ($files as $item)
        {
            if(!empty($item))
            {
                $item=rtrim($item, '/');
                $regex[]='#'.preg_quote($this -> transfer_path($item), '/').'#';
            }
        }
        /*foreach ($files as $file)
        {
            $regex[]='#'.$file.'$#';
        }*/
        $regex[]='#webp$#';
        return $regex;
    }

    public function scan_include_files_regex($regex)
    {
        $default_file_types=array();
        $default_file_types[]='png';
        $default_file_types[]='jpg';
        $default_file_types[]='jpeg';
        $scan_file_types=get_option('wpvivid_uc_scan_file_types',$default_file_types);

        $regex=array();
        foreach ($scan_file_types as $scan_file_type)
        {
            $regex[]='#.*\.'.$scan_file_type.'#';
        }

        return $regex;
    }

    public function add_exclude_files()
    {
        check_ajax_referer( 'wpvivid_ajax', 'nonce' );
        $check=current_user_can('manage_options');
        $check=apply_filters('wpvivid_ajax_check_security',$check);
        if(!$check)
        {
            die();
        }
        $json = $_POST['selected'];
        $json = stripslashes($json);
        $json = json_decode($json, true);

        $selected_list=$json['selected'];

        $sanitize_list=array();
        foreach ($selected_list as $item)
        {
            $sanitize_list[]=intval($item);
        }

        $scanner=new WPvivid_Uploads_Scanner();
        $files=$scanner->get_selected_files_list($sanitize_list);

        $list=new WPvivid_Unused_Upload_Files_List();

        if($files===false||empty($files))
        {

        }
        else
        {
            $options=get_option('wpvivid_uc_exclude_files_regex',array());

            $options=array_merge($files,$options);

            update_option('wpvivid_uc_exclude_files_regex',$options,'no');

            $scanner->delete_selected_files_list($sanitize_list);
        }


        $search='';
        if(isset($_POST['search']))
        {
            $search=$_POST['search'];
        }

        $folder='';
        if(isset($_POST['folder']))
        {
            $folder=$_POST['folder'];
        }

        $result=$scanner->get_scan_result($search,$folder);

        $list->set_list($result);

        $list->prepare_items();
        ob_start();
        $list->display();
        $html = ob_get_clean();

        $ret['result']='success';
        $ret['html']=$html;
        echo wp_json_encode($ret);
        die();
    }

    public function get_result_list()
    {
        check_ajax_referer( 'wpvivid_ajax', 'nonce' );
        $check=current_user_can('manage_options');
        $check=apply_filters('wpvivid_ajax_check_security',$check);
        if(!$check)
        {
            die();
        }

        try
        {
            $search='';
            if(isset($_POST['search']))
            {
                $search=$_POST['search'];

            }

            $folder='';
            if(isset($_POST['folder']))
            {
                $folder=$_POST['folder'];
            }

            $list=new WPvivid_Unused_Upload_Files_List();
            $scanner=new WPvivid_Uploads_Scanner();
            $result=$scanner->get_scan_result($search,$folder);

            if(isset($_POST['page']))
            {
                $list->set_list($result,$_POST['page']);
            }
            else
            {
                $list->set_list($result);
            }

            $list->prepare_items();
            ob_start();
            $list->display();
            $html = ob_get_clean();

            $ret['result']='success';
            $ret['html']=$html;
            if(empty($result))
            {
               $ret['empty']=1;
            }
            else
            {
                $ret['empty']=0;
            }
            echo wp_json_encode($ret);
        }
        catch (Exception $error)
        {
            $message = 'An exception has occurred. class: '.get_class($error).';msg: '.$error->getMessage().';code: '.$error->getCode().';line: '.$error->getLine().';in_file: '.$error->getFile().';';
            error_log($message);
            echo wp_json_encode(array('result'=>'failed','error'=>$message));
        }
        die();
    }

    public function isolate_selected_image()
    {
        check_ajax_referer( 'wpvivid_ajax', 'nonce' );
        $check=current_user_can('manage_options');
        $check=apply_filters('wpvivid_ajax_check_security',$check);
        if(!$check)
        {
            die();
        }

        try
        {
            $json = $_POST['selected'];
            $json = stripslashes($json);
            $json = json_decode($json, true);

            $selected_list=$json['selected'];
            $sanitize_list=array();
            foreach ($selected_list as $item)
            {
                $sanitize_list[]=intval($item);
            }

            $scanner=new WPvivid_Uploads_Scanner();
            $files=$scanner->get_selected_files_list($sanitize_list);

            if($files===false||empty($files))
            {

            }
            else
            {
                $iso=new WPvivid_Isolate_Files();
                $result=$iso->isolate_files($files);

                if($result['result']=='success')
                {
                    $scanner->delete_selected_files_list($selected_list);
                }
                else
                {
                    echo wp_json_encode($result);
                    die();
                }
            }


            $search='';
            if(isset($_POST['search']))
            {
                $search=$_POST['search'];
            }

            $folder='';
            if(isset($_POST['folder']))
            {
                $folder=$_POST['folder'];
            }

            $list=new WPvivid_Unused_Upload_Files_List();
            $scanner=new WPvivid_Uploads_Scanner();
            $result=$scanner->get_scan_result($search,$folder);

            $list->set_list($result);

            $list->prepare_items();
            ob_start();
            $list->display();
            $html = ob_get_clean();

            $ret['result']='success';
            $ret['html']=$html;

            $list=new WPvivid_Isolate_Files_List();
            $iso=new WPvivid_Isolate_Files();
            $result=$iso->get_isolate_files($search,'');
            if(isset($_POST['page']))
            {
                $list->set_list($result,$_POST['page']);
            }
            else
            {
                $list->set_list($result);
            }

            $list->prepare_items();
            ob_start();
            $list->display();
            $iso = ob_get_clean();
            $ret['iso']=$iso;
            echo wp_json_encode($ret);
        }
        catch (Exception $error)
        {
            $message = 'An exception has occurred. class: '.get_class($error).';msg: '.$error->getMessage().';code: '.$error->getCode().';line: '.$error->getLine().';in_file: '.$error->getFile().';';
            error_log($message);
            echo wp_json_encode(array('result'=>'failed','error'=>$message));
        }
        die();
    }

    public function start_isolate_all_image()
    {
        check_ajax_referer( 'wpvivid_ajax', 'nonce' );
        $check=current_user_can('manage_options');
        $check=apply_filters('wpvivid_ajax_check_security',$check);
        if(!$check)
        {
            die();
        }

        try
        {
            $search='';
            if(isset($_POST['search']))
            {
                $search=$_POST['search'];
            }

            $folder='';
            if(isset($_POST['folder']))
            {
                $folder=$_POST['folder'];
            }

            $iso=new WPvivid_Isolate_Files();
            $scanner=new WPvivid_Uploads_Scanner();

            $offset=0;
            $count=100;

            $iso->init_isolate_task();
            $files=$scanner->get_all_files_list($search,$folder,$offset,$count);

            if($files===false||empty($files))
            {
                $iso->update_isolate_task(0,'finished',100);

                $result['result']='success';
                $result['status']='finished';
                $result['continue']=0;

                echo wp_json_encode($result);
                die();
            }
            else
            {
                $offset+=$count;
                $result=$iso->isolate_files($files);

                $scanner->delete_all_files_list($search,$folder,$count);

                if($result['result']=='success')
                {
                    $iso->update_isolate_task($offset);
                }
                else
                {
                    echo wp_json_encode($result);
                    die();
                }
            }

            $ret['result']='success';
            $ret['status']='running';
            $ret['continue']=1;
            echo wp_json_encode($ret);
            die();
        }
        catch (Exception $error)
        {
            $message = 'An exception has occurred. class: '.get_class($error).';msg: '.$error->getMessage().';code: '.$error->getCode().';line: '.$error->getLine().';in_file: '.$error->getFile().';';
            error_log($message);
            echo wp_json_encode(array('result'=>'failed','error'=>$message));
        }
        die();
    }

    public function isolate_all_image()
    {
        check_ajax_referer( 'wpvivid_ajax', 'nonce' );
        $check=current_user_can('manage_options');
        $check=apply_filters('wpvivid_ajax_check_security',$check);
        if(!$check)
        {
            die();
        }

        try
        {
            $search='';
            if(isset($_POST['search']))
            {
                $search=$_POST['search'];
            }

            $folder='';
            if(isset($_POST['folder']))
            {
                $folder=$_POST['folder'];
            }

            $iso=new WPvivid_Isolate_Files();
            $scanner=new WPvivid_Uploads_Scanner();

            $offset=$iso->get_isolate_task_offset();

            if($offset===false)
            {
                $result['result']='success';
                $result['status']='finished';
                $result['continue']=0;

                echo wp_json_encode($result);
                die();
            }
            $start=0;
            $count=100;
            $files=$scanner->get_all_files_list($search,$folder,$start,$count);

            if($files===false||empty($files))
            {
                $iso->update_isolate_task(0,'finished',100);

                $result['result']='success';
                $result['status']='finished';
                $result['continue']=0;

                echo wp_json_encode($result);
                die();
            }
            else
            {
                $offset+=$count;
                $result=$iso->isolate_files($files);
                $scanner->delete_all_files_list($search,$folder,$count);

                if($result['result']=='success')
                {
                    $iso->update_isolate_task($offset);
                }
                else
                {
                    echo wp_json_encode($result);
                    die();
                }
            }

            $ret['result']='success';
            $ret['status']='running';
            $ret['continue']=1;
            echo wp_json_encode($ret);
            die();
        }
        catch (Exception $error)
        {
            $message = 'An exception has occurred. class: '.get_class($error).';msg: '.$error->getMessage().';code: '.$error->getCode().';line: '.$error->getLine().';in_file: '.$error->getFile().';';
            error_log($message);
            echo wp_json_encode(array('result'=>'failed','error'=>$message));
        }
        die();
    }

    public function get_iso_list()
    {
        check_ajax_referer( 'wpvivid_ajax', 'nonce' );
        $check=current_user_can('manage_options');
        $check=apply_filters('wpvivid_ajax_check_security',$check);
        if(!$check)
        {
            die();
        }

        try
        {
            $search='';
            if(isset($_POST['search']))
            {
                $search=$_POST['search'];
            }

            $folder='';
            if(isset($_POST['folder']))
            {
                $folder=$_POST['folder'];
            }

            $folder = str_replace('\\\\', '\\', $folder);

            $list=new WPvivid_Isolate_Files_List();
            $iso=new WPvivid_Isolate_Files();
            $result=$iso->get_isolate_files($search,$folder);
            if(isset($_POST['page']))
            {
                $list->set_list($result,$_POST['page']);
            }
            else
            {
                $list->set_list($result);
            }

            $list->prepare_items();
            ob_start();
            $list->display();
            $html = ob_get_clean();

            $ret['result']='success';
            $ret['html']=$html;
            if(empty($result))
            {
                $ret['empty']=1;
            }
            else
            {
                $ret['empty']=0;
            }
            echo wp_json_encode($ret);
        }
        catch (Exception $error)
        {
            $message = 'An exception has occurred. class: '.get_class($error).';msg: '.$error->getMessage().';code: '.$error->getCode().';line: '.$error->getLine().';in_file: '.$error->getFile().';';
            error_log($message);
            echo wp_json_encode(array('result'=>'failed','error'=>$message));
        }
        die();
    }

    public function delete_selected_image()
    {
        check_ajax_referer( 'wpvivid_ajax', 'nonce' );
        $check=current_user_can('manage_options');
        $check=apply_filters('wpvivid_ajax_check_security',$check);
        if(!$check)
        {
            die();
        }

        try
        {
            $json = $_POST['selected'];
            $json = stripslashes($json);
            $json = json_decode($json, true);

            $files=array_map( 'sanitize_text_field', $json['selected']);

            $iso=new WPvivid_Isolate_Files();

            $iso->delete_files($files);

            $search='';
            if(isset($_POST['search']))
            {
                $search=sanitize_text_field($_POST['search']);
            }

            $folder='';
            if(isset($_POST['folder']))
            {
                $folder=sanitize_text_field($_POST['folder']);
            }

            $folder = str_replace('\\\\', '\\', $folder);

            $list=new WPvivid_Isolate_Files_List();
            $iso=new WPvivid_Isolate_Files();
            $result=$iso->get_isolate_files($search,$folder);
            if(isset($_POST['page']))
            {
                $list->set_list($result,$_POST['page']);
            }
            else
            {
                $list->set_list($result);
            }

            $list->prepare_items();
            ob_start();
            $list->display();
            $html = ob_get_clean();

            $ret['result']='success';
            $ret['html']=$html;
            echo wp_json_encode($ret);
        }
        catch (Exception $error)
        {
            $message = 'An exception has occurred. class: '.get_class($error).';msg: '.$error->getMessage().';code: '.$error->getCode().';line: '.$error->getLine().';in_file: '.$error->getFile().';';
            error_log($message);
            echo wp_json_encode(array('result'=>'failed','error'=>$message));
        }
        die();
    }

    public function delete_all_image()
    {
        check_ajax_referer( 'wpvivid_ajax', 'nonce' );
        $check=current_user_can('manage_options');
        $check=apply_filters('wpvivid_ajax_check_security',$check);
        if(!$check)
        {
            die();
        }
        try
        {
            $search='';
            if(isset($_POST['search']))
            {
                $search=$_POST['search'];
            }

            $folder='';
            if(isset($_POST['folder']))
            {
                $folder=$_POST['folder'];
            }

            $iso=new WPvivid_Isolate_Files();

            $count=1000;

            $files=$iso->get_isolate_files($search,$folder,$count);

            if($files===false||empty($files))
            {
                $result['result']='success';
                $result['status']='finished';
                $result['continue']=0;

                echo wp_json_encode($result);
                die();
            }
            else
            {
                $iso->delete_files_ex($files);
            }

            $ret['result']='success';
            $ret['status']='running';
            $ret['continue']=1;
            echo wp_json_encode($ret);
            die();
        }
        catch (Exception $error)
        {
            $message = 'An exception has occurred. class: '.get_class($error).';msg: '.$error->getMessage().';code: '.$error->getCode().';line: '.$error->getLine().';in_file: '.$error->getFile().';';
            error_log($message);
            echo wp_json_encode(array('result'=>'failed','error'=>$message));
        }
        die();
    }

    //restore_selected_image
    public function restore_selected_image()
    {
        check_ajax_referer( 'wpvivid_ajax', 'nonce' );
        $check=current_user_can('manage_options');
        $check=apply_filters('wpvivid_ajax_check_security',$check);
        if(!$check)
        {
            die();
        }

        try
        {
            $json = $_POST['selected'];
            $json = stripslashes($json);
            $json = json_decode($json, true);

            $files=$json['selected'];

            $iso=new WPvivid_Isolate_Files();
            $iso->restore_files($files);

            $search='';
            if(isset($_POST['search']))
            {
                $search=$_POST['search'];
            }

            $folder='';
            if(isset($_POST['folder']))
            {
                $folder=$_POST['folder'];
            }

            $folder = str_replace('\\\\', '\\', $folder);

            $list=new WPvivid_Isolate_Files_List();
            $iso=new WPvivid_Isolate_Files();
            $result=$iso->get_isolate_files($search,$folder);
            if(isset($_POST['page']))
            {
                $list->set_list($result,$_POST['page']);
            }
            else
            {
                $list->set_list($result);
            }

            $list->prepare_items();
            ob_start();
            $list->display();
            $html = ob_get_clean();

            $ret['result']='success';
            $ret['html']=$html;
            echo wp_json_encode($ret);
        }
        catch (Exception $error)
        {
            $message = 'An exception has occurred. class: '.get_class($error).';msg: '.$error->getMessage().';code: '.$error->getCode().';line: '.$error->getLine().';in_file: '.$error->getFile().';';
            error_log($message);
            echo wp_json_encode(array('result'=>'failed','error'=>$message));
        }
        die();
    }

    public function restore_all_image()
    {
        check_ajax_referer( 'wpvivid_ajax', 'nonce' );
        $check=current_user_can('manage_options');
        $check=apply_filters('wpvivid_ajax_check_security',$check);
        if(!$check)
        {
            die();
        }
        try
        {
            $search='';
            if(isset($_POST['search']))
            {
                $search=$_POST['search'];
            }

            $folder='';
            if(isset($_POST['folder']))
            {
                $folder=$_POST['folder'];
            }

            $iso=new WPvivid_Isolate_Files();

            $count=100;

            $files=$iso->get_isolate_files($search,$folder,$count);

            if($files===false||empty($files))
            {
                $result['result']='success';
                $result['status']='finished';
                $result['continue']=0;

                echo wp_json_encode($result);
                die();
            }
            else
            {
                $iso->restore_files_ex($files);
            }

            $ret['result']='success';
            $ret['status']='running';
            $ret['continue']=1;
            echo wp_json_encode($ret);
            die();
        }
        catch (Exception $error)
        {
            $message = 'An exception has occurred. class: '.get_class($error).';msg: '.$error->getMessage().';code: '.$error->getCode().';line: '.$error->getLine().';in_file: '.$error->getFile().';';
            error_log($message);
            echo wp_json_encode(array('result'=>'failed','error'=>$message));
        }
        die();
    }
}includes/upload-cleaner/class-wpvivid-isolate-files.php000064400000026632151327705670017335 0ustar00<?php

if (!defined('WPVIVID_PLUGIN_DIR'))
{
    die;
}

class WPvivid_Isolate_Files
{
    public function __construct()
    {

    }

    public function check_folder()
    {
        if(!is_dir(WP_CONTENT_DIR.DIRECTORY_SEPARATOR.WPVIVID_UPLOADS_ISO_DIR))
        {
            @mkdir(WP_CONTENT_DIR.DIRECTORY_SEPARATOR.WPVIVID_UPLOADS_ISO_DIR,0777,true);
        }
    }

    public function isolate_files($files)
    {
        $upload_dir=wp_upload_dir();
        $root_path=$upload_dir['basedir'].DIRECTORY_SEPARATOR;

        if(!is_dir(WP_CONTENT_DIR.DIRECTORY_SEPARATOR.WPVIVID_UPLOADS_ISO_DIR))
        {
            @mkdir(WP_CONTENT_DIR.DIRECTORY_SEPARATOR.WPVIVID_UPLOADS_ISO_DIR,0777,true);
        }

        $iso_dir=WP_CONTENT_DIR.DIRECTORY_SEPARATOR.WPVIVID_UPLOADS_ISO_DIR.DIRECTORY_SEPARATOR;

        foreach ($files as $file)
        {
            $from=$root_path.$file;

            $to=$iso_dir.$file;

            if(file_exists($from))
            {
                if (!is_dir(dirname($to)))
                {
                    mkdir(dirname($to), 0777, true);
                }
                @rename($from,$to);
            }
        }
        $ret['result']='success';
        return $ret;
    }

    public function init_isolate_task()
    {
        $task['start_time']=time();
        $task['running_time']=time();
        $task['status']='running';
        $task['progress']=0;
        $task['offset']=0;

        update_option('init_isolate_task',$task,'no');
    }

    public function get_isolate_task_offset()
    {
        $task=get_option('scan_unused_files_task',array());
        if(empty($task))
        {
            return false;
        }

        if($task['status']=='finished')
        {
            return false;
        }

        return $task['offset'];
    }

    public function update_isolate_task($offset,$status='running',$progress=0)
    {
        $task=get_option('scan_unused_files_task',array());

        $task['running_time']=time();
        $task['status']=$status;
        $task['progress']=$progress;
        $task['offset']=$offset;

        update_option('scan_unused_files_task',$task,'no');
    }

    public function get_isolate_folder()
    {
        $root=WP_CONTENT_DIR.DIRECTORY_SEPARATOR.WPVIVID_UPLOADS_ISO_DIR;
        $ret=$this->get_folder_list($root);
        return $ret;
    }

    public function get_isolate_files($search='',$folder_ex='',$count=0)
    {
        $root=WP_CONTENT_DIR.DIRECTORY_SEPARATOR.WPVIVID_UPLOADS_ISO_DIR;
        $ret=$this->get_folder_list($root,$search);

        if(empty($folder_ex))
        {
            $result=$ret['files'];
            foreach ($ret['folders'] as $folder)
            {
                $files=array();
                $this->scan_list_uploaded_files($files,$root.DIRECTORY_SEPARATOR.$folder,$root,$folder,$search);
                $result=array_merge($result,$files);
            }
        }
        else if($folder_ex=='.')
        {
            $result=$ret['files'];

            if($count>0&&sizeof($result)>$count)
            {
                $result=array_slice($result, 0, $count);
            }
            return $result;
        }
        else
        {
            $files=array();
            $this->scan_list_uploaded_files($files,$root.DIRECTORY_SEPARATOR.$folder_ex,$root,$folder_ex,$search);
            $result=$files;
        }

        if($count>0&&sizeof($result)>$count)
        {
            $result=array_slice($result, 0, $count);
        }

        return $result;
    }

    private function get_folder_list($root_path,$search='')
    {
        $result['folders']=array();
        $result['folders'][]='root';
        $result['files']=array();
        if(!file_exists($root_path)){
            @mkdir($root_path, 0755, true);
        }
        $handler = opendir($root_path);
        if($handler!==false)
        {
            while (($filename = readdir($handler)) !== false)
            {
                if ($filename != "." && $filename != "..")
                {
                    if (is_dir($root_path . DIRECTORY_SEPARATOR . $filename))
                    {
                        if(preg_match('#^\d{4}$#',$filename))
                        {
                            $result['folders']=array_merge( $result['folders'],$this->get_sub_folder($root_path . DIRECTORY_SEPARATOR . $filename,$filename));
                        }
                        else
                        {
                            $ret=scandir($root_path . DIRECTORY_SEPARATOR . $filename);
                            if($ret!==false&&count($ret)!=2)
                            {
                                $result['folders'][]=$filename;
                            }

                        }

                    } else {

                        if($filename=='.htaccess'||$filename=='index.php')
                        {
                            continue;
                        }

                        if(empty($search))
                        {
                            $file['path']=$filename;
                            $file['folder']='.';
                            $result['files'][] = $file;
                        }
                        else if(preg_match('#'.$search.'#',$filename))
                        {
                            $file['path']=$filename;
                            $file['folder']='.';
                            $result['files'][] = $file;
                        }
                    }
                }
            }
            if($handler)
                @closedir($handler);
        }

        return $result;
    }

    private function get_sub_folder($path,$root)
    {
        $folders=array();
        $handler = opendir($path);
        if($handler!==false)
        {
            while (($filename = readdir($handler)) !== false)
            {
                if ($filename != "." && $filename != "..")
                {
                    if (is_dir($path . DIRECTORY_SEPARATOR . $filename))
                    {
                        $ret=scandir($path . DIRECTORY_SEPARATOR . $filename);
                        if($ret!==false&&count($ret)!=2)
                        {
                            $folders[]=$root.DIRECTORY_SEPARATOR.$filename;
                        }
                    }
                }
            }
            if($handler)
                @closedir($handler);
        }
        return $folders;
    }

    private function scan_list_uploaded_files( &$files,$path,$root,$folder,$search='')
    {
        $count = 0;
        if(is_dir($path))
        {
            $handler = opendir($path);
            if($handler!==false)
            {
                while (($filename = readdir($handler)) !== false)
                {
                    if ($filename != "." && $filename != "..")
                    {
                        $count++;

                        if (is_dir($path . DIRECTORY_SEPARATOR . $filename))
                        {
                            $this->scan_list_uploaded_files($files, $path . DIRECTORY_SEPARATOR . $filename,$root,$folder,$search);
                        } else {
                            if(empty($search))
                            {
                                $file['path']=str_replace($root . DIRECTORY_SEPARATOR,'',$path . DIRECTORY_SEPARATOR . $filename);
                                $file['folder']=$folder;
                                $files[] = $file;
                            }
                            else if(preg_match('#'.$search.'#',$filename))
                            {
                                $file['path']=str_replace($root . DIRECTORY_SEPARATOR,'',$path . DIRECTORY_SEPARATOR . $filename);
                                $file['folder']=$folder;
                                $files[] = $file;
                            }
                        }
                    }
                }
                if($handler)
                    @closedir($handler);
            }
        }

        return $files;
    }

    public function delete_files($files)
    {
        $root=WP_CONTENT_DIR.DIRECTORY_SEPARATOR.WPVIVID_UPLOADS_ISO_DIR;
        $delete_media_when_delete_file=get_option('wpvivid_uc_delete_media_when_delete_file',true);
        foreach ($files as $file)
        {
            @wp_delete_file($root.DIRECTORY_SEPARATOR.$file);

            if($delete_media_when_delete_file)
            {
                $attachment_id=$this->find_media_id_from_file($file);
                if($attachment_id)
                {
                    wp_delete_attachment( $attachment_id, true );
                }
            }
        }



    }

    public function delete_files_ex($files)
    {
        $root=WP_CONTENT_DIR.DIRECTORY_SEPARATOR.WPVIVID_UPLOADS_ISO_DIR;
        $delete_media_when_delete_file=get_option('wpvivid_uc_delete_media_when_delete_file',true);
        foreach ($files as $file)
        {
            @wp_delete_file($root.DIRECTORY_SEPARATOR.$file['path']);

            if($delete_media_when_delete_file)
            {
                $attachment_id=$this->find_media_id_from_file($file['path']);
                if($attachment_id)
                {
                    wp_delete_attachment( $attachment_id, true );
                }
            }
        }
    }

    public function find_media_id_from_file( $file )
    {
        global $wpdb;

        $file=basename($file);

        $sql = "SELECT post_id
			FROM {$wpdb->postmeta}
			WHERE meta_key = '_wp_attachment_metadata'
			AND meta_value LIKE '%$file%'";

        $ret = $wpdb->get_var( $sql );

        if(!$ret)
        {
            $sql = $wpdb->prepare( "SELECT post_id
			FROM {$wpdb->postmeta}
			WHERE meta_key = '_wp_attached_file'
			AND meta_value = %s", $file
            );
            $ret = $wpdb->get_var( $sql );
        }
        return $ret;
    }

    public function restore_files($files)
    {
        $upload_dir=wp_upload_dir();

        $root_path=WP_CONTENT_DIR.DIRECTORY_SEPARATOR.WPVIVID_UPLOADS_ISO_DIR.DIRECTORY_SEPARATOR;
        $upload_path=$upload_dir['basedir'].DIRECTORY_SEPARATOR;

        foreach ($files as $file)
        {
            $from=$root_path.$file;

            $to=$upload_path.$file;

            if(file_exists($from))
            {
                if (!is_dir(dirname($to)))
                {
                    mkdir(dirname($to), 0777, true);
                }
                @rename($from,$to);
            }
        }
        $ret['result']='success';
        return $ret;
    }

    public function restore_files_ex($files)
    {
        $upload_dir=wp_upload_dir();

        $root_path=WP_CONTENT_DIR.DIRECTORY_SEPARATOR.WPVIVID_UPLOADS_ISO_DIR.DIRECTORY_SEPARATOR;
        $upload_path=$upload_dir['basedir'].DIRECTORY_SEPARATOR;

        foreach ($files as $file)
        {
            $from=$root_path.$file['path'];

            $to=$upload_path.$file['path'];

            if(file_exists($from))
            {
                if (!is_dir(dirname($to)))
                {
                    mkdir(dirname($to), 0777, true);
                }
                @rename($from,$to);
            }
        }
        $ret['result']='success';
        return $ret;
    }
}includes/snapshot/class-wpvivid-tab-page-container-ex.php000064400000023665151327705670017616 0ustar00<?php

if (!defined('WPVIVID_PLUGIN_DIR'))
{
    die;
}

if(!class_exists('WPvivid_Tab_Page_Container'))
{
    include_once WPVIVID_PLUGIN_DIR . '/includes/class-wpvivid-tab-page-container.php';
}

class WPvivid_Tab_Page_Container_Ex extends WPvivid_Tab_Page_Container
{
    public $is_transparency=0;

    public function add_tab($title,$slug,$callback,$args=array())
    {
        $new_tab['title']=$title;
        $new_tab['slug']=$slug;
        $new_tab['page']=$callback;
        foreach ($args as $key=>$arg)
        {
            $new_tab[$key]=$arg;
            if($key === 'is_parent_tab') {
                $this->is_parent_tab = $arg;
            }
            if($key === 'transparency'){
                $this->is_transparency = $arg;
            }
        }

        $this->tabs[]=$new_tab;
    }

    public function display()
    {
        $class = '';
        if($this->is_transparency){
            $class .= ' wpvivid-intab-addon';
        }
        ?>
        <div class="wpvivid-one-coloum" id="<?php echo esc_attr($this->container_id); ?>">
            <h2 class="nav-tab-wrapper wpvivid-nav-tab-wrapper">
                <?php
                $this->display_tabs();
                ?>
            </h2>
        </div>
        <?php
        if($this->is_parent_tab){
            ?>
            <div style="margin: 10px 0 0 2px;">
                <div id="poststuff" style="padding-top: 0;">
                    <div id="post-body" class="metabox-holder columns-2">
                        <div id="post-body-content">
                            <div class="inside" style="margin-top:0;">
                                <div>
                                    <?php
                                    $this->display_page();
                                    ?>
                                </div>
                            </div>
                        </div>
                        <div id="postbox-container-1" class="postbox-container">
                            <div class="meta-box-sortables">
                            </div>
                        </div>
                    </div>
                    <br class="clear">
                </div>
            </div>
            <?php
        }
        else{
            ?>
            <?php
            $this->display_page();
            ?>
            <?php
        }
        ?>

        <script>
            jQuery('#<?php echo esc_js($this->container_id)?>').on("click",".<?php echo esc_js($this->container_id)?>-tab",function()
            {
                jQuery('#<?php echo esc_js($this->container_id)?>').find( '.<?php echo esc_js($this->container_id)?>-tab' ).each(function()
                {
                    jQuery(this).removeClass( "nav-tab-active wpvivid-nav-tab-active" );
                });

                jQuery('.<?php echo esc_js($this->container_id)?>-content').each(function()
                {
                    jQuery(this).hide();
                });

                var id=jQuery(this).attr('id');
                id= id.substr(12);

                jQuery("#wpvivid_page_"+id).show();
                jQuery(this).addClass( "nav-tab-active wpvivid-nav-tab-active" );
                var top = jQuery(this).offset().top-jQuery(this).height();
                jQuery('html, body').animate({scrollTop:top}, 'slow');
            });

            jQuery('#<?php echo esc_js($this->container_id)?>').on("click",".nav-tab-delete-img-addon",function(event)
            {
                event.stopPropagation();
                var redirect=jQuery(this).attr('redirect');
                jQuery(this).parent().hide();

                jQuery('#<?php echo esc_js($this->container_id)?>').find( '.<?php echo esc_js($this->container_id)?>-tab' ).each(function()
                {
                    jQuery(this).removeClass( "nav-tab-active wpvivid-nav-tab-active" );
                });

                jQuery('.<?php echo esc_js($this->container_id)?>-content').each(function()
                {
                    jQuery(this).hide();
                });

                jQuery("#wpvivid_page_"+redirect).show();
                jQuery("#wpvivid_tab_"+redirect).addClass( "nav-tab-active wpvivid-nav-tab-active" );
                //jQuery(this).addClass( "nav-tab-active wpvivid-nav-tab-active" );
            });

            jQuery(document).ready(function($)
            {
                jQuery(document).on('<?php echo esc_js($this->container_id)?>-show', function(event,id,redirect)
                {
                    jQuery('#<?php echo esc_js($this->container_id)?>').find( '.<?php echo esc_js($this->container_id)?>-tab' ).each(function()
                    {
                        jQuery(this).removeClass( "nav-tab-active wpvivid-nav-tab-active" );
                    });

                    jQuery('.<?php echo esc_js($this->container_id); ?>-content').each(function()
                    {
                        jQuery(this).hide();
                    });

                    jQuery("#wpvivid_page_"+id).show();
                    jQuery("#wpvivid_tab_"+id).show();
                    jQuery("#wpvivid_tab_"+id).find( '.nav-tab-delete-img-addon' ).each(function()
                    {
                        jQuery(this).attr('redirect',redirect);
                    });
                    jQuery("#wpvivid_tab_"+id).addClass( "nav-tab-active wpvivid-nav-tab-active" );
                    var top = jQuery("#wpvivid_tab_"+id).offset().top-jQuery("#wpvivid_tab_"+id).height();
                    jQuery('html, body').animate({scrollTop:top}, 'slow');
                });

                jQuery(document).on('<?php echo esc_js($this->container_id)?>-delete', function(event,id,redirect)
                {
                    jQuery('#<?php echo esc_js($this->container_id)?>').find( '.<?php echo esc_js($this->container_id)?>-tab' ).each(function()
                    {
                        jQuery(this).removeClass( "nav-tab-active wpvivid-nav-tab-active" );
                    });

                    jQuery('#<?php echo esc_js($this->container_id)?>').find( '.<?php echo esc_js($this->container_id)?>-content' ).each(function()
                    {
                        jQuery(this).hide();
                    });

                    jQuery("#wpvivid_page_"+id).hide();
                    jQuery("#wpvivid_tab_"+id).hide();
                    jQuery("#wpvivid_page_"+redirect).show();
                    jQuery("#wpvivid_tab_"+redirect).addClass( "nav-tab-active wpvivid-nav-tab-active" );
                });
            });
        </script>
        <?php
    }

    public function display_tabs()
    {
        $first=true;

        foreach ($this->tabs as $tab)
        {
            $class='nav-tab wpvivid-nav-tab '.$this->container_id.'-tab';
            $span_class='';
            $span_style='';
            if($first)
            {
                $class.=' nav-tab-active wpvivid-nav-tab-active';
                $first=false;
            }

            $style='cursor:pointer;';

            if(isset($tab['hide']))
            {
                $style.=' display: none';
            }

            if(isset($tab['span_class']))
            {
                $span_class.=$tab['span_class'];
            }
            if(isset($tab['span_style']))
            {
                $span_style.=$tab['span_style'];
            }
            if(isset($tab['can_delete']))
            {
                $class.=' delete';
            }
            if(isset($tab['transparency']))
            {
                $class.=' wpvivid-transparency-tab';
            }

            echo '<a id="wpvivid_tab_'.esc_attr($tab['slug']).'" class="'.esc_attr($class).'" style="'.esc_attr($style).'">';

            if(isset($tab['can_delete']))
            {
                echo '<span class="'.esc_attr($span_class).'" style="'.esc_attr($span_style).'"></span><span style="padding-right: 1em;">'.esc_attr($tab['title']).'</span>';
                if(isset($tab['redirect']))
                {
                    echo '<div class="nav-tab-delete-img-addon" redirect="'.esc_attr($tab['redirect']).'">
                                <span class="dashicons dashicons-no-alt wpvivid-dashicons-grey" style="width: 15px; height: 15px; font-size: 15px; padding-top: 0.4em;">
                       </div>';
                }
                else
                {
                    echo '<div class="nav-tab-delete-img-addon">
                          <span class="dashicons dashicons-no-alt wpvivid-dashicons-grey" style="width: 15px; height: 15px; font-size: 15px; padding-top: 0.4em;">
                       </div>';
                }
            }
            else
            {
                echo '<span class="'.esc_attr($span_class).'" style="'.esc_attr($span_style).'"></span><span>'.esc_attr($tab['title']).'</span>';
            }
            echo '</a>';
        }
    }

    public function display_page()
    {
        $first=true;
        foreach ($this->tabs as $tab)
        {
            //delete
            /*$style='display: none;';
            if($first)
            {
                if(isset($tab['hide']))
                {

                }
                else
                {
                    $style='';
                    $first=false;
                }
            }*/
            if(isset($tab['div_style']))
            {
                $style = $tab['div_style'];
            }
            else{
                $style='display: none;';
            }

            $class='wpvivid-one-coloum wpvivid-tabcontent ';
            $class.=$this->container_id.'-content';
            echo '<div id="wpvivid_page_'.esc_attr($tab['slug']).'" class="'.esc_attr($class).'" style="'.esc_attr($style).'">';
            call_user_func($tab['page']);
            echo '</div>';
        }
    }
}includes/snapshot/class-wpvivid-snapshots-list.php000064400000031631151327705670016527 0ustar00<?php
if (!defined('WPVIVID_PLUGIN_DIR'))
{
    die;
}

if ( ! class_exists( 'WP_List_Table' ) )
{
    require_once( ABSPATH . 'wp-admin/includes/class-wp-list-table.php' );
}

if ( ! class_exists( 'WPvivid_Snapshots_List_Ex' ) )
{
    class WPvivid_Snapshots_List_Ex extends WP_List_Table
    {
        public $page_num;
        public $Snapshots_list;

        public function __construct( $args = array() )
        {
            parent::__construct(
                array(
                    'plural' => 'snapshots',
                    'screen' => 'snapshots'
                )
            );
        }

        protected function get_table_classes()
        {
            return array( 'widefat striped' );
        }

        public function get_columns()
        {
            $columns = array();
            $columns['cb'] = __( 'cb', 'wpvivid-snapshot-database' );
            $columns['wpvivid_time'] = __( 'Time', 'wpvivid-snapshot-database' );
            $columns['wpvivid_type'] = __( 'Type', 'wpvivid-snapshot-database' );
            $columns['wpvivid_prefix'] = __( 'Prefix', 'wpvivid-snapshot-database' );
            $columns['wpvivid_comment'] = __( 'Comment', 'wpvivid-snapshot-database' );
            $columns['wpvivid_actions'] = __( 'Actions', 'wpvivid-snapshot-database' );
            return $columns;
        }

        public function column_cb( $data )
        {
            echo '<input type="checkbox"/>';
        }

        public function _column_wpvivid_time( $data )
        {
            $time = gmdate('M-d-Y H:i', $data['time']);
            echo '<td>' . esc_html( $time ) . '</td>';
        }

        public function _column_wpvivid_type( $data )
        {
            echo '<td>' . esc_html( $data['type'] ) . '</td>';
        }

        public function _column_wpvivid_prefix( $data )
        {
            echo '<td>' . esc_html( $data['id'] ) . '</td>';
        }

        public function _column_wpvivid_comment( $data )
        {
            echo '<td>' . esc_html($data['comment'] ) . '</td>';
        }

        public function _column_wpvivid_actions( $data )
        {
            echo '<td>
                    <div style="cursor:pointer;float:left;">
                        <span class="dashicons dashicons-update wpvivid-dashicons-green wpvivid-snapshot-restore"></span>
                        <span class="wpvivid-snapshot-restore">Restore</span>
                        <span style="width:1rem;">|</span>
                        <span class="dashicons dashicons-trash wpvivid-dashicons-grey wpvivid-snapshot-delete"></span>
                        <span class="wpvivid-snapshot-delete">Delete</span>
                    </div>
                </td>';
        }

        public function set_list($Snapshots_list,$page_num=1)
        {
            $this->Snapshots_list=$Snapshots_list;
            $this->page_num=$page_num;
        }

        public function get_pagenum()
        {
            if($this->page_num=='first')
            {
                $this->page_num=1;
            }
            else if($this->page_num=='last')
            {
                $this->page_num=$this->_pagination_args['total_pages'];
            }
            $pagenum = $this->page_num ? $this->page_num : 0;

            if ( isset( $this->_pagination_args['total_pages'] ) && $pagenum > $this->_pagination_args['total_pages'] )
            {
                $pagenum = $this->_pagination_args['total_pages'];
            }

            return max( 1, $pagenum );
        }

        public function prepare_items()
        {
            $columns = $this->get_columns();
            $hidden = array();
            $sortable = array();
            $this->_column_headers = array($columns, $hidden, $sortable);

            $total_items =sizeof($this->Snapshots_list);

            $this->set_pagination_args(
                array(
                    'total_items' => $total_items,
                    'per_page'    => 10,
                )
            );
        }

        public function has_items()
        {
            return !empty($this->Snapshots_list);
        }

        public function display_rows()
        {
            $this->_display_rows($this->Snapshots_list);
        }

        private function _display_rows($Snapshots_list)
        {
            $page=$this->get_pagenum();

            $page_file_list=array();
            $count=0;
            while ( $count<$page )
            {
                $page_file_list = array_splice( $Snapshots_list, 0, 10);
                $count++;
            }
            foreach ( $page_file_list as $data)
            {
                $this->single_row($data);
            }
        }

        public function single_row($data)
        {
            ?>
            <tr  class='wpvivid-snapshot-row' slug="<?php echo esc_attr($data['id']);?>">
                <?php $this->single_row_columns( $data ); ?>
            </tr>
            <?php
        }

        protected function pagination( $which )
        {
            if ( empty( $this->_pagination_args ) )
            {
                return;
            }

            $total_items     = $this->_pagination_args['total_items'];
            $total_pages     = $this->_pagination_args['total_pages'];
            $infinite_scroll = false;
            if ( isset( $this->_pagination_args['infinite_scroll'] ) )
            {
                $infinite_scroll = $this->_pagination_args['infinite_scroll'];
            }

            if ( 'top' === $which && $total_pages > 1 )
            {
                $this->screen->render_screen_reader_content( 'heading_pagination' );
            }

            $output = '<span class="displaying-num">' . sprintf( _n( '%s item', '%s items', $total_items ), number_format_i18n( $total_items ) ) . '</span>';

            $current              = $this->get_pagenum();

            $page_links = array();

            $total_pages_before = '<span class="paging-input">';
            $total_pages_after  = '</span></span>';

            $disable_first = $disable_last = $disable_prev = $disable_next = false;

            if ( $current == 1 ) {
                $disable_first = true;
                $disable_prev  = true;
            }
            if ( $current == 2 ) {
                $disable_first = true;
            }
            if ( $current == $total_pages ) {
                $disable_last = true;
                $disable_next = true;
            }
            if ( $current == $total_pages - 1 ) {
                $disable_last = true;
            }

            if ( $disable_first ) {
                $page_links[] = '<span class="tablenav-pages-navspan button disabled" aria-hidden="true">&laquo;</span>';
            } else {
                $page_links[] = sprintf(
                    "<div class='first-page button'><span class='screen-reader-text'>%s</span><span aria-hidden='true'>%s</span></div>",
                    __( 'First page' ),
                    '&laquo;'
                );
            }

            if ( $disable_prev ) {
                $page_links[] = '<span class="tablenav-pages-navspan button disabled" aria-hidden="true">&lsaquo;</span>';
            } else {
                $page_links[] = sprintf(
                    "<div class='prev-page button' value='%s'><span class='screen-reader-text'>%s</span><span aria-hidden='true'>%s</span></div>",
                    $current,
                    __( 'Previous page' ),
                    '&lsaquo;'
                );
            }

            if ( 'bottom' === $which ) {
                $html_current_page  = $current;
                $total_pages_before = '<span class="screen-reader-text">' . __( 'Current Page' ) . '</span><span id="table-paging" class="paging-input"><span class="tablenav-paging-text">';
            } else {
                $html_current_page = sprintf(
                    "%s<input class='current-page' id='current-page-selector-filelist' type='text' name='paged' value='%s' size='%d' aria-describedby='table-paging' /><span class='tablenav-paging-text'>",
                    '<label for="current-page-selector-filelist" class="screen-reader-text">' . __( 'Current Page' ) . '</label>',
                    $current,
                    strlen( $total_pages )
                );
            }
            $html_total_pages = sprintf( "<span class='total-pages'>%s</span>", number_format_i18n( $total_pages ) );
            $page_links[]     = $total_pages_before . sprintf( _x( '%1$s of %2$s', 'paging' ), $html_current_page, $html_total_pages ) . $total_pages_after;

            if ( $disable_next ) {
                $page_links[] = '<span class="tablenav-pages-navspan button disabled" aria-hidden="true">&rsaquo;</span>';
            } else {
                $page_links[] = sprintf(
                    "<div class='next-page button' value='%s'><span class='screen-reader-text'>%s</span><span aria-hidden='true'>%s</span></div>",
                    $current,
                    __( 'Next page' ),
                    '&rsaquo;'
                );
            }

            if ( $disable_last ) {
                $page_links[] = '<span class="tablenav-pages-navspan button disabled" aria-hidden="true">&raquo;</span>';
            } else {
                $page_links[] = sprintf(
                    "<div class='last-page button'><span class='screen-reader-text'>%s</span><span aria-hidden='true'>%s</span></div>",
                    __( 'Last page' ),
                    '&raquo;'
                );
            }

            $pagination_links_class = 'pagination-links';
            if ( ! empty( $infinite_scroll ) ) {
                $pagination_links_class .= ' hide-if-js';
            }
            $output .= "\n<span class='$pagination_links_class'>" . join( "\n", $page_links ) . '</span>';

            if ( $total_pages ) {
                $page_class = $total_pages < 2 ? ' one-page' : '';
            } else {
                $page_class = ' no-pages';
            }
            $this->_pagination = "<div class='tablenav-pages{$page_class}'>$output</div>";

            echo $this->_pagination;
        }

        protected function display_tablenav( $which ) {
            $css_type = '';
            if ( 'top' === $which ) {
                wp_nonce_field( 'bulk-' . $this->_args['plural'] );
                $css_type = 'margin: 0 0 10px 0';
            }
            else if( 'bottom' === $which ) {
                $css_type = 'margin: 10px 0 0 0';
            }

            $total_pages     = $this->_pagination_args['total_pages'];
            if ( $total_pages >1 && 'top' === $which)
            {
                ?>
                <div class="tablenav <?php echo esc_attr( $which ); ?>" style="<?php echo esc_attr($css_type); ?>">
                    <input type="submit" id="wpvivid_delete_snapshots_action" class="button action" value="Delete the selected snapshots">
                    <?php
                    $this->extra_tablenav( $which );
                    $this->pagination( $which );
                    ?>

                    <br class="clear" />
                </div>
                <?php
            }
            else if($total_pages >1)
            {
                ?>
                <div class="tablenav <?php echo esc_attr( $which ); ?>" style="<?php echo esc_attr($css_type); ?>">
                    <?php
                    $this->extra_tablenav( $which );
                    $this->pagination( $which );
                    ?>

                    <br class="clear" />
                </div>
                <?php
            }
            else if($total_pages <=1 && 'top' === $which)
            {
                ?>
                <input type="submit" id="wpvivid_delete_snapshots_action" class="button action" value="Delete the selected snapshots">
                <p></p>
                <?php
            }
        }

        public function display()
        {
            $singular = $this->_args['singular'];

            $this->display_tablenav( 'top' );

            $this->screen->render_screen_reader_content( 'heading_list' );
            ?>
            <table class="wp-list-table <?php echo esc_attr(implode( ' ', $this->get_table_classes() )); ?>">
                <thead>
                <tr>
                    <?php $this->print_column_headers(); ?>
                </tr>
                </thead>

                <tbody id="the-list"
                    <?php
                    if ( $singular ) {
                        echo esc_attr(" data-wp-lists='list:$singular'");
                    }
                    ?>
                >
                <?php $this->display_rows_or_placeholder(); ?>
                </tbody>

            </table>
            <?php
            $this->display_tablenav( 'bottom' );
        }
    }
}

includes/snapshot/class-wpvivid-snapshot-options.php000064400000004504151327705670017063 0ustar00<?php
if (!defined('WPVIVID_PLUGIN_DIR'))
{
    die;
}

class WPvivid_Snapshot_Option_Ex
{
    public $options_table;

    public function __construct()
    {
        global $wpdb;
        $this->options_table =  $wpdb->base_prefix."wpvivid_options";
        //$this->check_tables();
    }

    public function check_tables()
    {
        global $wpdb;

        if($wpdb->get_var("SHOW TABLES LIKE '$this->options_table'") != $this->options_table)
        {
            $sql = "CREATE TABLE IF NOT EXISTS $this->options_table (
               `option_id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
               `option_name` varchar(191) NOT NULL DEFAULT '',
				`option_value` longtext NOT NULL,
				PRIMARY KEY (`option_id`),
				UNIQUE KEY `option_name` (`option_name`)
                );";

            $wpdb->query($sql);
        }
    }

    public function get_option($option_name)
    {
        global $wpdb;

        if($wpdb->get_var("SHOW TABLES LIKE '$this->options_table'") != $this->options_table)
        {
            return false;
        }
        else
        {
            global $wpdb;

            $query =$wpdb->prepare('select option_value from '.$this->options_table .' where option_name = %s', $option_name);

            $result =$wpdb->get_var($query);
            if(empty($result))
            {
                return false;
            }
            else
            {
                return maybe_unserialize($result);
            }
        }
    }

    public function update_option($option_name,$value)
    {
        global $wpdb;

        if($this->is_exists_option($option_name))
        {
            $option_value = maybe_serialize($value);
            return $wpdb->update($this->options_table, compact('option_name', 'option_value'), compact('option_name'));
        }
        else
        {
            $option_value = maybe_serialize($value);
            return $wpdb->insert($this->options_table, compact('option_name', 'option_value'));
        }
    }

    public function is_exists_option($option_name)
    {
        global $wpdb;

        $query = $wpdb->prepare('select option_value from '.$this->options_table.' where option_name = %s', $option_name);
        $result =$wpdb->get_row($query);
        return !empty($result);
    }
}includes/snapshot/class-wpvivid-snapshot-function.php000064400000055251151327705670017222 0ustar00<?php

if (!defined('WPVIVID_PLUGIN_DIR'))
{
    die;
}

class WPvivid_Snapshot_Function_Ex
{
    public $options;

    public function __construct()
    {
        $this->options=new WPvivid_Snapshot_Option_Ex();
    }

    public function check_manual_snapshot()
    {
        $snapshots=$this->options->get_option('wpvivid_snapshot');
        $manual_snapshots=array();
        if($snapshots !== false)
        {
            foreach ($snapshots as $snapshot)
            {
                if($snapshot['type']=='manual')
                {
                    $manual_snapshots[]=$snapshot;
                }
            }
        }
        $setting=$this->options->get_option('wpvivid_snapshot_setting');
        $count=isset($setting['snapshot_retention'])?$setting['snapshot_retention']:6;
        if(empty($count))
        {
            $count=6;
        }

        usort($manual_snapshots, function ($a, $b)
        {
            if ($a['time'] == $b['time'])
                return 0;

            if ($a['time'] > $b['time'])
                return 1;
            else
                return -1;
        });

        while(count($manual_snapshots)>=$count)
        {
            $manual_snapshot=array_shift($manual_snapshots);
            $this->remove_snapshot($manual_snapshot['id']);
        }
    }

    public function create_merge_snapshot($comment='')
    {
        global $wpdb;

        $snapshot_id = 'wp'.$this->create_snapshot_uid();
        $tables = $wpdb->get_results('SHOW TABLE STATUS');
        $exclude_tables[]=$wpdb->prefix.'wpvivid_log';
        $exclude_tables[]=$wpdb->base_prefix."wpvivid_increment_big_ids";
        $exclude_tables[]=$wpdb->prefix.'wpvivid_merge_db';
        $exclude_tables[]=$wpdb->prefix.'wpvivid_options';
        $exclude_tables[]=$wpdb->base_prefix."wpvivid_record_task";
        $exclude_tables=apply_filters('wpvivid_create_snapshot_exclude_tables',$exclude_tables);

        $snapshot_tables=array();

        foreach ($tables as $table)
        {
            if (0 !== stripos($table->Name, $wpdb->prefix))
            {
                continue;
            }
            if (empty($table->Engine))
            {
                continue;
            }

            if(in_array($table->Name,$exclude_tables))
            {
                continue;
            }

            $snapshot_table['Name']=$table->Name;
            $snapshot_table['finished']=0;
            $snapshot_tables[$table->Name]=$snapshot_table;
        }

        if (!empty($snapshot_tables))
        {
            foreach ($snapshot_tables as $table_name=>$snapshot_table)
            {
                $new_table=$this->str_replace_first($wpdb->prefix,$snapshot_id,$table_name);

                $wpdb->query("OPTIMIZE TABLE {$table_name}");
                $wpdb->query("CREATE TABLE `{$new_table}` LIKE `{$table_name}`");
                $wpdb->query("INSERT `{$new_table}` SELECT * FROM `{$table_name}`");
            }
        }
        else
        {
            $ret['result']='failed';
            $ret['error']='create snapshot failed';
            return $ret;
        }

        $this->update_snapshot($snapshot_id,'merge',$comment);
        $ret['result']='success';
        $ret['finished']=1;
        $ret['snapshot_id']=$snapshot_id;

        return $ret;
    }

    public function check_dev_snapshot()
    {
        $snapshots=$this->options->get_option('wpvivid_snapshot');
        $dev_snapshots=array();
        foreach ($snapshots as $snapshot)
        {
            if($snapshot['type']=='dev')
            {
                $dev_snapshots[]=$snapshot;
            }
        }

        $setting=$this->options->get_option('wpvivid_merge_setting');
        $count=isset($setting['snapshot_retention'])?$setting['snapshot_retention']:6;
        if(empty($count))
        {
            $count=6;
        }

        while(count($dev_snapshots)>=$count)
        {
            usort($dev_snapshots, function ($a, $b)
            {
                if ($a['time'] == $b['time'])
                    return 0;

                if ($a['time'] > $b['time'])
                    return 1;
                else
                    return -1;
            });

            $dev_snapshot=array_shift($dev_snapshots);
            $this->remove_snapshot($dev_snapshot['id']);
        }
    }

    public function create_dev_snapshot($task_data)
    {
        global $wpdb;

        $snapshot_id = 'wp'.$this->create_snapshot_uid();
        $tables = $wpdb->get_results('SHOW TABLE STATUS');
        //$exclude_tables[]=$wpdb->prefix.'wpvivid_log';
        //$exclude_tables[]=$wpdb->prefix.'wpvivid_merge_db';
        $exclude_tables[]=$wpdb->prefix.'wpvivid_options';
        $exclude_tables[]=$wpdb->base_prefix."wpvivid_record_task";
        $exclude_tables=apply_filters('wpvivid_create_snapshot_exclude_tables',$exclude_tables);

        $start_time=time();
        $snapshot_tables=array();

        foreach ($tables as $table)
        {
            if (0 !== stripos($table->Name, $wpdb->prefix))
            {
                continue;
            }
            if (empty($table->Engine))
            {
                continue;
            }

            if(in_array($table->Name,$exclude_tables))
            {
                continue;
            }

            $snapshot_table['Name']=$table->Name;
            $snapshot_table['finished']=0;
            $snapshot_tables[$table->Name]=$snapshot_table;
        }

        $this->init_task($snapshot_id,$snapshot_tables,'dev',$task_data['comment']);

        if (!empty($snapshot_tables))
        {
            foreach ($snapshot_tables as $table_name=>$snapshot_table)
            {
                if($snapshot_table['finished']==1)
                    continue;

                $new_table=$this->str_replace_first($wpdb->prefix,$snapshot_id,$table_name);

                $wpdb->query("OPTIMIZE TABLE {$table_name}");
                $wpdb->query("CREATE TABLE `{$new_table}` LIKE `{$table_name}`");
                $wpdb->query("INSERT `{$new_table}` SELECT * FROM `{$table_name}`");

                $snapshot_tables[$table_name]['finished']=1;
                $this->update_task($snapshot_id,$snapshot_tables);

                if($this->is_time_limit_exceeded($start_time))
                {
                    $ret['result']='success';
                    $ret['finished']=0;
                    $ret['snapshot_id']=$snapshot_id;
                    $ret['snapshot_tables']=$snapshot_tables;
                    return $ret;
                }
            }
        }
        else
        {
            $ret['result']='failed';
            $ret['error']='create snapshot failed';
            return $ret;
        }

        $this->update_snapshot($snapshot_id,'dev',$task_data['comment']);
        $ret['result']='success';
        $ret['finished']=1;
        $ret['snapshot_id']=$snapshot_id;

        return $ret;
    }

    public function create_snapshot($type='',$comment='')
    {
        global $wpdb;

        $snapshot_id=$this->create_snapshot_uid();
        $snapshot_id = 'wp'.$snapshot_id;

        $tables = $wpdb->get_results('SHOW TABLE STATUS');

        $exclude_tables[]=$wpdb->prefix.'wpvivid_log';
        $exclude_tables[]=$wpdb->base_prefix."wpvivid_increment_big_ids";
        $exclude_tables[]=$wpdb->prefix.'wpvivid_merge_db';
        $exclude_tables[]=$wpdb->prefix.'wpvivid_options';
        $exclude_tables[]=$wpdb->base_prefix."wpvivid_record_task";
        $exclude_tables=apply_filters('wpvivid_create_snapshot_exclude_tables',$exclude_tables);

        $start_time=time();
        $snapshot_tables=array();

        foreach ($tables as $table)
        {
            if (0 !== stripos($table->Name, $wpdb->prefix))
            {
                continue;
            }
            if (empty($table->Engine))
            {
                continue;
            }

            if(in_array($table->Name,$exclude_tables))
            {
                continue;
            }

            $snapshot_table['Name']=$table->Name;
            $snapshot_table['finished']=0;
            $snapshot_tables[$table->Name]=$snapshot_table;
        }

        if(empty($type))
        {
            $type='manual';
        }

        $this->init_task($snapshot_id,$snapshot_tables,$type,$comment);

        if (!empty($snapshot_tables))
        {
            foreach ($snapshot_tables as $table_name=>$snapshot_table)
            {
                if($snapshot_table['finished']==1)
                    continue;

                $new_table=$this->str_replace_first($wpdb->prefix,$snapshot_id,$table_name);

                $wpdb->query("OPTIMIZE TABLE {$table_name}");
                $wpdb->query("DROP TABLE IF EXISTS `{$new_table}`");
                $wpdb->query("CREATE TABLE `{$new_table}` LIKE `{$table_name}`");
                $wpdb->query("INSERT `{$new_table}` SELECT * FROM `{$table_name}`");

                $snapshot_tables[$table_name]['finished']=1;
                $this->update_task($snapshot_id,$snapshot_tables);

                if($this->is_time_limit_exceeded($start_time))
                {
                    $ret['result']='success';
                    $ret['finished']=0;
                    $ret['snapshot_id']=$snapshot_id;
                    $ret['snapshot_tables']=$snapshot_tables;
                    return $ret;
                }
            }
        }
        else
        {
            $ret['result']='failed';
            $ret['error']='Creating the snapshot failed.';
            return $ret;
        }

        $this->update_task($snapshot_id,$snapshot_tables);
        $this->update_snapshot($snapshot_id,$type,$comment);
        $ret['result']='success';
        $ret['finished']=1;
        $ret['snapshot_id']=$snapshot_id;
        return $ret;
    }

    public function resume_create_snapshot()
    {
        global $wpdb;

        $start_time=time();

        $ret=$this->get_task_data();
        if($ret['result']=='failed')
        {
            return $ret;
        }

        $snapshot_id=$ret['snapshot_id'];
        $snapshot_tables=$ret['snapshot_tables'];
        $type=$ret['type'];
        $comment=$ret['comment'];

        if (!empty($snapshot_tables))
        {
            foreach ($snapshot_tables as $table_name=>$snapshot_table)
            {
                if($snapshot_table['finished']==1)
                    continue;

                $new_table=$this->str_replace_first($wpdb->prefix,$snapshot_id,$table_name);

                $wpdb->query("OPTIMIZE TABLE {$table_name}");
                $wpdb->query("DROP TABLE IF EXISTS `{$new_table}`");
                $wpdb->query("CREATE TABLE `{$new_table}` LIKE `{$table_name}`");
                $wpdb->query("INSERT `{$new_table}` SELECT * FROM `{$table_name}`");

                $snapshot_tables[$table_name]['finished']=1;
                $this->update_task($snapshot_id,$snapshot_tables);
                if($this->is_time_limit_exceeded($start_time))
                {
                    $ret['result']='success';
                    $ret['finished']=0;
                    $ret['snapshot_id']=$snapshot_id;
                    $ret['snapshot_tables']=$snapshot_tables;
                    $this->update_task($snapshot_id,$snapshot_tables);
                    return $ret;
                }
            }
        }
        else
        {
            $ret['result']='failed';
            $ret['error']='Creating the snapshot failed.';
            return $ret;
        }

        $this->update_task($snapshot_id,$snapshot_tables);
        $this->update_snapshot($snapshot_id,$type,$comment);
        $ret['result']='success';
        $ret['finished']=1;
        $ret['snapshot_id']=$snapshot_id;

        return $ret;
    }

    public function init_task($snapshot_id,$snapshot_tables,$type,$comment)
    {
        $snapshot_task=$this->options->get_option('wpvivid_snapshot_task');
        if(empty($snapshot_task))
        {
            $snapshot_task=array();
        }

        $snapshot_task['snapshot_id']=$snapshot_id;
        $snapshot_task['snapshot_tables']=$snapshot_tables;
        $snapshot_task['type']=$type;
        $snapshot_task['comment']=$comment;

        $this->options->update_option('wpvivid_snapshot_task',$snapshot_task);
    }

    public function get_progress()
    {
        $snapshot_task=$this->options->get_option('wpvivid_snapshot_task');
        if(empty($snapshot_task))
        {
            $progress['main_percent']='0%';
            $progress['doing']="Creating a snapshot.";
        }

        $snapshot_tables=$snapshot_task['snapshot_tables'];
        $i_sum=count($snapshot_tables);
        $i_finished=0;
        $b_finished=true;
        foreach ($snapshot_tables as $table_name=>$snapshot_table)
        {
            if($snapshot_table['finished']==1)
            {
                $i_finished++;
            }
            else
            {
                $b_finished=false;
            }
        }

        $i_progress=intval(($i_finished/$i_sum)*100);
        $progress['main_percent']=$i_progress.'%';
        if($b_finished)
        {
            $progress['doing']="Create snapshot completed.";
        }
        else
        {
            $progress['doing']="Creating a snapshot.";
        }

        return $progress;
    }

    public function update_task($snapshot_id,$snapshot_tables)
    {
        $snapshot_task=$this->options->get_option('wpvivid_snapshot_task');
        if(empty($snapshot_task))
        {
            $snapshot_task=array();
        }

        $snapshot_task['snapshot_id']=$snapshot_id;
        $snapshot_task['snapshot_tables']=$snapshot_tables;

        $this->options->update_option('wpvivid_snapshot_task',$snapshot_task);
    }

    public function get_task_data()
    {
        $snapshot_task=$this->options->get_option('wpvivid_snapshot_task');
        if(empty($snapshot_task))
        {
            $ret['result']='failed';
            $ret['error']='Creating snapshot task not found.';
            return $ret;
        }

        if(empty($snapshot_task['snapshot_id'])||empty($snapshot_task['snapshot_tables']))
        {
            $ret['result']='failed';
            $ret['error']='Creating snapshot task not found.';
            return $ret;
        }

        $ret['result']='success';
        $ret['snapshot_id']=$snapshot_task['snapshot_id'];
        $ret['snapshot_tables']=$snapshot_task['snapshot_tables'];
        $ret['type']=$snapshot_task['type'];
        $ret['comment']=$snapshot_task['comment'];
        return $ret;
    }

    public function get_snapshots($type='')
    {
        $snapshot_data=$this->options->get_option('wpvivid_snapshot');
        if(empty($snapshot_data))
        {
            $snapshot_data=array();
        }

        if(empty($type))
        {
            return $snapshot_data;
        }
        else
        {
            $get_snapshot_data=array();
            foreach ($snapshot_data as $data)
            {
                if($data['type']==$type)
                {
                    $get_snapshot_data[]=$data;
                }
            }
            return $snapshot_data;
        }
    }

    public function restore_snapshot($snapshot_id)
    {
        global $wpdb;

        $tables = $wpdb->get_results('SHOW TABLE STATUS');

        //$exclude_tables[]=$wpdb->prefix.'wpvivid_log';
        //$exclude_tables[]=$wpdb->base_prefix."wpvivid_increment_big_ids";
        //$exclude_tables[]=$wpdb->prefix.'wpvivid_merge_db';
        $exclude_tables[]=$wpdb->prefix.'wpvivid_options';
        $exclude_tables=apply_filters('wpvivid_create_snapshot_exclude_tables',$exclude_tables);

        add_filter('wpvivid_merge_query_lock', array($this,'query_lock'), 9999);

        $new_tables = array();
        if (is_array($tables))
        {
            foreach ($tables as $table)
            {
                if (0 !== stripos($table->Name, $snapshot_id))
                {
                    continue;
                }

                if (empty($table->Engine))
                {
                    continue;
                }

                if(in_array($table->Name,$exclude_tables))
                {
                    continue;
                }

                $new_table['Name']=$table->Name;
                $new_table['finished']=0;
                $new_tables[$table->Name] =$new_table;
            }
        }
        else
        {
            $ret['result']='failed';
            $ret['error']='Failed to retrieve list of database tables.';
            return $ret;
        }

        $this->init_restore_task($snapshot_id,$new_tables);

        /*
        foreach ($tables as $table)
        {
            if (0 !== stripos($table->Name, $wpdb->prefix))
            {
                continue;
            }
            if (empty($table->Engine))
            {
                continue;
            }

            if(in_array($table->Name,$exclude_tables))
            {
                continue;
            }

            $wpdb->query('DROP TABLE ' . $table->Name);
        }*/

        foreach ($new_tables as $table_name=>$table)
        {
            $new_table=$this->str_replace_first($snapshot_id,$wpdb->prefix,$table_name);

            $wpdb->query('DROP TABLE ' . $new_table);
            $wpdb->query("CREATE TABLE `{$new_table}` LIKE `{$table_name}`");
            $wpdb->query("INSERT `{$new_table}` SELECT * FROM `{$table_name}`");
            $new_tables[$table_name]['finished']=1;
            $this->update_restore_task($snapshot_id,$new_tables);
        }

        $ret['result']='success';
        return $ret;
    }

    public function init_restore_task($snapshot_id,$snapshot_tables)
    {
        $snapshot_task=$this->options->get_option('wpvivid_restore_snapshot_task');
        if(empty($snapshot_task))
        {
            $snapshot_task=array();
        }

        $snapshot_task['snapshot_id']=$snapshot_id;
        $snapshot_task['snapshot_tables']=$snapshot_tables;

        $this->options->update_option('wpvivid_restore_snapshot_task',$snapshot_task);
    }

    public function update_restore_task($snapshot_id,$snapshot_tables)
    {
        $snapshot_task=$this->options->get_option('wpvivid_restore_snapshot_task');
        if(empty($snapshot_task))
        {
            $snapshot_task=array();
        }

        $snapshot_task['snapshot_id']=$snapshot_id;
        $snapshot_task['snapshot_tables']=$snapshot_tables;

        $this->options->update_option('wpvivid_restore_snapshot_task',$snapshot_task);
    }

    public function get_restore_task_data()
    {
        $snapshot_task=$this->options->get_option('wpvivid_restore_snapshot_task');
        if(empty($snapshot_task))
        {
            $ret['result']='failed';
            $ret['error']='Restoring snapshot task not found.';
            return $ret;
        }

        if(empty($snapshot_task['snapshot_id'])||empty($snapshot_task['snapshot_tables']))
        {
            $ret['result']='failed';
            $ret['error']='Restoring snapshot task not found.';
            return $ret;
        }

        $ret['result']='success';
        $ret['snapshot_id']=$snapshot_task['snapshot_id'];
        $ret['snapshot_tables']=$snapshot_task['snapshot_tables'];
        return $ret;
    }

    public function remove_snapshot($snapshot_id)
    {
        global $wpdb;

        $tables = $wpdb->get_col($wpdb->prepare('SHOW TABLES LIKE %s', array($snapshot_id . '%')));
        foreach ($tables as $table)
        {
            $wpdb->query('DROP TABLE IF EXISTS `' . $table.'`');
        }

        $snapshot_data=$this->options->get_option('wpvivid_snapshot');
        unset($snapshot_data[$snapshot_id]);
        $this->options->update_option('wpvivid_snapshot',$snapshot_data);

        $ret['result']='success';
        return $ret;
    }

    public function str_replace_first($from, $to, $content)
    {
        $from = '/'.preg_quote($from, '/').'/';

        return preg_replace($from, $to, $content, 1);
    }

    public function create_snapshot_uid()
    {
        global $wpdb;
        $count = 0;

        do
        {
            $count++;
            $uid = sprintf('%06x', wp_rand(0, 0xFFFFFF));

            $verify_db = $wpdb->get_col($wpdb->prepare('SHOW TABLES LIKE %s', array('%' . $uid . '%')));

        } while (!empty($verify_db) && $count < 10);

        if ($count == 10)
        {
            $uid = false;
        }

        return $uid;
    }

    public function update_snapshot($snapshot_id,$type,$comment)
    {
        $snapshot_data=$this->options->get_option('wpvivid_snapshot');
        if(empty($snapshot_data))
        {
            $snapshot_data=array();
        }

        $snapshot_data[$snapshot_id]['id']=$snapshot_id;
        $snapshot_data[$snapshot_id]['type']=$type;
        $snapshot_data[$snapshot_id]['time']=time();
        $snapshot_data[$snapshot_id]['comment']=$comment;

        $this->options->update_option('wpvivid_snapshot',$snapshot_data);
    }

    public function is_time_limit_exceeded($start_time)
    {
        $time_limit =20;
        $time_taken = microtime(true) -$start_time;
        if($time_taken >= $time_limit)
        {
            return true;
        }

        return false;
    }

    public function get_dev_progress()
    {
        $snapshot_task=$this->options->get_option('wpvivid_snapshot_task');
        if(empty($snapshot_task))
        {
            $progress['main_percent']='0%';
            $progress['text']="creating snapshot for dev site.";
        }

        $snapshot_tables=$snapshot_task['snapshot_tables'];
        $i_sum=count($snapshot_tables);
        $i_finished=0;
        $b_finished=true;
        foreach ($snapshot_tables as $table_name=>$snapshot_table)
        {
            if($snapshot_table['finished']==1)
            {
                $i_finished++;
            }
            else
            {
                $b_finished=false;
            }
        }

        $i_progress=intval(($i_finished/$i_sum)*100);
        $progress['main_percent']=$i_progress.'%';
        if($b_finished)
        {
            $progress['text']="creating snapshot for dev site completed.";
        }
        else
        {
            $progress['text']="creating snapshot for dev site - $i_finished/$i_sum tables";
        }

        return $progress;
    }

    public function query_lock($lock)
    {
        return true;
    }
}includes/snapshot/class-wpvivid-snapshot.php000064400000216104151327705670015373 0ustar00<?php

if (!defined('WPVIVID_PLUGIN_DIR'))
{
    die;
}

class WPvivid_Snapshot_Ex
{
    public $options;
    public $main_tab;

    public function __construct()
    {
        include_once WPVIVID_PLUGIN_DIR . '/includes/snapshot/class-wpvivid-snapshot-function.php';
        include_once WPVIVID_PLUGIN_DIR . '/includes/snapshot/class-wpvivid-snapshot-options.php';
        if(is_admin())
        {
            include_once WPVIVID_PLUGIN_DIR . '/includes/snapshot/class-wpvivid-snapshots-list.php';

            add_filter('wpvivid_get_dashboard_menu', array($this, 'get_dashboard_menu'), 20, 2);
            add_filter('wpvivid_get_dashboard_screens', array($this, 'get_dashboard_screens'), 20);

            add_filter('wpvivid_snapshot_get_main_admin_menus',array($this,'get_main_admin_menus'),9999);

            $this->options=new WPvivid_Snapshot_Option_Ex();

            /*
            if (is_multisite())
            {
                add_action('network_admin_menu',array( $this,'add_admin_menu'));
            }
            else
            {
                add_action('admin_menu',array( $this,'add_admin_menu'));
            }
            add_filter('wpvivid_snapshot_get_screen_ids', array($this,'get_screen_ids'), 9999);
            */

            add_filter('wpvivid_get_admin_menus',array($this,'get_admin_menus'),22);
            add_filter('wpvivid_get_screen_ids',array($this,'get_screen_ids'),12);

            add_action('admin_enqueue_scripts', array($this, 'enqueue_styles'), 11);
            add_action('admin_enqueue_scripts', array($this, 'enqueue_scripts'), 11);


            add_action('wp_ajax_wpvivid_create_snapshot',array( $this,'create_snapshot'));
            add_action('wp_ajax_wpvivid_get_snapshot_progress',array( $this,'get_snapshot_progress'));
            //
            add_action('wp_ajax_wpvivid_resume_create_snapshot',array( $this,'resume_create_snapshot'));
            add_action('wp_ajax_wpvivid_restore_snapshot',array( $this,'restore_snapshot'));
            add_action('wp_ajax_wpvivid_get_restore_snapshot_status',array( $this,'get_restore_snapshot_status'));
            add_action('wp_ajax_wpvivid_delete_snapshot',array( $this,'delete_snapshot'));

            add_filter('wpvivid_check_create_snapshot',array($this,'check_create_snapshot'));
            add_action('wpvivid_create_snapshot',array($this,'create_snapshot_ex'),10,1);

            add_action('wp_ajax_wpvivid_set_snapshot_setting',array( $this,'set_setting'));
            //
            add_action('wpvivid_snapshot_add_sidebar',array( $this,'add_sidebar'));
            add_action('wpvivid_snapshot_add_sidebar_free', array( $this, 'add_sidebar_free' ));

            $snapshot_setting=$this->options->get_option('wpvivid_snapshot_setting');

            $quick_snapshot=isset($snapshot_setting['quick_snapshot'])?$snapshot_setting['quick_snapshot']:false;

            if($quick_snapshot)
            {
                add_action('admin_bar_menu',array( $this,'add_toolbar_items'),100);
                add_action('admin_footer',array( $this,'quick_snapshot'));
            }
        }

    }

    public function get_admin_menus($submenus)
    {
        $submenu['parent_slug']=apply_filters('wpvivid_white_label_slug', WPVIVID_PLUGIN_SLUG);
        $submenu['page_title']= apply_filters('wpvivid_white_label_display', 'WPvivid Backup');
        $submenu['menu_title']=__('Database Snapshots', 'wpvivid-backuprestore');
        $submenu['capability']='administrator';
        $submenu['menu_slug']=strtolower(sprintf('%s-snapshot-ex', apply_filters('wpvivid_white_label_slug', 'wpvivid')));
        $submenu['index']=2;
        $submenu['function']=array($this, 'init_page');
        $submenus[$submenu['menu_slug']]=$submenu;
        return $submenus;
    }

    public function get_screen_ids($screen_ids)
    {
        $screen_ids[]=apply_filters('wpvivid_white_label_screen_id', 'wpvivid-backup_page_wpvivid-snapshot-ex');
        return $screen_ids;
    }

    public function add_toolbar_items($wp_admin_bar)
    {
        $wp_admin_bar->add_menu(array(
            'id' => 'wpvivid_snapshot_admin_menu',
            'title' => '<span class="dashicons-camera-alt ab-icon"></span>'.'Quick Snapshot',
            'meta' =>array(
                'class' => 'wpvivid-quick-create-snapshot',
            )
        ));
    }

    public function add_admin_menu()
    {
        $page_title=apply_filters('wpvivid_white_label_display', 'WPvivid Snapshot');
        $menu_title=apply_filters('wpvivid_white_label_display', 'WPvivid Snapshot');

        $capability = 'administrator';

        $menu_slug ='wpvivid-snapshot';

        $function=array($this, 'init_page');
        $icon_url='dashicons-camera-alt';
        $position=100;

        $menu['page_title']= $page_title;
        $menu['menu_title']= $menu_title;
        $menu['capability']='administrator';
        $menu['menu_slug']=$menu_slug;
        $menu['function']=array($this, 'init_page');
        $menu['icon_url']=$icon_url;
        $menu['position']=100;

        $menu=apply_filters('wpvivid_snapshot_get_main_admin_menus', $menu);

        if($menu!=false)
            add_menu_page( $page_title, $menu_title, $capability, $menu_slug, $function, $icon_url, $position);
    }

    public function get_dashboard_menu($submenus,$parent_slug)
    {
        $display = apply_filters('wpvivid_get_menu_capability_addon', 'menu_database_snapshot');
        if($display)
        {
            $submenu['menu_slug'] = strtolower(sprintf('%s-snapshot', apply_filters('wpvivid_white_label_slug', 'wpvivid')));
            if(isset($submenus[$submenu['menu_slug']]))
            {
                unset($submenus[$submenu['menu_slug']]);
            }
            $submenu['parent_slug'] = $parent_slug;
            $submenu['page_title'] = apply_filters('wpvivid_white_label_display', 'Database Snapshots');
            $submenu['menu_title'] = 'Database Snapshots';
            $submenu['capability'] = 'administrator';
            $submenu['index'] = 11;//10;
            $submenu['function'] = array($this, 'init_page_pro');
            $submenus[$submenu['menu_slug']] = $submenu;
        }

        return $submenus;
    }

    public function get_dashboard_screens($screens)
    {
        $screen['menu_slug']='wpvivid-snapshot';
        $screen['screen_id']='wpvivid-plugin_page_wpvivid-snapshot';
        $screen['is_top']=false;
        $screens[]=$screen;
        return $screens;
    }

    public function get_main_admin_menus($menu)
    {
        if(class_exists('WPvivid_backup_pro'))
            return false;
        else
            return $menu;
    }

    /*
    public function get_screen_ids($screen_ids)
    {
        $screen_ids=array();
        $screen['menu_slug']='wpvivid-snapshot';
        $screen['screen_id']='toplevel_page_wpvivid-snapshot';
        $screen['is_top']=true;
        $screens[]=$screen;

        foreach ($screens as $screen)
        {
            $screen_ids[]=$screen['screen_id'];
            if(is_multisite())
            {
                if(substr($screen['screen_id'],-8)=='-network')
                    continue;
                $screen_ids[]=$screen['screen_id'].'-network';
            }
            else
            {
                $screen_ids[]=$screen['screen_id'];
            }
        }
        return $screen_ids;
    }
    */

    public function enqueue_styles()
    {
        $screen_ids=array();
        $screen_ids=apply_filters('wpvivid_get_screen_ids',$screen_ids);
        if(in_array(get_current_screen()->id,$screen_ids))
        {
            wp_enqueue_style('wpvivid_snapshot_ex', WPVIVID_PLUGIN_DIR_URL . 'css/wpvivid-snapshot-style.css', array(), WPVIVID_PLUGIN_VERSION, 'all');
        }
    }

    public function enqueue_scripts()
    {

        $snapshot_setting=$this->options->get_option('wpvivid_snapshot_setting');

        $quick_snapshot=isset($snapshot_setting['quick_snapshot'])?$snapshot_setting['quick_snapshot']:false;

        if($quick_snapshot)
        {
            wp_enqueue_style('wpvivid_quick_snapshot_ex', WPVIVID_PLUGIN_DIR_URL . 'css/wpvivid-quick-snapshot-style.css', array(), WPVIVID_PLUGIN_VERSION, 'all');
            wp_enqueue_style (  'wp-jquery-ui-dialog');
            wp_enqueue_script( 'jquery-ui-dialog' );
            wp_enqueue_script (  'wpvivid_qucick_snapshot_ex_js' ,       // handle
                WPVIVID_PLUGIN_DIR_URL . 'js/wpvivid-quick-snapshot.js'  ,       // source
                array('jquery-ui-dialog'),
                WPVIVID_PLUGIN_VERSION, false
            );
            wp_localize_script('wpvivid_qucick_snapshot_ex_js', 'wpvivid_quick_snapshot_ajax_object', array('ajax_url' => admin_url('admin-ajax.php'),'ajax_nonce'=>wp_create_nonce('wpvivid_ajax')));
        }
    }

    public function added_quick_snapshot($added)
    {
        return true;
    }

    public function quick_snapshot()
    {
        if(apply_filters('wpvivid_added_quick_snapshot',false))
        {
            return;
        }
        add_filter('wpvivid_added_quick_snapshot',array( $this,'added_quick_snapshot'));

        $current_url = $_SERVER['REQUEST_URI'];
        if (preg_match('/post.php?/', $current_url))
        {
            return;
        }
        ?>
        <div id="wpvivid_quick_snapshot_dialog">
            <span id="wpvivid_quick_snapshot_close" class="dashicons dashicons-no" style="float:right;cursor: pointer"></span>
            <div id="wpvivid_quick_snapshot_message_box" style="padding:20px 0;">
                <p style="text-align:center;font-size:24px;">
                    <span id="wpvivid_quick_snapshot_message">Are you sure you want to create a snapshot now?</span>
                    <span id="wpvivid_quick_snapshot_loading"><img src="<?php echo esc_url(admin_url()).'/images/loading.gif'; ?>"></span>
                </p>
                <p style="text-align:center;" id="wpvivid_quick_create_snapshot_comment_box">
                    <span class="dashicons dashicons-welcome-write-blog wpvivid-dashicons-green" style="margin-top:0.2em;"></span>
                    <span><strong>Comment the snapshot</strong>(optional): </span>
                    <input  id="wpvivid_quick_create_snapshot_comment" type="text" placeholder="e.g. mysnapshot">
                </p>
            </div>
            <div id="wpvivid_quick_snapshot_progress" style="display: none">
                <p>
                    <span class="wpvivid-span-progress">
                        <span class="wpvivid-span-processed-progress">0% completed</span>
                    </span>
                </p>
                <p><span>Action: </span><span></span><span></span></p>
            </div>
            <div style="padding:0 0 10px 0">
                <p style="text-align:center;">
                    <input class="button-primary"  style="width: 150px; height: 40px; font-size: 16px; margin-bottom: 10px; pointer-events: auto; opacity: 1;" id="wpvivid_quick_create_snapshot" type="submit" value="Create Now">
                </p>
            </div>
        </div>
        <script>
            var b_quick_end_create_progress=false;
            var b_quick_need_update=false;
            jQuery('.wpvivid-quick-create-snapshot').click(function()
            {
                jQuery("#wpvivid_quick_snapshot_message_box").show();
                //
                jQuery("#wpvivid_quick_snapshot_loading").hide();
                jQuery("#wpvivid_quick_create_snapshot_comment_box").show();
                jQuery("#wpvivid_quick_snapshot_progress").hide();
                //
                jQuery("#wpvivid_quick_snapshot_message").html("Are you sure you want to create a snapshot now?");
                jQuery("#wpvivid_quick_snapshot_dialog").dialog("widget").find(".ui-dialog-titlebar").hide();
                jQuery("#wpvivid_quick_snapshot_dialog").dialog("open");

                return false;
            });

            //wpvivid_quick_snapshot_close
            jQuery('#wpvivid_quick_snapshot_close').click(function()
            {
                jQuery("#wpvivid_quick_snapshot_dialog").dialog('close');
            });

            jQuery('#wpvivid_quick_create_snapshot').click(function()
            {
                wpvivid_quick_create_snapshot();
            });

            function wpvivid_quick_simulate_create_progress()
            {
                var MaxProgess = 30,
                    currentProgess = 0,
                    steps = 1,
                    time_steps=1000;

                var timer = setInterval(function ()
                {
                    if(currentProgess>100)
                    {
                        currentProgess=100;
                    }
                    else
                    {
                        currentProgess += steps;
                    }

                    if(b_quick_end_create_progress)
                    {
                        clearInterval(timer);
                        return;
                    }
                    var progress_html='<p><span class="wpvivid-span-progress">' +
                        '<span class="wpvivid-span-processed-progress wpvivid-span-processed-percent-progress" style="width: '+currentProgess+'%">' +
                        currentProgess+'% completed</span></span></p><p>' +
                        '<span class="dashicons dashicons-welcome-write-blog wpvivid-dashicons-grey"></span><span>Action:</span>' +
                        '<span>Creating the snapshot.</span></p>';

                    jQuery("#wpvivid_quick_snapshot_progress").html(progress_html);
                    if (currentProgess >= MaxProgess)
                    {
                        clearInterval(timer);
                    }
                }, time_steps);
            }

            function wpvivid_quick_create_snapshot()
            {
                var comment=jQuery('#wpvivid_quick_create_snapshot_comment').val();
                var ajax_data= {
                    'action': 'wpvivid_create_snapshot',
                    'comment':comment,
                };
                var default_progress='<p><span class="wpvivid-span-progress"><span class="wpvivid-span-processed-progress wpvivid-span-processed-percent-progress" style="width: 0%">0% completed</span></span></p><p><span class="dashicons dashicons-welcome-write-blog wpvivid-dashicons-grey"></span><span>Action:</span><span>Creating a snapshot.</span></p>';
                jQuery('#wpvivid_quick_snapshot_progress').show();
                jQuery('#wpvivid_quick_snapshot_progress').html(default_progress);
                jQuery("#wpvivid_quick_snapshot_loading").show();
                jQuery("#wpvivid_quick_create_snapshot_comment_box").hide();
                jQuery("#wpvivid_quick_snapshot_message").html("Creating the snapshot...");
                b_quick_need_update=true;
                b_quick_end_create_progress=false;
                wpvivid_quick_simulate_create_progress();

                setTimeout(function(){
                    wpvivid_quick_get_snapshot_progress();
                }, 3000);

                jQuery('#wpvivid_quick_create_snapshot').css({'pointer-events': 'none', 'opacity': '0.4'});
                wpvivid_post_request_quick(ajax_data, function(data)
                {
                    b_quick_end_create_progress=true;
                    var jsonarray = jQuery.parseJSON(data);
                    if (jsonarray.result === 'success')
                    {
                        jQuery('#wpvivid_quick_snapshot_progress').html(jsonarray.progress);
                        if(jsonarray.finished==1)
                        {
                            jQuery("#wpvivid_quick_snapshot_dialog").dialog('close');
                            b_quick_need_update=false;
                            jQuery('#wpvivid_quick_snapshot_progress').hide();
                            jQuery('#wpvivid_quick_create_snapshot').css({'pointer-events': 'auto', 'opacity': '1'});

                            jQuery("#wpvivid_quick_snapshot_loading").hide();
                            jQuery("#wpvivid_quick_create_snapshot_comment_box").show();
                            jQuery("#wpvivid_quick_snapshot_message").html("Are you sure you want to create a snapshot now?");

                            alert("Creating a snapshot completed successfully.");
                        }
                        else
                        {
                            wpvivid_quick_resume_create_snapshot();
                        }
                    }
                    else
                    {
                        alert(jsonarray.error);
                        b_quick_need_update=false;
                        jQuery('#wpvivid_quick_snapshot_progress').hide();
                        jQuery('#wpvivid_quick_create_snapshot').css({'pointer-events': 'auto', 'opacity': '1'});

                        jQuery("#wpvivid_quick_snapshot_loading").hide();
                        jQuery("#wpvivid_quick_create_snapshot_comment_box").show();
                        jQuery("#wpvivid_quick_snapshot_message").html("Are you sure you want to create a snapshot now?");
                    }
                }, function(XMLHttpRequest, textStatus, errorThrown)
                {
                    setTimeout(function(){
                        wpvivid_quick_resume_create_snapshot(0);
                    }, 15000);
                });
            }

            function wpvivid_quick_get_snapshot_progress()
            {
                var ajax_data= {
                    'action': 'wpvivid_get_snapshot_progress',
                };

                wpvivid_post_request_quick(ajax_data, function(data)
                {
                    var jsonarray = jQuery.parseJSON(data);
                    b_quick_end_create_progress=true;
                    jQuery('#wpvivid_quick_snapshot_progress').html(jsonarray.progress);

                    if(b_quick_need_update)
                    {
                        setTimeout(function(){
                            wpvivid_quick_get_snapshot_progress();
                        }, 1000);
                    }
                }, function(XMLHttpRequest, textStatus, errorThrown)
                {
                    if(b_quick_need_update)
                    {
                        setTimeout(function(){
                            wpvivid_quick_get_snapshot_progress();
                        }, 1000);
                    }
                });
            }

            function wpvivid_quick_resume_create_snapshot(resume)
            {
                if(resume>6)
                {
                    alert('Creating the snapshot timed out.');
                    b_quick_need_update=false;
                    jQuery("#wpvivid_quick_snapshot_message_box").show();
                    jQuery('#wpvivid_quick_snapshot_progress').hide();
                    jQuery('#wpvivid_quick_create_snapshot').css({'pointer-events': 'auto', 'opacity': '1'});

                    jQuery("#wpvivid_quick_snapshot_loading").hide();
                    jQuery("#wpvivid_quick_create_snapshot_comment_box").show();
                    jQuery("#wpvivid_quick_snapshot_message").html("Are you sure you want to create a snapshot now?");
                    return;
                }
                var ajax_data= {
                    'action': 'wpvivid_resume_create_snapshot'
                };

                wpvivid_post_request_quick(ajax_data, function(data)
                {
                    try {
                        var jsonarray = jQuery.parseJSON(data);
                        if (jsonarray.result === 'success')
                        {
                            jQuery('#wpvivid_quick_snapshot_progress').html(jsonarray.progress);

                            if(jsonarray.finished==1)
                            {
                                b_quick_need_update=false;
                                jQuery("#wpvivid_quick_snapshot_dialog").dialog('close');

                                jQuery("#wpvivid_quick_snapshot_message_box").show();
                                jQuery('#wpvivid_quick_snapshot_progress').hide();
                                jQuery('#wpvivid_quick_create_snapshot').css({'pointer-events': 'auto', 'opacity': '1'});

                                jQuery("#wpvivid_quick_snapshot_loading").hide();
                                jQuery("#wpvivid_quick_create_snapshot_comment_box").show();
                                jQuery("#wpvivid_quick_snapshot_message").html("Are you sure you want to create a snapshot now?");
                                alert("Creating a snapshot completed successfully.");
                            }
                            else
                            {
                                wpvivid_quick_resume_create_snapshot();
                            }
                        }
                        else
                        {
                            b_quick_need_update=false;
                            jQuery("#wpvivid_quick_snapshot_message_box").show();
                            alert(jsonarray.error);
                            jQuery('#wpvivid_quick_snapshot_progress').hide();
                            jQuery('#wpvivid_quick_create_snapshot').css({'pointer-events': 'auto', 'opacity': '1'});

                            jQuery("#wpvivid_quick_snapshot_loading").hide();
                            jQuery("#wpvivid_quick_create_snapshot_comment_box").show();
                            jQuery("#wpvivid_quick_snapshot_message").html("Are you sure you want to create a snapshot now?");
                        }
                    }
                    catch (e)
                    {
                        resume+=1;
                        setTimeout(function(){
                            wpvivid_quick_resume_create_snapshot(resume);
                        }, 15000);
                    }

                }, function(XMLHttpRequest, textStatus, errorThrown)
                {
                    resume+=1;
                    setTimeout(function(){
                        wpvivid_quick_resume_create_snapshot(resume);
                    }, 15000);
                });
            }

            jQuery(document).ready(function ()
            {
                jQuery(function($)
                {
                    jQuery("#wpvivid_quick_snapshot_dialog").dialog({
                        'dialogClass'   : 'noTitleStuff',
                        'modal'         : true,
                        'autoOpen'      : false,
                        'closeOnEscape' : true,
                        'width': '600px',
                        'minWidth' : "260px"
                    });
                });
            });
        </script>
        <?php
    }

    public function init_page()
    {
        $this->options->check_tables();
        ?>
        <div class="wrap" style="max-width:1720px;">
            <h1><?php echo esc_html( apply_filters('wpvivid_white_label_display', 'WPvivid').' Plugins - Snapshots'); ?></h1>

            <?php
            if(!class_exists('WPvivid_Tab_Page_Container'))
                include_once WPVIVID_PLUGIN_DIR . '/includes/class-wpvivid-tab-page-container.php';

            $args['is_parent_tab']=1;
            $this->main_tab=new WPvivid_Tab_Page_Container();
            $this->main_tab->add_tab('Snapshots','snapshots',array($this, 'output_snapshots'), $args);
            $this->main_tab->add_tab('Setting','snapshots_setting',array($this, 'output_snapshots_setting'), $args);
            $this->main_tab->display();
            ?>

        </div>
        <?php
    }

    public function init_page_pro()
    {
        $this->options->check_tables();
        ?>
        <div class="wrap wpvivid-canvas">
            <div class="icon32"></div>
            <h1><?php echo esc_html( apply_filters('wpvivid_white_label_display', 'WPvivid').' Plugins - Snapshots' ); ?></h1>
            <div id="wpvivid_backup_notice"></div>
            <div id="poststuff">
                <div id="post-body" class="metabox-holder columns-2">
                    <div id="post-body-content">
                        <div class="meta-box-sortables ui-sortable">
                            <div class="wpvivid-backup">
                                <?php $this->welcome_bar();?>
                                <div class="wpvivid-canvas wpvivid-clear-float">
                                    <!---  backup progress --->
                                    <?php

                                    if(!class_exists('WPvivid_Tab_Page_Container_Ex'))
                                        include_once WPVIVID_PLUGIN_DIR . '/includes/snapshot/class-wpvivid-tab-page-container-ex.php';
                                    $this->main_tab=new WPvivid_Tab_Page_Container_Ex();

                                    $args['is_parent_tab']=0;
                                    $args['div_style']='padding-top:0;display:block;';
                                    $args['span_class']='dashicons dashicons-camera';
                                    $args['span_style']='color:#007cba; padding-right:0.5em;margin-top:0.2em;';
                                    //
                                    $tabs['merge']['title']='Snapshots';
                                    $tabs['merge']['slug']='snapshots';
                                    $tabs['merge']['callback']=array($this, 'output_snapshots');
                                    $tabs['merge']['args']=$args;

                                    $args['div_style']='padding-top:0;';
                                    $args['span_class']='dashicons  dashicons-admin-generic';
                                    $args['span_style']='color:grey;padding-right:0.5em;margin-top:0.1em;';
                                    $tabs['snapshot']['title']='Setting';
                                    $tabs['snapshot']['slug']='snapshots_setting';
                                    $tabs['snapshot']['callback']=array($this, 'output_snapshots_setting');
                                    $tabs['snapshot']['args']=$args;

                                    foreach ($tabs as $key=>$tab)
                                    {
                                        $this->main_tab->add_tab($tab['title'],$tab['slug'],$tab['callback'], $tab['args']);
                                    }

                                    $this->main_tab->display();
                                    ?>
                                </div>
                            </div>
                        </div>
                    </div>
                    <?php
                    do_action( 'wpvivid_snapshot_add_sidebar');
                    ?>
                </div>
            </div>
        </div>
        <?php
    }

    public function welcome_bar()
    {
        ?>
        <div class="wpvivid-welcome-bar wpvivid-clear-float">
            <div class="wpvivid-welcome-bar-left">
                <p><span class="dashicons dashicons-camera-alt wpvivid-dashicons-large wpvivid-dashicons-green"></span><span class="wpvivid-page-title">Database Snapshots</span></p>
                <p><span class="about-description">Create snapshots of the website database and restore the database from a snapshot.</span></p>
            </div>
            <div class="wpvivid-welcome-bar-right">
                <p></p>
                <div style="float:right;">
                    <span>Local Time:</span>
                    <span>
                        <a href="<?php echo esc_attr(apply_filters('wpvivid_get_admin_url', '').'options-general.php'); ?>">
                            <?php
                            $offset=get_option('gmt_offset');
                            echo esc_html(gmdate("l, F-d-Y H:i",time()+$offset*60*60));
                            ?>
                        </a>
                    </span>
                    <span class="dashicons dashicons-editor-help wpvivid-dashicons-editor-help wpvivid-tooltip">
                        <div class="wpvivid-left">
                            <p>Clicking the date and time will redirect you to the WordPress General Settings page where you can change your timezone settings.</p>
                            <i></i> <!-- do not delete this line -->
                        </div>
                    </span>
                </div>
            </div>
        </div>
        <?php
    }

    public function output_snapshots()
    {
        $snapshot=new WPvivid_Snapshot_Function_Ex();
        $snapshot_data=$snapshot->get_snapshots();

        ?>
        <div class="postbox quicksnapshot">
            <div id="wpvivid_snapshot_progress" style="display: none">
                <p>
                    <span class="wpvivid-span-progress">
                        <span class="wpvivid-span-processed-progress wpvivid-span-processed-percent-progress">0% completed</span>
                    </span>
                </p>
                <p><span>Action: </span><span></span><span class="wpvivid-animate-flicker"></span></p>
            </div>
            <div>
                <input class="button-primary" style="width: 200px; height: 50px; font-size: 20px; margin-bottom: 10px; pointer-events: auto; opacity: 1;" id="wpvivid_create_snapshot" type="submit" value="Create a snapshot">
            </div>
            <div>
                <p>
                    <span class="dashicons dashicons-welcome-write-blog wpvivid-dashicons-green" style="margin-top:0.2em;"></span>
                    <span><strong>Comment the snapshot</strong>(optional): </span>
                    <input id="wpvivid_create_snapshot_comment" type="text" placeholder="e.g. mysnapshot">
                </p>
            </div>

            <div id="wpvivid_snapshots_list">
                <?php
                $Snapshots_list = new WPvivid_Snapshots_List_Ex();
                $Snapshots_list->set_list($snapshot_data);
                $Snapshots_list->prepare_items();
                $Snapshots_list->display();
                ?>
            </div>
        </div>
        <script>
            var b_need_update=false;
            var b_restore_finished=false;
            var b_end_create_progress=false;
            jQuery('#wpvivid_create_snapshot').click(function()
            {
                wpvivid_create_snapshot();
            });

            function wpvivid_simulate_restore_progress()
            {
                var MaxProgess = 95,
                    currentProgess = 0,
                    steps = 1,
                    time_steps=1000;

                var timer = setInterval(function ()
                {
                    if(b_restore_finished)
                    {
                        currentProgess=100;
                    }
                    else
                    {
                        currentProgess += steps;
                    }


                    var progress_html='<p><span class="wpvivid-span-progress">' +
                        '<span class="wpvivid-span-processed-progress wpvivid-span-processed-percent-progress" style="width: '+currentProgess+'%">' +
                        currentProgess+'% completed</span></span></p><p>' +
                        '<span class="dashicons dashicons-welcome-write-blog wpvivid-dashicons-grey"></span><span>Action:</span>' +
                        '<span>Restoring the snapshot.</span></p>';

                    jQuery("#wpvivid_snapshot_progress").html(progress_html);
                    if (currentProgess >= MaxProgess)
                    {
                        clearInterval(timer);
                    }
                }, time_steps);
            }

            function wpvivid_simulate_create_progress()
            {
                var MaxProgess = 30,
                    currentProgess = 0,
                    steps = 1,
                    time_steps=1000;

                var timer = setInterval(function ()
                {
                    if(currentProgess>100)
                    {
                        currentProgess=100;
                    }
                    else
                    {
                        currentProgess += steps;
                    }

                    if(b_end_create_progress)
                    {
                        clearInterval(timer);
                        return;
                    }
                    var progress_html='<p><span class="wpvivid-span-progress">' +
                        '<span class="wpvivid-span-processed-progress wpvivid-span-processed-percent-progress" style="width: '+currentProgess+'%">' +
                        currentProgess+'% completed</span></span></p><p>' +
                        '<span class="dashicons dashicons-welcome-write-blog wpvivid-dashicons-grey"></span><span>Action:</span>' +
                        '<span>Creating the snapshot.</span></p>';

                    jQuery("#wpvivid_snapshot_progress").html(progress_html);
                    if (currentProgess >= MaxProgess)
                    {
                        clearInterval(timer);
                    }
                }, time_steps);
            }

            function wpvivid_create_snapshot()
            {
                var comment=jQuery('#wpvivid_create_snapshot_comment').val();
                var ajax_data= {
                    'action': 'wpvivid_create_snapshot',
                    'comment':comment,
                };
                var default_progress='<p><span class="wpvivid-span-progress"><span class="wpvivid-span-processed-progress wpvivid-span-processed-percent-progress" style="width: 0%">0% completed</span></span></p><p><span class="dashicons dashicons-welcome-write-blog wpvivid-dashicons-grey"></span><span>Action:</span><span>Creating a snapshot.</span></p>';
                jQuery('#wpvivid_snapshot_progress').show();
                jQuery('#wpvivid_snapshot_progress').html(default_progress);

                b_need_update=true;
                b_end_create_progress=false;
                wpvivid_simulate_create_progress();

                setTimeout(function(){
                    wpvivid_get_snapshot_progress();
                }, 3000);

                jQuery('#wpvivid_create_snapshot').css({'pointer-events': 'none', 'opacity': '0.4'});
                wpvivid_post_request(ajax_data, function(data)
                {
                    b_end_create_progress=true;
                    var jsonarray = jQuery.parseJSON(data);
                    if (jsonarray.result === 'success')
                    {
                        jQuery('#wpvivid_snapshot_progress').html(jsonarray.progress);
                        if(jsonarray.finished==1)
                        {
                            b_need_update=false;
                            alert('Creating a snapshot completed successfully.');
                            location.reload();
                        }
                        else
                        {
                            wpvivid_resume_create_snapshot();
                        }
                    }
                    else
                    {
                        b_need_update=false;
                        alert(jsonarray.error);
                        jQuery('#wpvivid_snapshot_progress').hide();
                        jQuery('#wpvivid_create_snapshot').css({'pointer-events': 'auto', 'opacity': '1'});
                    }
                }, function(XMLHttpRequest, textStatus, errorThrown)
                {
                    setTimeout(function(){
                        wpvivid_resume_create_snapshot(0);
                    }, 15000);
                });
            }

            function wpvivid_get_snapshot_progress()
            {
                var ajax_data= {
                    'action': 'wpvivid_get_snapshot_progress',
                };

                wpvivid_post_request(ajax_data, function(data)
                {
                    var jsonarray = jQuery.parseJSON(data);
                    b_end_create_progress=true;
                    jQuery('#wpvivid_snapshot_progress').html(jsonarray.progress);

                    if(b_need_update)
                    {
                        setTimeout(function(){
                            wpvivid_get_snapshot_progress();
                        }, 1000);
                    }
                }, function(XMLHttpRequest, textStatus, errorThrown)
                {
                    if(b_need_update)
                    {
                        setTimeout(function(){
                            wpvivid_get_snapshot_progress();
                        }, 1000);
                    }
                });
            }

            function wpvivid_resume_create_snapshot(resume)
            {
                if(resume>6)
                {
                    b_need_update=false;
                    alert('Creating the snapshot timed out.');
                    jQuery('#wpvivid_snapshot_progress').hide();
                    jQuery('#wpvivid_create_snapshot').css({'pointer-events': 'auto', 'opacity': '1'});
                    return;
                }
                var ajax_data= {
                    'action': 'wpvivid_resume_create_snapshot'
                };

                wpvivid_post_request(ajax_data, function(data)
                {
                    try {
                        var jsonarray = jQuery.parseJSON(data);
                        if (jsonarray.result === 'success')
                        {
                            jQuery('#wpvivid_snapshot_progress').html(jsonarray.progress);

                            if(jsonarray.finished==1)
                            {
                                b_need_update=false;

                                alert('Creating a snapshot completed successfully.');
                                location.reload();
                            }
                            else
                            {
                                wpvivid_resume_create_snapshot();
                            }
                        }
                        else
                        {
                            b_need_update=false;
                            alert(jsonarray.error);
                            jQuery('#wpvivid_snapshot_progress').hide();
                            jQuery('#wpvivid_create_snapshot').css({'pointer-events': 'auto', 'opacity': '1'});
                        }
                    }
                    catch (e)
                    {
                        resume+=1;
                        setTimeout(function(){
                            wpvivid_resume_create_snapshot(resume);
                        }, 15000);
                    }

                }, function(XMLHttpRequest, textStatus, errorThrown)
                {
                    resume+=1;
                    setTimeout(function(){
                        wpvivid_resume_create_snapshot(resume);
                    }, 15000);
                });
            }

            jQuery('#wpvivid_snapshots_list').on("click",'.wpvivid-snapshot-restore',function()
            {
                var Obj=jQuery(this);
                var snapshot_id=Obj.closest('tr').attr('slug');

                var descript = '<?php esc_html_e('Are you sure you want to restore this snapshot?', 'wpvivid'); ?>';
                var ret = confirm(descript);
                if (ret === true)
                {
                    var ajax_data= {
                        'action': 'wpvivid_restore_snapshot',
                        'id':snapshot_id
                    };
                    jQuery('#wpvivid_create_snapshot').css({'pointer-events': 'none', 'opacity': '0.4'});
                    var default_progress='<p><span class="wpvivid-span-progress"><span class="wpvivid-span-processed-progress wpvivid-span-processed-percent-progress" style="width: 0%">0% completed</span></span></p><p><span class="dashicons dashicons-welcome-write-blog wpvivid-dashicons-grey"></span><span>Action:</span><span>Restoring the snapshot.</span></p>';
                    jQuery('#wpvivid_snapshot_progress').show();
                    jQuery('#wpvivid_snapshot_progress').html(default_progress);
                    b_restore_finished=false;
                    wpvivid_simulate_restore_progress();

                    wpvivid_post_request(ajax_data, function(data)
                    {
                        var jsonarray = jQuery.parseJSON(data);
                        if (jsonarray.result === 'success')
                        {
                            b_restore_finished=true;
                            jQuery('#wpvivid_snapshot_progress').html(jsonarray.progress);
                            alert('Restoring the snapshot completed successfully.');
                            location.reload();                        }
                        else
                        {
                            b_restore_finished=true;
                            jQuery('#wpvivid_snapshot_progress').hide();
                            alert(jsonarray.error);
                            jQuery('#wpvivid_create_snapshot').css({'pointer-events': 'auto', 'opacity': '1'});
                        }
                    }, function(XMLHttpRequest, textStatus, errorThrown)
                    {
                        setTimeout(function(){
                            wpvivid_get_restore_snapshot_status();
                        }, 1000);
                    });
                }
            });

            function wpvivid_get_restore_snapshot_status()
            {
                var ajax_data= {
                    'action': 'wpvivid_get_restore_snapshot_status',
                };

                wpvivid_post_request(ajax_data, function(data)
                {
                    try
                    {
                        var jsonarray = jQuery.parseJSON(data);
                        if (jsonarray.result === 'success')
                        {
                            if(jsonarray.finished==1)
                            {
                                jQuery('#wpvivid_snapshot_progress').html(jsonarray.progress);
                                b_restore_finished=true;
                                alert('Restoring the snapshot completed successfully.');
                                location.reload();
                            }
                            else
                            {
                                setTimeout(function(){
                                    wpvivid_get_restore_snapshot_status();
                                }, 1000);
                            }
                        }
                        else
                        {
                            b_restore_finished=true;
                            jQuery('#wpvivid_snapshot_progress').hide();
                            alert(jsonarray.error);
                            jQuery('#wpvivid_create_snapshot').css({'pointer-events': 'auto', 'opacity': '1'});
                        }
                    }
                    catch (err)
                    {
                        setTimeout(function(){
                            wpvivid_get_restore_snapshot_status();
                        }, 1000);
                    }

                }, function(XMLHttpRequest, textStatus, errorThrown)
                {
                    setTimeout(function(){
                        wpvivid_get_restore_snapshot_status();
                    }, 1000);
                });
            }

            jQuery('#wpvivid_snapshots_list').on("click",'.wpvivid-snapshot-delete',function()
            {
                var Obj=jQuery(this);
                var snapshot_id=Obj.closest('tr').attr('slug');

                var descript = '<?php esc_html_e('Are you sure you want to delete this snapshot?', 'wpvivid'); ?>';
                var ret = confirm(descript);
                if (ret === true)
                {
                    var ajax_data= {
                        'action': 'wpvivid_delete_snapshot',
                        'id':snapshot_id
                    };
                    jQuery('#wpvivid_create_snapshot').css({'pointer-events': 'none', 'opacity': '0.4'});
                    wpvivid_post_request(ajax_data, function(data)
                    {
                        var jsonarray = jQuery.parseJSON(data);
                        if (jsonarray.result === 'success')
                        {
                            alert('The snapshot has been deleted successfully.');
                            jQuery('#wpvivid_snapshots_list').html(jsonarray.html);
                            jQuery('#wpvivid_create_snapshot').css({'pointer-events': 'auto', 'opacity': '1'});
                        }
                        else
                        {
                            alert(jsonarray.error);
                            jQuery('#wpvivid_create_snapshot').css({'pointer-events': 'auto', 'opacity': '1'});
                        }
                    }, function(XMLHttpRequest, textStatus, errorThrown)
                    {
                        jQuery('#wpvivid_create_snapshot').css({'pointer-events': 'auto', 'opacity': '1'});
                        alert("Deleting the snapshot(s) failed.");
                    });
                }
            });

            jQuery('#wpvivid_snapshots_list').on("click",'#wpvivid_delete_snapshots_action',function()
            {
                var delete_snapshots_array = new Array();
                var count = 0;

                jQuery('#wpvivid_snapshots_list .wpvivid-snapshot-row input').each(function (i)
                {
                    if(jQuery(this).prop('checked'))
                    {
                        delete_snapshots_array[count] =jQuery(this).closest('tr').attr('slug');
                        count++;
                    }
                });
                if( count === 0 )
                {
                    alert('<?php esc_html_e('Please select at least one item.','wpvivid'); ?>');
                }
                else
                {
                    var descript = '<?php esc_html_e('Are you sure to delete the selected snapshots? These snapshots will be deleted permanently.', 'wpvivid'); ?>';

                    var ret = confirm(descript);
                    if (ret === true)
                    {
                        jQuery('#wpvivid_delete_snapshots_action').css({'pointer-events': 'none', 'opacity': '0.4'});
                        wpvivid_delete_snapshot_array(delete_snapshots_array,0);
                    }
                }
            });

            function wpvivid_delete_snapshot_array(delete_snapshots_array,index)
            {
                if(index >= delete_snapshots_array.length)
                {
                    alert('The snapshot has been deleted successfully.');
                    jQuery('#wpvivid_delete_snapshots_action').css({'pointer-events': 'auto', 'opacity': '1'});
                    return;
                }
                const snapshot_id = delete_snapshots_array[index];
                var ajax_data= {
                    'action': 'wpvivid_delete_snapshot',
                    'id':snapshot_id
                };
                wpvivid_post_request(ajax_data, function(data)
                {
                    var jsonarray = jQuery.parseJSON(data);
                    if (jsonarray.result === 'success')
                    {
                        jQuery('#wpvivid_snapshots_list').html(jsonarray.html);
                        index++;
                        wpvivid_delete_snapshot_array(delete_snapshots_array,index);
                    }
                    else
                    {
                        alert(jsonarray.error);
                        jQuery('#wpvivid_delete_snapshots_action').css({'pointer-events': 'auto', 'opacity': '1'});
                    }
                }, function(XMLHttpRequest, textStatus, errorThrown)
                {
                    alert('Deleting the snapshot(s) failed.');
                    jQuery('#wpvivid_delete_snapshots_action').css({'pointer-events': 'auto', 'opacity': '1'});
                });
            }
        </script>
        <?php
    }

    public function output_snapshots_setting()
    {

        $setting=$this->options->get_option('wpvivid_snapshot_setting');
        if(empty($setting))
        {
            $setting=array();
        }

        $snapshot_retention=isset($setting['snapshot_retention'])?$setting['snapshot_retention']:6;
        $quick_snapshot=isset($setting['quick_snapshot'])?$setting['quick_snapshot']:false;
        if($quick_snapshot)
        {
            $quick_snapshot='checked';
        }
        else
        {
            $quick_snapshot='';
        }
        ?>
        <div class="postbox quicksnapshot">
            <table class="widefat" style="border-left:none;border-top:none;border-right:none;">
                <tr>
                    <td class="row-title" style="min-width:200px;">
                        <label for="tablecell">Snapshot Retention</label>
                    </td>
                    <td>
                        <p>
                            <span>Up to </span>
                            <span>
                                <select id="wpvivid_snapshot_retention" option="setting" name="snapshot_retention">
                                    <option value="3">3</option>
                                    <option value="4">4</option>
                                    <option value="5">5</option>
                                    <option value="6" selected>6</option>
                                    <option value="7">7</option>
                                    <option value="8">8</option>
                                    <option value="9">9</option>
                                    <option value="10">10</option>
                                    <option value="11">11</option>
                                    <option value="12">12</option>
                                </select>
                            </span>
                            <span>snapshots retained</span></p>
                        <p>It is not recommended to create too many snapshots.</p>
                    </td>
                </tr>
                <tr>
                    <td class="row-title" style="min-width:200px;">
                        <label for="tablecell">Quick Snapshot</label>
                    </td>
                    <td>
                        <p>
                            <label class="wpvivid-checkbox">
                                <span>Enable Quick Snapshot</span>
                                <input type="checkbox" option="setting" name="quick_snapshot" <?php echo esc_attr($quick_snapshot); ?> />
                                <span class="wpvivid-checkbox-checkmark"></span>
                            </label>
                        </p>
                        <p><code>Show a menu in top admin bar for quickly creating a snapshot.</code></p>
                    </td>
                </tr>
            </table>
            <div style="padding:1em 1em 0 0;"><input class="button-primary wpvivid-snapshot-setting-save" type="submit" value="Save Changes"></div>
        </div>
        <script>
            jQuery('.wpvivid-snapshot-setting-save').click(function()
            {
                wpvivid_snapshot_setting_save();
            });

            function wpvivid_ajax_snapshot_data_transfer(data_type){
                var json = {};
                jQuery('input:checkbox[option='+data_type+']').each(function() {
                    var value = '0';
                    var key = jQuery(this).prop('name');
                    if(jQuery(this).prop('checked')) {
                        value = '1';
                    }
                    else {
                        value = '0';
                    }
                    json[key]=value;
                });
                jQuery('input:radio[option='+data_type+']').each(function() {
                    if(jQuery(this).prop('checked'))
                    {
                        var key = jQuery(this).prop('name');
                        var value = jQuery(this).prop('value');
                        json[key]=value;
                    }
                });
                jQuery('input:text[option='+data_type+']').each(function(){
                    var obj = {};
                    var key = jQuery(this).prop('name');
                    var value = jQuery(this).val();
                    json[key]=value;
                });
                jQuery('input:password[option='+data_type+']').each(function(){
                    var obj = {};
                    var key = jQuery(this).prop('name');
                    var value = jQuery(this).val();
                    json[key]=value;
                });
                jQuery('select[option='+data_type+']').each(function(){
                    var obj = {};
                    var key = jQuery(this).prop('name');
                    var value = jQuery(this).val();
                    json[key]=value;
                });
                return JSON.stringify(json);
            }

            function wpvivid_snapshot_setting_save()
            {
                var setting_data = wpvivid_ajax_snapshot_data_transfer('setting');
                var json = JSON.parse(setting_data);
                setting_data=JSON.stringify(json);

                var ajax_data = {
                    'action': 'wpvivid_set_snapshot_setting',
                    'setting': setting_data,
                };
                jQuery('.wpvivid-snapshot-setting-save').css({'pointer-events': 'none', 'opacity': '0.4'});
                wpvivid_post_request(ajax_data, function (data)
                {
                    try
                    {
                        var jsonarray = jQuery.parseJSON(data);

                        jQuery('.wpvivid-snapshot-setting-save').css({'pointer-events': 'auto', 'opacity': '1'});
                        if (jsonarray.result === 'success')
                        {
                            location.reload();
                        }
                        else
                        {
                            alert(jsonarray.error);
                        }
                    }
                    catch (err)
                    {
                        alert(err);
                        jQuery('.wpvivid-snapshot-setting-save').css({'pointer-events': 'auto', 'opacity': '1'});
                    }
                },function (XMLHttpRequest, textStatus, errorThrown)
                {
                    var error_msg = "request: "+ textStatus + "(" + errorThrown + "): an error occurred when changing snapshot settings. " +
                        "This error may be request not reaching or server not responding. Please try again later.";
                    alert(error_msg);
                });
            }

            jQuery(document).ready(function ()
            {
                jQuery('#wpvivid_snapshot_retention').val("<?php echo esc_attr($snapshot_retention)?>").change();
            });
        </script>
        <?php
    }

    public function create_snapshot()
    {
        check_ajax_referer( 'wpvivid_ajax', 'nonce' );
        $check=current_user_can('manage_options');
        $check=apply_filters('wpvivid_ajax_check_security',$check);
        if(!$check)
        {
            die();
        }

        if(isset($_POST['comment'])&&!empty($_POST['comment']))
        {
            $comment=sanitize_text_field($_POST['comment']);
        }
        else
        {
            $comment='';
        }

        set_time_limit(300);
        $snapshot=new WPvivid_Snapshot_Function_Ex();
        $snapshot->check_manual_snapshot();
        $ret=$snapshot->create_snapshot('manual',$comment);
        if($ret['result']=='success')
        {
            if($ret['finished']==1)
            {
                $ret['progress'] = '<p><span class="wpvivid-span-progress"><span class="wpvivid-span-processed-progress wpvivid-span-processed-percent-progress" style="width:100%">100% completed</span></span></p>         
                                     <p><span class="dashicons dashicons-welcome-write-blog wpvivid-dashicons-grey"></span><span>Action:</span><span>Create snapshot completed.</span></p>';
            }
            else
            {
                $progress=$snapshot->get_progress();
                $ret['progress'] = '<p><span class="wpvivid-span-progress"><span class="wpvivid-span-processed-progress wpvivid-span-processed-percent-progress" style="width:'.$progress['main_percent'].'">'.$progress['main_percent'].' completed</span></span></p>         
                                     <p><span class="dashicons dashicons-welcome-write-blog wpvivid-dashicons-grey"></span><span>Action:</span><span>'.$progress['doing'].'</span></p>';
            }
        }

        echo wp_json_encode($ret);
        die();
    }

    public function get_snapshot_progress()
    {
        check_ajax_referer( 'wpvivid_ajax', 'nonce' );
        $check=current_user_can('manage_options');
        $check=apply_filters('wpvivid_ajax_check_security',$check);
        if(!$check)
        {
            die();
        }

        set_time_limit(300);
        $snapshot=new WPvivid_Snapshot_Function_Ex();

        $progress=$snapshot->get_progress();
        $ret['progress'] = '<p><span class="wpvivid-span-progress"><span class="wpvivid-span-processed-progress wpvivid-span-processed-percent-progress" style="width:'.$progress['main_percent'].'">'.$progress['main_percent'].' completed</span></span></p>         
                                     <p><span class="dashicons dashicons-welcome-write-blog wpvivid-dashicons-grey"></span><span>Action:</span><span>'.$progress['doing'].'</span></p>';

        echo wp_json_encode($ret);
        die();
    }

    public function resume_create_snapshot()
    {
        check_ajax_referer( 'wpvivid_ajax', 'nonce' );
        $check=current_user_can('manage_options');
        $check=apply_filters('wpvivid_ajax_check_security',$check);
        if(!$check)
        {
            die();
        }

        set_time_limit(300);
        $snapshot=new WPvivid_Snapshot_Function_Ex();
        $ret=$snapshot->resume_create_snapshot();

        if($ret['result']=='success')
        {
            if($ret['finished']==1)
            {
                $ret['progress'] = '<p><span class="wpvivid-span-progress"><span class="wpvivid-span-processed-progress wpvivid-span-processed-percent-progress" style="width:100%">100% completed</span></span></p>         
                                     <p><span class="dashicons dashicons-welcome-write-blog wpvivid-dashicons-grey"></span><span>Action:</span><span>Create snapshot completed.</span></p>';
            }
            else
            {
                $progress=$snapshot->get_progress();
                $ret['progress'] = '<p><span class="wpvivid-span-progress"><span class="wpvivid-span-processed-progress wpvivid-span-processed-percent-progress" style="width:'.$progress['main_percent'].'">'.$progress['main_percent'].' completed</span></span></p>         
                                     <p><span class="dashicons dashicons-welcome-write-blog wpvivid-dashicons-grey"></span><span>Action:</span><span>'.$progress['doing'].'</span></p>';
            }
        }

        echo wp_json_encode($ret);
        die();
    }

    public function restore_snapshot()
    {
        check_ajax_referer( 'wpvivid_ajax', 'nonce' );
        $check=current_user_can('manage_options');
        $check=apply_filters('wpvivid_ajax_check_security',$check);
        if(!$check)
        {
            die();
        }

        if(isset($_POST['id']))
        {
            $snapshot_id=sanitize_text_field($_POST['id']);

            set_time_limit(300);
            $snapshot=new WPvivid_Snapshot_Function_Ex();
            $ret=$snapshot->restore_snapshot($snapshot_id);
            if($ret['result']=='success')
            {
                $ret['progress'] = '<p><span class="wpvivid-span-progress"><span class="wpvivid-span-processed-progress wpvivid-span-processed-percent-progress" style="width:100%">100% completed</span></span></p>         
                                     <p><span class="dashicons dashicons-welcome-write-blog wpvivid-dashicons-grey"></span><span>Action:</span><span>Restoring the snapshot completed.</span></p>';
            }
            echo wp_json_encode($ret);
        }

        die();
    }

    public function get_restore_snapshot_status()
    {
        check_ajax_referer( 'wpvivid_ajax', 'nonce' );
        $check=current_user_can('manage_options');
        $check=apply_filters('wpvivid_ajax_check_security',$check);
        if(!$check)
        {
            die();
        }

        set_time_limit(300);
        $snapshot=new WPvivid_Snapshot_Function_Ex();
        $ret=$snapshot->get_restore_task_data();

        if($ret['result']!='failed')
        {
            $finished=true;
            $i_sum=count($ret['snapshot_tables']);
            $i_finished=0;
            foreach ($ret['snapshot_tables'] as $table)
            {
                if($table['finished']==0)
                {
                    $finished=false;
                }
                else
                {
                    $i_finished++;
                }
            }

            $i_progress=intval(($i_finished/$i_sum)*100);
            $progress['main_percent']=$i_progress.'%';
            $progress['doing']="Restoring the snapshot.";
            $ret['progress'] = '<p><span class="wpvivid-span-progress"><span class="wpvivid-span-processed-progress wpvivid-span-processed-percent-progress" style="width:'.$progress['main_percent'].'">'.$progress['main_percent'].' completed</span></span></p>         
                                     <p><span class="dashicons dashicons-welcome-write-blog wpvivid-dashicons-grey"></span><span>Action:</span><span>'.$progress['doing'].'</span></p>';

            $ret['finished']=$finished;
        }

        echo wp_json_encode($ret);
        die();
    }

    public function delete_snapshot()
    {
        check_ajax_referer( 'wpvivid_ajax', 'nonce' );
        $check=current_user_can('manage_options');
        $check=apply_filters('wpvivid_ajax_check_security',$check);
        if(!$check)
        {
            die();
        }

        if(isset($_POST['id']))
        {
            $snapshot_id=sanitize_text_field($_POST['id']);

            set_time_limit(300);
            $snapshot=new WPvivid_Snapshot_Function_Ex();
            $ret=$snapshot->remove_snapshot($snapshot_id);
            if($ret['result']=='success')
            {
                $snapshot_data=$snapshot->get_snapshots();
                $Snapshots_list = new WPvivid_Snapshots_List_Ex();
                $Snapshots_list->set_list($snapshot_data);
                $Snapshots_list->prepare_items();
                ob_start();
                $Snapshots_list->display();
                $html = ob_get_clean();
                $ret['html']=$html;
            }

            echo wp_json_encode($ret);
        }
        die();
    }

    public function check_create_snapshot($check)
    {
        return true;
    }

    public function create_snapshot_ex($comment)
    {
        set_time_limit(300);
        $snapshot=new WPvivid_Snapshot_Function_Ex();
        $snapshot->create_snapshot('manual',$comment);
    }

    public function set_setting()
    {
        check_ajax_referer( 'wpvivid_ajax', 'nonce' );
        $check=current_user_can('manage_options');
        $check=apply_filters('wpvivid_ajax_check_security',$check);
        if(!$check)
        {
            die();
        }

        if(isset($_POST['setting'])&&!empty($_POST['setting']))
        {
            $json_setting = sanitize_text_field($_POST['setting']);
            $json_setting = stripslashes($json_setting);
            $setting = json_decode($json_setting, true);
            if (is_null($setting))
            {
                $ret['result']='failed';
                $ret['error']='json decode failed';
                echo wp_json_encode($ret);
                die();
            }

            $old_setting=$this->options->get_option('wpvivid_snapshot_setting');
            if(empty($setting))
            {
                $setting=array();
            }

            if(isset($setting['snapshot_retention']))
            {
                $old_setting['snapshot_retention']=intval($setting['snapshot_retention']);
            }

            if(isset($setting['quick_snapshot']))
            {
                $old_setting['quick_snapshot']=intval($setting['quick_snapshot']);
            }

            $this->options->update_option('wpvivid_snapshot_setting',$old_setting);
        }
        $ret['result']='success';
        echo wp_json_encode($ret);
        die();
    }

    public function add_sidebar_free()
    {
        if(defined('WPVIVID_SNAPSHOT_VERSION'))
        {
            $wpvivid_snapshot_version = WPVIVID_SNAPSHOT_VERSION;
        }
        else
        {
            $wpvivid_snapshot_version = WPVIVID_PLUGIN_VERSION;
        }

        ?>
        <div class="postbox">
            <h2>
                <div style="float: left; margin-right: 5px;"><span style="margin: 0; padding: 0"><?php esc_html_e('Current Version: ', 'wpvivid-backuprestore'); ?><?php echo esc_html($wpvivid_snapshot_version); ?></span></div>
                <div style="float: left; margin-right: 5px;"><span style="margin: 0; padding: 0">|</span></div>
                <div style="float: left; margin-left: 0;">
                    <span style="margin: 0; padding: 0"><a href="https://wordpress.org/plugins/wpvivid-snapshot-database/#developers" target="_blank" style="text-decoration: none;"><?php esc_html_e('ChangeLog', 'wpvivid-backuprestore'); ?></a></span>
                </div>
                <div style="clear: both;"></div>
            </h2>
        </div>
        <div id="wpvivid_backup_schedule_part"></div>
        <div class="postbox">
            <h2><span><?php esc_html_e('Troubleshooting', 'wpvivid-backuprestore'); ?></span></h2>
            <div class="inside">
                <table class="widefat" cellpadding="0">
                    <tbody>
                    <tr class="alternate">
                        <td class="row-title"><a href="https://docs.wpvivid.com/wpvivid-database-snapshots-create-database-snapshots-wordpress.html" target="_blank"><?php esc_html_e('Create Database Snapshots', 'wpvivid-backuprestore'); ?></a></td>
                    </tr>
                    <tr>
                        <td class="row-title"><a href="https://docs.wpvivid.com/wpvivid-database-snapshots-restore-database-snapshots-wordpress.html" target="_blank"><?php esc_html_e('Restore Database Snapshots', 'wpvivid-backuprestore'); ?></a></td>
                    </tr>
                    </tbody>
                </table>
            </div>
        </div>
        <div class="postbox">
            <h2><span><?php esc_html_e('Support', 'wpvivid-backuprestore'); ?></span></h2>
            <div class="inside">
                <table class="widefat" cellpadding="0">
                    <tbody>
                    <tr class="alternate"><td class="row-title"><a href="https://wordpress.org/support/plugin/wpvivid-snapshot-database" target="_blank"><?php esc_html_e('Get Support on Forum', 'wpvivid-backuprestore'); ?></a></td></tr>
                    </tbody>
                </table>
            </div>
        </div>
        <?php
    }

    public function add_sidebar()
    {
        if(apply_filters('wpvivid_show_sidebar',true))
        {
            ?>
            <div id="postbox-container-1" class="postbox-container">
                <div class="meta-box-sortables ui-sortable">
                    <div class="postbox  wpvivid-sidebar">
                        <h2 style="margin-top:0.5em;">
                            <span class="dashicons dashicons-book-alt wpvivid-dashicons-orange" ></span>
                            <span><?php esc_attr_e(
                                    'Documentation', 'WpAdminStyle'
                                ); ?></span></h2>
                        <div class="inside" style="padding-top:0;">
                            <ul class="" >
                                <li>
                                    <span class="dashicons dashicons-camera-alt wpvivid-dashicons-grey"></span>
                                    <a href="https://docs.wpvivid.com/wpvivid-database-snapshots-create-database-snapshots-wordpress.html"><b><?php esc_html_e('Create Database Snapshots', 'wpvivid'); ?></b></a>
                                    <small><span style="float: right;"><a href="#" style="text-decoration: none;"><span class="dashicons dashicons-migrate wpvivid-dashicons-grey"></span></a></span></small><br>
                                </li>
                                <li>
                                    <span class="dashicons dashicons-camera-alt wpvivid-dashicons-grey"></span>
                                    <a href="https://docs.wpvivid.com/wpvivid-database-snapshots-restore-database-snapshots-wordpress.html"><b><?php esc_html_e('Restore Database Snapshots', 'wpvivid'); ?></b></a>
                                    <small><span style="float: right;"><a href="#" style="text-decoration: none;"><span class="dashicons dashicons-migrate wpvivid-dashicons-grey"></span></a></span></small><br>
                                </li>
                            </ul>
                        </div>
                        <h2><span class="dashicons dashicons-businesswoman wpvivid-dashicons-green"></span>
                            <span><?php esc_attr_e(
                                    'Support', 'WpAdminStyle'
                                ); ?></span></h2>
                        <div class="inside">
                            <ul class="">
                                <li><span class="dashicons dashicons-admin-comments wpvivid-dashicons-green"></span>
                                    <a href="https://wordpress.org/support/plugin/snapshot-database/"><b><?php esc_html_e('Get Support on Forum', 'wpvivid'); ?></b></a>
                                    <br>
                                    <?php esc_html_e('If you need any help with our plugin, start a thread on the plugin support forum and we will respond shortly.', 'wpvivid'); ?>
                                </li>
                            </ul>

                        </div>
                    </div>
                </div>
            </div>
            <?php
        }
    }
}includes/class-wpvivid-additional-db-method.php000064400000021357151327705670015652 0ustar00<?php

class WPvivid_Additional_DB_Method
{
    private $dbuser;
    private $dbpass;
    private $dbhost;
    private $use_mysqli = false;
    private $dbh;
    private $has_connected = false;
    public $charset;
    public $collate;

    public function __construct($dbuser, $dbpass, $dbhost){
        $this->dbuser = $dbuser;
        $this->dbpass = $dbpass;
        $this->dbhost = $dbhost;

        if ( function_exists( 'mysqli_connect' ) ) {
            $this->use_mysqli = true;

            if ( defined( 'WP_USE_EXT_MYSQL' ) ) {
                $this->use_mysqli = ! WP_USE_EXT_MYSQL;
            }
        }
    }

    public function wpvivid_parse_db_host( $host ) {
        $port    = null;
        $socket  = null;
        $is_ipv6 = false;

        // First peel off the socket parameter from the right, if it exists.
        $socket_pos = strpos( $host, ':/' );
        if ( $socket_pos !== false ) {
            $socket = substr( $host, $socket_pos + 1 );
            $host   = substr( $host, 0, $socket_pos );
        }

        // We need to check for an IPv6 address first.
        // An IPv6 address will always contain at least two colons.
        if ( substr_count( $host, ':' ) > 1 ) {
            $pattern = '#^(?:\[)?(?P<host>[0-9a-fA-F:]+)(?:\]:(?P<port>[\d]+))?#';
            $is_ipv6 = true;
        } else {
            // We seem to be dealing with an IPv4 address.
            $pattern = '#^(?P<host>[^:/]*)(?::(?P<port>[\d]+))?#';
        }

        $matches = array();
        $result  = preg_match( $pattern, $host, $matches );

        if ( 1 !== $result ) {
            // Couldn't parse the address, bail.
            return false;
        }

        $host = '';
        foreach ( array( 'host', 'port' ) as $component ) {
            if ( ! empty( $matches[ $component ] ) ) {
                $$component = $matches[ $component ];
            }
        }

        return array( $host, $port, $socket, $is_ipv6 );
    }

    public function db_version() {
        if ( $this->use_mysqli ) {
            $server_info = mysqli_get_server_info( $this->dbh );
        } else {
            $server_info = mysql_get_server_info( $this->dbh );
        }
        return preg_replace( '/[^0-9.].*/', '', $server_info );
    }

    public function has_cap( $db_cap ) {
        $version = $this->db_version();

        switch ( strtolower( $db_cap ) ) {
            case 'collation':    // @since 2.5.0
            case 'group_concat': // @since 2.7.0
            case 'subqueries':   // @since 2.7.0
                return version_compare( $version, '4.1', '>=' );
            case 'set_charset':
                return version_compare( $version, '5.0.7', '>=' );
            case 'utf8mb4':      // @since 4.1.0
                if ( version_compare( $version, '5.5.3', '<' ) ) {
                    return false;
                }
                if ( $this->use_mysqli ) {
                    $client_version = mysqli_get_client_info();
                } else {
                    $client_version = mysql_get_client_info();
                }

                /*
                 * libmysql has supported utf8mb4 since 5.5.3, same as the MySQL server.
                 * mysqlnd has supported utf8mb4 since 5.0.9.
                 */
                if ( false !== strpos( $client_version, 'mysqlnd' ) ) {
                    $client_version = preg_replace( '/^\D+([\d.]+).*/', '$1', $client_version );
                    return version_compare( $client_version, '5.0.9', '>=' );
                } else {
                    return version_compare( $client_version, '5.5.3', '>=' );
                }
            case 'utf8mb4_520': // @since 4.6.0
                return version_compare( $version, '5.6', '>=' );
        }

        return false;
    }

    public function determine_charset( $charset, $collate ) {
        if ( ( $this->use_mysqli && ! ( $this->dbh instanceof mysqli ) ) || empty( $this->dbh ) ) {
            return compact( 'charset', 'collate' );
        }

        if ( 'utf8' === $charset && $this->has_cap( 'utf8mb4' ) ) {
            $charset = 'utf8mb4';
        }

        if ( 'utf8mb4' === $charset && ! $this->has_cap( 'utf8mb4' ) ) {
            $charset = 'utf8';
            $collate = str_replace( 'utf8mb4_', 'utf8_', $collate );
        }

        if ( 'utf8mb4' === $charset ) {
            // _general_ is outdated, so we can upgrade it to _unicode_, instead.
            if ( ! $collate || 'utf8_general_ci' === $collate ) {
                $collate = 'utf8mb4_unicode_ci';
            } else {
                $collate = str_replace( 'utf8_', 'utf8mb4_', $collate );
            }
        }

        // _unicode_520_ is a better collation, we should use that when it's available.
        if ( $this->has_cap( 'utf8mb4_520' ) && 'utf8mb4_unicode_ci' === $collate ) {
            $collate = 'utf8mb4_unicode_520_ci';
        }

        return compact( 'charset', 'collate' );
    }

    public function init_charset() {
        $charset = '';
        $collate = '';

        if ( function_exists( 'is_multisite' ) && is_multisite() ) {
            $charset = 'utf8';
            if ( defined( 'DB_COLLATE' ) && DB_COLLATE ) {
                $collate = DB_COLLATE;
            } else {
                $collate = 'utf8_general_ci';
            }
        } elseif ( defined( 'DB_COLLATE' ) ) {
            $collate = DB_COLLATE;
        }

        if ( defined( 'DB_CHARSET' ) ) {
            $charset = DB_CHARSET;
        }

        $charset_collate = $this->determine_charset( $charset, $collate );

        $this->charset = $charset_collate['charset'];
        $this->collate = $charset_collate['collate'];
    }

    public function wpvivid_do_connect($allow_bail = true){
        $new_link     = defined( 'MYSQL_NEW_LINK' ) ? MYSQL_NEW_LINK : true;
        $client_flags = defined( 'MYSQL_CLIENT_FLAGS' ) ? MYSQL_CLIENT_FLAGS : 0;

        $error_code = '';
        $error = 'Unknown Error.';

        if ( $this->use_mysqli ) {
            $this->dbh = mysqli_init();

            $host    = $this->dbhost;
            $port    = null;
            $socket  = null;
            $is_ipv6 = false;

            if ( $host_data = $this->wpvivid_parse_db_host( $this->dbhost ) ) {
                list( $host, $port, $socket, $is_ipv6 ) = $host_data;
            }

            if ( $is_ipv6 && extension_loaded( 'mysqlnd' ) ) {
                $host = "[$host]";
            }

            @mysqli_real_connect( $this->dbh, $host, $this->dbuser, $this->dbpass, null, $port, $socket, $client_flags );

            if ( $this->dbh->connect_errno ) {
                $error_code = $this->dbh->connect_errno;
                $error = $this->dbh->connect_error;
                $this->dbh = null;
                $attempt_fallback = true;

                if ( $this->has_connected ) {
                    $attempt_fallback = false;
                } elseif ( defined( 'WP_USE_EXT_MYSQL' ) && ! WP_USE_EXT_MYSQL ) {
                    $attempt_fallback = false;
                } elseif ( ! function_exists( 'mysql_connect' ) ) {
                    $attempt_fallback = false;
                }

                if ( $attempt_fallback ) {
                    $this->use_mysqli = false;
                    return $this->wpvivid_do_connect( $allow_bail );
                }
            }
        }
        else{
            $this->dbh = @mysql_connect( $this->dbhost, $this->dbuser, $this->dbpass, $new_link, $client_flags );
        }

        if($this->dbh){
            $this->has_connected = true;
            $ret['result'] = WPVIVID_SUCCESS;
        }
        else{
            $ret['result'] = WPVIVID_FAILED;
            $ret['error'] = $error_code.': '.$error;
        }

        return $ret;
    }

    public function wpvivid_show_additional_databases(){
        $query = 'SHOW DATABASES;';
        $result = '';
        if ( ! empty( $this->dbh ) && $this->use_mysqli ) {
            $result = mysqli_query( $this->dbh, $query );
        } elseif ( ! empty( $this->dbh ) ) {
            $result = mysql_query( $query, $this->dbh );
        }
        if ( $this->use_mysqli && $result instanceof mysqli_result ) {
            while ( $row = mysqli_fetch_object( $result ) ) {
                $database_array[] = $row;
            }
        } elseif ( is_resource( $result ) ) {
            while ( $row = mysql_fetch_object( $result ) ) {
                $database_array[] = $row;
            }
        }
        if(!empty($database_array)){
            foreach ($database_array as $key => $value){
                $last_result[] = $value->Database;
            }
        }
        return $last_result;
    }
}includes/mu-plugins/a-wpvivid-restore-mu-plugin-check.php000064400000002661151327705670017562 0ustar00<?php

/**
 * Plugin Name:       WPvivid Restore Must use plugin checker
 * Plugin URI:        https://wpvivid.com/
 * Description:       
 * Author:            WPvivid
 */

// If this file is called directly, abort.
if ( ! defined( "WPINC" ) ) die;

// Load and include
register_shutdown_function('wpvivid_deal_restore_shut_down_error');
// Run

function wpvivid_transfer_path($path)
{
        $path = str_replace('\\','/',$path);
        $values = explode('/',$path);
        return implode(DIRECTORY_SEPARATOR,$values);
}

function wpvivid_deal_restore_shut_down_error()
{
    $error = error_get_last();
    if (!is_null($error)&&($error['type']==E_ERROR||$error['type']==E_COMPILE_ERROR))
    {
        if(preg_match('/Failed opening required.*$/', $error['message']))
        {
            $error_file_path=$error['file'];
            $error_file_path=wpvivid_transfer_path($error_file_path);

            $mu_path = wpvivid_transfer_path(WPMU_PLUGIN_DIR);
            if(strpos($error_file_path,$mu_path)!==false)
            {
                @wp_delete_file($error_file_path);
                $restore_task=get_option('wpvivid_restore_task',array());

                $restore_task['status']='error';
                $restore_task['error']=$error['message'];
                $restore_task['error_mu_require_file']=$error['file'];
                update_option('wpvivid_restore_task',$restore_task,'no');
            }
        }
    }

    die();
}

includes/class-wpvivid-error-log.php000064400000014352151327705670013606 0ustar00<?php

if (!defined('WPVIVID_PLUGIN_DIR')){
    die;
}
class WPvivid_error_log
{
    public static function create_error_log($log_file_name)
    {
        $dir=dirname($log_file_name);
        $file=basename($log_file_name);
        if(!is_dir($dir.DIRECTORY_SEPARATOR.'error'))
        {
            @mkdir($dir.DIRECTORY_SEPARATOR.'error',0777,true);
            //@fopen($dir.DIRECTORY_SEPARATOR.'error'.'/index.html', 'x');
            $tempfile=@fopen($dir.DIRECTORY_SEPARATOR.'error'.'/.htaccess', 'x');
            if($tempfile)
            {
                //$text="deny from all";
                $text="<IfModule mod_rewrite.c>\r\nRewriteEngine On\r\nRewriteRule .* - [F,L]\r\n</IfModule>";
                fwrite($tempfile,$text );
                @fclose($tempfile);
            }
        }

        if(!file_exists($log_file_name))
        {
            return ;
        }

        if(file_exists($dir.DIRECTORY_SEPARATOR.'error'.DIRECTORY_SEPARATOR.$file))
        {
            @wp_delete_file($dir.DIRECTORY_SEPARATOR.'error'.DIRECTORY_SEPARATOR.$file);
        }

        @rename($log_file_name,$dir.DIRECTORY_SEPARATOR.'error'.DIRECTORY_SEPARATOR.$file);

        //self::delete_oldest_error_log();
    }

    public static function create_restore_error_log($log_file_name)
    {
        $dir=dirname($log_file_name);
        if(!is_dir($dir.DIRECTORY_SEPARATOR.'wpvivid_log'.DIRECTORY_SEPARATOR.'error'))
        {
            @mkdir($dir.DIRECTORY_SEPARATOR.'wpvivid_log'.DIRECTORY_SEPARATOR.'error',0777,true);
            //@fopen($dir.DIRECTORY_SEPARATOR.'wpvivid_log'.DIRECTORY_SEPARATOR.'error'.'/index.html', 'x');
            $tempfile=@fopen($dir.DIRECTORY_SEPARATOR.'wpvivid_log'.DIRECTORY_SEPARATOR.'error'.'/.htaccess', 'x');
            if($tempfile)
            {
                //$text="deny from all";
                $text="<IfModule mod_rewrite.c>\r\nRewriteEngine On\r\nRewriteRule .* - [F,L]\r\n</IfModule>";
                fwrite($tempfile,$text );
                @fclose($tempfile);
            }
        }
        $id = uniqid('wpvivid-');
        $file=$id.'_restore_log.txt';
        if(file_exists($dir.DIRECTORY_SEPARATOR.'error'.DIRECTORY_SEPARATOR.$file))
        {
            @wp_delete_file($dir.DIRECTORY_SEPARATOR.'error'.DIRECTORY_SEPARATOR.$file);
        }

        @copy($log_file_name,$dir.DIRECTORY_SEPARATOR.'wpvivid_log'.DIRECTORY_SEPARATOR.'error'.DIRECTORY_SEPARATOR.$file);
        //self::delete_oldest_error_log();
    }

    public static function delete_oldest_error_log()
    {
        $files=array();
        $log=new WPvivid_Log();
        $dir=$log->GetSaveLogFolder();
        $dir=$dir.'error';
        @$handler=opendir($dir);
        if($handler===false)
            return;
        $regex='#^wpvivid.*_log.txt#';
        while(($filename=readdir($handler))!==false)
        {
            if($filename != "." && $filename != "..")
            {
                if(is_dir($dir.DIRECTORY_SEPARATOR.$filename))
                {
                    continue;
                }else{
                    if(preg_match($regex,$filename))
                    {
                        $files[$filename] = $dir.DIRECTORY_SEPARATOR.$filename;
                    }
                }
            }
        }
        if($handler)
            @closedir($handler);
        $oldest=0;
        $oldest_filename='';
        $max_count=5;
        if(sizeof($files)>$max_count)
        {
            foreach ($files as $file)
            {
                if($oldest==0)
                {
                    $oldest=filemtime($file);
                    $oldest_filename=$file;
                }
                else
                {
                    if($oldest>filemtime($file))
                    {
                        $oldest=filemtime($file);
                        $oldest_filename=$file;
                    }
                }
            }

            if($oldest_filename!='')
            {
                @wp_delete_file($oldest_filename);
            }
        }
    }

    public static function get_error_log()
    {
        $log=new WPvivid_Log();
        $dir=$log->GetSaveLogFolder();
        $dir=$dir.'error';
        $files=array();
        $handler=opendir($dir);
        if($handler === false){
            return $files;
        }
        $regex='#^wpvivid.*_log.txt#';
        while(($filename=readdir($handler))!==false)
        {
            if($filename != "." && $filename != "..")
            {
                if(is_dir($dir.$filename))
                {
                    continue;
                }
                else{
                    if(preg_match($regex,$filename))
                    {
                        $files[] = $dir.DIRECTORY_SEPARATOR.$filename;
                    }
                }
            }
        }
        if($handler)
            @closedir($handler);
        return $files;
    }

    public static function get_staging_error_log()
    {
        if(!class_exists('WPvivid_Staging_Log_Free'))
            include_once WPVIVID_PLUGIN_DIR . '/includes/staging/class-wpvivid-staging-log.php';
        $log=new WPvivid_Staging_Log_Free();
        $dir=$log->GetSaveLogFolder();
        $dir=$dir.'error';
        $files=array();
        if(is_dir($dir) && file_exists($dir))
        {
            $handler=opendir($dir);
            if($handler === false){
                return $files;
            }
            $regex='#^wpvivid.*_log.txt#';
            while(($filename=readdir($handler))!==false)
            {
                if($filename != "." && $filename != "..")
                {
                    if(is_dir($dir.$filename))
                    {
                        continue;
                    }
                    else{
                        if(preg_match($regex,$filename))
                        {
                            $files[] = $dir.DIRECTORY_SEPARATOR.$filename;
                        }
                    }
                }
            }
            if($handler)
                @closedir($handler);
            return $files;
        }
        else
        {
            return $files;
        }
    }
}includes/class-wpvivid-interface-mainwp.php000064400000065073151327705670015135 0ustar00<?php

class WPvivid_Interface_MainWP
{
    public function __construct(){
        $this->load_wpvivid_mainwp_backup_filter();
        $this->load_wpvivid_mainwp_side_bar_filter();
        $this->load_wpvivid_mainwp_backup_list_filter();
        $this->load_wpvivid_mainwp_schedule_filter();
        $this->load_wpvivid_mainwp_setting_filter();
        $this->load_wpvivid_mainwp_remote_filter();
    }

    public function load_wpvivid_mainwp_backup_filter(){
        add_filter('wpvivid_get_status_mainwp', array($this, 'wpvivid_get_status_mainwp'));
        add_filter('wpvivid_get_backup_list_mainwp', array($this, 'wpvivid_get_backup_list_mainwp'));
        add_filter('wpvivid_get_backup_schedule_mainwp', array($this, 'wpvivid_get_backup_schedule_mainwp'));
        add_filter('wpvivid_get_default_remote_mainwp', array($this, 'wpvivid_get_default_remote_mainwp'));
        add_filter('wpvivid_prepare_backup_mainwp', array($this, 'wpvivid_prepare_backup_mainwp'));
        add_filter('wpvivid_backup_now_mainwp', array($this, 'wpvivid_backup_now_mainwp'));
        add_filter('wpvivid_view_backup_task_log_mainwp', array($this, 'wpvivid_view_backup_task_log_mainwp'));
        add_filter('wpvivid_backup_cancel_mainwp', array($this, 'wpvivid_backup_cancel_mainwp'));
        add_filter('wpvivid_set_backup_report_addon_mainwp', array($this, 'wpvivid_set_backup_report_addon_mainwp'));
    }

    public function load_wpvivid_mainwp_side_bar_filter(){
        add_filter('wpvivid_read_last_backup_log_mainwp', array($this, 'wpvivid_read_last_backup_log_mainwp'));
    }

    public function load_wpvivid_mainwp_backup_list_filter(){
        add_filter('wpvivid_set_security_lock_mainwp', array($this, 'wpvivid_set_security_lock_mainwp'));
        add_filter('wpvivid_view_log_mainwp', array($this, 'wpvivid_view_log_mainwp'));
        add_filter('wpvivid_init_download_page_mainwp', array($this, 'wpvivid_init_download_page_mainwp'));
        add_filter('wpvivid_prepare_download_backup_mainwp', array($this, 'wpvivid_prepare_download_backup_mainwp'));
        add_filter('wpvivid_get_download_task_mainwp', array($this, 'wpvivid_get_download_task_mainwp'));
        add_filter('wpvivid_download_backup_mainwp', array($this, 'wpvivid_download_backup_mainwp'));
        add_filter('wpvivid_delete_backup_mainwp', array($this, 'wpvivid_delete_backup_mainwp'));
        add_filter('wpvivid_delete_backup_array_mainwp', array($this, 'wpvivid_delete_backup_array_mainwp'));
    }

    public function load_wpvivid_mainwp_schedule_filter(){
        add_filter('wpvivid_set_schedule_mainwp', array($this, 'wpvivid_set_schedule_mainwp'));
    }

    public function load_wpvivid_mainwp_setting_filter(){
        add_filter('wpvivid_set_general_setting_mainwp', array($this, 'wpvivid_set_general_setting_mainwp'));
    }

    public function load_wpvivid_mainwp_remote_filter(){
        add_filter('wpvivid_set_remote_mainwp', array($this, 'wpvivid_set_remote_mainwp'));
    }

    public function wpvivid_get_status_mainwp($data){
        $ret['result']='success';
        $list_tasks=array();
        $tasks=WPvivid_Setting::get_tasks();
        foreach ($tasks as $task)
        {
            $backup = new WPvivid_Backup_Task($task['id']);
            $list_tasks[$task['id']]=$backup->get_backup_task_info($task['id']);
            if($list_tasks[$task['id']]['task_info']['need_update_last_task']===true){
                $task_msg = WPvivid_taskmanager::get_task($task['id']);
                WPvivid_Setting::update_option('wpvivid_last_msg',$task_msg);
                apply_filters('wpvivid_set_backup_report_addon_mainwp', $task_msg);
            }
        }
        $ret['wpvivid']['task']=$list_tasks;
        $backuplist=WPvivid_Backuplist::get_backuplist();
        $schedule=WPvivid_Schedule::get_schedule();
        $ret['wpvivid']['backup_list']=$backuplist;
        $ret['wpvivid']['schedule']=$schedule;
        $ret['wpvivid']['schedule']['last_message']=WPvivid_Setting::get_last_backup_message('wpvivid_last_msg');
        WPvivid_taskmanager::delete_marked_task();
        return $ret;
    }

    public function wpvivid_get_backup_list_mainwp($data){
        $backuplist=WPvivid_Backuplist::get_backuplist();
        $ret['result']='success';
        $ret['wpvivid']['backup_list']=$backuplist;
        return $ret;
    }

    public function wpvivid_get_backup_schedule_mainwp($data){
        $schedule=WPvivid_Schedule::get_schedule();
        $ret['result']='success';
        $ret['wpvivid']['schedule']=$schedule;
        $ret['wpvivid']['schedule']['last_message']=WPvivid_Setting::get_last_backup_message('wpvivid_last_msg');
        return $ret;
    }

    public function wpvivid_get_default_remote_mainwp($data){
        global $wpvivid_plugin;
        $ret['result']='success';
        $ret['remote_storage_type']=$wpvivid_plugin->function_realize->_get_default_remote_storage();
        return $ret;
    }

    public function wpvivid_prepare_backup_mainwp($data){
        $backup_options = $data['backup'];

        global $wpvivid_plugin;
        if(isset($backup_options)&&!empty($backup_options))
        {
            if (is_null($backup_options))
            {
                $ret['error']='Invalid parameter param:'.$backup_options;
                return $ret;
            }

            if(!isset($backup_options['type']))
            {
                $backup_options['type']='Manual';
            }

            if(!isset($backup_options['backup_files'])||empty($backup_options['backup_files']))
            {
                $ret['result']='failed';
                $ret['error']=__('A backup type is required.', 'wpvivid-backuprestore');
                return $ret;
            }

            if(!isset($backup_options['local'])||!isset($backup_options['remote']))
            {
                $ret['result']='failed';
                $ret['error']=__('Choose at least one storage location for backups.', 'wpvivid-backuprestore');
                return $ret;
            }

            if(empty($backup_options['local']) && empty($backup_options['remote']))
            {
                $ret['result']='failed';
                $ret['error']=__('Choose at least one storage location for backups.', 'wpvivid-backuprestore');
                return $ret;
            }

            if ($backup_options['remote'] === '1')
            {
                $remote_storage = WPvivid_Setting::get_remote_options();
                if ($remote_storage == false)
                {
                    $ret['result']='failed';
                    $ret['error'] = __('There is no default remote storage configured. Please set it up first.', 'wpvivid-backuprestore');
                    return $ret;
                }
            }

            if(apply_filters('wpvivid_need_clean_oldest_backup',true,$backup_options))
            {
                $wpvivid_plugin->clean_oldest_backup();
            }
            do_action('wpvivid_clean_oldest_backup',$backup_options);

            if($wpvivid_plugin->backup2->is_tasks_backup_running())
            {
                $ret['result']='failed';
                $ret['error']=__('A task is already running. Please wait until the running task is complete, and try again.', 'wpvivid-backuprestore');
                echo wp_json_encode($ret);
                die();
            }

            $settings=$wpvivid_plugin->backup2->get_backup_settings($backup_options);

            $backup=new WPvivid_Backup_Task_2();
            $ret=$backup->new_backup_task($backup_options,$settings);
        }
        else{
            $ret['error']='Error occurred while parsing the request data. Please try to run backup again.';
            return $ret;
        }
        return $ret;
    }

    public function wpvivid_backup_now_mainwp($data){
        global $wpvivid_plugin;
        try{
            $task_id = $data['task_id'];
            $task_id=sanitize_key($task_id);
            if (!isset($task_id)||empty($task_id)||!is_string($task_id))
            {
                $ret['result'] = 'failed';
                $ret['error']=__('Error occurred while parsing the request data. Please try to run backup again.', 'wpvivid-backuprestore');
                return $ret;
            }

            if ($wpvivid_plugin->backup2->is_tasks_backup_running($task_id))
            {
                $ret['result'] = 'failed';
                $ret['error'] = __('We detected that there is already a running backup task. Please wait until it completes then try again.', 'wpvivid-backuprestore');
                return $ret;
            }

            $wpvivid_plugin->backup2->update_backup_task_status($task_id,true,'running');
            $wpvivid_plugin->flush($task_id, true);
            $wpvivid_plugin->backup2->add_monitor_event($task_id);
            $wpvivid_plugin->backup2->task=new WPvivid_Backup_Task_2($task_id);
            $wpvivid_plugin->backup2->task->set_memory_limit();
            $wpvivid_plugin->backup2->task->set_time_limit();

            $wpvivid_plugin->wpvivid_log->OpenLogFile($wpvivid_plugin->backup2->task->task['options']['log_file_name']);
            $wpvivid_plugin->wpvivid_log->WriteLog('Start backing up.','notice');
            $wpvivid_plugin->wpvivid_log->WriteLogHander();

            if(!$wpvivid_plugin->backup2->task->is_backup_finished())
            {
                $ret=$wpvivid_plugin->backup2->backup();
                $wpvivid_plugin->backup2->task->clear_cache();
                if($ret['result']!='success')
                {
                    $wpvivid_plugin->wpvivid_log->WriteLog('Backup the file ends with an error '. $ret['error'],'error');
                    $wpvivid_plugin->backup2->task->update_backup_task_status(false,'error',false,false,$ret['error']);
                    do_action('wpvivid_handle_backup_2_failed', $task_id);
                    $wpvivid_plugin->backup2->clear_monitor_schedule($task_id);
                    $ret['result'] = 'failed';
                    $ret['error']='Backup the file ends with an error '. $ret['error'];
                    return $ret;
                }
            }

            if($wpvivid_plugin->backup2->task->need_upload())
            {
                $ret=$wpvivid_plugin->backup2->upload($task_id);
                if($ret['result'] == WPVIVID_SUCCESS)
                {
                    do_action('wpvivid_handle_backup_2_succeed',$task_id);
                    $wpvivid_plugin->backup2->update_backup_task_status($task_id,false,'completed');
                }
                else
                {
                    $wpvivid_plugin->wpvivid_log->WriteLog('Uploading the file ends with an error '. $ret['error'], 'error');
                    do_action('wpvivid_handle_backup_2_failed',$task_id);
                }
            }
            else
            {
                $wpvivid_plugin->wpvivid_log->WriteLog('Backup completed.','notice');
                do_action('wpvivid_handle_backup_2_succeed', $task_id);
                $wpvivid_plugin->backup2->update_backup_task_status($task_id,false,'completed');
            }
            $wpvivid_plugin->backup2->clear_monitor_schedule($task_id);
            $ret['result']='success';
            return $ret;
        }
        catch (Exception $error)
        {
            //catch error and stop task recording history
            $message = 'An exception has occurred. class:'.get_class($error).';msg:'.$error->getMessage().';code:'.$error->getCode().';line:'.$error->getLine().';in_file:'.$error->getFile().';';
            error_log($message);
            WPvivid_taskmanager::update_backup_task_status($task_id,false,'error',false,false,$message);
            $wpvivid_plugin->wpvivid_log->WriteLog($message,'error');
            do_action('wpvivid_handle_backup_2_failed',$task_id);
            $ret['result'] = 'failed';
            $ret['error']=$message;
            return $ret;
        }
    }

    public function wpvivid_view_backup_task_log_mainwp($data){
        $backup_task_id = $data['id'];
        global $wpvivid_plugin;
        if (!isset($backup_task_id)||empty($backup_task_id)||!is_string($backup_task_id)){
            $ret['error']='Reading the log failed. Please try again.';
            return $ret;
        }
        $backup_task_id = sanitize_key($backup_task_id);
        $ret=$wpvivid_plugin->function_realize->_get_log_file('tasklog', $backup_task_id);
        if($ret['result'] == 'success') {
            $file = fopen($ret['log_file'], 'r');
            if (!$file) {
                $ret['result'] = 'failed';
                $ret['error'] = __('Unable to open the log file.', 'wpvivid-backuprestore');
                return $ret;
            }
            $buffer = '';
            while (!feof($file)) {
                $buffer .= fread($file, 1024);
            }
            fclose($file);
            $ret['result'] = 'success';
            $ret['data'] = $buffer;
        }
        else{
            $ret['error']='Unknown error';
        }
        return $ret;
    }

    public function wpvivid_backup_cancel_mainwp($data){
        global $wpvivid_plugin;
        $ret=$wpvivid_plugin->function_realize->_backup_cancel();
        return $ret;
    }

    public function wpvivid_set_backup_report_addon_mainwp($data){
        if(isset($data['id']))
        {
            $task_id = $data['id'];
            $option = array();
            $option[$task_id]['task_id'] = $task_id;
            $option[$task_id]['backup_time'] = $data['status']['start_time'];
            if($data['status']['str'] == 'completed'){
                $option[$task_id]['status'] = 'Succeeded';
            }
            elseif($data['status']['str'] == 'error'){
                $option[$task_id]['status'] = 'Failed, '.$data['status']['error'];
            }
            elseif($data['status']['str'] == 'cancel'){
                $option[$task_id]['status'] = 'Canceled';
            }
            else{
                $option[$task_id]['status'] = 'The last backup message not found.';
            }

            $backup_reports = get_option('wpvivid_backup_reports', array());
            if(!empty($backup_reports)){
                foreach ($option as $key => $value){
                    $backup_reports[$key] = $value;
                    update_option('wpvivid_backup_reports', $backup_reports, 'no');
                }
            }
            else{
                update_option('wpvivid_backup_reports', $option, 'no');
            }
        }
    }

    public function wpvivid_read_last_backup_log_mainwp($data){
        $log_file_name = $data['log_file_name'];
        global $wpvivid_plugin;
        if(!isset($log_file_name)||empty($log_file_name)||!is_string($log_file_name))
        {
            $ret['result']='failed';
            $ret['error']=__('Reading the log failed. Please try again.', 'wpvivid-backuprestore');
            return $ret;
        }
        $log_file_name=sanitize_text_field($log_file_name);
        $ret=$wpvivid_plugin->function_realize->_get_log_file('lastlog', $log_file_name);
        if($ret['result'] == 'success') {
            $file = fopen($ret['log_file'], 'r');
            if (!$file) {
                $ret['result'] = 'failed';
                $ret['error'] = __('Unable to open the log file.', 'wpvivid-backuprestore');
                return $ret;
            }
            $buffer = '';
            while (!feof($file)) {
                $buffer .= fread($file, 1024);
            }
            fclose($file);
            $ret['result'] = 'success';
            $ret['data'] = $buffer;
        }
        else{
            $ret['error']='Unknown error';
        }
        return $ret;
    }

    public function wpvivid_set_security_lock_mainwp($data){
        $backup_id = $data['backup_id'];
        $lock = $data['lock'];
        if(!isset($backup_id)||empty($backup_id)||!is_string($backup_id)){
            $ret['error']='Backup id not found';
            return $ret;
        }
        if(!isset($lock)){
            $ret['error']='Invalid parameter param: lock';
            return $ret;
        }
        $backup_id=sanitize_key($backup_id);
        if($lock==0||$lock==1) {
        }
        else {
            $lock=0;
        }
        WPvivid_Backuplist::set_security_lock($backup_id,$lock);
        $backuplist=WPvivid_Backuplist::get_backuplist();
        $ret['wpvivid']['backup_list']=$backuplist;
        return $ret;
    }

    public function wpvivid_view_log_mainwp($data){
        $backup_id = $data['id'];
        global $wpvivid_plugin;
        if (!isset($backup_id)||empty($backup_id)||!is_string($backup_id)){
            $ret['error']='Backup id not found';
            return $ret;
        }
        $backup_id=sanitize_key($backup_id);
        $ret=$wpvivid_plugin->function_realize->_get_log_file('backuplist', $backup_id);
        if($ret['result'] == 'success') {
            $file = fopen($ret['log_file'], 'r');
            if (!$file) {
                $ret['result'] = 'failed';
                $ret['error'] = __('Unable to open the log file.', 'wpvivid-backuprestore');
                return $ret;
            }
            $buffer = '';
            while (!feof($file)) {
                $buffer .= fread($file, 1024);
            }
            fclose($file);
            $ret['data'] = $buffer;
        }
        else{
            $ret['error']='Unknown error';
        }
        return $ret;
    }

    public function wpvivid_init_download_page_mainwp($data){
        $backup_id = $data['backup_id'];
        global $wpvivid_plugin;
        if(!isset($backup_id)||empty($backup_id)||!is_string($backup_id)) {
            $ret['error']='Invalid parameter param:'.$backup_id;
            return $ret;
        }
        else {
            $backup_id=sanitize_key($backup_id);
            return $wpvivid_plugin->init_download($backup_id);
        }
    }

    public function wpvivid_prepare_download_backup_mainwp($data){
        $backup_id = $data['backup_id'];
        $file_name = $data['file_name'];
        if(!isset($backup_id)||empty($backup_id)||!is_string($backup_id))
        {
            $ret['error']='Invalid parameter param:'.$backup_id;
            return $ret;
        }
        if(!isset($file_name)||empty($file_name)||!is_string($file_name))
        {
            $ret['error']='Invalid parameter param:'.$file_name;
            return $ret;
        }
        $download_info=array();
        $download_info['backup_id']=sanitize_key($backup_id);
        $download_info['file_name'] = $file_name;

        @set_time_limit(600);
        if (session_id())
            session_write_close();
        try
        {
            $downloader=new WPvivid_downloader();
            $downloader->ready_download($download_info);
        }
        catch (Exception $e)
        {
            $message = 'A exception ('.get_class($e).') occurred '.$e->getMessage().' (Code: '.$e->getCode().', line '.$e->getLine().' in '.$e->getFile().')';
            error_log($message);
            return array('error'=>$message);
        }
        catch (Error $e)
        {
            $message = 'A error ('.get_class($e).') has occurred: '.$e->getMessage().' (Code: '.$e->getCode().', line '.$e->getLine().' in '.$e->getFile().')';
            error_log($message);
            return array('error'=>$message);
        }

        $ret['result']='success';
        return $ret;
    }

    public function wpvivid_get_download_task_mainwp($data){
        $backup_id = $data['backup_id'];
        if(!isset($backup_id)||empty($backup_id)||!is_string($backup_id)) {
            $ret['error']='Invalid parameter param:'.$backup_id;
            return $ret;
        }
        else {
            $backup = WPvivid_Backuplist::get_backup_by_id($backup_id);
            if ($backup === false) {
                $ret['result'] = WPVIVID_FAILED;
                $ret['error'] = 'backup id not found';
                return $ret;
            }
            $backup_item = new WPvivid_Backup_Item($backup);
            $ret = $backup_item->update_download_page($backup_id);
            return $ret;
        }
    }

    public function wpvivid_download_backup_mainwp($data){
        $backup_id = $data['backup_id'];
        $file_name = $data['file_name'];
        global $wpvivid_plugin;
        if(!isset($backup_id)||empty($backup_id)||!is_string($backup_id)) {
            $ret['error']='Invalid parameter param: backup_id';
            return $ret;
        }
        if(!isset($file_name)||empty($file_name)||!is_string($file_name)) {
            $ret['error']='Invalid parameter param: file_name';
            return $ret;
        }
        $backup_id=sanitize_key($backup_id);
        $cache=WPvivid_taskmanager::get_download_cache($backup_id);
        if($cache===false) {
            $wpvivid_plugin->init_download($backup_id);
            $cache=WPvivid_taskmanager::get_download_cache($backup_id);
        }
        $path=false;
        if(array_key_exists($file_name,$cache['files'])) {
            if($cache['files'][$file_name]['status']=='completed') {
                $path=$cache['files'][$file_name]['download_path'];
                $download_url = $cache['files'][$file_name]['download_url'];
            }
        }
        if($path!==false) {
            if (file_exists($path)) {
                $ret['download_url'] = $download_url;
                $ret['size'] = filesize($path);
            }
        }
        return $ret;
    }

    public function wpvivid_delete_backup_mainwp($data){
        $backup_id = $data['backup_id'];
        $force_del = $data['force'];
        global $wpvivid_plugin;
        if(!isset($backup_id)||empty($backup_id)||!is_string($backup_id)) {
            $ret['error']='Invalid parameter param: backup_id.';
            return $ret;
        }
        if(!isset($force_del)){
            $ret['error']='Invalid parameter param: force.';
            return $ret;
        }
        if($force_del==0||$force_del==1) {
        }
        else {
            $force_del=0;
        }
        $backup_id=sanitize_key($backup_id);
        $ret=$wpvivid_plugin->delete_backup_by_id($backup_id, $force_del);
        $backuplist=WPvivid_Backuplist::get_backuplist();
        $ret['wpvivid']['backup_list']=$backuplist;
        return $ret;
    }

    public function wpvivid_delete_backup_array_mainwp($data){
        $backup_id_array = $data['backup_id'];
        global $wpvivid_plugin;
        if(!isset($backup_id_array)||empty($backup_id_array)||!is_array($backup_id_array)) {
            $ret['error']='Invalid parameter param: backup_id';
            return $ret;
        }
        $ret=array();
        foreach($backup_id_array as $backup_id)
        {
            $backup_id=sanitize_key($backup_id);
            $ret=$wpvivid_plugin->delete_backup_by_id($backup_id);
        }
        $backuplist=WPvivid_Backuplist::get_backuplist();
        $ret['wpvivid']['backup_list']=$backuplist;
        return $ret;
    }

    public function wpvivid_set_schedule_mainwp($data){
        $schedule = $data['schedule'];
        $ret=array();
        try {
            if(isset($schedule)&&!empty($schedule)) {
                $json = $schedule;
                $json = stripslashes($json);
                $schedule = json_decode($json, true);
                if (is_null($schedule)) {
                    $ret['error']='bad parameter';
                    return $ret;
                }
                $ret=WPvivid_Schedule::set_schedule_ex($schedule);
                if($ret['result']!='success') {
                    return $ret;
                }
            }
            $ret['result']='success';
        }
        catch (Exception $error) {
            $message = 'An exception has occurred. class: '.get_class($error).';msg: '.$error->getMessage().';code: '.$error->getCode().';line: '.$error->getLine().';in_file: '.$error->getFile().';';
            error_log($message);
            return array('error'=>$message);
        }
        return $ret;
    }

    public function wpvivid_set_general_setting_mainwp($data){
        $setting = $data['setting'];
        $ret=array();
        try {
            if(isset($setting)&&!empty($setting)) {
                $json_setting = $setting;
                $json_setting = stripslashes($json_setting);
                $setting = json_decode($json_setting, true);
                if (is_null($setting)) {
                    $ret['error']='bad parameter';
                    return $ret;
                }

                if(isset($setting['wpvivid_compress_setting']['max_file_size']))
                {
                    $setting['wpvivid_common_setting']['max_file_size'] = str_replace('M', '', $setting['wpvivid_compress_setting']['max_file_size']);
                }

                if(isset($setting['wpvivid_compress_setting']['exclude_file_size']))
                {
                    $setting['wpvivid_common_setting']['exclude_file_size'] = $setting['wpvivid_compress_setting']['exclude_file_size'];
                }

                WPvivid_Setting::update_setting($setting);
            }

            $ret['result']='success';
        }
        catch (Exception $error) {
            $message = 'An exception has occurred. class: '.get_class($error).';msg: '.$error->getMessage().';code: '.$error->getCode().';line: '.$error->getLine().';in_file: '.$error->getFile().';';
            error_log($message);
            return array('error'=>$message);
        }
        return $ret;
    }

    public function wpvivid_set_remote_mainwp($data){
        $remote = $data['remote'];
        global $wpvivid_plugin;
        $ret=array();
        try {
            if(isset($remote)&&!empty($remote)) {
                $json = $remote;
                $json = stripslashes($json);
                $remote = json_decode($json, true);
                if (is_null($remote)) {
                    $ret['error']='bad parameter';
                    return $ret;
                }
                $wpvivid_plugin->function_realize->_set_remote($remote);
            }
            $ret['result']='success';
        }
        catch (Exception $error) {
            $message = 'An exception has occurred. class: '.get_class($error).';msg: '.$error->getMessage().';code: '.$error->getCode().';line: '.$error->getLine().';in_file: '.$error->getFile().';';
            error_log($message);
            return array('error'=>$message);
        }
        return $ret;
    }
}includes/zip/class-wpvivid-pclzip.php000064400000615546151327705670014015 0ustar00<?php
// --------------------------------------------------------------------------------
// PhpConcept Library - Zip Module 2.8.2
// --------------------------------------------------------------------------------
// License GNU/LGPL - Vincent Blavet - August 2009
// http://www.phpconcept.net
// --------------------------------------------------------------------------------
//
// Presentation :
//   PclZip is a PHP library that manage ZIP archives.
//   So far tests show that archives generated by PclZip are readable by
//   WinZip application and other tools.
//
// Description :
//   See readme.txt and http://www.phpconcept.net
//
// Warning :
//   This library and the associated files are non commercial, non professional
//   work.
//   It should not have unexpected results. However if any damage is caused by
//   this software the author can not be responsible.
//   The use of this software is at the risk of the user.
//
// --------------------------------------------------------------------------------
// $Id: pclzip.lib.php,v 1.60 2009/09/30 21:01:04 vblavet Exp $
// --------------------------------------------------------------------------------

  // ----- Constants
  if (!defined('PCLZIP_READ_BLOCK_SIZE')) {
    define( 'PCLZIP_READ_BLOCK_SIZE', 2048 );
  }

  // ----- File list separator
  // In version 1.x of PclZip, the separator for file list is a space
  // (which is not a very smart choice, specifically for windows paths !).
  // A better separator should be a comma (,). This constant gives you the
  // ability to change that.
  // However notice that changing this value, may have impact on existing
  // scripts, using space separated filenames.
  // Recommended values for compatibility with older versions :
  //define( 'PCLZIP_SEPARATOR', ' ' );
  // Recommended values for smart separation of filenames.
  if (!defined('PCLZIP_SEPARATOR')) {
    define( 'PCLZIP_SEPARATOR', ',' );
  }

  // ----- Error configuration
  // 0 : PclZip Class integrated error handling
  // 1 : PclError external library error handling. By enabling this
  //     you must ensure that you have included PclError library.
  // [2,...] : reserved for futur use
  if (!defined('PCLZIP_ERROR_EXTERNAL')) {
    define( 'PCLZIP_ERROR_EXTERNAL', 0 );
  }

  // ----- Optional static temporary directory
  //       By default temporary files are generated in the script current
  //       path.
  //       If defined :
  //       - MUST BE terminated by a '/'.
  //       - MUST be a valid, already created directory
  //       Samples :
  // define( 'PCLZIP_TEMPORARY_DIR', '/temp/' );
  // define( 'PCLZIP_TEMPORARY_DIR', 'C:/Temp/' );
  if (!defined('PCLZIP_TEMPORARY_DIR')) {
    define( 'PCLZIP_TEMPORARY_DIR', '' );
  }

  // ----- Optional threshold ratio for use of temporary files
  //       Pclzip sense the size of the file to add/extract and decide to
  //       use or not temporary file. The algorithm is looking for
  //       memory_limit of PHP and apply a ratio.
  //       threshold = memory_limit * ratio.
  //       Recommended values are under 0.5. Default 0.47.
  //       Samples :
  // define( 'PCLZIP_TEMPORARY_FILE_RATIO', 0.5 );
  if (!defined('PCLZIP_TEMPORARY_FILE_RATIO')) {
    define( 'PCLZIP_TEMPORARY_FILE_RATIO', 0.47 );
  }

// --------------------------------------------------------------------------------
// ***** UNDER THIS LINE NOTHING NEEDS TO BE MODIFIED *****
// --------------------------------------------------------------------------------

  // ----- Global variables
  $g_pclzip_version = "2.8.2";

  // ----- Error codes
  //   -1 : Unable to open file in binary write mode
  //   -2 : Unable to open file in binary read mode
  //   -3 : Invalid parameters
  //   -4 : File does not exist
  //   -5 : Filename is too long (max. 255)
  //   -6 : Not a valid zip file
  //   -7 : Invalid extracted file size
  //   -8 : Unable to create directory
  //   -9 : Invalid archive extension
  //  -10 : Invalid archive format
  //  -11 : Unable to delete file (wp_delete_file)
  //  -12 : Unable to rename file (rename)
  //  -13 : Invalid header checksum
  //  -14 : Invalid archive size
  define( 'WPVIVID_PCLZIP_ERR_USER_ABORTED', 2 );
  define( 'WPVIVID_PCLZIP_ERR_NO_ERROR', 0 );
  define( 'WPVIVID_PCLZIP_ERR_WRITE_OPEN_FAIL', -1 );
  define( 'WPVIVID_PCLZIP_ERR_READ_OPEN_FAIL', -2 );
  define( 'WPVIVID_PCLZIP_ERR_INVALID_PARAMETER', -3 );
  define( 'WPVIVID_PCLZIP_ERR_MISSING_FILE', -4 );
  define( 'WPVIVID_PCLZIP_ERR_FILENAME_TOO_LONG', -5 );
  define( 'WPVIVID_PCLZIP_ERR_INVALID_ZIP', -6 );
  define( 'WPVIVID_PCLZIP_ERR_BAD_EXTRACTED_FILE', -7 );
  define( 'WPVIVID_PCLZIP_ERR_DIR_CREATE_FAIL', -8 );
  define( 'WPVIVID_PCLZIP_ERR_BAD_EXTENSION', -9 );
  define( 'WPVIVID_PCLZIP_ERR_BAD_FORMAT', -10 );
  define( 'WPVIVID_PCLZIP_ERR_DELETE_FILE_FAIL', -11 );
  define( 'WPVIVID_PCLZIP_ERR_RENAME_FILE_FAIL', -12 );
  define( 'WPVIVID_PCLZIP_ERR_BAD_CHECKSUM', -13 );
  define( 'WPVIVID_PCLZIP_ERR_INVALID_ARCHIVE_ZIP', -14 );
  define( 'WPVIVID_PCLZIP_ERR_MISSING_OPTION_VALUE', -15 );
  define( 'WPVIVID_PCLZIP_ERR_INVALID_OPTION_VALUE', -16 );
  define( 'WPVIVID_PCLZIP_ERR_ALREADY_A_DIRECTORY', -17 );
  define( 'WPVIVID_PCLZIP_ERR_UNSUPPORTED_COMPRESSION', -18 );
  define( 'WPVIVID_PCLZIP_ERR_UNSUPPORTED_ENCRYPTION', -19 );
  define( 'WPVIVID_PCLZIP_ERR_INVALID_ATTRIBUTE_VALUE', -20 );
  define( 'WPVIVID_PCLZIP_ERR_DIRECTORY_RESTRICTION', -21 );

  // ----- Options values
  define( 'WPVIVID_PCLZIP_OPT_PATH', 77001 );
  define( 'WPVIVID_PCLZIP_OPT_ADD_PATH', 77002 );
  define( 'WPVIVID_PCLZIP_OPT_REMOVE_PATH', 77003 );
  define( 'WPVIVID_PCLZIP_OPT_REMOVE_ALL_PATH', 77004 );
  define( 'WPVIVID_PCLZIP_OPT_SET_CHMOD', 77005 );
  define( 'WPVIVID_PCLZIP_OPT_EXTRACT_AS_STRING', 77006 );
  define( 'WPVIVID_PCLZIP_OPT_NO_COMPRESSION', 77007 );
  define( 'WPVIVID_PCLZIP_OPT_BY_NAME', 77008 );
  define( 'WPVIVID_PCLZIP_OPT_BY_INDEX', 77009 );
  define( 'WPVIVID_PCLZIP_OPT_BY_EREG', 77010 );
  define( 'WPVIVID_PCLZIP_OPT_BY_PREG', 77011 );
  define( 'WPVIVID_PCLZIP_OPT_COMMENT', 77012 );
  define( 'WPVIVID_PCLZIP_OPT_ADD_COMMENT', 77013 );
  define( 'WPVIVID_PCLZIP_OPT_PREPEND_COMMENT', 77014 );
  define( 'WPVIVID_PCLZIP_OPT_EXTRACT_IN_OUTPUT', 77015 );
  define( 'WPVIVID_PCLZIP_OPT_REPLACE_NEWER', 77016 );
  define( 'WPVIVID_PCLZIP_OPT_STOP_ON_ERROR', 77017 );
  // Having big trouble with crypt. Need to multiply 2 long int
  // which is not correctly supported by PHP ...
  //define( 'PCLZIP_OPT_CRYPT', 77018 );
  define( 'WPVIVID_PCLZIP_OPT_EXTRACT_DIR_RESTRICTION', 77019 );
  define( 'WPVIVID_PCLZIP_OPT_TEMP_FILE_THRESHOLD', 77020 );
  define( 'WPVIVID_PCLZIP_OPT_ADD_TEMP_FILE_THRESHOLD', 77020 ); // alias
  define( 'WPVIVID_PCLZIP_OPT_TEMP_FILE_ON', 77021 );
  define( 'WPVIVID_PCLZIP_OPT_ADD_TEMP_FILE_ON', 77021 ); // alias
  define( 'WPVIVID_PCLZIP_OPT_TEMP_FILE_OFF', 77022 );
  define( 'WPVIVID_PCLZIP_OPT_ADD_TEMP_FILE_OFF', 77022 ); // alias

  // ----- File description attributes
  define( 'WPVIVID_PCLZIP_ATT_FILE_NAME', 79001 );
  define( 'WPVIVID_PCLZIP_ATT_FILE_NEW_SHORT_NAME', 79002 );
  define( 'WPVIVID_PCLZIP_ATT_FILE_NEW_FULL_NAME', 79003 );
  define( 'WPVIVID_PCLZIP_ATT_FILE_MTIME', 79004 );
  define( 'WPVIVID_PCLZIP_ATT_FILE_CONTENT', 79005 );
  define( 'WPVIVID_PCLZIP_ATT_FILE_COMMENT', 79006 );

  // ----- Call backs values
  define( 'WPVIVID_PCLZIP_CB_PRE_EXTRACT', 78001 );
  define( 'WPVIVID_PCLZIP_CB_POST_EXTRACT', 78002 );
  define( 'WPVIVID_PCLZIP_CB_PRE_ADD', 78003 );
  define( 'WPVIVID_PCLZIP_CB_POST_ADD', 78004 );
  /* For futur use
  define( 'PCLZIP_CB_PRE_LIST', 78005 );
  define( 'PCLZIP_CB_POST_LIST', 78006 );
  define( 'PCLZIP_CB_PRE_DELETE', 78007 );
  define( 'PCLZIP_CB_POST_DELETE', 78008 );
  */

  // --------------------------------------------------------------------------------
  // Class : PclZip
  // Description :
  //   PclZip is the class that represent a Zip archive.
  //   The public methods allow the manipulation of the archive.
  // Attributes :
  //   Attributes must not be accessed directly.
  // Methods :
  //   PclZip() : Object creator
  //   create() : Creates the Zip archive
  //   listContent() : List the content of the Zip archive
  //   extract() : Extract the content of the archive
  //   properties() : List the properties of the archive
  // --------------------------------------------------------------------------------
  class WPvivid_PclZip
  {
    // ----- Filename of the zip file
    var $zipname = '';

    // ----- File descriptor of the zip file
    var $zip_fd = 0;

    // ----- Internal error handling
    var $error_code = 1;
    var $error_string = '';

    // ----- Current status of the magic_quotes_runtime
    // This value store the php configuration for magic_quotes
    // The class can then disable the magic_quotes and reset it after
    var $magic_quotes_status;

  // --------------------------------------------------------------------------------
  // Function : PclZip()
  // Description :
  //   Creates a PclZip object and set the name of the associated Zip archive
  //   filename.
  //   Note that no real action is taken, if the archive does not exist it is not
  //   created. Use create() for that.
  // --------------------------------------------------------------------------------
  function __construct($p_zipname)
  {

    // ----- Tests the zlib
    if (!function_exists('gzopen'))
    {
      die('Abort '.basename(__FILE__).' : Missing zlib extensions');
    }

    // ----- Set the attributes
    $this->zipname = $p_zipname;
    $this->zip_fd = 0;
    $this->magic_quotes_status = -1;

    // ----- Return
    return;
  }

  public function PclZip($p_zipname) {
    self::__construct($p_zipname);
  }
  // --------------------------------------------------------------------------------

  // --------------------------------------------------------------------------------
  // Function :
  //   create($p_filelist, $p_add_dir="", $p_remove_dir="")
  //   create($p_filelist, $p_option, $p_option_value, ...)
  // Description :
  //   This method supports two different synopsis. The first one is historical.
  //   This method creates a Zip Archive. The Zip file is created in the
  //   filesystem. The files and directories indicated in $p_filelist
  //   are added in the archive. See the parameters description for the
  //   supported format of $p_filelist.
  //   When a directory is in the list, the directory and its content is added
  //   in the archive.
  //   In this synopsis, the function takes an optional variable list of
  //   options. See below the supported options.
  // Parameters :
  //   $p_filelist : An array containing file or directory names, or
  //                 a string containing one filename or one directory name, or
  //                 a string containing a list of filenames and/or directory
  //                 names separated by spaces.
  //   $p_add_dir : A path to add before the real path of the archived file,
  //                in order to have it memorized in the archive.
  //   $p_remove_dir : A path to remove from the real path of the file to archive,
  //                   in order to have a shorter path memorized in the archive.
  //                   When $p_add_dir and $p_remove_dir are set, $p_remove_dir
  //                   is removed first, before $p_add_dir is added.
  // Options :
  //   WPVIVID_PCLZIP_OPT_ADD_PATH :
  //   WPVIVID_PCLZIP_OPT_REMOVE_PATH :
  //   WPVIVID_PCLZIP_OPT_REMOVE_ALL_PATH :
  //   WPVIVID_PCLZIP_OPT_COMMENT :
  //   WPVIVID_PCLZIP_CB_PRE_ADD :
  //   WPVIVID_PCLZIP_CB_POST_ADD :
  // Return Values :
  //   0 on failure,
  //   The list of the added files, with a status of the add action.
  //   (see PclZip::listContent() for list entry format)
  // --------------------------------------------------------------------------------
  function create($p_filelist)
  {
    $v_result=1;

    // ----- Reset the error handler
    $this->privErrorReset();

    // ----- Set default values
    $v_options = array();
    $v_options[WPVIVID_PCLZIP_OPT_NO_COMPRESSION] = FALSE;

    // ----- Look for variable options arguments
    $v_size = func_num_args();

    // ----- Look for arguments
    if ($v_size > 1) {
      // ----- Get the arguments
      $v_arg_list = func_get_args();

      // ----- Remove from the options list the first argument
      array_shift($v_arg_list);
      $v_size--;

      // ----- Look for first arg
      if ((is_integer($v_arg_list[0])) && ($v_arg_list[0] > 77000)) {

        // ----- Parse the options
        $v_result = $this->privParseOptions($v_arg_list, $v_size, $v_options,
                                            array (WPVIVID_PCLZIP_OPT_REMOVE_PATH => 'optional',
                                                   WPVIVID_PCLZIP_OPT_REMOVE_ALL_PATH => 'optional',
                                                   WPVIVID_PCLZIP_OPT_ADD_PATH => 'optional',
                                                   WPVIVID_PCLZIP_CB_PRE_ADD => 'optional',
                                                   WPVIVID_PCLZIP_CB_POST_ADD => 'optional',
                                                   WPVIVID_PCLZIP_OPT_NO_COMPRESSION => 'optional',
                                                   WPVIVID_PCLZIP_OPT_COMMENT => 'optional',
                                                   WPVIVID_PCLZIP_OPT_TEMP_FILE_THRESHOLD => 'optional',
                                                   WPVIVID_PCLZIP_OPT_TEMP_FILE_ON => 'optional',
                                                   WPVIVID_PCLZIP_OPT_TEMP_FILE_OFF => 'optional'
                                                   //, PCLZIP_OPT_CRYPT => 'optional'
                                             ));
        if ($v_result != 1) {
          return 0;
        }
      }

      // ----- Look for 2 args
      // Here we need to support the first historic synopsis of the
      // method.
      else {

        // ----- Get the first argument
        $v_options[WPVIVID_PCLZIP_OPT_ADD_PATH] = $v_arg_list[0];

        // ----- Look for the optional second argument
        if ($v_size == 2) {
          $v_options[WPVIVID_PCLZIP_OPT_REMOVE_PATH] = $v_arg_list[1];
        }
        else if ($v_size > 2) {
          WPvivid_PclZip::privErrorLog(WPVIVID_PCLZIP_ERR_INVALID_PARAMETER,
		                       "Invalid number / type of arguments");
          return 0;
        }
      }
    }

    // ----- Look for default option values
    $this->privOptionDefaultThreshold($v_options);

    // ----- Init
    $v_string_list = array();
    $v_att_list = array();
    $v_filedescr_list = array();
    $p_result_list = array();

    // ----- Look if the $p_filelist is really an array
    if (is_array($p_filelist)) {

      // ----- Look if the first element is also an array
      //       This will mean that this is a file description entry
      if (isset($p_filelist[0]) && is_array($p_filelist[0])) {
        $v_att_list = $p_filelist;
      }

      // ----- The list is a list of string names
      else {
        $v_string_list = $p_filelist;
      }
    }

    // ----- Look if the $p_filelist is a string
    else if (is_string($p_filelist)) {
      // ----- Create a list from the string
      $v_string_list = explode(PCLZIP_SEPARATOR, $p_filelist);
    }

    // ----- Invalid variable type for $p_filelist
    else {
      WPvivid_PclZip::privErrorLog(WPVIVID_PCLZIP_ERR_INVALID_PARAMETER, "Invalid variable type p_filelist");
      return 0;
    }

    // ----- Reformat the string list
    if (sizeof($v_string_list) != 0) {
      foreach ($v_string_list as $v_string) {
        if ($v_string != '') {
          $v_att_list[][WPVIVID_PCLZIP_ATT_FILE_NAME] = $v_string;
        }
        else {
        }
      }
    }

    // ----- For each file in the list check the attributes
    $v_supported_attributes
    = array ( WPVIVID_PCLZIP_ATT_FILE_NAME => 'mandatory'
             ,WPVIVID_PCLZIP_ATT_FILE_NEW_SHORT_NAME => 'optional'
             ,WPVIVID_PCLZIP_ATT_FILE_NEW_FULL_NAME => 'optional'
             ,WPVIVID_PCLZIP_ATT_FILE_MTIME => 'optional'
             ,WPVIVID_PCLZIP_ATT_FILE_CONTENT => 'optional'
             ,WPVIVID_PCLZIP_ATT_FILE_COMMENT => 'optional'
						);
    foreach ($v_att_list as $v_entry) {
      $v_result = $this->privFileDescrParseAtt($v_entry,
                                               $v_filedescr_list[],
                                               $v_options,
                                               $v_supported_attributes);
      if ($v_result != 1) {
        return 0;
      }
    }

    // ----- Expand the filelist (expand directories)
    $v_result = $this->privFileDescrExpand($v_filedescr_list, $v_options);
    if ($v_result != 1) {
      return 0;
    }

    // ----- Call the create fct
    $v_result = $this->privCreate($v_filedescr_list, $p_result_list, $v_options);
    if ($v_result != 1) {
      return 0;
    }

    // ----- Return
    return $p_result_list;
  }
  // --------------------------------------------------------------------------------

  // --------------------------------------------------------------------------------
  // Function :
  //   add($p_filelist, $p_add_dir="", $p_remove_dir="")
  //   add($p_filelist, $p_option, $p_option_value, ...)
  // Description :
  //   This method supports two synopsis. The first one is historical.
  //   This methods add the list of files in an existing archive.
  //   If a file with the same name already exists, it is added at the end of the
  //   archive, the first one is still present.
  //   If the archive does not exist, it is created.
  // Parameters :
  //   $p_filelist : An array containing file or directory names, or
  //                 a string containing one filename or one directory name, or
  //                 a string containing a list of filenames and/or directory
  //                 names separated by spaces.
  //   $p_add_dir : A path to add before the real path of the archived file,
  //                in order to have it memorized in the archive.
  //   $p_remove_dir : A path to remove from the real path of the file to archive,
  //                   in order to have a shorter path memorized in the archive.
  //                   When $p_add_dir and $p_remove_dir are set, $p_remove_dir
  //                   is removed first, before $p_add_dir is added.
  // Options :
  //   WPVIVID_PCLZIP_OPT_ADD_PATH :
  //   WPVIVID_PCLZIP_OPT_REMOVE_PATH :
  //   WPVIVID_PCLZIP_OPT_REMOVE_ALL_PATH :
  //   WPVIVID_PCLZIP_OPT_COMMENT :
  //   WPVIVID_PCLZIP_OPT_ADD_COMMENT :
  //   WPVIVID_PCLZIP_OPT_PREPEND_COMMENT :
  //   WPVIVID_PCLZIP_CB_PRE_ADD :
  //   WPVIVID_PCLZIP_CB_POST_ADD :
  // Return Values :
  //   0 on failure,
  //   The list of the added files, with a status of the add action.
  //   (see PclZip::listContent() for list entry format)
  // --------------------------------------------------------------------------------
  function add($p_filelist)
  {
    $v_result=1;

    // ----- Reset the error handler
    $this->privErrorReset();

    // ----- Set default values
    $v_options = array();
    $v_options[WPVIVID_PCLZIP_OPT_NO_COMPRESSION] = FALSE;

    // ----- Look for variable options arguments
    $v_size = func_num_args();

    // ----- Look for arguments
    if ($v_size > 1) {
      // ----- Get the arguments
      $v_arg_list = func_get_args();

      // ----- Remove form the options list the first argument
      array_shift($v_arg_list);
      $v_size--;

      // ----- Look for first arg
      if ((is_integer($v_arg_list[0])) && ($v_arg_list[0] > 77000)) {

        // ----- Parse the options
        $v_result = $this->privParseOptions($v_arg_list, $v_size, $v_options,
                                            array (WPVIVID_PCLZIP_OPT_REMOVE_PATH => 'optional',
                                                   WPVIVID_PCLZIP_OPT_REMOVE_ALL_PATH => 'optional',
                                                   WPVIVID_PCLZIP_OPT_ADD_PATH => 'optional',
                                                   WPVIVID_PCLZIP_CB_PRE_ADD => 'optional',
                                                   WPVIVID_PCLZIP_CB_POST_ADD => 'optional',
                                                   WPVIVID_PCLZIP_OPT_NO_COMPRESSION => 'optional',
                                                   WPVIVID_PCLZIP_OPT_COMMENT => 'optional',
                                                   WPVIVID_PCLZIP_OPT_ADD_COMMENT => 'optional',
                                                   WPVIVID_PCLZIP_OPT_PREPEND_COMMENT => 'optional',
                                                   WPVIVID_PCLZIP_OPT_TEMP_FILE_THRESHOLD => 'optional',
                                                   WPVIVID_PCLZIP_OPT_TEMP_FILE_ON => 'optional',
                                                   WPVIVID_PCLZIP_OPT_TEMP_FILE_OFF => 'optional'
                                                   //, PCLZIP_OPT_CRYPT => 'optional'
												   ));
        if ($v_result != 1) {
          return 0;
        }
      }

      // ----- Look for 2 args
      // Here we need to support the first historic synopsis of the
      // method.
      else {

        // ----- Get the first argument
        $v_options[WPVIVID_PCLZIP_OPT_ADD_PATH] = $v_add_path = $v_arg_list[0];

        // ----- Look for the optional second argument
        if ($v_size == 2) {
          $v_options[WPVIVID_PCLZIP_OPT_REMOVE_PATH] = $v_arg_list[1];
        }
        else if ($v_size > 2) {
          // ----- Error log
          WPvivid_PclZip::privErrorLog(WPVIVID_PCLZIP_ERR_INVALID_PARAMETER, "Invalid number / type of arguments");

          // ----- Return
          return 0;
        }
      }
    }

    // ----- Look for default option values
    $this->privOptionDefaultThreshold($v_options);

    // ----- Init
    $v_string_list = array();
    $v_att_list = array();
    $v_filedescr_list = array();
    $p_result_list = array();

    // ----- Look if the $p_filelist is really an array
    if (is_array($p_filelist)) {

      // ----- Look if the first element is also an array
      //       This will mean that this is a file description entry
      if (isset($p_filelist[0]) && is_array($p_filelist[0])) {
        $v_att_list = $p_filelist;
      }

      // ----- The list is a list of string names
      else {
        $v_string_list = $p_filelist;
      }
    }

    // ----- Look if the $p_filelist is a string
    else if (is_string($p_filelist)) {
      // ----- Create a list from the string
      $v_string_list = explode(PCLZIP_SEPARATOR, $p_filelist);
    }

    // ----- Invalid variable type for $p_filelist
    else {
      WPvivid_PclZip::privErrorLog(WPVIVID_PCLZIP_ERR_INVALID_PARAMETER, "Invalid variable type '".gettype($p_filelist)."' for p_filelist");
      return 0;
    }

    // ----- Reformat the string list
    if (sizeof($v_string_list) != 0) {
      foreach ($v_string_list as $v_string) {
        $v_att_list[][WPVIVID_PCLZIP_ATT_FILE_NAME] = $v_string;
      }
    }

    // ----- For each file in the list check the attributes
    $v_supported_attributes
    = array ( WPVIVID_PCLZIP_ATT_FILE_NAME => 'mandatory'
             ,WPVIVID_PCLZIP_ATT_FILE_NEW_SHORT_NAME => 'optional'
             ,WPVIVID_PCLZIP_ATT_FILE_NEW_FULL_NAME => 'optional'
             ,WPVIVID_PCLZIP_ATT_FILE_MTIME => 'optional'
             ,WPVIVID_PCLZIP_ATT_FILE_CONTENT => 'optional'
             ,WPVIVID_PCLZIP_ATT_FILE_COMMENT => 'optional'
						);
    foreach ($v_att_list as $v_entry) {
      $v_result = $this->privFileDescrParseAtt($v_entry,
                                               $v_filedescr_list[],
                                               $v_options,
                                               $v_supported_attributes);
      if ($v_result != 1) {
        return 0;
      }
    }

    // ----- Expand the filelist (expand directories)
    $v_result = $this->privFileDescrExpand($v_filedescr_list, $v_options);
    if ($v_result != 1) {
      return 0;
    }

    // ----- Call the create fct
    $v_result = $this->privAdd($v_filedescr_list, $p_result_list, $v_options);
    if ($v_result != 1) {
      return 0;
    }

    // ----- Return
    return $p_result_list;
  }
  // --------------------------------------------------------------------------------

  // --------------------------------------------------------------------------------
  // Function : listContent()
  // Description :
  //   This public method, gives the list of the files and directories, with their
  //   properties.
  //   The properties of each entries in the list are (used also in other functions) :
  //     filename : Name of the file. For a create or add action it is the filename
  //                given by the user. For an extract function it is the filename
  //                of the extracted file.
  //     stored_filename : Name of the file / directory stored in the archive.
  //     size : Size of the stored file.
  //     compressed_size : Size of the file's data compressed in the archive
  //                       (without the headers overhead)
  //     mtime : Last known modification date of the file (UNIX timestamp)
  //     comment : Comment associated with the file
  //     folder : true | false
  //     index : index of the file in the archive
  //     status : status of the action (depending of the action) :
  //              Values are :
  //                ok : OK !
  //                filtered : the file / dir is not extracted (filtered by user)
  //                already_a_directory : the file can not be extracted because a
  //                                      directory with the same name already exists
  //                write_protected : the file can not be extracted because a file
  //                                  with the same name already exists and is
  //                                  write protected
  //                newer_exist : the file was not extracted because a newer file exists
  //                path_creation_fail : the file is not extracted because the folder
  //                                     does not exist and can not be created
  //                write_error : the file was not extracted because there was a
  //                              error while writing the file
  //                read_error : the file was not extracted because there was a error
  //                             while reading the file
  //                invalid_header : the file was not extracted because of an archive
  //                                 format error (bad file header)
  //   Note that each time a method can continue operating when there
  //   is an action error on a file, the error is only logged in the file status.
  // Return Values :
  //   0 on an unrecoverable failure,
  //   The list of the files in the archive.
  // --------------------------------------------------------------------------------
  function listContent()
  {
    $v_result=1;

    // ----- Reset the error handler
    $this->privErrorReset();

    // ----- Check archive
    if (!$this->privCheckFormat()) {
      return(0);
    }

    // ----- Call the extracting fct
    $p_list = array();
    if (($v_result = $this->privList($p_list)) != 1)
    {
      unset($p_list);
      return(0);
    }

    // ----- Return
    return $p_list;
  }
  // --------------------------------------------------------------------------------

  // --------------------------------------------------------------------------------
  // Function :
  //   extract($p_path="./", $p_remove_path="")
  //   extract([$p_option, $p_option_value, ...])
  // Description :
  //   This method supports two synopsis. The first one is historical.
  //   This method extract all the files / directories from the archive to the
  //   folder indicated in $p_path.
  //   If you want to ignore the 'root' part of path of the memorized files
  //   you can indicate this in the optional $p_remove_path parameter.
  //   By default, if a newer file with the same name already exists, the
  //   file is not extracted.
  //
  //   If both WPVIVID_PCLZIP_OPT_PATH and WPVIVID_PCLZIP_OPT_ADD_PATH options
  //   are used, the path indicated in WPVIVID_PCLZIP_OPT_ADD_PATH is append
  //   at the end of the path value of WPVIVID_PCLZIP_OPT_PATH.
  // Parameters :
  //   $p_path : Path where the files and directories are to be extracted
  //   $p_remove_path : First part ('root' part) of the memorized path
  //                    (if any similar) to remove while extracting.
  // Options :
  //   WPVIVID_PCLZIP_OPT_PATH :
  //   WPVIVID_PCLZIP_OPT_ADD_PATH :
  //   WPVIVID_PCLZIP_OPT_REMOVE_PATH :
  //   WPVIVID_PCLZIP_OPT_REMOVE_ALL_PATH :
  //   WPVIVID_PCLZIP_CB_PRE_EXTRACT :
  //   WPVIVID_PCLZIP_CB_POST_EXTRACT :
  // Return Values :
  //   0 or a negative value on failure,
  //   The list of the extracted files, with a status of the action.
  //   (see PclZip::listContent() for list entry format)
  // --------------------------------------------------------------------------------
  function extract()
  {
    $v_result=1;

    // ----- Reset the error handler
    $this->privErrorReset();

    // ----- Check archive
    if (!$this->privCheckFormat()) {
      return(0);
    }

    // ----- Set default values
    $v_options = array();
//    $v_path = "./";
    $v_path = '';
    $v_remove_path = "";
    $v_remove_all_path = false;

    // ----- Look for variable options arguments
    $v_size = func_num_args();

    // ----- Default values for option
    $v_options[WPVIVID_PCLZIP_OPT_EXTRACT_AS_STRING] = FALSE;

    // ----- Look for arguments
    if ($v_size > 0) {
      // ----- Get the arguments
      $v_arg_list = func_get_args();

      // ----- Look for first arg
      if ((is_integer($v_arg_list[0])) && ($v_arg_list[0] > 77000)) {

        // ----- Parse the options
        $v_result = $this->privParseOptions($v_arg_list, $v_size, $v_options,
                                            array (WPVIVID_PCLZIP_OPT_PATH => 'optional',
                                                   WPVIVID_PCLZIP_OPT_REMOVE_PATH => 'optional',
                                                   WPVIVID_PCLZIP_OPT_REMOVE_ALL_PATH => 'optional',
                                                   WPVIVID_PCLZIP_OPT_ADD_PATH => 'optional',
                                                   WPVIVID_PCLZIP_CB_PRE_EXTRACT => 'optional',
                                                   WPVIVID_PCLZIP_CB_POST_EXTRACT => 'optional',
                                                   WPVIVID_PCLZIP_OPT_SET_CHMOD => 'optional',
                                                   WPVIVID_PCLZIP_OPT_BY_NAME => 'optional',
                                                   WPVIVID_PCLZIP_OPT_BY_EREG => 'optional',
                                                   WPVIVID_PCLZIP_OPT_BY_PREG => 'optional',
                                                   WPVIVID_PCLZIP_OPT_BY_INDEX => 'optional',
                                                   WPVIVID_PCLZIP_OPT_EXTRACT_AS_STRING => 'optional',
                                                   WPVIVID_PCLZIP_OPT_EXTRACT_IN_OUTPUT => 'optional',
                                                   WPVIVID_PCLZIP_OPT_REPLACE_NEWER => 'optional'
                                                   ,WPVIVID_PCLZIP_OPT_STOP_ON_ERROR => 'optional'
                                                   ,WPVIVID_PCLZIP_OPT_EXTRACT_DIR_RESTRICTION => 'optional',
                                                   WPVIVID_PCLZIP_OPT_TEMP_FILE_THRESHOLD => 'optional',
                                                   WPVIVID_PCLZIP_OPT_TEMP_FILE_ON => 'optional',
                                                   WPVIVID_PCLZIP_OPT_TEMP_FILE_OFF => 'optional'
												    ));
        if ($v_result != 1) {
          return 0;
        }

        // ----- Set the arguments
        if (isset($v_options[WPVIVID_PCLZIP_OPT_PATH])) {
          $v_path = $v_options[WPVIVID_PCLZIP_OPT_PATH];
        }
        if (isset($v_options[WPVIVID_PCLZIP_OPT_REMOVE_PATH])) {
          $v_remove_path = $v_options[WPVIVID_PCLZIP_OPT_REMOVE_PATH];
        }
        if (isset($v_options[WPVIVID_PCLZIP_OPT_REMOVE_ALL_PATH])) {
          $v_remove_all_path = $v_options[WPVIVID_PCLZIP_OPT_REMOVE_ALL_PATH];
        }
        if (isset($v_options[WPVIVID_PCLZIP_OPT_ADD_PATH])) {
          // ----- Check for '/' in last path char
          if ((strlen($v_path) > 0) && (substr($v_path, -1) != '/')) {
            $v_path .= '/';
          }
          $v_path .= $v_options[WPVIVID_PCLZIP_OPT_ADD_PATH];
        }
      }

      // ----- Look for 2 args
      // Here we need to support the first historic synopsis of the
      // method.
      else {

        // ----- Get the first argument
        $v_path = $v_arg_list[0];

        // ----- Look for the optional second argument
        if ($v_size == 2) {
          $v_remove_path = $v_arg_list[1];
        }
        else if ($v_size > 2) {
          // ----- Error log
          WPvivid_PclZip::privErrorLog(WPVIVID_PCLZIP_ERR_INVALID_PARAMETER, "Invalid number / type of arguments");

          // ----- Return
          return 0;
        }
      }
    }

    // ----- Look for default option values
    $this->privOptionDefaultThreshold($v_options);

    // ----- Trace

    // ----- Call the extracting fct
    $p_list = array();
    $v_result = $this->privExtractByRule($p_list, $v_path, $v_remove_path,
	                                     $v_remove_all_path, $v_options);
    if ($v_result < 1) {
      unset($p_list);
      return(0);
    }

    // ----- Return
    return $p_list;
  }
  // --------------------------------------------------------------------------------


  // --------------------------------------------------------------------------------
  // Function :
  //   extractByIndex($p_index, $p_path="./", $p_remove_path="")
  //   extractByIndex($p_index, [$p_option, $p_option_value, ...])
  // Description :
  //   This method supports two synopsis. The first one is historical.
  //   This method is doing a partial extract of the archive.
  //   The extracted files or folders are identified by their index in the
  //   archive (from 0 to n).
  //   Note that if the index identify a folder, only the folder entry is
  //   extracted, not all the files included in the archive.
  // Parameters :
  //   $p_index : A single index (integer) or a string of indexes of files to
  //              extract. The form of the string is "0,4-6,8-12" with only numbers
  //              and '-' for range or ',' to separate ranges. No spaces or ';'
  //              are allowed.
  //   $p_path : Path where the files and directories are to be extracted
  //   $p_remove_path : First part ('root' part) of the memorized path
  //                    (if any similar) to remove while extracting.
  // Options :
  //   WPVIVID_PCLZIP_OPT_PATH :
  //   WPVIVID_PCLZIP_OPT_ADD_PATH :
  //   WPVIVID_PCLZIP_OPT_REMOVE_PATH :
  //   WPVIVID_PCLZIP_OPT_REMOVE_ALL_PATH :
  //   WPVIVID_PCLZIP_OPT_EXTRACT_AS_STRING : The files are extracted as strings and
  //     not as files.
  //     The resulting content is in a new field 'content' in the file
  //     structure.
  //     This option must be used alone (any other options are ignored).
  //   WPVIVID_PCLZIP_CB_PRE_EXTRACT :
  //   WPVIVID_PCLZIP_CB_POST_EXTRACT :
  // Return Values :
  //   0 on failure,
  //   The list of the extracted files, with a status of the action.
  //   (see PclZip::listContent() for list entry format)
  // --------------------------------------------------------------------------------
  //function extractByIndex($p_index, options...)
  function extractByIndex($p_index)
  {
    $v_result=1;

    // ----- Reset the error handler
    $this->privErrorReset();

    // ----- Check archive
    if (!$this->privCheckFormat()) {
      return(0);
    }

    // ----- Set default values
    $v_options = array();
//    $v_path = "./";
    $v_path = '';
    $v_remove_path = "";
    $v_remove_all_path = false;

    // ----- Look for variable options arguments
    $v_size = func_num_args();

    // ----- Default values for option
    $v_options[WPVIVID_PCLZIP_OPT_EXTRACT_AS_STRING] = FALSE;

    // ----- Look for arguments
    if ($v_size > 1) {
      // ----- Get the arguments
      $v_arg_list = func_get_args();

      // ----- Remove form the options list the first argument
      array_shift($v_arg_list);
      $v_size--;

      // ----- Look for first arg
      if ((is_integer($v_arg_list[0])) && ($v_arg_list[0] > 77000)) {

        // ----- Parse the options
        $v_result = $this->privParseOptions($v_arg_list, $v_size, $v_options,
                                            array (WPVIVID_PCLZIP_OPT_PATH => 'optional',
                                                   WPVIVID_PCLZIP_OPT_REMOVE_PATH => 'optional',
                                                   WPVIVID_PCLZIP_OPT_REMOVE_ALL_PATH => 'optional',
                                                   WPVIVID_PCLZIP_OPT_EXTRACT_AS_STRING => 'optional',
                                                   WPVIVID_PCLZIP_OPT_ADD_PATH => 'optional',
                                                   WPVIVID_PCLZIP_CB_PRE_EXTRACT => 'optional',
                                                   WPVIVID_PCLZIP_CB_POST_EXTRACT => 'optional',
                                                   WPVIVID_PCLZIP_OPT_SET_CHMOD => 'optional',
                                                   WPVIVID_PCLZIP_OPT_REPLACE_NEWER => 'optional'
                                                   ,WPVIVID_PCLZIP_OPT_STOP_ON_ERROR => 'optional'
                                                   ,WPVIVID_PCLZIP_OPT_EXTRACT_DIR_RESTRICTION => 'optional',
                                                   WPVIVID_PCLZIP_OPT_TEMP_FILE_THRESHOLD => 'optional',
                                                   WPVIVID_PCLZIP_OPT_TEMP_FILE_ON => 'optional',
                                                   WPVIVID_PCLZIP_OPT_TEMP_FILE_OFF => 'optional'
												   ));
        if ($v_result != 1) {
          return 0;
        }

        // ----- Set the arguments
        if (isset($v_options[WPVIVID_PCLZIP_OPT_PATH])) {
          $v_path = $v_options[WPVIVID_PCLZIP_OPT_PATH];
        }
        if (isset($v_options[WPVIVID_PCLZIP_OPT_REMOVE_PATH])) {
          $v_remove_path = $v_options[WPVIVID_PCLZIP_OPT_REMOVE_PATH];
        }
        if (isset($v_options[WPVIVID_PCLZIP_OPT_REMOVE_ALL_PATH])) {
          $v_remove_all_path = $v_options[WPVIVID_PCLZIP_OPT_REMOVE_ALL_PATH];
        }
        if (isset($v_options[WPVIVID_PCLZIP_OPT_ADD_PATH])) {
          // ----- Check for '/' in last path char
          if ((strlen($v_path) > 0) && (substr($v_path, -1) != '/')) {
            $v_path .= '/';
          }
          $v_path .= $v_options[WPVIVID_PCLZIP_OPT_ADD_PATH];
        }
        if (!isset($v_options[WPVIVID_PCLZIP_OPT_EXTRACT_AS_STRING])) {
          $v_options[WPVIVID_PCLZIP_OPT_EXTRACT_AS_STRING] = FALSE;
        }
        else {
        }
      }

      // ----- Look for 2 args
      // Here we need to support the first historic synopsis of the
      // method.
      else {

        // ----- Get the first argument
        $v_path = $v_arg_list[0];

        // ----- Look for the optional second argument
        if ($v_size == 2) {
          $v_remove_path = $v_arg_list[1];
        }
        else if ($v_size > 2) {
          // ----- Error log
          WPvivid_PclZip::privErrorLog(WPVIVID_PCLZIP_ERR_INVALID_PARAMETER, "Invalid number / type of arguments");

          // ----- Return
          return 0;
        }
      }
    }

    // ----- Trace

    // ----- Trick
    // Here I want to reuse extractByRule(), so I need to parse the $p_index
    // with privParseOptions()
    $v_arg_trick = array (WPVIVID_PCLZIP_OPT_BY_INDEX, $p_index);
    $v_options_trick = array();
    $v_result = $this->privParseOptions($v_arg_trick, sizeof($v_arg_trick), $v_options_trick,
                                        array (WPVIVID_PCLZIP_OPT_BY_INDEX => 'optional' ));
    if ($v_result != 1) {
        return 0;
    }
    $v_options[WPVIVID_PCLZIP_OPT_BY_INDEX] = $v_options_trick[WPVIVID_PCLZIP_OPT_BY_INDEX];

    // ----- Look for default option values
    $this->privOptionDefaultThreshold($v_options);

    // ----- Call the extracting fct
    if (($v_result = $this->privExtractByRule($p_list, $v_path, $v_remove_path, $v_remove_all_path, $v_options)) < 1) {
        return(0);
    }

    // ----- Return
    return $p_list;
  }
  // --------------------------------------------------------------------------------

  // --------------------------------------------------------------------------------
  // Function :
  //   delete([$p_option, $p_option_value, ...])
  // Description :
  //   This method removes files from the archive.
  //   If no parameters are given, then all the archive is emptied.
  // Parameters :
  //   None or optional arguments.
  // Options :
  //   WPVIVID_PCLZIP_OPT_BY_INDEX :
  //   WPVIVID_PCLZIP_OPT_BY_NAME :
  //   WPVIVID_PCLZIP_OPT_BY_EREG :
  //   WPVIVID_PCLZIP_OPT_BY_PREG :
  // Return Values :
  //   0 on failure,
  //   The list of the files which are still present in the archive.
  //   (see PclZip::listContent() for list entry format)
  // --------------------------------------------------------------------------------
  function delete()
  {
    $v_result=1;

    // ----- Reset the error handler
    $this->privErrorReset();

    // ----- Check archive
    if (!$this->privCheckFormat()) {
      return(0);
    }

    // ----- Set default values
    $v_options = array();

    // ----- Look for variable options arguments
    $v_size = func_num_args();

    // ----- Look for arguments
    if ($v_size > 0) {
      // ----- Get the arguments
      $v_arg_list = func_get_args();

      // ----- Parse the options
      $v_result = $this->privParseOptions($v_arg_list, $v_size, $v_options,
                                        array (WPVIVID_PCLZIP_OPT_BY_NAME => 'optional',
                                               WPVIVID_PCLZIP_OPT_BY_EREG => 'optional',
                                               WPVIVID_PCLZIP_OPT_BY_PREG => 'optional',
                                               WPVIVID_PCLZIP_OPT_BY_INDEX => 'optional' ));
      if ($v_result != 1) {
          return 0;
      }
    }

    // ----- Magic quotes trick
    $this->privDisableMagicQuotes();

    // ----- Call the delete fct
    $v_list = array();
    if (($v_result = $this->privDeleteByRule($v_list, $v_options)) != 1) {
      $this->privSwapBackMagicQuotes();
      unset($v_list);
      return(0);
    }

    // ----- Magic quotes trick
    $this->privSwapBackMagicQuotes();

    // ----- Return
    return $v_list;
  }
  // --------------------------------------------------------------------------------

  // --------------------------------------------------------------------------------
  // Function : deleteByIndex()
  // Description :
  //   ***** Deprecated *****
  //   delete(WPVIVID_PCLZIP_OPT_BY_INDEX, $p_index) should be preferred.
  // --------------------------------------------------------------------------------
  function deleteByIndex($p_index)
  {

    $p_list = $this->delete(WPVIVID_PCLZIP_OPT_BY_INDEX, $p_index);

    // ----- Return
    return $p_list;
  }
  // --------------------------------------------------------------------------------

  // --------------------------------------------------------------------------------
  // Function : properties()
  // Description :
  //   This method gives the properties of the archive.
  //   The properties are :
  //     nb : Number of files in the archive
  //     comment : Comment associated with the archive file
  //     status : not_exist, ok
  // Parameters :
  //   None
  // Return Values :
  //   0 on failure,
  //   An array with the archive properties.
  // --------------------------------------------------------------------------------
  function properties()
  {

    // ----- Reset the error handler
    $this->privErrorReset();

    // ----- Magic quotes trick
    $this->privDisableMagicQuotes();

    // ----- Check archive
    if (!$this->privCheckFormat()) {
      $this->privSwapBackMagicQuotes();
      return(0);
    }

    // ----- Default properties
    $v_prop = array();
    $v_prop['comment'] = '';
    $v_prop['nb'] = 0;
    $v_prop['status'] = 'not_exist';

    // ----- Look if file exists
    if (@is_file($this->zipname))
    {
      // ----- Open the zip file
      if (($this->zip_fd = @fopen($this->zipname, 'rb')) == 0)
      {
        $this->privSwapBackMagicQuotes();

        // ----- Error log
        WPvivid_PclZip::privErrorLog(WPVIVID_PCLZIP_ERR_READ_OPEN_FAIL, 'Unable to open archive \''.$this->zipname.'\' in binary read mode');

        // ----- Return
        return 0;
      }

      // ----- Read the central directory information
      $v_central_dir = array();
      if (($v_result = $this->privReadEndCentralDir($v_central_dir)) != 1)
      {
        $this->privSwapBackMagicQuotes();
        return 0;
      }

      // ----- Close the zip file
      $this->privCloseFd();

      // ----- Set the user attributes
      $v_prop['comment'] = $v_central_dir['comment'];
      $v_prop['nb'] = $v_central_dir['entries'];
      $v_prop['status'] = 'ok';
    }

    // ----- Magic quotes trick
    $this->privSwapBackMagicQuotes();

    // ----- Return
    return $v_prop;
  }
  // --------------------------------------------------------------------------------

  // --------------------------------------------------------------------------------
  // Function : duplicate()
  // Description :
  //   This method creates an archive by copying the content of an other one. If
  //   the archive already exist, it is replaced by the new one without any warning.
  // Parameters :
  //   $p_archive : The filename of a valid archive, or
  //                a valid PclZip object.
  // Return Values :
  //   1 on success.
  //   0 or a negative value on error (error code).
  // --------------------------------------------------------------------------------
  function duplicate($p_archive)
  {
    $v_result = 1;

    // ----- Reset the error handler
    $this->privErrorReset();

    // ----- Look if the $p_archive is a PclZip object
    if (is_object($p_archive) && $p_archive instanceof pclzip)
    {

      // ----- Duplicate the archive
      $v_result = $this->privDuplicate($p_archive->zipname);
    }

    // ----- Look if the $p_archive is a string (so a filename)
    else if (is_string($p_archive))
    {

      // ----- Check that $p_archive is a valid zip file
      // TBC : Should also check the archive format
      if (!is_file($p_archive)) {
        // ----- Error log
        WPvivid_PclZip::privErrorLog(WPVIVID_PCLZIP_ERR_MISSING_FILE, "No file with filename '".$p_archive."'");
        $v_result = WPVIVID_PCLZIP_ERR_MISSING_FILE;
      }
      else {
        // ----- Duplicate the archive
        $v_result = $this->privDuplicate($p_archive);
      }
    }

    // ----- Invalid variable
    else
    {
      // ----- Error log
      WPvivid_PclZip::privErrorLog(WPVIVID_PCLZIP_ERR_INVALID_PARAMETER, "Invalid variable type p_archive_to_add");
      $v_result = WPVIVID_PCLZIP_ERR_INVALID_PARAMETER;
    }

    // ----- Return
    return $v_result;
  }
  // --------------------------------------------------------------------------------

  // --------------------------------------------------------------------------------
  // Function : merge()
  // Description :
  //   This method merge the $p_archive_to_add archive at the end of the current
  //   one ($this).
  //   If the archive ($this) does not exist, the merge becomes a duplicate.
  //   If the $p_archive_to_add archive does not exist, the merge is a success.
  // Parameters :
  //   $p_archive_to_add : It can be directly the filename of a valid zip archive,
  //                       or a PclZip object archive.
  // Return Values :
  //   1 on success,
  //   0 or negative values on error (see below).
  // --------------------------------------------------------------------------------
  function merge($p_archive_to_add)
  {
    $v_result = 1;

    // ----- Reset the error handler
    $this->privErrorReset();

    // ----- Check archive
    if (!$this->privCheckFormat()) {
      return(0);
    }

    // ----- Look if the $p_archive_to_add is a PclZip object
    if (is_object($p_archive_to_add) && $p_archive_to_add instanceof pclzip)
    {

      // ----- Merge the archive
      $v_result = $this->privMerge($p_archive_to_add);
    }

    // ----- Look if the $p_archive_to_add is a string (so a filename)
    else if (is_string($p_archive_to_add))
    {

      // ----- Create a temporary archive
      $v_object_archive = new WPvivid_PclZip($p_archive_to_add);

      // ----- Merge the archive
      $v_result = $this->privMerge($v_object_archive);
    }

    // ----- Invalid variable
    else
    {
      // ----- Error log
      WPvivid_PclZip::privErrorLog(WPVIVID_PCLZIP_ERR_INVALID_PARAMETER, "Invalid variable type p_archive_to_add");
      $v_result = WPVIVID_PCLZIP_ERR_INVALID_PARAMETER;
    }

    // ----- Return
    return $v_result;
  }
  // --------------------------------------------------------------------------------



  // --------------------------------------------------------------------------------
  // Function : errorCode()
  // Description :
  // Parameters :
  // --------------------------------------------------------------------------------
  function errorCode()
  {
    if (PCLZIP_ERROR_EXTERNAL == 1) {
      return(PclErrorCode());
    }
    else {
      return($this->error_code);
    }
  }
  // --------------------------------------------------------------------------------

  // --------------------------------------------------------------------------------
  // Function : errorName()
  // Description :
  // Parameters :
  // --------------------------------------------------------------------------------
  function errorName($p_with_code=false)
  {
    $v_name = array ( WPVIVID_PCLZIP_ERR_NO_ERROR => 'WPVIVID_PCLZIP_ERR_NO_ERROR',
                      WPVIVID_PCLZIP_ERR_WRITE_OPEN_FAIL => 'WPVIVID_PCLZIP_ERR_WRITE_OPEN_FAIL',
                      WPVIVID_PCLZIP_ERR_READ_OPEN_FAIL => 'WPVIVID_PCLZIP_ERR_READ_OPEN_FAIL',
                      WPVIVID_PCLZIP_ERR_INVALID_PARAMETER => 'WPVIVID_PCLZIP_ERR_INVALID_PARAMETER',
                      WPVIVID_PCLZIP_ERR_MISSING_FILE => 'WPVIVID_PCLZIP_ERR_MISSING_FILE',
                      WPVIVID_PCLZIP_ERR_FILENAME_TOO_LONG => 'WPVIVID_PCLZIP_ERR_FILENAME_TOO_LONG',
                      WPVIVID_PCLZIP_ERR_INVALID_ZIP => 'WPVIVID_PCLZIP_ERR_INVALID_ZIP',
                      WPVIVID_PCLZIP_ERR_BAD_EXTRACTED_FILE => 'WPVIVID_PCLZIP_ERR_BAD_EXTRACTED_FILE',
                      WPVIVID_PCLZIP_ERR_DIR_CREATE_FAIL => 'WPVIVID_PCLZIP_ERR_DIR_CREATE_FAIL',
                      WPVIVID_PCLZIP_ERR_BAD_EXTENSION => 'WPVIVID_PCLZIP_ERR_BAD_EXTENSION',
                      WPVIVID_PCLZIP_ERR_BAD_FORMAT => 'WPVIVID_PCLZIP_ERR_BAD_FORMAT',
                      WPVIVID_PCLZIP_ERR_DELETE_FILE_FAIL => 'WPVIVID_PCLZIP_ERR_DELETE_FILE_FAIL',
                      WPVIVID_PCLZIP_ERR_RENAME_FILE_FAIL => 'WPVIVID_PCLZIP_ERR_RENAME_FILE_FAIL',
                      WPVIVID_PCLZIP_ERR_BAD_CHECKSUM => 'WPVIVID_PCLZIP_ERR_BAD_CHECKSUM',
                      WPVIVID_PCLZIP_ERR_INVALID_ARCHIVE_ZIP => 'WPVIVID_PCLZIP_ERR_INVALID_ARCHIVE_ZIP',
                      WPVIVID_PCLZIP_ERR_MISSING_OPTION_VALUE => 'WPVIVID_PCLZIP_ERR_MISSING_OPTION_VALUE',
                      WPVIVID_PCLZIP_ERR_INVALID_OPTION_VALUE => 'WPVIVID_PCLZIP_ERR_INVALID_OPTION_VALUE',
                      WPVIVID_PCLZIP_ERR_UNSUPPORTED_COMPRESSION => 'WPVIVID_PCLZIP_ERR_UNSUPPORTED_COMPRESSION',
                      WPVIVID_PCLZIP_ERR_UNSUPPORTED_ENCRYPTION => 'WPVIVID_PCLZIP_ERR_UNSUPPORTED_ENCRYPTION'
                      ,WPVIVID_PCLZIP_ERR_INVALID_ATTRIBUTE_VALUE => 'WPVIVID_PCLZIP_ERR_INVALID_ATTRIBUTE_VALUE'
                      ,WPVIVID_PCLZIP_ERR_DIRECTORY_RESTRICTION => 'WPVIVID_PCLZIP_ERR_DIRECTORY_RESTRICTION'
                    );

    if (isset($v_name[$this->error_code])) {
      $v_value = $v_name[$this->error_code];
    }
    else {
      $v_value = 'NoName';
    }

    if ($p_with_code) {
      return($v_value.' ('.$this->error_code.')');
    }
    else {
      return($v_value);
    }
  }
  // --------------------------------------------------------------------------------

  // --------------------------------------------------------------------------------
  // Function : errorInfo()
  // Description :
  // Parameters :
  // --------------------------------------------------------------------------------
  function errorInfo($p_full=false)
  {
    if (PCLZIP_ERROR_EXTERNAL == 1) {
      return(PclErrorString());
    }
    else {
      if ($p_full) {
        return($this->errorName(true)." : ".$this->error_string);
      }
      else {
        return($this->error_string." [code ".$this->error_code."]");
      }
    }
  }
  // --------------------------------------------------------------------------------


// --------------------------------------------------------------------------------
// ***** UNDER THIS LINE ARE DEFINED PRIVATE INTERNAL FUNCTIONS *****
// *****                                                        *****
// *****       THESES FUNCTIONS MUST NOT BE USED DIRECTLY       *****
// --------------------------------------------------------------------------------



  // --------------------------------------------------------------------------------
  // Function : privCheckFormat()
  // Description :
  //   This method check that the archive exists and is a valid zip archive.
  //   Several level of check exists. (futur)
  // Parameters :
  //   $p_level : Level of check. Default 0.
  //              0 : Check the first bytes (magic codes) (default value))
  //              1 : 0 + Check the central directory (futur)
  //              2 : 1 + Check each file header (futur)
  // Return Values :
  //   true on success,
  //   false on error, the error code is set.
  // --------------------------------------------------------------------------------
  function privCheckFormat($p_level=0)
  {
    $v_result = true;

	// ----- Reset the file system cache
    clearstatcache();

    // ----- Reset the error handler
    $this->privErrorReset();

    // ----- Look if the file exits
    if (!is_file($this->zipname)) {
      // ----- Error log
      WPvivid_PclZip::privErrorLog(WPVIVID_PCLZIP_ERR_MISSING_FILE, "Missing archive file '".$this->zipname."'");
      return(false);
    }

    // ----- Check that the file is readable
    if (!is_readable($this->zipname)) {
      // ----- Error log
      WPvivid_PclZip::privErrorLog(WPVIVID_PCLZIP_ERR_READ_OPEN_FAIL, "Unable to read archive '".$this->zipname."'");
      return(false);
    }

    // ----- Check the magic code
    // TBC

    // ----- Check the central header
    // TBC

    // ----- Check each file header
    // TBC

    // ----- Return
    return $v_result;
  }
  // --------------------------------------------------------------------------------

  // --------------------------------------------------------------------------------
  // Function : privParseOptions()
  // Description :
  //   This internal methods reads the variable list of arguments ($p_options_list,
  //   $p_size) and generate an array with the options and values ($v_result_list).
  //   $v_requested_options contains the options that can be present and those that
  //   must be present.
  //   $v_requested_options is an array, with the option value as key, and 'optional',
  //   or 'mandatory' as value.
  // Parameters :
  //   See above.
  // Return Values :
  //   1 on success.
  //   0 on failure.
  // --------------------------------------------------------------------------------
  function privParseOptions(&$p_options_list, $p_size, &$v_result_list, $v_requested_options=false)
  {
    $v_result=1;

    // ----- Read the options
    $i=0;
    while ($i<$p_size) {

      // ----- Check if the option is supported
      if (!isset($v_requested_options[$p_options_list[$i]])) {
        // ----- Error log
        WPvivid_PclZip::privErrorLog(WPVIVID_PCLZIP_ERR_INVALID_PARAMETER, "Invalid optional parameter '".$p_options_list[$i]."' for this method");

        // ----- Return
        return WPvivid_PclZip::errorCode();
      }

      // ----- Look for next option
      switch ($p_options_list[$i]) {
        // ----- Look for options that request a path value
        case WPVIVID_PCLZIP_OPT_PATH :
        case WPVIVID_PCLZIP_OPT_REMOVE_PATH :
        case WPVIVID_PCLZIP_OPT_ADD_PATH :
          // ----- Check the number of parameters
          if (($i+1) >= $p_size) {
            // ----- Error log
            WPvivid_PclZip::privErrorLog(WPVIVID_PCLZIP_ERR_MISSING_OPTION_VALUE, "Missing parameter value for option '".WPvivid_PclZipUtilOptionText($p_options_list[$i])."'");

            // ----- Return
            return WPvivid_PclZip::errorCode();
          }

          // ----- Get the value
          $v_result_list[$p_options_list[$i]] = WPvivid_PclZipUtilTranslateWinPath($p_options_list[$i+1], FALSE);
          $i++;
        break;

        case WPVIVID_PCLZIP_OPT_TEMP_FILE_THRESHOLD :
          // ----- Check the number of parameters
          if (($i+1) >= $p_size) {
            WPvivid_PclZip::privErrorLog(WPVIVID_PCLZIP_ERR_MISSING_OPTION_VALUE, "Missing parameter value for option '".WPvivid_PclZipUtilOptionText($p_options_list[$i])."'");
            return WPvivid_PclZip::errorCode();
          }

          // ----- Check for incompatible options
          if (isset($v_result_list[WPVIVID_PCLZIP_OPT_TEMP_FILE_OFF])) {
            WPvivid_PclZip::privErrorLog(WPVIVID_PCLZIP_ERR_INVALID_PARAMETER, "Option '".WPvivid_PclZipUtilOptionText($p_options_list[$i])."' can not be used with option 'WPVIVID_PCLZIP_OPT_TEMP_FILE_OFF'");
            return WPvivid_PclZip::errorCode();
          }

          // ----- Check the value
          $v_value = $p_options_list[$i+1];
          if ((!is_integer($v_value)) || ($v_value<0)) {
            WPvivid_PclZip::privErrorLog(WPVIVID_PCLZIP_ERR_INVALID_OPTION_VALUE, "Integer expected for option '".WPvivid_PclZipUtilOptionText($p_options_list[$i])."'");
            return WPvivid_PclZip::errorCode();
          }

          // ----- Get the value (and convert it in bytes)
          $v_result_list[$p_options_list[$i]] = $v_value*1048576;
          $i++;
        break;

        case WPVIVID_PCLZIP_OPT_TEMP_FILE_ON :
          // ----- Check for incompatible options
          if (isset($v_result_list[WPVIVID_PCLZIP_OPT_TEMP_FILE_OFF])) {
            WPvivid_PclZip::privErrorLog(WPVIVID_PCLZIP_ERR_INVALID_PARAMETER, "Option '".WPvivid_PclZipUtilOptionText($p_options_list[$i])."' can not be used with option 'WPVIVID_PCLZIP_OPT_TEMP_FILE_OFF'");
            return WPvivid_PclZip::errorCode();
          }

          $v_result_list[$p_options_list[$i]] = true;
        break;

        case WPVIVID_PCLZIP_OPT_TEMP_FILE_OFF :
          // ----- Check for incompatible options
          if (isset($v_result_list[WPVIVID_PCLZIP_OPT_TEMP_FILE_ON])) {
            WPvivid_PclZip::privErrorLog(WPVIVID_PCLZIP_ERR_INVALID_PARAMETER, "Option '".WPvivid_PclZipUtilOptionText($p_options_list[$i])."' can not be used with option 'WPVIVID_PCLZIP_OPT_TEMP_FILE_ON'");
            return WPvivid_PclZip::errorCode();
          }
          // ----- Check for incompatible options
          if (isset($v_result_list[WPVIVID_PCLZIP_OPT_TEMP_FILE_THRESHOLD])) {
            WPvivid_PclZip::privErrorLog(WPVIVID_PCLZIP_ERR_INVALID_PARAMETER, "Option '".WPvivid_PclZipUtilOptionText($p_options_list[$i])."' can not be used with option 'WPVIVID_PCLZIP_OPT_TEMP_FILE_THRESHOLD'");
            return WPvivid_PclZip::errorCode();
          }

          $v_result_list[$p_options_list[$i]] = true;
        break;

        case WPVIVID_PCLZIP_OPT_EXTRACT_DIR_RESTRICTION :
          // ----- Check the number of parameters
          if (($i+1) >= $p_size) {
            // ----- Error log
            WPvivid_PclZip::privErrorLog(WPVIVID_PCLZIP_ERR_MISSING_OPTION_VALUE, "Missing parameter value for option '".WPvivid_PclZipUtilOptionText($p_options_list[$i])."'");

            // ----- Return
            return WPvivid_PclZip::errorCode();
          }

          // ----- Get the value
          if (   is_string($p_options_list[$i+1])
              && ($p_options_list[$i+1] != '')) {
            $v_result_list[$p_options_list[$i]] = WPvivid_PclZipUtilTranslateWinPath($p_options_list[$i+1], FALSE);
            $i++;
          }
          else {
          }
        break;

        // ----- Look for options that request an array of string for value
        case WPVIVID_PCLZIP_OPT_BY_NAME :
          // ----- Check the number of parameters
          if (($i+1) >= $p_size) {
            // ----- Error log
            WPvivid_PclZip::privErrorLog(WPVIVID_PCLZIP_ERR_MISSING_OPTION_VALUE, "Missing parameter value for option '".WPvivid_PclZipUtilOptionText($p_options_list[$i])."'");

            // ----- Return
            return WPvivid_PclZip::errorCode();
          }

          // ----- Get the value
          if (is_string($p_options_list[$i+1])) {
              $v_result_list[$p_options_list[$i]][0] = $p_options_list[$i+1];
          }
          else if (is_array($p_options_list[$i+1])) {
              $v_result_list[$p_options_list[$i]] = $p_options_list[$i+1];
          }
          else {
            // ----- Error log
            WPvivid_PclZip::privErrorLog(WPVIVID_PCLZIP_ERR_INVALID_OPTION_VALUE, "Wrong parameter value for option '".WPvivid_PclZipUtilOptionText($p_options_list[$i])."'");

            // ----- Return
            return WPvivid_PclZip::errorCode();
          }
          $i++;
        break;

        // ----- Look for options that request an EREG or PREG expression
        case WPVIVID_PCLZIP_OPT_BY_EREG :
          // ereg() is deprecated starting with PHP 5.3. Move WPVIVID_PCLZIP_OPT_BY_EREG
          // to WPVIVID_PCLZIP_OPT_BY_PREG
          $p_options_list[$i] = WPVIVID_PCLZIP_OPT_BY_PREG;
        case WPVIVID_PCLZIP_OPT_BY_PREG :
        //case PCLZIP_OPT_CRYPT :
          // ----- Check the number of parameters
          if (($i+1) >= $p_size) {
            // ----- Error log
            WPvivid_PclZip::privErrorLog(WPVIVID_PCLZIP_ERR_MISSING_OPTION_VALUE, "Missing parameter value for option '".WPvivid_PclZipUtilOptionText($p_options_list[$i])."'");

            // ----- Return
            return WPvivid_PclZip::errorCode();
          }

          // ----- Get the value
          if (is_string($p_options_list[$i+1])) {
              $v_result_list[$p_options_list[$i]] = $p_options_list[$i+1];
          }
          else {
            // ----- Error log
            WPvivid_PclZip::privErrorLog(WPVIVID_PCLZIP_ERR_INVALID_OPTION_VALUE, "Wrong parameter value for option '".WPvivid_PclZipUtilOptionText($p_options_list[$i])."'");

            // ----- Return
            return WPvivid_PclZip::errorCode();
          }
          $i++;
        break;

        // ----- Look for options that takes a string
        case WPVIVID_PCLZIP_OPT_COMMENT :
        case WPVIVID_PCLZIP_OPT_ADD_COMMENT :
        case WPVIVID_PCLZIP_OPT_PREPEND_COMMENT :
          // ----- Check the number of parameters
          if (($i+1) >= $p_size) {
            // ----- Error log
            WPvivid_PclZip::privErrorLog(WPVIVID_PCLZIP_ERR_MISSING_OPTION_VALUE,
			                     "Missing parameter value for option '"
								 .WPvivid_PclZipUtilOptionText($p_options_list[$i])
								 ."'");

            // ----- Return
            return WPvivid_PclZip::errorCode();
          }

          // ----- Get the value
          if (is_string($p_options_list[$i+1])) {
              $v_result_list[$p_options_list[$i]] = $p_options_list[$i+1];
          }
          else {
            // ----- Error log
            WPvivid_PclZip::privErrorLog(WPVIVID_PCLZIP_ERR_INVALID_OPTION_VALUE,
			                     "Wrong parameter value for option '"
								 .WPvivid_PclZipUtilOptionText($p_options_list[$i])
								 ."'");

            // ----- Return
            return WPvivid_PclZip::errorCode();
          }
          $i++;
        break;

        // ----- Look for options that request an array of index
        case WPVIVID_PCLZIP_OPT_BY_INDEX :
          // ----- Check the number of parameters
          if (($i+1) >= $p_size) {
            // ----- Error log
            WPvivid_PclZip::privErrorLog(WPVIVID_PCLZIP_ERR_MISSING_OPTION_VALUE, "Missing parameter value for option '".WPvivid_PclZipUtilOptionText($p_options_list[$i])."'");

            // ----- Return
            return WPvivid_PclZip::errorCode();
          }

          // ----- Get the value
          $v_work_list = array();
          if (is_string($p_options_list[$i+1])) {

              // ----- Remove spaces
              $p_options_list[$i+1] = strtr($p_options_list[$i+1], ' ', '');

              // ----- Parse items
              $v_work_list = explode(",", $p_options_list[$i+1]);
          }
          else if (is_integer($p_options_list[$i+1])) {
              $v_work_list[0] = $p_options_list[$i+1].'-'.$p_options_list[$i+1];
          }
          else if (is_array($p_options_list[$i+1])) {
              $v_work_list = $p_options_list[$i+1];
          }
          else {
            // ----- Error log
            WPvivid_PclZip::privErrorLog(WPVIVID_PCLZIP_ERR_INVALID_OPTION_VALUE, "Value must be integer, string or array for option '".WPvivid_PclZipUtilOptionText($p_options_list[$i])."'");

            // ----- Return
            return WPvivid_PclZip::errorCode();
          }

          // ----- Reduce the index list
          // each index item in the list must be a couple with a start and
          // an end value : [0,3], [5-5], [8-10], ...
          // ----- Check the format of each item
          $v_sort_flag=false;
          $v_sort_value=0;
          for ($j=0; $j<sizeof($v_work_list); $j++) {
              // ----- Explode the item
              $v_item_list = explode("-", $v_work_list[$j]);
              $v_size_item_list = sizeof($v_item_list);

              // ----- TBC : Here we might check that each item is a
              // real integer ...

              // ----- Look for single value
              if ($v_size_item_list == 1) {
                  // ----- Set the option value
                  $v_result_list[$p_options_list[$i]][$j]['start'] = $v_item_list[0];
                  $v_result_list[$p_options_list[$i]][$j]['end'] = $v_item_list[0];
              }
              elseif ($v_size_item_list == 2) {
                  // ----- Set the option value
                  $v_result_list[$p_options_list[$i]][$j]['start'] = $v_item_list[0];
                  $v_result_list[$p_options_list[$i]][$j]['end'] = $v_item_list[1];
              }
              else {
                  // ----- Error log
                  WPvivid_PclZip::privErrorLog(WPVIVID_PCLZIP_ERR_INVALID_OPTION_VALUE, "Too many values in index range for option '".WPvivid_PclZipUtilOptionText($p_options_list[$i])."'");

                  // ----- Return
                  return WPvivid_PclZip::errorCode();
              }


              // ----- Look for list sort
              if ($v_result_list[$p_options_list[$i]][$j]['start'] < $v_sort_value) {
                  $v_sort_flag=true;

                  // ----- TBC : An automatic sort should be written ...
                  // ----- Error log
                  WPvivid_PclZip::privErrorLog(WPVIVID_PCLZIP_ERR_INVALID_OPTION_VALUE, "Invalid order of index range for option '".WPvivid_PclZipUtilOptionText($p_options_list[$i])."'");

                  // ----- Return
                  return WPvivid_PclZip::errorCode();
              }
              $v_sort_value = $v_result_list[$p_options_list[$i]][$j]['start'];
          }

          // ----- Sort the items
          if ($v_sort_flag) {
              // TBC : To Be Completed
          }

          // ----- Next option
          $i++;
        break;

        // ----- Look for options that request no value
        case WPVIVID_PCLZIP_OPT_REMOVE_ALL_PATH :
        case WPVIVID_PCLZIP_OPT_EXTRACT_AS_STRING :
        case WPVIVID_PCLZIP_OPT_NO_COMPRESSION :
        case WPVIVID_PCLZIP_OPT_EXTRACT_IN_OUTPUT :
        case WPVIVID_PCLZIP_OPT_REPLACE_NEWER :
        case WPVIVID_PCLZIP_OPT_STOP_ON_ERROR :
          $v_result_list[$p_options_list[$i]] = true;
        break;

        // ----- Look for options that request an octal value
        case WPVIVID_PCLZIP_OPT_SET_CHMOD :
          // ----- Check the number of parameters
          if (($i+1) >= $p_size) {
            // ----- Error log
            WPvivid_PclZip::privErrorLog(WPVIVID_PCLZIP_ERR_MISSING_OPTION_VALUE, "Missing parameter value for option '".WPvivid_PclZipUtilOptionText($p_options_list[$i])."'");

            // ----- Return
            return WPvivid_PclZip::errorCode();
          }

          // ----- Get the value
          $v_result_list[$p_options_list[$i]] = $p_options_list[$i+1];
          $i++;
        break;

        // ----- Look for options that request a call-back
        case WPVIVID_PCLZIP_CB_PRE_EXTRACT :
        case WPVIVID_PCLZIP_CB_POST_EXTRACT :
        case WPVIVID_PCLZIP_CB_PRE_ADD :
        case WPVIVID_PCLZIP_CB_POST_ADD :
        /* for futur use
        case PCLZIP_CB_PRE_DELETE :
        case PCLZIP_CB_POST_DELETE :
        case PCLZIP_CB_PRE_LIST :
        case PCLZIP_CB_POST_LIST :
        */
          // ----- Check the number of parameters
          if (($i+1) >= $p_size) {
            // ----- Error log
            WPvivid_PclZip::privErrorLog(WPVIVID_PCLZIP_ERR_MISSING_OPTION_VALUE, "Missing parameter value for option '".WPvivid_PclZipUtilOptionText($p_options_list[$i])."'");

            // ----- Return
            return WPvivid_PclZip::errorCode();
          }

          // ----- Get the value
          $v_function_name = $p_options_list[$i+1];

          // ----- Check that the value is a valid existing function
          if (!function_exists($v_function_name)) {
            // ----- Error log
            WPvivid_PclZip::privErrorLog(WPVIVID_PCLZIP_ERR_INVALID_OPTION_VALUE, "Function '".$v_function_name."()' is not an existing function for option '".WPvivid_PclZipUtilOptionText($p_options_list[$i])."'");

            // ----- Return
            return WPvivid_PclZip::errorCode();
          }

          // ----- Set the attribute
          $v_result_list[$p_options_list[$i]] = $v_function_name;
          $i++;
        break;

        default :
          // ----- Error log
          WPvivid_PclZip::privErrorLog(WPVIVID_PCLZIP_ERR_INVALID_PARAMETER,
		                       "Unknown parameter '"
							   .$p_options_list[$i]."'");

          // ----- Return
          return WPvivid_PclZip::errorCode();
      }

      // ----- Next options
      $i++;
    }

    // ----- Look for mandatory options
    if ($v_requested_options !== false) {
      for ($key=reset($v_requested_options); $key=key($v_requested_options); $key=next($v_requested_options)) {
        // ----- Look for mandatory option
        if ($v_requested_options[$key] == 'mandatory') {
          // ----- Look if present
          if (!isset($v_result_list[$key])) {
            // ----- Error log
            WPvivid_PclZip::privErrorLog(WPVIVID_PCLZIP_ERR_INVALID_PARAMETER, "Missing mandatory parameter ".WPvivid_PclZipUtilOptionText($key)."(".$key.")");

            // ----- Return
            return WPvivid_PclZip::errorCode();
          }
        }
      }
    }

    // ----- Look for default values
    if (!isset($v_result_list[WPVIVID_PCLZIP_OPT_TEMP_FILE_THRESHOLD])) {

    }

    // ----- Return
    return $v_result;
  }
  // --------------------------------------------------------------------------------

  // --------------------------------------------------------------------------------
  // Function : privOptionDefaultThreshold()
  // Description :
  // Parameters :
  // Return Values :
  // --------------------------------------------------------------------------------
  function privOptionDefaultThreshold(&$p_options)
  {
    $v_result=1;

    if (isset($p_options[WPVIVID_PCLZIP_OPT_TEMP_FILE_THRESHOLD])
        || isset($p_options[WPVIVID_PCLZIP_OPT_TEMP_FILE_OFF])) {
      return $v_result;
    }

    // ----- Get 'memory_limit' configuration value
    $v_memory_limit = ini_get('memory_limit');
    $v_memory_limit = trim($v_memory_limit);
    $v_memory_limit_int = (int) $v_memory_limit;
    $last = strtolower(substr($v_memory_limit, -1));

    if($last == 'g')
        //$v_memory_limit_int = $v_memory_limit_int*1024*1024*1024;
        $v_memory_limit_int = $v_memory_limit_int*1073741824;
    if($last == 'm')
        //$v_memory_limit_int = $v_memory_limit_int*1024*1024;
        $v_memory_limit_int = $v_memory_limit_int*1048576;
    if($last == 'k')
        $v_memory_limit_int = $v_memory_limit_int*1024;

    $p_options[WPVIVID_PCLZIP_OPT_TEMP_FILE_THRESHOLD] = floor($v_memory_limit_int*PCLZIP_TEMPORARY_FILE_RATIO);


    // ----- Sanity check : No threshold if value lower than 1M
    if ($p_options[WPVIVID_PCLZIP_OPT_TEMP_FILE_THRESHOLD] < 1048576) {
      unset($p_options[WPVIVID_PCLZIP_OPT_TEMP_FILE_THRESHOLD]);
    }

    // ----- Return
    return $v_result;
  }
  // --------------------------------------------------------------------------------

  // --------------------------------------------------------------------------------
  // Function : privFileDescrParseAtt()
  // Description :
  // Parameters :
  // Return Values :
  //   1 on success.
  //   0 on failure.
  // --------------------------------------------------------------------------------
  function privFileDescrParseAtt(&$p_file_list, &$p_filedescr, $v_options, $v_requested_options=false)
  {
    $v_result=1;

    // ----- For each file in the list check the attributes
    foreach ($p_file_list as $v_key => $v_value) {

      // ----- Check if the option is supported
      if (!isset($v_requested_options[$v_key])) {
        // ----- Error log
        WPvivid_PclZip::privErrorLog(WPVIVID_PCLZIP_ERR_INVALID_PARAMETER, "Invalid file attribute '".$v_key."' for this file");

        // ----- Return
        return WPvivid_PclZip::errorCode();
      }

      // ----- Look for attribute
      switch ($v_key) {
        case WPVIVID_PCLZIP_ATT_FILE_NAME :
          if (!is_string($v_value)) {
            WPvivid_PclZip::privErrorLog(WPVIVID_PCLZIP_ERR_INVALID_ATTRIBUTE_VALUE, "Invalid type ".gettype($v_value).". String expected for attribute '".WPvivid_PclZipUtilOptionText($v_key)."'");
            return WPvivid_PclZip::errorCode();
          }

          $p_filedescr['filename'] = WPvivid_PclZipUtilPathReduction($v_value);

          if ($p_filedescr['filename'] == '') {
            WPvivid_PclZip::privErrorLog(WPVIVID_PCLZIP_ERR_INVALID_ATTRIBUTE_VALUE, "Invalid empty filename for attribute '".WPvivid_PclZipUtilOptionText($v_key)."'");
            return WPvivid_PclZip::errorCode();
          }

        break;

        case WPVIVID_PCLZIP_ATT_FILE_NEW_SHORT_NAME :
          if (!is_string($v_value)) {
            WPvivid_PclZip::privErrorLog(WPVIVID_PCLZIP_ERR_INVALID_ATTRIBUTE_VALUE, "Invalid type ".gettype($v_value).". String expected for attribute '".WPvivid_PclZipUtilOptionText($v_key)."'");
            return WPvivid_PclZip::errorCode();
          }

          $p_filedescr['new_short_name'] = WPvivid_PclZipUtilPathReduction($v_value);

          if ($p_filedescr['new_short_name'] == '') {
            WPvivid_PclZip::privErrorLog(WPVIVID_PCLZIP_ERR_INVALID_ATTRIBUTE_VALUE, "Invalid empty short filename for attribute '".WPvivid_PclZipUtilOptionText($v_key)."'");
            return WPvivid_PclZip::errorCode();
          }
        break;

        case WPVIVID_PCLZIP_ATT_FILE_NEW_FULL_NAME :
          if (!is_string($v_value)) {
            WPvivid_PclZip::privErrorLog(WPVIVID_PCLZIP_ERR_INVALID_ATTRIBUTE_VALUE, "Invalid type ".gettype($v_value).". String expected for attribute '".WPvivid_PclZipUtilOptionText($v_key)."'");
            return WPvivid_PclZip::errorCode();
          }

          $p_filedescr['new_full_name'] = WPvivid_PclZipUtilPathReduction($v_value);

          if ($p_filedescr['new_full_name'] == '') {
            WPvivid_PclZip::privErrorLog(WPVIVID_PCLZIP_ERR_INVALID_ATTRIBUTE_VALUE, "Invalid empty full filename for attribute '".WPvivid_PclZipUtilOptionText($v_key)."'");
            return WPvivid_PclZip::errorCode();
          }
        break;

        // ----- Look for options that takes a string
        case WPVIVID_PCLZIP_ATT_FILE_COMMENT :
          if (!is_string($v_value)) {
            WPvivid_PclZip::privErrorLog(WPVIVID_PCLZIP_ERR_INVALID_ATTRIBUTE_VALUE, "Invalid type ".gettype($v_value).". String expected for attribute '".WPvivid_PclZipUtilOptionText($v_key)."'");
            return WPvivid_PclZip::errorCode();
          }

          $p_filedescr['comment'] = $v_value;
        break;

        case WPVIVID_PCLZIP_ATT_FILE_MTIME :
          if (!is_integer($v_value)) {
            WPvivid_PclZip::privErrorLog(WPVIVID_PCLZIP_ERR_INVALID_ATTRIBUTE_VALUE, "Invalid type ".gettype($v_value).". Integer expected for attribute '".WPvivid_PclZipUtilOptionText($v_key)."'");
            return WPvivid_PclZip::errorCode();
          }

          $p_filedescr['mtime'] = $v_value;
        break;

        case WPVIVID_PCLZIP_ATT_FILE_CONTENT :
          $p_filedescr['content'] = $v_value;
        break;

        default :
          // ----- Error log
          WPvivid_PclZip::privErrorLog(WPVIVID_PCLZIP_ERR_INVALID_PARAMETER,
		                           "Unknown parameter '".$v_key."'");

          // ----- Return
          return WPvivid_PclZip::errorCode();
      }

      // ----- Look for mandatory options
      if ($v_requested_options !== false) {
        for ($key=reset($v_requested_options); $key=key($v_requested_options); $key=next($v_requested_options)) {
          // ----- Look for mandatory option
          if ($v_requested_options[$key] == 'mandatory') {
            // ----- Look if present
            if (!isset($p_file_list[$key])) {
              WPvivid_PclZip::privErrorLog(WPVIVID_PCLZIP_ERR_INVALID_PARAMETER, "Missing mandatory parameter ".WPvivid_PclZipUtilOptionText($key)."(".$key.")");
              return WPvivid_PclZip::errorCode();
            }
          }
        }
      }

    // end foreach
    }

    // ----- Return
    return $v_result;
  }
  // --------------------------------------------------------------------------------

  // --------------------------------------------------------------------------------
  // Function : privFileDescrExpand()
  // Description :
  //   This method look for each item of the list to see if its a file, a folder
  //   or a string to be added as file. For any other type of files (link, other)
  //   just ignore the item.
  //   Then prepare the information that will be stored for that file.
  //   When its a folder, expand the folder with all the files that are in that
  //   folder (recursively).
  // Parameters :
  // Return Values :
  //   1 on success.
  //   0 on failure.
  // --------------------------------------------------------------------------------
  function privFileDescrExpand(&$p_filedescr_list, &$p_options)
  {
    $v_result=1;

    // ----- Create a result list
    $v_result_list = array();

    // ----- Look each entry
    for ($i=0; $i<sizeof($p_filedescr_list); $i++) {

      // ----- Get filedescr
      $v_descr = $p_filedescr_list[$i];

      // ----- Reduce the filename
      $v_descr['filename'] = WPvivid_PclZipUtilTranslateWinPath($v_descr['filename'], false);
      $v_descr['filename'] = WPvivid_PclZipUtilPathReduction($v_descr['filename']);

      // ----- Look for real file or folder
      if (file_exists($v_descr['filename'])) {
        if (@is_file($v_descr['filename'])) {
          $v_descr['type'] = 'file';
        }
        else if (@is_dir($v_descr['filename'])) {
          $v_descr['type'] = 'folder';
        }
        else if (@is_link($v_descr['filename'])) {
          // skip
          continue;
        }
        else {
          // skip
          continue;
        }
      }

      // ----- Look for string added as file
      else if (isset($v_descr['content'])) {
        $v_descr['type'] = 'virtual_file';
      }

      // ----- Missing file
      else {
        // ----- Error log
        WPvivid_PclZip::privErrorLog(WPVIVID_PCLZIP_ERR_MISSING_FILE, "File '".$v_descr['filename']."' does not exist");

        // ----- Return
        return WPvivid_PclZip::errorCode();
      }

      // ----- Calculate the stored filename
      $this->privCalculateStoredFilename($v_descr, $p_options);

      // ----- Add the descriptor in result list
      $v_result_list[sizeof($v_result_list)] = $v_descr;

      // ----- Look for folder
      if ($v_descr['type'] == 'folder') {
        // ----- List of items in folder
        $v_dirlist_descr = array();
        $v_dirlist_nb = 0;
        if ($v_folder_handler = @opendir($v_descr['filename'])) {
          while (($v_item_handler = @readdir($v_folder_handler)) !== false) {

            // ----- Skip '.' and '..'
            if (($v_item_handler == '.') || ($v_item_handler == '..')) {
                continue;
            }

            // ----- Compose the full filename
            $v_dirlist_descr[$v_dirlist_nb]['filename'] = $v_descr['filename'].'/'.$v_item_handler;

            // ----- Look for different stored filename
            // Because the name of the folder was changed, the name of the
            // files/sub-folders also change
            if (($v_descr['stored_filename'] != $v_descr['filename'])
                 && (!isset($p_options[WPVIVID_PCLZIP_OPT_REMOVE_ALL_PATH]))) {
              if ($v_descr['stored_filename'] != '') {
                $v_dirlist_descr[$v_dirlist_nb]['new_full_name'] = $v_descr['stored_filename'].'/'.$v_item_handler;
              }
              else {
                $v_dirlist_descr[$v_dirlist_nb]['new_full_name'] = $v_item_handler;
              }
            }

            $v_dirlist_nb++;
          }

          @closedir($v_folder_handler);
        }
        else {
          // TBC : unable to open folder in read mode
        }

        // ----- Expand each element of the list
        if ($v_dirlist_nb != 0) {
          // ----- Expand
          if (($v_result = $this->privFileDescrExpand($v_dirlist_descr, $p_options)) != 1) {
            return $v_result;
          }

          // ----- Concat the resulting list
          $v_result_list = array_merge($v_result_list, $v_dirlist_descr);
        }
        else {
        }

        // ----- Free local array
        unset($v_dirlist_descr);
      }
    }

    // ----- Get the result list
    $p_filedescr_list = $v_result_list;

    // ----- Return
    return $v_result;
  }
  // --------------------------------------------------------------------------------

  // --------------------------------------------------------------------------------
  // Function : privCreate()
  // Description :
  // Parameters :
  // Return Values :
  // --------------------------------------------------------------------------------
  function privCreate($p_filedescr_list, &$p_result_list, &$p_options)
  {
    $v_result=1;
    $v_list_detail = array();

    // ----- Magic quotes trick
    $this->privDisableMagicQuotes();

    // ----- Open the file in write mode
    if (($v_result = $this->privOpenFd('wb')) != 1)
    {
      // ----- Return
      return $v_result;
    }

    // ----- Add the list of files
    $v_result = $this->privAddList($p_filedescr_list, $p_result_list, $p_options);

    // ----- Close
    $this->privCloseFd();

    // ----- Magic quotes trick
    $this->privSwapBackMagicQuotes();

    // ----- Return
    return $v_result;
  }
  // --------------------------------------------------------------------------------

  // --------------------------------------------------------------------------------
  // Function : privAdd()
  // Description :
  // Parameters :
  // Return Values :
  // --------------------------------------------------------------------------------
  function privAdd($p_filedescr_list, &$p_result_list, &$p_options)
  {
    $v_result=1;
    $v_list_detail = array();

    // ----- Look if the archive exists or is empty
    if ((!is_file($this->zipname)) || (filesize($this->zipname) == 0))
    {

      // ----- Do a create
      $v_result = $this->privCreate($p_filedescr_list, $p_result_list, $p_options);

      // ----- Return
      return $v_result;
    }
    // ----- Magic quotes trick
    $this->privDisableMagicQuotes();

    // ----- Open the zip file
    if (($v_result=$this->privOpenFd('rb')) != 1)
    {
      // ----- Magic quotes trick
      $this->privSwapBackMagicQuotes();

      // ----- Return
      return $v_result;
    }

    // ----- Read the central directory information
    $v_central_dir = array();
    if (($v_result = $this->privReadEndCentralDir($v_central_dir)) != 1)
    {
      $this->privCloseFd();
      $this->privSwapBackMagicQuotes();
      return $v_result;
    }

    // ----- Go to beginning of File
    @rewind($this->zip_fd);

    // ----- Creates a temporary file
    $v_zip_temp_name = PCLZIP_TEMPORARY_DIR.uniqid('pclzip-').'.tmp';

    // ----- Open the temporary file in write mode
    if (($v_zip_temp_fd = @fopen($v_zip_temp_name, 'wb')) == 0)
    {
      $this->privCloseFd();
      $this->privSwapBackMagicQuotes();

      WPvivid_PclZip::privErrorLog(WPVIVID_PCLZIP_ERR_READ_OPEN_FAIL, 'Unable to open temporary file \''.$v_zip_temp_name.'\' in binary write mode');

      // ----- Return
      return WPvivid_PclZip::errorCode();
    }

    // ----- Copy the files from the archive to the temporary file
    // TBC : Here I should better append the file and go back to erase the central dir
    $v_size = $v_central_dir['offset'];
    while ($v_size != 0)
    {
      $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE);
      $v_buffer = fread($this->zip_fd, $v_read_size);
      @fwrite($v_zip_temp_fd, $v_buffer, $v_read_size);
      $v_size -= $v_read_size;
    }

    // ----- Swap the file descriptor
    // Here is a trick : I swap the temporary fd with the zip fd, in order to use
    // the following methods on the temporary fil and not the real archive
    $v_swap = $this->zip_fd;
    $this->zip_fd = $v_zip_temp_fd;
    $v_zip_temp_fd = $v_swap;

    // ----- Add the files
    $v_header_list = array();
    if (($v_result = $this->privAddFileList($p_filedescr_list, $v_header_list, $p_options)) != 1)
    {
      fclose($v_zip_temp_fd);
      $this->privCloseFd();
      @wp_delete_file($v_zip_temp_name);
      $this->privSwapBackMagicQuotes();

      // ----- Return
      return $v_result;
    }

    // ----- Store the offset of the central dir
    $v_offset = @ftell($this->zip_fd);

    // ----- Copy the block of file headers from the old archive
    $v_size = $v_central_dir['size'];
    while ($v_size != 0)
    {
      $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE);
      $v_buffer = @fread($v_zip_temp_fd, $v_read_size);
      @fwrite($this->zip_fd, $v_buffer, $v_read_size);
      $v_size -= $v_read_size;
    }

    // ----- Create the Central Dir files header
    for ($i=0, $v_count=0; $i<sizeof($v_header_list); $i++)
    {
      // ----- Create the file header
      if ($v_header_list[$i]['status'] == 'ok') {
        if (($v_result = $this->privWriteCentralFileHeader($v_header_list[$i])) != 1) {
          fclose($v_zip_temp_fd);
          $this->privCloseFd();
          @wp_delete_file($v_zip_temp_name);
          $this->privSwapBackMagicQuotes();

          // ----- Return
          return $v_result;
        }
        $v_count++;
      }

      // ----- Transform the header to a 'usable' info
      $this->privConvertHeader2FileInfo($v_header_list[$i], $p_result_list[$i]);
    }

    // ----- Zip file comment
    $v_comment = $v_central_dir['comment'];
    if (isset($p_options[WPVIVID_PCLZIP_OPT_COMMENT])) {
      $v_comment = $p_options[WPVIVID_PCLZIP_OPT_COMMENT];
    }
    if (isset($p_options[WPVIVID_PCLZIP_OPT_ADD_COMMENT])) {
      $v_comment = $v_comment.$p_options[WPVIVID_PCLZIP_OPT_ADD_COMMENT];
    }
    if (isset($p_options[WPVIVID_PCLZIP_OPT_PREPEND_COMMENT])) {
      $v_comment = $p_options[WPVIVID_PCLZIP_OPT_PREPEND_COMMENT].$v_comment;
    }

    // ----- Calculate the size of the central header
    $v_size = @ftell($this->zip_fd)-$v_offset;

    // ----- Create the central dir footer
    if (($v_result = $this->privWriteCentralHeader($v_count+$v_central_dir['entries'], $v_size, $v_offset, $v_comment)) != 1)
    {
      // ----- Reset the file list
      unset($v_header_list);
      $this->privSwapBackMagicQuotes();

      // ----- Return
      return $v_result;
    }

    // ----- Swap back the file descriptor
    $v_swap = $this->zip_fd;
    $this->zip_fd = $v_zip_temp_fd;
    $v_zip_temp_fd = $v_swap;

    // ----- Close
    $this->privCloseFd();

    // ----- Close the temporary file
    @fclose($v_zip_temp_fd);

    // ----- Magic quotes trick
    $this->privSwapBackMagicQuotes();

    // ----- Delete the zip file
    // TBC : I should test the result ...
    @wp_delete_file($this->zipname);

    // ----- Rename the temporary file
    // TBC : I should test the result ...
    //@rename($v_zip_temp_name, $this->zipname);
    WPvivid_PclZipUtilRename($v_zip_temp_name, $this->zipname);

    // ----- Return
    return $v_result;
  }
  // --------------------------------------------------------------------------------

  // --------------------------------------------------------------------------------
  // Function : privOpenFd()
  // Description :
  // Parameters :
  // --------------------------------------------------------------------------------
  function privOpenFd($p_mode)
  {
    $v_result=1;

    // ----- Look if already open
    if ($this->zip_fd != 0)
    {
      // ----- Error log
      WPvivid_PclZip::privErrorLog(WPVIVID_PCLZIP_ERR_READ_OPEN_FAIL, 'Zip file \''.$this->zipname.'\' already open');

      // ----- Return
      return WPvivid_PclZip::errorCode();
    }

    // ----- Open the zip file
    if (($this->zip_fd = @fopen($this->zipname, $p_mode)) == 0)
    {
      // ----- Error log
      WPvivid_PclZip::privErrorLog(WPVIVID_PCLZIP_ERR_READ_OPEN_FAIL, 'Unable to open archive \''.$this->zipname.'\' in '.$p_mode.' mode');

      // ----- Return
      return WPvivid_PclZip::errorCode();
    }

    // ----- Return
    return $v_result;
  }
  // --------------------------------------------------------------------------------

  // --------------------------------------------------------------------------------
  // Function : privCloseFd()
  // Description :
  // Parameters :
  // --------------------------------------------------------------------------------
  function privCloseFd()
  {
    $v_result=1;

    if ($this->zip_fd != 0)
      @fclose($this->zip_fd);
    $this->zip_fd = 0;

    // ----- Return
    return $v_result;
  }
  // --------------------------------------------------------------------------------

  // --------------------------------------------------------------------------------
  // Function : privAddList()
  // Description :
  //   $p_add_dir and $p_remove_dir will give the ability to memorize a path which is
  //   different from the real path of the file. This is useful if you want to have PclTar
  //   running in any directory, and memorize relative path from an other directory.
  // Parameters :
  //   $p_list : An array containing the file or directory names to add in the tar
  //   $p_result_list : list of added files with their properties (specially the status field)
  //   $p_add_dir : Path to add in the filename path archived
  //   $p_remove_dir : Path to remove in the filename path archived
  // Return Values :
  // --------------------------------------------------------------------------------
//  function privAddList($p_list, &$p_result_list, $p_add_dir, $p_remove_dir, $p_remove_all_dir, &$p_options)
  function privAddList($p_filedescr_list, &$p_result_list, &$p_options)
  {
    $v_result=1;

    // ----- Add the files
    $v_header_list = array();
    if (($v_result = $this->privAddFileList($p_filedescr_list, $v_header_list, $p_options)) != 1)
    {
      // ----- Return
      return $v_result;
    }

    // ----- Store the offset of the central dir
    $v_offset = @ftell($this->zip_fd);

    // ----- Create the Central Dir files header
    for ($i=0,$v_count=0; $i<sizeof($v_header_list); $i++)
    {
      // ----- Create the file header
      if ($v_header_list[$i]['status'] == 'ok') {
        if (($v_result = $this->privWriteCentralFileHeader($v_header_list[$i])) != 1) {
          // ----- Return
          return $v_result;
        }
        $v_count++;
      }

      // ----- Transform the header to a 'usable' info
      $this->privConvertHeader2FileInfo($v_header_list[$i], $p_result_list[$i]);
    }

    // ----- Zip file comment
    $v_comment = '';
    if (isset($p_options[WPVIVID_PCLZIP_OPT_COMMENT])) {
      $v_comment = $p_options[WPVIVID_PCLZIP_OPT_COMMENT];
    }

    // ----- Calculate the size of the central header
    $v_size = @ftell($this->zip_fd)-$v_offset;

    // ----- Create the central dir footer
    if (($v_result = $this->privWriteCentralHeader($v_count, $v_size, $v_offset, $v_comment)) != 1)
    {
      // ----- Reset the file list
      unset($v_header_list);

      // ----- Return
      return $v_result;
    }

    // ----- Return
    return $v_result;
  }
  // --------------------------------------------------------------------------------

  // --------------------------------------------------------------------------------
  // Function : privAddFileList()
  // Description :
  // Parameters :
  //   $p_filedescr_list : An array containing the file description
  //                      or directory names to add in the zip
  //   $p_result_list : list of added files with their properties (specially the status field)
  // Return Values :
  // --------------------------------------------------------------------------------
  function privAddFileList($p_filedescr_list, &$p_result_list, &$p_options)
  {
    $v_result=1;
    $v_header = array();

    // ----- Recuperate the current number of elt in list
    $v_nb = sizeof($p_result_list);

    // ----- Loop on the files
    for ($j=0; ($j<sizeof($p_filedescr_list)) && ($v_result==1); $j++) {
      // ----- Format the filename
      $p_filedescr_list[$j]['filename']
      = WPvivid_PclZipUtilTranslateWinPath($p_filedescr_list[$j]['filename'], false);


      // ----- Skip empty file names
      // TBC : Can this be possible ? not checked in DescrParseAtt ?
      if ($p_filedescr_list[$j]['filename'] == "") {
        continue;
      }

      // ----- Check the filename
      if (   ($p_filedescr_list[$j]['type'] != 'virtual_file')
          && (!file_exists($p_filedescr_list[$j]['filename']))) {
        WPvivid_PclZip::privErrorLog(WPVIVID_PCLZIP_ERR_MISSING_FILE, "File '".$p_filedescr_list[$j]['filename']."' does not exist");
        return WPvivid_PclZip::errorCode();
      }

      // ----- Look if it is a file or a dir with no all path remove option
      // or a dir with all its path removed
//      if (   (is_file($p_filedescr_list[$j]['filename']))
//          || (   is_dir($p_filedescr_list[$j]['filename'])
      if (   ($p_filedescr_list[$j]['type'] == 'file')
          || ($p_filedescr_list[$j]['type'] == 'virtual_file')
          || (   ($p_filedescr_list[$j]['type'] == 'folder')
              && (   !isset($p_options[WPVIVID_PCLZIP_OPT_REMOVE_ALL_PATH])
                  || !$p_options[WPVIVID_PCLZIP_OPT_REMOVE_ALL_PATH]))
          ) {

        // ----- Add the file
        $v_result = $this->privAddFile($p_filedescr_list[$j], $v_header,
                                       $p_options);
        if ($v_result != 1) {
          return $v_result;
        }

        // ----- Store the file infos
        $p_result_list[$v_nb++] = $v_header;
      }
    }

    // ----- Return
    return $v_result;
  }
  // --------------------------------------------------------------------------------

  // --------------------------------------------------------------------------------
  // Function : privAddFile()
  // Description :
  // Parameters :
  // Return Values :
  // --------------------------------------------------------------------------------
  function privAddFile($p_filedescr, &$p_header, &$p_options)
  {
    $v_result=1;

    // ----- Working variable
    $p_filename = $p_filedescr['filename'];

    // TBC : Already done in the fileAtt check ... ?
    if ($p_filename == "") {
      // ----- Error log
      WPvivid_PclZip::privErrorLog(WPVIVID_PCLZIP_ERR_INVALID_PARAMETER, "Invalid file list parameter (invalid or empty list)");

      // ----- Return
      return WPvivid_PclZip::errorCode();
    }

    // ----- Look for a stored different filename
    /* TBC : Removed
    if (isset($p_filedescr['stored_filename'])) {
      $v_stored_filename = $p_filedescr['stored_filename'];
    }
    else {
      $v_stored_filename = $p_filedescr['stored_filename'];
    }
    */

    // ----- Set the file properties
    clearstatcache();
    $p_header['version'] = 20;
    $p_header['version_extracted'] = 10;
    $p_header['flag'] = 0;
    $p_header['compression'] = 0;
    $p_header['crc'] = 0;
    $p_header['compressed_size'] = 0;
    $p_header['filename_len'] = strlen($p_filename);
    $p_header['extra_len'] = 0;
    $p_header['disk'] = 0;
    $p_header['internal'] = 0;
    $p_header['offset'] = 0;
    $p_header['filename'] = $p_filename;
// TBC : Removed    $p_header['stored_filename'] = $v_stored_filename;
    $p_header['stored_filename'] = $p_filedescr['stored_filename'];
    $p_header['extra'] = '';
    $p_header['status'] = 'ok';
    $p_header['index'] = -1;

    // ----- Look for regular file
    if ($p_filedescr['type']=='file') {
      $p_header['external'] = 0x00000000;
      $p_header['size'] = filesize($p_filename);
    }

    // ----- Look for regular folder
    else if ($p_filedescr['type']=='folder') {
      $p_header['external'] = 0x00000010;
      $p_header['mtime'] = filemtime($p_filename);
      $p_header['size'] = filesize($p_filename);
    }

    // ----- Look for virtual file
    else if ($p_filedescr['type'] == 'virtual_file') {
      $p_header['external'] = 0x00000000;
      $p_header['size'] = strlen($p_filedescr['content']);
    }


    // ----- Look for filetime
    if (isset($p_filedescr['mtime'])) {
      $p_header['mtime'] = $p_filedescr['mtime'];
    }
    else if ($p_filedescr['type'] == 'virtual_file') {
      $p_header['mtime'] = time();
    }
    else {
      $p_header['mtime'] = filemtime($p_filename);
    }

    // ------ Look for file comment
    if (isset($p_filedescr['comment'])) {
      $p_header['comment_len'] = strlen($p_filedescr['comment']);
      $p_header['comment'] = $p_filedescr['comment'];
    }
    else {
      $p_header['comment_len'] = 0;
      $p_header['comment'] = '';
    }

    // ----- Look for pre-add callback
    if (isset($p_options[WPVIVID_PCLZIP_CB_PRE_ADD])) {

      // ----- Generate a local information
      $v_local_header = array();
      $this->privConvertHeader2FileInfo($p_header, $v_local_header);

      // ----- Call the callback
      // Here I do not use call_user_func() because I need to send a reference to the
      // header.
      $v_result = $p_options[WPVIVID_PCLZIP_CB_PRE_ADD](WPVIVID_PCLZIP_CB_PRE_ADD, $v_local_header);
      if ($v_result == 0) {
        // ----- Change the file status
        $p_header['status'] = "skipped";
        $v_result = 1;
      }

      // ----- Update the information
      // Only some fields can be modified
      if ($p_header['stored_filename'] != $v_local_header['stored_filename']) {
        $p_header['stored_filename'] = WPvivid_PclZipUtilPathReduction($v_local_header['stored_filename']);
      }
    }

    // ----- Look for empty stored filename
    if ($p_header['stored_filename'] == "") {
      $p_header['status'] = "filtered";
    }

    // ----- Check the path length
    if (strlen($p_header['stored_filename']) > 0xFF) {
      $p_header['status'] = 'filename_too_long';
    }

    // ----- Look if no error, or file not skipped
    if ($p_header['status'] == 'ok') {

      // ----- Look for a file
      if ($p_filedescr['type'] == 'file') {
        // ----- Look for using temporary file to zip
        if ( (!isset($p_options[WPVIVID_PCLZIP_OPT_TEMP_FILE_OFF]))
            && (isset($p_options[WPVIVID_PCLZIP_OPT_TEMP_FILE_ON])
                || (isset($p_options[WPVIVID_PCLZIP_OPT_TEMP_FILE_THRESHOLD])
                    && ($p_options[WPVIVID_PCLZIP_OPT_TEMP_FILE_THRESHOLD] <= $p_header['size'])) ) ) {
          $v_result = $this->privAddFileUsingTempFile($p_filedescr, $p_header, $p_options);
          if ($v_result < WPVIVID_PCLZIP_ERR_NO_ERROR) {
            return $v_result;
          }
        }

        // ----- Use "in memory" zip algo
        else {

        // ----- Open the source file
        if (($v_file = @fopen($p_filename, "rb")) == 0) {
          WPvivid_PclZip::privErrorLog(WPVIVID_PCLZIP_ERR_READ_OPEN_FAIL, "Unable to open file '$p_filename' in binary read mode");
          return WPvivid_PclZip::errorCode();
        }

        // ----- Read the file content
          if($p_header['size']==0)
          {
            $v_content='';
          }
          else
          {
            $v_content = @fread($v_file, $p_header['size']);
          }

        // ----- Close the file
        @fclose($v_file);

        // ----- Calculate the CRC
        $p_header['crc'] = @crc32($v_content);

        // ----- Look for no compression
        if ($p_options[WPVIVID_PCLZIP_OPT_NO_COMPRESSION]) {
          // ----- Set header parameters
          $p_header['compressed_size'] = $p_header['size'];
          $p_header['compression'] = 0;
        }

        // ----- Look for normal compression
        else {
          // ----- Compress the content
          $v_content = @gzdeflate($v_content);

          // ----- Set header parameters
          $p_header['compressed_size'] = strlen($v_content);
          $p_header['compression'] = 8;
        }

        // ----- Call the header generation
        if (($v_result = $this->privWriteFileHeader($p_header)) != 1) {
          @fclose($v_file);
          return $v_result;
        }

        // ----- Write the compressed (or not) content
        @fwrite($this->zip_fd, $v_content, $p_header['compressed_size']);

        }

      }

      // ----- Look for a virtual file (a file from string)
      else if ($p_filedescr['type'] == 'virtual_file') {

        $v_content = $p_filedescr['content'];

        // ----- Calculate the CRC
        $p_header['crc'] = @crc32($v_content);

        // ----- Look for no compression
        if ($p_options[WPVIVID_PCLZIP_OPT_NO_COMPRESSION]) {
          // ----- Set header parameters
          $p_header['compressed_size'] = $p_header['size'];
          $p_header['compression'] = 0;
        }

        // ----- Look for normal compression
        else {
          // ----- Compress the content
          $v_content = @gzdeflate($v_content);

          // ----- Set header parameters
          $p_header['compressed_size'] = strlen($v_content);
          $p_header['compression'] = 8;
        }

        // ----- Call the header generation
        if (($v_result = $this->privWriteFileHeader($p_header)) != 1) {
          @fclose($v_file);
          return $v_result;
        }

        // ----- Write the compressed (or not) content
        @fwrite($this->zip_fd, $v_content, $p_header['compressed_size']);
      }

      // ----- Look for a directory
      else if ($p_filedescr['type'] == 'folder') {
        // ----- Look for directory last '/'
        if (@substr($p_header['stored_filename'], -1) != '/') {
          $p_header['stored_filename'] .= '/';
        }

        // ----- Set the file properties
        $p_header['size'] = 0;
        //$p_header['external'] = 0x41FF0010;   // Value for a folder : to be checked
        $p_header['external'] = 0x00000010;   // Value for a folder : to be checked

        // ----- Call the header generation
        if (($v_result = $this->privWriteFileHeader($p_header)) != 1)
        {
          return $v_result;
        }
      }
    }

    // ----- Look for post-add callback
    if (isset($p_options[WPVIVID_PCLZIP_CB_POST_ADD])) {

      // ----- Generate a local information
      $v_local_header = array();
      $this->privConvertHeader2FileInfo($p_header, $v_local_header);

      // ----- Call the callback
      // Here I do not use call_user_func() because I need to send a reference to the
      // header.
      $v_result = $p_options[WPVIVID_PCLZIP_CB_POST_ADD](WPVIVID_PCLZIP_CB_POST_ADD, $v_local_header);
      if ($v_result == 0) {
        // ----- Ignored
        $v_result = 1;
      }

      // ----- Update the information
      // Nothing can be modified
    }

    // ----- Return
    return $v_result;
  }
  // --------------------------------------------------------------------------------

  // --------------------------------------------------------------------------------
  // Function : privAddFileUsingTempFile()
  // Description :
  // Parameters :
  // Return Values :
  // --------------------------------------------------------------------------------
  function privAddFileUsingTempFile($p_filedescr, &$p_header, &$p_options)
  {
    $v_result=WPVIVID_PCLZIP_ERR_NO_ERROR;

    // ----- Working variable
    $p_filename = $p_filedescr['filename'];


    // ----- Open the source file
    if (($v_file = @fopen($p_filename, "rb")) == 0) {
      WPvivid_PclZip::privErrorLog(WPVIVID_PCLZIP_ERR_READ_OPEN_FAIL, "Unable to open file '$p_filename' in binary read mode");
      return WPvivid_PclZip::errorCode();
    }

    // ----- Creates a compressed temporary file
    $v_gzip_temp_name = PCLZIP_TEMPORARY_DIR.uniqid('pclzip-').'.gz';
    if (($v_file_compressed = @gzopen($v_gzip_temp_name, "wb")) == 0) {
      fclose($v_file);
      WPvivid_PclZip::privErrorLog(WPVIVID_PCLZIP_ERR_WRITE_OPEN_FAIL, 'Unable to open temporary file \''.$v_gzip_temp_name.'\' in binary write mode');
      return WPvivid_PclZip::errorCode();
    }

    // ----- Read the file by PCLZIP_READ_BLOCK_SIZE octets blocks
    $v_size = filesize($p_filename);
    while ($v_size != 0) {
      $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE);
      $v_buffer = @fread($v_file, $v_read_size);
      //$v_binary_data = pack('a'.$v_read_size, $v_buffer);
      @gzputs($v_file_compressed, $v_buffer, $v_read_size);
      $v_size -= $v_read_size;
    }

    // ----- Close the file
    @fclose($v_file);
    @gzclose($v_file_compressed);

    // ----- Check the minimum file size
    if (filesize($v_gzip_temp_name) < 18) {
      WPvivid_PclZip::privErrorLog(WPVIVID_PCLZIP_ERR_BAD_FORMAT, 'gzip temporary file \''.$v_gzip_temp_name.'\' has invalid filesize - should be minimum 18 bytes');
      return WPvivid_PclZip::errorCode();
    }

    // ----- Extract the compressed attributes
    if (($v_file_compressed = @fopen($v_gzip_temp_name, "rb")) == 0) {
      WPvivid_PclZip::privErrorLog(WPVIVID_PCLZIP_ERR_READ_OPEN_FAIL, 'Unable to open temporary file \''.$v_gzip_temp_name.'\' in binary read mode');
      return WPvivid_PclZip::errorCode();
    }

    // ----- Read the gzip file header
    $v_binary_data = @fread($v_file_compressed, 10);
    $v_data_header = unpack('a1id1/a1id2/a1cm/a1flag/Vmtime/a1xfl/a1os', $v_binary_data);

    // ----- Check some parameters
    $v_data_header['os'] = bin2hex($v_data_header['os']);

    // ----- Read the gzip file footer
    @fseek($v_file_compressed, filesize($v_gzip_temp_name)-8);
    $v_binary_data = @fread($v_file_compressed, 8);
    $v_data_footer = unpack('Vcrc/Vcompressed_size', $v_binary_data);

    // ----- Set the attributes
    $p_header['compression'] = ord($v_data_header['cm']);
    //$p_header['mtime'] = $v_data_header['mtime'];
    $p_header['crc'] = $v_data_footer['crc'];
    $p_header['compressed_size'] = filesize($v_gzip_temp_name)-18;

    // ----- Close the file
    @fclose($v_file_compressed);

    // ----- Call the header generation
    if (($v_result = $this->privWriteFileHeader($p_header)) != 1) {
      return $v_result;
    }

    // ----- Add the compressed data
    if (($v_file_compressed = @fopen($v_gzip_temp_name, "rb")) == 0)
    {
      WPvivid_PclZip::privErrorLog(WPVIVID_PCLZIP_ERR_READ_OPEN_FAIL, 'Unable to open temporary file \''.$v_gzip_temp_name.'\' in binary read mode');
      return WPvivid_PclZip::errorCode();
    }

    // ----- Read the file by PCLZIP_READ_BLOCK_SIZE octets blocks
    fseek($v_file_compressed, 10);
    $v_size = $p_header['compressed_size'];
    while ($v_size != 0)
    {
      $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE);
      $v_buffer = @fread($v_file_compressed, $v_read_size);
      //$v_binary_data = pack('a'.$v_read_size, $v_buffer);
      @fwrite($this->zip_fd, $v_buffer, $v_read_size);
      $v_size -= $v_read_size;
    }

    // ----- Close the file
    @fclose($v_file_compressed);

    // ----- Unlink the temporary file
    @wp_delete_file($v_gzip_temp_name);

    // ----- Return
    return $v_result;
  }
  // --------------------------------------------------------------------------------

  // --------------------------------------------------------------------------------
  // Function : privCalculateStoredFilename()
  // Description :
  //   Based on file descriptor properties and global options, this method
  //   calculate the filename that will be stored in the archive.
  // Parameters :
  // Return Values :
  // --------------------------------------------------------------------------------
  function privCalculateStoredFilename(&$p_filedescr, &$p_options)
  {
    $v_result=1;

    // ----- Working variables
    $p_filename = $p_filedescr['filename'];
    if (isset($p_options[WPVIVID_PCLZIP_OPT_ADD_PATH])) {
      $p_add_dir = $p_options[WPVIVID_PCLZIP_OPT_ADD_PATH];
    }
    else {
      $p_add_dir = '';
    }
    if (isset($p_options[WPVIVID_PCLZIP_OPT_REMOVE_PATH])) {
      $p_remove_dir = $p_options[WPVIVID_PCLZIP_OPT_REMOVE_PATH];
    }
    else {
      $p_remove_dir = '';
    }
    if (isset($p_options[WPVIVID_PCLZIP_OPT_REMOVE_ALL_PATH])) {
      $p_remove_all_dir = $p_options[WPVIVID_PCLZIP_OPT_REMOVE_ALL_PATH];
    }
    else {
      $p_remove_all_dir = 0;
    }


    // ----- Look for full name change
    if (isset($p_filedescr['new_full_name'])) {
      // ----- Remove drive letter if any
      $v_stored_filename = WPvivid_PclZipUtilTranslateWinPath($p_filedescr['new_full_name']);
    }

    // ----- Look for path and/or short name change
    else {

      // ----- Look for short name change
      // Its when we change just the filename but not the path
      if (isset($p_filedescr['new_short_name'])) {
        $v_path_info = pathinfo($p_filename);
        $v_dir = '';
        if ($v_path_info['dirname'] != '') {
          $v_dir = $v_path_info['dirname'].'/';
        }
        $v_stored_filename = $v_dir.$p_filedescr['new_short_name'];
      }
      else {
        // ----- Calculate the stored filename
        $v_stored_filename = $p_filename;
      }

      // ----- Look for all path to remove
      if ($p_remove_all_dir) {
        $v_stored_filename = basename($p_filename);
      }
      // ----- Look for partial path remove
      else if ($p_remove_dir != "") {
        if (substr($p_remove_dir, -1) != '/')
          $p_remove_dir .= "/";

        if (   (substr($p_filename, 0, 2) == "./")
            || (substr($p_remove_dir, 0, 2) == "./")) {

          if (   (substr($p_filename, 0, 2) == "./")
              && (substr($p_remove_dir, 0, 2) != "./")) {
            $p_remove_dir = "./".$p_remove_dir;
          }
          if (   (substr($p_filename, 0, 2) != "./")
              && (substr($p_remove_dir, 0, 2) == "./")) {
            $p_remove_dir = substr($p_remove_dir, 2);
          }
        }

        $v_compare = WPvivid_PclZipUtilPathInclusion($p_remove_dir,
                                             $v_stored_filename);
        if ($v_compare > 0) {
          if ($v_compare == 2) {
            $v_stored_filename = "";
          }
          else {
            $v_stored_filename = substr($v_stored_filename,
                                        strlen($p_remove_dir));
          }
        }
      }

      // ----- Remove drive letter if any
      $v_stored_filename = WPvivid_PclZipUtilTranslateWinPath($v_stored_filename);

      // ----- Look for path to add
      if ($p_add_dir != "") {
        if (substr($p_add_dir, -1) == "/")
          $v_stored_filename = $p_add_dir.$v_stored_filename;
        else
          $v_stored_filename = $p_add_dir."/".$v_stored_filename;
      }
    }

    // ----- Filename (reduce the path of stored name)
    $v_stored_filename = WPvivid_PclZipUtilPathReduction($v_stored_filename);
    $p_filedescr['stored_filename'] = $v_stored_filename;

    // ----- Return
    return $v_result;
  }
  // --------------------------------------------------------------------------------

  // --------------------------------------------------------------------------------
  // Function : privWriteFileHeader()
  // Description :
  // Parameters :
  // Return Values :
  // --------------------------------------------------------------------------------
  function privWriteFileHeader(&$p_header)
  {
    $v_result=1;

    // ----- Store the offset position of the file
    $p_header['offset'] = ftell($this->zip_fd);

    // ----- Transform UNIX mtime to DOS format mdate/mtime
    $v_date = getdate($p_header['mtime']);
    $v_mtime = ($v_date['hours']<<11) + ($v_date['minutes']<<5) + $v_date['seconds']/2;
    $v_mdate = (($v_date['year']-1980)<<9) + ($v_date['mon']<<5) + $v_date['mday'];

    // ----- Packed data
    $v_binary_data = pack("VvvvvvVVVvv", 0x04034b50,
	                      $p_header['version_extracted'], $p_header['flag'],
                          $p_header['compression'], $v_mtime, $v_mdate,
                          $p_header['crc'], $p_header['compressed_size'],
						  $p_header['size'],
                          strlen($p_header['stored_filename']),
						  $p_header['extra_len']);

    // ----- Write the first 148 bytes of the header in the archive
    fputs($this->zip_fd, $v_binary_data, 30);

    // ----- Write the variable fields
    if (strlen($p_header['stored_filename']) != 0)
    {
      fputs($this->zip_fd, $p_header['stored_filename'], strlen($p_header['stored_filename']));
    }
    if ($p_header['extra_len'] != 0)
    {
      fputs($this->zip_fd, $p_header['extra'], $p_header['extra_len']);
    }

    // ----- Return
    return $v_result;
  }
  // --------------------------------------------------------------------------------

  // --------------------------------------------------------------------------------
  // Function : privWriteCentralFileHeader()
  // Description :
  // Parameters :
  // Return Values :
  // --------------------------------------------------------------------------------
  function privWriteCentralFileHeader(&$p_header)
  {
    $v_result=1;

    // TBC
    //for(reset($p_header); $key = key($p_header); next($p_header)) {
    //}

    // ----- Transform UNIX mtime to DOS format mdate/mtime
    $v_date = getdate($p_header['mtime']);
    $v_mtime = ($v_date['hours']<<11) + ($v_date['minutes']<<5) + $v_date['seconds']/2;
    $v_mdate = (($v_date['year']-1980)<<9) + ($v_date['mon']<<5) + $v_date['mday'];


    // ----- Packed data
    $v_binary_data = pack("VvvvvvvVVVvvvvvVV", 0x02014b50,
	                      $p_header['version'], $p_header['version_extracted'],
                          $p_header['flag'], $p_header['compression'],
						  $v_mtime, $v_mdate, $p_header['crc'],
                          $p_header['compressed_size'], $p_header['size'],
                          strlen($p_header['stored_filename']),
						  $p_header['extra_len'], $p_header['comment_len'],
                          $p_header['disk'], $p_header['internal'],
						  $p_header['external'], $p_header['offset']);

    // ----- Write the 42 bytes of the header in the zip file
    fputs($this->zip_fd, $v_binary_data, 46);

    // ----- Write the variable fields
    if (strlen($p_header['stored_filename']) != 0)
    {
      fputs($this->zip_fd, $p_header['stored_filename'], strlen($p_header['stored_filename']));
    }
    if ($p_header['extra_len'] != 0)
    {
      fputs($this->zip_fd, $p_header['extra'], $p_header['extra_len']);
    }
    if ($p_header['comment_len'] != 0)
    {
      fputs($this->zip_fd, $p_header['comment'], $p_header['comment_len']);
    }

    // ----- Return
    return $v_result;
  }
  // --------------------------------------------------------------------------------

  // --------------------------------------------------------------------------------
  // Function : privWriteCentralHeader()
  // Description :
  // Parameters :
  // Return Values :
  // --------------------------------------------------------------------------------
  function privWriteCentralHeader($p_nb_entries, $p_size, $p_offset, $p_comment)
  {
    $v_result=1;

    // ----- Packed data
    $v_binary_data = pack("VvvvvVVv", 0x06054b50, 0, 0, $p_nb_entries,
	                      $p_nb_entries, $p_size,
						  $p_offset, strlen($p_comment));

    // ----- Write the 22 bytes of the header in the zip file
    fputs($this->zip_fd, $v_binary_data, 22);

    // ----- Write the variable fields
    if (strlen($p_comment) != 0)
    {
      fputs($this->zip_fd, $p_comment, strlen($p_comment));
    }

    // ----- Return
    return $v_result;
  }
  // --------------------------------------------------------------------------------

  // --------------------------------------------------------------------------------
  // Function : privList()
  // Description :
  // Parameters :
  // Return Values :
  // --------------------------------------------------------------------------------
  function privList(&$p_list)
  {
    $v_result=1;

    // ----- Magic quotes trick
    $this->privDisableMagicQuotes();

    // ----- Open the zip file
    if (($this->zip_fd = @fopen($this->zipname, 'rb')) == 0)
    {
      // ----- Magic quotes trick
      $this->privSwapBackMagicQuotes();

      // ----- Error log
      WPvivid_PclZip::privErrorLog(WPVIVID_PCLZIP_ERR_READ_OPEN_FAIL, 'Unable to open archive \''.$this->zipname.'\' in binary read mode');

      // ----- Return
      return WPvivid_PclZip::errorCode();
    }

    // ----- Read the central directory information
    $v_central_dir = array();
    if (($v_result = $this->privReadEndCentralDir($v_central_dir)) != 1)
    {
      $this->privSwapBackMagicQuotes();
      return $v_result;
    }

    // ----- Go to beginning of Central Dir
    @rewind($this->zip_fd);
    if (@fseek($this->zip_fd, $v_central_dir['offset']))
    {
      $this->privSwapBackMagicQuotes();

      // ----- Error log
      WPvivid_PclZip::privErrorLog(WPVIVID_PCLZIP_ERR_INVALID_ARCHIVE_ZIP, 'Invalid archive size');

      // ----- Return
      return WPvivid_PclZip::errorCode();
    }

    // ----- Read each entry
    for ($i=0; $i<$v_central_dir['entries']; $i++)
    {
      // ----- Read the file header
      if (($v_result = $this->privReadCentralFileHeader($v_header)) != 1)
      {
        $this->privSwapBackMagicQuotes();
        return $v_result;
      }
      $v_header['index'] = $i;

      // ----- Get the only interesting attributes
      $this->privConvertHeader2FileInfo($v_header, $p_list[$i]);
      unset($v_header);
    }

    // ----- Close the zip file
    $this->privCloseFd();

    // ----- Magic quotes trick
    $this->privSwapBackMagicQuotes();

    // ----- Return
    return $v_result;
  }
  // --------------------------------------------------------------------------------

  // --------------------------------------------------------------------------------
  // Function : privConvertHeader2FileInfo()
  // Description :
  //   This function takes the file information from the central directory
  //   entries and extract the interesting parameters that will be given back.
  //   The resulting file infos are set in the array $p_info
  //     $p_info['filename'] : Filename with full path. Given by user (add),
  //                           extracted in the filesystem (extract).
  //     $p_info['stored_filename'] : Stored filename in the archive.
  //     $p_info['size'] = Size of the file.
  //     $p_info['compressed_size'] = Compressed size of the file.
  //     $p_info['mtime'] = Last modification date of the file.
  //     $p_info['comment'] = Comment associated with the file.
  //     $p_info['folder'] = true/false : indicates if the entry is a folder or not.
  //     $p_info['status'] = status of the action on the file.
  //     $p_info['crc'] = CRC of the file content.
  // Parameters :
  // Return Values :
  // --------------------------------------------------------------------------------
  function privConvertHeader2FileInfo($p_header, &$p_info)
  {
    $v_result=1;

    // ----- Get the interesting attributes
    $v_temp_path = WPvivid_PclZipUtilPathReduction($p_header['filename']);
    $p_info['filename'] = $v_temp_path;
    $v_temp_path = WPvivid_PclZipUtilPathReduction($p_header['stored_filename']);
    $p_info['stored_filename'] = $v_temp_path;
    $p_info['size'] = $p_header['size'];
    $p_info['compressed_size'] = $p_header['compressed_size'];
    $p_info['mtime'] = $p_header['mtime'];
    $p_info['comment'] = $p_header['comment'];
    $p_info['folder'] = (($p_header['external']&0x00000010)==0x00000010);
    $p_info['index'] = $p_header['index'];
    $p_info['status'] = $p_header['status'];
    $p_info['crc'] = $p_header['crc'];

    // ----- Return
    return $v_result;
  }
  // --------------------------------------------------------------------------------

  // --------------------------------------------------------------------------------
  // Function : privExtractByRule()
  // Description :
  //   Extract a file or directory depending of rules (by index, by name, ...)
  // Parameters :
  //   $p_file_list : An array where will be placed the properties of each
  //                  extracted file
  //   $p_path : Path to add while writing the extracted files
  //   $p_remove_path : Path to remove (from the file memorized path) while writing the
  //                    extracted files. If the path does not match the file path,
  //                    the file is extracted with its memorized path.
  //                    $p_remove_path does not apply to 'list' mode.
  //                    $p_path and $p_remove_path are commulative.
  // Return Values :
  //   1 on success,0 or less on error (see error code list)
  // --------------------------------------------------------------------------------
  function privExtractByRule(&$p_file_list, $p_path, $p_remove_path, $p_remove_all_path, &$p_options)
  {
    $v_result=1;

    // ----- Magic quotes trick
    $this->privDisableMagicQuotes();

    // ----- Check the path
    if (   ($p_path == "")
	    || (   (substr($p_path, 0, 1) != "/")
		    && (substr($p_path, 0, 3) != "../")
			&& (substr($p_path,1,2)!=":/")))
      $p_path = "./".$p_path;

    // ----- Reduce the path last (and duplicated) '/'
    if (($p_path != "./") && ($p_path != "/"))
    {
      // ----- Look for the path end '/'
      while (substr($p_path, -1) == "/")
      {
        $p_path = substr($p_path, 0, strlen($p_path)-1);
      }
    }

    // ----- Look for path to remove format (should end by /)
    if (($p_remove_path != "") && (substr($p_remove_path, -1) != '/'))
    {
      $p_remove_path .= '/';
    }
    $p_remove_path_size = strlen($p_remove_path);

    // ----- Open the zip file
    if (($v_result = $this->privOpenFd('rb')) != 1)
    {
      $this->privSwapBackMagicQuotes();
      return $v_result;
    }

    // ----- Read the central directory information
    $v_central_dir = array();
    if (($v_result = $this->privReadEndCentralDir($v_central_dir)) != 1)
    {
      // ----- Close the zip file
      $this->privCloseFd();
      $this->privSwapBackMagicQuotes();

      return $v_result;
    }

    // ----- Start at beginning of Central Dir
    $v_pos_entry = $v_central_dir['offset'];

    // ----- Read each entry
    $j_start = 0;
    for ($i=0, $v_nb_extracted=0; $i<$v_central_dir['entries']; $i++)
    {

      // ----- Read next Central dir entry
      @rewind($this->zip_fd);
      if (@fseek($this->zip_fd, $v_pos_entry))
      {
        // ----- Close the zip file
        $this->privCloseFd();
        $this->privSwapBackMagicQuotes();

        // ----- Error log
        WPvivid_PclZip::privErrorLog(WPVIVID_PCLZIP_ERR_INVALID_ARCHIVE_ZIP, 'Invalid archive size');

        // ----- Return
        return WPvivid_PclZip::errorCode();
      }

      // ----- Read the file header
      $v_header = array();
      if (($v_result = $this->privReadCentralFileHeader($v_header)) != 1)
      {
        // ----- Close the zip file
        $this->privCloseFd();
        $this->privSwapBackMagicQuotes();

        return $v_result;
      }

      // ----- Store the index
      $v_header['index'] = $i;

      // ----- Store the file position
      $v_pos_entry = ftell($this->zip_fd);

      // ----- Look for the specific extract rules
      $v_extract = false;

      // ----- Look for extract by name rule
      if (   (isset($p_options[WPVIVID_PCLZIP_OPT_BY_NAME]))
          && ($p_options[WPVIVID_PCLZIP_OPT_BY_NAME] != 0)) {

          // ----- Look if the filename is in the list
          for ($j=0; ($j<sizeof($p_options[WPVIVID_PCLZIP_OPT_BY_NAME])) && (!$v_extract); $j++) {

              // ----- Look for a directory
              if (substr($p_options[WPVIVID_PCLZIP_OPT_BY_NAME][$j], -1) == "/") {

                  // ----- Look if the directory is in the filename path
                  if (   (strlen($v_header['stored_filename']) > strlen($p_options[WPVIVID_PCLZIP_OPT_BY_NAME][$j]))
                      && (substr($v_header['stored_filename'], 0, strlen($p_options[WPVIVID_PCLZIP_OPT_BY_NAME][$j])) == $p_options[WPVIVID_PCLZIP_OPT_BY_NAME][$j])) {
                      $v_extract = true;
                  }
              }
              // ----- Look for a filename
              elseif ($v_header['stored_filename'] == $p_options[WPVIVID_PCLZIP_OPT_BY_NAME][$j]) {
                  $v_extract = true;
              }
          }
      }

      // ----- Look for extract by ereg rule
      // ereg() is deprecated with PHP 5.3
      /*
      else if (   (isset($p_options[WPVIVID_PCLZIP_OPT_BY_EREG]))
               && ($p_options[WPVIVID_PCLZIP_OPT_BY_EREG] != "")) {

          if (ereg($p_options[WPVIVID_PCLZIP_OPT_BY_EREG], $v_header['stored_filename'])) {
              $v_extract = true;
          }
      }
      */

      // ----- Look for extract by preg rule
      else if (   (isset($p_options[WPVIVID_PCLZIP_OPT_BY_PREG]))
               && ($p_options[WPVIVID_PCLZIP_OPT_BY_PREG] != "")) {

          if (preg_match($p_options[WPVIVID_PCLZIP_OPT_BY_PREG], $v_header['stored_filename'])) {
              $v_extract = true;
          }
      }

      // ----- Look for extract by index rule
      else if (   (isset($p_options[WPVIVID_PCLZIP_OPT_BY_INDEX]))
               && ($p_options[WPVIVID_PCLZIP_OPT_BY_INDEX] != 0)) {

          // ----- Look if the index is in the list
          for ($j=$j_start; ($j<sizeof($p_options[WPVIVID_PCLZIP_OPT_BY_INDEX])) && (!$v_extract); $j++) {

              if (($i>=$p_options[WPVIVID_PCLZIP_OPT_BY_INDEX][$j]['start']) && ($i<=$p_options[WPVIVID_PCLZIP_OPT_BY_INDEX][$j]['end'])) {
                  $v_extract = true;
              }
              if ($i>=$p_options[WPVIVID_PCLZIP_OPT_BY_INDEX][$j]['end']) {
                  $j_start = $j+1;
              }

              if ($p_options[WPVIVID_PCLZIP_OPT_BY_INDEX][$j]['start']>$i) {
                  break;
              }
          }
      }

      // ----- Look for no rule, which means extract all the archive
      else {
          $v_extract = true;
      }

	  // ----- Check compression method
	  if (   ($v_extract)
	      && (   ($v_header['compression'] != 8)
		      && ($v_header['compression'] != 0))) {
          $v_header['status'] = 'unsupported_compression';

          // ----- Look for WPVIVID_PCLZIP_OPT_STOP_ON_ERROR
          if (   (isset($p_options[WPVIVID_PCLZIP_OPT_STOP_ON_ERROR]))
		      && ($p_options[WPVIVID_PCLZIP_OPT_STOP_ON_ERROR]===true)) {

              $this->privSwapBackMagicQuotes();

              WPvivid_PclZip::privErrorLog(WPVIVID_PCLZIP_ERR_UNSUPPORTED_COMPRESSION,
			                       "Filename '".$v_header['stored_filename']."' is "
				  	    	  	   ."compressed by an unsupported compression "
				  	    	  	   ."method (".$v_header['compression'].") ");

              return WPvivid_PclZip::errorCode();
		  }
	  }

	  // ----- Check encrypted files
	  if (($v_extract) && (($v_header['flag'] & 1) == 1)) {
          $v_header['status'] = 'unsupported_encryption';

          // ----- Look for WPVIVID_PCLZIP_OPT_STOP_ON_ERROR
          if (   (isset($p_options[WPVIVID_PCLZIP_OPT_STOP_ON_ERROR]))
		      && ($p_options[WPVIVID_PCLZIP_OPT_STOP_ON_ERROR]===true)) {

              $this->privSwapBackMagicQuotes();

              WPvivid_PclZip::privErrorLog(WPVIVID_PCLZIP_ERR_UNSUPPORTED_ENCRYPTION,
			                       "Unsupported encryption for "
				  	    	  	   ." filename '".$v_header['stored_filename']
								   ."'");

              return WPvivid_PclZip::errorCode();
		  }
    }

      // ----- Look for real extraction
      if (($v_extract) && ($v_header['status'] != 'ok')) {
          $v_result = $this->privConvertHeader2FileInfo($v_header,
		                                        $p_file_list[$v_nb_extracted++]);
          if ($v_result != 1) {
              $this->privCloseFd();
              $this->privSwapBackMagicQuotes();
              return $v_result;
          }

          $v_extract = false;
      }

      // ----- Look for real extraction
      if ($v_extract)
      {

        // ----- Go to the file position
        @rewind($this->zip_fd);
        if (@fseek($this->zip_fd, $v_header['offset']))
        {
          // ----- Close the zip file
          $this->privCloseFd();

          $this->privSwapBackMagicQuotes();

          // ----- Error log
          WPvivid_PclZip::privErrorLog(WPVIVID_PCLZIP_ERR_INVALID_ARCHIVE_ZIP, 'Invalid archive size');

          // ----- Return
          return WPvivid_PclZip::errorCode();
        }

        // ----- Look for extraction as string
        if ($p_options[WPVIVID_PCLZIP_OPT_EXTRACT_AS_STRING]) {

          $v_string = '';

          // ----- Extracting the file
          $v_result1 = $this->privExtractFileAsString($v_header, $v_string, $p_options);
          if ($v_result1 < 1) {
            $this->privCloseFd();
            $this->privSwapBackMagicQuotes();
            return $v_result1;
          }

          // ----- Get the only interesting attributes
          if (($v_result = $this->privConvertHeader2FileInfo($v_header, $p_file_list[$v_nb_extracted])) != 1)
          {
            // ----- Close the zip file
            $this->privCloseFd();
            $this->privSwapBackMagicQuotes();

            return $v_result;
          }

          // ----- Set the file content
          $p_file_list[$v_nb_extracted]['content'] = $v_string;

          // ----- Next extracted file
          $v_nb_extracted++;

          // ----- Look for user callback abort
          if ($v_result1 == 2) {
          	break;
          }
        }
        // ----- Look for extraction in standard output
        elseif (   (isset($p_options[WPVIVID_PCLZIP_OPT_EXTRACT_IN_OUTPUT]))
		        && ($p_options[WPVIVID_PCLZIP_OPT_EXTRACT_IN_OUTPUT])) {
          // ----- Extracting the file in standard output
          $v_result1 = $this->privExtractFileInOutput($v_header, $p_options);
          if ($v_result1 < 1) {
            $this->privCloseFd();
            $this->privSwapBackMagicQuotes();
            return $v_result1;
          }

          // ----- Get the only interesting attributes
          if (($v_result = $this->privConvertHeader2FileInfo($v_header, $p_file_list[$v_nb_extracted++])) != 1) {
            $this->privCloseFd();
            $this->privSwapBackMagicQuotes();
            return $v_result;
          }

          // ----- Look for user callback abort
          if ($v_result1 == 2) {
          	break;
          }
        }
        // ----- Look for normal extraction
        else {
          // ----- Extracting the file
          $v_result1 = $this->privExtractFile($v_header,
		                                      $p_path, $p_remove_path,
											  $p_remove_all_path,
											  $p_options);
          if ($v_result1 < 1) {
            $this->privCloseFd();
            $this->privSwapBackMagicQuotes();
            return $v_result1;
          }

          // ----- Get the only interesting attributes
          if (($v_result = $this->privConvertHeader2FileInfo($v_header, $p_file_list[$v_nb_extracted++])) != 1)
          {
            // ----- Close the zip file
            $this->privCloseFd();
            $this->privSwapBackMagicQuotes();

            return $v_result;
          }

          // ----- Look for user callback abort
          if ($v_result1 == 2) {
          	break;
          }
        }
      }
    }

    // ----- Close the zip file
    $this->privCloseFd();
    $this->privSwapBackMagicQuotes();

    // ----- Return
    return $v_result;
  }
  // --------------------------------------------------------------------------------

  // --------------------------------------------------------------------------------
  // Function : privExtractFile()
  // Description :
  // Parameters :
  // Return Values :
  //
  // 1 : ... ?
  // WPVIVID_PCLZIP_ERR_USER_ABORTED(2) : User ask for extraction stop in callback
  // --------------------------------------------------------------------------------
  function privExtractFile(&$p_entry, $p_path, $p_remove_path, $p_remove_all_path, &$p_options)
  {
    $v_result=1;

    // ----- Read the file header
    if (($v_result = $this->privReadFileHeader($v_header)) != 1)
    {
      // ----- Return
      return $v_result;
    }


    // ----- Check that the file header is coherent with $p_entry info
    if ($this->privCheckFileHeaders($v_header, $p_entry) != 1) {
        // TBC
    }

    // ----- Look for all path to remove
    if ($p_remove_all_path == true) {
        // ----- Look for folder entry that not need to be extracted
        if (($p_entry['external']&0x00000010)==0x00000010) {

            $p_entry['status'] = "filtered";

            return $v_result;
        }

        // ----- Get the basename of the path
        $p_entry['filename'] = basename($p_entry['filename']);
    }

    // ----- Look for path to remove
    else if ($p_remove_path != "")
    {
      if (WPvivid_PclZipUtilPathInclusion($p_remove_path, $p_entry['filename']) == 2)
      {

        // ----- Change the file status
        $p_entry['status'] = "filtered";

        // ----- Return
        return $v_result;
      }

      $p_remove_path_size = strlen($p_remove_path);
      if (substr($p_entry['filename'], 0, $p_remove_path_size) == $p_remove_path)
      {

        // ----- Remove the path
        $p_entry['filename'] = substr($p_entry['filename'], $p_remove_path_size);

      }
    }

    // ----- Add the path
    if ($p_path != '') {
      $p_entry['filename'] = $p_path."/".$p_entry['filename'];
    }

    // ----- Check a base_dir_restriction
    if (isset($p_options[WPVIVID_PCLZIP_OPT_EXTRACT_DIR_RESTRICTION])) {
      $v_inclusion
      = WPvivid_PclZipUtilPathInclusion($p_options[WPVIVID_PCLZIP_OPT_EXTRACT_DIR_RESTRICTION],
                                $p_entry['filename']);
      if ($v_inclusion == 0) {

        WPvivid_PclZip::privErrorLog(WPVIVID_PCLZIP_ERR_DIRECTORY_RESTRICTION,
			                     "Filename '".$p_entry['filename']."' is "
								 ."outside WPVIVID_PCLZIP_OPT_EXTRACT_DIR_RESTRICTION");

        return WPvivid_PclZip::errorCode();
      }
    }

    // ----- Look for pre-extract callback
    if (isset($p_options[WPVIVID_PCLZIP_CB_PRE_EXTRACT])) {

      // ----- Generate a local information
      $v_local_header = array();
      $this->privConvertHeader2FileInfo($p_entry, $v_local_header);

      // ----- Call the callback
      // Here I do not use call_user_func() because I need to send a reference to the
      // header.
      $v_result = $p_options[WPVIVID_PCLZIP_CB_PRE_EXTRACT](WPVIVID_PCLZIP_CB_PRE_EXTRACT, $v_local_header);
      if ($v_result == 0) {
        // ----- Change the file status
        $p_entry['status'] = "skipped";
        $v_result = 1;
      }

      // ----- Look for abort result
      if ($v_result == 2) {
        // ----- This status is internal and will be changed in 'skipped'
        $p_entry['status'] = "aborted";
      	$v_result = WPVIVID_PCLZIP_ERR_USER_ABORTED;
      }

      // ----- Update the information
      // Only some fields can be modified
      $p_entry['filename'] = $v_local_header['filename'];
    }


    // ----- Look if extraction should be done
    if ($p_entry['status'] == 'ok') {

    // ----- Look for specific actions while the file exist
    if (file_exists($p_entry['filename']))
    {

      // ----- Look if file is a directory
      if (is_dir($p_entry['filename']))
      {

        // ----- Change the file status
        $p_entry['status'] = "already_a_directory";

        // ----- Look for WPVIVID_PCLZIP_OPT_STOP_ON_ERROR
        // For historical reason first PclZip implementation does not stop
        // when this kind of error occurs.
        if (   (isset($p_options[WPVIVID_PCLZIP_OPT_STOP_ON_ERROR]))
		    && ($p_options[WPVIVID_PCLZIP_OPT_STOP_ON_ERROR]===true)) {

            WPvivid_PclZip::privErrorLog(WPVIVID_PCLZIP_ERR_ALREADY_A_DIRECTORY,
			                     "Filename '".$p_entry['filename']."' is "
								 ."already used by an existing directory");

            return WPvivid_PclZip::errorCode();
		    }
      }
      // ----- Look if file is write protected
      else if (!is_writeable($p_entry['filename']))
      {

        // ----- Change the file status
        $p_entry['status'] = "write_protected";

        // ----- Look for WPVIVID_PCLZIP_OPT_STOP_ON_ERROR
        // For historical reason first PclZip implementation does not stop
        // when this kind of error occurs.
        if (   (isset($p_options[WPVIVID_PCLZIP_OPT_STOP_ON_ERROR]))
		    && ($p_options[WPVIVID_PCLZIP_OPT_STOP_ON_ERROR]===true)) {

            WPvivid_PclZip::privErrorLog(WPVIVID_PCLZIP_ERR_WRITE_OPEN_FAIL,
			                     "Filename '".$p_entry['filename']."' exists "
								 ."and is write protected");

            return WPvivid_PclZip::errorCode();
		    }
      }

      // ----- Look if the extracted file is older
      else if (filemtime($p_entry['filename']) > $p_entry['mtime'])
      {
        // ----- Change the file status
        if (   (isset($p_options[WPVIVID_PCLZIP_OPT_REPLACE_NEWER]))
		    && ($p_options[WPVIVID_PCLZIP_OPT_REPLACE_NEWER]===true)) {
	  	  }
		    else {
            $p_entry['status'] = "newer_exist";

            // ----- Look for WPVIVID_PCLZIP_OPT_STOP_ON_ERROR
            // For historical reason first PclZip implementation does not stop
            // when this kind of error occurs.
            if (   (isset($p_options[WPVIVID_PCLZIP_OPT_STOP_ON_ERROR]))
		        && ($p_options[WPVIVID_PCLZIP_OPT_STOP_ON_ERROR]===true)) {

                WPvivid_PclZip::privErrorLog(WPVIVID_PCLZIP_ERR_WRITE_OPEN_FAIL,
			             "Newer version of '".$p_entry['filename']."' exists "
					    ."and option WPVIVID_PCLZIP_OPT_REPLACE_NEWER is not selected");

                return WPvivid_PclZip::errorCode();
		      }
		    }
      }
      else {
      }
    }

    // ----- Check the directory availability and create it if necessary
    else {
      if ((($p_entry['external']&0x00000010)==0x00000010) || (substr($p_entry['filename'], -1) == '/'))
        $v_dir_to_check = $p_entry['filename'];
      else if (!strstr($p_entry['filename'], "/"))
        $v_dir_to_check = "";
      else
        $v_dir_to_check = dirname($p_entry['filename']);

        if (($v_result = $this->privDirCheck($v_dir_to_check, (($p_entry['external']&0x00000010)==0x00000010))) != 1) {

          // ----- Change the file status
          $p_entry['status'] = "path_creation_fail";

          // ----- Return
          //return $v_result;
          $v_result = 1;
        }
      }
    }

    // ----- Look if extraction should be done
    if ($p_entry['status'] == 'ok') {

      // ----- Do the extraction (if not a folder)
      if (!(($p_entry['external']&0x00000010)==0x00000010))
      {
        // ----- Look for not compressed file
        if ($p_entry['compression'] == 0) {

    		  // ----- Opening destination file
          if (($v_dest_file = @fopen($p_entry['filename'], 'wb')) == 0)
          {

            // ----- Change the file status
            $p_entry['status'] = "write_error";

            // ----- Return
            return $v_result;
          }


          // ----- Read the file by PCLZIP_READ_BLOCK_SIZE octets blocks
          $v_size = $p_entry['compressed_size'];
          while ($v_size != 0)
          {
            $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE);
            $v_buffer = @fread($this->zip_fd, $v_read_size);
            /* Try to speed up the code
            $v_binary_data = pack('a'.$v_read_size, $v_buffer);
            @fwrite($v_dest_file, $v_binary_data, $v_read_size);
            */
            @fwrite($v_dest_file, $v_buffer, $v_read_size);
            $v_size -= $v_read_size;
          }

          // ----- Closing the destination file
          fclose($v_dest_file);

          // ----- Change the file mtime
          touch($p_entry['filename'], $p_entry['mtime']);


        }
        else {
          // ----- TBC
          // Need to be finished
          if (($p_entry['flag'] & 1) == 1) {
            WPvivid_PclZip::privErrorLog(WPVIVID_PCLZIP_ERR_UNSUPPORTED_ENCRYPTION, 'File \''.$p_entry['filename'].'\' is encrypted. Encrypted files are not supported.');
            return WPvivid_PclZip::errorCode();
          }


          // ----- Look for using temporary file to unzip
          if ( (!isset($p_options[WPVIVID_PCLZIP_OPT_TEMP_FILE_OFF]))
              && (isset($p_options[WPVIVID_PCLZIP_OPT_TEMP_FILE_ON])
                  || (isset($p_options[WPVIVID_PCLZIP_OPT_TEMP_FILE_THRESHOLD])
                      && ($p_options[WPVIVID_PCLZIP_OPT_TEMP_FILE_THRESHOLD] <= $p_entry['size'])) ) ) {
            $v_result = $this->privExtractFileUsingTempFile($p_entry, $p_options);
            if ($v_result < WPVIVID_PCLZIP_ERR_NO_ERROR) {
              return $v_result;
            }
          }

          // ----- Look for extract in memory
          else {


            // ----- Read the compressed file in a buffer (one shot)
            if ( $p_entry['compressed_size'] > 0 ) {
              $v_buffer = @fread($this->zip_fd, $p_entry['compressed_size']);
            }
            else {
              $v_buffer = false;
            }

            // ----- Decompress the file
            $v_file_content = @gzinflate($v_buffer);
            unset($v_buffer);
            if ($v_file_content === FALSE) {

              // ----- Change the file status
              // TBC
              $p_entry['status'] = "error";

              return $v_result;
            }

            // ----- Opening destination file
            if (($v_dest_file = @fopen($p_entry['filename'], 'wb')) == 0) {

              // ----- Change the file status
              $p_entry['status'] = "write_error";

              return $v_result;
            }

            // ----- Write the uncompressed data
            @fwrite($v_dest_file, $v_file_content, $p_entry['size']);
            unset($v_file_content);

            // ----- Closing the destination file
            @fclose($v_dest_file);

          }

          // ----- Change the file mtime
          @touch($p_entry['filename'], $p_entry['mtime']);
        }

        // ----- Look for chmod option
        if (isset($p_options[WPVIVID_PCLZIP_OPT_SET_CHMOD])) {

          // ----- Change the mode of the file
          @chmod($p_entry['filename'], $p_options[WPVIVID_PCLZIP_OPT_SET_CHMOD]);
        }

      }
    }

  	// ----- Change abort status
  	if ($p_entry['status'] == "aborted") {
        $p_entry['status'] = "skipped";
  	}

    // ----- Look for post-extract callback
    elseif (isset($p_options[WPVIVID_PCLZIP_CB_POST_EXTRACT])) {

      // ----- Generate a local information
      $v_local_header = array();
      $this->privConvertHeader2FileInfo($p_entry, $v_local_header);

      // ----- Call the callback
      // Here I do not use call_user_func() because I need to send a reference to the
      // header.
      $v_result = $p_options[WPVIVID_PCLZIP_CB_POST_EXTRACT](WPVIVID_PCLZIP_CB_POST_EXTRACT, $v_local_header);

      // ----- Look for abort result
      if ($v_result == 2) {
      	$v_result = WPVIVID_PCLZIP_ERR_USER_ABORTED;
      }
    }

    // ----- Return
    return $v_result;
  }
  // --------------------------------------------------------------------------------

  // --------------------------------------------------------------------------------
  // Function : privExtractFileUsingTempFile()
  // Description :
  // Parameters :
  // Return Values :
  // --------------------------------------------------------------------------------
  function privExtractFileUsingTempFile(&$p_entry, &$p_options)
  {
    $v_result=1;

    // ----- Creates a temporary file
    $v_gzip_temp_name = PCLZIP_TEMPORARY_DIR.uniqid('pclzip-').'.gz';
    if (($v_dest_file = @fopen($v_gzip_temp_name, "wb")) == 0) {
      fclose($v_file);
      WPvivid_PclZip::privErrorLog(WPVIVID_PCLZIP_ERR_WRITE_OPEN_FAIL, 'Unable to open temporary file \''.$v_gzip_temp_name.'\' in binary write mode');
      return WPvivid_PclZip::errorCode();
    }


    // ----- Write gz file format header
    $v_binary_data = pack('va1a1Va1a1', 0x8b1f, Chr($p_entry['compression']), Chr(0x00), time(), Chr(0x00), Chr(3));
    @fwrite($v_dest_file, $v_binary_data, 10);

    // ----- Read the file by PCLZIP_READ_BLOCK_SIZE octets blocks
    $v_size = $p_entry['compressed_size'];
    while ($v_size != 0)
    {
      $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE);
      $v_buffer = @fread($this->zip_fd, $v_read_size);
      //$v_binary_data = pack('a'.$v_read_size, $v_buffer);
      @fwrite($v_dest_file, $v_buffer, $v_read_size);
      $v_size -= $v_read_size;
    }

    // ----- Write gz file format footer
    $v_binary_data = pack('VV', $p_entry['crc'], $p_entry['size']);
    @fwrite($v_dest_file, $v_binary_data, 8);

    // ----- Close the temporary file
    @fclose($v_dest_file);

    // ----- Opening destination file
    if (($v_dest_file = @fopen($p_entry['filename'], 'wb')) == 0) {
      $p_entry['status'] = "write_error";
      return $v_result;
    }

    // ----- Open the temporary gz file
    if (($v_src_file = @gzopen($v_gzip_temp_name, 'rb')) == 0) {
      @fclose($v_dest_file);
      $p_entry['status'] = "read_error";
      WPvivid_PclZip::privErrorLog(WPVIVID_PCLZIP_ERR_READ_OPEN_FAIL, 'Unable to open temporary file \''.$v_gzip_temp_name.'\' in binary read mode');
      return WPvivid_PclZip::errorCode();
    }


    // ----- Read the file by PCLZIP_READ_BLOCK_SIZE octets blocks
    $v_size = $p_entry['size'];
    while ($v_size != 0) {
      $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE);
      $v_buffer = @gzread($v_src_file, $v_read_size);
      //$v_binary_data = pack('a'.$v_read_size, $v_buffer);
      @fwrite($v_dest_file, $v_buffer, $v_read_size);
      $v_size -= $v_read_size;
    }
    @fclose($v_dest_file);
    @gzclose($v_src_file);

    // ----- Delete the temporary file
    @wp_delete_file($v_gzip_temp_name);

    // ----- Return
    return $v_result;
  }
  // --------------------------------------------------------------------------------

  // --------------------------------------------------------------------------------
  // Function : privExtractFileInOutput()
  // Description :
  // Parameters :
  // Return Values :
  // --------------------------------------------------------------------------------
  function privExtractFileInOutput(&$p_entry, &$p_options)
  {
    $v_result=1;

    // ----- Read the file header
    if (($v_result = $this->privReadFileHeader($v_header)) != 1) {
      return $v_result;
    }


    // ----- Check that the file header is coherent with $p_entry info
    if ($this->privCheckFileHeaders($v_header, $p_entry) != 1) {
        // TBC
    }

    // ----- Look for pre-extract callback
    if (isset($p_options[WPVIVID_PCLZIP_CB_PRE_EXTRACT])) {

      // ----- Generate a local information
      $v_local_header = array();
      $this->privConvertHeader2FileInfo($p_entry, $v_local_header);

      // ----- Call the callback
      // Here I do not use call_user_func() because I need to send a reference to the
      // header.
//      eval('$v_result = '.$p_options[WPVIVID_PCLZIP_CB_PRE_EXTRACT].'(WPVIVID_PCLZIP_CB_PRE_EXTRACT, $v_local_header);');
      $v_result = $p_options[WPVIVID_PCLZIP_CB_PRE_EXTRACT](WPVIVID_PCLZIP_CB_PRE_EXTRACT, $v_local_header);
      if ($v_result == 0) {
        // ----- Change the file status
        $p_entry['status'] = "skipped";
        $v_result = 1;
      }

      // ----- Look for abort result
      if ($v_result == 2) {
        // ----- This status is internal and will be changed in 'skipped'
        $p_entry['status'] = "aborted";
      	$v_result = WPVIVID_PCLZIP_ERR_USER_ABORTED;
      }

      // ----- Update the information
      // Only some fields can be modified
      $p_entry['filename'] = $v_local_header['filename'];
    }

    // ----- Trace

    // ----- Look if extraction should be done
    if ($p_entry['status'] == 'ok') {

      // ----- Do the extraction (if not a folder)
      if (!(($p_entry['external']&0x00000010)==0x00000010)) {
        // ----- Look for not compressed file
        if ($p_entry['compressed_size'] == $p_entry['size']) {

          // ----- Read the file in a buffer (one shot)
          if ( $p_entry['compressed_size'] > 0 ) {
            $v_buffer = @fread($this->zip_fd, $p_entry['compressed_size']);
          }
          else {
            $v_buffer = false;
          }

          // ----- Send the file to the output
          echo $v_buffer;
          unset($v_buffer);
        }
        else {

          // ----- Read the compressed file in a buffer (one shot)
          if ( $p_entry['compressed_size'] > 0 ) {
            $v_buffer = @fread($this->zip_fd, $p_entry['compressed_size']);
          }
          else {
            $v_buffer = false;
          }

          // ----- Decompress the file
          $v_file_content = gzinflate($v_buffer);
          unset($v_buffer);

          // ----- Send the file to the output
          echo $v_file_content;
          unset($v_file_content);
        }
      }
    }

	// ----- Change abort status
	if ($p_entry['status'] == "aborted") {
      $p_entry['status'] = "skipped";
	}

    // ----- Look for post-extract callback
    elseif (isset($p_options[WPVIVID_PCLZIP_CB_POST_EXTRACT])) {

      // ----- Generate a local information
      $v_local_header = array();
      $this->privConvertHeader2FileInfo($p_entry, $v_local_header);

      // ----- Call the callback
      // Here I do not use call_user_func() because I need to send a reference to the
      // header.
      $v_result = $p_options[WPVIVID_PCLZIP_CB_POST_EXTRACT](WPVIVID_PCLZIP_CB_POST_EXTRACT, $v_local_header);

      // ----- Look for abort result
      if ($v_result == 2) {
      	$v_result = WPVIVID_PCLZIP_ERR_USER_ABORTED;
      }
    }

    return $v_result;
  }
  // --------------------------------------------------------------------------------

  // --------------------------------------------------------------------------------
  // Function : privExtractFileAsString()
  // Description :
  // Parameters :
  // Return Values :
  // --------------------------------------------------------------------------------
  function privExtractFileAsString(&$p_entry, &$p_string, &$p_options)
  {
    $v_result=1;

    // ----- Read the file header
    $v_header = array();
    if (($v_result = $this->privReadFileHeader($v_header)) != 1)
    {
      // ----- Return
      return $v_result;
    }


    // ----- Check that the file header is coherent with $p_entry info
    if ($this->privCheckFileHeaders($v_header, $p_entry) != 1) {
        // TBC
    }

    // ----- Look for pre-extract callback
    if (isset($p_options[WPVIVID_PCLZIP_CB_PRE_EXTRACT])) {

      // ----- Generate a local information
      $v_local_header = array();
      $this->privConvertHeader2FileInfo($p_entry, $v_local_header);

      // ----- Call the callback
      // Here I do not use call_user_func() because I need to send a reference to the
      // header.
      $v_result = $p_options[WPVIVID_PCLZIP_CB_PRE_EXTRACT](WPVIVID_PCLZIP_CB_PRE_EXTRACT, $v_local_header);
      if ($v_result == 0) {
        // ----- Change the file status
        $p_entry['status'] = "skipped";
        $v_result = 1;
      }

      // ----- Look for abort result
      if ($v_result == 2) {
        // ----- This status is internal and will be changed in 'skipped'
        $p_entry['status'] = "aborted";
      	$v_result = WPVIVID_PCLZIP_ERR_USER_ABORTED;
      }

      // ----- Update the information
      // Only some fields can be modified
      $p_entry['filename'] = $v_local_header['filename'];
    }


    // ----- Look if extraction should be done
    if ($p_entry['status'] == 'ok') {

      // ----- Do the extraction (if not a folder)
      if (!(($p_entry['external']&0x00000010)==0x00000010)) {
        // ----- Look for not compressed file
  //      if ($p_entry['compressed_size'] == $p_entry['size'])
        if ($p_entry['compression'] == 0) {

          // ----- Reading the file
          if ( $p_entry['compressed_size'] > 0 ) {
            $p_string = @fread($this->zip_fd, $p_entry['compressed_size']);
          }
          else {
            $p_string = false;
          }
        }
        else {

          // ----- Reading the file
          if ( $p_entry['compressed_size'] > 0 ) {
            $v_data = @fread($this->zip_fd, $p_entry['compressed_size']);
          }
          else {
            $v_data = false;
          }

          // ----- Decompress the file
          if (($p_string = @gzinflate($v_data)) === FALSE) {
              // TBC
          }
        }

        // ----- Trace
      }
      else {
          // TBC : error : can not extract a folder in a string
      }

    }

  	// ----- Change abort status
  	if ($p_entry['status'] == "aborted") {
        $p_entry['status'] = "skipped";
  	}

    // ----- Look for post-extract callback
    elseif (isset($p_options[WPVIVID_PCLZIP_CB_POST_EXTRACT])) {

      // ----- Generate a local information
      $v_local_header = array();
      $this->privConvertHeader2FileInfo($p_entry, $v_local_header);

      // ----- Swap the content to header
      $v_local_header['content'] = $p_string;
      $p_string = '';

      // ----- Call the callback
      // Here I do not use call_user_func() because I need to send a reference to the
      // header.
      $v_result = $p_options[WPVIVID_PCLZIP_CB_POST_EXTRACT](WPVIVID_PCLZIP_CB_POST_EXTRACT, $v_local_header);

      // ----- Swap back the content to header
      $p_string = $v_local_header['content'];
      unset($v_local_header['content']);

      // ----- Look for abort result
      if ($v_result == 2) {
      	$v_result = WPVIVID_PCLZIP_ERR_USER_ABORTED;
      }
    }

    // ----- Return
    return $v_result;
  }
  // --------------------------------------------------------------------------------

  // --------------------------------------------------------------------------------
  // Function : privReadFileHeader()
  // Description :
  // Parameters :
  // Return Values :
  // --------------------------------------------------------------------------------
  function privReadFileHeader(&$p_header)
  {
    $v_result=1;

    // ----- Read the 4 bytes signature
    $v_binary_data = @fread($this->zip_fd, 4);
    $v_data = unpack('Vid', $v_binary_data);

    // ----- Check signature
    if ($v_data['id'] != 0x04034b50)
    {

      // ----- Error log
      WPvivid_PclZip::privErrorLog(WPVIVID_PCLZIP_ERR_BAD_FORMAT, 'Invalid archive structure');

      // ----- Return
      return WPvivid_PclZip::errorCode();
    }

    // ----- Read the first 42 bytes of the header
    $v_binary_data = fread($this->zip_fd, 26);

    // ----- Look for invalid block size
    if (strlen($v_binary_data) != 26)
    {
      $p_header['filename'] = "";
      $p_header['status'] = "invalid_header";

      // ----- Error log
      WPvivid_PclZip::privErrorLog(WPVIVID_PCLZIP_ERR_BAD_FORMAT, "Invalid block size : ".strlen($v_binary_data));

      // ----- Return
      return WPvivid_PclZip::errorCode();
    }

    // ----- Extract the values
    $v_data = unpack('vversion/vflag/vcompression/vmtime/vmdate/Vcrc/Vcompressed_size/Vsize/vfilename_len/vextra_len', $v_binary_data);

    // ----- Get filename
    $p_header['filename'] = fread($this->zip_fd, $v_data['filename_len']);

    // ----- Get extra_fields
    if ($v_data['extra_len'] != 0) {
      $p_header['extra'] = fread($this->zip_fd, $v_data['extra_len']);
    }
    else {
      $p_header['extra'] = '';
    }

    // ----- Extract properties
    $p_header['version_extracted'] = $v_data['version'];
    $p_header['compression'] = $v_data['compression'];
    $p_header['size'] = $v_data['size'];
    $p_header['compressed_size'] = $v_data['compressed_size'];
    $p_header['crc'] = $v_data['crc'];
    $p_header['flag'] = $v_data['flag'];
    $p_header['filename_len'] = $v_data['filename_len'];

    // ----- Recuperate date in UNIX format
    $p_header['mdate'] = $v_data['mdate'];
    $p_header['mtime'] = $v_data['mtime'];
    if ($p_header['mdate'] && $p_header['mtime'])
    {
      // ----- Extract time
      $v_hour = ($p_header['mtime'] & 0xF800) >> 11;
      $v_minute = ($p_header['mtime'] & 0x07E0) >> 5;
      $v_seconde = ($p_header['mtime'] & 0x001F)*2;

      // ----- Extract date
      $v_year = (($p_header['mdate'] & 0xFE00) >> 9) + 1980;
      $v_month = ($p_header['mdate'] & 0x01E0) >> 5;
      $v_day = $p_header['mdate'] & 0x001F;

      // ----- Get UNIX date format
      $p_header['mtime'] = @mktime($v_hour, $v_minute, $v_seconde, $v_month, $v_day, $v_year);

    }
    else
    {
      $p_header['mtime'] = time();
    }

    // TBC
    //for(reset($v_data); $key = key($v_data); next($v_data)) {
    //}

    // ----- Set the stored filename
    $p_header['stored_filename'] = $p_header['filename'];

    // ----- Set the status field
    $p_header['status'] = "ok";

    // ----- Return
    return $v_result;
  }
  // --------------------------------------------------------------------------------

  // --------------------------------------------------------------------------------
  // Function : privReadCentralFileHeader()
  // Description :
  // Parameters :
  // Return Values :
  // --------------------------------------------------------------------------------
  function privReadCentralFileHeader(&$p_header)
  {
    $v_result=1;

    // ----- Read the 4 bytes signature
    $v_binary_data = @fread($this->zip_fd, 4);
    $v_data = unpack('Vid', $v_binary_data);

    // ----- Check signature
    if ($v_data['id'] != 0x02014b50)
    {

      // ----- Error log
      WPvivid_PclZip::privErrorLog(WPVIVID_PCLZIP_ERR_BAD_FORMAT, 'Invalid archive structure');

      // ----- Return
      return WPvivid_PclZip::errorCode();
    }

    // ----- Read the first 42 bytes of the header
    $v_binary_data = fread($this->zip_fd, 42);

    // ----- Look for invalid block size
    if (strlen($v_binary_data) != 42)
    {
      $p_header['filename'] = "";
      $p_header['status'] = "invalid_header";

      // ----- Error log
      WPvivid_PclZip::privErrorLog(WPVIVID_PCLZIP_ERR_BAD_FORMAT, "Invalid block size : ".strlen($v_binary_data));

      // ----- Return
      return WPvivid_PclZip::errorCode();
    }

    // ----- Extract the values
    $p_header = unpack('vversion/vversion_extracted/vflag/vcompression/vmtime/vmdate/Vcrc/Vcompressed_size/Vsize/vfilename_len/vextra_len/vcomment_len/vdisk/vinternal/Vexternal/Voffset', $v_binary_data);

    // ----- Get filename
    if ($p_header['filename_len'] != 0)
      $p_header['filename'] = fread($this->zip_fd, $p_header['filename_len']);
    else
      $p_header['filename'] = '';

    // ----- Get extra
    if ($p_header['extra_len'] != 0)
      $p_header['extra'] = fread($this->zip_fd, $p_header['extra_len']);
    else
      $p_header['extra'] = '';

    // ----- Get comment
    if ($p_header['comment_len'] != 0)
      $p_header['comment'] = fread($this->zip_fd, $p_header['comment_len']);
    else
      $p_header['comment'] = '';

    // ----- Extract properties

    // ----- Recuperate date in UNIX format
    //if ($p_header['mdate'] && $p_header['mtime'])
    // TBC : bug : this was ignoring time with 0/0/0
    if (1)
    {
      // ----- Extract time
      $v_hour = ($p_header['mtime'] & 0xF800) >> 11;
      $v_minute = ($p_header['mtime'] & 0x07E0) >> 5;
      $v_seconde = ($p_header['mtime'] & 0x001F)*2;

      // ----- Extract date
      $v_year = (($p_header['mdate'] & 0xFE00) >> 9) + 1980;
      $v_month = ($p_header['mdate'] & 0x01E0) >> 5;
      $v_day = $p_header['mdate'] & 0x001F;

      // ----- Get UNIX date format
      $p_header['mtime'] = @mktime($v_hour, $v_minute, $v_seconde, $v_month, $v_day, $v_year);

    }
    else
    {
      $p_header['mtime'] = time();
    }

    // ----- Set the stored filename
    $p_header['stored_filename'] = $p_header['filename'];

    // ----- Set default status to ok
    $p_header['status'] = 'ok';

    // ----- Look if it is a directory
    if (substr($p_header['filename'], -1) == '/') {
      //$p_header['external'] = 0x41FF0010;
      $p_header['external'] = 0x00000010;
    }


    // ----- Return
    return $v_result;
  }
  // --------------------------------------------------------------------------------

  // --------------------------------------------------------------------------------
  // Function : privCheckFileHeaders()
  // Description :
  // Parameters :
  // Return Values :
  //   1 on success,
  //   0 on error;
  // --------------------------------------------------------------------------------
  function privCheckFileHeaders(&$p_local_header, &$p_central_header)
  {
    $v_result=1;

  	// ----- Check the static values
  	// TBC
  	if ($p_local_header['filename'] != $p_central_header['filename']) {
  	}
  	if ($p_local_header['version_extracted'] != $p_central_header['version_extracted']) {
  	}
  	if ($p_local_header['flag'] != $p_central_header['flag']) {
  	}
  	if ($p_local_header['compression'] != $p_central_header['compression']) {
  	}
  	if ($p_local_header['mtime'] != $p_central_header['mtime']) {
  	}
  	if ($p_local_header['filename_len'] != $p_central_header['filename_len']) {
  	}

  	// ----- Look for flag bit 3
  	if (($p_local_header['flag'] & 8) == 8) {
          $p_local_header['size'] = $p_central_header['size'];
          $p_local_header['compressed_size'] = $p_central_header['compressed_size'];
          $p_local_header['crc'] = $p_central_header['crc'];
  	}

    // ----- Return
    return $v_result;
  }
  // --------------------------------------------------------------------------------

  // --------------------------------------------------------------------------------
  // Function : privReadEndCentralDir()
  // Description :
  // Parameters :
  // Return Values :
  // --------------------------------------------------------------------------------
  function privReadEndCentralDir(&$p_central_dir)
  {
    $v_result=1;

    // ----- Go to the end of the zip file
    $v_size = filesize($this->zipname);
    @fseek($this->zip_fd, $v_size);
    if (@ftell($this->zip_fd) != $v_size)
    {
      // ----- Error log
      WPvivid_PclZip::privErrorLog(WPVIVID_PCLZIP_ERR_BAD_FORMAT, 'Unable to go to the end of the archive \''.$this->zipname.'\'');

      // ----- Return
      return WPvivid_PclZip::errorCode();
    }

    // ----- First try : look if this is an archive with no commentaries (most of the time)
    // in this case the end of central dir is at 22 bytes of the file end
    $v_found = 0;
    if ($v_size > 26) {
      @fseek($this->zip_fd, $v_size-22);
      if (($v_pos = @ftell($this->zip_fd)) != ($v_size-22))
      {
        // ----- Error log
        WPvivid_PclZip::privErrorLog(WPVIVID_PCLZIP_ERR_BAD_FORMAT, 'Unable to seek back to the middle of the archive \''.$this->zipname.'\'');

        // ----- Return
        return WPvivid_PclZip::errorCode();
      }

      // ----- Read for bytes
      $v_binary_data = @fread($this->zip_fd, 4);
      $v_data = @unpack('Vid', $v_binary_data);

      // ----- Check signature
      if ($v_data['id'] == 0x06054b50) {
        $v_found = 1;
      }

      $v_pos = ftell($this->zip_fd);
    }

    // ----- Go back to the maximum possible size of the Central Dir End Record
    if (!$v_found) {
      $v_maximum_size = 65557; // 0xFFFF + 22;
      if ($v_maximum_size > $v_size)
        $v_maximum_size = $v_size;
      @fseek($this->zip_fd, $v_size-$v_maximum_size);
      if (@ftell($this->zip_fd) != ($v_size-$v_maximum_size))
      {
        // ----- Error log
        WPvivid_PclZip::privErrorLog(WPVIVID_PCLZIP_ERR_BAD_FORMAT, 'Unable to seek back to the middle of the archive \''.$this->zipname.'\'');

        // ----- Return
        return WPvivid_PclZip::errorCode();
      }

      // ----- Read byte per byte in order to find the signature
      $v_pos = ftell($this->zip_fd);
      $v_bytes = 0x00000000;
      while ($v_pos < $v_size)
      {
        // ----- Read a byte
        $v_byte = @fread($this->zip_fd, 1);

        // -----  Add the byte
        //$v_bytes = ($v_bytes << 8) | Ord($v_byte);
        // Note we mask the old value down such that once shifted we can never end up with more than a 32bit number
        // Otherwise on systems where we have 64bit integers the check below for the magic number will fail.
        $v_bytes = ( ($v_bytes & 0xFFFFFF) << 8) | Ord($v_byte);

        // ----- Compare the bytes
        if ($v_bytes == 0x504b0506)
        {
          $v_pos++;
          break;
        }

        $v_pos++;
      }

      // ----- Look if not found end of central dir
      if ($v_pos == $v_size)
      {

        // ----- Error log
        WPvivid_PclZip::privErrorLog(WPVIVID_PCLZIP_ERR_BAD_FORMAT, "Unable to find End of Central Dir Record signature");

        // ----- Return
        return WPvivid_PclZip::errorCode();
      }
    }

    // ----- Read the first 18 bytes of the header
    $v_binary_data = fread($this->zip_fd, 18);

    // ----- Look for invalid block size
    if (strlen($v_binary_data) != 18)
    {

      // ----- Error log
      WPvivid_PclZip::privErrorLog(WPVIVID_PCLZIP_ERR_BAD_FORMAT, "Invalid End of Central Dir Record size : ".strlen($v_binary_data));

      // ----- Return
      return WPvivid_PclZip::errorCode();
    }

    // ----- Extract the values
    $v_data = unpack('vdisk/vdisk_start/vdisk_entries/ventries/Vsize/Voffset/vcomment_size', $v_binary_data);

    // ----- Check the global size
    if (($v_pos + $v_data['comment_size'] + 18) != $v_size) {

	  // ----- Removed in release 2.2 see readme file
	  // The check of the file size is a little too strict.
	  // Some bugs where found when a zip is encrypted/decrypted with 'crypt'.
	  // While decrypted, zip has training 0 bytes
	  if (0) {
      // ----- Error log
      WPvivid_PclZip::privErrorLog(WPVIVID_PCLZIP_ERR_BAD_FORMAT,
	                       'The central dir is not at the end of the archive.'
						   .' Some trailing bytes exists after the archive.');

      // ----- Return
      return WPvivid_PclZip::errorCode();
	  }
    }

    // ----- Get comment
    if ($v_data['comment_size'] != 0) {
      $p_central_dir['comment'] = fread($this->zip_fd, $v_data['comment_size']);
    }
    else
      $p_central_dir['comment'] = '';

    $p_central_dir['entries'] = $v_data['entries'];
    $p_central_dir['disk_entries'] = $v_data['disk_entries'];
    $p_central_dir['offset'] = $v_data['offset'];
    $p_central_dir['size'] = $v_data['size'];
    $p_central_dir['disk'] = $v_data['disk'];
    $p_central_dir['disk_start'] = $v_data['disk_start'];

    // TBC
    //for(reset($p_central_dir); $key = key($p_central_dir); next($p_central_dir)) {
    //}

    // ----- Return
    return $v_result;
  }
  // --------------------------------------------------------------------------------

  // --------------------------------------------------------------------------------
  // Function : privDeleteByRule()
  // Description :
  // Parameters :
  // Return Values :
  // --------------------------------------------------------------------------------
  function privDeleteByRule(&$p_result_list, &$p_options)
  {
    $v_result=1;
    $v_list_detail = array();

    // ----- Open the zip file
    if (($v_result=$this->privOpenFd('rb')) != 1)
    {
      // ----- Return
      return $v_result;
    }

    // ----- Read the central directory information
    $v_central_dir = array();
    if (($v_result = $this->privReadEndCentralDir($v_central_dir)) != 1)
    {
      $this->privCloseFd();
      return $v_result;
    }

    // ----- Go to beginning of File
    @rewind($this->zip_fd);

    // ----- Scan all the files
    // ----- Start at beginning of Central Dir
    $v_pos_entry = $v_central_dir['offset'];
    @rewind($this->zip_fd);
    if (@fseek($this->zip_fd, $v_pos_entry))
    {
      // ----- Close the zip file
      $this->privCloseFd();

      // ----- Error log
      WPvivid_PclZip::privErrorLog(WPVIVID_PCLZIP_ERR_INVALID_ARCHIVE_ZIP, 'Invalid archive size');

      // ----- Return
      return WPvivid_PclZip::errorCode();
    }

    // ----- Read each entry
    $v_header_list = array();
    $j_start = 0;
    for ($i=0, $v_nb_extracted=0; $i<$v_central_dir['entries']; $i++)
    {

      // ----- Read the file header
      $v_header_list[$v_nb_extracted] = array();
      if (($v_result = $this->privReadCentralFileHeader($v_header_list[$v_nb_extracted])) != 1)
      {
        // ----- Close the zip file
        $this->privCloseFd();

        return $v_result;
      }


      // ----- Store the index
      $v_header_list[$v_nb_extracted]['index'] = $i;

      // ----- Look for the specific extract rules
      $v_found = false;

      // ----- Look for extract by name rule
      if (   (isset($p_options[WPVIVID_PCLZIP_OPT_BY_NAME]))
          && ($p_options[WPVIVID_PCLZIP_OPT_BY_NAME] != 0)) {

          // ----- Look if the filename is in the list
          for ($j=0; ($j<sizeof($p_options[WPVIVID_PCLZIP_OPT_BY_NAME])) && (!$v_found); $j++) {

              // ----- Look for a directory
              if (substr($p_options[WPVIVID_PCLZIP_OPT_BY_NAME][$j], -1) == "/") {

                  // ----- Look if the directory is in the filename path
                  if (   (strlen($v_header_list[$v_nb_extracted]['stored_filename']) > strlen($p_options[WPVIVID_PCLZIP_OPT_BY_NAME][$j]))
                      && (substr($v_header_list[$v_nb_extracted]['stored_filename'], 0, strlen($p_options[WPVIVID_PCLZIP_OPT_BY_NAME][$j])) == $p_options[WPVIVID_PCLZIP_OPT_BY_NAME][$j])) {
                      $v_found = true;
                  }
                  elseif (   (($v_header_list[$v_nb_extracted]['external']&0x00000010)==0x00000010) /* Indicates a folder */
                          && ($v_header_list[$v_nb_extracted]['stored_filename'].'/' == $p_options[WPVIVID_PCLZIP_OPT_BY_NAME][$j])) {
                      $v_found = true;
                  }
              }
              // ----- Look for a filename
              elseif ($v_header_list[$v_nb_extracted]['stored_filename'] == $p_options[WPVIVID_PCLZIP_OPT_BY_NAME][$j]) {
                  $v_found = true;
              }
          }
      }

      // ----- Look for extract by ereg rule
      // ereg() is deprecated with PHP 5.3
      /*
      else if (   (isset($p_options[WPVIVID_PCLZIP_OPT_BY_EREG]))
               && ($p_options[WPVIVID_PCLZIP_OPT_BY_EREG] != "")) {

          if (ereg($p_options[WPVIVID_PCLZIP_OPT_BY_EREG], $v_header_list[$v_nb_extracted]['stored_filename'])) {
              $v_found = true;
          }
      }
      */

      // ----- Look for extract by preg rule
      else if (   (isset($p_options[WPVIVID_PCLZIP_OPT_BY_PREG]))
               && ($p_options[WPVIVID_PCLZIP_OPT_BY_PREG] != "")) {

          if (preg_match($p_options[WPVIVID_PCLZIP_OPT_BY_PREG], $v_header_list[$v_nb_extracted]['stored_filename'])) {
              $v_found = true;
          }
      }

      // ----- Look for extract by index rule
      else if (   (isset($p_options[WPVIVID_PCLZIP_OPT_BY_INDEX]))
               && ($p_options[WPVIVID_PCLZIP_OPT_BY_INDEX] != 0)) {

          // ----- Look if the index is in the list
          for ($j=$j_start; ($j<sizeof($p_options[WPVIVID_PCLZIP_OPT_BY_INDEX])) && (!$v_found); $j++) {

              if (($i>=$p_options[WPVIVID_PCLZIP_OPT_BY_INDEX][$j]['start']) && ($i<=$p_options[WPVIVID_PCLZIP_OPT_BY_INDEX][$j]['end'])) {
                  $v_found = true;
              }
              if ($i>=$p_options[WPVIVID_PCLZIP_OPT_BY_INDEX][$j]['end']) {
                  $j_start = $j+1;
              }

              if ($p_options[WPVIVID_PCLZIP_OPT_BY_INDEX][$j]['start']>$i) {
                  break;
              }
          }
      }
      else {
      	$v_found = true;
      }

      // ----- Look for deletion
      if ($v_found)
      {
        unset($v_header_list[$v_nb_extracted]);
      }
      else
      {
        $v_nb_extracted++;
      }
    }

    // ----- Look if something need to be deleted
    if ($v_nb_extracted > 0) {

        // ----- Creates a temporary file
        $v_zip_temp_name = PCLZIP_TEMPORARY_DIR.uniqid('pclzip-').'.tmp';

        // ----- Creates a temporary zip archive
        $v_temp_zip = new WPvivid_PclZip($v_zip_temp_name);

        // ----- Open the temporary zip file in write mode
        if (($v_result = $v_temp_zip->privOpenFd('wb')) != 1) {
            $this->privCloseFd();

            // ----- Return
            return $v_result;
        }

        // ----- Look which file need to be kept
        for ($i=0; $i<sizeof($v_header_list); $i++) {

            // ----- Calculate the position of the header
            @rewind($this->zip_fd);
            if (@fseek($this->zip_fd,  $v_header_list[$i]['offset'])) {
                // ----- Close the zip file
                $this->privCloseFd();
                $v_temp_zip->privCloseFd();
                @wp_delete_file($v_zip_temp_name);

                // ----- Error log
                WPvivid_PclZip::privErrorLog(WPVIVID_PCLZIP_ERR_INVALID_ARCHIVE_ZIP, 'Invalid archive size');

                // ----- Return
                return WPvivid_PclZip::errorCode();
            }

            // ----- Read the file header
            $v_local_header = array();
            if (($v_result = $this->privReadFileHeader($v_local_header)) != 1) {
                // ----- Close the zip file
                $this->privCloseFd();
                $v_temp_zip->privCloseFd();
                @wp_delete_file($v_zip_temp_name);

                // ----- Return
                return $v_result;
            }

            // ----- Check that local file header is same as central file header
            if ($this->privCheckFileHeaders($v_local_header,
			                                $v_header_list[$i]) != 1) {
                // TBC
            }
            unset($v_local_header);

            // ----- Write the file header
            if (($v_result = $v_temp_zip->privWriteFileHeader($v_header_list[$i])) != 1) {
                // ----- Close the zip file
                $this->privCloseFd();
                $v_temp_zip->privCloseFd();
                @wp_delete_file($v_zip_temp_name);

                // ----- Return
                return $v_result;
            }

            // ----- Read/write the data block
            if (($v_result = WPvivid_PclZipUtilCopyBlock($this->zip_fd, $v_temp_zip->zip_fd, $v_header_list[$i]['compressed_size'])) != 1) {
                // ----- Close the zip file
                $this->privCloseFd();
                $v_temp_zip->privCloseFd();
                @wp_delete_file($v_zip_temp_name);

                // ----- Return
                return $v_result;
            }
        }

        // ----- Store the offset of the central dir
        $v_offset = @ftell($v_temp_zip->zip_fd);

        // ----- Re-Create the Central Dir files header
        for ($i=0; $i<sizeof($v_header_list); $i++) {
            // ----- Create the file header
            if (($v_result = $v_temp_zip->privWriteCentralFileHeader($v_header_list[$i])) != 1) {
                $v_temp_zip->privCloseFd();
                $this->privCloseFd();
                @wp_delete_file($v_zip_temp_name);

                // ----- Return
                return $v_result;
            }

            // ----- Transform the header to a 'usable' info
            $v_temp_zip->privConvertHeader2FileInfo($v_header_list[$i], $p_result_list[$i]);
        }


        // ----- Zip file comment
        $v_comment = '';
        if (isset($p_options[WPVIVID_PCLZIP_OPT_COMMENT])) {
          $v_comment = $p_options[WPVIVID_PCLZIP_OPT_COMMENT];
        }

        // ----- Calculate the size of the central header
        $v_size = @ftell($v_temp_zip->zip_fd)-$v_offset;

        // ----- Create the central dir footer
        if (($v_result = $v_temp_zip->privWriteCentralHeader(sizeof($v_header_list), $v_size, $v_offset, $v_comment)) != 1) {
            // ----- Reset the file list
            unset($v_header_list);
            $v_temp_zip->privCloseFd();
            $this->privCloseFd();
            @wp_delete_file($v_zip_temp_name);

            // ----- Return
            return $v_result;
        }

        // ----- Close
        $v_temp_zip->privCloseFd();
        $this->privCloseFd();

        // ----- Delete the zip file
        // TBC : I should test the result ...
        @wp_delete_file($this->zipname);

        // ----- Rename the temporary file
        // TBC : I should test the result ...
        //@rename($v_zip_temp_name, $this->zipname);
        WPvivid_PclZipUtilRename($v_zip_temp_name, $this->zipname);

        // ----- Destroy the temporary archive
        unset($v_temp_zip);
    }

    // ----- Remove every files : reset the file
    else if ($v_central_dir['entries'] != 0) {
        $this->privCloseFd();

        if (($v_result = $this->privOpenFd('wb')) != 1) {
          return $v_result;
        }

        if (($v_result = $this->privWriteCentralHeader(0, 0, 0, '')) != 1) {
          return $v_result;
        }

        $this->privCloseFd();
    }

    // ----- Return
    return $v_result;
  }
  // --------------------------------------------------------------------------------

  // --------------------------------------------------------------------------------
  // Function : privDirCheck()
  // Description :
  //   Check if a directory exists, if not it creates it and all the parents directory
  //   which may be useful.
  // Parameters :
  //   $p_dir : Directory path to check.
  // Return Values :
  //    1 : OK
  //   -1 : Unable to create directory
  // --------------------------------------------------------------------------------
  function privDirCheck($p_dir, $p_is_dir=false)
  {
    $v_result = 1;


    // ----- Remove the final '/'
    if (($p_is_dir) && (substr($p_dir, -1)=='/'))
    {
      $p_dir = substr($p_dir, 0, strlen($p_dir)-1);
    }

    // ----- Check the directory availability
    if ((is_dir($p_dir)) || ($p_dir == ""))
    {
      return 1;
    }

    // ----- Extract parent directory
    $p_parent_dir = dirname($p_dir);

    // ----- Just a check
    if ($p_parent_dir != $p_dir)
    {
      // ----- Look for parent directory
      if ($p_parent_dir != "")
      {
        if (($v_result = $this->privDirCheck($p_parent_dir)) != 1)
        {
          return $v_result;
        }
      }
    }

    // ----- Create the directory
    if (!@mkdir($p_dir, 0777))
    {
      // ----- Error log
      WPvivid_PclZip::privErrorLog(WPVIVID_PCLZIP_ERR_DIR_CREATE_FAIL, "Unable to create directory '$p_dir'");

      // ----- Return
      return WPvivid_PclZip::errorCode();
    }

    // ----- Return
    return $v_result;
  }
  // --------------------------------------------------------------------------------

  // --------------------------------------------------------------------------------
  // Function : privMerge()
  // Description :
  //   If $p_archive_to_add does not exist, the function exit with a success result.
  // Parameters :
  // Return Values :
  // --------------------------------------------------------------------------------
  function privMerge(&$p_archive_to_add)
  {
    $v_result=1;

    // ----- Look if the archive_to_add exists
    if (!is_file($p_archive_to_add->zipname))
    {

      // ----- Nothing to merge, so merge is a success
      $v_result = 1;

      // ----- Return
      return $v_result;
    }

    // ----- Look if the archive exists
    if (!is_file($this->zipname))
    {

      // ----- Do a duplicate
      $v_result = $this->privDuplicate($p_archive_to_add->zipname);

      // ----- Return
      return $v_result;
    }

    // ----- Open the zip file
    if (($v_result=$this->privOpenFd('rb')) != 1)
    {
      // ----- Return
      return $v_result;
    }

    // ----- Read the central directory information
    $v_central_dir = array();
    if (($v_result = $this->privReadEndCentralDir($v_central_dir)) != 1)
    {
      $this->privCloseFd();
      return $v_result;
    }

    // ----- Go to beginning of File
    @rewind($this->zip_fd);

    // ----- Open the archive_to_add file
    if (($v_result=$p_archive_to_add->privOpenFd('rb')) != 1)
    {
      $this->privCloseFd();

      // ----- Return
      return $v_result;
    }

    // ----- Read the central directory information
    $v_central_dir_to_add = array();
    if (($v_result = $p_archive_to_add->privReadEndCentralDir($v_central_dir_to_add)) != 1)
    {
      $this->privCloseFd();
      $p_archive_to_add->privCloseFd();

      return $v_result;
    }

    // ----- Go to beginning of File
    @rewind($p_archive_to_add->zip_fd);

    // ----- Creates a temporary file
    $v_zip_temp_name = PCLZIP_TEMPORARY_DIR.uniqid('pclzip-').'.tmp';

    // ----- Open the temporary file in write mode
    if (($v_zip_temp_fd = @fopen($v_zip_temp_name, 'wb')) == 0)
    {
      $this->privCloseFd();
      $p_archive_to_add->privCloseFd();

      WPvivid_PclZip::privErrorLog(WPVIVID_PCLZIP_ERR_READ_OPEN_FAIL, 'Unable to open temporary file \''.$v_zip_temp_name.'\' in binary write mode');

      // ----- Return
      return WPvivid_PclZip::errorCode();
    }

    // ----- Copy the files from the archive to the temporary file
    // TBC : Here I should better append the file and go back to erase the central dir
    $v_size = $v_central_dir['offset'];
    while ($v_size != 0)
    {
      $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE);
      $v_buffer = fread($this->zip_fd, $v_read_size);
      @fwrite($v_zip_temp_fd, $v_buffer, $v_read_size);
      $v_size -= $v_read_size;
    }

    // ----- Copy the files from the archive_to_add into the temporary file
    $v_size = $v_central_dir_to_add['offset'];
    while ($v_size != 0)
    {
      $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE);
      $v_buffer = fread($p_archive_to_add->zip_fd, $v_read_size);
      @fwrite($v_zip_temp_fd, $v_buffer, $v_read_size);
      $v_size -= $v_read_size;
    }

    // ----- Store the offset of the central dir
    $v_offset = @ftell($v_zip_temp_fd);

    // ----- Copy the block of file headers from the old archive
    $v_size = $v_central_dir['size'];
    while ($v_size != 0)
    {
      $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE);
      $v_buffer = @fread($this->zip_fd, $v_read_size);
      @fwrite($v_zip_temp_fd, $v_buffer, $v_read_size);
      $v_size -= $v_read_size;
    }

    // ----- Copy the block of file headers from the archive_to_add
    $v_size = $v_central_dir_to_add['size'];
    while ($v_size != 0)
    {
      $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE);
      $v_buffer = @fread($p_archive_to_add->zip_fd, $v_read_size);
      @fwrite($v_zip_temp_fd, $v_buffer, $v_read_size);
      $v_size -= $v_read_size;
    }

    // ----- Merge the file comments
    $v_comment = $v_central_dir['comment'].' '.$v_central_dir_to_add['comment'];

    // ----- Calculate the size of the (new) central header
    $v_size = @ftell($v_zip_temp_fd)-$v_offset;

    // ----- Swap the file descriptor
    // Here is a trick : I swap the temporary fd with the zip fd, in order to use
    // the following methods on the temporary fil and not the real archive fd
    $v_swap = $this->zip_fd;
    $this->zip_fd = $v_zip_temp_fd;
    $v_zip_temp_fd = $v_swap;

    // ----- Create the central dir footer
    if (($v_result = $this->privWriteCentralHeader($v_central_dir['entries']+$v_central_dir_to_add['entries'], $v_size, $v_offset, $v_comment)) != 1)
    {
      $this->privCloseFd();
      $p_archive_to_add->privCloseFd();
      @fclose($v_zip_temp_fd);
      $this->zip_fd = null;

      // ----- Reset the file list
      unset($v_header_list);

      // ----- Return
      return $v_result;
    }

    // ----- Swap back the file descriptor
    $v_swap = $this->zip_fd;
    $this->zip_fd = $v_zip_temp_fd;
    $v_zip_temp_fd = $v_swap;

    // ----- Close
    $this->privCloseFd();
    $p_archive_to_add->privCloseFd();

    // ----- Close the temporary file
    @fclose($v_zip_temp_fd);

    // ----- Delete the zip file
    // TBC : I should test the result ...
    @wp_delete_file($this->zipname);

    // ----- Rename the temporary file
    // TBC : I should test the result ...
    //@rename($v_zip_temp_name, $this->zipname);
    WPvivid_PclZipUtilRename($v_zip_temp_name, $this->zipname);

    // ----- Return
    return $v_result;
  }
  // --------------------------------------------------------------------------------

  // --------------------------------------------------------------------------------
  // Function : privDuplicate()
  // Description :
  // Parameters :
  // Return Values :
  // --------------------------------------------------------------------------------
  function privDuplicate($p_archive_filename)
  {
    $v_result=1;

    // ----- Look if the $p_archive_filename exists
    if (!is_file($p_archive_filename))
    {

      // ----- Nothing to duplicate, so duplicate is a success.
      $v_result = 1;

      // ----- Return
      return $v_result;
    }

    // ----- Open the zip file
    if (($v_result=$this->privOpenFd('wb')) != 1)
    {
      // ----- Return
      return $v_result;
    }

    // ----- Open the temporary file in write mode
    if (($v_zip_temp_fd = @fopen($p_archive_filename, 'rb')) == 0)
    {
      $this->privCloseFd();

      WPvivid_PclZip::privErrorLog(WPVIVID_PCLZIP_ERR_READ_OPEN_FAIL, 'Unable to open archive file \''.$p_archive_filename.'\' in binary write mode');

      // ----- ReturnR
      return WPvivid_PclZip::errorCode();
    }

    // ----- Copy the files from the archive to the temporary file
    // TBC : Here I should better append the file and go back to erase the central dir
    $v_size = filesize($p_archive_filename);
    while ($v_size != 0)
    {
      $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE);
      $v_buffer = fread($v_zip_temp_fd, $v_read_size);
      @fwrite($this->zip_fd, $v_buffer, $v_read_size);
      $v_size -= $v_read_size;
    }

    // ----- Close
    $this->privCloseFd();

    // ----- Close the temporary file
    @fclose($v_zip_temp_fd);

    // ----- Return
    return $v_result;
  }
  // --------------------------------------------------------------------------------

  // --------------------------------------------------------------------------------
  // Function : privErrorLog()
  // Description :
  // Parameters :
  // --------------------------------------------------------------------------------
  function privErrorLog($p_error_code=0, $p_error_string='')
  {
    if (PCLZIP_ERROR_EXTERNAL == 1) {
      PclError($p_error_code, $p_error_string);
    }
    else {
      $this->error_code = $p_error_code;
      $this->error_string = $p_error_string;
    }
  }
  // --------------------------------------------------------------------------------

  // --------------------------------------------------------------------------------
  // Function : privErrorReset()
  // Description :
  // Parameters :
  // --------------------------------------------------------------------------------
  function privErrorReset()
  {
    if (PCLZIP_ERROR_EXTERNAL == 1) {
      PclErrorReset();
    }
    else {
      $this->error_code = 0;
      $this->error_string = '';
    }
  }
  // --------------------------------------------------------------------------------

  // --------------------------------------------------------------------------------
  // Function : privDisableMagicQuotes()
  // Description :
  // Parameters :
  // Return Values :
  // --------------------------------------------------------------------------------
  function privDisableMagicQuotes()
  {
    $v_result=1;

	// EDIT for WordPress 5.3.0
	// magic_quote functions are deprecated in PHP 7.4, now assuming it's always off.
	/*

    // ----- Look if function exists
    if (   (!function_exists("get_magic_quotes_runtime"))
	    || (!function_exists("set_magic_quotes_runtime"))) {
      return $v_result;
	}

    // ----- Look if already done
    if ($this->magic_quotes_status != -1) {
      return $v_result;
	}

	// ----- Get and memorize the magic_quote value
	$this->magic_quotes_status = @get_magic_quotes_runtime();

	// ----- Disable magic_quotes
	if ($this->magic_quotes_status == 1) {
	  @set_magic_quotes_runtime(0);
	}
	*/

    // ----- Return
    return $v_result;
  }
  // --------------------------------------------------------------------------------

  // --------------------------------------------------------------------------------
  // Function : privSwapBackMagicQuotes()
  // Description :
  // Parameters :
  // Return Values :
  // --------------------------------------------------------------------------------
  function privSwapBackMagicQuotes()
  {
    $v_result=1;

	// EDIT for WordPress 5.3.0
	// magic_quote functions are deprecated in PHP 7.4, now assuming it's always off.
	/*

    // ----- Look if function exists
    if (   (!function_exists("get_magic_quotes_runtime"))
	    || (!function_exists("set_magic_quotes_runtime"))) {
      return $v_result;
	}

    // ----- Look if something to do
    if ($this->magic_quotes_status != -1) {
      return $v_result;
	}

	// ----- Swap back magic_quotes
	if ($this->magic_quotes_status == 1) {
  	  @set_magic_quotes_runtime($this->magic_quotes_status);
	}

	*/
    // ----- Return
    return $v_result;
  }
  // --------------------------------------------------------------------------------

  }
  // End of class
  // --------------------------------------------------------------------------------

  // --------------------------------------------------------------------------------
  // Function : WPvivid_PclZipUtilPathReduction()
  // Description :
  // Parameters :
  // Return Values :
  // --------------------------------------------------------------------------------
  function WPvivid_PclZipUtilPathReduction($p_dir)
  {
    $v_result = "";

    // ----- Look for not empty path
    if ($p_dir != "") {
      // ----- Explode path by directory names
      $v_list = explode("/", $p_dir);

      // ----- Study directories from last to first
      $v_skip = 0;
      for ($i=sizeof($v_list)-1; $i>=0; $i--) {
        // ----- Look for current path
        if ($v_list[$i] == ".") {
          // ----- Ignore this directory
          // Should be the first $i=0, but no check is done
        }
        else if ($v_list[$i] == "..") {
		  $v_skip++;
        }
        else if ($v_list[$i] == "") {
		  // ----- First '/' i.e. root slash
		  if ($i == 0) {
            $v_result = "/".$v_result;
		    if ($v_skip > 0) {
		        // ----- It is an invalid path, so the path is not modified
		        // TBC
		        $v_result = $p_dir;
                $v_skip = 0;
		    }
		  }
		  // ----- Last '/' i.e. indicates a directory
		  else if ($i == (sizeof($v_list)-1)) {
            $v_result = $v_list[$i];
		  }
		  // ----- Double '/' inside the path
		  else {
            // ----- Ignore only the double '//' in path,
            // but not the first and last '/'
		  }
        }
        else {
		  // ----- Look for item to skip
		  if ($v_skip > 0) {
		    $v_skip--;
		  }
		  else {
            $v_result = $v_list[$i].($i!=(sizeof($v_list)-1)?"/".$v_result:"");
		  }
        }
      }

      // ----- Look for skip
      if ($v_skip > 0) {
        while ($v_skip > 0) {
            $v_result = '../'.$v_result;
            $v_skip--;
        }
      }
    }

    // ----- Return
    return $v_result;
  }
  // --------------------------------------------------------------------------------

  // --------------------------------------------------------------------------------
  // Function : WPvivid_PclZipUtilPathInclusion()
  // Description :
  //   This function indicates if the path $p_path is under the $p_dir tree. Or,
  //   said in an other way, if the file or sub-dir $p_path is inside the dir
  //   $p_dir.
  //   The function indicates also if the path is exactly the same as the dir.
  //   This function supports path with duplicated '/' like '//', but does not
  //   support '.' or '..' statements.
  // Parameters :
  // Return Values :
  //   0 if $p_path is not inside directory $p_dir
  //   1 if $p_path is inside directory $p_dir
  //   2 if $p_path is exactly the same as $p_dir
  // --------------------------------------------------------------------------------
  function WPvivid_PclZipUtilPathInclusion($p_dir, $p_path)
  {
    $v_result = 1;

    // ----- Look for path beginning by ./
    if (   ($p_dir == '.')
        || ((strlen($p_dir) >=2) && (substr($p_dir, 0, 2) == './'))) {
      $p_dir = WPvivid_PclZipUtilTranslateWinPath(getcwd(), FALSE).'/'.substr($p_dir, 1);
    }
    if (   ($p_path == '.')
        || ((strlen($p_path) >=2) && (substr($p_path, 0, 2) == './'))) {
      $p_path = WPvivid_PclZipUtilTranslateWinPath(getcwd(), FALSE).'/'.substr($p_path, 1);
    }

    // ----- Explode dir and path by directory separator
    $v_list_dir = explode("/", $p_dir);
    $v_list_dir_size = sizeof($v_list_dir);
    $v_list_path = explode("/", $p_path);
    $v_list_path_size = sizeof($v_list_path);

    // ----- Study directories paths
    $i = 0;
    $j = 0;
    while (($i < $v_list_dir_size) && ($j < $v_list_path_size) && ($v_result)) {

      // ----- Look for empty dir (path reduction)
      if ($v_list_dir[$i] == '') {
        $i++;
        continue;
      }
      if ($v_list_path[$j] == '') {
        $j++;
        continue;
      }

      // ----- Compare the items
      if (($v_list_dir[$i] != $v_list_path[$j]) && ($v_list_dir[$i] != '') && ( $v_list_path[$j] != ''))  {
        $v_result = 0;
      }

      // ----- Next items
      $i++;
      $j++;
    }

    // ----- Look if everything seems to be the same
    if ($v_result) {
      // ----- Skip all the empty items
      while (($j < $v_list_path_size) && ($v_list_path[$j] == '')) $j++;
      while (($i < $v_list_dir_size) && ($v_list_dir[$i] == '')) $i++;

      if (($i >= $v_list_dir_size) && ($j >= $v_list_path_size)) {
        // ----- There are exactly the same
        $v_result = 2;
      }
      else if ($i < $v_list_dir_size) {
        // ----- The path is shorter than the dir
        $v_result = 0;
      }
    }

    // ----- Return
    return $v_result;
  }
  // --------------------------------------------------------------------------------

  // --------------------------------------------------------------------------------
  // Function : WPvivid_PclZipUtilCopyBlock()
  // Description :
  // Parameters :
  //   $p_mode : read/write compression mode
  //             0 : src & dest normal
  //             1 : src gzip, dest normal
  //             2 : src normal, dest gzip
  //             3 : src & dest gzip
  // Return Values :
  // --------------------------------------------------------------------------------
  function WPvivid_PclZipUtilCopyBlock($p_src, $p_dest, $p_size, $p_mode=0)
  {
    $v_result = 1;

    if ($p_mode==0)
    {
      while ($p_size != 0)
      {
        $v_read_size = ($p_size < PCLZIP_READ_BLOCK_SIZE ? $p_size : PCLZIP_READ_BLOCK_SIZE);
        $v_buffer = @fread($p_src, $v_read_size);
        @fwrite($p_dest, $v_buffer, $v_read_size);
        $p_size -= $v_read_size;
      }
    }
    else if ($p_mode==1)
    {
      while ($p_size != 0)
      {
        $v_read_size = ($p_size < PCLZIP_READ_BLOCK_SIZE ? $p_size : PCLZIP_READ_BLOCK_SIZE);
        $v_buffer = @gzread($p_src, $v_read_size);
        @fwrite($p_dest, $v_buffer, $v_read_size);
        $p_size -= $v_read_size;
      }
    }
    else if ($p_mode==2)
    {
      while ($p_size != 0)
      {
        $v_read_size = ($p_size < PCLZIP_READ_BLOCK_SIZE ? $p_size : PCLZIP_READ_BLOCK_SIZE);
        $v_buffer = @fread($p_src, $v_read_size);
        @gzwrite($p_dest, $v_buffer, $v_read_size);
        $p_size -= $v_read_size;
      }
    }
    else if ($p_mode==3)
    {
      while ($p_size != 0)
      {
        $v_read_size = ($p_size < PCLZIP_READ_BLOCK_SIZE ? $p_size : PCLZIP_READ_BLOCK_SIZE);
        $v_buffer = @gzread($p_src, $v_read_size);
        @gzwrite($p_dest, $v_buffer, $v_read_size);
        $p_size -= $v_read_size;
      }
    }

    // ----- Return
    return $v_result;
  }
  // --------------------------------------------------------------------------------

  // --------------------------------------------------------------------------------
  // Function : WPvivid_PclZipUtilRename()
  // Description :
  //   This function tries to do a simple rename() function. If it fails, it
  //   tries to copy the $p_src file in a new $p_dest file and then unlink the
  //   first one.
  // Parameters :
  //   $p_src : Old filename
  //   $p_dest : New filename
  // Return Values :
  //   1 on success, 0 on failure.
  // --------------------------------------------------------------------------------
  function WPvivid_PclZipUtilRename($p_src, $p_dest)
  {
    $v_result = 1;

    // ----- Try to rename the files
    if (!@rename($p_src, $p_dest)) {

      // ----- Try to copy & unlink the src
      if (!@copy($p_src, $p_dest)) {
        $v_result = 0;
      }
      else if (!@wp_delete_file($p_src)) {
        $v_result = 0;
      }
    }

    // ----- Return
    return $v_result;
  }
  // --------------------------------------------------------------------------------

  // --------------------------------------------------------------------------------
  // Function : WPvivid_PclZipUtilOptionText()
  // Description :
  //   Translate option value in text. Mainly for debug purpose.
  // Parameters :
  //   $p_option : the option value.
  // Return Values :
  //   The option text value.
  // --------------------------------------------------------------------------------
  function WPvivid_PclZipUtilOptionText($p_option)
  {

    $v_list = get_defined_constants();
    for (reset($v_list); $v_key = key($v_list); next($v_list)) {
	    $v_prefix = substr($v_key, 0, 10);
	    if ((   ($v_prefix == 'PCLZIP_OPT')
           || ($v_prefix == 'PCLZIP_CB_')
           || ($v_prefix == 'PCLZIP_ATT'))
	        && ($v_list[$v_key] == $p_option)) {
        return $v_key;
	    }
    }

    $v_result = 'Unknown';

    return $v_result;
  }
  // --------------------------------------------------------------------------------

  // --------------------------------------------------------------------------------
  // Function : WPvivid_PclZipUtilTranslateWinPath()
  // Description :
  //   Translate windows path by replacing '\' by '/' and optionally removing
  //   drive letter.
  // Parameters :
  //   $p_path : path to translate.
  //   $p_remove_disk_letter : true | false
  // Return Values :
  //   The path translated.
  // --------------------------------------------------------------------------------
  function WPvivid_PclZipUtilTranslateWinPath($p_path, $p_remove_disk_letter=true)
  {
      if (function_exists('php_uname') && strpos(ini_get('disable_functions'), 'php_uname') === false)
      {
          if (stristr(php_uname(), 'windows')) {
              // ----- Look for potential disk letter
              if (($p_remove_disk_letter) && (($v_position = strpos($p_path, ':')) != false)) {
                  $p_path = substr($p_path, $v_position+1);
              }
              // ----- Change potential windows directory separator
              if ((strpos($p_path, '\\') > 0) || (substr($p_path, 0,1) == '\\')) {
                  $p_path = strtr($p_path, '\\', '/');
              }
          }
      }
      else
      {
          if(DIRECTORY_SEPARATOR === '\\' && PHP_SHLIB_SUFFIX === 'dll' && PATH_SEPARATOR === ';')
          {
              // ----- Look for potential disk letter
              if (($p_remove_disk_letter) && (($v_position = strpos($p_path, ':')) != false)) {
                  $p_path = substr($p_path, $v_position+1);
              }
              // ----- Change potential windows directory separator
              if ((strpos($p_path, '\\') > 0) || (substr($p_path, 0,1) == '\\')) {
                  $p_path = strtr($p_path, '\\', '/');
              }
          }
      }
      return $p_path;
  }
  // --------------------------------------------------------------------------------


?>
includes/class-wpvivid-downloader.php000064400000015036151327705670014034 0ustar00<?php

if (!defined('WPVIVID_PLUGIN_DIR')){
    die;
}

class WPvivid_downloader
{
    private $task;

    public function ready_download($download_info)
    {
        $backup=WPvivid_Backuplist::get_backup_by_id($download_info['backup_id']);
        if(!$backup)
        {
            return false;
        }

        $file_info=false;

        if(isset($backup['backup']['files']))
        {
            foreach ($backup['backup']['files'] as $file)
            {
                if ($file['file_name'] == $download_info['file_name'])
                {
                    $file_info= $file;
                    break;
                }
            }
        }
        else if ($backup['backup']['ismerge'] == 1)
        {
            $backup_files = $backup['backup']['data']['meta']['files'];
            foreach ($backup_files as $file)
            {
                if ($file['file_name'] == $download_info['file_name'])
                {
                    $file_info = $file;
                    break;
                }
            }
        } else {
            foreach ($backup['backup']['data']['type'] as $type)
            {
                $backup_files = $type['files'];
                foreach ($backup_files as $file) {
                    if ($file['file_name'] == $download_info['file_name'])
                    {
                        $file_info = $file;
                        break;
                    }
                }
            }
        }

        if($file_info==false)
        {
            return false;
        }

        $backup_dir = WPvivid_Setting::get_backupdir();
        $local_path=WP_CONTENT_DIR.DIRECTORY_SEPARATOR.$backup_dir.DIRECTORY_SEPARATOR;
        //$local_path=WP_CONTENT_DIR.DIRECTORY_SEPARATOR.$backup['local']['path'].DIRECTORY_SEPARATOR;
        $need_download_files=array();

        $local_file=$local_path.$file_info['file_name'];
        if(file_exists($local_file))
        {
            if(filesize($local_file)!=$file_info['size'])
            {
                if(filesize($local_file)>$file_info['size'])
                {
                    @wp_delete_file($local_file);
                }
                $need_download_files[$file_info['file_name']]=$file_info;
            }
        }
        else {
            $need_download_files[$file_info['file_name']]=$file_info;
        }


        if(empty($need_download_files))
        {
            delete_option('wpvivid_download_cache');
        }
        else
        {
            if(WPvivid_taskmanager::is_download_task_running_v2($download_info['file_name']))
            {
                global $wpvivid_plugin;
                $wpvivid_plugin->wpvivid_log->WriteLog('has a downloading task,exit download.','test');
                return false;
            }
            else
            {
                WPvivid_taskmanager::delete_download_task_v2($download_info['file_name']);
                $task=WPvivid_taskmanager::new_download_task_v2($download_info['file_name']);
            }
        }

        foreach ($need_download_files as $file)
        {
            $ret=$this->download_ex($task,$backup['remote'],$file,$local_path);
            if($ret['result']==WPVIVID_FAILED)
            {
                return false;
            }
        }

        return true;
    }

    public function download_ex(&$task,$remotes,$file,$local_path)
    {
        $this->task=$task;

        $remote_option=array_shift($remotes);

        if(is_null($remote_option))
        {
            return array('result' => WPVIVID_FAILED ,'error'=>'Retrieving the cloud storage information failed while downloading backups. Please try again later.');
        }

        global $wpvivid_plugin;

        if(!class_exists('WPvivid_Remote_collection'))
        {
            include_once WPVIVID_PLUGIN_DIR . '/includes/class-wpvivid-remote-collection.php';
            $wpvivid_plugin->remote_collection=new WPvivid_Remote_collection();
        }
        $remote=$wpvivid_plugin->remote_collection->get_remote($remote_option);

        $ret=$remote->download($file,$local_path,array($this,'download_callback_v2'));

        if($ret['result']==WPVIVID_SUCCESS)
        {
            $progress=100;
            $wpvivid_plugin->wpvivid_download_log->WriteLog('Download completed.', 'notice');
            WPvivid_taskmanager::update_download_task_v2( $task,$progress,'completed');
            return $ret;
        }
        else
        {
            $progress=0;
            $message=$ret['error'];
            if($wpvivid_plugin->wpvivid_download_log)
            {
                $wpvivid_plugin->wpvivid_download_log->WriteLog('Download failed, ' . $message ,'error');
                $wpvivid_plugin->wpvivid_download_log->CloseFile();
                WPvivid_error_log::create_error_log($wpvivid_plugin->wpvivid_download_log->log_file);
            }
            else {
                $id = uniqid('wpvivid-');
                $log_file_name = $id . '_download';
                $log = new WPvivid_Log();
                $log->CreateLogFile($log_file_name, 'no_folder', 'download');
                $log->WriteLog($message, 'notice');
                $log->CloseFile();
                WPvivid_error_log::create_error_log($log->log_file);
            }
            WPvivid_taskmanager::update_download_task_v2($task,$progress,'error',$message);
            return $ret;
        }
    }

    public function download_callback_v2($offset,$current_name,$current_size,$last_time,$last_size)
    {
        global $wpvivid_plugin;
        $progress= floor(($offset/$current_size)* 100) ;
        $text='Total size:'.size_format($current_size,2).' downloaded:'.size_format($offset,2);
        $this->task['download_descript']=$text;
        $wpvivid_plugin->wpvivid_download_log->WriteLog('Total Size: '.$current_size.', Downloaded Size: '.$offset ,'notice');
        WPvivid_taskmanager::update_download_task_v2( $this->task,$progress,'running');
    }

    public static function delete($remote , $files)
    {
        global $wpvivid_plugin;

        @set_time_limit(60);

        if(!class_exists('WPvivid_Remote_collection'))
        {
            include_once WPVIVID_PLUGIN_DIR . '/includes/class-wpvivid-remote-collection.php';
            $wpvivid_plugin->remote_collection=new WPvivid_Remote_collection();
        }
        $remote=$wpvivid_plugin->remote_collection->get_remote($remote);

        $result =$remote->cleanup($files);

        return $result;
    }
}includes/class-wpvivid-export-import.php000064400000367017151327705670014540 0ustar00<?php

define('WPVIVID_IMPORT_EXPORT_DIR', 'ImportandExport');

class WPvivid_Export_Import
{
    public $main_tab;
    public $sub_tab;

    public $end_shutdown_function;

    public function __construct()
    {
        add_action('wp_ajax_wpvivid_export_post_step2', array($this, 'export_post_step2'));
        add_action('wp_ajax_wpvivid_export_post_step3', array($this, 'export_post_step3'));

        add_filter('wpvivid_get_toolbar_menus',array($this,'get_toolbar_menus'),21);
        add_filter('wpvivid_get_admin_menus',array($this,'get_admin_menus'),21);

        add_action('wp_ajax_wpvivid_export_now', array($this, 'export_now'));
        add_action('wp_ajax_wpvivid_prepare_export_post', array($this, 'prepare_export_post'));
        add_action('wp_ajax_wpvivid_export_list_tasks', array($this, 'list_tasks'));

        add_action('wp_ajax_wpvivid_get_post_list', array($this, 'get_list'));
        add_action('wp_ajax_wpvivid_get_post_list_page', array($this, 'get_list_page'));
        add_action('wp_ajax_wpvivid_get_import_list_page', array($this, 'get_import_list_page'));

        add_action('wp_ajax_wpvivid_get_export_list',array($this, 'get_export_list'));

        add_action('admin_head', array($this, 'my_admin_custom_styles'));

        add_action('wpvivid_handle_export_success',array($this,'handle_export_success'),10);
        add_action('wpvivid_handle_export_failed',array($this,'handle_export_failed'),10, 2);

        add_action('wp_ajax_wpvivid_delete_export_list',array($this,'delete_export_list'),10);

        add_filter('wpvivid_get_screen_ids',array($this,'get_screen_ids'),12);

        add_action('wp_ajax_wpvivid_start_import', array($this, 'start_import'));

        add_action('wp_ajax_wpvivid_download_export_backup', array($this, 'wpvivid_download_export_backup'));
        add_action('wp_ajax_wpvivid_check_import_file', array($this, 'check_import_file'));
        add_action('wp_ajax_wpvivid_upload_import_files',array($this,'upload_import_files'));
        add_action('wp_ajax_wpvivid_upload_import_file_complete', array($this, 'upload_import_file_complete'));
        add_action('wp_ajax_wpvivid_get_import_progress', array($this, 'get_import_progress'));
        add_action('wp_ajax_wpvivid_scan_import_folder', array($this, 'wpvivid_scan_import_folder'));
        add_action('wp_ajax_wpvivid_calc_import_folder_size', array($this, 'calc_import_folder_size'));
        add_action('wp_ajax_wpvivid_clean_import_folder', array($this, 'clean_import_folder'));
        //
        $this->end_shutdown_function = false;
    }

    public function get_screen_ids($screen_ids)
    {
        $screen_ids[]='wpvivid-backup_page_wpvivid-export-import';
        return $screen_ids;
    }

    public function get_toolbar_menus($toolbar_menus)
    {
        $admin_url = apply_filters('wpvivid_get_admin_url', '');

        $menu['id']='wpvivid_admin_menu_export_import';
        $menu['parent']='wpvivid_admin_menu';
        $menu['title']=__('Export & Import', 'wpvivid-backuprestore');
        $menu['tab']= 'admin.php?page=wpvivid-export-import';
        $menu['href']=$admin_url . 'admin.php?page=wpvivid-export-import';
        $menu['capability']='administrator';
        $menu['index']=4;
        $toolbar_menus[$menu['parent']]['child'][$menu['id']]=$menu;
        return $toolbar_menus;
    }

    public function get_admin_menus($submenus)
    {
        $submenu['parent_slug']=WPVIVID_PLUGIN_SLUG;
        $submenu['page_title']= 'WPvivid Backup';
        $submenu['menu_title']=__('Export & Import', 'wpvivid-backuprestore');
        $submenu['capability']='administrator';
        $submenu['menu_slug']='wpvivid-export-import';
        $submenu['index']=4;
        $submenu['function']=array($this, 'init_page');
        $submenus[$submenu['menu_slug']]=$submenu;
        return $submenus;
    }

    public function init_page()
    {
        ?>
        <div class="wrap" style="max-width:1720px;">
            <h1><?php
                $plugin_display_name = 'WPvivid Backup Plugin';
                $plugin_display_name = apply_filters('wpvivid_display_pro_name', $plugin_display_name);
                esc_html_e('WPvivid Backup Plugin', 'wpvivid-backuprestore');
                ?></h1>
            <div id="wpvivid_export_notice"></div>
            <?php
            $args['is_parent_tab']=1;
            $this->main_tab=new WPvivid_Tab_Page_Container();
            $this->main_tab->add_tab('Export','export',array($this, 'output_export'), $args);
            $this->main_tab->add_tab('Import','import',array($this, 'output_import'), $args);
            $this->main_tab->display();
            ?>
        </div>
        <?php
    }

    //export
    public function output_export()
    {
        $export_dir = WP_CONTENT_DIR.DIRECTORY_SEPARATOR.WPvivid_Setting::get_backupdir().DIRECTORY_SEPARATOR.WPVIVID_IMPORT_EXPORT_DIR;
        ?>
        <div class="postbox export-import-block">
            <div>
                <div class="wpvivid-element-space-bottom wpvivid-element-space-right" style="float: left;">
                    <img src="<?php echo esc_url(WPVIVID_PLUGIN_IMAGES_URL.'export-import.png'); ?>" style="width:50px;height:50px;">
                </div>
                <div style="box-sizing: border-box;">
                    <div class="wpvivid-text-space-bottom"><?php esc_html_e('Export posts or pages with images in bulk.', 'wpvivid-backuprestore'); ?>
                        <span class="wpvivid-feature-pro">
                            <a href="https://docs.wpvivid.com/export-content.html" target="_blank" style="text-decoration: none;"><?php esc_html_e('Learn more', 'wpvivid-backuprestore'); ?></a>
                        </span>
                    </div>
                    <div class="wpvivid-text-space-bottom"><?php esc_html_e('This will contain all of your posts, pages, comments, terms and images (original images, featured images and thumbnails).', 'wpvivid-backuprestore'); ?></div>
                    <div class="wpvivid-text-space-bottom"><strong><?php esc_html_e('Note:', 'wpvivid-backuprestore'); ?></strong>&nbsp<?php esc_html_e('Try to select fewer items when you are facing a shortage of server resources (typically presented as a timeout error).', 'wpvivid-backuprestore'); ?></div>
                    <div style="clear: both;"></div>
                </div>
                <div style="clear: both;"></div>
            </div>
            <div style="clear: both;"></div>

            <div style="background: #fff; border: 1px solid #e5e5e5; border-radius: 6px; margin-bottom: 10px; padding: 10px;">
                <div>
                    <?php
                    echo esc_html(sprintf('Exported files will be temporarily stored in %s directory', $export_dir));
                    ?>
                </div>
            </div>

            <div style="width:100%; border:1px solid #f1f1f1; float:left; box-sizing: border-box;margin-bottom:10px;">
                <div style="box-sizing: border-box; margin: 1px; background-color: #f1f1f1;"><h2><?php esc_html_e('Choose post type', 'wpvivid-backuprestore'); ?></h2></div>
            </div>
            <div style="clear: both;"></div>

            <div class="postbox wpvivid-element-space-bottom">
                <div class="wpvivid-export-type-provider wpvivid-export-type-post wpvivid-export-type-provider-active" onclick="wpvivid_select_export_type('post');">
                    <?php esc_html_e('Post', 'wpvivid-backuprestore'); ?>
                </div>
                <div class="wpvivid-export-type-provider wpvivid-export-type-page" onclick="wpvivid_select_export_type('page');">
                    <?php esc_html_e('Page', 'wpvivid-backuprestore'); ?>
                </div>
                <div class="wpvivid-export-type-provider">
                    <?php esc_html_e('More post types coming soon...', 'wpvivid-backuprestore'); ?>
                </div>
            </div>

            <div id="wpvivid_export_page">
                <input class="button-primary wpvivid-button-export-archieve" type="submit" name="post" value="<?php esc_attr_e('Next Step', 'wpvivid-backuprestore'); ?>" />
            </div>
            <div class="postbox" id="wpvivid_export_task_progress" style="display: none; margin-top: 10px; margin-bottom: 0;">
                <div class="action-progress-bar" id="wpvivid_export_bar_percent">
                    <div class="action-progress-bar-percent" style="width:0; height:24px;"></div>
                </div>
                <div style="clear: both;"></div>
                <div style="margin-left:10px; float: left; width:100%;"><p id="wpvivid_export_current_doing"></p></div>
                <div style="clear: both;"></div>
            </div>
            <div class="postbox" id="wpvivid_export_summary" style="display: none; margin-top: 10px; margin-bottom: 0; padding: 10px;"></div>
        </div>

        <script>
            var export_task_id='';
            var retry_count=0;

            var current_export_type = 'post';
            function wpvivid_select_export_type(export_type){
                jQuery('.wpvivid-export-type-provider').removeClass('wpvivid-export-type-provider-active');
                jQuery('.wpvivid-export-type-'+export_type).addClass('wpvivid-export-type-provider-active');
                if(current_export_type !== export_type){
                    current_export_type = export_type;
                    var button_html = '<input class="button-primary wpvivid-button-export-archieve" type="submit" name="'+export_type+'" value="<?php esc_attr_e('Next Step', 'wpvivid-backuprestore'); ?>" />';
                    jQuery('#wpvivid_export_page').html(button_html);
                    jQuery('#wpvivid_export_summary').hide();
                }
            }

            function wpvivid_archieve_export_info(post_type, is_running){
                var ajax_data = {
                    'action':'wpvivid_export_post_step2',
                    'post_type': post_type
                };
                wpvivid_post_request(ajax_data, function(data) {
                    var jsonarray = jQuery.parseJSON(data);
                    if (jsonarray.result === 'success') {
                        jQuery('#wpvivid_export_page').html(jsonarray.html);
                        if(is_running){
                            jQuery('#wpvivid_export_custom').hide();
                        }
                    }
                    else if (jsonarray.result === 'failed') {
                        alert(jsonarray.error);
                    }
                }, function(XMLHttpRequest, textStatus, errorThrown) {
                    var error_message = wpvivid_output_ajaxerror('export the previously-exported settings', textStatus, errorThrown);
                    alert(error_message);
                });
            }

            function wpvivid_import_lock_unlock(action){
                var css_pointer_event = '';
                var css_opacity = '';
                if(action === 'lock'){
                    css_pointer_event = 'none';
                    css_opacity = '0.4';
                }
                else{
                    css_pointer_event = 'auto';
                    css_opacity = '1';
                }
                jQuery('.wpvivid-export-type-provider').css({'pointer-events': css_pointer_event, 'opacity': css_opacity});
                jQuery('#wpvivid_export_page .wpvivid-button-export-archieve').css({'pointer-events': css_pointer_event, 'opacity': css_opacity});
                jQuery('#wpvivid_export_page #wpvivid-post-query-submit').css({'pointer-events': css_pointer_event, 'opacity': css_opacity});
                jQuery('#wpvivid_export_page #wpvivid-post-research-submit').css({'pointer-events': css_pointer_event, 'opacity': css_opacity});
                jQuery('input:radio[name=contain]').css({'pointer-events': css_pointer_event, 'opacity': css_opacity});
                jQuery('#wpvivid_export_page #wpvivid_start_export').css({'pointer-events': css_pointer_event, 'opacity': css_opacity});
                jQuery('#wpvivid_tab_export').css({'pointer-events': css_pointer_event, 'opacity': css_opacity});
                jQuery('#wpvivid_tab_import').css({'pointer-events': css_pointer_event, 'opacity': css_opacity});
                jQuery('#wpvivid_empty_import_folder').css({'pointer-events': css_pointer_event, 'opacity': css_opacity});
                jQuery('#wpvivid_select_import_file_button').css({'pointer-events': css_pointer_event, 'opacity': css_opacity});
                jQuery('#wpvivid_upload_file_list').css({'pointer-events': css_pointer_event, 'opacity': css_opacity});
                jQuery('.export-list-import').css({'pointer-events': css_pointer_event, 'opacity': css_opacity});
                jQuery('#wpvivid_start_import').css({'pointer-events': css_pointer_event, 'opacity': css_opacity});
                jQuery('#wpvivid_rechoose_import_file').css({'pointer-events': css_pointer_event, 'opacity': css_opacity});
            }

            function wpvivid_export_lock_unlock(action){
                var css_pointer_event = '';
                var css_opacity = '';
                if(action === 'lock'){
                    css_pointer_event = 'none';
                    css_opacity = '0.4';
                }
                else{
                    css_pointer_event = 'auto';
                    css_opacity = '1';
                }
                jQuery('.wpvivid-export-type-provider').css({'pointer-events': css_pointer_event, 'opacity': css_opacity});
                jQuery('#wpvivid_export_page .wpvivid-button-export-archieve').css({'pointer-events': css_pointer_event, 'opacity': css_opacity});
                jQuery('#wpvivid_export_page #wpvivid-post-query-submit').css({'pointer-events': css_pointer_event, 'opacity': css_opacity});
                jQuery('#wpvivid_export_page #wpvivid-post-research-submit').css({'pointer-events': css_pointer_event, 'opacity': css_opacity});
                jQuery('input:radio[name=contain]').css({'pointer-events': css_pointer_event, 'opacity': css_opacity});
                jQuery('#wpvivid_export_page #wpvivid_start_export').css({'pointer-events': css_pointer_event, 'opacity': css_opacity});
            }

            jQuery('#wpvivid_export_page').on("click", ".wpvivid-button-export-archieve", function(){
                var post_type = jQuery(this).attr('name');
                wpvivid_archieve_export_info(post_type, false);
            });

            jQuery('#wpvivid_export_page').on("click", "#wpvivid-post-research-submit",function()
            {
                jQuery('#wpvivid_post_selector').show();
                jQuery('#wpvivid_post_list').hide();
            });
            jQuery('#wpvivid_export_page').on("click", "#wpvivid-post-query-submit",function()
            {
                var post_type = jQuery('#wpvivid-post-query-submit').attr('name');
                var cat=jQuery('select[name=cat]').val();
                var authors=jQuery('select[name=post_author]').val();
                var post_start_date=jQuery('select[name=post_start_date]').val();
                var post_end_date=jQuery('select[name=post_end_date]').val();
                var post_ids=jQuery('input[name=post-id]').val();
                var post_title=jQuery('input[name=post-title]').val();
                var ajax_data = {
                    'action':'wpvivid_get_post_list',
                    'post_type': post_type,
                    'cat':cat,
                    'authors':authors,
                    'post_start_date':post_start_date,
                    'post_end_date':post_end_date,
                    'post_ids':post_ids,
                    'post_title':post_title
                };
                wpvivid_post_request(ajax_data, function(data) {
                    var jsonarray = jQuery.parseJSON(data);
                    if (jsonarray.result === 'success') {
                        jQuery('#wpvivid_post_selector').hide();
                        jQuery('#wpvivid_bottom_step2').show();
                        jQuery('#wpvivid_post_list').show();
                        jQuery('#wpvivid_post_list').html(jsonarray.rows);
                    }
                    else if (jsonarray.result === 'failed') {
                        alert(jsonarray.error);
                    }
                }, function(XMLHttpRequest, textStatus, errorThrown) {
                    var error_message = wpvivid_output_ajaxerror('export the previously-exported settings', textStatus, errorThrown);
                    alert(error_message);
                });
            });

            jQuery('#wpvivid_export_page').on("click", 'input:radio[name=contain]',function() {
                if(jQuery(this).val()==='list') {
                    jQuery('#wpvivid_export_custom').show();
                }
                else {
                    jQuery('#wpvivid_export_custom').hide();
                }
            });

            jQuery('#wpvivid_export_page').on("keyup", '#wpvivid_set_post_comment', function(){
                var post_comment = jQuery('#wpvivid_set_post_comment').val();
                if(post_comment === ''){
                    post_comment = '*';
                    jQuery('#wpvivid_post_comment').html(post_comment);
                }
                else{
                    var reg = RegExp(/wpvivid/, 'i');
                    if (post_comment.match(reg)) {
                        jQuery('#wpvivid_set_post_comment').val('');
                        jQuery('#wpvivid_post_comment').html('*');
                        alert('<?php esc_html_e('You can not use word \'wpvivid\' to comment the post.', 'wpvivid-backuprestore'); ?>');
                    }
                    else{
                        jQuery('#wpvivid_post_comment').html(post_comment);
                    }
                }
            });

            function wpvivid_check_export_status(){
                var check_status = false;
                jQuery('input[name="post[]"]').each(function (i) {
                    var id=jQuery(this).val();
                    if(jQuery(this).prop('checked')) {
                        check_status = true;
                        return;
                    }
                });
                return check_status;
            }

            jQuery('#wpvivid_export_page').on("click", '#cb-select-all-1', function() {
                if(jQuery(this).prop('checked')) {
                    jQuery('#wpvivid_start_export').css({'pointer-events': 'auto', 'opacity': '1'});
                }
                else{
                    jQuery('#wpvivid_start_export').css({'pointer-events': 'none', 'opacity': '0.4'});
                }
            });

            jQuery('#wpvivid_export_page').on("click", '#cb-select-all-2', function() {
                if(jQuery(this).prop('checked')) {
                    jQuery('#wpvivid_start_export').css({'pointer-events': 'auto', 'opacity': '1'});
                }
                else{
                    jQuery('#wpvivid_start_export').css({'pointer-events': 'none', 'opacity': '0.4'});
                }
            });

            jQuery('#wpvivid_export_page').on("click", 'input[name="post[]"]', function() {
                var check_status = wpvivid_check_export_status();
                if(check_status){
                    jQuery('#wpvivid_start_export').css({'pointer-events': 'auto', 'opacity': '1'});
                }
                else{
                    jQuery('#wpvivid_start_export').css({'pointer-events': 'none', 'opacity': '0.4'});
                }
            });

            jQuery('#wpvivid_export_page').on("click", '#wpvivid_start_export', function(){
                wpvivid_clear_notice('wpvivid_export_notice');
                jQuery('#wpvivid_export_summary').hide();
                var post_type = jQuery('#wpvivid_start_export').attr('name');

                var select_type='all';
                jQuery('input:radio[name=contain]').each(function() {
                    if(jQuery(this).prop('checked')) {
                        select_type=jQuery(this).val();
                    }
                });

                var has_item = false;
                var post_ids = {};
                jQuery('input[name="post[]"]').each(function (i) {
                    var id=jQuery(this).val();
                    if(jQuery(this).prop('checked')) {
                        post_ids[id]=1;
                        has_item = true;
                    }
                    else {
                        post_ids[id]=0;
                    }
                });

                if(select_type === 'list' && !has_item){
                    alert('<?php esc_html_e('Please select at least one item.', 'wpvivid-backuprestore'); ?>');
                }
                else{
                    var post_ids_json = {
                        'post_ids': post_ids
                    };

                    jQuery('#wpvivid_export_custom').hide();

                    var export_data = wpvivid_ajax_data_transfer('export');
                    export_data = JSON.parse(export_data);
                    jQuery.extend(export_data, post_ids_json);
                    export_data = JSON.stringify(export_data);

                    var ajax_data = {
                        'action': 'wpvivid_prepare_export_post',
                        'post_type': post_type,
                        'export_data': export_data
                    };
                    wpvivid_export_lock_unlock('lock');
                    wpvivid_post_request(ajax_data, function(data)
                    {
                        var jsonarray = jQuery.parseJSON(data);
                        if (jsonarray.result === 'success')
                        {
                            export_task_id=jsonarray.task_id;

                            var ajax_data = {
                                'action':'wpvivid_export_now',
                                'task_id':export_task_id
                            };

                            wpvivid_export_progpress();

                            wpvivid_post_request(ajax_data, function(data) {
                            },function(XMLHttpRequest, textStatus, errorThrown) {
                            });
                        }
                        else if (jsonarray.result === 'failed') {
                            wpvivid_export_lock_unlock('unlock');
                            alert(jsonarray.error);
                        }
                    }, function(XMLHttpRequest, textStatus, errorThrown) {
                        wpvivid_export_lock_unlock('unlock');
                        var error_message = wpvivid_output_ajaxerror('export the previously-exported settings', textStatus, errorThrown);
                        alert(error_message);
                    });
                }
            });

            jQuery('#wpvivid_export_page').on("click",'.first-page',function() {
                wpvivid_change_page('first');
            });

            jQuery('#wpvivid_export_page').on("click",'.prev-page',function() {
                var page=parseInt(jQuery(this).attr('value'));
                wpvivid_change_page(page-1);
            });

            jQuery('#wpvivid_export_page').on("click",'.next-page',function() {
                var page=parseInt(jQuery(this).attr('value'));
                wpvivid_change_page(page+1);
            });

            jQuery('#wpvivid_export_page').on("click",'.last-page',function() {
                wpvivid_change_page('last');
            });

            jQuery('#wpvivid_export_page').on("keypress", '.current-page', function(){
                if(event.keyCode === 13){
                    var page = jQuery(this).val();
                    wpvivid_change_page(page);
                }
            });

            function wpvivid_change_page(page) {
                var post_type='post';
                jQuery('input:radio[name=post_type]').each(function() {
                    if(jQuery(this).prop('checked'))
                    {
                        post_type=jQuery(this).val();
                    }
                });

                var post_ids = {};

                jQuery('input[name="post[]"]').each(function (i) {
                    var id=jQuery(this).val();
                    if(jQuery(this).prop('checked'))
                    {
                        post_ids[id]=1;
                    }
                    else
                    {
                        post_ids[id]=0;
                    }
                });

                var ajax_data = {
                    'action':'wpvivid_get_post_list_page',
                    'post_type': post_type,
                    'page': page,
                    'post_ids':post_ids
                };

                wpvivid_post_request(ajax_data, function(data) {
                    var jsonarray = jQuery.parseJSON(data);
                    if (jsonarray.result === 'success') {
                        jQuery('#wpvivid_post_list').html(jsonarray.rows);
                    }
                    else if (jsonarray.result === 'failed') {
                        alert(jsonarray.error);
                    }
                }, function(XMLHttpRequest, textStatus, errorThrown) {
                    var error_message = wpvivid_output_ajaxerror('export the previously-exported settings', textStatus, errorThrown);
                    alert(error_message);
                });
            }

            function wpvivid_export_progpress() {
                var ajax_data = {
                    'action': 'wpvivid_export_list_tasks',
                    'task_id': export_task_id
                };

                jQuery('#wpvivid_export_task_progress').show();

                wpvivid_post_request(ajax_data, function(data)
                {
                    var jsonarray = jQuery.parseJSON(data);
                    if (jsonarray.result === 'success')
                    {
                        retry_count=0;
                        export_task_id=jsonarray.task_id;
                        if(jsonarray.show)
                        {
                            jQuery('#wpvivid_export_task_progress').show();
                            jQuery('#wpvivid_export_summary').hide();
                            jQuery('#wpvivid_export_bar_percent').html(jsonarray.percent);
                            jQuery('#wpvivid_export_current_doing').html(jsonarray.doing);
                        }
                        else
                        {
                            jQuery('#wpvivid_export_task_progress').hide();
                        }

                        if(jsonarray.completed)
                        {
                            wpvivid_export_lock_unlock('unlock');
                            //jQuery('#wpvivid_export_notice').show();
                            //jQuery('#wpvivid_export_notice').append(jsonarray.doing);
                            jQuery('#wpvivid_export_summary').show();
                            jQuery('#wpvivid_export_summary').html(jsonarray.doing);
                            jQuery('html, body').animate({scrollTop: jQuery("#wpvivid_export_notice").offset().top}, 'slow');
                            wpvivid_download_export(jsonarray.file_name, jsonarray.file_size);
                        }

                        if(jsonarray.continue)
                        {
                            wpvivid_export_lock_unlock('lock');
                            setTimeout(function ()
                            {
                                wpvivid_export_progpress();
                            }, 3000);
                        }

                        if(jsonarray.error){
                            wpvivid_export_lock_unlock('unlock');
                            jQuery('#wpvivid_export_notice').show();
                            jQuery('#wpvivid_export_notice').append(jsonarray.doing);
                            jQuery('html, body').animate({scrollTop: jQuery("#wpvivid_export_notice").offset().top}, 'slow');
                        }
                    }
                    else
                    {
                        alert(jsonarray.error);
                    }
                }, function(XMLHttpRequest, textStatus, errorThrown)
                {
                    retry_count++;
                    if(retry_count<3)
                    {
                        setTimeout(function () {
                            wpvivid_export_progpress();
                        }, 3000);
                    }
                });
            }

            function wpvivid_download_export(file_name, file_size){
                location.href =ajaxurl+'?_wpnonce='+wpvivid_ajax_object.ajax_nonce+'&action=wpvivid_download_export_backup&file_name='+file_name+'&file_size='+file_size;
            }

            jQuery(document).ready(function (){
                <?php
                $task_id = false;
                $post_type = false;
                $tasks=WPvivid_Exporter_taskmanager::get_tasks();
                foreach ($tasks as $task){
                    $task_id = $task['id'];
                    $post_type = $task['options']['backup_options']['post_type'];
                    break;
                }
                ?>
                var task_id = '<?php echo esc_attr($task_id); ?>';
                if(task_id != false){
                    export_task_id = task_id;
                    wpvivid_export_lock_unlock('lock');
                    wpvivid_export_progpress();
                }
            });
        </script>
        <?php
    }

    public function export_post_step2()
    {
        check_ajax_referer( 'wpvivid_ajax', 'nonce' );
        $check=current_user_can('manage_options');
        $check=apply_filters('wpvivid_ajax_check_security',$check);
        if(!$check)
        {
            die();
        }
        if(isset($_POST['post_type']))
        {
            global $wpdb;
            $post_type = sanitize_text_field($_POST['post_type']);
            $descript_type = $post_type === 'post' ? 'posts' : 'pages';
            $btn_text = $post_type === 'post' ? __('Show Posts', 'wpvivid-backuprestore') : __('Show Pages', 'wpvivid-backuprestore');

            ob_start();
            ?>
            <div style="width:100%; border:1px solid #f1f1f1; float:left; box-sizing: border-box;margin-bottom:10px;">
                <div style="box-sizing: border-box; margin: 1px; background-color: #f1f1f1;"><h2><?php esc_html_e('Choose what to export', 'wpvivid-backuprestore'); ?></h2></div>
            </div>
            <div style="clear: both;"></div>
            <div style="width:100%; border:1px solid #f1f1f1; float:left; padding:10px 10px 0 10px;margin-bottom:10px; box-sizing: border-box;">
                <fieldset>
                    <legend class="screen-reader-text"><span>input type="radio"</span></legend>
                    <div class="wpvivid-element-space-bottom wpvivid-element-space-right" style="float: left;">
                        <label>
                            <input type="radio" option="export" name="contain" value="list" checked/><?php esc_html_e('Filter Posts/Pages', 'wpvivid-backuprestore'); ?>
                        </label>
                    </div>
                    <div style="clear: both;"></div>
                </fieldset>

                <div id="wpvivid_export_custom" style="margin-bottom: 10px;">
                    <table id="wpvivid_post_selector" class="wp-list-table widefat plugins" style="width:100%; border:1px solid #f1f1f1;">
                        <tbody>
                        <?php
                        if($post_type !== 'page') {
                            ?>
                            <tr>
                                <td class="plugin-title column-primary">
                                    <div class="wpvivid-storage-form regular-text">
                                        <?php
                                        wp_dropdown_categories(
                                            array(
                                                'class' => 'regular-text',
                                                'show_option_all' => __('All Categories', 'wpvivid-backuprestore')
                                            )
                                        );
                                        ?>
                                    </div>
                                </td>
                                <td class="column-description desc">
                                    <div class="wpvivid-storage-form-desc">
                                        <i>
                                            <?php
                                            echo esc_html(sprintf('Export %s of all categories or a specific category.', $descript_type));
                                            ?>
                                        </i>
                                    </div>
                                </td>
                            </tr>
                            <?php
                        }
                        ?>
                        <tr>
                            <td class="plugin-title column-primary">
                                <div class="wpvivid-storage-form regular-text">
                                    <?php
                                    $authors = $wpdb->get_col( "SELECT DISTINCT post_author FROM {$wpdb->posts} WHERE post_type = '$post_type'" );
                                    wp_dropdown_users(
                                        array(
                                            'class'           => 'regular-text',
                                            'include'         => $authors,
                                            'name'            => 'post_author',
                                            'multi'           => true,
                                            'show_option_all' => __( 'All Authors', 'wpvivid-backuprestore' ),
                                            'show'            => 'display_name_with_login',
                                        )
                                    );
                                    ?>
                                </div>
                            </td>
                            <td class="column-description desc">
                                <div class="wpvivid-storage-form-desc">
                                    <i>
                                        <?php
                                        echo esc_html(sprintf('Export %s of all authors or a specific author.', $descript_type));
                                        ?>
                                    </i>
                                </div>
                            </td>
                        </tr>

                        <tr>
                            <td class="plugin-title column-primary">
                                <div class="wpvivid-storage-form regular-text">
                                    <label for="post-start-date" class="label-responsive" style="display: block;"></label>
                                    <select class="regular-text" name="post_start_date" id="post-start-date">
                                        <option value="0"><?php esc_html_e( '&mdash; Select &mdash;', 'wpvivid-backuprestore' ); ?></option>
                                        <?php $this->export_date_options($post_type); ?>
                                    </select>
                                </div>
                            </td>
                            <td class="column-description desc">
                                <div class="wpvivid-storage-form-desc">
                                    <i>
                                        <?php
                                        echo esc_html(sprintf('Export %s published after this date.', $descript_type));
                                        ?>
                                    </i>
                                </div>
                            </td>
                        </tr>

                        <tr>
                            <td class="plugin-title column-primary">
                                <div class="wpvivid-storage-form regular-text">
                                    <label for="post-end-date" class="label-responsive" style="display: block;"></label>
                                    <select class="regular-text" name="post_end_date" id="post-end-date">
                                        <option value="0"><?php esc_html_e( '&mdash; Select &mdash;', 'wpvivid-backuprestore' ); ?></option>
                                        <?php $this->export_date_options($post_type); ?>
                                    </select>
                                </div>
                            </td>
                            <td class="column-description desc">
                                <div class="wpvivid-storage-form-desc">
                                    <i>
                                        <?php
                                        echo esc_html(sprintf('Export %s published before this date.', $descript_type));
                                        ?>
                                    </i>
                                </div>
                            </td>
                        </tr>

                        <tr style="display: none;">
                            <td class="plugin-title column-primary">
                                <div class="wpvivid-storage-form">
                                    <input type="text" class="regular-text" id="post-search-id-input" name="post-id" autocomplete="off" value=""/>
                                </div>
                            </td>
                            <td class="column-description desc">
                                <div class="wpvivid-storage-form-desc">
                                    <i>Enter a <?php echo esc_html($post_type); ?> ID.(optional)</i>
                                </div>
                            </td>
                        </tr>

                        <tr style="display: none;">
                            <td class="plugin-title column-primary">
                                <div class="wpvivid-storage-form">
                                    <input type="text" class="regular-text" id="post-search-title-input" name="post-title" autocomplete="off" value=""/>
                                </div>
                            </td>
                            <td class="column-description desc">
                                <div class="wpvivid-storage-form-desc">
                                    <i>Enter a <?php echo esc_html($post_type); ?> title.(optional)</i>
                                </div>
                            </td>
                        </tr>

                        <tr>
                            <td class="plugin-title column-primary">
                                <div class="wpvivid-storage-form">
                                    <input class="button-primary" id="wpvivid-post-query-submit" type="submit" name="<?php echo esc_attr($post_type); ?>" value="<?php echo esc_attr($btn_text); ?>" />
                                </div>
                            </td>
                            <td class="column-description desc">
                                <div class="wpvivid-storage-form-desc">
                                    <i>
                                        <?php
                                        echo esc_html(sprintf('Search for %s according to the above rules.', $post_type));
                                        ?>
                                    </i>
                                </div>
                            </td>
                        </tr>
                        </tbody>
                    </table>
                    <div id="wpvivid_post_list"></div>
                </div>
            </div>

            <div style="width:100%; border:1px solid #f1f1f1; float:left; box-sizing: border-box;margin-bottom:10px;">
                <div style="box-sizing: border-box; margin: 1px; background-color: #f1f1f1;"><h2><?php esc_html_e('Comment the export (optional)', 'wpvivid-backuprestore'); ?></h2></div>
            </div>
            <div style="clear: both;"></div>
            <div style="width:100%; border:1px solid #f1f1f1; float:left; padding:10px 10px 0 10px;margin-bottom:10px; box-sizing: border-box;">
                <div>
                    <div class="wpvivid-element-space-bottom wpvivid-text-space-right" style="float: left; padding-top: 6px;"><?php esc_html_e('Comment the export: ', 'wpvivid-backuprestore'); ?></div>
                    <div class="wpvivid-element-space-bottom wpvivid-text-space-right" style="float: left;">
                        <input type="text" option="export" name="post_comment" id="wpvivid_set_post_comment" onkeyup="value=value.replace(/[^a-zA-Z0-9]/g,'')" onpaste="value=value.replace(/[^\a-\z\A-\Z0-9]/g,'')" />
                    </div>
                    <div class="wpvivid-element-space-bottom wpvivid-text-space-right" style="float: left; padding-top: 6px;"><?php esc_html_e('Only letters (except for wpvivid) and numbers are allowed.', 'wpvivid-backuprestore'); ?></div>
                    <div style="clear: both;"></div>
                </div>
                <div>
                    <div class="wpvivid-element-space-bottom wpvivid-text-space-right" style="float: left;"><?php esc_html_e('Sample:', 'wpvivid-backuprestore'); ?></div>
                    <div class="wpvivid-element-space-bottom" style="float: left;">
                        <div class="wpvivid-element-space-bottom" style="display: inline;" id="wpvivid_post_comment">*</div><div class="wpvivid-element-space-bottom" style="display: inline;">_wpvivid-5dbf8d6a5f133_2019-11-08-03-15_export_<?php echo esc_html($post_type); ?>.zip</div>
                    </div>
                    <div style="clear: both;"></div>
                </div>
            </div>

            <div>
                <input class="button-primary" id="wpvivid_start_export" type="submit" name="<?php echo esc_attr($post_type); ?>" value="<?php esc_attr_e('Export and Download', 'wpvivid-backuprestore'); ?>" style="pointer-events: none; opacity: 0.4;">
            </div>
            <?php

            $html = ob_get_clean();
            $ret['result']='success';
            $ret['html']=$html;
        }
        else
        {
            $ret['result']='failed';
            $ret['error']='not set post type';
        }
        echo wp_json_encode($ret);
        die();
    }

    public function export_post_step3()
    {
        check_ajax_referer( 'wpvivid_ajax', 'nonce' );
        $check=current_user_can('manage_options');
        $check=apply_filters('wpvivid_ajax_check_security',$check);
        if(!$check)
        {
            die();
        }
        $post_type=sanitize_text_field($_POST['post_type']);
        $post_all=sanitize_text_field($_POST['all']);
        if(isset($post_type)&&isset($post_all))
        {
            $old_post_ids=array();
            if(isset($_POST['post_ids']))
            {
                $old_post_ids=$_POST['post_ids'];
                $old_post_ids = array_map( 'sanitize_key', $old_post_ids );
                $old_post_ids=(int)$old_post_ids;
            }

            $list_cache=get_option('wpvivid_list_cache',array());

            foreach ($old_post_ids as $id=>$checked)
            {
                if(isset($list_cache[$id]))
                {
                    $list_cache[$id]['checked']=$checked;
                }
            }
            WPvivid_Setting::update_option('wpvivid_list_cache',$list_cache);

            $post_count=0;

            if($post_all=='all')
            {
                global $wpdb;

                $where      = $wpdb->prepare( "post_type =%s", $post_type);
                $posts_ids = $wpdb->get_col( "SELECT ID FROM {$wpdb->posts} WHERE $where" );
                $post_count=sizeof($posts_ids);
            }
            else
            {
                foreach ($list_cache as $id=>$item)
                {
                    if($item['checked'])
                        $post_count++;
                }
            }

            ob_start();
            ?>
            <h2>Export post type:<strong><?php echo esc_html($post_type)?></strong></h2>
            <p>
                Selected post(s):<?php echo esc_html($post_count)?>
            </p>
            <p class="submit">
                <input type="button" class="button button-primary wpvivid-export-step3-prev" value="Prev step">
                <input type="button" class="button button-primary" id="wpvivid_start_export" value="Start Export">
            </p>
            <?php

            $html = ob_get_clean();
            $ret['result']='success';
            $ret['html']=$html;
        }
        else
        {
            $ret['result']='failed';
            $ret['error']='not set post type';
        }
        echo wp_json_encode($ret);
        die();
    }

    public function my_admin_custom_styles()
    {
        echo  '<style type="text/css">    
        .column-file_name { width:25% }
        .column-export_type { width:8% }
        .column-posts_count { width:8% }
        .column-media_size { width:8% }
        .column-import { width:8% }
    </style>';
    }

    public function get_list()
    {
        check_ajax_referer( 'wpvivid_ajax', 'nonce' );
        $check=current_user_can('manage_options');
        $check=apply_filters('wpvivid_ajax_check_security',$check);
        if(!$check)
        {
            die();
        }

        if(!isset($_POST['post_type'])&&!isset($_POST['cat'])&&!isset($_POST['authors'])&&!isset($_POST['post_start_date'])&&!isset($_POST['post_end_date']))
        {
            die();
        }

        if(isset($_POST['post_ids'])&&!empty($_POST['post_ids']))
        {
            $select_post_id=$_POST['post_ids'];
            $select_post_id = array_map( 'sanitize_key', $select_post_id );
            $select_post_id=(int)$select_post_id;
        }
        else
        {
            $select_post_id=0;
        }

        if(isset($_POST['post_title'])&&!empty($_POST['post_title']))
        {
            $post_title=sanitize_text_field($_POST['post_title']);
        }
        else
        {
            $post_title='';
        }
        //

        $post_type=sanitize_text_field($_POST['post_type']);
        if(isset($_POST['cat'])) {
            $cat = (int)sanitize_key($_POST['cat']);
        }
        $author=(int)sanitize_key($_POST['authors']);
        $post_start_date=sanitize_text_field($_POST['post_start_date']);
        $post_end_date=sanitize_text_field($_POST['post_end_date']);


        global $wpdb;

        $where      = $wpdb->prepare( "post_type =%s", $post_type);
        $join = '';
        if(isset($_POST['cat'])) {
            if ($term = term_exists($cat, 'category')) {
                $join = "INNER JOIN {$wpdb->term_relationships} ON ({$wpdb->posts}.ID = {$wpdb->term_relationships}.object_id)";
                $where .= $wpdb->prepare(" AND {$wpdb->term_relationships}.term_taxonomy_id = %d", $term['term_taxonomy_id']);
            }
        }
        if ( $author )
        {
            $where .= $wpdb->prepare( " AND {$wpdb->posts}.post_author = %d", $author );
        }
        if ( $post_start_date )
        {
            $where .= $wpdb->prepare( " AND {$wpdb->posts}.post_date >= %s", gmdate( 'Y-m-d', strtotime( $post_start_date ) ) );
        }
        if ( $post_end_date )
        {
            $where .= $wpdb->prepare( " AND {$wpdb->posts}.post_date < %s", gmdate( 'Y-m-d', strtotime( '+1 month', strtotime( $post_end_date ) ) ) );
        }
        if($select_post_id)
        {
            $where .= $wpdb->prepare( " AND {$wpdb->posts}.ID = %d", $select_post_id );
        }
        if($post_title)
        {
            $where .= $wpdb->prepare( " AND {$wpdb->posts}.post_title LIKE %s", '%' . $wpdb->esc_like($post_title) . '%' );
        }

        $posts_ids = $wpdb->get_col( "SELECT ID FROM {$wpdb->posts} $join WHERE $where" );

        asort($posts_ids);

        $list_cache=array();
        foreach ($posts_ids as $id)
        {
            $post_id['id']=$id;
            $post_id['checked']=0;
            $list_cache[$id]=$post_id;
        }
        WPvivid_Setting::update_option('wpvivid_list_cache',$list_cache);
        $page=1;

        $arg['screen']=$post_type;
        $myListTable = new WPvivid_Post_List($arg);
        $myListTable->set_post_ids($list_cache,$page);
        $myListTable->prepare_items();
        ob_start();
        $myListTable->display();
        $rows = ob_get_clean();
        $ret['result']='success';
        $ret['rows']=$rows;
        echo wp_json_encode($ret);

        die();
    }

    public function get_export_list()
    {
        check_ajax_referer( 'wpvivid_ajax', 'nonce' );
        $check=current_user_can('manage_options');
        $check=apply_filters('wpvivid_ajax_check_security',$check);
        if(!$check)
        {
            die();
        }
        $list = get_option('wpvivid_export_list',array());
        $display_list=new WPvivid_Export_List();
        $display_list->set_parent('wpvivid_import_list');
        $display_list->set_list($list);
        $display_list->prepare_items();
        ob_start();
        $display_list->display();
        $html = ob_get_clean();
        $ret['result']='success';
        $ret['html']=$html;
        echo wp_json_encode($ret);

        die();
    }

    public function get_list_page()
    {
        check_ajax_referer( 'wpvivid_ajax', 'nonce' );
        $check=current_user_can('manage_options');
        $check=apply_filters('wpvivid_ajax_check_security',$check);
        if(!$check)
        {
            die();
        }

        if(!isset($_POST['post_type'])&&!isset($_POST['page']))
        {
            die();
        }

        $list_cache=get_option('wpvivid_list_cache',array());

        WPvivid_Setting::update_option('wpvivid_list_cache',$list_cache);

        $page=sanitize_key($_POST['page']);

        $post_type=sanitize_text_field($_POST['post_type']);
        $arg['screen']=$post_type;

        $myListTable = new WPvivid_Post_List($arg);
        $myListTable->set_post_ids($list_cache,$page);
        $myListTable->prepare_items();
        ob_start();
        $myListTable->display();
        $rows = ob_get_clean();

        $ret['result']='success';
        $ret['rows']=$rows;
        echo wp_json_encode($ret);
        die();
    }

    public function export_date_options($post_type = 'post')
    {
        global $wpdb, $wp_locale;

        $months = $wpdb->get_results(
            $wpdb->prepare(
                "
		SELECT DISTINCT YEAR( post_date ) AS year, MONTH( post_date ) AS month
		FROM $wpdb->posts
		WHERE post_type = %s AND post_status != 'auto-draft'
		ORDER BY post_date DESC
	",
                $post_type
            )
        );

        $month_count = count( $months );
        if ( ! $month_count || ( 1 == $month_count && 0 == $months[0]->month ) ) {
            return;
        }

        foreach ( $months as $date ) {
            if ( 0 == $date->year ) {
                continue;
            }

            $month = zeroise( $date->month, 2 );
            echo '<option value="' . esc_attr($date->year) . '-' . esc_attr($month) . '">' . esc_html($wp_locale->get_month( $month ) . ' ' . $date->year) . '</option>';
        }
    }

    public function prepare_export_post()
    {
        check_ajax_referer( 'wpvivid_ajax', 'nonce' );
        $check=current_user_can('manage_options');
        $check=apply_filters('wpvivid_ajax_check_security',$check);
        if(!$check)
        {
            die();
        }
        if(isset($_POST['post_type'])&&isset($_POST['export_data']))
        {
            $post_type   = sanitize_text_field($_POST['post_type']);
            $json_export = sanitize_text_field($_POST['export_data']);
            $json_export = stripslashes($json_export);
            $export_data = json_decode($json_export, true);

            $post_ids=array();
            $posts_ids=array();
            if(isset($export_data['post_ids']) && !empty($export_data['post_ids']))
            {
                $post_ids=$export_data['post_ids'];
            }
            foreach ($post_ids as $id=>$checked)
            {
                if($checked)
                {
                    $posts_ids[]=$id;
                }
            }

            if(empty($posts_ids))
            {
                $ret['result']='failed';
                $ret['error']=__('Empty post id', 'wpvivid-backuprestore');
                echo wp_json_encode($ret);
                die();
            }
            if(WPvivid_Exporter_taskmanager::is_tasks_running())
            {
                $ret['result']='failed';
                $ret['error']=__('A task is already running. Please wait until the running task is complete, and try again.', 'wpvivid-backuprestore');
                echo wp_json_encode($ret);
                die();
            }

            $export_task=new WPvivid_Exporter_task();

            $options['post_ids']=$posts_ids;
            $options['post_type']=$post_type;
            $options['post_comment']=$export_data['post_comment'];

            $ret=$export_task->new_backup_task($options);
            echo wp_json_encode($ret);
        }
        die();
    }

    public function export_now()
    {
        check_ajax_referer( 'wpvivid_ajax', 'nonce' );
        $check=current_user_can('manage_options');
        $check=apply_filters('wpvivid_ajax_check_security',$check);
        if(!$check)
        {
            die();
        }
        try
        {
            if (!isset($_POST['task_id']) || empty($_POST['task_id']) || !is_string($_POST['task_id']))
            {
                $ret['result'] = 'failed';
                $ret['error'] = __('Error occurred while parsing the request data. Please try to run export task again.', 'wpvivid-backuprestore');
                echo wp_json_encode($ret);
                die();
            }

            $task_id = sanitize_key($_POST['task_id']);

            if(WPvivid_Exporter_taskmanager::is_tasks_running())
            {
                $ret['result'] = 'failed';
                $ret['error'] = __('A task is already running. Please wait until the running task is complete, and try again.', 'wpvivid-backuprestore');
                echo wp_json_encode($ret);
                die();
            }

            $this->export_post($task_id);
        }
        catch (Exception $error)
        {
            $message = 'An exception has occurred. class: '.get_class($error).';msg: '.$error->getMessage().';code: '.$error->getCode().';line: '.$error->getLine().';in_file: '.$error->getFile().';';
            error_log($message);
            echo wp_json_encode(array('result'=>'failed','error'=>$message));
            die();
        }
        die();
    }

    public function export_post($task_id)
    {
        $this->end_shutdown_function = false;
        register_shutdown_function(array($this,'deal_shutdown_error'),$task_id);
        @ignore_user_abort(true);

        WPvivid_Exporter_taskmanager::update_backup_task_status($task_id,true,'running');

        global $wpvivid_plugin;

        $wpvivid_plugin->wpvivid_log->OpenLogFile(WPvivid_Exporter_taskmanager::get_task_options($task_id,'log_file_name'));
        $wpvivid_plugin->wpvivid_log->WriteLog('Start export posts.','notice');
        $wpvivid_plugin->wpvivid_log->WriteLogHander();
        $this->flush($task_id);

        $export=new WPvivid_Exporter();

        @set_time_limit(900);
        try
        {
            $ret = $export->export($task_id);
            if($ret['result']=='success')
            {
                do_action('wpvivid_handle_export_success', $task_id, true);
            }
            else
            {
                $wpvivid_plugin->wpvivid_log->WriteLog($ret['error'],'error');
                WPvivid_Exporter_taskmanager::update_backup_task_status($task_id, false, 'error', false, false, $ret['error']);
                do_action('wpvivid_handle_export_failed', $task_id, true);
            }

        }
        catch (Exception $error)
        {
            $message = 'An error has occurred. class:'.get_class($error).';msg:'.$error->getMessage().';code:'.$error->getCode().';line:'.$error->getLine().';in_file:'.$error->getFile().';';
            error_log($message);
            WPvivid_Exporter_taskmanager::update_backup_task_status($task_id,false,'error',false,false,$message);
            $wpvivid_plugin->wpvivid_log->WriteLog($message,'error');
            $this->end_shutdown_function=true;
            die();
        }

        echo wp_json_encode($ret);
        $this->end_shutdown_function=true;
        die();
    }

    public function deal_shutdown_error($task_id)
    {
        if($this->end_shutdown_function===false)
        {
            global $wpvivid_plugin;

            $last_error = error_get_last();
            if (!empty($last_error) && !in_array($last_error['type'], array(E_NOTICE,E_WARNING,E_USER_NOTICE,E_USER_WARNING,E_DEPRECATED), true))
            {
                $error = $last_error;
            } else {
                $error = false;
            }
            if (WPvivid_Exporter_taskmanager::get_task($task_id) !== false)
            {
                if ($wpvivid_plugin->wpvivid_log->log_file_handle == false)
                {
                    $wpvivid_plugin->wpvivid_log->OpenLogFile(WPvivid_Exporter_taskmanager::get_task_options($task_id, 'log_file_name'));
                }

                $status = WPvivid_Exporter_taskmanager::get_backup_task_status($task_id);

                $message='in shutdown';

                if ($error !== false)
                {
                    $message= 'type: '. $error['type'] . ', ' . $error['message'] . ' file:' . $error['file'] . ' line:' . $error['line'];
                }
                WPvivid_Exporter_taskmanager::update_backup_task_status($task_id, false, 'error', false, $status['resume_count'], $message);
                if ($wpvivid_plugin->wpvivid_log)
                    $wpvivid_plugin->wpvivid_log->WriteLog($message, 'error');
            }
            die();
        }
    }

    public function handle_export_success($task_id)
    {
        global $wpvivid_plugin;
        WPvivid_Exporter_taskmanager::update_backup_task_status($task_id,false,'completed');

        $wpvivid_plugin->wpvivid_log->WriteLog('Finished to export post','notice');
    }

    public function handle_export_failed($task_id)
    {
    }

    public function list_tasks()
    {
        check_ajax_referer( 'wpvivid_ajax', 'nonce' );
        $check=current_user_can('manage_options');
        $check=apply_filters('wpvivid_ajax_check_security',$check);
        if(!$check)
        {
            die();
        }

        $ret['result']='success';
        $ret['show']=false;
        $tasks=WPvivid_Exporter_taskmanager::get_tasks();
        foreach ($tasks as $task)
        {
            $this->task_monitor($task['id']);
            $task=WPvivid_Exporter_taskmanager::get_task($task['id']);
            $status=WPvivid_Exporter_taskmanager::get_backup_task_status($task['id']);

            $ret['show']=true;
            $ret['completed']=false;
            $ret['error']=false;
            if($status['str']=='running'||$status['str']=='no_responds'||$status['str']=='ready')
            {
                $ret['continue']=1;
            }
            else
            {
                $ret['continue']=0;
                $ret['show']=false;
            }

            $progress=WPvivid_Exporter_taskmanager::get_backup_tasks_progress($task['id']);
            $ret['percent']='<div class="action-progress-bar-percent"  style="height:24px;width:'.(int)$progress['progress'].'%;"></div>';
            $ret['doing']=$task['data']['doing']='export';
            if($status['str']=='ready')
            {
                $ret['doing']=__('Ready to export. Progress: 0%, running time: 0second.','wpvivid-backuprestore');
            }
            else if($status['str']=='running')
            {
                $ret['doing']= ' '.__('Progress: ', 'wpvivid-backuprestore') . $progress['descript'] . ', '.__('running time: ', 'wpvivid-backuprestore') . $progress['running_time'];
            }
            else if($status['str']=='wait_resume')
            {
                $ret['doing']='Task '.$task['id'].' timed out, the export task will retry in '.$task['data']['next_resume_time'].' seconds, retry times: '.$task['status']['resume_count'].'.';
            }
            else if($status['str']=='no_responds')
            {
                $ret['doing']=__('The export task is not responding.','wpvivid-backuprestore');
            }
            else if($status['str']=='completed')
            {
                $file_name = $task['data']['export']['export_info']['file_name'];
                $file_size = $task['data']['export']['export_info']['size'];
                if($task['options']['backup_options']['post_type'] === 'post'){
                    $post_type = 'posts';
                }
                else{
                    $post_type = 'pages';
                }
                $msg = '<div style="margin-bottom: 10px;">The export task is completed and the automatic download starts. If the automatic download didn\'t run, please click <a style="cursor:pointer;" onclick="wpvivid_download_export(\''.$file_name.'\', \''.$file_size.'\');">here</a> to download.</div>';
                $msg .= '<div style="margin-bottom: 10px;">The count of exported '.$post_type.': '.$task['data']['export']['export_info']['post_count'].'.</div>';
                $msg .= '<div style="margin-bottom: 10px;">File name: '.$file_name.'.</div>';
                $msg .= '<div>File size: '.size_format($file_size, 2).'.</div>';

                $ret['completed']=true;
                $ret['file_name'] = $file_name;
                $ret['file_size'] = $file_size;
                $ret['doing']=$msg;
            }
            else if($status['str']=='error')
            {
                $ret['doing']='Export error: '.$task['status']['error'];
                $ret['doing']='<div class="notice notice-error is-dismissible inline"><p>'.__('Export error:', 'wpvivid-backuprestore').' '.$task['status']['error'].'</p></div>';
                $ret['error']=true;
            }

            if($ret['completed']||$ret['error'])
            {
                WPvivid_Exporter_taskmanager::delete_task($task['id']);
            }
        }
        echo wp_json_encode($ret);
        die();
    }

    public function task_monitor($task_id)
    {
        global $wpvivid_plugin;

        if(WPvivid_Exporter_taskmanager::get_task($task_id)!==false)
        {
            if($wpvivid_plugin->wpvivid_log->log_file_handle==false)
            {
                $wpvivid_plugin->wpvivid_log->OpenLogFile(WPvivid_Exporter_taskmanager::get_task_options($task_id,'log_file_name'));
            }

            $status=WPvivid_Exporter_taskmanager::get_backup_task_status($task_id);

            if($status['str']=='running'||$status['str']=='error'||$status['str']=='no_responds')
            {
                $limit=900;
                $time_spend=time()-$status['timeout'];

                if($time_spend>=$limit)
                {
                    //time out
                    $message=__('Task time out.', 'wpvivid-backuprestore');
                    WPvivid_Exporter_taskmanager::update_backup_task_status($task_id,false,'error',false,$status['resume_count'],$message);
                    if($wpvivid_plugin->wpvivid_log)
                        $wpvivid_plugin->wpvivid_log->WriteLog($message,'error');
                    $wpvivid_plugin->wpvivid_log->CloseFile();
                    WPvivid_error_log::create_error_log($wpvivid_plugin->wpvivid_log->log_file);
                }
                else {
                    $time_spend=time()-$status['run_time'];
                    if($time_spend>180)
                    {
                        $wpvivid_plugin->wpvivid_log->WriteLog('Not responding for a long time.','notice');
                        WPvivid_Exporter_taskmanager::update_backup_task_status($task_id,false,'no_responds',false,$status['resume_count']);
                    }
                }
            }
        }
    }

    public function delete_export_list()
    {
        check_ajax_referer( 'wpvivid_ajax', 'nonce' );
        $check=current_user_can('manage_options');
        $check=apply_filters('wpvivid_ajax_check_security',$check);
        if(!$check)
        {
            die();
        }

        if(isset($_POST['export_id']))
        {
            $id=sanitize_key($_POST['export_id']);
            $list = get_option('wpvivid_import_list_cache',array());
            if(empty($list))
            {
                $ret['result']='success';
            }
            else
            {
                if(isset($list[$id]))
                {
                    $item=$list[$id];
                    if(isset($item['export']))
                    {
                        foreach ($item['export'] as $file)
                        {
                            $path=WP_CONTENT_DIR.DIRECTORY_SEPARATOR.WPvivid_Setting::get_backupdir().DIRECTORY_SEPARATOR.WPVIVID_IMPORT_EXPORT_DIR.DIRECTORY_SEPARATOR.$file['file_name'];
                            @wp_delete_file($path);
                        }
                    }
                    unset($list[$id]);
                    WPvivid_Setting::update_option('wpvivid_import_list_cache',$list);
                    $ret['result']='success';
                }
                else
                {
                    $ret['result']='success';
                }
            }
            echo wp_json_encode($ret);
        }
        die();
    }

    public function wpvivid_download_export_backup()
    {
        check_ajax_referer( 'wpvivid_ajax', 'nonce' );
        $check=current_user_can('manage_options');
        $check=apply_filters('wpvivid_ajax_check_security',$check);
        if(!$check)
        {
            die();
        }
        try{
            if(isset($_REQUEST['file_name']) && !empty($_REQUEST['file_name']) && is_string($_REQUEST['file_name']) &&
                isset($_REQUEST['file_size']) && !empty($_REQUEST['file_size']) && is_string($_REQUEST['file_size'])){
                $file_name = sanitize_text_field($_REQUEST['file_name']);
                $file_size = intval(sanitize_key($_REQUEST['file_size']));

                $file_name = basename($file_name);

                $path=WP_CONTENT_DIR.DIRECTORY_SEPARATOR.WPvivid_Setting::get_backupdir().DIRECTORY_SEPARATOR.WPVIVID_IMPORT_EXPORT_DIR.DIRECTORY_SEPARATOR.$file_name;
                if (file_exists($path)) {
                    if (session_id()) {
                        session_write_close();
                    }
                    $size = filesize($path);
                    if($size === $file_size) {
                        if (!headers_sent()) {
                            header('Content-Description: File Transfer');
                            header('Content-Type: application/zip');
                            header('Content-Disposition: attachment; filename="' . basename($path) . '"');
                            header('Cache-Control: must-revalidate');
                            header('Content-Length: ' . $size);
                            header('Content-Transfer-Encoding: binary');
                        }
                        if ($size < 1024 * 1024 * 60) {
                            ob_end_clean();
                            readfile($path);
                            exit;
                        } else {
                            ob_end_clean();
                            $download_rate = 1024 * 10;
                            $file = fopen($path, "r");
                            while (!feof($file)) {
                                @set_time_limit(20);
                                // send the current file part to the browser
                                print fread($file, round($download_rate * 1024));
                                // flush the content to the browser
                                flush();
                                // sleep one second
                                sleep(1);
                            }
                            fclose($file);
                            exit;
                        }
                    }
                    else{
                        $admin_url = admin_url();
                        echo '<a href="'.esc_url($admin_url).'admin.php?page=wpvivid-export-import">';
                        esc_html_e('File size not match. please retry again.', 'wpvivid-backuprestore');
                        echo '</a>';
                        die();
                    }
                }

                $admin_url = admin_url();
                echo '<a href="'.esc_url($admin_url).'admin.php?page=wpvivid-export-import">';
                esc_html_e('File not found. Please retry again.', 'wpvivid-backuprestore');
                echo '</a>';
                die();
            }
        }
        catch (Exception $error) {
            $message = 'An exception has occurred. class: '.get_class($error).';msg: '.$error->getMessage().';code: '.$error->getCode().';line: '.$error->getLine().';in_file: '.$error->getFile().';';
            error_log($message);
            echo wp_json_encode(array('result'=>'failed','error'=>$message));
            die();
        }
    }

    //import
    public function output_import()
    {
        $import_dir = WP_CONTENT_DIR.DIRECTORY_SEPARATOR.WPvivid_Setting::get_backupdir().DIRECTORY_SEPARATOR.WPVIVID_IMPORT_EXPORT_DIR;
        WPvivid_Setting::update_option('wpvivid_import_list_cache',array());
        WPvivid_Setting::update_option('wpvivid_importer_task_list', array());
        ?>
        <div class="postbox export-import-block">
            <div>
                <div class="wpvivid-element-space-bottom wpvivid-element-space-right" style="float: left;">
                    <img src="<?php echo esc_url(WPVIVID_PLUGIN_IMAGES_URL.'export-import.png'); ?>" style="width:50px;height:50px;">
                </div>
                <div style="box-sizing: border-box;">
                    <div class="wpvivid-element-space-bottom wpvivid-element-space-right"><?php esc_html_e('Import posts or pages with images in bulk.', 'wpvivid-backuprestore'); ?>
                        <span class="wpvivid-feature-pro">
                            <a href="https://wpvivid.com/import-content" target="_blank" style="text-decoration: none;"><?php esc_html_e('Learn more', 'wpvivid-backuprestore'); ?></a>
                        </span>
                    </div>
                    <div class="wpvivid-element-space-bottom wpvivid-element-space-right"><strong><?php esc_html_e('Note:', 'wpvivid-backuprestore'); ?></strong>
                        <?php esc_html_e('To properly display the imported content, please make sure that the importing and exporting sites have the same environment, for example, same theme or pages built with the same page builder.', 'wpvivid-backuprestore'); ?>
                    </div>
                    <div style="clear: both;"></div>
                </div>
                <div style="clear: both;"></div>
            </div>
            <div style="clear: both;"></div>

            <div style="background: #fff; border: 1px solid #e5e5e5; border-radius: 6px; margin-bottom: 10px; padding: 10px;">
                <div style="margin-right: 10px; float: left; height: 28px; line-height: 28px;"><?php echo esc_html(sprintf('Imported files will be temporarily stored in %s directory', $import_dir)) ?></div>
                <div style="float: left;"><input class="button" type="submit" id="wpvivid_empty_import_folder" value="<?php esc_attr_e('Delete Exported Files In Folder', 'wpvivid-backuprestore'); ?>" onclick="wpvivid_clean_import_folder();" /></div>
                <div style="clear: both;"></div>
            </div>

            <div id="wpvivid_import_step1">
                <p><?php esc_html_e('Choose an export from your computer to import: ', 'wpvivid-backuprestore'); ?></p>
                <input class="button button-primary" type="button" id="wpvivid_select_import_file_button" value="<?php esc_attr_e('Upload and Import', 'wpvivid-backuprestore'); ?>" />
                <div id="wpvivid_upload_file_list" class="hide-if-no-js" style="margin-top: 10px; display: none;"></div>
                <br>
                <p><?php echo esc_html(sprintf('Or you can use ftp to upload the export to the directory %s. Then click the button below to scan the file to import.', $import_dir)); ?></p>
                <input class="button button-primary" type="button" value="<?php esc_attr_e('Scan Uploaded Exports', 'wpvivid-backuprestore'); ?>" onclick="wpvivid_refresh_import_list();" />
                <div class="wpvivid-export-import-block" id="wpvivid_import_list" style="margin-top: 10px; display: none;"></div>
            </div>
            <div id="wpvivid_import_step2" style="display: none;">
                <h3><?php esc_html_e('The importing file info', 'wpvivid-backuprestore'); ?></h3>
                <div id="wpvivid_import_file_data">
                </div>
                <h3><?php esc_html_e('Assign author', 'wpvivid-backuprestore'); ?></h3>
                <div>
                    <?php esc_html_e('Select an existing author:', 'wpvivid-backuprestore'); ?>
                    <?php wp_dropdown_users( array( 'name' => "user_map", 'multi' => true, 'show_option_all' => __( '- Select -', 'wpvivid-backuprestore' ) ) );?>
                </div>
                <h3><?php esc_html_e('Import Setting', 'wpvivid-backuprestore'); ?></h3>
                <div style="margin-bottom: 10px;">
                    <label>
                        <input type="checkbox" id="wpvivid_overwrite_existing" />
                        <span><strong id="wpvivid_import_type"><?php esc_html_e('Overwrite existing pages', 'wpvivid-backuprestore'); ?></strong></span>
                    </label>
                </div>
                <div style="margin-bottom: 10px;">
                    <span><?php esc_html_e('With this option checked, Pages/posts already existing will be overwritten with the updated ones in an import.', 'wpvivid-backuprestore'); ?></span>
                </div>
                <input class="button button-primary" type="button" id="wpvivid_start_import" value="<?php esc_attr_e('Import', 'wpvivid-backuprestore'); ?>" />
                <input class="button button-primary" type="button" id="wpvivid_rechoose_import_file" value="<?php esc_attr_e('Back', 'wpvivid-backuprestore'); ?>" />
            </div>
            <div id="wpvivid_import_step3" style="display: none;">
                <div class="postbox wpvivid-import-log" id="wpvivid_import_log" style="margin-top: 10px; margin-bottom: 0;"></div>
            </div>
        </div>
        <?php
        $chunk_size = min(wp_max_upload_size()-1024, 1048576*2);
        $plupload_init = array(
            'runtimes'            => 'html5,silverlight,flash,html4',
            'browse_button'       => 'wpvivid_select_import_file_button',
            'file_data_name'      => 'async-upload',
            'max_retries'		    => 3,
            'multiple_queues'     => true,
            'max_file_size'       => '10Gb',
            'chunk_size'        => $chunk_size.'b',
            'url'                 => admin_url('admin-ajax.php'),
            'flash_swf_url'       => includes_url('js/plupload/plupload.flash.swf'),
            'silverlight_xap_url' => includes_url('js/plupload/plupload.silverlight.xap'),
            'multipart'           => true,
            'urlstream_upload'    => true,
            'multi_selection'      => false,
            // additional post data to send to our ajax hook
            'multipart_params'    => array(
                '_ajax_nonce' => wp_create_nonce('wpvivid_ajax'),
                'action'      => 'wpvivid_upload_import_files',            // the ajax action name
            ),
        );
        if (is_file(ABSPATH.WPINC.'/js/plupload/Moxie.swf')) {
            $plupload_init['flash_swf_url'] = includes_url('js/plupload/Moxie.swf');
        } else {
            $plupload_init['flash_swf_url'] = includes_url('js/plupload/plupload.flash.swf');
        }

        if (is_file(ABSPATH.WPINC.'/js/plupload/Moxie.xap')) {
            $plupload_init['silverlight_xap_url'] = includes_url('js/plupload/Moxie.xap');
        } else {
            $plupload_init['silverlight_xap_url'] = includes_url('js/plupload/plupload.silverlight.swf');
        }

        // we should probably not apply this filter, plugins may expect wp's media uploader...
        $plupload_init = apply_filters('plupload_init', $plupload_init);
        $upload_file_image = includes_url( '/images/media/archive.png' );
        ?>
        <script type="text/javascript">
            var uploader;
            var import_file_name='';
            jQuery(document).ready(function($)
            {
                // create the uploader and pass the config from above
                jQuery('#wpvivid_upload_submit_btn').hide();
                uploader = new plupload.Uploader(<?php echo wp_json_encode($plupload_init); ?>);

                // checks if browser supports drag and drop upload, makes some css adjustments if necessary
                uploader.bind('Init', function(up)
                {
                    var uploaddiv = $('#wpvivid_plupload-upload-ui');

                    if(up.features.dragdrop){
                        uploaddiv.addClass('drag-drop');
                        $('#drag-drop-area')
                            .bind('dragover.wp-uploader', function(){ uploaddiv.addClass('drag-over'); })
                            .bind('dragleave.wp-uploader, drop.wp-uploader', function(){ uploaddiv.removeClass('drag-over'); });

                    }else{
                        uploaddiv.removeClass('drag-drop');
                        $('#drag-drop-area').unbind('.wp-uploader');
                    }
                });
                uploader.init();

                function wpvivid_check_plupload_added_files(up, files)
                {
                    jQuery('#wpvivid_import_list').hide();
                    var file=files[0];

                    var ajax_data = {
                        'action': 'wpvivid_check_import_file',
                        'file_name':file.name
                    };
                    wpvivid_post_request(ajax_data, function (data)
                    {
                        var jsonarray = jQuery.parseJSON(data);
                        if (jsonarray.result === "success")
                        {
                            jQuery('#wpvivid_select_import_file_button').css({'pointer-events': 'none', 'opacity': '0.4'});
                            var repeat_files = '';
                            plupload.each(files, function(file)
                            {
                                var brepeat=false;
                                var file_list = jQuery('#wpvivid_upload_file_list span');
                                file_list.each(function (index, value) {
                                    if (value.innerHTML === file.name) {
                                        brepeat=true;
                                    }
                                });
                                if(!brepeat) {
                                    jQuery('#wpvivid_upload_file_list').append(
                                        '<div id="' + file.id + '" style="width: 100%; height: 36px; background: #f1f1f1; margin-bottom: 1px;">' +
                                        '<img src=" <?php echo esc_attr($upload_file_image); ?> " alt="" style="float: left; margin: 2px 10px 0 3px; max-width: 40px; max-height: 32px;">' +
                                        '<div style="line-height: 36px; float: left; margin-left: 5px;"><span>' + file.name + '</span></div>' +
                                        '<div class="fileprogress" style="line-height: 36px; float: right; margin-right: 5px;"></div>' +
                                        '</div>' +
                                        '<div style="clear: both;"></div>'
                                    );
                                    jQuery('#wpvivid_upload_file_list').show();

                                    uploader.refresh();
                                    uploader.start();
                                }
                                else{
                                    if(repeat_files === ''){
                                        repeat_files += file.name;
                                    }
                                    else{
                                        repeat_files += ', ' + file.name;
                                    }
                                }
                            });
                            if(repeat_files !== ''){
                                alert(repeat_files + " already exists in upload list.");
                                repeat_files = '';
                            }
                        }
                        else if(jsonarray.result === "failed")
                        {
                            uploader.removeFile(file);
                            alert(jsonarray.error);
                        }
                    }, function (XMLHttpRequest, textStatus, errorThrown)
                    {
                        var error_message = wpvivid_output_ajaxerror('uploading backups', textStatus, errorThrown);
                        uploader.removeFile(file);
                        alert(error_message);
                    });
                }

                uploader.bind('FilesAdded', wpvivid_check_plupload_added_files);

                uploader.bind('Error', function(up, error)
                {
                    alert('Upload ' + error.file.name +' error, error code: ' + error.code + ', ' + error.message);
                    console.log(error);
                });

                uploader.bind('FileUploaded', function(up, file, response)
                {
                    var jsonarray = jQuery.parseJSON(response.response);
                    if(jsonarray.result == 'failed'){
                        alert('upload ' + file.name + ' failed, ' + jsonarray.error);
                    }
                });

                uploader.bind('UploadProgress', function(up, file)
                {
                    jQuery('#' + file.id + " .fileprogress").html(file.percent + "%");
                });

                uploader.bind('UploadComplete',function(up, files)
                {
                    jQuery('#wpvivid_select_import_file_button').css({'pointer-events': 'auto', 'opacity': '1'});
                    var ajax_data = {
                        'action': 'wpvivid_upload_import_file_complete',
                        'files':JSON.stringify(files)
                    };
                    wpvivid_post_request(ajax_data, function (data)
                    {
                        try
                        {
                            var jsonarray = jQuery.parseJSON(data);
                            if(jsonarray.result === 'success')
                            {
                                jQuery('#wpvivid_upload_file_list').html("");
                                jQuery('#wpvivid_upload_file_list').hide();
                                wpvivid_import_step2(jsonarray.data);
                            }
                            else if(jsonarray.result === 'failed')
                            {
                                jQuery('#wpvivid_upload_file_list').html("");
                                jQuery('#wpvivid_upload_file_list').hide();
                                alert(jsonarray.error);
                            }
                        }
                        catch(err)
                        {
                            alert(err);
                        }
                    }, function (XMLHttpRequest, textStatus, errorThrown)
                    {
                        var error_message = wpvivid_output_ajaxerror('refreshing backup list', textStatus, errorThrown);
                        alert(error_message);
                    });
                    plupload.each(files, function(file)
                    {
                        if(typeof file === 'undefined')
                        {

                        }
                        else
                        {
                            uploader.removeFile(file.id);
                        }
                    });
                })
            });

            function wpvivid_clean_import_folder()
            {
                var descript = '<?php esc_html_e('Are you sure you want to delete all the exported files in the /ImportandExport folder? All the export files in the folder will be permanently deleted.', 'wpvivid-backuprestore'); ?>';
                var ret = confirm(descript);
                if(ret === true){
                    var ajax_data = {
                        'action': 'wpvivid_clean_import_folder'
                    };
                    wpvivid_post_request(ajax_data, function (data)
                    {
                        try {
                            var jsonarray = jQuery.parseJSON(data);
                            if(jsonarray.html !== false) {
                                jQuery('#wpvivid_import_list').html(jsonarray.html);
                                jQuery('#wpvivid_empty_import_folder').val('<?php esc_attr_e('Delete Exported Files In Folder', 'wpvivid-backuprestore'); ?> ('+jsonarray.size+')');
                            }
                        }
                        catch(err) {
                            alert(err);
                        }
                    }, function (XMLHttpRequest, textStatus, errorThrown) {
                        var error_message = wpvivid_output_ajaxerror('scanning import folder', textStatus, errorThrown);
                        alert(error_message);
                    });
                }
            }

            function wpvivid_import_step2(data)
            {
                jQuery('#wpvivid_import_file_data').html('');
                var import_type = 'pages';
                jQuery.each(data, function (index, value)
                {
                    import_type = value['export_type'];
                    import_file_name=value['file_name'];
                    var list = "";
                    var myDate = new Date(value['time']*1000);
                    list += "<li>File name: " + value['file_name'] + "</li>";
                    list += "<li>Post type: " + value['export_type'] + "</li>";
                    list += "<li>Posts: " + value['posts_count'] + "</li>";
                    list += "<li>Media files size: " + value['media_size'] + "</li>";
                    list += "<li>Export time: " + myDate.toLocaleString('en-us') + "</li>";
                    jQuery("#wpvivid_import_file_data").append("<ul>"+ list +"</ul>");
                });

                jQuery('#wpvivid_import_type').html('Overwrite existing '+import_type+'s');

                jQuery('#wpvivid_import_step1').hide();
                jQuery('#wpvivid_import_step2').show();
                jQuery('#wpvivid_import_step3').hide();
            }

            function wpvivid_import_step3()
            {
                jQuery('#wpvivid_import_step1').hide();
                jQuery('#wpvivid_import_step2').hide();
                jQuery('#wpvivid_import_step3').show();
            }

            function wpvivid_return_import_page(){
                jQuery('#wpvivid_import_step1').show();
                jQuery('#wpvivid_import_step2').hide();
                jQuery('#wpvivid_import_step3').hide();
            }

            function wpvivid_monitor_import_task()
            {
                var ajax_data = {
                    'action': 'wpvivid_get_import_progress',
                };
                wpvivid_post_request(ajax_data, function(data)
                {
                    try
                    {
                        var jsonarray = jQuery.parseJSON(data);
                        if (typeof jsonarray === 'object')
                        {
                            if (jsonarray.result === 'success')
                            {
                                jQuery('#wpvivid_import_log').html("");
                                while (jsonarray.log.indexOf('\n') >= 0)
                                {
                                    var iLength = jsonarray.log.indexOf('\n');
                                    var log = jsonarray.log.substring(0, iLength);
                                    jsonarray.log = jsonarray.log.substring(iLength + 1);
                                    var insert_log = "<div style=\"clear:both;\">" + log + "</div>";
                                    jQuery('#wpvivid_import_log').append(insert_log);
                                    var div = jQuery('#wpvivid_import_log');
                                    div[0].scrollTop = div[0].scrollHeight;
                                }
                                if (jsonarray.status === 'wait')
                                {
                                    setTimeout(function () {
                                        wpvivid_monitor_import_task();
                                    }, 1000);
                                }
                                else if (jsonarray.status === 'completed')
                                {
                                    var insert_log = "<div style=\"clear:both;\"><a style='cursor: pointer;' onclick='wpvivid_return_import_page();'>Return import page</a></div>";
                                    jQuery('#wpvivid_import_log').append(insert_log);
                                    var div = jQuery('#wpvivid_import_log');
                                    div[0].scrollTop = div[0].scrollHeight;
                                    setTimeout(function () {
                                        alert("<?php esc_html_e('Import completed successfully.', 'wpvivid-backuprestore'); ?>");
                                    }, 1000);
                                    wpvivid_import_lock_unlock('unlock');
                                }
                                else if (jsonarray.status === 'error')
                                {
                                    alert("<?php esc_html_e('Import failed.', 'wpvivid-backuprestore'); ?>");
                                    wpvivid_import_lock_unlock('unlock');
                                }
                                else
                                {
                                    setTimeout(function ()
                                    {
                                        wpvivid_monitor_import_task();
                                    }, 1000);
                                }
                            }
                            else {
                                setTimeout(function () {
                                    wpvivid_monitor_import_task();
                                }, 1000);
                            }
                        }
                        else{
                            setTimeout(function () {
                                wpvivid_monitor_import_task();
                            }, 1000);
                        }
                    }
                    catch (err) {
                        setTimeout(function () {
                            wpvivid_monitor_import_task();
                        }, 1000);
                    }
                },function(XMLHttpRequest, textStatus, errorThrown) {
                    setTimeout(function () {
                        wpvivid_monitor_import_task();
                    }, 1000);
                });
            }

            jQuery('#wpvivid_start_import').click(function()
            {
                if(import_file_name!=='')
                {
                    var descript = '';
                    var user=jQuery('select[name="user_map"]').val();
                    if(user !== '0'){
                        wpvivid_start_import(import_file_name, user);
                    }
                    else{
                        alert('<?php esc_html_e('Please select an existing author to start importing.', 'wpvivid-backuprestore'); ?>');
                    }
                }
            });

            jQuery('#wpvivid_rechoose_import_file').click(function(){
                jQuery('#wpvivid_import_step1').show();
                jQuery('#wpvivid_import_step2').hide();
                jQuery('#wpvivid_import_step3').hide();
            });

            function wpvivid_start_import (file_name, user)
            {
                if(jQuery('#wpvivid_overwrite_existing').prop('checked')){
                    var overwrite_existing = 1;
                }
                else{
                    var overwrite_existing = 0;
                }

                wpvivid_import_lock_unlock('lock');
                wpvivid_monitor_import_task();
                wpvivid_import_step3();
                var ajax_data = {
                    'action':'wpvivid_start_import',
                    'file_name':file_name,
                    'user':user,
                    'update_exist':overwrite_existing
                };

                wpvivid_post_request(ajax_data, function(data)
                {
                }, function(XMLHttpRequest, textStatus, errorThrown)
                {
                });
            }

            var wpvivid_scan_data={};

            function wpvivid_refresh_import_list()
            {
                var ajax_data = {
                    'action': 'wpvivid_scan_import_folder'
                };
                wpvivid_post_request(ajax_data, function (data)
                {
                    try {
                        var jsonarray = jQuery.parseJSON(data);
                        if(jsonarray.html !== false)
                        {
                            wpvivid_scan_data=jsonarray.data;
                            jQuery('#wpvivid_import_list').show();
                            jQuery('#wpvivid_import_list').html(jsonarray.html);
                        }
                    }
                    catch(err) {
                        alert(err);
                    }
                }, function (XMLHttpRequest, textStatus, errorThrown)
                {
                    var error_message = wpvivid_output_ajaxerror('scanning import folder', textStatus, errorThrown);
                    alert(error_message);
                });
            }

            jQuery('#wpvivid_import_list').on("click",'.first-page',function()
            {
                wpvivid_change_import_page('first');
            });

            jQuery('#wpvivid_import_list').on("click",'.prev-page',function()
            {
                var page=parseInt(jQuery(this).attr('value'));
                wpvivid_change_import_page(page-1);
            });

            jQuery('#wpvivid_import_list').on("click",'.next-page',function()
            {
                var page=parseInt(jQuery(this).attr('value'));
                wpvivid_change_import_page(page+1);
            });

            jQuery('#wpvivid_import_list').on("click",'.last-page',function()
            {
                wpvivid_change_import_page('last');
            });

            jQuery('#wpvivid_import_list').on("keypress", '.current-page', function(){
                if(event.keyCode === 13){
                    var page = jQuery(this).val();
                    wpvivid_change_import_page(page);
                }
            });

            function wpvivid_change_import_page(page)
            {
                var post_ids = {};

                jQuery('input[name="export[]"]').each(function (i)
                {
                    var id=jQuery(this).val();
                    if(jQuery(this).prop('checked'))
                    {
                        post_ids[id]=1;
                    }
                    else
                    {
                        post_ids[id]=0;
                    }
                });

                var ajax_data = {
                    'action':'wpvivid_get_import_list_page',
                    'page': page,
                    'post_ids':post_ids
                };

                wpvivid_post_request(ajax_data, function(data)
                {
                    var jsonarray = jQuery.parseJSON(data);
                    if (jsonarray.result === 'success')
                    {
                        jQuery('#wpvivid_import_list').html(jsonarray.rows);
                    }
                    else if (jsonarray.result === 'failed')
                    {
                        alert(jsonarray.error);
                    }
                }, function(XMLHttpRequest, textStatus, errorThrown)
                {
                    var error_message = wpvivid_output_ajaxerror('export the previously-exported settings', textStatus, errorThrown);
                    alert(error_message);
                });
            }

            jQuery('#wpvivid_import_list').on("click",".wpvivid-export-list-item td",function()
            {
                var id = jQuery(this).parent().attr('id');

                if(jQuery(this).find('div.export-list-import').length !== 0)
                {
                    var data={};
                    data[id]=wpvivid_scan_data[id];
                    console.log(data[id]);
                    wpvivid_import_step2(data);
                    jQuery('#wpvivid_import_list').hide();
                }
            });

            function wpvivid_calc_import_folder_size(){
                var ajax_data = {
                    'action': 'wpvivid_calc_import_folder_size'
                };
                wpvivid_post_request(ajax_data, function(data)
                {
                    var jsonarray = jQuery.parseJSON(data);
                    if (jsonarray.result === 'success')
                    {
                        jQuery('#wpvivid_empty_import_folder').val('<?php esc_attr_e('Delete Exported Files In Folder', 'wpvivid-backuprestore'); ?> ('+jsonarray.size+')');
                    }
                }, function(XMLHttpRequest, textStatus, errorThrown)
                {
                    var error_message = wpvivid_output_ajaxerror('calc import folder size', textStatus, errorThrown);
                    alert(error_message);
                });
            }

            jQuery(document).ready(function (){
                wpvivid_calc_import_folder_size();
            });
        </script>
        <?php
    }

    public function wpvivid_check_import_file_name($file_name){
        if(preg_match('/wpvivid-.*_.*_export_.*\.zip$/', $file_name))
        {
            if(preg_match('/wpvivid-(.*?)_/',$file_name,$matches))
            {
                $id= $matches[0];
                $id=substr($id,0,strlen($id)-1);
                $ret['result']=WPVIVID_SUCCESS;
                $ret['id']=$id;
            }
            else
            {
                $ret['result']=WPVIVID_FAILED;
                $ret['error']=$file_name.' is not the file exported by WPvivid backup plugin.';
            }
        }
        else
        {
            $ret['result']=WPVIVID_FAILED;
            $ret['error']=$file_name.' is not the file exported by WPvivid backup plugin.';
        }
        return $ret;
    }

    public function check_import_file()
    {
        check_ajax_referer( 'wpvivid_ajax', 'nonce' );
        $check=current_user_can('manage_options');
        $check=apply_filters('wpvivid_ajax_check_security',$check);
        if(!$check)
        {
            die();
        }
        $file_name = sanitize_text_field($_POST['file_name']);
        if(isset($file_name))
        {
            $ret = $this->wpvivid_check_import_file_name($file_name);
        }
        else
        {
            $ret['result']=WPVIVID_FAILED;
            $ret['error']='Failed to post file name.';
        }

        echo wp_json_encode($ret);
        die();
    }

    public function upload_import_dir($uploads)
    {
        $uploads['path'] = WP_CONTENT_DIR.DIRECTORY_SEPARATOR.WPvivid_Setting::get_backupdir().DIRECTORY_SEPARATOR.WPVIVID_IMPORT_EXPORT_DIR;
        return $uploads;
    }

    public function upload_import_files()
    {
        check_ajax_referer( 'wpvivid_ajax', 'nonce' );
        $check=current_user_can('manage_options');
        $check=apply_filters('wpvivid_ajax_check_security',$check);
        if(!$check)
        {
            die();
        }
        $options['test_form'] =true;
        $options['action'] ='wpvivid_upload_import_files';
        $options['test_type'] = false;
        $options['ext'] = 'zip';
        $options['type'] = 'application/zip';
        add_filter('upload_dir', array($this, 'upload_import_dir'));

        $status = wp_handle_upload($_FILES['async-upload'],$options);

        remove_filter('upload_dir', array($this, 'upload_import_dir'));
        if (isset($status['error']))
        {
            echo wp_json_encode(array('result'=>WPVIVID_FAILED, 'error' => $status['error']));
            exit;
        }

        $file_name=basename(sanitize_text_field($_POST['name']));

        if (isset($_POST['chunks']) && isset($_POST['chunk']))
        {
            $chunks=sanitize_key($_POST['chunks']);
            $chunk=sanitize_key($_POST['chunk']);
            $path=WP_CONTENT_DIR.DIRECTORY_SEPARATOR.WPvivid_Setting::get_backupdir().DIRECTORY_SEPARATOR.WPVIVID_IMPORT_EXPORT_DIR.DIRECTORY_SEPARATOR;
            rename($status['file'],$path.$file_name.'_'.$chunk.'.tmp');
            $status['file'] = $path.$file_name.'_'.$chunk.'.tmp';
            if($chunk == $chunks-1)
            {
                $file_handle = fopen($path.$file_name, 'wb');
                if ($file_handle)
                {
                    for ($i=0; $i<$chunks; $i++)
                    {
                        $chunks_handle=fopen($path.$file_name.'_'.$i.'.tmp','rb');
                        if($chunks_handle)
                        {
                            while ($line = fread($chunks_handle, 1048576*2))
                            {
                                fwrite($file_handle, $line);
                            }
                            fclose($chunks_handle);
                            @wp_delete_file($path.$file_name.'_'.$i.'.tmp');
                        }
                    }
                    fclose($file_handle);
                }
            }
        }
        echo wp_json_encode(array('result'=>WPVIVID_SUCCESS));
        die();
    }

    public function get_import_progress()
    {
        try
        {
            check_ajax_referer( 'wpvivid_ajax', 'nonce' );
            $check=current_user_can('manage_options');
            $check=apply_filters('wpvivid_ajax_check_security',$check);
            if(!$check)
            {
                die();
            }

            $tasks=WPvivid_Impoter_taskmanager::get_tasks();
            foreach ($tasks as $task)
            {
                WPvivid_Impoter_taskmanager::get_task($task['id']);
                $import_log = new WPvivid_import_data();
                $ret['result'] = 'success';
                $ret['status'] = WPvivid_Impoter_taskmanager::get_import_task_status($task['id']);
                if ($ret['status'] === 'error')
                {
                    WPvivid_Impoter_taskmanager::delete_task($task['id']);
                }
                if($ret['status'] === 'completed')
                {
                    WPvivid_Impoter_taskmanager::delete_task($task['id']);
                }
                $ret['log'] = $import_log->get_log_content();
                echo wp_json_encode($ret);
                die();
            }
            $ret['result'] = 'success';
            $ret['status'] ='wait';
            $ret['log']='';
            echo wp_json_encode($ret);
            die();
        }
        catch (Exception $error) {
            $message = 'An exception has occurred. class: '.get_class($error).';msg: '.$error->getMessage().';code: '.$error->getCode().';line: '.$error->getLine().';in_file: '.$error->getFile().';';
            error_log($message);
            echo wp_json_encode(array('result'=>'failed','error'=>$message));
            die();
        }
    }

    public function upload_import_file_complete()
    {
        check_ajax_referer( 'wpvivid_ajax', 'nonce' );
        $check=current_user_can('manage_options');
        $check=apply_filters('wpvivid_ajax_check_security',$check);
        if(!$check)
        {
            die();
        }

        $ret['html']=false;
        if(isset($_POST['files']))
        {
            $files =sanitize_text_field($_POST['files']);
            $files =stripslashes($files);
            $files =json_decode($files,true);
            if(is_null($files))
            {
                $ret['result']=WPVIVID_FAILED;
                $ret['error']= 'Failed to decode files.';
                echo wp_json_encode($ret);
                die();
            }

            $path=WP_CONTENT_DIR.DIRECTORY_SEPARATOR.WPvivid_Setting::get_backupdir().DIRECTORY_SEPARATOR.WPVIVID_IMPORT_EXPORT_DIR.DIRECTORY_SEPARATOR;

            //if(preg_match('/wpvivid-.*_.*_to_.*\.zip$/',$files[0]['name']))
            //{
                $data=array();
                $check_result=true;
                foreach ($files as $file)
                {
                    $res=$this->check_is_import_file($path.$file['name']);
                    if($res['result'] =='success')
                    {
                        $add_file['file_name']=$file['name'];
                        $add_file['size']=filesize($path.$file['name']);
                        $add_file['export_type']=$res['export_type'];
                        $add_file['export_comment']=$res['export_comment'];
                        $add_file['posts_count']=$res['posts_count'];
                        $add_file['media_size']=size_format($res['media_size'],2);
                        $add_file['time']=$res['time'];
                        $data[]=$add_file;
                    }
                    else
                    {
                        $check_result=false;
                    }
                }

                if($check_result === true)
                {
                    $ret['result']=WPVIVID_SUCCESS;
                    $ret['data']=$data;
                }
                else
                {
                    $ret['result']=WPVIVID_FAILED;
                    $ret['error']='Upload file failed.';
                    foreach ($files as $file)
                    {
                        $this->clean_tmp_files($path, $file['name']);
                        @wp_delete_file($path . $file['name']);
                    }
                }
            /*}
            else
            {
                $ret['result']=WPVIVID_FAILED;
                $ret['error']='The file is not created by WPvivid backup plugin.';
            }*/
        }
        else {
            $ret['result']=WPVIVID_FAILED;
            $ret['error']='Failed to post file name.';
        }
        echo wp_json_encode($ret);
        die();
    }

    public function check_is_import_file($file_name)
    {
        $ret=$this->get_backup_file_info($file_name);
        if($ret['result'] === WPVIVID_SUCCESS)
        {
            $export_type_support_array = array('post', 'page');
            if(isset($ret['json_data']['post_type']) && in_array($ret['json_data']['post_type'], $export_type_support_array))
            {
                $ret['export_type']=$ret['json_data']['post_type'];
                $ret['export_comment']=isset($ret['json_data']['post_comment']) ? $ret['json_data']['post_comment'] : 'N/A';
                $ret['export_time']=isset($ret['json_data']['create_time']) ? $ret['json_data']['create_time'] : '';
                $ret['posts_count']=isset($ret['json_data']['posts_count']) ? $ret['json_data']['posts_count'] : 0;
                $ret['media_size']=isset($ret['json_data']['media_size']) ? $ret['json_data']['media_size'] : 0;
                $ret['time']=isset($ret['json_data']['create_time']) ? $ret['json_data']['create_time'] : time();
                return $ret;
            }
            else{
                $ret['result'] = WPVIVID_FAILED;
                $ret['error'] = 'The backup is not an import file.';
                return $ret;
            }
        }
        else
        {
            return $ret;
        }
    }

    public function get_backup_file_info($file_name)
    {
        if(!class_exists('WPvivid_ZipClass'))
            include_once WPVIVID_PLUGIN_DIR . '/includes/class-wpvivid-zipclass.php';
        $zip=new WPvivid_ZipClass();
        $ret=$zip->get_json_data($file_name, 'export');
        if($ret['result'] === WPVIVID_SUCCESS)
        {
            $json=$ret['json_data'];
            $json = json_decode($json, 1);
            if (is_null($json))
            {
                return array('result'=>WPVIVID_FAILED,'error'=>'Failed to decode json');
            } else {
                return array('result'=>WPVIVID_SUCCESS,'json_data'=>$json);
            }
        }
        else {
            return $ret;
        }
    }

    function clean_tmp_files($path, $filename){
        $handler=opendir($path);
        if($handler===false)
            return;
        while(($file=readdir($handler))!==false) {
            if (!is_dir($path.$file) && preg_match('/wpvivid-.*_.*_.*\.tmp$/', $file)) {
                $iPos = strrpos($file, '_');
                $file_temp = substr($file, 0, $iPos);
                if($file_temp === $filename) {
                    @wp_delete_file($path.$file);
                }
            }
        }
        @closedir($handler);
    }

    function wpvivid_write_upload_log($message, $id = ''){
        if($id === ''){
            $id=uniqid('wpvivid-');
        }
        global $wpvivid_plugin;
        $wpvivid_plugin->upload_log=new WPvivid_Log();
        $wpvivid_plugin->upload_log->CreateLogFile($id.'_upload','no_folder','upload');
        $wpvivid_plugin->upload_log->WriteLogHander();
        $wpvivid_plugin->upload_log->WriteLog($message,'notice');
    }

    public function wpvivid_check_is_import_file_ex($file_name, &$backup_id){
        if(preg_match('/wpvivid-.*_.*_to_.*\.zip$/', $file_name))
        {
            if(preg_match('/wpvivid-(.*?)_/', $file_name, $matches))
            {
                $id= $matches[0];
                $id=substr($id,0,strlen($id)-1);
                $backup_id=$id;
                return true;
            }
            else
            {
                return false;
            }
        }
        else
        {
            return false;
        }
    }

    public function calc_import_folder_size()
    {
        global $wpvivid_plugin;
        check_ajax_referer( 'wpvivid_ajax', 'nonce' );
        $check=current_user_can('manage_options');
        $check=apply_filters('wpvivid_ajax_check_security',$check);
        if(!$check)
        {
            die();
        }
        $path = WP_CONTENT_DIR.DIRECTORY_SEPARATOR.WPvivid_Setting::get_backupdir().DIRECTORY_SEPARATOR.WPVIVID_IMPORT_EXPORT_DIR.DIRECTORY_SEPARATOR;
        $bytes_total = 0;
        $path = realpath($path);
        if($path!==false && $path!='' && file_exists($path))
        {
            foreach(new RecursiveIteratorIterator(new RecursiveDirectoryIterator($path, FilesystemIterator::SKIP_DOTS)) as $object){
                $bytes_total += $object->getSize();
            }
        }
        $ret['result'] = WPVIVID_SUCCESS;
        $ret['size']   = $wpvivid_plugin->formatBytes($bytes_total);
        echo wp_json_encode($ret);
        die();
    }

    public function clean_import_folder(){
        check_ajax_referer( 'wpvivid_ajax', 'nonce' );
        $check=current_user_can('manage_options');
        $check=apply_filters('wpvivid_ajax_check_security',$check);
        if(!$check)
        {
            die();
        }
        $path = WP_CONTENT_DIR.DIRECTORY_SEPARATOR.WPvivid_Setting::get_backupdir().DIRECTORY_SEPARATOR.WPVIVID_IMPORT_EXPORT_DIR.DIRECTORY_SEPARATOR;
        if(is_dir($path))
        {
            $handler = opendir($path);
            if($handler!==false)
            {
                while (($filename = readdir($handler)) !== false){
                    if ($filename != "." && $filename != ".."){
                        if (is_dir($path  . $filename)) {
                            continue;
                        }
                        else{
                            $res=$this->check_is_import_file($path.$filename);
                            if($res['result'] =='success'){
                                @wp_delete_file($path.$filename);
                            }
                        }
                    }
                }
            }
        }

        $data = array();
        WPvivid_Setting::update_option('wpvivid_import_list_cache', $data);
        $page=1;
        $display_list=new WPvivid_Export_List();
        $display_list->set_parent('wpvivid_import_list');
        $display_list->set_list($data, $page);
        $display_list->prepare_items();
        ob_start();
        $display_list->display();
        $html = ob_get_clean();
        $ret['html']=$html;
        $ret['data']=$data;
        $ret['result']=WPVIVID_SUCCESS;

        global $wpvivid_plugin;
        $bytes_total = 0;
        $path = realpath($path);
        if($path!==false && $path!='' && file_exists($path))
        {
            foreach(new RecursiveIteratorIterator(new RecursiveDirectoryIterator($path, FilesystemIterator::SKIP_DOTS)) as $object)
            {
                $bytes_total += $object->getSize();
            }
        }
        $ret['size'] = $wpvivid_plugin->formatBytes($bytes_total);

        echo wp_json_encode($ret);
        die();
    }

    public function wpvivid_scan_import_folder()
    {
        check_ajax_referer( 'wpvivid_ajax', 'nonce' );
        $check=current_user_can('manage_options');
        $check=apply_filters('wpvivid_ajax_check_security',$check);
        if(!$check)
        {
            die();
        }

        $path=WP_CONTENT_DIR.DIRECTORY_SEPARATOR.WPvivid_Setting::get_backupdir().DIRECTORY_SEPARATOR.WPVIVID_IMPORT_EXPORT_DIR.DIRECTORY_SEPARATOR;

        $data=array();
        $count = 0;
        if(is_dir($path))
        {
            $handler = opendir($path);
            if($handler!==false)
            {
                while (($filename = readdir($handler)) !== false)
                {
                    if ($filename != "." && $filename != "..")
                    {
                        $count++;

                        if (is_dir($path  . $filename))
                        {
                            continue;
                        }
                        else {

                            $res=$this->check_is_import_file($path.$filename);
                            if($res['result'] =='success')
                            {
                                $add_file['file_name']=$filename;
                                $add_file['size']=filesize($path.$filename);
                                $add_file['export_type']=$res['export_type'];
                                $add_file['export_comment']=$res['export_comment'];
                                $add_file['posts_count']=$res['posts_count'];
                                $add_file['media_size']=size_format($res['media_size'],2);
                                $add_file['time']=$res['time'];
                                $data[$this->get_file_id($filename)]=$add_file;
                            }
                        }
                    }
                }
                if($handler)
                    @closedir($handler);
            }
        }
        else{
            global $wpvivid_plugin;
            $wpvivid_plugin->wpvivid_log=new WPvivid_Log();
            $id=uniqid('wpvivid-');
            $wpvivid_plugin->wpvivid_log->CreateLogFile($id.'_scan','no_folder','scan');
            $wpvivid_plugin->wpvivid_log->WriteLogHander();
            $wpvivid_plugin->wpvivid_log->WriteLog('Failed to get local storage directory.','notice');
            $ret['result']=WPVIVID_FAILED;
            $ret['error']='Failed to get local storage directory.';
        }
        WPvivid_Setting::update_option('wpvivid_import_list_cache', $data);
        $page=1;
        $display_list=new WPvivid_Export_List();
        $display_list->set_parent('wpvivid_import_list');
        $display_list->set_list($data, $page);
        $display_list->prepare_items();
        ob_start();
        $display_list->display();
        $html = ob_get_clean();
        $ret['html']=$html;
        $ret['data']=$data;
        $ret['result']=WPVIVID_SUCCESS;
        echo wp_json_encode($ret);
        die();
    }

    public function get_import_list_page(){
        check_ajax_referer( 'wpvivid_ajax', 'nonce' );
        $check=current_user_can('manage_options');
        $check=apply_filters('wpvivid_ajax_check_security',$check);
        if(!$check)
        {
            die();
        }
        if(!isset($_POST['page']))
        {
            die();
        }
        $page=sanitize_key($_POST['page']);

        $backups = get_option('wpvivid_import_list_cache');

        $display_list=new WPvivid_Export_List();
        $display_list->set_parent('wpvivid_import_list');
        $display_list->set_list($backups, $page);
        $display_list->prepare_items();
        ob_start();
        $display_list->display();
        $html = ob_get_clean();

        $ret['result']='success';
        $ret['rows']=$html;
        echo wp_json_encode($ret);
        die();
    }

    public function deal_import_shutdown_error()
    {
        if($this->end_shutdown_function===false){
            $last_error = error_get_last();
            if (!empty($last_error) && !in_array($last_error['type'], array(E_NOTICE,E_WARNING,E_USER_NOTICE,E_USER_WARNING,E_DEPRECATED), true)) {
                $error = $last_error;
            } else {
                $error = false;
            }
            $ret['result'] = 'failed';
            if ($error === false) {
                $ret['error'] = 'unknown Error';
            } else {
                $ret['error'] = 'type: '. $error['type'] . ', ' . $error['message'] . ' file:' . $error['file'] . ' line:' . $error['line'];
                error_log($ret['error']);
            }
            $id = uniqid('wpvivid-');
            $log_file_name = $id . '_import';
            $log = new WPvivid_Log();
            $log->CreateLogFile($log_file_name, 'no_folder', 'import');
            $log->WriteLog($ret['error'], 'notice');
            $log->CloseFile();
            WPvivid_error_log::create_error_log($log->log_file);
            echo wp_json_encode($ret);
            die();
        }
    }

    private function flush($task_id)
    {
        $ret['result']='success';
        $ret['task_id']=$task_id;
        $json=wp_json_encode($ret);
        if(!headers_sent())
        {
            header('Content-Length: '.strlen($json));
            header('Connection: close');
            header('Content-Encoding: none');
        }


        if (session_id())
            session_write_close();
        echo wp_json_encode($ret);

        if(function_exists('fastcgi_finish_request'))
        {
            fastcgi_finish_request();
        }
        else
        {
            ob_flush();
            flush();
        }
    }

    public function start_import()
    {
        global $wpvivid_plugin;
        check_ajax_referer( 'wpvivid_ajax', 'nonce' );
        $check=current_user_can('manage_options');
        $check=apply_filters('wpvivid_ajax_check_security',$check);
        if(!$check)
        {
            die();
        }
        $this->end_shutdown_function = false;
        register_shutdown_function(array($this,'deal_import_shutdown_error'));
        try
        {
            $file_name=sanitize_text_field($_POST['file_name']);
            if (isset($file_name) && !empty($file_name) && is_string($file_name))
            {
                $files=array();
                $options=array();
                $files[]=$file_name;
                $options['user']=0;
                if(isset($_POST['user']))
                {
                    $options['user']=sanitize_text_field($_POST['user']);
                }
                $options['update_exist']=0;
                if(isset($_POST['update_exist']))
                {
                    $options['update_exist']=sanitize_text_field($_POST['update_exist']);
                }

                $task_id=$this->get_file_id($file_name);
                WPvivid_Impoter_taskmanager::new_task($task_id, $files,$options);
                $import_log = new WPvivid_import_data();
                $import_log->wpvivid_create_import_log();
                $import_log->wpvivid_write_import_log('Start importing', 'notice');
                $this->flush($task_id);
                WPvivid_Impoter_taskmanager::update_import_task_status($task_id, 'running', true);
                $importer = new WPvivid_media_importer();
                $ret = $importer->import($task_id);
                echo wp_json_encode($ret);
            }
        }
        catch (Exception $error)
        {
            $message = 'An error has occurred. class:'.get_class($error).';msg:'.$error->getMessage().';code:'.$error->getCode().';line:'.$error->getLine().';in_file:'.$error->getFile().';';
            error_log($message);
            WPvivid_Exporter_taskmanager::update_backup_task_status($task_id,false,'error',false,false,$message);
            $wpvivid_plugin->wpvivid_log->WriteLog($message,'error');
            $this->end_shutdown_function=true;
            die();
        }
        $this->end_shutdown_function=true;
        die();
    }

    public function get_file_id($file_name)
    {
        if(preg_match('/wpvivid-.*_.*_to_.*\.zip$/',$file_name))
        {
            if(preg_match('/wpvivid-(.*?)_/',$file_name,$matches))
            {
                $id= $matches[0];
                $id=substr($id,0,strlen($id)-1);
                return $id;
            }
            else
            {
                $id=uniqid('wpvivid-');
                return $id;
            }
        }
        else
        {
            $id=uniqid('wpvivid-');
            return $id;
        }
    }
}includes/new_backup/class-wpvivid-mysqldump2.php000064400000101330151327705670016122 0ustar00<?php

if (!defined('WPVIVID_PLUGIN_DIR'))
{
    die;
}

use Exception as Exception;

class CompressTest_2
{
    private $fileHandler = null;

    /**
     * @param string $filename
     */
    public function open($filename)
    {
        $this->fileHandler = fopen($filename, "wb");
        if (false === $this->fileHandler) {
            throw new Exception("Output file is not writable");
        }

        return true;
    }

    public function write($str)
    {
        if (false === ($bytesWritten = fwrite($this->fileHandler, $str))) {
            throw new Exception("Writting to file failed! Probably, there is no more free space left?");
        }
        return $bytesWritten;
    }

    public function close()
    {
        return fclose($this->fileHandler);
    }

    public function get_size()
    {
        $fstat = fstat($this->fileHandler);
        return $fstat['size'];
    }
}

class WPvivid_Mysqldump2
{

    // Same as mysqldump
    const MAXLINESIZE = 1000000;

    // Available compression methods as constants
    const GZIP = 'Gzip';
    const BZIP2 = 'Bzip2';
    const NONE = 'None';

    // Available connection strings
    const UTF8 = 'utf8';
    const UTF8MB4 = 'utf8mb4';

    /**
     * Database username
     * @var string
     */
    public $user;
    /**
     * Database password
     * @var string
     */
    public $pass;
    /**
     * Destination filename, defaults to stdout
     * @var string
     */
    public $fileName = 'php://output';

    // Internal stuff
    private $tables = array();
    //private $dbHandler = null;
    private $dbType;
    private $compressManager;
    private $typeAdapter;
    private $dumpSettings = array();
    private $version;
    private $tableColumnTypes = array();
    public $log=false;
    public $task_id='';
    /**
     * database name, parsed from dsn
     * @var string
     */
    private $dbName;
    /**
     * host name, parsed from dsn
     * @var string
     */
    private $host;

    public $last_query_string='';

    public $task = false;
    public $file_index=1;
    public $tmp_file_name='';
    public $current_size=0;
    public $files=array();
    public $backup_tables;
    public $find_zero_date=false;

    public function __construct($task,$dump_setting)
    {
        if(is_a($task, 'WPvivid_Backup_Task_2'))
        {
            $this->task=$task;
        }
        else
        {
            throw new Exception('not as wpvivid task type');
        }

        $dumpSettingsDefault = array(
            'include-tables' => array(),
            'exclude-tables' => array(),
            'compress' => WPvivid_Mysqldump2::NONE,
            'init_commands' => array(),
            'no-data' => array(),
            'reset-auto-increment' => false,
            'add-drop-database' => false,
            'add-drop-table' => true,
            'add-drop-trigger' => true,
            'add-locks' => true,
            'complete-insert' => false,
            'default-character-set' => WPvivid_Mysqldump2::UTF8,
            'disable-keys' => true,
            'extended-insert' => false,
            'events' => false,
            'hex-blob' => true, /* faster than escaped content */
            'net_buffer_length' => self::MAXLINESIZE,
            'no-autocommit' => false,
            'no-create-info' => false,
            'lock-tables' => false,
            'routines' => false,
            'single-transaction' => true,
            'skip-triggers' => false,
            'skip-tz-utc' => false,
            'skip-comments' => false,
            'skip-dump-date' => false,
            'where' => '',

        );

        if(defined('DB_CHARSET'))
        {
            $dumpSettingsDefault['default-character-set']=DB_CHARSET;
        }

        $this->dumpSettings = $this->array_replace_recursive($dumpSettingsDefault, $dump_setting);

        $this->dumpSettings['init_commands'][] = "SET NAMES " . WPvivid_Mysqldump2::UTF8MB4;

        if (false === $this->dumpSettings['skip-tz-utc'])
        {
            $this->dumpSettings['init_commands'][] = "SET TIME_ZONE='+00:00'";
        }

        // Create a new compressManager to manage compressed output
        $this->compressManager = new CompressTest_2();
        $this->backup_tables=0;
    }

    public function connect()
    {
        $dbType=$this->dumpSettings['db_connect_method'];
        $host=$this->dumpSettings['host'];
        $user=$this->dumpSettings['user'];
        $pass=$this->dumpSettings['pass'];
        $database=$this->dumpSettings['database'];

        $this->typeAdapter = WPvividTypeAdapterFactory::create($dbType, null);
        $this->typeAdapter->connect($host,$database,$user,$pass,$this->dumpSettings['init_commands']);

    }

    public function write_header()
    {
        // Write some basic info to output file
        $upload_dir  = wp_upload_dir();

        $site_url=$this->dumpSettings['site_url'];
        $home_url=$this->dumpSettings['home_url'];
        $content_url=$this->dumpSettings['content_url'];
        $upload_url=$upload_dir['baseurl'];

        $this->compressManager->write('/* # site_url: '.$site_url.' */;'.PHP_EOL);
        $this->compressManager->write('/* # home_url: '.$home_url.' */;'.PHP_EOL);
        $this->compressManager->write('/* # content_url: '.$content_url.' */;'.PHP_EOL);
        $this->compressManager->write('/* # upload_url: '.$upload_url.' */;'.PHP_EOL);
        if(isset($this->dumpSettings['prefix']))
        {
            $table_prefix=$this->dumpSettings['prefix'];
            $this->compressManager->write('/* # table_prefix: '.$table_prefix.' */;'.PHP_EOL.PHP_EOL.PHP_EOL);
        }


        // Store server settings and use sanner defaults to dump
        $this->compressManager->write(
            $this->typeAdapter->backup_parameters($this->dumpSettings)
        );
    }

    public function write_footer()
    {
        // Restore saved parameters
        $this->compressManager->write(
            $this->typeAdapter->restore_parameters($this->dumpSettings)
        );
    }

    public function init_job()
    {
        $tables=$this->list_tables();

        if(empty($tables))
        {
            return false;
        }

        usort($tables, function ($a, $b)
        {
            if ($a['size'] == $b['size'])
                return 0;

            if ($a['size'] > $b['size'])
                return 1;
            else
                return -1;
        });

        $jobs=array();

        foreach ($tables as $table)
        {
            $jobs[$table['name']]['index']=0;
            $jobs[$table['name']]['finished']=0;
            $jobs[$table['name']]['created']=0;
            $jobs[$table['name']]['name']=$table['name'];
            $jobs[$table['name']]['size']=$table['size'];
            $jobs[$table['name']]['rows']=$table['rows'];
        }

        $this->task->update_current_sub_job($jobs);
        return $jobs;
    }

    public function start_jobs()
    {
        $this->tables= $this->task->get_current_sub_job();
        return $this->exportTables();
    }

    public function list_tables()
    {
        $tables=array();
        $views=array();

        global $wpdb;
        $resultSet=$wpdb->get_results('SHOW TABLE STATUS', ARRAY_A);

        $resultViews=$wpdb->get_results('SHOW FULL TABLES WHERE table_type = \'VIEW\'', ARRAY_A);
        if(!is_null($resultViews))
        {
            foreach ($resultViews as $view)
            {
                $name = 'Tables_in_'.DB_NAME;
                $views[] = $view[$name];
            }
        }

        if (is_null($resultSet))
        {
           return $tables;
        }

        if(isset($this->dumpSettings['prefix'])&&!empty($this->dumpSettings['prefix']))
        {
            $exclude = array('/^(?!' . $this->dumpSettings['prefix'] . ')/i');
        }
        else
        {
            $exclude=array();
        }
        foreach ($resultSet as $row)
        {
            if(isset($row['Comment']) && $row['Comment'] === 'VIEW')
            {
                continue;
            }

            if ( $this->matches($row['Name'], $this->dumpSettings['include-tables']) )
            {
                $table['name']=$row['Name'];
                $table['size']= ($row["Data_length"] + $row["Index_length"]);
                $table['rows']=$row['Rows'];
                $tables[]=$table;
                continue;
            }

            if(!empty($exclude))
            {
                if ( $this->matches($row['Name'], $exclude) )
                {
                    continue;
                }
            }

            if ( $this->matches($row['Name'], $this->dumpSettings['exclude-tables']) )
            {
                continue;
            }

            if(!empty($views))
            {
                if ( $this->matches($row['Name'], $views) )
                {
                    continue;
                }
            }

            $table['name']=$row['Name'];
            $table['size']= ($row["Data_length"] + $row["Index_length"]);
            $table['rows']=$row['Rows'];
            $tables[]=$table;
        }

        return $tables;
    }
    /**
     * Compare if $table name matches with a definition inside $arr
     * @param $table string
     * @param $arr array with strings or patterns
     * @return bool
     */
    private function matches($table, $arr) {
        $match = false;

        if(empty($arr))
        {
            return false;
        }

        foreach ($arr as $pattern) {
            if ( '/' != $pattern[0] ) {
                continue;
            }
            if ( 1 == preg_match($pattern, $table) ) {
                $match = true;
            }
        }

        return in_array($table, $arr) || $match;
    }

    public function check_tmp_file()
    {
        $max_file_size=$this->dumpSettings['max_file_size'];
        $max_backup_tables=5000;
        if($max_file_size==0)
            return;
        $this->current_size=$this->compressManager->get_size();
        $path=$this->dumpSettings['path'];

        if( $this->current_size>$max_file_size||$this->backup_tables>=$max_backup_tables)
        {
            $this->current_size=0;
            $this->backup_tables=0;
            $this->close_tmp_file();
            $name_file_name=$this->dumpSettings['file_prefix'].'.part'.sprintf('%03d',($this->file_index)).'.sql';
            $this->file_index++;
            rename($this->tmp_file_name,$path.DIRECTORY_SEPARATOR.$name_file_name);
            $this->task->update_current_sub_job($this->tables);
            $this->task->add_mysql_dump_files($name_file_name);
            $this->open_tmp_file();
        }
    }

    public function open_tmp_file($b_delete=false)
    {
        if($b_delete)
            @wp_delete_file( $this->tmp_file_name);
        $this->compressManager->open($this->tmp_file_name);
    }

    public function close_tmp_file()
    {
        $this->compressManager->close();
    }

    private function exportTables()
    {
        global $wpvivid_plugin;
        //tmp_file_name
        $path=$this->dumpSettings['path'];
        $this->tmp_file_name=$path.DIRECTORY_SEPARATOR.$this->dumpSettings['file_prefix'].'_tmp.sql';

        $this->open_tmp_file();
        $this->write_header();
        /*
        if(file_exists($this->tmp_file_name))
        {
            $this->open_tmp_file();
        }
        else
        {
            $this->open_tmp_file();
            $this->write_header();
        }*/

        // Exporting tables one by one
        $this->file_index=$this->task->get_current_mysql_file_index();
        $this->current_size=0;
        $tables=$this->tables;
        $i=0;
        $i_step=0;
        $this->backup_tables=0;
        if($this->task->task_id!=='')
        {
            $size = $this->task->get_backup_jobs();
            if(sizeof($size) > 0)
            {
                $i_step = intval(1 / (sizeof($size)) * 100);
            }
        }
        foreach ($tables as $name=>$table)
        {
            if($this->task->check_cancel_backup())
            {
                die();
            }

            if($table['finished']==1)
            {
                continue;
            }
            $index=$table['index'];
            $table_name=$table['name'];

            $message='Preparing to dump table '.$table_name;
            $wpvivid_plugin->wpvivid_log->WriteLog($message,'notice');

            $this->task->update_sub_task_progress($message);

            if($table['created']==0)
            {
                $this->getTableStructure($table_name);
                $this->tables[$name]['created']=1;
                //$this->task->update_current_sub_job($this->tables);
            }

            $this->tableColumnTypes[$table_name] = $this->getTableColumnTypes($table_name);
            if($this->tableColumnTypes[$table_name]===false)
            {
                continue;
            }

            $this->listValues($table_name,$index);
            $this->check_tmp_file();

            $this->tables[$name]['finished']=1;
            $this->backup_tables++;
            //$this->task->update_current_sub_job($this->tables);
            $i++;
            if($this->task->task_id!=='')
            {
                $i_progress=intval($i/sizeof($this->tables)*$i_step);
                $this->task->update_database_progress($i_progress);
            }
        }

        $this->current_size=$this->compressManager->get_size();
        if($this->current_size>0)
        {
            $this->close_tmp_file();
            $name_file_name=$this->dumpSettings['file_prefix'].'.part'.sprintf('%03d',($this->file_index)).'.sql';
            $this->file_index++;
            rename($this->tmp_file_name,$path.DIRECTORY_SEPARATOR.$name_file_name);
            $this->task->add_mysql_dump_files($name_file_name);
        }
        else
        {
            $this->close_tmp_file();
        }

        $ret['result']='success';
        return $ret;
    }
    /**
     * Table structure extractor
     *
     * @param string $tableName  Name of table to export
     * @return null
     */
    private function getTableStructure($tableName)
    {
        if (!$this->dumpSettings['no-create-info']) {
            $ret = '';
            if (!$this->dumpSettings['skip-comments']) {
                $ret = "--" . PHP_EOL .
                    "-- Table structure for table `$tableName`" . PHP_EOL .
                    "--" . PHP_EOL . PHP_EOL;
            }
            $stmt = $this->typeAdapter->show_create_table($tableName);

            foreach ($this->query($stmt) as $r)
            {
                $this->compressManager->write($ret);
                if ($this->dumpSettings['add-drop-table']) {
                    $this->compressManager->write(
                        $this->typeAdapter->drop_table($tableName)
                    );
                }

                $this->compressManager->write(
                    $this->typeAdapter->create_table($r, $this->dumpSettings)
                );
                break;
            }
        }

        return;
    }

    /**
     * Store column types to create data dumps and for Stand-In tables
     *
     * @param string $tableName  Name of table to export
     * @return array type column types detailed
     */

    private function getTableColumnTypes($tableName) {
        $columnTypes = array();
        $columns = $this->query(
            $this->typeAdapter->show_columns($tableName)
        );
        if($columns===false)
        {
            $error=$this->typeAdapter->errorInfo();
            if(isset($error[2])){
                $error = 'Error: '.$error[2];
            }
            else{
                $error = '';
            }
            $columns = $this->query(
                'DESCRIBE '.$tableName
            );
            if($columns===false)
            {
                $error=$this->typeAdapter->errorInfo();
                if(isset($error[2])){
                    $error = 'Error: '.$error[2];
                }
                else{
                    $error = '';
                }
                return false;
            }
        }

        foreach($columns as $key => $col) {
            $types = $this->typeAdapter->parseColumnType($col);
            $columnTypes[$col['Field']] = array(
                'is_numeric'=> $types['is_numeric'],
                'is_blob' => $types['is_blob'],
                'type' => $types['type'],
                'type_sql' => $col['Type'],
                'is_virtual' => $types['is_virtual']
            );
        }

        return $columnTypes;
    }

    /**
     * Escape values with quotes when needed
     *
     * @param string $tableName Name of table which contains rows
     * @param array $row Associative array of column names and values to be quoted
     *
     * @return array
     */
    private function escape($tableName, $row)
    {
        $ret = array();
        $columnTypes = $this->tableColumnTypes[$tableName];
        foreach ($row as $colName => $colValue) {
            if (is_null($colValue)) {
                $ret[] = "NULL";
            } elseif ($this->dumpSettings['hex-blob'] && $columnTypes[$colName]['is_blob']) {
                if ($columnTypes[$colName]['type'] == 'bit' || !empty($colValue)) {
                    $ret[] = "0x{$colValue}";
                } else {
                    $ret[] = "''";
                }
            } elseif ($columnTypes[$colName]['is_numeric']) {
                $ret[] = $colValue;
            } else {
                $ret[] = $this->typeAdapter->quote($colValue);
            }
        }
        return $ret;
    }


    private function listValues($tableName,$index)
    {
        global $wpvivid_plugin;
        $this->prepareListValues($tableName);

        $onlyOnce = true;
        $lineSize = 0;

        $colStmt = $this->getColumnStmt($tableName);

        global $wpdb;
        $prefix=$wpdb->base_prefix;
        $dbType=$this->dumpSettings['db_connect_method'];

        $start=$index;
        $limit_count=5000;

        //$sum =$wpdb->get_var("SELECT COUNT(1) FROM `{$tableName}`");
        $sum=0;
        $resultSet = $this->query("SELECT COUNT(1) FROM `{$tableName}`");
        foreach ($resultSet as $row)
        {
            $sum=$row['COUNT(1)'];
        }

        $this->typeAdapter->closeCursor($resultSet);

        if($dbType=='wpdb')
        {
            $b_options=false;
            if(substr($tableName, strlen($prefix))=='options')
            {
               $b_options=true;
            }

            $stmt = "SELECT " . implode(",", $colStmt) . " FROM `$tableName`";

            if ($this->dumpSettings['where']) {
                $stmt .= " WHERE {$this->dumpSettings['where']}";
            }

            $i=0;
            $i_check_cancel=0;
            $count=0;

            while($sum > $start)
            {
                $limit = " LIMIT {$limit_count} OFFSET {$start}";

                $query=$stmt.$limit;
                $resultSet = $this->query($query);

                if($resultSet===false)
                {
                    $error=$this->typeAdapter->errorInfo();
                    if(isset($error[2])){
                        $error = 'Error: '.$error[2];
                    }
                    else{
                        $error = '';
                    }
                    $this->endListValues($tableName);
                    return ;
                }

                foreach ($resultSet as $row)
                {
                    $i++;

                    $skip=false;

                    $vals = $this->escape($tableName, $row);

                    foreach($vals as $key => $value)
                    {
                        if($value === '\'0000-00-00 00:00:00\'')
                        {
                            //$vals[$key] = '\'1999-01-01 00:00:00\'';
                            $this->find_zero_date=true;
                        }

                        if($b_options)
                        {
                            if($value=="'wpvivid_task_list'")
                            {
                                $skip=true;
                            }
                        }
                    }

                    if($skip)
                        continue;
                    if ($onlyOnce || !$this->dumpSettings['extended-insert'])
                    {
                        if ($this->dumpSettings['complete-insert'])
                        {
                            $lineSize += $this->compressManager->write(
                                "INSERT INTO `$tableName` (" .
                                implode(", ", $colStmt) .
                                ") VALUES (" . implode(",", $vals) . ")"
                            );
                        } else {
                            $lineSize += $this->compressManager->write(
                                "INSERT INTO `$tableName` VALUES (" . implode(",", $vals) . ")"
                            );
                        }
                        $onlyOnce = false;
                    }
                    else {
                        $lineSize += $this->compressManager->write(",(" . implode(",", $vals) . ")");
                    }
                    if (($lineSize > $this->dumpSettings['net_buffer_length']) ||
                        !$this->dumpSettings['extended-insert']) {
                        $onlyOnce = true;
                        $lineSize = $this->compressManager->write(";" . PHP_EOL);
                    }

                    if($i>=200000)
                    {
                        $count+=$i;
                        $i=0;
                        if($this->task->task_id!=='')
                        {
                            $i_check_cancel++;
                            if($i_check_cancel>5)
                            {
                                $i_check_cancel=0;
                                $this->task->check_cancel_backup();
                            }
                            $message='Dumping table '.$tableName.', rows dumped: '.$count.' rows.';
                            $this->task->update_sub_task_progress($message);
                        }
                    }
                }

                $this->typeAdapter->closeCursor($resultSet);

                $start += $limit_count;
                $this->tables[$tableName]['index']=$start;
                //$this->task->update_current_sub_job($this->tables);
                $this->check_tmp_file();
            }

            if (!$onlyOnce) {
                $this->compressManager->write(";" . PHP_EOL);
            }

            $this->endListValues($tableName);
        }
        else
        {
            $b_options=false;
            if(substr($tableName, strlen($prefix))=='options')
            {
                $b_options=true;
            }

            $stmt = "SELECT " . implode(",", $colStmt) . " FROM `$tableName`";

            if ($this->dumpSettings['where']) {
                $stmt .= " WHERE {$this->dumpSettings['where']}";
            }

            $i=0;
            $i_check_cancel=0;
            $count=0;

            while($sum > $start)
            {
                $limit = " LIMIT {$limit_count} OFFSET {$start}";

                $query=$stmt.$limit;
                $resultSet = $this->query($query);

                if($resultSet===false)
                {
                    $error=$this->typeAdapter->errorInfo();
                    if(isset($error[2])){
                        $error = 'Error: '.$error[2];
                    }
                    else{
                        $error = '';
                    }
                    $this->endListValues($tableName);
                    return ;
                }

                foreach ($resultSet as $row)
                {
                    $skip=false;
                    $vals = $this->escape($tableName, $row);

                    foreach($vals as $key => $value)
                    {
                        if($value === '\'0000-00-00 00:00:00\'')
                        {
                            //$vals[$key] = '\'1999-01-01 00:00:00\'';
                            $this->find_zero_date=true;
                        }

                        if($b_options)
                        {
                            if($value=="'wpvivid_task_list'")
                            {
                                $skip=true;
                            }
                        }
                    }

                    if($skip)
                        continue;

                    if ($onlyOnce || !$this->dumpSettings['extended-insert'])
                    {
                        if ($this->dumpSettings['complete-insert'])
                        {
                            var_dump('test1');
                            $lineSize += $this->compressManager->write(
                                "INSERT INTO `$tableName` (" .
                                implode(", ", $colStmt) .
                                ") VALUES (" . implode(",", $vals) . ")"
                            );
                        } else {
                            $lineSize += $this->compressManager->write(
                                "INSERT INTO `$tableName` VALUES (" . implode(",", $vals) . ")"
                            );
                        }
                        $onlyOnce = false;
                    }
                    else {
                        $lineSize += $this->compressManager->write(",(" . implode(",", $vals) . ")");
                    }
                    if (($lineSize > $this->dumpSettings['net_buffer_length']) ||
                        !$this->dumpSettings['extended-insert']) {
                        $onlyOnce = true;
                        $lineSize = $this->compressManager->write(";" . PHP_EOL);
                    }

                    if($i>=200000)
                    {
                        $count+=$i;
                        $i=0;
                        if($this->task->task_id!=='')
                        {
                            $i_check_cancel++;
                            if($i_check_cancel>5)
                            {
                                $i_check_cancel=0;
                                $this->task->check_cancel_backup();
                            }
                            $message='Dumping table '.$tableName.', rows dumped: '.$count.' rows.';
                            $this->task->update_sub_task_progress($message);
                        }
                    }
                }

                $this->typeAdapter->closeCursor($resultSet);

                $start += $limit_count;
                $this->tables[$tableName]['index']=$start;
                //$this->task->update_current_sub_job($this->tables);
                $this->check_tmp_file();
            }

            if (!$onlyOnce) {
                $this->compressManager->write(";" . PHP_EOL);
            }

            $this->endListValues($tableName);
        }

        //$this->current_size+=$table['size'];
    }

    /**
     * Table rows extractor, append information prior to dump
     *
     * @param string $tableName  Name of table to export
     *
     * @return null
     */
    function prepareListValues($tableName)
    {
        if (!$this->dumpSettings['skip-comments']) {
            $this->compressManager->write(
                "--" . PHP_EOL .
                "-- Dumping data for table `$tableName`" .  PHP_EOL .
                "--" . PHP_EOL . PHP_EOL
            );
        }

        if ($this->dumpSettings['single-transaction']) {
            $this->exec($this->typeAdapter->setup_transaction());
            $this->exec($this->typeAdapter->start_transaction());
        }

        if ($this->dumpSettings['lock-tables'])
        {
            $this->typeAdapter->lock_table($tableName);

            //if($this -> privileges['LOCK TABLES'] == 0)
            //{
            //global $wpvivid_plugin;
            //    $wpvivid_plugin->wpvivid_log->WriteLog('The lack of LOCK TABLES privilege, the backup will skip lock_tables() to continue.','notice');
            //}else{
            //    $this->typeAdapter->lock_table($tableName);
            //}
        }

        if ($this->dumpSettings['add-locks']) {
            $this->compressManager->write(
                $this->typeAdapter->start_add_lock_table($tableName)
            );
        }

        if ($this->dumpSettings['disable-keys']) {
            $this->compressManager->write(
                $this->typeAdapter->start_add_disable_keys($tableName)
            );
        }

        // Disable autocommit for faster reload
        if ($this->dumpSettings['no-autocommit']) {
            $this->compressManager->write(
                $this->typeAdapter->start_disable_autocommit()
            );
        }

        return;
    }

    /**
     * Table rows extractor, close locks and commits after dump
     *
     * @param string $tableName  Name of table to export
     *
     * @return null
     */
    function endListValues($tableName)
    {
        if ($this->dumpSettings['disable-keys']) {
            $this->compressManager->write(
                $this->typeAdapter->end_add_disable_keys($tableName)
            );
        }

        if ($this->dumpSettings['add-locks']) {
            $this->compressManager->write(
                $this->typeAdapter->end_add_lock_table($tableName)
            );
        }

        if ($this->dumpSettings['single-transaction']) {
            $this->exec($this->typeAdapter->commit_transaction());
        }

        if ($this->dumpSettings['lock-tables']) {
            $this->typeAdapter->unlock_table($tableName);
        }

        // Commit to enable autocommit
        if ($this->dumpSettings['no-autocommit']) {
            $this->compressManager->write(
                $this->typeAdapter->end_disable_autocommit()
            );
        }

        $this->compressManager->write(PHP_EOL);

        return;
    }

    /**
     * Build SQL List of all columns on current table
     *
     * @param string $tableName  Name of table to get columns
     *
     * @return string SQL sentence with columns
     */
    function getColumnStmt($tableName)
    {
        $colStmt = array();
        foreach($this->tableColumnTypes[$tableName] as $colName => $colType) {
            if ($colType['type'] == 'bit' && $this->dumpSettings['hex-blob']) {
                $colStmt[] = "LPAD(HEX(`{$colName}`),2,'0') AS `{$colName}`";
            } else if ($colType['is_blob'] && $this->dumpSettings['hex-blob']) {
                $colStmt[] = "HEX(`{$colName}`) AS `{$colName}`";
            } else if ($colType['is_virtual']) {
                $this->dumpSettings['complete-insert'] = true;
                continue;
            } else {
                $colStmt[] = "`{$colName}`";
            }
        }

        return $colStmt;
    }

    /**
     * Custom array_replace_recursive to be used if PHP < 5.3
     * Replaces elements from passed arrays into the first array recursively
     *
     * @param array $array1 The array in which elements are replaced
     * @param array $array2 The array from which elements will be extracted
     *
     * @return array Returns an array, or NULL if an error occurs.
     */
    public function array_replace_recursive($array1, $array2)
    {
        if (function_exists('array_replace_recursive')) {
            return array_replace_recursive($array1, $array2);
        }

        foreach ($array2 as $key => $value) {
            if (is_array($value)) {
                $array1[$key] = $this->array_replace_recursive($array1[$key], $value);
            } else {
                $array1[$key] = $value;
            }
        }
        return $array1;
    }

    public function query($query_string)
    {
        $this->last_query_string=$query_string;
        return  $this->typeAdapter->query($query_string);
    }

    private function exec($query_string)
    {
        $this->last_query_string=$query_string;
        return  $this->typeAdapter->query($query_string);
    }

    public function is_has_zero_date()
    {
        if($this->find_zero_date)
        {
            return true;
        }
        else
        {
            return false;
        }
    }
}includes/new_backup/class-wpvivid-zip.php000064400000023735151327705670014623 0ustar00<?php

if (!defined('WPVIVID_PLUGIN_DIR'))
{
    die;
}

class WPvivid_Zip
{
    public $zip_object;

    public function __construct($zip_method='')
    {
        $this->check_available_zip_object($zip_method);
    }

    public function add_files($zip_file,$root_path,$files,$create=false,$json=false)
    {
        if($create)
        {
            if(file_exists($zip_file))
                @wp_delete_file($zip_file);
        }

        if($json!==false)
        {
            $this->add_json_file($zip_file,$json,$create);
        }

        if(file_exists($zip_file))
        {
            $this->zip_object->open($zip_file);
            clearstatcache();
        }
        else
        {
            $create_code = (version_compare(PHP_VERSION, '5.2.12', '>') && defined('ZIPARCHIVE::CREATE')) ? ZIPARCHIVE::CREATE : 1;
            $this->zip_object->open($zip_file, $create_code);
        }

        if(is_a($this->zip_object,'WPvivid_PclZip_2'))
            $this->zip_object->set_replace_path($root_path);

        foreach ($files as $file)
        {
            $new_file=str_replace($root_path,'',$file);
            if(file_exists($file))
            {
                $this->zip_object->addFile($file,$new_file);
            }
        }

        if($this->zip_object->close()===false)
        {
            $ret['result']='failed';
            $ret['error']='Failed to add zip files.';
            if(is_a($this->zip_object,'WPvivid_PclZip_2'))
            {
                $ret['error'].=' last error:'.$this->zip_object->last_error;
            }
            else if(is_a($this->zip_object,'ZipArchive'))
            {
                $ret['error'].=' status string:'.$this->zip_object->getStatusString();
            }
            return $ret;
        }

        $ret['result']='success';
        return $ret;
    }

    public function add_file($zip_file,$file,$add_as,$replace_path)
    {
        global $wpvivid_plugin;
        $wpvivid_plugin->wpvivid_log->WriteLog('Prepare to zip file. file: '.basename($file),'notice');

        if(file_exists($zip_file))
        {
            $this->zip_object->open($zip_file);
            clearstatcache();
        }
        else
        {
            $create_code = (version_compare(PHP_VERSION, '5.2.12', '>') && defined('ZIPARCHIVE::CREATE')) ? ZIPARCHIVE::CREATE : 1;
            $this->zip_object->open($zip_file, $create_code);
        }

        if(is_a($this->zip_object,'WPvivid_PclZip_2'))
            $this->zip_object->set_replace_path($replace_path);

        if($this->zip_object->addFile($file,$add_as)===false)
        {
            $ret['result']='failed';
            $ret['error']='Failed to add zip file '.$file;
            if(is_a($this->zip_object,'WPvivid_PclZip_2'))
            {
                $ret['error'].=' last error:'.$this->zip_object->last_error;
            }
            else if(is_a($this->zip_object,'ZipArchive'))
            {
                $ret['error'].=' status string:'.$this->zip_object->getStatusString();
            }
            return $ret;
        }

        if($this->zip_object->close()===false)
        {
            $ret['result']='failed';
            $ret['error']='Failed to add zip files.';
            if(is_a($this->zip_object,'WPvivid_PclZip_2'))
            {
                $ret['error'].=' last error:'.$this->zip_object->last_error;
            }
            else if(is_a($this->zip_object,'ZipArchive'))
            {
                $ret['error'].=' status string:'.$this->zip_object->getStatusString();
            }
            return $ret;
        }

        $ret['result']='success';
        $wpvivid_plugin->wpvivid_log->WriteLog('Adding zip files completed.'.basename($zip_file).', filesize: '.size_format(filesize($zip_file),2),'notice');

        return $ret;
    }

    public function add_json_file($zip_file,$json,$create=false)
    {
        if($create)
        {
            if(file_exists($zip_file))
                @wp_delete_file($zip_file);
        }
        $json['file']=basename($zip_file);
        $string=wp_json_encode($json);

        if(file_exists($zip_file))
        {
            $this->zip_object->open($zip_file);
            clearstatcache();
        }
        else
        {
            $create_code = (version_compare(PHP_VERSION, '5.2.12', '>') && defined('ZIPARCHIVE::CREATE')) ? ZIPARCHIVE::CREATE : 1;
            $this->zip_object->open($zip_file, $create_code);
        }

        if($this->zip_object->addFromString('wpvivid_package_info.json',$string)===false)
        {
            $ret['result']='failed';
            $ret['error']='Failed to add zip file';
            return $ret;
        }

        if(is_a($this->zip_object,'WPvivid_PclZip_2'))
        {

        }
        else
        {
            if($this->zip_object->close()===false)
            {
                $ret['result']='failed';
                $ret['error']='Failed to add zip file';
                return $ret;
            }
        }

        $ret['result']='success';
        return $ret;
    }

    public function check_available_zip_object($zip_method)
    {
        if($zip_method=='ziparchive'||empty($zip_method))
        {
            if($this->check_ziparchive_available())
            {
                $this->zip_object=new ZipArchive();
            }
            else
            {
                $this->zip_object=new WPvivid_PclZip_2();
            }
        }
        else
        {
            $this->zip_object=new WPvivid_PclZip_2();
        }
    }

    public function check_ziparchive_available()
    {
        if(class_exists('ZipArchive'))
        {
            if(method_exists('ZipArchive', 'addFile'))
            {
                return true;
            }
            else
            {
                return false;
            }
        }
        else
        {
            return false;
        }
    }

    public function addEmptyDir($zip_file,$folders)
    {
        $path=WP_CONTENT_DIR.DIRECTORY_SEPARATOR.WPvivid_Setting::get_backupdir().DIRECTORY_SEPARATOR;
        if(file_exists($path.$zip_file))
        {
            $this->zip_object->open($path.$zip_file);
        }
        else
        {
            $create_code = (version_compare(PHP_VERSION, '5.2.12', '>') && defined('ZIPARCHIVE::CREATE')) ? ZIPARCHIVE::CREATE : 1;
            $this->zip_object->open($path.$zip_file, $create_code);
        }

        foreach ($folders as $folder)
        {
            $this->zip_object->addEmptyDir($folder);
        }

        $this->zip_object->close();

        $ret['result']='success';
        return $ret;
    }
}

class WPvivid_PclZip_2
{
    public $addfiles;

    public $adddirs;

    public $path;

    public $pclzip;

    public $last_error;

    public $replace_path;

    public function __construct()
    {
        $this->addfiles = array();
        $this->adddirs = array();
        if(!defined('PCLZIP_TEMPORARY_DIR'))
        {
            $path=WP_CONTENT_DIR.DIRECTORY_SEPARATOR.WPvivid_Setting::get_backupdir().DIRECTORY_SEPARATOR;
            $temp_dir =$path.'wpvivid-pclzip-temp'.DIRECTORY_SEPARATOR;
            define(PCLZIP_TEMPORARY_DIR,$temp_dir);
        }

        if (!class_exists('WPvivid_PclZip'))
            include_once WPVIVID_PLUGIN_DIR . '/includes/zip/class-wpvivid-pclzip.php';
    }

    public function open($path, $flags = 0)
    {
        $ziparchive_create_match = (version_compare(PHP_VERSION, '5.2.12', '>') && defined('ZIPARCHIVE::CREATE')) ? ZIPARCHIVE::CREATE : 1;

        if ($flags == $ziparchive_create_match && file_exists($path))
            @wp_delete_file($path);

        $this->pclzip = new WPvivid_PclZip($path);

        if (empty($this->pclzip))
        {
            return false;
        }

        $this->path = $path;

        return true;

    }

    public function set_replace_path($replace_path)
    {
        $this->replace_path=$replace_path;
    }

    public function addFile($file, $add_as)
    {
        $this->addfiles[] = $file;
        return true;
    }

    public function addEmptyDir($dir)
    {
        $this->adddirs[] = $dir;
    }

    public function close()
    {
        if (empty($this->pclzip))
        {
            return false;
        }

        $path=WP_CONTENT_DIR.DIRECTORY_SEPARATOR.WPvivid_Setting::get_backupdir().DIRECTORY_SEPARATOR;

        foreach ($this->adddirs as $dir)
        {
            $ret=$this->pclzip->add($path.'emptydir', WPVIVID_PCLZIP_OPT_REMOVE_PATH, $path.'emptydir', WPVIVID_PCLZIP_OPT_ADD_PATH, $dir);
            if (!$ret)
            {
                $this->last_error = $this->pclzip->errorInfo(true);
                return false;
            }
        }

        if(!class_exists('WPvivid_ZipClass'))
            include_once WPVIVID_PLUGIN_DIR . '/includes/class-wpvivid-zipclass.php';
        $ret = $this->pclzip -> add($this->addfiles,WPVIVID_PCLZIP_OPT_REMOVE_PATH,$this->replace_path,WPVIVID_PCLZIP_CB_PRE_ADD,'wpvivid_function_per_add_callback',WPVIVID_PCLZIP_OPT_NO_COMPRESSION,WPVIVID_PCLZIP_OPT_TEMP_FILE_THRESHOLD,16);

        if (!$ret)
        {
            $this->last_error = $this->pclzip->errorInfo(true);
            return false;
        }

        $this->pclzip = false;
        $this->addfiles = array();
        $this->adddirs = array();

        clearstatcache();

        return true;
    }

    public function addFromString($file_name,$string)
    {
        $path=WP_CONTENT_DIR.DIRECTORY_SEPARATOR.WPvivid_Setting::get_backupdir().DIRECTORY_SEPARATOR;
        $temp_path = $path.$file_name;
        if(file_exists($temp_path))
        {
            @wp_delete_file($temp_path);
        }
        file_put_contents($temp_path,$string);
        $this->pclzip  -> add($temp_path,WPVIVID_PCLZIP_OPT_REMOVE_PATH,dirname($temp_path));
        @wp_delete_file($temp_path);
        return true;
    }
}includes/new_backup/class-wpvivid-backup-task_2.php000064400000344703151327705670016450 0ustar00<?php

if (!defined('WPVIVID_PLUGIN_DIR'))
{
    die;
}

class WPvivid_Backup_Task_2
{
    public $task;
    public $task_id;
    public $current_job;
    public $current_db;

    public function __construct($task_id=false,$task=array())
    {
        $this->task_id=false;
        $this->current_job=false;

        if(empty($task))
        {
            if(!empty($task_id))
            {
                $default = array();
                $options = get_option('wpvivid_task_list', $default);
                if(isset($options[$task_id]))
                {
                    $this->task=$options[$task_id];
                    $this->task_id=$task_id;
                }
            }
        }
        else
        {
            $this->task_id=$task_id;
            $this->task=$task;
        }

    }

    public function get_new_id()
    {
        $rand_id = substr(md5(time().rand()), 0,13);
        return 'wpvivid-'.$rand_id;
    }

    public function new_backup_task($options,$settings)
    {
        $this->task=array();
        $id=$this->get_new_id();
        $this->task['id']=$id;
        $this->task['type']=isset($options['type'])?$options['type']:'';

        if(isset($options['lock']))
        {
            $this->task['options']['lock']=$options['lock'];
        }
        else
        {
            $this->task['options']['lock']=0;
        }

        $this->task['status']['task_start_time']=time();
        $this->task['status']['task_end_time']=time();
        $this->task['status']['start_time']=time();
        $this->task['status']['run_time']=time();
        $this->task['status']['timeout']=time();
        $this->task['status']['str']='ready';
        $this->task['status']['resume_count']=0;

        $options['save_local']=isset($settings['save_local'])?$settings['save_local']:false;
        $this->set_backup_option($options);

        if(isset($options['remote']))
        {
            if($options['remote']=='1')
            {
                if(isset($options['remote_options']))
                {
                    $this->task['options']['remote_options']=$options['remote_options'];
                }
                else
                {
                    $this->task['options']['remote_options']=WPvivid_Setting::get_remote_options();
                }

            }
            else {
                $this->task['options']['remote_options']=false;
            }
        }
        else
        {
            $this->task['options']['remote_options']=false;
        }

        $this->task['setting']=$settings;

        $this->task['data']['doing']='backup';
        $this->task['data']['backup']['doing']='';
        $this->task['data']['backup']['progress']=0;
        $this->task['data']['backup']['sub_job']=array();
        $this->task['data']['upload']['doing']='';
        $this->task['data']['upload']['finished']=0;
        $this->task['data']['upload']['progress']=0;
        $this->task['data']['upload']['job_data']=array();
        $this->task['data']['upload']['sub_job']=array();



        $this->init_backup_job($options['backup_files']);
        $this->task['options']['backup_files']=$options['backup_files'];
        delete_option('wpvivid_task_list');
        WPvivid_Setting::update_task($id,$this->task);

        $ret['result']='success';
        $ret['task']=$this->task;
        $ret['task_id']=$this->task['id'];

        return $ret;
    }

    public function get_start_time()
    {
        return $this->task['status']['task_start_time'];
    }

    public function get_end_time()
    {
        return $this->task['status']['task_end_time'];
    }

    public function update_end_time()
    {
        $this->task['status']['task_end_time']=time();
        $this->update_task();
    }

    public function set_backup_option($options)
    {
        $offset=get_option('gmt_offset');
        $this->task['options']=$options;

        $general_setting=WPvivid_Setting::get_setting(true, "");

        if(isset($options['backup_prefix']) && !empty($options['backup_prefix']))
        {
            $this->task['options']['backup_prefix']=$options['backup_prefix'];
        }
        else
        {
            if(isset($general_setting['options']['wpvivid_common_setting']['domain_include'])&&$general_setting['options']['wpvivid_common_setting']['domain_include'])
            {
                $check_addon = apply_filters('wpvivid_check_setting_addon', 'not_addon');
                if (isset($general_setting['options']['wpvivid_common_setting']['backup_prefix']) && $check_addon == 'addon')
                {
                    $this->task['options']['backup_prefix'] = $general_setting['options']['wpvivid_common_setting']['backup_prefix'];
                }
                else {
                    $home_url_prefix = get_home_url();
                    $home_url_prefix = $this->parse_url_all($home_url_prefix);
                    $this->task['options']['backup_prefix'] = $home_url_prefix;
                }
            }
            else
            {
                $this->task['options']['backup_prefix']='';
            }
        }

        if(empty($this->task['options']['backup_prefix']))
        {
            $this->task['options']['file_prefix'] = $this->task['id'] . '_' . gmdate('Y-m-d-H-i', time()+$offset*60*60);
        }
        else
        {
            $this->task['options']['file_prefix'] =  $this->task['options']['backup_prefix'] . '_' . $this->task['id'] . '_' . gmdate('Y-m-d-H-i', time()+$offset*60*60);
        }
        $this->task['options']['file_prefix'] = apply_filters('wpvivid_backup_file_prefix',$this->task['options']['file_prefix'],$this->task['options']['backup_prefix'],$this->task['id'],$this->task['status']['start_time']);

        $this->task['options']['log_file_name']=$this->task['id'].'_backup';
        $log=new WPvivid_Log();
        $log->CreateLogFile($this->task['options']['log_file_name'],'no_folder','backup');
        $this->task['options']['log_file_path']=$log->log_file;
        $this->task['options']['prefix']=$this->task['options']['file_prefix'];
        $this->task['options']['dir']=WP_CONTENT_DIR.'/'.WPvivid_Setting::get_backupdir();
        $this->task['options']['backup_dir']=WPvivid_Setting::get_backupdir();

        $exclude_files=isset($options['exclude_files'])?$options['exclude_files']:array();
        $exclude_files=apply_filters('wpvivid_default_exclude_folders',$exclude_files);
        $this->task['options']['exclude-tables']=isset($options['exclude-tables'])?$options['exclude-tables']:array();
        $this->task['options']['exclude-tables']=$this->default_exclude_table($this->task['options']['exclude-tables']);

        $this->task['options']['include-tables']=isset($options['include-tables'])?$options['include-tables']:array();

        $this->task['options']['exclude_files']=$this->get_exclude_files($exclude_files);
        $this->task['options']['include_files']=$this->get_include_files();

        $this->task['options']['include_plugins']=isset($options['include_plugins'])?$options['include_plugins']:array();
        $this->task['options']['include_themes']=isset($options['include_themes'])?$options['include_themes']:array();

        if(isset($options['local']))
        {
            if($options['local']=='1')
            {
                $this->task['options']['save_local']=1;
            }
            else
            {
                //$this->task['options']['save_local']=0;
                $this->task['options']['save_local'] =isset($options['save_local'])?$options['save_local']:false;
            }
        }
        else
        {
            $this->task['options']['save_local']=1;
        }
        $this->task['options']['backup_options']['compress']['compress_type']='zip';
        $log->CloseFile();

        //$this->task['options']['remote_options'] = apply_filters('wpvivid_set_remote_options', $this->task['options']['remote_options'],$this->task['options']);
    }

    public function default_exclude_table($exclude_tables)
    {
        global $wpdb;
        $exclude_tables[]=$wpdb->base_prefix."wpvivid_log";
        $exclude_tables[]=$wpdb->base_prefix."wpvivid_increment_big_ids";
        $exclude_tables[]=$wpdb->base_prefix."wpvivid_options";
        $exclude_tables[]=$wpdb->base_prefix."wpvivid_record_task";
        $exclude_tables[]=$wpdb->base_prefix."wpvivid_merge_db";
        $exclude_tables[]=$wpdb->base_prefix."wpvivid_merge_ids";
        return $exclude_tables;
    }

    public function parse_url_all($url)
    {
        $parse = wp_parse_url($url);
        //$path=str_replace('/','_',$parse['path']);
        $path = '';
        if(isset($parse['path'])) {
            $parse['path'] = str_replace('/', '_', $parse['path']);
            $path = $parse['path'];
        }
        return $parse['host'].$path;
    }

    public function init_backup_job($backup_content)
    {
        $index=0;
        $this->task['jobs']=array();
        if($backup_content==='files')
        {
            $this->task['jobs'][$index]['backup_type']='backup_themes';
            $this->task['jobs'][$index]['finished']=0;
            $this->task['jobs'][$index]['progress']=0;
            $this->task['jobs'][$index]['file_index']=1;
            $this->task['jobs'][$index]['index']=0;
            $index++;

            $this->task['jobs'][$index]['backup_type']='backup_plugin';
            $this->task['jobs'][$index]['finished']=0;
            $this->task['jobs'][$index]['progress']=0;
            $this->task['jobs'][$index]['file_index']=1;
            $this->task['jobs'][$index]['index']=0;
            $index++;

            $this->task['jobs'][$index]['backup_type']='backup_uploads';
            $this->task['jobs'][$index]['finished']=0;
            $this->task['jobs'][$index]['progress']=0;
            $this->task['jobs'][$index]['file_index']=1;
            $this->task['jobs'][$index]['index']=0;
            $index++;

            $this->task['jobs'][$index]['backup_type']='backup_content';
            $this->task['jobs'][$index]['finished']=0;
            $this->task['jobs'][$index]['progress']=0;
            $this->task['jobs'][$index]['file_index']=1;
            $this->task['jobs'][$index]['index']=0;
            $index++;

            $this->task['jobs'][$index]['backup_type']='backup_core';
            $this->task['jobs'][$index]['finished']=0;
            $this->task['jobs'][$index]['progress']=0;
            $this->task['jobs'][$index]['file_index']=1;
            $this->task['jobs'][$index]['index']=0;
            $index++;
        }
        else if($backup_content==='files+db')
        {
            $this->task['jobs'][$index]['backup_type']='backup_db';
            $this->task['jobs'][$index]['finished']=0;
            $this->task['jobs'][$index]['progress']=0;
            $this->task['jobs'][$index]['file_index']=1;
            $this->task['jobs'][$index]['mysql_file_index']=1;
            $index++;

            $this->task['jobs'][$index]['backup_type']='backup_themes';
            $this->task['jobs'][$index]['finished']=0;
            $this->task['jobs'][$index]['progress']=0;
            $this->task['jobs'][$index]['file_index']=1;
            $this->task['jobs'][$index]['index']=0;
            $index++;

            $this->task['jobs'][$index]['backup_type']='backup_plugin';
            $this->task['jobs'][$index]['finished']=0;
            $this->task['jobs'][$index]['progress']=0;
            $this->task['jobs'][$index]['file_index']=1;
            $this->task['jobs'][$index]['index']=0;
            $index++;

            $this->task['jobs'][$index]['backup_type']='backup_uploads';
            $this->task['jobs'][$index]['finished']=0;
            $this->task['jobs'][$index]['progress']=0;
            $this->task['jobs'][$index]['file_index']=1;
            $this->task['jobs'][$index]['index']=0;
            $index++;

            $this->task['jobs'][$index]['backup_type']='backup_content';
            $this->task['jobs'][$index]['finished']=0;
            $this->task['jobs'][$index]['progress']=0;
            $this->task['jobs'][$index]['file_index']=1;
            $this->task['jobs'][$index]['index']=0;
            $index++;

            $this->task['jobs'][$index]['backup_type']='backup_core';
            $this->task['jobs'][$index]['finished']=0;
            $this->task['jobs'][$index]['progress']=0;
            $this->task['jobs'][$index]['file_index']=1;
            $this->task['jobs'][$index]['index']=0;
            $index++;
        }
        else if($backup_content==='db')
        {
            $this->task['jobs'][$index]['backup_type']='backup_db';
            $this->task['jobs'][$index]['finished']=0;
            $this->task['jobs'][$index]['progress']=0;
            $this->task['jobs'][$index]['file_index']=1;
            $this->task['jobs'][$index]['mysql_file_index']=1;
            $index++;
        }

        $is_merge=$this->task['setting']['is_merge'];
        if(count($this->task['jobs'])==1)
        {
            $is_merge=false;
        }

        if($is_merge)
        {
            $this->task['jobs'][$index]['backup_type']='backup_merge';
            $this->task['jobs'][$index]['finished']=0;
            $this->task['jobs'][$index]['progress']=0;
            $this->task['jobs'][$index]['file_index']=1;
            $this->task['jobs'][$index]['child_file']=array();
            $this->task['jobs'][$index]['index']=0;
        }
    }

    public function get_exclude_files($exclude_files=array())
    {
        $exclude_plugins=array();
        $exclude_plugins=apply_filters('wpvivid_exclude_plugins',$exclude_plugins);
        $exclude_regex=array();
        foreach ($exclude_plugins as $exclude_plugin)
        {
            $exclude_regex[]='#^'.preg_quote($this -> transfer_path(WP_PLUGIN_DIR.'/'.$exclude_plugin), '/').'#';
        }
        foreach ($exclude_files as $exclude_file)
        {
            if($exclude_file['type']=='file'||$exclude_file['type']=='folder')
            {
                if(file_exists($exclude_file['path']))
                {
                    $exclude_regex[]='#^'.preg_quote($this -> transfer_path($exclude_file['path']), '/').'#';
                }
                else
                {
                    $path=WP_CONTENT_DIR.'/'.$exclude_file['path'];
                    if(file_exists($path))
                    {
                        $exclude_regex[]='#^'.preg_quote($this -> transfer_path($path), '/').'#';
                    }
                }
            }
            else if($exclude_file['type']=='ext')
            {
                $exclude_regex[]='#^.*\.'.$exclude_file['path'].'$#';
            }
        }

        $exclude_regex[]='#^'.preg_quote($this -> transfer_path(WP_CONTENT_DIR).'/'.'wpvivid', '/').'#';
        $exclude_regex[]='#^'.preg_quote($this -> transfer_path(WP_CONTENT_DIR).'/'.WPvivid_Setting::get_backupdir(), '/').'#';

        if(defined('WPVIVID_UPLOADS_ISO_DIR'))
        {
            $exclude_regex[]='#^'.preg_quote($this -> transfer_path(WP_CONTENT_DIR).'/'.WPVIVID_UPLOADS_ISO_DIR, '/').'#';
        }

        return $exclude_regex;
    }

    public function get_backup_type_exclude_files($backup_type)
    {
        $exclude_regex=array();

        if($backup_type=='backup_content')
        {
            $upload_dir = wp_upload_dir();
            $exclude_regex[]='#^'.preg_quote($this -> transfer_path(WP_CONTENT_DIR).'/'.'plugins', '/').'#';
            $exclude_regex[]='#^'.preg_quote($this -> transfer_path($upload_dir['basedir']), '/').'$#';
            $exclude_regex[]='#^'.preg_quote($this->transfer_path(get_theme_root()), '/').'#';
        }
        
        return $exclude_regex;
    }

    public function get_include_files()
    {
        $include_regex[]='#^'.preg_quote($this -> transfer_path(ABSPATH.'wp-admin'), '/').'#';
        $include_regex[]='#^'.preg_quote($this->transfer_path(ABSPATH.'wp-includes'), '/').'#';
        $include_regex[]='#^'.preg_quote($this->transfer_path(ABSPATH.'lotties'), '/').'#';

        return $include_regex;
    }

    public function set_memory_limit()
    {
        $memory_limit=isset($this->task['setting']['memory_limit'])?$this->task['setting']['memory_limit']:WPVIVID_MEMORY_LIMIT;
        @ini_set('memory_limit', $memory_limit);
    }

    public function is_backup_finished()
    {
        $finished=true;

        foreach ($this->task['jobs'] as $job)
        {
            if($job['finished']==0)
            {
                $finished=false;
                break;
            }
        }
        return $finished;
    }

    public function update_sub_task_progress($progress)
    {
        $this->task['status']['run_time']=time();
        $this->task['status']['str']='running';
        $this->task['data']['doing']='backup';
        $sub_job_name=$this->task['jobs'][$this->current_job]['backup_type'];
        $this->task['data']['backup']['doing']=$sub_job_name;
        $this->task['data']['backup']['sub_job'][$sub_job_name]['progress']=$progress;
        if(!isset( $this->task['data']['backup']['sub_job'][$sub_job_name]['job_data']))
        {
            $this->task['data']['backup']['sub_job'][$sub_job_name]['job_data']=array();
        }
        $this->update_task();
    }

    public function get_next_job()
    {
        $job_key=false;
        foreach ($this->task['jobs'] as $key=>$job)
        {
            if($job['finished']==0)
            {
                $job_key=$key;
                break;
            }
        }
        return $job_key;
    }

    public function do_backup_job($key)
    {
        if(!isset($this->task['jobs'][$key]))
        {
            $ret['result']='failed';
            $ret['error']='not found job';
            return $ret;
        }

        //backup_type
        $this->current_job=$key;
        $job=$this->task['jobs'][$key];
        global $wpvivid_plugin;
        $wpvivid_plugin->wpvivid_log->WriteLog('Prepare to backup '.$job['backup_type'].' files.','notice');

        $this->update_sub_task_progress(sprintf('Start backing up %s.',$job['backup_type']));

        if($job['backup_type']=='backup_db')
        {
            $ret=$this->do_backup_db();
            if($ret['result']!='success')
            {
                return $ret;
            }
            else
            {
                $this->rename_backup_files($key);
            }
        }
        else if($job['backup_type']=='backup_merge')
        {
            $ret=$this->do_backup_merge();
            if($ret['result']!='success')
            {
                return $ret;
            }
            else
            {
                $this->rename_backup_files($key);
            }
        }
        else
        {
            $ret=$this->do_backup_files($job['backup_type']);
            if($ret['result']!='success')
            {
                return $ret;
            }
            else
            {
                $this->rename_backup_files($key);
            }
        }

        $wpvivid_plugin->wpvivid_log->WriteLog('Backing up '.$job['backup_type'].' completed.','notice');
        $this->task['jobs'][$key]['finished']=1;
        $this->task['status']['resume_count']=0;

        $this->update_sub_task_progress(sprintf('Backing up %s finished.',$job['backup_type']));
        $this->update_main_progress();

        $ret['result']='success';
        return $ret;
    }

    public function rename_backup_files($key)
    {
        if(isset($this->task['jobs'][$key]['zip_file']))
        {
            if(count($this->task['jobs'][$key]['zip_file'])==1)
            {
                $backup_type=$this->task['jobs'][$key]['backup_type'];
                $file_prefix=$this->task['options']['file_prefix'];

                $old_file=array_shift($this->task['jobs'][$key]['zip_file']);

                if($backup_type=='backup_merge')
                {
                    $backup_type='backup_all';
                }
                $filename=$file_prefix.'_'.$backup_type.'.zip';
                $zip['filename']=$filename;
                $zip['finished']=1;
                $this->task['jobs'][$key]['zip_file']=array();
                $this->task['jobs'][$key]['zip_file'][$filename]=$zip;

                $path=$this->task['options']['dir'].'/';

                rename($path.$old_file['filename'],$path.$filename);

                $this->update_task();
            }
        }
    }

    public function update_main_progress()
    {
        $i_finished_backup_count=0;
        $i_sum=count($this->task['jobs']);
        foreach ($this->task['jobs'] as $job)
        {
            if($job['finished']==1)
            {
                $i_finished_backup_count++;
            }
        }
        $i_progress=intval(($i_finished_backup_count/$i_sum)*100);
        $this->task['data']['backup']['progress']=$i_progress;
        $this->update_task();
    }

    public function update_database_progress($i_progress)
    {
        $this->task['data']['backup']['progress']=$i_progress;
        $this->update_task();
    }

    public function delete_canceled_backup_files($task_id)
    {
        $path = $this->task['options']['dir'];
        $handler=opendir($path);
        if($handler!==false)
        {
            while(($filename=readdir($handler))!==false)
            {
                if(preg_match('#'.$task_id.'#',$filename) || preg_match('#'.apply_filters('wpvivid_fix_wpvivid_free', $task_id).'#',$filename))
                {
                    @wp_delete_file($path.'/'.$filename);
                }
            }
            @closedir($handler);
        }
    }

    public function update_task()
    {
        wp_cache_flush();
        $default = array();
        $tasks = get_option('wpvivid_task_list', $default);
        if(array_key_exists ($this->task_id, $tasks))
        {
            $this->task['status']['run_time']=time();
            WPvivid_Setting::update_task($this->task_id,$this->task);
        }
        else
        {
            $this->delete_canceled_backup_files($this->task_id);
        }
    }

    public function do_backup_merge()
    {
        $root_path=$this->get_backup_root('backup_merge');

        $files=$this->get_merge_files($root_path);

        if(empty($files))
        {
            $ret['result']='success';
            return $ret;
        }

        $max_zip_file_size= $this->task['setting']['max_file_size']*1024*1024;


        $path=$this->task['options']['dir'].'/';

        $zip_method=isset($this->task['setting']['zip_method'])?$this->task['setting']['zip_method']:'ziparchive';
        $zip=new WPvivid_Zip($zip_method);

        $zip_file_name=$path.$this->get_zip_file('backup_merge');

        $numItems = count($files);
        $i = 0;
        $index=$this->get_zipped_file_index();
        foreach ($files as $file)
        {
            if($this->check_cancel_backup())
            {
                die();
            }

            if($i<$index)
            {
                $i++;
                continue;
            }

            if($max_zip_file_size==0)
                $max_zip_file_size = 4 * 1024 * 1024 * 1024;

            if(!file_exists($zip_file_name) || filesize($zip_file_name) == 0)
            {
                $zip->add_file($zip_file_name,$file,basename($file),dirname($file));
                $i++;

                $child_json=$this->get_file_json($file);
                $this->update_merge_zipped_file_index($i,basename($file),$child_json);

                if($i === $numItems)
                {
                    continue;
                }

                if((filesize($zip_file_name)>$max_zip_file_size) || ($i >= 55000))
                {
                    $json=array();
                    $json=$this->get_json_info('backup_merge',$json);
                    $this->update_zip_file(basename($zip_file_name),1,$json);
                    $zip_file_name=$path.$this->add_zip_file('backup_merge');
                }
            }
            else if(((filesize($zip_file_name) + filesize($file)) < $max_zip_file_size) && $i < 55000)
            {
                $zip->add_file($zip_file_name,$file,basename($file),dirname($file));
                $i++;

                $child_json=$this->get_file_json($file);
                $this->update_merge_zipped_file_index($i,basename($file),$child_json);

                if($i === $numItems)
                {
                    continue;
                }
            }
            else
            {
                $json=array();
                $json=$this->get_json_info('backup_merge',$json);
                $this->update_zip_file(basename($zip_file_name),1,$json);
                $zip_file_name=$path.$this->add_zip_file('backup_merge');

                $zip->add_file($zip_file_name,$file,basename($file),dirname($file));
                $i++;

                $child_json=$this->get_file_json($file);
                $this->update_merge_zipped_file_index($i,basename($file),$child_json);

                if($i === $numItems)
                {
                    continue;
                }
            }

            /*if(filesize($zip_file_name)>$max_zip_file_size)
            {
                $json=array();
                $json=$this->get_json_info('backup_merge',$json);
                $this->update_zip_file(basename($zip_file_name),1,$json);
                $zip_file_name=$path.$this->add_zip_file('backup_merge');
            }*/
        }

        $json=array();
        $json=$this->get_json_info('backup_merge',$json);
        $this->update_zip_file(basename($zip_file_name),1,$json);
        foreach ($files as $file)
        {
            @wp_delete_file($file);
        }
        $ret['result']='success';
        return $ret;
    }

    public function do_backup_files($backup_type)
    {
        $root_path=$this->get_backup_root($backup_type);
        $exclude_files=$this->get_backup_type_exclude_files($backup_type);
        $backup_symlink_folder=$this->task['setting']['backup_symlink_folder'];
        if($root_path===false)
        {
            $ret['result']='failed';
            $ret['error']='backup type not found';
            return $ret;
        }
        $compress_file_use_cache= $this->task['setting']['compress_file_use_cache'];

        $replace_path=$this->get_replace_path($backup_type);

        if($compress_file_use_cache)
        {
            if(!$this->check_cache_files())
            {
                $this->clean_zip_files();

                if($backup_type=='backup_core')
                {
                    $folders=$this->get_empty_folders($root_path,$replace_path,$backup_symlink_folder,$exclude_files,$this->task['options']['include_files']);
                }
                else if($backup_type=='backup_plugin')
                {
                    if(!empty($this->task['options']['include_plugins']))
                    {
                        $include_regex=array();
                        foreach ($this->task['options']['include_plugins'] as $plugins)
                        {
                            $include_regex[]='#^'.preg_quote($this -> transfer_path(WP_PLUGIN_DIR.DIRECTORY_SEPARATOR.$plugins), '/').'#';
                        }
                        $folders=$this->get_empty_folders($root_path,$replace_path,$backup_symlink_folder,$exclude_files,$include_regex);
                    }
                    else
                    {
                        $folders=$this->get_empty_folders($root_path,$replace_path,$backup_symlink_folder,$exclude_files);
                    }
                }
                else if($backup_type=='backup_themes')
                {
                    if(!empty($this->task['options']['include_themes']))
                    {
                        $include_regex=array();
                        foreach ($this->task['options']['include_themes'] as $themes)
                        {
                            $include_regex[]='#^'.preg_quote($this -> transfer_path(get_theme_root().DIRECTORY_SEPARATOR.$themes), '/').'#';
                        }
                        $folders=$this->get_empty_folders($root_path,$replace_path,$backup_symlink_folder,$exclude_files,$include_regex);
                    }
                    else
                    {
                        $folders=$this->get_empty_folders($root_path,$replace_path,$backup_symlink_folder,$exclude_files);
                    }
                }
                else
                {
                    $folders=$this->get_empty_folders($root_path,$replace_path,$backup_symlink_folder,$exclude_files);
                }

                $cache_file_prefix=WP_CONTENT_DIR.'/'.WPvivid_Setting::get_backupdir().'/'.$this->task['options']['file_prefix'].'_'.$backup_type.'_';

                if($backup_type=='backup_core')
                {
                    $ret=$this->create_cache_files($cache_file_prefix,$root_path,$backup_symlink_folder,$exclude_files,$this->task['options']['include_files']);
                }
                else if($backup_type=='backup_custom_other')
                {
                    $ret=$this->create_custom_other_cache_files($cache_file_prefix,$this->task['options']['custom_other_root'],$backup_symlink_folder,$exclude_files,$this->task['options']['custom_other_include_files']);
                }
                else if($backup_type=='backup_plugin')
                {
                    if(!empty($this->task['options']['include_plugins']))
                    {
                        $include_regex=array();
                        foreach ($this->task['options']['include_plugins'] as $plugins)
                        {
                            $include_regex[]='#^'.preg_quote($this -> transfer_path(WP_PLUGIN_DIR.DIRECTORY_SEPARATOR.$plugins), '/').'#';
                        }
                        $ret=$this->create_cache_files($cache_file_prefix,$root_path,$backup_symlink_folder,$exclude_files,$include_regex);
                    }
                    else
                    {
                        $ret=$this->create_cache_files($cache_file_prefix,$root_path,$backup_symlink_folder,$exclude_files);
                    }
                }
                else if($backup_type=='backup_themes')
                {
                    if(!empty($this->task['options']['include_themes']))
                    {
                        $include_regex=array();
                        foreach ($this->task['options']['include_themes'] as $themes)
                        {
                            $include_regex[]='#^'.preg_quote($this -> transfer_path(get_theme_root().DIRECTORY_SEPARATOR.$themes), '/').'#';
                        }
                        $ret=$this->create_cache_files($cache_file_prefix,$root_path,$backup_symlink_folder,$exclude_files,$include_regex);
                    }
                    else
                    {
                        $ret=$this->create_cache_files($cache_file_prefix,$root_path,$backup_symlink_folder,$exclude_files);
                    }
                }
                else
                {
                    $ret=$this->create_cache_files($cache_file_prefix,$root_path,$backup_symlink_folder,$exclude_files);
                }

                if($ret['is_empty']===true)
                {
                    $ret['result']='success';
                    $this->clean_tmp_files();
                    return $ret;
                }

                $ret=$this->_backup_empty_folder($folders,$backup_type);

                if($ret['result']!='success')
                {
                    return $ret;
                }
            }

            $ret=$this->_backup_files_use_cache($backup_type,$replace_path);
        }
        else
        {
            if($backup_type=='backup_core')
            {
                $folders=$this->get_empty_folders($root_path,$replace_path,$backup_symlink_folder,$exclude_files,$this->task['options']['include_files']);
            }
            else if($backup_type=='backup_plugin')
            {
                if(!empty($this->task['options']['include_plugins']))
                {
                    $include_regex=array();
                    foreach ($this->task['options']['include_plugins'] as $plugins)
                    {
                        $include_regex[]='#^'.preg_quote($this -> transfer_path(WP_PLUGIN_DIR.DIRECTORY_SEPARATOR.$plugins), '/').'#';
                    }
                    $folders=$this->get_empty_folders($root_path,$replace_path,$backup_symlink_folder,$exclude_files,$include_regex);
                }
                else
                {
                    $folders=$this->get_empty_folders($root_path,$replace_path,$backup_symlink_folder,$exclude_files);
                }
            }
            else if($backup_type=='backup_themes')
            {
                if(!empty($this->task['options']['include_themes']))
                {
                    $include_regex=array();
                    foreach ($this->task['options']['include_themes'] as $themes)
                    {
                        $include_regex[]='#^'.preg_quote($this -> transfer_path(get_theme_root().DIRECTORY_SEPARATOR.$themes), '/').'#';
                    }
                    $folders=$this->get_empty_folders($root_path,$replace_path,$backup_symlink_folder,$exclude_files,$include_regex);
                }
                else
                {
                    $folders=$this->get_empty_folders($root_path,$replace_path,$backup_symlink_folder,$exclude_files);
                }
            }
            else
            {
                $folders=$this->get_empty_folders($root_path,$replace_path,$backup_symlink_folder,$exclude_files);
            }

            if($backup_type=='backup_core')
            {
                $files=$this->get_files($root_path,$backup_symlink_folder,$exclude_files,$this->task['options']['include_files']);
            }
            else if($backup_type=='backup_plugin')
            {
                if(!empty($this->task['options']['include_plugins']))
                {
                    $include_regex=array();
                    foreach ($this->task['options']['include_plugins'] as $plugins)
                    {
                        $include_regex[]='#^'.preg_quote($this -> transfer_path(WP_PLUGIN_DIR.DIRECTORY_SEPARATOR.$plugins), '/').'#';
                    }
                    $files=$this->get_files($root_path,$backup_symlink_folder,$exclude_files,$include_regex);
                }
                else
                {
                    $files=$this->get_files($root_path,$backup_symlink_folder,$exclude_files);
                }
            }
            else if($backup_type=='backup_themes')
            {
                if(!empty($this->task['options']['include_themes']))
                {
                    $include_regex=array();
                    foreach ($this->task['options']['include_themes'] as $themes)
                    {
                        $include_regex[]='#^'.preg_quote($this -> transfer_path(get_theme_root().DIRECTORY_SEPARATOR.$themes), '/').'#';
                    }
                    $files=$this->get_files($root_path,$backup_symlink_folder,$exclude_files,$include_regex);
                }
                else
                {
                    $files=$this->get_files($root_path,$backup_symlink_folder,$exclude_files);
                }
            }
            else
            {
                $files=$this->get_files($root_path,$backup_symlink_folder,$exclude_files);
            }

            $replace_path=$this->get_replace_path($backup_type);
            if(empty($files))
            {
                $ret['result']='success';
                return $ret;
            }
            else
            {
                $ret=$this->_backup_empty_folder($folders,$backup_type);

                if($ret['result']!='success')
                {
                    return $ret;
                }

                $ret=$this->_backup_files($files,$replace_path,$backup_type);
            }
        }

        return $ret;
    }

    public function transfer_path($path)
    {
        $path = str_replace('\\','/',$path);
        $values = explode('/',$path);
        return implode('/',$values);
    }

    public function get_backup_root($backup_type)
    {
        if($backup_type=='backup_themes')
        {
            return $this->transfer_path(get_theme_root());
        }
        else if($backup_type=='backup_plugin')
        {
            return $this->transfer_path(WP_PLUGIN_DIR);
        }
        else if($backup_type=='backup_uploads')
        {
            $upload_dir = wp_upload_dir();
            return $this -> transfer_path($upload_dir['basedir']);
        }
        else if($backup_type=='backup_content')
        {
            return $this -> transfer_path(WP_CONTENT_DIR);
        }
        else if($backup_type=='backup_core')
        {
            return $this -> transfer_path(ABSPATH);
        }
        else if($backup_type=='backup_merge')
        {
            return $this -> transfer_path(WP_CONTENT_DIR.'/'.WPvivid_Setting::get_backupdir());
        }
        else
        {
            return false;
        }
    }

    public function get_replace_path($backup_type)
    {
        if($backup_type=='backup_themes')
        {
            return $this->transfer_path(WP_CONTENT_DIR.'/');
        }
        else if($backup_type=='backup_plugin')
        {
            return $this->transfer_path(WP_CONTENT_DIR.'/');
        }
        else if($backup_type=='backup_uploads')
        {
            return $this->transfer_path(WP_CONTENT_DIR.'/');
        }
        else if($backup_type=='backup_content')
        {
            return $this->transfer_path(WP_CONTENT_DIR.'/');
        }
        else if($backup_type=='backup_core')
        {
            return $this -> transfer_path(ABSPATH);
        }
        else
        {
            return false;
        }
    }

    public function check_cache_files()
    {
        if($this->current_job!==false)
        {
            if(isset($this->task['jobs'][$this->current_job]['cache_files']))
            {
                return true;
            }
        }
        return false;
    }

    public function clean_tmp_files()
    {
        if($this->current_job!==false)
        {
            if(isset($this->task['jobs'][$this->current_job]['cache_files']))
            {
                foreach ($this->task['jobs'][$this->current_job]['cache_files'] as $cache_file)
                {
                    @wp_delete_file($cache_file['name']);
                }
            }

            if(isset($this->task['jobs'][$this->current_job]['mysql_dump_files']))
            {
                $files=$this->task['jobs'][$this->current_job]['mysql_dump_files'];
                if(count($files)==1)
                {
                    $path=$this->task['options']['dir'].'/';
                    $new_file=$this->task['options']['file_prefix'].'_backup_db.sql';

                    @wp_delete_file($path.$new_file);
                }
                else
                {
                    $path=$this->task['options']['dir'].'/';
                    foreach ($files as $file)
                    {
                        @wp_delete_file($path.$file);
                    }
                }
            }
        }
    }

    public function get_empty_folders($root_path,$replace_path,$backup_symlink_folder=false,$exclude_files=array(),$include_files=array())
    {
        $folder=array();
        $exclude_regex=array_merge($this->task['options']['exclude_files'],$exclude_files);
        $root_path=untrailingslashit($root_path);
        $this->_get_folders($root_path,$replace_path,$folder,$backup_symlink_folder,$exclude_regex,$include_files);
        return $folder;
    }

    public function _get_folders($path,$replace_path,&$folders,$backup_symlink_folder=false,$exclude_regex=array(),$include_regex=array())
    {
        $handler = opendir($path);

        if($handler===false)
            return;

        while (($filename = readdir($handler)) !== false)
        {
            if ($filename != "." && $filename != "..")
            {
                if (is_dir($path . '/' . $filename))
                {
                    if($backup_symlink_folder == 1 || ($backup_symlink_folder == 0 && !@is_link($path . '/' . $filename)))
                    {
                        if($this->regex_match($exclude_regex, $this->transfer_path($path . '/' . $filename), 0))
                        {
                            if ($this->regex_match($include_regex, $path . '/' . $filename, 1))
                            {
                                $folders[]=str_replace($replace_path,'',$this->transfer_path($path . '/' . $filename));
                                $this->_get_folders($path . '/' . $filename,$replace_path,$folders,$backup_symlink_folder,$exclude_regex,$include_regex);
                            }
                        }
                    }
                }
            }
        }
        if($handler)
            @closedir($handler);

        return;
    }

    public function _backup_empty_folder($folders,$backup_type)
    {
        $file_prefix=$this->task['options']['file_prefix'];
        $file_index=$this->get_file_index();
        $zip_file_name=$file_prefix.'_'.$backup_type.'.part'.sprintf('%03d',($file_index)).'.zip';

        $zip_method=isset($this->task['setting']['zip_method'])?$this->task['setting']['zip_method']:'ziparchive';
        $zip=new WPvivid_Zip($zip_method);

        return $zip->addEmptyDir($zip_file_name,$folders);
    }

    public function create_cache_files($file_prefix,$root_path,$backup_symlink_folder=false,$exclude_files=array(),$include_files=array())
    {
        $number=1;
        $cache_file_handle=false;
        $max_cache_file_size=16*1024*1024;

        $exclude_regex=array_merge($this->task['options']['exclude_files'],$exclude_files);

        $exclude_file_size=$this->task['setting']['exclude_file_size'];

        $root_path=untrailingslashit($root_path);

        $skip_files_time=0;

        $files=$this->get_file_cache($root_path,$file_prefix,$cache_file_handle,$max_cache_file_size,$number,$backup_symlink_folder,$exclude_regex,$include_files,$exclude_file_size,$skip_files_time);

        if($this->current_job!==false)
        {
            foreach ($files as $file)
            {
                $file_data['name']=$file;
                $file_data['index']=0;
                $file_data['finished']=0;
                $this->task['jobs'][$this->current_job]['cache_files'][$file_data['name']]=$file_data;
            }

            $this->update_task();
        }

        $ret['result']='success';
        $ret['is_empty']=$this->is_cache_empty($files);
        return $ret;
    }

    public function is_cache_empty($files)
    {
        $empty=true;
        foreach ($files as $file)
        {
            if(filesize($file)>0)
            {
                $empty=false;
                break;
            }
        }
        return $empty;
    }

    public function create_custom_other_cache_files($file_prefix,$custom_other_root,$backup_symlink_folder=false,$exclude_files=array(),$include_files=array())
    {
        $number=1;
        $cache_file_handle=false;
        $max_cache_file_size=16*1024*1024;

        $exclude_regex=array_merge($this->task['options']['exclude_files'],$exclude_files);

        $exclude_file_size=$this->task['setting']['exclude_file_size'];

        if(isset($this->task['options']['incremental_options']))
        {
            $skip_files_time=$this->task['options']['incremental_options']['versions']['skip_files_time'];
        }
        else
        {
            $skip_files_time=0;
        }

        $files=array();
        foreach ($custom_other_root as $root_path)
        {
            $files1=$this->get_file_cache($root_path,$file_prefix,$cache_file_handle,$max_cache_file_size,$number,$backup_symlink_folder,$exclude_regex,$include_files,$exclude_file_size,$skip_files_time);
            $files=array_merge($files,$files1);
        }

        if($this->current_job!==false)
        {
            foreach ($files as $file)
            {
                $file_data['name']=$file;
                $file_data['index']=0;
                $file_data['finished']=0;
                $this->task['jobs'][$this->current_job]['cache_files'][$file_data['name']]=$file_data;
            }

            $this->update_task();
        }

        $ret['result']='success';
        $ret['is_empty']=$this->is_cache_empty($files);
        return $ret;
    }

    public function update_files_cache($file_data)
    {
        if($this->current_job!==false)
        {
            $this->task['jobs'][$this->current_job]['cache_files'][$file_data['name']]=$file_data;
            $this->task['status']['resume_count']=0;
            $this->update_task();
        }
    }

    public function get_files_cache_list()
    {
        if($this->current_job!==false)
        {
            return $this->task['jobs'][$this->current_job]['cache_files'];
        }
        else
        {
            return array();
        }
    }

    public function get_files_from_cache($cache_file,$index,$max_count)
    {
        $files=array();
        $file = new SplFileObject($cache_file);
        $file->seek($index);

        $file->setFlags( \SplFileObject::SKIP_EMPTY | \SplFileObject::READ_AHEAD );

        $count=0;

        while(!$file->eof())
        {
            $src = $file->fgets();

            $src=trim($src,PHP_EOL);

            if(empty($src))
                continue;

            if(!file_exists($src))
            {
                continue;
            }

            $files[$src]=$src;
            $count++;

            if($count>$max_count)
            {
                break;
            }
        }

        $ret['eof']=$file->eof();
        $ret['files']=$files;
        return $ret;
    }

    public function get_files_from_cache_by_size($cache_file,$index,$max_zip_file_size,$use_pclzip)
    {
        $files=array();
        $file = new SplFileObject($cache_file);
        //$file->seek($index);
        if (version_compare(PHP_VERSION, '8.0.1', '>=') || $index == 0) {
            $file->seek($index);
        } else {
            if( $index == 1 ){
                $file->rewind(); // Ensure to go at first row before exit
                $file->fgets(); // Read line 0. Cursor remains now at line 1
            } else {
                $file->seek($index-1);
            }
        }

        $file->setFlags( \SplFileObject::SKIP_EMPTY | \SplFileObject::READ_AHEAD );

        $current_size=0;
        $current=$index;
        $current_file_index = 0;
        while(!$file->eof())
        {
            $src = $file->fgets();
            $src=trim($src,PHP_EOL);
            if(empty($src))
            {
                continue;
            }

            if(!file_exists($src))
            {
                continue;
            }

            if($max_zip_file_size==0)
                $max_zip_file_size = 4 * 1024 * 1024 * 1024;

            if($current_size > 0)
            {
                $current_size+=filesize($src);
            }

            if($current_size == 0)
            {
                $current++;
                $current_file_index++;
                $files[$src]=$src;
                $current_size+=filesize($src);
            }
            else if(($current_size>$max_zip_file_size) || ($use_pclzip && ($current_file_index >= 55000)))
            {
                break;
            }
            else
            {
                $current++;
                $current_file_index++;
                $files[$src]=$src;
            }
        }

        $ret['eof']=$file->eof();
        $ret['index']=$current;
        $ret['files']=$files;
        return $ret;
    }

    public function get_files_count($files,$index,$add_files_count)
    {
        $add_files = array_slice($files,$index,$add_files_count);

        if($index+$add_files_count>count($files))
        {
            $eof=true;
        }
        else
        {
            $eof=false;
        }
        $ret['eof']=$eof;
        $ret['files']=$add_files;
        return $ret;
    }

    public function get_files_size($files,$index,$max_zip_file_size,$use_pclzip)
    {
        $current=0;
        $current_file_index = 0;
        $current_size=0;
        $add_files=array();
        foreach ($files as $file)
        {
            if($current<$index)
            {
                $current++;
                continue;
            }

            if($max_zip_file_size==0)
                $max_zip_file_size = 4 * 1024 * 1024 * 1024;

            if($current_size > 0)
            {
                $current_size+=filesize($file);
            }

            if($current_size == 0)
            {
                $current++;
                $current_file_index++;
                $add_files[]=$file;
                $current_size+=filesize($file);
            }
            else if(($current_size>$max_zip_file_size) || ($use_pclzip && ($current_file_index >= 55000)))
            {
                break;
            }
            else
            {
                $current++;
                $current_file_index++;
                $add_files[]=$file;
            }
        }

        if($current>=count($files))
        {
            $eof=true;
        }
        else
        {
            $eof=false;
        }
        $ret['eof']=$eof;
        $ret['index']=$current;
        $ret['files']=$add_files;
        return $ret;
    }

    public function get_file_cache($path,$cache_prefix,&$cache_file_handle,$max_cache_file_size,&$number,$backup_symlink_folder,$exclude_files,$include_files,$exclude_file_size,$skip_files_time)
    {
        $files=array();

        if(!$cache_file_handle)
        {
            $cache_file=$cache_prefix.$number.'.cache';
            $cache_file_handle=fopen($cache_file,'a');
            $files[] = $cache_file;
        }
        $handler = opendir($path);

        if($handler===false)
            return $files;

        while (($filename = readdir($handler)) !== false)
        {
            if ($filename != "." && $filename != "..")
            {
                if (is_dir($path . '/' . $filename))
                {
                    if($backup_symlink_folder == 1 || ($backup_symlink_folder == 0 && !@is_link($path . '/' . $filename)))
                    {
                        if($this->regex_match($exclude_files, $this->transfer_path($path . '/' . $filename), 0))
                        {
                            if ($this->regex_match($include_files, $path . '/' . $filename, 1))
                            {
                                $files2=$this->get_file_cache($path . '/' . $filename,$cache_prefix,$cache_file_handle,$max_cache_file_size,$number,$backup_symlink_folder,$exclude_files,$include_files,$exclude_file_size,$skip_files_time);
                                $files=array_merge($files,$files2);
                            }
                        }
                    }
                }
                else
                {
                    if($this->regex_match($exclude_files, $this->transfer_path($path . '/' . $filename), 0))
                    {
                        if(is_readable($path . '/' . $filename))
                        {
                            if($backup_symlink_folder == 1 || ($backup_symlink_folder == 0 && !@is_link($path . '/' . $filename)))
                            {
                                if ($exclude_file_size != 0)
                                {
                                    if (filesize($path . '/' . $filename) < $exclude_file_size * 1024 * 1024)
                                    {
                                        $add=true;
                                    }
                                    else
                                    {
                                        $add=false;
                                    }
                                }
                                else
                                {
                                    $add=true;
                                }

                                if($add)
                                {
                                    if($skip_files_time>0)
                                    {
                                        $file_time=filemtime($path . '/' . $filename);
                                        if($file_time>0&&$file_time>$skip_files_time)
                                        {
                                            $line = $this->transfer_path($path . '/' . $filename).PHP_EOL;
                                            fwrite($cache_file_handle, $line);
                                        }
                                    }
                                    else
                                    {
                                        $line = $this->transfer_path($path . '/' . $filename).PHP_EOL;
                                        fwrite($cache_file_handle, $line);
                                    }

                                }
                            }
                        }
                    }
                }
            }
        }
        if($handler)
            @closedir($handler);

        return $files;
    }

    public function get_zip_file($backup_type)
    {
        if($this->current_job!==false)
        {
            if(!isset($this->task['jobs'][$this->current_job]['zip_file']))
            {
                $file_prefix=$this->task['options']['file_prefix'];
                if($backup_type=='backup_merge')
                {
                    $backup_type='backup_all';
                }
                $filename=$file_prefix.'_'.$backup_type.'.part'.sprintf('%03d',($this->task['jobs'][$this->current_job]['file_index'])).'.zip';
                $zip['filename']=$filename;
                $zip['finished']=0;
                $this->task['jobs'][$this->current_job]['zip_file'][$filename]=$zip;
                $this->update_task();
                return $filename;
            }
            else
            {
                foreach ($this->task['jobs'][$this->current_job]['zip_file'] as $zip)
                {
                    if( $zip['finished']==0)
                    {
                        return $zip['filename'];
                    }
                }

                return false;
            }

        }
        else
        {
            return false;
        }
    }

    public function add_zip_file($backup_type)
    {
        if($this->current_job!==false)
        {
            $this->task['jobs'][$this->current_job]['file_index']++;
            $file_prefix=$this->task['options']['file_prefix'];
            if($backup_type=='backup_merge')
            {
                $backup_type='backup_all';
                $this->task['jobs'][$this->current_job]['child_file']=array();
            }
            $filename=$file_prefix.'_'.$backup_type.'.part'.sprintf('%03d',($this->task['jobs'][$this->current_job]['file_index'])).'.zip';

            $zip['filename']=$filename;
            $zip['finished']=0;
            $this->task['jobs'][$this->current_job]['zip_file'][$filename]=$zip;
            $this->task['status']['resume_count']=0;
            $this->update_task();
            $this->set_time_limit();
            return $filename;
        }
        else
        {
            return false;
        }
    }

    public function update_zip_file($zip_name,$finished,$json=array())
    {
        if($this->current_job!==false)
        {
            if($json!==false)
                $this->add_json_file($zip_name,$json);
            $this->task['jobs'][$this->current_job]['zip_file'][$zip_name]['finished']=$finished;
            $this->task['jobs'][$this->current_job]['zip_file'][$zip_name]['json']=$json;
            $this->update_task();
        }
    }

    public function add_json_file($zip_name,$json)
    {
        $zip_method=isset($this->task['setting']['zip_method'])?$this->task['setting']['zip_method']:'ziparchive';
        $zip=new WPvivid_Zip($zip_method);

        $path=WP_CONTENT_DIR.DIRECTORY_SEPARATOR.WPvivid_Setting::get_backupdir().DIRECTORY_SEPARATOR;
        return $zip->add_json_file($path.$zip_name,$json);
    }

    public function _backup_files_use_cache($backup_type,$replace_path)
    {
        global $wpvivid_plugin;

        $max_zip_file_size= $this->task['setting']['max_file_size']*1024*1024;
        $add_files_count=$this->task['setting']['compress_file_count'];
        $path=$this->task['options']['dir'].'/';
        $files_cache_list=$this->get_files_cache_list();

        $zip_method=isset($this->task['setting']['zip_method'])?$this->task['setting']['zip_method']:'ziparchive';
        $zip=new WPvivid_Zip($zip_method);

        if($zip_method=='ziparchive')
        {
            if($zip->check_ziparchive_available())
            {
                $use_pclzip=false;
            }
            else
            {
                $use_pclzip=true;
            }
        }
        else
        {
            $use_pclzip=true;
        }


        $zip_file_name=$path.$this->get_zip_file($backup_type);
        $json=array();
        $json=$this->get_json_info($backup_type,$json);

        $numItems = count($files_cache_list);
        $i = 0;

        foreach ($files_cache_list as $cache_file)
        {
            $i++;

            if($cache_file['finished']==1)
                continue;
            $eof=false;
            while(!$eof)
            {
                if ($this->check_cancel_backup())
                {
                    die();
                }

                if ($use_pclzip)
                {
                    $files_cache = $this->get_files_from_cache_by_size($cache_file['name'], $cache_file['index'], $max_zip_file_size, $use_pclzip);
                    $eof = $files_cache['eof'];
                    $files = $files_cache['files'];
                    $cache_file['index'] = $files_cache['index'];

                    $wpvivid_plugin->wpvivid_log->WriteLog('Compressing zip file:' . basename($zip_file_name) . ' index:' . $cache_file['index'], 'notice');
                    $zip->add_files($zip_file_name, $replace_path, $files, true, $json);
                    //$cache_file['index'] += $add_files_count;
                    $wpvivid_plugin->wpvivid_log->WriteLog('Compressing zip file:' . basename($zip_file_name) . ' success. index:' . $cache_file['index'] . ' file size:' . size_format(filesize($zip_file_name), 2), 'notice');

                    $this->update_zip_file(basename($zip_file_name), 1, false);
                    $this->update_files_cache($cache_file);
                    if ($i === $numItems && $eof)
                    {
                        continue;
                    }

                    $zip_file_name = $path . $this->add_zip_file($backup_type);
                }
                else
                {
                    //$files_cache = $this->get_files_from_cache($cache_file['name'], $cache_file['index'], $add_files_count);
                    $files_cache = $this->get_files_from_cache_by_size($cache_file['name'], $cache_file['index'], $max_zip_file_size, $use_pclzip);
                    $eof = $files_cache['eof'];
                    $files = $files_cache['files'];
                    $cache_file['index'] = $files_cache['index'];
                    $wpvivid_plugin->wpvivid_log->WriteLog('Compressing zip file:' . basename($zip_file_name) . ' index:' . $cache_file['index'], 'notice');
                    $zip->add_files($zip_file_name, $replace_path, $files);
                    //$cache_file['index'] += $add_files_count;
                    $this->update_files_cache($cache_file);
                    $wpvivid_plugin->wpvivid_log->WriteLog('Compressing zip file:' . basename($zip_file_name) . ' success. index:' . $cache_file['index'] . ' file size:' . size_format(filesize($zip_file_name), 2), 'notice');

                    if ($i === $numItems && $eof)
                    {
                        continue;
                    }

                    $this->update_zip_file(basename($zip_file_name), 1, $json);
                    $zip_file_name = $path . $this->add_zip_file($backup_type);

                    /*if ($max_zip_file_size !== 0 && (filesize($zip_file_name) > $max_zip_file_size))
                    {
                        $this->update_zip_file(basename($zip_file_name), 1, $json);
                        $zip_file_name = $path . $this->add_zip_file($backup_type);
                    }*/
                }
            }
            $cache_file['finished']=1;
            $this->update_files_cache($cache_file);
        }

        if(!$use_pclzip)
        {
            $this->update_zip_file(basename($zip_file_name),1,$json);
        }

        $this->clean_tmp_files();

        $ret['result']='success';
        return $ret;
    }

    public function get_json_info($backup_type,$json)
    {
        global $wpdb;
        if($backup_type=='backup_themes')
        {
            $json['file_type']='themes';
            $json['root_flag']='wp-content';
            $json['php_version']=phpversion();
            $json['mysql_version']=$wpdb->db_version();
            $json['wp_version'] = get_bloginfo( 'version' );
            $json['themes']=$this->get_themes_list();
        }
        else if($backup_type=='backup_plugin')
        {
            $json['file_type']='plugin';
            $json['root_flag']='wp-content';
            $json['php_version']=phpversion();
            $json['mysql_version']=$wpdb->db_version();
            $json['wp_version'] = get_bloginfo( 'version' );
            $json['plugin']=$this->get_plugins_list();
        }
        else if($backup_type=='backup_uploads')
        {
            $json['file_type']='upload';
            $json['root_flag']='wp-content';
            $json['php_version']=phpversion();
            $json['mysql_version']=$wpdb->db_version();
            $json['wp_version'] = get_bloginfo( 'version' );
        }
        else if($backup_type=='backup_content')
        {
            $json['file_type']='wp-content';
            $json['root_flag']='wp-content';
            $json['php_version']=phpversion();
            $json['mysql_version']=$wpdb->db_version();
            $json['wp_version'] = get_bloginfo( 'version' );
        }
        else if($backup_type=='backup_core')
        {
            $json['file_type']='wp-core';
            $json['include_path'][]='wp-includes';
            $json['include_path'][]='wp-admin';
            $json['wp_core']=1;
            $json['root_flag']='root';
            $json['home_url']=home_url();
        }
        else if($backup_type=='backup_db')
        {
            global $wpdb;
            $json['dump_db']=1;
            $json['file_type']='databases';
            if(isset($this->task['options']['site_id']))
            {
                $json['site_id']=$this->task['options']['site_id'];
                global $wpdb;
                $site_prefix= $wpdb->get_blog_prefix($this->task['options']['site_id']);
                $json['home_url']=get_home_url($this->task['options']['site_id']);
                $json['site_url']=get_site_url($this->task['options']['site_id']);
                $json['blog_prefix']=$site_prefix;
                $json['mu_migrate']=1;
                $json['base_prefix']=$wpdb->get_blog_prefix(0);
            }
            else
            {
                $json['home_url']=home_url();
            }

            $json['root_flag']='custom';
            $json['php_version']=phpversion();
            $json['mysql_version']=$wpdb->db_version();
            $json['wp_version'] = get_bloginfo( 'version' );
            if(is_multisite())
            {
                $json['is_mu']=1;
            }

            //encrypt_db
            if(isset($this->task['options']['encrypt_db'])&&$this->task['options']['encrypt_db'])
            {
                $json['is_crypt']=1;
            }

        }
        else if($backup_type=='backup_merge')
        {
            $json['has_child']=1;
            $json['home_url']=home_url();
            $json['root_flag']='custom';
            $json['php_version']=phpversion();
            $json['mysql_version']=$wpdb->db_version();
            $json['wp_version'] = get_bloginfo( 'version' );
            $json['child_file']=array();
            foreach ($this->task['jobs'][$this->current_job]['child_file'] as $file=>$child_json)
            {
                $json['child_file'][$file]=$child_json;
            }
        }

        return $json;
    }

    public function get_themes_list()
    {
        $themes_list=array();
        $list=wp_get_themes();
        foreach ($list as $key=>$item)
        {
            $path=$this -> transfer_path(get_theme_root().'/'.$key);

            if($this->regex_match($this->task['options']['exclude_files'],$path, 0))
            {
                $themes_list[$key]['slug']=$key;
            }
        }
        return $themes_list;
    }

    public function get_plugins_list()
    {
        $plugins_list=array();
        if(!function_exists('get_plugins'))
            require_once(ABSPATH . 'wp-admin/includes/plugin.php');
        $list=get_plugins();

        foreach ($list as $key=>$item)
        {
            if(dirname($key)=='.')
                continue;

            $path=$this -> transfer_path(WP_PLUGIN_DIR.'/'.$key);

            if($this->regex_match($this->task['options']['exclude_files'],$path, 0))
            {
                $plugins_list[dirname($key)]['slug']=dirname($key);
            }
        }
        return $plugins_list;
    }

    public function get_files($root_path,$backup_symlink_folder=false,$exclude_files=array(),$include_files=array())
    {
        $files=array();
        $exclude_regex=array_merge($this->task['options']['exclude_files'],$exclude_files);
        $exclude_file_size=$this->task['setting']['exclude_file_size'];
        $root_path=untrailingslashit($root_path);

        if(isset($this->task['options']['incremental_options']))
        {
            $skip_files_time=$this->task['options']['incremental_options']['versions']['skip_files_time'];
        }
        else
        {
            $skip_files_time=0;
        }

        $this->_get_files($root_path,$files,$backup_symlink_folder,$exclude_regex,$include_files,$exclude_file_size,$skip_files_time);

        return $files;
    }

    public function _get_files($path,&$files,$backup_symlink_folder,$exclude_regex,$include_regex,$exclude_file_size,$skip_files_time)
    {
        $handler = opendir($path);

        if($handler===false)
            return;

        while (($filename = readdir($handler)) !== false)
        {
            if ($filename != "." && $filename != "..")
            {
                if (is_dir($path . '/' . $filename))
                {
                    if($backup_symlink_folder == 1 || ($backup_symlink_folder == 0 && !@is_link($path . '/' . $filename)))
                    {
                        if($this->regex_match($exclude_regex, $this->transfer_path($path . '/' . $filename), 0))
                        {
                            if ($this->regex_match($include_regex, $path . '/' . $filename, 1))
                            {
                                $this->_get_files($path . '/' . $filename,$files,$backup_symlink_folder,$exclude_regex,$include_regex,$exclude_file_size,$skip_files_time);
                            }
                        }
                    }
                }
                else
                {
                    if(is_readable($path . '/' . $filename))
                    {
                        if($backup_symlink_folder == 1 || ($backup_symlink_folder == 0 && !@is_link($path . '/' . $filename)))
                        {
                            if($skip_files_time>0)
                            {
                                $file_time=filemtime($path . '/' . $filename);
                                if($file_time>0&&$file_time>$skip_files_time)
                                {
                                    if ($exclude_file_size == 0)
                                    {
                                        if($this->regex_match($exclude_regex, $this->transfer_path($path . '/' . $filename), 0))
                                        {
                                            $files[]=$this->transfer_path($path . '/' . $filename);
                                        }
                                    }
                                    else
                                    {
                                        if($this->regex_match($exclude_regex, $this->transfer_path($path . '/' . $filename), 0))
                                        {
                                            if (filesize($path . '/' . $filename) < $exclude_file_size * 1024 * 1024)
                                            {
                                                $files[]=$this->transfer_path($path . '/' . $filename);
                                            }
                                        }
                                    }
                                }
                            }
                            else
                            {
                                if ($exclude_file_size == 0)
                                {
                                    if($this->regex_match($exclude_regex, $this->transfer_path($path . '/' . $filename), 0))
                                    {
                                        $files[]=$this->transfer_path($path . '/' . $filename);
                                    }
                                }
                                else
                                {
                                    if($this->regex_match($exclude_regex, $this->transfer_path($path . '/' . $filename), 0))
                                    {
                                        if (filesize($path . '/' . $filename) < $exclude_file_size * 1024 * 1024)
                                        {
                                            $files[]=$this->transfer_path($path . '/' . $filename);
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
        if($handler)
            @closedir($handler);

        return;
    }

    public function get_merge_files($root_path)
    {
        $files=array();
        foreach ($this->task['jobs'] as $job)
        {
            if($job['backup_type']=='backup_merge')
                continue;

            if(isset($job['zip_file']))
            {
                foreach ($job['zip_file'] as $zip)
                {
                    if( $zip['finished']!=0)
                    {
                        $files[]=$root_path.'/'.$zip['filename'];
                    }
                }
            }
        }

        return $files;
    }

    private function regex_match($regex_array,$string,$mode)
    {
        if(empty($regex_array))
        {
            return true;
        }

        if($mode==0)
        {
            foreach ($regex_array as $regex)
            {
                if(preg_match($regex,$string))
                {
                    return false;
                }
            }

            return true;
        }

        if($mode==1)
        {
            foreach ($regex_array as $regex)
            {
                if(preg_match($regex,$string))
                {
                    return true;
                }
            }

            return false;
        }

        return true;
    }

    public function _backup_files($files,$replace_path,$backup_type)
    {
        global $wpvivid_plugin;
        $max_zip_file_size= $this->task['setting']['max_file_size']*1024*1024;
        $add_files_count=$this->task['setting']['compress_file_count'];
        $path=$this->task['options']['dir'].'/';

        $zip_method=isset($this->task['setting']['zip_method'])?$this->task['setting']['zip_method']:'ziparchive';
        $zip=new WPvivid_Zip($zip_method);

        if($zip_method=='ziparchive')
        {
            if($zip->check_ziparchive_available())
            {
                $use_pclzip=false;
            }
            else
            {
                $use_pclzip=true;
            }
        }
        else
        {
            $use_pclzip=true;
        }

        $zip_file_name=$path.$this->get_zip_file($backup_type);
        $json=array();
        $json=$this->get_json_info($backup_type,$json);
        $eof=false;
        $index=$this->get_zipped_file_index();
        while(!$eof)
        {
            if($this->check_cancel_backup())
            {
                die();
            }

            if($use_pclzip)
            {
                $files_count=$this->get_files_size($files,$index,$max_zip_file_size,$use_pclzip);
                $eof=$files_count['eof'];
                $index=$files_count['index'];
                $wpvivid_plugin->wpvivid_log->WriteLog('Compressing zip file:'.basename($zip_file_name).' index:'.$index,'notice');
                $ret=$zip->add_files($zip_file_name,$replace_path,$files_count['files'],true,$json);
                $wpvivid_plugin->wpvivid_log->WriteLog('Compressing zip file:'.basename($zip_file_name).' success. index:'.$index.' file size:'.size_format(filesize($zip_file_name),2),'notice');

                if($ret['result']!='success')
                {
                    return $ret;
                }

                $this->update_zip_file(basename($zip_file_name),1,false);
                $this->update_zipped_file_index($index);

                if($eof)
                {
                    continue;
                }

                $zip_file_name=$path.$this->add_zip_file($backup_type);
            }
            else
            {
                //$files_count=$this->get_files_count($files,$index,$add_files_count);
                $files_count=$this->get_files_size($files,$index,$max_zip_file_size,$use_pclzip);
                $eof=$files_count['eof'];
                //$index+=$add_files_count;
                $index=$files_count['index'];
                $wpvivid_plugin->wpvivid_log->WriteLog('Compressing zip file:'.basename($zip_file_name).' index:'.$index,'notice');
                $ret=$zip->add_files($zip_file_name,$replace_path,$files_count['files']);
                $wpvivid_plugin->wpvivid_log->WriteLog('Compressing zip file:'.basename($zip_file_name).' success. index:'.$index.' file size:'.size_format(filesize($zip_file_name),2),'notice');
                if($ret['result']!='success')
                {
                    return $ret;
                }

                $this->update_zipped_file_index($index);

                if($eof)
                {
                    continue;
                }

                $this->update_zip_file(basename($zip_file_name),1,$json);
                $zip_file_name=$path.$this->add_zip_file($backup_type);
            }
        }

        if(!$use_pclzip)
            $this->update_zip_file(basename($zip_file_name),1,$json);

        $ret['result']='success';
        return $ret;
    }

    public function do_backup_db()
    {
        if(!class_exists('WPvividTypeAdapterFactory'))
        {
            include_once WPVIVID_PLUGIN_DIR . '/includes/class-wpvivid-mysqldump-method.php';
        }
        $this->task['dump_setting']=$this->init_db_backup_setting();
        $dump = new WPvivid_Mysqldump2($this,$this->task['dump_setting']);
        $dump->connect();
        if(!isset($this->task['jobs'][$this->current_job]['sub_jobs']))
        {
            $ret=$dump->init_job();
            if($ret===false)
            {
                $ret['result']='failed';
                $ret['error']='tables not found.';
                return $ret;
            }
        }

        if($this->check_cancel_backup())
        {
            die();
        }

        global $wpvivid_plugin;
        $wpvivid_plugin->wpvivid_log->WriteLog('Start exporting database.','notice');
        $ret= $dump->start_jobs();
        if($ret['result']=='success')
        {
            $wpvivid_plugin->wpvivid_log->WriteLog('Exporting database finished.','notice');
            $files=$this->get_mysql_dump_files();
            $jobs=$this->get_current_sub_job();
            $tables=array();
            foreach ( $jobs as $job)
            {
                $table['name']=$job['name'];
                $table['size']=$job['size'];
                $table['rows']=$job['rows'];
                $tables[]=$table;
            }
            $wpvivid_plugin->wpvivid_log->WriteLog('Start compressing database','notice');
            $find_zero_date=$dump->is_has_zero_date();
            $ret=$this->zip_mysql_dump_files($files,$tables,$find_zero_date);
            $wpvivid_plugin->wpvivid_log->WriteLog('Compressing database completed','notice');
        }

        $this->clean_tmp_files();

        return $ret;
    }

    public function get_current_mysql_file_index()
    {
        if($this->current_job!==false)
        {
            return $this->task['jobs'][$this->current_job]['mysql_file_index'];
        }
        else
        {
            return 1;
        }
    }

    public function reset_mysql_file_index()
    {
        $this->task['jobs'][$this->current_job]['mysql_file_index']=1;
        $this->update_task();
    }

    public function get_mysql_dump_files()
    {
        if($this->current_job!==false)
        {
            $files=$this->task['jobs'][$this->current_job]['mysql_dump_files'];
            if(count($files)==1)
            {
                $path=$this->task['options']['dir'].'/';

                $file=array_shift($files);
                if($this->task['jobs'][$this->current_job]['backup_type']=='backup_additional_db')
                {
                    $new_file=$this->task['options']['file_prefix'].'_backup_additional_db.sql';
                }
                else
                {
                    $new_file=$this->task['options']['file_prefix'].'_backup_db.sql';
                }

                rename($path.$file,$path.$new_file);

                $dump_files=array();
                $dump_files[]=$path.$new_file;
            }
            else
            {
                $dump_files=array();
                $path=$this->task['options']['dir'].'/';

                foreach ($files as $file)
                {
                    $dump_files[]=$path.$file;
                }
            }

            return $dump_files;
        }
        else
        {
            return array();
        }

    }

    public function add_mysql_dump_files($name_file_name)
    {
        if($this->current_job!==false)
        {
            $this->task['jobs'][$this->current_job]['mysql_dump_files'][]=$name_file_name;
            $this->task['jobs'][$this->current_job]['mysql_file_index']++;
            $this->update_task();
        }
    }

    public function get_file_index()
    {
        if($this->current_job!==false)
        {
            return $this->task['jobs'][$this->current_job]['file_index'];
        }
        else
        {
            return 1;
        }
    }

    public function get_zipped_file_index()
    {
        if($this->current_job!==false)
        {
            return $this->task['jobs'][$this->current_job]['index'];
        }
        else
        {
            return 0;
        }
    }

    public function update_zipped_file_index($index)
    {
        if($this->current_job!==false)
        {
            $this->task['jobs'][$this->current_job]['index']=$index;
            $this->task['status']['resume_count']=0;
            $this->update_task();
        }
    }

    public function update_merge_zipped_file_index($index,$file,$json)
    {
        if($this->current_job!==false)
        {
            $this->task['jobs'][$this->current_job]['index']=$index;
            $this->task['status']['resume_count']=0;
            $this->task['jobs'][$this->current_job]['child_file'][$file]=$json;
            $this->update_task();
        }
    }

    public function zip_mysql_dump_files($files,$tables,$find_zero_date=false)
    {
        foreach ($files as $file)
        {
            $json['files'][]=basename($file);
        }
        $json['tables']=$tables;
        $json=$this->get_json_info('backup_db',$json);
        $max_zip_file_size= $this->task['setting']['max_file_size']*1024*1024;
        $path=$this->task['options']['dir'].'/';

        $zip_method=isset($this->task['setting']['zip_method'])?$this->task['setting']['zip_method']:'ziparchive';
        $zip=new WPvivid_Zip($zip_method);

        $zip_file_name=$path.$this->get_zip_file('backup_db');

        $numItems = count($files);
        $i = 0;

        foreach ($files as $file)
        {
            $ret=$zip->add_file($zip_file_name,$file,basename($file),dirname($file));
            if($ret['result']!='success')
            {
                return $ret;
            }

            if(++$i === $numItems)
            {
                continue;
            }

            if($max_zip_file_size !== 0 && (filesize($zip_file_name)>$max_zip_file_size))
            {
                $this->update_zip_file(basename($zip_file_name),1,$json);
                $zip_file_name=$path.$this->add_zip_file('backup_db');
            }
        }

        if($find_zero_date)
        {
            $json['find_zero_date']=1;
        }

        $this->update_zip_file(basename($zip_file_name),1,$json);
        $ret['result']='success';
        return $ret;
    }

    public function update_current_sub_job($jobs)
    {
        if($this->current_job!==false)
        {
            $this->task['jobs'][$this->current_job]['sub_jobs']=$jobs;
            $this->update_task();
        }
    }

    public function get_current_sub_job()
    {
        if($this->current_job!==false)
        {
            return $this->task['jobs'][$this->current_job]['sub_jobs'];
        }
        else
        {
            return false;
        }
    }

    public function init_db_backup_setting()
    {
        global $wpdb;

        $dump_setting['database'] = DB_NAME;
        $dump_setting['host'] = DB_HOST;
        $dump_setting['user'] = DB_USER;
        $dump_setting['pass'] = DB_PASSWORD;

        $dump_setting['site_url']=get_site_url();
        $dump_setting['home_url']=get_home_url();

        $dump_setting['content_url']=content_url();

        $dump_setting['prefix'] = $wpdb->get_blog_prefix(0);

        $db_connect_method = isset($this->task['setting']['db_connect_method']) ? $this->task['setting']['db_connect_method'] : 'wpdb';
        if ($db_connect_method === 'wpdb')
        {
            $dump_setting['db_connect_method']='wpdb';
        }
        else
        {
            $dump_setting['db_connect_method']='mysql';
        }

        $dump_setting['file_prefix']=$this->task['options']['file_prefix'];
        $dump_setting['path']=$this->task['options']['dir'];
        $dump_setting['max_file_size']=$this->task['setting']['max_sql_file_size']*1024*1024;

        $dump_setting['exclude-tables']=isset($this->task['options']['exclude-tables'])?$this->task['options']['exclude-tables']:array();
        $dump_setting['include-tables']=isset($this->task['options']['include-tables'])?$this->task['options']['include-tables']:array();
        return $dump_setting;
    }

    public function update_status($status)
    {
        $this->task['status']['str']=$status;
        $this->task['status']['run_time']=time();
        WPvivid_Setting::update_task($this->task_id,$this->task);
    }

    public function get_status()
    {
        return $this->task['status'];
    }

    public function set_time_limit()
    {
        //max_execution_time
        @set_time_limit( $this->task['setting']['max_execution_time']);
        $this->task['status']['timeout']=time();
        $this->update_task();
    }

    public function get_time_limit()
    {
        return $this->task['setting']['max_execution_time'];
    }

    public function get_max_resume_count()
    {
        return $this->task['setting']['max_resume_count'];
    }

    public function update_backup_task_status($reset_start_time=false,$status='',$reset_timeout=false,$resume_count=false,$error='')
    {
        $this->task['status']['run_time']=time();
        if($reset_start_time)
            $this->task['status']['start_time']=time();
        if(!empty($status))
        {
            $this->task['status']['str']=$status;
        }
        if($reset_timeout)
            $this->task['status']['timeout']=time();
        if($resume_count!==false)
        {
            $this->task['status']['resume_count']=$resume_count;
        }

        if(!empty($error))
        {
            $this->task['status']['error']=$error;
        }

        $this->update_task();
    }

    public function get_setting()
    {
        return $this->task['setting'];
    }

    public function get_unfinished_job()
    {
        $job=false;
        if(!$this->is_backup_finished())
        {
            $job_key=$this->get_next_job();
            if($job_key!==false)
            {
                $job=$this->task['jobs'][$job_key];
            }
        }

        return $job;
    }

    public function clean_zip_files()
    {
        if($this->current_job!==false)
        {
            if(isset($this->task['jobs'][$this->current_job]['zip_file']))
            {
                $path=$this->task['options']['dir'].'/';

                foreach ($this->task['jobs'][$this->current_job]['zip_file'] as $zip)
                {
                    @wp_delete_file($path.$zip['filename']);
                }

                unset($this->task['jobs'][$this->current_job]['zip_file']);
                $this->task['jobs'][$this->current_job]['file_index']=1;
                $this->update_task();
            }
        }
    }

    public function need_upload()
    {
        //remote_options
        if($this->task['options']['remote_options']===false)
        {
            return false;
        }
        else
        {
            return true;
        }
    }

    public function is_upload_finished()
    {
        $b_finished=true;

        if(array_key_exists('upload',$this->task['data']))
        {
            foreach ($this->task['data']['upload']['sub_job'] as $upload_job)
            {
                if($upload_job['finished']!=1)
                {
                    $b_finished=false;
                    break;
                }
            }
        }
        else
        {
            $b_finished=false;
        }

        return $b_finished;
    }

    public function get_remote_options()
    {
        return $this->task['options']['remote_options'];
    }

    public function get_backup_files()
    {
        $files=array();
        $root_path=$this -> transfer_path(WP_CONTENT_DIR.'/'.WPvivid_Setting::get_backupdir());
        foreach ($this->task['jobs'] as $job)
        {
            if($job['backup_type']=='backup_merge')
            {
                $files=array();
                if(isset($job['zip_file']))
                {
                    foreach ($job['zip_file'] as $zip)
                    {
                        $files[]=$root_path.'/'.$zip['filename'];
                    }
                }
                break;
            }

            if(isset($job['zip_file']))
            {
                foreach ($job['zip_file'] as $zip)
                {
                    $files[]=$root_path.'/'.$zip['filename'];
                }
            }
        }

        return $files;
    }

    public function update_backup_result()
    {
        $files=$this->get_backup_files();
        $backup_result['result']['result']='success';

        if(!empty($files))
        {
            foreach ($files as $file)
            {
                $file_data['file_name'] = basename($file);
                $file_data['size'] = filesize($file);
                $backup_result['result']['files'][] =$file_data;
            }
        }
        $is_merge=$this->task['setting']['is_merge'];
        if($is_merge==1)
        {
            $backup_result['key']='backup_merge';
        }

        $this->task['options']['backup_options']['ismerge']=$is_merge;
        $this->task['options']['backup_options']['backup'][]=$backup_result;
        $this->update_task();

    }

    public function add_new_backup()
    {
        $files=$this->get_backup_files();

        if(empty($files))
        {
            return;
        }

        $backup_data=array();
        $backup_data['type']=$this->task['type'];
        $backup_data['create_time']=$this->task['status']['start_time'];
        $backup_data['manual_delete']=0;
        $backup_data['local']['path']=$this->task['options']['backup_dir'];
        $backup_data['compress']['compress_type']='zip';
        $backup_data['save_local']=$this->task['options']['save_local'];
        if(isset($this->task['options']['backup_prefix']))
        {
            $backup_data['backup_prefix'] = $this->task['options']['backup_prefix'];
        }

        $backup_data['log']=$this->task['options']['log_file_path'];

        $backup_result['result']='success';

        foreach ($files as $file)
        {
            $file_data['file_name'] = basename($file);
            $file_data['size'] = filesize($file);
            $backup_result['files'][] =$file_data;
        }
        $backup_data['backup']=$backup_result;
        $backup_data['remote']=array();

        if(isset($this->task['options']['lock']))
        {
            $backup_data['lock'] = $this->task['options']['lock'];
        }

        $backup_list='wpvivid_backup_list';

        $backup_list=apply_filters('get_wpvivid_backup_list_name',$backup_list,$this->task['id']);

        $list = WPvivid_Setting::get_option($backup_list);
        $list[$this->task['id']]=$backup_data;
        WPvivid_Setting::update_option($backup_list,$list);
    }

    public function set_remote_lock()
    {
        $backup_lock=get_option('wpvivid_remote_backups_lock');
        $backup_id = $this->task['id'];
        if(isset($this->task['options']['lock']))
        {
            $lock = $this->task['options']['lock'];
        }
        else
        {
            $lock = 0;
        }
        if($lock)
        {
            $backup_lock[$backup_id]=1;
        }
        else {
            unset($backup_lock[$backup_id]);
        }
        update_option('wpvivid_remote_backups_lock',$backup_lock,'no');
    }

    public function add_exist_backup($backup_id,$type='Common')
    {
        $files=$this->get_backup_files();

        if(empty($files))
        {
            return;
        }

        $backup=WPvivid_Backuplist::get_backup_by_id($backup_id);
        $backup_result['files']=array();
        foreach ($files as $file)
        {
            $file_data['file_name'] = basename($file);
            $file_data['size'] = filesize($file);
            $backup_result['files'][] =$file_data;
        }
        $backup['backup']['files']=array_merge($backup['backup']['files'],$backup_result['files']);
        WPvivid_Backuplist::update_backup($backup_id,'backup', $backup['backup']);
    }

    public function clean_backup()
    {
        if(empty($this->task_id))
        {
            return;
        }

        $path = $this->task['options']['dir'];
        $handler=opendir($path);
        if($handler!==false)
        {
            while(($filename=readdir($handler))!==false)
            {
                if(preg_match('#'.$this->task_id.'#',$filename) || preg_match('#'.apply_filters('wpvivid_fix_wpvivid_free', $this->task_id).'#',$filename))
                {
                    @wp_delete_file($path.'/'.$filename);
                }
            }
            @closedir($handler);
        }
    }

    public function get_backup_task_info()
    {
        $list_tasks['status']=$this->task['status'];
        $list_tasks['is_canceled']=$this->is_task_canceled();
        $list_tasks['data']=$this->get_backup_tasks_progress();
        //
        $list_tasks['task_info']['need_next_schedule']=false;
        if($list_tasks['status']['str']=='running'||$list_tasks['status']['str']=='no_responds')
        {
            if($list_tasks['data']['running_stamp']>180)
            {
                $list_tasks['task_info']['need_next_schedule'] = true;
            }
            else{
                $list_tasks['task_info']['need_next_schedule'] = false;
            }
        }

        $list_tasks['task_info']['display_estimate_backup'] = '';

        $list_tasks['task_info']['backup_percent']=$list_tasks['data']['progress'].'%';
        //
        $list_tasks['task_info']['db_size']=0;
        $list_tasks['task_info']['file_size']=0;

        $list_tasks['task_info']['descript']='';
        $list_tasks['task_info']['css_btn_cancel']='pointer-events: auto; opacity: 1;';
        $list_tasks['task_info']['css_btn_log']='pointer-events: auto; opacity: 1;';
        $list_tasks['task_info']['total'] = 'N/A';
        $list_tasks['task_info']['upload'] = 'N/A';
        $list_tasks['task_info']['speed'] = 'N/A';
        $list_tasks['task_info']['network_connection'] = 'N/A';

        $list_tasks['task_info']['need_update_last_task']=false;
        if($list_tasks['status']['str']=='ready')
        {
            $list_tasks['task_info']['descript']=__('Ready to backup. Progress: 0%, running time: 0second.','wpvivid-backuprestore');
            $list_tasks['task_info']['css_btn_cancel']='pointer-events: none; opacity: 0.4;';
            $list_tasks['task_info']['css_btn_log']='pointer-events: none; opacity: 0.4;';
        }
        else if($list_tasks['status']['str']=='running')
        {
            if($list_tasks['is_canceled'] == false)
            {
                if($list_tasks['data']['type'] == 'upload')
                {
                    if(isset($list_tasks['data']['upload_data']) && !empty($list_tasks['data']['upload_data']))
                    {
                        $descript = $list_tasks['data']['upload_data']['descript'];
                        $offset = $list_tasks['data']['upload_data']['offset'];
                        $current_size = $list_tasks['data']['upload_data']['current_size'];
                        $last_time = $list_tasks['data']['upload_data']['last_time'];
                        $last_size = $list_tasks['data']['upload_data']['last_size'];
                        $speed = ($offset - $last_size) / (time() - $last_time);
                        $speed /= 1000;
                        $speed = round($speed, 2);
                        $speed .= 'kb/s';
                        if(!empty($current_size)) {
                            $list_tasks['task_info']['total'] = size_format($current_size,2);
                        }
                        if(!empty($offset)) {
                            $list_tasks['task_info']['upload'] = size_format($offset, 2);
                        }
                    }
                    else{
                        $descript = 'Start uploading.';
                        $speed = '0kb/s';
                        $list_tasks['task_info']['total'] = 'N/A';
                        $list_tasks['task_info']['upload'] = 'N/A';
                    }

                    $list_tasks['task_info']['speed'] = $speed;
                    $list_tasks['task_info']['descript'] = $descript.' '.__('Progress: ', 'wpvivid-backuprestore') . $list_tasks['task_info']['backup_percent'] . ', ' . __('running time: ', 'wpvivid-backuprestore') . $list_tasks['data']['running_time'];

                    $time_spend=time()-$list_tasks['status']['run_time'];
                    if($time_spend>30)
                    {
                        $list_tasks['task_info']['network_connection']='Retrying';
                    }
                    else
                    {
                        $list_tasks['task_info']['network_connection']='OK';
                    }
                }
                else {
                    $list_tasks['task_info']['descript'] = $list_tasks['data']['descript'] . ' '. __('Progress: ', 'wpvivid-backuprestore') . $list_tasks['task_info']['backup_percent'] . ', '. __('running time: ', 'wpvivid-backuprestore') . $list_tasks['data']['running_time'];
                }
                $list_tasks['task_info']['css_btn_cancel']='pointer-events: auto; opacity: 1;';
                $list_tasks['task_info']['css_btn_log']='pointer-events: auto; opacity: 1;';
            }
            else{
                $list_tasks['task_info']['descript']=__('The backup will be canceled after backing up the current chunk ends.','wpvivid-backuprestore');
                $list_tasks['task_info']['css_btn_cancel']='pointer-events: none; opacity: 0.4;';
                $list_tasks['task_info']['css_btn_log']='pointer-events: auto; opacity: 1;';
            }
        }
        else if($list_tasks['status']['str']=='wait_resume')
        {
            $list_tasks['task_info']['descript']='Task '.$this->task_id.' timed out, backup task will retry in '.$list_tasks['data']['next_resume_time'].' seconds, retry times: '.$list_tasks['status']['resume_count'].'.';
            $list_tasks['task_info']['css_btn_cancel']='pointer-events: auto; opacity: 1;';
            $list_tasks['task_info']['css_btn_log']='pointer-events: auto; opacity: 1;';
        }
        else if($list_tasks['status']['str']=='no_responds')
        {
            if($list_tasks['is_canceled'] == false)
            {
                $list_tasks['task_info']['descript']='Task , '.$list_tasks['data']['doing'].' is not responding. Progress: '.$list_tasks['task_info']['backup_percent'].', running time: '.$list_tasks['data']['running_time'];
                $list_tasks['task_info']['css_btn_cancel']='pointer-events: auto; opacity: 1;';
                $list_tasks['task_info']['css_btn_log']='pointer-events: auto; opacity: 1;';
            }
            else{
                $list_tasks['task_info']['descript']=__('The backup will be canceled after backing up the current chunk ends.','wpvivid-backuprestore');
                $list_tasks['task_info']['css_btn_cancel']='pointer-events: none; opacity: 0.4;';
                $list_tasks['task_info']['css_btn_log']='pointer-events: auto; opacity: 1;';
            }
        }
        else if($list_tasks['status']['str']=='completed')
        {
            $list_tasks['task_info']['descript']='Task '.$this->task_id.' completed.';
            $list_tasks['task_info']['css_btn_cancel']='pointer-events: auto; opacity: 1;';
            $list_tasks['task_info']['css_btn_log']='pointer-events: auto; opacity: 1;';
            $list_tasks['task_info']['need_update_last_task']=true;
        }
        else if($list_tasks['status']['str']=='error')
        {
            $list_tasks['task_info']['descript']='Backup error: '.$list_tasks['status']['error'];
            $list_tasks['task_info']['css_btn_cancel']='pointer-events: auto; opacity: 1;';
            $list_tasks['task_info']['css_btn_log']='pointer-events: auto; opacity: 1;';
            $list_tasks['task_info']['need_update_last_task']=true;
        }

        return $list_tasks;
    }

    public function is_task_canceled()
    {
        $file_name=$this->task['options']['file_prefix'];

        $file =$this->task['options']['dir'].'/'. $file_name . '_cancel';

        if (file_exists($file))
        {
            return true;
        }
        return false;
    }

    public function check_cancel_backup()
    {
        if($this->is_task_canceled())
        {
            global $wpvivid_plugin;
            $wpvivid_plugin->wpvivid_log->WriteLog('Backup cancelled.','notice');

            $this->update_status('cancel');
            $this->clean_tmp_files();

            $tasks=WPvivid_Setting::get_option('wpvivid_clean_task_2');
            $tasks[$this->task_id]=$this->task;
            WPvivid_Setting::update_option('wpvivid_clean_task_2',$tasks);

            $resume_time=time()+60;

            $b=wp_schedule_single_event($resume_time,'wpvivid_clean_backup_data_event_2',array($this->task_id));

            if($b===false)
            {
                $timestamp = wp_next_scheduled('wpvivid_clean_backup_data_event_2',array($this->task_id));

                if($timestamp!==false)
                {
                    $resume_time=max($resume_time,$timestamp+10*60+10);
                    wp_schedule_single_event($resume_time,'wpvivid_clean_backup_data_event_2',array($this->task_id));
                }
            }

            $timestamp =wp_next_scheduled('wpvivid_task_monitor_event',array($this->task_id));
            if($timestamp!==false)
            {
                wp_unschedule_event($timestamp,'wpvivid_task_monitor_event',array($this->task_id));
            }
            wp_cache_flush();
            WPvivid_taskmanager::delete_task($this->task_id);
            wp_cache_flush();

            $this->wpvivid_check_clear_litespeed_rule();

            return true;
        }
        else
        {
            return false;
        }
    }

    public function get_backup_tasks_progress()
    {
        $current_time=gmdate("Y-m-d H:i:s");
        $create_time=gmdate("Y-m-d H:i:s",$this->task['status']['start_time']);
        $time_diff=strtotime($current_time)-strtotime($create_time);
        $running_time='';
        if(gmdate("G",$time_diff) > 0){
            $running_time .= gmdate("G",$time_diff).' hour(s)';
        }
        if(intval(gmdate("i",$time_diff)) > 0){
            $running_time .= intval(gmdate("i",$time_diff)).' min(s)';
        }
        if(intval(gmdate("s",$time_diff)) > 0){
            $running_time .= intval(gmdate("s",$time_diff)).' second(s)';
        }
        $next_resume_time=$this->get_next_resume_time();

        $ret['type']=$this->task['data']['doing'];
        $ret['progress']=$this->task['data'][$ret['type']]['progress'];
        $ret['doing']=$this->task['data'][$ret['type']]['doing'];
        if(isset($this->task['data'][$ret['type']]['sub_job'][$ret['doing']]['progress']))
        {
            $ret['descript']=$this->task['data'][$ret['type']]['sub_job'][$ret['doing']]['progress'];
        }
        else
        {
            $ret['descript']='';
        }
        if(isset($this->task['data'][$ret['type']]['sub_job'][$ret['doing']]['upload_data']))
            $ret['upload_data']=$this->task['data'][$ret['type']]['sub_job'][$ret['doing']]['upload_data'];
        $this->task['data'][$ret['type']]['sub_job'][$ret['doing']]['upload_data']=false;
        $ret['running_time']=$running_time;
        $ret['running_stamp']=$time_diff;
        $ret['next_resume_time']=$next_resume_time;
        return $ret;
    }

    public function get_next_resume_time()
    {
        $timestamp=wp_next_scheduled(WPVIVID_RESUME_SCHEDULE_EVENT,array($this->task_id));
        if($timestamp!==false)
        {
            return $timestamp-time();
        }
        else
        {
            return false;
        }
    }

    public function clear_cache()
    {
        $path = WP_CONTENT_DIR.DIRECTORY_SEPARATOR.WPvivid_Setting::get_backupdir();
        $handler=opendir($path);
        if($handler!==false)
        {
            while(($filename=readdir($handler))!==false)
            {
                if(preg_match('#pclzip-.*\.tmp#', $filename)){
                    @wp_delete_file($path.DIRECTORY_SEPARATOR.$filename);
                }
                if(preg_match('#pclzip-.*\.gz#', $filename)){
                    @wp_delete_file($path.DIRECTORY_SEPARATOR.$filename);
                }
            }
            @closedir($handler);
        }
    }

    public function is_save_local()
    {
        return isset($this->task['options']['save_local'])?$this->task['options']['save_local']:false;
    }

    public function clean_local_files()
    {
        $path = WP_CONTENT_DIR.DIRECTORY_SEPARATOR.WPvivid_Setting::get_backupdir();
        $handler=opendir($path);
        if($handler!==false)
        {
            while(($filename=readdir($handler))!==false)
            {
                if(preg_match('#'.$this->task_id.'#',$filename) || preg_match('#'.apply_filters('wpvivid_fix_wpvivid_free', $this->task_id).'#',$filename))
                {
                    @wp_delete_file($path.DIRECTORY_SEPARATOR.$filename);
                }
            }
            @closedir($handler);
        }
    }

    public function get_backup_jobs()
    {
        return $this->task['jobs'];
    }

    public function get_file_json($file)
    {
        if(!class_exists('WPvivid_ZipClass'))
            include_once WPVIVID_PLUGIN_DIR . '/includes/class-wpvivid-zipclass.php';
        $zip=new WPvivid_ZipClass();

        $ret=$zip->get_json_data($file);
        if($ret['result'] === WPVIVID_SUCCESS)
        {
            $json=$ret['json_data'];
            $json = json_decode($json, 1);
            if (is_null($json))
            {
                return false;
            } else {
                return $json;
            }
        }
        else
        {
            return array();
        }
    }
    //adaptive settings
    public function check_memory_limit()
    {
        $current_memory_limit=$this->task['setting']['memory_limit'];
        $current_memory_int = (int) filter_var($current_memory_limit, FILTER_SANITIZE_NUMBER_INT);
        if($current_memory_int<512)
        {
            $this->task['setting']['memory_limit']='512M';
            $this->update_task();
            return true;
        }
        else if($current_memory_int<1024)
        {
            $this->task['setting']['memory_limit']=($current_memory_int+100).'M';
            $this->update_task();
            return true;
        }
        else
        {
            return false;
        }
    }

    public function check_timeout()
    {
        $job=$this->get_unfinished_job();
        if($job!==false)
        {
            if($job['backup_type']=='backup_db'||$job['backup_type']=='backup_additional_db')
            {
                if($this->task['setting']['max_sql_file_size']>200)
                {
                    $this->task['setting']['max_sql_file_size']=200;
                }
                else
                {
                    $this->task['setting']['max_sql_file_size']=max(10,$this->task['setting']['max_sql_file_size']-50);
                }
                $this->update_task();
            }
            else
            {
                //if($this->task['setting']['compress_file_use_cache']==false)
                //{
                //    $this->task['setting']['compress_file_use_cache']=true;
                //}

                if($this->task['setting']['compress_file_count']>=1000)
                {
                    $this->task['setting']['compress_file_count']=800;
                }
                else if($this->task['setting']['compress_file_count']>=800)
                {
                    $this->task['setting']['compress_file_count']=500;
                }
                else if($this->task['setting']['compress_file_count']>=500)
                {
                    $this->task['setting']['compress_file_count']=300;
                }
                else
                {
                    $this->task['setting']['compress_file_count']=100;
                }

                if($this->task['setting']['max_file_size']>200)
                {
                    $this->task['setting']['max_file_size']=200;
                }

                if($this->task['setting']['exclude_file_size']==0)
                {
                    $this->task['setting']['exclude_file_size']=200;
                }
                $this->update_task();
            }
        }
    }

    public function check_execution_time()
    {
        $this->task['setting']['max_execution_time']=$this->task['setting']['max_execution_time']+120;
        $this->update_task();
    }

    public function check_timeout_backup_failed()
    {
        //$job=$this->get_unfinished_job();
        //if($job!==false)
        //{
            //if($job['backup_type']=='backup_merge')
            //{
                //$this->task['setting']['is_merge']=false;
            //}
        //}

        $max_resume_count=$this->get_max_resume_count();
        $status=$this->get_status();
        $status['resume_count']++;
        if($status['resume_count']>$max_resume_count)
        {
            $this->task['setting']['max_resume_count']=max(20,$this->task['setting']['max_resume_count']+3);
            $this->update_task();
        }
    }

    public function update_schedule_last_backup_time()
    {

    }

    public function wpvivid_check_add_litespeed_server()
    {
        $litespeed=false;
        if ( isset( $_SERVER['HTTP_X_LSCACHE'] ) && $_SERVER['HTTP_X_LSCACHE'] )
        {
            $litespeed=true;
        }
        elseif ( isset( $_SERVER['LSWS_EDITION'] ) && strpos( $_SERVER['LSWS_EDITION'], 'Openlitespeed' ) === 0 ) {
            $litespeed=true;
        }
        elseif ( isset( $_SERVER['SERVER_SOFTWARE'] ) && $_SERVER['SERVER_SOFTWARE'] == 'LiteSpeed' ) {
            $litespeed=true;
        }

        if($litespeed)
        {
            global $wpvivid_plugin;
            $wpvivid_plugin->wpvivid_log->WriteLog('LiteSpeed Server.','notice');

            if ( ! function_exists( 'got_mod_rewrite' ) )
            {
                require_once ABSPATH . 'wp-admin/includes/misc.php';
            }

            if(function_exists('insert_with_markers'))
            {
                if(!function_exists('get_home_path'))
                    require_once(ABSPATH . 'wp-admin/includes/file.php');
                $home_path     = get_home_path();
                $htaccess_file = $home_path . '.htaccess';

                if ( ( ! file_exists( $htaccess_file ) && is_writable( $home_path ) ) || is_writable( $htaccess_file ) )
                {
                    if ( got_mod_rewrite() )
                    {
                        $line[]='<IfModule Litespeed>';
                        $line[]='RewriteEngine On';
                        $line[]='RewriteRule .* - [E=noabort:1, E=noconntimeout:1]';
                        $line[]='</IfModule>';
                        insert_with_markers($htaccess_file,'WPvivid Rewrite Rule for LiteSpeed',$line);
                        $wpvivid_plugin->wpvivid_log->WriteLog('Add LiteSpeed Rule','notice');
                    }
                    else
                    {
                        $wpvivid_plugin->wpvivid_log->WriteLog('mod_rewrite not found.','notice');
                    }
                }
                else
                {
                    $wpvivid_plugin->wpvivid_log->WriteLog('.htaccess file not exists or not writable.','notice');
                }
            }
            else
            {
                $wpvivid_plugin->wpvivid_log->WriteLog('insert_with_markers function not exists.','notice');
            }
        }
    }

    public function wpvivid_check_clear_litespeed_rule()
    {
        $litespeed=false;
        if ( isset( $_SERVER['HTTP_X_LSCACHE'] ) && $_SERVER['HTTP_X_LSCACHE'] )
        {
            $litespeed=true;
        }
        elseif ( isset( $_SERVER['LSWS_EDITION'] ) && strpos( $_SERVER['LSWS_EDITION'], 'Openlitespeed' ) === 0 ) {
            $litespeed=true;
        }
        elseif ( isset( $_SERVER['SERVER_SOFTWARE'] ) && $_SERVER['SERVER_SOFTWARE'] == 'LiteSpeed' ) {
            $litespeed=true;
        }

        if($litespeed)
        {
            global $wpvivid_plugin;
            $wpvivid_plugin->wpvivid_log->WriteLog('LiteSpeed Server.','notice');

            if ( ! function_exists( 'got_mod_rewrite' ) )
            {
                require_once ABSPATH . 'wp-admin/includes/misc.php';
            }

            if(function_exists('insert_with_markers'))
            {
                if(!function_exists('get_home_path'))
                    require_once(ABSPATH . 'wp-admin/includes/file.php');
                $home_path     = get_home_path();
                $htaccess_file = $home_path . '.htaccess';

                if ( ( ! file_exists( $htaccess_file ) && is_writable( $home_path ) ) || is_writable( $htaccess_file ) )
                {
                    if ( got_mod_rewrite() )
                    {
                        insert_with_markers($htaccess_file,'WPvivid Rewrite Rule for LiteSpeed','');
                        $wpvivid_plugin->wpvivid_log->WriteLog('Clear LiteSpeed Rule','notice');
                    }
                    else
                    {
                        $wpvivid_plugin->wpvivid_log->WriteLog('mod_rewrite not found.','notice');
                    }
                }
                else
                {
                    $wpvivid_plugin->wpvivid_log->WriteLog('.htaccess file not exists or not writable.','notice');
                }
            }
            else
            {
                $wpvivid_plugin->wpvivid_log->WriteLog('insert_with_markers function not exists.','notice');
            }
        }
    }

    public function wpvivid_disable_litespeed_cache_for_backup()
    {
        if (defined('LSCWP_V'))
        {
            do_action( 'litespeed_disable_all', 'stop for backup' );
        }
    }
}includes/new_backup/class-wpvivid-restore2.php000064400000217524151327705670015567 0ustar00<?php

if (!defined('WPVIVID_PLUGIN_DIR'))
{
    die;
}

class WPvivid_Restore_2
{
    public $log;
    public $end_shutdown_function;

    public function __construct()
    {
        $this->log=false;
        $this->end_shutdown_function=false;

        add_action('wp_ajax_wpvivid_init_restore_task_2',array($this,'init_restore_task'));

        add_action('wp_ajax_wpvivid_do_restore_2',array($this,'do_restore'));
        add_action('wp_ajax_nopriv_wpvivid_do_restore_2',array( $this,'do_restore'));

        add_action('wp_ajax_wpvivid_get_restore_progress_2',array( $this,'get_restore_progress'));
        add_action('wp_ajax_nopriv_wpvivid_get_restore_progress_2',array( $this,'get_restore_progress'));

        add_action('wp_ajax_wpvivid_finish_restore_2',array( $this,'finish_restore'));
        add_action('wp_ajax_nopriv_wpvivid_finish_restore_2',array( $this,'finish_restore'));

        add_action('wp_ajax_wpvivid_restore_failed_2',array( $this,'restore_failed'));
        add_action('wp_ajax_nopriv_wpvivid_restore_failed_2',array( $this,'restore_failed'));

        include_once WPVIVID_PLUGIN_DIR . '/includes/new_backup/class-wpvivid-restore-file-2.php';
        include_once WPVIVID_PLUGIN_DIR . '/includes/new_backup/class-wpvivid-restore-db-2.php';
    }

    public function init_restore_task()
    {
        check_ajax_referer( 'wpvivid_ajax', 'nonce' );
        $check=current_user_can('manage_options');
        $check=apply_filters('wpvivid_ajax_check_security',$check);
        if(!$check)
        {
            die();
        }

        if(!isset($_POST['backup_id'])||empty($_POST['backup_id'])||!is_string($_POST['backup_id']))
        {
            die();
        }

        $backup_id=sanitize_key($_POST['backup_id']);

        $restore_options=array();
        if(isset($_POST['restore_options']))
        {
            foreach ($_POST['restore_options'] as $key=>$option)
            {
                $restore_options[$key]=sanitize_text_field($option);
            }
        }

        if(isset($restore_options['restore_version']))
        {
            $restore_version=$restore_options['restore_version'];
        }
        else
        {
            $restore_version=0;
        }

        $restore_options['restore_detail_options']=array();

        $ret=$this->create_restore_task($backup_id,$restore_options,$restore_version);

        $this->write_litespeed_rule();
        $this->deactivate_plugins();

        if(!file_exists(WPMU_PLUGIN_DIR.'/a-wpvivid-restore-mu-plugin-check.php'))
        {
            if(file_exists(WPMU_PLUGIN_DIR))
                copy(WPVIVID_PLUGIN_DIR . '/includes/mu-plugins/a-wpvivid-restore-mu-plugin-check.php',WPMU_PLUGIN_DIR.'/a-wpvivid-restore-mu-plugin-check.php');
        }

        echo wp_json_encode($ret);
        die();
    }

    public function create_restore_task($backup_id,$restore_options,$restore_version)
    {
        $restore_task=array();
        $restore_task['backup_id']=$backup_id;
        $restore_task['restore_options']=$restore_options;
        $restore_task['update_time']=time();
        $restore_task['restore_timeout_count']=0;
        $backup = WPvivid_Backuplist::get_backup_by_id($backup_id);
        if($backup===false)
        {
            $ret['result']='failed';
            $ret['error']='backup not found';
            return $ret;
        }


        $backup_item = new WPvivid_Backup_Item($backup);
        $backup_file_info=$this->get_restore_files_info($backup_item,$restore_version,true);
        $sub_tasks=array();

        $b_reset_plugin=false;

        foreach ($backup_file_info as $key=>$files_info)
        {
            $task['type']=$key;
            if(isset($restore_options[$key]))
                $task['options']=$restore_options[$key];
            else
                $task['options']=array();

            $task['options']['restore_reset']=true;
            if($key=='themes')
            {
                $task['priority']=1;
                $task['unzip_file']['files']=$files_info['files'];
                $task['unzip_file']['unzip_finished']=0;
                $task['unzip_file']['last_action']='waiting...';
                $task['unzip_file']['last_unzip_file']='';
                $task['unzip_file']['last_unzip_file_index']=0;
            }
            else if($key=='plugin')
            {
                $task['priority']=2;
                $task['unzip_file']['files']=$files_info['files'];
                $task['unzip_file']['unzip_finished']=0;
                $task['unzip_file']['last_action']='waiting...';
                $task['unzip_file']['last_unzip_file']='';
                $task['unzip_file']['last_unzip_file_index']=0;

                $b_reset_plugin=isset($restore_options['restore_detail_options']['restore_reset'])?$restore_options['restore_detail_options']['restore_reset']:false;;
            }
            else if($key=='wp-content')
            {
                $task['priority']=3;
                $task['unzip_file']['files']=$files_info['files'];
                $task['unzip_file']['unzip_finished']=0;
                $task['unzip_file']['last_action']='waiting...';
                $task['unzip_file']['last_unzip_file']='';
                $task['unzip_file']['last_unzip_file_index']=0;
            }
            else if($key=='upload')
            {
                $task['priority']=4;
                $task['unzip_file']['files']=$files_info['files'];
                $task['unzip_file']['unzip_finished']=0;
                $task['unzip_file']['last_action']='waiting...';
                $task['unzip_file']['last_unzip_file']='';
                $task['unzip_file']['last_unzip_file_index']=0;
            }
            else if($key=='wp-core')
            {
                $task['priority']=5;
                $task['unzip_file']['files']=$files_info['files'];
                $task['unzip_file']['unzip_finished']=0;
                $task['unzip_file']['last_action']='waiting...';
                $task['unzip_file']['last_unzip_file']='';
                $task['unzip_file']['last_unzip_file_index']=0;
            }
            else if($key=='custom')
            {
                $task['priority']=6;
                $task['unzip_file']['files']=$files_info['files'];
                $task['unzip_file']['unzip_finished']=0;
                $task['unzip_file']['last_action']='waiting...';
                $task['unzip_file']['last_unzip_file']='';
                $task['unzip_file']['last_unzip_file_index']=0;
            }
            else if($key=='db'||$key=='databases')
            {
                $task['type']='databases';

                $task['unzip_file']['files']=$files_info['files'];

                $task['options']=array_merge($task['options'],$task['unzip_file']['files'][0]['options']);

                $task['unzip_file']['unzip_finished']=0;
                $task['unzip_file']['last_action']='waiting...';
                $task['unzip_file']['last_unzip_file']='';

                $task['exec_sql']['init_sql_finished']=0;
                $task['exec_sql']['create_snapshot_finished']=0;
                $task['exec_sql']['exec_sql_finished']=0;
                $task['exec_sql']['replace_rows_finished']=0;

                $task['exec_sql']['current_table']='';
                $task['exec_sql']['current_old_table']='';
                $task['exec_sql']['replace_tables']=array();
                //$task['exec_sql']['current_replace_table_finish']=false;
                //$task['exec_sql']['current_need_replace_table']=false;
                //$task['exec_sql']['current_replace_row']=0;

                $task['exec_sql']['last_action']='waiting...';
                $task['exec_sql']['last_query']='';

                $uid=$this->create_db_uid();
                if($uid===false)
                {
                    $ret['result']='failed';
                    $ret['error']='create db uid failed';
                    return $ret;
                }
                $task['exec_sql']['db_id']=$uid;
                $task['exec_sql']['sql_files']=array();
                $task['priority']=8;
                $restore_task['restore_db']=1;
            }
            else
            {
                $task['priority']=7;
                $task['unzip_file']['files']=$files_info['files'];
                $task['unzip_file']['unzip_finished']=0;
                $task['unzip_file']['last_action']='waiting...';
                $task['unzip_file']['last_unzip_file']='';
                $task['unzip_file']['last_unzip_file_index']=0;
            }

            $restore_reset=isset($restore_options['restore_detail_options']['restore_reset'])?$restore_options['restore_detail_options']['restore_reset']:false;
            $task['finished']=0;
            $task['last_msg']='waiting...';
            if($restore_reset)
            {
                $task['restore_reset']=true;
                $task['restore_reset_finished']=false;
            }
            else
            {
                $task['restore_reset']=false;
            }

            $restore_htaccess=isset($restore_options['restore_detail_options']['restore_htaccess'])?$restore_options['restore_detail_options']['restore_htaccess']:false;
            if($restore_htaccess)
            {
                $task['options']['restore_htaccess']=true;
            }
            else
            {
                $task['options']['restore_htaccess']=false;
            }

            $sub_tasks[]=$task;
        }
        usort($sub_tasks, function ($a, $b)
        {
            if ($a['priority'] == $b['priority'])
                return 0;

            if ($a['priority'] > $b['priority'])
                return 1;
            else
                return -1;
        });

        $restore_task['is_migrate'] = $backup_item->check_migrate_file();
        $restore_task['sub_tasks']=$sub_tasks;
        $restore_task['do_sub_task']=false;
        $restore_task['restore_detail_options']=$this->get_default_restore_options($restore_options['restore_detail_options']);

        $id=uniqid('wpvivid-');
        $log_file_name=$id.'_restore_log.txt';
        $this->log=new WPvivid_Log();
        $log_file=$this->log->GetSaveLogFolder().$log_file_name;
        $restore_task['log']=$log_file;

        $restore_task['last_log']='Init restore task completed.';
        $this->log->WriteLog($restore_task['last_log'],'notice');
        $restore_task['status']='ready';
        update_option('wpvivid_restore_task',$restore_task,'no');
        $ret['result']='success';
        $ret['reset_plugin']=$b_reset_plugin;
        $ret['task']=$restore_task;
        return $ret;
    }

    public function write_litespeed_rule($open=true)
    {
        $litespeed=false;
        if ( isset( $_SERVER['HTTP_X_LSCACHE'] ) && $_SERVER['HTTP_X_LSCACHE'] )
        {
            $litespeed=true;
        }
        elseif ( isset( $_SERVER['LSWS_EDITION'] ) && strpos( $_SERVER['LSWS_EDITION'], 'Openlitespeed' ) === 0 ) {
            $litespeed=true;
        }
        elseif ( isset( $_SERVER['SERVER_SOFTWARE'] ) && $_SERVER['SERVER_SOFTWARE'] == 'LiteSpeed' ) {
            $litespeed=true;
        }

        if($litespeed)
        {
            if (function_exists('insert_with_markers'))
            {
                $home_path     = get_home_path();
                $htaccess_file = $home_path . '.htaccess';

                if ( ( ! file_exists( $htaccess_file ) && is_writable( $home_path ) ) || is_writable( $htaccess_file ) )
                {
                    if ( got_mod_rewrite() )
                    {
                        if($open)
                        {
                            $line=array();
                            $line[]='<IfModule Litespeed>';
                            $line[]='RewriteEngine On';
                            $line[]='RewriteRule .* - [E=noabort:1, E=noconntimeout:1]';
                            $line[]='</IfModule>';
                            insert_with_markers($htaccess_file,'WPvivid_Restore',$line);
                        }
                        else
                        {
                            insert_with_markers($htaccess_file,'WPvivid_Restore','');
                        }

                    }
                }
            }
        }
    }

    public function deactivate_plugins()
    {
        if(is_multisite())
        {
            $current =  get_site_option( 'active_sitewide_plugins' );
            update_option( 'wpvivid_save_active_plugins', $current, 'no');

            $wpvivid_backup='wpvivid-backuprestore/wpvivid-backuprestore.php';

            if (array_key_exists($wpvivid_backup, $current) !== false)
            {
                unset($current[$wpvivid_backup]);
            }
            deactivate_plugins($current, true, true);
        }
        else
        {
            $current = get_option( 'active_plugins', array() );
            update_option( 'wpvivid_save_active_plugins', $current, 'no');

            $wpvivid_backup='wpvivid-backuprestore/wpvivid-backuprestore.php';

            if (($key = array_search($wpvivid_backup, $current)) !== false)
            {
                unset($current[$key]);
            }
            deactivate_plugins($current, true, false);
        }

    }

    public function get_default_restore_options($restore_detail_options)
    {
        $setting=get_option('wpvivid_common_setting',array());
        $restore_detail_options['max_allowed_packet']=32;
        $restore_detail_options['replace_rows_pre_request']=isset($setting['replace_rows_pre_request'])?$setting['replace_rows_pre_request']:10000;
        $restore_detail_options['restore_max_execution_time']=isset($setting['restore_max_execution_time'])?$setting['restore_max_execution_time']:WPVIVID_RESTORE_MAX_EXECUTION_TIME;
        $restore_detail_options['restore_memory_limit']=isset($setting['restore_memory_limit'])?$setting['restore_memory_limit']:WPVIVID_RESTORE_MEMORY_LIMIT;
        $restore_detail_options['sql_file_buffer_pre_request']=isset($setting['sql_file_buffer_pre_request'])?$setting['sql_file_buffer_pre_request']:'5';
        $restore_detail_options['use_index']=isset($setting['use_index'])?$setting['use_index']:1;
        $restore_detail_options['unzip_files_pre_request']=isset($setting['unzip_files_pre_request'])?$setting['unzip_files_pre_request']:1000;
        $restore_detail_options['db_connect_method']=isset($setting['db_connect_method'])?$setting['db_connect_method']:'wpdb';
        $restore_detail_options['restore_db_reset']=false;

        return $restore_detail_options;
    }

    public function create_db_uid()
    {
        global $wpdb;
        $count = 0;

        do
        {
            $count++;
            $uid = sprintf('%06x', wp_rand(0, 0xFFFFFF));

            $verify_db = $wpdb->get_col($wpdb->prepare('SHOW TABLES LIKE %s', array('%' . $uid . '%')));
        } while (!empty($verify_db) && $count < 10);

        if ($count == 10)
        {
            $uid = false;
        }

        return $uid;
    }

    public function get_restore_files_info($backup_item,$restore_version=0,$use_index=0)
    {
        $files=$backup_item->get_files(false);
        $files_info=array();

        foreach ($files as $file)
        {
            $files_info[$file]=$backup_item->get_file_info($file);
        }

        $info=array();
        $added_files=array();

        foreach ($files_info as $file_name=>$file_info)
        {
            if(isset($file_info['has_child']))
            {
                $info=$this->get_has_child_file_info($info,$file_name,$file_info,$added_files,$restore_version,$use_index);
            }
            else
            {
                if(isset($file_info['file_type']))
                {
                    if(isset($file_info['version']))
                    {
                        if($restore_version===false)
                        {
                            if (!in_array($file_name, $added_files))
                            {
                                $file_data['file_name']=$file_name;
                                $file_data['version']=$file_info['version'];
                                $file_data['has_version']=true;
                                $file_data['finished']=0;
                                if($use_index)
                                {
                                    $file_data['index']=0;
                                }
                                $file_data['options']=$file_info;
                                $info[$file_info['file_type']]['files'][]= $file_data;
                                $added_files[]=$file_name;
                            }
                        }
                        else
                        {
                            $version=$restore_version;
                            if($version>=$file_info['version'])
                            {
                                if (!in_array($file_name, $added_files))
                                {
                                    $file_data['file_name']=$file_name;
                                    $file_data['version']=$version;
                                    $file_data['has_version']=true;
                                    $file_data['finished']=0;
                                    if($use_index)
                                    {
                                        $file_data['index']=0;
                                    }
                                    $file_data['options']=$file_info;
                                    $info[$file_info['file_type']]['files'][]= $file_data;
                                    $added_files[]=$file_name;
                                }
                            }
                        }
                    }
                    else
                    {
                        if (!in_array($file_name, $added_files))
                        {
                            $file_data['file_name']=$file_name;
                            $file_data['version']=0;
                            $file_data['finished']=0;
                            if($use_index)
                            {
                                $file_data['index']=0;
                            }
                            $file_data['options']=$file_info;
                            $info[$file_info['file_type']]['files'][]= $file_data;
                            $added_files[]=$file_name;
                        }
                    }
                }
            }
        }

        return $info;
    }

    public function get_has_child_file_info($info,$file_name,$file_info,&$added_files,$restore_version=0,$use_index=0)
    {
        foreach ($file_info['child_file'] as $child_file_name=>$child_file_info)
        {
            if(isset($child_file_info['file_type']))
            {
                if(isset($child_file_info['version']))
                {
                    $info=$this->get_file_version_info($info,$file_name,$file_info,$child_file_name,$child_file_info,$restore_version,$added_files,$use_index);
                }
                else
                {
                    if (!in_array($child_file_name, $added_files))
                    {
                        $file_data['file_name']=$child_file_name;
                        $file_data['version']=0;
                        $file_data['parent_file']=$file_name;
                        $file_data['has_child']=1;
                        $file_data['extract_child_finished']=0;
                        $file_data['finished']=0;
                        if($use_index)
                        {
                            $file_data['index']=0;
                        }
                        $file_data['options']=$file_info['child_file'][$child_file_name];
                        $info[$child_file_info['file_type']]['files'][]=$file_data;
                        $added_files[]=$child_file_name;
                    }
                }
            }
        }
        return $info;
    }

    public function get_file_version_info($info,$file_name,$file_info,$child_file_name,$child_file_info,$restore_version,&$added_files,$use_index)
    {
        if($restore_version===false||$restore_version>=$child_file_info['version'])
        {
            if (!in_array($child_file_name, $added_files))
            {
                $file_data['file_name']=$child_file_name;
                $file_data['version']=$child_file_info['version'];
                $file_data['has_version']=true;
                $file_data['parent_file']=$file_name;
                $file_data['has_child']=1;
                $file_data['finished']=0;
                if($use_index)
                {
                    $file_data['index']=0;
                }
                $file_data['options']=$file_info['child_file'][$child_file_name];
                $info[$child_file_info['file_type']]['files'][]=$file_data;
                $added_files[]=$child_file_name;
            }
        }

        return $info;
    }

    public function deal_restore_shutdown_error()
    {
        $error = error_get_last();

        if (!is_null($error))
        {
            if(preg_match('/Allowed memory size of.*$/', $error['message']))
            {
                $restore_task=get_option('wpvivid_restore_task',array());

                $restore_detail_options=$restore_task['restore_detail_options'];
                $db_connect_method=$restore_detail_options['db_connect_method'];
                if($db_connect_method === 'wpdb')
                {
                    $key=$restore_task['do_sub_task'];
                    if($key!==false)
                    {
                        if($restore_task['sub_tasks'][$key]['type']==='databases')
                        {
                            global $wpdb;
                            $wpdb->get_results('COMMIT');
                        }
                    }
                }

                $restore_task['status']='error';
                $restore_task['error']=$error['message'];
                $restore_task['error_memory_limit']=true;
                update_option('wpvivid_restore_task',$restore_task,'no');
            }
        }

        die();
    }

    public function do_restore()
    {
        check_ajax_referer( 'wpvivid_ajax', 'nonce' );
        $check=current_user_can('manage_options');
        if(!$check)
        {
            die();
        }
        ini_set('display_errors', false);
        error_reporting(-1);
        register_shutdown_function(array($this,'deal_restore_shutdown_error'));

        try
        {
            if($this->check_restore_task()==false)
            {
                $ret['result']='failed';
                $ret['error']='restore task has error';
                echo wp_json_encode($ret);
                $this->end_shutdown_function=true;
                die();
            }

            $this->_enable_maintenance_mode();

            $this->set_restore_environment();
            //$this->flush();

            $ret=$this->_do_restore();

            $this->_disable_maintenance_mode();
            echo wp_json_encode($ret);
        }
        catch (Exception $error)
        {
            $restore_task=get_option('wpvivid_restore_task',array());
            $message = 'An exception has occurred. class: '.get_class($error).';msg: '.$error->getMessage().';code: '.$error->getCode().';line: '.$error->getLine().';in_file: '.$error->getFile().';';
            error_log($message);

            $this->_disable_maintenance_mode();

            $ret['result']='failed';
            $ret['error']=$message;
            $restore_task['status']='error';
            $restore_task['error']=$ret['error'];
            update_option('wpvivid_restore_task',$restore_task,'no');
            echo wp_json_encode($ret);
        }

        die();
    }

    public function _do_restore()
    {
        $ret['result']='success';

        $restore_task=get_option('wpvivid_restore_task',array());
        $this->log=new WPvivid_Log();
        $this->log->OpenLogFile( $restore_task['log'],'has_folder');

        if(empty($restore_task))
        {
            $ret['result']='failed';
            $ret['error']='task empty';
            return $ret;
        }

        $restore_task['do_sub_task']=false;

        foreach ($restore_task['sub_tasks'] as $key=>$sub_task)
        {
            if($sub_task['finished']==1)
            {
                continue;
            }
            else
            {
                $restore_task['do_sub_task']=$key;
                break;
            }
        }

        if($restore_task['do_sub_task']===false)
        {
            $ret['result']='failed';
            $ret['error']='no sub task';
            $restore_task['status']='error';
            $restore_task['error']=$ret['error'];
            update_option('wpvivid_restore_task',$restore_task,'no');
            return $ret;
        }
        else
        {
            $restore_task['status']='doing sub task';
            $restore_task['update_time']=time();
            update_option('wpvivid_restore_task',$restore_task,'no');
            return $this->do_sub_task();
        }
    }

    public function do_sub_task()
    {
        $restore_task=get_option('wpvivid_restore_task',array());

        $key=$restore_task['do_sub_task'];

        $sub_task=$restore_task['sub_tasks'][$key];

        if($sub_task['type']=='databases')
        {
            $this->log->WriteLog('Start restoring '.$sub_task['type'].'.','notice');

            $restore_db=new WPvivid_Restore_DB_2($this->log);
            $ret=$restore_db->restore($sub_task,$restore_task['backup_id']);
            if($ret['result']=='success')
            {
                $this->log->WriteLog('End restore '.$sub_task['type'].'.','notice');
                $restore_task=get_option('wpvivid_restore_task',array());
                $restore_task['sub_tasks'][$key]=$ret['sub_task'];
                $restore_task['status']='sub task finished';
                $restore_task['update_time']=time();
                update_option('wpvivid_restore_task',$restore_task,'no');
            }
            else
            {
                $restore_task=get_option('wpvivid_restore_task',array());
                $restore_task['status']='error';
                $restore_task['error']=$ret['error'];
                wp_cache_flush();
                update_option('wpvivid_restore_task',$restore_task,'no');
            }
        }
        else
        {
            $this->log->WriteLog('Start restoring '.$sub_task['type'].'.','notice');

            $restore_file=new WPvivid_Restore_File_2($this->log);
            $ret=$restore_file->restore($sub_task,$restore_task['backup_id']);
            if($ret['result']=='success')
            {
                $this->log->WriteLog('End restore '.$sub_task['type'].'.','notice');
                $restore_task=get_option('wpvivid_restore_task',array());
                $restore_task['sub_tasks'][$key]=$ret['sub_task'];
                $restore_task['status']='sub task finished';
                $restore_task['update_time']=time();
                update_option('wpvivid_restore_task',$restore_task,'no');
            }
            else
            {
                $restore_task=get_option('wpvivid_restore_task',array());
                $restore_task['status']='error';
                $restore_task['error']=$ret['error'];
                $this->log->WriteLog('End restore '.$sub_task['type'].' error:'.$ret['error'],'notice');
                update_option('wpvivid_restore_task',$restore_task,'no');
            }
        }


        return $ret;
    }

    public function check_restore_task()
    {
        $restore_task=get_option('wpvivid_restore_task',array());

        if(empty($restore_task))
        {
            return false;
        }

        $backup_id=$restore_task['backup_id'];
        $backup = WPvivid_Backuplist::get_backup_by_id($backup_id);
        if($backup===false)
        {
            return false;
        }

        if(empty($restore_task['sub_tasks']))
        {
            return false;
        }

        if($restore_task['do_sub_task']===false)
        {
            return true;
        }
        else
        {
            $sub_task_key=$restore_task['do_sub_task'];
            if(isset($restore_task['sub_tasks'][$sub_task_key]))
            {
                return true;
            }
            else
            {
                return false;
            }
        }
    }

    public function _enable_maintenance_mode()
    {
        //enable maintenance mode by create the .maintenance file.
        //If your wordpress version is greater than 4.6, use the enable_maintenance_mode filter to make our ajax request pass
        $this->init_filesystem();
        global $wp_filesystem;
        $file = $wp_filesystem->abspath() . '.maintenance';
        $maintenance_string = '<?php $upgrading = ' . (time()+1200) . ';';
        $maintenance_string.='global $wp_version;';
        $maintenance_string.='$version_check=version_compare($wp_version,4.6,\'>\' );';
        $maintenance_string.='if($version_check)';
        $maintenance_string.='{';
        $maintenance_string.='if(!function_exists(\'enable_maintenance_mode_filter\'))';
        $maintenance_string.='{';
        $maintenance_string.='function enable_maintenance_mode_filter($enable_checks,$upgrading)';
        $maintenance_string.='{';
        $maintenance_string.='if(is_admin()&&isset($_POST[\'wpvivid_restore\']))';
        $maintenance_string.='{';
        $maintenance_string.='return false;';
        $maintenance_string.='}';
        $maintenance_string.='return $enable_checks;';
        $maintenance_string.='}';
        $maintenance_string.='}';
        $maintenance_string.='add_filter( \'enable_maintenance_mode\',\'enable_maintenance_mode_filter\',10, 2 );';
        $maintenance_string.='}';
        $maintenance_string.='else';
        $maintenance_string.='{';
        $maintenance_string.='if(is_admin()&&isset($_POST[\'wpvivid_restore\']))';
        $maintenance_string.='{';
        $maintenance_string.='global $upgrading;';
        $maintenance_string.='$upgrading=0;';
        $maintenance_string.='return 1;';
        $maintenance_string.='}';
        $maintenance_string.='}';
        if ($wp_filesystem->exists( $file ) )
        {
            $wp_filesystem->delete($file);
        }
        $wp_filesystem->put_contents($file, $maintenance_string, FS_CHMOD_FILE);
    }

    public function _disable_maintenance_mode()
    {
        $this->init_filesystem();
        global $wp_filesystem;
        $file = $wp_filesystem->abspath() . '.maintenance';
        if ($wp_filesystem->exists( $file ))
        {
            $wp_filesystem->delete($file);
        }
    }

    public function init_filesystem()
    {
        $credentials = request_filesystem_credentials(wp_nonce_url(admin_url('admin.php')."?page=WPvivid", 'wpvivid-nonce'));

        if ( ! WP_Filesystem($credentials) )
        {
            return false;
        }
        return true;
    }

    public function set_restore_environment()
    {
        $restore_task=get_option('wpvivid_restore_task',array());

        $restore_detail_options=$restore_task['restore_detail_options'];
        $memory_limit = $restore_detail_options['restore_memory_limit'];
        $restore_max_execution_time= $restore_detail_options['restore_max_execution_time'];

        @set_time_limit($restore_max_execution_time);

        @ini_set('memory_limit', $memory_limit);
    }

    public function flush()
    {
        $ret['result'] = 'success';
        $txt = wp_json_encode($ret);

        if(!headers_sent()){
            header('Content-Length: '.( ( ! empty( $txt ) ) ? strlen( $txt ) : '0' ));
            header('Connection: close');
            header('Content-Encoding: none');
        }
        if (session_id())
            session_write_close();
        echo wp_json_encode($ret);

        if(function_exists('fastcgi_finish_request'))
        {
            fastcgi_finish_request();
        }
        else
        {
            if(ob_get_level()>0)
                ob_flush();
            flush();
        }
    }

    public function get_restore_progress()
    {
        check_ajax_referer( 'wpvivid_ajax', 'nonce' );
        $check=current_user_can('manage_options');
        if(!$check)
        {
            die();
        }
        $restore_task=get_option('wpvivid_restore_task',array());

        if($this->check_restore_task()==false)
        {
            $ret['result']='failed';
            $ret['error']='restore task has error';
            $ret['test']=$restore_task;
            echo wp_json_encode($ret);
            die();
        }

        $ret['test']=$restore_task;
        if($restore_task['status']=='error')
        {
            $ret['result']='failed';
            $ret['error']=$restore_task['error'];
            echo wp_json_encode($ret);
            die();
        }

        $key=$restore_task['do_sub_task'];

        if($key===false)
        {
            $ret['result']='success';
            $ret['do_sub_task']=false;
            $ret['status']='ready';
        }
        else
        {
            if(isset($restore_task['sub_tasks'][$key]))
            {
                $sub_task=$restore_task['sub_tasks'][$key];
                $do_sub_task=$sub_task['type'];
                if($sub_task['finished']==1)
                {
                    $ret['result']='success';
                    $ret['do_sub_task']=$do_sub_task;
                    if($this->check_task_finished())
                    {
                        $ret['status']='task finished';
                    }
                    else
                    {
                        $ret['status']='sub task finished';
                    }

                }
                else
                {
                    $ret['result']='success';
                    $ret['do_sub_task']=$do_sub_task;

                    if($restore_task['status']=='sub task finished')
                    {
                        $ret['status']='sub task finished';
                    }
                    else
                    {
                        $common_setting = WPvivid_Setting::get_option('wpvivid_common_setting');

                        if(isset($common_setting['restore_max_execution_time']))
                        {
                            $setting_restore_max_execution_time = intval($common_setting['restore_max_execution_time']);
                        }
                        else{
                            $setting_restore_max_execution_time = WPVIVID_RESTORE_MAX_EXECUTION_TIME;
                        }

                        $restore_detail_options=$restore_task['restore_detail_options'];
                        $restore_max_execution_time= isset($restore_detail_options['restore_max_execution_time'])?$restore_detail_options['restore_max_execution_time']:$setting_restore_max_execution_time;


                        if(time()-$restore_task['update_time']>$restore_max_execution_time)
                        {
                            $restore_task['restore_timeout_count']++;
                            update_option('wpvivid_restore_task',$restore_task,'no');
                            if($restore_task['restore_timeout_count']>6)
                            {
                                $ret['result']='failed';
                                $ret['error']='restore timeout';
                            }
                            else
                            {
                                $ret['status']='sub task finished';
                            }
                        }
                        else if(time()-$restore_task['update_time']>180)
                        {
                            $ret['status']='no response';
                        }
                        else
                        {
                            $ret['status']='doing sub task';
                        }
                    }
                }

                if($ret['result']=='success')
                {
                    $ret['main_msg']='doing restore '.$sub_task['type'];

                    $finished=0;
                    $total=count($restore_task['sub_tasks']);
                    $sub_tasks_progress=array();
                    $sub_tasks_progress_detail=array();
                    if($total==0)
                    {
                        $main_progress=0;
                    }
                    else
                    {
                        $sub_progress=0;
                        foreach ($restore_task['sub_tasks'] as $key=>$sub_task)
                        {
                            if($sub_task['type']=='themes')
                            {
                                $sub_progress_id='wpvivid_restore_themes_progress';
                                $sub_progress_detail_id='wpvivid_restore_themes_progress_detail';
                            }
                            else if($sub_task['type']=='plugin')
                            {
                                $sub_progress_id='wpvivid_restore_plugin_progress';
                                $sub_progress_detail_id='wpvivid_restore_plugin_progress_detail';
                            }
                            else if($sub_task['type']=='wp-content')
                            {
                                $sub_progress_id='wpvivid_restore_wp_content_progress';
                                $sub_progress_detail_id='wpvivid_restore_wp_content_progress_detail';
                            }
                            else if($sub_task['type']=='upload')
                            {
                                $sub_progress_id='wpvivid_restore_upload_progress';
                                $sub_progress_detail_id='wpvivid_restore_upload_progress_detail';
                            }
                            else if($sub_task['type']=='wp-core')
                            {
                                $sub_progress_id='wpvivid_restore_core_progress';
                                $sub_progress_detail_id='wpvivid_restore_core_progress_detail';
                            }
                            else if($sub_task['type']=='custom')
                            {
                                $sub_progress_id='wpvivid_restore_custom_progress';
                                $sub_progress_detail_id='wpvivid_restore_custom_progress_detail';
                            }
                            else if($sub_task['type']=='db'||$sub_task['type']=='databases')
                            {
                                $sub_progress_id='wpvivid_restore_databases_progress';
                                $sub_progress_detail_id='wpvivid_restore_databases_progress_detail';
                            }
                            else if($sub_task['type']=='additional_databases')
                            {
                                $sub_progress_id='wpvivid_restore_additional_db_progress';
                                $sub_progress_detail_id='wpvivid_restore_additional_db_progress_detail';
                            }
                            else
                            {
                                $sub_progress_id='';
                                $sub_progress_detail_id='';
                            }

                            if($sub_task['finished']==1)
                            {
                                $finished++;
                                $sub_progress+=100;
                                $sub_task_progress='Completed - 100%<span class="dashicons dashicons-yes" style="color:#8bc34a;"></span>';
                            }
                            else
                            {
                                if($sub_task['unzip_file']['last_action']=='waiting...')
                                {
                                    $sub_task_progress='waiting...';
                                }
                                else if($sub_task['type']=='databases')
                                {
                                    if($sub_task['unzip_file']['unzip_finished']==0)
                                    {
                                        $sub_task_progress= $sub_task['unzip_file']['last_action'].' - 0%';
                                    }
                                    else
                                    {
                                        if($restore_task['is_migrate'])
                                        {
                                            $file_size=0;
                                            $read_size=0;

                                            foreach ($sub_task['exec_sql']['sql_files'] as $sql_file)
                                            {
                                                $file_size+=$sql_file['sql_file_size'];
                                                $read_size+=$sql_file['sql_offset'];
                                            }

                                            $progress1=intval(($read_size/ $file_size)*50);
                                            $progress2=0;
                                            if(!empty($sub_task['exec_sql']['replace_tables']))
                                            {
                                                $need_replace_table = sizeof($sub_task['exec_sql']['replace_tables']);
                                                $replaced_tables=0;
                                                foreach ($sub_task['exec_sql']['replace_tables'] as $replace_table)
                                                {
                                                    if ($replace_table['finished'] == 1)
                                                    {
                                                        $replaced_tables++;
                                                    }
                                                }
                                                $progress2=intval(($replaced_tables/ $need_replace_table)*50);
                                            }

                                            $progress=$progress1+$progress2;

                                            $sub_progress+=$progress;
                                            $sub_task_progress= $sub_task['exec_sql']['last_action'].' - '.$progress.'%';
                                        }
                                        else
                                        {
                                            $file_size=0;
                                            $read_size=0;

                                            foreach ($sub_task['exec_sql']['sql_files'] as $sql_file)
                                            {
                                                $file_size+=$sql_file['sql_file_size'];
                                                $read_size+=$sql_file['sql_offset'];
                                            }

                                            $progress=intval(($read_size/ $file_size)*100);

                                            $sub_progress+=$progress;
                                            $sub_task_progress= $sub_task['exec_sql']['last_action'].' - '.$progress.'%';
                                        }
                                    }
                                }
                                else
                                {
                                    $files=$sub_task['unzip_file']['files'];
                                    $files_finished=0;
                                    $files_total=count($sub_task['unzip_file']['files']);
                                    foreach ($files as $index=>$file)
                                    {
                                        if ($file['finished'] == 1)
                                        {
                                            $files_finished++;
                                        }
                                    }

                                    if(isset($sub_task['unzip_file']['sum'])&&$sub_task['unzip_file']['start'])
                                    {
                                        $sum=$sub_task['unzip_file']['sum'];
                                        $start=$sub_task['unzip_file']['start'];

                                        if($sum>0)
                                        {
                                            $file_progress=intval((($start/$sum)*100)/$files_total);
                                        }
                                        else
                                        {
                                            $file_progress=0;
                                        }
                                    }
                                    else
                                    {
                                        $file_progress=0;
                                    }
                                    $progress=intval(($files_finished/ $files_total)*100)+$file_progress;
                                    $progress=min(100,$progress);
                                    $sub_progress+=$progress;
                                    $sub_task_progress= $sub_task['unzip_file']['last_action'].' - '.$progress.'%';
                                }
                            }

                            if(!empty($sub_progress_id))
                            {
                                $sub_tasks_progress[$sub_progress_id]=$sub_task_progress;
                            }

                            if(!empty($sub_progress_id))
                            {
                                $sub_tasks_progress_detail[$sub_progress_detail_id]['html']=$sub_task['last_msg'];
                                if($do_sub_task==$sub_task['type'])
                                {
                                    $sub_tasks_progress_detail[$sub_progress_detail_id]['show']=true;
                                }
                                else
                                {
                                    $sub_tasks_progress_detail[$sub_progress_detail_id]['show']=false;
                                }
                            }
                        }
                        $main_progress=intval($sub_progress/$total);
                        //$main_progress=intval(($finished/$total)*100);
                        $main_progress=min($main_progress,100);
                    }
                    $ret['sub_tasks_progress']=$sub_tasks_progress;
                    $ret['sub_tasks_progress_detail']=$sub_tasks_progress_detail;

                    $ret['main_task_progress_total']=$total;
                    $ret['main_task_progress_finished']=$finished;
                    $ret['main_progress']='<span class="action-progress-bar-percent wpvivid-span-processed-restore-percent-progress" style="width: '.$main_progress.'%; display:block; height:1.5em; border-radius:0; padding-left:0.5em;">'.$main_progress.'% completed</span>';

                    $buffer = '';
                    if(file_exists($restore_task['log']))
                    {
                        $file = fopen($restore_task['log'], 'r');

                        if ($file)
                        {
                            while (!feof($file)) {
                                $buffer .= fread($file, 1024);
                            }
                            fclose($file);
                        }
                    }
                    $ret['log'] = $buffer;

                    if(strlen($ret['log']) > 100*1024)
                    {
                        $ret['log']=substr($ret['log'], -100*1024);
                    }
                }
            }
            else
            {
                $ret['result']='failed';
                $ret['error']='sub task not found';
            }
        }

        echo wp_json_encode($ret);
        die();
    }

    public function check_task_finished()
    {
        $restore_task=get_option('wpvivid_restore_task',array());

        $finished=false;

        foreach ($restore_task['sub_tasks'] as $sub_task)
        {
            if($sub_task['finished']==1)
            {
                $finished=true;
            }
            else
            {
                $finished=false;
                break;
            }
        }
        return $finished;
    }

    public function finish_restore()
    {
        check_ajax_referer( 'wpvivid_ajax', 'nonce' );
        $check=current_user_can('manage_options');
        if(!$check)
        {
            die();
        }
        register_shutdown_function(array($this,'deal_restore_finish_shutdown_error'));
        ini_set('display_errors', 0);

        echo '<p style="font-size:1.5em;"><span>The restoration has been successfully completed.</span></p>';

        $this->_disable_maintenance_mode();
        $this->write_litespeed_rule(false);

        if(file_exists(WPMU_PLUGIN_DIR.'/a-wpvivid-restore-mu-plugin-check.php'))
        {
            @wp_delete_file(WPMU_PLUGIN_DIR.'/a-wpvivid-restore-mu-plugin-check.php');
        }

        $plugins= get_option( 'wpvivid_save_active_plugins', array() );

        $ret=$this->check_restore_db();

        $this->delete_temp_files();
        delete_transient( 'wp_core_block_css_files' );

        $restore_task=get_option('wpvivid_restore_task',array());

        if($restore_task['is_migrate'])
        {
            $this->check_force_ssl();
            $this->check_admin_plugins();
            $this->flush_elementor_cache();
            $this->regenerate_css_files();
            if(!is_multisite())
            {
                if (function_exists('save_mod_rewrite_rules'))
                {
                    if(isset($restore_task['restore_options']['restore_detail_options']['restore_htaccess'])&&$restore_task['restore_options']['restore_detail_options']['restore_htaccess'])
                    {
                        //
                    }
                    else
                    {
                        if (file_exists(get_home_path() . '.htaccess'))
                        {
                            $htaccess_data = file_get_contents(get_home_path() . '.htaccess');
                            $line = '';
                            if (preg_match('#AddHandler application/x-httpd-php.*#', $htaccess_data, $matcher))
                            {
                                $line = PHP_EOL . $matcher[0];

                                if (preg_match('#<IfModule mod_suphp.c>#', $htaccess_data, $matcher)) {
                                    $line .= PHP_EOL . '<IfModule mod_suphp.c>';
                                    if (preg_match('#suPHP_ConfigPath .*#', $htaccess_data, $matcher)) {
                                        $line .= PHP_EOL . $matcher[0];
                                    }
                                    $line .= PHP_EOL . '</IfModule>';
                                }
                            }
                            else if (preg_match('#AddHandler application/x-httpd-ea-php.*#', $htaccess_data, $matcher))
                            {
                                $line_temp = PHP_EOL . $matcher[0];

                                if (preg_match('#<IfModule mime_module>#', $htaccess_data, $matcher))
                                {
                                    $line .= PHP_EOL . '<IfModule mime_module>';
                                    $line .= $line_temp.PHP_EOL;
                                    $line .= PHP_EOL . '</IfModule>';
                                }
                            }
                            @rename(get_home_path() . '.htaccess', get_home_path() . '.htaccess_old');
                            save_mod_rewrite_rules();
                            if (!empty($line))
                                file_put_contents(get_home_path() . '.htaccess', $line, FILE_APPEND);
                        }
                        else
                        {
                            save_mod_rewrite_rules();
                        }
                    }

                    if(file_exists(get_home_path() . '.user.ini'))
                    {
                        @rename(get_home_path() . '.user.ini', get_home_path() . '.user.ini_old');
                        save_mod_rewrite_rules();
                    }
                }

            }
            //Migration notice
            $need_review=WPvivid_Setting::get_option('wpvivid_need_review');
            if($need_review=='not')
            {
                $review_time=WPvivid_Setting::get_option('wpvivid_review_time', false);
                if($review_time === false || time() >= $review_time)
                {
                    WPvivid_Setting::update_option('wpvivid_need_review','show');
                    $msg = 'Migration complete! We\'d love it if you could leave WPvivid Backup Plugin a 5-star rating. It really motivates us to keep improving!';
                    WPvivid_Setting::update_option('wpvivid_review_msg',$msg);
                    WPvivid_Setting::update_option('wpvivid_review_type', 'migration');
                }
            }
        }

        if($ret['has_db'])
        {
            $this->active_plugins();
        }
        else
        {
            $this->active_plugins($plugins);
        }


        if($restore_task['is_migrate'])
        {
            //$html.='<p style="font-size:1.5em;"><span>Save permalinks structure:</span><span><a href="'.admin_url('options-permalink.php').'" target="_blank">click here</a></span></p>';
            if($this->check_oxygen())
            {
                echo '<p style="font-size:1.5em;"><span>The restoration is almost complete, but there is a little bit job to do.</span></p>';
                echo '<p style="font-size:1.5em;"><span>We found that your website is using the Oxygen page builder. In order to restore this backup perfectly, please follow</span><span><a href="https://oxygenbuilder.com/documentation/other/importing-exporting/#resigning" target="_blank"> the guide </a>to regenerate the css.</span></p>';
            }

            if($this->check_divi())
            {
                $this->clean_divi_cache();
                echo '<p style="font-size:1.5em;"><span>The restoration is almost complete, but there is a little bit job to do.</span></p>';
                echo '<p style="font-size:1.5em;"><span>We found that your website is using the Divi theme. In order to restore this backup perfectly,</span><span>please follow<a href="https://divitheme.net/clear-divi-cache/" target="_blank"> the guide </a>to clean up the Divi cache</span></p>';
            }
        }

        if(isset( $restore_task['restore_options']['delete_local'])&& $restore_task['restore_options']['delete_local'])
        {
            $backup_id=$restore_task['backup_id'];
            $backup = WPvivid_Backuplist::get_backup_by_id($backup_id);
            if($backup!==false)
            {
                $backup_item = new WPvivid_Backup_Item($backup);
                if($backup_item->get_remote()!==false)
                {
                    $files=$backup_item->get_files(true);
                    foreach ($files as $file)
                    {
                        @wp_delete_file($file);
                    }
                }
            }
        }

        $siteurl = get_option( 'siteurl' );
        echo '<p style="font-size:1.5em;"><span><a href="'.esc_url($siteurl).'" target="_blank">Visit Site</a></span></p>';

        delete_option('wpvivid_restore_task');

        wp_cache_flush();
        die();
    }

    public function check_restore_db()
    {
        $has_db=false;

        $restore_task=get_option('wpvivid_restore_task',array());
        $this->log=new WPvivid_Log();
        $this->log->OpenLogFile( $restore_task['log'],'has_folder');
        foreach ($restore_task['sub_tasks'] as $sub_task)
        {
            if($sub_task['type']=='databases')
            {
                $has_db=true;

                $restore_db=new WPvivid_Restore_DB_2($this->log);
                $current_setting = WPvivid_Setting::export_setting_to_json();

                $ret=$restore_db->rename_db($sub_task);
                WPvivid_Setting::import_json_to_setting($current_setting);
                do_action('wpvivid_reset_schedule');
                do_action('wpvivid_do_after_restore_db');

                if($restore_task['is_migrate'] == '1')
                {
                    $option_name = 'wpvivid_staging_task_list';
                    global $wpdb;
                    $result = $wpdb->query($wpdb->prepare("DELETE FROM {$wpdb->options} WHERE option_name = %s", $option_name));
                    if(!$result)
                    {
                        $this->log->WriteLog('Delete migration option failed.', 'notice');
                    }
                }

                if($ret['result']!='success')
                {
                    $this->log->WriteLog('Restore database failed:'.$ret['error'],'notice');
                    $restore_db->remove_tmp_table($sub_task);
                    return $ret;
                }
                break;
            }
        }


        $ret['result']='success';
        $ret['has_db']=$has_db;
        return $ret;
    }

    public function delete_temp_files()
    {
        $restore_task=get_option('wpvivid_restore_task',array());
        $this->log=new WPvivid_Log();
        $this->log->OpenLogFile( $restore_task['log'],'has_folder');
        $this->log->WriteLog('Deleting temp files.','notice');
        $backup_id=$restore_task['backup_id'];
        $backup = WPvivid_Backuplist::get_backup_by_id($backup_id);
        $backup_item=new WPvivid_Backup_Item($backup);
        foreach($restore_task['sub_tasks'] as $key => $task)
        {
            foreach ($task['unzip_file']['files'] as $file)
            {
                if(isset($file['has_child']))
                {
                    $path= $backup_item->get_local_path().$file['file_name'];
                    //$this->log->WriteLog('clean file:'.$path,'notice');
                    if(file_exists($path))
                    {
                        @wp_delete_file($path);
                    }
                }
            }
        }
    }

    public function restore_failed()
    {
        check_ajax_referer( 'wpvivid_ajax', 'nonce' );
        $check=current_user_can('manage_options');
        if(!$check)
        {
            die();
        }
        register_shutdown_function(array($this,'deal_restore_finish_shutdown_error'));

        //echo '<p style="font-size:1.5em;"><span>Please adjust the advanced settings before restoring and retry.</span></p>';

        $this->_disable_maintenance_mode();
        $this->write_litespeed_rule(false);
        if(file_exists(WPMU_PLUGIN_DIR.'/a-wpvivid-restore-mu-plugin-check.php'))
        {
            @wp_delete_file(WPMU_PLUGIN_DIR.'/a-wpvivid-restore-mu-plugin-check.php');
        }
        $plugins= get_option( 'wpvivid_save_active_plugins', array() );

        $this->delete_temp_tables();
        $this->delete_temp_files();

        $this->active_plugins($plugins);

        $restore_task=get_option('wpvivid_restore_task',array());

        //$restore_detail_options=$restore_task['restore_detail_options'];
        //$unzip_files_pre_request=$restore_detail_options['unzip_files_pre_request'];
        echo 'Restore failed. ';
        if($restore_task['status']=='error')
        {
            echo 'Error:'.esc_html($restore_task['error']).' ';
            if(isset($restore_task['error_memory_limit']))
            {
                echo 'Memory exhausted during restoring..';
            }
            else if(isset($restore_task['error_mu_require_file']))
            {
                echo 'Restore must-use plugin '.esc_html($restore_task['error_mu_require_file']).' error.Plugin require file not found..';
            }
        }
        else
        {
            $key=$restore_task['do_sub_task'];

            if($key!==false)
            {
                if(isset($restore_task['sub_tasks'][$key]))
                {
                    //$error_msg='restore sub task '.$restore_task['sub_tasks'][$key]['type'].' timeout.';
                    if($restore_task['sub_tasks'][$key]['type']==='databases'||$restore_task['sub_tasks'][$key]['type']==='additional_databases')
                    {
                        echo 'Sql file importing time out.';
                        //$error_msg.='<p style="font-size:1.5em;">Pleases try to increase your max_allowed_packet(recommend 32M)</p>';
                        //$error_msg.='<p style="font-size:1.5em;">or reduce SQL buffer will be processed every PHP request(recommend 5M)</p>';
                        //$error_msg.='<p style="font-size:1.5em;">or reduce maximum rows of data in MYSQL table will be imported every time when restoring(recommend 10000)</p>';
                    }
                    else
                    {
                        echo 'File extracting time out.';
                        //$error_msg.='<p style="font-size:1.5em;">Pleases try to check user unzip files using index,and set files are unzipped every PHP request(recommend 1000)</p>';
                        //$error_msg.='<p style="font-size:1.5em;">and increase your PHP - max execution time(900s)</p>';
                    }
                }
                else
                {
                    //$error_msg='';
                    echo 'Restoring time out.';
                }
            }
        }

        delete_option('wpvivid_restore_task');
        wp_cache_flush();

        die();
    }

    public function deal_restore_finish_shutdown_error()
    {
        $error = error_get_last();
        if (!is_null($error))
        {
            if (empty($error) || !in_array($error['type'], array(E_ERROR,E_RECOVERABLE_ERROR,E_CORE_ERROR,E_COMPILE_ERROR), true))
            {
                $error = false;
            }

            if ($error !== false)
            {
                $message = 'type: '. $error['type'] . ', ' . $error['message'];
                echo '<p style="font-size:1.5em;">Error Info:'.esc_html($message).'</p>';;
            }
        }

        die();
    }

    public function delete_temp_tables()
    {
        $restore_task=get_option('wpvivid_restore_task',array());
        $this->log=new WPvivid_Log();
        $this->log->OpenLogFile( $restore_task['log'],'has_folder');
        foreach ($restore_task['sub_tasks'] as $sub_task)
        {
            if($sub_task['type']=='databases')
            {
                $restore_db=new WPvivid_Restore_DB_2($this->log);
                $restore_db->remove_tmp_table($sub_task);
            }
        }

        $ret['result']='success';
        return $ret;
    }

    public function check_force_ssl()
    {
        $plugins=array();
        if ( ! is_ssl() )
        {
            $plugins[]='really-simple-ssl/rlrsssl-really-simple-ssl.php';
            $plugins[]='wordpress-https/wordpress-https.php';
            $plugins[]='wp-force-ssl/wp-force-ssl.php';
            $plugins[]='force-https-littlebizzy/force-https.php';

            $current = get_option( 'active_plugins', array() );

            foreach ( $plugins as $plugin )
            {
                if ( ( $key = array_search( $plugin, $current ) ) !== false )
                {
                    unset( $current[ $key ] );
                }
            }

            update_option( 'active_plugins', $current );

            if ( get_option( 'woocommerce_force_ssl_checkout' ) )
            {
                update_option( 'woocommerce_force_ssl_checkout', 'no' );
            }
        }

    }

    public function check_admin_plugins()
    {
        $plugins=array();
        $plugins[]='wps-hide-login/wps-hide-login.php';
        $plugins[]='lockdown-wp-admin/lockdown-wp-admin.php';
        $plugins[]='rename-wp-login/rename-wp-login.php';
        $plugins[]='change-wp-admin-login/change-wp-admin-login.php';
        $plugins[]='hide-my-wp/index.php';
        $plugins[]='hide-login-page/hide-login-page.php';
        $plugins[]='wp-hide-security-enhancer/wp-hide.php';
        //
        $current = get_option( 'active_plugins', array() );

        foreach ( $plugins as $plugin )
        {
            if ( ( $key = array_search( $plugin, $current ) ) !== false )
            {
                unset( $current[ $key ] );
            }
        }

        update_option( 'active_plugins', $current );
    }

    public function flush_elementor_cache()
    {
        $wp_upload_dir=wp_upload_dir( null, false );
        $path =  $wp_upload_dir['basedir'] . '/elementor/css/' . '*';

        foreach ( glob( $path ) as $file_path ) {
            wp_delete_file( $file_path );
        }
        delete_post_meta_by_key( '_elementor_css' );
        delete_option( '_elementor_global_css' );
        delete_option( 'elementor-custom-breakpoints-files' );

        delete_option( '_elementor_assets_data' );
        delete_post_meta_by_key( '_elementor_inline_svg' );
    }

    public function regenerate_css_files()
    {
        delete_option( 'generateblocks_dynamic_css_posts' );
    }

    public function active_plugins($plugins=array())
    {
        wp_cache_flush();

        include_once( ABSPATH . 'wp-admin/includes/plugin.php' );

        $current = get_option( 'active_plugins', array() );
        $plugin_list=array();
        $plugin_list[]='wpvivid-backuprestore/wpvivid-backuprestore.php';
        $plugin_list=apply_filters('wpvivid_enable_plugins_list',$plugin_list);

        $current=array_merge($plugin_list,$current);
        // Add plugins
        if(!empty($plugins))
        {
            foreach ( $plugins as $plugin )
            {
                if ( ! in_array( $plugin, $current ) && ! is_wp_error( validate_plugin( $plugin ) ) ) {
                    $current[] = $plugin;
                }
            }
        }
        activate_plugins($current,'',false,true);
    }

    public function check_oxygen()
    {
        if (!function_exists('get_plugins'))
        {
            require_once ABSPATH . 'wp-admin/includes/plugin.php';
        }

        if ( ( $plugins = get_plugins() ) )
        {
            foreach ( $plugins as $key => $plugin )
            {
                if ( $key === 'oxygen/functions.php' )
                {
                    return true;
                }
            }
        }
        return false;
    }

    public function check_divi()
    {
        $themes=wp_get_themes();
        foreach ($themes as $key=>$theme)
        {
            if ( $key === 'Divi' )
            {
                return true;
            }
        }
        return false;
    }

    public function clean_divi_cache()
    {
        $_post_id = '*';
        $_owner   = '*';
        $_slug    = '*';

        $cache_dir= WP_CONTENT_DIR.DIRECTORY_SEPARATOR.'et-cache';

        $files = array_merge(
        // Remove any CSS files missing a parent folder.
            (array) glob( "{$cache_dir}/et-{$_owner}-*" ),
            // Remove CSS files for individual posts or all posts if $post_id set to 'all'.
            (array) glob( "{$cache_dir}/{$_post_id}/et-{$_owner}-{$_slug}*" ),
            // Remove CSS files that contain theme builder template CSS.
            // Multiple directories need to be searched through since * doesn't match / in the glob pattern.
            (array) glob( "{$cache_dir}/*/et-{$_owner}-{$_slug}-*tb-{$_post_id}*" ),
            (array) glob( "{$cache_dir}/*/*/et-{$_owner}-{$_slug}-*tb-{$_post_id}*" ),
            (array) glob( "{$cache_dir}/*/*/*/et-{$_owner}-{$_slug}-*tb-{$_post_id}*" ),
            (array) glob( "{$cache_dir}/*/et-{$_owner}-{$_slug}-*tb-for-{$_post_id}*" ),
            (array) glob( "{$cache_dir}/*/*/et-{$_owner}-{$_slug}-*tb-for-{$_post_id}*" ),
            (array) glob( "{$cache_dir}/*/*/*/et-{$_owner}-{$_slug}-*tb-for-{$_post_id}*" ),
            // Remove Dynamic CSS files for categories, tags, authors, archives, homepage post feed and search results.
            (array) glob( "{$cache_dir}/taxonomy/*/*/et-{$_owner}-dynamic*" ),
            (array) glob( "{$cache_dir}/author/*/et-{$_owner}-dynamic*" ),
            (array) glob( "{$cache_dir}/archive/et-{$_owner}-dynamic*" ),
            (array) glob( "{$cache_dir}/search/et-{$_owner}-dynamic*" ),
            (array) glob( "{$cache_dir}/notfound/et-{$_owner}-dynamic*" ),
            (array) glob( "{$cache_dir}/home/et-{$_owner}-dynamic*" )
        );

        $this->_remove_files_in_directory( $files, $cache_dir );

        $this->remove_empty_directories($cache_dir );

        delete_option( '_et_builder_global_feature_cache' );

        $post_meta_caches = array(
            'et_enqueued_post_fonts',
            '_et_dynamic_cached_shortcodes',
            '_et_dynamic_cached_attributes',
            '_et_builder_module_features_cache',
        );

        // Clear post meta caches.
        foreach ( $post_meta_caches as $post_meta_cache ) {
            if ( ! empty( $post_id ) ) {
                delete_post_meta( $post_id, $post_meta_cache );
            } else {
                delete_post_meta_by_key( $post_meta_cache );
            }
        }
    }

    public function remove_empty_directories( $path ) {
        $path = realpath( $path );

        if ( empty( $path ) ) {
            // $path doesn't exist
            return;
        }

        $path        = $this->normalize_path( $path );
        $content_dir = $this->normalize_path( WP_CONTENT_DIR );

        if ( 0 !== strpos( $path, $content_dir ) || $content_dir === $path ) {
            return;
        }

        $this->_remove_empty_directories($path);
    }

    public function _remove_empty_directories($path)
    {
        if ( ! is_dir( $path ) ) {
            return false;
        }

        $empty              = true;
        $directory_contents = glob( untrailingslashit( $path ) . '/*' );

        foreach ( (array) $directory_contents as $item ) {
            if ( ! $this->_remove_empty_directories( $item ) ) {
                $empty = false;
            }
        }

        return $empty ? @rmdir( $path ) : false;
    }

    public function _remove_files_in_directory( $files, $cache_dir )
    {
        $cache_dir=$this->normalize_path( $cache_dir );

        foreach ( $files as $file )
        {
            $file =$this->normalize_path( $file );

            if ( ! $this->starts_with( $file, $cache_dir ) ) {
                // File is not located inside cache directory so skip it.
                continue;
            }

            if ( is_file( $file ) )
            {
                @wp_delete_file($file);
            }
        }
    }

    public function starts_with( $string, $substring ) {
        return 0 === strpos( $string, $substring );
    }

    public function normalize_path( $path = '' )
    {
        $path = (string) $path;
        $path = str_replace( '..', '', $path );

        if ( function_exists( 'wp_normalize_path' ) ) {
            return wp_normalize_path( $path );
        }

        return str_replace( '\\', '/', $path );
    }

}includes/new_backup/class-wpvivid-restore-db-method-2.php000064400000045065151327705670017504 0ustar00<?php

if (!defined('WPVIVID_PLUGIN_DIR'))
{
    die;
}

class WPvivid_Restore_DB_WPDB_Method_2
{
    public $max_allow_packet;
    public $skip_query=0;
    public $last_error='';
    public $last_log='';

    public function connect_db()
    {
        global $wpdb;
        $wpdb->get_results('SET NAMES utf8mb4', ARRAY_A);
        $ret['result']=WPVIVID_SUCCESS;
        return $ret;
    }

    public function get_last_error()
    {
        return $this->last_error;
    }

    public function get_last_log()
    {
        return $this->last_log;
    }

    public function test_db()
    {
        global $wpdb;

        $test_table_new=uniqid('wpvivid_test_tables_');
        $columns='(test_id int primary key)';
        $test_table = $wpdb->get_results("CREATE TABLE IF NOT EXISTS $test_table_new $columns",ARRAY_A);

        if ($test_table!==false)
        {
            $this->last_log='The test to create table succeeds.';
            $test_table = $wpdb->get_results("INSERT INTO $test_table_new (`test_id`) VALUES ('123')",ARRAY_A);
            if($test_table!==false)
            {
                $this->last_log='The test to insert into table succeeds.';
                $test_table = $wpdb->get_results("DROP TABLE IF EXISTS $test_table_new",ARRAY_A);
                if($test_table!==false)
                {
                    $this->last_log='The test to drop table succeeds.';
                    return true;
                }
                else
                {
                    $this->last_error='Unable to drop table. The reason is '.$wpdb->last_error;
                    return false;
                }
            }
            else
            {
                $this->last_error='Unable to insert into table. The reason is '.$wpdb->last_error;
                return false;
            }
        }
        else {
            $this->last_error='Unable to create table. The reason is '.$wpdb->last_error;
            return false;
        }
    }

    public function check_max_allow_packet($log)
    {
        $restore_task=get_option('wpvivid_restore_task',array());
        $restore_detail_options=$restore_task['restore_detail_options'];
        $max_allowed_packet=$restore_detail_options['max_allowed_packet'];
        $set_max_allowed_packet=$max_allowed_packet*1024*1024;

        global $wpdb;
        $max_allowed_packet =$wpdb->get_var("SELECT @@session.max_allowed_packet");

        if($max_allowed_packet!==null)
        {
            if($max_allowed_packet<$set_max_allowed_packet)
            {
                $query='set global max_allowed_packet='.$set_max_allowed_packet;
                $test=$wpdb->get_results($query);
                var_dump($test);
                $wpdb->db_connect();
                $max_allowed_packet =$wpdb->get_var("SELECT @@session.max_allowed_packet");
                $this->max_allow_packet=$max_allowed_packet;
            }
            else
            {
                $this->max_allow_packet=$max_allowed_packet;
            }
        }
        else
        {
            $this->last_log='get max_allowed_packet failed.';
            $this->max_allow_packet=1048576;
        }
    }

    public function get_max_allow_packet()
    {
        return $this->max_allow_packet;
    }

    public function init_sql_mode()
    {
        global $wpdb;
        $res = $wpdb->get_var('SELECT @@SESSION.sql_mode');
        if($res===null)
        {
            $this->last_error='get sql_mode failed';
            return false;
        }
        else
        {
            $sql_mod = $res;
            $temp_sql_mode = str_replace('NO_ENGINE_SUBSTITUTION','',$sql_mod);
            $temp_sql_mode = 'ALLOW_INVALID_DATES,NO_AUTO_VALUE_ON_ZERO,'.$temp_sql_mode;
            $wpdb->get_results('SET SESSION sql_mode = "'.$temp_sql_mode.'"',ARRAY_A);
            return true;
        }
    }

    public function set_skip_query($count)
    {
        $this->skip_query=$count;
    }

    public function execute_sql($query)
    {
        if(preg_match('#SET TIME_ZONE=@OLD_TIME_ZONE#', $query))
        {
            return true;
        }
        if(preg_match('#SET SQL_MODE=@OLD_SQL_MODE#', $query))
        {
            return true;
        }
        if(preg_match('#SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS#', $query))
        {
            return true;
        }
        if(preg_match('#SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS#', $query))
        {
            return true;
        }
        if(preg_match('#SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT#', $query))
        {
            return true;
        }
        if(preg_match('#SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS#', $query))
        {
            return true;
        }
        if(preg_match('#SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION#', $query))
        {
            return true;
        }
        if(preg_match('#SET SQL_NOTES=@OLD_SQL_NOTES#', $query))
        {
            return true;
        }

        global $wpdb;
        if ($wpdb->get_results($query)===false)
        {
            $this->last_error=$wpdb->last_error;
            return false;
        }
        else
        {
            return true;
        }
    }

    public function query($sql,$output)
    {
        global $wpdb;
        return $wpdb->get_results($sql,$output);
    }

    public function errorInfo()
    {
        global $wpdb;
        return $wpdb->last_error;
    }
}

class WPvivid_Restore_DB_PDO_Mysql_Method_2
{
    private $db;
    public $max_allow_packet;
    public $skip_query=0;
    public $last_error='';
    public $last_log='';

    public function get_last_error()
    {
        return $this->last_error;
    }

    public function get_last_log()
    {
        return $this->last_log;
    }

    public function connect_db()
    {
        try
        {
            $res = explode(':',DB_HOST);
            $db_host = $res[0];
            $db_port = empty($res[1])?'':$res[1];
            if(!empty($db_port)) {
                $dsn='mysql:host=' . $db_host . ';port=' . $db_port . ';dbname=' . DB_NAME;
            }
            else{
                $dsn='mysql:host=' . $db_host . ';dbname=' . DB_NAME;
            }
            $this->db = null;
            $this->db=new PDO($dsn, DB_USER, DB_PASSWORD);
            $this->db->exec('SET NAMES utf8mb4');
            if(empty($this->db) || !$this->db)
            {
                if(class_exists('PDO'))
                {
                    $extensions=get_loaded_extensions();
                    if(array_search('pdo_mysql',$extensions))
                    {
                        $ret['result']=WPVIVID_FAILED;
                        $ret['error']='The error establishing a database connection. Please check wp-config.php file and make sure the information is correct.';
                    }
                    else{
                        $ret['result']=WPVIVID_FAILED;
                        $ret['error']='The pdo_mysql extension is not detected. Please install the extension first or choose wpdb option for Database connection method.';
                    }
                }
                else{
                    $ret['result']=WPVIVID_FAILED;
                    $ret['error']='The pdo_mysql extension is not detected. Please install the extension first or choose wpdb option for Database connection method.';
                }
            }
            else
            {
                $ret['result']=WPVIVID_SUCCESS;
            }
        }
        catch (Exception $e)
        {
            if(empty($this->db) || !$this->db)
            {
                if(class_exists('PDO'))
                {
                    $extensions=get_loaded_extensions();
                    if(array_search('pdo_mysql',$extensions))
                    {
                        $ret['result']=WPVIVID_FAILED;
                        $ret['error']='The error establishing a database connection. Please check wp-config.php file and make sure the information is correct.';
                    }
                    else{
                        $ret['result']=WPVIVID_FAILED;
                        $ret['error']='The pdo_mysql extension is not detected. Please install the extension first or choose wpdb option for Database connection method.';
                    }
                }
                else{
                    $ret['result']=WPVIVID_FAILED;
                    $ret['error']='The pdo_mysql extension is not detected. Please install the extension first or choose wpdb option for Database connection method.';
                }
            }
            else
            {
                $ret['result']=WPVIVID_FAILED;
                $ret['error']=$e->getMessage();
            }
        }
        return $ret;
    }

    public function test_db()
    {
        $test_table_new=uniqid('wpvivid_test_tables_');
        $columns='(test_id int primary key)';
        $test_table = $this->db->exec("CREATE TABLE IF NOT EXISTS $test_table_new $columns");

        if ($test_table!==false)
        {
            $this->last_log='The test to create table succeeds.';

            $test_table = $this->db->exec("INSERT INTO $test_table_new (`test_id`) VALUES ('123')");
            if($test_table!==false)
            {
                $this->last_log='The test to insert into table succeeds.';
                $test_table = $this->db->exec("DROP TABLE IF EXISTS $test_table_new");
                if($test_table!==false)
                {
                    $this->last_log='The test to drop table succeeds.';
                    return true;
                }
                else
                {
                    $error=$this->db->errorInfo();

                    $this->last_error='Unable to drop table. The reason is '.$error[2];
                    return false;
                }
            }
            else
            {
                $error=$this->db->errorInfo();
                $this->last_error='Unable to insert into table. The reason is '.$error[2];
                return false;
            }
        }
        else {
            $error=$this->db->errorInfo();
            $this->last_error='Unable to create table. The reason is '.$error[2];
            return false;
        }
    }

    public function check_max_allow_packet($log)
    {
        $restore_task=get_option('wpvivid_restore_task',array());
        $restore_detail_options=$restore_task['restore_detail_options'];
        $max_allowed_packet=$restore_detail_options['max_allowed_packet'];
        $set_max_allowed_packet=$max_allowed_packet*1024*1024;

        try{
            $max_allowed_packet = $this->db->query("SELECT @@session.max_allowed_packet;");
            if($max_allowed_packet)
            {
                $max_allowed_packet = $max_allowed_packet -> fetchAll();

                if(is_array($max_allowed_packet)&&isset($max_allowed_packet[0])&&isset($max_allowed_packet[0][0]))
                {
                    if($max_allowed_packet[0][0]<$set_max_allowed_packet)
                    {
                        $query='set global max_allowed_packet='.$set_max_allowed_packet;
                        $this->db->exec($query);
                        $this->connect_db();
                        $max_allowed_packet = $this->db->query("SELECT @@session.max_allowed_packet;");
                        $max_allowed_packet = $max_allowed_packet -> fetchAll();
                        if(is_array($max_allowed_packet)&&isset($max_allowed_packet[0])&&isset($max_allowed_packet[0][0]))
                        {
                            $this->max_allow_packet=$max_allowed_packet[0][0];
                        }
                        else
                        {
                            $this->max_allow_packet=1048576;
                        }
                    }
                    else
                    {
                        $this->max_allow_packet=$max_allowed_packet[0][0];
                    }

                }
                else
                {
                    $this->last_log='get max_allowed_packet failed.';
                    $this->max_allow_packet=1048576;
                }
            }
            else
            {
                $this->last_log='get max_allowed_packet failed.';
                $this->max_allow_packet=1048576;
            }
        }
        catch (Exception $error)
        {
            $message = 'An exception has occurred. class: '.get_class($error).';msg: '.$error->getMessage().';code: '.$error->getCode().';line: '.$error->getLine().';in_file: '.$error->getFile().';';
            $log->WriteLog($message, 'warning');
        }
    }

    public function get_max_allow_packet()
    {
        return $this->max_allow_packet;
    }

    public function init_sql_mode()
    {
        $res = $this->db->query('SELECT @@SESSION.sql_mode') -> fetchAll();
        $sql_mod = $res[0][0];

        $modes = explode( ',', $sql_mod );
        $modes = array_change_key_case( $modes, CASE_UPPER );

        $incompatible_modes = array(
            'NO_ZERO_DATE',
            'ONLY_FULL_GROUP_BY',
            'STRICT_TRANS_TABLES',
            'STRICT_ALL_TABLES',
            'TRADITIONAL',
        );

        $incompatible_modes = (array) apply_filters( 'incompatible_sql_modes', $incompatible_modes );

        foreach ( $modes as $i => $mode ) {
            if ( in_array( $mode, $incompatible_modes ) ) {
                unset( $modes[ $i ] );
            }
        }

        $sql_mod = implode( ',', $modes );

        //$temp_sql_mode = str_replace('NO_ENGINE_SUBSTITUTION','',$sql_mod);
        //$temp_sql_mode = 'NO_AUTO_VALUE_ON_ZERO,'.$temp_sql_mode;
        $this->db->query('SET SESSION sql_mode = "'.$sql_mod.'"');
        return true;
    }

    public function set_skip_query($count)
    {
        $this->skip_query=$count;
    }

    public function execute_sql($query)
    {
        if($this->skip_query>10)
        {
            if(strlen($query)>$this->max_allow_packet)
            {
                $this->last_log='skip query size:'.size_format(strlen($query));
                return true;
            }
        }

        if(preg_match('#SET TIME_ZONE=@OLD_TIME_ZONE#', $query))
        {
            return true;
        }
        if(preg_match('#SET SQL_MODE=@OLD_SQL_MODE#', $query))
        {
            return true;
        }
        if(preg_match('#SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS#', $query))
        {
            return true;
        }
        if(preg_match('#SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS#', $query))
        {
            return true;
        }
        if(preg_match('#SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT#', $query))
        {
            return true;
        }
        if(preg_match('#SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS#', $query))
        {
            return true;
        }
        if(preg_match('#SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION#', $query))
        {
            return true;
        }
        if(preg_match('#SET SQL_NOTES=@OLD_SQL_NOTES#', $query))
        {
            return true;
        }

        if ($this->db->exec($query)===false)
        {
            $info=$this->db->errorInfo();

            if($info[1] == 2006)
            {
                if(strlen($query)>$this->max_allow_packet)
                {
                    $this->skip_query++;
                    $this->last_error='max_allow_packet too small:'.size_format($this->max_allow_packet).' query size:'.size_format(strlen($query));
                }
                else
                {
                    $this->last_error='execute sql failed. The reason is '.$info[2];
                }
                $ret=$this->connect_db();
                if($ret['result']==WPVIVID_FAILED)
                {
                    $this->last_log='reconnect failed';

                }
                else {
                    $this->last_log='reconnect succeed';
                }
            }
            else
            {
                $this->last_error='execute sql failed. The reason is '.$info[2];
            }

            return false;
        }
        else
        {
            return true;
        }
    }

    public function query($sql,$output)
    {
        $ret=$this->db->query($sql);
        if($ret===false)
        {
            $error=$this->db->errorInfo();
            $this->last_error=$error[1].' - '.$error[2];
            return false;
        }
        else
        {
            return $ret -> fetchAll();
        }
    }

    public function errorInfo()
    {
        return $this->db->errorInfo();
    }
}

class WPvivid_Restore_DB_Method_2
{
    private $db;
    private $type;

    public function __construct()
    {
        $restore_task=get_option('wpvivid_restore_task',array());
        $restore_detail_options=$restore_task['restore_detail_options'];
        $db_connect_method=$restore_detail_options['db_connect_method'];
        if($db_connect_method === 'wpdb')
        {
            $this->db =new WPvivid_Restore_DB_WPDB_Method_2();
            $this->type='wpdb';
        }
        else{
            $this->db =new WPvivid_Restore_DB_PDO_Mysql_Method_2();
            $this->type='pdo_mysql';
        }
    }

    public function get_last_error()
    {
        return $this->db->last_error;
    }

    public function get_last_log()
    {
        return $this->db->last_log;
    }

    public function get_type()
    {
        return $this->type;
    }

    public function connect_db()
    {
        return $this->db->connect_db();
    }

    public function test_db()
    {
        return $this->db->test_db();
    }

    public function check_max_allow_packet($log)
    {
        $this->db->check_max_allow_packet($log);
    }

    public function get_max_allow_packet()
    {
        return $this->db->get_max_allow_packet();
    }

    public function init_sql_mode()
    {
        $this->db->init_sql_mode();
    }

    public function set_skip_query($count)
    {
        $this->db->set_skip_query($count);
    }

    public function execute_sql($query)
    {
        return $this->db->execute_sql($query);
    }

    public function query($sql,$output=ARRAY_A)
    {
        return $this->db->query($sql,$output);
    }

    public function errorInfo()
    {
        return $this->db->errorInfo();
    }
}includes/new_backup/class-wpvivid-restore-db-2.php000064400000422703151327705670016224 0ustar00<?php

if (!defined('WPVIVID_PLUGIN_DIR'))
{
    die;
}

class WPvivid_Restore_DB_2
{
    public $log;
    public $db_method;

    public $support_engines;
    public $support_charsets;
    public $support_collates;

    public $default_engines;
    public $default_charsets;
    public $default_collates;
    public $old_prefix;
    public $old_base_prefix;
    public $new_prefix;
    public $temp_new_prefix;

    public $old_site_url;
    public $old_home_url;
    public $old_content_url;
    public $old_upload_url;
    public $old_mu_single_site_upload_url;
    public $old_mu_single_home_upload_url;

    public $new_site_url;
    public $new_home_url;
    public $new_content_url;
    public $new_upload_url;

    public $is_migrate;

    public $replacing_table;

    public $is_mu;
    public $skip_table;

    public $sum;
    public $offset;

    public $replace_table_character_set;

    public function __construct($log=false)
    {
        $this->log=$log;
    }

    public function restore($sub_task,$backup_id)
    {
        add_filter('wpvivid_restore_db_skip_replace_tables', array($this, 'skip_tables'),10,2);
        add_filter('wpvivid_restore_db_skip_replace_rows', array($this, 'skip_rows'),10,3);

        add_filter('wpvivid_restore_db_skip_create_tables', array($this, 'skip_create_tables'),10,3);

        $files=$sub_task['unzip_file']['files'];
        $backup = WPvivid_Backuplist::get_backup_by_id($backup_id);
        $backup_item = new WPvivid_Backup_Item($backup);
        $local_path=$backup_item->get_local_path();

        if($sub_task['unzip_file']['unzip_finished']==0)
        {
            foreach ($files as $index=>$file)
            {
                if($file['finished']==1)
                {
                    continue;
                }

                $sub_task['unzip_file']['last_action']='Unzipping';
                $sub_task['unzip_file']['last_unzip_file']=$file['file_name'];
                $sub_task['last_msg']='<span><strong>Extracting file:</strong></span><span>'.$file['file_name'].'</span>';
                $this->update_sub_task($sub_task);

                if(isset($file['has_child']))
                {
                    if(!file_exists($local_path))
                    {
                        @mkdir($local_path);
                    }
                    $this->log->WriteLog('Extracting file:'.$file['parent_file'],'notice');

                    $sub_task['last_msg']='<span><strong>Extracting file:</strong></span><span>'.$file['parent_file'].'</span>';
                    $sub_task['unzip_file']['last_action']='Unzipping';
                    $sub_task['unzip_file']['last_unzip_file']=$file['parent_file'];
                    $this->update_sub_task($sub_task);

                    $extract_files[]=$file['file_name'];
                    $ret=$this->extract_ex($local_path.$file['parent_file'],$extract_files,untrailingslashit($local_path));
                    if($ret['result']!='success')
                    {
                        return $ret;
                    }
                    $this->log->WriteLog('Extracting file:'.$file['parent_file'].' finished.','notice');
                    $file_name=$local_path.$file['file_name'];
                }
                else
                {
                    $file_name=$local_path.$file['file_name'];
                }

                $root_path = $this->transfer_path(WP_CONTENT_DIR . DIRECTORY_SEPARATOR . WPvivid_Setting::get_backupdir());

                $root_path = rtrim($root_path, '/');
                $root_path = rtrim($root_path, DIRECTORY_SEPARATOR);
                $this->log->WriteLog('Extracting file:'.$file_name,'notice');
                $sub_task['last_msg']='<span><strong>Extracting file:</strong></span><span>'.$file_name.'</span>';
                $sub_task['unzip_file']['last_action']='Unzipping';
                $sub_task['unzip_file']['last_unzip_file']=$file['file_name'];
                $this->update_sub_task($sub_task);

                $ret=$this->extract($file_name,untrailingslashit($root_path));
                if($ret['result']!='success')
                {
                    return $ret;
                }
                $this->log->WriteLog('Extracting file:'.$file_name.' finished.','notice');
                $sub_task['unzip_file']['files'][$index]['finished']=1;
            }

            $is_crypt = false;
            if($files[0]['options']['dump_db'] == 1 && $files[0]['options']['file_type'] == 'databases')
            {
                $temp_sql_file=$backup_item->get_backup_path($files[0]['file_name']);
                $sql_files=$this->get_sql_file($temp_sql_file);
                foreach ($sql_files as $tmp_sql_file)
                {
                    $sql_file_name = $tmp_sql_file['file_name'];
                    if(preg_match('/.*\.crypt$/', $sql_file_name))
                    {
                        $is_crypt = true;
                        break;
                    }
                }
            }

            if(isset($files[0]['options']['is_crypt'])&&$files[0]['options']['is_crypt']=='1' || $is_crypt === true)
            {
                $ret['result']='failed';
                $ret['error']='The free version of WPvivid Backup Plugin does not support restoration of encrypted database backups. You can consider upgrading to the pro version as needed.';
                return $ret;
            }
            else
            {
                foreach ($files as $file)
                {
                    $sql_file_path=$backup_item->get_backup_path($file['file_name']);
                    $sql_files=$this->get_sql_file($sql_file_path);
                    foreach ($sql_files as $tmp_sql_file)
                    {
                        $sql_file['sql_file_name']=$tmp_sql_file['file_name'];
                        $sql_file['sql_file_size']=filesize($local_path.$tmp_sql_file['file_name']);
                        $sql_file['sql_offset']=0;
                        $sql_file['finished']=0;
                        $sub_task['exec_sql']['sql_files'][$sql_file['sql_file_name']]= $sql_file;
                    }
                }
            }
            $sub_task['unzip_file']['unzip_finished']=1;

            $ret['result']='success';
            $ret['sub_task']=$sub_task;
            $this->update_sub_task($sub_task);
            return $ret;
        }

        if(!empty($sub_task['exec_sql']['sql_files']))
        {
            $sql_files=$sub_task['exec_sql']['sql_files'];
        }
        else
        {
            foreach ($files as $file)
            {
                $sql_file_path=$backup_item->get_backup_path($file['file_name']);
                $sql_files=$this->get_sql_file($sql_file_path);
                foreach ($sql_files as $tmp_sql_file)
                {
                    $sql_file['sql_file_name']=$tmp_sql_file['file_name'];
                    $sql_file['sql_file_size']=filesize($local_path.$tmp_sql_file['file_name']);
                    $sql_file['sql_offset']=0;
                    $sql_file['finished']=0;
                    $sub_task['exec_sql']['sql_files'][$sql_file['sql_file_name']]= $sql_file;
                }
            }
            $this->update_sub_task($sub_task);
            $sql_files=$sub_task['exec_sql']['sql_files'];
        }

        if(empty($sql_files))
        {
            $ret['result']='failed';
            $ret['error']='Sql file not found.';
            return $ret;
        }

        ksort($sql_files);
        $ret=$this->restore_db($sql_files,$local_path,$sub_task);

        return $ret;
    }

    public function get_sql_file($path)
    {
        if(!class_exists('WPvivid_ZipClass'))
            include_once WPVIVID_PLUGIN_DIR . '/includes/class-wpvivid-zipclass.php';
        $zip=new WPvivid_ZipClass();
        return $zip->list_file($path);
    }

    public function update_sub_task($sub_task=false)
    {
        $restore_task=get_option('wpvivid_restore_task',array());

        if($restore_task['do_sub_task']!==false)
        {
            $key=$restore_task['do_sub_task'];
            $restore_task['update_time']=time();
            if($sub_task!==false)
                $restore_task['sub_tasks'][$key]=$sub_task;
            update_option('wpvivid_restore_task',$restore_task,'no');
        }
    }

    public function extract($file_name,$root_path)
    {
        if (!class_exists('WPvivid_PclZip'))
            include_once WPVIVID_PLUGIN_DIR . '/includes/zip/class-wpvivid-pclzip.php';

        if(!class_exists('WPvivid_ZipClass'))
            include_once WPVIVID_PLUGIN_DIR . '/includes/class-wpvivid-zipclass.php';

        if(!defined('PCLZIP_TEMPORARY_DIR'))
            define(PCLZIP_TEMPORARY_DIR,dirname($root_path));

        $archive = new WPvivid_PclZip($file_name);
        $zip_ret = $archive->extract(WPVIVID_PCLZIP_OPT_PATH, $root_path,WPVIVID_PCLZIP_OPT_REPLACE_NEWER,WPVIVID_PCLZIP_CB_PRE_EXTRACT,'wpvivid_function_pre_extract_callback',WPVIVID_PCLZIP_OPT_TEMP_FILE_THRESHOLD,16);
        if(!$zip_ret)
        {
            $ret['result']='failed';
            $ret['error'] = $archive->errorInfo(true);
            $this->log->WriteLog('Extracting failed. Error:'.$archive->errorInfo(true),'notice');
        }
        else
        {
            $ret['result']='success';
        }
        return $ret;
    }

    public function extract_ex($file_name,$extract_files,$root_path)
    {
        if (!class_exists('WPvivid_PclZip'))
            include_once WPVIVID_PLUGIN_DIR . '/includes/zip/class-wpvivid-pclzip.php';

        if(!class_exists('WPvivid_ZipClass'))
            include_once WPVIVID_PLUGIN_DIR . '/includes/class-wpvivid-zipclass.php';

        if(!defined('PCLZIP_TEMPORARY_DIR'))
            define(PCLZIP_TEMPORARY_DIR,dirname($root_path));

        $archive = new WPvivid_PclZip($file_name);
        $zip_ret = $archive->extract(WPVIVID_PCLZIP_OPT_BY_NAME,$extract_files,WPVIVID_PCLZIP_OPT_PATH, $root_path,WPVIVID_PCLZIP_OPT_REPLACE_NEWER,WPVIVID_PCLZIP_CB_PRE_EXTRACT,'wpvivid_function_pre_extract_callback',WPVIVID_PCLZIP_OPT_TEMP_FILE_THRESHOLD,16);
        if(!$zip_ret)
        {
            $ret['result']='failed';
            $ret['error'] = $archive->errorInfo(true);
            $this->log->WriteLog('Extracting failed. Error:'.$archive->errorInfo(true),'notice');
        }
        else
        {
            $ret['result']='success';
        }
        return $ret;
    }

    private function transfer_path($path)
    {
        $path = str_replace('\\','/',$path);
        $values = explode('/',$path);
        return implode(DIRECTORY_SEPARATOR,$values);
    }

    public function restore_db($sql_files,$local_path,$sub_task)
    {
        $ret['result']='success';
        $ret['sub_task']=$sub_task;

        foreach ($sql_files as $sql_file_name=>$sql_file)
        {
            if($sql_file['finished']==1)
            {
                continue;
            }
            $this->log->WriteLog('Start restoring sql file offset:'.$sql_file['sql_offset'],'notice');
            $sub_task['exec_sql']['last_action']='Importing';
            $this->sum=filesize($local_path.$sql_file_name);
            $this->offset=($sql_file['sql_offset']);

            $ret=$this->exec_sql($sql_file_name,$local_path,$sub_task);
            if($ret['result']=='success')
            {
                $sub_task=$ret['sub_task'];
                break;
            }
            else
            {
                $this->log->WriteLog('Restoring sql failed:'.$ret['error'],'notice');
                $this->remove_tmp_table($sub_task);
                return $ret;
            }
        }

        $exec_sql_finished=true;
        foreach ($sub_task['exec_sql']['sql_files'] as $sql_file_name=>$sql_file)
        {
            if($sql_file['finished']==0)
            {
                $exec_sql_finished=false;
                break;
            }
        }

        $sub_task['exec_sql']['exec_sql_finished']=$exec_sql_finished;
        if($sub_task['exec_sql']['exec_sql_finished']==1)
        {
            $ret=$this->replace_tables_rows($sub_task);
            if($ret['result']=='success')
            {
                $sub_task=$ret['sub_task'];
            }
            else
            {
                $this->log->WriteLog('Restoring failed:'.$ret['error'],'notice');
                $this->remove_tmp_table($sub_task);
                return $ret;
            }
        }

        if($sub_task['exec_sql']['replace_rows_finished']==1)
        {
            $ret=$this->finish_restore_db($sql_files,$local_path,$sub_task);
            if($ret['result']!='success')
            {
                return $ret;
            }
            $this->log->WriteLog('Restore db success','notice');
        }

        return $ret;
    }

    public function str_replace_first($from, $to, $content)
    {
        $from = '/'.preg_quote($from, '/').'/';

        return preg_replace($from, $to, $content, 1);
    }

    public function remove_tmp_table($sub_task)
    {
        global $wpdb;
        $temp_new_prefix='tmp'.$sub_task['exec_sql']['db_id'].'_';
        $tables = $wpdb->get_col($wpdb->prepare('SHOW TABLES LIKE %s', array($temp_new_prefix . '%')));
        foreach ($tables as $table)
        {
            $wpdb->query('DROP TABLE IF EXISTS `' . $table.'`');
        }
        $ret['result']='success';
        return $ret;
    }

    public function exec_sql($sql_file_name,$local_path,$sub_task)
    {
        include_once WPVIVID_PLUGIN_DIR . '/includes/new_backup/class-wpvivid-restore-db-method-2.php';

        $this->db_method=new WPvivid_Restore_DB_Method_2();
        $this->db_method->set_skip_query(0);
        $this->skip_table=false;

        $sql_file=$local_path.$sql_file_name;
        if(!file_exists($sql_file))
        {
            return array('result'=>'failed','error'=>'Database\'s .sql file not found. Please switch the database access method from WPDB to PDO in the plugin\'s Advanced Settings and try it again.');
        }

        $ret=$this->db_method->connect_db();
        if($ret['result']=='failed')
        {
            return $ret;
        }

        if($this->db_method->test_db()===false)
        {
            $this->log->WriteLog($this->db_method->get_last_error(),'notice');
            $ret['result']='failed';
            $ret['error']=$this->db_method->get_last_error();
            return $ret;
        }

        $this->db_method->check_max_allow_packet($this->log);

        $this->log->WriteLog($this->db_method->get_last_log(),'notice');

        $this->db_method->init_sql_mode();

        global $wpdb;
        $wpdb->query('SET FOREIGN_KEY_CHECKS=0;');

        $this->is_mu=false;
        if(isset($sub_task['options']['is_mu']))
        {
            $this->is_mu=true;
        }
        else
        {
            $this->is_mu=false;
        }

        if($sub_task['exec_sql']['init_sql_finished']!=1)
        {
            $ret=$this->init_restore_db($sql_file,$sub_task);
            $sub_task=$ret['sub_task'];
            $sub_task['exec_sql']['init_sql_finished']=1;
            $this->update_sub_task($sub_task);
        }
        else
        {
            $this->default_engines= $sub_task['db_info']['default_engine'];
            $this->default_charsets=$sub_task['db_info']['default_charsets'];
            $this->default_collates=$sub_task['db_info']['default_collates'];
            $this->old_base_prefix=$sub_task['db_info']['base_prefix'];
            $this->new_prefix=$sub_task['db_info']['new_prefix'];
            $this->temp_new_prefix=$sub_task['db_info']['temp_new_prefix'];

            $this->new_site_url= $sub_task['db_info']['new_site_url'];
            $this->new_home_url=$sub_task['db_info']['new_home_url'];
            $this->new_content_url=$sub_task['db_info']['new_content_url'];
            $this->new_upload_url=$sub_task['db_info']['new_upload_url'];
            $this->is_migrate=$sub_task['db_info']['is_migrate'];
            $this->old_site_url = $sub_task['db_info']['old_site_url'];
            $this->old_home_url =$sub_task['db_info']['old_home_url'];
            $this->old_content_url =$sub_task['db_info']['old_content_url'];
            $this->old_upload_url = $sub_task['db_info']['old_upload_url'];
            $this->old_prefix =$sub_task['db_info']['old_prefix'];
            if(isset($sub_task['db_info']['old_mu_single_site_upload_url']))
            {
                $this->old_mu_single_site_upload_url=$sub_task['db_info']['old_mu_single_site_upload_url'];
            }
            else
            {
                $this->old_mu_single_site_upload_url='';
            }

            if(isset($sub_task['db_info']['old_mu_single_home_upload_url']))
            {
                $this->old_mu_single_home_upload_url=$sub_task['db_info']['old_mu_single_home_upload_url'];
            }
            else
            {
                $this->old_mu_single_home_upload_url='';
            }


            $result = $wpdb->get_results("SHOW ENGINES", OBJECT_K);
            foreach ($result as $key=>$value)
            {
                $this->support_engines[]=$key;
            }

            $result = $wpdb->get_results("SHOW CHARACTER SET", OBJECT_K);
            foreach ($result as $key=>$value)
            {
                $this->support_charsets[]=$key;
            }

            $result = $wpdb->get_results("SHOW COLLATION", OBJECT_K);
            foreach ($result as $key=>$value)
            {
                $this->support_collates[$key]=$value;
            }
        }
        $sql_handle = fopen($sql_file,'r');
        if($sql_handle===false)
        {
            $ret['result']=WPVIVID_FAILED;
            $ret['error']='file not found. file name:'.$sql_file;
            return $ret;
        }

        fseek($sql_handle,$sub_task['exec_sql']['sql_files'][$sql_file_name]['sql_offset']);

        $line_num = 0;
        $query='';

        $restore_task=get_option('wpvivid_restore_task',array());
        $restore_detail_options=$restore_task['restore_detail_options'];
        $sql_file_buffer_pre_request=$restore_detail_options['sql_file_buffer_pre_request'];
        $max_buffer_size=$sql_file_buffer_pre_request*1024*1024;
        $this->replace_table_character_set=isset($restore_detail_options['replace_table_character_set'])?$restore_detail_options['replace_table_character_set']:false;

        $current_offset=$sub_task['exec_sql']['sql_files'][$sql_file_name]['sql_offset'];

        if(!empty($sub_task['exec_sql']['current_table']))
        {
            if(apply_filters('wpvivid_restore_db_skip_create_tables',false,$sub_task['exec_sql']['current_table'],$sub_task['options']))
            {
                $this->log->WriteLog('Skipping table '.$sub_task['exec_sql']['current_table'],'notice');
                $this->skip_table=$sub_task['exec_sql']['current_table'];
            }

            /*if($sub_task['exec_sql']['current_need_replace_table'])
            {
                $this->execute_sql('START TRANSACTION',$sub_task);

                $ret_replace_row=$this->do_replace_row($sql_file_name,$sub_task);
                $sub_task=$ret_replace_row['sub_task'];
                if(!$ret_replace_row['finished'])
                {
                    $this->execute_sql('COMMIT',$sub_task);
                    $sub_task['exec_sql']['sql_files'][$sql_file_name]['finished']=0;
                    $ret['result']='success';
                    $ret['sub_task']=$ret_replace_row['sub_task'];
                    //sub_task
                    $this->update_sub_task($sub_task);
                    return $ret;
                }
            }*/

            $sub_task['last_msg']='Importing sql file:'.$sql_file_name.' table: '.$sub_task['exec_sql']['current_table'].' '.size_format($this->offset,2).'/'.size_format($this->sum,2);
            $this->update_sub_task($sub_task);
        }
        else
        {
            $sub_task['last_msg']='Importing sql file:'.$sql_file_name.size_format($this->offset,2).'/'.size_format($this->sum,2);
            $this->update_sub_task($sub_task);
        }

        $this->execute_sql('START TRANSACTION',$sub_task);

        $progress_offset=$current_offset;

        while(!feof($sql_handle))
        {
            if(empty($query))
            {
                $sub_task['exec_sql']['sql_files'][$sql_file_name]['sql_offset']=ftell($sql_handle);
                $sub_task['exec_sql']['last_action']='Importing';

                if(!empty($sub_task['exec_sql']['current_table']))
                {
                    $sub_task['last_msg']='Importing sql file:'.$sql_file_name.' table: '.$sub_task['exec_sql']['current_table'].' '.size_format($sub_task['exec_sql']['sql_files'][$sql_file_name]['sql_offset'],2).'/'.size_format($this->sum,2);
                }
                else
                {
                    $sub_task['last_msg']='Importing sql file:'.$sql_file_name.size_format($sub_task['exec_sql']['sql_files'][$sql_file_name]['sql_offset'],2).'/'.size_format($this->sum,2);
                }
                $read_offset=$sub_task['exec_sql']['sql_files'][$sql_file_name]['sql_offset']-$current_offset;

                if($read_offset>$max_buffer_size)
                {
                    fclose($sql_handle);
                    $this->execute_sql('COMMIT',$sub_task);
                    $this->log->WriteLog('Reading sql file completed offset:'.$sub_task['exec_sql']['sql_files'][$sql_file_name]['sql_offset'],'notice');
                    $sub_task['exec_sql']['sql_files'][$sql_file_name]['finished']=0;
                    $ret['result']='success';
                    $ret['sub_task']=$sub_task;

                    return $ret;
                }
                else
                {
                    if($sub_task['exec_sql']['sql_files'][$sql_file_name]['sql_offset']-$progress_offset>1024*100)
                    {
                        $progress_offset=$sub_task['exec_sql']['sql_files'][$sql_file_name]['sql_offset'];
                        $this->update_sub_task($sub_task);
                    }
                }
            }

            $line = fgets($sql_handle);
            $line_num ++;
            $startWith = substr(trim($line), 0 ,2);
            $startWithEx = substr(trim($line), 0 ,3);
            $endWith = substr(trim($line), -1 ,1);
            $line = rtrim($line);

            if (empty($line) || $startWith == '--' || ($startWith == '/*'&&$startWithEx!='/*!') || $startWith == '//')
            {
                continue;
            }

            $query = $query . $line;
            if ($endWith == ';')
            {
                if (preg_match('#^\\s*CREATE TABLE#', $query))
                {
                    /*if (preg_match('/TYPE=/', $query))
                    {
                        $query = str_replace('TYPE=', 'ENGINE=', $query);
                    }*/
                    $this->skip_table=false;

                    $sub_task['exec_sql']['current_table']=$this->create_table($query,$sub_task['exec_sql']['current_old_table'],$sub_task);

                    if(apply_filters('wpvivid_restore_db_skip_create_tables',false,$sub_task['exec_sql']['current_table'],$sub_task['options']))
                    {
                        $this->log->WriteLog('Skipping table '.$sub_task['exec_sql']['current_table'],'notice');
                        $this->skip_table=$sub_task['exec_sql']['current_table'];
                    }
                    else
                    {
                        $this->log->WriteLog('Creating table '.$sub_task['exec_sql']['current_table'],'notice');
                    }
                    //$sub_task['exec_sql']['current_replace_table_finish']=false;
                    //$sub_task['exec_sql']['current_need_replace_table']=false;
                    $sub_task['exec_sql']['current_replace_row']=0;
                    $this->update_sub_task($sub_task);
                }
                else if(preg_match('#^\\s*LOCK TABLES#',$query))
                {
                    //$this->lock_table($query);
                }
                else if(preg_match('#^\\s*INSERT INTO#', $query))
                {
                    if($this->skip_table!==false&&$this->skip_table==$sub_task['exec_sql']['current_table'])
                    {
                        $query = '';
                        continue;
                    }

                    $this->insert($query,$sub_task);
                }
                else if(preg_match('#^\\s*DROP TABLE #', $query))
                {
                    if($this->skip_table!==false&&$this->skip_table==$sub_task['exec_sql']['current_table'])
                    {

                    }
                    else
                    {
                        if($this->is_migrate&&!empty($sub_task['exec_sql']['current_table']))
                        {
                            $replace_tables['current_table']=$sub_task['exec_sql']['current_table'];
                            $replace_tables['current_old_table']=$sub_task['exec_sql']['current_old_table'];
                            $replace_tables['finished']=0;
                            $replace_tables['offset']=0;
                            $sub_task['exec_sql']['replace_tables'][$sub_task['exec_sql']['current_table']]=$replace_tables;
                            $this->update_sub_task($sub_task);
                            /*if($this->is_og_table($sub_task['exec_sql']['current_old_table']))
                            {
                                $sub_task['exec_sql']['current_need_replace_table']=true;

                                $ret_replace_row=$this->do_replace_row($sql_file_name,$sub_task);
                                $sub_task=$ret_replace_row['sub_task'];
                                if(!$ret_replace_row['finished'])
                                {
                                    $this->execute_sql('COMMIT',$sub_task);
                                    $sub_task['exec_sql']['sql_files'][$sql_file_name]['finished']=0;
                                    $ret['result']='success';
                                    $ret['sub_task']=$sub_task;
                                    $this->update_sub_task($sub_task);
                                    return $ret;
                                }
                            }*/
                        }
                    }


                    $this->drop_table($query,$sub_task);
                }
                else if(preg_match('#\/*!#', $query))
                {
                    if($this->skip_table!==false&&$this->skip_table==$sub_task['exec_sql']['current_table'])
                    {
                        $query = '';
                        continue;
                    }

                    if ($this->replace_table_execute_sql($query,$sub_task['exec_sql']['current_old_table'],$sub_task)===false)
                    {
                        $query = '';
                        continue;
                    }
                }
                else
                {
                    if($this->skip_table!==false&&$this->skip_table==$sub_task['exec_sql']['current_table'])
                    {
                        $query = '';
                        continue;
                    }

                    if ( $this->execute_sql($query,$sub_task)===false)
                    {
                        $this->log->WriteLog($this->db_method->get_last_error(),'notice');
                        $query = '';
                        continue;
                    }
                }
                $query = '';
            }
        }

        $this->execute_sql('COMMIT',$sub_task);

        $sub_task['exec_sql']['sql_files'][$sql_file_name]['sql_offset']=ftell($sql_handle);

        $sub_task['exec_sql']['sql_files'][$sql_file_name]['finished']=1;

        $replace_tables['current_table']=$sub_task['exec_sql']['current_table'];
        $replace_tables['current_old_table']=$sub_task['exec_sql']['current_old_table'];
        $replace_tables['finished']=0;
        $replace_tables['offset']=0;
        $sub_task['exec_sql']['replace_tables'][$sub_task['exec_sql']['current_table']]=$replace_tables;
        $this->update_sub_task($sub_task);

        /*if($this->skip_table!==false&&$this->skip_table==$sub_task['exec_sql']['current_table'])
        {
            $sub_task['exec_sql']['sql_files'][$sql_file_name]['finished']=1;
            $sub_task['exec_sql']['current_replace_table_finish']=false;
        }
        else
        {
            $ret_replace_row=$this->do_replace_row($sql_file_name,$sub_task);
            $sub_task=$ret_replace_row['sub_task'];
            if($ret_replace_row['finished'])
            {
                $sub_task['exec_sql']['sql_files'][$sql_file_name]['finished']=1;
                $sub_task['exec_sql']['current_replace_table_finish']=false;
            }
            else
            {
                $sub_task['exec_sql']['sql_files'][$sql_file_name]['finished']=0;
            }
        }*/

        fclose($sql_handle);
        $sub_task['exec_sql']['last_action']='Importing';
        $this->update_sub_task($sub_task);
        $ret['result']='success';
        $ret['sub_task']=$sub_task;

        return $ret;
    }

    private function init_db($sub_task)
    {
        include_once WPVIVID_PLUGIN_DIR . '/includes/new_backup/class-wpvivid-restore-db-method-2.php';

        $this->db_method=new WPvivid_Restore_DB_Method_2();
        $this->db_method->set_skip_query(0);
        $this->skip_table=false;

        $ret=$this->db_method->connect_db();
        if($ret['result']=='failed')
        {
            return $ret;
        }

        $this->db_method->check_max_allow_packet($this->log);

        $this->log->WriteLog($this->db_method->get_last_log(),'notice');

        $this->db_method->init_sql_mode();

        $this->default_engines= $sub_task['db_info']['default_engine'];
        $this->default_charsets=$sub_task['db_info']['default_charsets'];
        $this->default_collates=$sub_task['db_info']['default_collates'];
        $this->old_base_prefix=$sub_task['db_info']['base_prefix'];
        $this->new_prefix=$sub_task['db_info']['new_prefix'];
        $this->temp_new_prefix=$sub_task['db_info']['temp_new_prefix'];

        $this->new_site_url= $sub_task['db_info']['new_site_url'];
        $this->new_home_url=$sub_task['db_info']['new_home_url'];
        $this->new_content_url=$sub_task['db_info']['new_content_url'];
        $this->new_upload_url=$sub_task['db_info']['new_upload_url'];
        $this->is_migrate=$sub_task['db_info']['is_migrate'];
        $this->old_site_url = $sub_task['db_info']['old_site_url'];
        $this->old_home_url =$sub_task['db_info']['old_home_url'];
        $this->old_content_url =$sub_task['db_info']['old_content_url'];
        $this->old_upload_url = $sub_task['db_info']['old_upload_url'];
        $this->old_prefix =$sub_task['db_info']['old_prefix'];
        if(isset($sub_task['db_info']['old_mu_single_site_upload_url']))
        {
            $this->old_mu_single_site_upload_url=$sub_task['db_info']['old_mu_single_site_upload_url'];
        }
        else
        {
            $this->old_mu_single_site_upload_url='';
        }

        if(isset($sub_task['db_info']['old_mu_single_home_upload_url']))
        {
            $this->old_mu_single_home_upload_url=$sub_task['db_info']['old_mu_single_home_upload_url'];
        }
        else
        {
            $this->old_mu_single_home_upload_url='';
        }

        $ret['result']='success';
        return $ret;
    }

    public function do_replace_row($sql_file_name,$sub_task)
    {
        if($this->is_migrate&&!empty($sub_task['exec_sql']['current_table']))
        {
            if($this->is_og_table($sub_task['exec_sql']['current_old_table']))
            {
                if($sub_task['exec_sql']['current_replace_table_finish'])
                {
                    $ret['finished']=1;
                }
                else
                {
                    $sub_task['exec_sql']['last_action']='Importing';
                    $this->update_sub_task($sub_task);

                    $restore_task=get_option('wpvivid_restore_task',array());
                    $restore_detail_options=$restore_task['restore_detail_options'];
                    $replace_rows_pre_request=$restore_detail_options['replace_rows_pre_request'];


                    $ret=$this->replace_row($sql_file_name,$sub_task,$replace_rows_pre_request);
                    $sub_task['exec_sql']['current_replace_row']=$ret['replace_row'];
                    $sub_task['exec_sql']['current_replace_table_finish']=$ret['current_replace_table_finish'];
                    if($ret['current_replace_table_finish']==false)
                    {
                        $ret['finished']=0;
                    }
                    else
                    {
                        $ret['finished']=1;
                    }
                }
            }
            else
            {
                $ret['finished']=1;
            }

        }
        else
        {
            $ret['finished']=1;
        }

        $ret['result']='success';
        $ret['sub_task']=$sub_task;
        return $ret;
    }

    public function do_replace_row_ex($table_name,$replace_table_data,$sub_task)
    {
        $this->init_db($sub_task);

        if($this->is_migrate&&!empty($replace_table_data['current_table']))
        {
            $tmp_option=$sub_task['options'];
            if($this->is_mu&&isset($tmp_option['site_id']))
            {
                if($this->is_mu_single_og_table($replace_table_data['current_old_table']))
                {
                    $sub_task['exec_sql']['last_action']='Importing';
                    $this->update_sub_task($sub_task);

                    $restore_task=get_option('wpvivid_restore_task',array());

                    $restore_detail_options=$restore_task['restore_detail_options'];
                    $replace_rows_pre_request=$restore_detail_options['replace_rows_pre_request'];

                    $this->execute_sql('START TRANSACTION',$sub_task);
                    $ret=$this->replace_row_ex($table_name,$replace_table_data,$sub_task,$replace_rows_pre_request);
                    $this->execute_sql('COMMIT',$sub_task);

                    $sub_task['exec_sql']['replace_tables'][$table_name]['offset']=$ret['replace_row'];
                    $sub_task['exec_sql']['replace_tables'][$table_name]['finished']=$ret['current_replace_table_finish'];
                    if($ret['current_replace_table_finish']==false)
                    {
                        $ret['finished']=0;
                    }
                    else
                    {
                        $ret['finished']=1;
                    }
                }
            }
            else if($this->is_og_table($replace_table_data['current_old_table']))
            {
                $sub_task['exec_sql']['last_action']='Importing';
                $this->update_sub_task($sub_task);

                $restore_task=get_option('wpvivid_restore_task',array());

                $restore_detail_options=$restore_task['restore_detail_options'];
                $replace_rows_pre_request=$restore_detail_options['replace_rows_pre_request'];

                $this->execute_sql('START TRANSACTION',$sub_task);
                $ret=$this->replace_row_ex($table_name,$replace_table_data,$sub_task,$replace_rows_pre_request);
                $this->execute_sql('COMMIT',$sub_task);

                $sub_task['exec_sql']['replace_tables'][$table_name]['offset']=$ret['replace_row'];
                $sub_task['exec_sql']['replace_tables'][$table_name]['finished']=$ret['current_replace_table_finish'];
                $sub_task['last_msg']=$ret['last_msg'];
                if($ret['current_replace_table_finish']==false)
                {
                    $ret['finished']=0;
                }
                else
                {
                    $ret['finished']=1;
                }
            }
            else
            {
                $sub_task['exec_sql']['replace_tables'][$table_name]['finished']=1;
                $ret['finished']=1;
            }

        }
        else
        {
            $sub_task['exec_sql']['replace_tables'][$table_name]['finished']=1;
            $ret['finished']=1;
        }

        $ret['result']='success';
        $ret['sub_task']=$sub_task;
        return $ret;
    }

    public function replace_tables_rows($sub_task)
    {
        $this->is_mu=false;
        if(isset($sub_task['options']['is_mu']))
        {
            $this->is_mu=true;
        }
        else
        {
            $this->is_mu=false;
        }

        if(empty($sub_task['exec_sql']['replace_tables']))
        {
            $sub_task['exec_sql']['replace_rows_finished']=1;
            $this->update_sub_task($sub_task);
            $ret['result']='success';
            $ret['sub_task']=$sub_task;
            return $ret;
        }
        else
        {
            foreach ($sub_task['exec_sql']['replace_tables'] as $table_name=>$replace_table_data)
            {
                if($replace_table_data['finished']==1)
                {
                    continue;
                }
                $this->log->WriteLog('Start replacing table '.$table_name.' offset:'.$replace_table_data['offset'],'notice');
                $sub_task['exec_sql']['last_action']='Importing';
                $ret=$this->do_replace_row_ex($table_name,$replace_table_data,$sub_task);
                if($ret['result']=='success')
                {
                    $sub_task=$ret['sub_task'];
                    break;
                }
                else
                {
                    $this->log->WriteLog('Restoring failed:'.$ret['error'],'notice');
                    $this->remove_tmp_table($sub_task);
                    return $ret;
                }
            }

            $replace_rows_finished=true;
            foreach ($sub_task['exec_sql']['replace_tables'] as $table_name=>$replace_table_data)
            {
                if($replace_table_data['finished']==0)
                {
                    $replace_rows_finished=false;
                    break;
                }
            }

            $sub_task['exec_sql']['replace_rows_finished']=$replace_rows_finished;
            $this->update_sub_task($sub_task);
            $ret['result']='success';
            $ret['sub_task']=$sub_task;

            return $ret;
        }
    }

    public function init_restore_db($sql_file,$sub_task)
    {
        global $wpdb;

        $option=$sub_task['options'];

        $this->support_engines=array();
        $this->support_charsets=array();
        $this->support_collates=array();
        $this->default_engines=array();
        $this->default_charsets=array();
        $this->default_collates=array();

        if(isset($option['default_engine']))
        {
            $sub_task['db_info']['default_engine']=$this->default_engines=$option['default_engine'];
        }
        else
        {
            $sub_task['db_info']['default_engine'][]=$this->default_engines[]='MyISAM';
        }

        if(isset($option['default_charsets']))
        {
            $sub_task['db_info']['default_charsets']=$this->default_charsets=$option['default_charsets'];
        }
        else
        {
            $sub_task['db_info']['default_charsets'][]=$this->default_charsets[]=DB_CHARSET;
        }

        if(isset($option['default_collations']))
        {
            $sub_task['db_info']['default_collates']=$this->default_collates=$option['default_collations'];
        }
        else
        {
            $sub_task['db_info']['default_collates'][]=$this->default_collates[]=DB_COLLATE;
        }


        if($this->is_mu&&isset($option['site_id']))
        {
            $sub_task['db_info']['base_prefix']=$this->old_base_prefix=$option['base_prefix'];
            $sub_task['db_info']['old_prefix']=$this->old_prefix=$option['blog_prefix'];
            $sub_task['db_info']['site_url']=$this->old_site_url=$option['site_url'];
            $sub_task['db_info']['home_url']=$this->old_home_url=$option['home_url'];
            $this->old_content_url='';
            $this->old_upload_url='';
            $sub_task['db_info']['old_mu_single_site_upload_url']=$this->old_mu_single_site_upload_url=trailingslashit($option['site_url']).'wp-content/uploads/sites/'.$option['site_id'];
            $sub_task['db_info']['old_mu_single_home_upload_url']=$this->old_mu_single_home_upload_url=trailingslashit($option['home_url']).'wp-content/uploads/sites/'.$option['site_id'];

            if($option['overwrite'])
            {
                $sub_task['db_info']['new_prefix']=$this->new_prefix=$wpdb->get_blog_prefix($option['overwrite_site']);
                $sub_task['db_info']['new_site_url']=$this->new_site_url= untrailingslashit(get_site_url($option['overwrite_site']));
                $sub_task['db_info']['new_home_url']=$this->new_home_url=untrailingslashit(get_home_url($option['overwrite_site']));
                $sub_task['db_info']['new_content_url']=$this->new_content_url=untrailingslashit(content_url());
                $upload_dir  = wp_upload_dir();
                $sub_task['db_info']['new_upload_url']=$this->new_upload_url=untrailingslashit($upload_dir['baseurl']);
            }
            else
            {
                $sub_task['db_info']['new_prefix']=$this->new_prefix=$wpdb->get_blog_prefix($option['site_id']);
                $sub_task['db_info']['new_site_url']=$this->new_site_url= untrailingslashit(get_site_url($option['site_id']));
                $sub_task['db_info']['new_home_url']=$this->new_home_url=untrailingslashit(get_home_url($option['site_id']));
                $sub_task['db_info']['new_content_url']=$this->new_content_url=untrailingslashit(content_url());
                $upload_dir  = wp_upload_dir();
                $sub_task['db_info']['new_upload_url']=$this->new_upload_url=untrailingslashit($upload_dir['baseurl']);
            }
            $sub_task['db_info']['temp_new_prefix']=$this->temp_new_prefix='tmp'.$sub_task['exec_sql']['db_id'].'_';
        }
        else
        {
            $this->old_prefix='';
            $this->old_base_prefix='';
            $this->old_mu_single_site_upload_url='';
            $this->old_mu_single_home_upload_url='';
            if(isset($option['mu_migrate']))
            {
                $sub_task['db_info']['base_prefix']=$this->old_base_prefix=$option['base_prefix'];
            }
            else
            {
                $sub_task['db_info']['base_prefix']=$this->old_base_prefix;
            }

            $sub_task['db_info']['new_prefix']=$this->new_prefix=$wpdb->base_prefix;
            $sub_task['db_info']['temp_new_prefix']=$this->temp_new_prefix='tmp'.$sub_task['exec_sql']['db_id'].'_';

            $sub_task['db_info']['new_site_url']= $this->new_site_url= untrailingslashit(site_url());
            $sub_task['db_info']['new_home_url']=$this->new_home_url=untrailingslashit(home_url());
            $sub_task['db_info']['new_content_url']=$this->new_content_url=untrailingslashit(content_url());

            $upload_dir  = wp_upload_dir();
            $sub_task['db_info']['new_upload_url']=$this->new_upload_url=untrailingslashit($upload_dir['baseurl']);
        }


        $wpdb->query('SET FOREIGN_KEY_CHECKS=0;');
        $result = $wpdb->get_results("SHOW ENGINES", OBJECT_K);
        foreach ($result as $key=>$value)
        {
            $this->support_engines[]=$key;
        }

        $result = $wpdb->get_results("SHOW CHARACTER SET", OBJECT_K);
        foreach ($result as $key=>$value)
        {
            $this->support_charsets[]=$key;
        }

        $result = $wpdb->get_results("SHOW COLLATION", OBJECT_K);
        foreach ($result as $key=>$value)
        {
            $this->support_collates[$key]=$value;
        }

        $sql_handle = fopen($sql_file,'r');
        if($sql_handle===false)
        {
            $ret['result']=WPVIVID_FAILED;
            $ret['error']='file not found. file name:'.$sql_file;
            return $ret;
        }

        $this->old_site_url='';
        $this->old_home_url='';
        $this->old_content_url='';
        $this->old_upload_url='';

        $line_num = 0;

        while(!feof($sql_handle))
        {
            if($line_num>50)
                break;
            $line = fgets($sql_handle);
            $line_num ++;
            $startWith = substr(trim($line), 0 ,2);
            $startWithEx = substr(trim($line), 0 ,3);
            $endWith = substr(trim($line), -1 ,1);
            $line = rtrim($line);
            if (empty($line) || $startWith == '--' || ($startWith == '/*'&&$startWithEx!='/*!') || $startWith == '//')
            {
                if ($endWith == ';' && preg_match('- # -',$line))
                {
                    $matcher = array();
                    if(empty($this -> site_url) && preg_match('# site_url: (.*?) #',$line,$matcher))
                    {
                        if(empty( $this->old_site_url))
                        {
                            $sub_task['db_info']['old_site_url']=$this->old_site_url = $matcher[1];
                        }

                    }
                    if(empty($this -> home_url) && preg_match('# home_url: (.*?) #',$line,$matcher))
                    {
                        if(empty( $this->old_home_url))
                        {
                            $sub_task['db_info']['old_home_url']=$this->old_home_url = $matcher[1];
                        }
                    }
                    if(empty($this -> content_url) && preg_match('# content_url: (.*?) #',$line,$matcher))
                    {
                        if(empty( $this->old_content_url))
                        {
                            $sub_task['db_info']['old_content_url']= $this->old_content_url = $matcher[1];
                        }
                    }
                    if(empty($this -> upload_url) && preg_match('# upload_url: (.*?) #',$line,$matcher))
                    {
                        if(empty( $this->old_upload_url))
                        {
                            $sub_task['db_info']['old_upload_url']=$this->old_upload_url = $matcher[1];
                        }

                    }
                    if(empty($this -> table_prefix) && preg_match('# table_prefix: (.*?) #',$line,$matcher))
                    {
                        if(empty( $this->old_prefix))
                        {
                            $sub_task['db_info']['old_prefix']= $this->old_prefix = $matcher[1];
                        }
                    }
                }
                continue;
            }
        }

        if($this->old_prefix!=$this->new_prefix||(!empty($this->old_site_url)&&$this->old_site_url!=$this->new_site_url))
        {
            $sub_task['db_info']['is_migrate']=$this->is_migrate=true;
        }
        else
        {
            $sub_task['db_info']['is_migrate']=false;
        }

        fclose($sql_handle);
        $ret['result']='success';
        $ret['sub_task']=$sub_task;
        return $ret;
    }

    private function create_table($query,&$current_old_table,$sub_task)
    {
        $table_name='';
        if (preg_match('/^\s*CREATE TABLE +\`?([^\`]*)\`?/i', $query, $matches))
        {
            $table_name = $matches[1];
            $current_old_table=$table_name;
        }

        $tmp_option=$sub_task['options'];
        if($this->is_mu&&isset($tmp_option['site_id']))
        {
            if($this->is_mu_single_og_table($table_name))
            {
                if(substr($table_name,strlen($this->old_base_prefix))=='users'||substr($table_name,strlen($this->old_base_prefix))=='usermeta')
                {
                    $new_table_name=$this->temp_new_prefix.substr($table_name,strlen($this->old_base_prefix));
                }
                else
                {
                    $new_table_name=$this->temp_new_prefix.substr($table_name,strlen($this->old_prefix));
                }
            }
            else
            {
                $new_table_name=$current_old_table;
            }
        }
        else if($this->is_og_table($table_name))
        {
            if(!empty($this->old_base_prefix)&&(substr($table_name,strlen($this->old_base_prefix))=='users'||substr($table_name,strlen($this->old_base_prefix))=='usermeta'))
            {
                $new_table_name=$this->temp_new_prefix.substr($table_name,strlen($this->old_base_prefix));
            }
            else
            {
                $new_table_name=$this->temp_new_prefix.substr($table_name,strlen($this->old_prefix));
            }
        }
        else
        {
            $new_table_name=$current_old_table;
        }

        if($this->old_prefix !== '')
        {
            $query=str_replace($table_name,$new_table_name,$query);
        }
        else
        {
            $query=preg_replace('/'.$table_name.'/',$new_table_name,$query, 1);
        }

        $table_name=$new_table_name;

        if(apply_filters('wpvivid_restore_db_skip_create_tables',false,$table_name,$sub_task['options']))
        {
            return $table_name;
        }

        if($this->replace_table_character_set)
        {
            if (preg_match('/ENGINE=([^\s;]+)/', $query, $matches))
            {
                $engine = $matches[1];
                $replace_engine=true;
                foreach ($this->support_engines as $support_engine)
                {
                    if(strtolower($engine)==strtolower($support_engine))
                    {
                        $replace_engine=false;
                        break;
                    }
                }

                if($replace_engine!==false)
                {
                    if(!empty($this->default_engines))
                        $replace_engine=$this->default_engines[0];
                }

                if($replace_engine!==false)
                {
                    $this->log->WriteLog('Create table replace engine:'.$engine.' to :'.$replace_engine,'notice');
                    $query=str_replace("ENGINE=$engine", "ENGINE=$replace_engine", $query);
                }
            }

            if (preg_match('/CHARSET ([^\s;]+)/', $query, $matches)||preg_match('/CHARSET=([^\s;]+)/', $query, $matches))
            {
                $charset = $matches[1];
                $replace_charset=true;
                foreach ($this->support_charsets as $support_charset)
                {
                    if(strtolower($charset)==strtolower($support_charset))
                    {
                        $replace_charset=false;
                        break;
                    }
                }

                if($replace_charset)
                {
                    $replace_charset=$this->default_charsets[0];
                }

                if($replace_charset!==false)
                {
                    $this->log->WriteLog('Create table replace charset:'.$charset.' to :'.$replace_charset,'notice');
                    $query=str_replace("CHARSET=$charset", "CHARSET=$replace_charset", $query);
                    $query=str_replace("CHARSET $charset", "CHARSET=$replace_charset", $query);
                    $charset=$replace_charset;
                }

                $collate='';

                if (preg_match('/ COLLATE ([a-zA-Z0-9._-]+)/i', $query, $matches))
                {
                    $collate = $matches[1];
                }
                else if(preg_match('/ COLLATE=([a-zA-Z0-9._-]+)/i', $query, $matches))
                {
                    $collate = $matches[1];
                }

                if(!empty($collate))
                {
                    $replace_collate=true;
                    foreach ($this->support_collates as $key=>$support_collate)
                    {
                        if(strtolower($charset)==strtolower($support_collate->Charset)&&strtolower($collate)==strtolower($key))
                        {
                            $replace_collate=false;
                            break;
                        }
                    }

                    if($replace_collate)
                    {
                        $replace_collate=false;
                        foreach ($this->support_collates as $key=>$support_collate)
                        {
                            if(strtolower($charset)==strtolower($support_collate->Charset))
                            {
                                if($support_collate->Default=='Yes')
                                {
                                    $replace_collate=$key;
                                }
                            }
                        }

                        if($replace_collate==false)
                        {
                            foreach ($this->support_collates as $key=>$support_collate)
                            {
                                if(strtolower($charset)==strtolower($support_collate->Charset))
                                {
                                    $replace_collate=$key;
                                    break;
                                }
                            }
                        }
                    }

                    if($replace_collate!==false)
                    {
                        $this->log->WriteLog('Create table replace collate:'.$collate.' to :'.$replace_collate.' '.$charset,'notice');
                        $query=str_replace("COLLATE $collate", "COLLATE $replace_collate", $query);
                        $query=str_replace("COLLATE=$collate", "COLLATE=$replace_collate", $query);
                    }
                }
            }
            else
            {
                if (preg_match('/ COLLATE ([a-zA-Z0-9._-]+)/i', $query, $matches))
                {
                    $collate = $matches[1];
                }
                else if(preg_match('/ COLLATE=([a-zA-Z0-9._-]+)/i', $query, $matches))
                {
                    $collate = $matches[1];
                }

                if(!empty($collate))
                {
                    $replace_collate=true;
                    foreach ($this->support_collates as $key=>$support_collate)
                    {
                        if(strtolower($collate)==strtolower($key))
                        {
                            $replace_collate=false;
                            break;
                        }
                    }

                    if($replace_collate)
                    {
                        $replace_collate=false;
                        foreach ($this->support_collates as $key=>$support_collate)
                        {
                            if(strtolower($this->default_charsets[0])==strtolower($support_collate->Charset))
                            {
                                if($support_collate->Default=='Yes')
                                {
                                    $replace_collate=$key;
                                }
                            }
                        }

                        if($replace_collate==false)
                        {
                            foreach ($this->support_collates as $key=>$support_collate)
                            {
                                if(strtolower($this->default_charsets[0])==strtolower($support_collate->Charset))
                                {
                                    $replace_collate=$key;
                                    break;
                                }
                            }
                        }
                    }

                    if($replace_collate!==false)
                    {
                        $this->log->WriteLog('Create table replace collate:'.$collate.' to :'.$replace_collate,'notice');
                        $query=str_replace("COLLATE $collate", "COLLATE $replace_collate", $query);
                        $query=str_replace("COLLATE=$collate", "COLLATE=$replace_collate", $query);
                    }
                }
            }

            if(preg_match('/\/\*!.*\*\//', $query, $matches))
            {
                $annotation_content = $matches[0];
                $query = str_replace($annotation_content, '', $query);
            }
        }


        $constraints = array();
        if (preg_match_all('/CONSTRAINT ([\a-zA-Z0-9_\']+) FOREIGN KEY \([a-zA-z0-9_\', ]+\) REFERENCES \'?([a-zA-z0-9_]+)\'? /i', $query, $constraint_matches))
        {
            $constraints = $constraint_matches;
        }
        else if (preg_match_all('/ FOREIGN KEY \([a-zA-z0-9_\', ]+\) REFERENCES \'?([a-zA-z0-9_]+)\'? /i', $query, $constraint_matches))
        {
            $constraints = $constraint_matches;
        }
        if (!empty($constraints) && $this->old_prefix != $this->new_prefix)
        {
            foreach ($constraints[0] as $constraint)
            {
                $updated_constraint = str_replace($this->old_prefix, $this->new_prefix, $constraint);
                $query = str_replace($constraint, $updated_constraint, $query);
            }
            $this->log->WriteLog('replace foreign key.','notice');
        }


        if($this->execute_sql($query,$sub_task)===false)
        {
            $this->log->WriteLog($this->db_method->get_last_error(),'notice');
        }

        return $table_name;
    }

    private function lock_table($query,$sub_task)
    {
        if (preg_match('/^\s*LOCK TABLES +\`?([^\`]*)\`?/i', $query, $matches))
        {
            $table_name = $matches[1];

            $tmp_option=$sub_task['options'];
            if($this->is_mu&&isset($tmp_option['site_id']))
            {
                if($this->is_mu_single_og_table($table_name))
                {
                    if(substr($table_name,strlen($this->old_base_prefix))=='users'||substr($table_name,strlen($this->old_base_prefix))=='usermeta')
                    {
                        $new_table_name=$this->temp_new_prefix.substr($table_name,strlen($this->old_base_prefix));
                    }
                    else
                    {
                        $new_table_name=$this->temp_new_prefix.substr($table_name,strlen($this->old_prefix));
                    }
                }
                else
                {
                    $new_table_name=$table_name;
                }
            }
            else if($this->is_og_table($table_name))
            {
                if(!empty($this->old_base_prefix)&&(substr($table_name,strlen($this->old_base_prefix))=='users'||substr($table_name,strlen($this->old_base_prefix))=='usermeta'))
                {
                    $new_table_name=$this->temp_new_prefix.substr($table_name,strlen($this->old_base_prefix));
                }
                else
                {
                    $new_table_name=$this->temp_new_prefix.substr($table_name,strlen($this->old_prefix));
                }
            }
            else
            {
                $new_table_name=$table_name;
            }

            $this->log->WriteLog('lock replace table:'.$table_name.' to :'.$new_table_name,'notice');
            $query=str_replace($table_name,$new_table_name,$query);
        }
        if($this->execute_sql($query,$sub_task)===false)
        {
            $this->log->WriteLog($this->db_method->get_last_error(),'notice');
        }
    }

    private function replace_table_execute_sql($query,$table_name,$sub_task)
    {
        $tmp_option=$sub_task['options'];
        if($this->is_mu&&isset($tmp_option['site_id']))
        {
            if($this->is_mu_single_og_table($table_name))
            {
                if(!empty($table_name))
                {
                    if(substr($table_name,strlen($this->old_base_prefix))=='users'||substr($table_name,strlen($this->old_base_prefix))=='usermeta')
                    {
                        $new_table_name=$this->temp_new_prefix.substr($table_name,strlen($this->old_base_prefix));
                    }
                    else
                    {
                        $new_table_name=$this->temp_new_prefix.substr($table_name,strlen($this->old_prefix));
                    }
                    if($this->old_prefix !== '')
                    {
                        $query=str_replace($table_name,$new_table_name,$query);
                    }
                    else
                    {
                        $query=preg_replace('/'.$table_name.'/',$new_table_name,$query, 1);
                    }
                }
            }
            else
            {
                $new_table_name=$table_name;
                $query=str_replace($table_name,$new_table_name,$query);
            }
        }
        else if($this->is_og_table($table_name))
        {
            if(!empty($table_name))
            {
                $new_table_name=$this->temp_new_prefix.substr($table_name,strlen($this->old_prefix));
                $query=str_replace($table_name,$new_table_name,$query);
            }
        }
        else
        {
            $new_table_name=$table_name;
            $query=str_replace($table_name,$new_table_name,$query);
        }

        if($this->execute_sql($query,$sub_task)===false)
        {
            $this->log->WriteLog($this->db_method->get_last_error(),'notice');
        }
    }

    private function insert($query,$sub_task)
    {
        if (preg_match('/^\s*INSERT INTO +\`?([^\`]*)\`?/i', $query, $matches))
        {
            $table_name = $matches[1];

            $tmp_option=$sub_task['options'];
            if($this->is_mu&&isset($tmp_option['site_id']))
            {
                if($this->is_mu_single_og_table($table_name))
                {
                    if(substr($table_name,strlen($this->old_base_prefix))=='users'||substr($table_name,strlen($this->old_base_prefix))=='usermeta')
                    {
                        $new_table_name=$this->temp_new_prefix.substr($table_name,strlen($this->old_base_prefix));
                    }
                    else
                    {
                        $new_table_name=$this->temp_new_prefix.substr($table_name,strlen($this->old_prefix));
                    }
                }
                else
                {
                    $new_table_name=$table_name;
                }
            }
            else if($this->is_og_table($table_name))
            {
                if(!empty($this->old_base_prefix)&&(substr($table_name,strlen($this->old_base_prefix))=='users'||substr($table_name,strlen($this->old_base_prefix))=='usermeta'))
                {
                    $new_table_name=$this->temp_new_prefix.substr($table_name,strlen($this->old_base_prefix));
                }
                else
                {
                    $new_table_name=$this->temp_new_prefix.substr($table_name,strlen($this->old_prefix));
                }
            }
            else
            {
                $new_table_name=$table_name;
            }

            if($this->old_prefix !== '')
            {
                $query=str_replace($table_name,$new_table_name,$query);
            }
            else
            {
                $query=preg_replace('/'.$table_name.'/',$new_table_name,$query, 1);
            }
        }

        if($this->execute_sql($query,$sub_task)===false)
        {
            $this->log->WriteLog($this->db_method->get_last_error(),'notice');
        }

        /*
        $pos=strpos($query,'mainwp_child_');
        if($pos!==false)
        {
            $this->log->WriteLog('skip insert item: '.$query,'notice');
        }
        else{
            if($this->execute_sql($query,$sub_task)===false)
            {
                $this->log->WriteLog($this->db_method->get_last_error(),'notice');
            }
        }*/
    }

    private function drop_table($query,$sub_task)
    {
        if (preg_match('/^\s*DROP TABLE IF EXISTS +\`?([^\`]*)\`?\s*;/i', $query, $matches))
        {
            $table_name = $matches[1];

            $tmp_option=$sub_task['options'];
            if($this->is_mu&&isset($tmp_option['site_id']))
            {
                if($this->is_mu_single_og_table($table_name))
                {
                    if(substr($table_name,strlen($this->old_base_prefix))=='users'||substr($table_name,strlen($this->old_base_prefix))=='usermeta')
                    {
                        $new_table_name=$this->temp_new_prefix.substr($table_name,strlen($this->old_base_prefix));
                    }
                    else
                    {
                        $new_table_name=$this->temp_new_prefix.substr($table_name,strlen($this->old_prefix));
                    }
                }
                else
                {
                    $new_table_name=$table_name;
                }
            }
            else if($this->is_og_table($table_name))
            {
                if(!empty($this->old_base_prefix)&&(substr($table_name,strlen($this->old_base_prefix))=='users'||substr($table_name,strlen($this->old_base_prefix))=='usermeta'))
                {
                    $new_table_name=$this->temp_new_prefix.substr($table_name,strlen($this->old_base_prefix));
                }
                else
                {
                    $new_table_name=$this->temp_new_prefix.substr($table_name,strlen($this->old_prefix));
                }
            }
            else
            {
                $new_table_name=$table_name;
            }

            $query=str_replace($table_name,$new_table_name,$query);
            $this->log->WriteLog('Drop table if exist '.$new_table_name,'notice');
        }
        if($this->execute_sql($query,$sub_task)===false)
        {
            $this->log->WriteLog($this->db_method->get_last_error(),'notice');
        }
    }

    private function replace_row_ex($table_name,$replace_table_data,$sub_task,$max_replace_row=100000)
    {
        global $wpdb;

        $max_replace_row=max(100,$max_replace_row);

        $row=$replace_table_data['offset'];
        $this->replacing_table=$table_name;
        $replace_current_table_finish=false;
        if(substr($table_name, strlen($this->temp_new_prefix))=='options')
        {
            if($this->old_prefix!=$this->new_prefix)
            {
                $update_query ='UPDATE '.$table_name.' SET option_name="'.$this->new_prefix.'user_roles" WHERE option_name="'.$this->old_prefix.'user_roles";';

                if($this->execute_sql($update_query,$sub_task)===false)
                {
                    $this->log->WriteLog($this->db_method->get_last_error(),'notice');
                }
            }
        }

        if(substr($table_name, strlen($this->temp_new_prefix))=='usermeta')
        {
            $tmp_option=$sub_task['options'];
            if($this->is_mu&&isset($tmp_option['site_id']))
            {
                $update_query ='UPDATE '.$table_name.' SET meta_key=REPLACE(meta_key,"'.$this->old_prefix.'","'.$this->new_prefix.'") WHERE meta_key LIKE "'.str_replace('_','\_',$this->old_prefix).'%";';
                if($this->execute_sql($update_query,$sub_task)===false)
                {
                    $this->log->WriteLog($this->db_method->get_last_error(),'notice');
                }

                $select_query='SELECT * FROM '.$table_name.' WHERE meta_key LIKE "%capabilities%";';
                $results = $wpdb->get_results($select_query,ARRAY_A);
                foreach ($results as $item)
                {
                    $update_query='UPDATE '.$table_name.' SET meta_key=\''.$this->new_prefix.'capabilities\' WHERE meta_key=\''.$item['meta_key'].'\';';
                    if($this->execute_sql($update_query,$sub_task)===false)
                    {
                        $this->log->WriteLog($this->db_method->get_last_error(),'notice');
                    }
                }

                $select_query='SELECT * FROM '.$table_name.' WHERE meta_key LIKE "%user_level%";';
                $results = $wpdb->get_results($select_query,ARRAY_A);
                foreach ($results as $item)
                {
                    $update_query='UPDATE '.$table_name.' SET meta_key=\''.$this->new_prefix.'user_level\' WHERE meta_key=\''.$item['meta_key'].'\';';
                    if($this->execute_sql($update_query,$sub_task)===false)
                    {
                        $this->log->WriteLog($this->db_method->get_last_error(),'notice');
                    }
                }
                $ret['result']='success';
                $ret['replace_row']=0;
                $ret['current_replace_table_finish']=true;
                $ret['last_msg']=$sub_task['last_msg'];
                return $ret;
            }
            else
            {
                if($this->old_prefix!=$this->new_prefix)
                {
                    if($this->old_prefix!=='')
                    {
                        $update_query ='UPDATE '.$table_name.' SET meta_key=REPLACE(meta_key,"'.$this->old_prefix.'","'.$this->new_prefix.'") WHERE meta_key LIKE "'.str_replace('_','\_',$this->old_prefix).'%";';

                        if($this->execute_sql($update_query,$sub_task)===false)
                        {
                            $this->log->WriteLog($this->db_method->get_last_error(),'notice');
                        }
                    }
                    else
                    {
                        if(is_multisite())
                        {
                            $select_query='SELECT * FROM '.$table_name.' WHERE meta_key LIKE "%_capabilities%";';
                            $results = $wpdb->get_results($select_query,ARRAY_A);
                            foreach ($results as $item)
                            {
                                $update_query='UPDATE '.$table_name.' SET meta_key=\''.$this->new_prefix.$item['meta_key'].'\' WHERE meta_key=\''.$item['meta_key'].'\';';
                                if($this->execute_sql($update_query,$sub_task)===false)
                                {
                                    $this->log->WriteLog($this->db_method->get_last_error(),'notice');
                                }
                            }

                            $select_query='SELECT * FROM '.$table_name.' WHERE meta_key LIKE "%_user_level%";';
                            $results = $wpdb->get_results($select_query,ARRAY_A);
                            foreach ($results as $item)
                            {
                                $update_query='UPDATE '.$table_name.' SET meta_key=\''.$this->new_prefix.$item['meta_key'].'\' WHERE meta_key=\''.$item['meta_key'].'\';';
                                if($this->execute_sql($update_query,$sub_task)===false)
                                {
                                    $this->log->WriteLog($this->db_method->get_last_error(),'notice');
                                }
                            }
                        }

                        $update_query = 'UPDATE ' . $table_name . ' SET meta_key="' . $this->new_prefix . 'capabilities" WHERE meta_key="' . $this->old_prefix . 'capabilities";';
                        if ($this->execute_sql($update_query, $sub_task) === false)
                        {
                            $this->log->WriteLog($this->db_method->get_last_error(), 'notice');
                        }

                        $update_query = 'UPDATE ' . $table_name . ' SET meta_key="' . $this->new_prefix . 'user_level" WHERE meta_key="' . $this->old_prefix . 'user_level";';
                        if ($this->execute_sql($update_query, $sub_task) === false)
                        {
                            $this->log->WriteLog($this->db_method->get_last_error(), 'notice');
                        }

                        $update_query = 'UPDATE ' . $table_name . ' SET meta_key="' . $this->new_prefix . 'user-settings" WHERE meta_key="' . $this->old_prefix . 'user-settings";';
                        if ($this->execute_sql($update_query, $sub_task) === false)
                        {
                            $this->log->WriteLog($this->db_method->get_last_error(), 'notice');
                        }

                        $update_query = 'UPDATE ' . $table_name . ' SET meta_key="' . $this->new_prefix . 'user-settings-time" WHERE meta_key="' . $this->old_prefix . 'user-settings-time";';
                        if ($this->execute_sql($update_query, $sub_task) === false)
                        {
                            $this->log->WriteLog($this->db_method->get_last_error(), 'notice');
                        }

                        $update_query = 'UPDATE ' . $table_name . ' SET meta_key="' . $this->new_prefix . 'dashboard_quick_press_last_post_id" WHERE meta_key="' . $this->old_prefix . 'dashboard_quick_press_last_post_id";';
                        if ($this->execute_sql($update_query, $sub_task) === false)
                        {
                            $this->log->WriteLog($this->db_method->get_last_error(), 'notice');
                        }
                    }
                    $ret['result']='success';
                    $ret['replace_row']=0;
                    $ret['current_replace_table_finish']=true;
                    $ret['last_msg']=$sub_task['last_msg'];
                    return $ret;
                }
            }
        }

        if(!empty($this->old_base_prefix)&&substr($table_name,strlen($this->temp_new_prefix))=='usermeta')
        {
            $tmp_option=$sub_task['options'];
            if($this->is_mu&&isset($tmp_option['site_id']))
            {
                $update_query ='UPDATE '.$table_name.' SET meta_key=REPLACE(meta_key,"'.$this->old_base_prefix.'","'.$this->new_prefix.'") WHERE meta_key LIKE "'.str_replace('_','\_',$this->old_base_prefix).'%";';
                if($this->execute_sql($update_query,$sub_task)===false)
                {
                    $this->log->WriteLog($this->db_method->get_last_error(),'notice');
                }

                $select_query='SELECT * FROM '.$table_name.' WHERE meta_key LIKE "%_capabilities%";';
                $results = $wpdb->get_results($select_query,ARRAY_A);
                foreach ($results as $item)
                {
                    $update_query='UPDATE '.$table_name.' SET meta_key=\''.$this->new_prefix.'capabilities\' WHERE meta_key=\''.$item['meta_key'].'\';';
                    if($this->execute_sql($update_query,$sub_task)===false)
                    {
                        $this->log->WriteLog($this->db_method->get_last_error(),'notice');
                    }
                }

                $select_query='SELECT * FROM '.$table_name.' WHERE meta_key LIKE "%_user_level%";';
                $results = $wpdb->get_results($select_query,ARRAY_A);
                foreach ($results as $item)
                {
                    $update_query='UPDATE '.$table_name.' SET meta_key=\''.$this->new_prefix.'user_level\' WHERE meta_key=\''.$item['meta_key'].'\';';
                    if($this->execute_sql($update_query,$sub_task)===false)
                    {
                        $this->log->WriteLog($this->db_method->get_last_error(),'notice');
                    }
                }
                $ret['result']='success';
                $ret['replace_row']=0;
                $ret['current_replace_table_finish']=true;
                $ret['last_msg']=$sub_task['last_msg'];
                return $ret;
            }
            else
            {
                if($this->old_base_prefix!=$this->new_prefix)
                {
                    if($this->old_base_prefix!=='')
                    {
                        $update_query ='UPDATE '.$table_name.' SET meta_key=REPLACE(meta_key,"'.$this->old_base_prefix.'","'.$this->new_prefix.'") WHERE meta_key LIKE "'.str_replace('_','\_',$this->old_base_prefix).'%";';

                        if($this->execute_sql($update_query,$sub_task)===false)
                        {
                            $this->log->WriteLog($this->db_method->get_last_error(),'notice');
                        }
                    }
                    else
                    {
                        if(is_multisite())
                        {
                            $select_query='SELECT * FROM '.$table_name.' WHERE meta_key LIKE "%_capabilities%";';
                            $results = $wpdb->get_results($select_query,ARRAY_A);
                            foreach ($results as $item)
                            {
                                $update_query='UPDATE '.$table_name.' SET meta_key=\''.$this->new_prefix.$item['meta_key'].'\' WHERE meta_key=\''.$item['meta_key'].'\';';
                                if($this->execute_sql($update_query,$sub_task)===false)
                                {
                                    $this->log->WriteLog($this->db_method->get_last_error(),'notice');
                                }
                            }

                            $select_query='SELECT * FROM '.$table_name.' WHERE meta_key LIKE "%_user_level%";';
                            $results = $wpdb->get_results($select_query,ARRAY_A);
                            foreach ($results as $item)
                            {
                                $update_query='UPDATE '.$table_name.' SET meta_key=\''.$this->new_prefix.$item['meta_key'].'\' WHERE meta_key=\''.$item['meta_key'].'\';';
                                if($this->execute_sql($update_query,$sub_task)===false)
                                {
                                    $this->log->WriteLog($this->db_method->get_last_error(),'notice');
                                }
                            }
                        }

                        $update_query = 'UPDATE ' . $table_name . ' SET meta_key="' . $this->new_prefix . 'capabilities" WHERE meta_key="' . $this->old_base_prefix . 'capabilities";';
                        if ($this->execute_sql($update_query, $sub_task) === false)
                        {
                            $this->log->WriteLog($this->db_method->get_last_error(), 'notice');
                        }

                        $update_query = 'UPDATE ' . $table_name . ' SET meta_key="' . $this->new_prefix . 'user_level" WHERE meta_key="' . $this->old_base_prefix . 'user_level";';
                        if ($this->execute_sql($update_query, $sub_task) === false)
                        {
                            $this->log->WriteLog($this->db_method->get_last_error(), 'notice');
                        }

                        $update_query = 'UPDATE ' . $table_name . ' SET meta_key="' . $this->new_prefix . 'user-settings" WHERE meta_key="' . $this->old_base_prefix . 'user-settings";';
                        if ($this->execute_sql($update_query, $sub_task) === false)
                        {
                            $this->log->WriteLog($this->db_method->get_last_error(), 'notice');
                        }

                        $update_query = 'UPDATE ' . $table_name . ' SET meta_key="' . $this->new_prefix . 'user-settings-time" WHERE meta_key="' . $this->old_base_prefix . 'user-settings-time";';
                        if ($this->execute_sql($update_query, $sub_task) === false)
                        {
                            $this->log->WriteLog($this->db_method->get_last_error(), 'notice');
                        }

                        $update_query = 'UPDATE ' . $table_name . ' SET meta_key="' . $this->new_prefix . 'dashboard_quick_press_last_post_id" WHERE meta_key="' . $this->old_base_prefix . 'dashboard_quick_press_last_post_id";';
                        if ($this->execute_sql($update_query, $sub_task) === false)
                        {
                            $this->log->WriteLog($this->db_method->get_last_error(), 'notice');
                        }
                    }

                    $ret['result']='success';
                    $ret['replace_row']=0;
                    $ret['current_replace_table_finish']=true;
                    $ret['last_msg']=$sub_task['last_msg'];
                    return $ret;
                }
            }
        }

        if($this->old_site_url==$this->new_site_url)
        {
            $ret['result']='success';
            $ret['replace_row']=0;
            $ret['current_replace_table_finish']=true;
            $ret['last_msg']=$sub_task['last_msg'];
            return $ret;
        }

        if($this->is_mu)
        {
            if(substr($table_name, strlen($this->temp_new_prefix))=='blogs')
            {
                $this->log->WriteLog('Update mu blogs table', 'notice');

                if((preg_match('#^https?://([^/]+)#i', $this->new_home_url, $matches) || preg_match('#^https?://([^/]+)#i', $this->new_site_url, $matches)) && (preg_match('#^https?://([^/]+)#i', $this->old_home_url, $old_matches) || preg_match('#^https?://([^/]+)#i', $this->old_site_url, $old_matches)))
                {
                    $new_string = strtolower($matches[1]);
                    $old_string = strtolower($old_matches[1]);
                    $new_path='';
                    $old_path='';

                    if(defined( 'PATH_CURRENT_SITE' ))
                    {
                        $new_path=PATH_CURRENT_SITE;
                    }

                    $query = 'SELECT * FROM `'.$table_name.'`';
                    $result=$this->db_method->query($query,ARRAY_A);
                    if($result && sizeof($result)>0)
                    {
                        $rows = $result;
                        foreach ($rows as $row)
                        {
                            $update=array();
                            $where=array();

                            if($row['blog_id']==1)
                            {
                                $old_path=$row['path'];
                            }

                            $old_domain_data = $row['domain'];
                            $new_domain_data=str_replace($old_string,$new_string,$old_domain_data);

                            $temp_where='`blog_id` = "' . $row['blog_id'] . '"';
                            if (is_callable(array($wpdb, 'remove_placeholder_escape')))
                                $temp_where = $wpdb->remove_placeholder_escape($temp_where);
                            $where[] = $temp_where;
                            $update[] = '`domain` = "' . $new_domain_data . '"';

                            if(!empty($old_path)&&!empty($new_path))
                            {
                                $old_path_data= $row['path'];
                                $new_path_data=$this->str_replace_first($old_path,$new_path,$old_path_data);
                                $update[] = '`path` = "' . $new_path_data . '"';
                            }

                            if(!empty($update)&&!empty($where))
                            {
                                $update_query = 'UPDATE `'.$table_name.'` SET '.implode(', ', $update).' WHERE '.implode(' AND ', array_filter($where)).';';
                                if($this->execute_sql($update_query,$sub_task)===false)
                                {
                                    $this->log->WriteLog($this->db_method->get_last_error(),'notice');
                                }
                            }
                        }
                    }
                }
            }
        }


        $skip_table=false;
        if(apply_filters('wpvivid_restore_db_skip_replace_tables',$skip_table,$table_name))
        {
            $this->log->WriteLog('Skipping table '.$table_name, 'Warning');
            $ret['result']='success';
            $ret['replace_row']=0;
            $ret['current_replace_table_finish']=true;
            $ret['last_msg']=$sub_task['last_msg'];
            return $ret;
        }

        $query = 'SELECT COUNT(*) FROM `'.$table_name.'`';

        $current_row=0;

        $result=$this->db_method->query($query,ARRAY_N);
        if($result && sizeof($result)>0)
        {
            $count=$result[0][0];
            $this->log->WriteLog('Counting of rows in '.$table_name.': '.$count, 'notice');
            if($count==0)
            {
                $ret['result']='success';
                $ret['replace_row']=0;
                $ret['current_replace_table_finish']=true;
                $ret['last_msg']=$sub_task['last_msg'];
                return $ret;
            }

            $query='DESCRIBE `'.$table_name.'`';
            $result=$this->db_method->query($query,ARRAY_A);
            $columns=array();
            foreach ($result as $data)
            {
                $column['Field']=$data['Field'];
                if($data['Key']=='PRI')
                    $column['PRI']=1;
                else
                    $column['PRI']=0;

                if($data['Type']=='mediumblob')
                {
                    $column['skip']=1;
                }
                $columns[]=$column;
            }

            $page=min(5000,$max_replace_row);

            $update_query='';

            $start_row=$row;
            $replace_row=0;

            for ($current_row = $start_row; $current_row < $count; $current_row += $page)
            {
                $this->log->WriteLog('Replacing the row in '.$current_row. ' line.', 'notice');
                $sub_task['last_msg']='Importing sql file table: '.$table_name.' Replaced row: '.$current_row.'/'.$count.';';

                $this->update_sub_task($sub_task);
                $query = 'SELECT * FROM `'.$table_name.'` LIMIT '.$current_row.', '.$page;
                $replace_row+=$page;
                $result=$this->db_method->query($query,ARRAY_A);
                if($result && sizeof($result)>0)
                {
                    $rows = $result;
                    foreach ($rows as $row)
                    {
                        $update=array();
                        $where=array();
                        foreach ($columns as $column)
                        {
                            if(isset($column['skip']))
                            {
                                //$this->log->WriteLog('Skipping mediumblob type data', 'notice');
                                continue;
                            }

                            $old_data = $row[$column['Field']];
                            if($column['PRI']==1)
                            {
                                $wpdb->escape_by_ref($old_data);
                                $temp_where='`'.$column['Field'].'` = "' . $old_data . '"';
                                if (is_callable(array($wpdb, 'remove_placeholder_escape')))
                                    $temp_where = $wpdb->remove_placeholder_escape($temp_where);
                                $where[] = $temp_where;
                            }

                            $skip_row=false;
                            if(apply_filters('wpvivid_restore_db_skip_replace_rows',$skip_row,$table_name,$column['Field']))
                            {
                                continue;
                            }
                            if (!is_null($old_data))
                            {
                                if(preg_match('/WP_HTML_Token/', $old_data, $matches))
                                {
                                    $this->log->WriteLog('Skip WP_HTML_Token.', 'notice');
                                    continue;
                                }
                            }
                            $new_data=$this->replace_row_data($old_data);
                            if($new_data==$old_data)
                                continue;

                            $wpdb->escape_by_ref($new_data);
                            if (is_callable(array($wpdb, 'remove_placeholder_escape')))
                                $new_data = $wpdb->remove_placeholder_escape($new_data);
                            $update[] = '`'.$column['Field'].'` = "' . $new_data . '"';
                        }

                        if(!empty($update)&&!empty($where))
                        {
                            $temp_query = 'UPDATE `'.$table_name.'` SET '.implode(', ', $update).' WHERE '.implode(' AND ', array_filter($where)).';';
                            $type=$this->db_method->get_type();

                            if($type=='pdo_mysql')
                            {
                                if($update_query=='')
                                {
                                    $update_query=$temp_query;
                                    if(strlen($update_query)>$this->db_method->get_max_allow_packet())
                                    {
                                        if($this->execute_sql($update_query,$sub_task)===false)
                                        {
                                            $this->log->WriteLog($this->db_method->get_last_error(),'notice');
                                        }

                                        $update_query='';
                                    }
                                }
                                else if(strlen($temp_query)+strlen($update_query)>$this->db_method->get_max_allow_packet())
                                {
                                    if($this->execute_sql($update_query,$sub_task)===false)
                                    {
                                        $this->log->WriteLog($this->db_method->get_last_error(),'notice');
                                    }
                                    $update_query=$temp_query;
                                }
                                else
                                {
                                    $update_query.=$temp_query;
                                }
                            }
                            else
                            {
                                $update_query=$temp_query;
                                if($this->execute_sql($update_query,$sub_task)===false)
                                {
                                    $this->log->WriteLog($this->db_method->get_last_error(),'notice');
                                }
                                $update_query='';
                            }

                        }
                        //return;
                    }
                }

                if($replace_row>$max_replace_row)
                {
                    $current_row+= $page;
                    break;
                }
            }

            if(!empty($update_query))
            {
                if($this->execute_sql($update_query,$sub_task)===false)
                {
                    $this->log->WriteLog($this->db_method->get_last_error(),'notice');
                }
            }

            if($current_row >= $count)
            {
                $replace_current_table_finish=true;
            }
        }
        else
        {
            $replace_current_table_finish=true;
            $current_row=0;
        }

        $this->log->WriteLog('Replacing row completed. Current row:'.$current_row, 'notice');

        $ret['result']='success';
        $ret['replace_row']=$current_row;
        $ret['max_replace_row']=$max_replace_row;
        $ret['current_replace_table_finish']=$replace_current_table_finish;
        $ret['last_msg']=$sub_task['last_msg'];
        $this->log->WriteLog(json_encode($ret), 'notice');
        return $ret;
    }

    private function replace_row($sql_file_name,$sub_task,$max_replace_row=100000)
    {
        global $wpdb;

        $max_replace_row=max(100,$max_replace_row);

        $table_name=$sub_task['exec_sql']['current_table'];
        $row=$sub_task['exec_sql']['current_replace_row'];

        $this->replacing_table=$table_name;
        $replace_current_table_finish=false;
        $this->log->WriteLog('Dumping table '.$table_name.' is complete. Start replacing row(s).', 'notice');

        if(substr($table_name, strlen($this->temp_new_prefix))=='options')
        {
            if($this->old_prefix!=$this->new_prefix)
            {
                $update_query ='UPDATE '.$table_name.' SET option_name="'.$this->new_prefix.'user_roles" WHERE option_name="'.$this->old_prefix.'user_roles";';

                if($this->execute_sql($update_query,$sub_task)===false)
                {
                    $this->log->WriteLog($this->db_method->get_last_error(),'notice');
                }
            }
        }

        if(substr($table_name, strlen($this->temp_new_prefix))=='usermeta')
        {
            if($this->old_prefix!=$this->new_prefix)
            {
                if($this->old_prefix!=='')
                {
                    $update_query ='UPDATE '.$table_name.' SET meta_key=REPLACE(meta_key,"'.$this->old_prefix.'","'.$this->new_prefix.'") WHERE meta_key LIKE "'.str_replace('_','\_',$this->old_prefix).'%";';

                    if($this->execute_sql($update_query,$sub_task)===false)
                    {
                        $this->log->WriteLog($this->db_method->get_last_error(),'notice');
                    }
                }
                else
                {
                    if(is_multisite())
                    {
                        $select_query='SELECT * FROM '.$table_name.' WHERE meta_key LIKE "%_capabilities%";';
                        $results = $wpdb->get_results($select_query,ARRAY_A);
                        foreach ($results as $item)
                        {
                            $update_query='UPDATE '.$table_name.' SET meta_key=\''.$this->new_prefix.$item['meta_key'].'\' WHERE meta_key=\''.$item['meta_key'].'\';';
                            if($this->execute_sql($update_query,$sub_task)===false)
                            {
                                $this->log->WriteLog($this->db_method->get_last_error(),'notice');
                            }
                        }

                        $select_query='SELECT * FROM '.$table_name.' WHERE meta_key LIKE "%_user_level%";';
                        $results = $wpdb->get_results($select_query,ARRAY_A);
                        foreach ($results as $item)
                        {
                            $update_query='UPDATE '.$table_name.' SET meta_key=\''.$this->new_prefix.$item['meta_key'].'\' WHERE meta_key=\''.$item['meta_key'].'\';';
                            if($this->execute_sql($update_query,$sub_task)===false)
                            {
                                $this->log->WriteLog($this->db_method->get_last_error(),'notice');
                            }
                        }
                    }

                    $update_query = 'UPDATE ' . $table_name . ' SET meta_key="' . $this->new_prefix . 'capabilities" WHERE meta_key="' . $this->old_prefix . 'capabilities";';
                    if ($this->execute_sql($update_query, $sub_task) === false)
                    {
                        $this->log->WriteLog($this->db_method->get_last_error(), 'notice');
                    }

                    $update_query = 'UPDATE ' . $table_name . ' SET meta_key="' . $this->new_prefix . 'user_level" WHERE meta_key="' . $this->old_prefix . 'user_level";';
                    if ($this->execute_sql($update_query, $sub_task) === false)
                    {
                        $this->log->WriteLog($this->db_method->get_last_error(), 'notice');
                    }

                    $update_query = 'UPDATE ' . $table_name . ' SET meta_key="' . $this->new_prefix . 'user-settings" WHERE meta_key="' . $this->old_prefix . 'user-settings";';
                    if ($this->execute_sql($update_query, $sub_task) === false)
                    {
                        $this->log->WriteLog($this->db_method->get_last_error(), 'notice');
                    }

                    $update_query = 'UPDATE ' . $table_name . ' SET meta_key="' . $this->new_prefix . 'user-settings-time" WHERE meta_key="' . $this->old_prefix . 'user-settings-time";';
                    if ($this->execute_sql($update_query, $sub_task) === false)
                    {
                        $this->log->WriteLog($this->db_method->get_last_error(), 'notice');
                    }

                    $update_query = 'UPDATE ' . $table_name . ' SET meta_key="' . $this->new_prefix . 'dashboard_quick_press_last_post_id" WHERE meta_key="' . $this->old_prefix . 'dashboard_quick_press_last_post_id";';
                    if ($this->execute_sql($update_query, $sub_task) === false)
                    {
                        $this->log->WriteLog($this->db_method->get_last_error(), 'notice');
                    }
                }
                $ret['result']='success';
                $ret['replace_row']=0;
                $ret['current_replace_table_finish']=true;
                return $ret;
            }
        }

        if(!empty($this->old_base_prefix)&&substr($table_name,strlen($this->temp_new_prefix))=='usermeta')
        {
            if($this->old_base_prefix!=$this->new_prefix)
            {
                if($this->old_base_prefix!=='')
                {
                    $update_query ='UPDATE '.$table_name.' SET meta_key=REPLACE(meta_key,"'.$this->old_base_prefix.'","'.$this->new_prefix.'") WHERE meta_key LIKE "'.str_replace('_','\_',$this->old_base_prefix).'%";';

                    if($this->execute_sql($update_query,$sub_task)===false)
                    {
                        $this->log->WriteLog($this->db_method->get_last_error(),'notice');
                    }
                }
                else
                {
                    if(is_multisite())
                    {
                        $select_query='SELECT * FROM '.$table_name.' WHERE meta_key LIKE "%_capabilities%";';
                        $results = $wpdb->get_results($select_query,ARRAY_A);
                        foreach ($results as $item)
                        {
                            $update_query='UPDATE '.$table_name.' SET meta_key=\''.$this->new_prefix.$item['meta_key'].'\' WHERE meta_key=\''.$item['meta_key'].'\';';
                            if($this->execute_sql($update_query,$sub_task)===false)
                            {
                                $this->log->WriteLog($this->db_method->get_last_error(),'notice');
                            }
                        }

                        $select_query='SELECT * FROM '.$table_name.' WHERE meta_key LIKE "%_user_level%";';
                        $results = $wpdb->get_results($select_query,ARRAY_A);
                        foreach ($results as $item)
                        {
                            $update_query='UPDATE '.$table_name.' SET meta_key=\''.$this->new_prefix.$item['meta_key'].'\' WHERE meta_key=\''.$item['meta_key'].'\';';
                            if($this->execute_sql($update_query,$sub_task)===false)
                            {
                                $this->log->WriteLog($this->db_method->get_last_error(),'notice');
                            }
                        }
                    }

                    $update_query = 'UPDATE ' . $table_name . ' SET meta_key="' . $this->new_prefix . 'capabilities" WHERE meta_key="' . $this->old_base_prefix . 'capabilities";';
                    if ($this->execute_sql($update_query, $sub_task) === false)
                    {
                        $this->log->WriteLog($this->db_method->get_last_error(), 'notice');
                    }

                    $update_query = 'UPDATE ' . $table_name . ' SET meta_key="' . $this->new_prefix . 'user_level" WHERE meta_key="' . $this->old_base_prefix . 'user_level";';
                    if ($this->execute_sql($update_query, $sub_task) === false)
                    {
                        $this->log->WriteLog($this->db_method->get_last_error(), 'notice');
                    }

                    $update_query = 'UPDATE ' . $table_name . ' SET meta_key="' . $this->new_prefix . 'user-settings" WHERE meta_key="' . $this->old_base_prefix . 'user-settings";';
                    if ($this->execute_sql($update_query, $sub_task) === false)
                    {
                        $this->log->WriteLog($this->db_method->get_last_error(), 'notice');
                    }

                    $update_query = 'UPDATE ' . $table_name . ' SET meta_key="' . $this->new_prefix . 'user-settings-time" WHERE meta_key="' . $this->old_base_prefix . 'user-settings-time";';
                    if ($this->execute_sql($update_query, $sub_task) === false)
                    {
                        $this->log->WriteLog($this->db_method->get_last_error(), 'notice');
                    }

                    $update_query = 'UPDATE ' . $table_name . ' SET meta_key="' . $this->new_prefix . 'dashboard_quick_press_last_post_id" WHERE meta_key="' . $this->old_base_prefix . 'dashboard_quick_press_last_post_id";';
                    if ($this->execute_sql($update_query, $sub_task) === false)
                    {
                        $this->log->WriteLog($this->db_method->get_last_error(), 'notice');
                    }
                }

                $ret['result']='success';
                $ret['replace_row']=0;
                $ret['current_replace_table_finish']=true;
                return $ret;
            }
        }

        if($this->old_site_url==$this->new_site_url)
        {
            $ret['result']='success';
            $ret['replace_row']=0;
            $ret['current_replace_table_finish']=true;
            return $ret;
        }

        if($this->is_mu)
        {
            if(substr($table_name, strlen($this->temp_new_prefix))=='blogs')
            {
                $this->log->WriteLog('Update mu blogs table', 'notice');

                if((preg_match('#^https?://([^/]+)#i', $this->new_home_url, $matches) || preg_match('#^https?://([^/]+)#i', $this->new_site_url, $matches)) && (preg_match('#^https?://([^/]+)#i', $this->old_home_url, $old_matches) || preg_match('#^https?://([^/]+)#i', $this->old_site_url, $old_matches)))
                {
                    $new_string = strtolower($matches[1]);
                    $old_string = strtolower($old_matches[1]);
                    $new_path='';
                    $old_path='';

                    if(defined( 'PATH_CURRENT_SITE' ))
                    {
                        $new_path=PATH_CURRENT_SITE;
                    }

                    $query = 'SELECT * FROM `'.$table_name.'`';
                    $result=$this->db_method->query($query,ARRAY_A);
                    if($result && sizeof($result)>0)
                    {
                        $rows = $result;
                        foreach ($rows as $row)
                        {
                            $update=array();
                            $where=array();

                            if($row['blog_id']==1)
                            {
                                $old_path=$row['path'];
                            }

                            $old_domain_data = $row['domain'];
                            $new_domain_data=str_replace($old_string,$new_string,$old_domain_data);

                            $temp_where='`blog_id` = "' . $row['blog_id'] . '"';
                            if (is_callable(array($wpdb, 'remove_placeholder_escape')))
                                $temp_where = $wpdb->remove_placeholder_escape($temp_where);
                            $where[] = $temp_where;
                            $update[] = '`domain` = "' . $new_domain_data . '"';

                            if(!empty($old_path)&&!empty($new_path))
                            {
                                $old_path_data= $row['path'];
                                $new_path_data=$this->str_replace_first($old_path,$new_path,$old_path_data);
                                $update[] = '`path` = "' . $new_path_data . '"';
                            }

                            if(!empty($update)&&!empty($where))
                            {
                                $update_query = 'UPDATE `'.$table_name.'` SET '.implode(', ', $update).' WHERE '.implode(' AND ', array_filter($where)).';';
                                if($this->execute_sql($update_query,$sub_task)===false)
                                {
                                    $this->log->WriteLog($this->db_method->get_last_error(),'notice');
                                }
                            }
                        }
                    }
                }
            }
        }


        $skip_table=false;
        if(apply_filters('wpvivid_restore_db_skip_replace_tables',$skip_table,$table_name))
        {
            $this->log->WriteLog('Skipping table '.$table_name, 'Warning');
            $ret['result']='success';
            $ret['replace_row']=0;
            $ret['current_replace_table_finish']=true;
            return $ret;
        }

        $query = 'SELECT COUNT(*) FROM `'.$table_name.'`';

        $current_row=0;

        $result=$this->db_method->query($query,ARRAY_N);
        if($result && sizeof($result)>0)
        {
            $count=$result[0][0];
            $this->log->WriteLog('Counting of rows in '.$table_name.': '.$count, 'notice');
            if($count==0)
            {
                $ret['result']='success';
                $ret['replace_row']=0;
                $ret['current_replace_table_finish']=true;
                return $ret;
            }

            $query='DESCRIBE `'.$table_name.'`';
            $result=$this->db_method->query($query,ARRAY_A);
            $columns=array();
            foreach ($result as $data)
            {
                $column['Field']=$data['Field'];
                if($data['Key']=='PRI')
                    $column['PRI']=1;
                else
                    $column['PRI']=0;

                if($data['Type']=='mediumblob')
                {
                    $column['skip']=1;
                }
                $columns[]=$column;
            }

            $page=min(5000,$max_replace_row);

            $update_query='';

            $start_row=$row;
            $replace_row=0;

            for ($current_row = $start_row; $current_row < $count; $current_row += $page)
            {
                $this->log->WriteLog('Replacing the row in '.$current_row. ' line.', 'notice');
                $sub_task['last_msg']='Importing sql file:'.$sql_file_name.' table: '.$table_name.' Replaced row: '.$current_row.'/'.$count.'; '.size_format($sub_task['exec_sql']['sql_files'][$sql_file_name]['sql_offset'],2).'/'.size_format($this->sum,2);

                $this->update_sub_task($sub_task);
                $query = 'SELECT * FROM `'.$table_name.'` LIMIT '.$current_row.', '.$page;
                $replace_row+=$page;
                $result=$this->db_method->query($query,ARRAY_A);
                if($result && sizeof($result)>0)
                {
                    $rows = $result;
                    foreach ($rows as $row)
                    {
                        $update=array();
                        $where=array();
                        foreach ($columns as $column)
                        {
                            if(isset($column['skip']))
                            {
                                //$this->log->WriteLog('Skipping mediumblob type data', 'notice');
                                continue;
                            }

                            $old_data = $row[$column['Field']];
                            if($column['PRI']==1)
                            {
                                $wpdb->escape_by_ref($old_data);
                                $temp_where='`'.$column['Field'].'` = "' . $old_data . '"';
                                if (is_callable(array($wpdb, 'remove_placeholder_escape')))
                                    $temp_where = $wpdb->remove_placeholder_escape($temp_where);
                                $where[] = $temp_where;
                            }

                            $skip_row=false;
                            if(apply_filters('wpvivid_restore_db_skip_replace_rows',$skip_row,$table_name,$column['Field']))
                            {
                                continue;
                            }
                            $new_data=$this->replace_row_data($old_data);
                            if($new_data==$old_data)
                                continue;

                            $wpdb->escape_by_ref($new_data);
                            if (is_callable(array($wpdb, 'remove_placeholder_escape')))
                                $new_data = $wpdb->remove_placeholder_escape($new_data);
                            $update[] = '`'.$column['Field'].'` = "' . $new_data . '"';
                        }

                        if(!empty($update)&&!empty($where))
                        {
                            $temp_query = 'UPDATE `'.$table_name.'` SET '.implode(', ', $update).' WHERE '.implode(' AND ', array_filter($where)).';';
                            $type=$this->db_method->get_type();

                            if($type=='pdo_mysql')
                            {
                                if($update_query=='')
                                {
                                    $update_query=$temp_query;
                                    if(strlen($update_query)>$this->db_method->get_max_allow_packet())
                                    {
                                        if($this->execute_sql($update_query,$sub_task)===false)
                                        {
                                            $this->log->WriteLog($this->db_method->get_last_error(),'notice');
                                        }

                                        $update_query='';
                                    }
                                }
                                else if(strlen($temp_query)+strlen($update_query)>$this->db_method->get_max_allow_packet())
                                {
                                    if($this->execute_sql($update_query,$sub_task)===false)
                                    {
                                        $this->log->WriteLog($this->db_method->get_last_error(),'notice');
                                    }
                                    $update_query=$temp_query;
                                }
                                else
                                {
                                    $update_query.=$temp_query;
                                }
                            }
                            else
                            {
                                $update_query=$temp_query;
                                if($this->execute_sql($update_query,$sub_task)===false)
                                {
                                    $this->log->WriteLog($this->db_method->get_last_error(),'notice');
                                }
                                $update_query='';
                            }

                        }
                        //return;
                    }
                }

                if($replace_row>$max_replace_row)
                {
                    $current_row+= $page;
                    break;
                }
            }

            if(!empty($update_query))
            {
                if($this->execute_sql($update_query,$sub_task)===false)
                {
                    $this->log->WriteLog($this->db_method->get_last_error(),'notice');
                }
            }

            if($current_row >= $count)
            {
                $replace_current_table_finish=true;
            }
        }
        else
        {
            $replace_current_table_finish=true;
            $current_row=0;
        }

        $this->log->WriteLog('Replacing row completed. Current row:'.$current_row, 'notice');

        $ret['result']='success';
        $ret['replace_row']=$current_row;
        $ret['max_replace_row']=$max_replace_row;
        $ret['current_replace_table_finish']=$replace_current_table_finish;
        $this->log->WriteLog(wp_json_encode($ret), 'notice');
        return $ret;
    }

    private function replace_row_data($old_data)
    {
        try{
            $unserialize_data = @unserialize($old_data, array('allowed_classes' => false));
            if($unserialize_data===false)
            {
                $old_data=$this->replace_string_v2($old_data);
            }
            else
            {
                $old_data=$this->replace_serialize_data($unserialize_data);
                $old_data=serialize($old_data);
            }
        }
        catch (Error $error)
        {
            $old_data=$this->replace_string_v2($old_data);
        }

        return $old_data;
    }

    private function replace_serialize_data($data)
    {
        if(is_string($data))
        {
            $serialize_data =@unserialize($data, array('allowed_classes' => false));
            if($serialize_data===false)
            {
                $data=$this->replace_string_v2($data);
            }
            else
            {
                $data=serialize($this->replace_serialize_data($serialize_data));
            }
        }
        else if(is_array($data))
        {
            foreach ($data as $key => $value)
            {
                if(is_string($value))
                {
                    $data[$key]=$this->replace_string_v2($value);
                }
                else if(is_array($value))
                {
                    $data[$key]=$this->replace_serialize_data($value);
                }
                else if(is_object($value))
                {
                    if (is_a($value, '__PHP_Incomplete_Class'))
                    {
                        //
                    }
                    else
                    {
                        $data[$key]=$this->replace_serialize_data($value);
                    }
                }
            }
        }
        else if(is_object($data))
        {
            $temp = $data; // new $data_class();
            if (is_a($data, '__PHP_Incomplete_Class'))
            {

            }
            else
            {
                $props = get_object_vars($data);
                foreach ($props as $key => $value)
                {
                    if (strpos($key, "\0")===0)
                        continue;
                    if(is_string($value))
                    {
                        $temp->$key =$this->replace_string_v2($value);
                    }
                    else if(is_array($value))
                    {
                        $temp->$key=$this->replace_serialize_data($value);
                    }
                    else if(is_object($value))
                    {
                        $temp->$key=$this->replace_serialize_data($value);
                    }
                }
            }
            $data = $temp;
            unset($temp);
        }

        return $data;
    }

    private function get_remove_http_link($url)
    {
        if (0 === stripos($url, 'https://'))
        {
            $mix_link = '//'.substr($url, 8);
        } elseif (0 === stripos($url, 'http://')) {
            $mix_link = '//'.substr($url, 7);
        }
        else
        {
            $mix_link=false;
        }
        return $mix_link;
    }

    private function get_remove_http_link_ex($url)
    {
        if (0 === stripos($url, 'https://'))
        {
            $mix_link = '\/\/'.substr($url, 8);
        } elseif (0 === stripos($url, 'http://')) {
            $mix_link = '\/\/'.substr($url, 7);
        }
        else
        {
            $mix_link=false;
        }
        return $mix_link;
    }

    private function get_http_link_at_quote($url)
    {
        return str_replace('/','\/',$url);
    }

    public function replace_string_v2($old_string)
    {
        if(!is_string($old_string))
        {
            return $old_string;
        }

        $from=array();
        $to=array();

        $new_url_use_https=false;
        if (0 === stripos($this->new_site_url, 'https://')|| stripos($this->new_site_url, 'https:\/\/'))
        {
            $new_url_use_https=true;
        }
        else if (0 === stripos($this->new_site_url, 'http://')|| stripos($this->new_site_url, 'http:\/\/'))
        {
            $new_url_use_https=false;
        }

        if(isset($this->old_mu_single_site_upload_url) && !empty($this->old_mu_single_site_upload_url))
        {
            $upload_dir = wp_upload_dir();
            $tmp_upload_url=untrailingslashit($upload_dir['baseurl']);

            $from[]=$this->old_mu_single_site_upload_url;
            $to[]=$tmp_upload_url;

            if(isset($this->old_mu_single_home_upload_url) && !empty($this->old_mu_single_home_upload_url))
            {
                if($this->old_mu_single_site_upload_url !== $this->old_mu_single_home_upload_url)
                {
                    $from[]=$this->old_mu_single_home_upload_url;
                    $to[]=$tmp_upload_url;
                }
            }
        }

        if($this->old_site_url!=$this->new_site_url)
        {
            if(substr($this->replacing_table, strlen($this->new_prefix))=='posts'||substr($this->replacing_table, strlen($this->new_prefix))=='postmeta'||substr($this->replacing_table, strlen($this->new_prefix))=='options')
            {
                $remove_http_link=$this->get_remove_http_link($this->old_site_url);
                if($remove_http_link!==false)
                {
                    $new_remove_http_link=$this->get_remove_http_link($this->new_site_url);
                    $from[]=$remove_http_link;
                    $to[]=$new_remove_http_link;

                    if($new_url_use_https)
                    {
                        $from[]='http:'.$new_remove_http_link;
                        $to[]='https:'.$new_remove_http_link;
                    }
                    else
                    {
                        $from[]='https:'.$new_remove_http_link;
                        $to[]='http:'.$new_remove_http_link;
                    }

                    $quote_old_site_url=$this->get_http_link_at_quote($remove_http_link);
                    $quote_new_site_url=$this->get_http_link_at_quote($new_remove_http_link);
                    $from[]=$quote_old_site_url;
                    $to[]=$quote_new_site_url;
                    if($new_url_use_https)
                    {
                        $from[]='http:'.$quote_new_site_url;
                        $to[]='https:'.$quote_new_site_url;
                    }
                    else
                    {
                        $from[]='https:'.$quote_new_site_url;
                        $to[]='http:'.$quote_new_site_url;
                    }
                }
                else
                {
                    $remove_http_link=$this->get_remove_http_link_ex($this->old_site_url);
                    if($remove_http_link!==false)
                    {
                        $new_remove_http_link=$this->get_remove_http_link_ex($this->new_site_url);
                        $from[]=$remove_http_link;
                        $to[]=$new_remove_http_link;

                        if($new_url_use_https)
                        {
                            $from[]='http:'.$new_remove_http_link;
                            $to[]='https:'.$new_remove_http_link;
                        }
                        else
                        {
                            $from[]='https:'.$new_remove_http_link;
                            $to[]='http:'.$new_remove_http_link;
                        }
                    }
                }

                $tmp_old_site_url = str_replace(':', '%3A', $this->old_site_url);
                $tmp_old_site_url = str_replace('/', '%2F', $tmp_old_site_url);

                $tmp_new_site_url = str_replace(':', '%3A', $this->new_site_url);
                $tmp_new_site_url = str_replace('/', '%2F', $tmp_new_site_url);

                $from[]=$tmp_old_site_url;
                $to[]=$tmp_new_site_url;
            }
            else
            {
                $from[]=$this->old_site_url;
                $to[]=$this->new_site_url;

                $from[]=str_replace('/', '\/', $this->old_site_url);
                $to[]=str_replace('/', '\/', $this->new_site_url);

                $tmp_old_site_url = str_replace(':', '%3A', $this->old_site_url);
                $tmp_old_site_url = str_replace('/', '%2F', $tmp_old_site_url);

                $tmp_new_site_url = str_replace(':', '%3A', $this->new_site_url);
                $tmp_new_site_url = str_replace('/', '%2F', $tmp_new_site_url);

                $from[]=$tmp_old_site_url;
                $to[]=$tmp_new_site_url;
            }
        }


        if($this->old_home_url!=$this->old_site_url&&$this->old_home_url!=$this->new_home_url)
        {
            if(substr($this->replacing_table, strlen($this->new_prefix))=='posts'||substr($this->replacing_table, strlen($this->new_prefix))=='postmeta'||substr($this->replacing_table, strlen($this->new_prefix))=='options')
            {
                $remove_http_link=$this->get_remove_http_link($this->old_home_url);
                if($remove_http_link!==false)
                {
                    $new_remove_http_link=$this->get_remove_http_link($this->new_home_url);
                    $from[]=$remove_http_link;
                    $to[]=$new_remove_http_link;

                    if($new_url_use_https)
                    {
                        $from[]='http:'.$new_remove_http_link;
                        $to[]='https:'.$new_remove_http_link;
                    }
                    else
                    {
                        $from[]='https:'.$new_remove_http_link;
                        $to[]='http:'.$new_remove_http_link;
                    }

                    $quote_old_site_url=$this->get_http_link_at_quote($remove_http_link);
                    $quote_new_site_url=$this->get_http_link_at_quote($new_remove_http_link);
                    $from[]=$quote_old_site_url;
                    $to[]=$quote_new_site_url;
                    if($new_url_use_https)
                    {
                        $from[]='http:'.$quote_new_site_url;
                        $to[]='https:'.$quote_new_site_url;
                    }
                    else
                    {
                        $from[]='https:'.$quote_new_site_url;
                        $to[]='http:'.$quote_new_site_url;
                    }
                }
                else
                {
                    $remove_http_link=$this->get_remove_http_link_ex($this->old_home_url);
                    if($remove_http_link!==false)
                    {
                        $new_remove_http_link=$this->get_remove_http_link_ex($this->new_home_url);
                        $from[]=$remove_http_link;
                        $to[]=$new_remove_http_link;

                        if($new_url_use_https)
                        {
                            $from[]='http:'.$new_remove_http_link;
                            $to[]='https:'.$new_remove_http_link;
                        }
                        else
                        {
                            $from[]='https:'.$new_remove_http_link;
                            $to[]='http:'.$new_remove_http_link;
                        }
                    }
                }
            }
            else
            {
                $from[]=$this->old_home_url;
                $to[]=$this->new_home_url;
            }
        }


        if(!empty($from)&&!empty($to))
        {
            $old_string=str_replace($from,$to,$old_string);
        }

        return $old_string;
    }

    public function skip_tables($skip_table,$table_name)
    {
        $skip_tables[]='adrotate_stats';
        $skip_tables[]='login_security_solution_fail';
        $skip_tables[]='icl_strings';
        $skip_tables[]='icl_string_positions';
        $skip_tables[]='icl_string_translations';
        $skip_tables[]='icl_languages_translations';
        $skip_tables[]='slim_stats';
        $skip_tables[]='slim_stats_archive';
        $skip_tables[]='es_online';
        $skip_tables[]='ahm_download_stats';
        $skip_tables[]='woocommerce_order_items';
        $skip_tables[]='woocommerce_sessions';
        $skip_tables[]='redirection_404';
        $skip_tables[]='redirection_logs';
        $skip_tables[]='wbz404_logs';
        $skip_tables[]='wbz404_redirects';
        $skip_tables[]='Counterize';
        $skip_tables[]='Counterize_UserAgents';
        $skip_tables[]='Counterize_Referers';
        $skip_tables[]='et_bloom_stats';
        $skip_tables[]='term_relationships';
        $skip_tables[]='lbakut_activity_log';
        $skip_tables[]='simple_feed_stats';
        $skip_tables[]='svisitor_stat';
        $skip_tables[]='itsec_log';
        $skip_tables[]='relevanssi_log';
        $skip_tables[]='wysija_email_user_stat';
        $skip_tables[]='wponlinebackup_generations';
        $skip_tables[]='blc_instances';
        $skip_tables[]='wp_rp_tags';
        $skip_tables[]='statpress';
        $skip_tables[]='wfHits';
        $skip_tables[]='wp_wfFileMods';
        $skip_tables[]='tts_trafficstats';
        $skip_tables[]='tts_referrer_stats';
        $skip_tables[]='dmsguestbook';
        $skip_tables[]='relevanssi';
        $skip_tables[]='wfFileMods';
        $skip_tables[]='learnpress_sessions';
        $skip_tables[]='icl_string_pages';
        $skip_tables[]='webarx_event_log';
        $skip_tables[]='duplicator_packages';
        $skip_tables[]='wsal_metadata';
        $skip_tables[]='wsal_occurrences';
        $skip_tables[]='simple_history_contexts';
        $skip_tables[]='simple_history';
        $skip_tables[]='wffilemods';
        $skip_tables[]='statpress';
        //
        if(in_array(substr($table_name, strlen($this->temp_new_prefix)),$skip_tables))
        {
            $skip_table=true;
        }
        else
        {
            $skip_table=false;
        }

        return $skip_table;
    }

    public function skip_rows($skip_rows,$table_name,$column_name)
    {
        $row['table_name']='posts';
        $row['column_name']='guid';
        $rows[]=$row;

        foreach ($rows as $row)
        {
            if($column_name==$row['column_name']&&$table_name==$this->temp_new_prefix.$row['table_name'])
            {
                $skip_rows=true;
                break;
            }
        }

        return $skip_rows;
    }

    public function skip_create_tables($skip_table,$table_name,$option)
    {
        if(isset($option['exclude_tables']))
        {
            $table_name=$this->old_prefix.substr($table_name,strlen($this->temp_new_prefix));
            if(array_key_exists($table_name,$option['exclude_tables']))
            {
                $skip_table=true;
            }
        }
        return $skip_table;
    }

    public function check_max_allow_packet_ex()
    {
        $max_all_packet_warning=false;
        include_once WPVIVID_PLUGIN_DIR . '/includes/new_backup/class-wpvivid-restore-db-method-2.php';
        $this->db_method=new WPvivid_Restore_DB_Method_2();

        $this->db_method->set_skip_query(0);

        $ret=$this->db_method->connect_db();
        if($ret['result']==WPVIVID_SUCCESS)
        {
            $max_allowed_packet = $this->db_method->query("SELECT @@session.max_allowed_packet;",ARRAY_N);
            if($max_allowed_packet)
            {
                if(is_array($max_allowed_packet)&&isset($max_allowed_packet[0])&&isset($max_allowed_packet[0][0]))
                {
                    if($max_allowed_packet[0][0]<16777216){
                        $max_all_packet_warning = 'max_allowed_packet = '.size_format($max_allowed_packet[0][0]).' is too small. The recommended value is 16M or higher. Too small value could lead to a failure when importing a larger database.';
                    }
                }
            }
        }
        return $max_all_packet_warning;
    }

    private function execute_sql($query,$sub_task)
    {
        $sub_task['exec_sql']['last_query']=$query;
        //$this->update_sub_task($sub_task);
        return $this->db_method->execute_sql($query);
    }

    public function finish_restore_db($sql_files,$local_path,$sub_task)
    {
        $this->init_db($sub_task);

        $option_table = $this->temp_new_prefix.'options';

        global $wpdb;

        $db_siteurl = false;
        $siteurl_sql = $wpdb->get_results( $wpdb->prepare( "SELECT * FROM $option_table WHERE option_name = %s", 'siteurl' ) );
        foreach ( $siteurl_sql as $siteurl )
        {
            $db_siteurl = untrailingslashit($siteurl->option_value);
        }
        if($db_siteurl !== false)
        {
            $update_query ='UPDATE '.$option_table.' SET option_value="'.$this->new_site_url.'" WHERE option_name="siteurl";';
            $this->log->WriteLog($update_query, 'notice');
            $this->log->WriteLog('update query len:'.strlen($update_query), 'notice');
            $this->execute_sql($update_query,$sub_task);
        }
        else
        {
            $insert_query = $wpdb->prepare("INSERT INTO {$option_table} (option_name,option_value) VALUES ('siteurl',%s)", $this->new_site_url);
            $this->log->WriteLog('siteurl not found, insert: '.$insert_query, 'notice');
            if ($wpdb->get_results($insert_query) === false) {
                $error = $wpdb->last_error;
                $this->log->WriteLog('insert siteurl failed: '.$error, 'notice');
            }
            else
            {
                $this->log->WriteLog('insert siteurl success', 'notice');
            }
        }


        $db_home = false;
        $home_sql = $wpdb->get_results( $wpdb->prepare( "SELECT * FROM $option_table WHERE option_name = %s", 'home' ) );
        foreach ( $home_sql as $home )
        {
            $db_home = untrailingslashit($home->option_value);
        }
        if($db_home !== false)
        {
            $update_query ='UPDATE '.$option_table.' SET option_value="'.$this->new_home_url.'" WHERE option_name="home";';
            $this->log->WriteLog($update_query, 'notice');
            $this->log->WriteLog('update query len:'.strlen($update_query), 'notice');
            $this->execute_sql($update_query,$sub_task);
        }
        else
        {
            $insert_query = $wpdb->prepare("INSERT INTO {$option_table} (option_name,option_value) VALUES ('home',%s)", $this->new_home_url);
            $this->log->WriteLog('home not found, insert: '.$insert_query, 'notice');
            if ($wpdb->get_results($insert_query) === false) {
                $error = $wpdb->last_error;
                $this->log->WriteLog('insert home failed: '.$error, 'notice');
            }
            else
            {
                $this->log->WriteLog('insert home success', 'notice');
            }
        }

        $sub_task['finished']=1;

        foreach ($sql_files as $sql_file_name=>$sql_file)
        {
            $tmp_sql_file=$local_path.$sql_file_name;
            if(file_exists($tmp_sql_file))
            {
                @wp_delete_file($tmp_sql_file);
            }
        }

        $ret['result']='success';
        $ret['sub_task']=$sub_task;
        return $ret;
    }

    public function rename_db($sub_task)
    {
        global $wpdb;

        //restore_db_reset
        $wpdb->query('SET FOREIGN_KEY_CHECKS=0;');

        $restore_task=get_option('wpvivid_restore_task',array());
        $restore_detail_options=$restore_task['restore_detail_options'];
        $restore_db_reset = $restore_detail_options['restore_db_reset'];

        $this->log->WriteLog('Restore db success,now start rename temp table name prefix to new site prefix.','notice');

        $temp_new_prefix='tmp'.$sub_task['exec_sql']['db_id'].'_';

        $tables = $wpdb->get_results('SHOW TABLE STATUS');
        $new_tables = array();
        if (is_array($tables))
        {
            foreach ($tables as $table)
            {
                if (0 !== stripos($table->Name, $temp_new_prefix))
                {
                    continue;
                }
                if (empty($table->Engine))
                {
                    continue;
                }
                $new_tables[] = $table->Name;
            }
        }
        else
        {
            $ret['result']='failed';
            $ret['error']='Getting temp tables failed.';
            return $ret;
        }

        if($restore_db_reset)
        {
            foreach ($tables as $table)
            {
                if (0 !== stripos($table->Name, $wpdb->prefix))
                {
                    continue;
                }
                if (empty($table->Engine))
                {
                    continue;
                }

                $wpdb->query('DROP TABLE IF EXISTS ' . $table->Name);
                $this->log->WriteLog('DROP TABLE IF EXISTS ' . $table->Name,'notice');
            }
        }
        else
        {
            foreach ($new_tables as $table)
            {
                $new_table=$this->str_replace_first($temp_new_prefix,$wpdb->prefix,$table);

                if($wpdb->query('DROP TABLE IF EXISTS ' . $new_table)===false)
                {
                    $error='Failed to drop table. Error:'.$wpdb->last_error;
                    $this->log->WriteLog($error,'error');
                    $ret['result']='failed';
                    $ret['error']=$error;
                    return $ret;
                }
                else
                {
                    $this->log->WriteLog('DROP TABLE IF EXISTS ' . $new_table,'notice');
                }
            }
        }

        foreach ($new_tables as $table)
        {
            $new_table=$this->str_replace_first($temp_new_prefix,$wpdb->prefix,$table);

            if($wpdb->query("RENAME TABLE {$table} TO {$new_table}")===false)
            {
                $error='Failed to rename table. Error:'.$wpdb->last_error;
                $this->log->WriteLog($error,'error');
                $ret['result']='failed';
                $ret['error']=$error;
                return $ret;
            }
            else
            {
                $this->log->WriteLog("RENAME TABLE {$table} TO {$new_table}",'notice');
            }
        }


        wp_cache_flush();
        update_option('wpvivid_restore_task',$restore_task,'no');

        $this->log->WriteLog('Replacing table prefix succeeded.','notice');

        //$ret=$this->test_access();
        $ret['result']='success';
        return $ret;
    }

    public function test_access()
    {
        $url=get_home_url();
        $options=array();
        $options['timeout']=15;
        $request=wp_remote_request($url,$options);
        if(!is_wp_error($request) && ($request['response']['code'] == 200))
        {
            $ret['result']='success';
        }
        else
        {
            $ret['result']='failed';
            if ( is_wp_error( $request ) )
            {
                $error_message = $request->get_error_message();
                $ret['error']="Sorry, something went wrong: $error_message. Please try again later or contact us.";
            }
            else if($request['response']['code'] != 200)
            {
                $ret['error']=$request['response']['message'];
            }
            else {
                $ret['error']=$request;
            }
        }

        return $ret;
    }

    public function is_og_table($table_name)
    {
        $table_prefix=substr($table_name,0,strlen($this->old_prefix));

        if($table_prefix==$this->old_prefix)
        {
            return true;
        }
        else
        {
            return false;
        }
    }

    public function is_mu_single_og_table($table_name)
    {
        $table_prefix=substr($table_name,0,strlen($this->old_prefix));

        if($table_prefix==$this->old_prefix)
        {
            return true;
        }
        else
        {
            $table_prefix=substr($table_name,0,strlen($this->old_base_prefix));
            if($table_prefix==$this->old_base_prefix)
            {
                return true;
            }
            else
            {
                return false;
            }
        }
    }
}includes/new_backup/class-wpvivid-backup2.php000064400000226076151327705670015353 0ustar00<?php

if (!defined('WPVIVID_PLUGIN_DIR'))
{
    die;
}

class WPvivid_Backup_2
{
    public $end_shutdown_function;
    public $current_task_id;
    public $task;
    public function __construct()
    {
        include_once WPVIVID_PLUGIN_DIR . '/includes/new_backup/class-wpvivid-backup-task_2.php';
        include_once WPVIVID_PLUGIN_DIR . '/includes/new_backup/class-wpvivid-mysqldump2.php';
        include_once WPVIVID_PLUGIN_DIR . '/includes/new_backup/class-wpvivid-zip.php';

        add_action('wp_ajax_wpvivid_prepare_backup_2',array( $this,'prepare_backup_2'));
        add_action('wp_ajax_wpvivid_delete_ready_task_2',array($this,'delete_ready_task_2'));

        add_action('wp_ajax_wpvivid_backup_now_2',array( $this,'backup_now_2'));
        add_action('wp_ajax_wpvivid_list_tasks_2',array( $this,'list_tasks'));

        add_action('wp_ajax_wpvivid_shutdown_backup',array( $this,'shutdown_backup'));
        add_action('wp_ajax_wpvivid_delete_task_2',array( $this,'delete_task'));
        add_action('wpvivid_task_monitor_event_2',array( $this,'task_monitor'));
        add_action('wpvivid_backup_2_schedule_event',array( $this,'backup_schedule'));
        //
        add_action('wpvivid_handle_backup_2_succeed',array($this,'handle_backup_succeed'),10);
        add_action('wpvivid_handle_backup_2_failed',array($this,'handle_backup_failed'),10);
        //
        add_action('wpvivid_clean_backup_2_data_event',array($this,'clean_backup_data_event'));
        //
        add_action(WPVIVID_MAIN_SCHEDULE_EVENT,array( $this,'main_schedule'));
        //
        add_filter('wpvivid_exclude_plugins',array($this,'exclude_plugins'),10);
        //migrate
        add_action('wp_ajax_wpvivid_send_backup_to_site_2',array( $this,'send_backup_to_site'));
        add_action('wp_ajax_wpvivid_migrate_now_2',array( $this,'migrate_now'));
        //
        add_filter('wpvivid_default_exclude_folders' ,array($this, 'default_exclude_folders'));
    }

    public function exclude_plugins($exclude_plugins)
    {
        $exclude_plugins[]='wpvivid-backuprestore';
        //$exclude_plugins[]='wp-cerber';
        $exclude_plugins[]='.';
        $exclude_plugins[]='wpvivid-backup-pro';
        $exclude_plugins[]='wpvividdashboard';
        //$exclude_plugins[]='wpvivid-staging';
        return $exclude_plugins;
    }

    public function prepare_backup_2()
    {
        global $wpvivid_plugin;
        check_ajax_referer( 'wpvivid_ajax', 'nonce' );
        $check=current_user_can('manage_options');
        $check=apply_filters('wpvivid_ajax_check_security',$check);
        if(!$check)
        {
            die();
        }

        try
        {
            if(isset($_POST['backup'])&&!empty($_POST['backup']))
            {
                $json = sanitize_text_field($_POST['backup']);
                $json = stripslashes($json);
                $backup_options = json_decode($json, true);
                if (is_null($backup_options))
                {
                    die();
                }

                if(!isset($backup_options['type']))
                {
                    $backup_options['type']='Manual';
                }

                if(!isset($backup_options['backup_files'])||empty($backup_options['backup_files']))
                {
                    $ret['result']='failed';
                    $ret['error']=__('A backup type is required.', 'wpvivid-backuprestore');
                    echo wp_json_encode($ret);
                    die();
                }

                if(!isset($backup_options['local'])||!isset($backup_options['remote']))
                {
                    $ret['result']='failed';
                    $ret['error']=__('Choose at least one storage location for backups.', 'wpvivid-backuprestore');
                    echo wp_json_encode($ret);
                    die();
                }

                if(empty($backup_options['local']) && empty($backup_options['remote']))
                {
                    $ret['result']='failed';
                    $ret['error']=__('Choose at least one storage location for backups.', 'wpvivid-backuprestore');
                    echo wp_json_encode($ret);
                    die();
                }

                if ($backup_options['remote'] === '1')
                {
                    $remote_storage = WPvivid_Setting::get_remote_options();
                    if ($remote_storage == false)
                    {
                        $ret['result']='failed';
                        $ret['error'] = __('There is no default remote storage configured. Please set it up first.', 'wpvivid-backuprestore');
                        echo wp_json_encode($ret);
                        die();
                    }
                }

                if(apply_filters('wpvivid_need_clean_oldest_backup',true,$backup_options))
                {
                    $wpvivid_plugin->clean_oldest_backup();
                }
                do_action('wpvivid_clean_oldest_backup',$backup_options);

                if($this->is_tasks_backup_running())
                {
                    $ret['result']='failed';
                    $ret['error']=__('A task is already running. Please wait until the running task is complete, and try again.', 'wpvivid-backuprestore');
                    echo wp_json_encode($ret);
                    die();
                }

                $settings=$this->get_backup_settings($backup_options);

                $backup=new WPvivid_Backup_Task_2();
                $ret=$backup->new_backup_task($backup_options,$settings);

                if($ret['result']=='success')
                {
                    $html = '';
                    $html = apply_filters('wpvivid_add_backup_list', $html);
                    $ret['html'] = $html;
                }

                echo wp_json_encode($ret);
                die();
            }
        }
        catch (Exception $error)
        {
            $ret['result']='failed';
            $message = 'An exception has occurred. class:'.get_class($error).';msg:'.$error->getMessage().';code:'.$error->getCode().';line:'.$error->getLine().';in_file:'.$error->getFile().';';
            $ret['error'] = $message;
            $id=uniqid('wpvivid-');
            $log_file_name=$id.'_backup';
            $log=new WPvivid_Log();
            $log->CreateLogFile($log_file_name,'no_folder','backup');
            $log->WriteLog($message,'notice');
            $log->CloseFile();
            WPvivid_error_log::create_error_log($log->log_file);
            error_log($message);
            echo wp_json_encode($ret);
            die();
        }
    }

    public function delete_ready_task_2()
    {
        check_ajax_referer( 'wpvivid_ajax', 'nonce' );
        $check=current_user_can('manage_options');
        $check=apply_filters('wpvivid_ajax_check_security',$check);
        if(!$check)
        {
            die();
        }

        $tasks = get_option('wpvivid_task_list', array());
        $delete_ids=array();
        foreach ($tasks as $task)
        {
            if($task['status']['str']=='ready')
            {
                $delete_ids[]=$task['id'];
            }
        }

        if(!empty($delete_ids))
        {
            foreach ($delete_ids as $id)
            {
                unset($tasks[$id]);
            }
            update_option('wpvivid_task_list',$tasks,'no');
        }

        $ret['result'] = 'success';
        echo wp_json_encode($ret);
        die();
    }

    public function is_tasks_backup_running($task_id='')
    {
        $tasks = get_option('wpvivid_task_list', array());

        if(empty($task_id))
        {
            foreach ($tasks as $task)
            {
                if ($task['status']['str']=='running'||$task['status']['str']=='no_responds')
                {
                    return true;
                }
            }
            return false;
        }
        else
        {
            if(isset($tasks[$task_id]))
            {
                $task=$tasks[$task_id];
                if ($task['status']['str']=='running'||$task['status']['str']=='no_responds')
                {
                    return true;
                }
            }
            return false;
        }
    }

    public function get_backup_settings($backup_options)
    {
        $common_setting=get_option('wpvivid_common_setting',array());
        $settings['db_connect_method']=isset($common_setting['db_connect_method'])?$common_setting['db_connect_method']:'wpdb';
        $settings['memory_limit']=isset($common_setting['memory_limit'])?$common_setting['memory_limit']:'512M';
        $settings['max_execution_time']=isset($common_setting['max_execution_time'])?$common_setting['max_execution_time']:900;
        $settings['compress_file_use_cache']=isset($common_setting['compress_file_use_cache'])?$common_setting['compress_file_use_cache']:false;
        $settings['compress_file_count']=isset($common_setting['compress_file_count'])?$common_setting['compress_file_count']:500;
        $settings['max_file_size']=isset($common_setting['max_file_size'])?$common_setting['max_file_size']:200;
        $settings['max_sql_file_size']=isset($common_setting['max_sql_file_size'])?$common_setting['max_sql_file_size']:200;
        $settings['exclude_file_size']=isset($common_setting['exclude_file_size'])?$common_setting['exclude_file_size']:0;
        $settings['max_resume_count']=isset($common_setting['max_resume_count'])?$common_setting['max_resume_count']:6;
        $settings['zip_method']=isset($common_setting['zip_method'])?$common_setting['zip_method']:6;
        $settings['is_merge']=isset($common_setting['ismerge'])?$common_setting['ismerge']:true;
        $settings['save_local']=isset($common_setting['retain_local'])?$common_setting['retain_local']:false;
        $settings['backup_symlink_folder']=isset($common_setting['backup_symlink_folder'])?$common_setting['backup_symlink_folder']:false;

        if(isset($common_setting['zip_method']))
        {
            if($common_setting['zip_method'] === 'ziparchive')
            {
                $settings['zip_method']= 'ziparchive';
            }
            else{
                $settings['zip_method']= 'pclzip';
            }
        }
        else
        {
            if(class_exists('ZipArchive'))
            {
                if(method_exists('ZipArchive', 'addFile'))
                {
                    $settings['zip_method']= 'ziparchive';
                }
                else
                {
                    $settings['zip_method']= 'pclzip';
                }
            }
            else
            {
                $settings['zip_method']= 'pclzip';
            }
        }

        return $settings;
    }

    public function has_backup_task_noreponse()
    {
        $tasks = get_option('wpvivid_task_list', array());
        foreach ($tasks as $task)
        {
            $current_time=time();
            $run_time=$task['status']['run_time'];
            $noreponse_time=$current_time-$run_time;
            if($noreponse_time >= 3600)
            {
                if ($task['status']['str']=='running'||$task['status']['str']=='no_responds'||$task['status']['str']=='wait_resume')
                {
                    unset($tasks[$task['id']]);
                }
            }
        }
        update_option('wpvivid_task_list', $tasks, 'no');
    }

    public function main_schedule($schedule_id='')
    {
        $this->has_backup_task_noreponse();
        global $wpvivid_plugin;

        do_action('wpvivid_set_current_schedule_id', $schedule_id);

        $schedule_options=WPvivid_Schedule::get_schedule($schedule_id);
        if(empty($schedule_options))
        {
            die();
        }

        $schedule_options['backup']['local'] = strval($schedule_options['backup']['local']);
        $schedule_options['backup']['remote'] = strval($schedule_options['backup']['remote']);
        $schedule_options['backup']['ismerge'] = strval($schedule_options['backup']['ismerge']);
        $schedule_options['backup']['lock'] = strval($schedule_options['backup']['lock']);

        if(!isset($schedule_options['backup']['type']))
        {
            $schedule_options['backup']['type']='Cron';
            $schedule_options['backup']['action']='backup';
        }


        $ret = $this->pre_new_backup($schedule_options['backup']);
        if ($ret['result'] == 'success')
        {
            $wpvivid_plugin->flush($ret['task_id']);
            //start backup task.
            $task_msg = WPvivid_taskmanager::get_task($ret['task_id']);
            $wpvivid_plugin->update_last_backup_time($task_msg);

            $this->backup_schedule($ret['task_id']);
        }
        $this->end_shutdown_function=true;
        die();
    }

    public function pre_new_backup($backup_options)
    {
        global $wpvivid_plugin;

        if(apply_filters('wpvivid_need_clean_oldest_backup',true,$backup_options))
        {
            $wpvivid_plugin->clean_oldest_backup();
        }
        do_action('wpvivid_clean_oldest_backup',$backup_options);

        if($this->is_tasks_backup_running())
        {
            $ret['result']='failed';
            $ret['error']=__('We detected that there is already a running backup task. Please wait until it completes then try again.', 'wpvivid');
            return $ret;
        }

        $settings=$this->get_backup_settings($backup_options);

        $backup=new WPvivid_Backup_Task_2();
        $ret=$backup->new_backup_task($backup_options,$settings);

        return $ret;
    }

    public function backup_now_2()
    {
        check_ajax_referer( 'wpvivid_ajax', 'nonce' );
        $check=current_user_can('manage_options');
        $check=apply_filters('wpvivid_ajax_check_security',$check);
        if(!$check)
        {
            die();
        }

        register_shutdown_function(array($this,'deal_backup_shutdown_error'));
        $this->end_shutdown_function=false;

        $task_id = sanitize_key($_POST['task_id']);
        $this->current_task_id=$task_id;
        global $wpvivid_plugin;

        if ($this->is_tasks_backup_running($task_id))
        {
            $ret['result'] = 'failed';
            $ret['error'] = __('We detected that there is already a running backup task. Please wait until it completes then try again.', 'wpvivid-backuprestore');
            echo wp_json_encode($ret);
            die();
        }

        try
        {
            $this->update_backup_task_status($task_id,true,'running');
            $wpvivid_plugin->flush($task_id);
            $this->add_monitor_event($task_id);
            $this->task=new WPvivid_Backup_Task_2($task_id);
            $this->task->set_memory_limit();
            $this->task->set_time_limit();

            $wpvivid_plugin->wpvivid_log->OpenLogFile($this->task->task['options']['log_file_name']);
            $wpvivid_plugin->wpvivid_log->WriteLog('Start backing up.','notice');
            $wpvivid_plugin->wpvivid_log->WriteLogHander();

            if(!$this->task->is_backup_finished())
            {
                $ret=$this->backup();
                $this->task->clear_cache();
                if($ret['result']!='success')
                {
                    $wpvivid_plugin->wpvivid_log->WriteLog('Backup the file ends with an error '. $ret['error'],'error');
                    $this->task->update_backup_task_status(false,'error',false,false,$ret['error']);
                    do_action('wpvivid_handle_backup_2_failed', $task_id);
                    $this->end_shutdown_function=true;
                    $this->clear_monitor_schedule($task_id);
                    die();
                }
            }

            if($this->task->need_upload())
            {
                $ret=$this->upload($task_id);
                if($ret['result'] == WPVIVID_SUCCESS)
                {
                    do_action('wpvivid_handle_backup_2_succeed',$task_id);
                    $this->update_backup_task_status($task_id,false,'completed');
                }
                else
                {
                    $wpvivid_plugin->wpvivid_log->WriteLog('Uploading the file ends with an error '. $ret['error'], 'error');
                    do_action('wpvivid_handle_backup_2_failed',$task_id);
                }
            }
            else
            {
                $wpvivid_plugin->wpvivid_log->WriteLog('Backup completed.','notice');
                do_action('wpvivid_handle_backup_2_succeed', $task_id);
                $this->update_backup_task_status($task_id,false,'completed');
            }
            $this->clear_monitor_schedule($task_id);
        }
        catch (Exception $error)
        {
            //catch error and stop task recording history
            $message = 'An exception has occurred. class:'.get_class($error).';msg:'.$error->getMessage().';code:'.$error->getCode().';line:'.$error->getLine().';in_file:'.$error->getFile().';';
            error_log($message);
            WPvivid_taskmanager::update_backup_task_status($task_id,false,'error',false,false,$message);
            $wpvivid_plugin->wpvivid_log->WriteLog($message,'error');
            do_action('wpvivid_handle_backup_2_failed',$task_id);
            $this->end_shutdown_function=true;
            die();
        }


        $this->end_shutdown_function=true;

        die();
    }

    public function backup_schedule($task_id)
    {
        $this->current_task_id=$task_id;
        if(empty($task_id))
        {
            die();
        }

        if ($this->is_tasks_backup_running($task_id))
        {
            $ret['result'] = 'failed';
            $ret['error'] = __('We detected that there is already a running backup task. Please wait until it completes then try again.', 'wpvivid-backuprestore');
            echo wp_json_encode($ret);
            die();
        }
        $this->end_shutdown_function=false;
        register_shutdown_function(array($this,'deal_backup_shutdown_error'));
        global $wpvivid_plugin;
        try
        {
            WPvivid_taskmanager::update_backup_task_status($task_id,true,'running');
            $wpvivid_plugin->flush($task_id);
            $this->add_monitor_event($task_id);
            $this->task=new WPvivid_Backup_Task_2($task_id);
            $this->task->set_memory_limit();
            $this->task->set_time_limit();

            $this->task->update_schedule_last_backup_time();

            $wpvivid_plugin->wpvivid_log->OpenLogFile(WPvivid_taskmanager::get_task_options($task_id,'log_file_name'));
            $wpvivid_plugin->wpvivid_log->WriteLog('Start backing up.','notice');
            $wpvivid_plugin->wpvivid_log->WriteLogHander();

            if(!$this->task->is_backup_finished())
            {
                $ret=$this->backup();
                $this->task->clear_cache();
                if($ret['result']!='success')
                {
                    $wpvivid_plugin->wpvivid_log->WriteLog('Backup the file ends with an error '. $ret['error'],'error');
                    $this->task->update_backup_task_status(false,'error',false,false,$ret['error']);
                    do_action('wpvivid_handle_backup_2_failed', $task_id);
                    $this->end_shutdown_function=true;
                    $this->clear_monitor_schedule($task_id);
                    die();
                }
            }

            if($this->task->need_upload())
            {
                $ret=$this->upload($task_id);
                if($ret['result'] == WPVIVID_SUCCESS)
                {
                    do_action('wpvivid_handle_backup_2_succeed',$task_id);
                    WPvivid_taskmanager::update_backup_task_status($task_id,false,'completed');
                }
                else
                {
                    $wpvivid_plugin->wpvivid_log->WriteLog('Uploading the file ends with an error '. $ret['error'], 'error');
                    do_action('wpvivid_handle_backup_2_failed',$task_id);
                }
            }
            else
            {
                $wpvivid_plugin->wpvivid_log->WriteLog('Backup completed.','notice');
                do_action('wpvivid_handle_backup_2_succeed', $task_id);
                WPvivid_taskmanager::update_backup_task_status($task_id,false,'completed');
            }
            $this->clear_monitor_schedule($task_id);
        }
        catch (Exception $error)
        {
            //catch error and stop task recording history
            $message = 'An exception has occurred. class:'.get_class($error).';msg:'.$error->getMessage().';code:'.$error->getCode().';line:'.$error->getLine().';in_file:'.$error->getFile().';';
            error_log($message);
            WPvivid_taskmanager::update_backup_task_status($task_id,false,'error',false,false,$message);
            $wpvivid_plugin->wpvivid_log->WriteLog($message,'error');
            do_action('wpvivid_handle_backup_2_failed',$task_id);
            $this->end_shutdown_function=true;
            die();
        }

        $this->end_shutdown_function=true;

        die();
    }

    public function backup()
    {
        $ret['result']='success';

        $this->task->wpvivid_check_add_litespeed_server();

        while (!$this->task->is_backup_finished())
        {
            if($this->task->check_cancel_backup())
            {
                $this->end_shutdown_function=true;
                die();
            }

            $job=$this->task->get_next_job();

            if($job===false)
                break;

            $this->task->set_time_limit();
            $ret=$this->task->do_backup_job($job);
            if($ret['result']!='success')
            {
                break;
            }
        }

        if($ret['result']==='success')
        {
            $check_res = apply_filters('wpvivid_check_backup_completeness', true, $this->task->task_id);
            if(!$check_res){
                $ret['result'] = 'failed';
                $ret['error'] = 'We have detected that this backup is either corrupted or incomplete. Please make sure your server disk space is sufficient then create a new backup. In order to successfully back up/restore a website, the amount of free server disk space needs to be at least twice the size of the website';
            }
        }

        return $ret;
    }

    public function upload($task_id)
    {
        global $wpvivid_plugin;

        $files=$this->task->get_backup_files();
        $wpvivid_plugin->wpvivid_log->WriteLog('files: '.wp_json_encode($files),'notice');
        $remote_options=$this->task->get_remote_options();

        $remote_option=array_shift($remote_options);

        if(!class_exists('WPvivid_Remote_collection'))
        {
            include_once WPVIVID_PLUGIN_DIR . '/includes/class-wpvivid-remote-collection.php';
            $wpvivid_plugin->remote_collection=new WPvivid_Remote_collection();
        }
        $remote=$wpvivid_plugin->remote_collection->get_remote($remote_option);

        try
        {
            $result=$remote->upload($task_id,$files,array($this,'upload_callback'));
            if($result['result']=='success')
            {
                $this->update_backup_task_status($task_id,false,'running',false,0);
                $wpvivid_plugin->wpvivid_log->WriteLog('Finish upload to '.$remote_option['type'],'notice');
                if($remote_option['type']!=='send_to_site')
                {
                    WPvivid_taskmanager::update_backup_sub_task_progress($task_id,'upload',$remote_option['id'],WPVIVID_UPLOAD_SUCCESS,'Finish upload to'.$remote_option['type']);
                }
                WPvivid_taskmanager::update_backup_main_task_progress($task_id,'upload',100,1);
                WPvivid_taskmanager::update_backup_task_status($task_id,false,'completed');
                return array('result' => 'success');
            }
            else
            {
                $wpvivid_plugin->wpvivid_log->WriteLog('Finish upload to '.$remote_option['type'].' error:'.$result['error'],'notice');
                if($remote_option['type']!=='send_to_site')
                {
                    WPvivid_taskmanager::update_backup_sub_task_progress($task_id,'upload',$remote_option['id'],WPVIVID_UPLOAD_FAILED,'Finish upload to'.$remote_option['type']);
                }
                $remote ->cleanup($files);
                $last_error=$result['error'];
                WPvivid_taskmanager::update_backup_task_status($task_id,false,'error',false,false,$last_error);
                return array('result' => 'failed' , 'error' => $last_error);
            }
        }
        catch (Exception $e)
        {
            //catch error and stop task recording history
            $wpvivid_plugin->wpvivid_log->WriteLog('Finish upload to '.$remote_option['type'].' error:'.$e->getMessage(),'notice');
            WPvivid_taskmanager::update_backup_sub_task_progress($task_id,'upload',$remote_option['id'],WPVIVID_UPLOAD_FAILED,'Finish upload to'.$remote_option['type']);
            $last_error=$e->getMessage();

            WPvivid_taskmanager::update_backup_task_status($task_id,false,'error',false,false,$last_error);
            return array('result' => 'failed' , 'error' => $last_error);
        }

    }

    public function upload_callback($offset,$current_name,$current_size,$last_time,$last_size)
    {
        $job_data=array();
        $upload_data=array();
        $upload_data['offset']=$offset;
        $upload_data['current_name']=$current_name;
        $upload_data['current_size']=$current_size;
        $upload_data['last_time']=$last_time;
        $upload_data['last_size']=$last_size;
        $upload_data['descript']='Uploading '.$current_name;
        $v =( $offset - $last_size ) / (time() - $last_time);
        $v /= 1000;
        $v=round($v,2);

        global $wpvivid_plugin;
        $this->task->check_cancel_backup();

        $message='Uploading '.$current_name.' Total size: '.size_format($current_size,2).' Uploaded: '.size_format($offset,2).' speed:'.$v.'kb/s';
        $wpvivid_plugin->wpvivid_log->WriteLog($message,'notice');
        $progress=intval(($offset/$current_size)*100);
        WPvivid_taskmanager::update_backup_main_task_progress($this->current_task_id,'upload',$progress,0);
        WPvivid_taskmanager::update_backup_sub_task_progress($this->current_task_id,'upload','',WPVIVID_UPLOAD_UNDO,$message, $job_data, $upload_data);
    }

    public function handle_backup_succeed($task_id)
    {
        $task= new WPvivid_Backup_Task_2($task_id);
        $task->update_end_time();
        if($task->task['type']=='Migrate')
        {
            $backup_success_count = WPvivid_Setting::get_option('wpvivid_transfer_success_count');
            if (empty($backup_success_count))
            {
                $backup_success_count = 0;
            }
            $backup_success_count++;
            WPvivid_Setting::update_option('wpvivid_transfer_success_count', $backup_success_count);

            global $wpvivid_plugin;
            $wpvivid_plugin->wpvivid_log->WriteLog('Upload finished. Delete task '.$task->task['id'], 'notice');
            $task->clean_local_files();

            $task->wpvivid_check_clear_litespeed_rule();
            $this->clear_monitor_schedule($task_id);
        }
        else
        {
            $backup=WPvivid_Backuplist::get_backup_by_id($task_id);
            if($backup!==false)
            {
                $task->add_exist_backup($task_id);
            }
            else
            {
                $task->add_new_backup();
            }

            if($task->need_upload())
            {
                if(!$task->is_save_local())
                {
                    $task->clean_local_files();
                }

                $remote_options=$this->task->get_remote_options();

                WPvivid_Backuplist::update_backup($task_id,'remote', $remote_options);
            }

            set_time_limit(120);
            $backup_ids=array();
            $backup_ids=apply_filters('wpvivid_get_oldest_backup_ids',$backup_ids,true);
            global $wpvivid_plugin;
            if(!empty($backup_ids))
            {
                foreach ($backup_ids as $backup_id)
                {
                    $wpvivid_plugin->delete_backup_by_id($backup_id);
                }
            }

            $backup_success_count = WPvivid_Setting::get_option('wpvivid_backup_success_count');
            if (empty($backup_success_count))
            {
                $backup_success_count = 0;
            }
            $backup_success_count++;
            WPvivid_Setting::update_option('wpvivid_backup_success_count', $backup_success_count);

            $wpvivid_plugin->wpvivid_analysis_backup($task->task);
            $task_msg = WPvivid_taskmanager::get_task($task_id);
            $wpvivid_plugin->update_last_backup_task($task_msg);

            $task_msg = WPvivid_taskmanager::get_task($task_id);
            update_option('wpvivid_last_msg',$task_msg,'no');

            $this->clear_monitor_schedule($task_id);

            WPvivid_taskmanager::mark_task($task_id);

            if(!class_exists('WPvivid_mail_report'))
                include_once WPVIVID_PLUGIN_DIR . '/includes/class-wpvivid-mail-report.php';
            WPvivid_mail_report::send_report_mail_ex($task_id);

            $task->wpvivid_check_clear_litespeed_rule();
        }
    }

    public function handle_backup_failed($task_id)
    {
        global $wpvivid_plugin;

        $task = WPvivid_taskmanager::get_task($task_id);

        if($task['type']=='Migrate')
        {
            $backup_error_array = WPvivid_Setting::get_option('wpvivid_transfer_error_array');
            if (empty($backup_error_array)) {
                $backup_error_array = array();
            }
            if (!array_key_exists($task['id'], $backup_error_array['bu_error']))
            {
                $backup_error_array['bu_error']['task_id'] = $task['id'];
                $backup_error_array['bu_error']['error_msg'] = $task['status']['error'];
                WPvivid_Setting::update_option('wpvivid_transfer_error_array', $backup_error_array);
            }

            $new_task= new WPvivid_Backup_Task_2($task_id);
            $new_task->update_end_time();
            $new_task->clean_backup();
            $wpvivid_plugin->wpvivid_log->WriteLog('Upload failed. Delete task '.$task['id'], 'notice');
            $this->clear_monitor_schedule($task_id);
            $new_task->wpvivid_check_clear_litespeed_rule();
        }
        else
        {
            $backup_error_array = WPvivid_Setting::get_option('wpvivid_backup_error_array');
            if (!isset($backup_error_array) || empty($backup_error_array))
            {
                $backup_error_array = array();
                $backup_error_array['bu_error']['task_id'] = '';
                $backup_error_array['bu_error']['error_msg'] = '';
            }
            if (!array_key_exists($task_id, $backup_error_array['bu_error']))
            {
                $backup_error_array['bu_error']['task_id'] = $task_id;
                $backup_error_array['bu_error']['error_msg'] = 'Unknown error.';

                $general_setting=WPvivid_Setting::get_setting(true, "");
                $need_notice = false;
                if(!isset($general_setting['options']['wpvivid_compress_setting']['subpackage_plugin_upload'])){
                    $need_notice = true;
                }
                else{
                    if($general_setting['options']['wpvivid_compress_setting']['subpackage_plugin_upload']){
                        $need_notice = false;
                    }
                    else{
                        $need_notice = true;
                    }
                }
                if($need_notice)
                {
                    $notice_msg = 'Backup error: '.$task['status']['error'].', task id: '.$task['id'];
                    $backup_error_array['bu_error']['error_msg']='<div class="notice notice-error inline"><p>'.$notice_msg.', Please switch to <a href="#" onclick="wpvivid_click_switch_page(\'wrap\', \'wpvivid_tab_debug\', true);">Website Info</a> page to send us the debug information. </p></div>';
                }
                else{
                    $notice_msg = 'Backup error: ' . $task['status']['error'] . ', task id: ' . $task['id'];
                    $backup_error_array['bu_error']['error_msg'] = '<div class="notice notice-error inline"><p>' . $notice_msg . ', Please switch to <a href="#" onclick="wpvivid_click_switch_page(\'wrap\', \'wpvivid_tab_debug\', true);">Website Info</a> page to send us the debug information. </p></div>';
                }
            }

            WPvivid_Setting::update_option('wpvivid_backup_error_array', $backup_error_array);
            $task_msg = WPvivid_taskmanager::get_task($task_id);
            $wpvivid_plugin->update_last_backup_task($task_msg);

            $task= new WPvivid_Backup_Task_2($task_id);
            $task->update_end_time();
            $this->add_clean_backup_data_event($task_id);

            $task_msg = WPvivid_taskmanager::get_task($task_id);
            update_option('wpvivid_last_msg',$task_msg,'no');

            global $wpvivid_plugin;
            if($wpvivid_plugin->wpvivid_log)
            {
                $wpvivid_plugin->wpvivid_log->WriteLog($task_msg['status']['error'],'error');
                $wpvivid_plugin->wpvivid_log->CloseFile();
                WPvivid_error_log::create_error_log($wpvivid_plugin->wpvivid_log->log_file);
            }

            $this->clear_monitor_schedule($task_id);
            if(!class_exists('WPvivid_mail_report'))
                include_once WPVIVID_PLUGIN_DIR . '/includes/class-wpvivid-mail-report.php';
            WPvivid_mail_report::send_report_mail_ex($task_id);

            WPvivid_taskmanager::mark_task($task_id);

            $task->wpvivid_check_clear_litespeed_rule();
        }
    }

    public function deal_backup_shutdown_error()
    {
        if($this->end_shutdown_function===false)
        {
            global $wpvivid_plugin;
            $options = get_option('wpvivid_task_list',array());
            if(!isset($options[$this->current_task_id]))
            {
                die();
            }

            $error = error_get_last();
            $resume_backup=false;
            $memory_limit=false;
            $max_execution_time=false;

            if (!is_null($error))
            {
                if (empty($error) || !in_array($error['type'], array(E_ERROR,E_RECOVERABLE_ERROR,E_CORE_ERROR,E_COMPILE_ERROR), true))
                {
                    $wpvivid_plugin->wpvivid_log->WriteLog('In shutdown function last message type:'.$error['type'].' str:'.$error['message'],'notice');
                }

                if(preg_match('/Allowed memory size of.*$/', $error['message']))
                {
                    $resume_backup=true;
                    $memory_limit=true;
                }
                else if(preg_match('/Maximum execution time of.*$/', $error['message']))
                {
                    $resume_backup=true;
                    $max_execution_time=true;
                }
            }

            $task= new WPvivid_Backup_Task_2($this->current_task_id);
            $status=$task->get_status();
            if($memory_limit===true)
            {
                if(!$task->check_memory_limit())
                {
                    $wpvivid_plugin->wpvivid_log->WriteLog('Backup the file ends with an error '. $error['message'],'error');
                    $task->update_backup_task_status(false,'error',false,$status['resume_count'],$error['message']);
                    do_action('wpvivid_handle_backup_2_failed', $this->current_task_id);
                    $resume_backup=false;
                }
            }

            if($max_execution_time===true)
            {
                $task->check_execution_time();
            }

            if($status['str']!='completed')
            {
                $max_resume_count=$task->get_max_resume_count();
                $status=$task->get_status();
                $status['resume_count']++;
                if($status['resume_count']>$max_resume_count)
                {
                    $message=__('Too many resumption attempts.', 'wpvivid-backuprestore');
                    $wpvivid_plugin->wpvivid_log->WriteLog('Backup the file ends with an error '. $message,'error');
                    $task->update_backup_task_status(false,'error',false,$status['resume_count'],$message);
                    if($resume_backup)
                        $task->check_timeout_backup_failed();
                    do_action('wpvivid_handle_backup_2_failed', $this->current_task_id);
                }
                else
                {
                    $message=__('Task timed out.', 'wpvivid-backuprestore');
                    $wpvivid_plugin->wpvivid_log->WriteLog('Task timed out.','error');
                    $timestamp = wp_next_scheduled('wpvivid_backup_2_schedule_event',array($this->current_task_id));
                    if($timestamp===false)
                    {
                        $task->update_backup_task_status(false,'wait_resume',false,$status['resume_count']);
                        if($this->add_resume_event($this->current_task_id)===false)
                        {
                            $task->update_backup_task_status(false,'error',false,$status['resume_count'],$message);
                            $wpvivid_plugin->wpvivid_log->WriteLog('Backup the file ends with an error '. $message,'error');
                            if($resume_backup)
                                $task->check_timeout_backup_failed();
                            do_action('wpvivid_handle_backup_2_failed', $this->current_task_id);
                        }
                    }
                }
            }
        }

        die();
    }

    public function list_tasks()
    {
        check_ajax_referer( 'wpvivid_ajax', 'nonce' );
        $check=current_user_can('manage_options');
        $check=apply_filters('wpvivid_ajax_check_security',$check);
        if(!$check)
        {
            die();
        }
        try
        {
            $ret = $this->_list_tasks();

            echo wp_json_encode($ret);
        }
        catch (Exception $error)
        {
            $message = 'An exception has occurred. class: '.get_class($error).';msg: '.$error->getMessage().';code: '.$error->getCode().';line: '.$error->getLine().';in_file: '.$error->getFile().';';
            error_log($message);
            echo wp_json_encode(array('result'=>'failed','error'=>$message));
            die();
        }

        die();
    }

    public function _list_tasks_ex()
    {
        if($this->wpvivid_check_litespeed_server() && $this->wpvivid_check_litespeed_cache_plugin())
        {
            wp_cache_delete('wpvivid_task_list', 'options');
        }

        $tasks = get_option('wpvivid_task_list', array());
        $ret['result']='success';
        $ret['progress_html']=false;

        foreach ($tasks as $task)
        {
            if(!isset($task['id']))
            {
                continue;
            }

            $ret['task_id']=$task['id'];
            $ret['need_update']=true;
            if(isset($task['options']['export']))
            {
                $ret['export'] =$task['options']['export'];
            }
            else
            {
                $ret['export'] ='';
            }
            $backup_task=new WPvivid_Backup_Task_2($task['id']);
            $info=$backup_task->get_backup_task_info();

            if($info['status']['str']=='ready'||$info['status']['str']=='running'||$info['status']['str']=='wait_resume'||$info['status']['str']=='no_responds')
            {
                $ret['running_backup_taskid']=$task['id'];

                if($info['status']['str']=='wait_resume')
                {
                    $ret['wait_resume']=true;
                    $ret['next_resume_time']=$info['data']['next_resume_time'];
                }

                if($info['status']['str']=='no_responds')
                {
                    $ret['task_no_response']=true;
                }

                $ret['progress_html'] = '<div class="action-progress-bar" id="wpvivid_action_progress_bar">
                                                <div class="action-progress-bar-percent" id="wpvivid_action_progress_bar_percent" style="height:24px;width:'.$info['task_info']['backup_percent'].'"></div>
                                             </div>
                                             <div id="wpvivid_estimate_upload_info" style="float: left;"> 
                                                <div class="backup-basic-info"><span class="wpvivid-element-space-right">' . __('Total Size:', 'wpvivid-backuprestore') . '</span><span>'.$info['task_info']['total'].'</span></div>
                                                <div class="backup-basic-info"><span class="wpvivid-element-space-right">' . __('Uploaded:', 'wpvivid-backuprestore') . '</span><span>'.$info['task_info']['upload'].'</span></div>
                                                <div class="backup-basic-info"><span class="wpvivid-element-space-right">' . __('Speed:', 'wpvivid-backuprestore') . '</span><span>'.$info['task_info']['speed'].'</span></div>
                                             </div>
                                             <div style="float: left;">
                                                <div class="backup-basic-info"><span class="wpvivid-element-space-right">' . __('Network Connection:', 'wpvivid-backuprestore') . '</span><span>'.$info['task_info']['network_connection'].'</span></div>
                                             </div>
                                             <div style="clear:both;"></div>
                                             <div style="margin-left:10px; float: left; width:100%;"><p id="wpvivid_current_doing">'.$info['task_info']['descript'].'</p></div>
                                             <div style="clear: both;"></div>
                                             <div>
                                                <div id="wpvivid_backup_cancel" class="backup-log-btn"><input class="button-primary" id="wpvivid_backup_cancel_btn" type="submit" value="' . esc_attr('Cancel', 'wpvivid-backuprestore') . '" style="'.$info['task_info']['css_btn_cancel'].'" /></div>
                                             </div>
                                             <div style="clear: both;"></div>';
            }
        }

        return $ret;
    }

    public function _list_tasks()
    {
        if($this->wpvivid_check_litespeed_server() && $this->wpvivid_check_litespeed_cache_plugin())
        {
            wp_cache_delete('wpvivid_task_list', 'options');
        }

        $tasks = get_option('wpvivid_task_list', array());
        $ret['result']='success';
        $ret['progress_html']=false;
        $ret['upload_progress_html']=false;
        $ret['success_notice_html'] =false;
        $ret['error_notice_html'] =false;
        $ret['need_update']=false;
        $ret['last_msg_html']=false;
        $ret['running_backup_taskid']='';
        $ret['wait_resume']=false;
        $ret['next_resume_time']=false;
        $ret['need_refresh_remote']=false;
        $ret['backup_finish_info']=false;
        $ret['task_no_response']=false;

        $finished_tasks=array();
        $backup_success_count=0;
        $backup_failed_count=0;
        $success_log_file_name = '';
        $ret['test']=$tasks;
        foreach ($tasks as $task)
        {
            if(!isset($task['id']))
            {
                continue;
            }

            $ret['task_id']=$task['id'];
            $ret['need_update']=true;
            if(isset($task['options']['export']))
            {
                $ret['export'] =$task['options']['export'];
            }
            else
            {
                $ret['export'] ='';
            }
            $backup_task=new WPvivid_Backup_Task_2($task['id']);
            $info=$backup_task->get_backup_task_info();
            $ret['need_next_schedule']=$info['task_info']['need_next_schedule'];
            if($info['task_info']['need_next_schedule']===true)
            {
                $timestamp = wp_next_scheduled('wpvivid_task_monitor_event_2',array($task['id']));
                if($timestamp===false)
                {
                    $this->add_monitor_event($task['id'],20);
                }
            }
            if($info['status']['str']=='ready'||$info['status']['str']=='running'||$info['status']['str']=='wait_resume'||$info['status']['str']=='no_responds')
            {
                $ret['running_backup_taskid']=$task['id'];

                if($info['status']['str']=='wait_resume')
                {
                    $ret['wait_resume']=true;
                    $ret['next_resume_time']=$info['data']['next_resume_time'];
                }

                if($info['status']['str']=='no_responds')
                {
                    $ret['task_no_response']=true;
                }

                $ret['progress_html'] = '<div class="action-progress-bar" id="wpvivid_action_progress_bar">
                                                <div class="action-progress-bar-percent" id="wpvivid_action_progress_bar_percent" style="height:24px;width:'.$info['task_info']['backup_percent'].'"></div>
                                             </div>
                                             <div id="wpvivid_estimate_upload_info" style="float: left;"> 
                                                <div class="backup-basic-info"><span class="wpvivid-element-space-right">' . __('Total Size:', 'wpvivid-backuprestore') . '</span><span>'.$info['task_info']['total'].'</span></div>
                                                <div class="backup-basic-info"><span class="wpvivid-element-space-right">' . __('Uploaded:', 'wpvivid-backuprestore') . '</span><span>'.$info['task_info']['upload'].'</span></div>
                                                <div class="backup-basic-info"><span class="wpvivid-element-space-right">' . __('Speed:', 'wpvivid-backuprestore') . '</span><span>'.$info['task_info']['speed'].'</span></div>
                                             </div>
                                             <div style="float: left;">
                                                <div class="backup-basic-info"><span class="wpvivid-element-space-right">' . __('Network Connection:', 'wpvivid-backuprestore') . '</span><span>'.$info['task_info']['network_connection'].'</span></div>
                                             </div>
                                             <div style="clear:both;"></div>
                                             <div style="margin-left:10px; float: left; width:100%;"><p id="wpvivid_current_doing">'.$info['task_info']['descript'].'</p></div>
                                             <div style="clear: both;"></div>
                                             <div>
                                                <div id="wpvivid_backup_cancel" class="backup-log-btn"><input class="button-primary" id="wpvivid_backup_cancel_btn" type="submit" value="' . esc_attr('Cancel', 'wpvivid-backuprestore') . '" style="'.$info['task_info']['css_btn_cancel'].'" /></div>
                                             </div>
                                             <div style="clear: both;"></div>';
            }

            if($info['status']['str']=='completed')
            {
                $finished_tasks[$task['id']]=$task;
                $backup_success_count++;
                $success_log_file_name = $task['id'].'_backup_log.txt';
            }
            else if($info['status']['str']=='error')
            {
                $finished_tasks[$task['id']]=$task;
                $backup_failed_count++;
            }

            if(isset($task['options']['export'])&&$task['options']['export']=='auto_migrate')
            {
                $ret['upload_progress_html']=$ret['progress_html'];
                $ret['progress_html']=false;
            }
        }

        if(!empty($ret['running_backup_taskid']))
        {
            $timestamp = wp_next_scheduled('wpvivid_task_monitor_event_2',array($ret['running_backup_taskid']));
            if($timestamp===false)
            {
                $this->add_monitor_event($ret['running_backup_taskid'],20);
            }
        }

        if($backup_success_count>0)
        {
            $notice_msg = $backup_success_count.' backup task(s) finished. Please switch to <a href="#" onclick="wpvivid_click_switch_page(\'wrap\', \'wpvivid_tab_log\', true);">Log</a> page to check the details.';
            $ret['success_notice_html'] ='<div class="notice notice-success is-dismissible inline" style="margin-bottom: 5px;"><p>'.$notice_msg.'</p>
                                    <button type="button" class="notice-dismiss" onclick="click_dismiss_notice(this);">
                                    <span class="screen-reader-text">Dismiss this notice.</span>
                                    </button>
                                    </div>';
        }

        //<a href="#" onclick="wpvivid_click_switch_page('wrap', 'wpvivid_tab_log', true);">Log</a>
        if($backup_failed_count>0)
        {
            $admin_url = apply_filters('wpvivid_get_admin_url', '');
            $notice_msg = $backup_failed_count.' backup task(s) have been failed. Please switch to <a href="#" onclick="wpvivid_click_switch_page(\'wrap\', \'wpvivid_tab_debug\', true);">Log</a> page to send us the debug information.';
            $ret['error_notice_html'] ='<div class="notice notice-error inline" style="margin-bottom: 5px;"><p>'.$notice_msg.'</p></div>';
        }

        $delete_ids=array();

        foreach ($tasks as $task)
        {
            if(array_key_exists($task['id'],$finished_tasks))
            {
                $delete_ids[]=$task['id'];
            }
        }
        foreach ($delete_ids as $id)
        {
            unset($tasks[$id]);
        }
        WPvivid_Setting::update_option('wpvivid_task_list',$tasks);

        return $ret;
    }

    public function shutdown_backup()
    {
        check_ajax_referer( 'wpvivid_ajax', 'nonce' );
        $check=current_user_can('manage_options');
        $check=apply_filters('wpvivid_ajax_check_security',$check);
        if(!$check)
        {
            die();
        }

        $task_id = sanitize_key($_POST['task_id']);
        $backup_task=new WPvivid_Backup_Task_2($task_id);
        if($backup_task->check_cancel_backup())
        {
            $ret['result'] = 'success';
        }
        else
        {
            $ret['result'] = 'failed';
        }

        echo wp_json_encode($ret);
        die();
    }

    public function delete_task()
    {
        check_ajax_referer( 'wpvivid_ajax', 'nonce' );
        $check=current_user_can('manage_options');
        $check=apply_filters('wpvivid_ajax_check_security',$check);
        if(!$check)
        {
            die();
        }

        if (isset($_POST['task_id']) && !empty($_POST['task_id']) && is_string($_POST['task_id']))
        {
            $task_id = sanitize_key($_POST['task_id']);

            $options = get_option('wpvivid_task_list', array());
            unset($options[$task_id]);

            update_option('wpvivid_task_list',$options,'no');
            $json['result'] = 'success';
            echo wp_json_encode($json);
        }

        die();
    }

    public function update_backup_task_status($task_id,$reset_start_time=false,$status='',$reset_timeout=false,$resume_count=false,$error='')
    {
        $tasks=get_option('wpvivid_task_list', array());
        if(array_key_exists ($task_id,$tasks))
        {
            $task = $tasks[$task_id];
            $task['status']['run_time']=time();
            if($reset_start_time)
                $task['status']['start_time']=time();
            if(!empty($status))
            {
                $task['status']['str']=$status;
            }
            if($reset_timeout)
                $task['status']['timeout']=time();
            if($resume_count!==false)
            {
                $task['status']['resume_count']=$resume_count;
            }

            if(!empty($error))
            {
                $task['status']['error']=$error;
            }

            $options = get_option('wpvivid_task_list', array());
            $options[$task_id]=$task;
            update_option('wpvivid_task_list',$options,'no');

            return true;
        }
        else
        {
            return false;
        }
    }

    public function task_monitor($task_id)
    {
        if(WPvivid_taskmanager::get_task($task_id)!==false)
        {
            $task=new WPvivid_Backup_Task_2($task_id);

            $status=$task->get_status();

            if($task->is_task_canceled())
            {
                $limit=$task->get_time_limit();

                $last_active_time=time()-$status['run_time'];
                if($last_active_time>180)
                {
                    if($task->check_cancel_backup())
                    {
                        $this->end_shutdown_function=true;
                        die();
                    }
                }
            }
            global $wpvivid_plugin;
            $wpvivid_plugin->wpvivid_log->OpenLogFile(WPvivid_taskmanager::get_task_options($task_id,'log_file_name'));

            if($status['str']=='running'||$status['str']=='error'||$status['str']=='no_responds')
            {
                $limit=$task->get_time_limit();

                $time_spend=time()-$status['timeout'];
                $last_active_time=time()-$status['run_time'];
                if($time_spend>$limit&&$last_active_time>180)
                {
                    //time out
                    $max_resume_count=$task->get_max_resume_count();
                    $task->check_timeout();
                    $status['resume_count']++;
                    if($status['resume_count']>$max_resume_count)
                    {
                        $message=__('Too many resumption attempts.', 'wpvivid-backuprestore');
                        $wpvivid_plugin->wpvivid_log->WriteLog('Backup the file ends with an error '. $message,'error');
                        $task->update_backup_task_status(false,'error',false,$status['resume_count'],$message);
                        $task->check_timeout_backup_failed();
                        do_action('wpvivid_handle_backup_2_failed', $task_id);
                    }
                    else
                    {
                        $message=__('Task timed out.', 'wpvivid-backuprestore');
                        $task->update_backup_task_status(false,'wait_resume',false,$status['resume_count']);
                        if($this->add_resume_event($task_id)===false)
                        {
                            $wpvivid_plugin->wpvivid_log->WriteLog('Backup the file ends with an error '. $message,'error');
                            $task->update_backup_task_status(false,'error',false,$status['resume_count'],$message);
                            $task->check_timeout_backup_failed();
                            do_action('wpvivid_handle_backup_2_failed', $task_id);
                        }
                    }
                }
                else
                {
                    $time_spend=time()-$status['run_time'];
                    if($time_spend>180)
                    {
                        $task->update_backup_task_status(false,'no_responds',false,$status['resume_count']);
                        $this->add_monitor_event($task_id);
                    }
                    else {
                        $this->add_monitor_event($task_id);
                    }
                }
            }
            else if($status['str']=='wait_resume')
            {
                $timestamp = wp_next_scheduled(WPVIVID_RESUME_SCHEDULE_EVENT,array($task_id));
                if($timestamp===false)
                {
                    $message = 'Task timed out (WebHosting).';
                    $task->update_backup_task_status(false, 'wait_resume', false, $status['resume_count']);
                    if ($this->add_resume_event($task_id)===false)
                    {
                        $wpvivid_plugin->wpvivid_log->WriteLog('Backup the file ends with an error '. $message,'error');
                        $task->update_backup_task_status(false, 'error', false, $status['resume_count'], $message);
                        $task->check_timeout_backup_failed();
                        do_action('wpvivid_handle_backup_2_failed', $task_id);
                    }
                }
            }
        }
    }

    private function add_resume_event($task_id)
    {
        $resume_time=time()+10;

        $b=wp_schedule_single_event($resume_time,'wpvivid_backup_2_schedule_event',array($task_id));

        if($b===false)
        {
            $timestamp = wp_next_scheduled('wpvivid_backup_2_schedule_event',array($task_id));

            if($timestamp===false)
            {
                return false;
            }
            else
            {
                return true;
            }
        }
        return true;
    }

    public function add_monitor_event($task_id,$next_time=120)
    {
        $resume_time=time()+$next_time;

        $timestamp = wp_next_scheduled('wpvivid_task_monitor_event_2',array($task_id));

        if($timestamp===false)
        {
            $b = wp_schedule_single_event($resume_time, 'wpvivid_task_monitor_event_2', array($task_id));
            if ($b === false)
            {
                return false;
            }
            else
            {
                return true;
            }
        }
        return true;
    }

    public function clear_monitor_schedule($id)
    {
        $timestamp =wp_next_scheduled('wpvivid_task_monitor_event_2',array($id));
        if($timestamp!==false)
        {
            wp_unschedule_event($timestamp,'wpvivid_task_monitor_event_2',array($id));
        }
    }

    public function add_clean_backup_data_event($task_id)
    {
        $task=WPvivid_taskmanager::get_task($task_id);
        $tasks=WPvivid_Setting::get_option('wpvivid_clean_task_2');
        $tasks[$task_id]=$task;
        WPvivid_Setting::update_option('wpvivid_clean_task_2',$tasks);

        $resume_time=time()+60;

        $b=wp_schedule_single_event($resume_time,'wpvivid_clean_backup_2_data_event',array($task_id));

        if($b===false)
        {
            $timestamp = wp_next_scheduled('wpvivid_clean_backup_2_data_event',array($task_id));

            if($timestamp!==false)
            {
                $resume_time=max($resume_time,$timestamp+10*60+10);

                $b=wp_schedule_single_event($resume_time,'wpvivid_clean_backup_2_data_event',array($task_id));

                if($b===false)
                {
                    return false;
                }
            }
            else
            {
                return false;
            }
        }
        return true;
    }

    public function clean_backup_data_event($task_id)
    {
        $tasks=get_option('wpvivid_clean_task_2',array());
        if(isset($tasks[$task_id]))
        {
            $task_data=$tasks[$task_id];
            unset($tasks[$task_id]);
        }
        update_option('wpvivid_clean_task_2',$tasks,'no');

        if(!empty($task_data))
        {
            $task= new WPvivid_Backup_Task_2($task_id,$task_data);
            $task->clean_backup();

            $files=array();

            if($task->need_upload())
            {
                $backup_files=$task->get_backup_files();
                foreach ($backup_files as $file)
                {
                    $files[]=basename($file);
                }
                if(!empty($files))
                {
                    if(!class_exists('WPvivid_Upload'))
                        include_once WPVIVID_PLUGIN_DIR . '/includes/class-wpvivid-upload.php';
                    $upload=new WPvivid_Upload();
                    $upload->clean_remote_backup($task->get_remote_options(),$files);
                }
            }
            //clean upload
        }
    }

    public function wpvivid_check_litespeed_server()
    {
        $litespeed=false;
        if ( isset( $_SERVER['HTTP_X_LSCACHE'] ) && $_SERVER['HTTP_X_LSCACHE'] )
        {
            $litespeed=true;
        }
        elseif ( isset( $_SERVER['LSWS_EDITION'] ) && strpos( $_SERVER['LSWS_EDITION'], 'Openlitespeed' ) === 0 ) {
            $litespeed=true;
        }
        elseif ( isset( $_SERVER['SERVER_SOFTWARE'] ) && $_SERVER['SERVER_SOFTWARE'] == 'LiteSpeed' ) {
            $litespeed=true;
        }

        return $litespeed;
    }

    public function wpvivid_check_litespeed_cache_plugin()
    {
        $litespeed_cache_plugin=false;
        if(!function_exists('get_plugins'))
            require_once(ABSPATH . 'wp-admin/includes/plugin.php');
        $litespeed_cache_slug='litespeed-cache/litespeed-cache.php';
        if (is_multisite())
        {
            $active_plugins = array();
            //network active
            $mu_active_plugins = get_site_option( 'active_sitewide_plugins', array() );
            if(!empty($mu_active_plugins)){
                foreach ($mu_active_plugins as $plugin_name => $data){
                    $active_plugins[] = $plugin_name;
                }
            }
            $plugins=get_mu_plugins();
            if(count($plugins) == 0 || !isset($plugins[$litespeed_cache_slug])){
                $plugins=get_plugins();
            }
        }
        else
        {
            $active_plugins = get_option('active_plugins');
            $plugins=get_plugins();
        }

        if(!empty($plugins))
        {
            if(isset($plugins[$litespeed_cache_slug]))
            {
                if(in_array($litespeed_cache_slug, $active_plugins))
                {
                    $litespeed_cache_plugin=true;
                }
                else
                {
                    $litespeed_cache_plugin=false;
                }
            }
            else
            {
                $litespeed_cache_plugin=false;
            }
        }
        else
        {
            $litespeed_cache_plugin=false;
        }

        return $litespeed_cache_plugin;
    }

    public function send_backup_to_site()
    {
        try
        {
            check_ajax_referer( 'wpvivid_ajax', 'nonce' );
            $check=current_user_can('manage_options');
            $check=apply_filters('wpvivid_ajax_check_security',$check);
            if(!$check)
            {
                die();
            }

            $options = WPvivid_Setting::get_option('wpvivid_saved_api_token');

            if (empty($options))
            {
                $ret['result'] = 'failed';
                $ret['error'] = __('A key is required.', 'wpvivid-backuprestore');
                echo wp_json_encode($ret);
                die();
            }

            $url = '';
            foreach ($options as $key => $value)
            {
                $url = $value['url'];
            }

            if ($url === '')
            {
                $ret['result'] = 'failed';
                $ret['error'] = __('The key is invalid.', 'wpvivid-backuprestore');
                echo wp_json_encode($ret);
                die();
            }

            if ($options[$url]['expires'] != 0 && $options[$url]['expires'] < time())
            {
                $ret['result'] = 'failed';
                $ret['error'] =  __('The key has expired.', 'wpvivid-backuprestore');
                echo wp_json_encode($ret);
                die();
            }

            $json['test_connect']=1;
            $json=wp_json_encode($json);
            $crypt=new WPvivid_crypt(base64_decode($options[$url]['token']));
            $data=$crypt->encrypt_message($json);
            $data=base64_encode($data);
            $args['body']=array('wpvivid_content'=>$data,'wpvivid_action'=>'send_to_site_connect');
            $response=wp_remote_post($url,$args);

            if ( is_wp_error( $response ) )
            {
                $ret['result']=WPVIVID_FAILED;
                $ret['error']= $response->get_error_message();
                echo wp_json_encode($ret);
                die();
            }
            else
            {
                if($response['response']['code']==200)
                {
                    $res=json_decode($response['body'],1);
                    if($res!=null) {
                        if($res['result']==WPVIVID_SUCCESS)
                        {

                        }
                        else
                        {
                            $ret['result']=WPVIVID_FAILED;
                            $ret['error']= $res['error'];
                            echo wp_json_encode($ret);
                            die();
                        }
                    }
                    else {
                        $ret['result']=WPVIVID_FAILED;
                        $ret['error']= 'failed to parse returned data, unable to establish connection with the target site.';
                        $ret['response']=$response;
                        echo wp_json_encode($ret);
                        die();
                    }
                }
                else {
                    $ret['result']=WPVIVID_FAILED;
                    $ret['error']= 'upload error '.$response['response']['code'].' '.$response['body'];
                    echo wp_json_encode($ret);
                    die();
                }
            }

            if (WPvivid_taskmanager::is_tasks_backup_running())
            {
                $ret['result'] = 'failed';
                $ret['error'] = __('A task is already running. Please wait until the running task is complete, and try again.', 'wpvivid-backuprestore');
                echo wp_json_encode($ret);
                die();
            }

            $remote_option['url'] = $options[$url]['url'];
            $remote_option['token'] = $options[$url]['token'];
            $remote_option['type'] = WPVIVID_REMOTE_SEND_TO_SITE;
            $remote_options['temp'] = $remote_option;

            $backup_options = stripslashes(sanitize_text_field($_POST['backup_options']));
            $backup_options = json_decode($backup_options, true);
            $backup['backup_files'] = $backup_options['transfer_type'];
            $backup['local'] = 0;
            $backup['remote'] = 1;
            $backup['ismerge'] = 1;
            $backup['lock'] = 0;
            $backup['remote_options'] = $remote_options;
            $backup['type']='Migrate';
            $backup['export']='auto_migrate';

            /*
            $backup_task = new WPvivid_Backup_Task();
            $ret = $backup_task->new_backup_task($backup, 'Manual', 'transfer');
            $task_id = $ret['task_id'];
            global $wpvivid_plugin;
            $wpvivid_plugin->check_backup($task_id, $backup);
            echo wp_json_encode($ret);
            die();
            */

            $settings=$this->get_backup_settings($backup);
            $task=new WPvivid_Backup_Task_2();
            $ret=$task->new_backup_task($backup,$settings);

            echo wp_json_encode($ret);
            die();
        }
        catch (Exception $e){
            $ret['result'] = 'failed';
            $ret['error'] = $e->getMessage();
            echo wp_json_encode($ret);
            die();
        }
    }

    public function migrate_now()
    {
        check_ajax_referer( 'wpvivid_ajax', 'nonce' );
        $check=current_user_can('manage_options');
        $check=apply_filters('wpvivid_ajax_check_security',$check);
        if(!$check)
        {
            die();
        }

        register_shutdown_function(array($this,'deal_backup_shutdown_error'));
        $this->end_shutdown_function=false;

        $task_id = sanitize_key($_POST['task_id']);
        $this->current_task_id=$task_id;
        global $wpvivid_plugin;

        if ($this->is_tasks_backup_running($task_id))
        {
            $ret['result'] = 'failed';
            $ret['error'] = __('We detected that there is already a running backup task. Please wait until it completes then try again.', 'wpvivid-backuprestore');
            echo wp_json_encode($ret);
            die();
        }

        try
        {
            $this->update_backup_task_status($task_id,true,'running');
            $wpvivid_plugin->flush($task_id);
            $this->add_monitor_event($task_id);
            $this->task=new WPvivid_Backup_Task_2($task_id);
            $this->task->set_memory_limit();
            $this->task->set_time_limit();

            $wpvivid_plugin->wpvivid_log->OpenLogFile($this->task->task['options']['log_file_name']);
            $wpvivid_plugin->wpvivid_log->WriteLog('Start backing up.','notice');
            $wpvivid_plugin->wpvivid_log->WriteLogHander();

            if(!$this->task->is_backup_finished())
            {
                $ret=$this->backup();
                $this->task->clear_cache();
                if($ret['result']!='success')
                {
                    $wpvivid_plugin->wpvivid_log->WriteLog('Backup the file ends with an error '. $ret['error'],'error');
                    $this->task->update_backup_task_status(false,'error',false,false,$ret['error']);
                    do_action('wpvivid_handle_backup_2_failed', $task_id);
                    $this->end_shutdown_function=true;
                    $this->clear_monitor_schedule($task_id);
                    die();
                }
            }

            if($this->task->need_upload())
            {
                $ret=$this->upload($task_id);
                if($ret['result'] == WPVIVID_SUCCESS)
                {
                    do_action('wpvivid_handle_backup_2_succeed',$task_id);
                    $this->update_backup_task_status($task_id,false,'completed');
                }
                else
                {
                    $wpvivid_plugin->wpvivid_log->WriteLog('Uploading the file ends with an error '. $ret['error'], 'error');
                    do_action('wpvivid_handle_backup_2_failed',$task_id);
                }
            }
            else
            {
                $wpvivid_plugin->wpvivid_log->WriteLog('Backup completed.','notice');
                do_action('wpvivid_handle_backup_2_succeed', $task_id);
                $this->update_backup_task_status($task_id,false,'completed');
            }
            $this->clear_monitor_schedule($task_id);
        }
        catch (Exception $error)
        {
            //catch error and stop task recording history
            $message = 'An exception has occurred. class:'.get_class($error).';msg:'.$error->getMessage().';code:'.$error->getCode().';line:'.$error->getLine().';in_file:'.$error->getFile().';';
            error_log($message);
            WPvivid_taskmanager::update_backup_task_status($task_id,false,'error',false,false,$message);
            $wpvivid_plugin->wpvivid_log->WriteLog($message,'error');
            do_action('wpvivid_handle_backup_2_failed',$task_id);
            $this->end_shutdown_function=true;
            die();
        }


        $this->end_shutdown_function=true;

        die();
    }

    public function default_exclude_folders($folders)
    {
        $upload_dir = wp_upload_dir();
        $exclude_default = array();
        $exclude_default[0]['type'] = 'folder';
        $exclude_default[0]['path'] = $upload_dir['basedir'].'/'.'backwpup';    // BackWPup backup directory
        $exclude_default[1]['type'] = 'folder';
        $exclude_default[1]['path'] = $upload_dir['basedir'].'/'.'ShortpixelBackups';   //ShortpixelBackups
        $exclude_default[2]['type'] = 'folder';
        $exclude_default[2]['path'] = $upload_dir['basedir'].'/'.'backup';
        $exclude_default[3]['type'] = 'folder';
        $exclude_default[3]['path'] = $upload_dir['basedir'].'/'.'backwpup';    // BackWPup backup directory
        $exclude_default[4]['type'] = 'folder';
        $exclude_default[4]['path'] = $upload_dir['basedir'].'/'.'backup-guard';    // Wordpress Backup and Migrate Plugin backup directory
        $exclude_default[5]['type'] = 'folder';
        $exclude_default[5]['path'] = WP_CONTENT_DIR.'/'.'updraft';     // Updraft Plus backup directory
        $exclude_default[6]['type'] = 'folder';
        $exclude_default[6]['path'] = WP_CONTENT_DIR.'/'.'ai1wm-backups';   // All-in-one WP migration backup directory
        $exclude_default[7]['type'] = 'folder';
        $exclude_default[7]['path'] = WP_CONTENT_DIR.'/'.'backups';     // Xcloner backup directory
        $exclude_default[8]['type'] = 'folder';
        $exclude_default[8]['path'] = WP_CONTENT_DIR.'/'.'upgrade';
        $exclude_default[10]['type'] = 'folder';
        $exclude_default[10]['path'] = WP_CONTENT_DIR.'/'.'cache';
        $exclude_default[11]['type'] = 'folder';
        $exclude_default[11]['path'] = WP_CONTENT_DIR.'/'.'wphb-cache';
        $exclude_default[12]['type'] = 'folder';
        $exclude_default[12]['path'] = WP_CONTENT_DIR.'/'.'backup';
        $exclude_default[13]['type'] = 'folder';
        $exclude_default[13]['path'] = WP_CONTENT_DIR.'/'.'Dropbox_Backup';
        //$exclude_default[14]['type'] = 'folder';
        //$exclude_default[14]['path'] = WP_CONTENT_DIR.'/'.'mu-plugins';
        $exclude_default[15]['type'] = 'folder';
        $exclude_default[15]['path'] = WP_CONTENT_DIR.'/'.'backups-dup-pro';    // duplicator backup directory
        $exclude_default[16]['type'] = 'folder';
        $exclude_default[16]['path'] = WP_CONTENT_DIR.'/'.'backup-migration';
        $exclude_default[17]['type'] = 'folder';
        $exclude_default[17]['path'] = WP_CONTENT_DIR.'/'.'backups-dup-lite';
        $exclude_default[18]['type'] = 'folder';
        $exclude_default[18]['path'] = WP_PLUGIN_DIR.'/'.'wp-cerber';
        $exclude_default[19]['type'] = 'file';
        $exclude_default[19]['path'] = WP_CONTENT_DIR.'/'.'mysql.sql';  //mysql
        $exclude_default[20]['type'] = 'folder';
        $exclude_default[20]['path'] = WP_CONTENT_DIR.'/'.'wpvivid_uploads';
        $exclude_default[21]['type'] = 'folder';
        $exclude_default[21]['path'] = WP_CONTENT_DIR.'/'.'WPvivid_Uploads';

        if(!empty($exclude_default))
        {
            foreach ($exclude_default as $index => $value)
            {
                $folders[$index]=$value;
            }
        }
        return $folders;
    }
}includes/new_backup/class-wpvivid-restore-file-2.php000064400000064730151327705670016560 0ustar00<?php

if (!defined('WPVIVID_PLUGIN_DIR'))
{
    die;
}

class WPvivid_Restore_File_2
{
    public $log;

    public function __construct($log=false)
    {
        $this->log=$log;
    }

    public function restore($sub_task,$backup_id)
    {
        if($sub_task['type']=='wp-core')
        {
            return $this->restore_core($sub_task,$backup_id);
        }

        $files=$sub_task['unzip_file']['files'];
        $GLOBALS['wpvivid_restore_addon_type'] =$sub_task['type'];
        //restore_reset

        foreach ($files as $index=>$file)
        {
            if($file['finished']==1)
            {
                continue;
            }

            $sub_task['unzip_file']['last_action']='Unzipping';
            $sub_task['unzip_file']['last_unzip_file']=$file['file_name'];
            $sub_task['last_msg']='<span><strong>Extracting file:</strong></span><span>'.$file['file_name'].'</span>';
            $this->update_sub_task($sub_task);

            $backup = WPvivid_Backuplist::get_backup_by_id($backup_id);
            $backup_item = new WPvivid_Backup_Item($backup);

            $extract_child_finished=isset($file['extract_child_finished'])?$file['extract_child_finished']:0;

            if(isset($file['has_child'])&&$extract_child_finished==0)
            {
                $sub_task['unzip_file']['last_action']='Unzipping';
                $sub_task['unzip_file']['last_unzip_file']=$file['parent_file'];
                $sub_task['unzip_file']['last_unzip_file_index']=0;
                $sub_task['last_msg']='<span><strong>Extracting file:</strong></span><span>'.$file['parent_file'].'</span>';
                $this->update_sub_task($sub_task);

                $root_path=$backup_item->get_local_path();

                if(!file_exists($root_path))
                {
                    @mkdir($root_path);
                }
                $this->log->WriteLog('Extracting file:'.$file['parent_file'],'notice');
                $extract_files[]=$file['file_name'];
                $ret=$this->extract_ex($root_path.$file['parent_file'],$extract_files,untrailingslashit($root_path),$sub_task['options']);
                if($ret['result']!='success')
                {
                    return $ret;
                }
                $this->log->WriteLog('Extracting file:'.$file['parent_file'].' succeeded.','notice');
                $file_name=$root_path.$file['file_name'];
                $sub_task['unzip_file']['files'][$index]['extract_child_finished']=1;
                $sub_task['last_msg']='<span><strong>Extracting file:</strong></span><span>'.$file['parent_file'].' completed.</span>';
                $this->update_sub_task($sub_task);
            }
            else
            {
                $root_path=$backup_item->get_local_path();
                $file_name=$root_path.$file['file_name'];
            }


            $root_path = '';
            if (isset($file['options']['root']))
            {
                $root_path = $this->transfer_path(get_home_path() . $file['options']['root']);
            }
            else if (isset($file['options']['root_flag']))
            {
                if ($file['options']['root_flag'] == WPVIVID_BACKUP_ROOT_WP_CONTENT)
                {
                    $root_path = $this->transfer_path(WP_CONTENT_DIR);
                }
                else if ($file['options']['root_flag'] == WPVIVID_BACKUP_ROOT_CUSTOM)
                {
                    $root_path = $this->transfer_path(WP_CONTENT_DIR . DIRECTORY_SEPARATOR . WPvivid_Setting::get_backupdir());
                }
                else if ($file['options']['root_flag'] == WPVIVID_BACKUP_ROOT_WP_ROOT)
                {
                    $root_path = $this->transfer_path(ABSPATH);
                }
                else if($file['options']['root_flag'] == WPVIVID_BACKUP_ROOT_WP_UPLOADS)
                {
                    $upload_dir = wp_upload_dir();
                    $upload_path = $upload_dir['basedir'];

                    $root_path = $this->transfer_path($upload_path);
                }
            }

            if($sub_task['restore_reset'])
            {
                if($sub_task['restore_reset_finished']===false)
                {
                    $sub_task['unzip_file']['last_action']='Unzipping';
                    $sub_task['last_msg']='<span>Cleaning folder:</span><span>'.$sub_task['type'].'</span>';
                    $this->update_sub_task($sub_task);
                    $this->log->WriteLog('Cleaning folder:'.$sub_task['type'],'notice');
                    $this->reset_restore($sub_task['type']);
                    $sub_task['restore_reset_finished']=true;
                    $sub_task['unzip_file']['last_action']='Unzipping';
                    $sub_task['last_msg']='<span>Cleaning folder:</span><span>'.$sub_task['type'].' completed.</span>';
                    $this->update_sub_task($sub_task);
                }
            }

            $root_path = rtrim($root_path, '/');
            $root_path = rtrim($root_path, DIRECTORY_SEPARATOR);

            $restore_task=get_option('wpvivid_restore_task',array());
            $restore_detail_options=$restore_task['restore_detail_options'];
            $unzip_files_pre_request=$restore_detail_options['unzip_files_pre_request'];
            $use_index=$restore_detail_options['use_index'];
            if($use_index==false)
            {
                $sub_task['last_msg']='<span><strong>Extracting file:</strong></span><span>'.$file['file_name'].'</span>';
                $sub_task['unzip_file']['last_action']='Unzipping';
                $sub_task['unzip_file']['last_unzip_file']=$file['file_name'];
                $sub_task['unzip_file']['last_unzip_file_index']=0;

                $this->update_sub_task($sub_task);
                $this->log->WriteLog('Extracting file:'.$file_name,'notice');
                $ret=$this->extract($file_name,untrailingslashit($root_path),$sub_task['options']);
                if($ret['result']!='success')
                {
                    return $ret;
                }
                $this->log->WriteLog('Extracting file:'.$file_name.' succeeded','notice');
                $sub_task['unzip_file']['files'][$index]['finished']=1;
                $sub_task['last_msg']='<span><strong>Extracting file:</strong></span><span>'.$file['file_name'].' completed.</span>';
            }
            else
            {
                $sum=$this->get_zip_file_count($file_name);

                $start=$file['index'];
                $sub_task['last_msg']='<span><strong>Extracting file:</strong></span><span>'.$file['file_name'].' '.$start.'/'.$sum.'</span>';
                $sub_task['unzip_file']['sum']=$sum;
                $sub_task['unzip_file']['start']=$start;
                $sub_task['unzip_file']['last_action']='Unzipping';
                $sub_task['unzip_file']['last_unzip_file']=$file['file_name'];
                $sub_task['unzip_file']['last_unzip_file_index']=$start;
                $this->update_sub_task($sub_task);
                $this->log->WriteLog('Extracting file:'.basename($file_name).' index:'.$start,'notice');
                $ret=$this->extract_by_index($file_name,untrailingslashit($root_path),$start,$start+$unzip_files_pre_request,$sub_task['options']);
                if($ret['result']!='success')
                {
                    return $ret;
                }
                $this->log->WriteLog('Extracting file:'.basename($file_name).' index:'.$start.' finished.','notice');
                $sub_task['unzip_file']['files'][$index]['index']=$start+$unzip_files_pre_request;
                $sub_task['unzip_file']['last_action']='Unzipping';
                if($start+$unzip_files_pre_request>=$sum)
                {
                    $sub_task['unzip_file']['files'][$index]['finished']=1;
                    $sub_task['unzip_file']['sum']=0;
                    $sub_task['unzip_file']['start']=0;
                    $sub_task['last_msg']='<span><strong>Extracting file:</strong></span><span>'.$file['file_name'].' completed.</span>';
                }
                else
                {
                    $sub_task['last_msg']='<span><strong>Extracting file:</strong></span><span>'.$file['file_name'].' '.$sub_task['unzip_file']['files'][$index]['index'].'/'.$sum.'</span>';
                }
            }

            break;
        }

        if($this->check_restore_finished($sub_task))
        {
            $sub_task['finished']=1;
            $sub_task['unzip_file']['unzip_finished']=1;
            $sub_task['unzip_file']['sum']=0;
            $sub_task['unzip_file']['start']=0;
        }

        $ret['result']='success';
        $ret['sub_task']=$sub_task;
        return $ret;
    }

    public function restore_core($sub_task,$backup_id)
    {
        $files=$sub_task['unzip_file']['files'];
        $GLOBALS['wpvivid_restore_addon_type'] =$sub_task['type'];
        //restore_reset
        foreach ($files as $index=>$file)
        {
            if($file['finished']==1)
            {
                continue;
            }

            $sub_task['unzip_file']['last_action']='Unzipping';
            $sub_task['unzip_file']['last_unzip_file']=$file['file_name'];
            $sub_task['last_msg']='<span><strong>Extracting file:</strong></span><span>'.$file['file_name'].'</span>';
            $this->update_sub_task($sub_task);

            $backup = WPvivid_Backuplist::get_backup_by_id($backup_id);
            $backup_item = new WPvivid_Backup_Item($backup);
            $extract_child_finished=isset($file['extract_child_finished'])?$file['extract_child_finished']:0;
            if(isset($file['has_child'])&&$extract_child_finished==0)
            {
                $sub_task['unzip_file']['last_action']='Unzipping';
                $sub_task['last_msg']='<span><strong>Extracting file:</strong></span><span>'.$file['parent_file'].'</span>';
                $this->update_sub_task($sub_task);

                $root_path=$backup_item->get_local_path();

                if(!file_exists($root_path))
                {
                    @mkdir($root_path);
                }
                $this->log->WriteLog('Extracting file:'.$file['parent_file'],'notice');

                $extract_files[]=$file['file_name'];
                $ret=$this->extract_ex($root_path.$file['parent_file'],$extract_files,untrailingslashit($root_path),$sub_task['options']);
                if($ret['result']!='success')
                {
                    return $ret;
                }
                $this->log->WriteLog('Extracting file:'.$file['parent_file'].' succeeded.','notice');
                $file_name=$root_path.$file['file_name'];
                $sub_task['unzip_file']['files'][$index]['extract_child_finished']=1;
                $sub_task['last_msg']='<span><strong>Extracting file:</strong></span><span>'.$file['parent_file'].' completed.</span>';
                $this->update_sub_task($sub_task);
            }
            else
            {
                $root_path=$backup_item->get_local_path();
                $file_name=$root_path.$file['file_name'];
            }

            if($sub_task['restore_reset'])
            {
                if($sub_task['restore_reset_finished']===false)
                {
                    $sub_task['unzip_file']['last_action']='Unzipping';
                    $sub_task['last_msg']='<span>Cleaning folder:</span><span>'.$sub_task['type'].'</span>';
                    $this->update_sub_task($sub_task);
                    $this->log->WriteLog('Cleaning folder:'.$sub_task['type'],'notice');
                    $this->reset_restore($sub_task['type']);

                    $sub_task['restore_reset_finished']=true;
                    $sub_task['unzip_file']['last_action']='Unzipping';
                    $sub_task['last_msg']='<span>Cleaning folder:</span><span>'.$sub_task['type'].' completed.</span>';
                    $this->update_sub_task($sub_task);
                }
            }

            $root_path = $this->transfer_path(ABSPATH);

            $root_path = rtrim($root_path, '/');
            $root_path = rtrim($root_path, DIRECTORY_SEPARATOR);

            $sub_task['last_msg']='<span><strong>Extracting Files:</strong></span><span>'.$file['file_name'].'</span>';
            $sub_task['unzip_file']['last_action']='Unzipping';
            $this->update_sub_task($sub_task);
            $this->log->WriteLog('Extracting file:'.basename($file_name),'notice');

            $ret=$this->extract($file_name,untrailingslashit($root_path),$sub_task['options']);
            if($ret['result']!='success')
            {
                return $ret;
            }
            $this->log->WriteLog('Extracting file:'.basename($file_name).' succeeded.','notice');
            $sub_task['unzip_file']['files'][$index]['finished']=1;
            $sub_task['last_msg']='<span><strong>Extracting Files:</strong></span><span>'.$file['file_name'].' finished</span>';
            $this->update_sub_task($sub_task);
        }

        if($this->check_restore_finished($sub_task))
        {
            $sub_task['finished']=1;
            $sub_task['unzip_file']['unzip_finished']=1;
        }

        $ret['result']='success';
        $ret['sub_task']=$sub_task;
        return $ret;
    }

    public function extract($file_name,$root_path,$option)
    {
        if (!class_exists('WPvivid_PclZip'))
            include_once WPVIVID_PLUGIN_DIR . '/includes/zip/class-wpvivid-pclzip.php';

        if(!empty($option))
        {
            $GLOBALS['wpvivid_restore_option'] = $option;
        }

        if(!defined('PCLZIP_TEMPORARY_DIR'))
            define(PCLZIP_TEMPORARY_DIR,dirname($root_path));

        $archive = new WPvivid_PclZip($file_name);
        $zip_ret = $archive->extract(WPVIVID_PCLZIP_OPT_PATH, $root_path,WPVIVID_PCLZIP_OPT_REPLACE_NEWER,WPVIVID_PCLZIP_CB_PRE_EXTRACT,'wpvivid_function_pre_extract_callback_2',WPVIVID_PCLZIP_OPT_TEMP_FILE_THRESHOLD,16);
        if(!$zip_ret)
        {
            $ret['result']='failed';
            $ret['error'] = $archive->errorInfo(true);
            $this->log->WriteLog('Extracting failed. Error:'.$archive->errorInfo(true),'notice');
        }
        else
        {
            $ret['result']='success';
        }
        return $ret;
    }

    public function extract_ex($file_name,$extract_files,$root_path,$option)
    {
        if (!class_exists('WPvivid_PclZip'))
            include_once WPVIVID_PLUGIN_DIR . '/includes/zip/class-wpvivid-pclzip.php';

        if(!empty($option))
        {
            $GLOBALS['wpvivid_restore_option'] = $option;
        }

        if(!defined('PCLZIP_TEMPORARY_DIR'))
            define(PCLZIP_TEMPORARY_DIR,dirname($root_path));

        $archive = new WPvivid_PclZip($file_name);
        $zip_ret = $archive->extract(WPVIVID_PCLZIP_OPT_BY_NAME,$extract_files,WPVIVID_PCLZIP_OPT_PATH, $root_path,WPVIVID_PCLZIP_OPT_REPLACE_NEWER,WPVIVID_PCLZIP_CB_PRE_EXTRACT,'wpvivid_function_pre_extract_callback_2',WPVIVID_PCLZIP_OPT_TEMP_FILE_THRESHOLD,16);
        if(!$zip_ret)
        {
            $ret['result']='failed';
            $ret['error'] = $archive->errorInfo(true);
            $this->log->WriteLog('Extracting failed. Error:'.$archive->errorInfo(true),'notice');
        }
        else
        {
            $ret['result']='success';
        }
        return $ret;
    }

    public function extract_by_index($file_name,$root_path,$start,$end,$option)
    {
        $index=$start.'-'.$end;

        if (!class_exists('WPvivid_PclZip'))
            include_once WPVIVID_PLUGIN_DIR . '/includes/zip/class-wpvivid-pclzip.php';

        if(!empty($option))
        {
            $GLOBALS['wpvivid_restore_option'] = $option;
        }

        if(!defined('PCLZIP_TEMPORARY_DIR'))
            define(PCLZIP_TEMPORARY_DIR,dirname($root_path));

        $archive = new WPvivid_PclZip($file_name);
        $zip_ret = $archive->extractByIndex($index,WPVIVID_PCLZIP_OPT_PATH, $root_path,WPVIVID_PCLZIP_OPT_REPLACE_NEWER,WPVIVID_PCLZIP_CB_PRE_EXTRACT,'wpvivid_function_pre_extract_callback_2',WPVIVID_PCLZIP_OPT_TEMP_FILE_THRESHOLD,16);
        if(!$zip_ret)
        {
            $ret['result']='failed';
            $ret['error'] = $archive->errorInfo(true);
            $this->log->WriteLog('Extracting failed. Error:'.$archive->errorInfo(true),'notice');
        }
        else
        {
            $ret['result']='success';
        }
        return $ret;
    }

    public function get_zip_file_count($file_name)
    {
        if (!class_exists('WPvivid_PclZip'))
            include_once WPVIVID_PLUGIN_DIR . '/includes/zip/class-wpvivid-pclzip.php';

        $archive = new WPvivid_PclZip($file_name);
        $properties=$archive->properties();
        return $properties['nb'];
    }

    public function check_restore_finished($sub_task)
    {
        $finished=true;

        $files=$sub_task['unzip_file']['files'];

        foreach ($files as $index=>$file)
        {
            if($file['finished']==1)
            {
                continue;
            }
            else
            {
                $finished=false;
            }
        }

        return $finished;
    }

    private function transfer_path($path)
    {
        $path = str_replace('\\','/',$path);
        $values = explode('/',$path);
        return implode(DIRECTORY_SEPARATOR,$values);
    }

    public function update_sub_task($sub_task=false)
    {
        $restore_task=get_option('wpvivid_restore_task',array());

        if($restore_task['do_sub_task']!==false)
        {
            $key=$restore_task['do_sub_task'];
            $restore_task['update_time']=time();
            if($sub_task!==false)
                $restore_task['sub_tasks'][$key]=$sub_task;
            update_option('wpvivid_restore_task',$restore_task,'no');
        }
    }

    public function reset_restore($type)
    {
        if($type=='themes')
        {
            return $this->delete_themes();
        }
        else if($type=='plugin')
        {
            return $this->delete_plugins();
        }
        else if($type=='upload')
        {
            return $this->delete_uploads();
        }
        else if($type=='wp-content')
        {
            return $this->delete_wp_content();
        }
        //else if($type=='mu_plugins')
        //{
        //    return $this->delete_mu_plugins();
        //}
        else  if($type=='wp-core')
        {
            return $this->delete_core();
        }
        $ret['result']='success';
        return $ret;
    }

    public function delete_themes()
    {
        if (!function_exists('delete_theme'))
        {
            require_once ABSPATH . 'wp-admin/includes/theme.php';
        }

        if (!function_exists('request_filesystem_credentials'))
        {
            require_once ABSPATH . 'wp-admin/includes/file.php';
        }

        $all_themes = wp_get_themes(array('errors' => null));

        foreach ($all_themes as $theme_slug => $theme_details)
        {
            delete_theme($theme_slug);
        }

        update_option('template', '');
        update_option('stylesheet', '');
        update_option('current_theme', '');

        $ret['result']=WPVIVID_SUCCESS;
        return $ret;
    }

    public function delete_plugins()
    {
        if (!function_exists('get_plugins'))
        {
            require_once ABSPATH . 'wp-admin/includes/plugin.php';
        }
        if (!function_exists('request_filesystem_credentials'))
        {
            require_once ABSPATH . 'wp-admin/includes/file.php';
        }

        $wpvivid_backup_pro='wpvivid-backup-pro/wpvivid-backup-pro.php';
        $wpvivid_backup='wpvivid-backuprestore/wpvivid-backuprestore.php';

        $all_plugins = get_plugins();
        unset($all_plugins[$wpvivid_backup_pro]);
        unset($all_plugins[$wpvivid_backup]);

        if (!empty($all_plugins))
        {
            $this->_delete_plugins(array_keys($all_plugins));
        }

        $ret['result']=WPVIVID_SUCCESS;
        return $ret;
    }

    public function _delete_plugins($plugins)
    {
        if ( empty( $plugins ) )
        {
            return false;
        }

        $plugins_dir = trailingslashit( WP_PLUGIN_DIR );

        foreach ( $plugins as $plugin_file )
        {
            $this_plugin_dir = trailingslashit( dirname( $plugins_dir . $plugin_file ) );

            // If plugin is in its own directory, recursively delete the directory.
            if ( strpos( $plugin_file, '/' ) && $this_plugin_dir != $plugins_dir )
            { //base check on if plugin includes directory separator AND that it's not the root plugin folder
                $this->delete_folder($this_plugin_dir,$plugins_dir);
            } else {
               @wp_delete_file($plugins_dir . $plugin_file);
            }
        }

        return true;
    }

    public function delete_uploads()
    {
        $upload_dir = wp_get_upload_dir();

        $this->delete_folder($upload_dir['basedir'], $upload_dir['basedir']);

        $ret['result']=WPVIVID_SUCCESS;
        return $ret;
    }

    public function delete_folder($folder, $base_folder)
    {
        $files = array_diff(scandir($folder), array('.', '..'));

        foreach ($files as $file)
        {
            if (is_dir($folder . DIRECTORY_SEPARATOR . $file))
            {
                $this->delete_folder($folder . DIRECTORY_SEPARATOR . $file, $base_folder);
            } else {
                @wp_delete_file($folder . DIRECTORY_SEPARATOR . $file);
            }
        } // foreach

        if ($folder != $base_folder)
        {
            $tmp = @rmdir($folder);
            return $tmp;
        } else {
            return true;
        }
    }

    public function delete_wp_content()
    {
        global $wpvivid_plugin;

        $wp_content_dir = trailingslashit(WP_CONTENT_DIR);

        $wpvivid_backup=WPvivid_Setting::get_backupdir();

        $whitelisted_folders = array('mu-plugins', 'plugins', 'themes', 'uploads',$wpvivid_backup);

        $dirs = glob($wp_content_dir . '*', GLOB_ONLYDIR);
        foreach ($dirs as $dir)
        {
            if (false == in_array(basename($dir), $whitelisted_folders))
            {
                $this->delete_folder($dir, $dir);
                @rmdir($dir);
            }
        }

        $ret['result']=WPVIVID_SUCCESS;
        return $ret;
    }

    public function delete_mu_plugins()
    {
        $ret['result']=WPVIVID_SUCCESS;

        $mu_plugins = get_mu_plugins();

        if(empty($mu_plugins))
        {
            return $ret;
        }

        $this->delete_folder(WPMU_PLUGIN_DIR, WPMU_PLUGIN_DIR);

        return $ret;
    }

    public function delete_core()
    {
        $ret['result']=WPVIVID_SUCCESS;

        require_once( ABSPATH . 'wp-admin/includes/update-core.php' );

        global $_old_files;

        $wp_dir = ABSPATH;

        foreach ( $_old_files as $old_file )
        {
            $old_file = $wp_dir . $old_file;
            if ( ! file_exists( $old_file ) )
            {
                continue;
            }

            // If the file isn't deleted, try writing an empty string to the file instead.
            @wp_delete_file($old_file);
        }
        return $ret;
    }
}

function wpvivid_function_pre_extract_callback_2($p_event, &$p_header)
{
    $plugins = substr(WP_PLUGIN_DIR, strpos(WP_PLUGIN_DIR, 'wp-content/'));

    if ( isset( $GLOBALS['wpvivid_restore_option'] ) )
    {
        $option = $GLOBALS['wpvivid_restore_option'];
        $type=$GLOBALS['wpvivid_restore_addon_type'];
        if ($type == 'themes')
        {
            if (isset($option['remove_themes']))
            {
                foreach ($option['remove_themes'] as $slug => $themes)
                {
                    if (empty($slug))
                        continue;
                    if(strpos($p_header['filename'],$plugins.DIRECTORY_SEPARATOR.$slug)!==false)
                    {
                        return 0;
                    }
                }
            }
        }
        else if ($type == 'plugin')
        {
            if (isset($option['remove_plugins']))
            {
                foreach ($option['remove_plugins'] as $slug => $plugin)
                {
                    if (empty($slug))
                        continue;
                    if(strpos($p_header['filename'],$plugins.'/'.$slug)!==false)
                    {
                        return 0;
                    }
                }
            }
        }
    }
    else
    {
        $option=array();
    }

    $path = str_replace('\\','/',WP_CONTENT_DIR);
    $content_path = $path.'/';
    if(strpos($p_header['filename'], $content_path.'advanced-cache.php')!==false)
    {
        return 0;
    }

    if(strpos($p_header['filename'], $content_path.'db.php')!==false)
    {
        return 0;
    }

    if(strpos($p_header['filename'], $content_path.'object-cache.php')!==false)
    {
        return 0;
    }

    if(strpos($p_header['filename'],$plugins.'/wpvivid-backuprestore')!==false)
    {
        return 0;
    }

    if(strpos($p_header['filename'],'wp-config.php')!==false)
    {
        return 0;
    }

    if(strpos($p_header['filename'],'wpvivid_package_info.json')!==false)
    {
        return 0;
    }

    if(isset($option['restore_htaccess'])&&$option['restore_htaccess'])
    {

    }
    else
    {
        if(strpos($p_header['filename'],'.htaccess')!==false)
        {
            return 0;
        }
    }

    if(strpos($p_header['filename'],'.user.ini')!==false)
    {
        return 0;
    }

    if(strpos($p_header['filename'],'wordfence-waf.php')!==false)
    {
        return 0;
    }

    if(strpos($p_header['filename'], $content_path.'mu-plugins/endurance-browser-cache.php')!==false)
    {
        return 0;
    }

    if(strpos($p_header['filename'], $content_path.'mu-plugins/endurance-page-cache.php')!==false)
    {
        return 0;
    }

    if(strpos($p_header['filename'], $content_path.'mu-plugins/endurance-php-edge.php')!==false)
    {
        return 0;
    }

    if(strpos($p_header['filename'], $content_path.'mu-plugins/wp-stack-cache.php')!==false)
    {
        return 0;
    }

    return 1;
}admin/css/wpvivid-quick-snapshot-style.css000064400000000611151327705670014746 0ustar00



.wpvivid-span-progress {
    display:block;
    width:100%;
    height:1.5em;
    background-color:#9d9fa5;
    border-radius:0;
    color: #fff;

}

.wpvivid-span-processed-progress {
    display:block;
    height:1.5em;
    background-color:#8bc34a;
    border-radius:0;
    color: #fff;
    padding-left:0.5em;
}

.wpvivid-dashicons-green {
    color:#8bc34a;
}admin/css/wpvivid-admin-bar.css000064400000000072151327705670012472 0ustar00#wpadminbar{
    background-color: #ff8d00 !important;
}admin/css/wpvivid-staging-custom.css000064400000101626151327705670013613 0ustar00.quickbackup-addon{width:100%; box-sizing:border-box;margin-right:10px; padding:10px;}
.quickbackup-addon label{display:inline-block;}

.quickstaging{width:100%;float:left; box-sizing:border-box;margin-right:10px; padding:10px;}
.quickstaging label{display:inline-block;margin-bottom:10px;}

.wpvivid-custom-setting-save{margin: 10px 0 10px 0 !important;display: block !important;float: left;}

.wpvivid-custom-td-border{border-left: 0;}
.wpvivid-custom-backup-detail-odd{margin: 5px 20px 15px 40px;}
.wpvivid-custom-backup-detail-even{background: #f7fcfe; border-left: 4px solid #f7fcfe;}

.wpvivid-custom-database-table-list tbody {
	display:block;
	max-height:200px;
	overflow-y:auto;
	padding: 0;
}
.wpvivid-custom-database-table-list thead{
	display:table;
	width:100%;
}

.wpvivid-custom-database-table-list tbody tr {
	display:table;
	width:100%;
	table-layout:fixed;
}

.wpvivid-schedule-time-select-addon,
.wpvivid-schedule-type-select-addon,
.wpvivid-schedule-remote-select-addon,
.wpvivid-schedule-create-addon{
	float: left;
	padding: 0 10px 10px 10px;
}

/* Popup container */
.wpvivid-custom-popup {
	width: 100%;
	position: relative;
	display: inline-block;
	//cursor: pointer;
}

.wpvivid-custom-popup .wpvivid-custom-popuptext {
	visibility: hidden;
	//width: 220px;
	//background-color: #555;
	//color: #fff;
	//text-align: center;
	border-radius: 6px;
	padding: 8px 0;
	//position: absolute;
	z-index: 1;
	bottom: 75%;
	//margin-left: -80px;
}

/* Popup arrow */
.wpvivid-custom-popup .wpvivid-custom-popuptext::before {
	/*content: "\25B2";
	position: absolute;
	//top: 100%;
	bottom: 95%;
	left: 20%;
	background-color: #555;
	margin-left: -5px;
	border-width: 5px;
	border-style: solid;
	border-color: #555 transparent transparent transparent;*/
}

/* Toggle this class when clicking on the popup container (hide and show the popup) */
.wpvivid-custom-popup .show {
	visibility: visible;
	-webkit-animation: fadeIn 1s;
	animation: fadeIn 1s
}

.wpvivid-custom-popup .hide {
	visibility: hidden;
	-webkit-animation: fadeIn 1s;
	animation: fadeIn 1s
}

.wpvivid-custom-tree-contain{
	width: 100%;
	height: 300px;
	border:1px dotted;
	overflow: auto;
}

#wpvivid_custom_backup_tree .jstree-container-ul > .jstree-node {
	background: transparent;
}

@media screen and (max-width:1650px) {
	.quickbackup-addon{
		float:none;
		width:100%;
	}
}

@media screen and (min-width:1600px) {
	.wpvivid-pro-list{
		width: calc(50% - 8px) !important;
	}
	.wpvivid-pro-list:nth-child(even){
		margin-right: 0 !important;
	}
	.wpvivid-pro-list:nth-child(odd){
		margin-left: 0 !important;
	}
	.wpvivid-pro-list:nth-child(3n){

	}
	.wpvivid-pro-list:nth-child(3n+1){

	}
}

.wpvivid-custom-table-tr-expand{
	background:#f1f1f1;
}

.wpvivid-custom-table-td-expand{
	font-weight:bold;
}

.wpvivid-custom-table{
	width: 100%;
}

.wpvivid-custom-database-wp-table-header{
	height:30px;
	border-left:4px solid #00a0d2;
	padding-left:5px;
	padding-top:5px;
	background-color:#f5f5f5;
}

.wpvivid-custom-database-other-table-header{
	height:30px;
	border-left:4px solid #00a0d2;
	padding-left:5px;
	padding-top:5px;
	background-color:#f5f5f5;
	margin-top: 5px;
}

.wpvivid-custom-database-table-column{
	width:32%;
	float:left;
}

@media screen and (min-width: 1921px) {
	.wpvivid-custom-database-table-column{
		width:24%;
		float:left;
	}
}

@media screen and (max-width: 1920px) {
	.wpvivid-custom-database-table-column{
		width:24%;
		float:left;
	}
}

@media screen and (max-width: 1550px) {
	.wpvivid-custom-database-table-column{
		width:32%;
		float:left;
	}
}

@media screen and (max-width: 1200px) {
	.wpvivid-custom-database-table-column{
		width:49%;
		float:left;
	}
	.wpvivid-database-table-addon{
		padding-top: 5px;
	}
}

@media screen and (max-width: 790px) {
	.wpvivid-custom-database-table-column{
		width:99%;
		float:left;
	}
}

.wpvivid-custom-uploads-left{
	width:30%;
	overflow:auto;
}

.wpvivid-custom-uploads-right{
	width:70%;
	padding: 5px;
}

.wpvivid-custom-uploads-tree{
	width: 100%;
	height: 250px;
	max-height: 250px;
	overflow-y: auto;
}

.wpvivid-export-download-list,
.wpvivid-custom-uploads-table{
	width: 100%;
	height: 250px;
	overflow: auto;
}

.wpvivid-export-download-list ul:nth-of-type(odd) {background:#f1f1f1;}
.wpvivid-export-download-list ul:nth-of-type(even) {}
.wpvivid-export-download-list ul,
.wpvivid-export-download-list ul li{
	width: 100%;
	height: 24px;
	margin: 0;
}

.wpvivid-custom-uploads-table ul:nth-of-type(odd) {background:#f1f1f1;}
.wpvivid-custom-uploads-table ul:nth-of-type(even) {}

.wpvivid-custom-uploads-table ul,
.wpvivid-custom-uploads-table ul li{
	width: 100%;
	height: 24px;
	margin: 0;
}

.wpvivid-custom-uploads-table .wpvivid-custom-table-li-left{
	width: 98%;
	margin: 0;
	float :left;
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
}

.wpvivid-custom-uploads-table .wpvivid-custom-table-li-right{
	width: 2%;
	margin: 0;
	float :left;
}

.wpvivid-custom-uploads-table-body{
	width:100%;
	float:left;
}

.wpvivid-custom-li-folder-icon{
	background: url("../js/jstree/dist/themes/default/32px.png") -260px -4px no-repeat;
	width: 24px;
	height: 24px;
	line-height: 24px;
	margin: 0 4px 0 4px;
	float: left;
}

.wpvivid-custom-li-file-icon{
	background: url("../js/jstree/dist/themes/default/32px.png") -100px -68px no-repeat;
	width: 24px;
	height: 24px;
	line-height: 24px;
	margin: 0 4px 0 4px;
	float: left;
}

.wpvivid-custom-li-font{
	height: 24px;
	line-height: 24px;
	margin: 0;
	float: left;
}

.wpvivid-custom-li-close{
	height: 24px;
	line-height: 24px;
	margin: 0 10px 0 0;
	float: right;
}

.wpvivid-custom-li-database-extra-left{
	height: 24px;
	line-height: 24px;
	margin: 0 0 0 10px;
	float: left;
}

.wpvivid-custom-li-database-extra-right{
	height: 24px;
	line-height: 24px;
	margin: 0 10px 0 0;
	float: right;
}

.wpvivid-custom-uploads-table-body-left{
	float:left;
}

.wpvivid-custom-uploads-table-body-right{
	float:right;
}

.wpvivid-backup-to-font{
	font-weight:bold;
}

.wpvivid-backup-now-space{
	padding-top:10px;
}

.wpvivid-backup-now-size{
	width:200px;
	height:50px;
	font-size:20px;
}

.wpvivid-backup-tips{
	float:left;
	width:100%;
	background-color:#f1f1f1;
}

.wpvivid-backup-to-remote{
	padding-left:20px;
	margin-top:5px;
	margin-bottom:10px;
	border-left:4px solid #00a0d2;
}

div.wpvivid-note{

    -moz-border-radius: 6px;
    -webkit-border-radius: 6px;
    background-color: #f5f5f5;
    background-image: url(../Images/icons/Pencil-48.png);
    background-position: 9px 0px;
    background-repeat: no-repeat;
    border: solid 1px #eee;
    border-radius: 6px;
    line-height: 18px;
    overflow: hidden;
    padding: 15px 60px;
    margin:10px 15px 10px 15px;
}

.wpvivid_tooltip {
	position: relative;
	width:12px;
	height:12px;
	border:1px solid #0073aa;
	background-color:#0073aa;
	color:#fff;
	text-align:center;
	vertical-align:middle;
	border-radius:12px;
}
.wpvivid_tooltiptext a{
	color: #aa0000;
}
.wpvivid_tooltip .wpvivid_tooltiptext {
	visibility: hidden;
	width: 250px;
	background-color: #0085ba;
	color: #fff;
	text-align: center;
	border-radius: 6px;
	padding: 5px;

	/* Position the tooltip */
	position: absolute;
	z-index: 1;
	bottom: 20px;
	left: 50%;
	margin-left: -125px; /* Use half of the width (120/2 = 60), to center the tooltip */
	box-sizing: border-box;
}

.wpvivid_tooltip:hover .wpvivid_tooltiptext {
	visibility: visible;
}
.wpvivid_tooltip .wpvivid_tooltiptext::after {
	content: " ";
	position: absolute;
	top: 100%; /* At the bottom of the tooltip */
	left: 50%;
	margin-left: -5px;
	border-width: 5px;
	border-style: solid;
	border-color:#0085ba transparent transparent transparent;
	box-sizing: border-box;
}
.wpvivid_click_popup{
	background-color:#f7f7f7;
	float:left;
	width:100%;
	border-radius: 6px;
	padding:10px;
	box-sizing:border-box;
}

.quickbackup-addon a{
	text-decoration: none;
}

.wpvivid-intab-addon .nav-tab-active,
.wpvivid-intab-addon .nav-tab-active:focus,
.wpvivid-intab-addon .nav-tab-active:focus:active,
.wpvivid-intab-addon .nav-tab-active:hover{
	border-bottom: 1px solid #ffffff;
}

.wpvivid-schedule-fix,
.wpvivid-debug-text-fix,
.wpvivid-setting-text-fix{
	height: 28px;
	line-height: 28px;
}

@media screen and (max-width: 782px) {
	.wpvivid-tooltip-fix{
		margin-top: 15px !important;
	}
	.wpvivid-schedule-fix,
	.wpvivid-debug-text-fix,
	.wpvivid-setting-text-fix{
		height: 36px;
		line-height: 36px;
	}
}

.wpvivid-text-space-bottom{margin-bottom: 5px;}
.wpvivid-text-space-right{margin-right: 5px;}
.wpvivid-element-space-bottom{margin-bottom: 10px;}
.wpvivid-element-space-right{margin-right: 10px;}
.wpvivid-restore-log-addon{padding: 10px;width: 100%;height: 500px;overflow: auto;box-sizing: border-box; border-top: none; border-color: #ccc;}
.wpvivid-custom-tree ul{margin: 0 !important;}
.wpvivid-staging-log{padding:10px; width:100%; height:300px; overflow:auto; box-sizing:border-box;}

.wpvivid-refresh-tree:hover{
	background: #0085ba;
	border-color: #0073aa #006799 #006799;
	box-shadow: 0 1px 0 #006799;
	color: #fff;
	text-decoration: none;
	text-shadow: 0 -1px 1px #006799, 1px 0 1px #006799, 0 1px 1px #006799, -1px 0 1px #006799;
	padding: 0 2px;
	border-radius: 3px;
}

.wpvivid-setting-addon input{
	margin: 0 4px 0 0;
}

.wpvivid-pro-update{
	display: inline-block;
	vertical-align: top;
	box-sizing: border-box;
	margin: 3px 0 0 2px;
	padding: 0 0 0 8px;
	min-width: 18px;
	height: 18px;
	border-radius: 9px;
	background-color: #ca4a1f;
	color: #fff;
	font-size: 11px;
	line-height: 1.6;
	text-align: center;
	z-index: 26;
}

.schedule-active
{
	background-color: #90ee90;
}

.wpvivid-transparency-tab,
.wpvivid-transparency-tab:hover,
.wpvivid-transparency-tab:focus,
.wpvivid-transparency-tab:focus:active{
	background: #ffffff;
}
.column-wpvivid_role_name { width:25% }
.column-wpvivid_role { width:8% }
.column-wpvivid_role_edit { width:8% }

.wpvivid-local-remote-backup-list span{margin: 0;}
.wpvivid-schedule-list span{margin: 0;}
.wpvivid-log-list span{margin: 0;}

.wpvivid-upload-tr{border: 2px solid #006799; box-sizing:border-box; -moz-box-sizing:border-box; -webkit-box-sizing:border-box;}
.wpvivid-backup-list{border-collapse: collapse;}
.wpvivid-list-td-center{text-align: center!important;}
.wpvivid-backup-option-box{width:100%; border:1px solid #f1f1f1; float:left; padding:10px 10px 0 10px; box-sizing: border-box; margin-top: 10px;}

.wpvivid-tree-dashicons{
	padding: 0 !important;
	width: auto !important;
	height: auto !important;
}

.wpvivid-tree-dashicons:before{
	padding: 0 !important;
	background-color: #007cba !important;
	font-size: 20px !important;
	color: #fff !important;
}

/**
 * All of the CSS for your admin-specific functionality should be
 * included in this file.
 */

.list-top-chip{float:left;display:block; vertical-align:middle; }
.list-top-chip::after{content:" "}
.backup-basic-info{min-width:100px; margin:10px; float:left;}
.quickbackup{width:calc(100% - 290px);float:left; box-sizing:border-box;margin-right:10px; padding:10px;}
.quickbackup label{display:inline-block;margin-bottom:10px;}
.qucikbackup-schedule{width:280px; float:left; box-sizing:border-box; min-width:280px;}
.quickstart-archive-block{width:33%;float:left; position:relative; box-sizing:border-box; }
.quickstart-storage-block{width:33%;padding:10px; float:left; position:relative; box-sizing:border-box;}
.quickstart-btn{width:33%;padding:10px; float:left; position:relative; box-sizing:border-box;}
.quickbackup-btn{display:block !important; margin:0 auto !important;width:150px;height:50px !important; line-height:48px !important; font-size:20px !important;}
.schedule-block{float:left;width:100%;padding:0 10px 10px 10px;box-sizing:border-box;}
.postbox:after{content: ".";display: block;height: 0;clear: both;visibility: hidden;}
.quickbackup-block .fieldset label{margin-bottom:5px;}
.custom-info{padding:10px;}
.quickstart-storage-setting{width:100%;float:left; padding-left:10px; box-sizing:border-box;background-color:#f1f1f1; margin-bottom:10px;}
.action-progress-bar{background-color:#f1f1f1 !important;margin:10px;color:#000;}
.action-progress-bar-percent{background-color:#0085ba !important;color:#fff;}
.storage-providers{float:left;padding:10px;cursor:pointer;}
.storage-providers:hover{background-color:#f1f1f1 !important; color:#fff !important;}
.storage-providers-active{background-color:#0085ba !important; color:#fff !important;}
.storage-account-block{padding:10px;}
.storage-account-button{margin:10px 10px 10px 0 !important;display:block !important; float:left;}
.schedule-tab-block{padding:10px;}
.schedule-tab-block input{display:line-block; margin-top:10px; margin-bottom:10px;margin-right:10px;}
.setting-tab-block{padding: 10px 10px 10px 0;}
.setting-tab-block input{display:line-block; margin-top:10px; margin-bottom:10px;margin-right:10px;}
.setting-page-content{padding-left:10px;}
.backup-log-btn{height:30px;float:left; margin-left:10px; margin-bottom:10px;}
@keyframes rotate { 0%   { transform: rotate(0); } 100%  { transform: rotate(359.9deg); } }
.wpvivid-backup-storage-list tr:hover,
.wpvivid-websiteinfo-list tr:hover,
.wpvivid-backuplist tr:hover,
.wpvivid-loglist tr:hover,
.wpvivid-remote-storage-list tr:hover {background-color: #f9f9f9;}
.restore_log{padding:10px; width:100%; height:500px; overflow:auto; box-sizing:border-box;}
.restore-button-position{margin:10px 10px 10px 0px; float:left;}
.restore-button-position input{margin-right:10px;}
.nav-tab.delete{display: block;overflow: hidden;position:relative;}
.nav-tab-delete-img{position: absolute;top: 0px;right: 3px;}
.button-secondary.log-page{background: #fff;color: #00a0d2;border: 1px solid #00a0d2;margin: 3px;}
.button-secondary.log-page:hover{color: #0b82a7;border: 1px solid #0b82a7;}
.button-secondary.log-page:focus{color: #00a0d2;border: 1px solid #00a0d2;}
.remote-storage-set-default-block{padding-top:10px;padding-left:10px;padding-right:10px;}
.remote-storage-set-default-block input{display:line-block; margin-top:10px; margin-bottom:10px;margin-right:10px;}
.remote-storage-ftp-passive-block,
.remote-storage-amazons3-storage-class,
.remote-storage-amazons3-encryption{padding-left:10px;}
.remote-storage-ftp-passive-block input,
.remote-storage-amazons3-storage-class input,
.remote-storage-amazons3-encryption input{display:line-block; margin-top:10px; margin-bottom:10px;margin-right:10px;}
.backup-list-head{}
.storage-account-form {padding-top: 1px; padding-bottom: 1px;}
.quicktransfer{width:100%; float:left; box-sizing:border-box;margin-right:10px; padding:10px;}
.quicktransfer label{display:inline-block;margin-bottom:10px;}
.quicktransfer-btn{display:block !important; margin:0 auto !important;width:220px;height:50px !important; line-height:48px !important; font-size:20px !important;}
.wpvivid-feature-pro{font-size:12px;border:1px solid #f1f1f1;padding:3px 5px;margin: 10px 10px 10px 0;border-radius:5px;}
@media screen and (max-width:1650px) {
	.qucikbackup-schedule{
		float:none;
		width:100%;
	}
	.quickbackup{
		float:none;
		width:100%;
	}
	.export-import-block{
		float:none;
		width:100%;
	}
}
@media only screen and (max-width: 1350px){
	#poststuff #post-body.columns-2 {
		margin:0;
	}
	#wpbody-content #post-body.columns-2 #postbox-container-1 {
		margin-right: 0;
		width: 100%;
	}

}

@media screen and (max-width:1315px) {
	.quickstart-storage-block{
		width:50%;
	}
	.quickstart-archive-block{
		width:50%;
	}
	.quickstart-btn{
		clear:both;
		float:none;
		width:100%;
	}
}
@media screen and (max-width:800px){
	.tablelistcolumn{
		display:block;
	}
	.backup-list-head{
		display:none;
	}
	.backuptime{
		font-size:16px;
	}
	.backuplist-delete-backup{
		display:none;
	}
	.website-info-head{
		display:none;
	}
	.log-head{
		display:none;
	}
	.lafowef{
		display:none;
	}
	.storage-account-form input{
		width:360px;
	}
}
@media screen and (max-width:600px) {
	.quickstart-storage-block{
		float:none;
		width:100%;
	}
	.quickstart-archive-block{
		float:none;
		width:100%;
	}
	.quickstart-btn{
		float:none;
		width:100%;
	}
}

/* Popup container */
.wpvivid-popup {
	position: relative;
	display: inline-block;
	cursor: pointer;
}

/* The actual popup (appears on top) */
.wpvivid-popup .wpvivid-popuptext {
	visibility: hidden;
	width: 220px;
	background-color: #555;
	color: #fff;
	text-align: center;
	border-radius: 6px;
	padding: 8px 0;
	position: absolute;
	z-index: 1;
	bottom: 75%;
	margin-left: -80px;
}

/* Popup arrow */
.wpvivid-popup .wpvivid-popuptext::after {
	content: "";
	position: absolute;
	top: 100%;
	left: 50%;
	margin-left: -5px;
	border-width: 5px;
	border-style: solid;
	border-color: #555 transparent transparent transparent;
}

/* Toggle this class when clicking on the popup container (hide and show the popup) */
.wpvivid-popup .show {
	visibility: visible;
	-webkit-animation: fadeIn 1s;
	animation: fadeIn 1s
}

.wpvivid-popup .hide {
	visibility: hidden;
	-webkit-animation: fadeIn 1s;
	animation: fadeIn 1s
}

/* Add animation (fade in the popup) */
@-webkit-keyframes fadeIn {
	from {opacity: 0;}
	to {opacity: 1;}
}

@keyframes fadeIn {
	from {opacity: 0;}
	to {opacity:1 ;}
}

.wpvivid-storage-form{
	margin-top:10px;
	margin-bottom:5px;
}

.wpvivid-storage-select{
	margin-top:10px;
	margin-bottom:5px;
	padding-bottom:8px;
	padding-left:2px;
}

.wpvivid-storage-form input[type=text],
.wpvivid-storage-form input[type=password]{
	width: 350px;
}

.wpvivid-storage-form-desc{
	background-color:#f5f5f5;
	margin-top:10px;
	padding:4px 5px;
}

.wpvivid-debug-text-fix{
	height: 28px;
	line-height: 28px;
}

@media screen and (max-width: 782px) {
	.wpvivid-debug-text-fix{
		height: 36px;
		line-height: 36px;
	}
}

.wpvivid-text-space-bottom{margin-bottom: 5px;}
.wpvivid-text-space-right{margin-right: 5px;}
.wpvivid-element-space-bottom{margin-bottom: 10px;}
.wpvivid-element-space-right{margin-right: 10px;}
.wpvivid-export-type-provider{float: left; cursor: pointer; padding: 10px; height: 30px; line-height: 30px; border-right: 1px solid #e5e5e5;}
.wpvivid-export-type-provider:hover{background-color:#f1f1f1 !important; color:#fff !important;}
.wpvivid-export-type-provider-active{background-color:#0085ba !important; color:#fff !important;}
.wpvivid-import-log{padding:10px; width:100%; height:500px; overflow:auto; box-sizing:border-box;}
.import > tbody > :nth-child(odd) {background-color: #ffffff;}
.import > tbody tr:hover {background-color: #f9f9f9;}
.import .column-delete {width: 10%;}
.wpvivid-export-import-block span{margin: 0;}
.export-import-block{width:100%; box-sizing:border-box;margin-right:10px; padding:10px;}
.export-import-block label{display:inline-block;}
.export-import-block a{text-decoration: none;}


.wpvivid-canvas {
	width:100%;
	box-sizing: border-box;
}
.wpvivid-dashboard, .wpvivid-backup{
	position: relative;

	border: 1px solid #ccd0d4;
	box-shadow: 0 1px 1px rgba(0,0,0,.04);
	background: #fff;
}
.wpvivid-welcome-panel {
	width:100%;
	padding: 0px 10px 0;
	border: 1px solid #ccd0d4;
	box-shadow: 0 1px 1px rgba(0,0,0,.04);
	background: #fff;
	box-sizing: border-box;
}
.wpvivid-red {
	background:red;
	color:#fff;
}
.wpvivid-green {
	background:#8bc34a;
	color:#fff;
}
.wpvivid-blue {
	background:#007cba;
	color:#fff;
}
.wpvivid-orange {
	background:orange;
	color:#fff;
}
.wpvivid-grey {
	background-color:grey;
	color: #fff;
}
.wpvivid-grey-light {
	background-color:#eee;
	color: grey;
}
.wpvivid-hover-blue:hover{
	background:#007cba;
	color:#fff;
	cursor:pointer;
}
.wpvivid-font-hover-blue:hover{
	color:#007cba;
	cursor:pointer;
}
.wpvivid-hover-green:hover{
	background:#8bc34a;
	color:#fff;
	cursor:pointer;
}
span.dashicons{
	margin-right:2px;
}
.wpvivid-welcome-panel .about-description{
	font-size: 16px;
	margin: 0;
}
.wpvivid-page-title {
	font-weight: 700;
	font-size:1.2em;
}
.wpvivid-dashicons-large {
	font-size:5em;
	width:1.2em;
	height:1.2em;
	float:left;
}
.wpvivid-dashicons-large:hover{
	text-shadow: 0px 0px 2px rgba(0,0,0,0.4);
}
.wpvivid-dashicons-middle {
	font-size:3em;
	width:1.2em;
	height:1.2em;
	float:left;

}

.wpvivid-welcome-bar{
	width:100%;
	box-sizing: border-box;

}
.wpvivid-welcome-bar-left, .wpvivid-block-left {
	width:50%;
	float:left;
	padding:1em;
	box-sizing: border-box;
}
.wpvivid-welcome-bar-right, .wpvivid-block-right {
	width:50%;
	float:left;
	padding:1em;
	box-sizing: border-box;
}
.wpvivid-features-box, .wpvivid-two-cols{
	width:50%;
	float:left;
	font:14px;
	padding:1em 1em 1em 1em;
	box-sizing: border-box;
}

.wpvivid-four-cols{
	width:25%;
	float:left;
	font:14px;

	box-sizing: border-box;
	border:1px solid #f1f1f1;
}
.wpvivid-one-coloum {
	width:100%;
	float:left;
	font:14px;
	padding:1em 1em 1em 1em;
	box-sizing: border-box;
}

.wpvivid-dashboard .wpvivid-two-col li {
	border-bottom: 1px solid #eee;
	margin: 0px;
	padding: 10px;
	box-sizing: border-box;

}

.wpvivid-dashboard .wpvivid-two-col li::after {
	content: "";
	clear: both;
	display: table;
}


.wpvivid-dashboard .wpvivid-two-col li img {
	padding:1em;
	width: 5em;
	height: 5em;
	float: left;
	margin: 0.5em;
}
.wpvivid-sidebar-main{
	width:100%;
	float:left;
	font:13px;
	box-sizing: border-box;

}
.wpvivid-sidebar-main li::after {
	content: "";
	clear: both;
	display: table;
}
.wpvivid-sidebar-main li {
	margin: 0px;
	padding: 10px;
	box-sizing: border-box;

}
.wpvivid-sidebar-main li img {
	padding:1em;
	width: 5em;
	height: 5em;
	float: left;
	margin: 0.5em;
}
.wpvivid-sidebar{
	width:100%;
	float:left;
	font:13px;
	box-sizing: border-box;

}
.wpvivid-sidebar li::after {
	content: "";
	clear: both;
	display: table;
}
.wpvivid-sidebar li {
	border-bottom: 1px solid #eee;
	margin: 0px;
	padding: 10px;
	box-sizing: border-box;

}
.wpvivid-sidebar li img {
	padding:1em;
	width: 5em;
	height: 5em;
	float: left;
	margin: 0.5em;
}
.wpvivid-features-box-image-progress {
	width:100%;
}
.wpvivid-features-box-image-optimiztion-details {
	width:50%;
}
.wpvivid-features-box-image-optimiztion-plate {
	width:50%;
}
.wpvivid-features-box-image-optimiztion-percentage{
	margin-top:2.5em;
}
.wpvivid-features-box-image-optimiztion-number {
	font-size: 5em;
	font-family:Sans-serif;
	font-weight:600;
	color:orange;
	float:left;
	text-shadow:0 0.05em 0.05em rgba(0,0,0,0.4);
}
.wpvivid-features-box-image-optimiztion-percentage-unit  {
	font-size:1em;
	color:grey;
	float:left;
	font-size:1.2em;
	padding-bottom:1em;
}
.wpvivid-rectangle {
	border-radius:0.4em;
	padding:0.5em;
	margin-left: 0.5em;
	margin-right:0.5em;
	font-size:0.8em;
}
.wpvivid-rectangle-small {
	border-radius:0.4em;
	padding:0.2em;
	margin-left: 0.5em;
	margin-right:0.5em;
	font-size:0.6em;
}
.wpvivid-icon-16px{
	font-size:16px;
	margin-top:2px;
	color:#aaa;
	cursor:pointer;
}
.wpvivid-icon-16px-nopointer{
	font-size:16px;
	margin-top:2px;
}
.wpvivid-text-line{
	padding:1px;
}
.wpvivid-text-selected{
	background:#eee;
}
.wpvivid-text-line:hover{
	background:#eee;
}
.wpvivid-span-progress {
	display:block;
	width:100%;
	height:1.5em;
	background-color:#9d9fa5;
	border-radius:0;
	color: #fff;

}
.wpvivid-span-processed-progress {
	display:block;
	height:1.5em;
	background-color:#8bc34a;
	border-radius:0;
	color: #fff;
	padding-left:0.5em;
}
.wpvivid-span-processed-percent-progress{
	width:53%;
}
.wpvivid-clear-float {
	content: "";
	clear: both;
	display: table;
	width:100%;
}
.wpvivid-title {
	font-size:1.2em;
	color:#000;
}
.wpvivid-nav-bar{
	width: 100%;
	float: left;
	padding: 1em;
	background-color: #fff;
	margin-bottom: 1em;
	border-top:1px solid #ddd;
	border-bottom:1px solid #ddd;
	box-sizing: border-box;

}

.wpvivid-two-col{
	width:50%;
	float:left;
	box-sizing: border-box;
}
.wpvivid-float-right{
	float:right;
}
.wpvivid-float-left{
	float:left;
}
.wpvivid-dashicons-white {
	color:#ffffff;
}
.wpvivid-dashicons-green {
	color:#8bc34a;
}
.wpvivid-dashicons-red {
	color:red;
}
.wpvivid-dashicons-orange  {
	color:orange;
}
.wpvivid-dashicons-blue {
	color:#007cba;
}
.wpvivid-dashicons-grey, .wpvivid-dashicons-editor-help{
	color:#999;
}
.wpvivid-nav-tab-wrapper {
	padding-bottom:0!important;

}
.wpvivid-nav-tab {
	background:#ffffff;
}
.wpvivid-nav-tab-active,.wpvivid-nav-tab-active, .wpvivid-nav-tab-active:focus:active, .wpvivid-nav-tab-active:hover {
	border-bottom:1px solid #ffffff;
	background:#ffffff;
}
.wpvivid-tabcontent {
	display: none;
}


.wpvivid-workflow {
	padding:1em;
	border:1px solid #eee;
	border-radius:8px;
}


/* tooltip */
.wpvivid-tooltip {
	display:inline-block;
	position:relative;
	text-align:left;
}
/* tooltip - top */
.wpvivid-top {
	min-width:300px;
	top:-20px;
	left:50%;
	transform:translate(-50%, -100%);
	padding:10px 20px;
	color:#444444;
	background-color:#FFFFFF;
	font-weight:normal;
	font-size:13px;
	border-radius:8px;
	position:absolute;
	z-index:99999999;
	box-sizing:border-box;
	box-shadow:0 1px 8px rgba(0,0,0,0.5);
	visibility:hidden; opacity:0; transition:opacity 0.8s;
}

.wpvivid-tooltip:hover .wpvivid-top {
	visibility:visible; opacity:1;
}

.wpvivid-tooltip .wpvivid-top i {
	position:absolute;
	top:100%;
	left:50%;
	margin-left:-12px;
	width:24px;
	height:12px;
	overflow:hidden;
}

.wpvivid-tooltip .wpvivid-top i::after {
	content:'';
	position:absolute;
	width:12px;
	height:12px;
	left:50%;
	transform:translate(-50%,-50%) rotate(45deg);
	background-color:#FFFFFF;
	box-shadow:0 1px 8px rgba(0,0,0,0.5);
}

/* tooltip - bottom */
.wpvivid-bottom {
	min-width:300px;
	top:40px;
	left:50%;
	transform:translate(-50%, 0);
	padding:10px 20px;
	color:#444444;
	background-color:#FFFFFF;
	font-weight:normal;
	font-size:13px;
	border-radius:8px;
	position:absolute;
	z-index:99999999;
	box-sizing:border-box;
	box-shadow:0 1px 8px rgba(0,0,0,0.5);
	visibility:hidden; opacity:0; transition:opacity 0.4s;
}

.wpvivid-dashicons-editor-help:hover .wpvivid-bottom {
	visibility:visible; opacity:1;
}
.wpvivid-dashicons-white:hover .wpvivid-bottom{
	visibility:visible; opacity:1;
}
.wpvivid-tooltip .wpvivid-bottom i {
	position:absolute;
	bottom:100%;
	left:50%;
	margin-left:-12px;
	width:24px;
	height:12px;
	overflow:hidden;
}

.wpvivid-tooltip .wpvivid-bottom i::after {
	content:'';
	position:absolute;
	width:12px;
	height:12px;
	left:50%;
	transform:translate(-50%,50%) rotate(45deg);
	background-color:#FFFFFF;
	box-shadow:0 1px 8px rgba(0,0,0,0.5);
}
/* tooltip - left */
.wpvivid-left {
	min-width:300px;
	top:50%;
	right:100%;
	margin-right:20px;
	transform:translate(0, -50%);
	padding:10px 20px;
	color:#444444;
	background-color:#FFFFFF;
	font-weight:normal;
	font-size:13px;
	border-radius:8px;
	position:absolute;
	z-index:99999999;
	box-sizing:border-box;
	box-shadow:0 1px 8px rgba(0,0,0,0.5);
	visibility:hidden; opacity:0; transition:opacity 0.8s;
}

.wpvivid-tooltip:hover .wpvivid-left {
	visibility:visible; opacity:1;
}

.wpvivid-tooltip .wpvivid-left i {
	position:absolute;
	top:50%;
	left:100%;
	margin-top:-12px;
	width:12px;
	height:24px;
	overflow:hidden;
}

.wpvivid-tooltip .wpvivid-left i::after {
	content:'';
	position:absolute;
	width:12px;
	height:12px;
	left:0;
	top:50%;
	transform:translate(-50%,-50%) rotate(-45deg);
	background-color:#FFFFFF;
	box-shadow:0 1px 8px rgba(0,0,0,0.5);
}
/* tooltip - right */
.wpvivid-right {
	min-width:300px;
	top:50%;
	left:100%;
	margin-left:20px;
	transform:translate(0, -50%);
	padding:10px 20px;
	color:#444444;
	background-color:#FFFFFF;
	font-weight:normal;
	font-size:13px;
	border-radius:8px;
	position:absolute;
	z-index:99999999;
	box-sizing:border-box;
	box-shadow:0 1px 8px rgba(0,0,0,0.5);
	visibility:hidden;
	opacity:0;
	transition:opacity 0.8s;
}

.wpvivid-tooltip:hover .wpvivid-right {
	visibility:visible; opacity:1;
}
.wpvivid-installer-list li{
	padding:0.2em 0;
}
.wpvivid-tooltip .wpvivid-right i {
	position:absolute;
	top:50%;
	right:100%;
	margin-top:-12px;
	width:12px;
	height:24px;
	overflow:hidden;
}

.wpvivid-tooltip .wpvivid-right i::after {
	content:'';
	position:absolute;
	width:12px;
	height:12px;
	left:0;
	top:50%;
	transform:translate(50%,-50%) rotate(-45deg);
	background-color:#FFFFFF;
	box-shadow:0 1px 8px rgba(0,0,0,0.5);
}

/* radio box style */

.wpvivid-radio {
	display: block;
	position: relative;
	padding-left: 2em;
	margin-bottom: 1em;
	cursor: pointer;
	font-size: 1em;
	-webkit-user-select: none;
	-moz-user-select: none;
	-ms-user-select: none;
	user-select: none;
}
/* Hide the browser's default radio button */
.wpvivid-radio input {
	position: absolute;
	opacity: 0;
	cursor: pointer;
}
/* Create a custom radio button */
.wpvivid-radio-checkmark {
	position: absolute;
	top: 2px;
	left: 0;
	height: 16px;
	width: 16px;
	background-color: #eee;
	border-radius: 50%;
}
/* On mouse-over, add a grey background color */
.wpvivid-radio:hover input ~ .wpvivid-radio-checkmark {
	background-color: #ccc;
}
/* When the radio button is checked, add a blue background */
.wpvivid-radio input:checked ~ .wpvivid-radio-checkmark {
	background-color: #8bc34a;
}
/* Create the indicator (the dot/circle - hidden when not checked) */
.wpvivid-radio-checkmark:after {
	content: "✔";
	position: absolute;
	font-size:12px;
	top:-1px;
	left:3px;
	display: none;
	color:white;
}
/* Show the indicator (dot/circle) when checked */
.wpvivid-radio input:checked ~ .wpvivid-radio-checkmark:after {
	display: block;
}

/* custom checkbox */

/* Customize the label (the container) */
.wpvivid-checkbox {
	display: block;
	position: relative;

	padding-left: 2em;
	cursor: pointer;
	font-size: 1em;

	-webkit-user-select: none;
	-moz-user-select: none;
	-ms-user-select: none;
	user-select: none;
}
/* Hide the browser's default checkbox */
.wpvivid-checkbox input {
	position: absolute;
	opacity: 0;
	cursor: pointer;
	height: 0;
	width: 0;
}
/* Create a custom checkbox */
.wpvivid-checkbox-checkmark {
	position: absolute;
	top: 3px;
	left: 0;
	height: 16px;
	width: 16px;
	background-color: #eee;
	border-radius: 4px;
}
/* On mouse-over, add a grey background color */
.wpvivid-checkbox:hover input ~ .wpvivid-checkbox-checkmark {
	background-color: #ccc;
	border-radius: 4px;
}
/* When the checkbox is checked, add a blue background */
.wpvivid-checkbox input:checked ~ .wpvivid-checkbox-checkmark {
	background-color: #8bc34a;
	border-radius: 4px;
}
/* Create the checkmark/indicator (hidden when not checked) */
.wpvivid-checkbox-checkmark:after {
	content: "";
	position: absolute;
	display: none;
}
/* Show the checkmark when checked */
.wpvivid-checkbox input:checked ~ .wpvivid-checkbox-checkmark:after {
	display: block;
}
/* Style the checkmark/indicator */
.wpvivid-checkbox .wpvivid-checkbox-checkmark:after {
	left: 4px;
	top: 0px;
	width: 5px;
	height: 10px;
	border: solid white;
	border-width: 0px 3px 3px 0;
	-webkit-transform: rotate(45deg);
	-ms-transform: rotate(45deg);
	transform: rotate(45deg);
}
/* toggle switch */

/* The switch - the box around the slider */
.wpvivid-switch {
	position: relative;
	display: inline-block;
	width: 32px;
	height: 16px;
}

/* Hide default HTML checkbox */
.wpvivid-switch input {
	opacity: 0;
	width: 0;
	height: 0;
}

/* The slider */
.wpvivid-slider {
	position: absolute;
	cursor: pointer;
	top: 0;
	left: 0;
	right: 0;
	bottom: 0px;
	background-color: #ccc;
	-webkit-transition: .4s;
	transition: .4s;
}

.wpvivid-slider:before {
	position: absolute;
	content: "";
	height: 12px;
	width: 12px;
	left: 2px;
	bottom: 2px;
	background-color: white;
	-webkit-transition: .4s;
	transition: .4s;
}

input:checked + .wpvivid-slider {
	background-color: #8bc34a;
}

input:focus + .wpvivid-slider {
	box-shadow: 0 0 1px #8bc34a;
}


input:checked + .wpvivid-slider:before {
	-webkit-transform: translateX(16px);
	-ms-transform: translateX(16px);
	transform: translateX(16px);
}

/* Rounded sliders */
.wpvivid-slider.wpvivid-round {
	border-radius: 16px;
}

.wpvivid-slider.wpvivid-round:before {
	border-radius: 50%;
}


/*general postion*/
.wpvivid-tooltip-padding-top {
	padding-top:0.2em;
}
.wpvivid-tooltip-padding-top-small {
	padding-top:0.1em;
}

/*  HD computer monitor */

@media only screen and (min-width: 1366px) {

	.wpvivid-four-cols{
		width:100%;
		margin-bottom:1em;
	}
}

/*  Tablets in landscape mode, older desktop monitors */

@media only screen and (min-width: 1024px) and (max-width: 1365px) {


	#poststuff #post-body.columns-2 {
		margin-right: 0px;
	}
	#wpbody-content #post-body.columns-2 #postbox-container-1 {
		margin-right: 0;
		width: 100%;
	}
	.wpvivid-four-cols{
		width:100%;
		margin-bottom:1em;
	}
}



/*  Tablets in portrait mode, large display smartphones landscape mode */

@media only screen and (min-width: 768px) and (max-width: 1023px) {
	.wpvivid-two-col {
		width:100%;
	}
	.wpvivid-float-right{
		float:left;
	}
	.wpvivid-four-cols{
		width:100%;
		margin-bottom:1em;
	}
	..wpvivid-welcome-bar-left{
		width:100%;

	}
	.wpvivid-welcome-bar-right{
		display:none;
	}
	.wpvivid-ignore{
		display:none;
	}

}

/*  Smartphones in landscape mode */

@media only screen and (min-width: 421px) and (max-width: 767px) {
	.wpvivid-features-box,.wpvivid-two-col {
		width:100%;
	}
	.wpvivid-float-right{
		float:left;
	}
	.wpvivid-welcome-bar-left{
		width:100%;
	}
	.wpvivid-welcome-bar-right{
		display:none;
	}
	.wpvivid-four-cols{
		width:100%;
		margin-bottom:1em;
	}
	.wpvivid-ignore{
		display:none;
	}
}


/*  Smartphones in portrait mode  */

@media only screen and (max-width: 420px) {
	.wpvivid-features-box,.wpvivid-two-col {
		width:100%;
	}
	.wpvivid-float-right{
		float:left;
	}
	.wpvivid-welcome-bar-left{
		width:100%;

	}
	.wpvivid-welcome-bar-right{
		display:none;
	}

	.wpvivid-four-cols{
		width:100%;
		margin-bottom:1em;
	}
	.wpvivid-ignore{
		display:none;
	}
	.wpvivid-img-opt-responsive{
		margin-top:1em;
		padding-top:1em;
		border-top:1px solid #f1f1f1;
	}
}
admin/css/wpvivid-admin.css000064400000020422151327705670011731 0ustar00/**
 * All of the CSS for your admin-specific functionality should be
 * included in this file.
 */

.list-top-chip{float:left;display:block; vertical-align:middle; }
.list-top-chip::after{content:" "}
.backup-basic-info{min-width:100px; margin:10px; float:left;}
.quickbackup{width:calc(100% - 290px);float:left; box-sizing:border-box;margin-right:10px; padding:10px;}
.quickbackup label{display:inline-block;margin-bottom:10px;}
.qucikbackup-schedule{width:280px; float:left; box-sizing:border-box; min-width:280px;}
.quickstart-archive-block{width:33%;float:left; position:relative; box-sizing:border-box; }
.quickstart-storage-block{width:33%;padding:10px; float:left; position:relative; box-sizing:border-box;}
.quickstart-btn{width:33%;padding:10px; float:left; position:relative; box-sizing:border-box;}
.quickbackup-btn{display:block !important; margin:0 auto !important;width:150px;height:50px !important; line-height:48px !important; font-size:20px !important;}
.schedule-block{float:left;width:100%;padding:0 10px 10px 10px;box-sizing:border-box;}
.postbox:after{content: ".";display: block;height: 0;clear: both;visibility: hidden;}
.quickbackup-block .fieldset label{margin-bottom:5px;}
.custom-info{padding:10px;}
.quickstart-storage-setting{width:100%;float:left; padding-left:10px; box-sizing:border-box;background-color:#f1f1f1; margin-bottom:10px;}
.action-progress-bar{background-color:#f1f1f1 !important;margin:10px;color:#000;}
.action-progress-bar-percent{background-color:#0085ba !important;color:#fff;}
.storage-providers{float:left;padding:10px;cursor:pointer;}
.storage-providers:hover{background-color:#f1f1f1 !important; color:#fff !important;}
.storage-providers-active{background-color:#0085ba !important; color:#fff !important;}
.storage-account-block{padding:10px;}
.storage-account-button{margin:10px 10px 10px 0 !important;display:block !important; float:left;}
.schedule-tab-block{padding:10px;}
.schedule-tab-block input{display:line-block; margin-top:10px; margin-bottom:10px;margin-right:10px;}
.setting-tab-block{padding: 10px 10px 10px 0;}
.setting-tab-block input{display:line-block; margin-top:10px; margin-bottom:10px;margin-right:10px;}
.setting-page-content{padding-left:10px;}
.backup-log-btn{height:30px;float:left; margin-left:10px; margin-bottom:10px;}
@keyframes rotate { 0%   { transform: rotate(0); } 100%  { transform: rotate(359.9deg); } }
.wpvivid-backup-storage-list tr:hover,
.wpvivid-websiteinfo-list tr:hover,
.wpvivid-backuplist tr:hover,
.wpvivid-loglist tr:hover,
.wpvivid-remote-storage-list tr:hover {background-color: #f9f9f9;}
.restore_log{padding:10px; width:100%; height:500px; overflow:auto; box-sizing:border-box;}
.restore-button-position{margin:10px 10px 10px 0px; float:left;}
.restore-button-position input{margin-right:10px;}
.nav-tab.delete{display: block;overflow: hidden;position:relative;}
.nav-tab-delete-img{position: absolute;top: 0px;right: 3px;}
.button-secondary.log-page{background: #fff;color: #00a0d2;border: 1px solid #00a0d2;margin: 3px;}
.button-secondary.log-page:hover{color: #0b82a7;border: 1px solid #0b82a7;}
.button-secondary.log-page:focus{color: #00a0d2;border: 1px solid #00a0d2;}
.remote-storage-set-default-block{padding-top:10px;padding-left:10px;padding-right:10px;}
.remote-storage-set-default-block input{display:line-block; margin-top:10px; margin-bottom:10px;margin-right:10px;}
.remote-storage-ftp-passive-block,
.remote-storage-amazons3-storage-class,
.remote-storage-amazons3-encryption{padding-left:10px;}
.remote-storage-ftp-passive-block input,
.remote-storage-amazons3-storage-class input,
.remote-storage-amazons3-encryption input{display:line-block; margin-top:10px; margin-bottom:10px;margin-right:10px;}
.backup-list-head{}
.storage-account-form {padding-top: 1px; padding-bottom: 1px;}
.quicktransfer{width:100%; float:left; box-sizing:border-box;margin-right:10px; padding:10px;}
.quicktransfer label{display:inline-block;margin-bottom:10px;}
.quicktransfer-btn{display:block !important; margin:0 auto !important;width:220px;height:50px !important; line-height:48px !important; font-size:20px !important;}
.wpvivid-feature-pro{font-size:12px;border:1px solid #f1f1f1;padding:3px 5px;margin: 10px 10px 10px 0;border-radius:5px;}
@media screen and (max-width:1650px) {
	.qucikbackup-schedule{
		float:none;
		width:100%;
	}
	.quickbackup{
		float:none;
		width:100%;
	}
	.export-import-block{
		float:none;
		width:100%;
	}
}
@media only screen and (max-width: 1350px){
	#poststuff #post-body.columns-2 {
    	margin:0;
	}
    #wpbody-content #post-body.columns-2 #postbox-container-1 {
        margin-right: 0;
        width: 100%;
	}

}

@media screen and (max-width:1315px) {
	.quickstart-storage-block{
		width:50%;
	}
	.quickstart-archive-block{
		width:50%;
	}
	.quickstart-btn{
		clear:both;
		float:none;
		width:100%;		
	}
}
@media screen and (max-width:800px){
	.tablelistcolumn{
		display:block;
	}
	.backup-list-head{
		display:none;
	}
    .backuptime{
		font-size:16px;		
	}
	.backuplist-delete-backup{
		display:none;
	}
	.website-info-head{
		display:none;
	}
	.log-head{
		display:none;
	}
	.lafowef{
		display:none;
	}
	.storage-account-form input{
		width:360px;
	}
}
@media screen and (max-width:600px) {
	.quickstart-storage-block{
		float:none;
		width:100%;
	}
	.quickstart-archive-block{
		float:none;
		width:100%;
	}
	.quickstart-btn{
		float:none;
		width:100%;		
	}
}

/* Popup container */
.wpvivid-popup {
	position: relative;
	display: inline-block;
	cursor: pointer;
}

/* The actual popup (appears on top) */
.wpvivid-popup .wpvivid-popuptext {
	visibility: hidden;
	width: 220px;
	background-color: #555;
	color: #fff;
	text-align: center;
	border-radius: 6px;
	padding: 8px 0;
	position: absolute;
	z-index: 1;
	bottom: 75%;
	margin-left: -80px;
}

/* Popup arrow */
.wpvivid-popup .wpvivid-popuptext::after {
	content: "";
	position: absolute;
	top: 100%;
	left: 50%;
	margin-left: -5px;
	border-width: 5px;
	border-style: solid;
	border-color: #555 transparent transparent transparent;
}

/* Toggle this class when clicking on the popup container (hide and show the popup) */
.wpvivid-popup .show {
	visibility: visible;
	-webkit-animation: fadeIn 1s;
	animation: fadeIn 1s
}

.wpvivid-popup .hide {
	visibility: hidden;
	-webkit-animation: fadeIn 1s;
	animation: fadeIn 1s
}

/* Add animation (fade in the popup) */
@-webkit-keyframes fadeIn {
	from {opacity: 0;}
	to {opacity: 1;}
}

@keyframes fadeIn {
	from {opacity: 0;}
	to {opacity:1 ;}
}

.wpvivid-storage-form{
	margin-top:10px;
	margin-bottom:5px;
}

.wpvivid-storage-select{
	margin-top:10px;
	margin-bottom:5px;
	padding-bottom:8px;
	padding-left:2px;
}

.wpvivid-storage-form input[type=text],
.wpvivid-storage-form input[type=password]{
	width: 350px;
}

.wpvivid-storage-form-desc{
	background-color:#f5f5f5;
	margin-top:10px;
	padding:4px 5px;
}

.wpvivid-debug-text-fix{
	height: 28px;
	line-height: 28px;
}

@media screen and (max-width: 782px) {
	.wpvivid-debug-text-fix{
		height: 36px;
		line-height: 36px;
	}
}

.wpvivid-text-space-bottom{margin-bottom: 5px;}
.wpvivid-text-space-right{margin-right: 5px;}
.wpvivid-element-space-bottom{margin-bottom: 10px;}
.wpvivid-element-space-right{margin-right: 10px;}
.wpvivid-export-type-provider{float: left; cursor: pointer; padding: 10px; height: 30px; line-height: 30px; border-right: 1px solid #e5e5e5;}
.wpvivid-export-type-provider:hover{background-color:#f1f1f1 !important;}
.wpvivid-export-type-provider-active{background-color:#0085ba !important;}
.wpvivid-import-log{padding:10px; width:100%; height:500px; overflow:auto; box-sizing:border-box;}
.import > tbody > :nth-child(odd) {background-color: #ffffff;}
.import > tbody tr:hover {background-color: #f9f9f9;}
.import .column-delete {width: 10%;}
.wpvivid-export-import-block span{margin: 0;}
.export-import-block{width:100%; box-sizing:border-box;margin-right:10px; padding:10px;}
.export-import-block label{display:inline-block;}
.export-import-block a{text-decoration: none;}

.quickbackup-addon{width:100%; box-sizing:border-box;margin-right:10px; padding:10px;}
.quickbackup-addon label{display:inline-block;}
@media screen and (max-width:1650px) {
	.quickbackup-addon{
		float:none;
		width:100%;
	}
}
.quickbackup-addon a{
	text-decoration: none;
}

.wpvivid-backup-tips{
	float:left;
	width:100%;
	background-color:#f1f1f1;
}

.wpvivid-custom-table-manager.nav-tab-wrapper :nth-child(n+3):nth-child(-n+11) {
	display:block;
}admin/css/wpvivid-snapshot-style.css000064400000051132151327705670013640 0ustar00/**
 * All of the CSS for WPvivid Plugins Dashboard should be
 * included in this file.
 */
 .wpvivid-canvas {
     max-width:1600px;
	 box-sizing: border-box;
 }
.wpvivid-dashboard, .wpvivid-backup{
	position: relative;

	border: 1px solid #ccd0d4;
	box-shadow: 0 1px 1px rgba(0,0,0,.04);
	background: #fff;
}
.wpvivid-welcome-panel {
    width:100%;
    padding: 0px 10px 0;
    border: 1px solid #ccd0d4;
    box-shadow: 0 1px 1px rgba(0,0,0,.04);
    background: #fff;
	box-sizing: border-box;
}
.wpvivid-red {
	background:red;
	color:#fff;
}
.wpvivid-green {
	background:#8bc34a;
	color:#fff;
}
.wpvivid-blue {
	background:#007cba;
	color:#fff;
}
.wpvivid-orange {
	background:orange;
	color:#fff;
}
.wpvivid-grey {
	background-color:grey;
	color: #fff;
}
span.dashicons{
	margin-right:2px;
}
.wpvivid-welcome-panel .about-description{
	font-size: 16px;
    margin: 0;
}
.wpvivid-page-title {
	font-weight: 700;
	font-size:1.2em;
}
.wpvivid-dashicons-large {
	font-size:5em;
	width:1.2em;
	height:1.2em;
	float:left;
}
.wpvivid-dashicons-large:hover{
	text-shadow: 0px 0px 2px rgba(0,0,0,0.4);
}
.wpvivid-dashicons-middle {
	font-size:3em;
	width:1.2em;
	height:1.2em;
	float:left;

}

.wpvivid-welcome-bar{
	width:100%;
	box-sizing: border-box;

}
.wpvivid-welcome-bar-left, .wpvivid-block-left {
	width:50%;
	float:left;
	padding:1em;
	box-sizing: border-box;
}
.wpvivid-welcome-bar-right, .wpvivid-block-right {
	width:50%;
	float:left;
	padding:1em;
	box-sizing: border-box;
}
.wpvivid-features-box, .wpvivid-two-cols{
	width:50%;
	float:left;
	font-size:14px;
	padding:1em 1em 1em 1em;
	box-sizing: border-box;
}
.wpvivid-four-cols{
	width:25%;
	float:left;
	font-size:14px;
	
	box-sizing: border-box;
	border:1px solid #f1f1f1;
}
.wpvivid-one-coloum {
	width:100%;
	float:left;
	padding:1em 1em 1em 1em;
	box-sizing: border-box;
}

.wpvivid-dashboard-list {
    padding:1em;
    border:1px solid #eee;
    box-sizing: border-box;
}

.wpvivid-dashboard li {
    border: 1px solid #eee;
    margin: 0px;
    padding: 10px;
    box-sizing: border-box;

}

.wpvivid-dashboard li::after {
    content: "";
    clear: both;
    display: table;
}

.wpvivid-dashboard .wpvivid-two-col li {
	border: 1px solid #eee;
    margin: 0px;
    padding: 10px;
    box-sizing: border-box;

}

.wpvivid-dashboard .wpvivid-two-col li::after {
	content: "";
    clear: both;
    display: table;
}

.wpvivid-dashboard .wpvivid-three-cols li img {
    padding:1em;
    width: 5em;
    height: 5em;
    float: left;
    margin: 0.5em;
}

.wpvivid-dashboard .wpvivid-two-col .wpvivid-three-cols li img {
	padding:1em;
	width: 5em;
    height: 5em;
    float: left;
    margin: 0.5em;
}
.wpvivid-dashboard .wpvivid-three-cols li
{
    width:33%;
    float:left;
    box-sizing: border-box;
}
.wpvivid-dashboard .wpvivid-three-cols-update{
    position:absolute;
    display:block;
    width:1.5em;
    height:1.5em;
    border:1px solid orange;
    border-radius:50%;
    text-align:center;
    vertical-align:middle;
    background:orange;
    color:#fff;
}
.wpvivid-dashboard .wpvivid-three-cols  li {
    border: 1px solid #eee;
    padding:0.2em 0.2em 0 0.2em;
    box-sizing: border-box;

}

.wpvivid-dashboard .wpvivid-three-cols li::after {
    content: "";
    clear: both;
    display: table;
}
.wpvivid-dashboard .wpvivid-three-cols-li {
    padding:0.5em 0;
}
.wpvivid-dashboard .wpvivid-three-cols-active {
    background:#eaf1fe;
}
.wpvivid-sidebar-main{
	width:100%;
	float:left;
	font-size:13px;
	box-sizing: border-box;

}
.wpvivid-sidebar-main li::after {
	content: "";
    clear: both;
    display: table;
}
.wpvivid-sidebar-main li {
    margin: 0px;
    padding: 10px;
    box-sizing: border-box;

}
.wpvivid-sidebar-main li img {
	padding:1em;
	width: 5em;
    height: 5em;
    float: left;
    margin: 0.5em;
}
.wpvivid-sidebar{
	width:100%;
	float:left;
	font-size:13px;
	box-sizing: border-box;
	
}
.wpvivid-sidebar li::after {
	content: "";
    clear: both;
    display: table;
}
.wpvivid-sidebar li {
	border-bottom: 1px solid #eee;
    margin: 0px;
    padding: 10px;
    box-sizing: border-box;

}
.wpvivid-sidebar li img {
	padding:1em;
	width: 5em;
    height: 5em;
    float: left;
    margin: 0.5em;
}
.wpvivid-features-box-image-progress {
	width:100%;
}
.wpvivid-features-box-image-optimiztion-details {
	width:50%;
}
.wpvivid-features-box-image-optimiztion-plate {
	width:50%;
}
.wpvivid-features-box-image-optimiztion-percentage{
	margin-top:2.5em;
}
.wpvivid-features-box-image-optimiztion-number {
	font-size: 5em;
    font-family:Sans-serif;
	font-weight:600;
	color:orange;
	float:left;
	text-shadow:0 0.05em 0.05em rgba(0,0,0,0.4);
}
.wpvivid-features-box-image-optimiztion-percentage-unit  {
	font-size:1em;
	color:grey;
	float:left;
	font-size:1.2em;
	padding-bottom:1em;
}
.wpvivid-rectangle {
	border-radius:0.4em;
	padding:0.5em;
	margin-left: 0.5em;
	margin-right:0.5em;
	font-size:0.8em;
}
.wpvivid-rectangle-small {
	border-radius:0.4em;
	padding:0.2em;
	margin-left: 0.5em;
	margin-right:0.5em;
	font-size:0.6em;
}
.wpvivid-span-progress {
	display:block;
	width:100%;
	height:1.5em;
	background-color:#9d9fa5;
	border-radius:0;
	color: #fff;
	
}
.wpvivid-span-processed-progress {
	display:block;
	height:1.5em;
	background-color:#8bc34a;
	border-radius:0;
	color: #fff;
	padding-left:0.5em;
}
.wpvivid-span-processed-percent-progress{
	width:53%;
}
.wpvivid-clear-float {
	content: "";
    clear: both;
    display: table;
	width:100%;
}
.wpvivid-title {
	font-size:1.2em;
	color:#000;
}
.wpvivid-nav-bar{
	width: 100%;
    float: left;
    padding: 1em;
    background-color: #fff;
    margin-bottom: 1em;
	border-top:1px solid #ddd;
	border-bottom:1px solid #ddd;
	box-sizing: border-box;

}

.wpvivid-two-col{
	width:50%;
	float:left;
	box-sizing: border-box;
}
.wpvivid-float-right{
	float:right;
}
.wpvivid-float-left{
	float:left;
}
.wpvivid-dashicons-green {
	color:#8bc34a;
}
.wpvivid-dashicons-red {
	color:red;
}
.wpvivid-dashicons-orange  {
	color:orange;
}
.wpvivid-dashicons-blue {
	color:#007cba;
}
.wpvivid-dashicons-grey, .wpvivid-dashicons-editor-help{
	color:#999;
}
.wpvivid-nav-tab-wrapper {
	padding-bottom:0!important;
	
}
.wpvivid-nav-tab {
	background:#ffffff;
}
.wpvivid-nav-tab-active,.wpvivid-nav-tab-active, .wpvivid-nav-tab-active:focus:active, .wpvivid-nav-tab-active:hover {
	border-bottom:1px solid #ffffff;
	background:#ffffff;
}
.wpvivid-tabcontent {
	display: none;
}


.wpvivid-workflow {
	padding:1em;
	border:1px solid #eee;
	border-radius:8px;
}


/* tooltip */
.wpvivid-tooltip {
    display:inline-block;
    position:relative;
    text-align:left;
}
/* tooltip - top */
.wpvivid-top {
    min-width:300px; 
    top:-20px;
    left:50%;
    transform:translate(-50%, -100%);
    padding:10px 20px;
    color:#444444;
    background-color:#FFFFFF;
    font-weight:normal;
    font-size:13px;
    border-radius:8px;
    position:absolute;
    z-index:99999999;
    box-sizing:border-box;
    box-shadow:0 1px 8px rgba(0,0,0,0.5);
    visibility:hidden; opacity:0; transition:opacity 0.8s;
}

.wpvivid-tooltip:hover .wpvivid-top {
    visibility:visible; opacity:1;
}

.wpvivid-tooltip .wpvivid-top i {
    position:absolute;
    top:100%;
    left:50%;
    margin-left:-12px;
    width:24px;
    height:12px;
    overflow:hidden;
}

.wpvivid-tooltip .wpvivid-top i::after {
    content:'';
    position:absolute;
    width:12px;
    height:12px;
    left:50%;
    transform:translate(-50%,-50%) rotate(45deg);
    background-color:#FFFFFF;
    box-shadow:0 1px 8px rgba(0,0,0,0.5);
}

/* tooltip - bottom */
.wpvivid-bottom {
    min-width:300px; 
    top:40px;
    left:50%;
    transform:translate(-50%, 0);
    padding:10px 20px;
    color:#444444;
    background-color:#FFFFFF;
    font-weight:normal;
    font-size:13px;
    border-radius:8px;
    position:absolute;
    z-index:99999999;
    box-sizing:border-box;
    box-shadow:0 1px 8px rgba(0,0,0,0.5);
    visibility:hidden; opacity:0; transition:opacity 0.4s;
}

.wpvivid-dashicons-editor-help:hover .wpvivid-bottom {
    visibility:visible; opacity:1;
}

.wpvivid-dashicons-white-editor-help:hover .wpvivid-bottom {
    visibility:visible; opacity:1;
}

.wpvivid-dashicons-white:hover .wpvivid-bottom {
    visibility:visible; opacity:1;
}

.wpvivid-tooltip .wpvivid-bottom i {
    position:absolute;
    bottom:100%;
    left:50%;
    margin-left:-12px;
    width:24px;
    height:12px;
    overflow:hidden;
}

.wpvivid-tooltip .wpvivid-bottom i::after {
    content:'';
    position:absolute;
    width:12px;
    height:12px;
    left:50%;
    transform:translate(-50%,50%) rotate(45deg);
    background-color:#FFFFFF;
    box-shadow:0 1px 8px rgba(0,0,0,0.5);
}
/* tooltip - left */
.wpvivid-left {
    min-width:300px; 
    top:50%;
    right:100%;
    margin-right:20px;
    transform:translate(0, -50%);
    padding:10px 20px;
    color:#444444;
    background-color:#FFFFFF;
    font-weight:normal;
    font-size:13px;
    border-radius:8px;
    position:absolute;
    z-index:99999999;
    box-sizing:border-box;
    box-shadow:0 1px 8px rgba(0,0,0,0.5);
    visibility:hidden; opacity:0; transition:opacity 0.8s;
}

.wpvivid-tooltip:hover .wpvivid-left {
    visibility:visible; opacity:1;
}

.wpvivid-tooltip .wpvivid-left i {
    position:absolute;
    top:50%;
    left:100%;
    margin-top:-12px;
    width:12px;
    height:24px;
    overflow:hidden;
}

.wpvivid-tooltip .wpvivid-left i::after {
    content:'';
    position:absolute;
    width:12px;
    height:12px;
    left:0;
    top:50%;
    transform:translate(-50%,-50%) rotate(-45deg);
    background-color:#FFFFFF;
    box-shadow:0 1px 8px rgba(0,0,0,0.5);
}
/* tooltip - right */
.wpvivid-right {
    min-width:300px; 
    top:50%;
    left:100%;
    margin-left:20px;
    transform:translate(0, -50%);
    padding:10px 20px;
    color:#444444;
    background-color:#FFFFFF;
    font-weight:normal;
    font-size:13px;
    border-radius:8px;
    position:absolute;
    z-index:99999999;
    box-sizing:border-box;
    box-shadow:0 1px 8px rgba(0,0,0,0.5);
    visibility:hidden; 
	opacity:0; 
	transition:opacity 0.8s;
}

.wpvivid-tooltip:hover .wpvivid-right {
    visibility:visible; opacity:1;
}
.wpvivid-installer-list li{
	padding:0.2em 0;
}
.wpvivid-tooltip .wpvivid-right i {
    position:absolute;
    top:50%;
    right:100%;
    margin-top:-12px;
    width:12px;
    height:24px;
    overflow:hidden;
}

.wpvivid-tooltip .wpvivid-right i::after {
    content:'';
    position:absolute;
    width:12px;
    height:12px;
    left:0;
    top:50%;
    transform:translate(50%,-50%) rotate(-45deg);
    background-color:#FFFFFF;
    box-shadow:0 1px 8px rgba(0,0,0,0.5);
}

/* radio box style */

.wpvivid-radio {
	display: block;
	position: relative;
	padding-left: 2em;
	margin-bottom: 1em;
	cursor: pointer;
	font-size: 1em;
	-webkit-user-select: none;
	-moz-user-select: none;
	-ms-user-select: none;
	user-select: none;
}
/* Hide the browser's default radio button */
.wpvivid-radio input {
  position: absolute;
  opacity: 0;
  cursor: pointer;
}
/* Create a custom radio button */
.wpvivid-radio-checkmark {
  position: absolute;
  top: 2px;
  left: 0;
  height: 16px;
  width: 16px;
  background-color: #eee;
  border-radius: 50%;
}
/* On mouse-over, add a grey background color */
.wpvivid-radio:hover input ~ .wpvivid-radio-checkmark {
  background-color: #ccc;
}
/* When the radio button is checked, add a blue background */
.wpvivid-radio input:checked ~ .wpvivid-radio-checkmark {
  background-color: #8bc34a;
}
/* Create the indicator (the dot/circle - hidden when not checked) */
.wpvivid-radio-checkmark:after {
  content: "✔";
  position: absolute;
  font-size:12px;
  top:-1px;
  left:3px;
  display: none;
  color:white;
}
/* Show the indicator (dot/circle) when checked */
.wpvivid-radio input:checked ~ .wpvivid-radio-checkmark:after {
	display: block;
}

/* custom checkbox */

/* Customize the label (the container) */
.wpvivid-checkbox {
  display: block;
  position: relative;

  padding-left: 2em;
  cursor: pointer;
  font-size: 1em;
  
  -webkit-user-select: none;
  -moz-user-select: none;
  -ms-user-select: none;
  user-select: none;
}
/* Hide the browser's default checkbox */
.wpvivid-checkbox input {
  position: absolute;
  opacity: 0;
  cursor: pointer;
  height: 0;
  width: 0;
}
/* Create a custom checkbox */
.wpvivid-checkbox-checkmark {
  position: absolute;
  top: 3px;
  left: 0;
  height: 16px;
  width: 16px;
  background-color: #eee;
  border-radius: 4px;
}
/* On mouse-over, add a grey background color */
.wpvivid-checkbox:hover input ~ .wpvivid-checkbox-checkmark {
  background-color: #ccc;
  border-radius: 4px;
}
/* When the checkbox is checked, add a blue background */
.wpvivid-checkbox input:checked ~ .wpvivid-checkbox-checkmark {
  background-color: #8bc34a;
  border-radius: 4px;
}
/* Create the checkmark/indicator (hidden when not checked) */
.wpvivid-checkbox-checkmark:after {
  content: "";
  position: absolute;
  display: none;
}
/* Show the checkmark when checked */
.wpvivid-checkbox input:checked ~ .wpvivid-checkbox-checkmark:after {
  display: block;
}
/* Style the checkmark/indicator */
.wpvivid-checkbox .wpvivid-checkbox-checkmark:after {
  left: 4px;
  top: 0px;
  width: 5px;
  height: 10px;
  border: solid white;
  border-width: 0px 3px 3px 0;
  -webkit-transform: rotate(45deg);
  -ms-transform: rotate(45deg);
  transform: rotate(45deg);
}
/* toggle switch */

/* The switch - the box around the slider */
.wpvivid-switch {
  position: relative;
  display: inline-block;
  width: 32px;
  height: 16px;
}

/* Hide default HTML checkbox */
.wpvivid-switch input {
  opacity: 0;
  width: 0;
  height: 0;
}

/* The slider */
.wpvivid-slider {
  position: absolute;
  cursor: pointer;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0px;
  background-color: #ccc;
  -webkit-transition: .4s;
  transition: .4s;
}

.wpvivid-slider:before {
  position: absolute;
  content: "";
  height: 12px;
  width: 12px;
  left: 2px;
  bottom: 2px;
  background-color: white;
  -webkit-transition: .4s;
  transition: .4s;
}

input:checked + .wpvivid-slider {
  background-color: #8bc34a;
}

input:focus + .wpvivid-slider {
  box-shadow: 0 0 1px #8bc34a;
}


input:checked + .wpvivid-slider:before {
  -webkit-transform: translateX(16px);
  -ms-transform: translateX(16px);
  transform: translateX(16px);
}

/* Rounded sliders */
.wpvivid-slider.wpvivid-round {
  border-radius: 16px;
}

.wpvivid-slider.wpvivid-round:before {
  border-radius: 50%;
}


/*general postion*/
.wpvivid-tooltip-padding-top {
	padding-top:0.2em;
}
.wpvivid-tooltip-padding-top-small {
	padding-top:0.1em;
}

/*  HD computer monitor */

@media only screen and (min-width: 1366px) {

.wpvivid-four-cols{
	width:100%;
	margin-bottom:1em;
}
}

/*  Tablets in landscape mode, older desktop monitors */

@media only screen and (min-width: 1024px) and (max-width: 1365px) {


#poststuff #post-body.columns-2 {
    margin-right: 0px;
}
#wpbody-content #post-body.columns-2 #postbox-container-1 {
    margin-right: 0;
    width: 100%;
}
.wpvivid-four-cols{
	width:100%;
	margin-bottom:1em;
}
}



/*  Tablets in portrait mode, large display smartphones landscape mode */

@media only screen and (min-width: 768px) and (max-width: 1023px) {
    .wpvivid-two-col {
        width:100%;
    }
    .wpvivid-float-right{
        float:left;
    }
    .wpvivid-dashboard .wpvivid-three-cols li{
        width:100%;
        box-sizing: border-box;
    }
    .wpvivid-four-cols{
        width:100%;
        margin-bottom:1em;
    }
    .wpvivid-welcome-bar-left{
        width:100%;

    }
    .wpvivid-welcome-bar-right{
        display:none;
    }
    .wpvivid-ignore{
        display:none;
    }

}

/*  Smartphones in landscape mode */

@media only screen and (min-width: 421px) and (max-width: 767px) {
.wpvivid-features-box,.wpvivid-two-col {
	width:100%;
}
.wpvivid-float-right{
	float:left;
}
.wpvivid-welcome-bar-left{
	width:100%;
}
.wpvivid-welcome-bar-right{
	display:none;
}
.wpvivid-four-cols{
	width:100%;
	margin-bottom:1em;
}
.wpvivid-ignore{
	display:none;
}
    .wpvivid-dashboard .wpvivid-three-cols li{
        width:100%;
        box-sizing: border-box;
    }
}

/*  Smartphones in portrait mode  */

@media only screen and (max-width: 420px) {
.wpvivid-features-box,.wpvivid-two-col {
	width:100%;
}
.wpvivid-float-right{
	float:left;
}
.wpvivid-welcome-bar-left{
	width:100%;

}
.wpvivid-welcome-bar-right{
	display:none;
}

.wpvivid-four-cols{
	width:100%;
	margin-bottom:1em;
}
.wpvivid-ignore{
	display:none;
}
.wpvivid-img-opt-responsive{
	margin-top:1em;
	padding-top:1em;
	border-top:1px solid #f1f1f1;
}
    .wpvivid-dashboard .wpvivid-three-cols li{
        width:100%;
        box-sizing: border-box;
    }
}

.wpvivid-rectangle {
    border-radius:0.4em;
    padding:0.5em;
    margin-left: 0.5em;
    margin-right:0.5em;
    font-size:0.8em;
}
.wpvivid-rectangle-small {
    border-radius:0.4em;
    padding:0.2em;
    margin-left: 0.5em;
    margin-right:0.5em;
    font-size:0.6em;
}
.wpvivid-icon-16px{
    font-size:16px;
    margin-top:2px;
    color:#aaa;
    cursor:pointer;
}
.wpvivid-icon-16px-nopointer{
    font-size:16px;
    margin-top:2px;
}
.wpvivid-text-line{
    padding:1px;
    white-space:nowrap;
}
.wpvivid-text-selected{
    background:#eee;
}
.wpvivid-text-line:hover{
    background:#eee;
}
.wpvivid-grey {
    background-color:grey;
    color: #fff;
}
.wpvivid-grey-light {
    background-color:#eee;
    color: grey;
}
.wpvivid-hover-blue:hover{
    background:#007cba;
    color:#fff;
    cursor:pointer;
}
.wpvivid-font-hover-blue:hover{
    color:#007cba;
    cursor:pointer;
}
.wpvivid-hover-green:hover{
    background:#8bc34a;
    color:#fff;
    cursor:pointer;
}
.nav-tab-delete-img-addon{position: absolute;top: 2px;right: 3px;}

/* WPvivid Restore Progress Bar */
.wpvivid-stepper-wrapper
{
    margin-top: auto;
    display: flex;
    justify-content: space-between;
    margin-bottom: 20px;
}
.wpvivid-stepper-item
{
    position: relative;
    display: flex;
    flex-direction: column;
    align-items: center;
    flex: 1;
}

@media (max-width: 768px)
{
    .wpvivid-stepper-item
    {
        font-size: 12px;
    }
}


.wpvivid-stepper-item::before
{
    position: absolute;
    content: "";
    border-bottom: 2px solid #ccc;
    width: 100%;
    top: 20px;
    left: -50%;
    z-index: 2;
}

.wpvivid-stepper-item::after {
    position: absolute;
    content: "";
    border-bottom: 2px solid #ccc;
    width: 100%;
    top: 20px;
    left: 50%;
    z-index: 2;
}

.wpvivid-stepper-item .wpvivid-step-counter {
    position: relative;
    z-index: 5;
    display: flex;
    justify-content: center;
    align-items: center;
    width: 40px;
    height: 40px;
    border-radius: 50%;
    background: #ccc;
    margin-bottom: 6px;
}

.wpvivid-stepper-item.active {
    font-weight: bold;
}

.wpvivid-stepper-item.completed .wpvivid-step-counter {
    background-color: #8bc34a;
    color:#fff;
}

.wpvivid-stepper-item.completed::after {
    position: absolute;
    content: "";
    border-bottom: 2px solid #4bb543;
    width: 100%;
    top: 20px;
    left: 50%;
    z-index: 3;
}

.wpvivid-stepper-item:first-child::before {
    content: none;
}
.wpvivid-stepper-item:last-child::after {
    content: none;
}

.wpvivid-custom-exclude-tree-info .jstree-open>.jstree-ocl {
    background-position: -165px -75px !important;
}

.wpvivid-custom-exclude-tree-info .jstree-closed>.jstree-ocl {
    background-position: -133px -75px !important;
}

.wpvivid-addon-info-text {
    overflow: hidden;
    text-overflow: ellipsis;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
}

.wpvivid-addons {
    cursor:pointer;
}

.wpvivid-remote-storage-tab h2.nav-tab-wrapper :nth-child(n+3):nth-child(-n+11) {
    display:block;
}

.quicksnapshot{width:100%;float:left; box-sizing:border-box;margin-right:10px; padding:10px;}
.quicksnapshot label{display:inline-block;margin-bottom:10px;}admin/class-wpvivid-admin.php000064400000237237151327705670012261 0ustar00<?php

/**
 * The admin-specific functionality of the plugin.
 *
 * @link       https://wpvivid.com
 * @since      0.9.1
 *
 * @package    WPvivid
 * @subpackage WPvivid/admin
 */

/**
 * The admin-specific functionality of the plugin.
 *
 * Defines the plugin name, version, and two examples hooks for how to
 * enqueue the admin-specific stylesheet and JavaScript.
 *
 * @package    WPvivid
 * @subpackage WPvivid/admin
 * @author     wpvivid team
 */
if (!defined('WPVIVID_PLUGIN_DIR')){
    die;
}
class WPvivid_Admin
{

    /**
     * The ID of this plugin.
     *
     * 
     * @access   private
     * @var      string    $plugin_name    The ID of this plugin.
     */
    private $plugin_name;

    /**
     * The version of this plugin.
     *
     * 
     * @access   private
     * @var      string    $version    The current version of this plugin.
     */
    private $version;

    private $screen_ids;

    private $toolbar_menus;

    private $submenus;
    /**
     * Initialize the class and set its properties.
     *
     * 
     * @param      string    $plugin_name       The name of this plugin.
     * @param      string    $version    The version of this plugin.
     */
    public function __construct($plugin_name, $version)
    {
        add_action('admin_enqueue_scripts',array( $this,'enqueue_styles'));
        add_action('admin_enqueue_scripts',array( $this,'enqueue_scripts'));

        if(is_multisite())
        {
            add_action('network_admin_menu',array( $this,'add_plugin_admin_menu'));
        }
        else
        {
            add_action('admin_menu',array( $this,'add_plugin_admin_menu'));
        }

        add_action('admin_bar_menu',array( $this,'add_toolbar_items'),100);
        add_action('admin_head',array( $this,'wpvivid_get_siteurl'),100);

        $plugin_basename = plugin_basename( plugin_dir_path( __DIR__ ) . 'wpvivid-backuprestore.php' );
        add_filter('plugin_action_links_' . $plugin_basename, array( $this,'add_action_links'));

        add_filter('wpvivid_add_tab_page', array($this, 'wpvivid_add_default_tab_page'));

        add_filter('wpvivid_get_screen_ids',array($this,'get_screen_ids'),10);
        add_filter('wpvivid_get_toolbar_menus',array($this,'get_toolbar_menus'),10);
        add_filter('wpvivid_get_admin_menus',array($this,'get_admin_menus'),10);
        //add_filter('wpvivid_add_side_bar', array($this, 'wpvivid_add_side_bar'), 10, 2);
        add_action('wpvivid_add_side_bar', array($this, 'wpvivid_add_side_bar'), 10, 2);

        add_action('wpvivid_before_setup_page',array($this,'show_add_my_review'));
        add_action('wpvivid_before_setup_page',array($this,'check_extensions'));
        add_action('wpvivid_before_setup_page',array($this,'check_amazons3'));
        //add_action('wpvivid_before_setup_page',array($this,'check_dropbox'));
        add_action('wpvivid_before_setup_page',array($this,'init_js_var'));

        add_filter('wpvivid_add_log_tab_page', array($this, 'add_log_tab_page'), 10);

        add_action('admin_notices', array($this, 'check_wpvivid_pro_version'));
        add_action('admin_notices', array($this, 'check_wpvivid_free_htaccess_rule'));

        add_filter('wpvivid_current_user_show_toolbar', array($this, 'current_user_show_toolbar'), 10, 2);

        $this->plugin_name = $plugin_name;
        $this->version = $version;
    }

    public function current_user_show_toolbar($show_toolbar, $capability)
    {
        if(is_admin()&&current_user_can('manage_options'))
        {
            $show_toolbar=true;
        }
        return $show_toolbar;
    }

    public function add_log_tab_page($setting_array)
    {
        $setting_array['backup_log_page'] = array('index' => '1', 'tab_func' =>  array($this, 'wpvivid_add_tab_log'), 'page_func' => array($this, 'wpvivid_add_page_log'));
        //$setting_array['read_log_page'] = array('index' => '2', 'tab_func' =>  array($this, 'wpvivid_add_tab_read_log'), 'page_func' => array($this, 'wpvivid_add_page_read_log'));
        return $setting_array;
    }

    public function get_screen_ids($screen_ids)
    {
        $screen_ids[]='toplevel_page_'.$this->plugin_name;
        $screen_ids[]='wpvivid-backup_page_wpvivid-transfer';
        $screen_ids[]='wpvivid-backup_page_wpvivid-setting';
        $screen_ids[]='wpvivid-backup_page_wpvivid-schedule';
        $screen_ids[]='wpvivid-backup_page_wpvivid-remote';
        $screen_ids[]='wpvivid-backup_page_wpvivid-website';
        $screen_ids[]='wpvivid-backup_page_wpvivid-log';
        $screen_ids[]='wpvivid-backup_page_wpvivid-key';
        $screen_ids[]='wpvivid-backup_page_wpvivid-mainwp';
        $screen_ids[]='wpvivid-backup_page_wpvivid_premium';
        return $screen_ids;
    }

    public function get_toolbar_menus($toolbar_menus)
    {
        $menu['id']='wpvivid_admin_menu';
        $menu['title']='WPvivid Backup';
        $toolbar_menus[$menu['id']]=$menu;

        $admin_url = admin_url();

        $menu['id']='wpvivid_admin_menu_backup';
        $menu['parent']='wpvivid_admin_menu';
        $menu['title']=__('Backup & Restore', 'wpvivid-backuprestore');
        $menu['tab']='admin.php?page=WPvivid&tab-backup';
        $menu['href']=$admin_url . 'admin.php?page=WPvivid&tab-backup';
        $menu['capability']='administrator';
        $menu['index']=1;
        $toolbar_menus[$menu['parent']]['child'][$menu['id']]=$menu;

        return $toolbar_menus;
    }

    public function get_admin_menus($submenus)
    {
        $submenu['parent_slug']=$this->plugin_name;
        $submenu['page_title']='WPvivid Backup';
        $submenu['menu_title']=__('Backup & Restore', 'wpvivid-backuprestore');
        $submenu['capability']='administrator';
        $submenu['menu_slug']=$this->plugin_name;
        $submenu['function']=array($this, 'display_plugin_setup_page');
        $submenu['index']=1;
        $submenus[$submenu['menu_slug']]=$submenu;

        $submenu['parent_slug']=$this->plugin_name;
        $submenu['page_title']='WPvivid Backup';
        $submenu['menu_title']=__('Settings', 'wpvivid-backuprestore');
        $submenu['capability']='administrator';
        $submenu['menu_slug']='wpvivid-setting';
        $submenu['function']=array($this, 'display_plugin_setup_page');
        $submenu['index']=5;
        $submenus[$submenu['menu_slug']]=$submenu;

        return $submenus;
    }

    public function wpvivid_add_side_bar($html, $show_schedule = false){
        $wpvivid_version = WPVIVID_PLUGIN_VERSION;
        $wpvivid_version = apply_filters('wpvivid_display_pro_version', $wpvivid_version);

        ?>
        <div class="postbox">
            <h2>
                <div style="float: left; margin-right: 5px;"><span style="margin: 0; padding: 0"><?php esc_html_e('Current Version: ', 'wpvivid-backuprestore'); echo esc_html($wpvivid_version);?></span></div>
                <div style="float: left; margin-right: 5px;"><span style="margin: 0; padding: 0">|</span></div>
                <div style="float: left; margin-left: 0;">
                    <span style="margin: 0; padding: 0"><a href="https://wordpress.org/plugins/wpvivid-backuprestore/#developers" target="_blank" style="text-decoration: none;"><?php esc_html_e('ChangeLog', 'wpvivid-backuprestore'); ?></a></span>
                </div>
                <div style="clear: both;"></div>
            </h2>
        </div>
        <div class="postbox">
            <h2>
                <?php esc_html_e('Like the plugin? A'); ?>
                <a href="https://wordpress.org/support/plugin/wpvivid-backuprestore/reviews/?filter=5#new-post" target="_blank" style="color: #ffba00;" ">&#9733;&#9733;&#9733;&#9733;&#9733;</a>

                <?php esc_html_e('review will motivate us a lot.', 'wpvivid-backuprestore'); ?>
            </h2>
        </div>
        <div id="wpvivid_backup_schedule_part"></div>
        <div class="postbox">
            <h2><span><?php esc_html_e('Troubleshooting', 'wpvivid-backuprestore'); ?></span></h2>
            <div class="inside">
                <table class="widefat" cellpadding="0">
                    <tbody>
                    <tr class="alternate">
                        <td class="row-title">
                            <?php esc_html_e('Read','wpvivid-backuprestore')?>
                            <a href="https://docs.wpvivid.com/troubleshooting-issues-wpvivid-backup-plugin.html" target="_blank"><?php esc_html_e('Troubleshooting page','wpvivid-backuprestore')?></a>
                            <?php esc_html_e(' for faster solutions.', 'wpvivid-backuprestore'); ?>
                        </td>
                    </tr>
                    <tr>
                        <td class="row-title">
                            <?php esc_html_e('Adjust','wpvivid-backuprestore')?>
                            <a href="https://docs.wpvivid.com/wpvivid-backup-free-advanced-settings.html" target="_blank"><?php esc_html_e('Adjust Advanced Settings','wpvivid-backuprestore')?></a>
                            <?php esc_html_e(' for higher task success rate.', 'wpvivid-backuprestore'); ?>
                        </td>
                    </tr>
                    </tbody>
                </table>
            </div>
        </div>
        <div class="postbox">
            <h2><span><?php esc_html_e('How-to', 'wpvivid-backuprestore'); ?></span></h2>
            <div class="inside">
                <table class="widefat" cellpadding="0">
                    <tbody>
                    <tr class="alternate"><td class="row-title"><a href="https://docs.wpvivid.com/wpvivid-backup-free-general-settings.html" target="_blank"><?php esc_html_e('WPvivid Backup Settings', 'wpvivid-backuprestore'); ?></a></td></tr>
                    <tr><td class="row-title"><a href="https://docs.wpvivid.com/get-started-create-manual-backup.html" target="_blank"><?php esc_html_e('Create a Manual Backup', 'wpvivid-backuprestore'); ?></a></td></tr>
                    <tr class="alternate"><td class="row-title"><a href="https://docs.wpvivid.com/get-started-restore-site.html" target="_blank"><?php esc_html_e('Restore Your Site from a Backup', 'wpvivid-backuprestore'); ?></a></td></tr>
                    <tr><td class="row-title"><a href="https://docs.wpvivid.com/get-started-transfer-site.html" target="_blank"><?php esc_html_e('Migrate WordPress', 'wpvivid-backuprestore'); ?></a></td></tr>
                    </tbody>
                </table>
            </div>
        </div>
        <?php
    }

    /**
     * Register the stylesheets for the admin area.
     *
     * 
     */
    public function enqueue_styles()
    {
        $this->screen_ids=apply_filters('wpvivid_get_screen_ids',$this->screen_ids);
        if(in_array(get_current_screen()->id,$this->screen_ids))
        {
            wp_enqueue_style($this->plugin_name, WPVIVID_PLUGIN_DIR_URL . 'css/wpvivid-admin.css', array(), $this->version, 'all');
            do_action('wpvivid_do_enqueue_styles');
        }
    }

    /**
     * Register the JavaScript for the admin area.
     *
     * 
     */
    public function enqueue_scripts()
    {
        $this->screen_ids=apply_filters('wpvivid_get_screen_ids',$this->screen_ids);

        if(in_array(get_current_screen()->id,$this->screen_ids))
        {
            wp_enqueue_script($this->plugin_name, WPVIVID_PLUGIN_DIR_URL . 'js/wpvivid-admin.js', array('jquery'), $this->version, false);
            wp_localize_script($this->plugin_name, 'wpvivid_ajax_object', array('ajax_url' => admin_url('admin-ajax.php'),'ajax_nonce'=>wp_create_nonce('wpvivid_ajax')));

            wp_localize_script($this->plugin_name, 'wpvividlion', array(
                'warning' => __('Warning:', 'wpvivid-backuprestore'),
                'error' => __('Error:', 'wpvivid-backuprestore'),
                'remotealias' => __('Warning: An alias for remote storage is required.', 'wpvivid-backuprestore'),
                'remoteexist' => __('Warning: The alias already exists in storage list.', 'wpvivid-backuprestore'),
                'backup_calc_timeout' => __('Calculating the size of files, folder and database timed out. If you continue to receive this error, please go to the plugin settings, uncheck \'Calculate the size of files, folder and database before backing up\', save changes, then try again.', 'wpvivid-backuprestore'),
                'restore_step1' => __('Step One: In the backup list, click the \'Restore\' button on the backup you want to restore. This will bring up the restore tab', 'wpvivid-backuprestore'),
                'restore_step2' => __('Step Two: Choose an option to complete restore, if any', 'wpvivid-backuprestore'),
                'restore_step3' => __('Step Three: Click \'Restore\' button', 'wpvivid-backuprestore'),
                'get_key_step1' => __('1. Visit Key tab page of WPvivid backup plugin of destination site.', 'wpvivid-backuprestore'),
                'get_key_step2' => __('2. Generate a key by clicking Generate button and copy it.', 'wpvivid-backuprestore'),
                'get_key_step3' => __('3. Go back to this page and paste the key in key box below. Lastly, click Save button.', 'wpvivid-backuprestore'),
            ));

            wp_enqueue_script('plupload-all');
            do_action('wpvivid_do_enqueue_scripts');
        }
    }

    /**
     * Register the administration menu for this plugin into the WordPress Dashboard menu.
     *
     * 
     */
    public function add_plugin_admin_menu()
    {

        /*
         * Add a settings page for this plugin to the Settings menu.
         *
         * NOTE:  Alternative menu locations are available via WordPress administration menu functions.
         *
         *        Administration Menus: http://codex.wordpress.org/Administration_Menus
         *
         */

        if(!is_multisite())
        {
            $active_plugins = get_option('active_plugins');
        }
        else
        {
            $active_plugins = array();
            //network active
            $mu_active_plugins = get_site_option( 'active_sitewide_plugins', array() );
            if(!empty($mu_active_plugins)){
                foreach ($mu_active_plugins as $plugin_name => $data){
                    $active_plugins[] = $plugin_name;
                }
            }
        }
        if(!function_exists('get_plugins'))
            require_once(ABSPATH . 'wp-admin/includes/plugin.php');
        $plugins=get_plugins();
        $pro_wpvivid_slug='wpvivid-backup-pro/wpvivid-backup-pro.php';
        $is_active_pro=false;
        if(!empty($plugins))
        {
            if(isset($plugins[$pro_wpvivid_slug]))
            {
                if(in_array($pro_wpvivid_slug, $active_plugins))
                {
                    $is_active_pro=true;
                }
            }
        }

        if(is_multisite() && !$is_active_pro)
        {
            $menu['page_title']= 'WPvivid Backup';
            $menu['menu_title']= 'WPvivid Backup';
            $menu['capability']='administrator';
            $menu['menu_slug']= $this->plugin_name;
            $menu['function']=array($this, 'display_plugin_setup_page_mu');
            $menu['icon_url']='dashicons-cloud';
            $menu['position']=100;
            $menu = apply_filters('wpvivid_get_main_admin_menus', $menu);
            add_menu_page( $menu['page_title'],$menu['menu_title'], $menu['capability'], $menu['menu_slug'], $menu['function'], $menu['icon_url'], $menu['position']);
        }
        else
        {
            $menu['page_title']= 'WPvivid Backup';
            $menu['menu_title']= 'WPvivid Backup';
            $menu['capability']='administrator';
            $menu['menu_slug']= $this->plugin_name;
            $menu['function']=array($this, 'display_plugin_setup_page');
            $menu['icon_url']='dashicons-cloud';
            $menu['position']=100;
            $menu = apply_filters('wpvivid_get_main_admin_menus', $menu);
            add_menu_page( $menu['page_title'],$menu['menu_title'], $menu['capability'], $menu['menu_slug'], $menu['function'], $menu['icon_url'], $menu['position']);
            $this->submenus = apply_filters('wpvivid_get_admin_menus', $this->submenus);
            usort($this->submenus, function ($a, $b) {
                if ($a['index'] == $b['index'])
                    return 0;

                if ($a['index'] > $b['index'])
                    return 1;
                else
                    return -1;
            });
            foreach ($this->submenus as $submenu) {
                add_submenu_page(
                    $submenu['parent_slug'],
                    $submenu['page_title'],
                    $submenu['menu_title'],
                    $submenu['capability'],
                    $submenu['menu_slug'],
                    $submenu['function']);
            }
        }
    }

    function add_toolbar_items($wp_admin_bar)
    {
        if (is_multisite())
        {
            $active_plugins = array();
            //network active
            $mu_active_plugins = get_site_option( 'active_sitewide_plugins', array() );
            if(!empty($mu_active_plugins)){
                foreach ($mu_active_plugins as $plugin_name => $data){
                    $active_plugins[] = $plugin_name;
                }
            }

            if(!function_exists('get_plugins'))
                require_once(ABSPATH . 'wp-admin/includes/plugin.php');
            $plugins=get_plugins();
            $pro_wpvivid_slug='wpvivid-backup-pro/wpvivid-backup-pro.php';
            $is_active_pro=false;
            if(!empty($plugins))
            {
                if(isset($plugins[$pro_wpvivid_slug]))
                {
                    if(in_array($pro_wpvivid_slug, $active_plugins))
                    {
                        $is_active_pro=true;
                    }
                }
            }

            if(!is_network_admin())
            {
                return ;
            }
            if(!$is_active_pro)
            {
                return ;
            }
        }
        
        global $wpvivid_plugin;
        if(is_admin())
        {
            $show_admin_bar = $this->get_admin_bar_setting();
            if ($show_admin_bar === true)
            {
                $show_toolbar=false;
                if(is_admin()&&current_user_can('manage_options'))
                {
                    $show_toolbar=true;
                }
                else
                {
                    $show_toolbar=apply_filters('wpvivid_current_user_show_toolbar', $show_toolbar, 'wpvivid-can-show-toolbar');
                }
                if($show_toolbar)
                {
                    $this->toolbar_menus = apply_filters('wpvivid_get_toolbar_menus', $this->toolbar_menus);
                    foreach ($this->toolbar_menus as $menu)
                    {
                        $wp_admin_bar->add_menu(array(
                            'id' => $menu['id'],
                            'title' => '<span class="dashicons-cloud ab-icon"></span>'.$menu['title']
                        ));
                        if (isset($menu['child']))
                        {
                            usort($menu['child'], function ($a, $b)
                            {
                                if($a['index']==$b['index'])
                                    return 0;

                                if($a['index']>$b['index'])
                                    return 1;
                                else
                                    return -1;
                            });
                            foreach ($menu['child'] as $child_menu) {
                                if(isset($child_menu['capability']) && current_user_can($child_menu['capability'])) {
                                    $wp_admin_bar->add_menu(array(
                                        'id' => $child_menu['id'],
                                        'parent' => $menu['id'],
                                        'title' => $child_menu['title'],
                                        'href' => $child_menu['href']
                                    ));
                                }
                            }
                        }
                    }
                }
            }
        }
    }

    public function get_admin_bar_setting()
    {
        return WPvivid_Setting::get_admin_bar_setting();
    }

    public function add_action_links( $links )
    {
        if(!is_multisite())
        {
            $active_plugins = get_option('active_plugins');
        }
        else
        {
            $active_plugins = array();
            //network active
            $mu_active_plugins = get_site_option( 'active_sitewide_plugins', array() );
            if(!empty($mu_active_plugins)){
                foreach ($mu_active_plugins as $plugin_name => $data){
                    $active_plugins[] = $plugin_name;
                }
            }
        }
        if(!function_exists('get_plugins'))
            require_once(ABSPATH . 'wp-admin/includes/plugin.php');
        $plugins=get_plugins();
        $pro_wpvivid_slug='wpvivid-backup-pro/wpvivid-backup-pro.php';
        $is_active_pro=false;
        if(!empty($plugins))
        {
            if(isset($plugins[$pro_wpvivid_slug]))
            {
                if(in_array($pro_wpvivid_slug, $active_plugins))
                {
                    $is_active_pro=true;
                }
            }
        }

        if($is_active_pro)
        {
            if(!is_multisite())
            {
                $settings_link = array(
                    '<a href="' . admin_url( 'admin.php?page=' . strtolower(sprintf('%s-dashboard', apply_filters('wpvivid_white_label_slug', 'wpvivid'))) ) . '">' . __('Settings', 'wpvivid-backuprestore') . '</a>',
                );
            }
            else
            {
                $settings_link = array(
                    '<a href="' . network_admin_url( 'admin.php?page=' . strtolower(sprintf('%s-dashboard', apply_filters('wpvivid_white_label_slug', 'wpvivid'))) ) . '">' . __('Settings', 'wpvivid-backuprestore') . '</a>',
                );
            }
        }
        else
        {
            if(!is_multisite())
            {
                $settings_link = array(
                    '<a href="' . admin_url( 'admin.php?page=' . apply_filters('wpvivid_white_label_slug', $this->plugin_name) ) . '">' . __('Settings', 'wpvivid-backuprestore') . '</a>',
                );
            }
            else
            {
                $settings_link = array(
                    '<a href="' . network_admin_url( 'admin.php?page=' . apply_filters('wpvivid_white_label_slug', $this->plugin_name) ) . '">' . __('Settings', 'wpvivid-backuprestore') . '</a>',
                );
            }
        }

        return array_merge(  $settings_link, $links );
    }

    public static function wpvivid_get_siteurl(){
        $wpvivid_siteurl = array();
        $wpvivid_siteurl['home_url'] = home_url();
        $wpvivid_siteurl['plug_url'] = plugins_url();
        $wpvivid_siteurl['site_url'] = get_option( 'siteurl' );
        return $wpvivid_siteurl;
    }

    /**
     * Render the settings page for this plugin.
     *
     * 
     */
    public function display_plugin_setup_page()
    {
        do_action('wpvivid_before_setup_page');

        add_action('wpvivid_display_page',array($this,'display'));

        do_action('wpvivid_display_page');
    }

    public function display_plugin_setup_page_mu()
    {
        ?>
        <div class="notice notice-info" style="margin: 10px; padding: 10px;">
            <span>The free version of WPvivid Backup Plugin does not support WordPress Multisite. You can consider upgrading to the <a href="https://wpvivid.com/pricing">pro version</a> as needed.</span>
        </div>
        <?php
    }

    public function migrate_notice()
    {
        $migrate_notice=false;
        $migrate_status=WPvivid_Setting::get_option('wpvivid_migrate_status');
        if(!empty($migrate_status) && $migrate_status == 'completed')
        {
            $migrate_notice=true;
            echo '<div class="notice notice-warning is-dismissible"><p>';
            esc_html_e('Migration is complete and htaccess file is replaced. In order to successfully complete the migration, you\'d better reinstall 301 redirect plugin, firewall and security plugin, and caching plugin if they exist.', 'wpvivid-backuprestore');
            echo '</p></div>';
            WPvivid_Setting::delete_option('wpvivid_migrate_status');
        }
        $restore = new WPvivid_restore_data();
        if ($restore->has_restore())
        {
            $restore_status = $restore->get_restore_status();
            if ($restore_status === WPVIVID_RESTORE_COMPLETED)
            {
                $restore->clean_restore_data();
                do_action('wpvivid_rebuild_backup_list');
                $need_review=WPvivid_Setting::get_option('wpvivid_need_review');
                if($need_review=='not')
                {
                    WPvivid_Setting::update_option('wpvivid_need_review','show');
                    $msg = esc_html(__('Cheers! WPvivid Backup plugin has restored successfully your website. If you found WPvivid Backup plugin helpful, a 5-star rating would be highly appreciated, which motivates us to keep providing new features.', 'wpvivid-backuprestore'));
                    WPvivid_Setting::update_option('wpvivid_review_msg',$msg);
                }
                else{
                    if(!$migrate_notice)
                    {
                        echo '<div class="notice notice-success is-dismissible"><p>'.esc_html_e('Restore completed successfully.', 'wpvivid-backuprestore').'</p></div>';
                    }
                }
            }
        }
    }

    public function display()
    {
        include_once('partials/wpvivid-admin-display.php');
    }

    public static function wpvivid_get_page_request()
    {
        $request_page='wpvivid_tab_general';

        if(isset($_REQUEST['wpvivid-remote-page-mainwp'])){
            $request_page='wpvivid_tab_remote_storage';
        }
        if(isset($_REQUEST['tab-backup']))
        {
            $request_page='wpvivid_tab_general';
        }
        else if(isset($_REQUEST['tab-schedule']))
        {
            $request_page='wpvivid_tab_schedule';
        }
        else if(isset($_REQUEST['tab-transfer']))
        {
            $request_page='wpvivid_tab_migrate';
        }
        else if(isset($_REQUEST['tab-remote-storage']))
        {
            $request_page='wpvivid_tab_remote_storage';
        }
        else if(isset($_REQUEST['tab-settings']))
        {
            $request_page='wpvivid_tab_setting';
        }
        else if(isset($_REQUEST['tab-website-info']))
        {
            $request_page='wpvivid_tab_debug';
        }
        else if(isset($_REQUEST['tab-logs']))
        {
            $request_page='wpvivid_tab_log';
        }
        else if(isset($_REQUEST['tab-key']))
        {
            $request_page='wpvivid_tab_key';
        }
        else if(isset($_REQUEST['tab-mainwp']))
        {
            $request_page='wpvivid_tab_mainwp';
        }
        else if(isset($_REQUEST['page'])&&$_REQUEST['page']=='wpvivid-pro')
        {
            $request_page='wpvivid_tab_pro';
        }
        else if(isset($_REQUEST['page'])&&$_REQUEST['page']=='wpvivid-setting')
        {
            $request_page='wpvivid_tab_setting';
        }

        $request_page=apply_filters('wpvivid_set_page_request',$request_page);

        return $request_page;
    }

    public static function show_add_my_review()
    {
        $review = WPvivid_Setting::get_option('wpvivid_need_review');
        $review_msg = WPvivid_Setting::get_option('wpvivid_review_msg');
        if (empty($review))
        {
            WPvivid_Setting::update_option('wpvivid_need_review', 'not');
        } else {
            if ($review == 'not')
            {
            }
            else if ($review == 'show')
            {
                if(!empty($review_msg))
                {
                    echo '<div class="notice notice-info notice-rate is-dismissible" id="wpvivid_notice_rate">
                    <p>' . esc_html($review_msg) . '</p>
                    <div style="padding-bottom: 10px;">
                    <span><input type="button" class="button-primary" option="review" name="rate-now" value="'.esc_attr__('Rate Us', 'wpvivid-backuprestore').'" /></span>
                    <span><input type="button" class="button-secondary" option="review" name="ask-later" value="'.esc_attr__('Maybe Later', 'wpvivid-backuprestore').'" /></span>
                    <span><input type="button" class="button-secondary" option="review" name="already-done" value="'.esc_attr__('Already Done', 'wpvivid-backuprestore').'" /></span>
                    <span><input type="button" class="button-secondary" option="review" name="never-ask" value="'.esc_attr__('Never', 'wpvivid-backuprestore').'" /></span>
                    </div>
                    </div>';
                }
            } else if ($review == 'do_not_ask')
            {
            } else
                {
                if (time() > $review)
                {
                    if(!empty($review_msg))
                    {
                        echo '<div class="notice notice-info notice-rate is-dismissible" id="wpvivid_notice_rate">
                        <p>' . esc_html($review_msg) . '</p>
                        <div style="padding-bottom: 10px;">
                        <span><input type="button" class="button-primary" option="review" name="rate-now" value="'.esc_attr__('Rate Us', 'wpvivid-backuprestore').'" /></span>    
                        <span><input type="button" class="button-secondary" option="review" name="ask-later" value="'.esc_attr__('Maybe Later', 'wpvivid-backuprestore').'" /></span>
                        <span><input type="button" class="button-secondary" option="review" name="already-done" value="'.esc_attr__('Already Done', 'wpvivid-backuprestore').'" /></span>
                        <span><input type="button" class="button-secondary" option="review" name="never-ask" value="'.esc_attr__('Never', 'wpvivid-backuprestore').'" /></span>
                        </div>
                        </div>';
                    }
                }
            }
        }
    }

    public function check_amazons3()
    {
        $remoteslist=WPvivid_Setting::get_all_remote_options();
        $need_amazons3_notice = false;
        if(isset($remoteslist) && !empty($remoteslist))
        {
            foreach ($remoteslist as $remote_id => $value)
            {
                if($remote_id === 'remote_selected')
                {
                    continue;
                }
                if($value['type'] == 'amazons3' && isset($value['s3Path']))
                {
                    $need_amazons3_notice = true;
                }
                if($value['type'] == 's3compat' && isset($value['s3directory']))
                {
                    $need_amazons3_notice = true;
                }
            }
        }
        if($need_amazons3_notice)
        {
            $amazons3_notice = WPvivid_Setting::get_option('wpvivid_amazons3_notice', 'not init');
            if($amazons3_notice === 'not init')
            {
                $notice_message = __('As Amazon S3 and DigitalOcean Space have upgraded their connection methods, please delete the previous connections and re-add your Amazon S3/DigitalOcean Space accounts to make sure the connections work.', 'wpvivid-backuprestore');
                echo '<div class="notice notice-warning" id="wpvivid_amazons3_notice">
                        <p>' . esc_html($notice_message) . '</p>
                        <div style="padding-bottom: 10px;">
                        <span><input type="button" class="button-secondary" value="I Understand" onclick="wpvivid_click_amazons3_notice();" /></span>
                        </div>
                        </div>';
            }
        }
    }

    public function check_dropbox()
    {
        if (is_multisite())
        {
            if(!is_network_admin())
            {
                return ;
            }
        }

        if(!function_exists('get_plugins'))
            require_once(ABSPATH . 'wp-admin/includes/plugin.php');
        $pro_wpvivid_slug='wpvivid-backup-pro/wpvivid-backup-pro.php';
        if (is_multisite())
        {
            $active_plugins = array();
            //network active
            $mu_active_plugins = get_site_option( 'active_sitewide_plugins', array() );
            if(!empty($mu_active_plugins)){
                foreach ($mu_active_plugins as $plugin_name => $data){
                    $active_plugins[] = $plugin_name;
                }
            }
            $plugins=get_mu_plugins();
            if(count($plugins) == 0 || !isset($plugins[$pro_wpvivid_slug])){
                $plugins=get_plugins();
            }
        }
        else
        {
            $active_plugins = get_option('active_plugins');
            $plugins=get_plugins();
        }

        if(!empty($plugins))
        {
            if(isset($plugins[$pro_wpvivid_slug]))
            {
                if(in_array($pro_wpvivid_slug, $active_plugins))
                {
                    return;
                }
            }

            $remoteslist=WPvivid_Setting::get_all_remote_options();
            $need_dropbox_notice = false;
            if(isset($remoteslist) && !empty($remoteslist))
            {
                foreach ($remoteslist as $remote_id => $value)
                {
                    if($remote_id === 'remote_selected')
                    {
                        continue;
                    }
                    if($value['type'] == 'dropbox' && !isset($value['refresh_token']))
                    {
                        $need_dropbox_notice = true;
                    }
                }
            }
            if($need_dropbox_notice)
            {
                $notice_message = __('Because Dropbox has upgraded their API on September 30, 2021, the new API is no longer compatible with the previous app\'s settings. Please re-add your Dropbox storage to ensure that it works properly.', 'wpvivid-backuprestore');
                echo '<div class="notice notice-warning">
                                    <p>' . esc_html($notice_message) . '</p>
                               </div>';
            }
        }
    }

    public function check_extensions()
    {
        $common_setting = WPvivid_Setting::get_setting(false, 'wpvivid_common_setting');
        $db_connect_method = isset($common_setting['options']['wpvivid_common_setting']['db_connect_method']) ? $common_setting['options']['wpvivid_common_setting']['db_connect_method'] : 'wpdb';
        $need_php_extensions = array();
        $need_extensions_count = 0;
        $extensions=get_loaded_extensions();
        if(!function_exists("curl_init")){
            $need_php_extensions[$need_extensions_count] = 'curl';
            $need_extensions_count++;
        }
        if(!class_exists('PDO')){
            $need_php_extensions[$need_extensions_count] = 'PDO';
            $need_extensions_count++;
        }
        if(!function_exists("gzopen"))
        {
            $need_php_extensions[$need_extensions_count] = 'zlib';
            $need_extensions_count++;
        }
        if(!array_search('pdo_mysql',$extensions) && $db_connect_method === 'pdo')
        {
            $need_php_extensions[$need_extensions_count] = 'pdo_mysql';
            $need_extensions_count++;
        }
        if(!empty($need_php_extensions)){
            $msg = '';
            $figure = 0;
            foreach ($need_php_extensions as $extension){
                $figure++;
                if($figure == 1){
                    $msg .= $extension;
                }
                else if($figure < $need_extensions_count) {
                    $msg .= ', '.$extension;
                }
                else if($figure == $need_extensions_count){
                    $msg .= ' and '.$extension;
                }
            }
            if($figure == 1){
                echo '<div class="notice notice-error"><p>';
                echo sprintf('The %s extension is not detected. Please install the extension first.', esc_html($msg));
                echo '</p></div>';
            }
            else{
                echo '<div class="notice notice-error"><p>';
                echo sprintf('The %s extensions are not detected. Please install the extensions first.', esc_html($msg));
                echo '</p></div>';
            }
        }

        if (!class_exists('PclZip')) include_once(ABSPATH.'/wp-admin/includes/class-pclzip.php');
        if (!class_exists('PclZip'))
        {
            echo '<div class="notice notice-error"><p>';
            esc_html_e('Class PclZip is not detected. Please update or reinstall your WordPress.', 'wpvivid-backuprestore');
            echo '</p></div>';
        }

        $hide_notice = get_option('wpvivid_hide_wp_cron_notice', false);
        if(defined( 'DISABLE_WP_CRON' ) && DISABLE_WP_CRON && $hide_notice === false){
            echo '<div class="notice notice-error notice-wp-cron is-dismissible"><p>';
            esc_html_e('In order to execute the scheduled backups properly, please set the DISABLE_WP_CRON constant to false. If you are using an external cron system, simply click \'X\' to dismiss this message.', 'wpvivid-backuprestore');
            echo '</p></div>';
        }
    }

    public function check_wpvivid_pro_version()
    {
        if (is_multisite())
        {
            if(!is_network_admin())
            {
                return ;
            }
        }

        if(!function_exists('get_plugins'))
            require_once(ABSPATH . 'wp-admin/includes/plugin.php');
        $pro_wpvivid_slug='wpvivid-backup-pro/wpvivid-backup-pro.php';
        if (is_multisite())
        {
            $active_plugins = array();
            //network active
            $mu_active_plugins = get_site_option( 'active_sitewide_plugins', array() );
            if(!empty($mu_active_plugins)){
                foreach ($mu_active_plugins as $plugin_name => $data){
                    $active_plugins[] = $plugin_name;
                }
            }
            $plugins=get_mu_plugins();
            if(count($plugins) == 0 || !isset($plugins[$pro_wpvivid_slug])){
                $plugins=get_plugins();
            }
        }
        else
        {
            $active_plugins = get_option('active_plugins');
            $plugins=get_plugins();
        }

        if(!empty($plugins))
        {
            if(isset($plugins[$pro_wpvivid_slug]))
            {
                if(in_array($pro_wpvivid_slug, $active_plugins))
                {
                    if(version_compare('2.0.23',$plugins[$pro_wpvivid_slug]['Version'],'>'))
                    {
                        ?>
                        <div class="notice notice-warning" style="padding: 11px 15px;">
                            <?php echo sprintf('We detected that you are using a lower version of %s Pro, please update it to 2.0.23 or higher to ensure backing up to Google Drive works properly.', 'wpvivid-backuprestore'), esc_html(apply_filters('wpvivid_white_label_display', 'WPvivid Backup Plugin')); ?>
                        </div>
                        <?php
                    }
                }
            }
        }
    }

    public function check_wpvivid_free_htaccess_rule()
    {
        $is_check = get_option('wpvivid_check_htaccess_rule_free', false);
        if(!$is_check)
        {
            $dir = WPvivid_Setting::get_option('wpvivid_local_setting');
            if (!isset($dir['path']))
            {
                $dir = WPvivid_Setting::set_default_local_option();
            }

            $wpvivid_backup_dir_htaccess = WP_CONTENT_DIR.DIRECTORY_SEPARATOR.$dir['path'].DIRECTORY_SEPARATOR.'.htaccess';
            $wpvivid_backup_log_htaccess = WP_CONTENT_DIR.DIRECTORY_SEPARATOR.$dir['path'].DIRECTORY_SEPARATOR.'wpvivid_log'.DIRECTORY_SEPARATOR.'.htaccess';
            $wpvivid_backup_error_log_htaccess = WP_CONTENT_DIR.DIRECTORY_SEPARATOR.$dir['path'].DIRECTORY_SEPARATOR.'wpvivid_log'.DIRECTORY_SEPARATOR.'error'.'/.htaccess';
            $wpvivid_staging_dir_htaccess = WP_CONTENT_DIR.DIRECTORY_SEPARATOR.'wpvivid_staging'.DIRECTORY_SEPARATOR.'.htaccess';
            $wpvivid_staging_error_log_htaccess = WP_CONTENT_DIR.DIRECTORY_SEPARATOR.'wpvivid_staging'.DIRECTORY_SEPARATOR.'error'.'/.htaccess';

            if(file_exists($wpvivid_backup_dir_htaccess))
            {
                wp_delete_file($wpvivid_backup_dir_htaccess);
                WPvivid_Setting::wpvivid_write_htaccess_rule($wpvivid_backup_dir_htaccess);
            }

            if(file_exists($wpvivid_backup_log_htaccess))
            {
                wp_delete_file($wpvivid_backup_log_htaccess);
                WPvivid_Setting::wpvivid_write_htaccess_rule($wpvivid_backup_log_htaccess);
            }

            if(file_exists($wpvivid_backup_error_log_htaccess))
            {
                wp_delete_file($wpvivid_backup_error_log_htaccess);
                WPvivid_Setting::wpvivid_write_htaccess_rule($wpvivid_backup_error_log_htaccess);
            }

            if(file_exists($wpvivid_staging_dir_htaccess))
            {
                wp_delete_file($wpvivid_staging_dir_htaccess);
                WPvivid_Setting::wpvivid_write_htaccess_rule($wpvivid_staging_dir_htaccess);
            }

            if(file_exists($wpvivid_staging_error_log_htaccess))
            {
                wp_delete_file($wpvivid_staging_error_log_htaccess);
                WPvivid_Setting::wpvivid_write_htaccess_rule($wpvivid_staging_error_log_htaccess);
            }

            update_option('wpvivid_check_htaccess_rule_free', true, 'no');
        }
    }

    public function init_js_var()
    {
        global $wpvivid_plugin;

        $loglist=$wpvivid_plugin->get_log_list_ex();
        $remoteslist=WPvivid_Setting::get_all_remote_options();
        $default_remote_storage='';
        foreach ($remoteslist['remote_selected'] as $value)
        {
            $default_remote_storage=$value;
        }
        ?>
        <script>
            var wpvivid_siteurl = '<?php
                $wpvivid_siteurl = array();
                $wpvivid_siteurl=WPvivid_Admin::wpvivid_get_siteurl();
                echo esc_url($wpvivid_siteurl['site_url']);
                ?>';
            var wpvivid_plugurl =  '<?php
                echo esc_url(WPVIVID_PLUGIN_URL);
                ?>';
            var wpvivid_log_count = '<?php
                echo esc_attr(sizeof($loglist['log_list']['file']));
                ?>';
            var wpvivid_log_array = '<?php
                echo wp_json_encode($loglist);
                ?>';
            var wpvivid_page_request = '<?php
                $page_request = WPvivid_Admin::wpvivid_get_page_request();
                echo esc_attr($page_request);
                ?>';
            var wpvivid_default_remote_storage = '<?php
                echo esc_attr($default_remote_storage);
                ?>';
        </script>
        <?php
    }

    public function wpvivid_add_default_tab_page($page_array){
        $page_array['backup_restore'] = array('index' => '1', 'tab_func' => array($this, 'wpvivid_add_tab_backup_restore'), 'page_func' => array($this, 'wpvivid_add_page_backup'));
        $page_array['schedule'] = array('index' => '2', 'tab_func' => array($this, 'wpvivid_add_tab_schedule'), 'page_func' => array($this, 'wpvivid_add_page_schedule'));
        $page_array['remote_storage'] = array('index' => '4', 'tab_func' => array($this, 'wpvivid_add_tab_remote_storage'), 'page_func' => array($this, 'wpvivid_add_page_remote_storage'));
        $page_array['setting'] = array('index' => '5', 'tab_func' => array($this, 'wpvivid_add_tab_setting'), 'page_func' => array($this, 'wpvivid_add_page_setting'));
        $page_array['website_info'] = array('index' => '6', 'tab_func' => array($this, 'wpvivid_add_tab_website_info'), 'page_func' => array($this, 'wpvivid_add_page_website_info'));
        $page_array['log'] = array('index' => '7', 'tab_func' => array($this, 'wpvivid_add_tab_log_ex'), 'page_func' => array($this, 'wpvivid_add_page_log_ex'));
        $page_array['read_log'] = array('index' => '29', 'tab_func' => array($this, 'wpvivid_add_tab_read_log'), 'page_func' => array($this, 'wpvivid_add_page_read_log'));
        $page_array['premium'] = array('index' => '10', 'tab_func' => array($this, 'wpvivid_add_tab_premium'), 'page_func' => array($this, 'wpvivid_add_page_premium'));
        $hide_mwp_tab_page = get_option('wpvivid_hide_mwp_tab_page_v1', false);
        if($hide_mwp_tab_page === false) {
            $page_array['mwp'] = array('index' => '30', 'tab_func' => array($this, 'wpvivid_add_tab_mwp'), 'page_func' => array($this, 'wpvivid_add_page_mwp'));
        }
        return $page_array;
    }

    public function wpvivid_add_tab_backup_restore(){
        ?>
        <a href="#" id="wpvivid_tab_general" class="nav-tab wrap-nav-tab nav-tab-active" onclick="switchTabs(event,'general-page')"><?php esc_html_e('Backup & Restore', 'wpvivid-backuprestore'); ?></a>
        <?php
    }

    public function wpvivid_add_tab_schedule(){
        ?>
        <a href="#" id="wpvivid_tab_schedule" class="nav-tab wrap-nav-tab" onclick="switchTabs(event,'schedule-page')"><?php esc_html_e('Schedule', 'wpvivid-backuprestore'); ?></a>
        <?php
    }

    public function wpvivid_add_tab_remote_storage(){
        ?>
        <a href="#" id="wpvivid_tab_remote_storage" class="nav-tab wrap-nav-tab" onclick="switchTabs(event,'storage-page')"><?php esc_html_e('Remote Storage', 'wpvivid-backuprestore'); ?></a>
        <?php
    }

    public function wpvivid_add_tab_setting(){
        ?>
        <a href="#" id="wpvivid_tab_setting" class="nav-tab wrap-nav-tab" onclick="switchTabs(event,'settings-page')"><?php esc_html_e('Settings', 'wpvivid-backuprestore'); ?></a>
        <?php
    }

    public function wpvivid_add_tab_website_info(){
        ?>
        <a href="#" id="wpvivid_tab_debug" class="nav-tab wrap-nav-tab" onclick="switchTabs(event,'debug-page')"><?php esc_html_e('Debug', 'wpvivid-backuprestore'); ?></a>
        <?php
    }

    public function wpvivid_add_tab_log(){
        ?>
        <a href="#" id="wpvivid_tab_log" class="nav-tab log-nav-tab nav-tab-active" onclick="switchlogTabs(event,'logs-page')"><?php esc_html_e('Backup Logs', 'wpvivid-backuprestore'); ?></a>
        <?php
    }

    public function wpvivid_add_tab_read_log(){
        ?>
        <a href="#" id="wpvivid_tab_read_log" class="nav-tab wrap-nav-tab delete" onclick="switchTabs(event,'log-read-page')" style="display: none;">
            <div style="margin-right: 15px;"><?php esc_html_e('Log', 'wpvivid-backuprestore'); ?></div>
            <div class="nav-tab-delete-img">
                <img src="<?php echo esc_url( WPVIVID_PLUGIN_URL.'/admin/partials/images/delete-tab.png' ); ?>" style="vertical-align:middle; cursor:pointer;" onclick="wpvivid_close_tab(event, 'wpvivid_tab_read_log', 'wrap', 'wpvivid_tab_log');" />
            </div>
        </a>
        <?php
    }

    public function wpvivid_add_tab_mwp(){
        ?>
        <a href="#" id="wpvivid_tab_mainwp" class="nav-tab wrap-nav-tab delete" onclick="switchTabs(event, 'mwp-page')">
            <div style="margin-right: 15px;"><?php esc_html_e('MainWP', 'wpvivid-backuprestore'); ?></div>
            <div class="nav-tab-delete-img">
                <img src="<?php echo esc_url(WPVIVID_PLUGIN_URL.'/admin/partials/images/delete-tab.png'); ?>" style="vertical-align:middle; cursor:pointer;" onclick="wpvivid_close_tab(event, 'wpvivid_tab_mainwp', 'wrap', 'wpvivid_tab_general');" />
            </div>
        </a>
        <?php
    }

    public function wpvivid_add_tab_premium(){
        ?>
        <a href="#" id="wpvivid_tab_premium" class="nav-tab wrap-nav-tab" onclick="switchTabs(event,'premium-page')"><?php esc_html_e('Premium', 'wpvivid-backuprestore'); ?></a>
        <?php
    }

    public function wpvivid_add_page_backup()
    {
        ?>
        <div id="general-page" class="wrap-tab-content wpvivid_tab_general" name="tab-backup" style="width:100%;">
            <div class="meta-box-sortables ui-sortable">
                <?php
                do_action('wpvivid_backuppage_add_module');
                ?>
                <h2 class="nav-tab-wrapper" id="wpvivid_backup_tab" style="padding-bottom:0!important;">
                <?php
                $backuplist_array = array();
                $backuplist_array = apply_filters('wpvivid_backuppage_load_backuplist', $backuplist_array);
                foreach ($backuplist_array as $list_name) {
                    add_action('wpvivid_backuppage_add_tab', $list_name['tab_func'], $list_name['index']);
                    add_action('wpvivid_backuppage_add_page', $list_name['page_func'], $list_name['index']);
                }
                do_action('wpvivid_backuppage_add_tab');
                ?>
                </h2>
                <?php  do_action('wpvivid_backuppage_add_page'); ?>
            </div>
        </div>
        <script>
            <?php do_action('wpvivid_backup_do_js'); ?>
        </script>
        <?php
    }

    public function wpvivid_add_page_schedule()
    {
        ?>
        <div id="schedule-page" class="wrap-tab-content wpvivid_tab_schedule" name="tab-schedule" style="display: none;">
            <div>
                <table class="widefat">
                    <tbody>
                    <?php do_action('wpvivid_schedule_add_cell'); ?>
                    <tfoot>
                    <tr>
                        <th class="row-title"><input class="button-primary storage-account-button" id="wpvivid_schedule_save" type="submit" name="" value="<?php esc_attr_e( 'Save Changes', 'wpvivid-backuprestore' ); ?>" /></th>
                        <th></th>
                    </tr>
                    </tfoot>
                    </tbody>
                </table>
            </div>
        </div>
        <script>
            jQuery('#wpvivid_schedule_save').click(function(){
                wpvivid_set_schedule();
                wpvivid_settings_changed = false;
            });

            function wpvivid_set_schedule()
            {
                var schedule_data = wpvivid_ajax_data_transfer('schedule');
                var ajax_data = {
                    'action': 'wpvivid_set_schedule',
                    'schedule': schedule_data
                };
                jQuery('#wpvivid_schedule_save').css({'pointer-events': 'none', 'opacity': '0.4'});
                wpvivid_post_request(ajax_data, function (data) {
                    try {
                        var jsonarray = jQuery.parseJSON(data);

                        jQuery('#wpvivid_schedule_save').css({'pointer-events': 'auto', 'opacity': '1'});
                        if (jsonarray.result === 'success') {
                            location.reload();
                        }
                        else {
                            alert(jsonarray.error);
                        }
                    }
                    catch (err) {
                        alert(err);
                        jQuery('#wpvivid_schedule_save').css({'pointer-events': 'auto', 'opacity': '1'});
                    }
                }, function (XMLHttpRequest, textStatus, errorThrown) {
                    jQuery('#wpvivid_schedule_save').css({'pointer-events': 'auto', 'opacity': '1'});
                    var error_message = wpvivid_output_ajaxerror('changing schedule', textStatus, errorThrown);
                    alert(error_message);
                });
            }
        </script>
        <?php
    }

    public function wpvivid_add_page_remote_storage()
    {
        ?>
        <div id="storage-page" class="wrap-tab-content wpvivid_tab_remote_storage" name="tab-storage" style="display:none;">
            <div>
                <div class="storage-content" id="storage-brand-2" style="">
                    <div class="postbox">
                        <?php do_action('wpvivid_add_storage_tab'); ?>
                    </div>
                    <div class="postbox storage-account-block" id="wpvivid_storage_account_block">
                        <?php do_action('wpvivid_add_storage_page'); ?>
                    </div>
                    <h2 class="nav-tab-wrapper" style="padding-bottom:0!important;">
                        <?php do_action('wpvivid_storage_add_tab'); ?>
                    </h2>
                    <?php do_action('wpvivid_storage_add_page'); ?>
                </div>
            </div>
        </div>
        <?php
    }

    public function wpvivid_add_page_setting()
    {
        ?>
        <div id="settings-page" class="wrap-tab-content wpvivid_tab_setting" name="tab-setting" style="display:none;">
            <div>
                <h2 class="nav-tab-wrapper" style="padding-bottom:0!important;">
                    <?php
                    $setting_array = array();
                    $setting_array = apply_filters('wpvivid_add_setting_tab_page', $setting_array);
                    foreach ($setting_array as $setting_name) {
                        add_action('wpvivid_settingpage_add_tab', $setting_name['tab_func'], $setting_name['index']);
                        add_action('wpvivid_settingpage_add_page', $setting_name['page_func'], $setting_name['index']);
                    }
                    do_action('wpvivid_settingpage_add_tab');
                    ?>
                </h2>
                <?php do_action('wpvivid_settingpage_add_page'); ?>
                <div><input class="button-primary" id="wpvivid_setting_general_save" type="submit" value="<?php esc_attr_e( 'Save Changes', 'wpvivid-backuprestore' ); ?>" /></div>
            </div>
        </div>
        <script>
            jQuery('#wpvivid_setting_general_save').click(function(){
                wpvivid_set_general_settings();
                wpvivid_settings_changed = false;
            });

            function wpvivid_set_general_settings()
            {
                var setting_data = wpvivid_ajax_data_transfer('setting');
                var ajax_data = {
                    'action': 'wpvivid_set_general_setting',
                    'setting': setting_data
                };
                jQuery('#wpvivid_setting_general_save').css({'pointer-events': 'none', 'opacity': '0.4'});
                wpvivid_post_request(ajax_data, function (data) {
                    try {
                        var jsonarray = jQuery.parseJSON(data);

                        jQuery('#wpvivid_setting_general_save').css({'pointer-events': 'auto', 'opacity': '1'});
                        if (jsonarray.result === 'success') {
                            location.reload();
                        }
                        else {
                            alert(jsonarray.error);
                        }
                    }
                    catch (err) {
                        alert(err);
                        jQuery('#wpvivid_setting_general_save').css({'pointer-events': 'auto', 'opacity': '1'});
                    }
                }, function (XMLHttpRequest, textStatus, errorThrown) {
                    jQuery('#wpvivid_setting_general_save').css({'pointer-events': 'auto', 'opacity': '1'});
                    var error_message = wpvivid_output_ajaxerror('changing base settings', textStatus, errorThrown);
                    alert(error_message);
                });
            }
        </script>
        <?php
    }

    public function wpvivid_add_tab_log_ex(){
        ?>
        <a href="#" id="wpvivid_tab_log_ex" class="nav-tab wrap-nav-tab" onclick="switchTabs(event,'logs-page-ex')"><?php esc_html_e('Logs', 'wpvivid-backuprestore'); ?></a>
        <?php
    }

    public function wpvivid_add_page_log_ex()
    {
        ?>
         <div id="logs-page-ex" class="wrap-tab-content wpvivid_tab_log" name="tab-logs" style="display:none;">
             <div>
                 <h2 class="nav-tab-wrapper" style="padding-bottom:0!important;">
                     <?php
                     $setting_array = array();
                     $setting_array = apply_filters('wpvivid_add_log_tab_page', $setting_array);
                     foreach ($setting_array as $setting_name) {
                         add_action('wpvivid_logpage_add_tab', $setting_name['tab_func'], $setting_name['index']);
                         add_action('wpvivid_logpage_add_page', $setting_name['page_func'], $setting_name['index']);
                     }
                     do_action('wpvivid_logpage_add_tab');
                     ?>
                 </h2>
                 <?php do_action('wpvivid_logpage_add_page'); ?>
             </div>
         </div>
        <?php
    }

    public function wpvivid_add_page_website_info()
    {
        ?>
        <div id="debug-page" class="wrap-tab-content wpvivid_tab_debug" name="tab-debug" style="display:none;">
            <table class="widefat">
                <div style="padding: 0 0 20px 10px;"><?php esc_html_e('There are two ways available to send us the debug information. The first one is recommended.', 'wpvivid-backuprestore'); ?></div>
                <div style="padding-left: 10px;">
                    <strong><?php esc_html_e('Method 1.', 'wpvivid-backuprestore'); ?></strong> <?php esc_html_e('If you have configured SMTP on your site, enter your email address and click the button below to send us the relevant information (website info and errors logs) when you are encountering errors. This will help us figure out what happened. Once the issue is resolved, we will inform you by your email address.', 'wpvivid-backuprestore'); ?>
                </div>
                <div style="padding:10px 10px 0">
                    <span class="wpvivid-element-space-right"><?php esc_html_e('WPvivid support email:', 'wpvivid-backuprestore'); ?></span><input type="text" id="wpvivid_support_mail" value="support@wpvivid.com" readonly />
                    <span class="wpvivid-element-space-right"><?php esc_html_e('Your email:', 'wpvivid-backuprestore'); ?></span><input type="text" id="wpvivid_user_mail" />
                </div>
                <div style="padding:10px 10px 0">
                    <div style="float: left;">
                        <div class="wpvivid-element-space-bottom wpvivid-text-space-right wpvivid-debug-text-fix" style="float: left;">
                            <?php esc_html_e('I am using:', 'wpvivid-backuprestore'); ?>
                        </div>
                        <div class="wpvivid-element-space-bottom wpvivid-text-space-right" style="float: left;">
                            <select id="wpvivid_debug_type">
                                <option selected="selected" value="sharehost"><?php esc_html_e('share hosting', 'wpvivid-backuprestore'); ?></option>
                                <option value="vps"><?php esc_html_e('VPS hosting', 'wpvivid-backuprestore'); ?></option>
                            </select>
                        </div>
                        <div style="clear: both;"></div>
                    </div>
                    <div id="wpvivid_debug_host" style="float: left;">
                        <div class="wpvivid-element-space-bottom wpvivid-text-space-right wpvivid-debug-text-fix" style="float: left;">
                            <?php esc_html_e('My web hosting provider is:', 'wpvivid-backuprestore'); ?>
                        </div>
                        <div class="wpvivid-element-space-bottom wpvivid-text-space-right" style="float: left;">
                            <input type="text" id="wpvivid_host_provider"/></div>
                        <div style="clear: both;"></div>
                    </div>
                    <div style="clear: both;"></div>
                </div>
                <div style="padding:0 10px;">
                    <textarea id="wpvivid_debug_comment" class="wp-editor-area" style="width:100%; height: 200px;" autocomplete="off" cols="60" placeholder="<?php esc_attr_e('Please describe your problem here.', 'wpvivid-backuprestore'); ?>" ></textarea>
                </div>
                <div class="schedule-tab-block">
                    <input class="button-primary" type="submit" value="<?php esc_attr_e( 'Send Debug Information to Us', 'wpvivid-backuprestore' ); ?>" onclick="wpvivid_click_send_debug_info();" />
                </div>
                <div style="clear:both;"></div>
                <div style="padding-left: 10px;">
                    <strong><?php esc_html_e('Method 2.', 'wpvivid-backuprestore'); ?></strong> <?php esc_html_e('If you didn’t configure SMTP on your site, click the button below to download the relevant information (website info and error logs) to your PC when you are encountering some errors. Sending the files to us will help us diagnose what happened.', 'wpvivid-backuprestore'); ?>
                </div>
                <div class="schedule-tab-block">
                    <input class="button-primary" id="wpvivid_download_website_info" type="submit" name="download-website-info" value="<?php esc_attr_e( 'Download', 'wpvivid-backuprestore' ); ?>" />
                </div>
                <thead class="website-info-head">
                <tr>
                    <th class="row-title" style="min-width: 260px;"><?php esc_html_e( 'Website Info Key', 'wpvivid-backuprestore' ); ?></th>
                    <th><?php esc_html_e( 'Website Info Value', 'wpvivid-backuprestore' ); ?></th>
                </tr>
                </thead>
                <tbody class="wpvivid-websiteinfo-list" id="wpvivid_websiteinfo_list">
                <?php
                global $wpvivid_plugin;
                $website_info=$wpvivid_plugin->get_website_info();
                if(!empty($website_info['data'])){
                    foreach ($website_info['data'] as $key=>$value) { ?>
                        <?php
                        $website_value='';
                        if (is_array($value)) {
                            foreach ($value as $arr_value) {
                                if (empty($website_value)) {
                                    $website_value = $website_value . $arr_value;
                                } else {
                                    $website_value = $website_value . ', ' . $arr_value;
                                }
                            }
                        }
                        else{
                            if($value === true || $value === false){
                                if($value === true) {
                                    $website_value = 'true';
                                }
                                else{
                                    $website_value = 'false';
                                }
                            }
                            else {
                                $website_value = $value;
                            }
                        }
                        ?>
                        <tr>
                            <td class="row-title tablelistcolumn"><label for="tablecell"><?php echo esc_html($key); ?></label></td>
                            <td class="tablelistcolumn"><?php echo esc_html($website_value); ?></td>
                        </tr>
                    <?php }} ?>
                </tbody>
            </table>
        </div>
        <script>
            jQuery('#wpvivid_download_website_info').click(function(){
                wpvivid_download_website_info();
            });

            /**
             * Download the relevant website info and error logs to your PC for debugging purposes.
             */
            function wpvivid_download_website_info(){
                wpvivid_location_href=true;
                location.href =ajaxurl+'?_wpnonce='+wpvivid_ajax_object.ajax_nonce+'&action=wpvivid_create_debug_package';
            }

            jQuery("#wpvivid_debug_type").change(function()
            {
                if(jQuery(this).val()=='sharehost')
                {
                    jQuery("#wpvivid_debug_host").show();
                }
                else
                {
                    jQuery("#wpvivid_debug_host").hide();
                }
            });

            function wpvivid_click_send_debug_info(){
                var wpvivid_user_mail = jQuery('#wpvivid_user_mail').val();
                var server_type = jQuery('#wpvivid_debug_type').val();
                var host_provider = jQuery('#wpvivid_host_provider').val();
                var comment = jQuery('#wpvivid_debug_comment').val();

                var ajax_data = {
                    'action': 'wpvivid_send_debug_info',
                    'user_mail': wpvivid_user_mail,
                    'server_type':server_type,
                    'host_provider':host_provider,
                    'comment':comment
                };
                wpvivid_post_request(ajax_data, function (data) {
                    try {
                        var jsonarray = jQuery.parseJSON(data);
                        if (jsonarray.result === "success") {
                            alert("<?php esc_html_e('Send succeeded.', 'wpvivid-backuprestore'); ?>");
                        }
                        else {
                            alert(jsonarray.error);
                        }
                    }
                    catch (err) {
                        alert(err);
                    }
                }, function (XMLHttpRequest, textStatus, errorThrown) {
                    var error_message = wpvivid_output_ajaxerror('sending debug information', textStatus, errorThrown);
                    alert(error_message);
                });
            }
        </script>
        <?php
    }

    public function wpvivid_add_page_log()
    {
        global $wpvivid_plugin;
        $display_log_count=array(0=>"10",1=>"20",2=>"30",3=>"40",4=>"50");
        $max_log_diaplay=20;
        $loglist=$wpvivid_plugin->get_log_list_ex();
        ?>
        <div id="logs-page" class="log-tab-content wpvivid_tab_log" name="tab-logs">
            <table class="wp-list-table widefat plugins">
                <thead class="log-head">
                <tr>
                    <th class="row-title"><?php esc_html_e( 'Date', 'wpvivid-backuprestore' ); ?></th>
                    <th><?php esc_html_e( 'Log Type', 'wpvivid-backuprestore' ); ?></th>
                    <th><?php esc_html_e( 'Log File Name', 'wpvivid-backuprestore' ); ?></th>
                    <th><?php esc_html_e( 'Action', 'wpvivid-backuprestore' ); ?></th>
                </tr>
                </thead>
                <tbody class="wpvivid-loglist" id="wpvivid_loglist">
                <?php
                do_action('wpvivid_get_log_list_output');
                ?>
                </tbody>
            </table>
            <div style="padding-top: 10px; text-align: center;">
                <input class="button-secondary log-page" id="wpvivid_pre_log_page" type="submit" value="<?php esc_attr_e( ' < Pre page ', 'wpvivid-backuprestore' ); ?>" />
                <div style="font-size: 12px; display: inline-block; padding-left: 10px;">
                                <span id="wpvivid_log_page_info" style="line-height: 35px;">
                                    <?php
                                    $current_page=1;
                                    $max_page=ceil(sizeof($loglist['log_list']['file'])/$max_log_diaplay);
                                    if($max_page == 0) $max_page = 1;
                                    echo esc_html($current_page.' / '.$max_page);
                                    ?>
                                </span>
                </div>
                <input class="button-secondary log-page" id="wpvivid_next_log_page" type="submit" value="<?php esc_attr_e( ' Next page > ', 'wpvivid-backuprestore' ); ?>" />
                <div style="float: right;">
                    <select name="" id="wpvivid_display_log_count">
                        <?php
                        foreach ($display_log_count as $value){
                            if($value == $max_log_diaplay){
                                echo '<option selected="selected" value="' . esc_attr($value) . '">' . esc_html($value) . '</option>';
                            }
                            else {
                                echo '<option value="' . esc_attr($value) . '">' . esc_html($value) . '</option>';
                            }
                        }
                        ?>
                    </select>
                </div>
            </div>
        </div>
        <script>
            jQuery('#wpvivid_display_log_count').on("change", function(){
                wpvivid_display_log_page();
            });

            jQuery('#wpvivid_pre_log_page').click(function(){
                wpvivid_pre_log_page();
            });

            jQuery('#wpvivid_next_log_page').click(function(){
                wpvivid_next_log_page();
            });

            function wpvivid_pre_log_page(){
                if(wpvivid_cur_log_page > 1){
                    wpvivid_cur_log_page--;
                }
                wpvivid_display_log_page();
            }

            function wpvivid_next_log_page(){
                var display_count = jQuery("#wpvivid_display_log_count option:selected").val();
                var max_pages=Math.ceil(wpvivid_log_count/display_count);
                if(wpvivid_cur_log_page < max_pages){
                    wpvivid_cur_log_page++;
                }
                wpvivid_display_log_page();
            }

            function wpvivid_display_log_page(){
                var display_count = jQuery("#wpvivid_display_log_count option:selected").val();
                var max_pages=Math.ceil(wpvivid_log_count/display_count);
                if(max_pages == 0) max_pages = 1;
                jQuery('#wpvivid_log_page_info').html(wpvivid_cur_log_page+ " / "+max_pages);

                var begin = (wpvivid_cur_log_page - 1) * display_count;
                var end = parseInt(begin) + parseInt(display_count);
                jQuery("#wpvivid_loglist tr").hide();
                jQuery('#wpvivid_loglist tr').each(function(i){
                    if (i >= begin && i < end)
                    {
                        jQuery(this).show();
                    }
                });
            }

            function wpvivid_retrieve_log_list()
            {
                var ajax_data = {
                    'action': 'wpvivid_get_log_list'
                };
                wpvivid_post_request(ajax_data, function(data){
                    try {
                        var jsonarray = jQuery.parseJSON(data);
                        if (jsonarray.result === "success") {
                            jQuery('#wpvivid_loglist').html("");
                            jQuery('#wpvivid_loglist').append(jsonarray.html);
                            wpvivid_log_count = jsonarray.log_count;
                            wpvivid_display_log_page();
                        }
                    }
                    catch(err){
                        alert(err);
                    }
                }, function(XMLHttpRequest, textStatus, errorThrown) {
                    setTimeout(function () {
                        wpvivid_retrieve_log_list();
                    }, 3000);
                });
            }
        </script>
        <?php
    }

    public function wpvivid_add_page_read_log()
    {
        ?>
        <div id="log-read-page" class="wrap-tab-content wpvivid_tab_read_log" style="display:none;">
            <div class="postbox restore_log" id="wpvivid_read_log_content">
                <div></div>
            </div>
        </div>
        <?php
    }

    public function wpvivid_add_page_mwp()
    {
        ?>
        <div id="mwp-page" class="wrap-tab-content wpvivid_tab_mainwp" name="tab-mwp" style="display:none;">
            <div style="padding: 10px; background-color: #fff;">
                <div style="margin-bottom: 10px;">
                    <?php esc_html_e('If you are a MainWP user, you can set up and control WPvivid Backup Free and Pro for every child site directly from your MainWP dashboard, using our WPvivid Backup for MainWP extension.', 'wpvivid-backuprestore'); ?>
                </div>
                <div style="margin-bottom: 10px;">
                    <input type="button" class="button-primary" id="wpvivid_download_mainwp_extension" value="<?php esc_attr_e('Download WPvivid Backup for MainWP', 'wpvivid-backuprestore'); ?>" />
                </div>
                <div style="margin-bottom: 10px;">
                    <?php esc_html_e('1. Create and download backups for a specific child site', 'wpvivid-backuprestore'); ?>
                </div>
                <div style="margin-bottom: 10px;">
                    <?php esc_html_e('2. Set backup schedules for all child sites', 'wpvivid-backuprestore'); ?>
                </div>
                <div style="margin-bottom: 10px;">
                    <?php
                    esc_html_e('3. Set WPvivid Backup Free and Pro settings for all child sites', 'wpvivid-backuprestore');
                    ?>
                </div>
                <div style="margin-bottom: 10px;">
                    <?php
                    esc_html_e('4. Install, claim and update WPvivid Backup Pro for child sites in bulk', 'wpvivid-backuprestore');
                    ?>
                </div>
                <div>
                    <?php
                    esc_html_e('5. Set up remote storage for child sites in bulk (for WPvivid Backup Pro only)', 'wpvivid-backuprestore');
                    ?>
                </div>
            </div>
        </div>
        <script>
            jQuery('#wpvivid_download_mainwp_extension').click(function(){
                var tempwindow=window.open('_blank');
                tempwindow.location='https://wordpress.org/plugins/wpvivid-backup-mainwp';
            });
            jQuery('#wpvivid_ask_for_discount').click(function(){
                var tempwindow=window.open('_blank');
                tempwindow.location='https://wpvivid.com/wpvivid-backup-for-mainwp';
            });
        </script>
        <?php
    }

    public function wpvivid_add_page_premium(){
        ?>
        <div id="premium-page" class="wrap-tab-content wpvivid_tab_premium" name="tab-premium" style="display: none;">
            <table class="wp-list-table widefat plugins" style="border-collapse: collapse;">
                <thead>
                <tr class="backup-list-head" style="border-bottom: 0;">
                    <th><?php esc_html_e('Features', 'wpvivid-backuprestore'); ?></th>
                    <th style="text-align:center;"><?php esc_html_e('Blogger', 'wpvivid-backuprestore'); ?></th>
                    <th style="text-align:center;"><?php esc_html_e('Freelancer', 'wpvivid-backuprestore'); ?></th>
                    <th style="text-align:center;"><?php esc_html_e('Small Business', 'wpvivid-backuprestore'); ?></th>
                    <th style="text-align:center;"><?php esc_html_e('Ultimate', 'wpvivid-backuprestore'); ?></th>
                </tr>
                </thead>
                <tbody class="wpvivid-backuplist">
                <tr style="">
                    <td>
                        <p><strong><?php esc_html_e('Domains', 'wpvivid-backuprestore'); ?></strong></p>
                        <p><strong><?php esc_html_e('Backup & Migration Pro', 'wpvivid-backuprestore'); ?></strong></p>
                        <p><strong><?php esc_html_e('Image Optimization Pro (Unlimited/domain)', 'wpvivid-backuprestore'); ?></strong></p>
                        <p><strong><?php esc_html_e('Mulitsite Support', 'wpvivid-backuprestore'); ?></strong></p>
                        <p><strong><?php esc_html_e('Staging Pro', 'wpvivid-backuprestore'); ?></strong></p>
                        <p><strong><?php esc_html_e('White Label', 'wpvivid-backuprestore'); ?></strong></p>
                        <p><strong><?php esc_html_e('Roles & Capabilities', 'wpvivid-backuprestore'); ?></strong></p>
                    </td>
                    <td style="text-align:center;">
                        <p><span style="color: #81d742;"><?php esc_html_e('2 domains', 'wpvivid-backuprestore'); ?></span></p>
                        <p><span style="height: 12px;width: 12px;background-color: #81d742;border-radius: 50%;display: inline-block;"></span></p>
                        <p><span style="height: 12px;width: 12px;background-color: #81d742;border-radius: 50%;display: inline-block;"></span></p>
                        <p><span style="height: 12px;width: 12px;background-color: #81d742;border-radius: 50%;display: inline-block;"></span></p>
                        <p><span style="height: 12px;width: 12px;background-color: #81d742;border-radius: 50%;display: inline-block;"></span></p>
                        <p><span style="height: 12px;width: 12px;background-color: #81d742;border-radius: 50%;display: inline-block;"></span></p>
                        <p><span style="height: 12px;width: 12px;background-color: #81d742;border-radius: 50%;display: inline-block;"></span></p>
                    </td>
                    <td style="text-align:center;">
                        <p><span style="color: #81d742;"><?php esc_html_e('Up to 10 domains', 'wpvivid-backuprestore'); ?></span></p>
                        <p><span style="height: 12px;width: 12px;background-color: #81d742;border-radius: 50%;display: inline-block;"></span></p>
                        <p><span style="height: 12px;width: 12px;background-color: #81d742;border-radius: 50%;display: inline-block;"></span></p>
                        <p><span style="height: 12px;width: 12px;background-color: #81d742;border-radius: 50%;display: inline-block;"></span></p>
                        <p><span style="height: 12px;width: 12px;background-color: #81d742;border-radius: 50%;display: inline-block;"></span></p>
                        <p><span style="height: 12px;width: 12px;background-color: #81d742;border-radius: 50%;display: inline-block;"></span></p>
                        <p><span style="height: 12px;width: 12px;background-color: #81d742;border-radius: 50%;display: inline-block;"></span></p>
                    </td>
                    <td style="text-align:center;">
                        <p><span style="color: #81d742;"><?php esc_html_e('Up to 50 domains', 'wpvivid-backuprestore'); ?></span></p>
                        <p><span style="height: 12px;width: 12px;background-color: #81d742;border-radius: 50%;display: inline-block;"></span></p>
                        <p><span style="height: 12px;width: 12px;background-color: #81d742;border-radius: 50%;display: inline-block;"></span></p>
                        <p><span style="height: 12px;width: 12px;background-color: #81d742;border-radius: 50%;display: inline-block;"></span></p>
                        <p><span style="height: 12px;width: 12px;background-color: #81d742;border-radius: 50%;display: inline-block;"></span></p>
                        <p><span style="height: 12px;width: 12px;background-color: #81d742;border-radius: 50%;display: inline-block;"></span></p>
                        <p><span style="height: 12px;width: 12px;background-color: #81d742;border-radius: 50%;display: inline-block;"></span></p>
                    </td>
                    <td style="text-align:center;">
                        <p><span style="color: #81d742;"><?php esc_html_e('Unlimited domains', 'wpvivid-backuprestore'); ?></span></p>
                        <p><span style="height: 12px;width: 12px;background-color: #81d742;border-radius: 50%;display: inline-block;"></span></p>
                        <p><span style="height: 12px;width: 12px;background-color: #81d742;border-radius: 50%;display: inline-block;"></span></p>
                        <p><span style="height: 12px;width: 12px;background-color: #81d742;border-radius: 50%;display: inline-block;"></span></p>
                        <p><span style="height: 12px;width: 12px;background-color: #81d742;border-radius: 50%;display: inline-block;"></span></p>
                        <p><span style="height: 12px;width: 12px;background-color: #81d742;border-radius: 50%;display: inline-block;"></span></p>
                        <p><span style="height: 12px;width: 12px;background-color: #81d742;border-radius: 50%;display: inline-block;"></span></p>
                    </td>
                </tr>
                </tbody>
                <tfoot>
                <tr>
                    <th colspan="5" style="text-align:center;"><p style="margin-top: 6px;"><a href="https://wpvivid.com/pricing" class="page-title-action"><?php esc_html_e('See Plans', 'wpvivid-backuprestore'); ?></a></p></th>
                </tr>
                </tfoot>
            </table>
        </div>
        <?php
    }
}admin/partials/wpvivid-settings-page-display.php000064400000146031151327705670016111 0ustar00<?php

function wpvivid_general_settings()
{
    $general_setting=WPvivid_Setting::get_setting(true, "");
    $display_backup_count=isset($general_setting['options']['wpvivid_common_setting']['max_backup_count'])?$general_setting['options']['wpvivid_common_setting']['max_backup_count']:WPVIVID_DEFAULT_BACKUP_COUNT;
    //
    $display_backup_count=intval($display_backup_count);
    if($display_backup_count > 7)
    {
        $display_backup_count = 7;
    }
    if($general_setting['options']['wpvivid_common_setting']['estimate_backup'])
    {
        $wpvivid_setting_estimate_backup='checked';
    }
    else{
        $wpvivid_setting_estimate_backup='';
    }
    /*if(!isset($general_setting['options']['wpvivid_common_setting']['show_tab_menu'])){
        $wpvivid_show_tab_menu='checked';
    }
    else {
        if ($general_setting['options']['wpvivid_common_setting']['show_tab_menu']) {
            $wpvivid_show_tab_menu = 'checked';
        } else {
            $wpvivid_show_tab_menu = '';
        }
    }*/
    if(!isset($general_setting['options']['wpvivid_common_setting']['show_admin_bar'])){
        $show_admin_bar = 'checked';
    }
    else{
        if($general_setting['options']['wpvivid_common_setting']['show_admin_bar']){
            $show_admin_bar = 'checked';
        }
        else{
            $show_admin_bar = '';
        }
    }
    if(!isset($general_setting['options']['wpvivid_common_setting']['domain_include'])){
        $wpvivid_domain_include = 'checked';
    }
    else{
        if($general_setting['options']['wpvivid_common_setting']['domain_include']){
            $wpvivid_domain_include = 'checked';
        }
        else{
            $wpvivid_domain_include = '';
        }
    }
    if(!isset($general_setting['options']['wpvivid_common_setting']['ismerge'])){
        $wpvivid_ismerge = 'checked';
    }
    else{
        if($general_setting['options']['wpvivid_common_setting']['ismerge'] == '1'){
            $wpvivid_ismerge = 'checked';
        }
        else{
            $wpvivid_ismerge = '';
        }
    }
    if(!isset($general_setting['options']['wpvivid_common_setting']['retain_local'])){
        $wpvivid_retain_local = '';
    }
    else{
        if($general_setting['options']['wpvivid_common_setting']['retain_local'] == '1'){
            $wpvivid_retain_local = 'checked';
        }
        else{
            $wpvivid_retain_local = '';
        }
    }

    if(!isset($general_setting['options']['wpvivid_common_setting']['uninstall_clear_folder'])){
        $uninstall_clear_folder = '';
    }
    else{
        if($general_setting['options']['wpvivid_common_setting']['uninstall_clear_folder'] == '1'){
            $uninstall_clear_folder = 'checked';
        }
        else{
            $uninstall_clear_folder = '';
        }
    }

    if(!isset($general_setting['options']['wpvivid_common_setting']['backup_symlink_folder'])){
        $backup_symlink_folder = '';
    }
    else{
        if($general_setting['options']['wpvivid_common_setting']['backup_symlink_folder'] == '1'){
            $backup_symlink_folder = 'checked';
        }
        else{
            $backup_symlink_folder = '';
        }
    }

    global $wpvivid_plugin;
    $out_of_date=$wpvivid_plugin->_get_out_of_date_info();
    ?>
    <div class="postbox schedule-tab-block">
        <div>
            <select option="setting" name="max_backup_count" id="wpvivid_max_backup_count">
                <?php
                for($i=1; $i<8;$i++){
                    if($i === $display_backup_count){
                        echo '<option selected="selected" value="' . esc_attr($i) . '">' . esc_attr($i) . '</option>';
                    }
                    else {
                        echo '<option value="' . esc_attr($i) . '">' . esc_attr($i) . '</option>';
                    }
                }
                ?>
            </select><strong style="margin-right: 10px;"><?php esc_html_e('backups retained', 'wpvivid-backuprestore'); ?></strong><a href="https://docs.wpvivid.com/wpvivid-backup-pro-backup-retention.html" style="text-decoration: none;"><?php esc_html_e('Pro feature: Retain more backups', 'wpvivid-backuprestore'); ?></a>
        </div>
        <div>
            <label for="wpvivid_estimate_backup">
                <input type="checkbox" option="setting" name="estimate_backup" id="wpvivid_estimate_backup" value="1" <?php echo esc_attr($wpvivid_setting_estimate_backup); ?> />
                <span><?php esc_html_e('Calculate the size of files, folder and database before backing up', 'wpvivid-backuprestore' ); ?></span>
            </label>
        </div>
        <div>
            <label>
                <input type="checkbox" option="setting" name="show_admin_bar" <?php echo esc_attr($show_admin_bar); ?> />
                <span><?php esc_html_e('Show WPvivid backup plugin on top admin bar', 'wpvivid-backuprestore'); ?></span>
            </label>
        </div>
        <div>
            <label>
                <input type="checkbox" option="setting" name="ismerge" <?php echo esc_attr($wpvivid_ismerge); ?> />
                <span><?php esc_html_e('Merge all the backup files into single package when a backup completes. This will save great disk spaces, though takes longer time. We recommended you check the option especially on sites with insufficient server resources.', 'wpvivid-backuprestore'); ?></span>
            </label>
        </div>
        <div>
            <label>
                <input type="checkbox" option="setting" name="retain_local" <?php echo esc_attr($wpvivid_retain_local); ?> />
                <span><?php esc_html_e('Keep storing the backups in localhost after uploading to remote storage', 'wpvivid-backuprestore'); ?></span>
            </label>
        </div>
        <div>
            <label>
                <input type="checkbox" option="setting" name="uninstall_clear_folder" <?php echo esc_attr($uninstall_clear_folder); ?> />
                <span><?php echo esc_html(sprintf('Delete the /%s folder and all backups in it when deleting WPvivid Backup plugin.', $general_setting['options']['wpvivid_local_setting']['path'])); ?></span>
            </label>
        </div>
        <div>
            <label>
                <input type="checkbox" option="setting" name="backup_symlink_folder" <?php echo esc_attr($backup_symlink_folder); ?> />
                <span><?php esc_html_e('Back up symlink folders. Including symlink folders may cause backup/migration failure. Uncheck this option unless you know how symlink folders work.', 'wpvivid-backuprestore'); ?></span>
            </label>
        </div>
    </div>
    <div class="postbox schedule-tab-block">
        <div><strong><?php esc_html_e('Backup Folder', 'wpvivid-backuprestore'); ?></strong></div>
        <div class="setting-tab-block">
            <div><p><?php esc_html_e('Name your folder, this folder must be writable for creating backup files.', 'wpvivid-backuprestore' ); ?><p> </div>
            <input type="text" placeholder="wpvividbackups" option="setting" name="path" id="wpvivid_option_backup_dir" class="all-options" value="<?php echo esc_attr($general_setting['options']['wpvivid_local_setting']['path']); ?>" onkeyup="value=value.replace(/[^\a-\z\A-\Z0-9]/g,'')" onpaste="value=value.replace(/[^\a-\z\A-\Z0-9]/g,'')" />
            <p><span class="wpvivid-element-space-right"><?php esc_html_e('Local storage directory:', 'wpvivid-backuprestore'); ?></span><span><?php echo esc_html(WP_CONTENT_DIR.'/'); ?><span id="wpvivid_setting_local_storage_path"><?php echo esc_html($general_setting['options']['wpvivid_local_setting']['path']); ?></span></span></p>
        </div>
        <div>
            <label>
                <input type="checkbox" option="setting" name="domain_include" <?php echo esc_attr($wpvivid_domain_include); ?> />
                <span><?php esc_html_e('Display domain(url) of current site in backup name. (e.g. domain_wpvivid-5ceb938b6dca9_2019-05-27-07-36_backup_all.zip)', 'wpvivid-backuprestore'); ?></span>
            </label>
        </div>
    </div>
    <div class="postbox schedule-tab-block">
        <div><strong><?php esc_html_e('Remove out-of-date backups', 'wpvivid-backuprestore'); ?></strong></div>
        <div class="setting-tab-block" style="padding-bottom: 0;">
            <fieldset>
                <label for="users_can_register">
                    <p><span class="wpvivid-element-space-right"><?php esc_html_e('Web Server Directory:', 'wpvivid-backuprestore'); ?></span><span id="wpvivid_out_of_date_local_path"><?php echo esc_html($out_of_date['web_server']); ?></span></p>
                    <p><span style="margin-right: 2px;"><?php esc_html_e('Remote Storage Directory:', 'wpvivid-backuprestore'); ?></span><span id="wpvivid_out_of_date_remote_path">
                                    <?php
                                    $wpvivid_get_remote_directory = '';
                                    $wpvivid_get_remote_directory = apply_filters('wpvivid_get_remote_directory', $wpvivid_get_remote_directory);
                                    echo esc_html($wpvivid_get_remote_directory);
                                    ?>
                                </span>
                    </p>
                </label>
            </fieldset>
        </div>
        <div class="setting-tab-block" style="padding: 10px 10px 0 0;">
            <input class="button-primary" id="wpvivid_delete_out_of_backup" style="margin-right:10px;" type="submit" name="delete-out-of-backup" value="<?php esc_attr_e( 'Remove', 'wpvivid-backuprestore' ); ?>" />
            <p><?php esc_html_e('The action is irreversible! It will remove all backups are out-of-date (including local web server and remote storage) if they exist.', 'wpvivid-backuprestore'); ?> </p>
        </div>
    </div>
    <script>
        jQuery('#wpvivid_delete_out_of_backup').click(function(){
            wpvivid_delete_out_of_date_backups();
        });

        /**
         * This function will delete out of date backups.
         */
        function wpvivid_delete_out_of_date_backups(){
            var ajax_data={
                'action': 'wpvivid_clean_out_of_date_backup'
            };
            jQuery('#wpvivid_delete_out_of_backup').css({'pointer-events': 'none', 'opacity': '0.4'});
            wpvivid_post_request(ajax_data, function(data){
                jQuery('#wpvivid_delete_out_of_backup').css({'pointer-events': 'auto', 'opacity': '1'});
                try {
                    var jsonarray = jQuery.parseJSON(data);
                    if (jsonarray.result === "success") {
                        alert("<?php esc_html_e('Out of date backups have been removed.', 'wpvivid-backuprestore'); ?>");
                        wpvivid_handle_backup_data(data);
                    }
                }
                catch(err){
                    alert(err);
                    jQuery('#wpvivid_delete_out_of_backup').css({'pointer-events': 'auto', 'opacity': '1'});
                }
            }, function(XMLHttpRequest, textStatus, errorThrown) {
                var error_message = wpvivid_output_ajaxerror('deleting out of date backups', textStatus, errorThrown);
                alert(error_message);
                jQuery('#wpvivid_delete_out_of_backup').css({'pointer-events': 'auto', 'opacity': '1'});
            });
        }
    </script>
    <?php
}

function wpvivid_email_report()
{
    $general_setting=WPvivid_Setting::get_setting(true, "");
    $setting_email_enable='';
    $setting_email_display = 'display: none;';
    if(isset($general_setting['options']['wpvivid_email_setting']['email_enable'])){
        if($general_setting['options']['wpvivid_email_setting']['email_enable']){
            $setting_email_enable='checked';
            $setting_email_display = '';
        }
    }
    $wpvivid_setting_email_always='';
    $wpvivid_setting_email_failed='';
    if(isset($general_setting['options']['wpvivid_email_setting']['always'])&&$general_setting['options']['wpvivid_email_setting']['always']) {
        $wpvivid_setting_email_always='checked';
    }
    else{
        $wpvivid_setting_email_failed='checked';
    }
    ?>
    <div class="postbox schedule-tab-block" id="wpvivid_email_report">
        <div><p>In order to use this function, please install a <strong><a target="_blank" href="https://wpvivid.com/8-best-smtp-plugins-for-wordpress.html" style="text-decoration: none;">WordPress SMTP plugin</a></strong> of your preference and configure your SMTP server first. This is because WordPress uses the PHP Mail function to send its emails by default, which is not supported by many hosts and can cause issues if it is not set properly.
        </div>
        <div>
            <label for="wpvivid_general_email_enable">
                <input type="checkbox" option="setting" name="email_enable" id="wpvivid_general_email_enable" value="1" <?php echo esc_attr($setting_email_enable); ?> />
                <span><strong><?php esc_html_e( 'Enable email report', 'wpvivid-backuprestore' ); ?></strong></span>
            </label>
        </div>
        <div id="wpvivid_general_email_setting" style="<?php echo esc_attr($setting_email_display); ?>" >
            <input type="text" placeholder="example@yourdomain.com" option="setting" name="send_to" class="regular-text" id="wpvivid_mail" value="<?php
            if(!empty($general_setting['options']['wpvivid_email_setting']['send_to'])) {
                foreach ($general_setting['options']['wpvivid_email_setting']['send_to'] as $mail) {
                    if(!empty($mail) && !is_array($mail)) {
                        echo esc_attr($mail);
                        break;
                    }
                }
            }
            ?>" />
            <input class="button-secondary" id="wpvivid_send_email_test" style="margin-top:10px;" type="submit" name="" value="<?php esc_attr_e( 'Test Email', 'wpvivid-backuprestore' ); ?>" title="Send an email for testing mail function"/>
            <div id="wpvivid_send_email_res"></div>
            <fieldset class="setting-tab-block">
                <label >
                    <input type="radio" option="setting" name="always" value="1" <?php echo esc_attr($wpvivid_setting_email_always); ?> />
                    <span><?php esc_html_e( 'Always send an email notification when a backup is complete', 'wpvivid-backuprestore' ); ?></span>
                </label><br>
                <label >
                    <input type="radio" option="setting" name="always" value="0" <?php echo esc_attr($wpvivid_setting_email_failed); ?> />
                    <span><?php esc_html_e( 'Only send an email notification when a backup fails', 'wpvivid-backuprestore' ); ?></span>
                </label><br>
            </fieldset>
            <div style="margin-bottom: 10px;">
                <a href="https://wpvivid.com/wpvivid-backup-pro-email-report?utm_source=client_email_report&utm_medium=inner_link&utm_campaign=access" style="text-decoration: none;"><?php esc_html_e('Pro feature: Add another email address to get report', 'wpvivid-backuprestore'); ?></a>
            </div>
        </div>
    </div>
    <script>
        jQuery('#wpvivid_send_email_test').click(function(){
            wpvivid_email_test();
        });

        /**
         * After enabling email report feature, and test if an email address works or not
         */
        function wpvivid_email_test(){
            var mail = jQuery('#wpvivid_mail').val();
            var ajax_data = {
                'action': 'wpvivid_test_send_mail',
                'send_to': mail
            };
            wpvivid_post_request(ajax_data, function(data){
                try {
                    var jsonarray = jQuery.parseJSON(data);
                    if (jsonarray.result === 'success') {
                        jQuery('#wpvivid_send_email_res').html('Test succeeded.');
                    }
                    else {
                        jQuery('#wpvivid_send_email_res').html('Test failed, ' + jsonarray.error);
                    }
                }
                catch(err){
                    alert(err);
                }
            }, function(XMLHttpRequest, textStatus, errorThrown) {
                var error_message = wpvivid_output_ajaxerror('sending test mail', textStatus, errorThrown);
                alert(error_message);
            });
        }
    </script>
    <?php
}

function wpvivid_clean_junk()
{
    global $wpvivid_plugin;
    $junk_file=$wpvivid_plugin->_junk_files_info_ex();
    ?>
    <div class="postbox schedule-tab-block" id="wpvivid_clean_junk">
        <div>
            <strong><?php esc_html_e('Web-server disk space in use by WPvivid', 'wpvivid-backuprestore'); ?></strong>
        </div>
        <div class="setting-tab-block">
            <div class="setting-tab-block">
                <span class="wpvivid-element-space-right"><?php esc_html_e('Total Size:', 'wpvivid-backuprestore'); ?></span>
                <span class="wpvivid-size-calc wpvivid-element-space-right" id="wpvivid_junk_sum_size"><?php echo esc_html($junk_file['sum_size']); ?></span>
                <span class="wpvivid-element-space-right"><?php esc_html_e( 'Backup Size:', 'wpvivid-backuprestore' ); ?></span>
                <span class="wpvivid-size-calc wpvivid-element-space-right" id="wpvivid_backup_size"><?php echo esc_html($junk_file['backup_size']); ?></span>
                <input class="button-secondary" id="wpvivid_calculate_size" style="margin-left:10px;" type="submit" name="Calculate-Sizes" value="<?php esc_attr_e( 'Calculate Sizes', 'wpvivid-backuprestore' ); ?>" />
            </div>
            <fieldset>
                <label for="wpvivid_junk_log">
                    <input type="checkbox" id="wpvivid_junk_log" option="junk-files" name="log" value="junk-log" />
                    <span class="wpvivid-element-space-right"><?php esc_html_e( 'Logs Size:', 'wpvivid-backuprestore' ); ?></span>
                    <span class="wpvivid-size-calc" id="wpvivid_log_size"><?php echo esc_html($junk_file['log_dir_size']); ?></span>
                </label>
            </fieldset>
            <fieldset>
                <label for="wpvivid_junk_backup_cache">
                    <input type="checkbox" id="wpvivid_junk_backup_cache" option="junk-files" name="backup_cache" value="junk-backup-cache" />
                    <span class="wpvivid-element-space-right"><?php esc_html_e( 'Backup Cache Size:', 'wpvivid-backuprestore' ); ?></span>
                    <span class="wpvivid-size-calc" id="wpvivid_backup_cache_size"><?php echo esc_html($junk_file['backup_cache_size']); ?></span>
                </label>
            </fieldset>
            <fieldset>
                <label for="wpvivid_junk_file">
                    <input type="checkbox" id="wpvivid_junk_file" option="junk-files" name="junk_files" value="junk-files" />
                    <span class="wpvivid-element-space-right"><?php esc_html_e( 'Junk Size:', 'wpvivid-backuprestore' ); ?></span>
                    <span class="wpvivid-size-calc" id="wpvivid_junk_size"><?php echo esc_html($junk_file['junk_size']); ?></span>
                </label>
            </fieldset>
        </div>
        <div><input class="button-primary" id="wpvivid_clean_junk_file" type="submit" name="Empty-all-files" value="<?php esc_attr_e( 'Empty', 'wpvivid-backuprestore' ); ?>" /></div>
        <div style="clear:both;"></div>
    </div>
    <script>
        jQuery('#wpvivid_calculate_size').click(function(){
            wpvivid_calculate_diskspaceused();
        });

        jQuery('#wpvivid_clean_junk_file').click(function(){
            wpvivid_clean_junk_files();
        });

        /**
         * Calculate the server disk space in use by WPvivid.
         */
        function wpvivid_calculate_diskspaceused(){
            var ajax_data={
                'action': 'wpvivid_junk_files_info'
            };
            var current_size = jQuery('#wpvivid_junk_sum_size').html();
            jQuery('#wpvivid_calculate_size').css({'pointer-events': 'none', 'opacity': '0.4'});
            jQuery('#wpvivid_clean_junk_file').css({'pointer-events': 'none', 'opacity': '0.4'});
            jQuery('.wpvivid-size-calc').html("calculating...");
            wpvivid_post_request(ajax_data, function(data){
                jQuery('#wpvivid_calculate_size').css({'pointer-events': 'auto', 'opacity': '1'});
                jQuery('#wpvivid_clean_junk_file').css({'pointer-events': 'auto', 'opacity': '1'});
                try {
                    var jsonarray = jQuery.parseJSON(data);
                    if (jsonarray.result === "success") {
                        jQuery('#wpvivid_junk_sum_size').html(jsonarray.data.sum_size);
                        jQuery('#wpvivid_log_size').html(jsonarray.data.log_dir_size);
                        jQuery('#wpvivid_backup_cache_size').html(jsonarray.data.backup_cache_size);
                        jQuery('#wpvivid_junk_size').html(jsonarray.data.junk_size);
                        jQuery('#wpvivid_backup_size').html(jsonarray.data.backup_size);
                    }
                }
                catch(err){
                    jQuery('#wpvivid_calculate_size').css({'pointer-events': 'auto', 'opacity': '1'});
                    jQuery('#wpvivid_clean_junk_file').css({'pointer-events': 'auto', 'opacity': '1'});
                    jQuery('#wpvivid_junk_sum_size').html(current_size);
                }
            }, function(XMLHttpRequest, textStatus, errorThrown) {
                jQuery('#wpvivid_calculate_size').css({'pointer-events': 'auto', 'opacity': '1'});
                jQuery('#wpvivid_clean_junk_file').css({'pointer-events': 'auto', 'opacity': '1'});
                jQuery('#wpvivid_junk_sum_size').html(current_size);
            });
        }

        /**
         * Clean junk files created during backups and restorations off your web server disk.
         */
        function wpvivid_clean_junk_files(){
            var descript = '<?php esc_html_e('The selected item(s) will be permanently deleted. Are you sure you want to continue?', 'wpvivid-backuprestore'); ?>';
            var ret = confirm(descript);
            if(ret === true){
                var option_data = wpvivid_ajax_data_transfer('junk-files');
                var ajax_data = {
                    'action': 'wpvivid_clean_local_storage',
                    'options': option_data
                };
                jQuery('#wpvivid_calculate_size').css({'pointer-events': 'none', 'opacity': '0.4'});
                jQuery('#wpvivid_clean_junk_file').css({'pointer-events': 'none', 'opacity': '0.4'});
                wpvivid_post_request(ajax_data, function (data) {
                    jQuery('#wpvivid_calculate_size').css({'pointer-events': 'auto', 'opacity': '1'});
                    jQuery('#wpvivid_clean_junk_file').css({'pointer-events': 'auto', 'opacity': '1'});
                    jQuery('input[option="junk-files"]').prop('checked', false);
                    try {
                        var jsonarray = jQuery.parseJSON(data);
                        alert(jsonarray.msg);
                        if (jsonarray.result === "success") {
                            jQuery('#wpvivid_junk_sum_size').html(jsonarray.data.sum_size);
                            jQuery('#wpvivid_log_size').html(jsonarray.data.log_dir_size);
                            jQuery('#wpvivid_backup_cache_size').html(jsonarray.data.backup_cache_size);
                            jQuery('#wpvivid_junk_size').html(jsonarray.data.junk_size);
                            jQuery('#wpvivid_backup_size').html(jsonarray.data.backup_size);
                            jQuery('#wpvivid_loglist').html("");
                            jQuery('#wpvivid_loglist').append(jsonarray.html);
                            wpvivid_log_count = jsonarray.log_count;
                            wpvivid_display_log_page();
                        }
                    }
                    catch(err){
                        alert(err);
                    }
                }, function (XMLHttpRequest, textStatus, errorThrown) {
                    var error_message = wpvivid_output_ajaxerror('cleaning out junk files', textStatus, errorThrown);
                    alert(error_message);
                    jQuery('#wpvivid_calculate_size').css({'pointer-events': 'auto', 'opacity': '1'});
                    jQuery('#wpvivid_clean_junk_file').css({'pointer-events': 'auto', 'opacity': '1'});
                });
            }
        }

        jQuery(document).ready(function ()
        {
            //wpvivid_calculate_diskspaceused();
        });
    </script>
    <?php
}

function wpvivid_export_import_settings()
{
    ?>
    <div class="postbox schedule-tab-block" id="wpvivid_export_import">
        <div class="setting-tab-block" style="padding-bottom: 0;">
            <input class="button-primary" id="wpvivid_setting_export" type="button" name="" value="<?php esc_attr_e( 'Export', 'wpvivid-backuprestore' ); ?>" />
            <p><?php esc_html_e('Click \'Export\' button to save WPvivid settings on your local computer.', 'wpvivid-backuprestore'); ?> </p>
        </div>
        <div class="setting-tab-block" style="padding: 0 10px 0 0;">
            <input type="file" name="fileTrans" id="wpvivid_select_import_file"></br>
            <input class="button-primary" id="wpvivid_setting_import" type="button" name="" value="<?php esc_attr_e( 'Import', 'wpvivid-backuprestore' ); ?>" />
            <p><?php esc_html_e('Importing the json file can help you set WPvivid\'s configuration on another wordpress site quickly.', 'wpvivid-backuprestore'); ?></p>
        </div>
        <div style="clear:both;"></div>
    </div>
    <script>
        jQuery('#wpvivid_setting_export').click(function(){
            wpvivid_export_settings();
        });

        jQuery('#wpvivid_setting_import').click(function(){
            wpvivid_import_settings();
        });

        function wpvivid_export_settings() {
            wpvivid_location_href=true;
            location.href =ajaxurl+'?_wpnonce='+wpvivid_ajax_object.ajax_nonce+'&action=wpvivid_export_setting&setting=1&history=1&review=0';
        }

        function wpvivid_import_settings(){
            var files = jQuery('input[name="fileTrans"]').prop('files');

            if(files.length == 0){
                alert('Choose a settings file and import it by clicking Import button.');
                return;
            }
            else{
                var reader = new FileReader();
                reader.readAsText(files[0], "UTF-8");
                reader.onload = function(evt){
                    var fileString = evt.target.result;
                    var ajax_data = {
                        'action': 'wpvivid_import_setting',
                        'data': fileString
                    };
                    wpvivid_post_request(ajax_data, function(data){
                        try {
                            var jsonarray = jQuery.parseJSON(data);
                            if (jsonarray.result === 'success') {
                                alert('The plugin settings were imported successfully.');
                                location.reload();
                            }
                            else {
                                alert('Error: ' + jsonarray.error);
                            }
                        }
                        catch(err){
                            alert(err);
                        }
                    }, function(XMLHttpRequest, textStatus, errorThrown) {
                        var error_message = wpvivid_output_ajaxerror('importing the previously-exported settings', textStatus, errorThrown);
                        jQuery('#wpvivid_display_log_content').html(error_message);
                    });
                }
            }
        }
    </script>
    <?php
}

function wpvivid_advanced_settings()
{
    //wpvivid_compress_setting backup
    $common_setting=get_option('wpvivid_common_setting',array());
    $max_file_size=isset($common_setting['max_file_size'])?$common_setting['max_file_size']:WPVIVID_DEFAULT_MAX_FILE_SIZE;
    $exclude_file_size=isset($common_setting['exclude_file_size'])?$common_setting['exclude_file_size']:WPVIVID_DEFAULT_EXCLUDE_FILE_SIZE;
    $max_execution_time=isset($common_setting['max_execution_time'])?$common_setting['max_execution_time']:WPVIVID_MAX_EXECUTION_TIME;
    $memory_limit=isset($common_setting['memory_limit'])?$common_setting['memory_limit']:WPVIVID_MEMORY_LIMIT;
    $migrate_size=isset($common_setting['migrate_size'])?$common_setting['migrate_size']:WPVIVID_MIGRATE_SIZE;
    $wpvivid_max_resume_count=isset($common_setting['max_resume_count'])?$common_setting['max_resume_count']:WPVIVID_RESUME_RETRY_TIMES;
    //$compress_file_use_cache=isset($common_setting['compress_file_use_cache'])?$common_setting['compress_file_use_cache']:false;
    $compress_file_count=isset($common_setting['compress_file_count'])?$common_setting['compress_file_count']:500;
    $max_sql_file_size=isset($common_setting['max_sql_file_size'])?$common_setting['max_sql_file_size']:200;

    //restore
    $restore_max_execution_time=isset($common_setting['restore_max_execution_time'])?$common_setting['restore_max_execution_time']:WPVIVID_RESTORE_MAX_EXECUTION_TIME;
    $restore_memory_limit=isset($common_setting['restore_memory_limit'])?$common_setting['restore_memory_limit']:WPVIVID_RESTORE_MEMORY_LIMIT;
    $replace_rows_pre_request=isset($common_setting['replace_rows_pre_request'])?$common_setting['replace_rows_pre_request']:10000;
    $sql_file_buffer_pre_request=isset($common_setting['sql_file_buffer_pre_request'])?$common_setting['sql_file_buffer_pre_request']:'5';
    $use_index=isset($common_setting['use_index'])?$common_setting['use_index']:1;
    if($use_index)
    {
        $use_index=' checked';
    }
    else
    {
        $use_index=' ';
    }
    $unzip_files_pre_request=isset($common_setting['unzip_files_pre_request'])?$common_setting['unzip_files_pre_request']:1000;

    //common
    if(isset($common_setting['db_connect_method']))
    {
        if($common_setting['db_connect_method'] === 'wpdb')
        {
            $db_method_wpdb = 'checked';
            $db_method_pdo  = '';
        }
        else
        {
            $db_method_wpdb = '';
            $db_method_pdo  = 'checked';
        }
    }
    else
    {
        $db_method_wpdb = 'checked';
        $db_method_pdo  = '';
    }

    if(isset($common_setting['zip_method']))
    {
        if($common_setting['zip_method'] === 'ziparchive')
        {
            $zip_method_archive = 'checked';
            $zip_method_pclzip  = '';
        }
        else{
            $zip_method_archive = '';
            $zip_method_pclzip  = 'checked';
        }
    }
    else
    {
        if(class_exists('ZipArchive'))
        {
            if(method_exists('ZipArchive', 'addFile'))
            {
                $zip_method_archive = 'checked';
                $zip_method_pclzip  = '';
            }
            else
            {
                $zip_method_archive = '';
                $zip_method_pclzip  = 'checked';
            }
        }
        else
        {
            $zip_method_archive = '';
            $zip_method_pclzip  = 'checked';
        }
    }

    if(isset($common_setting['backup_params']))
    {
        if($common_setting['backup_params'] === 'low')
        {
            $backup_params_low    = 'checked';
            $backup_params_mid    = '';
            $backup_params_high   = '';
            $backup_params_custom = '';
            $backup_custom_setting_display = 'display: none;';
        }
        else if($common_setting['backup_params'] === 'mid')
        {
            $backup_params_low    = '';
            $backup_params_mid    = 'checked';
            $backup_params_high   = '';
            $backup_params_custom = '';
            $backup_custom_setting_display = 'display: none;';
        }
        else if($common_setting['backup_params'] === 'high')
        {
            $backup_params_low    = '';
            $backup_params_mid    = '';
            $backup_params_high   = 'checked';
            $backup_params_custom = '';
            $backup_custom_setting_display = 'display: none;';
        }
        else if($common_setting['backup_params'] === 'custom')
        {
            $backup_params_low    = '';
            $backup_params_mid    = '';
            $backup_params_high   = '';
            $backup_params_custom = 'checked';
            $backup_custom_setting_display = '';
        }
        else
        {
            $backup_params_low    = 'checked';
            $backup_params_mid    = '';
            $backup_params_high   = '';
            $backup_params_custom = '';
            $backup_custom_setting_display = 'display: none;';
        }
    }
    else if(isset($common_setting['compress_file_count']))
    {
        $backup_params_low    = '';
        $backup_params_mid    = '';
        $backup_params_high   = '';
        $backup_params_custom = 'checked';
        $backup_custom_setting_display = '';
    }
    else
    {
        $backup_params_low    = 'checked';
        $backup_params_mid    = '';
        $backup_params_high   = '';
        $backup_params_custom = '';
        $backup_custom_setting_display = 'display: none;';
    }

    ?>
    <div class="postbox schedule-tab-block wpvivid-setting-addon" style="margin-bottom: 10px; padding-bottom: 0;">
        <div class="wpvivid-element-space-bottom">
            <strong><?php esc_html_e('Database access method.', 'wpvivid-backuprestore'); ?></strong>
        </div>
        <div class="wpvivid-element-space-bottom">
            <label>
                <input type="radio" option="setting" name="db_connect_method" value="wpdb" <?php echo esc_attr($db_method_wpdb); ?> />
                <span class="wpvivid-element-space-right"><strong>WPDB</strong></span><span><?php esc_html_e('WPDB option has a better compatibility, but the speed of backup and restore is slower.', 'wpvivid-backuprestore'); ?></span>
            </label>
        </div>
        <div class="wpvivid-element-space-bottom">
            <label>
                <input type="radio" option="setting" name="db_connect_method" value="pdo" <?php echo esc_attr($db_method_pdo); ?> />
                <span class="wpvivid-element-space-right"><strong>PDO</strong></span><span><?php esc_html_e('It is recommended to choose PDO option if pdo_mysql extension is installed on your server, which lets you backup and restore your site faster.', 'wpvivid-backuprestore'); ?></span>
            </label>
        </div>
    </div>
    <div class="postbox schedule-tab-block wpvivid-setting-addon" style="margin-bottom: 10px; padding-bottom: 0;">
        <div class="wpvivid-element-space-bottom">
            <strong><?php esc_html_e('Backup compression method.', 'wpvivid-backuprestore'); ?></strong>
        </div>
        <div class="wpvivid-element-space-bottom">
            <label>
                <input type="radio" option="setting" name="zip_method" value="ziparchive" <?php echo esc_attr($zip_method_archive); ?> />
                <span class="wpvivid-element-space-right"><strong>ZipArchive</strong></span><span><?php esc_html_e('ZipArchive has a better flexibility which provides a higher backup success rate and speed. WPvivid Backup Plugin uses ZipArchive method by default. Using this method requires the ZIP extension to be installed within your PHP.', 'wpvivid-backuprestore'); ?></span>
            </label>
        </div>
        <div class="wpvivid-element-space-bottom">
            <label>
                <input type="radio" option="setting" name="zip_method" value="pclzip" <?php echo esc_attr($zip_method_pclzip); ?> />
                <span class="wpvivid-element-space-right"><strong>PCLZIP</strong></span><span><?php esc_html_e('PclZip is a much slower but more stable zip method that is included in every WordPress install. WPvivid will automatically switch to PclZip if the ZIP extension is not installed within your PHP.', 'wpvivid-backuprestore'); ?></span>
            </label>
        </div>
    </div>
    <div class="postbox schedule-tab-block setting-page-content">
        <div style="padding-top: 10px;">
            <div class="wpvivid-element-space-bottom">
                <strong><?php esc_html_e('Backup performance mode.', 'wpvivid-backuprestore'); ?></strong>
            </div>
            <div class="wpvivid-element-space-bottom">
                <label>
                    <input type="radio" option="setting" name="backup_params" value="low" <?php esc_attr_e($backup_params_low); ?> />
                    <span class="wpvivid-element-space-right"><strong>Low (Balanced)</strong></span><span><?php esc_html_e('Use this default setting for minimal server resource usage, but expect longer backup times. Best for shared hosting or limited resources. Backups are split into 200MB chunks.', 'wpvivid-backuprestore'); ?></span>
                </label>
            </div>
            <div class="wpvivid-element-space-bottom">
                <label>
                    <input type="radio" option="setting" name="backup_params" value="mid" <?php esc_attr_e($backup_params_mid); ?> />
                    <span class="wpvivid-element-space-right"><strong>Mid (Standard)</strong></span><span><?php esc_html_e('This mode offers a good balance between backup speed and resource usage. It\'s suitable for most web hosting environments.', 'wpvivid-backuprestore'); ?></span>
                </label>
            </div>
            <div class="wpvivid-element-space-bottom">
                <label>
                    <input type="radio" option="setting" name="backup_params" value="high" <?php esc_attr_e($backup_params_high); ?> />
                    <span class="wpvivid-element-space-right"><strong>High (Accelerated)</strong></span><span><?php esc_html_e('This mode uses more server resources to reduce backup time, but is only recommended for dedicated servers. If backups time out or get stuck, consider Mid or Low mode. Backups are split into 4GB chunks.', 'wpvivid-backuprestore'); ?></span>
                </label>
            </div>
            <div class="wpvivid-element-space-bottom">
                <label>
                    <input type="radio" option="setting" name="backup_params" value="custom" <?php esc_attr_e($backup_params_custom); ?> />
                    <span class="wpvivid-element-space-right"><strong>Custom (Advanced)</strong></span><span><?php esc_html_e('This mode allows fine-tuning of backup parameters. Incorrect configuration can lead to backup failures. It is recommended to use only with specific guidance from our support team.', 'wpvivid-backuprestore'); ?></span>
                </label>
            </div>
            <div id="wpvivid_custom_backup_params" style="<?php esc_attr_e($backup_custom_setting_display); ?>">
                <div><strong><?php esc_html_e('Compress Files Every', 'wpvivid-backuprestore'); ?></strong></div>
                <div class="setting-tab-block">
                    <input type="text" placeholder="200" option="setting" name="max_file_size" id="wpvivid_max_zip" class="all-options" value="<?php echo esc_attr(str_replace('M', '', $max_file_size)); ?>" onkeyup="value=value.replace(/\D/g,'')" />MB
                    <div><p><?php esc_html_e( 'Some web hosting providers limit large zip files (e.g. 200MB), and therefore splitting your backup into many parts is an ideal way to avoid hitting the limitation if you are running a big website.  Please try to adjust the value if you are encountering backup errors. When you set a value of 0MB, backups will be split every 4GB.', 'wpvivid-backuprestore' ); ?></div></p>
                </div>
                <div><strong><?php esc_html_e('Exclude the files which are larger than', 'wpvivid-backuprestore'); ?></strong></div>
                <div class="setting-tab-block">
                    <input type="text" placeholder="0" option="setting" name="exclude_file_size" id="wpvivid_ignore_large" class="all-options" value="<?php echo esc_attr($exclude_file_size); ?>" onkeyup="value=value.replace(/\D/g,'')" />MB
                    <div><p><?php esc_html_e( 'Using the option will ignore the file larger than the certain size in MB when backing up, \'0\' (zero) means unlimited.', 'wpvivid-backuprestore' ); ?></p></div>
                </div>
                <div><strong><?php esc_html_e('PHP script execution timeout for backup', 'wpvivid-backuprestore'); ?></strong></div>
                <div class="setting-tab-block">
                    <input type="text" placeholder="900" option="setting" name="max_execution_time" id="wpvivid_option_timeout" class="all-options" value="<?php echo esc_attr($max_execution_time); ?>" onkeyup="value=value.replace(/\D/g,'')" /><?php esc_html_e('Seconds', 'wpvivid-backuprestore'); ?>
                    <div><p><?php esc_html_e( 'The time-out is not your server PHP time-out. With the execution time exhausted, our plugin will shut the process of backup down. If the progress of backup encounters a time-out, that means you have a medium or large sized website, please try to scale the value bigger.', 'wpvivid-backuprestore' ); ?></p></div>
                </div>
                <div><strong><?php esc_html_e('PHP Memory Limit for backup', 'wpvivid-backuprestore'); ?></strong></div>
                <div class="setting-tab-block">
                    <input type="text" placeholder="256" option="setting" name="memory_limit" class="all-options" value="<?php echo esc_attr(str_replace('M', '', $memory_limit)); ?>" onkeyup="value=value.replace(/\D/g,'')" />MB
                    <div><p><?php esc_html_e('Adjust this value to apply for a temporary PHP memory limit for WPvivid backup plugin to run a backup. We set this value to 256M by default. Increase the value if you encounter a memory exhausted error. Note: some web hosting providers may not support this.', 'wpvivid-backuprestore'); ?></p></div>
                </div>
                <div><strong><?php esc_html_e('The number of files compressed to the backup zip each time', 'wpvivid-backuprestore'); ?></strong></div>
                <div class="setting-tab-block">
                    <input type="text" placeholder="500" option="setting" name="compress_file_count" id="wpvivid_compress_file_count" class="all-options" value="<?php echo esc_attr($compress_file_count); ?>" onkeyup="value=value.replace(/\D/g,'')" /><?php esc_html_e('Files', 'wpvivid-backuprestore'); ?>
                    <div><p><?php esc_html_e( 'When taking a backup, the plugin will compress this number of files to the backup zip each time. The default value is 500. The lower the value, the longer time the backup will take, but the higher the backup success rate. If you encounter a backup timeout issue, try to decrease this value.', 'wpvivid-backuprestore' ); ?></p></div>
                </div>
                <div><strong><?php esc_html_e('Split a sql file every this size', 'wpvivid-backuprestore'); ?></strong></div>
                <div class="setting-tab-block">
                    <input type="text" placeholder="200" option="setting" name="max_sql_file_size" id="wpvivid_max_sql_file_size" class="all-options" value="<?php echo esc_attr($max_sql_file_size); ?>" onkeyup="value=value.replace(/\D/g,'')" /><?php esc_html_e('MB', 'wpvivid-backuprestore'); ?>
                    <div><p><?php esc_html_e( 'Some web hosting providers limit large zip files (e.g. 200MB), and therefore splitting your backup into many parts is an ideal way to avoid hitting the limitation if you are running a big website. Please try to adjust the value if you are encountering backup errors. If you use a value of 0 MB, any backup files won\'t be split.', 'wpvivid-backuprestore' ); ?></p></div>
                </div>
                <div><strong><?php esc_html_e('Chunk Size', 'wpvivid-backuprestore'); ?></strong></div>
                <div class="setting-tab-block">
                    <input type="text" placeholder="2048" option="setting" name="migrate_size" class="all-options" value="<?php echo esc_attr($migrate_size); ?>" onkeyup="value=value.replace(/\D/g,'')" />KB
                    <div><p><?php esc_html_e('e.g. if you choose a chunk size of 2MB, a 8MB file will use 4 chunks. Decreasing this value will break the ISP\'s transmission limit, for example:512KB', 'wpvivid-backuprestore'); ?></p></div>
                </div>
                <div>
                    <strong>Retrying </strong>
                    <select option="setting" name="max_resume_count">
                        <?php
                        for($resume_count=3; $resume_count<10; $resume_count++){
                            if($resume_count === $wpvivid_max_resume_count){
                                echo '<option selected="selected" value="'.esc_attr($resume_count).'">'.esc_html($resume_count).'</option>';
                            }
                            else{
                                echo '<option value="'.esc_attr($resume_count).'">'.esc_html($resume_count).'</option>';
                            }
                        }
                        ?>
                    </select>
                    <strong> times when encountering a time-out error</strong>
                </div>
            </div>
        </div>
    </div>
    <div class="postbox schedule-tab-block wpvivid-setting-addon" style="margin-bottom: 10px; padding-bottom: 0;">
        <div>
            <div>
                <input type="checkbox" option="setting" name="use_index" style="margin-right: 0px;" <?php echo esc_attr($use_index); ?> />
                <strong><?php esc_html_e('Extract files by index for restoration', 'wpvivid-backuprestore'); ?></strong>
            </div>
            <div><p><?php esc_html_e('Specify the number of files to be extracted per request. The lower the number is, the slower the restoration, but the lower the chance of a timeout error or restore failure.', 'wpvivid-backuprestore'); ?></p></div>
        </div>
        <div class="setting-tab-block">
            <input type="text" placeholder="1000" option="setting" name="unzip_files_pre_request" class="all-options" value="<?php echo esc_attr($unzip_files_pre_request); ?>" onkeyup="value=value.replace(/\D/g,'')" />Files are unzipped every PHP request
        </div>
        <div><strong><?php esc_html_e('PHP script execution timeout for restore', 'wpvivid-backuprestore'); ?></strong></div>
        <div class="setting-tab-block">
            <input type="text" placeholder="300" option="setting" name="restore_max_execution_time" class="all-options" value="<?php echo esc_attr($restore_max_execution_time); ?>" onkeyup="value=value.replace(/\D/g,'')" /><?php esc_html_e('Seconds', 'wpvivid-backuprestore'); ?>
            <div><p><?php esc_html_e( 'The time-out is not your server PHP time-out. With the execution time exhausted, our plugin will shut the process of restore down. If the progress of restore encounters a time-out, that means you have a medium or large sized website, please try to scale the value bigger.', 'wpvivid-backuprestore' ); ?></p></div>
        </div>
        <div><strong><?php esc_html_e('PHP Memory Limit for restoration', 'wpvivid-backuprestore'); ?></strong></div>
        <div class="setting-tab-block">
            <input type="text" placeholder="256" option="setting" name="restore_memory_limit" class="all-options" value="<?php echo esc_attr(str_replace('M', '', $restore_memory_limit)); ?>" onkeyup="value=value.replace(/\D/g,'')" />MB
            <div><p><?php esc_html_e('Adjust this value to apply for a temporary PHP memory limit for WPvivid backup plugin in restore process. We set this value to 256M by default. Increase the value if you encounter a memory exhausted error. Note: some web hosting providers may not support this.', 'wpvivid-backuprestore'); ?></p></div>
        </div>
        <div><strong><?php esc_html_e('Maximum rows of data to be processed per request for restoration', 'wpvivid-backuprestore'); ?></strong></div>
        <div class="setting-tab-block">
            <input type="text" placeholder="10000" option="setting" name="replace_rows_pre_request" class="all-options" value="<?php echo esc_attr($replace_rows_pre_request); ?>" onkeyup="value=value.replace(/\D/g,'')" />rows
            <div><p><?php esc_html_e('The smaller it is, the slower the restoration will be, but the lower the chance of a timeout error.', 'wpvivid-backuprestore'); ?></p></div>
        </div>
        <div><strong><?php esc_html_e('Maximum size of sql file to be imported per request for restoration', 'wpvivid-backuprestore'); ?></strong></div>
        <div class="setting-tab-block">
            <input type="text" placeholder="5" option="setting" name="sql_file_buffer_pre_request" class="all-options" value="<?php echo esc_attr($sql_file_buffer_pre_request); ?>" onkeyup="value=value.replace(/\D/g,'')" />MB
            <div><p><?php esc_html_e('Maximum rows of data to be processed per request.', 'wpvivid-backuprestore'); ?></p></div>
        </div>
    </div>
    <?php
}

function wpvivid_add_setting_tab_page($setting_array){
    $setting_array['general_setting'] = array('index' => '1', 'tab_func' => 'wpvivid_settingpage_add_tab_general', 'page_func' => 'wpvivid_settingpage_add_page_general');
    $setting_array['advance_setting'] = array('index' => '2', 'tab_func' => 'wpvivid_settingpage_add_tab_advance', 'page_func' => 'wpvivid_settingpage_add_page_advance');
    return $setting_array;
}

function wpvivid_settingpage_add_tab_general(){
    ?>
    <a href="#" id="wpvivid_tab_general_setting" class="nav-tab setting-nav-tab nav-tab-active" onclick="switchsettingTabs(event,'page-general-setting')"><?php esc_html_e('General Settings', 'wpvivid-backuprestore'); ?></a>
    <?php
}

function wpvivid_settingpage_add_tab_advance(){
    ?>
    <a href="#" id="wpvivid_tab_advance_setting" class="nav-tab setting-nav-tab" onclick="switchsettingTabs(event,'page-advance-setting')"><?php esc_html_e('Advanced Settings', 'wpvivid-backuprestore'); ?></a>
    <?php
}

function wpvivid_settingpage_add_page_general(){
    ?>
    <div class="setting-tab-content wpvivid_tab_general_setting" id="page-general-setting" style="margin-top: 10px;">
        <?php do_action('wpvivid_setting_add_general_cell'); ?>
    </div>
    <?php
}

function wpvivid_settingpage_add_page_advance(){
    ?>
    <div class="setting-tab-content wpvivid_tab_advance_setting" id="page-advance-setting" style="margin-top: 10px; display: none;">
        <?php do_action('wpvivid_setting_add_advance_cell'); ?>
    </div>
    <?php
}

add_filter('wpvivid_add_setting_tab_page', 'wpvivid_add_setting_tab_page', 10);

add_action('wpvivid_setting_add_general_cell','wpvivid_general_settings',10);
add_action('wpvivid_setting_add_advance_cell','wpvivid_advanced_settings',13);
add_action('wpvivid_setting_add_general_cell','wpvivid_email_report',14);
add_action('wpvivid_setting_add_general_cell','wpvivid_clean_junk',15);
add_action('wpvivid_setting_add_general_cell','wpvivid_export_import_settings',16);
?>
admin/partials/wpvivid-schedule-page-display.php000064400000041335151327705670016046 0ustar00<?php

function wpvivid_schedule_settings()
{
    ?>
    <tr>
        <td class="row-title wpvivid-backup-settings-table tablelistcolumn"><label for="tablecell"><?php esc_html_e('Schedule Settings', 'wpvivid-backuprestore'); ?></label></td>
        <td class="tablelistcolumn">
            <div id="storage-brand-3">
                <div>
                    <div>
                        <div class="postbox schedule-tab-block">
                            <label for="wpvivid_schedule_enable">
                                <input option="schedule" name="enable" type="checkbox" id="wpvivid_schedule_enable" />
                                <span><?php esc_html_e( 'Enable backup schedule', 'wpvivid-backuprestore' ); ?></span>
                            </label><br>
                            <label>
                                <div style="float: left;">
                                    <input type="checkbox" disabled />
                                    <span class="wpvivid-element-space-right" style="color: #ddd;"><?php esc_html_e('Enable Incremental Backup', 'wpvivid-backuprestore'); ?></span>
                                </div>
                                <div style="float: left; height: 32px; line-height: 32px;">
                                    <span class="wpvivid-feature-pro">
                                        <a href="https://docs.wpvivid.com/wpvivid-backup-pro-incremental-backups.html"><?php esc_html_e('Pro feature: learn more', 'wpvivid-backuprestore'); ?></a>
                                    </span>
                                </div>
                                <div style="clear: both;"></div>
                            </label>
                            <label>
                                <div style="float: left;">
                                    <input type="checkbox" disabled />
                                    <span class="wpvivid-element-space-right" style="color: #ddd;"><?php esc_html_e('Advanced Schedule', 'wpvivid-backuprestore'); ?></span>
                                </div>
                                <div style="float: left; height: 32px; line-height: 32px;">
                                    <span class="wpvivid-feature-pro">
                                        <a href="https://docs.wpvivid.com/wpvivid-backup-pro-schedule-overview.html"><?php esc_html_e('Pro feature: learn more', 'wpvivid-backuprestore'); ?></a>
                                    </span>
                                </div>
                                <div style="clear: both;"></div>
                            </label>
                            <div style="clear: both;"></div>
                            <div>
                                <?php
                                $time = '00:00:00';
                                $utime = strtotime($time);
                                echo '<p>1) '.'Scheduled job will start at <strong>UTC</strong> time:'.'&nbsp'.esc_html(gmdate('H:i:s', $utime)).'</p>';
                                echo '<p>2) ';
                                esc_html_e('Being subjected to mechanisms of PHP, a scheduled backup task for your site will be triggered only when the site receives at least a visit at any page.', 'wpvivid-backuprestore');
                                echo '</p>';
                                ?>
                            </div>
                        </div>
                        <div class="postbox schedule-tab-block">
                            <fieldset>
                                <legend class="screen-reader-text"><span>input type="radio"</span></legend>
                                <?php
                                $display_array = array("12Hours", "Daily", "Weekly", "Fortnightly", "Monthly");
                                foreach($display_array as $display)
                                {
                                    $schedule_check = wpvivid_check_schedule_type($display);
                                    if($schedule_check['result'])
                                    {
                                        echo ' <label><input type="radio" option="schedule" name="recurrence" value="'.esc_attr($schedule_check['type']).'" />';
                                        if($display === '12Hours'){
                                            echo '<span>'.esc_html__('12Hours', 'wpvivid-backuprestore').'</span></label><br>';
                                        }
                                        if($display === 'Daily'){
                                            echo '<span>'.esc_html__('Daily', 'wpvivid-backuprestore').'</span></label><br>';
                                        }
                                        if($display === 'Weekly'){
                                            echo '<span>'.esc_html__('Weekly', 'wpvivid-backuprestore').'</span></label><br>';
                                        }
                                        if($display === 'Fortnightly'){
                                            echo '<span>'.esc_html__('Fortnightly', 'wpvivid-backuprestore').'</span></label><br>';
                                        }
                                        if($display === 'Monthly'){
                                            echo '<span>'.esc_html__('Monthly', 'wpvivid-backuprestore').'</span></label><br>';
                                        }
                                    }
                                    else{
                                        echo '<p>Warning: Unable to set '.esc_html($display).' backup schedule</p>';
                                    }
                                }
                                echo '<label>';
                                echo '<div style="float: left;">';
                                echo '<input type="radio" disabled />';
                                echo '<span class="wpvivid-element-space-right" style="color: #ddd;">';esc_html_e('Custom', 'wpvivid-backuprestore');echo '</span>';
                                echo '</div>';
                                echo '<div style="float: left; height: 32px; line-height: 32px;">';
                                echo '<span class="wpvivid-feature-pro">';
                                echo '<a href="https://docs.wpvivid.com/wpvivid-backup-pro-customize-start-time.html" style="text-decoration: none; margin-top: 10px;">';esc_html_e('Pro feature: learn more', 'wpvivid-backuprestore');echo '</a>';
                                echo '</span>';
                                echo '</div>';
                                echo '</label><br>';
                                ?>
                            </fieldset>
                        </div>
                    </div>
                </div>
                <div class="postbox schedule-tab-block" id="wpvivid_schedule_backup_type">
                    <div>
                        <div>
                            <fieldset>
                                <legend class="screen-reader-text"><span>input type="radio"</span></legend>
                                <?php
                                echo '<label>';
                                echo '<input type="radio" option="schedule" name="backup_type" value="files+db"/>';
                                echo '<span>'.esc_html__('Database + Files (WordPress Files)', 'wpvivid-backuprestore').'</span>';
                                echo '</label><br>';

                                echo '<label>';
                                echo '<input type="radio" option="schedule" name="backup_type" value="files"/>';
                                echo '<span>'.esc_html__('WordPress Files (Exclude Database)', 'wpvivid-backuprestore').'</span>';
                                echo '</label><br>';

                                echo '<label>';
                                echo '<input type="radio" option="schedule" name="backup_type" value="db"/>';
                                echo '<span>'.esc_html__('Only Database', 'wpvivid-backuprestore').'</span>';
                                echo '</label><br>';

                                echo '<label>';
                                echo '<div style="float: left;">';
                                echo '<input type="radio" disabled />';
                                echo '<span class="wpvivid-element-space-right" style="color: #ddd;">'.esc_html__('Custom', 'wpvivid-backuprestore').'</span>';
                                echo '</div>';
                                echo '<div style="float: left; height: 32px; line-height: 32px;">';
                                echo '<span class="wpvivid-feature-pro">';
                                echo '<a href="https://docs.wpvivid.com/wpvivid-backup-pro-customize-what-to-backup-for-schedule.html" style="text-decoration: none;">'.esc_html__('Pro feature: learn more', 'wpvivid-backuprestore').'</a>';
                                echo '</span>';
                                echo '</div>';
                                echo '</label><br>';
                                ?>
                            </fieldset>
                        </div>
                        <div style="clear:both;"></div>
                    </div>
                </div>
                <div class="postbox schedule-tab-block" id="wpvivid_schedule_remote_storage">
                    <div id="wpvivid_schedule_backup_local_remote">
                        <?php
                        $schedule=WPvivid_Schedule::get_schedule();
                        $backup_local = 'checked';
                        $backup_remote = '';
                        if($schedule['enable'] == true)
                        {
                            if($schedule['backup']['remote'] === 1)
                            {
                                $backup_local = '';
                                $backup_remote = 'checked';
                            }
                            else{
                                $backup_local = 'checked';
                                $backup_remote = '';
                            }
                        }
                        echo '<fieldset>
                   <label title="">
                        <input type="radio" option="schedule" name="save_local_remote" value="local" '.esc_attr($backup_local).' />
                        <span>'.esc_html__( 'Save backups on localhost (web server)', 'wpvivid-backuprestore' ).'</span>
                   </label><br>
                   <label title="">
                        <input type="radio" option="schedule" name="save_local_remote" value="remote" '.esc_attr($backup_remote).' />
                        <span>'.esc_html__( 'Send backups to remote storage (You can choose whether to keep the backup in localhost after it is uploaded to cloud storage in Settings.)', 'wpvivid-backuprestore' ).'</span>
                   </label>
                   <label style="display: none;">
                        <input type="checkbox" option="schedule" name="lock" value="0" />
                   </label>
                   </fieldset>';
                        ?>
                    </div>
                    <div id="schedule_upload_storage" style="cursor:pointer;" title="<?php esc_html_e('Highlighted icon illuminates that you have choosed a remote storage to store backups', 'wpvivid-backuprestore'); ?>">
                        <?php
                        $remoteslist=WPvivid_Setting::get_all_remote_options();
                        $default_remote_storage=array();
                        foreach ($remoteslist['remote_selected'] as $value) {
                            $default_remote_storage[]=$value;
                        }
                        $remote_storage_type=array();
                        foreach ($remoteslist as $key=>$value)
                        {
                            if(in_array($key, $default_remote_storage))
                            {
                                $remote_storage_type[]=$value['type'];
                            }
                        }

                        $remote=array();
                        $remote=apply_filters('wpvivid_remote_pic', $remote);
                        if(is_array($remote))
                        {
                            foreach ($remote as $key => $value) {
                                $title = $value['title'];
                                if (in_array($key, $remote_storage_type)) {
                                    $pic = $value['selected_pic'];
                                } else {
                                    $pic = $value['default_pic'];
                                }
                                $url = apply_filters('wpvivid_get_wpvivid_pro_url', WPVIVID_PLUGIN_URL, $key);
                                echo '<img  src="' . esc_url($url . $pic) . '" style="vertical-align:middle; " title="' . esc_attr($title) . '"/>';
                            }
                            echo '<img onclick="wpvivid_click_switch_page(\'wrap\', \'wpvivid_tab_remote_storage\', true);" src="'.esc_url(WPVIVID_PLUGIN_URL.'/admin/partials/images/add-storages.png').'" style="vertical-align:middle;" title="'.esc_attr__('Add a storage', 'wpvivid-backuprestore').'"/>';
                        }
                        ?>
                    </div>
                </div>
                <div class="postbox schedule-tab-block">
                    <div style="float:left; color: #ddd; margin-right: 10px;">
                        <?php esc_html_e('+ Add another schedule', 'wpvivid-backuprestore'); ?>
                    </div>
                    <span class="wpvivid-feature-pro">
                        <a href="https://docs.wpvivid.com/wpvivid-backup-pro-creating-schedules.html"><?php esc_html_e('Pro feature: learn more', 'wpvivid-backuprestore'); ?></a>
                    </span>
                </div>
            </div>
        </td>
    </tr>
    <script>
        <?php
        do_action('wpvivid_schedule_do_js');
        ?>
    </script>
    <?php
}

function wpvivid_check_schedule_type($display)
{
    $schedule_type = array(
        'wpvivid_12hours'       =>  '12Hours',
        'twicedaily'             =>  '12Hours',
        'wpvivid_daily'         =>   'Daily',
        'daily'                  =>   'Daily',
        'onceday'                =>   'Daily',
        'wpvivid_weekly'        =>   'Weekly',
        'weekly'                 =>   'Weekly',
        'wpvivid_fortnightly'  =>   'Fortnightly',
        'fortnightly'           =>   'Fortnightly',
        'wpvivid_monthly'      =>   'Monthly',
        'monthly'               =>    'Monthly',
        'montly'                =>    'Monthly'
    );
        $schedules = wp_get_schedules();
        $check_res = false;
        $ret = array();
        foreach ($schedule_type as $key => $value){
            if($value == $display){
                if(isset($schedules[$key])){
                    $check_res = true;
                    $ret['type']=$key;
                    break;
                }
            }
        }
        $ret['result']=$check_res;
        return $ret;
}

function wpvivid_schedule_do_js()
{
    $schedule=WPvivid_Schedule::get_schedule();
    if($schedule['enable'] == true)
    {
        ?>
        jQuery("#wpvivid_schedule_enable").prop('checked', true);
        <?php
        if($schedule['backup']['remote'] === 1)
        {
            $schedule_remote='remote';
        }
        else{
            $schedule_remote='local';
        }
    }
    else{
        $schedule['recurrence']='wpvivid_daily';
        $schedule['backup']['backup_files']='files+db';
        $schedule_remote='local';
    }
    ?>
    jQuery("input:radio[value='<?php echo esc_attr($schedule['recurrence'])?>']").prop('checked', true);
    jQuery("input:radio[value='<?php echo esc_attr($schedule['backup']['backup_files'])?>']").prop('checked', true);
    jQuery("input:radio[name='save_local_remote'][value='remote']").click(function()
    {
    <?php
    $remote_id_array = WPvivid_Setting::get_user_history('remote_selected');
    $remote_id = '';
    foreach ($remote_id_array as $value)
    {
        $remote_id = $value;
    }
    if(empty($remote_id))
    {
        ?>
        alert("<?php esc_html_e('There is no default remote storage configured. Please set it up first.', 'wpvivid-backuprestore'); ?>");
        jQuery("input:radio[name='save_local_remote'][value='local']").prop('checked', true);
        <?php
    }
    ?>
    });
    <?php
}

add_action('wpvivid_schedule_add_cell','wpvivid_schedule_settings',11);
add_action('wpvivid_schedule_do_js','wpvivid_schedule_do_js',10);
?>

admin/partials/wpvivid-remote-storage-page-display.php000064400000051245151327705670017210 0ustar00<?php

function wpvivid_add_tab_storage_list()
{
    ?>
    <a href="#" id="wpvivid_tab_storage_list" class="nav-tab storage-nav-tab nav-tab-active" onclick="switchstorageTabs(event,'page-storage-list','page-storage-list')"><?php esc_html_e('Storages', 'wpvivid-backuprestore'); ?></a>
    <?php
}

function wpvivid_add_tab_storage_edit()
{
    ?>
    <a href="#" id="wpvivid_tab_storage_edit" class="nav-tab storage-nav-tab delete" onclick="switchstorageTabs(event,'page-storage_edit','page-storage_edit')" style="display: none;">
        <div id="wpvivid_tab_storage_edit_text" style="margin-right: 15px;"><?php esc_html_e('Storage Edit', 'wpvivid-backuprestore'); ?></div>
        <div class="nav-tab-delete-img">
            <img src="<?php echo esc_url(plugins_url( 'images/delete-tab.png', __FILE__ )); ?>" style="vertical-align:middle; cursor:pointer;" onclick="wpvivid_close_tab(event, 'wpvivid_tab_storage_edit', 'storage', 'wpvivid_tab_storage_list');" />
        </div>
    </a>
    <?php
}

function wpvivid_add_page_storage_list()
{
    ?>
    <div class="storage-tab-content wpvivid_tab_storage_list" id="page-storage-list">
        <div style="margin-top:10px;"><p><strong><?php esc_html_e('Please choose one storage to save your backups (remote storage)', 'wpvivid-backuprestore'); ?></strong></p></div>
        <div class="schedule-tab-block"></div>
        <div class="">
            <table class="widefat">
                <thead>
                <tr>
                    <th></th>
                    <th></th>
                    <th><?php esc_html_e( 'Storage Provider', 'wpvivid-backuprestore' ); ?></th>
                    <th class="row-title"><?php esc_html_e( 'Remote Storage Alias', 'wpvivid-backuprestore' ); ?></th>
                    <th><?php esc_html_e( 'Actions', 'wpvivid-backuprestore' ); ?></th>
                </tr>
                </thead>
                <tbody class="wpvivid-remote-storage-list" id="wpvivid_remote_storage_list">
                <?php
                $remoteslist=WPvivid_Setting::get_all_remote_options();
                $default_remote_storage='';
                foreach ($remoteslist['remote_selected'] as $value) {
                    $default_remote_storage=$value;
                }
                $i=1;
                foreach ($remoteslist as $key=>$value)
                {
                    if($key === 'remote_selected')
                    {
                        continue;
                    }
                    if ($key === $default_remote_storage)
                    {
                        $check_status = 'checked';
                    }
                    else
                    {
                        $check_status='';
                    }
                    $storage_type = $value['type'];
                    $storage_type=apply_filters('wpvivid_storage_provider_tran', $storage_type);
                   echo '<tr>
                <td>'.esc_html($i++).'</td>
                <td><input type="checkbox" name="remote_storage" value="'.esc_attr($key).'" '.esc_attr($check_status).' /></td>
                <td>'.esc_html($storage_type).'</td>
                <td class="row-title"><label for="tablecell">'.esc_html($value['name']).'</label></td>
                <td>
                    <div style="float: left;"><img src="'.esc_url(WPVIVID_PLUGIN_URL.'/admin/partials/images/Edit.png').'" onclick="click_retrieve_remote_storage(\''.esc_attr($key).'\',\''.esc_attr($value['type']).'\',\''.esc_attr($value['name']).'\'
                    );" style="vertical-align:middle; cursor:pointer;" title="'. esc_html__('Edit the remote storage', 'wpvivid-backuprestore') .'"/></div>
                    <div><img src="'.esc_url(WPVIVID_PLUGIN_URL.'/admin/partials/images/Delete.png').'" onclick="wpvivid_delete_remote_storage(\''.esc_attr($key).'\'
                    );" style="vertical-align:middle; cursor:pointer;" title="'. esc_html__('Remove the remote storage', 'wpvivid-backuprestore') .'"/></div>
                </td>
                </tr>';
                }
                ?>
                </tbody>
                <tfoot>
                <tr>
                    <th colspan="5" class="row-title"><input class="button-primary" id="wpvivid_set_default_remote_storage" type="submit" name="choose-remote-storage" value="<?php esc_attr_e( 'Save Changes', 'wpvivid-backuprestore' ); ?>" /></th>
                </tr>
                </tfoot>
            </table>
        </div>
    </div>
    <script>
        jQuery('input[option=add-remote]').click(function(){
            var storage_type = jQuery(".storage-providers-active").attr("remote_type");
            wpvivid_add_remote_storage(storage_type);
            wpvivid_settings_changed = false;
        });

        jQuery('#wpvivid_set_default_remote_storage').click(function(){
            wpvivid_set_default_remote_storage();
            wpvivid_settings_changed = false;
        });

        /**
         * Add remote storages to the list
         *
         * @param action        - The action to add or test a remote storage
         * @param storage_type  - Remote storage types (Amazon S3, SFTP and FTP server)
         */
        function wpvivid_add_remote_storage(storage_type)
        {
            var remote_from = wpvivid_ajax_data_transfer(storage_type);
            var ajax_data;
            ajax_data = {
                'action': 'wpvivid_add_remote',
                'remote': remote_from,
                'type': storage_type
            };
            jQuery('input[option=add-remote]').css({'pointer-events': 'none', 'opacity': '0.4'});
            jQuery('#wpvivid_remote_notice').html('');
            wpvivid_post_request(ajax_data, function (data)
            {
                try
                {
                    var jsonarray = jQuery.parseJSON(data);
                    if (jsonarray.result === 'success')
                    {
                        jQuery('input[option=add-remote]').css({'pointer-events': 'auto', 'opacity': '1'});
                        jQuery('input:text[option='+storage_type+']').each(function(){
                            jQuery(this).val('');
                        });
                        jQuery('input:password[option='+storage_type+']').each(function(){
                            jQuery(this).val('');
                        });
                        wpvivid_handle_remote_storage_data(data);
                    }
                    else if (jsonarray.result === 'failed')
                    {
                        jQuery('#wpvivid_remote_notice').html(jsonarray.notice);
                        jQuery('input[option=add-remote]').css({'pointer-events': 'auto', 'opacity': '1'});
                    }
                }
                catch (err)
                {
                    alert(err);
                    jQuery('input[option=add-remote]').css({'pointer-events': 'auto', 'opacity': '1'});
                }

            }, function (XMLHttpRequest, textStatus, errorThrown)
            {
                var error_message = wpvivid_output_ajaxerror('adding the remote storage', textStatus, errorThrown);
                alert(error_message);
                jQuery('input[option=add-remote]').css({'pointer-events': 'auto', 'opacity': '1'});
            });
        }

        function wpvivid_edit_remote_storage() {
            var data_tran = 'edit-'+wpvivid_editing_storage_type;
            var remote_data = wpvivid_ajax_data_transfer(data_tran);
            var ajax_data;
            ajax_data = {
                'action': 'wpvivid_edit_remote',
                'remote': remote_data,
                'id': wpvivid_editing_storage_id,
                'type': wpvivid_editing_storage_type
            };
            jQuery('#wpvivid_remote_notice').html('');
            wpvivid_post_request(ajax_data, function(data){
                try {
                    var jsonarray = jQuery.parseJSON(data);
                    if (jsonarray.result === 'success') {
                        jQuery('#wpvivid_tab_storage_edit').hide();
                        wpvivid_click_switch_page('storage', 'wpvivid_tab_storage_list', true);
                        wpvivid_handle_remote_storage_data(data);
                    }
                    else if (jsonarray.result === 'failed') {
                        jQuery('#wpvivid_remote_notice').html(jsonarray.notice);
                    }
                }
                catch(err){
                    alert(err);
                }
            },function(XMLHttpRequest, textStatus, errorThrown) {
                var error_message = wpvivid_output_ajaxerror('editing the remote storage', textStatus, errorThrown);
                alert(error_message);
            });
        }

        /**
         * Set a default remote storage for backups.
         */
        function wpvivid_set_default_remote_storage(){
            var remote_storage = new Array();
            //remote_storage[0] = jQuery("input[name='remote_storage']:checked").val();
            jQuery.each(jQuery("input[name='remote_storage']:checked"), function()
            {
                remote_storage.push(jQuery(this).val());
            });

            var ajax_data = {
                'action': 'wpvivid_set_default_remote_storage',
                'remote_storage': remote_storage
            };
            jQuery('#wpvivid_remote_notice').html('');
            wpvivid_post_request(ajax_data, function(data){
                wpvivid_handle_remote_storage_data(data);
            }, function(XMLHttpRequest, textStatus, errorThrown) {
                var error_message = wpvivid_output_ajaxerror('setting up the default remote storage', textStatus, errorThrown);
                alert(error_message);
            });
        }

        jQuery('#wpvivid_remote_storage_list').on("click", "input", function(){
            var check_status = true;
            if(jQuery(this).prop('checked') === true){
                check_status = true;
            }
            else {
                check_status = false;
            }
            jQuery('input[name="remote_storage"]').prop('checked', false);
            if(check_status === true){
                jQuery(this).prop('checked', true);
            }
            else {
                jQuery(this).prop('checked', false);
            }
        });

        function wpvivid_delete_remote_storage(storage_id){
            var descript = '<?php esc_html_e('Deleting a remote storage will make it unavailable until it is added again. Are you sure to continue?', 'wpvivid-backuprestore'); ?>';
            var ret = confirm(descript);
            if(ret === true){
                var ajax_data = {
                    'action': 'wpvivid_delete_remote',
                    'remote_id': storage_id
                };
                wpvivid_post_request(ajax_data, function(data){
                    wpvivid_handle_remote_storage_data(data);
                },function(XMLHttpRequest, textStatus, errorThrown) {
                    var error_message = wpvivid_output_ajaxerror('deleting the remote storage', textStatus, errorThrown);
                    alert(error_message);
                });
            }
        }

        function wpvivid_handle_remote_storage_data(data){
            var i = 0;
            try {
                var jsonarray = jQuery.parseJSON(data);
                if (jsonarray.result === 'success') {
                    jQuery('#wpvivid_remote_storage_list').html('');
                    jQuery('#wpvivid_remote_storage_list').append(jsonarray.html);
                    jQuery('#upload_storage').html(jsonarray.pic);
                    jQuery('#schedule_upload_storage').html(jsonarray.pic);
                    jQuery('#wpvivid_out_of_date_remote_path').html(jsonarray.dir);
                    jQuery('#wpvivid_schedule_backup_local_remote').html(jsonarray.local_remote);
                    wpvivid_control_remote_storage(jsonarray.remote_storage);
                    jQuery('#wpvivid_remote_notice').html(jsonarray.notice);
                }
                else if(jsonarray.result === 'failed'){
                    alert(jsonarray.error);
                }
            }
            catch(err){
                alert(err);
            }
        }

        function wpvivid_control_remote_storage(has_remote){
            if(!has_remote){
                if(jQuery("input:radio[name='save_local_remote'][value='remote']").prop('checked')) {
                    alert("<?php esc_html_e('There is no default remote storage configured. Please set it up first.', 'wpvivid-backuprestore'); ?>");
                    jQuery("input:radio[name='save_local_remote'][value='local']").prop('checked', true);
                }
            }
        }

        function click_retrieve_remote_storage(id,type,name)
        {
            wpvivid_editing_storage_id = id;
            jQuery('.remote-storage-edit').hide();
            jQuery('#wpvivid_tab_storage_edit').show();
            jQuery('#wpvivid_tab_storage_edit_text').html(name);
            wpvivid_editing_storage_type=type;
            jQuery('#remote_storage_edit_'+wpvivid_editing_storage_type).fadeIn();
            wpvivid_click_switch_page('storage', 'wpvivid_tab_storage_edit', true);

            var ajax_data = {
                'action': 'wpvivid_retrieve_remote',
                'remote_id': id
            };
            wpvivid_post_request(ajax_data, function(data)
            {
                try
                {
                    var jsonarray = jQuery.parseJSON(data);
                    if (jsonarray.result === 'success')
                    {
                        /*jQuery('input:text[option=edit-'+jsonarray.type+']').each(function(){
                            var key = jQuery(this).prop('name');
                            jQuery(this).val(jsonarray[key]);
                        });
                        jQuery('input:password[option=edit-'+jsonarray.type+']').each(function(){
                            var key = jQuery(this).prop('name');
                            jQuery(this).val(jsonarray[key]);
                        });*/
                        jQuery('input:checkbox[option=edit-'+jsonarray.type+']').each(function() {
                            var key = jQuery(this).prop('name');
                            var value;
                            if(jsonarray[key] == '0'){
                                value = false;
                            }
                            else{
                                value = true;
                            }
                            jQuery(this).prop('checked', value);
                        });
                    }
                    else
                    {
                        alert(jsonarray.error);
                    }
                }
                catch(err)
                {
                    alert(err);
                }
            },function(XMLHttpRequest, textStatus, errorThrown)
            {
                var error_message = wpvivid_output_ajaxerror('retrieving the remote storage', textStatus, errorThrown);
                alert(error_message);
            });
        }
    </script>
    <?php
}

function wpvivid_add_page_storage_edit()
{
    ?>
    <div class="storage-tab-content wpvivid_tab_storage_edit" id="page-storage_edit" style="display:none;">
        <div><?php do_action('wpvivid_edit_remote_page'); ?></div>
    </div>
    <script>
        jQuery('input[option=edit-remote]').click(function(){
            wpvivid_edit_remote_storage();
        });
    </script>
    <?php
}

function wpvivid_storage_list($html)
{
    $html='<h2 class="nav-tab-wrapper" style="padding-bottom:0!important;">';
    $html.='<a href="#" id="wpvivid_tab_storage_list" class="nav-tab storage-nav-tab nav-tab-active" onclick="switchstorageTabs(event,\'page-storage-list\',\'page-storage-list\')">'. __('Storages', 'wpvivid-backuprestore').'</a>';
    $html.='<a href="#" id="wpvivid_tab_storage_edit" class="nav-tab storage-nav-tab delete" onclick="switchstorageTabs(event,\'page-storage_edit\',\'page-storage_edit\')" style="display: none;">
        <div id="wpvivid_tab_storage_edit_text" style="margin-right: 15px;">'.__('Storage Edit', 'wpvivid-backuprestore').'</div>
        <div class="nav-tab-delete-img">
            <img src="'.esc_url(plugins_url( 'images/delete-tab.png', __FILE__ )).'" style="vertical-align:middle; cursor:pointer;" onclick="wpvivid_close_tab(event, \'wpvivid_tab_storage_edit\', \'storage\', \'wpvivid_tab_storage_list\');" />
        </div>
    </a>';
    $html.='</h2>';
    $html.='<div class="storage-tab-content wpvivid_tab_storage_list" id="page-storage-list">
        <div style="margin-top:10px;"><p><strong>'.__('Please choose one storage to save your backups (remote storage)', 'wpvivid-backuprestore').'</strong></p></div>
        <div class="schedule-tab-block"></div>
        <div class="">
            <table class="widefat">
                <thead>
                <tr>
                    <th></th>
                    <th></th>
                    <th>'. __( 'Storage Provider', 'wpvivid-backuprestore' ).'</th>
                    <th class="row-title">'. __( 'Remote Storage Alias', 'wpvivid-backuprestore' ).'</th>
                    <th>'. __( 'Actions', 'wpvivid-backuprestore' ).'</th>
                </tr>
                </thead>
                <tbody class="wpvivid-remote-storage-list" id="wpvivid_remote_storage_list">
                ';
    $html_list='';
    $html.= apply_filters('wpvivid_add_remote_storage_list', $html_list);
    $html.='</tbody><tfoot><tr>
            <th colspan="5" class="row-title"><input class="button-primary" id="wpvivid_set_default_remote_storage" type="submit" name="choose-remote-storage" value="'.esc_attr__( 'Save Changes', 'wpvivid-backuprestore' ).'" /></th>
            </tr></tfoot></table></div></div>';

    $html .= '<script>
            jQuery(\'#wpvivid_remote_storage_list\').on("click", "input", function(){
                var check_status = true;
                if(jQuery(this).prop(\'checked\') === true){
                     check_status = true;
                }
                else {
                    check_status = false;
                }
                jQuery(\'input[name = "remote_storage"]\').prop(\'checked\', false);
                if(check_status === true){
                    jQuery(this).prop(\'checked\', true);
                 }
                else {
                    jQuery(this).prop(\'checked\', false);
                }
            });
            </script>';
    return $html;
}

add_action('wpvivid_storage_add_tab', 'wpvivid_add_tab_storage_list', 10);
add_action('wpvivid_storage_add_tab', 'wpvivid_add_tab_storage_edit', 11);
add_action('wpvivid_storage_add_page', 'wpvivid_add_page_storage_list', 10);
add_action('wpvivid_storage_add_page', 'wpvivid_add_page_storage_edit', 11);
//add_filter('wpvivid_storage_list','wpvivid_storage_list',10);
?>



<script>
    function select_remote_storage(evt, storage_page_id)
    {
        var i, tablecontent, tablinks;
        tablinks = document.getElementsByClassName("storage-providers");
        for (i = 0; i < tablinks.length; i++) {
            tablinks[i].className = tablinks[i].className.replace("storage-providers-active", "");
        }
        evt.currentTarget.className += " storage-providers-active";

        jQuery(".storage-account-page").hide();
        jQuery("#"+storage_page_id).show();
    }
    function switchstorageTabs(evt,contentName,storage_page_id) {
        // Declare all variables
        var i, tabcontent, tablinks;

        // Get all elements with class="table-list-content" and hide them
        tabcontent = document.getElementsByClassName("storage-tab-content");
        for (i = 0; i < tabcontent.length; i++) {
            tabcontent[i].style.display = "none";
        }

        // Get all elements with class="table-nav-tab" and remove the class "nav-tab-active"
        tablinks = document.getElementsByClassName("storage-nav-tab");
        for (i = 0; i < tablinks.length; i++) {
            tablinks[i].className = tablinks[i].className.replace(" nav-tab-active", "");
        }

        // Show the current tab, and add an "storage-menu-active" class to the button that opened the tab
        document.getElementById(contentName).style.display = "block";
        evt.currentTarget.className += " nav-tab-active";

        var top = jQuery('#'+storage_page_id).offset().top-jQuery('#'+storage_page_id).height();
        jQuery('html, body').animate({scrollTop:top}, 'slow');
    }
</script>admin/partials/wpvivid-admin-display.php000064400000024750151327705670014432 0ustar00<?php
/**
 * Provide a admin area view for the plugin
 *
 * This file is used to markup the admin-facing aspects of the plugin.
 *
 * @link       https://wpvivid.com
 * @since      0.9.1
 *
 * @package    WPvivid
 * @subpackage WPvivid/admin/partials
 */

include_once WPVIVID_PLUGIN_DIR .'/admin/partials/wpvivid-backup-restore-page-display.php';
include_once WPVIVID_PLUGIN_DIR .'/admin/partials/wpvivid-remote-storage-page-display.php';
include_once WPVIVID_PLUGIN_DIR .'/admin/partials/wpvivid-settings-page-display.php';
include_once WPVIVID_PLUGIN_DIR .'/admin/partials/wpvivid-schedule-page-display.php';
include_once WPVIVID_PLUGIN_DIR .'/admin/partials/wpvivid-website-info-page-display.php';

if (!defined('WPVIVID_PLUGIN_DIR'))
{
    die;
}

global $wpvivid_plugin;
$schedule=WPvivid_Schedule::get_schedule();

do_action('show_notice');

?>

<?php

$page_array = array();
$page_array = apply_filters('wpvivid_add_tab_page', $page_array);
foreach ($page_array as $page_name){
    add_action('wpvivid_backuprestore_add_tab', $page_name['tab_func'], $page_name['index']);
    add_action('wpvivid_backuprestore_add_page', $page_name['page_func'], $page_name['index']);
}

?>

<div class="wrap">
    <h1><?php
        $plugin_display_name = 'WPvivid Backup Plugin';
        $plugin_display_name = apply_filters('wpvivid_display_pro_name', $plugin_display_name);
        esc_html_e('WPvivid Backup Plugin', 'wpvivid-backuprestore');
        ?></h1>
    <div id="wpvivid_backup_notice">
        <?php
        if($schedule['enable'] == true) {
            if($schedule['backup']['remote'] === 1)
            {
                $remoteslist=WPvivid_Setting::get_all_remote_options();
                $default_remote_storage='';
                foreach ($remoteslist['remote_selected'] as $value)
                {
                    $default_remote_storage=$value;
                }
                if($default_remote_storage == ''){
                    echo '<div class="notice notice-warning is-dismissible"><p>';
                    esc_html_e('Warning: There is no default remote storage available for the scheduled backups, please set up it first.', 'wpvivid-backuprestore');
                    echo '</p></div>';
                }
            }
        }
        ?>
    </div>
    <?php do_action('wpvivid_add_schedule_notice'); ?>
    <div id="wpvivid_remote_notice"></div>
</div>
<h2 class="nav-tab-wrapper wpvivid-custom-table-manager">
    <?php
    do_action('wpvivid_backuprestore_add_tab');
    ?>
</h2>
<div class="wrap" style="max-width:1720px;">
    <div id="poststuff" style="padding-top: 0;">
        <div id="post-body" class="metabox-holder columns-2">
            <div id="post-body-content">
                <div class="inside" style="margin-top:0;">
                    <?php
                    do_action('wpvivid_backuprestore_add_page');
                    ?>
                </div>
            </div>

            <div id="postbox-container-1" class="postbox-container">
                <div class="meta-box-sortables">
                    <?php
                    $html = '';
                    //echo apply_filters('wpvivid_add_side_bar' ,$html, true);
                    do_action('wpvivid_add_side_bar' ,$html, true);
                    ?>
                </div>
            </div>
        </div>
        <br class="clear">
    </div>
</div>

<script>
    function switchTabs(evt,contentName) {
        // Declare all variables
        var i, tabcontent, tablinks;

        // Get all elements with class="tabcontent" and hide them
        tabcontent = document.getElementsByClassName("wrap-tab-content");
        for (i = 0; i < tabcontent.length; i++) {
            tabcontent[i].style.display = "none";
        }

        // Get all elements with class="wrap-nav-tab" and remove the class "active"
        tablinks = document.getElementsByClassName("wrap-nav-tab");
        for (i = 0; i < tablinks.length; i++) {
            tablinks[i].className = tablinks[i].className.replace(" nav-tab-active", "");
        }

        // Show the current tab, and add an "nav-tab-active" class to the button that opened the tab
        document.getElementById(contentName).style.display = "block";
        evt.currentTarget.className += " nav-tab-active";
        jQuery( document ).trigger( 'wpvivid-switch-tabs', contentName );
        //nav-tab-active
    }
    function switchrestoreTabs(evt,contentName) {
        // Declare all variables
        var i, tabcontent, tablinks;

        // Get all elements with class="table-list-content" and hide them
        tabcontent = document.getElementsByClassName("backup-tab-content");
        for (i = 0; i < tabcontent.length; i++) {
            tabcontent[i].style.display = "none";
        }

        // Get all elements with class="table-nav-tab" and remove the class "nav-tab-active"
        tablinks = document.getElementsByClassName("backup-nav-tab");
        for (i = 0; i < tablinks.length; i++) {
            tablinks[i].className = tablinks[i].className.replace(" nav-tab-active", "");
        }

        // Show the current tab, and add an "storage-menu-active" class to the button that opened the tab
        document.getElementById(contentName).style.display = "block";
        evt.currentTarget.className += " nav-tab-active";
    }
    function switchlogTabs(evt,contentName) {
        // Declare all variables
        var i, tabcontent, tablinks;

        // Get all elements with class="table-list-content" and hide them
        tabcontent = document.getElementsByClassName("log-tab-content");
        for (i = 0; i < tabcontent.length; i++) {
            tabcontent[i].style.display = "none";
        }

        // Get all elements with class="table-nav-tab" and remove the class "nav-tab-active"
        tablinks = document.getElementsByClassName("log-nav-tab");
        for (i = 0; i < tablinks.length; i++) {
            tablinks[i].className = tablinks[i].className.replace(" nav-tab-active", "");
        }

        // Show the current tab, and add an "storage-menu-active" class to the button that opened the tab
        document.getElementById(contentName).style.display = "block";
        evt.currentTarget.className += " nav-tab-active";
    }
    function switchsettingTabs(evt,contentName) {
        // Declare all variables
        var i, tabcontent, tablinks;

        // Get all elements with class="table-list-content" and hide them
        tabcontent = document.getElementsByClassName("setting-tab-content");
        for (i = 0; i < tabcontent.length; i++) {
            tabcontent[i].style.display = "none";
        }

        // Get all elements with class="table-nav-tab" and remove the class "nav-tab-active"
        tablinks = document.getElementsByClassName("setting-nav-tab");
        for (i = 0; i < tablinks.length; i++) {
            tablinks[i].className = tablinks[i].className.replace(" nav-tab-active", "");
        }

        // Show the current tab, and add an "storage-menu-active" class to the button that opened the tab
        document.getElementById(contentName).style.display = "block";
        evt.currentTarget.className += " nav-tab-active";
    }
    function switchstorageTabs(remote_type,storage_page_id)
    {
        var i, tabcontent, tablinks,contentName;
        contentName='storage-page';
        // Get all elements with class="tabcontent" and hide them
        tabcontent = document.getElementsByClassName("wrap-tab-content");
        for (i = 0; i < tabcontent.length; i++) {
            tabcontent[i].style.display = "none";
        }

        // Get all elements with class="wrap-nav-tab" and remove the class "active"
        tablinks = document.getElementsByClassName("wrap-nav-tab");
        for (i = 0; i < tablinks.length; i++) {
            tablinks[i].className = tablinks[i].className.replace(" nav-tab-active", "");
        }

        // Show the current tab, and add an "nav-tab-active" class to the button that opened the tab
        document.getElementById(contentName).style.display = "block";
        jQuery('#wpvivid_tab_remote_storage').addClass('nav-tab-active');
        jQuery( document ).trigger( 'wpvivid-switch-tabs', contentName );
        start_select_remote_storage(remote_type,storage_page_id);

    }

    function start_select_remote_storage(remote_type, storage_page_id)
    {
        var i, tablecontent, tablinks;
        tablinks = document.getElementsByClassName("storage-providers");
        for (i = 0; i < tablinks.length; i++) {
            tablinks[i].className = tablinks[i].className.replace("storage-providers-active", "");
        }
        jQuery("div[remote_type='"+remote_type+"']").addClass('storage-providers-active');

        jQuery(".storage-account-page").hide();
        jQuery("#"+storage_page_id).show();
    }

    function wpvivid_getrequest()
    {
        wpvivid_click_switch_page('wrap', wpvivid_page_request, false);
    }

    function wpvivid_task_monitor()
    {
        setTimeout(function () {
            wpvivid_task_monitor();
        }, 120000);

        var ajax_data = {
            'action': 'wpvivid_task_monitor'
        };

        wpvivid_post_request(ajax_data, function (data)
        {
        },function (XMLHttpRequest, textStatus, errorThrown)
        {
        });
    }

    jQuery(document).ready(function ()
    {
        wpvivid_getrequest();
        //wpvivid_task_monitor();
        <?php
        $default_task_type = array();
        $default_task_type = apply_filters('wpvivid_get_task_type', $default_task_type);
        if(empty($default_task_type)){
        ?>
        //wpvivid_activate_cron();
        //wpvivid_manage_task();
        <?php
        }
        ?>

        <?php
           if (isset($_GET['main_tab']))
            {
                $tab=esc_html($_GET['main_tab']);

                if($tab=='storage')
                {
                    $sub_tab=isset($_GET['sub_tab'])?$_GET['sub_tab']:'googledrive';
                    $sub_page=isset($_GET['sub_page'])?$_GET['sub_page']:'storage_account_google_drive';
                    if($sub_tab === 'googledrive' || $sub_tab === 'dropbox' || $sub_tab === 'one_drive')
                    {
                        if($sub_page === 'storage_account_google_drive' || $sub_page === 'storage_account_dropbox' || $sub_page === 'storage_account_one_drive')
                        {
                            ?>
                            switchstorageTabs('<?php echo esc_attr($sub_tab); ?>','<?php echo esc_attr($sub_page) ?>');
                            <?php

                        }
                    }
                }
            }
        ?>
        //switchTabs(event,'storage-page')
    });

</script>admin/partials/images/unlocked.png000064400000002267151327705670013256 0ustar00�PNG


IHDRv��tEXtSoftwareAdobe ImageReadyq�e<xiTXtXML:com.adobe.xmp<?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?> <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.6-c132 79.159284, 2016/04/19-13:13:40        "> <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <rdf:Description rdf:about="" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmlns:stRef="http://ns.adobe.com/xap/1.0/sType/ResourceRef#" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmpMM:OriginalDocumentID="xmp.did:47c2336c-10d5-b044-9fab-6eca53f0688d" xmpMM:DocumentID="xmp.did:5DC7566A043211E99A7AEF2EE2706B57" xmpMM:InstanceID="xmp.iid:5DC75669043211E99A7AEF2EE2706B57" xmp:CreatorTool="Adobe Photoshop CC 2015.5 (Windows)"> <xmpMM:DerivedFrom stRef:instanceID="xmp.iid:47c2336c-10d5-b044-9fab-6eca53f0688d" stRef:documentID="xmp.did:47c2336c-10d5-b044-9fab-6eca53f0688d"/> </rdf:Description> </rdf:RDF> </x:xmpmeta> <?xpacket end="r"?>�'F��IDATx�b���?�@&���
��d,]L���c�_Ps� ~
Ŀ������@�Ļ��	j�V ����@�@��@lė������g ��$��7���F@�����߀�=�,vbA,��)���ّHyF(��=<�1(۔Ҭ����,��B�f{�',�\@�����W�� �7?�i}�\5S;;1��2G-v�(^�
�IEND�B`�admin/partials/images/stroage-google-drive(gray).png000064400000002701151327705670016474 0ustar00�PNG


IHDR;0��tEXtSoftwareAdobe ImageReadyq�e<&iTXtXML:com.adobe.xmp<?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?> <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.6-c132 79.159284, 2016/04/19-13:13:40        "> <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <rdf:Description rdf:about="" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmlns:stRef="http://ns.adobe.com/xap/1.0/sType/ResourceRef#" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmpMM:DocumentID="xmp.did:C981BBDB01C911E98D1FC071E6457FD2" xmpMM:InstanceID="xmp.iid:C981BBDA01C911E98D1FC071E6457FD2" xmp:CreatorTool="Adobe Photoshop CC 2014 (Windows)"> <xmpMM:DerivedFrom stRef:instanceID="xmp.iid:D96A48ABD5C211E8A49EDE26ADB27049" stRef:documentID="xmp.did:D96A48ACD5C211E8A49EDE26ADB27049"/> </rdf:Description> </rdf:RDF> </x:xmpmeta> <?xpacket end="r"?>�z0$1IDATx�b���?�@&�#�bcݺu8���
qqqm���$`�k �:w��\
���H�q���-���w��:Q n�JP}���@���ׯlD��fdd�G��@K��$��?~����<fN����@l�,���7UE@���!dY�-��F���70�o�@�9��q%�`�&4#`�?%`�W�d1з�@��&hB�F��ˀ�V$��}@̎�D��A������ '�b�o]�Tq>�j��v�k1�Rb��?~��>����n��D��,�|�Ě��]H�& ~E@�g 0���ӧO���@�G��o7!}�����Uޡ���K��[
An'�W�k1j' %��//�w���bD=cdd$d�	6�[�����_dbb
�g*ͨZl�����0�-�>�u�e �İw�S��Z�
�qҕ+W~c�N@ˏ�Eh�n133kS��	@K�*2Ae��K���BK�#�-N���U 6�`J����4�o�[I�&��������{�I0��$���챱�p�IEND�B`�admin/partials/images/storage-dropbox(gray).png000064400000003050151327705670015564 0ustar00�PNG


IHDR;0��tEXtSoftwareAdobe ImageReadyq�e<&iTXtXML:com.adobe.xmp<?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?> <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.6-c132 79.159284, 2016/04/19-13:13:40        "> <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <rdf:Description rdf:about="" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmlns:stRef="http://ns.adobe.com/xap/1.0/sType/ResourceRef#" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmpMM:DocumentID="xmp.did:1470AF6201C811E9B6FEAB4D7E055F12" xmpMM:InstanceID="xmp.iid:1470AF6101C811E9B6FEAB4D7E055F12" xmp:CreatorTool="Adobe Photoshop CC 2014 (Windows)"> <xmpMM:DerivedFrom stRef:instanceID="xmp.iid:5333288ED5C011E88F93D6A5992D4330" stRef:documentID="xmp.did:5333288FD5C011E88F93D6A5992D4330"/> </rdf:Description> </rdf:RDF> </x:xmpmeta> <?xpacket end="r"?>YX���IDATx��MHTQ�gď&w)�5�B�#����BS���0.�E1+$j�G���"W��,7�I��0��0`��?�x��}������?��s�7�Lz�²<gd�/pv�!��|�Q�,�赁��*���:�K`
��`<^������+}��Hk�e�_�����5�Y�}l��I��&�/��ŚA�QY5e�ʊ�=j+���V��Q
�H��+gT>D~����q�~y�*�,�
~h:��W��X��*ߝj���-��U`��	ԁp�>�:jXm���n�z4��M�>pl,��԰O�
�T'���%PV���&�o@=5N����;�“>r�����B�n��r��I�.�a�]�!h�gaȒ`Tz��;�v��bZ����ѭ�{��S	�2��}.��(0�F��gd��|p~q+��A�O��,:�H�ld!��٦�Ej��Nu�����j[�<c�m/8�-Ejm�V<��T�
���Uf��f���EX���6
�;]ܮ>!Τ��]C��ON�2�c%�`_���ƾ���;���+	f�0�
frٓ\[kx�S�`�����=?�P��������	��	[�M2��������z�3��>���!�MG�{��00͛��9�$IEND�B`�admin/partials/images/locked.png000064400000002300151327705670012677 0ustar00�PNG


IHDRv��tEXtSoftwareAdobe ImageReadyq�e<xiTXtXML:com.adobe.xmp<?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?> <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.6-c132 79.159284, 2016/04/19-13:13:40        "> <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <rdf:Description rdf:about="" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmlns:stRef="http://ns.adobe.com/xap/1.0/sType/ResourceRef#" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmpMM:OriginalDocumentID="xmp.did:47c2336c-10d5-b044-9fab-6eca53f0688d" xmpMM:DocumentID="xmp.did:E2B0C2B0042E11E98421E35E2516AA3B" xmpMM:InstanceID="xmp.iid:E2B0C2AF042E11E98421E35E2516AA3B" xmp:CreatorTool="Adobe Photoshop CC 2015.5 (Windows)"> <xmpMM:DerivedFrom stRef:instanceID="xmp.iid:47c2336c-10d5-b044-9fab-6eca53f0688d" stRef:documentID="xmp.did:47c2336c-10d5-b044-9fab-6eca53f0688d"/> </rdf:Description> </rdf:RDF> </x:xmpmeta> <?xpacket end="r"?>�?U�IDATx�b���?�@&���
��d,]L����c�OP��9 ���b4���A��	�]@<���b-T�c4��U@�����'T��7B�@�Imۂ�7#Y�e7C�lh�lP�>��hjh���c{G������o�l���C务j���ġ�P�G��ĉ4)2�@|��D/�A�Qj,�Y��N�ih����O��cFjW�����Q������.6����IEND�B`�admin/partials/images/storage-digitalocean.png000064400000002577151327705670015543 0ustar00�PNG


IHDR;0��tEXtSoftwareAdobe ImageReadyq�e<&iTXtXML:com.adobe.xmp<?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?> <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.6-c132 79.159284, 2016/04/19-13:13:40        "> <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <rdf:Description rdf:about="" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmlns:stRef="http://ns.adobe.com/xap/1.0/sType/ResourceRef#" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmpMM:DocumentID="xmp.did:56AC76863E5611E9B847C7B01A9A1ACF" xmpMM:InstanceID="xmp.iid:56AC76853E5611E9B847C7B01A9A1ACF" xmp:CreatorTool="Adobe Photoshop CC 2014 (Windows)"> <xmpMM:DerivedFrom stRef:instanceID="xmp.iid:E0A2516108DF11E9BD0BB4B775E93A54" stRef:documentID="xmp.did:E0A2516208DF11E9BD0BB4B775E93A54"/> </rdf:Description> </rdf:RDF> </x:xmpmeta> <?xpacket end="r"?>�l�IDATx��K(Da��z�"y�(E��D�dAy$E2;;��؈�,�X�+���(IY�aʳaAyD��F^y���s����~3�͜��=瞯3s߹���2�C�~�@����|���?���L�Wp�x�����v#u�
�	2U�
����0�A��3���v�R�H���%Ѡ��s��v0O�tP݋@5��
l`A6p
��lS�<p�QP
&Ac����Ɖ���M�fAPM6��[h�i�Z���7����j�8[�L�bA�?u6�,�c�l����~,���э2�Ub�P�Er�����]��4�ݛ9楐�Leb\r> ���e����2q�q+<��"�\��m����9�'N�U�m\���8fIh��i+0t��e�	|Z(�F�*U��T��
�U9B5�wZe��A���O�k?�a�A��)��N_����݋�6A�o�ek����uEn�
��3�J�>���G���_�O|�IEND�B`�admin/partials/images/storage-dropbox.png000064400000010156151327705670014565 0ustar00�PNG


IHDR;0��
CiCCPICC profilexڝSwX��>�eVB��l�"#��Y��a�@Ņ�
V�HU�
H���(�gA��Z�U\8�ܧ�}z��������y��&��j9R�<:��OH�ɽ�H� ���g��yx~t�?��op�.$���P&W ��"��R�.T���S�d
�ly|B"�
��I>ة��آ���(G$@�`U�R,����@".���Y�2G��v�X�@`��B,� 8C� L�0ҿ�_p��H�˕͗K�3���w����!��l�Ba)f	�"���#H�L����8?������f�l��Ţ�k�o">!����N���_���p��u�k�[�Vh�]3�	�Z
�z��y8�@��P�<
�%b��0�>�3�o�~��@��z�q�@������qanv�R���B1n��#�Dž��)��4�\,��X��P"M�y�R�D!ɕ��2���	�w
��O�N���l�~��X�v@~�-��g42y�����@+͗����\��L�D��*�A�������aD@$�<B�
��AT�:��������18
��\��p`����	A�a!:�b��"���"aH4��� �Q"��r��Bj�]H#�-r9�\@���� 2����G1���Q�u@���Ơs�t4]���k��=�����K�ut}��c��1f��a\��E`�X&�c�X5V�5cX7v��a�$���^��l���GXLXC�%�#��W	��1�'"��O�%z��xb:��XF�&�!!�%^'_�H$ɒ�N
!%�2IIkH�H-�S�>�i�L&�m������ �����O�����:ň�L	�$R��J5e?���2B���Qͩ����:�ZIm�vP/S��4u�%͛Cˤ-��Кigi�h/�t�	݃E�З�k�����w
�
��Hb(k{��/�L�ӗ��T0�2�g��oUX*�*|���:�V�~��TUsU?�y�T�U�^V}�FU�P�	��թU��6��RwR�P�Q_��_���c
���F��H�Tc���!�2e�XB�rV�,k�Mb[���Lv�v/{LSCs�f�f�f��q�Ʊ��9ٜJ�!�
�{--?-��j�f�~�7�zھ�b�r�����up�@�,��:m:�u	�6�Q����u��>�c�y�	�����G�m������7046�l18c�̐c�k�i�����h���h��I�'�&�g�5x>f�ob�4�e�k<abi2ۤĤ��)͔k�f�Ѵ�t���,ܬج��9՜k�a�ټ����E��J�6�ǖږ|��M����V>VyV�V׬I�\�,�m�WlPW��:�˶�����v�m���)�)�Sn�1��
���9�a�%�m����;t;|rtu�vlp���4éĩ��Wgg�s��5�K���v�Sm���n�z˕��ҵ�����ܭ�m���=�}��M.��]�=�A��X�q�㝧�����/^v^Y^��O��&��0m���[��{`:>=e���>�>�z�����"�=�#~�~�~���;������y��N`������k��5��/>B	
Yr�o���c3�g,����Z�0�&L�����~o��L�̶��Gl��i��})*2�.�Q�Stqt�,֬�Y�g��񏩌�;�j�rvg�jlRlc웸�����x��E�t$	�����=��s�l�3��T�tc��ܢ����˞w<Y5Y�|8����?� BP/O�nM򄛅OE����Q���J<��V��8�;}C�h�OFu�3	OR+y���#�MVD�ެ��q�-9�����R
i��+�0�(�Of++�
�y�m�����#�s��l�Lѣ�R�PL/�+x[[x�H�HZ�3�f��#�|���P���ظxY��"�E�#�Sw.1]R�dxi��}�h˲��P�XRU�jy��R�ҥ�C+�W4�����n��Z�ca�dU�j��[V*�_�p�����F���WN_�|�ym���J����H��n��Y��J�jA�І�
���_mJ�t�zj��ʹ���5a5�[̶���6��z�]�V������&�ֿ�w{��;��켵+xWk�E}�n��ݏb���~ݸGwOŞ�{�{�E��jtolܯ���	mR6�H:p囀oڛ�w�pZ*�A�'ߦ|{�P������ߙ���Hy+�:�u�-�m�=���茣�^G���~�1�cu�5�W���(=�䂓�d���N?=ԙ�y�L��k]Q]�gCϞ?t�L�_�����]�p�"�b�%�K�=�=G~p��H�[o�e���W<�t�M�;����j��s��.]�y�����n&��%���v��w
�L�]z�x����������e�m�`�`��Y�	�����Ӈ��G�G�#F#���
��dΓ᧲���~V�y�s����K�X�����Ͽ�y��r﫩�:�#���y=���}���ǽ�(�@�P��cǧ�O�>�|��/���9%tEXtSoftwareAdobe ImageReadyq�e<&iTXtXML:com.adobe.xmp<?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?> <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.6-c014 79.156797, 2014/08/20-09:53:02        "> <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <rdf:Description rdf:about="" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmlns:stRef="http://ns.adobe.com/xap/1.0/sType/ResourceRef#" xmp:CreatorTool="Adobe Photoshop CC 2014 (Windows)" xmpMM:InstanceID="xmp.iid:5333288ED5C011E88F93D6A5992D4330" xmpMM:DocumentID="xmp.did:5333288FD5C011E88F93D6A5992D4330"> <xmpMM:DerivedFrom stRef:instanceID="xmp.iid:5333288CD5C011E88F93D6A5992D4330" stRef:documentID="xmp.did:5333288DD5C011E88F93D6A5992D4330"/> </rdf:Description> </rdf:RDF> </x:xmpmeta> <?xpacket end="r"?>�B��IDATx��MHTQ�g�2kRⶈ> ��KZ�Rm�Z�4Ѣ]�"�E8��ZD�&�j�i�v��Ȭ6�PD���P(�t�_��7�λ���p߹�s��s&^,cKa�bKd�;�����oD��7�oG5�׊!�Hl�✈[|�<���D���f�oő�xF����Z�2�E��8��^���ޱ��"' ǘ͌�]4�]�S�O��Y6�~h���f�i[�_b���gq�������W#~��{�G��yqUl�����;������Mb<4�R$�I1k��Y�����:�V��(��b�x��:��N��hGsa�t̉{b�x�����}(��1W�w�+�.n���#�Jq�9]h�67�:�0�Zd9��2A۸��c�+	�Pn��m$��.0^Gz���z�O�Q��h�x�&%S�8L�=�#�&�Q8���Y����"�f�O��x��2VF�褐�s���Q�:OEI��6)z�E��>q�LZRkZ�J걩(���U��o�1t�F��#��ۙ��$�N�(�&1�����}��C<c��ً�N\��_�`���d��jz.�k[<o�+P��6{?!���
u�����joM�����n�l�I\q�j��iv�®^�펉/��Xd?>AG���-�0��A]�ZIEND�B`�admin/partials/images/storage-amazon-s3.png000064400000007756151327705670014734 0ustar00�PNG


IHDR;0��
CiCCPICC profilexڝSwX��>�eVB��l�"#��Y��a�@Ņ�
V�HU�
H���(�gA��Z�U\8�ܧ�}z��������y��&��j9R�<:��OH�ɽ�H� ���g��yx~t�?��op�.$���P&W ��"��R�.T���S�d
�ly|B"�
��I>ة��آ���(G$@�`U�R,����@".���Y�2G��v�X�@`��B,� 8C� L�0ҿ�_p��H�˕͗K�3���w����!��l�Ba)f	�"���#H�L����8?������f�l��Ţ�k�o">!����N���_���p��u�k�[�Vh�]3�	�Z
�z��y8�@��P�<
�%b��0�>�3�o�~��@��z�q�@������qanv�R���B1n��#�Dž��)��4�\,��X��P"M�y�R�D!ɕ��2���	�w
��O�N���l�~��X�v@~�-��g42y�����@+͗����\��L�D��*�A�������aD@$�<B�
��AT�:��������18
��\��p`����	A�a!:�b��"���"aH4��� �Q"��r��Bj�]H#�-r9�\@���� 2����G1���Q�u@���Ơs�t4]���k��=�����K�ut}��c��1f��a\��E`�X&�c�X5V�5cX7v��a�$���^��l���GXLXC�%�#��W	��1�'"��O�%z��xb:��XF�&�!!�%^'_�H$ɒ�N
!%�2IIkH�H-�S�>�i�L&�m������ �����O�����:ň�L	�$R��J5e?���2B���Qͩ����:�ZIm�vP/S��4u�%͛Cˤ-��Кigi�h/�t�	݃E�З�k�����w
�
��Hb(k{��/�L�ӗ��T0�2�g��oUX*�*|���:�V�~��TUsU?�y�T�U�^V}�FU�P�	��թU��6��RwR�P�Q_��_���c
���F��H�Tc���!�2e�XB�rV�,k�Mb[���Lv�v/{LSCs�f�f�f��q�Ʊ��9ٜJ�!�
�{--?-��j�f�~�7�zھ�b�r�����up�@�,��:m:�u	�6�Q����u��>�c�y�	�����G�m������7046�l18c�̐c�k�i�����h���h��I�'�&�g�5x>f�ob�4�e�k<abi2ۤĤ��)͔k�f�Ѵ�t���,ܬج��9՜k�a�ټ����E��J�6�ǖږ|��M����V>VyV�V׬I�\�,�m�WlPW��:�˶�����v�m���)�)�Sn�1��
���9�a�%�m����;t;|rtu�vlp���4éĩ��Wgg�s��5�K���v�Sm���n�z˕��ҵ�����ܭ�m���=�}��M.��]�=�A��X�q�㝧�����/^v^Y^��O��&��0m���[��{`:>=e���>�>�z�����"�=�#~�~�~���;������y��N`������k��5��/>B	
Yr�o���c3�g,����Z�0�&L�����~o��L�̶��Gl��i��})*2�.�Q�Stqt�,֬�Y�g��񏩌�;�j�rvg�jlRlc웸�����x��E�t$	�����=��s�l�3��T�tc��ܢ����˞w<Y5Y�|8����?� BP/O�nM򄛅OE����Q���J<��V��8�;}C�h�OFu�3	OR+y���#�MVD�ެ��q�-9�����R
i��+�0�(�Of++�
�y�m�����#�s��l�Lѣ�R�PL/�+x[[x�H�HZ�3�f��#�|���P���ظxY��"�E�#�Sw.1]R�dxi��}�h˲��P�XRU�jy��R�ҥ�C+�W4�����n��Z�ca�dU�j��[V*�_�p�����F���WN_�|�ym���J����H��n��Y��J�jA�І�
���_mJ�t�zj��ʹ���5a5�[̶���6��z�]�V������&�ֿ�w{��;��켵+xWk�E}�n��ݏb���~ݸGwOŞ�{�{�E��jtolܯ���	mR6�H:p囀oڛ�w�pZ*�A�'ߦ|{�P������ߙ���Hy+�:�u�-�m�=���茣�^G���~�1�cu�5�W���(=�䂓�d���N?=ԙ�y�L��k]Q]�gCϞ?t�L�_�����]�p�"�b�%�K�=�=G~p��H�[o�e���W<�t�M�;����j��s��.]�y�����n&��%���v��w
�L�]z�x����������e�m�`�`��Y�	�����Ӈ��G�G�#F#���
��dΓ᧲���~V�y�s����K�X�����Ͽ�y��r﫩�:�#���y=���}���ǽ�(�@�P��cǧ�O�>�|��/���9%tEXtSoftwareAdobe ImageReadyq�e<&iTXtXML:com.adobe.xmp<?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?> <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.6-c014 79.156797, 2014/08/20-09:53:02        "> <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <rdf:Description rdf:about="" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmlns:stRef="http://ns.adobe.com/xap/1.0/sType/ResourceRef#" xmp:CreatorTool="Adobe Photoshop CC 2014 (Windows)" xmpMM:InstanceID="xmp.iid:A085CF4FD5C011E8AD1EAEDEC88D8AF1" xmpMM:DocumentID="xmp.did:A085CF50D5C011E8AD1EAEDEC88D8AF1"> <xmpMM:DerivedFrom stRef:instanceID="xmp.iid:A085CF4DD5C011E8AD1EAEDEC88D8AF1" stRef:documentID="xmp.did:A085CF4ED5C011E8AD1EAEDEC88D8AF1"/> </rdf:Description> </rdf:RDF> </x:xmpmeta> <?xpacket end="r"?>yS��IDATx���KHTQ�q�G�I�"1!�A�A�(����΅�
����Bt�F7��uJQ A�
E�EA-"HD�j"�����O9\�a�3�v������s��1�X,�������r��GG��o�dK	�b?u|¹_�	l�n�_DF��	ڈ���(�.�9��Q�bI��`���gm�ᾎ�F�"C�l��hA=�R�U�8��:����	�r�$�w�s�����8׭O�칶W�DZ��4u��E�s�<n�Z��&>�{x��U�Q?/����>����9l)i�k�4�$fѫV{婀�<�b�!Q�� ~���]x�+��V��6~���k��L�w�Zc���3�x��hӵ�Tg.o6�%�4��/{��9��?I�����x��-9:�F]0���~�	o����jr�4/[r=Ɣ&�M
���N��/%�$^����w*A�N��ul�/5�Z��ej=v�.)h�7K�ځ��d�
��2�v+��6��n}��]f��w�`�Hx�e�IEND�B`�admin/partials/images/download.png000064400000002275151327705670013260 0ustar00�PNG


IHDRv��tEXtSoftwareAdobe ImageReadyq�e<xiTXtXML:com.adobe.xmp<?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?> <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.6-c132 79.159284, 2016/04/19-13:13:40        "> <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <rdf:Description rdf:about="" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmlns:stRef="http://ns.adobe.com/xap/1.0/sType/ResourceRef#" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmpMM:OriginalDocumentID="xmp.did:47c2336c-10d5-b044-9fab-6eca53f0688d" xmpMM:DocumentID="xmp.did:242D1478028E11E9A788917C3A3CF8D7" xmpMM:InstanceID="xmp.iid:242D1477028E11E9A788917C3A3CF8D7" xmp:CreatorTool="Adobe Photoshop CC 2015.5 (Windows)"> <xmpMM:DerivedFrom stRef:instanceID="xmp.iid:47c2336c-10d5-b044-9fab-6eca53f0688d" stRef:documentID="xmp.did:47c2336c-10d5-b044-9fab-6eca53f0688d"/> </rdf:Description> </rdf:RDF> </x:xmpmeta> <?xpacket end="r"?>g�'��IDATx�b���?�@&���Y�Q�X��d��w�����@��_�(=�Z�d��h�J�Br�,3C�|Hr2@���o�i�_b,rې�K�8��A���@9��@�C�8^�M8�@A��i���@��	ķh��AAė�Ī�x;)�0�jQL��q ��1��W��BAV��j@��@�#�bJ�F�C��b��Ō���Q�����,h���IEND�B`�admin/partials/images/stroage-google-drive.png000064400000010066151327705670015473 0ustar00�PNG


IHDR;0��
CiCCPICC profilexڝSwX��>�eVB��l�"#��Y��a�@Ņ�
V�HU�
H���(�gA��Z�U\8�ܧ�}z��������y��&��j9R�<:��OH�ɽ�H� ���g��yx~t�?��op�.$���P&W ��"��R�.T���S�d
�ly|B"�
��I>ة��آ���(G$@�`U�R,����@".���Y�2G��v�X�@`��B,� 8C� L�0ҿ�_p��H�˕͗K�3���w����!��l�Ba)f	�"���#H�L����8?������f�l��Ţ�k�o">!����N���_���p��u�k�[�Vh�]3�	�Z
�z��y8�@��P�<
�%b��0�>�3�o�~��@��z�q�@������qanv�R���B1n��#�Dž��)��4�\,��X��P"M�y�R�D!ɕ��2���	�w
��O�N���l�~��X�v@~�-��g42y�����@+͗����\��L�D��*�A�������aD@$�<B�
��AT�:��������18
��\��p`����	A�a!:�b��"���"aH4��� �Q"��r��Bj�]H#�-r9�\@���� 2����G1���Q�u@���Ơs�t4]���k��=�����K�ut}��c��1f��a\��E`�X&�c�X5V�5cX7v��a�$���^��l���GXLXC�%�#��W	��1�'"��O�%z��xb:��XF�&�!!�%^'_�H$ɒ�N
!%�2IIkH�H-�S�>�i�L&�m������ �����O�����:ň�L	�$R��J5e?���2B���Qͩ����:�ZIm�vP/S��4u�%͛Cˤ-��Кigi�h/�t�	݃E�З�k�����w
�
��Hb(k{��/�L�ӗ��T0�2�g��oUX*�*|���:�V�~��TUsU?�y�T�U�^V}�FU�P�	��թU��6��RwR�P�Q_��_���c
���F��H�Tc���!�2e�XB�rV�,k�Mb[���Lv�v/{LSCs�f�f�f��q�Ʊ��9ٜJ�!�
�{--?-��j�f�~�7�zھ�b�r�����up�@�,��:m:�u	�6�Q����u��>�c�y�	�����G�m������7046�l18c�̐c�k�i�����h���h��I�'�&�g�5x>f�ob�4�e�k<abi2ۤĤ��)͔k�f�Ѵ�t���,ܬج��9՜k�a�ټ����E��J�6�ǖږ|��M����V>VyV�V׬I�\�,�m�WlPW��:�˶�����v�m���)�)�Sn�1��
���9�a�%�m����;t;|rtu�vlp���4éĩ��Wgg�s��5�K���v�Sm���n�z˕��ҵ�����ܭ�m���=�}��M.��]�=�A��X�q�㝧�����/^v^Y^��O��&��0m���[��{`:>=e���>�>�z�����"�=�#~�~�~���;������y��N`������k��5��/>B	
Yr�o���c3�g,����Z�0�&L�����~o��L�̶��Gl��i��})*2�.�Q�Stqt�,֬�Y�g��񏩌�;�j�rvg�jlRlc웸�����x��E�t$	�����=��s�l�3��T�tc��ܢ����˞w<Y5Y�|8����?� BP/O�nM򄛅OE����Q���J<��V��8�;}C�h�OFu�3	OR+y���#�MVD�ެ��q�-9�����R
i��+�0�(�Of++�
�y�m�����#�s��l�Lѣ�R�PL/�+x[[x�H�HZ�3�f��#�|���P���ظxY��"�E�#�Sw.1]R�dxi��}�h˲��P�XRU�jy��R�ҥ�C+�W4�����n��Z�ca�dU�j��[V*�_�p�����F���WN_�|�ym���J����H��n��Y��J�jA�І�
���_mJ�t�zj��ʹ���5a5�[̶���6��z�]�V������&�ֿ�w{��;��켵+xWk�E}�n��ݏb���~ݸGwOŞ�{�{�E��jtolܯ���	mR6�H:p囀oڛ�w�pZ*�A�'ߦ|{�P������ߙ���Hy+�:�u�-�m�=���茣�^G���~�1�cu�5�W���(=�䂓�d���N?=ԙ�y�L��k]Q]�gCϞ?t�L�_�����]�p�"�b�%�K�=�=G~p��H�[o�e���W<�t�M�;����j��s��.]�y�����n&��%���v��w
�L�]z�x����������e�m�`�`��Y�	�����Ӈ��G�G�#F#���
��dΓ᧲���~V�y�s����K�X�����Ͽ�y��r﫩�:�#���y=���}���ǽ�(�@�P��cǧ�O�>�|��/���9%tEXtSoftwareAdobe ImageReadyq�e<&iTXtXML:com.adobe.xmp<?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?> <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.6-c014 79.156797, 2014/08/20-09:53:02        "> <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <rdf:Description rdf:about="" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmlns:stRef="http://ns.adobe.com/xap/1.0/sType/ResourceRef#" xmp:CreatorTool="Adobe Photoshop CC 2014 (Windows)" xmpMM:InstanceID="xmp.iid:D96A48ABD5C211E8A49EDE26ADB27049" xmpMM:DocumentID="xmp.did:D96A48ACD5C211E8A49EDE26ADB27049"> <xmpMM:DerivedFrom stRef:instanceID="xmp.iid:D96A48A9D5C211E8A49EDE26ADB27049" stRef:documentID="xmp.did:D96A48AAD5C211E8A49EDE26ADB27049"/> </rdf:Description> </rdf:RDF> </x:xmpmeta> <?xpacket end="r"?>v���WIDATx�b���?�@&�#�b����"�I�B�R߶dH|�$`�k �b�9��X�"�ǭS�qY~��x��:Q n�JP}�����{|lD���눭E-��$���Ye�}c9O��)�q8�!���S��7���!�b�o��T7����L<��p� "�{��s���J ��&�����?Ƨ,��
�,�VH�����?#C�#�oD���kER|���L���;	X�
rl��
+@��uR��TP\5zr�e�p*{p @KY��
@�~;o�}�a7�ٍ�r|A�Ě��]H�& ~E@�g`
j��Ab@�6�0���M(��m"��G@�{ Va�>���mDX�
�R(Xħ	��Zl>����X�nd����0�	�7R�lff��sE��-ZjlA�;TM//-
�%�����W?_Ś�~J�e�b�Oa��v���zʃ��i�"�*>�U��O��5;-?��䷏ַ�iS��	@K�*2Ae�H;��ӫh>
-���8-�TT���e���,Z���7b+���u���A<h����h�~�Z`����0|
OIEND�B`�admin/partials/images/Delete.png000064400000002271151327705670012647 0ustar00�PNG


IHDRv��tEXtSoftwareAdobe ImageReadyq�e<xiTXtXML:com.adobe.xmp<?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?> <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.6-c132 79.159284, 2016/04/19-13:13:40        "> <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <rdf:Description rdf:about="" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmlns:stRef="http://ns.adobe.com/xap/1.0/sType/ResourceRef#" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmpMM:OriginalDocumentID="xmp.did:47c2336c-10d5-b044-9fab-6eca53f0688d" xmpMM:DocumentID="xmp.did:B0DF8AE301CA11E9B6E4DC878156B8F6" xmpMM:InstanceID="xmp.iid:B0DF8AE201CA11E9B6E4DC878156B8F6" xmp:CreatorTool="Adobe Photoshop CC 2015.5 (Windows)"> <xmpMM:DerivedFrom stRef:instanceID="xmp.iid:47c2336c-10d5-b044-9fab-6eca53f0688d" stRef:documentID="xmp.did:47c2336c-10d5-b044-9fab-6eca53f0688d"/> </rdf:Description> </rdf:RDF> </x:xmpmeta> <?xpacket end="r"?>/>N�IDATx�b���?�@&���
��d,]L��c�OP������(U�	lbM$>'sC� �"G���>԰؛��$_�Z>f�8��SB��-~�{�!����4#TY�-����{(�,N3������SSݐ��!5�L�į�8�c�Yi��c �
��@m�A��'1�
�����Q�����PL1`me�Z<�,0�p:�'XH�IEND�B`�admin/partials/images/storage-local(gray).png000064400000002440151327705670015203 0ustar00�PNG


IHDR;0��tEXtSoftwareAdobe ImageReadyq�e<&iTXtXML:com.adobe.xmp<?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?> <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.6-c132 79.159284, 2016/04/19-13:13:40        "> <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <rdf:Description rdf:about="" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmlns:stRef="http://ns.adobe.com/xap/1.0/sType/ResourceRef#" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmpMM:DocumentID="xmp.did:4FCAF6013FF511E9A268FADA8886613B" xmpMM:InstanceID="xmp.iid:4FCAF6003FF511E9A268FADA8886613B" xmp:CreatorTool="Adobe Photoshop CC 2014 (Windows)"> <xmpMM:DerivedFrom stRef:instanceID="xmp.iid:E0A2516108DF11E9BD0BB4B775E93A54" stRef:documentID="xmp.did:E0A2516208DF11E9BD0BB4B775E93A54"/> </rdf:Description> </rdf:RDF> </x:xmpmeta> <?xpacket end="r"?>�2���IDATx��V�j�@c�5
��^�(}��O��C���(9$�Q!�O����P캁-����ۙ�oV�m[:��Glv=\.�t��$Q=�d+N0���4
�y.��a�yE�i�x<��f���o�[��f�F�����ز,��G�4����k]�,���~B��1=c��g�ɜq��7�-�=�;��_��O��S{��3v�n��=]��h�:}��~�۶Y��tb�ErB9dY�+�0!MSF�.��k��猴,K̔mJ�wO���9��bq��q�NR!1�'�� A`�y�oe?�AWK�R{BǟԈ���ˌL�X��"9�V�ަŽ�r�L&L�xUb|UUA��$�jQWr��
���A�K�H��ZL�&R��o�*�-�����iD�AIEND�B`�admin/partials/images/staging/logs.png000064400000003513151327705670014045 0ustar00�PNG


IHDR22?��tEXtSoftwareAdobe ImageReadyq�e<!iTXtXML:com.adobe.xmp<?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?> <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.6-c142 79.160924, 2017/07/13-01:06:39        "> <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <rdf:Description rdf:about="" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmlns:stRef="http://ns.adobe.com/xap/1.0/sType/ResourceRef#" xmp:CreatorTool="Adobe Photoshop CC (Windows)" xmpMM:InstanceID="xmp.iid:4719A575D4AE11E986F3AE5CA2AB5BEB" xmpMM:DocumentID="xmp.did:4719A576D4AE11E986F3AE5CA2AB5BEB"> <xmpMM:DerivedFrom stRef:instanceID="xmp.iid:4719A573D4AE11E986F3AE5CA2AB5BEB" stRef:documentID="xmp.did:4719A574D4AE11E986F3AE5CA2AB5BEB"/> </rdf:Description> </rdf:RDF> </x:xmpmeta> <?xpacket end="r"?>!�V��IDATx��kHTA���Z�YB/L*����TRbd��aDQA=��Q�e��>�&�L3�
�L!1���� L(Ң I����f��V�wy�gv�k�sΜ93�z���&�����:/j7�	��8�>���e]P�8�P�e"��N�.X�9'�� s�"U5�cQM E:7>�k,n�01��(dΨ�J��%?�aP��V��_A퉨>M}"�8�4�q1Ry�T��͊� ���qC(��ź�7#O�E�2�!3A��8]�	��|�.�Cf�
�*��C�C��T��Y�\�50@ǣ�+�����4�/��oE�Gԭ���D�Q<�9�m�ӫAN���y>�w�$�R��cMf]+�$	!+
�Y*t�U	�>d6��7~�M��L��e�c��g
�< \��:15\U#\\�*t8���4�5`�މE����)%"6J:
��O�B�k�)�cd�bc�E(��.���F�t�j�)�I^�ET婔˝{A�Uf��'�M�2S�<��p����ڱ�VJi&Pv��}�﫰�1f$F!b$�Jsp	��K�-%	1P�w���]+(��Nɠ宅�;��*Q˨���ܖ1�fT�h�rK�bB�o���C~_�3D
j��[jSgn�	��sµ�t�!�͒0�k}�ג���j�jr:��o�F-��Z'Q�ܕ�O�#	�Q�gCGfp��|sʵށ�6��n�,B���s�(&Zzޅę͵�{�v��ؑ�TR�1��h�o
�sʘ�N(\ۭ�Z�v�z�OG�;�k�lZ;�k�Z<��(����d&?W��5�<Ҫk�k�K;~3�o��`��
�"�
���rf�>X�Zk�����ȏjkC�6�E|�����&YBHX�1;3��3�D}���q���)���A1�a���҆�	���"��\�*HD��S��26�ϧ�IEND�B`�admin/partials/images/staging/delete-tab.png000064400000002160151327705670015104 0ustar00�PNG


IHDR��
tEXtSoftwareAdobe ImageReadyq�e<(iTXtXML:com.adobe.xmp<?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?> <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.6-c132 79.159284, 2016/04/19-13:13:40        "> <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <rdf:Description rdf:about="" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmlns:stRef="http://ns.adobe.com/xap/1.0/sType/ResourceRef#" xmp:CreatorTool="Adobe Photoshop CC 2015.5 (Windows)" xmpMM:InstanceID="xmp.iid:C7131C05028B11E98D4BA2DB25EBEBC6" xmpMM:DocumentID="xmp.did:C7131C06028B11E98D4BA2DB25EBEBC6"> <xmpMM:DerivedFrom stRef:instanceID="xmp.iid:C7131C03028B11E98D4BA2DB25EBEBC6" stRef:documentID="xmp.did:C7131C04028B11E98D4BA2DB25EBEBC6"/> </rdf:Description> </rdf:RDF> </x:xmpmeta> <?xpacket end="r"?>�q`�IDATx�b���?5�����\iiiǁԃY�fE����@|(C�ہ8h@�a�@�0�~���}���R�A�P�#@,
��@����1J�@���L ~��@�4�ّԼH=bc ���0��p���K�|?�
�FH:�]V
�'�F��-�55�"1@�NB�_���!�|<�.T�4d3Z���E@�GV,��7 ��.J�˷IEND�B`�admin/partials/images/staging/schedule.png000064400000004132151327705670014673 0ustar00�PNG


IHDR22?��tEXtSoftwareAdobe ImageReadyq�e<!iTXtXML:com.adobe.xmp<?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?> <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.6-c142 79.160924, 2017/07/13-01:06:39        "> <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <rdf:Description rdf:about="" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmlns:stRef="http://ns.adobe.com/xap/1.0/sType/ResourceRef#" xmp:CreatorTool="Adobe Photoshop CC (Windows)" xmpMM:InstanceID="xmp.iid:801D89B2D4AD11E9909EA78DB4964AD8" xmpMM:DocumentID="xmp.did:801D89B3D4AD11E9909EA78DB4964AD8"> <xmpMM:DerivedFrom stRef:instanceID="xmp.iid:801D89B0D4AD11E9909EA78DB4964AD8" stRef:documentID="xmp.did:801D89B1D4AD11E9909EA78DB4964AD8"/> </rdf:Description> </rdf:RDF> </x:xmpmeta> <?xpacket end="r"?>��Y��IDATx��Z]lLA޻V%����o%QQ�Y	��$<!���AT<x��'$~BB���M�V�F<4$[�R�]�M)�T�|Gέ1����%�'�2w�9s�;3g~v
�C)�Y�ER�V.+t�\j�e���}H*���:�_W�(Dr��C����2���91X�*��S�����hl�2�³ρ=CQn8hۧi��k�g�����$-t�X�<?��Uw����\�A����A`���g�
x��cIf"�����Ǐ7� t��؎�h,�/��.�9�G$�"9.(u@���[��1pMa<L��@�T.�x�+l��
��8�#�m s�	�Տ8v��h��0*
��4>9{�ks��M�̀N����(߬�q�jCyn��W�,��E�S��3��� ��t3�^D�7�uh+�'�"���$�+��z��$
����~"�™aϔ.�N��ܣ(���z4��6>�>�xc��x>Ѯ��F�p	(�~���+f�#���"n��ʎIc��[���H��^�d�?����j����\`(����[ŗċ�J"��:Ð�T�ӈi�i��~K�ɡ0����6�,�"��}B3�u�B��0��)�4�rZ�J���u�YG��¢��i�k"�^K'�C���
���p?G�[1t�	�Yh8�����}P��N�r��>�5ujT�<~�g�$�������c�,
������V���
#�v2�LT�ә#�v:]��!|�����	��
�9Tg��}�N�4
���nh꼄��w;��-H�9һY6�t*눒cA��>d��izn�6��t�Ɔ,�싻���lG�.�c�澋6u���a��1{����r�?j���|I���u$I$I��ʞ�����#@T+��F2YS�	u�)��Z�,��D+;ݞS�i:�h�T��Z��BDQ',�)�,�)�����vh��H�L��6v��ceC�4Ϗ�]WD�����o�9�e+�D��+�kt���P�~
�J�]�̷�WaqXq�˼�����&_�14d����N-ϓX�T�V�8��[��	uM���|v �B��H�}����:"R*���00H��!�_��C,��gط2��þ�̟��)����걿2�Sb���}�]%�a��\F%�4��XK$��\����m�=8Μ���D�:}��|`:D����IEND�B`�admin/partials/images/staging/role-cap.png000064400000040667151327705670014616 0ustar00�PNG


IHDR22?��	pHYs��8iTXtXML:com.adobe.xmp<?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?>
<x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.5-c014 79.151481, 2013/03/13-12:09:15        ">
   <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
      <rdf:Description rdf:about=""
            xmlns:xmp="http://ns.adobe.com/xap/1.0/"
            xmlns:dc="http://purl.org/dc/elements/1.1/"
            xmlns:photoshop="http://ns.adobe.com/photoshop/1.0/"
            xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/"
            xmlns:stEvt="http://ns.adobe.com/xap/1.0/sType/ResourceEvent#"
            xmlns:tiff="http://ns.adobe.com/tiff/1.0/"
            xmlns:exif="http://ns.adobe.com/exif/1.0/">
         <xmp:CreatorTool>Adobe Photoshop CC (Windows)</xmp:CreatorTool>
         <xmp:CreateDate>2019-12-02T13:47:17+08:00</xmp:CreateDate>
         <xmp:ModifyDate>2019-12-02T13:47:59+08:00</xmp:ModifyDate>
         <xmp:MetadataDate>2019-12-02T13:47:59+08:00</xmp:MetadataDate>
         <dc:format>image/png</dc:format>
         <photoshop:ColorMode>3</photoshop:ColorMode>
         <xmpMM:InstanceID>xmp.iid:107e1267-550e-5a4f-8df3-cdcf1aab0a5a</xmpMM:InstanceID>
         <xmpMM:DocumentID>xmp.did:107e1267-550e-5a4f-8df3-cdcf1aab0a5a</xmpMM:DocumentID>
         <xmpMM:OriginalDocumentID>xmp.did:107e1267-550e-5a4f-8df3-cdcf1aab0a5a</xmpMM:OriginalDocumentID>
         <xmpMM:History>
            <rdf:Seq>
               <rdf:li rdf:parseType="Resource">
                  <stEvt:action>created</stEvt:action>
                  <stEvt:instanceID>xmp.iid:107e1267-550e-5a4f-8df3-cdcf1aab0a5a</stEvt:instanceID>
                  <stEvt:when>2019-12-02T13:47:17+08:00</stEvt:when>
                  <stEvt:softwareAgent>Adobe Photoshop CC (Windows)</stEvt:softwareAgent>
               </rdf:li>
            </rdf:Seq>
         </xmpMM:History>
         <tiff:Orientation>1</tiff:Orientation>
         <tiff:XResolution>720000/10000</tiff:XResolution>
         <tiff:YResolution>720000/10000</tiff:YResolution>
         <tiff:ResolutionUnit>2</tiff:ResolutionUnit>
         <exif:ColorSpace>65535</exif:ColorSpace>
         <exif:PixelXDimension>50</exif:PixelXDimension>
         <exif:PixelYDimension>50</exif:PixelYDimension>
      </rdf:Description>
   </rdf:RDF>
</x:xmpmeta>
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                            
<?xpacket end="w"?>nlc cHRMz%������u0�`:�o�_�F	IDATx��{pT��?�ޛ��&�@��A|�0"�X��`[,�:�㣊��v��C��L�֎J�”g*
�U���`"!�2��d$���nv��?���M @�8��df9��s~�s���wP"��A,�&��p�Y΅凄?�S ���+~����|��8�����]1wC��Pŗ�s��X�u������o��g��i�����/p=�^��v��ˀ-�|���
|��c���[��|�����| 
��#�u�_��o���2`o;����5]R��`WvT���%��j���i� ��S��<@T�W{��^{�y����r�@N6��bE0�����1l���EX��h�-c,��@�4î'�;$�,p�|��ˍ)o����/
���!���b��e��,��!��Z��u~9�o�,�D[��(p�SvJhH�������O��
	(;%8���[|�@���{�h�3��Ɯ�e]�����L'�� �a��@���q�N�ɵ��SQr�i�_�B��zl�5YМ�܂(`k�w6 ��Ro�QD�4~� �0�j˺H9Pip��#��a���w�ֹ���^�Q�I)�I�1�m�2k�q�z;���d�ֹ�	�����|��ޯ�c�cs����nr�H���J���^Y<f�I��Ժci�g�UO�%�/�<�q=9�
@j
��!弄��$�m���}ȱ���;̈�árW�p<8�i?u����d\-*u��d�mK���ڐ
է�$`�$��t�����	 jY�k������i�v �[��4�� C|�M	4e�N�CW������>�ol�����n2��QX_+�e�髽�	��6�n���A|K����&�m�'Ę/)�O�`r�"��R����B ����:2��'\��f2b���(F��ȵ��uxϘ7Ëߝ,�ڞ����$�,P
C�=��>�8`i�b���x��w�4e�_�m��/�tT�f���?�Wg^
y6$�<��vZ�߀Mƃ�2Px"	V��\�&T�X�v"��zM/�/G� 
7�W\_��I��(�%�M�K7��R����T�b�b�q �3�l
���&ąH@�1�QF�gh�1K�P�Ґr��R�4���^���x2�V̋$]�|�D�
R@��
��C�M����cĹ��9D��(E2��`)f�o׺�`�O�6Ya+5��&3�)^c1�7��q�q#<_�bp[;���l�e��[NG�1aXO-k�� ŗMА[�ī�
��.U��t�@���a˰������b���_�t�=�"��AEm�SF�)W�V��Ja�!�a��X�#��a��ـ�x����H�U#�Y��%��A������#���4�}��_�r?k���)Jlŕ��6ޙ�ױ���7��?�n("I�U���w��V�ocCU#D����~�h�^?�.�'��@Z�8����?�G(?&d����βNT�~O&��@� 3ʏ	�6/�%ݺ��A|V y�U��,���=J��*�2"�����_�>�{?���1PS���t��
��*B��2"̫��|�{|X�w������g{�A�{:���a�1�0��C�f��E�t>�<�I�Eu��
`�v|��>E�W*j��_	��X��W>07k{��:`��N�w�����V���@;�)�4����o���|�s�c��#ªax�'�&n$�d�ֵM 4ZD��ˆ��PZ+,�/�p+K|��f��M�F�Q�/A�uA�P��٨`��m"W���;�rƳ��c���x\X�O܄�i�o�����F��$cX\���G��E�-�yp�"�M�����PW�K��s��>�u"/"=g4F`6��j�Hz:�IK��Ѻc��^bz�>`�!-�?�D��u�<c�>3�u�,�k{`��XJk�,�`�xp��E1��5�6|����rnb�
+��B\�+&�F�#q�u��j_�b���l'�
��x\
�U3�o�Ζ�z�3��D]�����d���!�2©\۽\1�r&1��w!�z�Ǚ�mH�`g��?ᴯ���~�y}pı��NAe#�Yhy����A�q���{�H�7@c�XF��{�::�d�K���V�U+^ݗ�2�Z71����Į��Q-L*VD��1_hu���:�y�-���pW�
@*�_�&��5
��0��������gLj��?2�IEND�B`�admin/partials/images/staging/backup-migration.png000064400000004213151327705670016333 0ustar00�PNG


IHDR22?��tEXtSoftwareAdobe ImageReadyq�e<!iTXtXML:com.adobe.xmp<?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?> <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.6-c142 79.160924, 2017/07/13-01:06:39        "> <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <rdf:Description rdf:about="" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmlns:stRef="http://ns.adobe.com/xap/1.0/sType/ResourceRef#" xmp:CreatorTool="Adobe Photoshop CC (Windows)" xmpMM:InstanceID="xmp.iid:5260F3B1D4AD11E9BFECFE8688861E8F" xmpMM:DocumentID="xmp.did:5260F3B2D4AD11E9BFECFE8688861E8F"> <xmpMM:DerivedFrom stRef:instanceID="xmp.iid:5260F3AFD4AD11E9BFECFE8688861E8F" stRef:documentID="xmp.did:5260F3B0D4AD11E9BFECFE8688861E8F"/> </rdf:Description> </rdf:RDF> </x:xmpmeta> <?xpacket end="r"?>Z���IDATx��ZklTE�[j+�%F	 �D|AE0�yj��
F�
ZAJx%-� ���������Y�@���������� �bY��9]�c�n���I����;s��33gάa��2C5E�v�}ɇ�h�7��8lֽ9bX�J���$FS���'H��L�]�$ˑ�*�#�?@g�E �����@�����bX,��7�3���w�s/�� s����d6x~�O�X�g�M$#��'1	�7���!9�Ł ��﹒(�"_��N���H�sz��П��c�/j6qD�Ǣ=���~g�셈�A��!Y�j�t����0uM�K�
�ҙ#��>�d%0š��<Z�i@�8�l.z�� T�ٴ�r6��CDu%�	���	j��#2�^s��4��㺏hO�N��X�^Z��.��S��Ռ& Y��*ڣfk�#h�$\�J���T
�<��0І�M_�rZw�+��RE��r:ż����`�k}��?�U�e�X��t6���P�1S�:����-y1p%�Y�TpD������	������C�{�H���!r��XA`��M��.��
ٙ&V
�=���FF��D����ݭ���"�dz��נ����ϵ�#`L��Z.>
��n��$�i$�!�*�n���Gf��i��
�t�^ɔlN�D�n`^B�(N�
9�o�x��u̐A��� D�W�86�Љr�}���PW
�Yl��C�vK�K�#�_�yë�I
�ӶE5�GI����	Lh:4�Uhge��A~�������Lγ4�"��d��<��봡2�7F�d����jO\7�]��'#��Pq޽'�P���sٲ�O�L������jU��������h��I"_#?���Z2���L|~�s@�miZ�IXu&������
1F��2�ؽ�jq3������Eg�N�q���E6/�>�E\�_�����k`��޾�3F�p�剈}��8��������������D2���C�7\��;�]�?+wAW��w+;"�䎳��� �-�t3|�<�]���#+yx��|�s��Yx�����
����_�(�>���	1�{����K|7�Px0wIf�W����X�"�����<)")g�n�5�?���.y}��/R
�j1���p��U~n�5Sx�8�{�_�.@Wʶl'�m�{�"���I�T�p3���|��I��-`�Gk����J�����(�ʝIEND�B`�admin/partials/images/staging/storage-wasabi(gray).png000064400000005266151327705670017024 0ustar00�PNG


IHDR;0��	pHYs���iTXtXML:com.adobe.xmp<?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?> <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.6-c145 79.163499, 2018/08/13-16:40:22        "> <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <rdf:Description rdf:about="" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmlns:stRef="http://ns.adobe.com/xap/1.0/sType/ResourceRef#" xmlns:stEvt="http://ns.adobe.com/xap/1.0/sType/ResourceEvent#" xmlns:photoshop="http://ns.adobe.com/photoshop/1.0/" xmp:CreatorTool="Adobe Photoshop CC 2019 (Windows)" xmp:CreateDate="2020-02-12T11:10:40+08:00" xmp:ModifyDate="2020-02-12T13:21:59+08:00" xmp:MetadataDate="2020-02-12T13:21:59+08:00" dc:format="image/png" xmpMM:InstanceID="xmp.iid:84c2ff3b-6895-e044-b89a-f9d393349fbf" xmpMM:DocumentID="xmp.did:741E26F54D5711EABFD6C6DE0EC22EC3" xmpMM:OriginalDocumentID="xmp.did:741E26F54D5711EABFD6C6DE0EC22EC3" photoshop:ColorMode="3" photoshop:ICCProfile="sRGB IEC61966-2.1"> <xmpMM:DerivedFrom stRef:instanceID="xmp.iid:4703A8B24D4511EA9501D05750F51F83" stRef:documentID="xmp.did:4703A8B34D4511EA9501D05750F51F83"/> <xmpMM:History> <rdf:Seq> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:84c2ff3b-6895-e044-b89a-f9d393349fbf" stEvt:when="2020-02-12T13:21:59+08:00" stEvt:softwareAgent="Adobe Photoshop CC 2019 (Windows)" stEvt:changed="/"/> </rdf:Seq> </xmpMM:History> </rdf:Description> </rdf:RDF> </x:xmpmeta> <?xpacket end="r"?>}9� nIDATH���{��e����̐b�B��$l�u��ji�+E[I5���t'�3�/����D�I��%R[���
���y>}_?��?����~�s��<ϧ����AH=����
G�%�c�c5>����@��1Fcz��/�|��O�t�o����h&�Q��	wb�T/^=�����r�p��x�_��4fO�4O��|�pSä�J�u����-�$�M��1=�I��y�0_pb��W`Jj�ƍ�(�q��.M�v�����c�K��0���-�Q���}��l�9��"��Q���
Ƥj�½�3���w&�_h�83�6a8^��Aj�ƹ��0A��_�a�M�nI��`!NN�F�[ټ�x-�;4�V�쀓�
Ƨ��?&��ڝ�'��
F/�@[��ҡ��R��4�OC�
:�d\�{S�u�W�Efk#�; �
�"�Oz8�bdE0�`���]�AX^��yfk�7з�M����9����e������R���tI�:.��(	�1�w��֭"�>�qx��/�D�2�D�^���JwW�?N�����$��
�f��:f�"e���m��wp��`�Ϝ���l�{�*�=�kz������N��"�*S�R���U�1ی/S��p�@Q8��;R�LQ�g�e<wdkoO�]�&ܝ�j*X=���Io%r�Gһ���$�3qa��@(�s�!o��Ŧ*X��&"f��[��]��1\,2���11ӧ�Lo���p��X��	_-�����:5�l��m5!�C�W���-�|�p�*\��qG��DL���$���G��-��N̮��?���҉
��+E��tZ�S\��7�)�9Ay	��O�7W#Q��8�B:��L�����D�F�0�M�A�;Gds�cIA��FY�f(�\U�
���ωk1�\��
Id��bq%��̥�x��N��8�f`�����ꇸ�X��rMi@�r&������������W#�ׂP�����b`}��
�C��1��߬�����l�DVT�{��lȍul^)X�GV���u�ڵ�Ѣ�4��l\�l�R��´�e�*u����l<xPY"���L��P�����U���m�O��?
���IEND�B`�admin/partials/images/staging/Log.png000064400000002202151327705670013614 0ustar00�PNG


IHDRv��tEXtSoftwareAdobe ImageReadyq�e<xiTXtXML:com.adobe.xmp<?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?> <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.6-c132 79.159284, 2016/04/19-13:13:40        "> <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <rdf:Description rdf:about="" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmlns:stRef="http://ns.adobe.com/xap/1.0/sType/ResourceRef#" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmpMM:OriginalDocumentID="xmp.did:47c2336c-10d5-b044-9fab-6eca53f0688d" xmpMM:DocumentID="xmp.did:2D6A139601C511E9B7FFEDB0836C9876" xmpMM:InstanceID="xmp.iid:2D6A139501C511E9B7FFEDB0836C9876" xmp:CreatorTool="Adobe Photoshop CC 2015.5 (Windows)"> <xmpMM:DerivedFrom stRef:instanceID="xmp.iid:47c2336c-10d5-b044-9fab-6eca53f0688d" stRef:documentID="xmp.did:47c2336c-10d5-b044-9fab-6eca53f0688d"/> </rdf:Description> </rdf:RDF> </x:xmpmeta> <?xpacket end="r"?>W���IDATx�b���?�@&���
��d,]L����c�vP�'b�5h�}8�v�5bw ~AM���j�(+�+UAq+Q;�A�J'c���_i� p
�F*�����F}<��!�c v!B
�-��b����U3��E��V����b��M(�=�D�IEND�B`�admin/partials/images/staging/Fresh-list.png000064400000007730151327705670015126 0ustar00�PNG


IHDRddp�TtEXtSoftwareAdobe ImageReadyq�e<�iTXtXML:com.adobe.xmp<?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?> <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.6-c145 79.163499, 2018/08/13-16:40:22        "> <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <rdf:Description rdf:about="" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmlns:stRef="http://ns.adobe.com/xap/1.0/sType/ResourceRef#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmp:CreatorTool="Adobe Photoshop CC (Windows)" xmp:CreateDate="2020-06-13T20:21:29+08:00" xmp:ModifyDate="2020-06-13T20:26:22+08:00" xmp:MetadataDate="2020-06-13T20:26:22+08:00" xmpMM:InstanceID="xmp.iid:17BB2C4DAD7111EABF00A4E5218208E4" xmpMM:DocumentID="xmp.did:17BB2C4EAD7111EABF00A4E5218208E4" dc:format="image/png"> <xmpMM:DerivedFrom stRef:instanceID="xmp.iid:809B5F910A0A11EAAAA0F0455F8115EC" stRef:documentID="xmp.did:809B5F920A0A11EAAAA0F0455F8115EC"/> </rdf:Description> </rdf:RDF> </x:xmpmeta> <?xpacket end="r"?>�T��IDATx��]	tT��g�$d�/���R*�RA�(B)�mm�*��=.`�˱���Aik�JZ�V,ˉ@K���e`b�"eɐ�l��d2�y�:��Nf�y��;���w�����w���p�r��*�ppB8!�N'��	�p(Z��J%e��wp&/�LE��j
v�k(�DY?wfAk'�R9Dz�$B�?��<�a�`CyI��"Dn�8ѱJ��Ϳ��Qj!�?=4��\_QC� l)��Z����
��L�^疯�� +#�$$�G	j5d��BJ����ӏ�"e�W�.:p7�tZ(�245��;��cҶI�FCr��v;�1�X���Ç�r��'č�zN���v�:o;e{��w�Z�})��h4��ӨyT�}{k!�X�p��2�F��߫�nۊK�!?/����)�W��]]6��T��q���*��PlsK+�VT2I,;w�k6��G��UV���`	��!Y	�a��}��`<?��B1N��Qa��W���@a�ꈶ}W��}[�ڌ�\<�fSw�5i�]
���N��Vc��s���e��N7�&�� ؓl���퇚������D�κ��im�h!��
���[�2�m�p���#�|�%�b,i�n�i�L�!�g�P�4R��p��������2��{�7ٚa�̓`���2�G�:���\)����i𭄡��r��[˿nmB��C���~���KJ'E�h3�W9�J�����pw��P�g��^�H[����~�a�l�cf�!��9�l���r�B)w�ޜ))�����3��ôY���[�I}Xț�6��x_�63�!�L�ֆ=B޼�鐭τ�I�aB�S�W�&��<Eؗ��[���q*�'�X�$��s`�q�@��Z�@DyGn=ի�r-3�$B����n�5	��.j��=��ܷ���k9�,V(M~���<�k9,� �ש\jM{�0���e�k�>O(�$��a��:읽[� �I.��kL����0(.O"�"RX�4����Pa�[��ɼPT��7�nJ����Rm� ��+0��Ζtۇ���H�iA����]	�,W`t�¶ʎ�Z<��
�"�n��{���|�R�����ʒopF^D��2a��;����P��'��]M�#^�.����!ʋ��dc�GB�n��{_��|F^C�`L��$��<:�2Ud��2�)�O!rE�E�}Y��X=��z5��nJ-�ϫ�8!+�'%v���m�@N���g_�:;����Uk;��"٧��j�ʵ��T��
B�im��h�w��m0��A"j��y��y��	�ppB8����w���%?�u�B9@��ܙFb�]a�Gpe��JD	��E�����l���</����툙,�X�B�pb#o���7u�1B�����RA|��wRA��B�݌�ɚ�����(�۶Aϫy�Ct1�!�Eܩ#��`�H���������N��:�b�{&�cHf;��/nx�_���r)��X�#�詿��+
��ڰ.��/�b�0L���D�>�l�9�b�5
����
��W)+�:!K�YE2(��#��P�-~sD�3��1���B)hc�D��?�D��G)���1�Cy�B�
Qb�U��9���ʸ����Tle}%��Z�]����zY���x�[�E=���a�z�|����4W��ݽ���F�lRn.�_m��p�%��%�Ã�L�B�[ƀ��f�fU�!�5H�Y_�h�J�&��
����>n'�D�
s�_"1�y�0�d���#K �A�P����Qt���A�(�DI��d�2�?���P;�R��2$�Nzֿ] 7�~t��Qf�B<�\,���嘬
�u<��	�9M�-��%�d8��`�3~�0�qR��A�1��|-����ZS���G?F^���u�(��	���7j��s�O��+`A�c�S	����`{��B���;Şt(��.����y��TM���2�02��p�Xx�8�6i�h-��:Ul/�0�9����Q	�9�-y�h
���r,���B%�g}�"in��:iHk�rU�>���rg*X�rL��U݃��`��B^�^ZϕIJ��\��������1-2�e��k�^(��8���`��e��셐��J���ޛ��nN��t�g�>&�C�8�p����y��e��}�@�ڽ��v�Ѷ�^��@;��h�;b� �c��W��RG���+�Œ�>'˓���M��h�t��Q�IH��x��j�Jڹ��/��^�$�"8�^�km!E���0�iT�Py�ed<���P
�2~*��-�(0@�O"C`Jm��3��=��b�t�<k��B��>h(�b��T.�D��d��LI#�{4�R,꺶�e�jk�p��d�}}F�C>Tz&��m�;p.�1$SqNA��B�p��-w�����F(��\�#Ec� ��K�L0&�b���bH����w��]�|mn�|x�xNAW���p�z�?��w���o�~����'��'��ui�q�k����a^�#�UiC%��T��y��?��g�/9�zV�6���BK	��fs d6�n�e��G�D��N`+�Ow�H���Ukol��	# U��&�l�B��H�=rm!���`d��tHx�5�GR����@ɠQ�;[�܇����O���6$���T4��s�e�a�X�dl�̠l�����G���3H���Ef���/�~�OL��{�� ��@��&�'��g.	�\�/'��C�P���j{����4��4Pz��O��bP0��ځu�8��0	�Z�;Ȕ��V-�1jE4#g,��D�G�&3����N��C8��1�ppB8!�N'��	���?�|~N�IEND�B`�admin/partials/images/staging/Fresh-tab.png000064400000004532151327705670014716 0ustar00�PNG


IHDR22?��tEXtSoftwareAdobe ImageReadyq�e<�iTXtXML:com.adobe.xmp<?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?> <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.6-c145 79.163499, 2018/08/13-16:40:22        "> <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <rdf:Description rdf:about="" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmlns:stRef="http://ns.adobe.com/xap/1.0/sType/ResourceRef#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmp:CreatorTool="Adobe Photoshop CC (Windows)" xmp:CreateDate="2020-06-13T20:21:23+08:00" xmp:ModifyDate="2020-06-13T20:28:25+08:00" xmp:MetadataDate="2020-06-13T20:28:25+08:00" xmpMM:InstanceID="xmp.iid:60813326AD7111EA8DD8CF02795BB3AF" xmpMM:DocumentID="xmp.did:60813327AD7111EA8DD8CF02795BB3AF" dc:format="image/png"> <xmpMM:DerivedFrom stRef:instanceID="xmp.iid:AABD6850FD4A11E9B1199D0F9C5ADB1E" stRef:documentID="xmp.did:AABD6851FD4A11E9B1199D0F9C5ADB1E"/> </rdf:Description> </rdf:RDF> </x:xmpmeta> <?xpacket end="r"?>�y��
IDATx��ZklU>�]���I[k
Ŗ�6*�f��(���>~41b�?4�`Ԇh4��㇆�`�!�h�H�4��T|�ji�T����}l�ۙqg�sfg'����[2��$g�3w����=�����0,��"!���x����Sl�}�;���|Ȇ 6c�X
kGF��]*�pnv֬$���Y9��l9��4��x�z8F��U��Ƒ��}�Dͬ���n^�ĩ_���*�V���N@�+�֯����W�ZQ+�m�=?���� � �"���	>��U�+͡k���Dh^
I�}.Z�Z�F��@�ߓJ^����<s镔���7b��>9�X];��7��t!WYX� 7�Rl��c�
pwN}̵K����'�{��
���)�v�U3�o2�踖?�#�e�֞��,��k2��_כ� ߑe�+�`�[�
v�&�6fՂ�o?��Q{���ϼ&B���O�1sJ���`+���*��m�i9�0��ˊ���g5��J��%\�r�@����Q@��aX�Ӆ�j&T��>�}�g�Ƽ{b�S_z�:c��V,�FxY�~Pk��f�g�_���](�]ّ�'��A�k-\�@�E�V�U�O��th�3��*g����	H��⨱��|L;jRD.{���OGZ��+���ՊvZ�߷�ƪ;ا�Q�#�R�2�^~@n��:�X(����Uѯ��"t�����h�<��V�A|��L1-��ӿB�8����7_ �7�_�G�x�3�ӦhC&hd��}��~=2�On�iN�m����S+���sX|�\��l	�]���S�n���eI��R�r���f4Q�#�nw�70�K)��D@��� ߖ���c]���u��$�^A�LhZ�2Z��gm�(�V��%g��ʎ�Y��J�3jbN�I�Nu�ݏ2Ϛ�Z�?�R��`��џP�#�dtzR�`�����<]=��%�q�s�xVoe��ҵw��E�j7�#�ݨs�Qk2./E-�)
�{���E��P���,�(�KR��E�LdZ����h���N���Fr�#�ȴΫ�h䰮M'�"����f����e��{�ػ���LQ�t���Pa���>�2'�D-@'c�&�CuOBr_��=2y����$2�F ����5�d:�v�F�ލ��3�;���T�^S����q�Z�k�)
�Te�kɂE-I�W@�Qt�}��V�U@���A���w'ɴZ �-
]hHA�з(-v��2a��-M5�17�b���?��}xX�IEND�B`�admin/partials/images/staging/debug.png000064400000005073151327705670014172 0ustar00�PNG


IHDR22?��tEXtSoftwareAdobe ImageReadyq�e<!iTXtXML:com.adobe.xmp<?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?> <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.6-c142 79.160924, 2017/07/13-01:06:39        "> <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <rdf:Description rdf:about="" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmlns:stRef="http://ns.adobe.com/xap/1.0/sType/ResourceRef#" xmp:CreatorTool="Adobe Photoshop CC (Windows)" xmpMM:InstanceID="xmp.iid:247AD5AFD4AE11E99CB0D599924A8A94" xmpMM:DocumentID="xmp.did:247AD5B0D4AE11E99CB0D599924A8A94"> <xmpMM:DerivedFrom stRef:instanceID="xmp.iid:247AD5ADD4AE11E99CB0D599924A8A94" stRef:documentID="xmp.did:247AD5AED4AE11E99CB0D599924A8A94"/> </rdf:Description> </rdf:RDF> </x:xmpmeta> <?xpacket end="r"?>�{�'�IDATx��Z
�UE��W��k���b��R��adB�H���"��~hK"��!o�@EW,2�V�~Č��I�D��V��Y�m�����w�n��}���Mف��yw�9�7sΙ3��J&��в��������ŋ��f�U�r�CN4��YE�/��K�C�o Cހ�v��ů��H-dd(�9<����v��%�ɂH,.�� A:��p�J���s@d1mU�v����k���-�5K��?
�X�
|����^}Hn�WG�?fC���G�(d�̩��O3Ft�Y�1Ǎ�X��
"���12�}���͂�@>�>$��6���}�֬a�"�bi�SH'߯�<+�/,u��!RȾ��\�W��yX�K�Ր�s�uk�:5lF1R��#Dh>�r1�k�\B���.�t�6#"y�O�0�i$�����C�=�a3r�b�'B|2�`��#	�3�nr���{B�fD��}��%�����FHb�R
���$ �cl�%�v
��9li�>Θ���;�zf����6#"�l�4潷$�}��<���u���"��H%W�46�}S�1�ؗX���>�����d/%t�A\�b��)����|^�W.�Hs���>�V}��b*'�%��X\��y5΢9����
���|�n��;j�^��S��\�F�ӹ�"�����L!�&�2]n��8G)m�أ��1)$������N�>+��!�Lr�v��
��5��JrF���0����XI�g���ӟd�[�{'���;	�UY��'83��T���b��2�
�l��q!�y+Zb��h-�$�7�+;�g�`�r
�-�R��%e���Z4�P&x-��y�֭�	��M����!�U�A�LQ����M�[ٝG@.��q��D��Vtܠ�[c2��\���)�d�:h%��Wg Ӫ�x�ٴm��wUw@�z�Śj�����v�a��o�3�u�X�;<��'8'���8�2M޿@Dt,��V%n���'>$&1�mu���H[.��
�㛀��Msr�FN)$�
�({���zA"��Y���
6�@�G!qS��������ԃ9ܨ$fk.���0�)�B��^�')�Q�;�D��&6�x�G��%��<����
P���j�6����5_9k�3����׻X��
�'�T����lz�U�,�b�X4n'8!9��7�
�Cw�RH��c��
/�� :���Wĥ>�
�WƎ%�$1���H�r�)�
�Y,Q�(����w�e|W�+z���L0���01]>4p�=P,�&e�y���z�%�۸R�,U���_�eKlƷ(~}(މn��2�II���c����%�3TM�+ř���e��L�aZ[Ţq�a鯾W�w���Y�7�g���;�L��|���d�M�y:~3 R��f'ȵ

gW\Kʜ�!Jf��TMR��`@��jC�a\�ӆ�R~�{����mȻ�$O�����,c#>�w��0+Rdq��͔9�=a��L����-���-��(ӊ�#�]<��P���AHj��<_$Y�m��m��
K�
��G�4�
�5���$��d �E������P���݋�h���V�<�s5��~ˡ'�D�i��RVWq���}�I���E��M���:ڮ �*b3�ZBF~���1�͏����F�:B۵.�&ߖ��������`+>%��scIEND�B`�admin/partials/images/staging/pro.png000064400000004360151327705670013702 0ustar00�PNG


IHDRddp�TtEXtSoftwareAdobe ImageReadyq�e<!iTXtXML:com.adobe.xmp<?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?> <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.6-c142 79.160924, 2017/07/13-01:06:39        "> <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <rdf:Description rdf:about="" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmlns:stRef="http://ns.adobe.com/xap/1.0/sType/ResourceRef#" xmp:CreatorTool="Adobe Photoshop CC (Windows)" xmpMM:InstanceID="xmp.iid:710D3E65D4AE11E9BFD2A4585F5B1992" xmpMM:DocumentID="xmp.did:710D3E66D4AE11E9BFD2A4585F5B1992"> <xmpMM:DerivedFrom stRef:instanceID="xmp.iid:710D3E63D4AE11E9BFD2A4585F5B1992" stRef:documentID="xmp.did:710D3E64D4AE11E9BFD2A4585F5B1992"/> </rdf:Description> </rdf:RDF> </x:xmpmeta> <?xpacket end="r"?>�j|4eIDATx��m�U��ܮY+Y�Z	��.b!�[Zv���.FJ� !���/���+~�CA�ee�,��za?�do�d/�&�Ү�����;��;s��s��9sw��yy~;��{��\.+����9_��C>�ڦ�K/��؂��٭���@�!��	{:�W`��L ���[[��ݰu�A���P�&�1��׺�s�U_�=c���~��`��E1clUD�_���O�����G\����P<�a����C�܉p�U(^S�h(��0�F�:��0�Ayև4.H2�"b�x-���D�lfD�{>pˠx���<�2��~�
��a8ųF��q�qɠ|���HC^��v_�0*�������ZHu?�z��
�y+By���F(W�?<y�]�Z��͸w���02��M��a�	o9;
;ߝ��D�S�oޞ���i7}2�C���j��H��̟	`L��3�u�Bީ�ö��������a؋)a����0�b`��(ȣG�V�_Ơ���U'�}->����G'\�䑻�FgD�Q�y���ҥ��3!�sJҥ���Pse�m
��u'��{� Ϟ�|
{�@IV
�$��tl�����t�_�w	`���Xj��2ޢ����_�tL?�ixs�6G�3懓 �����#�G�h��3��eq�'`g�� �.8���!���j��n�͋�;��KgL�~�|��]�ӧ�!�c0
T�����H�n8���e�0nG��jlb�b]�GL8>>(#~�.kvTMO�m �B��K�f���T�uSIG�3e��͢.�*�Df��̏�R�'l˸\�&>������Z����)�|�f\2���e�8��@(!�@�"��@(��@(!�@�"��"��y*�*�yhI��."�Z�����MVj�ńm�uy|C I�_g��ѓR���/a��Ft�e��Gڣ���"�U-$��E B�PB �E �E B�PB �U@��̢[rS��ȱ��E�����W�
>�
�S\�ɬو�#1�d����M����,�<�ȓ5�x��m����OJ4.�oaQ�]��W|x��Ȥk���GY� �p��`-[*_���@v �������
��[�̭MUjB�d˾��RW�	�`y�d�܁�9���\h�#.�X���1���7u��@�,��֫�l��IEo�eR��V{�;�Ɍ�!�<�`�S4�/�@��	��f�du�����B";����q]W����
wW�N51��j]W�}H�ܲ�&p�K�ֿ^R��蜛IEND�B`�admin/partials/images/staging/staging.png000064400000004164151327705670014540 0ustar00�PNG


IHDR22?��tEXtSoftwareAdobe ImageReadyq�e<!iTXtXML:com.adobe.xmp<?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?> <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.6-c142 79.160924, 2017/07/13-01:06:39        "> <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <rdf:Description rdf:about="" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmlns:stRef="http://ns.adobe.com/xap/1.0/sType/ResourceRef#" xmp:CreatorTool="Adobe Photoshop CC (Windows)" xmpMM:InstanceID="xmp.iid:AABD6850FD4A11E9B1199D0F9C5ADB1E" xmpMM:DocumentID="xmp.did:AABD6851FD4A11E9B1199D0F9C5ADB1E"> <xmpMM:DerivedFrom stRef:instanceID="xmp.iid:AABD684EFD4A11E9B1199D0F9C5ADB1E" stRef:documentID="xmp.did:AABD684FFD4A11E9B1199D0F9C5ADB1E"/> </rdf:Description> </rdf:RDF> </x:xmpmeta> <?xpacket end="r"?>�J�.�IDATx��Z]lTE>K�Rj)i�V���P��*��%`�i"LL�}��k$Ġb�Ic�D�i�D��Rm���T��_�X�J�Tl���7{gs]o�����nz�ә�;s�Μs����b�4Nr�<��{_�*4[�s�L�Npc�ʚ�=Q���,ޝ(x�6[ b��IW#�h�����	�����Ey=���9��i�_/]��2%��G���fY����E����& s�n���Y����rM�D��Z(m?v�����]YB yzt��i	��ȹ�n	��$44$�HX:O��`ϥLu�<�m��Bw�{=�{)&E"Q�<u6,�ˎ�G��w�)ʑ��@r�9�3@�����_H7ߜ����KSݡ�@Dn��5URS:U��
H�΀��b���׿���ˋ��e��G$p��y��+dJ�x�<��ܿ�]�|��1O�9��R���%�w�k���9ฮ�l���C�Cn\+��Q(���&�1����ߩ���xS
�JP��_���|�TFH&��\M.֍w�Z�X]�,H��� �J�����T�ľMj�����T�k�֥�݄V��8Y�s�D`S
�ء�Õn{�Vq2ѵ�m漎2��Z�ZO*�iX4�qN��k��
w'�Q}sZ�.�D�����O����h
Z�nE0�D��0}��	�k8_[{�����|g0'#Y�ȶ�ʵ�V����d�����]t�3�0&h��|��D��,��n��L���e�ZU�l�/@�=��}/�9�D���~�Л�� ��o�O�Mu��j�1 ��FP>����iʎ00�@�-��м4\�1
��@no���>C�z�l���jH.K��Sa���5͛��x�6~ת�Ҕ{ܚҵ�e�h�1�Ð{`4_��	1��hf���L�v�6�=�y�Mֺ
��������ۓj�9�ʵ

�Pű��3�I>7�~������>��7Z�D����;MN3k����Dz����^�b k���h��V�y����n�����ZO2T	�.}r�Ґ��y*N�Z'��+vd�m�)xȀ\�ȷn�֕�8B�4;Ƃ�?�(Eh�p�.�D�&w��`��>7Y����0ԫ֙�r7�yŀ�A�Z0GS��C Hm�
C2�N�N��ď8;��Ô�g�cj�u������x)��gɨe��՟���J��+�/ѹ��7��?�ٯN��	|E��$�oQxaE�
�E�4��B���6�FԹ�<�����|G��7�ydIEND�B`�admin/partials/images/staging/download.png000064400000002275151327705670014714 0ustar00�PNG


IHDRv��tEXtSoftwareAdobe ImageReadyq�e<xiTXtXML:com.adobe.xmp<?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?> <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.6-c132 79.159284, 2016/04/19-13:13:40        "> <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <rdf:Description rdf:about="" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmlns:stRef="http://ns.adobe.com/xap/1.0/sType/ResourceRef#" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmpMM:OriginalDocumentID="xmp.did:47c2336c-10d5-b044-9fab-6eca53f0688d" xmpMM:DocumentID="xmp.did:242D1478028E11E9A788917C3A3CF8D7" xmpMM:InstanceID="xmp.iid:242D1477028E11E9A788917C3A3CF8D7" xmp:CreatorTool="Adobe Photoshop CC 2015.5 (Windows)"> <xmpMM:DerivedFrom stRef:instanceID="xmp.iid:47c2336c-10d5-b044-9fab-6eca53f0688d" stRef:documentID="xmp.did:47c2336c-10d5-b044-9fab-6eca53f0688d"/> </rdf:Description> </rdf:RDF> </x:xmpmeta> <?xpacket end="r"?>g�'��IDATx�b���?�@&���Y�Q�X��d��w�����@��_�(=�Z�d��h�J�Br�,3C�|Hr2@���o�i�_b,rې�K�8��A���@9��@�C�8^�M8�@A��i���@��	ķh��AAė�Ī�x;)�0�jQL��q ��1��W��BAV��j@��@�#�bJ�F�C��b��Ō���Q�����,h���IEND�B`�admin/partials/images/staging/storage-pcloud(gray).png000064400000005152151327705670017036 0ustar00�PNG


IHDR;0��	pHYs���iTXtXML:com.adobe.xmp<?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?> <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.6-c145 79.163499, 2018/08/13-16:40:22        "> <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <rdf:Description rdf:about="" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmlns:stRef="http://ns.adobe.com/xap/1.0/sType/ResourceRef#" xmlns:stEvt="http://ns.adobe.com/xap/1.0/sType/ResourceEvent#" xmlns:photoshop="http://ns.adobe.com/photoshop/1.0/" xmp:CreatorTool="Adobe Photoshop CC 2019 (Windows)" xmp:CreateDate="2020-02-12T11:10:40+08:00" xmp:ModifyDate="2020-02-12T13:22:09+08:00" xmp:MetadataDate="2020-02-12T13:22:09+08:00" dc:format="image/png" xmpMM:InstanceID="xmp.iid:bb28bd8d-5691-b145-b349-8d43db9b8e8a" xmpMM:DocumentID="xmp.did:63DFD01D4D5711EABFC5A30243025F15" xmpMM:OriginalDocumentID="xmp.did:63DFD01D4D5711EABFC5A30243025F15" photoshop:ColorMode="3" photoshop:ICCProfile="sRGB IEC61966-2.1"> <xmpMM:DerivedFrom stRef:instanceID="xmp.iid:643493B74D4511EAA009E9A0D228D4DA" stRef:documentID="xmp.did:643493B84D4511EAA009E9A0D228D4DA"/> <xmpMM:History> <rdf:Seq> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:bb28bd8d-5691-b145-b349-8d43db9b8e8a" stEvt:when="2020-02-12T13:22:09+08:00" stEvt:softwareAgent="Adobe Photoshop CC 2019 (Windows)" stEvt:changed="/"/> </rdf:Seq> </xmpMM:History> </rdf:Description> </rdf:RDF> </x:xmpmeta> <?xpacket end="r"?>�]�"IDATH��Oh\E�?3���k�6�M��
$$$���V=x� ңG��=y�E�"��'����x����� �6��M�d����73��n6����|a`����}��f�����PX���O8ׯ_�;�O/c.O��{Rʟ�k��~ �$�h�/	!z���ت��^���Z_VJ�����c>�֞dxx�����G!a���A>�.��kJ������c�W��&&&�}�R�����j��q N�8A&�q+��{J)|p�c17whT�1J��3g���d�T*]�{zzchh���Evvv"��E	{�bx��������(��y�&Zk�  �L�۴��K�X$��0==͍7Ƙ��C�9�s�n������Z��}�'�Jq��-�8&�L233� \.���2+++�������Ry��&��)�q�_��O
��t���-*�
B�����0��j���0<<LE����J�p�Qk��>B�চ�)�#�63B�,&�ʥ�%���YXX C|�`ss�D"��yXkG�"�^m��
�dG�NI)])%Q+@?RJ<��u]�:B�8�Z��8Xk=�w�Ǜ�����ֆ���}�z�N>�G)�i��X��$��������cZ���m�޽۪�D"�Rޑ�_h��q�0u�Z���H�R�r9:�l�R��R�8���5?~�8�Z�j��N��	||/�{]%�B���>|���aE�Z
cJ)�1��K��N������@)E�R!��155��y�a���"�o�fww�q�R"�������I�8f}}�3��	|y�b)%w��!CΞ=Koo/�B���u�8n�AǸ���ӧ�}�L&���A��͹�w���1H)cpp�r�L�X�Z�b��u]�;F:�foo���e�0�V��(4/�S�7�I�[k�}�t:M�>�;;;�EJ��Z��ˇ͕fGY�q���uZ5��e����j��Zc�E)յ�w�C��Kb����"���$���BSe3��h�� ||q�?"���;������̺IEND�B`�admin/partials/images/staging/backups-restore.png000064400000004024151327705670016210 0ustar00�PNG


IHDR22?��tEXtSoftwareAdobe ImageReadyq�e<!iTXtXML:com.adobe.xmp<?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?> <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.6-c142 79.160924, 2017/07/13-01:06:39        "> <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <rdf:Description rdf:about="" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmlns:stRef="http://ns.adobe.com/xap/1.0/sType/ResourceRef#" xmp:CreatorTool="Adobe Photoshop CC (Windows)" xmpMM:InstanceID="xmp.iid:6D69F8B8D4AD11E98BEBFE845A008D1C" xmpMM:DocumentID="xmp.did:6D69F8B9D4AD11E98BEBFE845A008D1C"> <xmpMM:DerivedFrom stRef:instanceID="xmp.iid:6D69F8B6D4AD11E98BEBFE845A008D1C" stRef:documentID="xmp.did:6D69F8B7D4AD11E98BEBFE845A008D1C"/> </rdf:Description> </rdf:RDF> </x:xmpmeta> <?xpacket end="r"?>
��c�IDATx��ZiHQ�g��v*��Q&��@Y��6i�2**�6I"#_�&h��Dd���2�SAiT?"(-P�diE$f�y���Nb�o{�w�ܹo�|w;��;.��rqi���j`�
�n9fO��,.&���fR�X2'K$\��T^
|��NR�|���D"4��H$j��@$�AH��@�����U��(.���W ;�3e��n$̡z�Q�L��*�DrD*�5!���`�һ�H��P�фu��� A�l��#k����A�� �EQq���R�Z#����2;�p�����Ļ�-���d�TT�zȜ@r���w4��¦@��y�����;Px��
b��f�]"��jGhX�A�
|5����T�4��|g)|�w�M������mo�*	��y2�@����zղ��p�>�&E���~�~S$�$l��bŪ��D~���}:f��
��sC�<� �Ju���߈��0��Ye�'"�ν`&�!ֳR��m�Goh��fn(1��ͣ�XX�Do�"���c/7�{�d6+�.\gN��C�.&�&���f��B�n_?���@6�@���2�f��<�5�گ��i
�9X���i!	�>C:������'��:��gE��r9�J��W�^�ԓ�Ge�~��ӑ��G4ԋܼ��T�HHCA��]��A�ȥp��u<�D�3-�l%���(ie�ls[ЫysbЕIku���� q�-
;��ЊB�7�)��#�VY%���FIH*�=�S3
�[x��ʡu���ndvH�]�m ���!�ı8�i0�W)&� ���^�S�J���*ԥ��r�-���؊��/iQ�ρu[n�ާx�'�qo�\��d�JB�Ƴ�8��&NcP���X�j)6��a��7��Pwr�C�h�4d|�&�.6��X!S*�crY(Πs�7\�8���Ӭ0�~����	�?�ĢN�ӈP<">��T��A&�\D����J��7�L��d� ��M��6uH<y��p'�R���)z��7�B�ڃ�
�ך�z8oh�ÞU�IȚd�D�8!9
e��y�ș�gjM_=��,�����U�]|T�~>s2��gb(9�ó_9+��;��IEND�B`�admin/partials/images/staging/settings.png000064400000005542151327705670014745 0ustar00�PNG


IHDR22?��tEXtSoftwareAdobe ImageReadyq�e<!iTXtXML:com.adobe.xmp<?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?> <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.6-c142 79.160924, 2017/07/13-01:06:39        "> <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <rdf:Description rdf:about="" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmlns:stRef="http://ns.adobe.com/xap/1.0/sType/ResourceRef#" xmp:CreatorTool="Adobe Photoshop CC (Windows)" xmpMM:InstanceID="xmp.iid:D4FF9A00D4AD11E98BBCFBC5C3B83C09" xmpMM:DocumentID="xmp.did:D4FF9A01D4AD11E98BBCFBC5C3B83C09"> <xmpMM:DerivedFrom stRef:instanceID="xmp.iid:D4FF99FED4AD11E98BBCFBC5C3B83C09" stRef:documentID="xmp.did:D4FF99FFD4AD11E98BBCFBC5C3B83C09"/> </rdf:Description> </rdf:RDF> </x:xmpmeta> <?xpacket end="r"?>R���IDATx��Z�MU>3�F1�V#ja<�j�����DVIy��S�n��z�T���w��%�ZKyD��Q�IX3�C�}G�c�{�g��Zߜ}�~����Ir.��Y���4�P
HcS1P|��޹CI��8��@O�4�6p)����ʪ����L���^4�)��2�PW�/��B�	��}�GC���ҵ!�ƛ���%��,�׀��@-�$���ЋK�wٯ﫶��|@�g��!5g<�n�w�}_�����\�^O�2v 0
�0_�q	�\";K��e�47�0�h6�-p�ڙ0#dBnjh��.���s��Ϝ�xj�N����?B�a���l�$ŸN���|�	x��삖���Xdy�6�<�p�+x����Ǖ�[d���M�_�0�����wD�xS��I�	)��k�DNP5풓�-I�a|���𖨶��p�jk�(B�
Z�`~�����9xL��#@%�b]�7����.rNA-��CQ��C�Z�Z�^|~�	��'��H
�Y��]Qa��&�DMʟ˄��0�.
.�$rw�R���>�A|.7����&ړ	`j[^d�*��
��I��J�X�_c0 V0ԇ%h40��F�R8�o��7�$��P�8�	+-Lԣk>B1�w}8-�`�\QZt�q�0Gm�����pSs2��\�P��vh�I7���8W�c��&�ga\��]d�*�F�p�w-<a���)���HW2uI,LA�+&VG��p�+R¡�qG.��v8�,̙j8���x�?ϑW�\���>���Qu��
��[|k�pHd�5��o.���D�=T�?cG�N#�>����AXa�E^�.��kvP��i.е�~�(�3A�q
"�qF;%��U�7Dz��=Tf&���f0#��i�y��s>��7to���0�޲�B����͸�d�D*	�ezXY��XO�l��2��K���E�o�c�|8�����8�cj6/�n�����0R�?6���t�=<h�H՗�V�ҡ����3�}?z�؜���z>:O���٪^ #�U=�b �n-����]��dQ��2��T��V)O�8�vg���WA�[jȈ>��1<��j]����0�I�{��5�7�ڑ����R��&*Rse��kbQ[3A�-�Ԏu��9�le^D�aG�*����-LDc�d����a�{��AU�26p�o�q7�&gX�\����B{/[̾�ƩW�<�f���;�,�,�id�E�B+]�t"�k#ղ�ս�lPq���sS�Y�SY�d�F4fȋ�V&�уv���,Ld)�2פ�l�w����ŘE�� �F�1�R��[������+��.�s&v!�ržb����D�T�l��qHѷJS)�V�Ą$=���D�g�����2�D]����N�2U
�Q͢F��DԤ_�͓�\�H��
蘧4Z.蘘h�"&��%���L��9~2�,�Ǎ�S�4�c��U��,��=��:)�4��Z�Ҩ��I�z˶�P��C#�xϙ����D���'����)�2oc��L��z��P~	:ټ5j�G����{�kn�	&����@�4�<뗑"%�c�c�$v6��+&�5�7�=�2(��R��ȧ�~&��	E눅�L���
4��z�LW��+�Z�&Ws����'R���~r�����d��=�K�|u�Hn���!�Z�h�	�/[�E[�>��v1r�-�j!�j�T�.��[�0��AT�s�\�g���[sxK�����􅴿%��Fj�\�wk[b�+��^ΕJ/ ��W,��T�R$믿#�It��Y#f�ˁb�����„��t`<����m�7��<LHߙ+n�8�[�q�O%��6��$%p��a��8}������xh����6���k����D<�S�O�JL;��_j�Al���A~�p�A��aW�܍�Ky0��(�%Lc��<�;H��{�D����»<�E�`tö-��IEND�B`�admin/partials/images/staging/storage-wasabi.png000064400000004253151327705670016013 0ustar00�PNG


IHDR;0��tEXtSoftwareAdobe ImageReadyq�e<�iTXtXML:com.adobe.xmp<?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?> <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.6-c145 79.163499, 2018/08/13-16:40:22        "> <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <rdf:Description rdf:about="" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmlns:stRef="http://ns.adobe.com/xap/1.0/sType/ResourceRef#" xmp:CreatorTool="Adobe Photoshop CC 2019 (Windows)" xmp:CreateDate="2020-02-12T11:10:40+08:00" xmp:ModifyDate="2020-02-12T11:10:52+08:00" xmp:MetadataDate="2020-02-12T11:10:52+08:00" dc:format="image/png" xmpMM:InstanceID="xmp.iid:4703A8B24D4511EA9501D05750F51F83" xmpMM:DocumentID="xmp.did:4703A8B34D4511EA9501D05750F51F83"> <xmpMM:DerivedFrom stRef:instanceID="xmp.iid:4703A8B04D4511EA9501D05750F51F83" stRef:documentID="xmp.did:4703A8B14D4511EA9501D05750F51F83"/> </rdf:Description> </rdf:RDF> </x:xmpmeta> <?xpacket end="r"?>�+�VIDATxڜ�wl�Uƿ{i��Œ6�XʬŔ��G(HK$�`H�2E��0��KLe��?L�P����@�B�`�4"�UZK��&�!ON�-œ�rϸ�y�����4�5�� �
�@�.���{�\�߆1�YoF�I�{=�{�eP���׳�3��'�NX���`?���6�MpL�<�*{\�.��+������u�
�-�Ǡ�5D�TSc���"���f��[lscA�n���DT��<�m��T�s0���:�U��x<~�z_�<I��5_�f����@��l/s\I��QJ���49������Eu�p�_�.�C@��SŲo!���L��=�f�~�p�j��Nb-�Osl*ͤw���|�9玂�倹N�1��`.�������쟧G��Ps�mTi&�ϵU�8�A�	��⤅�B.��j�r���%�s�˜�K@;h��|Yaz���/�[�Lt�j�t��%1���=i
�����r'�y�!y�w�Kt���.<U7�k$�8��@�f��8�f�w������҉���L�%0�} 듨~u�N75am���ܠ�W�܋�0�C�&q�
xQF��
�p�ayɺH)�;U�3�����r�v0+%��5�SY�Y)W;���a�՘��0�f1d\ҷ48L�Y-{WI����̅�"�T:��8����q
�s3��We���@�^�;�ܽT��ԩ&�)p���Iߙf%����`���$�&����a����_����zl��Mgvj�U���Õƃ1�D��R��E�	ޗ��Dyދ���1D�J�o�˥�,���6���A�"Z�R�H�8�L)B����f��3xismxR^�ũ�y�d��`��/g���l��E�̩����Z;%�Xz�	!��eQ_����9�&�2�D��M�0��m!�9�#�Z�R��O��0'ڝk�y���s�ږ�k���U��-/�P�o"��]��n�7g��ߔ8<G���%�~����rC�$�q�<
k���ވ�=�O�w�|:S��|��9�1+%�Z�7�/�Bzx���v��+�J�a&?��z�^��j�׼D���
0��;�n�+IEND�B`�admin/partials/images/staging/living-site.png000064400000007475151327705670015346 0ustar00�PNG


IHDRddp�TtEXtSoftwareAdobe ImageReadyq�e<!iTXtXML:com.adobe.xmp<?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?> <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.6-c142 79.160924, 2017/07/13-01:06:39        "> <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <rdf:Description rdf:about="" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmlns:stRef="http://ns.adobe.com/xap/1.0/sType/ResourceRef#" xmp:CreatorTool="Adobe Photoshop CC (Windows)" xmpMM:InstanceID="xmp.iid:809B5F910A0A11EAAAA0F0455F8115EC" xmpMM:DocumentID="xmp.did:809B5F920A0A11EAAAA0F0455F8115EC"> <xmpMM:DerivedFrom stRef:instanceID="xmp.iid:809B5F8F0A0A11EAAAA0F0455F8115EC" stRef:documentID="xmp.did:809B5F900A0A11EAAAA0F0455F8115EC"/> </rdf:Description> </rdf:RDF> </x:xmpmeta> <?xpacket end="r"?>g��.�IDATx��]p��onn!	!JPp�v@ �)lH�ZlhK[^����j�3�ӡ��#c�E�
"�[S(/y��T(PA"$����f�u���N�2���ٛ�{Ϟ�����Ξ��jkk#� N�@��Q�((B!
�E��"D�� ċ.��[�n��B�<�2�%S�)b��\`���qc�NY�i,���!L��?���)��C�3L�"+Bd������&�T������-_��N��N�M�{��T������%1�23�)!�c*O�x(�g:uMJ2g	qq���F�)�J�qĈn,Sm���M�x����v�Qm]=�m���W8t0�tO���Vڴu546j��量w�h�?:p�.U^UT�g��XJ��򩁭=.�=�)F��n�c���q*��l=���M���1�b����M��>N�w���kUTW��-?����ܢ�����(��;�C+��B�7�k�ȉrK���Xi�YȲ\�(�x��;��)�d���{T����)(B!�k�k�M��>���Tg�f��4�xy�B�K�һ_Lg�M`R��XB�o�jGFAvwzmT*���[����ɇ'���=�c������j��=��Y���X��ax�&g��k�)�}�[&΍��u�V�H%�Yt�R5}u��5�Xc[p���{�ݩ]�e���-P�!����b�&1���~Pr�f����8+@1P��Q�� ��~#P�4�!HBD[%t�YP
��z�L�D$�B`�(��]}��h�BI��Vi��X�����1ƺ�8�l V���G=�y��<��\���,a!����vg�x\L���B�N�E����uG�cF�w@;�+���&�P�����>�B���A�l�O�E�Q!���_�ֺ��e���X�$I��[Qc��Y��C� �?�r; ^�|�G���&�tR�k4�4���҄�(��Ҭ6Bp���P��x�<�"��ZV��e�H��6����[����9#}Ҙ8b`�G���Z��%�d����FO>�|6�4$��8��^�}\�m=R�����D���ɳ�xڃgo��,��%Ř X��@���n����@
�!ˁ�q�k6��F�ci�h�&c�&D�CaPH鶑p�F�҃q�"��!�؟��b"*�Mw�ԉ?����`E�,�Ҭ�����
dLX{��(��d�w��S`�>`Y�s�� bG�cz+6f[’G�8T��ͮ�/�Z��8ԉ�%��6���ܞ@�V��T�肍����}/\���G�7��u��I*ڢ�����[��\�n��e��l(:�:$��ZJ�72���B�?xB���`�T��V'n��"������*���E�"DA�Q�-Bʲx`���/RlW�`n��e	��,ڈ�׷Yb�F̧�r�>�ش���e��,M,�$2��e1����b=!C�j̃j��Yj����␃.��V�����zG��'X~���e�ž��hb�'�mDy�Ã:3~�7S����A�u]Tګ�Q�(8h`�����&?�dֲl�xqɦ}z��WI���茑�ox��&�e��P*���f7Kj'�7��<�c=��m�pH���7�=!OZ�gr'���=�.c���b�������EwN�M���N"�,}X'%,k�$:�3o�d�)KE4b�3,K(����c�Rd`���C;�p��X�����������I��}�0Ή!�ܮ��5����<����f�{)Yw��7���l�W�K���x3�e��4��`قX��DH�Ri�D���|����ٗt� ��w��L�n50�.��82�B���N��B�"$:d�ě�,)aV���=��w�+)ҁ�C�X��2�S��o'��&��ǑeI��<���y3/JׁG�r���k	��,�1�/?���2f�L�{�A��L�,��|K&8�[��M��=�(�V:^j\@�d����Ovw�Yi~����8y\��B��H}�C�@n��E�j}$	���F���_$������3hPR>eħ�׻�7��c�[�����aX�����sG�-�f/��!�+w����i�ʞ����g?�D�_f���Ni��^�Pp*�[��6��p���x���xso���d�͚B=��|��3�ǥ��U��ƨH�@ϰ"㽪
t����<j��R/O&�ΚL�nߧ��W����앋��	�?�$��ȍmMTV�����y��d�s�Ө[�y���޹��v�<h�5�:0D��K���PA�c�8V��i#�(c��J���zgIe^���O�_���g=
��l�{k�zMs��Z�����S{�oB�!k"�Q�tV��Yy�Kd<̛��T�u�LZ�}C|�+�/Sq�4�g	��%O#&ӓN�3�Pb��n�ݢ���о�c�N�!�}��r%E��Q�������ć�M�5KwI��]P��\Jt%X��֕�r�™P�p'D�Uj�7q�A|��P�REK������/���}�hik	��@8.��Ķ���}�b�;Ծ�8�������������3M�iJ�x�K��>е5���RyÙ`ϫ��;��
1���n��a�DZ1Ż�#%q*gq�y�j`��%[jvӼ�E�I�翭���H���v(d\��Jm ��~DJ+�#�%x9��}�Wh���T��Js��K;F�ZBz,o��쑱�ȯ�y�3�E�O3.9��X���C%��?�C�!�Z��E���p�c�|�
k��M~V��iL�����2 �;���gR�Y���"{��Q'c�)._��'�o⽡x�7�V���!���p��a֋��a2V1DKu)&�l�OI���@q�o�>N	��zR0��k;�5�8�a�D�Z�p��=U�n�+r�&PL�&�.�&h+��ӁNb�!
΀*B!�E�"DA�QP�|��£��J�rIEND�B`�admin/partials/images/staging/staging-site.png000064400000005753151327705670015507 0ustar00�PNG


IHDRddp�TtEXtSoftwareAdobe ImageReadyq�e<!iTXtXML:com.adobe.xmp<?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?> <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.6-c142 79.160924, 2017/07/13-01:06:39        "> <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <rdf:Description rdf:about="" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmlns:stRef="http://ns.adobe.com/xap/1.0/sType/ResourceRef#" xmp:CreatorTool="Adobe Photoshop CC (Windows)" xmpMM:InstanceID="xmp.iid:98E8B20C0A0A11EA8985834794CB0210" xmpMM:DocumentID="xmp.did:98E8B20D0A0A11EA8985834794CB0210"> <xmpMM:DerivedFrom stRef:instanceID="xmp.iid:98E8B20A0A0A11EA8985834794CB0210" stRef:documentID="xmp.did:98E8B20B0A0A11EA8985834794CB0210"/> </rdf:Description> </rdf:RDF> </x:xmpmeta> <?xpacket end="r"?>�gX�`IDATx��]}lE��~P(�(_ŖQC��R1F4�j��D�/c�h�A��D��
Aŏ"*��*D X-�h)أ��ws�Mg�w��_��ٽٷ��7{��@WW�`؃ ��	a0!L�	aBL�`B��E�T����7m.�M��$��Ti�q��$ճC6���1!DD}\N���SԐ, b:T2
2�i㪘(�����o���U���rf�N��^e���A���"/77��N��^8dpL{FF�_Z,��F��ca����5����23E�͓��AQ<z��jۥtӤ��0�j��m�/8���ttv����LE$"��㕽ݪ�@0 ��p?���332��@P���c�o�tC{{H���^1b�PQ�wc̾ݿ�*Ɩ�'��E빶h���GDGG��B����M��<�
��8鈎�-�bϾ�I$"�����!�H�e3��H^H�����a�4�.����eY&�	a0!�[٫��1�6O��e��I�BhU�,��9!��D�7F|��<�)!4:���6Ӿ�)"�b�3tz���~�h�d������1�nj
_�oR�
��i����G�xK���/W�[m��vD��s�8{xɥ{�s���Ő)�#�g���oo����"�s���.��rh�s�w�BH�9��0� �a
o��!X<�iO��`8�'�a`<���H� ���}�dH����UC�oI]Pz�_�O@.��v��޶�u�6�#��
����N��_����o�-ϫ;�	0tO��FF�<��>�`���*+�])R9]*��I�Z‰�h�
0��4��u�����;��Ồ�xa�G��l�C�T�
mj���N��xԕ�v9�<ϑ��u��!�$�4�L�q˸�����La�ˑ��3`Ը�����p1�J:� ���ׁ�(uD��������$)0��XIh�σ<b��C�\� &^H��@�E�8cT5�v�7�,Y���0��*0����L!D&>$sG֠)I�+��J�@�3`@I�Z ��KO�dJ��A �����n!қ��%O�Ec4���9Q�/�"�mճ1��w�B���t����Dԯpz�	�LG'�ò<1���DKg镺Qd��CB�d�b8F��?���C?h�9dN��DԪ�8/V�O�ɲM��r�b�8􉜥�m��t}z
�n�+�A
����y�Y����uCX�B�u2�6���&���z�M���աQTά�4B��Ex�0�g��[!��ۻG2��Ť�:������6��„0�&�����hb3<R���C���Z�g�:V��AL�}��I
����
;V��!�"Y������[��sH�1D�iOBu�)~�E#|j�m~!��:�r1�{]u]�	��<�t��5���Rr�b��l���(}|Ȃ�:���)!t�z�<fs�B:���.{L°kb)�B�Z	=O�+��L���,')�Ѷ
$[INj^����%���t�I�E�om�Z���!}� ;���g-��$O���E��G7&y91ӭ�\�.g-K���^s΂=L��Ў{m�臻�ȉ�zb��B��>������]�#����Q�&ҥ�W�,:Q�6G-���E��c�y�`B.�z�m6X~��zc���d��~x�ݛ$��QG�*��#�m�ɐ���3^6�_����Y�a$�� %�����QS�=�"���g��Yt��ڰ�����x���	�τ�w{�#�N���4�h$�@��E~�u�O���D�A}h���:-粗��0!�&�x���^��D��{���h��u�^����;�u��>���q�+HVjd�����GH�q�܆Oemo�E#��*�
�Bq!�D/'��,"do�m~s��^��J��sKI��C0)|��4_�H^ �>� ,�o~z11D���|b��O�ಗ�9!yl��#/B�̦x��6L"���gs=�l�$C	���Em�����#dL6�ܕ�j��`2��3T�����5l�^C����)�/`Rz����c��+	��[t����f�Đ��0!&�	a0!L�	aB>���M��f�IEND�B`�admin/partials/images/staging/remote-storage.png000064400000004226151327705670016040 0ustar00�PNG


IHDR22?��tEXtSoftwareAdobe ImageReadyq�e<!iTXtXML:com.adobe.xmp<?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?> <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.6-c142 79.160924, 2017/07/13-01:06:39        "> <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <rdf:Description rdf:about="" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmlns:stRef="http://ns.adobe.com/xap/1.0/sType/ResourceRef#" xmp:CreatorTool="Adobe Photoshop CC (Windows)" xmpMM:InstanceID="xmp.iid:9B881117D4AD11E99FA48F4190642189" xmpMM:DocumentID="xmp.did:9B881118D4AD11E99FA48F4190642189"> <xmpMM:DerivedFrom stRef:instanceID="xmp.iid:9B881115D4AD11E99FA48F4190642189" stRef:documentID="xmp.did:9B881116D4AD11E99FA48F4190642189"/> </rdf:Description> </rdf:RDF> </x:xmpmeta> <?xpacket end="r"?>�OIDATx��ZklU���*I�Pbb�a*��-`(Eh%�&"D
�Q���1<~�|�J$�y�h@IPL4Hh�6�Z�"�B@��-��}�
^
���,��ƛ|9s�ܹs�=�qΙ
��a�3��$%��A�*�H��Lr�M�Pl|�p�>_�b�U�#Q�~
�B���ٕ�l����݀�����@��'"C�W�	X
�����������a�w�s"P.b��g��P��(�[,P���gZ<#�����d��N<��T�<W��[ ,�9�g�u����H��%`m<�t�v8���� /����Ů�����/�"S����B�#��t���Ơ|w�?�ϸ��`;��e�_�O����@5~�K�"���x(�`��"7$����S�������E\��A�����q.�"�}&W�r�s���`4W�p��4`���Mf�5��¥�5���>sY�'n��o��a?���/b=���c�s�e�_d�A�[����nG]F�w�t��"`��Sm����`��a��)��0X^���E>���:ͯ$d�6M�������Q�2t��V:.S5�l���޾.���u�y4�D�T�r��z`�Z�j�m��0yrPr��V�>	����m�rҒ��M�Y�܌8tN�@���8:7��9���Q_��w�@qt>G"ժ�˙����@��[)�0S?��ޭ�ӹ��򄧮��V��U�<a�����"�
��
Na�پ3ey6�G��6��z(�.c�3K%�|i��1�q���V\���.�Ѫ���1��ѷ�>C8�ij.�8�R��6���Erz��0�G����v�{��)-�
X䧞�Yr�Y�5��8�fL����l�����󢌵G�<���i��s��yhU�J�]S`�HyF�>גց���5*������gdF��Q�9	�X�w,���TY��ΐ"��w��l@GN���Yӌ^f�W���.�[�����pc
�oR�8M��p�$F*�Ta��n%�5r�N�e�&���ǔ��2��T,�}��wj�[��"�`L�])��&%���
��ĝ���Jys��r���NQ�'ⷒ��_ /��Aj��	�C�f�9�W��*IPe�E ��??��u_Z���E�6�uQv}����H(�5�F�Ut0��+]5��G���J�:zj�[��^��,�c?
\1�{� ��n(��m����Y0���nk1��|����J
""����px.�Hĸ���dz���y��jIEND�B`�admin/partials/images/staging/storage-pcloud.png000064400000004351151327705670016032 0ustar00�PNG


IHDR;0��tEXtSoftwareAdobe ImageReadyq�e<�iTXtXML:com.adobe.xmp<?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?> <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.6-c145 79.163499, 2018/08/13-16:40:22        "> <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <rdf:Description rdf:about="" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmlns:stRef="http://ns.adobe.com/xap/1.0/sType/ResourceRef#" xmp:CreatorTool="Adobe Photoshop CC 2019 (Windows)" xmp:CreateDate="2020-02-12T11:10:40+08:00" xmp:ModifyDate="2020-02-12T11:11:41+08:00" xmp:MetadataDate="2020-02-12T11:11:41+08:00" dc:format="image/png" xmpMM:InstanceID="xmp.iid:643493B74D4511EAA009E9A0D228D4DA" xmpMM:DocumentID="xmp.did:643493B84D4511EAA009E9A0D228D4DA"> <xmpMM:DerivedFrom stRef:instanceID="xmp.iid:643493B54D4511EAA009E9A0D228D4DA" stRef:documentID="xmp.did:643493B64D4511EAA009E9A0D228D4DA"/> </rdf:Description> </rdf:RDF> </x:xmpmeta> <?xpacket end="r"?>�V�l�IDATx��VmlSe~�G��u��Z[�MQX��!FP#10�1Bt����ĐI��ÿ�H��1QNM30C�"fƘ,Љ +�u�c��s�y/��k:�����I�{�}�{�y�s��i�ƒq�ւ�m��oC�~%��9�i���F���
��d	'������$�7����^b_*_ةJ��ά¥
�x
�$�mW����=U5�'�jڑMkڲ�;�^�t�����P���N�ӑ�G�	�,��G����fZ�KU��������!�ɡ@��`�׉u�-�\��P<�"���w��c/���-���O����U�O�~P��� �ل7�܃g|t��t��Ze�}�����J<G���rF��ֲ�����/#�:�Z\��Uwj��4zn��گ���nk#���Y�7I�Ѥ�8J�P��&���8�.6�fj�Ի���b3y,w�p�&�����[�t
��n`�g)v�q`x�ͩH#���x�x������$�*:�ƒ/����D�D��LNDz3�2z���x�<�~�z���;	(:"E؊�k57���t��Y�����dtJ����W~�
KN`͏q%�կ��:!�����h`̓ȓ@If���F�;����5�]L6F8��V���<N=M6��L�c?�`K!S(��*���2�t��+���c�"�;�}�,�?�ˣ;2�"�od�_��%�qk�@��j���~U0wn��"�CS)t]�`��fb�=�Ja���|9�%��4�9sӾX[I�##���-:)��?eq2>�C�o1�ߛ�k0���wO�}h�'> �y�Vr,j�9S��m�:�,͍S��ȄXCd�X6���G��~�kR�R�_���C��K5�	�,(��o,w,N�������m���<��a=����gr�P�x{iP��#fɮ̡�R��"�|6�f
Vw�#J�ql�x�Ǝ/F'�1�b���:5����).��R��j^��	�O+�HL�L�c[C-�((�8$���:�j�p��!6��罔�p2#���52b�f�x%��E`5F�v���a��b�Y��>�z9���E��b��t:5������/'G�(��PB(����H�_*�#�����dz�(k+��:�p���:��y�\�R�����ZLSi�~����`QJ�~�G�IEND�B`�admin/partials/images/Log.png000064400000002202151327705670012160 0ustar00�PNG


IHDRv��tEXtSoftwareAdobe ImageReadyq�e<xiTXtXML:com.adobe.xmp<?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?> <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.6-c132 79.159284, 2016/04/19-13:13:40        "> <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <rdf:Description rdf:about="" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmlns:stRef="http://ns.adobe.com/xap/1.0/sType/ResourceRef#" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmpMM:OriginalDocumentID="xmp.did:47c2336c-10d5-b044-9fab-6eca53f0688d" xmpMM:DocumentID="xmp.did:2D6A139601C511E9B7FFEDB0836C9876" xmpMM:InstanceID="xmp.iid:2D6A139501C511E9B7FFEDB0836C9876" xmp:CreatorTool="Adobe Photoshop CC 2015.5 (Windows)"> <xmpMM:DerivedFrom stRef:instanceID="xmp.iid:47c2336c-10d5-b044-9fab-6eca53f0688d" stRef:documentID="xmp.did:47c2336c-10d5-b044-9fab-6eca53f0688d"/> </rdf:Description> </rdf:RDF> </x:xmpmeta> <?xpacket end="r"?>W���IDATx�b���?�@&���
��d,]L����c�vP�'b�5h�}8�v�5bw ~AM���j�(+�+UAq+Q;�A�J'c���_i� p
�F*�����F}<��!�c v!B
�-��b����U3��E��V����b��M(�=�D�IEND�B`�admin/partials/images/storage-local.png000064400000002630151327705670014200 0ustar00�PNG


IHDR;0��tEXtSoftwareAdobe ImageReadyq�e<&iTXtXML:com.adobe.xmp<?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?> <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.6-c142 79.160924, 2017/07/13-01:06:39        "> <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <rdf:Description rdf:about="" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmlns:stRef="http://ns.adobe.com/xap/1.0/sType/ResourceRef#" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmpMM:DocumentID="xmp.did:1154A75C494511E9BB43A27438C9825C" xmpMM:InstanceID="xmp.iid:1154A75B494511E9BB43A27438C9825C" xmp:CreatorTool="Adobe Photoshop CC 2014 (Windows)"> <xmpMM:DerivedFrom stRef:instanceID="xmp.iid:593DEC2C3FF511E98409C4CE0EEF5353" stRef:documentID="xmp.did:593DEC2D3FF511E98409C4CE0EEF5353"/> </rdf:Description> </rdf:RDF> </x:xmpmeta> <?xpacket end="r"?>R	IDATx���KQǿo���n�-IԴ��[�x�^J�K��^*��.B������U<��<�B��^A� V�	ݐwm���K���o�I:0,��|޼��Β0���&�z`z�!y�1�>)��B�2���8%8�W�M�L
��td���1�lm�}�7�I,�~ǻ��8��w����t�K���"��#������� �;�5j��c_xar�����<ʂ @Dk�1�yj�]��=_T��g�wl�
/�9�@�!��<�K�.0�Ba$��h�u��^
y�@��aM�Mhԇ]W���i��"3�ڥ|	�~}Ŭ���b'�/֦�K�������Z�b$�jW��;�/�T}����,�2�i%�iu�6`�[|���˼i����C������_BWs���5�{�M��r%x��)x�M��һt���X.�1��3�]�ݚ��OX�CEV1�������� ��ND"�=t�nK4z��*StP?�Z��g��E)s\_�W_�E��4���)�IEND�B`�admin/partials/images/Restore.png000064400000002527151327705670013074 0ustar00�PNG


IHDRv��tEXtSoftwareAdobe ImageReadyq�e<xiTXtXML:com.adobe.xmp<?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?> <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.6-c132 79.159284, 2016/04/19-13:13:40        "> <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <rdf:Description rdf:about="" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmlns:stRef="http://ns.adobe.com/xap/1.0/sType/ResourceRef#" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmpMM:OriginalDocumentID="xmp.did:47c2336c-10d5-b044-9fab-6eca53f0688d" xmpMM:DocumentID="xmp.did:61E56F7101C511E98101B3DAD4299B65" xmpMM:InstanceID="xmp.iid:61E56F7001C511E98101B3DAD4299B65" xmp:CreatorTool="Adobe Photoshop CC 2015.5 (Windows)"> <xmpMM:DerivedFrom stRef:instanceID="xmp.iid:47c2336c-10d5-b044-9fab-6eca53f0688d" stRef:documentID="xmp.did:47c2336c-10d5-b044-9fab-6eca53f0688d"/> </rdf:Description> </rdf:RDF> </x:xmpmeta> <?xpacket end="r"?>v�ouIDATx���M(�A��}�7R�))���]��=Q[+qTR�r rGHI
I�(�����N)������w�w}<�3k�کO���γ��yf����Dɋd���[�����v�_��n�!���Ж��E��	:P��e�1vQ�:��5�a��3�U}�f#�2�nP�
|��
���Z�'�;q�<�1#0��fc���6��r)���OaV�C��{�t��	<�.�/#����Qu,��-L�L����!�1�S]�c�&�-���C�&��F0n���*]��2	�<����@
�t��vZ�9���J �k���I��Y�2ϯ]99��g�'ޕ@bJ�q�)�}zI�xToF�:�����r&���>os��U~�wOUӎg�IEND�B`�admin/partials/images/storage-microsoft-onedrive(gray).png000064400000002530151327705670017727 0ustar00�PNG


IHDR;0��tEXtSoftwareAdobe ImageReadyq�e<&iTXtXML:com.adobe.xmp<?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?> <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.6-c132 79.159284, 2016/04/19-13:13:40        "> <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <rdf:Description rdf:about="" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmlns:stRef="http://ns.adobe.com/xap/1.0/sType/ResourceRef#" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmpMM:DocumentID="xmp.did:3AD46B2301C911E9A898A791897CE48B" xmpMM:InstanceID="xmp.iid:3AD46B2201C911E9A898A791897CE48B" xmp:CreatorTool="Adobe Photoshop CC 2014 (Windows)"> <xmpMM:DerivedFrom stRef:instanceID="xmp.iid:5F5D97DCD5C311E8AC65CCF3D46ABEE8" stRef:documentID="xmp.did:5F5D97DDD5C311E8AC65CCF3D46ABEE8"/> </rdf:Description> </rdf:RDF> </x:xmpmeta> <?xpacket end="r"?>����IDATx����+�q��Q�H?r�Ev�n�����-��MN�{�rp�V�vS��ʁɏH[���(%F�B�
��ԷO��03�RӼ�Q��3���_�q�{T��*������N��@+Rp�?���P=O�O\�]|C*�(�֐��Pg�H�-�"�l�mĿh�s��5��Z�9ő6�m��4X�Zd(U%#I�'e�?��m��ip;�m��(�p?V0�N��ͫZ��6�[��62,ퟰ�|��P�p�WڴǸ,5
��p�mM�V�メ}]��QL�\�`ޔcmH����y��ӧ-���4���!4i�?�|?bS<��jo�=q���/�;o��ze��s&dAE[y��M�~CuR�9�BR#7b
����w�
+L�ø�n̗7�S0_ɹ���X������#s�Z�J1��(B�������\�.qQV�ݩIEND�B`�admin/partials/images/storage-amazon-s3(gray).png000064400000002647151327705670015732 0ustar00�PNG


IHDR;0��tEXtSoftwareAdobe ImageReadyq�e<&iTXtXML:com.adobe.xmp<?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?> <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.6-c132 79.159284, 2016/04/19-13:13:40        "> <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <rdf:Description rdf:about="" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmlns:stRef="http://ns.adobe.com/xap/1.0/sType/ResourceRef#" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmpMM:DocumentID="xmp.did:D710EF9C01C611E983AE84892F595970" xmpMM:InstanceID="xmp.iid:D710EF9B01C611E983AE84892F595970" xmp:CreatorTool="Adobe Photoshop CC 2014 (Windows)"> <xmpMM:DerivedFrom stRef:instanceID="xmp.iid:A085CF4FD5C011E8AD1EAEDEC88D8AF1" stRef:documentID="xmp.did:A085CF50D5C011E8AD1EAEDEC88D8AF1"/> </rdf:Description> </rdf:RDF> </x:xmpmeta> <?xpacket end="r"?>Mh*�IDATx���MHa��]�_�K(��B��K�u0Ó�I^4J��0Ѓ���I<W�!
A�B�� �z�C��Ȣb�@���}�70���� ���gv��������B�X�8_��U��8�:��~6
���>>�k��^�c��.w���e�:��w�F�Qf�=�����-'pۉ��ly����1b�(���c��S࿶�t��<��w������ B_��|/��
��a��9]��5�-��j��T���>b��_�J��[�t��B�9~��;8��~k���?YK�71�g�!Gf���i#��7|��26O�_q?тW�zS��hC0�ъOZ��X��S�>��c]K�e:���9�u��9��jI�s���*p1ު��5��8��ES0e	�&_��M�p{��y�+�*4h_6�����a+ڀ֏D���V�'��B��{
�#��	zV�ְ��ج�j�5U��6`������;
z�o K�z�Y���=T��_��6o�@��k����ĥ|$������bIEND�B`�admin/partials/images/storage-sftp(gray).png000064400000002333151327705670015066 0ustar00�PNG


IHDR;0��tEXtSoftwareAdobe ImageReadyq�e<&iTXtXML:com.adobe.xmp<?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?> <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.6-c132 79.159284, 2016/04/19-13:13:40        "> <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <rdf:Description rdf:about="" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmlns:stRef="http://ns.adobe.com/xap/1.0/sType/ResourceRef#" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmpMM:DocumentID="xmp.did:0A5DDAF608DF11E995F78FD166AE2D63" xmpMM:InstanceID="xmp.iid:0A5DDAF508DF11E995F78FD166AE2D63" xmp:CreatorTool="Adobe Photoshop CC 2014 (Windows)"> <xmpMM:DerivedFrom stRef:instanceID="xmp.iid:3DFCD658D5C411E8939893D4C7F785F1" stRef:documentID="xmp.did:3DFCD659D5C411E8939893D4C7F785F1"/> </rdf:Description> </rdf:RDF> </x:xmpmeta> <?xpacket end="r"?>3�5�KIDATx��V�J�@�F�����p�6��}Uˠ�W�}�ܸh#�ʕ��H63�أ)1{btd�=���q�i����3�	���AQ��r�(P�!� �n3M���Q�+�'D}W�kG|D���F��eC�>'&|�
�]�AE�^Ep�a��a�<Oض�X۶!I�y�(
x��N8�s�@�uȲʲY��I�8˲@�$R�r^��4
������ix����0�$��|ߟ���v]j���*HӔ8��4��X�c��R�� �ugGu]�XS�‡�an$�@��qw�jny�B�O`���-�Lm.��LxO�0qhr��	CIEND�B`�admin/partials/images/delete-tab.png000064400000002160151327705670013450 0ustar00�PNG


IHDR��
tEXtSoftwareAdobe ImageReadyq�e<(iTXtXML:com.adobe.xmp<?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?> <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.6-c132 79.159284, 2016/04/19-13:13:40        "> <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <rdf:Description rdf:about="" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmlns:stRef="http://ns.adobe.com/xap/1.0/sType/ResourceRef#" xmp:CreatorTool="Adobe Photoshop CC 2015.5 (Windows)" xmpMM:InstanceID="xmp.iid:C7131C05028B11E98D4BA2DB25EBEBC6" xmpMM:DocumentID="xmp.did:C7131C06028B11E98D4BA2DB25EBEBC6"> <xmpMM:DerivedFrom stRef:instanceID="xmp.iid:C7131C03028B11E98D4BA2DB25EBEBC6" stRef:documentID="xmp.did:C7131C04028B11E98D4BA2DB25EBEBC6"/> </rdf:Description> </rdf:RDF> </x:xmpmeta> <?xpacket end="r"?>�q`�IDATx�b���?5�����\iiiǁԃY�fE����@|(C�ہ8h@�a�@�0�~���}���R�A�P�#@,
��@����1J�@���L ~��@�4�ّԼH=bc ���0��p���K�|?�
�FH:�]V
�'�F��-�55�"1@�NB�_���!�|<�.T�4d3Z���E@�GV,��7 ��.J�˷IEND�B`�admin/partials/images/export-import.png000064400000003540151327705670014276 0ustar00�PNG


IHDR22?��tEXtSoftwareAdobe ImageReadyq�e<!iTXtXML:com.adobe.xmp<?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?> <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.6-c142 79.160924, 2017/07/13-01:06:39        "> <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <rdf:Description rdf:about="" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmlns:stRef="http://ns.adobe.com/xap/1.0/sType/ResourceRef#" xmp:CreatorTool="Adobe Photoshop CC (Windows)" xmpMM:InstanceID="xmp.iid:4E3BE9CAFD4B11E9AAA7C4051F30BFB2" xmpMM:DocumentID="xmp.did:4E3BE9CBFD4B11E9AAA7C4051F30BFB2"> <xmpMM:DerivedFrom stRef:instanceID="xmp.iid:4E3BE9C8FD4B11E9AAA7C4051F30BFB2" stRef:documentID="xmp.did:4E3BE9C9FD4B11E9AAA7C4051F30BFB2"/> </rdf:Description> </rdf:RDF> </x:xmpmeta> <?xpacket end="r"?>��ʳ�IDATx��Z[HQ��MQQ�EIt%�-C!*�%Č�"�D��Ĉ
Z�^+"���%�@K�m1��n����_�((��t�~�L�-;κ��L���ٝ����?��w�h4�M�Ҧ�yBݏ��t|�@;в��"z����{�%)�5�z�̈4)a�5m�~����@:d�݃:J�"���7gJr}�12_�2�HT�
<�i��z�����ݿ6@�O;!x�+�m�:��!�V�����g��|��Z�q�3�N4�D%p|���F���N9-Ĝ��C�lE����I!:����+����srj�\\C�%��B��J'C�+o�ޔ����3�	1�=���z��U@�(���Yq�6�Q8�6�B��C��,���@��$_�${L+.'M��X�FEؔ��IR~e�\��!y+�uU���r��BF㼏��v�{9-�G���\��\ഐ�
���#��#";�~�r��kDv��h!�M�%[7?��aC4�:0g�ƴ�S����H��x�i��gO��u���M����c!��Q,�P�Y�)�i���NqZE���V�h�}΃�Z_�bM,���<�Ё��E�V
P£QՈ�1dQ���n�г�e]�lD���-�|�b(�٫&p3���z8�'�6�4SO�Tw=:cL~��"�0�ZB��uQ�i��<9!~߈X|�.���l��/?�����s� ��GP�)����(��ތ��Ņ$�A\�=6{��b���*��k���AuIl#\3�a�*�CE�
7��ٚ�}^��):X
�P��}�x�îM����a�Dt�b���_	W�6��n�u��BD����"�J�����m�*�EC�RV^1νv�L�_u3B2B2B�;�L��{,Ns}��d
��d�)>9ًA���jj��4F�iOIIB�O0N��鳈�.h�&�R1j{�����f�	No�J�IEND�B`�admin/partials/images/Edit.png000064400000002373151327705670012335 0ustar00�PNG


IHDRv��tEXtSoftwareAdobe ImageReadyq�e<"iTXtXML:com.adobe.xmp<?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?> <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.3-c011 66.145661, 2012/02/06-14:56:27        "> <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <rdf:Description rdf:about="" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmlns:stRef="http://ns.adobe.com/xap/1.0/sType/ResourceRef#" xmp:CreatorTool="Adobe Photoshop CS6 (Windows)" xmpMM:InstanceID="xmp.iid:6685C3AC329011E9B4DFF443DFB58487" xmpMM:DocumentID="xmp.did:6685C3AD329011E9B4DFF443DFB58487"> <xmpMM:DerivedFrom stRef:instanceID="xmp.iid:6685C3AA329011E9B4DFF443DFB58487" stRef:documentID="xmp.did:6685C3AB329011E9B4DFF443DFB58487"/> </rdf:Description> </rdf:RDF> </x:xmpmeta> <?xpacket end="r"?> ��KoIDATx�얽KA������Vci#~K@�I��i((j�N�BTD����Dl���m��M�6z��'��]
���ݙ�����\<�M',f:d��YoX�۹��r�(�=�N���K��	R�σ�@��6��~���m�	̨�)Ege4�8t%��9
�6��M��>U��@�,}bp���`�e��*w���`
��G�b+,=�s���x�o�>y�~p�i�-u��M���b�}�\Ӷ�
�STz�
� I��
2'�*X��L�4�ip�x�>����s���a���G��o�#7u��8M�u�%�+�ZH���3�Ω�njm��c�u&�v�^�ҫZސ���{�6�E/��	w��;qGE ^IEND�B`�admin/partials/images/storage-digitalocean(gray).png000064400000002642151327705670016540 0ustar00�PNG


IHDR;0��tEXtSoftwareAdobe ImageReadyq�e<&iTXtXML:com.adobe.xmp<?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?> <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.6-c132 79.159284, 2016/04/19-13:13:40        "> <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <rdf:Description rdf:about="" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmlns:stRef="http://ns.adobe.com/xap/1.0/sType/ResourceRef#" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmpMM:DocumentID="xmp.did:7D0A31163E5611E98844E632C4955923" xmpMM:InstanceID="xmp.iid:7D0A31153E5611E98844E632C4955923" xmp:CreatorTool="Adobe Photoshop CC 2014 (Windows)"> <xmpMM:DerivedFrom stRef:instanceID="xmp.iid:E0A2516108DF11E9BD0BB4B775E93A54" stRef:documentID="xmp.did:E0A2516208DF11E9BD0BB4B775E93A54"/> </rdf:Description> </rdf:RDF> </x:xmpmeta> <?xpacket end="r"?>�6
IDATx�얻K\A�w}�`%1�h��@BT�FD--��4"���"�!A�
>�F�,�M��4E(�(A��Y�~SL�������ǹs����̙��x<�KDK�%�%����v�S�P �a�`>
�Lb�Mw5��0���L��,|B|���0c��"9��yF4�o<,� �^��1���L�H��^
�.!�#>i%��H���.�����C��>����F�D�<�G�=J�v7Q��/`*�@q?�6�:n�J:�����e���
6�5Z�����JT���Kl�_i��[�	^`VW����
�p%6 D,K��ӑɬ���(~�<�?�Y]%�L������Ƽ�+�Mca>��Bv����c��� U�Gl6�3y�D�[�v��	�a��1�mR-�O�s@�_k�bR�L\���)-�5>2	�%k9(ˡH�ڂ��tx
�rM�����qiz-��e7L�~��*���P'O2Ͽ>�;ޝ���@�]���4kr뷌W�L"��A}���Ia�v#�3w���B�CIEND�B`�admin/partials/images/storage-ftp(gray).png000064400000002211151327705670014676 0ustar00�PNG


IHDR;0��tEXtSoftwareAdobe ImageReadyq�e<&iTXtXML:com.adobe.xmp<?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?> <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.6-c132 79.159284, 2016/04/19-13:13:40        "> <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <rdf:Description rdf:about="" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmlns:stRef="http://ns.adobe.com/xap/1.0/sType/ResourceRef#" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmpMM:DocumentID="xmp.did:E0A2516208DF11E9BD0BB4B775E93A54" xmpMM:InstanceID="xmp.iid:E0A2516108DF11E9BD0BB4B775E93A54" xmp:CreatorTool="Adobe Photoshop CC 2014 (Windows)"> <xmpMM:DerivedFrom stRef:instanceID="xmp.iid:3DFCD658D5C411E8939893D4C7F785F1" stRef:documentID="xmp.did:3DFCD659D5C411E8939893D4C7F785F1"/> </rdf:Description> </rdf:RDF> </x:xmpmeta> <?xpacket end="r"?>���}�IDATx�b���?�@&���
� s�={V�ꁘ��_@l�'ɱLJJ
����X
l@��%��cb�k����(���x?�G������ɓ'w��A���fx�����1�[XX0�8qE����AOO��������HUU��@����L;88��\�t��ŋ222�
�?��W�<}��a�78K�P�ZBB�t��5.@��FfvbDns�E�6����hY=�-މ3q������CD��;�5IEND�B`�admin/partials/images/storage-ftp.png000064400000002270151327705670013677 0ustar00�PNG


IHDR;0��tEXtSoftwareAdobe ImageReadyq�e<&iTXtXML:com.adobe.xmp<?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?> <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.6-c132 79.159284, 2016/04/19-13:13:40        "> <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <rdf:Description rdf:about="" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmlns:stRef="http://ns.adobe.com/xap/1.0/sType/ResourceRef#" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmpMM:DocumentID="xmp.did:6872EC7108DE11E98464F5E37B8271B5" xmpMM:InstanceID="xmp.iid:6872EC7008DE11E98464F5E37B8271B5" xmp:CreatorTool="Adobe Photoshop CC 2014 (Windows)"> <xmpMM:DerivedFrom stRef:instanceID="xmp.iid:3DFCD658D5C411E8939893D4C7F785F1" stRef:documentID="xmp.did:3DFCD659D5C411E8939893D4C7F785F1"/> </rdf:Description> </rdf:RDF> </x:xmpmeta> <?xpacket end="r"?>�@~`(IDATx�b���?�@&���
� sޭ�)���_=�l�����Hى��:I�e�	�%U��ś�?�~���˧g@�1���X�B�Fq�?�I���cͫ�f5dx�Y3222�I1|~���������,�F~	�������|<�1
��``��F{̾�j삘�/W10趡�}漛@�?I�f�)��.��琅�5��H̓���
:53A
"��(�[f�5�_m'�b|A����H��(�"S0��h�9|-fbfމ;q����T�PdIʜ>IEND�B`�admin/partials/images/storage-sftp.png000064400000002421151327705670014060 0ustar00�PNG


IHDR;0��tEXtSoftwareAdobe ImageReadyq�e<&iTXtXML:com.adobe.xmp<?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?> <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.6-c132 79.159284, 2016/04/19-13:13:40        "> <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <rdf:Description rdf:about="" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmlns:stRef="http://ns.adobe.com/xap/1.0/sType/ResourceRef#" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmpMM:DocumentID="xmp.did:A21976ED08DE11E98652A10F1D3E8943" xmpMM:InstanceID="xmp.iid:A21976EC08DE11E98652A10F1D3E8943" xmp:CreatorTool="Adobe Photoshop CC 2014 (Windows)"> <xmpMM:DerivedFrom stRef:instanceID="xmp.iid:3DFCD658D5C411E8939893D4C7F785F1" stRef:documentID="xmp.did:3DFCD659D5C411E8939893D4C7F785F1"/> </rdf:Description> </rdf:RDF> </x:xmpmeta> <?xpacket end="r"?>-��j�IDATx�b���?�@&���
� sޭ�)���_=�l�����Hى��:I�e�	�%U��ś�?�~���˧g@�1���X�B�Fq�?�I���cͫ�f5dx�Y3222�I1|~���������,�F~	�������|<�8�7c`P˅�����)�1������[^T�W10ȇ!e�;7��X�b����I�����4�AX�=��������;����'��w瀖�00p�-����@��L#|rU�d ��}
��M��p� ;���>"-6���9	A,&�@�?ٌHH�DtwP%US5I�%Td
b-����L��;q'�Ѡ��� �=_�O�s�(IEND�B`�admin/partials/images/add-storages.png000064400000002201151327705670014013 0ustar00�PNG


IHDR;0��tEXtSoftwareAdobe ImageReadyq�e<&iTXtXML:com.adobe.xmp<?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?> <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.6-c132 79.159284, 2016/04/19-13:13:40        "> <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <rdf:Description rdf:about="" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmlns:stRef="http://ns.adobe.com/xap/1.0/sType/ResourceRef#" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmpMM:DocumentID="xmp.did:093ACC69043911E9BCC5AB519ABAFCEF" xmpMM:InstanceID="xmp.iid:093ACC68043911E9BCC5AB519ABAFCEF" xmp:CreatorTool="Adobe Photoshop CC 2014 (Windows)"> <xmpMM:DerivedFrom stRef:instanceID="xmp.iid:7238E37DD5C911E8B15B9FF4C86E8873" stRef:documentID="xmp.did:7238E37ED5C911E8B15B9FF4C86E8873"/> </rdf:Description> </rdf:RDF> </x:xmpmeta> <?xpacket end="r"?>�R��IDATx�b���?�@���3�Z*%%��&�	��?O��g"��#^�n9#(��>��@,į��IF �b? �@��M ���P_߃� [,
fZ�@̇�bz�{�,�C���Y�J���Y�B��b�x7)��b�މ�a4U����aB�3U���jF=z0�3���vY�HF��zH%����AMS�H��@�
jo�B	���qu_#�" V�����t��0T�
 �LWY��X�IEND�B`�admin/partials/images/storage-microsoft-onedrive.png000064400000007671151327705670016736 0ustar00�PNG


IHDR;0��
CiCCPICC profilexڝSwX��>�eVB��l�"#��Y��a�@Ņ�
V�HU�
H���(�gA��Z�U\8�ܧ�}z��������y��&��j9R�<:��OH�ɽ�H� ���g��yx~t�?��op�.$���P&W ��"��R�.T���S�d
�ly|B"�
��I>ة��آ���(G$@�`U�R,����@".���Y�2G��v�X�@`��B,� 8C� L�0ҿ�_p��H�˕͗K�3���w����!��l�Ba)f	�"���#H�L����8?������f�l��Ţ�k�o">!����N���_���p��u�k�[�Vh�]3�	�Z
�z��y8�@��P�<
�%b��0�>�3�o�~��@��z�q�@������qanv�R���B1n��#�Dž��)��4�\,��X��P"M�y�R�D!ɕ��2���	�w
��O�N���l�~��X�v@~�-��g42y�����@+͗����\��L�D��*�A�������aD@$�<B�
��AT�:��������18
��\��p`����	A�a!:�b��"���"aH4��� �Q"��r��Bj�]H#�-r9�\@���� 2����G1���Q�u@���Ơs�t4]���k��=�����K�ut}��c��1f��a\��E`�X&�c�X5V�5cX7v��a�$���^��l���GXLXC�%�#��W	��1�'"��O�%z��xb:��XF�&�!!�%^'_�H$ɒ�N
!%�2IIkH�H-�S�>�i�L&�m������ �����O�����:ň�L	�$R��J5e?���2B���Qͩ����:�ZIm�vP/S��4u�%͛Cˤ-��Кigi�h/�t�	݃E�З�k�����w
�
��Hb(k{��/�L�ӗ��T0�2�g��oUX*�*|���:�V�~��TUsU?�y�T�U�^V}�FU�P�	��թU��6��RwR�P�Q_��_���c
���F��H�Tc���!�2e�XB�rV�,k�Mb[���Lv�v/{LSCs�f�f�f��q�Ʊ��9ٜJ�!�
�{--?-��j�f�~�7�zھ�b�r�����up�@�,��:m:�u	�6�Q����u��>�c�y�	�����G�m������7046�l18c�̐c�k�i�����h���h��I�'�&�g�5x>f�ob�4�e�k<abi2ۤĤ��)͔k�f�Ѵ�t���,ܬج��9՜k�a�ټ����E��J�6�ǖږ|��M����V>VyV�V׬I�\�,�m�WlPW��:�˶�����v�m���)�)�Sn�1��
���9�a�%�m����;t;|rtu�vlp���4éĩ��Wgg�s��5�K���v�Sm���n�z˕��ҵ�����ܭ�m���=�}��M.��]�=�A��X�q�㝧�����/^v^Y^��O��&��0m���[��{`:>=e���>�>�z�����"�=�#~�~�~���;������y��N`������k��5��/>B	
Yr�o���c3�g,����Z�0�&L�����~o��L�̶��Gl��i��})*2�.�Q�Stqt�,֬�Y�g��񏩌�;�j�rvg�jlRlc웸�����x��E�t$	�����=��s�l�3��T�tc��ܢ����˞w<Y5Y�|8����?� BP/O�nM򄛅OE����Q���J<��V��8�;}C�h�OFu�3	OR+y���#�MVD�ެ��q�-9�����R
i��+�0�(�Of++�
�y�m�����#�s��l�Lѣ�R�PL/�+x[[x�H�HZ�3�f��#�|���P���ظxY��"�E�#�Sw.1]R�dxi��}�h˲��P�XRU�jy��R�ҥ�C+�W4�����n��Z�ca�dU�j��[V*�_�p�����F���WN_�|�ym���J����H��n��Y��J�jA�І�
���_mJ�t�zj��ʹ���5a5�[̶���6��z�]�V������&�ֿ�w{��;��켵+xWk�E}�n��ݏb���~ݸGwOŞ�{�{�E��jtolܯ���	mR6�H:p囀oڛ�w�pZ*�A�'ߦ|{�P������ߙ���Hy+�:�u�-�m�=���茣�^G���~�1�cu�5�W���(=�䂓�d���N?=ԙ�y�L��k]Q]�gCϞ?t�L�_�����]�p�"�b�%�K�=�=G~p��H�[o�e���W<�t�M�;����j��s��.]�y�����n&��%���v��w
�L�]z�x����������e�m�`�`��Y�	�����Ӈ��G�G�#F#���
��dΓ᧲���~V�y�s����K�X�����Ͽ�y��r﫩�:�#���y=���}���ǽ�(�@�P��cǧ�O�>�|��/���9%tEXtSoftwareAdobe ImageReadyq�e<&iTXtXML:com.adobe.xmp<?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?> <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.6-c014 79.156797, 2014/08/20-09:53:02        "> <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <rdf:Description rdf:about="" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmlns:stRef="http://ns.adobe.com/xap/1.0/sType/ResourceRef#" xmp:CreatorTool="Adobe Photoshop CC 2014 (Windows)" xmpMM:InstanceID="xmp.iid:5F5D97DCD5C311E8AC65CCF3D46ABEE8" xmpMM:DocumentID="xmp.did:5F5D97DDD5C311E8AC65CCF3D46ABEE8"> <xmpMM:DerivedFrom stRef:instanceID="xmp.iid:5F5D97DAD5C311E8AC65CCF3D46ABEE8" stRef:documentID="xmp.did:5F5D97DBD5C311E8AC65CCF3D46ABEE8"/> </rdf:Description> </rdf:RDF> </x:xmpmeta> <?xpacket end="r"?>X$�IDATx���+EA�����<J)�GI]��
�P���	Ɇ�,H�"�<")l�R�dE�A�����fSJw�kNg��w�73�����
������q���0�;EU�PuC%��1����B˛���Sd|>�(F�M�ъ�-�@�`%�A#gu	�0���RcPc5�v��� e�� BM�����6w)b���f���RDb	ʥT�0�t�:�B!$��i�Fw�U����`n�Va
���Wiދ��Ⱥ��>��gR���1�N��4���^�|��Q�}���\X�1�j�tC���"���|�eL�6��A5%߼����~$�<��aS)�t)�or�_erH�3C{��!hPt�Gh�RQ��h�N��06�	񰋼T%�Vt8�t�.$�^�xZs{��xf��=p��a�(v}sȯd��p��Q�Q�gy;y$�6�V�����n�����#�``�~���IEND�B`�admin/partials/wpvivid-website-info-page-display.php000064400000003220151327705670016634 0ustar00<?php
?>
<script>
    function wpvivid_get_ini_memory_limit() {
        var ajax_data = {
            'action': 'wpvivid_get_ini_memory_limit'
        };
        wpvivid_post_request(ajax_data, function (data) {
            try {
                jQuery('#wpvivid_websiteinfo_list tr').each(function (i) {
                    jQuery(this).children('td').each(function (j) {
                        if (j == 0) {
                            if (jQuery(this).html().indexOf('memory_limit') >= 0) {
                                jQuery(this).next().html(data);
                            }
                        }
                    });
                });
            }
            catch (err) {
                setTimeout(function ()
                {
                    wpvivid_get_ini_memory_limit();
                }, 3000);
            }
        }, function (XMLHttpRequest, textStatus, errorThrown) {
            setTimeout(function ()
            {
                wpvivid_get_ini_memory_limit();
            }, 3000);
        });
    }

    //
    jQuery('#wpvivid_tab_debug').click(function()
    {
        wpvivid_get_ini_memory_limit();
    });

    jQuery(document).ready(function ()
    {
        jQuery(document).on('wpvivid-switch-tabs', function(event,contentName)
        {
            if(contentName=='debug-page')
            {
                wpvivid_get_ini_memory_limit();
            }

            if(contentName=='settings-page')
            {
                wpvivid_calculate_diskspaceused();
            }
        });

        //wpvivid_get_ini_memory_limit();
    });
</script>
<?php
?>
admin/partials/wpvivid-backup-restore-page-display.php000064400000433255151327705670017206 0ustar00<?php

if ( ! class_exists( 'WP_List_Table' ) )
{
    require_once( ABSPATH . 'wp-admin/includes/class-wp-list-table.php' );
}

class WPvivid_Files_List extends WP_List_Table
{
    public $page_num;
    public $file_list;
    public $backup_id;

    public function __construct( $args = array() )
    {
        parent::__construct(
            array(
                'plural' => 'files',
                'screen' => 'files'
            )
        );
    }

    protected function get_table_classes()
    {
        return array( 'widefat striped' );
    }

    public function get_columns()
    {
        $columns = array();
        $columns['wpvivid_file'] = __( 'File', 'wpvivid-backuprestore' );
        return $columns;
    }

    public function _column_wpvivid_file( $file )
    {
        echo '<td class="tablelistcolumn">
                    <div style="padding:0 0 10px 0;">
                        <span>'. esc_html($file['key']).'</span>
                    </div>
                    <div class="wpvivid-download-status" style="padding:0;">';
        if($file['status']=='completed')
        {
            echo '<span>'.esc_html__('File Size: ', 'wpvivid-backuprestore').'</span><span class="wpvivid-element-space-right wpvivid-download-file-size">'.esc_html($file['size']).'</span><span class="wpvivid-element-space-right">|</span><span class=" wpvivid-element-space-right wpvivid-ready-download"><a style="cursor: pointer;">'. esc_html__('Download', 'wpvivid-backuprestore').'</a></span>';
        }
        else if($file['status']=='file_not_found')
        {
            echo '<span>' . esc_html__('File not found', 'wpvivid-backuprestore') . '</span>';
        }
        else if($file['status']=='need_download')
        {
            echo '<span>'.esc_html__('File Size: ', 'wpvivid-backuprestore').'</span><span class="wpvivid-element-space-right wpvivid-download-file-size">'.esc_html($file['size']).'</span><span class="wpvivid-element-space-right">|</span><span class="wpvivid-element-space-right"><a class="wpvivid-download" style="cursor: pointer;">'. esc_html__('Prepare to Download', 'wpvivid-backuprestore').'</a></span>';
        }
        else if($file['status']=='running')
        {
            echo '<div class="wpvivid-element-space-bottom">
                        <span class="wpvivid-element-space-right">' . esc_html__('Retriving (remote storage to web server)', 'wpvivid-backuprestore') . '</span><span class="wpvivid-element-space-right">|</span><span>' . esc_html__('File Size: ', 'wpvivid-backuprestore') . '</span><span class="wpvivid-element-space-right wpvivid-download-file-size">'.esc_html($file['size']).'</span><span class="wpvivid-element-space-right">|</span><span>'. esc_html__('Downloaded Size: ', 'wpvivid-backuprestore').'</span><span>'.esc_html($file['downloaded_size']).'</span>
                    </div>
                    <div style="width:100%;height:10px; background-color:#dcdcdc;">
                        <div style="background-color:#0085ba; float:left;width:'.esc_attr($file['progress_text']).'%;height:10px;"></div>
                    </div>';
        }
        else if($file['status']=='timeout')
        {
            echo '<div class="wpvivid-element-space-bottom">
                        <span>Download timeout, please retry.</span>
                    </div>
                    <div>
                        <span>'.esc_html__('File Size: ', 'wpvivid-backuprestore').'</span><span class="wpvivid-element-space-right wpvivid-download-file-size">'.esc_html($file['size']).'</span><span class="wpvivid-element-space-right">|</span><span class="wpvivid-element-space-right"><a class="wpvivid-download" style="cursor: pointer;">'. esc_html__('Prepare to Download', 'wpvivid-backuprestore').'</a></span>
                    </div>';
        }
        else if($file['status']=='error')
        {
            echo '<div class="wpvivid-element-space-bottom">
                        <span>'.esc_html($file['error']).'</span>
                    </div>
                    <div>
                        <span>'.esc_html__('File Size: ', 'wpvivid-backuprestore').'</span><span class="wpvivid-element-space-right wpvivid-download-file-size">'.esc_html($file['size']).'</span><span class="wpvivid-element-space-right">|</span><span class="wpvivid-element-space-right"><a class="wpvivid-download" style="cursor: pointer;">'. esc_html__('Prepare to Download', 'wpvivid-backuprestore').'</a></span>
                    </div>';
        }

        echo '</div></td>';
        //size
    }

    public function set_files_list($file_list,$backup_id,$page_num=1)
    {
        $this->file_list=$file_list;
        $this->backup_id=$backup_id;
        $this->page_num=$page_num;
    }

    public function get_pagenum()
    {
        if($this->page_num=='first')
        {
            $this->page_num=1;
        }
        else if($this->page_num=='last')
        {
            $this->page_num=$this->_pagination_args['total_pages'];
        }
        $pagenum = $this->page_num ? $this->page_num : 0;

        if ( isset( $this->_pagination_args['total_pages'] ) && $pagenum > $this->_pagination_args['total_pages'] )
        {
            $pagenum = $this->_pagination_args['total_pages'];
        }

        return max( 1, $pagenum );
    }

    public function prepare_items()
    {
        $columns = $this->get_columns();
        $hidden = array();
        $sortable = array();
        $this->_column_headers = array($columns, $hidden, $sortable);

        $total_items =sizeof($this->file_list);

        $this->set_pagination_args(
            array(
                'total_items' => $total_items,
                'per_page'    => 10,
            )
        );
    }

    public function has_items()
    {
        return !empty($this->file_list);
    }

    public function display_rows()
    {
        $this->_display_rows($this->file_list);
    }

    private function _display_rows($file_list)
    {
        $page=$this->get_pagenum();

        $page_file_list=array();
        $count=0;
        while ( $count<$page )
        {
            $page_file_list = array_splice( $file_list, 0, 10);
            $count++;
        }
        foreach ( $page_file_list as $key=>$file)
        {
            $file['key']=$key;
            $this->single_row($file);
        }
    }

    public function single_row($file)
    {
        ?>
        <tr slug="<?php echo esc_attr($file['key'])?>">
            <?php $this->single_row_columns( $file ); ?>
        </tr>
        <?php
    }

    protected function pagination( $which )
    {
        if ( empty( $this->_pagination_args ) )
        {
            return;
        }

        $total_items     = $this->_pagination_args['total_items'];
        $total_pages     = $this->_pagination_args['total_pages'];
        $infinite_scroll = false;
        if ( isset( $this->_pagination_args['infinite_scroll'] ) )
        {
            $infinite_scroll = $this->_pagination_args['infinite_scroll'];
        }

        if ( 'top' === $which && $total_pages > 1 )
        {
            $this->screen->render_screen_reader_content( 'heading_pagination' );
        }

        $output = '<span class="displaying-num">' . sprintf( _n( '%s item', '%s items', $total_items, 'wpvivid-backuprestore' ), number_format_i18n( $total_items ) ) . '</span>';

        $current              = $this->get_pagenum();

        $page_links = array();

        $total_pages_before = '<span class="paging-input">';
        $total_pages_after  = '</span></span>';

        $disable_first = $disable_last = $disable_prev = $disable_next = false;

        if ( $current == 1 ) {
            $disable_first = true;
            $disable_prev  = true;
        }
        if ( $current == 2 ) {
            $disable_first = true;
        }
        if ( $current == $total_pages ) {
            $disable_last = true;
            $disable_next = true;
        }
        if ( $current == $total_pages - 1 ) {
            $disable_last = true;
        }

        if ( $disable_first ) {
            $page_links[] = '<span class="tablenav-pages-navspan button disabled" aria-hidden="true">&laquo;</span>';
        } else {
            $page_links[] = sprintf(
                "<div class='first-page button'><span class='screen-reader-text'>%s</span><span aria-hidden='true'>%s</span></div>",
                __( 'First page', 'wpvivid-backuprestore' ),
                '&laquo;'
            );
        }

        if ( $disable_prev ) {
            $page_links[] = '<span class="tablenav-pages-navspan button disabled" aria-hidden="true">&lsaquo;</span>';
        } else {
            $page_links[] = sprintf(
                "<div class='prev-page button' value='%s'><span class='screen-reader-text'>%s</span><span aria-hidden='true'>%s</span></div>",
                $current,
                __( 'Previous page', 'wpvivid-backuprestore' ),
                '&lsaquo;'
            );
        }

        if ( 'bottom' === $which ) {
            $html_current_page  = $current;
            $total_pages_before = '<span class="screen-reader-text">' . __( 'Current Page', 'wpvivid-backuprestore' ) . '</span><span id="table-paging" class="paging-input"><span class="tablenav-paging-text">';
        } else {
            $html_current_page = sprintf(
                "%s<input class='current-page' id='current-page-selector-filelist' type='text' name='paged' value='%s' size='%d' aria-describedby='table-paging' /><span class='tablenav-paging-text'>",
                '<label for="current-page-selector-filelist" class="screen-reader-text">' . __( 'Current Page', 'wpvivid-backuprestore' ) . '</label>',
                $current,
                strlen( $total_pages )
            );
        }
        $html_total_pages = sprintf( "<span class='total-pages'>%s</span>", number_format_i18n( $total_pages ) );
        $page_links[]     = $total_pages_before . sprintf( _x( '%1$s of %2$s', 'paging', 'wpvivid-backuprestore' ), $html_current_page, $html_total_pages ) . $total_pages_after;

        if ( $disable_next ) {
            $page_links[] = '<span class="tablenav-pages-navspan button disabled" aria-hidden="true">&rsaquo;</span>';
        } else {
            $page_links[] = sprintf(
                "<div class='next-page button' value='%s'><span class='screen-reader-text'>%s</span><span aria-hidden='true'>%s</span></div>",
                $current,
                __( 'Next page', 'wpvivid-backuprestore' ),
                '&rsaquo;'
            );
        }

        if ( $disable_last ) {
            $page_links[] = '<span class="tablenav-pages-navspan button disabled" aria-hidden="true">&raquo;</span>';
        } else {
            $page_links[] = sprintf(
                "<div class='last-page button'><span class='screen-reader-text'>%s</span><span aria-hidden='true'>%s</span></div>",
                __( 'Last page', 'wpvivid-backuprestore' ),
                '&raquo;'
            );
        }

        $pagination_links_class = 'pagination-links';
        if ( ! empty( $infinite_scroll ) ) {
            $pagination_links_class .= ' hide-if-js';
        }
        $output .= "\n<span class='$pagination_links_class'>" . join( "\n", $page_links ) . '</span>';

        if ( $total_pages ) {
            $page_class = $total_pages < 2 ? ' one-page' : '';
        } else {
            $page_class = ' no-pages';
        }
        $this->_pagination = "<div class='tablenav-pages{$page_class}'>$output</div>";

        echo $this->_pagination;
    }

    protected function display_tablenav( $which ) {
        $css_type = '';
        if ( 'top' === $which ) {
            wp_nonce_field( 'bulk-' . $this->_args['plural'] );
            $css_type = 'margin: 0 0 10px 0';
        }
        else if( 'bottom' === $which ) {
            $css_type = 'margin: 10px 0 0 0';
        }

        $total_pages     = $this->_pagination_args['total_pages'];
        if ( $total_pages >1)
        {
            ?>
            <div class="tablenav <?php echo esc_attr( $which ); ?>" style="<?php echo esc_attr($css_type); ?>">
                <?php
                $this->extra_tablenav( $which );
                $this->pagination( $which );
                ?>

                <br class="clear" />
            </div>
            <?php
        }
    }

    public function display()
    {
        $singular = $this->_args['singular'];

        $this->display_tablenav( 'top' );

        $this->screen->render_screen_reader_content( 'heading_list' );
        ?>
        <table class="wp-list-table <?php echo esc_attr(implode( ' ', $this->get_table_classes() )); ?>">
            <thead>
            <tr>
                <?php $this->print_column_headers(); ?>
            </tr>
            </thead>

            <tbody id="the-list"
                <?php
                if ( $singular ) {
                    echo esc_attr(" data-wp-lists='list:$singular'");
                }
                ?>
            >
            <?php $this->display_rows_or_placeholder(); ?>
            </tbody>

        </table>
        <?php
        $this->display_tablenav( 'bottom' );
    }
}


function wpvivid_add_backup_type($type_name)
{
    ?>
    <label>
        <input type="radio" option="backup" name="<?php echo esc_attr($type_name); ?>" value="files+db" checked />
        <span><?php esc_html_e( 'Database + Files (WordPress Files)', 'wpvivid-backuprestore' ); ?></span>
    </label><br>
    <label>
        <input type="radio" option="backup" name="<?php echo esc_attr($type_name); ?>" value="files" />
        <span><?php esc_html_e( 'WordPress Files (Exclude Database)', 'wpvivid-backuprestore' ); ?></span>
    </label><br>
    <label>
        <input type="radio" option="backup" name="<?php echo esc_attr($type_name); ?>" value="db" />
        <span><?php esc_html_e( 'Only Database', 'wpvivid-backuprestore' ); ?></span>
    </label><br>
    <label>
        <div style="float: left;">
            <input type="radio" disabled />
            <span class="wpvivid-element-space-right" style="color: #ddd;"><?php esc_html_e('Custom', 'wpvivid-backuprestore'); ?></span>
        </div>
        <span class="wpvivid-feature-pro">
            <a href="https://docs.wpvivid.com/wpvivid-backup-pro-overview.html" style="text-decoration: none;"><?php esc_html_e('Pro feature: learn more', 'wpvivid-backuprestore'); ?></a>
        </span>
    </label>
    <br>
    <?php
}

function wpvivid_backup_do_js()
{
    global $wpvivid_plugin;
    $backup_task=$wpvivid_plugin->backup2->_list_tasks_ex();
    $general_setting=WPvivid_Setting::get_setting(true, "");

    if( (isset($general_setting['options']['wpvivid_common_setting']['estimate_backup'])) &&($general_setting['options']['wpvivid_common_setting']['estimate_backup'] == 0) )
    {
        ?>
        jQuery('#wpvivid_estimate_backup_info').hide();
        <?php
    }

    if($backup_task['progress_html']===false)
    {
        ?>
        jQuery('#wpvivid_postbox_backup_percent').hide();
        jQuery('#wpvivid_backup_cancel_btn').css({'pointer-events': 'auto', 'opacity': '1'});
        jQuery('#wpvivid_quickbackup_btn').css({'pointer-events': 'auto', 'opacity': '1'});
        <?php
    }
    else
    {
        ?>
        jQuery('#wpvivid_postbox_backup_percent').show();
        <?php
    }
}

function wpvivid_download_backup_descript($html){
    $html = '<p><strong>'.__('About backup download', 'wpvivid-backuprestore').'</strong></p>';
    $html .= '<ul>';
    $html .= '<li>'.__('->If backups are stored in remote storage, our plugin will retrieve the backup to your web server first. This may take a little time depending on the size of backup files. Please be patient. Then you can download them to your PC.', 'wpvivid-backuprestore').'</li>';
    $html .= '<li>'.__('->If backups are stored in web server, the plugin will list all relevant files immediately.', 'wpvivid-backuprestore').'</li>';
    $html .= '</ul>';
    return $html;
}

function wpvivid_restore_website_descript($html){
    $html = '<p><a href="#" id="wpvivid_how_to_restore_backup_describe" onclick="wpvivid_click_how_to_restore_backup();" style="text-decoration: none;">'.__('How to restore your website from a backup(scheduled, manual, uploaded and received backup)', 'wpvivid-backuprestore').'</a></p>';
    $html .= '<div id="wpvivid_how_to_restore_backup"></div>';
    return $html;
}

function wpvivid_backuppage_load_backuplist($backuplist_array){
    $backuplist_array['list_backup'] = array('index' => '1', 'tab_func' => 'wpvivid_backuppage_add_tab_backup', 'page_func' => 'wpvivid_backuppage_add_page_backup');
    $backuplist_array['list_log'] = array('index' => '3', 'tab_func' => 'wpvivid_backuppage_add_tab_log', 'page_func' => 'wpvivid_backuppage_add_page_log');
    $backuplist_array['list_restore'] = array('index' => '4', 'tab_func' => 'wpvivid_backuppage_add_tab_restore', 'page_func' => 'wpvivid_backuppage_add_page_restore');
    $backuplist_array['list_download'] = array('index' => '5', 'tab_func' => 'wpvivid_backuppage_add_tab_downlaod', 'page_func' => 'wpvivid_backuppage_add_page_downlaod');
    return $backuplist_array;
}

function wpvivid_backuppage_add_tab_backup(){
    ?>
    <a href="#" id="wpvivid_tab_backup" class="nav-tab backup-nav-tab nav-tab-active" onclick="switchrestoreTabs(event,'page-backups')"><?php esc_html_e('Backups', 'wpvivid-backuprestore'); ?></a>
    <?php
}

function wpvivid_backuppage_add_tab_log(){
    ?>
    <a href="#" id="wpvivid_tab_backup_log" class="nav-tab backup-nav-tab delete" onclick="switchrestoreTabs(event,'page-log')" style="display: none;">
        <div style="margin-right: 15px;"><?php esc_html_e('Log', 'wpvivid-backuprestore'); ?></div>
        <div class="nav-tab-delete-img">
            <img src="<?php echo esc_url(plugins_url( 'images/delete-tab.png', __FILE__ )); ?>" style="vertical-align:middle; cursor:pointer;" onclick="wpvivid_close_tab(event, 'wpvivid_tab_backup_log', 'backup', 'wpvivid_tab_backup');" />
        </div>
    </a>
    <?php
}

function wpvivid_backuppage_add_tab_restore(){
    ?>
    <a href="#" id="wpvivid_tab_restore" class="nav-tab backup-nav-tab delete" onclick="switchrestoreTabs(event,'page-restore')" style="display: none;">
        <div style="margin-right: 15px;"><?php esc_html_e('Restore', 'wpvivid-backuprestore'); ?></div>
        <div class="nav-tab-delete-img">
            <img src="<?php echo esc_url(plugins_url( 'images/delete-tab.png', __FILE__ )); ?>" style="vertical-align:middle; cursor:pointer;" onclick="wpvivid_close_tab(event, 'wpvivid_tab_restore', 'backup', 'wpvivid_tab_backup');" />
        </div>
    </a>
    <?php
}

function wpvivid_backuppage_add_tab_downlaod(){
    ?>
    <a href="#" id="wpvivid_tab_download" class="nav-tab backup-nav-tab delete" onclick="switchrestoreTabs(event,'page-download')" style="display: none;">
        <div style="margin-right: 15px;"><?php esc_html_e('Download', 'wpvivid-backuprestore'); ?></div>
        <div class="nav-tab-delete-img">
            <img src="<?php echo esc_url(plugins_url( 'images/delete-tab.png', __FILE__ )); ?>" style="vertical-align:middle; cursor:pointer;" onclick="wpvivid_close_tab(event, 'wpvivid_tab_download', 'backup', 'wpvivid_tab_backup');" />
        </div>
    </a>
    <?php
}

function wpvivid_backuppage_add_page_backup(){
    $backuplist=WPvivid_Backuplist::get_backuplist();
    $display_backup_count = WPvivid_Setting::get_max_backup_count();
    ?>
    <div class="backup-tab-content wpvivid_tab_backup" id="page-backups">
        <div style="margin-top:10px; margin-bottom:10px;">
            <?php
            echo '<p><strong>'.esc_html__('About backup download', 'wpvivid-backuprestore').'</strong></p>';
            echo '<ul>';
            echo '<li>'.esc_html__('->If backups are stored in remote storage, our plugin will retrieve the backup to your web server first. This may take a little time depending on the size of backup files. Please be patient. Then you can download them to your PC.', 'wpvivid-backuprestore').'</li>';
            echo '<li>'.esc_html__('->If backups are stored in web server, the plugin will list all relevant files immediately.', 'wpvivid-backuprestore').'</li>';
            echo '</ul>';
            ?>
        </div>
        <div style="margin-bottom:10px;">
            <?php
            echo '<p><a href="#" id="wpvivid_how_to_restore_backup_describe" onclick="wpvivid_click_how_to_restore_backup();" style="text-decoration: none;">'.esc_html__('How to restore your website from a backup(scheduled, manual, uploaded and received backup)', 'wpvivid-backuprestore').'</a></p>';
            echo '<div id="wpvivid_how_to_restore_backup"></div>';
            ?>
        </div>
        <div style="clear:both;"></div>
        <?php
        do_action('wpvivid_rescan_backup_list');
        ?>
        <table class="wp-list-table widefat plugins" id="wpvivid_backuplist_table" style="border-collapse: collapse;">
            <thead>
            <tr class="backup-list-head" style="border-bottom: 0;">
                <td></td>
                <th><?php esc_html_e( 'Backup','wpvivid-backuprestore'); ?></th>
                <th><?php esc_html_e( 'Storage','wpvivid-backuprestore'); ?></th>
                <th><?php esc_html_e( 'Download','wpvivid-backuprestore'); ?></th>
                <th><?php esc_html_e( 'Restore', 'wpvivid-backuprestore'); ?></th>
                <th><?php esc_html_e( 'Delete','wpvivid-backuprestore'); ?></th>
            </tr>
            </thead>
            <tbody class="wpvivid-backuplist" id="wpvivid_backup_list">
            <?php
            do_action('wpvivid_add_backup_list_output');
            ?>
            </tbody>
            <tfoot>
            <tr>
                <th><input name="" type="checkbox" id="backup_list_all_check" value="1" /></th>
                <th class="row-title" colspan="5"><a onclick="wpvivid_delete_backups_inbatches();" style="cursor: pointer;"><?php esc_html_e('Delete the selected backups', 'wpvivid-backuprestore'); ?></a></th>
            </tr>
            </tfoot>
        </table>
    </div>
    <script>
        function wpvivid_retrieve_backup_list(){
            var ajax_data = {
                'action': 'wpvivid_get_backup_list'
            };
            wpvivid_post_request(ajax_data, function(data){
                try {
                    var jsonarray = jQuery.parseJSON(data);
                    if (jsonarray.result === 'success') {
                        jQuery('#wpvivid_backup_list').html('');
                        jQuery('#wpvivid_backup_list').append(jsonarray.html);
                    }
                }
                catch(err){
                    alert(err);
                }
            },function(XMLHttpRequest, textStatus, errorThrown) {
                setTimeout(function () {
                    wpvivid_retrieve_backup_list();
                }, 3000);
            });
        }

        function wpvivid_handle_backup_data(data){
            try {
                var jsonarray = jQuery.parseJSON(data);
                if (jsonarray.result === 'success') {
                    jQuery('#wpvivid_backup_list').html('');
                    jQuery('#wpvivid_backup_list').append(jsonarray.html);
                }
                else if(jsonarray.result === 'failed'){
                    alert(jsonarray.error);
                }
            }
            catch(err){
                alert(err);
            }
        }

        function wpvivid_click_check_backup(backup_id, list_name){
            var name = "";
            var all_check = true;
            jQuery('#'+list_name+' tr').each(function (i) {
                jQuery(this).children('th').each(function (j) {
                    if(j === 0) {
                        var id = jQuery(this).find("input[type=checkbox]").attr("id");
                        if (id === backup_id) {
                            name = jQuery(this).parent().children('td').eq(0).find("img").attr("name");
                            if (name === "unlock") {
                                if (jQuery(this).find("input[type=checkbox]").prop('checked') === false) {
                                    all_check = false;
                                }
                            }
                            else {
                                jQuery(this).find("input[type=checkbox]").prop('checked', false);
                                all_check = false;
                            }
                        }
                        else {
                            if (jQuery(this).find("input[type=checkbox]").prop('checked') === false) {
                                all_check = false;
                            }
                        }
                    }
                });
            });
            if(all_check === true){
                jQuery('#backup_list_all_check').prop('checked', true);
            }
            else{
                jQuery('#backup_list_all_check').prop('checked', false);
            }
        }

        function wpvivid_set_backup_lock(backup_id, lock_status){
            //var check_status = true;
            if(lock_status === "lock"){
                var lock=0;
            }
            else{
                var lock=1;
            }
            //if(check_status)
            //{
                var ajax_data = {
                    'action': 'wpvivid_set_security_lock',
                    'backup_id': backup_id,
                    'lock': lock
                };
                wpvivid_post_request(ajax_data, function (data) {
                    try {
                        var jsonarray = jQuery.parseJSON(data);
                        if (jsonarray.result === 'success') {
                            jQuery('#wpvivid_lock_' + backup_id).html(jsonarray.html);
                        }
                    }
                    catch (err) {
                        alert(err);
                    }
                }, function (XMLHttpRequest, textStatus, errorThrown) {
                    var error_message = wpvivid_output_ajaxerror('setting up a lock for the backup', textStatus, errorThrown);
                    alert(error_message);
                });
            //}
        }

        function wpvivid_read_log(action, param, log_type){
            var tab_id = '';
            var content_id = '';
            var ajax_data = '';
            var show_page = '';
            if(typeof param === 'undefined')    param = '';
            if(typeof log_type === 'undefined')    log_type = '';
            switch(action){
                case 'wpvivid_view_backup_task_log':
                    ajax_data = {
                        'action':action,
                        'id':running_backup_taskid
                    };
                    tab_id = 'wpvivid_tab_backup_log';
                    content_id = 'wpvivid_display_log_content';
                    show_page = 'backup_page';
                    break;
                case 'wpvivid_read_last_backup_log':
                    var ajax_data = {
                        'action': action,
                        'log_file_name': param
                    };
                    tab_id = 'wpvivid_tab_backup_log';
                    content_id = 'wpvivid_display_log_content';
                    show_page = 'backup_page';
                    break;
                case 'wpvivid_view_backup_log':
                    var ajax_data={
                        'action':action,
                        'id':param
                    };
                    tab_id = 'wpvivid_tab_backup_log';
                    content_id = 'wpvivid_display_log_content';
                    show_page = 'backup_page';
                    break;
                case 'wpvivid_view_log':
                    var ajax_data={
                        'action':action,
                        'id':param,
                        'log_type':log_type
                    };
                    tab_id = 'wpvivid_tab_read_log';
                    content_id = 'wpvivid_read_log_content';
                    show_page = 'log_page';
                    break;
                default:
                    break;
            }
            jQuery('#'+tab_id).show();
            jQuery('#'+content_id).html("");
            if(show_page === 'backup_page'){
                //wpvivid_click_switch_backup_page(tab_id);
                wpvivid_click_switch_page('backup', tab_id, true);
            }
            else if(show_page === 'log_page') {
                wpvivid_click_switch_page('wrap', tab_id, true);
            }
            wpvivid_post_request(ajax_data, function(data){
                wpvivid_show_log(data, content_id);
            }, function(XMLHttpRequest, textStatus, errorThrown) {
                var div = 'Reading the log failed. Please try again.';
                jQuery('#wpvivid_display_log_content').html(div);
            });
        }

        /*function wpvivid_initialize_download(backup_id, list_name){
            wpvivid_reset_backup_list(list_name);
            jQuery('#wpvivid_download_loading_'+backup_id).addClass('is-active');
            tmp_current_click_backupid = backup_id;
            var ajax_data = {
                'action':'wpvivid_init_download_page',
                'backup_id':backup_id
            };
            wpvivid_post_request(ajax_data, function(data){
                try {
                    var jsonarray = jQuery.parseJSON(data);
                    jQuery('#wpvivid_download_loading_'+backup_id).removeClass('is-active');
                    if (jsonarray.result === 'success') {
                        jQuery('#wpvivid_file_part_' + backup_id).html("");
                        var i = 0;
                        var file_not_found = false;
                        var file_name = '';
                        jQuery.each(jsonarray.files, function (index, value) {
                            i++;
                            file_name = index;
                            if (value.status === 'need_download') {
                                jQuery('#wpvivid_file_part_' + backup_id).append(value.html);
                                //tmp_current_click_backupid = '';
                            }
                            else if (value.status === 'running') {
                                if (m_downloading_file_name === file_name) {
                                    wpvivid_lock_download(tmp_current_click_backupid);
                                }
                                jQuery('#wpvivid_file_part_' + backup_id).append(value.html);
                            }
                            else if (value.status === 'completed') {
                                if (m_downloading_file_name === file_name) {
                                    wpvivid_unlock_download(tmp_current_click_backupid);
                                    m_downloading_id = '';
                                    m_downloading_file_name = '';
                                }
                                jQuery('#wpvivid_file_part_' + backup_id).append(value.html);
                                //tmp_current_click_backupid = '';
                            }
                            else if (value.status === 'timeout') {
                                if (m_downloading_file_name === file_name) {
                                    wpvivid_unlock_download(tmp_current_click_backupid);
                                    m_downloading_id = '';
                                    m_downloading_file_name = '';
                                }
                                jQuery('#wpvivid_file_part_' + backup_id).append(value.html);
                                //tmp_current_click_backupid = '';
                            }
                            else if (value.status === 'file_not_found') {
                                wpvivid_unlock_download(tmp_current_click_backupid);
                                wpvivid_reset_backup_list(list_name);
                                file_not_found = true;
                                alert("Download failed, file not found. The file might has been moved, renamed or deleted. Please verify the file exists and try again.");
                                //tmp_current_click_backupid = '';
                                return false;
                            }
                        });
                        if (file_not_found === false) {
                            jQuery('#wpvivid_file_part_' + backup_id).append(jsonarray.place_html);
                        }
                    }
                }
                catch(err){
                    alert(err);
                    jQuery('#wpvivid_download_loading_'+backup_id).removeClass('is-active');
                }
            },function(XMLHttpRequest, textStatus, errorThrown){
                jQuery('#wpvivid_download_loading_'+backup_id).removeClass('is-active');
                var error_message = wpvivid_output_ajaxerror('initializing download information', textStatus, errorThrown);
                alert(error_message);
            });
        }*/

        function wpvivid_reset_backup_list(list_name){
            jQuery('#'+list_name+' tr').each(function(i){
                jQuery(this).children('td').each(function (j) {
                    if (j == 2) {
                        var backup_id = jQuery(this).parent().children('th').find("input[type=checkbox]").attr("id");
                        var download_btn = '<div id="wpvivid_file_part_' + backup_id + '" style="float:left;padding:10px 10px 10px 0px;">' +
                            '<div style="cursor:pointer;" onclick="wpvivid_initialize_download(\'' + backup_id + '\', \''+list_name+'\');" title="<?php esc_html_e('Prepare to download the backup', 'wpvivid-backuprestore'); ?>">' +
                            '<img id="wpvivid_download_btn_' + backup_id + '" src="' + wpvivid_plugurl + '/admin/partials/images/download.png" style="vertical-align:middle;" />Download' +
                            '<div class="spinner" id="wpvivid_download_loading_' + backup_id + '" style="float:right;width:auto;height:auto;padding:10px 180px 10px 0;background-position:0 0;"></div>' +
                            '</div>' +
                            '</div>';
                        jQuery(this).html(download_btn);
                    }
                });
            });
        }

        function wpvivid_lock_download(backup_id){
            jQuery('#wpvivid_backup_list tr').each(function(i){
                jQuery(this).children('td').each(function (j) {
                    if (j == 2) {
                        jQuery(this).css({'pointer-events': 'none', 'opacity': '0.4'});
                    }
                });
            });
        }

        function wpvivid_unlock_download(backup_id){
            jQuery('#wpvivid_backup_list tr').each(function(i){
                jQuery(this).children('td').each(function (j) {
                    if (j == 2) {
                        jQuery(this).css({'pointer-events': 'auto', 'opacity': '1'});
                    }
                });
            });
        }

        /**
         * Start downloading backup
         *
         * @param part_num  - The part number for the download object
         * @param backup_id - The unique ID for the backup
         * @param file_name - File name
         */
        function wpvivid_prepare_download(part_num, backup_id, file_name){
            var ajax_data = {
                'action': 'wpvivid_prepare_download_backup',
                'backup_id':backup_id,
                'file_name':file_name
            };
            var progress = '0%';
            jQuery("#"+backup_id+"-text-part-"+part_num).html("<a>Retriving(remote storage to web server)</a>");
            jQuery("#"+backup_id+"-progress-part-"+part_num).css('width', progress);
            task_retry_times = 0;
            m_need_update_2 = true;
            wpvivid_lock_download(backup_id);
            m_downloading_id = backup_id;
            tmp_current_click_backupid = backup_id;
            m_downloading_file_name = file_name;
            wpvivid_post_request(ajax_data, function(data)
            {
            }, function(XMLHttpRequest, textStatus, errorThrown)
            {
            }, 0);
        }

        /**
         * Download backups to user's computer.
         *
         * @param backup_id     - The unique ID for the backup
         * @param backup_type   - The types of the backup
         * @param file_name     - File name
         */
        function wpvivid_download(backup_id, backup_type, file_name){
            wpvivid_location_href=true;
            location.href =ajaxurl+'?_wpnonce='+wpvivid_ajax_object.ajax_nonce+'&action=wpvivid_download_backup&backup_id='+backup_id+'&download_type='+backup_type+'&file_name='+file_name;
        }

        function wpvivid_initialize_restore(backup_id, backup_time, backup_type, restore_type='backup'){
            var time_type = 'backup';
            var log_type = '';
            var tab_type = '';
            var page_type = 'backup';
            if(restore_type == 'backup'){
                time_type = 'backup';
                log_type = '';
                tab_type = '';
                page_type = 'backup';
            }
            else if(restore_type == 'transfer'){
                time_type = 'transfer';
                log_type = 'transfer_';
                tab_type = 'add_';
                page_type = 'migrate';
            }
            wpvivid_restore_backup_type = backup_type;
            jQuery('#wpvivid_restore_'+time_type+'_time').html(backup_time);
            m_restore_backup_id = backup_id;
            jQuery('#wpvivid_restore_'+log_type+'log').html("");
            jQuery('#wpvivid_'+tab_type+'tab_restore').show();
            wpvivid_click_switch_page(page_type, 'wpvivid_'+tab_type+'tab_restore', true);
            wpvivid_init_restore_data(restore_type);
        }

        function click_dismiss_restore_check_notice(obj){
            wpvivid_display_restore_check = false;
            jQuery(obj).parent().remove();
        }

        /**
         * This function will initialize restore information
         *
         * @param backup_id - The unique ID for the backup
         */
        function wpvivid_init_restore_data(restore_type)
        {
            wpvivid_resotre_is_migrate=0;
            var restore_method = '';
            if(restore_type == 'backup'){
                restore_method = '';
            }
            else if(restore_type == 'transfer'){
                restore_method = 'transfer_';
            }
            jQuery('#wpvivid_replace_domain').prop('checked', false);
            jQuery('#wpvivid_keep_domain').prop('checked', false);
            jQuery('#wpvivid_restore_'+restore_method+'btn').css({'pointer-events': 'none', 'opacity': '0.4'});
            jQuery('#wpvivid_clean_'+restore_method+'restore').css({'pointer-events': 'none', 'opacity': '0.4'});
            jQuery('#wpvivid_rollback_'+restore_method+'btn').css({'pointer-events': 'none', 'opacity': '0.4'});
            jQuery('#wpvivid_download_'+restore_method+'btn').css({'pointer-events': 'none', 'opacity': '0.4'});
            jQuery('#wpvivid_restore_'+restore_method+'part').show();
            jQuery('#wpvivid_clean_'+restore_method+'part').hide();
            jQuery('#wpvivid_rollback_'+restore_method+'part').hide();
            jQuery('#wpvivid_download_'+restore_method+'part').hide();

            jQuery('#wpvivid_init_restore_data').addClass('is-active');

            jQuery('#wpvivid_restore_progress').hide();
            jQuery('#wpvivid_restore_log').show();
            jQuery('#wpvivid_restore_box').show();
            jQuery('#wpvivid_restore_success').hide();
            jQuery('#wpvivid_restore_failed').hide();
            var ajax_data = {
                'action':'wpvivid_init_restore_page',
                'backup_id':m_restore_backup_id
            };
            wpvivid_post_request(ajax_data, function(data)
            {
                try
                {
                    var jsonarray = jQuery.parseJSON(data);
                    var init_status = false;
                    if(typeof jsonarray.has_zero_date !== 'undefined')
                    {
                        jQuery('#wpvivid_restore_zero_date_warning_info').show();
                    }
                    else
                    {
                        jQuery('#wpvivid_restore_zero_date_warning_info').hide();
                    }
                    if(typeof jsonarray.wp_version_check !== 'undefined')
                    {
                        if(!jsonarray.wp_version_check)
                        {
                            jQuery('#wpvivid_restore_wp_version_warning_info').show();
                        }
                        else
                        {
                            jQuery('#wpvivid_restore_wp_version_warning_info').hide();
                        }
                    }
                    else
                    {
                        jQuery('#wpvivid_restore_wp_version_warning_info').hide();
                    }
                    if(jsonarray.result === 'success')
                    {
                        jQuery('#wpvivid_restore_'+restore_method+'btn').css({'pointer-events': 'auto', 'opacity': '1'});
                        jQuery('#wpvivid_download_'+restore_method+'btn').css({'pointer-events': 'none', 'opacity': '0.4'});
                        jQuery('#wpvivid_restore_'+restore_method+'part').show();
                        jQuery('#wpvivid_download_'+restore_method+'part').hide();
                        wpvivid_restore_need_download = false;
                        init_status = true;
                    }
                    else if (jsonarray.result === "need_download")
                    {
                        init_status = true;
                        wpvivid_restore_download_array = new Array();
                        var download_num = 0;
                        jQuery.each(jsonarray.files, function (index, value)
                        {
                            if (value.status === "need_download")
                            {
                                wpvivid_restore_download_array[download_num] = new Array('file_name', 'size', 'md5');
                                wpvivid_restore_download_array[download_num]['file_name'] = index;
                                wpvivid_restore_download_array[download_num]['size'] = value.size;
                                wpvivid_restore_download_array[download_num]['md5'] = value.md5;
                                download_num++;
                            }
                        });
                        wpvivid_restore_download_index=0;
                        wpvivid_restore_need_download = true;
                        jQuery('#wpvivid_restore_'+restore_method+'btn').css({'pointer-events': 'none', 'opacity': '0.4'});
                        jQuery('#wpvivid_download_'+restore_method+'btn').css({'pointer-events': 'auto', 'opacity': '1'});
                        jQuery('#wpvivid_restore_'+restore_method+'part').hide();
                        jQuery('#wpvivid_download_'+restore_method+'part').show();
                    }
                    else if (jsonarray.result === "failed") {
                        jQuery('#wpvivid_init_restore_data').removeClass('is-active');
                        wpvivid_display_restore_msg(jsonarray.error, restore_type);
                    }

                    if(init_status){
                        if(jsonarray.max_allow_packet_warning != false || jsonarray.memory_limit_warning != false) {
                            if(!wpvivid_display_restore_check) {
                                wpvivid_display_restore_check = true;
                                var output = '';
                                if(jsonarray.max_allow_packet_warning != false){
                                    output += "<p>" + jsonarray.max_allow_packet_warning + "</p>";
                                }
                                if(jsonarray.memory_limit_warning != false){
                                    output += "<p>" + jsonarray.memory_limit_warning + "</p>";
                                }
                                var div = "<div class='notice notice-warning is-dismissible inline'>" +
                                    output +
                                    "<button type='button' class='notice-dismiss' onclick='click_dismiss_restore_check_notice(this);'>" +
                                    "<span class='screen-reader-text'>Dismiss this notice.</span>" +
                                    "</button>" +
                                    "</div>";
                                jQuery('#wpvivid_restore_check').append(div);
                            }
                        }
                        jQuery('#wpvivid_init_restore_data').removeClass('is-active');
                        if(wpvivid_restore_need_download == false) {
                            jQuery('#wpvivid_restore_' + restore_method + 'btn').css({'pointer-events': 'auto', 'opacity': '1'});
                            jQuery('#wpvivid_clean_' + restore_method + 'restore').css({'pointer-events': 'none', 'opacity': '0.4'});
                            jQuery('#wpvivid_rollback_' + restore_method + 'btn').css({'pointer-events': 'none', 'opacity': '0.4'});
                            jQuery('#wpvivid_restore_' + restore_method + 'part').show();
                            jQuery('#wpvivid_clean_' + restore_method + 'part').hide();
                            jQuery('#wpvivid_rollback_' + restore_method + 'part').hide();
                            jQuery('#wpvivid_restore_is_migrate').css({'pointer-events': 'auto', 'opacity': '1'});

                            jQuery('#wpvivid_restore_is_migrate').hide();
                            jQuery('#wpvivid_restore_' + restore_method + 'btn').css({'pointer-events': 'auto', 'opacity': '1'});

                            wpvivid_resotre_is_migrate = jsonarray.is_migrate;

                            if (jsonarray.is_migrate_ui === 1) {
                                jQuery('#wpvivid_restore_is_migrate').show();
                                jQuery('#wpvivid_replace_domain').prop('checked', false);
                                jQuery('#wpvivid_keep_domain').prop('checked', false);
                            }
                            else {
                                jQuery('#wpvivid_restore_is_migrate').hide();
                                jQuery('#wpvivid_restore_' + restore_method + 'btn').css({'pointer-events': 'auto', 'opacity': '1'});
                            }

                            wpvivid_interface_flow_control();
                        }
                    }
                }
                catch(err){
                    alert(err);
                }
            }, function(XMLHttpRequest, textStatus, errorThrown) {
                jQuery('#wpvivid_init_restore_data').removeClass('is-active');
                var error_message = wpvivid_output_ajaxerror('initializing restore information', textStatus, errorThrown);
                wpvivid_display_restore_msg(error_message, restore_type);
            });
        }

        function wpvivid_delete_selected_backup(backup_id, list_name){
            var name = '';
            jQuery('#wpvivid_backup_list tr').each(function(i){
                jQuery(this).children('td').each(function (j) {
                    if (j == 0) {
                        var id = jQuery(this).parent().children('th').find("input[type=checkbox]").attr("id");
                        if(id === backup_id){
                            name = jQuery(this).parent().children('td').eq(0).find('img').attr('name');
                        }
                    }
                });
            });
            var descript = '';
            var force_del = 0;
            var bdownloading = false;
            if(name === 'lock') {
                descript = '<?php esc_html_e('This backup is locked, are you sure to remove it? This backup will be deleted permanently from your hosting (localhost) and remote storages.', 'wpvivid-backuprestore'); ?>';
                force_del = 1;
            }
            else{
                descript = '<?php esc_html_e('Are you sure to remove this backup? This backup will be deleted permanently.', 'wpvivid-backuprestore'); ?>';
                force_del = 0;
            }
            if(m_downloading_id === backup_id){
                bdownloading = true;
                descript = '<?php esc_html_e('This request will delete the backup being downloaded, are you sure you want to continue?', 'wpvivid-backuprestore'); ?>';
                force_del = 1;
            }
            var ret = confirm(descript);
            if(ret === true){
                var ajax_data={
                    'action': 'wpvivid_delete_backup',
                    'backup_id': backup_id,
                    'force': force_del
                };
                wpvivid_post_request(ajax_data, function(data){
                    wpvivid_handle_backup_data(data);
                    if(bdownloading){
                        m_downloading_id = '';
                    }
                }, function(XMLHttpRequest, textStatus, errorThrown) {
                    var error_message = wpvivid_output_ajaxerror('deleting the backup', textStatus, errorThrown);
                    alert(error_message);
                });
            }
        }
        function wpvivid_delete_backups_inbatches(){
            var delete_backup_array = new Array();
            var count = 0;
            var bdownloading = false;
            jQuery('#wpvivid_backup_list tr').each(function (i) {
                jQuery(this).children('th').each(function (j) {
                    if (j == 0) {
                        if(jQuery(this).find('input[type=checkbox]').prop('checked')){
                            delete_backup_array[count] = jQuery(this).find('input[type=checkbox]').attr('id');
                            if(m_downloading_id === jQuery(this).find('input[type=checkbox]').attr('id')){
                                bdownloading = true;
                            }
                            count++;
                        }
                    }
                });
            });
            if( count === 0 ){
                alert('<?php esc_html_e('Please select at least one item.','wpvivid-backuprestore'); ?>');
            }
            else {
                var descript = '';
                if(bdownloading) {
                    descript = '<?php esc_html_e('This request might delete the backup being downloaded, are you sure you want to continue?', 'wpvivid-backuprestore'); ?>';
                }
                else{
                    descript = '<?php esc_html_e('Are you sure to remove the selected backups? These backups will be deleted permanently.', 'wpvivid-backuprestore'); ?>';
                }
                var ret = confirm(descript);
                if (ret === true) {
                    var ajax_data = {
                        'action': 'wpvivid_delete_backup_array',
                        'backup_id': delete_backup_array
                    };
                    wpvivid_post_request(ajax_data, function (data) {
                        wpvivid_handle_backup_data(data);
                        jQuery('#backup_list_all_check').prop('checked', false);
                        if(bdownloading){
                            m_downloading_id = '';
                        }
                    }, function (XMLHttpRequest, textStatus, errorThrown) {
                        var error_message = wpvivid_output_ajaxerror('deleting the backup', textStatus, errorThrown);
                        alert(error_message);
                    });
                }
            }
        }

        jQuery('#backup_list_all_check').click(function(){
            var name = '';
            if(jQuery('#backup_list_all_check').prop('checked')) {
                jQuery('#wpvivid_backup_list tr').each(function (i) {
                    jQuery(this).children('th').each(function (j) {
                        if (j == 0) {
                            name = jQuery(this).parent().children('td').eq(0).find("img").attr("name");
                            if(name === 'unlock') {
                                jQuery(this).find("input[type=checkbox]").prop('checked', true);
                            }
                            else{
                                jQuery(this).find("input[type=checkbox]").prop('checked', false);
                            }
                        }
                    });
                });
            }
            else{
                jQuery('#wpvivid_backup_list tr').each(function (i) {
                    jQuery(this).children('th').each(function (j) {
                        if (j == 0) {
                            jQuery(this).find("input[type=checkbox]").prop('checked', false);
                        }
                    });
                });
            }
        });

        function click_dismiss_restore_notice(obj){
            wpvivid_display_restore_backup = false;
            jQuery(obj).parent().remove();
        }

        function wpvivid_click_how_to_restore_backup(){
            if(!wpvivid_display_restore_backup){
                wpvivid_display_restore_backup = true;
                var top = jQuery('#wpvivid_how_to_restore_backup_describe').offset().top-jQuery('#wpvivid_how_to_restore_backup_describe').height();
                jQuery('html, body').animate({scrollTop:top}, 'slow');
                var div = "<div class='notice notice-info is-dismissible inline'>" +
                    "<p>" + wpvividlion.restore_step1 + "</p>" +
                    "<p>" + wpvividlion.restore_step2 + "</p>" +
                    "<p>" + wpvividlion.restore_step3 + "</p>" +
                    "<button type='button' class='notice-dismiss' onclick='click_dismiss_restore_notice(this);'>" +
                    "<span class='screen-reader-text'>Dismiss this notice.</span>" +
                    "</button>" +
                    "</div>";
                jQuery('#wpvivid_how_to_restore_backup').append(div);
            }
        }
    </script>
    <?php
}

function wpvivid_backuppage_add_page_log(){
    ?>
    <div class="backup-tab-content wpvivid_tab_backup_log" id="page-log" style="display:none;">
        <div class="postbox restore_log" id="wpvivid_display_log_content">
            <div></div>
        </div>
    </div>
    <?php
}

function wpvivid_backuppage_add_page_restore()
{
    $general_setting=WPvivid_Setting::get_setting(true, "");
    if(isset($general_setting['options']['wpvivid_common_setting']['restore_max_execution_time'])){
        $restore_max_execution_time = intval($general_setting['options']['wpvivid_common_setting']['restore_max_execution_time']);
    }
    else{
        $restore_max_execution_time = WPVIVID_RESTORE_MAX_EXECUTION_TIME;
    }
    ?>
    <div class="backup-tab-content wpvivid_tab_restore" id="page-restore" style="display:none;">
        <div id="wpvivid_restore_zero_date_warning_info" class="wpvivid-one-coloum" style="border:2px solid orange; margin-bottom:1em; border-radius:0.3em; display: none;">
            <span style="color:orange;">Warning: We have detected that the backup contains zero dates '0000-00-00', while NO_ZERO_DATE which forbids zero dates is enabled on the current server, which may cause a restore failure. It is recommended to temporarily disable NO_ZERO_DATE on the server.</span>
        </div>
        <div id="wpvivid_restore_wp_version_warning_info" class="wpvivid-one-coloum" style="border:2px solid orange; margin-bottom:1em; border-radius:0.3em; display: none;">
            <span style="color:orange;">We have detected that the WordPress version in the backup is too old and may not be compatible with the current site environment. It is <strong>recommended</strong> to exclude WordPress core from the restoration to avoid any compatibility issues. Some PHP functions called by old WordPress may have been deprecated in new versions. So the restoration may be successful, but the site may not run properly after that.</span>
        </div>
        <div id="wpvivid_restore_box">
            <h3><?php esc_html_e('Restore backup from:', 'wpvivid-backuprestore'); ?><span id="wpvivid_restore_backup_time"></span></h3>
            <p><strong><?php esc_html_e('Please do not close the page or switch to other pages when a restore task is running, as it could trigger some unexpected errors.', 'wpvivid-backuprestore'); ?></strong></p>
            <p><?php esc_html_e('Restore function will replace the current site\'s themes, plugins, uploads, database and/or other content directories with the existing equivalents in the selected backup.', 'wpvivid-backuprestore'); ?></p>
            <div id="wpvivid_restore_check"></div>
            <div class="restore-button-position" id="wpvivid_restore_part"><input class="button-primary" id="wpvivid_restore_btn" type="submit" name="restore" value="<?php esc_attr_e( 'Restore', 'wpvivid-backuprestore' ); ?>" onclick="wpvivid_start_restore();" /></div>
            <div class="restore-button-position" id="wpvivid_download_part">
                <input class="button-primary" id="wpvivid_download_btn" type="submit" name="download" value="<?php esc_attr_e( 'Retrieve the backup to localhost', 'wpvivid-backuprestore' ); ?>" />
                <span><?php esc_html_e('The backup is stored on the remote storage, click on the button to download it to localhost.', 'wpvivid-backuprestore'); ?></span>
            </div>
            <div class="spinner" id="wpvivid_init_restore_data" style="float:left;width:auto;height:auto;padding:10px 20px 20px 0;background-position:0 10px;"></div>
        </div>
        <div style="clear:both;"></div>
        <div id="wpvivid_restore_progress" style="margin-bottom:1em;margin-top:1em;display: none">
            <div style="padding-top:1em;border-top:1px solid #cccccc;">
                <span class="dashicons dashicons-update wpvivid-dashicons-blue"></span>
                <span>Restoring: overall progress</span>
            </div>
            <div style="padding: 1em 0;">
                    <span class="wpvivid-span-progress" id="wpvivid_main_progress">
                        <span class="action-progress-bar-percent wpvivid-span-processed-percent-progress" style="width: 0%; display:block; height:1.5em; border-radius:0; padding-left:0.5em;">0% completed</span>
                    </span>
            </div>
        </div>
        <div class="postbox restore_log" id="wpvivid_restore_log"></div>
        <div id="wpvivid_restore_success" style="display: none">
            <div style="font-size:4em; font-weight:900; color:#8bc34a; text-align:center;padding-top:1.5em;">
                <span>Congratulations !!!</span>
            </div>
            <div id="wpvivid_restore_finished_msg" style="width:600px; text-align:center; margin: 5em auto; border-top:5px solid #eaf1fe;border-bottom:5px solid #eaf1fe;">
            </div>
        </div>
        <div id="wpvivid_restore_failed" style="display: none">
            <div style="font-size:2em; font-weight:900; color:orange; text-align:center;padding-top:1.5em;">
                <span>Oops, The restoration seems to have encountered a problem:(</span>
            </div>
            <div id="wpvivid_restore_failed_msg" style="width:600px; text-align:center; margin: 5em auto; border-top:5px solid #eaf1fe;border-bottom:5px solid #eaf1fe;">
            </div>
        </div>
    </div>
    <script>
        var restore_max_exection_time = '<?php echo esc_attr($restore_max_execution_time); ?>';
        restore_max_exection_time = restore_max_exection_time * 1000;
        var m_restore_type='';
        jQuery('#wpvivid_download_btn').click(function()
        {
            wpvivid_download_restore_file('backup');
        });

        function wpvivid_download_restore_file(restore_type)
        {
            var restore_method = '';
            if(restore_type == 'backup'){
                restore_method = '';
            }
            else if(restore_type == 'transfer'){
                restore_method = 'transfer_';
            }

            jQuery('#wpvivid_download_'+restore_method+'btn').css({'pointer-events': 'none', 'opacity': '0.4'});
            wpvivid_restore_lock();
            if(wpvivid_restore_download_array.length===0)
            {
                wpvivid_display_restore_msg("Downloading backup file failed. Backup file might be deleted or network doesn't work properly. Please verify the file and confirm the network connection and try again later.", restore_type);
                wpvivid_restore_unlock();
                return false;
            }

            if(wpvivid_restore_download_index+1>wpvivid_restore_download_array.length)
            {
                var ajax_data = {
                    'action': 'wpvivid_check_download_has_zero_date',
                    'backup_id': m_restore_backup_id
                }
                wpvivid_post_request(ajax_data, function(data)
                {
                    try
                    {
                        var jsonarray = jQuery.parseJSON(data);
                        if(jsonarray.result === 'success')
                        {
                            if(typeof jsonarray.has_zero_date !== 'undefined')
                            {
                                jQuery('#wpvivid_restore_zero_date_warning_info').show();
                            }
                            else
                            {
                                jQuery('#wpvivid_restore_zero_date_warning_info').hide();
                            }

                            if(typeof jsonarray.wp_version_check !== 'undefined')
                            {
                                if(!jsonarray.wp_version_check)
                                {
                                    jQuery('#wpvivid_restore_wp_version_warning_info').show();
                                }
                                else
                                {
                                    jQuery('#wpvivid_restore_wp_version_warning_info').hide();
                                }
                            }
                            else
                            {
                                jQuery('#wpvivid_restore_wp_version_warning_info').hide();
                            }
                            wpvivid_download_display_restore_msg(restore_type, restore_method);
                        }
                        else
                        {
                            wpvivid_download_display_restore_msg(restore_type, restore_method);
                        }
                    }
                    catch(err){
                        wpvivid_download_display_restore_msg(restore_type, restore_method);
                    }
                }, function(XMLHttpRequest, textStatus, errorThrown) {
                    wpvivid_download_display_restore_msg(restore_type, restore_method);
                });
            }
            else
            {
                wpvivid_display_restore_msg("Downloading backup file " +  wpvivid_restore_download_array[wpvivid_restore_download_index]['file_name'], restore_type);
                wpvivid_display_restore_msg('', restore_type, wpvivid_restore_download_index);
                var ajax_data = {
                    'action': 'wpvivid_download_restore',
                    'backup_id': m_restore_backup_id,
                    'file_name': wpvivid_restore_download_array[wpvivid_restore_download_index]['file_name'],
                    'size': wpvivid_restore_download_array[wpvivid_restore_download_index]['size'],
                    'md5': wpvivid_restore_download_array[wpvivid_restore_download_index]['md5']
                }
                wpvivid_get_download_restore_progress_retry=0;
                wpvivid_monitor_download_restore_task(restore_type);
                wpvivid_post_request(ajax_data, function (data) {
                }, function (XMLHttpRequest, textStatus, errorThrown) {
                }, 0);
            }
        }

        function wpvivid_monitor_download_restore_task(restore_type)
        {
            var restore_method = '';
            if(restore_type == 'backup'){
                restore_method = '';
            }
            else if(restore_type == 'transfer'){
                restore_method = 'transfer_';
            }

            var ajax_data={
                'action':'wpvivid_get_download_restore_progress',
                'file_name': wpvivid_restore_download_array[wpvivid_restore_download_index]['file_name'],
                'size': wpvivid_restore_download_array[wpvivid_restore_download_index]['size'],
                'md5': wpvivid_restore_download_array[wpvivid_restore_download_index]['md5']
            };

            wpvivid_post_request(ajax_data, function(data)
            {
                try
                {
                    var jsonarray = jQuery.parseJSON(data);
                    if(typeof jsonarray ==='object')
                    {
                        if(jsonarray.result === "success")
                        {
                            if(jsonarray.status==='completed')
                            {
                                wpvivid_display_restore_msg(wpvivid_restore_download_array[wpvivid_restore_download_index]['file_name'] + ' download succeeded.', restore_type, wpvivid_restore_download_index, false);
                                wpvivid_restore_download_index++;
                                wpvivid_download_restore_file(restore_type);
                                wpvivid_restore_unlock();
                            }
                            else if(jsonarray.status==='error')
                            {
                                jQuery('#wpvivid_restore_'+restore_method+'btn').css({'pointer-events': 'none', 'opacity': '0.4'});
                                jQuery('#wpvivid_clean_'+restore_method+'restore').css({'pointer-events': 'none', 'opacity': '0.4'});
                                jQuery('#wpvivid_rollback_'+restore_method+'btn').css({'pointer-events': 'none', 'opacity': '0.4'});
                                jQuery('#wpvivid_download_'+restore_method+'btn').css({'pointer-events': 'auto', 'opacity': '1'});
                                jQuery('#wpvivid_restore_'+restore_method+'part').hide();
                                jQuery('#wpvivid_clean_'+restore_method+'part').hide();
                                jQuery('#wpvivid_rollback_'+restore_method+'part').hide();
                                jQuery('#wpvivid_download_'+restore_method+'part').show();
                                var error_message = jsonarray.error;
                                wpvivid_display_restore_msg(error_message,restore_type,wpvivid_restore_download_array[wpvivid_restore_download_index]['file_name'],false);
                                wpvivid_restore_unlock();
                            }
                            else if(jsonarray.status==='running')
                            {
                                jQuery('#wpvivid_download_'+restore_method+'btn').css({'pointer-events': 'none', 'opacity': '0.4'});
                                wpvivid_display_restore_msg(jsonarray.log, restore_type, wpvivid_restore_download_index, false);
                                setTimeout(function()
                                {
                                    wpvivid_monitor_download_restore_task(restore_type);
                                }, 3000);
                                wpvivid_restore_lock();
                            }
                            else if(jsonarray.status==='timeout')
                            {
                                wpvivid_get_download_restore_progress_retry++;
                                if(wpvivid_get_download_restore_progress_retry>10)
                                {
                                    jQuery('#wpvivid_restore_'+restore_method+'btn').css({'pointer-events': 'none', 'opacity': '0.4'});
                                    jQuery('#wpvivid_clean_'+restore_method+'restore').css({'pointer-events': 'none', 'opacity': '0.4'});
                                    jQuery('#wpvivid_rollback_'+restore_method+'btn').css({'pointer-events': 'none', 'opacity': '0.4'});
                                    jQuery('#wpvivid_download_'+restore_method+'btn').css({'pointer-events': 'auto', 'opacity': '1'});
                                    jQuery('#wpvivid_restore_'+restore_method+'part').hide();
                                    jQuery('#wpvivid_clean_'+restore_method+'part').hide();
                                    jQuery('#wpvivid_rollback_'+restore_method+'part').hide();
                                    jQuery('#wpvivid_download_'+restore_method+'part').show();
                                    var error_message = jsonarray.error;
                                    wpvivid_display_restore_msg(error_message, restore_type);
                                    wpvivid_restore_unlock();
                                }
                                else
                                {
                                    setTimeout(function()
                                    {
                                        wpvivid_monitor_download_restore_task(restore_type);
                                    }, 3000);
                                }
                            }
                            else
                            {
                                setTimeout(function()
                                {
                                    wpvivid_monitor_download_restore_task(restore_type);
                                }, 3000);
                            }
                        }
                        else
                        {
                            wpvivid_get_download_restore_progress_retry++;
                            if(wpvivid_get_download_restore_progress_retry>10)
                            {
                                jQuery('#wpvivid_restore_'+restore_method+'btn').css({'pointer-events': 'none', 'opacity': '0.4'});
                                jQuery('#wpvivid_clean_'+restore_method+'restore').css({'pointer-events': 'none', 'opacity': '0.4'});
                                jQuery('#wpvivid_rollback_'+restore_method+'btn').css({'pointer-events': 'none', 'opacity': '0.4'});
                                jQuery('#wpvivid_download_'+restore_method+'btn').css({'pointer-events': 'auto', 'opacity': '1'});
                                jQuery('#wpvivid_restore_'+restore_method+'part').hide();
                                jQuery('#wpvivid_clean_'+restore_method+'part').hide();
                                jQuery('#wpvivid_rollback_'+restore_method+'part').hide();
                                jQuery('#wpvivid_download_'+restore_method+'part').show();
                                var error_message = jsonarray.error;
                                wpvivid_display_restore_msg(error_message, restore_type);
                                wpvivid_restore_unlock();
                            }
                            else
                            {
                                setTimeout(function()
                                {
                                    wpvivid_monitor_download_restore_task(restore_type);
                                }, 3000);
                            }
                        }
                    }
                    else
                    {
                        setTimeout(function()
                        {
                            wpvivid_monitor_download_restore_task(restore_type);
                        }, 3000);
                    }
                }
                catch(err){
                    setTimeout(function()
                    {
                        wpvivid_monitor_download_restore_task(restore_type);
                    }, 3000);
                }
            }, function(XMLHttpRequest, textStatus, errorThrown)
            {
                setTimeout(function()
                {
                    wpvivid_monitor_download_restore_task(restore_type);
                }, 1000);
            });
        }

        function wpvivid_download_display_restore_msg(restore_type, restore_method)
        {
            wpvivid_display_restore_msg("Download succeeded.", restore_type);
            wpvivid_restore_need_download = false;
            jQuery('#wpvivid_restore_' + restore_method + 'btn').css({'pointer-events': 'auto', 'opacity': '1'});
            jQuery('#wpvivid_clean_' + restore_method + 'restore').css({'pointer-events': 'none', 'opacity': '0.4'});
            jQuery('#wpvivid_rollback_' + restore_method + 'btn').css({'pointer-events': 'none', 'opacity': '0.4'});
            jQuery('#wpvivid_download_'+restore_method+'btn').css({'pointer-events': 'none', 'opacity': '0.4'});
            jQuery('#wpvivid_restore_' + restore_method + 'part').show();
            jQuery('#wpvivid_clean_' + restore_method + 'part').hide();
            jQuery('#wpvivid_rollback_' + restore_method + 'part').hide();
            jQuery('#wpvivid_download_'+restore_method+'part').hide();
        }

        /**
         * This function will start the process of restoring a backup
         */
        function wpvivid_start_restore(restore_type = 'backup')
        {
            if(!wpvivid_restore_sure){
                var descript = '<?php esc_html_e('Are you sure to continue?', 'wpvivid-backuprestore'); ?>';
                var ret = confirm(descript);
            }
            else{
                ret = true;
            }
            if (ret === true) {
                wpvivid_restore_sure = true;
                var restore_method = '';
                if (restore_type == 'backup') {
                    restore_method = '';
                }
                else if (restore_type == 'transfer') {
                    restore_method = 'transfer_';
                }
                m_restore_type=restore_method;
                jQuery('#wpvivid_restore_' + restore_method + 'log').html("");
                jQuery('#wpvivid_restore_' + restore_method + 'btn').css({'pointer-events': 'none', 'opacity': '0.4'});
                jQuery('#wpvivid_restore_' + restore_method + 'part').show();
                jQuery('#wpvivid_restore_progress').show();
                jQuery('#wpvivid_main_progress').html('<span class="action-progress-bar-percent wpvivid-span-processed-percent-progress" style="width: 0%; display:block; height:1.5em; border-radius:0; padding-left:0.5em;">0%</span>');
                wpvivid_restore_lock();
                wpvivid_restoring = true;
                if (wpvivid_restore_need_download)
                {
                    wpvivid_download_restore_file(restore_type);
                }
                else
                {
                    //wpvivid_monitor_restore_task(restore_type);
                    //if(wpvivid_resotre_is_migrate==0)
                    //{
                    //    jQuery('input:radio[option=restore]').each(function()
                    //    {
                    //       if(jQuery(this).prop('checked'))
                    //        {
                    //            var value = jQuery(this).prop('value');
                    //            if(value == '1')
                    //            {
                    //                wpvivid_resotre_is_migrate = '1';
                    //            }
                    //        }
                    //    });
                    //}
                    //wpvivid_restore(restore_type);

                    wpvivid_init_restore();
                }
            }
        }

        function wpvivid_init_restore()
        {
            var ajax_data = {
                'action':'wpvivid_init_restore_task_2',
                'backup_id': m_restore_backup_id,
            };

            wpvivid_post_request(ajax_data, function(data)
            {
                try
                {
                    var jsonarray = jQuery.parseJSON(data);

                    if (jsonarray.result === 'success')
                    {
                        wpvivid_do_restore();
                    }
                    else
                    {
                        jQuery('#wpvivid_restore_'+m_restore_type+'btn').css({'pointer-events': 'none', 'opacity': '0.4'});
                        jQuery('#wpvivid_download_'+m_restore_type+'btn').css({'pointer-events': 'auto', 'opacity': '1'});
                        jQuery('#wpvivid_restore_'+m_restore_type+'part').hide();
                        jQuery('#wpvivid_download_'+m_restore_type+'part').show();
                        var error_message = jsonarray.error;
                        wpvivid_restore_unlock();
                        wpvivid_restoring = false;
                        alert("<?php esc_html_e('Restore failed.', 'wpvivid-backuprestore'); ?>");
                        wpvivid_display_restore_msg(error_message,m_restore_type,wpvivid_restore_download_array[wpvivid_restore_download_index]['file_name'],false);

                    }
                }
                catch (err)
                {
                    alert(err);
                }
            }, function(XMLHttpRequest, textStatus, errorThrown)
            {
                var error_message = wpvivid_output_ajaxerror('init restore task', textStatus, errorThrown);
                alert(error_message);
            });
        }

        function wpvivid_do_restore()
        {
            var ajax_data = {
                'action':'wpvivid_do_restore_2',
                'wpvivid_restore':'1'
            };
            wpvivid_post_request(ajax_data, function(data)
            {
                setTimeout(function(){
                    wpvivid_get_restore_progress();
                }, 1000);
            }, function(XMLHttpRequest, textStatus, errorThrown)
            {
                wpvivid_get_restore_progress();
            });
        }

        function wpvivid_get_restore_progress()
        {
            var ajax_data = {
                'action':'wpvivid_get_restore_progress_2',
                'wpvivid_restore':'1'
            };
            wpvivid_post_request(ajax_data, function(data)
            {
                try
                {
                    var jsonarray = jQuery.parseJSON(data);

                    if (jsonarray.result === 'success')
                    {
                        wpvivid_output_progress(jsonarray);

                        if(jsonarray.status=='ready')
                        {
                            wpvivid_do_restore();
                        }
                        else if(jsonarray.status=='sub task finished')
                        {
                            wpvivid_do_restore();
                        }
                        else if(jsonarray.status=='task finished')
                        {
                            wpvivid_finish_restore();
                        }
                        else if(jsonarray.status=='doing sub task')
                        {
                            setTimeout(function(){
                                wpvivid_get_restore_progress();
                            }, 2000);
                        }
                        else if(jsonarray.status=='no response')
                        {
                            setTimeout(function(){
                                wpvivid_get_restore_progress();
                            }, 2000);
                        }
                    }
                    else {
                        wpvivid_restore_failed();
                    }
                }
                catch (err)
                {
                    setTimeout(function(){
                        wpvivid_get_restore_progress();
                    }, 2000);
                }
            }, function(XMLHttpRequest, textStatus, errorThrown)
            {
                setTimeout(function(){
                    wpvivid_get_restore_progress();
                }, 2000);
            });
        }

        function wpvivid_finish_restore()
        {
            var ajax_data = {
                'action':'wpvivid_finish_restore_2',
                'wpvivid_restore':'1'
            };
            wpvivid_post_request(ajax_data, function(data)
            {
                wpvivid_restore_unlock();
                alert("<?php esc_html_e('Restore completed successfully.', 'wpvivid-backuprestore'); ?>");
                location.reload();

                //jQuery('#wpvivid_restore_progress').hide();
                //jQuery('#wpvivid_restore_log').hide();
                //jQuery('#wpvivid_restore_box').hide();
                //jQuery('#wpvivid_restore_success').show();
                //jQuery('#wpvivid_restore_failed').hide();
                //jQuery('#wpvivid_restore_finished_msg').html(data);
                //wpvivid_restoring = false;
            }, function(XMLHttpRequest, textStatus, errorThrown)
            {
                wpvivid_restore_unlock();
                alert("<?php esc_html_e('Restore completed successfully.', 'wpvivid-backuprestore'); ?>");
                location.reload();

                //jQuery('#wpvivid_restore_progress').hide();
                //jQuery('#wpvivid_restore_log').hide();
                //jQuery('#wpvivid_restore_success').show();
                //jQuery('#wpvivid_restore_failed').hide();
                //
                //jQuery('#wpvivid_restore_finished_msg').html(XMLHttpRequest.responseText);
                //wpvivid_restoring = false;
                //wpvivid_restore_unlock();
                //alert("<?php esc_html_e('Restore completed successfully.', 'wpvivid-backuprestore'); ?>");
                //location.reload();
            });
        }

        function wpvivid_restore_failed()
        {
            var ajax_data = {
                'action':'wpvivid_restore_failed_2',
                'wpvivid_restore':'1'
            };

            wpvivid_post_request(ajax_data, function(data)
            {
                jQuery('#wpvivid_restore_progress').hide();
                //jQuery('#wpvivid_restore_log').hide();
                //jQuery('#wpvivid_restore_box').hide();
                //jQuery('#wpvivid_restore_success').hide();
                //jQuery('#wpvivid_restore_failed').show();
                //jQuery('#wpvivid_restore_failed_msg').html(data);

                wpvivid_restore_unlock();
                wpvivid_restoring = false;
                jQuery('#wpvivid_restore_' + m_restore_type + 'btn').css({'pointer-events': 'auto', 'opacity': '1'});
                alert(data);

            }, function(XMLHttpRequest, textStatus, errorThrown)
            {
                jQuery('#wpvivid_restore_progress').hide();
                //jQuery('#wpvivid_restore_log').hide();
                //jQuery('#wpvivid_restore_box').hide();
                //jQuery('#wpvivid_restore_success').hide();
                //jQuery('#wpvivid_restore_failed').show();
                //jQuery('#wpvivid_restore_failed_msg').html(XMLHttpRequest.responseText);

                wpvivid_restore_unlock();
                wpvivid_restoring = false;
                jQuery('#wpvivid_restore_' + m_restore_type + 'btn').css({'pointer-events': 'auto', 'opacity': '1'});
                alert("<?php esc_html_e('Restore failed.', 'wpvivid-backuprestore'); ?>");
            });
        }

        function wpvivid_output_progress(jsonarray)
        {
            jQuery('#wpvivid_main_progress').html(jsonarray.main_progress);

            jQuery('#wpvivid_restore_' + m_restore_type + 'log').html("");
            while (jsonarray.log.indexOf('\n') >= 0)
            {
                var iLength = jsonarray.log.indexOf('\n');
                var log = jsonarray.log.substring(0, iLength);
                jsonarray.log = jsonarray.log.substring(iLength + 1);
                var insert_log = "<div style=\"clear:both;\">" + log + "</div>";
                jQuery('#wpvivid_restore_' + m_restore_type + 'log').append(insert_log);
                var div = jQuery('#wpvivid_restore_' + m_restore_type + 'log');
                div[0].scrollTop = div[0].scrollHeight;
            }
        }



        /**
         * Monitor restore task.
         */
        /*
        function wpvivid_monitor_restore_task(restore_type){
            var restore_method = '';
            if(restore_type == 'backup'){
                restore_method = '';
            }
            else if(restore_type == 'transfer'){
                restore_method = 'transfer_';
            }

            var ajax_data={
                'action':'wpvivid_get_restore_progress',
                'wpvivid_restore' : '1',
                'backup_id':m_restore_backup_id,
            };

            if(wpvivid_restore_timeout){
                jQuery('#wpvivid_restore_'+restore_method+'btn').css({'pointer-events': 'auto', 'opacity': '1'});
                jQuery('#wpvivid_clean_'+restore_method+'restore').css({'pointer-events': 'none', 'opacity': '0.4'});
                jQuery('#wpvivid_rollback_'+restore_method+'btn').css({'pointer-events': 'none', 'opacity': '0.4'});
                jQuery('#wpvivid_restore_'+restore_method+'part').show();
                jQuery('#wpvivid_clean_'+restore_method+'part').hide();
                jQuery('#wpvivid_rollback_'+restore_method+'part').hide();
                wpvivid_restore_unlock();
                wpvivid_restoring = false;
                wpvivid_display_restore_msg("Website restore times out.", restore_type);
            }
            else {
                wpvivid_post_request(ajax_data, function (data) {
                    try {
                        var jsonarray = jQuery.parseJSON(data);
                        if (typeof jsonarray === 'object') {
                            if (jsonarray.result === "success") {
                                jQuery('#wpvivid_restore_' + restore_method + 'log').html("");
                                while (jsonarray.log.indexOf('\n') >= 0) {
                                    var iLength = jsonarray.log.indexOf('\n');
                                    var log = jsonarray.log.substring(0, iLength);
                                    jsonarray.log = jsonarray.log.substring(iLength + 1);
                                    var insert_log = "<div style=\"clear:both;\">" + log + "</div>";
                                    jQuery('#wpvivid_restore_' + restore_method + 'log').append(insert_log);
                                    var div = jQuery('#wpvivid_restore_' + restore_method + 'log');
                                    div[0].scrollTop = div[0].scrollHeight;
                                }

                                if (jsonarray.status === 'wait') {
                                    wpvivid_restoring = true;
                                    jQuery('#wpvivid_restore_' + restore_method + 'btn').css({
                                        'pointer-events': 'none',
                                        'opacity': '0.4'
                                    });
                                    jQuery('#wpvivid_clean_' + restore_method + 'restore').css({
                                        'pointer-events': 'none',
                                        'opacity': '0.4'
                                    });
                                    jQuery('#wpvivid_rollback_' + restore_method + 'btn').css({
                                        'pointer-events': 'none',
                                        'opacity': '0.4'
                                    });
                                    jQuery('#wpvivid_restore_' + restore_method + 'part').show();
                                    jQuery('#wpvivid_clean_' + restore_method + 'part').hide();
                                    jQuery('#wpvivid_rollback_' + restore_method + 'part').hide();
                                    wpvivid_restore(restore_type);
                                    setTimeout(function () {
                                        wpvivid_monitor_restore_task(restore_type);
                                    }, 1000);
                                }
                                else if (jsonarray.status === 'completed') {
                                    wpvivid_restoring = false;
                                    wpvivid_restore(restore_type);
                                    wpvivid_restore_unlock();
                                    alert(" //esc_html_e('Restore completed successfully.', 'wpvivid-backuprestore'); ");
                                    location.reload();
                                }
                                else if (jsonarray.status === 'error') {
                                    wpvivid_restore_unlock();
                                    wpvivid_restoring = false;
                                    jQuery('#wpvivid_restore_' + restore_method + 'btn').css({'pointer-events': 'auto', 'opacity': '1'});
                                    alert(" //esc_html_e('Restore failed.', 'wpvivid-backuprestore'); ");
                                }
                                else {
                                    setTimeout(function () {
                                        wpvivid_monitor_restore_task(restore_type);
                                    }, 1000);
                                }
                            }
                            else {
                                setTimeout(function () {
                                    wpvivid_monitor_restore_task(restore_type);
                                }, 1000);
                            }
                        }
                        else {
                            setTimeout(function () {
                                wpvivid_monitor_restore_task(restore_type);
                            }, 1000);
                        }
                    }
                    catch (err) {
                        setTimeout(function () {
                            wpvivid_monitor_restore_task(restore_type);
                        }, 1000);
                    }
                }, function (XMLHttpRequest, textStatus, errorThrown) {
                    setTimeout(function () {
                        wpvivid_monitor_restore_task(restore_type);
                    }, 1000);
                });
            }
        }

        function wpvivid_restore(restore_type){
            var restore_method = '';
            if(restore_type == 'backup'){
                restore_method = '';
            }
            else if(restore_type == 'transfer'){
                restore_method = 'transfer_';
            }

            var skip_old_site = '1';
            var extend_option = {
                'skip_backup_old_site':skip_old_site,
                'skip_backup_old_database':skip_old_site
            };

            var migrate_option = {
                'is_migrate':wpvivid_resotre_is_migrate,
            };
            jQuery.extend(extend_option, migrate_option);

            var restore_options = {
                0:'backup_db',
                1:'backup_themes',
                2:'backup_plugin',
                3:'backup_uploads',
                4:'backup_content',
                5:'backup_core'
            };
            jQuery.extend(restore_options, extend_option);
            var json = JSON.stringify(restore_options);
            var ajax_data={
                'action':'wpvivid_restore',
                'wpvivid_restore':'1',
                'backup_id':m_restore_backup_id,
                'restore_options':json
            };
            setTimeout(function () {
                wpvivid_restore_timeout = true;
            }, restore_max_exection_time);
            wpvivid_post_request(ajax_data, function(data) {
            }, function(XMLHttpRequest, textStatus, errorThrown) {
            });
        }
        */

        function wpvivid_display_restore_msg(msg, restore_type, div_id, append = true){
            var restore_method = '';
            if(restore_type == 'backup'){
                restore_method = '';
            }
            else if(restore_type == 'transfer'){
                restore_method = 'transfer_';
            }

            if(typeof div_id == 'undefined') {
                var restore_msg = "<div style=\"clear:both;\">" + msg + "</div>";
            }
            else{
                var restore_msg = "<div id=\"restore_file_"+div_id+"\"  style=\"clear:both;\">" + msg + "</div>";
            }
            if(append == true) {
                jQuery('#wpvivid_restore_'+restore_method+'log').append(restore_msg);
            }
            else{
                if(jQuery('#restore_file_'+div_id).length )
                {
                    jQuery('#restore_file_'+div_id).html(msg);
                }
                else
                {
                    jQuery('#wpvivid_restore_'+restore_method+'log').append(restore_msg);
                }
            }
            var div = jQuery('#wpvivid_restore_' + restore_method + 'log');
            div[0].scrollTop = div[0].scrollHeight;
        }

        /**
         * Lock certain operations while a restore task is running.
         */
        function wpvivid_restore_lock(){
            jQuery('#wpvivid_postbox_backup_percent').css({'pointer-events': 'none', 'opacity': '1'});
            jQuery('#wpvivid_postbox_backup').css({'pointer-events': 'none', 'opacity': '1'});
            jQuery('#wpvivid_postbox_backup_schedule').css({'pointer-events': 'none', 'opacity': '1'});
            jQuery('#wpvivid_quickbackup_btn').css({'pointer-events': 'none', 'opacity': '1'});
            jQuery('#wpvivid_tab_backup').css({'pointer-events': 'none', 'opacity': '1'});
            jQuery('#wpvivid_tab_upload').css({'pointer-events': 'none', 'opacity': '1'});
            jQuery('#wpvivid_tab_backup_log').css({'pointer-events': 'none', 'opacity': '1'});
            jQuery('#wpvivid_tab_restore').css({'pointer-events': 'none', 'opacity': '1'});
            jQuery('#page-backups').css({'pointer-events': 'none', 'opacity': '1'});
            jQuery('#storage-page').css({'pointer-events': 'none', 'opacity': '1'});
            jQuery('#settings-page').css({'pointer-events': 'none', 'opacity': '1'});
            jQuery('#debug-page').css({'pointer-events': 'none', 'opacity': '1'});
            jQuery('#logs-page').css({'pointer-events': 'none', 'opacity': '1'});
            jQuery('#wpvivid_tab_migrate').css({'pointer-events': 'none', 'opacity': '1'});
            jQuery('#wpvivid_add_tab_migrate').css({'pointer-events': 'none', 'opacity': '1'});
            jQuery('#wpvivid_add_tab_import').css({'pointer-events': 'none', 'opacity': '1'});
            jQuery('#wpvivid_add_tab_key').css({'pointer-events': 'none', 'opacity': '1'});
            jQuery('#wpvivid_add_tab_log').css({'pointer-events': 'none', 'opacity': '1'});
            jQuery('#wpvivid_add_tab_restore').css({'pointer-events': 'none', 'opacity': '1'});
            jQuery('#wpvivid_restore_is_migrate').css({'pointer-events': 'none', 'opacity': '1'});
            jQuery('#wpvivid_replace_domain').css({'pointer-events': 'none', 'opacity': '1'});
            jQuery('#wpvivid_keep_domain').css({'pointer-events': 'none', 'opacity': '1'});
        }

        /**
         * Unlock the operations once restore task completed.
         */
        function wpvivid_restore_unlock(){
            jQuery('#wpvivid_postbox_backup_percent').css({'pointer-events': 'auto', 'opacity': '1'});
            jQuery('#wpvivid_postbox_backup').css({'pointer-events': 'auto', 'opacity': '1'});
            jQuery('#wpvivid_postbox_backup_schedule').css({'pointer-events': 'auto', 'opacity': '1'});
            jQuery('#wpvivid_quickbackup_btn').css({'pointer-events': 'auto', 'opacity': '1'});
            jQuery('#wpvivid_tab_backup').css({'pointer-events': 'auto', 'opacity': '1'});
            jQuery('#wpvivid_tab_upload').css({'pointer-events': 'auto', 'opacity': '1'});
            jQuery('#wpvivid_tab_backup_log').css({'pointer-events': 'auto', 'opacity': '1'});
            jQuery('#wpvivid_tab_restore').css({'pointer-events': 'auto', 'opacity': '1'});
            jQuery('#page-backups').css({'pointer-events': 'auto', 'opacity': '1'});
            jQuery('#storage-page').css({'pointer-events': 'auto', 'opacity': '1'});
            jQuery('#settings-page').css({'pointer-events': 'auto', 'opacity': '1'});
            jQuery('#debug-page').css({'pointer-events': 'auto', 'opacity': '1'});
            jQuery('#logs-page').css({'pointer-events': 'auto', 'opacity': '1'});
            jQuery('#wpvivid_tab_migrate').css({'pointer-events': 'auto', 'opacity': '1'});
            jQuery('#wpvivid_add_tab_migrate').css({'pointer-events': 'auto', 'opacity': '1'});
            jQuery('#wpvivid_add_tab_import').css({'pointer-events': 'auto', 'opacity': '1'});
            jQuery('#wpvivid_add_tab_key').css({'pointer-events': 'auto', 'opacity': '1'});
            jQuery('#wpvivid_add_tab_log').css({'pointer-events': 'auto', 'opacity': '1'});
            jQuery('#wpvivid_add_tab_restore').css({'pointer-events': 'auto', 'opacity': '1'});
            jQuery('#wpvivid_restore_is_migrate').css({'pointer-events': 'auto', 'opacity': '1'});
            jQuery('#wpvivid_replace_domain').css({'pointer-events': 'auto', 'opacity': '1'});
            jQuery('#wpvivid_keep_domain').css({'pointer-events': 'auto', 'opacity': '1'});
        }
    </script>
    <?php
}

function wpvivid_backuppage_add_page_downlaod(){
    ?>
    <div class="backup-tab-content wpvivid_tab_download" id="page-download" style="padding-top: 1em; display:none;">
        <div id="wpvivid_init_download_info">
            <div style="float: left; height: 20px; line-height: 20px; margin-top: 4px;">Initializing the download info</div>
            <div class="spinner" style="float: left;"></div>
            <div style="clear: both;"></div>
        </div>
        <div class="wpvivid-element-space-bottom" id="wpvivid_files_list">
        </div>
    </div>

    <script>
        var wpvivid_download_files_list = wpvivid_download_files_list || {};
        wpvivid_download_files_list.backup_id='';
        wpvivid_download_files_list.wpvivid_download_file_array = Array();
        wpvivid_download_files_list.wpvivid_download_lock_array = Array();
        wpvivid_download_files_list.init=function(backup_id) {
            wpvivid_download_files_list.backup_id=backup_id;
            wpvivid_download_files_list.wpvivid_download_file_array.splice(0, wpvivid_download_files_list.wpvivid_download_file_array.length);
        };

        wpvivid_download_files_list.add_download_queue=function(filename) {
            var download_file_size = jQuery("[slug='"+filename+"']").find('.wpvivid-download-status').find('.wpvivid-download-file-size').html();
            var tmp_html = '<div class="wpvivid-element-space-bottom">' +
                '<span class="wpvivid-element-space-right">Retriving (remote storage to web server)</span><span class="wpvivid-element-space-right">|</span><span>File Size: </span><span class="wpvivid-element-space-right">'+download_file_size+'</span><span class="wpvivid-element-space-right">|</span><span>Downloaded Size: </span><span>0</span>' +
                '</div>' +
                '<div style="width:100%;height:10px; background-color:#dcdcdc;">' +
                '<div style="background-color:#0085ba; float:left;width:0%;height:10px;"></div>' +
                '</div>';
            jQuery("[slug='"+filename+"']").find('.wpvivid-download-status').html(tmp_html);
            if(jQuery.inArray(filename, wpvivid_download_files_list.wpvivid_download_file_array) === -1) {
                wpvivid_download_files_list.wpvivid_download_file_array.push(filename);
            }
            var ajax_data = {
                'action': 'wpvivid_prepare_download_backup',
                'backup_id':wpvivid_download_files_list.backup_id,
                'file_name':filename
            };
            wpvivid_post_request(ajax_data, function(data)
            {
            }, function(XMLHttpRequest, textStatus, errorThrown)
            {
            }, 0);

            wpvivid_download_files_list.check_queue();
        };

        wpvivid_download_files_list.check_queue=function() {
            if(jQuery.inArray(wpvivid_download_files_list.backup_id, wpvivid_download_files_list.wpvivid_download_lock_array) !== -1){
                return;
            }
            var ajax_data = {
                'action': 'wpvivid_get_download_progress',
                'backup_id':wpvivid_download_files_list.backup_id,
            };
            wpvivid_download_files_list.wpvivid_download_lock_array.push(wpvivid_download_files_list.backup_id);
            wpvivid_post_request(ajax_data, function(data)
            {
                wpvivid_download_files_list.wpvivid_download_lock_array.splice(jQuery.inArray(wpvivid_download_files_list.backup_id, wpvivid_download_files_list.wpvivid_download_file_array),1);
                var jsonarray = jQuery.parseJSON(data);
                if (jsonarray.result === 'success')
                {
                    jQuery.each(jsonarray.files,function (index, value)
                    {
                        if(jQuery.inArray(index, wpvivid_download_files_list.wpvivid_download_file_array) !== -1) {
                            if(value.status === 'timeout' || value.status === 'completed' || value.status === 'error'){
                                wpvivid_download_files_list.wpvivid_download_file_array.splice(jQuery.inArray(index, wpvivid_download_files_list.wpvivid_download_file_array),1);
                            }
                            wpvivid_download_files_list.update_item(index, value);
                        }
                    });

                    //if(jsonarray.need_update)
                    if(wpvivid_download_files_list.wpvivid_download_file_array.length > 0)
                    {
                        setTimeout(function()
                        {
                            wpvivid_download_files_list.check_queue();
                        }, 3000);
                    }
                }
            }, function(XMLHttpRequest, textStatus, errorThrown)
            {
                wpvivid_download_files_list.wpvivid_download_lock_array.splice(jQuery.inArray(wpvivid_download_files_list.backup_id, wpvivid_download_files_list.wpvivid_download_file_array),1);
                setTimeout(function()
                {
                    wpvivid_download_files_list.check_queue();
                }, 3000);
            }, 0);
        };

        wpvivid_download_files_list.update_item=function(index,file) {
            jQuery("[slug='"+index+"']").find('.wpvivid-download-status').html(file.html);
        };

        wpvivid_download_files_list.download_now=function(filename) {
            wpvivid_location_href=true;
            location.href =ajaxurl+'?_wpnonce='+wpvivid_ajax_object.ajax_nonce+'&action=wpvivid_download_backup&backup_id='+wpvivid_download_files_list.backup_id+'&file_name='+filename;
        };

        function wpvivid_initialize_download(backup_id, list_name){
            jQuery('#wpvivid_tab_download').show();
            wpvivid_click_switch_page('backup', 'wpvivid_tab_download', true);
            wpvivid_init_download_page(backup_id);


            /*wpvivid_reset_backup_list(list_name);
            jQuery('#wpvivid_download_loading_'+backup_id).addClass('is-active');
            tmp_current_click_backupid = backup_id;
            var ajax_data = {
                'action':'wpvivid_init_download_page',
                'backup_id':backup_id
            };
            wpvivid_post_request(ajax_data, function(data){
                try {
                    var jsonarray = jQuery.parseJSON(data);
                    jQuery('#wpvivid_download_loading_'+backup_id).removeClass('is-active');
                    if (jsonarray.result === 'success') {
                        jQuery('#wpvivid_file_part_' + backup_id).html("");
                        var i = 0;
                        var file_not_found = false;
                        var file_name = '';
                        jQuery.each(jsonarray.files, function (index, value) {
                            i++;
                            file_name = index;
                            if (value.status === 'need_download') {
                                jQuery('#wpvivid_file_part_' + backup_id).append(value.html);
                                //tmp_current_click_backupid = '';
                            }
                            else if (value.status === 'running') {
                                if (m_downloading_file_name === file_name) {
                                    wpvivid_lock_download(tmp_current_click_backupid);
                                }
                                jQuery('#wpvivid_file_part_' + backup_id).append(value.html);
                            }
                            else if (value.status === 'completed') {
                                if (m_downloading_file_name === file_name) {
                                    wpvivid_unlock_download(tmp_current_click_backupid);
                                    m_downloading_id = '';
                                    m_downloading_file_name = '';
                                }
                                jQuery('#wpvivid_file_part_' + backup_id).append(value.html);
                                //tmp_current_click_backupid = '';
                            }
                            else if (value.status === 'timeout') {
                                if (m_downloading_file_name === file_name) {
                                    wpvivid_unlock_download(tmp_current_click_backupid);
                                    m_downloading_id = '';
                                    m_downloading_file_name = '';
                                }
                                jQuery('#wpvivid_file_part_' + backup_id).append(value.html);
                                //tmp_current_click_backupid = '';
                            }
                            else if (value.status === 'file_not_found') {
                                wpvivid_unlock_download(tmp_current_click_backupid);
                                wpvivid_reset_backup_list(list_name);
                                file_not_found = true;
                                alert("Download failed, file not found. The file might has been moved, renamed or deleted. Please verify the file exists and try again.");
                                //tmp_current_click_backupid = '';
                                return false;
                            }
                        });
                        if (file_not_found === false) {
                            jQuery('#wpvivid_file_part_' + backup_id).append(jsonarray.place_html);
                        }
                    }
                }
                catch(err){
                    alert(err);
                    jQuery('#wpvivid_download_loading_'+backup_id).removeClass('is-active');
                }
            },function(XMLHttpRequest, textStatus, errorThrown){
                jQuery('#wpvivid_download_loading_'+backup_id).removeClass('is-active');
                var error_message = wpvivid_output_ajaxerror('initializing download information', textStatus, errorThrown);
                alert(error_message);
            });*/
        }

        function wpvivid_init_download_page(backup_id){
            jQuery('#wpvivid_files_list').html('');
            jQuery('#wpvivid_init_download_info').show();
            jQuery('#wpvivid_init_download_info').find('.spinner').addClass('is-active');
            var ajax_data = {
                'action':'wpvivid_init_download_page',
                'backup_id':backup_id
            };
            var retry = '<input type="button" class="button button-primary" value="Retry the initialization" onclick="wpvivid_init_download_page(\''+backup_id+'\');" />';

            wpvivid_post_request(ajax_data, function(data)
            {
                jQuery('#wpvivid_init_download_info').hide();
                jQuery('#wpvivid_init_download_info').find('.spinner').removeClass('is-active');
                try
                {
                    var jsonarray = jQuery.parseJSON(data);
                    if (jsonarray.result === 'success')
                    {
                        wpvivid_download_files_list.init(backup_id);
                        var need_check_queue = false;
                        jQuery.each(jsonarray.files,function (index, value)
                        {
                            if(value.status === 'running'){
                                if(jQuery.inArray(index, wpvivid_download_files_list.wpvivid_download_file_array) === -1) {
                                    wpvivid_download_files_list.wpvivid_download_file_array.push(index);
                                    need_check_queue = true;
                                }
                            }
                        });
                        if(need_check_queue) {
                            wpvivid_download_files_list.check_queue();
                        }
                        jQuery('#wpvivid_files_list').html(jsonarray.html);
                    }
                    else{
                        alert(jsonarray.error);
                        jQuery('#wpvivid_files_list').html(retry);
                    }
                }
                catch(err)
                {
                    alert(err);
                    jQuery('#wpvivid_files_list').html(retry);
                }
            },function(XMLHttpRequest, textStatus, errorThrown)
            {
                jQuery('#wpvivid_init_download_info').hide();
                jQuery('#wpvivid_init_download_info').find('.spinner').removeClass('is-active');
                var error_message = wpvivid_output_ajaxerror('initializing download information', textStatus, errorThrown);
                alert(error_message);
                jQuery('#wpvivid_files_list').html(retry);
            });
        }

        function wpvivid_download_change_page(page)
        {
            var backup_id=wpvivid_download_files_list.backup_id;

            var ajax_data = {
                'action':'wpvivid_get_download_page_ex',
                'backup_id':backup_id,
                'page':page
            };

            jQuery('#wpvivid_files_list').html('');
            jQuery('#wpvivid_init_download_info').show();
            jQuery('#wpvivid_init_download_info').find('.spinner').addClass('is-active');

            wpvivid_post_request(ajax_data, function(data)
            {
                jQuery('#wpvivid_init_download_info').hide();
                jQuery('#wpvivid_init_download_info').find('.spinner').removeClass('is-active');
                try
                {
                    var jsonarray = jQuery.parseJSON(data);
                    if (jsonarray.result === 'success')
                    {
                        jQuery('#wpvivid_files_list').html(jsonarray.html);
                    }
                    else{
                        alert(jsonarray.error);
                    }
                }
                catch(err)
                {
                    alert(err);
                }
            },function(XMLHttpRequest, textStatus, errorThrown)
            {
                jQuery('#wpvivid_init_download_info').hide();
                jQuery('#wpvivid_init_download_info').find('.spinner').removeClass('is-active');
                var error_message = wpvivid_output_ajaxerror('initializing download information', textStatus, errorThrown);
                alert(error_message);
            });
        }

        jQuery('#wpvivid_files_list').on("click",'.wpvivid-download',function()
        {
            var Obj=jQuery(this);
            var file_name=Obj.closest('tr').attr('slug');
            wpvivid_download_files_list.add_download_queue(file_name);
        });
        jQuery('#wpvivid_files_list').on("click",'.wpvivid-ready-download',function()
        {
            var Obj=jQuery(this);
            var file_name=Obj.closest('tr').attr('slug');
            wpvivid_download_files_list.download_now(file_name);
        });

        jQuery('#wpvivid_files_list').on("click",'.first-page',function() {
            wpvivid_download_change_page('first');
        });

        jQuery('#wpvivid_files_list').on("click",'.prev-page',function() {
            var page=parseInt(jQuery(this).attr('value'));
            wpvivid_download_change_page(page-1);
        });

        jQuery('#wpvivid_files_list').on("click",'.next-page',function() {
            var page=parseInt(jQuery(this).attr('value'));
            wpvivid_download_change_page(page+1);
        });

        jQuery('#wpvivid_files_list').on("click",'.last-page',function() {
            wpvivid_download_change_page('last');
        });

        jQuery('#wpvivid_files_list').on("keypress", '.current-page', function(){
            if(event.keyCode === 13){
                var page = jQuery(this).val();
                wpvivid_download_change_page(page);
            }
        });
    </script>
    <?php
}

function wpvivid_backuppage_add_progress_module(){
    ?>
    <div class="postbox" id="wpvivid_postbox_backup_percent" style="display: none;">
        <div class="action-progress-bar" id="wpvivid_action_progress_bar">
            <div class="action-progress-bar-percent" id="wpvivid_action_progress_bar_percent" style="height:24px;width:0;"></div>
        </div>
        <div id="wpvivid_estimate_upload_info" style="float: left;">
            <div class="backup-basic-info"><span class="wpvivid-element-space-right"><?php esc_html_e('Total Size:', 'wpvivid-backuprestore'); ?></span><span>N/A</span></div>
            <div class="backup-basic-info"><span class="wpvivid-element-space-right"><?php esc_html_e('Uploaded:', 'wpvivid-backuprestore'); ?></span><span>N/A</span></div>
            <div class="backup-basic-info"><span class="wpvivid-element-space-right"><?php esc_html_e('Speed:', 'wpvivid-backuprestore'); ?></span><span>N/A</span></div>
        </div>
        <div style="float: left;">
            <div class="backup-basic-info"><span class="wpvivid-element-space-right"><?php esc_html_e('Network Connection:', 'wpvivid-backuprestore'); ?></span><span>N/A</span></div>
        </div>
        <div style="clear:both;"></div>
        <div style="margin-left:10px; float: left; width:100%;"><p id="wpvivid_current_doing"></p></div>
        <div style="clear: both;"></div>
        <div>
            <div id="wpvivid_backup_cancel" class="backup-log-btn"><input class="button-primary" id="wpvivid_backup_cancel_btn" type="submit" value="<?php esc_attr_e( 'Cancel', 'wpvivid-backuprestore' ); ?>"  /></div>
            <div id="wpvivid_backup_log" class="backup-log-btn"><input class="button-primary" id="wpvivid_backup_log_btn" type="submit" value="<?php esc_attr_e( 'Log', 'wpvivid-backuprestore' ); ?>" /></div>
        </div>
        <div style="clear: both;"></div>
    </div>
    <script>
        jQuery('#wpvivid_postbox_backup_percent').on("click", "input", function(){
            if(jQuery(this).attr('id') === 'wpvivid_backup_cancel_btn'){
                wpvivid_cancel_backup();
            }
            if(jQuery(this).attr('id') === 'wpvivid_backup_log_btn'){
                wpvivid_read_log('wpvivid_view_backup_task_log');
            }
        });
            
        function wpvivid_cancel_backup()
        {
            var ajax_data= {
                'action': 'wpvivid_backup_cancel'
                //'task_id': running_backup_taskid
            };
            jQuery('#wpvivid_backup_cancel_btn').css({'pointer-events': 'none', 'opacity': '0.4'});
            wpvivid_post_request(ajax_data, function(data)
            {
                try
                {
                    var jsonarray = jQuery.parseJSON(data);
                    if(jsonarray.no_response)
                    {
                        var ret = confirm(jsonarray.msg);
                        if(ret === true)
                        {
                            wpvivid_termination_backup_task(jsonarray.task_id);
                        }
                    }
                    else
                    {
                        jQuery('#wpvivid_current_doing').html(jsonarray.msg);
                    }
                }
                catch(err)
                {
                    alert(err);
                }
            }, function(XMLHttpRequest, textStatus, errorThrown)
            {
                jQuery('#wpvivid_backup_cancel_btn').css({'pointer-events': 'auto', 'opacity': '1'});
                var error_message = wpvivid_output_ajaxerror('cancelling the backup', textStatus, errorThrown);
                wpvivid_add_notice('Backup', 'Error', error_message);
            });
        }

        function wpvivid_termination_backup_task(task_id)
        {
            var ajax_data= {
                'action': 'wpvivid_shutdown_backup',
                'task_id': task_id
            };
            wpvivid_post_request(ajax_data, function(data)
            {
                try
                {
                }
                catch(err)
                {
                    alert(err);
                }
            }, function(XMLHttpRequest, textStatus, errorThrown)
            {
                var error_message = wpvivid_output_ajaxerror('terminationing the backup', textStatus, errorThrown);
                wpvivid_add_notice('Backup', 'Error', error_message);
            });
        }
    </script>
    <?php
}

function wpvivid_backuppage_add_backup_module(){
    ?>
    <div class="postbox quickbackup" id="wpvivid_postbox_backup">
        <?php
        do_action('wpvivid_backup_module_add_sub');
        ?>
    </div>
   <?php
}

function wpvivid_backup_module_add_descript(){
    $backupdir=WPvivid_Setting::get_backupdir();
    ?>
    <div style="font-size: 14px; padding: 8px 12px; margin: 0; line-height: 1.4; font-weight: 600;">
        <span style="margin-right: 5px;"><?php esc_html_e( 'Back Up Manually','wpvivid-backuprestore'); ?></span>
        <span style="margin-right: 5px;">|</span>
        <span style="margin-right: 0;"><a href="<?php echo esc_url('https://wordpress.org/plugins/compressx/'); ?>" style="text-decoration: none;"><?php esc_html_e('Try our AVIF and WebP conversion tool, it\'s free', 'wpvivid-backuprestore'); ?></a></span>
    </div>
    <div class="quickstart-storage-setting">
        <span class="list-top-chip backup" name="ismerge" value="1" style="margin: 10px 10px 10px 0;"><?php esc_html_e('Local Storage Directory:', 'wpvivid-backuprestore'); ?></span>
        <span class="list-top-chip" id="wpvivid_local_storage_path" style="margin: 10px 10px 10px 0;"><?php echo esc_html(WP_CONTENT_DIR.DIRECTORY_SEPARATOR.$backupdir); ?></span>
        <span class="list-top-chip" style="margin: 10px 10px 10px 0;"><a href="#" onclick="wpvivid_click_switch_page('wrap', 'wpvivid_tab_setting', true);" style="text-decoration: none;"><?php esc_html_e('rename directory', 'wpvivid-backuprestore'); ?></a></span>
    </div>
    <?php
}

function wpvivid_backup_module_add_backup_type(){
    $type_name = 'backup_files';
    ?>
    <div class="quickstart-archive-block">
        <fieldset>
            <legend class="screen-reader-text"><span>input type="radio"</span></legend>
            <?php do_action('wpvivid_add_backup_type', $type_name); ?>
            <label style="display: none;">
                <input type="checkbox" option="backup" name="ismerge" value="1" checked />
            </label><br>
        </fieldset>
    </div>
    <?php
}

function wpvivid_backup_module_add_send_remote(){
    $pic='';
    ?>
    <div class="quickstart-storage-block">
        <fieldset>
            <legend class="screen-reader-text"><span>input type="checkbox"</span></legend>
            <label>
                <input type="radio" id="wpvivid_backup_local" option="backup_ex" name="local_remote" value="local" checked />
                <span><?php esc_html_e( 'Save Backups to Local', 'wpvivid-backuprestore' ); ?></span>
            </label><br>
            <label>
                <input type="radio" id="wpvivid_backup_remote" option="backup_ex" name="local_remote" value="remote" />
                <span><?php esc_html_e( 'Send Backup to Remote Storage:', 'wpvivid-backuprestore' ); ?></span>
            </label><br>
            <div id="upload_storage" style="cursor:pointer;" title="Highlighted icon illuminates that you have choosed a remote storage to store backups">
                <?php
                $remoteslist=WPvivid_Setting::get_all_remote_options();
                $default_remote_storage=array();
                foreach ($remoteslist['remote_selected'] as $value) {
                    $default_remote_storage[]=$value;
                }
                $remote_storage_type=array();
                foreach ($remoteslist as $key=>$value)
                {
                    if(in_array($key, $default_remote_storage))
                    {
                        $remote_storage_type[]=$value['type'];
                    }
                }

                $remote=array();
                $remote=apply_filters('wpvivid_remote_pic', $remote);
                if(is_array($remote))
                {
                    foreach ($remote as $key => $value) {
                        $title = $value['title'];
                        if (in_array($key, $remote_storage_type)) {
                            $pic = $value['selected_pic'];
                        } else {
                            $pic = $value['default_pic'];
                        }
                        $url = apply_filters('wpvivid_get_wpvivid_pro_url', WPVIVID_PLUGIN_URL, $key);
                        echo '<img  src="' . esc_url($url . $pic) . '" style="vertical-align:middle; " title="' . esc_attr($title) . '"/>';
                    }
                    echo '<img onclick="wpvivid_click_switch_page(\'wrap\', \'wpvivid_tab_remote_storage\', true);" src="'.esc_url(WPVIVID_PLUGIN_URL.'/admin/partials/images/add-storages.png').'" style="vertical-align:middle;" title="'.esc_attr__('Add a storage', 'wpvivid-backuprestore').'"/>';
                }
                ?>
            </div>
        </fieldset>
    </div>
    <?php
}

function wpvivid_backup_module_add_exec(){
    ?>
    <div class="quickstart-btn" style="padding-top:20px;">
        <input class="button-primary quickbackup-btn" id="wpvivid_quickbackup_btn" type="submit" value="<?php esc_attr_e( 'Backup Now', 'wpvivid-backuprestore'); ?>" />
        <div class="schedule-tab-block" style="text-align:center;">
            <fieldset>
                <label>
                    <input type="checkbox" id="wpvivid_backup_lock" option="backup" name="lock" />
                    <span><?php esc_html_e( 'This backup can only be deleted manually', 'wpvivid-backuprestore' ); ?></span>
                </label>
            </fieldset>
        </div>
    </div>
    <script>

    var m_need_update_2=false;
    jQuery(document).ready(function ()
    {
        wpvivid_activate_cron_2();
        m_need_update_2=true;
        wpvivid_manage_task_2();
    });

    jQuery(document).ready(function($)
    {
        jQuery(document).on('wpvivid_update_local_backup', function(event)
        {
            wpvivid_retrieve_backup_list();
        });

        jQuery(document).on('wpvivid_update_log_list', function(event)
        {
            wpvivid_retrieve_log_list();
        });
    });

    function wpvivid_activate_cron_2()
    {
        var next_get_time = 3 * 60 * 1000;
        wpvivid_cron_task();
        setTimeout("wpvivid_activate_cron_2()", next_get_time);
        setTimeout(function(){
            m_need_update_2=true;
        }, 10000);
    }

    function wpvivid_manage_task_2()
    {
        if(m_need_update_2 === true)
        {
            m_need_update_2 = false;
            wpvivid_check_runningtask_2();
        }
        else{
            setTimeout(function()
            {
                wpvivid_manage_task_2();
            }, 3000);
        }
    }

    function wpvivid_check_runningtask_2()
    {
        var ajax_data = {
            'action': 'wpvivid_list_tasks_2'
        };

        wpvivid_post_request(ajax_data, function (data)
        {
            setTimeout(function ()
            {
                wpvivid_manage_task_2();
            }, 3000);
            try
            {
                var jsonarray = jQuery.parseJSON(data);
                wpvivid_list_task_2_data(jsonarray);
            }
            catch(err)
            {
                alert(err);
            }
        }, function (XMLHttpRequest, textStatus, errorThrown)
        {
            setTimeout(function ()
            {
                m_need_update_2 = true;
                wpvivid_manage_task_2();
            }, 3000);
        }, 0);
    }

    function wpvivid_list_task_2_data(data)
    {
        var b_has_data = false;

        if(data.progress_html!==false)
        {
            jQuery('#wpvivid_postbox_backup_percent').show();
            jQuery('#wpvivid_postbox_backup_percent').html(data.progress_html);
        }
        else
        {
            if(!wpvivid_prepare_backup)
                jQuery('#wpvivid_postbox_backup_percent').hide();
        }

        if(data.upload_progress_html!==false)
        {
            jQuery('#wpvivid_upload_backup_percent').show();
            jQuery('#wpvivid_upload_backup_percent').html(data.upload_progress_html);
        }
        else
        {
            jQuery('#wpvivid_upload_backup_percent').hide();
        }


        var update_backup=false;
        if (data.success_notice_html !== false)
        {
            jQuery('#wpvivid_backup_notice').show();
            jQuery('#wpvivid_backup_notice').html(data.success_notice_html);
            update_backup=true;
        }
        if(data.error_notice_html !== false)
        {
            jQuery('#wpvivid_backup_notice').show();
            jQuery('#wpvivid_backup_notice').html(data.error_notice_html);
            update_backup=true;
        }

        if(update_backup)
        {
            jQuery( document ).trigger( 'wpvivid_update_local_backup');
            jQuery( document ).trigger( 'wpvivid_update_log_list');
        }

        if(data.last_msg_html !== false)
        {
            jQuery('#wpvivid_last_backup_msg').html(data.last_msg_html);
        }

        if(data.need_update)
        {
            m_need_update_2 = true;
        }

        if(data.task_no_response)
        {
            //jQuery('#wpvivid_current_doing').html('Task no response');
            jQuery('#wpvivid_backup_cancel_btn').css({'pointer-events': 'auto', 'opacity': '1'});
        }

        if(data.running_backup_taskid!== '')
        {
            b_has_data = true;
            task_retry_times = 0;
            running_backup_taskid = data.running_backup_taskid;
            wpvivid_control_backup_lock();
            if(data.wait_resume)
            {
                if (data.next_resume_time !== 'get next resume time failed.')
                {
                    wpvivid_resume_backup2(running_backup_taskid, data.next_resume_time);
                }
                else {
                    wpvivid_delete_backup_task(running_backup_taskid);
                }
            }
        }
        else
        {
            if(!wpvivid_prepare_backup)
            {
                jQuery('#wpvivid_backup_cancel_btn').css({'pointer-events': 'auto', 'opacity': '1'});
                wpvivid_control_backup_unlock();
            }
            running_backup_taskid='';
        }
        if (!b_has_data)
        {
            task_retry_times++;
            if (task_retry_times < 5)
            {
                m_need_update_2 = true;
            }
        }
    }

    function wpvivid_resume_backup2(backup_id, next_resume_time)
    {
        if(next_resume_time < 0){
            next_resume_time = 0;
        }
        next_resume_time = next_resume_time * 1000;
        setTimeout("wpvivid_cron_task()", next_resume_time);
        setTimeout(function(){
            task_retry_times = 0;
            m_need_update_2=true;
        }, next_resume_time);
    }

    jQuery('#wpvivid_quickbackup_btn').click(function(){
        wpvivid_clear_notice('wpvivid_backup_notice');
        wpvivid_start_backup();
    });
    
    function wpvivid_start_backup(){
        var bcheck=true;
        var bdownloading=false;
        if(m_downloading_id !== '') {
            var descript = '<?php esc_html_e('This request might delete the backup being downloaded, are you sure you want to continue?', 'wpvivid-backuprestore'); ?>';
            var ret = confirm(descript);
            if (ret === true) {
                bcheck=true;
                bdownloading=true;
            }
            else{
                bcheck=false;
            }
        }
        if(bcheck) {
            var backup_data = wpvivid_ajax_data_transfer('backup');
            backup_data = JSON.parse(backup_data);
            jQuery('input:radio[option=backup_ex]').each(function() {
                if(jQuery(this).prop('checked'))
                {
                    var key = jQuery(this).prop('name');
                    var value = jQuery(this).prop('value');
                    var json = new Array();
                    if(value == 'local'){
                        json['local']='1';
                        json['remote']='0';
                    }
                    else if(value == 'remote'){
                        json['local']='0';
                        json['remote']='1';
                    }
                }
                jQuery.extend(backup_data, json);
            });
            backup_data = JSON.stringify(backup_data);
            var ajax_data = {
                'action': 'wpvivid_prepare_backup_2',
                'backup': backup_data
            };
            wpvivid_control_backup_lock();
            jQuery('#wpvivid_backup_cancel_btn').css({'pointer-events': 'none', 'opacity': '0.4'});
            jQuery('#wpvivid_backup_log_btn').css({'pointer-events': 'none', 'opacity': '0.4'});

            jQuery('#wpvivid_postbox_backup_percent').html('<div class="action-progress-bar" id="wpvivid_action_progress_bar">\n' +
                '                                                <div class="action-progress-bar-percent" id="wpvivid_action_progress_bar_percent" style="height:24px;width:0%"></div>\n' +
                '                                             </div>\n' +
                '                                             <div id="wpvivid_estimate_upload_info" style="float: left;"> \n' +
                '                                                <div class="backup-basic-info"><span class="wpvivid-element-space-right">Total Size:</span><span>N/A</span></div>\n' +
                '                                                <div class="backup-basic-info"><span class="wpvivid-element-space-right">Uploaded:</span><span>N/A</span></div>\n' +
                '                                                <div class="backup-basic-info"><span class="wpvivid-element-space-right">Speed:</span><span>N/A</span></div>\n' +
                '                                             </div>\n' +
                '                                             <div style="float: left;">\n' +
                '                                                <div class="backup-basic-info"><span class="wpvivid-element-space-right">Network Connection:</span><span>N/A</span></div>\n' +
                '                                             </div>\n' +
                '                                             <div style="clear:both;"></div>\n' +
                '                                             <div style="margin-left:10px; float: left; width:100%;"><p id="wpvivid_current_doing">Ready to backup. Progress: 0%, running time: 0second.</p></div>\n' +
                '                                             <div style="clear: both;"></div>\n' +
                '                                             <div>\n' +
                '                                                <div id="wpvivid_backup_cancel" class="backup-log-btn"><input class="button-primary" id="wpvivid_backup_cancel_btn" type="submit" value="Cancel" style="pointer-events: none; opacity: 0.4;" /></div>\n' +
                '                                             </div>\n' +
                '                                             <div style="clear: both;"></div>');
            jQuery('#wpvivid_postbox_backup_percent').show();

            wpvivid_completed_backup = 1;
            wpvivid_prepare_backup = true;
            wpvivid_post_request(ajax_data, function (data)
            {
                wpvivid_prepare_backup = false;
                try {
                    var jsonarray = jQuery.parseJSON(data);
                    if (jsonarray.result === 'failed') {
                        wpvivid_delete_ready_task(jsonarray.error);
                    }
                    else if (jsonarray.result === 'success') {
                        if(bdownloading) {
                            m_downloading_id = '';
                        }
                        m_backup_task_id = jsonarray.task_id;

                        jQuery('#wpvivid_backup_list').html('');
                        jQuery('#wpvivid_backup_list').append(jsonarray.html);
                        wpvivid_backup_now(m_backup_task_id);
                        /*
                         var descript = '';
                        if (jsonarray.check.alert_db === true || jsonarray.check.alter_files === true) {
                            descript = 'The database (the dumping SQL file) might be too large, backing up the database may run out of server memory and result in a backup failure.\n' +
                                'One or more files might be too large, backing up the file(s) may run out of server memory and result in a backup failure.\n' +
                                'Click OK button and continue to back up.';
                            var ret = confirm(descript);
                            if (ret === true) {
                                jQuery('#wpvivid_backup_list').html('');
                                jQuery('#wpvivid_backup_list').append(jsonarray.html);
                                wpvivid_backup_now(m_backup_task_id);
                            }
                            else {
                                jQuery('#wpvivid_backup_cancel_btn').css({'pointer-events': 'auto', 'opacity': '1'});
                                jQuery('#wpvivid_backup_log_btn').css({'pointer-events': 'auto', 'opacity': '1'});
                                wpvivid_control_backup_unlock();
                                jQuery('#wpvivid_postbox_backup_percent').hide();
                            }
                        }
                        else{
                            jQuery('#wpvivid_backup_list').html('');
                            jQuery('#wpvivid_backup_list').append(jsonarray.html);
                            wpvivid_backup_now(jsonarray.task_id);
                        } */
                    }
                }
                catch (err) {
                    wpvivid_delete_ready_task(err);
                }
            }, function (XMLHttpRequest, textStatus, errorThrown) {
                //var error_message = wpvivid_output_ajaxerror('preparing the backup', textStatus, errorThrown);
                var error_message=wpvividlion.backup_calc_timeout;//'Calculating the size of files, folder and database timed out. If you continue to receive this error, please go to the plugin settings, uncheck \'Calculate the size of files, folder and database before backing up\', save changes, then try again.';
                wpvivid_delete_ready_task(error_message);
            });
        }
    }
    
    function wpvivid_backup_now(task_id){
        var ajax_data = {
            'action': 'wpvivid_backup_now_2',
            'task_id': task_id
        };
        task_retry_times = 0;
        m_need_update_2=true;
        wpvivid_post_request(ajax_data, function(data){
        }, function(XMLHttpRequest, textStatus, errorThrown) {
        });
    }
    
    function wpvivid_delete_backup_task(task_id){
        var ajax_data = {
            'action': 'wpvivid_delete_task_2',
            'task_id': task_id
        };
        wpvivid_post_request(ajax_data, function(data){}, function(XMLHttpRequest, textStatus, errorThrown) {
        });
    }
    
    function wpvivid_control_backup_lock(){
        jQuery('#wpvivid_quickbackup_btn').css({'pointer-events': 'none', 'opacity': '0.4'});
        jQuery('#wpvivid_transfer_btn').css({'pointer-events': 'none', 'opacity': '0.4'});
    }
    
    function wpvivid_control_backup_unlock(){
        jQuery('#wpvivid_quickbackup_btn').css({'pointer-events': 'auto', 'opacity': '1'});
        jQuery('#wpvivid_transfer_btn').css({'pointer-events': 'auto', 'opacity': '1'});
    }
    
    function wpvivid_delete_ready_task(error){
        var ajax_data={
            'action': 'wpvivid_delete_ready_task_2'
        };
        wpvivid_post_request(ajax_data, function (data) {
            try {
                var jsonarray = jQuery.parseJSON(data);
                if (jsonarray.result === 'success') {
                    wpvivid_add_notice('Backup', 'Error', error);
                    wpvivid_control_backup_unlock();
                    jQuery('#wpvivid_postbox_backup_percent').hide();
                }
            }
            catch(err){
                wpvivid_add_notice('Backup', 'Error', err);
                wpvivid_control_backup_unlock();
                jQuery('#wpvivid_postbox_backup_percent').hide();
            }
        }, function (XMLHttpRequest, textStatus, errorThrown) {
            setTimeout(function () {
                wpvivid_delete_ready_task(error);
            }, 3000);
        });
    }
    </script>
    <?php
}

function wpvivid_backup_module_add_tips(){
    ?>
    <div class="custom-info" style="float:left; width:100%;">
        <strong><?php esc_html_e('Tip:', 'wpvivid-backuprestore'); ?></strong>&nbsp<?php esc_html_e('The settings are only for manual backup, which won\'t affect schedule settings.', 'wpvivid-backuprestore'); ?>
    </div>
    <?php
}

function wpvivid_backuppage_add_schedule_module(){
    $schedule=WPvivid_Schedule::get_schedule();
    if($schedule['enable']){
        $schedule_status='Enabled';
        $next_backup_time=gmdate("l, F-d-Y H:i", $schedule['next_start']);
    }
    else{
        $schedule_status='Disabled';
        $next_backup_time='N/A';
    }
    ?>
    <div class="postbox qucikbackup-schedule" id="wpvivid_postbox_backup_schedule">
        <h2><span><?php esc_html_e( 'Backup Schedule','wpvivid-backuprestore'); ?></span></h2>
        <div class="schedule-block">
            <p id="wpvivid_schedule_status"><strong><?php esc_html_e('Schedule Status: ', 'wpvivid-backuprestore'); ?></strong><?php echo esc_html($schedule_status); ?></p>
            <div id="wpvivid_schedule_info">
                <p><strong><?php esc_html_e('Server Time: ', 'wpvivid-backuprestore'); ?></strong><?php echo esc_html(gmdate("F-d-Y H:i",time())); ?></p>
                <p><span id="wpvivid_last_backup_msg"><?php do_action('wpvivid_get_last_backup_message_output'); ?></span></p>
                <p id="wpvivid_next_backup"><strong><?php esc_html_e('Next Backup: ', 'wpvivid-backuprestore'); ?></strong><?php echo esc_html($next_backup_time); ?></p>
            </div>
        </div>
    </div>
    <div style="clear:both;"></div>
    <?php
}


add_action('wpvivid_add_backup_type', 'wpvivid_add_backup_type', 11);
add_action('wpvivid_backup_do_js', 'wpvivid_backup_do_js', 10);
add_filter('wpvivid_download_backup_descript', 'wpvivid_download_backup_descript', 10);
add_filter('wpvivid_restore_website_descript', 'wpvivid_restore_website_descript', 10);

add_filter('wpvivid_backuppage_load_backuplist', 'wpvivid_backuppage_load_backuplist', 10);

add_action('wpvivid_backuppage_add_module', 'wpvivid_backuppage_add_progress_module', 10);
add_action('wpvivid_backuppage_add_module', 'wpvivid_backuppage_add_backup_module', 11);
add_action('wpvivid_backuppage_add_module', 'wpvivid_backuppage_add_schedule_module', 12);

add_action('wpvivid_backup_module_add_sub', 'wpvivid_backup_module_add_descript');
add_action('wpvivid_backup_module_add_sub', 'wpvivid_backup_module_add_backup_type');
add_action('wpvivid_backup_module_add_sub', 'wpvivid_backup_module_add_send_remote');
add_action('wpvivid_backup_module_add_sub', 'wpvivid_backup_module_add_exec');
add_action('wpvivid_backup_module_add_sub', 'wpvivid_backup_module_add_tips');

?>admin/index.php000064400000000032151327705670007464 0ustar00<?php // Silence is goldenadmin/js/wpvivid-quick-snapshot.js000064400000001171151327705670013262 0ustar00function wpvivid_post_request_quick(ajax_data, callback, error_callback, time_out){
    if(typeof time_out === 'undefined')    time_out = 30000;
    ajax_data.nonce=wpvivid_quick_snapshot_ajax_object.ajax_nonce;
    jQuery.ajax({
        type: "post",
        url: wpvivid_quick_snapshot_ajax_object.ajax_url,
        data: ajax_data,
        cache:false,
        success: function (data) {
            callback(data);
        },
        error: function (XMLHttpRequest, textStatus, errorThrown) {
            error_callback(XMLHttpRequest, textStatus, errorThrown);
        },
        timeout: time_out
    });
}

admin/js/jstree/dist/jstree.js000064400001121653151327705670012367 0ustar00/*globals jQuery, define, module, exports, require, window, document, postMessage */
(function (factory) {
	"use strict";
	if (typeof define === 'function' && define.amd) {
		define(['jquery'], factory);
	}
	else if(typeof module !== 'undefined' && module.exports) {
		module.exports = factory(require('jquery'));
	}
	else {
		factory(jQuery);
	}
}(function ($, undefined) {
	"use strict";
/*!
 * jsTree 3.3.7
 * http://jstree.com/
 *
 * Copyright (c) 2014 Ivan Bozhanov (http://vakata.com)
 *
 * Licensed same as jquery - under the terms of the MIT License
 *   http://www.opensource.org/licenses/mit-license.php
 */
/*!
 * if using jslint please allow for the jQuery global and use following options:
 * jslint: loopfunc: true, browser: true, ass: true, bitwise: true, continue: true, nomen: true, plusplus: true, regexp: true, unparam: true, todo: true, white: true
 */
/*jshint -W083 */

	// prevent another load? maybe there is a better way?
	if($.jstree) {
		return;
	}

	/**
	 * ### jsTree core functionality
	 */

	// internal variables
	var instance_counter = 0,
		ccp_node = false,
		ccp_mode = false,
		ccp_inst = false,
		themes_loaded = [],
		src = $('script:last').attr('src'),
		document = window.document; // local variable is always faster to access then a global

	/**
	 * holds all jstree related functions and variables, including the actual class and methods to create, access and manipulate instances.
	 * @name $.jstree
	 */
	$.jstree = {
		/**
		 * specifies the jstree version in use
		 * @name $.jstree.version
		 */
		version : '3.3.7',
		/**
		 * holds all the default options used when creating new instances
		 * @name $.jstree.defaults
		 */
		defaults : {
			/**
			 * configure which plugins will be active on an instance. Should be an array of strings, where each element is a plugin name. The default is `[]`
			 * @name $.jstree.defaults.plugins
			 */
			plugins : []
		},
		/**
		 * stores all loaded jstree plugins (used internally)
		 * @name $.jstree.plugins
		 */
		plugins : {},
		path : src && src.indexOf('/') !== -1 ? src.replace(/\/[^\/]+$/,'') : '',
		idregex : /[\\:&!^|()\[\]<>@*'+~#";.,=\- \/${}%?`]/g,
		root : '#'
	};
	
	/**
	 * creates a jstree instance
	 * @name $.jstree.create(el [, options])
	 * @param {DOMElement|jQuery|String} el the element to create the instance on, can be jQuery extended or a selector
	 * @param {Object} options options for this instance (extends `$.jstree.defaults`)
	 * @return {jsTree} the new instance
	 */
	$.jstree.create = function (el, options) {
		var tmp = new $.jstree.core(++instance_counter),
			opt = options;
		options = $.extend(true, {}, $.jstree.defaults, options);
		if(opt && opt.plugins) {
			options.plugins = opt.plugins;
		}
		$.each(options.plugins, function (i, k) {
			if(i !== 'core') {
				tmp = tmp.plugin(k, options[k]);
			}
		});
		$(el).data('jstree', tmp);
		tmp.init(el, options);
		return tmp;
	};
	/**
	 * remove all traces of jstree from the DOM and destroy all instances
	 * @name $.jstree.destroy()
	 */
	$.jstree.destroy = function () {
		$('.jstree:jstree').jstree('destroy');
		$(document).off('.jstree');
	};
	/**
	 * the jstree class constructor, used only internally
	 * @private
	 * @name $.jstree.core(id)
	 * @param {Number} id this instance's index
	 */
	$.jstree.core = function (id) {
		this._id = id;
		this._cnt = 0;
		this._wrk = null;
		this._data = {
			core : {
				themes : {
					name : false,
					dots : false,
					icons : false,
					ellipsis : false
				},
				selected : [],
				last_error : {},
				working : false,
				worker_queue : [],
				focused : null
			}
		};
	};
	/**
	 * get a reference to an existing instance
	 *
	 * __Examples__
	 *
	 *	// provided a container with an ID of "tree", and a nested node with an ID of "branch"
	 *	// all of there will return the same instance
	 *	$.jstree.reference('tree');
	 *	$.jstree.reference('#tree');
	 *	$.jstree.reference($('#tree'));
	 *	$.jstree.reference(document.getElementByID('tree'));
	 *	$.jstree.reference('branch');
	 *	$.jstree.reference('#branch');
	 *	$.jstree.reference($('#branch'));
	 *	$.jstree.reference(document.getElementByID('branch'));
	 *
	 * @name $.jstree.reference(needle)
	 * @param {DOMElement|jQuery|String} needle
	 * @return {jsTree|null} the instance or `null` if not found
	 */
	$.jstree.reference = function (needle) {
		var tmp = null,
			obj = null;
		if(needle && needle.id && (!needle.tagName || !needle.nodeType)) { needle = needle.id; }

		if(!obj || !obj.length) {
			try { obj = $(needle); } catch (ignore) { }
		}
		if(!obj || !obj.length) {
			try { obj = $('#' + needle.replace($.jstree.idregex,'\\$&')); } catch (ignore) { }
		}
		if(obj && obj.length && (obj = obj.closest('.jstree')).length && (obj = obj.data('jstree'))) {
			tmp = obj;
		}
		else {
			$('.jstree').each(function () {
				var inst = $(this).data('jstree');
				if(inst && inst._model.data[needle]) {
					tmp = inst;
					return false;
				}
			});
		}
		return tmp;
	};
	/**
	 * Create an instance, get an instance or invoke a command on a instance.
	 *
	 * If there is no instance associated with the current node a new one is created and `arg` is used to extend `$.jstree.defaults` for this new instance. There would be no return value (chaining is not broken).
	 *
	 * If there is an existing instance and `arg` is a string the command specified by `arg` is executed on the instance, with any additional arguments passed to the function. If the function returns a value it will be returned (chaining could break depending on function).
	 *
	 * If there is an existing instance and `arg` is not a string the instance itself is returned (similar to `$.jstree.reference`).
	 *
	 * In any other case - nothing is returned and chaining is not broken.
	 *
	 * __Examples__
	 *
	 *	$('#tree1').jstree(); // creates an instance
	 *	$('#tree2').jstree({ plugins : [] }); // create an instance with some options
	 *	$('#tree1').jstree('open_node', '#branch_1'); // call a method on an existing instance, passing additional arguments
	 *	$('#tree2').jstree(); // get an existing instance (or create an instance)
	 *	$('#tree2').jstree(true); // get an existing instance (will not create new instance)
	 *	$('#branch_1').jstree().select_node('#branch_1'); // get an instance (using a nested element and call a method)
	 *
	 * @name $().jstree([arg])
	 * @param {String|Object} arg
	 * @return {Mixed}
	 */
	$.fn.jstree = function (arg) {
		// check for string argument
		var is_method	= (typeof arg === 'string'),
			args		= Array.prototype.slice.call(arguments, 1),
			result		= null;
		if(arg === true && !this.length) { return false; }
		this.each(function () {
			// get the instance (if there is one) and method (if it exists)
			var instance = $.jstree.reference(this),
				method = is_method && instance ? instance[arg] : null;
			// if calling a method, and method is available - execute on the instance
			result = is_method && method ?
				method.apply(instance, args) :
				null;
			// if there is no instance and no method is being called - create one
			if(!instance && !is_method && (arg === undefined || $.isPlainObject(arg))) {
				$.jstree.create(this, arg);
			}
			// if there is an instance and no method is called - return the instance
			if( (instance && !is_method) || arg === true ) {
				result = instance || false;
			}
			// if there was a method call which returned a result - break and return the value
			if(result !== null && result !== undefined) {
				return false;
			}
		});
		// if there was a method call with a valid return value - return that, otherwise continue the chain
		return result !== null && result !== undefined ?
			result : this;
	};
	/**
	 * used to find elements containing an instance
	 *
	 * __Examples__
	 *
	 *	$('div:jstree').each(function () {
	 *		$(this).jstree('destroy');
	 *	});
	 *
	 * @name $(':jstree')
	 * @return {jQuery}
	 */
	$.expr.pseudos.jstree = $.expr.createPseudo(function(search) {
		return function(a) {
			return $(a).hasClass('jstree') &&
				$(a).data('jstree') !== undefined;
		};
	});

	/**
	 * stores all defaults for the core
	 * @name $.jstree.defaults.core
	 */
	$.jstree.defaults.core = {
		/**
		 * data configuration
		 *
		 * If left as `false` the HTML inside the jstree container element is used to populate the tree (that should be an unordered list with list items).
		 *
		 * You can also pass in a HTML string or a JSON array here.
		 *
		 * It is possible to pass in a standard jQuery-like AJAX config and jstree will automatically determine if the response is JSON or HTML and use that to populate the tree.
		 * In addition to the standard jQuery ajax options here you can suppy functions for `data` and `url`, the functions will be run in the current instance's scope and a param will be passed indicating which node is being loaded, the return value of those functions will be used.
		 *
		 * The last option is to specify a function, that function will receive the node being loaded as argument and a second param which is a function which should be called with the result.
		 *
		 * __Examples__
		 *
		 *	// AJAX
		 *	$('#tree').jstree({
		 *		'core' : {
		 *			'data' : {
		 *				'url' : '/get/children/',
		 *				'data' : function (node) {
		 *					return { 'id' : node.id };
		 *				}
		 *			}
		 *		});
		 *
		 *	// direct data
		 *	$('#tree').jstree({
		 *		'core' : {
		 *			'data' : [
		 *				'Simple root node',
		 *				{
		 *					'id' : 'node_2',
		 *					'text' : 'Root node with options',
		 *					'state' : { 'opened' : true, 'selected' : true },
		 *					'children' : [ { 'text' : 'Child 1' }, 'Child 2']
		 *				}
		 *			]
		 *		}
		 *	});
		 *
		 *	// function
		 *	$('#tree').jstree({
		 *		'core' : {
		 *			'data' : function (obj, callback) {
		 *				callback.call(this, ['Root 1', 'Root 2']);
		 *			}
		 *		});
		 *
		 * @name $.jstree.defaults.core.data
		 */
		data			: false,
		/**
		 * configure the various strings used throughout the tree
		 *
		 * You can use an object where the key is the string you need to replace and the value is your replacement.
		 * Another option is to specify a function which will be called with an argument of the needed string and should return the replacement.
		 * If left as `false` no replacement is made.
		 *
		 * __Examples__
		 *
		 *	$('#tree').jstree({
		 *		'core' : {
		 *			'strings' : {
		 *				'Loading ...' : 'Please wait ...'
		 *			}
		 *		}
		 *	});
		 *
		 * @name $.jstree.defaults.core.strings
		 */
		strings			: false,
		/**
		 * determines what happens when a user tries to modify the structure of the tree
		 * If left as `false` all operations like create, rename, delete, move or copy are prevented.
		 * You can set this to `true` to allow all interactions or use a function to have better control.
		 *
		 * __Examples__
		 *
		 *	$('#tree').jstree({
		 *		'core' : {
		 *			'check_callback' : function (operation, node, node_parent, node_position, more) {
		 *				// operation can be 'create_node', 'rename_node', 'delete_node', 'move_node', 'copy_node' or 'edit'
		 *				// in case of 'rename_node' node_position is filled with the new node name
		 *				return operation === 'rename_node' ? true : false;
		 *			}
		 *		}
		 *	});
		 *
		 * @name $.jstree.defaults.core.check_callback
		 */
		check_callback	: false,
		/**
		 * a callback called with a single object parameter in the instance's scope when something goes wrong (operation prevented, ajax failed, etc)
		 * @name $.jstree.defaults.core.error
		 */
		error			: $.noop,
		/**
		 * the open / close animation duration in milliseconds - set this to `false` to disable the animation (default is `200`)
		 * @name $.jstree.defaults.core.animation
		 */
		animation		: 200,
		/**
		 * a boolean indicating if multiple nodes can be selected
		 * @name $.jstree.defaults.core.multiple
		 */
		multiple		: true,
		/**
		 * theme configuration object
		 * @name $.jstree.defaults.core.themes
		 */
		themes			: {
			/**
			 * the name of the theme to use (if left as `false` the default theme is used)
			 * @name $.jstree.defaults.core.themes.name
			 */
			name			: false,
			/**
			 * the URL of the theme's CSS file, leave this as `false` if you have manually included the theme CSS (recommended). You can set this to `true` too which will try to autoload the theme.
			 * @name $.jstree.defaults.core.themes.url
			 */
			url				: false,
			/**
			 * the location of all jstree themes - only used if `url` is set to `true`
			 * @name $.jstree.defaults.core.themes.dir
			 */
			dir				: false,
			/**
			 * a boolean indicating if connecting dots are shown
			 * @name $.jstree.defaults.core.themes.dots
			 */
			dots			: true,
			/**
			 * a boolean indicating if node icons are shown
			 * @name $.jstree.defaults.core.themes.icons
			 */
			icons			: true,
			/**
			 * a boolean indicating if node ellipsis should be shown - this only works with a fixed with on the container
			 * @name $.jstree.defaults.core.themes.ellipsis
			 */
			ellipsis		: false,
			/**
			 * a boolean indicating if the tree background is striped
			 * @name $.jstree.defaults.core.themes.stripes
			 */
			stripes			: false,
			/**
			 * a string (or boolean `false`) specifying the theme variant to use (if the theme supports variants)
			 * @name $.jstree.defaults.core.themes.variant
			 */
			variant			: false,
			/**
			 * a boolean specifying if a reponsive version of the theme should kick in on smaller screens (if the theme supports it). Defaults to `false`.
			 * @name $.jstree.defaults.core.themes.responsive
			 */
			responsive		: false
		},
		/**
		 * if left as `true` all parents of all selected nodes will be opened once the tree loads (so that all selected nodes are visible to the user)
		 * @name $.jstree.defaults.core.expand_selected_onload
		 */
		expand_selected_onload : true,
		/**
		 * if left as `true` web workers will be used to parse incoming JSON data where possible, so that the UI will not be blocked by large requests. Workers are however about 30% slower. Defaults to `true`
		 * @name $.jstree.defaults.core.worker
		 */
		worker : true,
		/**
		 * Force node text to plain text (and escape HTML). Defaults to `false`
		 * @name $.jstree.defaults.core.force_text
		 */
		force_text : false,
		/**
		 * Should the node be toggled if the text is double clicked. Defaults to `true`
		 * @name $.jstree.defaults.core.dblclick_toggle
		 */
		dblclick_toggle : true,
		/**
		 * Should the loaded nodes be part of the state. Defaults to `false`
		 * @name $.jstree.defaults.core.loaded_state
		 */
		loaded_state : false,
		/**
		 * Should the last active node be focused when the tree container is blurred and the focused again. This helps working with screen readers. Defaults to `true`
		 * @name $.jstree.defaults.core.restore_focus
		 */
		restore_focus : true,
		/**
		 * Default keyboard shortcuts (an object where each key is the button name or combo - like 'enter', 'ctrl-space', 'p', etc and the value is the function to execute in the instance's scope)
		 * @name $.jstree.defaults.core.keyboard
		 */
		keyboard : {
			'ctrl-space': function (e) {
				// aria defines space only with Ctrl
				e.type = "click";
				$(e.currentTarget).trigger(e);
			},
			'enter': function (e) {
				// enter
				e.type = "click";
				$(e.currentTarget).trigger(e);
			},
			'left': function (e) {
				// left
				e.preventDefault();
				if(this.is_open(e.currentTarget)) {
					this.close_node(e.currentTarget);
				}
				else {
					var o = this.get_parent(e.currentTarget);
					if(o && o.id !== $.jstree.root) { this.get_node(o, true).children('.jstree-anchor').focus(); }
				}
			},
			'up': function (e) {
				// up
				e.preventDefault();
				var o = this.get_prev_dom(e.currentTarget);
				if(o && o.length) { o.children('.jstree-anchor').focus(); }
			},
			'right': function (e) {
				// right
				e.preventDefault();
				if(this.is_closed(e.currentTarget)) {
					this.open_node(e.currentTarget, function (o) { this.get_node(o, true).children('.jstree-anchor').focus(); });
				}
				else if (this.is_open(e.currentTarget)) {
					var o = this.get_node(e.currentTarget, true).children('.jstree-children')[0];
					if(o) { $(this._firstChild(o)).children('.jstree-anchor').focus(); }
				}
			},
			'down': function (e) {
				// down
				e.preventDefault();
				var o = this.get_next_dom(e.currentTarget);
				if(o && o.length) { o.children('.jstree-anchor').focus(); }
			},
			'*': function (e) {
				// aria defines * on numpad as open_all - not very common
				this.open_all();
			},
			'home': function (e) {
				// home
				e.preventDefault();
				var o = this._firstChild(this.get_container_ul()[0]);
				if(o) { $(o).children('.jstree-anchor').filter(':visible').focus(); }
			},
			'end': function (e) {
				// end
				e.preventDefault();
				this.element.find('.jstree-anchor').filter(':visible').last().focus();
			},
			'f2': function (e) {
				// f2 - safe to include - if check_callback is false it will fail
				e.preventDefault();
				this.edit(e.currentTarget);
			}
		}
	};
	$.jstree.core.prototype = {
		/**
		 * used to decorate an instance with a plugin. Used internally.
		 * @private
		 * @name plugin(deco [, opts])
		 * @param  {String} deco the plugin to decorate with
		 * @param  {Object} opts options for the plugin
		 * @return {jsTree}
		 */
		plugin : function (deco, opts) {
			var Child = $.jstree.plugins[deco];
			if(Child) {
				this._data[deco] = {};
				Child.prototype = this;
				return new Child(opts, this);
			}
			return this;
		},
		/**
		 * initialize the instance. Used internally.
		 * @private
		 * @name init(el, optons)
		 * @param {DOMElement|jQuery|String} el the element we are transforming
		 * @param {Object} options options for this instance
		 * @trigger init.jstree, loading.jstree, loaded.jstree, ready.jstree, changed.jstree
		 */
		init : function (el, options) {
			this._model = {
				data : {},
				changed : [],
				force_full_redraw : false,
				redraw_timeout : false,
				default_state : {
					loaded : true,
					opened : false,
					selected : false,
					disabled : false
				}
			};
			this._model.data[$.jstree.root] = {
				id : $.jstree.root,
				parent : null,
				parents : [],
				children : [],
				children_d : [],
				state : { loaded : false }
			};

			this.element = $(el).addClass('jstree jstree-' + this._id);
			this.settings = options;

			this._data.core.ready = false;
			this._data.core.loaded = false;
			this._data.core.rtl = (this.element.css("direction") === "rtl");
			this.element[this._data.core.rtl ? 'addClass' : 'removeClass']("jstree-rtl");
			this.element.attr('role','tree');
			if(this.settings.core.multiple) {
				this.element.attr('aria-multiselectable', true);
			}
			if(!this.element.attr('tabindex')) {
				this.element.attr('tabindex','0');
			}

			this.bind();
			/**
			 * triggered after all events are bound
			 * @event
			 * @name init.jstree
			 */
			this.trigger("init");

			this._data.core.original_container_html = this.element.find(" > ul > li").clone(true);
			this._data.core.original_container_html
				.find("li").addBack()
				.contents().filter(function() {
					return this.nodeType === 3 && (!this.nodeValue || /^\s+$/.test(this.nodeValue));
				})
				.remove();
			this.element.html("<"+"ul class='jstree-container-ul jstree-children' role='group'><"+"li id='j"+this._id+"_loading' class='jstree-initial-node jstree-loading jstree-leaf jstree-last' role='tree-item'><i class='jstree-icon jstree-ocl'></i><"+"a class='jstree-anchor' href='#'><i class='jstree-icon jstree-themeicon-hidden'></i>" + this.get_string("Loading ...") + "</a></li></ul>");
			this.element.attr('aria-activedescendant','j' + this._id + '_loading');
			this._data.core.li_height = this.get_container_ul().children("li").first().outerHeight() || 24;
			this._data.core.node = this._create_prototype_node();
			/**
			 * triggered after the loading text is shown and before loading starts
			 * @event
			 * @name loading.jstree
			 */
			this.trigger("loading");
			this.load_node($.jstree.root);
		},
		/**
		 * destroy an instance
		 * @name destroy()
		 * @param  {Boolean} keep_html if not set to `true` the container will be emptied, otherwise the current DOM elements will be kept intact
		 */
		destroy : function (keep_html) {
			/**
			 * triggered before the tree is destroyed
			 * @event
			 * @name destroy.jstree
			 */
			this.trigger("destroy");
			if(this._wrk) {
				try {
					window.URL.revokeObjectURL(this._wrk);
					this._wrk = null;
				}
				catch (ignore) { }
			}
			if(!keep_html) { this.element.empty(); }
			this.teardown();
		},
		/**
		 * Create a prototype node
		 * @name _create_prototype_node()
		 * @return {DOMElement}
		 */
		_create_prototype_node : function () {
			var _node = document.createElement('LI'), _temp1, _temp2;
			_node.setAttribute('role', 'treeitem');
			_temp1 = document.createElement('I');
			_temp1.className = 'jstree-icon jstree-ocl';
			_temp1.setAttribute('role', 'presentation');
			_node.appendChild(_temp1);
			_temp1 = document.createElement('A');
			_temp1.className = 'jstree-anchor';
			_temp1.setAttribute('href','#');
			_temp1.setAttribute('tabindex','-1');
			_temp2 = document.createElement('I');
			_temp2.className = 'jstree-icon jstree-themeicon';
			_temp2.setAttribute('role', 'presentation');
			_temp1.appendChild(_temp2);
			_node.appendChild(_temp1);
			_temp1 = _temp2 = null;

			return _node;
		},
		_kbevent_to_func : function (e) {
			var keys = {
				8: "Backspace", 9: "Tab", 13: "Return", 19: "Pause", 27: "Esc",
				32: "Space", 33: "PageUp", 34: "PageDown", 35: "End", 36: "Home",
				37: "Left", 38: "Up", 39: "Right", 40: "Down", 44: "Print", 45: "Insert",
				46: "Delete", 96: "Numpad0", 97: "Numpad1", 98: "Numpad2", 99 : "Numpad3",
				100: "Numpad4", 101: "Numpad5", 102: "Numpad6", 103: "Numpad7",
				104: "Numpad8", 105: "Numpad9", '-13': "NumpadEnter", 112: "F1",
				113: "F2", 114: "F3", 115: "F4", 116: "F5", 117: "F6", 118: "F7",
				119: "F8", 120: "F9", 121: "F10", 122: "F11", 123: "F12", 144: "Numlock",
				145: "Scrolllock", 16: 'Shift', 17: 'Ctrl', 18: 'Alt',
				48: '0',  49: '1',  50: '2',  51: '3',  52: '4', 53:  '5',
				54: '6',  55: '7',  56: '8',  57: '9',  59: ';',  61: '=', 65:  'a',
				66: 'b',  67: 'c',  68: 'd',  69: 'e',  70: 'f',  71: 'g', 72:  'h',
				73: 'i',  74: 'j',  75: 'k',  76: 'l',  77: 'm',  78: 'n', 79:  'o',
				80: 'p',  81: 'q',  82: 'r',  83: 's',  84: 't',  85: 'u', 86:  'v',
				87: 'w',  88: 'x',  89: 'y',  90: 'z', 107: '+', 109: '-', 110: '.',
				186: ';', 187: '=', 188: ',', 189: '-', 190: '.', 191: '/', 192: '`',
				219: '[', 220: '\\',221: ']', 222: "'", 111: '/', 106: '*', 173: '-'
			};
			var parts = [];
			if (e.ctrlKey) { parts.push('ctrl'); }
			if (e.altKey) { parts.push('alt'); }
			if (e.shiftKey) { parts.push('shift'); }
			parts.push(keys[e.which] || e.which);
			parts = parts.sort().join('-').toLowerCase();

			var kb = this.settings.core.keyboard, i, tmp;
			for (i in kb) {
				if (kb.hasOwnProperty(i)) {
					tmp = i;
					if (tmp !== '-' && tmp !== '+') {
						tmp = tmp.replace('--', '-MINUS').replace('+-', '-MINUS').replace('++', '-PLUS').replace('-+', '-PLUS');
						tmp = tmp.split(/-|\+/).sort().join('-').replace('MINUS', '-').replace('PLUS', '+').toLowerCase();
					}
					if (tmp === parts) {
						return kb[i];
					}
				}
			}
			return null;
		},
		/**
		 * part of the destroying of an instance. Used internally.
		 * @private
		 * @name teardown()
		 */
		teardown : function () {
			this.unbind();
			this.element
				.removeClass('jstree')
				.removeData('jstree')
				.find("[class^='jstree']")
					.addBack()
					.attr("class", function () { return this.className.replace(/jstree[^ ]*|$/ig,''); });
			this.element = null;
		},
		/**
		 * bind all events. Used internally.
		 * @private
		 * @name bind()
		 */
		bind : function () {
			var word = '',
				tout = null,
				was_click = 0;
			this.element
				.on("dblclick.jstree", function (e) {
						if(e.target.tagName && e.target.tagName.toLowerCase() === "input") { return true; }
						if(document.selection && document.selection.empty) {
							document.selection.empty();
						}
						else {
							if(window.getSelection) {
								var sel = window.getSelection();
								try {
									sel.removeAllRanges();
									sel.collapse();
								} catch (ignore) { }
							}
						}
					})
				.on("mousedown.jstree", $.proxy(function (e) {
						if(e.target === this.element[0]) {
							e.preventDefault(); // prevent losing focus when clicking scroll arrows (FF, Chrome)
							was_click = +(new Date()); // ie does not allow to prevent losing focus
						}
					}, this))
				.on("mousedown.jstree", ".jstree-ocl", function (e) {
						e.preventDefault(); // prevent any node inside from losing focus when clicking the open/close icon
					})
				.on("click.jstree", ".jstree-ocl", $.proxy(function (e) {
						this.toggle_node(e.target);
					}, this))
				.on("dblclick.jstree", ".jstree-anchor", $.proxy(function (e) {
						if(e.target.tagName && e.target.tagName.toLowerCase() === "input") { return true; }
						if(this.settings.core.dblclick_toggle) {
							this.toggle_node(e.target);
						}
					}, this))
				.on("click.jstree", ".jstree-anchor", $.proxy(function (e) {
						e.preventDefault();
						if(e.currentTarget !== document.activeElement) { $(e.currentTarget).focus(); }
						this.activate_node(e.currentTarget, e);
					}, this))
				.on('keydown.jstree', '.jstree-anchor', $.proxy(function (e) {
						if(e.target.tagName && e.target.tagName.toLowerCase() === "input") { return true; }
						if(this._data.core.rtl) {
							if(e.which === 37) { e.which = 39; }
							else if(e.which === 39) { e.which = 37; }
						}
						var f = this._kbevent_to_func(e);
						if (f) {
							var r = f.call(this, e);
							if (r === false || r === true) {
								return r;
							}
						}
					}, this))
				.on("load_node.jstree", $.proxy(function (e, data) {
						if(data.status) {
							if(data.node.id === $.jstree.root && !this._data.core.loaded) {
								this._data.core.loaded = true;
								if(this._firstChild(this.get_container_ul()[0])) {
									this.element.attr('aria-activedescendant',this._firstChild(this.get_container_ul()[0]).id);
								}
								/**
								 * triggered after the root node is loaded for the first time
								 * @event
								 * @name loaded.jstree
								 */
								this.trigger("loaded");
							}
							if(!this._data.core.ready) {
								setTimeout($.proxy(function() {
									if(this.element && !this.get_container_ul().find('.jstree-loading').length) {
										this._data.core.ready = true;
										if(this._data.core.selected.length) {
											if(this.settings.core.expand_selected_onload) {
												var tmp = [], i, j;
												for(i = 0, j = this._data.core.selected.length; i < j; i++) {
													tmp = tmp.concat(this._model.data[this._data.core.selected[i]].parents);
												}
												tmp = $.vakata.array_unique(tmp);
												for(i = 0, j = tmp.length; i < j; i++) {
													this.open_node(tmp[i], false, 0);
												}
											}
											this.trigger('changed', { 'action' : 'ready', 'selected' : this._data.core.selected });
										}
										/**
										 * triggered after all nodes are finished loading
										 * @event
										 * @name ready.jstree
										 */
										this.trigger("ready");
									}
								}, this), 0);
							}
						}
					}, this))
				// quick searching when the tree is focused
				.on('keypress.jstree', $.proxy(function (e) {
						if(e.target.tagName && e.target.tagName.toLowerCase() === "input") { return true; }
						if(tout) { clearTimeout(tout); }
						tout = setTimeout(function () {
							word = '';
						}, 500);

						var chr = String.fromCharCode(e.which).toLowerCase(),
							col = this.element.find('.jstree-anchor').filter(':visible'),
							ind = col.index(document.activeElement) || 0,
							end = false;
						word += chr;

						// match for whole word from current node down (including the current node)
						if(word.length > 1) {
							col.slice(ind).each($.proxy(function (i, v) {
								if($(v).text().toLowerCase().indexOf(word) === 0) {
									$(v).focus();
									end = true;
									return false;
								}
							}, this));
							if(end) { return; }

							// match for whole word from the beginning of the tree
							col.slice(0, ind).each($.proxy(function (i, v) {
								if($(v).text().toLowerCase().indexOf(word) === 0) {
									$(v).focus();
									end = true;
									return false;
								}
							}, this));
							if(end) { return; }
						}
						// list nodes that start with that letter (only if word consists of a single char)
						if(new RegExp('^' + chr.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&') + '+$').test(word)) {
							// search for the next node starting with that letter
							col.slice(ind + 1).each($.proxy(function (i, v) {
								if($(v).text().toLowerCase().charAt(0) === chr) {
									$(v).focus();
									end = true;
									return false;
								}
							}, this));
							if(end) { return; }

							// search from the beginning
							col.slice(0, ind + 1).each($.proxy(function (i, v) {
								if($(v).text().toLowerCase().charAt(0) === chr) {
									$(v).focus();
									end = true;
									return false;
								}
							}, this));
							if(end) { return; }
						}
					}, this))
				// THEME RELATED
				.on("init.jstree", $.proxy(function () {
						var s = this.settings.core.themes;
						this._data.core.themes.dots			= s.dots;
						this._data.core.themes.stripes		= s.stripes;
						this._data.core.themes.icons		= s.icons;
						this._data.core.themes.ellipsis		= s.ellipsis;
						this.set_theme(s.name || "default", s.url);
						this.set_theme_variant(s.variant);
					}, this))
				.on("loading.jstree", $.proxy(function () {
						this[ this._data.core.themes.dots ? "show_dots" : "hide_dots" ]();
						this[ this._data.core.themes.icons ? "show_icons" : "hide_icons" ]();
						this[ this._data.core.themes.stripes ? "show_stripes" : "hide_stripes" ]();
						this[ this._data.core.themes.ellipsis ? "show_ellipsis" : "hide_ellipsis" ]();
					}, this))
				.on('blur.jstree', '.jstree-anchor', $.proxy(function (e) {
						this._data.core.focused = null;
						$(e.currentTarget).filter('.jstree-hovered').mouseleave();
						this.element.attr('tabindex', '0');
					}, this))
				.on('focus.jstree', '.jstree-anchor', $.proxy(function (e) {
						var tmp = this.get_node(e.currentTarget);
						if(tmp && tmp.id) {
							this._data.core.focused = tmp.id;
						}
						this.element.find('.jstree-hovered').not(e.currentTarget).mouseleave();
						$(e.currentTarget).mouseenter();
						this.element.attr('tabindex', '-1');
					}, this))
				.on('focus.jstree', $.proxy(function () {
						if(+(new Date()) - was_click > 500 && !this._data.core.focused && this.settings.core.restore_focus) {
							was_click = 0;
							var act = this.get_node(this.element.attr('aria-activedescendant'), true);
							if(act) {
								act.find('> .jstree-anchor').focus();
							}
						}
					}, this))
				.on('mouseenter.jstree', '.jstree-anchor', $.proxy(function (e) {
						this.hover_node(e.currentTarget);
					}, this))
				.on('mouseleave.jstree', '.jstree-anchor', $.proxy(function (e) {
						this.dehover_node(e.currentTarget);
					}, this));
		},
		/**
		 * part of the destroying of an instance. Used internally.
		 * @private
		 * @name unbind()
		 */
		unbind : function () {
			this.element.off('.jstree');
			$(document).off('.jstree-' + this._id);
		},
		/**
		 * trigger an event. Used internally.
		 * @private
		 * @name trigger(ev [, data])
		 * @param  {String} ev the name of the event to trigger
		 * @param  {Object} data additional data to pass with the event
		 */
		trigger : function (ev, data) {
			if(!data) {
				data = {};
			}
			data.instance = this;
			this.element.triggerHandler(ev.replace('.jstree','') + '.jstree', data);
		},
		/**
		 * returns the jQuery extended instance container
		 * @name get_container()
		 * @return {jQuery}
		 */
		get_container : function () {
			return this.element;
		},
		/**
		 * returns the jQuery extended main UL node inside the instance container. Used internally.
		 * @private
		 * @name get_container_ul()
		 * @return {jQuery}
		 */
		get_container_ul : function () {
			return this.element.children(".jstree-children").first();
		},
		/**
		 * gets string replacements (localization). Used internally.
		 * @private
		 * @name get_string(key)
		 * @param  {String} key
		 * @return {String}
		 */
		get_string : function (key) {
			var a = this.settings.core.strings;
			if($.isFunction(a)) { return a.call(this, key); }
			if(a && a[key]) { return a[key]; }
			return key;
		},
		/**
		 * gets the first child of a DOM node. Used internally.
		 * @private
		 * @name _firstChild(dom)
		 * @param  {DOMElement} dom
		 * @return {DOMElement}
		 */
		_firstChild : function (dom) {
			dom = dom ? dom.firstChild : null;
			while(dom !== null && dom.nodeType !== 1) {
				dom = dom.nextSibling;
			}
			return dom;
		},
		/**
		 * gets the next sibling of a DOM node. Used internally.
		 * @private
		 * @name _nextSibling(dom)
		 * @param  {DOMElement} dom
		 * @return {DOMElement}
		 */
		_nextSibling : function (dom) {
			dom = dom ? dom.nextSibling : null;
			while(dom !== null && dom.nodeType !== 1) {
				dom = dom.nextSibling;
			}
			return dom;
		},
		/**
		 * gets the previous sibling of a DOM node. Used internally.
		 * @private
		 * @name _previousSibling(dom)
		 * @param  {DOMElement} dom
		 * @return {DOMElement}
		 */
		_previousSibling : function (dom) {
			dom = dom ? dom.previousSibling : null;
			while(dom !== null && dom.nodeType !== 1) {
				dom = dom.previousSibling;
			}
			return dom;
		},
		/**
		 * get the JSON representation of a node (or the actual jQuery extended DOM node) by using any input (child DOM element, ID string, selector, etc)
		 * @name get_node(obj [, as_dom])
		 * @param  {mixed} obj
		 * @param  {Boolean} as_dom
		 * @return {Object|jQuery}
		 */
		get_node : function (obj, as_dom) {
			if(obj && obj.id) {
				obj = obj.id;
			}
			if (obj instanceof jQuery && obj.length && obj[0].id) {
				obj = obj[0].id;
			}
			var dom;
			try {
				if(this._model.data[obj]) {
					obj = this._model.data[obj];
				}
				else if(typeof obj === "string" && this._model.data[obj.replace(/^#/, '')]) {
					obj = this._model.data[obj.replace(/^#/, '')];
				}
				else if(typeof obj === "string" && (dom = $('#' + obj.replace($.jstree.idregex,'\\$&'), this.element)).length && this._model.data[dom.closest('.jstree-node').attr('id')]) {
					obj = this._model.data[dom.closest('.jstree-node').attr('id')];
				}
				else if((dom = this.element.find(obj)).length && this._model.data[dom.closest('.jstree-node').attr('id')]) {
					obj = this._model.data[dom.closest('.jstree-node').attr('id')];
				}
				else if((dom = this.element.find(obj)).length && dom.hasClass('jstree')) {
					obj = this._model.data[$.jstree.root];
				}
				else {
					return false;
				}

				if(as_dom) {
					obj = obj.id === $.jstree.root ? this.element : $('#' + obj.id.replace($.jstree.idregex,'\\$&'), this.element);
				}
				return obj;
			} catch (ex) { return false; }
		},
		/**
		 * get the path to a node, either consisting of node texts, or of node IDs, optionally glued together (otherwise an array)
		 * @name get_path(obj [, glue, ids])
		 * @param  {mixed} obj the node
		 * @param  {String} glue if you want the path as a string - pass the glue here (for example '/'), if a falsy value is supplied here, an array is returned
		 * @param  {Boolean} ids if set to true build the path using ID, otherwise node text is used
		 * @return {mixed}
		 */
		get_path : function (obj, glue, ids) {
			obj = obj.parents ? obj : this.get_node(obj);
			if(!obj || obj.id === $.jstree.root || !obj.parents) {
				return false;
			}
			var i, j, p = [];
			p.push(ids ? obj.id : obj.text);
			for(i = 0, j = obj.parents.length; i < j; i++) {
				p.push(ids ? obj.parents[i] : this.get_text(obj.parents[i]));
			}
			p = p.reverse().slice(1);
			return glue ? p.join(glue) : p;
		},
		/**
		 * get the next visible node that is below the `obj` node. If `strict` is set to `true` only sibling nodes are returned.
		 * @name get_next_dom(obj [, strict])
		 * @param  {mixed} obj
		 * @param  {Boolean} strict
		 * @return {jQuery}
		 */
		get_next_dom : function (obj, strict) {
			var tmp;
			obj = this.get_node(obj, true);
			if(obj[0] === this.element[0]) {
				tmp = this._firstChild(this.get_container_ul()[0]);
				while (tmp && tmp.offsetHeight === 0) {
					tmp = this._nextSibling(tmp);
				}
				return tmp ? $(tmp) : false;
			}
			if(!obj || !obj.length) {
				return false;
			}
			if(strict) {
				tmp = obj[0];
				do {
					tmp = this._nextSibling(tmp);
				} while (tmp && tmp.offsetHeight === 0);
				return tmp ? $(tmp) : false;
			}
			if(obj.hasClass("jstree-open")) {
				tmp = this._firstChild(obj.children('.jstree-children')[0]);
				while (tmp && tmp.offsetHeight === 0) {
					tmp = this._nextSibling(tmp);
				}
				if(tmp !== null) {
					return $(tmp);
				}
			}
			tmp = obj[0];
			do {
				tmp = this._nextSibling(tmp);
			} while (tmp && tmp.offsetHeight === 0);
			if(tmp !== null) {
				return $(tmp);
			}
			return obj.parentsUntil(".jstree",".jstree-node").nextAll(".jstree-node:visible").first();
		},
		/**
		 * get the previous visible node that is above the `obj` node. If `strict` is set to `true` only sibling nodes are returned.
		 * @name get_prev_dom(obj [, strict])
		 * @param  {mixed} obj
		 * @param  {Boolean} strict
		 * @return {jQuery}
		 */
		get_prev_dom : function (obj, strict) {
			var tmp;
			obj = this.get_node(obj, true);
			if(obj[0] === this.element[0]) {
				tmp = this.get_container_ul()[0].lastChild;
				while (tmp && tmp.offsetHeight === 0) {
					tmp = this._previousSibling(tmp);
				}
				return tmp ? $(tmp) : false;
			}
			if(!obj || !obj.length) {
				return false;
			}
			if(strict) {
				tmp = obj[0];
				do {
					tmp = this._previousSibling(tmp);
				} while (tmp && tmp.offsetHeight === 0);
				return tmp ? $(tmp) : false;
			}
			tmp = obj[0];
			do {
				tmp = this._previousSibling(tmp);
			} while (tmp && tmp.offsetHeight === 0);
			if(tmp !== null) {
				obj = $(tmp);
				while(obj.hasClass("jstree-open")) {
					obj = obj.children(".jstree-children").first().children(".jstree-node:visible:last");
				}
				return obj;
			}
			tmp = obj[0].parentNode.parentNode;
			return tmp && tmp.className && tmp.className.indexOf('jstree-node') !== -1 ? $(tmp) : false;
		},
		/**
		 * get the parent ID of a node
		 * @name get_parent(obj)
		 * @param  {mixed} obj
		 * @return {String}
		 */
		get_parent : function (obj) {
			obj = this.get_node(obj);
			if(!obj || obj.id === $.jstree.root) {
				return false;
			}
			return obj.parent;
		},
		/**
		 * get a jQuery collection of all the children of a node (node must be rendered), returns false on error
		 * @name get_children_dom(obj)
		 * @param  {mixed} obj
		 * @return {jQuery}
		 */
		get_children_dom : function (obj) {
			obj = this.get_node(obj, true);
			if(obj[0] === this.element[0]) {
				return this.get_container_ul().children(".jstree-node");
			}
			if(!obj || !obj.length) {
				return false;
			}
			return obj.children(".jstree-children").children(".jstree-node");
		},
		/**
		 * checks if a node has children
		 * @name is_parent(obj)
		 * @param  {mixed} obj
		 * @return {Boolean}
		 */
		is_parent : function (obj) {
			obj = this.get_node(obj);
			return obj && (obj.state.loaded === false || obj.children.length > 0);
		},
		/**
		 * checks if a node is loaded (its children are available)
		 * @name is_loaded(obj)
		 * @param  {mixed} obj
		 * @return {Boolean}
		 */
		is_loaded : function (obj) {
			obj = this.get_node(obj);
			return obj && obj.state.loaded;
		},
		/**
		 * check if a node is currently loading (fetching children)
		 * @name is_loading(obj)
		 * @param  {mixed} obj
		 * @return {Boolean}
		 */
		is_loading : function (obj) {
			obj = this.get_node(obj);
			return obj && obj.state && obj.state.loading;
		},
		/**
		 * check if a node is opened
		 * @name is_open(obj)
		 * @param  {mixed} obj
		 * @return {Boolean}
		 */
		is_open : function (obj) {
			obj = this.get_node(obj);
			return obj && obj.state.opened;
		},
		/**
		 * check if a node is in a closed state
		 * @name is_closed(obj)
		 * @param  {mixed} obj
		 * @return {Boolean}
		 */
		is_closed : function (obj) {
			obj = this.get_node(obj);
			return obj && this.is_parent(obj) && !obj.state.opened;
		},
		/**
		 * check if a node has no children
		 * @name is_leaf(obj)
		 * @param  {mixed} obj
		 * @return {Boolean}
		 */
		is_leaf : function (obj) {
			return !this.is_parent(obj);
		},
		/**
		 * loads a node (fetches its children using the `core.data` setting). Multiple nodes can be passed to by using an array.
		 * @name load_node(obj [, callback])
		 * @param  {mixed} obj
		 * @param  {function} callback a function to be executed once loading is complete, the function is executed in the instance's scope and receives two arguments - the node and a boolean status
		 * @return {Boolean}
		 * @trigger load_node.jstree
		 */
		load_node : function (obj, callback) {
			var k, l, i, j, c;
			if($.isArray(obj)) {
				this._load_nodes(obj.slice(), callback);
				return true;
			}
			obj = this.get_node(obj);
			if(!obj) {
				if(callback) { callback.call(this, obj, false); }
				return false;
			}
			// if(obj.state.loading) { } // the node is already loading - just wait for it to load and invoke callback? but if called implicitly it should be loaded again?
			if(obj.state.loaded) {
				obj.state.loaded = false;
				for(i = 0, j = obj.parents.length; i < j; i++) {
					this._model.data[obj.parents[i]].children_d = $.vakata.array_filter(this._model.data[obj.parents[i]].children_d, function (v) {
						return $.inArray(v, obj.children_d) === -1;
					});
				}
				for(k = 0, l = obj.children_d.length; k < l; k++) {
					if(this._model.data[obj.children_d[k]].state.selected) {
						c = true;
					}
					delete this._model.data[obj.children_d[k]];
				}
				if (c) {
					this._data.core.selected = $.vakata.array_filter(this._data.core.selected, function (v) {
						return $.inArray(v, obj.children_d) === -1;
					});
				}
				obj.children = [];
				obj.children_d = [];
				if(c) {
					this.trigger('changed', { 'action' : 'load_node', 'node' : obj, 'selected' : this._data.core.selected });
				}
			}
			obj.state.failed = false;
			obj.state.loading = true;
			this.get_node(obj, true).addClass("jstree-loading").attr('aria-busy',true);
			this._load_node(obj, $.proxy(function (status) {
				obj = this._model.data[obj.id];
				obj.state.loading = false;
				obj.state.loaded = status;
				obj.state.failed = !obj.state.loaded;
				var dom = this.get_node(obj, true), i = 0, j = 0, m = this._model.data, has_children = false;
				for(i = 0, j = obj.children.length; i < j; i++) {
					if(m[obj.children[i]] && !m[obj.children[i]].state.hidden) {
						has_children = true;
						break;
					}
				}
				if(obj.state.loaded && dom && dom.length) {
					dom.removeClass('jstree-closed jstree-open jstree-leaf');
					if (!has_children) {
						dom.addClass('jstree-leaf');
					}
					else {
						if (obj.id !== '#') {
							dom.addClass(obj.state.opened ? 'jstree-open' : 'jstree-closed');
						}
					}
				}
				dom.removeClass("jstree-loading").attr('aria-busy',false);
				/**
				 * triggered after a node is loaded
				 * @event
				 * @name load_node.jstree
				 * @param {Object} node the node that was loading
				 * @param {Boolean} status was the node loaded successfully
				 */
				this.trigger('load_node', { "node" : obj, "status" : status });
				if(callback) {
					callback.call(this, obj, status);
				}
			}, this));
			return true;
		},
		/**
		 * load an array of nodes (will also load unavailable nodes as soon as they appear in the structure). Used internally.
		 * @private
		 * @name _load_nodes(nodes [, callback])
		 * @param  {array} nodes
		 * @param  {function} callback a function to be executed once loading is complete, the function is executed in the instance's scope and receives one argument - the array passed to _load_nodes
		 */
		_load_nodes : function (nodes, callback, is_callback, force_reload) {
			var r = true,
				c = function () { this._load_nodes(nodes, callback, true); },
				m = this._model.data, i, j, tmp = [];
			for(i = 0, j = nodes.length; i < j; i++) {
				if(m[nodes[i]] && ( (!m[nodes[i]].state.loaded && !m[nodes[i]].state.failed) || (!is_callback && force_reload) )) {
					if(!this.is_loading(nodes[i])) {
						this.load_node(nodes[i], c);
					}
					r = false;
				}
			}
			if(r) {
				for(i = 0, j = nodes.length; i < j; i++) {
					if(m[nodes[i]] && m[nodes[i]].state.loaded) {
						tmp.push(nodes[i]);
					}
				}
				if(callback && !callback.done) {
					callback.call(this, tmp);
					callback.done = true;
				}
			}
		},
		/**
		 * loads all unloaded nodes
		 * @name load_all([obj, callback])
		 * @param {mixed} obj the node to load recursively, omit to load all nodes in the tree
		 * @param {function} callback a function to be executed once loading all the nodes is complete,
		 * @trigger load_all.jstree
		 */
		load_all : function (obj, callback) {
			if(!obj) { obj = $.jstree.root; }
			obj = this.get_node(obj);
			if(!obj) { return false; }
			var to_load = [],
				m = this._model.data,
				c = m[obj.id].children_d,
				i, j;
			if(obj.state && !obj.state.loaded) {
				to_load.push(obj.id);
			}
			for(i = 0, j = c.length; i < j; i++) {
				if(m[c[i]] && m[c[i]].state && !m[c[i]].state.loaded) {
					to_load.push(c[i]);
				}
			}
			if(to_load.length) {
				this._load_nodes(to_load, function () {
					this.load_all(obj, callback);
				});
			}
			else {
				/**
				 * triggered after a load_all call completes
				 * @event
				 * @name load_all.jstree
				 * @param {Object} node the recursively loaded node
				 */
				if(callback) { callback.call(this, obj); }
				this.trigger('load_all', { "node" : obj });
			}
		},
		/**
		 * handles the actual loading of a node. Used only internally.
		 * @private
		 * @name _load_node(obj [, callback])
		 * @param  {mixed} obj
		 * @param  {function} callback a function to be executed once loading is complete, the function is executed in the instance's scope and receives one argument - a boolean status
		 * @return {Boolean}
		 */
		_load_node : function (obj, callback) {
			var s = this.settings.core.data, t;
			var notTextOrCommentNode = function notTextOrCommentNode () {
				return this.nodeType !== 3 && this.nodeType !== 8;
			};
			// use original HTML
			if(!s) {
				if(obj.id === $.jstree.root) {
					return this._append_html_data(obj, this._data.core.original_container_html.clone(true), function (status) {
						callback.call(this, status);
					});
				}
				else {
					return callback.call(this, false);
				}
				// return callback.call(this, obj.id === $.jstree.root ? this._append_html_data(obj, this._data.core.original_container_html.clone(true)) : false);
			}
			if($.isFunction(s)) {
				return s.call(this, obj, $.proxy(function (d) {
					if(d === false) {
						callback.call(this, false);
					}
					else {
						this[typeof d === 'string' ? '_append_html_data' : '_append_json_data'](obj, typeof d === 'string' ? $($.parseHTML(d)).filter(notTextOrCommentNode) : d, function (status) {
							callback.call(this, status);
						});
					}
					// return d === false ? callback.call(this, false) : callback.call(this, this[typeof d === 'string' ? '_append_html_data' : '_append_json_data'](obj, typeof d === 'string' ? $(d) : d));
				}, this));
			}
			if(typeof s === 'object') {
				if(s.url) {
					s = $.extend(true, {}, s);
					if($.isFunction(s.url)) {
						s.url = s.url.call(this, obj);
					}
					if($.isFunction(s.data)) {
						s.data = s.data.call(this, obj);
					}
					return $.ajax(s)
						.done($.proxy(function (d,t,x) {
								var type = x.getResponseHeader('Content-Type');
								if((type && type.indexOf('json') !== -1) || typeof d === "object") {
									return this._append_json_data(obj, d, function (status) { callback.call(this, status); });
									//return callback.call(this, this._append_json_data(obj, d));
								}
								if((type && type.indexOf('html') !== -1) || typeof d === "string") {
									return this._append_html_data(obj, $($.parseHTML(d)).filter(notTextOrCommentNode), function (status) { callback.call(this, status); });
									// return callback.call(this, this._append_html_data(obj, $(d)));
								}
								this._data.core.last_error = { 'error' : 'ajax', 'plugin' : 'core', 'id' : 'core_04', 'reason' : 'Could not load node', 'data' : JSON.stringify({ 'id' : obj.id, 'xhr' : x }) };
								this.settings.core.error.call(this, this._data.core.last_error);
								return callback.call(this, false);
							}, this))
						.fail($.proxy(function (f) {
								this._data.core.last_error = { 'error' : 'ajax', 'plugin' : 'core', 'id' : 'core_04', 'reason' : 'Could not load node', 'data' : JSON.stringify({ 'id' : obj.id, 'xhr' : f }) };
								callback.call(this, false);
								this.settings.core.error.call(this, this._data.core.last_error);
							}, this));
				}
				if ($.isArray(s)) {
					t = $.extend(true, [], s);
				} else if ($.isPlainObject(s)) {
					t = $.extend(true, {}, s);
				} else {
					t = s;
				}
				if(obj.id === $.jstree.root) {
					return this._append_json_data(obj, t, function (status) {
						callback.call(this, status);
					});
				}
				else {
					this._data.core.last_error = { 'error' : 'nodata', 'plugin' : 'core', 'id' : 'core_05', 'reason' : 'Could not load node', 'data' : JSON.stringify({ 'id' : obj.id }) };
					this.settings.core.error.call(this, this._data.core.last_error);
					return callback.call(this, false);
				}
				//return callback.call(this, (obj.id === $.jstree.root ? this._append_json_data(obj, t) : false) );
			}
			if(typeof s === 'string') {
				if(obj.id === $.jstree.root) {
					return this._append_html_data(obj, $($.parseHTML(s)).filter(notTextOrCommentNode), function (status) {
						callback.call(this, status);
					});
				}
				else {
					this._data.core.last_error = { 'error' : 'nodata', 'plugin' : 'core', 'id' : 'core_06', 'reason' : 'Could not load node', 'data' : JSON.stringify({ 'id' : obj.id }) };
					this.settings.core.error.call(this, this._data.core.last_error);
					return callback.call(this, false);
				}
				//return callback.call(this, (obj.id === $.jstree.root ? this._append_html_data(obj, $(s)) : false) );
			}
			return callback.call(this, false);
		},
		/**
		 * adds a node to the list of nodes to redraw. Used only internally.
		 * @private
		 * @name _node_changed(obj [, callback])
		 * @param  {mixed} obj
		 */
		_node_changed : function (obj) {
			obj = this.get_node(obj);
      if (obj && $.inArray(obj.id, this._model.changed) === -1) {
				this._model.changed.push(obj.id);
			}
		},
		/**
		 * appends HTML content to the tree. Used internally.
		 * @private
		 * @name _append_html_data(obj, data)
		 * @param  {mixed} obj the node to append to
		 * @param  {String} data the HTML string to parse and append
		 * @trigger model.jstree, changed.jstree
		 */
		_append_html_data : function (dom, data, cb) {
			dom = this.get_node(dom);
			dom.children = [];
			dom.children_d = [];
			var dat = data.is('ul') ? data.children() : data,
				par = dom.id,
				chd = [],
				dpc = [],
				m = this._model.data,
				p = m[par],
				s = this._data.core.selected.length,
				tmp, i, j;
			dat.each($.proxy(function (i, v) {
				tmp = this._parse_model_from_html($(v), par, p.parents.concat());
				if(tmp) {
					chd.push(tmp);
					dpc.push(tmp);
					if(m[tmp].children_d.length) {
						dpc = dpc.concat(m[tmp].children_d);
					}
				}
			}, this));
			p.children = chd;
			p.children_d = dpc;
			for(i = 0, j = p.parents.length; i < j; i++) {
				m[p.parents[i]].children_d = m[p.parents[i]].children_d.concat(dpc);
			}
			/**
			 * triggered when new data is inserted to the tree model
			 * @event
			 * @name model.jstree
			 * @param {Array} nodes an array of node IDs
			 * @param {String} parent the parent ID of the nodes
			 */
			this.trigger('model', { "nodes" : dpc, 'parent' : par });
			if(par !== $.jstree.root) {
				this._node_changed(par);
				this.redraw();
			}
			else {
				this.get_container_ul().children('.jstree-initial-node').remove();
				this.redraw(true);
			}
			if(this._data.core.selected.length !== s) {
				this.trigger('changed', { 'action' : 'model', 'selected' : this._data.core.selected });
			}
			cb.call(this, true);
		},
		/**
		 * appends JSON content to the tree. Used internally.
		 * @private
		 * @name _append_json_data(obj, data)
		 * @param  {mixed} obj the node to append to
		 * @param  {String} data the JSON object to parse and append
		 * @param  {Boolean} force_processing internal param - do not set
		 * @trigger model.jstree, changed.jstree
		 */
		_append_json_data : function (dom, data, cb, force_processing) {
			if(this.element === null) { return; }
			dom = this.get_node(dom);
			dom.children = [];
			dom.children_d = [];
			// *%$@!!!
			if(data.d) {
				data = data.d;
				if(typeof data === "string") {
					data = JSON.parse(data);
				}
			}
			if(!$.isArray(data)) { data = [data]; }
			var w = null,
				args = {
					'df'	: this._model.default_state,
					'dat'	: data,
					'par'	: dom.id,
					'm'		: this._model.data,
					't_id'	: this._id,
					't_cnt'	: this._cnt,
					'sel'	: this._data.core.selected
				},
				func = function (data, undefined) {
					if(data.data) { data = data.data; }
					var dat = data.dat,
						par = data.par,
						chd = [],
						dpc = [],
						add = [],
						df = data.df,
						t_id = data.t_id,
						t_cnt = data.t_cnt,
						m = data.m,
						p = m[par],
						sel = data.sel,
						tmp, i, j, rslt,
						parse_flat = function (d, p, ps) {
							if(!ps) { ps = []; }
							else { ps = ps.concat(); }
							if(p) { ps.unshift(p); }
							var tid = d.id.toString(),
								i, j, c, e,
								tmp = {
									id			: tid,
									text		: d.text || '',
									icon		: d.icon !== undefined ? d.icon : true,
									parent		: p,
									parents		: ps,
									children	: d.children || [],
									children_d	: d.children_d || [],
									data		: d.data,
									state		: { },
									li_attr		: { id : false },
									a_attr		: { href : '#' },
									original	: false
								};
							for(i in df) {
								if(df.hasOwnProperty(i)) {
									tmp.state[i] = df[i];
								}
							}
							if(d && d.data && d.data.jstree && d.data.jstree.icon) {
								tmp.icon = d.data.jstree.icon;
							}
							if(tmp.icon === undefined || tmp.icon === null || tmp.icon === "") {
								tmp.icon = true;
							}
							if(d && d.data) {
								tmp.data = d.data;
								if(d.data.jstree) {
									for(i in d.data.jstree) {
										if(d.data.jstree.hasOwnProperty(i)) {
											tmp.state[i] = d.data.jstree[i];
										}
									}
								}
							}
							if(d && typeof d.state === 'object') {
								for (i in d.state) {
									if(d.state.hasOwnProperty(i)) {
										tmp.state[i] = d.state[i];
									}
								}
							}
							if(d && typeof d.li_attr === 'object') {
								for (i in d.li_attr) {
									if(d.li_attr.hasOwnProperty(i)) {
										tmp.li_attr[i] = d.li_attr[i];
									}
								}
							}
							if(!tmp.li_attr.id) {
								tmp.li_attr.id = tid;
							}
							if(d && typeof d.a_attr === 'object') {
								for (i in d.a_attr) {
									if(d.a_attr.hasOwnProperty(i)) {
										tmp.a_attr[i] = d.a_attr[i];
									}
								}
							}
							if(d && d.children && d.children === true) {
								tmp.state.loaded = false;
								tmp.children = [];
								tmp.children_d = [];
							}
							m[tmp.id] = tmp;
							for(i = 0, j = tmp.children.length; i < j; i++) {
								c = parse_flat(m[tmp.children[i]], tmp.id, ps);
								e = m[c];
								tmp.children_d.push(c);
								if(e.children_d.length) {
									tmp.children_d = tmp.children_d.concat(e.children_d);
								}
							}
							delete d.data;
							delete d.children;
							m[tmp.id].original = d;
							if(tmp.state.selected) {
								add.push(tmp.id);
							}
							return tmp.id;
						},
						parse_nest = function (d, p, ps) {
							if(!ps) { ps = []; }
							else { ps = ps.concat(); }
							if(p) { ps.unshift(p); }
							var tid = false, i, j, c, e, tmp;
							do {
								tid = 'j' + t_id + '_' + (++t_cnt);
							} while(m[tid]);

							tmp = {
								id			: false,
								text		: typeof d === 'string' ? d : '',
								icon		: typeof d === 'object' && d.icon !== undefined ? d.icon : true,
								parent		: p,
								parents		: ps,
								children	: [],
								children_d	: [],
								data		: null,
								state		: { },
								li_attr		: { id : false },
								a_attr		: { href : '#' },
								original	: false
							};
							for(i in df) {
								if(df.hasOwnProperty(i)) {
									tmp.state[i] = df[i];
								}
							}
							if(d && d.id) { tmp.id = d.id.toString(); }
							if(d && d.text) { tmp.text = d.text; }
							if(d && d.data && d.data.jstree && d.data.jstree.icon) {
								tmp.icon = d.data.jstree.icon;
							}
							if(tmp.icon === undefined || tmp.icon === null || tmp.icon === "") {
								tmp.icon = true;
							}
							if(d && d.data) {
								tmp.data = d.data;
								if(d.data.jstree) {
									for(i in d.data.jstree) {
										if(d.data.jstree.hasOwnProperty(i)) {
											tmp.state[i] = d.data.jstree[i];
										}
									}
								}
							}
							if(d && typeof d.state === 'object') {
								for (i in d.state) {
									if(d.state.hasOwnProperty(i)) {
										tmp.state[i] = d.state[i];
									}
								}
							}
							if(d && typeof d.li_attr === 'object') {
								for (i in d.li_attr) {
									if(d.li_attr.hasOwnProperty(i)) {
										tmp.li_attr[i] = d.li_attr[i];
									}
								}
							}
							if(tmp.li_attr.id && !tmp.id) {
								tmp.id = tmp.li_attr.id.toString();
							}
							if(!tmp.id) {
								tmp.id = tid;
							}
							if(!tmp.li_attr.id) {
								tmp.li_attr.id = tmp.id;
							}
							if(d && typeof d.a_attr === 'object') {
								for (i in d.a_attr) {
									if(d.a_attr.hasOwnProperty(i)) {
										tmp.a_attr[i] = d.a_attr[i];
									}
								}
							}
							if(d && d.children && d.children.length) {
								for(i = 0, j = d.children.length; i < j; i++) {
									c = parse_nest(d.children[i], tmp.id, ps);
									e = m[c];
									tmp.children.push(c);
									if(e.children_d.length) {
										tmp.children_d = tmp.children_d.concat(e.children_d);
									}
								}
								tmp.children_d = tmp.children_d.concat(tmp.children);
							}
							if(d && d.children && d.children === true) {
								tmp.state.loaded = false;
								tmp.children = [];
								tmp.children_d = [];
							}
							delete d.data;
							delete d.children;
							tmp.original = d;
							m[tmp.id] = tmp;
							if(tmp.state.selected) {
								add.push(tmp.id);
							}
							return tmp.id;
						};

					if(dat.length && dat[0].id !== undefined && dat[0].parent !== undefined) {
						// Flat JSON support (for easy import from DB):
						// 1) convert to object (foreach)
						for(i = 0, j = dat.length; i < j; i++) {
							if(!dat[i].children) {
								dat[i].children = [];
							}
							if(!dat[i].state) {
								dat[i].state = {};
							}
							m[dat[i].id.toString()] = dat[i];
						}
						// 2) populate children (foreach)
						for(i = 0, j = dat.length; i < j; i++) {
							if (!m[dat[i].parent.toString()]) {
								this._data.core.last_error = { 'error' : 'parse', 'plugin' : 'core', 'id' : 'core_07', 'reason' : 'Node with invalid parent', 'data' : JSON.stringify({ 'id' : dat[i].id.toString(), 'parent' : dat[i].parent.toString() }) };
								this.settings.core.error.call(this, this._data.core.last_error);
								continue;
							}

							m[dat[i].parent.toString()].children.push(dat[i].id.toString());
							// populate parent.children_d
							p.children_d.push(dat[i].id.toString());
						}
						// 3) normalize && populate parents and children_d with recursion
						for(i = 0, j = p.children.length; i < j; i++) {
							tmp = parse_flat(m[p.children[i]], par, p.parents.concat());
							dpc.push(tmp);
							if(m[tmp].children_d.length) {
								dpc = dpc.concat(m[tmp].children_d);
							}
						}
						for(i = 0, j = p.parents.length; i < j; i++) {
							m[p.parents[i]].children_d = m[p.parents[i]].children_d.concat(dpc);
						}
						// ?) three_state selection - p.state.selected && t - (if three_state foreach(dat => ch) -> foreach(parents) if(parent.selected) child.selected = true;
						rslt = {
							'cnt' : t_cnt,
							'mod' : m,
							'sel' : sel,
							'par' : par,
							'dpc' : dpc,
							'add' : add
						};
					}
					else {
						for(i = 0, j = dat.length; i < j; i++) {
							tmp = parse_nest(dat[i], par, p.parents.concat());
							if(tmp) {
								chd.push(tmp);
								dpc.push(tmp);
								if(m[tmp].children_d.length) {
									dpc = dpc.concat(m[tmp].children_d);
								}
							}
						}
						p.children = chd;
						p.children_d = dpc;
						for(i = 0, j = p.parents.length; i < j; i++) {
							m[p.parents[i]].children_d = m[p.parents[i]].children_d.concat(dpc);
						}
						rslt = {
							'cnt' : t_cnt,
							'mod' : m,
							'sel' : sel,
							'par' : par,
							'dpc' : dpc,
							'add' : add
						};
					}
					if(typeof window === 'undefined' || typeof window.document === 'undefined') {
						postMessage(rslt);
					}
					else {
						return rslt;
					}
				},
				rslt = function (rslt, worker) {
					if(this.element === null) { return; }
					this._cnt = rslt.cnt;
					var i, m = this._model.data;
					for (i in m) {
						if (m.hasOwnProperty(i) && m[i].state && m[i].state.loading && rslt.mod[i]) {
							rslt.mod[i].state.loading = true;
						}
					}
					this._model.data = rslt.mod; // breaks the reference in load_node - careful

					if(worker) {
						var j, a = rslt.add, r = rslt.sel, s = this._data.core.selected.slice();
						m = this._model.data;
						// if selection was changed while calculating in worker
						if(r.length !== s.length || $.vakata.array_unique(r.concat(s)).length !== r.length) {
							// deselect nodes that are no longer selected
							for(i = 0, j = r.length; i < j; i++) {
								if($.inArray(r[i], a) === -1 && $.inArray(r[i], s) === -1) {
									m[r[i]].state.selected = false;
								}
							}
							// select nodes that were selected in the mean time
							for(i = 0, j = s.length; i < j; i++) {
								if($.inArray(s[i], r) === -1) {
									m[s[i]].state.selected = true;
								}
							}
						}
					}
					if(rslt.add.length) {
						this._data.core.selected = this._data.core.selected.concat(rslt.add);
					}

					this.trigger('model', { "nodes" : rslt.dpc, 'parent' : rslt.par });

					if(rslt.par !== $.jstree.root) {
						this._node_changed(rslt.par);
						this.redraw();
					}
					else {
						// this.get_container_ul().children('.jstree-initial-node').remove();
						this.redraw(true);
					}
					if(rslt.add.length) {
						this.trigger('changed', { 'action' : 'model', 'selected' : this._data.core.selected });
					}
					cb.call(this, true);
				};
			if(this.settings.core.worker && window.Blob && window.URL && window.Worker) {
				try {
					if(this._wrk === null) {
						this._wrk = window.URL.createObjectURL(
							new window.Blob(
								['self.onmessage = ' + func.toString()],
								{type:"text/javascript"}
							)
						);
					}
					if(!this._data.core.working || force_processing) {
						this._data.core.working = true;
						w = new window.Worker(this._wrk);
						w.onmessage = $.proxy(function (e) {
							rslt.call(this, e.data, true);
							try { w.terminate(); w = null; } catch(ignore) { }
							if(this._data.core.worker_queue.length) {
								this._append_json_data.apply(this, this._data.core.worker_queue.shift());
							}
							else {
								this._data.core.working = false;
							}
						}, this);
						if(!args.par) {
							if(this._data.core.worker_queue.length) {
								this._append_json_data.apply(this, this._data.core.worker_queue.shift());
							}
							else {
								this._data.core.working = false;
							}
						}
						else {
							w.postMessage(args);
						}
					}
					else {
						this._data.core.worker_queue.push([dom, data, cb, true]);
					}
				}
				catch(e) {
					rslt.call(this, func(args), false);
					if(this._data.core.worker_queue.length) {
						this._append_json_data.apply(this, this._data.core.worker_queue.shift());
					}
					else {
						this._data.core.working = false;
					}
				}
			}
			else {
				rslt.call(this, func(args), false);
			}
		},
		/**
		 * parses a node from a jQuery object and appends them to the in memory tree model. Used internally.
		 * @private
		 * @name _parse_model_from_html(d [, p, ps])
		 * @param  {jQuery} d the jQuery object to parse
		 * @param  {String} p the parent ID
		 * @param  {Array} ps list of all parents
		 * @return {String} the ID of the object added to the model
		 */
		_parse_model_from_html : function (d, p, ps) {
			if(!ps) { ps = []; }
			else { ps = [].concat(ps); }
			if(p) { ps.unshift(p); }
			var c, e, m = this._model.data,
				data = {
					id			: false,
					text		: false,
					icon		: true,
					parent		: p,
					parents		: ps,
					children	: [],
					children_d	: [],
					data		: null,
					state		: { },
					li_attr		: { id : false },
					a_attr		: { href : '#' },
					original	: false
				}, i, tmp, tid;
			for(i in this._model.default_state) {
				if(this._model.default_state.hasOwnProperty(i)) {
					data.state[i] = this._model.default_state[i];
				}
			}
			tmp = $.vakata.attributes(d, true);
			$.each(tmp, function (i, v) {
				v = $.trim(v);
				if(!v.length) { return true; }
				data.li_attr[i] = v;
				if(i === 'id') {
					data.id = v.toString();
				}
			});
			tmp = d.children('a').first();
			if(tmp.length) {
				tmp = $.vakata.attributes(tmp, true);
				$.each(tmp, function (i, v) {
					v = $.trim(v);
					if(v.length) {
						data.a_attr[i] = v;
					}
				});
			}
			tmp = d.children("a").first().length ? d.children("a").first().clone() : d.clone();
			tmp.children("ins, i, ul").remove();
			tmp = tmp.html();
			tmp = $('<div />').html(tmp);
			data.text = this.settings.core.force_text ? tmp.text() : tmp.html();
			tmp = d.data();
			data.data = tmp ? $.extend(true, {}, tmp) : null;
			data.state.opened = d.hasClass('jstree-open');
			data.state.selected = d.children('a').hasClass('jstree-clicked');
			data.state.disabled = d.children('a').hasClass('jstree-disabled');
			if(data.data && data.data.jstree) {
				for(i in data.data.jstree) {
					if(data.data.jstree.hasOwnProperty(i)) {
						data.state[i] = data.data.jstree[i];
					}
				}
			}
			tmp = d.children("a").children(".jstree-themeicon");
			if(tmp.length) {
				data.icon = tmp.hasClass('jstree-themeicon-hidden') ? false : tmp.attr('rel');
			}
			if(data.state.icon !== undefined) {
				data.icon = data.state.icon;
			}
			if(data.icon === undefined || data.icon === null || data.icon === "") {
				data.icon = true;
			}
			tmp = d.children("ul").children("li");
			do {
				tid = 'j' + this._id + '_' + (++this._cnt);
			} while(m[tid]);
			data.id = data.li_attr.id ? data.li_attr.id.toString() : tid;
			if(tmp.length) {
				tmp.each($.proxy(function (i, v) {
					c = this._parse_model_from_html($(v), data.id, ps);
					e = this._model.data[c];
					data.children.push(c);
					if(e.children_d.length) {
						data.children_d = data.children_d.concat(e.children_d);
					}
				}, this));
				data.children_d = data.children_d.concat(data.children);
			}
			else {
				if(d.hasClass('jstree-closed')) {
					data.state.loaded = false;
				}
			}
			if(data.li_attr['class']) {
				data.li_attr['class'] = data.li_attr['class'].replace('jstree-closed','').replace('jstree-open','');
			}
			if(data.a_attr['class']) {
				data.a_attr['class'] = data.a_attr['class'].replace('jstree-clicked','').replace('jstree-disabled','');
			}
			m[data.id] = data;
			if(data.state.selected) {
				this._data.core.selected.push(data.id);
			}
			return data.id;
		},
		/**
		 * parses a node from a JSON object (used when dealing with flat data, which has no nesting of children, but has id and parent properties) and appends it to the in memory tree model. Used internally.
		 * @private
		 * @name _parse_model_from_flat_json(d [, p, ps])
		 * @param  {Object} d the JSON object to parse
		 * @param  {String} p the parent ID
		 * @param  {Array} ps list of all parents
		 * @return {String} the ID of the object added to the model
		 */
		_parse_model_from_flat_json : function (d, p, ps) {
			if(!ps) { ps = []; }
			else { ps = ps.concat(); }
			if(p) { ps.unshift(p); }
			var tid = d.id.toString(),
				m = this._model.data,
				df = this._model.default_state,
				i, j, c, e,
				tmp = {
					id			: tid,
					text		: d.text || '',
					icon		: d.icon !== undefined ? d.icon : true,
					parent		: p,
					parents		: ps,
					children	: d.children || [],
					children_d	: d.children_d || [],
					data		: d.data,
					state		: { },
					li_attr		: { id : false },
					a_attr		: { href : '#' },
					original	: false
				};
			for(i in df) {
				if(df.hasOwnProperty(i)) {
					tmp.state[i] = df[i];
				}
			}
			if(d && d.data && d.data.jstree && d.data.jstree.icon) {
				tmp.icon = d.data.jstree.icon;
			}
			if(tmp.icon === undefined || tmp.icon === null || tmp.icon === "") {
				tmp.icon = true;
			}
			if(d && d.data) {
				tmp.data = d.data;
				if(d.data.jstree) {
					for(i in d.data.jstree) {
						if(d.data.jstree.hasOwnProperty(i)) {
							tmp.state[i] = d.data.jstree[i];
						}
					}
				}
			}
			if(d && typeof d.state === 'object') {
				for (i in d.state) {
					if(d.state.hasOwnProperty(i)) {
						tmp.state[i] = d.state[i];
					}
				}
			}
			if(d && typeof d.li_attr === 'object') {
				for (i in d.li_attr) {
					if(d.li_attr.hasOwnProperty(i)) {
						tmp.li_attr[i] = d.li_attr[i];
					}
				}
			}
			if(!tmp.li_attr.id) {
				tmp.li_attr.id = tid;
			}
			if(d && typeof d.a_attr === 'object') {
				for (i in d.a_attr) {
					if(d.a_attr.hasOwnProperty(i)) {
						tmp.a_attr[i] = d.a_attr[i];
					}
				}
			}
			if(d && d.children && d.children === true) {
				tmp.state.loaded = false;
				tmp.children = [];
				tmp.children_d = [];
			}
			m[tmp.id] = tmp;
			for(i = 0, j = tmp.children.length; i < j; i++) {
				c = this._parse_model_from_flat_json(m[tmp.children[i]], tmp.id, ps);
				e = m[c];
				tmp.children_d.push(c);
				if(e.children_d.length) {
					tmp.children_d = tmp.children_d.concat(e.children_d);
				}
			}
			delete d.data;
			delete d.children;
			m[tmp.id].original = d;
			if(tmp.state.selected) {
				this._data.core.selected.push(tmp.id);
			}
			return tmp.id;
		},
		/**
		 * parses a node from a JSON object and appends it to the in memory tree model. Used internally.
		 * @private
		 * @name _parse_model_from_json(d [, p, ps])
		 * @param  {Object} d the JSON object to parse
		 * @param  {String} p the parent ID
		 * @param  {Array} ps list of all parents
		 * @return {String} the ID of the object added to the model
		 */
		_parse_model_from_json : function (d, p, ps) {
			if(!ps) { ps = []; }
			else { ps = ps.concat(); }
			if(p) { ps.unshift(p); }
			var tid = false, i, j, c, e, m = this._model.data, df = this._model.default_state, tmp;
			do {
				tid = 'j' + this._id + '_' + (++this._cnt);
			} while(m[tid]);

			tmp = {
				id			: false,
				text		: typeof d === 'string' ? d : '',
				icon		: typeof d === 'object' && d.icon !== undefined ? d.icon : true,
				parent		: p,
				parents		: ps,
				children	: [],
				children_d	: [],
				data		: null,
				state		: { },
				li_attr		: { id : false },
				a_attr		: { href : '#' },
				original	: false
			};
			for(i in df) {
				if(df.hasOwnProperty(i)) {
					tmp.state[i] = df[i];
				}
			}
			if(d && d.id) { tmp.id = d.id.toString(); }
			if(d && d.text) { tmp.text = d.text; }
			if(d && d.data && d.data.jstree && d.data.jstree.icon) {
				tmp.icon = d.data.jstree.icon;
			}
			if(tmp.icon === undefined || tmp.icon === null || tmp.icon === "") {
				tmp.icon = true;
			}
			if(d && d.data) {
				tmp.data = d.data;
				if(d.data.jstree) {
					for(i in d.data.jstree) {
						if(d.data.jstree.hasOwnProperty(i)) {
							tmp.state[i] = d.data.jstree[i];
						}
					}
				}
			}
			if(d && typeof d.state === 'object') {
				for (i in d.state) {
					if(d.state.hasOwnProperty(i)) {
						tmp.state[i] = d.state[i];
					}
				}
			}
			if(d && typeof d.li_attr === 'object') {
				for (i in d.li_attr) {
					if(d.li_attr.hasOwnProperty(i)) {
						tmp.li_attr[i] = d.li_attr[i];
					}
				}
			}
			if(tmp.li_attr.id && !tmp.id) {
				tmp.id = tmp.li_attr.id.toString();
			}
			if(!tmp.id) {
				tmp.id = tid;
			}
			if(!tmp.li_attr.id) {
				tmp.li_attr.id = tmp.id;
			}
			if(d && typeof d.a_attr === 'object') {
				for (i in d.a_attr) {
					if(d.a_attr.hasOwnProperty(i)) {
						tmp.a_attr[i] = d.a_attr[i];
					}
				}
			}
			if(d && d.children && d.children.length) {
				for(i = 0, j = d.children.length; i < j; i++) {
					c = this._parse_model_from_json(d.children[i], tmp.id, ps);
					e = m[c];
					tmp.children.push(c);
					if(e.children_d.length) {
						tmp.children_d = tmp.children_d.concat(e.children_d);
					}
				}
				tmp.children_d = tmp.children_d.concat(tmp.children);
			}
			if(d && d.children && d.children === true) {
				tmp.state.loaded = false;
				tmp.children = [];
				tmp.children_d = [];
			}
			delete d.data;
			delete d.children;
			tmp.original = d;
			m[tmp.id] = tmp;
			if(tmp.state.selected) {
				this._data.core.selected.push(tmp.id);
			}
			return tmp.id;
		},
		/**
		 * redraws all nodes that need to be redrawn. Used internally.
		 * @private
		 * @name _redraw()
		 * @trigger redraw.jstree
		 */
		_redraw : function () {
			var nodes = this._model.force_full_redraw ? this._model.data[$.jstree.root].children.concat([]) : this._model.changed.concat([]),
				f = document.createElement('UL'), tmp, i, j, fe = this._data.core.focused;
			for(i = 0, j = nodes.length; i < j; i++) {
				tmp = this.redraw_node(nodes[i], true, this._model.force_full_redraw);
				if(tmp && this._model.force_full_redraw) {
					f.appendChild(tmp);
				}
			}
			if(this._model.force_full_redraw) {
				f.className = this.get_container_ul()[0].className;
				f.setAttribute('role','group');
				this.element.empty().append(f);
				//this.get_container_ul()[0].appendChild(f);
			}
			if(fe !== null && this.settings.core.restore_focus) {
				tmp = this.get_node(fe, true);
				if(tmp && tmp.length && tmp.children('.jstree-anchor')[0] !== document.activeElement) {
					tmp.children('.jstree-anchor').focus();
				}
				else {
					this._data.core.focused = null;
				}
			}
			this._model.force_full_redraw = false;
			this._model.changed = [];
			/**
			 * triggered after nodes are redrawn
			 * @event
			 * @name redraw.jstree
			 * @param {array} nodes the redrawn nodes
			 */
			this.trigger('redraw', { "nodes" : nodes });
		},
		/**
		 * redraws all nodes that need to be redrawn or optionally - the whole tree
		 * @name redraw([full])
		 * @param {Boolean} full if set to `true` all nodes are redrawn.
		 */
		redraw : function (full) {
			if(full) {
				this._model.force_full_redraw = true;
			}
			//if(this._model.redraw_timeout) {
			//	clearTimeout(this._model.redraw_timeout);
			//}
			//this._model.redraw_timeout = setTimeout($.proxy(this._redraw, this),0);
			this._redraw();
		},
		/**
		 * redraws a single node's children. Used internally.
		 * @private
		 * @name draw_children(node)
		 * @param {mixed} node the node whose children will be redrawn
		 */
		draw_children : function (node) {
			var obj = this.get_node(node),
				i = false,
				j = false,
				k = false,
				d = document;
			if(!obj) { return false; }
			if(obj.id === $.jstree.root) { return this.redraw(true); }
			node = this.get_node(node, true);
			if(!node || !node.length) { return false; } // TODO: quick toggle

			node.children('.jstree-children').remove();
			node = node[0];
			if(obj.children.length && obj.state.loaded) {
				k = d.createElement('UL');
				k.setAttribute('role', 'group');
				k.className = 'jstree-children';
				for(i = 0, j = obj.children.length; i < j; i++) {
					k.appendChild(this.redraw_node(obj.children[i], true, true));
				}
				node.appendChild(k);
			}
		},
		/**
		 * redraws a single node. Used internally.
		 * @private
		 * @name redraw_node(node, deep, is_callback, force_render)
		 * @param {mixed} node the node to redraw
		 * @param {Boolean} deep should child nodes be redrawn too
		 * @param {Boolean} is_callback is this a recursion call
		 * @param {Boolean} force_render should children of closed parents be drawn anyway
		 */
		redraw_node : function (node, deep, is_callback, force_render) {
			var obj = this.get_node(node),
				par = false,
				ind = false,
				old = false,
				i = false,
				j = false,
				k = false,
				c = '',
				d = document,
				m = this._model.data,
				f = false,
				s = false,
				tmp = null,
				t = 0,
				l = 0,
				has_children = false,
				last_sibling = false;
			if(!obj) { return false; }
			if(obj.id === $.jstree.root) {  return this.redraw(true); }
			deep = deep || obj.children.length === 0;
			node = !document.querySelector ? document.getElementById(obj.id) : this.element[0].querySelector('#' + ("0123456789".indexOf(obj.id[0]) !== -1 ? '\\3' + obj.id[0] + ' ' + obj.id.substr(1).replace($.jstree.idregex,'\\$&') : obj.id.replace($.jstree.idregex,'\\$&')) ); //, this.element);
			if(!node) {
				deep = true;
				//node = d.createElement('LI');
				if(!is_callback) {
					par = obj.parent !== $.jstree.root ? $('#' + obj.parent.replace($.jstree.idregex,'\\$&'), this.element)[0] : null;
					if(par !== null && (!par || !m[obj.parent].state.opened)) {
						return false;
					}
					ind = $.inArray(obj.id, par === null ? m[$.jstree.root].children : m[obj.parent].children);
				}
			}
			else {
				node = $(node);
				if(!is_callback) {
					par = node.parent().parent()[0];
					if(par === this.element[0]) {
						par = null;
					}
					ind = node.index();
				}
				// m[obj.id].data = node.data(); // use only node's data, no need to touch jquery storage
				if(!deep && obj.children.length && !node.children('.jstree-children').length) {
					deep = true;
				}
				if(!deep) {
					old = node.children('.jstree-children')[0];
				}
				f = node.children('.jstree-anchor')[0] === document.activeElement;
				node.remove();
				//node = d.createElement('LI');
				//node = node[0];
			}
			node = this._data.core.node.cloneNode(true);
			// node is DOM, deep is boolean

			c = 'jstree-node ';
			for(i in obj.li_attr) {
				if(obj.li_attr.hasOwnProperty(i)) {
					if(i === 'id') { continue; }
					if(i !== 'class') {
						node.setAttribute(i, obj.li_attr[i]);
					}
					else {
						c += obj.li_attr[i];
					}
				}
			}
			if(!obj.a_attr.id) {
				obj.a_attr.id = obj.id + '_anchor';
			}
			node.setAttribute('aria-selected', !!obj.state.selected);
			node.setAttribute('aria-level', obj.parents.length);
			node.setAttribute('aria-labelledby', obj.a_attr.id);
			if(obj.state.disabled) {
				node.setAttribute('aria-disabled', true);
			}

			for(i = 0, j = obj.children.length; i < j; i++) {
				if(!m[obj.children[i]].state.hidden) {
					has_children = true;
					break;
				}
			}
			if(obj.parent !== null && m[obj.parent] && !obj.state.hidden) {
				i = $.inArray(obj.id, m[obj.parent].children);
				last_sibling = obj.id;
				if(i !== -1) {
					i++;
					for(j = m[obj.parent].children.length; i < j; i++) {
						if(!m[m[obj.parent].children[i]].state.hidden) {
							last_sibling = m[obj.parent].children[i];
						}
						if(last_sibling !== obj.id) {
							break;
						}
					}
				}
			}

			if(obj.state.hidden) {
				c += ' jstree-hidden';
			}
			if (obj.state.loading) {
				c += ' jstree-loading';
			}
			if(obj.state.loaded && !has_children) {
				c += ' jstree-leaf';
			}
			else {
				c += obj.state.opened && obj.state.loaded ? ' jstree-open' : ' jstree-closed';
				node.setAttribute('aria-expanded', (obj.state.opened && obj.state.loaded) );
			}
			if(last_sibling === obj.id) {
				c += ' jstree-last';
			}
			node.id = obj.id;
			node.className = c;
			c = ( obj.state.selected ? ' jstree-clicked' : '') + ( obj.state.disabled ? ' jstree-disabled' : '');
			for(j in obj.a_attr) {
				if(obj.a_attr.hasOwnProperty(j)) {
					if(j === 'href' && obj.a_attr[j] === '#') { continue; }
					if(j !== 'class') {
						node.childNodes[1].setAttribute(j, obj.a_attr[j]);
					}
					else {
						c += ' ' + obj.a_attr[j];
					}
				}
			}
			if(c.length) {
				node.childNodes[1].className = 'jstree-anchor ' + c;
			}
			if((obj.icon && obj.icon !== true) || obj.icon === false) {
				if(obj.icon === false) {
					node.childNodes[1].childNodes[0].className += ' jstree-themeicon-hidden';
				}
				else if(obj.icon.indexOf('/') === -1 && obj.icon.indexOf('.') === -1) {
					node.childNodes[1].childNodes[0].className += ' ' + obj.icon + ' jstree-themeicon-custom';
				}
				else {
					node.childNodes[1].childNodes[0].style.backgroundImage = 'url("'+obj.icon+'")';
					node.childNodes[1].childNodes[0].style.backgroundPosition = 'center center';
					node.childNodes[1].childNodes[0].style.backgroundSize = 'auto';
					node.childNodes[1].childNodes[0].className += ' jstree-themeicon-custom';
				}
			}

			if(this.settings.core.force_text) {
				node.childNodes[1].appendChild(d.createTextNode(obj.text));
			}
			else {
				node.childNodes[1].innerHTML += obj.text;
			}


			if(deep && obj.children.length && (obj.state.opened || force_render) && obj.state.loaded) {
				k = d.createElement('UL');
				k.setAttribute('role', 'group');
				k.className = 'jstree-children';
				for(i = 0, j = obj.children.length; i < j; i++) {
					k.appendChild(this.redraw_node(obj.children[i], deep, true));
				}
				node.appendChild(k);
			}
			if(old) {
				node.appendChild(old);
			}
			if(!is_callback) {
				// append back using par / ind
				if(!par) {
					par = this.element[0];
				}
				for(i = 0, j = par.childNodes.length; i < j; i++) {
					if(par.childNodes[i] && par.childNodes[i].className && par.childNodes[i].className.indexOf('jstree-children') !== -1) {
						tmp = par.childNodes[i];
						break;
					}
				}
				if(!tmp) {
					tmp = d.createElement('UL');
					tmp.setAttribute('role', 'group');
					tmp.className = 'jstree-children';
					par.appendChild(tmp);
				}
				par = tmp;

				if(ind < par.childNodes.length) {
					par.insertBefore(node, par.childNodes[ind]);
				}
				else {
					par.appendChild(node);
				}
				if(f) {
					t = this.element[0].scrollTop;
					l = this.element[0].scrollLeft;
					node.childNodes[1].focus();
					this.element[0].scrollTop = t;
					this.element[0].scrollLeft = l;
				}
			}
			if(obj.state.opened && !obj.state.loaded) {
				obj.state.opened = false;
				setTimeout($.proxy(function () {
					this.open_node(obj.id, false, 0);
				}, this), 0);
			}
			return node;
		},
		/**
		 * opens a node, revealing its children. If the node is not loaded it will be loaded and opened once ready.
		 * @name open_node(obj [, callback, animation])
		 * @param {mixed} obj the node to open
		 * @param {Function} callback a function to execute once the node is opened
		 * @param {Number} animation the animation duration in milliseconds when opening the node (overrides the `core.animation` setting). Use `false` for no animation.
		 * @trigger open_node.jstree, after_open.jstree, before_open.jstree
		 */
		open_node : function (obj, callback, animation) {
			var t1, t2, d, t;
			if($.isArray(obj)) {
				obj = obj.slice();
				for(t1 = 0, t2 = obj.length; t1 < t2; t1++) {
					this.open_node(obj[t1], callback, animation);
				}
				return true;
			}
			obj = this.get_node(obj);
			if(!obj || obj.id === $.jstree.root) {
				return false;
			}
			animation = animation === undefined ? this.settings.core.animation : animation;
			if(!this.is_closed(obj)) {
				if(callback) {
					callback.call(this, obj, false);
				}
				return false;
			}
			if(!this.is_loaded(obj)) {
				if(this.is_loading(obj)) {
					return setTimeout($.proxy(function () {
						this.open_node(obj, callback, animation);
					}, this), 500);
				}
				this.load_node(obj, function (o, ok) {
					return ok ? this.open_node(o, callback, animation) : (callback ? callback.call(this, o, false) : false);
				});
			}
			else {
				d = this.get_node(obj, true);
				t = this;
				if(d.length) {
					if(animation && d.children(".jstree-children").length) {
						d.children(".jstree-children").stop(true, true);
					}
					if(obj.children.length && !this._firstChild(d.children('.jstree-children')[0])) {
						this.draw_children(obj);
						//d = this.get_node(obj, true);
					}
					if(!animation) {
						this.trigger('before_open', { "node" : obj });
						d[0].className = d[0].className.replace('jstree-closed', 'jstree-open');
						d[0].setAttribute("aria-expanded", true);
					}
					else {
						this.trigger('before_open', { "node" : obj });
						d
							.children(".jstree-children").css("display","none").end()
							.removeClass("jstree-closed").addClass("jstree-open").attr("aria-expanded", true)
							.children(".jstree-children").stop(true, true)
								.slideDown(animation, function () {
									this.style.display = "";
									if (t.element) {
										t.trigger("after_open", { "node" : obj });
									}
								});
					}
				}
				obj.state.opened = true;
				if(callback) {
					callback.call(this, obj, true);
				}
				if(!d.length) {
					/**
					 * triggered when a node is about to be opened (if the node is supposed to be in the DOM, it will be, but it won't be visible yet)
					 * @event
					 * @name before_open.jstree
					 * @param {Object} node the opened node
					 */
					this.trigger('before_open', { "node" : obj });
				}
				/**
				 * triggered when a node is opened (if there is an animation it will not be completed yet)
				 * @event
				 * @name open_node.jstree
				 * @param {Object} node the opened node
				 */
				this.trigger('open_node', { "node" : obj });
				if(!animation || !d.length) {
					/**
					 * triggered when a node is opened and the animation is complete
					 * @event
					 * @name after_open.jstree
					 * @param {Object} node the opened node
					 */
					this.trigger("after_open", { "node" : obj });
				}
				return true;
			}
		},
		/**
		 * opens every parent of a node (node should be loaded)
		 * @name _open_to(obj)
		 * @param {mixed} obj the node to reveal
		 * @private
		 */
		_open_to : function (obj) {
			obj = this.get_node(obj);
			if(!obj || obj.id === $.jstree.root) {
				return false;
			}
			var i, j, p = obj.parents;
			for(i = 0, j = p.length; i < j; i+=1) {
				if(i !== $.jstree.root) {
					this.open_node(p[i], false, 0);
				}
			}
			return $('#' + obj.id.replace($.jstree.idregex,'\\$&'), this.element);
		},
		/**
		 * closes a node, hiding its children
		 * @name close_node(obj [, animation])
		 * @param {mixed} obj the node to close
		 * @param {Number} animation the animation duration in milliseconds when closing the node (overrides the `core.animation` setting). Use `false` for no animation.
		 * @trigger close_node.jstree, after_close.jstree
		 */
		close_node : function (obj, animation) {
			var t1, t2, t, d;
			if($.isArray(obj)) {
				obj = obj.slice();
				for(t1 = 0, t2 = obj.length; t1 < t2; t1++) {
					this.close_node(obj[t1], animation);
				}
				return true;
			}
			obj = this.get_node(obj);
			if(!obj || obj.id === $.jstree.root) {
				return false;
			}
			if(this.is_closed(obj)) {
				return false;
			}
			animation = animation === undefined ? this.settings.core.animation : animation;
			t = this;
			d = this.get_node(obj, true);

			obj.state.opened = false;
			/**
			 * triggered when a node is closed (if there is an animation it will not be complete yet)
			 * @event
			 * @name close_node.jstree
			 * @param {Object} node the closed node
			 */
			this.trigger('close_node',{ "node" : obj });
			if(!d.length) {
				/**
				 * triggered when a node is closed and the animation is complete
				 * @event
				 * @name after_close.jstree
				 * @param {Object} node the closed node
				 */
				this.trigger("after_close", { "node" : obj });
			}
			else {
				if(!animation) {
					d[0].className = d[0].className.replace('jstree-open', 'jstree-closed');
					d.attr("aria-expanded", false).children('.jstree-children').remove();
					this.trigger("after_close", { "node" : obj });
				}
				else {
					d
						.children(".jstree-children").attr("style","display:block !important").end()
						.removeClass("jstree-open").addClass("jstree-closed").attr("aria-expanded", false)
						.children(".jstree-children").stop(true, true).slideUp(animation, function () {
							this.style.display = "";
							d.children('.jstree-children').remove();
							if (t.element) {
								t.trigger("after_close", { "node" : obj });
							}
						});
				}
			}
		},
		/**
		 * toggles a node - closing it if it is open, opening it if it is closed
		 * @name toggle_node(obj)
		 * @param {mixed} obj the node to toggle
		 */
		toggle_node : function (obj) {
			var t1, t2;
			if($.isArray(obj)) {
				obj = obj.slice();
				for(t1 = 0, t2 = obj.length; t1 < t2; t1++) {
					this.toggle_node(obj[t1]);
				}
				return true;
			}
			if(this.is_closed(obj)) {
				return this.open_node(obj);
			}
			if(this.is_open(obj)) {
				return this.close_node(obj);
			}
		},
		/**
		 * opens all nodes within a node (or the tree), revealing their children. If the node is not loaded it will be loaded and opened once ready.
		 * @name open_all([obj, animation, original_obj])
		 * @param {mixed} obj the node to open recursively, omit to open all nodes in the tree
		 * @param {Number} animation the animation duration in milliseconds when opening the nodes, the default is no animation
		 * @param {jQuery} reference to the node that started the process (internal use)
		 * @trigger open_all.jstree
		 */
		open_all : function (obj, animation, original_obj) {
			if(!obj) { obj = $.jstree.root; }
			obj = this.get_node(obj);
			if(!obj) { return false; }
			var dom = obj.id === $.jstree.root ? this.get_container_ul() : this.get_node(obj, true), i, j, _this;
			if(!dom.length) {
				for(i = 0, j = obj.children_d.length; i < j; i++) {
					if(this.is_closed(this._model.data[obj.children_d[i]])) {
						this._model.data[obj.children_d[i]].state.opened = true;
					}
				}
				return this.trigger('open_all', { "node" : obj });
			}
			original_obj = original_obj || dom;
			_this = this;
			dom = this.is_closed(obj) ? dom.find('.jstree-closed').addBack() : dom.find('.jstree-closed');
			dom.each(function () {
				_this.open_node(
					this,
					function(node, status) { if(status && this.is_parent(node)) { this.open_all(node, animation, original_obj); } },
					animation || 0
				);
			});
			if(original_obj.find('.jstree-closed').length === 0) {
				/**
				 * triggered when an `open_all` call completes
				 * @event
				 * @name open_all.jstree
				 * @param {Object} node the opened node
				 */
				this.trigger('open_all', { "node" : this.get_node(original_obj) });
			}
		},
		/**
		 * closes all nodes within a node (or the tree), revealing their children
		 * @name close_all([obj, animation])
		 * @param {mixed} obj the node to close recursively, omit to close all nodes in the tree
		 * @param {Number} animation the animation duration in milliseconds when closing the nodes, the default is no animation
		 * @trigger close_all.jstree
		 */
		close_all : function (obj, animation) {
			if(!obj) { obj = $.jstree.root; }
			obj = this.get_node(obj);
			if(!obj) { return false; }
			var dom = obj.id === $.jstree.root ? this.get_container_ul() : this.get_node(obj, true),
				_this = this, i, j;
			if(dom.length) {
				dom = this.is_open(obj) ? dom.find('.jstree-open').addBack() : dom.find('.jstree-open');
				$(dom.get().reverse()).each(function () { _this.close_node(this, animation || 0); });
			}
			for(i = 0, j = obj.children_d.length; i < j; i++) {
				this._model.data[obj.children_d[i]].state.opened = false;
			}
			/**
			 * triggered when an `close_all` call completes
			 * @event
			 * @name close_all.jstree
			 * @param {Object} node the closed node
			 */
			this.trigger('close_all', { "node" : obj });
		},
		/**
		 * checks if a node is disabled (not selectable)
		 * @name is_disabled(obj)
		 * @param  {mixed} obj
		 * @return {Boolean}
		 */
		is_disabled : function (obj) {
			obj = this.get_node(obj);
			return obj && obj.state && obj.state.disabled;
		},
		/**
		 * enables a node - so that it can be selected
		 * @name enable_node(obj)
		 * @param {mixed} obj the node to enable
		 * @trigger enable_node.jstree
		 */
		enable_node : function (obj) {
			var t1, t2;
			if($.isArray(obj)) {
				obj = obj.slice();
				for(t1 = 0, t2 = obj.length; t1 < t2; t1++) {
					this.enable_node(obj[t1]);
				}
				return true;
			}
			obj = this.get_node(obj);
			if(!obj || obj.id === $.jstree.root) {
				return false;
			}
			obj.state.disabled = false;
			this.get_node(obj,true).children('.jstree-anchor').removeClass('jstree-disabled').attr('aria-disabled', false);
			/**
			 * triggered when an node is enabled
			 * @event
			 * @name enable_node.jstree
			 * @param {Object} node the enabled node
			 */
			this.trigger('enable_node', { 'node' : obj });
		},
		/**
		 * disables a node - so that it can not be selected
		 * @name disable_node(obj)
		 * @param {mixed} obj the node to disable
		 * @trigger disable_node.jstree
		 */
		disable_node : function (obj) {
			var t1, t2;
			if($.isArray(obj)) {
				obj = obj.slice();
				for(t1 = 0, t2 = obj.length; t1 < t2; t1++) {
					this.disable_node(obj[t1]);
				}
				return true;
			}
			obj = this.get_node(obj);
			if(!obj || obj.id === $.jstree.root) {
				return false;
			}
			obj.state.disabled = true;
			this.get_node(obj,true).children('.jstree-anchor').addClass('jstree-disabled').attr('aria-disabled', true);
			/**
			 * triggered when an node is disabled
			 * @event
			 * @name disable_node.jstree
			 * @param {Object} node the disabled node
			 */
			this.trigger('disable_node', { 'node' : obj });
		},
		/**
		 * determines if a node is hidden
		 * @name is_hidden(obj)
		 * @param {mixed} obj the node
		 */
		is_hidden : function (obj) {
			obj = this.get_node(obj);
			return obj.state.hidden === true;
		},
		/**
		 * hides a node - it is still in the structure but will not be visible
		 * @name hide_node(obj)
		 * @param {mixed} obj the node to hide
		 * @param {Boolean} skip_redraw internal parameter controlling if redraw is called
		 * @trigger hide_node.jstree
		 */
		hide_node : function (obj, skip_redraw) {
			var t1, t2;
			if($.isArray(obj)) {
				obj = obj.slice();
				for(t1 = 0, t2 = obj.length; t1 < t2; t1++) {
					this.hide_node(obj[t1], true);
				}
				if (!skip_redraw) {
					this.redraw();
				}
				return true;
			}
			obj = this.get_node(obj);
			if(!obj || obj.id === $.jstree.root) {
				return false;
			}
			if(!obj.state.hidden) {
				obj.state.hidden = true;
				this._node_changed(obj.parent);
				if(!skip_redraw) {
					this.redraw();
				}
				/**
				 * triggered when an node is hidden
				 * @event
				 * @name hide_node.jstree
				 * @param {Object} node the hidden node
				 */
				this.trigger('hide_node', { 'node' : obj });
			}
		},
		/**
		 * shows a node
		 * @name show_node(obj)
		 * @param {mixed} obj the node to show
		 * @param {Boolean} skip_redraw internal parameter controlling if redraw is called
		 * @trigger show_node.jstree
		 */
		show_node : function (obj, skip_redraw) {
			var t1, t2;
			if($.isArray(obj)) {
				obj = obj.slice();
				for(t1 = 0, t2 = obj.length; t1 < t2; t1++) {
					this.show_node(obj[t1], true);
				}
				if (!skip_redraw) {
					this.redraw();
				}
				return true;
			}
			obj = this.get_node(obj);
			if(!obj || obj.id === $.jstree.root) {
				return false;
			}
			if(obj.state.hidden) {
				obj.state.hidden = false;
				this._node_changed(obj.parent);
				if(!skip_redraw) {
					this.redraw();
				}
				/**
				 * triggered when an node is shown
				 * @event
				 * @name show_node.jstree
				 * @param {Object} node the shown node
				 */
				this.trigger('show_node', { 'node' : obj });
			}
		},
		/**
		 * hides all nodes
		 * @name hide_all()
		 * @trigger hide_all.jstree
		 */
		hide_all : function (skip_redraw) {
			var i, m = this._model.data, ids = [];
			for(i in m) {
				if(m.hasOwnProperty(i) && i !== $.jstree.root && !m[i].state.hidden) {
					m[i].state.hidden = true;
					ids.push(i);
				}
			}
			this._model.force_full_redraw = true;
			if(!skip_redraw) {
				this.redraw();
			}
			/**
			 * triggered when all nodes are hidden
			 * @event
			 * @name hide_all.jstree
			 * @param {Array} nodes the IDs of all hidden nodes
			 */
			this.trigger('hide_all', { 'nodes' : ids });
			return ids;
		},
		/**
		 * shows all nodes
		 * @name show_all()
		 * @trigger show_all.jstree
		 */
		show_all : function (skip_redraw) {
			var i, m = this._model.data, ids = [];
			for(i in m) {
				if(m.hasOwnProperty(i) && i !== $.jstree.root && m[i].state.hidden) {
					m[i].state.hidden = false;
					ids.push(i);
				}
			}
			this._model.force_full_redraw = true;
			if(!skip_redraw) {
				this.redraw();
			}
			/**
			 * triggered when all nodes are shown
			 * @event
			 * @name show_all.jstree
			 * @param {Array} nodes the IDs of all shown nodes
			 */
			this.trigger('show_all', { 'nodes' : ids });
			return ids;
		},
		/**
		 * called when a node is selected by the user. Used internally.
		 * @private
		 * @name activate_node(obj, e)
		 * @param {mixed} obj the node
		 * @param {Object} e the related event
		 * @trigger activate_node.jstree, changed.jstree
		 */
		activate_node : function (obj, e) {
			if(this.is_disabled(obj)) {
				return false;
			}
			if(!e || typeof e !== 'object') {
				e = {};
			}

			// ensure last_clicked is still in the DOM, make it fresh (maybe it was moved?) and make sure it is still selected, if not - make last_clicked the last selected node
			this._data.core.last_clicked = this._data.core.last_clicked && this._data.core.last_clicked.id !== undefined ? this.get_node(this._data.core.last_clicked.id) : null;
			if(this._data.core.last_clicked && !this._data.core.last_clicked.state.selected) { this._data.core.last_clicked = null; }
			if(!this._data.core.last_clicked && this._data.core.selected.length) { this._data.core.last_clicked = this.get_node(this._data.core.selected[this._data.core.selected.length - 1]); }

			if(!this.settings.core.multiple || (!e.metaKey && !e.ctrlKey && !e.shiftKey) || (e.shiftKey && (!this._data.core.last_clicked || !this.get_parent(obj) || this.get_parent(obj) !== this._data.core.last_clicked.parent ) )) {
				if(!this.settings.core.multiple && (e.metaKey || e.ctrlKey || e.shiftKey) && this.is_selected(obj)) {
					this.deselect_node(obj, false, e);
				}
				else {
					this.deselect_all(true);
					this.select_node(obj, false, false, e);
					this._data.core.last_clicked = this.get_node(obj);
				}
			}
			else {
				if(e.shiftKey) {
					var o = this.get_node(obj).id,
						l = this._data.core.last_clicked.id,
						p = this.get_node(this._data.core.last_clicked.parent).children,
						c = false,
						i, j;
					for(i = 0, j = p.length; i < j; i += 1) {
						// separate IFs work whem o and l are the same
						if(p[i] === o) {
							c = !c;
						}
						if(p[i] === l) {
							c = !c;
						}
						if(!this.is_disabled(p[i]) && (c || p[i] === o || p[i] === l)) {
							if (!this.is_hidden(p[i])) {
								this.select_node(p[i], true, false, e);
							}
						}
						else {
							this.deselect_node(p[i], true, e);
						}
					}
					this.trigger('changed', { 'action' : 'select_node', 'node' : this.get_node(obj), 'selected' : this._data.core.selected, 'event' : e });
				}
				else {
					if(!this.is_selected(obj)) {
						this.select_node(obj, false, false, e);
					}
					else {
						this.deselect_node(obj, false, e);
					}
				}
			}
			/**
			 * triggered when an node is clicked or intercated with by the user
			 * @event
			 * @name activate_node.jstree
			 * @param {Object} node
			 * @param {Object} event the ooriginal event (if any) which triggered the call (may be an empty object)
			 */
			this.trigger('activate_node', { 'node' : this.get_node(obj), 'event' : e });
		},
		/**
		 * applies the hover state on a node, called when a node is hovered by the user. Used internally.
		 * @private
		 * @name hover_node(obj)
		 * @param {mixed} obj
		 * @trigger hover_node.jstree
		 */
		hover_node : function (obj) {
			obj = this.get_node(obj, true);
			if(!obj || !obj.length || obj.children('.jstree-hovered').length) {
				return false;
			}
			var o = this.element.find('.jstree-hovered'), t = this.element;
			if(o && o.length) { this.dehover_node(o); }

			obj.children('.jstree-anchor').addClass('jstree-hovered');
			/**
			 * triggered when an node is hovered
			 * @event
			 * @name hover_node.jstree
			 * @param {Object} node
			 */
			this.trigger('hover_node', { 'node' : this.get_node(obj) });
			setTimeout(function () { t.attr('aria-activedescendant', obj[0].id); }, 0);
		},
		/**
		 * removes the hover state from a nodecalled when a node is no longer hovered by the user. Used internally.
		 * @private
		 * @name dehover_node(obj)
		 * @param {mixed} obj
		 * @trigger dehover_node.jstree
		 */
		dehover_node : function (obj) {
			obj = this.get_node(obj, true);
			if(!obj || !obj.length || !obj.children('.jstree-hovered').length) {
				return false;
			}
			obj.children('.jstree-anchor').removeClass('jstree-hovered');
			/**
			 * triggered when an node is no longer hovered
			 * @event
			 * @name dehover_node.jstree
			 * @param {Object} node
			 */
			this.trigger('dehover_node', { 'node' : this.get_node(obj) });
		},
		/**
		 * select a node
		 * @name select_node(obj [, supress_event, prevent_open])
		 * @param {mixed} obj an array can be used to select multiple nodes
		 * @param {Boolean} supress_event if set to `true` the `changed.jstree` event won't be triggered
		 * @param {Boolean} prevent_open if set to `true` parents of the selected node won't be opened
		 * @trigger select_node.jstree, changed.jstree
		 */
		select_node : function (obj, supress_event, prevent_open, e) {
			var dom, t1, t2, th;
			if($.isArray(obj)) {
				obj = obj.slice();
				for(t1 = 0, t2 = obj.length; t1 < t2; t1++) {
					this.select_node(obj[t1], supress_event, prevent_open, e);
				}
				return true;
			}
			obj = this.get_node(obj);
			if(!obj || obj.id === $.jstree.root) {
				return false;
			}
			dom = this.get_node(obj, true);
			if(!obj.state.selected) {
				obj.state.selected = true;
				this._data.core.selected.push(obj.id);
				if(!prevent_open) {
					dom = this._open_to(obj);
				}
				if(dom && dom.length) {
					dom.attr('aria-selected', true).children('.jstree-anchor').addClass('jstree-clicked');
				}
				/**
				 * triggered when an node is selected
				 * @event
				 * @name select_node.jstree
				 * @param {Object} node
				 * @param {Array} selected the current selection
				 * @param {Object} event the event (if any) that triggered this select_node
				 */
				this.trigger('select_node', { 'node' : obj, 'selected' : this._data.core.selected, 'event' : e });
				if(!supress_event) {
					/**
					 * triggered when selection changes
					 * @event
					 * @name changed.jstree
					 * @param {Object} node
					 * @param {Object} action the action that caused the selection to change
					 * @param {Array} selected the current selection
					 * @param {Object} event the event (if any) that triggered this changed event
					 */
					this.trigger('changed', { 'action' : 'select_node', 'node' : obj, 'selected' : this._data.core.selected, 'event' : e });
				}
			}
		},
		/**
		 * deselect a node
		 * @name deselect_node(obj [, supress_event])
		 * @param {mixed} obj an array can be used to deselect multiple nodes
		 * @param {Boolean} supress_event if set to `true` the `changed.jstree` event won't be triggered
		 * @trigger deselect_node.jstree, changed.jstree
		 */
		deselect_node : function (obj, supress_event, e) {
			var t1, t2, dom;
			if($.isArray(obj)) {
				obj = obj.slice();
				for(t1 = 0, t2 = obj.length; t1 < t2; t1++) {
					this.deselect_node(obj[t1], supress_event, e);
				}
				return true;
			}
			obj = this.get_node(obj);
			if(!obj || obj.id === $.jstree.root) {
				return false;
			}
			dom = this.get_node(obj, true);
			if(obj.state.selected) {
				obj.state.selected = false;
				this._data.core.selected = $.vakata.array_remove_item(this._data.core.selected, obj.id);
				if(dom.length) {
					dom.attr('aria-selected', false).children('.jstree-anchor').removeClass('jstree-clicked');
				}
				/**
				 * triggered when an node is deselected
				 * @event
				 * @name deselect_node.jstree
				 * @param {Object} node
				 * @param {Array} selected the current selection
				 * @param {Object} event the event (if any) that triggered this deselect_node
				 */
				this.trigger('deselect_node', { 'node' : obj, 'selected' : this._data.core.selected, 'event' : e });
				if(!supress_event) {
					this.trigger('changed', { 'action' : 'deselect_node', 'node' : obj, 'selected' : this._data.core.selected, 'event' : e });
				}
			}
		},
		/**
		 * select all nodes in the tree
		 * @name select_all([supress_event])
		 * @param {Boolean} supress_event if set to `true` the `changed.jstree` event won't be triggered
		 * @trigger select_all.jstree, changed.jstree
		 */
		select_all : function (supress_event) {
			var tmp = this._data.core.selected.concat([]), i, j;
			this._data.core.selected = this._model.data[$.jstree.root].children_d.concat();
			for(i = 0, j = this._data.core.selected.length; i < j; i++) {
				if(this._model.data[this._data.core.selected[i]]) {
					this._model.data[this._data.core.selected[i]].state.selected = true;
				}
			}
			this.redraw(true);
			/**
			 * triggered when all nodes are selected
			 * @event
			 * @name select_all.jstree
			 * @param {Array} selected the current selection
			 */
			this.trigger('select_all', { 'selected' : this._data.core.selected });
			if(!supress_event) {
				this.trigger('changed', { 'action' : 'select_all', 'selected' : this._data.core.selected, 'old_selection' : tmp });
			}
		},
		/**
		 * deselect all selected nodes
		 * @name deselect_all([supress_event])
		 * @param {Boolean} supress_event if set to `true` the `changed.jstree` event won't be triggered
		 * @trigger deselect_all.jstree, changed.jstree
		 */
		deselect_all : function (supress_event) {
			var tmp = this._data.core.selected.concat([]), i, j;
			for(i = 0, j = this._data.core.selected.length; i < j; i++) {
				if(this._model.data[this._data.core.selected[i]]) {
					this._model.data[this._data.core.selected[i]].state.selected = false;
				}
			}
			this._data.core.selected = [];
			this.element.find('.jstree-clicked').removeClass('jstree-clicked').parent().attr('aria-selected', false);
			/**
			 * triggered when all nodes are deselected
			 * @event
			 * @name deselect_all.jstree
			 * @param {Object} node the previous selection
			 * @param {Array} selected the current selection
			 */
			this.trigger('deselect_all', { 'selected' : this._data.core.selected, 'node' : tmp });
			if(!supress_event) {
				this.trigger('changed', { 'action' : 'deselect_all', 'selected' : this._data.core.selected, 'old_selection' : tmp });
			}
		},
		/**
		 * checks if a node is selected
		 * @name is_selected(obj)
		 * @param  {mixed}  obj
		 * @return {Boolean}
		 */
		is_selected : function (obj) {
			obj = this.get_node(obj);
			if(!obj || obj.id === $.jstree.root) {
				return false;
			}
			return obj.state.selected;
		},
		/**
		 * get an array of all selected nodes
		 * @name get_selected([full])
		 * @param  {mixed}  full if set to `true` the returned array will consist of the full node objects, otherwise - only IDs will be returned
		 * @return {Array}
		 */
		get_selected : function (full) {
			return full ? $.map(this._data.core.selected, $.proxy(function (i) { return this.get_node(i); }, this)) : this._data.core.selected.slice();
		},
		/**
		 * get an array of all top level selected nodes (ignoring children of selected nodes)
		 * @name get_top_selected([full])
		 * @param  {mixed}  full if set to `true` the returned array will consist of the full node objects, otherwise - only IDs will be returned
		 * @return {Array}
		 */
		get_top_selected : function (full) {
			var tmp = this.get_selected(true),
				obj = {}, i, j, k, l;
			for(i = 0, j = tmp.length; i < j; i++) {
				obj[tmp[i].id] = tmp[i];
			}
			for(i = 0, j = tmp.length; i < j; i++) {
				for(k = 0, l = tmp[i].children_d.length; k < l; k++) {
					if(obj[tmp[i].children_d[k]]) {
						delete obj[tmp[i].children_d[k]];
					}
				}
			}
			tmp = [];
			for(i in obj) {
				if(obj.hasOwnProperty(i)) {
					tmp.push(i);
				}
			}
			return full ? $.map(tmp, $.proxy(function (i) { return this.get_node(i); }, this)) : tmp;
		},
		/**
		 * get an array of all bottom level selected nodes (ignoring selected parents)
		 * @name get_bottom_selected([full])
		 * @param  {mixed}  full if set to `true` the returned array will consist of the full node objects, otherwise - only IDs will be returned
		 * @return {Array}
		 */
		get_bottom_selected : function (full) {
			var tmp = this.get_selected(true),
				obj = [], i, j;
			for(i = 0, j = tmp.length; i < j; i++) {
				if(!tmp[i].children.length) {
					obj.push(tmp[i].id);
				}
			}
			return full ? $.map(obj, $.proxy(function (i) { return this.get_node(i); }, this)) : obj;
		},
		/**
		 * gets the current state of the tree so that it can be restored later with `set_state(state)`. Used internally.
		 * @name get_state()
		 * @private
		 * @return {Object}
		 */
		get_state : function () {
			var state	= {
				'core' : {
					'open' : [],
					'loaded' : [],
					'scroll' : {
						'left' : this.element.scrollLeft(),
						'top' : this.element.scrollTop()
					},
					/*!
					'themes' : {
						'name' : this.get_theme(),
						'icons' : this._data.core.themes.icons,
						'dots' : this._data.core.themes.dots
					},
					*/
					'selected' : []
				}
			}, i;
			for(i in this._model.data) {
				if(this._model.data.hasOwnProperty(i)) {
					if(i !== $.jstree.root) {
						if(this._model.data[i].state.loaded && this.settings.core.loaded_state) {
							state.core.loaded.push(i);
						}
						if(this._model.data[i].state.opened) {
							state.core.open.push(i);
						}
						if(this._model.data[i].state.selected) {
							state.core.selected.push(i);
						}
					}
				}
			}
			return state;
		},
		/**
		 * sets the state of the tree. Used internally.
		 * @name set_state(state [, callback])
		 * @private
		 * @param {Object} state the state to restore. Keep in mind this object is passed by reference and jstree will modify it.
		 * @param {Function} callback an optional function to execute once the state is restored.
		 * @trigger set_state.jstree
		 */
		set_state : function (state, callback) {
			if(state) {
				if(state.core && state.core.selected && state.core.initial_selection === undefined) {
					state.core.initial_selection = this._data.core.selected.concat([]).sort().join(',');
				}
				if(state.core) {
					var res, n, t, _this, i;
					if(state.core.loaded) {
						if(!this.settings.core.loaded_state || !$.isArray(state.core.loaded) || !state.core.loaded.length) {
							delete state.core.loaded;
							this.set_state(state, callback);
						}
						else {
							this._load_nodes(state.core.loaded, function (nodes) {
								delete state.core.loaded;
								this.set_state(state, callback);
							});
						}
						return false;
					}
					if(state.core.open) {
						if(!$.isArray(state.core.open) || !state.core.open.length) {
							delete state.core.open;
							this.set_state(state, callback);
						}
						else {
							this._load_nodes(state.core.open, function (nodes) {
								this.open_node(nodes, false, 0);
								delete state.core.open;
								this.set_state(state, callback);
							});
						}
						return false;
					}
					if(state.core.scroll) {
						if(state.core.scroll && state.core.scroll.left !== undefined) {
							this.element.scrollLeft(state.core.scroll.left);
						}
						if(state.core.scroll && state.core.scroll.top !== undefined) {
							this.element.scrollTop(state.core.scroll.top);
						}
						delete state.core.scroll;
						this.set_state(state, callback);
						return false;
					}
					if(state.core.selected) {
						_this = this;
						if (state.core.initial_selection === undefined ||
							state.core.initial_selection === this._data.core.selected.concat([]).sort().join(',')
						) {
							this.deselect_all();
							$.each(state.core.selected, function (i, v) {
								_this.select_node(v, false, true);
							});
						}
						delete state.core.initial_selection;
						delete state.core.selected;
						this.set_state(state, callback);
						return false;
					}
					for(i in state) {
						if(state.hasOwnProperty(i) && i !== "core" && $.inArray(i, this.settings.plugins) === -1) {
							delete state[i];
						}
					}
					if($.isEmptyObject(state.core)) {
						delete state.core;
						this.set_state(state, callback);
						return false;
					}
				}
				if($.isEmptyObject(state)) {
					state = null;
					if(callback) { callback.call(this); }
					/**
					 * triggered when a `set_state` call completes
					 * @event
					 * @name set_state.jstree
					 */
					this.trigger('set_state');
					return false;
				}
				return true;
			}
			return false;
		},
		/**
		 * refreshes the tree - all nodes are reloaded with calls to `load_node`.
		 * @name refresh()
		 * @param {Boolean} skip_loading an option to skip showing the loading indicator
		 * @param {Mixed} forget_state if set to `true` state will not be reapplied, if set to a function (receiving the current state as argument) the result of that function will be used as state
		 * @trigger refresh.jstree
		 */
		refresh : function (skip_loading, forget_state) {
			this._data.core.state = forget_state === true ? {} : this.get_state();
			if(forget_state && $.isFunction(forget_state)) { this._data.core.state = forget_state.call(this, this._data.core.state); }
			this._cnt = 0;
			this._model.data = {};
			this._model.data[$.jstree.root] = {
				id : $.jstree.root,
				parent : null,
				parents : [],
				children : [],
				children_d : [],
				state : { loaded : false }
			};
			this._data.core.selected = [];
			this._data.core.last_clicked = null;
			this._data.core.focused = null;

			var c = this.get_container_ul()[0].className;
			if(!skip_loading) {
				this.element.html("<"+"ul class='"+c+"' role='group'><"+"li class='jstree-initial-node jstree-loading jstree-leaf jstree-last' role='treeitem' id='j"+this._id+"_loading'><i class='jstree-icon jstree-ocl'></i><"+"a class='jstree-anchor' href='#'><i class='jstree-icon jstree-themeicon-hidden'></i>" + this.get_string("Loading ...") + "</a></li></ul>");
				this.element.attr('aria-activedescendant','j'+this._id+'_loading');
			}
			this.load_node($.jstree.root, function (o, s) {
				if(s) {
					this.get_container_ul()[0].className = c;
					if(this._firstChild(this.get_container_ul()[0])) {
						this.element.attr('aria-activedescendant',this._firstChild(this.get_container_ul()[0]).id);
					}
					this.set_state($.extend(true, {}, this._data.core.state), function () {
						/**
						 * triggered when a `refresh` call completes
						 * @event
						 * @name refresh.jstree
						 */
						this.trigger('refresh');
					});
				}
				this._data.core.state = null;
			});
		},
		/**
		 * refreshes a node in the tree (reload its children) all opened nodes inside that node are reloaded with calls to `load_node`.
		 * @name refresh_node(obj)
		 * @param  {mixed} obj the node
		 * @trigger refresh_node.jstree
		 */
		refresh_node : function (obj) {
			obj = this.get_node(obj);
			if(!obj || obj.id === $.jstree.root) { return false; }
			var opened = [], to_load = [], s = this._data.core.selected.concat([]);
			to_load.push(obj.id);
			if(obj.state.opened === true) { opened.push(obj.id); }
			this.get_node(obj, true).find('.jstree-open').each(function() { to_load.push(this.id); opened.push(this.id); });
			this._load_nodes(to_load, $.proxy(function (nodes) {
				this.open_node(opened, false, 0);
				this.select_node(s);
				/**
				 * triggered when a node is refreshed
				 * @event
				 * @name refresh_node.jstree
				 * @param {Object} node - the refreshed node
				 * @param {Array} nodes - an array of the IDs of the nodes that were reloaded
				 */
				this.trigger('refresh_node', { 'node' : obj, 'nodes' : nodes });
			}, this), false, true);
		},
		/**
		 * set (change) the ID of a node
		 * @name set_id(obj, id)
		 * @param  {mixed} obj the node
		 * @param  {String} id the new ID
		 * @return {Boolean}
		 * @trigger set_id.jstree
		 */
		set_id : function (obj, id) {
			obj = this.get_node(obj);
			if(!obj || obj.id === $.jstree.root) { return false; }
			var i, j, m = this._model.data, old = obj.id;
			id = id.toString();
			// update parents (replace current ID with new one in children and children_d)
			m[obj.parent].children[$.inArray(obj.id, m[obj.parent].children)] = id;
			for(i = 0, j = obj.parents.length; i < j; i++) {
				m[obj.parents[i]].children_d[$.inArray(obj.id, m[obj.parents[i]].children_d)] = id;
			}
			// update children (replace current ID with new one in parent and parents)
			for(i = 0, j = obj.children.length; i < j; i++) {
				m[obj.children[i]].parent = id;
			}
			for(i = 0, j = obj.children_d.length; i < j; i++) {
				m[obj.children_d[i]].parents[$.inArray(obj.id, m[obj.children_d[i]].parents)] = id;
			}
			i = $.inArray(obj.id, this._data.core.selected);
			if(i !== -1) { this._data.core.selected[i] = id; }
			// update model and obj itself (obj.id, this._model.data[KEY])
			i = this.get_node(obj.id, true);
			if(i) {
				i.attr('id', id); //.children('.jstree-anchor').attr('id', id + '_anchor').end().attr('aria-labelledby', id + '_anchor');
				if(this.element.attr('aria-activedescendant') === obj.id) {
					this.element.attr('aria-activedescendant', id);
				}
			}
			delete m[obj.id];
			obj.id = id;
			obj.li_attr.id = id;
			m[id] = obj;
			/**
			 * triggered when a node id value is changed
			 * @event
			 * @name set_id.jstree
			 * @param {Object} node
			 * @param {String} old the old id
			 */
			this.trigger('set_id',{ "node" : obj, "new" : obj.id, "old" : old });
			return true;
		},
		/**
		 * get the text value of a node
		 * @name get_text(obj)
		 * @param  {mixed} obj the node
		 * @return {String}
		 */
		get_text : function (obj) {
			obj = this.get_node(obj);
			return (!obj || obj.id === $.jstree.root) ? false : obj.text;
		},
		/**
		 * set the text value of a node. Used internally, please use `rename_node(obj, val)`.
		 * @private
		 * @name set_text(obj, val)
		 * @param  {mixed} obj the node, you can pass an array to set the text on multiple nodes
		 * @param  {String} val the new text value
		 * @return {Boolean}
		 * @trigger set_text.jstree
		 */
		set_text : function (obj, val) {
			var t1, t2;
			if($.isArray(obj)) {
				obj = obj.slice();
				for(t1 = 0, t2 = obj.length; t1 < t2; t1++) {
					this.set_text(obj[t1], val);
				}
				return true;
			}
			obj = this.get_node(obj);
			if(!obj || obj.id === $.jstree.root) { return false; }
			obj.text = val;
			if(this.get_node(obj, true).length) {
				this.redraw_node(obj.id);
			}
			/**
			 * triggered when a node text value is changed
			 * @event
			 * @name set_text.jstree
			 * @param {Object} obj
			 * @param {String} text the new value
			 */
			this.trigger('set_text',{ "obj" : obj, "text" : val });
			return true;
		},
		/**
		 * gets a JSON representation of a node (or the whole tree)
		 * @name get_json([obj, options])
		 * @param  {mixed} obj
		 * @param  {Object} options
		 * @param  {Boolean} options.no_state do not return state information
		 * @param  {Boolean} options.no_id do not return ID
		 * @param  {Boolean} options.no_children do not include children
		 * @param  {Boolean} options.no_data do not include node data
		 * @param  {Boolean} options.no_li_attr do not include LI attributes
		 * @param  {Boolean} options.no_a_attr do not include A attributes
		 * @param  {Boolean} options.flat return flat JSON instead of nested
		 * @return {Object}
		 */
		get_json : function (obj, options, flat) {
			obj = this.get_node(obj || $.jstree.root);
			if(!obj) { return false; }
			if(options && options.flat && !flat) { flat = []; }
			var tmp = {
				'id' : obj.id,
				'text' : obj.text,
				'icon' : this.get_icon(obj),
				'li_attr' : $.extend(true, {}, obj.li_attr),
				'a_attr' : $.extend(true, {}, obj.a_attr),
				'state' : {},
				'data' : options && options.no_data ? false : $.extend(true, $.isArray(obj.data)?[]:{}, obj.data)
				//( this.get_node(obj, true).length ? this.get_node(obj, true).data() : obj.data ),
			}, i, j;
			if(options && options.flat) {
				tmp.parent = obj.parent;
			}
			else {
				tmp.children = [];
			}
			if(!options || !options.no_state) {
				for(i in obj.state) {
					if(obj.state.hasOwnProperty(i)) {
						tmp.state[i] = obj.state[i];
					}
				}
			} else {
				delete tmp.state;
			}
			if(options && options.no_li_attr) {
				delete tmp.li_attr;
			}
			if(options && options.no_a_attr) {
				delete tmp.a_attr;
			}
			if(options && options.no_id) {
				delete tmp.id;
				if(tmp.li_attr && tmp.li_attr.id) {
					delete tmp.li_attr.id;
				}
				if(tmp.a_attr && tmp.a_attr.id) {
					delete tmp.a_attr.id;
				}
			}
			if(options && options.flat && obj.id !== $.jstree.root) {
				flat.push(tmp);
			}
			if(!options || !options.no_children) {
				for(i = 0, j = obj.children.length; i < j; i++) {
					if(options && options.flat) {
						this.get_json(obj.children[i], options, flat);
					}
					else {
						tmp.children.push(this.get_json(obj.children[i], options));
					}
				}
			}
			return options && options.flat ? flat : (obj.id === $.jstree.root ? tmp.children : tmp);
		},
		/**
		 * create a new node (do not confuse with load_node)
		 * @name create_node([par, node, pos, callback, is_loaded])
		 * @param  {mixed}   par       the parent node (to create a root node use either "#" (string) or `null`)
		 * @param  {mixed}   node      the data for the new node (a valid JSON object, or a simple string with the name)
		 * @param  {mixed}   pos       the index at which to insert the node, "first" and "last" are also supported, default is "last"
		 * @param  {Function} callback a function to be called once the node is created
		 * @param  {Boolean} is_loaded internal argument indicating if the parent node was succesfully loaded
		 * @return {String}            the ID of the newly create node
		 * @trigger model.jstree, create_node.jstree
		 */
		create_node : function (par, node, pos, callback, is_loaded) {
			if(par === null) { par = $.jstree.root; }
			par = this.get_node(par);
			if(!par) { return false; }
			pos = pos === undefined ? "last" : pos;
			if(!pos.toString().match(/^(before|after)$/) && !is_loaded && !this.is_loaded(par)) {
				return this.load_node(par, function () { this.create_node(par, node, pos, callback, true); });
			}
			if(!node) { node = { "text" : this.get_string('New node') }; }
			if(typeof node === "string") {
				node = { "text" : node };
			} else {
				node = $.extend(true, {}, node);
			}
			if(node.text === undefined) { node.text = this.get_string('New node'); }
			var tmp, dpc, i, j;

			if(par.id === $.jstree.root) {
				if(pos === "before") { pos = "first"; }
				if(pos === "after") { pos = "last"; }
			}
			switch(pos) {
				case "before":
					tmp = this.get_node(par.parent);
					pos = $.inArray(par.id, tmp.children);
					par = tmp;
					break;
				case "after" :
					tmp = this.get_node(par.parent);
					pos = $.inArray(par.id, tmp.children) + 1;
					par = tmp;
					break;
				case "inside":
				case "first":
					pos = 0;
					break;
				case "last":
					pos = par.children.length;
					break;
				default:
					if(!pos) { pos = 0; }
					break;
			}
			if(pos > par.children.length) { pos = par.children.length; }
			if(!node.id) { node.id = true; }
			if(!this.check("create_node", node, par, pos)) {
				this.settings.core.error.call(this, this._data.core.last_error);
				return false;
			}
			if(node.id === true) { delete node.id; }
			node = this._parse_model_from_json(node, par.id, par.parents.concat());
			if(!node) { return false; }
			tmp = this.get_node(node);
			dpc = [];
			dpc.push(node);
			dpc = dpc.concat(tmp.children_d);
			this.trigger('model', { "nodes" : dpc, "parent" : par.id });

			par.children_d = par.children_d.concat(dpc);
			for(i = 0, j = par.parents.length; i < j; i++) {
				this._model.data[par.parents[i]].children_d = this._model.data[par.parents[i]].children_d.concat(dpc);
			}
			node = tmp;
			tmp = [];
			for(i = 0, j = par.children.length; i < j; i++) {
				tmp[i >= pos ? i+1 : i] = par.children[i];
			}
			tmp[pos] = node.id;
			par.children = tmp;

			this.redraw_node(par, true);
			/**
			 * triggered when a node is created
			 * @event
			 * @name create_node.jstree
			 * @param {Object} node
			 * @param {String} parent the parent's ID
			 * @param {Number} position the position of the new node among the parent's children
			 */
			this.trigger('create_node', { "node" : this.get_node(node), "parent" : par.id, "position" : pos });
			if(callback) { callback.call(this, this.get_node(node)); }
			return node.id;
		},
		/**
		 * set the text value of a node
		 * @name rename_node(obj, val)
		 * @param  {mixed} obj the node, you can pass an array to rename multiple nodes to the same name
		 * @param  {String} val the new text value
		 * @return {Boolean}
		 * @trigger rename_node.jstree
		 */
		rename_node : function (obj, val) {
			var t1, t2, old;
			if($.isArray(obj)) {
				obj = obj.slice();
				for(t1 = 0, t2 = obj.length; t1 < t2; t1++) {
					this.rename_node(obj[t1], val);
				}
				return true;
			}
			obj = this.get_node(obj);
			if(!obj || obj.id === $.jstree.root) { return false; }
			old = obj.text;
			if(!this.check("rename_node", obj, this.get_parent(obj), val)) {
				this.settings.core.error.call(this, this._data.core.last_error);
				return false;
			}
			this.set_text(obj, val); // .apply(this, Array.prototype.slice.call(arguments))
			/**
			 * triggered when a node is renamed
			 * @event
			 * @name rename_node.jstree
			 * @param {Object} node
			 * @param {String} text the new value
			 * @param {String} old the old value
			 */
			this.trigger('rename_node', { "node" : obj, "text" : val, "old" : old });
			return true;
		},
		/**
		 * remove a node
		 * @name delete_node(obj)
		 * @param  {mixed} obj the node, you can pass an array to delete multiple nodes
		 * @return {Boolean}
		 * @trigger delete_node.jstree, changed.jstree
		 */
		delete_node : function (obj) {
			var t1, t2, par, pos, tmp, i, j, k, l, c, top, lft;
			if($.isArray(obj)) {
				obj = obj.slice();
				for(t1 = 0, t2 = obj.length; t1 < t2; t1++) {
					this.delete_node(obj[t1]);
				}
				return true;
			}
			obj = this.get_node(obj);
			if(!obj || obj.id === $.jstree.root) { return false; }
			par = this.get_node(obj.parent);
			pos = $.inArray(obj.id, par.children);
			c = false;
			if(!this.check("delete_node", obj, par, pos)) {
				this.settings.core.error.call(this, this._data.core.last_error);
				return false;
			}
			if(pos !== -1) {
				par.children = $.vakata.array_remove(par.children, pos);
			}
			tmp = obj.children_d.concat([]);
			tmp.push(obj.id);
			for(i = 0, j = obj.parents.length; i < j; i++) {
				this._model.data[obj.parents[i]].children_d = $.vakata.array_filter(this._model.data[obj.parents[i]].children_d, function (v) {
					return $.inArray(v, tmp) === -1;
				});
			}
			for(k = 0, l = tmp.length; k < l; k++) {
				if(this._model.data[tmp[k]].state.selected) {
					c = true;
					break;
				}
			}
			if (c) {
				this._data.core.selected = $.vakata.array_filter(this._data.core.selected, function (v) {
					return $.inArray(v, tmp) === -1;
				});
			}
			/**
			 * triggered when a node is deleted
			 * @event
			 * @name delete_node.jstree
			 * @param {Object} node
			 * @param {String} parent the parent's ID
			 */
			this.trigger('delete_node', { "node" : obj, "parent" : par.id });
			if(c) {
				this.trigger('changed', { 'action' : 'delete_node', 'node' : obj, 'selected' : this._data.core.selected, 'parent' : par.id });
			}
			for(k = 0, l = tmp.length; k < l; k++) {
				delete this._model.data[tmp[k]];
			}
			if($.inArray(this._data.core.focused, tmp) !== -1) {
				this._data.core.focused = null;
				top = this.element[0].scrollTop;
				lft = this.element[0].scrollLeft;
				if(par.id === $.jstree.root) {
					if (this._model.data[$.jstree.root].children[0]) {
						this.get_node(this._model.data[$.jstree.root].children[0], true).children('.jstree-anchor').focus();
					}
				}
				else {
					this.get_node(par, true).children('.jstree-anchor').focus();
				}
				this.element[0].scrollTop  = top;
				this.element[0].scrollLeft = lft;
			}
			this.redraw_node(par, true);
			return true;
		},
		/**
		 * check if an operation is premitted on the tree. Used internally.
		 * @private
		 * @name check(chk, obj, par, pos)
		 * @param  {String} chk the operation to check, can be "create_node", "rename_node", "delete_node", "copy_node" or "move_node"
		 * @param  {mixed} obj the node
		 * @param  {mixed} par the parent
		 * @param  {mixed} pos the position to insert at, or if "rename_node" - the new name
		 * @param  {mixed} more some various additional information, for example if a "move_node" operations is triggered by DND this will be the hovered node
		 * @return {Boolean}
		 */
		check : function (chk, obj, par, pos, more) {
			obj = obj && obj.id ? obj : this.get_node(obj);
			par = par && par.id ? par : this.get_node(par);
			var tmp = chk.match(/^move_node|copy_node|create_node$/i) ? par : obj,
				chc = this.settings.core.check_callback;
			if(chk === "move_node" || chk === "copy_node") {
				if((!more || !more.is_multi) && (obj.id === par.id || (chk === "move_node" && $.inArray(obj.id, par.children) === pos) || $.inArray(par.id, obj.children_d) !== -1)) {
					this._data.core.last_error = { 'error' : 'check', 'plugin' : 'core', 'id' : 'core_01', 'reason' : 'Moving parent inside child', 'data' : JSON.stringify({ 'chk' : chk, 'pos' : pos, 'obj' : obj && obj.id ? obj.id : false, 'par' : par && par.id ? par.id : false }) };
					return false;
				}
			}
			if(tmp && tmp.data) { tmp = tmp.data; }
			if(tmp && tmp.functions && (tmp.functions[chk] === false || tmp.functions[chk] === true)) {
				if(tmp.functions[chk] === false) {
					this._data.core.last_error = { 'error' : 'check', 'plugin' : 'core', 'id' : 'core_02', 'reason' : 'Node data prevents function: ' + chk, 'data' : JSON.stringify({ 'chk' : chk, 'pos' : pos, 'obj' : obj && obj.id ? obj.id : false, 'par' : par && par.id ? par.id : false }) };
				}
				return tmp.functions[chk];
			}
			if(chc === false || ($.isFunction(chc) && chc.call(this, chk, obj, par, pos, more) === false) || (chc && chc[chk] === false)) {
				this._data.core.last_error = { 'error' : 'check', 'plugin' : 'core', 'id' : 'core_03', 'reason' : 'User config for core.check_callback prevents function: ' + chk, 'data' : JSON.stringify({ 'chk' : chk, 'pos' : pos, 'obj' : obj && obj.id ? obj.id : false, 'par' : par && par.id ? par.id : false }) };
				return false;
			}
			return true;
		},
		/**
		 * get the last error
		 * @name last_error()
		 * @return {Object}
		 */
		last_error : function () {
			return this._data.core.last_error;
		},
		/**
		 * move a node to a new parent
		 * @name move_node(obj, par [, pos, callback, is_loaded])
		 * @param  {mixed} obj the node to move, pass an array to move multiple nodes
		 * @param  {mixed} par the new parent
		 * @param  {mixed} pos the position to insert at (besides integer values, "first" and "last" are supported, as well as "before" and "after"), defaults to integer `0`
		 * @param  {function} callback a function to call once the move is completed, receives 3 arguments - the node, the new parent and the position
		 * @param  {Boolean} is_loaded internal parameter indicating if the parent node has been loaded
		 * @param  {Boolean} skip_redraw internal parameter indicating if the tree should be redrawn
		 * @param  {Boolean} instance internal parameter indicating if the node comes from another instance
		 * @trigger move_node.jstree
		 */
		move_node : function (obj, par, pos, callback, is_loaded, skip_redraw, origin) {
			var t1, t2, old_par, old_pos, new_par, old_ins, is_multi, dpc, tmp, i, j, k, l, p;

			par = this.get_node(par);
			pos = pos === undefined ? 0 : pos;
			if(!par) { return false; }
			if(!pos.toString().match(/^(before|after)$/) && !is_loaded && !this.is_loaded(par)) {
				return this.load_node(par, function () { this.move_node(obj, par, pos, callback, true, false, origin); });
			}

			if($.isArray(obj)) {
				if(obj.length === 1) {
					obj = obj[0];
				}
				else {
					//obj = obj.slice();
					for(t1 = 0, t2 = obj.length; t1 < t2; t1++) {
						if((tmp = this.move_node(obj[t1], par, pos, callback, is_loaded, false, origin))) {
							par = tmp;
							pos = "after";
						}
					}
					this.redraw();
					return true;
				}
			}
			obj = obj && obj.id ? obj : this.get_node(obj);

			if(!obj || obj.id === $.jstree.root) { return false; }

			old_par = (obj.parent || $.jstree.root).toString();
			new_par = (!pos.toString().match(/^(before|after)$/) || par.id === $.jstree.root) ? par : this.get_node(par.parent);
			old_ins = origin ? origin : (this._model.data[obj.id] ? this : $.jstree.reference(obj.id));
			is_multi = !old_ins || !old_ins._id || (this._id !== old_ins._id);
			old_pos = old_ins && old_ins._id && old_par && old_ins._model.data[old_par] && old_ins._model.data[old_par].children ? $.inArray(obj.id, old_ins._model.data[old_par].children) : -1;
			if(old_ins && old_ins._id) {
				obj = old_ins._model.data[obj.id];
			}

			if(is_multi) {
				if((tmp = this.copy_node(obj, par, pos, callback, is_loaded, false, origin))) {
					if(old_ins) { old_ins.delete_node(obj); }
					return tmp;
				}
				return false;
			}
			//var m = this._model.data;
			if(par.id === $.jstree.root) {
				if(pos === "before") { pos = "first"; }
				if(pos === "after") { pos = "last"; }
			}
			switch(pos) {
				case "before":
					pos = $.inArray(par.id, new_par.children);
					break;
				case "after" :
					pos = $.inArray(par.id, new_par.children) + 1;
					break;
				case "inside":
				case "first":
					pos = 0;
					break;
				case "last":
					pos = new_par.children.length;
					break;
				default:
					if(!pos) { pos = 0; }
					break;
			}
			if(pos > new_par.children.length) { pos = new_par.children.length; }
			if(!this.check("move_node", obj, new_par, pos, { 'core' : true, 'origin' : origin, 'is_multi' : (old_ins && old_ins._id && old_ins._id !== this._id), 'is_foreign' : (!old_ins || !old_ins._id) })) {
				this.settings.core.error.call(this, this._data.core.last_error);
				return false;
			}
			if(obj.parent === new_par.id) {
				dpc = new_par.children.concat();
				tmp = $.inArray(obj.id, dpc);
				if(tmp !== -1) {
					dpc = $.vakata.array_remove(dpc, tmp);
					if(pos > tmp) { pos--; }
				}
				tmp = [];
				for(i = 0, j = dpc.length; i < j; i++) {
					tmp[i >= pos ? i+1 : i] = dpc[i];
				}
				tmp[pos] = obj.id;
				new_par.children = tmp;
				this._node_changed(new_par.id);
				this.redraw(new_par.id === $.jstree.root);
			}
			else {
				// clean old parent and up
				tmp = obj.children_d.concat();
				tmp.push(obj.id);
				for(i = 0, j = obj.parents.length; i < j; i++) {
					dpc = [];
					p = old_ins._model.data[obj.parents[i]].children_d;
					for(k = 0, l = p.length; k < l; k++) {
						if($.inArray(p[k], tmp) === -1) {
							dpc.push(p[k]);
						}
					}
					old_ins._model.data[obj.parents[i]].children_d = dpc;
				}
				old_ins._model.data[old_par].children = $.vakata.array_remove_item(old_ins._model.data[old_par].children, obj.id);

				// insert into new parent and up
				for(i = 0, j = new_par.parents.length; i < j; i++) {
					this._model.data[new_par.parents[i]].children_d = this._model.data[new_par.parents[i]].children_d.concat(tmp);
				}
				dpc = [];
				for(i = 0, j = new_par.children.length; i < j; i++) {
					dpc[i >= pos ? i+1 : i] = new_par.children[i];
				}
				dpc[pos] = obj.id;
				new_par.children = dpc;
				new_par.children_d.push(obj.id);
				new_par.children_d = new_par.children_d.concat(obj.children_d);

				// update object
				obj.parent = new_par.id;
				tmp = new_par.parents.concat();
				tmp.unshift(new_par.id);
				p = obj.parents.length;
				obj.parents = tmp;

				// update object children
				tmp = tmp.concat();
				for(i = 0, j = obj.children_d.length; i < j; i++) {
					this._model.data[obj.children_d[i]].parents = this._model.data[obj.children_d[i]].parents.slice(0,p*-1);
					Array.prototype.push.apply(this._model.data[obj.children_d[i]].parents, tmp);
				}

				if(old_par === $.jstree.root || new_par.id === $.jstree.root) {
					this._model.force_full_redraw = true;
				}
				if(!this._model.force_full_redraw) {
					this._node_changed(old_par);
					this._node_changed(new_par.id);
				}
				if(!skip_redraw) {
					this.redraw();
				}
			}
			if(callback) { callback.call(this, obj, new_par, pos); }
			/**
			 * triggered when a node is moved
			 * @event
			 * @name move_node.jstree
			 * @param {Object} node
			 * @param {String} parent the parent's ID
			 * @param {Number} position the position of the node among the parent's children
			 * @param {String} old_parent the old parent of the node
			 * @param {Number} old_position the old position of the node
			 * @param {Boolean} is_multi do the node and new parent belong to different instances
			 * @param {jsTree} old_instance the instance the node came from
			 * @param {jsTree} new_instance the instance of the new parent
			 */
			this.trigger('move_node', { "node" : obj, "parent" : new_par.id, "position" : pos, "old_parent" : old_par, "old_position" : old_pos, 'is_multi' : (old_ins && old_ins._id && old_ins._id !== this._id), 'is_foreign' : (!old_ins || !old_ins._id), 'old_instance' : old_ins, 'new_instance' : this });
			return obj.id;
		},
		/**
		 * copy a node to a new parent
		 * @name copy_node(obj, par [, pos, callback, is_loaded])
		 * @param  {mixed} obj the node to copy, pass an array to copy multiple nodes
		 * @param  {mixed} par the new parent
		 * @param  {mixed} pos the position to insert at (besides integer values, "first" and "last" are supported, as well as "before" and "after"), defaults to integer `0`
		 * @param  {function} callback a function to call once the move is completed, receives 3 arguments - the node, the new parent and the position
		 * @param  {Boolean} is_loaded internal parameter indicating if the parent node has been loaded
		 * @param  {Boolean} skip_redraw internal parameter indicating if the tree should be redrawn
		 * @param  {Boolean} instance internal parameter indicating if the node comes from another instance
		 * @trigger model.jstree copy_node.jstree
		 */
		copy_node : function (obj, par, pos, callback, is_loaded, skip_redraw, origin) {
			var t1, t2, dpc, tmp, i, j, node, old_par, new_par, old_ins, is_multi;

			par = this.get_node(par);
			pos = pos === undefined ? 0 : pos;
			if(!par) { return false; }
			if(!pos.toString().match(/^(before|after)$/) && !is_loaded && !this.is_loaded(par)) {
				return this.load_node(par, function () { this.copy_node(obj, par, pos, callback, true, false, origin); });
			}

			if($.isArray(obj)) {
				if(obj.length === 1) {
					obj = obj[0];
				}
				else {
					//obj = obj.slice();
					for(t1 = 0, t2 = obj.length; t1 < t2; t1++) {
						if((tmp = this.copy_node(obj[t1], par, pos, callback, is_loaded, true, origin))) {
							par = tmp;
							pos = "after";
						}
					}
					this.redraw();
					return true;
				}
			}
			obj = obj && obj.id ? obj : this.get_node(obj);
			if(!obj || obj.id === $.jstree.root) { return false; }

			old_par = (obj.parent || $.jstree.root).toString();
			new_par = (!pos.toString().match(/^(before|after)$/) || par.id === $.jstree.root) ? par : this.get_node(par.parent);
			old_ins = origin ? origin : (this._model.data[obj.id] ? this : $.jstree.reference(obj.id));
			is_multi = !old_ins || !old_ins._id || (this._id !== old_ins._id);

			if(old_ins && old_ins._id) {
				obj = old_ins._model.data[obj.id];
			}

			if(par.id === $.jstree.root) {
				if(pos === "before") { pos = "first"; }
				if(pos === "after") { pos = "last"; }
			}
			switch(pos) {
				case "before":
					pos = $.inArray(par.id, new_par.children);
					break;
				case "after" :
					pos = $.inArray(par.id, new_par.children) + 1;
					break;
				case "inside":
				case "first":
					pos = 0;
					break;
				case "last":
					pos = new_par.children.length;
					break;
				default:
					if(!pos) { pos = 0; }
					break;
			}
			if(pos > new_par.children.length) { pos = new_par.children.length; }
			if(!this.check("copy_node", obj, new_par, pos, { 'core' : true, 'origin' : origin, 'is_multi' : (old_ins && old_ins._id && old_ins._id !== this._id), 'is_foreign' : (!old_ins || !old_ins._id) })) {
				this.settings.core.error.call(this, this._data.core.last_error);
				return false;
			}
			node = old_ins ? old_ins.get_json(obj, { no_id : true, no_data : true, no_state : true }) : obj;
			if(!node) { return false; }
			if(node.id === true) { delete node.id; }
			node = this._parse_model_from_json(node, new_par.id, new_par.parents.concat());
			if(!node) { return false; }
			tmp = this.get_node(node);
			if(obj && obj.state && obj.state.loaded === false) { tmp.state.loaded = false; }
			dpc = [];
			dpc.push(node);
			dpc = dpc.concat(tmp.children_d);
			this.trigger('model', { "nodes" : dpc, "parent" : new_par.id });

			// insert into new parent and up
			for(i = 0, j = new_par.parents.length; i < j; i++) {
				this._model.data[new_par.parents[i]].children_d = this._model.data[new_par.parents[i]].children_d.concat(dpc);
			}
			dpc = [];
			for(i = 0, j = new_par.children.length; i < j; i++) {
				dpc[i >= pos ? i+1 : i] = new_par.children[i];
			}
			dpc[pos] = tmp.id;
			new_par.children = dpc;
			new_par.children_d.push(tmp.id);
			new_par.children_d = new_par.children_d.concat(tmp.children_d);

			if(new_par.id === $.jstree.root) {
				this._model.force_full_redraw = true;
			}
			if(!this._model.force_full_redraw) {
				this._node_changed(new_par.id);
			}
			if(!skip_redraw) {
				this.redraw(new_par.id === $.jstree.root);
			}
			if(callback) { callback.call(this, tmp, new_par, pos); }
			/**
			 * triggered when a node is copied
			 * @event
			 * @name copy_node.jstree
			 * @param {Object} node the copied node
			 * @param {Object} original the original node
			 * @param {String} parent the parent's ID
			 * @param {Number} position the position of the node among the parent's children
			 * @param {String} old_parent the old parent of the node
			 * @param {Number} old_position the position of the original node
			 * @param {Boolean} is_multi do the node and new parent belong to different instances
			 * @param {jsTree} old_instance the instance the node came from
			 * @param {jsTree} new_instance the instance of the new parent
			 */
			this.trigger('copy_node', { "node" : tmp, "original" : obj, "parent" : new_par.id, "position" : pos, "old_parent" : old_par, "old_position" : old_ins && old_ins._id && old_par && old_ins._model.data[old_par] && old_ins._model.data[old_par].children ? $.inArray(obj.id, old_ins._model.data[old_par].children) : -1,'is_multi' : (old_ins && old_ins._id && old_ins._id !== this._id), 'is_foreign' : (!old_ins || !old_ins._id), 'old_instance' : old_ins, 'new_instance' : this });
			return tmp.id;
		},
		/**
		 * cut a node (a later call to `paste(obj)` would move the node)
		 * @name cut(obj)
		 * @param  {mixed} obj multiple objects can be passed using an array
		 * @trigger cut.jstree
		 */
		cut : function (obj) {
			if(!obj) { obj = this._data.core.selected.concat(); }
			if(!$.isArray(obj)) { obj = [obj]; }
			if(!obj.length) { return false; }
			var tmp = [], o, t1, t2;
			for(t1 = 0, t2 = obj.length; t1 < t2; t1++) {
				o = this.get_node(obj[t1]);
				if(o && o.id && o.id !== $.jstree.root) { tmp.push(o); }
			}
			if(!tmp.length) { return false; }
			ccp_node = tmp;
			ccp_inst = this;
			ccp_mode = 'move_node';
			/**
			 * triggered when nodes are added to the buffer for moving
			 * @event
			 * @name cut.jstree
			 * @param {Array} node
			 */
			this.trigger('cut', { "node" : obj });
		},
		/**
		 * copy a node (a later call to `paste(obj)` would copy the node)
		 * @name copy(obj)
		 * @param  {mixed} obj multiple objects can be passed using an array
		 * @trigger copy.jstree
		 */
		copy : function (obj) {
			if(!obj) { obj = this._data.core.selected.concat(); }
			if(!$.isArray(obj)) { obj = [obj]; }
			if(!obj.length) { return false; }
			var tmp = [], o, t1, t2;
			for(t1 = 0, t2 = obj.length; t1 < t2; t1++) {
				o = this.get_node(obj[t1]);
				if(o && o.id && o.id !== $.jstree.root) { tmp.push(o); }
			}
			if(!tmp.length) { return false; }
			ccp_node = tmp;
			ccp_inst = this;
			ccp_mode = 'copy_node';
			/**
			 * triggered when nodes are added to the buffer for copying
			 * @event
			 * @name copy.jstree
			 * @param {Array} node
			 */
			this.trigger('copy', { "node" : obj });
		},
		/**
		 * get the current buffer (any nodes that are waiting for a paste operation)
		 * @name get_buffer()
		 * @return {Object} an object consisting of `mode` ("copy_node" or "move_node"), `node` (an array of objects) and `inst` (the instance)
		 */
		get_buffer : function () {
			return { 'mode' : ccp_mode, 'node' : ccp_node, 'inst' : ccp_inst };
		},
		/**
		 * check if there is something in the buffer to paste
		 * @name can_paste()
		 * @return {Boolean}
		 */
		can_paste : function () {
			return ccp_mode !== false && ccp_node !== false; // && ccp_inst._model.data[ccp_node];
		},
		/**
		 * copy or move the previously cut or copied nodes to a new parent
		 * @name paste(obj [, pos])
		 * @param  {mixed} obj the new parent
		 * @param  {mixed} pos the position to insert at (besides integer, "first" and "last" are supported), defaults to integer `0`
		 * @trigger paste.jstree
		 */
		paste : function (obj, pos) {
			obj = this.get_node(obj);
			if(!obj || !ccp_mode || !ccp_mode.match(/^(copy_node|move_node)$/) || !ccp_node) { return false; }
			if(this[ccp_mode](ccp_node, obj, pos, false, false, false, ccp_inst)) {
				/**
				 * triggered when paste is invoked
				 * @event
				 * @name paste.jstree
				 * @param {String} parent the ID of the receiving node
				 * @param {Array} node the nodes in the buffer
				 * @param {String} mode the performed operation - "copy_node" or "move_node"
				 */
				this.trigger('paste', { "parent" : obj.id, "node" : ccp_node, "mode" : ccp_mode });
			}
			ccp_node = false;
			ccp_mode = false;
			ccp_inst = false;
		},
		/**
		 * clear the buffer of previously copied or cut nodes
		 * @name clear_buffer()
		 * @trigger clear_buffer.jstree
		 */
		clear_buffer : function () {
			ccp_node = false;
			ccp_mode = false;
			ccp_inst = false;
			/**
			 * triggered when the copy / cut buffer is cleared
			 * @event
			 * @name clear_buffer.jstree
			 */
			this.trigger('clear_buffer');
		},
		/**
		 * put a node in edit mode (input field to rename the node)
		 * @name edit(obj [, default_text, callback])
		 * @param  {mixed} obj
		 * @param  {String} default_text the text to populate the input with (if omitted or set to a non-string value the node's text value is used)
		 * @param  {Function} callback a function to be called once the text box is blurred, it is called in the instance's scope and receives the node, a status parameter (true if the rename is successful, false otherwise) and a boolean indicating if the user cancelled the edit. You can access the node's title using .text
		 */
		edit : function (obj, default_text, callback) {
			var rtl, w, a, s, t, h1, h2, fn, tmp, cancel = false;
			obj = this.get_node(obj);
			if(!obj) { return false; }
			if(!this.check("edit", obj, this.get_parent(obj))) {
				this.settings.core.error.call(this, this._data.core.last_error);
				return false;
			}
			tmp = obj;
			default_text = typeof default_text === 'string' ? default_text : obj.text;
			this.set_text(obj, "");
			obj = this._open_to(obj);
			tmp.text = default_text;

			rtl = this._data.core.rtl;
			w  = this.element.width();
			this._data.core.focused = tmp.id;
			a  = obj.children('.jstree-anchor').focus();
			s  = $('<span>');
			/*!
			oi = obj.children("i:visible"),
			ai = a.children("i:visible"),
			w1 = oi.width() * oi.length,
			w2 = ai.width() * ai.length,
			*/
			t  = default_text;
			h1 = $("<"+"div />", { css : { "position" : "absolute", "top" : "-200px", "left" : (rtl ? "0px" : "-1000px"), "visibility" : "hidden" } }).appendTo(document.body);
			h2 = $("<"+"input />", {
						"value" : t,
						"class" : "jstree-rename-input",
						// "size" : t.length,
						"css" : {
							"padding" : "0",
							"border" : "1px solid silver",
							"box-sizing" : "border-box",
							"display" : "inline-block",
							"height" : (this._data.core.li_height) + "px",
							"lineHeight" : (this._data.core.li_height) + "px",
							"width" : "150px" // will be set a bit further down
						},
						"blur" : $.proxy(function (e) {
							e.stopImmediatePropagation();
							e.preventDefault();
							var i = s.children(".jstree-rename-input"),
								v = i.val(),
								f = this.settings.core.force_text,
								nv;
							if(v === "") { v = t; }
							h1.remove();
							s.replaceWith(a);
							s.remove();
							t = f ? t : $('<div></div>').append($.parseHTML(t)).html();
							obj = this.get_node(obj);
							this.set_text(obj, t);
							nv = !!this.rename_node(obj, f ? $('<div></div>').text(v).text() : $('<div></div>').append($.parseHTML(v)).html());
							if(!nv) {
								this.set_text(obj, t); // move this up? and fix #483
							}
							this._data.core.focused = tmp.id;
							setTimeout($.proxy(function () {
								var node = this.get_node(tmp.id, true);
								if(node.length) {
									this._data.core.focused = tmp.id;
									node.children('.jstree-anchor').focus();
								}
							}, this), 0);
							if(callback) {
								callback.call(this, tmp, nv, cancel);
							}
							h2 = null;
						}, this),
						"keydown" : function (e) {
							var key = e.which;
							if(key === 27) {
								cancel = true;
								this.value = t;
							}
							if(key === 27 || key === 13 || key === 37 || key === 38 || key === 39 || key === 40 || key === 32) {
								e.stopImmediatePropagation();
							}
							if(key === 27 || key === 13) {
								e.preventDefault();
								this.blur();
							}
						},
						"click" : function (e) { e.stopImmediatePropagation(); },
						"mousedown" : function (e) { e.stopImmediatePropagation(); },
						"keyup" : function (e) {
							h2.width(Math.min(h1.text("pW" + this.value).width(),w));
						},
						"keypress" : function(e) {
							if(e.which === 13) { return false; }
						}
					});
				fn = {
						fontFamily		: a.css('fontFamily')		|| '',
						fontSize		: a.css('fontSize')			|| '',
						fontWeight		: a.css('fontWeight')		|| '',
						fontStyle		: a.css('fontStyle')		|| '',
						fontStretch		: a.css('fontStretch')		|| '',
						fontVariant		: a.css('fontVariant')		|| '',
						letterSpacing	: a.css('letterSpacing')	|| '',
						wordSpacing		: a.css('wordSpacing')		|| ''
				};
			s.attr('class', a.attr('class')).append(a.contents().clone()).append(h2);
			a.replaceWith(s);
			h1.css(fn);
			h2.css(fn).width(Math.min(h1.text("pW" + h2[0].value).width(),w))[0].select();
			$(document).one('mousedown.jstree touchstart.jstree dnd_start.vakata', function (e) {
				if (h2 && e.target !== h2) {
					$(h2).blur();
				}
			});
		},


		/**
		 * changes the theme
		 * @name set_theme(theme_name [, theme_url])
		 * @param {String} theme_name the name of the new theme to apply
		 * @param {mixed} theme_url  the location of the CSS file for this theme. Omit or set to `false` if you manually included the file. Set to `true` to autoload from the `core.themes.dir` directory.
		 * @trigger set_theme.jstree
		 */
		set_theme : function (theme_name, theme_url) {
			if(!theme_name) { return false; }
			if(theme_url === true) {
				var dir = this.settings.core.themes.dir;
				if(!dir) { dir = $.jstree.path + '/themes'; }
				theme_url = dir + '/' + theme_name + '/style.css';
			}
			if(theme_url && $.inArray(theme_url, themes_loaded) === -1) {
				$('head').append('<'+'link rel="stylesheet" href="' + theme_url + '" type="text/css" />');
				themes_loaded.push(theme_url);
			}
			if(this._data.core.themes.name) {
				this.element.removeClass('jstree-' + this._data.core.themes.name);
			}
			this._data.core.themes.name = theme_name;
			this.element.addClass('jstree-' + theme_name);
			this.element[this.settings.core.themes.responsive ? 'addClass' : 'removeClass' ]('jstree-' + theme_name + '-responsive');
			/**
			 * triggered when a theme is set
			 * @event
			 * @name set_theme.jstree
			 * @param {String} theme the new theme
			 */
			this.trigger('set_theme', { 'theme' : theme_name });
		},
		/**
		 * gets the name of the currently applied theme name
		 * @name get_theme()
		 * @return {String}
		 */
		get_theme : function () { return this._data.core.themes.name; },
		/**
		 * changes the theme variant (if the theme has variants)
		 * @name set_theme_variant(variant_name)
		 * @param {String|Boolean} variant_name the variant to apply (if `false` is used the current variant is removed)
		 */
		set_theme_variant : function (variant_name) {
			if(this._data.core.themes.variant) {
				this.element.removeClass('jstree-' + this._data.core.themes.name + '-' + this._data.core.themes.variant);
			}
			this._data.core.themes.variant = variant_name;
			if(variant_name) {
				this.element.addClass('jstree-' + this._data.core.themes.name + '-' + this._data.core.themes.variant);
			}
		},
		/**
		 * gets the name of the currently applied theme variant
		 * @name get_theme()
		 * @return {String}
		 */
		get_theme_variant : function () { return this._data.core.themes.variant; },
		/**
		 * shows a striped background on the container (if the theme supports it)
		 * @name show_stripes()
		 */
		show_stripes : function () {
			this._data.core.themes.stripes = true;
			this.get_container_ul().addClass("jstree-striped");
			/**
			 * triggered when stripes are shown
			 * @event
			 * @name show_stripes.jstree
			 */
			this.trigger('show_stripes');
		},
		/**
		 * hides the striped background on the container
		 * @name hide_stripes()
		 */
		hide_stripes : function () {
			this._data.core.themes.stripes = false;
			this.get_container_ul().removeClass("jstree-striped");
			/**
			 * triggered when stripes are hidden
			 * @event
			 * @name hide_stripes.jstree
			 */
			this.trigger('hide_stripes');
		},
		/**
		 * toggles the striped background on the container
		 * @name toggle_stripes()
		 */
		toggle_stripes : function () { if(this._data.core.themes.stripes) { this.hide_stripes(); } else { this.show_stripes(); } },
		/**
		 * shows the connecting dots (if the theme supports it)
		 * @name show_dots()
		 */
		show_dots : function () {
			this._data.core.themes.dots = true;
			this.get_container_ul().removeClass("jstree-no-dots");
			/**
			 * triggered when dots are shown
			 * @event
			 * @name show_dots.jstree
			 */
			this.trigger('show_dots');
		},
		/**
		 * hides the connecting dots
		 * @name hide_dots()
		 */
		hide_dots : function () {
			this._data.core.themes.dots = false;
			this.get_container_ul().addClass("jstree-no-dots");
			/**
			 * triggered when dots are hidden
			 * @event
			 * @name hide_dots.jstree
			 */
			this.trigger('hide_dots');
		},
		/**
		 * toggles the connecting dots
		 * @name toggle_dots()
		 */
		toggle_dots : function () { if(this._data.core.themes.dots) { this.hide_dots(); } else { this.show_dots(); } },
		/**
		 * show the node icons
		 * @name show_icons()
		 */
		show_icons : function () {
			this._data.core.themes.icons = true;
			this.get_container_ul().removeClass("jstree-no-icons");
			/**
			 * triggered when icons are shown
			 * @event
			 * @name show_icons.jstree
			 */
			this.trigger('show_icons');
		},
		/**
		 * hide the node icons
		 * @name hide_icons()
		 */
		hide_icons : function () {
			this._data.core.themes.icons = false;
			this.get_container_ul().addClass("jstree-no-icons");
			/**
			 * triggered when icons are hidden
			 * @event
			 * @name hide_icons.jstree
			 */
			this.trigger('hide_icons');
		},
		/**
		 * toggle the node icons
		 * @name toggle_icons()
		 */
		toggle_icons : function () { if(this._data.core.themes.icons) { this.hide_icons(); } else { this.show_icons(); } },
		/**
		 * show the node ellipsis
		 * @name show_icons()
		 */
		show_ellipsis : function () {
			this._data.core.themes.ellipsis = true;
			this.get_container_ul().addClass("jstree-ellipsis");
			/**
			 * triggered when ellisis is shown
			 * @event
			 * @name show_ellipsis.jstree
			 */
			this.trigger('show_ellipsis');
		},
		/**
		 * hide the node ellipsis
		 * @name hide_ellipsis()
		 */
		hide_ellipsis : function () {
			this._data.core.themes.ellipsis = false;
			this.get_container_ul().removeClass("jstree-ellipsis");
			/**
			 * triggered when ellisis is hidden
			 * @event
			 * @name hide_ellipsis.jstree
			 */
			this.trigger('hide_ellipsis');
		},
		/**
		 * toggle the node ellipsis
		 * @name toggle_icons()
		 */
		toggle_ellipsis : function () { if(this._data.core.themes.ellipsis) { this.hide_ellipsis(); } else { this.show_ellipsis(); } },
		/**
		 * set the node icon for a node
		 * @name set_icon(obj, icon)
		 * @param {mixed} obj
		 * @param {String} icon the new icon - can be a path to an icon or a className, if using an image that is in the current directory use a `./` prefix, otherwise it will be detected as a class
		 */
		set_icon : function (obj, icon) {
			var t1, t2, dom, old;
			if($.isArray(obj)) {
				obj = obj.slice();
				for(t1 = 0, t2 = obj.length; t1 < t2; t1++) {
					this.set_icon(obj[t1], icon);
				}
				return true;
			}
			obj = this.get_node(obj);
			if(!obj || obj.id === $.jstree.root) { return false; }
			old = obj.icon;
			obj.icon = icon === true || icon === null || icon === undefined || icon === '' ? true : icon;
			dom = this.get_node(obj, true).children(".jstree-anchor").children(".jstree-themeicon");
			if(icon === false) {
				dom.removeClass('jstree-themeicon-custom ' + old).css("background","").removeAttr("rel");
				this.hide_icon(obj);
			}
			else if(icon === true || icon === null || icon === undefined || icon === '') {
				dom.removeClass('jstree-themeicon-custom ' + old).css("background","").removeAttr("rel");
				if(old === false) { this.show_icon(obj); }
			}
			else if(icon.indexOf("/") === -1 && icon.indexOf(".") === -1) {
				dom.removeClass(old).css("background","");
				dom.addClass(icon + ' jstree-themeicon-custom').attr("rel",icon);
				if(old === false) { this.show_icon(obj); }
			}
			else {
				dom.removeClass(old).css("background","");
				dom.addClass('jstree-themeicon-custom').css("background", "url('" + icon + "') center center no-repeat").attr("rel",icon);
				if(old === false) { this.show_icon(obj); }
			}
			return true;
		},
		/**
		 * get the node icon for a node
		 * @name get_icon(obj)
		 * @param {mixed} obj
		 * @return {String}
		 */
		get_icon : function (obj) {
			obj = this.get_node(obj);
			return (!obj || obj.id === $.jstree.root) ? false : obj.icon;
		},
		/**
		 * hide the icon on an individual node
		 * @name hide_icon(obj)
		 * @param {mixed} obj
		 */
		hide_icon : function (obj) {
			var t1, t2;
			if($.isArray(obj)) {
				obj = obj.slice();
				for(t1 = 0, t2 = obj.length; t1 < t2; t1++) {
					this.hide_icon(obj[t1]);
				}
				return true;
			}
			obj = this.get_node(obj);
			if(!obj || obj === $.jstree.root) { return false; }
			obj.icon = false;
			this.get_node(obj, true).children(".jstree-anchor").children(".jstree-themeicon").addClass('jstree-themeicon-hidden');
			return true;
		},
		/**
		 * show the icon on an individual node
		 * @name show_icon(obj)
		 * @param {mixed} obj
		 */
		show_icon : function (obj) {
			var t1, t2, dom;
			if($.isArray(obj)) {
				obj = obj.slice();
				for(t1 = 0, t2 = obj.length; t1 < t2; t1++) {
					this.show_icon(obj[t1]);
				}
				return true;
			}
			obj = this.get_node(obj);
			if(!obj || obj === $.jstree.root) { return false; }
			dom = this.get_node(obj, true);
			obj.icon = dom.length ? dom.children(".jstree-anchor").children(".jstree-themeicon").attr('rel') : true;
			if(!obj.icon) { obj.icon = true; }
			dom.children(".jstree-anchor").children(".jstree-themeicon").removeClass('jstree-themeicon-hidden');
			return true;
		}
	};

	// helpers
	$.vakata = {};
	// collect attributes
	$.vakata.attributes = function(node, with_values) {
		node = $(node)[0];
		var attr = with_values ? {} : [];
		if(node && node.attributes) {
			$.each(node.attributes, function (i, v) {
				if($.inArray(v.name.toLowerCase(),['style','contenteditable','hasfocus','tabindex']) !== -1) { return; }
				if(v.value !== null && $.trim(v.value) !== '') {
					if(with_values) { attr[v.name] = v.value; }
					else { attr.push(v.name); }
				}
			});
		}
		return attr;
	};
	$.vakata.array_unique = function(array) {
		var a = [], i, j, l, o = {};
		for(i = 0, l = array.length; i < l; i++) {
			if(o[array[i]] === undefined) {
				a.push(array[i]);
				o[array[i]] = true;
			}
		}
		return a;
	};
	// remove item from array
	$.vakata.array_remove = function(array, from) {
		array.splice(from, 1);
		return array;
		//var rest = array.slice((to || from) + 1 || array.length);
		//array.length = from < 0 ? array.length + from : from;
		//array.push.apply(array, rest);
		//return array;
	};
	// remove item from array
	$.vakata.array_remove_item = function(array, item) {
		var tmp = $.inArray(item, array);
		return tmp !== -1 ? $.vakata.array_remove(array, tmp) : array;
	};
	$.vakata.array_filter = function(c,a,b,d,e) {
		if (c.filter) {
			return c.filter(a, b);
		}
		d=[];
		for (e in c) {
			if (~~e+''===e+'' && e>=0 && a.call(b,c[e],+e,c)) {
				d.push(c[e]);
			}
		}
		return d;
	};


/**
 * ### Changed plugin
 *
 * This plugin adds more information to the `changed.jstree` event. The new data is contained in the `changed` event data property, and contains a lists of `selected` and `deselected` nodes.
 */

	$.jstree.plugins.changed = function (options, parent) {
		var last = [];
		this.trigger = function (ev, data) {
			var i, j;
			if(!data) {
				data = {};
			}
			if(ev.replace('.jstree','') === 'changed') {
				data.changed = { selected : [], deselected : [] };
				var tmp = {};
				for(i = 0, j = last.length; i < j; i++) {
					tmp[last[i]] = 1;
				}
				for(i = 0, j = data.selected.length; i < j; i++) {
					if(!tmp[data.selected[i]]) {
						data.changed.selected.push(data.selected[i]);
					}
					else {
						tmp[data.selected[i]] = 2;
					}
				}
				for(i = 0, j = last.length; i < j; i++) {
					if(tmp[last[i]] === 1) {
						data.changed.deselected.push(last[i]);
					}
				}
				last = data.selected.slice();
			}
			/**
			 * triggered when selection changes (the "changed" plugin enhances the original event with more data)
			 * @event
			 * @name changed.jstree
			 * @param {Object} node
			 * @param {Object} action the action that caused the selection to change
			 * @param {Array} selected the current selection
			 * @param {Object} changed an object containing two properties `selected` and `deselected` - both arrays of node IDs, which were selected or deselected since the last changed event
			 * @param {Object} event the event (if any) that triggered this changed event
			 * @plugin changed
			 */
			parent.trigger.call(this, ev, data);
		};
		this.refresh = function (skip_loading, forget_state) {
			last = [];
			return parent.refresh.apply(this, arguments);
		};
	};

/**
 * ### Checkbox plugin
 *
 * This plugin renders checkbox icons in front of each node, making multiple selection much easier.
 * It also supports tri-state behavior, meaning that if a node has a few of its children checked it will be rendered as undetermined, and state will be propagated up.
 */

	var _i = document.createElement('I');
	_i.className = 'jstree-icon jstree-checkbox';
	_i.setAttribute('role', 'presentation');
	/**
	 * stores all defaults for the checkbox plugin
	 * @name $.jstree.defaults.checkbox
	 * @plugin checkbox
	 */
	$.jstree.defaults.checkbox = {
		/**
		 * a boolean indicating if checkboxes should be visible (can be changed at a later time using `show_checkboxes()` and `hide_checkboxes`). Defaults to `true`.
		 * @name $.jstree.defaults.checkbox.visible
		 * @plugin checkbox
		 */
		visible				: true,
		/**
		 * a boolean indicating if checkboxes should cascade down and have an undetermined state. Defaults to `true`.
		 * @name $.jstree.defaults.checkbox.three_state
		 * @plugin checkbox
		 */
		three_state			: true,
		/**
		 * a boolean indicating if clicking anywhere on the node should act as clicking on the checkbox. Defaults to `true`.
		 * @name $.jstree.defaults.checkbox.whole_node
		 * @plugin checkbox
		 */
		whole_node			: true,
		/**
		 * a boolean indicating if the selected style of a node should be kept, or removed. Defaults to `true`.
		 * @name $.jstree.defaults.checkbox.keep_selected_style
		 * @plugin checkbox
		 */
		keep_selected_style	: true,
		/**
		 * This setting controls how cascading and undetermined nodes are applied.
		 * If 'up' is in the string - cascading up is enabled, if 'down' is in the string - cascading down is enabled, if 'undetermined' is in the string - undetermined nodes will be used.
		 * If `three_state` is set to `true` this setting is automatically set to 'up+down+undetermined'. Defaults to ''.
		 * @name $.jstree.defaults.checkbox.cascade
		 * @plugin checkbox
		 */
		cascade				: '',
		/**
		 * This setting controls if checkbox are bound to the general tree selection or to an internal array maintained by the checkbox plugin. Defaults to `true`, only set to `false` if you know exactly what you are doing.
		 * @name $.jstree.defaults.checkbox.tie_selection
		 * @plugin checkbox
		 */
		tie_selection		: true,

		/**
		 * This setting controls if cascading down affects disabled checkboxes
		 * @name $.jstree.defaults.checkbox.cascade_to_disabled
		 * @plugin checkbox
		 */
		cascade_to_disabled : true,

		/**
		 * This setting controls if cascading down affects hidden checkboxes
		 * @name $.jstree.defaults.checkbox.cascade_to_hidden
		 * @plugin checkbox
		 */
		cascade_to_hidden : true
	};
	$.jstree.plugins.checkbox = function (options, parent) {
		this.bind = function () {
			parent.bind.call(this);
			this._data.checkbox.uto = false;
			this._data.checkbox.selected = [];
			if(this.settings.checkbox.three_state) {
				this.settings.checkbox.cascade = 'up+down+undetermined';
			}
			this.element
				.on("init.jstree", $.proxy(function () {
						this._data.checkbox.visible = this.settings.checkbox.visible;
						if(!this.settings.checkbox.keep_selected_style) {
							this.element.addClass('jstree-checkbox-no-clicked');
						}
						if(this.settings.checkbox.tie_selection) {
							this.element.addClass('jstree-checkbox-selection');
						}
					}, this))
				.on("loading.jstree", $.proxy(function () {
						this[ this._data.checkbox.visible ? 'show_checkboxes' : 'hide_checkboxes' ]();
					}, this));
			if(this.settings.checkbox.cascade.indexOf('undetermined') !== -1) {
				this.element
					.on('changed.jstree uncheck_node.jstree check_node.jstree uncheck_all.jstree check_all.jstree move_node.jstree copy_node.jstree redraw.jstree open_node.jstree', $.proxy(function () {
							// only if undetermined is in setting
							if(this._data.checkbox.uto) { clearTimeout(this._data.checkbox.uto); }
							this._data.checkbox.uto = setTimeout($.proxy(this._undetermined, this), 50);
						}, this));
			}
			if(!this.settings.checkbox.tie_selection) {
				this.element
					.on('model.jstree', $.proxy(function (e, data) {
						var m = this._model.data,
							p = m[data.parent],
							dpc = data.nodes,
							i, j;
						for(i = 0, j = dpc.length; i < j; i++) {
							m[dpc[i]].state.checked = m[dpc[i]].state.checked || (m[dpc[i]].original && m[dpc[i]].original.state && m[dpc[i]].original.state.checked);
							if(m[dpc[i]].state.checked) {
								this._data.checkbox.selected.push(dpc[i]);
							}
						}
					}, this));
			}
			if(this.settings.checkbox.cascade.indexOf('up') !== -1 || this.settings.checkbox.cascade.indexOf('down') !== -1) {
				this.element
					.on('model.jstree', $.proxy(function (e, data) {
							var m = this._model.data,
								p = m[data.parent],
								dpc = data.nodes,
								chd = [],
								c, i, j, k, l, tmp, s = this.settings.checkbox.cascade, t = this.settings.checkbox.tie_selection;

							if(s.indexOf('down') !== -1) {
								// apply down
								if(p.state[ t ? 'selected' : 'checked' ]) {
									for(i = 0, j = dpc.length; i < j; i++) {
										m[dpc[i]].state[ t ? 'selected' : 'checked' ] = true;
									}

									this._data[ t ? 'core' : 'checkbox' ].selected = this._data[ t ? 'core' : 'checkbox' ].selected.concat(dpc);
								}
								else {
									for(i = 0, j = dpc.length; i < j; i++) {
										if(m[dpc[i]].state[ t ? 'selected' : 'checked' ]) {
											for(k = 0, l = m[dpc[i]].children_d.length; k < l; k++) {
												m[m[dpc[i]].children_d[k]].state[ t ? 'selected' : 'checked' ] = true;
											}
											this._data[ t ? 'core' : 'checkbox' ].selected = this._data[ t ? 'core' : 'checkbox' ].selected.concat(m[dpc[i]].children_d);
										}
									}
								}
							}

							if(s.indexOf('up') !== -1) {
								// apply up
								for(i = 0, j = p.children_d.length; i < j; i++) {
									if(!m[p.children_d[i]].children.length) {
										chd.push(m[p.children_d[i]].parent);
									}
								}
								chd = $.vakata.array_unique(chd);
								for(k = 0, l = chd.length; k < l; k++) {
									p = m[chd[k]];
									while(p && p.id !== $.jstree.root) {
										c = 0;
										for(i = 0, j = p.children.length; i < j; i++) {
											c += m[p.children[i]].state[ t ? 'selected' : 'checked' ];
										}
										if(c === j) {
											p.state[ t ? 'selected' : 'checked' ] = true;
											this._data[ t ? 'core' : 'checkbox' ].selected.push(p.id);
											tmp = this.get_node(p, true);
											if(tmp && tmp.length) {
												tmp.attr('aria-selected', true).children('.jstree-anchor').addClass( t ? 'jstree-clicked' : 'jstree-checked');
											}
										}
										else {
											break;
										}
										p = this.get_node(p.parent);
									}
								}
							}

							this._data[ t ? 'core' : 'checkbox' ].selected = $.vakata.array_unique(this._data[ t ? 'core' : 'checkbox' ].selected);
						}, this))
					.on(this.settings.checkbox.tie_selection ? 'select_node.jstree' : 'check_node.jstree', $.proxy(function (e, data) {
							var self = this,
								obj = data.node,
								m = this._model.data,
								par = this.get_node(obj.parent),
								i, j, c, tmp, s = this.settings.checkbox.cascade, t = this.settings.checkbox.tie_selection,
								sel = {}, cur = this._data[ t ? 'core' : 'checkbox' ].selected;

							for (i = 0, j = cur.length; i < j; i++) {
								sel[cur[i]] = true;
							}

							// apply down
							if(s.indexOf('down') !== -1) {
								//this._data[ t ? 'core' : 'checkbox' ].selected = $.vakata.array_unique(this._data[ t ? 'core' : 'checkbox' ].selected.concat(obj.children_d));
								var selectedIds = this._cascade_new_checked_state(obj.id, true);
								var temp = obj.children_d.concat(obj.id);
								for (i = 0, j = temp.length; i < j; i++) {
									if (selectedIds.indexOf(temp[i]) > -1) {
										sel[temp[i]] = true;
									}
									else {
										delete sel[temp[i]];
									}
								}
							}

							// apply up
							if(s.indexOf('up') !== -1) {
								while(par && par.id !== $.jstree.root) {
									c = 0;
									for(i = 0, j = par.children.length; i < j; i++) {
										c += m[par.children[i]].state[ t ? 'selected' : 'checked' ];
									}
									if(c === j) {
										par.state[ t ? 'selected' : 'checked' ] = true;
										sel[par.id] = true;
										//this._data[ t ? 'core' : 'checkbox' ].selected.push(par.id);
										tmp = this.get_node(par, true);
										if(tmp && tmp.length) {
											tmp.attr('aria-selected', true).children('.jstree-anchor').addClass(t ? 'jstree-clicked' : 'jstree-checked');
										}
									}
									else {
										break;
									}
									par = this.get_node(par.parent);
								}
							}

							cur = [];
							for (i in sel) {
								if (sel.hasOwnProperty(i)) {
									cur.push(i);
								}
							}
							this._data[ t ? 'core' : 'checkbox' ].selected = cur;
						}, this))
					.on(this.settings.checkbox.tie_selection ? 'deselect_all.jstree' : 'uncheck_all.jstree', $.proxy(function (e, data) {
							var obj = this.get_node($.jstree.root),
								m = this._model.data,
								i, j, tmp;
							for(i = 0, j = obj.children_d.length; i < j; i++) {
								tmp = m[obj.children_d[i]];
								if(tmp && tmp.original && tmp.original.state && tmp.original.state.undetermined) {
									tmp.original.state.undetermined = false;
								}
							}
						}, this))
					.on(this.settings.checkbox.tie_selection ? 'deselect_node.jstree' : 'uncheck_node.jstree', $.proxy(function (e, data) {
							var self = this,
								obj = data.node,
								dom = this.get_node(obj, true),
								i, j, tmp, s = this.settings.checkbox.cascade, t = this.settings.checkbox.tie_selection,
								cur = this._data[ t ? 'core' : 'checkbox' ].selected, sel = {},
								stillSelectedIds = [],
								allIds = obj.children_d.concat(obj.id);

							// apply down
							if(s.indexOf('down') !== -1) {
								var selectedIds = this._cascade_new_checked_state(obj.id, false);

								cur = cur.filter(function(id) {
									return allIds.indexOf(id) === -1 || selectedIds.indexOf(id) > -1;
								});
							}

							// only apply up if cascade up is enabled and if this node is not selected
							// (if all child nodes are disabled and cascade_to_disabled === false then this node will till be selected).
							if(s.indexOf('up') !== -1 && cur.indexOf(obj.id) === -1) {
								for(i = 0, j = obj.parents.length; i < j; i++) {
									tmp = this._model.data[obj.parents[i]];
									tmp.state[ t ? 'selected' : 'checked' ] = false;
									if(tmp && tmp.original && tmp.original.state && tmp.original.state.undetermined) {
										tmp.original.state.undetermined = false;
									}
									tmp = this.get_node(obj.parents[i], true);
									if(tmp && tmp.length) {
										tmp.attr('aria-selected', false).children('.jstree-anchor').removeClass(t ? 'jstree-clicked' : 'jstree-checked');
									}
								}

								cur = cur.filter(function(id) {
									return obj.parents.indexOf(id) === -1;
								});
							}

							this._data[ t ? 'core' : 'checkbox' ].selected = cur;
						}, this));
			}
			if(this.settings.checkbox.cascade.indexOf('up') !== -1) {
				this.element
					.on('delete_node.jstree', $.proxy(function (e, data) {
							// apply up (whole handler)
							var p = this.get_node(data.parent),
								m = this._model.data,
								i, j, c, tmp, t = this.settings.checkbox.tie_selection;
							while(p && p.id !== $.jstree.root && !p.state[ t ? 'selected' : 'checked' ]) {
								c = 0;
								for(i = 0, j = p.children.length; i < j; i++) {
									c += m[p.children[i]].state[ t ? 'selected' : 'checked' ];
								}
								if(j > 0 && c === j) {
									p.state[ t ? 'selected' : 'checked' ] = true;
									this._data[ t ? 'core' : 'checkbox' ].selected.push(p.id);
									tmp = this.get_node(p, true);
									if(tmp && tmp.length) {
										tmp.attr('aria-selected', true).children('.jstree-anchor').addClass(t ? 'jstree-clicked' : 'jstree-checked');
									}
								}
								else {
									break;
								}
								p = this.get_node(p.parent);
							}
						}, this))
					.on('move_node.jstree', $.proxy(function (e, data) {
							// apply up (whole handler)
							var is_multi = data.is_multi,
								old_par = data.old_parent,
								new_par = this.get_node(data.parent),
								m = this._model.data,
								p, c, i, j, tmp, t = this.settings.checkbox.tie_selection;
							if(!is_multi) {
								p = this.get_node(old_par);
								while(p && p.id !== $.jstree.root && !p.state[ t ? 'selected' : 'checked' ]) {
									c = 0;
									for(i = 0, j = p.children.length; i < j; i++) {
										c += m[p.children[i]].state[ t ? 'selected' : 'checked' ];
									}
									if(j > 0 && c === j) {
										p.state[ t ? 'selected' : 'checked' ] = true;
										this._data[ t ? 'core' : 'checkbox' ].selected.push(p.id);
										tmp = this.get_node(p, true);
										if(tmp && tmp.length) {
											tmp.attr('aria-selected', true).children('.jstree-anchor').addClass(t ? 'jstree-clicked' : 'jstree-checked');
										}
									}
									else {
										break;
									}
									p = this.get_node(p.parent);
								}
							}
							p = new_par;
							while(p && p.id !== $.jstree.root) {
								c = 0;
								for(i = 0, j = p.children.length; i < j; i++) {
									c += m[p.children[i]].state[ t ? 'selected' : 'checked' ];
								}
								if(c === j) {
									if(!p.state[ t ? 'selected' : 'checked' ]) {
										p.state[ t ? 'selected' : 'checked' ] = true;
										this._data[ t ? 'core' : 'checkbox' ].selected.push(p.id);
										tmp = this.get_node(p, true);
										if(tmp && tmp.length) {
											tmp.attr('aria-selected', true).children('.jstree-anchor').addClass(t ? 'jstree-clicked' : 'jstree-checked');
										}
									}
								}
								else {
									if(p.state[ t ? 'selected' : 'checked' ]) {
										p.state[ t ? 'selected' : 'checked' ] = false;
										this._data[ t ? 'core' : 'checkbox' ].selected = $.vakata.array_remove_item(this._data[ t ? 'core' : 'checkbox' ].selected, p.id);
										tmp = this.get_node(p, true);
										if(tmp && tmp.length) {
											tmp.attr('aria-selected', false).children('.jstree-anchor').removeClass(t ? 'jstree-clicked' : 'jstree-checked');
										}
									}
									else {
										break;
									}
								}
								p = this.get_node(p.parent);
							}
						}, this));
			}
		};
		/**
		 * get an array of all nodes whose state is "undetermined"
		 * @name get_undetermined([full])
		 * @param  {boolean} full: if set to `true` the returned array will consist of the full node objects, otherwise - only IDs will be returned
		 * @return {Array}
		 * @plugin checkbox
		 */
		this.get_undetermined = function (full) {
			if (this.settings.checkbox.cascade.indexOf('undetermined') === -1) {
				return [];
			}
			var i, j, k, l, o = {}, m = this._model.data, t = this.settings.checkbox.tie_selection, s = this._data[ t ? 'core' : 'checkbox' ].selected, p = [], tt = this, r = [];
			for(i = 0, j = s.length; i < j; i++) {
				if(m[s[i]] && m[s[i]].parents) {
					for(k = 0, l = m[s[i]].parents.length; k < l; k++) {
						if(o[m[s[i]].parents[k]] !== undefined) {
							break;
						}
						if(m[s[i]].parents[k] !== $.jstree.root) {
							o[m[s[i]].parents[k]] = true;
							p.push(m[s[i]].parents[k]);
						}
					}
				}
			}
			// attempt for server side undetermined state
			this.element.find('.jstree-closed').not(':has(.jstree-children)')
				.each(function () {
					var tmp = tt.get_node(this), tmp2;
					
					if(!tmp) { return; }
					
					if(!tmp.state.loaded) {
						if(tmp.original && tmp.original.state && tmp.original.state.undetermined && tmp.original.state.undetermined === true) {
							if(o[tmp.id] === undefined && tmp.id !== $.jstree.root) {
								o[tmp.id] = true;
								p.push(tmp.id);
							}
							for(k = 0, l = tmp.parents.length; k < l; k++) {
								if(o[tmp.parents[k]] === undefined && tmp.parents[k] !== $.jstree.root) {
									o[tmp.parents[k]] = true;
									p.push(tmp.parents[k]);
								}
							}
						}
					}
					else {
						for(i = 0, j = tmp.children_d.length; i < j; i++) {
							tmp2 = m[tmp.children_d[i]];
							if(!tmp2.state.loaded && tmp2.original && tmp2.original.state && tmp2.original.state.undetermined && tmp2.original.state.undetermined === true) {
								if(o[tmp2.id] === undefined && tmp2.id !== $.jstree.root) {
									o[tmp2.id] = true;
									p.push(tmp2.id);
								}
								for(k = 0, l = tmp2.parents.length; k < l; k++) {
									if(o[tmp2.parents[k]] === undefined && tmp2.parents[k] !== $.jstree.root) {
										o[tmp2.parents[k]] = true;
										p.push(tmp2.parents[k]);
									}
								}
							}
						}
					}
				});
			for (i = 0, j = p.length; i < j; i++) {
				if(!m[p[i]].state[ t ? 'selected' : 'checked' ]) {
					r.push(full ? m[p[i]] : p[i]);
				}
			}
			return r;
		};
		/**
		 * set the undetermined state where and if necessary. Used internally.
		 * @private
		 * @name _undetermined()
		 * @plugin checkbox
		 */
		this._undetermined = function () {
			if(this.element === null) { return; }
			var p = this.get_undetermined(false), i, j, s;

			this.element.find('.jstree-undetermined').removeClass('jstree-undetermined');
			for (i = 0, j = p.length; i < j; i++) {
				s = this.get_node(p[i], true);
				if(s && s.length) {
					s.children('.jstree-anchor').children('.jstree-checkbox').addClass('jstree-undetermined');
				}
			}
		};
		this.redraw_node = function(obj, deep, is_callback, force_render) {
			obj = parent.redraw_node.apply(this, arguments);
			if(obj) {
				var i, j, tmp = null, icon = null;
				for(i = 0, j = obj.childNodes.length; i < j; i++) {
					if(obj.childNodes[i] && obj.childNodes[i].className && obj.childNodes[i].className.indexOf("jstree-anchor") !== -1) {
						tmp = obj.childNodes[i];
						break;
					}
				}
				if(tmp) {
					if(!this.settings.checkbox.tie_selection && this._model.data[obj.id].state.checked) { tmp.className += ' jstree-checked'; }
					icon = _i.cloneNode(false);
					if(this._model.data[obj.id].state.checkbox_disabled) { icon.className += ' jstree-checkbox-disabled'; }
					tmp.insertBefore(icon, tmp.childNodes[0]);
				}
			}
			if(!is_callback && this.settings.checkbox.cascade.indexOf('undetermined') !== -1) {
				if(this._data.checkbox.uto) { clearTimeout(this._data.checkbox.uto); }
				this._data.checkbox.uto = setTimeout($.proxy(this._undetermined, this), 50);
			}
			return obj;
		};
		/**
		 * show the node checkbox icons
		 * @name show_checkboxes()
		 * @plugin checkbox
		 */
		this.show_checkboxes = function () { this._data.core.themes.checkboxes = true; this.get_container_ul().removeClass("jstree-no-checkboxes"); };
		/**
		 * hide the node checkbox icons
		 * @name hide_checkboxes()
		 * @plugin checkbox
		 */
		this.hide_checkboxes = function () { this._data.core.themes.checkboxes = false; this.get_container_ul().addClass("jstree-no-checkboxes"); };
		/**
		 * toggle the node icons
		 * @name toggle_checkboxes()
		 * @plugin checkbox
		 */
		this.toggle_checkboxes = function () { if(this._data.core.themes.checkboxes) { this.hide_checkboxes(); } else { this.show_checkboxes(); } };
		/**
		 * checks if a node is in an undetermined state
		 * @name is_undetermined(obj)
		 * @param  {mixed} obj
		 * @return {Boolean}
		 */
		this.is_undetermined = function (obj) {
			obj = this.get_node(obj);
			var s = this.settings.checkbox.cascade, i, j, t = this.settings.checkbox.tie_selection, d = this._data[ t ? 'core' : 'checkbox' ].selected, m = this._model.data;
			if(!obj || obj.state[ t ? 'selected' : 'checked' ] === true || s.indexOf('undetermined') === -1 || (s.indexOf('down') === -1 && s.indexOf('up') === -1)) {
				return false;
			}
			if(!obj.state.loaded && obj.original.state.undetermined === true) {
				return true;
			}
			for(i = 0, j = obj.children_d.length; i < j; i++) {
				if($.inArray(obj.children_d[i], d) !== -1 || (!m[obj.children_d[i]].state.loaded && m[obj.children_d[i]].original.state.undetermined)) {
					return true;
				}
			}
			return false;
		};
		/**
		 * disable a node's checkbox
		 * @name disable_checkbox(obj)
		 * @param {mixed} obj an array can be used too
		 * @trigger disable_checkbox.jstree
		 * @plugin checkbox
		 */
		this.disable_checkbox = function (obj) {
			var t1, t2, dom;
			if($.isArray(obj)) {
				obj = obj.slice();
				for(t1 = 0, t2 = obj.length; t1 < t2; t1++) {
					this.disable_checkbox(obj[t1]);
				}
				return true;
			}
			obj = this.get_node(obj);
			if(!obj || obj.id === $.jstree.root) {
				return false;
			}
			dom = this.get_node(obj, true);
			if(!obj.state.checkbox_disabled) {
				obj.state.checkbox_disabled = true;
				if(dom && dom.length) {
					dom.children('.jstree-anchor').children('.jstree-checkbox').addClass('jstree-checkbox-disabled');
				}
				/**
				 * triggered when an node's checkbox is disabled
				 * @event
				 * @name disable_checkbox.jstree
				 * @param {Object} node
				 * @plugin checkbox
				 */
				this.trigger('disable_checkbox', { 'node' : obj });
			}
		};
		/**
		 * enable a node's checkbox
		 * @name disable_checkbox(obj)
		 * @param {mixed} obj an array can be used too
		 * @trigger enable_checkbox.jstree
		 * @plugin checkbox
		 */
		this.enable_checkbox = function (obj) {
			var t1, t2, dom;
			if($.isArray(obj)) {
				obj = obj.slice();
				for(t1 = 0, t2 = obj.length; t1 < t2; t1++) {
					this.enable_checkbox(obj[t1]);
				}
				return true;
			}
			obj = this.get_node(obj);
			if(!obj || obj.id === $.jstree.root) {
				return false;
			}
			dom = this.get_node(obj, true);
			if(obj.state.checkbox_disabled) {
				obj.state.checkbox_disabled = false;
				if(dom && dom.length) {
					dom.children('.jstree-anchor').children('.jstree-checkbox').removeClass('jstree-checkbox-disabled');
				}
				/**
				 * triggered when an node's checkbox is enabled
				 * @event
				 * @name enable_checkbox.jstree
				 * @param {Object} node
				 * @plugin checkbox
				 */
				this.trigger('enable_checkbox', { 'node' : obj });
			}
		};

		this.activate_node = function (obj, e) {
			if($(e.target).hasClass('jstree-checkbox-disabled')) {
				return false;
			}
			if(this.settings.checkbox.tie_selection && (this.settings.checkbox.whole_node || $(e.target).hasClass('jstree-checkbox'))) {
				e.ctrlKey = true;
			}
			if(this.settings.checkbox.tie_selection || (!this.settings.checkbox.whole_node && !$(e.target).hasClass('jstree-checkbox'))) {
				return parent.activate_node.call(this, obj, e);
			}
			if(this.is_disabled(obj)) {
				return false;
			}
			if(this.is_checked(obj)) {
				this.uncheck_node(obj, e);
			}
			else {
				this.check_node(obj, e);
			}
			this.trigger('activate_node', { 'node' : this.get_node(obj) });
		};

		/**
		 * Cascades checked state to a node and all its descendants. This function does NOT affect hidden and disabled nodes (or their descendants).
		 * However if these unaffected nodes are already selected their ids will be included in the returned array.
		 * @private
		 * @param {string} id the node ID
		 * @param {bool} checkedState should the nodes be checked or not
		 * @returns {Array} Array of all node id's (in this tree branch) that are checked.
		 */
		this._cascade_new_checked_state = function (id, checkedState) {
			var self = this;
			var t = this.settings.checkbox.tie_selection;
			var node = this._model.data[id];
			var selectedNodeIds = [];
			var selectedChildrenIds = [], i, j, selectedChildIds;

			if (
				(this.settings.checkbox.cascade_to_disabled || !node.state.disabled) &&
				(this.settings.checkbox.cascade_to_hidden || !node.state.hidden)
			) {
				//First try and check/uncheck the children
				if (node.children) {
					for (i = 0, j = node.children.length; i < j; i++) {
						var childId = node.children[i];
						selectedChildIds = self._cascade_new_checked_state(childId, checkedState);
						selectedNodeIds = selectedNodeIds.concat(selectedChildIds);
						if (selectedChildIds.indexOf(childId) > -1) {
							selectedChildrenIds.push(childId);
						}
					}
				}

				var dom = self.get_node(node, true);

				//A node's state is undetermined if some but not all of it's children are checked/selected .
				var undetermined = selectedChildrenIds.length > 0 && selectedChildrenIds.length < node.children.length;

				if(node.original && node.original.state && node.original.state.undetermined) {
					node.original.state.undetermined = undetermined;
				}

				//If a node is undetermined then remove selected class
				if (undetermined) {
					node.state[ t ? 'selected' : 'checked' ] = false;
					dom.attr('aria-selected', false).children('.jstree-anchor').removeClass(t ? 'jstree-clicked' : 'jstree-checked');
				}
				//Otherwise, if the checkedState === true (i.e. the node is being checked now) and all of the node's children are checked (if it has any children),
				//check the node and style it correctly.
				else if (checkedState && selectedChildrenIds.length === node.children.length) {
					node.state[ t ? 'selected' : 'checked' ] = checkedState;
					selectedNodeIds.push(node.id);

					dom.attr('aria-selected', true).children('.jstree-anchor').addClass(t ? 'jstree-clicked' : 'jstree-checked');
				}
				else {
					node.state[ t ? 'selected' : 'checked' ] = false;
					dom.attr('aria-selected', false).children('.jstree-anchor').removeClass(t ? 'jstree-clicked' : 'jstree-checked');
				}
			}
			else {
				selectedChildIds = this.get_checked_descendants(id);

				if (node.state[ t ? 'selected' : 'checked' ]) {
					selectedChildIds.push(node.id);
				}

				selectedNodeIds = selectedNodeIds.concat(selectedChildIds);
			}

			return selectedNodeIds;
		};

		/**
		 * Gets ids of nodes selected in branch (of tree) specified by id (does not include the node specified by id)
		 * @name get_checked_descendants(obj)
		 * @param {string} id the node ID
		 * @return {Array} array of IDs
		 * @plugin checkbox
		 */
		this.get_checked_descendants = function (id) {
			var self = this;
			var t = self.settings.checkbox.tie_selection;
			var node = self._model.data[id];

			return node.children_d.filter(function(_id) {
				return self._model.data[_id].state[ t ? 'selected' : 'checked' ];
			});
		};

		/**
		 * check a node (only if tie_selection in checkbox settings is false, otherwise select_node will be called internally)
		 * @name check_node(obj)
		 * @param {mixed} obj an array can be used to check multiple nodes
		 * @trigger check_node.jstree
		 * @plugin checkbox
		 */
		this.check_node = function (obj, e) {
			if(this.settings.checkbox.tie_selection) { return this.select_node(obj, false, true, e); }
			var dom, t1, t2, th;
			if($.isArray(obj)) {
				obj = obj.slice();
				for(t1 = 0, t2 = obj.length; t1 < t2; t1++) {
					this.check_node(obj[t1], e);
				}
				return true;
			}
			obj = this.get_node(obj);
			if(!obj || obj.id === $.jstree.root) {
				return false;
			}
			dom = this.get_node(obj, true);
			if(!obj.state.checked) {
				obj.state.checked = true;
				this._data.checkbox.selected.push(obj.id);
				if(dom && dom.length) {
					dom.children('.jstree-anchor').addClass('jstree-checked');
				}
				/**
				 * triggered when an node is checked (only if tie_selection in checkbox settings is false)
				 * @event
				 * @name check_node.jstree
				 * @param {Object} node
				 * @param {Array} selected the current selection
				 * @param {Object} event the event (if any) that triggered this check_node
				 * @plugin checkbox
				 */
				this.trigger('check_node', { 'node' : obj, 'selected' : this._data.checkbox.selected, 'event' : e });
			}
		};
		/**
		 * uncheck a node (only if tie_selection in checkbox settings is false, otherwise deselect_node will be called internally)
		 * @name uncheck_node(obj)
		 * @param {mixed} obj an array can be used to uncheck multiple nodes
		 * @trigger uncheck_node.jstree
		 * @plugin checkbox
		 */
		this.uncheck_node = function (obj, e) {
			if(this.settings.checkbox.tie_selection) { return this.deselect_node(obj, false, e); }
			var t1, t2, dom;
			if($.isArray(obj)) {
				obj = obj.slice();
				for(t1 = 0, t2 = obj.length; t1 < t2; t1++) {
					this.uncheck_node(obj[t1], e);
				}
				return true;
			}
			obj = this.get_node(obj);
			if(!obj || obj.id === $.jstree.root) {
				return false;
			}
			dom = this.get_node(obj, true);
			if(obj.state.checked) {
				obj.state.checked = false;
				this._data.checkbox.selected = $.vakata.array_remove_item(this._data.checkbox.selected, obj.id);
				if(dom.length) {
					dom.children('.jstree-anchor').removeClass('jstree-checked');
				}
				/**
				 * triggered when an node is unchecked (only if tie_selection in checkbox settings is false)
				 * @event
				 * @name uncheck_node.jstree
				 * @param {Object} node
				 * @param {Array} selected the current selection
				 * @param {Object} event the event (if any) that triggered this uncheck_node
				 * @plugin checkbox
				 */
				this.trigger('uncheck_node', { 'node' : obj, 'selected' : this._data.checkbox.selected, 'event' : e });
			}
		};
		
		/**
		 * checks all nodes in the tree (only if tie_selection in checkbox settings is false, otherwise select_all will be called internally)
		 * @name check_all()
		 * @trigger check_all.jstree, changed.jstree
		 * @plugin checkbox
		 */
		this.check_all = function () {
			if(this.settings.checkbox.tie_selection) { return this.select_all(); }
			var tmp = this._data.checkbox.selected.concat([]), i, j;
			this._data.checkbox.selected = this._model.data[$.jstree.root].children_d.concat();
			for(i = 0, j = this._data.checkbox.selected.length; i < j; i++) {
				if(this._model.data[this._data.checkbox.selected[i]]) {
					this._model.data[this._data.checkbox.selected[i]].state.checked = true;
				}
			}
			this.redraw(true);
			/**
			 * triggered when all nodes are checked (only if tie_selection in checkbox settings is false)
			 * @event
			 * @name check_all.jstree
			 * @param {Array} selected the current selection
			 * @plugin checkbox
			 */
			this.trigger('check_all', { 'selected' : this._data.checkbox.selected });
		};
		/**
		 * uncheck all checked nodes (only if tie_selection in checkbox settings is false, otherwise deselect_all will be called internally)
		 * @name uncheck_all()
		 * @trigger uncheck_all.jstree
		 * @plugin checkbox
		 */
		this.uncheck_all = function () {
			if(this.settings.checkbox.tie_selection) { return this.deselect_all(); }
			var tmp = this._data.checkbox.selected.concat([]), i, j;
			for(i = 0, j = this._data.checkbox.selected.length; i < j; i++) {
				if(this._model.data[this._data.checkbox.selected[i]]) {
					this._model.data[this._data.checkbox.selected[i]].state.checked = false;
				}
			}
			this._data.checkbox.selected = [];
			this.element.find('.jstree-checked').removeClass('jstree-checked');
			/**
			 * triggered when all nodes are unchecked (only if tie_selection in checkbox settings is false)
			 * @event
			 * @name uncheck_all.jstree
			 * @param {Object} node the previous selection
			 * @param {Array} selected the current selection
			 * @plugin checkbox
			 */
			this.trigger('uncheck_all', { 'selected' : this._data.checkbox.selected, 'node' : tmp });
		};
		/**
		 * checks if a node is checked (if tie_selection is on in the settings this function will return the same as is_selected)
		 * @name is_checked(obj)
		 * @param  {mixed}  obj
		 * @return {Boolean}
		 * @plugin checkbox
		 */
		this.is_checked = function (obj) {
			if(this.settings.checkbox.tie_selection) { return this.is_selected(obj); }
			obj = this.get_node(obj);
			if(!obj || obj.id === $.jstree.root) { return false; }
			return obj.state.checked;
		};
		/**
		 * get an array of all checked nodes (if tie_selection is on in the settings this function will return the same as get_selected)
		 * @name get_checked([full])
		 * @param  {mixed}  full if set to `true` the returned array will consist of the full node objects, otherwise - only IDs will be returned
		 * @return {Array}
		 * @plugin checkbox
		 */
		this.get_checked = function (full) {
			if(this.settings.checkbox.tie_selection) { return this.get_selected(full); }
			return full ? $.map(this._data.checkbox.selected, $.proxy(function (i) { return this.get_node(i); }, this)) : this._data.checkbox.selected;
		};
		/**
		 * get an array of all top level checked nodes (ignoring children of checked nodes) (if tie_selection is on in the settings this function will return the same as get_top_selected)
		 * @name get_top_checked([full])
		 * @param  {mixed}  full if set to `true` the returned array will consist of the full node objects, otherwise - only IDs will be returned
		 * @return {Array}
		 * @plugin checkbox
		 */
		this.get_top_checked = function (full) {
			if(this.settings.checkbox.tie_selection) { return this.get_top_selected(full); }
			var tmp = this.get_checked(true),
				obj = {}, i, j, k, l;
			for(i = 0, j = tmp.length; i < j; i++) {
				obj[tmp[i].id] = tmp[i];
			}
			for(i = 0, j = tmp.length; i < j; i++) {
				for(k = 0, l = tmp[i].children_d.length; k < l; k++) {
					if(obj[tmp[i].children_d[k]]) {
						delete obj[tmp[i].children_d[k]];
					}
				}
			}
			tmp = [];
			for(i in obj) {
				if(obj.hasOwnProperty(i)) {
					tmp.push(i);
				}
			}
			return full ? $.map(tmp, $.proxy(function (i) { return this.get_node(i); }, this)) : tmp;
		};
		/**
		 * get an array of all bottom level checked nodes (ignoring selected parents) (if tie_selection is on in the settings this function will return the same as get_bottom_selected)
		 * @name get_bottom_checked([full])
		 * @param  {mixed}  full if set to `true` the returned array will consist of the full node objects, otherwise - only IDs will be returned
		 * @return {Array}
		 * @plugin checkbox
		 */
		this.get_bottom_checked = function (full) {
			if(this.settings.checkbox.tie_selection) { return this.get_bottom_selected(full); }
			var tmp = this.get_checked(true),
				obj = [], i, j;
			for(i = 0, j = tmp.length; i < j; i++) {
				if(!tmp[i].children.length) {
					obj.push(tmp[i].id);
				}
			}
			return full ? $.map(obj, $.proxy(function (i) { return this.get_node(i); }, this)) : obj;
		};
		this.load_node = function (obj, callback) {
			var k, l, i, j, c, tmp;
			if(!$.isArray(obj) && !this.settings.checkbox.tie_selection) {
				tmp = this.get_node(obj);
				if(tmp && tmp.state.loaded) {
					for(k = 0, l = tmp.children_d.length; k < l; k++) {
						if(this._model.data[tmp.children_d[k]].state.checked) {
							c = true;
							this._data.checkbox.selected = $.vakata.array_remove_item(this._data.checkbox.selected, tmp.children_d[k]);
						}
					}
				}
			}
			return parent.load_node.apply(this, arguments);
		};
		this.get_state = function () {
			var state = parent.get_state.apply(this, arguments);
			if(this.settings.checkbox.tie_selection) { return state; }
			state.checkbox = this._data.checkbox.selected.slice();
			return state;
		};
		this.set_state = function (state, callback) {
			var res = parent.set_state.apply(this, arguments);
			if(res && state.checkbox) {
				if(!this.settings.checkbox.tie_selection) {
					this.uncheck_all();
					var _this = this;
					$.each(state.checkbox, function (i, v) {
						_this.check_node(v);
					});
				}
				delete state.checkbox;
				this.set_state(state, callback);
				return false;
			}
			return res;
		};
		this.refresh = function (skip_loading, forget_state) {
			if(this.settings.checkbox.tie_selection) {
				this._data.checkbox.selected = [];
			}
			return parent.refresh.apply(this, arguments);
		};
	};

	// include the checkbox plugin by default
	// $.jstree.defaults.plugins.push("checkbox");


/**
 * ### Conditionalselect plugin
 *
 * This plugin allows defining a callback to allow or deny node selection by user input (activate node method).
 */

	/**
	 * a callback (function) which is invoked in the instance's scope and receives two arguments - the node and the event that triggered the `activate_node` call. Returning false prevents working with the node, returning true allows invoking activate_node. Defaults to returning `true`.
	 * @name $.jstree.defaults.checkbox.visible
	 * @plugin checkbox
	 */
	$.jstree.defaults.conditionalselect = function () { return true; };
	$.jstree.plugins.conditionalselect = function (options, parent) {
		// own function
		this.activate_node = function (obj, e) {
			if(this.settings.conditionalselect.call(this, this.get_node(obj), e)) {
				return parent.activate_node.call(this, obj, e);
			}
		};
	};


/**
 * ### Contextmenu plugin
 *
 * Shows a context menu when a node is right-clicked.
 */

	/**
	 * stores all defaults for the contextmenu plugin
	 * @name $.jstree.defaults.contextmenu
	 * @plugin contextmenu
	 */
	$.jstree.defaults.contextmenu = {
		/**
		 * a boolean indicating if the node should be selected when the context menu is invoked on it. Defaults to `true`.
		 * @name $.jstree.defaults.contextmenu.select_node
		 * @plugin contextmenu
		 */
		select_node : true,
		/**
		 * a boolean indicating if the menu should be shown aligned with the node. Defaults to `true`, otherwise the mouse coordinates are used.
		 * @name $.jstree.defaults.contextmenu.show_at_node
		 * @plugin contextmenu
		 */
		show_at_node : true,
		/**
		 * an object of actions, or a function that accepts a node and a callback function and calls the callback function with an object of actions available for that node (you can also return the items too).
		 *
		 * Each action consists of a key (a unique name) and a value which is an object with the following properties (only label and action are required). Once a menu item is activated the `action` function will be invoked with an object containing the following keys: item - the contextmenu item definition as seen below, reference - the DOM node that was used (the tree node), element - the contextmenu DOM element, position - an object with x/y properties indicating the position of the menu.
		 *
		 * * `separator_before` - a boolean indicating if there should be a separator before this item
		 * * `separator_after` - a boolean indicating if there should be a separator after this item
		 * * `_disabled` - a boolean indicating if this action should be disabled
		 * * `label` - a string - the name of the action (could be a function returning a string)
		 * * `title` - a string - an optional tooltip for the item
		 * * `action` - a function to be executed if this item is chosen, the function will receive 
		 * * `icon` - a string, can be a path to an icon or a className, if using an image that is in the current directory use a `./` prefix, otherwise it will be detected as a class
		 * * `shortcut` - keyCode which will trigger the action if the menu is open (for example `113` for rename, which equals F2)
		 * * `shortcut_label` - shortcut label (like for example `F2` for rename)
		 * * `submenu` - an object with the same structure as $.jstree.defaults.contextmenu.items which can be used to create a submenu - each key will be rendered as a separate option in a submenu that will appear once the current item is hovered
		 *
		 * @name $.jstree.defaults.contextmenu.items
		 * @plugin contextmenu
		 */
		items : function (o, cb) { // Could be an object directly
			return {
				"create" : {
					"separator_before"	: false,
					"separator_after"	: true,
					"_disabled"			: false, //(this.check("create_node", data.reference, {}, "last")),
					"label"				: "Create",
					"action"			: function (data) {
						var inst = $.jstree.reference(data.reference),
							obj = inst.get_node(data.reference);
						inst.create_node(obj, {}, "last", function (new_node) {
							try {
								inst.edit(new_node);
							} catch (ex) {
								setTimeout(function () { inst.edit(new_node); },0);
							}
						});
					}
				},
				"rename" : {
					"separator_before"	: false,
					"separator_after"	: false,
					"_disabled"			: false, //(this.check("rename_node", data.reference, this.get_parent(data.reference), "")),
					"label"				: "Rename",
					/*!
					"shortcut"			: 113,
					"shortcut_label"	: 'F2',
					"icon"				: "glyphicon glyphicon-leaf",
					*/
					"action"			: function (data) {
						var inst = $.jstree.reference(data.reference),
							obj = inst.get_node(data.reference);
						inst.edit(obj);
					}
				},
				"remove" : {
					"separator_before"	: false,
					"icon"				: false,
					"separator_after"	: false,
					"_disabled"			: false, //(this.check("delete_node", data.reference, this.get_parent(data.reference), "")),
					"label"				: "Delete",
					"action"			: function (data) {
						var inst = $.jstree.reference(data.reference),
							obj = inst.get_node(data.reference);
						if(inst.is_selected(obj)) {
							inst.delete_node(inst.get_selected());
						}
						else {
							inst.delete_node(obj);
						}
					}
				},
				"ccp" : {
					"separator_before"	: true,
					"icon"				: false,
					"separator_after"	: false,
					"label"				: "Edit",
					"action"			: false,
					"submenu" : {
						"cut" : {
							"separator_before"	: false,
							"separator_after"	: false,
							"label"				: "Cut",
							"action"			: function (data) {
								var inst = $.jstree.reference(data.reference),
									obj = inst.get_node(data.reference);
								if(inst.is_selected(obj)) {
									inst.cut(inst.get_top_selected());
								}
								else {
									inst.cut(obj);
								}
							}
						},
						"copy" : {
							"separator_before"	: false,
							"icon"				: false,
							"separator_after"	: false,
							"label"				: "Copy",
							"action"			: function (data) {
								var inst = $.jstree.reference(data.reference),
									obj = inst.get_node(data.reference);
								if(inst.is_selected(obj)) {
									inst.copy(inst.get_top_selected());
								}
								else {
									inst.copy(obj);
								}
							}
						},
						"paste" : {
							"separator_before"	: false,
							"icon"				: false,
							"_disabled"			: function (data) {
								return !$.jstree.reference(data.reference).can_paste();
							},
							"separator_after"	: false,
							"label"				: "Paste",
							"action"			: function (data) {
								var inst = $.jstree.reference(data.reference),
									obj = inst.get_node(data.reference);
								inst.paste(obj);
							}
						}
					}
				}
			};
		}
	};

	$.jstree.plugins.contextmenu = function (options, parent) {
		this.bind = function () {
			parent.bind.call(this);

			var last_ts = 0, cto = null, ex, ey;
			this.element
				.on("init.jstree loading.jstree ready.jstree", $.proxy(function () {
						this.get_container_ul().addClass('jstree-contextmenu');
					}, this))
				.on("contextmenu.jstree", ".jstree-anchor", $.proxy(function (e, data) {
						if (e.target.tagName.toLowerCase() === 'input') {
							return;
						}
						e.preventDefault();
						last_ts = e.ctrlKey ? +new Date() : 0;
						if(data || cto) {
							last_ts = (+new Date()) + 10000;
						}
						if(cto) {
							clearTimeout(cto);
						}
						if(!this.is_loading(e.currentTarget)) {
							this.show_contextmenu(e.currentTarget, e.pageX, e.pageY, e);
						}
					}, this))
				.on("click.jstree", ".jstree-anchor", $.proxy(function (e) {
						if(this._data.contextmenu.visible && (!last_ts || (+new Date()) - last_ts > 250)) { // work around safari & macOS ctrl+click
							$.vakata.context.hide();
						}
						last_ts = 0;
					}, this))
				.on("touchstart.jstree", ".jstree-anchor", function (e) {
						if(!e.originalEvent || !e.originalEvent.changedTouches || !e.originalEvent.changedTouches[0]) {
							return;
						}
						ex = e.originalEvent.changedTouches[0].clientX;
						ey = e.originalEvent.changedTouches[0].clientY;
						cto = setTimeout(function () {
							$(e.currentTarget).trigger('contextmenu', true);
						}, 750);
					})
				.on('touchmove.vakata.jstree', function (e) {
						if(cto && e.originalEvent && e.originalEvent.changedTouches && e.originalEvent.changedTouches[0] && (Math.abs(ex - e.originalEvent.changedTouches[0].clientX) > 10 || Math.abs(ey - e.originalEvent.changedTouches[0].clientY) > 10)) {
							clearTimeout(cto);
							$.vakata.context.hide();
						}
					})
				.on('touchend.vakata.jstree', function (e) {
						if(cto) {
							clearTimeout(cto);
						}
					});

			/*!
			if(!('oncontextmenu' in document.body) && ('ontouchstart' in document.body)) {
				var el = null, tm = null;
				this.element
					.on("touchstart", ".jstree-anchor", function (e) {
						el = e.currentTarget;
						tm = +new Date();
						$(document).one("touchend", function (e) {
							e.target = document.elementFromPoint(e.originalEvent.targetTouches[0].pageX - window.pageXOffset, e.originalEvent.targetTouches[0].pageY - window.pageYOffset);
							e.currentTarget = e.target;
							tm = ((+(new Date())) - tm);
							if(e.target === el && tm > 600 && tm < 1000) {
								e.preventDefault();
								$(el).trigger('contextmenu', e);
							}
							el = null;
							tm = null;
						});
					});
			}
			*/
			$(document).on("context_hide.vakata.jstree", $.proxy(function (e, data) {
				this._data.contextmenu.visible = false;
				$(data.reference).removeClass('jstree-context');
			}, this));
		};
		this.teardown = function () {
			if(this._data.contextmenu.visible) {
				$.vakata.context.hide();
			}
			parent.teardown.call(this);
		};

		/**
		 * prepare and show the context menu for a node
		 * @name show_contextmenu(obj [, x, y])
		 * @param {mixed} obj the node
		 * @param {Number} x the x-coordinate relative to the document to show the menu at
		 * @param {Number} y the y-coordinate relative to the document to show the menu at
		 * @param {Object} e the event if available that triggered the contextmenu
		 * @plugin contextmenu
		 * @trigger show_contextmenu.jstree
		 */
		this.show_contextmenu = function (obj, x, y, e) {
			obj = this.get_node(obj);
			if(!obj || obj.id === $.jstree.root) { return false; }
			var s = this.settings.contextmenu,
				d = this.get_node(obj, true),
				a = d.children(".jstree-anchor"),
				o = false,
				i = false;
			if(s.show_at_node || x === undefined || y === undefined) {
				o = a.offset();
				x = o.left;
				y = o.top + this._data.core.li_height;
			}
			if(this.settings.contextmenu.select_node && !this.is_selected(obj)) {
				this.activate_node(obj, e);
			}

			i = s.items;
			if($.isFunction(i)) {
				i = i.call(this, obj, $.proxy(function (i) {
					this._show_contextmenu(obj, x, y, i);
				}, this));
			}
			if($.isPlainObject(i)) {
				this._show_contextmenu(obj, x, y, i);
			}
		};
		/**
		 * show the prepared context menu for a node
		 * @name _show_contextmenu(obj, x, y, i)
		 * @param {mixed} obj the node
		 * @param {Number} x the x-coordinate relative to the document to show the menu at
		 * @param {Number} y the y-coordinate relative to the document to show the menu at
		 * @param {Number} i the object of items to show
		 * @plugin contextmenu
		 * @trigger show_contextmenu.jstree
		 * @private
		 */
		this._show_contextmenu = function (obj, x, y, i) {
			var d = this.get_node(obj, true),
				a = d.children(".jstree-anchor");
			$(document).one("context_show.vakata.jstree", $.proxy(function (e, data) {
				var cls = 'jstree-contextmenu jstree-' + this.get_theme() + '-contextmenu';
				$(data.element).addClass(cls);
				a.addClass('jstree-context');
			}, this));
			this._data.contextmenu.visible = true;
			$.vakata.context.show(a, { 'x' : x, 'y' : y }, i);
			/**
			 * triggered when the contextmenu is shown for a node
			 * @event
			 * @name show_contextmenu.jstree
			 * @param {Object} node the node
			 * @param {Number} x the x-coordinate of the menu relative to the document
			 * @param {Number} y the y-coordinate of the menu relative to the document
			 * @plugin contextmenu
			 */
			this.trigger('show_contextmenu', { "node" : obj, "x" : x, "y" : y });
		};
	};

	// contextmenu helper
	(function ($) {
		var right_to_left = false,
			vakata_context = {
				element		: false,
				reference	: false,
				position_x	: 0,
				position_y	: 0,
				items		: [],
				html		: "",
				is_visible	: false
			};

		$.vakata.context = {
			settings : {
				hide_onmouseleave	: 0,
				icons				: true
			},
			_trigger : function (event_name) {
				$(document).triggerHandler("context_" + event_name + ".vakata", {
					"reference"	: vakata_context.reference,
					"element"	: vakata_context.element,
					"position"	: {
						"x" : vakata_context.position_x,
						"y" : vakata_context.position_y
					}
				});
			},
			_execute : function (i) {
				i = vakata_context.items[i];
				return i && (!i._disabled || ($.isFunction(i._disabled) && !i._disabled({ "item" : i, "reference" : vakata_context.reference, "element" : vakata_context.element }))) && i.action ? i.action.call(null, {
							"item"		: i,
							"reference"	: vakata_context.reference,
							"element"	: vakata_context.element,
							"position"	: {
								"x" : vakata_context.position_x,
								"y" : vakata_context.position_y
							}
						}) : false;
			},
			_parse : function (o, is_callback) {
				if(!o) { return false; }
				if(!is_callback) {
					vakata_context.html		= "";
					vakata_context.items	= [];
				}
				var str = "",
					sep = false,
					tmp;

				if(is_callback) { str += "<"+"ul>"; }
				$.each(o, function (i, val) {
					if(!val) { return true; }
					vakata_context.items.push(val);
					if(!sep && val.separator_before) {
						str += "<"+"li class='vakata-context-separator'><"+"a href='#' " + ($.vakata.context.settings.icons ? '' : 'style="margin-left:0px;"') + ">&#160;<"+"/a><"+"/li>";
					}
					sep = false;
					str += "<"+"li class='" + (val._class || "") + (val._disabled === true || ($.isFunction(val._disabled) && val._disabled({ "item" : val, "reference" : vakata_context.reference, "element" : vakata_context.element })) ? " vakata-contextmenu-disabled " : "") + "' "+(val.shortcut?" data-shortcut='"+val.shortcut+"' ":'')+">";
					str += "<"+"a href='#' rel='" + (vakata_context.items.length - 1) + "' " + (val.title ? "title='" + val.title + "'" : "") + ">";
					if($.vakata.context.settings.icons) {
						str += "<"+"i ";
						if(val.icon) {
							if(val.icon.indexOf("/") !== -1 || val.icon.indexOf(".") !== -1) { str += " style='background:url(\"" + val.icon + "\") center center no-repeat' "; }
							else { str += " class='" + val.icon + "' "; }
						}
						str += "><"+"/i><"+"span class='vakata-contextmenu-sep'>&#160;<"+"/span>";
					}
					str += ($.isFunction(val.label) ? val.label({ "item" : i, "reference" : vakata_context.reference, "element" : vakata_context.element }) : val.label) + (val.shortcut?' <span class="vakata-contextmenu-shortcut vakata-contextmenu-shortcut-'+val.shortcut+'">'+ (val.shortcut_label || '') +'</span>':'') + "<"+"/a>";
					if(val.submenu) {
						tmp = $.vakata.context._parse(val.submenu, true);
						if(tmp) { str += tmp; }
					}
					str += "<"+"/li>";
					if(val.separator_after) {
						str += "<"+"li class='vakata-context-separator'><"+"a href='#' " + ($.vakata.context.settings.icons ? '' : 'style="margin-left:0px;"') + ">&#160;<"+"/a><"+"/li>";
						sep = true;
					}
				});
				str  = str.replace(/<li class\='vakata-context-separator'\><\/li\>$/,"");
				if(is_callback) { str += "</ul>"; }
				/**
				 * triggered on the document when the contextmenu is parsed (HTML is built)
				 * @event
				 * @plugin contextmenu
				 * @name context_parse.vakata
				 * @param {jQuery} reference the element that was right clicked
				 * @param {jQuery} element the DOM element of the menu itself
				 * @param {Object} position the x & y coordinates of the menu
				 */
				if(!is_callback) { vakata_context.html = str; $.vakata.context._trigger("parse"); }
				return str.length > 10 ? str : false;
			},
			_show_submenu : function (o) {
				o = $(o);
				if(!o.length || !o.children("ul").length) { return; }
				var e = o.children("ul"),
					xl = o.offset().left,
					x = xl + o.outerWidth(),
					y = o.offset().top,
					w = e.width(),
					h = e.height(),
					dw = $(window).width() + $(window).scrollLeft(),
					dh = $(window).height() + $(window).scrollTop();
				// може да се спести е една проверка - дали няма някой от класовете вече нагоре
				if(right_to_left) {
					o[x - (w + 10 + o.outerWidth()) < 0 ? "addClass" : "removeClass"]("vakata-context-left");
				}
				else {
					o[x + w > dw  && xl > dw - x ? "addClass" : "removeClass"]("vakata-context-right");
				}
				if(y + h + 10 > dh) {
					e.css("bottom","-1px");
				}

				//if does not fit - stick it to the side
				if (o.hasClass('vakata-context-right')) {
					if (xl < w) {
						e.css("margin-right", xl - w);
					}
				} else {
					if (dw - x < w) {
						e.css("margin-left", dw - x - w);
					}
				}

				e.show();
			},
			show : function (reference, position, data) {
				var o, e, x, y, w, h, dw, dh, cond = true;
				if(vakata_context.element && vakata_context.element.length) {
					vakata_context.element.width('');
				}
				switch(cond) {
					case (!position && !reference):
						return false;
					case (!!position && !!reference):
						vakata_context.reference	= reference;
						vakata_context.position_x	= position.x;
						vakata_context.position_y	= position.y;
						break;
					case (!position && !!reference):
						vakata_context.reference	= reference;
						o = reference.offset();
						vakata_context.position_x	= o.left + reference.outerHeight();
						vakata_context.position_y	= o.top;
						break;
					case (!!position && !reference):
						vakata_context.position_x	= position.x;
						vakata_context.position_y	= position.y;
						break;
				}
				if(!!reference && !data && $(reference).data('vakata_contextmenu')) {
					data = $(reference).data('vakata_contextmenu');
				}
				if($.vakata.context._parse(data)) {
					vakata_context.element.html(vakata_context.html);
				}
				if(vakata_context.items.length) {
					vakata_context.element.appendTo(document.body);
					e = vakata_context.element;
					x = vakata_context.position_x;
					y = vakata_context.position_y;
					w = e.width();
					h = e.height();
					dw = $(window).width() + $(window).scrollLeft();
					dh = $(window).height() + $(window).scrollTop();
					if(right_to_left) {
						x -= (e.outerWidth() - $(reference).outerWidth());
						if(x < $(window).scrollLeft() + 20) {
							x = $(window).scrollLeft() + 20;
						}
					}
					if(x + w + 20 > dw) {
						x = dw - (w + 20);
					}
					if(y + h + 20 > dh) {
						y = dh - (h + 20);
					}

					vakata_context.element
						.css({ "left" : x, "top" : y })
						.show()
						.find('a').first().focus().parent().addClass("vakata-context-hover");
					vakata_context.is_visible = true;
					/**
					 * triggered on the document when the contextmenu is shown
					 * @event
					 * @plugin contextmenu
					 * @name context_show.vakata
					 * @param {jQuery} reference the element that was right clicked
					 * @param {jQuery} element the DOM element of the menu itself
					 * @param {Object} position the x & y coordinates of the menu
					 */
					$.vakata.context._trigger("show");
				}
			},
			hide : function () {
				if(vakata_context.is_visible) {
					vakata_context.element.hide().find("ul").hide().end().find(':focus').blur().end().detach();
					vakata_context.is_visible = false;
					/**
					 * triggered on the document when the contextmenu is hidden
					 * @event
					 * @plugin contextmenu
					 * @name context_hide.vakata
					 * @param {jQuery} reference the element that was right clicked
					 * @param {jQuery} element the DOM element of the menu itself
					 * @param {Object} position the x & y coordinates of the menu
					 */
					$.vakata.context._trigger("hide");
				}
			}
		};
		$(function () {
			right_to_left = $(document.body).css("direction") === "rtl";
			var to = false;

			vakata_context.element = $("<ul class='vakata-context'></ul>");
			vakata_context.element
				.on("mouseenter", "li", function (e) {
					e.stopImmediatePropagation();

					if($.contains(this, e.relatedTarget)) {
						// премахнато заради delegate mouseleave по-долу
						// $(this).find(".vakata-context-hover").removeClass("vakata-context-hover");
						return;
					}

					if(to) { clearTimeout(to); }
					vakata_context.element.find(".vakata-context-hover").removeClass("vakata-context-hover").end();

					$(this)
						.siblings().find("ul").hide().end().end()
						.parentsUntil(".vakata-context", "li").addBack().addClass("vakata-context-hover");
					$.vakata.context._show_submenu(this);
				})
				// тестово - дали не натоварва?
				.on("mouseleave", "li", function (e) {
					if($.contains(this, e.relatedTarget)) { return; }
					$(this).find(".vakata-context-hover").addBack().removeClass("vakata-context-hover");
				})
				.on("mouseleave", function (e) {
					$(this).find(".vakata-context-hover").removeClass("vakata-context-hover");
					if($.vakata.context.settings.hide_onmouseleave) {
						to = setTimeout(
							(function (t) {
								return function () { $.vakata.context.hide(); };
							}(this)), $.vakata.context.settings.hide_onmouseleave);
					}
				})
				.on("click", "a", function (e) {
					e.preventDefault();
				//})
				//.on("mouseup", "a", function (e) {
					if(!$(this).blur().parent().hasClass("vakata-context-disabled") && $.vakata.context._execute($(this).attr("rel")) !== false) {
						$.vakata.context.hide();
					}
				})
				.on('keydown', 'a', function (e) {
						var o = null;
						switch(e.which) {
							case 13:
							case 32:
								e.type = "click";
								e.preventDefault();
								$(e.currentTarget).trigger(e);
								break;
							case 37:
								if(vakata_context.is_visible) {
									vakata_context.element.find(".vakata-context-hover").last().closest("li").first().find("ul").hide().find(".vakata-context-hover").removeClass("vakata-context-hover").end().end().children('a').focus();
									e.stopImmediatePropagation();
									e.preventDefault();
								}
								break;
							case 38:
								if(vakata_context.is_visible) {
									o = vakata_context.element.find("ul:visible").addBack().last().children(".vakata-context-hover").removeClass("vakata-context-hover").prevAll("li:not(.vakata-context-separator)").first();
									if(!o.length) { o = vakata_context.element.find("ul:visible").addBack().last().children("li:not(.vakata-context-separator)").last(); }
									o.addClass("vakata-context-hover").children('a').focus();
									e.stopImmediatePropagation();
									e.preventDefault();
								}
								break;
							case 39:
								if(vakata_context.is_visible) {
									vakata_context.element.find(".vakata-context-hover").last().children("ul").show().children("li:not(.vakata-context-separator)").removeClass("vakata-context-hover").first().addClass("vakata-context-hover").children('a').focus();
									e.stopImmediatePropagation();
									e.preventDefault();
								}
								break;
							case 40:
								if(vakata_context.is_visible) {
									o = vakata_context.element.find("ul:visible").addBack().last().children(".vakata-context-hover").removeClass("vakata-context-hover").nextAll("li:not(.vakata-context-separator)").first();
									if(!o.length) { o = vakata_context.element.find("ul:visible").addBack().last().children("li:not(.vakata-context-separator)").first(); }
									o.addClass("vakata-context-hover").children('a').focus();
									e.stopImmediatePropagation();
									e.preventDefault();
								}
								break;
							case 27:
								$.vakata.context.hide();
								e.preventDefault();
								break;
							default:
								//console.log(e.which);
								break;
						}
					})
				.on('keydown', function (e) {
					e.preventDefault();
					var a = vakata_context.element.find('.vakata-contextmenu-shortcut-' + e.which).parent();
					if(a.parent().not('.vakata-context-disabled')) {
						a.click();
					}
				});

			$(document)
				.on("mousedown.vakata.jstree", function (e) {
					if(vakata_context.is_visible && vakata_context.element[0] !== e.target  && !$.contains(vakata_context.element[0], e.target)) {
						$.vakata.context.hide();
					}
				})
				.on("context_show.vakata.jstree", function (e, data) {
					vakata_context.element.find("li:has(ul)").children("a").addClass("vakata-context-parent");
					if(right_to_left) {
						vakata_context.element.addClass("vakata-context-rtl").css("direction", "rtl");
					}
					// also apply a RTL class?
					vakata_context.element.find("ul").hide().end();
				});
		});
	}($));
	// $.jstree.defaults.plugins.push("contextmenu");


/**
 * ### Drag'n'drop plugin
 *
 * Enables dragging and dropping of nodes in the tree, resulting in a move or copy operations.
 */

	/**
	 * stores all defaults for the drag'n'drop plugin
	 * @name $.jstree.defaults.dnd
	 * @plugin dnd
	 */
	$.jstree.defaults.dnd = {
		/**
		 * a boolean indicating if a copy should be possible while dragging (by pressint the meta key or Ctrl). Defaults to `true`.
		 * @name $.jstree.defaults.dnd.copy
		 * @plugin dnd
		 */
		copy : true,
		/**
		 * a number indicating how long a node should remain hovered while dragging to be opened. Defaults to `500`.
		 * @name $.jstree.defaults.dnd.open_timeout
		 * @plugin dnd
		 */
		open_timeout : 500,
		/**
		 * a function invoked each time a node is about to be dragged, invoked in the tree's scope and receives the nodes about to be dragged as an argument (array) and the event that started the drag - return `false` to prevent dragging
		 * @name $.jstree.defaults.dnd.is_draggable
		 * @plugin dnd
		 */
		is_draggable : true,
		/**
		 * a boolean indicating if checks should constantly be made while the user is dragging the node (as opposed to checking only on drop), default is `true`
		 * @name $.jstree.defaults.dnd.check_while_dragging
		 * @plugin dnd
		 */
		check_while_dragging : true,
		/**
		 * a boolean indicating if nodes from this tree should only be copied with dnd (as opposed to moved), default is `false`
		 * @name $.jstree.defaults.dnd.always_copy
		 * @plugin dnd
		 */
		always_copy : false,
		/**
		 * when dropping a node "inside", this setting indicates the position the node should go to - it can be an integer or a string: "first" (same as 0) or "last", default is `0`
		 * @name $.jstree.defaults.dnd.inside_pos
		 * @plugin dnd
		 */
		inside_pos : 0,
		/**
		 * when starting the drag on a node that is selected this setting controls if all selected nodes are dragged or only the single node, default is `true`, which means all selected nodes are dragged when the drag is started on a selected node
		 * @name $.jstree.defaults.dnd.drag_selection
		 * @plugin dnd
		 */
		drag_selection : true,
		/**
		 * controls whether dnd works on touch devices. If left as boolean true dnd will work the same as in desktop browsers, which in some cases may impair scrolling. If set to boolean false dnd will not work on touch devices. There is a special third option - string "selected" which means only selected nodes can be dragged on touch devices.
		 * @name $.jstree.defaults.dnd.touch
		 * @plugin dnd
		 */
		touch : true,
		/**
		 * controls whether items can be dropped anywhere on the node, not just on the anchor, by default only the node anchor is a valid drop target. Works best with the wholerow plugin. If enabled on mobile depending on the interface it might be hard for the user to cancel the drop, since the whole tree container will be a valid drop target.
		 * @name $.jstree.defaults.dnd.large_drop_target
		 * @plugin dnd
		 */
		large_drop_target : false,
		/**
		 * controls whether a drag can be initiated from any part of the node and not just the text/icon part, works best with the wholerow plugin. Keep in mind it can cause problems with tree scrolling on mobile depending on the interface - in that case set the touch option to "selected".
		 * @name $.jstree.defaults.dnd.large_drag_target
		 * @plugin dnd
		 */
		large_drag_target : false,
		/**
		 * controls whether use HTML5 dnd api instead of classical. That will allow better integration of dnd events with other HTML5 controls.
		 * @reference http://caniuse.com/#feat=dragndrop
		 * @name $.jstree.defaults.dnd.use_html5
		 * @plugin dnd
		 */
		use_html5: false
	};
	var drg, elm;
	// TODO: now check works by checking for each node individually, how about max_children, unique, etc?
	$.jstree.plugins.dnd = function (options, parent) {
		this.init = function (el, options) {
			parent.init.call(this, el, options);
			this.settings.dnd.use_html5 = this.settings.dnd.use_html5 && ('draggable' in document.createElement('span'));
		};
		this.bind = function () {
			parent.bind.call(this);

			this.element
				.on(this.settings.dnd.use_html5 ? 'dragstart.jstree' : 'mousedown.jstree touchstart.jstree', this.settings.dnd.large_drag_target ? '.jstree-node' : '.jstree-anchor', $.proxy(function (e) {
						if(this.settings.dnd.large_drag_target && $(e.target).closest('.jstree-node')[0] !== e.currentTarget) {
							return true;
						}
						if(e.type === "touchstart" && (!this.settings.dnd.touch || (this.settings.dnd.touch === 'selected' && !$(e.currentTarget).closest('.jstree-node').children('.jstree-anchor').hasClass('jstree-clicked')))) {
							return true;
						}
						var obj = this.get_node(e.target),
							mlt = this.is_selected(obj) && this.settings.dnd.drag_selection ? this.get_top_selected().length : 1,
							txt = (mlt > 1 ? mlt + ' ' + this.get_string('nodes') : this.get_text(e.currentTarget));
						if(this.settings.core.force_text) {
							txt = $.vakata.html.escape(txt);
						}
						if(obj && obj.id && obj.id !== $.jstree.root && (e.which === 1 || e.type === "touchstart" || e.type === "dragstart") &&
							(this.settings.dnd.is_draggable === true || ($.isFunction(this.settings.dnd.is_draggable) && this.settings.dnd.is_draggable.call(this, (mlt > 1 ? this.get_top_selected(true) : [obj]), e)))
						) {
							drg = { 'jstree' : true, 'origin' : this, 'obj' : this.get_node(obj,true), 'nodes' : mlt > 1 ? this.get_top_selected() : [obj.id] };
							elm = e.currentTarget;
							if (this.settings.dnd.use_html5) {
								$.vakata.dnd._trigger('start', e, { 'helper': $(), 'element': elm, 'data': drg });
							} else {
								this.element.trigger('mousedown.jstree');
								return $.vakata.dnd.start(e, drg, '<div id="jstree-dnd" class="jstree-' + this.get_theme() + ' jstree-' + this.get_theme() + '-' + this.get_theme_variant() + ' ' + ( this.settings.core.themes.responsive ? ' jstree-dnd-responsive' : '' ) + '"><i class="jstree-icon jstree-er"></i>' + txt + '<ins class="jstree-copy" style="display:none;">+</ins></div>');
							}
						}
					}, this));
			if (this.settings.dnd.use_html5) {
				this.element
					.on('dragover.jstree', function (e) {
							e.preventDefault();
							$.vakata.dnd._trigger('move', e, { 'helper': $(), 'element': elm, 'data': drg });
							return false;
						})
					//.on('dragenter.jstree', this.settings.dnd.large_drop_target ? '.jstree-node' : '.jstree-anchor', $.proxy(function (e) {
					//		e.preventDefault();
					//		$.vakata.dnd._trigger('move', e, { 'helper': $(), 'element': elm, 'data': drg });
					//		return false;
					//	}, this))
					.on('drop.jstree', $.proxy(function (e) {
							e.preventDefault();
							$.vakata.dnd._trigger('stop', e, { 'helper': $(), 'element': elm, 'data': drg });
							return false;
						}, this));
			}
		};
		this.redraw_node = function(obj, deep, callback, force_render) {
			obj = parent.redraw_node.apply(this, arguments);
			if (obj && this.settings.dnd.use_html5) {
				if (this.settings.dnd.large_drag_target) {
					obj.setAttribute('draggable', true);
				} else {
					var i, j, tmp = null;
					for(i = 0, j = obj.childNodes.length; i < j; i++) {
						if(obj.childNodes[i] && obj.childNodes[i].className && obj.childNodes[i].className.indexOf("jstree-anchor") !== -1) {
							tmp = obj.childNodes[i];
							break;
						}
					}
					if(tmp) {
						tmp.setAttribute('draggable', true);
					}
				}
			}
			return obj;
		};
	};

	$(function() {
		// bind only once for all instances
		var lastmv = false,
			laster = false,
			lastev = false,
			opento = false,
			marker = $('<div id="jstree-marker">&#160;</div>').hide(); //.appendTo('body');

		$(document)
			.on('dragover.vakata.jstree', function (e) {
				if (elm) {
					$.vakata.dnd._trigger('move', e, { 'helper': $(), 'element': elm, 'data': drg });
				}
			})
			.on('drop.vakata.jstree', function (e) {
				if (elm) {
					$.vakata.dnd._trigger('stop', e, { 'helper': $(), 'element': elm, 'data': drg });
					elm = null;
					drg = null;
				}
			})
			.on('dnd_start.vakata.jstree', function (e, data) {
				lastmv = false;
				lastev = false;
				if(!data || !data.data || !data.data.jstree) { return; }
				marker.appendTo(document.body); //.show();
			})
			.on('dnd_move.vakata.jstree', function (e, data) {
				var isDifferentNode = data.event.target !== lastev.target;
				if(opento) {
					if (!data.event || data.event.type !== 'dragover' || isDifferentNode) {
						clearTimeout(opento);
					}
				}
				if(!data || !data.data || !data.data.jstree) { return; }

				// if we are hovering the marker image do nothing (can happen on "inside" drags)
				if(data.event.target.id && data.event.target.id === 'jstree-marker') {
					return;
				}
				lastev = data.event;

				var ins = $.jstree.reference(data.event.target),
					ref = false,
					off = false,
					rel = false,
					tmp, l, t, h, p, i, o, ok, t1, t2, op, ps, pr, ip, tm, is_copy, pn;
				// if we are over an instance
				if(ins && ins._data && ins._data.dnd) {
					marker.attr('class', 'jstree-' + ins.get_theme() + ( ins.settings.core.themes.responsive ? ' jstree-dnd-responsive' : '' ));
					is_copy = data.data.origin && (data.data.origin.settings.dnd.always_copy || (data.data.origin.settings.dnd.copy && (data.event.metaKey || data.event.ctrlKey)));
					data.helper
						.children().attr('class', 'jstree-' + ins.get_theme() + ' jstree-' + ins.get_theme() + '-' + ins.get_theme_variant() + ' ' + ( ins.settings.core.themes.responsive ? ' jstree-dnd-responsive' : '' ))
						.find('.jstree-copy').first()[ is_copy ? 'show' : 'hide' ]();

					// if are hovering the container itself add a new root node
					//console.log(data.event);
					if( (data.event.target === ins.element[0] || data.event.target === ins.get_container_ul()[0]) && ins.get_container_ul().children().length === 0) {
						ok = true;
						for(t1 = 0, t2 = data.data.nodes.length; t1 < t2; t1++) {
							ok = ok && ins.check( (data.data.origin && (data.data.origin.settings.dnd.always_copy || (data.data.origin.settings.dnd.copy && (data.event.metaKey || data.event.ctrlKey)) ) ? "copy_node" : "move_node"), (data.data.origin && data.data.origin !== ins ? data.data.origin.get_node(data.data.nodes[t1]) : data.data.nodes[t1]), $.jstree.root, 'last', { 'dnd' : true, 'ref' : ins.get_node($.jstree.root), 'pos' : 'i', 'origin' : data.data.origin, 'is_multi' : (data.data.origin && data.data.origin !== ins), 'is_foreign' : (!data.data.origin) });
							if(!ok) { break; }
						}
						if(ok) {
							lastmv = { 'ins' : ins, 'par' : $.jstree.root, 'pos' : 'last' };
							marker.hide();
							data.helper.find('.jstree-icon').first().removeClass('jstree-er').addClass('jstree-ok');
							if (data.event.originalEvent && data.event.originalEvent.dataTransfer) {
								data.event.originalEvent.dataTransfer.dropEffect = is_copy ? 'copy' : 'move';
							}
							return;
						}
					}
					else {
						// if we are hovering a tree node
						ref = ins.settings.dnd.large_drop_target ? $(data.event.target).closest('.jstree-node').children('.jstree-anchor') : $(data.event.target).closest('.jstree-anchor');
						if(ref && ref.length && ref.parent().is('.jstree-closed, .jstree-open, .jstree-leaf')) {
							off = ref.offset();
							rel = (data.event.pageY !== undefined ? data.event.pageY : data.event.originalEvent.pageY) - off.top;
							h = ref.outerHeight();
							if(rel < h / 3) {
								o = ['b', 'i', 'a'];
							}
							else if(rel > h - h / 3) {
								o = ['a', 'i', 'b'];
							}
							else {
								o = rel > h / 2 ? ['i', 'a', 'b'] : ['i', 'b', 'a'];
							}
							$.each(o, function (j, v) {
								switch(v) {
									case 'b':
										l = off.left - 6;
										t = off.top;
										p = ins.get_parent(ref);
										i = ref.parent().index();
										break;
									case 'i':
										ip = ins.settings.dnd.inside_pos;
										tm = ins.get_node(ref.parent());
										l = off.left - 2;
										t = off.top + h / 2 + 1;
										p = tm.id;
										i = ip === 'first' ? 0 : (ip === 'last' ? tm.children.length : Math.min(ip, tm.children.length));
										break;
									case 'a':
										l = off.left - 6;
										t = off.top + h;
										p = ins.get_parent(ref);
										i = ref.parent().index() + 1;
										break;
								}
								ok = true;
								for(t1 = 0, t2 = data.data.nodes.length; t1 < t2; t1++) {
									op = data.data.origin && (data.data.origin.settings.dnd.always_copy || (data.data.origin.settings.dnd.copy && (data.event.metaKey || data.event.ctrlKey))) ? "copy_node" : "move_node";
									ps = i;
									if(op === "move_node" && v === 'a' && (data.data.origin && data.data.origin === ins) && p === ins.get_parent(data.data.nodes[t1])) {
										pr = ins.get_node(p);
										if(ps > $.inArray(data.data.nodes[t1], pr.children)) {
											ps -= 1;
										}
									}
									ok = ok && ( (ins && ins.settings && ins.settings.dnd && ins.settings.dnd.check_while_dragging === false) || ins.check(op, (data.data.origin && data.data.origin !== ins ? data.data.origin.get_node(data.data.nodes[t1]) : data.data.nodes[t1]), p, ps, { 'dnd' : true, 'ref' : ins.get_node(ref.parent()), 'pos' : v, 'origin' : data.data.origin, 'is_multi' : (data.data.origin && data.data.origin !== ins), 'is_foreign' : (!data.data.origin) }) );
									if(!ok) {
										if(ins && ins.last_error) { laster = ins.last_error(); }
										break;
									}
								}
								if(v === 'i' && ref.parent().is('.jstree-closed') && ins.settings.dnd.open_timeout) {
									if (!data.event || data.event.type !== 'dragover' || isDifferentNode) {
										if (opento) { clearTimeout(opento); }
										opento = setTimeout((function (x, z) { return function () { x.open_node(z); }; }(ins, ref)), ins.settings.dnd.open_timeout);
									}
								}
								if(ok) {
									pn = ins.get_node(p, true);
									if (!pn.hasClass('.jstree-dnd-parent')) {
										$('.jstree-dnd-parent').removeClass('jstree-dnd-parent');
										pn.addClass('jstree-dnd-parent');
									}
									lastmv = { 'ins' : ins, 'par' : p, 'pos' : v === 'i' && ip === 'last' && i === 0 && !ins.is_loaded(tm) ? 'last' : i };
									marker.css({ 'left' : l + 'px', 'top' : t + 'px' }).show();
									data.helper.find('.jstree-icon').first().removeClass('jstree-er').addClass('jstree-ok');
									if (data.event.originalEvent && data.event.originalEvent.dataTransfer) {
										data.event.originalEvent.dataTransfer.dropEffect = is_copy ? 'copy' : 'move';
									}
									laster = {};
									o = true;
									return false;
								}
							});
							if(o === true) { return; }
						}
					}
				}
				$('.jstree-dnd-parent').removeClass('jstree-dnd-parent');
				lastmv = false;
				data.helper.find('.jstree-icon').removeClass('jstree-ok').addClass('jstree-er');
				if (data.event.originalEvent && data.event.originalEvent.dataTransfer) {
					//data.event.originalEvent.dataTransfer.dropEffect = 'none';
				}
				marker.hide();
			})
			.on('dnd_scroll.vakata.jstree', function (e, data) {
				if(!data || !data.data || !data.data.jstree) { return; }
				marker.hide();
				lastmv = false;
				lastev = false;
				data.helper.find('.jstree-icon').first().removeClass('jstree-ok').addClass('jstree-er');
			})
			.on('dnd_stop.vakata.jstree', function (e, data) {
				$('.jstree-dnd-parent').removeClass('jstree-dnd-parent');
				if(opento) { clearTimeout(opento); }
				if(!data || !data.data || !data.data.jstree) { return; }
				marker.hide().detach();
				var i, j, nodes = [];
				if(lastmv) {
					for(i = 0, j = data.data.nodes.length; i < j; i++) {
						nodes[i] = data.data.origin ? data.data.origin.get_node(data.data.nodes[i]) : data.data.nodes[i];
					}
					lastmv.ins[ data.data.origin && (data.data.origin.settings.dnd.always_copy || (data.data.origin.settings.dnd.copy && (data.event.metaKey || data.event.ctrlKey))) ? 'copy_node' : 'move_node' ](nodes, lastmv.par, lastmv.pos, false, false, false, data.data.origin);
				}
				else {
					i = $(data.event.target).closest('.jstree');
					if(i.length && laster && laster.error && laster.error === 'check') {
						i = i.jstree(true);
						if(i) {
							i.settings.core.error.call(this, laster);
						}
					}
				}
				lastev = false;
				lastmv = false;
			})
			.on('keyup.jstree keydown.jstree', function (e, data) {
				data = $.vakata.dnd._get();
				if(data && data.data && data.data.jstree) {
					if (e.type === "keyup" && e.which === 27) {
						if (opento) { clearTimeout(opento); }
						lastmv = false;
						laster = false;
						lastev = false;
						opento = false;
						marker.hide().detach();
						$.vakata.dnd._clean();
					} else {
						data.helper.find('.jstree-copy').first()[ data.data.origin && (data.data.origin.settings.dnd.always_copy || (data.data.origin.settings.dnd.copy && (e.metaKey || e.ctrlKey))) ? 'show' : 'hide' ]();
						if(lastev) {
							lastev.metaKey = e.metaKey;
							lastev.ctrlKey = e.ctrlKey;
							$.vakata.dnd._trigger('move', lastev);
						}
					}
				}
			});
	});

	// helpers
	(function ($) {
		$.vakata.html = {
			div : $('<div />'),
			escape : function (str) {
				return $.vakata.html.div.text(str).html();
			},
			strip : function (str) {
				return $.vakata.html.div.empty().append($.parseHTML(str)).text();
			}
		};
		// private variable
		var vakata_dnd = {
			element	: false,
			target	: false,
			is_down	: false,
			is_drag	: false,
			helper	: false,
			helper_w: 0,
			data	: false,
			init_x	: 0,
			init_y	: 0,
			scroll_l: 0,
			scroll_t: 0,
			scroll_e: false,
			scroll_i: false,
			is_touch: false
		};
		$.vakata.dnd = {
			settings : {
				scroll_speed		: 10,
				scroll_proximity	: 20,
				helper_left			: 5,
				helper_top			: 10,
				threshold			: 5,
				threshold_touch		: 10
			},
			_trigger : function (event_name, e, data) {
				if (data === undefined) {
					data = $.vakata.dnd._get();
				}
				data.event = e;
				$(document).triggerHandler("dnd_" + event_name + ".vakata", data);
			},
			_get : function () {
				return {
					"data"		: vakata_dnd.data,
					"element"	: vakata_dnd.element,
					"helper"	: vakata_dnd.helper
				};
			},
			_clean : function () {
				if(vakata_dnd.helper) { vakata_dnd.helper.remove(); }
				if(vakata_dnd.scroll_i) { clearInterval(vakata_dnd.scroll_i); vakata_dnd.scroll_i = false; }
				vakata_dnd = {
					element	: false,
					target	: false,
					is_down	: false,
					is_drag	: false,
					helper	: false,
					helper_w: 0,
					data	: false,
					init_x	: 0,
					init_y	: 0,
					scroll_l: 0,
					scroll_t: 0,
					scroll_e: false,
					scroll_i: false,
					is_touch: false
				};
				$(document).off("mousemove.vakata.jstree touchmove.vakata.jstree", $.vakata.dnd.drag);
				$(document).off("mouseup.vakata.jstree touchend.vakata.jstree", $.vakata.dnd.stop);
			},
			_scroll : function (init_only) {
				if(!vakata_dnd.scroll_e || (!vakata_dnd.scroll_l && !vakata_dnd.scroll_t)) {
					if(vakata_dnd.scroll_i) { clearInterval(vakata_dnd.scroll_i); vakata_dnd.scroll_i = false; }
					return false;
				}
				if(!vakata_dnd.scroll_i) {
					vakata_dnd.scroll_i = setInterval($.vakata.dnd._scroll, 100);
					return false;
				}
				if(init_only === true) { return false; }

				var i = vakata_dnd.scroll_e.scrollTop(),
					j = vakata_dnd.scroll_e.scrollLeft();
				vakata_dnd.scroll_e.scrollTop(i + vakata_dnd.scroll_t * $.vakata.dnd.settings.scroll_speed);
				vakata_dnd.scroll_e.scrollLeft(j + vakata_dnd.scroll_l * $.vakata.dnd.settings.scroll_speed);
				if(i !== vakata_dnd.scroll_e.scrollTop() || j !== vakata_dnd.scroll_e.scrollLeft()) {
					/**
					 * triggered on the document when a drag causes an element to scroll
					 * @event
					 * @plugin dnd
					 * @name dnd_scroll.vakata
					 * @param {Mixed} data any data supplied with the call to $.vakata.dnd.start
					 * @param {DOM} element the DOM element being dragged
					 * @param {jQuery} helper the helper shown next to the mouse
					 * @param {jQuery} event the element that is scrolling
					 */
					$.vakata.dnd._trigger("scroll", vakata_dnd.scroll_e);
				}
			},
			start : function (e, data, html) {
				if(e.type === "touchstart" && e.originalEvent && e.originalEvent.changedTouches && e.originalEvent.changedTouches[0]) {
					e.pageX = e.originalEvent.changedTouches[0].pageX;
					e.pageY = e.originalEvent.changedTouches[0].pageY;
					e.target = document.elementFromPoint(e.originalEvent.changedTouches[0].pageX - window.pageXOffset, e.originalEvent.changedTouches[0].pageY - window.pageYOffset);
				}
				if(vakata_dnd.is_drag) { $.vakata.dnd.stop({}); }
				try {
					e.currentTarget.unselectable = "on";
					e.currentTarget.onselectstart = function() { return false; };
					if(e.currentTarget.style) {
						e.currentTarget.style.touchAction = "none";
						e.currentTarget.style.msTouchAction = "none";
						e.currentTarget.style.MozUserSelect = "none";
					}
				} catch(ignore) { }
				vakata_dnd.init_x	= e.pageX;
				vakata_dnd.init_y	= e.pageY;
				vakata_dnd.data		= data;
				vakata_dnd.is_down	= true;
				vakata_dnd.element	= e.currentTarget;
				vakata_dnd.target	= e.target;
				vakata_dnd.is_touch	= e.type === "touchstart";
				if(html !== false) {
					vakata_dnd.helper = $("<div id='vakata-dnd'></div>").html(html).css({
						"display"		: "block",
						"margin"		: "0",
						"padding"		: "0",
						"position"		: "absolute",
						"top"			: "-2000px",
						"lineHeight"	: "16px",
						"zIndex"		: "10000"
					});
				}
				$(document).on("mousemove.vakata.jstree touchmove.vakata.jstree", $.vakata.dnd.drag);
				$(document).on("mouseup.vakata.jstree touchend.vakata.jstree", $.vakata.dnd.stop);
				return false;
			},
			drag : function (e) {
				if(e.type === "touchmove" && e.originalEvent && e.originalEvent.changedTouches && e.originalEvent.changedTouches[0]) {
					e.pageX = e.originalEvent.changedTouches[0].pageX;
					e.pageY = e.originalEvent.changedTouches[0].pageY;
					e.target = document.elementFromPoint(e.originalEvent.changedTouches[0].pageX - window.pageXOffset, e.originalEvent.changedTouches[0].pageY - window.pageYOffset);
				}
				if(!vakata_dnd.is_down) { return; }
				if(!vakata_dnd.is_drag) {
					if(
						Math.abs(e.pageX - vakata_dnd.init_x) > (vakata_dnd.is_touch ? $.vakata.dnd.settings.threshold_touch : $.vakata.dnd.settings.threshold) ||
						Math.abs(e.pageY - vakata_dnd.init_y) > (vakata_dnd.is_touch ? $.vakata.dnd.settings.threshold_touch : $.vakata.dnd.settings.threshold)
					) {
						if(vakata_dnd.helper) {
							vakata_dnd.helper.appendTo(document.body);
							vakata_dnd.helper_w = vakata_dnd.helper.outerWidth();
						}
						vakata_dnd.is_drag = true;
						$(vakata_dnd.target).one('click.vakata', false);
						/**
						 * triggered on the document when a drag starts
						 * @event
						 * @plugin dnd
						 * @name dnd_start.vakata
						 * @param {Mixed} data any data supplied with the call to $.vakata.dnd.start
						 * @param {DOM} element the DOM element being dragged
						 * @param {jQuery} helper the helper shown next to the mouse
						 * @param {Object} event the event that caused the start (probably mousemove)
						 */
						$.vakata.dnd._trigger("start", e);
					}
					else { return; }
				}

				var d  = false, w  = false,
					dh = false, wh = false,
					dw = false, ww = false,
					dt = false, dl = false,
					ht = false, hl = false;

				vakata_dnd.scroll_t = 0;
				vakata_dnd.scroll_l = 0;
				vakata_dnd.scroll_e = false;
				$($(e.target).parentsUntil("body").addBack().get().reverse())
					.filter(function () {
						return	(/^auto|scroll$/).test($(this).css("overflow")) &&
								(this.scrollHeight > this.offsetHeight || this.scrollWidth > this.offsetWidth);
					})
					.each(function () {
						var t = $(this), o = t.offset();
						if(this.scrollHeight > this.offsetHeight) {
							if(o.top + t.height() - e.pageY < $.vakata.dnd.settings.scroll_proximity)	{ vakata_dnd.scroll_t = 1; }
							if(e.pageY - o.top < $.vakata.dnd.settings.scroll_proximity)				{ vakata_dnd.scroll_t = -1; }
						}
						if(this.scrollWidth > this.offsetWidth) {
							if(o.left + t.width() - e.pageX < $.vakata.dnd.settings.scroll_proximity)	{ vakata_dnd.scroll_l = 1; }
							if(e.pageX - o.left < $.vakata.dnd.settings.scroll_proximity)				{ vakata_dnd.scroll_l = -1; }
						}
						if(vakata_dnd.scroll_t || vakata_dnd.scroll_l) {
							vakata_dnd.scroll_e = $(this);
							return false;
						}
					});

				if(!vakata_dnd.scroll_e) {
					d  = $(document); w = $(window);
					dh = d.height(); wh = w.height();
					dw = d.width(); ww = w.width();
					dt = d.scrollTop(); dl = d.scrollLeft();
					if(dh > wh && e.pageY - dt < $.vakata.dnd.settings.scroll_proximity)		{ vakata_dnd.scroll_t = -1;  }
					if(dh > wh && wh - (e.pageY - dt) < $.vakata.dnd.settings.scroll_proximity)	{ vakata_dnd.scroll_t = 1; }
					if(dw > ww && e.pageX - dl < $.vakata.dnd.settings.scroll_proximity)		{ vakata_dnd.scroll_l = -1; }
					if(dw > ww && ww - (e.pageX - dl) < $.vakata.dnd.settings.scroll_proximity)	{ vakata_dnd.scroll_l = 1; }
					if(vakata_dnd.scroll_t || vakata_dnd.scroll_l) {
						vakata_dnd.scroll_e = d;
					}
				}
				if(vakata_dnd.scroll_e) { $.vakata.dnd._scroll(true); }

				if(vakata_dnd.helper) {
					ht = parseInt(e.pageY + $.vakata.dnd.settings.helper_top, 10);
					hl = parseInt(e.pageX + $.vakata.dnd.settings.helper_left, 10);
					if(dh && ht + 25 > dh) { ht = dh - 50; }
					if(dw && hl + vakata_dnd.helper_w > dw) { hl = dw - (vakata_dnd.helper_w + 2); }
					vakata_dnd.helper.css({
						left	: hl + "px",
						top		: ht + "px"
					});
				}
				/**
				 * triggered on the document when a drag is in progress
				 * @event
				 * @plugin dnd
				 * @name dnd_move.vakata
				 * @param {Mixed} data any data supplied with the call to $.vakata.dnd.start
				 * @param {DOM} element the DOM element being dragged
				 * @param {jQuery} helper the helper shown next to the mouse
				 * @param {Object} event the event that caused this to trigger (most likely mousemove)
				 */
				$.vakata.dnd._trigger("move", e);
				return false;
			},
			stop : function (e) {
				if(e.type === "touchend" && e.originalEvent && e.originalEvent.changedTouches && e.originalEvent.changedTouches[0]) {
					e.pageX = e.originalEvent.changedTouches[0].pageX;
					e.pageY = e.originalEvent.changedTouches[0].pageY;
					e.target = document.elementFromPoint(e.originalEvent.changedTouches[0].pageX - window.pageXOffset, e.originalEvent.changedTouches[0].pageY - window.pageYOffset);
				}
				if(vakata_dnd.is_drag) {
					/**
					 * triggered on the document when a drag stops (the dragged element is dropped)
					 * @event
					 * @plugin dnd
					 * @name dnd_stop.vakata
					 * @param {Mixed} data any data supplied with the call to $.vakata.dnd.start
					 * @param {DOM} element the DOM element being dragged
					 * @param {jQuery} helper the helper shown next to the mouse
					 * @param {Object} event the event that caused the stop
					 */
					if (e.target !== vakata_dnd.target) {
						$(vakata_dnd.target).off('click.vakata');
					}
					$.vakata.dnd._trigger("stop", e);
				}
				else {
					if(e.type === "touchend" && e.target === vakata_dnd.target) {
						var to = setTimeout(function () { $(e.target).click(); }, 100);
						$(e.target).one('click', function() { if(to) { clearTimeout(to); } });
					}
				}
				$.vakata.dnd._clean();
				return false;
			}
		};
	}($));

	// include the dnd plugin by default
	// $.jstree.defaults.plugins.push("dnd");


/**
 * ### Massload plugin
 *
 * Adds massload functionality to jsTree, so that multiple nodes can be loaded in a single request (only useful with lazy loading).
 */

	/**
	 * massload configuration
	 *
	 * It is possible to set this to a standard jQuery-like AJAX config.
	 * In addition to the standard jQuery ajax options here you can supply functions for `data` and `url`, the functions will be run in the current instance's scope and a param will be passed indicating which node IDs need to be loaded, the return value of those functions will be used.
	 *
	 * You can also set this to a function, that function will receive the node IDs being loaded as argument and a second param which is a function (callback) which should be called with the result.
	 *
	 * Both the AJAX and the function approach rely on the same return value - an object where the keys are the node IDs, and the value is the children of that node as an array.
	 *
	 *	{
	 *		"id1" : [{ "text" : "Child of ID1", "id" : "c1" }, { "text" : "Another child of ID1", "id" : "c2" }],
	 *		"id2" : [{ "text" : "Child of ID2", "id" : "c3" }]
	 *	}
	 * 
	 * @name $.jstree.defaults.massload
	 * @plugin massload
	 */
	$.jstree.defaults.massload = null;
	$.jstree.plugins.massload = function (options, parent) {
		this.init = function (el, options) {
			this._data.massload = {};
			parent.init.call(this, el, options);
		};
		this._load_nodes = function (nodes, callback, is_callback, force_reload) {
			var s = this.settings.massload,
				nodesString = JSON.stringify(nodes),
				toLoad = [],
				m = this._model.data,
				i, j, dom;
			if (!is_callback) {
				for(i = 0, j = nodes.length; i < j; i++) {
					if(!m[nodes[i]] || ( (!m[nodes[i]].state.loaded && !m[nodes[i]].state.failed) || force_reload) ) {
						toLoad.push(nodes[i]);
						dom = this.get_node(nodes[i], true);
						if (dom && dom.length) {
							dom.addClass("jstree-loading").attr('aria-busy',true);
						}
					}
				}
				this._data.massload = {};
				if (toLoad.length) {
					if($.isFunction(s)) {
						return s.call(this, toLoad, $.proxy(function (data) {
							var i, j;
							if(data) {
								for(i in data) {
									if(data.hasOwnProperty(i)) {
										this._data.massload[i] = data[i];
									}
								}
							}
							for(i = 0, j = nodes.length; i < j; i++) {
								dom = this.get_node(nodes[i], true);
								if (dom && dom.length) {
									dom.removeClass("jstree-loading").attr('aria-busy',false);
								}
							}
							parent._load_nodes.call(this, nodes, callback, is_callback, force_reload);
						}, this));
					}
					if(typeof s === 'object' && s && s.url) {
						s = $.extend(true, {}, s);
						if($.isFunction(s.url)) {
							s.url = s.url.call(this, toLoad);
						}
						if($.isFunction(s.data)) {
							s.data = s.data.call(this, toLoad);
						}
						return $.ajax(s)
							.done($.proxy(function (data,t,x) {
									var i, j;
									if(data) {
										for(i in data) {
											if(data.hasOwnProperty(i)) {
												this._data.massload[i] = data[i];
											}
										}
									}
									for(i = 0, j = nodes.length; i < j; i++) {
										dom = this.get_node(nodes[i], true);
										if (dom && dom.length) {
											dom.removeClass("jstree-loading").attr('aria-busy',false);
										}
									}
									parent._load_nodes.call(this, nodes, callback, is_callback, force_reload);
								}, this))
							.fail($.proxy(function (f) {
									parent._load_nodes.call(this, nodes, callback, is_callback, force_reload);
								}, this));
					}
				}
			}
			return parent._load_nodes.call(this, nodes, callback, is_callback, force_reload);
		};
		this._load_node = function (obj, callback) {
			var data = this._data.massload[obj.id],
				rslt = null, dom;
			if(data) {
				rslt = this[typeof data === 'string' ? '_append_html_data' : '_append_json_data'](
					obj,
					typeof data === 'string' ? $($.parseHTML(data)).filter(function () { return this.nodeType !== 3; }) : data,
					function (status) { callback.call(this, status); }
				);
				dom = this.get_node(obj.id, true);
				if (dom && dom.length) {
					dom.removeClass("jstree-loading").attr('aria-busy',false);
				}
				delete this._data.massload[obj.id];
				return rslt;
			}
			return parent._load_node.call(this, obj, callback);
		};
	};

/**
 * ### Search plugin
 *
 * Adds search functionality to jsTree.
 */

	/**
	 * stores all defaults for the search plugin
	 * @name $.jstree.defaults.search
	 * @plugin search
	 */
	$.jstree.defaults.search = {
		/**
		 * a jQuery-like AJAX config, which jstree uses if a server should be queried for results.
		 *
		 * A `str` (which is the search string) parameter will be added with the request, an optional `inside` parameter will be added if the search is limited to a node id. The expected result is a JSON array with nodes that need to be opened so that matching nodes will be revealed.
		 * Leave this setting as `false` to not query the server. You can also set this to a function, which will be invoked in the instance's scope and receive 3 parameters - the search string, the callback to call with the array of nodes to load, and the optional node ID to limit the search to
		 * @name $.jstree.defaults.search.ajax
		 * @plugin search
		 */
		ajax : false,
		/**
		 * Indicates if the search should be fuzzy or not (should `chnd3` match `child node 3`). Default is `false`.
		 * @name $.jstree.defaults.search.fuzzy
		 * @plugin search
		 */
		fuzzy : false,
		/**
		 * Indicates if the search should be case sensitive. Default is `false`.
		 * @name $.jstree.defaults.search.case_sensitive
		 * @plugin search
		 */
		case_sensitive : false,
		/**
		 * Indicates if the tree should be filtered (by default) to show only matching nodes (keep in mind this can be a heavy on large trees in old browsers).
		 * This setting can be changed at runtime when calling the search method. Default is `false`.
		 * @name $.jstree.defaults.search.show_only_matches
		 * @plugin search
		 */
		show_only_matches : false,
		/**
		 * Indicates if the children of matched element are shown (when show_only_matches is true)
		 * This setting can be changed at runtime when calling the search method. Default is `false`.
		 * @name $.jstree.defaults.search.show_only_matches_children
		 * @plugin search
		 */
		show_only_matches_children : false,
		/**
		 * Indicates if all nodes opened to reveal the search result, should be closed when the search is cleared or a new search is performed. Default is `true`.
		 * @name $.jstree.defaults.search.close_opened_onclear
		 * @plugin search
		 */
		close_opened_onclear : true,
		/**
		 * Indicates if only leaf nodes should be included in search results. Default is `false`.
		 * @name $.jstree.defaults.search.search_leaves_only
		 * @plugin search
		 */
		search_leaves_only : false,
		/**
		 * If set to a function it wil be called in the instance's scope with two arguments - search string and node (where node will be every node in the structure, so use with caution).
		 * If the function returns a truthy value the node will be considered a match (it might not be displayed if search_only_leaves is set to true and the node is not a leaf). Default is `false`.
		 * @name $.jstree.defaults.search.search_callback
		 * @plugin search
		 */
		search_callback : false
	};

	$.jstree.plugins.search = function (options, parent) {
		this.bind = function () {
			parent.bind.call(this);

			this._data.search.str = "";
			this._data.search.dom = $();
			this._data.search.res = [];
			this._data.search.opn = [];
			this._data.search.som = false;
			this._data.search.smc = false;
			this._data.search.hdn = [];

			this.element
				.on("search.jstree", $.proxy(function (e, data) {
						if(this._data.search.som && data.res.length) {
							var m = this._model.data, i, j, p = [], k, l;
							for(i = 0, j = data.res.length; i < j; i++) {
								if(m[data.res[i]] && !m[data.res[i]].state.hidden) {
									p.push(data.res[i]);
									p = p.concat(m[data.res[i]].parents);
									if(this._data.search.smc) {
										for (k = 0, l = m[data.res[i]].children_d.length; k < l; k++) {
											if (m[m[data.res[i]].children_d[k]] && !m[m[data.res[i]].children_d[k]].state.hidden) {
												p.push(m[data.res[i]].children_d[k]);
											}
										}
									}
								}
							}
							p = $.vakata.array_remove_item($.vakata.array_unique(p), $.jstree.root);
							this._data.search.hdn = this.hide_all(true);
							this.show_node(p, true);
							this.redraw(true);
						}
					}, this))
				.on("clear_search.jstree", $.proxy(function (e, data) {
						if(this._data.search.som && data.res.length) {
							this.show_node(this._data.search.hdn, true);
							this.redraw(true);
						}
					}, this));
		};
		/**
		 * used to search the tree nodes for a given string
		 * @name search(str [, skip_async])
		 * @param {String} str the search string
		 * @param {Boolean} skip_async if set to true server will not be queried even if configured
		 * @param {Boolean} show_only_matches if set to true only matching nodes will be shown (keep in mind this can be very slow on large trees or old browsers)
		 * @param {mixed} inside an optional node to whose children to limit the search
		 * @param {Boolean} append if set to true the results of this search are appended to the previous search
		 * @plugin search
		 * @trigger search.jstree
		 */
		this.search = function (str, skip_async, show_only_matches, inside, append, show_only_matches_children) {
			if(str === false || $.trim(str.toString()) === "") {
				return this.clear_search();
			}
			inside = this.get_node(inside);
			inside = inside && inside.id ? inside.id : null;
			str = str.toString();
			var s = this.settings.search,
				a = s.ajax ? s.ajax : false,
				m = this._model.data,
				f = null,
				r = [],
				p = [], i, j;
			if(this._data.search.res.length && !append) {
				this.clear_search();
			}
			if(show_only_matches === undefined) {
				show_only_matches = s.show_only_matches;
			}
			if(show_only_matches_children === undefined) {
				show_only_matches_children = s.show_only_matches_children;
			}
			if(!skip_async && a !== false) {
				if($.isFunction(a)) {
					return a.call(this, str, $.proxy(function (d) {
							if(d && d.d) { d = d.d; }
							this._load_nodes(!$.isArray(d) ? [] : $.vakata.array_unique(d), function () {
								this.search(str, true, show_only_matches, inside, append, show_only_matches_children);
							});
						}, this), inside);
				}
				else {
					a = $.extend({}, a);
					if(!a.data) { a.data = {}; }
					a.data.str = str;
					if(inside) {
						a.data.inside = inside;
					}
					if (this._data.search.lastRequest) {
						this._data.search.lastRequest.abort();
					}
					this._data.search.lastRequest = $.ajax(a)
						.fail($.proxy(function () {
							this._data.core.last_error = { 'error' : 'ajax', 'plugin' : 'search', 'id' : 'search_01', 'reason' : 'Could not load search parents', 'data' : JSON.stringify(a) };
							this.settings.core.error.call(this, this._data.core.last_error);
						}, this))
						.done($.proxy(function (d) {
							if(d && d.d) { d = d.d; }
							this._load_nodes(!$.isArray(d) ? [] : $.vakata.array_unique(d), function () {
								this.search(str, true, show_only_matches, inside, append, show_only_matches_children);
							});
						}, this));
					return this._data.search.lastRequest;
				}
			}
			if(!append) {
				this._data.search.str = str;
				this._data.search.dom = $();
				this._data.search.res = [];
				this._data.search.opn = [];
				this._data.search.som = show_only_matches;
				this._data.search.smc = show_only_matches_children;
			}

			f = new $.vakata.search(str, true, { caseSensitive : s.case_sensitive, fuzzy : s.fuzzy });
			$.each(m[inside ? inside : $.jstree.root].children_d, function (ii, i) {
				var v = m[i];
				if(v.text && !v.state.hidden && (!s.search_leaves_only || (v.state.loaded && v.children.length === 0)) && ( (s.search_callback && s.search_callback.call(this, str, v)) || (!s.search_callback && f.search(v.text).isMatch) ) ) {
					r.push(i);
					p = p.concat(v.parents);
				}
			});
			if(r.length) {
				p = $.vakata.array_unique(p);
				for(i = 0, j = p.length; i < j; i++) {
					if(p[i] !== $.jstree.root && m[p[i]] && this.open_node(p[i], null, 0) === true) {
						this._data.search.opn.push(p[i]);
					}
				}
				if(!append) {
					this._data.search.dom = $(this.element[0].querySelectorAll('#' + $.map(r, function (v) { return "0123456789".indexOf(v[0]) !== -1 ? '\\3' + v[0] + ' ' + v.substr(1).replace($.jstree.idregex,'\\$&') : v.replace($.jstree.idregex,'\\$&'); }).join(', #')));
					this._data.search.res = r;
				}
				else {
					this._data.search.dom = this._data.search.dom.add($(this.element[0].querySelectorAll('#' + $.map(r, function (v) { return "0123456789".indexOf(v[0]) !== -1 ? '\\3' + v[0] + ' ' + v.substr(1).replace($.jstree.idregex,'\\$&') : v.replace($.jstree.idregex,'\\$&'); }).join(', #'))));
					this._data.search.res = $.vakata.array_unique(this._data.search.res.concat(r));
				}
				this._data.search.dom.children(".jstree-anchor").addClass('jstree-search');
			}
			/**
			 * triggered after search is complete
			 * @event
			 * @name search.jstree
			 * @param {jQuery} nodes a jQuery collection of matching nodes
			 * @param {String} str the search string
			 * @param {Array} res a collection of objects represeing the matching nodes
			 * @plugin search
			 */
			this.trigger('search', { nodes : this._data.search.dom, str : str, res : this._data.search.res, show_only_matches : show_only_matches });
		};
		/**
		 * used to clear the last search (removes classes and shows all nodes if filtering is on)
		 * @name clear_search()
		 * @plugin search
		 * @trigger clear_search.jstree
		 */
		this.clear_search = function () {
			if(this.settings.search.close_opened_onclear) {
				this.close_node(this._data.search.opn, 0);
			}
			/**
			 * triggered after search is complete
			 * @event
			 * @name clear_search.jstree
			 * @param {jQuery} nodes a jQuery collection of matching nodes (the result from the last search)
			 * @param {String} str the search string (the last search string)
			 * @param {Array} res a collection of objects represeing the matching nodes (the result from the last search)
			 * @plugin search
			 */
			this.trigger('clear_search', { 'nodes' : this._data.search.dom, str : this._data.search.str, res : this._data.search.res });
			if(this._data.search.res.length) {
				this._data.search.dom = $(this.element[0].querySelectorAll('#' + $.map(this._data.search.res, function (v) {
					return "0123456789".indexOf(v[0]) !== -1 ? '\\3' + v[0] + ' ' + v.substr(1).replace($.jstree.idregex,'\\$&') : v.replace($.jstree.idregex,'\\$&');
				}).join(', #')));
				this._data.search.dom.children(".jstree-anchor").removeClass("jstree-search");
			}
			this._data.search.str = "";
			this._data.search.res = [];
			this._data.search.opn = [];
			this._data.search.dom = $();
		};

		this.redraw_node = function(obj, deep, callback, force_render) {
			obj = parent.redraw_node.apply(this, arguments);
			if(obj) {
				if($.inArray(obj.id, this._data.search.res) !== -1) {
					var i, j, tmp = null;
					for(i = 0, j = obj.childNodes.length; i < j; i++) {
						if(obj.childNodes[i] && obj.childNodes[i].className && obj.childNodes[i].className.indexOf("jstree-anchor") !== -1) {
							tmp = obj.childNodes[i];
							break;
						}
					}
					if(tmp) {
						tmp.className += ' jstree-search';
					}
				}
			}
			return obj;
		};
	};

	// helpers
	(function ($) {
		// from http://kiro.me/projects/fuse.html
		$.vakata.search = function(pattern, txt, options) {
			options = options || {};
			options = $.extend({}, $.vakata.search.defaults, options);
			if(options.fuzzy !== false) {
				options.fuzzy = true;
			}
			pattern = options.caseSensitive ? pattern : pattern.toLowerCase();
			var MATCH_LOCATION	= options.location,
				MATCH_DISTANCE	= options.distance,
				MATCH_THRESHOLD	= options.threshold,
				patternLen = pattern.length,
				matchmask, pattern_alphabet, match_bitapScore, search;
			if(patternLen > 32) {
				options.fuzzy = false;
			}
			if(options.fuzzy) {
				matchmask = 1 << (patternLen - 1);
				pattern_alphabet = (function () {
					var mask = {},
						i = 0;
					for (i = 0; i < patternLen; i++) {
						mask[pattern.charAt(i)] = 0;
					}
					for (i = 0; i < patternLen; i++) {
						mask[pattern.charAt(i)] |= 1 << (patternLen - i - 1);
					}
					return mask;
				}());
				match_bitapScore = function (e, x) {
					var accuracy = e / patternLen,
						proximity = Math.abs(MATCH_LOCATION - x);
					if(!MATCH_DISTANCE) {
						return proximity ? 1.0 : accuracy;
					}
					return accuracy + (proximity / MATCH_DISTANCE);
				};
			}
			search = function (text) {
				text = options.caseSensitive ? text : text.toLowerCase();
				if(pattern === text || text.indexOf(pattern) !== -1) {
					return {
						isMatch: true,
						score: 0
					};
				}
				if(!options.fuzzy) {
					return {
						isMatch: false,
						score: 1
					};
				}
				var i, j,
					textLen = text.length,
					scoreThreshold = MATCH_THRESHOLD,
					bestLoc = text.indexOf(pattern, MATCH_LOCATION),
					binMin, binMid,
					binMax = patternLen + textLen,
					lastRd, start, finish, rd, charMatch,
					score = 1,
					locations = [];
				if (bestLoc !== -1) {
					scoreThreshold = Math.min(match_bitapScore(0, bestLoc), scoreThreshold);
					bestLoc = text.lastIndexOf(pattern, MATCH_LOCATION + patternLen);
					if (bestLoc !== -1) {
						scoreThreshold = Math.min(match_bitapScore(0, bestLoc), scoreThreshold);
					}
				}
				bestLoc = -1;
				for (i = 0; i < patternLen; i++) {
					binMin = 0;
					binMid = binMax;
					while (binMin < binMid) {
						if (match_bitapScore(i, MATCH_LOCATION + binMid) <= scoreThreshold) {
							binMin = binMid;
						} else {
							binMax = binMid;
						}
						binMid = Math.floor((binMax - binMin) / 2 + binMin);
					}
					binMax = binMid;
					start = Math.max(1, MATCH_LOCATION - binMid + 1);
					finish = Math.min(MATCH_LOCATION + binMid, textLen) + patternLen;
					rd = new Array(finish + 2);
					rd[finish + 1] = (1 << i) - 1;
					for (j = finish; j >= start; j--) {
						charMatch = pattern_alphabet[text.charAt(j - 1)];
						if (i === 0) {
							rd[j] = ((rd[j + 1] << 1) | 1) & charMatch;
						} else {
							rd[j] = ((rd[j + 1] << 1) | 1) & charMatch | (((lastRd[j + 1] | lastRd[j]) << 1) | 1) | lastRd[j + 1];
						}
						if (rd[j] & matchmask) {
							score = match_bitapScore(i, j - 1);
							if (score <= scoreThreshold) {
								scoreThreshold = score;
								bestLoc = j - 1;
								locations.push(bestLoc);
								if (bestLoc > MATCH_LOCATION) {
									start = Math.max(1, 2 * MATCH_LOCATION - bestLoc);
								} else {
									break;
								}
							}
						}
					}
					if (match_bitapScore(i + 1, MATCH_LOCATION) > scoreThreshold) {
						break;
					}
					lastRd = rd;
				}
				return {
					isMatch: bestLoc >= 0,
					score: score
				};
			};
			return txt === true ? { 'search' : search } : search(txt);
		};
		$.vakata.search.defaults = {
			location : 0,
			distance : 100,
			threshold : 0.6,
			fuzzy : false,
			caseSensitive : false
		};
	}($));

	// include the search plugin by default
	// $.jstree.defaults.plugins.push("search");


/**
 * ### Sort plugin
 *
 * Automatically sorts all siblings in the tree according to a sorting function.
 */

	/**
	 * the settings function used to sort the nodes.
	 * It is executed in the tree's context, accepts two nodes as arguments and should return `1` or `-1`.
	 * @name $.jstree.defaults.sort
	 * @plugin sort
	 */
	$.jstree.defaults.sort = function (a, b) {
		//return this.get_type(a) === this.get_type(b) ? (this.get_text(a) > this.get_text(b) ? 1 : -1) : this.get_type(a) >= this.get_type(b);
		return this.get_text(a) > this.get_text(b) ? 1 : -1;
	};
	$.jstree.plugins.sort = function (options, parent) {
		this.bind = function () {
			parent.bind.call(this);
			this.element
				.on("model.jstree", $.proxy(function (e, data) {
						this.sort(data.parent, true);
					}, this))
				.on("rename_node.jstree create_node.jstree", $.proxy(function (e, data) {
						this.sort(data.parent || data.node.parent, false);
						this.redraw_node(data.parent || data.node.parent, true);
					}, this))
				.on("move_node.jstree copy_node.jstree", $.proxy(function (e, data) {
						this.sort(data.parent, false);
						this.redraw_node(data.parent, true);
					}, this));
		};
		/**
		 * used to sort a node's children
		 * @private
		 * @name sort(obj [, deep])
		 * @param  {mixed} obj the node
		 * @param {Boolean} deep if set to `true` nodes are sorted recursively.
		 * @plugin sort
		 * @trigger search.jstree
		 */
		this.sort = function (obj, deep) {
			var i, j;
			obj = this.get_node(obj);
			if(obj && obj.children && obj.children.length) {
				obj.children.sort($.proxy(this.settings.sort, this));
				if(deep) {
					for(i = 0, j = obj.children_d.length; i < j; i++) {
						this.sort(obj.children_d[i], false);
					}
				}
			}
		};
	};

	// include the sort plugin by default
	// $.jstree.defaults.plugins.push("sort");

/**
 * ### State plugin
 *
 * Saves the state of the tree (selected nodes, opened nodes) on the user's computer using available options (localStorage, cookies, etc)
 */

	var to = false;
	/**
	 * stores all defaults for the state plugin
	 * @name $.jstree.defaults.state
	 * @plugin state
	 */
	$.jstree.defaults.state = {
		/**
		 * A string for the key to use when saving the current tree (change if using multiple trees in your project). Defaults to `jstree`.
		 * @name $.jstree.defaults.state.key
		 * @plugin state
		 */
		key		: 'jstree',
		/**
		 * A space separated list of events that trigger a state save. Defaults to `changed.jstree open_node.jstree close_node.jstree`.
		 * @name $.jstree.defaults.state.events
		 * @plugin state
		 */
		events	: 'changed.jstree open_node.jstree close_node.jstree check_node.jstree uncheck_node.jstree',
		/**
		 * Time in milliseconds after which the state will expire. Defaults to 'false' meaning - no expire.
		 * @name $.jstree.defaults.state.ttl
		 * @plugin state
		 */
		ttl		: false,
		/**
		 * A function that will be executed prior to restoring state with one argument - the state object. Can be used to clear unwanted parts of the state.
		 * @name $.jstree.defaults.state.filter
		 * @plugin state
		 */
		filter	: false,
		/**
		 * Should loaded nodes be restored (setting this to true means that it is possible that the whole tree will be loaded for some users - use with caution). Defaults to `false`
		 * @name $.jstree.defaults.state.preserve_loaded
		 * @plugin state
		 */
		preserve_loaded : false
	};
	$.jstree.plugins.state = function (options, parent) {
		this.bind = function () {
			parent.bind.call(this);
			var bind = $.proxy(function () {
				this.element.on(this.settings.state.events, $.proxy(function () {
					if(to) { clearTimeout(to); }
					to = setTimeout($.proxy(function () { this.save_state(); }, this), 100);
				}, this));
				/**
				 * triggered when the state plugin is finished restoring the state (and immediately after ready if there is no state to restore).
				 * @event
				 * @name state_ready.jstree
				 * @plugin state
				 */
				this.trigger('state_ready');
			}, this);
			this.element
				.on("ready.jstree", $.proxy(function (e, data) {
						this.element.one("restore_state.jstree", bind);
						if(!this.restore_state()) { bind(); }
					}, this));
		};
		/**
		 * save the state
		 * @name save_state()
		 * @plugin state
		 */
		this.save_state = function () {
			var tm = this.get_state();
			if (!this.settings.state.preserve_loaded) {
				delete tm.core.loaded;
			}
			var st = { 'state' : tm, 'ttl' : this.settings.state.ttl, 'sec' : +(new Date()) };
			$.vakata.storage.set(this.settings.state.key, JSON.stringify(st));
		};
		/**
		 * restore the state from the user's computer
		 * @name restore_state()
		 * @plugin state
		 */
		this.restore_state = function () {
			var k = $.vakata.storage.get(this.settings.state.key);
			if(!!k) { try { k = JSON.parse(k); } catch(ex) { return false; } }
			if(!!k && k.ttl && k.sec && +(new Date()) - k.sec > k.ttl) { return false; }
			if(!!k && k.state) { k = k.state; }
			if(!!k && $.isFunction(this.settings.state.filter)) { k = this.settings.state.filter.call(this, k); }
			if(!!k) {
				if (!this.settings.state.preserve_loaded) {
					delete k.core.loaded;
				}
				this.element.one("set_state.jstree", function (e, data) { data.instance.trigger('restore_state', { 'state' : $.extend(true, {}, k) }); });
				this.set_state(k);
				return true;
			}
			return false;
		};
		/**
		 * clear the state on the user's computer
		 * @name clear_state()
		 * @plugin state
		 */
		this.clear_state = function () {
			return $.vakata.storage.del(this.settings.state.key);
		};
	};

	(function ($, undefined) {
		$.vakata.storage = {
			// simply specifying the functions in FF throws an error
			set : function (key, val) { return window.localStorage.setItem(key, val); },
			get : function (key) { return window.localStorage.getItem(key); },
			del : function (key) { return window.localStorage.removeItem(key); }
		};
	}($));

	// include the state plugin by default
	// $.jstree.defaults.plugins.push("state");

/**
 * ### Types plugin
 *
 * Makes it possible to add predefined types for groups of nodes, which make it possible to easily control nesting rules and icon for each group.
 */

	/**
	 * An object storing all types as key value pairs, where the key is the type name and the value is an object that could contain following keys (all optional).
	 *
	 * * `max_children` the maximum number of immediate children this node type can have. Do not specify or set to `-1` for unlimited.
	 * * `max_depth` the maximum number of nesting this node type can have. A value of `1` would mean that the node can have children, but no grandchildren. Do not specify or set to `-1` for unlimited.
	 * * `valid_children` an array of node type strings, that nodes of this type can have as children. Do not specify or set to `-1` for no limits.
	 * * `icon` a string - can be a path to an icon or a className, if using an image that is in the current directory use a `./` prefix, otherwise it will be detected as a class. Omit to use the default icon from your theme.
	 * * `li_attr` an object of values which will be used to add HTML attributes on the resulting LI DOM node (merged with the node's own data)
	 * * `a_attr` an object of values which will be used to add HTML attributes on the resulting A DOM node (merged with the node's own data)
	 *
	 * There are two predefined types:
	 *
	 * * `#` represents the root of the tree, for example `max_children` would control the maximum number of root nodes.
	 * * `default` represents the default node - any settings here will be applied to all nodes that do not have a type specified.
	 *
	 * @name $.jstree.defaults.types
	 * @plugin types
	 */
	$.jstree.defaults.types = {
		'default' : {}
	};
	$.jstree.defaults.types[$.jstree.root] = {};

	$.jstree.plugins.types = function (options, parent) {
		this.init = function (el, options) {
			var i, j;
			if(options && options.types && options.types['default']) {
				for(i in options.types) {
					if(i !== "default" && i !== $.jstree.root && options.types.hasOwnProperty(i)) {
						for(j in options.types['default']) {
							if(options.types['default'].hasOwnProperty(j) && options.types[i][j] === undefined) {
								options.types[i][j] = options.types['default'][j];
							}
						}
					}
				}
			}
			parent.init.call(this, el, options);
			this._model.data[$.jstree.root].type = $.jstree.root;
		};
		this.refresh = function (skip_loading, forget_state) {
			parent.refresh.call(this, skip_loading, forget_state);
			this._model.data[$.jstree.root].type = $.jstree.root;
		};
		this.bind = function () {
			this.element
				.on('model.jstree', $.proxy(function (e, data) {
						var m = this._model.data,
							dpc = data.nodes,
							t = this.settings.types,
							i, j, c = 'default', k;
						for(i = 0, j = dpc.length; i < j; i++) {
							c = 'default';
							if(m[dpc[i]].original && m[dpc[i]].original.type && t[m[dpc[i]].original.type]) {
								c = m[dpc[i]].original.type;
							}
							if(m[dpc[i]].data && m[dpc[i]].data.jstree && m[dpc[i]].data.jstree.type && t[m[dpc[i]].data.jstree.type]) {
								c = m[dpc[i]].data.jstree.type;
							}
							m[dpc[i]].type = c;
							if(m[dpc[i]].icon === true && t[c].icon !== undefined) {
								m[dpc[i]].icon = t[c].icon;
							}
							if(t[c].li_attr !== undefined && typeof t[c].li_attr === 'object') {
								for (k in t[c].li_attr) {
									if (t[c].li_attr.hasOwnProperty(k)) {
										if (k === 'id') {
											continue;
										}
										else if (m[dpc[i]].li_attr[k] === undefined) {
											m[dpc[i]].li_attr[k] = t[c].li_attr[k];
										}
										else if (k === 'class') {
											m[dpc[i]].li_attr['class'] = t[c].li_attr['class'] + ' ' + m[dpc[i]].li_attr['class'];
										}
									}
								}
							}
							if(t[c].a_attr !== undefined && typeof t[c].a_attr === 'object') {
								for (k in t[c].a_attr) {
									if (t[c].a_attr.hasOwnProperty(k)) {
										if (k === 'id') {
											continue;
										}
										else if (m[dpc[i]].a_attr[k] === undefined) {
											m[dpc[i]].a_attr[k] = t[c].a_attr[k];
										}
										else if (k === 'href' && m[dpc[i]].a_attr[k] === '#') {
											m[dpc[i]].a_attr['href'] = t[c].a_attr['href'];
										}
										else if (k === 'class') {
											m[dpc[i]].a_attr['class'] = t[c].a_attr['class'] + ' ' + m[dpc[i]].a_attr['class'];
										}
									}
								}
							}
						}
						m[$.jstree.root].type = $.jstree.root;
					}, this));
			parent.bind.call(this);
		};
		this.get_json = function (obj, options, flat) {
			var i, j,
				m = this._model.data,
				opt = options ? $.extend(true, {}, options, {no_id:false}) : {},
				tmp = parent.get_json.call(this, obj, opt, flat);
			if(tmp === false) { return false; }
			if($.isArray(tmp)) {
				for(i = 0, j = tmp.length; i < j; i++) {
					tmp[i].type = tmp[i].id && m[tmp[i].id] && m[tmp[i].id].type ? m[tmp[i].id].type : "default";
					if(options && options.no_id) {
						delete tmp[i].id;
						if(tmp[i].li_attr && tmp[i].li_attr.id) {
							delete tmp[i].li_attr.id;
						}
						if(tmp[i].a_attr && tmp[i].a_attr.id) {
							delete tmp[i].a_attr.id;
						}
					}
				}
			}
			else {
				tmp.type = tmp.id && m[tmp.id] && m[tmp.id].type ? m[tmp.id].type : "default";
				if(options && options.no_id) {
					tmp = this._delete_ids(tmp);
				}
			}
			return tmp;
		};
		this._delete_ids = function (tmp) {
			if($.isArray(tmp)) {
				for(var i = 0, j = tmp.length; i < j; i++) {
					tmp[i] = this._delete_ids(tmp[i]);
				}
				return tmp;
			}
			delete tmp.id;
			if(tmp.li_attr && tmp.li_attr.id) {
				delete tmp.li_attr.id;
			}
			if(tmp.a_attr && tmp.a_attr.id) {
				delete tmp.a_attr.id;
			}
			if(tmp.children && $.isArray(tmp.children)) {
				tmp.children = this._delete_ids(tmp.children);
			}
			return tmp;
		};
		this.check = function (chk, obj, par, pos, more) {
			if(parent.check.call(this, chk, obj, par, pos, more) === false) { return false; }
			obj = obj && obj.id ? obj : this.get_node(obj);
			par = par && par.id ? par : this.get_node(par);
			var m = obj && obj.id ? (more && more.origin ? more.origin : $.jstree.reference(obj.id)) : null, tmp, d, i, j;
			m = m && m._model && m._model.data ? m._model.data : null;
			switch(chk) {
				case "create_node":
				case "move_node":
				case "copy_node":
					if(chk !== 'move_node' || $.inArray(obj.id, par.children) === -1) {
						tmp = this.get_rules(par);
						if(tmp.max_children !== undefined && tmp.max_children !== -1 && tmp.max_children === par.children.length) {
							this._data.core.last_error = { 'error' : 'check', 'plugin' : 'types', 'id' : 'types_01', 'reason' : 'max_children prevents function: ' + chk, 'data' : JSON.stringify({ 'chk' : chk, 'pos' : pos, 'obj' : obj && obj.id ? obj.id : false, 'par' : par && par.id ? par.id : false }) };
							return false;
						}
						if(tmp.valid_children !== undefined && tmp.valid_children !== -1 && $.inArray((obj.type || 'default'), tmp.valid_children) === -1) {
							this._data.core.last_error = { 'error' : 'check', 'plugin' : 'types', 'id' : 'types_02', 'reason' : 'valid_children prevents function: ' + chk, 'data' : JSON.stringify({ 'chk' : chk, 'pos' : pos, 'obj' : obj && obj.id ? obj.id : false, 'par' : par && par.id ? par.id : false }) };
							return false;
						}
						if(m && obj.children_d && obj.parents) {
							d = 0;
							for(i = 0, j = obj.children_d.length; i < j; i++) {
								d = Math.max(d, m[obj.children_d[i]].parents.length);
							}
							d = d - obj.parents.length + 1;
						}
						if(d <= 0 || d === undefined) { d = 1; }
						do {
							if(tmp.max_depth !== undefined && tmp.max_depth !== -1 && tmp.max_depth < d) {
								this._data.core.last_error = { 'error' : 'check', 'plugin' : 'types', 'id' : 'types_03', 'reason' : 'max_depth prevents function: ' + chk, 'data' : JSON.stringify({ 'chk' : chk, 'pos' : pos, 'obj' : obj && obj.id ? obj.id : false, 'par' : par && par.id ? par.id : false }) };
								return false;
							}
							par = this.get_node(par.parent);
							tmp = this.get_rules(par);
							d++;
						} while(par);
					}
					break;
			}
			return true;
		};
		/**
		 * used to retrieve the type settings object for a node
		 * @name get_rules(obj)
		 * @param {mixed} obj the node to find the rules for
		 * @return {Object}
		 * @plugin types
		 */
		this.get_rules = function (obj) {
			obj = this.get_node(obj);
			if(!obj) { return false; }
			var tmp = this.get_type(obj, true);
			if(tmp.max_depth === undefined) { tmp.max_depth = -1; }
			if(tmp.max_children === undefined) { tmp.max_children = -1; }
			if(tmp.valid_children === undefined) { tmp.valid_children = -1; }
			return tmp;
		};
		/**
		 * used to retrieve the type string or settings object for a node
		 * @name get_type(obj [, rules])
		 * @param {mixed} obj the node to find the rules for
		 * @param {Boolean} rules if set to `true` instead of a string the settings object will be returned
		 * @return {String|Object}
		 * @plugin types
		 */
		this.get_type = function (obj, rules) {
			obj = this.get_node(obj);
			return (!obj) ? false : ( rules ? $.extend({ 'type' : obj.type }, this.settings.types[obj.type]) : obj.type);
		};
		/**
		 * used to change a node's type
		 * @name set_type(obj, type)
		 * @param {mixed} obj the node to change
		 * @param {String} type the new type
		 * @plugin types
		 */
		this.set_type = function (obj, type) {
			var m = this._model.data, t, t1, t2, old_type, old_icon, k, d, a;
			if($.isArray(obj)) {
				obj = obj.slice();
				for(t1 = 0, t2 = obj.length; t1 < t2; t1++) {
					this.set_type(obj[t1], type);
				}
				return true;
			}
			t = this.settings.types;
			obj = this.get_node(obj);
			if(!t[type] || !obj) { return false; }
			d = this.get_node(obj, true);
			if (d && d.length) {
				a = d.children('.jstree-anchor');
			}
			old_type = obj.type;
			old_icon = this.get_icon(obj);
			obj.type = type;
			if(old_icon === true || !t[old_type] || (t[old_type].icon !== undefined && old_icon === t[old_type].icon)) {
				this.set_icon(obj, t[type].icon !== undefined ? t[type].icon : true);
			}

			// remove old type props
			if(t[old_type] && t[old_type].li_attr !== undefined && typeof t[old_type].li_attr === 'object') {
				for (k in t[old_type].li_attr) {
					if (t[old_type].li_attr.hasOwnProperty(k)) {
						if (k === 'id') {
							continue;
						}
						else if (k === 'class') {
							m[obj.id].li_attr['class'] = (m[obj.id].li_attr['class'] || '').replace(t[old_type].li_attr[k], '');
							if (d) { d.removeClass(t[old_type].li_attr[k]); }
						}
						else if (m[obj.id].li_attr[k] === t[old_type].li_attr[k]) {
							m[obj.id].li_attr[k] = null;
							if (d) { d.removeAttr(k); }
						}
					}
				}
			}
			if(t[old_type] && t[old_type].a_attr !== undefined && typeof t[old_type].a_attr === 'object') {
				for (k in t[old_type].a_attr) {
					if (t[old_type].a_attr.hasOwnProperty(k)) {
						if (k === 'id') {
							continue;
						}
						else if (k === 'class') {
							m[obj.id].a_attr['class'] = (m[obj.id].a_attr['class'] || '').replace(t[old_type].a_attr[k], '');
							if (a) { a.removeClass(t[old_type].a_attr[k]); }
						}
						else if (m[obj.id].a_attr[k] === t[old_type].a_attr[k]) {
							if (k === 'href') {
								m[obj.id].a_attr[k] = '#';
								if (a) { a.attr('href', '#'); }
							}
							else {
								delete m[obj.id].a_attr[k];
								if (a) { a.removeAttr(k); }
							}
						}
					}
				}
			}

			// add new props
			if(t[type].li_attr !== undefined && typeof t[type].li_attr === 'object') {
				for (k in t[type].li_attr) {
					if (t[type].li_attr.hasOwnProperty(k)) {
						if (k === 'id') {
							continue;
						}
						else if (m[obj.id].li_attr[k] === undefined) {
							m[obj.id].li_attr[k] = t[type].li_attr[k];
							if (d) {
								if (k === 'class') {
									d.addClass(t[type].li_attr[k]);
								}
								else {
									d.attr(k, t[type].li_attr[k]);
								}
							}
						}
						else if (k === 'class') {
							m[obj.id].li_attr['class'] = t[type].li_attr[k] + ' ' + m[obj.id].li_attr['class'];
							if (d) { d.addClass(t[type].li_attr[k]); }
						}
					}
				}
			}
			if(t[type].a_attr !== undefined && typeof t[type].a_attr === 'object') {
				for (k in t[type].a_attr) {
					if (t[type].a_attr.hasOwnProperty(k)) {
						if (k === 'id') {
							continue;
						}
						else if (m[obj.id].a_attr[k] === undefined) {
							m[obj.id].a_attr[k] = t[type].a_attr[k];
							if (a) {
								if (k === 'class') {
									a.addClass(t[type].a_attr[k]);
								}
								else {
									a.attr(k, t[type].a_attr[k]);
								}
							}
						}
						else if (k === 'href' && m[obj.id].a_attr[k] === '#') {
							m[obj.id].a_attr['href'] = t[type].a_attr['href'];
							if (a) { a.attr('href', t[type].a_attr['href']); }
						}
						else if (k === 'class') {
							m[obj.id].a_attr['class'] = t[type].a_attr['class'] + ' ' + m[obj.id].a_attr['class'];
							if (a) { a.addClass(t[type].a_attr[k]); }
						}
					}
				}
			}

			return true;
		};
	};
	// include the types plugin by default
	// $.jstree.defaults.plugins.push("types");


/**
 * ### Unique plugin
 *
 * Enforces that no nodes with the same name can coexist as siblings.
 */

	/**
	 * stores all defaults for the unique plugin
	 * @name $.jstree.defaults.unique
	 * @plugin unique
	 */
	$.jstree.defaults.unique = {
		/**
		 * Indicates if the comparison should be case sensitive. Default is `false`.
		 * @name $.jstree.defaults.unique.case_sensitive
		 * @plugin unique
		 */
		case_sensitive : false,
		/**
		 * Indicates if white space should be trimmed before the comparison. Default is `false`.
		 * @name $.jstree.defaults.unique.trim_whitespace
		 * @plugin unique
		 */
		trim_whitespace : false,
		/**
		 * A callback executed in the instance's scope when a new node is created and the name is already taken, the two arguments are the conflicting name and the counter. The default will produce results like `New node (2)`.
		 * @name $.jstree.defaults.unique.duplicate
		 * @plugin unique
		 */
		duplicate : function (name, counter) {
			return name + ' (' + counter + ')';
		}
	};

	$.jstree.plugins.unique = function (options, parent) {
		this.check = function (chk, obj, par, pos, more) {
			if(parent.check.call(this, chk, obj, par, pos, more) === false) { return false; }
			obj = obj && obj.id ? obj : this.get_node(obj);
			par = par && par.id ? par : this.get_node(par);
			if(!par || !par.children) { return true; }
			var n = chk === "rename_node" ? pos : obj.text,
				c = [],
				s = this.settings.unique.case_sensitive,
				w = this.settings.unique.trim_whitespace,
				m = this._model.data, i, j, t;
			for(i = 0, j = par.children.length; i < j; i++) {
				t = m[par.children[i]].text;
				if (!s) {
					t = t.toLowerCase();
				}
				if (w) {
					t = t.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, '');
				}
				c.push(t);
			}
			if(!s) { n = n.toLowerCase(); }
			if (w) { n = n.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, ''); }
			switch(chk) {
				case "delete_node":
					return true;
				case "rename_node":
					t = obj.text || '';
					if (!s) {
						t = t.toLowerCase();
					}
					if (w) {
						t = t.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, '');
					}
					i = ($.inArray(n, c) === -1 || (obj.text && t === n));
					if(!i) {
						this._data.core.last_error = { 'error' : 'check', 'plugin' : 'unique', 'id' : 'unique_01', 'reason' : 'Child with name ' + n + ' already exists. Preventing: ' + chk, 'data' : JSON.stringify({ 'chk' : chk, 'pos' : pos, 'obj' : obj && obj.id ? obj.id : false, 'par' : par && par.id ? par.id : false }) };
					}
					return i;
				case "create_node":
					i = ($.inArray(n, c) === -1);
					if(!i) {
						this._data.core.last_error = { 'error' : 'check', 'plugin' : 'unique', 'id' : 'unique_04', 'reason' : 'Child with name ' + n + ' already exists. Preventing: ' + chk, 'data' : JSON.stringify({ 'chk' : chk, 'pos' : pos, 'obj' : obj && obj.id ? obj.id : false, 'par' : par && par.id ? par.id : false }) };
					}
					return i;
				case "copy_node":
					i = ($.inArray(n, c) === -1);
					if(!i) {
						this._data.core.last_error = { 'error' : 'check', 'plugin' : 'unique', 'id' : 'unique_02', 'reason' : 'Child with name ' + n + ' already exists. Preventing: ' + chk, 'data' : JSON.stringify({ 'chk' : chk, 'pos' : pos, 'obj' : obj && obj.id ? obj.id : false, 'par' : par && par.id ? par.id : false }) };
					}
					return i;
				case "move_node":
					i = ( (obj.parent === par.id && (!more || !more.is_multi)) || $.inArray(n, c) === -1);
					if(!i) {
						this._data.core.last_error = { 'error' : 'check', 'plugin' : 'unique', 'id' : 'unique_03', 'reason' : 'Child with name ' + n + ' already exists. Preventing: ' + chk, 'data' : JSON.stringify({ 'chk' : chk, 'pos' : pos, 'obj' : obj && obj.id ? obj.id : false, 'par' : par && par.id ? par.id : false }) };
					}
					return i;
			}
			return true;
		};
		this.create_node = function (par, node, pos, callback, is_loaded) {
			if(!node || node.text === undefined) {
				if(par === null) {
					par = $.jstree.root;
				}
				par = this.get_node(par);
				if(!par) {
					return parent.create_node.call(this, par, node, pos, callback, is_loaded);
				}
				pos = pos === undefined ? "last" : pos;
				if(!pos.toString().match(/^(before|after)$/) && !is_loaded && !this.is_loaded(par)) {
					return parent.create_node.call(this, par, node, pos, callback, is_loaded);
				}
				if(!node) { node = {}; }
				var tmp, n, dpc, i, j, m = this._model.data, s = this.settings.unique.case_sensitive, w = this.settings.unique.trim_whitespace, cb = this.settings.unique.duplicate, t;
				n = tmp = this.get_string('New node');
				dpc = [];
				for(i = 0, j = par.children.length; i < j; i++) {
					t = m[par.children[i]].text;
					if (!s) {
						t = t.toLowerCase();
					}
					if (w) {
						t = t.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, '');
					}
					dpc.push(t);
				}
				i = 1;
				t = n;
				if (!s) {
					t = t.toLowerCase();
				}
				if (w) {
					t = t.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, '');
				}
				while($.inArray(t, dpc) !== -1) {
					n = cb.call(this, tmp, (++i)).toString();
					t = n;
					if (!s) {
						t = t.toLowerCase();
					}
					if (w) {
						t = t.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, '');
					}
				}
				node.text = n;
			}
			return parent.create_node.call(this, par, node, pos, callback, is_loaded);
		};
	};

	// include the unique plugin by default
	// $.jstree.defaults.plugins.push("unique");


/**
 * ### Wholerow plugin
 *
 * Makes each node appear block level. Making selection easier. May cause slow down for large trees in old browsers.
 */

	var div = document.createElement('DIV');
	div.setAttribute('unselectable','on');
	div.setAttribute('role','presentation');
	div.className = 'jstree-wholerow';
	div.innerHTML = '&#160;';
	$.jstree.plugins.wholerow = function (options, parent) {
		this.bind = function () {
			parent.bind.call(this);

			this.element
				.on('ready.jstree set_state.jstree', $.proxy(function () {
						this.hide_dots();
					}, this))
				.on("init.jstree loading.jstree ready.jstree", $.proxy(function () {
						//div.style.height = this._data.core.li_height + 'px';
						this.get_container_ul().addClass('jstree-wholerow-ul');
					}, this))
				.on("deselect_all.jstree", $.proxy(function (e, data) {
						this.element.find('.jstree-wholerow-clicked').removeClass('jstree-wholerow-clicked');
					}, this))
				.on("changed.jstree", $.proxy(function (e, data) {
						this.element.find('.jstree-wholerow-clicked').removeClass('jstree-wholerow-clicked');
						var tmp = false, i, j;
						for(i = 0, j = data.selected.length; i < j; i++) {
							tmp = this.get_node(data.selected[i], true);
							if(tmp && tmp.length) {
								tmp.children('.jstree-wholerow').addClass('jstree-wholerow-clicked');
							}
						}
					}, this))
				.on("open_node.jstree", $.proxy(function (e, data) {
						this.get_node(data.node, true).find('.jstree-clicked').parent().children('.jstree-wholerow').addClass('jstree-wholerow-clicked');
					}, this))
				.on("hover_node.jstree dehover_node.jstree", $.proxy(function (e, data) {
						if(e.type === "hover_node" && this.is_disabled(data.node)) { return; }
						this.get_node(data.node, true).children('.jstree-wholerow')[e.type === "hover_node"?"addClass":"removeClass"]('jstree-wholerow-hovered');
					}, this))
				.on("contextmenu.jstree", ".jstree-wholerow", $.proxy(function (e) {
						if (this._data.contextmenu) {
							e.preventDefault();
							var tmp = $.Event('contextmenu', { metaKey : e.metaKey, ctrlKey : e.ctrlKey, altKey : e.altKey, shiftKey : e.shiftKey, pageX : e.pageX, pageY : e.pageY });
							$(e.currentTarget).closest(".jstree-node").children(".jstree-anchor").first().trigger(tmp);
						}
					}, this))
				/*!
				.on("mousedown.jstree touchstart.jstree", ".jstree-wholerow", function (e) {
						if(e.target === e.currentTarget) {
							var a = $(e.currentTarget).closest(".jstree-node").children(".jstree-anchor");
							e.target = a[0];
							a.trigger(e);
						}
					})
				*/
				.on("click.jstree", ".jstree-wholerow", function (e) {
						e.stopImmediatePropagation();
						var tmp = $.Event('click', { metaKey : e.metaKey, ctrlKey : e.ctrlKey, altKey : e.altKey, shiftKey : e.shiftKey });
						$(e.currentTarget).closest(".jstree-node").children(".jstree-anchor").first().trigger(tmp).focus();
					})
				.on("dblclick.jstree", ".jstree-wholerow", function (e) {
						e.stopImmediatePropagation();
						var tmp = $.Event('dblclick', { metaKey : e.metaKey, ctrlKey : e.ctrlKey, altKey : e.altKey, shiftKey : e.shiftKey });
						$(e.currentTarget).closest(".jstree-node").children(".jstree-anchor").first().trigger(tmp).focus();
					})
				.on("click.jstree", ".jstree-leaf > .jstree-ocl", $.proxy(function (e) {
						e.stopImmediatePropagation();
						var tmp = $.Event('click', { metaKey : e.metaKey, ctrlKey : e.ctrlKey, altKey : e.altKey, shiftKey : e.shiftKey });
						$(e.currentTarget).closest(".jstree-node").children(".jstree-anchor").first().trigger(tmp).focus();
					}, this))
				.on("mouseover.jstree", ".jstree-wholerow, .jstree-icon", $.proxy(function (e) {
						e.stopImmediatePropagation();
						if(!this.is_disabled(e.currentTarget)) {
							this.hover_node(e.currentTarget);
						}
						return false;
					}, this))
				.on("mouseleave.jstree", ".jstree-node", $.proxy(function (e) {
						this.dehover_node(e.currentTarget);
					}, this));
		};
		this.teardown = function () {
			if(this.settings.wholerow) {
				this.element.find(".jstree-wholerow").remove();
			}
			parent.teardown.call(this);
		};
		this.redraw_node = function(obj, deep, callback, force_render) {
			obj = parent.redraw_node.apply(this, arguments);
			if(obj) {
				var tmp = div.cloneNode(true);
				//tmp.style.height = this._data.core.li_height + 'px';
				if($.inArray(obj.id, this._data.core.selected) !== -1) { tmp.className += ' jstree-wholerow-clicked'; }
				if(this._data.core.focused && this._data.core.focused === obj.id) { tmp.className += ' jstree-wholerow-hovered'; }
				obj.insertBefore(tmp, obj.childNodes[0]);
			}
			return obj;
		};
	};
	// include the wholerow plugin by default
	// $.jstree.defaults.plugins.push("wholerow");
	if(window.customElements && Object && Object.create) {
		var proto = Object.create(HTMLElement.prototype);
		proto.createdCallback = function () {
			var c = { core : {}, plugins : [] }, i;
			for(i in $.jstree.plugins) {
				if($.jstree.plugins.hasOwnProperty(i) && this.attributes[i]) {
					c.plugins.push(i);
					if(this.getAttribute(i) && JSON.parse(this.getAttribute(i))) {
						c[i] = JSON.parse(this.getAttribute(i));
					}
				}
			}
			for(i in $.jstree.defaults.core) {
				if($.jstree.defaults.core.hasOwnProperty(i) && this.attributes[i]) {
					c.core[i] = JSON.parse(this.getAttribute(i)) || this.getAttribute(i);
				}
			}
			$(this).jstree(c);
		};
		// proto.attributeChangedCallback = function (name, previous, value) { };
		try {
			window.customElements.define("vakata-jstree", function() {}, { prototype: proto });
		} catch (ignore) { }
	}

}));admin/js/jstree/dist/themes/default-dark/style.min.css000064400000072277151327705670017027 0ustar00.jstree-node,.jstree-children,.jstree-container-ul{display:block;margin:0;padding:0;list-style-type:none;list-style-image:none}.jstree-node{white-space:nowrap}.jstree-anchor{display:inline-block;color:black;white-space:nowrap;padding:0 4px 0 1px;margin:0;vertical-align:top}.jstree-anchor:focus{outline:0}.jstree-anchor,.jstree-anchor:link,.jstree-anchor:visited,.jstree-anchor:hover,.jstree-anchor:active{text-decoration:none;color:inherit}.jstree-icon{display:inline-block;text-decoration:none;margin:0;padding:0;vertical-align:top;text-align:center}.jstree-icon:empty{display:inline-block;text-decoration:none;margin:0;padding:0;vertical-align:top;text-align:center}.jstree-ocl{cursor:pointer}.jstree-leaf>.jstree-ocl{cursor:default}.jstree .jstree-open>.jstree-children{display:block}.jstree .jstree-closed>.jstree-children,.jstree .jstree-leaf>.jstree-children{display:none}.jstree-anchor>.jstree-themeicon{margin-right:2px}.jstree-no-icons .jstree-themeicon,.jstree-anchor>.jstree-themeicon-hidden{display:none}.jstree-hidden,.jstree-node.jstree-hidden{display:none}.jstree-rtl .jstree-anchor{padding:0 1px 0 4px}.jstree-rtl .jstree-anchor>.jstree-themeicon{margin-left:2px;margin-right:0}.jstree-rtl .jstree-node{margin-left:0}.jstree-rtl .jstree-container-ul>.jstree-node{margin-right:0}.jstree-wholerow-ul{position:relative;display:inline-block;min-width:100%}.jstree-wholerow-ul .jstree-leaf>.jstree-ocl{cursor:pointer}.jstree-wholerow-ul .jstree-anchor,.jstree-wholerow-ul .jstree-icon{position:relative}.jstree-wholerow-ul .jstree-wholerow{width:100%;cursor:pointer;position:absolute;left:0;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.jstree-contextmenu .jstree-anchor{-webkit-user-select:none;-webkit-touch-callout:none}.vakata-context{display:none}.vakata-context,.vakata-context ul{margin:0;padding:2px;position:absolute;background:#f5f5f5;border:1px solid #979797;box-shadow:2px 2px 2px #999999}.vakata-context ul{list-style:none;left:100%;margin-top:-2.7em;margin-left:-4px}.vakata-context .vakata-context-right ul{left:auto;right:100%;margin-left:auto;margin-right:-4px}.vakata-context li{list-style:none}.vakata-context li>a{display:block;padding:0 2em 0 2em;text-decoration:none;width:auto;color:black;white-space:nowrap;line-height:2.4em;text-shadow:1px 1px 0 white;border-radius:1px}.vakata-context li>a:hover{position:relative;background-color:#e8eff7;box-shadow:0 0 2px #0a6aa1}.vakata-context li>a.vakata-context-parent{background-image:url("data:image/gif;base64,R0lGODlhCwAHAIAAACgoKP///yH5BAEAAAEALAAAAAALAAcAAAIORI4JlrqN1oMSnmmZDQUAOw==");background-position:right center;background-repeat:no-repeat}.vakata-context li>a:focus{outline:0}.vakata-context .vakata-context-hover>a{position:relative;background-color:#e8eff7;box-shadow:0 0 2px #0a6aa1}.vakata-context .vakata-context-separator>a,.vakata-context .vakata-context-separator>a:hover{background:white;border:0;border-top:1px solid #e2e3e3;height:1px;min-height:1px;max-height:1px;padding:0;margin:0 0 0 2.4em;border-left:1px solid #e0e0e0;text-shadow:0 0 0 transparent;box-shadow:0 0 0 transparent;border-radius:0}.vakata-context .vakata-contextmenu-disabled a,.vakata-context .vakata-contextmenu-disabled a:hover{color:silver;background-color:transparent;border:0;box-shadow:0 0 0}.vakata-context .vakata-contextmenu-disabled>a>i{filter:grayscale(100%)}.vakata-context li>a>i{text-decoration:none;display:inline-block;width:2.4em;height:2.4em;background:transparent;margin:0 0 0 -2em;vertical-align:top;text-align:center;line-height:2.4em}.vakata-context li>a>i:empty{width:2.4em;line-height:2.4em}.vakata-context li>a .vakata-contextmenu-sep{display:inline-block;width:1px;height:2.4em;background:white;margin:0 .5em 0 0;border-left:1px solid #e2e3e3}.vakata-context .vakata-contextmenu-shortcut{font-size:.8em;color:silver;opacity:.5;display:none}.vakata-context-rtl ul{left:auto;right:100%;margin-left:auto;margin-right:-4px}.vakata-context-rtl li>a.vakata-context-parent{background-image:url("data:image/gif;base64,R0lGODlhCwAHAIAAACgoKP///yH5BAEAAAEALAAAAAALAAcAAAINjI+AC7rWHIsPtmoxLAA7");background-position:left center;background-repeat:no-repeat}.vakata-context-rtl .vakata-context-separator>a{margin:0 2.4em 0 0;border-left:0;border-right:1px solid #e2e3e3}.vakata-context-rtl .vakata-context-left ul{right:auto;left:100%;margin-left:-4px;margin-right:auto}.vakata-context-rtl li>a>i{margin:0 -2em 0 0}.vakata-context-rtl li>a .vakata-contextmenu-sep{margin:0 0 0 .5em;border-left-color:white;background:#e2e3e3}#jstree-marker{position:absolute;top:0;left:0;margin:-5px 0 0 0;padding:0;border-right:0;border-top:5px solid transparent;border-bottom:5px solid transparent;border-left:5px solid;width:0;height:0;font-size:0;line-height:0}#jstree-dnd{line-height:16px;margin:0;padding:4px}#jstree-dnd .jstree-icon,#jstree-dnd .jstree-copy{display:inline-block;text-decoration:none;margin:0 2px 0 0;padding:0;width:16px;height:16px}#jstree-dnd .jstree-ok{background:green}#jstree-dnd .jstree-er{background:red}#jstree-dnd .jstree-copy{margin:0 2px 0 2px}.jstree-default-dark .jstree-node,.jstree-default-dark .jstree-icon{background-repeat:no-repeat;background-color:transparent}.jstree-default-dark .jstree-anchor,.jstree-default-dark .jstree-animated,.jstree-default-dark .jstree-wholerow{transition:background-color .15s,box-shadow .15s}.jstree-default-dark .jstree-hovered{background:#555;border-radius:2px;box-shadow:inset 0 0 1px #555}.jstree-default-dark .jstree-context{background:#555;border-radius:2px;box-shadow:inset 0 0 1px #555}.jstree-default-dark .jstree-clicked{background:#5fa2db;border-radius:2px;box-shadow:inset 0 0 1px #666666}.jstree-default-dark .jstree-no-icons .jstree-anchor>.jstree-themeicon{display:none}.jstree-default-dark .jstree-disabled{background:transparent;color:#666666}.jstree-default-dark .jstree-disabled.jstree-hovered{background:transparent;box-shadow:none}.jstree-default-dark .jstree-disabled.jstree-clicked{background:#333333}.jstree-default-dark .jstree-disabled>.jstree-icon{opacity:.8;filter:url("data:image/svg+xml;utf8,<svg xmlns=\'http://www.w3.org/2000/svg\'><filter id=\'jstree-grayscale\'><feColorMatrix type=\'matrix\' values=\'0.3333 0.3333 0.3333 0 0 0.3333 0.3333 0.3333 0 0 0.3333 0.3333 0.3333 0 0 0 0 0 1 0\'/></filter></svg>#jstree-grayscale");filter:gray;-webkit-filter:grayscale(100%)}.jstree-default-dark .jstree-search{font-style:italic;color:#ffffff;font-weight:bold}.jstree-default-dark .jstree-no-checkboxes .jstree-checkbox{display:none !important}.jstree-default-dark.jstree-checkbox-no-clicked .jstree-clicked{background:transparent;box-shadow:none}.jstree-default-dark.jstree-checkbox-no-clicked .jstree-clicked.jstree-hovered{background:#555}.jstree-default-dark.jstree-checkbox-no-clicked>.jstree-wholerow-ul .jstree-wholerow-clicked{background:transparent}.jstree-default-dark.jstree-checkbox-no-clicked>.jstree-wholerow-ul .jstree-wholerow-clicked.jstree-wholerow-hovered{background:#555}.jstree-default-dark>.jstree-striped{min-width:100%;display:inline-block;background:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAAkCAMAAAB/qqA+AAAABlBMVEUAAAAAAAClZ7nPAAAAAnRSTlMNAMM9s3UAAAAXSURBVHjajcEBAQAAAIKg/H/aCQZ70AUBjAATb6YPDgAAAABJRU5ErkJggg==") left top repeat}.jstree-default-dark>.jstree-wholerow-ul .jstree-hovered,.jstree-default-dark>.jstree-wholerow-ul .jstree-clicked{background:transparent;box-shadow:none;border-radius:0}.jstree-default-dark .jstree-wholerow{-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box}.jstree-default-dark .jstree-wholerow-hovered{background:#555}.jstree-default-dark .jstree-wholerow-clicked{background:#5fa2db;background:-webkit-linear-gradient(top, #5fa2db 0, #5fa2db 100%);background:linear-gradient(to bottom, #5fa2db 0, #5fa2db 100%)}.jstree-default-dark .jstree-node{min-height:24px;line-height:24px;margin-left:24px;min-width:24px}.jstree-default-dark .jstree-anchor{line-height:24px;height:24px}.jstree-default-dark .jstree-icon{width:24px;height:24px;line-height:24px}.jstree-default-dark .jstree-icon:empty{width:24px;height:24px;line-height:24px}.jstree-default-dark.jstree-rtl .jstree-node{margin-right:24px}.jstree-default-dark .jstree-wholerow{height:24px}.jstree-default-dark .jstree-node,.jstree-default-dark .jstree-icon{background-image:url("32px.png")}.jstree-default-dark .jstree-node{background-position:-292px -4px;background-repeat:repeat-y}.jstree-default-dark .jstree-last{background:transparent}.jstree-default-dark .jstree-open>.jstree-ocl{background-position:-132px -4px}.jstree-default-dark .jstree-closed>.jstree-ocl{background-position:-100px -4px}.jstree-default-dark .jstree-leaf>.jstree-ocl{background-position:-68px -4px}.jstree-default-dark .jstree-themeicon{background-position:-260px -4px}.jstree-default-dark>.jstree-no-dots .jstree-node,.jstree-default-dark>.jstree-no-dots .jstree-leaf>.jstree-ocl{background:transparent}.jstree-default-dark>.jstree-no-dots .jstree-open>.jstree-ocl{background-position:-36px -4px}.jstree-default-dark>.jstree-no-dots .jstree-closed>.jstree-ocl{background-position:-4px -4px}.jstree-default-dark .jstree-disabled{background:transparent}.jstree-default-dark .jstree-disabled.jstree-hovered{background:transparent}.jstree-default-dark .jstree-disabled.jstree-clicked{background:#efefef}.jstree-default-dark .jstree-checkbox{background-position:-164px -4px}.jstree-default-dark .jstree-checkbox:hover{background-position:-164px -36px}.jstree-default-dark.jstree-checkbox-selection .jstree-clicked>.jstree-checkbox,.jstree-default-dark .jstree-checked>.jstree-checkbox{background-position:-228px -4px}.jstree-default-dark.jstree-checkbox-selection .jstree-clicked>.jstree-checkbox:hover,.jstree-default-dark .jstree-checked>.jstree-checkbox:hover{background-position:-228px -36px}.jstree-default-dark .jstree-anchor>.jstree-undetermined{background-position:-196px -4px}.jstree-default-dark .jstree-anchor>.jstree-undetermined:hover{background-position:-196px -36px}.jstree-default-dark .jstree-checkbox-disabled{opacity:.8;filter:url("data:image/svg+xml;utf8,<svg xmlns=\'http://www.w3.org/2000/svg\'><filter id=\'jstree-grayscale\'><feColorMatrix type=\'matrix\' values=\'0.3333 0.3333 0.3333 0 0 0.3333 0.3333 0.3333 0 0 0.3333 0.3333 0.3333 0 0 0 0 0 1 0\'/></filter></svg>#jstree-grayscale");filter:gray;-webkit-filter:grayscale(100%)}.jstree-default-dark>.jstree-striped{background-size:auto 48px}.jstree-default-dark.jstree-rtl .jstree-node{background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAACAQMAAAB49I5GAAAABlBMVEUAAAAdHRvEkCwcAAAAAXRSTlMAQObYZgAAAAxJREFUCNdjAAMOBgAAGAAJMwQHdQAAAABJRU5ErkJggg==");background-position:100% 1px;background-repeat:repeat-y}.jstree-default-dark.jstree-rtl .jstree-last{background:transparent}.jstree-default-dark.jstree-rtl .jstree-open>.jstree-ocl{background-position:-132px -36px}.jstree-default-dark.jstree-rtl .jstree-closed>.jstree-ocl{background-position:-100px -36px}.jstree-default-dark.jstree-rtl .jstree-leaf>.jstree-ocl{background-position:-68px -36px}.jstree-default-dark.jstree-rtl>.jstree-no-dots .jstree-node,.jstree-default-dark.jstree-rtl>.jstree-no-dots .jstree-leaf>.jstree-ocl{background:transparent}.jstree-default-dark.jstree-rtl>.jstree-no-dots .jstree-open>.jstree-ocl{background-position:-36px -36px}.jstree-default-dark.jstree-rtl>.jstree-no-dots .jstree-closed>.jstree-ocl{background-position:-4px -36px}.jstree-default-dark .jstree-themeicon-custom{background-color:transparent;background-image:none;background-position:0 0}.jstree-default-dark>.jstree-container-ul .jstree-loading>.jstree-ocl{background:url("throbber.gif") center center no-repeat}.jstree-default-dark .jstree-file{background:url("32px.png") -100px -68px no-repeat}.jstree-default-dark .jstree-folder{background:url("32px.png") -260px -4px no-repeat}.jstree-default-dark>.jstree-container-ul>.jstree-node{margin-left:0;margin-right:0}#jstree-dnd.jstree-default-dark{line-height:24px;padding:0 4px}#jstree-dnd.jstree-default-dark .jstree-ok,#jstree-dnd.jstree-default-dark .jstree-er{background-image:url("32px.png");background-repeat:no-repeat;background-color:transparent}#jstree-dnd.jstree-default-dark i{background:transparent;width:24px;height:24px;line-height:24px}#jstree-dnd.jstree-default-dark .jstree-ok{background-position:-4px -68px}#jstree-dnd.jstree-default-dark .jstree-er{background-position:-36px -68px}.jstree-default-dark .jstree-ellipsis{overflow:hidden}.jstree-default-dark .jstree-ellipsis .jstree-anchor{width:calc(100% - 29px);text-overflow:ellipsis;overflow:hidden}.jstree-default-dark.jstree-rtl .jstree-node{background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAACAQMAAAB49I5GAAAABlBMVEUAAAAdHRvEkCwcAAAAAXRSTlMAQObYZgAAAAxJREFUCNdjAAMOBgAAGAAJMwQHdQAAAABJRU5ErkJggg==")}.jstree-default-dark.jstree-rtl .jstree-last{background:transparent}.jstree-default-dark-small .jstree-node{min-height:18px;line-height:18px;margin-left:18px;min-width:18px}.jstree-default-dark-small .jstree-anchor{line-height:18px;height:18px}.jstree-default-dark-small .jstree-icon{width:18px;height:18px;line-height:18px}.jstree-default-dark-small .jstree-icon:empty{width:18px;height:18px;line-height:18px}.jstree-default-dark-small.jstree-rtl .jstree-node{margin-right:18px}.jstree-default-dark-small .jstree-wholerow{height:18px}.jstree-default-dark-small .jstree-node,.jstree-default-dark-small .jstree-icon{background-image:url("32px.png")}.jstree-default-dark-small .jstree-node{background-position:-295px -7px;background-repeat:repeat-y}.jstree-default-dark-small .jstree-last{background:transparent}.jstree-default-dark-small .jstree-open>.jstree-ocl{background-position:-135px -7px}.jstree-default-dark-small .jstree-closed>.jstree-ocl{background-position:-103px -7px}.jstree-default-dark-small .jstree-leaf>.jstree-ocl{background-position:-71px -7px}.jstree-default-dark-small .jstree-themeicon{background-position:-263px -7px}.jstree-default-dark-small>.jstree-no-dots .jstree-node,.jstree-default-dark-small>.jstree-no-dots .jstree-leaf>.jstree-ocl{background:transparent}.jstree-default-dark-small>.jstree-no-dots .jstree-open>.jstree-ocl{background-position:-39px -7px}.jstree-default-dark-small>.jstree-no-dots .jstree-closed>.jstree-ocl{background-position:-7px -7px}.jstree-default-dark-small .jstree-disabled{background:transparent}.jstree-default-dark-small .jstree-disabled.jstree-hovered{background:transparent}.jstree-default-dark-small .jstree-disabled.jstree-clicked{background:#efefef}.jstree-default-dark-small .jstree-checkbox{background-position:-167px -7px}.jstree-default-dark-small .jstree-checkbox:hover{background-position:-167px -39px}.jstree-default-dark-small.jstree-checkbox-selection .jstree-clicked>.jstree-checkbox,.jstree-default-dark-small .jstree-checked>.jstree-checkbox{background-position:-231px -7px}.jstree-default-dark-small.jstree-checkbox-selection .jstree-clicked>.jstree-checkbox:hover,.jstree-default-dark-small .jstree-checked>.jstree-checkbox:hover{background-position:-231px -39px}.jstree-default-dark-small .jstree-anchor>.jstree-undetermined{background-position:-199px -7px}.jstree-default-dark-small .jstree-anchor>.jstree-undetermined:hover{background-position:-199px -39px}.jstree-default-dark-small .jstree-checkbox-disabled{opacity:.8;filter:url("data:image/svg+xml;utf8,<svg xmlns=\'http://www.w3.org/2000/svg\'><filter id=\'jstree-grayscale\'><feColorMatrix type=\'matrix\' values=\'0.3333 0.3333 0.3333 0 0 0.3333 0.3333 0.3333 0 0 0.3333 0.3333 0.3333 0 0 0 0 0 1 0\'/></filter></svg>#jstree-grayscale");filter:gray;-webkit-filter:grayscale(100%)}.jstree-default-dark-small>.jstree-striped{background-size:auto 36px}.jstree-default-dark-small.jstree-rtl .jstree-node{background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAACAQMAAAB49I5GAAAABlBMVEUAAAAdHRvEkCwcAAAAAXRSTlMAQObYZgAAAAxJREFUCNdjAAMOBgAAGAAJMwQHdQAAAABJRU5ErkJggg==");background-position:100% 1px;background-repeat:repeat-y}.jstree-default-dark-small.jstree-rtl .jstree-last{background:transparent}.jstree-default-dark-small.jstree-rtl .jstree-open>.jstree-ocl{background-position:-135px -39px}.jstree-default-dark-small.jstree-rtl .jstree-closed>.jstree-ocl{background-position:-103px -39px}.jstree-default-dark-small.jstree-rtl .jstree-leaf>.jstree-ocl{background-position:-71px -39px}.jstree-default-dark-small.jstree-rtl>.jstree-no-dots .jstree-node,.jstree-default-dark-small.jstree-rtl>.jstree-no-dots .jstree-leaf>.jstree-ocl{background:transparent}.jstree-default-dark-small.jstree-rtl>.jstree-no-dots .jstree-open>.jstree-ocl{background-position:-39px -39px}.jstree-default-dark-small.jstree-rtl>.jstree-no-dots .jstree-closed>.jstree-ocl{background-position:-7px -39px}.jstree-default-dark-small .jstree-themeicon-custom{background-color:transparent;background-image:none;background-position:0 0}.jstree-default-dark-small>.jstree-container-ul .jstree-loading>.jstree-ocl{background:url("throbber.gif") center center no-repeat}.jstree-default-dark-small .jstree-file{background:url("32px.png") -103px -71px no-repeat}.jstree-default-dark-small .jstree-folder{background:url("32px.png") -263px -7px no-repeat}.jstree-default-dark-small>.jstree-container-ul>.jstree-node{margin-left:0;margin-right:0}#jstree-dnd.jstree-default-dark-small{line-height:18px;padding:0 4px}#jstree-dnd.jstree-default-dark-small .jstree-ok,#jstree-dnd.jstree-default-dark-small .jstree-er{background-image:url("32px.png");background-repeat:no-repeat;background-color:transparent}#jstree-dnd.jstree-default-dark-small i{background:transparent;width:18px;height:18px;line-height:18px}#jstree-dnd.jstree-default-dark-small .jstree-ok{background-position:-7px -71px}#jstree-dnd.jstree-default-dark-small .jstree-er{background-position:-39px -71px}.jstree-default-dark-small .jstree-ellipsis{overflow:hidden}.jstree-default-dark-small .jstree-ellipsis .jstree-anchor{width:calc(100% - 23px);text-overflow:ellipsis;overflow:hidden}.jstree-default-dark-small.jstree-rtl .jstree-node{background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABIAAAACAQMAAABv1h6PAAAABlBMVEUAAAAdHRvEkCwcAAAAAXRSTlMAQObYZgAAAAxJREFUCNdjAAMHBgAAiABBI4gz9AAAAABJRU5ErkJggg==")}.jstree-default-dark-small.jstree-rtl .jstree-last{background:transparent}.jstree-default-dark-large .jstree-node{min-height:32px;line-height:32px;margin-left:32px;min-width:32px}.jstree-default-dark-large .jstree-anchor{line-height:32px;height:32px}.jstree-default-dark-large .jstree-icon{width:32px;height:32px;line-height:32px}.jstree-default-dark-large .jstree-icon:empty{width:32px;height:32px;line-height:32px}.jstree-default-dark-large.jstree-rtl .jstree-node{margin-right:32px}.jstree-default-dark-large .jstree-wholerow{height:32px}.jstree-default-dark-large .jstree-node,.jstree-default-dark-large .jstree-icon{background-image:url("32px.png")}.jstree-default-dark-large .jstree-node{background-position:-288px 0;background-repeat:repeat-y}.jstree-default-dark-large .jstree-last{background:transparent}.jstree-default-dark-large .jstree-open>.jstree-ocl{background-position:-128px 0}.jstree-default-dark-large .jstree-closed>.jstree-ocl{background-position:-96px 0}.jstree-default-dark-large .jstree-leaf>.jstree-ocl{background-position:-64px 0}.jstree-default-dark-large .jstree-themeicon{background-position:-256px 0}.jstree-default-dark-large>.jstree-no-dots .jstree-node,.jstree-default-dark-large>.jstree-no-dots .jstree-leaf>.jstree-ocl{background:transparent}.jstree-default-dark-large>.jstree-no-dots .jstree-open>.jstree-ocl{background-position:-32px 0}.jstree-default-dark-large>.jstree-no-dots .jstree-closed>.jstree-ocl{background-position:0 0}.jstree-default-dark-large .jstree-disabled{background:transparent}.jstree-default-dark-large .jstree-disabled.jstree-hovered{background:transparent}.jstree-default-dark-large .jstree-disabled.jstree-clicked{background:#efefef}.jstree-default-dark-large .jstree-checkbox{background-position:-160px 0}.jstree-default-dark-large .jstree-checkbox:hover{background-position:-160px -32px}.jstree-default-dark-large.jstree-checkbox-selection .jstree-clicked>.jstree-checkbox,.jstree-default-dark-large .jstree-checked>.jstree-checkbox{background-position:-224px 0}.jstree-default-dark-large.jstree-checkbox-selection .jstree-clicked>.jstree-checkbox:hover,.jstree-default-dark-large .jstree-checked>.jstree-checkbox:hover{background-position:-224px -32px}.jstree-default-dark-large .jstree-anchor>.jstree-undetermined{background-position:-192px 0}.jstree-default-dark-large .jstree-anchor>.jstree-undetermined:hover{background-position:-192px -32px}.jstree-default-dark-large .jstree-checkbox-disabled{opacity:.8;filter:url("data:image/svg+xml;utf8,<svg xmlns=\'http://www.w3.org/2000/svg\'><filter id=\'jstree-grayscale\'><feColorMatrix type=\'matrix\' values=\'0.3333 0.3333 0.3333 0 0 0.3333 0.3333 0.3333 0 0 0.3333 0.3333 0.3333 0 0 0 0 0 1 0\'/></filter></svg>#jstree-grayscale");filter:gray;-webkit-filter:grayscale(100%)}.jstree-default-dark-large>.jstree-striped{background-size:auto 64px}.jstree-default-dark-large.jstree-rtl .jstree-node{background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAACAQMAAAB49I5GAAAABlBMVEUAAAAdHRvEkCwcAAAAAXRSTlMAQObYZgAAAAxJREFUCNdjAAMOBgAAGAAJMwQHdQAAAABJRU5ErkJggg==");background-position:100% 1px;background-repeat:repeat-y}.jstree-default-dark-large.jstree-rtl .jstree-last{background:transparent}.jstree-default-dark-large.jstree-rtl .jstree-open>.jstree-ocl{background-position:-128px -32px}.jstree-default-dark-large.jstree-rtl .jstree-closed>.jstree-ocl{background-position:-96px -32px}.jstree-default-dark-large.jstree-rtl .jstree-leaf>.jstree-ocl{background-position:-64px -32px}.jstree-default-dark-large.jstree-rtl>.jstree-no-dots .jstree-node,.jstree-default-dark-large.jstree-rtl>.jstree-no-dots .jstree-leaf>.jstree-ocl{background:transparent}.jstree-default-dark-large.jstree-rtl>.jstree-no-dots .jstree-open>.jstree-ocl{background-position:-32px -32px}.jstree-default-dark-large.jstree-rtl>.jstree-no-dots .jstree-closed>.jstree-ocl{background-position:0 -32px}.jstree-default-dark-large .jstree-themeicon-custom{background-color:transparent;background-image:none;background-position:0 0}.jstree-default-dark-large>.jstree-container-ul .jstree-loading>.jstree-ocl{background:url("throbber.gif") center center no-repeat}.jstree-default-dark-large .jstree-file{background:url("32px.png") -96px -64px no-repeat}.jstree-default-dark-large .jstree-folder{background:url("32px.png") -256px 0 no-repeat}.jstree-default-dark-large>.jstree-container-ul>.jstree-node{margin-left:0;margin-right:0}#jstree-dnd.jstree-default-dark-large{line-height:32px;padding:0 4px}#jstree-dnd.jstree-default-dark-large .jstree-ok,#jstree-dnd.jstree-default-dark-large .jstree-er{background-image:url("32px.png");background-repeat:no-repeat;background-color:transparent}#jstree-dnd.jstree-default-dark-large i{background:transparent;width:32px;height:32px;line-height:32px}#jstree-dnd.jstree-default-dark-large .jstree-ok{background-position:0 -64px}#jstree-dnd.jstree-default-dark-large .jstree-er{background-position:-32px -64px}.jstree-default-dark-large .jstree-ellipsis{overflow:hidden}.jstree-default-dark-large .jstree-ellipsis .jstree-anchor{width:calc(100% - 37px);text-overflow:ellipsis;overflow:hidden}.jstree-default-dark-large.jstree-rtl .jstree-node{background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAACAQMAAAAD0EyKAAAABlBMVEUAAAAdHRvEkCwcAAAAAXRSTlMAQObYZgAAAAxJREFUCNdjgIIGBgABCgCBvVLXcAAAAABJRU5ErkJggg==")}.jstree-default-dark-large.jstree-rtl .jstree-last{background:transparent}@media (max-width:768px){#jstree-dnd.jstree-dnd-responsive{line-height:40px;font-weight:bold;font-size:1.1em;text-shadow:1px 1px white}#jstree-dnd.jstree-dnd-responsive>i{background:transparent;width:40px;height:40px}#jstree-dnd.jstree-dnd-responsive>.jstree-ok{background-image:url("40px.png");background-position:0 -200px;background-size:120px 240px}#jstree-dnd.jstree-dnd-responsive>.jstree-er{background-image:url("40px.png");background-position:-40px -200px;background-size:120px 240px}#jstree-marker.jstree-dnd-responsive{border-left-width:10px;border-top-width:10px;border-bottom-width:10px;margin-top:-10px}}@media (max-width:768px){.jstree-default-dark-responsive .jstree-icon{background-image:url("40px.png")}.jstree-default-dark-responsive .jstree-node,.jstree-default-dark-responsive .jstree-leaf>.jstree-ocl{background:transparent}.jstree-default-dark-responsive .jstree-node{min-height:40px;line-height:40px;margin-left:40px;min-width:40px;white-space:nowrap}.jstree-default-dark-responsive .jstree-anchor{line-height:40px;height:40px}.jstree-default-dark-responsive .jstree-icon,.jstree-default-dark-responsive .jstree-icon:empty{width:40px;height:40px;line-height:40px}.jstree-default-dark-responsive>.jstree-container-ul>.jstree-node{margin-left:0}.jstree-default-dark-responsive.jstree-rtl .jstree-node{margin-left:0;margin-right:40px;background:transparent}.jstree-default-dark-responsive.jstree-rtl .jstree-container-ul>.jstree-node{margin-right:0}.jstree-default-dark-responsive .jstree-ocl,.jstree-default-dark-responsive .jstree-themeicon,.jstree-default-dark-responsive .jstree-checkbox{background-size:120px 240px}.jstree-default-dark-responsive .jstree-leaf>.jstree-ocl,.jstree-default-dark-responsive.jstree-rtl .jstree-leaf>.jstree-ocl{background:transparent}.jstree-default-dark-responsive .jstree-open>.jstree-ocl{background-position:0 0 !important}.jstree-default-dark-responsive .jstree-closed>.jstree-ocl{background-position:0 -40px !important}.jstree-default-dark-responsive.jstree-rtl .jstree-closed>.jstree-ocl{background-position:-40px 0 !important}.jstree-default-dark-responsive .jstree-themeicon{background-position:-40px -40px}.jstree-default-dark-responsive .jstree-checkbox,.jstree-default-dark-responsive .jstree-checkbox:hover{background-position:-40px -80px}.jstree-default-dark-responsive.jstree-checkbox-selection .jstree-clicked>.jstree-checkbox,.jstree-default-dark-responsive.jstree-checkbox-selection .jstree-clicked>.jstree-checkbox:hover,.jstree-default-dark-responsive .jstree-checked>.jstree-checkbox,.jstree-default-dark-responsive .jstree-checked>.jstree-checkbox:hover{background-position:0 -80px}.jstree-default-dark-responsive .jstree-anchor>.jstree-undetermined,.jstree-default-dark-responsive .jstree-anchor>.jstree-undetermined:hover{background-position:0 -120px}.jstree-default-dark-responsive .jstree-anchor{font-weight:bold;font-size:1.1em;text-shadow:1px 1px white}.jstree-default-dark-responsive>.jstree-striped{background:transparent}.jstree-default-dark-responsive .jstree-wholerow{border-top:1px solid #666;border-bottom:1px solid #000;background:#333333;height:40px}.jstree-default-dark-responsive .jstree-wholerow-hovered{background:#555}.jstree-default-dark-responsive .jstree-wholerow-clicked{background:#5fa2db}.jstree-default-dark-responsive .jstree-children .jstree-last>.jstree-wholerow{box-shadow:inset 0 -6px 3px -5px #111111}.jstree-default-dark-responsive .jstree-children .jstree-open>.jstree-wholerow{box-shadow:inset 0 6px 3px -5px #111111;border-top:0}.jstree-default-dark-responsive .jstree-children .jstree-open+.jstree-open{box-shadow:none}.jstree-default-dark-responsive .jstree-node,.jstree-default-dark-responsive .jstree-icon,.jstree-default-dark-responsive .jstree-node>.jstree-ocl,.jstree-default-dark-responsive .jstree-themeicon,.jstree-default-dark-responsive .jstree-checkbox{background-image:url("40px.png");background-size:120px 240px}.jstree-default-dark-responsive .jstree-node{background-position:-80px 0;background-repeat:repeat-y}.jstree-default-dark-responsive .jstree-last{background:transparent}.jstree-default-dark-responsive .jstree-leaf>.jstree-ocl{background-position:-40px -120px}.jstree-default-dark-responsive .jstree-last>.jstree-ocl{background-position:-40px -160px}.jstree-default-dark-responsive .jstree-themeicon-custom{background-color:transparent;background-image:none;background-position:0 0}.jstree-default-dark-responsive .jstree-file{background:url("40px.png") 0 -160px no-repeat;background-size:120px 240px}.jstree-default-dark-responsive .jstree-folder{background:url("40px.png") -40px -40px no-repeat;background-size:120px 240px}.jstree-default-dark-responsive>.jstree-container-ul>.jstree-node{margin-left:0;margin-right:0}}.jstree-default-dark{background:#333}.jstree-default-dark .jstree-anchor{color:#999;text-shadow:1px 1px 0 rgba(0,0,0,0.5)}.jstree-default-dark .jstree-clicked,.jstree-default-dark .jstree-checked{color:white}.jstree-default-dark .jstree-hovered{color:white}#jstree-marker.jstree-default-dark{border-left-color:#999;background:transparent}.jstree-default-dark .jstree-anchor>.jstree-icon{opacity:.75}.jstree-default-dark .jstree-clicked>.jstree-icon,.jstree-default-dark .jstree-hovered>.jstree-icon,.jstree-default-dark .jstree-checked>.jstree-icon{opacity:1}.jstree-default-dark.jstree-rtl .jstree-node{background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAACAQMAAAB49I5GAAAABlBMVEUAAACZmZl+9SADAAAAAXRSTlMAQObYZgAAAAxJREFUCNdjAAMOBgAAGAAJMwQHdQAAAABJRU5ErkJggg==")}.jstree-default-dark.jstree-rtl .jstree-last{background:transparent}.jstree-default-dark-small.jstree-rtl .jstree-node{background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABIAAAACAQMAAABv1h6PAAAABlBMVEUAAACZmZl+9SADAAAAAXRSTlMAQObYZgAAAAxJREFUCNdjAAMHBgAAiABBI4gz9AAAAABJRU5ErkJggg==")}.jstree-default-dark-small.jstree-rtl .jstree-last{background:transparent}.jstree-default-dark-large.jstree-rtl .jstree-node{background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAACAQMAAAAD0EyKAAAABlBMVEUAAACZmZl+9SADAAAAAXRSTlMAQObYZgAAAAxJREFUCNdjgIIGBgABCgCBvVLXcAAAAABJRU5ErkJggg==")}.jstree-default-dark-large.jstree-rtl .jstree-last{background:transparent}admin/js/jstree/dist/themes/default-dark/style.css000064400000103107151327705670016230 0ustar00/* jsTree default dark theme */
.jstree-node,
.jstree-children,
.jstree-container-ul {
  display: block;
  margin: 0;
  padding: 0;
  list-style-type: none;
  list-style-image: none;
}
.jstree-node {
  white-space: nowrap;
}
.jstree-anchor {
  display: inline-block;
  color: black;
  white-space: nowrap;
  padding: 0 4px 0 1px;
  margin: 0;
  vertical-align: top;
}
.jstree-anchor:focus {
  outline: 0;
}
.jstree-anchor,
.jstree-anchor:link,
.jstree-anchor:visited,
.jstree-anchor:hover,
.jstree-anchor:active {
  text-decoration: none;
  color: inherit;
}
.jstree-icon {
  display: inline-block;
  text-decoration: none;
  margin: 0;
  padding: 0;
  vertical-align: top;
  text-align: center;
}
.jstree-icon:empty {
  display: inline-block;
  text-decoration: none;
  margin: 0;
  padding: 0;
  vertical-align: top;
  text-align: center;
}
.jstree-ocl {
  cursor: pointer;
}
.jstree-leaf > .jstree-ocl {
  cursor: default;
}
.jstree .jstree-open > .jstree-children {
  display: block;
}
.jstree .jstree-closed > .jstree-children,
.jstree .jstree-leaf > .jstree-children {
  display: none;
}
.jstree-anchor > .jstree-themeicon {
  margin-right: 2px;
}
.jstree-no-icons .jstree-themeicon,
.jstree-anchor > .jstree-themeicon-hidden {
  display: none;
}
.jstree-hidden,
.jstree-node.jstree-hidden {
  display: none;
}
.jstree-rtl .jstree-anchor {
  padding: 0 1px 0 4px;
}
.jstree-rtl .jstree-anchor > .jstree-themeicon {
  margin-left: 2px;
  margin-right: 0;
}
.jstree-rtl .jstree-node {
  margin-left: 0;
}
.jstree-rtl .jstree-container-ul > .jstree-node {
  margin-right: 0;
}
.jstree-wholerow-ul {
  position: relative;
  display: inline-block;
  min-width: 100%;
}
.jstree-wholerow-ul .jstree-leaf > .jstree-ocl {
  cursor: pointer;
}
.jstree-wholerow-ul .jstree-anchor,
.jstree-wholerow-ul .jstree-icon {
  position: relative;
}
.jstree-wholerow-ul .jstree-wholerow {
  width: 100%;
  cursor: pointer;
  position: absolute;
  left: 0;
  -webkit-user-select: none;
  -moz-user-select: none;
  -ms-user-select: none;
  user-select: none;
}
.jstree-contextmenu .jstree-anchor {
  -webkit-user-select: none;
  /* disable selection/Copy of UIWebView */
  -webkit-touch-callout: none;
  /* disable the IOS popup when long-press on a link */
}
.vakata-context {
  display: none;
}
.vakata-context,
.vakata-context ul {
  margin: 0;
  padding: 2px;
  position: absolute;
  background: #f5f5f5;
  border: 1px solid #979797;
  box-shadow: 2px 2px 2px #999999;
}
.vakata-context ul {
  list-style: none;
  left: 100%;
  margin-top: -2.7em;
  margin-left: -4px;
}
.vakata-context .vakata-context-right ul {
  left: auto;
  right: 100%;
  margin-left: auto;
  margin-right: -4px;
}
.vakata-context li {
  list-style: none;
}
.vakata-context li > a {
  display: block;
  padding: 0 2em 0 2em;
  text-decoration: none;
  width: auto;
  color: black;
  white-space: nowrap;
  line-height: 2.4em;
  text-shadow: 1px 1px 0 white;
  border-radius: 1px;
}
.vakata-context li > a:hover {
  position: relative;
  background-color: #e8eff7;
  box-shadow: 0 0 2px #0a6aa1;
}
.vakata-context li > a.vakata-context-parent {
  background-image: url("data:image/gif;base64,R0lGODlhCwAHAIAAACgoKP///yH5BAEAAAEALAAAAAALAAcAAAIORI4JlrqN1oMSnmmZDQUAOw==");
  background-position: right center;
  background-repeat: no-repeat;
}
.vakata-context li > a:focus {
  outline: 0;
}
.vakata-context .vakata-context-hover > a {
  position: relative;
  background-color: #e8eff7;
  box-shadow: 0 0 2px #0a6aa1;
}
.vakata-context .vakata-context-separator > a,
.vakata-context .vakata-context-separator > a:hover {
  background: white;
  border: 0;
  border-top: 1px solid #e2e3e3;
  height: 1px;
  min-height: 1px;
  max-height: 1px;
  padding: 0;
  margin: 0 0 0 2.4em;
  border-left: 1px solid #e0e0e0;
  text-shadow: 0 0 0 transparent;
  box-shadow: 0 0 0 transparent;
  border-radius: 0;
}
.vakata-context .vakata-contextmenu-disabled a,
.vakata-context .vakata-contextmenu-disabled a:hover {
  color: silver;
  background-color: transparent;
  border: 0;
  box-shadow: 0 0 0;
}
.vakata-context .vakata-contextmenu-disabled > a > i {
  filter: grayscale(100%);
}
.vakata-context li > a > i {
  text-decoration: none;
  display: inline-block;
  width: 2.4em;
  height: 2.4em;
  background: transparent;
  margin: 0 0 0 -2em;
  vertical-align: top;
  text-align: center;
  line-height: 2.4em;
}
.vakata-context li > a > i:empty {
  width: 2.4em;
  line-height: 2.4em;
}
.vakata-context li > a .vakata-contextmenu-sep {
  display: inline-block;
  width: 1px;
  height: 2.4em;
  background: white;
  margin: 0 0.5em 0 0;
  border-left: 1px solid #e2e3e3;
}
.vakata-context .vakata-contextmenu-shortcut {
  font-size: 0.8em;
  color: silver;
  opacity: 0.5;
  display: none;
}
.vakata-context-rtl ul {
  left: auto;
  right: 100%;
  margin-left: auto;
  margin-right: -4px;
}
.vakata-context-rtl li > a.vakata-context-parent {
  background-image: url("data:image/gif;base64,R0lGODlhCwAHAIAAACgoKP///yH5BAEAAAEALAAAAAALAAcAAAINjI+AC7rWHIsPtmoxLAA7");
  background-position: left center;
  background-repeat: no-repeat;
}
.vakata-context-rtl .vakata-context-separator > a {
  margin: 0 2.4em 0 0;
  border-left: 0;
  border-right: 1px solid #e2e3e3;
}
.vakata-context-rtl .vakata-context-left ul {
  right: auto;
  left: 100%;
  margin-left: -4px;
  margin-right: auto;
}
.vakata-context-rtl li > a > i {
  margin: 0 -2em 0 0;
}
.vakata-context-rtl li > a .vakata-contextmenu-sep {
  margin: 0 0 0 0.5em;
  border-left-color: white;
  background: #e2e3e3;
}
#jstree-marker {
  position: absolute;
  top: 0;
  left: 0;
  margin: -5px 0 0 0;
  padding: 0;
  border-right: 0;
  border-top: 5px solid transparent;
  border-bottom: 5px solid transparent;
  border-left: 5px solid;
  width: 0;
  height: 0;
  font-size: 0;
  line-height: 0;
}
#jstree-dnd {
  line-height: 16px;
  margin: 0;
  padding: 4px;
}
#jstree-dnd .jstree-icon,
#jstree-dnd .jstree-copy {
  display: inline-block;
  text-decoration: none;
  margin: 0 2px 0 0;
  padding: 0;
  width: 16px;
  height: 16px;
}
#jstree-dnd .jstree-ok {
  background: green;
}
#jstree-dnd .jstree-er {
  background: red;
}
#jstree-dnd .jstree-copy {
  margin: 0 2px 0 2px;
}
.jstree-default-dark .jstree-node,
.jstree-default-dark .jstree-icon {
  background-repeat: no-repeat;
  background-color: transparent;
}
.jstree-default-dark .jstree-anchor,
.jstree-default-dark .jstree-animated,
.jstree-default-dark .jstree-wholerow {
  transition: background-color 0.15s, box-shadow 0.15s;
}
.jstree-default-dark .jstree-hovered {
  background: #555;
  border-radius: 2px;
  box-shadow: inset 0 0 1px #555;
}
.jstree-default-dark .jstree-context {
  background: #555;
  border-radius: 2px;
  box-shadow: inset 0 0 1px #555;
}
.jstree-default-dark .jstree-clicked {
  background: #5fa2db;
  border-radius: 2px;
  box-shadow: inset 0 0 1px #666666;
}
.jstree-default-dark .jstree-no-icons .jstree-anchor > .jstree-themeicon {
  display: none;
}
.jstree-default-dark .jstree-disabled {
  background: transparent;
  color: #666666;
}
.jstree-default-dark .jstree-disabled.jstree-hovered {
  background: transparent;
  box-shadow: none;
}
.jstree-default-dark .jstree-disabled.jstree-clicked {
  background: #333333;
}
.jstree-default-dark .jstree-disabled > .jstree-icon {
  opacity: 0.8;
  filter: url("data:image/svg+xml;utf8,<svg xmlns=\'http://www.w3.org/2000/svg\'><filter id=\'jstree-grayscale\'><feColorMatrix type=\'matrix\' values=\'0.3333 0.3333 0.3333 0 0 0.3333 0.3333 0.3333 0 0 0.3333 0.3333 0.3333 0 0 0 0 0 1 0\'/></filter></svg>#jstree-grayscale");
  /* Firefox 10+ */
  filter: gray;
  /* IE6-9 */
  -webkit-filter: grayscale(100%);
  /* Chrome 19+ & Safari 6+ */
}
.jstree-default-dark .jstree-search {
  font-style: italic;
  color: #ffffff;
  font-weight: bold;
}
.jstree-default-dark .jstree-no-checkboxes .jstree-checkbox {
  display: none !important;
}
.jstree-default-dark.jstree-checkbox-no-clicked .jstree-clicked {
  background: transparent;
  box-shadow: none;
}
.jstree-default-dark.jstree-checkbox-no-clicked .jstree-clicked.jstree-hovered {
  background: #555;
}
.jstree-default-dark.jstree-checkbox-no-clicked > .jstree-wholerow-ul .jstree-wholerow-clicked {
  background: transparent;
}
.jstree-default-dark.jstree-checkbox-no-clicked > .jstree-wholerow-ul .jstree-wholerow-clicked.jstree-wholerow-hovered {
  background: #555;
}
.jstree-default-dark > .jstree-striped {
  min-width: 100%;
  display: inline-block;
  background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAAkCAMAAAB/qqA+AAAABlBMVEUAAAAAAAClZ7nPAAAAAnRSTlMNAMM9s3UAAAAXSURBVHjajcEBAQAAAIKg/H/aCQZ70AUBjAATb6YPDgAAAABJRU5ErkJggg==") left top repeat;
}
.jstree-default-dark > .jstree-wholerow-ul .jstree-hovered,
.jstree-default-dark > .jstree-wholerow-ul .jstree-clicked {
  background: transparent;
  box-shadow: none;
  border-radius: 0;
}
.jstree-default-dark .jstree-wholerow {
  -moz-box-sizing: border-box;
  -webkit-box-sizing: border-box;
  box-sizing: border-box;
}
.jstree-default-dark .jstree-wholerow-hovered {
  background: #555;
}
.jstree-default-dark .jstree-wholerow-clicked {
  background: #5fa2db;
  background: -webkit-linear-gradient(top, #5fa2db 0%, #5fa2db 100%);
  background: linear-gradient(to bottom, #5fa2db 0%, #5fa2db 100%);
}
.jstree-default-dark .jstree-node {
  min-height: 24px;
  line-height: 24px;
  margin-left: 24px;
  min-width: 24px;
}
.jstree-default-dark .jstree-anchor {
  line-height: 24px;
  height: 24px;
}
.jstree-default-dark .jstree-icon {
  width: 24px;
  height: 24px;
  line-height: 24px;
}
.jstree-default-dark .jstree-icon:empty {
  width: 24px;
  height: 24px;
  line-height: 24px;
}
.jstree-default-dark.jstree-rtl .jstree-node {
  margin-right: 24px;
}
.jstree-default-dark .jstree-wholerow {
  height: 24px;
}
.jstree-default-dark .jstree-node,
.jstree-default-dark .jstree-icon {
  background-image: url("32px.png");
}
.jstree-default-dark .jstree-node {
  background-position: -292px -4px;
  background-repeat: repeat-y;
}
.jstree-default-dark .jstree-last {
  background: transparent;
}
.jstree-default-dark .jstree-open > .jstree-ocl {
  background-position: -132px -4px;
}
.jstree-default-dark .jstree-closed > .jstree-ocl {
  background-position: -100px -4px;
}
.jstree-default-dark .jstree-leaf > .jstree-ocl {
  background-position: -68px -4px;
}
.jstree-default-dark .jstree-themeicon {
  background-position: -260px -4px;
}
.jstree-default-dark > .jstree-no-dots .jstree-node,
.jstree-default-dark > .jstree-no-dots .jstree-leaf > .jstree-ocl {
  background: transparent;
}
.jstree-default-dark > .jstree-no-dots .jstree-open > .jstree-ocl {
  background-position: -36px -4px;
}
.jstree-default-dark > .jstree-no-dots .jstree-closed > .jstree-ocl {
  background-position: -4px -4px;
}
.jstree-default-dark .jstree-disabled {
  background: transparent;
}
.jstree-default-dark .jstree-disabled.jstree-hovered {
  background: transparent;
}
.jstree-default-dark .jstree-disabled.jstree-clicked {
  background: #efefef;
}
.jstree-default-dark .jstree-checkbox {
  background-position: -164px -4px;
}
.jstree-default-dark .jstree-checkbox:hover {
  background-position: -164px -36px;
}
.jstree-default-dark.jstree-checkbox-selection .jstree-clicked > .jstree-checkbox,
.jstree-default-dark .jstree-checked > .jstree-checkbox {
  background-position: -228px -4px;
}
.jstree-default-dark.jstree-checkbox-selection .jstree-clicked > .jstree-checkbox:hover,
.jstree-default-dark .jstree-checked > .jstree-checkbox:hover {
  background-position: -228px -36px;
}
.jstree-default-dark .jstree-anchor > .jstree-undetermined {
  background-position: -196px -4px;
}
.jstree-default-dark .jstree-anchor > .jstree-undetermined:hover {
  background-position: -196px -36px;
}
.jstree-default-dark .jstree-checkbox-disabled {
  opacity: 0.8;
  filter: url("data:image/svg+xml;utf8,<svg xmlns=\'http://www.w3.org/2000/svg\'><filter id=\'jstree-grayscale\'><feColorMatrix type=\'matrix\' values=\'0.3333 0.3333 0.3333 0 0 0.3333 0.3333 0.3333 0 0 0.3333 0.3333 0.3333 0 0 0 0 0 1 0\'/></filter></svg>#jstree-grayscale");
  /* Firefox 10+ */
  filter: gray;
  /* IE6-9 */
  -webkit-filter: grayscale(100%);
  /* Chrome 19+ & Safari 6+ */
}
.jstree-default-dark > .jstree-striped {
  background-size: auto 48px;
}
.jstree-default-dark.jstree-rtl .jstree-node {
  background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAACAQMAAAB49I5GAAAABlBMVEUAAAAdHRvEkCwcAAAAAXRSTlMAQObYZgAAAAxJREFUCNdjAAMOBgAAGAAJMwQHdQAAAABJRU5ErkJggg==");
  background-position: 100% 1px;
  background-repeat: repeat-y;
}
.jstree-default-dark.jstree-rtl .jstree-last {
  background: transparent;
}
.jstree-default-dark.jstree-rtl .jstree-open > .jstree-ocl {
  background-position: -132px -36px;
}
.jstree-default-dark.jstree-rtl .jstree-closed > .jstree-ocl {
  background-position: -100px -36px;
}
.jstree-default-dark.jstree-rtl .jstree-leaf > .jstree-ocl {
  background-position: -68px -36px;
}
.jstree-default-dark.jstree-rtl > .jstree-no-dots .jstree-node,
.jstree-default-dark.jstree-rtl > .jstree-no-dots .jstree-leaf > .jstree-ocl {
  background: transparent;
}
.jstree-default-dark.jstree-rtl > .jstree-no-dots .jstree-open > .jstree-ocl {
  background-position: -36px -36px;
}
.jstree-default-dark.jstree-rtl > .jstree-no-dots .jstree-closed > .jstree-ocl {
  background-position: -4px -36px;
}
.jstree-default-dark .jstree-themeicon-custom {
  background-color: transparent;
  background-image: none;
  background-position: 0 0;
}
.jstree-default-dark > .jstree-container-ul .jstree-loading > .jstree-ocl {
  background: url("throbber.gif") center center no-repeat;
}
.jstree-default-dark .jstree-file {
  background: url("32px.png") -100px -68px no-repeat;
}
.jstree-default-dark .jstree-folder {
  background: url("32px.png") -260px -4px no-repeat;
}
.jstree-default-dark > .jstree-container-ul > .jstree-node {
  margin-left: 0;
  margin-right: 0;
}
#jstree-dnd.jstree-default-dark {
  line-height: 24px;
  padding: 0 4px;
}
#jstree-dnd.jstree-default-dark .jstree-ok,
#jstree-dnd.jstree-default-dark .jstree-er {
  background-image: url("32px.png");
  background-repeat: no-repeat;
  background-color: transparent;
}
#jstree-dnd.jstree-default-dark i {
  background: transparent;
  width: 24px;
  height: 24px;
  line-height: 24px;
}
#jstree-dnd.jstree-default-dark .jstree-ok {
  background-position: -4px -68px;
}
#jstree-dnd.jstree-default-dark .jstree-er {
  background-position: -36px -68px;
}
.jstree-default-dark .jstree-ellipsis {
  overflow: hidden;
}
.jstree-default-dark .jstree-ellipsis .jstree-anchor {
  width: calc(100% - 29px);
  text-overflow: ellipsis;
  overflow: hidden;
}
.jstree-default-dark.jstree-rtl .jstree-node {
  background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAACAQMAAAB49I5GAAAABlBMVEUAAAAdHRvEkCwcAAAAAXRSTlMAQObYZgAAAAxJREFUCNdjAAMOBgAAGAAJMwQHdQAAAABJRU5ErkJggg==");
}
.jstree-default-dark.jstree-rtl .jstree-last {
  background: transparent;
}
.jstree-default-dark-small .jstree-node {
  min-height: 18px;
  line-height: 18px;
  margin-left: 18px;
  min-width: 18px;
}
.jstree-default-dark-small .jstree-anchor {
  line-height: 18px;
  height: 18px;
}
.jstree-default-dark-small .jstree-icon {
  width: 18px;
  height: 18px;
  line-height: 18px;
}
.jstree-default-dark-small .jstree-icon:empty {
  width: 18px;
  height: 18px;
  line-height: 18px;
}
.jstree-default-dark-small.jstree-rtl .jstree-node {
  margin-right: 18px;
}
.jstree-default-dark-small .jstree-wholerow {
  height: 18px;
}
.jstree-default-dark-small .jstree-node,
.jstree-default-dark-small .jstree-icon {
  background-image: url("32px.png");
}
.jstree-default-dark-small .jstree-node {
  background-position: -295px -7px;
  background-repeat: repeat-y;
}
.jstree-default-dark-small .jstree-last {
  background: transparent;
}
.jstree-default-dark-small .jstree-open > .jstree-ocl {
  background-position: -135px -7px;
}
.jstree-default-dark-small .jstree-closed > .jstree-ocl {
  background-position: -103px -7px;
}
.jstree-default-dark-small .jstree-leaf > .jstree-ocl {
  background-position: -71px -7px;
}
.jstree-default-dark-small .jstree-themeicon {
  background-position: -263px -7px;
}
.jstree-default-dark-small > .jstree-no-dots .jstree-node,
.jstree-default-dark-small > .jstree-no-dots .jstree-leaf > .jstree-ocl {
  background: transparent;
}
.jstree-default-dark-small > .jstree-no-dots .jstree-open > .jstree-ocl {
  background-position: -39px -7px;
}
.jstree-default-dark-small > .jstree-no-dots .jstree-closed > .jstree-ocl {
  background-position: -7px -7px;
}
.jstree-default-dark-small .jstree-disabled {
  background: transparent;
}
.jstree-default-dark-small .jstree-disabled.jstree-hovered {
  background: transparent;
}
.jstree-default-dark-small .jstree-disabled.jstree-clicked {
  background: #efefef;
}
.jstree-default-dark-small .jstree-checkbox {
  background-position: -167px -7px;
}
.jstree-default-dark-small .jstree-checkbox:hover {
  background-position: -167px -39px;
}
.jstree-default-dark-small.jstree-checkbox-selection .jstree-clicked > .jstree-checkbox,
.jstree-default-dark-small .jstree-checked > .jstree-checkbox {
  background-position: -231px -7px;
}
.jstree-default-dark-small.jstree-checkbox-selection .jstree-clicked > .jstree-checkbox:hover,
.jstree-default-dark-small .jstree-checked > .jstree-checkbox:hover {
  background-position: -231px -39px;
}
.jstree-default-dark-small .jstree-anchor > .jstree-undetermined {
  background-position: -199px -7px;
}
.jstree-default-dark-small .jstree-anchor > .jstree-undetermined:hover {
  background-position: -199px -39px;
}
.jstree-default-dark-small .jstree-checkbox-disabled {
  opacity: 0.8;
  filter: url("data:image/svg+xml;utf8,<svg xmlns=\'http://www.w3.org/2000/svg\'><filter id=\'jstree-grayscale\'><feColorMatrix type=\'matrix\' values=\'0.3333 0.3333 0.3333 0 0 0.3333 0.3333 0.3333 0 0 0.3333 0.3333 0.3333 0 0 0 0 0 1 0\'/></filter></svg>#jstree-grayscale");
  /* Firefox 10+ */
  filter: gray;
  /* IE6-9 */
  -webkit-filter: grayscale(100%);
  /* Chrome 19+ & Safari 6+ */
}
.jstree-default-dark-small > .jstree-striped {
  background-size: auto 36px;
}
.jstree-default-dark-small.jstree-rtl .jstree-node {
  background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAACAQMAAAB49I5GAAAABlBMVEUAAAAdHRvEkCwcAAAAAXRSTlMAQObYZgAAAAxJREFUCNdjAAMOBgAAGAAJMwQHdQAAAABJRU5ErkJggg==");
  background-position: 100% 1px;
  background-repeat: repeat-y;
}
.jstree-default-dark-small.jstree-rtl .jstree-last {
  background: transparent;
}
.jstree-default-dark-small.jstree-rtl .jstree-open > .jstree-ocl {
  background-position: -135px -39px;
}
.jstree-default-dark-small.jstree-rtl .jstree-closed > .jstree-ocl {
  background-position: -103px -39px;
}
.jstree-default-dark-small.jstree-rtl .jstree-leaf > .jstree-ocl {
  background-position: -71px -39px;
}
.jstree-default-dark-small.jstree-rtl > .jstree-no-dots .jstree-node,
.jstree-default-dark-small.jstree-rtl > .jstree-no-dots .jstree-leaf > .jstree-ocl {
  background: transparent;
}
.jstree-default-dark-small.jstree-rtl > .jstree-no-dots .jstree-open > .jstree-ocl {
  background-position: -39px -39px;
}
.jstree-default-dark-small.jstree-rtl > .jstree-no-dots .jstree-closed > .jstree-ocl {
  background-position: -7px -39px;
}
.jstree-default-dark-small .jstree-themeicon-custom {
  background-color: transparent;
  background-image: none;
  background-position: 0 0;
}
.jstree-default-dark-small > .jstree-container-ul .jstree-loading > .jstree-ocl {
  background: url("throbber.gif") center center no-repeat;
}
.jstree-default-dark-small .jstree-file {
  background: url("32px.png") -103px -71px no-repeat;
}
.jstree-default-dark-small .jstree-folder {
  background: url("32px.png") -263px -7px no-repeat;
}
.jstree-default-dark-small > .jstree-container-ul > .jstree-node {
  margin-left: 0;
  margin-right: 0;
}
#jstree-dnd.jstree-default-dark-small {
  line-height: 18px;
  padding: 0 4px;
}
#jstree-dnd.jstree-default-dark-small .jstree-ok,
#jstree-dnd.jstree-default-dark-small .jstree-er {
  background-image: url("32px.png");
  background-repeat: no-repeat;
  background-color: transparent;
}
#jstree-dnd.jstree-default-dark-small i {
  background: transparent;
  width: 18px;
  height: 18px;
  line-height: 18px;
}
#jstree-dnd.jstree-default-dark-small .jstree-ok {
  background-position: -7px -71px;
}
#jstree-dnd.jstree-default-dark-small .jstree-er {
  background-position: -39px -71px;
}
.jstree-default-dark-small .jstree-ellipsis {
  overflow: hidden;
}
.jstree-default-dark-small .jstree-ellipsis .jstree-anchor {
  width: calc(100% - 23px);
  text-overflow: ellipsis;
  overflow: hidden;
}
.jstree-default-dark-small.jstree-rtl .jstree-node {
  background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABIAAAACAQMAAABv1h6PAAAABlBMVEUAAAAdHRvEkCwcAAAAAXRSTlMAQObYZgAAAAxJREFUCNdjAAMHBgAAiABBI4gz9AAAAABJRU5ErkJggg==");
}
.jstree-default-dark-small.jstree-rtl .jstree-last {
  background: transparent;
}
.jstree-default-dark-large .jstree-node {
  min-height: 32px;
  line-height: 32px;
  margin-left: 32px;
  min-width: 32px;
}
.jstree-default-dark-large .jstree-anchor {
  line-height: 32px;
  height: 32px;
}
.jstree-default-dark-large .jstree-icon {
  width: 32px;
  height: 32px;
  line-height: 32px;
}
.jstree-default-dark-large .jstree-icon:empty {
  width: 32px;
  height: 32px;
  line-height: 32px;
}
.jstree-default-dark-large.jstree-rtl .jstree-node {
  margin-right: 32px;
}
.jstree-default-dark-large .jstree-wholerow {
  height: 32px;
}
.jstree-default-dark-large .jstree-node,
.jstree-default-dark-large .jstree-icon {
  background-image: url("32px.png");
}
.jstree-default-dark-large .jstree-node {
  background-position: -288px 0px;
  background-repeat: repeat-y;
}
.jstree-default-dark-large .jstree-last {
  background: transparent;
}
.jstree-default-dark-large .jstree-open > .jstree-ocl {
  background-position: -128px 0px;
}
.jstree-default-dark-large .jstree-closed > .jstree-ocl {
  background-position: -96px 0px;
}
.jstree-default-dark-large .jstree-leaf > .jstree-ocl {
  background-position: -64px 0px;
}
.jstree-default-dark-large .jstree-themeicon {
  background-position: -256px 0px;
}
.jstree-default-dark-large > .jstree-no-dots .jstree-node,
.jstree-default-dark-large > .jstree-no-dots .jstree-leaf > .jstree-ocl {
  background: transparent;
}
.jstree-default-dark-large > .jstree-no-dots .jstree-open > .jstree-ocl {
  background-position: -32px 0px;
}
.jstree-default-dark-large > .jstree-no-dots .jstree-closed > .jstree-ocl {
  background-position: 0px 0px;
}
.jstree-default-dark-large .jstree-disabled {
  background: transparent;
}
.jstree-default-dark-large .jstree-disabled.jstree-hovered {
  background: transparent;
}
.jstree-default-dark-large .jstree-disabled.jstree-clicked {
  background: #efefef;
}
.jstree-default-dark-large .jstree-checkbox {
  background-position: -160px 0px;
}
.jstree-default-dark-large .jstree-checkbox:hover {
  background-position: -160px -32px;
}
.jstree-default-dark-large.jstree-checkbox-selection .jstree-clicked > .jstree-checkbox,
.jstree-default-dark-large .jstree-checked > .jstree-checkbox {
  background-position: -224px 0px;
}
.jstree-default-dark-large.jstree-checkbox-selection .jstree-clicked > .jstree-checkbox:hover,
.jstree-default-dark-large .jstree-checked > .jstree-checkbox:hover {
  background-position: -224px -32px;
}
.jstree-default-dark-large .jstree-anchor > .jstree-undetermined {
  background-position: -192px 0px;
}
.jstree-default-dark-large .jstree-anchor > .jstree-undetermined:hover {
  background-position: -192px -32px;
}
.jstree-default-dark-large .jstree-checkbox-disabled {
  opacity: 0.8;
  filter: url("data:image/svg+xml;utf8,<svg xmlns=\'http://www.w3.org/2000/svg\'><filter id=\'jstree-grayscale\'><feColorMatrix type=\'matrix\' values=\'0.3333 0.3333 0.3333 0 0 0.3333 0.3333 0.3333 0 0 0.3333 0.3333 0.3333 0 0 0 0 0 1 0\'/></filter></svg>#jstree-grayscale");
  /* Firefox 10+ */
  filter: gray;
  /* IE6-9 */
  -webkit-filter: grayscale(100%);
  /* Chrome 19+ & Safari 6+ */
}
.jstree-default-dark-large > .jstree-striped {
  background-size: auto 64px;
}
.jstree-default-dark-large.jstree-rtl .jstree-node {
  background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAACAQMAAAB49I5GAAAABlBMVEUAAAAdHRvEkCwcAAAAAXRSTlMAQObYZgAAAAxJREFUCNdjAAMOBgAAGAAJMwQHdQAAAABJRU5ErkJggg==");
  background-position: 100% 1px;
  background-repeat: repeat-y;
}
.jstree-default-dark-large.jstree-rtl .jstree-last {
  background: transparent;
}
.jstree-default-dark-large.jstree-rtl .jstree-open > .jstree-ocl {
  background-position: -128px -32px;
}
.jstree-default-dark-large.jstree-rtl .jstree-closed > .jstree-ocl {
  background-position: -96px -32px;
}
.jstree-default-dark-large.jstree-rtl .jstree-leaf > .jstree-ocl {
  background-position: -64px -32px;
}
.jstree-default-dark-large.jstree-rtl > .jstree-no-dots .jstree-node,
.jstree-default-dark-large.jstree-rtl > .jstree-no-dots .jstree-leaf > .jstree-ocl {
  background: transparent;
}
.jstree-default-dark-large.jstree-rtl > .jstree-no-dots .jstree-open > .jstree-ocl {
  background-position: -32px -32px;
}
.jstree-default-dark-large.jstree-rtl > .jstree-no-dots .jstree-closed > .jstree-ocl {
  background-position: 0px -32px;
}
.jstree-default-dark-large .jstree-themeicon-custom {
  background-color: transparent;
  background-image: none;
  background-position: 0 0;
}
.jstree-default-dark-large > .jstree-container-ul .jstree-loading > .jstree-ocl {
  background: url("throbber.gif") center center no-repeat;
}
.jstree-default-dark-large .jstree-file {
  background: url("32px.png") -96px -64px no-repeat;
}
.jstree-default-dark-large .jstree-folder {
  background: url("32px.png") -256px 0px no-repeat;
}
.jstree-default-dark-large > .jstree-container-ul > .jstree-node {
  margin-left: 0;
  margin-right: 0;
}
#jstree-dnd.jstree-default-dark-large {
  line-height: 32px;
  padding: 0 4px;
}
#jstree-dnd.jstree-default-dark-large .jstree-ok,
#jstree-dnd.jstree-default-dark-large .jstree-er {
  background-image: url("32px.png");
  background-repeat: no-repeat;
  background-color: transparent;
}
#jstree-dnd.jstree-default-dark-large i {
  background: transparent;
  width: 32px;
  height: 32px;
  line-height: 32px;
}
#jstree-dnd.jstree-default-dark-large .jstree-ok {
  background-position: 0px -64px;
}
#jstree-dnd.jstree-default-dark-large .jstree-er {
  background-position: -32px -64px;
}
.jstree-default-dark-large .jstree-ellipsis {
  overflow: hidden;
}
.jstree-default-dark-large .jstree-ellipsis .jstree-anchor {
  width: calc(100% - 37px);
  text-overflow: ellipsis;
  overflow: hidden;
}
.jstree-default-dark-large.jstree-rtl .jstree-node {
  background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAACAQMAAAAD0EyKAAAABlBMVEUAAAAdHRvEkCwcAAAAAXRSTlMAQObYZgAAAAxJREFUCNdjgIIGBgABCgCBvVLXcAAAAABJRU5ErkJggg==");
}
.jstree-default-dark-large.jstree-rtl .jstree-last {
  background: transparent;
}
@media (max-width: 768px) {
  #jstree-dnd.jstree-dnd-responsive {
    line-height: 40px;
    font-weight: bold;
    font-size: 1.1em;
    text-shadow: 1px 1px white;
  }
  #jstree-dnd.jstree-dnd-responsive > i {
    background: transparent;
    width: 40px;
    height: 40px;
  }
  #jstree-dnd.jstree-dnd-responsive > .jstree-ok {
    background-image: url("40px.png");
    background-position: 0 -200px;
    background-size: 120px 240px;
  }
  #jstree-dnd.jstree-dnd-responsive > .jstree-er {
    background-image: url("40px.png");
    background-position: -40px -200px;
    background-size: 120px 240px;
  }
  #jstree-marker.jstree-dnd-responsive {
    border-left-width: 10px;
    border-top-width: 10px;
    border-bottom-width: 10px;
    margin-top: -10px;
  }
}
@media (max-width: 768px) {
  .jstree-default-dark-responsive {
    /*
	.jstree-open > .jstree-ocl,
	.jstree-closed > .jstree-ocl { border-radius:20px; background-color:white; }
	*/
  }
  .jstree-default-dark-responsive .jstree-icon {
    background-image: url("40px.png");
  }
  .jstree-default-dark-responsive .jstree-node,
  .jstree-default-dark-responsive .jstree-leaf > .jstree-ocl {
    background: transparent;
  }
  .jstree-default-dark-responsive .jstree-node {
    min-height: 40px;
    line-height: 40px;
    margin-left: 40px;
    min-width: 40px;
    white-space: nowrap;
  }
  .jstree-default-dark-responsive .jstree-anchor {
    line-height: 40px;
    height: 40px;
  }
  .jstree-default-dark-responsive .jstree-icon,
  .jstree-default-dark-responsive .jstree-icon:empty {
    width: 40px;
    height: 40px;
    line-height: 40px;
  }
  .jstree-default-dark-responsive > .jstree-container-ul > .jstree-node {
    margin-left: 0;
  }
  .jstree-default-dark-responsive.jstree-rtl .jstree-node {
    margin-left: 0;
    margin-right: 40px;
    background: transparent;
  }
  .jstree-default-dark-responsive.jstree-rtl .jstree-container-ul > .jstree-node {
    margin-right: 0;
  }
  .jstree-default-dark-responsive .jstree-ocl,
  .jstree-default-dark-responsive .jstree-themeicon,
  .jstree-default-dark-responsive .jstree-checkbox {
    background-size: 120px 240px;
  }
  .jstree-default-dark-responsive .jstree-leaf > .jstree-ocl,
  .jstree-default-dark-responsive.jstree-rtl .jstree-leaf > .jstree-ocl {
    background: transparent;
  }
  .jstree-default-dark-responsive .jstree-open > .jstree-ocl {
    background-position: 0 0 !important;
  }
  .jstree-default-dark-responsive .jstree-closed > .jstree-ocl {
    background-position: 0 -40px !important;
  }
  .jstree-default-dark-responsive.jstree-rtl .jstree-closed > .jstree-ocl {
    background-position: -40px 0 !important;
  }
  .jstree-default-dark-responsive .jstree-themeicon {
    background-position: -40px -40px;
  }
  .jstree-default-dark-responsive .jstree-checkbox,
  .jstree-default-dark-responsive .jstree-checkbox:hover {
    background-position: -40px -80px;
  }
  .jstree-default-dark-responsive.jstree-checkbox-selection .jstree-clicked > .jstree-checkbox,
  .jstree-default-dark-responsive.jstree-checkbox-selection .jstree-clicked > .jstree-checkbox:hover,
  .jstree-default-dark-responsive .jstree-checked > .jstree-checkbox,
  .jstree-default-dark-responsive .jstree-checked > .jstree-checkbox:hover {
    background-position: 0 -80px;
  }
  .jstree-default-dark-responsive .jstree-anchor > .jstree-undetermined,
  .jstree-default-dark-responsive .jstree-anchor > .jstree-undetermined:hover {
    background-position: 0 -120px;
  }
  .jstree-default-dark-responsive .jstree-anchor {
    font-weight: bold;
    font-size: 1.1em;
    text-shadow: 1px 1px white;
  }
  .jstree-default-dark-responsive > .jstree-striped {
    background: transparent;
  }
  .jstree-default-dark-responsive .jstree-wholerow {
    border-top: 1px solid #666;
    border-bottom: 1px solid #000;
    background: #333333;
    height: 40px;
  }
  .jstree-default-dark-responsive .jstree-wholerow-hovered {
    background: #555;
  }
  .jstree-default-dark-responsive .jstree-wholerow-clicked {
    background: #5fa2db;
  }
  .jstree-default-dark-responsive .jstree-children .jstree-last > .jstree-wholerow {
    box-shadow: inset 0 -6px 3px -5px #111111;
  }
  .jstree-default-dark-responsive .jstree-children .jstree-open > .jstree-wholerow {
    box-shadow: inset 0 6px 3px -5px #111111;
    border-top: 0;
  }
  .jstree-default-dark-responsive .jstree-children .jstree-open + .jstree-open {
    box-shadow: none;
  }
  .jstree-default-dark-responsive .jstree-node,
  .jstree-default-dark-responsive .jstree-icon,
  .jstree-default-dark-responsive .jstree-node > .jstree-ocl,
  .jstree-default-dark-responsive .jstree-themeicon,
  .jstree-default-dark-responsive .jstree-checkbox {
    background-image: url("40px.png");
    background-size: 120px 240px;
  }
  .jstree-default-dark-responsive .jstree-node {
    background-position: -80px 0;
    background-repeat: repeat-y;
  }
  .jstree-default-dark-responsive .jstree-last {
    background: transparent;
  }
  .jstree-default-dark-responsive .jstree-leaf > .jstree-ocl {
    background-position: -40px -120px;
  }
  .jstree-default-dark-responsive .jstree-last > .jstree-ocl {
    background-position: -40px -160px;
  }
  .jstree-default-dark-responsive .jstree-themeicon-custom {
    background-color: transparent;
    background-image: none;
    background-position: 0 0;
  }
  .jstree-default-dark-responsive .jstree-file {
    background: url("40px.png") 0 -160px no-repeat;
    background-size: 120px 240px;
  }
  .jstree-default-dark-responsive .jstree-folder {
    background: url("40px.png") -40px -40px no-repeat;
    background-size: 120px 240px;
  }
  .jstree-default-dark-responsive > .jstree-container-ul > .jstree-node {
    margin-left: 0;
    margin-right: 0;
  }
}
.jstree-default-dark {
  background: #333;
}
.jstree-default-dark .jstree-anchor {
  color: #999;
  text-shadow: 1px 1px 0 rgba(0, 0, 0, 0.5);
}
.jstree-default-dark .jstree-clicked,
.jstree-default-dark .jstree-checked {
  color: white;
}
.jstree-default-dark .jstree-hovered {
  color: white;
}
#jstree-marker.jstree-default-dark {
  border-left-color: #999;
  background: transparent;
}
.jstree-default-dark .jstree-anchor > .jstree-icon {
  opacity: 0.75;
}
.jstree-default-dark .jstree-clicked > .jstree-icon,
.jstree-default-dark .jstree-hovered > .jstree-icon,
.jstree-default-dark .jstree-checked > .jstree-icon {
  opacity: 1;
}
.jstree-default-dark.jstree-rtl .jstree-node {
  background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAACAQMAAAB49I5GAAAABlBMVEUAAACZmZl+9SADAAAAAXRSTlMAQObYZgAAAAxJREFUCNdjAAMOBgAAGAAJMwQHdQAAAABJRU5ErkJggg==");
}
.jstree-default-dark.jstree-rtl .jstree-last {
  background: transparent;
}
.jstree-default-dark-small.jstree-rtl .jstree-node {
  background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABIAAAACAQMAAABv1h6PAAAABlBMVEUAAACZmZl+9SADAAAAAXRSTlMAQObYZgAAAAxJREFUCNdjAAMHBgAAiABBI4gz9AAAAABJRU5ErkJggg==");
}
.jstree-default-dark-small.jstree-rtl .jstree-last {
  background: transparent;
}
.jstree-default-dark-large.jstree-rtl .jstree-node {
  background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAACAQMAAAAD0EyKAAAABlBMVEUAAACZmZl+9SADAAAAAXRSTlMAQObYZgAAAAxJREFUCNdjgIIGBgABCgCBvVLXcAAAAABJRU5ErkJggg==");
}
.jstree-default-dark-large.jstree-rtl .jstree-last {
  background: transparent;
}
admin/js/jstree/dist/themes/default-dark/32px.png000064400000002765151327705670015670 0ustar00�PNG


IHDR@`[�[�PLTE�����������������հ�����???��Ƌ��   hhh����������虙���������������������������������������ȏ��xxxt{FtRNS1=%+Oa;����y�۵�D̐9Fi��"��IDATx��S�0�IҴ,��"(�nBZJ[��o{�&}�@f�z?�z�O��k��,�֛M��
D�
�vl`S���p�:@98�դ-
t��9w�Mu�w����ON�k���y�
�C���eš�
,�'�\��:����z�(�`M�����¤�\0Z�3�&��E/	��}M?|E�Zy~��6�?+�?R��� ��)?���/!&
$‡�T�W��������0Z~~
4��	���V�ϐ�UJY�����?V<b���Sx~�F�$.m��K���I����	��]~���M����'yz��x�����q�5�Oa�(?�")#U�5��=]'I$�۽���X�(�0��Z�AF��f
�7���
s@��5��dM.�t��r�{�meHru��0d�����0k�R��'�~�Ա�O���u.%����<W��KF�����o��Ȭ�Q��ұ�OBr����w"���]T����y�F
������Žn��X'4�40 ���f�w~�s~:7ֹ�����^J޵��끔��F�v��6��@��@�c`O�G��l Rl 6�[`�����ڙ�n
�;h��dްr��x������>����@�؛������=��|����_z�4�?h��=1��������ʯ�W)4�?hGT�`�(��WU��G�_�j���-5�O)�8��:h�o{>���|��0�8����Z{VJ�����@Y�y7���|��;�{V8�7q>�׈!�/�7q>��1�f=�㴷�H7�"��"���tl Rl �1��-���?�8N����7z�Ɓ�\��{�e䌣ߎ�����ףd6�>D�Az�d���|�<���BL��^����ǁ_�
�K���!���#l,�y&�)6PgD���'��G�5�e"�@c�X����S�~s��;V�]/��ǭ�VY~:9$�?A~�@�i�"�w��4?b��S�Y��3�`I�E�Qa�������l�(�6�Q�ď�K8��`	�;���챠�g�0�/�P�'FO"k���,K��)g�4_�)FOl	*n�
�~/ӷ@�5@�4A	�+l�8�����ac�Pf����"��-Q��l&�ѭ\���k�5�
<�i��	��.�h�bB�@\�B:�@�"5�"xk�)ϋD-��IEND�B`�admin/js/jstree/dist/themes/default-dark/40px.png000064400000014576151327705670015672 0ustar00�PNG


IHDR���*��PLTE333�������������������������������������������������������������������������������������ssstttuuuvvvwwwxxxyyyzzz{{{|||}}}~~~���


XXX$$$dddhhhooo000UUUVVVWWWXXXYYYZZZ[[[\\\]]]^^^TTTNNNOOOPPPQQQRRRSSSTTTUUUVVV[[[KKKLLL���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������U���tRNS
%&(01369:=>>CEGGGGIKTTTTTTTTTTUX\\\\\\\\\\ahhy�����������������������������?�
IDATx���k�ՙ�s�5#�ØE�dB<���l�-������o�5��f���q��`�����JH���~�}�*.6�Yc�<Д��Q�˾�]jIݒ���:��qK}N������\�����/�����
o�31a�k�k�k�kp���g�:�1/>�����[�9�8��OL��g�q��͐�XXl��
\l��
Zl:d9|6��Ͱ���;�J���3��@�]�V:���sm�s�,2��}������E6c�x��`sD���#�P#l����ű�;�����T����z|6���WB׽.���
�Y����P����Q�^7p��M5����xy]�m��\�gVn��.�T�}M]���{���mjY�xǬ=�������՛)�������p�ڹ��5iw��ݼ�c�u�îb�8z�Z�����h̥=�}x����P�+��
��mX�Y�Ju�cu!��HA)�Z�B�
��s��kJ��	İ�.���A��	�]S�rT��Y��H)U���1:`駚<��q���LĎ��0r�KJ���"2��_��'����1��[�w�9��Y�ׅ�A&b&�a���<�vJ�Z�&0&_<��[�YK硄�M��d<p�(��LĘy�B��-�ck\	^3���O�*\X��E,��z��-���*bUm�K��o���#BH�B��{K9ky�;��@d�dN-�w�,\<$�Ꝅ����0�-u�O�q��
d��S���k��
�,6�8p%J��n?y�U�@�N��oS]��_p%E�`M���E��RmJ��S��¹�@fr�k���՗����Cs��w=�{K�E��3��NJ�T���[�Vh�3��I/�}o�iҏ�Ͼ�;�����͡+T_��
�kkb�ԡWK��0�so���\�?�B���?��511e6�m"a��W^Ŋ�>F�&�owde�3����"�����p��[~�����S
!��,1��~���Vm<U�����Bp%��Tj��"3,�z�t�/�w^B���N;�B|j����٣�0=�띻!�[Z�{��{����{�0	����oKH��n!֏6�
�d��pi��s��B�-g��? �~���y��,:������k�k�k�k�k�k�d���*冎��R�+�m��j��Bh��Ǒ߆�e4#�X�x5�ȬTse���x���jq�u��~�"$�d��+��&��6��3^
-N
�> �bkƫA�s՞�D�G��Y9/�֤�3^
$^|���n�e��_~��ؙ�j���mp�J��2^
 ��[�P(�G��x5�X}�w�oUH}�sD� ��kQz
�A���(���iqHG�<�E׳��b�G.>��٤Ұ4"��a	!
K��Ͱ4�V=F�ñ���(�=񈡸�L+~�~S�؉��]Z�����/�[�:�W�ق���P�`@���&�%�2E<5��іD"���l�J�e��
&��O�?s�%��޼�
:��\]�����g^��i�ؚg/�>�F�Mzq��њ���w��S8C�Mzq��eޒ
����P)��6`������,2#f\��UBp��H�����ޜ��1��Ǿ���s��d����}_�M��0�?����VL���'�ox��ĥ�>�薂;�#l��"�8���ʢ��� 2����*�Gx��3�>�8��oK[������۾�ؗ�{KaExq�g�^,9��eF"y����|%�S�a�a��G���V�T��Od�Ԏ�'��}W ��<�>����#�a������]��s��ga����ɫ���η�y&C
�x����<p�7�2�P ӘJ��N� H��פ���������)_I)<7i��#ߞ���*`D�||7��e�5��V����^O���D�s�|sƫ{���f��y�13��R���j`��f��<���	��|-���!<H,��3
�9؅�.>���}�-��*T0b��jY�4��Dxa��`
��o�k�k�k�k�k�k��eث&:��������8�/�p���#�5Ve&֨�-�J���.�[am�Ѻ��4���{�����U���di��[y�R*0r7���v�$S}�r.�#X���#c�B��ڛ5/��M��@6(�9���~��'6��z�O���p^O�����
���m:hw]RYo�HV@��X}�|�>G�����ܵ��R� �.��z=�9יm������L���=�D��j�ŧ�oy�YK���;��f�<��_yr���n�6��@x�i�)�uƵ'�w��K��9��_35���n1�M��{���\:[O<���1��f�}��ia6��ܘf��>�^?��u��\�d{�D�\y7��ӾL��]1��4y���\�0��.�TP��V����9�D��&��7^���\ky��w+�T#ӓ*`�uU%�h�]��;���L��<|'p�T����7�ZMF�h�]ys���k�S���'�&-�S�����ؕ7��H_D^��N�`�%X-I��^"�F�ʛ�?J_r���N��FK�����c��ʛĻ���E���>�	�.��h�
�$`�+����[�$�<�a��:��(f�a��b��U��g8���g����#
R�^"�R^g�9A�Ӥ!�m}�SdP����(o9P������u�Ax]�� �R�h��7������C��n�,�;�$��.�rB
!j��W�����W���⩼r�,
�W�!�s��3+��"�٭rY�û�k�P�5[.;���r!��R*zq�ʹ�࿦_�����)�a��Fy���g^2 $���Y�mf���TV̘:S�r��S^(�\*Y*����N��e�t�i���on{��Gy!�=,�\);�\.�&cn�@.`$�S'�޲�x//�9k����"��T�J)�X"�cf��[���8r���K�>� 1Ø��]�oy��cy�=��)bT�\��d�D�s*5�kվ�O�Pr����Ti���@̘��N�<�b���r�ˏ+���֤m�Xb:�:�M���-/�Ӑ�uҜ=V�m;hN�ȋ��wyx����>:��F���H�p���>�1\&<���t�5X�5X�5X�5X�G�l�k�k�k�k�k�k�k�k���\�+�mG �U��h�
��WA�#��* pt�_�P��@�Q�8R���G*�U�h�
��W���_�L-‹���~pS�+��W�
2�����_����Ucئ{��2�ncơO�k�k�Gܔ���aj��؃�r[��DMPL�M���Y�Z���X*T,R����p���z�ڍ��t%�U��6p���_݈P�䶢�����
\�mE�?������>\�Ū��W�c2�U ��RR��I� �_1��:�nT׈��_m?����83e���0Mƃ����Z�+(��V��d���"���j����WƬ���>d���p5���Z9�=#�C�J/}X����W
/_���w�;A�
\�%ʿM0֑,HJ�a�����"��*p��_r�'J��G)�U0��
��WA�#��*0pT�_F$�_����#���������������}���/t��`
�`
�`
��I{��-��)��]�1���z�sݞ�}��fl[���=��dic���w����M�8M��I����Q��'	L�b����[<Ȱ4����&������Ƶ��ڪ�Z^9j�I�_r���{�M��	h��=�ڟ��&��h�D�S?j��3�'>�?ͯ�x/�>��_O�8(�0��lj�ϥ�%�VیK��I����-��#���[A�Ӫ4�LZ�|#j�7؈Y��R�^`��oD
,D��Q��{�U��Nop�%!z�
1l�=��%���^�8F�'���9�L��_�{���,���j��e�%��v"~'=#,DgZ&�+?x�O�<5��F���i3^`sF��Kj��i�
����R���9m����	\<ѷ��b��1L�֧kp��:�������������#_t�5X�5X�58���\���h�
ῡ6�٘���"<�ϻ����?:a�]F�o��舆�y�ޅ�Z�8��<������J�6���x.�E��l.��`���(���Y ��:P�`w���t.�9��Qϥ���|�g++�:is�z��>+���A�s_%p�\�x[��Zhsͫ�y>�������y[�s-��\�-^�b�e��x�m�6����V�VU�\����#��_��p�R�[e3�t),�'bv�"Sj�w{()�\
�Xv�ڹΩG�R�t6�%�wV�x`/rV����YX�`���Ł{�fw������v��7�w��8c�9��8�uo��Gm}�x{�+���}����8owp�t�!v�����@�]���ݚx4^`]�x����U�/���y;�
~��QɉG����V����x_�'�}#Z��~�N�O_�JA�@t}
��� "�g�Pa~�1іo���V��O_��#"�z ��jmEe��^��'��Bu���BK<��*���vE����NSN@"ƌ����5���.e��+�e8M:yE�zF<#��(U�@`��`;g�/;�_����|��?p
\:xY
Q�fD @��OF`��{�Ug�/n	љA������I+l��:IY�j?:�Ad�7;�Z�[�h��1�C^8Z�h���K�II�<��F�Q����+}�{��{���38?�2�=,}�ck��11��8f:h{^�~�ײ�c(%�6�\;�eZy�s��٨�=n�;7��}��pY�c�B�����/[���4����Y�o���'�b ��~�bɩ{d&�p���$$�z��ޤ���U��8��%�'�֔Z+��ߪ4�ja�m�vF�Z-Ąb���AO<*˝��:#�S;X�1��}��/��R�HU+,uھ���>#k��)����g�Ww$W
 2
s:u����'�1H��j.��"�3OC�+�E��E̠Dr�}{�r��J��(����z-�2�w,}��)��GO�9U���4;f�����x����Q�Qsa�Z*�w�ع��4�[Ϭ��C�J=�j��0`�ՓB(aL�v���9���g�.+E�H>��}s��r��e�L�v�Y��a�����-�)�����v;��#>wj��;W[���m�C�0�s�^Irs����.g��+�}���0�x���_^���~�Ɵx�U����eu��ʼn�rqd��~q����\�jߗ�~)bb_����5�ߏ{�/�g��<��b��۳�ؘ0p���Z/op�e��p"�dx��A�Y��/"�MZ���q�ñ;Jkp��:���۶�?9#2=-wNIEND�B`�admin/js/jstree/dist/themes/default-dark/throbber.gif000064400000002670151327705670016657 0ustar00GIF89a�```���@@@PPPppp���������hhh999���xxx���XXX���333!�NETSCAPE2.0!�Created with ajaxload.info!�
,@p�ɗ�2uJ�:�S"�L�$�cB�J����"S�P(	G��%��$Bʄ`��d��c���ד!L�XwVrP5�g��*�a��	:1
o!Y3�  �!�
,]��Ȓ�N&�A�IG�8�R�(Pp�'�aE�I��A0[�֣� X���X@���7i0[��h$
��(�,�UC��%s s�j#!�
,Z���X.�TJG&9F�%��TE�5�0H�р���=M ��`�R.�� �|�
�V�}��������� ���9���S"!�
,\��)���g�4��-�&C�� !���%V����1P�%����
�"p�r�eQ�(��`,P	��d�}��BIb��&�-��!�
,Eɉ�XY
���!(^��HqX^OF�a�J��D����B�rɔ>C�@�9N�"BkJf��PE!�
,k���Ơإ&���%� �������Ј`G�0��1HL��8�`��U
�`` ��@�,���A6O	0(0&��B��fJPh
#il�~!�
,[����X	���xJH2>L�Y���H�-	t�L�Y�&�$��X��L�q8NC#��<1a�x$n�S��x\y�ĚH�;
�1n8�)!�
,V�I!ǐM�Z�X��]��$�&PNC8��H�����n�����@�5��H+k��±�
c:c�
��WJ q86
��ڡ�4�c!�
,]��R�X�'�&\�T�u	�a�e
��,x�/E�(
'���Q0�!�P�M��Q8x�Ƃ�(��v8�z��"�~>� !�6J9!�
,Z���R��ji��Z�m�&��N
2\TI������Q��J���r�&�Ł)Q�
FÁ08
IC00�-Lsp���f`u0�5��E!�
,X��I_�x&Q��ER���
d�M�ZEӉ��g��G @��D���U���p>�"��,$�d 7�`�&H��&���!�
,Z���������F�|R�E�	�i㕰�Y+!� ��@@P����50xp2�#�i �N�0lN��$0@H��N}X<̏a}q���/)�D;admin/js/jstree/dist/themes/default/32px.png000064400000013043151327705670014740 0ustar00�PNG


IHDR@`lK�(�IDATx��XT���7�����G���[u�4IP?���,���c&hy�2Ox�J;ie�'�G������*r󂢢)��
�MA`�3ܯ3�o�{fӈ������y~��lf���_{��G �Z�XJ���s�b���o��M.cr���ZX���8r���…'@ '0N�\8��p��	��	� N�,@.\�pdr����������P��hU��~��8�zN�^�����ȅ�
e㦍0�t:�.`�IԵ�tu:z��,�C=��A~,��Q�}j�d��uj��6�X�}��5۷����৩��Dl��$���j���@����XlD��EE�($
�`4���(.�K�.��`�%�f���+�+`,�6��_����<��m��+�кj�OEi����'9�����a��r�Z	��0M��.ZK�n	�a�J\y�������_P�"!�b��TZ!ˉ����k�hĹ�����2���	\̈AB�I\ȊF܍�p#��:Z	P�(b�~��W"���
���y�К�H��$B�sM��"@5�\jзo_������<�tnMt+MY����ϗ���g�H^��H����s~^>|bO�
�/�7Ε�D\y(.���\�.1~�KU���y������gG�bw\����ԝ�_$�����K]���X�荎s��VS6��lW/��,��i�	�� ��mGs���h�DԪ��.@g��̀!ˀ��4���#+�$�+$�/��@`TX����ss��l�x~9sn\��JD�ľ��S�a�K�6�����S��C�/?���	2~���_Ƿ���qF���7��S���T,��� Mq鯅InT�?�P_S	Ю	���� }��O�BkL����tuB��Ӯ#5�:�_OCZ�
!�,�dg��ǣ����%E���B�$�yQ^X�4������ɜ��Y؜�|3��&���=���K4�Ѷ���E<���O���,��E�>2��c��^��x��@x�7�
��h��?��\u��
�h�1��H']�MG�M����S��L����kFF&���`HrJ�,´�4d���%�������Z$@��=� �,H|�&��'Ic�q��BN��v��7�L	#�UO��dס#�އqG�`�N��Dw�g���v������$��D��M�#&�&� 4�a+8�����u�����'z�-Z����5��[�T�
�S���U�"�)���z�\KN�ex]t��,C��-�ƌF9�� 	v����O�Ô�~x�ģ�󐐓+����BF�usU���S���Bp!ٍ�����$�m��A��z!&1���$��^�lT�����*�rΩ!�[��'�e	ѓ���H�O�^�גEL�t����20�z�'?�!���n�a����cXp{��j�'�m��y�FMҾ�ڇ����0`���t�	��%��_s/_��i���_N��(��O�m"T�G�+����A��i!���~R‹OH@|��!B��/#%�"�Tqn�1MYQ[�t���ƛz�H����[$<��}|%в^���눉�F��-@�ڦ�赪3�ZB��VDۏ�x�����e����$u����)�E����S���E'��2
�Zؚ�?�/}���Bi0A��T�yAB+zno��C�i#K�����8�ǎP�H�h%@���[p��.�znpB���G��}��n�x��A��g+8-��y`+`k�_[&Xk}�^��D����P�ԾDw@'�S$�Ir���R!��rAX�U'b+��T�?��N���!�=�7��M�㤴�����&�)�O��$��V|->2�=�����e�.+GY��.//����-�Q���]����5/�S�ȍ�����w�~�-���]��Ş�k��V�r3�V6���2�fj���pS�Z��kn��֍K������ �0͒�(�#�
�a�/@{ޏ��0L��w'�aN�,@�a8����d '@ ð9���	���=�}��	�a '@N��>���0� '@n�ard��	� �ϰ92���	��o���|ˁ�����3� èNʖ�-��~�ηd�lSZМ:AM��ڴ]��v�6+�h�/@i�)���_�h�rL�>W�����;A8r�j��ZjKrn���e�YzUDB��5U壦 �e��)�DUq:�Jn��Ԁ�
ږl�*�u�w�JpJ��O?ElR�//A����Jb�#���[�H|-��0*��� �&,R*�	��0�Ҏ#��f\���N �p	��q܍�8�~\^���B�
��l?�߫�Xy�u���|Hh���V��Ϸ����U۷o_p��}{�&J����x�_i��	�*+�$Ţ�d����Յ��IX�=��a\�sA�P���p�U�!��$��Ů�dY�-�(}V���Q������7�"�=�笆_�b����Dot��m��l��?�JP`H�
Y�T�N��&*_����"A{Ӓ������g�H��Bz��	�*���]RYE7l$����6W�`M�ؔ9~9�"�9"�W��Y����Xqz�������d�E����o��_L�f��0���X������bY�4�|���Z��=��4��ET���@:�����`K?�ά�H��K@y�iT�3�r��N��R,]�4�y�l�8�T�����gI�!c26f̀O�,����#�x�%xG�Ul�zt����k�̏
��`�/b��1�9�ч7���ֹ��:�o�����߉�t������'}�(���$h/Z��tL��ͪ��(ϼ���S(K9��Q�qI�bU�H��$T�S*J�F�yW0��'��<>N�O/��O.��'I��#�s�;c���k�(!�4��dס���cܑ.{�FE��+ѝ��$�xc����0	�C$<,a�FW��r\�AN�6����({~�*�M~k�V������׶���	�I�fVU��B~'Q�t�W�4��SḚ$C�{,D(��Ь֜��B��W���?Q=0%���ꏩ��ፓ�⿣\��O����g�mP�������hC�{�Ev#��Ű0G�Ip�!a�`�p.��|�5��'���ز�?%��of
�Sa�Q�Ƅ(�"Dx�ʯ�I0N�0A�S���ؠ�-� �N����^H�=���#�܃��v���Q��� ����<��<(a�6	����K�'p�w7��������2����zfuō"�Bѥ]��1~J����%LjDx�20"�i()[f�:R���I~�}|��G���c����ĠQ���z`5Zu�ZB��VD�V����6���oWhB4�3�b�	萺e��>,��+Sxq��B��?Dɵ�diEj��)7^~��z�t�\�Zk��u�I+�/C�D�/��s�]r/�����v���}����"@�	�.�y�L�$��;z��>ؔ�R�
�#��v%!^�/$��*�}�W�hq%��N�U���x�'!d絕�W��DX�spj
�^��Z�L���p_�<�zNDo�O~
Z����z
�������+AF�+A�23Se�<�O�+�,�����j4�����ۡ\����tGT�y�G���鋑�ȏ�u��R8�I� *�i9�N�Z`�Q�f�_�?��4�����!P�m�V�Q
��0�� �0,@N�ð92���0� �0,@N�ð92���0� �0�92�	��0'@ �0�Y��pd2� �aX��Y����{`��/I�t���$�W��0�`��F��靶hQƾ?�yRÃq�{��K���s�X-�$Ili�c�t���'�|��1���x����5����u_sin��*��m���v��n�AA(\��f�����\T�n^۶� %;w��JP�		���K�㦔�Ur����
��C�p����e��Տ�+!ܰ.�=I-9	m�p�=(z�9ԭ\���kk"\\&F
�׭�Ax8
�>����ˎt�:V���''��e6� }mK||��3K����;��೯���<ե�-�ϻ���wvx�j��.F`�
�M�Fǹ�F���N��:/6�� KPԹ3j�OG��U�\�X�X�زؾؿ�=�T�.(I��C���lL�fAqI1��&!�N�*
����C�g2~�������:wS6�owlŅwr|ӽ�\?K/MŲ�ixe��di�K��"޷M��.��5rFA���0ϛ�…���0}��D���G��&����$���9	�M,��(@��]���W:?�)x���?��7#����z�[�?>7�����
���>x��@̌��0߾zɳ�@������d�/�/(T=�0�f��y���O�뷓֥��Z�
%��9A�ͷ�@��V|)�QH���dȃ"'@5�dס�~]
�邱�:aTd����G^�z���1Qwap���!����vW%�n���"R��'�Kr,�J��gO��7�0fL���(pc����)p�С^�	6�L���pT��d�g��M�G����F�i�aa�p��C� ����E2��h+��m'�������%	Rw��G�?kV����'�a�0���sEؐ�pTsث�K=�8�<(a�6	����K�'趬���}n��Ztb��kW�/�$H������
B���ջŴ��`Sv�G��e6�k�ħ`yps0d2�՞��mt�%N�-��f+>:}�$y��d)�9:N���P{J��Y
�\!ڵ�U�β��9;�l�@&N��۵넖62r�H/��T`���Itwe���:���d@ �	P�y��]��ٽ�tB�
N�{��i���ҟ��?���/�#^��Ĝ��ա�n10��9Ús;v���ݻ/�R�M-?�#Fx�TWW��hУ���󫣚�	̜N�N��t}���
�|���풟����~�������
:9�h��<g=/x��;~����ﴴ��$�������J��	rd8jx%�����lG{�L�Ԟ�����yk��Do�u��r��}�b��\���VZW�i0v�n����bc]Ii	n��DL}��S`H�,@�`˺��5�.�޲�o~������Z���4Ϛ�jSUU5*++���QYQ���*�V����:�0�[��l���Zڛ�����[`�`�B�nއ��5�������-0��A� '@���w����.x���\�n߱]�������v]�.���|%.�Y��w.�Y�\�p�جȅ.�6r�…'@.\�p�ȅ.�$2ô��)I��u��IEND�B`�admin/js/jstree/dist/themes/default/style.min.css000064400000065221151327705670016077 0ustar00.jstree-node,.jstree-children,.jstree-container-ul{display:block;margin:0;padding:0;list-style-type:none;list-style-image:none}.jstree-node{white-space:nowrap}.jstree-anchor{display:inline-block;color:black;white-space:nowrap;padding:0 4px 0 1px;margin:0;vertical-align:top}.jstree-anchor:focus{outline:0}.jstree-anchor,.jstree-anchor:link,.jstree-anchor:visited,.jstree-anchor:hover,.jstree-anchor:active{text-decoration:none;color:inherit}.jstree-icon{display:inline-block;text-decoration:none;margin:0;padding:0;vertical-align:top;text-align:center}.jstree-icon:empty{display:inline-block;text-decoration:none;margin:0;padding:0;vertical-align:top;text-align:center}.jstree-ocl{cursor:pointer}.jstree-leaf>.jstree-ocl{cursor:default}.jstree .jstree-open>.jstree-children{display:block}.jstree .jstree-closed>.jstree-children,.jstree .jstree-leaf>.jstree-children{display:none}.jstree-anchor>.jstree-themeicon{margin-right:2px}.jstree-no-icons .jstree-themeicon,.jstree-anchor>.jstree-themeicon-hidden{display:none}.jstree-hidden,.jstree-node.jstree-hidden{display:none}.jstree-rtl .jstree-anchor{padding:0 1px 0 4px}.jstree-rtl .jstree-anchor>.jstree-themeicon{margin-left:2px;margin-right:0}.jstree-rtl .jstree-node{margin-left:0}.jstree-rtl .jstree-container-ul>.jstree-node{margin-right:0}.jstree-wholerow-ul{position:relative;display:inline-block;min-width:100%}.jstree-wholerow-ul .jstree-leaf>.jstree-ocl{cursor:pointer}.jstree-wholerow-ul .jstree-anchor,.jstree-wholerow-ul .jstree-icon{position:relative}.jstree-wholerow-ul .jstree-wholerow{width:100%;cursor:pointer;position:absolute;left:0;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.jstree-contextmenu .jstree-anchor{-webkit-user-select:none;-webkit-touch-callout:none}.vakata-context{display:none}.vakata-context,.vakata-context ul{margin:0;padding:2px;position:absolute;background:#f5f5f5;border:1px solid #979797;box-shadow:2px 2px 2px #999999}.vakata-context ul{list-style:none;left:100%;margin-top:-2.7em;margin-left:-4px}.vakata-context .vakata-context-right ul{left:auto;right:100%;margin-left:auto;margin-right:-4px}.vakata-context li{list-style:none}.vakata-context li>a{display:block;padding:0 2em 0 2em;text-decoration:none;width:auto;color:black;white-space:nowrap;line-height:2.4em;text-shadow:1px 1px 0 white;border-radius:1px}.vakata-context li>a:hover{position:relative;background-color:#e8eff7;box-shadow:0 0 2px #0a6aa1}.vakata-context li>a.vakata-context-parent{background-image:url("data:image/gif;base64,R0lGODlhCwAHAIAAACgoKP///yH5BAEAAAEALAAAAAALAAcAAAIORI4JlrqN1oMSnmmZDQUAOw==");background-position:right center;background-repeat:no-repeat}.vakata-context li>a:focus{outline:0}.vakata-context .vakata-context-hover>a{position:relative;background-color:#e8eff7;box-shadow:0 0 2px #0a6aa1}.vakata-context .vakata-context-separator>a,.vakata-context .vakata-context-separator>a:hover{background:white;border:0;border-top:1px solid #e2e3e3;height:1px;min-height:1px;max-height:1px;padding:0;margin:0 0 0 2.4em;border-left:1px solid #e0e0e0;text-shadow:0 0 0 transparent;box-shadow:0 0 0 transparent;border-radius:0}.vakata-context .vakata-contextmenu-disabled a,.vakata-context .vakata-contextmenu-disabled a:hover{color:silver;background-color:transparent;border:0;box-shadow:0 0 0}.vakata-context .vakata-contextmenu-disabled>a>i{filter:grayscale(100%)}.vakata-context li>a>i{text-decoration:none;display:inline-block;width:2.4em;height:2.4em;background:transparent;margin:0 0 0 -2em;vertical-align:top;text-align:center;line-height:2.4em}.vakata-context li>a>i:empty{width:2.4em;line-height:2.4em}.vakata-context li>a .vakata-contextmenu-sep{display:inline-block;width:1px;height:2.4em;background:white;margin:0 .5em 0 0;border-left:1px solid #e2e3e3}.vakata-context .vakata-contextmenu-shortcut{font-size:.8em;color:silver;opacity:.5;display:none}.vakata-context-rtl ul{left:auto;right:100%;margin-left:auto;margin-right:-4px}.vakata-context-rtl li>a.vakata-context-parent{background-image:url("data:image/gif;base64,R0lGODlhCwAHAIAAACgoKP///yH5BAEAAAEALAAAAAALAAcAAAINjI+AC7rWHIsPtmoxLAA7");background-position:left center;background-repeat:no-repeat}.vakata-context-rtl .vakata-context-separator>a{margin:0 2.4em 0 0;border-left:0;border-right:1px solid #e2e3e3}.vakata-context-rtl .vakata-context-left ul{right:auto;left:100%;margin-left:-4px;margin-right:auto}.vakata-context-rtl li>a>i{margin:0 -2em 0 0}.vakata-context-rtl li>a .vakata-contextmenu-sep{margin:0 0 0 .5em;border-left-color:white;background:#e2e3e3}#jstree-marker{position:absolute;top:0;left:0;margin:-5px 0 0 0;padding:0;border-right:0;border-top:5px solid transparent;border-bottom:5px solid transparent;border-left:5px solid;width:0;height:0;font-size:0;line-height:0}#jstree-dnd{line-height:16px;margin:0;padding:4px}#jstree-dnd .jstree-icon,#jstree-dnd .jstree-copy{display:inline-block;text-decoration:none;margin:0 2px 0 0;padding:0;width:16px;height:16px}#jstree-dnd .jstree-ok{background:green}#jstree-dnd .jstree-er{background:red}#jstree-dnd .jstree-copy{margin:0 2px 0 2px}.jstree-default .jstree-node,.jstree-default .jstree-icon{background-repeat:no-repeat;background-color:transparent}.jstree-default .jstree-anchor,.jstree-default .jstree-animated,.jstree-default .jstree-wholerow{transition:background-color .15s,box-shadow .15s}.jstree-default .jstree-hovered{background:#e7f4f9;border-radius:2px;box-shadow:inset 0 0 1px #cccccc}.jstree-default .jstree-context{background:#e7f4f9;border-radius:2px;box-shadow:inset 0 0 1px #cccccc}.jstree-default .jstree-clicked{background:#beebff;border-radius:2px;box-shadow:inset 0 0 1px #999999}.jstree-default .jstree-no-icons .jstree-anchor>.jstree-themeicon{display:none}.jstree-default .jstree-disabled{background:transparent;color:#666666}.jstree-default .jstree-disabled.jstree-hovered{background:transparent;box-shadow:none}.jstree-default .jstree-disabled.jstree-clicked{background:#efefef}.jstree-default .jstree-disabled>.jstree-icon{opacity:.8;filter:url("data:image/svg+xml;utf8,<svg xmlns=\'http://www.w3.org/2000/svg\'><filter id=\'jstree-grayscale\'><feColorMatrix type=\'matrix\' values=\'0.3333 0.3333 0.3333 0 0 0.3333 0.3333 0.3333 0 0 0.3333 0.3333 0.3333 0 0 0 0 0 1 0\'/></filter></svg>#jstree-grayscale");filter:gray;-webkit-filter:grayscale(100%)}.jstree-default .jstree-search{font-style:italic;color:#8b0000;font-weight:bold}.jstree-default .jstree-no-checkboxes .jstree-checkbox{display:none !important}.jstree-default.jstree-checkbox-no-clicked .jstree-clicked{background:transparent;box-shadow:none}.jstree-default.jstree-checkbox-no-clicked .jstree-clicked.jstree-hovered{background:#e7f4f9}.jstree-default.jstree-checkbox-no-clicked>.jstree-wholerow-ul .jstree-wholerow-clicked{background:transparent}.jstree-default.jstree-checkbox-no-clicked>.jstree-wholerow-ul .jstree-wholerow-clicked.jstree-wholerow-hovered{background:#e7f4f9}.jstree-default>.jstree-striped{min-width:100%;display:inline-block;background:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAAkCAMAAAB/qqA+AAAABlBMVEUAAAAAAAClZ7nPAAAAAnRSTlMNAMM9s3UAAAAXSURBVHjajcEBAQAAAIKg/H/aCQZ70AUBjAATb6YPDgAAAABJRU5ErkJggg==") left top repeat}.jstree-default>.jstree-wholerow-ul .jstree-hovered,.jstree-default>.jstree-wholerow-ul .jstree-clicked{background:transparent;box-shadow:none;border-radius:0}.jstree-default .jstree-wholerow{-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box}.jstree-default .jstree-wholerow-hovered{background:#e7f4f9}.jstree-default .jstree-wholerow-clicked{background:#beebff;background:-webkit-linear-gradient(top, #beebff 0, #a8e4ff 100%);background:linear-gradient(to bottom, #beebff 0, #a8e4ff 100%)}.jstree-default .jstree-node{min-height:24px;line-height:24px;margin-left:24px;min-width:24px}.jstree-default .jstree-anchor{line-height:24px;height:24px}.jstree-default .jstree-icon{width:24px;height:24px;line-height:24px}.jstree-default .jstree-icon:empty{width:24px;height:24px;line-height:24px}.jstree-default.jstree-rtl .jstree-node{margin-right:24px}.jstree-default .jstree-wholerow{height:24px}.jstree-default .jstree-node,.jstree-default .jstree-icon{background-image:url("32px.png")}.jstree-default .jstree-node{background-position:-292px -4px;background-repeat:repeat-y}.jstree-default .jstree-last{background:transparent}.jstree-default .jstree-open>.jstree-ocl{background-position:-132px -4px}.jstree-default .jstree-closed>.jstree-ocl{background-position:-100px -4px}.jstree-default .jstree-leaf>.jstree-ocl{background-position:-68px -4px}.jstree-default .jstree-themeicon{background-position:-260px -4px}.jstree-default>.jstree-no-dots .jstree-node,.jstree-default>.jstree-no-dots .jstree-leaf>.jstree-ocl{background:transparent}.jstree-default>.jstree-no-dots .jstree-open>.jstree-ocl{background-position:-36px -4px}.jstree-default>.jstree-no-dots .jstree-closed>.jstree-ocl{background-position:-4px -4px}.jstree-default .jstree-disabled{background:transparent}.jstree-default .jstree-disabled.jstree-hovered{background:transparent}.jstree-default .jstree-disabled.jstree-clicked{background:#efefef}.jstree-default .jstree-checkbox{background-position:-164px -4px}.jstree-default .jstree-checkbox:hover{background-position:-164px -36px}.jstree-default.jstree-checkbox-selection .jstree-clicked>.jstree-checkbox,.jstree-default .jstree-checked>.jstree-checkbox{background-position:-228px -4px}.jstree-default.jstree-checkbox-selection .jstree-clicked>.jstree-checkbox:hover,.jstree-default .jstree-checked>.jstree-checkbox:hover{background-position:-228px -36px}.jstree-default .jstree-anchor>.jstree-undetermined{background-position:-196px -4px}.jstree-default .jstree-anchor>.jstree-undetermined:hover{background-position:-196px -36px}.jstree-default .jstree-checkbox-disabled{opacity:.8;filter:url("data:image/svg+xml;utf8,<svg xmlns=\'http://www.w3.org/2000/svg\'><filter id=\'jstree-grayscale\'><feColorMatrix type=\'matrix\' values=\'0.3333 0.3333 0.3333 0 0 0.3333 0.3333 0.3333 0 0 0.3333 0.3333 0.3333 0 0 0 0 0 1 0\'/></filter></svg>#jstree-grayscale");filter:gray;-webkit-filter:grayscale(100%)}.jstree-default>.jstree-striped{background-size:auto 48px}.jstree-default.jstree-rtl .jstree-node{background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAACAQMAAAB49I5GAAAABlBMVEUAAAAdHRvEkCwcAAAAAXRSTlMAQObYZgAAAAxJREFUCNdjAAMOBgAAGAAJMwQHdQAAAABJRU5ErkJggg==");background-position:100% 1px;background-repeat:repeat-y}.jstree-default.jstree-rtl .jstree-last{background:transparent}.jstree-default.jstree-rtl .jstree-open>.jstree-ocl{background-position:-132px -36px}.jstree-default.jstree-rtl .jstree-closed>.jstree-ocl{background-position:-100px -36px}.jstree-default.jstree-rtl .jstree-leaf>.jstree-ocl{background-position:-68px -36px}.jstree-default.jstree-rtl>.jstree-no-dots .jstree-node,.jstree-default.jstree-rtl>.jstree-no-dots .jstree-leaf>.jstree-ocl{background:transparent}.jstree-default.jstree-rtl>.jstree-no-dots .jstree-open>.jstree-ocl{background-position:-36px -36px}.jstree-default.jstree-rtl>.jstree-no-dots .jstree-closed>.jstree-ocl{background-position:-4px -36px}.jstree-default .jstree-themeicon-custom{background-color:transparent;background-image:none;background-position:0 0}.jstree-default>.jstree-container-ul .jstree-loading>.jstree-ocl{background:url("throbber.gif") center center no-repeat}.jstree-default .jstree-file{background:url("32px.png") -100px -68px no-repeat}.jstree-default .jstree-folder{background:url("32px.png") -260px -4px no-repeat}.jstree-default>.jstree-container-ul>.jstree-node{margin-left:0;margin-right:0}#jstree-dnd.jstree-default{line-height:24px;padding:0 4px}#jstree-dnd.jstree-default .jstree-ok,#jstree-dnd.jstree-default .jstree-er{background-image:url("32px.png");background-repeat:no-repeat;background-color:transparent}#jstree-dnd.jstree-default i{background:transparent;width:24px;height:24px;line-height:24px}#jstree-dnd.jstree-default .jstree-ok{background-position:-4px -68px}#jstree-dnd.jstree-default .jstree-er{background-position:-36px -68px}.jstree-default .jstree-ellipsis{overflow:hidden}.jstree-default .jstree-ellipsis .jstree-anchor{width:calc(100% - 29px);text-overflow:ellipsis;overflow:hidden}.jstree-default.jstree-rtl .jstree-node{background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAACAQMAAAB49I5GAAAABlBMVEUAAAAdHRvEkCwcAAAAAXRSTlMAQObYZgAAAAxJREFUCNdjAAMOBgAAGAAJMwQHdQAAAABJRU5ErkJggg==")}.jstree-default.jstree-rtl .jstree-last{background:transparent}.jstree-default-small .jstree-node{min-height:18px;line-height:18px;margin-left:18px;min-width:18px}.jstree-default-small .jstree-anchor{line-height:18px;height:18px}.jstree-default-small .jstree-icon{width:18px;height:18px;line-height:18px}.jstree-default-small .jstree-icon:empty{width:18px;height:18px;line-height:18px}.jstree-default-small.jstree-rtl .jstree-node{margin-right:18px}.jstree-default-small .jstree-wholerow{height:18px}.jstree-default-small .jstree-node,.jstree-default-small .jstree-icon{background-image:url("32px.png")}.jstree-default-small .jstree-node{background-position:-295px -7px;background-repeat:repeat-y}.jstree-default-small .jstree-last{background:transparent}.jstree-default-small .jstree-open>.jstree-ocl{background-position:-135px -7px}.jstree-default-small .jstree-closed>.jstree-ocl{background-position:-103px -7px}.jstree-default-small .jstree-leaf>.jstree-ocl{background-position:-71px -7px}.jstree-default-small .jstree-themeicon{background-position:-263px -7px}.jstree-default-small>.jstree-no-dots .jstree-node,.jstree-default-small>.jstree-no-dots .jstree-leaf>.jstree-ocl{background:transparent}.jstree-default-small>.jstree-no-dots .jstree-open>.jstree-ocl{background-position:-39px -7px}.jstree-default-small>.jstree-no-dots .jstree-closed>.jstree-ocl{background-position:-7px -7px}.jstree-default-small .jstree-disabled{background:transparent}.jstree-default-small .jstree-disabled.jstree-hovered{background:transparent}.jstree-default-small .jstree-disabled.jstree-clicked{background:#efefef}.jstree-default-small .jstree-checkbox{background-position:-167px -7px}.jstree-default-small .jstree-checkbox:hover{background-position:-167px -39px}.jstree-default-small.jstree-checkbox-selection .jstree-clicked>.jstree-checkbox,.jstree-default-small .jstree-checked>.jstree-checkbox{background-position:-231px -7px}.jstree-default-small.jstree-checkbox-selection .jstree-clicked>.jstree-checkbox:hover,.jstree-default-small .jstree-checked>.jstree-checkbox:hover{background-position:-231px -39px}.jstree-default-small .jstree-anchor>.jstree-undetermined{background-position:-199px -7px}.jstree-default-small .jstree-anchor>.jstree-undetermined:hover{background-position:-199px -39px}.jstree-default-small .jstree-checkbox-disabled{opacity:.8;filter:url("data:image/svg+xml;utf8,<svg xmlns=\'http://www.w3.org/2000/svg\'><filter id=\'jstree-grayscale\'><feColorMatrix type=\'matrix\' values=\'0.3333 0.3333 0.3333 0 0 0.3333 0.3333 0.3333 0 0 0.3333 0.3333 0.3333 0 0 0 0 0 1 0\'/></filter></svg>#jstree-grayscale");filter:gray;-webkit-filter:grayscale(100%)}.jstree-default-small>.jstree-striped{background-size:auto 36px}.jstree-default-small.jstree-rtl .jstree-node{background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAACAQMAAAB49I5GAAAABlBMVEUAAAAdHRvEkCwcAAAAAXRSTlMAQObYZgAAAAxJREFUCNdjAAMOBgAAGAAJMwQHdQAAAABJRU5ErkJggg==");background-position:100% 1px;background-repeat:repeat-y}.jstree-default-small.jstree-rtl .jstree-last{background:transparent}.jstree-default-small.jstree-rtl .jstree-open>.jstree-ocl{background-position:-135px -39px}.jstree-default-small.jstree-rtl .jstree-closed>.jstree-ocl{background-position:-103px -39px}.jstree-default-small.jstree-rtl .jstree-leaf>.jstree-ocl{background-position:-71px -39px}.jstree-default-small.jstree-rtl>.jstree-no-dots .jstree-node,.jstree-default-small.jstree-rtl>.jstree-no-dots .jstree-leaf>.jstree-ocl{background:transparent}.jstree-default-small.jstree-rtl>.jstree-no-dots .jstree-open>.jstree-ocl{background-position:-39px -39px}.jstree-default-small.jstree-rtl>.jstree-no-dots .jstree-closed>.jstree-ocl{background-position:-7px -39px}.jstree-default-small .jstree-themeicon-custom{background-color:transparent;background-image:none;background-position:0 0}.jstree-default-small>.jstree-container-ul .jstree-loading>.jstree-ocl{background:url("throbber.gif") center center no-repeat}.jstree-default-small .jstree-file{background:url("32px.png") -103px -71px no-repeat}.jstree-default-small .jstree-folder{background:url("32px.png") -263px -7px no-repeat}.jstree-default-small>.jstree-container-ul>.jstree-node{margin-left:0;margin-right:0}#jstree-dnd.jstree-default-small{line-height:18px;padding:0 4px}#jstree-dnd.jstree-default-small .jstree-ok,#jstree-dnd.jstree-default-small .jstree-er{background-image:url("32px.png");background-repeat:no-repeat;background-color:transparent}#jstree-dnd.jstree-default-small i{background:transparent;width:18px;height:18px;line-height:18px}#jstree-dnd.jstree-default-small .jstree-ok{background-position:-7px -71px}#jstree-dnd.jstree-default-small .jstree-er{background-position:-39px -71px}.jstree-default-small .jstree-ellipsis{overflow:hidden}.jstree-default-small .jstree-ellipsis .jstree-anchor{width:calc(100% - 23px);text-overflow:ellipsis;overflow:hidden}.jstree-default-small.jstree-rtl .jstree-node{background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABIAAAACAQMAAABv1h6PAAAABlBMVEUAAAAdHRvEkCwcAAAAAXRSTlMAQObYZgAAAAxJREFUCNdjAAMHBgAAiABBI4gz9AAAAABJRU5ErkJggg==")}.jstree-default-small.jstree-rtl .jstree-last{background:transparent}.jstree-default-large .jstree-node{min-height:32px;line-height:32px;margin-left:32px;min-width:32px}.jstree-default-large .jstree-anchor{line-height:32px;height:32px}.jstree-default-large .jstree-icon{width:32px;height:32px;line-height:32px}.jstree-default-large .jstree-icon:empty{width:32px;height:32px;line-height:32px}.jstree-default-large.jstree-rtl .jstree-node{margin-right:32px}.jstree-default-large .jstree-wholerow{height:32px}.jstree-default-large .jstree-node,.jstree-default-large .jstree-icon{background-image:url("32px.png")}.jstree-default-large .jstree-node{background-position:-288px 0;background-repeat:repeat-y}.jstree-default-large .jstree-last{background:transparent}.jstree-default-large .jstree-open>.jstree-ocl{background-position:-128px 0}.jstree-default-large .jstree-closed>.jstree-ocl{background-position:-96px 0}.jstree-default-large .jstree-leaf>.jstree-ocl{background-position:-64px 0}.jstree-default-large .jstree-themeicon{background-position:-256px 0}.jstree-default-large>.jstree-no-dots .jstree-node,.jstree-default-large>.jstree-no-dots .jstree-leaf>.jstree-ocl{background:transparent}.jstree-default-large>.jstree-no-dots .jstree-open>.jstree-ocl{background-position:-32px 0}.jstree-default-large>.jstree-no-dots .jstree-closed>.jstree-ocl{background-position:0 0}.jstree-default-large .jstree-disabled{background:transparent}.jstree-default-large .jstree-disabled.jstree-hovered{background:transparent}.jstree-default-large .jstree-disabled.jstree-clicked{background:#efefef}.jstree-default-large .jstree-checkbox{background-position:-160px 0}.jstree-default-large .jstree-checkbox:hover{background-position:-160px -32px}.jstree-default-large.jstree-checkbox-selection .jstree-clicked>.jstree-checkbox,.jstree-default-large .jstree-checked>.jstree-checkbox{background-position:-224px 0}.jstree-default-large.jstree-checkbox-selection .jstree-clicked>.jstree-checkbox:hover,.jstree-default-large .jstree-checked>.jstree-checkbox:hover{background-position:-224px -32px}.jstree-default-large .jstree-anchor>.jstree-undetermined{background-position:-192px 0}.jstree-default-large .jstree-anchor>.jstree-undetermined:hover{background-position:-192px -32px}.jstree-default-large .jstree-checkbox-disabled{opacity:.8;filter:url("data:image/svg+xml;utf8,<svg xmlns=\'http://www.w3.org/2000/svg\'><filter id=\'jstree-grayscale\'><feColorMatrix type=\'matrix\' values=\'0.3333 0.3333 0.3333 0 0 0.3333 0.3333 0.3333 0 0 0.3333 0.3333 0.3333 0 0 0 0 0 1 0\'/></filter></svg>#jstree-grayscale");filter:gray;-webkit-filter:grayscale(100%)}.jstree-default-large>.jstree-striped{background-size:auto 64px}.jstree-default-large.jstree-rtl .jstree-node{background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAACAQMAAAB49I5GAAAABlBMVEUAAAAdHRvEkCwcAAAAAXRSTlMAQObYZgAAAAxJREFUCNdjAAMOBgAAGAAJMwQHdQAAAABJRU5ErkJggg==");background-position:100% 1px;background-repeat:repeat-y}.jstree-default-large.jstree-rtl .jstree-last{background:transparent}.jstree-default-large.jstree-rtl .jstree-open>.jstree-ocl{background-position:-128px -32px}.jstree-default-large.jstree-rtl .jstree-closed>.jstree-ocl{background-position:-96px -32px}.jstree-default-large.jstree-rtl .jstree-leaf>.jstree-ocl{background-position:-64px -32px}.jstree-default-large.jstree-rtl>.jstree-no-dots .jstree-node,.jstree-default-large.jstree-rtl>.jstree-no-dots .jstree-leaf>.jstree-ocl{background:transparent}.jstree-default-large.jstree-rtl>.jstree-no-dots .jstree-open>.jstree-ocl{background-position:-32px -32px}.jstree-default-large.jstree-rtl>.jstree-no-dots .jstree-closed>.jstree-ocl{background-position:0 -32px}.jstree-default-large .jstree-themeicon-custom{background-color:transparent;background-image:none;background-position:0 0}.jstree-default-large>.jstree-container-ul .jstree-loading>.jstree-ocl{background:url("throbber.gif") center center no-repeat}.jstree-default-large .jstree-file{background:url("32px.png") -96px -64px no-repeat}.jstree-default-large .jstree-folder{background:url("32px.png") -256px 0 no-repeat}.jstree-default-large>.jstree-container-ul>.jstree-node{margin-left:0;margin-right:0}#jstree-dnd.jstree-default-large{line-height:32px;padding:0 4px}#jstree-dnd.jstree-default-large .jstree-ok,#jstree-dnd.jstree-default-large .jstree-er{background-image:url("32px.png");background-repeat:no-repeat;background-color:transparent}#jstree-dnd.jstree-default-large i{background:transparent;width:32px;height:32px;line-height:32px}#jstree-dnd.jstree-default-large .jstree-ok{background-position:0 -64px}#jstree-dnd.jstree-default-large .jstree-er{background-position:-32px -64px}.jstree-default-large .jstree-ellipsis{overflow:hidden}.jstree-default-large .jstree-ellipsis .jstree-anchor{width:calc(100% - 37px);text-overflow:ellipsis;overflow:hidden}.jstree-default-large.jstree-rtl .jstree-node{background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAACAQMAAAAD0EyKAAAABlBMVEUAAAAdHRvEkCwcAAAAAXRSTlMAQObYZgAAAAxJREFUCNdjgIIGBgABCgCBvVLXcAAAAABJRU5ErkJggg==")}.jstree-default-large.jstree-rtl .jstree-last{background:transparent}@media (max-width:768px){#jstree-dnd.jstree-dnd-responsive{line-height:40px;font-weight:bold;font-size:1.1em;text-shadow:1px 1px white}#jstree-dnd.jstree-dnd-responsive>i{background:transparent;width:40px;height:40px}#jstree-dnd.jstree-dnd-responsive>.jstree-ok{background-image:url("40px.png");background-position:0 -200px;background-size:120px 240px}#jstree-dnd.jstree-dnd-responsive>.jstree-er{background-image:url("40px.png");background-position:-40px -200px;background-size:120px 240px}#jstree-marker.jstree-dnd-responsive{border-left-width:10px;border-top-width:10px;border-bottom-width:10px;margin-top:-10px}}@media (max-width:768px){.jstree-default-responsive .jstree-icon{background-image:url("40px.png")}.jstree-default-responsive .jstree-node,.jstree-default-responsive .jstree-leaf>.jstree-ocl{background:transparent}.jstree-default-responsive .jstree-node{min-height:40px;line-height:40px;margin-left:40px;min-width:40px;white-space:nowrap}.jstree-default-responsive .jstree-anchor{line-height:40px;height:40px}.jstree-default-responsive .jstree-icon,.jstree-default-responsive .jstree-icon:empty{width:40px;height:40px;line-height:40px}.jstree-default-responsive>.jstree-container-ul>.jstree-node{margin-left:0}.jstree-default-responsive.jstree-rtl .jstree-node{margin-left:0;margin-right:40px;background:transparent}.jstree-default-responsive.jstree-rtl .jstree-container-ul>.jstree-node{margin-right:0}.jstree-default-responsive .jstree-ocl,.jstree-default-responsive .jstree-themeicon,.jstree-default-responsive .jstree-checkbox{background-size:120px 240px}.jstree-default-responsive .jstree-leaf>.jstree-ocl,.jstree-default-responsive.jstree-rtl .jstree-leaf>.jstree-ocl{background:transparent}.jstree-default-responsive .jstree-open>.jstree-ocl{background-position:0 0 !important}.jstree-default-responsive .jstree-closed>.jstree-ocl{background-position:0 -40px !important}.jstree-default-responsive.jstree-rtl .jstree-closed>.jstree-ocl{background-position:-40px 0 !important}.jstree-default-responsive .jstree-themeicon{background-position:-40px -40px}.jstree-default-responsive .jstree-checkbox,.jstree-default-responsive .jstree-checkbox:hover{background-position:-40px -80px}.jstree-default-responsive.jstree-checkbox-selection .jstree-clicked>.jstree-checkbox,.jstree-default-responsive.jstree-checkbox-selection .jstree-clicked>.jstree-checkbox:hover,.jstree-default-responsive .jstree-checked>.jstree-checkbox,.jstree-default-responsive .jstree-checked>.jstree-checkbox:hover{background-position:0 -80px}.jstree-default-responsive .jstree-anchor>.jstree-undetermined,.jstree-default-responsive .jstree-anchor>.jstree-undetermined:hover{background-position:0 -120px}.jstree-default-responsive .jstree-anchor{font-weight:bold;font-size:1.1em;text-shadow:1px 1px white}.jstree-default-responsive>.jstree-striped{background:transparent}.jstree-default-responsive .jstree-wholerow{border-top:1px solid rgba(255,255,255,0.7);border-bottom:1px solid rgba(64,64,64,0.2);background:#ebebeb;height:40px}.jstree-default-responsive .jstree-wholerow-hovered{background:#e7f4f9}.jstree-default-responsive .jstree-wholerow-clicked{background:#beebff}.jstree-default-responsive .jstree-children .jstree-last>.jstree-wholerow{box-shadow:inset 0 -6px 3px -5px #666666}.jstree-default-responsive .jstree-children .jstree-open>.jstree-wholerow{box-shadow:inset 0 6px 3px -5px #666666;border-top:0}.jstree-default-responsive .jstree-children .jstree-open+.jstree-open{box-shadow:none}.jstree-default-responsive .jstree-node,.jstree-default-responsive .jstree-icon,.jstree-default-responsive .jstree-node>.jstree-ocl,.jstree-default-responsive .jstree-themeicon,.jstree-default-responsive .jstree-checkbox{background-image:url("40px.png");background-size:120px 240px}.jstree-default-responsive .jstree-node{background-position:-80px 0;background-repeat:repeat-y}.jstree-default-responsive .jstree-last{background:transparent}.jstree-default-responsive .jstree-leaf>.jstree-ocl{background-position:-40px -120px}.jstree-default-responsive .jstree-last>.jstree-ocl{background-position:-40px -160px}.jstree-default-responsive .jstree-themeicon-custom{background-color:transparent;background-image:none;background-position:0 0}.jstree-default-responsive .jstree-file{background:url("40px.png") 0 -160px no-repeat;background-size:120px 240px}.jstree-default-responsive .jstree-folder{background:url("40px.png") -40px -40px no-repeat;background-size:120px 240px}.jstree-default-responsive>.jstree-container-ul>.jstree-node{margin-left:0;margin-right:0}}admin/js/jstree/dist/themes/default/throbber.gif000064400000002670151327705670015740 0ustar00GIF89a������྾�zzzXXX666������$$$hhhFFF������!�NETSCAPE2.0!�Created with ajaxload.info!�
,@p�ɗ�2uJ�:�S"�L�$�cB�J����"S�P(	G��%��$Bʄ`��d��c���ד!L�XwVrP5�g��*�a��	:1
o!Y3�  �!�
,]��Ȓ�N&�A�IG�8�R�(Pp�'�aE�I��A0[�֣� X���X@���7i0[��h$
��(�,�UC��%s s�j#!�
,Z���X.�TJG&9F�%��TE�5�0H�р���=M ��`�R.�� �|�
�V�}��������� ���9���S"!�
,\��)���g�4��-�&C�� !���%V����1P�%����
�"p�r�eQ�(��`,P	��d�}��BIb��&�-��!�
,Eɉ�XY
���!(^��HqX^OF�a�J��D����B�rɔ>C�@�9N�"BkJf��PE!�
,k���Ơإ&���%� �������Ј`G�0��1HL��8�`��U
�`` ��@�,���A6O	0(0&��B��fJPh
#il�~!�
,[����X	���xJH2>L�Y���H�-	t�L�Y�&�$��X��L�q8NC#��<1a�x$n�S��x\y�ĚH�;
�1n8�)!�
,V�I!ǐM�Z�X��]��$�&PNC8��H�����n�����@�5��H+k��±�
c:c�
��WJ q86
��ڡ�4�c!�
,]��R�X�'�&\�T�u	�a�e
��,x�/E�(
'���Q0�!�P�M��Q8x�Ƃ�(��v8�z��"�~>� !�6J9!�
,Z���R��ji��Z�m�&��N
2\TI������Q��J���r�&�Ł)Q�
FÁ08
IC00�-Lsp���f`u0�5��E!�
,X��I_�x&Q��ER���
d�M�ZEӉ��g��G @��D���U���p>�"��,$�d 7�`�&H��&���!�
,Z���������F�|R�E�	�i㕰�Y+!� ��@@P����50xp2�#�i �N�0lN��$0@H��N}X<̏a}q���/)�D;admin/js/jstree/dist/themes/default/40px.png000064400000004252151327705670014741 0ustar00�PNG


IHDR���*�EPLTE333������333333���333333333333333���333333333333333333333333333<<<���tRNS
!PY\]����c�7�IDATx���[��6���m�M�����S{���!�XŸov���,+�������v0`��0`�},�=��H�{jb�{zb�WIl���̓^�u�ʫ!V�P8|P�*�σCȫJ?;�=/>
�э���8����G�{\��{�O�ޣ�&ޓ�e_��c�6�s�uo��#�F�S�m���V�3�Gy�^q3�.8��W��~�{5�'̲W����fȫZz�jM�x���j^��%�Bl'���iiz�?�^�-M�u���^á���d�v^Z�ktzx+���ז4�6S<�i;�I<�Ĭ�4�v��~"^{q�R����4�R�˥m����Z<@��xE-�-�W�6^��xť�K�0`��0`��0`�����},�]��T�w��a���`�¢�R�V-��H�nq�}�R�g��:�u
|�ag��+�p�k�{ێ@�/i�b�A˺X��d^�x�y,)�}���������g��'�������.�gĀ���x,xZ2y`zH��R�X'i�xm�Xg�[ja1��R����ĕJ��ќ���
}NN,����Y����t��a7
0`��0`��0`��0`����	q��邵7j��&��wA��B>
����g�}���u�~6�IK\��gӽ���޺~6�-�4x�������Wv��?o
~�WE����o��{�:���;n�7���^�ݣ�C���S��OMw��WG�o�嗇�"g�%�X�|4�zI��Z�g5>K'�Z.�ܤ����$��X:�����D���l)}__yzx8I�v>��GHvVO���Z^��87��W_��Ԣ��e~1M}y�r����q��%�E(֋Z�ˌ��-��Ɍ���h+�7�Fq)`��0`��0`��0`���0�vE(�Mˌ����.�n^jܾ���
ݦ��(�7	��b�|�� �x���sw�Xv��wM0`��<
X;�0`����6M;N"~����ӆ[.oA|����Z�+[�
W�v+����i�0`��0`��7� `��0`��{'Jd�����_�O�0�O�8�x p�e4p�28�:8|�9��$�R�3�N&?}�/�1[�����ϟ��~����h�<4��~Z��F;À0`��0`���F_l�Gw
0`��0`�����0`��G�o{�g�x,����0`��"2�����p�ED$|Ȣo"2�Oe�+"!���c�ٱ‡�,"2=e��/~q���σ-�mB�c~�t޾n�'�����Q����	^�|���m��e��=�m��%޾n����{�XD6=���i{�>��ƫ�N7�w/ow陶��v޼8��g��f�K��$8���� 8.τR��o�G3N�<����{jǪ)}4`������3y}g�l�Y'w���q�V%v���8Ԫ���=�_�%� �y-��p�n��%je�����Q���|�D�W/�6�M߿sU�`)�����o��c��s4oH��<�ofP}��f��Λ�tWW��9rpԍ\�mJc��h�j�[eAz���d{8�6�%/�xR��8�J��s4$K.�g�G]�F۬e�5G]�G5���x�Q�*¯��ƻ?�+�k�d�����yW`ɀ�ÿ�X�E�$�,"�Җ�fK�m�s��F��/=���Sƣ4��	p�h�`���E+�M^zmd-���ˑ�t7���J�Xy���\��kK�R���e~��W">Օ#�tҲ�>�,��
�JZ�*���x=ׁ9����`)]ϕ�mҲ\��,�K=8s�H
,�7���Ï�\����׫����ǚ_�d�7�b8��3`?��F�>
0`��v�:Q�OBń�IEND�B`�admin/js/jstree/dist/themes/default/style.css000064400000075626151327705670015327 0ustar00/* jsTree default theme */
.jstree-node,
.jstree-children,
.jstree-container-ul {
  display: block;
  margin: 0;
  padding: 0;
  list-style-type: none;
  list-style-image: none;
}
.jstree-node {
  white-space: nowrap;
}
.jstree-anchor {
  display: inline-block;
  color: black;
  white-space: nowrap;
  padding: 0 4px 0 1px;
  margin: 0;
  vertical-align: top;
}
.jstree-anchor:focus {
  outline: 0;
}
.jstree-anchor,
.jstree-anchor:link,
.jstree-anchor:visited,
.jstree-anchor:hover,
.jstree-anchor:active {
  text-decoration: none;
  color: inherit;
}
.jstree-icon {
  display: inline-block;
  text-decoration: none;
  margin: 0;
  padding: 0;
  vertical-align: top;
  text-align: center;
}
.jstree-icon:empty {
  display: inline-block;
  text-decoration: none;
  margin: 0;
  padding: 0;
  vertical-align: top;
  text-align: center;
}
.jstree-ocl {
  cursor: pointer;
}
.jstree-leaf > .jstree-ocl {
  cursor: default;
}
.jstree .jstree-open > .jstree-children {
  display: block;
}
.jstree .jstree-closed > .jstree-children,
.jstree .jstree-leaf > .jstree-children {
  display: none;
}
.jstree-anchor > .jstree-themeicon {
  margin-right: 2px;
}
.jstree-no-icons .jstree-themeicon,
.jstree-anchor > .jstree-themeicon-hidden {
  display: none;
}
.jstree-hidden,
.jstree-node.jstree-hidden {
  display: none;
}
.jstree-rtl .jstree-anchor {
  padding: 0 1px 0 4px;
}
.jstree-rtl .jstree-anchor > .jstree-themeicon {
  margin-left: 2px;
  margin-right: 0;
}
.jstree-rtl .jstree-node {
  margin-left: 0;
}
.jstree-rtl .jstree-container-ul > .jstree-node {
  margin-right: 0;
}
.jstree-wholerow-ul {
  position: relative;
  display: inline-block;
  min-width: 100%;
}
.jstree-wholerow-ul .jstree-leaf > .jstree-ocl {
  cursor: pointer;
}
.jstree-wholerow-ul .jstree-anchor,
.jstree-wholerow-ul .jstree-icon {
  position: relative;
}
.jstree-wholerow-ul .jstree-wholerow {
  width: 100%;
  cursor: pointer;
  position: absolute;
  left: 0;
  -webkit-user-select: none;
  -moz-user-select: none;
  -ms-user-select: none;
  user-select: none;
}
.jstree-contextmenu .jstree-anchor {
  -webkit-user-select: none;
  /* disable selection/Copy of UIWebView */
  -webkit-touch-callout: none;
  /* disable the IOS popup when long-press on a link */
}
.vakata-context {
  display: none;
}
.vakata-context,
.vakata-context ul {
  margin: 0;
  padding: 2px;
  position: absolute;
  background: #f5f5f5;
  border: 1px solid #979797;
  box-shadow: 2px 2px 2px #999999;
}
.vakata-context ul {
  list-style: none;
  left: 100%;
  margin-top: -2.7em;
  margin-left: -4px;
}
.vakata-context .vakata-context-right ul {
  left: auto;
  right: 100%;
  margin-left: auto;
  margin-right: -4px;
}
.vakata-context li {
  list-style: none;
}
.vakata-context li > a {
  display: block;
  padding: 0 2em 0 2em;
  text-decoration: none;
  width: auto;
  color: black;
  white-space: nowrap;
  line-height: 2.4em;
  text-shadow: 1px 1px 0 white;
  border-radius: 1px;
}
.vakata-context li > a:hover {
  position: relative;
  background-color: #e8eff7;
  box-shadow: 0 0 2px #0a6aa1;
}
.vakata-context li > a.vakata-context-parent {
  background-image: url("data:image/gif;base64,R0lGODlhCwAHAIAAACgoKP///yH5BAEAAAEALAAAAAALAAcAAAIORI4JlrqN1oMSnmmZDQUAOw==");
  background-position: right center;
  background-repeat: no-repeat;
}
.vakata-context li > a:focus {
  outline: 0;
}
.vakata-context .vakata-context-hover > a {
  position: relative;
  background-color: #e8eff7;
  box-shadow: 0 0 2px #0a6aa1;
}
.vakata-context .vakata-context-separator > a,
.vakata-context .vakata-context-separator > a:hover {
  background: white;
  border: 0;
  border-top: 1px solid #e2e3e3;
  height: 1px;
  min-height: 1px;
  max-height: 1px;
  padding: 0;
  margin: 0 0 0 2.4em;
  border-left: 1px solid #e0e0e0;
  text-shadow: 0 0 0 transparent;
  box-shadow: 0 0 0 transparent;
  border-radius: 0;
}
.vakata-context .vakata-contextmenu-disabled a,
.vakata-context .vakata-contextmenu-disabled a:hover {
  color: silver;
  background-color: transparent;
  border: 0;
  box-shadow: 0 0 0;
}
.vakata-context .vakata-contextmenu-disabled > a > i {
  filter: grayscale(100%);
}
.vakata-context li > a > i {
  text-decoration: none;
  display: inline-block;
  width: 2.4em;
  height: 2.4em;
  background: transparent;
  margin: 0 0 0 -2em;
  vertical-align: top;
  text-align: center;
  line-height: 2.4em;
}
.vakata-context li > a > i:empty {
  width: 2.4em;
  line-height: 2.4em;
}
.vakata-context li > a .vakata-contextmenu-sep {
  display: inline-block;
  width: 1px;
  height: 2.4em;
  background: white;
  margin: 0 0.5em 0 0;
  border-left: 1px solid #e2e3e3;
}
.vakata-context .vakata-contextmenu-shortcut {
  font-size: 0.8em;
  color: silver;
  opacity: 0.5;
  display: none;
}
.vakata-context-rtl ul {
  left: auto;
  right: 100%;
  margin-left: auto;
  margin-right: -4px;
}
.vakata-context-rtl li > a.vakata-context-parent {
  background-image: url("data:image/gif;base64,R0lGODlhCwAHAIAAACgoKP///yH5BAEAAAEALAAAAAALAAcAAAINjI+AC7rWHIsPtmoxLAA7");
  background-position: left center;
  background-repeat: no-repeat;
}
.vakata-context-rtl .vakata-context-separator > a {
  margin: 0 2.4em 0 0;
  border-left: 0;
  border-right: 1px solid #e2e3e3;
}
.vakata-context-rtl .vakata-context-left ul {
  right: auto;
  left: 100%;
  margin-left: -4px;
  margin-right: auto;
}
.vakata-context-rtl li > a > i {
  margin: 0 -2em 0 0;
}
.vakata-context-rtl li > a .vakata-contextmenu-sep {
  margin: 0 0 0 0.5em;
  border-left-color: white;
  background: #e2e3e3;
}
#jstree-marker {
  position: absolute;
  top: 0;
  left: 0;
  margin: -5px 0 0 0;
  padding: 0;
  border-right: 0;
  border-top: 5px solid transparent;
  border-bottom: 5px solid transparent;
  border-left: 5px solid;
  width: 0;
  height: 0;
  font-size: 0;
  line-height: 0;
}
#jstree-dnd {
  line-height: 16px;
  margin: 0;
  padding: 4px;
}
#jstree-dnd .jstree-icon,
#jstree-dnd .jstree-copy {
  display: inline-block;
  text-decoration: none;
  margin: 0 2px 0 0;
  padding: 0;
  width: 16px;
  height: 16px;
}
#jstree-dnd .jstree-ok {
  background: green;
}
#jstree-dnd .jstree-er {
  background: red;
}
#jstree-dnd .jstree-copy {
  margin: 0 2px 0 2px;
}
.jstree-default .jstree-node,
.jstree-default .jstree-icon {
  background-repeat: no-repeat;
  background-color: transparent;
}
.jstree-default .jstree-anchor,
.jstree-default .jstree-animated,
.jstree-default .jstree-wholerow {
  transition: background-color 0.15s, box-shadow 0.15s;
}
.jstree-default .jstree-hovered {
  background: #e7f4f9;
  border-radius: 2px;
  box-shadow: inset 0 0 1px #cccccc;
}
.jstree-default .jstree-context {
  background: #e7f4f9;
  border-radius: 2px;
  box-shadow: inset 0 0 1px #cccccc;
}
.jstree-default .jstree-clicked {
  background: #beebff;
  border-radius: 2px;
  box-shadow: inset 0 0 1px #999999;
}
.jstree-default .jstree-no-icons .jstree-anchor > .jstree-themeicon {
  display: none;
}
.jstree-default .jstree-disabled {
  background: transparent;
  color: #666666;
}
.jstree-default .jstree-disabled.jstree-hovered {
  background: transparent;
  box-shadow: none;
}
.jstree-default .jstree-disabled.jstree-clicked {
  background: #efefef;
}
.jstree-default .jstree-disabled > .jstree-icon {
  opacity: 0.8;
  filter: url("data:image/svg+xml;utf8,<svg xmlns=\'http://www.w3.org/2000/svg\'><filter id=\'jstree-grayscale\'><feColorMatrix type=\'matrix\' values=\'0.3333 0.3333 0.3333 0 0 0.3333 0.3333 0.3333 0 0 0.3333 0.3333 0.3333 0 0 0 0 0 1 0\'/></filter></svg>#jstree-grayscale");
  /* Firefox 10+ */
  filter: gray;
  /* IE6-9 */
  -webkit-filter: grayscale(100%);
  /* Chrome 19+ & Safari 6+ */
}
.jstree-default .jstree-search {
  font-style: italic;
  color: #8b0000;
  font-weight: bold;
}
.jstree-default .jstree-no-checkboxes .jstree-checkbox {
  display: none !important;
}
.jstree-default.jstree-checkbox-no-clicked .jstree-clicked {
  background: transparent;
  box-shadow: none;
}
.jstree-default.jstree-checkbox-no-clicked .jstree-clicked.jstree-hovered {
  background: #e7f4f9;
}
.jstree-default.jstree-checkbox-no-clicked > .jstree-wholerow-ul .jstree-wholerow-clicked {
  background: transparent;
}
.jstree-default.jstree-checkbox-no-clicked > .jstree-wholerow-ul .jstree-wholerow-clicked.jstree-wholerow-hovered {
  background: #e7f4f9;
}
.jstree-default > .jstree-striped {
  min-width: 100%;
  display: inline-block;
  background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAAkCAMAAAB/qqA+AAAABlBMVEUAAAAAAAClZ7nPAAAAAnRSTlMNAMM9s3UAAAAXSURBVHjajcEBAQAAAIKg/H/aCQZ70AUBjAATb6YPDgAAAABJRU5ErkJggg==") left top repeat;
}
.jstree-default > .jstree-wholerow-ul .jstree-hovered,
.jstree-default > .jstree-wholerow-ul .jstree-clicked {
  background: transparent;
  box-shadow: none;
  border-radius: 0;
}
.jstree-default .jstree-wholerow {
  -moz-box-sizing: border-box;
  -webkit-box-sizing: border-box;
  box-sizing: border-box;
}
.jstree-default .jstree-wholerow-hovered {
  background: #e7f4f9;
}
.jstree-default .jstree-wholerow-clicked {
  background: #beebff;
  background: -webkit-linear-gradient(top, #beebff 0%, #a8e4ff 100%);
  background: linear-gradient(to bottom, #beebff 0%, #a8e4ff 100%);
}
.jstree-default .jstree-node {
  min-height: 24px;
  line-height: 24px;
  margin-left: 24px;
  min-width: 24px;
}
.jstree-default .jstree-anchor {
  line-height: 24px;
  height: 24px;
}
.jstree-default .jstree-icon {
  width: 24px;
  height: 24px;
  line-height: 24px;
}
.jstree-default .jstree-icon:empty {
  width: 24px;
  height: 24px;
  line-height: 24px;
}
.jstree-default.jstree-rtl .jstree-node {
  margin-right: 24px;
}
.jstree-default .jstree-wholerow {
  height: 24px;
}
.jstree-default .jstree-node,
.jstree-default .jstree-icon {
  background-image: url("32px.png");
}
.jstree-default .jstree-node {
  background-position: -292px -4px;
  background-repeat: repeat-y;
}
.jstree-default .jstree-last {
  background: transparent;
}
.jstree-default .jstree-open > .jstree-ocl {
  background-position: -132px -4px;
}
.jstree-default .jstree-closed > .jstree-ocl {
  background-position: -100px -4px;
}
.jstree-default .jstree-leaf > .jstree-ocl {
  background-position: -68px -4px;
}
.jstree-default .jstree-themeicon {
  background-position: -260px -4px;
}
.jstree-default > .jstree-no-dots .jstree-node,
.jstree-default > .jstree-no-dots .jstree-leaf > .jstree-ocl {
  background: transparent;
}
.jstree-default > .jstree-no-dots .jstree-open > .jstree-ocl {
  background-position: -36px -4px;
}
.jstree-default > .jstree-no-dots .jstree-closed > .jstree-ocl {
  background-position: -4px -4px;
}
.jstree-default .jstree-disabled {
  background: transparent;
}
.jstree-default .jstree-disabled.jstree-hovered {
  background: transparent;
}
.jstree-default .jstree-disabled.jstree-clicked {
  background: #efefef;
}
.jstree-default .jstree-checkbox {
  background-position: -164px -4px;
}
.jstree-default .jstree-checkbox:hover {
  background-position: -164px -36px;
}
.jstree-default.jstree-checkbox-selection .jstree-clicked > .jstree-checkbox,
.jstree-default .jstree-checked > .jstree-checkbox {
  background-position: -228px -4px;
}
.jstree-default.jstree-checkbox-selection .jstree-clicked > .jstree-checkbox:hover,
.jstree-default .jstree-checked > .jstree-checkbox:hover {
  background-position: -228px -36px;
}
.jstree-default .jstree-anchor > .jstree-undetermined {
  background-position: -196px -4px;
}
.jstree-default .jstree-anchor > .jstree-undetermined:hover {
  background-position: -196px -36px;
}
.jstree-default .jstree-checkbox-disabled {
  opacity: 0.8;
  filter: url("data:image/svg+xml;utf8,<svg xmlns=\'http://www.w3.org/2000/svg\'><filter id=\'jstree-grayscale\'><feColorMatrix type=\'matrix\' values=\'0.3333 0.3333 0.3333 0 0 0.3333 0.3333 0.3333 0 0 0.3333 0.3333 0.3333 0 0 0 0 0 1 0\'/></filter></svg>#jstree-grayscale");
  /* Firefox 10+ */
  filter: gray;
  /* IE6-9 */
  -webkit-filter: grayscale(100%);
  /* Chrome 19+ & Safari 6+ */
}
.jstree-default > .jstree-striped {
  background-size: auto 48px;
}
.jstree-default.jstree-rtl .jstree-node {
  background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAACAQMAAAB49I5GAAAABlBMVEUAAAAdHRvEkCwcAAAAAXRSTlMAQObYZgAAAAxJREFUCNdjAAMOBgAAGAAJMwQHdQAAAABJRU5ErkJggg==");
  background-position: 100% 1px;
  background-repeat: repeat-y;
}
.jstree-default.jstree-rtl .jstree-last {
  background: transparent;
}
.jstree-default.jstree-rtl .jstree-open > .jstree-ocl {
  background-position: -132px -36px;
}
.jstree-default.jstree-rtl .jstree-closed > .jstree-ocl {
  background-position: -100px -36px;
}
.jstree-default.jstree-rtl .jstree-leaf > .jstree-ocl {
  background-position: -68px -36px;
}
.jstree-default.jstree-rtl > .jstree-no-dots .jstree-node,
.jstree-default.jstree-rtl > .jstree-no-dots .jstree-leaf > .jstree-ocl {
  background: transparent;
}
.jstree-default.jstree-rtl > .jstree-no-dots .jstree-open > .jstree-ocl {
  background-position: -36px -36px;
}
.jstree-default.jstree-rtl > .jstree-no-dots .jstree-closed > .jstree-ocl {
  background-position: -4px -36px;
}
.jstree-default .jstree-themeicon-custom {
  background-color: transparent;
  background-image: none;
  background-position: 0 0;
}
.jstree-default > .jstree-container-ul .jstree-loading > .jstree-ocl {
  background: url("throbber.gif") center center no-repeat;
}
.jstree-default .jstree-file {
  background: url("32px.png") -100px -68px no-repeat;
}
.jstree-default .jstree-folder {
  background: url("32px.png") -260px -4px no-repeat;
}
.jstree-default > .jstree-container-ul > .jstree-node {
  margin-left: 0;
  margin-right: 0;
}
#jstree-dnd.jstree-default {
  line-height: 24px;
  padding: 0 4px;
}
#jstree-dnd.jstree-default .jstree-ok,
#jstree-dnd.jstree-default .jstree-er {
  background-image: url("32px.png");
  background-repeat: no-repeat;
  background-color: transparent;
}
#jstree-dnd.jstree-default i {
  background: transparent;
  width: 24px;
  height: 24px;
  line-height: 24px;
}
#jstree-dnd.jstree-default .jstree-ok {
  background-position: -4px -68px;
}
#jstree-dnd.jstree-default .jstree-er {
  background-position: -36px -68px;
}
.jstree-default .jstree-ellipsis {
  overflow: hidden;
}
.jstree-default .jstree-ellipsis .jstree-anchor {
  width: calc(100% - 29px);
  text-overflow: ellipsis;
  overflow: hidden;
}
.jstree-default.jstree-rtl .jstree-node {
  background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAACAQMAAAB49I5GAAAABlBMVEUAAAAdHRvEkCwcAAAAAXRSTlMAQObYZgAAAAxJREFUCNdjAAMOBgAAGAAJMwQHdQAAAABJRU5ErkJggg==");
}
.jstree-default.jstree-rtl .jstree-last {
  background: transparent;
}
.jstree-default-small .jstree-node {
  min-height: 18px;
  line-height: 18px;
  margin-left: 18px;
  min-width: 18px;
}
.jstree-default-small .jstree-anchor {
  line-height: 18px;
  height: 18px;
}
.jstree-default-small .jstree-icon {
  width: 18px;
  height: 18px;
  line-height: 18px;
}
.jstree-default-small .jstree-icon:empty {
  width: 18px;
  height: 18px;
  line-height: 18px;
}
.jstree-default-small.jstree-rtl .jstree-node {
  margin-right: 18px;
}
.jstree-default-small .jstree-wholerow {
  height: 18px;
}
.jstree-default-small .jstree-node,
.jstree-default-small .jstree-icon {
  background-image: url("32px.png");
}
.jstree-default-small .jstree-node {
  background-position: -295px -7px;
  background-repeat: repeat-y;
}
.jstree-default-small .jstree-last {
  background: transparent;
}
.jstree-default-small .jstree-open > .jstree-ocl {
  background-position: -135px -7px;
}
.jstree-default-small .jstree-closed > .jstree-ocl {
  background-position: -103px -7px;
}
.jstree-default-small .jstree-leaf > .jstree-ocl {
  background-position: -71px -7px;
}
.jstree-default-small .jstree-themeicon {
  background-position: -263px -7px;
}
.jstree-default-small > .jstree-no-dots .jstree-node,
.jstree-default-small > .jstree-no-dots .jstree-leaf > .jstree-ocl {
  background: transparent;
}
.jstree-default-small > .jstree-no-dots .jstree-open > .jstree-ocl {
  background-position: -39px -7px;
}
.jstree-default-small > .jstree-no-dots .jstree-closed > .jstree-ocl {
  background-position: -7px -7px;
}
.jstree-default-small .jstree-disabled {
  background: transparent;
}
.jstree-default-small .jstree-disabled.jstree-hovered {
  background: transparent;
}
.jstree-default-small .jstree-disabled.jstree-clicked {
  background: #efefef;
}
.jstree-default-small .jstree-checkbox {
  background-position: -167px -7px;
}
.jstree-default-small .jstree-checkbox:hover {
  background-position: -167px -39px;
}
.jstree-default-small.jstree-checkbox-selection .jstree-clicked > .jstree-checkbox,
.jstree-default-small .jstree-checked > .jstree-checkbox {
  background-position: -231px -7px;
}
.jstree-default-small.jstree-checkbox-selection .jstree-clicked > .jstree-checkbox:hover,
.jstree-default-small .jstree-checked > .jstree-checkbox:hover {
  background-position: -231px -39px;
}
.jstree-default-small .jstree-anchor > .jstree-undetermined {
  background-position: -199px -7px;
}
.jstree-default-small .jstree-anchor > .jstree-undetermined:hover {
  background-position: -199px -39px;
}
.jstree-default-small .jstree-checkbox-disabled {
  opacity: 0.8;
  filter: url("data:image/svg+xml;utf8,<svg xmlns=\'http://www.w3.org/2000/svg\'><filter id=\'jstree-grayscale\'><feColorMatrix type=\'matrix\' values=\'0.3333 0.3333 0.3333 0 0 0.3333 0.3333 0.3333 0 0 0.3333 0.3333 0.3333 0 0 0 0 0 1 0\'/></filter></svg>#jstree-grayscale");
  /* Firefox 10+ */
  filter: gray;
  /* IE6-9 */
  -webkit-filter: grayscale(100%);
  /* Chrome 19+ & Safari 6+ */
}
.jstree-default-small > .jstree-striped {
  background-size: auto 36px;
}
.jstree-default-small.jstree-rtl .jstree-node {
  background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAACAQMAAAB49I5GAAAABlBMVEUAAAAdHRvEkCwcAAAAAXRSTlMAQObYZgAAAAxJREFUCNdjAAMOBgAAGAAJMwQHdQAAAABJRU5ErkJggg==");
  background-position: 100% 1px;
  background-repeat: repeat-y;
}
.jstree-default-small.jstree-rtl .jstree-last {
  background: transparent;
}
.jstree-default-small.jstree-rtl .jstree-open > .jstree-ocl {
  background-position: -135px -39px;
}
.jstree-default-small.jstree-rtl .jstree-closed > .jstree-ocl {
  background-position: -103px -39px;
}
.jstree-default-small.jstree-rtl .jstree-leaf > .jstree-ocl {
  background-position: -71px -39px;
}
.jstree-default-small.jstree-rtl > .jstree-no-dots .jstree-node,
.jstree-default-small.jstree-rtl > .jstree-no-dots .jstree-leaf > .jstree-ocl {
  background: transparent;
}
.jstree-default-small.jstree-rtl > .jstree-no-dots .jstree-open > .jstree-ocl {
  background-position: -39px -39px;
}
.jstree-default-small.jstree-rtl > .jstree-no-dots .jstree-closed > .jstree-ocl {
  background-position: -7px -39px;
}
.jstree-default-small .jstree-themeicon-custom {
  background-color: transparent;
  background-image: none;
  background-position: 0 0;
}
.jstree-default-small > .jstree-container-ul .jstree-loading > .jstree-ocl {
  background: url("throbber.gif") center center no-repeat;
}
.jstree-default-small .jstree-file {
  background: url("32px.png") -103px -71px no-repeat;
}
.jstree-default-small .jstree-folder {
  background: url("32px.png") -263px -7px no-repeat;
}
.jstree-default-small > .jstree-container-ul > .jstree-node {
  margin-left: 0;
  margin-right: 0;
}
#jstree-dnd.jstree-default-small {
  line-height: 18px;
  padding: 0 4px;
}
#jstree-dnd.jstree-default-small .jstree-ok,
#jstree-dnd.jstree-default-small .jstree-er {
  background-image: url("32px.png");
  background-repeat: no-repeat;
  background-color: transparent;
}
#jstree-dnd.jstree-default-small i {
  background: transparent;
  width: 18px;
  height: 18px;
  line-height: 18px;
}
#jstree-dnd.jstree-default-small .jstree-ok {
  background-position: -7px -71px;
}
#jstree-dnd.jstree-default-small .jstree-er {
  background-position: -39px -71px;
}
.jstree-default-small .jstree-ellipsis {
  overflow: hidden;
}
.jstree-default-small .jstree-ellipsis .jstree-anchor {
  width: calc(100% - 23px);
  text-overflow: ellipsis;
  overflow: hidden;
}
.jstree-default-small.jstree-rtl .jstree-node {
  background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABIAAAACAQMAAABv1h6PAAAABlBMVEUAAAAdHRvEkCwcAAAAAXRSTlMAQObYZgAAAAxJREFUCNdjAAMHBgAAiABBI4gz9AAAAABJRU5ErkJggg==");
}
.jstree-default-small.jstree-rtl .jstree-last {
  background: transparent;
}
.jstree-default-large .jstree-node {
  min-height: 32px;
  line-height: 32px;
  margin-left: 32px;
  min-width: 32px;
}
.jstree-default-large .jstree-anchor {
  line-height: 32px;
  height: 32px;
}
.jstree-default-large .jstree-icon {
  width: 32px;
  height: 32px;
  line-height: 32px;
}
.jstree-default-large .jstree-icon:empty {
  width: 32px;
  height: 32px;
  line-height: 32px;
}
.jstree-default-large.jstree-rtl .jstree-node {
  margin-right: 32px;
}
.jstree-default-large .jstree-wholerow {
  height: 32px;
}
.jstree-default-large .jstree-node,
.jstree-default-large .jstree-icon {
  background-image: url("32px.png");
}
.jstree-default-large .jstree-node {
  background-position: -288px 0px;
  background-repeat: repeat-y;
}
.jstree-default-large .jstree-last {
  background: transparent;
}
.jstree-default-large .jstree-open > .jstree-ocl {
  background-position: -128px 0px;
}
.jstree-default-large .jstree-closed > .jstree-ocl {
  background-position: -96px 0px;
}
.jstree-default-large .jstree-leaf > .jstree-ocl {
  background-position: -64px 0px;
}
.jstree-default-large .jstree-themeicon {
  background-position: -256px 0px;
}
.jstree-default-large > .jstree-no-dots .jstree-node,
.jstree-default-large > .jstree-no-dots .jstree-leaf > .jstree-ocl {
  background: transparent;
}
.jstree-default-large > .jstree-no-dots .jstree-open > .jstree-ocl {
  background-position: -32px 0px;
}
.jstree-default-large > .jstree-no-dots .jstree-closed > .jstree-ocl {
  background-position: 0px 0px;
}
.jstree-default-large .jstree-disabled {
  background: transparent;
}
.jstree-default-large .jstree-disabled.jstree-hovered {
  background: transparent;
}
.jstree-default-large .jstree-disabled.jstree-clicked {
  background: #efefef;
}
.jstree-default-large .jstree-checkbox {
  background-position: -160px 0px;
}
.jstree-default-large .jstree-checkbox:hover {
  background-position: -160px -32px;
}
.jstree-default-large.jstree-checkbox-selection .jstree-clicked > .jstree-checkbox,
.jstree-default-large .jstree-checked > .jstree-checkbox {
  background-position: -224px 0px;
}
.jstree-default-large.jstree-checkbox-selection .jstree-clicked > .jstree-checkbox:hover,
.jstree-default-large .jstree-checked > .jstree-checkbox:hover {
  background-position: -224px -32px;
}
.jstree-default-large .jstree-anchor > .jstree-undetermined {
  background-position: -192px 0px;
}
.jstree-default-large .jstree-anchor > .jstree-undetermined:hover {
  background-position: -192px -32px;
}
.jstree-default-large .jstree-checkbox-disabled {
  opacity: 0.8;
  filter: url("data:image/svg+xml;utf8,<svg xmlns=\'http://www.w3.org/2000/svg\'><filter id=\'jstree-grayscale\'><feColorMatrix type=\'matrix\' values=\'0.3333 0.3333 0.3333 0 0 0.3333 0.3333 0.3333 0 0 0.3333 0.3333 0.3333 0 0 0 0 0 1 0\'/></filter></svg>#jstree-grayscale");
  /* Firefox 10+ */
  filter: gray;
  /* IE6-9 */
  -webkit-filter: grayscale(100%);
  /* Chrome 19+ & Safari 6+ */
}
.jstree-default-large > .jstree-striped {
  background-size: auto 64px;
}
.jstree-default-large.jstree-rtl .jstree-node {
  background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAACAQMAAAB49I5GAAAABlBMVEUAAAAdHRvEkCwcAAAAAXRSTlMAQObYZgAAAAxJREFUCNdjAAMOBgAAGAAJMwQHdQAAAABJRU5ErkJggg==");
  background-position: 100% 1px;
  background-repeat: repeat-y;
}
.jstree-default-large.jstree-rtl .jstree-last {
  background: transparent;
}
.jstree-default-large.jstree-rtl .jstree-open > .jstree-ocl {
  background-position: -128px -32px;
}
.jstree-default-large.jstree-rtl .jstree-closed > .jstree-ocl {
  background-position: -96px -32px;
}
.jstree-default-large.jstree-rtl .jstree-leaf > .jstree-ocl {
  background-position: -64px -32px;
}
.jstree-default-large.jstree-rtl > .jstree-no-dots .jstree-node,
.jstree-default-large.jstree-rtl > .jstree-no-dots .jstree-leaf > .jstree-ocl {
  background: transparent;
}
.jstree-default-large.jstree-rtl > .jstree-no-dots .jstree-open > .jstree-ocl {
  background-position: -32px -32px;
}
.jstree-default-large.jstree-rtl > .jstree-no-dots .jstree-closed > .jstree-ocl {
  background-position: 0px -32px;
}
.jstree-default-large .jstree-themeicon-custom {
  background-color: transparent;
  background-image: none;
  background-position: 0 0;
}
.jstree-default-large > .jstree-container-ul .jstree-loading > .jstree-ocl {
  background: url("throbber.gif") center center no-repeat;
}
.jstree-default-large .jstree-file {
  background: url("32px.png") -96px -64px no-repeat;
}
.jstree-default-large .jstree-folder {
  background: url("32px.png") -256px 0px no-repeat;
}
.jstree-default-large > .jstree-container-ul > .jstree-node {
  margin-left: 0;
  margin-right: 0;
}
#jstree-dnd.jstree-default-large {
  line-height: 32px;
  padding: 0 4px;
}
#jstree-dnd.jstree-default-large .jstree-ok,
#jstree-dnd.jstree-default-large .jstree-er {
  background-image: url("32px.png");
  background-repeat: no-repeat;
  background-color: transparent;
}
#jstree-dnd.jstree-default-large i {
  background: transparent;
  width: 32px;
  height: 32px;
  line-height: 32px;
}
#jstree-dnd.jstree-default-large .jstree-ok {
  background-position: 0px -64px;
}
#jstree-dnd.jstree-default-large .jstree-er {
  background-position: -32px -64px;
}
.jstree-default-large .jstree-ellipsis {
  overflow: hidden;
}
.jstree-default-large .jstree-ellipsis .jstree-anchor {
  width: calc(100% - 37px);
  text-overflow: ellipsis;
  overflow: hidden;
}
.jstree-default-large.jstree-rtl .jstree-node {
  background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAACAQMAAAAD0EyKAAAABlBMVEUAAAAdHRvEkCwcAAAAAXRSTlMAQObYZgAAAAxJREFUCNdjgIIGBgABCgCBvVLXcAAAAABJRU5ErkJggg==");
}
.jstree-default-large.jstree-rtl .jstree-last {
  background: transparent;
}
@media (max-width: 768px) {
  #jstree-dnd.jstree-dnd-responsive {
    line-height: 40px;
    font-weight: bold;
    font-size: 1.1em;
    text-shadow: 1px 1px white;
  }
  #jstree-dnd.jstree-dnd-responsive > i {
    background: transparent;
    width: 40px;
    height: 40px;
  }
  #jstree-dnd.jstree-dnd-responsive > .jstree-ok {
    background-image: url("40px.png");
    background-position: 0 -200px;
    background-size: 120px 240px;
  }
  #jstree-dnd.jstree-dnd-responsive > .jstree-er {
    background-image: url("40px.png");
    background-position: -40px -200px;
    background-size: 120px 240px;
  }
  #jstree-marker.jstree-dnd-responsive {
    border-left-width: 10px;
    border-top-width: 10px;
    border-bottom-width: 10px;
    margin-top: -10px;
  }
}
@media (max-width: 768px) {
  .jstree-default-responsive {
    /*
	.jstree-open > .jstree-ocl,
	.jstree-closed > .jstree-ocl { border-radius:20px; background-color:white; }
	*/
  }
  .jstree-default-responsive .jstree-icon {
    background-image: url("40px.png");
  }
  .jstree-default-responsive .jstree-node,
  .jstree-default-responsive .jstree-leaf > .jstree-ocl {
    background: transparent;
  }
  .jstree-default-responsive .jstree-node {
    min-height: 40px;
    line-height: 40px;
    margin-left: 40px;
    min-width: 40px;
    white-space: nowrap;
  }
  .jstree-default-responsive .jstree-anchor {
    line-height: 40px;
    height: 40px;
  }
  .jstree-default-responsive .jstree-icon,
  .jstree-default-responsive .jstree-icon:empty {
    width: 40px;
    height: 40px;
    line-height: 40px;
  }
  .jstree-default-responsive > .jstree-container-ul > .jstree-node {
    margin-left: 0;
  }
  .jstree-default-responsive.jstree-rtl .jstree-node {
    margin-left: 0;
    margin-right: 40px;
    background: transparent;
  }
  .jstree-default-responsive.jstree-rtl .jstree-container-ul > .jstree-node {
    margin-right: 0;
  }
  .jstree-default-responsive .jstree-ocl,
  .jstree-default-responsive .jstree-themeicon,
  .jstree-default-responsive .jstree-checkbox {
    background-size: 120px 240px;
  }
  .jstree-default-responsive .jstree-leaf > .jstree-ocl,
  .jstree-default-responsive.jstree-rtl .jstree-leaf > .jstree-ocl {
    background: transparent;
  }
  .jstree-default-responsive .jstree-open > .jstree-ocl {
    background-position: 0 0 !important;
  }
  .jstree-default-responsive .jstree-closed > .jstree-ocl {
    background-position: 0 -40px !important;
  }
  .jstree-default-responsive.jstree-rtl .jstree-closed > .jstree-ocl {
    background-position: -40px 0 !important;
  }
  .jstree-default-responsive .jstree-themeicon {
    background-position: -40px -40px;
  }
  .jstree-default-responsive .jstree-checkbox,
  .jstree-default-responsive .jstree-checkbox:hover {
    background-position: -40px -80px;
  }
  .jstree-default-responsive.jstree-checkbox-selection .jstree-clicked > .jstree-checkbox,
  .jstree-default-responsive.jstree-checkbox-selection .jstree-clicked > .jstree-checkbox:hover,
  .jstree-default-responsive .jstree-checked > .jstree-checkbox,
  .jstree-default-responsive .jstree-checked > .jstree-checkbox:hover {
    background-position: 0 -80px;
  }
  .jstree-default-responsive .jstree-anchor > .jstree-undetermined,
  .jstree-default-responsive .jstree-anchor > .jstree-undetermined:hover {
    background-position: 0 -120px;
  }
  .jstree-default-responsive .jstree-anchor {
    font-weight: bold;
    font-size: 1.1em;
    text-shadow: 1px 1px white;
  }
  .jstree-default-responsive > .jstree-striped {
    background: transparent;
  }
  .jstree-default-responsive .jstree-wholerow {
    border-top: 1px solid rgba(255, 255, 255, 0.7);
    border-bottom: 1px solid rgba(64, 64, 64, 0.2);
    background: #ebebeb;
    height: 40px;
  }
  .jstree-default-responsive .jstree-wholerow-hovered {
    background: #e7f4f9;
  }
  .jstree-default-responsive .jstree-wholerow-clicked {
    background: #beebff;
  }
  .jstree-default-responsive .jstree-children .jstree-last > .jstree-wholerow {
    box-shadow: inset 0 -6px 3px -5px #666666;
  }
  .jstree-default-responsive .jstree-children .jstree-open > .jstree-wholerow {
    box-shadow: inset 0 6px 3px -5px #666666;
    border-top: 0;
  }
  .jstree-default-responsive .jstree-children .jstree-open + .jstree-open {
    box-shadow: none;
  }
  .jstree-default-responsive .jstree-node,
  .jstree-default-responsive .jstree-icon,
  .jstree-default-responsive .jstree-node > .jstree-ocl,
  .jstree-default-responsive .jstree-themeicon,
  .jstree-default-responsive .jstree-checkbox {
    background-image: url("40px.png");
    background-size: 120px 240px;
  }
  .jstree-default-responsive .jstree-node {
    background-position: -80px 0;
    background-repeat: repeat-y;
  }
  .jstree-default-responsive .jstree-last {
    background: transparent;
  }
  .jstree-default-responsive .jstree-leaf > .jstree-ocl {
    background-position: -40px -120px;
  }
  .jstree-default-responsive .jstree-last > .jstree-ocl {
    background-position: -40px -160px;
  }
  .jstree-default-responsive .jstree-themeicon-custom {
    background-color: transparent;
    background-image: none;
    background-position: 0 0;
  }
  .jstree-default-responsive .jstree-file {
    background: url("40px.png") 0 -160px no-repeat;
    background-size: 120px 240px;
  }
  .jstree-default-responsive .jstree-folder {
    background: url("40px.png") -40px -40px no-repeat;
    background-size: 120px 240px;
  }
  .jstree-default-responsive > .jstree-container-ul > .jstree-node {
    margin-left: 0;
    margin-right: 0;
  }
}
admin/js/jstree/dist/jstree.min.js000064400000417274151327705670013157 0ustar00/*! jsTree - v3.3.7 - 2018-11-06 - (MIT) */
!function(a){"use strict";"function"==typeof define&&define.amd?define(["jquery"],a):"undefined"!=typeof module&&module.exports?module.exports=a(require("jquery")):a(jQuery)}(function(a,b){"use strict";if(!a.jstree){var c=0,d=!1,e=!1,f=!1,g=[],h=a("script:last").attr("src"),i=window.document;a.jstree={version:"3.3.7",defaults:{plugins:[]},plugins:{},path:h&&-1!==h.indexOf("/")?h.replace(/\/[^\/]+$/,""):"",idregex:/[\\:&!^|()\[\]<>@*'+~#";.,=\- \/${}%?`]/g,root:"#"},a.jstree.create=function(b,d){var e=new a.jstree.core(++c),f=d;return d=a.extend(!0,{},a.jstree.defaults,d),f&&f.plugins&&(d.plugins=f.plugins),a.each(d.plugins,function(a,b){"core"!==a&&(e=e.plugin(b,d[b]))}),a(b).data("jstree",e),e.init(b,d),e},a.jstree.destroy=function(){a(".jstree:jstree").jstree("destroy"),a(i).off(".jstree")},a.jstree.core=function(a){this._id=a,this._cnt=0,this._wrk=null,this._data={core:{themes:{name:!1,dots:!1,icons:!1,ellipsis:!1},selected:[],last_error:{},working:!1,worker_queue:[],focused:null}}},a.jstree.reference=function(b){var c=null,d=null;if(!b||!b.id||b.tagName&&b.nodeType||(b=b.id),!d||!d.length)try{d=a(b)}catch(e){}if(!d||!d.length)try{d=a("#"+b.replace(a.jstree.idregex,"\\$&"))}catch(e){}return d&&d.length&&(d=d.closest(".jstree")).length&&(d=d.data("jstree"))?c=d:a(".jstree").each(function(){var d=a(this).data("jstree");return d&&d._model.data[b]?(c=d,!1):void 0}),c},a.fn.jstree=function(c){var d="string"==typeof c,e=Array.prototype.slice.call(arguments,1),f=null;return c!==!0||this.length?(this.each(function(){var g=a.jstree.reference(this),h=d&&g?g[c]:null;return f=d&&h?h.apply(g,e):null,g||d||c!==b&&!a.isPlainObject(c)||a.jstree.create(this,c),(g&&!d||c===!0)&&(f=g||!1),null!==f&&f!==b?!1:void 0}),null!==f&&f!==b?f:this):!1},a.expr.pseudos.jstree=a.expr.createPseudo(function(c){return function(c){return a(c).hasClass("jstree")&&a(c).data("jstree")!==b}}),a.jstree.defaults.core={data:!1,strings:!1,check_callback:!1,error:a.noop,animation:200,multiple:!0,themes:{name:!1,url:!1,dir:!1,dots:!0,icons:!0,ellipsis:!1,stripes:!1,variant:!1,responsive:!1},expand_selected_onload:!0,worker:!0,force_text:!1,dblclick_toggle:!0,loaded_state:!1,restore_focus:!0,keyboard:{"ctrl-space":function(b){b.type="click",a(b.currentTarget).trigger(b)},enter:function(b){b.type="click",a(b.currentTarget).trigger(b)},left:function(b){if(b.preventDefault(),this.is_open(b.currentTarget))this.close_node(b.currentTarget);else{var c=this.get_parent(b.currentTarget);c&&c.id!==a.jstree.root&&this.get_node(c,!0).children(".jstree-anchor").focus()}},up:function(a){a.preventDefault();var b=this.get_prev_dom(a.currentTarget);b&&b.length&&b.children(".jstree-anchor").focus()},right:function(b){if(b.preventDefault(),this.is_closed(b.currentTarget))this.open_node(b.currentTarget,function(a){this.get_node(a,!0).children(".jstree-anchor").focus()});else if(this.is_open(b.currentTarget)){var c=this.get_node(b.currentTarget,!0).children(".jstree-children")[0];c&&a(this._firstChild(c)).children(".jstree-anchor").focus()}},down:function(a){a.preventDefault();var b=this.get_next_dom(a.currentTarget);b&&b.length&&b.children(".jstree-anchor").focus()},"*":function(a){this.open_all()},home:function(b){b.preventDefault();var c=this._firstChild(this.get_container_ul()[0]);c&&a(c).children(".jstree-anchor").filter(":visible").focus()},end:function(a){a.preventDefault(),this.element.find(".jstree-anchor").filter(":visible").last().focus()},f2:function(a){a.preventDefault(),this.edit(a.currentTarget)}}},a.jstree.core.prototype={plugin:function(b,c){var d=a.jstree.plugins[b];return d?(this._data[b]={},d.prototype=this,new d(c,this)):this},init:function(b,c){this._model={data:{},changed:[],force_full_redraw:!1,redraw_timeout:!1,default_state:{loaded:!0,opened:!1,selected:!1,disabled:!1}},this._model.data[a.jstree.root]={id:a.jstree.root,parent:null,parents:[],children:[],children_d:[],state:{loaded:!1}},this.element=a(b).addClass("jstree jstree-"+this._id),this.settings=c,this._data.core.ready=!1,this._data.core.loaded=!1,this._data.core.rtl="rtl"===this.element.css("direction"),this.element[this._data.core.rtl?"addClass":"removeClass"]("jstree-rtl"),this.element.attr("role","tree"),this.settings.core.multiple&&this.element.attr("aria-multiselectable",!0),this.element.attr("tabindex")||this.element.attr("tabindex","0"),this.bind(),this.trigger("init"),this._data.core.original_container_html=this.element.find(" > ul > li").clone(!0),this._data.core.original_container_html.find("li").addBack().contents().filter(function(){return 3===this.nodeType&&(!this.nodeValue||/^\s+$/.test(this.nodeValue))}).remove(),this.element.html("<ul class='jstree-container-ul jstree-children' role='group'><li id='j"+this._id+"_loading' class='jstree-initial-node jstree-loading jstree-leaf jstree-last' role='tree-item'><i class='jstree-icon jstree-ocl'></i><a class='jstree-anchor' href='#'><i class='jstree-icon jstree-themeicon-hidden'></i>"+this.get_string("Loading ...")+"</a></li></ul>"),this.element.attr("aria-activedescendant","j"+this._id+"_loading"),this._data.core.li_height=this.get_container_ul().children("li").first().outerHeight()||24,this._data.core.node=this._create_prototype_node(),this.trigger("loading"),this.load_node(a.jstree.root)},destroy:function(a){if(this.trigger("destroy"),this._wrk)try{window.URL.revokeObjectURL(this._wrk),this._wrk=null}catch(b){}a||this.element.empty(),this.teardown()},_create_prototype_node:function(){var a=i.createElement("LI"),b,c;return a.setAttribute("role","treeitem"),b=i.createElement("I"),b.className="jstree-icon jstree-ocl",b.setAttribute("role","presentation"),a.appendChild(b),b=i.createElement("A"),b.className="jstree-anchor",b.setAttribute("href","#"),b.setAttribute("tabindex","-1"),c=i.createElement("I"),c.className="jstree-icon jstree-themeicon",c.setAttribute("role","presentation"),b.appendChild(c),a.appendChild(b),b=c=null,a},_kbevent_to_func:function(a){var b={8:"Backspace",9:"Tab",13:"Return",19:"Pause",27:"Esc",32:"Space",33:"PageUp",34:"PageDown",35:"End",36:"Home",37:"Left",38:"Up",39:"Right",40:"Down",44:"Print",45:"Insert",46:"Delete",96:"Numpad0",97:"Numpad1",98:"Numpad2",99:"Numpad3",100:"Numpad4",101:"Numpad5",102:"Numpad6",103:"Numpad7",104:"Numpad8",105:"Numpad9","-13":"NumpadEnter",112:"F1",113:"F2",114:"F3",115:"F4",116:"F5",117:"F6",118:"F7",119:"F8",120:"F9",121:"F10",122:"F11",123:"F12",144:"Numlock",145:"Scrolllock",16:"Shift",17:"Ctrl",18:"Alt",48:"0",49:"1",50:"2",51:"3",52:"4",53:"5",54:"6",55:"7",56:"8",57:"9",59:";",61:"=",65:"a",66:"b",67:"c",68:"d",69:"e",70:"f",71:"g",72:"h",73:"i",74:"j",75:"k",76:"l",77:"m",78:"n",79:"o",80:"p",81:"q",82:"r",83:"s",84:"t",85:"u",86:"v",87:"w",88:"x",89:"y",90:"z",107:"+",109:"-",110:".",186:";",187:"=",188:",",189:"-",190:".",191:"/",192:"`",219:"[",220:"\\",221:"]",222:"'",111:"/",106:"*",173:"-"},c=[];a.ctrlKey&&c.push("ctrl"),a.altKey&&c.push("alt"),a.shiftKey&&c.push("shift"),c.push(b[a.which]||a.which),c=c.sort().join("-").toLowerCase();var d=this.settings.core.keyboard,e,f;for(e in d)if(d.hasOwnProperty(e)&&(f=e,"-"!==f&&"+"!==f&&(f=f.replace("--","-MINUS").replace("+-","-MINUS").replace("++","-PLUS").replace("-+","-PLUS"),f=f.split(/-|\+/).sort().join("-").replace("MINUS","-").replace("PLUS","+").toLowerCase()),f===c))return d[e];return null},teardown:function(){this.unbind(),this.element.removeClass("jstree").removeData("jstree").find("[class^='jstree']").addBack().attr("class",function(){return this.className.replace(/jstree[^ ]*|$/gi,"")}),this.element=null},bind:function(){var b="",c=null,d=0;this.element.on("dblclick.jstree",function(a){if(a.target.tagName&&"input"===a.target.tagName.toLowerCase())return!0;if(i.selection&&i.selection.empty)i.selection.empty();else if(window.getSelection){var b=window.getSelection();try{b.removeAllRanges(),b.collapse()}catch(c){}}}).on("mousedown.jstree",a.proxy(function(a){a.target===this.element[0]&&(a.preventDefault(),d=+new Date)},this)).on("mousedown.jstree",".jstree-ocl",function(a){a.preventDefault()}).on("click.jstree",".jstree-ocl",a.proxy(function(a){this.toggle_node(a.target)},this)).on("dblclick.jstree",".jstree-anchor",a.proxy(function(a){return a.target.tagName&&"input"===a.target.tagName.toLowerCase()?!0:void(this.settings.core.dblclick_toggle&&this.toggle_node(a.target))},this)).on("click.jstree",".jstree-anchor",a.proxy(function(b){b.preventDefault(),b.currentTarget!==i.activeElement&&a(b.currentTarget).focus(),this.activate_node(b.currentTarget,b)},this)).on("keydown.jstree",".jstree-anchor",a.proxy(function(a){if(a.target.tagName&&"input"===a.target.tagName.toLowerCase())return!0;this._data.core.rtl&&(37===a.which?a.which=39:39===a.which&&(a.which=37));var b=this._kbevent_to_func(a);if(b){var c=b.call(this,a);if(c===!1||c===!0)return c}},this)).on("load_node.jstree",a.proxy(function(b,c){c.status&&(c.node.id!==a.jstree.root||this._data.core.loaded||(this._data.core.loaded=!0,this._firstChild(this.get_container_ul()[0])&&this.element.attr("aria-activedescendant",this._firstChild(this.get_container_ul()[0]).id),this.trigger("loaded")),this._data.core.ready||setTimeout(a.proxy(function(){if(this.element&&!this.get_container_ul().find(".jstree-loading").length){if(this._data.core.ready=!0,this._data.core.selected.length){if(this.settings.core.expand_selected_onload){var b=[],c,d;for(c=0,d=this._data.core.selected.length;d>c;c++)b=b.concat(this._model.data[this._data.core.selected[c]].parents);for(b=a.vakata.array_unique(b),c=0,d=b.length;d>c;c++)this.open_node(b[c],!1,0)}this.trigger("changed",{action:"ready",selected:this._data.core.selected})}this.trigger("ready")}},this),0))},this)).on("keypress.jstree",a.proxy(function(d){if(d.target.tagName&&"input"===d.target.tagName.toLowerCase())return!0;c&&clearTimeout(c),c=setTimeout(function(){b=""},500);var e=String.fromCharCode(d.which).toLowerCase(),f=this.element.find(".jstree-anchor").filter(":visible"),g=f.index(i.activeElement)||0,h=!1;if(b+=e,b.length>1){if(f.slice(g).each(a.proxy(function(c,d){return 0===a(d).text().toLowerCase().indexOf(b)?(a(d).focus(),h=!0,!1):void 0},this)),h)return;if(f.slice(0,g).each(a.proxy(function(c,d){return 0===a(d).text().toLowerCase().indexOf(b)?(a(d).focus(),h=!0,!1):void 0},this)),h)return}if(new RegExp("^"+e.replace(/[-\/\\^$*+?.()|[\]{}]/g,"\\$&")+"+$").test(b)){if(f.slice(g+1).each(a.proxy(function(b,c){return a(c).text().toLowerCase().charAt(0)===e?(a(c).focus(),h=!0,!1):void 0},this)),h)return;if(f.slice(0,g+1).each(a.proxy(function(b,c){return a(c).text().toLowerCase().charAt(0)===e?(a(c).focus(),h=!0,!1):void 0},this)),h)return}},this)).on("init.jstree",a.proxy(function(){var a=this.settings.core.themes;this._data.core.themes.dots=a.dots,this._data.core.themes.stripes=a.stripes,this._data.core.themes.icons=a.icons,this._data.core.themes.ellipsis=a.ellipsis,this.set_theme(a.name||"default",a.url),this.set_theme_variant(a.variant)},this)).on("loading.jstree",a.proxy(function(){this[this._data.core.themes.dots?"show_dots":"hide_dots"](),this[this._data.core.themes.icons?"show_icons":"hide_icons"](),this[this._data.core.themes.stripes?"show_stripes":"hide_stripes"](),this[this._data.core.themes.ellipsis?"show_ellipsis":"hide_ellipsis"]()},this)).on("blur.jstree",".jstree-anchor",a.proxy(function(b){this._data.core.focused=null,a(b.currentTarget).filter(".jstree-hovered").mouseleave(),this.element.attr("tabindex","0")},this)).on("focus.jstree",".jstree-anchor",a.proxy(function(b){var c=this.get_node(b.currentTarget);c&&c.id&&(this._data.core.focused=c.id),this.element.find(".jstree-hovered").not(b.currentTarget).mouseleave(),a(b.currentTarget).mouseenter(),this.element.attr("tabindex","-1")},this)).on("focus.jstree",a.proxy(function(){if(+new Date-d>500&&!this._data.core.focused&&this.settings.core.restore_focus){d=0;var a=this.get_node(this.element.attr("aria-activedescendant"),!0);a&&a.find("> .jstree-anchor").focus()}},this)).on("mouseenter.jstree",".jstree-anchor",a.proxy(function(a){this.hover_node(a.currentTarget)},this)).on("mouseleave.jstree",".jstree-anchor",a.proxy(function(a){this.dehover_node(a.currentTarget)},this))},unbind:function(){this.element.off(".jstree"),a(i).off(".jstree-"+this._id)},trigger:function(a,b){b||(b={}),b.instance=this,this.element.triggerHandler(a.replace(".jstree","")+".jstree",b)},get_container:function(){return this.element},get_container_ul:function(){return this.element.children(".jstree-children").first()},get_string:function(b){var c=this.settings.core.strings;return a.isFunction(c)?c.call(this,b):c&&c[b]?c[b]:b},_firstChild:function(a){a=a?a.firstChild:null;while(null!==a&&1!==a.nodeType)a=a.nextSibling;return a},_nextSibling:function(a){a=a?a.nextSibling:null;while(null!==a&&1!==a.nodeType)a=a.nextSibling;return a},_previousSibling:function(a){a=a?a.previousSibling:null;while(null!==a&&1!==a.nodeType)a=a.previousSibling;return a},get_node:function(b,c){b&&b.id&&(b=b.id),b instanceof jQuery&&b.length&&b[0].id&&(b=b[0].id);var d;try{if(this._model.data[b])b=this._model.data[b];else if("string"==typeof b&&this._model.data[b.replace(/^#/,"")])b=this._model.data[b.replace(/^#/,"")];else if("string"==typeof b&&(d=a("#"+b.replace(a.jstree.idregex,"\\$&"),this.element)).length&&this._model.data[d.closest(".jstree-node").attr("id")])b=this._model.data[d.closest(".jstree-node").attr("id")];else if((d=this.element.find(b)).length&&this._model.data[d.closest(".jstree-node").attr("id")])b=this._model.data[d.closest(".jstree-node").attr("id")];else{if(!(d=this.element.find(b)).length||!d.hasClass("jstree"))return!1;b=this._model.data[a.jstree.root]}return c&&(b=b.id===a.jstree.root?this.element:a("#"+b.id.replace(a.jstree.idregex,"\\$&"),this.element)),b}catch(e){return!1}},get_path:function(b,c,d){if(b=b.parents?b:this.get_node(b),!b||b.id===a.jstree.root||!b.parents)return!1;var e,f,g=[];for(g.push(d?b.id:b.text),e=0,f=b.parents.length;f>e;e++)g.push(d?b.parents[e]:this.get_text(b.parents[e]));return g=g.reverse().slice(1),c?g.join(c):g},get_next_dom:function(b,c){var d;if(b=this.get_node(b,!0),b[0]===this.element[0]){d=this._firstChild(this.get_container_ul()[0]);while(d&&0===d.offsetHeight)d=this._nextSibling(d);return d?a(d):!1}if(!b||!b.length)return!1;if(c){d=b[0];do d=this._nextSibling(d);while(d&&0===d.offsetHeight);return d?a(d):!1}if(b.hasClass("jstree-open")){d=this._firstChild(b.children(".jstree-children")[0]);while(d&&0===d.offsetHeight)d=this._nextSibling(d);if(null!==d)return a(d)}d=b[0];do d=this._nextSibling(d);while(d&&0===d.offsetHeight);return null!==d?a(d):b.parentsUntil(".jstree",".jstree-node").nextAll(".jstree-node:visible").first()},get_prev_dom:function(b,c){var d;if(b=this.get_node(b,!0),b[0]===this.element[0]){d=this.get_container_ul()[0].lastChild;while(d&&0===d.offsetHeight)d=this._previousSibling(d);return d?a(d):!1}if(!b||!b.length)return!1;if(c){d=b[0];do d=this._previousSibling(d);while(d&&0===d.offsetHeight);return d?a(d):!1}d=b[0];do d=this._previousSibling(d);while(d&&0===d.offsetHeight);if(null!==d){b=a(d);while(b.hasClass("jstree-open"))b=b.children(".jstree-children").first().children(".jstree-node:visible:last");return b}return d=b[0].parentNode.parentNode,d&&d.className&&-1!==d.className.indexOf("jstree-node")?a(d):!1},get_parent:function(b){return b=this.get_node(b),b&&b.id!==a.jstree.root?b.parent:!1},get_children_dom:function(a){return a=this.get_node(a,!0),a[0]===this.element[0]?this.get_container_ul().children(".jstree-node"):a&&a.length?a.children(".jstree-children").children(".jstree-node"):!1},is_parent:function(a){return a=this.get_node(a),a&&(a.state.loaded===!1||a.children.length>0)},is_loaded:function(a){return a=this.get_node(a),a&&a.state.loaded},is_loading:function(a){return a=this.get_node(a),a&&a.state&&a.state.loading},is_open:function(a){return a=this.get_node(a),a&&a.state.opened},is_closed:function(a){return a=this.get_node(a),a&&this.is_parent(a)&&!a.state.opened},is_leaf:function(a){return!this.is_parent(a)},load_node:function(b,c){var d,e,f,g,h;if(a.isArray(b))return this._load_nodes(b.slice(),c),!0;if(b=this.get_node(b),!b)return c&&c.call(this,b,!1),!1;if(b.state.loaded){for(b.state.loaded=!1,f=0,g=b.parents.length;g>f;f++)this._model.data[b.parents[f]].children_d=a.vakata.array_filter(this._model.data[b.parents[f]].children_d,function(c){return-1===a.inArray(c,b.children_d)});for(d=0,e=b.children_d.length;e>d;d++)this._model.data[b.children_d[d]].state.selected&&(h=!0),delete this._model.data[b.children_d[d]];h&&(this._data.core.selected=a.vakata.array_filter(this._data.core.selected,function(c){return-1===a.inArray(c,b.children_d)})),b.children=[],b.children_d=[],h&&this.trigger("changed",{action:"load_node",node:b,selected:this._data.core.selected})}return b.state.failed=!1,b.state.loading=!0,this.get_node(b,!0).addClass("jstree-loading").attr("aria-busy",!0),this._load_node(b,a.proxy(function(a){b=this._model.data[b.id],b.state.loading=!1,b.state.loaded=a,b.state.failed=!b.state.loaded;var d=this.get_node(b,!0),e=0,f=0,g=this._model.data,h=!1;for(e=0,f=b.children.length;f>e;e++)if(g[b.children[e]]&&!g[b.children[e]].state.hidden){h=!0;break}b.state.loaded&&d&&d.length&&(d.removeClass("jstree-closed jstree-open jstree-leaf"),h?"#"!==b.id&&d.addClass(b.state.opened?"jstree-open":"jstree-closed"):d.addClass("jstree-leaf")),d.removeClass("jstree-loading").attr("aria-busy",!1),this.trigger("load_node",{node:b,status:a}),c&&c.call(this,b,a)},this)),!0},_load_nodes:function(a,b,c,d){var e=!0,f=function(){this._load_nodes(a,b,!0)},g=this._model.data,h,i,j=[];for(h=0,i=a.length;i>h;h++)g[a[h]]&&(!g[a[h]].state.loaded&&!g[a[h]].state.failed||!c&&d)&&(this.is_loading(a[h])||this.load_node(a[h],f),e=!1);if(e){for(h=0,i=a.length;i>h;h++)g[a[h]]&&g[a[h]].state.loaded&&j.push(a[h]);b&&!b.done&&(b.call(this,j),b.done=!0)}},load_all:function(b,c){if(b||(b=a.jstree.root),b=this.get_node(b),!b)return!1;var d=[],e=this._model.data,f=e[b.id].children_d,g,h;for(b.state&&!b.state.loaded&&d.push(b.id),g=0,h=f.length;h>g;g++)e[f[g]]&&e[f[g]].state&&!e[f[g]].state.loaded&&d.push(f[g]);d.length?this._load_nodes(d,function(){this.load_all(b,c)}):(c&&c.call(this,b),this.trigger("load_all",{node:b}))},_load_node:function(b,c){var d=this.settings.core.data,e,f=function g(){return 3!==this.nodeType&&8!==this.nodeType};return d?a.isFunction(d)?d.call(this,b,a.proxy(function(d){d===!1?c.call(this,!1):this["string"==typeof d?"_append_html_data":"_append_json_data"](b,"string"==typeof d?a(a.parseHTML(d)).filter(f):d,function(a){c.call(this,a)})},this)):"object"==typeof d?d.url?(d=a.extend(!0,{},d),a.isFunction(d.url)&&(d.url=d.url.call(this,b)),a.isFunction(d.data)&&(d.data=d.data.call(this,b)),a.ajax(d).done(a.proxy(function(d,e,g){var h=g.getResponseHeader("Content-Type");return h&&-1!==h.indexOf("json")||"object"==typeof d?this._append_json_data(b,d,function(a){c.call(this,a)}):h&&-1!==h.indexOf("html")||"string"==typeof d?this._append_html_data(b,a(a.parseHTML(d)).filter(f),function(a){c.call(this,a)}):(this._data.core.last_error={error:"ajax",plugin:"core",id:"core_04",reason:"Could not load node",data:JSON.stringify({id:b.id,xhr:g})},this.settings.core.error.call(this,this._data.core.last_error),c.call(this,!1))},this)).fail(a.proxy(function(a){this._data.core.last_error={error:"ajax",plugin:"core",id:"core_04",reason:"Could not load node",data:JSON.stringify({id:b.id,xhr:a})},c.call(this,!1),this.settings.core.error.call(this,this._data.core.last_error)},this))):(e=a.isArray(d)?a.extend(!0,[],d):a.isPlainObject(d)?a.extend(!0,{},d):d,b.id===a.jstree.root?this._append_json_data(b,e,function(a){c.call(this,a)}):(this._data.core.last_error={error:"nodata",plugin:"core",id:"core_05",reason:"Could not load node",data:JSON.stringify({id:b.id})},this.settings.core.error.call(this,this._data.core.last_error),c.call(this,!1))):"string"==typeof d?b.id===a.jstree.root?this._append_html_data(b,a(a.parseHTML(d)).filter(f),function(a){c.call(this,a)}):(this._data.core.last_error={error:"nodata",plugin:"core",id:"core_06",reason:"Could not load node",data:JSON.stringify({id:b.id})},this.settings.core.error.call(this,this._data.core.last_error),c.call(this,!1)):c.call(this,!1):b.id===a.jstree.root?this._append_html_data(b,this._data.core.original_container_html.clone(!0),function(a){c.call(this,a)}):c.call(this,!1)},_node_changed:function(b){b=this.get_node(b),b&&-1===a.inArray(b.id,this._model.changed)&&this._model.changed.push(b.id)},_append_html_data:function(b,c,d){b=this.get_node(b),b.children=[],b.children_d=[];var e=c.is("ul")?c.children():c,f=b.id,g=[],h=[],i=this._model.data,j=i[f],k=this._data.core.selected.length,l,m,n;for(e.each(a.proxy(function(b,c){l=this._parse_model_from_html(a(c),f,j.parents.concat()),l&&(g.push(l),h.push(l),i[l].children_d.length&&(h=h.concat(i[l].children_d)))},this)),j.children=g,j.children_d=h,m=0,n=j.parents.length;n>m;m++)i[j.parents[m]].children_d=i[j.parents[m]].children_d.concat(h);this.trigger("model",{nodes:h,parent:f}),f!==a.jstree.root?(this._node_changed(f),this.redraw()):(this.get_container_ul().children(".jstree-initial-node").remove(),this.redraw(!0)),this._data.core.selected.length!==k&&this.trigger("changed",{action:"model",selected:this._data.core.selected}),d.call(this,!0)},_append_json_data:function(b,c,d,e){if(null!==this.element){b=this.get_node(b),b.children=[],b.children_d=[],c.d&&(c=c.d,"string"==typeof c&&(c=JSON.parse(c))),a.isArray(c)||(c=[c]);var f=null,g={df:this._model.default_state,dat:c,par:b.id,m:this._model.data,t_id:this._id,t_cnt:this._cnt,sel:this._data.core.selected},h=function(a,b){a.data&&(a=a.data);var c=a.dat,d=a.par,e=[],f=[],g=[],h=a.df,i=a.t_id,j=a.t_cnt,k=a.m,l=k[d],m=a.sel,n,o,p,q,r=function(a,c,d){d=d?d.concat():[],c&&d.unshift(c);var e=a.id.toString(),f,i,j,l,m={id:e,text:a.text||"",icon:a.icon!==b?a.icon:!0,parent:c,parents:d,children:a.children||[],children_d:a.children_d||[],data:a.data,state:{},li_attr:{id:!1},a_attr:{href:"#"},original:!1};for(f in h)h.hasOwnProperty(f)&&(m.state[f]=h[f]);if(a&&a.data&&a.data.jstree&&a.data.jstree.icon&&(m.icon=a.data.jstree.icon),(m.icon===b||null===m.icon||""===m.icon)&&(m.icon=!0),a&&a.data&&(m.data=a.data,a.data.jstree))for(f in a.data.jstree)a.data.jstree.hasOwnProperty(f)&&(m.state[f]=a.data.jstree[f]);if(a&&"object"==typeof a.state)for(f in a.state)a.state.hasOwnProperty(f)&&(m.state[f]=a.state[f]);if(a&&"object"==typeof a.li_attr)for(f in a.li_attr)a.li_attr.hasOwnProperty(f)&&(m.li_attr[f]=a.li_attr[f]);if(m.li_attr.id||(m.li_attr.id=e),a&&"object"==typeof a.a_attr)for(f in a.a_attr)a.a_attr.hasOwnProperty(f)&&(m.a_attr[f]=a.a_attr[f]);for(a&&a.children&&a.children===!0&&(m.state.loaded=!1,m.children=[],m.children_d=[]),k[m.id]=m,f=0,i=m.children.length;i>f;f++)j=r(k[m.children[f]],m.id,d),l=k[j],m.children_d.push(j),l.children_d.length&&(m.children_d=m.children_d.concat(l.children_d));return delete a.data,delete a.children,k[m.id].original=a,m.state.selected&&g.push(m.id),m.id},s=function(a,c,d){d=d?d.concat():[],c&&d.unshift(c);var e=!1,f,l,m,n,o;do e="j"+i+"_"+ ++j;while(k[e]);o={id:!1,text:"string"==typeof a?a:"",icon:"object"==typeof a&&a.icon!==b?a.icon:!0,parent:c,parents:d,children:[],children_d:[],data:null,state:{},li_attr:{id:!1},a_attr:{href:"#"},original:!1};for(f in h)h.hasOwnProperty(f)&&(o.state[f]=h[f]);if(a&&a.id&&(o.id=a.id.toString()),a&&a.text&&(o.text=a.text),a&&a.data&&a.data.jstree&&a.data.jstree.icon&&(o.icon=a.data.jstree.icon),(o.icon===b||null===o.icon||""===o.icon)&&(o.icon=!0),a&&a.data&&(o.data=a.data,a.data.jstree))for(f in a.data.jstree)a.data.jstree.hasOwnProperty(f)&&(o.state[f]=a.data.jstree[f]);if(a&&"object"==typeof a.state)for(f in a.state)a.state.hasOwnProperty(f)&&(o.state[f]=a.state[f]);if(a&&"object"==typeof a.li_attr)for(f in a.li_attr)a.li_attr.hasOwnProperty(f)&&(o.li_attr[f]=a.li_attr[f]);if(o.li_attr.id&&!o.id&&(o.id=o.li_attr.id.toString()),o.id||(o.id=e),o.li_attr.id||(o.li_attr.id=o.id),a&&"object"==typeof a.a_attr)for(f in a.a_attr)a.a_attr.hasOwnProperty(f)&&(o.a_attr[f]=a.a_attr[f]);if(a&&a.children&&a.children.length){for(f=0,l=a.children.length;l>f;f++)m=s(a.children[f],o.id,d),n=k[m],o.children.push(m),n.children_d.length&&(o.children_d=o.children_d.concat(n.children_d));o.children_d=o.children_d.concat(o.children)}return a&&a.children&&a.children===!0&&(o.state.loaded=!1,o.children=[],o.children_d=[]),delete a.data,delete a.children,o.original=a,k[o.id]=o,o.state.selected&&g.push(o.id),o.id};if(c.length&&c[0].id!==b&&c[0].parent!==b){for(o=0,p=c.length;p>o;o++)c[o].children||(c[o].children=[]),c[o].state||(c[o].state={}),k[c[o].id.toString()]=c[o];for(o=0,p=c.length;p>o;o++)k[c[o].parent.toString()]?(k[c[o].parent.toString()].children.push(c[o].id.toString()),l.children_d.push(c[o].id.toString())):(this._data.core.last_error={error:"parse",plugin:"core",id:"core_07",reason:"Node with invalid parent",data:JSON.stringify({id:c[o].id.toString(),parent:c[o].parent.toString()})},this.settings.core.error.call(this,this._data.core.last_error));for(o=0,p=l.children.length;p>o;o++)n=r(k[l.children[o]],d,l.parents.concat()),f.push(n),k[n].children_d.length&&(f=f.concat(k[n].children_d));for(o=0,p=l.parents.length;p>o;o++)k[l.parents[o]].children_d=k[l.parents[o]].children_d.concat(f);q={cnt:j,mod:k,sel:m,par:d,dpc:f,add:g}}else{for(o=0,p=c.length;p>o;o++)n=s(c[o],d,l.parents.concat()),n&&(e.push(n),f.push(n),k[n].children_d.length&&(f=f.concat(k[n].children_d)));for(l.children=e,l.children_d=f,o=0,p=l.parents.length;p>o;o++)k[l.parents[o]].children_d=k[l.parents[o]].children_d.concat(f);q={cnt:j,mod:k,sel:m,par:d,dpc:f,add:g}}return"undefined"!=typeof window&&"undefined"!=typeof window.document?q:void postMessage(q)},i=function(b,c){if(null!==this.element){this._cnt=b.cnt;var e,f=this._model.data;for(e in f)f.hasOwnProperty(e)&&f[e].state&&f[e].state.loading&&b.mod[e]&&(b.mod[e].state.loading=!0);if(this._model.data=b.mod,c){var g,h=b.add,i=b.sel,j=this._data.core.selected.slice();if(f=this._model.data,i.length!==j.length||a.vakata.array_unique(i.concat(j)).length!==i.length){for(e=0,g=i.length;g>e;e++)-1===a.inArray(i[e],h)&&-1===a.inArray(i[e],j)&&(f[i[e]].state.selected=!1);for(e=0,g=j.length;g>e;e++)-1===a.inArray(j[e],i)&&(f[j[e]].state.selected=!0)}}b.add.length&&(this._data.core.selected=this._data.core.selected.concat(b.add)),this.trigger("model",{nodes:b.dpc,parent:b.par}),b.par!==a.jstree.root?(this._node_changed(b.par),this.redraw()):this.redraw(!0),b.add.length&&this.trigger("changed",{action:"model",selected:this._data.core.selected}),d.call(this,!0)}};if(this.settings.core.worker&&window.Blob&&window.URL&&window.Worker)try{null===this._wrk&&(this._wrk=window.URL.createObjectURL(new window.Blob(["self.onmessage = "+h.toString()],{type:"text/javascript"}))),!this._data.core.working||e?(this._data.core.working=!0,f=new window.Worker(this._wrk),f.onmessage=a.proxy(function(a){i.call(this,a.data,!0);try{f.terminate(),f=null}catch(b){}this._data.core.worker_queue.length?this._append_json_data.apply(this,this._data.core.worker_queue.shift()):this._data.core.working=!1},this),g.par?f.postMessage(g):this._data.core.worker_queue.length?this._append_json_data.apply(this,this._data.core.worker_queue.shift()):this._data.core.working=!1):this._data.core.worker_queue.push([b,c,d,!0])}catch(j){i.call(this,h(g),!1),this._data.core.worker_queue.length?this._append_json_data.apply(this,this._data.core.worker_queue.shift()):this._data.core.working=!1}else i.call(this,h(g),!1)}},_parse_model_from_html:function(c,d,e){e=e?[].concat(e):[],d&&e.unshift(d);var f,g,h=this._model.data,i={id:!1,text:!1,icon:!0,parent:d,parents:e,children:[],children_d:[],data:null,state:{},li_attr:{id:!1},a_attr:{href:"#"},original:!1},j,k,l;for(j in this._model.default_state)this._model.default_state.hasOwnProperty(j)&&(i.state[j]=this._model.default_state[j]);if(k=a.vakata.attributes(c,!0),a.each(k,function(b,c){return c=a.trim(c),c.length?(i.li_attr[b]=c,void("id"===b&&(i.id=c.toString()))):!0}),k=c.children("a").first(),k.length&&(k=a.vakata.attributes(k,!0),a.each(k,function(b,c){c=a.trim(c),c.length&&(i.a_attr[b]=c)})),k=c.children("a").first().length?c.children("a").first().clone():c.clone(),k.children("ins, i, ul").remove(),k=k.html(),k=a("<div />").html(k),i.text=this.settings.core.force_text?k.text():k.html(),k=c.data(),i.data=k?a.extend(!0,{},k):null,i.state.opened=c.hasClass("jstree-open"),i.state.selected=c.children("a").hasClass("jstree-clicked"),i.state.disabled=c.children("a").hasClass("jstree-disabled"),i.data&&i.data.jstree)for(j in i.data.jstree)i.data.jstree.hasOwnProperty(j)&&(i.state[j]=i.data.jstree[j]);k=c.children("a").children(".jstree-themeicon"),k.length&&(i.icon=k.hasClass("jstree-themeicon-hidden")?!1:k.attr("rel")),i.state.icon!==b&&(i.icon=i.state.icon),(i.icon===b||null===i.icon||""===i.icon)&&(i.icon=!0),k=c.children("ul").children("li");do l="j"+this._id+"_"+ ++this._cnt;while(h[l]);return i.id=i.li_attr.id?i.li_attr.id.toString():l,k.length?(k.each(a.proxy(function(b,c){f=this._parse_model_from_html(a(c),i.id,e),g=this._model.data[f],i.children.push(f),g.children_d.length&&(i.children_d=i.children_d.concat(g.children_d))},this)),i.children_d=i.children_d.concat(i.children)):c.hasClass("jstree-closed")&&(i.state.loaded=!1),i.li_attr["class"]&&(i.li_attr["class"]=i.li_attr["class"].replace("jstree-closed","").replace("jstree-open","")),i.a_attr["class"]&&(i.a_attr["class"]=i.a_attr["class"].replace("jstree-clicked","").replace("jstree-disabled","")),h[i.id]=i,i.state.selected&&this._data.core.selected.push(i.id),i.id},_parse_model_from_flat_json:function(a,c,d){d=d?d.concat():[],c&&d.unshift(c);var e=a.id.toString(),f=this._model.data,g=this._model.default_state,h,i,j,k,l={id:e,text:a.text||"",icon:a.icon!==b?a.icon:!0,parent:c,parents:d,children:a.children||[],children_d:a.children_d||[],data:a.data,state:{},li_attr:{id:!1},a_attr:{href:"#"},original:!1};for(h in g)g.hasOwnProperty(h)&&(l.state[h]=g[h]);if(a&&a.data&&a.data.jstree&&a.data.jstree.icon&&(l.icon=a.data.jstree.icon),(l.icon===b||null===l.icon||""===l.icon)&&(l.icon=!0),a&&a.data&&(l.data=a.data,a.data.jstree))for(h in a.data.jstree)a.data.jstree.hasOwnProperty(h)&&(l.state[h]=a.data.jstree[h]);if(a&&"object"==typeof a.state)for(h in a.state)a.state.hasOwnProperty(h)&&(l.state[h]=a.state[h]);if(a&&"object"==typeof a.li_attr)for(h in a.li_attr)a.li_attr.hasOwnProperty(h)&&(l.li_attr[h]=a.li_attr[h]);if(l.li_attr.id||(l.li_attr.id=e),a&&"object"==typeof a.a_attr)for(h in a.a_attr)a.a_attr.hasOwnProperty(h)&&(l.a_attr[h]=a.a_attr[h]);for(a&&a.children&&a.children===!0&&(l.state.loaded=!1,l.children=[],l.children_d=[]),f[l.id]=l,h=0,i=l.children.length;i>h;h++)j=this._parse_model_from_flat_json(f[l.children[h]],l.id,d),k=f[j],l.children_d.push(j),k.children_d.length&&(l.children_d=l.children_d.concat(k.children_d));return delete a.data,delete a.children,f[l.id].original=a,l.state.selected&&this._data.core.selected.push(l.id),l.id},_parse_model_from_json:function(a,c,d){d=d?d.concat():[],c&&d.unshift(c);var e=!1,f,g,h,i,j=this._model.data,k=this._model.default_state,l;do e="j"+this._id+"_"+ ++this._cnt;while(j[e]);l={id:!1,text:"string"==typeof a?a:"",icon:"object"==typeof a&&a.icon!==b?a.icon:!0,parent:c,parents:d,children:[],children_d:[],data:null,state:{},li_attr:{id:!1},a_attr:{href:"#"},original:!1};for(f in k)k.hasOwnProperty(f)&&(l.state[f]=k[f]);if(a&&a.id&&(l.id=a.id.toString()),a&&a.text&&(l.text=a.text),a&&a.data&&a.data.jstree&&a.data.jstree.icon&&(l.icon=a.data.jstree.icon),(l.icon===b||null===l.icon||""===l.icon)&&(l.icon=!0),a&&a.data&&(l.data=a.data,a.data.jstree))for(f in a.data.jstree)a.data.jstree.hasOwnProperty(f)&&(l.state[f]=a.data.jstree[f]);if(a&&"object"==typeof a.state)for(f in a.state)a.state.hasOwnProperty(f)&&(l.state[f]=a.state[f]);if(a&&"object"==typeof a.li_attr)for(f in a.li_attr)a.li_attr.hasOwnProperty(f)&&(l.li_attr[f]=a.li_attr[f]);if(l.li_attr.id&&!l.id&&(l.id=l.li_attr.id.toString()),
l.id||(l.id=e),l.li_attr.id||(l.li_attr.id=l.id),a&&"object"==typeof a.a_attr)for(f in a.a_attr)a.a_attr.hasOwnProperty(f)&&(l.a_attr[f]=a.a_attr[f]);if(a&&a.children&&a.children.length){for(f=0,g=a.children.length;g>f;f++)h=this._parse_model_from_json(a.children[f],l.id,d),i=j[h],l.children.push(h),i.children_d.length&&(l.children_d=l.children_d.concat(i.children_d));l.children_d=l.children_d.concat(l.children)}return a&&a.children&&a.children===!0&&(l.state.loaded=!1,l.children=[],l.children_d=[]),delete a.data,delete a.children,l.original=a,j[l.id]=l,l.state.selected&&this._data.core.selected.push(l.id),l.id},_redraw:function(){var b=this._model.force_full_redraw?this._model.data[a.jstree.root].children.concat([]):this._model.changed.concat([]),c=i.createElement("UL"),d,e,f,g=this._data.core.focused;for(e=0,f=b.length;f>e;e++)d=this.redraw_node(b[e],!0,this._model.force_full_redraw),d&&this._model.force_full_redraw&&c.appendChild(d);this._model.force_full_redraw&&(c.className=this.get_container_ul()[0].className,c.setAttribute("role","group"),this.element.empty().append(c)),null!==g&&this.settings.core.restore_focus&&(d=this.get_node(g,!0),d&&d.length&&d.children(".jstree-anchor")[0]!==i.activeElement?d.children(".jstree-anchor").focus():this._data.core.focused=null),this._model.force_full_redraw=!1,this._model.changed=[],this.trigger("redraw",{nodes:b})},redraw:function(a){a&&(this._model.force_full_redraw=!0),this._redraw()},draw_children:function(b){var c=this.get_node(b),d=!1,e=!1,f=!1,g=i;if(!c)return!1;if(c.id===a.jstree.root)return this.redraw(!0);if(b=this.get_node(b,!0),!b||!b.length)return!1;if(b.children(".jstree-children").remove(),b=b[0],c.children.length&&c.state.loaded){for(f=g.createElement("UL"),f.setAttribute("role","group"),f.className="jstree-children",d=0,e=c.children.length;e>d;d++)f.appendChild(this.redraw_node(c.children[d],!0,!0));b.appendChild(f)}},redraw_node:function(b,c,d,e){var f=this.get_node(b),g=!1,h=!1,j=!1,k=!1,l=!1,m=!1,n="",o=i,p=this._model.data,q=!1,r=!1,s=null,t=0,u=0,v=!1,w=!1;if(!f)return!1;if(f.id===a.jstree.root)return this.redraw(!0);if(c=c||0===f.children.length,b=i.querySelector?this.element[0].querySelector("#"+(-1!=="0123456789".indexOf(f.id[0])?"\\3"+f.id[0]+" "+f.id.substr(1).replace(a.jstree.idregex,"\\$&"):f.id.replace(a.jstree.idregex,"\\$&"))):i.getElementById(f.id))b=a(b),d||(g=b.parent().parent()[0],g===this.element[0]&&(g=null),h=b.index()),c||!f.children.length||b.children(".jstree-children").length||(c=!0),c||(j=b.children(".jstree-children")[0]),q=b.children(".jstree-anchor")[0]===i.activeElement,b.remove();else if(c=!0,!d){if(g=f.parent!==a.jstree.root?a("#"+f.parent.replace(a.jstree.idregex,"\\$&"),this.element)[0]:null,!(null===g||g&&p[f.parent].state.opened))return!1;h=a.inArray(f.id,null===g?p[a.jstree.root].children:p[f.parent].children)}b=this._data.core.node.cloneNode(!0),n="jstree-node ";for(k in f.li_attr)if(f.li_attr.hasOwnProperty(k)){if("id"===k)continue;"class"!==k?b.setAttribute(k,f.li_attr[k]):n+=f.li_attr[k]}for(f.a_attr.id||(f.a_attr.id=f.id+"_anchor"),b.setAttribute("aria-selected",!!f.state.selected),b.setAttribute("aria-level",f.parents.length),b.setAttribute("aria-labelledby",f.a_attr.id),f.state.disabled&&b.setAttribute("aria-disabled",!0),k=0,l=f.children.length;l>k;k++)if(!p[f.children[k]].state.hidden){v=!0;break}if(null!==f.parent&&p[f.parent]&&!f.state.hidden&&(k=a.inArray(f.id,p[f.parent].children),w=f.id,-1!==k))for(k++,l=p[f.parent].children.length;l>k;k++)if(p[p[f.parent].children[k]].state.hidden||(w=p[f.parent].children[k]),w!==f.id)break;f.state.hidden&&(n+=" jstree-hidden"),f.state.loading&&(n+=" jstree-loading"),f.state.loaded&&!v?n+=" jstree-leaf":(n+=f.state.opened&&f.state.loaded?" jstree-open":" jstree-closed",b.setAttribute("aria-expanded",f.state.opened&&f.state.loaded)),w===f.id&&(n+=" jstree-last"),b.id=f.id,b.className=n,n=(f.state.selected?" jstree-clicked":"")+(f.state.disabled?" jstree-disabled":"");for(l in f.a_attr)if(f.a_attr.hasOwnProperty(l)){if("href"===l&&"#"===f.a_attr[l])continue;"class"!==l?b.childNodes[1].setAttribute(l,f.a_attr[l]):n+=" "+f.a_attr[l]}if(n.length&&(b.childNodes[1].className="jstree-anchor "+n),(f.icon&&f.icon!==!0||f.icon===!1)&&(f.icon===!1?b.childNodes[1].childNodes[0].className+=" jstree-themeicon-hidden":-1===f.icon.indexOf("/")&&-1===f.icon.indexOf(".")?b.childNodes[1].childNodes[0].className+=" "+f.icon+" jstree-themeicon-custom":(b.childNodes[1].childNodes[0].style.backgroundImage='url("'+f.icon+'")',b.childNodes[1].childNodes[0].style.backgroundPosition="center center",b.childNodes[1].childNodes[0].style.backgroundSize="auto",b.childNodes[1].childNodes[0].className+=" jstree-themeicon-custom")),this.settings.core.force_text?b.childNodes[1].appendChild(o.createTextNode(f.text)):b.childNodes[1].innerHTML+=f.text,c&&f.children.length&&(f.state.opened||e)&&f.state.loaded){for(m=o.createElement("UL"),m.setAttribute("role","group"),m.className="jstree-children",k=0,l=f.children.length;l>k;k++)m.appendChild(this.redraw_node(f.children[k],c,!0));b.appendChild(m)}if(j&&b.appendChild(j),!d){for(g||(g=this.element[0]),k=0,l=g.childNodes.length;l>k;k++)if(g.childNodes[k]&&g.childNodes[k].className&&-1!==g.childNodes[k].className.indexOf("jstree-children")){s=g.childNodes[k];break}s||(s=o.createElement("UL"),s.setAttribute("role","group"),s.className="jstree-children",g.appendChild(s)),g=s,h<g.childNodes.length?g.insertBefore(b,g.childNodes[h]):g.appendChild(b),q&&(t=this.element[0].scrollTop,u=this.element[0].scrollLeft,b.childNodes[1].focus(),this.element[0].scrollTop=t,this.element[0].scrollLeft=u)}return f.state.opened&&!f.state.loaded&&(f.state.opened=!1,setTimeout(a.proxy(function(){this.open_node(f.id,!1,0)},this),0)),b},open_node:function(c,d,e){var f,g,h,i;if(a.isArray(c)){for(c=c.slice(),f=0,g=c.length;g>f;f++)this.open_node(c[f],d,e);return!0}return c=this.get_node(c),c&&c.id!==a.jstree.root?(e=e===b?this.settings.core.animation:e,this.is_closed(c)?this.is_loaded(c)?(h=this.get_node(c,!0),i=this,h.length&&(e&&h.children(".jstree-children").length&&h.children(".jstree-children").stop(!0,!0),c.children.length&&!this._firstChild(h.children(".jstree-children")[0])&&this.draw_children(c),e?(this.trigger("before_open",{node:c}),h.children(".jstree-children").css("display","none").end().removeClass("jstree-closed").addClass("jstree-open").attr("aria-expanded",!0).children(".jstree-children").stop(!0,!0).slideDown(e,function(){this.style.display="",i.element&&i.trigger("after_open",{node:c})})):(this.trigger("before_open",{node:c}),h[0].className=h[0].className.replace("jstree-closed","jstree-open"),h[0].setAttribute("aria-expanded",!0))),c.state.opened=!0,d&&d.call(this,c,!0),h.length||this.trigger("before_open",{node:c}),this.trigger("open_node",{node:c}),e&&h.length||this.trigger("after_open",{node:c}),!0):this.is_loading(c)?setTimeout(a.proxy(function(){this.open_node(c,d,e)},this),500):void this.load_node(c,function(a,b){return b?this.open_node(a,d,e):d?d.call(this,a,!1):!1}):(d&&d.call(this,c,!1),!1)):!1},_open_to:function(b){if(b=this.get_node(b),!b||b.id===a.jstree.root)return!1;var c,d,e=b.parents;for(c=0,d=e.length;d>c;c+=1)c!==a.jstree.root&&this.open_node(e[c],!1,0);return a("#"+b.id.replace(a.jstree.idregex,"\\$&"),this.element)},close_node:function(c,d){var e,f,g,h;if(a.isArray(c)){for(c=c.slice(),e=0,f=c.length;f>e;e++)this.close_node(c[e],d);return!0}return c=this.get_node(c),c&&c.id!==a.jstree.root?this.is_closed(c)?!1:(d=d===b?this.settings.core.animation:d,g=this,h=this.get_node(c,!0),c.state.opened=!1,this.trigger("close_node",{node:c}),void(h.length?d?h.children(".jstree-children").attr("style","display:block !important").end().removeClass("jstree-open").addClass("jstree-closed").attr("aria-expanded",!1).children(".jstree-children").stop(!0,!0).slideUp(d,function(){this.style.display="",h.children(".jstree-children").remove(),g.element&&g.trigger("after_close",{node:c})}):(h[0].className=h[0].className.replace("jstree-open","jstree-closed"),h.attr("aria-expanded",!1).children(".jstree-children").remove(),this.trigger("after_close",{node:c})):this.trigger("after_close",{node:c}))):!1},toggle_node:function(b){var c,d;if(a.isArray(b)){for(b=b.slice(),c=0,d=b.length;d>c;c++)this.toggle_node(b[c]);return!0}return this.is_closed(b)?this.open_node(b):this.is_open(b)?this.close_node(b):void 0},open_all:function(b,c,d){if(b||(b=a.jstree.root),b=this.get_node(b),!b)return!1;var e=b.id===a.jstree.root?this.get_container_ul():this.get_node(b,!0),f,g,h;if(!e.length){for(f=0,g=b.children_d.length;g>f;f++)this.is_closed(this._model.data[b.children_d[f]])&&(this._model.data[b.children_d[f]].state.opened=!0);return this.trigger("open_all",{node:b})}d=d||e,h=this,e=this.is_closed(b)?e.find(".jstree-closed").addBack():e.find(".jstree-closed"),e.each(function(){h.open_node(this,function(a,b){b&&this.is_parent(a)&&this.open_all(a,c,d)},c||0)}),0===d.find(".jstree-closed").length&&this.trigger("open_all",{node:this.get_node(d)})},close_all:function(b,c){if(b||(b=a.jstree.root),b=this.get_node(b),!b)return!1;var d=b.id===a.jstree.root?this.get_container_ul():this.get_node(b,!0),e=this,f,g;for(d.length&&(d=this.is_open(b)?d.find(".jstree-open").addBack():d.find(".jstree-open"),a(d.get().reverse()).each(function(){e.close_node(this,c||0)})),f=0,g=b.children_d.length;g>f;f++)this._model.data[b.children_d[f]].state.opened=!1;this.trigger("close_all",{node:b})},is_disabled:function(a){return a=this.get_node(a),a&&a.state&&a.state.disabled},enable_node:function(b){var c,d;if(a.isArray(b)){for(b=b.slice(),c=0,d=b.length;d>c;c++)this.enable_node(b[c]);return!0}return b=this.get_node(b),b&&b.id!==a.jstree.root?(b.state.disabled=!1,this.get_node(b,!0).children(".jstree-anchor").removeClass("jstree-disabled").attr("aria-disabled",!1),void this.trigger("enable_node",{node:b})):!1},disable_node:function(b){var c,d;if(a.isArray(b)){for(b=b.slice(),c=0,d=b.length;d>c;c++)this.disable_node(b[c]);return!0}return b=this.get_node(b),b&&b.id!==a.jstree.root?(b.state.disabled=!0,this.get_node(b,!0).children(".jstree-anchor").addClass("jstree-disabled").attr("aria-disabled",!0),void this.trigger("disable_node",{node:b})):!1},is_hidden:function(a){return a=this.get_node(a),a.state.hidden===!0},hide_node:function(b,c){var d,e;if(a.isArray(b)){for(b=b.slice(),d=0,e=b.length;e>d;d++)this.hide_node(b[d],!0);return c||this.redraw(),!0}return b=this.get_node(b),b&&b.id!==a.jstree.root?void(b.state.hidden||(b.state.hidden=!0,this._node_changed(b.parent),c||this.redraw(),this.trigger("hide_node",{node:b}))):!1},show_node:function(b,c){var d,e;if(a.isArray(b)){for(b=b.slice(),d=0,e=b.length;e>d;d++)this.show_node(b[d],!0);return c||this.redraw(),!0}return b=this.get_node(b),b&&b.id!==a.jstree.root?void(b.state.hidden&&(b.state.hidden=!1,this._node_changed(b.parent),c||this.redraw(),this.trigger("show_node",{node:b}))):!1},hide_all:function(b){var c,d=this._model.data,e=[];for(c in d)d.hasOwnProperty(c)&&c!==a.jstree.root&&!d[c].state.hidden&&(d[c].state.hidden=!0,e.push(c));return this._model.force_full_redraw=!0,b||this.redraw(),this.trigger("hide_all",{nodes:e}),e},show_all:function(b){var c,d=this._model.data,e=[];for(c in d)d.hasOwnProperty(c)&&c!==a.jstree.root&&d[c].state.hidden&&(d[c].state.hidden=!1,e.push(c));return this._model.force_full_redraw=!0,b||this.redraw(),this.trigger("show_all",{nodes:e}),e},activate_node:function(a,c){if(this.is_disabled(a))return!1;if(c&&"object"==typeof c||(c={}),this._data.core.last_clicked=this._data.core.last_clicked&&this._data.core.last_clicked.id!==b?this.get_node(this._data.core.last_clicked.id):null,this._data.core.last_clicked&&!this._data.core.last_clicked.state.selected&&(this._data.core.last_clicked=null),!this._data.core.last_clicked&&this._data.core.selected.length&&(this._data.core.last_clicked=this.get_node(this._data.core.selected[this._data.core.selected.length-1])),this.settings.core.multiple&&(c.metaKey||c.ctrlKey||c.shiftKey)&&(!c.shiftKey||this._data.core.last_clicked&&this.get_parent(a)&&this.get_parent(a)===this._data.core.last_clicked.parent))if(c.shiftKey){var d=this.get_node(a).id,e=this._data.core.last_clicked.id,f=this.get_node(this._data.core.last_clicked.parent).children,g=!1,h,i;for(h=0,i=f.length;i>h;h+=1)f[h]===d&&(g=!g),f[h]===e&&(g=!g),this.is_disabled(f[h])||!g&&f[h]!==d&&f[h]!==e?this.deselect_node(f[h],!0,c):this.is_hidden(f[h])||this.select_node(f[h],!0,!1,c);this.trigger("changed",{action:"select_node",node:this.get_node(a),selected:this._data.core.selected,event:c})}else this.is_selected(a)?this.deselect_node(a,!1,c):this.select_node(a,!1,!1,c);else!this.settings.core.multiple&&(c.metaKey||c.ctrlKey||c.shiftKey)&&this.is_selected(a)?this.deselect_node(a,!1,c):(this.deselect_all(!0),this.select_node(a,!1,!1,c),this._data.core.last_clicked=this.get_node(a));this.trigger("activate_node",{node:this.get_node(a),event:c})},hover_node:function(a){if(a=this.get_node(a,!0),!a||!a.length||a.children(".jstree-hovered").length)return!1;var b=this.element.find(".jstree-hovered"),c=this.element;b&&b.length&&this.dehover_node(b),a.children(".jstree-anchor").addClass("jstree-hovered"),this.trigger("hover_node",{node:this.get_node(a)}),setTimeout(function(){c.attr("aria-activedescendant",a[0].id)},0)},dehover_node:function(a){return a=this.get_node(a,!0),a&&a.length&&a.children(".jstree-hovered").length?(a.children(".jstree-anchor").removeClass("jstree-hovered"),void this.trigger("dehover_node",{node:this.get_node(a)})):!1},select_node:function(b,c,d,e){var f,g,h,i;if(a.isArray(b)){for(b=b.slice(),g=0,h=b.length;h>g;g++)this.select_node(b[g],c,d,e);return!0}return b=this.get_node(b),b&&b.id!==a.jstree.root?(f=this.get_node(b,!0),void(b.state.selected||(b.state.selected=!0,this._data.core.selected.push(b.id),d||(f=this._open_to(b)),f&&f.length&&f.attr("aria-selected",!0).children(".jstree-anchor").addClass("jstree-clicked"),this.trigger("select_node",{node:b,selected:this._data.core.selected,event:e}),c||this.trigger("changed",{action:"select_node",node:b,selected:this._data.core.selected,event:e})))):!1},deselect_node:function(b,c,d){var e,f,g;if(a.isArray(b)){for(b=b.slice(),e=0,f=b.length;f>e;e++)this.deselect_node(b[e],c,d);return!0}return b=this.get_node(b),b&&b.id!==a.jstree.root?(g=this.get_node(b,!0),void(b.state.selected&&(b.state.selected=!1,this._data.core.selected=a.vakata.array_remove_item(this._data.core.selected,b.id),g.length&&g.attr("aria-selected",!1).children(".jstree-anchor").removeClass("jstree-clicked"),this.trigger("deselect_node",{node:b,selected:this._data.core.selected,event:d}),c||this.trigger("changed",{action:"deselect_node",node:b,selected:this._data.core.selected,event:d})))):!1},select_all:function(b){var c=this._data.core.selected.concat([]),d,e;for(this._data.core.selected=this._model.data[a.jstree.root].children_d.concat(),d=0,e=this._data.core.selected.length;e>d;d++)this._model.data[this._data.core.selected[d]]&&(this._model.data[this._data.core.selected[d]].state.selected=!0);this.redraw(!0),this.trigger("select_all",{selected:this._data.core.selected}),b||this.trigger("changed",{action:"select_all",selected:this._data.core.selected,old_selection:c})},deselect_all:function(a){var b=this._data.core.selected.concat([]),c,d;for(c=0,d=this._data.core.selected.length;d>c;c++)this._model.data[this._data.core.selected[c]]&&(this._model.data[this._data.core.selected[c]].state.selected=!1);this._data.core.selected=[],this.element.find(".jstree-clicked").removeClass("jstree-clicked").parent().attr("aria-selected",!1),this.trigger("deselect_all",{selected:this._data.core.selected,node:b}),a||this.trigger("changed",{action:"deselect_all",selected:this._data.core.selected,old_selection:b})},is_selected:function(b){return b=this.get_node(b),b&&b.id!==a.jstree.root?b.state.selected:!1},get_selected:function(b){return b?a.map(this._data.core.selected,a.proxy(function(a){return this.get_node(a)},this)):this._data.core.selected.slice()},get_top_selected:function(b){var c=this.get_selected(!0),d={},e,f,g,h;for(e=0,f=c.length;f>e;e++)d[c[e].id]=c[e];for(e=0,f=c.length;f>e;e++)for(g=0,h=c[e].children_d.length;h>g;g++)d[c[e].children_d[g]]&&delete d[c[e].children_d[g]];c=[];for(e in d)d.hasOwnProperty(e)&&c.push(e);return b?a.map(c,a.proxy(function(a){return this.get_node(a)},this)):c},get_bottom_selected:function(b){var c=this.get_selected(!0),d=[],e,f;for(e=0,f=c.length;f>e;e++)c[e].children.length||d.push(c[e].id);return b?a.map(d,a.proxy(function(a){return this.get_node(a)},this)):d},get_state:function(){var b={core:{open:[],loaded:[],scroll:{left:this.element.scrollLeft(),top:this.element.scrollTop()},selected:[]}},c;for(c in this._model.data)this._model.data.hasOwnProperty(c)&&c!==a.jstree.root&&(this._model.data[c].state.loaded&&this.settings.core.loaded_state&&b.core.loaded.push(c),this._model.data[c].state.opened&&b.core.open.push(c),this._model.data[c].state.selected&&b.core.selected.push(c));return b},set_state:function(c,d){if(c){if(c.core&&c.core.selected&&c.core.initial_selection===b&&(c.core.initial_selection=this._data.core.selected.concat([]).sort().join(",")),c.core){var e,f,g,h,i;if(c.core.loaded)return this.settings.core.loaded_state&&a.isArray(c.core.loaded)&&c.core.loaded.length?this._load_nodes(c.core.loaded,function(a){delete c.core.loaded,this.set_state(c,d)}):(delete c.core.loaded,this.set_state(c,d)),!1;if(c.core.open)return a.isArray(c.core.open)&&c.core.open.length?this._load_nodes(c.core.open,function(a){this.open_node(a,!1,0),delete c.core.open,this.set_state(c,d)}):(delete c.core.open,this.set_state(c,d)),!1;if(c.core.scroll)return c.core.scroll&&c.core.scroll.left!==b&&this.element.scrollLeft(c.core.scroll.left),c.core.scroll&&c.core.scroll.top!==b&&this.element.scrollTop(c.core.scroll.top),delete c.core.scroll,this.set_state(c,d),!1;if(c.core.selected)return h=this,(c.core.initial_selection===b||c.core.initial_selection===this._data.core.selected.concat([]).sort().join(","))&&(this.deselect_all(),a.each(c.core.selected,function(a,b){h.select_node(b,!1,!0)})),delete c.core.initial_selection,delete c.core.selected,this.set_state(c,d),!1;for(i in c)c.hasOwnProperty(i)&&"core"!==i&&-1===a.inArray(i,this.settings.plugins)&&delete c[i];if(a.isEmptyObject(c.core))return delete c.core,this.set_state(c,d),!1}return a.isEmptyObject(c)?(c=null,d&&d.call(this),this.trigger("set_state"),!1):!0}return!1},refresh:function(b,c){this._data.core.state=c===!0?{}:this.get_state(),c&&a.isFunction(c)&&(this._data.core.state=c.call(this,this._data.core.state)),this._cnt=0,this._model.data={},this._model.data[a.jstree.root]={id:a.jstree.root,parent:null,parents:[],children:[],children_d:[],state:{loaded:!1}},this._data.core.selected=[],this._data.core.last_clicked=null,this._data.core.focused=null;var d=this.get_container_ul()[0].className;b||(this.element.html("<ul class='"+d+"' role='group'><li class='jstree-initial-node jstree-loading jstree-leaf jstree-last' role='treeitem' id='j"+this._id+"_loading'><i class='jstree-icon jstree-ocl'></i><a class='jstree-anchor' href='#'><i class='jstree-icon jstree-themeicon-hidden'></i>"+this.get_string("Loading ...")+"</a></li></ul>"),this.element.attr("aria-activedescendant","j"+this._id+"_loading")),this.load_node(a.jstree.root,function(b,c){c&&(this.get_container_ul()[0].className=d,this._firstChild(this.get_container_ul()[0])&&this.element.attr("aria-activedescendant",this._firstChild(this.get_container_ul()[0]).id),this.set_state(a.extend(!0,{},this._data.core.state),function(){this.trigger("refresh")})),this._data.core.state=null})},refresh_node:function(b){if(b=this.get_node(b),!b||b.id===a.jstree.root)return!1;var c=[],d=[],e=this._data.core.selected.concat([]);d.push(b.id),b.state.opened===!0&&c.push(b.id),this.get_node(b,!0).find(".jstree-open").each(function(){d.push(this.id),c.push(this.id)}),this._load_nodes(d,a.proxy(function(a){this.open_node(c,!1,0),this.select_node(e),this.trigger("refresh_node",{node:b,nodes:a})},this),!1,!0)},set_id:function(b,c){if(b=this.get_node(b),!b||b.id===a.jstree.root)return!1;var d,e,f=this._model.data,g=b.id;for(c=c.toString(),f[b.parent].children[a.inArray(b.id,f[b.parent].children)]=c,d=0,e=b.parents.length;e>d;d++)f[b.parents[d]].children_d[a.inArray(b.id,f[b.parents[d]].children_d)]=c;for(d=0,e=b.children.length;e>d;d++)f[b.children[d]].parent=c;for(d=0,e=b.children_d.length;e>d;d++)f[b.children_d[d]].parents[a.inArray(b.id,f[b.children_d[d]].parents)]=c;return d=a.inArray(b.id,this._data.core.selected),-1!==d&&(this._data.core.selected[d]=c),d=this.get_node(b.id,!0),d&&(d.attr("id",c),this.element.attr("aria-activedescendant")===b.id&&this.element.attr("aria-activedescendant",c)),delete f[b.id],b.id=c,b.li_attr.id=c,f[c]=b,this.trigger("set_id",{node:b,"new":b.id,old:g}),!0},get_text:function(b){return b=this.get_node(b),b&&b.id!==a.jstree.root?b.text:!1},set_text:function(b,c){var d,e;if(a.isArray(b)){for(b=b.slice(),d=0,e=b.length;e>d;d++)this.set_text(b[d],c);return!0}return b=this.get_node(b),b&&b.id!==a.jstree.root?(b.text=c,this.get_node(b,!0).length&&this.redraw_node(b.id),this.trigger("set_text",{obj:b,text:c}),!0):!1},get_json:function(b,c,d){if(b=this.get_node(b||a.jstree.root),!b)return!1;c&&c.flat&&!d&&(d=[]);var e={id:b.id,text:b.text,icon:this.get_icon(b),li_attr:a.extend(!0,{},b.li_attr),a_attr:a.extend(!0,{},b.a_attr),state:{},data:c&&c.no_data?!1:a.extend(!0,a.isArray(b.data)?[]:{},b.data)},f,g;if(c&&c.flat?e.parent=b.parent:e.children=[],c&&c.no_state)delete e.state;else for(f in b.state)b.state.hasOwnProperty(f)&&(e.state[f]=b.state[f]);if(c&&c.no_li_attr&&delete e.li_attr,c&&c.no_a_attr&&delete e.a_attr,c&&c.no_id&&(delete e.id,e.li_attr&&e.li_attr.id&&delete e.li_attr.id,e.a_attr&&e.a_attr.id&&delete e.a_attr.id),c&&c.flat&&b.id!==a.jstree.root&&d.push(e),!c||!c.no_children)for(f=0,g=b.children.length;g>f;f++)c&&c.flat?this.get_json(b.children[f],c,d):e.children.push(this.get_json(b.children[f],c));return c&&c.flat?d:b.id===a.jstree.root?e.children:e},create_node:function(c,d,e,f,g){if(null===c&&(c=a.jstree.root),c=this.get_node(c),!c)return!1;if(e=e===b?"last":e,!e.toString().match(/^(before|after)$/)&&!g&&!this.is_loaded(c))return this.load_node(c,function(){this.create_node(c,d,e,f,!0)});d||(d={text:this.get_string("New node")}),d="string"==typeof d?{text:d}:a.extend(!0,{},d),d.text===b&&(d.text=this.get_string("New node"));var h,i,j,k;switch(c.id===a.jstree.root&&("before"===e&&(e="first"),"after"===e&&(e="last")),e){case"before":h=this.get_node(c.parent),e=a.inArray(c.id,h.children),c=h;break;case"after":h=this.get_node(c.parent),e=a.inArray(c.id,h.children)+1,c=h;break;case"inside":case"first":e=0;break;case"last":e=c.children.length;break;default:e||(e=0)}if(e>c.children.length&&(e=c.children.length),d.id||(d.id=!0),!this.check("create_node",d,c,e))return this.settings.core.error.call(this,this._data.core.last_error),!1;if(d.id===!0&&delete d.id,d=this._parse_model_from_json(d,c.id,c.parents.concat()),!d)return!1;for(h=this.get_node(d),i=[],i.push(d),i=i.concat(h.children_d),this.trigger("model",{nodes:i,parent:c.id}),c.children_d=c.children_d.concat(i),j=0,k=c.parents.length;k>j;j++)this._model.data[c.parents[j]].children_d=this._model.data[c.parents[j]].children_d.concat(i);for(d=h,h=[],j=0,k=c.children.length;k>j;j++)h[j>=e?j+1:j]=c.children[j];return h[e]=d.id,c.children=h,this.redraw_node(c,!0),this.trigger("create_node",{node:this.get_node(d),parent:c.id,position:e}),f&&f.call(this,this.get_node(d)),d.id},rename_node:function(b,c){var d,e,f;if(a.isArray(b)){for(b=b.slice(),d=0,e=b.length;e>d;d++)this.rename_node(b[d],c);return!0}return b=this.get_node(b),b&&b.id!==a.jstree.root?(f=b.text,this.check("rename_node",b,this.get_parent(b),c)?(this.set_text(b,c),this.trigger("rename_node",{node:b,text:c,old:f}),!0):(this.settings.core.error.call(this,this._data.core.last_error),!1)):!1},delete_node:function(b){var c,d,e,f,g,h,i,j,k,l,m,n;if(a.isArray(b)){for(b=b.slice(),c=0,d=b.length;d>c;c++)this.delete_node(b[c]);return!0}if(b=this.get_node(b),!b||b.id===a.jstree.root)return!1;if(e=this.get_node(b.parent),f=a.inArray(b.id,e.children),l=!1,!this.check("delete_node",b,e,f))return this.settings.core.error.call(this,this._data.core.last_error),!1;for(-1!==f&&(e.children=a.vakata.array_remove(e.children,f)),g=b.children_d.concat([]),g.push(b.id),h=0,i=b.parents.length;i>h;h++)this._model.data[b.parents[h]].children_d=a.vakata.array_filter(this._model.data[b.parents[h]].children_d,function(b){return-1===a.inArray(b,g)});for(j=0,k=g.length;k>j;j++)if(this._model.data[g[j]].state.selected){l=!0;break}for(l&&(this._data.core.selected=a.vakata.array_filter(this._data.core.selected,function(b){return-1===a.inArray(b,g)})),this.trigger("delete_node",{node:b,parent:e.id}),l&&this.trigger("changed",{action:"delete_node",node:b,selected:this._data.core.selected,parent:e.id}),j=0,k=g.length;k>j;j++)delete this._model.data[g[j]];return-1!==a.inArray(this._data.core.focused,g)&&(this._data.core.focused=null,m=this.element[0].scrollTop,n=this.element[0].scrollLeft,e.id===a.jstree.root?this._model.data[a.jstree.root].children[0]&&this.get_node(this._model.data[a.jstree.root].children[0],!0).children(".jstree-anchor").focus():this.get_node(e,!0).children(".jstree-anchor").focus(),this.element[0].scrollTop=m,this.element[0].scrollLeft=n),this.redraw_node(e,!0),!0},check:function(b,c,d,e,f){c=c&&c.id?c:this.get_node(c),d=d&&d.id?d:this.get_node(d);var g=b.match(/^move_node|copy_node|create_node$/i)?d:c,h=this.settings.core.check_callback;return"move_node"!==b&&"copy_node"!==b||f&&f.is_multi||c.id!==d.id&&("move_node"!==b||a.inArray(c.id,d.children)!==e)&&-1===a.inArray(d.id,c.children_d)?(g&&g.data&&(g=g.data),g&&g.functions&&(g.functions[b]===!1||g.functions[b]===!0)?(g.functions[b]===!1&&(this._data.core.last_error={error:"check",plugin:"core",id:"core_02",reason:"Node data prevents function: "+b,data:JSON.stringify({chk:b,pos:e,obj:c&&c.id?c.id:!1,par:d&&d.id?d.id:!1})}),g.functions[b]):h===!1||a.isFunction(h)&&h.call(this,b,c,d,e,f)===!1||h&&h[b]===!1?(this._data.core.last_error={error:"check",plugin:"core",id:"core_03",reason:"User config for core.check_callback prevents function: "+b,data:JSON.stringify({chk:b,pos:e,obj:c&&c.id?c.id:!1,par:d&&d.id?d.id:!1})},!1):!0):(this._data.core.last_error={error:"check",plugin:"core",id:"core_01",reason:"Moving parent inside child",data:JSON.stringify({chk:b,pos:e,obj:c&&c.id?c.id:!1,par:d&&d.id?d.id:!1})},!1)},last_error:function(){return this._data.core.last_error},move_node:function(c,d,e,f,g,h,i){var j,k,l,m,n,o,p,q,r,s,t,u,v,w;if(d=this.get_node(d),e=e===b?0:e,!d)return!1;if(!e.toString().match(/^(before|after)$/)&&!g&&!this.is_loaded(d))return this.load_node(d,function(){this.move_node(c,d,e,f,!0,!1,i)});if(a.isArray(c)){if(1!==c.length){for(j=0,k=c.length;k>j;j++)(r=this.move_node(c[j],d,e,f,g,!1,i))&&(d=r,e="after");return this.redraw(),!0}c=c[0]}if(c=c&&c.id?c:this.get_node(c),!c||c.id===a.jstree.root)return!1;if(l=(c.parent||a.jstree.root).toString(),n=e.toString().match(/^(before|after)$/)&&d.id!==a.jstree.root?this.get_node(d.parent):d,o=i?i:this._model.data[c.id]?this:a.jstree.reference(c.id),p=!o||!o._id||this._id!==o._id,m=o&&o._id&&l&&o._model.data[l]&&o._model.data[l].children?a.inArray(c.id,o._model.data[l].children):-1,o&&o._id&&(c=o._model.data[c.id]),p)return(r=this.copy_node(c,d,e,f,g,!1,i))?(o&&o.delete_node(c),r):!1;switch(d.id===a.jstree.root&&("before"===e&&(e="first"),"after"===e&&(e="last")),e){case"before":e=a.inArray(d.id,n.children);break;case"after":e=a.inArray(d.id,n.children)+1;break;case"inside":case"first":e=0;break;case"last":e=n.children.length;break;default:e||(e=0)}if(e>n.children.length&&(e=n.children.length),!this.check("move_node",c,n,e,{core:!0,origin:i,is_multi:o&&o._id&&o._id!==this._id,is_foreign:!o||!o._id}))return this.settings.core.error.call(this,this._data.core.last_error),!1;if(c.parent===n.id){for(q=n.children.concat(),r=a.inArray(c.id,q),-1!==r&&(q=a.vakata.array_remove(q,r),e>r&&e--),r=[],s=0,t=q.length;t>s;s++)r[s>=e?s+1:s]=q[s];r[e]=c.id,n.children=r,this._node_changed(n.id),this.redraw(n.id===a.jstree.root)}else{for(r=c.children_d.concat(),r.push(c.id),s=0,t=c.parents.length;t>s;s++){for(q=[],w=o._model.data[c.parents[s]].children_d,u=0,v=w.length;v>u;u++)-1===a.inArray(w[u],r)&&q.push(w[u]);o._model.data[c.parents[s]].children_d=q}for(o._model.data[l].children=a.vakata.array_remove_item(o._model.data[l].children,c.id),s=0,t=n.parents.length;t>s;s++)this._model.data[n.parents[s]].children_d=this._model.data[n.parents[s]].children_d.concat(r);for(q=[],s=0,t=n.children.length;t>s;s++)q[s>=e?s+1:s]=n.children[s];for(q[e]=c.id,n.children=q,n.children_d.push(c.id),n.children_d=n.children_d.concat(c.children_d),c.parent=n.id,r=n.parents.concat(),r.unshift(n.id),w=c.parents.length,c.parents=r,r=r.concat(),s=0,t=c.children_d.length;t>s;s++)this._model.data[c.children_d[s]].parents=this._model.data[c.children_d[s]].parents.slice(0,-1*w),Array.prototype.push.apply(this._model.data[c.children_d[s]].parents,r);(l===a.jstree.root||n.id===a.jstree.root)&&(this._model.force_full_redraw=!0),this._model.force_full_redraw||(this._node_changed(l),this._node_changed(n.id)),h||this.redraw()}return f&&f.call(this,c,n,e),this.trigger("move_node",{node:c,parent:n.id,position:e,old_parent:l,old_position:m,is_multi:o&&o._id&&o._id!==this._id,is_foreign:!o||!o._id,old_instance:o,new_instance:this}),c.id},copy_node:function(c,d,e,f,g,h,i){var j,k,l,m,n,o,p,q,r,s,t;if(d=this.get_node(d),e=e===b?0:e,!d)return!1;if(!e.toString().match(/^(before|after)$/)&&!g&&!this.is_loaded(d))return this.load_node(d,function(){this.copy_node(c,d,e,f,!0,!1,i)});if(a.isArray(c)){if(1!==c.length){for(j=0,k=c.length;k>j;j++)(m=this.copy_node(c[j],d,e,f,g,!0,i))&&(d=m,e="after");return this.redraw(),!0}c=c[0]}if(c=c&&c.id?c:this.get_node(c),!c||c.id===a.jstree.root)return!1;switch(q=(c.parent||a.jstree.root).toString(),r=e.toString().match(/^(before|after)$/)&&d.id!==a.jstree.root?this.get_node(d.parent):d,s=i?i:this._model.data[c.id]?this:a.jstree.reference(c.id),t=!s||!s._id||this._id!==s._id,s&&s._id&&(c=s._model.data[c.id]),d.id===a.jstree.root&&("before"===e&&(e="first"),"after"===e&&(e="last")),e){case"before":e=a.inArray(d.id,r.children);break;case"after":e=a.inArray(d.id,r.children)+1;break;case"inside":case"first":e=0;break;case"last":e=r.children.length;break;default:e||(e=0)}if(e>r.children.length&&(e=r.children.length),!this.check("copy_node",c,r,e,{core:!0,origin:i,is_multi:s&&s._id&&s._id!==this._id,is_foreign:!s||!s._id}))return this.settings.core.error.call(this,this._data.core.last_error),!1;if(p=s?s.get_json(c,{no_id:!0,no_data:!0,no_state:!0}):c,!p)return!1;if(p.id===!0&&delete p.id,p=this._parse_model_from_json(p,r.id,r.parents.concat()),!p)return!1;for(m=this.get_node(p),c&&c.state&&c.state.loaded===!1&&(m.state.loaded=!1),l=[],l.push(p),l=l.concat(m.children_d),this.trigger("model",{nodes:l,parent:r.id}),n=0,o=r.parents.length;o>n;n++)this._model.data[r.parents[n]].children_d=this._model.data[r.parents[n]].children_d.concat(l);for(l=[],n=0,o=r.children.length;o>n;n++)l[n>=e?n+1:n]=r.children[n];return l[e]=m.id,r.children=l,r.children_d.push(m.id),r.children_d=r.children_d.concat(m.children_d),r.id===a.jstree.root&&(this._model.force_full_redraw=!0),this._model.force_full_redraw||this._node_changed(r.id),h||this.redraw(r.id===a.jstree.root),f&&f.call(this,m,r,e),this.trigger("copy_node",{node:m,original:c,parent:r.id,position:e,old_parent:q,old_position:s&&s._id&&q&&s._model.data[q]&&s._model.data[q].children?a.inArray(c.id,s._model.data[q].children):-1,is_multi:s&&s._id&&s._id!==this._id,is_foreign:!s||!s._id,old_instance:s,new_instance:this}),m.id},cut:function(b){if(b||(b=this._data.core.selected.concat()),a.isArray(b)||(b=[b]),!b.length)return!1;var c=[],g,h,i;for(h=0,i=b.length;i>h;h++)g=this.get_node(b[h]),g&&g.id&&g.id!==a.jstree.root&&c.push(g);
return c.length?(d=c,f=this,e="move_node",void this.trigger("cut",{node:b})):!1},copy:function(b){if(b||(b=this._data.core.selected.concat()),a.isArray(b)||(b=[b]),!b.length)return!1;var c=[],g,h,i;for(h=0,i=b.length;i>h;h++)g=this.get_node(b[h]),g&&g.id&&g.id!==a.jstree.root&&c.push(g);return c.length?(d=c,f=this,e="copy_node",void this.trigger("copy",{node:b})):!1},get_buffer:function(){return{mode:e,node:d,inst:f}},can_paste:function(){return e!==!1&&d!==!1},paste:function(a,b){return a=this.get_node(a),a&&e&&e.match(/^(copy_node|move_node)$/)&&d?(this[e](d,a,b,!1,!1,!1,f)&&this.trigger("paste",{parent:a.id,node:d,mode:e}),d=!1,e=!1,void(f=!1)):!1},clear_buffer:function(){d=!1,e=!1,f=!1,this.trigger("clear_buffer")},edit:function(b,c,d){var e,f,g,h,j,k,l,m,n,o=!1;return(b=this.get_node(b))?this.check("edit",b,this.get_parent(b))?(n=b,c="string"==typeof c?c:b.text,this.set_text(b,""),b=this._open_to(b),n.text=c,e=this._data.core.rtl,f=this.element.width(),this._data.core.focused=n.id,g=b.children(".jstree-anchor").focus(),h=a("<span>"),j=c,k=a("<div />",{css:{position:"absolute",top:"-200px",left:e?"0px":"-1000px",visibility:"hidden"}}).appendTo(i.body),l=a("<input />",{value:j,"class":"jstree-rename-input",css:{padding:"0",border:"1px solid silver","box-sizing":"border-box",display:"inline-block",height:this._data.core.li_height+"px",lineHeight:this._data.core.li_height+"px",width:"150px"},blur:a.proxy(function(c){c.stopImmediatePropagation(),c.preventDefault();var e=h.children(".jstree-rename-input"),f=e.val(),i=this.settings.core.force_text,m;""===f&&(f=j),k.remove(),h.replaceWith(g),h.remove(),j=i?j:a("<div></div>").append(a.parseHTML(j)).html(),b=this.get_node(b),this.set_text(b,j),m=!!this.rename_node(b,i?a("<div></div>").text(f).text():a("<div></div>").append(a.parseHTML(f)).html()),m||this.set_text(b,j),this._data.core.focused=n.id,setTimeout(a.proxy(function(){var a=this.get_node(n.id,!0);a.length&&(this._data.core.focused=n.id,a.children(".jstree-anchor").focus())},this),0),d&&d.call(this,n,m,o),l=null},this),keydown:function(a){var b=a.which;27===b&&(o=!0,this.value=j),(27===b||13===b||37===b||38===b||39===b||40===b||32===b)&&a.stopImmediatePropagation(),(27===b||13===b)&&(a.preventDefault(),this.blur())},click:function(a){a.stopImmediatePropagation()},mousedown:function(a){a.stopImmediatePropagation()},keyup:function(a){l.width(Math.min(k.text("pW"+this.value).width(),f))},keypress:function(a){return 13===a.which?!1:void 0}}),m={fontFamily:g.css("fontFamily")||"",fontSize:g.css("fontSize")||"",fontWeight:g.css("fontWeight")||"",fontStyle:g.css("fontStyle")||"",fontStretch:g.css("fontStretch")||"",fontVariant:g.css("fontVariant")||"",letterSpacing:g.css("letterSpacing")||"",wordSpacing:g.css("wordSpacing")||""},h.attr("class",g.attr("class")).append(g.contents().clone()).append(l),g.replaceWith(h),k.css(m),l.css(m).width(Math.min(k.text("pW"+l[0].value).width(),f))[0].select(),void a(i).one("mousedown.jstree touchstart.jstree dnd_start.vakata",function(b){l&&b.target!==l&&a(l).blur()})):(this.settings.core.error.call(this,this._data.core.last_error),!1):!1},set_theme:function(b,c){if(!b)return!1;if(c===!0){var d=this.settings.core.themes.dir;d||(d=a.jstree.path+"/themes"),c=d+"/"+b+"/style.css"}c&&-1===a.inArray(c,g)&&(a("head").append('<link rel="stylesheet" href="'+c+'" type="text/css" />'),g.push(c)),this._data.core.themes.name&&this.element.removeClass("jstree-"+this._data.core.themes.name),this._data.core.themes.name=b,this.element.addClass("jstree-"+b),this.element[this.settings.core.themes.responsive?"addClass":"removeClass"]("jstree-"+b+"-responsive"),this.trigger("set_theme",{theme:b})},get_theme:function(){return this._data.core.themes.name},set_theme_variant:function(a){this._data.core.themes.variant&&this.element.removeClass("jstree-"+this._data.core.themes.name+"-"+this._data.core.themes.variant),this._data.core.themes.variant=a,a&&this.element.addClass("jstree-"+this._data.core.themes.name+"-"+this._data.core.themes.variant)},get_theme_variant:function(){return this._data.core.themes.variant},show_stripes:function(){this._data.core.themes.stripes=!0,this.get_container_ul().addClass("jstree-striped"),this.trigger("show_stripes")},hide_stripes:function(){this._data.core.themes.stripes=!1,this.get_container_ul().removeClass("jstree-striped"),this.trigger("hide_stripes")},toggle_stripes:function(){this._data.core.themes.stripes?this.hide_stripes():this.show_stripes()},show_dots:function(){this._data.core.themes.dots=!0,this.get_container_ul().removeClass("jstree-no-dots"),this.trigger("show_dots")},hide_dots:function(){this._data.core.themes.dots=!1,this.get_container_ul().addClass("jstree-no-dots"),this.trigger("hide_dots")},toggle_dots:function(){this._data.core.themes.dots?this.hide_dots():this.show_dots()},show_icons:function(){this._data.core.themes.icons=!0,this.get_container_ul().removeClass("jstree-no-icons"),this.trigger("show_icons")},hide_icons:function(){this._data.core.themes.icons=!1,this.get_container_ul().addClass("jstree-no-icons"),this.trigger("hide_icons")},toggle_icons:function(){this._data.core.themes.icons?this.hide_icons():this.show_icons()},show_ellipsis:function(){this._data.core.themes.ellipsis=!0,this.get_container_ul().addClass("jstree-ellipsis"),this.trigger("show_ellipsis")},hide_ellipsis:function(){this._data.core.themes.ellipsis=!1,this.get_container_ul().removeClass("jstree-ellipsis"),this.trigger("hide_ellipsis")},toggle_ellipsis:function(){this._data.core.themes.ellipsis?this.hide_ellipsis():this.show_ellipsis()},set_icon:function(c,d){var e,f,g,h;if(a.isArray(c)){for(c=c.slice(),e=0,f=c.length;f>e;e++)this.set_icon(c[e],d);return!0}return c=this.get_node(c),c&&c.id!==a.jstree.root?(h=c.icon,c.icon=d===!0||null===d||d===b||""===d?!0:d,g=this.get_node(c,!0).children(".jstree-anchor").children(".jstree-themeicon"),d===!1?(g.removeClass("jstree-themeicon-custom "+h).css("background","").removeAttr("rel"),this.hide_icon(c)):d===!0||null===d||d===b||""===d?(g.removeClass("jstree-themeicon-custom "+h).css("background","").removeAttr("rel"),h===!1&&this.show_icon(c)):-1===d.indexOf("/")&&-1===d.indexOf(".")?(g.removeClass(h).css("background",""),g.addClass(d+" jstree-themeicon-custom").attr("rel",d),h===!1&&this.show_icon(c)):(g.removeClass(h).css("background",""),g.addClass("jstree-themeicon-custom").css("background","url('"+d+"') center center no-repeat").attr("rel",d),h===!1&&this.show_icon(c)),!0):!1},get_icon:function(b){return b=this.get_node(b),b&&b.id!==a.jstree.root?b.icon:!1},hide_icon:function(b){var c,d;if(a.isArray(b)){for(b=b.slice(),c=0,d=b.length;d>c;c++)this.hide_icon(b[c]);return!0}return b=this.get_node(b),b&&b!==a.jstree.root?(b.icon=!1,this.get_node(b,!0).children(".jstree-anchor").children(".jstree-themeicon").addClass("jstree-themeicon-hidden"),!0):!1},show_icon:function(b){var c,d,e;if(a.isArray(b)){for(b=b.slice(),c=0,d=b.length;d>c;c++)this.show_icon(b[c]);return!0}return b=this.get_node(b),b&&b!==a.jstree.root?(e=this.get_node(b,!0),b.icon=e.length?e.children(".jstree-anchor").children(".jstree-themeicon").attr("rel"):!0,b.icon||(b.icon=!0),e.children(".jstree-anchor").children(".jstree-themeicon").removeClass("jstree-themeicon-hidden"),!0):!1}},a.vakata={},a.vakata.attributes=function(b,c){b=a(b)[0];var d=c?{}:[];return b&&b.attributes&&a.each(b.attributes,function(b,e){-1===a.inArray(e.name.toLowerCase(),["style","contenteditable","hasfocus","tabindex"])&&null!==e.value&&""!==a.trim(e.value)&&(c?d[e.name]=e.value:d.push(e.name))}),d},a.vakata.array_unique=function(a){var c=[],d,e,f,g={};for(d=0,f=a.length;f>d;d++)g[a[d]]===b&&(c.push(a[d]),g[a[d]]=!0);return c},a.vakata.array_remove=function(a,b){return a.splice(b,1),a},a.vakata.array_remove_item=function(b,c){var d=a.inArray(c,b);return-1!==d?a.vakata.array_remove(b,d):b},a.vakata.array_filter=function(a,b,c,d,e){if(a.filter)return a.filter(b,c);d=[];for(e in a)~~e+""==e+""&&e>=0&&b.call(c,a[e],+e,a)&&d.push(a[e]);return d},a.jstree.plugins.changed=function(a,b){var c=[];this.trigger=function(a,d){var e,f;if(d||(d={}),"changed"===a.replace(".jstree","")){d.changed={selected:[],deselected:[]};var g={};for(e=0,f=c.length;f>e;e++)g[c[e]]=1;for(e=0,f=d.selected.length;f>e;e++)g[d.selected[e]]?g[d.selected[e]]=2:d.changed.selected.push(d.selected[e]);for(e=0,f=c.length;f>e;e++)1===g[c[e]]&&d.changed.deselected.push(c[e]);c=d.selected.slice()}b.trigger.call(this,a,d)},this.refresh=function(a,d){return c=[],b.refresh.apply(this,arguments)}};var j=i.createElement("I");j.className="jstree-icon jstree-checkbox",j.setAttribute("role","presentation"),a.jstree.defaults.checkbox={visible:!0,three_state:!0,whole_node:!0,keep_selected_style:!0,cascade:"",tie_selection:!0,cascade_to_disabled:!0,cascade_to_hidden:!0},a.jstree.plugins.checkbox=function(c,d){this.bind=function(){d.bind.call(this),this._data.checkbox.uto=!1,this._data.checkbox.selected=[],this.settings.checkbox.three_state&&(this.settings.checkbox.cascade="up+down+undetermined"),this.element.on("init.jstree",a.proxy(function(){this._data.checkbox.visible=this.settings.checkbox.visible,this.settings.checkbox.keep_selected_style||this.element.addClass("jstree-checkbox-no-clicked"),this.settings.checkbox.tie_selection&&this.element.addClass("jstree-checkbox-selection")},this)).on("loading.jstree",a.proxy(function(){this[this._data.checkbox.visible?"show_checkboxes":"hide_checkboxes"]()},this)),-1!==this.settings.checkbox.cascade.indexOf("undetermined")&&this.element.on("changed.jstree uncheck_node.jstree check_node.jstree uncheck_all.jstree check_all.jstree move_node.jstree copy_node.jstree redraw.jstree open_node.jstree",a.proxy(function(){this._data.checkbox.uto&&clearTimeout(this._data.checkbox.uto),this._data.checkbox.uto=setTimeout(a.proxy(this._undetermined,this),50)},this)),this.settings.checkbox.tie_selection||this.element.on("model.jstree",a.proxy(function(a,b){var c=this._model.data,d=c[b.parent],e=b.nodes,f,g;for(f=0,g=e.length;g>f;f++)c[e[f]].state.checked=c[e[f]].state.checked||c[e[f]].original&&c[e[f]].original.state&&c[e[f]].original.state.checked,c[e[f]].state.checked&&this._data.checkbox.selected.push(e[f])},this)),(-1!==this.settings.checkbox.cascade.indexOf("up")||-1!==this.settings.checkbox.cascade.indexOf("down"))&&this.element.on("model.jstree",a.proxy(function(b,c){var d=this._model.data,e=d[c.parent],f=c.nodes,g=[],h,i,j,k,l,m,n=this.settings.checkbox.cascade,o=this.settings.checkbox.tie_selection;if(-1!==n.indexOf("down"))if(e.state[o?"selected":"checked"]){for(i=0,j=f.length;j>i;i++)d[f[i]].state[o?"selected":"checked"]=!0;this._data[o?"core":"checkbox"].selected=this._data[o?"core":"checkbox"].selected.concat(f)}else for(i=0,j=f.length;j>i;i++)if(d[f[i]].state[o?"selected":"checked"]){for(k=0,l=d[f[i]].children_d.length;l>k;k++)d[d[f[i]].children_d[k]].state[o?"selected":"checked"]=!0;this._data[o?"core":"checkbox"].selected=this._data[o?"core":"checkbox"].selected.concat(d[f[i]].children_d)}if(-1!==n.indexOf("up")){for(i=0,j=e.children_d.length;j>i;i++)d[e.children_d[i]].children.length||g.push(d[e.children_d[i]].parent);for(g=a.vakata.array_unique(g),k=0,l=g.length;l>k;k++){e=d[g[k]];while(e&&e.id!==a.jstree.root){for(h=0,i=0,j=e.children.length;j>i;i++)h+=d[e.children[i]].state[o?"selected":"checked"];if(h!==j)break;e.state[o?"selected":"checked"]=!0,this._data[o?"core":"checkbox"].selected.push(e.id),m=this.get_node(e,!0),m&&m.length&&m.attr("aria-selected",!0).children(".jstree-anchor").addClass(o?"jstree-clicked":"jstree-checked"),e=this.get_node(e.parent)}}}this._data[o?"core":"checkbox"].selected=a.vakata.array_unique(this._data[o?"core":"checkbox"].selected)},this)).on(this.settings.checkbox.tie_selection?"select_node.jstree":"check_node.jstree",a.proxy(function(b,c){var d=this,e=c.node,f=this._model.data,g=this.get_node(e.parent),h,i,j,k,l=this.settings.checkbox.cascade,m=this.settings.checkbox.tie_selection,n={},o=this._data[m?"core":"checkbox"].selected;for(h=0,i=o.length;i>h;h++)n[o[h]]=!0;if(-1!==l.indexOf("down")){var p=this._cascade_new_checked_state(e.id,!0),q=e.children_d.concat(e.id);for(h=0,i=q.length;i>h;h++)p.indexOf(q[h])>-1?n[q[h]]=!0:delete n[q[h]]}if(-1!==l.indexOf("up"))while(g&&g.id!==a.jstree.root){for(j=0,h=0,i=g.children.length;i>h;h++)j+=f[g.children[h]].state[m?"selected":"checked"];if(j!==i)break;g.state[m?"selected":"checked"]=!0,n[g.id]=!0,k=this.get_node(g,!0),k&&k.length&&k.attr("aria-selected",!0).children(".jstree-anchor").addClass(m?"jstree-clicked":"jstree-checked"),g=this.get_node(g.parent)}o=[];for(h in n)n.hasOwnProperty(h)&&o.push(h);this._data[m?"core":"checkbox"].selected=o},this)).on(this.settings.checkbox.tie_selection?"deselect_all.jstree":"uncheck_all.jstree",a.proxy(function(b,c){var d=this.get_node(a.jstree.root),e=this._model.data,f,g,h;for(f=0,g=d.children_d.length;g>f;f++)h=e[d.children_d[f]],h&&h.original&&h.original.state&&h.original.state.undetermined&&(h.original.state.undetermined=!1)},this)).on(this.settings.checkbox.tie_selection?"deselect_node.jstree":"uncheck_node.jstree",a.proxy(function(a,b){var c=this,d=b.node,e=this.get_node(d,!0),f,g,h,i=this.settings.checkbox.cascade,j=this.settings.checkbox.tie_selection,k=this._data[j?"core":"checkbox"].selected,l={},m=[],n=d.children_d.concat(d.id);if(-1!==i.indexOf("down")){var o=this._cascade_new_checked_state(d.id,!1);k=k.filter(function(a){return-1===n.indexOf(a)||o.indexOf(a)>-1})}if(-1!==i.indexOf("up")&&-1===k.indexOf(d.id)){for(f=0,g=d.parents.length;g>f;f++)h=this._model.data[d.parents[f]],h.state[j?"selected":"checked"]=!1,h&&h.original&&h.original.state&&h.original.state.undetermined&&(h.original.state.undetermined=!1),h=this.get_node(d.parents[f],!0),h&&h.length&&h.attr("aria-selected",!1).children(".jstree-anchor").removeClass(j?"jstree-clicked":"jstree-checked");k=k.filter(function(a){return-1===d.parents.indexOf(a)})}this._data[j?"core":"checkbox"].selected=k},this)),-1!==this.settings.checkbox.cascade.indexOf("up")&&this.element.on("delete_node.jstree",a.proxy(function(b,c){var d=this.get_node(c.parent),e=this._model.data,f,g,h,i,j=this.settings.checkbox.tie_selection;while(d&&d.id!==a.jstree.root&&!d.state[j?"selected":"checked"]){for(h=0,f=0,g=d.children.length;g>f;f++)h+=e[d.children[f]].state[j?"selected":"checked"];if(!(g>0&&h===g))break;d.state[j?"selected":"checked"]=!0,this._data[j?"core":"checkbox"].selected.push(d.id),i=this.get_node(d,!0),i&&i.length&&i.attr("aria-selected",!0).children(".jstree-anchor").addClass(j?"jstree-clicked":"jstree-checked"),d=this.get_node(d.parent)}},this)).on("move_node.jstree",a.proxy(function(b,c){var d=c.is_multi,e=c.old_parent,f=this.get_node(c.parent),g=this._model.data,h,i,j,k,l,m=this.settings.checkbox.tie_selection;if(!d){h=this.get_node(e);while(h&&h.id!==a.jstree.root&&!h.state[m?"selected":"checked"]){for(i=0,j=0,k=h.children.length;k>j;j++)i+=g[h.children[j]].state[m?"selected":"checked"];if(!(k>0&&i===k))break;h.state[m?"selected":"checked"]=!0,this._data[m?"core":"checkbox"].selected.push(h.id),l=this.get_node(h,!0),l&&l.length&&l.attr("aria-selected",!0).children(".jstree-anchor").addClass(m?"jstree-clicked":"jstree-checked"),h=this.get_node(h.parent)}}h=f;while(h&&h.id!==a.jstree.root){for(i=0,j=0,k=h.children.length;k>j;j++)i+=g[h.children[j]].state[m?"selected":"checked"];if(i===k)h.state[m?"selected":"checked"]||(h.state[m?"selected":"checked"]=!0,this._data[m?"core":"checkbox"].selected.push(h.id),l=this.get_node(h,!0),l&&l.length&&l.attr("aria-selected",!0).children(".jstree-anchor").addClass(m?"jstree-clicked":"jstree-checked"));else{if(!h.state[m?"selected":"checked"])break;h.state[m?"selected":"checked"]=!1,this._data[m?"core":"checkbox"].selected=a.vakata.array_remove_item(this._data[m?"core":"checkbox"].selected,h.id),l=this.get_node(h,!0),l&&l.length&&l.attr("aria-selected",!1).children(".jstree-anchor").removeClass(m?"jstree-clicked":"jstree-checked")}h=this.get_node(h.parent)}},this))},this.get_undetermined=function(c){if(-1===this.settings.checkbox.cascade.indexOf("undetermined"))return[];var d,e,f,g,h={},i=this._model.data,j=this.settings.checkbox.tie_selection,k=this._data[j?"core":"checkbox"].selected,l=[],m=this,n=[];for(d=0,e=k.length;e>d;d++)if(i[k[d]]&&i[k[d]].parents)for(f=0,g=i[k[d]].parents.length;g>f;f++){if(h[i[k[d]].parents[f]]!==b)break;i[k[d]].parents[f]!==a.jstree.root&&(h[i[k[d]].parents[f]]=!0,l.push(i[k[d]].parents[f]))}for(this.element.find(".jstree-closed").not(":has(.jstree-children)").each(function(){var c=m.get_node(this),j;if(c)if(c.state.loaded){for(d=0,e=c.children_d.length;e>d;d++)if(j=i[c.children_d[d]],!j.state.loaded&&j.original&&j.original.state&&j.original.state.undetermined&&j.original.state.undetermined===!0)for(h[j.id]===b&&j.id!==a.jstree.root&&(h[j.id]=!0,l.push(j.id)),f=0,g=j.parents.length;g>f;f++)h[j.parents[f]]===b&&j.parents[f]!==a.jstree.root&&(h[j.parents[f]]=!0,l.push(j.parents[f]))}else if(c.original&&c.original.state&&c.original.state.undetermined&&c.original.state.undetermined===!0)for(h[c.id]===b&&c.id!==a.jstree.root&&(h[c.id]=!0,l.push(c.id)),f=0,g=c.parents.length;g>f;f++)h[c.parents[f]]===b&&c.parents[f]!==a.jstree.root&&(h[c.parents[f]]=!0,l.push(c.parents[f]))}),d=0,e=l.length;e>d;d++)i[l[d]].state[j?"selected":"checked"]||n.push(c?i[l[d]]:l[d]);return n},this._undetermined=function(){if(null!==this.element){var a=this.get_undetermined(!1),b,c,d;for(this.element.find(".jstree-undetermined").removeClass("jstree-undetermined"),b=0,c=a.length;c>b;b++)d=this.get_node(a[b],!0),d&&d.length&&d.children(".jstree-anchor").children(".jstree-checkbox").addClass("jstree-undetermined")}},this.redraw_node=function(b,c,e,f){if(b=d.redraw_node.apply(this,arguments)){var g,h,i=null,k=null;for(g=0,h=b.childNodes.length;h>g;g++)if(b.childNodes[g]&&b.childNodes[g].className&&-1!==b.childNodes[g].className.indexOf("jstree-anchor")){i=b.childNodes[g];break}i&&(!this.settings.checkbox.tie_selection&&this._model.data[b.id].state.checked&&(i.className+=" jstree-checked"),k=j.cloneNode(!1),this._model.data[b.id].state.checkbox_disabled&&(k.className+=" jstree-checkbox-disabled"),i.insertBefore(k,i.childNodes[0]))}return e||-1===this.settings.checkbox.cascade.indexOf("undetermined")||(this._data.checkbox.uto&&clearTimeout(this._data.checkbox.uto),this._data.checkbox.uto=setTimeout(a.proxy(this._undetermined,this),50)),b},this.show_checkboxes=function(){this._data.core.themes.checkboxes=!0,this.get_container_ul().removeClass("jstree-no-checkboxes")},this.hide_checkboxes=function(){this._data.core.themes.checkboxes=!1,this.get_container_ul().addClass("jstree-no-checkboxes")},this.toggle_checkboxes=function(){this._data.core.themes.checkboxes?this.hide_checkboxes():this.show_checkboxes()},this.is_undetermined=function(b){b=this.get_node(b);var c=this.settings.checkbox.cascade,d,e,f=this.settings.checkbox.tie_selection,g=this._data[f?"core":"checkbox"].selected,h=this._model.data;if(!b||b.state[f?"selected":"checked"]===!0||-1===c.indexOf("undetermined")||-1===c.indexOf("down")&&-1===c.indexOf("up"))return!1;if(!b.state.loaded&&b.original.state.undetermined===!0)return!0;for(d=0,e=b.children_d.length;e>d;d++)if(-1!==a.inArray(b.children_d[d],g)||!h[b.children_d[d]].state.loaded&&h[b.children_d[d]].original.state.undetermined)return!0;return!1},this.disable_checkbox=function(b){var c,d,e;if(a.isArray(b)){for(b=b.slice(),c=0,d=b.length;d>c;c++)this.disable_checkbox(b[c]);return!0}return b=this.get_node(b),b&&b.id!==a.jstree.root?(e=this.get_node(b,!0),void(b.state.checkbox_disabled||(b.state.checkbox_disabled=!0,e&&e.length&&e.children(".jstree-anchor").children(".jstree-checkbox").addClass("jstree-checkbox-disabled"),this.trigger("disable_checkbox",{node:b})))):!1},this.enable_checkbox=function(b){var c,d,e;if(a.isArray(b)){for(b=b.slice(),c=0,d=b.length;d>c;c++)this.enable_checkbox(b[c]);return!0}return b=this.get_node(b),b&&b.id!==a.jstree.root?(e=this.get_node(b,!0),void(b.state.checkbox_disabled&&(b.state.checkbox_disabled=!1,e&&e.length&&e.children(".jstree-anchor").children(".jstree-checkbox").removeClass("jstree-checkbox-disabled"),this.trigger("enable_checkbox",{node:b})))):!1},this.activate_node=function(b,c){return a(c.target).hasClass("jstree-checkbox-disabled")?!1:(this.settings.checkbox.tie_selection&&(this.settings.checkbox.whole_node||a(c.target).hasClass("jstree-checkbox"))&&(c.ctrlKey=!0),this.settings.checkbox.tie_selection||!this.settings.checkbox.whole_node&&!a(c.target).hasClass("jstree-checkbox")?d.activate_node.call(this,b,c):this.is_disabled(b)?!1:(this.is_checked(b)?this.uncheck_node(b,c):this.check_node(b,c),void this.trigger("activate_node",{node:this.get_node(b)})))},this._cascade_new_checked_state=function(a,b){var c=this,d=this.settings.checkbox.tie_selection,e=this._model.data[a],f=[],g=[],h,i,j;if(!this.settings.checkbox.cascade_to_disabled&&e.state.disabled||!this.settings.checkbox.cascade_to_hidden&&e.state.hidden)j=this.get_checked_descendants(a),e.state[d?"selected":"checked"]&&j.push(e.id),f=f.concat(j);else{if(e.children)for(h=0,i=e.children.length;i>h;h++){var k=e.children[h];j=c._cascade_new_checked_state(k,b),f=f.concat(j),j.indexOf(k)>-1&&g.push(k)}var l=c.get_node(e,!0),m=g.length>0&&g.length<e.children.length;e.original&&e.original.state&&e.original.state.undetermined&&(e.original.state.undetermined=m),m?(e.state[d?"selected":"checked"]=!1,l.attr("aria-selected",!1).children(".jstree-anchor").removeClass(d?"jstree-clicked":"jstree-checked")):b&&g.length===e.children.length?(e.state[d?"selected":"checked"]=b,f.push(e.id),l.attr("aria-selected",!0).children(".jstree-anchor").addClass(d?"jstree-clicked":"jstree-checked")):(e.state[d?"selected":"checked"]=!1,l.attr("aria-selected",!1).children(".jstree-anchor").removeClass(d?"jstree-clicked":"jstree-checked"))}return f},this.get_checked_descendants=function(a){var b=this,c=b.settings.checkbox.tie_selection,d=b._model.data[a];return d.children_d.filter(function(a){return b._model.data[a].state[c?"selected":"checked"]})},this.check_node=function(b,c){if(this.settings.checkbox.tie_selection)return this.select_node(b,!1,!0,c);var d,e,f,g;if(a.isArray(b)){for(b=b.slice(),e=0,f=b.length;f>e;e++)this.check_node(b[e],c);return!0}return b=this.get_node(b),b&&b.id!==a.jstree.root?(d=this.get_node(b,!0),void(b.state.checked||(b.state.checked=!0,this._data.checkbox.selected.push(b.id),d&&d.length&&d.children(".jstree-anchor").addClass("jstree-checked"),this.trigger("check_node",{node:b,selected:this._data.checkbox.selected,event:c})))):!1},this.uncheck_node=function(b,c){if(this.settings.checkbox.tie_selection)return this.deselect_node(b,!1,c);var d,e,f;if(a.isArray(b)){for(b=b.slice(),d=0,e=b.length;e>d;d++)this.uncheck_node(b[d],c);return!0}return b=this.get_node(b),b&&b.id!==a.jstree.root?(f=this.get_node(b,!0),void(b.state.checked&&(b.state.checked=!1,this._data.checkbox.selected=a.vakata.array_remove_item(this._data.checkbox.selected,b.id),f.length&&f.children(".jstree-anchor").removeClass("jstree-checked"),this.trigger("uncheck_node",{node:b,selected:this._data.checkbox.selected,event:c})))):!1},this.check_all=function(){if(this.settings.checkbox.tie_selection)return this.select_all();var b=this._data.checkbox.selected.concat([]),c,d;for(this._data.checkbox.selected=this._model.data[a.jstree.root].children_d.concat(),c=0,d=this._data.checkbox.selected.length;d>c;c++)this._model.data[this._data.checkbox.selected[c]]&&(this._model.data[this._data.checkbox.selected[c]].state.checked=!0);this.redraw(!0),this.trigger("check_all",{selected:this._data.checkbox.selected})},this.uncheck_all=function(){if(this.settings.checkbox.tie_selection)return this.deselect_all();var a=this._data.checkbox.selected.concat([]),b,c;for(b=0,c=this._data.checkbox.selected.length;c>b;b++)this._model.data[this._data.checkbox.selected[b]]&&(this._model.data[this._data.checkbox.selected[b]].state.checked=!1);this._data.checkbox.selected=[],this.element.find(".jstree-checked").removeClass("jstree-checked"),this.trigger("uncheck_all",{selected:this._data.checkbox.selected,node:a})},this.is_checked=function(b){return this.settings.checkbox.tie_selection?this.is_selected(b):(b=this.get_node(b),b&&b.id!==a.jstree.root?b.state.checked:!1)},this.get_checked=function(b){return this.settings.checkbox.tie_selection?this.get_selected(b):b?a.map(this._data.checkbox.selected,a.proxy(function(a){return this.get_node(a)},this)):this._data.checkbox.selected},this.get_top_checked=function(b){if(this.settings.checkbox.tie_selection)return this.get_top_selected(b);var c=this.get_checked(!0),d={},e,f,g,h;for(e=0,f=c.length;f>e;e++)d[c[e].id]=c[e];for(e=0,f=c.length;f>e;e++)for(g=0,h=c[e].children_d.length;h>g;g++)d[c[e].children_d[g]]&&delete d[c[e].children_d[g]];c=[];for(e in d)d.hasOwnProperty(e)&&c.push(e);return b?a.map(c,a.proxy(function(a){return this.get_node(a)},this)):c},this.get_bottom_checked=function(b){if(this.settings.checkbox.tie_selection)return this.get_bottom_selected(b);var c=this.get_checked(!0),d=[],e,f;for(e=0,f=c.length;f>e;e++)c[e].children.length||d.push(c[e].id);return b?a.map(d,a.proxy(function(a){return this.get_node(a)},this)):d},this.load_node=function(b,c){var e,f,g,h,i,j;if(!a.isArray(b)&&!this.settings.checkbox.tie_selection&&(j=this.get_node(b),j&&j.state.loaded))for(e=0,f=j.children_d.length;f>e;e++)this._model.data[j.children_d[e]].state.checked&&(i=!0,this._data.checkbox.selected=a.vakata.array_remove_item(this._data.checkbox.selected,j.children_d[e]));return d.load_node.apply(this,arguments)},this.get_state=function(){var a=d.get_state.apply(this,arguments);return this.settings.checkbox.tie_selection?a:(a.checkbox=this._data.checkbox.selected.slice(),a)},this.set_state=function(b,c){var e=d.set_state.apply(this,arguments);if(e&&b.checkbox){if(!this.settings.checkbox.tie_selection){this.uncheck_all();var f=this;a.each(b.checkbox,function(a,b){f.check_node(b)})}return delete b.checkbox,this.set_state(b,c),!1}return e},this.refresh=function(a,b){return this.settings.checkbox.tie_selection&&(this._data.checkbox.selected=[]),d.refresh.apply(this,arguments)}},a.jstree.defaults.conditionalselect=function(){return!0},a.jstree.plugins.conditionalselect=function(a,b){this.activate_node=function(a,c){return this.settings.conditionalselect.call(this,this.get_node(a),c)?b.activate_node.call(this,a,c):void 0}},a.jstree.defaults.contextmenu={select_node:!0,show_at_node:!0,items:function(b,c){return{create:{separator_before:!1,separator_after:!0,_disabled:!1,label:"Create",action:function(b){var c=a.jstree.reference(b.reference),d=c.get_node(b.reference);c.create_node(d,{},"last",function(a){try{c.edit(a)}catch(b){setTimeout(function(){c.edit(a)},0)}})}},rename:{separator_before:!1,separator_after:!1,_disabled:!1,label:"Rename",action:function(b){var c=a.jstree.reference(b.reference),d=c.get_node(b.reference);c.edit(d)}},remove:{separator_before:!1,icon:!1,separator_after:!1,_disabled:!1,label:"Delete",action:function(b){var c=a.jstree.reference(b.reference),d=c.get_node(b.reference);c.is_selected(d)?c.delete_node(c.get_selected()):c.delete_node(d)}},ccp:{separator_before:!0,icon:!1,separator_after:!1,label:"Edit",action:!1,submenu:{cut:{separator_before:!1,separator_after:!1,label:"Cut",action:function(b){var c=a.jstree.reference(b.reference),d=c.get_node(b.reference);c.is_selected(d)?c.cut(c.get_top_selected()):c.cut(d)}},copy:{separator_before:!1,icon:!1,separator_after:!1,label:"Copy",action:function(b){var c=a.jstree.reference(b.reference),d=c.get_node(b.reference);c.is_selected(d)?c.copy(c.get_top_selected()):c.copy(d)}},paste:{separator_before:!1,icon:!1,_disabled:function(b){return!a.jstree.reference(b.reference).can_paste()},separator_after:!1,label:"Paste",action:function(b){var c=a.jstree.reference(b.reference),d=c.get_node(b.reference);c.paste(d)}}}}}}},a.jstree.plugins.contextmenu=function(c,d){this.bind=function(){d.bind.call(this);var b=0,c=null,e,f;this.element.on("init.jstree loading.jstree ready.jstree",a.proxy(function(){this.get_container_ul().addClass("jstree-contextmenu")},this)).on("contextmenu.jstree",".jstree-anchor",a.proxy(function(a,d){"input"!==a.target.tagName.toLowerCase()&&(a.preventDefault(),b=a.ctrlKey?+new Date:0,(d||c)&&(b=+new Date+1e4),c&&clearTimeout(c),this.is_loading(a.currentTarget)||this.show_contextmenu(a.currentTarget,a.pageX,a.pageY,a))},this)).on("click.jstree",".jstree-anchor",a.proxy(function(c){this._data.contextmenu.visible&&(!b||+new Date-b>250)&&a.vakata.context.hide(),b=0},this)).on("touchstart.jstree",".jstree-anchor",function(b){b.originalEvent&&b.originalEvent.changedTouches&&b.originalEvent.changedTouches[0]&&(e=b.originalEvent.changedTouches[0].clientX,f=b.originalEvent.changedTouches[0].clientY,c=setTimeout(function(){a(b.currentTarget).trigger("contextmenu",!0)},750))}).on("touchmove.vakata.jstree",function(b){c&&b.originalEvent&&b.originalEvent.changedTouches&&b.originalEvent.changedTouches[0]&&(Math.abs(e-b.originalEvent.changedTouches[0].clientX)>10||Math.abs(f-b.originalEvent.changedTouches[0].clientY)>10)&&(clearTimeout(c),a.vakata.context.hide())}).on("touchend.vakata.jstree",function(a){c&&clearTimeout(c)}),a(i).on("context_hide.vakata.jstree",a.proxy(function(b,c){this._data.contextmenu.visible=!1,a(c.reference).removeClass("jstree-context")},this))},this.teardown=function(){this._data.contextmenu.visible&&a.vakata.context.hide(),d.teardown.call(this)},this.show_contextmenu=function(c,d,e,f){if(c=this.get_node(c),!c||c.id===a.jstree.root)return!1;var g=this.settings.contextmenu,h=this.get_node(c,!0),i=h.children(".jstree-anchor"),j=!1,k=!1;(g.show_at_node||d===b||e===b)&&(j=i.offset(),d=j.left,e=j.top+this._data.core.li_height),this.settings.contextmenu.select_node&&!this.is_selected(c)&&this.activate_node(c,f),k=g.items,a.isFunction(k)&&(k=k.call(this,c,a.proxy(function(a){this._show_contextmenu(c,d,e,a)},this))),a.isPlainObject(k)&&this._show_contextmenu(c,d,e,k)},this._show_contextmenu=function(b,c,d,e){var f=this.get_node(b,!0),g=f.children(".jstree-anchor");a(i).one("context_show.vakata.jstree",a.proxy(function(b,c){var d="jstree-contextmenu jstree-"+this.get_theme()+"-contextmenu";a(c.element).addClass(d),g.addClass("jstree-context")},this)),this._data.contextmenu.visible=!0,a.vakata.context.show(g,{x:c,y:d},e),this.trigger("show_contextmenu",{node:b,x:c,y:d})}},function(a){var b=!1,c={element:!1,reference:!1,position_x:0,position_y:0,items:[],html:"",is_visible:!1};a.vakata.context={settings:{hide_onmouseleave:0,icons:!0},_trigger:function(b){a(i).triggerHandler("context_"+b+".vakata",{reference:c.reference,element:c.element,position:{x:c.position_x,y:c.position_y}})},_execute:function(b){return b=c.items[b],b&&(!b._disabled||a.isFunction(b._disabled)&&!b._disabled({item:b,reference:c.reference,element:c.element}))&&b.action?b.action.call(null,{item:b,reference:c.reference,element:c.element,position:{x:c.position_x,y:c.position_y}}):!1},_parse:function(b,d){if(!b)return!1;d||(c.html="",c.items=[]);var e="",f=!1,g;return d&&(e+="<ul>"),a.each(b,function(b,d){return d?(c.items.push(d),!f&&d.separator_before&&(e+="<li class='vakata-context-separator'><a href='#' "+(a.vakata.context.settings.icons?"":'style="margin-left:0px;"')+">&#160;</a></li>"),f=!1,e+="<li class='"+(d._class||"")+(d._disabled===!0||a.isFunction(d._disabled)&&d._disabled({item:d,reference:c.reference,element:c.element})?" vakata-contextmenu-disabled ":"")+"' "+(d.shortcut?" data-shortcut='"+d.shortcut+"' ":"")+">",e+="<a href='#' rel='"+(c.items.length-1)+"' "+(d.title?"title='"+d.title+"'":"")+">",a.vakata.context.settings.icons&&(e+="<i ",d.icon&&(e+=-1!==d.icon.indexOf("/")||-1!==d.icon.indexOf(".")?" style='background:url(\""+d.icon+"\") center center no-repeat' ":" class='"+d.icon+"' "),e+="></i><span class='vakata-contextmenu-sep'>&#160;</span>"),e+=(a.isFunction(d.label)?d.label({item:b,reference:c.reference,element:c.element}):d.label)+(d.shortcut?' <span class="vakata-contextmenu-shortcut vakata-contextmenu-shortcut-'+d.shortcut+'">'+(d.shortcut_label||"")+"</span>":"")+"</a>",
d.submenu&&(g=a.vakata.context._parse(d.submenu,!0),g&&(e+=g)),e+="</li>",void(d.separator_after&&(e+="<li class='vakata-context-separator'><a href='#' "+(a.vakata.context.settings.icons?"":'style="margin-left:0px;"')+">&#160;</a></li>",f=!0))):!0}),e=e.replace(/<li class\='vakata-context-separator'\><\/li\>$/,""),d&&(e+="</ul>"),d||(c.html=e,a.vakata.context._trigger("parse")),e.length>10?e:!1},_show_submenu:function(c){if(c=a(c),c.length&&c.children("ul").length){var d=c.children("ul"),e=c.offset().left,f=e+c.outerWidth(),g=c.offset().top,h=d.width(),i=d.height(),j=a(window).width()+a(window).scrollLeft(),k=a(window).height()+a(window).scrollTop();b?c[f-(h+10+c.outerWidth())<0?"addClass":"removeClass"]("vakata-context-left"):c[f+h>j&&e>j-f?"addClass":"removeClass"]("vakata-context-right"),g+i+10>k&&d.css("bottom","-1px"),c.hasClass("vakata-context-right")?h>e&&d.css("margin-right",e-h):h>j-f&&d.css("margin-left",j-f-h),d.show()}},show:function(d,e,f){var g,h,j,k,l,m,n,o,p=!0;switch(c.element&&c.element.length&&c.element.width(""),p){case!e&&!d:return!1;case!!e&&!!d:c.reference=d,c.position_x=e.x,c.position_y=e.y;break;case!e&&!!d:c.reference=d,g=d.offset(),c.position_x=g.left+d.outerHeight(),c.position_y=g.top;break;case!!e&&!d:c.position_x=e.x,c.position_y=e.y}d&&!f&&a(d).data("vakata_contextmenu")&&(f=a(d).data("vakata_contextmenu")),a.vakata.context._parse(f)&&c.element.html(c.html),c.items.length&&(c.element.appendTo(i.body),h=c.element,j=c.position_x,k=c.position_y,l=h.width(),m=h.height(),n=a(window).width()+a(window).scrollLeft(),o=a(window).height()+a(window).scrollTop(),b&&(j-=h.outerWidth()-a(d).outerWidth(),j<a(window).scrollLeft()+20&&(j=a(window).scrollLeft()+20)),j+l+20>n&&(j=n-(l+20)),k+m+20>o&&(k=o-(m+20)),c.element.css({left:j,top:k}).show().find("a").first().focus().parent().addClass("vakata-context-hover"),c.is_visible=!0,a.vakata.context._trigger("show"))},hide:function(){c.is_visible&&(c.element.hide().find("ul").hide().end().find(":focus").blur().end().detach(),c.is_visible=!1,a.vakata.context._trigger("hide"))}},a(function(){b="rtl"===a(i.body).css("direction");var d=!1;c.element=a("<ul class='vakata-context'></ul>"),c.element.on("mouseenter","li",function(b){b.stopImmediatePropagation(),a.contains(this,b.relatedTarget)||(d&&clearTimeout(d),c.element.find(".vakata-context-hover").removeClass("vakata-context-hover").end(),a(this).siblings().find("ul").hide().end().end().parentsUntil(".vakata-context","li").addBack().addClass("vakata-context-hover"),a.vakata.context._show_submenu(this))}).on("mouseleave","li",function(b){a.contains(this,b.relatedTarget)||a(this).find(".vakata-context-hover").addBack().removeClass("vakata-context-hover")}).on("mouseleave",function(b){a(this).find(".vakata-context-hover").removeClass("vakata-context-hover"),a.vakata.context.settings.hide_onmouseleave&&(d=setTimeout(function(b){return function(){a.vakata.context.hide()}}(this),a.vakata.context.settings.hide_onmouseleave))}).on("click","a",function(b){b.preventDefault(),a(this).blur().parent().hasClass("vakata-context-disabled")||a.vakata.context._execute(a(this).attr("rel"))===!1||a.vakata.context.hide()}).on("keydown","a",function(b){var d=null;switch(b.which){case 13:case 32:b.type="click",b.preventDefault(),a(b.currentTarget).trigger(b);break;case 37:c.is_visible&&(c.element.find(".vakata-context-hover").last().closest("li").first().find("ul").hide().find(".vakata-context-hover").removeClass("vakata-context-hover").end().end().children("a").focus(),b.stopImmediatePropagation(),b.preventDefault());break;case 38:c.is_visible&&(d=c.element.find("ul:visible").addBack().last().children(".vakata-context-hover").removeClass("vakata-context-hover").prevAll("li:not(.vakata-context-separator)").first(),d.length||(d=c.element.find("ul:visible").addBack().last().children("li:not(.vakata-context-separator)").last()),d.addClass("vakata-context-hover").children("a").focus(),b.stopImmediatePropagation(),b.preventDefault());break;case 39:c.is_visible&&(c.element.find(".vakata-context-hover").last().children("ul").show().children("li:not(.vakata-context-separator)").removeClass("vakata-context-hover").first().addClass("vakata-context-hover").children("a").focus(),b.stopImmediatePropagation(),b.preventDefault());break;case 40:c.is_visible&&(d=c.element.find("ul:visible").addBack().last().children(".vakata-context-hover").removeClass("vakata-context-hover").nextAll("li:not(.vakata-context-separator)").first(),d.length||(d=c.element.find("ul:visible").addBack().last().children("li:not(.vakata-context-separator)").first()),d.addClass("vakata-context-hover").children("a").focus(),b.stopImmediatePropagation(),b.preventDefault());break;case 27:a.vakata.context.hide(),b.preventDefault()}}).on("keydown",function(a){a.preventDefault();var b=c.element.find(".vakata-contextmenu-shortcut-"+a.which).parent();b.parent().not(".vakata-context-disabled")&&b.click()}),a(i).on("mousedown.vakata.jstree",function(b){c.is_visible&&c.element[0]!==b.target&&!a.contains(c.element[0],b.target)&&a.vakata.context.hide()}).on("context_show.vakata.jstree",function(a,d){c.element.find("li:has(ul)").children("a").addClass("vakata-context-parent"),b&&c.element.addClass("vakata-context-rtl").css("direction","rtl"),c.element.find("ul").hide().end()})})}(a),a.jstree.defaults.dnd={copy:!0,open_timeout:500,is_draggable:!0,check_while_dragging:!0,always_copy:!1,inside_pos:0,drag_selection:!0,touch:!0,large_drop_target:!1,large_drag_target:!1,use_html5:!1};var k,l;a.jstree.plugins.dnd=function(b,c){this.init=function(a,b){c.init.call(this,a,b),this.settings.dnd.use_html5=this.settings.dnd.use_html5&&"draggable"in i.createElement("span")},this.bind=function(){c.bind.call(this),this.element.on(this.settings.dnd.use_html5?"dragstart.jstree":"mousedown.jstree touchstart.jstree",this.settings.dnd.large_drag_target?".jstree-node":".jstree-anchor",a.proxy(function(b){if(this.settings.dnd.large_drag_target&&a(b.target).closest(".jstree-node")[0]!==b.currentTarget)return!0;if("touchstart"===b.type&&(!this.settings.dnd.touch||"selected"===this.settings.dnd.touch&&!a(b.currentTarget).closest(".jstree-node").children(".jstree-anchor").hasClass("jstree-clicked")))return!0;var c=this.get_node(b.target),d=this.is_selected(c)&&this.settings.dnd.drag_selection?this.get_top_selected().length:1,e=d>1?d+" "+this.get_string("nodes"):this.get_text(b.currentTarget);if(this.settings.core.force_text&&(e=a.vakata.html.escape(e)),c&&c.id&&c.id!==a.jstree.root&&(1===b.which||"touchstart"===b.type||"dragstart"===b.type)&&(this.settings.dnd.is_draggable===!0||a.isFunction(this.settings.dnd.is_draggable)&&this.settings.dnd.is_draggable.call(this,d>1?this.get_top_selected(!0):[c],b))){if(k={jstree:!0,origin:this,obj:this.get_node(c,!0),nodes:d>1?this.get_top_selected():[c.id]},l=b.currentTarget,!this.settings.dnd.use_html5)return this.element.trigger("mousedown.jstree"),a.vakata.dnd.start(b,k,'<div id="jstree-dnd" class="jstree-'+this.get_theme()+" jstree-"+this.get_theme()+"-"+this.get_theme_variant()+" "+(this.settings.core.themes.responsive?" jstree-dnd-responsive":"")+'"><i class="jstree-icon jstree-er"></i>'+e+'<ins class="jstree-copy" style="display:none;">+</ins></div>');a.vakata.dnd._trigger("start",b,{helper:a(),element:l,data:k})}},this)),this.settings.dnd.use_html5&&this.element.on("dragover.jstree",function(b){return b.preventDefault(),a.vakata.dnd._trigger("move",b,{helper:a(),element:l,data:k}),!1}).on("drop.jstree",a.proxy(function(b){return b.preventDefault(),a.vakata.dnd._trigger("stop",b,{helper:a(),element:l,data:k}),!1},this))},this.redraw_node=function(a,b,d,e){if(a=c.redraw_node.apply(this,arguments),a&&this.settings.dnd.use_html5)if(this.settings.dnd.large_drag_target)a.setAttribute("draggable",!0);else{var f,g,h=null;for(f=0,g=a.childNodes.length;g>f;f++)if(a.childNodes[f]&&a.childNodes[f].className&&-1!==a.childNodes[f].className.indexOf("jstree-anchor")){h=a.childNodes[f];break}h&&h.setAttribute("draggable",!0)}return a}},a(function(){var c=!1,d=!1,e=!1,f=!1,g=a('<div id="jstree-marker">&#160;</div>').hide();a(i).on("dragover.vakata.jstree",function(b){l&&a.vakata.dnd._trigger("move",b,{helper:a(),element:l,data:k})}).on("drop.vakata.jstree",function(b){l&&(a.vakata.dnd._trigger("stop",b,{helper:a(),element:l,data:k}),l=null,k=null)}).on("dnd_start.vakata.jstree",function(a,b){c=!1,e=!1,b&&b.data&&b.data.jstree&&g.appendTo(i.body)}).on("dnd_move.vakata.jstree",function(h,i){var j=i.event.target!==e.target;if(f&&(!i.event||"dragover"!==i.event.type||j)&&clearTimeout(f),i&&i.data&&i.data.jstree&&(!i.event.target.id||"jstree-marker"!==i.event.target.id)){e=i.event;var k=a.jstree.reference(i.event.target),l=!1,m=!1,n=!1,o,p,q,r,s,t,u,v,w,x,y,z,A,B,C,D,E;if(k&&k._data&&k._data.dnd)if(g.attr("class","jstree-"+k.get_theme()+(k.settings.core.themes.responsive?" jstree-dnd-responsive":"")),D=i.data.origin&&(i.data.origin.settings.dnd.always_copy||i.data.origin.settings.dnd.copy&&(i.event.metaKey||i.event.ctrlKey)),i.helper.children().attr("class","jstree-"+k.get_theme()+" jstree-"+k.get_theme()+"-"+k.get_theme_variant()+" "+(k.settings.core.themes.responsive?" jstree-dnd-responsive":"")).find(".jstree-copy").first()[D?"show":"hide"](),i.event.target!==k.element[0]&&i.event.target!==k.get_container_ul()[0]||0!==k.get_container_ul().children().length){if(l=k.settings.dnd.large_drop_target?a(i.event.target).closest(".jstree-node").children(".jstree-anchor"):a(i.event.target).closest(".jstree-anchor"),l&&l.length&&l.parent().is(".jstree-closed, .jstree-open, .jstree-leaf")&&(m=l.offset(),n=(i.event.pageY!==b?i.event.pageY:i.event.originalEvent.pageY)-m.top,r=l.outerHeight(),u=r/3>n?["b","i","a"]:n>r-r/3?["a","i","b"]:n>r/2?["i","a","b"]:["i","b","a"],a.each(u,function(b,e){switch(e){case"b":p=m.left-6,q=m.top,s=k.get_parent(l),t=l.parent().index();break;case"i":B=k.settings.dnd.inside_pos,C=k.get_node(l.parent()),p=m.left-2,q=m.top+r/2+1,s=C.id,t="first"===B?0:"last"===B?C.children.length:Math.min(B,C.children.length);break;case"a":p=m.left-6,q=m.top+r,s=k.get_parent(l),t=l.parent().index()+1}for(v=!0,w=0,x=i.data.nodes.length;x>w;w++)if(y=i.data.origin&&(i.data.origin.settings.dnd.always_copy||i.data.origin.settings.dnd.copy&&(i.event.metaKey||i.event.ctrlKey))?"copy_node":"move_node",z=t,"move_node"===y&&"a"===e&&i.data.origin&&i.data.origin===k&&s===k.get_parent(i.data.nodes[w])&&(A=k.get_node(s),z>a.inArray(i.data.nodes[w],A.children)&&(z-=1)),v=v&&(k&&k.settings&&k.settings.dnd&&k.settings.dnd.check_while_dragging===!1||k.check(y,i.data.origin&&i.data.origin!==k?i.data.origin.get_node(i.data.nodes[w]):i.data.nodes[w],s,z,{dnd:!0,ref:k.get_node(l.parent()),pos:e,origin:i.data.origin,is_multi:i.data.origin&&i.data.origin!==k,is_foreign:!i.data.origin})),!v){k&&k.last_error&&(d=k.last_error());break}return"i"===e&&l.parent().is(".jstree-closed")&&k.settings.dnd.open_timeout&&(!i.event||"dragover"!==i.event.type||j)&&(f&&clearTimeout(f),f=setTimeout(function(a,b){return function(){a.open_node(b)}}(k,l),k.settings.dnd.open_timeout)),v?(E=k.get_node(s,!0),E.hasClass(".jstree-dnd-parent")||(a(".jstree-dnd-parent").removeClass("jstree-dnd-parent"),E.addClass("jstree-dnd-parent")),c={ins:k,par:s,pos:"i"!==e||"last"!==B||0!==t||k.is_loaded(C)?t:"last"},g.css({left:p+"px",top:q+"px"}).show(),i.helper.find(".jstree-icon").first().removeClass("jstree-er").addClass("jstree-ok"),i.event.originalEvent&&i.event.originalEvent.dataTransfer&&(i.event.originalEvent.dataTransfer.dropEffect=D?"copy":"move"),d={},u=!0,!1):void 0}),u===!0))return}else{for(v=!0,w=0,x=i.data.nodes.length;x>w;w++)if(v=v&&k.check(i.data.origin&&(i.data.origin.settings.dnd.always_copy||i.data.origin.settings.dnd.copy&&(i.event.metaKey||i.event.ctrlKey))?"copy_node":"move_node",i.data.origin&&i.data.origin!==k?i.data.origin.get_node(i.data.nodes[w]):i.data.nodes[w],a.jstree.root,"last",{dnd:!0,ref:k.get_node(a.jstree.root),pos:"i",origin:i.data.origin,is_multi:i.data.origin&&i.data.origin!==k,is_foreign:!i.data.origin}),!v)break;if(v)return c={ins:k,par:a.jstree.root,pos:"last"},g.hide(),i.helper.find(".jstree-icon").first().removeClass("jstree-er").addClass("jstree-ok"),void(i.event.originalEvent&&i.event.originalEvent.dataTransfer&&(i.event.originalEvent.dataTransfer.dropEffect=D?"copy":"move"))}a(".jstree-dnd-parent").removeClass("jstree-dnd-parent"),c=!1,i.helper.find(".jstree-icon").removeClass("jstree-ok").addClass("jstree-er"),i.event.originalEvent&&i.event.originalEvent.dataTransfer,g.hide()}}).on("dnd_scroll.vakata.jstree",function(a,b){b&&b.data&&b.data.jstree&&(g.hide(),c=!1,e=!1,b.helper.find(".jstree-icon").first().removeClass("jstree-ok").addClass("jstree-er"))}).on("dnd_stop.vakata.jstree",function(b,h){if(a(".jstree-dnd-parent").removeClass("jstree-dnd-parent"),f&&clearTimeout(f),h&&h.data&&h.data.jstree){g.hide().detach();var i,j,k=[];if(c){for(i=0,j=h.data.nodes.length;j>i;i++)k[i]=h.data.origin?h.data.origin.get_node(h.data.nodes[i]):h.data.nodes[i];c.ins[h.data.origin&&(h.data.origin.settings.dnd.always_copy||h.data.origin.settings.dnd.copy&&(h.event.metaKey||h.event.ctrlKey))?"copy_node":"move_node"](k,c.par,c.pos,!1,!1,!1,h.data.origin)}else i=a(h.event.target).closest(".jstree"),i.length&&d&&d.error&&"check"===d.error&&(i=i.jstree(!0),i&&i.settings.core.error.call(this,d));e=!1,c=!1}}).on("keyup.jstree keydown.jstree",function(b,h){h=a.vakata.dnd._get(),h&&h.data&&h.data.jstree&&("keyup"===b.type&&27===b.which?(f&&clearTimeout(f),c=!1,d=!1,e=!1,f=!1,g.hide().detach(),a.vakata.dnd._clean()):(h.helper.find(".jstree-copy").first()[h.data.origin&&(h.data.origin.settings.dnd.always_copy||h.data.origin.settings.dnd.copy&&(b.metaKey||b.ctrlKey))?"show":"hide"](),e&&(e.metaKey=b.metaKey,e.ctrlKey=b.ctrlKey,a.vakata.dnd._trigger("move",e))))})}),function(a){a.vakata.html={div:a("<div />"),escape:function(b){return a.vakata.html.div.text(b).html()},strip:function(b){return a.vakata.html.div.empty().append(a.parseHTML(b)).text()}};var c={element:!1,target:!1,is_down:!1,is_drag:!1,helper:!1,helper_w:0,data:!1,init_x:0,init_y:0,scroll_l:0,scroll_t:0,scroll_e:!1,scroll_i:!1,is_touch:!1};a.vakata.dnd={settings:{scroll_speed:10,scroll_proximity:20,helper_left:5,helper_top:10,threshold:5,threshold_touch:10},_trigger:function(c,d,e){e===b&&(e=a.vakata.dnd._get()),e.event=d,a(i).triggerHandler("dnd_"+c+".vakata",e)},_get:function(){return{data:c.data,element:c.element,helper:c.helper}},_clean:function(){c.helper&&c.helper.remove(),c.scroll_i&&(clearInterval(c.scroll_i),c.scroll_i=!1),c={element:!1,target:!1,is_down:!1,is_drag:!1,helper:!1,helper_w:0,data:!1,init_x:0,init_y:0,scroll_l:0,scroll_t:0,scroll_e:!1,scroll_i:!1,is_touch:!1},a(i).off("mousemove.vakata.jstree touchmove.vakata.jstree",a.vakata.dnd.drag),a(i).off("mouseup.vakata.jstree touchend.vakata.jstree",a.vakata.dnd.stop)},_scroll:function(b){if(!c.scroll_e||!c.scroll_l&&!c.scroll_t)return c.scroll_i&&(clearInterval(c.scroll_i),c.scroll_i=!1),!1;if(!c.scroll_i)return c.scroll_i=setInterval(a.vakata.dnd._scroll,100),!1;if(b===!0)return!1;var d=c.scroll_e.scrollTop(),e=c.scroll_e.scrollLeft();c.scroll_e.scrollTop(d+c.scroll_t*a.vakata.dnd.settings.scroll_speed),c.scroll_e.scrollLeft(e+c.scroll_l*a.vakata.dnd.settings.scroll_speed),(d!==c.scroll_e.scrollTop()||e!==c.scroll_e.scrollLeft())&&a.vakata.dnd._trigger("scroll",c.scroll_e)},start:function(b,d,e){"touchstart"===b.type&&b.originalEvent&&b.originalEvent.changedTouches&&b.originalEvent.changedTouches[0]&&(b.pageX=b.originalEvent.changedTouches[0].pageX,b.pageY=b.originalEvent.changedTouches[0].pageY,b.target=i.elementFromPoint(b.originalEvent.changedTouches[0].pageX-window.pageXOffset,b.originalEvent.changedTouches[0].pageY-window.pageYOffset)),c.is_drag&&a.vakata.dnd.stop({});try{b.currentTarget.unselectable="on",b.currentTarget.onselectstart=function(){return!1},b.currentTarget.style&&(b.currentTarget.style.touchAction="none",b.currentTarget.style.msTouchAction="none",b.currentTarget.style.MozUserSelect="none")}catch(f){}return c.init_x=b.pageX,c.init_y=b.pageY,c.data=d,c.is_down=!0,c.element=b.currentTarget,c.target=b.target,c.is_touch="touchstart"===b.type,e!==!1&&(c.helper=a("<div id='vakata-dnd'></div>").html(e).css({display:"block",margin:"0",padding:"0",position:"absolute",top:"-2000px",lineHeight:"16px",zIndex:"10000"})),a(i).on("mousemove.vakata.jstree touchmove.vakata.jstree",a.vakata.dnd.drag),a(i).on("mouseup.vakata.jstree touchend.vakata.jstree",a.vakata.dnd.stop),!1},drag:function(b){if("touchmove"===b.type&&b.originalEvent&&b.originalEvent.changedTouches&&b.originalEvent.changedTouches[0]&&(b.pageX=b.originalEvent.changedTouches[0].pageX,b.pageY=b.originalEvent.changedTouches[0].pageY,b.target=i.elementFromPoint(b.originalEvent.changedTouches[0].pageX-window.pageXOffset,b.originalEvent.changedTouches[0].pageY-window.pageYOffset)),c.is_down){if(!c.is_drag){if(!(Math.abs(b.pageX-c.init_x)>(c.is_touch?a.vakata.dnd.settings.threshold_touch:a.vakata.dnd.settings.threshold)||Math.abs(b.pageY-c.init_y)>(c.is_touch?a.vakata.dnd.settings.threshold_touch:a.vakata.dnd.settings.threshold)))return;c.helper&&(c.helper.appendTo(i.body),c.helper_w=c.helper.outerWidth()),c.is_drag=!0,a(c.target).one("click.vakata",!1),a.vakata.dnd._trigger("start",b)}var d=!1,e=!1,f=!1,g=!1,h=!1,j=!1,k=!1,l=!1,m=!1,n=!1;return c.scroll_t=0,c.scroll_l=0,c.scroll_e=!1,a(a(b.target).parentsUntil("body").addBack().get().reverse()).filter(function(){return/^auto|scroll$/.test(a(this).css("overflow"))&&(this.scrollHeight>this.offsetHeight||this.scrollWidth>this.offsetWidth)}).each(function(){var d=a(this),e=d.offset();return this.scrollHeight>this.offsetHeight&&(e.top+d.height()-b.pageY<a.vakata.dnd.settings.scroll_proximity&&(c.scroll_t=1),b.pageY-e.top<a.vakata.dnd.settings.scroll_proximity&&(c.scroll_t=-1)),this.scrollWidth>this.offsetWidth&&(e.left+d.width()-b.pageX<a.vakata.dnd.settings.scroll_proximity&&(c.scroll_l=1),b.pageX-e.left<a.vakata.dnd.settings.scroll_proximity&&(c.scroll_l=-1)),c.scroll_t||c.scroll_l?(c.scroll_e=a(this),!1):void 0}),c.scroll_e||(d=a(i),e=a(window),f=d.height(),g=e.height(),h=d.width(),j=e.width(),k=d.scrollTop(),l=d.scrollLeft(),f>g&&b.pageY-k<a.vakata.dnd.settings.scroll_proximity&&(c.scroll_t=-1),f>g&&g-(b.pageY-k)<a.vakata.dnd.settings.scroll_proximity&&(c.scroll_t=1),h>j&&b.pageX-l<a.vakata.dnd.settings.scroll_proximity&&(c.scroll_l=-1),h>j&&j-(b.pageX-l)<a.vakata.dnd.settings.scroll_proximity&&(c.scroll_l=1),(c.scroll_t||c.scroll_l)&&(c.scroll_e=d)),c.scroll_e&&a.vakata.dnd._scroll(!0),c.helper&&(m=parseInt(b.pageY+a.vakata.dnd.settings.helper_top,10),n=parseInt(b.pageX+a.vakata.dnd.settings.helper_left,10),f&&m+25>f&&(m=f-50),h&&n+c.helper_w>h&&(n=h-(c.helper_w+2)),c.helper.css({left:n+"px",top:m+"px"})),a.vakata.dnd._trigger("move",b),!1}},stop:function(b){if("touchend"===b.type&&b.originalEvent&&b.originalEvent.changedTouches&&b.originalEvent.changedTouches[0]&&(b.pageX=b.originalEvent.changedTouches[0].pageX,b.pageY=b.originalEvent.changedTouches[0].pageY,b.target=i.elementFromPoint(b.originalEvent.changedTouches[0].pageX-window.pageXOffset,b.originalEvent.changedTouches[0].pageY-window.pageYOffset)),c.is_drag)b.target!==c.target&&a(c.target).off("click.vakata"),a.vakata.dnd._trigger("stop",b);else if("touchend"===b.type&&b.target===c.target){var d=setTimeout(function(){a(b.target).click()},100);a(b.target).one("click",function(){d&&clearTimeout(d)})}return a.vakata.dnd._clean(),!1}}}(a),a.jstree.defaults.massload=null,a.jstree.plugins.massload=function(b,c){this.init=function(a,b){this._data.massload={},c.init.call(this,a,b)},this._load_nodes=function(b,d,e,f){var g=this.settings.massload,h=JSON.stringify(b),i=[],j=this._model.data,k,l,m;if(!e){for(k=0,l=b.length;l>k;k++)(!j[b[k]]||!j[b[k]].state.loaded&&!j[b[k]].state.failed||f)&&(i.push(b[k]),m=this.get_node(b[k],!0),m&&m.length&&m.addClass("jstree-loading").attr("aria-busy",!0));if(this._data.massload={},i.length){if(a.isFunction(g))return g.call(this,i,a.proxy(function(a){var g,h;if(a)for(g in a)a.hasOwnProperty(g)&&(this._data.massload[g]=a[g]);for(g=0,h=b.length;h>g;g++)m=this.get_node(b[g],!0),m&&m.length&&m.removeClass("jstree-loading").attr("aria-busy",!1);c._load_nodes.call(this,b,d,e,f)},this));if("object"==typeof g&&g&&g.url)return g=a.extend(!0,{},g),a.isFunction(g.url)&&(g.url=g.url.call(this,i)),a.isFunction(g.data)&&(g.data=g.data.call(this,i)),a.ajax(g).done(a.proxy(function(a,g,h){var i,j;if(a)for(i in a)a.hasOwnProperty(i)&&(this._data.massload[i]=a[i]);for(i=0,j=b.length;j>i;i++)m=this.get_node(b[i],!0),m&&m.length&&m.removeClass("jstree-loading").attr("aria-busy",!1);c._load_nodes.call(this,b,d,e,f)},this)).fail(a.proxy(function(a){c._load_nodes.call(this,b,d,e,f)},this))}}return c._load_nodes.call(this,b,d,e,f)},this._load_node=function(b,d){var e=this._data.massload[b.id],f=null,g;return e?(f=this["string"==typeof e?"_append_html_data":"_append_json_data"](b,"string"==typeof e?a(a.parseHTML(e)).filter(function(){return 3!==this.nodeType}):e,function(a){d.call(this,a)}),g=this.get_node(b.id,!0),g&&g.length&&g.removeClass("jstree-loading").attr("aria-busy",!1),delete this._data.massload[b.id],f):c._load_node.call(this,b,d)}},a.jstree.defaults.search={ajax:!1,fuzzy:!1,case_sensitive:!1,show_only_matches:!1,show_only_matches_children:!1,close_opened_onclear:!0,search_leaves_only:!1,search_callback:!1},a.jstree.plugins.search=function(c,d){this.bind=function(){d.bind.call(this),this._data.search.str="",this._data.search.dom=a(),this._data.search.res=[],this._data.search.opn=[],this._data.search.som=!1,this._data.search.smc=!1,this._data.search.hdn=[],this.element.on("search.jstree",a.proxy(function(b,c){if(this._data.search.som&&c.res.length){var d=this._model.data,e,f,g=[],h,i;for(e=0,f=c.res.length;f>e;e++)if(d[c.res[e]]&&!d[c.res[e]].state.hidden&&(g.push(c.res[e]),g=g.concat(d[c.res[e]].parents),this._data.search.smc))for(h=0,i=d[c.res[e]].children_d.length;i>h;h++)d[d[c.res[e]].children_d[h]]&&!d[d[c.res[e]].children_d[h]].state.hidden&&g.push(d[c.res[e]].children_d[h]);g=a.vakata.array_remove_item(a.vakata.array_unique(g),a.jstree.root),this._data.search.hdn=this.hide_all(!0),this.show_node(g,!0),this.redraw(!0)}},this)).on("clear_search.jstree",a.proxy(function(a,b){this._data.search.som&&b.res.length&&(this.show_node(this._data.search.hdn,!0),this.redraw(!0))},this))},this.search=function(c,d,e,f,g,h){if(c===!1||""===a.trim(c.toString()))return this.clear_search();f=this.get_node(f),f=f&&f.id?f.id:null,c=c.toString();var i=this.settings.search,j=i.ajax?i.ajax:!1,k=this._model.data,l=null,m=[],n=[],o,p;if(this._data.search.res.length&&!g&&this.clear_search(),e===b&&(e=i.show_only_matches),h===b&&(h=i.show_only_matches_children),!d&&j!==!1)return a.isFunction(j)?j.call(this,c,a.proxy(function(b){b&&b.d&&(b=b.d),this._load_nodes(a.isArray(b)?a.vakata.array_unique(b):[],function(){this.search(c,!0,e,f,g,h)})},this),f):(j=a.extend({},j),j.data||(j.data={}),j.data.str=c,f&&(j.data.inside=f),this._data.search.lastRequest&&this._data.search.lastRequest.abort(),this._data.search.lastRequest=a.ajax(j).fail(a.proxy(function(){this._data.core.last_error={error:"ajax",plugin:"search",id:"search_01",reason:"Could not load search parents",data:JSON.stringify(j)},this.settings.core.error.call(this,this._data.core.last_error)},this)).done(a.proxy(function(b){b&&b.d&&(b=b.d),this._load_nodes(a.isArray(b)?a.vakata.array_unique(b):[],function(){this.search(c,!0,e,f,g,h)})},this)),this._data.search.lastRequest);if(g||(this._data.search.str=c,this._data.search.dom=a(),this._data.search.res=[],this._data.search.opn=[],this._data.search.som=e,this._data.search.smc=h),l=new a.vakata.search(c,!0,{caseSensitive:i.case_sensitive,fuzzy:i.fuzzy}),a.each(k[f?f:a.jstree.root].children_d,function(a,b){var d=k[b];d.text&&!d.state.hidden&&(!i.search_leaves_only||d.state.loaded&&0===d.children.length)&&(i.search_callback&&i.search_callback.call(this,c,d)||!i.search_callback&&l.search(d.text).isMatch)&&(m.push(b),n=n.concat(d.parents))}),m.length){for(n=a.vakata.array_unique(n),o=0,p=n.length;p>o;o++)n[o]!==a.jstree.root&&k[n[o]]&&this.open_node(n[o],null,0)===!0&&this._data.search.opn.push(n[o]);g?(this._data.search.dom=this._data.search.dom.add(a(this.element[0].querySelectorAll("#"+a.map(m,function(b){return-1!=="0123456789".indexOf(b[0])?"\\3"+b[0]+" "+b.substr(1).replace(a.jstree.idregex,"\\$&"):b.replace(a.jstree.idregex,"\\$&")}).join(", #")))),this._data.search.res=a.vakata.array_unique(this._data.search.res.concat(m))):(this._data.search.dom=a(this.element[0].querySelectorAll("#"+a.map(m,function(b){return-1!=="0123456789".indexOf(b[0])?"\\3"+b[0]+" "+b.substr(1).replace(a.jstree.idregex,"\\$&"):b.replace(a.jstree.idregex,"\\$&")}).join(", #"))),this._data.search.res=m),this._data.search.dom.children(".jstree-anchor").addClass("jstree-search")}this.trigger("search",{nodes:this._data.search.dom,str:c,res:this._data.search.res,show_only_matches:e})},this.clear_search=function(){this.settings.search.close_opened_onclear&&this.close_node(this._data.search.opn,0),this.trigger("clear_search",{nodes:this._data.search.dom,str:this._data.search.str,res:this._data.search.res}),this._data.search.res.length&&(this._data.search.dom=a(this.element[0].querySelectorAll("#"+a.map(this._data.search.res,function(b){return-1!=="0123456789".indexOf(b[0])?"\\3"+b[0]+" "+b.substr(1).replace(a.jstree.idregex,"\\$&"):b.replace(a.jstree.idregex,"\\$&")}).join(", #"))),this._data.search.dom.children(".jstree-anchor").removeClass("jstree-search")),this._data.search.str="",this._data.search.res=[],this._data.search.opn=[],this._data.search.dom=a()},this.redraw_node=function(b,c,e,f){if(b=d.redraw_node.apply(this,arguments),b&&-1!==a.inArray(b.id,this._data.search.res)){var g,h,i=null;for(g=0,h=b.childNodes.length;h>g;g++)if(b.childNodes[g]&&b.childNodes[g].className&&-1!==b.childNodes[g].className.indexOf("jstree-anchor")){i=b.childNodes[g];break}i&&(i.className+=" jstree-search")}return b}},function(a){a.vakata.search=function(b,c,d){d=d||{},d=a.extend({},a.vakata.search.defaults,d),d.fuzzy!==!1&&(d.fuzzy=!0),b=d.caseSensitive?b:b.toLowerCase();var e=d.location,f=d.distance,g=d.threshold,h=b.length,i,j,k,l;return h>32&&(d.fuzzy=!1),d.fuzzy&&(i=1<<h-1,j=function(){var a={},c=0;for(c=0;h>c;c++)a[b.charAt(c)]=0;for(c=0;h>c;c++)a[b.charAt(c)]|=1<<h-c-1;return a}(),k=function(a,b){var c=a/h,d=Math.abs(e-b);return f?c+d/f:d?1:c}),l=function(a){if(a=d.caseSensitive?a:a.toLowerCase(),b===a||-1!==a.indexOf(b))return{isMatch:!0,score:0};if(!d.fuzzy)return{isMatch:!1,score:1};var c,f,l=a.length,m=g,n=a.indexOf(b,e),o,p,q=h+l,r,s,t,u,v,w=1,x=[];for(-1!==n&&(m=Math.min(k(0,n),m),n=a.lastIndexOf(b,e+h),-1!==n&&(m=Math.min(k(0,n),m))),n=-1,c=0;h>c;c++){o=0,p=q;while(p>o)k(c,e+p)<=m?o=p:q=p,p=Math.floor((q-o)/2+o);for(q=p,s=Math.max(1,e-p+1),t=Math.min(e+p,l)+h,u=new Array(t+2),u[t+1]=(1<<c)-1,f=t;f>=s;f--)if(v=j[a.charAt(f-1)],0===c?u[f]=(u[f+1]<<1|1)&v:u[f]=(u[f+1]<<1|1)&v|((r[f+1]|r[f])<<1|1)|r[f+1],u[f]&i&&(w=k(c,f-1),m>=w)){if(m=w,n=f-1,x.push(n),!(n>e))break;s=Math.max(1,2*e-n)}if(k(c+1,e)>m)break;r=u}return{isMatch:n>=0,score:w}},c===!0?{search:l}:l(c)},a.vakata.search.defaults={location:0,distance:100,threshold:.6,fuzzy:!1,caseSensitive:!1}}(a),a.jstree.defaults.sort=function(a,b){return this.get_text(a)>this.get_text(b)?1:-1},a.jstree.plugins.sort=function(b,c){this.bind=function(){c.bind.call(this),this.element.on("model.jstree",a.proxy(function(a,b){this.sort(b.parent,!0)},this)).on("rename_node.jstree create_node.jstree",a.proxy(function(a,b){this.sort(b.parent||b.node.parent,!1),this.redraw_node(b.parent||b.node.parent,!0)},this)).on("move_node.jstree copy_node.jstree",a.proxy(function(a,b){this.sort(b.parent,!1),this.redraw_node(b.parent,!0)},this))},this.sort=function(b,c){var d,e;if(b=this.get_node(b),b&&b.children&&b.children.length&&(b.children.sort(a.proxy(this.settings.sort,this)),c))for(d=0,e=b.children_d.length;e>d;d++)this.sort(b.children_d[d],!1)}};var m=!1;a.jstree.defaults.state={key:"jstree",events:"changed.jstree open_node.jstree close_node.jstree check_node.jstree uncheck_node.jstree",ttl:!1,filter:!1,preserve_loaded:!1},a.jstree.plugins.state=function(b,c){this.bind=function(){c.bind.call(this);var b=a.proxy(function(){this.element.on(this.settings.state.events,a.proxy(function(){m&&clearTimeout(m),m=setTimeout(a.proxy(function(){this.save_state()},this),100)},this)),this.trigger("state_ready")},this);this.element.on("ready.jstree",a.proxy(function(a,c){this.element.one("restore_state.jstree",b),this.restore_state()||b()},this))},this.save_state=function(){var b=this.get_state();this.settings.state.preserve_loaded||delete b.core.loaded;var c={state:b,ttl:this.settings.state.ttl,sec:+new Date};a.vakata.storage.set(this.settings.state.key,JSON.stringify(c))},this.restore_state=function(){var b=a.vakata.storage.get(this.settings.state.key);if(b)try{b=JSON.parse(b)}catch(c){return!1}return b&&b.ttl&&b.sec&&+new Date-b.sec>b.ttl?!1:(b&&b.state&&(b=b.state),b&&a.isFunction(this.settings.state.filter)&&(b=this.settings.state.filter.call(this,b)),b?(this.settings.state.preserve_loaded||delete b.core.loaded,this.element.one("set_state.jstree",function(c,d){d.instance.trigger("restore_state",{state:a.extend(!0,{},b)})}),this.set_state(b),!0):!1)},this.clear_state=function(){return a.vakata.storage.del(this.settings.state.key)}},function(a,b){a.vakata.storage={set:function(a,b){return window.localStorage.setItem(a,b)},get:function(a){return window.localStorage.getItem(a)},del:function(a){return window.localStorage.removeItem(a)}}}(a),a.jstree.defaults.types={"default":{}},a.jstree.defaults.types[a.jstree.root]={},a.jstree.plugins.types=function(c,d){this.init=function(c,e){var f,g;if(e&&e.types&&e.types["default"])for(f in e.types)if("default"!==f&&f!==a.jstree.root&&e.types.hasOwnProperty(f))for(g in e.types["default"])e.types["default"].hasOwnProperty(g)&&e.types[f][g]===b&&(e.types[f][g]=e.types["default"][g]);d.init.call(this,c,e),this._model.data[a.jstree.root].type=a.jstree.root},this.refresh=function(b,c){d.refresh.call(this,b,c),this._model.data[a.jstree.root].type=a.jstree.root},this.bind=function(){this.element.on("model.jstree",a.proxy(function(c,d){var e=this._model.data,f=d.nodes,g=this.settings.types,h,i,j="default",k;for(h=0,i=f.length;i>h;h++){if(j="default",e[f[h]].original&&e[f[h]].original.type&&g[e[f[h]].original.type]&&(j=e[f[h]].original.type),e[f[h]].data&&e[f[h]].data.jstree&&e[f[h]].data.jstree.type&&g[e[f[h]].data.jstree.type]&&(j=e[f[h]].data.jstree.type),e[f[h]].type=j,e[f[h]].icon===!0&&g[j].icon!==b&&(e[f[h]].icon=g[j].icon),g[j].li_attr!==b&&"object"==typeof g[j].li_attr)for(k in g[j].li_attr)if(g[j].li_attr.hasOwnProperty(k)){if("id"===k)continue;e[f[h]].li_attr[k]===b?e[f[h]].li_attr[k]=g[j].li_attr[k]:"class"===k&&(e[f[h]].li_attr["class"]=g[j].li_attr["class"]+" "+e[f[h]].li_attr["class"])}if(g[j].a_attr!==b&&"object"==typeof g[j].a_attr)for(k in g[j].a_attr)if(g[j].a_attr.hasOwnProperty(k)){if("id"===k)continue;e[f[h]].a_attr[k]===b?e[f[h]].a_attr[k]=g[j].a_attr[k]:"href"===k&&"#"===e[f[h]].a_attr[k]?e[f[h]].a_attr.href=g[j].a_attr.href:"class"===k&&(e[f[h]].a_attr["class"]=g[j].a_attr["class"]+" "+e[f[h]].a_attr["class"])}}e[a.jstree.root].type=a.jstree.root},this)),d.bind.call(this)},this.get_json=function(b,c,e){var f,g,h=this._model.data,i=c?a.extend(!0,{},c,{no_id:!1}):{},j=d.get_json.call(this,b,i,e);if(j===!1)return!1;if(a.isArray(j))for(f=0,g=j.length;g>f;f++)j[f].type=j[f].id&&h[j[f].id]&&h[j[f].id].type?h[j[f].id].type:"default",c&&c.no_id&&(delete j[f].id,j[f].li_attr&&j[f].li_attr.id&&delete j[f].li_attr.id,j[f].a_attr&&j[f].a_attr.id&&delete j[f].a_attr.id);else j.type=j.id&&h[j.id]&&h[j.id].type?h[j.id].type:"default",c&&c.no_id&&(j=this._delete_ids(j));return j},this._delete_ids=function(b){if(a.isArray(b)){for(var c=0,d=b.length;d>c;c++)b[c]=this._delete_ids(b[c]);return b}return delete b.id,
b.li_attr&&b.li_attr.id&&delete b.li_attr.id,b.a_attr&&b.a_attr.id&&delete b.a_attr.id,b.children&&a.isArray(b.children)&&(b.children=this._delete_ids(b.children)),b},this.check=function(c,e,f,g,h){if(d.check.call(this,c,e,f,g,h)===!1)return!1;e=e&&e.id?e:this.get_node(e),f=f&&f.id?f:this.get_node(f);var i=e&&e.id?h&&h.origin?h.origin:a.jstree.reference(e.id):null,j,k,l,m;switch(i=i&&i._model&&i._model.data?i._model.data:null,c){case"create_node":case"move_node":case"copy_node":if("move_node"!==c||-1===a.inArray(e.id,f.children)){if(j=this.get_rules(f),j.max_children!==b&&-1!==j.max_children&&j.max_children===f.children.length)return this._data.core.last_error={error:"check",plugin:"types",id:"types_01",reason:"max_children prevents function: "+c,data:JSON.stringify({chk:c,pos:g,obj:e&&e.id?e.id:!1,par:f&&f.id?f.id:!1})},!1;if(j.valid_children!==b&&-1!==j.valid_children&&-1===a.inArray(e.type||"default",j.valid_children))return this._data.core.last_error={error:"check",plugin:"types",id:"types_02",reason:"valid_children prevents function: "+c,data:JSON.stringify({chk:c,pos:g,obj:e&&e.id?e.id:!1,par:f&&f.id?f.id:!1})},!1;if(i&&e.children_d&&e.parents){for(k=0,l=0,m=e.children_d.length;m>l;l++)k=Math.max(k,i[e.children_d[l]].parents.length);k=k-e.parents.length+1}(0>=k||k===b)&&(k=1);do{if(j.max_depth!==b&&-1!==j.max_depth&&j.max_depth<k)return this._data.core.last_error={error:"check",plugin:"types",id:"types_03",reason:"max_depth prevents function: "+c,data:JSON.stringify({chk:c,pos:g,obj:e&&e.id?e.id:!1,par:f&&f.id?f.id:!1})},!1;f=this.get_node(f.parent),j=this.get_rules(f),k++}while(f)}}return!0},this.get_rules=function(a){if(a=this.get_node(a),!a)return!1;var c=this.get_type(a,!0);return c.max_depth===b&&(c.max_depth=-1),c.max_children===b&&(c.max_children=-1),c.valid_children===b&&(c.valid_children=-1),c},this.get_type=function(b,c){return b=this.get_node(b),b?c?a.extend({type:b.type},this.settings.types[b.type]):b.type:!1},this.set_type=function(c,d){var e=this._model.data,f,g,h,i,j,k,l,m;if(a.isArray(c)){for(c=c.slice(),g=0,h=c.length;h>g;g++)this.set_type(c[g],d);return!0}if(f=this.settings.types,c=this.get_node(c),!f[d]||!c)return!1;if(l=this.get_node(c,!0),l&&l.length&&(m=l.children(".jstree-anchor")),i=c.type,j=this.get_icon(c),c.type=d,(j===!0||!f[i]||f[i].icon!==b&&j===f[i].icon)&&this.set_icon(c,f[d].icon!==b?f[d].icon:!0),f[i]&&f[i].li_attr!==b&&"object"==typeof f[i].li_attr)for(k in f[i].li_attr)if(f[i].li_attr.hasOwnProperty(k)){if("id"===k)continue;"class"===k?(e[c.id].li_attr["class"]=(e[c.id].li_attr["class"]||"").replace(f[i].li_attr[k],""),l&&l.removeClass(f[i].li_attr[k])):e[c.id].li_attr[k]===f[i].li_attr[k]&&(e[c.id].li_attr[k]=null,l&&l.removeAttr(k))}if(f[i]&&f[i].a_attr!==b&&"object"==typeof f[i].a_attr)for(k in f[i].a_attr)if(f[i].a_attr.hasOwnProperty(k)){if("id"===k)continue;"class"===k?(e[c.id].a_attr["class"]=(e[c.id].a_attr["class"]||"").replace(f[i].a_attr[k],""),m&&m.removeClass(f[i].a_attr[k])):e[c.id].a_attr[k]===f[i].a_attr[k]&&("href"===k?(e[c.id].a_attr[k]="#",m&&m.attr("href","#")):(delete e[c.id].a_attr[k],m&&m.removeAttr(k)))}if(f[d].li_attr!==b&&"object"==typeof f[d].li_attr)for(k in f[d].li_attr)if(f[d].li_attr.hasOwnProperty(k)){if("id"===k)continue;e[c.id].li_attr[k]===b?(e[c.id].li_attr[k]=f[d].li_attr[k],l&&("class"===k?l.addClass(f[d].li_attr[k]):l.attr(k,f[d].li_attr[k]))):"class"===k&&(e[c.id].li_attr["class"]=f[d].li_attr[k]+" "+e[c.id].li_attr["class"],l&&l.addClass(f[d].li_attr[k]))}if(f[d].a_attr!==b&&"object"==typeof f[d].a_attr)for(k in f[d].a_attr)if(f[d].a_attr.hasOwnProperty(k)){if("id"===k)continue;e[c.id].a_attr[k]===b?(e[c.id].a_attr[k]=f[d].a_attr[k],m&&("class"===k?m.addClass(f[d].a_attr[k]):m.attr(k,f[d].a_attr[k]))):"href"===k&&"#"===e[c.id].a_attr[k]?(e[c.id].a_attr.href=f[d].a_attr.href,m&&m.attr("href",f[d].a_attr.href)):"class"===k&&(e[c.id].a_attr["class"]=f[d].a_attr["class"]+" "+e[c.id].a_attr["class"],m&&m.addClass(f[d].a_attr[k]))}return!0}},a.jstree.defaults.unique={case_sensitive:!1,trim_whitespace:!1,duplicate:function(a,b){return a+" ("+b+")"}},a.jstree.plugins.unique=function(c,d){this.check=function(b,c,e,f,g){if(d.check.call(this,b,c,e,f,g)===!1)return!1;if(c=c&&c.id?c:this.get_node(c),e=e&&e.id?e:this.get_node(e),!e||!e.children)return!0;var h="rename_node"===b?f:c.text,i=[],j=this.settings.unique.case_sensitive,k=this.settings.unique.trim_whitespace,l=this._model.data,m,n,o;for(m=0,n=e.children.length;n>m;m++)o=l[e.children[m]].text,j||(o=o.toLowerCase()),k&&(o=o.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,"")),i.push(o);switch(j||(h=h.toLowerCase()),k&&(h=h.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,"")),b){case"delete_node":return!0;case"rename_node":return o=c.text||"",j||(o=o.toLowerCase()),k&&(o=o.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,"")),m=-1===a.inArray(h,i)||c.text&&o===h,m||(this._data.core.last_error={error:"check",plugin:"unique",id:"unique_01",reason:"Child with name "+h+" already exists. Preventing: "+b,data:JSON.stringify({chk:b,pos:f,obj:c&&c.id?c.id:!1,par:e&&e.id?e.id:!1})}),m;case"create_node":return m=-1===a.inArray(h,i),m||(this._data.core.last_error={error:"check",plugin:"unique",id:"unique_04",reason:"Child with name "+h+" already exists. Preventing: "+b,data:JSON.stringify({chk:b,pos:f,obj:c&&c.id?c.id:!1,par:e&&e.id?e.id:!1})}),m;case"copy_node":return m=-1===a.inArray(h,i),m||(this._data.core.last_error={error:"check",plugin:"unique",id:"unique_02",reason:"Child with name "+h+" already exists. Preventing: "+b,data:JSON.stringify({chk:b,pos:f,obj:c&&c.id?c.id:!1,par:e&&e.id?e.id:!1})}),m;case"move_node":return m=c.parent===e.id&&(!g||!g.is_multi)||-1===a.inArray(h,i),m||(this._data.core.last_error={error:"check",plugin:"unique",id:"unique_03",reason:"Child with name "+h+" already exists. Preventing: "+b,data:JSON.stringify({chk:b,pos:f,obj:c&&c.id?c.id:!1,par:e&&e.id?e.id:!1})}),m}return!0},this.create_node=function(c,e,f,g,h){if(!e||e.text===b){if(null===c&&(c=a.jstree.root),c=this.get_node(c),!c)return d.create_node.call(this,c,e,f,g,h);if(f=f===b?"last":f,!f.toString().match(/^(before|after)$/)&&!h&&!this.is_loaded(c))return d.create_node.call(this,c,e,f,g,h);e||(e={});var i,j,k,l,m,n=this._model.data,o=this.settings.unique.case_sensitive,p=this.settings.unique.trim_whitespace,q=this.settings.unique.duplicate,r;for(j=i=this.get_string("New node"),k=[],l=0,m=c.children.length;m>l;l++)r=n[c.children[l]].text,o||(r=r.toLowerCase()),p&&(r=r.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,"")),k.push(r);l=1,r=j,o||(r=r.toLowerCase()),p&&(r=r.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,""));while(-1!==a.inArray(r,k))j=q.call(this,i,++l).toString(),r=j,o||(r=r.toLowerCase()),p&&(r=r.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,""));e.text=j}return d.create_node.call(this,c,e,f,g,h)}};var n=i.createElement("DIV");if(n.setAttribute("unselectable","on"),n.setAttribute("role","presentation"),n.className="jstree-wholerow",n.innerHTML="&#160;",a.jstree.plugins.wholerow=function(b,c){this.bind=function(){c.bind.call(this),this.element.on("ready.jstree set_state.jstree",a.proxy(function(){this.hide_dots()},this)).on("init.jstree loading.jstree ready.jstree",a.proxy(function(){this.get_container_ul().addClass("jstree-wholerow-ul")},this)).on("deselect_all.jstree",a.proxy(function(a,b){this.element.find(".jstree-wholerow-clicked").removeClass("jstree-wholerow-clicked")},this)).on("changed.jstree",a.proxy(function(a,b){this.element.find(".jstree-wholerow-clicked").removeClass("jstree-wholerow-clicked");var c=!1,d,e;for(d=0,e=b.selected.length;e>d;d++)c=this.get_node(b.selected[d],!0),c&&c.length&&c.children(".jstree-wholerow").addClass("jstree-wholerow-clicked")},this)).on("open_node.jstree",a.proxy(function(a,b){this.get_node(b.node,!0).find(".jstree-clicked").parent().children(".jstree-wholerow").addClass("jstree-wholerow-clicked")},this)).on("hover_node.jstree dehover_node.jstree",a.proxy(function(a,b){"hover_node"===a.type&&this.is_disabled(b.node)||this.get_node(b.node,!0).children(".jstree-wholerow")["hover_node"===a.type?"addClass":"removeClass"]("jstree-wholerow-hovered")},this)).on("contextmenu.jstree",".jstree-wholerow",a.proxy(function(b){if(this._data.contextmenu){b.preventDefault();var c=a.Event("contextmenu",{metaKey:b.metaKey,ctrlKey:b.ctrlKey,altKey:b.altKey,shiftKey:b.shiftKey,pageX:b.pageX,pageY:b.pageY});a(b.currentTarget).closest(".jstree-node").children(".jstree-anchor").first().trigger(c)}},this)).on("click.jstree",".jstree-wholerow",function(b){b.stopImmediatePropagation();var c=a.Event("click",{metaKey:b.metaKey,ctrlKey:b.ctrlKey,altKey:b.altKey,shiftKey:b.shiftKey});a(b.currentTarget).closest(".jstree-node").children(".jstree-anchor").first().trigger(c).focus()}).on("dblclick.jstree",".jstree-wholerow",function(b){b.stopImmediatePropagation();var c=a.Event("dblclick",{metaKey:b.metaKey,ctrlKey:b.ctrlKey,altKey:b.altKey,shiftKey:b.shiftKey});a(b.currentTarget).closest(".jstree-node").children(".jstree-anchor").first().trigger(c).focus()}).on("click.jstree",".jstree-leaf > .jstree-ocl",a.proxy(function(b){b.stopImmediatePropagation();var c=a.Event("click",{metaKey:b.metaKey,ctrlKey:b.ctrlKey,altKey:b.altKey,shiftKey:b.shiftKey});a(b.currentTarget).closest(".jstree-node").children(".jstree-anchor").first().trigger(c).focus()},this)).on("mouseover.jstree",".jstree-wholerow, .jstree-icon",a.proxy(function(a){return a.stopImmediatePropagation(),this.is_disabled(a.currentTarget)||this.hover_node(a.currentTarget),!1},this)).on("mouseleave.jstree",".jstree-node",a.proxy(function(a){this.dehover_node(a.currentTarget)},this))},this.teardown=function(){this.settings.wholerow&&this.element.find(".jstree-wholerow").remove(),c.teardown.call(this)},this.redraw_node=function(b,d,e,f){if(b=c.redraw_node.apply(this,arguments)){var g=n.cloneNode(!0);-1!==a.inArray(b.id,this._data.core.selected)&&(g.className+=" jstree-wholerow-clicked"),this._data.core.focused&&this._data.core.focused===b.id&&(g.className+=" jstree-wholerow-hovered"),b.insertBefore(g,b.childNodes[0])}return b}},window.customElements&&Object&&Object.create){var o=Object.create(HTMLElement.prototype);o.createdCallback=function(){var b={core:{},plugins:[]},c;for(c in a.jstree.plugins)a.jstree.plugins.hasOwnProperty(c)&&this.attributes[c]&&(b.plugins.push(c),this.getAttribute(c)&&JSON.parse(this.getAttribute(c))&&(b[c]=JSON.parse(this.getAttribute(c))));for(c in a.jstree.defaults.core)a.jstree.defaults.core.hasOwnProperty(c)&&this.attributes[c]&&(b.core[c]=JSON.parse(this.getAttribute(c))||this.getAttribute(c));a(this).jstree(b)};try{window.customElements.define("vakata-jstree",function(){},{prototype:o})}catch(p){}}}});admin/js/wpvivid-admin.js000064400000066367151327705670011423 0ustar00var task_retry_times=0;
var running_backup_taskid='';
var tmp_current_click_backupid = '';
var m_need_update=true;
var m_restore_backup_id;
var m_backup_task_id;
var m_downloading_file_name = '';
var m_downloading_id = '';
var wpvivid_settings_changed = false;
var wpvivid_cur_log_page = 1;
var wpvivid_completed_backup = 1;
var wpvivid_prepare_backup=false;
var wpvivid_restoring=false;
var wpvivid_location_href=false;
var wpvivid_editing_storage_id = '';
var wpvivid_editing_storage_type = '';
var wpvivid_restore_download_array;
var wpvivid_restore_download_index = 0;
var wpvivid_get_download_restore_progress_retry = 0;
var wpvivid_restore_timeout = false;
var wpvivid_restore_need_download = false;
var wpvivid_display_restore_backup = false;
var wpvivid_restore_backup_type = '';
var wpvivid_display_restore_check = false;
var wpvivid_restore_sure = false;
var wpvivid_resotre_is_migrate=0;
(function ($) {
    'use strict';

    /**
     * All of the code for your admin-facing JavaScript source
     * should reside in this file.
     *
     * Note: It has been assumed you will write jQuery code here, so the
     * $ function reference has been prepared for usage within the scope
     * of this function.
     *
     * This enables you to define handlers, for when the DOM is ready:
     *
     * $(function() {
     *
     * });
     *
     * When the window is loaded:
     *
     * $( window ).load(function() {
     *
     * });
     *
     * ...and/or other possibilities.
     *
     * Ideally, it is not considered best practise to attach more than a
     * single DOM-ready or window-load handler for a particular page.
     * Although scripts in the WordPress core, Plugins and Themes may be
     * practising this, we should strive to set a better example in our own work.
     */
    $(document).ready(function () {
        //wpvivid_getrequest();

        wpvivid_interface_flow_control();

        $('input[option=review]').click(function(){
            var name = jQuery(this).prop('name');
            wpvivid_add_review_info(name);
        });

        $(document).on('click', '.notice-rate .notice-dismiss', function(){
            var name = 'dismiss';
            wpvivid_add_review_info(name);
        });

        $(document).on('click', '.notice-wp-cron .notice-dismiss', function(){
            var ajax_data = {
                'action': 'wpvivid_hide_wp_cron_notice'
            };
            wpvivid_post_request(ajax_data, function(res){
            }, function(XMLHttpRequest, textStatus, errorThrown) {
            });
        });
    });
    
})(jQuery);

function wpvivid_popup_tour(style) {
    var popup = document.getElementById("wpvivid_popup_tour");
    if (popup != null) {
        popup.classList.add(style);
    }
}

window.onbeforeunload = function(e) {
    if (wpvivid_settings_changed) {
        if (wpvivid_location_href){
            wpvivid_location_href = false;
        }
        else {
            return 'You are leaving the page without saving your changes, any unsaved changes on the page will be lost, are you sure you want to continue?';
        }
    }
}

/**
 * Refresh the scheduled task list as regularly as a preset interval(3-minute), to retrieve and activate the scheduled cron jobs.
 */
function wpvivid_activate_cron(){
    var next_get_time = 3 * 60 * 1000;
    wpvivid_cron_task();
    setTimeout("wpvivid_activate_cron()", next_get_time);
    setTimeout(function(){
        m_need_update=true;
    }, 10000);
}

/**
 * Send an Ajax request
 *
 * @param ajax_data         - Data in Ajax request
 * @param callback          - A callback function when the request is succeeded
 * @param error_callback    - A callback function when the request is failed
 * @param time_out          - The timeout for Ajax request
 */
function wpvivid_post_request(ajax_data, callback, error_callback, time_out){
    if(typeof time_out === 'undefined')    time_out = 30000;
    ajax_data.nonce=wpvivid_ajax_object.ajax_nonce;
    jQuery.ajax({
        type: "post",
        url: wpvivid_ajax_object.ajax_url,
        data: ajax_data,
        success: function (data) {
            callback(data);
        },
        error: function (XMLHttpRequest, textStatus, errorThrown) {
            error_callback(XMLHttpRequest, textStatus, errorThrown);
        },
        timeout: time_out
    });
}

/**
 * Check if there are running tasks (backup and download)
 */
function wpvivid_check_runningtask(){
    var ajax_data = {
        'action': 'wpvivid_list_tasks',
        'backup_id': tmp_current_click_backupid
    };
    if(wpvivid_restoring === false) {
        wpvivid_post_request(ajax_data, function (data) {
            setTimeout(function () {
                wpvivid_manage_task();
            }, 3000);
            try {
                var jsonarray = jQuery.parseJSON(data);
                if (jsonarray.success_notice_html != false) {
                    jQuery('#wpvivid_backup_notice').show();
                    jQuery('#wpvivid_backup_notice').append(jsonarray.success_notice_html);
                }
                if(jsonarray.error_notice_html != false){
                    jQuery('#wpvivid_backup_notice').show();
                    jQuery.each(jsonarray.error_notice_html, function (index, value) {
                        jQuery('#wpvivid_backup_notice').append(value.error_msg);
                    });
                }
                if(jsonarray.backuplist_html != false) {
                    jQuery('#wpvivid_backup_list').html('');
                    jQuery('#wpvivid_backup_list').append(jsonarray.backuplist_html);
                }
                var b_has_data = false;
                if (jsonarray.backup.data.length !== 0) {
                    b_has_data = true;
                    task_retry_times = 0;
                    if (jsonarray.backup.result === 'success') {
                        wpvivid_prepare_backup = false;
                        jQuery.each(jsonarray.backup.data, function (index, value) {
                            if (value.status.str === 'ready') {
                                jQuery('#wpvivid_postbox_backup_percent').html(value.progress_html);
                                m_need_update = true;
                            }
                            else if (value.status.str === 'running') {
                                running_backup_taskid = index;
                                wpvivid_control_backup_lock();
                                jQuery('#wpvivid_postbox_backup_percent').show();
                                jQuery('#wpvivid_postbox_backup_percent').html(value.progress_html);
                                m_need_update = true;
                            }
                            else if (value.status.str === 'wait_resume') {
                                running_backup_taskid = index;
                                wpvivid_control_backup_lock();
                                jQuery('#wpvivid_postbox_backup_percent').show();
                                jQuery('#wpvivid_postbox_backup_percent').html(value.progress_html);
                                if (value.data.next_resume_time !== 'get next resume time failed.') {
                                    wpvivid_resume_backup(index, value.data.next_resume_time);
                                }
                                else {
                                    wpvivid_delete_backup_task(index);
                                }
                            }
                            else if (value.status.str === 'no_responds') {
                                running_backup_taskid = index;
                                wpvivid_control_backup_lock();
                                jQuery('#wpvivid_postbox_backup_percent').show();
                                jQuery('#wpvivid_postbox_backup_percent').html(value.progress_html);
                                m_need_update = true;
                            }
                            else if (value.status.str === 'completed') {
                                jQuery('#wpvivid_postbox_backup_percent').html(value.progress_html);
                                wpvivid_control_backup_unlock();
                                jQuery('#wpvivid_postbox_backup_percent').hide();
                                jQuery('#wpvivid_last_backup_msg').html(jsonarray.last_msg_html);
                                jQuery('#wpvivid_loglist').html("");
                                jQuery('#wpvivid_loglist').append(jsonarray.log_html);
                                wpvivid_log_count = jsonarray.log_count;
                                wpvivid_display_log_page();
                                running_backup_taskid = '';
                                m_backup_task_id = '';
                                m_need_update = true;
                            }
                            else if (value.status.str === 'error') {
                                jQuery('#wpvivid_postbox_backup_percent').html(value.progress_html);
                                wpvivid_control_backup_unlock();
                                jQuery('#wpvivid_postbox_backup_percent').hide();
                                jQuery('#wpvivid_last_backup_msg').html(jsonarray.last_msg_html);
                                jQuery('#wpvivid_loglist').html("");
                                jQuery('#wpvivid_loglist').append(jsonarray.log_html);
                                running_backup_taskid = '';
                                m_backup_task_id = '';
                                m_need_update = true;
                            }
                        });
                    }
                }
                else
                {
                    if(running_backup_taskid !== '')
                    {
                        jQuery('#wpvivid_backup_cancel_btn').css({'pointer-events': 'auto', 'opacity': '1'});
                        jQuery('#wpvivid_backup_log_btn').css({'pointer-events': 'auto', 'opacity': '1'});
                        wpvivid_control_backup_unlock();
                        jQuery('#wpvivid_postbox_backup_percent').hide();
                        wpvivid_retrieve_backup_list();
                        wpvivid_retrieve_last_backup_message();
                        wpvivid_retrieve_log_list();
                        running_backup_taskid='';
                    }
                }
                /*if (jsonarray.download.length !== 0) {
                    if(jsonarray.download.result === 'success') {
                        b_has_data = true;
                        task_retry_times = 0;
                        var i = 0;
                        var file_name = '';
                        jQuery('#wpvivid_file_part_' + tmp_current_click_backupid).html("");
                        var b_download_finish = false;
                        jQuery.each(jsonarray.download.files, function (index, value) {
                            i++;
                            file_name = index;
                            var progress = '0%';
                            if (value.status === 'need_download') {
                                if (m_downloading_file_name === file_name) {
                                    m_need_update = true;
                                }
                                jQuery('#wpvivid_file_part_' + tmp_current_click_backupid).append(value.html);
                                //b_download_finish=true;
                            }
                            else if (value.status === 'running') {
                                if (m_downloading_file_name === file_name) {
                                    wpvivid_lock_download(tmp_current_click_backupid);
                                }
                                m_need_update = true;
                                jQuery('#wpvivid_file_part_' + tmp_current_click_backupid).append(value.html);
                                b_download_finish = false;
                            }
                            else if (value.status === 'completed') {
                                if (m_downloading_file_name === file_name) {
                                    wpvivid_unlock_download(tmp_current_click_backupid);
                                    m_downloading_id = '';
                                    m_downloading_file_name = '';
                                }
                                jQuery('#wpvivid_file_part_' + tmp_current_click_backupid).append(value.html);
                                b_download_finish = true;
                            }
                            else if (value.status === 'error') {
                                if (m_downloading_file_name === file_name) {
                                    wpvivid_unlock_download(tmp_current_click_backupid);
                                    m_downloading_id = '';
                                    m_downloading_file_name = '';
                                }
                                alert(value.error);
                                jQuery('#wpvivid_file_part_' + tmp_current_click_backupid).append(value.html);
                                b_download_finish = true;
                            }
                            else if (value.status === 'timeout') {
                                if (m_downloading_file_name === file_name) {
                                    wpvivid_unlock_download(tmp_current_click_backupid);
                                    m_downloading_id = '';
                                    m_downloading_file_name = '';
                                }
                                alert('Download timeout, please retry.');
                                jQuery('#wpvivid_file_part_' + tmp_current_click_backupid).append(value.html);
                                b_download_finish = true;
                            }
                        });
                        jQuery('#wpvivid_file_part_' + tmp_current_click_backupid).append(jsonarray.download.place_html);
                        if (b_download_finish == true) {
                            tmp_current_click_backupid = '';
                        }
                    }
                    else{
                        b_has_data = true;
                        alert(jsonarray.download.error);
                    }
                }*/
                if (!b_has_data) {
                    task_retry_times++;
                    if (task_retry_times < 5) {
                        m_need_update = true;
                    }
                }
            }
            catch(err){
                alert(err);
            }
        }, function (XMLHttpRequest, textStatus, errorThrown)
        {
            task_retry_times++;
            if (task_retry_times < 5)
            {
                setTimeout(function () {
                    m_need_update = true;
                    wpvivid_manage_task();
                }, 3000);
            }
        });
    }
}

/**
 * This function will show the log on a text box.
 *
 * @param data - The log message returned by server
 */
function wpvivid_show_log(data, content_id){
    jQuery('#'+content_id).html("");
    try {
        var jsonarray = jQuery.parseJSON(data);
        if (jsonarray.result === "success") {
            var log_data = jsonarray.data;
            while (log_data.indexOf('\n') >= 0) {
                var iLength = log_data.indexOf('\n');
                var log = log_data.substring(0, iLength);
                log_data = log_data.substring(iLength + 1);
                var insert_log = "<div style=\"clear:both;\">" + log + "</div>";
                jQuery('#'+content_id).append(insert_log);
            }
        }
        else if (jsonarray.result === "failed") {
            jQuery('#'+content_id).html(jsonarray.error);
        }
    }
    catch(err){
        alert(err);
        var div = "Reading the log failed. Please try again.";
        jQuery('#'+content_id).html(div);
    }
}

/**
 * Resume the backup task automatically in 1 minute in a timeout situation
 *
 * @param backup_id         - A unique ID for a backup
 * @param next_resume_time  - A time interval for resuming next timeout backup task
 */
function wpvivid_resume_backup(backup_id, next_resume_time){
    if(next_resume_time < 0){
        next_resume_time = 0;
    }
    next_resume_time = next_resume_time * 1000;
    setTimeout("wpvivid_cron_task()", next_resume_time);
    setTimeout(function(){
        task_retry_times = 0;
        m_need_update=true;
    }, next_resume_time);
}

/**
 * This function will retrieve the last backup message
 */
function wpvivid_retrieve_last_backup_message(){
    var ajax_data={
        'action': 'wpvivid_get_last_backup'
    };
    wpvivid_post_request(ajax_data, function(data){
        try {
            var jsonarray = jQuery.parseJSON(data);
            jQuery('#wpvivid_last_backup_msg').html(jsonarray.data);
        }
        catch(err){
            alert(err);
        }
    }, function(XMLHttpRequest, textStatus, errorThrown) {
        var error_message = wpvivid_output_ajaxerror('retrieving the last backup log', textStatus, errorThrown);
        jQuery('#wpvivid_last_backup_msg').html(error_message);
    });
}

/**
 * This function will control interface flow.
 */
function wpvivid_interface_flow_control(){
    jQuery('#wpvivid_general_email_enable').click(function(){
        if(jQuery('#wpvivid_general_email_enable').prop('checked') === true){
            jQuery('#wpvivid_general_email_setting').show();

        }
        else{
            jQuery('#wpvivid_general_email_setting').hide();
        }
    });

    jQuery("input[name='schedule-backup-files']").bind("click",function(){
        if(jQuery(this).val() === "custom"){
            jQuery('#wpvivid_choosed_folders').show();
            if(jQuery("input[name='wpvivid-schedule-custom-folders'][value='other']").prop('checked')){
                jQuery('#wpvivid_file_tree_browser').show();
            }
            else{
                jQuery('#wpvivid_file_tree_browser').hide();
            }
        }
        else{
            jQuery('#wpvivid_choosed_folders').hide();
            jQuery('#wpvivid_file_tree_browser').hide();
        }
    });

    jQuery("input[name='wpvivid-schedule-custom-folders']").bind("click",function(){
        if(jQuery("input[name='wpvivid-schedule-custom-folders'][value='other']").prop('checked')){
            jQuery('#wpvivid_file_tree_browser').show();
        }
        else{
            jQuery('#wpvivid_file_tree_browser').hide();
        }
    });

    jQuery('#settings-page input[type=checkbox]:not([option=junk-files])').on("change", function(){
        wpvivid_settings_changed = true;
    });

    jQuery('#settings-page input[type=radio]').on("change", function(){
        wpvivid_settings_changed = true;
    });

    jQuery('#settings-page input[type=text]').on("keyup", function(){
        wpvivid_settings_changed = true;
    });

    /*jQuery("#wpvivid_storage_account_block input:not([type=checkbox])").on("keyup", function(){
        wpvivid_settings_changed = true;
    });*/

    /*jQuery('#wpvivid_storage_account_block input[type=checkbox]').on("change", function(){
        wpvivid_settings_changed = true;
    });*/

    jQuery('input:radio[option=restore]').click(function() {
        jQuery('input:radio[option=restore]').each(function () {
            if (jQuery(this).prop('checked')) {
                jQuery('#wpvivid_restore_btn').css({'pointer-events': 'auto', 'opacity': '1'});
            }
        });
    });

    jQuery('input:radio[option=setting][name=backup_params]').click(function()
    {
        if(jQuery(this).prop('checked'))
        {
            var value = jQuery(this).prop('value');
            if(value=='custom')
            {
                jQuery('#wpvivid_custom_backup_params').show();
            }
            else
            {
                jQuery('#wpvivid_custom_backup_params').hide();
            }
        }
    });
}

/**
 * Manage backup and download tasks. Retrieve the data every 3 seconds for checking if the backup or download tasks exist or not.
 */
function wpvivid_manage_task() {
    if(m_need_update === true){
        m_need_update = false;
        wpvivid_check_runningtask();
    }
    else{
        setTimeout(function(){
            wpvivid_manage_task();
        }, 3000);
    }
}

function wpvivid_add_notice(notice_action, notice_type, notice_msg){
    var notice_id="";
    var tmp_notice_msg = "";
    if(notice_type === "Warning"){
        tmp_notice_msg = "Warning: " + notice_msg;
    }
    else if(notice_type === "Error"){
        tmp_notice_msg = "Error: " + notice_msg;
    }
    else if(notice_type === "Success"){
        tmp_notice_msg = "Success: " + notice_msg;
    }
    else if(notice_type === "Info"){
        tmp_notice_msg = notice_msg;
    }
    switch(notice_action){
        case "Backup":
            notice_id="wpvivid_backup_notice";
            break;
    }
    var bfind = false;
    $div = jQuery('#'+notice_id).children('div').children('p');
    $div.each(function (index, value) {
        if(notice_action === "Backup" && notice_type === "Success"){
            bfind = false;
            return false;
        }
        if (value.innerHTML === tmp_notice_msg) {
            bfind = true;
            return false;
        }
    });
    if (bfind === false) {
        jQuery('#'+notice_id).show();
        var div = '';
        if(notice_type === "Warning"){
            div = "<div class='notice notice-warning is-dismissible inline'><p>" + wpvividlion.warning + notice_msg + "</p>" +
                "<button type='button' class='notice-dismiss' onclick='click_dismiss_notice(this);'>" +
                "<span class='screen-reader-text'>Dismiss this notice.</span>" +
                "</button>" +
                "</div>";
        }
        else if(notice_type === "Error"){
            div = "<div class=\"notice notice-error inline\"><p>" + wpvividlion.error + notice_msg + "</p></div>";
        }
        else if(notice_type === "Success"){
            wpvivid_clear_notice('wpvivid_backup_notice');
            jQuery('#wpvivid_backup_notice').show();
            var success_msg = wpvivid_completed_backup + " backup tasks have been completed. Please switch to <a href=\"#\" onclick=\"wpvivid_click_switch_page('wrap', 'wpvivid_tab_log', true);\">Log</a> page to check the details.\n";
            div = "<div class='notice notice-success is-dismissible inline'><p>" + success_msg + "</p>" +
                "<button type='button' class='notice-dismiss' onclick='click_dismiss_notice(this);'>" +
                "<span class='screen-reader-text'>Dismiss this notice.</span>" +
                "</button>" +
                "</div>";
            wpvivid_completed_backup++;
        }
        else if(notice_type === "Info"){
            div = "<div class='notice notice-info is-dismissible inline'><p>" + notice_msg + "</p>" +
                "<button type='button' class='notice-dismiss' onclick='click_dismiss_notice(this);'>" +
                "<span class='screen-reader-text'>Dismiss this notice.</span>" +
                "</button>" +
                "</div>";
        }
        jQuery('#'+notice_id).append(div);
    }
}

function click_dismiss_notice(obj){
    wpvivid_completed_backup = 1;
    jQuery(obj).parent().remove();
}

function wpvivid_cron_task(){
    jQuery.get(wpvivid_siteurl+'/wp-cron.php');
}

function wpvivid_clear_notice(notice_id){
    var t = document.getElementById(notice_id);
    if(t !== null)
    {
        var oDiv = t.getElementsByTagName("div");
        var count = oDiv.length;
        for (count; count > 0; count--) {
            var i = count - 1;
            oDiv[i].parentNode.removeChild(oDiv[i]);
        }
    }
    jQuery('#'+notice_id).hide();
}

function wpvivid_click_switch_page(tab, type, scroll)
{
    jQuery('.'+tab+'-tab-content:not(.' + type + ')').hide();
    jQuery('.'+tab+'-tab-content.' + type).show();
    jQuery('.'+tab+'-nav-tab:not(#' + type + ')').removeClass('nav-tab-active');
    jQuery('.'+tab+'-nav-tab#' + type).addClass('nav-tab-active');
    if(scroll == true){
        var top = jQuery('#'+type).offset().top-jQuery('#'+type).height();
        jQuery('html, body').animate({scrollTop:top}, 'slow');
    }
}

function wpvivid_close_tab(event, hide_tab, type, show_tab){
    event.stopPropagation();
    jQuery('#'+hide_tab).hide();
    if(hide_tab === 'wpvivid_tab_mainwp'){
        wpvivid_hide_mainwp_tab_page();
    }
    wpvivid_click_switch_page(type, show_tab, true);
}

function wpvivid_hide_mainwp_tab_page(){
    var ajax_data = {
        'action': 'wpvivid_hide_mainwp_tab_page'
    };
    wpvivid_post_request(ajax_data, function(res){
    }, function(XMLHttpRequest, textStatus, errorThrown) {
    });
}

/**
 * Output ajax error in a standard format.
 *
 * @param action        - The specific operation
 * @param textStatus    - The textual status message returned by the server
 * @param errorThrown   - The error message thrown by server
 *
 * @returns {string}
 */
function wpvivid_output_ajaxerror(action, textStatus, errorThrown){
    action = 'trying to establish communication with your server';
    var error_msg = "wpvivid_request: "+ textStatus + "(" + errorThrown + "): an error occurred when " + action + ". " +
        "This error may be request not reaching or server not responding. Please try again later.";
        //"This error could be caused by an unstable internet connection. Please try again later.";
    return error_msg;
}

function wpvivid_add_review_info(review){
    var ajax_data={
        'action': 'wpvivid_need_review',
        'review': review
    };
    jQuery('#wpvivid_notice_rate').hide();
    wpvivid_post_request(ajax_data, function(res){
        if(typeof res != 'undefined' && res != ''){
            var tempwindow=window.open('_blank');
            tempwindow.location=res;
        }
    }, function(XMLHttpRequest, textStatus, errorThrown) {
    });
}

function wpvivid_click_amazons3_notice(){
    var ajax_data={
        'action': 'wpvivid_amazons3_notice'
    };
    jQuery('#wpvivid_amazons3_notice').hide();
    wpvivid_post_request(ajax_data, function(res){
    }, function(XMLHttpRequest, textStatus, errorThrown) {
    });
}

function wpvivid_ajax_data_transfer(data_type){
    var json = {};
    jQuery('input:checkbox[option='+data_type+']').each(function() {
        var value = '0';
        var key = jQuery(this).prop('name');
        if(jQuery(this).prop('checked')) {
            value = '1';
        }
        else {
            value = '0';
        }
        json[key]=value;
    });
    jQuery('input:radio[option='+data_type+']').each(function() {
        if(jQuery(this).prop('checked'))
        {
            var key = jQuery(this).prop('name');
            var value = jQuery(this).prop('value');
            json[key]=value;
        }
    });
    jQuery('input:text[option='+data_type+']').each(function(){
        var obj = {};
        var key = jQuery(this).prop('name');
        var value = jQuery(this).val();
        json[key]=value;
    });
    jQuery('textarea[option='+data_type+']').each(function(){
        var obj = {};
        var key = jQuery(this).prop('name');
        var value = jQuery(this).val();
        json[key]=value;
    });
    jQuery('input:password[option='+data_type+']').each(function(){
        var obj = {};
        var key = jQuery(this).prop('name');
        var value = jQuery(this).val();
        json[key]=value;
    });
    jQuery('select[option='+data_type+']').each(function(){
        var obj = {};
        var key = jQuery(this).prop('name');
        var value = jQuery(this).val();
        json[key]=value;
    });
    return JSON.stringify(json);
}languages/wpvivid-backuprestore-bg_BG.po000064400000353246151327705670014401 0ustar00msgid ""
msgstr ""
"Project-Id-Version: wpvivid backup plugin\n"
"Last-Translator: \n"
"Language-Team: Български\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Poedit-KeywordsList: __;_e;esc_attr;esc_attr_e\n"
"X-Poedit-Basepath: ..\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
"X-Poedit-SourceCharset: utf-8\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2020-06-09 14:13+0000\n"
"PO-Revision-Date: 2020-07-27 18:41+0800\n"
"Language: bg_BG\n"
"X-Generator: Poedit 2.3.1\n"
"X-Loco-Version: 2.4.0; wp-5.4.1\n"
"X-Poedit-SearchPath-0: .\n"

#: admin/class-wpvivid-admin.php:100
#: admin/class-wpvivid-admin.php:114
#: admin/class-wpvivid-admin.php:635
msgid "Backup & Restore"
msgstr "Архивиране и възстановяване"

#: admin/class-wpvivid-admin.php:123
#: admin/class-wpvivid-admin.php:313
#: admin/class-wpvivid-admin.php:653
msgid "Settings"
msgstr "Настройки"

#: admin/class-wpvivid-admin.php:139
msgid "Current Version:"
msgstr "Текуща версия:"

#: admin/class-wpvivid-admin.php:142
msgid "ChangeLog"
msgstr "Промени"

#: admin/class-wpvivid-admin.php:149
msgid "Troubleshooting"
msgstr "Отстраняване на проблеми"

#: admin/class-wpvivid-admin.php:154
msgid "Read <a href=\"https://wpvivid.com/troubleshooting-issues-wpvivid-backup-plugin\" target=\"_blank\">Troubleshooting page</a> for faster solutions."
msgstr "Прочетете <a href=\"https://wpvivid.com/troubleshooting-issues-wpvivid-backup-plugin\" target=\"_blank\">Страница за отстраняване на неизправности</a> за по-бързи решения."

#: admin/class-wpvivid-admin.php:157
msgid "Adjust <a href=\"https://wpvivid.com/wpvivid-backup-plugin-advanced-settings.html\" target=\"_blank\">Advanced Settings</a> for higher task success rate."
msgstr "Регулирайте <a href=\"https://wpvivid.com/wpvivid-backup-plugin-advanced-settings.html\" target=\"_blank\">разширените настройки</a> за по-висока скорост на успеваемост на задачите."

#: admin/class-wpvivid-admin.php:164
msgid "How-to"
msgstr "Как-да"

#: admin/class-wpvivid-admin.php:168
msgid "WPvivid Backup Settings"
msgstr "Настройки на WPvivid архивиране"

#: admin/class-wpvivid-admin.php:169
msgid "Create a Manual Backup"
msgstr "Създаване на ръчно архивиране"

#: admin/class-wpvivid-admin.php:170
msgid "Restore Your Site from a Backup"
msgstr "Възстановяване на вашия сайт от архив"

#: admin/class-wpvivid-admin.php:171
msgid "Migrate WordPress"
msgstr "Мигриране на WordPress"

#: admin/class-wpvivid-admin.php:209
msgid "Warning:"
msgstr "Внимание:"

#: admin/class-wpvivid-admin.php:210
msgid "Error:"
msgstr "Грешка:"

#: admin/class-wpvivid-admin.php:211
msgid "Warning: An alias for remote storage is required."
msgstr "Предупреждение: Изисква се псевдоним за отдалечено съхранение."

#: admin/class-wpvivid-admin.php:212
msgid "Warning: The alias already exists in storage list."
msgstr "Предупреждение: Псевдонимът вече съществува в списъка за съхранение."

#: admin/class-wpvivid-admin.php:213
msgid "Calculating the size of files, folder and database timed out. If you continue to receive this error, please go to the plugin settings, uncheck 'Calculate the size of files, folder and database before backing up', save changes, then try again."
msgstr "Изчисляване на размера на файловете, папката и базата данни изтече. Ако продължавате да получавате тази грешка, моля, отидете на настройките на приставката, махнете отметката от \"Изчислете размера на файловете, папките и базата данни, преди да архивирате\", запишете промените и опитайте отново."

#: admin/class-wpvivid-admin.php:346
msgid "Migration is complete and htaccess file is replaced. In order to successfully complete the migration, you'd better reinstall 301 redirect plugin, firewall and security plugin, and caching plugin if they exist."
msgstr "Миграцията е завършена и htaccess файлът се заменя. За да завършите успешно миграцията, по-добре е да преинсталирате 301 добавка за пренасочване, защитна стена и приставка за сигурност и кеширане плъгин, ако те съществуват."

#: admin/class-wpvivid-admin.php:361
msgid "Cheers! WPvivid Backup plugin has restored successfully your website. If you found WPvivid Backup plugin helpful, a 5-star rating would be highly appreciated, which motivates us to keep providing new features."
msgstr "Поздрав! WPvivid Backup плъгинът е възстановил успешно вашия сайт. Ако установите, че WPvivid Backup плъгинът е полезен, ще сме много благодарни, ако ни дадете 5-звезден рейтинг, което ни мотивира да продължим да предоставяме нови функции."

#: admin/class-wpvivid-admin.php:367
msgid "Restore completed successfully."
msgstr "Възстановяването завърши успешно."

#: admin/class-wpvivid-admin.php:454
#: admin/class-wpvivid-admin.php:471
msgid "Rate Us"
msgstr "Оценете ни"

#: admin/class-wpvivid-admin.php:455
#: admin/class-wpvivid-admin.php:472
msgid "Maybe Later"
msgstr "Може би по-късно"

#: admin/class-wpvivid-admin.php:456
#: admin/class-wpvivid-admin.php:473
msgid "Never"
msgstr "Никога"

#: admin/class-wpvivid-admin.php:509
msgid "As Amazon S3 and DigitalOcean Space have upgraded their connection methods, please delete the previous connections and re-add your Amazon S3/DigitalOcean Space accounts to make sure the connections work."
msgstr "Както Amazon S3 и DigitalOcean Space са модернизирани своите методи за свързване, моля, изтрийте предишните връзки и добавете отново вашите Amazon S3 /DigitalOcean Space сметки, за да се уверите, че връзките работят."

#: admin/class-wpvivid-admin.php:561
#, php-format
msgid "The %s extension is not detected. Please install the extension first."
msgstr "Не е открито разширение %s. Първо инсталирайте разширението."

#: admin/class-wpvivid-admin.php:564
#, php-format
msgid "The %s extensions are not detected. Please install the extensions first."
msgstr "Не са открити разширения %s. Първо инсталирайте разширенията."

#: admin/class-wpvivid-admin.php:570
msgid "Class PclZip is not detected. Please update or reinstall your WordPress."
msgstr "Не се открива клас PCLZip. Актуализирайте или преинсталирайте wordPress."

#: admin/class-wpvivid-admin.php:575
msgid "In order to execute the scheduled backups properly, please set the DISABLE_WP_CRON constant to false."
msgstr "За да изпълните планираните архиви правилно, задайте DISABLE_WP_CRON постоянна на неистински."

#: admin/class-wpvivid-admin.php:641
msgid "Schedule"
msgstr "Планиране"

#: admin/class-wpvivid-admin.php:647
msgid "Remote Storage"
msgstr "Отдалечено съхранение"

#: admin/class-wpvivid-admin.php:659
msgid "Debug"
msgstr "Отстраняване на грешки"

#: admin/class-wpvivid-admin.php:665
msgid "Logs"
msgstr "Регистри"

#: admin/class-wpvivid-admin.php:672
#: admin/partials/wpvivid-backup-restore-page-display.php:130
#: admin/partials/wpvivid-backup-restore-page-display.php:1505
msgid "Log"
msgstr "Регистър"

#: admin/class-wpvivid-admin.php:683
msgid "MainWP"
msgstr "MainWP"

#: admin/class-wpvivid-admin.php:693
msgid "Premium"
msgstr "Премиум"

#: admin/class-wpvivid-admin.php:735
#: admin/class-wpvivid-admin.php:822
#: admin/partials/wpvivid-remote-storage-page-display.php:48
msgid "Save Changes"
msgstr "Запази промените"

#: admin/class-wpvivid-admin.php:870
msgid "There are two ways available to send us the debug information. The first one is recommended."
msgstr "Има два начина за изпращане на информацията за отстраняване на грешки. Първият се препоръчва."

#: admin/class-wpvivid-admin.php:872
msgid "Method 1."
msgstr "Метод 1."

#: admin/class-wpvivid-admin.php:872
msgid "If you have configured SMTP on your site, enter your email address and click the button below to send us the relevant information (website info and errors logs) when you are encountering errors. This will help us figure out what happened. Once the issue is resolved, we will inform you by your email address."
msgstr "Ако сте конфигурирали SMTP на вашия сайт, въведете вашия имейл адрес и щракнете върху бутона по-долу, за да ни изпратите съответната информация (уеб сайт информация и грешки регистрационни файлове), когато се натъкнете на грешки. Това ще ни помогне да разберем какво се е случило. След като проблемът бъде решен, ще Ви информираме на вашия имейл адрес."

#: admin/class-wpvivid-admin.php:875
msgid "WPvivid support email:"
msgstr "WPvivid имейл за поддръжка:"

#: admin/class-wpvivid-admin.php:876
msgid "Your email:"
msgstr "Вашият имейл:"

#: admin/class-wpvivid-admin.php:881
msgid "I am using:"
msgstr "Аз използвам:"

#: admin/class-wpvivid-admin.php:893
msgid "My web hosting provider is:"
msgstr "Моят доставчик на уеб хостинг е:"

#: admin/class-wpvivid-admin.php:902
msgid "Please describe your problem here."
msgstr "Моля, опишете проблема си тук."

#: admin/class-wpvivid-admin.php:905
msgid "Send Debug Information to Us"
msgstr "Изпращане на информация за отстраняване на грешки до нас"

#: admin/class-wpvivid-admin.php:909
msgid "Method 2."
msgstr "Метод 2."

#: admin/class-wpvivid-admin.php:909
msgid "If you didn’t configure SMTP on your site, click the button below to download the relevant information (website info and error logs) to your PC when you are encountering some errors. Sending the files to us will help us diagnose what happened."
msgstr "Ако не сте конфигурирали SMTP на сайта си, щракнете върху бутона по-долу, за да изтеглите съответната информация (информация за уеб сайта и регистрационни файлове за грешки) на вашия компютър, когато срещате някои грешки. Изпращането на файловете до нас ще ни помогне да диагностицираме какво се е случило."

#: admin/class-wpvivid-admin.php:912
#: admin/partials/wpvivid-backup-restore-page-display.php:178
msgid "Download"
msgstr "Изтегляне"

#: admin/class-wpvivid-admin.php:916
msgid "Website Info Key"
msgstr "Ключ за информация за уеб сайта"

#: admin/class-wpvivid-admin.php:917
msgid "Website Info Value"
msgstr "Стойност на уеб сайта"

#: admin/class-wpvivid-admin.php:1030
msgid "Date"
msgstr "Дата"

#: admin/class-wpvivid-admin.php:1031
msgid "Log Type"
msgstr "Тип на регистъра"

#: admin/class-wpvivid-admin.php:1032
msgid "Log File Name"
msgstr "Име на регистрационния файл"

#: admin/class-wpvivid-admin.php:1033
msgid "Action"
msgstr "Действие"

#: admin/class-wpvivid-admin.php:1045
msgid " < Pre page "
msgstr " < Преди страница  "

#: admin/class-wpvivid-admin.php:1056
msgid " Next page > "
msgstr " Следваща страница > "

#: admin/class-wpvivid-admin.php:1164
msgid "If you are a MainWP user, you can set up and control WPvivid Backup Free and Pro for every child site directly from your MainWP dashboard, using our WPvivid Backup for MainWP extension."
msgstr "Ако сте MainWP потребител, можете да настроите и управлявате WPvivid Backup Free и Pro за всеки сайт на детето директно от таблото на MainWP, като използвате нашия WPvivid Backup за mainWP разширение."

#: admin/class-wpvivid-admin.php:1167
msgid "Download WPvivid Backup for MainWP"
msgstr "Изтегляне на WPvivid архивиране за MainWP"

#: admin/class-wpvivid-admin.php:1170
msgid "1. Create and download backups for a specific child site"
msgstr "1. Създаване и изтегляне на резервни копия за конкретен дъщерен сайт"

#: admin/class-wpvivid-admin.php:1173
msgid "2. Set backup schedules for all child sites"
msgstr "2. Задайте графици за архивиране за всички дъщерни сайтове"

#: admin/class-wpvivid-admin.php:1177
msgid "3. Set WPvivid Backup Free and Pro settings for all child sites"
msgstr "3. Задайте WPvivid Архивиране Безплатни и Pro настройки за всички сайтове за деца"

#: admin/class-wpvivid-admin.php:1182
msgid "4. Install, claim and update WPvivid Backup Pro for child sites in bulk"
msgstr "4. Инсталиране, претенции и актуализация WPvivid Backup Pro за дъщерни сайтове в насипно състояние"

#: admin/class-wpvivid-admin.php:1187
msgid "5. Set up remote storage for child sites in bulk (for WPvivid Backup Pro only)"
msgstr "5. Настройте отдалечено съхранение за деца сайтове в насипно състояние (за WPvivid Backup Pro само)"

#: admin/class-wpvivid-admin.php:1192
#, php-format
msgid "We also offer a 40% off discount on WPvivid Backup Pro for MainWP users."
msgstr "Ние също можем да предложим 40% oотстъпка за WPvivid Backup Pro за потребители на MainWP."

#: admin/class-wpvivid-admin.php:1196
msgid "Ask For A 40% OFF Discount"
msgstr "Попитайте за 40% Отстъпка"

#: admin/class-wpvivid-admin.php:1219
msgid "Pro Version Features"
msgstr "Функции на версията на Pro"

#: admin/class-wpvivid-admin.php:1220
msgid "Basic"
msgstr "Основни"

#: admin/class-wpvivid-admin.php:1221
msgid "Freelancer"
msgstr "На свободна практика"

#: admin/class-wpvivid-admin.php:1222
msgid "Ultimate"
msgstr "Ултимат"

#: admin/class-wpvivid-admin.php:1228
msgid "Websites"
msgstr "Сайтове"

#: admin/class-wpvivid-admin.php:1229
#: admin/class-wpvivid-admin.php:1230
#: admin/class-wpvivid-admin.php:1231
#: admin/class-wpvivid-admin.php:1232
msgid "Backup:"
msgstr "Архивиране:"

#: admin/class-wpvivid-admin.php:1229
#: admin/class-wpvivid-admin.php:1233
msgid "Custom Content"
msgstr "Собствено Съдържание"

#: admin/class-wpvivid-admin.php:1230
msgid "Incremental Backup"
msgstr "Постъпково архивиране"

#: admin/class-wpvivid-admin.php:1231
msgid "Create a restore point"
msgstr "Създаване на точка на възстановяване"

#: admin/class-wpvivid-admin.php:1232
msgid "Include/exclude files/folders"
msgstr "Включване/изключване на файлове/папки"

#: admin/class-wpvivid-admin.php:1233
#: admin/class-wpvivid-admin.php:1234
msgid "Migration:"
msgstr "Миграция:"

#: admin/class-wpvivid-admin.php:1234
msgid "Migration via remote storage"
msgstr "Миграция чрез отдалечено съхранение"

#: admin/class-wpvivid-admin.php:1235
#: admin/class-wpvivid-admin.php:1236
msgid "Remote Storage:"
msgstr "Отдалечено съхранение:"

#: admin/class-wpvivid-admin.php:1235
msgid "Custom Directory (leading cloud storage providers)"
msgstr "Персонализиран указател (водещи доставчици на облачно съхранение)"

#: admin/class-wpvivid-admin.php:1236
msgid "WASABI/Pcloud (Only Pro)"
msgstr "WASABI/Pcloud (само Pro)"

#: admin/class-wpvivid-admin.php:1237
#: admin/class-wpvivid-admin.php:1238
#: admin/class-wpvivid-admin.php:1239
#: admin/class-wpvivid-admin.php:1240
msgid "Schedule:"
msgstr "График:"

#: admin/class-wpvivid-admin.php:1237
msgid "Incremental Backup Schedule"
msgstr "График за постъпково архивиране"

#: admin/class-wpvivid-admin.php:1238
msgid "Custom Timezone"
msgstr "Часова зона по избор"

#: admin/class-wpvivid-admin.php:1239
msgid "Custom content for each schedule"
msgstr "Потребителско съдържание за всеки график"

#: admin/class-wpvivid-admin.php:1240
msgid "Custom start time of schedule"
msgstr "Персонализирано начало на графика"

#: admin/class-wpvivid-admin.php:1241
#: admin/class-wpvivid-admin.php:1242
msgid "Restore:"
msgstr "Възстановяване:"

#: admin/class-wpvivid-admin.php:1241
msgid "Restore a website from remote storage"
msgstr "Възстановяване на уеб сайт от отдалечено хранилище"

#: admin/class-wpvivid-admin.php:1242
msgid "Restore what you want from a backup"
msgstr "Възстановяване на това, което искате от архив"

#: admin/class-wpvivid-admin.php:1243
msgid "Email Reports:"
msgstr "Имейл отчети:"

#: admin/class-wpvivid-admin.php:1243
msgid "Send email reports to multiple email addresses"
msgstr "Изпращане на имейл отчети до няколко имейл адреса"

#: admin/class-wpvivid-admin.php:1244
#: admin/class-wpvivid-admin.php:1245
msgid "Staging (add-on):"
msgstr "Спиране (добавка):"

#: admin/class-wpvivid-admin.php:1244
msgid "Create a sub-directory staging site with one-click"
msgstr "Създаване на поддиректорен сайт с едно кликване"

#: admin/class-wpvivid-admin.php:1245
msgid "Publish a staging site to a live site with one-click"
msgstr "Публикуване на сайт за спиране в сайт на живо с едно кликване"

#: admin/class-wpvivid-admin.php:1246
msgid "Roles & Capabilities (add-on):"
msgstr "Роли и възможности (добавка):"

#: admin/class-wpvivid-admin.php:1246
msgid "Display the individual sections according to user roles & capabilities"
msgstr "Показване на отделни секции според ролите на потребителите & способностите"

#: admin/class-wpvivid-admin.php:1247
msgid "Support:"
msgstr "Подкрепа:"

#: admin/class-wpvivid-admin.php:1247
msgid "Ticket 7x24 support"
msgstr "Поддръжка на билет 7x24"

#: admin/class-wpvivid-admin.php:1250
msgid "Up to 3 sites"
msgstr "До 3 сайта"

#: admin/class-wpvivid-admin.php:1272
msgid "Up to 100 sites"
msgstr "До 100 сайта"

#: admin/class-wpvivid-admin.php:1294
msgid "Unlimited"
msgstr "Неограничено"

#: admin/class-wpvivid-admin.php:1319
msgid "*No credit card needed. Trial starts with the Free Trial plan with 2 sites. You can choose a plan at the end of the trial."
msgstr "*Не е необходима кредитна карта. Пробният период започва с плана за безплатен пробен период с 2 обекта. Можете да изберете план в края на процеса."

#: admin/class-wpvivid-admin.php:1320
msgid "START 14-DAY FREE TRIAL"
msgstr "ЗАПОЧНЕТЕ 14-дневен безплатен пробен период"

#: admin/partials/wpvivid-admin-display.php:49
msgid "WPvivid Backup Plugin"
msgstr "WPvivid Backup плъгин"

#: admin/partials/wpvivid-admin-display.php:63
msgid "Warning: There is no default remote storage available for the scheduled backups, please set up it first."
msgstr "Предупреждение: Няма налично отдалечено място за съхранение по подразбиране за планираните архиви, първо го настройте."

#: admin/partials/wpvivid-backup-restore-page-display.php:7
#: admin/partials/wpvivid-schedule-page-display.php:126
msgid "Database + Files (WordPress Files)"
msgstr "База данни + файлове (файлове в WordPress)"

#: admin/partials/wpvivid-backup-restore-page-display.php:11
#: admin/partials/wpvivid-schedule-page-display.php:131
msgid "WordPress Files (Exclude Database)"
msgstr "Файлове в WordPress (изключване на базата данни)"

#: admin/partials/wpvivid-backup-restore-page-display.php:15
#: admin/partials/wpvivid-schedule-page-display.php:136
msgid "Only Database"
msgstr "Само база данни"

#: admin/partials/wpvivid-backup-restore-page-display.php:20
msgid "Create a staging site"
msgstr "Създаване на сайт за спиране"

#: admin/partials/wpvivid-backup-restore-page-display.php:23
#: admin/partials/wpvivid-backup-restore-page-display.php:32
#: admin/partials/wpvivid-schedule-page-display.php:24
#: admin/partials/wpvivid-schedule-page-display.php:36
#: admin/partials/wpvivid-schedule-page-display.php:98
#: admin/partials/wpvivid-schedule-page-display.php:146
msgid "Pro feature: learn more"
msgstr "Pro функция: научете повече"

#: admin/partials/wpvivid-backup-restore-page-display.php:29
#: admin/partials/wpvivid-schedule-page-display.php:142
msgid "Custom"
msgstr "Персонализиране"

#: admin/partials/wpvivid-backup-restore-page-display.php:100
msgid "About backup download"
msgstr "За изтеглянето на архив"

#: admin/partials/wpvivid-backup-restore-page-display.php:102
msgid "->If backups are stored in remote storage, our plugin will retrieve the backup to your web server first. This may take a little time depending on the size of backup files. Please be patient. Then you can download them to your PC."
msgstr "->Ако архивите се съхраняват в отдалечено хранилище, нашата приставка ще извлече архива на вашия уеб сървър първо. Това може да отнеме малко време в зависимост от размера на архивните файлове. Моля те, бъди търпелив. След това можете да ги изтеглите на компютъра си."

#: admin/partials/wpvivid-backup-restore-page-display.php:103
msgid "->If backups are stored in web server, the plugin will list all relevant files immediately."
msgstr "->Ако архивите се съхраняват в уеб сървъра, приставката ще изброи всички съответни файлове незабавно."

#: admin/partials/wpvivid-backup-restore-page-display.php:109
msgid "How to restore your website from a backup(scheduled, manual, uploaded and received backup)"
msgstr "Как да възстановите уебсайта си от архивно копие (планирано, ръчно, качено и получи резервно копие)"

#: admin/partials/wpvivid-backup-restore-page-display.php:123
msgid "Backups"
msgstr "Архивиране"

#: admin/partials/wpvivid-backup-restore-page-display.php:141
#: admin/partials/wpvivid-backup-restore-page-display.php:179
#: admin/partials/wpvivid-backup-restore-page-display.php:892
msgid "Restore"
msgstr "Възстановяване"

#: admin/partials/wpvivid-backup-restore-page-display.php:176
msgid "Backup"
msgstr "Архивиране"

#: admin/partials/wpvivid-backup-restore-page-display.php:177
msgid "Storage"
msgstr "Обем"

#: admin/partials/wpvivid-backup-restore-page-display.php:180
msgid "Delete"
msgstr "Изтрий"

#: admin/partials/wpvivid-backup-restore-page-display.php:193
msgid "Delete the selected backups"
msgstr "Изтриване на избраните архиви"

#: admin/partials/wpvivid-backup-restore-page-display.php:725
msgid "This backup is locked, are you sure to remove it? This backup will be deleted permanently from your hosting (localhost) and remote storages."
msgstr "Това архивиране е заключено, сигурни ли сте, че сте го махнали? Това архивиране ще бъде изтрито завинаги от вашия хостинг (Localhost) и отдалечени складове."

#: admin/partials/wpvivid-backup-restore-page-display.php:729
msgid "Are you sure to remove this backup? This backup will be deleted permanently from your hosting (localhost) and remote storages."
msgstr "Сигурни ли сте, че сте го махнали? Това архивиране ще бъде изтрито завинаги от вашия хостинг (Localhost) и отдалечени складове."

#: admin/partials/wpvivid-backup-restore-page-display.php:734
msgid "This request will delete the backup being downloaded, are you sure you want to continue?"
msgstr "Тази заявка ще изтрие изтегления архив, сигурен ли сте, че искате да продължите?"

#: admin/partials/wpvivid-backup-restore-page-display.php:773
msgid "Please select at least one item."
msgstr "Изберете поне един елемент."

#: admin/partials/wpvivid-backup-restore-page-display.php:778
msgid "This request might delete the backup being downloaded, are you sure you want to continue?"
msgstr "Тази заявка може да изтрие изтегленото резервно копие, наистина ли искате да продължите?"

#: admin/partials/wpvivid-backup-restore-page-display.php:781
msgid "Are you sure to remove the selected backups? These backups will be deleted permanently from your hosting (localhost)."
msgstr "Сигурни ли сте, че сте избрали да премахнете избраните архиви? Тези архиви ще бъдат изтрити завинаги от хостинга ви (Localhost)."

#: admin/partials/wpvivid-backup-restore-page-display.php:842
msgid "Step One: In the backup list, click the 'Restore' button on the backup you want to restore. This will bring up the restore tab"
msgstr "Първа стъпка: В списъка за архивиране, кликнете върху \"Възстановяване\" бутон на архива, който искате да възстановите. Това ще доведе до раздела за възстановяване"

#: admin/partials/wpvivid-backup-restore-page-display.php:843
msgid "Step Two: Choose an option to complete restore, if any"
msgstr "Стъпка две: Изберете опция за завършване на възстановяването, ако има такава"

#: admin/partials/wpvivid-backup-restore-page-display.php:844
msgid "Step Three: Click 'Restore' button"
msgstr "Стъпка трета: Кликнете върху бутона \"Възстановяване\""

#: admin/partials/wpvivid-backup-restore-page-display.php:877
msgid "Restore backup from:"
msgstr "Възстановяване на архив от:"

#: admin/partials/wpvivid-backup-restore-page-display.php:878
msgid "Please do not close the page or switch to other pages when a restore task is running, as it could trigger some unexpected errors."
msgstr "Моля, не затваряйте страницата и не превключвайте към други страници, когато изпълнява задача за възстановяване, тъй като тя може да предизвика някои неочаквани грешки."

#: admin/partials/wpvivid-backup-restore-page-display.php:879
msgid "Restore function will replace the current site's themes, plugins, uploads, database and/or other content directories with the existing equivalents in the selected backup."
msgstr "Функцията за възстановяване ще замени темите, плъгините, качването, базата данни и/или други директории на съдържанието със съществуващите еквиваленти в избрания архив."

#: admin/partials/wpvivid-backup-restore-page-display.php:882
#, php-format
msgid "Restore and replace the original domain (URL) with %s (migration)"
msgstr "Възстановяване и заместване на първоначалния домейн (URL) с %s (миграция)"

#: admin/partials/wpvivid-backup-restore-page-display.php:885
msgid "Restore and keep the original domain (URL) unchanged"
msgstr "Възстановяване и запазване на оригиналния домейн (URL) непроменен"

#: admin/partials/wpvivid-backup-restore-page-display.php:889
#: admin/partials/wpvivid-backup-restore-page-display.php:1787
msgid "Tips:"
msgstr "Съвети:"

#: admin/partials/wpvivid-backup-restore-page-display.php:889
msgid "The plugin detects automatically either site restoration or migration (replacing the domain name) based on the current domain name. If the domain name in backup file is same as the current one, it starts restoring. On the contrary, restoring backup means to replace with the current domain name. The precondition is that the backup is created by version 0.9.21 or later."
msgstr "Приставката открива автоматично или възстановяване на сайт или миграция (заместване на името на домейна) въз основа на текущото име на домейн. Ако името на домейна в архивния файл е същото като текущото, то започва да се възстановява. Напротив, възстановяването на резервното копие означава да замени с текущото име на домейн. Предварително условие е, че архивирането е създаден от версия 0.9.21 или по-нова."

#: admin/partials/wpvivid-backup-restore-page-display.php:893
msgid "Terminate"
msgstr "Прекрати"

#: admin/partials/wpvivid-backup-restore-page-display.php:894
msgid "Rollback"
msgstr "Връщане"

#: admin/partials/wpvivid-backup-restore-page-display.php:896
msgid "Retrieve the backup to localhost"
msgstr "Извличане на резервно копие на localhost"

#: admin/partials/wpvivid-backup-restore-page-display.php:897
msgid "The backup is stored on the remote storage, click on the button to download it to localhost."
msgstr "Архивът се съхранява на отдалеченото хранилище, кликнете върху бутона, за да го изтеглите в Localhost."

#: admin/partials/wpvivid-backup-restore-page-display.php:1489
msgid "Database Size:"
msgstr "Размер на базата данни:"

#: admin/partials/wpvivid-backup-restore-page-display.php:1490
msgid "File Size:"
msgstr "Размер на файла (kb)"

#: admin/partials/wpvivid-backup-restore-page-display.php:1493
#: admin/partials/wpvivid-settings-page-display.php:291
msgid "Total Size:"
msgstr "Общ размер:"

#: admin/partials/wpvivid-backup-restore-page-display.php:1494
msgid "Uploaded:"
msgstr "Качил:"

#: admin/partials/wpvivid-backup-restore-page-display.php:1495
msgid "Speed:"
msgstr "Скорост:"

#: admin/partials/wpvivid-backup-restore-page-display.php:1498
msgid "Network Connection:"
msgstr "Мрежова връзка:"

#: admin/partials/wpvivid-backup-restore-page-display.php:1504
msgid "Cancel"
msgstr "Отказ"

#: admin/partials/wpvivid-backup-restore-page-display.php:1557
msgid "Back Up Manually"
msgstr "Ръчно архивиране"

#: admin/partials/wpvivid-backup-restore-page-display.php:1559
msgid "Export Content"
msgstr "Извличане на съдържание"

#: admin/partials/wpvivid-backup-restore-page-display.php:1560
msgid "new feature"
msgstr "нова функция"

#: admin/partials/wpvivid-backup-restore-page-display.php:1563
msgid "Local Storage Directory:"
msgstr "Директория за локални хранилища:"

#: admin/partials/wpvivid-backup-restore-page-display.php:1565
msgid "rename directory"
msgstr "преименуване на директория"

#: admin/partials/wpvivid-backup-restore-page-display.php:1594
msgid "Save Backups to Local"
msgstr "Запиши архивите локално"

#: admin/partials/wpvivid-backup-restore-page-display.php:1598
msgid "Send Backup to Remote Storage:"
msgstr "Изпращане на резервно копие на отдалечено хранилище:"

#: admin/partials/wpvivid-backup-restore-page-display.php:1611
msgid "Backup Now"
msgstr "Архивиране сега"

#: admin/partials/wpvivid-backup-restore-page-display.php:1616
msgid "This backup can only be deleted manually"
msgstr "Това архивиране може да бъде изтрито само ръчно"

#: admin/partials/wpvivid-backup-restore-page-display.php:1787
msgid "The settings are only for manual backup, which won't affect schedule settings."
msgstr "Настройките са само за ръчно архивиране, което няма да засегне настройките за график."

#: admin/partials/wpvivid-backup-restore-page-display.php:1806
msgid "Backup Schedule"
msgstr "Разписание за архивиране"

#: admin/partials/wpvivid-backup-restore-page-display.php:1808
msgid "Schedule Status: "
msgstr "Състояние на разписание: "

#: admin/partials/wpvivid-backup-restore-page-display.php:1810
msgid "Server Time: "
msgstr "Час на сървъра: "

#: admin/partials/wpvivid-backup-restore-page-display.php:1812
msgid "Next Backup: "
msgstr "Следващо архивиране: "

#: admin/partials/wpvivid-remote-storage-page-display.php:6
#: admin/partials/wpvivid-remote-storage-page-display.php:318
msgid "Storages"
msgstr "Хранилища"

#: admin/partials/wpvivid-remote-storage-page-display.php:14
#: admin/partials/wpvivid-remote-storage-page-display.php:320
msgid "Storage Edit"
msgstr "Редактиране на хранилище"

#: admin/partials/wpvivid-remote-storage-page-display.php:26
#: admin/partials/wpvivid-remote-storage-page-display.php:327
msgid "Please choose one storage to save your backups (remote storage)"
msgstr "Изберете едно място за съхранение, за да запазите архивите си (отдалечено хранилище)"

#: admin/partials/wpvivid-remote-storage-page-display.php:34
#: admin/partials/wpvivid-remote-storage-page-display.php:335
msgid "Storage Provider"
msgstr "Доставчик на хранилище"

#: admin/partials/wpvivid-remote-storage-page-display.php:35
#: admin/partials/wpvivid-remote-storage-page-display.php:336
msgid "Remote Storage Alias"
msgstr "Псевдоним на отдалечено хранилище"

#: admin/partials/wpvivid-remote-storage-page-display.php:36
#: admin/partials/wpvivid-remote-storage-page-display.php:337
msgid "Actions"
msgstr "Действия"

#: admin/partials/wpvivid-schedule-page-display.php:7
msgid "Schedule Settings"
msgstr "Настройки на графика"

#: admin/partials/wpvivid-schedule-page-display.php:15
msgid "Enable backup schedule"
msgstr "Разрешаване на разписанието за архивиране"

#: admin/partials/wpvivid-schedule-page-display.php:20
msgid "Enable Incremental Backup"
msgstr "Разрешаване на постъпково архивиране"

#: admin/partials/wpvivid-schedule-page-display.php:32
msgid "Advanced Schedule"
msgstr "Разширено разписание"

#: admin/partials/wpvivid-schedule-page-display.php:85
msgid "Highlighted icon illuminates that you have choosed a remote storage to store backups"
msgstr "Маркирана икона осветява, че сте избрали отдалечено съхранение за съхранение на архиви"

#: admin/partials/wpvivid-schedule-page-display.php:95
msgid "+ Add another schedule"
msgstr "+ Добавяне на друг график"

#: admin/partials/wpvivid-schedule-page-display.php:117
msgid "Scheduled job will start at <strong>UTC</strong> time:"
msgstr "Планираното задание ще започне по <strong>UTC</strong> време:"

#: admin/partials/wpvivid-schedule-page-display.php:118
msgid "Being subjected to mechanisms of PHP, a scheduled backup task for your site will be triggered only when the site receives at least a visit at any page."
msgstr "Като се подлага на механизми на PHP, планирана бекъп задача за вашия сайт ще се задейства само когато сайтът получи поне посещение на всяка страница."

#: admin/partials/wpvivid-settings-page-display.php:88
msgid "backups retained"
msgstr "запазени архиви"

#: admin/partials/wpvivid-settings-page-display.php:88
msgid "Pro feature: Retain more backups"
msgstr "Pro функция: Запазват повече резервни копия"

#: admin/partials/wpvivid-settings-page-display.php:93
msgid "Calculate the size of files, folder and database before backing up"
msgstr "Изчислете размера на файловете, папките и базата данни, преди да архивирате"

#: admin/partials/wpvivid-settings-page-display.php:99
msgid "Show WPvivid backup plugin on top admin bar"
msgstr "Показване на WPvivid резервна добавка на горната администраторска лента"

#: admin/partials/wpvivid-settings-page-display.php:105
msgid "Merge all the backup files into single package when a backup completes. This will save great disk spaces, though takes longer time. We recommended you check the option especially on sites with insufficient server resources."
msgstr "Обединяване на всички архивни файлове в един пакет, когато архивирането завърши. Това ще спести големи дискови пространства, макар че отнема повече време. Препоръчваме ви да проверите опцията, особено на сайтове с недостатъчни сървърни ресурси."

#: admin/partials/wpvivid-settings-page-display.php:111
msgid "Keep storing the backups in localhost after uploading to remote storage"
msgstr "Съхранявайте архивите в Localhost след качването на отдалечено хранилище"

#: admin/partials/wpvivid-settings-page-display.php:116
msgid "Backup Folder"
msgstr "Папка за архивиране"

#: admin/partials/wpvivid-settings-page-display.php:118
msgid "Name your folder, this folder must be writable for creating backup files."
msgstr "Името на папката трябва да може да се записва за създаване на архивни файлове."

#: admin/partials/wpvivid-settings-page-display.php:120
msgid "Local storage directory:"
msgstr "Директория за локални хранилища:"

#: admin/partials/wpvivid-settings-page-display.php:125
msgid "Display domain(url) of current site in backup name. (e.g. domain_wpvivid-5ceb938b6dca9_2019-05-27-07-36_backup_all.zip)"
msgstr "Показване на домейн(URL) на текущия сайт в името на архива. (например domain_wpvivid-5ceb938b6dca9_2019-05-27-07-36_backup_all.zip)"

#: admin/partials/wpvivid-settings-page-display.php:130
msgid "Remove out-of-date backups"
msgstr "Премахване на архиви с несъстояли"

#: admin/partials/wpvivid-settings-page-display.php:134
msgid "Web Server Directory:"
msgstr "Директория на уеб сървъра:"

#: admin/partials/wpvivid-settings-page-display.php:135
msgid "Remote Storage Directory:"
msgstr "Директория за отдалечено съхранение:"

#: admin/partials/wpvivid-settings-page-display.php:147
msgid "Remove"
msgstr "Премахни"

#: admin/partials/wpvivid-settings-page-display.php:148
msgid "The action is irreversible! It will remove all backups are out-of-date (including local web server and remote storage) if they exist."
msgstr "Действието е необратимо! Той ще премахне всички архиви са отечещи на дата (включително локален уеб сървър и отдалечено съхранение), ако те съществуват."

#: admin/partials/wpvivid-settings-page-display.php:208
msgid "In order to use this function, please install a <strong><a target=\"_blank\" href=\"https://wpvivid.com/8-best-smtp-plugins-for-wordpress.html\" style=\"text-decoration: none;\">WordPress SMTP plugin</a></strong> of your preference and configure your SMTP server first. This is because WordPress uses the PHP Mail function to send its emails by default, which is not supported by many hosts and can cause issues if it is not set properly."
msgstr "За да използвате тази функция, моля инсталирайте <strong><a target=\"_blank\" href=\"https://wpvivid.com/8-best-smtp-plugins-for-wordpress.html\" style=\"text-decoration: none;\">WordPress SMTP плъгин</a></strong> на вашите предпочитания и първо да конфигурирате вашия SMTP сървър. Това е така, защото WordPress използва php Mail функция, за да изпраща имейлите си по подразбиране, което не се поддържа от много хостове и може да предизвика проблеми, ако не е зададено правилно."

#: admin/partials/wpvivid-settings-page-display.php:213
msgid "Enable email report"
msgstr "Разрешаване на отчет за имейл"

#: admin/partials/wpvivid-settings-page-display.php:227
msgid "Test Email"
msgstr "Тест на имейл"

#: admin/partials/wpvivid-settings-page-display.php:232
msgid "Always send an email notification when a backup is complete"
msgstr "Винаги изпращайте имейл съобщение, когато архивирането завърши"

#: admin/partials/wpvivid-settings-page-display.php:236
msgid "Only send an email notification when a backup fails"
msgstr "Само изпраща имейл съобщение, когато архивиране е неуспешно"

#: admin/partials/wpvivid-settings-page-display.php:240
msgid "Pro feature: Add another email address to get report"
msgstr "Pro функция: Добавете друг имейл адрес, за да получите отчет"

#: admin/partials/wpvivid-settings-page-display.php:287
msgid "Web-server disk space in use by WPvivid"
msgstr "Уеб сървър дисково пространство използвани от WPvivid"

#: admin/partials/wpvivid-settings-page-display.php:293
msgid "Calculate Sizes"
msgstr "Изчисляване на размери"

#: admin/partials/wpvivid-settings-page-display.php:298
msgid "logs"
msgstr "регистри"

#: admin/partials/wpvivid-settings-page-display.php:299
#: admin/partials/wpvivid-settings-page-display.php:310
#: admin/partials/wpvivid-settings-page-display.php:317
msgid "Path:"
msgstr "Път:"

#: admin/partials/wpvivid-settings-page-display.php:305
msgid "Backup Cache"
msgstr "Кеш за архивиране"

#: admin/partials/wpvivid-settings-page-display.php:309
msgid "Junk"
msgstr "Нежелана"

#: admin/partials/wpvivid-settings-page-display.php:316
msgid "Temporary Files"
msgstr "Временни файлове"

#: admin/partials/wpvivid-settings-page-display.php:318
msgid "Temporary Files are created by wpvivid when restoring a website."
msgstr "Временни файлове са създадени от wpvivid при възстановяване на уеб сайт."

#: admin/partials/wpvivid-settings-page-display.php:322
msgid "Empty"
msgstr "Празен"

#: admin/partials/wpvivid-settings-page-display.php:424
msgid "Export"
msgstr "Експортиране"

#: admin/partials/wpvivid-settings-page-display.php:425
msgid "Click 'Export' button to save WPvivid settings on your local computer."
msgstr "Кликнете върху бутона \"Експортиране\", за да запазите настройките на WPvivid на вашия локален компютър."

#: admin/partials/wpvivid-settings-page-display.php:429
msgid "Import"
msgstr "Импорт"

#: admin/partials/wpvivid-settings-page-display.php:430
msgid "Importing the json file can help you set WPvivid's configuration on another wordpress site quickly."
msgstr "Импортирането на Json файл може да ви помогне да настроите конфигурацията WPvivid на друг сайт WordPress бързо."

#: admin/partials/wpvivid-settings-page-display.php:550
msgid "Enable the option when backup failed."
msgstr "Разрешаване на опцията при неуспешно архивиране."

#: admin/partials/wpvivid-settings-page-display.php:550
msgid "Special optimization for web hosting/shared hosting"
msgstr "Специална оптимизация за уеб хостинг / споделен хостинг"

#: admin/partials/wpvivid-settings-page-display.php:554
msgid "Enable optimization mode for web hosting/shared hosting"
msgstr "Активирайте режим за оптимизация за уеб хостинг / споделен хостинг"

#: admin/partials/wpvivid-settings-page-display.php:557
msgid "Enabling this option can improve the backup success rate, but it will take more time for backup."
msgstr "Активирането на тази опция може да подобри скоростта на успешно архивиране, но ще отнеме повече време за архивиране."

#: admin/partials/wpvivid-settings-page-display.php:564
msgid "Database access method."
msgstr "Метод за достъп до базата данни."

#: admin/partials/wpvivid-settings-page-display.php:569
msgid "WPDB option has a better compatibility, but the speed of backup and restore is slower."
msgstr "WPDB опция има по-добра съвместимост, но скоростта на архивиране и възстановяване е по-бавно."

#: admin/partials/wpvivid-settings-page-display.php:575
msgid "It is recommended to choose PDO option if pdo_mysql extension is installed on your server, which lets you backup and restore your site faster."
msgstr "Препоръчително е да изберете опцията за PDO, ако pdo_mysql разширение е инсталирано на вашия сървър, което ви позволява да архивирате и възстановявате сайта си по-бързо."

#: admin/partials/wpvivid-settings-page-display.php:583
msgid "It will cause a lower CPU Usage and is recommended in a web hosting/ shared hosting environment."
msgstr "Това ще доведе до по-ниска употреба на CPU и се препоръчва в уеб хостинг / споделена хостинг среда."

#: admin/partials/wpvivid-settings-page-display.php:583
msgid "Only Archive without compressing"
msgstr "Само архив без компресиране"

#: admin/partials/wpvivid-settings-page-display.php:587
msgid "It will cause a higher CPU Usage and is recommended in a VPS/ dedicated hosting environment."
msgstr "Това ще доведе до по-висока употреба на CPU и се препоръчва в VPS / посветен хостинг среда."

#: admin/partials/wpvivid-settings-page-display.php:587
msgid "Compress and Archive"
msgstr "Компресиране и архивиране"

#: admin/partials/wpvivid-settings-page-display.php:596
msgid "Compress Files Every"
msgstr "Компресиране на файловете Всеки"

#: admin/partials/wpvivid-settings-page-display.php:599
msgid "Some web hosting providers limit large zip files (e.g. 200MB), and therefore splitting your backup into many parts is an ideal way to avoid hitting the limitation if you are running a big website.  Please try to adjust the value if you are encountering backup errors. If you use a value of 0 MB, any backup files won't be split."
msgstr "Някои уеб хостинг доставчици ограничават големи пощенски файлове (например 200MB) и следователно разделянето на архива на много части е идеален начин да избегнете удрянето на ограничението, ако използвате голям уебсайт.  Опитайте да коригирате стойността, ако се натъквате на грешки в архива. Ако използвате стойност от 0 МБ, всички архивни файлове няма да бъдат разделени."

#: admin/partials/wpvivid-settings-page-display.php:601
msgid "Exclude the files which are larger than"
msgstr "Изключване на файлове, които са по-големи от"

#: admin/partials/wpvivid-settings-page-display.php:604
msgid "Using the option will ignore the file larger than the certain size in MB when backing up, '0' (zero) means unlimited."
msgstr "Използването на опцията ще игнорира файла, по-голям от определен размер в МБ, когато архивирате, \"0\" (нула) означава неограничен."

#: admin/partials/wpvivid-settings-page-display.php:606
msgid "PHP script execution timeout for backup"
msgstr "Време за изпълнение на PHP скрипт за архивиране"

#: admin/partials/wpvivid-settings-page-display.php:609
msgid "The time-out is not your server PHP time-out. With the execution time exhausted, our plugin will shut the process of backup down. If the progress of backup encounters a time-out, that means you have a medium or large sized website, please try to scale the value bigger."
msgstr "Времето за изчакване на сървъра не е времето за изчакване на сървъра. С времето за изпълнение изчерпани, нашата приставка ще затвори процеса на архивиране надолу. Ако прогресът на архивирането на данни се сблъска с изчакване, това означава, че имате средно или голям по размер уебсайт, моля, опитайте да мащабите по-голямата стойност."

#: admin/partials/wpvivid-settings-page-display.php:611
msgid "PHP script execution timeout for restore"
msgstr "Времето за изпълнение на PHP скрипт за възстановяване"

#: admin/partials/wpvivid-settings-page-display.php:614
msgid "The time-out is not your server PHP time-out. With the execution time exhausted, our plugin will shut the process of restore down. If the progress of restore encounters a time-out, that means you have a medium or large sized website, please try to scale the value bigger."
msgstr "Времето за изчакване на сървъра не е времето за изчакване на сървъра. С времето за изпълнение изчерпани, нашата приставка ще затвори процеса на възстановяване. Ако прогресът на възстановяването на даден период от време, това означава, че имате средно или голям по размер уебсайт, моля, опитайте да мащаба на стойността по-голяма."

#: admin/partials/wpvivid-settings-page-display.php:616
msgid "PHP Memory Limit for backup"
msgstr "Граница на запаметяване на PHP за архивиране"

#: admin/partials/wpvivid-settings-page-display.php:619
msgid "Adjust this value to apply for a temporary PHP memory limit for WPvivid backup plugin to run a backup. We set this value to 256M by default. Increase the value if you encounter a memory exhausted error. Note: some web hosting providers may not support this."
msgstr "Коригирайте тази стойност, за да приложите ограничение за временна PHP памет за WPvivid backup плъгин за изпълнение на резервно копие. Тази стойност е 256M по подразбиране. Увеличете стойността, ако се натъкнете на памет изчерпана грешка. Забележка: някои уеб хостинг доставчици може да не поддържат това."

#: admin/partials/wpvivid-settings-page-display.php:621
msgid "PHP Memory Limit for restoration"
msgstr "Граница на php паметта за възстановяване"

#: admin/partials/wpvivid-settings-page-display.php:624
msgid "Adjust this value to apply for a temporary PHP memory limit for WPvivid backup plugin in restore process. We set this value to 256M by default. Increase the value if you encounter a memory exhausted error. Note: some web hosting providers may not support this."
msgstr "Коригирайте тази стойност, за да се приложи временно ограничение на PHP паметта за WPvivid резервна добавка в процеса на възстановяване. Тази стойност е 256M по подразбиране. Увеличете стойността, ако се натъкнете на памет изчерпана грешка. Забележка: някои уеб хостинг доставчици може да не поддържат това."

#: admin/partials/wpvivid-settings-page-display.php:626
msgid "Chunk Size"
msgstr "Размер на парчето"

#: admin/partials/wpvivid-settings-page-display.php:629
msgid "e.g.  if you choose a chunk size of 2MB, a 8MB file will use 4 chunks. Decreasing this value will break the ISP's transmission limit, for example:512KB"
msgstr "например.  ако изберете размер на парче 2 МБ, 8MB файл ще използва 4 парчета. Намаляването на тази стойност ще прекъсне границата на пренос на ISP, например: 512 КБ"

#: admin/partials/wpvivid-settings-page-display.php:643
#, php-format
msgid "<strong>Retrying </strong>%s<strong> times when encountering a time-out error</strong>"
msgstr "<strong>Повторен опит на </strong>%s<strong> пъти при възникване на грешка за изчакване</strong>"

#: admin/partials/wpvivid-settings-page-display.php:659
msgid "General Settings"
msgstr "Основни настройки"

#: admin/partials/wpvivid-settings-page-display.php:665
msgid "Advanced Settings"
msgstr "Разширение настройки"

#: includes/class-wpvivid-backup-uploader.php:493
msgid "Tips: Click the button below to scan all uploaded or received backups in directory"
msgstr "Съвети: Кликнете върху бутона по-долу, за да сканирате всички качени или получени архиви в директорията"

#: includes/class-wpvivid-backup-uploader.php:496
msgid "Scan uploaded backup or received backup"
msgstr "Сканиране качено архивиране или получаване на резервно копие"

#: includes/class-wpvivid-backup-uploader.php:543
msgid "Drop files here"
msgstr "Пуснете файловете тук"

#: includes/class-wpvivid-backup-uploader.php:545
msgid "Select Files"
msgstr "Избиране на файл"

#: includes/class-wpvivid-backup.php:1062
msgid "Ready to backup. Progress: 0%, running time: 0second."
msgstr "Готови за подкрепление. Напредък: 0%, време на изпълнение: 0секунда."

#: includes/class-wpvivid-backup.php:1097
#: includes/class-wpvivid-backup.php:1110
#: includes/class-wpvivid-export-import.php:1308
msgid "Progress: "
msgstr "Напредък: "

#: includes/class-wpvivid-backup.php:1097
#: includes/class-wpvivid-backup.php:1110
#: includes/class-wpvivid-export-import.php:1308
msgid "running time: "
msgstr "време на работа: "

#: includes/class-wpvivid-backup.php:1116
#: includes/class-wpvivid-backup.php:1133
#: includes/class-wpvivid-function-realize.php:42
msgid "The backup will be canceled after backing up the current chunk ends."
msgstr "Архивирането ще бъде отменено след архивиране на текущия блок."

#: includes/class-wpvivid-backup.php:2256
#, php-format
msgid "Start backing up %s."
msgstr "Стартиране на архивирането на %s."

#: includes/class-wpvivid-backup.php:2264
#, php-format
msgid "Backing up %s finished."
msgstr "Архивирането на %s е завършено."

#: includes/class-wpvivid-export-import.php:65
#: includes/class-wpvivid-export-import.php:78
msgid "Export & Import"
msgstr "Експортиране - импортиране"

#: includes/class-wpvivid-export-import.php:119
msgid "Export posts or pages with images in bulk."
msgstr "Експортирайте публикации или страници с изображения в насипно състояние."

#: includes/class-wpvivid-export-import.php:121
#: includes/class-wpvivid-export-import.php:1515
msgid "Learn more"
msgstr "Научете още"

#: includes/class-wpvivid-export-import.php:124
msgid "This will contain all of your posts, pages, comments, terms and images (original images, featured images and thumbnails)."
msgstr "Това ще съдържа всички ваши публикации, страници, коментари, термини и изображения (оригинални изображения, представени изображения и миниатюри)."

#: includes/class-wpvivid-export-import.php:125
#: includes/class-wpvivid-export-import.php:1518
msgid "Note:"
msgstr "Бележка:"

#: includes/class-wpvivid-export-import.php:125
msgid "Try to select fewer items when you are facing a shortage of server resources (typically presented as a timeout error)."
msgstr "Опитайте да изберете по-малко елементи, когато сте изправени пред недостиг на сървърни ресурси (обикновено представени като грешка за изчакване)."

#: includes/class-wpvivid-export-import.php:135
#, php-format
msgid "Exported files will be temporarily stored in %s directory"
msgstr "Експортираните файлове ще бъдат временно съхранени в директория %s"

#: includes/class-wpvivid-export-import.php:141
msgid "Choose post type"
msgstr "Изберете тип публикация"

#: includes/class-wpvivid-export-import.php:147
msgid "Post"
msgstr "Публикация"

#: includes/class-wpvivid-export-import.php:150
msgid "Page"
msgstr "Страница"

#: includes/class-wpvivid-export-import.php:153
msgid "More post types coming soon..."
msgstr "Скоро ще има още видове пост..."

#: includes/class-wpvivid-export-import.php:158
#: includes/class-wpvivid-export-import.php:181
msgid "Next Step"
msgstr "Следваща Стъпка"

#: includes/class-wpvivid-export-import.php:623
msgid "Choose what to export"
msgstr "Изберете какво да експортирате"

#: includes/class-wpvivid-export-import.php:631
msgid "Filter Posts/Pages"
msgstr "Филтриране на публикации/страници"

#: includes/class-wpvivid-export-import.php:650
msgid "All Categories"
msgstr "Всички категории"

#: includes/class-wpvivid-export-import.php:660
#, php-format
msgid "Export %s of all categories or a specific category."
msgstr "Експортиране на %s от всички категории или конкретна категория."

#: includes/class-wpvivid-export-import.php:680
msgid "All Authors"
msgstr "Всички автори"

#: includes/class-wpvivid-export-import.php:691
#, php-format
msgid "Export %s of all authors or a specific author."
msgstr "Експортиране на %s от всички автори или конкретен автор."

#: includes/class-wpvivid-export-import.php:703
#: includes/class-wpvivid-export-import.php:724
msgid "&mdash; Select &mdash;"
msgstr "&mdash; Изберете &mdash;"

#: includes/class-wpvivid-export-import.php:712
#, php-format
msgid "Export %s published after this date."
msgstr "Експортиране на %s, публикувано след тази дата."

#: includes/class-wpvivid-export-import.php:733
#, php-format
msgid "Export %s published before this date."
msgstr "Експортиране на %s, публикувано преди тази дата."

#: includes/class-wpvivid-export-import.php:769
msgid "Search"
msgstr "Търсене"

#: includes/class-wpvivid-export-import.php:776
#, php-format
msgid "Search for %s according to the above rules."
msgstr "Търсене на %s съгласно горните правила."

#: includes/class-wpvivid-export-import.php:789
msgid "Comment the export (optional)"
msgstr "Коментиране на експортирането (по избор)"

#: includes/class-wpvivid-export-import.php:794
msgid "Comment the export: "
msgstr "Коментирайте износа: "

#: includes/class-wpvivid-export-import.php:798
msgid "Only letters (except for wpvivid) and numbers are allowed."
msgstr "Допускат се само букви (с изключение на wpvivid) и цифри."

#: includes/class-wpvivid-export-import.php:802
msgid "Sample:"
msgstr "Пример:"

#: includes/class-wpvivid-export-import.php:811
msgid "Export and Download"
msgstr "Експортиране и изтегляне"

#: includes/class-wpvivid-export-import.php:1116
msgid "Empty post id"
msgstr "Празен ИД на публикация"

#: includes/class-wpvivid-export-import.php:1123
#: includes/class-wpvivid-export-import.php:1159
msgid "A task is already running. Please wait until the running task is complete, and try again."
msgstr "Вече се изпълнява задача. Изчакайте, докато изпълняваната задача завърши и опитайте отново."

#: includes/class-wpvivid-export-import.php:1149
msgid "Error occurred while parsing the request data. Please try to run export task again."
msgstr "Възникна грешка при анализиране на данните за заявката. Опитайте да изпълните задачата за експортиране отново."

#: includes/class-wpvivid-export-import.php:1304
msgid "Ready to export. Progress: 0%, running time: 0second."
msgstr "Готови за експортиране. Напредък: 0%, време на изпълнение: 0секунда."

#: includes/class-wpvivid-export-import.php:1316
msgid "The export task is not responding."
msgstr "Задачата за експортиране не отговаря."

#: includes/class-wpvivid-export-import.php:1341
msgid "Export error:"
msgstr "Грешка при експортиране:"

#: includes/class-wpvivid-export-import.php:1375
msgid "Task time out."
msgstr "Време за изчакване на задачата."

#: includes/class-wpvivid-export-import.php:1482
msgid "File size not match. please retry again."
msgstr "Размерът на файла не съвпада. опитайте отново."

#: includes/class-wpvivid-export-import.php:1488
msgid "File not found. please retry again."
msgstr "Файлът не е намерен. опитайте отново."

#: includes/class-wpvivid-export-import.php:1513
msgid "Import posts or pages with images in bulk."
msgstr "Импортирайте публикации или страници с изображения в насипно състояние."

#: includes/class-wpvivid-export-import.php:1519
msgid "To properly display the imported content, please make sure that the importing and exporting sites have the same environment, for example, same theme or pages built with the same page builder."
msgstr "За да покажете правилно импортираното съдържание, уверете се, че обектите за импортиране и експортиране имат една и съща среда, например същата тема или страници, създадени със същия конструктор на страници."

#: includes/class-wpvivid-export-import.php:1528
#, php-format
msgid "Imported files will be temporarily stored in directory %s"
msgstr "Импортираните файлове ще бъдат временно съхранени в директория %s"

#: includes/class-wpvivid-export-import.php:1529
msgid "Delete Exported Files In Folder"
msgstr "Изтриване на експортираните файлове в папката"

#: includes/class-wpvivid-export-import.php:1534
msgid "Choose an export from your computer to import: "
msgstr "Изберете експортиране от компютъра за импортиране: "

#: includes/class-wpvivid-export-import.php:1535
msgid "Upload and Import"
msgstr "Качване и импортиране"

#: includes/class-wpvivid-export-import.php:1538
#, php-format
msgid "Or you can use ftp to upload the export to the directory %s. Then click the button below to scan the file to import."
msgstr "Или можете да използвате FTP, за да качите експортирането в директорията %s. След това кликнете върху бутона по-долу, за да сканирате файла за импортиране."

#: includes/class-wpvivid-export-import.php:1539
msgid "Scan Uploaded Exports"
msgstr "Сканиране на качените експорти"

#: includes/class-wpvivid-export-import.php:1543
msgid "The importing file info"
msgstr "Информация за файла за импортиране"

#: includes/class-wpvivid-export-import.php:1546
msgid "Assign author"
msgstr "Присвояване на автор"

#: includes/class-wpvivid-export-import.php:1548
msgid "Select an existing author:"
msgstr "Изберете съществуващ автор:"

#: includes/class-wpvivid-export-import.php:1549
msgid "- Select -"
msgstr "- Избери -"

#: includes/class-wpvivid-export-import.php:1551
msgid "Import Setting"
msgstr "Настройки за импортиране"

#: includes/class-wpvivid-export-import.php:1555
msgid "Overwrite existing pages"
msgstr "Презапис на съществуващи страници"

#: includes/class-wpvivid-export-import.php:1559
msgid "With this option checked, Pages/posts already existing will be overwritten with the updated ones in an import."
msgstr "При тази отметка, страници/публикации, които вече са съществуващи, ще бъдат презаписани с актуализираните в импортираните."

#: includes/class-wpvivid-export-import.php:1561
msgid "Start to Import"
msgstr "Начало на импортиране"

#: includes/class-wpvivid-export-import.php:1562
msgid "Back to Import Page"
msgstr "Обратно към страницата за импортиране"

#: includes/class-wpvivid-exporter.php:71
#: includes/class-wpvivid-importer.php:50
msgid "Select All"
msgstr "Избор на всичко"

#: includes/class-wpvivid-exporter.php:118
msgid "Author"
msgstr "Автор"

#: includes/class-wpvivid-exporter.php:150
msgid "Comments"
msgstr "Коментари"

#: includes/class-wpvivid-exporter.php:265
msgid "Unpublished"
msgstr "Непубликуван"

#: includes/class-wpvivid-exporter.php:275
#, php-format
msgid "%s ago"
msgstr "преди %s"

#: includes/class-wpvivid-exporter.php:282
msgid "Published"
msgstr "Публикувана"

#: includes/class-wpvivid-exporter.php:285
msgid "Missed schedule"
msgstr "Пропуснат график"

#: includes/class-wpvivid-exporter.php:287
msgid "Scheduled"
msgstr "Планирана"

#: includes/class-wpvivid-exporter.php:290
msgid "Last Modified"
msgstr "Последна промяна"

#: includes/class-wpvivid-exporter.php:548
#: includes/class-wpvivid-importer.php:304
msgid "First page"
msgstr "Първа страница"

#: includes/class-wpvivid-exporter.php:559
#: includes/class-wpvivid-importer.php:315
msgid "Previous page"
msgstr "Предишна страница"

#: includes/class-wpvivid-exporter.php:566
#: includes/class-wpvivid-exporter.php:570
#: includes/class-wpvivid-importer.php:322
#: includes/class-wpvivid-importer.php:326
msgid "Current Page"
msgstr "Текуща Страница"

#: includes/class-wpvivid-exporter.php:584
#: includes/class-wpvivid-importer.php:340
msgid "Next page"
msgstr "Следваща страница"

#: includes/class-wpvivid-exporter.php:594
#: includes/class-wpvivid-importer.php:350
msgid "Last page"
msgstr "Последна страница"

#: includes/class-wpvivid-function-realize.php:65
#: includes/class-wpvivid-function-realize.php:93
msgid "Retrieving the backup information failed while showing log. Please try again later."
msgstr "Извличането на архивната информация е неуспешно при показване на регистрационния файл. Моля, опитайте отново по-късно."

#: includes/class-wpvivid-function-realize.php:70
#: includes/class-wpvivid-function-realize.php:82
#: includes/class-wpvivid-function-realize.php:99
msgid "The log not found."
msgstr "Регистрационният файл не е намерен."

#: includes/class-wpvivid-importer.php:86
msgid "File Name"
msgstr "Име на файл"

#: includes/class-wpvivid-importer.php:87
msgid "Post Types"
msgstr "Видове публикации"

#: includes/class-wpvivid-importer.php:88
msgid "Count"
msgstr "Брой"

#: includes/class-wpvivid-importer.php:89
msgid "Media Files Size"
msgstr "Размер на медийните файлове"

#: includes/class-wpvivid-importer.php:167
msgid "Type: "
msgstr "Тип: "

#: includes/class-wpvivid-importer.php:1669
msgid "File not exist, file:"
msgstr "Файлът не съществува, файл:"

#: includes/class-wpvivid-importer.php:1676
#: includes/class-wpvivid-importer.php:1777
msgid "Sorry, this file type is not permitted for security reasons."
msgstr "За съжаление, този тип файлове не е разрешен от съображения за сигурност."

#: includes/class-wpvivid-importer.php:1724
msgid "Fetching attachments is not enabled"
msgstr "Извличането на прикачени файлове не е активирано"

#: includes/class-wpvivid-importer.php:1737
msgid "Invalid file type"
msgstr "Невалиден тип файл"

#: includes/class-wpvivid-importer.php:1782
msgid "File not exist file:"
msgstr "Файлът не съществува:"

#: includes/class-wpvivid-importer.php:1877
#: includes/class-wpvivid-importer.php:1913
#: includes/class-wpvivid-importer.php:1921
msgid "There was an error when reading this WXR file"
msgstr "При четене на този WXR файл възникна грешка"

#: includes/class-wpvivid-importer.php:1878
msgid "Details are shown above. The importer will now try again with a different parser..."
msgstr "Подробностите са показани по-горе. Ще опитаме отново с друг анализатор ..."

#: includes/class-wpvivid-importer.php:1925
#: includes/class-wpvivid-importer.php:1930
#: includes/class-wpvivid-importer.php:2155
#: includes/class-wpvivid-importer.php:2351
msgid "This does not appear to be a WXR file, missing/invalid WXR version number"
msgstr "Това изглежда не е WXR файл, липсващ или невалиден номер на версията на WXR"

#: includes/class-wpvivid-interface-mainwp.php:138
msgid "Error occurred while parsing the request data. Please try to run backup again."
msgstr "Възникна грешка при анализиране на данните за заявката. Опитайте да изпълните отново архивиране."

#: includes/class-wpvivid-interface-mainwp.php:182
#: includes/class-wpvivid-interface-mainwp.php:220
#: includes/class-wpvivid-interface-mainwp.php:273
msgid "Unable to open the log file."
msgstr "Регистрационният файл не може да се отвори."

#: includes/class-wpvivid-interface-mainwp.php:211
msgid "Reading the log failed. Please try again."
msgstr "Четенето на регистрационния файл е неуспешно. Моля, опитайте отново."

#: includes/class-wpvivid-mail-report.php:558
msgid "Unable to send email. Please check the configuration of email server."
msgstr "Не може да се изпрати имейл. Моля, проверете конфигурацията на имейл сървъра."

#: includes/class-wpvivid-migrate.php:49
msgid "Auto-Migration"
msgstr "Автоматично мигриране"

#: includes/class-wpvivid-migrate.php:55
msgid "Key"
msgstr "Ключ"

#: includes/class-wpvivid-migrate.php:390
msgid "In order to allow another site to send a backup to this site, please generate a key below. Once the key is generated, this site is ready to receive a backup from another site. Then, please copy and paste the key in sending site and save it."
msgstr "За да позволите на друг сайт да изпрати резервно копие на този сайт, моля генерирайте ключ по-долу. След като ключът е генериран, този сайт е готов да получи резервно копие от друг сайт. След това копирайте и поставете ключа в изпращането на сайта и го запазете."

#: includes/class-wpvivid-migrate.php:392
msgid "The key will expire in "
msgstr "Ключът ще изтече в "

#: includes/class-wpvivid-migrate.php:399
msgid "Tips: For security reason, please choose an appropriate expiration time for the key."
msgstr "Съвети: Поради причини за сигурност, моля, изберете подходящо време за изтичане на ключа."

#: includes/class-wpvivid-migrate.php:401
msgid "Generate"
msgstr "Генериране"

#: includes/class-wpvivid-migrate.php:850
msgid "Please paste the key below."
msgstr "Моля, поставете ключа по-долу."

#: includes/class-wpvivid-migrate.php:850
msgid "How to get a site key?"
msgstr "Как да получите ключ за сайта?"

#: includes/class-wpvivid-migrate.php:853
msgid "Save"
msgstr "Запази"

#: includes/class-wpvivid-migrate.php:969
msgid "1. Visit Key tab page of WPvivid backup plugin of destination site."
msgstr "1. Посетете ключови раздел на WPvivid резервна плъгин на целевия сайт."

#: includes/class-wpvivid-migrate.php:970
msgid "2. Generate a key by clicking Generate button and copy it."
msgstr "2. Генерирайте ключ, като кликнете върху бутона \"Генерирам\" и го копирайте."

#: includes/class-wpvivid-migrate.php:971
msgid "3. Go back to this page and paste the key in key box below. Lastly, click Save button."
msgstr "3, Върнете се на тази страница и поставете ключа в полето по-долу. Накрая, щракнете върху Бутона Запазване."

#: includes/class-wpvivid-migrate.php:985
msgid "The feature can help you transfer a Wordpress site to a new domain(site). It would be a convenient way to migrate your WP site from dev environment to live server or from old server to the new."
msgstr "Функцията може да ви помогне да прехвърлите wordpress сайт в нов домейн (сайт). Това би било удобен начин да мигрирате вашия WP сайт от dev среда на сървър на живо или от стар сървър към нов."

#: includes/class-wpvivid-migrate.php:995
msgid "Choose the content you want to transfer"
msgstr "Изберете съдържанието, което искате да прехвърлите"

#: includes/class-wpvivid-migrate.php:1010
msgid "Clone then Transfer"
msgstr "Клонинг след това прехвърляне"

#: includes/class-wpvivid-migrate.php:1090
msgid "Note: "
msgstr "Забележка: "

#: includes/class-wpvivid-migrate.php:1091
msgid "1. In order to successfully complete the migration, you'd better deactivate <a href=\"https://wpvivid.com/best-redirect-plugins.html\" target=\"_blank\" style=\"text-decoration: none;\">301 redirect plugin</a>, <a href=\"https://wpvivid.com/8-best-wordpress-firewall-plugins.html\" target=\"_blank\" style=\"text-decoration: none;\">firewall and security plugin</a>, and <a href=\"https://wpvivid.com/best-free-wordpress-caching-plugins.html\" target=\"_blank\" style=\"text-decoration: none;\">caching plugin</a> (if they exist) before transferring website."
msgstr "1. За да завършите успешно миграцията, по-добре деактивирайте <a href=\"https://wpvivid.com/best-redirect-plugins.html\" target=\"_blank\" style=\"text-decoration: none;\">301 приставка за пренасочване,</a> <a href=\"https://wpvivid.com/8-best-wordpress-firewall-plugins.html\" target=\"_blank\" style=\"text-decoration: none;\">защитна стена и приставка за сигурност</a>и <a href=\"https://wpvivid.com/best-free-wordpress-caching-plugins.html\" target=\"_blank\" style=\"text-decoration: none;\">кеширане плъгин</a> (ако те съществуват) преди прехвърлянето на уебсайта."

#: includes/class-wpvivid-migrate.php:1092
msgid "2. Please migrate website with the manual way when using <strong>Local by Flywheel</strong> environment."
msgstr "2. Моля мигрирайте уебсайт с ръчното използване на <strong>local by Flywheel</strong> среда."

#: includes/class-wpvivid-migrate.php:1098
msgid "<strong>Tips: </strong>The unstable connection between sites could cause a failure of files transfer. In this case, uploading backups to destination site is a good alternative to the automatic website migration."
msgstr "<strong>Съвети:</strong> Нестабилната връзка между сайтове може да доведе до отказ на прехвърляне на файлове. В този случай качването на резервни копия на сайта местоназначение е добра алтернатива на автоматичната миграция на уебсайта."

#: includes/class-wpvivid-migrate.php:1099
msgid "How to migrate Wordpress site manually to a new domain(site) with WPvivid backup plugin?"
msgstr "Как да мигрирате WordPress сайт ръчно в нов домейн (сайт) с WPvivid резервна добавка?"

#: includes/class-wpvivid-migrate.php:1100
msgid "1. Download a backup in backups list to your computer."
msgstr "1. Изтеглете резервно копие в списъка с резервни копия на компютъра си."

#: includes/class-wpvivid-migrate.php:1101
msgid "2. Upload the backup to destination site. There are two ways available to use:"
msgstr "2. Качете резервното копие на мястото на местоназначението. Има два начина, които могат да се използват:"

#: includes/class-wpvivid-migrate.php:1102
msgid "2.1 Upload the backup to the upload section of WPvivid backup plugin in destination site."
msgstr "2.1 Качване на резервно копие в секцията за качване на WPvivid резервна плъгин в целевия сайт."

#: includes/class-wpvivid-migrate.php:1103
#, php-format
msgid "2.2 Upload the backup with FTP client to backup directory %s in destination site, then click <strong>Scan uploaded backup or received backup</strong> button."
msgstr "2.2 Качване на резервно копие с FTP клиент в директорията за архивиране %s в целевия сайт, след което щракнете върху <strong>Сканиране качено архивиране или получи бутон за архивиране</strong>."

#: includes/class-wpvivid-migrate.php:1104
msgid "3. Once done, the backup appears in backups list. Then, restore the backup."
msgstr "3. След като направите, архивирането се появява в списъка с резервни копия. След това възстановете архива."

#: includes/class-wpvivid-migrate.php:1124
msgid "Choose what to migrate"
msgstr "Изберете какво да мигрирате"

#: includes/class-wpvivid-migrate.php:1185
msgid "Transfer succeeded. Please scan the backup list on the destination site to display the backup, then restore the backup."
msgstr "Прехвърлянето е успешно. Сканирайте списъка с резервни копия на сайта местоназначение, за да покажете архива, след което възстановете архива."

#: includes/class-wpvivid-migrate.php:1245
msgid "Upload"
msgstr "Качи"

#: includes/class-wpvivid-migrate.php:1255
#, php-format
msgid "The backups will be uploaded to %s directory."
msgstr "Архивните файлове ще бъдат качени в директория %s."

#: includes/class-wpvivid-migrate.php:1258
msgid "Note: The files you want to upload must be a backup created by WPvivid backup plugin. Make sure that uploading every part of a backup to the directory if the backup is split into many parts"
msgstr "Забележка: Файловете, които искате да качите, трябва да бъдат резервно копие, създадено от WPvivid backup плъгин. Уверете се, че качването на всяка част от архива в директорията, ако архивирането е разделено на много части"

#: includes/class-wpvivid-schedule.php:34
msgid "12 Hours"
msgstr "12 часа"

#: includes/class-wpvivid-schedule.php:40
#: includes/class-wpvivid-schedule.php:94
msgid "Daily"
msgstr "Ежедневно"

#: includes/class-wpvivid-schedule.php:46
#: includes/class-wpvivid-schedule.php:97
msgid "Weekly"
msgstr "Седмично"

#: includes/class-wpvivid-schedule.php:52
#: includes/class-wpvivid-schedule.php:100
msgid "Fortnightly"
msgstr "Две седмици"

#: includes/class-wpvivid-schedule.php:58
#: includes/class-wpvivid-schedule.php:103
msgid "Monthly"
msgstr "Месечно"

#: includes/class-wpvivid-schedule.php:91
msgid "12Hours"
msgstr "12Часа"

#: includes/class-wpvivid-schedule.php:282
#: includes/class-wpvivid-schedule.php:296
msgid "Creating scheduled tasks failed. Please try again later."
msgstr "Неуспешно създаване на планирани задачи. Моля, опитайте отново по-късно."

#: includes/class-wpvivid.php:507
#: includes/class-wpvivid.php:515
msgid "A backup type is required."
msgstr "Изисква се тип архивиране."

#: includes/class-wpvivid.php:521
#: includes/class-wpvivid.php:530
msgid "Choose at least one storage location for backups."
msgstr "Изберете поне едно място за съхранение за архивиране."

#: includes/class-wpvivid.php:541
#: includes/class-wpvivid.php:4557
msgid "There is no default remote storage configured. Please set it up first."
msgstr "Няма конфигурирано отдалечено място за съхранение по подразбиране. Моля, първо го настройте."

#: includes/class-wpvivid.php:1408
#: includes/class-wpvivid.php:1430
#: includes/class-wpvivid.php:1456
#: includes/class-wpvivid.php:1581
#: includes/class-wpvivid.php:1701
msgid "Too many resumption attempts."
msgstr "Твърде много опити за възобновяване."

#: includes/class-wpvivid.php:1589
#: includes/class-wpvivid.php:1709
msgid "Task timed out."
msgstr "Времето за изчакване на задачата изтече."

#: includes/class-wpvivid.php:2479
msgid "Retrieving the backup(s) information failed while deleting the selected backup(s). Please try again later."
msgstr "Извличането на информация за архивирането е неуспешно при изтриване на избраните архиви. Моля, опитайте отново по-късно."

#: includes/class-wpvivid.php:2489
msgid "Unable to delete the locked backup. Please unlock it first and try again."
msgstr "Не може да се изтрие заключеното архивиране. Първо го отключите и опитайте отново."

#: includes/class-wpvivid.php:2592
msgid "You have successfully added a remote storage."
msgstr "Успешно сте добавили отдалечено хранилище."

#: includes/class-wpvivid.php:2644
msgid "Fail to delete the remote storage, can not retrieve the storage infomation. Please try again."
msgstr "Не може да изтриете отдалеченото хранилище, не може да извлече информацията за съхранение. Моля, опитайте отново."

#: includes/class-wpvivid.php:2672
msgid "Failed to get the remote storage information. Please try again later."
msgstr "Неуспешно получаване на информация за отдалечено съхранение. Моля, опитайте отново по-късно."

#: includes/class-wpvivid.php:3416
msgid "restore failed error unknown"
msgstr "неизвестна грешка при възстановяване"

#: includes/class-wpvivid.php:3449
msgid "The restore file not found. Please verify the file exists."
msgstr "Файлът за възстановяване не е намерен. Проверете дали файлът съществува."

#: includes/class-wpvivid.php:3561
#: includes/class-wpvivid.php:3577
msgid "The last backup message not found."
msgstr "Последното съобщение за архивиране не е намерено."

#: includes/class-wpvivid.php:3580
msgid "Last Backup: "
msgstr "Последно архивиране: "

#: includes/class-wpvivid.php:3677
#, php-format
msgid "%d backup tasks have been completed. Please switch to <a href=\"#\" onclick=\"wpvivid_click_switch_page('wrap', 'wpvivid_tab_log', true);\">Log</a> page to check the details."
msgstr "%d архивиране са завършени. Преминете към <a href=\"#\" onclick=\"wpvivid_click_switch_page('wrap', 'wpvivid_tab_log', true);\">логаритмичен</a> страницата, за да проверите данните."

#: includes/class-wpvivid.php:3770
msgid "Getting backup directory failed. Please try again later."
msgstr "Неуспешно получаване на директория за архивиране. Моля, опитайте отново по-късно."

#: includes/class-wpvivid.php:3965
msgid "Choose at least one type of junk files for deleting."
msgstr "Изберете поне един тип нежелани файлове за изтриване."

#: includes/class-wpvivid.php:4039
msgid "The selected junk flies have been deleted."
msgstr "Избраните нежелани мухи са били изтрити."

#: includes/class-wpvivid.php:4210
msgid "Choose one storage from the list to be the default storage."
msgstr "Изберете едно място за съхранение от списъка, за да бъде хранилището по подразбиране."

#: includes/class-wpvivid.php:4465
#: includes/class-wpvivid.php:4473
msgid "The maximum zip file size is required."
msgstr "Изисква се максималният размер на zip файла."

#: includes/class-wpvivid.php:4479
#: includes/class-wpvivid.php:4486
msgid "The size for excluded files is required."
msgstr "Размерът на изключените файлове е необходим."

#: includes/class-wpvivid.php:4492
#: includes/class-wpvivid.php:4499
msgid "The maximum execution time for PHP script is required."
msgstr "Изисква се максимално време за изпълнение на PHP скрипт."

#: includes/class-wpvivid.php:4505
#: includes/class-wpvivid.php:4512
msgid "The local storage path is required."
msgstr "Изисква се локален път за съхранение."

#: includes/class-wpvivid.php:4522
msgid "An email address is required."
msgstr "Изисква се имейл адрес."

#: includes/class-wpvivid.php:4531
#: includes/class-wpvivid.php:4535
msgid "The pdo_mysql extension is not detected. Please install the extension first or choose wpdb option for Database connection method."
msgstr "Не е открито разширението на pdo_mysql. Моля, първо инсталирайте разширението или изберете wpdb опция за метод за връзка с база данни."

#: includes/class-wpvivid.php:4654
msgid "The selected file is not the setting file for WPvivid. Please upload the right file."
msgstr "Избраният файл не е файл за настройка за WPvivid. Моля, качете правилния файл."

#: includes/class-wpvivid.php:4676
msgid "Invalid email address"
msgstr "Невалиден email"

#: includes/class-wpvivid.php:5248
msgid "Type:"
msgstr "Вид:"

#: includes/class-wpvivid.php:5402
msgid "Save backups on localhost (web server)"
msgstr "Запазване на архивите на localhost (уеб сървър)"

#: includes/class-wpvivid.php:5406
msgid "Send backups to remote storage (choose this option, the local backup will be deleted after uploading to remote storage completely)"
msgstr "Изпращане на резервни копия на отдалечено съхранение (изберете тази опция, локалния архив ще бъде изтрит след качването на отдалечено съхранение напълно)"

#: includes/class-wpvivid.php:5571
msgid "User's email address is required."
msgstr "Имейл адрес на потребителя е необходим."

#: includes/class-wpvivid.php:5576
msgid "Please enter a valid email address."
msgstr "Моля, въведете валиден имейл адрес."

#: includes/customclass/class-wpvivid-amazons3-plus.php:44
msgid "Amazon S3"
msgstr "1000000000"

#: includes/customclass/class-wpvivid-amazons3-plus.php:62
#: includes/customclass/class-wpvivid-amazons3-plus.php:200
msgid "Enter Your Amazon S3 Account"
msgstr "Въведете своя профил в Amazon S3"

#: includes/customclass/class-wpvivid-amazons3-plus.php:70
#: includes/customclass/class-wpvivid-amazons3-plus.php:208
msgid "Enter a unique alias: e.g. Amazon S3-001"
msgstr "Въведете уникален псевдоним: например Amazon S3-001"

#: includes/customclass/class-wpvivid-amazons3-plus.php:75
#: includes/customclass/class-wpvivid-amazons3-plus.php:213
#: includes/customclass/class-wpvivid-dropbox.php:477
#: includes/customclass/class-wpvivid-dropbox.php:602
#: includes/customclass/class-wpvivid-ftpclass.php:57
#: includes/customclass/class-wpvivid-ftpclass.php:184
#: includes/customclass/class-wpvivid-google-drive.php:319
#: includes/customclass/class-wpvivid-google-drive.php:447
msgid "A name to help you identify the storage if you have multiple remote storage connected."
msgstr "Име, което да ви помогне да идентифицирате хранилището, ако сте свързани няколко отдалечени места за съхранение."

#: includes/customclass/class-wpvivid-amazons3-plus.php:82
#: includes/customclass/class-wpvivid-amazons3-plus.php:220
msgid "Amazon S3 access key"
msgstr "Ключ за достъп на Amazon S3"

#: includes/customclass/class-wpvivid-amazons3-plus.php:87
#: includes/customclass/class-wpvivid-amazons3-plus.php:225
msgid "Enter your Amazon S3 access key."
msgstr "Въведете ключа си за достъп до Amazon S3."

#: includes/customclass/class-wpvivid-amazons3-plus.php:87
#: includes/customclass/class-wpvivid-amazons3-plus.php:225
msgid "How to get an AmazonS3 access key."
msgstr "Как да получите достъп до AmazonS3 ключ."

#: includes/customclass/class-wpvivid-amazons3-plus.php:94
#: includes/customclass/class-wpvivid-amazons3-plus.php:232
msgid "Amazon S3 secret key"
msgstr "Тайният ключ на Amazon S3"

#: includes/customclass/class-wpvivid-amazons3-plus.php:99
#: includes/customclass/class-wpvivid-amazons3-plus.php:237
msgid "Enter your Amazon S3 secret key."
msgstr "Въведете вашия Amazon S3 тайна ключ."

#: includes/customclass/class-wpvivid-amazons3-plus.php:99
#: includes/customclass/class-wpvivid-amazons3-plus.php:237
msgid "How to get an AmazonS3 secret key."
msgstr "Как да получите тайната на AmazonS3 ключ."

#: includes/customclass/class-wpvivid-amazons3-plus.php:106
#: includes/customclass/class-wpvivid-amazons3-plus.php:244
msgid "Amazon S3 Bucket Name(e.g. test)"
msgstr "Име на кофата на Амазонка S3 (напр. тест)"

#: includes/customclass/class-wpvivid-amazons3-plus.php:111
#: includes/customclass/class-wpvivid-amazons3-plus.php:249
msgid "Enter an existed Bucket to create a custom backup storage directory."
msgstr "Въведете съществувало хранилище, за да създадете директория за съхранение по избор."

#: includes/customclass/class-wpvivid-amazons3-plus.php:118
#: includes/customclass/class-wpvivid-amazons3-plus.php:256
msgid "Custom Path"
msgstr "Път по избор"

#: includes/customclass/class-wpvivid-amazons3-plus.php:123
#: includes/customclass/class-wpvivid-amazons3-plus.php:261
msgid "Customize the directory where you want to store backups within the Bucket."
msgstr "Персонализирайте директорията, в която искате да съхранявате архиви в \"Кофа\"."

#: includes/customclass/class-wpvivid-amazons3-plus.php:131
#: includes/customclass/class-wpvivid-dropbox.php:509
#: includes/customclass/class-wpvivid-ftpclass.php:125
#: includes/customclass/class-wpvivid-google-drive.php:351
msgid "Set as the default remote storage."
msgstr "Задайте като отдалечено хранилище по подразбиране."

#: includes/customclass/class-wpvivid-amazons3-plus.php:137
#: includes/customclass/class-wpvivid-dropbox.php:515
#: includes/customclass/class-wpvivid-ftpclass.php:131
#: includes/customclass/class-wpvivid-google-drive.php:357
msgid "Once checked, all this sites backups sent to a remote storage destination will be uploaded to this storage by default."
msgstr "След като е маркирано, всички резервни копия на тези сайтове, изпратени до местоназначение за отдалечено съхранение, ще бъдат качени по подразбиране на това място за съхранение."

#: includes/customclass/class-wpvivid-amazons3-plus.php:145
#: includes/customclass/class-wpvivid-amazons3-plus.php:269
msgid "Storage class: Standard (infrequent access)."
msgstr "Клас на съхранение: Стандартен (нечестен достъп)."

#: includes/customclass/class-wpvivid-amazons3-plus.php:151
#: includes/customclass/class-wpvivid-amazons3-plus.php:275
msgid "Check the option to use Amazon S3 Standard-Infrequent Access (S3 Standard-IA) storage class for data transfer."
msgstr "Проверете опцията за използване на amazon S3 стандартен-рядък достъп (S3 Standard-IA) клас за съхранение за прехвърляне на данни."

#: includes/customclass/class-wpvivid-amazons3-plus.php:159
#: includes/customclass/class-wpvivid-amazons3-plus.php:283
msgid "Server-side encryption."
msgstr "Шифроване от страна на сървъра."

#: includes/customclass/class-wpvivid-amazons3-plus.php:165
#: includes/customclass/class-wpvivid-amazons3-plus.php:289
msgid "Check the option to use Amazon S3 server-side encryption to protect data."
msgstr "Проверете опцията за използване на шифроване от страна на сървъра на Amazon S3, за да защитите данните."

#: includes/customclass/class-wpvivid-amazons3-plus.php:173
#: includes/customclass/class-wpvivid-ftpclass.php:153
msgid "Test and Add"
msgstr "Тест и добавяне"

#: includes/customclass/class-wpvivid-amazons3-plus.php:178
msgid "Click the button to connect to Amazon S3 storage and add it to the storage list below."
msgstr "Щракнете върху бутона, за да се свържете с хранилището на Amazon S3 и го добавете към списъка за съхранение по-долу."

#: includes/customclass/class-wpvivid-amazons3-plus.php:187
msgid "The simplexml extension is not detected. Please install the extension first."
msgstr "Разширението на simplexml не се открива. Първо инсталирайте разширението."

#: includes/customclass/class-wpvivid-amazons3-plus.php:302
#: includes/customclass/class-wpvivid-dropbox.php:614
#: includes/customclass/class-wpvivid-ftpclass.php:259
#: includes/customclass/class-wpvivid-google-drive.php:459
msgid "Click the button to save the changes."
msgstr "Щракнете върху бутона, за да запишете промените."

#: includes/customclass/class-wpvivid-dropbox.php:433
msgid "You have authenticated the Dropbox account as your remote storage."
msgstr "Вие сте удостоверени Dropbox акаунт като вашето отдалечено място за съхранение."

#: includes/customclass/class-wpvivid-dropbox.php:441
#: includes/customclass/class-wpvivid-google-drive.php:280
msgid "You have successfully updated the storage alias."
msgstr "Успешно сте актуализирали псевдонима на хранилището."

#: includes/customclass/class-wpvivid-dropbox.php:452
msgid "Dropbox"
msgstr "Dropbox"

#: includes/customclass/class-wpvivid-dropbox.php:462
msgid "Please read <a target=\"_blank\" href=\"https://wpvivid.com/privacy-policy\" style=\"text-decoration: none;\">this privacy policy</a> for use of our Dropbox authorization app (none of your backup data is sent to us)."
msgstr "Моля, прочетете <a target=\"_blank\" href=\"https://wpvivid.com/privacy-policy\" style=\"text-decoration: none;\">тази декларация</a> за поверителност за използване на нашето Приложение за разрешение Dropbox (нито един от вашите архивни данни не се изпраща до нас)."

#: includes/customclass/class-wpvivid-dropbox.php:465
#: includes/customclass/class-wpvivid-dropbox.php:590
msgid "Enter Your Dropbox Information"
msgstr "Въведете информацията за вашата Dropbox"

#: includes/customclass/class-wpvivid-dropbox.php:472
#: includes/customclass/class-wpvivid-dropbox.php:597
msgid "Enter a unique alias: e.g. Dropbox-001"
msgstr "Въведете уникален псевдоним: например Dropbox-001"

#: includes/customclass/class-wpvivid-dropbox.php:489
#: includes/customclass/class-wpvivid-google-drive.php:331
msgid "All backups will be uploaded to this directory."
msgstr "Всички архиви ще бъдат качени в тази директория."

#: includes/customclass/class-wpvivid-dropbox.php:501
#: includes/customclass/class-wpvivid-google-drive.php:343
msgid "Pro feature: Create a directory for storing the backups of the site"
msgstr "Pro функция: Създаване на директория за съхраняване на архивите на сайта"

#: includes/customclass/class-wpvivid-dropbox.php:522
msgid "Authenticate with Dropbox"
msgstr "Удостоверяване с Dropbox"

#: includes/customclass/class-wpvivid-dropbox.php:527
msgid "Click the button to get Dropbox authentication and add it to the storage list below."
msgstr "Щракнете върху бутона, за да получите удостоверяване Dropbox и да го добавите към списъка за съхранение по-долу."

#: includes/customclass/class-wpvivid-ftpclass.php:36
msgid "FTP"
msgstr "FTP"

#: includes/customclass/class-wpvivid-ftpclass.php:45
#: includes/customclass/class-wpvivid-ftpclass.php:172
msgid "Enter Your FTP Account"
msgstr "Въведете вашия FTP акаунт"

#: includes/customclass/class-wpvivid-ftpclass.php:52
#: includes/customclass/class-wpvivid-ftpclass.php:179
msgid "Enter an unique alias: e.g. FTP-001"
msgstr "Въведете уникален псевдоним: например FTP-001"

#: includes/customclass/class-wpvivid-ftpclass.php:64
#: includes/customclass/class-wpvivid-ftpclass.php:191
msgid "FTP server (server's port 21)"
msgstr "FTP сървър (порт на сървъра 21)"

#: includes/customclass/class-wpvivid-ftpclass.php:69
#: includes/customclass/class-wpvivid-ftpclass.php:196
msgid "Enter the FTP server."
msgstr "Въведете FTP сървъра."

#: includes/customclass/class-wpvivid-ftpclass.php:81
msgid "Pro feature: Change the FTP default port number"
msgstr "Pro функция: Промяна на FTP номера на порт по подразбиране"

#: includes/customclass/class-wpvivid-ftpclass.php:88
#: includes/customclass/class-wpvivid-ftpclass.php:203
msgid "FTP login"
msgstr "FTP вход"

#: includes/customclass/class-wpvivid-ftpclass.php:93
#: includes/customclass/class-wpvivid-ftpclass.php:208
msgid "Enter your FTP server user name."
msgstr "Въведете потребителското име на FTP сървъра."

#: includes/customclass/class-wpvivid-ftpclass.php:100
#: includes/customclass/class-wpvivid-ftpclass.php:215
msgid "FTP password"
msgstr "FTP парола"

#: includes/customclass/class-wpvivid-ftpclass.php:105
#: includes/customclass/class-wpvivid-ftpclass.php:220
msgid "Enter the FTP server password."
msgstr "Въведете паролата на FTP сървъра."

#: includes/customclass/class-wpvivid-ftpclass.php:112
#: includes/customclass/class-wpvivid-ftpclass.php:227
msgid "Absolute path must exist(e.g. /home/username)"
msgstr "Абсолютен път трябва да съществува (напр. /начало/потребителско име)"

#: includes/customclass/class-wpvivid-ftpclass.php:117
#: includes/customclass/class-wpvivid-ftpclass.php:232
msgid "Enter an absolute path and a custom subdirectory (optional) for holding the backups of current website. For example, /home/username/customfolder"
msgstr "Въведете абсолютен път и персонализирана поддиректорията (по избор) за задържане на резервните копия на текущия сайт. Например ,,/дом/потребителско име/персонализирана папка"

#: includes/customclass/class-wpvivid-ftpclass.php:139
#: includes/customclass/class-wpvivid-ftpclass.php:240
msgid "Uncheck this to enable FTP active mode."
msgstr "Премахнете отметката от това, за да активирате активен режим на FTP."

#: includes/customclass/class-wpvivid-ftpclass.php:145
#: includes/customclass/class-wpvivid-ftpclass.php:246
msgid "Uncheck the option to use FTP active mode when transferring files. Make sure the FTP server you are configuring supports the active FTP mode."
msgstr "Премахнете отметката от опцията за използване на FTP активен режим при прехвърляне на файлове. Уверете се, че ftp сървърът, който конфигурирате, поддържа активния FTP режим."

#: includes/customclass/class-wpvivid-ftpclass.php:158
msgid "Click the button to connect to FTP server and add it to the storage list below."
msgstr "Щракнете върху бутона, за да се свържете с FTP сървър и го добавете към списъка за съхранение по-долу."

#: includes/customclass/class-wpvivid-google-drive.php:111
#: includes/customclass/class-wpvivid-google-drive.php:207
msgid "Authentication failed, the client_secrets.json file is missing. Please make sure the client_secrets.json file is in wpvivid-backuprestore\\includes\\customclass directory."
msgstr "Неуспешно удостоверяване, липсва файлът client_secrets.json. Моля, уверете се, че файлът client_secrets.json е в wpvivid-backuprestore\\включва\\customclass директория."

#: includes/customclass/class-wpvivid-google-drive.php:115
#: includes/customclass/class-wpvivid-google-drive.php:211
msgid "Authentication failed, the format of the client_secrets.json file is incorrect. Please delete and re-install the plugin to recreate the file."
msgstr "Неуспешно удостоверяване, форматът на файла client_secrets.json е неправилен. Изтрийте и инсталирайте отново приставката, за да създадете файла."

#: includes/customclass/class-wpvivid-google-drive.php:272
msgid "You have authenticated the WPvividGoogle Drive account as your remote storage."
msgstr "Вие сте удостоверили профила в WPvividGoogle Диск като вашето отдалечено хранилище."

#: includes/customclass/class-wpvivid-google-drive.php:292
msgid "WPvividGoogle Drive"
msgstr "WPvividGoogle Диск"

#: includes/customclass/class-wpvivid-google-drive.php:304
msgid "Please read <a target=\"_blank\" href=\"https://wpvivid.com/privacy-policy\" style=\"text-decoration: none;\">this privacy policy</a> for use of our WPvividGoogle Drive authorization app (none of your backup data is sent to us)."
msgstr "Моля, прочетете <a target=\"_blank\" href=\"https://wpvivid.com/privacy-policy\" style=\"text-decoration: none;\">тази декларация</a> за поверителност за използване на приложението ни за упълномощаване WPvividGoogle Диск (нито един от вашите архивни данни не се изпраща до нас)."

#: includes/customclass/class-wpvivid-google-drive.php:307
#: includes/customclass/class-wpvivid-google-drive.php:435
msgid "Enter Your WPvividGoogle Drive Information"
msgstr "Въведете информацията за google Диск"

#: includes/customclass/class-wpvivid-google-drive.php:314
#: includes/customclass/class-wpvivid-google-drive.php:442
msgid "Enter a unique alias: e.g. WPvividGoogle Drive-001"
msgstr "Въведете уникален псевдоним: например WPvividGoogle Диск-001"

#: includes/customclass/class-wpvivid-google-drive.php:364
msgid "Authenticate with WPvividGoogle Drive"
msgstr "Удостоверяване с WPvividGoogle Диск"

#: includes/customclass/class-wpvivid-google-drive.php:369
msgid "Click the button to get WPvividGoogle authentication and add it to the storage list below."
msgstr "Кликнете върху бутона, за да получите удостоверяване от WPvividGoogle и да го добавите към списъка за съхранение по-долу."

#: includes/customclass/class-wpvivid-one-drive.php:240
msgid "You have authenticated the Microsoft OneDrive account as your remote storage."
msgstr "Вие сте удостоверили акаунта на Microsoft OneDrive като вашето отдалечено място за съхранение."

#: includes/customclass/class-wpvivid-one-drive.php:260
msgid "Microsoft OneDrive"
msgstr "Преглед на оригиналната статия на английски: 301"

#: includes/customclass/class-wpvivid-one-drive.php:272
msgid "Please read <a target=\"_blank\" href=\"https://wpvivid.com/privacy-policy\" style=\"text-decoration: none;\">this privacy policy</a> for use of our Microsoft OneDrive authorization app (none of your backup data is sent to us)."
msgstr "Моля, прочетете <a target=\"_blank\" href=\"https://wpvivid.com/privacy-policy\" style=\"text-decoration: none;\">тази декларация</a> за поверителност за използване на нашето приложение за оторизиране на Microsoft OneDrive (нито една от вашите данни не се изпраща до нас)."

#: includes/customclass/class-wpvivid-one-drive.php:275
#: includes/customclass/class-wpvivid-one-drive.php:403
msgid "Enter Your Microsoft OneDrive Information"
msgstr "Въведете информацията за Вашия Microsoft OneDrive"

#: includes/customclass/class-wpvivid-one-drive.php:282
#: includes/customclass/class-wpvivid-one-drive.php:410
msgid "Enter a unique alias: e.g. OneDrive-001"
msgstr "Въведете уникален псевдоним: например OneDrive-001"

#: includes/customclass/class-wpvivid-one-drive.php:332
msgid "Authenticate with Microsoft OneDrive"
msgstr "Удостоверяване с Microsoft OneDrive"

#: includes/customclass/class-wpvivid-one-drive.php:337
msgid "Click the button to get Microsoft authentication and add it to the storage list below."
msgstr "Щракнете върху бутона, за да получите Microsoft удостоверяване и да го добавите към списъка за съхранение по-долу."

#: includes/customclass/class-wpvivid-s3compat.php:403
msgid "DigitalOcean Spaces"
msgstr "Дигитални пространства"

#: includes/customclass/class-wpvivid-s3compat.php:412
#: includes/customclass/class-wpvivid-s3compat.php:527
msgid "Enter Your DigitalOcean Spaces Account"
msgstr "Въведете вашия цифров акаунт за пространства"

#: includes/customclass/class-wpvivid-s3compat.php:420
#: includes/customclass/class-wpvivid-s3compat.php:535
msgid "Enter a unique alias: e.g. DOS-001"
msgstr "Въведете уникален псевдоним: например DOS-001"

#: includes/customclass/class-wpvivid-s3compat.php:432
#: includes/customclass/class-wpvivid-s3compat.php:547
msgid "DigitalOcean Spaces access key"
msgstr "Ключ за достъп до цифрови пространства"

#: includes/customclass/class-wpvivid-s3compat.php:437
#: includes/customclass/class-wpvivid-s3compat.php:552
msgid "Enter your DigitalOcean Spaces access key"
msgstr "Въведете ключа за достъп до цифровите пространства"

#: includes/customclass/class-wpvivid-s3compat.php:444
#: includes/customclass/class-wpvivid-s3compat.php:559
msgid "DigitalOcean Spaces secret key"
msgstr "Цифровококийски пространства таен ключ"

#: includes/customclass/class-wpvivid-s3compat.php:449
#: includes/customclass/class-wpvivid-s3compat.php:564
msgid "Enter your DigitalOcean Spaces secret key"
msgstr "Въведете таен ключ за цифровите пространства"

#: includes/customclass/class-wpvivid-s3compat.php:456
#: includes/customclass/class-wpvivid-s3compat.php:571
msgid "Space Name(e.g. test)"
msgstr "Име на интервала (напр. тест)"

#: includes/customclass/class-wpvivid-s3compat.php:461
#: includes/customclass/class-wpvivid-s3compat.php:576
msgid "Enter an existed Space to create a custom backup storage directory."
msgstr "Въведете съществувало пространство, за да създадете директория за съхранение по избор."

#: includes/customclass/class-wpvivid-s3compat.php:473
#: includes/customclass/class-wpvivid-s3compat.php:588
msgid "Customize the directory where you want to store backups within the Space."
msgstr "Персонализирайте директорията, където искате да съхранявате архиви в пространството."

#: includes/customclass/class-wpvivid-s3compat.php:480
#: includes/customclass/class-wpvivid-s3compat.php:595
msgid "region.digitaloceanspaces.com"
msgstr "region.digitaloceanspaces.com"

#: includes/customclass/class-wpvivid-s3compat.php:485
#: includes/customclass/class-wpvivid-s3compat.php:600
msgid "Enter the DigitalOcean Endpoint for the storage"
msgstr "Въведете крайна точка digitalOcean за съхранение"

#: includes/customclass/class-wpvivid-s3compat.php:512
msgid "Click the button to connect to DigitalOcean Spaces storage and add it to the storage list below."
msgstr "Щракнете върху бутона, за да се свържете с DigitalOcean Spaces за съхранение и да го добавите към списъка за съхранение по-долу."

#: includes/customclass/class-wpvivid-sftpclass.php:38
msgid "SFTP"
msgstr "Sftp"

#: includes/customclass/class-wpvivid-sftpclass.php:48
#: includes/customclass/class-wpvivid-sftpclass.php:163
msgid "Enter Your SFTP Account"
msgstr "Въведете своя SFTP акаунт"

#: includes/customclass/class-wpvivid-sftpclass.php:56
#: includes/customclass/class-wpvivid-sftpclass.php:171
msgid "Enter a unique alias: e.g. SFTP-001"
msgstr "Въведете уникален псевдоним: например SFTP-001"

#: includes/customclass/class-wpvivid-sftpclass.php:68
#: includes/customclass/class-wpvivid-sftpclass.php:183
msgid "Server Address"
msgstr "Адрес на сървъра"

#: includes/customclass/class-wpvivid-sftpclass.php:73
#: includes/customclass/class-wpvivid-sftpclass.php:188
msgid "Enter the server address."
msgstr "Въведете адреса на сървъра."

#: includes/customclass/class-wpvivid-sftpclass.php:80
#: includes/customclass/class-wpvivid-sftpclass.php:195
msgid "User Name"
msgstr "Потребителско име"

#: includes/customclass/class-wpvivid-sftpclass.php:85
#: includes/customclass/class-wpvivid-sftpclass.php:200
msgid "Enter the user name."
msgstr "Въведете потребителското име."

#: includes/customclass/class-wpvivid-sftpclass.php:92
#: includes/customclass/class-wpvivid-sftpclass.php:207
msgid "User Password"
msgstr "Потребителска парола"

#: includes/customclass/class-wpvivid-sftpclass.php:97
#: includes/customclass/class-wpvivid-sftpclass.php:212
msgid "Enter the user password."
msgstr "Въведете потребителската парола."

#: includes/customclass/class-wpvivid-sftpclass.php:104
#: includes/customclass/class-wpvivid-sftpclass.php:219
msgid "Port"
msgstr "Порт"

#: includes/customclass/class-wpvivid-sftpclass.php:109
#: includes/customclass/class-wpvivid-sftpclass.php:224
msgid "Enter the server port."
msgstr "Въведете сървъра порт."

#: includes/customclass/class-wpvivid-sftpclass.php:116
#: includes/customclass/class-wpvivid-sftpclass.php:231
msgid "Absolute path must exist(e.g. /var)"
msgstr "Абсолютен път трябва да съществува (напр. /var)"

#: includes/customclass/class-wpvivid-sftpclass.php:121
#: includes/customclass/class-wpvivid-sftpclass.php:236
msgid "Enter an absolute path and a custom subdirectory (optional) for holding the backups of current website. For example, /var/customfolder/"
msgstr "Въведете абсолютен път и персонализирана поддиректорията (по избор) за задържане на резервните копия на текущия сайт. Например /var/ customfolder/"

#: includes/customclass/class-wpvivid-sftpclass.php:148
msgid "Click the button to connect to SFTP server and add it to the storage list below."
msgstr "Щракнете върху бутона, за да се свържете със SFTP сървър и да го добавите към списъка за съхранение по-долу."

languages/wpvivid-backuprestore-it_IT.po000064400000300352151327705670014437 0ustar00msgid ""
msgstr ""
"Project-Id-Version: wpvivid backup plugin\n"
"Last-Translator: \n"
"Language-Team: Italian\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Poedit-KeywordsList: __;_e;esc_attr;esc_attr_e\n"
"X-Poedit-Basepath: ..\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
"X-Poedit-SourceCharset: utf-8\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2020-05-12 15:27+0000\n"
"PO-Revision-Date: 2020-07-27 18:40+0800\n"
"Language: it_IT\n"
"X-Generator: Loco https://localise.biz/\n"
"X-Loco-Version: 2.3.3; wp-5.4.1\n"
"X-Poedit-SearchPath-0: .\n"

#: admin/class-wpvivid-admin.php:100
#: admin/class-wpvivid-admin.php:114
#: admin/class-wpvivid-admin.php:635
msgid "Backup & Restore"
msgstr "Backup & Ripristino"

#: admin/class-wpvivid-admin.php:123
#: admin/class-wpvivid-admin.php:313
#: admin/class-wpvivid-admin.php:653
msgid "Settings"
msgstr "Impostazioni"

#: admin/class-wpvivid-admin.php:139
msgid "Current Version:"
msgstr "Versione Corrente:"

#: admin/class-wpvivid-admin.php:142
msgid "ChangeLog"
msgstr "ChangeLog"

#: admin/class-wpvivid-admin.php:149
msgid "Troubleshooting"
msgstr "Risoluzione dei problemi"

#: admin/class-wpvivid-admin.php:154
msgid "Read <a href=\"https://wpvivid.com/troubleshooting-issues-wpvivid-backup-plugin\" target=\"_blank\">Troubleshooting page</a> for faster solutions."
msgstr "Leggi <a href=\"https://wpvivid.com/troubleshooting-issues-wpvivid-backup-plugin\" target=\"_blank\">la pagina di Risoluzione dei problemi</a> per soluzioni più rapide."

#: admin/class-wpvivid-admin.php:157
msgid "Adjust <a href=\"https://wpvivid.com/wpvivid-backup-plugin-advanced-settings.html\" target=\"_blank\">Advanced Settings</a> for higher task success rate."
msgstr "Configura le <a href=\"https://wpvivid.com/wpvivid-backup-plugin-advanced-settings.html\" target=\"_blank\">Impostazioni avanzate</a> per una percentuale più alta di successo nello svolgimento delle attività."

#: admin/class-wpvivid-admin.php:164
msgid "How-to"
msgstr "Come fare"

#: admin/class-wpvivid-admin.php:168
msgid "WPvivid Backup Settings"
msgstr "Impostazioni di Backup WPvivid "

#: admin/class-wpvivid-admin.php:169
msgid "Create a Manual Backup"
msgstr "Crea un Backup Manuale"

#: admin/class-wpvivid-admin.php:170
msgid "Restore Your Site from a Backup"
msgstr "Ripristina il tuo sito da un backup"

#: admin/class-wpvivid-admin.php:171
msgid "Migrate WordPress"
msgstr "Migra WordPress"

#: admin/class-wpvivid-admin.php:209
msgid "Warning:"
msgstr "Attenzione:"

#: admin/class-wpvivid-admin.php:210
msgid "Error:"
msgstr "Errore:"

#: admin/class-wpvivid-admin.php:211
msgid "Warning: An alias for remote storage is required."
msgstr "Attenzione: Un alias per l'archiviazione remota è necessario."

#: admin/class-wpvivid-admin.php:212
msgid "Warning: The alias already exists in storage list."
msgstr "Attenzione: l'alias esiste già nella lista degli storage."

#: admin/class-wpvivid-admin.php:213
msgid "Calculating the size of files, folder and database timed out. If you continue to receive this error, please go to the plugin settings, uncheck 'Calculate the size of files, folder and database before backing up', save changes, then try again."
msgstr "Il calcolo delle dimensioni dei file, delle cartelle e database è terminata a causa di un time out. Se si continua a ricevere questo errore, si prega di andare alle impostazioni dei plug-in, deselezionare 'Calcolare le dimensioni dei file, delle cartelle e database prima di eseguire il backup', salvare le modifiche, quindi provare di nuovo."

#: admin/class-wpvivid-admin.php:346
msgid "Migration is complete and htaccess file is replaced. In order to successfully complete the migration, you'd better reinstall 301 redirect plugin, firewall and security plugin, and caching plugin if they exist."
msgstr "La migrazione è completa e il file htaccess è stato sostituito. Per completare con successo la migrazione, è meglio reinstallare il plugin 301 redirect, il plugin firewall e il plugin di sicurezza e il plugin di caching, se esistono."

#: admin/class-wpvivid-admin.php:361
msgid "Cheers! WPvivid Backup plugin has restored successfully your website. If you found WPvivid Backup plugin helpful, a 5-star rating would be highly appreciated, which motivates us to keep providing new features."
msgstr "Evviva! WPvivid Backup plugin ha ripristinato con successo il tuo sito web. Se hai trovato WPvivid Backup plugin utile, una valutazione di 5 stelle sarebbe molto apprezzata, cosa che ci spinge a continuare a offrire nuove funzionalità."

#: admin/class-wpvivid-admin.php:367
msgid "Restore completed successfully."
msgstr "Ripristino completato."

#: admin/class-wpvivid-admin.php:454
#: admin/class-wpvivid-admin.php:471
msgid "Rate Us"
msgstr "Votaci"

#: admin/class-wpvivid-admin.php:455
#: admin/class-wpvivid-admin.php:472
msgid "Maybe Later"
msgstr "Forse Più Tardi"

#: admin/class-wpvivid-admin.php:456
#: admin/class-wpvivid-admin.php:473
msgid "Never"
msgstr "Mai"

#: admin/class-wpvivid-admin.php:509
msgid "As Amazon S3 and DigitalOcean Space have upgraded their connection methods, please delete the previous connections and re-add your Amazon S3/DigitalOcean Space accounts to make sure the connections work."
msgstr "Poiché Amazon S3 e DigitalOcean Space hanno aggiornato i loro metodi di connessione, si prega di cancellare le precedenti connessioni e ri-aggiungere i tuoi account tuo Amazon S3/DigitalOcean Space per assicurarti che le connessioni funzionino."

#: admin/class-wpvivid-admin.php:561
#, php-format
msgid "The %s extension is not detected. Please install the extension first."
msgstr "L'estensione %s non è stata rilevata. Si prega di installare prima l'estensione."

#: admin/class-wpvivid-admin.php:564
#, php-format
msgid "The %s extensions are not detected. Please install the extensions first."
msgstr "Le estensioni %s non sono state rilevate. Si prega di installare prima le estensioni."

#: admin/class-wpvivid-admin.php:570
msgid "Class PclZip is not detected. Please update or reinstall your WordPress."
msgstr "Classe PclZip non rilevata. Si prega di aggiornare o reinstallare WordPress."

#: admin/class-wpvivid-admin.php:575
msgid "In order to execute the scheduled backups properly, please set the DISABLE_WP_CRON constant to false."
msgstr "Per eseguire correttamente i backup pianificati, impostare la costante DISABLE_WP_CRON su false."

#: admin/class-wpvivid-admin.php:641
msgid "Schedule"
msgstr "Pianificazione"

#: admin/class-wpvivid-admin.php:647
msgid "Remote Storage"
msgstr "Archiviazione Remota"

#: admin/class-wpvivid-admin.php:659
msgid "Debug"
msgstr "Debug"

#: admin/class-wpvivid-admin.php:665
msgid "Logs"
msgstr "Logs"

#: admin/class-wpvivid-admin.php:672
#: admin/partials/wpvivid-backup-restore-page-display.php:130
#: admin/partials/wpvivid-backup-restore-page-display.php:1505
msgid "Log"
msgstr "Registro"

#: admin/class-wpvivid-admin.php:683
msgid "MainWP"
msgstr "MainWP"

#: admin/class-wpvivid-admin.php:693
msgid "Premium"
msgstr "Premium"

#: admin/class-wpvivid-admin.php:735
#: admin/class-wpvivid-admin.php:822
#: admin/partials/wpvivid-remote-storage-page-display.php:48
msgid "Save Changes"
msgstr "Salvare le modifiche"

#: admin/class-wpvivid-admin.php:870
msgid "There are two ways available to send us the debug information. The first one is recommended."
msgstr "Ci sono due modi per inviarci le informazioni di debug. Il primo è raccomandato."

#: admin/class-wpvivid-admin.php:872
msgid "Method 1."
msgstr "Metodo 1."

#: admin/class-wpvivid-admin.php:872
msgid "If you have configured SMTP on your site, enter your email address and click the button below to send us the relevant information (website info and errors logs) when you are encountering errors. This will help us figure out what happened. Once the issue is resolved, we will inform you by your email address."
msgstr "Se è stato configurato l'SMTP sul tuo sito, inserisci il tuo indirizzo e-mail e fai clic sul pulsante qui sotto per inviarci le informazioni rilevanti (informazioni sul sito e gli errori di log) quando si verificano errori. Questo ci aiuterà a capire cosa è successo. Una volta risolto il problema, provvederemo ad informarti per tuo indirizzo e-mail."

#: admin/class-wpvivid-admin.php:875
msgid "WPvivid support email:"
msgstr "Supporto e-mail di WPvivid :"

#: admin/class-wpvivid-admin.php:876
msgid "Your email:"
msgstr "La tua email:"

#: admin/class-wpvivid-admin.php:881
msgid "I am using:"
msgstr "Io sto utilizzando:"

#: admin/class-wpvivid-admin.php:893
msgid "My web hosting provider is:"
msgstr "Il mio provider di web hosting è:"

#: admin/class-wpvivid-admin.php:902
msgid "Please describe your problem here."
msgstr "Si prega di descrivere il problema qui."

#: admin/class-wpvivid-admin.php:905
msgid "Send Debug Information to Us"
msgstr "Invia le Informazioni di Debug a Noi"

#: admin/class-wpvivid-admin.php:909
msgid "Method 2."
msgstr "Metodo 2."

#: admin/class-wpvivid-admin.php:909
msgid "If you didn’t configure SMTP on your site, click the button below to download the relevant information (website info and error logs) to your PC when you are encountering some errors. Sending the files to us will help us diagnose what happened."
msgstr "Se non hai configurato l'SMTP sul tuo sito, clicca sul pulsante qui sotto per scaricare le informazioni rilevanti (informazioni sul sito web e registri degli errori) sul tuo PC quando incontrate degli errori. L'invio dei file ci aiuterà a diagnosticare l'accaduto."

#: admin/class-wpvivid-admin.php:912
#: admin/partials/wpvivid-backup-restore-page-display.php:178
msgid "Download"
msgstr "Scarica"

#: admin/class-wpvivid-admin.php:916
msgid "Website Info Key"
msgstr "Info Key del sito web"

#: admin/class-wpvivid-admin.php:917
msgid "Website Info Value"
msgstr "Valore Info del sito web "

#: admin/class-wpvivid-admin.php:1030
msgid "Date"
msgstr "Data"

#: admin/class-wpvivid-admin.php:1031
msgid "Log Type"
msgstr "Tipo di Log"

#: admin/class-wpvivid-admin.php:1032
msgid "Log File Name"
msgstr "Nome del File di Log"

#: admin/class-wpvivid-admin.php:1033
msgid "Action"
msgstr "Azione"

#: admin/class-wpvivid-admin.php:1045
msgid " < Pre page "
msgstr "< Pagina Precedente"

#: admin/class-wpvivid-admin.php:1056
msgid " Next page > "
msgstr "Pagina Seguente >"

#: admin/class-wpvivid-admin.php:1164
msgid "If you are a MainWP user, you can set up and control WPvivid Backup Free and Pro for every child site directly from your MainWP dashboard, using our WPvivid Backup for MainWP extension."
msgstr "Se sei un utente MainWP, puoi impostare e controllare WPvivid Backup Free e Pro per ogni sito child direttamente dalla Bacheca di MainWP, utilizzando la nostra estensione WPvivid Backup for MainWP."

#: admin/class-wpvivid-admin.php:1167
msgid "Download WPvivid Backup for MainWP"
msgstr "Scarica WPvivid di Backup per MainWP"

#: admin/class-wpvivid-admin.php:1170
msgid "1. Create and download backups for a specific child site"
msgstr "1. Crea e scarica i backup per un determinato sito child"

#: admin/class-wpvivid-admin.php:1173
msgid "2. Set backup schedules for all child sites"
msgstr "2. Pianifica backup per tutti i siti child"

#: admin/class-wpvivid-admin.php:1177
msgid "3. Set WPvivid Backup Free and Pro settings for all child sites"
msgstr "3. Configura le impostazioni di WPvivid Backup Free e Pro per tutti i siti child"

#: admin/class-wpvivid-admin.php:1182
msgid "4. Install, claim and update WPvivid Backup Pro for child sites in bulk"
msgstr "4. Installa, attiva e aggiorna WPvivid Backup Pro in massa per i siti child"

#: admin/class-wpvivid-admin.php:1187
msgid "5. Set up remote storage for child sites in bulk (for WPvivid Backup Pro only)"
msgstr "5. Imposta in massa l'archiviazione remota per i siti child (per WPvivid Backup Pro)."

#: admin/class-wpvivid-admin.php:1192
#, php-format
msgid "We also offer a 40% off discount on WPvivid Backup Pro for MainWP users."
msgstr "Offriamo anche uno sconto del 40% su WPvivid Backup Pro per gli utenti MainWP."

#: admin/class-wpvivid-admin.php:1196
msgid "Ask For A 40% OFF Discount"
msgstr "Chiedi uno sconto del 40% di sconto"

#: admin/class-wpvivid-admin.php:1219
msgid "Pro Version Features"
msgstr "Funzionalità della versione Pro"

#: admin/class-wpvivid-admin.php:1220
msgid "Basic"
msgstr "Di base"

#: admin/class-wpvivid-admin.php:1221
msgid "Freelancer"
msgstr "Freelance"

#: admin/class-wpvivid-admin.php:1222
msgid "Ultimate"
msgstr "Ultimate"

#: admin/class-wpvivid-admin.php:1228
msgid "Websites"
msgstr "Siti web"

#: admin/class-wpvivid-admin.php:1229
#: admin/class-wpvivid-admin.php:1230
#: admin/class-wpvivid-admin.php:1231
#: admin/class-wpvivid-admin.php:1232
msgid "Backup:"
msgstr "Backup:"

#: admin/class-wpvivid-admin.php:1229
#: admin/class-wpvivid-admin.php:1233
msgid "Custom Content"
msgstr "Contenuto Personalizzato"

#: admin/class-wpvivid-admin.php:1230
msgid "Incremental Backup"
msgstr "Backup Incrementale"

#: admin/class-wpvivid-admin.php:1231
msgid "Create a restore point"
msgstr "Crea un punto di ripristino"

#: admin/class-wpvivid-admin.php:1232
msgid "Include/exclude files/folders"
msgstr "Includere/escludere i file e le cartelle"

#: admin/class-wpvivid-admin.php:1233
#: admin/class-wpvivid-admin.php:1234
msgid "Migration:"
msgstr "Migrazione:"

#: admin/class-wpvivid-admin.php:1234
msgid "Migration via remote storage"
msgstr "Migrazione tramite archiviazione remota"

#: admin/class-wpvivid-admin.php:1235
#: admin/class-wpvivid-admin.php:1236
msgid "Remote Storage:"
msgstr "Archiviazione Remota:"

#: admin/class-wpvivid-admin.php:1235
msgid "Custom Directory (leading cloud storage providers)"
msgstr "Directory personalizzata (fornitori leader di cloud storage)"

#: admin/class-wpvivid-admin.php:1236
msgid "WASABI/Pcloud (Only Pro)"
msgstr "WASABI/Pcloud (Solo Pro)"

#: admin/class-wpvivid-admin.php:1237
#: admin/class-wpvivid-admin.php:1238
#: admin/class-wpvivid-admin.php:1239
#: admin/class-wpvivid-admin.php:1240
msgid "Schedule:"
msgstr "Pianificazione:"

#: admin/class-wpvivid-admin.php:1237
msgid "Incremental Backup Schedule"
msgstr "Pianificazione Di Backup Incrementale"

#: admin/class-wpvivid-admin.php:1238
msgid "Custom Timezone"
msgstr "Fuso Orario Personalizzato"

#: admin/class-wpvivid-admin.php:1239
msgid "Custom content for each schedule"
msgstr "Contenuti personalizzati per ogni programma"

#: admin/class-wpvivid-admin.php:1240
msgid "Custom start time of schedule"
msgstr "Orario personalizzato di inizio del programma "

#: admin/class-wpvivid-admin.php:1241
#: admin/class-wpvivid-admin.php:1242
msgid "Restore:"
msgstr "Ripristino:"

#: admin/class-wpvivid-admin.php:1241
msgid "Restore a website from remote storage"
msgstr "Ripristina un sito web dallo storage remoto"

#: admin/class-wpvivid-admin.php:1242
msgid "Restore what you want from a backup"
msgstr "Ripristina ciò che vuoi da un backup"

#: admin/class-wpvivid-admin.php:1243
msgid "Email Reports:"
msgstr "Rapporti E-Mail:"

#: admin/class-wpvivid-admin.php:1243
msgid "Send email reports to multiple email addresses"
msgstr "Invia rapporti e-mail a più indirizzi e-mail"

#: admin/class-wpvivid-admin.php:1244
#: admin/class-wpvivid-admin.php:1245
msgid "Staging (add-on):"
msgstr "Staging (add-on):"

#: admin/class-wpvivid-admin.php:1244
msgid "Create a sub-directory staging site with one-click"
msgstr "Crea una sotto-cartella del sito di staging con un solo clic"

#: admin/class-wpvivid-admin.php:1245
msgid "Publish a staging site to a live site with one-click"
msgstr "Pubblica un sito di staging su un sito live con un solo clic"

#: admin/class-wpvivid-admin.php:1246
msgid "Roles & Capabilities (add-on):"
msgstr "Ruoli e capacità (add-on):"

#: admin/class-wpvivid-admin.php:1246
msgid "Display the individual sections according to user roles & capabilities"
msgstr "Visualizza le singole sezioni in base ai ruoli e alle capacità dell'utente"

#: admin/class-wpvivid-admin.php:1247
msgid "Support:"
msgstr "Supporto:"

#: admin/class-wpvivid-admin.php:1247
msgid "Ticket 7x24 support"
msgstr "Ticket di supporto 7x24 "

#: admin/class-wpvivid-admin.php:1250
msgid "Up to 3 sites"
msgstr "Fino a 3 siti"

#: admin/class-wpvivid-admin.php:1272
msgid "Up to 100 sites"
msgstr "Fino a 100 siti"

#: admin/class-wpvivid-admin.php:1294
msgid "Unlimited"
msgstr "Illimitato"

#: admin/class-wpvivid-admin.php:1319
msgid "*No credit card needed. Trial starts with the Free Trial plan with 2 sites. You can choose a plan at the end of the trial."
msgstr "*Non è necessaria la carta di credito. La prova inizia con il piano di prova gratuita con 2 siti. È possibile scegliere un piano alla fine della prova."

#: admin/class-wpvivid-admin.php:1320
msgid "START 14-DAY FREE TRIAL"
msgstr "INIZIA LA PROVA GRATUITA DI 14 GIORNI"

#: admin/partials/wpvivid-admin-display.php:49
msgid "WPvivid Backup Plugin"
msgstr "WPvivid Backup Plugin"

#: admin/partials/wpvivid-admin-display.php:63
msgid "Warning: There is no default remote storage available for the scheduled backups, please set up it first."
msgstr "Attenzione: non è disponibile uno storage remoto predefinito per i backup pianificati, si prega di impostarla prima."

#: admin/partials/wpvivid-backup-restore-page-display.php:7
#: admin/partials/wpvivid-schedule-page-display.php:126
msgid "Database + Files (WordPress Files)"
msgstr "Database + File (File Di WordPress)"

#: admin/partials/wpvivid-backup-restore-page-display.php:11
#: admin/partials/wpvivid-schedule-page-display.php:131
msgid "WordPress Files (Exclude Database)"
msgstr "File WordPress (Escludi database)"

#: admin/partials/wpvivid-backup-restore-page-display.php:15
#: admin/partials/wpvivid-schedule-page-display.php:136
msgid "Only Database"
msgstr "Solo Database"

#: admin/partials/wpvivid-backup-restore-page-display.php:20
msgid "Create a staging site"
msgstr "Crea un sito di staging"

#: admin/partials/wpvivid-backup-restore-page-display.php:23
#: admin/partials/wpvivid-backup-restore-page-display.php:32
#: admin/partials/wpvivid-schedule-page-display.php:24
#: admin/partials/wpvivid-schedule-page-display.php:36
#: admin/partials/wpvivid-schedule-page-display.php:98
#: admin/partials/wpvivid-schedule-page-display.php:146
msgid "Pro feature: learn more"
msgstr "Funzionalità Pro: per saperne di più"

#: admin/partials/wpvivid-backup-restore-page-display.php:29
#: admin/partials/wpvivid-schedule-page-display.php:142
msgid "Custom"
msgstr "Personalizzato"

#: admin/partials/wpvivid-backup-restore-page-display.php:100
msgid "About backup download"
msgstr "Riguardo il download dei backup "

#: admin/partials/wpvivid-backup-restore-page-display.php:102
msgid "->If backups are stored in remote storage, our plugin will retrieve the backup to your web server first. This may take a little time depending on the size of backup files. Please be patient. Then you can download them to your PC."
msgstr "->Se i backup sono memorizzati nello storage remoto, il nostro plugin recupererà prima il backup sul tuo server web. Questo può richiedere un po' di tempo a seconda della dimensione dei file di backup. Si prega di avere pazienza. Poi potrai scaricarli sul vostro PC."

#: admin/partials/wpvivid-backup-restore-page-display.php:103
msgid "->If backups are stored in web server, the plugin will list all relevant files immediately."
msgstr "->Se i backup sono memorizzati nel server web, il plugin elencherà immediatamente tutti i file rilevanti."

#: admin/partials/wpvivid-backup-restore-page-display.php:109
msgid "How to restore your website from a backup(scheduled, manual, uploaded and received backup)"
msgstr "Come ripristinare il vostro sito web da un backup(programmato, manuale, caricato e ricevuto)"

#: admin/partials/wpvivid-backup-restore-page-display.php:123
msgid "Backups"
msgstr "Backup"

#: admin/partials/wpvivid-backup-restore-page-display.php:141
#: admin/partials/wpvivid-backup-restore-page-display.php:179
#: admin/partials/wpvivid-backup-restore-page-display.php:892
msgid "Restore"
msgstr "Ripristina"

#: admin/partials/wpvivid-backup-restore-page-display.php:176
msgid "Backup"
msgstr "Backup"

#: admin/partials/wpvivid-backup-restore-page-display.php:177
msgid "Storage"
msgstr "Storage"

#: admin/partials/wpvivid-backup-restore-page-display.php:180
msgid "Delete"
msgstr "Elimina"

#: admin/partials/wpvivid-backup-restore-page-display.php:193
msgid "Delete the selected backups"
msgstr "Elimina i backup selezionati"

#: admin/partials/wpvivid-backup-restore-page-display.php:725
msgid "This backup is locked, are you sure to remove it? This backup will be deleted permanently from your hosting (localhost) and remote storages."
msgstr "Questo backup è bloccato, sei sicuro di poterlo rimuovere? Questo backup sarà cancellato in modo permanente dal vostro hosting (localhost) e dai magazzini remoti."

#: admin/partials/wpvivid-backup-restore-page-display.php:729
msgid "Are you sure to remove this backup? This backup will be deleted permanently from your hosting (localhost) and remote storages."
msgstr "Sei sicuro di voler rimuovere questo backup? Questo backup verrà eliminato in modo permanente dal tuo hosting (localhost) e storage remoto."

#: admin/partials/wpvivid-backup-restore-page-display.php:734
msgid "This request will delete the backup being downloaded, are you sure you want to continue?"
msgstr "Questa richiesta cancellerà il backup in fase di download, sei sicuro di voler continuare?"

#: admin/partials/wpvivid-backup-restore-page-display.php:773
msgid "Please select at least one item."
msgstr "Si prega di selezionare almeno un elemento."

#: admin/partials/wpvivid-backup-restore-page-display.php:778
msgid "This request might delete the backup being downloaded, are you sure you want to continue?"
msgstr "Questa richiesta potrebbe cancellare il backup in fase di download, sei sicuro di voler continuare?"

#: admin/partials/wpvivid-backup-restore-page-display.php:781
msgid "Are you sure to remove the selected backups? These backups will be deleted permanently from your hosting (localhost)."
msgstr "Sei sicuro di voler rimuovere il backup? Questi backup verranno eliminati in modo permanente dal tuo hosting (localhost)."

#: admin/partials/wpvivid-backup-restore-page-display.php:842
msgid "Step One: In the backup list, click the 'Restore' button on the backup you want to restore. This will bring up the restore tab"
msgstr "Passo 1: Nell'elenco dei backup, fare clic sul pulsante 'Ripristina' del backup che si desidera ripristinare. In questo modo si aprirà la scheda di ripristino"

#: admin/partials/wpvivid-backup-restore-page-display.php:843
msgid "Step Two: Choose an option to complete restore, if any"
msgstr "Fase Due: Scegliere una opzione per completare il ripristino, se presente"

#: admin/partials/wpvivid-backup-restore-page-display.php:844
msgid "Step Three: Click 'Restore' button"
msgstr "Fase Tre: fare Clic su pulsante 'Ripristina' "

#: admin/partials/wpvivid-backup-restore-page-display.php:877
msgid "Restore backup from:"
msgstr "Ripristino backup da:"

#: admin/partials/wpvivid-backup-restore-page-display.php:878
msgid "Please do not close the page or switch to other pages when a restore task is running, as it could trigger some unexpected errors."
msgstr "Si prega di non chiudere la pagina o di non passare ad altre pagine quando è in corso un'attività di ripristino, in quanto potrebbe innescare alcuni errori imprevisti."

#: admin/partials/wpvivid-backup-restore-page-display.php:879
msgid "Restore function will replace the current site's themes, plugins, uploads, database and/or other content directories with the existing equivalents in the selected backup."
msgstr "La funzione Ripristino sostituirà i temi, i plugin, gli upload, i database e/o altre directory di contenuti del sito corrente con gli equivalenti esistenti nel backup selezionato."

#: admin/partials/wpvivid-backup-restore-page-display.php:882
#, php-format
msgid "Restore and replace the original domain (URL) with %s (migration)"
msgstr "Ripristina e sostituisci il dominio originale (URL) con %s (migrazione)"

#: admin/partials/wpvivid-backup-restore-page-display.php:885
msgid "Restore and keep the original domain (URL) unchanged"
msgstr "Ripristina e mantenere il dominio originale (URL) invariato"

#: admin/partials/wpvivid-backup-restore-page-display.php:889
#: admin/partials/wpvivid-backup-restore-page-display.php:1787
msgid "Tips:"
msgstr "Suggerimenti:"

#: admin/partials/wpvivid-backup-restore-page-display.php:889
msgid "The plugin detects automatically either site restoration or migration (replacing the domain name) based on the current domain name. If the domain name in backup file is same as the current one, it starts restoring. On the contrary, restoring backup means to replace with the current domain name. The precondition is that the backup is created by version 0.9.21 or later."
msgstr "Il plugin rileva automaticamente il ripristino del sito o la migrazione (sostituzione del nome a dominio) in base al nome a dominio corrente. Se il nome del dominio nel file di backup è uguale a quello attuale, inizia il ripristino. Al contrario, ripristinare il backup significa sostituire con il nome di dominio corrente. Il presupposto è che il backup venga creato dalla versione 0.9.21 o successiva."

#: admin/partials/wpvivid-backup-restore-page-display.php:893
msgid "Terminate"
msgstr "Termina"

#: admin/partials/wpvivid-backup-restore-page-display.php:894
msgid "Rollback"
msgstr "Rollback"

#: admin/partials/wpvivid-backup-restore-page-display.php:896
msgid "Retrieve the backup to localhost"
msgstr "Recupera il backup su localhost"

#: admin/partials/wpvivid-backup-restore-page-display.php:897
msgid "The backup is stored on the remote storage, click on the button to download it to localhost."
msgstr "Il backup viene memorizzato sullo storage remoto, fai clic sul pulsante per scaricarlo su localhost."

#: admin/partials/wpvivid-backup-restore-page-display.php:1489
msgid "Database Size:"
msgstr "Dimensione del database:"

#: admin/partials/wpvivid-backup-restore-page-display.php:1490
msgid "File Size:"
msgstr "Dimensione del File:"

#: admin/partials/wpvivid-backup-restore-page-display.php:1493
#: admin/partials/wpvivid-settings-page-display.php:291
msgid "Total Size:"
msgstr "Dimensione Totale:"

#: admin/partials/wpvivid-backup-restore-page-display.php:1494
msgid "Uploaded:"
msgstr "Caricato:"

#: admin/partials/wpvivid-backup-restore-page-display.php:1495
msgid "Speed:"
msgstr "Velocità:"

#: admin/partials/wpvivid-backup-restore-page-display.php:1498
msgid "Network Connection:"
msgstr "Connessione di rete:"

#: admin/partials/wpvivid-backup-restore-page-display.php:1504
msgid "Cancel"
msgstr "Annulla"

#: admin/partials/wpvivid-backup-restore-page-display.php:1557
msgid "Back Up Manually"
msgstr "Esegui Il Backup Manualmente"

#: admin/partials/wpvivid-backup-restore-page-display.php:1559
msgid "Export Content"
msgstr "Esporta il contenuto"

#: admin/partials/wpvivid-backup-restore-page-display.php:1560
msgid "new feature"
msgstr "nuova funzionalità"

#: admin/partials/wpvivid-backup-restore-page-display.php:1563
msgid "Local Storage Directory:"
msgstr "Directory di storage locale:"

#: admin/partials/wpvivid-backup-restore-page-display.php:1565
msgid "rename directory"
msgstr "rinomina directory"

#: admin/partials/wpvivid-backup-restore-page-display.php:1594
msgid "Save Backups to Local"
msgstr "Salva i backup in locale"

#: admin/partials/wpvivid-backup-restore-page-display.php:1598
msgid "Send Backup to Remote Storage:"
msgstr "Invia il backup allo storage remoto:"

#: admin/partials/wpvivid-backup-restore-page-display.php:1611
msgid "Backup Now"
msgstr "Esegui il Backup Ora"

#: admin/partials/wpvivid-backup-restore-page-display.php:1616
msgid "This backup can only be deleted manually"
msgstr "Questo backup può essere cancellato solo manualmente"

#: admin/partials/wpvivid-backup-restore-page-display.php:1787
msgid "The settings are only for manual backup, which won't affect schedule settings."
msgstr "Le impostazioni sono solo per il backup manuale, che non influisce sulle impostazioni di programmazione."

#: admin/partials/wpvivid-backup-restore-page-display.php:1806
msgid "Backup Schedule"
msgstr "Pianifica il Backup"

#: admin/partials/wpvivid-backup-restore-page-display.php:1808
msgid "Schedule Status: "
msgstr "Stato di Pianificazione: "

#: admin/partials/wpvivid-backup-restore-page-display.php:1810
msgid "Server Time: "
msgstr "Ora del Server: "

#: admin/partials/wpvivid-backup-restore-page-display.php:1812
msgid "Next Backup: "
msgstr "Backup Successivo: "

#: admin/partials/wpvivid-remote-storage-page-display.php:6
#: admin/partials/wpvivid-remote-storage-page-display.php:318
msgid "Storages"
msgstr "Storage"

#: admin/partials/wpvivid-remote-storage-page-display.php:14
#: admin/partials/wpvivid-remote-storage-page-display.php:320
msgid "Storage Edit"
msgstr "Modifica Storage"

#: admin/partials/wpvivid-remote-storage-page-display.php:26
#: admin/partials/wpvivid-remote-storage-page-display.php:327
msgid "Please choose one storage to save your backups (remote storage)"
msgstr "Scegli un solo storage per salvare i tuoi backup (storage remoto)"

#: admin/partials/wpvivid-remote-storage-page-display.php:34
#: admin/partials/wpvivid-remote-storage-page-display.php:335
msgid "Storage Provider"
msgstr "Storage Provider"

#: admin/partials/wpvivid-remote-storage-page-display.php:35
#: admin/partials/wpvivid-remote-storage-page-display.php:336
msgid "Remote Storage Alias"
msgstr "Archiviazione Remota Alias"

#: admin/partials/wpvivid-remote-storage-page-display.php:36
#: admin/partials/wpvivid-remote-storage-page-display.php:337
msgid "Actions"
msgstr "Azioni"

#: admin/partials/wpvivid-schedule-page-display.php:7
msgid "Schedule Settings"
msgstr "Impostazioni Di Pianificazione"

#: admin/partials/wpvivid-schedule-page-display.php:15
msgid "Enable backup schedule"
msgstr "Attiva la pianificazione del backup"

#: admin/partials/wpvivid-schedule-page-display.php:20
msgid "Enable Incremental Backup"
msgstr "Attiva Il Backup Incrementale"

#: admin/partials/wpvivid-schedule-page-display.php:32
msgid "Advanced Schedule"
msgstr "Pianificazione Avanzata"

#: admin/partials/wpvivid-schedule-page-display.php:85
msgid "Highlighted icon illuminates that you have choosed a remote storage to store backups"
msgstr "L'icona evidenziata indica che è stata scelta uno storage remoto per memorizzare i backup"

#: admin/partials/wpvivid-schedule-page-display.php:95
msgid "+ Add another schedule"
msgstr "+ Aggiungi un'altra pianificazione"

#: admin/partials/wpvivid-schedule-page-display.php:117
msgid "Scheduled job will start at <strong>UTC</strong> time:"
msgstr "Il lavoro pianificato inizierà a <strong>UTC</strong> time:"

#: admin/partials/wpvivid-schedule-page-display.php:118
msgid "Being subjected to mechanisms of PHP, a scheduled backup task for your site will be triggered only when the site receives at least a visit at any page."
msgstr "Essendo soggetto a meccanismi di PHP, un'attività di backup pianificata per il vostro sito verrà attivata solo quando il sito riceverà almeno una visita in qualsiasi pagina."

#: admin/partials/wpvivid-settings-page-display.php:88
msgid "backups retained"
msgstr "backup conservati"

#: admin/partials/wpvivid-settings-page-display.php:88
msgid "Pro feature: Retain more backups"
msgstr "Funzionalità Pro: conserva più copie di backup"

#: admin/partials/wpvivid-settings-page-display.php:93
msgid "Calculate the size of files, folder and database before backing up"
msgstr "Calcola la dimensione dei file, delle cartelle e del database prima di eseguire il backup"

#: admin/partials/wpvivid-settings-page-display.php:99
msgid "Show WPvivid backup plugin on top admin bar"
msgstr "Mostra WPvivid backup plugin in cima all'admin bar"

#: admin/partials/wpvivid-settings-page-display.php:105
msgid "Merge all the backup files into single package when a backup completes. This will save great disk spaces, though takes longer time. We recommended you check the option especially on sites with insufficient server resources."
msgstr "Unisci tutti i file di backup in un unico pacchetto quando un backup è completato. In questo modo si risparmiano grandi spazi su disco, anche se ci vuole più tempo. Si consiglia di controllare l'opzione soprattutto su siti con risorse server insufficienti."

#: admin/partials/wpvivid-settings-page-display.php:111
msgid "Keep storing the backups in localhost after uploading to remote storage"
msgstr "Continua a memorizzare i backup in localhost dopo l'upload nello storage remoto"

#: admin/partials/wpvivid-settings-page-display.php:116
msgid "Backup Folder"
msgstr "Cartella Di Backup"

#: admin/partials/wpvivid-settings-page-display.php:118
msgid "Name your folder, this folder must be writable for creating backup files."
msgstr "Dai un nome alla tua cartella, questa cartella deve essere scrivibile per la creazione di file di backup."

#: admin/partials/wpvivid-settings-page-display.php:120
msgid "Local storage directory:"
msgstr "Directory di archiviazione locale:"

#: admin/partials/wpvivid-settings-page-display.php:125
msgid "Display domain(url) of current site in backup name. (e.g. domain_wpvivid-5ceb938b6dca9_2019-05-27-07-36_backup_all.zip)"
msgstr "Visualizza il dominio(url) del sito corrente nel nome di backup. (ad esempio dominio_wpvivid-5ceb938b6dca9_2019-05-27-07-36_backup_all.zip)"

#: admin/partials/wpvivid-settings-page-display.php:130
msgid "Remove out-of-date backups"
msgstr "Rimuovi i backup obsoleti"

#: admin/partials/wpvivid-settings-page-display.php:134
msgid "Web Server Directory:"
msgstr "Directory Del Server Web:"

#: admin/partials/wpvivid-settings-page-display.php:135
msgid "Remote Storage Directory:"
msgstr "Archiviazione Remota Directory:"

#: admin/partials/wpvivid-settings-page-display.php:147
msgid "Remove"
msgstr "Rimuovere"

#: admin/partials/wpvivid-settings-page-display.php:148
msgid "The action is irreversible! It will remove all backups are out-of-date (including local web server and remote storage) if they exist."
msgstr "L'azione è irreversibile! Rimuove tutti i backup non aggiornati (incluso il server web locale e l'archiviazione remota) se presenti."

#: admin/partials/wpvivid-settings-page-display.php:208
msgid "In order to use this function, please install a <strong><a target=\"_blank\" href=\"https://wpvivid.com/8-best-smtp-plugins-for-wordpress.html\" style=\"text-decoration: none;\">WordPress SMTP plugin</a></strong> of your preference and configure your SMTP server first. This is because WordPress uses the PHP Mail function to send its emails by default, which is not supported by many hosts and can cause issues if it is not set properly."
msgstr "Per utilizzare questa funzione, si prega di installare un <strong><a target=\"_blank\" href=\"https://wpvivid.com/8-best-smtp-plugins-for-wordpress.html\" style=\"text-decoration: none;\">plugin di SMTP di WordPress </a></strong> di tua preferenza e configurare prima il tuo server SMTP. Questo perché WordPress utilizza la funzione PHP Mail per inviare le sue e-mail di default, che non è supportata da molti host e può causare problemi se non è impostata correttamente."

#: admin/partials/wpvivid-settings-page-display.php:213
msgid "Enable email report"
msgstr "Attiva il report via e-mail"

#: admin/partials/wpvivid-settings-page-display.php:227
msgid "Test Email"
msgstr "E-Mail di Prova"

#: admin/partials/wpvivid-settings-page-display.php:232
msgid "Always send an email notification when a backup is complete"
msgstr "Invia sempre una mail di notifica quando il backup è completo"

#: admin/partials/wpvivid-settings-page-display.php:236
msgid "Only send an email notification when a backup fails"
msgstr "Invia una notifica via e-mail solo quando un backup non riesce"

#: admin/partials/wpvivid-settings-page-display.php:240
msgid "Pro feature: Add another email address to get report"
msgstr "Funzionalità Pro: aggiungi un altro indirizzo email per ottenere report"

#: admin/partials/wpvivid-settings-page-display.php:287
msgid "Web-server disk space in use by WPvivid"
msgstr "Spazio su disco del server web in uso da WPvivid"

#: admin/partials/wpvivid-settings-page-display.php:293
msgid "Calculate Sizes"
msgstr "Calcola Le Dimensioni"

#: admin/partials/wpvivid-settings-page-display.php:298
msgid "logs"
msgstr "logs"

#: admin/partials/wpvivid-settings-page-display.php:299
#: admin/partials/wpvivid-settings-page-display.php:310
#: admin/partials/wpvivid-settings-page-display.php:317
msgid "Path:"
msgstr "Percorso:"

#: admin/partials/wpvivid-settings-page-display.php:305
msgid "Backup Cache"
msgstr "Backup Della Cache"

#: admin/partials/wpvivid-settings-page-display.php:309
msgid "Junk"
msgstr "Spazzatura"

#: admin/partials/wpvivid-settings-page-display.php:316
msgid "Temporary Files"
msgstr "File Temporanei"

#: admin/partials/wpvivid-settings-page-display.php:318
msgid "Temporary Files are created by wpvivid when restoring a website."
msgstr "I file temporanei vengono creati da wpvivid durante il ripristino di un sito web."

#: admin/partials/wpvivid-settings-page-display.php:322
msgid "Empty"
msgstr "Vuoto"

#: admin/partials/wpvivid-settings-page-display.php:424
msgid "Export"
msgstr "Esporta"

#: admin/partials/wpvivid-settings-page-display.php:425
msgid "Click 'Export' button to save WPvivid settings on your local computer."
msgstr "Clicca sul pulsante \"Esporta\" per salvare le impostazioni di WPvivid sul computer locale."

#: admin/partials/wpvivid-settings-page-display.php:429
msgid "Import"
msgstr "Importazione"

#: admin/partials/wpvivid-settings-page-display.php:430
msgid "Importing the json file can help you set WPvivid's configuration on another wordpress site quickly."
msgstr "L'importazione del file json può aiutare ad impostare rapidamente la configurazione di WPvivid su un altro sito WordPress."

#: admin/partials/wpvivid-settings-page-display.php:550
msgid "Enable the option when backup failed."
msgstr "Attiva l'opzione quando il backup fallisce."

#: admin/partials/wpvivid-settings-page-display.php:550
msgid "Special optimization for web hosting/shared hosting"
msgstr "Ottimizzazione speciale per il web hosting/hosting condiviso"

#: admin/partials/wpvivid-settings-page-display.php:554
msgid "Enable optimization mode for web hosting/shared hosting"
msgstr "Attiva la modalità di ottimizzazione per il web hosting/hosting condiviso"

#: admin/partials/wpvivid-settings-page-display.php:557
msgid "Enabling this option can improve the backup success rate, but it will take more time for backup."
msgstr "L'attivazione di questa opzione consente di migliorare il tasso di successo del backup, ma richiede più tempo per il backup."

#: admin/partials/wpvivid-settings-page-display.php:564
msgid "Database access method."
msgstr "Metodo di accesso al database."

#: admin/partials/wpvivid-settings-page-display.php:569
msgid "WPDB option has a better compatibility, but the speed of backup and restore is slower."
msgstr "L'opzione WPDB ha una migliore compatibilità, ma la velocità di backup e ripristino è più lenta."

#: admin/partials/wpvivid-settings-page-display.php:575
msgid "It is recommended to choose PDO option if pdo_mysql extension is installed on your server, which lets you backup and restore your site faster."
msgstr "Si consiglia di scegliere l'opzione PDO se l'estensione pdo_mysql è installata sul vostro server, che consente di eseguire il backup e il ripristino del sito più velocemente."

#: admin/partials/wpvivid-settings-page-display.php:583
msgid "It will cause a lower CPU Usage and is recommended in a web hosting/ shared hosting environment."
msgstr "Ciò comporterà un minore utilizzo della CPU e non è raccomandato in un web hosting/ ambiente di hosting condiviso."

#: admin/partials/wpvivid-settings-page-display.php:583
msgid "Only Archive without compressing"
msgstr "Solo archivio senza compressione"

#: admin/partials/wpvivid-settings-page-display.php:587
msgid "It will cause a higher CPU Usage and is recommended in a VPS/ dedicated hosting environment."
msgstr "Provocherà un maggiore utilizzo della CPU ed è raccomandato in un ambiente di hosting dedicato o VPS."

#: admin/partials/wpvivid-settings-page-display.php:587
msgid "Compress and Archive"
msgstr "Comprimi e Archivia"

#: admin/partials/wpvivid-settings-page-display.php:596
msgid "Compress Files Every"
msgstr "Comprimi ogni File"

#: admin/partials/wpvivid-settings-page-display.php:599
msgid "Some web hosting providers limit large zip files (e.g. 200MB), and therefore splitting your backup into many parts is an ideal way to avoid hitting the limitation if you are running a big website.  Please try to adjust the value if you are encountering backup errors. If you use a value of 0 MB, any backup files won't be split."
msgstr "Alcuni fornitori di web hosting limitano i file zip di grandi dimensioni (ad esempio 200 MB), e quindi dividere il backup in molte parti è un modo ideale per evitare di colpire la limitazione se si esegue un grande sito web.  Si prega di provare a regolare tale valore se si riscontrano errori di backup. Se si utilizza un valore di 0 MB, i file di backup non verranno divisi."

#: admin/partials/wpvivid-settings-page-display.php:601
msgid "Exclude the files which are larger than"
msgstr "Escludi i file che sono più grandi di"

#: admin/partials/wpvivid-settings-page-display.php:604
msgid "Using the option will ignore the file larger than the certain size in MB when backing up, '0' (zero) means unlimited."
msgstr "Usando l'opzione si ignorerà il file più grande della dimensione determinata in MB durante il backup, '0' (zero) significa illimitato."

#: admin/partials/wpvivid-settings-page-display.php:606
msgid "PHP script execution timeout for backup"
msgstr "Timeout di esecuzione dello script PHP per il backup"

#: admin/partials/wpvivid-settings-page-display.php:609
msgid "The time-out is not your server PHP time-out. With the execution time exhausted, our plugin will shut the process of backup down. If the progress of backup encounters a time-out, that means you have a medium or large sized website, please try to scale the value bigger."
msgstr "Il time-out non è il time-out del vostro server PHP. Con il tempo di esecuzione esaurito, il nostro plugin interromperà il processo di backup. Se l'avanzamento del backup incontra un time-out, ciò significa che hai un sito web di medie o grandi dimensioni, prova ad aumentare il valore."

#: admin/partials/wpvivid-settings-page-display.php:611
msgid "PHP script execution timeout for restore"
msgstr "Timeout di esecuzione dello script PHP per il ripristino"

#: admin/partials/wpvivid-settings-page-display.php:614
msgid "The time-out is not your server PHP time-out. With the execution time exhausted, our plugin will shut the process of restore down. If the progress of restore encounters a time-out, that means you have a medium or large sized website, please try to scale the value bigger."
msgstr "Il time-out non è il time-out del vostro server PHP. Con il tempo di esecuzione esaurito, il nostro plugin chiuderà il processo di ripristino. Se il processo di ripristino incontra un time-out, ciò significa che avete un sito web di medie o grandi dimensioni, prova ad aumentare il valore."

#: admin/partials/wpvivid-settings-page-display.php:616
msgid "PHP Memory Limit for backup"
msgstr "Limite di memoria PHP per il backup"

#: admin/partials/wpvivid-settings-page-display.php:619
msgid "Adjust this value to apply for a temporary PHP memory limit for WPvivid backup plugin to run a backup. We set this value to 256M by default. Increase the value if you encounter a memory exhausted error. Note: some web hosting providers may not support this."
msgstr "Regola questo valore da applicare temporaneamente a PHP memory limit per WPvivid backup plugin nel processo di ripristino. Per impostazione predefinita abbiamo regolato questo valore a 256M. Aumenta il valore se si verifica un errore di memoria esaurita,. Nota: per alcuni provider di web hosting questo valore non può essere modificato."

#: admin/partials/wpvivid-settings-page-display.php:621
msgid "PHP Memory Limit for restoration"
msgstr "Limite di memoria PHP per il ripristino"

#: admin/partials/wpvivid-settings-page-display.php:624
msgid "Adjust this value to apply for a temporary PHP memory limit for WPvivid backup plugin in restore process. We set this value to 256M by default. Increase the value if you encounter a memory exhausted error. Note: some web hosting providers may not support this."
msgstr "Regola questo valore da applicare temporaneamente a PHP memory limit per WPvivid backup plugin nel processo di ripristino. Per impostazione predefinita abbiamo regolato questo valore a 256M. Aumenta il valore se si verifica un errore di memoria esaurita,. Nota: per alcuni provider di web hosting questo valore non può essere modificato."

#: admin/partials/wpvivid-settings-page-display.php:626
msgid "Chunk Size"
msgstr "Dimensione del Blocco"

#: admin/partials/wpvivid-settings-page-display.php:629
msgid "e.g.  if you choose a chunk size of 2MB, a 8MB file will use 4 chunks. Decreasing this value will break the ISP's transmission limit, for example:512KB"
msgstr "ad esempio, se si sceglie una dimensione del blocco di 2MB, un file da 8MB userà 4 blocchi. Ridurre questo valore per spezzare il limite di trasmissione ISP, ad esempio:512 KB"

#: admin/partials/wpvivid-settings-page-display.php:643
#, php-format
msgid "<strong>Retrying </strong>%s<strong> times when encountering a time-out error</strong>"
msgstr "<strong>Riprovando </strong>%s<strong>volte</strong> quando si verifica un errore di timeout</strong>"

#: admin/partials/wpvivid-settings-page-display.php:659
msgid "General Settings"
msgstr "Impostazioni Generali"

#: admin/partials/wpvivid-settings-page-display.php:665
msgid "Advanced Settings"
msgstr "Impostazioni Avanzate"

#: includes/class-wpvivid-backup-uploader.php:493
msgid "Tips: Click the button below to scan all uploaded or received backups in directory"
msgstr "Suggerimenti: Fare clic sul pulsante qui sotto per scansionare tutti i backup caricati o ricevuti nella directory"

#: includes/class-wpvivid-backup-uploader.php:496
msgid "Scan uploaded backup or received backup"
msgstr "Scansiona backup caricato o ricevuto"

#: includes/class-wpvivid-backup-uploader.php:543
msgid "Drop files here"
msgstr "Rilascia i file qui"

#: includes/class-wpvivid-backup-uploader.php:545
msgid "Select Files"
msgstr "Selezionare i File"

#: includes/class-wpvivid-backup.php:1062
msgid "Ready to backup. Progress: 0%, running time: 0second."
msgstr "Pronti per il backup. Progressi: 0%, tempo di esecuzione: 0 secondi."

#: includes/class-wpvivid-backup.php:1097
#: includes/class-wpvivid-backup.php:1110
#: includes/class-wpvivid-export-import.php:1308
msgid "Progress: "
msgstr "Avanzamento:"

#: includes/class-wpvivid-backup.php:1097
#: includes/class-wpvivid-backup.php:1110
#: includes/class-wpvivid-export-import.php:1308
msgid "running time: "
msgstr "tempo di esecuzione: "

#: includes/class-wpvivid-backup.php:1116
#: includes/class-wpvivid-backup.php:1133
#: includes/class-wpvivid-function-realize.php:42
msgid "The backup will be canceled after backing up the current chunk ends."
msgstr "Il backup sarà annullato dopo aver eseguito il backup delle parti correnti."

#: includes/class-wpvivid-backup.php:2256
#, php-format
msgid "Start backing up %s."
msgstr "Avvia il backup di %s."

#: includes/class-wpvivid-backup.php:2264
#, php-format
msgid "Backing up %s finished."
msgstr "Il backup di %s finito."

#: includes/class-wpvivid-export-import.php:65
#: includes/class-wpvivid-export-import.php:78
msgid "Export & Import"
msgstr "Esporta & Importa"

#: includes/class-wpvivid-export-import.php:119
msgid "Export posts or pages with images in bulk."
msgstr "Esporta in massa i post o le pagine con immagini."

#: includes/class-wpvivid-export-import.php:121
#: includes/class-wpvivid-export-import.php:1515
msgid "Learn more"
msgstr "Per saperne di più"

#: includes/class-wpvivid-export-import.php:124
msgid "This will contain all of your posts, pages, comments, terms and images (original images, featured images and thumbnails)."
msgstr "Questo conterrà tutti i vostri post, pagine, commenti, termini e immagini (immagini originali, immagini in primo piano e miniature)."

#: includes/class-wpvivid-export-import.php:125
#: includes/class-wpvivid-export-import.php:1518
msgid "Note:"
msgstr "Nota:"

#: includes/class-wpvivid-export-import.php:125
msgid "Try to select fewer items when you are facing a shortage of server resources (typically presented as a timeout error)."
msgstr "Provare a selezionare un numero inferiore di elementi quando si trovano di fronte a una carenza di risorse del server (in genere presentato come un errore di timeout)."

#: includes/class-wpvivid-export-import.php:135
#, php-format
msgid "Exported files will be temporarily stored in %s directory"
msgstr "I file esportati saranno temporaneamente memorizzati nella directory %s"

#: includes/class-wpvivid-export-import.php:141
msgid "Choose post type"
msgstr "Scegli il tipo di post"

#: includes/class-wpvivid-export-import.php:147
msgid "Post"
msgstr "Post"

#: includes/class-wpvivid-export-import.php:150
msgid "Page"
msgstr "Pagina"

#: includes/class-wpvivid-export-import.php:153
msgid "More post types coming soon..."
msgstr "Altri tipi di post in arrivo..."

#: includes/class-wpvivid-export-import.php:158
#: includes/class-wpvivid-export-import.php:181
msgid "Next Step"
msgstr "Prossimo passo"

#: includes/class-wpvivid-export-import.php:623
msgid "Choose what to export"
msgstr "Scegli ciò che vuoi esportare"

#: includes/class-wpvivid-export-import.php:631
msgid "Filter Posts/Pages"
msgstr "Filtro Messaggi/Pagine"

#: includes/class-wpvivid-export-import.php:650
msgid "All Categories"
msgstr "Tutte le Categorie"

#: includes/class-wpvivid-export-import.php:660
#, php-format
msgid "Export %s of all categories or a specific category."
msgstr "Esporta %s tutte le categorie o una categoria specifica."

#: includes/class-wpvivid-export-import.php:680
msgid "All Authors"
msgstr "Tutti gli Autori"

#: includes/class-wpvivid-export-import.php:691
#, php-format
msgid "Export %s of all authors or a specific author."
msgstr "Esporta %s di tutti gli autori o un autore specifico."

#: includes/class-wpvivid-export-import.php:703
#: includes/class-wpvivid-export-import.php:724
msgid "&mdash; Select &mdash;"
msgstr "&mdash; Seleziona &mdash;"

#: includes/class-wpvivid-export-import.php:712
#, php-format
msgid "Export %s published after this date."
msgstr "Esporta %s\", pubblicati dopo questa data."

#: includes/class-wpvivid-export-import.php:733
#, php-format
msgid "Export %s published before this date."
msgstr "Esportare %s pubblicati prima di questa data."

#: includes/class-wpvivid-export-import.php:769
msgid "Search"
msgstr "Ricerca"

#: includes/class-wpvivid-export-import.php:776
#, php-format
msgid "Search for %s according to the above rules."
msgstr "Ricerca per %s secondo le regole di cui sopra."

#: includes/class-wpvivid-export-import.php:789
msgid "Comment the export (optional)"
msgstr "Commenta l'esportazione (opzionale)"

#: includes/class-wpvivid-export-import.php:794
msgid "Comment the export: "
msgstr "Commenta l'esportazione: "

#: includes/class-wpvivid-export-import.php:798
msgid "Only letters (except for wpvivid) and numbers are allowed."
msgstr "Sono ammesse solo lettere (eccetto per wpvivid) e numeri."

#: includes/class-wpvivid-export-import.php:802
msgid "Sample:"
msgstr "Campione:"

#: includes/class-wpvivid-export-import.php:811
msgid "Export and Download"
msgstr "Esporta e Scarica"

#: includes/class-wpvivid-export-import.php:1116
msgid "Empty post id"
msgstr "Post id vuoto "

#: includes/class-wpvivid-export-import.php:1123
#: includes/class-wpvivid-export-import.php:1159
msgid "A task is already running. Please wait until the running task is complete, and try again."
msgstr "Un task è già in esecuzione. Si prega di attendere fino a quando il task in esecuzione sia completato, e di provare di nuovo."

#: includes/class-wpvivid-export-import.php:1149
msgid "Error occurred while parsing the request data. Please try to run export task again."
msgstr "Si è verificato un errore durante l'analisi dei dati della richiesta. Si prega di provare a eseguire di nuovo l'attività di esportazione."

#: includes/class-wpvivid-export-import.php:1304
msgid "Ready to export. Progress: 0%, running time: 0second."
msgstr "Pronto per l'esportazione. Progressi: 0%, tempo di esecuzione: 0 secondi."

#: includes/class-wpvivid-export-import.php:1316
msgid "The export task is not responding."
msgstr "Il task di esportazione non risponde."

#: includes/class-wpvivid-export-import.php:1341
msgid "Export error:"
msgstr "Errore di esportazione:"

#: includes/class-wpvivid-export-import.php:1375
msgid "Task time out."
msgstr "Task time out."

#: includes/class-wpvivid-export-import.php:1482
msgid "File size not match. please retry again."
msgstr "La dimensione del file non corrisponde. Riprovare di nuovo."

#: includes/class-wpvivid-export-import.php:1488
msgid "File not found. please retry again."
msgstr "File non trovato. si prega di riprovare di nuovo."

#: includes/class-wpvivid-export-import.php:1513
msgid "Import posts or pages with images in bulk."
msgstr "Importa in massa post o pagine con immagini."

#: includes/class-wpvivid-export-import.php:1519
msgid "To properly display the imported content, please make sure that the importing and exporting sites have the same environment, for example, same theme or pages built with the same page builder."
msgstr "Per visualizzare correttamente il contenuto importato, si prega di assicurarsi che l'importazione e l'esportazione siti abbiano lo stesso ambiente, per esempio, lo stesso tema o pagine costruite con lo page-builder."

#: includes/class-wpvivid-export-import.php:1528
#, php-format
msgid "Imported files will be temporarily stored in directory %s"
msgstr "I file importati saranno temporaneamente memorizzati nella directory %s"

#: includes/class-wpvivid-export-import.php:1529
msgid "Delete Exported Files In Folder"
msgstr "Elimina I file esportati nella Cartella"

#: includes/class-wpvivid-export-import.php:1534
msgid "Choose an export from your computer to import: "
msgstr "Scegli dal computer un'esportazione da importare: "

#: includes/class-wpvivid-export-import.php:1535
msgid "Upload and Import"
msgstr "Carica e Importa"

#: includes/class-wpvivid-export-import.php:1538
#, php-format
msgid "Or you can use ftp to upload the export to the directory %s. Then click the button below to scan the file to import."
msgstr "Oppure puoi usare ftp per caricare il file di esportazione nella directory %s. Quindi clicca sul pulsante qui sotto per scansionare il file da importare."

#: includes/class-wpvivid-export-import.php:1539
msgid "Scan Uploaded Exports"
msgstr "Scansiona Esportazioni Caricate"

#: includes/class-wpvivid-export-import.php:1543
msgid "The importing file info"
msgstr "Le informazioni sul file di importazione"

#: includes/class-wpvivid-export-import.php:1546
msgid "Assign author"
msgstr "Assegna un autore"

#: includes/class-wpvivid-export-import.php:1548
msgid "Select an existing author:"
msgstr "Selezionare un autore esistente:"

#: includes/class-wpvivid-export-import.php:1549
msgid "- Select -"
msgstr "- Seleziona -"

#: includes/class-wpvivid-export-import.php:1551
msgid "Import Setting"
msgstr "Impostazione di Importazione"

#: includes/class-wpvivid-export-import.php:1555
msgid "Overwrite existing pages"
msgstr "Sovrascrivere le pagine esistenti"

#: includes/class-wpvivid-export-import.php:1559
msgid "With this option checked, Pages/posts already existing will be overwritten with the updated ones in an import."
msgstr "Con questa opzione selezionata, le pagine/pubbliche già esistenti saranno sovrascritte con quelle aggiornate in un'importazione."

#: includes/class-wpvivid-export-import.php:1561
msgid "Start to Import"
msgstr "Inizia l'Importazione"

#: includes/class-wpvivid-export-import.php:1562
msgid "Back to Import Page"
msgstr "Torna alla Pagina di Importazione"

#: includes/class-wpvivid-exporter.php:71
#: includes/class-wpvivid-importer.php:50
msgid "Select All"
msgstr "Seleziona Tutto"

#: includes/class-wpvivid-exporter.php:118
msgid "Author"
msgstr "Autore"

#: includes/class-wpvivid-exporter.php:150
msgid "Comments"
msgstr "Commenti"

#: includes/class-wpvivid-exporter.php:265
msgid "Unpublished"
msgstr "Inedito"

#: includes/class-wpvivid-exporter.php:275
#, php-format
msgid "%s ago"
msgstr "%s fa"

#: includes/class-wpvivid-exporter.php:282
msgid "Published"
msgstr "Pubblicato"

#: includes/class-wpvivid-exporter.php:285
msgid "Missed schedule"
msgstr "Programmazione mancata"

#: includes/class-wpvivid-exporter.php:287
msgid "Scheduled"
msgstr "Pianificate"

#: includes/class-wpvivid-exporter.php:290
msgid "Last Modified"
msgstr "Ultima Modifica"

#: includes/class-wpvivid-exporter.php:548
#: includes/class-wpvivid-importer.php:304
msgid "First page"
msgstr "Prima pagina"

#: includes/class-wpvivid-exporter.php:559
#: includes/class-wpvivid-importer.php:315
msgid "Previous page"
msgstr "Pagina precedente"

#: includes/class-wpvivid-exporter.php:566
#: includes/class-wpvivid-exporter.php:570
#: includes/class-wpvivid-importer.php:322
#: includes/class-wpvivid-importer.php:326
msgid "Current Page"
msgstr "Pagina Corrente"

#: includes/class-wpvivid-exporter.php:584
#: includes/class-wpvivid-importer.php:340
msgid "Next page"
msgstr "Pagina successiva"

#: includes/class-wpvivid-exporter.php:594
#: includes/class-wpvivid-importer.php:350
msgid "Last page"
msgstr "Ultima pagina"

#: includes/class-wpvivid-function-realize.php:65
#: includes/class-wpvivid-function-realize.php:93
msgid "Retrieving the backup information failed while showing log. Please try again later."
msgstr "Il recupero delle informazioni di backup non è riuscito mentre si mostrava il log. Riprovare più tardi."

#: includes/class-wpvivid-function-realize.php:70
#: includes/class-wpvivid-function-realize.php:82
#: includes/class-wpvivid-function-realize.php:99
msgid "The log not found."
msgstr "Log non trovato."

#: includes/class-wpvivid-importer.php:86
msgid "File Name"
msgstr "Nome del File"

#: includes/class-wpvivid-importer.php:87
msgid "Post Types"
msgstr "Tipi di post"

#: includes/class-wpvivid-importer.php:88
msgid "Count"
msgstr "Conta"

#: includes/class-wpvivid-importer.php:89
msgid "Media Files Size"
msgstr "Dimensione dei file multimediali"

#: includes/class-wpvivid-importer.php:167
msgid "Type: "
msgstr "Tipo: "

#: includes/class-wpvivid-importer.php:1669
msgid "File not exist, file:"
msgstr "Il File non esiste, file:"

#: includes/class-wpvivid-importer.php:1676
#: includes/class-wpvivid-importer.php:1777
msgid "Sorry, this file type is not permitted for security reasons."
msgstr "Mi dispiace, questo tipo di file non è consentito per motivi di sicurezza."

#: includes/class-wpvivid-importer.php:1724
msgid "Fetching attachments is not enabled"
msgstr "Il recupero degli allegati non è abilitato"

#: includes/class-wpvivid-importer.php:1737
msgid "Invalid file type"
msgstr "Tipo di file non valido"

#: includes/class-wpvivid-importer.php:1782
msgid "File not exist file:"
msgstr "File non esiste file:"

#: includes/class-wpvivid-importer.php:1877
#: includes/class-wpvivid-importer.php:1913
#: includes/class-wpvivid-importer.php:1921
msgid "There was an error when reading this WXR file"
msgstr "Si è verificato un errore durante la lettura di questo file WXR"

#: includes/class-wpvivid-importer.php:1878
msgid "Details are shown above. The importer will now try again with a different parser..."
msgstr "I dettagli sono riportati sopra. L'importatore ora riproverà con un altro parser..."

#: includes/class-wpvivid-importer.php:1925
#: includes/class-wpvivid-importer.php:1930
#: includes/class-wpvivid-importer.php:2155
#: includes/class-wpvivid-importer.php:2351
msgid "This does not appear to be a WXR file, missing/invalid WXR version number"
msgstr "Questo non sembra essere un file WXR, numero di versione WXR mancante/non valido"

#: includes/class-wpvivid-interface-mainwp.php:138
msgid "Error occurred while parsing the request data. Please try to run backup again."
msgstr "Si è verificato un errore durante l'analisi dei dati della richiesta. Si prega di provare a eseguire di nuovo il backup."

#: includes/class-wpvivid-interface-mainwp.php:182
#: includes/class-wpvivid-interface-mainwp.php:220
#: includes/class-wpvivid-interface-mainwp.php:273
msgid "Unable to open the log file."
msgstr "Impossibile aprire il file di registro."

#: includes/class-wpvivid-interface-mainwp.php:211
msgid "Reading the log failed. Please try again."
msgstr "La lettura del registro dei log non è riuscita. Riprovare."

#: includes/class-wpvivid-mail-report.php:558
msgid "Unable to send email. Please check the configuration of email server."
msgstr "Impossibile inviare e-mail. Si prega di controllare la configurazione del server di posta elettronica."

#: includes/class-wpvivid-migrate.php:49
msgid "Auto-Migration"
msgstr "Auto-Migrazione"

#: includes/class-wpvivid-migrate.php:55
msgid "Key"
msgstr "Chiave"

#: includes/class-wpvivid-migrate.php:390
msgid "In order to allow another site to send a backup to this site, please generate a key below. Once the key is generated, this site is ready to receive a backup from another site. Then, please copy and paste the key in sending site and save it."
msgstr "Per consentire ad un altro sito di inviare un backup a questo sito, si prega di generare una chiave qui sotto. Una volta generata la chiave, questo sito è pronto a ricevere un backup da un altro sito. Quindi, si prega di copiare e incollare la chiave nel sito di invio e salvarla."

#: includes/class-wpvivid-migrate.php:392
msgid "The key will expire in "
msgstr "La chiave scadrà in "

#: includes/class-wpvivid-migrate.php:399
msgid "Tips: For security reason, please choose an appropriate expiration time for the key."
msgstr "Suggerimenti: Per motivi di sicurezza, si prega di scegliere un tempo di scadenza appropriato per la chiave."

#: includes/class-wpvivid-migrate.php:401
msgid "Generate"
msgstr "Genera"

#: includes/class-wpvivid-migrate.php:850
msgid "Please paste the key below."
msgstr "Si prega di incollare la chiave qui di seguito."

#: includes/class-wpvivid-migrate.php:850
msgid "How to get a site key?"
msgstr "Come ottenere una site key?"

#: includes/class-wpvivid-migrate.php:853
msgid "Save"
msgstr "Salva"

#: includes/class-wpvivid-migrate.php:969
msgid "1. Visit Key tab page of WPvivid backup plugin of destination site."
msgstr "1. Visita la scheda Chiave nella pagina di WPvivid backup plugin del sito di destinazione."

#: includes/class-wpvivid-migrate.php:970
msgid "2. Generate a key by clicking Generate button and copy it."
msgstr "2. Genera una chiave cliccando il pulsante Genera e copialo."

#: includes/class-wpvivid-migrate.php:971
msgid "3. Go back to this page and paste the key in key box below. Lastly, click Save button."
msgstr "3. Torna a questa pagina e incolla il codice nella casella chiave sottostante. Infine, fai clic su Salva."

#: includes/class-wpvivid-migrate.php:985
msgid "The feature can help you transfer a Wordpress site to a new domain(site). It would be a convenient way to migrate your WP site from dev environment to live server or from old server to the new."
msgstr "La funzione può aiutare a trasferire un sito Wordpress su un nuovo dominio (sito). Sarebbe un modo conveniente per migrare il vostro sito WP dall'ambiente dev al live server o dal vecchio server al nuovo."

#: includes/class-wpvivid-migrate.php:995
msgid "Choose the content you want to transfer"
msgstr "Scegli il contenuto che vuoi trasferire"

#: includes/class-wpvivid-migrate.php:1010
msgid "Clone then Transfer"
msgstr "Clona e Trasferisci"

#: includes/class-wpvivid-migrate.php:1090
msgid "Note: "
msgstr "Nota: "

#: includes/class-wpvivid-migrate.php:1091
msgid "1. In order to successfully complete the migration, you'd better deactivate <a href=\"https://wpvivid.com/best-redirect-plugins.html\" target=\"_blank\" style=\"text-decoration: none;\">301 redirect plugin</a>, <a href=\"https://wpvivid.com/8-best-wordpress-firewall-plugins.html\" target=\"_blank\" style=\"text-decoration: none;\">firewall and security plugin</a>, and <a href=\"https://wpvivid.com/best-free-wordpress-caching-plugins.html\" target=\"_blank\" style=\"text-decoration: none;\">caching plugin</a> (if they exist) before transferring website."
msgstr "1. Per completare con successo la migrazione, è meglio disattivare <a href=\"https://wpvivid.com/best-redirect-plugins.html\" target=\"_blank\" style=\"text-decoration: none;\">plugin di reindirizzamento 301</a>, <a href=\"https://wpvivid.com/8-best-wordpress-firewall-plugins. html\" target=\"_blank\" style=\"text-decoration: nessuno;\">firewall e plugin di sicurezza</a>, e <a href=\"https://wpvivid.com/best-free-wordpress-caching-plugins.html\" target=\"_blank\" style=\"text-decoration: nessuno;\">plugin di caching</a> (se esistono) prima di trasferire il sito web."

#: includes/class-wpvivid-migrate.php:1092
msgid "2. Please migrate website with the manual way when using <strong>Local by Flywheel</strong> environment."
msgstr "2. Si prega di migrare sito web manualmente quando si utilizza <strong>Local by Flywheel</strong>."

#: includes/class-wpvivid-migrate.php:1098
msgid "<strong>Tips: </strong>The unstable connection between sites could cause a failure of files transfer. In this case, uploading backups to destination site is a good alternative to the automatic website migration."
msgstr "<strong>Suggerimenti: </strong>La connessione instabile tra i siti potrebbe causare un fallimento del trasferimento dei file. In questo caso, il caricamento del backup sul sito di destinazione è una buona alternativa alla migrazione automatica del sito web."

#: includes/class-wpvivid-migrate.php:1099
msgid "How to migrate Wordpress site manually to a new domain(site) with WPvivid backup plugin?"
msgstr "Come migrare manualmente il sito Wordpress su un nuovo dominio (sito) con il plugin di backup WPvivid?"

#: includes/class-wpvivid-migrate.php:1100
msgid "1. Download a backup in backups list to your computer."
msgstr "1. Scarica un backup dall'elenco di backup sul tuo computer."

#: includes/class-wpvivid-migrate.php:1101
msgid "2. Upload the backup to destination site. There are two ways available to use:"
msgstr "2. Carica il backup del sito di destinazione. Ci sono due modi disponibili:"

#: includes/class-wpvivid-migrate.php:1102
msgid "2.1 Upload the backup to the upload section of WPvivid backup plugin in destination site."
msgstr "2.1 Carica il backup nella sezione di caricamento di WPvivid backup plugin nel sito di destinazione."

#: includes/class-wpvivid-migrate.php:1103
#, php-format
msgid "2.2 Upload the backup with FTP client to backup directory %s in destination site, then click <strong>Scan uploaded backup or received backup</strong> button."
msgstr "2.2 Carica il backup tramite client FTP nella directory di backup %s del sito di destinazione, quindi fai clic sul pulsante <strong>Scan backup caricato o backup ricevuto</strong>."

#: includes/class-wpvivid-migrate.php:1104
msgid "3. Once done, the backup appears in backups list. Then, restore the backup."
msgstr "3. Una volta fatto, il backup viene visualizzato nell'elenco dei backup. Quindi, ripristina il backup."

#: includes/class-wpvivid-migrate.php:1124
msgid "Choose what to migrate"
msgstr "Scegli ciò che vuoi migrare"

#: includes/class-wpvivid-migrate.php:1185
msgid "Transfer succeeded. Please scan the backup list on the destination site to display the backup, then restore the backup."
msgstr "Il trasferimento è riuscito. Si prega di scansione l'elenco di backup del sito di destinazione per visualizzare il backup e poi ripristinare il backup."

#: includes/class-wpvivid-migrate.php:1245
msgid "Upload"
msgstr "Carica"

#: includes/class-wpvivid-migrate.php:1255
#, php-format
msgid "The backups will be uploaded to %s directory."
msgstr "I backup verranno caricati nella directory %s."

#: includes/class-wpvivid-migrate.php:1258
msgid "Note: The files you want to upload must be a backup created by WPvivid backup plugin. Make sure that uploading every part of a backup to the directory if the backup is split into many parts"
msgstr "Nota: I file che si desidera caricare devono essere un backup creato dal plugin di backup di WPvivid. Assicurarsi di aver caricato ogni parte del backup nella directory se il backup è suddiviso in più parti"

#: includes/class-wpvivid-schedule.php:34
msgid "12 Hours"
msgstr "12 Ore"

#: includes/class-wpvivid-schedule.php:40
#: includes/class-wpvivid-schedule.php:94
msgid "Daily"
msgstr "Giornaliero"

#: includes/class-wpvivid-schedule.php:46
#: includes/class-wpvivid-schedule.php:97
msgid "Weekly"
msgstr "Settimanale"

#: includes/class-wpvivid-schedule.php:52
#: includes/class-wpvivid-schedule.php:100
msgid "Fortnightly"
msgstr "Quindicinale"

#: includes/class-wpvivid-schedule.php:58
#: includes/class-wpvivid-schedule.php:103
msgid "Monthly"
msgstr "Mensile"

#: includes/class-wpvivid-schedule.php:91
msgid "12Hours"
msgstr "12 ore"

#: includes/class-wpvivid-schedule.php:282
#: includes/class-wpvivid-schedule.php:296
msgid "Creating scheduled tasks failed. Please try again later."
msgstr "Creazione di attività pianificate non riuscita. Si prega di riprovare più tardi."

#: includes/class-wpvivid.php:507
#: includes/class-wpvivid.php:515
msgid "A backup type is required."
msgstr "È richiesto un tipo di backup."

#: includes/class-wpvivid.php:521
#: includes/class-wpvivid.php:530
msgid "Choose at least one storage location for backups."
msgstr "Scegli almeno una posizione di archiviazione per il backup."

#: includes/class-wpvivid.php:541
#: includes/class-wpvivid.php:4557
msgid "There is no default remote storage configured. Please set it up first."
msgstr "Non è stata configurata una memoria remota predefinita. Si prega di impostarla prima."

#: includes/class-wpvivid.php:1408
#: includes/class-wpvivid.php:1430
#: includes/class-wpvivid.php:1456
#: includes/class-wpvivid.php:1581
#: includes/class-wpvivid.php:1701
msgid "Too many resumption attempts."
msgstr "Troppi tentativi di ripresa."

#: includes/class-wpvivid.php:1589
#: includes/class-wpvivid.php:1709
msgid "Task timed out."
msgstr "Task è scaduto."

#: includes/class-wpvivid.php:2479
msgid "Retrieving the backup(s) information failed while deleting the selected backup(s). Please try again later."
msgstr "Il recupero delle informazioni del backup non è riuscito mentre si cancellavano i backup selezionati. Riprovare più tardi."

#: includes/class-wpvivid.php:2489
msgid "Unable to delete the locked backup. Please unlock it first and try again."
msgstr "Non è possibile cancellare il backup bloccato. Si prega di sbloccarlo prima e provare di nuovo."

#: includes/class-wpvivid.php:2592
msgid "You have successfully added a remote storage."
msgstr "Hai aggiunto con successo uno storage remoto."

#: includes/class-wpvivid.php:2644
msgid "Fail to delete the remote storage, can not retrieve the storage infomation. Please try again."
msgstr "Impossibile cancellare lo storage remoto, non è possibile recuperare le informazioni sulla storage. Si prega di riprovare."

#: includes/class-wpvivid.php:2672
msgid "Failed to get the remote storage information. Please try again later."
msgstr "Impossibile ottenere le informazioni di archiviazione remota. Si prega di riprovare più tardi."

#: includes/class-wpvivid.php:3416
msgid "restore failed error unknown"
msgstr "ripristino non riuscito errore sconosciuto"

#: includes/class-wpvivid.php:3449
msgid "The restore file not found. Please verify the file exists."
msgstr "Il file di ripristino non è stato trovato. Si prega di verificare che il file esista."

#: includes/class-wpvivid.php:3561
#: includes/class-wpvivid.php:3577
msgid "The last backup message not found."
msgstr "L'ultimo messaggio di backup non è stato trovato."

#: includes/class-wpvivid.php:3580
msgid "Last Backup: "
msgstr "Ultimo Backup: "

#: includes/class-wpvivid.php:3677
#, php-format
msgid "%d backup tasks have been completed. Please switch to <a href=\"#\" onclick=\"wpvivid_click_switch_page('wrap', 'wpvivid_tab_log', true);\">Log</a> page to check the details."
msgstr "%d I task di backup sono stati completati.. Vai alla <a href=\"#\" onclick=\"wpvivid_click_switch_page('wrap', 'wpvivid_tab_log', true);\">Pagina di Log</a>  per controllare i dettagli."

#: includes/class-wpvivid.php:3770
msgid "Getting backup directory failed. Please try again later."
msgstr "Impossibile ottenere la directory di backup. Riprovare più tardi."

#: includes/class-wpvivid.php:3965
msgid "Choose at least one type of junk files for deleting."
msgstr "Scegli almeno un tipo di file indesiderato da eliminare."

#: includes/class-wpvivid.php:4039
msgid "The selected junk flies have been deleted."
msgstr "I file spazzatura selezionati sono stati cancellati."

#: includes/class-wpvivid.php:4210
msgid "Choose one storage from the list to be the default storage."
msgstr "Scegli uno storage dalla lista come storage predefinito."

#: includes/class-wpvivid.php:4465
#: includes/class-wpvivid.php:4473
msgid "The maximum zip file size is required."
msgstr "È richiesta la dimensione massima del file zip."

#: includes/class-wpvivid.php:4479
#: includes/class-wpvivid.php:4486
msgid "The size for excluded files is required."
msgstr "È richiesta la dimensione per i file esclusi."

#: includes/class-wpvivid.php:4492
#: includes/class-wpvivid.php:4499
msgid "The maximum execution time for PHP script is required."
msgstr "È richiesto il tempo di esecuzione massimo per gli script PHP."

#: includes/class-wpvivid.php:4505
#: includes/class-wpvivid.php:4512
msgid "The local storage path is required."
msgstr "È necessario il percorso di storage locale."

#: includes/class-wpvivid.php:4522
msgid "An email address is required."
msgstr "Un indirizzo e-mail è obbligatorio."

#: includes/class-wpvivid.php:4531
#: includes/class-wpvivid.php:4535
msgid "The pdo_mysql extension is not detected. Please install the extension first or choose wpdb option for Database connection method."
msgstr "L'estensione pdo_mysql non viene rilevata. Si prega di installare prima l'estensione o di scegliere l'opzione wpdb per il metodo di connessione al database."

#: includes/class-wpvivid.php:4654
msgid "The selected file is not the setting file for WPvivid. Please upload the right file."
msgstr "Il file selezionato non è il file di impostazione di WPvivid. Si prega di caricare il file giusto."

#: includes/class-wpvivid.php:4676
msgid "Invalid email address"
msgstr "Indirizzo email non valido"

#: includes/class-wpvivid.php:5248
msgid "Type:"
msgstr "Tipo:"

#: includes/class-wpvivid.php:5402
msgid "Save backups on localhost (web server)"
msgstr "Salva i backup su localhost (server web)"

#: includes/class-wpvivid.php:5406
msgid "Send backups to remote storage (choose this option, the local backup will be deleted after uploading to remote storage completely)"
msgstr "Invia i backup allo storage remoto (scegli questa opzione, il backup locale verrà cancellato dopo averlo caricato completamente nello storage remoto)"

#: includes/class-wpvivid.php:5571
msgid "User's email address is required."
msgstr "Indirizzo email dell'utente è obbligatorio."

#: includes/class-wpvivid.php:5576
msgid "Please enter a valid email address."
msgstr "Si prega di inserire un indirizzo email valido."

#: includes/customclass/class-wpvivid-amazons3-plus.php:44
msgid "Amazon S3"
msgstr "Amazon S3"

#: includes/customclass/class-wpvivid-amazons3-plus.php:62
#: includes/customclass/class-wpvivid-amazons3-plus.php:200
msgid "Enter Your Amazon S3 Account"
msgstr "Inserisci Il Tuo Account Amazon S3"

#: includes/customclass/class-wpvivid-amazons3-plus.php:70
#: includes/customclass/class-wpvivid-amazons3-plus.php:208
msgid "Enter a unique alias: e.g. Amazon S3-001"
msgstr "Inserisci un alias univoco: ad esempio, Amazon S3-001"

#: includes/customclass/class-wpvivid-amazons3-plus.php:75
#: includes/customclass/class-wpvivid-amazons3-plus.php:213
#: includes/customclass/class-wpvivid-dropbox.php:477
#: includes/customclass/class-wpvivid-dropbox.php:602
#: includes/customclass/class-wpvivid-ftpclass.php:57
#: includes/customclass/class-wpvivid-ftpclass.php:184
#: includes/customclass/class-wpvivid-google-drive.php:319
#: includes/customclass/class-wpvivid-google-drive.php:447
msgid "A name to help you identify the storage if you have multiple remote storage connected."
msgstr "Un nome per aiutarti a identificare lo storage se hai collegato più storage remoti."

#: includes/customclass/class-wpvivid-amazons3-plus.php:82
#: includes/customclass/class-wpvivid-amazons3-plus.php:220
msgid "Amazon S3 access key"
msgstr "Amazon S3 access key"

#: includes/customclass/class-wpvivid-amazons3-plus.php:87
#: includes/customclass/class-wpvivid-amazons3-plus.php:225
msgid "Enter your Amazon S3 access key."
msgstr "Inserisci la tua access key di Amazon S3."

#: includes/customclass/class-wpvivid-amazons3-plus.php:87
#: includes/customclass/class-wpvivid-amazons3-plus.php:225
msgid "How to get an AmazonS3 access key."
msgstr "Come ottenere una access key di AmazonS3."

#: includes/customclass/class-wpvivid-amazons3-plus.php:94
#: includes/customclass/class-wpvivid-amazons3-plus.php:232
msgid "Amazon S3 secret key"
msgstr "Amazon S3 secret key"

#: includes/customclass/class-wpvivid-amazons3-plus.php:99
#: includes/customclass/class-wpvivid-amazons3-plus.php:237
msgid "Enter your Amazon S3 secret key."
msgstr "Inserisci la tua secret key di Amazon S3."

#: includes/customclass/class-wpvivid-amazons3-plus.php:99
#: includes/customclass/class-wpvivid-amazons3-plus.php:237
msgid "How to get an AmazonS3 secret key."
msgstr "Come ottenere una secret key di AmazonS3."

#: includes/customclass/class-wpvivid-amazons3-plus.php:106
#: includes/customclass/class-wpvivid-amazons3-plus.php:244
msgid "Amazon S3 Bucket Name(e.g. test)"
msgstr "Nome del Bucket di Amazon S3 (ad esempio, test)"

#: includes/customclass/class-wpvivid-amazons3-plus.php:111
#: includes/customclass/class-wpvivid-amazons3-plus.php:249
msgid "Enter an existed Bucket to create a custom backup storage directory."
msgstr "Inserisci un Bucket esistente per creare una directory di archiviazione di backup personalizzata."

#: includes/customclass/class-wpvivid-amazons3-plus.php:118
#: includes/customclass/class-wpvivid-amazons3-plus.php:256
msgid "Custom Path"
msgstr "Percorso Personalizzato"

#: includes/customclass/class-wpvivid-amazons3-plus.php:123
#: includes/customclass/class-wpvivid-amazons3-plus.php:261
msgid "Customize the directory where you want to store backups within the Bucket."
msgstr "Personalizza la directory in cui si desidera archiviare i backup all'interno del Bucket."

#: includes/customclass/class-wpvivid-amazons3-plus.php:131
#: includes/customclass/class-wpvivid-dropbox.php:509
#: includes/customclass/class-wpvivid-ftpclass.php:125
#: includes/customclass/class-wpvivid-google-drive.php:351
msgid "Set as the default remote storage."
msgstr "Impostato come storage remoto predefinito."

#: includes/customclass/class-wpvivid-amazons3-plus.php:137
#: includes/customclass/class-wpvivid-dropbox.php:515
#: includes/customclass/class-wpvivid-ftpclass.php:131
#: includes/customclass/class-wpvivid-google-drive.php:357
msgid "Once checked, all this sites backups sent to a remote storage destination will be uploaded to this storage by default."
msgstr "Una volta spuntati, tutti i backup di questi siti inviati a una destinazione di archiviazione remota saranno caricati in questo storage di default."

#: includes/customclass/class-wpvivid-amazons3-plus.php:145
#: includes/customclass/class-wpvivid-amazons3-plus.php:269
msgid "Storage class: Standard (infrequent access)."
msgstr "Classe di storage: Standard (accesso non frequente)."

#: includes/customclass/class-wpvivid-amazons3-plus.php:151
#: includes/customclass/class-wpvivid-amazons3-plus.php:275
msgid "Check the option to use Amazon S3 Standard-Infrequent Access (S3 Standard-IA) storage class for data transfer."
msgstr "Seleziona l'opzione per utilizzare la classe di archiviazione Amazon S3 Standard-Infrequent Access (S3 Standard-IA) per il trasferimento dei dati."

#: includes/customclass/class-wpvivid-amazons3-plus.php:159
#: includes/customclass/class-wpvivid-amazons3-plus.php:283
msgid "Server-side encryption."
msgstr "Crittografia Lato Server."

#: includes/customclass/class-wpvivid-amazons3-plus.php:165
#: includes/customclass/class-wpvivid-amazons3-plus.php:289
msgid "Check the option to use Amazon S3 server-side encryption to protect data."
msgstr "Seleziona l'opzione per utilizzare la crittazione lato server di Amazon S3 per proteggere i dati."

#: includes/customclass/class-wpvivid-amazons3-plus.php:173
#: includes/customclass/class-wpvivid-ftpclass.php:153
msgid "Test and Add"
msgstr "Testa e Aggiungi"

#: includes/customclass/class-wpvivid-amazons3-plus.php:178
msgid "Click the button to connect to Amazon S3 storage and add it to the storage list below."
msgstr "Clicca il pulsante per connetterti allo storage di Amazon S3 e aggiungerlo alla lista degli storage qui di seguito."

#: includes/customclass/class-wpvivid-amazons3-plus.php:187
msgid "The simplexml extension is not detected. Please install the extension first."
msgstr "L'estensione simplexml non viene rilevata. Si prega di installare prima l'estensione."

#: includes/customclass/class-wpvivid-amazons3-plus.php:302
#: includes/customclass/class-wpvivid-dropbox.php:614
#: includes/customclass/class-wpvivid-ftpclass.php:259
#: includes/customclass/class-wpvivid-google-drive.php:459
msgid "Click the button to save the changes."
msgstr "Clicca il pulsante per salvare le modifiche."

#: includes/customclass/class-wpvivid-dropbox.php:433
msgid "You have authenticated the Dropbox account as your remote storage."
msgstr "Hai autenticato l'account Dropbox come tuo storage remoto."

#: includes/customclass/class-wpvivid-dropbox.php:441
#: includes/customclass/class-wpvivid-google-drive.php:280
msgid "You have successfully updated the storage alias."
msgstr "Hai aggiornato con successo l'alias di archiviazione."

#: includes/customclass/class-wpvivid-dropbox.php:452
msgid "Dropbox"
msgstr "Dropbox"

#: includes/customclass/class-wpvivid-dropbox.php:462
msgid "Please read <a target=\"_blank\" href=\"https://wpvivid.com/privacy-policy\" style=\"text-decoration: none;\">this privacy policy</a> for use of our Dropbox authorization app (none of your backup data is sent to us)."
msgstr "Si prega di leggere <a target=\"_blank\" href=\"https://wpvivid.com/privacy-policy\" style=\"text-decoration: none;\">questa informativa sulla privacy</a> per l'utilizzo della nostra applicazione di autorizzazione per Dropbox (nessuno dei dati di backup viene inviato a noi)."

#: includes/customclass/class-wpvivid-dropbox.php:465
#: includes/customclass/class-wpvivid-dropbox.php:590
msgid "Enter Your Dropbox Information"
msgstr "Inserisci le informazioni del tuo account Dropbox"

#: includes/customclass/class-wpvivid-dropbox.php:472
#: includes/customclass/class-wpvivid-dropbox.php:597
msgid "Enter a unique alias: e.g. Dropbox-001"
msgstr "Inserisci un alias univoco: ad esempio Dropbox-001"

#: includes/customclass/class-wpvivid-dropbox.php:489
#: includes/customclass/class-wpvivid-google-drive.php:331
msgid "All backups will be uploaded to this directory."
msgstr "Tutti i backup verranno caricati in questa directory."

#: includes/customclass/class-wpvivid-dropbox.php:501
#: includes/customclass/class-wpvivid-google-drive.php:343
msgid "Pro feature: Create a directory for storing the backups of the site"
msgstr "Funzionalità Pro: crea una cartella per memorizzare il backup del sito"

#: includes/customclass/class-wpvivid-dropbox.php:522
msgid "Authenticate with Dropbox"
msgstr "Autenticati con Dropbox"

#: includes/customclass/class-wpvivid-dropbox.php:527
msgid "Click the button to get Dropbox authentication and add it to the storage list below."
msgstr "Clicca il pulsante per ottenere il codice di autenticazione di Dropbox e aggiungerlo alla lista degli storage qui di seguito."

#: includes/customclass/class-wpvivid-ftpclass.php:36
msgid "FTP"
msgstr "FTP"

#: includes/customclass/class-wpvivid-ftpclass.php:45
#: includes/customclass/class-wpvivid-ftpclass.php:172
msgid "Enter Your FTP Account"
msgstr "Inserisci il tuo Account FTP"

#: includes/customclass/class-wpvivid-ftpclass.php:52
#: includes/customclass/class-wpvivid-ftpclass.php:179
msgid "Enter an unique alias: e.g. FTP-001"
msgstr "Inserisci un alias univoco: ad esempio FTP-001"

#: includes/customclass/class-wpvivid-ftpclass.php:64
#: includes/customclass/class-wpvivid-ftpclass.php:191
msgid "FTP server (server's port 21)"
msgstr "FTP server (server sulla porta 21)"

#: includes/customclass/class-wpvivid-ftpclass.php:69
#: includes/customclass/class-wpvivid-ftpclass.php:196
msgid "Enter the FTP server."
msgstr "Inserisci il server FTP."

#: includes/customclass/class-wpvivid-ftpclass.php:81
msgid "Pro feature: Change the FTP default port number"
msgstr "Funzionalità Pro: cambia il numero di porta predefinito FTP"

#: includes/customclass/class-wpvivid-ftpclass.php:88
#: includes/customclass/class-wpvivid-ftpclass.php:203
msgid "FTP login"
msgstr "Accesso FTP"

#: includes/customclass/class-wpvivid-ftpclass.php:93
#: includes/customclass/class-wpvivid-ftpclass.php:208
msgid "Enter your FTP server user name."
msgstr "Inserisci il nome utente del tuo server FTP."

#: includes/customclass/class-wpvivid-ftpclass.php:100
#: includes/customclass/class-wpvivid-ftpclass.php:215
msgid "FTP password"
msgstr "Password FTP"

#: includes/customclass/class-wpvivid-ftpclass.php:105
#: includes/customclass/class-wpvivid-ftpclass.php:220
msgid "Enter the FTP server password."
msgstr "Inserisci la password del server FTP."

#: includes/customclass/class-wpvivid-ftpclass.php:112
#: includes/customclass/class-wpvivid-ftpclass.php:227
msgid "Absolute path must exist(e.g. /home/username)"
msgstr "Un percorso assoluto deve esistere(per esempio, /home/nomeutente)"

#: includes/customclass/class-wpvivid-ftpclass.php:117
#: includes/customclass/class-wpvivid-ftpclass.php:232
msgid "Enter an absolute path and a custom subdirectory (optional) for holding the backups of current website. For example, /home/username/customfolder"
msgstr "Inserisci un percorso assoluto e una sottodirectory personalizzata (opzionale) per tenere i backup del sito web corrente. Ad esempio, /home/nomeutente/cartellapersonalizzata"

#: includes/customclass/class-wpvivid-ftpclass.php:139
#: includes/customclass/class-wpvivid-ftpclass.php:240
msgid "Uncheck this to enable FTP active mode."
msgstr "Deseleziona questa opzione per abilitare il server FTP in modalità attiva."

#: includes/customclass/class-wpvivid-ftpclass.php:145
#: includes/customclass/class-wpvivid-ftpclass.php:246
msgid "Uncheck the option to use FTP active mode when transferring files. Make sure the FTP server you are configuring supports the active FTP mode."
msgstr "Deselezionare l'opzione per utilizzare l'FTP in modalità attiva quando il trasferimento di file. Assicurarsi che il server FTP configurazione supporti la modalità FTP attiva."

#: includes/customclass/class-wpvivid-ftpclass.php:158
msgid "Click the button to connect to FTP server and add it to the storage list below."
msgstr "Clicca il pulsante per connetterti al server FTP e aggiungerlo alla lista degli storage qui di seguito."

#: includes/customclass/class-wpvivid-google-drive.php:111
#: includes/customclass/class-wpvivid-google-drive.php:207
msgid "Authentication failed, the client_secrets.json file is missing. Please make sure the client_secrets.json file is in wpvivid-backuprestore\\includes\\customclass directory."
msgstr "Autenticazione non riuscita, il file client_secrets.json è mancante. Si prega di assicurarsi che il file  client_secrets.json sia in wpvivid-backuprestore\\include\\customclass directory."

#: includes/customclass/class-wpvivid-google-drive.php:115
#: includes/customclass/class-wpvivid-google-drive.php:211
msgid "Authentication failed, the format of the client_secrets.json file is incorrect. Please delete and re-install the plugin to recreate the file."
msgstr "Autenticazione non riuscita, il formato del file client_secrets.json non è corretto. Si consiglia di cancellare e re-installare il plugin per ricreare il file."

#: includes/customclass/class-wpvivid-google-drive.php:272
msgid "You have authenticated the WPvividGoogle Drive account as your remote storage."
msgstr "Hai autenticato l'account WPvividGoogle Drive come tuo storage remoto."

#: includes/customclass/class-wpvivid-google-drive.php:292
msgid "WPvividGoogle Drive"
msgstr "WPvividGoogle Drive"

#: includes/customclass/class-wpvivid-google-drive.php:304
msgid "Please read <a target=\"_blank\" href=\"https://wpvivid.com/privacy-policy\" style=\"text-decoration: none;\">this privacy policy</a> for use of our WPvividGoogle Drive authorization app (none of your backup data is sent to us)."
msgstr "Si prega di leggere <a target=\"_blank\" href=\"https://wpvivid.com/privacy-policy\" style=\"text-decoration: none;\">questa informativa sulla privacy</a> per l'utilizzo della nostra applicazione di autorizzazione a WPvividGoogle Drive (nessuno dei dati di backup viene inviato a noi)."

#: includes/customclass/class-wpvivid-google-drive.php:307
#: includes/customclass/class-wpvivid-google-drive.php:435
msgid "Enter Your WPvividGoogle Drive Information"
msgstr "Inserisci le informazioni del tuo account WPvividGoogle Drive"

#: includes/customclass/class-wpvivid-google-drive.php:314
#: includes/customclass/class-wpvivid-google-drive.php:442
msgid "Enter a unique alias: e.g. WPvividGoogle Drive-001"
msgstr "Inserisci un alias univoco: ad esempio WPvividGoogle Drive-001"

#: includes/customclass/class-wpvivid-google-drive.php:364
msgid "Authenticate with WPvividGoogle Drive"
msgstr "Autenticati con WPvividGoogle Drive"

#: includes/customclass/class-wpvivid-google-drive.php:369
msgid "Click the button to get WPvividGoogle authentication and add it to the storage list below."
msgstr "Clicca il pulsante per ottenere il codice di autenticazione di WPvividGoogle e aggiungerlo alla lista degli storage qui di seguito."

#: includes/customclass/class-wpvivid-one-drive.php:240
msgid "You have authenticated the Microsoft OneDrive account as your remote storage."
msgstr "Hai autenticato l'account Microsoft OneDrive come tuo storage remoto."

#: includes/customclass/class-wpvivid-one-drive.php:260
msgid "Microsoft OneDrive"
msgstr "Microsoft OneDrive"

#: includes/customclass/class-wpvivid-one-drive.php:272
msgid "Please read <a target=\"_blank\" href=\"https://wpvivid.com/privacy-policy\" style=\"text-decoration: none;\">this privacy policy</a> for use of our Microsoft OneDrive authorization app (none of your backup data is sent to us)."
msgstr "Si prega di leggere <a target=\"_blank\" href=\"https://wpvivid.com/privacy-policy\" style=\"text-decoration: none;\">questa informativa sulla privacy</a> per l'utilizzo della nostra applicazione di autorizzazione a Microsoft OneDrive (nessuno dei dati di backup viene inviato a noi)."

#: includes/customclass/class-wpvivid-one-drive.php:275
#: includes/customclass/class-wpvivid-one-drive.php:403
msgid "Enter Your Microsoft OneDrive Information"
msgstr "Inserisci le informazioni del tuo account Microsoft OneDrive"

#: includes/customclass/class-wpvivid-one-drive.php:282
#: includes/customclass/class-wpvivid-one-drive.php:410
msgid "Enter a unique alias: e.g. OneDrive-001"
msgstr "Inserisci un alias univoco: ad esempio OneDrive-001"

#: includes/customclass/class-wpvivid-one-drive.php:332
msgid "Authenticate with Microsoft OneDrive"
msgstr "Autenticati con Microsoft OneDrive"

#: includes/customclass/class-wpvivid-one-drive.php:337
msgid "Click the button to get Microsoft authentication and add it to the storage list below."
msgstr "Clicca il pulsante per ottenere il codice di autenticazione di Microsoft e  aggiungerlo alla lista degli storage qui di seguito."

#: includes/customclass/class-wpvivid-s3compat.php:403
msgid "DigitalOcean Spaces"
msgstr "DigitalOcean Spaces"

#: includes/customclass/class-wpvivid-s3compat.php:412
#: includes/customclass/class-wpvivid-s3compat.php:527
msgid "Enter Your DigitalOcean Spaces Account"
msgstr "Inserisci l'account dei tuoi Spazi DigitalOcean"

#: includes/customclass/class-wpvivid-s3compat.php:420
#: includes/customclass/class-wpvivid-s3compat.php:535
msgid "Enter a unique alias: e.g. DOS-001"
msgstr "Inserisci un alias univoco: ad esempio DOS-001"

#: includes/customclass/class-wpvivid-s3compat.php:432
#: includes/customclass/class-wpvivid-s3compat.php:547
msgid "DigitalOcean Spaces access key"
msgstr "DigitalOcean Spaces access key"

#: includes/customclass/class-wpvivid-s3compat.php:437
#: includes/customclass/class-wpvivid-s3compat.php:552
msgid "Enter your DigitalOcean Spaces access key"
msgstr "Inserisci l'access key degli Spazi DigitalOcean"

#: includes/customclass/class-wpvivid-s3compat.php:444
#: includes/customclass/class-wpvivid-s3compat.php:559
msgid "DigitalOcean Spaces secret key"
msgstr "DigitalOcean Spaces secret key"

#: includes/customclass/class-wpvivid-s3compat.php:449
#: includes/customclass/class-wpvivid-s3compat.php:564
msgid "Enter your DigitalOcean Spaces secret key"
msgstr "Inserisci la secret key dei tuoi Spazi DigitalOcean"

#: includes/customclass/class-wpvivid-s3compat.php:456
#: includes/customclass/class-wpvivid-s3compat.php:571
msgid "Space Name(e.g. test)"
msgstr "Nome dello spazio(ad esempio test)"

#: includes/customclass/class-wpvivid-s3compat.php:461
#: includes/customclass/class-wpvivid-s3compat.php:576
msgid "Enter an existed Space to create a custom backup storage directory."
msgstr "Inserisci uno Spazio esistente per creare una directory di archiviazione di backup personalizzata."

#: includes/customclass/class-wpvivid-s3compat.php:473
#: includes/customclass/class-wpvivid-s3compat.php:588
msgid "Customize the directory where you want to store backups within the Space."
msgstr "Personalizzare la directory in cui si desidera archiviare i backup all'interno dello Spazio."

#: includes/customclass/class-wpvivid-s3compat.php:480
#: includes/customclass/class-wpvivid-s3compat.php:595
msgid "region.digitaloceanspaces.com"
msgstr "region.digitaloceanspaces.com"

#: includes/customclass/class-wpvivid-s3compat.php:485
#: includes/customclass/class-wpvivid-s3compat.php:600
msgid "Enter the DigitalOcean Endpoint for the storage"
msgstr "Inserisci l'Endpoint di DigitalOcean per l'archiviazione"

#: includes/customclass/class-wpvivid-s3compat.php:512
msgid "Click the button to connect to DigitalOcean Spaces storage and add it to the storage list below."
msgstr "Clicca il pulsante per connetterti allo storage di DigitalOcean Space e di aggiungerlo alla lista degli storage qui di seguito."

#: includes/customclass/class-wpvivid-sftpclass.php:38
msgid "SFTP"
msgstr "SFTP"

#: includes/customclass/class-wpvivid-sftpclass.php:48
#: includes/customclass/class-wpvivid-sftpclass.php:163
msgid "Enter Your SFTP Account"
msgstr "Inserisci il Tuo SFTP Account"

#: includes/customclass/class-wpvivid-sftpclass.php:56
#: includes/customclass/class-wpvivid-sftpclass.php:171
msgid "Enter a unique alias: e.g. SFTP-001"
msgstr "Inserisci un alias univoco: ad esempio, SFTP-001"

#: includes/customclass/class-wpvivid-sftpclass.php:68
#: includes/customclass/class-wpvivid-sftpclass.php:183
msgid "Server Address"
msgstr "Indirizzo del Server"

#: includes/customclass/class-wpvivid-sftpclass.php:73
#: includes/customclass/class-wpvivid-sftpclass.php:188
msgid "Enter the server address."
msgstr "Inserisci l'indirizzo del server."

#: includes/customclass/class-wpvivid-sftpclass.php:80
#: includes/customclass/class-wpvivid-sftpclass.php:195
msgid "User Name"
msgstr "Nome Utente"

#: includes/customclass/class-wpvivid-sftpclass.php:85
#: includes/customclass/class-wpvivid-sftpclass.php:200
msgid "Enter the user name."
msgstr "Inserisci il nome utente."

#: includes/customclass/class-wpvivid-sftpclass.php:92
#: includes/customclass/class-wpvivid-sftpclass.php:207
msgid "User Password"
msgstr "Password Utente"

#: includes/customclass/class-wpvivid-sftpclass.php:97
#: includes/customclass/class-wpvivid-sftpclass.php:212
msgid "Enter the user password."
msgstr "Inserisci la password utente."

#: includes/customclass/class-wpvivid-sftpclass.php:104
#: includes/customclass/class-wpvivid-sftpclass.php:219
msgid "Port"
msgstr "Porta"

#: includes/customclass/class-wpvivid-sftpclass.php:109
#: includes/customclass/class-wpvivid-sftpclass.php:224
msgid "Enter the server port."
msgstr "Inserisci la porta del server."

#: includes/customclass/class-wpvivid-sftpclass.php:116
#: includes/customclass/class-wpvivid-sftpclass.php:231
msgid "Absolute path must exist(e.g. /var)"
msgstr "Un percorso assoluto deve esistere(ad esempio, /var)"

#: includes/customclass/class-wpvivid-sftpclass.php:121
#: includes/customclass/class-wpvivid-sftpclass.php:236
msgid "Enter an absolute path and a custom subdirectory (optional) for holding the backups of current website. For example, /var/customfolder/"
msgstr "Inserisci un percorso assoluto e una sottodirectory personalizzata (opzionale) per tenere i backup del sito web corrente. Per esempio, /var/cartellapersonalizzata/"

#: includes/customclass/class-wpvivid-sftpclass.php:148
msgid "Click the button to connect to SFTP server and add it to the storage list below."
msgstr "Clicca il pulsante per connetterti al server SFTP e aggiungerlo alla lista degli storage qui di seguito."

languages/wpvivid-backuprestore-bg_BG.mo000064400000255145151327705670014375 0ustar00�����p)q)
~)��)7*>*zU*�*
�*��*[�+84,6m,�,C�.//:/hQ/+�/N�/Y50��0V-1K�1?�1G2NX2V�2��2�3V�3YD4�4-�4#�45
5�5�5�6�7�7�7�7/�7;!8	]8 g8�8�8�8u�8~F9��9�:
�:�:�:$�:�;��;P<W<f<w<�<�<�<�<
�<
�<�<�<�<=�=�=B�=��=�>	�>n�>Ie?��?/�@1�@4�@;ATA'eA�A�A
�AH�AFBVUB`�BO
CP]CT�CSDVWD%�D�D�DEE$E9ENETEkE�E2�E8�EFF"F)F28FkFwF �F�FJ�FIG[G"aG�G�G�G�G�G�G�GS�GMHaH�Hw�HFI^I"gI�I�I�I�I
�I�I�I�I7
J%BJ`hJ�J&�J
K,K#CK)gK�K(�K"�K&�K+L'HL#pL��L�%MD�MC�M#6N/ZN�N�N�N�N�NO O ?O)`O)�O �ON�OS$PxP'P�P.�P3�P$Q%6Q\QlQ{Q
�Q*�Q9�QR	RRR];RE�R#�R	S

SS-S#CS(gS�S
�S�S
�S�S�S8�STT%TzT"�T"�TX�TZ0U�U�U��U�XV4NW�X�X*�X9�Xc�X�bYeSZ��Zj\�\�\�\�\��\\n]`�],^G1^y^
}^
�^	�^
�^�^�^�^
�^�^�^___�$_	`	``+`�=`a
,a7aGaOanaI�a�a�a
�a	�a	bbb�bv�b Rc
sc:�c3�ct�ced~d �d'�d(�dee?e"We�ze#�e f�<f�g��g �h�h�h
�h�h
ii4&i/[iC�i �i�i
j4j	HjRj�Zj)�j5k5Ikk�k�k�k�k�k�k�k%l4=lArl�l�l��l#�m�m �mS�mj6n�n�n�n�n�n�n�n	o&o=o'So{o�o�o	�o	�o6�o�o+�o
&p1p>pYpxp��p.qGq
Vqdq"|q�q+�qH�q<sZs3ps�s�s�s�s~�s"at6�t�t�t�t,�tuu u/u?u@Ou	�u
�u�uE�uH�u�Av\�vD$w-iw"�w��w|x�x"�x#�x�x6y&=y�dyr�y:Y{T�{*�{N|Lc|(�|
�|�}\�~FT-�(���I�YɀX#�y|���
�R�Tc����x���w���v*�������I���E��d�'�	�$�0�
@�N�U�	g�	q�
{�!��u��!�V:�������և1߇2�hD�H����'�4�E�X�a�nh�"׉B��G=�M��-ӊ0�2�>��O������*�G�*V� ��%���Ȏ
ŏ ӏ�-��*��;���|ؓ�U��֔x���
)��4����jF�����n�L��^������k�������H�0�����+��|��Q>��������%��ѧ'�'�=�W�Xw�uЪ
F�FQ�)��&«*�����Yҭ,,�&Y�'��/��2د�����
��)�,�FL�7��˲4� �$6�[�.y�����ӳ
�*�����
��˷�ڷ����h�_�bb�bŻ�(�,ż^�9Q�3�� ��w��X��
��ؿ����f��&�������X��7��J�']���;��0���7�DF�4��X����������'��z���%��L��?#��c������B��*��:�L�*U���U��7����+��G��I��_�����@��(���'�@�+M�Ey�N��6�zE�Z����3��S%�Cy�,��>��L)�+v�T��N��RF�Z��S��OH�?���������q�N�Oa�:��$��2�)D�7n�=��B��:'�^b�S��O��e���
��P��D�e]�s��T7�V��1��,�.B�-q����z'�������2��������Zr���!��'�1,�C^�T��?��7�S�&i�!�������]��m�6�AE�C������V���;0�*l�{���. ��O�x���N���������FN�)��;����"�*7��b�������A�'J�r�!����<�<�D�3U���������3���
�
�V�!G��i�C��;Mm6|:���}�'��!��4E�36_Sn�"?8OxH�T`f����6{	5�	@�
6)�`���t2�.7!Ln!}-�j�d8��M /n�o�!8�L}2x�x))�?�D*Q|>��E�^/v��2�?�7�S1�B�����4���O�
8F,S�M�9�q(�&�.�
_$�F���3 aJ h� ![1"�"�"9�"^#b#u#��#��&3"'fV'�' �';�'(*(&S(`z)��)h*.q**�*Y�*%+8+9J+J�+�+~�+n,,�,m�,o#-�-��.s[/Z�/E*0Ip0@�1"�1[2Dz2A�2d3Nf3��3��4��7�8J�8��8w9R�9aJ:X�<�?��?L\@W�@A�B��B�:C�C'�D
E�E��E|oFC�G0HEH.JIyI�J�J�J��JO<K��K4LyNM�M�M�MN N()NRN!^N'�NH�N��N�O��O�P4�P+�PQt,Q�Q�!R��R0�S]�S9T'PTxT�T��TOzU��U�WV��VO~Wb�W1XJXhX�Y�Y�Y2�YEZFZA�W�U�loB���a��3�5�$�Vi�|�3m�jP�Wc���:b�P��[n�D�v�
���Q� ;�+����b�&'I�?CnD�6��Fy����V`(^"��p����a��dz0�	=	���1"�����0�E��Ep=�)tht�����B�@�{�L����r�;�>�
kA��]���q��N��Y����6��9���(smJU[��wo�<���d�$:��%}�_�-.ls+��ZuFj&OR}�SQ��,�X�>/����'yKeO�����.4-\,��H8
�������|���^hxC\H�z��i���~�����J��f�����5��!��M
�N�����X��%1���*?7�#�g�������#��G��@���R���7]K29/�Yxc�Z{�����������f���u�����*�!��wT��T���G_<r�v2��kq���S~ e�)����84I������g������ML��` < Pre page  Next page > %d backup tasks have been completed. Please switch to <a href="#" onclick="wpvivid_click_switch_page('wrap', 'wpvivid_tab_log', true);">Log</a> page to check the details.%s ago&mdash; Select &mdash;*No credit card needed. Trial starts with the Free Trial plan with 2 sites. You can choose a plan at the end of the trial.+ Add another schedule- Select -->If backups are stored in remote storage, our plugin will retrieve the backup to your web server first. This may take a little time depending on the size of backup files. Please be patient. Then you can download them to your PC.->If backups are stored in web server, the plugin will list all relevant files immediately.1. Create and download backups for a specific child site1. Download a backup in backups list to your computer.1. In order to successfully complete the migration, you'd better deactivate <a href="https://wpvivid.com/best-redirect-plugins.html" target="_blank" style="text-decoration: none;">301 redirect plugin</a>, <a href="https://wpvivid.com/8-best-wordpress-firewall-plugins.html" target="_blank" style="text-decoration: none;">firewall and security plugin</a>, and <a href="https://wpvivid.com/best-free-wordpress-caching-plugins.html" target="_blank" style="text-decoration: none;">caching plugin</a> (if they exist) before transferring website.1. Visit Key tab page of WPvivid backup plugin of destination site.12 Hours12Hours2. Generate a key by clicking Generate button and copy it.2. Please migrate website with the manual way when using <strong>Local by Flywheel</strong> environment.2. Set backup schedules for all child sites2. Upload the backup to destination site. There are two ways available to use:2.1 Upload the backup to the upload section of WPvivid backup plugin in destination site.2.2 Upload the backup with FTP client to backup directory %s in destination site, then click <strong>Scan uploaded backup or received backup</strong> button.3. Go back to this page and paste the key in key box below. Lastly, click Save button.3. Once done, the backup appears in backups list. Then, restore the backup.3. Set WPvivid Backup Free and Pro settings for all child sites4. Install, claim and update WPvivid Backup Pro for child sites in bulk5. Set up remote storage for child sites in bulk (for WPvivid Backup Pro only)<strong>Retrying </strong>%s<strong> times when encountering a time-out error</strong><strong>Tips: </strong>The unstable connection between sites could cause a failure of files transfer. In this case, uploading backups to destination site is a good alternative to the automatic website migration.A backup type is required.A name to help you identify the storage if you have multiple remote storage connected.A task is already running. Please wait until the running task is complete, and try again.About backup downloadAbsolute path must exist(e.g. /home/username)Absolute path must exist(e.g. /var)ActionActionsAdjust <a href="https://wpvivid.com/wpvivid-backup-plugin-advanced-settings.html" target="_blank">Advanced Settings</a> for higher task success rate.Adjust this value to apply for a temporary PHP memory limit for WPvivid backup plugin in restore process. We set this value to 256M by default. Increase the value if you encounter a memory exhausted error. Note: some web hosting providers may not support this.Adjust this value to apply for a temporary PHP memory limit for WPvivid backup plugin to run a backup. We set this value to 256M by default. Increase the value if you encounter a memory exhausted error. Note: some web hosting providers may not support this.Advanced ScheduleAdvanced SettingsAll AuthorsAll CategoriesAll backups will be uploaded to this directory.Always send an email notification when a backup is completeAmazon S3Amazon S3 Bucket Name(e.g. test)Amazon S3 access keyAmazon S3 secret keyAn email address is required.Are you sure to remove the selected backups? These backups will be deleted permanently from your hosting (localhost).Are you sure to remove this backup? This backup will be deleted permanently from your hosting (localhost) and remote storages.As Amazon S3 and DigitalOcean Space have upgraded their connection methods, please delete the previous connections and re-add your Amazon S3/DigitalOcean Space accounts to make sure the connections work.Ask For A 40% OFF DiscountAssign authorAuthenticate with DropboxAuthenticate with Google DriveAuthenticate with Microsoft OneDriveAuthentication failed, the client_secrets.json file is missing. Please make sure the client_secrets.json file is in wpvivid-backuprestore\includes\customclass directory.Authentication failed, the format of the client_secrets.json file is incorrect. Please delete and re-install the plugin to recreate the file.AuthorAuto-MigrationBack Up ManuallyBack to Import PageBacking up %s finished.BackupBackup & RestoreBackup CacheBackup FolderBackup NowBackup ScheduleBackup:BackupsBasicBeing subjected to mechanisms of PHP, a scheduled backup task for your site will be triggered only when the site receives at least a visit at any page.Calculate SizesCalculate the size of files, folder and database before backing upCalculating the size of files, folder and database timed out. If you continue to receive this error, please go to the plugin settings, uncheck 'Calculate the size of files, folder and database before backing up', save changes, then try again.CancelChangeLogCheck the option to use Amazon S3 Standard-Infrequent Access (S3 Standard-IA) storage class for data transfer.Check the option to use Amazon S3 server-side encryption to protect data.Cheers! WPvivid Backup plugin has restored successfully your website. If you found WPvivid Backup plugin helpful, a 5-star rating would be highly appreciated, which motivates us to keep providing new features.Choose an export from your computer to import: Choose at least one storage location for backups.Choose at least one type of junk files for deleting.Choose one storage from the list to be the default storage.Choose post typeChoose the content you want to transferChoose what to exportChoose what to migrateChunk SizeClass PclZip is not detected. Please update or reinstall your WordPress.Click 'Export' button to save WPvivid settings on your local computer.Click the button to connect to Amazon S3 storage and add it to the storage list below.Click the button to connect to DigitalOcean Spaces storage and add it to the storage list below.Click the button to connect to FTP server and add it to the storage list below.Click the button to connect to SFTP server and add it to the storage list below.Click the button to get Dropbox authentication and add it to the storage list below.Click the button to get Google authentication and add it to the storage list below.Click the button to get Microsoft authentication and add it to the storage list below.Click the button to save the changes.Clone then TransferComment the export (optional)Comment the export: CommentsCompress Files EveryCompress and ArchiveCountCreate a Manual BackupCreate a restore pointCreate a staging siteCreate a sub-directory staging site with one-clickCreating scheduled tasks failed. Please try again later.Current PageCurrent Version:CustomCustom ContentCustom Directory (leading cloud storage providers)Custom PathCustom TimezoneCustom content for each scheduleCustom start time of scheduleCustomize the directory where you want to store backups within the Bucket.Customize the directory where you want to store backups within the Space.DailyDatabase + Files (WordPress Files)Database Size:Database access method.DateDebugDeleteDelete Exported Files In FolderDelete the selected backupsDetails are shown above. The importer will now try again with a different parser...DigitalOcean SpacesDigitalOcean Spaces access keyDigitalOcean Spaces secret keyDisplay domain(url) of current site in backup name. (e.g. domain_wpvivid-5ceb938b6dca9_2019-05-27-07-36_backup_all.zip)Display the individual sections according to user roles & capabilitiesDownloadDownload WPvivid Backup for MainWPDrop files hereDropboxEmail Reports:EmptyEmpty post idEnable Incremental BackupEnable backup scheduleEnable email reportEnable optimization mode for web hosting/shared hostingEnable the option when backup failed.Enabling this option can improve the backup success rate, but it will take more time for backup.Enter Your Amazon S3 AccountEnter Your DigitalOcean Spaces AccountEnter Your Dropbox InformationEnter Your FTP AccountEnter Your Google Drive InformationEnter Your Microsoft OneDrive InformationEnter Your SFTP AccountEnter a unique alias: e.g. Amazon S3-001Enter a unique alias: e.g. DOS-001Enter a unique alias: e.g. Dropbox-001Enter a unique alias: e.g. Google Drive-001Enter a unique alias: e.g. OneDrive-001Enter a unique alias: e.g. SFTP-001Enter an absolute path and a custom subdirectory (optional) for holding the backups of current website. For example, /home/username/customfolderEnter an absolute path and a custom subdirectory (optional) for holding the backups of current website. For example, /var/customfolder/Enter an existed Bucket to create a custom backup storage directory.Enter an existed Space to create a custom backup storage directory.Enter an unique alias: e.g. FTP-001Enter the DigitalOcean Endpoint for the storageEnter the FTP server password.Enter the FTP server.Enter the server address.Enter the server port.Enter the user name.Enter the user password.Enter your Amazon S3 access key.Enter your Amazon S3 secret key.Enter your DigitalOcean Spaces access keyEnter your DigitalOcean Spaces secret keyEnter your FTP server user name.Error occurred while parsing the request data. Please try to run backup again.Error occurred while parsing the request data. Please try to run export task again.Error:Exclude the files which are larger thanExportExport %s of all authors or a specific author.Export %s of all categories or a specific category.Export %s published after this date.Export %s published before this date.Export & ImportExport ContentExport and DownloadExport error:Export posts or pages with images in bulk.Exported files will be temporarily stored in %s directoryFTPFTP loginFTP passwordFTP server (server's port 21)Fail to delete the remote storage, can not retrieve the storage infomation. Please try again.Failed to get the remote storage information. Please try again later.Fetching attachments is not enabledFile NameFile Size:File not exist file:File not exist, file:File not found. please retry again.File size not match. please retry again.Filter Posts/PagesFirst pageFortnightlyFreelancerGeneral SettingsGenerateGetting backup directory failed. Please try again later.Google DriveHighlighted icon illuminates that you have choosed a remote storage to store backupsHow to get a site key?How to get an AmazonS3 access key.How to get an AmazonS3 secret key.How to migrate Wordpress site manually to a new domain(site) with WPvivid backup plugin?How to restore your website from a backup(scheduled, manual, uploaded and received backup)How-toI am using:If you are a MainWP user, you can set up and control WPvivid Backup Free and Pro for every child site directly from your MainWP dashboard, using our WPvivid Backup for MainWP extension.If you didn’t configure SMTP on your site, click the button below to download the relevant information (website info and error logs) to your PC when you are encountering some errors. Sending the files to us will help us diagnose what happened.If you have configured SMTP on your site, enter your email address and click the button below to send us the relevant information (website info and errors logs) when you are encountering errors. This will help us figure out what happened. Once the issue is resolved, we will inform you by your email address.ImportImport SettingImport posts or pages with images in bulk.Imported files will be temporarily stored in directory %sImporting the json file can help you set WPvivid's configuration on another wordpress site quickly.In order to allow another site to send a backup to this site, please generate a key below. Once the key is generated, this site is ready to receive a backup from another site. Then, please copy and paste the key in sending site and save it.In order to execute the scheduled backups properly, please set the DISABLE_WP_CRON constant to false.In order to use this function, please install a <strong><a target="_blank" href="https://wpvivid.com/8-best-smtp-plugins-for-wordpress.html" style="text-decoration: none;">WordPress SMTP plugin</a></strong> of your preference and configure your SMTP server first. This is because WordPress uses the PHP Mail function to send its emails by default, which is not supported by many hosts and can cause issues if it is not set properly.Include/exclude files/foldersIncremental BackupIncremental Backup ScheduleInvalid email addressInvalid file typeIt is recommended to choose PDO option if pdo_mysql extension is installed on your server, which lets you backup and restore your site faster.It will cause a higher CPU Usage and is recommended in a VPS/ dedicated hosting environment.It will cause a lower CPU Usage and is recommended in a web hosting/ shared hosting environment.JunkKeep storing the backups in localhost after uploading to remote storageKeyLast Backup: Last ModifiedLast pageLearn moreLocal Storage Directory:Local storage directory:LogLog File NameLog TypeLogsMainWPMaybe LaterMedia Files SizeMerge all the backup files into single package when a backup completes. This will save great disk spaces, though takes longer time. We recommended you check the option especially on sites with insufficient server resources.Method 1.Method 2.Microsoft OneDriveMigrate WordPressMigration is complete and htaccess file is replaced. In order to successfully complete the migration, you'd better reinstall 301 redirect plugin, firewall and security plugin, and caching plugin if they exist.Migration via remote storageMigration:Missed scheduleMonthlyMore post types coming soon...My web hosting provider is:Name your folder, this folder must be writable for creating backup files.Network Connection:NeverNext Backup: Next StepNext pageNote:Note: Note: The files you want to upload must be a backup created by WPvivid backup plugin. Make sure that uploading every part of a backup to the directory if the backup is split into many partsOnce checked, all this sites backups sent to a remote storage destination will be uploaded to this storage by default.Only Archive without compressingOnly DatabaseOnly letters (except for wpvivid) and numbers are allowed.Only send an email notification when a backup failsOr you can use ftp to upload the export to the directory %s. Then click the button below to scan the file to import.Overwrite existing pagesPHP Memory Limit for backupPHP Memory Limit for restorationPHP script execution timeout for backupPHP script execution timeout for restorePagePath:Please choose one storage to save your backups (remote storage)Please describe your problem here.Please do not close the page or switch to other pages when a restore task is running, as it could trigger some unexpected errors.Please enter a valid email address.Please paste the key below.Please read <a target="_blank" href="https://wpvivid.com/privacy-policy" style="text-decoration: none;">this privacy policy</a> for use of our Dropbox authorization app (none of your backup data is sent to us).Please read <a target="_blank" href="https://wpvivid.com/privacy-policy" style="text-decoration: none;">this privacy policy</a> for use of our Google Drive authorization app (none of your backup data is sent to us).Please read <a target="_blank" href="https://wpvivid.com/privacy-policy" style="text-decoration: none;">this privacy policy</a> for use of our Microsoft OneDrive authorization app (none of your backup data is sent to us).Please select at least one item.PortPostPost TypesPremiumPrevious pagePro Version FeaturesPro feature: Add another email address to get reportPro feature: Change the FTP default port numberPro feature: Create a directory for storing the backups of the sitePro feature: Retain more backupsPro feature: learn moreProgress: Publish a staging site to a live site with one-clickPublishedRate UsRead <a href="https://wpvivid.com/troubleshooting-issues-wpvivid-backup-plugin" target="_blank">Troubleshooting page</a> for faster solutions.Reading the log failed. Please try again.Ready to backup. Progress: 0%, running time: 0second.Ready to export. Progress: 0%, running time: 0second.Remote StorageRemote Storage AliasRemote Storage Directory:Remote Storage:RemoveRemove out-of-date backupsRestoreRestore Your Site from a BackupRestore a website from remote storageRestore and keep the original domain (URL) unchangedRestore and replace the original domain (URL) with %s (migration)Restore backup from:Restore completed successfully.Restore function will replace the current site's themes, plugins, uploads, database and/or other content directories with the existing equivalents in the selected backup.Restore what you want from a backupRestore:Retrieve the backup to localhostRetrieving the backup information failed while showing log. Please try again later.Retrieving the backup(s) information failed while deleting the selected backup(s). Please try again later.Roles & Capabilities (add-on):RollbackSFTPSTART 14-DAY FREE TRIALSample:SaveSave Backups to LocalSave ChangesSave backups on localhost (web server)Scan Uploaded ExportsScan uploaded backup or received backupScheduleSchedule SettingsSchedule Status: Schedule:ScheduledScheduled job will start at <strong>UTC</strong> time:SearchSearch for %s according to the above rules.Select AllSelect FilesSelect an existing author:Send Backup to Remote Storage:Send Debug Information to UsSend backups to remote storage (choose this option, the local backup will be deleted after uploading to remote storage completely)Send email reports to multiple email addressesServer AddressServer Time: Server-side encryption.Set as the default remote storage.SettingsShow WPvivid backup plugin on top admin barSome web hosting providers limit large zip files (e.g. 200MB), and therefore splitting your backup into many parts is an ideal way to avoid hitting the limitation if you are running a big website.  Please try to adjust the value if you are encountering backup errors. If you use a value of 0 MB, any backup files won't be split.Sorry, this file type is not permitted for security reasons.Space Name(e.g. test)Special optimization for web hosting/shared hostingSpeed:Staging (add-on):Start backing up %s.Start to ImportStep One: In the backup list, click the 'Restore' button on the backup you want to restore. This will bring up the restore tabStep Three: Click 'Restore' buttonStep Two: Choose an option to complete restore, if anyStorageStorage EditStorage ProviderStorage class: Standard (infrequent access).StoragesSupport:Task time out.Task timed out.Temporary FilesTemporary Files are created by wpvivid when restoring a website.TerminateTest EmailTest and AddThe %s extension is not detected. Please install the extension first.The %s extensions are not detected. Please install the extensions first.The action is irreversible! It will remove all backups are out-of-date (including local web server and remote storage) if they exist.The backup is stored on the remote storage, click on the button to download it to localhost.The backup will be canceled after backing up the current chunk ends.The backups will be uploaded to %s directory.The export task is not responding.The feature can help you transfer a Wordpress site to a new domain(site). It would be a convenient way to migrate your WP site from dev environment to live server or from old server to the new.The importing file infoThe key will expire in The last backup message not found.The local storage path is required.The log not found.The maximum execution time for PHP script is required.The maximum zip file size is required.The pdo_mysql extension is not detected. Please install the extension first or choose wpdb option for Database connection method.The plugin detects automatically either site restoration or migration (replacing the domain name) based on the current domain name. If the domain name in backup file is same as the current one, it starts restoring. On the contrary, restoring backup means to replace with the current domain name. The precondition is that the backup is created by version 0.9.21 or later.The restore file not found. Please verify the file exists.The selected file is not the setting file for WPvivid. Please upload the right file.The selected junk flies have been deleted.The settings are only for manual backup, which won't affect schedule settings.The simplexml extension is not detected. Please install the extension first.The size for excluded files is required.The time-out is not your server PHP time-out. With the execution time exhausted, our plugin will shut the process of backup down. If the progress of backup encounters a time-out, that means you have a medium or large sized website, please try to scale the value bigger.The time-out is not your server PHP time-out. With the execution time exhausted, our plugin will shut the process of restore down. If the progress of restore encounters a time-out, that means you have a medium or large sized website, please try to scale the value bigger.There are two ways available to send us the debug information. The first one is recommended.There is no default remote storage configured. Please set it up first.There was an error when reading this WXR fileThis backup can only be deleted manuallyThis backup is locked, are you sure to remove it? This backup will be deleted permanently from your hosting (localhost) and remote storages.This does not appear to be a WXR file, missing/invalid WXR version numberThis request might delete the backup being downloaded, are you sure you want to continue?This request will delete the backup being downloaded, are you sure you want to continue?This will contain all of your posts, pages, comments, terms and images (original images, featured images and thumbnails).Ticket 7x24 supportTips:Tips: Click the button below to scan all uploaded or received backups in directoryTips: For security reason, please choose an appropriate expiration time for the key.To properly display the imported content, please make sure that the importing and exporting sites have the same environment, for example, same theme or pages built with the same page builder.Too many resumption attempts.Total Size:Transfer succeeded. Please scan the backup list on the destination site to display the backup, then restore the backup.TroubleshootingTry to select fewer items when you are facing a shortage of server resources (typically presented as a timeout error).Type:Type: UltimateUnable to delete the locked backup. Please unlock it first and try again.Unable to open the log file.Unable to send email. Please check the configuration of email server.Uncheck the option to use FTP active mode when transferring files. Make sure the FTP server you are configuring supports the active FTP mode.Uncheck this to enable FTP active mode.UnlimitedUnpublishedUp to 100 sitesUp to 3 sitesUploadUpload and ImportUploaded:User NameUser PasswordUser's email address is required.Using the option will ignore the file larger than the certain size in MB when backing up, '0' (zero) means unlimited.WASABI/Pcloud (Only Pro)WPDB option has a better compatibility, but the speed of backup and restore is slower.WPvivid Backup PluginWPvivid Backup SettingsWPvivid support email:Warning:Warning: An alias for remote storage is required.Warning: The alias already exists in storage list.Warning: There is no default remote storage available for the scheduled backups, please set up it first.We also offer a 40% off discount on WPvivid Backup Pro for MainWP users.Web Server Directory:Web-server disk space in use by WPvividWebsite Info KeyWebsite Info ValueWebsitesWeeklyWith this option checked, Pages/posts already existing will be overwritten with the updated ones in an import.WordPress Files (Exclude Database)You have authenticated the Dropbox account as your remote storage.You have authenticated the Google Drive account as your remote storage.You have authenticated the Microsoft OneDrive account as your remote storage.You have successfully added a remote storage.You have successfully updated the storage alias.Your email:backups retainede.g.  if you choose a chunk size of 2MB, a 8MB file will use 4 chunks. Decreasing this value will break the ISP's transmission limit, for example:512KBlogsnew featureregion.digitaloceanspaces.comrename directoryrestore failed error unknownrunning time: Project-Id-Version: wpvivid backup plugin
Last-Translator: 刘赓 <420723111@qq.com>
Language-Team: Български
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
X-Poedit-KeywordsList: __;_e;esc_attr;esc_attr_e
X-Poedit-Basepath: ..
Plural-Forms: nplurals=2; plural=n != 1;
X-Poedit-SourceCharset: utf-8
Report-Msgid-Bugs-To: 
POT-Creation-Date: 2020-06-09 14:13+0000
PO-Revision-Date: 2020-07-27 18:41+0800
Language: bg_BG
X-Generator: Poedit 2.3.1
X-Loco-Version: 2.4.0; wp-5.4.1
X-Poedit-SearchPath-0: .
 < Преди страница   Следваща страница > %d архивиране са завършени. Преминете към <a href="#" onclick="wpvivid_click_switch_page('wrap', 'wpvivid_tab_log', true);">логаритмичен</a> страницата, за да проверите данните.преди %s&mdash; Изберете &mdash;*Не е необходима кредитна карта. Пробният период започва с плана за безплатен пробен период с 2 обекта. Можете да изберете план в края на процеса.+ Добавяне на друг график- Избери -->Ако архивите се съхраняват в отдалечено хранилище, нашата приставка ще извлече архива на вашия уеб сървър първо. Това може да отнеме малко време в зависимост от размера на архивните файлове. Моля те, бъди търпелив. След това можете да ги изтеглите на компютъра си.->Ако архивите се съхраняват в уеб сървъра, приставката ще изброи всички съответни файлове незабавно.1. Създаване и изтегляне на резервни копия за конкретен дъщерен сайт1. Изтеглете резервно копие в списъка с резервни копия на компютъра си.1. За да завършите успешно миграцията, по-добре деактивирайте <a href="https://wpvivid.com/best-redirect-plugins.html" target="_blank" style="text-decoration: none;">301 приставка за пренасочване,</a> <a href="https://wpvivid.com/8-best-wordpress-firewall-plugins.html" target="_blank" style="text-decoration: none;">защитна стена и приставка за сигурност</a>и <a href="https://wpvivid.com/best-free-wordpress-caching-plugins.html" target="_blank" style="text-decoration: none;">кеширане плъгин</a> (ако те съществуват) преди прехвърлянето на уебсайта.1. Посетете ключови раздел на WPvivid резервна плъгин на целевия сайт.12 часа12Часа2. Генерирайте ключ, като кликнете върху бутона "Генерирам" и го копирайте.2. Моля мигрирайте уебсайт с ръчното използване на <strong>local by Flywheel</strong> среда.2. Задайте графици за архивиране за всички дъщерни сайтове2. Качете резервното копие на мястото на местоназначението. Има два начина, които могат да се използват:2.1 Качване на резервно копие в секцията за качване на WPvivid резервна плъгин в целевия сайт.2.2 Качване на резервно копие с FTP клиент в директорията за архивиране %s в целевия сайт, след което щракнете върху <strong>Сканиране качено архивиране или получи бутон за архивиране</strong>.3, Върнете се на тази страница и поставете ключа в полето по-долу. Накрая, щракнете върху Бутона Запазване.3. След като направите, архивирането се появява в списъка с резервни копия. След това възстановете архива.3. Задайте WPvivid Архивиране Безплатни и Pro настройки за всички сайтове за деца4. Инсталиране, претенции и актуализация WPvivid Backup Pro за дъщерни сайтове в насипно състояние5. Настройте отдалечено съхранение за деца сайтове в насипно състояние (за WPvivid Backup Pro само)<strong>Повторен опит на </strong>%s<strong> пъти при възникване на грешка за изчакване</strong><strong>Съвети:</strong> Нестабилната връзка между сайтове може да доведе до отказ на прехвърляне на файлове. В този случай качването на резервни копия на сайта местоназначение е добра алтернатива на автоматичната миграция на уебсайта.Изисква се тип архивиране.Име, което да ви помогне да идентифицирате хранилището, ако сте свързани няколко отдалечени места за съхранение.Вече се изпълнява задача. Изчакайте, докато изпълняваната задача завърши и опитайте отново.За изтеглянето на архивАбсолютен път трябва да съществува (напр. /начало/потребителско име)Абсолютен път трябва да съществува (напр. /var)ДействиеДействияРегулирайте <a href="https://wpvivid.com/wpvivid-backup-plugin-advanced-settings.html" target="_blank">разширените настройки</a> за по-висока скорост на успеваемост на задачите.Коригирайте тази стойност, за да се приложи временно ограничение на PHP паметта за WPvivid резервна добавка в процеса на възстановяване. Тази стойност е 256M по подразбиране. Увеличете стойността, ако се натъкнете на памет изчерпана грешка. Забележка: някои уеб хостинг доставчици може да не поддържат това.Коригирайте тази стойност, за да приложите ограничение за временна PHP памет за WPvivid backup плъгин за изпълнение на резервно копие. Тази стойност е 256M по подразбиране. Увеличете стойността, ако се натъкнете на памет изчерпана грешка. Забележка: някои уеб хостинг доставчици може да не поддържат това.Разширено разписаниеРазширение настройкиВсички авториВсички категорииВсички архиви ще бъдат качени в тази директория.Винаги изпращайте имейл съобщение, когато архивирането завърши1000000000Име на кофата на Амазонка S3 (напр. тест)Ключ за достъп на Amazon S3Тайният ключ на Amazon S3Изисква се имейл адрес.Сигурни ли сте, че сте избрали да премахнете избраните архиви? Тези архиви ще бъдат изтрити завинаги от хостинга ви (Localhost).Сигурни ли сте, че сте го махнали? Това архивиране ще бъде изтрито завинаги от вашия хостинг (Localhost) и отдалечени складове.Както Amazon S3 и DigitalOcean Space са модернизирани своите методи за свързване, моля, изтрийте предишните връзки и добавете отново вашите Amazon S3 /DigitalOcean Space сметки, за да се уверите, че връзките работят.Попитайте за 40% ОтстъпкаПрисвояване на авторУдостоверяване с DropboxУдостоверяване с Google ДискУдостоверяване с Microsoft OneDriveНеуспешно удостоверяване, липсва файлът client_secrets.json. Моля, уверете се, че файлът client_secrets.json е в wpvivid-backuprestore\включва\customclass директория.Неуспешно удостоверяване, форматът на файла client_secrets.json е неправилен. Изтрийте и инсталирайте отново приставката, за да създадете файла.АвторАвтоматично мигриранеРъчно архивиранеОбратно към страницата за импортиранеАрхивирането на %s е завършено.АрхивиранеАрхивиране и възстановяванеКеш за архивиранеПапка за архивиранеАрхивиране сегаРазписание за архивиранеАрхивиране:АрхивиранеОсновниКато се подлага на механизми на PHP, планирана бекъп задача за вашия сайт ще се задейства само когато сайтът получи поне посещение на всяка страница.Изчисляване на размериИзчислете размера на файловете, папките и базата данни, преди да архивиратеИзчисляване на размера на файловете, папката и базата данни изтече. Ако продължавате да получавате тази грешка, моля, отидете на настройките на приставката, махнете отметката от "Изчислете размера на файловете, папките и базата данни, преди да архивирате", запишете промените и опитайте отново.ОтказПромениПроверете опцията за използване на amazon S3 стандартен-рядък достъп (S3 Standard-IA) клас за съхранение за прехвърляне на данни.Проверете опцията за използване на шифроване от страна на сървъра на Amazon S3, за да защитите данните.Поздрав! WPvivid Backup плъгинът е възстановил успешно вашия сайт. Ако установите, че WPvivid Backup плъгинът е полезен, ще сме много благодарни, ако ни дадете 5-звезден рейтинг, което ни мотивира да продължим да предоставяме нови функции.Изберете експортиране от компютъра за импортиране: Изберете поне едно място за съхранение за архивиране.Изберете поне един тип нежелани файлове за изтриване.Изберете едно място за съхранение от списъка, за да бъде хранилището по подразбиране.Изберете тип публикацияИзберете съдържанието, което искате да прехвърлитеИзберете какво да експортиратеИзберете какво да мигриратеРазмер на парчетоНе се открива клас PCLZip. Актуализирайте или преинсталирайте wordPress.Кликнете върху бутона "Експортиране", за да запазите настройките на WPvivid на вашия локален компютър.Щракнете върху бутона, за да се свържете с хранилището на Amazon S3 и го добавете към списъка за съхранение по-долу.Щракнете върху бутона, за да се свържете с DigitalOcean Spaces за съхранение и да го добавите към списъка за съхранение по-долу.Щракнете върху бутона, за да се свържете с FTP сървър и го добавете към списъка за съхранение по-долу.Щракнете върху бутона, за да се свържете със SFTP сървър и да го добавите към списъка за съхранение по-долу.Щракнете върху бутона, за да получите удостоверяване Dropbox и да го добавите към списъка за съхранение по-долу.Кликнете върху бутона, за да получите удостоверяване от Google и да го добавите към списъка за съхранение по-долу.Щракнете върху бутона, за да получите Microsoft удостоверяване и да го добавите към списъка за съхранение по-долу.Щракнете върху бутона, за да запишете промените.Клонинг след това прехвърлянеКоментиране на експортирането (по избор)Коментирайте износа: КоментариКомпресиране на файловете ВсекиКомпресиране и архивиранеБройСъздаване на ръчно архивиранеСъздаване на точка на възстановяванеСъздаване на сайт за спиранеСъздаване на поддиректорен сайт с едно кликванеНеуспешно създаване на планирани задачи. Моля, опитайте отново по-късно.Текуща СтраницаТекуща версия:ПерсонализиранеСобствено СъдържаниеПерсонализиран указател (водещи доставчици на облачно съхранение)Път по изборЧасова зона по изборПотребителско съдържание за всеки графикПерсонализирано начало на графикаПерсонализирайте директорията, в която искате да съхранявате архиви в "Кофа".Персонализирайте директорията, където искате да съхранявате архиви в пространството.ЕжедневноБаза данни + файлове (файлове в WordPress)Размер на базата данни:Метод за достъп до базата данни.ДатаОтстраняване на грешкиИзтрийИзтриване на експортираните файлове в папкатаИзтриване на избраните архивиПодробностите са показани по-горе. Ще опитаме отново с друг анализатор ...Дигитални пространстваКлюч за достъп до цифрови пространстваЦифровококийски пространства таен ключПоказване на домейн(URL) на текущия сайт в името на архива. (например domain_wpvivid-5ceb938b6dca9_2019-05-27-07-36_backup_all.zip)Показване на отделни секции според ролите на потребителите & способноститеИзтеглянеИзтегляне на WPvivid архивиране за MainWPПуснете файловете тукDropboxИмейл отчети:ПразенПразен ИД на публикацияРазрешаване на постъпково архивиранеРазрешаване на разписанието за архивиранеРазрешаване на отчет за имейлАктивирайте режим за оптимизация за уеб хостинг / споделен хостингРазрешаване на опцията при неуспешно архивиране.Активирането на тази опция може да подобри скоростта на успешно архивиране, но ще отнеме повече време за архивиране.Въведете своя профил в Amazon S3Въведете вашия цифров акаунт за пространстваВъведете информацията за вашата DropboxВъведете вашия FTP акаунтВъведете информацията за google ДискВъведете информацията за Вашия Microsoft OneDriveВъведете своя SFTP акаунтВъведете уникален псевдоним: например Amazon S3-001Въведете уникален псевдоним: например DOS-001Въведете уникален псевдоним: например Dropbox-001Въведете уникален псевдоним: например Google Диск-001Въведете уникален псевдоним: например OneDrive-001Въведете уникален псевдоним: например SFTP-001Въведете абсолютен път и персонализирана поддиректорията (по избор) за задържане на резервните копия на текущия сайт. Например ,,/дом/потребителско име/персонализирана папкаВъведете абсолютен път и персонализирана поддиректорията (по избор) за задържане на резервните копия на текущия сайт. Например /var/ customfolder/Въведете съществувало хранилище, за да създадете директория за съхранение по избор.Въведете съществувало пространство, за да създадете директория за съхранение по избор.Въведете уникален псевдоним: например FTP-001Въведете крайна точка digitalOcean за съхранениеВъведете паролата на FTP сървъра.Въведете FTP сървъра.Въведете адреса на сървъра.Въведете сървъра порт.Въведете потребителското име.Въведете потребителската парола.Въведете ключа си за достъп до Amazon S3.Въведете вашия Amazon S3 тайна ключ.Въведете ключа за достъп до цифровите пространстваВъведете таен ключ за цифровите пространстваВъведете потребителското име на FTP сървъра.Възникна грешка при анализиране на данните за заявката. Опитайте да изпълните отново архивиране.Възникна грешка при анализиране на данните за заявката. Опитайте да изпълните задачата за експортиране отново.Грешка:Изключване на файлове, които са по-големи отЕкспортиранеЕкспортиране на %s от всички автори или конкретен автор.Експортиране на %s от всички категории или конкретна категория.Експортиране на %s, публикувано след тази дата.Експортиране на %s, публикувано преди тази дата.Експортиране - импортиранеИзвличане на съдържаниеЕкспортиране и изтеглянеГрешка при експортиране:Експортирайте публикации или страници с изображения в насипно състояние.Експортираните файлове ще бъдат временно съхранени в директория %sFTPFTP входFTP паролаFTP сървър (порт на сървъра 21)Не може да изтриете отдалеченото хранилище, не може да извлече информацията за съхранение. Моля, опитайте отново.Неуспешно получаване на информация за отдалечено съхранение. Моля, опитайте отново по-късно.Извличането на прикачени файлове не е активираноИме на файлРазмер на файла (kb)Файлът не съществува:Файлът не съществува, файл:Файлът не е намерен. опитайте отново.Размерът на файла не съвпада. опитайте отново.Филтриране на публикации/странициПърва страницаДве седмициНа свободна практикаОсновни настройкиГенериранеНеуспешно получаване на директория за архивиране. Моля, опитайте отново по-късно.Google ДискМаркирана икона осветява, че сте избрали отдалечено съхранение за съхранение на архивиКак да получите ключ за сайта?Как да получите достъп до AmazonS3 ключ.Как да получите тайната на AmazonS3 ключ.Как да мигрирате WordPress сайт ръчно в нов домейн (сайт) с WPvivid резервна добавка?Как да възстановите уебсайта си от архивно копие (планирано, ръчно, качено и получи резервно копие)Как-даАз използвам:Ако сте MainWP потребител, можете да настроите и управлявате WPvivid Backup Free и Pro за всеки сайт на детето директно от таблото на MainWP, като използвате нашия WPvivid Backup за mainWP разширение.Ако не сте конфигурирали SMTP на сайта си, щракнете върху бутона по-долу, за да изтеглите съответната информация (информация за уеб сайта и регистрационни файлове за грешки) на вашия компютър, когато срещате някои грешки. Изпращането на файловете до нас ще ни помогне да диагностицираме какво се е случило.Ако сте конфигурирали SMTP на вашия сайт, въведете вашия имейл адрес и щракнете върху бутона по-долу, за да ни изпратите съответната информация (уеб сайт информация и грешки регистрационни файлове), когато се натъкнете на грешки. Това ще ни помогне да разберем какво се е случило. След като проблемът бъде решен, ще Ви информираме на вашия имейл адрес.ИмпортНастройки за импортиранеИмпортирайте публикации или страници с изображения в насипно състояние.Импортираните файлове ще бъдат временно съхранени в директория %sИмпортирането на Json файл може да ви помогне да настроите конфигурацията WPvivid на друг сайт WordPress бързо.За да позволите на друг сайт да изпрати резервно копие на този сайт, моля генерирайте ключ по-долу. След като ключът е генериран, този сайт е готов да получи резервно копие от друг сайт. След това копирайте и поставете ключа в изпращането на сайта и го запазете.За да изпълните планираните архиви правилно, задайте DISABLE_WP_CRON постоянна на неистински.За да използвате тази функция, моля инсталирайте <strong><a target="_blank" href="https://wpvivid.com/8-best-smtp-plugins-for-wordpress.html" style="text-decoration: none;">WordPress SMTP плъгин</a></strong> на вашите предпочитания и първо да конфигурирате вашия SMTP сървър. Това е така, защото WordPress използва php Mail функция, за да изпраща имейлите си по подразбиране, което не се поддържа от много хостове и може да предизвика проблеми, ако не е зададено правилно.Включване/изключване на файлове/папкиПостъпково архивиранеГрафик за постъпково архивиранеНевалиден emailНевалиден тип файлПрепоръчително е да изберете опцията за PDO, ако pdo_mysql разширение е инсталирано на вашия сървър, което ви позволява да архивирате и възстановявате сайта си по-бързо.Това ще доведе до по-висока употреба на CPU и се препоръчва в VPS / посветен хостинг среда.Това ще доведе до по-ниска употреба на CPU и се препоръчва в уеб хостинг / споделена хостинг среда.НежеланаСъхранявайте архивите в Localhost след качването на отдалечено хранилищеКлючПоследно архивиране: Последна промянаПоследна страницаНаучете ощеДиректория за локални хранилища:Директория за локални хранилища:РегистърИме на регистрационния файлТип на регистъраРегистриMainWPМоже би по-късноРазмер на медийните файловеОбединяване на всички архивни файлове в един пакет, когато архивирането завърши. Това ще спести големи дискови пространства, макар че отнема повече време. Препоръчваме ви да проверите опцията, особено на сайтове с недостатъчни сървърни ресурси.Метод 1.Метод 2.Преглед на оригиналната статия на английски: 301Мигриране на WordPressМиграцията е завършена и htaccess файлът се заменя. За да завършите успешно миграцията, по-добре е да преинсталирате 301 добавка за пренасочване, защитна стена и приставка за сигурност и кеширане плъгин, ако те съществуват.Миграция чрез отдалечено съхранениеМиграция:Пропуснат графикМесечноСкоро ще има още видове пост...Моят доставчик на уеб хостинг е:Името на папката трябва да може да се записва за създаване на архивни файлове.Мрежова връзка:НикогаСледващо архивиране: Следваща СтъпкаСледваща страницаБележка:Забележка: Забележка: Файловете, които искате да качите, трябва да бъдат резервно копие, създадено от WPvivid backup плъгин. Уверете се, че качването на всяка част от архива в директорията, ако архивирането е разделено на много частиСлед като е маркирано, всички резервни копия на тези сайтове, изпратени до местоназначение за отдалечено съхранение, ще бъдат качени по подразбиране на това място за съхранение.Само архив без компресиранеСамо база данниДопускат се само букви (с изключение на wpvivid) и цифри.Само изпраща имейл съобщение, когато архивиране е неуспешноИли можете да използвате FTP, за да качите експортирането в директорията %s. След това кликнете върху бутона по-долу, за да сканирате файла за импортиране.Презапис на съществуващи странициГраница на запаметяване на PHP за архивиранеГраница на php паметта за възстановяванеВреме за изпълнение на PHP скрипт за архивиранеВремето за изпълнение на PHP скрипт за възстановяванеСтраницаПът:Изберете едно място за съхранение, за да запазите архивите си (отдалечено хранилище)Моля, опишете проблема си тук.Моля, не затваряйте страницата и не превключвайте към други страници, когато изпълнява задача за възстановяване, тъй като тя може да предизвика някои неочаквани грешки.Моля, въведете валиден имейл адрес.Моля, поставете ключа по-долу.Моля, прочетете <a target="_blank" href="https://wpvivid.com/privacy-policy" style="text-decoration: none;">тази декларация</a> за поверителност за използване на нашето Приложение за разрешение Dropbox (нито един от вашите архивни данни не се изпраща до нас).Моля, прочетете <a target="_blank" href="https://wpvivid.com/privacy-policy" style="text-decoration: none;">тази декларация</a> за поверителност за използване на приложението ни за упълномощаване Google Диск (нито един от вашите архивни данни не се изпраща до нас).Моля, прочетете <a target="_blank" href="https://wpvivid.com/privacy-policy" style="text-decoration: none;">тази декларация</a> за поверителност за използване на нашето приложение за оторизиране на Microsoft OneDrive (нито една от вашите данни не се изпраща до нас).Изберете поне един елемент.ПортПубликацияВидове публикацииПремиумПредишна страницаФункции на версията на ProPro функция: Добавете друг имейл адрес, за да получите отчетPro функция: Промяна на FTP номера на порт по подразбиранеPro функция: Създаване на директория за съхраняване на архивите на сайтаPro функция: Запазват повече резервни копияPro функция: научете повечеНапредък: Публикуване на сайт за спиране в сайт на живо с едно кликванеПубликуванаОценете ниПрочетете <a href="https://wpvivid.com/troubleshooting-issues-wpvivid-backup-plugin" target="_blank">Страница за отстраняване на неизправности</a> за по-бързи решения.Четенето на регистрационния файл е неуспешно. Моля, опитайте отново.Готови за подкрепление. Напредък: 0%, време на изпълнение: 0секунда.Готови за експортиране. Напредък: 0%, време на изпълнение: 0секунда.Отдалечено съхранениеПсевдоним на отдалечено хранилищеДиректория за отдалечено съхранение:Отдалечено съхранение:ПремахниПремахване на архиви с несъстоялиВъзстановяванеВъзстановяване на вашия сайт от архивВъзстановяване на уеб сайт от отдалечено хранилищеВъзстановяване и запазване на оригиналния домейн (URL) непромененВъзстановяване и заместване на първоначалния домейн (URL) с %s (миграция)Възстановяване на архив от:Възстановяването завърши успешно.Функцията за възстановяване ще замени темите, плъгините, качването, базата данни и/или други директории на съдържанието със съществуващите еквиваленти в избрания архив.Възстановяване на това, което искате от архивВъзстановяване:Извличане на резервно копие на localhostИзвличането на архивната информация е неуспешно при показване на регистрационния файл. Моля, опитайте отново по-късно.Извличането на информация за архивирането е неуспешно при изтриване на избраните архиви. Моля, опитайте отново по-късно.Роли и възможности (добавка):ВръщанеSftpЗАПОЧНЕТЕ 14-дневен безплатен пробен периодПример:ЗапазиЗапиши архивите локалноЗапази променитеЗапазване на архивите на localhost (уеб сървър)Сканиране на качените експортиСканиране качено архивиране или получаване на резервно копиеПланиранеНастройки на графикаСъстояние на разписание: График:ПланиранаПланираното задание ще започне по <strong>UTC</strong> време:ТърсенеТърсене на %s съгласно горните правила.Избор на всичкоИзбиране на файлИзберете съществуващ автор:Изпращане на резервно копие на отдалечено хранилище:Изпращане на информация за отстраняване на грешки до насИзпращане на резервни копия на отдалечено съхранение (изберете тази опция, локалния архив ще бъде изтрит след качването на отдалечено съхранение напълно)Изпращане на имейл отчети до няколко имейл адресаАдрес на сървъраЧас на сървъра: Шифроване от страна на сървъра.Задайте като отдалечено хранилище по подразбиране.НастройкиПоказване на WPvivid резервна добавка на горната администраторска лентаНякои уеб хостинг доставчици ограничават големи пощенски файлове (например 200MB) и следователно разделянето на архива на много части е идеален начин да избегнете удрянето на ограничението, ако използвате голям уебсайт.  Опитайте да коригирате стойността, ако се натъквате на грешки в архива. Ако използвате стойност от 0 МБ, всички архивни файлове няма да бъдат разделени.За съжаление, този тип файлове не е разрешен от съображения за сигурност.Име на интервала (напр. тест)Специална оптимизация за уеб хостинг / споделен хостингСкорост:Спиране (добавка):Стартиране на архивирането на %s.Начало на импортиранеПърва стъпка: В списъка за архивиране, кликнете върху "Възстановяване" бутон на архива, който искате да възстановите. Това ще доведе до раздела за възстановяванеСтъпка трета: Кликнете върху бутона "Възстановяване"Стъпка две: Изберете опция за завършване на възстановяването, ако има такаваОбемРедактиране на хранилищеДоставчик на хранилищеКлас на съхранение: Стандартен (нечестен достъп).ХранилищаПодкрепа:Време за изчакване на задачата.Времето за изчакване на задачата изтече.Временни файловеВременни файлове са създадени от wpvivid при възстановяване на уеб сайт.ПрекратиТест на имейлТест и добавянеНе е открито разширение %s. Първо инсталирайте разширението.Не са открити разширения %s. Първо инсталирайте разширенията.Действието е необратимо! Той ще премахне всички архиви са отечещи на дата (включително локален уеб сървър и отдалечено съхранение), ако те съществуват.Архивът се съхранява на отдалеченото хранилище, кликнете върху бутона, за да го изтеглите в Localhost.Архивирането ще бъде отменено след архивиране на текущия блок.Архивните файлове ще бъдат качени в директория %s.Задачата за експортиране не отговаря.Функцията може да ви помогне да прехвърлите wordpress сайт в нов домейн (сайт). Това би било удобен начин да мигрирате вашия WP сайт от dev среда на сървър на живо или от стар сървър към нов.Информация за файла за импортиранеКлючът ще изтече в Последното съобщение за архивиране не е намерено.Изисква се локален път за съхранение.Регистрационният файл не е намерен.Изисква се максимално време за изпълнение на PHP скрипт.Изисква се максималният размер на zip файла.Не е открито разширението на pdo_mysql. Моля, първо инсталирайте разширението или изберете wpdb опция за метод за връзка с база данни.Приставката открива автоматично или възстановяване на сайт или миграция (заместване на името на домейна) въз основа на текущото име на домейн. Ако името на домейна в архивния файл е същото като текущото, то започва да се възстановява. Напротив, възстановяването на резервното копие означава да замени с текущото име на домейн. Предварително условие е, че архивирането е създаден от версия 0.9.21 или по-нова.Файлът за възстановяване не е намерен. Проверете дали файлът съществува.Избраният файл не е файл за настройка за WPvivid. Моля, качете правилния файл.Избраните нежелани мухи са били изтрити.Настройките са само за ръчно архивиране, което няма да засегне настройките за график.Разширението на simplexml не се открива. Първо инсталирайте разширението.Размерът на изключените файлове е необходим.Времето за изчакване на сървъра не е времето за изчакване на сървъра. С времето за изпълнение изчерпани, нашата приставка ще затвори процеса на архивиране надолу. Ако прогресът на архивирането на данни се сблъска с изчакване, това означава, че имате средно или голям по размер уебсайт, моля, опитайте да мащабите по-голямата стойност.Времето за изчакване на сървъра не е времето за изчакване на сървъра. С времето за изпълнение изчерпани, нашата приставка ще затвори процеса на възстановяване. Ако прогресът на възстановяването на даден период от време, това означава, че имате средно или голям по размер уебсайт, моля, опитайте да мащаба на стойността по-голяма.Има два начина за изпращане на информацията за отстраняване на грешки. Първият се препоръчва.Няма конфигурирано отдалечено място за съхранение по подразбиране. Моля, първо го настройте.При четене на този WXR файл възникна грешкаТова архивиране може да бъде изтрито само ръчноТова архивиране е заключено, сигурни ли сте, че сте го махнали? Това архивиране ще бъде изтрито завинаги от вашия хостинг (Localhost) и отдалечени складове.Това изглежда не е WXR файл, липсващ или невалиден номер на версията на WXRТази заявка може да изтрие изтегленото резервно копие, наистина ли искате да продължите?Тази заявка ще изтрие изтегления архив, сигурен ли сте, че искате да продължите?Това ще съдържа всички ваши публикации, страници, коментари, термини и изображения (оригинални изображения, представени изображения и миниатюри).Поддръжка на билет 7x24Съвети:Съвети: Кликнете върху бутона по-долу, за да сканирате всички качени или получени архиви в директориятаСъвети: Поради причини за сигурност, моля, изберете подходящо време за изтичане на ключа.За да покажете правилно импортираното съдържание, уверете се, че обектите за импортиране и експортиране имат една и съща среда, например същата тема или страници, създадени със същия конструктор на страници.Твърде много опити за възобновяване.Общ размер:Прехвърлянето е успешно. Сканирайте списъка с резервни копия на сайта местоназначение, за да покажете архива, след което възстановете архива.Отстраняване на проблемиОпитайте да изберете по-малко елементи, когато сте изправени пред недостиг на сървърни ресурси (обикновено представени като грешка за изчакване).Вид:Тип: УлтиматНе може да се изтрие заключеното архивиране. Първо го отключите и опитайте отново.Регистрационният файл не може да се отвори.Не може да се изпрати имейл. Моля, проверете конфигурацията на имейл сървъра.Премахнете отметката от опцията за използване на FTP активен режим при прехвърляне на файлове. Уверете се, че ftp сървърът, който конфигурирате, поддържа активния FTP режим.Премахнете отметката от това, за да активирате активен режим на FTP.НеограниченоНепубликуванДо 100 сайтаДо 3 сайтаКачиКачване и импортиранеКачил:Потребителско имеПотребителска паролаИмейл адрес на потребителя е необходим.Използването на опцията ще игнорира файла, по-голям от определен размер в МБ, когато архивирате, "0" (нула) означава неограничен.WASABI/Pcloud (само Pro)WPDB опция има по-добра съвместимост, но скоростта на архивиране и възстановяване е по-бавно.WPvivid Backup плъгинНастройки на WPvivid архивиранеWPvivid имейл за поддръжка:Внимание:Предупреждение: Изисква се псевдоним за отдалечено съхранение.Предупреждение: Псевдонимът вече съществува в списъка за съхранение.Предупреждение: Няма налично отдалечено място за съхранение по подразбиране за планираните архиви, първо го настройте.Ние също можем да предложим 40% oотстъпка за WPvivid Backup Pro за потребители на MainWP.Директория на уеб сървъра:Уеб сървър дисково пространство използвани от WPvividКлюч за информация за уеб сайтаСтойност на уеб сайтаСайтовеСедмичноПри тази отметка, страници/публикации, които вече са съществуващи, ще бъдат презаписани с актуализираните в импортираните.Файлове в WordPress (изключване на базата данни)Вие сте удостоверени Dropbox акаунт като вашето отдалечено място за съхранение.Вие сте удостоверили профила в Google Диск като вашето отдалечено хранилище.Вие сте удостоверили акаунта на Microsoft OneDrive като вашето отдалечено място за съхранение.Успешно сте добавили отдалечено хранилище.Успешно сте актуализирали псевдонима на хранилището.Вашият имейл:запазени архивинапример.  ако изберете размер на парче 2 МБ, 8MB файл ще използва 4 парчета. Намаляването на тази стойност ще прекъсне границата на пренос на ISP, например: 512 КБрегистринова функцияregion.digitaloceanspaces.comпреименуване на директориянеизвестна грешка при възстановяваневреме на работа: languages/wpvivid-backuprestore-it_IT.mo000064400000202255151327705670014437 0ustar00�����p)q)
~)��)7*>*zU*�*
�*��*[�+84,6m,�,C�.//:/hQ/+�/N�/Y50��0V-1K�1?�1G2NX2V�2��2�3V�3YD4�4-�4#�45
5�5�5�6�7�7�7�7/�7;!8	]8 g8�8�8�8u�8~F9��9�:
�:�:�:$�:�;��;P<W<f<w<�<�<�<�<
�<
�<�<�<�<=�=�=B�=��=�>	�>n�>Ie?��?/�@1�@4�@;ATA'eA�A�A
�AH�AFBVUB`�BO
CP]CT�CSDVWD%�D�D�DEE$E9ENETEkE�E2�E8�EFF"F)F28FkFwF �F�FJ�FIG[G"aG�G�G�G�G�G�G�GS�GMHaH�Hw�HFI^I"gI�I�I�I�I
�I�I�I�I7
J%BJ`hJ�J&�J
K,K#CK)gK�K(�K"�K&�K+L'HL#pL��L�%MD�MC�M#6N/ZN�N�N�N�N�NO O ?O)`O)�O �ON�OS$PxP'P�P.�P3�P$Q%6Q\QlQ{Q
�Q*�Q9�QR	RRR];RE�R#�R	S

SS-S#CS(gS�S
�S�S
�S�S�S8�STT%TzT"�T"�TX�TZ0U�U�U��U�XV4NW�X�X*�X9�Xc�X�bYeSZ��Zj\�\�\�\�\��\\n]`�],^G1^y^
}^
�^	�^
�^�^�^�^
�^�^�^___�$_	`	``+`�=`a
,a7aGaOanaI�a�a�a
�a	�a	bbb�bv�b Rc
sc:�c3�ct�ced~d �d'�d(�dee?e"We�ze#�e f�<f�g��g �h�h�h
�h�h
ii4&i/[iC�i �i�i
j4j	HjRj�Zj)�j5k5Ikk�k�k�k�k�k�k�k%l4=lArl�l�l��l#�m�m �mS�mj6n�n�n�n�n�n�n�n	o&o=o'So{o�o�o	�o	�o6�o�o+�o
&p1p>pYpxp��p.qGq
Vqdq"|q�q+�qH�q<sZs3ps�s�s�s�s~�s"at6�t�t�t�t,�tuu u/u?u@Ou	�u
�u�uE�uH�u�Av\�vD$w-iw"�w��w|x�x"�x#�x�x6y&=y�dyr�y:Y{T�{*�{N|Lc|(�|
�|�}\�~FT-�(���I�YɀX#�y|���
�R�Tc����x���w���v*�������I���E��d�'�	�$�0�
@�N�U�	g�	q�
{�!��u��!�V:�������և1߇2�hD�H����'�4�E�X�a�nh�"׉B��G=�M��-ӊ0�2�>��O������*�G�-V��������`�f����"�
=�K�jX�8Ñ<��+9�Ze���ǔ<Δb�*n�K��d��J�i��fi�PЗK!�Um�eØ)�,�TL��� !�AB�4�������ǛR��R�<�T�j�{�5��>ğ	�/
�=�R�$g�y�������#������ע"����ңs�z���!��ɤ�����"�7�K�S�Z��b��Y)�W��ۧ	���a����2ϩ;�8>�8w���'Ǫ��+�LA�Y��s�\�gܬhD�}��|+����,)�V�#j�������įدޯ���<)�Rf���ɰܰ�<�A�Y�+t�.��Xϱ\(���#����β����'�(�TE�����ͳ��Kx�Ĵ$̴��
��$�3�#Q�u�J��+ܵ}�"��/��1ٶ�6(�<_���5��.�2�7R�3��0�������aA�b��.�85�%n���!��ϻ��)&�)P�/z�3��,޼y�����&�@�5H�8~�)��-��!�6�H�1`�G��ڿ޿�"��{�_��+��
"�0�E�[�1u�;������	��4�B;�~�Z����)�),�fV�\��	�$��8�	��b�k�x�,��G��{
���`����(���%�:�U��m�g�u��
��O�W�^�n�
~�����"��������
��� '�H�	K�	U�_�r����'o���������"��i�o������������������� /�
P�9^�>�����!q�#��'��4��8�M�	T�A^�'�����/r�/��
������+�4�:�?�L�T� f�H��<��G
�0U�&����<��
����	�;��D��I0�z�������	����
�#�+3�;_�G��������%������i�|�����#�%(�	N�X�^�w�(����$����	�(�B�R�<^���.������ ��$�$;��`�-��%�:�K�*e���2��y��KJ�"��<��
����*��@�-��I�X�`�q�4����	��������Q��K�S�c�Qt�U����d��L�.T�%�����(w���2��,���?'�0g�����5�V��c"�4��h��U$�.z�!��$��Q��VB�@��5���P��c�[j���L�
e�qs�l��R�*�G��Z����������`�'+�fS����Kk�
����
���	�
��,&��S��d��Z�p�����>��:��u3N��0CYs|��!
:,?gE�-�5
Q_�q"';Y*l�A�W�U�loB���a��3�5�$�Vi�|�3m�jP�Wc���:b�P��[n�D�v�
���Q� ;�+����b�&'I�?CnD�6��Fy����V`(^"��p����a��dz0�	=	���1"�����0�E��Ep=�)tht�����B�@�{�L����r�;�>�
kA��]���q��N��Y����6��9���(smJU[��wo�<���d�$:��%}�_�-.ls+��ZuFj&OR}�SQ��,�X�>/����'yKeO�����.4-\,��H8
�������|���^hxC\H�z��i���~�����J��f�����5��!��M
�N�����X��%1���*?7�#�g�������#��G��@���R���7]K29/�Yxc�Z{�����������f���u�����*�!��wT��T���G_<r�v2��kq���S~ e�)����84I������g������ML��` < Pre page  Next page > %d backup tasks have been completed. Please switch to <a href="#" onclick="wpvivid_click_switch_page('wrap', 'wpvivid_tab_log', true);">Log</a> page to check the details.%s ago&mdash; Select &mdash;*No credit card needed. Trial starts with the Free Trial plan with 2 sites. You can choose a plan at the end of the trial.+ Add another schedule- Select -->If backups are stored in remote storage, our plugin will retrieve the backup to your web server first. This may take a little time depending on the size of backup files. Please be patient. Then you can download them to your PC.->If backups are stored in web server, the plugin will list all relevant files immediately.1. Create and download backups for a specific child site1. Download a backup in backups list to your computer.1. In order to successfully complete the migration, you'd better deactivate <a href="https://wpvivid.com/best-redirect-plugins.html" target="_blank" style="text-decoration: none;">301 redirect plugin</a>, <a href="https://wpvivid.com/8-best-wordpress-firewall-plugins.html" target="_blank" style="text-decoration: none;">firewall and security plugin</a>, and <a href="https://wpvivid.com/best-free-wordpress-caching-plugins.html" target="_blank" style="text-decoration: none;">caching plugin</a> (if they exist) before transferring website.1. Visit Key tab page of WPvivid backup plugin of destination site.12 Hours12Hours2. Generate a key by clicking Generate button and copy it.2. Please migrate website with the manual way when using <strong>Local by Flywheel</strong> environment.2. Set backup schedules for all child sites2. Upload the backup to destination site. There are two ways available to use:2.1 Upload the backup to the upload section of WPvivid backup plugin in destination site.2.2 Upload the backup with FTP client to backup directory %s in destination site, then click <strong>Scan uploaded backup or received backup</strong> button.3. Go back to this page and paste the key in key box below. Lastly, click Save button.3. Once done, the backup appears in backups list. Then, restore the backup.3. Set WPvivid Backup Free and Pro settings for all child sites4. Install, claim and update WPvivid Backup Pro for child sites in bulk5. Set up remote storage for child sites in bulk (for WPvivid Backup Pro only)<strong>Retrying </strong>%s<strong> times when encountering a time-out error</strong><strong>Tips: </strong>The unstable connection between sites could cause a failure of files transfer. In this case, uploading backups to destination site is a good alternative to the automatic website migration.A backup type is required.A name to help you identify the storage if you have multiple remote storage connected.A task is already running. Please wait until the running task is complete, and try again.About backup downloadAbsolute path must exist(e.g. /home/username)Absolute path must exist(e.g. /var)ActionActionsAdjust <a href="https://wpvivid.com/wpvivid-backup-plugin-advanced-settings.html" target="_blank">Advanced Settings</a> for higher task success rate.Adjust this value to apply for a temporary PHP memory limit for WPvivid backup plugin in restore process. We set this value to 256M by default. Increase the value if you encounter a memory exhausted error. Note: some web hosting providers may not support this.Adjust this value to apply for a temporary PHP memory limit for WPvivid backup plugin to run a backup. We set this value to 256M by default. Increase the value if you encounter a memory exhausted error. Note: some web hosting providers may not support this.Advanced ScheduleAdvanced SettingsAll AuthorsAll CategoriesAll backups will be uploaded to this directory.Always send an email notification when a backup is completeAmazon S3Amazon S3 Bucket Name(e.g. test)Amazon S3 access keyAmazon S3 secret keyAn email address is required.Are you sure to remove the selected backups? These backups will be deleted permanently from your hosting (localhost).Are you sure to remove this backup? This backup will be deleted permanently from your hosting (localhost) and remote storages.As Amazon S3 and DigitalOcean Space have upgraded their connection methods, please delete the previous connections and re-add your Amazon S3/DigitalOcean Space accounts to make sure the connections work.Ask For A 40% OFF DiscountAssign authorAuthenticate with DropboxAuthenticate with Google DriveAuthenticate with Microsoft OneDriveAuthentication failed, the client_secrets.json file is missing. Please make sure the client_secrets.json file is in wpvivid-backuprestore\includes\customclass directory.Authentication failed, the format of the client_secrets.json file is incorrect. Please delete and re-install the plugin to recreate the file.AuthorAuto-MigrationBack Up ManuallyBack to Import PageBacking up %s finished.BackupBackup & RestoreBackup CacheBackup FolderBackup NowBackup ScheduleBackup:BackupsBasicBeing subjected to mechanisms of PHP, a scheduled backup task for your site will be triggered only when the site receives at least a visit at any page.Calculate SizesCalculate the size of files, folder and database before backing upCalculating the size of files, folder and database timed out. If you continue to receive this error, please go to the plugin settings, uncheck 'Calculate the size of files, folder and database before backing up', save changes, then try again.CancelChangeLogCheck the option to use Amazon S3 Standard-Infrequent Access (S3 Standard-IA) storage class for data transfer.Check the option to use Amazon S3 server-side encryption to protect data.Cheers! WPvivid Backup plugin has restored successfully your website. If you found WPvivid Backup plugin helpful, a 5-star rating would be highly appreciated, which motivates us to keep providing new features.Choose an export from your computer to import: Choose at least one storage location for backups.Choose at least one type of junk files for deleting.Choose one storage from the list to be the default storage.Choose post typeChoose the content you want to transferChoose what to exportChoose what to migrateChunk SizeClass PclZip is not detected. Please update or reinstall your WordPress.Click 'Export' button to save WPvivid settings on your local computer.Click the button to connect to Amazon S3 storage and add it to the storage list below.Click the button to connect to DigitalOcean Spaces storage and add it to the storage list below.Click the button to connect to FTP server and add it to the storage list below.Click the button to connect to SFTP server and add it to the storage list below.Click the button to get Dropbox authentication and add it to the storage list below.Click the button to get Google authentication and add it to the storage list below.Click the button to get Microsoft authentication and add it to the storage list below.Click the button to save the changes.Clone then TransferComment the export (optional)Comment the export: CommentsCompress Files EveryCompress and ArchiveCountCreate a Manual BackupCreate a restore pointCreate a staging siteCreate a sub-directory staging site with one-clickCreating scheduled tasks failed. Please try again later.Current PageCurrent Version:CustomCustom ContentCustom Directory (leading cloud storage providers)Custom PathCustom TimezoneCustom content for each scheduleCustom start time of scheduleCustomize the directory where you want to store backups within the Bucket.Customize the directory where you want to store backups within the Space.DailyDatabase + Files (WordPress Files)Database Size:Database access method.DateDebugDeleteDelete Exported Files In FolderDelete the selected backupsDetails are shown above. The importer will now try again with a different parser...DigitalOcean SpacesDigitalOcean Spaces access keyDigitalOcean Spaces secret keyDisplay domain(url) of current site in backup name. (e.g. domain_wpvivid-5ceb938b6dca9_2019-05-27-07-36_backup_all.zip)Display the individual sections according to user roles & capabilitiesDownloadDownload WPvivid Backup for MainWPDrop files hereDropboxEmail Reports:EmptyEmpty post idEnable Incremental BackupEnable backup scheduleEnable email reportEnable optimization mode for web hosting/shared hostingEnable the option when backup failed.Enabling this option can improve the backup success rate, but it will take more time for backup.Enter Your Amazon S3 AccountEnter Your DigitalOcean Spaces AccountEnter Your Dropbox InformationEnter Your FTP AccountEnter Your Google Drive InformationEnter Your Microsoft OneDrive InformationEnter Your SFTP AccountEnter a unique alias: e.g. Amazon S3-001Enter a unique alias: e.g. DOS-001Enter a unique alias: e.g. Dropbox-001Enter a unique alias: e.g. Google Drive-001Enter a unique alias: e.g. OneDrive-001Enter a unique alias: e.g. SFTP-001Enter an absolute path and a custom subdirectory (optional) for holding the backups of current website. For example, /home/username/customfolderEnter an absolute path and a custom subdirectory (optional) for holding the backups of current website. For example, /var/customfolder/Enter an existed Bucket to create a custom backup storage directory.Enter an existed Space to create a custom backup storage directory.Enter an unique alias: e.g. FTP-001Enter the DigitalOcean Endpoint for the storageEnter the FTP server password.Enter the FTP server.Enter the server address.Enter the server port.Enter the user name.Enter the user password.Enter your Amazon S3 access key.Enter your Amazon S3 secret key.Enter your DigitalOcean Spaces access keyEnter your DigitalOcean Spaces secret keyEnter your FTP server user name.Error occurred while parsing the request data. Please try to run backup again.Error occurred while parsing the request data. Please try to run export task again.Error:Exclude the files which are larger thanExportExport %s of all authors or a specific author.Export %s of all categories or a specific category.Export %s published after this date.Export %s published before this date.Export & ImportExport ContentExport and DownloadExport error:Export posts or pages with images in bulk.Exported files will be temporarily stored in %s directoryFTPFTP loginFTP passwordFTP server (server's port 21)Fail to delete the remote storage, can not retrieve the storage infomation. Please try again.Failed to get the remote storage information. Please try again later.Fetching attachments is not enabledFile NameFile Size:File not exist file:File not exist, file:File not found. please retry again.File size not match. please retry again.Filter Posts/PagesFirst pageFortnightlyFreelancerGeneral SettingsGenerateGetting backup directory failed. Please try again later.Google DriveHighlighted icon illuminates that you have choosed a remote storage to store backupsHow to get a site key?How to get an AmazonS3 access key.How to get an AmazonS3 secret key.How to migrate Wordpress site manually to a new domain(site) with WPvivid backup plugin?How to restore your website from a backup(scheduled, manual, uploaded and received backup)How-toI am using:If you are a MainWP user, you can set up and control WPvivid Backup Free and Pro for every child site directly from your MainWP dashboard, using our WPvivid Backup for MainWP extension.If you didn’t configure SMTP on your site, click the button below to download the relevant information (website info and error logs) to your PC when you are encountering some errors. Sending the files to us will help us diagnose what happened.If you have configured SMTP on your site, enter your email address and click the button below to send us the relevant information (website info and errors logs) when you are encountering errors. This will help us figure out what happened. Once the issue is resolved, we will inform you by your email address.ImportImport SettingImport posts or pages with images in bulk.Imported files will be temporarily stored in directory %sImporting the json file can help you set WPvivid's configuration on another wordpress site quickly.In order to allow another site to send a backup to this site, please generate a key below. Once the key is generated, this site is ready to receive a backup from another site. Then, please copy and paste the key in sending site and save it.In order to execute the scheduled backups properly, please set the DISABLE_WP_CRON constant to false.In order to use this function, please install a <strong><a target="_blank" href="https://wpvivid.com/8-best-smtp-plugins-for-wordpress.html" style="text-decoration: none;">WordPress SMTP plugin</a></strong> of your preference and configure your SMTP server first. This is because WordPress uses the PHP Mail function to send its emails by default, which is not supported by many hosts and can cause issues if it is not set properly.Include/exclude files/foldersIncremental BackupIncremental Backup ScheduleInvalid email addressInvalid file typeIt is recommended to choose PDO option if pdo_mysql extension is installed on your server, which lets you backup and restore your site faster.It will cause a higher CPU Usage and is recommended in a VPS/ dedicated hosting environment.It will cause a lower CPU Usage and is recommended in a web hosting/ shared hosting environment.JunkKeep storing the backups in localhost after uploading to remote storageKeyLast Backup: Last ModifiedLast pageLearn moreLocal Storage Directory:Local storage directory:LogLog File NameLog TypeLogsMainWPMaybe LaterMedia Files SizeMerge all the backup files into single package when a backup completes. This will save great disk spaces, though takes longer time. We recommended you check the option especially on sites with insufficient server resources.Method 1.Method 2.Microsoft OneDriveMigrate WordPressMigration is complete and htaccess file is replaced. In order to successfully complete the migration, you'd better reinstall 301 redirect plugin, firewall and security plugin, and caching plugin if they exist.Migration via remote storageMigration:Missed scheduleMonthlyMore post types coming soon...My web hosting provider is:Name your folder, this folder must be writable for creating backup files.Network Connection:NeverNext Backup: Next StepNext pageNote:Note: Note: The files you want to upload must be a backup created by WPvivid backup plugin. Make sure that uploading every part of a backup to the directory if the backup is split into many partsOnce checked, all this sites backups sent to a remote storage destination will be uploaded to this storage by default.Only Archive without compressingOnly DatabaseOnly letters (except for wpvivid) and numbers are allowed.Only send an email notification when a backup failsOr you can use ftp to upload the export to the directory %s. Then click the button below to scan the file to import.Overwrite existing pagesPHP Memory Limit for backupPHP Memory Limit for restorationPHP script execution timeout for backupPHP script execution timeout for restorePagePath:Please choose one storage to save your backups (remote storage)Please describe your problem here.Please do not close the page or switch to other pages when a restore task is running, as it could trigger some unexpected errors.Please enter a valid email address.Please paste the key below.Please read <a target="_blank" href="https://wpvivid.com/privacy-policy" style="text-decoration: none;">this privacy policy</a> for use of our Dropbox authorization app (none of your backup data is sent to us).Please read <a target="_blank" href="https://wpvivid.com/privacy-policy" style="text-decoration: none;">this privacy policy</a> for use of our Google Drive authorization app (none of your backup data is sent to us).Please read <a target="_blank" href="https://wpvivid.com/privacy-policy" style="text-decoration: none;">this privacy policy</a> for use of our Microsoft OneDrive authorization app (none of your backup data is sent to us).Please select at least one item.PortPostPost TypesPremiumPrevious pagePro Version FeaturesPro feature: Add another email address to get reportPro feature: Change the FTP default port numberPro feature: Create a directory for storing the backups of the sitePro feature: Retain more backupsPro feature: learn moreProgress: Publish a staging site to a live site with one-clickPublishedRate UsRead <a href="https://wpvivid.com/troubleshooting-issues-wpvivid-backup-plugin" target="_blank">Troubleshooting page</a> for faster solutions.Reading the log failed. Please try again.Ready to backup. Progress: 0%, running time: 0second.Ready to export. Progress: 0%, running time: 0second.Remote StorageRemote Storage AliasRemote Storage Directory:Remote Storage:RemoveRemove out-of-date backupsRestoreRestore Your Site from a BackupRestore a website from remote storageRestore and keep the original domain (URL) unchangedRestore and replace the original domain (URL) with %s (migration)Restore backup from:Restore completed successfully.Restore function will replace the current site's themes, plugins, uploads, database and/or other content directories with the existing equivalents in the selected backup.Restore what you want from a backupRestore:Retrieve the backup to localhostRetrieving the backup information failed while showing log. Please try again later.Retrieving the backup(s) information failed while deleting the selected backup(s). Please try again later.Roles & Capabilities (add-on):RollbackSFTPSTART 14-DAY FREE TRIALSample:SaveSave Backups to LocalSave ChangesSave backups on localhost (web server)Scan Uploaded ExportsScan uploaded backup or received backupScheduleSchedule SettingsSchedule Status: Schedule:ScheduledScheduled job will start at <strong>UTC</strong> time:SearchSearch for %s according to the above rules.Select AllSelect FilesSelect an existing author:Send Backup to Remote Storage:Send Debug Information to UsSend backups to remote storage (choose this option, the local backup will be deleted after uploading to remote storage completely)Send email reports to multiple email addressesServer AddressServer Time: Server-side encryption.Set as the default remote storage.SettingsShow WPvivid backup plugin on top admin barSome web hosting providers limit large zip files (e.g. 200MB), and therefore splitting your backup into many parts is an ideal way to avoid hitting the limitation if you are running a big website.  Please try to adjust the value if you are encountering backup errors. If you use a value of 0 MB, any backup files won't be split.Sorry, this file type is not permitted for security reasons.Space Name(e.g. test)Special optimization for web hosting/shared hostingSpeed:Staging (add-on):Start backing up %s.Start to ImportStep One: In the backup list, click the 'Restore' button on the backup you want to restore. This will bring up the restore tabStep Three: Click 'Restore' buttonStep Two: Choose an option to complete restore, if anyStorageStorage EditStorage ProviderStorage class: Standard (infrequent access).StoragesSupport:Task time out.Task timed out.Temporary FilesTemporary Files are created by wpvivid when restoring a website.TerminateTest EmailTest and AddThe %s extension is not detected. Please install the extension first.The %s extensions are not detected. Please install the extensions first.The action is irreversible! It will remove all backups are out-of-date (including local web server and remote storage) if they exist.The backup is stored on the remote storage, click on the button to download it to localhost.The backup will be canceled after backing up the current chunk ends.The backups will be uploaded to %s directory.The export task is not responding.The feature can help you transfer a Wordpress site to a new domain(site). It would be a convenient way to migrate your WP site from dev environment to live server or from old server to the new.The importing file infoThe key will expire in The last backup message not found.The local storage path is required.The log not found.The maximum execution time for PHP script is required.The maximum zip file size is required.The pdo_mysql extension is not detected. Please install the extension first or choose wpdb option for Database connection method.The plugin detects automatically either site restoration or migration (replacing the domain name) based on the current domain name. If the domain name in backup file is same as the current one, it starts restoring. On the contrary, restoring backup means to replace with the current domain name. The precondition is that the backup is created by version 0.9.21 or later.The restore file not found. Please verify the file exists.The selected file is not the setting file for WPvivid. Please upload the right file.The selected junk flies have been deleted.The settings are only for manual backup, which won't affect schedule settings.The simplexml extension is not detected. Please install the extension first.The size for excluded files is required.The time-out is not your server PHP time-out. With the execution time exhausted, our plugin will shut the process of backup down. If the progress of backup encounters a time-out, that means you have a medium or large sized website, please try to scale the value bigger.The time-out is not your server PHP time-out. With the execution time exhausted, our plugin will shut the process of restore down. If the progress of restore encounters a time-out, that means you have a medium or large sized website, please try to scale the value bigger.There are two ways available to send us the debug information. The first one is recommended.There is no default remote storage configured. Please set it up first.There was an error when reading this WXR fileThis backup can only be deleted manuallyThis backup is locked, are you sure to remove it? This backup will be deleted permanently from your hosting (localhost) and remote storages.This does not appear to be a WXR file, missing/invalid WXR version numberThis request might delete the backup being downloaded, are you sure you want to continue?This request will delete the backup being downloaded, are you sure you want to continue?This will contain all of your posts, pages, comments, terms and images (original images, featured images and thumbnails).Ticket 7x24 supportTips:Tips: Click the button below to scan all uploaded or received backups in directoryTips: For security reason, please choose an appropriate expiration time for the key.To properly display the imported content, please make sure that the importing and exporting sites have the same environment, for example, same theme or pages built with the same page builder.Too many resumption attempts.Total Size:Transfer succeeded. Please scan the backup list on the destination site to display the backup, then restore the backup.TroubleshootingTry to select fewer items when you are facing a shortage of server resources (typically presented as a timeout error).Type:Type: UltimateUnable to delete the locked backup. Please unlock it first and try again.Unable to open the log file.Unable to send email. Please check the configuration of email server.Uncheck the option to use FTP active mode when transferring files. Make sure the FTP server you are configuring supports the active FTP mode.Uncheck this to enable FTP active mode.UnlimitedUnpublishedUp to 100 sitesUp to 3 sitesUploadUpload and ImportUploaded:User NameUser PasswordUser's email address is required.Using the option will ignore the file larger than the certain size in MB when backing up, '0' (zero) means unlimited.WASABI/Pcloud (Only Pro)WPDB option has a better compatibility, but the speed of backup and restore is slower.WPvivid Backup PluginWPvivid Backup SettingsWPvivid support email:Warning:Warning: An alias for remote storage is required.Warning: The alias already exists in storage list.Warning: There is no default remote storage available for the scheduled backups, please set up it first.We also offer a 40% off discount on WPvivid Backup Pro for MainWP users.Web Server Directory:Web-server disk space in use by WPvividWebsite Info KeyWebsite Info ValueWebsitesWeeklyWith this option checked, Pages/posts already existing will be overwritten with the updated ones in an import.WordPress Files (Exclude Database)You have authenticated the Dropbox account as your remote storage.You have authenticated the Google Drive account as your remote storage.You have authenticated the Microsoft OneDrive account as your remote storage.You have successfully added a remote storage.You have successfully updated the storage alias.Your email:backups retainede.g.  if you choose a chunk size of 2MB, a 8MB file will use 4 chunks. Decreasing this value will break the ISP's transmission limit, for example:512KBlogsnew featureregion.digitaloceanspaces.comrename directoryrestore failed error unknownrunning time: Project-Id-Version: wpvivid backup plugin
Last-Translator: 刘赓 <420723111@qq.com>
Language-Team: Italian
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
X-Poedit-KeywordsList: __;_e;esc_attr;esc_attr_e
X-Poedit-Basepath: ..
Plural-Forms: nplurals=2; plural=n != 1;
X-Poedit-SourceCharset: utf-8
Report-Msgid-Bugs-To: 
POT-Creation-Date: 2020-05-12 15:27+0000
PO-Revision-Date: 2020-07-27 18:40+0800
Language: it_IT
X-Generator: Loco https://localise.biz/
X-Loco-Version: 2.3.3; wp-5.4.1
X-Poedit-SearchPath-0: .
< Pagina PrecedentePagina Seguente >%d I task di backup sono stati completati.. Vai alla <a href="#" onclick="wpvivid_click_switch_page('wrap', 'wpvivid_tab_log', true);">Pagina di Log</a>  per controllare i dettagli.%s fa&mdash; Seleziona &mdash;*Non è necessaria la carta di credito. La prova inizia con il piano di prova gratuita con 2 siti. È possibile scegliere un piano alla fine della prova.+ Aggiungi un'altra pianificazione- Seleziona -->Se i backup sono memorizzati nello storage remoto, il nostro plugin recupererà prima il backup sul tuo server web. Questo può richiedere un po' di tempo a seconda della dimensione dei file di backup. Si prega di avere pazienza. Poi potrai scaricarli sul vostro PC.->Se i backup sono memorizzati nel server web, il plugin elencherà immediatamente tutti i file rilevanti.1. Crea e scarica i backup per un determinato sito child1. Scarica un backup dall'elenco di backup sul tuo computer.1. Per completare con successo la migrazione, è meglio disattivare <a href="https://wpvivid.com/best-redirect-plugins.html" target="_blank" style="text-decoration: none;">plugin di reindirizzamento 301</a>, <a href="https://wpvivid.com/8-best-wordpress-firewall-plugins. html" target="_blank" style="text-decoration: nessuno;">firewall e plugin di sicurezza</a>, e <a href="https://wpvivid.com/best-free-wordpress-caching-plugins.html" target="_blank" style="text-decoration: nessuno;">plugin di caching</a> (se esistono) prima di trasferire il sito web.1. Visita la scheda Chiave nella pagina di WPvivid backup plugin del sito di destinazione.12 Ore12 ore2. Genera una chiave cliccando il pulsante Genera e copialo.2. Si prega di migrare sito web manualmente quando si utilizza <strong>Local by Flywheel</strong>.2. Pianifica backup per tutti i siti child2. Carica il backup del sito di destinazione. Ci sono due modi disponibili:2.1 Carica il backup nella sezione di caricamento di WPvivid backup plugin nel sito di destinazione.2.2 Carica il backup tramite client FTP nella directory di backup %s del sito di destinazione, quindi fai clic sul pulsante <strong>Scan backup caricato o backup ricevuto</strong>.3. Torna a questa pagina e incolla il codice nella casella chiave sottostante. Infine, fai clic su Salva.3. Una volta fatto, il backup viene visualizzato nell'elenco dei backup. Quindi, ripristina il backup.3. Configura le impostazioni di WPvivid Backup Free e Pro per tutti i siti child4. Installa, attiva e aggiorna WPvivid Backup Pro in massa per i siti child5. Imposta in massa l'archiviazione remota per i siti child (per WPvivid Backup Pro).<strong>Riprovando </strong>%s<strong>volte</strong> quando si verifica un errore di timeout</strong><strong>Suggerimenti: </strong>La connessione instabile tra i siti potrebbe causare un fallimento del trasferimento dei file. In questo caso, il caricamento del backup sul sito di destinazione è una buona alternativa alla migrazione automatica del sito web.È richiesto un tipo di backup.Un nome per aiutarti a identificare lo storage se hai collegato più storage remoti.Un task è già in esecuzione. Si prega di attendere fino a quando il task in esecuzione sia completato, e di provare di nuovo.Riguardo il download dei backup Un percorso assoluto deve esistere(per esempio, /home/nomeutente)Un percorso assoluto deve esistere(ad esempio, /var)AzioneAzioniConfigura le <a href="https://wpvivid.com/wpvivid-backup-plugin-advanced-settings.html" target="_blank">Impostazioni avanzate</a> per una percentuale più alta di successo nello svolgimento delle attività.Regola questo valore da applicare temporaneamente a PHP memory limit per WPvivid backup plugin nel processo di ripristino. Per impostazione predefinita abbiamo regolato questo valore a 256M. Aumenta il valore se si verifica un errore di memoria esaurita,. Nota: per alcuni provider di web hosting questo valore non può essere modificato.Regola questo valore da applicare temporaneamente a PHP memory limit per WPvivid backup plugin nel processo di ripristino. Per impostazione predefinita abbiamo regolato questo valore a 256M. Aumenta il valore se si verifica un errore di memoria esaurita,. Nota: per alcuni provider di web hosting questo valore non può essere modificato.Pianificazione AvanzataImpostazioni AvanzateTutti gli AutoriTutte le CategorieTutti i backup verranno caricati in questa directory.Invia sempre una mail di notifica quando il backup è completoAmazon S3Nome del Bucket di Amazon S3 (ad esempio, test)Amazon S3 access keyAmazon S3 secret keyUn indirizzo e-mail è obbligatorio.Sei sicuro di voler rimuovere il backup? Questi backup verranno eliminati in modo permanente dal tuo hosting (localhost).Sei sicuro di voler rimuovere questo backup? Questo backup verrà eliminato in modo permanente dal tuo hosting (localhost) e storage remoto.Poiché Amazon S3 e DigitalOcean Space hanno aggiornato i loro metodi di connessione, si prega di cancellare le precedenti connessioni e ri-aggiungere i tuoi account tuo Amazon S3/DigitalOcean Space per assicurarti che le connessioni funzionino.Chiedi uno sconto del 40% di scontoAssegna un autoreAutenticati con DropboxAutenticati con Google DriveAutenticati con Microsoft OneDriveAutenticazione non riuscita, il file client_secrets.json è mancante. Si prega di assicurarsi che il file  client_secrets.json sia in wpvivid-backuprestore\include\customclass directory.Autenticazione non riuscita, il formato del file client_secrets.json non è corretto. Si consiglia di cancellare e re-installare il plugin per ricreare il file.AutoreAuto-MigrazioneEsegui Il Backup ManualmenteTorna alla Pagina di ImportazioneIl backup di %s finito.BackupBackup & RipristinoBackup Della CacheCartella Di BackupEsegui il Backup OraPianifica il BackupBackup:BackupDi baseEssendo soggetto a meccanismi di PHP, un'attività di backup pianificata per il vostro sito verrà attivata solo quando il sito riceverà almeno una visita in qualsiasi pagina.Calcola Le DimensioniCalcola la dimensione dei file, delle cartelle e del database prima di eseguire il backupIl calcolo delle dimensioni dei file, delle cartelle e database è terminata a causa di un time out. Se si continua a ricevere questo errore, si prega di andare alle impostazioni dei plug-in, deselezionare 'Calcolare le dimensioni dei file, delle cartelle e database prima di eseguire il backup', salvare le modifiche, quindi provare di nuovo.AnnullaChangeLogSeleziona l'opzione per utilizzare la classe di archiviazione Amazon S3 Standard-Infrequent Access (S3 Standard-IA) per il trasferimento dei dati.Seleziona l'opzione per utilizzare la crittazione lato server di Amazon S3 per proteggere i dati.Evviva! WPvivid Backup plugin ha ripristinato con successo il tuo sito web. Se hai trovato WPvivid Backup plugin utile, una valutazione di 5 stelle sarebbe molto apprezzata, cosa che ci spinge a continuare a offrire nuove funzionalità.Scegli dal computer un'esportazione da importare: Scegli almeno una posizione di archiviazione per il backup.Scegli almeno un tipo di file indesiderato da eliminare.Scegli uno storage dalla lista come storage predefinito.Scegli il tipo di postScegli il contenuto che vuoi trasferireScegli ciò che vuoi esportareScegli ciò che vuoi migrareDimensione del BloccoClasse PclZip non rilevata. Si prega di aggiornare o reinstallare WordPress.Clicca sul pulsante "Esporta" per salvare le impostazioni di WPvivid sul computer locale.Clicca il pulsante per connetterti allo storage di Amazon S3 e aggiungerlo alla lista degli storage qui di seguito.Clicca il pulsante per connetterti allo storage di DigitalOcean Space e di aggiungerlo alla lista degli storage qui di seguito.Clicca il pulsante per connetterti al server FTP e aggiungerlo alla lista degli storage qui di seguito.Clicca il pulsante per connetterti al server SFTP e aggiungerlo alla lista degli storage qui di seguito.Clicca il pulsante per ottenere il codice di autenticazione di Dropbox e aggiungerlo alla lista degli storage qui di seguito.Clicca il pulsante per ottenere il codice di autenticazione di Google e aggiungerlo alla lista degli storage qui di seguito.Clicca il pulsante per ottenere il codice di autenticazione di Microsoft e  aggiungerlo alla lista degli storage qui di seguito.Clicca il pulsante per salvare le modifiche.Clona e TrasferisciCommenta l'esportazione (opzionale)Commenta l'esportazione: CommentiComprimi ogni FileComprimi e ArchiviaContaCrea un Backup ManualeCrea un punto di ripristinoCrea un sito di stagingCrea una sotto-cartella del sito di staging con un solo clicCreazione di attività pianificate non riuscita. Si prega di riprovare più tardi.Pagina CorrenteVersione Corrente:PersonalizzatoContenuto PersonalizzatoDirectory personalizzata (fornitori leader di cloud storage)Percorso PersonalizzatoFuso Orario PersonalizzatoContenuti personalizzati per ogni programmaOrario personalizzato di inizio del programma Personalizza la directory in cui si desidera archiviare i backup all'interno del Bucket.Personalizzare la directory in cui si desidera archiviare i backup all'interno dello Spazio.GiornalieroDatabase + File (File Di WordPress)Dimensione del database:Metodo di accesso al database.DataDebugEliminaElimina I file esportati nella CartellaElimina i backup selezionatiI dettagli sono riportati sopra. L'importatore ora riproverà con un altro parser...DigitalOcean SpacesDigitalOcean Spaces access keyDigitalOcean Spaces secret keyVisualizza il dominio(url) del sito corrente nel nome di backup. (ad esempio dominio_wpvivid-5ceb938b6dca9_2019-05-27-07-36_backup_all.zip)Visualizza le singole sezioni in base ai ruoli e alle capacità dell'utenteScaricaScarica WPvivid di Backup per MainWPRilascia i file quiDropboxRapporti E-Mail:VuotoPost id vuoto Attiva Il Backup IncrementaleAttiva la pianificazione del backupAttiva il report via e-mailAttiva la modalità di ottimizzazione per il web hosting/hosting condivisoAttiva l'opzione quando il backup fallisce.L'attivazione di questa opzione consente di migliorare il tasso di successo del backup, ma richiede più tempo per il backup.Inserisci Il Tuo Account Amazon S3Inserisci l'account dei tuoi Spazi DigitalOceanInserisci le informazioni del tuo account DropboxInserisci il tuo Account FTPInserisci le informazioni del tuo account Google DriveInserisci le informazioni del tuo account Microsoft OneDriveInserisci il Tuo SFTP AccountInserisci un alias univoco: ad esempio, Amazon S3-001Inserisci un alias univoco: ad esempio DOS-001Inserisci un alias univoco: ad esempio Dropbox-001Inserisci un alias univoco: ad esempio Google Drive-001Inserisci un alias univoco: ad esempio OneDrive-001Inserisci un alias univoco: ad esempio, SFTP-001Inserisci un percorso assoluto e una sottodirectory personalizzata (opzionale) per tenere i backup del sito web corrente. Ad esempio, /home/nomeutente/cartellapersonalizzataInserisci un percorso assoluto e una sottodirectory personalizzata (opzionale) per tenere i backup del sito web corrente. Per esempio, /var/cartellapersonalizzata/Inserisci un Bucket esistente per creare una directory di archiviazione di backup personalizzata.Inserisci uno Spazio esistente per creare una directory di archiviazione di backup personalizzata.Inserisci un alias univoco: ad esempio FTP-001Inserisci l'Endpoint di DigitalOcean per l'archiviazioneInserisci la password del server FTP.Inserisci il server FTP.Inserisci l'indirizzo del server.Inserisci la porta del server.Inserisci il nome utente.Inserisci la password utente.Inserisci la tua access key di Amazon S3.Inserisci la tua secret key di Amazon S3.Inserisci l'access key degli Spazi DigitalOceanInserisci la secret key dei tuoi Spazi DigitalOceanInserisci il nome utente del tuo server FTP.Si è verificato un errore durante l'analisi dei dati della richiesta. Si prega di provare a eseguire di nuovo il backup.Si è verificato un errore durante l'analisi dei dati della richiesta. Si prega di provare a eseguire di nuovo l'attività di esportazione.Errore:Escludi i file che sono più grandi diEsportaEsporta %s di tutti gli autori o un autore specifico.Esporta %s tutte le categorie o una categoria specifica.Esporta %s", pubblicati dopo questa data.Esportare %s pubblicati prima di questa data.Esporta & ImportaEsporta il contenutoEsporta e ScaricaErrore di esportazione:Esporta in massa i post o le pagine con immagini.I file esportati saranno temporaneamente memorizzati nella directory %sFTPAccesso FTPPassword FTPFTP server (server sulla porta 21)Impossibile cancellare lo storage remoto, non è possibile recuperare le informazioni sulla storage. Si prega di riprovare.Impossibile ottenere le informazioni di archiviazione remota. Si prega di riprovare più tardi.Il recupero degli allegati non è abilitatoNome del FileDimensione del File:File non esiste file:Il File non esiste, file:File non trovato. si prega di riprovare di nuovo.La dimensione del file non corrisponde. Riprovare di nuovo.Filtro Messaggi/PaginePrima paginaQuindicinaleFreelanceImpostazioni GeneraliGeneraImpossibile ottenere la directory di backup. Riprovare più tardi.Google DriveL'icona evidenziata indica che è stata scelta uno storage remoto per memorizzare i backupCome ottenere una site key?Come ottenere una access key di AmazonS3.Come ottenere una secret key di AmazonS3.Come migrare manualmente il sito Wordpress su un nuovo dominio (sito) con il plugin di backup WPvivid?Come ripristinare il vostro sito web da un backup(programmato, manuale, caricato e ricevuto)Come fareIo sto utilizzando:Se sei un utente MainWP, puoi impostare e controllare WPvivid Backup Free e Pro per ogni sito child direttamente dalla Bacheca di MainWP, utilizzando la nostra estensione WPvivid Backup for MainWP.Se non hai configurato l'SMTP sul tuo sito, clicca sul pulsante qui sotto per scaricare le informazioni rilevanti (informazioni sul sito web e registri degli errori) sul tuo PC quando incontrate degli errori. L'invio dei file ci aiuterà a diagnosticare l'accaduto.Se è stato configurato l'SMTP sul tuo sito, inserisci il tuo indirizzo e-mail e fai clic sul pulsante qui sotto per inviarci le informazioni rilevanti (informazioni sul sito e gli errori di log) quando si verificano errori. Questo ci aiuterà a capire cosa è successo. Una volta risolto il problema, provvederemo ad informarti per tuo indirizzo e-mail.ImportazioneImpostazione di ImportazioneImporta in massa post o pagine con immagini.I file importati saranno temporaneamente memorizzati nella directory %sL'importazione del file json può aiutare ad impostare rapidamente la configurazione di WPvivid su un altro sito WordPress.Per consentire ad un altro sito di inviare un backup a questo sito, si prega di generare una chiave qui sotto. Una volta generata la chiave, questo sito è pronto a ricevere un backup da un altro sito. Quindi, si prega di copiare e incollare la chiave nel sito di invio e salvarla.Per eseguire correttamente i backup pianificati, impostare la costante DISABLE_WP_CRON su false.Per utilizzare questa funzione, si prega di installare un <strong><a target="_blank" href="https://wpvivid.com/8-best-smtp-plugins-for-wordpress.html" style="text-decoration: none;">plugin di SMTP di WordPress </a></strong> di tua preferenza e configurare prima il tuo server SMTP. Questo perché WordPress utilizza la funzione PHP Mail per inviare le sue e-mail di default, che non è supportata da molti host e può causare problemi se non è impostata correttamente.Includere/escludere i file e le cartelleBackup IncrementalePianificazione Di Backup IncrementaleIndirizzo email non validoTipo di file non validoSi consiglia di scegliere l'opzione PDO se l'estensione pdo_mysql è installata sul vostro server, che consente di eseguire il backup e il ripristino del sito più velocemente.Provocherà un maggiore utilizzo della CPU ed è raccomandato in un ambiente di hosting dedicato o VPS.Ciò comporterà un minore utilizzo della CPU e non è raccomandato in un web hosting/ ambiente di hosting condiviso.SpazzaturaContinua a memorizzare i backup in localhost dopo l'upload nello storage remotoChiaveUltimo Backup: Ultima ModificaUltima paginaPer saperne di piùDirectory di storage locale:Directory di archiviazione locale:RegistroNome del File di LogTipo di LogLogsMainWPForse Più TardiDimensione dei file multimedialiUnisci tutti i file di backup in un unico pacchetto quando un backup è completato. In questo modo si risparmiano grandi spazi su disco, anche se ci vuole più tempo. Si consiglia di controllare l'opzione soprattutto su siti con risorse server insufficienti.Metodo 1.Metodo 2.Microsoft OneDriveMigra WordPressLa migrazione è completa e il file htaccess è stato sostituito. Per completare con successo la migrazione, è meglio reinstallare il plugin 301 redirect, il plugin firewall e il plugin di sicurezza e il plugin di caching, se esistono.Migrazione tramite archiviazione remotaMigrazione:Programmazione mancataMensileAltri tipi di post in arrivo...Il mio provider di web hosting è:Dai un nome alla tua cartella, questa cartella deve essere scrivibile per la creazione di file di backup.Connessione di rete:MaiBackup Successivo: Prossimo passoPagina successivaNota:Nota: Nota: I file che si desidera caricare devono essere un backup creato dal plugin di backup di WPvivid. Assicurarsi di aver caricato ogni parte del backup nella directory se il backup è suddiviso in più partiUna volta spuntati, tutti i backup di questi siti inviati a una destinazione di archiviazione remota saranno caricati in questo storage di default.Solo archivio senza compressioneSolo DatabaseSono ammesse solo lettere (eccetto per wpvivid) e numeri.Invia una notifica via e-mail solo quando un backup non riesceOppure puoi usare ftp per caricare il file di esportazione nella directory %s. Quindi clicca sul pulsante qui sotto per scansionare il file da importare.Sovrascrivere le pagine esistentiLimite di memoria PHP per il backupLimite di memoria PHP per il ripristinoTimeout di esecuzione dello script PHP per il backupTimeout di esecuzione dello script PHP per il ripristinoPaginaPercorso:Scegli un solo storage per salvare i tuoi backup (storage remoto)Si prega di descrivere il problema qui.Si prega di non chiudere la pagina o di non passare ad altre pagine quando è in corso un'attività di ripristino, in quanto potrebbe innescare alcuni errori imprevisti.Si prega di inserire un indirizzo email valido.Si prega di incollare la chiave qui di seguito.Si prega di leggere <a target="_blank" href="https://wpvivid.com/privacy-policy" style="text-decoration: none;">questa informativa sulla privacy</a> per l'utilizzo della nostra applicazione di autorizzazione per Dropbox (nessuno dei dati di backup viene inviato a noi).Si prega di leggere <a target="_blank" href="https://wpvivid.com/privacy-policy" style="text-decoration: none;">questa informativa sulla privacy</a> per l'utilizzo della nostra applicazione di autorizzazione a Google Drive (nessuno dei dati di backup viene inviato a noi).Si prega di leggere <a target="_blank" href="https://wpvivid.com/privacy-policy" style="text-decoration: none;">questa informativa sulla privacy</a> per l'utilizzo della nostra applicazione di autorizzazione a Microsoft OneDrive (nessuno dei dati di backup viene inviato a noi).Si prega di selezionare almeno un elemento.PortaPostTipi di postPremiumPagina precedenteFunzionalità della versione ProFunzionalità Pro: aggiungi un altro indirizzo email per ottenere reportFunzionalità Pro: cambia il numero di porta predefinito FTPFunzionalità Pro: crea una cartella per memorizzare il backup del sitoFunzionalità Pro: conserva più copie di backupFunzionalità Pro: per saperne di piùAvanzamento:Pubblica un sito di staging su un sito live con un solo clicPubblicatoVotaciLeggi <a href="https://wpvivid.com/troubleshooting-issues-wpvivid-backup-plugin" target="_blank">la pagina di Risoluzione dei problemi</a> per soluzioni più rapide.La lettura del registro dei log non è riuscita. Riprovare.Pronti per il backup. Progressi: 0%, tempo di esecuzione: 0 secondi.Pronto per l'esportazione. Progressi: 0%, tempo di esecuzione: 0 secondi.Archiviazione RemotaArchiviazione Remota AliasArchiviazione Remota Directory:Archiviazione Remota:RimuovereRimuovi i backup obsoletiRipristinaRipristina il tuo sito da un backupRipristina un sito web dallo storage remotoRipristina e mantenere il dominio originale (URL) invariatoRipristina e sostituisci il dominio originale (URL) con %s (migrazione)Ripristino backup da:Ripristino completato.La funzione Ripristino sostituirà i temi, i plugin, gli upload, i database e/o altre directory di contenuti del sito corrente con gli equivalenti esistenti nel backup selezionato.Ripristina ciò che vuoi da un backupRipristino:Recupera il backup su localhostIl recupero delle informazioni di backup non è riuscito mentre si mostrava il log. Riprovare più tardi.Il recupero delle informazioni del backup non è riuscito mentre si cancellavano i backup selezionati. Riprovare più tardi.Ruoli e capacità (add-on):RollbackSFTPINIZIA LA PROVA GRATUITA DI 14 GIORNICampione:SalvaSalva i backup in localeSalvare le modificheSalva i backup su localhost (server web)Scansiona Esportazioni CaricateScansiona backup caricato o ricevutoPianificazioneImpostazioni Di PianificazioneStato di Pianificazione: Pianificazione:PianificateIl lavoro pianificato inizierà a <strong>UTC</strong> time:RicercaRicerca per %s secondo le regole di cui sopra.Seleziona TuttoSelezionare i FileSelezionare un autore esistente:Invia il backup allo storage remoto:Invia le Informazioni di Debug a NoiInvia i backup allo storage remoto (scegli questa opzione, il backup locale verrà cancellato dopo averlo caricato completamente nello storage remoto)Invia rapporti e-mail a più indirizzi e-mailIndirizzo del ServerOra del Server: Crittografia Lato Server.Impostato come storage remoto predefinito.ImpostazioniMostra WPvivid backup plugin in cima all'admin barAlcuni fornitori di web hosting limitano i file zip di grandi dimensioni (ad esempio 200 MB), e quindi dividere il backup in molte parti è un modo ideale per evitare di colpire la limitazione se si esegue un grande sito web.  Si prega di provare a regolare tale valore se si riscontrano errori di backup. Se si utilizza un valore di 0 MB, i file di backup non verranno divisi.Mi dispiace, questo tipo di file non è consentito per motivi di sicurezza.Nome dello spazio(ad esempio test)Ottimizzazione speciale per il web hosting/hosting condivisoVelocità:Staging (add-on):Avvia il backup di %s.Inizia l'ImportazionePasso 1: Nell'elenco dei backup, fare clic sul pulsante 'Ripristina' del backup che si desidera ripristinare. In questo modo si aprirà la scheda di ripristinoFase Tre: fare Clic su pulsante 'Ripristina' Fase Due: Scegliere una opzione per completare il ripristino, se presenteStorageModifica StorageStorage ProviderClasse di storage: Standard (accesso non frequente).StorageSupporto:Task time out.Task è scaduto.File TemporaneiI file temporanei vengono creati da wpvivid durante il ripristino di un sito web.TerminaE-Mail di ProvaTesta e AggiungiL'estensione %s non è stata rilevata. Si prega di installare prima l'estensione.Le estensioni %s non sono state rilevate. Si prega di installare prima le estensioni.L'azione è irreversibile! Rimuove tutti i backup non aggiornati (incluso il server web locale e l'archiviazione remota) se presenti.Il backup viene memorizzato sullo storage remoto, fai clic sul pulsante per scaricarlo su localhost.Il backup sarà annullato dopo aver eseguito il backup delle parti correnti.I backup verranno caricati nella directory %s.Il task di esportazione non risponde.La funzione può aiutare a trasferire un sito Wordpress su un nuovo dominio (sito). Sarebbe un modo conveniente per migrare il vostro sito WP dall'ambiente dev al live server o dal vecchio server al nuovo.Le informazioni sul file di importazioneLa chiave scadrà in L'ultimo messaggio di backup non è stato trovato.È necessario il percorso di storage locale.Log non trovato.È richiesto il tempo di esecuzione massimo per gli script PHP.È richiesta la dimensione massima del file zip.L'estensione pdo_mysql non viene rilevata. Si prega di installare prima l'estensione o di scegliere l'opzione wpdb per il metodo di connessione al database.Il plugin rileva automaticamente il ripristino del sito o la migrazione (sostituzione del nome a dominio) in base al nome a dominio corrente. Se il nome del dominio nel file di backup è uguale a quello attuale, inizia il ripristino. Al contrario, ripristinare il backup significa sostituire con il nome di dominio corrente. Il presupposto è che il backup venga creato dalla versione 0.9.21 o successiva.Il file di ripristino non è stato trovato. Si prega di verificare che il file esista.Il file selezionato non è il file di impostazione di WPvivid. Si prega di caricare il file giusto.I file spazzatura selezionati sono stati cancellati.Le impostazioni sono solo per il backup manuale, che non influisce sulle impostazioni di programmazione.L'estensione simplexml non viene rilevata. Si prega di installare prima l'estensione.È richiesta la dimensione per i file esclusi.Il time-out non è il time-out del vostro server PHP. Con il tempo di esecuzione esaurito, il nostro plugin interromperà il processo di backup. Se l'avanzamento del backup incontra un time-out, ciò significa che hai un sito web di medie o grandi dimensioni, prova ad aumentare il valore.Il time-out non è il time-out del vostro server PHP. Con il tempo di esecuzione esaurito, il nostro plugin chiuderà il processo di ripristino. Se il processo di ripristino incontra un time-out, ciò significa che avete un sito web di medie o grandi dimensioni, prova ad aumentare il valore.Ci sono due modi per inviarci le informazioni di debug. Il primo è raccomandato.Non è stata configurata una memoria remota predefinita. Si prega di impostarla prima.Si è verificato un errore durante la lettura di questo file WXRQuesto backup può essere cancellato solo manualmenteQuesto backup è bloccato, sei sicuro di poterlo rimuovere? Questo backup sarà cancellato in modo permanente dal vostro hosting (localhost) e dai magazzini remoti.Questo non sembra essere un file WXR, numero di versione WXR mancante/non validoQuesta richiesta potrebbe cancellare il backup in fase di download, sei sicuro di voler continuare?Questa richiesta cancellerà il backup in fase di download, sei sicuro di voler continuare?Questo conterrà tutti i vostri post, pagine, commenti, termini e immagini (immagini originali, immagini in primo piano e miniature).Ticket di supporto 7x24 Suggerimenti:Suggerimenti: Fare clic sul pulsante qui sotto per scansionare tutti i backup caricati o ricevuti nella directorySuggerimenti: Per motivi di sicurezza, si prega di scegliere un tempo di scadenza appropriato per la chiave.Per visualizzare correttamente il contenuto importato, si prega di assicurarsi che l'importazione e l'esportazione siti abbiano lo stesso ambiente, per esempio, lo stesso tema o pagine costruite con lo page-builder.Troppi tentativi di ripresa.Dimensione Totale:Il trasferimento è riuscito. Si prega di scansione l'elenco di backup del sito di destinazione per visualizzare il backup e poi ripristinare il backup.Risoluzione dei problemiProvare a selezionare un numero inferiore di elementi quando si trovano di fronte a una carenza di risorse del server (in genere presentato come un errore di timeout).Tipo:Tipo: UltimateNon è possibile cancellare il backup bloccato. Si prega di sbloccarlo prima e provare di nuovo.Impossibile aprire il file di registro.Impossibile inviare e-mail. Si prega di controllare la configurazione del server di posta elettronica.Deselezionare l'opzione per utilizzare l'FTP in modalità attiva quando il trasferimento di file. Assicurarsi che il server FTP configurazione supporti la modalità FTP attiva.Deseleziona questa opzione per abilitare il server FTP in modalità attiva.IllimitatoIneditoFino a 100 sitiFino a 3 sitiCaricaCarica e ImportaCaricato:Nome UtentePassword UtenteIndirizzo email dell'utente è obbligatorio.Usando l'opzione si ignorerà il file più grande della dimensione determinata in MB durante il backup, '0' (zero) significa illimitato.WASABI/Pcloud (Solo Pro)L'opzione WPDB ha una migliore compatibilità, ma la velocità di backup e ripristino è più lenta.WPvivid Backup PluginImpostazioni di Backup WPvivid Supporto e-mail di WPvivid :Attenzione:Attenzione: Un alias per l'archiviazione remota è necessario.Attenzione: l'alias esiste già nella lista degli storage.Attenzione: non è disponibile uno storage remoto predefinito per i backup pianificati, si prega di impostarla prima.Offriamo anche uno sconto del 40% su WPvivid Backup Pro per gli utenti MainWP.Directory Del Server Web:Spazio su disco del server web in uso da WPvividInfo Key del sito webValore Info del sito web Siti webSettimanaleCon questa opzione selezionata, le pagine/pubbliche già esistenti saranno sovrascritte con quelle aggiornate in un'importazione.File WordPress (Escludi database)Hai autenticato l'account Dropbox come tuo storage remoto.Hai autenticato l'account Google Drive come tuo storage remoto.Hai autenticato l'account Microsoft OneDrive come tuo storage remoto.Hai aggiunto con successo uno storage remoto.Hai aggiornato con successo l'alias di archiviazione.La tua email:backup conservatiad esempio, se si sceglie una dimensione del blocco di 2MB, un file da 8MB userà 4 blocchi. Ridurre questo valore per spezzare il limite di trasmissione ISP, ad esempio:512 KBlogsnuova funzionalitàregion.digitaloceanspaces.comrinomina directoryripristino non riuscito errore sconosciutotempo di esecuzione: languages/wpvivid-backuprestore.pot000064400000205651151327705670013623 0ustar00msgid ""
msgstr ""
"Project-Id-Version: wpvivid backup plugin\n"
"Last-Translator: Team WPvivid <support@wpvivid.com>\n"
"Language-Team: Team WPvivid <support@wpvivid.com>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Poedit-KeywordsList: __;_e;esc_attr;esc_attr_e\n"
"X-Poedit-Basepath: ..\n"
"Plural-Forms: nplurals=2; plural=(n!=1);\n"
"X-Poedit-SourceCharset: utf-8\n"
"X-Poedit-SearchPath-0: .\n"

#: admin/class-wpvivid-admin.php:100
#: admin/class-wpvivid-admin.php:114
#: admin/class-wpvivid-admin.php:635
msgid "Backup & Restore"
msgstr ""

#: admin/class-wpvivid-admin.php:123
#: admin/class-wpvivid-admin.php:313
#: admin/class-wpvivid-admin.php:653
msgid "Settings"
msgstr ""

#: admin/class-wpvivid-admin.php:139
msgid "Current Version:"
msgstr ""

#: admin/class-wpvivid-admin.php:142
msgid "ChangeLog"
msgstr ""

#: admin/class-wpvivid-admin.php:149
msgid "Troubleshooting"
msgstr ""

#: admin/class-wpvivid-admin.php:154
msgid "Read <a href=\"https://wpvivid.com/troubleshooting-issues-wpvivid-backup-plugin\" target=\"_blank\">Troubleshooting page</a> for faster solutions."
msgstr ""

#: admin/class-wpvivid-admin.php:157
msgid "Adjust <a href=\"https://wpvivid.com/wpvivid-backup-plugin-advanced-settings.html\" target=\"_blank\">Advanced Settings</a> for higher task success rate."
msgstr ""

#: admin/class-wpvivid-admin.php:164
msgid "How-to"
msgstr ""

#: admin/class-wpvivid-admin.php:168
msgid "WPvivid Backup Settings"
msgstr ""

#: admin/class-wpvivid-admin.php:169
msgid "Create a Manual Backup"
msgstr ""

#: admin/class-wpvivid-admin.php:170
msgid "Restore Your Site from a Backup"
msgstr ""

#: admin/class-wpvivid-admin.php:171
msgid "Migrate WordPress"
msgstr ""

#: admin/class-wpvivid-admin.php:209
msgid "Warning:"
msgstr ""

#: admin/class-wpvivid-admin.php:210
msgid "Error:"
msgstr ""

#: admin/class-wpvivid-admin.php:211
msgid "Warning: An alias for remote storage is required."
msgstr ""

#: admin/class-wpvivid-admin.php:212
msgid "Warning: The alias already exists in storage list."
msgstr ""

#: admin/class-wpvivid-admin.php:213
msgid "Calculating the size of files, folder and database timed out. If you continue to receive this error, please go to the plugin settings, uncheck 'Calculate the size of files, folder and database before backing up', save changes, then try again."
msgstr ""

#: admin/class-wpvivid-admin.php:346
msgid "Migration is complete and htaccess file is replaced. In order to successfully complete the migration, you'd better reinstall 301 redirect plugin, firewall and security plugin, and caching plugin if they exist."
msgstr ""

#: admin/class-wpvivid-admin.php:361
msgid "Cheers! WPvivid Backup plugin has restored successfully your website. If you found WPvivid Backup plugin helpful, a 5-star rating would be highly appreciated, which motivates us to keep providing new features."
msgstr ""

#: admin/class-wpvivid-admin.php:367
msgid "Restore completed successfully."
msgstr ""

#: admin/class-wpvivid-admin.php:454
#: admin/class-wpvivid-admin.php:471
msgid "Rate Us"
msgstr ""

#: admin/class-wpvivid-admin.php:455
#: admin/class-wpvivid-admin.php:472
msgid "Maybe Later"
msgstr ""

#: admin/class-wpvivid-admin.php:456
#: admin/class-wpvivid-admin.php:473
msgid "Never"
msgstr ""

#: admin/class-wpvivid-admin.php:509
msgid "As Amazon S3 and DigitalOcean Space have upgraded their connection methods, please delete the previous connections and re-add your Amazon S3/DigitalOcean Space accounts to make sure the connections work."
msgstr ""

#: admin/class-wpvivid-admin.php:561
#, php-format
msgid "The %s extension is not detected. Please install the extension first."
msgstr ""

#: admin/class-wpvivid-admin.php:564
#, php-format
msgid "The %s extensions are not detected. Please install the extensions first."
msgstr ""

#: admin/class-wpvivid-admin.php:570
msgid "Class PclZip is not detected. Please update or reinstall your WordPress."
msgstr ""

#: admin/class-wpvivid-admin.php:575
msgid "In order to execute the scheduled backups properly, please set the DISABLE_WP_CRON constant to false."
msgstr ""

#: admin/class-wpvivid-admin.php:641
msgid "Schedule"
msgstr ""

#: admin/class-wpvivid-admin.php:647
msgid "Remote Storage"
msgstr ""

#: admin/class-wpvivid-admin.php:659
msgid "Debug"
msgstr ""

#: admin/class-wpvivid-admin.php:665
msgid "Logs"
msgstr ""

#: admin/class-wpvivid-admin.php:672
#: admin/partials/wpvivid-backup-restore-page-display.php:130
#: admin/partials/wpvivid-backup-restore-page-display.php:1505
msgid "Log"
msgstr ""

#: admin/class-wpvivid-admin.php:683
msgid "MainWP"
msgstr ""

#: admin/class-wpvivid-admin.php:693
msgid "Premium"
msgstr ""

#: admin/class-wpvivid-admin.php:735
#: admin/class-wpvivid-admin.php:822
#: admin/partials/wpvivid-remote-storage-page-display.php:48
msgid "Save Changes"
msgstr ""

#: admin/class-wpvivid-admin.php:870
msgid "There are two ways available to send us the debug information. The first one is recommended."
msgstr ""

#: admin/class-wpvivid-admin.php:872
msgid "Method 1."
msgstr ""

#: admin/class-wpvivid-admin.php:872
msgid "If you have configured SMTP on your site, enter your email address and click the button below to send us the relevant information (website info and errors logs) when you are encountering errors. This will help us figure out what happened. Once the issue is resolved, we will inform you by your email address."
msgstr ""

#: admin/class-wpvivid-admin.php:875
msgid "WPvivid support email:"
msgstr ""

#: admin/class-wpvivid-admin.php:876
msgid "Your email:"
msgstr ""

#: admin/class-wpvivid-admin.php:881
msgid "I am using:"
msgstr ""

#: admin/class-wpvivid-admin.php:893
msgid "My web hosting provider is:"
msgstr ""

#: admin/class-wpvivid-admin.php:902
msgid "Please describe your problem here."
msgstr ""

#: admin/class-wpvivid-admin.php:905
msgid "Send Debug Information to Us"
msgstr ""

#: admin/class-wpvivid-admin.php:909
msgid "Method 2."
msgstr ""

#: admin/class-wpvivid-admin.php:909
msgid "If you didn’t configure SMTP on your site, click the button below to download the relevant information (website info and error logs) to your PC when you are encountering some errors. Sending the files to us will help us diagnose what happened."
msgstr ""

#: admin/class-wpvivid-admin.php:912
#: admin/partials/wpvivid-backup-restore-page-display.php:178
msgid "Download"
msgstr ""

#: admin/class-wpvivid-admin.php:916
msgid "Website Info Key"
msgstr ""

#: admin/class-wpvivid-admin.php:917
msgid "Website Info Value"
msgstr ""

#: admin/class-wpvivid-admin.php:1030
msgid "Date"
msgstr ""

#: admin/class-wpvivid-admin.php:1031
msgid "Log Type"
msgstr ""

#: admin/class-wpvivid-admin.php:1032
msgid "Log File Name"
msgstr ""

#: admin/class-wpvivid-admin.php:1033
msgid "Action"
msgstr ""

#: admin/class-wpvivid-admin.php:1045
msgid " < Pre page "
msgstr ""

#: admin/class-wpvivid-admin.php:1056
msgid " Next page > "
msgstr ""

#: admin/class-wpvivid-admin.php:1164
msgid "If you are a MainWP user, you can set up and control WPvivid Backup Free and Pro for every child site directly from your MainWP dashboard, using our WPvivid Backup for MainWP extension."
msgstr ""

#: admin/class-wpvivid-admin.php:1167
msgid "Download WPvivid Backup for MainWP"
msgstr ""

#: admin/class-wpvivid-admin.php:1170
msgid "1. Create and download backups for a specific child site"
msgstr ""

#: admin/class-wpvivid-admin.php:1173
msgid "2. Set backup schedules for all child sites"
msgstr ""

#: admin/class-wpvivid-admin.php:1177
msgid "3. Set WPvivid Backup Free and Pro settings for all child sites"
msgstr ""

#: admin/class-wpvivid-admin.php:1182
msgid "4. Install, claim and update WPvivid Backup Pro for child sites in bulk"
msgstr ""

#: admin/class-wpvivid-admin.php:1187
msgid "5. Set up remote storage for child sites in bulk (for WPvivid Backup Pro only)"
msgstr ""

#: admin/class-wpvivid-admin.php:1192
#, php-format
msgid "We also offer a 40% off discount on WPvivid Backup Pro for MainWP users."
msgstr ""

#: admin/class-wpvivid-admin.php:1196
msgid "Ask For A 40% OFF Discount"
msgstr ""

#: admin/class-wpvivid-admin.php:1219
msgid "Pro Version Features"
msgstr ""

#: admin/class-wpvivid-admin.php:1220
msgid "Basic"
msgstr ""

#: admin/class-wpvivid-admin.php:1221
msgid "Freelancer"
msgstr ""

#: admin/class-wpvivid-admin.php:1222
msgid "Ultimate"
msgstr ""

#: admin/class-wpvivid-admin.php:1228
msgid "Websites"
msgstr ""

#: admin/class-wpvivid-admin.php:1229
#: admin/class-wpvivid-admin.php:1230
#: admin/class-wpvivid-admin.php:1231
#: admin/class-wpvivid-admin.php:1232
msgid "Backup:"
msgstr ""

#: admin/class-wpvivid-admin.php:1229
#: admin/class-wpvivid-admin.php:1233
msgid "Custom Content"
msgstr ""

#: admin/class-wpvivid-admin.php:1230
msgid "Incremental Backup"
msgstr ""

#: admin/class-wpvivid-admin.php:1231
msgid "Create a restore point"
msgstr ""

#: admin/class-wpvivid-admin.php:1232
msgid "Include/exclude files/folders"
msgstr ""

#: admin/class-wpvivid-admin.php:1233
#: admin/class-wpvivid-admin.php:1234
msgid "Migration:"
msgstr ""

#: admin/class-wpvivid-admin.php:1234
msgid "Migration via remote storage"
msgstr ""

#: admin/class-wpvivid-admin.php:1235
#: admin/class-wpvivid-admin.php:1236
msgid "Remote Storage:"
msgstr ""

#: admin/class-wpvivid-admin.php:1235
msgid "Custom Directory (leading cloud storage providers)"
msgstr ""

#: admin/class-wpvivid-admin.php:1236
msgid "WASABI/Pcloud (Only Pro)"
msgstr ""

#: admin/class-wpvivid-admin.php:1237
#: admin/class-wpvivid-admin.php:1238
#: admin/class-wpvivid-admin.php:1239
#: admin/class-wpvivid-admin.php:1240
msgid "Schedule:"
msgstr ""

#: admin/class-wpvivid-admin.php:1237
msgid "Incremental Backup Schedule"
msgstr ""

#: admin/class-wpvivid-admin.php:1238
msgid "Custom Timezone"
msgstr ""

#: admin/class-wpvivid-admin.php:1239
msgid "Custom content for each schedule"
msgstr ""

#: admin/class-wpvivid-admin.php:1240
msgid "Custom start time of schedule"
msgstr ""

#: admin/class-wpvivid-admin.php:1241
#: admin/class-wpvivid-admin.php:1242
msgid "Restore:"
msgstr ""

#: admin/class-wpvivid-admin.php:1241
msgid "Restore a website from remote storage"
msgstr ""

#: admin/class-wpvivid-admin.php:1242
msgid "Restore what you want from a backup"
msgstr ""

#: admin/class-wpvivid-admin.php:1243
msgid "Email Reports:"
msgstr ""

#: admin/class-wpvivid-admin.php:1243
msgid "Send email reports to multiple email addresses"
msgstr ""

#: admin/class-wpvivid-admin.php:1244
#: admin/class-wpvivid-admin.php:1245
msgid "Staging (add-on):"
msgstr ""

#: admin/class-wpvivid-admin.php:1244
msgid "Create a sub-directory staging site with one-click"
msgstr ""

#: admin/class-wpvivid-admin.php:1245
msgid "Publish a staging site to a live site with one-click"
msgstr ""

#: admin/class-wpvivid-admin.php:1246
msgid "Roles & Capabilities (add-on):"
msgstr ""

#: admin/class-wpvivid-admin.php:1246
msgid "Display the individual sections according to user roles & capabilities"
msgstr ""

#: admin/class-wpvivid-admin.php:1247
msgid "Support:"
msgstr ""

#: admin/class-wpvivid-admin.php:1247
msgid "Ticket 7x24 support"
msgstr ""

#: admin/class-wpvivid-admin.php:1250
msgid "Up to 3 sites"
msgstr ""

#: admin/class-wpvivid-admin.php:1272
msgid "Up to 100 sites"
msgstr ""

#: admin/class-wpvivid-admin.php:1294
msgid "Unlimited"
msgstr ""

#: admin/class-wpvivid-admin.php:1319
msgid "*No credit card needed. Trial starts with the Free Trial plan with 2 sites. You can choose a plan at the end of the trial."
msgstr ""

#: admin/class-wpvivid-admin.php:1320
msgid "START 14-DAY FREE TRIAL"
msgstr ""

#: admin/partials/wpvivid-admin-display.php:49
msgid "WPvivid Backup Plugin"
msgstr ""

#: admin/partials/wpvivid-admin-display.php:63
msgid "Warning: There is no default remote storage available for the scheduled backups, please set up it first."
msgstr ""

#: admin/partials/wpvivid-backup-restore-page-display.php:7
#: admin/partials/wpvivid-schedule-page-display.php:126
msgid "Database + Files (WordPress Files)"
msgstr ""

#: admin/partials/wpvivid-backup-restore-page-display.php:11
#: admin/partials/wpvivid-schedule-page-display.php:131
msgid "WordPress Files (Exclude Database)"
msgstr ""

#: admin/partials/wpvivid-backup-restore-page-display.php:15
#: admin/partials/wpvivid-schedule-page-display.php:136
msgid "Only Database"
msgstr ""

#: admin/partials/wpvivid-backup-restore-page-display.php:20
msgid "Create a staging site"
msgstr ""

#: admin/partials/wpvivid-backup-restore-page-display.php:23
#: admin/partials/wpvivid-backup-restore-page-display.php:32
#: admin/partials/wpvivid-schedule-page-display.php:24
#: admin/partials/wpvivid-schedule-page-display.php:36
#: admin/partials/wpvivid-schedule-page-display.php:98
#: admin/partials/wpvivid-schedule-page-display.php:146
msgid "Pro feature: learn more"
msgstr ""

#: admin/partials/wpvivid-backup-restore-page-display.php:29
#: admin/partials/wpvivid-schedule-page-display.php:142
msgid "Custom"
msgstr ""

#: admin/partials/wpvivid-backup-restore-page-display.php:100
msgid "About backup download"
msgstr ""

#: admin/partials/wpvivid-backup-restore-page-display.php:102
msgid "->If backups are stored in remote storage, our plugin will retrieve the backup to your web server first. This may take a little time depending on the size of backup files. Please be patient. Then you can download them to your PC."
msgstr ""

#: admin/partials/wpvivid-backup-restore-page-display.php:103
msgid "->If backups are stored in web server, the plugin will list all relevant files immediately."
msgstr ""

#: admin/partials/wpvivid-backup-restore-page-display.php:109
msgid "How to restore your website from a backup(scheduled, manual, uploaded and received backup)"
msgstr ""

#: admin/partials/wpvivid-backup-restore-page-display.php:123
msgid "Backups"
msgstr ""

#: admin/partials/wpvivid-backup-restore-page-display.php:141
#: admin/partials/wpvivid-backup-restore-page-display.php:179
#: admin/partials/wpvivid-backup-restore-page-display.php:892
msgid "Restore"
msgstr ""

#: admin/partials/wpvivid-backup-restore-page-display.php:176
msgid "Backup"
msgstr ""

#: admin/partials/wpvivid-backup-restore-page-display.php:177
msgid "Storage"
msgstr ""

#: admin/partials/wpvivid-backup-restore-page-display.php:180
msgid "Delete"
msgstr ""

#: admin/partials/wpvivid-backup-restore-page-display.php:193
msgid "Delete the selected backups"
msgstr ""

#: admin/partials/wpvivid-backup-restore-page-display.php:725
msgid "This backup is locked, are you sure to remove it? This backup will be deleted permanently from your hosting (localhost) and remote storages."
msgstr ""

#: admin/partials/wpvivid-backup-restore-page-display.php:729
msgid "Are you sure to remove this backup? This backup will be deleted permanently from your hosting (localhost) and remote storages."
msgstr ""

#: admin/partials/wpvivid-backup-restore-page-display.php:734
msgid "This request will delete the backup being downloaded, are you sure you want to continue?"
msgstr ""

#: admin/partials/wpvivid-backup-restore-page-display.php:773
msgid "Please select at least one item."
msgstr ""

#: admin/partials/wpvivid-backup-restore-page-display.php:778
msgid "This request might delete the backup being downloaded, are you sure you want to continue?"
msgstr ""

#: admin/partials/wpvivid-backup-restore-page-display.php:781
msgid "Are you sure to remove the selected backups? These backups will be deleted permanently from your hosting (localhost)."
msgstr ""

#: admin/partials/wpvivid-backup-restore-page-display.php:842
msgid "Step One: In the backup list, click the 'Restore' button on the backup you want to restore. This will bring up the restore tab"
msgstr ""

#: admin/partials/wpvivid-backup-restore-page-display.php:843
msgid "Step Two: Choose an option to complete restore, if any"
msgstr ""

#: admin/partials/wpvivid-backup-restore-page-display.php:844
msgid "Step Three: Click 'Restore' button"
msgstr ""

#: admin/partials/wpvivid-backup-restore-page-display.php:877
msgid "Restore backup from:"
msgstr ""

#: admin/partials/wpvivid-backup-restore-page-display.php:878
msgid "Please do not close the page or switch to other pages when a restore task is running, as it could trigger some unexpected errors."
msgstr ""

#: admin/partials/wpvivid-backup-restore-page-display.php:879
msgid "Restore function will replace the current site's themes, plugins, uploads, database and/or other content directories with the existing equivalents in the selected backup."
msgstr ""

#: admin/partials/wpvivid-backup-restore-page-display.php:882
#, php-format
msgid "Restore and replace the original domain (URL) with %s (migration)"
msgstr ""

#: admin/partials/wpvivid-backup-restore-page-display.php:885
msgid "Restore and keep the original domain (URL) unchanged"
msgstr ""

#: admin/partials/wpvivid-backup-restore-page-display.php:889
#: admin/partials/wpvivid-backup-restore-page-display.php:1787
msgid "Tips:"
msgstr ""

#: admin/partials/wpvivid-backup-restore-page-display.php:889
msgid "The plugin detects automatically either site restoration or migration (replacing the domain name) based on the current domain name. If the domain name in backup file is same as the current one, it starts restoring. On the contrary, restoring backup means to replace with the current domain name. The precondition is that the backup is created by version 0.9.21 or later."
msgstr ""

#: admin/partials/wpvivid-backup-restore-page-display.php:893
msgid "Terminate"
msgstr ""

#: admin/partials/wpvivid-backup-restore-page-display.php:894
msgid "Rollback"
msgstr ""

#: admin/partials/wpvivid-backup-restore-page-display.php:896
msgid "Retrieve the backup to localhost"
msgstr ""

#: admin/partials/wpvivid-backup-restore-page-display.php:897
msgid "The backup is stored on the remote storage, click on the button to download it to localhost."
msgstr ""

#: admin/partials/wpvivid-backup-restore-page-display.php:1489
msgid "Database Size:"
msgstr ""

#: admin/partials/wpvivid-backup-restore-page-display.php:1490
msgid "File Size:"
msgstr ""

#: admin/partials/wpvivid-backup-restore-page-display.php:1493
#: admin/partials/wpvivid-settings-page-display.php:291
msgid "Total Size:"
msgstr ""

#: admin/partials/wpvivid-backup-restore-page-display.php:1494
msgid "Uploaded:"
msgstr ""

#: admin/partials/wpvivid-backup-restore-page-display.php:1495
msgid "Speed:"
msgstr ""

#: admin/partials/wpvivid-backup-restore-page-display.php:1498
msgid "Network Connection:"
msgstr ""

#: admin/partials/wpvivid-backup-restore-page-display.php:1504
msgid "Cancel"
msgstr ""

#: admin/partials/wpvivid-backup-restore-page-display.php:1557
msgid "Back Up Manually"
msgstr ""

#: admin/partials/wpvivid-backup-restore-page-display.php:1559
msgid "Export Content"
msgstr ""

#: admin/partials/wpvivid-backup-restore-page-display.php:1560
msgid "new feature"
msgstr ""

#: admin/partials/wpvivid-backup-restore-page-display.php:1563
msgid "Local Storage Directory:"
msgstr ""

#: admin/partials/wpvivid-backup-restore-page-display.php:1565
msgid "rename directory"
msgstr ""

#: admin/partials/wpvivid-backup-restore-page-display.php:1594
msgid "Save Backups to Local"
msgstr ""

#: admin/partials/wpvivid-backup-restore-page-display.php:1598
msgid "Send Backup to Remote Storage:"
msgstr ""

#: admin/partials/wpvivid-backup-restore-page-display.php:1611
msgid "Backup Now"
msgstr ""

#: admin/partials/wpvivid-backup-restore-page-display.php:1616
msgid "This backup can only be deleted manually"
msgstr ""

#: admin/partials/wpvivid-backup-restore-page-display.php:1787
msgid "The settings are only for manual backup, which won't affect schedule settings."
msgstr ""

#: admin/partials/wpvivid-backup-restore-page-display.php:1806
msgid "Backup Schedule"
msgstr ""

#: admin/partials/wpvivid-backup-restore-page-display.php:1808
msgid "Schedule Status: "
msgstr ""

#: admin/partials/wpvivid-backup-restore-page-display.php:1810
msgid "Server Time: "
msgstr ""

#: admin/partials/wpvivid-backup-restore-page-display.php:1812
msgid "Next Backup: "
msgstr ""

#: admin/partials/wpvivid-remote-storage-page-display.php:6
#: admin/partials/wpvivid-remote-storage-page-display.php:318
msgid "Storages"
msgstr ""

#: admin/partials/wpvivid-remote-storage-page-display.php:14
#: admin/partials/wpvivid-remote-storage-page-display.php:320
msgid "Storage Edit"
msgstr ""

#: admin/partials/wpvivid-remote-storage-page-display.php:26
#: admin/partials/wpvivid-remote-storage-page-display.php:327
msgid "Please choose one storage to save your backups (remote storage)"
msgstr ""

#: admin/partials/wpvivid-remote-storage-page-display.php:34
#: admin/partials/wpvivid-remote-storage-page-display.php:335
msgid "Storage Provider"
msgstr ""

#: admin/partials/wpvivid-remote-storage-page-display.php:35
#: admin/partials/wpvivid-remote-storage-page-display.php:336
msgid "Remote Storage Alias"
msgstr ""

#: admin/partials/wpvivid-remote-storage-page-display.php:36
#: admin/partials/wpvivid-remote-storage-page-display.php:337
msgid "Actions"
msgstr ""

#: admin/partials/wpvivid-schedule-page-display.php:7
msgid "Schedule Settings"
msgstr ""

#: admin/partials/wpvivid-schedule-page-display.php:15
msgid "Enable backup schedule"
msgstr ""

#: admin/partials/wpvivid-schedule-page-display.php:20
msgid "Enable Incremental Backup"
msgstr ""

#: admin/partials/wpvivid-schedule-page-display.php:32
msgid "Advanced Schedule"
msgstr ""

#: admin/partials/wpvivid-schedule-page-display.php:85
msgid "Highlighted icon illuminates that you have choosed a remote storage to store backups"
msgstr ""

#: admin/partials/wpvivid-schedule-page-display.php:95
msgid "+ Add another schedule"
msgstr ""

#: admin/partials/wpvivid-schedule-page-display.php:117
msgid "Scheduled job will start at <strong>UTC</strong> time:"
msgstr ""

#: admin/partials/wpvivid-schedule-page-display.php:118
msgid "Being subjected to mechanisms of PHP, a scheduled backup task for your site will be triggered only when the site receives at least a visit at any page."
msgstr ""

#: admin/partials/wpvivid-settings-page-display.php:88
msgid "backups retained"
msgstr ""

#: admin/partials/wpvivid-settings-page-display.php:88
msgid "Pro feature: Retain more backups"
msgstr ""

#: admin/partials/wpvivid-settings-page-display.php:93
msgid "Calculate the size of files, folder and database before backing up"
msgstr ""

#: admin/partials/wpvivid-settings-page-display.php:99
msgid "Show WPvivid backup plugin on top admin bar"
msgstr ""

#: admin/partials/wpvivid-settings-page-display.php:105
msgid "Merge all the backup files into single package when a backup completes. This will save great disk spaces, though takes longer time. We recommended you check the option especially on sites with insufficient server resources."
msgstr ""

#: admin/partials/wpvivid-settings-page-display.php:111
msgid "Keep storing the backups in localhost after uploading to remote storage"
msgstr ""

#: admin/partials/wpvivid-settings-page-display.php:116
msgid "Backup Folder"
msgstr ""

#: admin/partials/wpvivid-settings-page-display.php:118
msgid "Name your folder, this folder must be writable for creating backup files."
msgstr ""

#: admin/partials/wpvivid-settings-page-display.php:120
msgid "Local storage directory:"
msgstr ""

#: admin/partials/wpvivid-settings-page-display.php:125
msgid "Display domain(url) of current site in backup name. (e.g. domain_wpvivid-5ceb938b6dca9_2019-05-27-07-36_backup_all.zip)"
msgstr ""

#: admin/partials/wpvivid-settings-page-display.php:130
msgid "Remove out-of-date backups"
msgstr ""

#: admin/partials/wpvivid-settings-page-display.php:134
msgid "Web Server Directory:"
msgstr ""

#: admin/partials/wpvivid-settings-page-display.php:135
msgid "Remote Storage Directory:"
msgstr ""

#: admin/partials/wpvivid-settings-page-display.php:147
msgid "Remove"
msgstr ""

#: admin/partials/wpvivid-settings-page-display.php:148
msgid "The action is irreversible! It will remove all backups are out-of-date (including local web server and remote storage) if they exist."
msgstr ""

#: admin/partials/wpvivid-settings-page-display.php:208
msgid "In order to use this function, please install a <strong><a target=\"_blank\" href=\"https://wpvivid.com/8-best-smtp-plugins-for-wordpress.html\" style=\"text-decoration: none;\">WordPress SMTP plugin</a></strong> of your preference and configure your SMTP server first. This is because WordPress uses the PHP Mail function to send its emails by default, which is not supported by many hosts and can cause issues if it is not set properly."
msgstr ""

#: admin/partials/wpvivid-settings-page-display.php:213
msgid "Enable email report"
msgstr ""

#: admin/partials/wpvivid-settings-page-display.php:227
msgid "Test Email"
msgstr ""

#: admin/partials/wpvivid-settings-page-display.php:232
msgid "Always send an email notification when a backup is complete"
msgstr ""

#: admin/partials/wpvivid-settings-page-display.php:236
msgid "Only send an email notification when a backup fails"
msgstr ""

#: admin/partials/wpvivid-settings-page-display.php:240
msgid "Pro feature: Add another email address to get report"
msgstr ""

#: admin/partials/wpvivid-settings-page-display.php:287
msgid "Web-server disk space in use by WPvivid"
msgstr ""

#: admin/partials/wpvivid-settings-page-display.php:293
msgid "Calculate Sizes"
msgstr ""

#: admin/partials/wpvivid-settings-page-display.php:298
msgid "logs"
msgstr ""

#: admin/partials/wpvivid-settings-page-display.php:299
#: admin/partials/wpvivid-settings-page-display.php:310
#: admin/partials/wpvivid-settings-page-display.php:317
msgid "Path:"
msgstr ""

#: admin/partials/wpvivid-settings-page-display.php:305
msgid "Backup Cache"
msgstr ""

#: admin/partials/wpvivid-settings-page-display.php:309
msgid "Junk"
msgstr ""

#: admin/partials/wpvivid-settings-page-display.php:316
msgid "Temporary Files"
msgstr ""

#: admin/partials/wpvivid-settings-page-display.php:318
msgid "Temporary Files are created by wpvivid when restoring a website."
msgstr ""

#: admin/partials/wpvivid-settings-page-display.php:322
msgid "Empty"
msgstr ""

#: admin/partials/wpvivid-settings-page-display.php:424
msgid "Export"
msgstr ""

#: admin/partials/wpvivid-settings-page-display.php:425
msgid "Click 'Export' button to save WPvivid settings on your local computer."
msgstr ""

#: admin/partials/wpvivid-settings-page-display.php:429
msgid "Import"
msgstr ""

#: admin/partials/wpvivid-settings-page-display.php:430
msgid "Importing the json file can help you set WPvivid's configuration on another wordpress site quickly."
msgstr ""

#: admin/partials/wpvivid-settings-page-display.php:550
msgid "Enable the option when backup failed."
msgstr ""

#: admin/partials/wpvivid-settings-page-display.php:550
msgid "Special optimization for web hosting/shared hosting"
msgstr ""

#: admin/partials/wpvivid-settings-page-display.php:554
msgid "Enable optimization mode for web hosting/shared hosting"
msgstr ""

#: admin/partials/wpvivid-settings-page-display.php:557
msgid "Enabling this option can improve the backup success rate, but it will take more time for backup."
msgstr ""

#: admin/partials/wpvivid-settings-page-display.php:564
msgid "Database access method."
msgstr ""

#: admin/partials/wpvivid-settings-page-display.php:569
msgid "WPDB option has a better compatibility, but the speed of backup and restore is slower."
msgstr ""

#: admin/partials/wpvivid-settings-page-display.php:575
msgid "It is recommended to choose PDO option if pdo_mysql extension is installed on your server, which lets you backup and restore your site faster."
msgstr ""

#: admin/partials/wpvivid-settings-page-display.php:583
msgid "It will cause a lower CPU Usage and is recommended in a web hosting/ shared hosting environment."
msgstr ""

#: admin/partials/wpvivid-settings-page-display.php:583
msgid "Only Archive without compressing"
msgstr ""

#: admin/partials/wpvivid-settings-page-display.php:587
msgid "It will cause a higher CPU Usage and is recommended in a VPS/ dedicated hosting environment."
msgstr ""

#: admin/partials/wpvivid-settings-page-display.php:587
msgid "Compress and Archive"
msgstr ""

#: admin/partials/wpvivid-settings-page-display.php:596
msgid "Compress Files Every"
msgstr ""

#: admin/partials/wpvivid-settings-page-display.php:599
msgid "Some web hosting providers limit large zip files (e.g. 200MB), and therefore splitting your backup into many parts is an ideal way to avoid hitting the limitation if you are running a big website.  Please try to adjust the value if you are encountering backup errors. If you use a value of 0 MB, any backup files won't be split."
msgstr ""

#: admin/partials/wpvivid-settings-page-display.php:601
msgid "Exclude the files which are larger than"
msgstr ""

#: admin/partials/wpvivid-settings-page-display.php:604
msgid "Using the option will ignore the file larger than the certain size in MB when backing up, '0' (zero) means unlimited."
msgstr ""

#: admin/partials/wpvivid-settings-page-display.php:606
msgid "PHP script execution timeout for backup"
msgstr ""

#: admin/partials/wpvivid-settings-page-display.php:609
msgid "The time-out is not your server PHP time-out. With the execution time exhausted, our plugin will shut the process of backup down. If the progress of backup encounters a time-out, that means you have a medium or large sized website, please try to scale the value bigger."
msgstr ""

#: admin/partials/wpvivid-settings-page-display.php:611
msgid "PHP script execution timeout for restore"
msgstr ""

#: admin/partials/wpvivid-settings-page-display.php:614
msgid "The time-out is not your server PHP time-out. With the execution time exhausted, our plugin will shut the process of restore down. If the progress of restore encounters a time-out, that means you have a medium or large sized website, please try to scale the value bigger."
msgstr ""

#: admin/partials/wpvivid-settings-page-display.php:616
msgid "PHP Memory Limit for backup"
msgstr ""

#: admin/partials/wpvivid-settings-page-display.php:619
msgid "Adjust this value to apply for a temporary PHP memory limit for WPvivid backup plugin to run a backup. We set this value to 256M by default. Increase the value if you encounter a memory exhausted error. Note: some web hosting providers may not support this."
msgstr ""

#: admin/partials/wpvivid-settings-page-display.php:621
msgid "PHP Memory Limit for restoration"
msgstr ""

#: admin/partials/wpvivid-settings-page-display.php:624
msgid "Adjust this value to apply for a temporary PHP memory limit for WPvivid backup plugin in restore process. We set this value to 256M by default. Increase the value if you encounter a memory exhausted error. Note: some web hosting providers may not support this."
msgstr ""

#: admin/partials/wpvivid-settings-page-display.php:626
msgid "Chunk Size"
msgstr ""

#: admin/partials/wpvivid-settings-page-display.php:629
msgid "e.g.  if you choose a chunk size of 2MB, a 8MB file will use 4 chunks. Decreasing this value will break the ISP's transmission limit, for example:512KB"
msgstr ""

#: admin/partials/wpvivid-settings-page-display.php:643
#, php-format
msgid "<strong>Retrying </strong>%s<strong> times when encountering a time-out error</strong>"
msgstr ""

#: admin/partials/wpvivid-settings-page-display.php:659
msgid "General Settings"
msgstr ""

#: admin/partials/wpvivid-settings-page-display.php:665
msgid "Advanced Settings"
msgstr ""

#: includes/class-wpvivid-backup-uploader.php:493
msgid "Tips: Click the button below to scan all uploaded or received backups in directory"
msgstr ""

#: includes/class-wpvivid-backup-uploader.php:496
msgid "Scan uploaded backup or received backup"
msgstr ""

#: includes/class-wpvivid-backup-uploader.php:543
msgid "Drop files here"
msgstr ""

#: includes/class-wpvivid-backup-uploader.php:545
msgid "Select Files"
msgstr ""

#: includes/class-wpvivid-backup.php:1062
msgid "Ready to backup. Progress: 0%, running time: 0second."
msgstr ""

#: includes/class-wpvivid-backup.php:1097
#: includes/class-wpvivid-backup.php:1110
#: includes/class-wpvivid-export-import.php:1308
msgid "Progress: "
msgstr ""

#: includes/class-wpvivid-backup.php:1097
#: includes/class-wpvivid-backup.php:1110
#: includes/class-wpvivid-export-import.php:1308
msgid "running time: "
msgstr ""

#: includes/class-wpvivid-backup.php:1116
#: includes/class-wpvivid-backup.php:1133
#: includes/class-wpvivid-function-realize.php:42
msgid "The backup will be canceled after backing up the current chunk ends."
msgstr ""

#: includes/class-wpvivid-backup.php:2256
#, php-format
msgid "Start backing up %s."
msgstr ""

#: includes/class-wpvivid-backup.php:2264
#, php-format
msgid "Backing up %s finished."
msgstr ""

#: includes/class-wpvivid-export-import.php:65
#: includes/class-wpvivid-export-import.php:78
msgid "Export & Import"
msgstr ""

#: includes/class-wpvivid-export-import.php:119
msgid "Export posts or pages with images in bulk."
msgstr ""

#: includes/class-wpvivid-export-import.php:121
#: includes/class-wpvivid-export-import.php:1515
msgid "Learn more"
msgstr ""

#: includes/class-wpvivid-export-import.php:124
msgid "This will contain all of your posts, pages, comments, terms and images (original images, featured images and thumbnails)."
msgstr ""

#: includes/class-wpvivid-export-import.php:125
#: includes/class-wpvivid-export-import.php:1518
msgid "Note:"
msgstr ""

#: includes/class-wpvivid-export-import.php:125
msgid "Try to select fewer items when you are facing a shortage of server resources (typically presented as a timeout error)."
msgstr ""

#: includes/class-wpvivid-export-import.php:135
#, php-format
msgid "Exported files will be temporarily stored in %s directory"
msgstr ""

#: includes/class-wpvivid-export-import.php:141
msgid "Choose post type"
msgstr ""

#: includes/class-wpvivid-export-import.php:147
msgid "Post"
msgstr ""

#: includes/class-wpvivid-export-import.php:150
msgid "Page"
msgstr ""

#: includes/class-wpvivid-export-import.php:153
msgid "More post types coming soon..."
msgstr ""

#: includes/class-wpvivid-export-import.php:158
#: includes/class-wpvivid-export-import.php:181
msgid "Next Step"
msgstr ""

#: includes/class-wpvivid-export-import.php:623
msgid "Choose what to export"
msgstr ""

#: includes/class-wpvivid-export-import.php:631
msgid "Filter Posts/Pages"
msgstr ""

#: includes/class-wpvivid-export-import.php:650
msgid "All Categories"
msgstr ""

#: includes/class-wpvivid-export-import.php:660
#, php-format
msgid "Export %s of all categories or a specific category."
msgstr ""

#: includes/class-wpvivid-export-import.php:680
msgid "All Authors"
msgstr ""

#: includes/class-wpvivid-export-import.php:691
#, php-format
msgid "Export %s of all authors or a specific author."
msgstr ""

#: includes/class-wpvivid-export-import.php:703
#: includes/class-wpvivid-export-import.php:724
msgid "&mdash; Select &mdash;"
msgstr ""

#: includes/class-wpvivid-export-import.php:712
#, php-format
msgid "Export %s published after this date."
msgstr ""

#: includes/class-wpvivid-export-import.php:733
#, php-format
msgid "Export %s published before this date."
msgstr ""

#: includes/class-wpvivid-export-import.php:769
msgid "Search"
msgstr ""

#: includes/class-wpvivid-export-import.php:776
#, php-format
msgid "Search for %s according to the above rules."
msgstr ""

#: includes/class-wpvivid-export-import.php:789
msgid "Comment the export (optional)"
msgstr ""

#: includes/class-wpvivid-export-import.php:794
msgid "Comment the export: "
msgstr ""

#: includes/class-wpvivid-export-import.php:798
msgid "Only letters (except for wpvivid) and numbers are allowed."
msgstr ""

#: includes/class-wpvivid-export-import.php:802
msgid "Sample:"
msgstr ""

#: includes/class-wpvivid-export-import.php:811
msgid "Export and Download"
msgstr ""

#: includes/class-wpvivid-export-import.php:1116
msgid "Empty post id"
msgstr ""

#: includes/class-wpvivid-export-import.php:1123
#: includes/class-wpvivid-export-import.php:1159
msgid "A task is already running. Please wait until the running task is complete, and try again."
msgstr ""

#: includes/class-wpvivid-export-import.php:1149
msgid "Error occurred while parsing the request data. Please try to run export task again."
msgstr ""

#: includes/class-wpvivid-export-import.php:1304
msgid "Ready to export. Progress: 0%, running time: 0second."
msgstr ""

#: includes/class-wpvivid-export-import.php:1316
msgid "The export task is not responding."
msgstr ""

#: includes/class-wpvivid-export-import.php:1341
msgid "Export error:"
msgstr ""

#: includes/class-wpvivid-export-import.php:1375
msgid "Task time out."
msgstr ""

#: includes/class-wpvivid-export-import.php:1482
msgid "File size not match. please retry again."
msgstr ""

#: includes/class-wpvivid-export-import.php:1488
msgid "File not found. please retry again."
msgstr ""

#: includes/class-wpvivid-export-import.php:1513
msgid "Import posts or pages with images in bulk."
msgstr ""

#: includes/class-wpvivid-export-import.php:1519
msgid "To properly display the imported content, please make sure that the importing and exporting sites have the same environment, for example, same theme or pages built with the same page builder."
msgstr ""

#: includes/class-wpvivid-export-import.php:1528
#, php-format
msgid "Imported files will be temporarily stored in directory %s"
msgstr ""

#: includes/class-wpvivid-export-import.php:1529
msgid "Delete Exported Files In Folder"
msgstr ""

#: includes/class-wpvivid-export-import.php:1534
msgid "Choose an export from your computer to import: "
msgstr ""

#: includes/class-wpvivid-export-import.php:1535
msgid "Upload and Import"
msgstr ""

#: includes/class-wpvivid-export-import.php:1538
#, php-format
msgid "Or you can use ftp to upload the export to the directory %s. Then click the button below to scan the file to import."
msgstr ""

#: includes/class-wpvivid-export-import.php:1539
msgid "Scan Uploaded Exports"
msgstr ""

#: includes/class-wpvivid-export-import.php:1543
msgid "The importing file info"
msgstr ""

#: includes/class-wpvivid-export-import.php:1546
msgid "Assign author"
msgstr ""

#: includes/class-wpvivid-export-import.php:1548
msgid "Select an existing author:"
msgstr ""

#: includes/class-wpvivid-export-import.php:1549
msgid "- Select -"
msgstr ""

#: includes/class-wpvivid-export-import.php:1551
msgid "Import Setting"
msgstr ""

#: includes/class-wpvivid-export-import.php:1555
msgid "Overwrite existing pages"
msgstr ""

#: includes/class-wpvivid-export-import.php:1559
msgid "With this option checked, Pages/posts already existing will be overwritten with the updated ones in an import."
msgstr ""

#: includes/class-wpvivid-export-import.php:1561
msgid "Start to Import"
msgstr ""

#: includes/class-wpvivid-export-import.php:1562
msgid "Back to Import Page"
msgstr ""

#: includes/class-wpvivid-exporter.php:71
#: includes/class-wpvivid-importer.php:50
msgid "Select All"
msgstr ""

#: includes/class-wpvivid-exporter.php:118
msgid "Author"
msgstr ""

#: includes/class-wpvivid-exporter.php:150
msgid "Comments"
msgstr ""

#: includes/class-wpvivid-exporter.php:265
msgid "Unpublished"
msgstr ""

#: includes/class-wpvivid-exporter.php:275
#, php-format
msgid "%s ago"
msgstr ""

#: includes/class-wpvivid-exporter.php:282
msgid "Published"
msgstr ""

#: includes/class-wpvivid-exporter.php:285
msgid "Missed schedule"
msgstr ""

#: includes/class-wpvivid-exporter.php:287
msgid "Scheduled"
msgstr ""

#: includes/class-wpvivid-exporter.php:290
msgid "Last Modified"
msgstr ""

#: includes/class-wpvivid-exporter.php:548
#: includes/class-wpvivid-importer.php:304
msgid "First page"
msgstr ""

#: includes/class-wpvivid-exporter.php:559
#: includes/class-wpvivid-importer.php:315
msgid "Previous page"
msgstr ""

#: includes/class-wpvivid-exporter.php:566
#: includes/class-wpvivid-exporter.php:570
#: includes/class-wpvivid-importer.php:322
#: includes/class-wpvivid-importer.php:326
msgid "Current Page"
msgstr ""

#: includes/class-wpvivid-exporter.php:584
#: includes/class-wpvivid-importer.php:340
msgid "Next page"
msgstr ""

#: includes/class-wpvivid-exporter.php:594
#: includes/class-wpvivid-importer.php:350
msgid "Last page"
msgstr ""

#: includes/class-wpvivid-function-realize.php:65
#: includes/class-wpvivid-function-realize.php:93
msgid "Retrieving the backup information failed while showing log. Please try again later."
msgstr ""

#: includes/class-wpvivid-function-realize.php:70
#: includes/class-wpvivid-function-realize.php:82
#: includes/class-wpvivid-function-realize.php:99
msgid "The log not found."
msgstr ""

#: includes/class-wpvivid-importer.php:86
msgid "File Name"
msgstr ""

#: includes/class-wpvivid-importer.php:87
msgid "Post Types"
msgstr ""

#: includes/class-wpvivid-importer.php:88
msgid "Count"
msgstr ""

#: includes/class-wpvivid-importer.php:89
msgid "Media Files Size"
msgstr ""

#: includes/class-wpvivid-importer.php:167
msgid "Type: "
msgstr ""

#: includes/class-wpvivid-importer.php:1669
msgid "File not exist, file:"
msgstr ""

#: includes/class-wpvivid-importer.php:1676
#: includes/class-wpvivid-importer.php:1777
msgid "Sorry, this file type is not permitted for security reasons."
msgstr ""

#: includes/class-wpvivid-importer.php:1724
msgid "Fetching attachments is not enabled"
msgstr ""

#: includes/class-wpvivid-importer.php:1737
msgid "Invalid file type"
msgstr ""

#: includes/class-wpvivid-importer.php:1782
msgid "File not exist file:"
msgstr ""

#: includes/class-wpvivid-importer.php:1877
#: includes/class-wpvivid-importer.php:1913
#: includes/class-wpvivid-importer.php:1921
msgid "There was an error when reading this WXR file"
msgstr ""

#: includes/class-wpvivid-importer.php:1878
msgid "Details are shown above. The importer will now try again with a different parser..."
msgstr ""

#: includes/class-wpvivid-importer.php:1925
#: includes/class-wpvivid-importer.php:1930
#: includes/class-wpvivid-importer.php:2155
#: includes/class-wpvivid-importer.php:2351
msgid "This does not appear to be a WXR file, missing/invalid WXR version number"
msgstr ""

#: includes/class-wpvivid-interface-mainwp.php:138
msgid "Error occurred while parsing the request data. Please try to run backup again."
msgstr ""

#: includes/class-wpvivid-interface-mainwp.php:182
#: includes/class-wpvivid-interface-mainwp.php:220
#: includes/class-wpvivid-interface-mainwp.php:273
msgid "Unable to open the log file."
msgstr ""

#: includes/class-wpvivid-interface-mainwp.php:211
msgid "Reading the log failed. Please try again."
msgstr ""

#: includes/class-wpvivid-mail-report.php:558
msgid "Unable to send email. Please check the configuration of email server."
msgstr ""

#: includes/class-wpvivid-migrate.php:49
msgid "Auto-Migration"
msgstr ""

#: includes/class-wpvivid-migrate.php:55
msgid "Key"
msgstr ""

#: includes/class-wpvivid-migrate.php:390
msgid "In order to allow another site to send a backup to this site, please generate a key below. Once the key is generated, this site is ready to receive a backup from another site. Then, please copy and paste the key in sending site and save it."
msgstr ""

#: includes/class-wpvivid-migrate.php:392
msgid "The key will expire in "
msgstr ""

#: includes/class-wpvivid-migrate.php:399
msgid "Tips: For security reason, please choose an appropriate expiration time for the key."
msgstr ""

#: includes/class-wpvivid-migrate.php:401
msgid "Generate"
msgstr ""

#: includes/class-wpvivid-migrate.php:850
msgid "Please paste the key below."
msgstr ""

#: includes/class-wpvivid-migrate.php:850
msgid "How to get a site key?"
msgstr ""

#: includes/class-wpvivid-migrate.php:853
msgid "Save"
msgstr ""

#: includes/class-wpvivid-migrate.php:969
msgid "1. Visit Key tab page of WPvivid backup plugin of destination site."
msgstr ""

#: includes/class-wpvivid-migrate.php:970
msgid "2. Generate a key by clicking Generate button and copy it."
msgstr ""

#: includes/class-wpvivid-migrate.php:971
msgid "3. Go back to this page and paste the key in key box below. Lastly, click Save button."
msgstr ""

#: includes/class-wpvivid-migrate.php:985
msgid "The feature can help you transfer a Wordpress site to a new domain(site). It would be a convenient way to migrate your WP site from dev environment to live server or from old server to the new."
msgstr ""

#: includes/class-wpvivid-migrate.php:995
msgid "Choose the content you want to transfer"
msgstr ""

#: includes/class-wpvivid-migrate.php:1010
msgid "Clone then Transfer"
msgstr ""

#: includes/class-wpvivid-migrate.php:1090
msgid "Note: "
msgstr ""

#: includes/class-wpvivid-migrate.php:1091
msgid "1. In order to successfully complete the migration, you'd better deactivate <a href=\"https://wpvivid.com/best-redirect-plugins.html\" target=\"_blank\" style=\"text-decoration: none;\">301 redirect plugin</a>, <a href=\"https://wpvivid.com/8-best-wordpress-firewall-plugins.html\" target=\"_blank\" style=\"text-decoration: none;\">firewall and security plugin</a>, and <a href=\"https://wpvivid.com/best-free-wordpress-caching-plugins.html\" target=\"_blank\" style=\"text-decoration: none;\">caching plugin</a> (if they exist) before transferring website."
msgstr ""

#: includes/class-wpvivid-migrate.php:1092
msgid "2. Please migrate website with the manual way when using <strong>Local by Flywheel</strong> environment."
msgstr ""

#: includes/class-wpvivid-migrate.php:1098
msgid "<strong>Tips: </strong>The unstable connection between sites could cause a failure of files transfer. In this case, uploading backups to destination site is a good alternative to the automatic website migration."
msgstr ""

#: includes/class-wpvivid-migrate.php:1099
msgid "How to migrate Wordpress site manually to a new domain(site) with WPvivid backup plugin?"
msgstr ""

#: includes/class-wpvivid-migrate.php:1100
msgid "1. Download a backup in backups list to your computer."
msgstr ""

#: includes/class-wpvivid-migrate.php:1101
msgid "2. Upload the backup to destination site. There are two ways available to use:"
msgstr ""

#: includes/class-wpvivid-migrate.php:1102
msgid "2.1 Upload the backup to the upload section of WPvivid backup plugin in destination site."
msgstr ""

#: includes/class-wpvivid-migrate.php:1103
#, php-format
msgid "2.2 Upload the backup with FTP client to backup directory %s in destination site, then click <strong>Scan uploaded backup or received backup</strong> button."
msgstr ""

#: includes/class-wpvivid-migrate.php:1104
msgid "3. Once done, the backup appears in backups list. Then, restore the backup."
msgstr ""

#: includes/class-wpvivid-migrate.php:1124
msgid "Choose what to migrate"
msgstr ""

#: includes/class-wpvivid-migrate.php:1185
msgid "Transfer succeeded. Please scan the backup list on the destination site to display the backup, then restore the backup."
msgstr ""

#: includes/class-wpvivid-migrate.php:1245
msgid "Upload"
msgstr ""

#: includes/class-wpvivid-migrate.php:1255
#, php-format
msgid "The backups will be uploaded to %s directory."
msgstr ""

#: includes/class-wpvivid-migrate.php:1258
msgid "Note: The files you want to upload must be a backup created by WPvivid backup plugin. Make sure that uploading every part of a backup to the directory if the backup is split into many parts"
msgstr ""

#: includes/class-wpvivid-schedule.php:34
msgid "12 Hours"
msgstr ""

#: includes/class-wpvivid-schedule.php:40
#: includes/class-wpvivid-schedule.php:94
msgid "Daily"
msgstr ""

#: includes/class-wpvivid-schedule.php:46
#: includes/class-wpvivid-schedule.php:97
msgid "Weekly"
msgstr ""

#: includes/class-wpvivid-schedule.php:52
#: includes/class-wpvivid-schedule.php:100
msgid "Fortnightly"
msgstr ""

#: includes/class-wpvivid-schedule.php:58
#: includes/class-wpvivid-schedule.php:103
msgid "Monthly"
msgstr ""

#: includes/class-wpvivid-schedule.php:91
msgid "12Hours"
msgstr ""

#: includes/class-wpvivid-schedule.php:282
#: includes/class-wpvivid-schedule.php:296
msgid "Creating scheduled tasks failed. Please try again later."
msgstr ""

#: includes/class-wpvivid.php:507
#: includes/class-wpvivid.php:515
msgid "A backup type is required."
msgstr ""

#: includes/class-wpvivid.php:521
#: includes/class-wpvivid.php:530
msgid "Choose at least one storage location for backups."
msgstr ""

#: includes/class-wpvivid.php:541
#: includes/class-wpvivid.php:4557
msgid "There is no default remote storage configured. Please set it up first."
msgstr ""

#: includes/class-wpvivid.php:1408
#: includes/class-wpvivid.php:1430
#: includes/class-wpvivid.php:1456
#: includes/class-wpvivid.php:1581
#: includes/class-wpvivid.php:1701
msgid "Too many resumption attempts."
msgstr ""

#: includes/class-wpvivid.php:1589
#: includes/class-wpvivid.php:1709
msgid "Task timed out."
msgstr ""

#: includes/class-wpvivid.php:2479
msgid "Retrieving the backup(s) information failed while deleting the selected backup(s). Please try again later."
msgstr ""

#: includes/class-wpvivid.php:2489
msgid "Unable to delete the locked backup. Please unlock it first and try again."
msgstr ""

#: includes/class-wpvivid.php:2592
msgid "You have successfully added a remote storage."
msgstr ""

#: includes/class-wpvivid.php:2644
msgid "Fail to delete the remote storage, can not retrieve the storage infomation. Please try again."
msgstr ""

#: includes/class-wpvivid.php:2672
msgid "Failed to get the remote storage information. Please try again later."
msgstr ""

#: includes/class-wpvivid.php:3416
msgid "restore failed error unknown"
msgstr ""

#: includes/class-wpvivid.php:3449
msgid "The restore file not found. Please verify the file exists."
msgstr ""

#: includes/class-wpvivid.php:3561
#: includes/class-wpvivid.php:3577
msgid "The last backup message not found."
msgstr ""

#: includes/class-wpvivid.php:3580
msgid "Last Backup: "
msgstr ""

#: includes/class-wpvivid.php:3677
#, php-format
msgid "%d backup tasks have been completed. Please switch to <a href=\"#\" onclick=\"wpvivid_click_switch_page('wrap', 'wpvivid_tab_log', true);\">Log</a> page to check the details."
msgstr ""

#: includes/class-wpvivid.php:3770
msgid "Getting backup directory failed. Please try again later."
msgstr ""

#: includes/class-wpvivid.php:3965
msgid "Choose at least one type of junk files for deleting."
msgstr ""

#: includes/class-wpvivid.php:4039
msgid "The selected junk flies have been deleted."
msgstr ""

#: includes/class-wpvivid.php:4210
msgid "Choose one storage from the list to be the default storage."
msgstr ""

#: includes/class-wpvivid.php:4465
#: includes/class-wpvivid.php:4473
msgid "The maximum zip file size is required."
msgstr ""

#: includes/class-wpvivid.php:4479
#: includes/class-wpvivid.php:4486
msgid "The size for excluded files is required."
msgstr ""

#: includes/class-wpvivid.php:4492
#: includes/class-wpvivid.php:4499
msgid "The maximum execution time for PHP script is required."
msgstr ""

#: includes/class-wpvivid.php:4505
#: includes/class-wpvivid.php:4512
msgid "The local storage path is required."
msgstr ""

#: includes/class-wpvivid.php:4522
msgid "An email address is required."
msgstr ""

#: includes/class-wpvivid.php:4531
#: includes/class-wpvivid.php:4535
msgid "The pdo_mysql extension is not detected. Please install the extension first or choose wpdb option for Database connection method."
msgstr ""

#: includes/class-wpvivid.php:4654
msgid "The selected file is not the setting file for WPvivid. Please upload the right file."
msgstr ""

#: includes/class-wpvivid.php:4676
msgid "Invalid email address"
msgstr ""

#: includes/class-wpvivid.php:5248
msgid "Type:"
msgstr ""

#: includes/class-wpvivid.php:5402
msgid "Save backups on localhost (web server)"
msgstr ""

#: includes/class-wpvivid.php:5406
msgid "Send backups to remote storage (choose this option, the local backup will be deleted after uploading to remote storage completely)"
msgstr ""

#: includes/class-wpvivid.php:5571
msgid "User's email address is required."
msgstr ""

#: includes/class-wpvivid.php:5576
msgid "Please enter a valid email address."
msgstr ""

#: includes/customclass/class-wpvivid-amazons3-plus.php:44
msgid "Amazon S3"
msgstr ""

#: includes/customclass/class-wpvivid-amazons3-plus.php:62
#: includes/customclass/class-wpvivid-amazons3-plus.php:200
msgid "Enter Your Amazon S3 Account"
msgstr ""

#: includes/customclass/class-wpvivid-amazons3-plus.php:70
#: includes/customclass/class-wpvivid-amazons3-plus.php:208
msgid "Enter a unique alias: e.g. Amazon S3-001"
msgstr ""

#: includes/customclass/class-wpvivid-amazons3-plus.php:75
#: includes/customclass/class-wpvivid-amazons3-plus.php:213
#: includes/customclass/class-wpvivid-dropbox.php:477
#: includes/customclass/class-wpvivid-dropbox.php:602
#: includes/customclass/class-wpvivid-ftpclass.php:57
#: includes/customclass/class-wpvivid-ftpclass.php:184
#: includes/customclass/class-wpvivid-google-drive.php:319
#: includes/customclass/class-wpvivid-google-drive.php:447
msgid "A name to help you identify the storage if you have multiple remote storage connected."
msgstr ""

#: includes/customclass/class-wpvivid-amazons3-plus.php:82
#: includes/customclass/class-wpvivid-amazons3-plus.php:220
msgid "Amazon S3 access key"
msgstr ""

#: includes/customclass/class-wpvivid-amazons3-plus.php:87
#: includes/customclass/class-wpvivid-amazons3-plus.php:225
msgid "Enter your Amazon S3 access key."
msgstr ""

#: includes/customclass/class-wpvivid-amazons3-plus.php:87
#: includes/customclass/class-wpvivid-amazons3-plus.php:225
msgid "How to get an AmazonS3 access key."
msgstr ""

#: includes/customclass/class-wpvivid-amazons3-plus.php:94
#: includes/customclass/class-wpvivid-amazons3-plus.php:232
msgid "Amazon S3 secret key"
msgstr ""

#: includes/customclass/class-wpvivid-amazons3-plus.php:99
#: includes/customclass/class-wpvivid-amazons3-plus.php:237
msgid "Enter your Amazon S3 secret key."
msgstr ""

#: includes/customclass/class-wpvivid-amazons3-plus.php:99
#: includes/customclass/class-wpvivid-amazons3-plus.php:237
msgid "How to get an AmazonS3 secret key."
msgstr ""

#: includes/customclass/class-wpvivid-amazons3-plus.php:106
#: includes/customclass/class-wpvivid-amazons3-plus.php:244
msgid "Amazon S3 Bucket Name(e.g. test)"
msgstr ""

#: includes/customclass/class-wpvivid-amazons3-plus.php:111
#: includes/customclass/class-wpvivid-amazons3-plus.php:249
msgid "Enter an existed Bucket to create a custom backup storage directory."
msgstr ""

#: includes/customclass/class-wpvivid-amazons3-plus.php:118
#: includes/customclass/class-wpvivid-amazons3-plus.php:256
msgid "Custom Path"
msgstr ""

#: includes/customclass/class-wpvivid-amazons3-plus.php:123
#: includes/customclass/class-wpvivid-amazons3-plus.php:261
msgid "Customize the directory where you want to store backups within the Bucket."
msgstr ""

#: includes/customclass/class-wpvivid-amazons3-plus.php:131
#: includes/customclass/class-wpvivid-dropbox.php:509
#: includes/customclass/class-wpvivid-ftpclass.php:125
#: includes/customclass/class-wpvivid-google-drive.php:351
msgid "Set as the default remote storage."
msgstr ""

#: includes/customclass/class-wpvivid-amazons3-plus.php:137
#: includes/customclass/class-wpvivid-dropbox.php:515
#: includes/customclass/class-wpvivid-ftpclass.php:131
#: includes/customclass/class-wpvivid-google-drive.php:357
msgid "Once checked, all this sites backups sent to a remote storage destination will be uploaded to this storage by default."
msgstr ""

#: includes/customclass/class-wpvivid-amazons3-plus.php:145
#: includes/customclass/class-wpvivid-amazons3-plus.php:269
msgid "Storage class: Standard (infrequent access)."
msgstr ""

#: includes/customclass/class-wpvivid-amazons3-plus.php:151
#: includes/customclass/class-wpvivid-amazons3-plus.php:275
msgid "Check the option to use Amazon S3 Standard-Infrequent Access (S3 Standard-IA) storage class for data transfer."
msgstr ""

#: includes/customclass/class-wpvivid-amazons3-plus.php:159
#: includes/customclass/class-wpvivid-amazons3-plus.php:283
msgid "Server-side encryption."
msgstr ""

#: includes/customclass/class-wpvivid-amazons3-plus.php:165
#: includes/customclass/class-wpvivid-amazons3-plus.php:289
msgid "Check the option to use Amazon S3 server-side encryption to protect data."
msgstr ""

#: includes/customclass/class-wpvivid-amazons3-plus.php:173
#: includes/customclass/class-wpvivid-ftpclass.php:153
msgid "Test and Add"
msgstr ""

#: includes/customclass/class-wpvivid-amazons3-plus.php:178
msgid "Click the button to connect to Amazon S3 storage and add it to the storage list below."
msgstr ""

#: includes/customclass/class-wpvivid-amazons3-plus.php:187
msgid "The simplexml extension is not detected. Please install the extension first."
msgstr ""

#: includes/customclass/class-wpvivid-amazons3-plus.php:302
#: includes/customclass/class-wpvivid-dropbox.php:614
#: includes/customclass/class-wpvivid-ftpclass.php:259
#: includes/customclass/class-wpvivid-google-drive.php:459
msgid "Click the button to save the changes."
msgstr ""

#: includes/customclass/class-wpvivid-dropbox.php:433
msgid "You have authenticated the Dropbox account as your remote storage."
msgstr ""

#: includes/customclass/class-wpvivid-dropbox.php:441
#: includes/customclass/class-wpvivid-google-drive.php:280
msgid "You have successfully updated the storage alias."
msgstr ""

#: includes/customclass/class-wpvivid-dropbox.php:452
msgid "Dropbox"
msgstr ""

#: includes/customclass/class-wpvivid-dropbox.php:462
msgid "Please read <a target=\"_blank\" href=\"https://wpvivid.com/privacy-policy\" style=\"text-decoration: none;\">this privacy policy</a> for use of our Dropbox authorization app (none of your backup data is sent to us)."
msgstr ""

#: includes/customclass/class-wpvivid-dropbox.php:465
#: includes/customclass/class-wpvivid-dropbox.php:590
msgid "Enter Your Dropbox Information"
msgstr ""

#: includes/customclass/class-wpvivid-dropbox.php:472
#: includes/customclass/class-wpvivid-dropbox.php:597
msgid "Enter a unique alias: e.g. Dropbox-001"
msgstr ""

#: includes/customclass/class-wpvivid-dropbox.php:489
#: includes/customclass/class-wpvivid-google-drive.php:331
msgid "All backups will be uploaded to this directory."
msgstr ""

#: includes/customclass/class-wpvivid-dropbox.php:501
#: includes/customclass/class-wpvivid-google-drive.php:343
msgid "Pro feature: Create a directory for storing the backups of the site"
msgstr ""

#: includes/customclass/class-wpvivid-dropbox.php:522
msgid "Authenticate with Dropbox"
msgstr ""

#: includes/customclass/class-wpvivid-dropbox.php:527
msgid "Click the button to get Dropbox authentication and add it to the storage list below."
msgstr ""

#: includes/customclass/class-wpvivid-ftpclass.php:36
msgid "FTP"
msgstr ""

#: includes/customclass/class-wpvivid-ftpclass.php:45
#: includes/customclass/class-wpvivid-ftpclass.php:172
msgid "Enter Your FTP Account"
msgstr ""

#: includes/customclass/class-wpvivid-ftpclass.php:52
#: includes/customclass/class-wpvivid-ftpclass.php:179
msgid "Enter an unique alias: e.g. FTP-001"
msgstr ""

#: includes/customclass/class-wpvivid-ftpclass.php:64
#: includes/customclass/class-wpvivid-ftpclass.php:191
msgid "FTP server (server's port 21)"
msgstr ""

#: includes/customclass/class-wpvivid-ftpclass.php:69
#: includes/customclass/class-wpvivid-ftpclass.php:196
msgid "Enter the FTP server."
msgstr ""

#: includes/customclass/class-wpvivid-ftpclass.php:81
msgid "Pro feature: Change the FTP default port number"
msgstr ""

#: includes/customclass/class-wpvivid-ftpclass.php:88
#: includes/customclass/class-wpvivid-ftpclass.php:203
msgid "FTP login"
msgstr ""

#: includes/customclass/class-wpvivid-ftpclass.php:93
#: includes/customclass/class-wpvivid-ftpclass.php:208
msgid "Enter your FTP server user name."
msgstr ""

#: includes/customclass/class-wpvivid-ftpclass.php:100
#: includes/customclass/class-wpvivid-ftpclass.php:215
msgid "FTP password"
msgstr ""

#: includes/customclass/class-wpvivid-ftpclass.php:105
#: includes/customclass/class-wpvivid-ftpclass.php:220
msgid "Enter the FTP server password."
msgstr ""

#: includes/customclass/class-wpvivid-ftpclass.php:112
#: includes/customclass/class-wpvivid-ftpclass.php:227
msgid "Absolute path must exist(e.g. /home/username)"
msgstr ""

#: includes/customclass/class-wpvivid-ftpclass.php:117
#: includes/customclass/class-wpvivid-ftpclass.php:232
msgid "Enter an absolute path and a custom subdirectory (optional) for holding the backups of current website. For example, /home/username/customfolder"
msgstr ""

#: includes/customclass/class-wpvivid-ftpclass.php:139
#: includes/customclass/class-wpvivid-ftpclass.php:240
msgid "Uncheck this to enable FTP active mode."
msgstr ""

#: includes/customclass/class-wpvivid-ftpclass.php:145
#: includes/customclass/class-wpvivid-ftpclass.php:246
msgid "Uncheck the option to use FTP active mode when transferring files. Make sure the FTP server you are configuring supports the active FTP mode."
msgstr ""

#: includes/customclass/class-wpvivid-ftpclass.php:158
msgid "Click the button to connect to FTP server and add it to the storage list below."
msgstr ""

#: includes/customclass/class-wpvivid-google-drive.php:111
#: includes/customclass/class-wpvivid-google-drive.php:207
msgid "Authentication failed, the client_secrets.json file is missing. Please make sure the client_secrets.json file is in wpvivid-backuprestore\\includes\\customclass directory."
msgstr ""

#: includes/customclass/class-wpvivid-google-drive.php:115
#: includes/customclass/class-wpvivid-google-drive.php:211
msgid "Authentication failed, the format of the client_secrets.json file is incorrect. Please delete and re-install the plugin to recreate the file."
msgstr ""

#: includes/customclass/class-wpvivid-google-drive.php:272
msgid "You have authenticated the WPvividGoogle Drive account as your remote storage."
msgstr ""

#: includes/customclass/class-wpvivid-google-drive.php:292
msgid "WPvividGoogle Drive"
msgstr ""

#: includes/customclass/class-wpvivid-google-drive.php:304
msgid "Please read <a target=\"_blank\" href=\"https://wpvivid.com/privacy-policy\" style=\"text-decoration: none;\">this privacy policy</a> for use of our WPvividGoogle Drive authorization app (none of your backup data is sent to us)."
msgstr ""

#: includes/customclass/class-wpvivid-google-drive.php:307
#: includes/customclass/class-wpvivid-google-drive.php:435
msgid "Enter Your WPvividGoogle Drive Information"
msgstr ""

#: includes/customclass/class-wpvivid-google-drive.php:314
#: includes/customclass/class-wpvivid-google-drive.php:442
msgid "Enter a unique alias: e.g. WPvividGoogle Drive-001"
msgstr ""

#: includes/customclass/class-wpvivid-google-drive.php:364
msgid "Authenticate with WPvividGoogle Drive"
msgstr ""

#: includes/customclass/class-wpvivid-google-drive.php:369
msgid "Click the button to get WPvividGoogle authentication and add it to the storage list below."
msgstr ""

#: includes/customclass/class-wpvivid-one-drive.php:240
msgid "You have authenticated the Microsoft OneDrive account as your remote storage."
msgstr ""

#: includes/customclass/class-wpvivid-one-drive.php:260
msgid "Microsoft OneDrive"
msgstr ""

#: includes/customclass/class-wpvivid-one-drive.php:272
msgid "Please read <a target=\"_blank\" href=\"https://wpvivid.com/privacy-policy\" style=\"text-decoration: none;\">this privacy policy</a> for use of our Microsoft OneDrive authorization app (none of your backup data is sent to us)."
msgstr ""

#: includes/customclass/class-wpvivid-one-drive.php:275
#: includes/customclass/class-wpvivid-one-drive.php:403
msgid "Enter Your Microsoft OneDrive Information"
msgstr ""

#: includes/customclass/class-wpvivid-one-drive.php:282
#: includes/customclass/class-wpvivid-one-drive.php:410
msgid "Enter a unique alias: e.g. OneDrive-001"
msgstr ""

#: includes/customclass/class-wpvivid-one-drive.php:332
msgid "Authenticate with Microsoft OneDrive"
msgstr ""

#: includes/customclass/class-wpvivid-one-drive.php:337
msgid "Click the button to get Microsoft authentication and add it to the storage list below."
msgstr ""

#: includes/customclass/class-wpvivid-s3compat.php:403
msgid "DigitalOcean Spaces"
msgstr ""

#: includes/customclass/class-wpvivid-s3compat.php:412
#: includes/customclass/class-wpvivid-s3compat.php:527
msgid "Enter Your DigitalOcean Spaces Account"
msgstr ""

#: includes/customclass/class-wpvivid-s3compat.php:420
#: includes/customclass/class-wpvivid-s3compat.php:535
msgid "Enter a unique alias: e.g. DOS-001"
msgstr ""

#: includes/customclass/class-wpvivid-s3compat.php:432
#: includes/customclass/class-wpvivid-s3compat.php:547
msgid "DigitalOcean Spaces access key"
msgstr ""

#: includes/customclass/class-wpvivid-s3compat.php:437
#: includes/customclass/class-wpvivid-s3compat.php:552
msgid "Enter your DigitalOcean Spaces access key"
msgstr ""

#: includes/customclass/class-wpvivid-s3compat.php:444
#: includes/customclass/class-wpvivid-s3compat.php:559
msgid "DigitalOcean Spaces secret key"
msgstr ""

#: includes/customclass/class-wpvivid-s3compat.php:449
#: includes/customclass/class-wpvivid-s3compat.php:564
msgid "Enter your DigitalOcean Spaces secret key"
msgstr ""

#: includes/customclass/class-wpvivid-s3compat.php:456
#: includes/customclass/class-wpvivid-s3compat.php:571
msgid "Space Name(e.g. test)"
msgstr ""

#: includes/customclass/class-wpvivid-s3compat.php:461
#: includes/customclass/class-wpvivid-s3compat.php:576
msgid "Enter an existed Space to create a custom backup storage directory."
msgstr ""

#: includes/customclass/class-wpvivid-s3compat.php:473
#: includes/customclass/class-wpvivid-s3compat.php:588
msgid "Customize the directory where you want to store backups within the Space."
msgstr ""

#: includes/customclass/class-wpvivid-s3compat.php:480
#: includes/customclass/class-wpvivid-s3compat.php:595
msgid "region.digitaloceanspaces.com"
msgstr ""

#: includes/customclass/class-wpvivid-s3compat.php:485
#: includes/customclass/class-wpvivid-s3compat.php:600
msgid "Enter the DigitalOcean Endpoint for the storage"
msgstr ""

#: includes/customclass/class-wpvivid-s3compat.php:512
msgid "Click the button to connect to DigitalOcean Spaces storage and add it to the storage list below."
msgstr ""

#: includes/customclass/class-wpvivid-sftpclass.php:38
msgid "SFTP"
msgstr ""

#: includes/customclass/class-wpvivid-sftpclass.php:48
#: includes/customclass/class-wpvivid-sftpclass.php:163
msgid "Enter Your SFTP Account"
msgstr ""

#: includes/customclass/class-wpvivid-sftpclass.php:56
#: includes/customclass/class-wpvivid-sftpclass.php:171
msgid "Enter a unique alias: e.g. SFTP-001"
msgstr ""

#: includes/customclass/class-wpvivid-sftpclass.php:68
#: includes/customclass/class-wpvivid-sftpclass.php:183
msgid "Server Address"
msgstr ""

#: includes/customclass/class-wpvivid-sftpclass.php:73
#: includes/customclass/class-wpvivid-sftpclass.php:188
msgid "Enter the server address."
msgstr ""

#: includes/customclass/class-wpvivid-sftpclass.php:80
#: includes/customclass/class-wpvivid-sftpclass.php:195
msgid "User Name"
msgstr ""

#: includes/customclass/class-wpvivid-sftpclass.php:85
#: includes/customclass/class-wpvivid-sftpclass.php:200
msgid "Enter the user name."
msgstr ""

#: includes/customclass/class-wpvivid-sftpclass.php:92
#: includes/customclass/class-wpvivid-sftpclass.php:207
msgid "User Password"
msgstr ""

#: includes/customclass/class-wpvivid-sftpclass.php:97
#: includes/customclass/class-wpvivid-sftpclass.php:212
msgid "Enter the user password."
msgstr ""

#: includes/customclass/class-wpvivid-sftpclass.php:104
#: includes/customclass/class-wpvivid-sftpclass.php:219
msgid "Port"
msgstr ""

#: includes/customclass/class-wpvivid-sftpclass.php:109
#: includes/customclass/class-wpvivid-sftpclass.php:224
msgid "Enter the server port."
msgstr ""

#: includes/customclass/class-wpvivid-sftpclass.php:116
#: includes/customclass/class-wpvivid-sftpclass.php:231
msgid "Absolute path must exist(e.g. /var)"
msgstr ""

#: includes/customclass/class-wpvivid-sftpclass.php:121
#: includes/customclass/class-wpvivid-sftpclass.php:236
msgid "Enter an absolute path and a custom subdirectory (optional) for holding the backups of current website. For example, /var/customfolder/"
msgstr ""

#: includes/customclass/class-wpvivid-sftpclass.php:148
msgid "Click the button to connect to SFTP server and add it to the storage list below."
msgstr ""

index.php000064400000000032151327705670006374 0ustar00<?php // Silence is goldenvendor/psr/log/LICENSE000064400000002075151327705670010454 0ustar00Copyright (c) 2012 PHP Framework Interoperability Group

Permission is hereby granted, free of charge, to any person obtaining a copy 
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights 
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 
copies of the Software, and to permit persons to whom the Software is 
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in 
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
vendor/psr/log/.gitignore000064400000000007151327705670011430 0ustar00vendor
vendor/psr/log/composer.json000064400000001070151327705670012163 0ustar00{
    "name": "psr/log",
    "description": "Common interface for logging libraries",
    "keywords": ["psr", "psr-3", "log"],
    "homepage": "https://github.com/php-fig/log",
    "license": "MIT",
    "authors": [
        {
            "name": "PHP-FIG",
            "homepage": "http://www.php-fig.org/"
        }
    ],
    "require": {
        "php": ">=5.3.0"
    },
    "autoload": {
        "psr-4": {
            "WPvividPsr\\Log\\": "Psr/Log/"
        }
    },
    "extra": {
        "branch-alias": {
            "dev-master": "1.0.x-dev"
        }
    }
}
vendor/psr/log/Psr/Log/LoggerAwareTrait.php000064400000000624151327705670014646 0ustar00<?php

namespace WPvividPsr\Log;

/**
 * Basic Implementation of LoggerAwareInterface.
 */
trait LoggerAwareTrait
{
    /**
     * The logger instance.
     *
     * @var LoggerInterface
     */
    protected $logger;

    /**
     * Sets a logger.
     *
     * @param LoggerInterface $logger
     */
    public function setLogger(LoggerInterface $logger)
    {
        $this->logger = $logger;
    }
}
vendor/psr/log/Psr/Log/NullLogger.php000064400000001222151327705670013510 0ustar00<?php

namespace WPvividPsr\Log;

/**
 * This Logger can be used to avoid conditional log calls.
 *
 * Logging should always be optional, and if no logger is provided to your
 * library creating a NullLogger instance to have something to throw logs at
 * is a good way to avoid littering your code with `if ($this->logger) { }`
 * blocks.
 */
class NullLogger extends AbstractLogger
{
    /**
     * Logs with an arbitrary level.
     *
     * @param mixed  $level
     * @param string $message
     * @param array  $context
     *
     * @return void
     */
    public function log($level, $message, array $context = array())
    {
        // noop
    }
}
vendor/psr/log/Psr/Log/InvalidArgumentException.php000064400000000147151327705670016413 0ustar00<?php

namespace WPvividPsr\Log;

class InvalidArgumentException extends \InvalidArgumentException
{
}
vendor/psr/log/Psr/Log/LoggerInterface.php000064400000005746151327705670014515 0ustar00<?php

namespace WPvividPsr\Log;

/**
 * Describes a logger instance.
 *
 * The message MUST be a string or object implementing __toString().
 *
 * The message MAY contain placeholders in the form: {foo} where foo
 * will be replaced by the context data in key "foo".
 *
 * The context array can contain arbitrary data. The only assumption that
 * can be made by implementors is that if an Exception instance is given
 * to produce a stack trace, it MUST be in a key named "exception".
 *
 * See https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-3-logger-interface.md
 * for the full interface specification.
 */
interface LoggerInterface
{
    /**
     * System is unusable.
     *
     * @param string $message
     * @param array  $context
     *
     * @return void
     */
    public function emergency($message, array $context = array());

    /**
     * Action must be taken immediately.
     *
     * Example: Entire website down, database unavailable, etc. This should
     * trigger the SMS alerts and wake you up.
     *
     * @param string $message
     * @param array  $context
     *
     * @return void
     */
    public function alert($message, array $context = array());

    /**
     * Critical conditions.
     *
     * Example: Application component unavailable, unexpected exception.
     *
     * @param string $message
     * @param array  $context
     *
     * @return void
     */
    public function critical($message, array $context = array());

    /**
     * Runtime errors that do not require immediate action but should typically
     * be logged and monitored.
     *
     * @param string $message
     * @param array  $context
     *
     * @return void
     */
    public function error($message, array $context = array());

    /**
     * Exceptional occurrences that are not errors.
     *
     * Example: Use of deprecated APIs, poor use of an API, undesirable things
     * that are not necessarily wrong.
     *
     * @param string $message
     * @param array  $context
     *
     * @return void
     */
    public function warning($message, array $context = array());

    /**
     * Normal but significant events.
     *
     * @param string $message
     * @param array  $context
     *
     * @return void
     */
    public function notice($message, array $context = array());

    /**
     * Interesting events.
     *
     * Example: User logs in, SQL logs.
     *
     * @param string $message
     * @param array  $context
     *
     * @return void
     */
    public function info($message, array $context = array());

    /**
     * Detailed debug information.
     *
     * @param string $message
     * @param array  $context
     *
     * @return void
     */
    public function debug($message, array $context = array());

    /**
     * Logs with an arbitrary level.
     *
     * @param mixed  $level
     * @param string $message
     * @param array  $context
     *
     * @return void
     */
    public function log($level, $message, array $context = array());
}
vendor/psr/log/Psr/Log/LoggerTrait.php000064400000006446151327705670013676 0ustar00<?php

namespace WPvividPsr\Log;

/**
 * This is a simple Logger trait that classes unable to extend AbstractLogger
 * (because they extend another class, etc) can include.
 *
 * It simply delegates all log-level-specific methods to the `log` method to
 * reduce boilerplate code that a simple Logger that does the same thing with
 * messages regardless of the error level has to implement.
 */
trait LoggerTrait
{
    /**
     * System is unusable.
     *
     * @param string $message
     * @param array  $context
     *
     * @return void
     */
    public function emergency($message, array $context = array())
    {
        $this->log(LogLevel::EMERGENCY, $message, $context);
    }

    /**
     * Action must be taken immediately.
     *
     * Example: Entire website down, database unavailable, etc. This should
     * trigger the SMS alerts and wake you up.
     *
     * @param string $message
     * @param array  $context
     *
     * @return void
     */
    public function alert($message, array $context = array())
    {
        $this->log(LogLevel::ALERT, $message, $context);
    }

    /**
     * Critical conditions.
     *
     * Example: Application component unavailable, unexpected exception.
     *
     * @param string $message
     * @param array  $context
     *
     * @return void
     */
    public function critical($message, array $context = array())
    {
        $this->log(LogLevel::CRITICAL, $message, $context);
    }

    /**
     * Runtime errors that do not require immediate action but should typically
     * be logged and monitored.
     *
     * @param string $message
     * @param array  $context
     *
     * @return void
     */
    public function error($message, array $context = array())
    {
        $this->log(LogLevel::ERROR, $message, $context);
    }

    /**
     * Exceptional occurrences that are not errors.
     *
     * Example: Use of deprecated APIs, poor use of an API, undesirable things
     * that are not necessarily wrong.
     *
     * @param string $message
     * @param array  $context
     *
     * @return void
     */
    public function warning($message, array $context = array())
    {
        $this->log(LogLevel::WARNING, $message, $context);
    }

    /**
     * Normal but significant events.
     *
     * @param string $message
     * @param array  $context
     *
     * @return void
     */
    public function notice($message, array $context = array())
    {
        $this->log(LogLevel::NOTICE, $message, $context);
    }

    /**
     * Interesting events.
     *
     * Example: User logs in, SQL logs.
     *
     * @param string $message
     * @param array  $context
     *
     * @return void
     */
    public function info($message, array $context = array())
    {
        $this->log(LogLevel::INFO, $message, $context);
    }

    /**
     * Detailed debug information.
     *
     * @param string $message
     * @param array  $context
     *
     * @return void
     */
    public function debug($message, array $context = array())
    {
        $this->log(LogLevel::DEBUG, $message, $context);
    }

    /**
     * Logs with an arbitrary level.
     *
     * @param mixed  $level
     * @param string $message
     * @param array  $context
     *
     * @return void
     */
    abstract public function log($level, $message, array $context = array());
}
vendor/psr/log/Psr/Log/AbstractLogger.php000064400000006027151327705670014351 0ustar00<?php

namespace WPvividPsr\Log;

/**
 * This is a simple Logger implementation that other Loggers can inherit from.
 *
 * It simply delegates all log-level-specific methods to the `log` method to
 * reduce boilerplate code that a simple Logger that does the same thing with
 * messages regardless of the error level has to implement.
 */
abstract class AbstractLogger implements LoggerInterface
{
    /**
     * System is unusable.
     *
     * @param string $message
     * @param array  $context
     *
     * @return void
     */
    public function emergency($message, array $context = array())
    {
        $this->log(LogLevel::EMERGENCY, $message, $context);
    }

    /**
     * Action must be taken immediately.
     *
     * Example: Entire website down, database unavailable, etc. This should
     * trigger the SMS alerts and wake you up.
     *
     * @param string $message
     * @param array  $context
     *
     * @return void
     */
    public function alert($message, array $context = array())
    {
        $this->log(LogLevel::ALERT, $message, $context);
    }

    /**
     * Critical conditions.
     *
     * Example: Application component unavailable, unexpected exception.
     *
     * @param string $message
     * @param array  $context
     *
     * @return void
     */
    public function critical($message, array $context = array())
    {
        $this->log(LogLevel::CRITICAL, $message, $context);
    }

    /**
     * Runtime errors that do not require immediate action but should typically
     * be logged and monitored.
     *
     * @param string $message
     * @param array  $context
     *
     * @return void
     */
    public function error($message, array $context = array())
    {
        $this->log(LogLevel::ERROR, $message, $context);
    }

    /**
     * Exceptional occurrences that are not errors.
     *
     * Example: Use of deprecated APIs, poor use of an API, undesirable things
     * that are not necessarily wrong.
     *
     * @param string $message
     * @param array  $context
     *
     * @return void
     */
    public function warning($message, array $context = array())
    {
        $this->log(LogLevel::WARNING, $message, $context);
    }

    /**
     * Normal but significant events.
     *
     * @param string $message
     * @param array  $context
     *
     * @return void
     */
    public function notice($message, array $context = array())
    {
        $this->log(LogLevel::NOTICE, $message, $context);
    }

    /**
     * Interesting events.
     *
     * Example: User logs in, SQL logs.
     *
     * @param string $message
     * @param array  $context
     *
     * @return void
     */
    public function info($message, array $context = array())
    {
        $this->log(LogLevel::INFO, $message, $context);
    }

    /**
     * Detailed debug information.
     *
     * @param string $message
     * @param array  $context
     *
     * @return void
     */
    public function debug($message, array $context = array())
    {
        $this->log(LogLevel::DEBUG, $message, $context);
    }
}
vendor/psr/log/Psr/Log/LoggerAwareInterface.php000064400000000460151327705670015461 0ustar00<?php

namespace WPvividPsr\Log;

/**
 * Describes a logger-aware instance.
 */
interface LoggerAwareInterface
{
    /**
     * Sets a logger instance on the object.
     *
     * @param LoggerInterface $logger
     *
     * @return void
     */
    public function setLogger(LoggerInterface $logger);
}
vendor/psr/log/Psr/Log/Test/TestLogger.php000064400000010631151327705670014440 0ustar00<?php

namespace WPvividPsr\Log\Test;

use WPvividPsr\Log\AbstractLogger;

/**
 * Used for testing purposes.
 *
 * It records all records and gives you access to them for verification.
 *
 * @method bool hasEmergency($record)
 * @method bool hasAlert($record)
 * @method bool hasCritical($record)
 * @method bool hasError($record)
 * @method bool hasWarning($record)
 * @method bool hasNotice($record)
 * @method bool hasInfo($record)
 * @method bool hasDebug($record)
 *
 * @method bool hasEmergencyRecords()
 * @method bool hasAlertRecords()
 * @method bool hasCriticalRecords()
 * @method bool hasErrorRecords()
 * @method bool hasWarningRecords()
 * @method bool hasNoticeRecords()
 * @method bool hasInfoRecords()
 * @method bool hasDebugRecords()
 *
 * @method bool hasEmergencyThatContains($message)
 * @method bool hasAlertThatContains($message)
 * @method bool hasCriticalThatContains($message)
 * @method bool hasErrorThatContains($message)
 * @method bool hasWarningThatContains($message)
 * @method bool hasNoticeThatContains($message)
 * @method bool hasInfoThatContains($message)
 * @method bool hasDebugThatContains($message)
 *
 * @method bool hasEmergencyThatMatches($message)
 * @method bool hasAlertThatMatches($message)
 * @method bool hasCriticalThatMatches($message)
 * @method bool hasErrorThatMatches($message)
 * @method bool hasWarningThatMatches($message)
 * @method bool hasNoticeThatMatches($message)
 * @method bool hasInfoThatMatches($message)
 * @method bool hasDebugThatMatches($message)
 *
 * @method bool hasEmergencyThatPasses($message)
 * @method bool hasAlertThatPasses($message)
 * @method bool hasCriticalThatPasses($message)
 * @method bool hasErrorThatPasses($message)
 * @method bool hasWarningThatPasses($message)
 * @method bool hasNoticeThatPasses($message)
 * @method bool hasInfoThatPasses($message)
 * @method bool hasDebugThatPasses($message)
 */
class TestLogger extends AbstractLogger
{
    /**
     * @var array
     */
    public $records = [];

    public $recordsByLevel = [];

    /**
     * @inheritdoc
     */
    public function log($level, $message, array $context = [])
    {
        $record = [
            'level' => $level,
            'message' => $message,
            'context' => $context,
        ];

        $this->recordsByLevel[$record['level']][] = $record;
        $this->records[] = $record;
    }

    public function hasRecords($level)
    {
        return isset($this->recordsByLevel[$level]);
    }

    public function hasRecord($record, $level)
    {
        if (is_string($record)) {
            $record = ['message' => $record];
        }
        return $this->hasRecordThatPasses(function ($rec) use ($record) {
            if ($rec['message'] !== $record['message']) {
                return false;
            }
            if (isset($record['context']) && $rec['context'] !== $record['context']) {
                return false;
            }
            return true;
        }, $level);
    }

    public function hasRecordThatContains($message, $level)
    {
        return $this->hasRecordThatPasses(function ($rec) use ($message) {
            return strpos($rec['message'], $message) !== false;
        }, $level);
    }

    public function hasRecordThatMatches($regex, $level)
    {
        return $this->hasRecordThatPasses(function ($rec) use ($regex) {
            return preg_match($regex, $rec['message']) > 0;
        }, $level);
    }

    public function hasRecordThatPasses(callable $predicate, $level)
    {
        if (!isset($this->recordsByLevel[$level])) {
            return false;
        }
        foreach ($this->recordsByLevel[$level] as $i => $rec) {
            if (call_user_func($predicate, $rec, $i)) {
                return true;
            }
        }
        return false;
    }

    public function __call($method, $args)
    {
        if (preg_match('/(.*)(Debug|Info|Notice|Warning|Error|Critical|Alert|Emergency)(.*)/', $method, $matches) > 0) {
            $genericMethod = $matches[1] . ('Records' !== $matches[3] ? 'Record' : '') . $matches[3];
            $level = strtolower($matches[2]);
            if (method_exists($this, $genericMethod)) {
                $args[] = $level;
                return call_user_func_array([$this, $genericMethod], $args);
            }
        }
        throw new \BadMethodCallException('Call to undefined method ' . get_class($this) . '::' . $method . '()');
    }

    public function reset()
    {
        $this->records = [];
    }
}
vendor/psr/log/Psr/Log/Test/LoggerInterfaceTest.php000064400000011210151327705670016253 0ustar00<?php

namespace WPvividPsr\Log\Test;

use WPvividPsr\Log\LoggerInterface;
use WPvividPsr\Log\LogLevel;

/**
 * Provides a base test class for ensuring compliance with the LoggerInterface.
 *
 * Implementors can extend the class and implement abstract methods to run this
 * as part of their test suite.
 */
abstract class LoggerInterfaceTest extends \PHPUnit_Framework_TestCase
{
    /**
     * @return LoggerInterface
     */
    abstract public function getLogger();

    /**
     * This must return the log messages in order.
     *
     * The simple formatting of the messages is: "<LOG LEVEL> <MESSAGE>".
     *
     * Example ->error('Foo') would yield "error Foo".
     *
     * @return string[]
     */
    abstract public function getLogs();

    public function testImplements()
    {
        $this->assertInstanceOf('WPvividPsr\Log\LoggerInterface', $this->getLogger());
    }

    /**
     * @dataProvider provideLevelsAndMessages
     */
    public function testLogsAtAllLevels($level, $message)
    {
        $logger = $this->getLogger();
        $logger->{$level}($message, array('user' => 'Bob'));
        $logger->log($level, $message, array('user' => 'Bob'));

        $expected = array(
            $level.' message of level '.$level.' with context: Bob',
            $level.' message of level '.$level.' with context: Bob',
        );
        $this->assertEquals($expected, $this->getLogs());
    }

    public function provideLevelsAndMessages()
    {
        return array(
            LogLevel::EMERGENCY => array(LogLevel::EMERGENCY, 'message of level emergency with context: {user}'),
            LogLevel::ALERT => array(LogLevel::ALERT, 'message of level alert with context: {user}'),
            LogLevel::CRITICAL => array(LogLevel::CRITICAL, 'message of level critical with context: {user}'),
            LogLevel::ERROR => array(LogLevel::ERROR, 'message of level error with context: {user}'),
            LogLevel::WARNING => array(LogLevel::WARNING, 'message of level warning with context: {user}'),
            LogLevel::NOTICE => array(LogLevel::NOTICE, 'message of level notice with context: {user}'),
            LogLevel::INFO => array(LogLevel::INFO, 'message of level info with context: {user}'),
            LogLevel::DEBUG => array(LogLevel::DEBUG, 'message of level debug with context: {user}'),
        );
    }

    /**
     * @expectedException \Psr\Log\InvalidArgumentException
     */
    public function testThrowsOnInvalidLevel()
    {
        $logger = $this->getLogger();
        $logger->log('invalid level', 'Foo');
    }

    public function testContextReplacement()
    {
        $logger = $this->getLogger();
        $logger->info('{Message {nothing} {user} {foo.bar} a}', array('user' => 'Bob', 'foo.bar' => 'Bar'));

        $expected = array('info {Message {nothing} Bob Bar a}');
        $this->assertEquals($expected, $this->getLogs());
    }

    public function testObjectCastToString()
    {
        if (method_exists($this, 'createPartialMock')) {
            $dummy = $this->createPartialMock('WPvividPsr\Log\Test\DummyTest', array('__toString'));
        } else {
            $dummy = $this->getMock('WPvividPsr\Log\Test\DummyTest', array('__toString'));
        }
        $dummy->expects($this->once())
            ->method('__toString')
            ->will($this->returnValue('DUMMY'));

        $this->getLogger()->warning($dummy);

        $expected = array('warning DUMMY');
        $this->assertEquals($expected, $this->getLogs());
    }

    public function testContextCanContainAnything()
    {
        $closed = fopen('php://memory', 'r');
        fclose($closed);

        $context = array(
            'bool' => true,
            'null' => null,
            'string' => 'Foo',
            'int' => 0,
            'float' => 0.5,
            'nested' => array('with object' => new DummyTest),
            'object' => new \DateTime,
            'resource' => fopen('php://memory', 'r'),
            'closed' => $closed,
        );

        $this->getLogger()->warning('Crazy context data', $context);

        $expected = array('warning Crazy context data');
        $this->assertEquals($expected, $this->getLogs());
    }

    public function testContextExceptionKeyCanBeExceptionOrOtherValues()
    {
        $logger = $this->getLogger();
        $logger->warning('Random message', array('exception' => 'oops'));
        $logger->critical('Uncaught Exception!', array('exception' => new \LogicException('Fail')));

        $expected = array(
            'warning Random message',
            'critical Uncaught Exception!'
        );
        $this->assertEquals($expected, $this->getLogs());
    }
}

class DummyTest
{
    public function __toString()
    {
    }
}
vendor/psr/log/Psr/Log/LogLevel.php000064400000000527151327705670013156 0ustar00<?php

namespace WPvividPsr\Log;

/**
 * Describes log levels.
 */
class LogLevel
{
    const EMERGENCY = 'emergency';
    const ALERT     = 'alert';
    const CRITICAL  = 'critical';
    const ERROR     = 'error';
    const WARNING   = 'warning';
    const NOTICE    = 'notice';
    const INFO      = 'info';
    const DEBUG     = 'debug';
}
vendor/psr/log/README.md000064400000002201151327705670010715 0ustar00PSR Log
=======

This repository holds all interfaces/classes/traits related to
[PSR-3](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-3-logger-interface.md).

Note that this is not a logger of its own. It is merely an interface that
describes a logger. See the specification for more details.

Installation
------------

```bash
composer require psr/log
```

Usage
-----

If you need a logger, you can use the interface like this:

```php
<?php

use Psr\Log\LoggerInterface;

class Foo
{
    private $logger;

    public function __construct(LoggerInterface $logger = null)
    {
        $this->logger = $logger;
    }

    public function doSomething()
    {
        if ($this->logger) {
            $this->logger->info('Doing work');
        }

        // do something useful
    }
}
```

You can then pick one of the implementations of the interface to get a logger.

If you want to implement the interface, you can require this package and
implement `Psr\Log\LoggerInterface` in your code. Please read the
[specification text](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-3-logger-interface.md)
for details.
vendor/psr/cache/composer.json000064400000001010151327705670012437 0ustar00{
    "name": "psr/cache",
    "description": "Common interface for caching libraries",
    "keywords": ["psr", "psr-6", "cache"],
    "license": "MIT",
    "authors": [
        {
            "name": "PHP-FIG",
            "homepage": "http://www.php-fig.org/"
        }
    ],
    "require": {
        "php": ">=5.3.0"
    },
    "autoload": {
        "psr-4": {
            "WPvividPsr\\Cache\\": "src/"
        }
    },
    "extra": {
        "branch-alias": {
            "dev-master": "1.0.x-dev"
        }
    }
}
vendor/psr/cache/src/CacheException.php000064400000000226151327705670014107 0ustar00<?php

namespace WPvividPsr\Cache;

/**
 * Exception interface for all exceptions thrown by an Implementing Library.
 */
interface CacheException
{
}
vendor/psr/cache/src/InvalidArgumentException.php000064400000000462151327705670016177 0ustar00<?php

namespace WPvividPsr\Cache;

/**
 * Exception interface for invalid cache arguments.
 *
 * Any time an invalid argument is passed into a method it must throw an
 * exception class which implements Psr\Cache\InvalidArgumentException.
 */
interface InvalidArgumentException extends CacheException
{
}
vendor/psr/cache/src/CacheItemInterface.php000064400000007303151327705670014673 0ustar00<?php

namespace WPvividPsr\Cache;

/**
 * CacheItemInterface defines an interface for interacting with objects inside a cache.
 *
 * Each Item object MUST be associated with a specific key, which can be set
 * according to the implementing system and is typically passed by the
 * Cache\CacheItemPoolInterface object.
 *
 * The Cache\CacheItemInterface object encapsulates the storage and retrieval of
 * cache items. Each Cache\CacheItemInterface is generated by a
 * Cache\CacheItemPoolInterface object, which is responsible for any required
 * setup as well as associating the object with a unique Key.
 * Cache\CacheItemInterface objects MUST be able to store and retrieve any type
 * of PHP value defined in the Data section of the specification.
 *
 * Calling Libraries MUST NOT instantiate Item objects themselves. They may only
 * be requested from a Pool object via the getItem() method.  Calling Libraries
 * SHOULD NOT assume that an Item created by one Implementing Library is
 * compatible with a Pool from another Implementing Library.
 */
interface CacheItemInterface
{
    /**
     * Returns the key for the current cache item.
     *
     * The key is loaded by the Implementing Library, but should be available to
     * the higher level callers when needed.
     *
     * @return string
     *   The key string for this cache item.
     */
    public function getKey();

    /**
     * Retrieves the value of the item from the cache associated with this object's key.
     *
     * The value returned must be identical to the value originally stored by set().
     *
     * If isHit() returns false, this method MUST return null. Note that null
     * is a legitimate cached value, so the isHit() method SHOULD be used to
     * differentiate between "null value was found" and "no value was found."
     *
     * @return mixed
     *   The value corresponding to this cache item's key, or null if not found.
     */
    public function get();

    /**
     * Confirms if the cache item lookup resulted in a cache hit.
     *
     * Note: This method MUST NOT have a race condition between calling isHit()
     * and calling get().
     *
     * @return bool
     *   True if the request resulted in a cache hit. False otherwise.
     */
    public function isHit();

    /**
     * Sets the value represented by this cache item.
     *
     * The $value argument may be any item that can be serialized by PHP,
     * although the method of serialization is left up to the Implementing
     * Library.
     *
     * @param mixed $value
     *   The serializable value to be stored.
     *
     * @return static
     *   The invoked object.
     */
    public function set($value);

    /**
     * Sets the expiration time for this cache item.
     *
     * @param \DateTimeInterface|null $expiration
     *   The point in time after which the item MUST be considered expired.
     *   If null is passed explicitly, a default value MAY be used. If none is set,
     *   the value should be stored permanently or for as long as the
     *   implementation allows.
     *
     * @return static
     *   The called object.
     */
    public function expiresAt($expiration);

    /**
     * Sets the expiration time for this cache item.
     *
     * @param int|\DateInterval|null $time
     *   The period of time from the present after which the item MUST be considered
     *   expired. An integer parameter is understood to be the time in seconds until
     *   expiration. If null is passed explicitly, a default value MAY be used.
     *   If none is set, the value should be stored permanently or for as long as the
     *   implementation allows.
     *
     * @return static
     *   The called object.
     */
    public function expiresAfter($time);
}
vendor/psr/cache/src/CacheItemPoolInterface.php000064400000010454151327705670015526 0ustar00<?php

namespace WPvividPsr\Cache;

/**
 * CacheItemPoolInterface generates CacheItemInterface objects.
 *
 * The primary purpose of Cache\CacheItemPoolInterface is to accept a key from
 * the Calling Library and return the associated Cache\CacheItemInterface object.
 * It is also the primary point of interaction with the entire cache collection.
 * All configuration and initialization of the Pool is left up to an
 * Implementing Library.
 */
interface CacheItemPoolInterface
{
    /**
     * Returns a Cache Item representing the specified key.
     *
     * This method must always return a CacheItemInterface object, even in case of
     * a cache miss. It MUST NOT return null.
     *
     * @param string $key
     *   The key for which to return the corresponding Cache Item.
     *
     * @throws InvalidArgumentException
     *   If the $key string is not a legal value a \Psr\Cache\InvalidArgumentException
     *   MUST be thrown.
     *
     * @return CacheItemInterface
     *   The corresponding Cache Item.
     */
    public function getItem($key);

    /**
     * Returns a traversable set of cache items.
     *
     * @param string[] $keys
     *   An indexed array of keys of items to retrieve.
     *
     * @throws InvalidArgumentException
     *   If any of the keys in $keys are not a legal value a \Psr\Cache\InvalidArgumentException
     *   MUST be thrown.
     *
     * @return array|\Traversable
     *   A traversable collection of Cache Items keyed by the cache keys of
     *   each item. A Cache item will be returned for each key, even if that
     *   key is not found. However, if no keys are specified then an empty
     *   traversable MUST be returned instead.
     */
    public function getItems(array $keys = array());

    /**
     * Confirms if the cache contains specified cache item.
     *
     * Note: This method MAY avoid retrieving the cached value for performance reasons.
     * This could result in a race condition with CacheItemInterface::get(). To avoid
     * such situation use CacheItemInterface::isHit() instead.
     *
     * @param string $key
     *   The key for which to check existence.
     *
     * @throws InvalidArgumentException
     *   If the $key string is not a legal value a \Psr\Cache\InvalidArgumentException
     *   MUST be thrown.
     *
     * @return bool
     *   True if item exists in the cache, false otherwise.
     */
    public function hasItem($key);

    /**
     * Deletes all items in the pool.
     *
     * @return bool
     *   True if the pool was successfully cleared. False if there was an error.
     */
    public function clear();

    /**
     * Removes the item from the pool.
     *
     * @param string $key
     *   The key to delete.
     *
     * @throws InvalidArgumentException
     *   If the $key string is not a legal value a \Psr\Cache\InvalidArgumentException
     *   MUST be thrown.
     *
     * @return bool
     *   True if the item was successfully removed. False if there was an error.
     */
    public function deleteItem($key);

    /**
     * Removes multiple items from the pool.
     *
     * @param string[] $keys
     *   An array of keys that should be removed from the pool.

     * @throws InvalidArgumentException
     *   If any of the keys in $keys are not a legal value a \Psr\Cache\InvalidArgumentException
     *   MUST be thrown.
     *
     * @return bool
     *   True if the items were successfully removed. False if there was an error.
     */
    public function deleteItems(array $keys);

    /**
     * Persists a cache item immediately.
     *
     * @param CacheItemInterface $item
     *   The cache item to save.
     *
     * @return bool
     *   True if the item was successfully persisted. False if there was an error.
     */
    public function save(CacheItemInterface $item);

    /**
     * Sets a cache item to be persisted later.
     *
     * @param CacheItemInterface $item
     *   The cache item to save.
     *
     * @return bool
     *   False if the item could not be queued or if a commit was attempted and failed. True otherwise.
     */
    public function saveDeferred(CacheItemInterface $item);

    /**
     * Persists any deferred cache items.
     *
     * @return bool
     *   True if all not-yet-saved items were successfully saved or there were none. False otherwise.
     */
    public function commit();
}
vendor/psr/cache/README.md000064400000000425151327705670011205 0ustar00PSR Cache
=========

This repository holds all interfaces defined by
[PSR-6](http://www.php-fig.org/psr/psr-6/).

Note that this is not a Cache implementation of its own. It is merely an
interface that describes a Cache implementation. See the specification for more 
details.
vendor/psr/cache/LICENSE.txt000064400000002070151327705670011547 0ustar00Copyright (c) 2015 PHP Framework Interoperability Group

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
vendor/psr/cache/CHANGELOG.md000064400000001352151327705670011537 0ustar00# Changelog

All notable changes to this project will be documented in this file, in reverse chronological order by release.

## 1.0.1 - 2016-08-06

### Fixed

- Make spacing consistent in phpdoc annotations php-fig/cache#9 - chalasr
- Fix grammar in phpdoc annotations php-fig/cache#10 - chalasr
- Be more specific in docblocks that `getItems()` and `deleteItems()` take an array of strings (`string[]`) compared to just `array` php-fig/cache#8 - GrahamCampbell
- For `expiresAt()` and `expiresAfter()` in CacheItemInterface fix docblock to specify null as a valid parameters as well as an implementation of DateTimeInterface php-fig/cache#7 - GrahamCampbell

## 1.0.0 - 2015-12-11

Initial stable release; reflects accepted PSR-6 specification
vendor/psr/http-message/CHANGELOG.md000064400000002063151327705670013075 0ustar00# Changelog

All notable changes to this project will be documented in this file, in reverse chronological order by release.

## 1.0.1 - 2016-08-06

### Added

- Nothing.

### Deprecated

- Nothing.

### Removed

- Nothing.

### Fixed

- Updated all `@return self` annotation references in interfaces to use
  `@return static`, which more closelly follows the semantics of the
  specification.
- Updated the `MessageInterface::getHeaders()` return annotation to use the
  value `string[][]`, indicating the format is a nested array of strings.
- Updated the `@link` annotation for `RequestInterface::withRequestTarget()`
  to point to the correct section of RFC 7230.
- Updated the `ServerRequestInterface::withUploadedFiles()` parameter annotation
  to add the parameter name (`$uploadedFiles`).
- Updated a `@throws` annotation for the `UploadedFileInterface::moveTo()`
  method to correctly reference the method parameter (it was referencing an
  incorrect parameter name previously).

## 1.0.0 - 2016-05-18

Initial stable release; reflects accepted PSR-7 specification.
vendor/psr/http-message/LICENSE000064400000002075151327705670012274 0ustar00Copyright (c) 2014 PHP Framework Interoperability Group

Permission is hereby granted, free of charge, to any person obtaining a copy 
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights 
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 
copies of the Software, and to permit persons to whom the Software is 
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in 
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
vendor/psr/http-message/composer.json000064400000001164151327705670014007 0ustar00{
    "name": "psr/http-message",
    "description": "Common interface for HTTP messages",
    "keywords": ["psr", "psr-7", "http", "http-message", "request", "response"],
    "homepage": "https://github.com/php-fig/http-message",
    "license": "MIT",
    "authors": [
        {
            "name": "PHP-FIG",
            "homepage": "http://www.php-fig.org/"
        }
    ],
    "require": {
        "php": ">=5.3.0"
    },
    "autoload": {
        "psr-4": {
            "WPvividPsr\\Http\\Message\\": "src/"
        }
    },
    "extra": {
        "branch-alias": {
            "dev-master": "1.0.x-dev"
        }
    }
}
vendor/psr/http-message/src/ServerRequestInterface.php000064400000023571151327705670017233 0ustar00<?php

namespace WPvividPsr\Http\Message;

/**
 * Representation of an incoming, server-side HTTP request.
 *
 * Per the HTTP specification, this interface includes properties for
 * each of the following:
 *
 * - Protocol version
 * - HTTP method
 * - URI
 * - Headers
 * - Message body
 *
 * Additionally, it encapsulates all data as it has arrived to the
 * application from the CGI and/or PHP environment, including:
 *
 * - The values represented in $_SERVER.
 * - Any cookies provided (generally via $_COOKIE)
 * - Query string arguments (generally via $_GET, or as parsed via parse_str())
 * - Upload files, if any (as represented by $_FILES)
 * - Deserialized body parameters (generally from $_POST)
 *
 * $_SERVER values MUST be treated as immutable, as they represent application
 * state at the time of request; as such, no methods are provided to allow
 * modification of those values. The other values provide such methods, as they
 * can be restored from $_SERVER or the request body, and may need treatment
 * during the application (e.g., body parameters may be deserialized based on
 * content type).
 *
 * Additionally, this interface recognizes the utility of introspecting a
 * request to derive and match additional parameters (e.g., via URI path
 * matching, decrypting cookie values, deserializing non-form-encoded body
 * content, matching authorization headers to users, etc). These parameters
 * are stored in an "attributes" property.
 *
 * Requests are considered immutable; all methods that might change state MUST
 * be implemented such that they retain the internal state of the current
 * message and return an instance that contains the changed state.
 */
interface ServerRequestInterface extends RequestInterface
{
    /**
     * Retrieve server parameters.
     *
     * Retrieves data related to the incoming request environment,
     * typically derived from PHP's $_SERVER superglobal. The data IS NOT
     * REQUIRED to originate from $_SERVER.
     *
     * @return array
     */
    public function getServerParams();

    /**
     * Retrieve cookies.
     *
     * Retrieves cookies sent by the client to the server.
     *
     * The data MUST be compatible with the structure of the $_COOKIE
     * superglobal.
     *
     * @return array
     */
    public function getCookieParams();

    /**
     * Return an instance with the specified cookies.
     *
     * The data IS NOT REQUIRED to come from the $_COOKIE superglobal, but MUST
     * be compatible with the structure of $_COOKIE. Typically, this data will
     * be injected at instantiation.
     *
     * This method MUST NOT update the related Cookie header of the request
     * instance, nor related values in the server params.
     *
     * This method MUST be implemented in such a way as to retain the
     * immutability of the message, and MUST return an instance that has the
     * updated cookie values.
     *
     * @param array $cookies Array of key/value pairs representing cookies.
     * @return static
     */
    public function withCookieParams(array $cookies);

    /**
     * Retrieve query string arguments.
     *
     * Retrieves the deserialized query string arguments, if any.
     *
     * Note: the query params might not be in sync with the URI or server
     * params. If you need to ensure you are only getting the original
     * values, you may need to parse the query string from `getUri()->getQuery()`
     * or from the `QUERY_STRING` server param.
     *
     * @return array
     */
    public function getQueryParams();

    /**
     * Return an instance with the specified query string arguments.
     *
     * These values SHOULD remain immutable over the course of the incoming
     * request. They MAY be injected during instantiation, such as from PHP's
     * $_GET superglobal, or MAY be derived from some other value such as the
     * URI. In cases where the arguments are parsed from the URI, the data
     * MUST be compatible with what PHP's parse_str() would return for
     * purposes of how duplicate query parameters are handled, and how nested
     * sets are handled.
     *
     * Setting query string arguments MUST NOT change the URI stored by the
     * request, nor the values in the server params.
     *
     * This method MUST be implemented in such a way as to retain the
     * immutability of the message, and MUST return an instance that has the
     * updated query string arguments.
     *
     * @param array $query Array of query string arguments, typically from
     *     $_GET.
     * @return static
     */
    public function withQueryParams(array $query);

    /**
     * Retrieve normalized file upload data.
     *
     * This method returns upload metadata in a normalized tree, with each leaf
     * an instance of Psr\Http\Message\UploadedFileInterface.
     *
     * These values MAY be prepared from $_FILES or the message body during
     * instantiation, or MAY be injected via withUploadedFiles().
     *
     * @return array An array tree of UploadedFileInterface instances; an empty
     *     array MUST be returned if no data is present.
     */
    public function getUploadedFiles();

    /**
     * Create a new instance with the specified uploaded files.
     *
     * This method MUST be implemented in such a way as to retain the
     * immutability of the message, and MUST return an instance that has the
     * updated body parameters.
     *
     * @param array $uploadedFiles An array tree of UploadedFileInterface instances.
     * @return static
     * @throws \InvalidArgumentException if an invalid structure is provided.
     */
    public function withUploadedFiles(array $uploadedFiles);

    /**
     * Retrieve any parameters provided in the request body.
     *
     * If the request Content-Type is either application/x-www-form-urlencoded
     * or multipart/form-data, and the request method is POST, this method MUST
     * return the contents of $_POST.
     *
     * Otherwise, this method may return any results of deserializing
     * the request body content; as parsing returns structured content, the
     * potential types MUST be arrays or objects only. A null value indicates
     * the absence of body content.
     *
     * @return null|array|object The deserialized body parameters, if any.
     *     These will typically be an array or object.
     */
    public function getParsedBody();

    /**
     * Return an instance with the specified body parameters.
     *
     * These MAY be injected during instantiation.
     *
     * If the request Content-Type is either application/x-www-form-urlencoded
     * or multipart/form-data, and the request method is POST, use this method
     * ONLY to inject the contents of $_POST.
     *
     * The data IS NOT REQUIRED to come from $_POST, but MUST be the results of
     * deserializing the request body content. Deserialization/parsing returns
     * structured data, and, as such, this method ONLY accepts arrays or objects,
     * or a null value if nothing was available to parse.
     *
     * As an example, if content negotiation determines that the request data
     * is a JSON payload, this method could be used to create a request
     * instance with the deserialized parameters.
     *
     * This method MUST be implemented in such a way as to retain the
     * immutability of the message, and MUST return an instance that has the
     * updated body parameters.
     *
     * @param null|array|object $data The deserialized body data. This will
     *     typically be in an array or object.
     * @return static
     * @throws \InvalidArgumentException if an unsupported argument type is
     *     provided.
     */
    public function withParsedBody($data);

    /**
     * Retrieve attributes derived from the request.
     *
     * The request "attributes" may be used to allow injection of any
     * parameters derived from the request: e.g., the results of path
     * match operations; the results of decrypting cookies; the results of
     * deserializing non-form-encoded message bodies; etc. Attributes
     * will be application and request specific, and CAN be mutable.
     *
     * @return array Attributes derived from the request.
     */
    public function getAttributes();

    /**
     * Retrieve a single derived request attribute.
     *
     * Retrieves a single derived request attribute as described in
     * getAttributes(). If the attribute has not been previously set, returns
     * the default value as provided.
     *
     * This method obviates the need for a hasAttribute() method, as it allows
     * specifying a default value to return if the attribute is not found.
     *
     * @see getAttributes()
     * @param string $name The attribute name.
     * @param mixed $default Default value to return if the attribute does not exist.
     * @return mixed
     */
    public function getAttribute($name, $default = null);

    /**
     * Return an instance with the specified derived request attribute.
     *
     * This method allows setting a single derived request attribute as
     * described in getAttributes().
     *
     * This method MUST be implemented in such a way as to retain the
     * immutability of the message, and MUST return an instance that has the
     * updated attribute.
     *
     * @see getAttributes()
     * @param string $name The attribute name.
     * @param mixed $value The value of the attribute.
     * @return static
     */
    public function withAttribute($name, $value);

    /**
     * Return an instance that removes the specified derived request attribute.
     *
     * This method allows removing a single derived request attribute as
     * described in getAttributes().
     *
     * This method MUST be implemented in such a way as to retain the
     * immutability of the message, and MUST return an instance that removes
     * the attribute.
     *
     * @see getAttributes()
     * @param string $name The attribute name.
     * @return static
     */
    public function withoutAttribute($name);
}
vendor/psr/http-message/src/MessageInterface.php000064400000015405151327705670015775 0ustar00<?php

namespace WPvividPsr\Http\Message;

/**
 * HTTP messages consist of requests from a client to a server and responses
 * from a server to a client. This interface defines the methods common to
 * each.
 *
 * Messages are considered immutable; all methods that might change state MUST
 * be implemented such that they retain the internal state of the current
 * message and return an instance that contains the changed state.
 *
 * @link http://www.ietf.org/rfc/rfc7230.txt
 * @link http://www.ietf.org/rfc/rfc7231.txt
 */
interface MessageInterface
{
    /**
     * Retrieves the HTTP protocol version as a string.
     *
     * The string MUST contain only the HTTP version number (e.g., "1.1", "1.0").
     *
     * @return string HTTP protocol version.
     */
    public function getProtocolVersion();

    /**
     * Return an instance with the specified HTTP protocol version.
     *
     * The version string MUST contain only the HTTP version number (e.g.,
     * "1.1", "1.0").
     *
     * This method MUST be implemented in such a way as to retain the
     * immutability of the message, and MUST return an instance that has the
     * new protocol version.
     *
     * @param string $version HTTP protocol version
     * @return static
     */
    public function withProtocolVersion($version);

    /**
     * Retrieves all message header values.
     *
     * The keys represent the header name as it will be sent over the wire, and
     * each value is an array of strings associated with the header.
     *
     *     // Represent the headers as a string
     *     foreach ($message->getHeaders() as $name => $values) {
     *         echo $name . ": " . implode(", ", $values);
     *     }
     *
     *     // Emit headers iteratively:
     *     foreach ($message->getHeaders() as $name => $values) {
     *         foreach ($values as $value) {
     *             header(sprintf('%s: %s', $name, $value), false);
     *         }
     *     }
     *
     * While header names are not case-sensitive, getHeaders() will preserve the
     * exact case in which headers were originally specified.
     *
     * @return string[][] Returns an associative array of the message's headers. Each
     *     key MUST be a header name, and each value MUST be an array of strings
     *     for that header.
     */
    public function getHeaders();

    /**
     * Checks if a header exists by the given case-insensitive name.
     *
     * @param string $name Case-insensitive header field name.
     * @return bool Returns true if any header names match the given header
     *     name using a case-insensitive string comparison. Returns false if
     *     no matching header name is found in the message.
     */
    public function hasHeader($name);

    /**
     * Retrieves a message header value by the given case-insensitive name.
     *
     * This method returns an array of all the header values of the given
     * case-insensitive header name.
     *
     * If the header does not appear in the message, this method MUST return an
     * empty array.
     *
     * @param string $name Case-insensitive header field name.
     * @return string[] An array of string values as provided for the given
     *    header. If the header does not appear in the message, this method MUST
     *    return an empty array.
     */
    public function getHeader($name);

    /**
     * Retrieves a comma-separated string of the values for a single header.
     *
     * This method returns all of the header values of the given
     * case-insensitive header name as a string concatenated together using
     * a comma.
     *
     * NOTE: Not all header values may be appropriately represented using
     * comma concatenation. For such headers, use getHeader() instead
     * and supply your own delimiter when concatenating.
     *
     * If the header does not appear in the message, this method MUST return
     * an empty string.
     *
     * @param string $name Case-insensitive header field name.
     * @return string A string of values as provided for the given header
     *    concatenated together using a comma. If the header does not appear in
     *    the message, this method MUST return an empty string.
     */
    public function getHeaderLine($name);

    /**
     * Return an instance with the provided value replacing the specified header.
     *
     * While header names are case-insensitive, the casing of the header will
     * be preserved by this function, and returned from getHeaders().
     *
     * This method MUST be implemented in such a way as to retain the
     * immutability of the message, and MUST return an instance that has the
     * new and/or updated header and value.
     *
     * @param string $name Case-insensitive header field name.
     * @param string|string[] $value Header value(s).
     * @return static
     * @throws \InvalidArgumentException for invalid header names or values.
     */
    public function withHeader($name, $value);

    /**
     * Return an instance with the specified header appended with the given value.
     *
     * Existing values for the specified header will be maintained. The new
     * value(s) will be appended to the existing list. If the header did not
     * exist previously, it will be added.
     *
     * This method MUST be implemented in such a way as to retain the
     * immutability of the message, and MUST return an instance that has the
     * new header and/or value.
     *
     * @param string $name Case-insensitive header field name to add.
     * @param string|string[] $value Header value(s).
     * @return static
     * @throws \InvalidArgumentException for invalid header names or values.
     */
    public function withAddedHeader($name, $value);

    /**
     * Return an instance without the specified header.
     *
     * Header resolution MUST be done without case-sensitivity.
     *
     * This method MUST be implemented in such a way as to retain the
     * immutability of the message, and MUST return an instance that removes
     * the named header.
     *
     * @param string $name Case-insensitive header field name to remove.
     * @return static
     */
    public function withoutHeader($name);

    /**
     * Gets the body of the message.
     *
     * @return StreamInterface Returns the body as a stream.
     */
    public function getBody();

    /**
     * Return an instance with the specified message body.
     *
     * The body MUST be a StreamInterface object.
     *
     * This method MUST be implemented in such a way as to retain the
     * immutability of the message, and MUST return a new instance that has the
     * new body stream.
     *
     * @param StreamInterface $body Body.
     * @return static
     * @throws \InvalidArgumentException When the body is not valid.
     */
    public function withBody(StreamInterface $body);
}
vendor/psr/http-message/src/UploadedFileInterface.php000064400000011130151327705670016735 0ustar00<?php

namespace WPvividPsr\Http\Message;

/**
 * Value object representing a file uploaded through an HTTP request.
 *
 * Instances of this interface are considered immutable; all methods that
 * might change state MUST be implemented such that they retain the internal
 * state of the current instance and return an instance that contains the
 * changed state.
 */
interface UploadedFileInterface
{
    /**
     * Retrieve a stream representing the uploaded file.
     *
     * This method MUST return a StreamInterface instance, representing the
     * uploaded file. The purpose of this method is to allow utilizing native PHP
     * stream functionality to manipulate the file upload, such as
     * stream_copy_to_stream() (though the result will need to be decorated in a
     * native PHP stream wrapper to work with such functions).
     *
     * If the moveTo() method has been called previously, this method MUST raise
     * an exception.
     *
     * @return StreamInterface Stream representation of the uploaded file.
     * @throws \RuntimeException in cases when no stream is available or can be
     *     created.
     */
    public function getStream();

    /**
     * Move the uploaded file to a new location.
     *
     * Use this method as an alternative to move_uploaded_file(). This method is
     * guaranteed to work in both SAPI and non-SAPI environments.
     * Implementations must determine which environment they are in, and use the
     * appropriate method (move_uploaded_file(), rename(), or a stream
     * operation) to perform the operation.
     *
     * $targetPath may be an absolute path, or a relative path. If it is a
     * relative path, resolution should be the same as used by PHP's rename()
     * function.
     *
     * The original file or stream MUST be removed on completion.
     *
     * If this method is called more than once, any subsequent calls MUST raise
     * an exception.
     *
     * When used in an SAPI environment where $_FILES is populated, when writing
     * files via moveTo(), is_uploaded_file() and move_uploaded_file() SHOULD be
     * used to ensure permissions and upload status are verified correctly.
     *
     * If you wish to move to a stream, use getStream(), as SAPI operations
     * cannot guarantee writing to stream destinations.
     *
     * @see http://php.net/is_uploaded_file
     * @see http://php.net/move_uploaded_file
     * @param string $targetPath Path to which to move the uploaded file.
     * @throws \InvalidArgumentException if the $targetPath specified is invalid.
     * @throws \RuntimeException on any error during the move operation, or on
     *     the second or subsequent call to the method.
     */
    public function moveTo($targetPath);
    
    /**
     * Retrieve the file size.
     *
     * Implementations SHOULD return the value stored in the "size" key of
     * the file in the $_FILES array if available, as PHP calculates this based
     * on the actual size transmitted.
     *
     * @return int|null The file size in bytes or null if unknown.
     */
    public function getSize();
    
    /**
     * Retrieve the error associated with the uploaded file.
     *
     * The return value MUST be one of PHP's UPLOAD_ERR_XXX constants.
     *
     * If the file was uploaded successfully, this method MUST return
     * UPLOAD_ERR_OK.
     *
     * Implementations SHOULD return the value stored in the "error" key of
     * the file in the $_FILES array.
     *
     * @see http://php.net/manual/en/features.file-upload.errors.php
     * @return int One of PHP's UPLOAD_ERR_XXX constants.
     */
    public function getError();
    
    /**
     * Retrieve the filename sent by the client.
     *
     * Do not trust the value returned by this method. A client could send
     * a malicious filename with the intention to corrupt or hack your
     * application.
     *
     * Implementations SHOULD return the value stored in the "name" key of
     * the file in the $_FILES array.
     *
     * @return string|null The filename sent by the client or null if none
     *     was provided.
     */
    public function getClientFilename();
    
    /**
     * Retrieve the media type sent by the client.
     *
     * Do not trust the value returned by this method. A client could send
     * a malicious media type with the intention to corrupt or hack your
     * application.
     *
     * Implementations SHOULD return the value stored in the "type" key of
     * the file in the $_FILES array.
     *
     * @return string|null The media type sent by the client or null if none
     *     was provided.
     */
    public function getClientMediaType();
}
vendor/psr/http-message/src/RequestInterface.php000064400000011325151327705670016036 0ustar00<?php

namespace WPvividPsr\Http\Message;

/**
 * Representation of an outgoing, client-side request.
 *
 * Per the HTTP specification, this interface includes properties for
 * each of the following:
 *
 * - Protocol version
 * - HTTP method
 * - URI
 * - Headers
 * - Message body
 *
 * During construction, implementations MUST attempt to set the Host header from
 * a provided URI if no Host header is provided.
 *
 * Requests are considered immutable; all methods that might change state MUST
 * be implemented such that they retain the internal state of the current
 * message and return an instance that contains the changed state.
 */
interface RequestInterface extends MessageInterface
{
    /**
     * Retrieves the message's request target.
     *
     * Retrieves the message's request-target either as it will appear (for
     * clients), as it appeared at request (for servers), or as it was
     * specified for the instance (see withRequestTarget()).
     *
     * In most cases, this will be the origin-form of the composed URI,
     * unless a value was provided to the concrete implementation (see
     * withRequestTarget() below).
     *
     * If no URI is available, and no request-target has been specifically
     * provided, this method MUST return the string "/".
     *
     * @return string
     */
    public function getRequestTarget();

    /**
     * Return an instance with the specific request-target.
     *
     * If the request needs a non-origin-form request-target — e.g., for
     * specifying an absolute-form, authority-form, or asterisk-form —
     * this method may be used to create an instance with the specified
     * request-target, verbatim.
     *
     * This method MUST be implemented in such a way as to retain the
     * immutability of the message, and MUST return an instance that has the
     * changed request target.
     *
     * @link http://tools.ietf.org/html/rfc7230#section-5.3 (for the various
     *     request-target forms allowed in request messages)
     * @param mixed $requestTarget
     * @return static
     */
    public function withRequestTarget($requestTarget);

    /**
     * Retrieves the HTTP method of the request.
     *
     * @return string Returns the request method.
     */
    public function getMethod();

    /**
     * Return an instance with the provided HTTP method.
     *
     * While HTTP method names are typically all uppercase characters, HTTP
     * method names are case-sensitive and thus implementations SHOULD NOT
     * modify the given string.
     *
     * This method MUST be implemented in such a way as to retain the
     * immutability of the message, and MUST return an instance that has the
     * changed request method.
     *
     * @param string $method Case-sensitive method.
     * @return static
     * @throws \InvalidArgumentException for invalid HTTP methods.
     */
    public function withMethod($method);

    /**
     * Retrieves the URI instance.
     *
     * This method MUST return a UriInterface instance.
     *
     * @link http://tools.ietf.org/html/rfc3986#section-4.3
     * @return UriInterface Returns a UriInterface instance
     *     representing the URI of the request.
     */
    public function getUri();

    /**
     * Returns an instance with the provided URI.
     *
     * This method MUST update the Host header of the returned request by
     * default if the URI contains a host component. If the URI does not
     * contain a host component, any pre-existing Host header MUST be carried
     * over to the returned request.
     *
     * You can opt-in to preserving the original state of the Host header by
     * setting `$preserveHost` to `true`. When `$preserveHost` is set to
     * `true`, this method interacts with the Host header in the following ways:
     *
     * - If the Host header is missing or empty, and the new URI contains
     *   a host component, this method MUST update the Host header in the returned
     *   request.
     * - If the Host header is missing or empty, and the new URI does not contain a
     *   host component, this method MUST NOT update the Host header in the returned
     *   request.
     * - If a Host header is present and non-empty, this method MUST NOT update
     *   the Host header in the returned request.
     *
     * This method MUST be implemented in such a way as to retain the
     * immutability of the message, and MUST return an instance that has the
     * new UriInterface instance.
     *
     * @link http://tools.ietf.org/html/rfc3986#section-4.3
     * @param UriInterface $uri New request URI to use.
     * @param bool $preserveHost Preserve the original state of the Host header.
     * @return static
     */
    public function withUri(UriInterface $uri, $preserveHost = false);
}
vendor/psr/http-message/src/UriInterface.php000064400000030506151327705670015147 0ustar00<?php
namespace WPvividPsr\Http\Message;

/**
 * Value object representing a URI.
 *
 * This interface is meant to represent URIs according to RFC 3986 and to
 * provide methods for most common operations. Additional functionality for
 * working with URIs can be provided on top of the interface or externally.
 * Its primary use is for HTTP requests, but may also be used in other
 * contexts.
 *
 * Instances of this interface are considered immutable; all methods that
 * might change state MUST be implemented such that they retain the internal
 * state of the current instance and return an instance that contains the
 * changed state.
 *
 * Typically the Host header will be also be present in the request message.
 * For server-side requests, the scheme will typically be discoverable in the
 * server parameters.
 *
 * @link http://tools.ietf.org/html/rfc3986 (the URI specification)
 */
interface UriInterface
{
    /**
     * Retrieve the scheme component of the URI.
     *
     * If no scheme is present, this method MUST return an empty string.
     *
     * The value returned MUST be normalized to lowercase, per RFC 3986
     * Section 3.1.
     *
     * The trailing ":" character is not part of the scheme and MUST NOT be
     * added.
     *
     * @see https://tools.ietf.org/html/rfc3986#section-3.1
     * @return string The URI scheme.
     */
    public function getScheme();

    /**
     * Retrieve the authority component of the URI.
     *
     * If no authority information is present, this method MUST return an empty
     * string.
     *
     * The authority syntax of the URI is:
     *
     * <pre>
     * [user-info@]host[:port]
     * </pre>
     *
     * If the port component is not set or is the standard port for the current
     * scheme, it SHOULD NOT be included.
     *
     * @see https://tools.ietf.org/html/rfc3986#section-3.2
     * @return string The URI authority, in "[user-info@]host[:port]" format.
     */
    public function getAuthority();

    /**
     * Retrieve the user information component of the URI.
     *
     * If no user information is present, this method MUST return an empty
     * string.
     *
     * If a user is present in the URI, this will return that value;
     * additionally, if the password is also present, it will be appended to the
     * user value, with a colon (":") separating the values.
     *
     * The trailing "@" character is not part of the user information and MUST
     * NOT be added.
     *
     * @return string The URI user information, in "username[:password]" format.
     */
    public function getUserInfo();

    /**
     * Retrieve the host component of the URI.
     *
     * If no host is present, this method MUST return an empty string.
     *
     * The value returned MUST be normalized to lowercase, per RFC 3986
     * Section 3.2.2.
     *
     * @see http://tools.ietf.org/html/rfc3986#section-3.2.2
     * @return string The URI host.
     */
    public function getHost();

    /**
     * Retrieve the port component of the URI.
     *
     * If a port is present, and it is non-standard for the current scheme,
     * this method MUST return it as an integer. If the port is the standard port
     * used with the current scheme, this method SHOULD return null.
     *
     * If no port is present, and no scheme is present, this method MUST return
     * a null value.
     *
     * If no port is present, but a scheme is present, this method MAY return
     * the standard port for that scheme, but SHOULD return null.
     *
     * @return null|int The URI port.
     */
    public function getPort();

    /**
     * Retrieve the path component of the URI.
     *
     * The path can either be empty or absolute (starting with a slash) or
     * rootless (not starting with a slash). Implementations MUST support all
     * three syntaxes.
     *
     * Normally, the empty path "" and absolute path "/" are considered equal as
     * defined in RFC 7230 Section 2.7.3. But this method MUST NOT automatically
     * do this normalization because in contexts with a trimmed base path, e.g.
     * the front controller, this difference becomes significant. It's the task
     * of the user to handle both "" and "/".
     *
     * The value returned MUST be percent-encoded, but MUST NOT double-encode
     * any characters. To determine what characters to encode, please refer to
     * RFC 3986, Sections 2 and 3.3.
     *
     * As an example, if the value should include a slash ("/") not intended as
     * delimiter between path segments, that value MUST be passed in encoded
     * form (e.g., "%2F") to the instance.
     *
     * @see https://tools.ietf.org/html/rfc3986#section-2
     * @see https://tools.ietf.org/html/rfc3986#section-3.3
     * @return string The URI path.
     */
    public function getPath();

    /**
     * Retrieve the query string of the URI.
     *
     * If no query string is present, this method MUST return an empty string.
     *
     * The leading "?" character is not part of the query and MUST NOT be
     * added.
     *
     * The value returned MUST be percent-encoded, but MUST NOT double-encode
     * any characters. To determine what characters to encode, please refer to
     * RFC 3986, Sections 2 and 3.4.
     *
     * As an example, if a value in a key/value pair of the query string should
     * include an ampersand ("&") not intended as a delimiter between values,
     * that value MUST be passed in encoded form (e.g., "%26") to the instance.
     *
     * @see https://tools.ietf.org/html/rfc3986#section-2
     * @see https://tools.ietf.org/html/rfc3986#section-3.4
     * @return string The URI query string.
     */
    public function getQuery();

    /**
     * Retrieve the fragment component of the URI.
     *
     * If no fragment is present, this method MUST return an empty string.
     *
     * The leading "#" character is not part of the fragment and MUST NOT be
     * added.
     *
     * The value returned MUST be percent-encoded, but MUST NOT double-encode
     * any characters. To determine what characters to encode, please refer to
     * RFC 3986, Sections 2 and 3.5.
     *
     * @see https://tools.ietf.org/html/rfc3986#section-2
     * @see https://tools.ietf.org/html/rfc3986#section-3.5
     * @return string The URI fragment.
     */
    public function getFragment();

    /**
     * Return an instance with the specified scheme.
     *
     * This method MUST retain the state of the current instance, and return
     * an instance that contains the specified scheme.
     *
     * Implementations MUST support the schemes "http" and "https" case
     * insensitively, and MAY accommodate other schemes if required.
     *
     * An empty scheme is equivalent to removing the scheme.
     *
     * @param string $scheme The scheme to use with the new instance.
     * @return static A new instance with the specified scheme.
     * @throws \InvalidArgumentException for invalid or unsupported schemes.
     */
    public function withScheme($scheme);

    /**
     * Return an instance with the specified user information.
     *
     * This method MUST retain the state of the current instance, and return
     * an instance that contains the specified user information.
     *
     * Password is optional, but the user information MUST include the
     * user; an empty string for the user is equivalent to removing user
     * information.
     *
     * @param string $user The user name to use for authority.
     * @param null|string $password The password associated with $user.
     * @return static A new instance with the specified user information.
     */
    public function withUserInfo($user, $password = null);

    /**
     * Return an instance with the specified host.
     *
     * This method MUST retain the state of the current instance, and return
     * an instance that contains the specified host.
     *
     * An empty host value is equivalent to removing the host.
     *
     * @param string $host The hostname to use with the new instance.
     * @return static A new instance with the specified host.
     * @throws \InvalidArgumentException for invalid hostnames.
     */
    public function withHost($host);

    /**
     * Return an instance with the specified port.
     *
     * This method MUST retain the state of the current instance, and return
     * an instance that contains the specified port.
     *
     * Implementations MUST raise an exception for ports outside the
     * established TCP and UDP port ranges.
     *
     * A null value provided for the port is equivalent to removing the port
     * information.
     *
     * @param null|int $port The port to use with the new instance; a null value
     *     removes the port information.
     * @return static A new instance with the specified port.
     * @throws \InvalidArgumentException for invalid ports.
     */
    public function withPort($port);

    /**
     * Return an instance with the specified path.
     *
     * This method MUST retain the state of the current instance, and return
     * an instance that contains the specified path.
     *
     * The path can either be empty or absolute (starting with a slash) or
     * rootless (not starting with a slash). Implementations MUST support all
     * three syntaxes.
     *
     * If the path is intended to be domain-relative rather than path relative then
     * it must begin with a slash ("/"). Paths not starting with a slash ("/")
     * are assumed to be relative to some base path known to the application or
     * consumer.
     *
     * Users can provide both encoded and decoded path characters.
     * Implementations ensure the correct encoding as outlined in getPath().
     *
     * @param string $path The path to use with the new instance.
     * @return static A new instance with the specified path.
     * @throws \InvalidArgumentException for invalid paths.
     */
    public function withPath($path);

    /**
     * Return an instance with the specified query string.
     *
     * This method MUST retain the state of the current instance, and return
     * an instance that contains the specified query string.
     *
     * Users can provide both encoded and decoded query characters.
     * Implementations ensure the correct encoding as outlined in getQuery().
     *
     * An empty query string value is equivalent to removing the query string.
     *
     * @param string $query The query string to use with the new instance.
     * @return static A new instance with the specified query string.
     * @throws \InvalidArgumentException for invalid query strings.
     */
    public function withQuery($query);

    /**
     * Return an instance with the specified URI fragment.
     *
     * This method MUST retain the state of the current instance, and return
     * an instance that contains the specified URI fragment.
     *
     * Users can provide both encoded and decoded fragment characters.
     * Implementations ensure the correct encoding as outlined in getFragment().
     *
     * An empty fragment value is equivalent to removing the fragment.
     *
     * @param string $fragment The fragment to use with the new instance.
     * @return static A new instance with the specified fragment.
     */
    public function withFragment($fragment);

    /**
     * Return the string representation as a URI reference.
     *
     * Depending on which components of the URI are present, the resulting
     * string is either a full URI or relative reference according to RFC 3986,
     * Section 4.1. The method concatenates the various components of the URI,
     * using the appropriate delimiters:
     *
     * - If a scheme is present, it MUST be suffixed by ":".
     * - If an authority is present, it MUST be prefixed by "//".
     * - The path can be concatenated without delimiters. But there are two
     *   cases where the path has to be adjusted to make the URI reference
     *   valid as PHP does not allow to throw an exception in __toString():
     *     - If the path is rootless and an authority is present, the path MUST
     *       be prefixed by "/".
     *     - If the path is starting with more than one "/" and no authority is
     *       present, the starting slashes MUST be reduced to one.
     * - If a query is present, it MUST be prefixed by "?".
     * - If a fragment is present, it MUST be prefixed by "#".
     *
     * @see http://tools.ietf.org/html/rfc3986#section-4.1
     * @return string
     */
    public function __toString();
}
vendor/psr/http-message/src/ResponseInterface.php000064400000005046151327705670016207 0ustar00<?php

namespace WPvividPsr\Http\Message;

/**
 * Representation of an outgoing, server-side response.
 *
 * Per the HTTP specification, this interface includes properties for
 * each of the following:
 *
 * - Protocol version
 * - Status code and reason phrase
 * - Headers
 * - Message body
 *
 * Responses are considered immutable; all methods that might change state MUST
 * be implemented such that they retain the internal state of the current
 * message and return an instance that contains the changed state.
 */
interface ResponseInterface extends MessageInterface
{
    /**
     * Gets the response status code.
     *
     * The status code is a 3-digit integer result code of the server's attempt
     * to understand and satisfy the request.
     *
     * @return int Status code.
     */
    public function getStatusCode();

    /**
     * Return an instance with the specified status code and, optionally, reason phrase.
     *
     * If no reason phrase is specified, implementations MAY choose to default
     * to the RFC 7231 or IANA recommended reason phrase for the response's
     * status code.
     *
     * This method MUST be implemented in such a way as to retain the
     * immutability of the message, and MUST return an instance that has the
     * updated status and reason phrase.
     *
     * @link http://tools.ietf.org/html/rfc7231#section-6
     * @link http://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml
     * @param int $code The 3-digit integer result code to set.
     * @param string $reasonPhrase The reason phrase to use with the
     *     provided status code; if none is provided, implementations MAY
     *     use the defaults as suggested in the HTTP specification.
     * @return static
     * @throws \InvalidArgumentException For invalid status code arguments.
     */
    public function withStatus($code, $reasonPhrase = '');

    /**
     * Gets the response reason phrase associated with the status code.
     *
     * Because a reason phrase is not a required element in a response
     * status line, the reason phrase value MAY be null. Implementations MAY
     * choose to return the default RFC 7231 recommended reason phrase (or those
     * listed in the IANA HTTP Status Code Registry) for the response's
     * status code.
     *
     * @link http://tools.ietf.org/html/rfc7231#section-6
     * @link http://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml
     * @return string Reason phrase; must return an empty string if none present.
     */
    public function getReasonPhrase();
}
vendor/psr/http-message/src/StreamInterface.php000064400000011221151327705670015634 0ustar00<?php

namespace WPvividPsr\Http\Message;

/**
 * Describes a data stream.
 *
 * Typically, an instance will wrap a PHP stream; this interface provides
 * a wrapper around the most common operations, including serialization of
 * the entire stream to a string.
 */
interface StreamInterface
{
    /**
     * Reads all data from the stream into a string, from the beginning to end.
     *
     * This method MUST attempt to seek to the beginning of the stream before
     * reading data and read the stream until the end is reached.
     *
     * Warning: This could attempt to load a large amount of data into memory.
     *
     * This method MUST NOT raise an exception in order to conform with PHP's
     * string casting operations.
     *
     * @see http://php.net/manual/en/language.oop5.magic.php#object.tostring
     * @return string
     */
    public function __toString();

    /**
     * Closes the stream and any underlying resources.
     *
     * @return void
     */
    public function close();

    /**
     * Separates any underlying resources from the stream.
     *
     * After the stream has been detached, the stream is in an unusable state.
     *
     * @return resource|null Underlying PHP stream, if any
     */
    public function detach();

    /**
     * Get the size of the stream if known.
     *
     * @return int|null Returns the size in bytes if known, or null if unknown.
     */
    public function getSize();

    /**
     * Returns the current position of the file read/write pointer
     *
     * @return int Position of the file pointer
     * @throws \RuntimeException on error.
     */
    public function tell();

    /**
     * Returns true if the stream is at the end of the stream.
     *
     * @return bool
     */
    public function eof();

    /**
     * Returns whether or not the stream is seekable.
     *
     * @return bool
     */
    public function isSeekable();

    /**
     * Seek to a position in the stream.
     *
     * @link http://www.php.net/manual/en/function.fseek.php
     * @param int $offset Stream offset
     * @param int $whence Specifies how the cursor position will be calculated
     *     based on the seek offset. Valid values are identical to the built-in
     *     PHP $whence values for `fseek()`.  SEEK_SET: Set position equal to
     *     offset bytes SEEK_CUR: Set position to current location plus offset
     *     SEEK_END: Set position to end-of-stream plus offset.
     * @throws \RuntimeException on failure.
     */
    public function seek($offset, $whence = SEEK_SET);

    /**
     * Seek to the beginning of the stream.
     *
     * If the stream is not seekable, this method will raise an exception;
     * otherwise, it will perform a seek(0).
     *
     * @see seek()
     * @link http://www.php.net/manual/en/function.fseek.php
     * @throws \RuntimeException on failure.
     */
    public function rewind();

    /**
     * Returns whether or not the stream is writable.
     *
     * @return bool
     */
    public function isWritable();

    /**
     * Write data to the stream.
     *
     * @param string $string The string that is to be written.
     * @return int Returns the number of bytes written to the stream.
     * @throws \RuntimeException on failure.
     */
    public function write($string);

    /**
     * Returns whether or not the stream is readable.
     *
     * @return bool
     */
    public function isReadable();

    /**
     * Read data from the stream.
     *
     * @param int $length Read up to $length bytes from the object and return
     *     them. Fewer than $length bytes may be returned if underlying stream
     *     call returns fewer bytes.
     * @return string Returns the data read from the stream, or an empty string
     *     if no bytes are available.
     * @throws \RuntimeException if an error occurs.
     */
    public function read($length);

    /**
     * Returns the remaining contents in a string
     *
     * @return string
     * @throws \RuntimeException if unable to read or an error occurs while
     *     reading.
     */
    public function getContents();

    /**
     * Get stream metadata as an associative array or retrieve a specific key.
     *
     * The keys returned are identical to the keys returned from PHP's
     * stream_get_meta_data() function.
     *
     * @link http://php.net/manual/en/function.stream-get-meta-data.php
     * @param string $key Specific metadata to retrieve.
     * @return array|mixed|null Returns an associative array if no key is
     *     provided. Returns a specific key value if a key is provided and the
     *     value is found, or null if the key is not found.
     */
    public function getMetadata($key = null);
}
vendor/psr/http-message/README.md000064400000000546151327705670012547 0ustar00PSR Http Message
================

This repository holds all interfaces/classes/traits related to
[PSR-7](http://www.php-fig.org/psr/psr-7/).

Note that this is not a HTTP message implementation of its own. It is merely an
interface that describes a HTTP message. See the specification for more details.

Usage
-----

We'll certainly need some stuff in here.vendor/google/auth/src/CredentialsLoader.php000064400000016172151327705670015166 0ustar00<?php
/*
 * Copyright 2015 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

namespace WPvividGoogle\Auth;

use WPvividGoogle\Auth\Credentials\InsecureCredentials;
use WPvividGoogle\Auth\Credentials\ServiceAccountCredentials;
use WPvividGoogle\Auth\Credentials\UserRefreshCredentials;

/**
 * CredentialsLoader contains the behaviour used to locate and find default
 * credentials files on the file system.
 */
abstract class CredentialsLoader implements FetchAuthTokenInterface
{
    const TOKEN_CREDENTIAL_URI = 'https://oauth2.googleapis.com/token';
    const ENV_VAR = 'GOOGLE_APPLICATION_CREDENTIALS';
    const WELL_KNOWN_PATH = 'gcloud/application_default_credentials.json';
    const NON_WINDOWS_WELL_KNOWN_PATH_BASE = '.config';
    const AUTH_METADATA_KEY = 'authorization';

    /**
     * @param string $cause
     * @return string
     */
    private static function unableToReadEnv($cause)
    {
        $msg = 'Unable to read the credential file specified by ';
        $msg .= ' GOOGLE_APPLICATION_CREDENTIALS: ';
        $msg .= $cause;

        return $msg;
    }

    /**
     * @return bool
     */
    private static function isOnWindows()
    {
        return strtoupper(substr(PHP_OS, 0, 3)) === 'WIN';
    }

    /**
     * Load a JSON key from the path specified in the environment.
     *
     * Load a JSON key from the path specified in the environment
     * variable GOOGLE_APPLICATION_CREDENTIALS. Return null if
     * GOOGLE_APPLICATION_CREDENTIALS is not specified.
     *
     * @return array JSON key | null
     */
    public static function fromEnv()
    {
        $path = getenv(self::ENV_VAR);
        if (empty($path)) {
            return;
        }
        if (!file_exists($path)) {
            $cause = 'file ' . $path . ' does not exist';
            throw new \DomainException(self::unableToReadEnv($cause));
        }
        $jsonKey = file_get_contents($path);
        return json_decode($jsonKey, true);
    }

    /**
     * Load a JSON key from a well known path.
     *
     * The well known path is OS dependent:
     * - windows: %APPDATA%/gcloud/application_default_credentials.json
     * - others: $HOME/.config/gcloud/application_default_credentials.json
     *
     * If the file does not exists, this returns null.
     *
     * @return array JSON key | null
     */
    public static function fromWellKnownFile()
    {
        $rootEnv = self::isOnWindows() ? 'APPDATA' : 'HOME';
        $path = [getenv($rootEnv)];
        if (!self::isOnWindows()) {
            $path[] = self::NON_WINDOWS_WELL_KNOWN_PATH_BASE;
        }
        $path[] = self::WELL_KNOWN_PATH;
        $path = implode(DIRECTORY_SEPARATOR, $path);
        if (!file_exists($path)) {
            return;
        }
        $jsonKey = file_get_contents($path);
        return json_decode($jsonKey, true);
    }

    /**
     * Create a new Credentials instance.
     *
     * @param string|array $scope the scope of the access request, expressed
     *   either as an Array or as a space-delimited String.
     * @param array $jsonKey the JSON credentials.
     *
     * @return ServiceAccountCredentials|UserRefreshCredentials
     */
    public static function makeCredentials($scope, array $jsonKey)
    {
        if (!array_key_exists('type', $jsonKey)) {
            throw new \InvalidArgumentException('json key is missing the type field');
        }

        if ($jsonKey['type'] == 'service_account') {
            return new ServiceAccountCredentials($scope, $jsonKey);
        }

        if ($jsonKey['type'] == 'authorized_user') {
            return new UserRefreshCredentials($scope, $jsonKey);
        }

        throw new \InvalidArgumentException('invalid value in the type field');
    }

    /**
     * Create an authorized HTTP Client from an instance of FetchAuthTokenInterface.
     *
     * @param FetchAuthTokenInterface $fetcher is used to fetch the auth token
     * @param array $httpClientOptoins (optional) Array of request options to apply.
     * @param callable $httpHandler (optional) http client to fetch the token.
     * @param callable $tokenCallback (optional) function to be called when a new token is fetched.
     *
     * @return \GuzzleHttp\Client
     */
    public static function makeHttpClient(
        FetchAuthTokenInterface $fetcher,
        array $httpClientOptions = [],
        callable $httpHandler = null,
        callable $tokenCallback = null
    ) {
        $version = \WPvividGuzzleHttp\ClientInterface::VERSION;

        switch ($version[0]) {
            case '5':
                $client = new \WPvividGuzzleHttp\Client($httpClientOptions);
                $client->setDefaultOption('auth', 'google_auth');
                $subscriber = new Subscriber\AuthTokenSubscriber(
                    $fetcher,
                    $httpHandler,
                    $tokenCallback
                );
                $client->getEmitter()->attach($subscriber);
                return $client;
            case '6':
                $middleware = new Middleware\AuthTokenMiddleware(
                    $fetcher,
                    $httpHandler,
                    $tokenCallback
                );
                $stack = \WPvividGuzzleHttp\HandlerStack::create();
                $stack->push($middleware);

                return new \WPvividGuzzleHttp\Client([
                   'handler' => $stack,
                   'auth' => 'google_auth',
                ] + $httpClientOptions);
            default:
                throw new \Exception('Version not supported');
        }
    }

    /**
     * Create a new instance of InsecureCredentials.
     *
     * @return InsecureCredentials
     */
    public static function makeInsecureCredentials()
    {
        return new InsecureCredentials();
    }

    /**
     * export a callback function which updates runtime metadata.
     *
     * @return array updateMetadata function
     */
    public function getUpdateMetadataFunc()
    {
        return array($this, 'updateMetadata');
    }

    /**
     * Updates metadata with the authorization token.
     *
     * @param array $metadata metadata hashmap
     * @param string $authUri optional auth uri
     * @param callable $httpHandler callback which delivers psr7 request
     *
     * @return array updated metadata hashmap
     */
    public function updateMetadata(
        $metadata,
        $authUri = null,
        callable $httpHandler = null
    ) {
        $result = $this->fetchAuthToken($httpHandler);
        if (!isset($result['access_token'])) {
            return $metadata;
        }
        $metadata_copy = $metadata;
        $metadata_copy[self::AUTH_METADATA_KEY] = array('Bearer ' . $result['access_token']);

        return $metadata_copy;
    }
}
vendor/google/auth/src/FetchAuthTokenCache.php000064400000005463151327705670015403 0ustar00<?php
/*
 * Copyright 2010 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

namespace WPvividGoogle\Auth;

use WPvividPsr\Cache\CacheItemPoolInterface;

/**
 * A class to implement caching for any object implementing
 * FetchAuthTokenInterface
 */
class FetchAuthTokenCache implements FetchAuthTokenInterface
{
    use CacheTrait;

    /**
     * @var FetchAuthTokenInterface
     */
    private $fetcher;

    /**
     * @var array
     */
    private $cacheConfig;

    /**
     * @var CacheItemPoolInterface
     */
    private $cache;

    public function __construct(
        FetchAuthTokenInterface $fetcher,
        array $cacheConfig = null,
        CacheItemPoolInterface $cache
    ) {
        $this->fetcher = $fetcher;
        $this->cache = $cache;
        $this->cacheConfig = array_merge([
            'lifetime' => 1500,
            'prefix' => '',
        ], (array) $cacheConfig);
    }

    /**
     * Implements FetchAuthTokenInterface#fetchAuthToken.
     *
     * Checks the cache for a valid auth token and fetches the auth tokens
     * from the supplied fetcher.
     *
     * @param callable $httpHandler callback which delivers psr7 request
     *
     * @return array the response
     *
     * @throws \Exception
     */
    public function fetchAuthToken(callable $httpHandler = null)
    {
        // Use the cached value if its available.
        //
        // TODO: correct caching; update the call to setCachedValue to set the expiry
        // to the value returned with the auth token.
        //
        // TODO: correct caching; enable the cache to be cleared.
        $cacheKey = $this->fetcher->getCacheKey();
        $cached = $this->getCachedValue($cacheKey);
        if (!empty($cached)) {
            return ['access_token' => $cached];
        }

        $auth_token = $this->fetcher->fetchAuthToken($httpHandler);

        if (isset($auth_token['access_token'])) {
            $this->setCachedValue($cacheKey, $auth_token['access_token']);
        }

        return $auth_token;
    }

    /**
     * @return string
     */
    public function getCacheKey()
    {
        return $this->getFullCacheKey($this->fetcher->getCacheKey());
    }

    /**
     * @return array|null
     */
    public function getLastReceivedToken()
    {
        return $this->fetcher->getLastReceivedToken();
    }
}
vendor/google/auth/src/CacheTrait.php000064400000004175151327705670013611 0ustar00<?php
/*
 * Copyright 2015 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

namespace WPvividGoogle\Auth;

trait CacheTrait
{
    private $maxKeyLength = 64;

    /**
     * Gets the cached value if it is present in the cache when that is
     * available.
     */
    private function getCachedValue($k)
    {
        if (is_null($this->cache)) {
            return;
        }

        $key = $this->getFullCacheKey($k);
        if (is_null($key)) {
            return;
        }

        $cacheItem = $this->cache->getItem($key);
        if ($cacheItem->isHit()) {
            return $cacheItem->get();
        }
    }

    /**
     * Saves the value in the cache when that is available.
     */
    private function setCachedValue($k, $v)
    {
        if (is_null($this->cache)) {
            return;
        }

        $key = $this->getFullCacheKey($k);
        if (is_null($key)) {
            return;
        }

        $cacheItem = $this->cache->getItem($key);
        $cacheItem->set($v);
        $cacheItem->expiresAfter($this->cacheConfig['lifetime']);
        return $this->cache->save($cacheItem);
    }

    private function getFullCacheKey($key)
    {
        if (is_null($key)) {
            return;
        }

        $key = $this->cacheConfig['prefix'] . $key;

        // ensure we do not have illegal characters
        $key = preg_replace('|[^a-zA-Z0-9_\.!]|', '', $key);

        // Hash keys if they exceed $maxKeyLength (defaults to 64)
        if ($this->maxKeyLength && strlen($key) > $this->maxKeyLength) {
            $key = substr(hash('sha256', $key), 0, $this->maxKeyLength);
        }

        return $key;
    }
}
vendor/google/auth/src/OAuth2.php000064400000103000151327705670012667 0ustar00<?php
/*
 * Copyright 2015 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

namespace WPvividGoogle\Auth;

use WPvividGoogle\Auth\HttpHandler\HttpHandlerFactory;
use WPvividGuzzleHttp\Psr7;
use WPvividGuzzleHttp\Psr7\Request;
use InvalidArgumentException;
use WPvividPsr\Http\Message\RequestInterface;
use WPvividPsr\Http\Message\ResponseInterface;
use WPvividPsr\Http\Message\UriInterface;

/**
 * OAuth2 supports authentication by OAuth2 2-legged flows.
 *
 * It primary supports
 * - service account authorization
 * - authorization where a user already has an access token
 */
class OAuth2 implements FetchAuthTokenInterface
{
    const DEFAULT_EXPIRY_SECONDS = 3600; // 1 hour
    const DEFAULT_SKEW_SECONDS = 60; // 1 minute
    const JWT_URN = 'urn:ietf:params:oauth:grant-type:jwt-bearer';

    /**
     * TODO: determine known methods from the keys of JWT::methods.
     */
    public static $knownSigningAlgorithms = array(
        'HS256',
        'HS512',
        'HS384',
        'RS256',
    );

    /**
     * The well known grant types.
     *
     * @var array
     */
    public static $knownGrantTypes = array(
        'authorization_code',
        'refresh_token',
        'password',
        'client_credentials',
    );

    /**
     * - authorizationUri
     *   The authorization server's HTTP endpoint capable of
     *   authenticating the end-user and obtaining authorization.
     *
     * @var UriInterface
     */
    private $authorizationUri;

    /**
     * - tokenCredentialUri
     *   The authorization server's HTTP endpoint capable of issuing
     *   tokens and refreshing expired tokens.
     *
     * @var UriInterface
     */
    private $tokenCredentialUri;

    /**
     * The redirection URI used in the initial request.
     *
     * @var string
     */
    private $redirectUri;

    /**
     * A unique identifier issued to the client to identify itself to the
     * authorization server.
     *
     * @var string
     */
    private $clientId;

    /**
     * A shared symmetric secret issued by the authorization server, which is
     * used to authenticate the client.
     *
     * @var string
     */
    private $clientSecret;

    /**
     * The resource owner's username.
     *
     * @var string
     */
    private $username;

    /**
     * The resource owner's password.
     *
     * @var string
     */
    private $password;

    /**
     * The scope of the access request, expressed either as an Array or as a
     * space-delimited string.
     *
     * @var string
     */
    private $scope;

    /**
     * An arbitrary string designed to allow the client to maintain state.
     *
     * @var string
     */
    private $state;

    /**
     * The authorization code issued to this client.
     *
     * Only used by the authorization code access grant type.
     *
     * @var string
     */
    private $code;

    /**
     * The issuer ID when using assertion profile.
     *
     * @var string
     */
    private $issuer;

    /**
     * The target audience for assertions.
     *
     * @var string
     */
    private $audience;

    /**
     * The target sub when issuing assertions.
     *
     * @var string
     */
    private $sub;

    /**
     * The number of seconds assertions are valid for.
     *
     * @var int
     */
    private $expiry;

    /**
     * The signing key when using assertion profile.
     *
     * @var string
     */
    private $signingKey;

    /**
     * The signing algorithm when using an assertion profile.
     *
     * @var string
     */
    private $signingAlgorithm;

    /**
     * The refresh token associated with the access token to be refreshed.
     *
     * @var string
     */
    private $refreshToken;

    /**
     * The current access token.
     *
     * @var string
     */
    private $accessToken;

    /**
     * The current ID token.
     *
     * @var string
     */
    private $idToken;

    /**
     * The lifetime in seconds of the current access token.
     *
     * @var int
     */
    private $expiresIn;

    /**
     * The expiration time of the access token as a number of seconds since the
     * unix epoch.
     *
     * @var int
     */
    private $expiresAt;

    /**
     * The issue time of the access token as a number of seconds since the unix
     * epoch.
     *
     * @var int
     */
    private $issuedAt;

    /**
     * The current grant type.
     *
     * @var string
     */
    private $grantType;

    /**
     * When using an extension grant type, this is the set of parameters used by
     * that extension.
     */
    private $extensionParams;

    /**
     * When using the toJwt function, these claims will be added to the JWT
     * payload.
     */
    private $additionalClaims;

    /**
     * Create a new OAuthCredentials.
     *
     * The configuration array accepts various options
     *
     * - authorizationUri
     *   The authorization server's HTTP endpoint capable of
     *   authenticating the end-user and obtaining authorization.
     *
     * - tokenCredentialUri
     *   The authorization server's HTTP endpoint capable of issuing
     *   tokens and refreshing expired tokens.
     *
     * - clientId
     *   A unique identifier issued to the client to identify itself to the
     *   authorization server.
     *
     * - clientSecret
     *   A shared symmetric secret issued by the authorization server,
     *   which is used to authenticate the client.
     *
     * - scope
     *   The scope of the access request, expressed either as an Array
     *   or as a space-delimited String.
     *
     * - state
     *   An arbitrary string designed to allow the client to maintain state.
     *
     * - redirectUri
     *   The redirection URI used in the initial request.
     *
     * - username
     *   The resource owner's username.
     *
     * - password
     *   The resource owner's password.
     *
     * - issuer
     *   Issuer ID when using assertion profile
     *
     * - audience
     *   Target audience for assertions
     *
     * - expiry
     *   Number of seconds assertions are valid for
     *
     * - signingKey
     *   Signing key when using assertion profile
     *
     * - refreshToken
     *   The refresh token associated with the access token
     *   to be refreshed.
     *
     * - accessToken
     *   The current access token for this client.
     *
     * - idToken
     *   The current ID token for this client.
     *
     * - extensionParams
     *   When using an extension grant type, this is the set of parameters used
     *   by that extension.
     *
     * @param array $config Configuration array
     */
    public function __construct(array $config)
    {
        $opts = array_merge([
            'expiry' => self::DEFAULT_EXPIRY_SECONDS,
            'extensionParams' => [],
            'authorizationUri' => null,
            'redirectUri' => null,
            'tokenCredentialUri' => null,
            'state' => null,
            'username' => null,
            'password' => null,
            'clientId' => null,
            'clientSecret' => null,
            'issuer' => null,
            'sub' => null,
            'audience' => null,
            'signingKey' => null,
            'signingAlgorithm' => null,
            'scope' => null,
            'additionalClaims' => [],
        ], $config);

        $this->setAuthorizationUri($opts['authorizationUri']);
        $this->setRedirectUri($opts['redirectUri']);
        $this->setTokenCredentialUri($opts['tokenCredentialUri']);
        $this->setState($opts['state']);
        $this->setUsername($opts['username']);
        $this->setPassword($opts['password']);
        $this->setClientId($opts['clientId']);
        $this->setClientSecret($opts['clientSecret']);
        $this->setIssuer($opts['issuer']);
        $this->setSub($opts['sub']);
        $this->setExpiry($opts['expiry']);
        $this->setAudience($opts['audience']);
        $this->setSigningKey($opts['signingKey']);
        $this->setSigningAlgorithm($opts['signingAlgorithm']);
        $this->setScope($opts['scope']);
        $this->setExtensionParams($opts['extensionParams']);
        $this->setAdditionalClaims($opts['additionalClaims']);
        $this->updateToken($opts);
    }

    /**
     * Verifies the idToken if present.
     *
     * - if none is present, return null
     * - if present, but invalid, raises DomainException.
     * - otherwise returns the payload in the idtoken as a PHP object.
     *
     * if $publicKey is null, the key is decoded without being verified.
     *
     * @param string $publicKey The public key to use to authenticate the token
     * @param array $allowed_algs List of supported verification algorithms
     *
     * @return null|object
     */
    public function verifyIdToken($publicKey = null, $allowed_algs = array())
    {
        $idToken = $this->getIdToken();
        if (is_null($idToken)) {
            return null;
        }

        $resp = $this->jwtDecode($idToken, $publicKey, $allowed_algs);
        if (!property_exists($resp, 'aud')) {
            throw new \DomainException('No audience found the id token');
        }
        if ($resp->aud != $this->getAudience()) {
            throw new \DomainException('Wrong audience present in the id token');
        }

        return $resp;
    }

    /**
     * Obtains the encoded jwt from the instance data.
     *
     * @param array $config array optional configuration parameters
     *
     * @return string
     */
    public function toJwt(array $config = [])
    {
        if (is_null($this->getSigningKey())) {
            throw new \DomainException('No signing key available');
        }
        if (is_null($this->getSigningAlgorithm())) {
            throw new \DomainException('No signing algorithm specified');
        }
        $now = time();

        $opts = array_merge([
            'skew' => self::DEFAULT_SKEW_SECONDS,
        ], $config);

        $assertion = [
            'iss' => $this->getIssuer(),
            'aud' => $this->getAudience(),
            'exp' => ($now + $this->getExpiry()),
            'iat' => ($now - $opts['skew']),
        ];
        foreach ($assertion as $k => $v) {
            if (is_null($v)) {
                throw new \DomainException($k . ' should not be null');
            }
        }
        if (!(is_null($this->getScope()))) {
            $assertion['scope'] = $this->getScope();
        }
        if (!(is_null($this->getSub()))) {
            $assertion['sub'] = $this->getSub();
        }
        $assertion += $this->getAdditionalClaims();

        return $this->jwtEncode($assertion, $this->getSigningKey(),
            $this->getSigningAlgorithm());
    }

    /**
     * Generates a request for token credentials.
     *
     * @return RequestInterface the authorization Url.
     */
    public function generateCredentialsRequest()
    {
        $uri = $this->getTokenCredentialUri();
        if (is_null($uri)) {
            throw new \DomainException('No token credential URI was set.');
        }

        $grantType = $this->getGrantType();
        $params = array('grant_type' => $grantType);
        switch ($grantType) {
            case 'authorization_code':
                $params['code'] = $this->getCode();
                $params['redirect_uri'] = $this->getRedirectUri();
                $this->addClientCredentials($params);
                break;
            case 'password':
                $params['username'] = $this->getUsername();
                $params['password'] = $this->getPassword();
                $this->addClientCredentials($params);
                break;
            case 'refresh_token':
                $params['refresh_token'] = $this->getRefreshToken();
                $this->addClientCredentials($params);
                break;
            case self::JWT_URN:
                $params['assertion'] = $this->toJwt();
                break;
            default:
                if (!is_null($this->getRedirectUri())) {
                    # Grant type was supposed to be 'authorization_code', as there
                    # is a redirect URI.
                    throw new \DomainException('Missing authorization code');
                }
                unset($params['grant_type']);
                if (!is_null($grantType)) {
                    $params['grant_type'] = $grantType;
                }
                $params = array_merge($params, $this->getExtensionParams());
        }

        $headers = [
            'Cache-Control' => 'no-store',
            'Content-Type' => 'application/x-www-form-urlencoded',
        ];

        return new Request(
            'POST',
            $uri,
            $headers,
            Psr7\build_query($params)
        );
    }

    /**
     * Fetches the auth tokens based on the current state.
     *
     * @param callable $httpHandler callback which delivers psr7 request
     *
     * @return array the response
     */
    public function fetchAuthToken(callable $httpHandler = null)
    {
        if (is_null($httpHandler)) {
            $httpHandler = HttpHandlerFactory::build();
        }

        $response = $httpHandler($this->generateCredentialsRequest());
        $credentials = $this->parseTokenResponse($response);
        $this->updateToken($credentials);

        return $credentials;
    }

    /**
     * Obtains a key that can used to cache the results of #fetchAuthToken.
     *
     * The key is derived from the scopes.
     *
     * @return string a key that may be used to cache the auth token.
     */
    public function getCacheKey()
    {
        if (is_string($this->scope)) {
            return $this->scope;
        }

        if (is_array($this->scope)) {
            return implode(':', $this->scope);
        }

        // If scope has not set, return null to indicate no caching.
        return null;
    }

    /**
     * Parses the fetched tokens.
     *
     * @param ResponseInterface $resp the response.
     *
     * @return array the tokens parsed from the response body.
     *
     * @throws \Exception
     */
    public function parseTokenResponse(ResponseInterface $resp)
    {
        $body = (string)$resp->getBody();
        if ($resp->hasHeader('Content-Type') &&
            $resp->getHeaderLine('Content-Type') == 'application/x-www-form-urlencoded'
        ) {
            $res = array();
            parse_str($body, $res);

            return $res;
        }

        // Assume it's JSON; if it's not throw an exception
        if (null === $res = json_decode($body, true)) {
            throw new \Exception('Invalid JSON response');
        }

        return $res;
    }

    /**
     * Updates an OAuth 2.0 client.
     *
     * @example
     *   client.updateToken([
     *     'refresh_token' => 'n4E9O119d',
     *     'access_token' => 'FJQbwq9',
     *     'expires_in' => 3600
     *   ])
     *
     * @param array $config
     *  The configuration parameters related to the token.
     *
     *  - refresh_token
     *    The refresh token associated with the access token
     *    to be refreshed.
     *
     *  - access_token
     *    The current access token for this client.
     *
     *  - id_token
     *    The current ID token for this client.
     *
     *  - expires_in
     *    The time in seconds until access token expiration.
     *
     *  - expires_at
     *    The time as an integer number of seconds since the Epoch
     *
     *  - issued_at
     *    The timestamp that the token was issued at.
     */
    public function updateToken(array $config)
    {
        $opts = array_merge([
            'extensionParams' => [],
            'access_token' => null,
            'id_token' => null,
            'expires_in' => null,
            'expires_at' => null,
            'issued_at' => null,
        ], $config);

        $this->setExpiresAt($opts['expires_at']);
        $this->setExpiresIn($opts['expires_in']);
        // By default, the token is issued at `Time.now` when `expiresIn` is set,
        // but this can be used to supply a more precise time.
        if (!is_null($opts['issued_at'])) {
            $this->setIssuedAt($opts['issued_at']);
        }

        $this->setAccessToken($opts['access_token']);
        $this->setIdToken($opts['id_token']);
        // The refresh token should only be updated if a value is explicitly
        // passed in, as some access token responses do not include a refresh
        // token.
        if (array_key_exists('refresh_token', $opts)) {
            $this->setRefreshToken($opts['refresh_token']);
        }
    }

    /**
     * Builds the authorization Uri that the user should be redirected to.
     *
     * @param array $config configuration options that customize the return url
     *
     * @return UriInterface the authorization Url.
     *
     * @throws InvalidArgumentException
     */
    public function buildFullAuthorizationUri(array $config = [])
    {
        if (is_null($this->getAuthorizationUri())) {
            throw new InvalidArgumentException(
                'requires an authorizationUri to have been set');
        }

        $params = array_merge([
            'response_type' => 'code',
            'access_type' => 'offline',
            'client_id' => $this->clientId,
            'redirect_uri' => $this->redirectUri,
            'state' => $this->state,
            'scope' => $this->getScope(),
        ], $config);

        // Validate the auth_params
        if (is_null($params['client_id'])) {
            throw new InvalidArgumentException(
                'missing the required client identifier');
        }
        if (is_null($params['redirect_uri'])) {
            throw new InvalidArgumentException('missing the required redirect URI');
        }
        if (!empty($params['prompt']) && !empty($params['approval_prompt'])) {
            throw new InvalidArgumentException(
                'prompt and approval_prompt are mutually exclusive');
        }

        // Construct the uri object; return it if it is valid.
        $result = clone $this->authorizationUri;
        $existingParams = Psr7\parse_query($result->getQuery());

        $result = $result->withQuery(
            Psr7\build_query(array_merge($existingParams, $params))
        );

        if ($result->getScheme() != 'https') {
            throw new InvalidArgumentException(
                'Authorization endpoint must be protected by TLS');
        }

        return $result;
    }

    /**
     * Sets the authorization server's HTTP endpoint capable of authenticating
     * the end-user and obtaining authorization.
     *
     * @param string $uri
     */
    public function setAuthorizationUri($uri)
    {
        $this->authorizationUri = $this->coerceUri($uri);
    }

    /**
     * Gets the authorization server's HTTP endpoint capable of authenticating
     * the end-user and obtaining authorization.
     *
     * @return UriInterface
     */
    public function getAuthorizationUri()
    {
        return $this->authorizationUri;
    }

    /**
     * Gets the authorization server's HTTP endpoint capable of issuing tokens
     * and refreshing expired tokens.
     *
     * @return string
     */
    public function getTokenCredentialUri()
    {
        return $this->tokenCredentialUri;
    }

    /**
     * Sets the authorization server's HTTP endpoint capable of issuing tokens
     * and refreshing expired tokens.
     *
     * @param string $uri
     */
    public function setTokenCredentialUri($uri)
    {
        $this->tokenCredentialUri = $this->coerceUri($uri);
    }

    /**
     * Gets the redirection URI used in the initial request.
     *
     * @return string
     */
    public function getRedirectUri()
    {
        return $this->redirectUri;
    }

    /**
     * Sets the redirection URI used in the initial request.
     *
     * @param string $uri
     */
    public function setRedirectUri($uri)
    {
        if (is_null($uri)) {
            $this->redirectUri = null;

            return;
        }
        // redirect URI must be absolute
        if (!$this->isAbsoluteUri($uri)) {
            // "postmessage" is a reserved URI string in Google-land
            // @see https://developers.google.com/identity/sign-in/web/server-side-flow
            if ('postmessage' !== (string)$uri) {
                throw new InvalidArgumentException(
                    'Redirect URI must be absolute');
            }
        }
        $this->redirectUri = (string)$uri;
    }

    /**
     * Gets the scope of the access requests as a space-delimited String.
     *
     * @return string
     */
    public function getScope()
    {
        if (is_null($this->scope)) {
            return $this->scope;
        }

        return implode(' ', $this->scope);
    }

    /**
     * Sets the scope of the access request, expressed either as an Array or as
     * a space-delimited String.
     *
     * @param string|array $scope
     *
     * @throws InvalidArgumentException
     */
    public function setScope($scope)
    {
        if (is_null($scope)) {
            $this->scope = null;
        } elseif (is_string($scope)) {
            $this->scope = explode(' ', $scope);
        } elseif (is_array($scope)) {
            foreach ($scope as $s) {
                $pos = strpos($s, ' ');
                if ($pos !== false) {
                    throw new InvalidArgumentException(
                        'array scope values should not contain spaces');
                }
            }
            $this->scope = $scope;
        } else {
            throw new InvalidArgumentException(
                'scopes should be a string or array of strings');
        }
    }

    /**
     * Gets the current grant type.
     *
     * @return string
     */
    public function getGrantType()
    {
        if (!is_null($this->grantType)) {
            return $this->grantType;
        }

        // Returns the inferred grant type, based on the current object instance
        // state.
        if (!is_null($this->code)) {
            return 'authorization_code';
        }

        if (!is_null($this->refreshToken)) {
            return 'refresh_token';
        }

        if (!is_null($this->username) && !is_null($this->password)) {
            return 'password';
        }

        if (!is_null($this->issuer) && !is_null($this->signingKey)) {
            return self::JWT_URN;
        }

        return null;
    }

    /**
     * Sets the current grant type.
     *
     * @param $grantType
     *
     * @throws InvalidArgumentException
     */
    public function setGrantType($grantType)
    {
        if (in_array($grantType, self::$knownGrantTypes)) {
            $this->grantType = $grantType;
        } else {
            // validate URI
            if (!$this->isAbsoluteUri($grantType)) {
                throw new InvalidArgumentException(
                    'invalid grant type');
            }
            $this->grantType = (string)$grantType;
        }
    }

    /**
     * Gets an arbitrary string designed to allow the client to maintain state.
     *
     * @return string
     */
    public function getState()
    {
        return $this->state;
    }

    /**
     * Sets an arbitrary string designed to allow the client to maintain state.
     *
     * @param string $state
     */
    public function setState($state)
    {
        $this->state = $state;
    }

    /**
     * Gets the authorization code issued to this client.
     */
    public function getCode()
    {
        return $this->code;
    }

    /**
     * Sets the authorization code issued to this client.
     *
     * @param string $code
     */
    public function setCode($code)
    {
        $this->code = $code;
    }

    /**
     * Gets the resource owner's username.
     */
    public function getUsername()
    {
        return $this->username;
    }

    /**
     * Sets the resource owner's username.
     *
     * @param string $username
     */
    public function setUsername($username)
    {
        $this->username = $username;
    }

    /**
     * Gets the resource owner's password.
     */
    public function getPassword()
    {
        return $this->password;
    }

    /**
     * Sets the resource owner's password.
     *
     * @param $password
     */
    public function setPassword($password)
    {
        $this->password = $password;
    }

    /**
     * Sets a unique identifier issued to the client to identify itself to the
     * authorization server.
     */
    public function getClientId()
    {
        return $this->clientId;
    }

    /**
     * Sets a unique identifier issued to the client to identify itself to the
     * authorization server.
     *
     * @param $clientId
     */
    public function setClientId($clientId)
    {
        $this->clientId = $clientId;
    }

    /**
     * Gets a shared symmetric secret issued by the authorization server, which
     * is used to authenticate the client.
     */
    public function getClientSecret()
    {
        return $this->clientSecret;
    }

    /**
     * Sets a shared symmetric secret issued by the authorization server, which
     * is used to authenticate the client.
     *
     * @param $clientSecret
     */
    public function setClientSecret($clientSecret)
    {
        $this->clientSecret = $clientSecret;
    }

    /**
     * Gets the Issuer ID when using assertion profile.
     */
    public function getIssuer()
    {
        return $this->issuer;
    }

    /**
     * Sets the Issuer ID when using assertion profile.
     *
     * @param string $issuer
     */
    public function setIssuer($issuer)
    {
        $this->issuer = $issuer;
    }

    /**
     * Gets the target sub when issuing assertions.
     */
    public function getSub()
    {
        return $this->sub;
    }

    /**
     * Sets the target sub when issuing assertions.
     *
     * @param string $sub
     */
    public function setSub($sub)
    {
        $this->sub = $sub;
    }

    /**
     * Gets the target audience when issuing assertions.
     */
    public function getAudience()
    {
        return $this->audience;
    }

    /**
     * Sets the target audience when issuing assertions.
     *
     * @param string $audience
     */
    public function setAudience($audience)
    {
        $this->audience = $audience;
    }

    /**
     * Gets the signing key when using an assertion profile.
     */
    public function getSigningKey()
    {
        return $this->signingKey;
    }

    /**
     * Sets the signing key when using an assertion profile.
     *
     * @param string $signingKey
     */
    public function setSigningKey($signingKey)
    {
        $this->signingKey = $signingKey;
    }

    /**
     * Gets the signing algorithm when using an assertion profile.
     *
     * @return string
     */
    public function getSigningAlgorithm()
    {
        return $this->signingAlgorithm;
    }

    /**
     * Sets the signing algorithm when using an assertion profile.
     *
     * @param string $signingAlgorithm
     */
    public function setSigningAlgorithm($signingAlgorithm)
    {
        if (is_null($signingAlgorithm)) {
            $this->signingAlgorithm = null;
        } elseif (!in_array($signingAlgorithm, self::$knownSigningAlgorithms)) {
            throw new InvalidArgumentException('unknown signing algorithm');
        } else {
            $this->signingAlgorithm = $signingAlgorithm;
        }
    }

    /**
     * Gets the set of parameters used by extension when using an extension
     * grant type.
     */
    public function getExtensionParams()
    {
        return $this->extensionParams;
    }

    /**
     * Sets the set of parameters used by extension when using an extension
     * grant type.
     *
     * @param $extensionParams
     */
    public function setExtensionParams($extensionParams)
    {
        $this->extensionParams = $extensionParams;
    }

    /**
     * Gets the number of seconds assertions are valid for.
     */
    public function getExpiry()
    {
        return $this->expiry;
    }

    /**
     * Sets the number of seconds assertions are valid for.
     *
     * @param int $expiry
     */
    public function setExpiry($expiry)
    {
        $this->expiry = $expiry;
    }

    /**
     * Gets the lifetime of the access token in seconds.
     */
    public function getExpiresIn()
    {
        return $this->expiresIn;
    }

    /**
     * Sets the lifetime of the access token in seconds.
     *
     * @param int $expiresIn
     */
    public function setExpiresIn($expiresIn)
    {
        if (is_null($expiresIn)) {
            $this->expiresIn = null;
            $this->issuedAt = null;
        } else {
            $this->issuedAt = time();
            $this->expiresIn = (int)$expiresIn;
        }
    }

    /**
     * Gets the time the current access token expires at.
     *
     * @return int
     */
    public function getExpiresAt()
    {
        if (!is_null($this->expiresAt)) {
            return $this->expiresAt;
        }

        if (!is_null($this->issuedAt) && !is_null($this->expiresIn)) {
            return $this->issuedAt + $this->expiresIn;
        }

        return null;
    }

    /**
     * Returns true if the acccess token has expired.
     *
     * @return bool
     */
    public function isExpired()
    {
        $expiration = $this->getExpiresAt();
        $now = time();

        return !is_null($expiration) && $now >= $expiration;
    }

    /**
     * Sets the time the current access token expires at.
     *
     * @param int $expiresAt
     */
    public function setExpiresAt($expiresAt)
    {
        $this->expiresAt = $expiresAt;
    }

    /**
     * Gets the time the current access token was issued at.
     */
    public function getIssuedAt()
    {
        return $this->issuedAt;
    }

    /**
     * Sets the time the current access token was issued at.
     *
     * @param int $issuedAt
     */
    public function setIssuedAt($issuedAt)
    {
        $this->issuedAt = $issuedAt;
    }

    /**
     * Gets the current access token.
     */
    public function getAccessToken()
    {
        return $this->accessToken;
    }

    /**
     * Sets the current access token.
     *
     * @param string $accessToken
     */
    public function setAccessToken($accessToken)
    {
        $this->accessToken = $accessToken;
    }

    /**
     * Gets the current ID token.
     */
    public function getIdToken()
    {
        return $this->idToken;
    }

    /**
     * Sets the current ID token.
     *
     * @param $idToken
     */
    public function setIdToken($idToken)
    {
        $this->idToken = $idToken;
    }

    /**
     * Gets the refresh token associated with the current access token.
     */
    public function getRefreshToken()
    {
        return $this->refreshToken;
    }

    /**
     * Sets the refresh token associated with the current access token.
     *
     * @param $refreshToken
     */
    public function setRefreshToken($refreshToken)
    {
        $this->refreshToken = $refreshToken;
    }

    /**
     * Sets additional claims to be included in the JWT token
     *
     * @param array $additionalClaims
     */
    public function setAdditionalClaims(array $additionalClaims)
    {
        $this->additionalClaims = $additionalClaims;
    }

    /**
     * Gets the additional claims to be included in the JWT token.
     *
     * @return array
     */
    public function getAdditionalClaims()
    {
        return $this->additionalClaims;
    }

    /**
     * The expiration of the last received token.
     *
     * @return array
     */
    public function getLastReceivedToken()
    {
        if ($token = $this->getAccessToken()) {
            return [
                'access_token' => $token,
                'expires_at' => $this->getExpiresAt(),
            ];
        }

        return null;
    }

    /**
     * @todo handle uri as array
     *
     * @param string $uri
     *
     * @return null|UriInterface
     */
    private function coerceUri($uri)
    {
        if (is_null($uri)) {
            return;
        }

        return Psr7\uri_for($uri);
    }

    /**
     * @param string $idToken
     * @param string|array|null $publicKey
     * @param array $allowedAlgs
     *
     * @return object
     */
    private function jwtDecode($idToken, $publicKey, $allowedAlgs)
    {
        if (class_exists('Firebase\JWT\JWT')) {
            return \Firebase\JWT\JWT::decode($idToken, $publicKey, $allowedAlgs);
        }

        return \JWT::decode($idToken, $publicKey, $allowedAlgs);
    }

    private function jwtEncode($assertion, $signingKey, $signingAlgorithm)
    {
        if (class_exists('Firebase\JWT\JWT')) {
            return \Firebase\JWT\JWT::encode($assertion, $signingKey,
                $signingAlgorithm);
        }

        return \JWT::encode($assertion, $signingKey, $signingAlgorithm);
    }

    /**
     * Determines if the URI is absolute based on its scheme and host or path
     * (RFC 3986).
     *
     * @param string $uri
     *
     * @return bool
     */
    private function isAbsoluteUri($uri)
    {
        $uri = $this->coerceUri($uri);

        return $uri->getScheme() && ($uri->getHost() || $uri->getPath());
    }

    /**
     * @param array $params
     *
     * @return array
     */
    private function addClientCredentials(&$params)
    {
        $clientId = $this->getClientId();
        $clientSecret = $this->getClientSecret();

        if ($clientId && $clientSecret) {
            $params['client_id'] = $clientId;
            $params['client_secret'] = $clientSecret;
        }

        return $params;
    }
}
vendor/google/auth/src/ApplicationDefaultCredentials.php000064400000014454151327705670017531 0ustar00<?php
/*
 * Copyright 2015 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

namespace WPvividGoogle\Auth;

use DomainException;
use WPvividGoogle\Auth\Credentials\AppIdentityCredentials;
use WPvividGoogle\Auth\Credentials\GCECredentials;
use WPvividGoogle\Auth\Middleware\AuthTokenMiddleware;
use WPvividGoogle\Auth\Subscriber\AuthTokenSubscriber;
use WPvividPsr\Cache\CacheItemPoolInterface;

/**
 * ApplicationDefaultCredentials obtains the default credentials for
 * authorizing a request to a Google service.
 *
 * Application Default Credentials are described here:
 * https://developers.google.com/accounts/docs/application-default-credentials
 *
 * This class implements the search for the application default credentials as
 * described in the link.
 *
 * It provides three factory methods:
 * - #get returns the computed credentials object
 * - #getSubscriber returns an AuthTokenSubscriber built from the credentials object
 * - #getMiddleware returns an AuthTokenMiddleware built from the credentials object
 *
 * This allows it to be used as follows with GuzzleHttp\Client:
 *
 *   use Google\Auth\ApplicationDefaultCredentials;
 *   use GuzzleHttp\Client;
 *   use GuzzleHttp\HandlerStack;
 *
 *   $middleware = ApplicationDefaultCredentials::getMiddleware(
 *       'https://www.googleapis.com/auth/taskqueue'
 *   );
 *   $stack = HandlerStack::create();
 *   $stack->push($middleware);
 *
 *   $client = new Client([
 *       'handler' => $stack,
 *       'base_uri' => 'https://www.googleapis.com/taskqueue/v1beta2/projects/',
 *       'auth' => 'google_auth' // authorize all requests
 *   ]);
 *
 *   $res = $client->get('myproject/taskqueues/myqueue');
 */
class ApplicationDefaultCredentials
{
    /**
     * Obtains an AuthTokenSubscriber that uses the default FetchAuthTokenInterface
     * implementation to use in this environment.
     *
     * If supplied, $scope is used to in creating the credentials instance if
     * this does not fallback to the compute engine defaults.
     *
     * @param string|array scope the scope of the access request, expressed
     *   either as an Array or as a space-delimited String.
     * @param callable $httpHandler callback which delivers psr7 request
     * @param array $cacheConfig configuration for the cache when it's present
     * @param CacheItemPoolInterface $cache an implementation of CacheItemPoolInterface
     *
     * @return AuthTokenSubscriber
     *
     * @throws DomainException if no implementation can be obtained.
     */
    public static function getSubscriber(
        $scope = null,
        callable $httpHandler = null,
        array $cacheConfig = null,
        CacheItemPoolInterface $cache = null
    ) {
        $creds = self::getCredentials($scope, $httpHandler, $cacheConfig, $cache);

        return new AuthTokenSubscriber($creds, $httpHandler);
    }

    /**
     * Obtains an AuthTokenMiddleware that uses the default FetchAuthTokenInterface
     * implementation to use in this environment.
     *
     * If supplied, $scope is used to in creating the credentials instance if
     * this does not fallback to the compute engine defaults.
     *
     * @param string|array scope the scope of the access request, expressed
     *   either as an Array or as a space-delimited String.
     * @param callable $httpHandler callback which delivers psr7 request
     * @param array $cacheConfig configuration for the cache when it's present
     * @param CacheItemPoolInterface $cache
     *
     * @return AuthTokenMiddleware
     *
     * @throws DomainException if no implementation can be obtained.
     */
    public static function getMiddleware(
        $scope = null,
        callable $httpHandler = null,
        array $cacheConfig = null,
        CacheItemPoolInterface $cache = null
    ) {
        $creds = self::getCredentials($scope, $httpHandler, $cacheConfig, $cache);

        return new AuthTokenMiddleware($creds, $httpHandler);
    }

    /**
     * Obtains the default FetchAuthTokenInterface implementation to use
     * in this environment.
     *
     * If supplied, $scope is used to in creating the credentials instance if
     * this does not fallback to the Compute Engine defaults.
     *
     * @param string|array scope the scope of the access request, expressed
     *   either as an Array or as a space-delimited String.
     * @param callable $httpHandler callback which delivers psr7 request
     * @param array $cacheConfig configuration for the cache when it's present
     * @param CacheItemPoolInterface $cache
     *
     * @return CredentialsLoader
     *
     * @throws DomainException if no implementation can be obtained.
     */
    public static function getCredentials(
        $scope = null,
        callable $httpHandler = null,
        array $cacheConfig = null,
        CacheItemPoolInterface $cache = null
    ) {
        $creds = null;
        $jsonKey = CredentialsLoader::fromEnv()
            ?: CredentialsLoader::fromWellKnownFile();

        if (!is_null($jsonKey)) {
            $creds = CredentialsLoader::makeCredentials($scope, $jsonKey);
        } elseif (AppIdentityCredentials::onAppEngine() && !GCECredentials::onAppEngineFlexible()) {
            $creds = new AppIdentityCredentials($scope);
        } elseif (GCECredentials::onGce($httpHandler)) {
            $creds = new GCECredentials();
        }

        if (is_null($creds)) {
            throw new \DomainException(self::notFound());
        }
        if (!is_null($cache)) {
            $creds = new FetchAuthTokenCache($creds, $cacheConfig, $cache);
        }
        return $creds;
    }

    private static function notFound()
    {
        $msg = 'Could not load the default credentials. Browse to ';
        $msg .= 'https://developers.google.com';
        $msg .= '/accounts/docs/application-default-credentials';
        $msg .= ' for more information';

        return $msg;
    }
}
vendor/google/auth/src/Middleware/AuthTokenMiddleware.php000064400000007557151327705670017566 0ustar00<?php
/*
 * Copyright 2015 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

namespace WPvividGoogle\Auth\Middleware;

use WPvividGoogle\Auth\FetchAuthTokenInterface;
use WPvividPsr\Http\Message\RequestInterface;

/**
 * AuthTokenMiddleware is a Guzzle Middleware that adds an Authorization header
 * provided by an object implementing FetchAuthTokenInterface.
 *
 * The FetchAuthTokenInterface#fetchAuthToken is used to obtain a hash; one of
 * the values value in that hash is added as the authorization header.
 *
 * Requests will be accessed with the authorization header:
 *
 * 'authorization' 'Bearer <value of auth_token>'
 */
class AuthTokenMiddleware
{
    /**
     * @var callback
     */
    private $httpHandler;

    /**
     * @var FetchAuthTokenInterface
     */
    private $fetcher;

    /**
     * @var callable
     */
    private $tokenCallback;

    /**
     * Creates a new AuthTokenMiddleware.
     *
     * @param FetchAuthTokenInterface $fetcher is used to fetch the auth token
     * @param callable $httpHandler (optional) callback which delivers psr7 request
     * @param callable $tokenCallback (optional) function to be called when a new token is fetched.
     */
    public function __construct(
        FetchAuthTokenInterface $fetcher,
        callable $httpHandler = null,
        callable $tokenCallback = null
    ) {
        $this->fetcher = $fetcher;
        $this->httpHandler = $httpHandler;
        $this->tokenCallback = $tokenCallback;
    }

    /**
     * Updates the request with an Authorization header when auth is 'google_auth'.
     *
     *   use Google\Auth\Middleware\AuthTokenMiddleware;
     *   use Google\Auth\OAuth2;
     *   use GuzzleHttp\Client;
     *   use GuzzleHttp\HandlerStack;
     *
     *   $config = [..<oauth config param>.];
     *   $oauth2 = new OAuth2($config)
     *   $middleware = new AuthTokenMiddleware($oauth2);
     *   $stack = HandlerStack::create();
     *   $stack->push($middleware);
     *
     *   $client = new Client([
     *       'handler' => $stack,
     *       'base_uri' => 'https://www.googleapis.com/taskqueue/v1beta2/projects/',
     *       'auth' => 'google_auth' // authorize all requests
     *   ]);
     *
     *   $res = $client->get('myproject/taskqueues/myqueue');
     *
     * @param callable $handler
     *
     * @return \Closure
     */
    public function __invoke(callable $handler)
    {
        return function (RequestInterface $request, array $options) use ($handler) {
            // Requests using "auth"="google_auth" will be authorized.
            if (!isset($options['auth']) || $options['auth'] !== 'google_auth') {
                return $handler($request, $options);
            }

            $request = $request->withHeader('authorization', 'Bearer ' . $this->fetchToken());

            return $handler($request, $options);
        };
    }

    /**
     * Call fetcher to fetch the token.
     *
     * @return string
     */
    private function fetchToken()
    {
        $auth_tokens = $this->fetcher->fetchAuthToken($this->httpHandler);

        if (array_key_exists('access_token', $auth_tokens)) {
            // notify the callback if applicable
            if ($this->tokenCallback) {
                call_user_func($this->tokenCallback, $this->fetcher->getCacheKey(), $auth_tokens['access_token']);
            }

            return $auth_tokens['access_token'];
        }
    }
}
vendor/google/auth/src/Middleware/SimpleMiddleware.php000064400000005465151327705670017111 0ustar00<?php
/*
 * Copyright 2015 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

namespace WPvividGoogle\Auth\Middleware;

use WPvividGuzzleHttp\Psr7;
use WPvividPsr\Http\Message\RequestInterface;

/**
 * SimpleMiddleware is a Guzzle Middleware that implements Google's Simple API
 * access.
 *
 * Requests are accessed using the Simple API access developer key.
 */
class SimpleMiddleware
{
    /**
     * @var array
     */
    private $config;

    /**
     * Create a new Simple plugin.
     *
     * The configuration array expects one option
     * - key: required, otherwise InvalidArgumentException is thrown
     *
     * @param array $config Configuration array
     */
    public function __construct(array $config)
    {
        if (!isset($config['key'])) {
            throw new \InvalidArgumentException('requires a key to have been set');
        }

        $this->config = array_merge(['key' => null], $config);
    }

    /**
     * Updates the request query with the developer key if auth is set to simple.
     *
     *   use Google\Auth\Middleware\SimpleMiddleware;
     *   use GuzzleHttp\Client;
     *   use GuzzleHttp\HandlerStack;
     *
     *   $my_key = 'is not the same as yours';
     *   $middleware = new SimpleMiddleware(['key' => $my_key]);
     *   $stack = HandlerStack::create();
     *   $stack->push($middleware);
     *
     *   $client = new Client([
     *       'handler' => $stack,
     *       'base_uri' => 'https://www.googleapis.com/discovery/v1/',
     *       'auth' => 'simple'
     *   ]);
     *
     *   $res = $client->get('drive/v2/rest');
     *
     * @param callable $handler
     *
     * @return \Closure
     */
    public function __invoke(callable $handler)
    {
        return function (RequestInterface $request, array $options) use ($handler) {
            // Requests using "auth"="scoped" will be authorized.
            if (!isset($options['auth']) || $options['auth'] !== 'simple') {
                return $handler($request, $options);
            }

            $query = Psr7\parse_query($request->getUri()->getQuery());
            $params = array_merge($query, $this->config);
            $uri = $request->getUri()->withQuery(Psr7\build_query($params));
            $request = $request->withUri($uri);

            return $handler($request, $options);
        };
    }
}
vendor/google/auth/src/Middleware/ScopedAccessTokenMiddleware.php000064400000011765151327705670021220 0ustar00<?php
/*
 * Copyright 2015 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

namespace WPvividGoogle\Auth\Middleware;

use WPvividGoogle\Auth\CacheTrait;
use WPvividPsr\Cache\CacheItemPoolInterface;
use WPvividPsr\Http\Message\RequestInterface;

/**
 * ScopedAccessTokenMiddleware is a Guzzle Middleware that adds an Authorization
 * header provided by a closure.
 *
 * The closure returns an access token, taking the scope, either a single
 * string or an array of strings, as its value.  If provided, a cache will be
 * used to preserve the access token for a given lifetime.
 *
 * Requests will be accessed with the authorization header:
 *
 * 'authorization' 'Bearer <value of auth_token>'
 */
class ScopedAccessTokenMiddleware
{
    use CacheTrait;

    const DEFAULT_CACHE_LIFETIME = 1500;

    /**
     * @var CacheItemPoolInterface
     */
    private $cache;

    /**
     * @var array configuration
     */
    private $cacheConfig;

    /**
     * @var callable
     */
    private $tokenFunc;

    /**
     * @var array|string
     */
    private $scopes;

    /**
     * Creates a new ScopedAccessTokenMiddleware.
     *
     * @param callable $tokenFunc a token generator function
     * @param array|string $scopes the token authentication scopes
     * @param array $cacheConfig configuration for the cache when it's present
     * @param CacheItemPoolInterface $cache an implementation of CacheItemPoolInterface
     */
    public function __construct(
        callable $tokenFunc,
        $scopes,
        array $cacheConfig = null,
        CacheItemPoolInterface $cache = null
    ) {
        $this->tokenFunc = $tokenFunc;
        if (!(is_string($scopes) || is_array($scopes))) {
            throw new \InvalidArgumentException(
                'wants scope should be string or array');
        }
        $this->scopes = $scopes;

        if (!is_null($cache)) {
            $this->cache = $cache;
            $this->cacheConfig = array_merge([
                'lifetime' => self::DEFAULT_CACHE_LIFETIME,
                'prefix' => '',
            ], $cacheConfig);
        }
    }

    /**
     * Updates the request with an Authorization header when auth is 'scoped'.
     *
     *   E.g this could be used to authenticate using the AppEngine
     *   AppIdentityService.
     *
     *   use google\appengine\api\app_identity\AppIdentityService;
     *   use Google\Auth\Middleware\ScopedAccessTokenMiddleware;
     *   use GuzzleHttp\Client;
     *   use GuzzleHttp\HandlerStack;
     *
     *   $scope = 'https://www.googleapis.com/auth/taskqueue'
     *   $middleware = new ScopedAccessTokenMiddleware(
     *       'AppIdentityService::getAccessToken',
     *       $scope,
     *       [ 'prefix' => 'Google\Auth\ScopedAccessToken::' ],
     *       $cache = new Memcache()
     *   );
     *   $stack = HandlerStack::create();
     *   $stack->push($middleware);
     *
     *   $client = new Client([
     *       'handler' => $stack,
     *       'base_url' => 'https://www.googleapis.com/taskqueue/v1beta2/projects/',
     *       'auth' => 'scoped' // authorize all requests
     *   ]);
     *
     *   $res = $client->get('myproject/taskqueues/myqueue');
     *
     * @param callable $handler
     *
     * @return \Closure
     */
    public function __invoke(callable $handler)
    {
        return function (RequestInterface $request, array $options) use ($handler) {
            // Requests using "auth"="scoped" will be authorized.
            if (!isset($options['auth']) || $options['auth'] !== 'scoped') {
                return $handler($request, $options);
            }

            $request = $request->withHeader('authorization', 'Bearer ' . $this->fetchToken());

            return $handler($request, $options);
        };
    }

    /**
     * @return string
     */
    private function getCacheKey()
    {
        $key = null;

        if (is_string($this->scopes)) {
            $key .= $this->scopes;
        } elseif (is_array($this->scopes)) {
            $key .= implode(':', $this->scopes);
        }

        return $key;
    }

    /**
     * Determine if token is available in the cache, if not call tokenFunc to
     * fetch it.
     *
     * @return string
     */
    private function fetchToken()
    {
        $cacheKey = $this->getCacheKey();
        $cached = $this->getCachedValue($cacheKey);

        if (!empty($cached)) {
            return $cached;
        }

        $token = call_user_func($this->tokenFunc, $this->scopes);
        $this->setCachedValue($cacheKey, $token);

        return $token;
    }
}
vendor/google/auth/src/Cache/Item.php000064400000010025151327705670013472 0ustar00<?php
/*
 * Copyright 2016 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

namespace WPvividGoogle\Auth\Cache;

use WPvividPsr\Cache\CacheItemInterface;

/**
 * A cache item.
 */
final class Item implements CacheItemInterface
{
    /**
     * @var string
     */
    private $key;

    /**
     * @var mixed
     */
    private $value;

    /**
     * @var \DateTime
     */
    private $expiration;

    /**
     * @var bool
     */
    private $isHit = false;

    /**
     * @param string $key
     */
    public function __construct($key)
    {
        $this->key = $key;
    }

    /**
     * {@inheritdoc}
     */
    public function getKey()
    {
        return $this->key;
    }

    /**
     * {@inheritdoc}
     */
    public function get()
    {
        return $this->isHit() ? $this->value : null;
    }

    /**
     * {@inheritdoc}
     */
    public function isHit()
    {
        if (!$this->isHit) {
            return false;
        }

        if ($this->expiration === null) {
            return true;
        }

        return new \DateTime() < $this->expiration;
    }

    /**
     * {@inheritdoc}
     */
    public function set($value)
    {
        $this->isHit = true;
        $this->value = $value;

        return $this;
    }

    /**
     * {@inheritdoc}
     */
    public function expiresAt($expiration)
    {
        if ($this->isValidExpiration($expiration)) {
            $this->expiration = $expiration;

            return $this;
        }

        $implementationMessage = interface_exists('DateTimeInterface')
            ? 'implement interface DateTimeInterface'
            : 'be an instance of DateTime';

        $error = sprintf(
            'Argument 1 passed to %s::expiresAt() must %s, %s given',
            get_class($this),
            $implementationMessage,
            gettype($expiration)
        );

        $this->handleError($error);
    }

    /**
     * {@inheritdoc}
     */
    public function expiresAfter($time)
    {
        if (is_int($time)) {
            $this->expiration = new \DateTime("now + $time seconds");
        } elseif ($time instanceof \DateInterval) {
            $this->expiration = (new \DateTime())->add($time);
        } elseif ($time === null) {
            $this->expiration = $time;
        } else {
            $message = 'Argument 1 passed to %s::expiresAfter() must be an ' .
                       'instance of DateInterval or of the type integer, %s given';
            $error = sprintf($message, get_class($this), gettype($time));

            $this->handleError($error);
        }

        return $this;
    }

    /**
     * Handles an error.
     *
     * @param string $error
     * @throws \TypeError
     */
    private function handleError($error)
    {
        if (class_exists('TypeError')) {
            throw new \TypeError($error);
        }

        trigger_error($error, E_USER_ERROR);
    }

    /**
     * Determines if an expiration is valid based on the rules defined by PSR6.
     *
     * @param mixed $expiration
     * @return bool
     */
    private function isValidExpiration($expiration)
    {
        if ($expiration === null) {
            return true;
        }

        // We test for two types here due to the fact the DateTimeInterface
        // was not introduced until PHP 5.5. Checking for the DateTime type as
        // well allows us to support 5.4.
        if ($expiration instanceof \DateTimeInterface) {
            return true;
        }

        if ($expiration instanceof \DateTime) {
            return true;
        }

        return false;
    }
}
vendor/google/auth/src/Cache/MemoryCacheItemPool.php000064400000006174151327705670016453 0ustar00<?php
/*
 * Copyright 2016 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

namespace WPvividGoogle\Auth\Cache;

use WPvividPsr\Cache\CacheItemInterface;
use WPvividPsr\Cache\CacheItemPoolInterface;

/**
 * Simple in-memory cache implementation.
 */
final class MemoryCacheItemPool implements CacheItemPoolInterface
{
    /**
     * @var CacheItemInterface[]
     */
    private $items;

    /**
     * @var CacheItemInterface[]
     */
    private $deferredItems;

    /**
     * {@inheritdoc}
     */
    public function getItem($key)
    {
        return current($this->getItems([$key]));
    }

    /**
     * {@inheritdoc}
     */
    public function getItems(array $keys = [])
    {
        $items = [];

        foreach ($keys as $key) {
            $items[$key] = $this->hasItem($key) ? clone $this->items[$key] : new Item($key);
        }

        return $items;
    }

    /**
     * {@inheritdoc}
     */
    public function hasItem($key)
    {
        $this->isValidKey($key);

        return isset($this->items[$key]) && $this->items[$key]->isHit();
    }

    /**
     * {@inheritdoc}
     */
    public function clear()
    {
        $this->items = [];
        $this->deferredItems = [];

        return true;
    }

    /**
     * {@inheritdoc}
     */
    public function deleteItem($key)
    {
        return $this->deleteItems([$key]);
    }

    /**
     * {@inheritdoc}
     */
    public function deleteItems(array $keys)
    {
        array_walk($keys, [$this, 'isValidKey']);

        foreach ($keys as $key) {
            unset($this->items[$key]);
        }

        return true;
    }

    /**
     * {@inheritdoc}
     */
    public function save(CacheItemInterface $item)
    {
        $this->items[$item->getKey()] = $item;

        return true;
    }

    /**
     * {@inheritdoc}
     */
    public function saveDeferred(CacheItemInterface $item)
    {
        $this->deferredItems[$item->getKey()] = $item;

        return true;
    }

    /**
     * {@inheritdoc}
     */
    public function commit()
    {
        foreach ($this->deferredItems as $item) {
            $this->save($item);
        }

        $this->deferredItems = [];

        return true;
    }

    /**
     * Determines if the provided key is valid.
     *
     * @param string $key
     * @return bool
     * @throws InvalidArgumentException
     */
    private function isValidKey($key)
    {
        $invalidCharacters = '{}()/\\\\@:';

        if (!is_string($key) || preg_match("#[$invalidCharacters]#", $key)) {
            throw new InvalidArgumentException('The provided key is not valid: ' . var_export($key, true));
        }

        return true;
    }
}
vendor/google/auth/src/Cache/InvalidArgumentException.php000064400000001472151327705670017552 0ustar00<?php
/*
 * Copyright 2016 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

namespace WPvividGoogle\Auth\Cache;

use WPvividPsr\Cache\InvalidArgumentException as PsrInvalidArgumentException;

class InvalidArgumentException extends \InvalidArgumentException implements PsrInvalidArgumentException
{
}
vendor/google/auth/src/Cache/SysVCacheItemPool.php000064400000013270151327705670016102 0ustar00<?php
/**
 * Copyright 2018 Google Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
namespace WPvividGoogle\Auth\Cache;

use WPvividPsr\Cache\CacheItemInterface;
use WPvividPsr\Cache\CacheItemPoolInterface;

/**
 * SystemV shared memory based CacheItemPool implementation.
 *
 * This CacheItemPool implementation can be used among multiple processes, but
 * it doesn't provide any locking mechanism. If multiple processes write to
 * this ItemPool, you have to avoid race condition manually in your code.
 */
class SysVCacheItemPool implements CacheItemPoolInterface
{
    const VAR_KEY = 1;

    const DEFAULT_PROJ = 'A';

    const DEFAULT_MEMSIZE = 10000;

    const DEFAULT_PERM = 0600;

    /** @var int */
    private $sysvKey;

    /**
     * @var CacheItemInterface[]
     */
    private $items;

    /**
     * @var CacheItemInterface[]
     */
    private $deferredItems;

    /**
     * @var array
     */
    private $options;

    /**
     * Save the current items.
     *
     * @return bool true when success, false upon failure
     */
    private function saveCurrentItems()
    {
        $shmid = shm_attach(
            $this->sysvKey,
            $this->options['memsize'],
            $this->options['perm']
        );
        if ($shmid !== false) {
            $ret = shm_put_var(
                $shmid,
                $this->options['variableKey'],
                $this->items
            );
            shm_detach($shmid);
            return $ret;
        }
        return false;
    }

    /**
     * Load the items from the shared memory.
     *
     * @return bool true when success, false upon failure
     */
    private function loadItems()
    {
        $shmid = shm_attach(
            $this->sysvKey,
            $this->options['memsize'],
            $this->options['perm']
        );
        if ($shmid !== false) {
            $data = @shm_get_var($shmid, $this->options['variableKey']);
            if (!empty($data)) {
                $this->items = $data;
            } else {
                $this->items = [];
            }
            shm_detach($shmid);
            return true;
        }
        return false;
    }

    /**
     * Create a SystemV shared memory based CacheItemPool.
     *
     * @param array $options [optional] {
     *     Configuration options.
     *
     *     @type int $variableKey The variable key for getting the data from
     *           the shared memory. **Defaults to** 1.
     *     @type string $proj The project identifier for ftok. This needs to
     *           be a one character string. **Defaults to** 'A'.
     *     @type int $memsize The memory size in bytes for shm_attach.
     *           **Defaults to** 10000.
     *     @type int $perm The permission for shm_attach. **Defaults to** 0600.
     */
    public function __construct($options = [])
    {
        if (! extension_loaded('sysvshm')) {
            throw \RuntimeException(
                'sysvshm extension is required to use this ItemPool');
        }
        $this->options = $options + [
            'variableKey' => self::VAR_KEY,
            'proj' => self::DEFAULT_PROJ,
            'memsize' => self::DEFAULT_MEMSIZE,
            'perm' => self::DEFAULT_PERM
        ];
        $this->items = [];
        $this->deferredItems = [];
        $this->sysvKey = ftok(__FILE__, $this->options['proj']);
        $this->loadItems();
    }

    /**
     * {@inheritdoc}
     */
    public function getItem($key)
    {
        $this->loadItems();
        return current($this->getItems([$key]));
    }

    /**
     * {@inheritdoc}
     */
    public function getItems(array $keys = [])
    {
        $this->loadItems();
        $items = [];
        foreach ($keys as $key) {
            $items[$key] = $this->hasItem($key) ?
                clone $this->items[$key] :
                new Item($key);
        }
        return $items;
    }

    /**
     * {@inheritdoc}
     */
    public function hasItem($key)
    {
        $this->loadItems();
        return isset($this->items[$key]) && $this->items[$key]->isHit();
    }

    /**
     * {@inheritdoc}
     */
    public function clear()
    {
        $this->items = [];
        $this->deferredItems = [];
        return $this->saveCurrentItems();
    }

    /**
     * {@inheritdoc}
     */
    public function deleteItem($key)
    {
        return $this->deleteItems([$key]);
    }

    /**
     * {@inheritdoc}
     */
    public function deleteItems(array $keys)
    {
        foreach ($keys as $key) {
            unset($this->items[$key]);
        }
        return $this->saveCurrentItems();
    }

    /**
     * {@inheritdoc}
     */
    public function save(CacheItemInterface $item)
    {
        $this->items[$item->getKey()] = $item;
        return $this->saveCurrentItems();
    }

    /**
     * {@inheritdoc}
     */
    public function saveDeferred(CacheItemInterface $item)
    {
        $this->deferredItems[$item->getKey()] = $item;
        return true;
    }

    /**
     * {@inheritdoc}
     */
    public function commit()
    {
        foreach ($this->deferredItems as $item) {
            if ($this->save($item) === false) {
                return false;
            }
        }
        $this->deferredItems = [];
        return true;
    }
}
vendor/google/auth/src/Subscriber/SimpleSubscriber.php000064400000005107151327705670017156 0ustar00<?php
/*
 * Copyright 2015 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

namespace WPvividGoogle\Auth\Subscriber;

use WPvividGuzzleHttp\Event\BeforeEvent;
use WPvividGuzzleHttp\Event\RequestEvents;
use WPvividGuzzleHttp\Event\SubscriberInterface;

/**
 * SimpleSubscriber is a Guzzle Subscriber that implements Google's Simple API
 * access.
 *
 * Requests are accessed using the Simple API access developer key.
 */
class SimpleSubscriber implements SubscriberInterface
{
    /**
     * @var array
     */
    private $config;

    /**
     * Create a new Simple plugin.
     *
     * The configuration array expects one option
     * - key: required, otherwise InvalidArgumentException is thrown
     *
     * @param array $config Configuration array
     */
    public function __construct(array $config)
    {
        if (!isset($config['key'])) {
            throw new \InvalidArgumentException('requires a key to have been set');
        }

        $this->config = array_merge([], $config);
    }

    /**
     * @return array
     */
    public function getEvents()
    {
        return ['before' => ['onBefore', RequestEvents::SIGN_REQUEST]];
    }

    /**
     * Updates the request query with the developer key if auth is set to simple.
     *
     *   use Google\Auth\Subscriber\SimpleSubscriber;
     *   use GuzzleHttp\Client;
     *
     *   $my_key = 'is not the same as yours';
     *   $subscriber = new SimpleSubscriber(['key' => $my_key]);
     *
     *   $client = new Client([
     *      'base_url' => 'https://www.googleapis.com/discovery/v1/',
     *      'defaults' => ['auth' => 'simple']
     *   ]);
     *   $client->getEmitter()->attach($subscriber);
     *
     *   $res = $client->get('drive/v2/rest');
     *
     * @param BeforeEvent $event
     */
    public function onBefore(BeforeEvent $event)
    {
        // Requests using "auth"="simple" with the developer key.
        $request = $event->getRequest();
        if ($request->getConfig()['auth'] != 'simple') {
            return;
        }
        $request->getQuery()->overwriteWith($this->config);
    }
}
vendor/google/auth/src/Subscriber/AuthTokenSubscriber.php000064400000007271151327705670017633 0ustar00<?php
/*
 * Copyright 2015 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

namespace WPvividGoogle\Auth\Subscriber;

use WPvividGoogle\Auth\FetchAuthTokenInterface;
use WPvividGuzzleHttp\Event\BeforeEvent;
use WPvividGuzzleHttp\Event\RequestEvents;
use WPvividGuzzleHttp\Event\SubscriberInterface;

/**
 * AuthTokenSubscriber is a Guzzle Subscriber that adds an Authorization header
 * provided by an object implementing FetchAuthTokenInterface.
 *
 * The FetchAuthTokenInterface#fetchAuthToken is used to obtain a hash; one of
 * the values value in that hash is added as the authorization header.
 *
 * Requests will be accessed with the authorization header:
 *
 * 'authorization' 'Bearer <value of auth_token>'
 */
class AuthTokenSubscriber implements SubscriberInterface
{
    /**
     * @var callable
     */
    private $httpHandler;

    /**
     * @var FetchAuthTokenInterface
     */
    private $fetcher;

    /**
     * @var callable
     */
    private $tokenCallback;

    /**
     * Creates a new AuthTokenSubscriber.
     *
     * @param FetchAuthTokenInterface $fetcher is used to fetch the auth token
     * @param callable $httpHandler (optional) http client to fetch the token.
     * @param callable $tokenCallback (optional) function to be called when a new token is fetched.
     */
    public function __construct(
        FetchAuthTokenInterface $fetcher,
        callable $httpHandler = null,
        callable $tokenCallback = null
    ) {
        $this->fetcher = $fetcher;
        $this->httpHandler = $httpHandler;
        $this->tokenCallback = $tokenCallback;
    }

    /**
     * @return array
     */
    public function getEvents()
    {
        return ['before' => ['onBefore', RequestEvents::SIGN_REQUEST]];
    }

    /**
     * Updates the request with an Authorization header when auth is 'fetched_auth_token'.
     *
     *   use GuzzleHttp\Client;
     *   use Google\Auth\OAuth2;
     *   use Google\Auth\Subscriber\AuthTokenSubscriber;
     *
     *   $config = [..<oauth config param>.];
     *   $oauth2 = new OAuth2($config)
     *   $subscriber = new AuthTokenSubscriber($oauth2);
     *
     *   $client = new Client([
     *      'base_url' => 'https://www.googleapis.com/taskqueue/v1beta2/projects/',
     *      'defaults' => ['auth' => 'google_auth']
     *   ]);
     *   $client->getEmitter()->attach($subscriber);
     *
     *   $res = $client->get('myproject/taskqueues/myqueue');
     *
     * @param BeforeEvent $event
     */
    public function onBefore(BeforeEvent $event)
    {
        // Requests using "auth"="google_auth" will be authorized.
        $request = $event->getRequest();
        if ($request->getConfig()['auth'] != 'google_auth') {
            return;
        }

        // Fetch the auth token.
        $auth_tokens = $this->fetcher->fetchAuthToken($this->httpHandler);
        if (array_key_exists('access_token', $auth_tokens)) {
            $request->setHeader('authorization', 'Bearer ' . $auth_tokens['access_token']);

            // notify the callback if applicable
            if ($this->tokenCallback) {
                call_user_func($this->tokenCallback, $this->fetcher->getCacheKey(), $auth_tokens['access_token']);
            }
        }
    }
}
vendor/google/auth/src/Subscriber/ScopedAccessTokenSubscriber.php000064400000012056151327705670021266 0ustar00<?php
/*
 * Copyright 2015 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

namespace WPvividGoogle\Auth\Subscriber;

use WPvividGoogle\Auth\CacheTrait;
use WPvividGuzzleHttp\Event\BeforeEvent;
use WPvividGuzzleHttp\Event\RequestEvents;
use WPvividGuzzleHttp\Event\SubscriberInterface;
use WPvividPsr\Cache\CacheItemPoolInterface;

/**
 * ScopedAccessTokenSubscriber is a Guzzle Subscriber that adds an Authorization
 * header provided by a closure.
 *
 * The closure returns an access token, taking the scope, either a single
 * string or an array of strings, as its value.  If provided, a cache will be
 * used to preserve the access token for a given lifetime.
 *
 * Requests will be accessed with the authorization header:
 *
 * 'authorization' 'Bearer <access token obtained from the closure>'
 */
class ScopedAccessTokenSubscriber implements SubscriberInterface
{
    use CacheTrait;

    const DEFAULT_CACHE_LIFETIME = 1500;

    /**
     * @var CacheItemPoolInterface
     */
    private $cache;

    /**
     * @var callable The access token generator function
     */
    private $tokenFunc;

    /**
     * @var array|string The scopes used to generate the token
     */
    private $scopes;

    /**
     * @var array
     */
    private $cacheConfig;

    /**
     * Creates a new ScopedAccessTokenSubscriber.
     *
     * @param callable $tokenFunc a token generator function
     * @param array|string $scopes the token authentication scopes
     * @param array $cacheConfig configuration for the cache when it's present
     * @param CacheItemPoolInterface $cache an implementation of CacheItemPoolInterface
     */
    public function __construct(
        callable $tokenFunc,
        $scopes,
        array $cacheConfig = null,
        CacheItemPoolInterface $cache = null
    ) {
        $this->tokenFunc = $tokenFunc;
        if (!(is_string($scopes) || is_array($scopes))) {
            throw new \InvalidArgumentException(
                'wants scope should be string or array');
        }
        $this->scopes = $scopes;

        if (!is_null($cache)) {
            $this->cache = $cache;
            $this->cacheConfig = array_merge([
                'lifetime' => self::DEFAULT_CACHE_LIFETIME,
                'prefix' => '',
            ], $cacheConfig);
        }
    }

    /**
     * @return array
     */
    public function getEvents()
    {
        return ['before' => ['onBefore', RequestEvents::SIGN_REQUEST]];
    }

    /**
     * Updates the request with an Authorization header when auth is 'scoped'.
     *
     *   E.g this could be used to authenticate using the AppEngine
     *   AppIdentityService.
     *
     *   use google\appengine\api\app_identity\AppIdentityService;
     *   use Google\Auth\Subscriber\ScopedAccessTokenSubscriber;
     *   use GuzzleHttp\Client;
     *
     *   $scope = 'https://www.googleapis.com/auth/taskqueue'
     *   $subscriber = new ScopedAccessToken(
     *       'AppIdentityService::getAccessToken',
     *       $scope,
     *       ['prefix' => 'Google\Auth\ScopedAccessToken::'],
     *       $cache = new Memcache()
     *   );
     *
     *   $client = new Client([
     *       'base_url' => 'https://www.googleapis.com/taskqueue/v1beta2/projects/',
     *       'defaults' => ['auth' => 'scoped']
     *   ]);
     *   $client->getEmitter()->attach($subscriber);
     *
     *   $res = $client->get('myproject/taskqueues/myqueue');
     *
     * @param BeforeEvent $event
     */
    public function onBefore(BeforeEvent $event)
    {
        // Requests using "auth"="scoped" will be authorized.
        $request = $event->getRequest();
        if ($request->getConfig()['auth'] != 'scoped') {
            return;
        }
        $auth_header = 'Bearer ' . $this->fetchToken();
        $request->setHeader('authorization', $auth_header);
    }

    /**
     * @return string
     */
    private function getCacheKey()
    {
        $key = null;

        if (is_string($this->scopes)) {
            $key .= $this->scopes;
        } elseif (is_array($this->scopes)) {
            $key .= implode(':', $this->scopes);
        }

        return $key;
    }

    /**
     * Determine if token is available in the cache, if not call tokenFunc to
     * fetch it.
     *
     * @return string
     */
    private function fetchToken()
    {
        $cacheKey = $this->getCacheKey();
        $cached = $this->getCachedValue($cacheKey);

        if (!empty($cached)) {
            return $cached;
        }

        $token = call_user_func($this->tokenFunc, $this->scopes);
        $this->setCachedValue($cacheKey, $token);

        return $token;
    }
}
vendor/google/auth/src/Credentials/ServiceAccountCredentials.php000064400000012746151327705670021135 0ustar00<?php
/*
 * Copyright 2015 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

namespace WPvividGoogle\Auth\Credentials;

use WPvividGoogle\Auth\CredentialsLoader;
use WPvividGoogle\Auth\OAuth2;

/**
 * ServiceAccountCredentials supports authorization using a Google service
 * account.
 *
 * (cf https://developers.google.com/accounts/docs/OAuth2ServiceAccount)
 *
 * It's initialized using the json key file that's downloadable from developer
 * console, which should contain a private_key and client_email fields that it
 * uses.
 *
 * Use it with AuthTokenMiddleware to authorize http requests:
 *
 *   use Google\Auth\Credentials\ServiceAccountCredentials;
 *   use Google\Auth\Middleware\AuthTokenMiddleware;
 *   use GuzzleHttp\Client;
 *   use GuzzleHttp\HandlerStack;
 *
 *   $sa = new ServiceAccountCredentials(
 *       'https://www.googleapis.com/auth/taskqueue',
 *       '/path/to/your/json/key_file.json'
 *   );
 *   $middleware = new AuthTokenMiddleware($sa);
 *   $stack = HandlerStack::create();
 *   $stack->push($middleware);
 *
 *   $client = new Client([
 *       'handler' => $stack,
 *       'base_uri' => 'https://www.googleapis.com/taskqueue/v1beta2/projects/',
 *       'auth' => 'google_auth' // authorize all requests
 *   ]);
 *
 *   $res = $client->get('myproject/taskqueues/myqueue');
 */
class ServiceAccountCredentials extends CredentialsLoader
{
    /**
     * The OAuth2 instance used to conduct authorization.
     *
     * @var OAuth2
     */
    protected $auth;

    /**
     * Create a new ServiceAccountCredentials.
     *
     * @param string|array $scope the scope of the access request, expressed
     *   either as an Array or as a space-delimited String.
     * @param string|array $jsonKey JSON credential file path or JSON credentials
     *   as an associative array
     * @param string $sub an email address account to impersonate, in situations when
     *   the service account has been delegated domain wide access.
     */
    public function __construct(
        $scope,
        $jsonKey,
        $sub = null
    ) {
        if (is_string($jsonKey)) {
            if (!file_exists($jsonKey)) {
                throw new \InvalidArgumentException('file does not exist');
            }
            $jsonKeyStream = file_get_contents($jsonKey);
            if (!$jsonKey = json_decode($jsonKeyStream, true)) {
                throw new \LogicException('invalid json for auth config');
            }
        }
        if (!array_key_exists('client_email', $jsonKey)) {
            throw new \InvalidArgumentException(
                'json key is missing the client_email field');
        }
        if (!array_key_exists('private_key', $jsonKey)) {
            throw new \InvalidArgumentException(
                'json key is missing the private_key field');
        }
        $this->auth = new OAuth2([
            'audience' => self::TOKEN_CREDENTIAL_URI,
            'issuer' => $jsonKey['client_email'],
            'scope' => $scope,
            'signingAlgorithm' => 'RS256',
            'signingKey' => $jsonKey['private_key'],
            'sub' => $sub,
            'tokenCredentialUri' => self::TOKEN_CREDENTIAL_URI,
        ]);
    }

    /**
     * @param callable $httpHandler
     *
     * @return array
     */
    public function fetchAuthToken(callable $httpHandler = null)
    {
        return $this->auth->fetchAuthToken($httpHandler);
    }

    /**
     * @return string
     */
    public function getCacheKey()
    {
        $key = $this->auth->getIssuer() . ':' . $this->auth->getCacheKey();
        if ($sub = $this->auth->getSub()) {
            $key .= ':' . $sub;
        }

        return $key;
    }

    /**
     * @return array
     */
    public function getLastReceivedToken()
    {
        return $this->auth->getLastReceivedToken();
    }

    /**
     * Updates metadata with the authorization token.
     *
     * @param array $metadata metadata hashmap
     * @param string $authUri optional auth uri
     * @param callable $httpHandler callback which delivers psr7 request
     *
     * @return array updated metadata hashmap
     */
    public function updateMetadata(
        $metadata,
        $authUri = null,
        callable $httpHandler = null
    ) {
        // scope exists. use oauth implementation
        $scope = $this->auth->getScope();
        if (!is_null($scope)) {
            return parent::updateMetadata($metadata, $authUri, $httpHandler);
        }

        // no scope found. create jwt with the auth uri
        $credJson = array(
            'private_key' => $this->auth->getSigningKey(),
            'client_email' => $this->auth->getIssuer(),
        );
        $jwtCreds = new ServiceAccountJwtAccessCredentials($credJson);

        return $jwtCreds->updateMetadata($metadata, $authUri, $httpHandler);
    }

    /**
     * @param string $sub an email address account to impersonate, in situations when
     *   the service account has been delegated domain wide access.
     */
    public function setSub($sub)
    {
        $this->auth->setSub($sub);
    }
}
vendor/google/auth/src/Credentials/AppIdentityCredentials.php000064400000011366151327705670020447 0ustar00<?php
/*
 * Copyright 2015 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

namespace WPvividGoogle\Auth\Credentials;

/*
 * The AppIdentityService class is automatically defined on App Engine,
 * so including this dependency is not necessary, and will result in a
 * PHP fatal error in the App Engine environment.
 */
use google\appengine\api\app_identity\AppIdentityService;
use WPvividGoogle\Auth\CredentialsLoader;

/**
 * AppIdentityCredentials supports authorization on Google App Engine.
 *
 * It can be used to authorize requests using the AuthTokenMiddleware or
 * AuthTokenSubscriber, but will only succeed if being run on App Engine:
 *
 *   use Google\Auth\Credentials\AppIdentityCredentials;
 *   use Google\Auth\Middleware\AuthTokenMiddleware;
 *   use GuzzleHttp\Client;
 *   use GuzzleHttp\HandlerStack;
 *
 *   $gae = new AppIdentityCredentials('https://www.googleapis.com/auth/books');
 *   $middleware = new AuthTokenMiddleware($gae);
 *   $stack = HandlerStack::create();
 *   $stack->push($middleware);
 *
 *   $client = new Client([
 *       'handler' => $stack,
 *       'base_uri' => 'https://www.googleapis.com/books/v1',
 *       'auth' => 'google_auth'
 *   ]);
 *
 *   $res = $client->get('volumes?q=Henry+David+Thoreau&country=US');
 */
class AppIdentityCredentials extends CredentialsLoader
{
    /**
     * Result of fetchAuthToken.
     *
     * @array
     */
    protected $lastReceivedToken;

    /**
     * Array of OAuth2 scopes to be requested.
     */
    private $scope;

    public function __construct($scope = array())
    {
        $this->scope = $scope;
    }

    /**
     * Determines if this an App Engine instance, by accessing the
     * SERVER_SOFTWARE environment variable (prod) or the APPENGINE_RUNTIME
     * environment variable (dev).
     *
     * @return true if this an App Engine Instance, false otherwise
     */
    public static function onAppEngine()
    {
        $appEngineProduction = isset($_SERVER['SERVER_SOFTWARE']) &&
            0 === strpos($_SERVER['SERVER_SOFTWARE'], 'Google App Engine');
        if ($appEngineProduction) {
            return true;
        }
        $appEngineDevAppServer = isset($_SERVER['APPENGINE_RUNTIME']) &&
            $_SERVER['APPENGINE_RUNTIME'] == 'php';
        if ($appEngineDevAppServer) {
            return true;
        }
        return false;
    }

    /**
     * Implements FetchAuthTokenInterface#fetchAuthToken.
     *
     * Fetches the auth tokens using the AppIdentityService if available.
     * As the AppIdentityService uses protobufs to fetch the access token,
     * the GuzzleHttp\ClientInterface instance passed in will not be used.
     *
     * @param callable $httpHandler callback which delivers psr7 request
     *
     * @return array the auth metadata:
     *  array(2) {
     *   ["access_token"]=>
     *   string(3) "xyz"
     *   ["expiration_time"]=>
     *   string(10) "1444339905"
     *  }
     *
     * @throws \Exception
     */
    public function fetchAuthToken(callable $httpHandler = null)
    {
        if (!self::onAppEngine()) {
            return array();
        }

        if (!class_exists('google\appengine\api\app_identity\AppIdentityService')) {
            throw new \Exception(
                'This class must be run in App Engine, or you must include the AppIdentityService '
                . 'mock class defined in tests/mocks/AppIdentityService.php'
            );
        }

        // AppIdentityService expects an array when multiple scopes are supplied
        $scope = is_array($this->scope) ? $this->scope : explode(' ', $this->scope);

        $token = AppIdentityService::getAccessToken($scope);
        $this->lastReceivedToken = $token;

        return $token;
    }

    /**
     * @return array|null
     */
    public function getLastReceivedToken()
    {
        if ($this->lastReceivedToken) {
            return [
                'access_token' => $this->lastReceivedToken['access_token'],
                'expires_at' => $this->lastReceivedToken['expiration_time'],
            ];
        }

        return null;
    }

    /**
     * Caching is handled by the underlying AppIdentityService, return empty string
     * to prevent caching.
     *
     * @return string
     */
    public function getCacheKey()
    {
        return '';
    }
}
vendor/google/auth/src/Credentials/IAMCredentials.php000064400000004650151327705670016621 0ustar00<?php
/*
 * Copyright 2015 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

namespace WPvividGoogle\Auth\Credentials;

/**
 * Authenticates requests using IAM credentials.
 */
class IAMCredentials
{
    const SELECTOR_KEY = 'x-goog-iam-authority-selector';
    const TOKEN_KEY = 'x-goog-iam-authorization-token';

    /**
     * @var string
     */
    private $selector;

    /**
     * @var string
     */
    private $token;

    /**
     * @param $selector string the IAM selector
     * @param $token string the IAM token
     */
    public function __construct($selector, $token)
    {
        if (!is_string($selector)) {
            throw new \InvalidArgumentException(
                'selector must be a string');
        }
        if (!is_string($token)) {
            throw new \InvalidArgumentException(
                'token must be a string');
        }

        $this->selector = $selector;
        $this->token = $token;
    }

    /**
     * export a callback function which updates runtime metadata.
     *
     * @return array updateMetadata function
     */
    public function getUpdateMetadataFunc()
    {
        return array($this, 'updateMetadata');
    }

    /**
     * Updates metadata with the appropriate header metadata.
     *
     * @param array $metadata metadata hashmap
     * @param string $unusedAuthUri optional auth uri
     * @param callable $httpHandler callback which delivers psr7 request
     *        Note: this param is unused here, only included here for
     *        consistency with other credentials class
     *
     * @return array updated metadata hashmap
     */
    public function updateMetadata(
        $metadata,
        $unusedAuthUri = null,
        callable $httpHandler = null
    ) {
        $metadata_copy = $metadata;
        $metadata_copy[self::SELECTOR_KEY] = $this->selector;
        $metadata_copy[self::TOKEN_KEY] = $this->token;

        return $metadata_copy;
    }
}
vendor/google/auth/src/Credentials/InsecureCredentials.php000064400000003337151327705670017771 0ustar00<?php
/*
 * Copyright 2018 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

namespace WPvividGoogle\Auth\Credentials;

use WPvividGoogle\Auth\FetchAuthTokenInterface;

/**
 * Provides a set of credentials that will always return an empty access token.
 * This is useful for APIs which do not require authentication, for local
 * service emulators, and for testing.
 */
class InsecureCredentials implements FetchAuthTokenInterface
{
    /**
     * @var array
     */
    private $token = [
        'access_token' => ''
    ];

    /**
     * Fetches the auth token. In this case it returns an empty string.
     *
     * @param callable $httpHandler
     * @return array
     */
    public function fetchAuthToken(callable $httpHandler = null)
    {
        return $this->token;
    }

    /**
     * Returns the cache key. In this case it returns a null value, disabling
     * caching.
     *
     * @return string|null
     */
    public function getCacheKey()
    {
        return null;
    }

    /**
     * Fetches the last received token. In this case, it returns the same empty string
     * auth token.
     *
     * @return array
     */
    public function getLastReceivedToken()
    {
        return $this->token;
    }
}
vendor/google/auth/src/Credentials/GCECredentials.php000064400000016430151327705670016610 0ustar00<?php
/*
 * Copyright 2015 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

namespace WPvividGoogle\Auth\Credentials;

use WPvividGoogle\Auth\CredentialsLoader;
use WPvividGoogle\Auth\HttpHandler\HttpHandlerFactory;
use WPvividGuzzleHttp\Exception\ClientException;
use WPvividGuzzleHttp\Exception\RequestException;
use WPvividGuzzleHttp\Exception\ServerException;
use WPvividGuzzleHttp\Psr7\Request;

/**
 * GCECredentials supports authorization on Google Compute Engine.
 *
 * It can be used to authorize requests using the AuthTokenMiddleware, but will
 * only succeed if being run on GCE:
 *
 *   use Google\Auth\Credentials\GCECredentials;
 *   use Google\Auth\Middleware\AuthTokenMiddleware;
 *   use GuzzleHttp\Client;
 *   use GuzzleHttp\HandlerStack;
 *
 *   $gce = new GCECredentials();
 *   $middleware = new AuthTokenMiddleware($gce);
 *   $stack = HandlerStack::create();
 *   $stack->push($middleware);
 *
 *   $client = new Client([
 *      'handler' => $stack,
 *      'base_uri' => 'https://www.googleapis.com/taskqueue/v1beta2/projects/',
 *      'auth' => 'google_auth'
 *   ]);
 *
 *   $res = $client->get('myproject/taskqueues/myqueue');
 */
class GCECredentials extends CredentialsLoader
{
    const cacheKey = 'GOOGLE_AUTH_PHP_GCE';
    /**
     * The metadata IP address on appengine instances.
     *
     * The IP is used instead of the domain 'metadata' to avoid slow responses
     * when not on Compute Engine.
     */
    const METADATA_IP = '169.254.169.254';

    /**
     * The metadata path of the default token.
     */
    const TOKEN_URI_PATH = 'v1/instance/service-accounts/default/token';

    /**
     * The header whose presence indicates GCE presence.
     */
    const FLAVOR_HEADER = 'Metadata-Flavor';

    /**
     * Note: the explicit `timeout` and `tries` below is a workaround. The underlying
     * issue is that resolving an unknown host on some networks will take
     * 20-30 seconds; making this timeout short fixes the issue, but
     * could lead to false negatives in the event that we are on GCE, but
     * the metadata resolution was particularly slow. The latter case is
     * "unlikely" since the expected 4-nines time is about 0.5 seconds.
     * This allows us to limit the total ping maximum timeout to 1.5 seconds
     * for developer desktop scenarios.
     */
    const MAX_COMPUTE_PING_TRIES = 3;
    const COMPUTE_PING_CONNECTION_TIMEOUT_S = 0.5;

    /**
     * Flag used to ensure that the onGCE test is only done once;.
     *
     * @var bool
     */
    private $hasCheckedOnGce = false;

    /**
     * Flag that stores the value of the onGCE check.
     *
     * @var bool
     */
    private $isOnGce = false;

    /**
     * Result of fetchAuthToken.
     */
    protected $lastReceivedToken;

    /**
     * The full uri for accessing the default token.
     *
     * @return string
     */
    public static function getTokenUri()
    {
        $base = 'http://' . self::METADATA_IP . '/computeMetadata/';

        return $base . self::TOKEN_URI_PATH;
    }

    /**
     * Determines if this an App Engine Flexible instance, by accessing the
     * GAE_INSTANCE environment variable.
     *
     * @return true if this an App Engine Flexible Instance, false otherwise
     */
    public static function onAppEngineFlexible()
    {
        return substr(getenv('GAE_INSTANCE'), 0, 4) === 'aef-';
    }

    /**
     * Determines if this a GCE instance, by accessing the expected metadata
     * host.
     * If $httpHandler is not specified a the default HttpHandler is used.
     *
     * @param callable $httpHandler callback which delivers psr7 request
     *
     * @return true if this a GCEInstance false otherwise
     */
    public static function onGce(callable $httpHandler = null)
    {
        if (is_null($httpHandler)) {
            $httpHandler = HttpHandlerFactory::build();
        }
        $checkUri = 'http://' . self::METADATA_IP;
        for ($i = 1; $i <= self::MAX_COMPUTE_PING_TRIES; $i++) {
            try {
                // Comment from: oauth2client/client.py
                //
                // Note: the explicit `timeout` below is a workaround. The underlying
                // issue is that resolving an unknown host on some networks will take
                // 20-30 seconds; making this timeout short fixes the issue, but
                // could lead to false negatives in the event that we are on GCE, but
                // the metadata resolution was particularly slow. The latter case is
                // "unlikely".
                $resp = $httpHandler(
                    new Request('GET', $checkUri),
                    ['timeout' => self::COMPUTE_PING_CONNECTION_TIMEOUT_S]
                );

                return $resp->getHeaderLine(self::FLAVOR_HEADER) == 'Google';
            } catch (ClientException $e) {
            } catch (ServerException $e) {
            } catch (RequestException $e) {
            }
            $httpHandler = HttpHandlerFactory::build();
        }
        return false;
    }

    /**
     * Implements FetchAuthTokenInterface#fetchAuthToken.
     *
     * Fetches the auth tokens from the GCE metadata host if it is available.
     * If $httpHandler is not specified a the default HttpHandler is used.
     *
     * @param callable $httpHandler callback which delivers psr7 request
     *
     * @return array the response
     *
     * @throws \Exception
     */
    public function fetchAuthToken(callable $httpHandler = null)
    {
        if (is_null($httpHandler)) {
            $httpHandler = HttpHandlerFactory::build();
        }
        if (!$this->hasCheckedOnGce) {
            $this->isOnGce = self::onGce($httpHandler);
        }
        if (!$this->isOnGce) {
            return array();  // return an empty array with no access token
        }
        $resp = $httpHandler(
            new Request(
                'GET',
                self::getTokenUri(),
                [self::FLAVOR_HEADER => 'Google']
            )
        );
        $body = (string)$resp->getBody();

        // Assume it's JSON; if it's not throw an exception
        if (null === $json = json_decode($body, true)) {
            throw new \Exception('Invalid JSON response');
        }

        // store this so we can retrieve it later
        $this->lastReceivedToken = $json;
        $this->lastReceivedToken['expires_at'] = time() + $json['expires_in'];

        return $json;
    }

    /**
     * @return string
     */
    public function getCacheKey()
    {
        return self::cacheKey;
    }

    /**
     * @return array|null
     */
    public function getLastReceivedToken()
    {
        if ($this->lastReceivedToken) {
            return [
                'access_token' => $this->lastReceivedToken['access_token'],
                'expires_at' => $this->lastReceivedToken['expires_at'],
            ];
        }

        return null;
    }
}
vendor/google/auth/src/Credentials/ServiceAccountJwtAccessCredentials.php000064400000007434151327705670022742 0ustar00<?php
/*
 * Copyright 2015 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

namespace WPvividGoogle\Auth\Credentials;

use WPvividGoogle\Auth\CredentialsLoader;
use WPvividGoogle\Auth\OAuth2;

/**
 * Authenticates requests using Google's Service Account credentials via
 * JWT Access.
 *
 * This class allows authorizing requests for service accounts directly
 * from credentials from a json key file downloaded from the developer
 * console (via 'Generate new Json Key').  It is not part of any OAuth2
 * flow, rather it creates a JWT and sends that as a credential.
 */
class ServiceAccountJwtAccessCredentials extends CredentialsLoader
{
    /**
     * The OAuth2 instance used to conduct authorization.
     *
     * @var OAuth2
     */
    protected $auth;

    /**
     * Create a new ServiceAccountJwtAccessCredentials.
     *
     * @param string|array $jsonKey JSON credential file path or JSON credentials
     *   as an associative array
     */
    public function __construct($jsonKey)
    {
        if (is_string($jsonKey)) {
            if (!file_exists($jsonKey)) {
                throw new \InvalidArgumentException('file does not exist');
            }
            $jsonKeyStream = file_get_contents($jsonKey);
            if (!$jsonKey = json_decode($jsonKeyStream, true)) {
                throw new \LogicException('invalid json for auth config');
            }
        }
        if (!array_key_exists('client_email', $jsonKey)) {
            throw new \InvalidArgumentException(
                'json key is missing the client_email field');
        }
        if (!array_key_exists('private_key', $jsonKey)) {
            throw new \InvalidArgumentException(
                'json key is missing the private_key field');
        }
        $this->auth = new OAuth2([
            'issuer' => $jsonKey['client_email'],
            'sub' => $jsonKey['client_email'],
            'signingAlgorithm' => 'RS256',
            'signingKey' => $jsonKey['private_key'],
        ]);
    }

    /**
     * Updates metadata with the authorization token.
     *
     * @param array $metadata metadata hashmap
     * @param string $authUri optional auth uri
     * @param callable $httpHandler callback which delivers psr7 request
     *
     * @return array updated metadata hashmap
     */
    public function updateMetadata(
        $metadata,
        $authUri = null,
        callable $httpHandler = null
    ) {
        if (empty($authUri)) {
            return $metadata;
        }

        $this->auth->setAudience($authUri);

        return parent::updateMetadata($metadata, $authUri, $httpHandler);
    }

    /**
     * Implements FetchAuthTokenInterface#fetchAuthToken.
     *
     * @param callable $httpHandler
     *
     * @return array|void
     */
    public function fetchAuthToken(callable $httpHandler = null)
    {
        $audience = $this->auth->getAudience();
        if (empty($audience)) {
            return null;
        }

        $access_token = $this->auth->toJwt();

        return array('access_token' => $access_token);
    }

    /**
     * @return string
     */
    public function getCacheKey()
    {
        return $this->auth->getCacheKey();
    }

    /**
     * @return array
     */
    public function getLastReceivedToken()
    {
        return $this->auth->getLastReceivedToken();
    }
}
vendor/google/auth/src/Credentials/UserRefreshCredentials.php000064400000010713151327705670020445 0ustar00<?php
/*
 * Copyright 2015 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

namespace WPvividGoogle\Auth\Credentials;

use WPvividGoogle\Auth\CredentialsLoader;
use WPvividGoogle\Auth\OAuth2;

/**
 * Authenticates requests using User Refresh credentials.
 *
 * This class allows authorizing requests from user refresh tokens.
 *
 * This the end of the result of a 3LO flow.  E.g, the end result of
 * 'gcloud auth login' saves a file with these contents in well known
 * location
 *
 * @see [Application Default Credentials](http://goo.gl/mkAHpZ)
 */
class UserRefreshCredentials extends CredentialsLoader
{
    const CLOUD_SDK_CLIENT_ID =
        '764086051850-6qr4p6gpi6hn506pt8ejuq83di341hur.apps.googleusercontent.com';

    const SUPPRESS_CLOUD_SDK_CREDS_WARNING_ENV = 'SUPPRESS_GCLOUD_CREDS_WARNING';

    /**
     * The OAuth2 instance used to conduct authorization.
     *
     * @var OAuth2
     */
    protected $auth;

    /**
     * Create a new UserRefreshCredentials.
     *
     * @param string|array $scope the scope of the access request, expressed
     *   either as an Array or as a space-delimited String.
     * @param string|array $jsonKey JSON credential file path or JSON credentials
     *   as an associative array
     */
    public function __construct(
        $scope,
        $jsonKey
    ) {
        if (is_string($jsonKey)) {
            if (!file_exists($jsonKey)) {
                throw new \InvalidArgumentException('file does not exist');
            }
            $jsonKeyStream = file_get_contents($jsonKey);
            if (!$jsonKey = json_decode($jsonKeyStream, true)) {
                throw new \LogicException('invalid json for auth config');
            }
        }
        if (!array_key_exists('client_id', $jsonKey)) {
            throw new \InvalidArgumentException(
                'json key is missing the client_id field');
        }
        if (!array_key_exists('client_secret', $jsonKey)) {
            throw new \InvalidArgumentException(
                'json key is missing the client_secret field');
        }
        if (!array_key_exists('refresh_token', $jsonKey)) {
            throw new \InvalidArgumentException(
                'json key is missing the refresh_token field');
        }
        $this->auth = new OAuth2([
            'clientId' => $jsonKey['client_id'],
            'clientSecret' => $jsonKey['client_secret'],
            'refresh_token' => $jsonKey['refresh_token'],
            'scope' => $scope,
            'tokenCredentialUri' => self::TOKEN_CREDENTIAL_URI,
        ]);
        if ($jsonKey['client_id'] === self::CLOUD_SDK_CLIENT_ID
            && getenv(self::SUPPRESS_CLOUD_SDK_CREDS_WARNING_ENV) !== 'true') {
            trigger_error(
                'Your application has authenticated using end user credentials '
                . 'from Google Cloud SDK. We recommend that most server '
                . 'applications use service accounts instead. If your '
                . 'application continues to use end user credentials '
                . 'from Cloud SDK, you might receive a "quota exceeded" '
                . 'or "API not enabled" error. For more information about '
                . 'service accounts, see '
                . 'https://cloud.google.com/docs/authentication/. '
                . 'To disable this warning, set '
                . self::SUPPRESS_CLOUD_SDK_CREDS_WARNING_ENV
                . ' environment variable to "true".',
                E_USER_WARNING);
        }
    }

    /**
     * @param callable $httpHandler
     *
     * @return array
     */
    public function fetchAuthToken(callable $httpHandler = null)
    {
        return $this->auth->fetchAuthToken($httpHandler);
    }

    /**
     * @return string
     */
    public function getCacheKey()
    {
        return $this->auth->getClientId() . ':' . $this->auth->getCacheKey();
    }

    /**
     * @return array
     */
    public function getLastReceivedToken()
    {
        return $this->auth->getLastReceivedToken();
    }
}
vendor/google/auth/src/FetchAuthTokenInterface.php000064400000003172151327705670016273 0ustar00<?php
/*
 * Copyright 2015 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

namespace WPvividGoogle\Auth;

/**
 * An interface implemented by objects that can fetch auth tokens.
 */
interface FetchAuthTokenInterface
{
    /**
     * Fetches the auth tokens based on the current state.
     *
     * @param callable $httpHandler callback which delivers psr7 request
     *
     * @return array a hash of auth tokens
     */
    public function fetchAuthToken(callable $httpHandler = null);

    /**
     * Obtains a key that can used to cache the results of #fetchAuthToken.
     *
     * If the value is empty, the auth token is not cached.
     *
     * @return string a key that may be used to cache the auth token.
     */
    public function getCacheKey();

    /**
     * Returns an associative array with the token and
     * expiration time.
     *
     * @return null|array {
     *      The last received access token.
     *
     * @var string $access_token The access token string.
     * @var int $expires_at The time the token expires as a UNIX timestamp.
     * }
     */
    public function getLastReceivedToken();
}
vendor/google/auth/src/HttpHandler/Guzzle6HttpHandler.php000064400000002225151327705670017515 0ustar00<?php

namespace WPvividGoogle\Auth\HttpHandler;

use WPvividGuzzleHttp\ClientInterface;
use WPvividPsr\Http\Message\RequestInterface;
use WPvividPsr\Http\Message\ResponseInterface;

class Guzzle6HttpHandler
{
    /**
     * @var ClientInterface
     */
    private $client;

    /**
     * @param ClientInterface $client
     */
    public function __construct(ClientInterface $client)
    {
        $this->client = $client;
    }

    /**
     * Accepts a PSR-7 request and an array of options and returns a PSR-7 response.
     *
     * @param RequestInterface $request
     * @param array $options
     *
     * @return ResponseInterface
     */
    public function __invoke(RequestInterface $request, array $options = [])
    {
        return $this->client->send($request, $options);
    }

    /**
     * Accepts a PSR-7 request and an array of options and returns a PromiseInterface
     *
     * @param RequestInterface $request
     * @param array $options
     *
     * @return \GuzzleHttp\Promise\Promise
     */
    public function async(RequestInterface $request, array $options = [])
    {
        return $this->client->sendAsync($request, $options);
    }
}
vendor/google/auth/src/HttpHandler/HttpHandlerFactory.php000064400000002645151327705670017564 0ustar00<?php
/**
 * Copyright 2015 Google Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
namespace WPvividGoogle\Auth\HttpHandler;

use WPvividGuzzleHttp\Client;
use WPvividGuzzleHttp\ClientInterface;

class HttpHandlerFactory
{
    /**
     * Builds out a default http handler for the installed version of guzzle.
     *
     * @param ClientInterface $client
     *
     * @return Guzzle5HttpHandler|Guzzle6HttpHandler
     *
     * @throws \Exception
     */
    public static function build(ClientInterface $client = null)
    {
        $version = ClientInterface::VERSION;
        $client = $client ?: new Client();

        switch ($version[0]) {
            case '5':
                return new Guzzle5HttpHandler($client);
            case '6':
                return new Guzzle6HttpHandler($client);
            default:
                throw new \Exception('Version not supported');
        }
    }
}
vendor/google/auth/src/HttpHandler/Guzzle5HttpHandler.php000064400000007530151327705670017520 0ustar00<?php
/**
 * Copyright 2015 Google Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
namespace WPvividGoogle\Auth\HttpHandler;

use Exception;
use WPvividGuzzleHttp\ClientInterface;
use WPvividGuzzleHttp\Message\ResponseInterface as Guzzle5ResponseInterface;
use WPvividGuzzleHttp\Promise\Promise;
use WPvividGuzzleHttp\Promise\RejectedPromise;
use WPvividGuzzleHttp\Psr7\Response;
use WPvividPsr\Http\Message\RequestInterface;
use WPvividPsr\Http\Message\ResponseInterface;

class Guzzle5HttpHandler
{
    /**
     * @var ClientInterface
     */
    private $client;

    /**
     * @param ClientInterface $client
     */
    public function __construct(ClientInterface $client)
    {
        $this->client = $client;
    }

    /**
     * Accepts a PSR-7 Request and an array of options and returns a PSR-7 response.
     *
     * @param RequestInterface $request
     * @param array $options
     *
     * @return ResponseInterface
     */
    public function __invoke(RequestInterface $request, array $options = [])
    {
        $response = $this->client->send(
            $this->createGuzzle5Request($request, $options)
        );

        return $this->createPsr7Response($response);
    }

    /**
     * Accepts a PSR-7 request and an array of options and returns a PromiseInterface
     *
     * @param RequestInterface $request
     * @param array $options
     *
     * @return Promise
     */
    public function async(RequestInterface $request, array $options = [])
    {
        if (!class_exists('WPvividGuzzleHttp\Promise\Promise')) {
            throw new Exception('Install guzzlehttp/promises to use async with Guzzle 5');
        }

        $futureResponse = $this->client->send(
            $this->createGuzzle5Request(
                $request,
                ['future' => true] + $options
            )
        );

        $promise = new Promise(
            function () use ($futureResponse) {
                try {
                    $futureResponse->wait();
                } catch (Exception $e) {
                    // The promise is already delivered when the exception is
                    // thrown, so don't rethrow it.
                }
            },
            [$futureResponse, 'cancel']
        );

        $futureResponse->then([$promise, 'resolve'], [$promise, 'reject']);

        return $promise->then(
            function (Guzzle5ResponseInterface $response) {
                // Adapt the Guzzle 5 Response to a PSR-7 Response.
                return $this->createPsr7Response($response);
            },
            function (Exception $e) {
                return new RejectedPromise($e);
            }
        );
    }

    private function createGuzzle5Request(RequestInterface $request, array $options)
    {
        return $this->client->createRequest(
            $request->getMethod(),
            $request->getUri(),
            array_merge_recursive([
                'headers' => $request->getHeaders(),
                'body' => $request->getBody(),
            ], $options)
        );
    }

    private function createPsr7Response(Guzzle5ResponseInterface $response)
    {
        return new Response(
            $response->getStatusCode(),
            $response->getHeaders() ?: [],
            $response->getBody(),
            $response->getProtocolVersion(),
            $response->getReasonPhrase()
        );
    }
}
vendor/google/auth/composer.json000064400000001321151327705670013012 0ustar00{
  "name": "google/auth",
  "type": "library",
  "description": "Google Auth Library for PHP",
  "keywords": ["google", "oauth2", "authentication"],
  "homepage": "http://github.com/google/google-auth-library-php",
  "license": "Apache-2.0",
  "require": {
    "php": ">=5.4",
    "firebase/php-jwt": "~2.0|~3.0|~4.0|~5.0",
    "guzzlehttp/guzzle": "~5.3.1|~6.0",
    "guzzlehttp/psr7": "^1.2",
    "psr/http-message": "^1.0",
    "psr/cache": "^1.0"
  },
  "require-dev": {
    "guzzlehttp/promises": "0.1.1|^1.3",
    "friendsofphp/php-cs-fixer": "^1.11",
    "phpunit/phpunit": "^4.8.36|^5.7",
    "sebastian/comparator": ">=1.2.3"
  },
  "autoload": {
    "psr-4": {
      "WPvividGoogle\\Auth\\": "src"
    }
  }
}
vendor/google/auth/.editorconfig000064400000000564151327705670012755 0ustar00# EditorConfig is awesome: http://EditorConfig.org

# top-most EditorConfig file
root = true
charset = utf-8

# Get rid of whitespace to avoid diffs with a bunch of EOL changes
trim_trailing_whitespace = true

# Unix-style newlines with a newline ending every file
[*]
end_of_line = lf
insert_final_newline = true

# PHP-Files
[*.php]
indent_style = space
indent_size = 4
vendor/google/auth/CODE_OF_CONDUCT.md000064400000003675151327705670013105 0ustar00# Contributor Code of Conduct

As contributors and maintainers of this project,
and in the interest of fostering an open and welcoming community,
we pledge to respect all people who contribute through reporting issues,
posting feature requests, updating documentation,
submitting pull requests or patches, and other activities.

We are committed to making participation in this project
a harassment-free experience for everyone,
regardless of level of experience, gender, gender identity and expression,
sexual orientation, disability, personal appearance,
body size, race, ethnicity, age, religion, or nationality.

Examples of unacceptable behavior by participants include:

* The use of sexualized language or imagery
* Personal attacks
* Trolling or insulting/derogatory comments
* Public or private harassment
* Publishing other's private information,
such as physical or electronic
addresses, without explicit permission
* Other unethical or unprofessional conduct.

Project maintainers have the right and responsibility to remove, edit, or reject
comments, commits, code, wiki edits, issues, and other contributions
that are not aligned to this Code of Conduct.
By adopting this Code of Conduct,
project maintainers commit themselves to fairly and consistently
applying these principles to every aspect of managing this project.
Project maintainers who do not follow or enforce the Code of Conduct
may be permanently removed from the project team.

This code of conduct applies both within project spaces and in public spaces
when an individual is representing the project or its community.

Instances of abusive, harassing, or otherwise unacceptable behavior
may be reported by opening an issue
or contacting one or more of the project maintainers.

This Code of Conduct is adapted from the [Contributor Covenant](http://contributor-covenant.org), version 1.2.0,
available at [http://contributor-covenant.org/version/1/2/0/](http://contributor-covenant.org/version/1/2/0/)
vendor/google/auth/COPYING000064400000026116151327705670011334 0ustar00
                                 Apache License
                           Version 2.0, January 2004
                        http://www.apache.org/licenses/

   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION

   1. Definitions.

      "License" shall mean the terms and conditions for use, reproduction,
      and distribution as defined by Sections 1 through 9 of this document.

      "Licensor" shall mean the copyright owner or entity authorized by
      the copyright owner that is granting the License.

      "Legal Entity" shall mean the union of the acting entity and all
      other entities that control, are controlled by, or are under common
      control with that entity. For the purposes of this definition,
      "control" means (i) the power, direct or indirect, to cause the
      direction or management of such entity, whether by contract or
      otherwise, or (ii) ownership of fifty percent (50%) or more of the
      outstanding shares, or (iii) beneficial ownership of such entity.

      "You" (or "Your") shall mean an individual or Legal Entity
      exercising permissions granted by this License.

      "Source" form shall mean the preferred form for making modifications,
      including but not limited to software source code, documentation
      source, and configuration files.

      "Object" form shall mean any form resulting from mechanical
      transformation or translation of a Source form, including but
      not limited to compiled object code, generated documentation,
      and conversions to other media types.

      "Work" shall mean the work of authorship, whether in Source or
      Object form, made available under the License, as indicated by a
      copyright notice that is included in or attached to the work
      (an example is provided in the Appendix below).

      "Derivative Works" shall mean any work, whether in Source or Object
      form, that is based on (or derived from) the Work and for which the
      editorial revisions, annotations, elaborations, or other modifications
      represent, as a whole, an original work of authorship. For the purposes
      of this License, Derivative Works shall not include works that remain
      separable from, or merely link (or bind by name) to the interfaces of,
      the Work and Derivative Works thereof.

      "Contribution" shall mean any work of authorship, including
      the original version of the Work and any modifications or additions
      to that Work or Derivative Works thereof, that is intentionally
      submitted to Licensor for inclusion in the Work by the copyright owner
      or by an individual or Legal Entity authorized to submit on behalf of
      the copyright owner. For the purposes of this definition, "submitted"
      means any form of electronic, verbal, or written communication sent
      to the Licensor or its representatives, including but not limited to
      communication on electronic mailing lists, source code control systems,
      and issue tracking systems that are managed by, or on behalf of, the
      Licensor for the purpose of discussing and improving the Work, but
      excluding communication that is conspicuously marked or otherwise
      designated in writing by the copyright owner as "Not a Contribution."

      "Contributor" shall mean Licensor and any individual or Legal Entity
      on behalf of whom a Contribution has been received by Licensor and
      subsequently incorporated within the Work.

   2. Grant of Copyright License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      copyright license to reproduce, prepare Derivative Works of,
      publicly display, publicly perform, sublicense, and distribute the
      Work and such Derivative Works in Source or Object form.

   3. Grant of Patent License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      (except as stated in this section) patent license to make, have made,
      use, offer to sell, sell, import, and otherwise transfer the Work,
      where such license applies only to those patent claims licensable
      by such Contributor that are necessarily infringed by their
      Contribution(s) alone or by combination of their Contribution(s)
      with the Work to which such Contribution(s) was submitted. If You
      institute patent litigation against any entity (including a
      cross-claim or counterclaim in a lawsuit) alleging that the Work
      or a Contribution incorporated within the Work constitutes direct
      or contributory patent infringement, then any patent licenses
      granted to You under this License for that Work shall terminate
      as of the date such litigation is filed.

   4. Redistribution. You may reproduce and distribute copies of the
      Work or Derivative Works thereof in any medium, with or without
      modifications, and in Source or Object form, provided that You
      meet the following conditions:

      (a) You must give any other recipients of the Work or
          Derivative Works a copy of this License; and

      (b) You must cause any modified files to carry prominent notices
          stating that You changed the files; and

      (c) You must retain, in the Source form of any Derivative Works
          that You distribute, all copyright, patent, trademark, and
          attribution notices from the Source form of the Work,
          excluding those notices that do not pertain to any part of
          the Derivative Works; and

      (d) If the Work includes a "NOTICE" text file as part of its
          distribution, then any Derivative Works that You distribute must
          include a readable copy of the attribution notices contained
          within such NOTICE file, excluding those notices that do not
          pertain to any part of the Derivative Works, in at least one
          of the following places: within a NOTICE text file distributed
          as part of the Derivative Works; within the Source form or
          documentation, if provided along with the Derivative Works; or,
          within a display generated by the Derivative Works, if and
          wherever such third-party notices normally appear. The contents
          of the NOTICE file are for informational purposes only and
          do not modify the License. You may add Your own attribution
          notices within Derivative Works that You distribute, alongside
          or as an addendum to the NOTICE text from the Work, provided
          that such additional attribution notices cannot be construed
          as modifying the License.

      You may add Your own copyright statement to Your modifications and
      may provide additional or different license terms and conditions
      for use, reproduction, or distribution of Your modifications, or
      for any such Derivative Works as a whole, provided Your use,
      reproduction, and distribution of the Work otherwise complies with
      the conditions stated in this License.

   5. Submission of Contributions. Unless You explicitly state otherwise,
      any Contribution intentionally submitted for inclusion in the Work
      by You to the Licensor shall be under the terms and conditions of
      this License, without any additional terms or conditions.
      Notwithstanding the above, nothing herein shall supersede or modify
      the terms of any separate license agreement you may have executed
      with Licensor regarding such Contributions.

   6. Trademarks. This License does not grant permission to use the trade
      names, trademarks, service marks, or product names of the Licensor,
      except as required for reasonable and customary use in describing the
      origin of the Work and reproducing the content of the NOTICE file.

   7. Disclaimer of Warranty. Unless required by applicable law or
      agreed to in writing, Licensor provides the Work (and each
      Contributor provides its Contributions) on an "AS IS" BASIS,
      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
      implied, including, without limitation, any warranties or conditions
      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
      PARTICULAR PURPOSE. You are solely responsible for determining the
      appropriateness of using or redistributing the Work and assume any
      risks associated with Your exercise of permissions under this License.

   8. Limitation of Liability. In no event and under no legal theory,
      whether in tort (including negligence), contract, or otherwise,
      unless required by applicable law (such as deliberate and grossly
      negligent acts) or agreed to in writing, shall any Contributor be
      liable to You for damages, including any direct, indirect, special,
      incidental, or consequential damages of any character arising as a
      result of this License or out of the use or inability to use the
      Work (including but not limited to damages for loss of goodwill,
      work stoppage, computer failure or malfunction, or any and all
      other commercial damages or losses), even if such Contributor
      has been advised of the possibility of such damages.

   9. Accepting Warranty or Additional Liability. While redistributing
      the Work or Derivative Works thereof, You may choose to offer,
      and charge a fee for, acceptance of support, warranty, indemnity,
      or other liability obligations and/or rights consistent with this
      License. However, in accepting such obligations, You may act only
      on Your own behalf and on Your sole responsibility, not on behalf
      of any other Contributor, and only if You agree to indemnify,
      defend, and hold each Contributor harmless for any liability
      incurred by, or claims asserted against, such Contributor by reason
      of your accepting any such warranty or additional liability.

   END OF TERMS AND CONDITIONS

   APPENDIX: How to apply the Apache License to your work.

      To apply the Apache License to your work, attach the following
      boilerplate notice, with the fields enclosed by brackets "[]"
      replaced with your own identifying information. (Don't include
      the brackets!)  The text should be enclosed in the appropriate
      comment syntax for the file format. We also recommend that a
      file or class name and description of purpose be included on the
      same "printed page" as the copyright notice for easier
      identification within third-party archives.

   Copyright 2015 Google Inc.

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
vendor/google/auth/.php_cs000064400000003343151327705670011553 0ustar00<?php
/**
 * This file represents the configuration for Code Sniffing PSR-2-related
 * automatic checks of coding guidelines
 * Install @fabpot's great php-cs-fixer tool via
 *
 *  $ composer global require fabpot/php-cs-fixer
 *
 * And then simply run
 *
 *  $ php-cs-fixer fix --config-file .php_cs
 *
 * inside the root directory.
 *
 * 	 http://www.php-fig.org/psr/psr-2/
 * 	 http://cs.sensiolabs.org
 */

if (PHP_SAPI !== 'cli') {
    die('This script supports command line usage only. Please check your command.');
}
// Define in which folders to search and which folders to exclude
// Exclude some directories that are excluded by Git anyways to speed up the sniffing
$finder = Symfony\CS\Finder\DefaultFinder::create()
    ->exclude('vendor')
    ->in(__DIR__);

// Return a Code Sniffing configuration using
// all sniffers needed for PSR-2
// and additionally:
//  - Remove leading slashes in use clauses.
//  - PHP single-line arrays should not have trailing comma.
//  - Single-line whitespace before closing semicolon are prohibited.
//  - Remove unused use statements in the PHP source code
//  - Ensure Concatenation to have at least one whitespace around
//  - Remove trailing whitespace at the end of blank lines.
return Symfony\CS\Config\Config::create()
    ->level(Symfony\CS\FixerInterface::PSR2_LEVEL)
    ->fixers([
        'remove_leading_slash_use',
        'single_array_no_trailing_comma',
        'spaces_before_semicolon',
        'unused_use',
        'concat_with_spaces',
        'whitespacy_lines',
        'ordered_use',
        'single_quote',
        'duplicate_semicolon',
        'extra_empty_lines',
        'phpdoc_no_package',
        'phpdoc_scalar',
        'no_empty_lines_after_phpdocs'
    ])
    ->finder($finder);
vendor/google/auth/README.md000064400000011714151327705670011556 0ustar00# Google Auth Library for PHP

<dl>
  <dt>Homepage</dt><dd><a href="http://www.github.com/google/google-auth-library-php">http://www.github.com/google/google-auth-library-php</a></dd>
  <dt>Authors</dt>
    <dd><a href="mailto:temiola@google.com">Tim Emiola</a></dd>
    <dd><a href="mailto:stanleycheung@google.com">Stanley Cheung</a></dd>
    <dd><a href="mailto:betterbrent@google.com">Brent Shaffer</a></dd>
  <dt>Copyright</dt><dd>Copyright © 2015 Google, Inc.</dd>
  <dt>License</dt><dd>Apache 2.0</dd>
</dl>

## Description

This is Google's officially supported PHP client library for using OAuth 2.0
authorization and authentication with Google APIs.

### Installing via Composer

The recommended way to install the google auth library is through
[Composer](http://getcomposer.org).

```bash
# Install Composer
curl -sS https://getcomposer.org/installer | php
```

Next, run the Composer command to install the latest stable version:

```bash
composer.phar require google/auth
```

## Application Default Credentials

This library provides an implementation of
[application default credentials][application default credentials] for PHP.

The Application Default Credentials provide a simple way to get authorization
credentials for use in calling Google APIs.

They are best suited for cases when the call needs to have the same identity
and authorization level for the application independent of the user. This is
the recommended approach to authorize calls to Cloud APIs, particularly when
you're building an application that uses Google Compute Engine.

#### Download your Service Account Credentials JSON file

To use `Application Default Credentials`, You first need to download a set of
JSON credentials for your project. Go to **APIs & Auth** > **Credentials** in
the [Google Developers Console][developer console] and select
**Service account** from the **Add credentials** dropdown.

> This file is your *only copy* of these credentials. It should never be
> committed with your source code, and should be stored securely.

Once downloaded, store the path to this file in the
`GOOGLE_APPLICATION_CREDENTIALS` environment variable.

```php
putenv('GOOGLE_APPLICATION_CREDENTIALS=/path/to/my/credentials.json');
```

> PHP's `putenv` function is just one way to set an environment variable.
> Consider using `.htaccess` or apache configuration files as well.

#### Enable the API you want to use

Before making your API call, you must be sure the API you're calling has been
enabled. Go to **APIs & Auth** > **APIs** in the
[Google Developers Console][developer console] and enable the APIs you'd like to
call. For the example below, you must enable the `Drive API`.

#### Call the APIs

As long as you update the environment variable below to point to *your* JSON
credentials file, the following code should output a list of your Drive files.

```php
use Google\Auth\ApplicationDefaultCredentials;
use GuzzleHttp\Client;
use GuzzleHttp\HandlerStack;

// specify the path to your application credentials
putenv('GOOGLE_APPLICATION_CREDENTIALS=/path/to/my/credentials.json');

// define the scopes for your API call
$scopes = ['https://www.googleapis.com/auth/drive.readonly'];

// create middleware
$middleware = ApplicationDefaultCredentials::getMiddleware($scopes);
$stack = HandlerStack::create();
$stack->push($middleware);

// create the HTTP client
$client = new Client([
  'handler' => $stack,
  'base_uri' => 'https://www.googleapis.com',
  'auth' => 'google_auth'  // authorize all requests
]);

// make the request
$response = $client->get('drive/v2/files');

// show the result!
print_r((string) $response->getBody());
```

##### Guzzle 5 Compatibility

If you are using [Guzzle 5][Guzzle 5], replace the `create middleware` and
`create the HTTP Client` steps with the following:

```php
// create the HTTP client
$client = new Client([
  'base_url' => 'https://www.googleapis.com',
  'auth' => 'google_auth'  // authorize all requests
]);

// create subscriber
$subscriber = ApplicationDefaultCredentials::getSubscriber($scopes);
$client->getEmitter()->attach($subscriber);

```

## License

This library is licensed under Apache 2.0. Full license text is
available in [COPYING][copying].

## Contributing

See [CONTRIBUTING][contributing].

## Support

Please
[report bugs at the project on Github](https://github.com/google/google-auth-library-php/issues). Don't
hesitate to
[ask questions](http://stackoverflow.com/questions/tagged/google-auth-library-php)
about the client or APIs on [StackOverflow](http://stackoverflow.com).

[google-apis-php-client]: https://github.com/google/google-api-php-client
[application default credentials]: https://developers.google.com/accounts/docs/application-default-credentials
[contributing]: https://github.com/google/google-auth-library-php/tree/master/CONTRIBUTING.md
[copying]: https://github.com/google/google-auth-library-php/tree/master/COPYING
[Guzzle]: https://github.com/guzzle/guzzle
[Guzzle 5]: http://docs.guzzlephp.org/en/5.3
[developer console]: https://console.developers.google.com
vendor/google/auth/CHANGELOG.md000064400000003510151327705670012103 0ustar00## 1.4.0 (09/17/2018)

### Changes

 * Add support for insecure credentials (#208)

## 1.3.3 (08/27/2018)

### Changes

 * Add retry and increase timeout for GCE credentials (#195)
 * [Docs] Fix spelling (#204)
 * Update token url (#206)

## 1.3.2 (07/23/2018)

### Changes

 * Only emits a warning for gcloud credentials (#202)

## 1.3.1 (07/19/2018)

### Changes

 * Added a warning for 3 legged OAuth credentials (#199)
 * [Code cleanup] Removed useless else after return (#193)

## 1.3.0 (06/04/2018)

### Changes

 * Fixes usage of deprecated env var for GAE Flex (#189)
 * fix - guzzlehttp/psr7 dependency version definition (#190)
 * Added SystemV shared memory based CacheItemPool (#191)

## 1.2.1 (24/01/2018)

### Changes

 * Fixes array merging bug in Guzzle5HttpHandler (#186)
 * Fixes constructor argument bug in Subscriber & Middleware (#184)

## 1.2.0 (6/12/2017)

### Changes

 * Adds async method to HTTP handlers (#176)
 * Misc bug fixes and improvements (#177, #175, #178)

## 1.1.0 (10/10/2017)

### Changes

 * Supports additional claims in JWT tokens (#171)
 * Adds makeHttpClient for creating authorized Guzzle clients (#162)
 * Misc bug fixes/improvements (#168, #161, #167, #170, #143)

## 1.0.1 (31/07/2017)

### Changes

* Adds support for Firebase 5.0 (#159)

## 1.0.0 (12/06/2017)

### Changes

* Adds hashing and shortening to enforce max key length ([@bshaffer])
* Fix for better PSR-6 compliance - verifies a hit before getting the cache item ([@bshaffer])
* README fixes ([@bshaffer])
* Change authorization header key to lowercase ([@stanley-cheung])

## 0.4.0 (23/04/2015)

### Changes

* Export callback function to update auth metadata ([@stanley-cheung][])
* Adds an implementation of User Refresh Token auth ([@stanley-cheung][])

[@bshaffer]: https://github.com/bshaffer
[@stanley-cheung]: https://github.com/stanley-cheung
vendor/google/auth/LICENSE000064400000024020151327705670011276 0ustar00Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/

TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION

1. Definitions.

"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.

"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.

"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.

"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.

"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.

"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.

"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).

"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.

"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."

"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.

2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.

3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.

4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:

(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and

(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and

(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and

(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.

You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.

5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.

6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.

7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.

8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.

9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.

END OF TERMS AND CONDITIONS

APPENDIX: How to apply the Apache License to your work.

To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.

Copyright [yyyy] [name of copyright owner]

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.


vendor/google/auth/CONTRIBUTING.md000064400000007107151327705670012531 0ustar00# How to become a contributor and submit your own code

## Contributor License Agreements

We'd love to accept your sample apps and patches! Before we can take them, we
have to jump a couple of legal hurdles.

Please fill out either the individual or corporate Contributor License Agreement
(CLA).

  * If you are an individual writing original source code and you're sure you
    own the intellectual property, then you'll need to sign an [individual CLA]
    (http://code.google.com/legal/individual-cla-v1.0.html).
  * If you work for a company that wants to allow you to contribute your work,
    then you'll need to sign a [corporate CLA]
    (http://code.google.com/legal/corporate-cla-v1.0.html).

Follow either of the two links above to access the appropriate CLA and
instructions for how to sign and return it. Once we receive it, we'll be able to
accept your pull requests.

## Issue reporting

* Check that the issue has not already been reported.
* Check that the issue has not already been fixed in the latest code
  (a.k.a. `master`).
* Be clear, concise and precise in your description of the problem.
* Open an issue with a descriptive title and a summary in grammatically correct,
    complete sentences.
* Include any relevant code to the issue summary.

## Pull requests

* Read [how to properly contribute to open source projects on Github][2].
* Fork the project.
* Use a topic/feature branch to easily amend a pull request later, if necessary.
* Write [good commit messages][3].
* Use the same coding conventions as the rest of the project.
* Commit and push until you are happy with your contribution.
* Make sure to add tests for it. This is important so I don't break it
  in a future version unintentionally.
* Add an entry to the [Changelog](CHANGELOG.md) accordingly. See [changelog entry format](#changelog-entry-format).
* Please try not to mess with the Rakefile, version, or history. If you want to
  have your own version, or is otherwise necessary, that is fine, but please
  isolate to its own commit so I can cherry-pick around it.
* Make sure the test suite is passing and the code you wrote doesn't produce
  phpunit or phplint offenses.
* [Squash related commits together][5].
* Open a [pull request][4] that relates to *only* one subject with a clear title
  and description in grammatically correct, complete sentences.

### Changelog entry format

Here are a few examples:

```
* ADC Support for User Refresh Tokens (@tbetbetbe[])
* [#16](https://github.com/google/google-auth-library-php/issues/16): ADC Support for User Refresh Tokens ([@tbetbetbe][])
```

* Mark it up in [Markdown syntax][6].
* The entry line should start with `* ` (an asterisk and a space).
* If the change has a related GitHub issue (e.g. a bug fix for a reported issue), put a link to the issue as `[#16](https://github.com/google/google-auth-library-php/issues/16): `.
* Describe the brief of the change. The sentence should end with a punctuation.
* At the end of the entry, add an implicit link to your GitHub user page as `([@username][])`.
* If this is your first contribution to google-auth-library-php project, add a link definition for the implicit link to the bottom of the changelog as `[@username]: https://github.com/username`.

[1]: https://github.com/google/google-auth-php-library/issues
[2]: http://gun.io/blog/how-to-github-fork-branch-and-pull-request
[3]: http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html
[4]: https://help.github.com/articles/using-pull-requests
[5]: http://gitready.com/advanced/2009/02/10/squashing-commits-with-rebase.html
[6]: http://daringfireball.net/projects/markdown/syntax
vendor/google/auth/autoload.php000064400000002127151327705670012616 0ustar00<?php
/*
 * Copyright 2014 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

function oauth2client_php_autoload($className)
{
    $classPath = explode('_', $className);
    if ($classPath[0] != 'Google') {
        return;
    }
    if (count($classPath) > 3) {
        // Maximum class file path depth in this project is 3.
    $classPath = array_slice($classPath, 0, 3);
    }
    $filePath = dirname(__FILE__) . '/src/' . implode('/', $classPath) . '.php';
    if (file_exists($filePath)) {
        require_once $filePath;
    }
}

spl_autoload_register('oauth2client_php_autoload');
vendor/google/auth/.gitignore000064400000000060151327705670012257 0ustar00*~
vendor
composer.lock

# IntelliJ
.idea
*.iml
vendor/google/auth/.travis.yml000064400000000672151327705670012411 0ustar00language: php

branches:
  only: [master]

sudo: false

php:
  - 5.4
  - 5.5
  - 5.6
  - 7.0
  - 7.1
  - 7.2

env:
  -
  - COMPOSER_ARGS="--prefer-lowest"
matrix:
  include:
    - php: "7.2"
      env: RUN_CS_FIXER=true

before_script:
  - composer update $COMPOSER_ARGS

script:
  - if [ "${RUN_CS_FIXER}" = "true" ]; then
      vendor/bin/php-cs-fixer fix --dry-run --diff --config-file=.php_cs .;
    else
      vendor/bin/phpunit;
    fi
vendor/google/apiclient-services/README.md000064400000001507151327705670014405 0ustar00Google PHP API Client Services
==============================

## Requirements

[Google API PHP Client](https://github.com/googleapis/google-api-php-client/releases)

## Usage in v2 of Google API PHP Client

This library is automatically updated daily with new API changes, and tagged weekly.
It is installed as part of the 
[Google API PHP Client](https://github.com/googleapis/google-api-php-client/releases)
library via Composer, which will pull down the most recent tag.

## Usage in v1

If you are currently using the [`v1-master`](https://github.com/googleapis/google-api-php-client/tree/v1-master)
branch of the client library, but want to use the latest API services, you can
do so by requiring this library directly into your project via the same composer command:

```sh
composer require google/apiclient-services:dev-master
```
vendor/google/apiclient-services/LICENSE000064400000024020151327705670014126 0ustar00Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/

TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION

1. Definitions.

"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.

"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.

"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.

"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.

"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.

"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.

"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).

"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.

"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."

"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.

2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.

3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.

4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:

(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and

(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and

(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and

(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.

You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.

5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.

6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.

7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.

8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.

9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.

END OF TERMS AND CONDITIONS

APPENDIX: How to apply the Apache License to your work.

To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.

Copyright [yyyy] [name of copyright owner]

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.


vendor/google/apiclient-services/composer.json000064400000000700151327705670015642 0ustar00{
    "name": "google/apiclient-services",
    "type": "library",
    "description": "Client library for Google APIs",
    "keywords": ["google"],
    "homepage": "http://developers.google.com/api-client-library/php",
    "license": "Apache-2.0",
    "require": {
        "php": ">=5.4"
    },
    "require-dev": {
        "phpunit/phpunit": "~4.8"
    },
    "autoload": {
        "psr-0": {
            "Google_Service_": "src"
        }
    }
}
vendor/google/apiclient-services/src/Google/Service/Drive/TeamDriveList.php000064400000002641151327705670023027 0ustar00<?php
/*
 * Copyright 2014 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

class Google_Service_Drive_TeamDriveList extends Google_Collection
{
  protected $collection_key = 'teamDrives';
  public $kind;
  public $nextPageToken;
  protected $teamDrivesType = 'Google_Service_Drive_TeamDrive';
  protected $teamDrivesDataType = 'array';

  public function setKind($kind)
  {
    $this->kind = $kind;
  }
  public function getKind()
  {
    return $this->kind;
  }
  public function setNextPageToken($nextPageToken)
  {
    $this->nextPageToken = $nextPageToken;
  }
  public function getNextPageToken()
  {
    return $this->nextPageToken;
  }
  /**
   * @param Google_Service_Drive_TeamDrive
   */
  public function setTeamDrives($teamDrives)
  {
    $this->teamDrives = $teamDrives;
  }
  /**
   * @return Google_Service_Drive_TeamDrive
   */
  public function getTeamDrives()
  {
    return $this->teamDrives;
  }
}
vendor/google/apiclient-services/src/Google/Service/Drive/TeamDriveBackgroundImageFile.php000064400000002473151327705670025741 0ustar00<?php
/*
 * Copyright 2014 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

class Google_Service_Drive_TeamDriveBackgroundImageFile extends Google_Model
{
  public $id;
  public $width;
  public $xCoordinate;
  public $yCoordinate;

  public function setId($id)
  {
    $this->id = $id;
  }
  public function getId()
  {
    return $this->id;
  }
  public function setWidth($width)
  {
    $this->width = $width;
  }
  public function getWidth()
  {
    return $this->width;
  }
  public function setXCoordinate($xCoordinate)
  {
    $this->xCoordinate = $xCoordinate;
  }
  public function getXCoordinate()
  {
    return $this->xCoordinate;
  }
  public function setYCoordinate($yCoordinate)
  {
    $this->yCoordinate = $yCoordinate;
  }
  public function getYCoordinate()
  {
    return $this->yCoordinate;
  }
}
vendor/google/apiclient-services/src/Google/Service/Drive/DriveFileContentHintsThumbnail.php000064400000001751151327705670026372 0ustar00<?php
/*
 * Copyright 2014 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

class Google_Service_Drive_DriveFileContentHintsThumbnail extends Google_Model
{
  public $image;
  public $mimeType;

  public function setImage($image)
  {
    $this->image = $image;
  }
  public function getImage()
  {
    return $this->image;
  }
  public function setMimeType($mimeType)
  {
    $this->mimeType = $mimeType;
  }
  public function getMimeType()
  {
    return $this->mimeType;
  }
}
vendor/google/apiclient-services/src/Google/Service/Drive/Resource/Teamdrives.php000064400000011006151327705670024200 0ustar00<?php
/*
 * Copyright 2014 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

/**
 * The "teamdrives" collection of methods.
 * Typical usage is:
 *  <code>
 *   $driveService = new Google_Service_Drive(...);
 *   $teamdrives = $driveService->teamdrives;
 *  </code>
 */
class Google_Service_Drive_Resource_Teamdrives extends Google_Service_Resource
{
  /**
   * Creates a new Team Drive. (teamdrives.create)
   *
   * @param string $requestId An ID, such as a random UUID, which uniquely
   * identifies this user's request for idempotent creation of a Team Drive. A
   * repeated request by the same user and with the same request ID will avoid
   * creating duplicates by attempting to create the same Team Drive. If the Team
   * Drive already exists a 409 error will be returned.
   * @param Google_Service_Drive_TeamDrive $postBody
   * @param array $optParams Optional parameters.
   * @return Google_Service_Drive_TeamDrive
   */
  public function create($requestId, Google_Service_Drive_TeamDrive $postBody, $optParams = array())
  {
    $params = array('requestId' => $requestId, 'postBody' => $postBody);
    $params = array_merge($params, $optParams);
    return $this->call('create', array($params), "Google_Service_Drive_TeamDrive");
  }
  /**
   * Permanently deletes a Team Drive for which the user is an organizer. The Team
   * Drive cannot contain any untrashed items. (teamdrives.delete)
   *
   * @param string $teamDriveId The ID of the Team Drive
   * @param array $optParams Optional parameters.
   */
  public function delete($teamDriveId, $optParams = array())
  {
    $params = array('teamDriveId' => $teamDriveId);
    $params = array_merge($params, $optParams);
    return $this->call('delete', array($params));
  }
  /**
   * Gets a Team Drive's metadata by ID. (teamdrives.get)
   *
   * @param string $teamDriveId The ID of the Team Drive
   * @param array $optParams Optional parameters.
   *
   * @opt_param bool useDomainAdminAccess Issue the request as a domain
   * administrator; if set to true, then the requester will be granted access if
   * they are an administrator of the domain to which the Team Drive belongs.
   * @return Google_Service_Drive_TeamDrive
   */
  public function get($teamDriveId, $optParams = array())
  {
    $params = array('teamDriveId' => $teamDriveId);
    $params = array_merge($params, $optParams);
    return $this->call('get', array($params), "Google_Service_Drive_TeamDrive");
  }
  /**
   * Lists the user's Team Drives. (teamdrives.listTeamdrives)
   *
   * @param array $optParams Optional parameters.
   *
   * @opt_param int pageSize Maximum number of Team Drives to return.
   * @opt_param string pageToken Page token for Team Drives.
   * @opt_param string q Query string for searching Team Drives.
   * @opt_param bool useDomainAdminAccess Issue the request as a domain
   * administrator; if set to true, then all Team Drives of the domain in which
   * the requester is an administrator are returned.
   * @return Google_Service_Drive_TeamDriveList
   */
  public function listTeamdrives($optParams = array())
  {
    $params = array();
    $params = array_merge($params, $optParams);
    return $this->call('list', array($params), "Google_Service_Drive_TeamDriveList");
  }
  /**
   * Updates a Team Drive's metadata (teamdrives.update)
   *
   * @param string $teamDriveId The ID of the Team Drive
   * @param Google_Service_Drive_TeamDrive $postBody
   * @param array $optParams Optional parameters.
   *
   * @opt_param bool useDomainAdminAccess Issue the request as a domain
   * administrator; if set to true, then the requester will be granted access if
   * they are an administrator of the domain to which the Team Drive belongs.
   * @return Google_Service_Drive_TeamDrive
   */
  public function update($teamDriveId, Google_Service_Drive_TeamDrive $postBody, $optParams = array())
  {
    $params = array('teamDriveId' => $teamDriveId, 'postBody' => $postBody);
    $params = array_merge($params, $optParams);
    return $this->call('update', array($params), "Google_Service_Drive_TeamDrive");
  }
}
vendor/google/apiclient-services/src/Google/Service/Drive/Resource/Replies.php000064400000010554151327705670023507 0ustar00<?php
/*
 * Copyright 2014 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

/**
 * The "replies" collection of methods.
 * Typical usage is:
 *  <code>
 *   $driveService = new Google_Service_Drive(...);
 *   $replies = $driveService->replies;
 *  </code>
 */
class Google_Service_Drive_Resource_Replies extends Google_Service_Resource
{
  /**
   * Creates a new reply to a comment. (replies.create)
   *
   * @param string $fileId The ID of the file.
   * @param string $commentId The ID of the comment.
   * @param Google_Service_Drive_Reply $postBody
   * @param array $optParams Optional parameters.
   * @return Google_Service_Drive_Reply
   */
  public function create($fileId, $commentId, Google_Service_Drive_Reply $postBody, $optParams = array())
  {
    $params = array('fileId' => $fileId, 'commentId' => $commentId, 'postBody' => $postBody);
    $params = array_merge($params, $optParams);
    return $this->call('create', array($params), "Google_Service_Drive_Reply");
  }
  /**
   * Deletes a reply. (replies.delete)
   *
   * @param string $fileId The ID of the file.
   * @param string $commentId The ID of the comment.
   * @param string $replyId The ID of the reply.
   * @param array $optParams Optional parameters.
   */
  public function delete($fileId, $commentId, $replyId, $optParams = array())
  {
    $params = array('fileId' => $fileId, 'commentId' => $commentId, 'replyId' => $replyId);
    $params = array_merge($params, $optParams);
    return $this->call('delete', array($params));
  }
  /**
   * Gets a reply by ID. (replies.get)
   *
   * @param string $fileId The ID of the file.
   * @param string $commentId The ID of the comment.
   * @param string $replyId The ID of the reply.
   * @param array $optParams Optional parameters.
   *
   * @opt_param bool includeDeleted Whether to return deleted replies. Deleted
   * replies will not include their original content.
   * @return Google_Service_Drive_Reply
   */
  public function get($fileId, $commentId, $replyId, $optParams = array())
  {
    $params = array('fileId' => $fileId, 'commentId' => $commentId, 'replyId' => $replyId);
    $params = array_merge($params, $optParams);
    return $this->call('get', array($params), "Google_Service_Drive_Reply");
  }
  /**
   * Lists a comment's replies. (replies.listReplies)
   *
   * @param string $fileId The ID of the file.
   * @param string $commentId The ID of the comment.
   * @param array $optParams Optional parameters.
   *
   * @opt_param bool includeDeleted Whether to include deleted replies. Deleted
   * replies will not include their original content.
   * @opt_param int pageSize The maximum number of replies to return per page.
   * @opt_param string pageToken The token for continuing a previous list request
   * on the next page. This should be set to the value of 'nextPageToken' from the
   * previous response.
   * @return Google_Service_Drive_ReplyList
   */
  public function listReplies($fileId, $commentId, $optParams = array())
  {
    $params = array('fileId' => $fileId, 'commentId' => $commentId);
    $params = array_merge($params, $optParams);
    return $this->call('list', array($params), "Google_Service_Drive_ReplyList");
  }
  /**
   * Updates a reply with patch semantics. (replies.update)
   *
   * @param string $fileId The ID of the file.
   * @param string $commentId The ID of the comment.
   * @param string $replyId The ID of the reply.
   * @param Google_Service_Drive_Reply $postBody
   * @param array $optParams Optional parameters.
   * @return Google_Service_Drive_Reply
   */
  public function update($fileId, $commentId, $replyId, Google_Service_Drive_Reply $postBody, $optParams = array())
  {
    $params = array('fileId' => $fileId, 'commentId' => $commentId, 'replyId' => $replyId, 'postBody' => $postBody);
    $params = array_merge($params, $optParams);
    return $this->call('update', array($params), "Google_Service_Drive_Reply");
  }
}
vendor/google/apiclient-services/src/Google/Service/Drive/Resource/Changes.php000064400000013333151327705670023452 0ustar00<?php
/*
 * Copyright 2014 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

/**
 * The "changes" collection of methods.
 * Typical usage is:
 *  <code>
 *   $driveService = new Google_Service_Drive(...);
 *   $changes = $driveService->changes;
 *  </code>
 */
class Google_Service_Drive_Resource_Changes extends Google_Service_Resource
{
  /**
   * Gets the starting pageToken for listing future changes.
   * (changes.getStartPageToken)
   *
   * @param array $optParams Optional parameters.
   *
   * @opt_param bool supportsTeamDrives Whether the requesting application
   * supports Team Drives.
   * @opt_param string teamDriveId The ID of the Team Drive for which the starting
   * pageToken for listing future changes from that Team Drive will be returned.
   * @return Google_Service_Drive_StartPageToken
   */
  public function getStartPageToken($optParams = array())
  {
    $params = array();
    $params = array_merge($params, $optParams);
    return $this->call('getStartPageToken', array($params), "Google_Service_Drive_StartPageToken");
  }
  /**
   * Lists the changes for a user or Team Drive. (changes.listChanges)
   *
   * @param string $pageToken The token for continuing a previous list request on
   * the next page. This should be set to the value of 'nextPageToken' from the
   * previous response or to the response from the getStartPageToken method.
   * @param array $optParams Optional parameters.
   *
   * @opt_param bool includeCorpusRemovals Whether changes should include the file
   * resource if the file is still accessible by the user at the time of the
   * request, even when a file was removed from the list of changes and there will
   * be no further change entries for this file.
   * @opt_param bool includeRemoved Whether to include changes indicating that
   * items have been removed from the list of changes, for example by deletion or
   * loss of access.
   * @opt_param bool includeTeamDriveItems Whether Team Drive files or changes
   * should be included in results.
   * @opt_param int pageSize The maximum number of changes to return per page.
   * @opt_param bool restrictToMyDrive Whether to restrict the results to changes
   * inside the My Drive hierarchy. This omits changes to files such as those in
   * the Application Data folder or shared files which have not been added to My
   * Drive.
   * @opt_param string spaces A comma-separated list of spaces to query within the
   * user corpus. Supported values are 'drive', 'appDataFolder' and 'photos'.
   * @opt_param bool supportsTeamDrives Whether the requesting application
   * supports Team Drives.
   * @opt_param string teamDriveId The Team Drive from which changes will be
   * returned. If specified the change IDs will be reflective of the Team Drive;
   * use the combined Team Drive ID and change ID as an identifier.
   * @return Google_Service_Drive_ChangeList
   */
  public function listChanges($pageToken, $optParams = array())
  {
    $params = array('pageToken' => $pageToken);
    $params = array_merge($params, $optParams);
    return $this->call('list', array($params), "Google_Service_Drive_ChangeList");
  }
  /**
   * Subscribes to changes for a user. (changes.watch)
   *
   * @param string $pageToken The token for continuing a previous list request on
   * the next page. This should be set to the value of 'nextPageToken' from the
   * previous response or to the response from the getStartPageToken method.
   * @param Google_Service_Drive_Channel $postBody
   * @param array $optParams Optional parameters.
   *
   * @opt_param bool includeCorpusRemovals Whether changes should include the file
   * resource if the file is still accessible by the user at the time of the
   * request, even when a file was removed from the list of changes and there will
   * be no further change entries for this file.
   * @opt_param bool includeRemoved Whether to include changes indicating that
   * items have been removed from the list of changes, for example by deletion or
   * loss of access.
   * @opt_param bool includeTeamDriveItems Whether Team Drive files or changes
   * should be included in results.
   * @opt_param int pageSize The maximum number of changes to return per page.
   * @opt_param bool restrictToMyDrive Whether to restrict the results to changes
   * inside the My Drive hierarchy. This omits changes to files such as those in
   * the Application Data folder or shared files which have not been added to My
   * Drive.
   * @opt_param string spaces A comma-separated list of spaces to query within the
   * user corpus. Supported values are 'drive', 'appDataFolder' and 'photos'.
   * @opt_param bool supportsTeamDrives Whether the requesting application
   * supports Team Drives.
   * @opt_param string teamDriveId The Team Drive from which changes will be
   * returned. If specified the change IDs will be reflective of the Team Drive;
   * use the combined Team Drive ID and change ID as an identifier.
   * @return Google_Service_Drive_Channel
   */
  public function watch($pageToken, Google_Service_Drive_Channel $postBody, $optParams = array())
  {
    $params = array('pageToken' => $pageToken, 'postBody' => $postBody);
    $params = array_merge($params, $optParams);
    return $this->call('watch', array($params), "Google_Service_Drive_Channel");
  }
}
vendor/google/apiclient-services/src/Google/Service/Drive/Resource/About.php000064400000002353151327705670023154 0ustar00<?php
/*
 * Copyright 2014 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

/**
 * The "about" collection of methods.
 * Typical usage is:
 *  <code>
 *   $driveService = new Google_Service_Drive(...);
 *   $about = $driveService->about;
 *  </code>
 */
class Google_Service_Drive_Resource_About extends Google_Service_Resource
{
  /**
   * Gets information about the user, the user's Drive, and system capabilities.
   * (about.get)
   *
   * @param array $optParams Optional parameters.
   * @return Google_Service_Drive_About
   */
  public function get($optParams = array())
  {
    $params = array();
    $params = array_merge($params, $optParams);
    return $this->call('get', array($params), "Google_Service_Drive_About");
  }
}
vendor/google/apiclient-services/src/Google/Service/Drive/Resource/Comments.php000064400000010151151327705670023662 0ustar00<?php
/*
 * Copyright 2014 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

/**
 * The "comments" collection of methods.
 * Typical usage is:
 *  <code>
 *   $driveService = new Google_Service_Drive(...);
 *   $comments = $driveService->comments;
 *  </code>
 */
class Google_Service_Drive_Resource_Comments extends Google_Service_Resource
{
  /**
   * Creates a new comment on a file. (comments.create)
   *
   * @param string $fileId The ID of the file.
   * @param Google_Service_Drive_Comment $postBody
   * @param array $optParams Optional parameters.
   * @return Google_Service_Drive_Comment
   */
  public function create($fileId, Google_Service_Drive_Comment $postBody, $optParams = array())
  {
    $params = array('fileId' => $fileId, 'postBody' => $postBody);
    $params = array_merge($params, $optParams);
    return $this->call('create', array($params), "Google_Service_Drive_Comment");
  }
  /**
   * Deletes a comment. (comments.delete)
   *
   * @param string $fileId The ID of the file.
   * @param string $commentId The ID of the comment.
   * @param array $optParams Optional parameters.
   */
  public function delete($fileId, $commentId, $optParams = array())
  {
    $params = array('fileId' => $fileId, 'commentId' => $commentId);
    $params = array_merge($params, $optParams);
    return $this->call('delete', array($params));
  }
  /**
   * Gets a comment by ID. (comments.get)
   *
   * @param string $fileId The ID of the file.
   * @param string $commentId The ID of the comment.
   * @param array $optParams Optional parameters.
   *
   * @opt_param bool includeDeleted Whether to return deleted comments. Deleted
   * comments will not include their original content.
   * @return Google_Service_Drive_Comment
   */
  public function get($fileId, $commentId, $optParams = array())
  {
    $params = array('fileId' => $fileId, 'commentId' => $commentId);
    $params = array_merge($params, $optParams);
    return $this->call('get', array($params), "Google_Service_Drive_Comment");
  }
  /**
   * Lists a file's comments. (comments.listComments)
   *
   * @param string $fileId The ID of the file.
   * @param array $optParams Optional parameters.
   *
   * @opt_param bool includeDeleted Whether to include deleted comments. Deleted
   * comments will not include their original content.
   * @opt_param int pageSize The maximum number of comments to return per page.
   * @opt_param string pageToken The token for continuing a previous list request
   * on the next page. This should be set to the value of 'nextPageToken' from the
   * previous response.
   * @opt_param string startModifiedTime The minimum value of 'modifiedTime' for
   * the result comments (RFC 3339 date-time).
   * @return Google_Service_Drive_CommentList
   */
  public function listComments($fileId, $optParams = array())
  {
    $params = array('fileId' => $fileId);
    $params = array_merge($params, $optParams);
    return $this->call('list', array($params), "Google_Service_Drive_CommentList");
  }
  /**
   * Updates a comment with patch semantics. (comments.update)
   *
   * @param string $fileId The ID of the file.
   * @param string $commentId The ID of the comment.
   * @param Google_Service_Drive_Comment $postBody
   * @param array $optParams Optional parameters.
   * @return Google_Service_Drive_Comment
   */
  public function update($fileId, $commentId, Google_Service_Drive_Comment $postBody, $optParams = array())
  {
    $params = array('fileId' => $fileId, 'commentId' => $commentId, 'postBody' => $postBody);
    $params = array_merge($params, $optParams);
    return $this->call('update', array($params), "Google_Service_Drive_Comment");
  }
}
vendor/google/apiclient-services/src/Google/Service/Drive/Resource/Permissions.php000064400000015101151327705670024410 0ustar00<?php
/*
 * Copyright 2014 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

/**
 * The "permissions" collection of methods.
 * Typical usage is:
 *  <code>
 *   $driveService = new Google_Service_Drive(...);
 *   $permissions = $driveService->permissions;
 *  </code>
 */
class Google_Service_Drive_Resource_Permissions extends Google_Service_Resource
{
  /**
   * Creates a permission for a file or Team Drive. (permissions.create)
   *
   * @param string $fileId The ID of the file or Team Drive.
   * @param Google_Service_Drive_Permission $postBody
   * @param array $optParams Optional parameters.
   *
   * @opt_param string emailMessage A plain text custom message to include in the
   * notification email.
   * @opt_param bool sendNotificationEmail Whether to send a notification email
   * when sharing to users or groups. This defaults to true for users and groups,
   * and is not allowed for other requests. It must not be disabled for ownership
   * transfers.
   * @opt_param bool supportsTeamDrives Whether the requesting application
   * supports Team Drives.
   * @opt_param bool transferOwnership Whether to transfer ownership to the
   * specified user and downgrade the current owner to a writer. This parameter is
   * required as an acknowledgement of the side effect.
   * @opt_param bool useDomainAdminAccess Issue the request as a domain
   * administrator; if set to true, then the requester will be granted access if
   * they are an administrator of the domain to which the item belongs.
   * @return Google_Service_Drive_Permission
   */
  public function create($fileId, Google_Service_Drive_Permission $postBody, $optParams = array())
  {
    $params = array('fileId' => $fileId, 'postBody' => $postBody);
    $params = array_merge($params, $optParams);
    return $this->call('create', array($params), "Google_Service_Drive_Permission");
  }
  /**
   * Deletes a permission. (permissions.delete)
   *
   * @param string $fileId The ID of the file or Team Drive.
   * @param string $permissionId The ID of the permission.
   * @param array $optParams Optional parameters.
   *
   * @opt_param bool supportsTeamDrives Whether the requesting application
   * supports Team Drives.
   * @opt_param bool useDomainAdminAccess Issue the request as a domain
   * administrator; if set to true, then the requester will be granted access if
   * they are an administrator of the domain to which the item belongs.
   */
  public function delete($fileId, $permissionId, $optParams = array())
  {
    $params = array('fileId' => $fileId, 'permissionId' => $permissionId);
    $params = array_merge($params, $optParams);
    return $this->call('delete', array($params));
  }
  /**
   * Gets a permission by ID. (permissions.get)
   *
   * @param string $fileId The ID of the file.
   * @param string $permissionId The ID of the permission.
   * @param array $optParams Optional parameters.
   *
   * @opt_param bool supportsTeamDrives Whether the requesting application
   * supports Team Drives.
   * @opt_param bool useDomainAdminAccess Issue the request as a domain
   * administrator; if set to true, then the requester will be granted access if
   * they are an administrator of the domain to which the item belongs.
   * @return Google_Service_Drive_Permission
   */
  public function get($fileId, $permissionId, $optParams = array())
  {
    $params = array('fileId' => $fileId, 'permissionId' => $permissionId);
    $params = array_merge($params, $optParams);
    return $this->call('get', array($params), "Google_Service_Drive_Permission");
  }
  /**
   * Lists a file's or Team Drive's permissions. (permissions.listPermissions)
   *
   * @param string $fileId The ID of the file or Team Drive.
   * @param array $optParams Optional parameters.
   *
   * @opt_param int pageSize The maximum number of permissions to return per page.
   * When not set for files in a Team Drive, at most 100 results will be returned.
   * When not set for files that are not in a Team Drive, the entire list will be
   * returned.
   * @opt_param string pageToken The token for continuing a previous list request
   * on the next page. This should be set to the value of 'nextPageToken' from the
   * previous response.
   * @opt_param bool supportsTeamDrives Whether the requesting application
   * supports Team Drives.
   * @opt_param bool useDomainAdminAccess Issue the request as a domain
   * administrator; if set to true, then the requester will be granted access if
   * they are an administrator of the domain to which the item belongs.
   * @return Google_Service_Drive_PermissionList
   */
  public function listPermissions($fileId, $optParams = array())
  {
    $params = array('fileId' => $fileId);
    $params = array_merge($params, $optParams);
    return $this->call('list', array($params), "Google_Service_Drive_PermissionList");
  }
  /**
   * Updates a permission with patch semantics. (permissions.update)
   *
   * @param string $fileId The ID of the file or Team Drive.
   * @param string $permissionId The ID of the permission.
   * @param Google_Service_Drive_Permission $postBody
   * @param array $optParams Optional parameters.
   *
   * @opt_param bool removeExpiration Whether to remove the expiration date.
   * @opt_param bool supportsTeamDrives Whether the requesting application
   * supports Team Drives.
   * @opt_param bool transferOwnership Whether to transfer ownership to the
   * specified user and downgrade the current owner to a writer. This parameter is
   * required as an acknowledgement of the side effect.
   * @opt_param bool useDomainAdminAccess Issue the request as a domain
   * administrator; if set to true, then the requester will be granted access if
   * they are an administrator of the domain to which the item belongs.
   * @return Google_Service_Drive_Permission
   */
  public function update($fileId, $permissionId, Google_Service_Drive_Permission $postBody, $optParams = array())
  {
    $params = array('fileId' => $fileId, 'permissionId' => $permissionId, 'postBody' => $postBody);
    $params = array_merge($params, $optParams);
    return $this->call('update', array($params), "Google_Service_Drive_Permission");
  }
}
vendor/google/apiclient-services/src/Google/Service/Drive/Resource/Files.php000064400000025405151327705670023147 0ustar00<?php
/*
 * Copyright 2014 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

/**
 * The "files" collection of methods.
 * Typical usage is:
 *  <code>
 *   $driveService = new Google_Service_Drive(...);
 *   $files = $driveService->files;
 *  </code>
 */
class Google_Service_Drive_Resource_Files extends Google_Service_Resource
{
  /**
   * Creates a copy of a file and applies any requested updates with patch
   * semantics. (files.copy)
   *
   * @param string $fileId The ID of the file.
   * @param Google_Service_Drive_DriveFile $postBody
   * @param array $optParams Optional parameters.
   *
   * @opt_param bool ignoreDefaultVisibility Whether to ignore the domain's
   * default visibility settings for the created file. Domain administrators can
   * choose to make all uploaded files visible to the domain by default; this
   * parameter bypasses that behavior for the request. Permissions are still
   * inherited from parent folders.
   * @opt_param bool keepRevisionForever Whether to set the 'keepForever' field in
   * the new head revision. This is only applicable to files with binary content
   * in Drive.
   * @opt_param string ocrLanguage A language hint for OCR processing during image
   * import (ISO 639-1 code).
   * @opt_param bool supportsTeamDrives Whether the requesting application
   * supports Team Drives.
   * @return Google_Service_Drive_DriveFile
   */
  public function copy($fileId, Google_Service_Drive_DriveFile $postBody, $optParams = array())
  {
    $params = array('fileId' => $fileId, 'postBody' => $postBody);
    $params = array_merge($params, $optParams);
    return $this->call('copy', array($params), "Google_Service_Drive_DriveFile");
  }
  /**
   * Creates a new file. (files.create)
   *
   * @param Google_Service_Drive_DriveFile $postBody
   * @param array $optParams Optional parameters.
   *
   * @opt_param bool ignoreDefaultVisibility Whether to ignore the domain's
   * default visibility settings for the created file. Domain administrators can
   * choose to make all uploaded files visible to the domain by default; this
   * parameter bypasses that behavior for the request. Permissions are still
   * inherited from parent folders.
   * @opt_param bool keepRevisionForever Whether to set the 'keepForever' field in
   * the new head revision. This is only applicable to files with binary content
   * in Drive.
   * @opt_param string ocrLanguage A language hint for OCR processing during image
   * import (ISO 639-1 code).
   * @opt_param bool supportsTeamDrives Whether the requesting application
   * supports Team Drives.
   * @opt_param bool useContentAsIndexableText Whether to use the uploaded content
   * as indexable text.
   * @return Google_Service_Drive_DriveFile
   */
  public function create(Google_Service_Drive_DriveFile $postBody, $optParams = array())
  {
    $params = array('postBody' => $postBody);
    $params = array_merge($params, $optParams);
    return $this->call('create', array($params), "Google_Service_Drive_DriveFile");
  }
  /**
   * Permanently deletes a file owned by the user without moving it to the trash.
   * If the file belongs to a Team Drive the user must be an organizer on the
   * parent. If the target is a folder, all descendants owned by the user are also
   * deleted. (files.delete)
   *
   * @param string $fileId The ID of the file.
   * @param array $optParams Optional parameters.
   *
   * @opt_param bool supportsTeamDrives Whether the requesting application
   * supports Team Drives.
   */
  public function delete($fileId, $optParams = array())
  {
    $params = array('fileId' => $fileId);
    $params = array_merge($params, $optParams);
    return $this->call('delete', array($params));
  }
  /**
   * Permanently deletes all of the user's trashed files. (files.emptyTrash)
   *
   * @param array $optParams Optional parameters.
   */
  public function emptyTrash($optParams = array())
  {
    $params = array();
    $params = array_merge($params, $optParams);
    return $this->call('emptyTrash', array($params));
  }
  /**
   * Exports a Google Doc to the requested MIME type and returns the exported
   * content. Please note that the exported content is limited to 10MB.
   * (files.export)
   *
   * @param string $fileId The ID of the file.
   * @param string $mimeType The MIME type of the format requested for this
   * export.
   * @param array $optParams Optional parameters.
   */
  public function export($fileId, $mimeType, $optParams = array())
  {
    $params = array('fileId' => $fileId, 'mimeType' => $mimeType);
    $params = array_merge($params, $optParams);
    return $this->call('export', array($params));
  }
  /**
   * Generates a set of file IDs which can be provided in create requests.
   * (files.generateIds)
   *
   * @param array $optParams Optional parameters.
   *
   * @opt_param int count The number of IDs to return.
   * @opt_param string space The space in which the IDs can be used to create new
   * files. Supported values are 'drive' and 'appDataFolder'.
   * @return Google_Service_Drive_GeneratedIds
   */
  public function generateIds($optParams = array())
  {
    $params = array();
    $params = array_merge($params, $optParams);
    return $this->call('generateIds', array($params), "Google_Service_Drive_GeneratedIds");
  }
  /**
   * Gets a file's metadata or content by ID. (files.get)
   *
   * @param string $fileId The ID of the file.
   * @param array $optParams Optional parameters.
   *
   * @opt_param bool acknowledgeAbuse Whether the user is acknowledging the risk
   * of downloading known malware or other abusive files. This is only applicable
   * when alt=media.
   * @opt_param bool supportsTeamDrives Whether the requesting application
   * supports Team Drives.
   * @return Google_Service_Drive_DriveFile
   */
  public function get($fileId, $optParams = array())
  {
    $params = array('fileId' => $fileId);
    $params = array_merge($params, $optParams);
    return $this->call('get', array($params), "Google_Service_Drive_DriveFile");
  }
  /**
   * Lists or searches files. (files.listFiles)
   *
   * @param array $optParams Optional parameters.
   *
   * @opt_param string corpora Comma-separated list of bodies of items
   * (files/documents) to which the query applies. Supported bodies are 'user',
   * 'domain', 'teamDrive' and 'allTeamDrives'. 'allTeamDrives' must be combined
   * with 'user'; all other values must be used in isolation. Prefer 'user' or
   * 'teamDrive' to 'allTeamDrives' for efficiency.
   * @opt_param string corpus The source of files to list. Deprecated: use
   * 'corpora' instead.
   * @opt_param bool includeTeamDriveItems Whether Team Drive items should be
   * included in results.
   * @opt_param string orderBy A comma-separated list of sort keys. Valid keys are
   * 'createdTime', 'folder', 'modifiedByMeTime', 'modifiedTime', 'name',
   * 'name_natural', 'quotaBytesUsed', 'recency', 'sharedWithMeTime', 'starred',
   * and 'viewedByMeTime'. Each key sorts ascending by default, but may be
   * reversed with the 'desc' modifier. Example usage:
   * ?orderBy=folder,modifiedTime desc,name. Please note that there is a current
   * limitation for users with approximately one million files in which the
   * requested sort order is ignored.
   * @opt_param int pageSize The maximum number of files to return per page.
   * Partial or empty result pages are possible even before the end of the files
   * list has been reached.
   * @opt_param string pageToken The token for continuing a previous list request
   * on the next page. This should be set to the value of 'nextPageToken' from the
   * previous response.
   * @opt_param string q A query for filtering the file results. See the "Search
   * for Files" guide for supported syntax.
   * @opt_param string spaces A comma-separated list of spaces to query within the
   * corpus. Supported values are 'drive', 'appDataFolder' and 'photos'.
   * @opt_param bool supportsTeamDrives Whether the requesting application
   * supports Team Drives.
   * @opt_param string teamDriveId ID of Team Drive to search.
   * @return Google_Service_Drive_FileList
   */
  public function listFiles($optParams = array())
  {
    $params = array();
    $params = array_merge($params, $optParams);
    return $this->call('list', array($params), "Google_Service_Drive_FileList");
  }
  /**
   * Updates a file's metadata and/or content with patch semantics. (files.update)
   *
   * @param string $fileId The ID of the file.
   * @param Google_Service_Drive_DriveFile $postBody
   * @param array $optParams Optional parameters.
   *
   * @opt_param string addParents A comma-separated list of parent IDs to add.
   * @opt_param bool keepRevisionForever Whether to set the 'keepForever' field in
   * the new head revision. This is only applicable to files with binary content
   * in Drive.
   * @opt_param string ocrLanguage A language hint for OCR processing during image
   * import (ISO 639-1 code).
   * @opt_param string removeParents A comma-separated list of parent IDs to
   * remove.
   * @opt_param bool supportsTeamDrives Whether the requesting application
   * supports Team Drives.
   * @opt_param bool useContentAsIndexableText Whether to use the uploaded content
   * as indexable text.
   * @return Google_Service_Drive_DriveFile
   */
  public function update($fileId, Google_Service_Drive_DriveFile $postBody, $optParams = array())
  {
    $params = array('fileId' => $fileId, 'postBody' => $postBody);
    $params = array_merge($params, $optParams);
    return $this->call('update', array($params), "Google_Service_Drive_DriveFile");
  }
  /**
   * Subscribes to changes to a file (files.watch)
   *
   * @param string $fileId The ID of the file.
   * @param Google_Service_Drive_Channel $postBody
   * @param array $optParams Optional parameters.
   *
   * @opt_param bool acknowledgeAbuse Whether the user is acknowledging the risk
   * of downloading known malware or other abusive files. This is only applicable
   * when alt=media.
   * @opt_param bool supportsTeamDrives Whether the requesting application
   * supports Team Drives.
   * @return Google_Service_Drive_Channel
   */
  public function watch($fileId, Google_Service_Drive_Channel $postBody, $optParams = array())
  {
    $params = array('fileId' => $fileId, 'postBody' => $postBody);
    $params = array_merge($params, $optParams);
    return $this->call('watch', array($params), "Google_Service_Drive_Channel");
  }
}
vendor/google/apiclient-services/src/Google/Service/Drive/Resource/Revisions.php000064400000006761151327705670024072 0ustar00<?php
/*
 * Copyright 2014 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

/**
 * The "revisions" collection of methods.
 * Typical usage is:
 *  <code>
 *   $driveService = new Google_Service_Drive(...);
 *   $revisions = $driveService->revisions;
 *  </code>
 */
class Google_Service_Drive_Resource_Revisions extends Google_Service_Resource
{
  /**
   * Permanently deletes a revision. This method is only applicable to files with
   * binary content in Drive. (revisions.delete)
   *
   * @param string $fileId The ID of the file.
   * @param string $revisionId The ID of the revision.
   * @param array $optParams Optional parameters.
   */
  public function delete($fileId, $revisionId, $optParams = array())
  {
    $params = array('fileId' => $fileId, 'revisionId' => $revisionId);
    $params = array_merge($params, $optParams);
    return $this->call('delete', array($params));
  }
  /**
   * Gets a revision's metadata or content by ID. (revisions.get)
   *
   * @param string $fileId The ID of the file.
   * @param string $revisionId The ID of the revision.
   * @param array $optParams Optional parameters.
   *
   * @opt_param bool acknowledgeAbuse Whether the user is acknowledging the risk
   * of downloading known malware or other abusive files. This is only applicable
   * when alt=media.
   * @return Google_Service_Drive_Revision
   */
  public function get($fileId, $revisionId, $optParams = array())
  {
    $params = array('fileId' => $fileId, 'revisionId' => $revisionId);
    $params = array_merge($params, $optParams);
    return $this->call('get', array($params), "Google_Service_Drive_Revision");
  }
  /**
   * Lists a file's revisions. (revisions.listRevisions)
   *
   * @param string $fileId The ID of the file.
   * @param array $optParams Optional parameters.
   *
   * @opt_param int pageSize The maximum number of revisions to return per page.
   * @opt_param string pageToken The token for continuing a previous list request
   * on the next page. This should be set to the value of 'nextPageToken' from the
   * previous response.
   * @return Google_Service_Drive_RevisionList
   */
  public function listRevisions($fileId, $optParams = array())
  {
    $params = array('fileId' => $fileId);
    $params = array_merge($params, $optParams);
    return $this->call('list', array($params), "Google_Service_Drive_RevisionList");
  }
  /**
   * Updates a revision with patch semantics. (revisions.update)
   *
   * @param string $fileId The ID of the file.
   * @param string $revisionId The ID of the revision.
   * @param Google_Service_Drive_Revision $postBody
   * @param array $optParams Optional parameters.
   * @return Google_Service_Drive_Revision
   */
  public function update($fileId, $revisionId, Google_Service_Drive_Revision $postBody, $optParams = array())
  {
    $params = array('fileId' => $fileId, 'revisionId' => $revisionId, 'postBody' => $postBody);
    $params = array_merge($params, $optParams);
    return $this->call('update', array($params), "Google_Service_Drive_Revision");
  }
}
vendor/google/apiclient-services/src/Google/Service/Drive/Resource/Channels.php000064400000002405151327705670023633 0ustar00<?php
/*
 * Copyright 2014 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

/**
 * The "channels" collection of methods.
 * Typical usage is:
 *  <code>
 *   $driveService = new Google_Service_Drive(...);
 *   $channels = $driveService->channels;
 *  </code>
 */
class Google_Service_Drive_Resource_Channels extends Google_Service_Resource
{
  /**
   * Stop watching resources through this channel (channels.stop)
   *
   * @param Google_Service_Drive_Channel $postBody
   * @param array $optParams Optional parameters.
   */
  public function stop(Google_Service_Drive_Channel $postBody, $optParams = array())
  {
    $params = array('postBody' => $postBody);
    $params = array_merge($params, $optParams);
    return $this->call('stop', array($params));
  }
}
vendor/google/apiclient-services/src/Google/Service/Drive/GeneratedIds.php000064400000002154151327705670022650 0ustar00<?php
/*
 * Copyright 2014 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

class Google_Service_Drive_GeneratedIds extends Google_Collection
{
  protected $collection_key = 'ids';
  public $ids;
  public $kind;
  public $space;

  public function setIds($ids)
  {
    $this->ids = $ids;
  }
  public function getIds()
  {
    return $this->ids;
  }
  public function setKind($kind)
  {
    $this->kind = $kind;
  }
  public function getKind()
  {
    return $this->kind;
  }
  public function setSpace($space)
  {
    $this->space = $space;
  }
  public function getSpace()
  {
    return $this->space;
  }
}
vendor/google/apiclient-services/src/Google/Service/Drive/User.php000064400000003232151327705670021226 0ustar00<?php
/*
 * Copyright 2014 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

class Google_Service_Drive_User extends Google_Model
{
  public $displayName;
  public $emailAddress;
  public $kind;
  public $me;
  public $permissionId;
  public $photoLink;

  public function setDisplayName($displayName)
  {
    $this->displayName = $displayName;
  }
  public function getDisplayName()
  {
    return $this->displayName;
  }
  public function setEmailAddress($emailAddress)
  {
    $this->emailAddress = $emailAddress;
  }
  public function getEmailAddress()
  {
    return $this->emailAddress;
  }
  public function setKind($kind)
  {
    $this->kind = $kind;
  }
  public function getKind()
  {
    return $this->kind;
  }
  public function setMe($me)
  {
    $this->me = $me;
  }
  public function getMe()
  {
    return $this->me;
  }
  public function setPermissionId($permissionId)
  {
    $this->permissionId = $permissionId;
  }
  public function getPermissionId()
  {
    return $this->permissionId;
  }
  public function setPhotoLink($photoLink)
  {
    $this->photoLink = $photoLink;
  }
  public function getPhotoLink()
  {
    return $this->photoLink;
  }
}
vendor/google/apiclient-services/src/Google/Service/Drive/Reply.php000064400000004447151327705670021414 0ustar00<?php
/*
 * Copyright 2014 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

class Google_Service_Drive_Reply extends Google_Model
{
  public $action;
  protected $authorType = 'Google_Service_Drive_User';
  protected $authorDataType = '';
  public $content;
  public $createdTime;
  public $deleted;
  public $htmlContent;
  public $id;
  public $kind;
  public $modifiedTime;

  public function setAction($action)
  {
    $this->action = $action;
  }
  public function getAction()
  {
    return $this->action;
  }
  /**
   * @param Google_Service_Drive_User
   */
  public function setAuthor(Google_Service_Drive_User $author)
  {
    $this->author = $author;
  }
  /**
   * @return Google_Service_Drive_User
   */
  public function getAuthor()
  {
    return $this->author;
  }
  public function setContent($content)
  {
    $this->content = $content;
  }
  public function getContent()
  {
    return $this->content;
  }
  public function setCreatedTime($createdTime)
  {
    $this->createdTime = $createdTime;
  }
  public function getCreatedTime()
  {
    return $this->createdTime;
  }
  public function setDeleted($deleted)
  {
    $this->deleted = $deleted;
  }
  public function getDeleted()
  {
    return $this->deleted;
  }
  public function setHtmlContent($htmlContent)
  {
    $this->htmlContent = $htmlContent;
  }
  public function getHtmlContent()
  {
    return $this->htmlContent;
  }
  public function setId($id)
  {
    $this->id = $id;
  }
  public function getId()
  {
    return $this->id;
  }
  public function setKind($kind)
  {
    $this->kind = $kind;
  }
  public function getKind()
  {
    return $this->kind;
  }
  public function setModifiedTime($modifiedTime)
  {
    $this->modifiedTime = $modifiedTime;
  }
  public function getModifiedTime()
  {
    return $this->modifiedTime;
  }
}
vendor/google/apiclient-services/src/Google/Service/Drive/DriveFileContentHints.php000064400000002520151327705670024521 0ustar00<?php
/*
 * Copyright 2014 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

class Google_Service_Drive_DriveFileContentHints extends Google_Model
{
  public $indexableText;
  protected $thumbnailType = 'Google_Service_Drive_DriveFileContentHintsThumbnail';
  protected $thumbnailDataType = '';

  public function setIndexableText($indexableText)
  {
    $this->indexableText = $indexableText;
  }
  public function getIndexableText()
  {
    return $this->indexableText;
  }
  /**
   * @param Google_Service_Drive_DriveFileContentHintsThumbnail
   */
  public function setThumbnail(Google_Service_Drive_DriveFileContentHintsThumbnail $thumbnail)
  {
    $this->thumbnail = $thumbnail;
  }
  /**
   * @return Google_Service_Drive_DriveFileContentHintsThumbnail
   */
  public function getThumbnail()
  {
    return $this->thumbnail;
  }
}
vendor/google/apiclient-services/src/Google/Service/Drive/About.php000064400000007032151327705670021364 0ustar00<?php
/*
 * Copyright 2014 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

class Google_Service_Drive_About extends Google_Collection
{
  protected $collection_key = 'teamDriveThemes';
  public $appInstalled;
  public $canCreateTeamDrives;
  public $exportFormats;
  public $folderColorPalette;
  public $importFormats;
  public $kind;
  public $maxImportSizes;
  public $maxUploadSize;
  protected $storageQuotaType = 'Google_Service_Drive_AboutStorageQuota';
  protected $storageQuotaDataType = '';
  protected $teamDriveThemesType = 'Google_Service_Drive_AboutTeamDriveThemes';
  protected $teamDriveThemesDataType = 'array';
  protected $userType = 'Google_Service_Drive_User';
  protected $userDataType = '';

  public function setAppInstalled($appInstalled)
  {
    $this->appInstalled = $appInstalled;
  }
  public function getAppInstalled()
  {
    return $this->appInstalled;
  }
  public function setCanCreateTeamDrives($canCreateTeamDrives)
  {
    $this->canCreateTeamDrives = $canCreateTeamDrives;
  }
  public function getCanCreateTeamDrives()
  {
    return $this->canCreateTeamDrives;
  }
  public function setExportFormats($exportFormats)
  {
    $this->exportFormats = $exportFormats;
  }
  public function getExportFormats()
  {
    return $this->exportFormats;
  }
  public function setFolderColorPalette($folderColorPalette)
  {
    $this->folderColorPalette = $folderColorPalette;
  }
  public function getFolderColorPalette()
  {
    return $this->folderColorPalette;
  }
  public function setImportFormats($importFormats)
  {
    $this->importFormats = $importFormats;
  }
  public function getImportFormats()
  {
    return $this->importFormats;
  }
  public function setKind($kind)
  {
    $this->kind = $kind;
  }
  public function getKind()
  {
    return $this->kind;
  }
  public function setMaxImportSizes($maxImportSizes)
  {
    $this->maxImportSizes = $maxImportSizes;
  }
  public function getMaxImportSizes()
  {
    return $this->maxImportSizes;
  }
  public function setMaxUploadSize($maxUploadSize)
  {
    $this->maxUploadSize = $maxUploadSize;
  }
  public function getMaxUploadSize()
  {
    return $this->maxUploadSize;
  }
  /**
   * @param Google_Service_Drive_AboutStorageQuota
   */
  public function setStorageQuota(Google_Service_Drive_AboutStorageQuota $storageQuota)
  {
    $this->storageQuota = $storageQuota;
  }
  /**
   * @return Google_Service_Drive_AboutStorageQuota
   */
  public function getStorageQuota()
  {
    return $this->storageQuota;
  }
  /**
   * @param Google_Service_Drive_AboutTeamDriveThemes
   */
  public function setTeamDriveThemes($teamDriveThemes)
  {
    $this->teamDriveThemes = $teamDriveThemes;
  }
  /**
   * @return Google_Service_Drive_AboutTeamDriveThemes
   */
  public function getTeamDriveThemes()
  {
    return $this->teamDriveThemes;
  }
  /**
   * @param Google_Service_Drive_User
   */
  public function setUser(Google_Service_Drive_User $user)
  {
    $this->user = $user;
  }
  /**
   * @return Google_Service_Drive_User
   */
  public function getUser()
  {
    return $this->user;
  }
}
vendor/google/apiclient-services/src/Google/Service/Drive/DriveFileCapabilities.php000064400000014216151327705670024477 0ustar00<?php
/*
 * Copyright 2014 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

class Google_Service_Drive_DriveFileCapabilities extends Google_Model
{
  public $canAddChildren;
  public $canChangeCopyRequiresWriterPermission;
  public $canChangeViewersCanCopyContent;
  public $canComment;
  public $canCopy;
  public $canDelete;
  public $canDeleteChildren;
  public $canDownload;
  public $canEdit;
  public $canListChildren;
  public $canMoveChildrenOutOfTeamDrive;
  public $canMoveChildrenWithinTeamDrive;
  public $canMoveItemIntoTeamDrive;
  public $canMoveItemOutOfTeamDrive;
  public $canMoveItemWithinTeamDrive;
  public $canMoveTeamDriveItem;
  public $canReadRevisions;
  public $canReadTeamDrive;
  public $canRemoveChildren;
  public $canRename;
  public $canShare;
  public $canTrash;
  public $canTrashChildren;
  public $canUntrash;

  public function setCanAddChildren($canAddChildren)
  {
    $this->canAddChildren = $canAddChildren;
  }
  public function getCanAddChildren()
  {
    return $this->canAddChildren;
  }
  public function setCanChangeCopyRequiresWriterPermission($canChangeCopyRequiresWriterPermission)
  {
    $this->canChangeCopyRequiresWriterPermission = $canChangeCopyRequiresWriterPermission;
  }
  public function getCanChangeCopyRequiresWriterPermission()
  {
    return $this->canChangeCopyRequiresWriterPermission;
  }
  public function setCanChangeViewersCanCopyContent($canChangeViewersCanCopyContent)
  {
    $this->canChangeViewersCanCopyContent = $canChangeViewersCanCopyContent;
  }
  public function getCanChangeViewersCanCopyContent()
  {
    return $this->canChangeViewersCanCopyContent;
  }
  public function setCanComment($canComment)
  {
    $this->canComment = $canComment;
  }
  public function getCanComment()
  {
    return $this->canComment;
  }
  public function setCanCopy($canCopy)
  {
    $this->canCopy = $canCopy;
  }
  public function getCanCopy()
  {
    return $this->canCopy;
  }
  public function setCanDelete($canDelete)
  {
    $this->canDelete = $canDelete;
  }
  public function getCanDelete()
  {
    return $this->canDelete;
  }
  public function setCanDeleteChildren($canDeleteChildren)
  {
    $this->canDeleteChildren = $canDeleteChildren;
  }
  public function getCanDeleteChildren()
  {
    return $this->canDeleteChildren;
  }
  public function setCanDownload($canDownload)
  {
    $this->canDownload = $canDownload;
  }
  public function getCanDownload()
  {
    return $this->canDownload;
  }
  public function setCanEdit($canEdit)
  {
    $this->canEdit = $canEdit;
  }
  public function getCanEdit()
  {
    return $this->canEdit;
  }
  public function setCanListChildren($canListChildren)
  {
    $this->canListChildren = $canListChildren;
  }
  public function getCanListChildren()
  {
    return $this->canListChildren;
  }
  public function setCanMoveChildrenOutOfTeamDrive($canMoveChildrenOutOfTeamDrive)
  {
    $this->canMoveChildrenOutOfTeamDrive = $canMoveChildrenOutOfTeamDrive;
  }
  public function getCanMoveChildrenOutOfTeamDrive()
  {
    return $this->canMoveChildrenOutOfTeamDrive;
  }
  public function setCanMoveChildrenWithinTeamDrive($canMoveChildrenWithinTeamDrive)
  {
    $this->canMoveChildrenWithinTeamDrive = $canMoveChildrenWithinTeamDrive;
  }
  public function getCanMoveChildrenWithinTeamDrive()
  {
    return $this->canMoveChildrenWithinTeamDrive;
  }
  public function setCanMoveItemIntoTeamDrive($canMoveItemIntoTeamDrive)
  {
    $this->canMoveItemIntoTeamDrive = $canMoveItemIntoTeamDrive;
  }
  public function getCanMoveItemIntoTeamDrive()
  {
    return $this->canMoveItemIntoTeamDrive;
  }
  public function setCanMoveItemOutOfTeamDrive($canMoveItemOutOfTeamDrive)
  {
    $this->canMoveItemOutOfTeamDrive = $canMoveItemOutOfTeamDrive;
  }
  public function getCanMoveItemOutOfTeamDrive()
  {
    return $this->canMoveItemOutOfTeamDrive;
  }
  public function setCanMoveItemWithinTeamDrive($canMoveItemWithinTeamDrive)
  {
    $this->canMoveItemWithinTeamDrive = $canMoveItemWithinTeamDrive;
  }
  public function getCanMoveItemWithinTeamDrive()
  {
    return $this->canMoveItemWithinTeamDrive;
  }
  public function setCanMoveTeamDriveItem($canMoveTeamDriveItem)
  {
    $this->canMoveTeamDriveItem = $canMoveTeamDriveItem;
  }
  public function getCanMoveTeamDriveItem()
  {
    return $this->canMoveTeamDriveItem;
  }
  public function setCanReadRevisions($canReadRevisions)
  {
    $this->canReadRevisions = $canReadRevisions;
  }
  public function getCanReadRevisions()
  {
    return $this->canReadRevisions;
  }
  public function setCanReadTeamDrive($canReadTeamDrive)
  {
    $this->canReadTeamDrive = $canReadTeamDrive;
  }
  public function getCanReadTeamDrive()
  {
    return $this->canReadTeamDrive;
  }
  public function setCanRemoveChildren($canRemoveChildren)
  {
    $this->canRemoveChildren = $canRemoveChildren;
  }
  public function getCanRemoveChildren()
  {
    return $this->canRemoveChildren;
  }
  public function setCanRename($canRename)
  {
    $this->canRename = $canRename;
  }
  public function getCanRename()
  {
    return $this->canRename;
  }
  public function setCanShare($canShare)
  {
    $this->canShare = $canShare;
  }
  public function getCanShare()
  {
    return $this->canShare;
  }
  public function setCanTrash($canTrash)
  {
    $this->canTrash = $canTrash;
  }
  public function getCanTrash()
  {
    return $this->canTrash;
  }
  public function setCanTrashChildren($canTrashChildren)
  {
    $this->canTrashChildren = $canTrashChildren;
  }
  public function getCanTrashChildren()
  {
    return $this->canTrashChildren;
  }
  public function setCanUntrash($canUntrash)
  {
    $this->canUntrash = $canUntrash;
  }
  public function getCanUntrash()
  {
    return $this->canUntrash;
  }
}
vendor/google/apiclient-services/src/Google/Service/Drive/TeamDrive.php000064400000006412151327705670022173 0ustar00<?php
/*
 * Copyright 2014 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

class Google_Service_Drive_TeamDrive extends Google_Model
{
  protected $backgroundImageFileType = 'Google_Service_Drive_TeamDriveBackgroundImageFile';
  protected $backgroundImageFileDataType = '';
  public $backgroundImageLink;
  protected $capabilitiesType = 'Google_Service_Drive_TeamDriveCapabilities';
  protected $capabilitiesDataType = '';
  public $colorRgb;
  public $createdTime;
  public $id;
  public $kind;
  public $name;
  protected $restrictionsType = 'Google_Service_Drive_TeamDriveRestrictions';
  protected $restrictionsDataType = '';
  public $themeId;

  /**
   * @param Google_Service_Drive_TeamDriveBackgroundImageFile
   */
  public function setBackgroundImageFile(Google_Service_Drive_TeamDriveBackgroundImageFile $backgroundImageFile)
  {
    $this->backgroundImageFile = $backgroundImageFile;
  }
  /**
   * @return Google_Service_Drive_TeamDriveBackgroundImageFile
   */
  public function getBackgroundImageFile()
  {
    return $this->backgroundImageFile;
  }
  public function setBackgroundImageLink($backgroundImageLink)
  {
    $this->backgroundImageLink = $backgroundImageLink;
  }
  public function getBackgroundImageLink()
  {
    return $this->backgroundImageLink;
  }
  /**
   * @param Google_Service_Drive_TeamDriveCapabilities
   */
  public function setCapabilities(Google_Service_Drive_TeamDriveCapabilities $capabilities)
  {
    $this->capabilities = $capabilities;
  }
  /**
   * @return Google_Service_Drive_TeamDriveCapabilities
   */
  public function getCapabilities()
  {
    return $this->capabilities;
  }
  public function setColorRgb($colorRgb)
  {
    $this->colorRgb = $colorRgb;
  }
  public function getColorRgb()
  {
    return $this->colorRgb;
  }
  public function setCreatedTime($createdTime)
  {
    $this->createdTime = $createdTime;
  }
  public function getCreatedTime()
  {
    return $this->createdTime;
  }
  public function setId($id)
  {
    $this->id = $id;
  }
  public function getId()
  {
    return $this->id;
  }
  public function setKind($kind)
  {
    $this->kind = $kind;
  }
  public function getKind()
  {
    return $this->kind;
  }
  public function setName($name)
  {
    $this->name = $name;
  }
  public function getName()
  {
    return $this->name;
  }
  /**
   * @param Google_Service_Drive_TeamDriveRestrictions
   */
  public function setRestrictions(Google_Service_Drive_TeamDriveRestrictions $restrictions)
  {
    $this->restrictions = $restrictions;
  }
  /**
   * @return Google_Service_Drive_TeamDriveRestrictions
   */
  public function getRestrictions()
  {
    return $this->restrictions;
  }
  public function setThemeId($themeId)
  {
    $this->themeId = $themeId;
  }
  public function getThemeId()
  {
    return $this->themeId;
  }
}
vendor/google/apiclient-services/src/Google/Service/Drive/AboutTeamDriveThemes.php000064400000002301151327705670024325 0ustar00<?php
/*
 * Copyright 2014 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

class Google_Service_Drive_AboutTeamDriveThemes extends Google_Model
{
  public $backgroundImageLink;
  public $colorRgb;
  public $id;

  public function setBackgroundImageLink($backgroundImageLink)
  {
    $this->backgroundImageLink = $backgroundImageLink;
  }
  public function getBackgroundImageLink()
  {
    return $this->backgroundImageLink;
  }
  public function setColorRgb($colorRgb)
  {
    $this->colorRgb = $colorRgb;
  }
  public function getColorRgb()
  {
    return $this->colorRgb;
  }
  public function setId($id)
  {
    $this->id = $id;
  }
  public function getId()
  {
    return $this->id;
  }
}
vendor/google/apiclient-services/src/Google/Service/Drive/DriveFileImageMediaMetadataLocation.php000064400000002264151327705670027222 0ustar00<?php
/*
 * Copyright 2014 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

class Google_Service_Drive_DriveFileImageMediaMetadataLocation extends Google_Model
{
  public $altitude;
  public $latitude;
  public $longitude;

  public function setAltitude($altitude)
  {
    $this->altitude = $altitude;
  }
  public function getAltitude()
  {
    return $this->altitude;
  }
  public function setLatitude($latitude)
  {
    $this->latitude = $latitude;
  }
  public function getLatitude()
  {
    return $this->latitude;
  }
  public function setLongitude($longitude)
  {
    $this->longitude = $longitude;
  }
  public function getLongitude()
  {
    return $this->longitude;
  }
}
vendor/google/apiclient-services/src/Google/Service/Drive/CommentList.php000064400000002607151327705670022553 0ustar00<?php
/*
 * Copyright 2014 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

class Google_Service_Drive_CommentList extends Google_Collection
{
  protected $collection_key = 'comments';
  protected $commentsType = 'Google_Service_Drive_Comment';
  protected $commentsDataType = 'array';
  public $kind;
  public $nextPageToken;

  /**
   * @param Google_Service_Drive_Comment
   */
  public function setComments($comments)
  {
    $this->comments = $comments;
  }
  /**
   * @return Google_Service_Drive_Comment
   */
  public function getComments()
  {
    return $this->comments;
  }
  public function setKind($kind)
  {
    $this->kind = $kind;
  }
  public function getKind()
  {
    return $this->kind;
  }
  public function setNextPageToken($nextPageToken)
  {
    $this->nextPageToken = $nextPageToken;
  }
  public function getNextPageToken()
  {
    return $this->nextPageToken;
  }
}
vendor/google/apiclient-services/src/Google/Service/Drive/DriveFileVideoMediaMetadata.php000064400000002254151327705670025554 0ustar00<?php
/*
 * Copyright 2014 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

class Google_Service_Drive_DriveFileVideoMediaMetadata extends Google_Model
{
  public $durationMillis;
  public $height;
  public $width;

  public function setDurationMillis($durationMillis)
  {
    $this->durationMillis = $durationMillis;
  }
  public function getDurationMillis()
  {
    return $this->durationMillis;
  }
  public function setHeight($height)
  {
    $this->height = $height;
  }
  public function getHeight()
  {
    return $this->height;
  }
  public function setWidth($width)
  {
    $this->width = $width;
  }
  public function getWidth()
  {
    return $this->width;
  }
}
vendor/google/apiclient-services/src/Google/Service/Drive/DriveFile.php000064400000032370151327705670022166 0ustar00<?php
/*
 * Copyright 2014 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

class Google_Service_Drive_DriveFile extends Google_Collection
{
  protected $collection_key = 'spaces';
  public $appProperties;
  protected $capabilitiesType = 'Google_Service_Drive_DriveFileCapabilities';
  protected $capabilitiesDataType = '';
  protected $contentHintsType = 'Google_Service_Drive_DriveFileContentHints';
  protected $contentHintsDataType = '';
  public $copyRequiresWriterPermission;
  public $createdTime;
  public $description;
  public $explicitlyTrashed;
  public $exportLinks;
  public $fileExtension;
  public $folderColorRgb;
  public $fullFileExtension;
  public $hasAugmentedPermissions;
  public $hasThumbnail;
  public $headRevisionId;
  public $iconLink;
  public $id;
  protected $imageMediaMetadataType = 'Google_Service_Drive_DriveFileImageMediaMetadata';
  protected $imageMediaMetadataDataType = '';
  public $isAppAuthorized;
  public $kind;
  protected $lastModifyingUserType = 'Google_Service_Drive_User';
  protected $lastModifyingUserDataType = '';
  public $md5Checksum;
  public $mimeType;
  public $modifiedByMe;
  public $modifiedByMeTime;
  public $modifiedTime;
  public $name;
  public $originalFilename;
  public $ownedByMe;
  protected $ownersType = 'Google_Service_Drive_User';
  protected $ownersDataType = 'array';
  public $parents;
  public $permissionIds;
  protected $permissionsType = 'Google_Service_Drive_Permission';
  protected $permissionsDataType = 'array';
  public $properties;
  public $quotaBytesUsed;
  public $shared;
  public $sharedWithMeTime;
  protected $sharingUserType = 'Google_Service_Drive_User';
  protected $sharingUserDataType = '';
  public $size;
  public $spaces;
  public $starred;
  public $teamDriveId;
  public $thumbnailLink;
  public $thumbnailVersion;
  public $trashed;
  public $trashedTime;
  protected $trashingUserType = 'Google_Service_Drive_User';
  protected $trashingUserDataType = '';
  public $version;
  protected $videoMediaMetadataType = 'Google_Service_Drive_DriveFileVideoMediaMetadata';
  protected $videoMediaMetadataDataType = '';
  public $viewedByMe;
  public $viewedByMeTime;
  public $viewersCanCopyContent;
  public $webContentLink;
  public $webViewLink;
  public $writersCanShare;

  public function setAppProperties($appProperties)
  {
    $this->appProperties = $appProperties;
  }
  public function getAppProperties()
  {
    return $this->appProperties;
  }
  /**
   * @param Google_Service_Drive_DriveFileCapabilities
   */
  public function setCapabilities(Google_Service_Drive_DriveFileCapabilities $capabilities)
  {
    $this->capabilities = $capabilities;
  }
  /**
   * @return Google_Service_Drive_DriveFileCapabilities
   */
  public function getCapabilities()
  {
    return $this->capabilities;
  }
  /**
   * @param Google_Service_Drive_DriveFileContentHints
   */
  public function setContentHints(Google_Service_Drive_DriveFileContentHints $contentHints)
  {
    $this->contentHints = $contentHints;
  }
  /**
   * @return Google_Service_Drive_DriveFileContentHints
   */
  public function getContentHints()
  {
    return $this->contentHints;
  }
  public function setCopyRequiresWriterPermission($copyRequiresWriterPermission)
  {
    $this->copyRequiresWriterPermission = $copyRequiresWriterPermission;
  }
  public function getCopyRequiresWriterPermission()
  {
    return $this->copyRequiresWriterPermission;
  }
  public function setCreatedTime($createdTime)
  {
    $this->createdTime = $createdTime;
  }
  public function getCreatedTime()
  {
    return $this->createdTime;
  }
  public function setDescription($description)
  {
    $this->description = $description;
  }
  public function getDescription()
  {
    return $this->description;
  }
  public function setExplicitlyTrashed($explicitlyTrashed)
  {
    $this->explicitlyTrashed = $explicitlyTrashed;
  }
  public function getExplicitlyTrashed()
  {
    return $this->explicitlyTrashed;
  }
  public function setExportLinks($exportLinks)
  {
    $this->exportLinks = $exportLinks;
  }
  public function getExportLinks()
  {
    return $this->exportLinks;
  }
  public function setFileExtension($fileExtension)
  {
    $this->fileExtension = $fileExtension;
  }
  public function getFileExtension()
  {
    return $this->fileExtension;
  }
  public function setFolderColorRgb($folderColorRgb)
  {
    $this->folderColorRgb = $folderColorRgb;
  }
  public function getFolderColorRgb()
  {
    return $this->folderColorRgb;
  }
  public function setFullFileExtension($fullFileExtension)
  {
    $this->fullFileExtension = $fullFileExtension;
  }
  public function getFullFileExtension()
  {
    return $this->fullFileExtension;
  }
  public function setHasAugmentedPermissions($hasAugmentedPermissions)
  {
    $this->hasAugmentedPermissions = $hasAugmentedPermissions;
  }
  public function getHasAugmentedPermissions()
  {
    return $this->hasAugmentedPermissions;
  }
  public function setHasThumbnail($hasThumbnail)
  {
    $this->hasThumbnail = $hasThumbnail;
  }
  public function getHasThumbnail()
  {
    return $this->hasThumbnail;
  }
  public function setHeadRevisionId($headRevisionId)
  {
    $this->headRevisionId = $headRevisionId;
  }
  public function getHeadRevisionId()
  {
    return $this->headRevisionId;
  }
  public function setIconLink($iconLink)
  {
    $this->iconLink = $iconLink;
  }
  public function getIconLink()
  {
    return $this->iconLink;
  }
  public function setId($id)
  {
    $this->id = $id;
  }
  public function getId()
  {
    return $this->id;
  }
  /**
   * @param Google_Service_Drive_DriveFileImageMediaMetadata
   */
  public function setImageMediaMetadata(Google_Service_Drive_DriveFileImageMediaMetadata $imageMediaMetadata)
  {
    $this->imageMediaMetadata = $imageMediaMetadata;
  }
  /**
   * @return Google_Service_Drive_DriveFileImageMediaMetadata
   */
  public function getImageMediaMetadata()
  {
    return $this->imageMediaMetadata;
  }
  public function setIsAppAuthorized($isAppAuthorized)
  {
    $this->isAppAuthorized = $isAppAuthorized;
  }
  public function getIsAppAuthorized()
  {
    return $this->isAppAuthorized;
  }
  public function setKind($kind)
  {
    $this->kind = $kind;
  }
  public function getKind()
  {
    return $this->kind;
  }
  /**
   * @param Google_Service_Drive_User
   */
  public function setLastModifyingUser(Google_Service_Drive_User $lastModifyingUser)
  {
    $this->lastModifyingUser = $lastModifyingUser;
  }
  /**
   * @return Google_Service_Drive_User
   */
  public function getLastModifyingUser()
  {
    return $this->lastModifyingUser;
  }
  public function setMd5Checksum($md5Checksum)
  {
    $this->md5Checksum = $md5Checksum;
  }
  public function getMd5Checksum()
  {
    return $this->md5Checksum;
  }
  public function setMimeType($mimeType)
  {
    $this->mimeType = $mimeType;
  }
  public function getMimeType()
  {
    return $this->mimeType;
  }
  public function setModifiedByMe($modifiedByMe)
  {
    $this->modifiedByMe = $modifiedByMe;
  }
  public function getModifiedByMe()
  {
    return $this->modifiedByMe;
  }
  public function setModifiedByMeTime($modifiedByMeTime)
  {
    $this->modifiedByMeTime = $modifiedByMeTime;
  }
  public function getModifiedByMeTime()
  {
    return $this->modifiedByMeTime;
  }
  public function setModifiedTime($modifiedTime)
  {
    $this->modifiedTime = $modifiedTime;
  }
  public function getModifiedTime()
  {
    return $this->modifiedTime;
  }
  public function setName($name)
  {
    $this->name = $name;
  }
  public function getName()
  {
    return $this->name;
  }
  public function setOriginalFilename($originalFilename)
  {
    $this->originalFilename = $originalFilename;
  }
  public function getOriginalFilename()
  {
    return $this->originalFilename;
  }
  public function setOwnedByMe($ownedByMe)
  {
    $this->ownedByMe = $ownedByMe;
  }
  public function getOwnedByMe()
  {
    return $this->ownedByMe;
  }
  /**
   * @param Google_Service_Drive_User
   */
  public function setOwners($owners)
  {
    $this->owners = $owners;
  }
  /**
   * @return Google_Service_Drive_User
   */
  public function getOwners()
  {
    return $this->owners;
  }
  public function setParents($parents)
  {
    $this->parents = $parents;
  }
  public function getParents()
  {
    return $this->parents;
  }
  public function setPermissionIds($permissionIds)
  {
    $this->permissionIds = $permissionIds;
  }
  public function getPermissionIds()
  {
    return $this->permissionIds;
  }
  /**
   * @param Google_Service_Drive_Permission
   */
  public function setPermissions($permissions)
  {
    $this->permissions = $permissions;
  }
  /**
   * @return Google_Service_Drive_Permission
   */
  public function getPermissions()
  {
    return $this->permissions;
  }
  public function setProperties($properties)
  {
    $this->properties = $properties;
  }
  public function getProperties()
  {
    return $this->properties;
  }
  public function setQuotaBytesUsed($quotaBytesUsed)
  {
    $this->quotaBytesUsed = $quotaBytesUsed;
  }
  public function getQuotaBytesUsed()
  {
    return $this->quotaBytesUsed;
  }
  public function setShared($shared)
  {
    $this->shared = $shared;
  }
  public function getShared()
  {
    return $this->shared;
  }
  public function setSharedWithMeTime($sharedWithMeTime)
  {
    $this->sharedWithMeTime = $sharedWithMeTime;
  }
  public function getSharedWithMeTime()
  {
    return $this->sharedWithMeTime;
  }
  /**
   * @param Google_Service_Drive_User
   */
  public function setSharingUser(Google_Service_Drive_User $sharingUser)
  {
    $this->sharingUser = $sharingUser;
  }
  /**
   * @return Google_Service_Drive_User
   */
  public function getSharingUser()
  {
    return $this->sharingUser;
  }
  public function setSize($size)
  {
    $this->size = $size;
  }
  public function getSize()
  {
    return $this->size;
  }
  public function setSpaces($spaces)
  {
    $this->spaces = $spaces;
  }
  public function getSpaces()
  {
    return $this->spaces;
  }
  public function setStarred($starred)
  {
    $this->starred = $starred;
  }
  public function getStarred()
  {
    return $this->starred;
  }
  public function setTeamDriveId($teamDriveId)
  {
    $this->teamDriveId = $teamDriveId;
  }
  public function getTeamDriveId()
  {
    return $this->teamDriveId;
  }
  public function setThumbnailLink($thumbnailLink)
  {
    $this->thumbnailLink = $thumbnailLink;
  }
  public function getThumbnailLink()
  {
    return $this->thumbnailLink;
  }
  public function setThumbnailVersion($thumbnailVersion)
  {
    $this->thumbnailVersion = $thumbnailVersion;
  }
  public function getThumbnailVersion()
  {
    return $this->thumbnailVersion;
  }
  public function setTrashed($trashed)
  {
    $this->trashed = $trashed;
  }
  public function getTrashed()
  {
    return $this->trashed;
  }
  public function setTrashedTime($trashedTime)
  {
    $this->trashedTime = $trashedTime;
  }
  public function getTrashedTime()
  {
    return $this->trashedTime;
  }
  /**
   * @param Google_Service_Drive_User
   */
  public function setTrashingUser(Google_Service_Drive_User $trashingUser)
  {
    $this->trashingUser = $trashingUser;
  }
  /**
   * @return Google_Service_Drive_User
   */
  public function getTrashingUser()
  {
    return $this->trashingUser;
  }
  public function setVersion($version)
  {
    $this->version = $version;
  }
  public function getVersion()
  {
    return $this->version;
  }
  /**
   * @param Google_Service_Drive_DriveFileVideoMediaMetadata
   */
  public function setVideoMediaMetadata(Google_Service_Drive_DriveFileVideoMediaMetadata $videoMediaMetadata)
  {
    $this->videoMediaMetadata = $videoMediaMetadata;
  }
  /**
   * @return Google_Service_Drive_DriveFileVideoMediaMetadata
   */
  public function getVideoMediaMetadata()
  {
    return $this->videoMediaMetadata;
  }
  public function setViewedByMe($viewedByMe)
  {
    $this->viewedByMe = $viewedByMe;
  }
  public function getViewedByMe()
  {
    return $this->viewedByMe;
  }
  public function setViewedByMeTime($viewedByMeTime)
  {
    $this->viewedByMeTime = $viewedByMeTime;
  }
  public function getViewedByMeTime()
  {
    return $this->viewedByMeTime;
  }
  public function setViewersCanCopyContent($viewersCanCopyContent)
  {
    $this->viewersCanCopyContent = $viewersCanCopyContent;
  }
  public function getViewersCanCopyContent()
  {
    return $this->viewersCanCopyContent;
  }
  public function setWebContentLink($webContentLink)
  {
    $this->webContentLink = $webContentLink;
  }
  public function getWebContentLink()
  {
    return $this->webContentLink;
  }
  public function setWebViewLink($webViewLink)
  {
    $this->webViewLink = $webViewLink;
  }
  public function getWebViewLink()
  {
    return $this->webViewLink;
  }
  public function setWritersCanShare($writersCanShare)
  {
    $this->writersCanShare = $writersCanShare;
  }
  public function getWritersCanShare()
  {
    return $this->writersCanShare;
  }
}
vendor/google/apiclient-services/src/Google/Service/Drive/Revision.php000064400000006365151327705670022120 0ustar00<?php
/*
 * Copyright 2014 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

class Google_Service_Drive_Revision extends Google_Model
{
  public $exportLinks;
  public $id;
  public $keepForever;
  public $kind;
  protected $lastModifyingUserType = 'Google_Service_Drive_User';
  protected $lastModifyingUserDataType = '';
  public $md5Checksum;
  public $mimeType;
  public $modifiedTime;
  public $originalFilename;
  public $publishAuto;
  public $published;
  public $publishedOutsideDomain;
  public $size;

  public function setExportLinks($exportLinks)
  {
    $this->exportLinks = $exportLinks;
  }
  public function getExportLinks()
  {
    return $this->exportLinks;
  }
  public function setId($id)
  {
    $this->id = $id;
  }
  public function getId()
  {
    return $this->id;
  }
  public function setKeepForever($keepForever)
  {
    $this->keepForever = $keepForever;
  }
  public function getKeepForever()
  {
    return $this->keepForever;
  }
  public function setKind($kind)
  {
    $this->kind = $kind;
  }
  public function getKind()
  {
    return $this->kind;
  }
  /**
   * @param Google_Service_Drive_User
   */
  public function setLastModifyingUser(Google_Service_Drive_User $lastModifyingUser)
  {
    $this->lastModifyingUser = $lastModifyingUser;
  }
  /**
   * @return Google_Service_Drive_User
   */
  public function getLastModifyingUser()
  {
    return $this->lastModifyingUser;
  }
  public function setMd5Checksum($md5Checksum)
  {
    $this->md5Checksum = $md5Checksum;
  }
  public function getMd5Checksum()
  {
    return $this->md5Checksum;
  }
  public function setMimeType($mimeType)
  {
    $this->mimeType = $mimeType;
  }
  public function getMimeType()
  {
    return $this->mimeType;
  }
  public function setModifiedTime($modifiedTime)
  {
    $this->modifiedTime = $modifiedTime;
  }
  public function getModifiedTime()
  {
    return $this->modifiedTime;
  }
  public function setOriginalFilename($originalFilename)
  {
    $this->originalFilename = $originalFilename;
  }
  public function getOriginalFilename()
  {
    return $this->originalFilename;
  }
  public function setPublishAuto($publishAuto)
  {
    $this->publishAuto = $publishAuto;
  }
  public function getPublishAuto()
  {
    return $this->publishAuto;
  }
  public function setPublished($published)
  {
    $this->published = $published;
  }
  public function getPublished()
  {
    return $this->published;
  }
  public function setPublishedOutsideDomain($publishedOutsideDomain)
  {
    $this->publishedOutsideDomain = $publishedOutsideDomain;
  }
  public function getPublishedOutsideDomain()
  {
    return $this->publishedOutsideDomain;
  }
  public function setSize($size)
  {
    $this->size = $size;
  }
  public function getSize()
  {
    return $this->size;
  }
}
vendor/google/apiclient-services/src/Google/Service/Drive/TeamDriveCapabilities.php000064400000012206151327705670024503 0ustar00<?php
/*
 * Copyright 2014 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

class Google_Service_Drive_TeamDriveCapabilities extends Google_Model
{
  public $canAddChildren;
  public $canChangeCopyRequiresWriterPermissionRestriction;
  public $canChangeDomainUsersOnlyRestriction;
  public $canChangeTeamDriveBackground;
  public $canChangeTeamMembersOnlyRestriction;
  public $canComment;
  public $canCopy;
  public $canDeleteChildren;
  public $canDeleteTeamDrive;
  public $canDownload;
  public $canEdit;
  public $canListChildren;
  public $canManageMembers;
  public $canReadRevisions;
  public $canRemoveChildren;
  public $canRename;
  public $canRenameTeamDrive;
  public $canShare;
  public $canTrashChildren;

  public function setCanAddChildren($canAddChildren)
  {
    $this->canAddChildren = $canAddChildren;
  }
  public function getCanAddChildren()
  {
    return $this->canAddChildren;
  }
  public function setCanChangeCopyRequiresWriterPermissionRestriction($canChangeCopyRequiresWriterPermissionRestriction)
  {
    $this->canChangeCopyRequiresWriterPermissionRestriction = $canChangeCopyRequiresWriterPermissionRestriction;
  }
  public function getCanChangeCopyRequiresWriterPermissionRestriction()
  {
    return $this->canChangeCopyRequiresWriterPermissionRestriction;
  }
  public function setCanChangeDomainUsersOnlyRestriction($canChangeDomainUsersOnlyRestriction)
  {
    $this->canChangeDomainUsersOnlyRestriction = $canChangeDomainUsersOnlyRestriction;
  }
  public function getCanChangeDomainUsersOnlyRestriction()
  {
    return $this->canChangeDomainUsersOnlyRestriction;
  }
  public function setCanChangeTeamDriveBackground($canChangeTeamDriveBackground)
  {
    $this->canChangeTeamDriveBackground = $canChangeTeamDriveBackground;
  }
  public function getCanChangeTeamDriveBackground()
  {
    return $this->canChangeTeamDriveBackground;
  }
  public function setCanChangeTeamMembersOnlyRestriction($canChangeTeamMembersOnlyRestriction)
  {
    $this->canChangeTeamMembersOnlyRestriction = $canChangeTeamMembersOnlyRestriction;
  }
  public function getCanChangeTeamMembersOnlyRestriction()
  {
    return $this->canChangeTeamMembersOnlyRestriction;
  }
  public function setCanComment($canComment)
  {
    $this->canComment = $canComment;
  }
  public function getCanComment()
  {
    return $this->canComment;
  }
  public function setCanCopy($canCopy)
  {
    $this->canCopy = $canCopy;
  }
  public function getCanCopy()
  {
    return $this->canCopy;
  }
  public function setCanDeleteChildren($canDeleteChildren)
  {
    $this->canDeleteChildren = $canDeleteChildren;
  }
  public function getCanDeleteChildren()
  {
    return $this->canDeleteChildren;
  }
  public function setCanDeleteTeamDrive($canDeleteTeamDrive)
  {
    $this->canDeleteTeamDrive = $canDeleteTeamDrive;
  }
  public function getCanDeleteTeamDrive()
  {
    return $this->canDeleteTeamDrive;
  }
  public function setCanDownload($canDownload)
  {
    $this->canDownload = $canDownload;
  }
  public function getCanDownload()
  {
    return $this->canDownload;
  }
  public function setCanEdit($canEdit)
  {
    $this->canEdit = $canEdit;
  }
  public function getCanEdit()
  {
    return $this->canEdit;
  }
  public function setCanListChildren($canListChildren)
  {
    $this->canListChildren = $canListChildren;
  }
  public function getCanListChildren()
  {
    return $this->canListChildren;
  }
  public function setCanManageMembers($canManageMembers)
  {
    $this->canManageMembers = $canManageMembers;
  }
  public function getCanManageMembers()
  {
    return $this->canManageMembers;
  }
  public function setCanReadRevisions($canReadRevisions)
  {
    $this->canReadRevisions = $canReadRevisions;
  }
  public function getCanReadRevisions()
  {
    return $this->canReadRevisions;
  }
  public function setCanRemoveChildren($canRemoveChildren)
  {
    $this->canRemoveChildren = $canRemoveChildren;
  }
  public function getCanRemoveChildren()
  {
    return $this->canRemoveChildren;
  }
  public function setCanRename($canRename)
  {
    $this->canRename = $canRename;
  }
  public function getCanRename()
  {
    return $this->canRename;
  }
  public function setCanRenameTeamDrive($canRenameTeamDrive)
  {
    $this->canRenameTeamDrive = $canRenameTeamDrive;
  }
  public function getCanRenameTeamDrive()
  {
    return $this->canRenameTeamDrive;
  }
  public function setCanShare($canShare)
  {
    $this->canShare = $canShare;
  }
  public function getCanShare()
  {
    return $this->canShare;
  }
  public function setCanTrashChildren($canTrashChildren)
  {
    $this->canTrashChildren = $canTrashChildren;
  }
  public function getCanTrashChildren()
  {
    return $this->canTrashChildren;
  }
}
vendor/google/apiclient-services/src/Google/Service/Drive/TeamDriveRestrictions.php000064400000003247151327705670024607 0ustar00<?php
/*
 * Copyright 2014 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

class Google_Service_Drive_TeamDriveRestrictions extends Google_Model
{
  public $adminManagedRestrictions;
  public $copyRequiresWriterPermission;
  public $domainUsersOnly;
  public $teamMembersOnly;

  public function setAdminManagedRestrictions($adminManagedRestrictions)
  {
    $this->adminManagedRestrictions = $adminManagedRestrictions;
  }
  public function getAdminManagedRestrictions()
  {
    return $this->adminManagedRestrictions;
  }
  public function setCopyRequiresWriterPermission($copyRequiresWriterPermission)
  {
    $this->copyRequiresWriterPermission = $copyRequiresWriterPermission;
  }
  public function getCopyRequiresWriterPermission()
  {
    return $this->copyRequiresWriterPermission;
  }
  public function setDomainUsersOnly($domainUsersOnly)
  {
    $this->domainUsersOnly = $domainUsersOnly;
  }
  public function getDomainUsersOnly()
  {
    return $this->domainUsersOnly;
  }
  public function setTeamMembersOnly($teamMembersOnly)
  {
    $this->teamMembersOnly = $teamMembersOnly;
  }
  public function getTeamMembersOnly()
  {
    return $this->teamMembersOnly;
  }
}
vendor/google/apiclient-services/src/Google/Service/Drive/ChangeList.php000064400000003143151327705670022332 0ustar00<?php
/*
 * Copyright 2014 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

class Google_Service_Drive_ChangeList extends Google_Collection
{
  protected $collection_key = 'changes';
  protected $changesType = 'Google_Service_Drive_Change';
  protected $changesDataType = 'array';
  public $kind;
  public $newStartPageToken;
  public $nextPageToken;

  /**
   * @param Google_Service_Drive_Change
   */
  public function setChanges($changes)
  {
    $this->changes = $changes;
  }
  /**
   * @return Google_Service_Drive_Change
   */
  public function getChanges()
  {
    return $this->changes;
  }
  public function setKind($kind)
  {
    $this->kind = $kind;
  }
  public function getKind()
  {
    return $this->kind;
  }
  public function setNewStartPageToken($newStartPageToken)
  {
    $this->newStartPageToken = $newStartPageToken;
  }
  public function getNewStartPageToken()
  {
    return $this->newStartPageToken;
  }
  public function setNextPageToken($nextPageToken)
  {
    $this->nextPageToken = $nextPageToken;
  }
  public function getNextPageToken()
  {
    return $this->nextPageToken;
  }
}
vendor/google/apiclient-services/src/Google/Service/Drive/CommentQuotedFileContent.php000064400000001743151327705670025234 0ustar00<?php
/*
 * Copyright 2014 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

class Google_Service_Drive_CommentQuotedFileContent extends Google_Model
{
  public $mimeType;
  public $value;

  public function setMimeType($mimeType)
  {
    $this->mimeType = $mimeType;
  }
  public function getMimeType()
  {
    return $this->mimeType;
  }
  public function setValue($value)
  {
    $this->value = $value;
  }
  public function getValue()
  {
    return $this->value;
  }
}
vendor/google/apiclient-services/src/Google/Service/Drive/ReplyList.php000064400000002566151327705670022250 0ustar00<?php
/*
 * Copyright 2014 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

class Google_Service_Drive_ReplyList extends Google_Collection
{
  protected $collection_key = 'replies';
  public $kind;
  public $nextPageToken;
  protected $repliesType = 'Google_Service_Drive_Reply';
  protected $repliesDataType = 'array';

  public function setKind($kind)
  {
    $this->kind = $kind;
  }
  public function getKind()
  {
    return $this->kind;
  }
  public function setNextPageToken($nextPageToken)
  {
    $this->nextPageToken = $nextPageToken;
  }
  public function getNextPageToken()
  {
    return $this->nextPageToken;
  }
  /**
   * @param Google_Service_Drive_Reply
   */
  public function setReplies($replies)
  {
    $this->replies = $replies;
  }
  /**
   * @return Google_Service_Drive_Reply
   */
  public function getReplies()
  {
    return $this->replies;
  }
}
vendor/google/apiclient-services/src/Google/Service/Drive/RevisionList.php000064400000002624151327705670022746 0ustar00<?php
/*
 * Copyright 2014 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

class Google_Service_Drive_RevisionList extends Google_Collection
{
  protected $collection_key = 'revisions';
  public $kind;
  public $nextPageToken;
  protected $revisionsType = 'Google_Service_Drive_Revision';
  protected $revisionsDataType = 'array';

  public function setKind($kind)
  {
    $this->kind = $kind;
  }
  public function getKind()
  {
    return $this->kind;
  }
  public function setNextPageToken($nextPageToken)
  {
    $this->nextPageToken = $nextPageToken;
  }
  public function getNextPageToken()
  {
    return $this->nextPageToken;
  }
  /**
   * @param Google_Service_Drive_Revision
   */
  public function setRevisions($revisions)
  {
    $this->revisions = $revisions;
  }
  /**
   * @return Google_Service_Drive_Revision
   */
  public function getRevisions()
  {
    return $this->revisions;
  }
}
vendor/google/apiclient-services/src/Google/Service/Drive/PermissionTeamDrivePermissionDetails.php000064400000002717151327705670027627 0ustar00<?php
/*
 * Copyright 2014 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

class Google_Service_Drive_PermissionTeamDrivePermissionDetails extends Google_Model
{
  public $inherited;
  public $inheritedFrom;
  public $role;
  public $teamDrivePermissionType;

  public function setInherited($inherited)
  {
    $this->inherited = $inherited;
  }
  public function getInherited()
  {
    return $this->inherited;
  }
  public function setInheritedFrom($inheritedFrom)
  {
    $this->inheritedFrom = $inheritedFrom;
  }
  public function getInheritedFrom()
  {
    return $this->inheritedFrom;
  }
  public function setRole($role)
  {
    $this->role = $role;
  }
  public function getRole()
  {
    return $this->role;
  }
  public function setTeamDrivePermissionType($teamDrivePermissionType)
  {
    $this->teamDrivePermissionType = $teamDrivePermissionType;
  }
  public function getTeamDrivePermissionType()
  {
    return $this->teamDrivePermissionType;
  }
}
vendor/google/apiclient-services/src/Google/Service/Drive/Change.php000064400000004456151327705670021506 0ustar00<?php
/*
 * Copyright 2014 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

class Google_Service_Drive_Change extends Google_Model
{
  protected $fileType = 'Google_Service_Drive_DriveFile';
  protected $fileDataType = '';
  public $fileId;
  public $kind;
  public $removed;
  protected $teamDriveType = 'Google_Service_Drive_TeamDrive';
  protected $teamDriveDataType = '';
  public $teamDriveId;
  public $time;
  public $type;

  /**
   * @param Google_Service_Drive_DriveFile
   */
  public function setFile(Google_Service_Drive_DriveFile $file)
  {
    $this->file = $file;
  }
  /**
   * @return Google_Service_Drive_DriveFile
   */
  public function getFile()
  {
    return $this->file;
  }
  public function setFileId($fileId)
  {
    $this->fileId = $fileId;
  }
  public function getFileId()
  {
    return $this->fileId;
  }
  public function setKind($kind)
  {
    $this->kind = $kind;
  }
  public function getKind()
  {
    return $this->kind;
  }
  public function setRemoved($removed)
  {
    $this->removed = $removed;
  }
  public function getRemoved()
  {
    return $this->removed;
  }
  /**
   * @param Google_Service_Drive_TeamDrive
   */
  public function setTeamDrive(Google_Service_Drive_TeamDrive $teamDrive)
  {
    $this->teamDrive = $teamDrive;
  }
  /**
   * @return Google_Service_Drive_TeamDrive
   */
  public function getTeamDrive()
  {
    return $this->teamDrive;
  }
  public function setTeamDriveId($teamDriveId)
  {
    $this->teamDriveId = $teamDriveId;
  }
  public function getTeamDriveId()
  {
    return $this->teamDriveId;
  }
  public function setTime($time)
  {
    $this->time = $time;
  }
  public function getTime()
  {
    return $this->time;
  }
  public function setType($type)
  {
    $this->type = $type;
  }
  public function getType()
  {
    return $this->type;
  }
}
vendor/google/apiclient-services/src/Google/Service/Drive/Comment.php000064400000006543151327705670021722 0ustar00<?php
/*
 * Copyright 2014 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

class Google_Service_Drive_Comment extends Google_Collection
{
  protected $collection_key = 'replies';
  public $anchor;
  protected $authorType = 'Google_Service_Drive_User';
  protected $authorDataType = '';
  public $content;
  public $createdTime;
  public $deleted;
  public $htmlContent;
  public $id;
  public $kind;
  public $modifiedTime;
  protected $quotedFileContentType = 'Google_Service_Drive_CommentQuotedFileContent';
  protected $quotedFileContentDataType = '';
  protected $repliesType = 'Google_Service_Drive_Reply';
  protected $repliesDataType = 'array';
  public $resolved;

  public function setAnchor($anchor)
  {
    $this->anchor = $anchor;
  }
  public function getAnchor()
  {
    return $this->anchor;
  }
  /**
   * @param Google_Service_Drive_User
   */
  public function setAuthor(Google_Service_Drive_User $author)
  {
    $this->author = $author;
  }
  /**
   * @return Google_Service_Drive_User
   */
  public function getAuthor()
  {
    return $this->author;
  }
  public function setContent($content)
  {
    $this->content = $content;
  }
  public function getContent()
  {
    return $this->content;
  }
  public function setCreatedTime($createdTime)
  {
    $this->createdTime = $createdTime;
  }
  public function getCreatedTime()
  {
    return $this->createdTime;
  }
  public function setDeleted($deleted)
  {
    $this->deleted = $deleted;
  }
  public function getDeleted()
  {
    return $this->deleted;
  }
  public function setHtmlContent($htmlContent)
  {
    $this->htmlContent = $htmlContent;
  }
  public function getHtmlContent()
  {
    return $this->htmlContent;
  }
  public function setId($id)
  {
    $this->id = $id;
  }
  public function getId()
  {
    return $this->id;
  }
  public function setKind($kind)
  {
    $this->kind = $kind;
  }
  public function getKind()
  {
    return $this->kind;
  }
  public function setModifiedTime($modifiedTime)
  {
    $this->modifiedTime = $modifiedTime;
  }
  public function getModifiedTime()
  {
    return $this->modifiedTime;
  }
  /**
   * @param Google_Service_Drive_CommentQuotedFileContent
   */
  public function setQuotedFileContent(Google_Service_Drive_CommentQuotedFileContent $quotedFileContent)
  {
    $this->quotedFileContent = $quotedFileContent;
  }
  /**
   * @return Google_Service_Drive_CommentQuotedFileContent
   */
  public function getQuotedFileContent()
  {
    return $this->quotedFileContent;
  }
  /**
   * @param Google_Service_Drive_Reply
   */
  public function setReplies($replies)
  {
    $this->replies = $replies;
  }
  /**
   * @return Google_Service_Drive_Reply
   */
  public function getReplies()
  {
    return $this->replies;
  }
  public function setResolved($resolved)
  {
    $this->resolved = $resolved;
  }
  public function getResolved()
  {
    return $this->resolved;
  }
}
vendor/google/apiclient-services/src/Google/Service/Drive/AboutStorageQuota.php000064400000002566151327705670023732 0ustar00<?php
/*
 * Copyright 2014 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

class Google_Service_Drive_AboutStorageQuota extends Google_Model
{
  public $limit;
  public $usage;
  public $usageInDrive;
  public $usageInDriveTrash;

  public function setLimit($limit)
  {
    $this->limit = $limit;
  }
  public function getLimit()
  {
    return $this->limit;
  }
  public function setUsage($usage)
  {
    $this->usage = $usage;
  }
  public function getUsage()
  {
    return $this->usage;
  }
  public function setUsageInDrive($usageInDrive)
  {
    $this->usageInDrive = $usageInDrive;
  }
  public function getUsageInDrive()
  {
    return $this->usageInDrive;
  }
  public function setUsageInDriveTrash($usageInDriveTrash)
  {
    $this->usageInDriveTrash = $usageInDriveTrash;
  }
  public function getUsageInDriveTrash()
  {
    return $this->usageInDriveTrash;
  }
}
vendor/google/apiclient-services/src/Google/Service/Drive/FileList.php000064400000003121151327705670022020 0ustar00<?php
/*
 * Copyright 2014 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

class Google_Service_Drive_FileList extends Google_Collection
{
  protected $collection_key = 'files';
  protected $filesType = 'Google_Service_Drive_DriveFile';
  protected $filesDataType = 'array';
  public $incompleteSearch;
  public $kind;
  public $nextPageToken;

  /**
   * @param Google_Service_Drive_DriveFile
   */
  public function setFiles($files)
  {
    $this->files = $files;
  }
  /**
   * @return Google_Service_Drive_DriveFile
   */
  public function getFiles()
  {
    return $this->files;
  }
  public function setIncompleteSearch($incompleteSearch)
  {
    $this->incompleteSearch = $incompleteSearch;
  }
  public function getIncompleteSearch()
  {
    return $this->incompleteSearch;
  }
  public function setKind($kind)
  {
    $this->kind = $kind;
  }
  public function getKind()
  {
    return $this->kind;
  }
  public function setNextPageToken($nextPageToken)
  {
    $this->nextPageToken = $nextPageToken;
  }
  public function getNextPageToken()
  {
    return $this->nextPageToken;
  }
}
vendor/google/apiclient-services/src/Google/Service/Drive/Permission.php000064400000006207151327705670022445 0ustar00<?php
/*
 * Copyright 2014 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

class Google_Service_Drive_Permission extends Google_Collection
{
  protected $collection_key = 'teamDrivePermissionDetails';
  public $allowFileDiscovery;
  public $deleted;
  public $displayName;
  public $domain;
  public $emailAddress;
  public $expirationTime;
  public $id;
  public $kind;
  public $photoLink;
  public $role;
  protected $teamDrivePermissionDetailsType = 'Google_Service_Drive_PermissionTeamDrivePermissionDetails';
  protected $teamDrivePermissionDetailsDataType = 'array';
  public $type;

  public function setAllowFileDiscovery($allowFileDiscovery)
  {
    $this->allowFileDiscovery = $allowFileDiscovery;
  }
  public function getAllowFileDiscovery()
  {
    return $this->allowFileDiscovery;
  }
  public function setDeleted($deleted)
  {
    $this->deleted = $deleted;
  }
  public function getDeleted()
  {
    return $this->deleted;
  }
  public function setDisplayName($displayName)
  {
    $this->displayName = $displayName;
  }
  public function getDisplayName()
  {
    return $this->displayName;
  }
  public function setDomain($domain)
  {
    $this->domain = $domain;
  }
  public function getDomain()
  {
    return $this->domain;
  }
  public function setEmailAddress($emailAddress)
  {
    $this->emailAddress = $emailAddress;
  }
  public function getEmailAddress()
  {
    return $this->emailAddress;
  }
  public function setExpirationTime($expirationTime)
  {
    $this->expirationTime = $expirationTime;
  }
  public function getExpirationTime()
  {
    return $this->expirationTime;
  }
  public function setId($id)
  {
    $this->id = $id;
  }
  public function getId()
  {
    return $this->id;
  }
  public function setKind($kind)
  {
    $this->kind = $kind;
  }
  public function getKind()
  {
    return $this->kind;
  }
  public function setPhotoLink($photoLink)
  {
    $this->photoLink = $photoLink;
  }
  public function getPhotoLink()
  {
    return $this->photoLink;
  }
  public function setRole($role)
  {
    $this->role = $role;
  }
  public function getRole()
  {
    return $this->role;
  }
  /**
   * @param Google_Service_Drive_PermissionTeamDrivePermissionDetails
   */
  public function setTeamDrivePermissionDetails($teamDrivePermissionDetails)
  {
    $this->teamDrivePermissionDetails = $teamDrivePermissionDetails;
  }
  /**
   * @return Google_Service_Drive_PermissionTeamDrivePermissionDetails
   */
  public function getTeamDrivePermissionDetails()
  {
    return $this->teamDrivePermissionDetails;
  }
  public function setType($type)
  {
    $this->type = $type;
  }
  public function getType()
  {
    return $this->type;
  }
}
vendor/google/apiclient-services/src/Google/Service/Drive/DriveFileImageMediaMetadata.php000064400000011266151327705670025533 0ustar00<?php
/*
 * Copyright 2014 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

class Google_Service_Drive_DriveFileImageMediaMetadata extends Google_Model
{
  public $aperture;
  public $cameraMake;
  public $cameraModel;
  public $colorSpace;
  public $exposureBias;
  public $exposureMode;
  public $exposureTime;
  public $flashUsed;
  public $focalLength;
  public $height;
  public $isoSpeed;
  public $lens;
  protected $locationType = 'Google_Service_Drive_DriveFileImageMediaMetadataLocation';
  protected $locationDataType = '';
  public $maxApertureValue;
  public $meteringMode;
  public $rotation;
  public $sensor;
  public $subjectDistance;
  public $time;
  public $whiteBalance;
  public $width;

  public function setAperture($aperture)
  {
    $this->aperture = $aperture;
  }
  public function getAperture()
  {
    return $this->aperture;
  }
  public function setCameraMake($cameraMake)
  {
    $this->cameraMake = $cameraMake;
  }
  public function getCameraMake()
  {
    return $this->cameraMake;
  }
  public function setCameraModel($cameraModel)
  {
    $this->cameraModel = $cameraModel;
  }
  public function getCameraModel()
  {
    return $this->cameraModel;
  }
  public function setColorSpace($colorSpace)
  {
    $this->colorSpace = $colorSpace;
  }
  public function getColorSpace()
  {
    return $this->colorSpace;
  }
  public function setExposureBias($exposureBias)
  {
    $this->exposureBias = $exposureBias;
  }
  public function getExposureBias()
  {
    return $this->exposureBias;
  }
  public function setExposureMode($exposureMode)
  {
    $this->exposureMode = $exposureMode;
  }
  public function getExposureMode()
  {
    return $this->exposureMode;
  }
  public function setExposureTime($exposureTime)
  {
    $this->exposureTime = $exposureTime;
  }
  public function getExposureTime()
  {
    return $this->exposureTime;
  }
  public function setFlashUsed($flashUsed)
  {
    $this->flashUsed = $flashUsed;
  }
  public function getFlashUsed()
  {
    return $this->flashUsed;
  }
  public function setFocalLength($focalLength)
  {
    $this->focalLength = $focalLength;
  }
  public function getFocalLength()
  {
    return $this->focalLength;
  }
  public function setHeight($height)
  {
    $this->height = $height;
  }
  public function getHeight()
  {
    return $this->height;
  }
  public function setIsoSpeed($isoSpeed)
  {
    $this->isoSpeed = $isoSpeed;
  }
  public function getIsoSpeed()
  {
    return $this->isoSpeed;
  }
  public function setLens($lens)
  {
    $this->lens = $lens;
  }
  public function getLens()
  {
    return $this->lens;
  }
  /**
   * @param Google_Service_Drive_DriveFileImageMediaMetadataLocation
   */
  public function setLocation(Google_Service_Drive_DriveFileImageMediaMetadataLocation $location)
  {
    $this->location = $location;
  }
  /**
   * @return Google_Service_Drive_DriveFileImageMediaMetadataLocation
   */
  public function getLocation()
  {
    return $this->location;
  }
  public function setMaxApertureValue($maxApertureValue)
  {
    $this->maxApertureValue = $maxApertureValue;
  }
  public function getMaxApertureValue()
  {
    return $this->maxApertureValue;
  }
  public function setMeteringMode($meteringMode)
  {
    $this->meteringMode = $meteringMode;
  }
  public function getMeteringMode()
  {
    return $this->meteringMode;
  }
  public function setRotation($rotation)
  {
    $this->rotation = $rotation;
  }
  public function getRotation()
  {
    return $this->rotation;
  }
  public function setSensor($sensor)
  {
    $this->sensor = $sensor;
  }
  public function getSensor()
  {
    return $this->sensor;
  }
  public function setSubjectDistance($subjectDistance)
  {
    $this->subjectDistance = $subjectDistance;
  }
  public function getSubjectDistance()
  {
    return $this->subjectDistance;
  }
  public function setTime($time)
  {
    $this->time = $time;
  }
  public function getTime()
  {
    return $this->time;
  }
  public function setWhiteBalance($whiteBalance)
  {
    $this->whiteBalance = $whiteBalance;
  }
  public function getWhiteBalance()
  {
    return $this->whiteBalance;
  }
  public function setWidth($width)
  {
    $this->width = $width;
  }
  public function getWidth()
  {
    return $this->width;
  }
}
vendor/google/apiclient-services/src/Google/Service/Drive/Channel.php000064400000004325151327705670021664 0ustar00<?php
/*
 * Copyright 2014 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

class Google_Service_Drive_Channel extends Google_Model
{
  public $address;
  public $expiration;
  public $id;
  public $kind;
  public $params;
  public $payload;
  public $resourceId;
  public $resourceUri;
  public $token;
  public $type;

  public function setAddress($address)
  {
    $this->address = $address;
  }
  public function getAddress()
  {
    return $this->address;
  }
  public function setExpiration($expiration)
  {
    $this->expiration = $expiration;
  }
  public function getExpiration()
  {
    return $this->expiration;
  }
  public function setId($id)
  {
    $this->id = $id;
  }
  public function getId()
  {
    return $this->id;
  }
  public function setKind($kind)
  {
    $this->kind = $kind;
  }
  public function getKind()
  {
    return $this->kind;
  }
  public function setParams($params)
  {
    $this->params = $params;
  }
  public function getParams()
  {
    return $this->params;
  }
  public function setPayload($payload)
  {
    $this->payload = $payload;
  }
  public function getPayload()
  {
    return $this->payload;
  }
  public function setResourceId($resourceId)
  {
    $this->resourceId = $resourceId;
  }
  public function getResourceId()
  {
    return $this->resourceId;
  }
  public function setResourceUri($resourceUri)
  {
    $this->resourceUri = $resourceUri;
  }
  public function getResourceUri()
  {
    return $this->resourceUri;
  }
  public function setToken($token)
  {
    $this->token = $token;
  }
  public function getToken()
  {
    return $this->token;
  }
  public function setType($type)
  {
    $this->type = $type;
  }
  public function getType()
  {
    return $this->type;
  }
}
vendor/google/apiclient-services/src/Google/Service/Drive/StartPageToken.php000064400000001774151327705670023214 0ustar00<?php
/*
 * Copyright 2014 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

class Google_Service_Drive_StartPageToken extends Google_Model
{
  public $kind;
  public $startPageToken;

  public function setKind($kind)
  {
    $this->kind = $kind;
  }
  public function getKind()
  {
    return $this->kind;
  }
  public function setStartPageToken($startPageToken)
  {
    $this->startPageToken = $startPageToken;
  }
  public function getStartPageToken()
  {
    return $this->startPageToken;
  }
}
vendor/google/apiclient-services/src/Google/Service/Drive/PermissionList.php000064400000002656151327705670023305 0ustar00<?php
/*
 * Copyright 2014 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

class Google_Service_Drive_PermissionList extends Google_Collection
{
  protected $collection_key = 'permissions';
  public $kind;
  public $nextPageToken;
  protected $permissionsType = 'Google_Service_Drive_Permission';
  protected $permissionsDataType = 'array';

  public function setKind($kind)
  {
    $this->kind = $kind;
  }
  public function getKind()
  {
    return $this->kind;
  }
  public function setNextPageToken($nextPageToken)
  {
    $this->nextPageToken = $nextPageToken;
  }
  public function getNextPageToken()
  {
    return $this->nextPageToken;
  }
  /**
   * @param Google_Service_Drive_Permission
   */
  public function setPermissions($permissions)
  {
    $this->permissions = $permissions;
  }
  /**
   * @return Google_Service_Drive_Permission
   */
  public function getPermissions()
  {
    return $this->permissions;
  }
}
vendor/google/apiclient-services/src/Google/Service/Drive.php000064400000076546151327705670020332 0ustar00<?php
/*
 * Copyright 2014 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

/**
 * Service definition for Drive (v3).
 *
 * <p>
 * Manages files in Drive including uploading, downloading, searching, detecting
 * changes, and updating sharing permissions.</p>
 *
 * <p>
 * For more information about this service, see the API
 * <a href="https://developers.google.com/drive/" target="_blank">Documentation</a>
 * </p>
 *
 * @author Google, Inc.
 */
class Google_Service_Drive extends Google_Service
{
  /** See, edit, create, and delete all of your Google Drive files. */
  const DRIVE =
      "https://www.googleapis.com/auth/drive";
  /** View and manage its own configuration data in your Google Drive. */
  const DRIVE_APPDATA =
      "https://www.googleapis.com/auth/drive.appdata";
  /** View and manage Google Drive files and folders that you have opened or created with this app. */
  const DRIVE_FILE =
      "https://www.googleapis.com/auth/drive.file";
  /** View and manage metadata of files in your Google Drive. */
  const DRIVE_METADATA =
      "https://www.googleapis.com/auth/drive.metadata";
  /** View metadata for files in your Google Drive. */
  const DRIVE_METADATA_READONLY =
      "https://www.googleapis.com/auth/drive.metadata.readonly";
  /** View the photos, videos and albums in your Google Photos. */
  const DRIVE_PHOTOS_READONLY =
      "https://www.googleapis.com/auth/drive.photos.readonly";
  /** See and download all your Google Drive files. */
  const DRIVE_READONLY =
      "https://www.googleapis.com/auth/drive.readonly";
  /** Modify your Google Apps Script scripts' behavior. */
  const DRIVE_SCRIPTS =
      "https://www.googleapis.com/auth/drive.scripts";

  public $about;
  public $changes;
  public $channels;
  public $comments;
  public $files;
  public $permissions;
  public $replies;
  public $revisions;
  public $teamdrives;
  
  /**
   * Constructs the internal representation of the Drive service.
   *
   * @param Google_Client $client
   */
  public function __construct(Google_Client $client)
  {
    parent::__construct($client);
    $this->rootUrl = 'https://www.googleapis.com/';
    $this->servicePath = 'drive/v3/';
    $this->version = 'v3';
    $this->serviceName = 'drive';

    $this->about = new Google_Service_Drive_Resource_About(
        $this,
        $this->serviceName,
        'about',
        array(
          'methods' => array(
            'get' => array(
              'path' => 'about',
              'httpMethod' => 'GET',
              'parameters' => array(),
            ),
          )
        )
    );
    $this->changes = new Google_Service_Drive_Resource_Changes(
        $this,
        $this->serviceName,
        'changes',
        array(
          'methods' => array(
            'getStartPageToken' => array(
              'path' => 'changes/startPageToken',
              'httpMethod' => 'GET',
              'parameters' => array(
                'supportsTeamDrives' => array(
                  'location' => 'query',
                  'type' => 'boolean',
                ),
                'teamDriveId' => array(
                  'location' => 'query',
                  'type' => 'string',
                ),
              ),
            ),'list' => array(
              'path' => 'changes',
              'httpMethod' => 'GET',
              'parameters' => array(
                'pageToken' => array(
                  'location' => 'query',
                  'type' => 'string',
                  'required' => true,
                ),
                'includeCorpusRemovals' => array(
                  'location' => 'query',
                  'type' => 'boolean',
                ),
                'includeRemoved' => array(
                  'location' => 'query',
                  'type' => 'boolean',
                ),
                'includeTeamDriveItems' => array(
                  'location' => 'query',
                  'type' => 'boolean',
                ),
                'pageSize' => array(
                  'location' => 'query',
                  'type' => 'integer',
                ),
                'restrictToMyDrive' => array(
                  'location' => 'query',
                  'type' => 'boolean',
                ),
                'spaces' => array(
                  'location' => 'query',
                  'type' => 'string',
                ),
                'supportsTeamDrives' => array(
                  'location' => 'query',
                  'type' => 'boolean',
                ),
                'teamDriveId' => array(
                  'location' => 'query',
                  'type' => 'string',
                ),
              ),
            ),'watch' => array(
              'path' => 'changes/watch',
              'httpMethod' => 'POST',
              'parameters' => array(
                'pageToken' => array(
                  'location' => 'query',
                  'type' => 'string',
                  'required' => true,
                ),
                'includeCorpusRemovals' => array(
                  'location' => 'query',
                  'type' => 'boolean',
                ),
                'includeRemoved' => array(
                  'location' => 'query',
                  'type' => 'boolean',
                ),
                'includeTeamDriveItems' => array(
                  'location' => 'query',
                  'type' => 'boolean',
                ),
                'pageSize' => array(
                  'location' => 'query',
                  'type' => 'integer',
                ),
                'restrictToMyDrive' => array(
                  'location' => 'query',
                  'type' => 'boolean',
                ),
                'spaces' => array(
                  'location' => 'query',
                  'type' => 'string',
                ),
                'supportsTeamDrives' => array(
                  'location' => 'query',
                  'type' => 'boolean',
                ),
                'teamDriveId' => array(
                  'location' => 'query',
                  'type' => 'string',
                ),
              ),
            ),
          )
        )
    );
    $this->channels = new Google_Service_Drive_Resource_Channels(
        $this,
        $this->serviceName,
        'channels',
        array(
          'methods' => array(
            'stop' => array(
              'path' => 'channels/stop',
              'httpMethod' => 'POST',
              'parameters' => array(),
            ),
          )
        )
    );
    $this->comments = new Google_Service_Drive_Resource_Comments(
        $this,
        $this->serviceName,
        'comments',
        array(
          'methods' => array(
            'create' => array(
              'path' => 'files/{fileId}/comments',
              'httpMethod' => 'POST',
              'parameters' => array(
                'fileId' => array(
                  'location' => 'path',
                  'type' => 'string',
                  'required' => true,
                ),
              ),
            ),'delete' => array(
              'path' => 'files/{fileId}/comments/{commentId}',
              'httpMethod' => 'DELETE',
              'parameters' => array(
                'fileId' => array(
                  'location' => 'path',
                  'type' => 'string',
                  'required' => true,
                ),
                'commentId' => array(
                  'location' => 'path',
                  'type' => 'string',
                  'required' => true,
                ),
              ),
            ),'get' => array(
              'path' => 'files/{fileId}/comments/{commentId}',
              'httpMethod' => 'GET',
              'parameters' => array(
                'fileId' => array(
                  'location' => 'path',
                  'type' => 'string',
                  'required' => true,
                ),
                'commentId' => array(
                  'location' => 'path',
                  'type' => 'string',
                  'required' => true,
                ),
                'includeDeleted' => array(
                  'location' => 'query',
                  'type' => 'boolean',
                ),
              ),
            ),'list' => array(
              'path' => 'files/{fileId}/comments',
              'httpMethod' => 'GET',
              'parameters' => array(
                'fileId' => array(
                  'location' => 'path',
                  'type' => 'string',
                  'required' => true,
                ),
                'includeDeleted' => array(
                  'location' => 'query',
                  'type' => 'boolean',
                ),
                'pageSize' => array(
                  'location' => 'query',
                  'type' => 'integer',
                ),
                'pageToken' => array(
                  'location' => 'query',
                  'type' => 'string',
                ),
                'startModifiedTime' => array(
                  'location' => 'query',
                  'type' => 'string',
                ),
              ),
            ),'update' => array(
              'path' => 'files/{fileId}/comments/{commentId}',
              'httpMethod' => 'PATCH',
              'parameters' => array(
                'fileId' => array(
                  'location' => 'path',
                  'type' => 'string',
                  'required' => true,
                ),
                'commentId' => array(
                  'location' => 'path',
                  'type' => 'string',
                  'required' => true,
                ),
              ),
            ),
          )
        )
    );
    $this->files = new Google_Service_Drive_Resource_Files(
        $this,
        $this->serviceName,
        'files',
        array(
          'methods' => array(
            'copy' => array(
              'path' => 'files/{fileId}/copy',
              'httpMethod' => 'POST',
              'parameters' => array(
                'fileId' => array(
                  'location' => 'path',
                  'type' => 'string',
                  'required' => true,
                ),
                'ignoreDefaultVisibility' => array(
                  'location' => 'query',
                  'type' => 'boolean',
                ),
                'keepRevisionForever' => array(
                  'location' => 'query',
                  'type' => 'boolean',
                ),
                'ocrLanguage' => array(
                  'location' => 'query',
                  'type' => 'string',
                ),
                'supportsTeamDrives' => array(
                  'location' => 'query',
                  'type' => 'boolean',
                ),
              ),
            ),'create' => array(
              'path' => 'files',
              'httpMethod' => 'POST',
              'parameters' => array(
                'ignoreDefaultVisibility' => array(
                  'location' => 'query',
                  'type' => 'boolean',
                ),
                'keepRevisionForever' => array(
                  'location' => 'query',
                  'type' => 'boolean',
                ),
                'ocrLanguage' => array(
                  'location' => 'query',
                  'type' => 'string',
                ),
                'supportsTeamDrives' => array(
                  'location' => 'query',
                  'type' => 'boolean',
                ),
                'useContentAsIndexableText' => array(
                  'location' => 'query',
                  'type' => 'boolean',
                ),
              ),
            ),'delete' => array(
              'path' => 'files/{fileId}',
              'httpMethod' => 'DELETE',
              'parameters' => array(
                'fileId' => array(
                  'location' => 'path',
                  'type' => 'string',
                  'required' => true,
                ),
                'supportsTeamDrives' => array(
                  'location' => 'query',
                  'type' => 'boolean',
                ),
              ),
            ),'emptyTrash' => array(
              'path' => 'files/trash',
              'httpMethod' => 'DELETE',
              'parameters' => array(),
            ),'export' => array(
              'path' => 'files/{fileId}/export',
              'httpMethod' => 'GET',
              'parameters' => array(
                'fileId' => array(
                  'location' => 'path',
                  'type' => 'string',
                  'required' => true,
                ),
                'mimeType' => array(
                  'location' => 'query',
                  'type' => 'string',
                  'required' => true,
                ),
              ),
            ),'generateIds' => array(
              'path' => 'files/generateIds',
              'httpMethod' => 'GET',
              'parameters' => array(
                'count' => array(
                  'location' => 'query',
                  'type' => 'integer',
                ),
                'space' => array(
                  'location' => 'query',
                  'type' => 'string',
                ),
              ),
            ),'get' => array(
              'path' => 'files/{fileId}',
              'httpMethod' => 'GET',
              'parameters' => array(
                'fileId' => array(
                  'location' => 'path',
                  'type' => 'string',
                  'required' => true,
                ),
                'acknowledgeAbuse' => array(
                  'location' => 'query',
                  'type' => 'boolean',
                ),
                'supportsTeamDrives' => array(
                  'location' => 'query',
                  'type' => 'boolean',
                ),
              ),
            ),'list' => array(
              'path' => 'files',
              'httpMethod' => 'GET',
              'parameters' => array(
                'corpora' => array(
                  'location' => 'query',
                  'type' => 'string',
                ),
                'corpus' => array(
                  'location' => 'query',
                  'type' => 'string',
                ),
                'includeTeamDriveItems' => array(
                  'location' => 'query',
                  'type' => 'boolean',
                ),
                'orderBy' => array(
                  'location' => 'query',
                  'type' => 'string',
                ),
                'pageSize' => array(
                  'location' => 'query',
                  'type' => 'integer',
                ),
                'pageToken' => array(
                  'location' => 'query',
                  'type' => 'string',
                ),
                'q' => array(
                  'location' => 'query',
                  'type' => 'string',
                ),
                'spaces' => array(
                  'location' => 'query',
                  'type' => 'string',
                ),
                'supportsTeamDrives' => array(
                  'location' => 'query',
                  'type' => 'boolean',
                ),
                'teamDriveId' => array(
                  'location' => 'query',
                  'type' => 'string',
                ),
              ),
            ),'update' => array(
              'path' => 'files/{fileId}',
              'httpMethod' => 'PATCH',
              'parameters' => array(
                'fileId' => array(
                  'location' => 'path',
                  'type' => 'string',
                  'required' => true,
                ),
                'addParents' => array(
                  'location' => 'query',
                  'type' => 'string',
                ),
                'keepRevisionForever' => array(
                  'location' => 'query',
                  'type' => 'boolean',
                ),
                'ocrLanguage' => array(
                  'location' => 'query',
                  'type' => 'string',
                ),
                'removeParents' => array(
                  'location' => 'query',
                  'type' => 'string',
                ),
                'supportsTeamDrives' => array(
                  'location' => 'query',
                  'type' => 'boolean',
                ),
                'useContentAsIndexableText' => array(
                  'location' => 'query',
                  'type' => 'boolean',
                ),
              ),
            ),'watch' => array(
              'path' => 'files/{fileId}/watch',
              'httpMethod' => 'POST',
              'parameters' => array(
                'fileId' => array(
                  'location' => 'path',
                  'type' => 'string',
                  'required' => true,
                ),
                'acknowledgeAbuse' => array(
                  'location' => 'query',
                  'type' => 'boolean',
                ),
                'supportsTeamDrives' => array(
                  'location' => 'query',
                  'type' => 'boolean',
                ),
              ),
            ),
          )
        )
    );
    $this->permissions = new Google_Service_Drive_Resource_Permissions(
        $this,
        $this->serviceName,
        'permissions',
        array(
          'methods' => array(
            'create' => array(
              'path' => 'files/{fileId}/permissions',
              'httpMethod' => 'POST',
              'parameters' => array(
                'fileId' => array(
                  'location' => 'path',
                  'type' => 'string',
                  'required' => true,
                ),
                'emailMessage' => array(
                  'location' => 'query',
                  'type' => 'string',
                ),
                'sendNotificationEmail' => array(
                  'location' => 'query',
                  'type' => 'boolean',
                ),
                'supportsTeamDrives' => array(
                  'location' => 'query',
                  'type' => 'boolean',
                ),
                'transferOwnership' => array(
                  'location' => 'query',
                  'type' => 'boolean',
                ),
                'useDomainAdminAccess' => array(
                  'location' => 'query',
                  'type' => 'boolean',
                ),
              ),
            ),'delete' => array(
              'path' => 'files/{fileId}/permissions/{permissionId}',
              'httpMethod' => 'DELETE',
              'parameters' => array(
                'fileId' => array(
                  'location' => 'path',
                  'type' => 'string',
                  'required' => true,
                ),
                'permissionId' => array(
                  'location' => 'path',
                  'type' => 'string',
                  'required' => true,
                ),
                'supportsTeamDrives' => array(
                  'location' => 'query',
                  'type' => 'boolean',
                ),
                'useDomainAdminAccess' => array(
                  'location' => 'query',
                  'type' => 'boolean',
                ),
              ),
            ),'get' => array(
              'path' => 'files/{fileId}/permissions/{permissionId}',
              'httpMethod' => 'GET',
              'parameters' => array(
                'fileId' => array(
                  'location' => 'path',
                  'type' => 'string',
                  'required' => true,
                ),
                'permissionId' => array(
                  'location' => 'path',
                  'type' => 'string',
                  'required' => true,
                ),
                'supportsTeamDrives' => array(
                  'location' => 'query',
                  'type' => 'boolean',
                ),
                'useDomainAdminAccess' => array(
                  'location' => 'query',
                  'type' => 'boolean',
                ),
              ),
            ),'list' => array(
              'path' => 'files/{fileId}/permissions',
              'httpMethod' => 'GET',
              'parameters' => array(
                'fileId' => array(
                  'location' => 'path',
                  'type' => 'string',
                  'required' => true,
                ),
                'pageSize' => array(
                  'location' => 'query',
                  'type' => 'integer',
                ),
                'pageToken' => array(
                  'location' => 'query',
                  'type' => 'string',
                ),
                'supportsTeamDrives' => array(
                  'location' => 'query',
                  'type' => 'boolean',
                ),
                'useDomainAdminAccess' => array(
                  'location' => 'query',
                  'type' => 'boolean',
                ),
              ),
            ),'update' => array(
              'path' => 'files/{fileId}/permissions/{permissionId}',
              'httpMethod' => 'PATCH',
              'parameters' => array(
                'fileId' => array(
                  'location' => 'path',
                  'type' => 'string',
                  'required' => true,
                ),
                'permissionId' => array(
                  'location' => 'path',
                  'type' => 'string',
                  'required' => true,
                ),
                'removeExpiration' => array(
                  'location' => 'query',
                  'type' => 'boolean',
                ),
                'supportsTeamDrives' => array(
                  'location' => 'query',
                  'type' => 'boolean',
                ),
                'transferOwnership' => array(
                  'location' => 'query',
                  'type' => 'boolean',
                ),
                'useDomainAdminAccess' => array(
                  'location' => 'query',
                  'type' => 'boolean',
                ),
              ),
            ),
          )
        )
    );
    $this->replies = new Google_Service_Drive_Resource_Replies(
        $this,
        $this->serviceName,
        'replies',
        array(
          'methods' => array(
            'create' => array(
              'path' => 'files/{fileId}/comments/{commentId}/replies',
              'httpMethod' => 'POST',
              'parameters' => array(
                'fileId' => array(
                  'location' => 'path',
                  'type' => 'string',
                  'required' => true,
                ),
                'commentId' => array(
                  'location' => 'path',
                  'type' => 'string',
                  'required' => true,
                ),
              ),
            ),'delete' => array(
              'path' => 'files/{fileId}/comments/{commentId}/replies/{replyId}',
              'httpMethod' => 'DELETE',
              'parameters' => array(
                'fileId' => array(
                  'location' => 'path',
                  'type' => 'string',
                  'required' => true,
                ),
                'commentId' => array(
                  'location' => 'path',
                  'type' => 'string',
                  'required' => true,
                ),
                'replyId' => array(
                  'location' => 'path',
                  'type' => 'string',
                  'required' => true,
                ),
              ),
            ),'get' => array(
              'path' => 'files/{fileId}/comments/{commentId}/replies/{replyId}',
              'httpMethod' => 'GET',
              'parameters' => array(
                'fileId' => array(
                  'location' => 'path',
                  'type' => 'string',
                  'required' => true,
                ),
                'commentId' => array(
                  'location' => 'path',
                  'type' => 'string',
                  'required' => true,
                ),
                'replyId' => array(
                  'location' => 'path',
                  'type' => 'string',
                  'required' => true,
                ),
                'includeDeleted' => array(
                  'location' => 'query',
                  'type' => 'boolean',
                ),
              ),
            ),'list' => array(
              'path' => 'files/{fileId}/comments/{commentId}/replies',
              'httpMethod' => 'GET',
              'parameters' => array(
                'fileId' => array(
                  'location' => 'path',
                  'type' => 'string',
                  'required' => true,
                ),
                'commentId' => array(
                  'location' => 'path',
                  'type' => 'string',
                  'required' => true,
                ),
                'includeDeleted' => array(
                  'location' => 'query',
                  'type' => 'boolean',
                ),
                'pageSize' => array(
                  'location' => 'query',
                  'type' => 'integer',
                ),
                'pageToken' => array(
                  'location' => 'query',
                  'type' => 'string',
                ),
              ),
            ),'update' => array(
              'path' => 'files/{fileId}/comments/{commentId}/replies/{replyId}',
              'httpMethod' => 'PATCH',
              'parameters' => array(
                'fileId' => array(
                  'location' => 'path',
                  'type' => 'string',
                  'required' => true,
                ),
                'commentId' => array(
                  'location' => 'path',
                  'type' => 'string',
                  'required' => true,
                ),
                'replyId' => array(
                  'location' => 'path',
                  'type' => 'string',
                  'required' => true,
                ),
              ),
            ),
          )
        )
    );
    $this->revisions = new Google_Service_Drive_Resource_Revisions(
        $this,
        $this->serviceName,
        'revisions',
        array(
          'methods' => array(
            'delete' => array(
              'path' => 'files/{fileId}/revisions/{revisionId}',
              'httpMethod' => 'DELETE',
              'parameters' => array(
                'fileId' => array(
                  'location' => 'path',
                  'type' => 'string',
                  'required' => true,
                ),
                'revisionId' => array(
                  'location' => 'path',
                  'type' => 'string',
                  'required' => true,
                ),
              ),
            ),'get' => array(
              'path' => 'files/{fileId}/revisions/{revisionId}',
              'httpMethod' => 'GET',
              'parameters' => array(
                'fileId' => array(
                  'location' => 'path',
                  'type' => 'string',
                  'required' => true,
                ),
                'revisionId' => array(
                  'location' => 'path',
                  'type' => 'string',
                  'required' => true,
                ),
                'acknowledgeAbuse' => array(
                  'location' => 'query',
                  'type' => 'boolean',
                ),
              ),
            ),'list' => array(
              'path' => 'files/{fileId}/revisions',
              'httpMethod' => 'GET',
              'parameters' => array(
                'fileId' => array(
                  'location' => 'path',
                  'type' => 'string',
                  'required' => true,
                ),
                'pageSize' => array(
                  'location' => 'query',
                  'type' => 'integer',
                ),
                'pageToken' => array(
                  'location' => 'query',
                  'type' => 'string',
                ),
              ),
            ),'update' => array(
              'path' => 'files/{fileId}/revisions/{revisionId}',
              'httpMethod' => 'PATCH',
              'parameters' => array(
                'fileId' => array(
                  'location' => 'path',
                  'type' => 'string',
                  'required' => true,
                ),
                'revisionId' => array(
                  'location' => 'path',
                  'type' => 'string',
                  'required' => true,
                ),
              ),
            ),
          )
        )
    );
    $this->teamdrives = new Google_Service_Drive_Resource_Teamdrives(
        $this,
        $this->serviceName,
        'teamdrives',
        array(
          'methods' => array(
            'create' => array(
              'path' => 'teamdrives',
              'httpMethod' => 'POST',
              'parameters' => array(
                'requestId' => array(
                  'location' => 'query',
                  'type' => 'string',
                  'required' => true,
                ),
              ),
            ),'delete' => array(
              'path' => 'teamdrives/{teamDriveId}',
              'httpMethod' => 'DELETE',
              'parameters' => array(
                'teamDriveId' => array(
                  'location' => 'path',
                  'type' => 'string',
                  'required' => true,
                ),
              ),
            ),'get' => array(
              'path' => 'teamdrives/{teamDriveId}',
              'httpMethod' => 'GET',
              'parameters' => array(
                'teamDriveId' => array(
                  'location' => 'path',
                  'type' => 'string',
                  'required' => true,
                ),
                'useDomainAdminAccess' => array(
                  'location' => 'query',
                  'type' => 'boolean',
                ),
              ),
            ),'list' => array(
              'path' => 'teamdrives',
              'httpMethod' => 'GET',
              'parameters' => array(
                'pageSize' => array(
                  'location' => 'query',
                  'type' => 'integer',
                ),
                'pageToken' => array(
                  'location' => 'query',
                  'type' => 'string',
                ),
                'q' => array(
                  'location' => 'query',
                  'type' => 'string',
                ),
                'useDomainAdminAccess' => array(
                  'location' => 'query',
                  'type' => 'boolean',
                ),
              ),
            ),'update' => array(
              'path' => 'teamdrives/{teamDriveId}',
              'httpMethod' => 'PATCH',
              'parameters' => array(
                'teamDriveId' => array(
                  'location' => 'path',
                  'type' => 'string',
                  'required' => true,
                ),
                'useDomainAdminAccess' => array(
                  'location' => 'query',
                  'type' => 'boolean',
                ),
              ),
            ),
          )
        )
    );
  }
}
vendor/composer/autoload_namespaces.php000064400000001643151327705670014431 0ustar00<?php

// autoload_namespaces.php @generated by Composer

$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);

return array(
    'System' => array($vendorDir . '/phpseclib/phpseclib/phpseclib'),
    'Net' => array($vendorDir . '/phpseclib/phpseclib/phpseclib'),
    'Math' => array($vendorDir . '/phpseclib/phpseclib/phpseclib'),
    'Guzzle\\Tests' => array($vendorDir . '/guzzle/guzzle/tests'),
    'Guzzle' => array($vendorDir . '/guzzle/guzzle/src'),
    'Google_Service_' => array($vendorDir . '/google/apiclient-services/src'),
    'Google_' => array($baseDir . '/includes/lib/google-api-php-client/src'),
    'WPvivid_Google_' => array($baseDir . '/includes/lib2/google-api-php-client/src'),
    'File' => array($vendorDir . '/phpseclib/phpseclib/phpseclib'),
    'Crypt' => array($vendorDir . '/phpseclib/phpseclib/phpseclib'),
    'Aws' => array($baseDir . '/includes/lib/aws-sdk-php-2.8.31/src'),
);
vendor/composer/autoload_psr4.php000064400000001622151327705670013177 0ustar00<?php

// autoload_psr4.php @generated by Composer

$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);

return array(
    'Symfony\\Component\\EventDispatcher\\' => array($vendorDir . '/symfony/event-dispatcher'),
    'WPvividPsr\\Log\\' => array($vendorDir . '/psr/log/Psr/Log'),
    'WPvividPsr\\Http\\Message\\' => array($vendorDir . '/psr/http-message/src'),
    'WPvividPsr\\Cache\\' => array($vendorDir . '/psr/cache/src'),
    'WPvividMonolog\\' => array($vendorDir . '/monolog/monolog/src/Monolog'),
    'WPvividGuzzleHttp\\Psr7\\' => array($vendorDir . '/guzzlehttp/psr7/src'),
    'WPvividGuzzleHttp\\Promise\\' => array($vendorDir . '/guzzlehttp/promises/src'),
    'WPvividGuzzleHttp\\' => array($vendorDir . '/guzzlehttp/guzzle/src'),
    'WPvividGoogle\\Auth\\' => array($vendorDir . '/google/auth/src'),
    'Firebase\\JWT\\' => array($vendorDir . '/firebase/php-jwt/src'),
);
vendor/composer/LICENSE000064400000002056151327705670010715 0ustar00
Copyright (c) Nils Adermann, Jordi Boggiano

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is furnished
to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

vendor/composer/ClassLoader.php000064400000032156151327705670012621 0ustar00<?php

/*
 * This file is part of Composer.
 *
 * (c) Nils Adermann <naderman@naderman.de>
 *     Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Composer\Autoload;

/**
 * ClassLoader implements a PSR-0, PSR-4 and classmap class loader.
 *
 *     $loader = new \Composer\Autoload\ClassLoader();
 *
 *     // register classes with namespaces
 *     $loader->add('Symfony\Component', __DIR__.'/component');
 *     $loader->add('Symfony',           __DIR__.'/framework');
 *
 *     // activate the autoloader
 *     $loader->register();
 *
 *     // to enable searching the include path (eg. for PEAR packages)
 *     $loader->setUseIncludePath(true);
 *
 * In this example, if you try to use a class in the Symfony\Component
 * namespace or one of its children (Symfony\Component\Console for instance),
 * the autoloader will first look for the class under the component/
 * directory, and it will then fallback to the framework/ directory if not
 * found before giving up.
 *
 * This class is loosely based on the Symfony UniversalClassLoader.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 * @author Jordi Boggiano <j.boggiano@seld.be>
 * @see    http://www.php-fig.org/psr/psr-0/
 * @see    http://www.php-fig.org/psr/psr-4/
 */
class ClassLoader
{
    // PSR-4
    private $prefixLengthsPsr4 = array();
    private $prefixDirsPsr4 = array();
    private $fallbackDirsPsr4 = array();

    // PSR-0
    private $prefixesPsr0 = array();
    private $fallbackDirsPsr0 = array();

    private $useIncludePath = false;
    private $classMap = array();
    private $classMapAuthoritative = false;
    private $missingClasses = array();
    private $apcuPrefix;

    public function getPrefixes()
    {
        if (!empty($this->prefixesPsr0)) {
            return call_user_func_array('array_merge', $this->prefixesPsr0);
        }

        return array();
    }

    public function getPrefixesPsr4()
    {
        return $this->prefixDirsPsr4;
    }

    public function getFallbackDirs()
    {
        return $this->fallbackDirsPsr0;
    }

    public function getFallbackDirsPsr4()
    {
        return $this->fallbackDirsPsr4;
    }

    public function getClassMap()
    {
        return $this->classMap;
    }

    /**
     * @param array $classMap Class to filename map
     */
    public function addClassMap(array $classMap)
    {
        if ($this->classMap) {
            $this->classMap = array_merge($this->classMap, $classMap);
        } else {
            $this->classMap = $classMap;
        }
    }

    /**
     * Registers a set of PSR-0 directories for a given prefix, either
     * appending or prepending to the ones previously set for this prefix.
     *
     * @param string       $prefix  The prefix
     * @param array|string $paths   The PSR-0 root directories
     * @param bool         $prepend Whether to prepend the directories
     */
    public function add($prefix, $paths, $prepend = false)
    {
        if (!$prefix) {
            if ($prepend) {
                $this->fallbackDirsPsr0 = array_merge(
                    (array) $paths,
                    $this->fallbackDirsPsr0
                );
            } else {
                $this->fallbackDirsPsr0 = array_merge(
                    $this->fallbackDirsPsr0,
                    (array) $paths
                );
            }

            return;
        }

        $first = $prefix[0];
        if (!isset($this->prefixesPsr0[$first][$prefix])) {
            $this->prefixesPsr0[$first][$prefix] = (array) $paths;

            return;
        }
        if ($prepend) {
            $this->prefixesPsr0[$first][$prefix] = array_merge(
                (array) $paths,
                $this->prefixesPsr0[$first][$prefix]
            );
        } else {
            $this->prefixesPsr0[$first][$prefix] = array_merge(
                $this->prefixesPsr0[$first][$prefix],
                (array) $paths
            );
        }
    }

    /**
     * Registers a set of PSR-4 directories for a given namespace, either
     * appending or prepending to the ones previously set for this namespace.
     *
     * @param string       $prefix  The prefix/namespace, with trailing '\\'
     * @param array|string $paths   The PSR-4 base directories
     * @param bool         $prepend Whether to prepend the directories
     *
     * @throws \InvalidArgumentException
     */
    public function addPsr4($prefix, $paths, $prepend = false)
    {
        if (!$prefix) {
            // Register directories for the root namespace.
            if ($prepend) {
                $this->fallbackDirsPsr4 = array_merge(
                    (array) $paths,
                    $this->fallbackDirsPsr4
                );
            } else {
                $this->fallbackDirsPsr4 = array_merge(
                    $this->fallbackDirsPsr4,
                    (array) $paths
                );
            }
        } elseif (!isset($this->prefixDirsPsr4[$prefix])) {
            // Register directories for a new namespace.
            $length = strlen($prefix);
            if ('\\' !== $prefix[$length - 1]) {
                throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
            }
            $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
            $this->prefixDirsPsr4[$prefix] = (array) $paths;
        } elseif ($prepend) {
            // Prepend directories for an already registered namespace.
            $this->prefixDirsPsr4[$prefix] = array_merge(
                (array) $paths,
                $this->prefixDirsPsr4[$prefix]
            );
        } else {
            // Append directories for an already registered namespace.
            $this->prefixDirsPsr4[$prefix] = array_merge(
                $this->prefixDirsPsr4[$prefix],
                (array) $paths
            );
        }
    }

    /**
     * Registers a set of PSR-0 directories for a given prefix,
     * replacing any others previously set for this prefix.
     *
     * @param string       $prefix The prefix
     * @param array|string $paths  The PSR-0 base directories
     */
    public function set($prefix, $paths)
    {
        if (!$prefix) {
            $this->fallbackDirsPsr0 = (array) $paths;
        } else {
            $this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths;
        }
    }

    /**
     * Registers a set of PSR-4 directories for a given namespace,
     * replacing any others previously set for this namespace.
     *
     * @param string       $prefix The prefix/namespace, with trailing '\\'
     * @param array|string $paths  The PSR-4 base directories
     *
     * @throws \InvalidArgumentException
     */
    public function setPsr4($prefix, $paths)
    {
        if (!$prefix) {
            $this->fallbackDirsPsr4 = (array) $paths;
        } else {
            $length = strlen($prefix);
            if ('\\' !== $prefix[$length - 1]) {
                throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
            }
            $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
            $this->prefixDirsPsr4[$prefix] = (array) $paths;
        }
    }

    /**
     * Turns on searching the include path for class files.
     *
     * @param bool $useIncludePath
     */
    public function setUseIncludePath($useIncludePath)
    {
        $this->useIncludePath = $useIncludePath;
    }

    /**
     * Can be used to check if the autoloader uses the include path to check
     * for classes.
     *
     * @return bool
     */
    public function getUseIncludePath()
    {
        return $this->useIncludePath;
    }

    /**
     * Turns off searching the prefix and fallback directories for classes
     * that have not been registered with the class map.
     *
     * @param bool $classMapAuthoritative
     */
    public function setClassMapAuthoritative($classMapAuthoritative)
    {
        $this->classMapAuthoritative = $classMapAuthoritative;
    }

    /**
     * Should class lookup fail if not found in the current class map?
     *
     * @return bool
     */
    public function isClassMapAuthoritative()
    {
        return $this->classMapAuthoritative;
    }

    /**
     * APCu prefix to use to cache found/not-found classes, if the extension is enabled.
     *
     * @param string|null $apcuPrefix
     */
    public function setApcuPrefix($apcuPrefix)
    {
        $this->apcuPrefix = function_exists('apcu_fetch') && ini_get('apc.enabled') ? $apcuPrefix : null;
    }

    /**
     * The APCu prefix in use, or null if APCu caching is not enabled.
     *
     * @return string|null
     */
    public function getApcuPrefix()
    {
        return $this->apcuPrefix;
    }

    /**
     * Registers this instance as an autoloader.
     *
     * @param bool $prepend Whether to prepend the autoloader or not
     */
    public function register($prepend = false)
    {
        spl_autoload_register(array($this, 'loadClass'), true, $prepend);
    }

    /**
     * Unregisters this instance as an autoloader.
     */
    public function unregister()
    {
        spl_autoload_unregister(array($this, 'loadClass'));
    }

    /**
     * Loads the given class or interface.
     *
     * @param  string    $class The name of the class
     * @return bool|null True if loaded, null otherwise
     */
    public function loadClass($class)
    {
        if ($file = $this->findFile($class)) {
            includeFile($file);

            return true;
        }
    }

    /**
     * Finds the path to the file where the class is defined.
     *
     * @param string $class The name of the class
     *
     * @return string|false The path if found, false otherwise
     */
    public function findFile($class)
    {
        // class map lookup
        if (isset($this->classMap[$class])) {
            return $this->classMap[$class];
        }
        if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) {
            return false;
        }
        if (null !== $this->apcuPrefix) {
            $file = apcu_fetch($this->apcuPrefix.$class, $hit);
            if ($hit) {
                return $file;
            }
        }

        $file = $this->findFileWithExtension($class, '.php');

        // Search for Hack files if we are running on HHVM
        if (false === $file && defined('HHVM_VERSION')) {
            $file = $this->findFileWithExtension($class, '.hh');
        }

        if (null !== $this->apcuPrefix) {
            apcu_add($this->apcuPrefix.$class, $file);
        }

        if (false === $file) {
            // Remember that this class does not exist.
            $this->missingClasses[$class] = true;
        }

        return $file;
    }

    private function findFileWithExtension($class, $ext)
    {
        // PSR-4 lookup
        $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;

        $first = $class[0];
        if (isset($this->prefixLengthsPsr4[$first])) {
            $subPath = $class;
            while (false !== $lastPos = strrpos($subPath, '\\')) {
                $subPath = substr($subPath, 0, $lastPos);
                $search = $subPath . '\\';
                if (isset($this->prefixDirsPsr4[$search])) {
                    $pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1);
                    foreach ($this->prefixDirsPsr4[$search] as $dir) {
                        if (file_exists($file = $dir . $pathEnd)) {
                            return $file;
                        }
                    }
                }
            }
        }

        // PSR-4 fallback dirs
        foreach ($this->fallbackDirsPsr4 as $dir) {
            if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
                return $file;
            }
        }

        // PSR-0 lookup
        if (false !== $pos = strrpos($class, '\\')) {
            // namespaced class name
            $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
                . strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
        } else {
            // PEAR-like class name
            $logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
        }

        if (isset($this->prefixesPsr0[$first])) {
            foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
                if (0 === strpos($class, $prefix)) {
                    foreach ($dirs as $dir) {
                        if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
                            return $file;
                        }
                    }
                }
            }
        }

        // PSR-0 fallback dirs
        foreach ($this->fallbackDirsPsr0 as $dir) {
            if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
                return $file;
            }
        }

        // PSR-0 include paths.
        if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
            return $file;
        }

        return false;
    }
}

/**
 * Scope isolated include.
 *
 * Prevents access to $this/self from included files.
 */
function includeFile($file)
{
    include $file;
}
vendor/composer/include_paths.php000064400000000302151327705670013233 0ustar00<?php

// include_paths.php @generated by Composer

$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);

return array(
    $vendorDir . '/phpseclib/phpseclib/phpseclib',
);
vendor/composer/autoload_real.php000064400000005040151327705670013230 0ustar00<?php

// autoload_real.php @generated by Composer

class ComposerAutoloaderInit381ef710b1ae857a1b0cca4550c7cd7e
{
    private static $loader;

    public static function loadClassLoader($class)
    {
        if ('Composer\Autoload\ClassLoader' === $class) {
            require __DIR__ . '/ClassLoader.php';
        }
    }

    public static function getLoader()
    {
        if (null !== self::$loader) {
            return self::$loader;
        }

        spl_autoload_register(array('ComposerAutoloaderInit381ef710b1ae857a1b0cca4550c7cd7e', 'loadClassLoader'), true, true);
        self::$loader = $loader = new \Composer\Autoload\ClassLoader();
        spl_autoload_unregister(array('ComposerAutoloaderInit381ef710b1ae857a1b0cca4550c7cd7e', 'loadClassLoader'));

        $includePaths = require __DIR__ . '/include_paths.php';
        $includePaths[] = get_include_path();
        set_include_path(implode(PATH_SEPARATOR, $includePaths));

        $useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
        if ($useStaticLoader) {
            require_once __DIR__ . '/autoload_static.php';

            call_user_func(\Composer\Autoload\ComposerStaticInit381ef710b1ae857a1b0cca4550c7cd7e::getInitializer($loader));
        } else {
            $map = require __DIR__ . '/autoload_namespaces.php';
            foreach ($map as $namespace => $path) {
                $loader->set($namespace, $path);
            }

            $map = require __DIR__ . '/autoload_psr4.php';
            foreach ($map as $namespace => $path) {
                $loader->setPsr4($namespace, $path);
            }

            $classMap = require __DIR__ . '/autoload_classmap.php';
            if ($classMap) {
                $loader->addClassMap($classMap);
            }
        }

        $loader->register(false);

        if ($useStaticLoader) {
            $includeFiles = Composer\Autoload\ComposerStaticInit381ef710b1ae857a1b0cca4550c7cd7e::$files;
        } else {
            $includeFiles = require __DIR__ . '/autoload_files.php';
        }
        foreach ($includeFiles as $fileIdentifier => $file) {
            composerRequire381ef710b1ae857a1b0cca4550c7cd7e($fileIdentifier, $file);
        }

        return $loader;
    }
}

function composerRequire381ef710b1ae857a1b0cca4550c7cd7e($fileIdentifier, $file)
{
    if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
        require $file;

        $GLOBALS['__composer_autoload_files'][$fileIdentifier] = true;
    }
}
vendor/composer/autoload_files.php000064400000004207151327705670013413 0ustar00<?php

// autoload_files.php @generated by Composer

$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);

if (PHP_VERSION_ID < 50500)
{
    return array(
        //'7b11c4dc42b3b3023073cb14e519683c' => $vendorDir . '/ralouphie/getallheaders/src/getallheaders.php',
        //'decc78cc4436b1292c6c0d151b19445c' => $vendorDir . '/phpseclib/phpseclib/phpseclib/bootstrap.php',
        //'3919eeb97e98d4648304477f8ef734ba' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/Random.php',
        '7b11c4dc42b3b3023073cb14e519684c' => $vendorDir . '/ralouphie/getallheaders/src/getallheaders.php',
        'decc78cc4436b1292c6c0d151b19446c' => $vendorDir . '/phpseclib/phpseclib/phpseclib/bootstrap.php',
        '3919eeb97e98d4648304477f8ef735ba' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/Random.php',
    );
}
else
{
    return array(
        //'7b11c4dc42b3b3023073cb14e519683c' => $vendorDir . '/ralouphie/getallheaders/src/getallheaders.php',
        //'c964ee0ededf28c96ebd9db5099ef910' => $vendorDir . '/guzzlehttp/promises/src/functions_include.php',
        //'a0edc8309cc5e1d60e3047b5df6b7052' => $vendorDir . '/guzzlehttp/psr7/src/functions_include.php',
        //'37a3dc5111fe8f707ab4c132ef1dbc62' => $vendorDir . '/guzzlehttp/guzzle/src/functions_include.php',
        //'decc78cc4436b1292c6c0d151b19445c' => $vendorDir . '/phpseclib/phpseclib/phpseclib/bootstrap.php',
        //'3919eeb97e98d4648304477f8ef734ba' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/Random.php',
        '7b11c4dc42b3b3023073cb14e519684c' => $vendorDir . '/ralouphie/getallheaders/src/getallheaders.php',
        'c964ee0ededf28c96ebd9db5099ef911' => $vendorDir . '/guzzlehttp/promises/src/functions_include.php',
        'a0edc8309cc5e1d60e3047b5df6b7057' => $vendorDir . '/guzzlehttp/psr7/src/functions_include.php',
        '37a3dc5111fe8f707ab4c132ef1dbc63' => $vendorDir . '/guzzlehttp/guzzle/src/functions_include.php',
        'decc78cc4436b1292c6c0d151b19446c' => $vendorDir . '/phpseclib/phpseclib/phpseclib/bootstrap.php',
        '3919eeb97e98d4648304477f8ef735ba' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/Random.php',
    );
}

vendor/composer/installed.json000064400000074074151327705670012573 0ustar00[
    {
        "name": "firebase/php-jwt",
        "version": "v5.0.0",
        "version_normalized": "5.0.0.0",
        "source": {
            "type": "git",
            "url": "https://github.com/firebase/php-jwt.git",
            "reference": "9984a4d3a32ae7673d6971ea00bae9d0a1abba0e"
        },
        "dist": {
            "type": "zip",
            "url": "https://api.github.com/repos/firebase/php-jwt/zipball/9984a4d3a32ae7673d6971ea00bae9d0a1abba0e",
            "reference": "9984a4d3a32ae7673d6971ea00bae9d0a1abba0e",
            "shasum": "",
            "mirrors": [
                {
                    "url": "https://dl.laravel-china.org/%package%/%reference%.%type%",
                    "preferred": true
                }
            ]
        },
        "require": {
            "php": ">=5.3.0"
        },
        "require-dev": {
            "phpunit/phpunit": " 4.8.35"
        },
        "time": "2017-06-27T22:17:23+00:00",
        "type": "library",
        "installation-source": "dist",
        "autoload": {
            "psr-4": {
                "Firebase\\JWT\\": "src"
            }
        },
        "notification-url": "https://packagist.org/downloads/",
        "license": [
            "BSD-3-Clause"
        ],
        "authors": [
            {
                "name": "Neuman Vong",
                "email": "neuman+pear@twilio.com",
                "role": "Developer"
            },
            {
                "name": "Anant Narayanan",
                "email": "anant@php.net",
                "role": "Developer"
            }
        ],
        "description": "A simple library to encode and decode JSON Web Tokens (JWT) in PHP. Should conform to the current spec.",
        "homepage": "https://github.com/firebase/php-jwt"
    },
    {
        "name": "google/apiclient-services",
        "version": "v0.13",
        "version_normalized": "0.13.0.0",
        "source": {
            "type": "git",
            "url": "https://github.com/googleapis/google-api-php-client-services.git",
            "reference": "0805897f3435f9eea73ce21da9d55f51c69c1171"
        },
        "dist": {
            "type": "zip",
            "url": "https://api.github.com/repos/googleapis/google-api-php-client-services/zipball/0805897f3435f9eea73ce21da9d55f51c69c1171",
            "reference": "0805897f3435f9eea73ce21da9d55f51c69c1171",
            "shasum": "",
            "mirrors": [
                {
                    "url": "https://dl.laravel-china.org/%package%/%reference%.%type%",
                    "preferred": true
                }
            ]
        },
        "require": {
            "php": ">=5.4"
        },
        "require-dev": {
            "phpunit/phpunit": "~4.8"
        },
        "time": "2017-07-07T16:01:27+00:00",
        "type": "library",
        "installation-source": "dist",
        "autoload": {
            "psr-0": {
                "Google_Service_": "src"
            }
        },
        "notification-url": "https://packagist.org/downloads/",
        "license": [
            "Apache-2.0"
        ],
        "description": "Client library for Google APIs",
        "homepage": "http://developers.google.com/api-client-library/php",
        "keywords": [
            "google"
        ]
    },
    {
        "name": "google/auth",
        "version": "v1.4.0",
        "version_normalized": "1.4.0.0",
        "source": {
            "type": "git",
            "url": "https://github.com/googleapis/google-auth-library-php.git",
            "reference": "196237248e636a3554a7d9e4dfddeb97f450ab5c"
        },
        "dist": {
            "type": "zip",
            "url": "https://api.github.com/repos/googleapis/google-auth-library-php/zipball/196237248e636a3554a7d9e4dfddeb97f450ab5c",
            "reference": "196237248e636a3554a7d9e4dfddeb97f450ab5c",
            "shasum": "",
            "mirrors": [
                {
                    "url": "https://dl.laravel-china.org/%package%/%reference%.%type%",
                    "preferred": true
                }
            ]
        },
        "require": {
            "firebase/php-jwt": "~2.0|~3.0|~4.0|~5.0",
            "guzzlehttp/guzzle": "~5.3.1|~6.0",
            "guzzlehttp/psr7": "^1.2",
            "php": ">=5.4",
            "psr/cache": "^1.0",
            "psr/http-message": "^1.0"
        },
        "require-dev": {
            "friendsofphp/php-cs-fixer": "^1.11",
            "guzzlehttp/promises": "0.1.1|^1.3",
            "phpunit/phpunit": "^4.8.36|^5.7",
            "sebastian/comparator": ">=1.2.3"
        },
        "time": "2018-09-17T20:29:21+00:00",
        "type": "library",
        "installation-source": "dist",
        "autoload": {
            "psr-4": {
                "WPvividGoogle\\Auth\\": "src"
            }
        },
        "notification-url": "https://packagist.org/downloads/",
        "license": [
            "Apache-2.0"
        ],
        "description": "Google Auth Library for PHP",
        "homepage": "http://github.com/google/google-auth-library-php",
        "keywords": [
            "Authentication",
            "google",
            "oauth2"
        ]
    },
    {
        "name": "guzzle/guzzle",
        "version": "v3.9.3",
        "version_normalized": "3.9.3.0",
        "source": {
            "type": "git",
            "url": "https://github.com/guzzle/guzzle3.git",
            "reference": "0645b70d953bc1c067bbc8d5bc53194706b628d9"
        },
        "dist": {
            "type": "zip",
            "url": "https://api.github.com/repos/guzzle/guzzle3/zipball/0645b70d953bc1c067bbc8d5bc53194706b628d9",
            "reference": "0645b70d953bc1c067bbc8d5bc53194706b628d9",
            "shasum": "",
            "mirrors": [
                {
                    "url": "https://dl.laravel-china.org/%package%/%reference%.%type%",
                    "preferred": true
                }
            ]
        },
        "require": {
            "ext-curl": "*",
            "php": ">=5.3.3",
            "symfony/event-dispatcher": "~2.1"
        },
        "replace": {
            "guzzle/batch": "self.version",
            "guzzle/cache": "self.version",
            "guzzle/common": "self.version",
            "guzzle/http": "self.version",
            "guzzle/inflection": "self.version",
            "guzzle/iterator": "self.version",
            "guzzle/log": "self.version",
            "guzzle/parser": "self.version",
            "guzzle/plugin": "self.version",
            "guzzle/plugin-async": "self.version",
            "guzzle/plugin-backoff": "self.version",
            "guzzle/plugin-cache": "self.version",
            "guzzle/plugin-cookie": "self.version",
            "guzzle/plugin-curlauth": "self.version",
            "guzzle/plugin-error-response": "self.version",
            "guzzle/plugin-history": "self.version",
            "guzzle/plugin-log": "self.version",
            "guzzle/plugin-md5": "self.version",
            "guzzle/plugin-mock": "self.version",
            "guzzle/plugin-oauth": "self.version",
            "guzzle/service": "self.version",
            "guzzle/stream": "self.version"
        },
        "require-dev": {
            "doctrine/cache": "~1.3",
            "monolog/monolog": "~1.0",
            "phpunit/phpunit": "3.7.*",
            "psr/log": "~1.0",
            "symfony/class-loader": "~2.1",
            "zendframework/zend-cache": "2.*,<2.3",
            "zendframework/zend-log": "2.*,<2.3"
        },
        "suggest": {
            "guzzlehttp/guzzle": "Guzzle 5 has moved to a new package name. The package you have installed, Guzzle 3, is deprecated."
        },
        "time": "2015-03-18T18:23:50+00:00",
        "type": "library",
        "extra": {
            "branch-alias": {
                "dev-master": "3.9-dev"
            }
        },
        "installation-source": "dist",
        "autoload": {
            "psr-0": {
                "Guzzle": "src/",
                "Guzzle\\Tests": "tests/"
            }
        },
        "notification-url": "https://packagist.org/downloads/",
        "license": [
            "MIT"
        ],
        "authors": [
            {
                "name": "Michael Dowling",
                "email": "mtdowling@gmail.com",
                "homepage": "https://github.com/mtdowling"
            },
            {
                "name": "Guzzle Community",
                "homepage": "https://github.com/guzzle/guzzle/contributors"
            }
        ],
        "description": "PHP HTTP client. This library is deprecated in favor of https://packagist.org/packages/guzzlehttp/guzzle",
        "homepage": "http://guzzlephp.org/",
        "keywords": [
            "client",
            "curl",
            "framework",
            "http",
            "http client",
            "rest",
            "web service"
        ],
        "abandoned": "guzzlehttp/guzzle"
    },
    {
        "name": "guzzlehttp/guzzle",
        "version": "6.3.3",
        "version_normalized": "6.3.3.0",
        "source": {
            "type": "git",
            "url": "https://github.com/guzzle/guzzle.git",
            "reference": "407b0cb880ace85c9b63c5f9551db498cb2d50ba"
        },
        "dist": {
            "type": "zip",
            "url": "https://api.github.com/repos/guzzle/guzzle/zipball/407b0cb880ace85c9b63c5f9551db498cb2d50ba",
            "reference": "407b0cb880ace85c9b63c5f9551db498cb2d50ba",
            "shasum": "",
            "mirrors": [
                {
                    "url": "https://dl.laravel-china.org/%package%/%reference%.%type%",
                    "preferred": true
                }
            ]
        },
        "require": {
            "guzzlehttp/promises": "^1.0",
            "guzzlehttp/psr7": "^1.4",
            "php": ">=5.5"
        },
        "require-dev": {
            "ext-curl": "*",
            "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.4 || ^7.0",
            "psr/log": "^1.0"
        },
        "suggest": {
            "psr/log": "Required for using the Log middleware"
        },
        "time": "2018-04-22T15:46:56+00:00",
        "type": "library",
        "extra": {
            "branch-alias": {
                "dev-master": "6.3-dev"
            }
        },
        "installation-source": "dist",
        "autoload": {
            "files": [
                "src/functions_include.php"
            ],
            "psr-4": {
                "WPvividGuzzleHttp\\": "src/"
            }
        },
        "notification-url": "https://packagist.org/downloads/",
        "license": [
            "MIT"
        ],
        "authors": [
            {
                "name": "Michael Dowling",
                "email": "mtdowling@gmail.com",
                "homepage": "https://github.com/mtdowling"
            }
        ],
        "description": "Guzzle is a PHP HTTP client library",
        "homepage": "http://guzzlephp.org/",
        "keywords": [
            "client",
            "curl",
            "framework",
            "http",
            "http client",
            "rest",
            "web service"
        ]
    },
    {
        "name": "guzzlehttp/promises",
        "version": "v1.3.1",
        "version_normalized": "1.3.1.0",
        "source": {
            "type": "git",
            "url": "https://github.com/guzzle/promises.git",
            "reference": "a59da6cf61d80060647ff4d3eb2c03a2bc694646"
        },
        "dist": {
            "type": "zip",
            "url": "https://api.github.com/repos/guzzle/promises/zipball/a59da6cf61d80060647ff4d3eb2c03a2bc694646",
            "reference": "a59da6cf61d80060647ff4d3eb2c03a2bc694646",
            "shasum": "",
            "mirrors": [
                {
                    "url": "https://dl.laravel-china.org/%package%/%reference%.%type%",
                    "preferred": true
                }
            ]
        },
        "require": {
            "php": ">=5.5.0"
        },
        "require-dev": {
            "phpunit/phpunit": "^4.0"
        },
        "time": "2016-12-20T10:07:11+00:00",
        "type": "library",
        "extra": {
            "branch-alias": {
                "dev-master": "1.4-dev"
            }
        },
        "installation-source": "dist",
        "autoload": {
            "psr-4": {
                "WPvividGuzzleHttp\\Promise\\": "src/"
            },
            "files": [
                "src/functions_include.php"
            ]
        },
        "notification-url": "https://packagist.org/downloads/",
        "license": [
            "MIT"
        ],
        "authors": [
            {
                "name": "Michael Dowling",
                "email": "mtdowling@gmail.com",
                "homepage": "https://github.com/mtdowling"
            }
        ],
        "description": "Guzzle promises library",
        "keywords": [
            "promise"
        ]
    },
    {
        "name": "guzzlehttp/psr7",
        "version": "1.5.2",
        "version_normalized": "1.5.2.0",
        "source": {
            "type": "git",
            "url": "https://github.com/guzzle/psr7.git",
            "reference": "9f83dded91781a01c63574e387eaa769be769115"
        },
        "dist": {
            "type": "zip",
            "url": "https://api.github.com/repos/guzzle/psr7/zipball/9f83dded91781a01c63574e387eaa769be769115",
            "reference": "9f83dded91781a01c63574e387eaa769be769115",
            "shasum": "",
            "mirrors": [
                {
                    "url": "https://dl.laravel-china.org/%package%/%reference%.%type%",
                    "preferred": true
                }
            ]
        },
        "require": {
            "php": ">=5.4.0",
            "psr/http-message": "~1.0",
            "ralouphie/getallheaders": "^2.0.5"
        },
        "provide": {
            "psr/http-message-implementation": "1.0"
        },
        "require-dev": {
            "phpunit/phpunit": "~4.8.36 || ^5.7.27 || ^6.5.8"
        },
        "time": "2018-12-04T20:46:45+00:00",
        "type": "library",
        "extra": {
            "branch-alias": {
                "dev-master": "1.5-dev"
            }
        },
        "installation-source": "dist",
        "autoload": {
            "psr-4": {
                "WPvividGuzzleHttp\\Psr7\\": "src/"
            },
            "files": [
                "src/functions_include.php"
            ]
        },
        "notification-url": "https://packagist.org/downloads/",
        "license": [
            "MIT"
        ],
        "authors": [
            {
                "name": "Michael Dowling",
                "email": "mtdowling@gmail.com",
                "homepage": "https://github.com/mtdowling"
            },
            {
                "name": "Tobias Schultze",
                "homepage": "https://github.com/Tobion"
            }
        ],
        "description": "PSR-7 message implementation that also provides common utility methods",
        "keywords": [
            "http",
            "message",
            "psr-7",
            "request",
            "response",
            "stream",
            "uri",
            "url"
        ]
    },
    {
        "name": "monolog/monolog",
        "version": "1.24.0",
        "version_normalized": "1.24.0.0",
        "source": {
            "type": "git",
            "url": "https://github.com/Seldaek/monolog.git",
            "reference": "bfc9ebb28f97e7a24c45bdc3f0ff482e47bb0266"
        },
        "dist": {
            "type": "zip",
            "url": "https://api.github.com/repos/Seldaek/monolog/zipball/bfc9ebb28f97e7a24c45bdc3f0ff482e47bb0266",
            "reference": "bfc9ebb28f97e7a24c45bdc3f0ff482e47bb0266",
            "shasum": "",
            "mirrors": [
                {
                    "url": "https://dl.laravel-china.org/%package%/%reference%.%type%",
                    "preferred": true
                }
            ]
        },
        "require": {
            "php": ">=5.3.0",
            "psr/log": "~1.0"
        },
        "provide": {
            "psr/log-implementation": "1.0.0"
        },
        "require-dev": {
            "aws/aws-sdk-php": "^2.4.9 || ^3.0",
            "doctrine/couchdb": "~1.0@dev",
            "graylog2/gelf-php": "~1.0",
            "jakub-onderka/php-parallel-lint": "0.9",
            "php-amqplib/php-amqplib": "~2.4",
            "php-console/php-console": "^3.1.3",
            "phpunit/phpunit": "~4.5",
            "phpunit/phpunit-mock-objects": "2.3.0",
            "ruflin/elastica": ">=0.90 <3.0",
            "sentry/sentry": "^0.13",
            "swiftmailer/swiftmailer": "^5.3|^6.0"
        },
        "suggest": {
            "aws/aws-sdk-php": "Allow sending log messages to AWS services like DynamoDB",
            "doctrine/couchdb": "Allow sending log messages to a CouchDB server",
            "ext-amqp": "Allow sending log messages to an AMQP server (1.0+ required)",
            "ext-mongo": "Allow sending log messages to a MongoDB server",
            "graylog2/gelf-php": "Allow sending log messages to a GrayLog2 server",
            "mongodb/mongodb": "Allow sending log messages to a MongoDB server via PHP Driver",
            "php-amqplib/php-amqplib": "Allow sending log messages to an AMQP server using php-amqplib",
            "php-console/php-console": "Allow sending log messages to Google Chrome",
            "rollbar/rollbar": "Allow sending log messages to Rollbar",
            "ruflin/elastica": "Allow sending log messages to an Elastic Search server",
            "sentry/sentry": "Allow sending log messages to a Sentry server"
        },
        "time": "2018-11-05T09:00:11+00:00",
        "type": "library",
        "extra": {
            "branch-alias": {
                "dev-master": "2.0.x-dev"
            }
        },
        "installation-source": "dist",
        "autoload": {
            "psr-4": {
                "Monolog\\": "src/Monolog"
            }
        },
        "notification-url": "https://packagist.org/downloads/",
        "license": [
            "MIT"
        ],
        "authors": [
            {
                "name": "Jordi Boggiano",
                "email": "j.boggiano@seld.be",
                "homepage": "http://seld.be"
            }
        ],
        "description": "Sends your logs to files, sockets, inboxes, databases and various web services",
        "homepage": "http://github.com/Seldaek/monolog",
        "keywords": [
            "log",
            "logging",
            "psr-3"
        ]
    },
    {
        "name": "phpseclib/phpseclib",
        "version": "1.0.14",
        "version_normalized": "1.0.14.0",
        "source": {
            "type": "git",
            "url": "https://github.com/phpseclib/phpseclib.git",
            "reference": "7432a6959afe98b9e93153d0034b0f62ad890d31"
        },
        "dist": {
            "type": "zip",
            "url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/7432a6959afe98b9e93153d0034b0f62ad890d31",
            "reference": "7432a6959afe98b9e93153d0034b0f62ad890d31",
            "shasum": "",
            "mirrors": [
                {
                    "url": "https://dl.laravel-china.org/%package%/%reference%.%type%",
                    "preferred": true
                }
            ]
        },
        "require": {
            "php": ">=5.0.0"
        },
        "require-dev": {
            "phing/phing": "~2.7",
            "phpunit/phpunit": "^4.8.35|^5.7|^6.0",
            "sami/sami": "~2.0",
            "squizlabs/php_codesniffer": "~2.0"
        },
        "suggest": {
            "ext-gmp": "Install the GMP (GNU Multiple Precision) extension in order to speed up arbitrary precision integer arithmetic operations.",
            "ext-mcrypt": "Install the Mcrypt extension in order to speed up a wide variety of cryptographic operations.",
            "pear-pear/PHP_Compat": "Install PHP_Compat to get phpseclib working on PHP < 5.0.0."
        },
        "time": "2019-01-27T19:35:10+00:00",
        "type": "library",
        "installation-source": "dist",
        "autoload": {
            "psr-0": {
                "Crypt": "phpseclib/",
                "File": "phpseclib/",
                "Math": "phpseclib/",
                "Net": "phpseclib/",
                "System": "phpseclib/"
            },
            "files": [
                "phpseclib/bootstrap.php",
                "phpseclib/Crypt/Random.php"
            ]
        },
        "notification-url": "https://packagist.org/downloads/",
        "include-path": [
            "phpseclib/"
        ],
        "license": [
            "MIT"
        ],
        "authors": [
            {
                "name": "Jim Wigginton",
                "email": "terrafrost@php.net",
                "role": "Lead Developer"
            },
            {
                "name": "Patrick Monnerat",
                "email": "pm@datasphere.ch",
                "role": "Developer"
            },
            {
                "name": "Andreas Fischer",
                "email": "bantu@phpbb.com",
                "role": "Developer"
            },
            {
                "name": "Hans-Jürgen Petrich",
                "email": "petrich@tronic-media.com",
                "role": "Developer"
            },
            {
                "name": "Graham Campbell",
                "email": "graham@alt-three.com",
                "role": "Developer"
            }
        ],
        "description": "PHP Secure Communications Library - Pure-PHP implementations of RSA, AES, SSH2, SFTP, X.509 etc.",
        "homepage": "http://phpseclib.sourceforge.net",
        "keywords": [
            "BigInteger",
            "aes",
            "asn.1",
            "asn1",
            "blowfish",
            "crypto",
            "cryptography",
            "encryption",
            "rsa",
            "security",
            "sftp",
            "signature",
            "signing",
            "ssh",
            "twofish",
            "x.509",
            "x509"
        ]
    },
    {
        "name": "psr/cache",
        "version": "1.0.1",
        "version_normalized": "1.0.1.0",
        "source": {
            "type": "git",
            "url": "https://github.com/php-fig/cache.git",
            "reference": "d11b50ad223250cf17b86e38383413f5a6764bf8"
        },
        "dist": {
            "type": "zip",
            "url": "https://api.github.com/repos/php-fig/cache/zipball/d11b50ad223250cf17b86e38383413f5a6764bf8",
            "reference": "d11b50ad223250cf17b86e38383413f5a6764bf8",
            "shasum": "",
            "mirrors": [
                {
                    "url": "https://dl.laravel-china.org/%package%/%reference%.%type%",
                    "preferred": true
                }
            ]
        },
        "require": {
            "php": ">=5.3.0"
        },
        "time": "2016-08-06T20:24:11+00:00",
        "type": "library",
        "extra": {
            "branch-alias": {
                "dev-master": "1.0.x-dev"
            }
        },
        "installation-source": "dist",
        "autoload": {
            "psr-4": {
                "WPvividPsr\\Cache\\": "src/"
            }
        },
        "notification-url": "https://packagist.org/downloads/",
        "license": [
            "MIT"
        ],
        "authors": [
            {
                "name": "PHP-FIG",
                "homepage": "http://www.php-fig.org/"
            }
        ],
        "description": "Common interface for caching libraries",
        "keywords": [
            "cache",
            "psr",
            "psr-6"
        ]
    },
    {
        "name": "psr/http-message",
        "version": "1.0.1",
        "version_normalized": "1.0.1.0",
        "source": {
            "type": "git",
            "url": "https://github.com/php-fig/http-message.git",
            "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363"
        },
        "dist": {
            "type": "zip",
            "url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363",
            "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363",
            "shasum": "",
            "mirrors": [
                {
                    "url": "https://dl.laravel-china.org/%package%/%reference%.%type%",
                    "preferred": true
                }
            ]
        },
        "require": {
            "php": ">=5.3.0"
        },
        "time": "2016-08-06T14:39:51+00:00",
        "type": "library",
        "extra": {
            "branch-alias": {
                "dev-master": "1.0.x-dev"
            }
        },
        "installation-source": "dist",
        "autoload": {
            "psr-4": {
                "WPvividPsr\\Http\\Message\\": "src/"
            }
        },
        "notification-url": "https://packagist.org/downloads/",
        "license": [
            "MIT"
        ],
        "authors": [
            {
                "name": "PHP-FIG",
                "homepage": "http://www.php-fig.org/"
            }
        ],
        "description": "Common interface for HTTP messages",
        "homepage": "https://github.com/php-fig/http-message",
        "keywords": [
            "http",
            "http-message",
            "psr",
            "psr-7",
            "request",
            "response"
        ]
    },
    {
        "name": "psr/log",
        "version": "1.1.0",
        "version_normalized": "1.1.0.0",
        "source": {
            "type": "git",
            "url": "https://github.com/php-fig/log.git",
            "reference": "6c001f1daafa3a3ac1d8ff69ee4db8e799a654dd"
        },
        "dist": {
            "type": "zip",
            "url": "https://api.github.com/repos/php-fig/log/zipball/6c001f1daafa3a3ac1d8ff69ee4db8e799a654dd",
            "reference": "6c001f1daafa3a3ac1d8ff69ee4db8e799a654dd",
            "shasum": "",
            "mirrors": [
                {
                    "url": "https://dl.laravel-china.org/%package%/%reference%.%type%",
                    "preferred": true
                }
            ]
        },
        "require": {
            "php": ">=5.3.0"
        },
        "time": "2018-11-20T15:27:04+00:00",
        "type": "library",
        "extra": {
            "branch-alias": {
                "dev-master": "1.0.x-dev"
            }
        },
        "installation-source": "dist",
        "autoload": {
            "psr-4": {
                "WPvividPsr\\Log\\": "Psr/Log/"
            }
        },
        "notification-url": "https://packagist.org/downloads/",
        "license": [
            "MIT"
        ],
        "authors": [
            {
                "name": "PHP-FIG",
                "homepage": "http://www.php-fig.org/"
            }
        ],
        "description": "Common interface for logging libraries",
        "homepage": "https://github.com/php-fig/log",
        "keywords": [
            "log",
            "psr",
            "psr-3"
        ]
    },
    {
        "name": "ralouphie/getallheaders",
        "version": "2.0.5",
        "version_normalized": "2.0.5.0",
        "source": {
            "type": "git",
            "url": "https://github.com/ralouphie/getallheaders.git",
            "reference": "5601c8a83fbba7ef674a7369456d12f1e0d0eafa"
        },
        "dist": {
            "type": "zip",
            "url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/5601c8a83fbba7ef674a7369456d12f1e0d0eafa",
            "reference": "5601c8a83fbba7ef674a7369456d12f1e0d0eafa",
            "shasum": "",
            "mirrors": [
                {
                    "url": "https://dl.laravel-china.org/%package%/%reference%.%type%",
                    "preferred": true
                }
            ]
        },
        "require": {
            "php": ">=5.3"
        },
        "require-dev": {
            "phpunit/phpunit": "~3.7.0",
            "satooshi/php-coveralls": ">=1.0"
        },
        "time": "2016-02-11T07:05:27+00:00",
        "type": "library",
        "installation-source": "dist",
        "autoload": {
            "files": [
                "src/getallheaders.php"
            ]
        },
        "notification-url": "https://packagist.org/downloads/",
        "license": [
            "MIT"
        ],
        "authors": [
            {
                "name": "Ralph Khattar",
                "email": "ralph.khattar@gmail.com"
            }
        ],
        "description": "A polyfill for getallheaders."
    },
    {
        "name": "symfony/event-dispatcher",
        "version": "v2.8.49",
        "version_normalized": "2.8.49.0",
        "source": {
            "type": "git",
            "url": "https://github.com/symfony/event-dispatcher.git",
            "reference": "a77e974a5fecb4398833b0709210e3d5e334ffb0"
        },
        "dist": {
            "type": "zip",
            "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/a77e974a5fecb4398833b0709210e3d5e334ffb0",
            "reference": "a77e974a5fecb4398833b0709210e3d5e334ffb0",
            "shasum": "",
            "mirrors": [
                {
                    "url": "https://dl.laravel-china.org/%package%/%reference%.%type%",
                    "preferred": true
                }
            ]
        },
        "require": {
            "php": ">=5.3.9"
        },
        "require-dev": {
            "psr/log": "~1.0",
            "symfony/config": "^2.0.5|~3.0.0",
            "symfony/dependency-injection": "~2.6|~3.0.0",
            "symfony/expression-language": "~2.6|~3.0.0",
            "symfony/stopwatch": "~2.3|~3.0.0"
        },
        "suggest": {
            "symfony/dependency-injection": "",
            "symfony/http-kernel": ""
        },
        "time": "2018-11-21T14:20:20+00:00",
        "type": "library",
        "extra": {
            "branch-alias": {
                "dev-master": "2.8-dev"
            }
        },
        "installation-source": "dist",
        "autoload": {
            "psr-4": {
                "Symfony\\Component\\EventDispatcher\\": ""
            },
            "exclude-from-classmap": [
                "/Tests/"
            ]
        },
        "notification-url": "https://packagist.org/downloads/",
        "license": [
            "MIT"
        ],
        "authors": [
            {
                "name": "Fabien Potencier",
                "email": "fabien@symfony.com"
            },
            {
                "name": "Symfony Community",
                "homepage": "https://symfony.com/contributors"
            }
        ],
        "description": "Symfony EventDispatcher Component",
        "homepage": "https://symfony.com"
    }
]
vendor/composer/autoload_static.php000064400000015220151327705670013575 0ustar00<?php

// autoload_static.php @generated by Composer

namespace Composer\Autoload;

class ComposerStaticInit381ef710b1ae857a1b0cca4550c7cd7e
{
    public static $files = array (
        //'7b11c4dc42b3b3023073cb14e519683c' => __DIR__ . '/..' . '/ralouphie/getallheaders/src/getallheaders.php',
        //'c964ee0ededf28c96ebd9db5099ef910' => __DIR__ . '/..' . '/guzzlehttp/promises/src/functions_include.php',
        //'a0edc8309cc5e1d60e3047b5df6b7052' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/functions_include.php',
        //'37a3dc5111fe8f707ab4c132ef1dbc62' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/functions_include.php',
        //'decc78cc4436b1292c6c0d151b19445c' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/bootstrap.php',
        //'3919eeb97e98d4648304477f8ef734ba' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/Random.php',
        '7b11c4dc42b3b3023073cb14e519684c' => __DIR__ . '/..' . '/ralouphie/getallheaders/src/getallheaders.php',
        'c964ee0ededf28c96ebd9db5099ef911' => __DIR__ . '/..' . '/guzzlehttp/promises/src/functions_include.php',
        'a0edc8309cc5e1d60e3047b5df6b7057' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/functions_include.php',
        '37a3dc5111fe8f707ab4c132ef1dbc63' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/functions_include.php',
        'decc78cc4436b1292c6c0d151b19446c' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/bootstrap.php',
        '3919eeb97e98d4648304477f8ef735ba' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/Random.php',

    );

    public static $prefixLengthsPsr4 = array (
        'S' => 
        array (
            'Symfony\\Component\\EventDispatcher\\' => 34,
        ),
        /*'P' =>
        array (
            'Psr\\Log\\' => 8,
            'Psr\\Http\\Message\\' => 17,
            'Psr\\Cache\\' => 10,
        ),*/
        /*'M' =>
        array (
            'Monolog\\' => 8,
        ),*/
        'W' =>
        array (
            'WPvividGuzzleHttp\\Psr7\\' => 23,
            'WPvividGuzzleHttp\\Promise\\' => 26,
            'WPvividGuzzleHttp\\' => 18,
            'WPvividGoogle\\Auth\\' => 19,
            'WPvividPsr\\Log\\' => 15,
            'WPvividPsr\\Http\\Message\\' => 24,
            'WPvividPsr\\Cache\\' => 17,
            'WPvividMonolog\\' => 15,
        ),
        'F' => 
        array (
            'Firebase\\JWT\\' => 13,
        ),
    );

    public static $prefixDirsPsr4 = array (
        'Symfony\\Component\\EventDispatcher\\' => 
        array (
            0 => __DIR__ . '/..' . '/symfony/event-dispatcher',
        ),
        'WPvividPsr\\Log\\' =>
        array (
            0 => __DIR__ . '/..' . '/psr/log/Psr/Log',
        ),
        'WPvividPsr\\Http\\Message\\' =>
        array (
            0 => __DIR__ . '/..' . '/psr/http-message/src',
        ),
        'WPvividPsr\\Cache\\' =>
        array (
            0 => __DIR__ . '/..' . '/psr/cache/src',
        ),
        'WPvividMonolog\\' =>
        array (
            0 => __DIR__ . '/..' . '/monolog/monolog/src/Monolog',
        ),
        'WPvividGuzzleHttp\\Psr7\\' =>
        array (
            0 => __DIR__ . '/..' . '/guzzlehttp/psr7/src',
        ),
        'WPvividGuzzleHttp\\Promise\\' =>
        array (
            0 => __DIR__ . '/..' . '/guzzlehttp/promises/src',
        ),
        'WPvividGuzzleHttp\\' =>
        array (
            0 => __DIR__ . '/..' . '/guzzlehttp/guzzle/src',
        ),
        'WPvividGoogle\\Auth\\' =>
        array (
            0 => __DIR__ . '/..' . '/google/auth/src',
        ),
        'Firebase\\JWT\\' => 
        array (
            0 => __DIR__ . '/..' . '/firebase/php-jwt/src',
        ),
    );

    public static $prefixesPsr0 = array (
        'S' => 
        array (
            'System' => 
            array (
                0 => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib',
            ),
        ),
        'N' => 
        array (
            'Net' => 
            array (
                0 => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib',
            ),
        ),
        'M' => 
        array (
            'Math' => 
            array (
                0 => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib',
            ),
        ),
        'G' => 
        array (
            'Guzzle\\Tests' => 
            array (
                0 => __DIR__ . '/..' . '/guzzle/guzzle/tests',
            ),
            'Guzzle' => 
            array (
                0 => __DIR__ . '/..' . '/guzzle/guzzle/src',
            ),
            'Google_Service_' => 
            array (
                0 => __DIR__ . '/..' . '/google/apiclient-services/src',
            ),
            'Google_' => 
            array (
                0 => __DIR__ . '/../..' . '/includes/lib/google-api-php-client/src',
            ),
        ),
        'W' =>
            array (
                'WPvivid_Google_' =>
                    array (
                        0 => __DIR__ . '/../..' . '/includes/lib2/google-api-php-client/src',
                    ),
        ),
        'F' => 
        array (
            'File' => 
            array (
                0 => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib',
            ),
        ),
        'C' => 
        array (
            'Crypt' => 
            array (
                0 => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib',
            ),
        ),
        'A' => 
        array (
            'Aws' => 
            array (
                0 => __DIR__ . '/../..' . '/includes/lib/aws-sdk-php-2.8.31/src',
            ),
        ),
    );

    public static $classMap = array (
        'Google_Service_Exception' => __DIR__ . '/../..' . '/includes/lib/google-api-php-client/src/Google/Service/Exception.php',
        'Google_Service_Resource' => __DIR__ . '/../..' . '/includes/lib/google-api-php-client/src/Google/Service/Resource.php',
        'WPvivid_Google_Service_Exception' => __DIR__ . '/../..' . '/includes/lib2/google-api-php-client/src/WPvivid/Google/Service/Exception.php',
        'WPvivid_Google_Service_Resource' => __DIR__ . '/../..' . '/includes/lib2/google-api-php-client/src/WPvivid/Google/Service/Resource.php',
    );

    public static function getInitializer(ClassLoader $loader)
    {
        return \Closure::bind(function () use ($loader) {
            $loader->prefixLengthsPsr4 = ComposerStaticInit381ef710b1ae857a1b0cca4550c7cd7e::$prefixLengthsPsr4;
            $loader->prefixDirsPsr4 = ComposerStaticInit381ef710b1ae857a1b0cca4550c7cd7e::$prefixDirsPsr4;
            $loader->prefixesPsr0 = ComposerStaticInit381ef710b1ae857a1b0cca4550c7cd7e::$prefixesPsr0;
            $loader->classMap = ComposerStaticInit381ef710b1ae857a1b0cca4550c7cd7e::$classMap;

        }, null, ClassLoader::class);
    }
}
vendor/composer/autoload_classmap.php000064400000001206151327705670014110 0ustar00<?php

// autoload_classmap.php @generated by Composer

$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);

return array(
    'Google_Service_Exception' => $baseDir . '/includes/lib/google-api-php-client/src/Google/Service/Exception.php',
    'Google_Service_Resource' => $baseDir . '/includes/lib/google-api-php-client/src/Google/Service/Resource.php',
    'WPvivid_Google_Service_Exception' => $baseDir .  '/includes/lib2/google-api-php-client/src/WPvivid/Google/Service/Exception.php',
    'WPvivid_Google_Service_Resource' => $baseDir . '/includes/lib2/google-api-php-client/src/WPvivid/Google/Service/Resource.php',
);
vendor/guzzle/guzzle/composer.json000064400000004701151327705670013442 0ustar00{
    "name": "guzzle/guzzle",
    "type": "library",
    "description": "PHP HTTP client. This library is deprecated in favor of https://packagist.org/packages/guzzlehttp/guzzle",
    "keywords": ["framework", "http", "rest", "web service", "curl", "client", "HTTP client"],
    "homepage": "http://guzzlephp.org/",
    "license": "MIT",

    "authors": [
        {
            "name": "Michael Dowling",
            "email": "mtdowling@gmail.com",
            "homepage": "https://github.com/mtdowling"
        },
        {
            "name": "Guzzle Community",
            "homepage": "https://github.com/guzzle/guzzle/contributors"
        }
    ],

    "replace": {
        "guzzle/batch": "self.version",
        "guzzle/cache": "self.version",
        "guzzle/common": "self.version",
        "guzzle/http": "self.version",
        "guzzle/inflection": "self.version",
        "guzzle/iterator": "self.version",
        "guzzle/log": "self.version",
        "guzzle/parser": "self.version",
        "guzzle/plugin": "self.version",
        "guzzle/plugin-async": "self.version",
        "guzzle/plugin-backoff": "self.version",
        "guzzle/plugin-cache": "self.version",
        "guzzle/plugin-cookie": "self.version",
        "guzzle/plugin-curlauth": "self.version",
        "guzzle/plugin-error-response": "self.version",
        "guzzle/plugin-history": "self.version",
        "guzzle/plugin-log": "self.version",
        "guzzle/plugin-md5": "self.version",
        "guzzle/plugin-mock": "self.version",
        "guzzle/plugin-oauth": "self.version",
        "guzzle/service": "self.version",
        "guzzle/stream": "self.version"
    },

    "require": {
        "php": ">=5.3.3",
        "ext-curl": "*",
        "symfony/event-dispatcher": "~2.1"
    },

    "autoload": {
        "psr-0": {
            "Guzzle": "src/",
            "Guzzle\\Tests": "tests/"
        }
    },

    "suggest": {
        "guzzlehttp/guzzle": "Guzzle 5 has moved to a new package name. The package you have installed, Guzzle 3, is deprecated."
    },

    "scripts": {
        "test": "phpunit"
    },

    "require-dev": {
        "doctrine/cache": "~1.3",
        "symfony/class-loader": "~2.1",
        "monolog/monolog": "~1.0",
        "psr/log": "~1.0",
        "zendframework/zend-cache": "2.*,<2.3",
        "zendframework/zend-log": "2.*,<2.3",
        "phpunit/phpunit": "3.7.*"
    },

    "extra": {
        "branch-alias": {
            "dev-master": "3.9-dev"
        }
    }
}
vendor/guzzle/guzzle/README.md000064400000004757151327705670012212 0ustar00Guzzle, PHP HTTP client and webservice framework
================================================

# This is an old version of Guzzle

This repository is for Guzzle 3.x. Guzzle 5.x, the new version of Guzzle, has
been released and is available at
[https://github.com/guzzle/guzzle](https://github.com/guzzle/guzzle). The
documentation for Guzzle version 5+ can be found at
[http://guzzlephp.org](http://guzzlephp.org).

Guzzle 3 is only maintained for bug and security fixes. Guzzle 3 will be EOL
at some point in late 2015.

### About Guzzle 3

[![Composer Downloads](https://poser.pugx.org/guzzle/guzzle/d/total.png)](https://packagist.org/packages/guzzle/guzzle)
 [![Build Status](https://secure.travis-ci.org/guzzle/guzzle3.png?branch=master)](http://travis-ci.org/guzzle/guzzle3)

- Extremely powerful API provides all the power of cURL with a simple interface.
- Truly take advantage of HTTP/1.1 with persistent connections, connection pooling, and parallel requests.
- Service description DSL allows you build awesome web service clients faster.
- Symfony2 event-based plugin system allows you to completely modify the behavior of a request.

Get answers with: [Documentation](http://guzzle3.readthedocs.org/en/latest/), [Forums](https://groups.google.com/forum/?hl=en#!forum/guzzle), IRC ([#guzzlephp](irc://irc.freenode.net/#guzzlephp) @ irc.freenode.net)

### Installing via Composer

The recommended way to install Guzzle is through [Composer](http://getcomposer.org).

```bash
# Install Composer
curl -sS https://getcomposer.org/installer | php

# Add Guzzle as a dependency
php composer.phar require guzzle/guzzle:~3.9
```

After installing, you need to require Composer's autoloader:

```php
require 'vendor/autoload.php';
```
## Known Issues

1. Problem following a specific redirect: https://github.com/guzzle/guzzle/issues/385.
   This has been fixed in Guzzle 4/5.
2. Root XML attributes not serialized in a service description: https://github.com/guzzle/guzzle3/issues/5.
   This has been fixed in Guzzle 4/5.
3. Accept-Encoding not preserved when following redirect: https://github.com/guzzle/guzzle3/issues/9
   Fixed in Guzzle 4/5.
4. String "Array" Transmitted w/ PostFiles and Duplicate Aggregator: https://github.com/guzzle/guzzle3/issues/10
   Fixed in Guzzle 4/5.
5. Recursive model references with array items: https://github.com/guzzle/guzzle3/issues/13
   Fixed in Guzzle 4/5
6. String "Array" Transmitted w/ PostFiles and Duplicate Aggregator: https://github.com/guzzle/guzzle3/issues/10
   Fixed in Guzzle 4/5.
vendor/guzzle/guzzle/phar-stub.php000064400000001130151327705670013327 0ustar00<?php

Phar::mapPhar('guzzle.phar');

require_once 'phar://guzzle.phar/vendor/symfony/class-loader/Symfony/Component/ClassLoader/UniversalClassLoader.php';

$classLoader = new Symfony\Component\ClassLoader\UniversalClassLoader();
$classLoader->registerNamespaces(array(
    'Guzzle' => 'phar://guzzle.phar/src',
    'Symfony\\Component\\EventDispatcher' => 'phar://guzzle.phar/vendor/symfony/event-dispatcher',
    'Doctrine' => 'phar://guzzle.phar/vendor/doctrine/common/lib',
    'WPvividMonolog' => 'phar://guzzle.phar/vendor/monolog/monolog/src'
));
$classLoader->register();

__HALT_COMPILER();
vendor/guzzle/guzzle/src/Guzzle/Service/composer.json000064400000001436151327705670017113 0ustar00{
    "name": "guzzle/service",
    "description": "Guzzle service component for abstracting RESTful web services",
    "homepage": "http://guzzlephp.org/",
    "keywords": ["web service", "webservice", "REST", "guzzle"],
    "license": "MIT",
    "authors": [
        {
            "name": "Michael Dowling",
            "email": "mtdowling@gmail.com",
            "homepage": "https://github.com/mtdowling"
        }
    ],
    "require": {
        "php": ">=5.3.2",
        "guzzle/cache": "self.version",
        "guzzle/http": "self.version",
        "guzzle/inflection": "self.version"
    },
    "autoload": {
        "psr-0": { "Guzzle\\Service": "" }
    },
    "target-dir": "Guzzle/Service",
    "extra": {
        "branch-alias": {
            "dev-master": "3.7-dev"
        }
    }
}
vendor/guzzle/guzzle/src/Guzzle/Service/Command/AbstractCommand.php000064400000030064151327705670021521 0ustar00<?php

namespace Guzzle\Service\Command;

use Guzzle\Common\Collection;
use Guzzle\Common\Exception\InvalidArgumentException;
use Guzzle\Http\Message\RequestInterface;
use Guzzle\Http\Curl\CurlHandle;
use Guzzle\Service\Client;
use Guzzle\Service\ClientInterface;
use Guzzle\Service\Description\Operation;
use Guzzle\Service\Description\OperationInterface;
use Guzzle\Service\Description\ValidatorInterface;
use Guzzle\Service\Description\SchemaValidator;
use Guzzle\Service\Exception\CommandException;
use Guzzle\Service\Exception\ValidationException;

/**
 * Command object to handle preparing and processing client requests and responses of the requests
 */
abstract class AbstractCommand extends Collection implements CommandInterface
{
    // @deprecated: Option used to specify custom headers to add to the generated request
    const HEADERS_OPTION = 'command.headers';
    // @deprecated: Option used to add an onComplete method to a command
    const ON_COMPLETE = 'command.on_complete';
    // @deprecated: Option used to change the entity body used to store a response
    const RESPONSE_BODY = 'command.response_body';

    // Option used to add request options to the request created by a command
    const REQUEST_OPTIONS = 'command.request_options';
    // command values to not count as additionalParameters
    const HIDDEN_PARAMS = 'command.hidden_params';
    // Option used to disable any pre-sending command validation
    const DISABLE_VALIDATION = 'command.disable_validation';
    // Option used to override how a command result will be formatted
    const RESPONSE_PROCESSING = 'command.response_processing';
    // Different response types that commands can use
    const TYPE_RAW = 'raw';
    const TYPE_MODEL = 'model';
    const TYPE_NO_TRANSLATION = 'no_translation';

    /** @var ClientInterface Client object used to execute the command */
    protected $client;

    /** @var RequestInterface The request object associated with the command */
    protected $request;

    /** @var mixed The result of the command */
    protected $result;

    /** @var OperationInterface API information about the command */
    protected $operation;

    /** @var mixed callable */
    protected $onComplete;

    /** @var ValidatorInterface Validator used to prepare and validate properties against a JSON schema */
    protected $validator;

    /**
     * @param array|Collection   $parameters Collection of parameters to set on the command
     * @param OperationInterface $operation Command definition from description
     */
    public function __construct($parameters = array(), OperationInterface $operation = null)
    {
        parent::__construct($parameters);
        $this->operation = $operation ?: $this->createOperation();
        foreach ($this->operation->getParams() as $name => $arg) {
            $currentValue = $this[$name];
            $configValue = $arg->getValue($currentValue);
            // If default or static values are set, then this should always be updated on the config object
            if ($currentValue !== $configValue) {
                $this[$name] = $configValue;
            }
        }

        $headers = $this[self::HEADERS_OPTION];
        if (!$headers instanceof Collection) {
            $this[self::HEADERS_OPTION] = new Collection((array) $headers);
        }

        // You can set a command.on_complete option in your parameters to set an onComplete callback
        if ($onComplete = $this['command.on_complete']) {
            unset($this['command.on_complete']);
            $this->setOnComplete($onComplete);
        }

        // Set the hidden additional parameters
        if (!$this[self::HIDDEN_PARAMS]) {
            $this[self::HIDDEN_PARAMS] = array(
                self::HEADERS_OPTION,
                self::RESPONSE_PROCESSING,
                self::HIDDEN_PARAMS,
                self::REQUEST_OPTIONS
            );
        }

        $this->init();
    }

    /**
     * Custom clone behavior
     */
    public function __clone()
    {
        $this->request = null;
        $this->result = null;
    }

    /**
     * Execute the command in the same manner as calling a function
     *
     * @return mixed Returns the result of {@see AbstractCommand::execute}
     */
    public function __invoke()
    {
        return $this->execute();
    }

    public function getName()
    {
        return $this->operation->getName();
    }

    /**
     * Get the API command information about the command
     *
     * @return OperationInterface
     */
    public function getOperation()
    {
        return $this->operation;
    }

    public function setOnComplete($callable)
    {
        if (!is_callable($callable)) {
            throw new InvalidArgumentException('The onComplete function must be callable');
        }

        $this->onComplete = $callable;

        return $this;
    }

    public function execute()
    {
        if (!$this->client) {
            throw new CommandException('A client must be associated with the command before it can be executed.');
        }

        return $this->client->execute($this);
    }

    public function getClient()
    {
        return $this->client;
    }

    public function setClient(ClientInterface $client)
    {
        $this->client = $client;

        return $this;
    }

    public function getRequest()
    {
        if (!$this->request) {
            throw new CommandException('The command must be prepared before retrieving the request');
        }

        return $this->request;
    }

    public function getResponse()
    {
        if (!$this->isExecuted()) {
            $this->execute();
        }

        return $this->request->getResponse();
    }

    public function getResult()
    {
        if (!$this->isExecuted()) {
            $this->execute();
        }

        if (null === $this->result) {
            $this->process();
            // Call the onComplete method if one is set
            if ($this->onComplete) {
                call_user_func($this->onComplete, $this);
            }
        }

        return $this->result;
    }

    public function setResult($result)
    {
        $this->result = $result;

        return $this;
    }

    public function isPrepared()
    {
        return $this->request !== null;
    }

    public function isExecuted()
    {
        return $this->request !== null && $this->request->getState() == 'complete';
    }

    public function prepare()
    {
        if (!$this->isPrepared()) {
            if (!$this->client) {
                throw new CommandException('A client must be associated with the command before it can be prepared.');
            }

            // If no response processing value was specified, then attempt to use the highest level of processing
            if (!isset($this[self::RESPONSE_PROCESSING])) {
                $this[self::RESPONSE_PROCESSING] = self::TYPE_MODEL;
            }

            // Notify subscribers of the client that the command is being prepared
            $this->client->dispatch('command.before_prepare', array('command' => $this));

            // Fail on missing required arguments, and change parameters via filters
            $this->validate();
            // Delegate to the subclass that implements the build method
            $this->build();

            // Add custom request headers set on the command
            if ($headers = $this[self::HEADERS_OPTION]) {
                foreach ($headers as $key => $value) {
                    $this->request->setHeader($key, $value);
                }
            }

            // Add any curl options to the request
            if ($options = $this[Client::CURL_OPTIONS]) {
                $this->request->getCurlOptions()->overwriteWith(CurlHandle::parseCurlConfig($options));
            }

            // Set a custom response body
            if ($responseBody = $this[self::RESPONSE_BODY]) {
                $this->request->setResponseBody($responseBody);
            }

            $this->client->dispatch('command.after_prepare', array('command' => $this));
        }

        return $this->request;
    }

    /**
     * Set the validator used to validate and prepare command parameters and nested JSON schemas. If no validator is
     * set, then the command will validate using the default {@see SchemaValidator}.
     *
     * @param ValidatorInterface $validator Validator used to prepare and validate properties against a JSON schema
     *
     * @return self
     */
    public function setValidator(ValidatorInterface $validator)
    {
        $this->validator = $validator;

        return $this;
    }

    public function getRequestHeaders()
    {
        return $this[self::HEADERS_OPTION];
    }

    /**
     * Initialize the command (hook that can be implemented in subclasses)
     */
    protected function init() {}

    /**
     * Create the request object that will carry out the command
     */
    abstract protected function build();

    /**
     * Hook used to create an operation for concrete commands that are not associated with a service description
     *
     * @return OperationInterface
     */
    protected function createOperation()
    {
        return new Operation(array('name' => get_class($this)));
    }

    /**
     * Create the result of the command after the request has been completed.
     * Override this method in subclasses to customize this behavior
     */
    protected function process()
    {
        $this->result = $this[self::RESPONSE_PROCESSING] != self::TYPE_RAW
            ? DefaultResponseParser::getInstance()->parse($this)
            : $this->request->getResponse();
    }

    /**
     * Validate and prepare the command based on the schema and rules defined by the command's Operation object
     *
     * @throws ValidationException when validation errors occur
     */
    protected function validate()
    {
        // Do not perform request validation/transformation if it is disable
        if ($this[self::DISABLE_VALIDATION]) {
            return;
        }

        $errors = array();
        $validator = $this->getValidator();
        foreach ($this->operation->getParams() as $name => $schema) {
            $value = $this[$name];
            if (!$validator->validate($schema, $value)) {
                $errors = array_merge($errors, $validator->getErrors());
            } elseif ($value !== $this[$name]) {
                // Update the config value if it changed and no validation errors were encountered
                $this->data[$name] = $value;
            }
        }

        // Validate additional parameters
        $hidden = $this[self::HIDDEN_PARAMS];

        if ($properties = $this->operation->getAdditionalParameters()) {
            foreach ($this->toArray() as $name => $value) {
                // It's only additional if it isn't defined in the schema
                if (!$this->operation->hasParam($name) && !in_array($name, $hidden)) {
                    // Always set the name so that error messages are useful
                    $properties->setName($name);
                    if (!$validator->validate($properties, $value)) {
                        $errors = array_merge($errors, $validator->getErrors());
                    } elseif ($value !== $this[$name]) {
                        $this->data[$name] = $value;
                    }
                }
            }
        }

        if (!empty($errors)) {
            $e = new ValidationException('Validation errors: ' . implode("\n", $errors));
            $e->setErrors($errors);
            throw $e;
        }
    }

    /**
     * Get the validator used to prepare and validate properties. If no validator has been set on the command, then
     * the default {@see SchemaValidator} will be used.
     *
     * @return ValidatorInterface
     */
    protected function getValidator()
    {
        if (!$this->validator) {
            $this->validator = SchemaValidator::getInstance();
        }

        return $this->validator;
    }

    /**
     * Get array of any validation errors
     * If no validator has been set then return false
     */
    public function getValidationErrors()
    {
        if (!$this->validator) {
            return false;
        }

        return $this->validator->getErrors();
    }
}
vendor/guzzle/guzzle/src/Guzzle/Service/Command/CreateResponseClassEvent.php000064400000001074151327705670023370 0ustar00<?php

namespace Guzzle\Service\Command;

use Guzzle\Common\Event;

/**
 * Event class emitted with the operation.parse_class event
 */
class CreateResponseClassEvent extends Event
{
    /**
     * Set the result of the object creation
     *
     * @param mixed $result Result value to set
     */
    public function setResult($result)
    {
        $this['result'] = $result;
        $this->stopPropagation();
    }

    /**
     * Get the created object
     *
     * @return mixed
     */
    public function getResult()
    {
        return $this['result'];
    }
}
vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/RequestVisitorInterface.php000064400000002067151327705670030072 0ustar00<?php

namespace Guzzle\Service\Command\LocationVisitor\Request;

use Guzzle\Http\Message\RequestInterface;
use Guzzle\Service\Description\Parameter;
use Guzzle\Service\Command\CommandInterface;

/**
 * Location visitor used to add values to different locations in a request with different behaviors as needed
 */
interface RequestVisitorInterface
{
    /**
     * Called after visiting all parameters
     *
     * @param CommandInterface $command Command being visited
     * @param RequestInterface $request Request being visited
     */
    public function after(CommandInterface $command, RequestInterface $request);

    /**
     * Called once for each parameter being visited that matches the location type
     *
     * @param CommandInterface $command Command being visited
     * @param RequestInterface $request Request being visited
     * @param Parameter        $param   Parameter being visited
     * @param mixed            $value   Value to set
     */
    public function visit(CommandInterface $command, RequestInterface $request, Parameter $param, $value);
}
vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/JsonVisitor.php000064400000003750151327705670025532 0ustar00<?php

namespace Guzzle\Service\Command\LocationVisitor\Request;

use Guzzle\Http\Message\RequestInterface;
use Guzzle\Service\Command\CommandInterface;
use Guzzle\Service\Description\Parameter;

/**
 * Visitor used to apply a parameter to an array that will be serialized as a top level key-value pair in a JSON body
 */
class JsonVisitor extends AbstractRequestVisitor
{
    /** @var bool Whether or not to add a Content-Type header when JSON is found */
    protected $jsonContentType = 'application/json';

    /** @var \SplObjectStorage Data object for persisting JSON data */
    protected $data;

    public function __construct()
    {
        $this->data = new \SplObjectStorage();
    }

    /**
     * Set the Content-Type header to add to the request if JSON is added to the body. This visitor does not add a
     * Content-Type header unless you specify one here.
     *
     * @param string $header Header to set when JSON is added (e.g. application/json)
     *
     * @return self
     */
    public function setContentTypeHeader($header = 'application/json')
    {
        $this->jsonContentType = $header;

        return $this;
    }

    public function visit(CommandInterface $command, RequestInterface $request, Parameter $param, $value)
    {
        if (isset($this->data[$command])) {
            $json = $this->data[$command];
        } else {
            $json = array();
        }
        $json[$param->getWireName()] = $this->prepareValue($value, $param);
        $this->data[$command] = $json;
    }

    public function after(CommandInterface $command, RequestInterface $request)
    {
        if (isset($this->data[$command])) {
            // Don't overwrite the Content-Type if one is set
            if ($this->jsonContentType && !$request->hasHeader('Content-Type')) {
                $request->setHeader('Content-Type', $this->jsonContentType);
            }

            $request->setBody(json_encode($this->data[$command]));
            unset($this->data[$command]);
        }
    }
}
vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/PostFileVisitor.php000064400000001276151327705670026347 0ustar00<?php

namespace Guzzle\Service\Command\LocationVisitor\Request;

use Guzzle\Http\Message\RequestInterface;
use Guzzle\Http\Message\PostFileInterface;
use Guzzle\Service\Command\CommandInterface;
use Guzzle\Service\Description\Parameter;

/**
 * Visitor used to apply a parameter to a POST file
 */
class PostFileVisitor extends AbstractRequestVisitor
{
    public function visit(CommandInterface $command, RequestInterface $request, Parameter $param, $value)
    {
        $value = $param->filter($value);
        if ($value instanceof PostFileInterface) {
            $request->addPostFile($value);
        } else {
            $request->addPostFile($param->getWireName(), $value);
        }
    }
}
vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/AbstractRequestVisitor.php000064400000004376151327705670027742 0ustar00<?php

namespace Guzzle\Service\Command\LocationVisitor\Request;

use Guzzle\Service\Command\CommandInterface;
use Guzzle\Http\Message\RequestInterface;
use Guzzle\Service\Description\Parameter;

abstract class AbstractRequestVisitor implements RequestVisitorInterface
{
    /**
     * @codeCoverageIgnore
     */
    public function after(CommandInterface $command, RequestInterface $request) {}

    /**
     * @codeCoverageIgnore
     */
    public function visit(CommandInterface $command, RequestInterface $request, Parameter $param, $value) {}

    /**
     * Prepare (filter and set desired name for request item) the value for request.
     *
     * @param mixed                                     $value
     * @param \Guzzle\Service\Description\Parameter     $param
     *
     * @return array|mixed
     */
    protected function prepareValue($value, Parameter $param)
    {
        return is_array($value)
            ? $this->resolveRecursively($value, $param)
            : $param->filter($value);
    }

    /**
     * Map nested parameters into the location_key based parameters
     *
     * @param array     $value Value to map
     * @param Parameter $param Parameter that holds information about the current key
     *
     * @return array Returns the mapped array
     */
    protected function resolveRecursively(array $value, Parameter $param)
    {
        foreach ($value as $name => &$v) {
            switch ($param->getType()) {
                case 'object':
                    if ($subParam = $param->getProperty($name)) {
                        $key = $subParam->getWireName();
                        $value[$key] = $this->prepareValue($v, $subParam);
                        if ($name != $key) {
                            unset($value[$name]);
                        }
                    } elseif ($param->getAdditionalProperties() instanceof Parameter) {
                        $v = $this->prepareValue($v, $param->getAdditionalProperties());
                    }
                    break;
                case 'array':
                    if ($items = $param->getItems()) {
                        $v = $this->prepareValue($v, $items);
                    }
                    break;
            }
        }

        return $param->filter($value);
    }
}
vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/BodyVisitor.php000064400000004657151327705670025525 0ustar00<?php

namespace Guzzle\Service\Command\LocationVisitor\Request;

use Guzzle\Http\EntityBody;
use Guzzle\Http\Message\EntityEnclosingRequestInterface;
use Guzzle\Http\Message\RequestInterface;
use Guzzle\Http\EntityBodyInterface;
use Guzzle\Service\Command\CommandInterface;
use Guzzle\Service\Description\Parameter;

/**
 * Visitor used to apply a body to a request
 *
 * This visitor can use a data parameter of 'expect' to control the Expect header. Set the expect data parameter to
 * false to disable the expect header, or set the value to an integer so that the expect 100-continue header is only
 * added if the Content-Length of the entity body is greater than the value.
 */
class BodyVisitor extends AbstractRequestVisitor
{
    public function visit(CommandInterface $command, RequestInterface $request, Parameter $param, $value)
    {
        $value = $param->filter($value);
        $entityBody = EntityBody::factory($value);
        $request->setBody($entityBody);
        $this->addExpectHeader($request, $entityBody, $param->getData('expect_header'));
        // Add the Content-Encoding header if one is set on the EntityBody
        if ($encoding = $entityBody->getContentEncoding()) {
            $request->setHeader('Content-Encoding', $encoding);
        }
    }

    /**
     * Add the appropriate expect header to a request
     *
     * @param EntityEnclosingRequestInterface $request Request to update
     * @param EntityBodyInterface             $body    Entity body of the request
     * @param string|int                      $expect  Expect header setting
     */
    protected function addExpectHeader(EntityEnclosingRequestInterface $request, EntityBodyInterface $body, $expect)
    {
        // Allow the `expect` data parameter to be set to remove the Expect header from the request
        if ($expect === false) {
            $request->removeHeader('Expect');
        } elseif ($expect !== true) {
            // Default to using a MB as the point in which to start using the expect header
            $expect = $expect ?: 1048576;
            // If the expect_header value is numeric then only add if the size is greater than the cutoff
            if (is_numeric($expect) && $body->getSize()) {
                if ($body->getSize() < $expect) {
                    $request->removeHeader('Expect');
                } else {
                    $request->setHeader('Expect', '100-Continue');
                }
            }
        }
    }
}
vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/PostFieldVisitor.php000064400000001015151327705670026502 0ustar00<?php

namespace Guzzle\Service\Command\LocationVisitor\Request;

use Guzzle\Http\Message\RequestInterface;
use Guzzle\Service\Command\CommandInterface;
use Guzzle\Service\Description\Parameter;

/**
 * Visitor used to apply a parameter to a POST field
 */
class PostFieldVisitor extends AbstractRequestVisitor
{
    public function visit(CommandInterface $command, RequestInterface $request, Parameter $param, $value)
    {
        $request->setPostField($param->getWireName(), $this->prepareValue($value, $param));
    }
}
vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/XmlVisitor.php000064400000017643151327705670025367 0ustar00<?php

namespace Guzzle\Service\Command\LocationVisitor\Request;

use Guzzle\Http\Message\RequestInterface;
use Guzzle\Service\Command\CommandInterface;
use Guzzle\Service\Description\Operation;
use Guzzle\Service\Description\Parameter;

/**
 * Location visitor used to serialize XML bodies
 */
class XmlVisitor extends AbstractRequestVisitor
{
    /** @var \SplObjectStorage Data object for persisting XML data */
    protected $data;

    /** @var bool Content-Type header added when XML is found */
    protected $contentType = 'application/xml';

    public function __construct()
    {
        $this->data = new \SplObjectStorage();
    }

    /**
     * Change the content-type header that is added when XML is found
     *
     * @param string $header Header to set when XML is found
     *
     * @return self
     */
    public function setContentTypeHeader($header)
    {
        $this->contentType = $header;

        return $this;
    }

    public function visit(CommandInterface $command, RequestInterface $request, Parameter $param, $value)
    {
        $xml = isset($this->data[$command])
            ? $this->data[$command]
            : $this->createRootElement($param->getParent());
        $this->addXml($xml, $param, $value);

        $this->data[$command] = $xml;
    }

    public function after(CommandInterface $command, RequestInterface $request)
    {
        $xml = null;

        // If data was found that needs to be serialized, then do so
        if (isset($this->data[$command])) {
            $xml = $this->finishDocument($this->data[$command]);
            unset($this->data[$command]);
        } else {
            // Check if XML should always be sent for the command
            $operation = $command->getOperation();
            if ($operation->getData('xmlAllowEmpty')) {
                $xmlWriter = $this->createRootElement($operation);
                $xml = $this->finishDocument($xmlWriter);
            }
        }

        if ($xml) {
            // Don't overwrite the Content-Type if one is set
            if ($this->contentType && !$request->hasHeader('Content-Type')) {
                $request->setHeader('Content-Type', $this->contentType);
            }
            $request->setBody($xml);
        }
    }

    /**
     * Create the root XML element to use with a request
     *
     * @param Operation $operation Operation object
     *
     * @return \XMLWriter
     */
    protected function createRootElement(Operation $operation)
    {
        static $defaultRoot = array('name' => 'Request');
        // If no root element was specified, then just wrap the XML in 'Request'
        $root = $operation->getData('xmlRoot') ?: $defaultRoot;
        // Allow the XML declaration to be customized with xmlEncoding
        $encoding = $operation->getData('xmlEncoding');

        $xmlWriter = $this->startDocument($encoding);

        $xmlWriter->startElement($root['name']);
        // Create the wrapping element with no namespaces if no namespaces were present
        if (!empty($root['namespaces'])) {
            // Create the wrapping element with an array of one or more namespaces
            foreach ((array) $root['namespaces'] as $prefix => $uri) {
                $nsLabel = 'xmlns';
                if (!is_numeric($prefix)) {
                    $nsLabel .= ':'.$prefix;
                }
                $xmlWriter->writeAttribute($nsLabel, $uri);
            }
        }
        return $xmlWriter;
    }

    /**
     * Recursively build the XML body
     *
     * @param \XMLWriter $xmlWriter XML to modify
     * @param Parameter  $param     API Parameter
     * @param mixed      $value     Value to add
     */
    protected function addXml(\XMLWriter $xmlWriter, Parameter $param, $value)
    {
        if ($value === null) {
            return;
        }

        $value = $param->filter($value);
        $type = $param->getType();
        $name = $param->getWireName();
        $prefix = null;
        $namespace = $param->getData('xmlNamespace');
        if (false !== strpos($name, ':')) {
            list($prefix, $name) = explode(':', $name, 2);
        }

        if ($type == 'object' || $type == 'array') {
            if (!$param->getData('xmlFlattened')) {
                $xmlWriter->startElementNS(null, $name, $namespace);
            }
            if ($param->getType() == 'array') {
                $this->addXmlArray($xmlWriter, $param, $value);
            } elseif ($param->getType() == 'object') {
                $this->addXmlObject($xmlWriter, $param, $value);
            }
            if (!$param->getData('xmlFlattened')) {
                $xmlWriter->endElement();
            }
            return;
        }
        if ($param->getData('xmlAttribute')) {
            $this->writeAttribute($xmlWriter, $prefix, $name, $namespace, $value);
        } else {
            $this->writeElement($xmlWriter, $prefix, $name, $namespace, $value);
        }
    }

    /**
     * Write an attribute with namespace if used
     *
     * @param  \XMLWriter $xmlWriter XMLWriter instance
     * @param  string     $prefix    Namespace prefix if any
     * @param  string     $name      Attribute name
     * @param  string     $namespace The uri of the namespace
     * @param  string     $value     The attribute content
     */
    protected function writeAttribute($xmlWriter, $prefix, $name, $namespace, $value)
    {
        if (empty($namespace)) {
            $xmlWriter->writeAttribute($name, $value);
        } else {
            $xmlWriter->writeAttributeNS($prefix, $name, $namespace, $value);
        }
    }

    /**
     * Write an element with namespace if used
     *
     * @param  \XMLWriter $xmlWriter XML writer resource
     * @param  string     $prefix    Namespace prefix if any
     * @param  string     $name      Element name
     * @param  string     $namespace The uri of the namespace
     * @param  string     $value     The element content
     */
    protected function writeElement(\XMLWriter $xmlWriter, $prefix, $name, $namespace, $value)
    {
        $xmlWriter->startElementNS($prefix, $name, $namespace);
        if (strpbrk($value, '<>&')) {
            $xmlWriter->writeCData($value);
        } else {
            $xmlWriter->writeRaw($value);
        }
        $xmlWriter->endElement();
    }

    /**
     * Create a new xml writer and start a document
     *
     * @param  string $encoding document encoding
     *
     * @return \XMLWriter the writer resource
     */
    protected function startDocument($encoding)
    {
        $xmlWriter = new \XMLWriter();
        $xmlWriter->openMemory();
        $xmlWriter->startDocument('1.0', $encoding);

        return $xmlWriter;
    }

    /**
     * End the document and return the output
     *
     * @param \XMLWriter $xmlWriter
     *
     * @return \string the writer resource
     */
    protected function finishDocument($xmlWriter)
    {
        $xmlWriter->endDocument();

        return $xmlWriter->outputMemory();
    }

    /**
     * Add an array to the XML
     */
    protected function addXmlArray(\XMLWriter $xmlWriter, Parameter $param, &$value)
    {
        if ($items = $param->getItems()) {
            foreach ($value as $v) {
                $this->addXml($xmlWriter, $items, $v);
            }
        }
    }

    /**
     * Add an object to the XML
     */
    protected function addXmlObject(\XMLWriter $xmlWriter, Parameter $param, &$value)
    {
        $noAttributes = array();
        // add values which have attributes
        foreach ($value as $name => $v) {
            if ($property = $param->getProperty($name)) {
                if ($property->getData('xmlAttribute')) {
                    $this->addXml($xmlWriter, $property, $v);
                } else {
                    $noAttributes[] = array('value' => $v, 'property' => $property);
                }
            }
        }
        // now add values with no attributes
        foreach ($noAttributes as $element) {
            $this->addXml($xmlWriter, $element['property'], $element['value']);
        }
    }
}
vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/QueryVisitor.php000064400000001030151327705670025713 0ustar00<?php

namespace Guzzle\Service\Command\LocationVisitor\Request;

use Guzzle\Http\Message\RequestInterface;
use Guzzle\Service\Command\CommandInterface;
use Guzzle\Service\Description\Parameter;

/**
 * Visitor used to apply a parameter to a request's query string
 */
class QueryVisitor extends AbstractRequestVisitor
{
    public function visit(CommandInterface $command, RequestInterface $request, Parameter $param, $value)
    {
        $request->getQuery()->set($param->getWireName(), $this->prepareValue($value, $param));
    }
}
vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/ResponseBodyVisitor.php000064400000000763151327705670027236 0ustar00<?php

namespace Guzzle\Service\Command\LocationVisitor\Request;

use Guzzle\Http\Message\RequestInterface;
use Guzzle\Service\Command\CommandInterface;
use Guzzle\Service\Description\Parameter;

/**
 * Visitor used to change the location in which a response body is saved
 */
class ResponseBodyVisitor extends AbstractRequestVisitor
{
    public function visit(CommandInterface $command, RequestInterface $request, Parameter $param, $value)
    {
        $request->setResponseBody($value);
    }
}
vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/HeaderVisitor.php000064400000002771151327705670026013 0ustar00<?php

namespace Guzzle\Service\Command\LocationVisitor\Request;

use Guzzle\Common\Exception\InvalidArgumentException;
use Guzzle\Http\Message\RequestInterface;
use Guzzle\Service\Command\CommandInterface;
use Guzzle\Service\Description\Parameter;

/**
 * Visitor used to apply a parameter to a header value
 */
class HeaderVisitor extends AbstractRequestVisitor
{
    public function visit(CommandInterface $command, RequestInterface $request, Parameter $param, $value)
    {
        $value = $param->filter($value);
        if ($param->getType() == 'object' && $param->getAdditionalProperties() instanceof Parameter) {
            $this->addPrefixedHeaders($request, $param, $value);
        } else {
            $request->setHeader($param->getWireName(), $value);
        }
    }

    /**
     * Add a prefixed array of headers to the request
     *
     * @param RequestInterface $request Request to update
     * @param Parameter        $param   Parameter object
     * @param array            $value   Header array to add
     *
     * @throws InvalidArgumentException
     */
    protected function addPrefixedHeaders(RequestInterface $request, Parameter $param, $value)
    {
        if (!is_array($value)) {
            throw new InvalidArgumentException('An array of mapped headers expected, but received a single value');
        }
        $prefix = $param->getSentAs();
        foreach ($value as $headerName => $headerValue) {
            $request->setHeader($prefix . $headerName, $headerValue);
        }
    }
}
vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/JsonVisitor.php000064400000006672151327705670025706 0ustar00<?php

namespace Guzzle\Service\Command\LocationVisitor\Response;

use Guzzle\Http\Message\Response;
use Guzzle\Service\Description\Parameter;
use Guzzle\Service\Command\CommandInterface;

/**
 * Location visitor used to marshal JSON response data into a formatted array.
 *
 * Allows top level JSON parameters to be inserted into the result of a command. The top level attributes are grabbed
 * from the response's JSON data using the name value by default. Filters can be applied to parameters as they are
 * traversed. This allows data to be normalized before returning it to users (for example converting timestamps to
 * DateTime objects).
 */
class JsonVisitor extends AbstractResponseVisitor
{
    public function before(CommandInterface $command, array &$result)
    {
        // Ensure that the result of the command is always rooted with the parsed JSON data
        $result = $command->getResponse()->json();
    }

    public function visit(
        CommandInterface $command,
        Response $response,
        Parameter $param,
        &$value,
        $context =  null
    ) {
        $name = $param->getName();
        $key = $param->getWireName();
        if (isset($value[$key])) {
            $this->recursiveProcess($param, $value[$key]);
            if ($key != $name) {
                $value[$name] = $value[$key];
                unset($value[$key]);
            }
        }
    }

    /**
     * Recursively process a parameter while applying filters
     *
     * @param Parameter $param API parameter being validated
     * @param mixed     $value Value to validate and process. The value may change during this process.
     */
    protected function recursiveProcess(Parameter $param, &$value)
    {
        if ($value === null) {
            return;
        }

        if (is_array($value)) {
            $type = $param->getType();
            if ($type == 'array') {
                foreach ($value as &$item) {
                    $this->recursiveProcess($param->getItems(), $item);
                }
            } elseif ($type == 'object' && !isset($value[0])) {
                // On the above line, we ensure that the array is associative and not numerically indexed
                $knownProperties = array();
                if ($properties = $param->getProperties()) {
                    foreach ($properties as $property) {
                        $name = $property->getName();
                        $key = $property->getWireName();
                        $knownProperties[$name] = 1;
                        if (isset($value[$key])) {
                            $this->recursiveProcess($property, $value[$key]);
                            if ($key != $name) {
                                $value[$name] = $value[$key];
                                unset($value[$key]);
                            }
                        }
                    }
                }

                // Remove any unknown and potentially unsafe properties
                if ($param->getAdditionalProperties() === false) {
                    $value = array_intersect_key($value, $knownProperties);
                } elseif (($additional = $param->getAdditionalProperties()) !== true) {
                    // Validate and filter additional properties
                    foreach ($value as &$v) {
                        $this->recursiveProcess($additional, $v);
                    }
                }
            }
        }

        $value = $param->filter($value);
    }
}
vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/HeaderVisitor.php000064400000003426151327705670026157 0ustar00<?php

namespace Guzzle\Service\Command\LocationVisitor\Response;

use Guzzle\Http\Message\Response;
use Guzzle\Service\Description\Parameter;
use Guzzle\Service\Command\CommandInterface;

/**
 * Location visitor used to add a particular header of a response to a key in the result
 */
class HeaderVisitor extends AbstractResponseVisitor
{
    public function visit(
        CommandInterface $command,
        Response $response,
        Parameter $param,
        &$value,
        $context =  null
    ) {
        if ($param->getType() == 'object' && $param->getAdditionalProperties() instanceof Parameter) {
            $this->processPrefixedHeaders($response, $param, $value);
        } else {
            $value[$param->getName()] = $param->filter((string) $response->getHeader($param->getWireName()));
        }
    }

    /**
     * Process a prefixed header array
     *
     * @param Response  $response Response that contains the headers
     * @param Parameter $param    Parameter object
     * @param array     $value    Value response array to modify
     */
    protected function processPrefixedHeaders(Response $response, Parameter $param, &$value)
    {
        // Grab prefixed headers that should be placed into an array with the prefix stripped
        if ($prefix = $param->getSentAs()) {
            $container = $param->getName();
            $len = strlen($prefix);
            // Find all matching headers and place them into the containing element
            foreach ($response->getHeaders()->toArray() as $key => $header) {
                if (stripos($key, $prefix) === 0) {
                    // Account for multi-value headers
                    $value[$container][substr($key, $len)] = count($header) == 1 ? end($header) : $header;
                }
            }
        }
    }
}
vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/AbstractResponseVisitor.php000064400000001146151327705670030246 0ustar00<?php

namespace Guzzle\Service\Command\LocationVisitor\Response;

use Guzzle\Service\Command\CommandInterface;
use Guzzle\Http\Message\Response;
use Guzzle\Service\Description\Parameter;

/**
 * {@inheritdoc}
 * @codeCoverageIgnore
 */
abstract class AbstractResponseVisitor implements ResponseVisitorInterface
{
    public function before(CommandInterface $command, array &$result) {}

    public function after(CommandInterface $command) {}

    public function visit(
        CommandInterface $command,
        Response $response,
        Parameter $param,
        &$value,
        $context =  null
    ) {}
}
vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/BodyVisitor.php000064400000001063151327705670025657 0ustar00<?php

namespace Guzzle\Service\Command\LocationVisitor\Response;

use Guzzle\Service\Command\CommandInterface;
use Guzzle\Http\Message\Response;
use Guzzle\Service\Description\Parameter;

/**
 * Visitor used to add the body of a response to a particular key
 */
class BodyVisitor extends AbstractResponseVisitor
{
    public function visit(
        CommandInterface $command,
        Response $response,
        Parameter $param,
        &$value,
        $context =  null
    ) {
        $value[$param->getName()] = $param->filter($response->getBody());
    }
}
vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/StatusCodeVisitor.php000064400000001102151327705670027032 0ustar00<?php

namespace Guzzle\Service\Command\LocationVisitor\Response;

use Guzzle\Http\Message\Response;
use Guzzle\Service\Description\Parameter;
use Guzzle\Service\Command\CommandInterface;

/**
 * Location visitor used to add the status code of a response to a key in the result
 */
class StatusCodeVisitor extends AbstractResponseVisitor
{
    public function visit(
        CommandInterface $command,
        Response $response,
        Parameter $param,
        &$value,
        $context =  null
    ) {
        $value[$param->getName()] = $response->getStatusCode();
    }
}
guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/ResponseVisitorInterface.php000064400000003062151327705670030323 0ustar00vendor<?php

namespace Guzzle\Service\Command\LocationVisitor\Response;

use Guzzle\Http\Message\Response;
use Guzzle\Service\Description\Parameter;
use Guzzle\Service\Command\CommandInterface;

/**
 * Location visitor used to parse values out of a response into an associative array
 */
interface ResponseVisitorInterface
{
    /**
     * Called before visiting all parameters. This can be used for seeding the result of a command with default
     * data (e.g. populating with JSON data in the response then adding to the parsed data).
     *
     * @param CommandInterface $command Command being visited
     * @param array            $result  Result value to update if needed (e.g. parsing XML or JSON)
     */
    public function before(CommandInterface $command, array &$result);

    /**
     * Called after visiting all parameters
     *
     * @param CommandInterface $command Command being visited
     */
    public function after(CommandInterface $command);

    /**
     * Called once for each parameter being visited that matches the location type
     *
     * @param CommandInterface $command  Command being visited
     * @param Response         $response Response being visited
     * @param Parameter        $param    Parameter being visited
     * @param mixed            $value    Result associative array value being updated by reference
     * @param mixed            $context  Parsing context
     */
    public function visit(
        CommandInterface $command,
        Response $response,
        Parameter $param,
        &$value,
        $context =  null
    );
}
vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/XmlVisitor.php000064400000012720151327705670025524 0ustar00<?php

namespace Guzzle\Service\Command\LocationVisitor\Response;

use Guzzle\Http\Message\Response;
use Guzzle\Service\Description\Parameter;
use Guzzle\Service\Command\CommandInterface;

/**
 * Location visitor used to marshal XML response data into a formatted array
 */
class XmlVisitor extends AbstractResponseVisitor
{
    public function before(CommandInterface $command, array &$result)
    {
        // Set the result of the command to the array conversion of the XML body
        $result = json_decode(json_encode($command->getResponse()->xml()), true);
    }

    public function visit(
        CommandInterface $command,
        Response $response,
        Parameter $param,
        &$value,
        $context =  null
    ) {
        $sentAs = $param->getWireName();
        $name = $param->getName();
        if (isset($value[$sentAs])) {
            $this->recursiveProcess($param, $value[$sentAs]);
            if ($name != $sentAs) {
                $value[$name] = $value[$sentAs];
                unset($value[$sentAs]);
            }
        }
    }

    /**
     * Recursively process a parameter while applying filters
     *
     * @param Parameter $param API parameter being processed
     * @param mixed     $value Value to validate and process. The value may change during this process.
     */
    protected function recursiveProcess(Parameter $param, &$value)
    {
        $type = $param->getType();

        if (!is_array($value)) {
            if ($type == 'array') {
                // Cast to an array if the value was a string, but should be an array
                $this->recursiveProcess($param->getItems(), $value);
                $value = array($value);
            }
        } elseif ($type == 'object') {
            $this->processObject($param, $value);
        } elseif ($type == 'array') {
            $this->processArray($param, $value);
        } elseif ($type == 'string' && gettype($value) == 'array') {
            $value = '';
        }

        if ($value !== null) {
            $value = $param->filter($value);
        }
    }

    /**
     * Process an array
     *
     * @param Parameter $param API parameter being parsed
     * @param mixed     $value Value to process
     */
    protected function processArray(Parameter $param, &$value)
    {
        // Convert the node if it was meant to be an array
        if (!isset($value[0])) {
            // Collections fo nodes are sometimes wrapped in an additional array. For example:
            // <Items><Item><a>1</a></Item><Item><a>2</a></Item></Items> should become:
            // array('Items' => array(array('a' => 1), array('a' => 2))
            // Some nodes are not wrapped. For example: <Foo><a>1</a></Foo><Foo><a>2</a></Foo>
            // should become array('Foo' => array(array('a' => 1), array('a' => 2))
            if ($param->getItems() && isset($value[$param->getItems()->getWireName()])) {
                // Account for the case of a collection wrapping wrapped nodes: Items => Item[]
                $value = $value[$param->getItems()->getWireName()];
                // If the wrapped node only had one value, then make it an array of nodes
                if (!isset($value[0]) || !is_array($value)) {
                    $value = array($value);
                }
            } elseif (!empty($value)) {
                // Account for repeated nodes that must be an array: Foo => Baz, Foo => Baz, but only if the
                // value is set and not empty
                $value = array($value);
            }
        }

        foreach ($value as &$item) {
            $this->recursiveProcess($param->getItems(), $item);
        }
    }

    /**
     * Process an object
     *
     * @param Parameter $param API parameter being parsed
     * @param mixed     $value Value to process
     */
    protected function processObject(Parameter $param, &$value)
    {
        // Ensure that the array is associative and not numerically indexed
        if (!isset($value[0]) && ($properties = $param->getProperties())) {
            $knownProperties = array();
            foreach ($properties as $property) {
                $name = $property->getName();
                $sentAs = $property->getWireName();
                $knownProperties[$name] = 1;
                if ($property->getData('xmlAttribute')) {
                    $this->processXmlAttribute($property, $value);
                } elseif (isset($value[$sentAs])) {
                    $this->recursiveProcess($property, $value[$sentAs]);
                    if ($name != $sentAs) {
                        $value[$name] = $value[$sentAs];
                        unset($value[$sentAs]);
                    }
                }
            }

            // Remove any unknown and potentially unsafe properties
            if ($param->getAdditionalProperties() === false) {
                $value = array_intersect_key($value, $knownProperties);
            }
        }
    }

    /**
     * Process an XML attribute property
     *
     * @param Parameter $property Property to process
     * @param array     $value    Value to process and update
     */
    protected function processXmlAttribute(Parameter $property, array &$value)
    {
        $sentAs = $property->getWireName();
        if (isset($value['@attributes'][$sentAs])) {
            $value[$property->getName()] = $value['@attributes'][$sentAs];
            unset($value['@attributes'][$sentAs]);
            if (empty($value['@attributes'])) {
                unset($value['@attributes']);
            }
        }
    }
}
vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/ReasonPhraseVisitor.php000064400000001110151327705670027345 0ustar00<?php

namespace Guzzle\Service\Command\LocationVisitor\Response;

use Guzzle\Http\Message\Response;
use Guzzle\Service\Description\Parameter;
use Guzzle\Service\Command\CommandInterface;

/**
 * Location visitor used to add the reason phrase of a response to a key in the result
 */
class ReasonPhraseVisitor extends AbstractResponseVisitor
{
    public function visit(
        CommandInterface $command,
        Response $response,
        Parameter $param,
        &$value,
        $context =  null
    ) {
        $value[$param->getName()] = $response->getReasonPhrase();
    }
}
vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/VisitorFlyweight.php000064400000011323151327705670025126 0ustar00<?php

namespace Guzzle\Service\Command\LocationVisitor;

use Guzzle\Common\Exception\InvalidArgumentException;
use Guzzle\Service\Command\LocationVisitor\Request\RequestVisitorInterface;
use Guzzle\Service\Command\LocationVisitor\Response\ResponseVisitorInterface;

/**
 * Flyweight factory used to instantiate request and response visitors
 */
class VisitorFlyweight
{
    /** @var self Singleton instance of self */
    protected static $instance;

    /** @var array Default array of mappings of location names to classes */
    protected static $defaultMappings = array(
        'request.body'          => 'Guzzle\Service\Command\LocationVisitor\Request\BodyVisitor',
        'request.header'        => 'Guzzle\Service\Command\LocationVisitor\Request\HeaderVisitor',
        'request.json'          => 'Guzzle\Service\Command\LocationVisitor\Request\JsonVisitor',
        'request.postField'     => 'Guzzle\Service\Command\LocationVisitor\Request\PostFieldVisitor',
        'request.postFile'      => 'Guzzle\Service\Command\LocationVisitor\Request\PostFileVisitor',
        'request.query'         => 'Guzzle\Service\Command\LocationVisitor\Request\QueryVisitor',
        'request.response_body' => 'Guzzle\Service\Command\LocationVisitor\Request\ResponseBodyVisitor',
        'request.responseBody'  => 'Guzzle\Service\Command\LocationVisitor\Request\ResponseBodyVisitor',
        'request.xml'           => 'Guzzle\Service\Command\LocationVisitor\Request\XmlVisitor',
        'response.body'         => 'Guzzle\Service\Command\LocationVisitor\Response\BodyVisitor',
        'response.header'       => 'Guzzle\Service\Command\LocationVisitor\Response\HeaderVisitor',
        'response.json'         => 'Guzzle\Service\Command\LocationVisitor\Response\JsonVisitor',
        'response.reasonPhrase' => 'Guzzle\Service\Command\LocationVisitor\Response\ReasonPhraseVisitor',
        'response.statusCode'   => 'Guzzle\Service\Command\LocationVisitor\Response\StatusCodeVisitor',
        'response.xml'          => 'Guzzle\Service\Command\LocationVisitor\Response\XmlVisitor'
    );

    /** @var array Array of mappings of location names to classes */
    protected $mappings;

    /** @var array Cache of instantiated visitors */
    protected $cache = array();

    /**
     * @return self
     * @codeCoverageIgnore
     */
    public static function getInstance()
    {
        if (!self::$instance) {
            self::$instance = new self();
        }

        return self::$instance;
    }

    /**
     * @param array $mappings Array mapping request.name and response.name to location visitor classes. Leave null to
     *                        use the default values.
     */
    public function __construct(array $mappings = null)
    {
        $this->mappings = $mappings === null ? self::$defaultMappings : $mappings;
    }

    /**
     * Get an instance of a request visitor by location name
     *
     * @param string $visitor Visitor name
     *
     * @return RequestVisitorInterface
     */
    public function getRequestVisitor($visitor)
    {
        return $this->getKey('request.' . $visitor);
    }

    /**
     * Get an instance of a response visitor by location name
     *
     * @param string $visitor Visitor name
     *
     * @return ResponseVisitorInterface
     */
    public function getResponseVisitor($visitor)
    {
        return $this->getKey('response.' . $visitor);
    }

    /**
     * Add a response visitor to the factory by name
     *
     * @param string                  $name    Name of the visitor
     * @param RequestVisitorInterface $visitor Visitor to add
     *
     * @return self
     */
    public function addRequestVisitor($name, RequestVisitorInterface $visitor)
    {
        $this->cache['request.' . $name] = $visitor;

        return $this;
    }

    /**
     * Add a response visitor to the factory by name
     *
     * @param string                   $name    Name of the visitor
     * @param ResponseVisitorInterface $visitor Visitor to add
     *
     * @return self
     */
    public function addResponseVisitor($name, ResponseVisitorInterface $visitor)
    {
        $this->cache['response.' . $name] = $visitor;

        return $this;
    }

    /**
     * Get a visitor by key value name
     *
     * @param string $key Key name to retrieve
     *
     * @return mixed
     * @throws InvalidArgumentException
     */
    private function getKey($key)
    {
        if (!isset($this->cache[$key])) {
            if (!isset($this->mappings[$key])) {
                list($type, $name) = explode('.', $key);
                throw new InvalidArgumentException("No {$type} visitor has been mapped for {$name}");
            }
            $this->cache[$key] = new $this->mappings[$key];
        }

        return $this->cache[$key];
    }
}
vendor/guzzle/guzzle/src/Guzzle/Service/Command/OperationResponseParser.php000064400000016321151327705670023313 0ustar00<?php

namespace Guzzle\Service\Command;

use Guzzle\Http\Message\Response;
use Guzzle\Service\Command\LocationVisitor\VisitorFlyweight;
use Guzzle\Service\Command\LocationVisitor\Response\ResponseVisitorInterface;
use Guzzle\Service\Description\Parameter;
use Guzzle\Service\Description\OperationInterface;
use Guzzle\Service\Description\Operation;
use Guzzle\Service\Exception\ResponseClassException;
use Guzzle\Service\Resource\Model;

/**
 * Response parser that attempts to marshal responses into an associative array based on models in a service description
 */
class OperationResponseParser extends DefaultResponseParser
{
    /** @var VisitorFlyweight $factory Visitor factory */
    protected $factory;

    /** @var self */
    protected static $instance;

    /** @var bool */
    private $schemaInModels;

    /**
     * @return self
     * @codeCoverageIgnore
     */
    public static function getInstance()
    {
        if (!static::$instance) {
            static::$instance = new static(VisitorFlyweight::getInstance());
        }

        return static::$instance;
    }

    /**
     * @param VisitorFlyweight $factory        Factory to use when creating visitors
     * @param bool             $schemaInModels Set to true to inject schemas into models
     */
    public function __construct(VisitorFlyweight $factory, $schemaInModels = false)
    {
        $this->factory = $factory;
        $this->schemaInModels = $schemaInModels;
    }

    /**
     * Add a location visitor to the command
     *
     * @param string                   $location Location to associate with the visitor
     * @param ResponseVisitorInterface $visitor  Visitor to attach
     *
     * @return self
     */
    public function addVisitor($location, ResponseVisitorInterface $visitor)
    {
        $this->factory->addResponseVisitor($location, $visitor);

        return $this;
    }

    protected function handleParsing(CommandInterface $command, Response $response, $contentType)
    {
        $operation = $command->getOperation();
        $type = $operation->getResponseType();
        $model = null;

        if ($type == OperationInterface::TYPE_MODEL) {
            $model = $operation->getServiceDescription()->getModel($operation->getResponseClass());
        } elseif ($type == OperationInterface::TYPE_CLASS) {
            return $this->parseClass($command);
        }

        if (!$model) {
            // Return basic processing if the responseType is not model or the model cannot be found
            return parent::handleParsing($command, $response, $contentType);
        } elseif ($command[AbstractCommand::RESPONSE_PROCESSING] != AbstractCommand::TYPE_MODEL) {
            // Returns a model with no visiting if the command response processing is not model
            return new Model(parent::handleParsing($command, $response, $contentType));
        } else {
            // Only inject the schema into the model if "schemaInModel" is true
            return new Model($this->visitResult($model, $command, $response), $this->schemaInModels ? $model : null);
        }
    }

    /**
     * Parse a class object
     *
     * @param CommandInterface $command Command to parse into an object
     *
     * @return mixed
     * @throws ResponseClassException
     */
    protected function parseClass(CommandInterface $command)
    {
        // Emit the operation.parse_class event. If a listener injects a 'result' property, then that will be the result
        $event = new CreateResponseClassEvent(array('command' => $command));
        $command->getClient()->getEventDispatcher()->dispatch('command.parse_response', $event);
        if ($result = $event->getResult()) {
            return $result;
        }

        $className = $command->getOperation()->getResponseClass();
        if (!method_exists($className, 'fromCommand')) {
            throw new ResponseClassException("{$className} must exist and implement a static fromCommand() method");
        }

        return $className::fromCommand($command);
    }

    /**
     * Perform transformations on the result array
     *
     * @param Parameter        $model    Model that defines the structure
     * @param CommandInterface $command  Command that performed the operation
     * @param Response         $response Response received
     *
     * @return array Returns the array of result data
     */
    protected function visitResult(Parameter $model, CommandInterface $command, Response $response)
    {
        $foundVisitors = $result = $knownProps = array();
        $props = $model->getProperties();

        foreach ($props as $schema) {
            if ($location = $schema->getLocation()) {
                // Trigger the before method on the first found visitor of this type
                if (!isset($foundVisitors[$location])) {
                    $foundVisitors[$location] = $this->factory->getResponseVisitor($location);
                    $foundVisitors[$location]->before($command, $result);
                }
            }
        }

        // Visit additional properties when it is an actual schema
        if (($additional = $model->getAdditionalProperties()) instanceof Parameter) {
            $this->visitAdditionalProperties($model, $command, $response, $additional, $result, $foundVisitors);
        }

        // Apply the parameter value with the location visitor
        foreach ($props as $schema) {
            $knownProps[$schema->getName()] = 1;
            if ($location = $schema->getLocation()) {
                $foundVisitors[$location]->visit($command, $response, $schema, $result);
            }
        }

        // Remove any unknown and potentially unsafe top-level properties
        if ($additional === false) {
            $result = array_intersect_key($result, $knownProps);
        }

        // Call the after() method of each found visitor
        foreach ($foundVisitors as $visitor) {
            $visitor->after($command);
        }

        return $result;
    }

    protected function visitAdditionalProperties(
        Parameter $model,
        CommandInterface $command,
        Response $response,
        Parameter $additional,
        &$result,
        array &$foundVisitors
    ) {
        // Only visit when a location is specified
        if ($location = $additional->getLocation()) {
            if (!isset($foundVisitors[$location])) {
                $foundVisitors[$location] = $this->factory->getResponseVisitor($location);
                $foundVisitors[$location]->before($command, $result);
            }
            // Only traverse if an array was parsed from the before() visitors
            if (is_array($result)) {
                // Find each additional property
                foreach (array_keys($result) as $key) {
                    // Check if the model actually knows this property. If so, then it is not additional
                    if (!$model->getProperty($key)) {
                        // Set the name to the key so that we can parse it with each visitor
                        $additional->setName($key);
                        $foundVisitors[$location]->visit($command, $response, $additional, $result);
                    }
                }
                // Reset the additionalProperties name to null
                $additional->setName(null);
            }
        }
    }
}
vendor/guzzle/guzzle/src/Guzzle/Service/Command/CommandInterface.php000064400000006705151327705670021663 0ustar00<?php

namespace Guzzle\Service\Command;

use Guzzle\Common\Collection;
use Guzzle\Common\Exception\InvalidArgumentException;
use Guzzle\Http\Message\Response;
use Guzzle\Http\Message\RequestInterface;
use Guzzle\Service\Exception\CommandException;
use Guzzle\Service\Description\OperationInterface;
use Guzzle\Service\ClientInterface;
use Guzzle\Common\ToArrayInterface;

/**
 * A command object that contains parameters that can be modified and accessed like an array and turned into an array
 */
interface CommandInterface extends \ArrayAccess, ToArrayInterface
{
    /**
     * Get the short form name of the command
     *
     * @return string
     */
    public function getName();

    /**
     * Get the API operation information about the command
     *
     * @return OperationInterface
     */
    public function getOperation();

    /**
     * Execute the command and return the result
     *
     * @return mixed Returns the result of {@see CommandInterface::execute}
     * @throws CommandException if a client has not been associated with the command
     */
    public function execute();

    /**
     * Get the client object that will execute the command
     *
     * @return ClientInterface|null
     */
    public function getClient();

    /**
     * Set the client object that will execute the command
     *
     * @param ClientInterface $client The client object that will execute the command
     *
     * @return self
     */
    public function setClient(ClientInterface $client);

    /**
     * Get the request object associated with the command
     *
     * @return RequestInterface
     * @throws CommandException if the command has not been executed
     */
    public function getRequest();

    /**
     * Get the response object associated with the command
     *
     * @return Response
     * @throws CommandException if the command has not been executed
     */
    public function getResponse();

    /**
     * Get the result of the command
     *
     * @return Response By default, commands return a Response object unless overridden in a subclass
     * @throws CommandException if the command has not been executed
     */
    public function getResult();

    /**
     * Set the result of the command
     *
     * @param mixed $result Result to set
     *
     * @return self
     */
    public function setResult($result);

    /**
     * Returns TRUE if the command has been prepared for executing
     *
     * @return bool
     */
    public function isPrepared();

    /**
     * Returns TRUE if the command has been executed
     *
     * @return bool
     */
    public function isExecuted();

    /**
     * Prepare the command for executing and create a request object.
     *
     * @return RequestInterface Returns the generated request
     * @throws CommandException if a client object has not been set previously or in the prepare()
     */
    public function prepare();

    /**
     * Get the object that manages the request headers that will be set on any outbound requests from the command
     *
     * @return Collection
     */
    public function getRequestHeaders();

    /**
     * Specify a callable to execute when the command completes
     *
     * @param mixed $callable Callable to execute when the command completes. The callable must accept a
     *                        {@see CommandInterface} object as the only argument.
     * @return self
     * @throws InvalidArgumentException
     */
    public function setOnComplete($callable);
}
vendor/guzzle/guzzle/src/Guzzle/Service/Command/ResponseParserInterface.php000064400000000753151327705670023255 0ustar00<?php

namespace Guzzle\Service\Command;

/**
 * Parses the HTTP response of a command and sets the appropriate result on a command object
 */
interface ResponseParserInterface
{
    /**
     * Parse the HTTP response received by the command and update the command's result contents
     *
     * @param CommandInterface $command Command to parse and update
     *
     * @return mixed Returns the result to set on the command
     */
    public function parse(CommandInterface $command);
}
vendor/guzzle/guzzle/src/Guzzle/Service/Command/ResponseClassInterface.php000064400000000673151327705700023061 0ustar00<?php

namespace Guzzle\Service\Command;

/**
 * Interface used to accept a completed OperationCommand and parse the result into a specific response type
 */
interface ResponseClassInterface
{
    /**
     * Create a response model object from a completed command
     *
     * @param OperationCommand $command That serialized the request
     *
     * @return self
     */
    public static function fromCommand(OperationCommand $command);
}
vendor/guzzle/guzzle/src/Guzzle/Service/Command/Factory/AliasFactory.php000064400000002140151327705700022433 0ustar00<?php

namespace Guzzle\Service\Command\Factory;

use Guzzle\Common\Exception\InvalidArgumentException;
use Guzzle\Service\ClientInterface;

/**
 * Command factory used when you need to provide aliases to commands
 */
class AliasFactory implements FactoryInterface
{
    /** @var array Associative array mapping command aliases to the aliased command */
    protected $aliases;

    /** @var ClientInterface Client used to retry using aliases */
    protected $client;

    /**
     * @param ClientInterface $client  Client used to retry with the alias
     * @param array           $aliases Associative array mapping aliases to the alias
     */
    public function __construct(ClientInterface $client, array $aliases)
    {
        $this->client = $client;
        $this->aliases = $aliases;
    }

    public function factory($name, array $args = array())
    {
        if (isset($this->aliases[$name])) {
            try {
                return $this->client->getCommand($this->aliases[$name], $args);
            } catch (InvalidArgumentException $e) {
                return null;
            }
        }
    }
}
vendor/guzzle/guzzle/src/Guzzle/Service/Command/Factory/ServiceDescriptionFactory.php000064400000004155151327705700025216 0ustar00<?php

namespace Guzzle\Service\Command\Factory;

use Guzzle\Service\Description\ServiceDescriptionInterface;
use Guzzle\Inflection\InflectorInterface;

/**
 * Command factory used to create commands based on service descriptions
 */
class ServiceDescriptionFactory implements FactoryInterface
{
    /** @var ServiceDescriptionInterface */
    protected $description;

    /** @var InflectorInterface */
    protected $inflector;

    /**
     * @param ServiceDescriptionInterface $description Service description
     * @param InflectorInterface          $inflector   Optional inflector to use if the command is not at first found
     */
    public function __construct(ServiceDescriptionInterface $description, InflectorInterface $inflector = null)
    {
        $this->setServiceDescription($description);
        $this->inflector = $inflector;
    }

    /**
     * Change the service description used with the factory
     *
     * @param ServiceDescriptionInterface $description Service description to use
     *
     * @return FactoryInterface
     */
    public function setServiceDescription(ServiceDescriptionInterface $description)
    {
        $this->description = $description;

        return $this;
    }

    /**
     * Returns the service description
     *
     * @return ServiceDescriptionInterface
     */
    public function getServiceDescription()
    {
        return $this->description;
    }

    public function factory($name, array $args = array())
    {
        $command = $this->description->getOperation($name);

        // If a command wasn't found, then try to uppercase the first letter and try again
        if (!$command) {
            $command = $this->description->getOperation(ucfirst($name));
            // If an inflector was passed, then attempt to get the command using snake_case inflection
            if (!$command && $this->inflector) {
                $command = $this->description->getOperation($this->inflector->snake($name));
            }
        }

        if ($command) {
            $class = $command->getClass();
            return new $class($args, $command, $this->description);
        }
    }
}
vendor/guzzle/guzzle/src/Guzzle/Service/Command/Factory/MapFactory.php000064400000001177151327705700022130 0ustar00<?php

namespace Guzzle\Service\Command\Factory;

/**
 * Command factory used when explicitly mapping strings to command classes
 */
class MapFactory implements FactoryInterface
{
    /** @var array Associative array mapping command names to classes */
    protected $map;

    /** @param array $map Associative array mapping command names to classes */
    public function __construct(array $map)
    {
        $this->map = $map;
    }

    public function factory($name, array $args = array())
    {
        if (isset($this->map[$name])) {
            $class = $this->map[$name];

            return new $class($args);
        }
    }
}
vendor/guzzle/guzzle/src/Guzzle/Service/Command/Factory/CompositeFactory.php000064400000010032151327705700023343 0ustar00<?php

namespace Guzzle\Service\Command\Factory;

use Guzzle\Service\Command\CommandInterface;
use Guzzle\Service\ClientInterface;

/**
 * Composite factory used by a client object to create command objects utilizing multiple factories
 */
class CompositeFactory implements \IteratorAggregate, \Countable, FactoryInterface
{
    /** @var array Array of command factories */
    protected $factories;

    /**
     * Get the default chain to use with clients
     *
     * @param ClientInterface $client Client to base the chain on
     *
     * @return self
     */
    public static function getDefaultChain(ClientInterface $client)
    {
        $factories = array();
        if ($description = $client->getDescription()) {
            $factories[] = new ServiceDescriptionFactory($description);
        }
        $factories[] = new ConcreteClassFactory($client);

        return new self($factories);
    }

    /**
     * @param array $factories Array of command factories
     */
    public function __construct(array $factories = array())
    {
        $this->factories = $factories;
    }

    /**
     * Add a command factory to the chain
     *
     * @param FactoryInterface        $factory Factory to add
     * @param string|FactoryInterface $before  Insert the new command factory before a command factory class or object
     *                                         matching a class name.
     * @return CompositeFactory
     */
    public function add(FactoryInterface $factory, $before = null)
    {
        $pos = null;

        if ($before) {
            foreach ($this->factories as $i => $f) {
                if ($before instanceof FactoryInterface) {
                    if ($f === $before) {
                        $pos = $i;
                        break;
                    }
                } elseif (is_string($before)) {
                    if ($f instanceof $before) {
                        $pos = $i;
                        break;
                    }
                }
            }
        }

        if ($pos === null) {
            $this->factories[] = $factory;
        } else {
            array_splice($this->factories, $i, 0, array($factory));
        }

        return $this;
    }

    /**
     * Check if the chain contains a specific command factory
     *
     * @param FactoryInterface|string $factory Factory to check
     *
     * @return bool
     */
    public function has($factory)
    {
        return (bool) $this->find($factory);
    }

    /**
     * Remove a specific command factory from the chain
     *
     * @param string|FactoryInterface $factory Factory to remove by name or instance
     *
     * @return CompositeFactory
     */
    public function remove($factory = null)
    {
        if (!($factory instanceof FactoryInterface)) {
            $factory = $this->find($factory);
        }

        $this->factories = array_values(array_filter($this->factories, function($f) use ($factory) {
            return $f !== $factory;
        }));

        return $this;
    }

    /**
     * Get a command factory by class name
     *
     * @param string|FactoryInterface $factory Command factory class or instance
     *
     * @return null|FactoryInterface
     */
    public function find($factory)
    {
        foreach ($this->factories as $f) {
            if ($factory === $f || (is_string($factory) && $f instanceof $factory)) {
                return $f;
            }
        }
    }

    /**
     * Create a command using the associated command factories
     *
     * @param string $name Name of the command
     * @param array  $args Command arguments
     *
     * @return CommandInterface
     */
    public function factory($name, array $args = array())
    {
        foreach ($this->factories as $factory) {
            $command = $factory->factory($name, $args);
            if ($command) {
                return $command;
            }
        }
    }

    public function count()
    {
        return count($this->factories);
    }

    public function getIterator()
    {
        return new \ArrayIterator($this->factories);
    }
}
vendor/guzzle/guzzle/src/Guzzle/Service/Command/Factory/FactoryInterface.php000064400000000652151327705700023310 0ustar00<?php

namespace Guzzle\Service\Command\Factory;

use Guzzle\Service\Command\CommandInterface;

/**
 * Interface for creating commands by name
 */
interface FactoryInterface
{
    /**
     * Create a command by name
     *
     * @param string $name Command to create
     * @param array  $args Command arguments
     *
     * @return CommandInterface|null
     */
    public function factory($name, array $args = array());
}
vendor/guzzle/guzzle/src/Guzzle/Service/Command/Factory/ConcreteClassFactory.php000064400000003105151327705700024134 0ustar00<?php

namespace Guzzle\Service\Command\Factory;

use Guzzle\Inflection\InflectorInterface;
use Guzzle\Inflection\Inflector;
use Guzzle\Service\ClientInterface;

/**
 * Command factory used to create commands referencing concrete command classes
 */
class ConcreteClassFactory implements FactoryInterface
{
    /** @var ClientInterface */
    protected $client;

    /** @var InflectorInterface */
    protected $inflector;

    /**
     * @param ClientInterface    $client    Client that owns the commands
     * @param InflectorInterface $inflector Inflector used to resolve class names
     */
    public function __construct(ClientInterface $client, InflectorInterface $inflector = null)
    {
        $this->client = $client;
        $this->inflector = $inflector ?: Inflector::getDefault();
    }

    public function factory($name, array $args = array())
    {
        // Determine the class to instantiate based on the namespace of the current client and the default directory
        $prefix = $this->client->getConfig('command.prefix');
        if (!$prefix) {
            // The prefix can be specified in a factory method and is cached
            $prefix = implode('\\', array_slice(explode('\\', get_class($this->client)), 0, -1)) . '\\Command\\';
            $this->client->getConfig()->set('command.prefix', $prefix);
        }

        $class = $prefix . str_replace(' ', '\\', ucwords(str_replace('.', ' ', $this->inflector->camel($name))));

        // Create the concrete command if it exists
        if (class_exists($class)) {
            return new $class($args);
        }
    }
}
vendor/guzzle/guzzle/src/Guzzle/Service/Command/DefaultRequestSerializer.php000064400000013326151327705700023442 0ustar00<?php

namespace Guzzle\Service\Command;

use Guzzle\Http\Message\RequestInterface;
use Guzzle\Service\Command\LocationVisitor\Request\RequestVisitorInterface;
use Guzzle\Service\Command\LocationVisitor\VisitorFlyweight;
use Guzzle\Service\Description\OperationInterface;
use Guzzle\Service\Description\Parameter;

/**
 * Default request serializer that transforms command options and operation parameters into a request
 */
class DefaultRequestSerializer implements RequestSerializerInterface
{
    /** @var VisitorFlyweight $factory Visitor factory */
    protected $factory;

    /** @var self */
    protected static $instance;

    /**
     * @return self
     * @codeCoverageIgnore
     */
    public static function getInstance()
    {
        if (!self::$instance) {
            self::$instance = new self(VisitorFlyweight::getInstance());
        }

        return self::$instance;
    }

    /**
     * @param VisitorFlyweight $factory Factory to use when creating visitors
     */
    public function __construct(VisitorFlyweight $factory)
    {
        $this->factory = $factory;
    }

    /**
     * Add a location visitor to the serializer
     *
     * @param string                   $location Location to associate with the visitor
     * @param RequestVisitorInterface  $visitor  Visitor to attach
     *
     * @return self
     */
    public function addVisitor($location, RequestVisitorInterface $visitor)
    {
        $this->factory->addRequestVisitor($location, $visitor);

        return $this;
    }

    public function prepare(CommandInterface $command)
    {
        $request = $this->createRequest($command);
        // Keep an array of visitors found in the operation
        $foundVisitors = array();
        $operation = $command->getOperation();

        // Add arguments to the request using the location attribute
        foreach ($operation->getParams() as $name => $arg) {
            /** @var $arg \Guzzle\Service\Description\Parameter */
            $location = $arg->getLocation();
            // Skip 'uri' locations because they've already been processed
            if ($location && $location != 'uri') {
                // Instantiate visitors as they are detected in the properties
                if (!isset($foundVisitors[$location])) {
                    $foundVisitors[$location] = $this->factory->getRequestVisitor($location);
                }
                // Ensure that a value has been set for this parameter
                $value = $command[$name];
                if ($value !== null) {
                    // Apply the parameter value with the location visitor
                    $foundVisitors[$location]->visit($command, $request, $arg, $value);
                }
            }
        }

        // Serialize additional parameters
        if ($additional = $operation->getAdditionalParameters()) {
            if ($visitor = $this->prepareAdditionalParameters($operation, $command, $request, $additional)) {
                $foundVisitors[$additional->getLocation()] = $visitor;
            }
        }

        // Call the after method on each visitor found in the operation
        foreach ($foundVisitors as $visitor) {
            $visitor->after($command, $request);
        }

        return $request;
    }

    /**
     * Serialize additional parameters
     *
     * @param OperationInterface $operation  Operation that owns the command
     * @param CommandInterface   $command    Command to prepare
     * @param RequestInterface   $request    Request to serialize
     * @param Parameter          $additional Additional parameters
     *
     * @return null|RequestVisitorInterface
     */
    protected function prepareAdditionalParameters(
        OperationInterface $operation,
        CommandInterface $command,
        RequestInterface $request,
        Parameter $additional
    ) {
        if (!($location = $additional->getLocation())) {
            return;
        }

        $visitor = $this->factory->getRequestVisitor($location);
        $hidden = $command[$command::HIDDEN_PARAMS];

        foreach ($command->toArray() as $key => $value) {
            // Ignore values that are null or built-in command options
            if ($value !== null
                && !in_array($key, $hidden)
                && !$operation->hasParam($key)
            ) {
                $additional->setName($key);
                $visitor->visit($command, $request, $additional, $value);
            }
        }

        return $visitor;
    }

    /**
     * Create a request for the command and operation
     *
     * @param CommandInterface $command Command to create a request for
     *
     * @return RequestInterface
     */
    protected function createRequest(CommandInterface $command)
    {
        $operation = $command->getOperation();
        $client = $command->getClient();
        $options = $command[AbstractCommand::REQUEST_OPTIONS] ?: array();

        // If the command does not specify a template, then assume the base URL of the client
        if (!($uri = $operation->getUri())) {
            return $client->createRequest($operation->getHttpMethod(), $client->getBaseUrl(), null, null, $options);
        }

        // Get the path values and use the client config settings
        $variables = array();
        foreach ($operation->getParams() as $name => $arg) {
            if ($arg->getLocation() == 'uri') {
                if (isset($command[$name])) {
                    $variables[$name] = $arg->filter($command[$name]);
                    if (!is_array($variables[$name])) {
                        $variables[$name] = (string) $variables[$name];
                    }
                }
            }
        }

        return $client->createRequest($operation->getHttpMethod(), array($uri, $variables), null, null, $options);
    }
}
vendor/guzzle/guzzle/src/Guzzle/Service/Command/DefaultResponseParser.php000064400000002725151327705700022734 0ustar00<?php

namespace Guzzle\Service\Command;

use Guzzle\Http\Message\Response;

/**
 * Default HTTP response parser used to marshal JSON responses into arrays and XML responses into SimpleXMLElement
 */
class DefaultResponseParser implements ResponseParserInterface
{
    /** @var self */
    protected static $instance;

    /**
     * @return self
     * @codeCoverageIgnore
     */
    public static function getInstance()
    {
        if (!self::$instance) {
            self::$instance = new self;
        }

        return self::$instance;
    }

    public function parse(CommandInterface $command)
    {
        $response = $command->getRequest()->getResponse();

        // Account for hard coded content-type values specified in service descriptions
        if ($contentType = $command['command.expects']) {
            $response->setHeader('Content-Type', $contentType);
        } else {
            $contentType = (string) $response->getHeader('Content-Type');
        }

        return $this->handleParsing($command, $response, $contentType);
    }

    protected function handleParsing(CommandInterface $command, Response $response, $contentType)
    {
        $result = $response;
        if ($result->getBody()) {
            if (stripos($contentType, 'json') !== false) {
                $result = $result->json();
            } elseif (stripos($contentType, 'xml') !== false) {
                $result = $result->xml();
            }
        }

        return $result;
    }
}
vendor/guzzle/guzzle/src/Guzzle/Service/Command/OperationCommand.php000064400000004733151327705700021714 0ustar00<?php

namespace Guzzle\Service\Command;

/**
 * A command that creates requests based on {@see Guzzle\Service\Description\OperationInterface} objects, and if the
 * matching operation uses a service description model in the responseClass attribute, then this command will marshal
 * the response into an associative array based on the JSON schema of the model.
 */
class OperationCommand extends AbstractCommand
{
    /** @var RequestSerializerInterface */
    protected $requestSerializer;

    /** @var ResponseParserInterface Response parser */
    protected $responseParser;

    /**
     * Set the response parser used with the command
     *
     * @param ResponseParserInterface $parser Response parser
     *
     * @return self
     */
    public function setResponseParser(ResponseParserInterface $parser)
    {
        $this->responseParser = $parser;

        return $this;
    }

    /**
     * Set the request serializer used with the command
     *
     * @param RequestSerializerInterface $serializer Request serializer
     *
     * @return self
     */
    public function setRequestSerializer(RequestSerializerInterface $serializer)
    {
        $this->requestSerializer = $serializer;

        return $this;
    }

    /**
     * Get the request serializer used with the command
     *
     * @return RequestSerializerInterface
     */
    public function getRequestSerializer()
    {
        if (!$this->requestSerializer) {
            // Use the default request serializer if none was found
            $this->requestSerializer = DefaultRequestSerializer::getInstance();
        }

        return $this->requestSerializer;
    }

    /**
     * Get the response parser used for the operation
     *
     * @return ResponseParserInterface
     */
    public function getResponseParser()
    {
        if (!$this->responseParser) {
            // Use the default response parser if none was found
            $this->responseParser = OperationResponseParser::getInstance();
        }

        return $this->responseParser;
    }

    protected function build()
    {
        // Prepare and serialize the request
        $this->request = $this->getRequestSerializer()->prepare($this);
    }

    protected function process()
    {
        // Do not process the response if 'command.response_processing' is set to 'raw'
        $this->result = $this[self::RESPONSE_PROCESSING] == self::TYPE_RAW
            ? $this->request->getResponse()
            : $this->getResponseParser()->parse($this);
    }
}
vendor/guzzle/guzzle/src/Guzzle/Service/Command/RequestSerializerInterface.php000064400000000746151327705700023760 0ustar00<?php

namespace Guzzle\Service\Command;

use Guzzle\Http\Message\RequestInterface;
use Guzzle\Service\Command\CommandInterface;

/**
 * Translates command options and operation parameters into a request object
 */
interface RequestSerializerInterface
{
    /**
     * Create a request for a command
     *
     * @param CommandInterface $command Command that will own the request
     *
     * @return RequestInterface
     */
    public function prepare(CommandInterface $command);
}
vendor/guzzle/guzzle/src/Guzzle/Service/Command/ClosureCommand.php000064400000002435151327705700021365 0ustar00<?php

namespace Guzzle\Service\Command;

use Guzzle\Common\Exception\InvalidArgumentException;
use Guzzle\Common\Exception\UnexpectedValueException;
use Guzzle\Http\Message\RequestInterface;

/**
 * A ClosureCommand is a command that allows dynamic commands to be created at runtime using a closure to prepare the
 * request. A closure key and \Closure value must be passed to the command in the constructor. The closure must
 * accept the command object as an argument.
 */
class ClosureCommand extends AbstractCommand
{
    /**
     * {@inheritdoc}
     * @throws InvalidArgumentException if a closure was not passed
     */
    protected function init()
    {
        if (!$this['closure']) {
            throw new InvalidArgumentException('A closure must be passed in the parameters array');
        }
    }

    /**
     * {@inheritdoc}
     * @throws UnexpectedValueException If the closure does not return a request
     */
    protected function build()
    {
        $closure = $this['closure'];
        /** @var $closure \Closure */
        $this->request = $closure($this, $this->operation);

        if (!$this->request || !$this->request instanceof RequestInterface) {
            throw new UnexpectedValueException('Closure command did not return a RequestInterface object');
        }
    }
}
vendor/guzzle/guzzle/src/Guzzle/Service/ConfigLoaderInterface.php000064400000001273151327705700021250 0ustar00<?php

namespace Guzzle\Service;

/**
 * Interface used for loading configuration data (service descriptions, service builder configs, etc)
 *
 * If a loaded configuration data sets includes a top level key containing an 'includes' section, then the data in the
 * file will extend the merged result of all of the included config files.
 */
interface ConfigLoaderInterface
{
    /**
     * Loads configuration data and returns an array of the loaded result
     *
     * @param mixed $config  Data to load (filename or array of data)
     * @param array $options Array of options to use when loading
     *
     * @return mixed
     */
    public function load($config, array $options = array());
}
vendor/guzzle/guzzle/src/Guzzle/Service/CachingConfigLoader.php000064400000002224151327705700020701 0ustar00<?php

namespace Guzzle\Service;

use Guzzle\Cache\CacheAdapterInterface;

/**
 * Decorator that adds caching to a service description loader
 */
class CachingConfigLoader implements ConfigLoaderInterface
{
    /** @var ConfigLoaderInterface */
    protected $loader;

    /** @var CacheAdapterInterface */
    protected $cache;

    /**
     * @param ConfigLoaderInterface $loader Loader used to load the config when there is a cache miss
     * @param CacheAdapterInterface $cache  Object used to cache the loaded result
     */
    public function __construct(ConfigLoaderInterface $loader, CacheAdapterInterface $cache)
    {
        $this->loader = $loader;
        $this->cache = $cache;
    }

    public function load($config, array $options = array())
    {
        if (!is_string($config)) {
            $key = false;
        } else {
            $key = 'loader_' . crc32($config);
            if ($result = $this->cache->fetch($key)) {
                return $result;
            }
        }

        $result = $this->loader->load($config, $options);
        if ($key) {
            $this->cache->save($key, $result);
        }

        return $result;
    }
}
vendor/guzzle/guzzle/src/Guzzle/Service/Builder/ServiceBuilderLoader.php000064400000006504151327705700022521 0ustar00<?php

namespace Guzzle\Service\Builder;

use Guzzle\Service\AbstractConfigLoader;
use Guzzle\Service\Exception\ServiceNotFoundException;

/**
 * Service builder config loader
 */
class ServiceBuilderLoader extends AbstractConfigLoader
{
    protected function build($config, array $options)
    {
        // A service builder class can be specified in the class field
        $class = !empty($config['class']) ? $config['class'] : __NAMESPACE__ . '\\ServiceBuilder';

        // Account for old style configs that do not have a services array
        $services = isset($config['services']) ? $config['services'] : $config;

        // Validate the configuration and handle extensions
        foreach ($services as $name => &$service) {

            $service['params'] = isset($service['params']) ? $service['params'] : array();

            // Check if this client builder extends another client
            if (!empty($service['extends'])) {

                // Make sure that the service it's extending has been defined
                if (!isset($services[$service['extends']])) {
                    throw new ServiceNotFoundException(
                        "{$name} is trying to extend a non-existent service: {$service['extends']}"
                    );
                }

                $extended = &$services[$service['extends']];

                // Use the correct class attribute
                if (empty($service['class'])) {
                    $service['class'] = isset($extended['class']) ? $extended['class'] : '';
                }
                if ($extendsParams = isset($extended['params']) ? $extended['params'] : false) {
                    $service['params'] = $service['params'] + $extendsParams;
                }
            }

            // Overwrite default values with global parameter values
            if (!empty($options)) {
                $service['params'] = $options + $service['params'];
            }

            $service['class'] = isset($service['class']) ? $service['class'] : '';
        }

        return new $class($services);
    }

    protected function mergeData(array $a, array $b)
    {
        $result = $b + $a;

        // Merge services using a recursive union of arrays
        if (isset($a['services']) && $b['services']) {

            // Get a union of the services of the two arrays
            $result['services'] = $b['services'] + $a['services'];

            // Merge each service in using a union of the two arrays
            foreach ($result['services'] as $name => &$service) {

                // By default, services completely override a previously defined service unless it extends itself
                if (isset($a['services'][$name]['extends'])
                    && isset($b['services'][$name]['extends'])
                    && $b['services'][$name]['extends'] == $name
                ) {
                    $service += $a['services'][$name];
                    // Use the `extends` attribute of the parent
                    $service['extends'] = $a['services'][$name]['extends'];
                    // Merge parameters using a union if both have parameters
                    if (isset($a['services'][$name]['params'])) {
                        $service['params'] += $a['services'][$name]['params'];
                    }
                }
            }
        }

        return $result;
    }
}
vendor/guzzle/guzzle/src/Guzzle/Service/Builder/ServiceBuilderInterface.php000064400000003706151327705700023214 0ustar00<?php

namespace Guzzle\Service\Builder;

use Guzzle\Service\Exception\ServiceNotFoundException;

/**
 * Service builder used to store and build clients or arbitrary data. Client configuration data can be supplied to tell
 * the service builder how to create and cache {@see \Guzzle\Service\ClientInterface} objects. Arbitrary data can be
 * supplied and accessed from a service builder. Arbitrary data and other clients can be referenced by name in client
 * configuration arrays to make them input for building other clients (e.g. "{key}").
 */
interface ServiceBuilderInterface
{
    /**
     * Get a ClientInterface object or arbitrary data from the service builder
     *
     * @param string     $name      Name of the registered service or data to retrieve
     * @param bool|array $throwAway Only pertains to retrieving client objects built using a configuration array.
     *                              Set to TRUE to not store the client for later retrieval from the ServiceBuilder.
     *                              If an array is specified, that data will overwrite the configured params of the
     *                              client if the client implements {@see \Guzzle\Common\FromConfigInterface} and will
     *                              not store the client for later retrieval.
     *
     * @return \Guzzle\Service\ClientInterface|mixed
     * @throws ServiceNotFoundException when a client or data cannot be found by the given name
     */
    public function get($name, $throwAway = false);

    /**
     * Register a service or arbitrary data by name with the service builder
     *
     * @param string $key     Name of the client or data to register
     * @param mixed  $service Client configuration array or arbitrary data to register. The client configuration array
     *                        must include a 'class' (string) and 'params' (array) key.
     *
     * @return ServiceBuilderInterface
     */
    public function set($key, $service);
}
vendor/guzzle/guzzle/src/Guzzle/Service/Builder/ServiceBuilder.php000064400000013431151327705700021367 0ustar00<?php

namespace Guzzle\Service\Builder;

use Guzzle\Common\AbstractHasDispatcher;
use Guzzle\Service\ClientInterface;
use Guzzle\Service\Exception\ServiceBuilderException;
use Guzzle\Service\Exception\ServiceNotFoundException;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;

/**
 * {@inheritdoc}
 *
 * Clients and data can be set, retrieved, and removed by accessing the service builder like an associative array.
 */
class ServiceBuilder extends AbstractHasDispatcher implements ServiceBuilderInterface, \ArrayAccess, \Serializable
{
    /** @var array Service builder configuration data */
    protected $builderConfig = array();

    /** @var array Instantiated client objects */
    protected $clients = array();

    /** @var ServiceBuilderLoader Cached instance of the service builder loader */
    protected static $cachedFactory;

    /** @var array Plugins to attach to each client created by the service builder */
    protected $plugins = array();

    /**
     * Create a new ServiceBuilder using configuration data sourced from an
     * array, .js|.json or .php file.
     *
     * @param array|string $config           The full path to an .json|.js or .php file, or an associative array
     * @param array        $globalParameters Array of global parameters to pass to every service as it is instantiated.
     *
     * @return ServiceBuilderInterface
     * @throws ServiceBuilderException if a file cannot be opened
     * @throws ServiceNotFoundException when trying to extend a missing client
     */
    public static function factory($config = null, array $globalParameters = array())
    {
        // @codeCoverageIgnoreStart
        if (!static::$cachedFactory) {
            static::$cachedFactory = new ServiceBuilderLoader();
        }
        // @codeCoverageIgnoreEnd

        return self::$cachedFactory->load($config, $globalParameters);
    }

    /**
     * @param array $serviceBuilderConfig Service configuration settings:
     *     - name: Name of the service
     *     - class: Client class to instantiate using a factory method
     *     - params: array of key value pair configuration settings for the builder
     */
    public function __construct(array $serviceBuilderConfig = array())
    {
        $this->builderConfig = $serviceBuilderConfig;
    }

    public static function getAllEvents()
    {
        return array('service_builder.create_client');
    }

    public function unserialize($serialized)
    {
        $this->builderConfig = json_decode($serialized, true);
    }

    public function serialize()
    {
        return json_encode($this->builderConfig);
    }

    /**
     * Attach a plugin to every client created by the builder
     *
     * @param EventSubscriberInterface $plugin Plugin to attach to each client
     *
     * @return self
     */
    public function addGlobalPlugin(EventSubscriberInterface $plugin)
    {
        $this->plugins[] = $plugin;

        return $this;
    }

    /**
     * Get data from the service builder without triggering the building of a service
     *
     * @param string $name Name of the service to retrieve
     *
     * @return array|null
     */
    public function getData($name)
    {
        return isset($this->builderConfig[$name]) ? $this->builderConfig[$name] : null;
    }

    public function get($name, $throwAway = false)
    {
        if (!isset($this->builderConfig[$name])) {

            // Check to see if arbitrary data is being referenced
            if (isset($this->clients[$name])) {
                return $this->clients[$name];
            }

            // Check aliases and return a match if found
            foreach ($this->builderConfig as $actualName => $config) {
                if (isset($config['alias']) && $config['alias'] == $name) {
                    return $this->get($actualName, $throwAway);
                }
            }
            throw new ServiceNotFoundException('No service is registered as ' . $name);
        }

        if (!$throwAway && isset($this->clients[$name])) {
            return $this->clients[$name];
        }

        $builder =& $this->builderConfig[$name];

        // Convert references to the actual client
        foreach ($builder['params'] as &$v) {
            if (is_string($v) && substr($v, 0, 1) == '{' && substr($v, -1) == '}') {
                $v = $this->get(trim($v, '{} '));
            }
        }

        // Get the configured parameters and merge in any parameters provided for throw-away clients
        $config = $builder['params'];
        if (is_array($throwAway)) {
            $config = $throwAway + $config;
        }

        $client = $builder['class']::factory($config);

        if (!$throwAway) {
            $this->clients[$name] = $client;
        }

        if ($client instanceof ClientInterface) {
            foreach ($this->plugins as $plugin) {
                $client->addSubscriber($plugin);
            }
            // Dispatch an event letting listeners know a client was created
            $this->dispatch('service_builder.create_client', array('client' => $client));
        }

        return $client;
    }

    public function set($key, $service)
    {
        if (is_array($service) && isset($service['class']) && isset($service['params'])) {
            $this->builderConfig[$key] = $service;
        } else {
            $this->clients[$key] = $service;
        }

        return $this;
    }

    public function offsetSet($offset, $value)
    {
        $this->set($offset, $value);
    }

    public function offsetUnset($offset)
    {
        unset($this->builderConfig[$offset]);
        unset($this->clients[$offset]);
    }

    public function offsetExists($offset)
    {
        return isset($this->builderConfig[$offset]) || isset($this->clients[$offset]);
    }

    public function offsetGet($offset)
    {
        return $this->get($offset);
    }
}
vendor/guzzle/guzzle/src/Guzzle/Service/Description/ValidatorInterface.php000064400000001721151327705700023122 0ustar00<?php

namespace Guzzle\Service\Description;

/**
 * Validator responsible for preparing and validating parameters against the parameter's schema
 */
interface ValidatorInterface
{
    /**
     * Validate a value against the acceptable types, regular expressions, minimum, maximums, instanceOf, enums, etc
     * Add default and static values to the passed in variable. If the validation completes successfully, the input
     * must be run correctly through the matching schema's filters attribute.
     *
     * @param Parameter $param Schema that is being validated against the value
     * @param mixed     $value Value to validate and process. The value may change during this process.
     *
     * @return bool  Returns true if the input data is valid for the schema
     */
    public function validate(Parameter $param, &$value);

    /**
     * Get validation errors encountered while validating
     *
     * @return array
     */
    public function getErrors();
}
vendor/guzzle/guzzle/src/Guzzle/Service/Description/OperationInterface.php000064400000007211151327705700023135 0ustar00<?php

namespace Guzzle\Service\Description;

use Guzzle\Common\ToArrayInterface;

/**
 * Interface defining data objects that hold the information of an API operation
 */
interface OperationInterface extends ToArrayInterface
{
    const TYPE_PRIMITIVE = 'primitive';
    const TYPE_CLASS = 'class';
    const TYPE_DOCUMENTATION = 'documentation';
    const TYPE_MODEL = 'model';

    /**
     * Get the service description that the operation belongs to
     *
     * @return ServiceDescriptionInterface|null
     */
    public function getServiceDescription();

    /**
     * Set the service description that the operation belongs to
     *
     * @param ServiceDescriptionInterface $description Service description
     *
     * @return self
     */
    public function setServiceDescription(ServiceDescriptionInterface $description);

    /**
     * Get the params of the operation
     *
     * @return array
     */
    public function getParams();

    /**
     * Returns an array of parameter names
     *
     * @return array
     */
    public function getParamNames();

    /**
     * Check if the operation has a specific parameter by name
     *
     * @param string $name Name of the param
     *
     * @return bool
     */
    public function hasParam($name);

    /**
     * Get a single parameter of the operation
     *
     * @param string $param Parameter to retrieve by name
     *
     * @return Parameter|null
     */
    public function getParam($param);

    /**
     * Get the HTTP method of the operation
     *
     * @return string|null
     */
    public function getHttpMethod();

    /**
     * Get the concrete operation class that implements this operation
     *
     * @return string
     */
    public function getClass();

    /**
     * Get the name of the operation
     *
     * @return string|null
     */
    public function getName();

    /**
     * Get a short summary of what the operation does
     *
     * @return string|null
     */
    public function getSummary();

    /**
     * Get a longer text field to explain the behavior of the operation
     *
     * @return string|null
     */
    public function getNotes();

    /**
     * Get the documentation URL of the operation
     *
     * @return string|null
     */
    public function getDocumentationUrl();

    /**
     * Get what is returned from the method. Can be a primitive, class name, or model. For example, the responseClass
     * could be 'array', which would inherently use a responseType of 'primitive'. Using a class name would set a
     * responseType of 'class'. Specifying a model by ID will use a responseType of 'model'.
     *
     * @return string|null
     */
    public function getResponseClass();

    /**
     * Get information about how the response is unmarshalled: One of 'primitive', 'class', 'model', or 'documentation'
     *
     * @return string
     */
    public function getResponseType();

    /**
     * Get notes about the response of the operation
     *
     * @return string|null
     */
    public function getResponseNotes();

    /**
     * Get whether or not the operation is deprecated
     *
     * @return bool
     */
    public function getDeprecated();

    /**
     * Get the URI that will be merged into the generated request
     *
     * @return string
     */
    public function getUri();

    /**
     * Get the errors that could be encountered when executing the operation
     *
     * @return array
     */
    public function getErrorResponses();

    /**
     * Get extra data from the operation
     *
     * @param string $name Name of the data point to retrieve
     *
     * @return mixed|null
     */
    public function getData($name);
}
vendor/guzzle/guzzle/src/Guzzle/Service/Description/Parameter.php000064400000060737151327705700021310 0ustar00<?php

namespace Guzzle\Service\Description;

use Guzzle\Common\Exception\InvalidArgumentException;

/**
 * API parameter object used with service descriptions
 */
class Parameter
{
    protected $name;
    protected $description;
    protected $serviceDescription;
    protected $type;
    protected $required;
    protected $enum;
    protected $pattern;
    protected $minimum;
    protected $maximum;
    protected $minLength;
    protected $maxLength;
    protected $minItems;
    protected $maxItems;
    protected $default;
    protected $static;
    protected $instanceOf;
    protected $filters;
    protected $location;
    protected $sentAs;
    protected $data;
    protected $properties = array();
    protected $additionalProperties;
    protected $items;
    protected $parent;
    protected $ref;
    protected $format;
    protected $propertiesCache = null;

    /**
     * Create a new Parameter using an associative array of data. The array can contain the following information:
     * - name:          (string) Unique name of the parameter
     * - type:          (string|array) Type of variable (string, number, integer, boolean, object, array, numeric,
     *                  null, any). Types are using for validation and determining the structure of a parameter. You
     *                  can use a union type by providing an array of simple types. If one of the union types matches
     *                  the provided value, then the value is valid.
     * - instanceOf:    (string) When the type is an object, you can specify the class that the object must implement
     * - required:      (bool) Whether or not the parameter is required
     * - default:       (mixed) Default value to use if no value is supplied
     * - static:        (bool) Set to true to specify that the parameter value cannot be changed from the default
     * - description:   (string) Documentation of the parameter
     * - location:      (string) The location of a request used to apply a parameter. Custom locations can be registered
     *                  with a command, but the defaults are uri, query, header, body, json, xml, postField, postFile.
     * - sentAs:        (string) Specifies how the data being modeled is sent over the wire. For example, you may wish
     *                  to include certain headers in a response model that have a normalized casing of FooBar, but the
     *                  actual header is x-foo-bar. In this case, sentAs would be set to x-foo-bar.
     * - filters:       (array) Array of static method names to to run a parameter value through. Each value in the
     *                  array must be a string containing the full class path to a static method or an array of complex
     *                  filter information. You can specify static methods of classes using the full namespace class
     *                  name followed by '::' (e.g. Foo\Bar::baz()). Some filters require arguments in order to properly
     *                  filter a value. For complex filters, use a hash containing a 'method' key pointing to a static
     *                  method, and an 'args' key containing an array of positional arguments to pass to the method.
     *                  Arguments can contain keywords that are replaced when filtering a value: '@value' is replaced
     *                  with the value being validated, '@api' is replaced with the Parameter object.
     * - properties:    When the type is an object, you can specify nested parameters
     * - additionalProperties: (array) This attribute defines a schema for all properties that are not explicitly
     *                  defined in an object type definition. If specified, the value MUST be a schema or a boolean. If
     *                  false is provided, no additional properties are allowed beyond the properties defined in the
     *                  schema. The default value is an empty schema which allows any value for additional properties.
     * - items:         This attribute defines the allowed items in an instance array, and MUST be a schema or an array
     *                  of schemas. The default value is an empty schema which allows any value for items in the
     *                  instance array.
     *                  When this attribute value is a schema and the instance value is an array, then all the items
     *                  in the array MUST be valid according to the schema.
     * - pattern:       When the type is a string, you can specify the regex pattern that a value must match
     * - enum:          When the type is a string, you can specify a list of acceptable values
     * - minItems:      (int) Minimum number of items allowed in an array
     * - maxItems:      (int) Maximum number of items allowed in an array
     * - minLength:     (int) Minimum length of a string
     * - maxLength:     (int) Maximum length of a string
     * - minimum:       (int) Minimum value of an integer
     * - maximum:       (int) Maximum value of an integer
     * - data:          (array) Any additional custom data to use when serializing, validating, etc
     * - format:        (string) Format used to coax a value into the correct format when serializing or unserializing.
     *                  You may specify either an array of filters OR a format, but not both.
     *                  Supported values: date-time, date, time, timestamp, date-time-http
     * - $ref:          (string) String referencing a service description model. The parameter is replaced by the
     *                  schema contained in the model.
     *
     * @param array                       $data        Array of data as seen in service descriptions
     * @param ServiceDescriptionInterface $description Service description used to resolve models if $ref tags are found
     *
     * @throws InvalidArgumentException
     */
    public function __construct(array $data = array(), ServiceDescriptionInterface $description = null)
    {
        if ($description) {
            if (isset($data['$ref'])) {
                if ($model = $description->getModel($data['$ref'])) {
                    $data = $model->toArray() + $data;
                }
            } elseif (isset($data['extends'])) {
                // If this parameter extends from another parameter then start with the actual data
                // union in the parent's data (e.g. actual supersedes parent)
                if ($extends = $description->getModel($data['extends'])) {
                    $data += $extends->toArray();
                }
            }
        }

        // Pull configuration data into the parameter
        foreach ($data as $key => $value) {
            $this->{$key} = $value;
        }

        $this->serviceDescription = $description;
        $this->required = (bool) $this->required;
        $this->data = (array) $this->data;

        if ($this->filters) {
            $this->setFilters((array) $this->filters);
        }

        if ($this->type == 'object' && $this->additionalProperties === null) {
            $this->additionalProperties = true;
        }
    }

    /**
     * Convert the object to an array
     *
     * @return array
     */
    public function toArray()
    {
        static $checks = array('required', 'description', 'static', 'type', 'format', 'instanceOf', 'location', 'sentAs',
            'pattern', 'minimum', 'maximum', 'minItems', 'maxItems', 'minLength', 'maxLength', 'data', 'enum',
            'filters');

        $result = array();

        // Anything that is in the `Items` attribute of an array *must* include it's name if available
        if ($this->parent instanceof self && $this->parent->getType() == 'array' && isset($this->name)) {
            $result['name'] = $this->name;
        }

        foreach ($checks as $c) {
            if ($value = $this->{$c}) {
                $result[$c] = $value;
            }
        }

        if ($this->default !== null) {
            $result['default'] = $this->default;
        }

        if ($this->items !== null) {
            $result['items'] = $this->getItems()->toArray();
        }

        if ($this->additionalProperties !== null) {
            $result['additionalProperties'] = $this->getAdditionalProperties();
            if ($result['additionalProperties'] instanceof self) {
                $result['additionalProperties'] = $result['additionalProperties']->toArray();
            }
        }

        if ($this->type == 'object' && $this->properties) {
            $result['properties'] = array();
            foreach ($this->getProperties() as $name => $property) {
                $result['properties'][$name] = $property->toArray();
            }
        }

        return $result;
    }

    /**
     * Get the default or static value of the command based on a value
     *
     * @param string $value Value that is currently set
     *
     * @return mixed Returns the value, a static value if one is present, or a default value
     */
    public function getValue($value)
    {
        if ($this->static || ($this->default !== null && $value === null)) {
            return $this->default;
        }

        return $value;
    }

    /**
     * Run a value through the filters OR format attribute associated with the parameter
     *
     * @param mixed $value Value to filter
     *
     * @return mixed Returns the filtered value
     */
    public function filter($value)
    {
        // Formats are applied exclusively and supersed filters
        if ($this->format) {
            return SchemaFormatter::format($this->format, $value);
        }

        // Convert Boolean values
        if ($this->type == 'boolean' && !is_bool($value)) {
            $value = filter_var($value, FILTER_VALIDATE_BOOLEAN);
        }

        // Apply filters to the value
        if ($this->filters) {
            foreach ($this->filters as $filter) {
                if (is_array($filter)) {
                    // Convert complex filters that hold value place holders
                    foreach ($filter['args'] as &$data) {
                        if ($data == '@value') {
                            $data = $value;
                        } elseif ($data == '@api') {
                            $data = $this;
                        }
                    }
                    $value = call_user_func_array($filter['method'], $filter['args']);
                } else {
                    $value = call_user_func($filter, $value);
                }
            }
        }

        return $value;
    }

    /**
     * Get the name of the parameter
     *
     * @return string
     */
    public function getName()
    {
        return $this->name;
    }

    /**
     * Get the key of the parameter, where sentAs will supersede name if it is set
     *
     * @return string
     */
    public function getWireName()
    {
        return $this->sentAs ?: $this->name;
    }

    /**
     * Set the name of the parameter
     *
     * @param string $name Name to set
     *
     * @return self
     */
    public function setName($name)
    {
        $this->name = $name;

        return $this;
    }

    /**
     * Get the type(s) of the parameter
     *
     * @return string|array
     */
    public function getType()
    {
        return $this->type;
    }

    /**
     * Set the type(s) of the parameter
     *
     * @param string|array $type Type of parameter or array of simple types used in a union
     *
     * @return self
     */
    public function setType($type)
    {
        $this->type = $type;

        return $this;
    }

    /**
     * Get if the parameter is required
     *
     * @return bool
     */
    public function getRequired()
    {
        return $this->required;
    }

    /**
     * Set if the parameter is required
     *
     * @param bool $isRequired Whether or not the parameter is required
     *
     * @return self
     */
    public function setRequired($isRequired)
    {
        $this->required = (bool) $isRequired;

        return $this;
    }

    /**
     * Get the default value of the parameter
     *
     * @return string|null
     */
    public function getDefault()
    {
        return $this->default;
    }

    /**
     * Set the default value of the parameter
     *
     * @param string|null $default Default value to set
     *
     * @return self
     */
    public function setDefault($default)
    {
        $this->default = $default;

        return $this;
    }

    /**
     * Get the description of the parameter
     *
     * @return string|null
     */
    public function getDescription()
    {
        return $this->description;
    }

    /**
     * Set the description of the parameter
     *
     * @param string $description Description
     *
     * @return self
     */
    public function setDescription($description)
    {
        $this->description = $description;

        return $this;
    }

    /**
     * Get the minimum acceptable value for an integer
     *
     * @return int|null
     */
    public function getMinimum()
    {
        return $this->minimum;
    }

    /**
     * Set the minimum acceptable value for an integer
     *
     * @param int|null $min Minimum
     *
     * @return self
     */
    public function setMinimum($min)
    {
        $this->minimum = $min;

        return $this;
    }

    /**
     * Get the maximum acceptable value for an integer
     *
     * @return int|null
     */
    public function getMaximum()
    {
        return $this->maximum;
    }

    /**
     * Set the maximum acceptable value for an integer
     *
     * @param int $max Maximum
     *
     * @return self
     */
    public function setMaximum($max)
    {
        $this->maximum = $max;

        return $this;
    }

    /**
     * Get the minimum allowed length of a string value
     *
     * @return int
     */
    public function getMinLength()
    {
        return $this->minLength;
    }

    /**
     * Set the minimum allowed length of a string value
     *
     * @param int|null $min Minimum
     *
     * @return self
     */
    public function setMinLength($min)
    {
        $this->minLength = $min;

        return $this;
    }

    /**
     * Get the maximum allowed length of a string value
     *
     * @return int|null
     */
    public function getMaxLength()
    {
        return $this->maxLength;
    }

    /**
     * Set the maximum allowed length of a string value
     *
     * @param int $max Maximum length
     *
     * @return self
     */
    public function setMaxLength($max)
    {
        $this->maxLength = $max;

        return $this;
    }

    /**
     * Get the maximum allowed number of items in an array value
     *
     * @return int|null
     */
    public function getMaxItems()
    {
        return $this->maxItems;
    }

    /**
     * Set the maximum allowed number of items in an array value
     *
     * @param int $max Maximum
     *
     * @return self
     */
    public function setMaxItems($max)
    {
        $this->maxItems = $max;

        return $this;
    }

    /**
     * Get the minimum allowed number of items in an array value
     *
     * @return int
     */
    public function getMinItems()
    {
        return $this->minItems;
    }

    /**
     * Set the minimum allowed number of items in an array value
     *
     * @param int|null $min Minimum
     *
     * @return self
     */
    public function setMinItems($min)
    {
        $this->minItems = $min;

        return $this;
    }

    /**
     * Get the location of the parameter
     *
     * @return string|null
     */
    public function getLocation()
    {
        return $this->location;
    }

    /**
     * Set the location of the parameter
     *
     * @param string|null $location Location of the parameter
     *
     * @return self
     */
    public function setLocation($location)
    {
        $this->location = $location;

        return $this;
    }

    /**
     * Get the sentAs attribute of the parameter that used with locations to sentAs an attribute when it is being
     * applied to a location.
     *
     * @return string|null
     */
    public function getSentAs()
    {
        return $this->sentAs;
    }

    /**
     * Set the sentAs attribute
     *
     * @param string|null $name Name of the value as it is sent over the wire
     *
     * @return self
     */
    public function setSentAs($name)
    {
        $this->sentAs = $name;

        return $this;
    }

    /**
     * Retrieve a known property from the parameter by name or a data property by name. When not specific name value
     * is specified, all data properties will be returned.
     *
     * @param string|null $name Specify a particular property name to retrieve
     *
     * @return array|mixed|null
     */
    public function getData($name = null)
    {
        if (!$name) {
            return $this->data;
        }

        if (isset($this->data[$name])) {
            return $this->data[$name];
        } elseif (isset($this->{$name})) {
            return $this->{$name};
        }

        return null;
    }

    /**
     * Set the extra data properties of the parameter or set a specific extra property
     *
     * @param string|array|null $nameOrData The name of a specific extra to set or an array of extras to set
     * @param mixed|null        $data       When setting a specific extra property, specify the data to set for it
     *
     * @return self
     */
    public function setData($nameOrData, $data = null)
    {
        if (is_array($nameOrData)) {
            $this->data = $nameOrData;
        } else {
            $this->data[$nameOrData] = $data;
        }

        return $this;
    }

    /**
     * Get whether or not the default value can be changed
     *
     * @return mixed|null
     */
    public function getStatic()
    {
        return $this->static;
    }

    /**
     * Set to true if the default value cannot be changed
     *
     * @param bool $static True or false
     *
     * @return self
     */
    public function setStatic($static)
    {
        $this->static = (bool) $static;

        return $this;
    }

    /**
     * Get an array of filters used by the parameter
     *
     * @return array
     */
    public function getFilters()
    {
        return $this->filters ?: array();
    }

    /**
     * Set the array of filters used by the parameter
     *
     * @param array $filters Array of functions to use as filters
     *
     * @return self
     */
    public function setFilters(array $filters)
    {
        $this->filters = array();
        foreach ($filters as $filter) {
            $this->addFilter($filter);
        }

        return $this;
    }

    /**
     * Add a filter to the parameter
     *
     * @param string|array $filter Method to filter the value through
     *
     * @return self
     * @throws InvalidArgumentException
     */
    public function addFilter($filter)
    {
        if (is_array($filter)) {
            if (!isset($filter['method'])) {
                throw new InvalidArgumentException('A [method] value must be specified for each complex filter');
            }
        }

        if (!$this->filters) {
            $this->filters = array($filter);
        } else {
            $this->filters[] = $filter;
        }

        return $this;
    }

    /**
     * Get the parent object (an {@see OperationInterface} or {@see Parameter}
     *
     * @return OperationInterface|Parameter|null
     */
    public function getParent()
    {
        return $this->parent;
    }

    /**
     * Set the parent object of the parameter
     *
     * @param OperationInterface|Parameter|null $parent Parent container of the parameter
     *
     * @return self
     */
    public function setParent($parent)
    {
        $this->parent = $parent;

        return $this;
    }

    /**
     * Get the properties of the parameter
     *
     * @return array
     */
    public function getProperties()
    {
        if (!$this->propertiesCache) {
            $this->propertiesCache = array();
            foreach (array_keys($this->properties) as $name) {
                $this->propertiesCache[$name] = $this->getProperty($name);
            }
        }

        return $this->propertiesCache;
    }

    /**
     * Get a specific property from the parameter
     *
     * @param string $name Name of the property to retrieve
     *
     * @return null|Parameter
     */
    public function getProperty($name)
    {
        if (!isset($this->properties[$name])) {
            return null;
        }

        if (!($this->properties[$name] instanceof self)) {
            $this->properties[$name]['name'] = $name;
            $this->properties[$name] = new static($this->properties[$name], $this->serviceDescription);
            $this->properties[$name]->setParent($this);
        }

        return $this->properties[$name];
    }

    /**
     * Remove a property from the parameter
     *
     * @param string $name Name of the property to remove
     *
     * @return self
     */
    public function removeProperty($name)
    {
        unset($this->properties[$name]);
        $this->propertiesCache = null;

        return $this;
    }

    /**
     * Add a property to the parameter
     *
     * @param Parameter $property Properties to set
     *
     * @return self
     */
    public function addProperty(Parameter $property)
    {
        $this->properties[$property->getName()] = $property;
        $property->setParent($this);
        $this->propertiesCache = null;

        return $this;
    }

    /**
     * Get the additionalProperties value of the parameter
     *
     * @return bool|Parameter|null
     */
    public function getAdditionalProperties()
    {
        if (is_array($this->additionalProperties)) {
            $this->additionalProperties = new static($this->additionalProperties, $this->serviceDescription);
            $this->additionalProperties->setParent($this);
        }

        return $this->additionalProperties;
    }

    /**
     * Set the additionalProperties value of the parameter
     *
     * @param bool|Parameter|null $additional Boolean to allow any, an Parameter to specify a schema, or false to disallow
     *
     * @return self
     */
    public function setAdditionalProperties($additional)
    {
        $this->additionalProperties = $additional;

        return $this;
    }

    /**
     * Set the items data of the parameter
     *
     * @param Parameter|null $items Items to set
     *
     * @return self
     */
    public function setItems(Parameter $items = null)
    {
        if ($this->items = $items) {
            $this->items->setParent($this);
        }

        return $this;
    }

    /**
     * Get the item data of the parameter
     *
     * @return Parameter|null
     */
    public function getItems()
    {
        if (is_array($this->items)) {
            $this->items = new static($this->items, $this->serviceDescription);
            $this->items->setParent($this);
        }

        return $this->items;
    }

    /**
     * Get the class that the parameter must implement
     *
     * @return null|string
     */
    public function getInstanceOf()
    {
        return $this->instanceOf;
    }

    /**
     * Set the class that the parameter must be an instance of
     *
     * @param string|null $instanceOf Class or interface name
     *
     * @return self
     */
    public function setInstanceOf($instanceOf)
    {
        $this->instanceOf = $instanceOf;

        return $this;
    }

    /**
     * Get the enum of strings that are valid for the parameter
     *
     * @return array|null
     */
    public function getEnum()
    {
        return $this->enum;
    }

    /**
     * Set the enum of strings that are valid for the parameter
     *
     * @param array|null $enum Array of strings or null
     *
     * @return self
     */
    public function setEnum(array $enum = null)
    {
        $this->enum = $enum;

        return $this;
    }

    /**
     * Get the regex pattern that must match a value when the value is a string
     *
     * @return string
     */
    public function getPattern()
    {
        return $this->pattern;
    }

    /**
     * Set the regex pattern that must match a value when the value is a string
     *
     * @param string $pattern Regex pattern
     *
     * @return self
     */
    public function setPattern($pattern)
    {
        $this->pattern = $pattern;

        return $this;
    }

    /**
     * Get the format attribute of the schema
     *
     * @return string
     */
    public function getFormat()
    {
        return $this->format;
    }

    /**
     * Set the format attribute of the schema
     *
     * @param string $format Format to set (e.g. date, date-time, timestamp, time, date-time-http)
     *
     * @return self
     */
    public function setFormat($format)
    {
        $this->format = $format;

        return $this;
    }
}
vendor/guzzle/guzzle/src/Guzzle/Service/Description/SchemaFormatter.php000064400000007751151327705700022451 0ustar00<?php

namespace Guzzle\Service\Description;

use Guzzle\Common\Exception\InvalidArgumentException;

/**
 * JSON Schema formatter class
 */
class SchemaFormatter
{
    /** @var \DateTimeZone */
    protected static $utcTimeZone;

    /**
     * Format a value by a registered format name
     *
     * @param string $format Registered format used to format the value
     * @param mixed  $value  Value being formatted
     *
     * @return mixed
     */
    public static function format($format, $value)
    {
        switch ($format) {
            case 'date-time':
                return self::formatDateTime($value);
            case 'date-time-http':
                return self::formatDateTimeHttp($value);
            case 'date':
                return self::formatDate($value);
            case 'time':
                return self::formatTime($value);
            case 'timestamp':
                return self::formatTimestamp($value);
            case 'boolean-string':
                return self::formatBooleanAsString($value);
            default:
                return $value;
        }
    }

    /**
     * Create a ISO 8601 (YYYY-MM-DDThh:mm:ssZ) formatted date time value in UTC time
     *
     * @param string|integer|\DateTime $value Date time value
     *
     * @return string
     */
    public static function formatDateTime($value)
    {
        return self::dateFormatter($value, 'Y-m-d\TH:i:s\Z');
    }

    /**
     * Create an HTTP date (RFC 1123 / RFC 822) formatted UTC date-time string
     *
     * @param string|integer|\DateTime $value Date time value
     *
     * @return string
     */
    public static function formatDateTimeHttp($value)
    {
        return self::dateFormatter($value, 'D, d M Y H:i:s \G\M\T');
    }

    /**
     * Create a YYYY-MM-DD formatted string
     *
     * @param string|integer|\DateTime $value Date time value
     *
     * @return string
     */
    public static function formatDate($value)
    {
        return self::dateFormatter($value, 'Y-m-d');
    }

    /**
     * Create a hh:mm:ss formatted string
     *
     * @param string|integer|\DateTime $value Date time value
     *
     * @return string
     */
    public static function formatTime($value)
    {
        return self::dateFormatter($value, 'H:i:s');
    }

    /**
     * Formats a boolean value as a string
     *
     * @param string|integer|bool $value Value to convert to a boolean 'true' / 'false' value
     *
     * @return string
     */
    public static function formatBooleanAsString($value)
    {
        return filter_var($value, FILTER_VALIDATE_BOOLEAN) ? 'true' : 'false';
    }

    /**
     * Return a UNIX timestamp in the UTC timezone
     *
     * @param string|integer|\DateTime $value Time value
     *
     * @return int
     */
    public static function formatTimestamp($value)
    {
        return (int) self::dateFormatter($value, 'U');
    }

    /**
     * Get a UTC DateTimeZone object
     *
     * @return \DateTimeZone
     */
    protected static function getUtcTimeZone()
    {
        // @codeCoverageIgnoreStart
        if (!self::$utcTimeZone) {
            self::$utcTimeZone = new \DateTimeZone('UTC');
        }
        // @codeCoverageIgnoreEnd

        return self::$utcTimeZone;
    }

    /**
     * Perform the actual DateTime formatting
     *
     * @param int|string|\DateTime $dateTime Date time value
     * @param string               $format   Format of the result
     *
     * @return string
     * @throws InvalidArgumentException
     */
    protected static function dateFormatter($dateTime, $format)
    {
        if (is_numeric($dateTime)) {
            return gmdate($format, (int) $dateTime);
        }

        if (is_string($dateTime)) {
            $dateTime = new \DateTime($dateTime);
        }

        if ($dateTime instanceof \DateTime) {
            return $dateTime->setTimezone(self::getUtcTimeZone())->format($format);
        }

        throw new InvalidArgumentException('Date/Time values must be either a string, integer, or DateTime object');
    }
}
vendor/guzzle/guzzle/src/Guzzle/Service/Description/SchemaValidator.php000064400000027152151327705700022430 0ustar00<?php

namespace Guzzle\Service\Description;

use Guzzle\Common\ToArrayInterface;

/**
 * Default parameter validator
 */
class SchemaValidator implements ValidatorInterface
{
    /** @var self Cache instance of the object */
    protected static $instance;

    /** @var bool Whether or not integers are converted to strings when an integer is received for a string input */
    protected $castIntegerToStringType;

    /** @var array Errors encountered while validating */
    protected $errors;

    /**
     * @return self
     * @codeCoverageIgnore
     */
    public static function getInstance()
    {
        if (!self::$instance) {
            self::$instance = new self();
        }

        return self::$instance;
    }

    /**
     * @param bool $castIntegerToStringType Set to true to convert integers into strings when a required type is a
     *                                      string and the input value is an integer. Defaults to true.
     */
    public function __construct($castIntegerToStringType = true)
    {
        $this->castIntegerToStringType = $castIntegerToStringType;
    }

    public function validate(Parameter $param, &$value)
    {
        $this->errors = array();
        $this->recursiveProcess($param, $value);

        if (empty($this->errors)) {
            return true;
        } else {
            sort($this->errors);
            return false;
        }
    }

    /**
     * Get the errors encountered while validating
     *
     * @return array
     */
    public function getErrors()
    {
        return $this->errors ?: array();
    }

    /**
     * Recursively validate a parameter
     *
     * @param Parameter $param  API parameter being validated
     * @param mixed     $value  Value to validate and validate. The value may change during this validate.
     * @param string    $path   Current validation path (used for error reporting)
     * @param int       $depth  Current depth in the validation validate
     *
     * @return bool Returns true if valid, or false if invalid
     */
    protected function recursiveProcess(Parameter $param, &$value, $path = '', $depth = 0)
    {
        // Update the value by adding default or static values
        $value = $param->getValue($value);

        $required = $param->getRequired();
        // if the value is null and the parameter is not required or is static, then skip any further recursion
        if ((null === $value && !$required) || $param->getStatic()) {
            return true;
        }

        $type = $param->getType();
        // Attempt to limit the number of times is_array is called by tracking if the value is an array
        $valueIsArray = is_array($value);
        // If a name is set then update the path so that validation messages are more helpful
        if ($name = $param->getName()) {
            $path .= "[{$name}]";
        }

        if ($type == 'object') {

            // Objects are either associative arrays, ToArrayInterface, or some other object
            if ($param->getInstanceOf()) {
                $instance = $param->getInstanceOf();
                if (!($value instanceof $instance)) {
                    $this->errors[] = "{$path} must be an instance of {$instance}";
                    return false;
                }
            }

            // Determine whether or not this "value" has properties and should be traversed
            $traverse = $temporaryValue = false;

            // Convert the value to an array
            if (!$valueIsArray && $value instanceof ToArrayInterface) {
                $value = $value->toArray();
            }

            if ($valueIsArray) {
                // Ensure that the array is associative and not numerically indexed
                if (isset($value[0])) {
                    $this->errors[] = "{$path} must be an array of properties. Got a numerically indexed array.";
                    return false;
                }
                $traverse = true;
            } elseif ($value === null) {
                // Attempt to let the contents be built up by default values if possible
                $value = array();
                $temporaryValue = $valueIsArray = $traverse = true;
            }

            if ($traverse) {

                if ($properties = $param->getProperties()) {
                    // if properties were found, the validate each property of the value
                    foreach ($properties as $property) {
                        $name = $property->getName();
                        if (isset($value[$name])) {
                            $this->recursiveProcess($property, $value[$name], $path, $depth + 1);
                        } else {
                            $current = null;
                            $this->recursiveProcess($property, $current, $path, $depth + 1);
                            // Only set the value if it was populated with something
                            if (null !== $current) {
                                $value[$name] = $current;
                            }
                        }
                    }
                }

                $additional = $param->getAdditionalProperties();
                if ($additional !== true) {
                    // If additional properties were found, then validate each against the additionalProperties attr.
                    $keys = array_keys($value);
                    // Determine the keys that were specified that were not listed in the properties of the schema
                    $diff = array_diff($keys, array_keys($properties));
                    if (!empty($diff)) {
                        // Determine which keys are not in the properties
                        if ($additional instanceOf Parameter) {
                            foreach ($diff as $key) {
                                $this->recursiveProcess($additional, $value[$key], "{$path}[{$key}]", $depth);
                            }
                        } else {
                            // if additionalProperties is set to false and there are additionalProperties in the values, then fail
                            foreach ($diff as $prop) {
                                $this->errors[] = sprintf('%s[%s] is not an allowed property', $path, $prop);
                            }
                        }
                    }
                }

                // A temporary value will be used to traverse elements that have no corresponding input value.
                // This allows nested required parameters with default values to bubble up into the input.
                // Here we check if we used a temp value and nothing bubbled up, then we need to remote the value.
                if ($temporaryValue && empty($value)) {
                    $value = null;
                    $valueIsArray = false;
                }
            }

        } elseif ($type == 'array' && $valueIsArray && $param->getItems()) {
            foreach ($value as $i => &$item) {
                // Validate each item in an array against the items attribute of the schema
                $this->recursiveProcess($param->getItems(), $item, $path . "[{$i}]", $depth + 1);
            }
        }

        // If the value is required and the type is not null, then there is an error if the value is not set
        if ($required && $value === null && $type != 'null') {
            $message = "{$path} is " . ($param->getType() ? ('a required ' . implode(' or ', (array) $param->getType())) : 'required');
            if ($param->getDescription()) {
                $message .= ': ' . $param->getDescription();
            }
            $this->errors[] = $message;
            return false;
        }

        // Validate that the type is correct. If the type is string but an integer was passed, the class can be
        // instructed to cast the integer to a string to pass validation. This is the default behavior.
        if ($type && (!$type = $this->determineType($type, $value))) {
            if ($this->castIntegerToStringType && $param->getType() == 'string' && is_integer($value)) {
                $value = (string) $value;
            } else {
                $this->errors[] = "{$path} must be of type " . implode(' or ', (array) $param->getType());
            }
        }

        // Perform type specific validation for strings, arrays, and integers
        if ($type == 'string') {

            // Strings can have enums which are a list of predefined values
            if (($enum = $param->getEnum()) && !in_array($value, $enum)) {
                $this->errors[] = "{$path} must be one of " . implode(' or ', array_map(function ($s) {
                    return '"' . addslashes($s) . '"';
                }, $enum));
            }
            // Strings can have a regex pattern that the value must match
            if (($pattern  = $param->getPattern()) && !preg_match($pattern, $value)) {
                $this->errors[] = "{$path} must match the following regular expression: {$pattern}";
            }

            $strLen = null;
            if ($min = $param->getMinLength()) {
                $strLen = strlen($value);
                if ($strLen < $min) {
                    $this->errors[] = "{$path} length must be greater than or equal to {$min}";
                }
            }
            if ($max = $param->getMaxLength()) {
                if (($strLen ?: strlen($value)) > $max) {
                    $this->errors[] = "{$path} length must be less than or equal to {$max}";
                }
            }

        } elseif ($type == 'array') {

            $size = null;
            if ($min = $param->getMinItems()) {
                $size = count($value);
                if ($size < $min) {
                    $this->errors[] = "{$path} must contain {$min} or more elements";
                }
            }
            if ($max = $param->getMaxItems()) {
                if (($size ?: count($value)) > $max) {
                    $this->errors[] = "{$path} must contain {$max} or fewer elements";
                }
            }

        } elseif ($type == 'integer' || $type == 'number' || $type == 'numeric') {
            if (($min = $param->getMinimum()) && $value < $min) {
                $this->errors[] = "{$path} must be greater than or equal to {$min}";
            }
            if (($max = $param->getMaximum()) && $value > $max) {
                $this->errors[] = "{$path} must be less than or equal to {$max}";
            }
        }

        return empty($this->errors);
    }

    /**
     * From the allowable types, determine the type that the variable matches
     *
     * @param string $type  Parameter type
     * @param mixed  $value Value to determine the type
     *
     * @return string|bool Returns the matching type on
     */
    protected function determineType($type, $value)
    {
        foreach ((array) $type as $t) {
            if ($t == 'string' && (is_string($value) || (is_object($value) && method_exists($value, '__toString')))) {
                return 'string';
            } elseif ($t == 'object' && (is_array($value) || is_object($value))) {
                return 'object';
            } elseif ($t == 'array' && is_array($value)) {
                return 'array';
            } elseif ($t == 'integer' && is_integer($value)) {
                return 'integer';
            } elseif ($t == 'boolean' && is_bool($value)) {
                return 'boolean';
            } elseif ($t == 'number' && is_numeric($value)) {
                return 'number';
            } elseif ($t == 'numeric' && is_numeric($value)) {
                return 'numeric';
            } elseif ($t == 'null' && !$value) {
                return 'null';
            } elseif ($t == 'any') {
                return 'any';
            }
        }

        return false;
    }
}
vendor/guzzle/guzzle/src/Guzzle/Service/Description/ServiceDescription.php000064400000016214151327705700023163 0ustar00<?php

namespace Guzzle\Service\Description;

use Guzzle\Common\Exception\InvalidArgumentException;
use Guzzle\Common\ToArrayInterface;

/**
 * A ServiceDescription stores service information based on a service document
 */
class ServiceDescription implements ServiceDescriptionInterface, ToArrayInterface
{
    /** @var array Array of {@see OperationInterface} objects */
    protected $operations = array();

    /** @var array Array of API models */
    protected $models = array();

    /** @var string Name of the API */
    protected $name;

    /** @var string API version */
    protected $apiVersion;

    /** @var string Summary of the API */
    protected $description;

    /** @var array Any extra API data */
    protected $extraData = array();

    /** @var ServiceDescriptionLoader Factory used in factory method */
    protected static $descriptionLoader;

    /** @var string baseUrl/basePath */
    protected $baseUrl;

    /**
     * {@inheritdoc}
     * @param string|array $config  File to build or array of operation information
     * @param array        $options Service description factory options
     *
     * @return self
     */
    public static function factory($config, array $options = array())
    {
        // @codeCoverageIgnoreStart
        if (!self::$descriptionLoader) {
            self::$descriptionLoader = new ServiceDescriptionLoader();
        }
        // @codeCoverageIgnoreEnd

        return self::$descriptionLoader->load($config, $options);
    }

    /**
     * @param array $config Array of configuration data
     */
    public function __construct(array $config = array())
    {
        $this->fromArray($config);
    }

    public function serialize()
    {
        return json_encode($this->toArray());
    }

    public function unserialize($json)
    {
        $this->operations = array();
        $this->fromArray(json_decode($json, true));
    }

    public function toArray()
    {
        $result = array(
            'name'        => $this->name,
            'apiVersion'  => $this->apiVersion,
            'baseUrl'     => $this->baseUrl,
            'description' => $this->description
        ) + $this->extraData;
        $result['operations'] = array();
        foreach ($this->getOperations() as $name => $operation) {
            $result['operations'][$operation->getName() ?: $name] = $operation->toArray();
        }
        if (!empty($this->models)) {
            $result['models'] = array();
            foreach ($this->models as $id => $model) {
                $result['models'][$id] = $model instanceof Parameter ? $model->toArray(): $model;
            }
        }

        return array_filter($result);
    }

    public function getBaseUrl()
    {
        return $this->baseUrl;
    }

    /**
     * Set the baseUrl of the description
     *
     * @param string $baseUrl Base URL of each operation
     *
     * @return self
     */
    public function setBaseUrl($baseUrl)
    {
        $this->baseUrl = $baseUrl;

        return $this;
    }

    public function getOperations()
    {
        foreach (array_keys($this->operations) as $name) {
            $this->getOperation($name);
        }

        return $this->operations;
    }

    public function hasOperation($name)
    {
        return isset($this->operations[$name]);
    }

    public function getOperation($name)
    {
        // Lazily retrieve and build operations
        if (!isset($this->operations[$name])) {
            return null;
        }

        if (!($this->operations[$name] instanceof Operation)) {
            $this->operations[$name] = new Operation($this->operations[$name], $this);
        }

        return $this->operations[$name];
    }

    /**
     * Add a operation to the service description
     *
     * @param OperationInterface $operation Operation to add
     *
     * @return self
     */
    public function addOperation(OperationInterface $operation)
    {
        $this->operations[$operation->getName()] = $operation->setServiceDescription($this);

        return $this;
    }

    public function getModel($id)
    {
        if (!isset($this->models[$id])) {
            return null;
        }

        if (!($this->models[$id] instanceof Parameter)) {
            $this->models[$id] = new Parameter($this->models[$id] + array('name' => $id), $this);
        }

        return $this->models[$id];
    }

    public function getModels()
    {
        // Ensure all models are converted into parameter objects
        foreach (array_keys($this->models) as $id) {
            $this->getModel($id);
        }

        return $this->models;
    }

    public function hasModel($id)
    {
        return isset($this->models[$id]);
    }

    /**
     * Add a model to the service description
     *
     * @param Parameter $model Model to add
     *
     * @return self
     */
    public function addModel(Parameter $model)
    {
        $this->models[$model->getName()] = $model;

        return $this;
    }

    public function getApiVersion()
    {
        return $this->apiVersion;
    }

    public function getName()
    {
        return $this->name;
    }

    public function getDescription()
    {
        return $this->description;
    }

    public function getData($key)
    {
        return isset($this->extraData[$key]) ? $this->extraData[$key] : null;
    }

    public function setData($key, $value)
    {
        $this->extraData[$key] = $value;

        return $this;
    }

    /**
     * Initialize the state from an array
     *
     * @param array $config Configuration data
     * @throws InvalidArgumentException
     */
    protected function fromArray(array $config)
    {
        // Keep a list of default keys used in service descriptions that is later used to determine extra data keys
        static $defaultKeys = array('name', 'models', 'apiVersion', 'baseUrl', 'description');
        // Pull in the default configuration values
        foreach ($defaultKeys as $key) {
            if (isset($config[$key])) {
                $this->{$key} = $config[$key];
            }
        }

        // Account for the Swagger name for Guzzle's baseUrl
        if (isset($config['basePath'])) {
            $this->baseUrl = $config['basePath'];
        }

        // Ensure that the models and operations properties are always arrays
        $this->models = (array) $this->models;
        $this->operations = (array) $this->operations;

        // We want to add operations differently than adding the other properties
        $defaultKeys[] = 'operations';

        // Create operations for each operation
        if (isset($config['operations'])) {
            foreach ($config['operations'] as $name => $operation) {
                if (!($operation instanceof Operation) && !is_array($operation)) {
                    throw new InvalidArgumentException('Invalid operation in service description: '
                        . gettype($operation));
                }
                $this->operations[$name] = $operation;
            }
        }

        // Get all of the additional properties of the service description and store them in a data array
        foreach (array_diff(array_keys($config), $defaultKeys) as $key) {
            $this->extraData[$key] = $config[$key];
        }
    }
}
vendor/guzzle/guzzle/src/Guzzle/Service/Description/ServiceDescriptionLoader.php000064400000004747151327705700024322 0ustar00<?php

namespace Guzzle\Service\Description;

use Guzzle\Service\AbstractConfigLoader;
use Guzzle\Service\Exception\DescriptionBuilderException;

/**
 * Loader for service descriptions
 */
class ServiceDescriptionLoader extends AbstractConfigLoader
{
    protected function build($config, array $options)
    {
        $operations = array();
        if (!empty($config['operations'])) {
            foreach ($config['operations'] as $name => $op) {
                $name = $op['name'] = isset($op['name']) ? $op['name'] : $name;
                // Extend other operations
                if (!empty($op['extends'])) {
                    $this->resolveExtension($name, $op, $operations);
                }
                $op['parameters'] = isset($op['parameters']) ? $op['parameters'] : array();
                $operations[$name] = $op;
            }
        }

        return new ServiceDescription(array(
            'apiVersion'  => isset($config['apiVersion']) ? $config['apiVersion'] : null,
            'baseUrl'     => isset($config['baseUrl']) ? $config['baseUrl'] : null,
            'description' => isset($config['description']) ? $config['description'] : null,
            'operations'  => $operations,
            'models'      => isset($config['models']) ? $config['models'] : null
        ) + $config);
    }

    /**
     * @param string $name       Name of the operation
     * @param array  $op         Operation value array
     * @param array  $operations Currently loaded operations
     * @throws DescriptionBuilderException when extending a non-existent operation
     */
    protected function resolveExtension($name, array &$op, array &$operations)
    {
        $resolved = array();
        $original = empty($op['parameters']) ? false: $op['parameters'];
        $hasClass = !empty($op['class']);
        foreach ((array) $op['extends'] as $extendedCommand) {
            if (empty($operations[$extendedCommand])) {
                throw new DescriptionBuilderException("{$name} extends missing operation {$extendedCommand}");
            }
            $toArray = $operations[$extendedCommand];
            $resolved = empty($resolved)
                ? $toArray['parameters']
                : array_merge($resolved, $toArray['parameters']);

            $op = $op + $toArray;
            if (!$hasClass && isset($toArray['class'])) {
                $op['class'] = $toArray['class'];
            }
        }
        $op['parameters'] = $original ? array_merge($resolved, $original) : $resolved;
    }
}
vendor/guzzle/guzzle/src/Guzzle/Service/Description/ServiceDescriptionInterface.php000064400000004261151327705700025003 0ustar00<?php

namespace Guzzle\Service\Description;

/**
 * A ServiceDescription stores service information based on a service document
 */
interface ServiceDescriptionInterface extends \Serializable
{
    /**
     * Get the basePath/baseUrl of the description
     *
     * @return string
     */
    public function getBaseUrl();

    /**
     * Get the API operations of the service
     *
     * @return array Returns an array of {@see OperationInterface} objects
     */
    public function getOperations();

    /**
     * Check if the service has an operation by name
     *
     * @param string $name Name of the operation to check
     *
     * @return bool
     */
    public function hasOperation($name);

    /**
     * Get an API operation by name
     *
     * @param string $name Name of the command
     *
     * @return OperationInterface|null
     */
    public function getOperation($name);

    /**
     * Get a specific model from the description
     *
     * @param string $id ID of the model
     *
     * @return Parameter|null
     */
    public function getModel($id);

    /**
     * Get all service description models
     *
     * @return array
     */
    public function getModels();

    /**
     * Check if the description has a specific model by name
     *
     * @param string $id ID of the model
     *
     * @return bool
     */
    public function hasModel($id);

    /**
     * Get the API version of the service
     *
     * @return string
     */
    public function getApiVersion();

    /**
     * Get the name of the API
     *
     * @return string
     */
    public function getName();

    /**
     * Get a summary of the purpose of the API
     *
     * @return string
     */
    public function getDescription();

    /**
     * Get arbitrary data from the service description that is not part of the Guzzle spec
     *
     * @param string $key Data key to retrieve
     *
     * @return null|mixed
     */
    public function getData($key);

    /**
     * Set arbitrary data on the service description
     *
     * @param string $key   Data key to set
     * @param mixed  $value Value to set
     *
     * @return self
     */
    public function setData($key, $value);
}
vendor/guzzle/guzzle/src/Guzzle/Service/Description/Operation.php000064400000036566151327705700021333 0ustar00<?php

namespace Guzzle\Service\Description;

use Guzzle\Common\Exception\InvalidArgumentException;

/**
 * Data object holding the information of an API command
 */
class Operation implements OperationInterface
{
    /** @var string Default command class to use when none is specified */
    const DEFAULT_COMMAND_CLASS = 'Guzzle\\Service\\Command\\OperationCommand';

    /** @var array Hashmap of properties that can be specified. Represented as a hash to speed up constructor. */
    protected static $properties = array(
        'name' => true, 'httpMethod' => true, 'uri' => true, 'class' => true, 'responseClass' => true,
        'responseType' => true, 'responseNotes' => true, 'notes' => true, 'summary' => true, 'documentationUrl' => true,
        'deprecated' => true, 'data' => true, 'parameters' => true, 'additionalParameters' => true,
        'errorResponses' => true
    );

    /** @var array Parameters */
    protected $parameters = array();

    /** @var Parameter Additional parameters schema */
    protected $additionalParameters;

    /** @var string Name of the command */
    protected $name;

    /** @var string HTTP method */
    protected $httpMethod;

    /** @var string This is a short summary of what the operation does */
    protected $summary;

    /** @var string A longer text field to explain the behavior of the operation. */
    protected $notes;

    /** @var string Reference URL providing more information about the operation */
    protected $documentationUrl;

    /** @var string HTTP URI of the command */
    protected $uri;

    /** @var string Class of the command object */
    protected $class;

    /** @var string This is what is returned from the method */
    protected $responseClass;

    /** @var string Type information about the response */
    protected $responseType;

    /** @var string Information about the response returned by the operation */
    protected $responseNotes;

    /** @var bool Whether or not the command is deprecated */
    protected $deprecated;

    /** @var array Array of errors that could occur when running the command */
    protected $errorResponses;

    /** @var ServiceDescriptionInterface */
    protected $description;

    /** @var array Extra operation information */
    protected $data;

    /**
     * Builds an Operation object using an array of configuration data:
     * - name:               (string) Name of the command
     * - httpMethod:         (string) HTTP method of the operation
     * - uri:                (string) URI template that can create a relative or absolute URL
     * - class:              (string) Concrete class that implements this command
     * - parameters:         (array) Associative array of parameters for the command. {@see Parameter} for information.
     * - summary:            (string) This is a short summary of what the operation does
     * - notes:              (string) A longer text field to explain the behavior of the operation.
     * - documentationUrl:   (string) Reference URL providing more information about the operation
     * - responseClass:      (string) This is what is returned from the method. Can be a primitive, PSR-0 compliant
     *                       class name, or model.
     * - responseNotes:      (string) Information about the response returned by the operation
     * - responseType:       (string) One of 'primitive', 'class', 'model', or 'documentation'. If not specified, this
     *                       value will be automatically inferred based on whether or not there is a model matching the
     *                       name, if a matching PSR-0 compliant class name is found, or set to 'primitive' by default.
     * - deprecated:         (bool) Set to true if this is a deprecated command
     * - errorResponses:     (array) Errors that could occur when executing the command. Array of hashes, each with a
     *                       'code' (the HTTP response code), 'reason' (response reason phrase or description of the
     *                       error), and 'class' (a custom exception class that would be thrown if the error is
     *                       encountered).
     * - data:               (array) Any extra data that might be used to help build or serialize the operation
     * - additionalParameters: (null|array) Parameter schema to use when an option is passed to the operation that is
     *                                      not in the schema
     *
     * @param array                       $config      Array of configuration data
     * @param ServiceDescriptionInterface $description Service description used to resolve models if $ref tags are found
     */
    public function __construct(array $config = array(), ServiceDescriptionInterface $description = null)
    {
        $this->description = $description;

        // Get the intersection of the available properties and properties set on the operation
        foreach (array_intersect_key($config, self::$properties) as $key => $value) {
            $this->{$key} = $value;
        }

        $this->class = $this->class ?: self::DEFAULT_COMMAND_CLASS;
        $this->deprecated = (bool) $this->deprecated;
        $this->errorResponses = $this->errorResponses ?: array();
        $this->data = $this->data ?: array();

        if (!$this->responseClass) {
            $this->responseClass = 'array';
            $this->responseType = 'primitive';
        } elseif ($this->responseType) {
            // Set the response type to perform validation
            $this->setResponseType($this->responseType);
        } else {
            // A response class was set and no response type was set, so guess what the type is
            $this->inferResponseType();
        }

        // Parameters need special handling when adding
        if ($this->parameters) {
            foreach ($this->parameters as $name => $param) {
                if ($param instanceof Parameter) {
                    $param->setName($name)->setParent($this);
                } elseif (is_array($param)) {
                    $param['name'] = $name;
                    $this->addParam(new Parameter($param, $this->description));
                }
            }
        }

        if ($this->additionalParameters) {
            if ($this->additionalParameters instanceof Parameter) {
                $this->additionalParameters->setParent($this);
            } elseif (is_array($this->additionalParameters)) {
                $this->setadditionalParameters(new Parameter($this->additionalParameters, $this->description));
            }
        }
    }

    public function toArray()
    {
        $result = array();
        // Grab valid properties and filter out values that weren't set
        foreach (array_keys(self::$properties) as $check) {
            if ($value = $this->{$check}) {
                $result[$check] = $value;
            }
        }
        // Remove the name property
        unset($result['name']);
        // Parameters need to be converted to arrays
        $result['parameters'] = array();
        foreach ($this->parameters as $key => $param) {
            $result['parameters'][$key] = $param->toArray();
        }
        // Additional parameters need to be cast to an array
        if ($this->additionalParameters instanceof Parameter) {
            $result['additionalParameters'] = $this->additionalParameters->toArray();
        }

        return $result;
    }

    public function getServiceDescription()
    {
        return $this->description;
    }

    public function setServiceDescription(ServiceDescriptionInterface $description)
    {
        $this->description = $description;

        return $this;
    }

    public function getParams()
    {
        return $this->parameters;
    }

    public function getParamNames()
    {
        return array_keys($this->parameters);
    }

    public function hasParam($name)
    {
        return isset($this->parameters[$name]);
    }

    public function getParam($param)
    {
        return isset($this->parameters[$param]) ? $this->parameters[$param] : null;
    }

    /**
     * Add a parameter to the command
     *
     * @param Parameter $param Parameter to add
     *
     * @return self
     */
    public function addParam(Parameter $param)
    {
        $this->parameters[$param->getName()] = $param;
        $param->setParent($this);

        return $this;
    }

    /**
     * Remove a parameter from the command
     *
     * @param string $name Name of the parameter to remove
     *
     * @return self
     */
    public function removeParam($name)
    {
        unset($this->parameters[$name]);

        return $this;
    }

    public function getHttpMethod()
    {
        return $this->httpMethod;
    }

    /**
     * Set the HTTP method of the command
     *
     * @param string $httpMethod Method to set
     *
     * @return self
     */
    public function setHttpMethod($httpMethod)
    {
        $this->httpMethod = $httpMethod;

        return $this;
    }

    public function getClass()
    {
        return $this->class;
    }

    /**
     * Set the concrete class of the command
     *
     * @param string $className Concrete class name
     *
     * @return self
     */
    public function setClass($className)
    {
        $this->class = $className;

        return $this;
    }

    public function getName()
    {
        return $this->name;
    }

    /**
     * Set the name of the command
     *
     * @param string $name Name of the command
     *
     * @return self
     */
    public function setName($name)
    {
        $this->name = $name;

        return $this;
    }

    public function getSummary()
    {
        return $this->summary;
    }

    /**
     * Set a short summary of what the operation does
     *
     * @param string $summary Short summary of the operation
     *
     * @return self
     */
    public function setSummary($summary)
    {
        $this->summary = $summary;

        return $this;
    }

    public function getNotes()
    {
        return $this->notes;
    }

    /**
     * Set a longer text field to explain the behavior of the operation.
     *
     * @param string $notes Notes on the operation
     *
     * @return self
     */
    public function setNotes($notes)
    {
        $this->notes = $notes;

        return $this;
    }

    public function getDocumentationUrl()
    {
        return $this->documentationUrl;
    }

    /**
     * Set the URL pointing to additional documentation on the command
     *
     * @param string $docUrl Documentation URL
     *
     * @return self
     */
    public function setDocumentationUrl($docUrl)
    {
        $this->documentationUrl = $docUrl;

        return $this;
    }

    public function getResponseClass()
    {
        return $this->responseClass;
    }

    /**
     * Set what is returned from the method. Can be a primitive, class name, or model. For example: 'array',
     * 'Guzzle\\Foo\\Baz', or 'MyModelName' (to reference a model by ID).
     *
     * @param string $responseClass Type of response
     *
     * @return self
     */
    public function setResponseClass($responseClass)
    {
        $this->responseClass = $responseClass;
        $this->inferResponseType();

        return $this;
    }

    public function getResponseType()
    {
        return $this->responseType;
    }

    /**
     * Set qualifying information about the responseClass. One of 'primitive', 'class', 'model', or 'documentation'
     *
     * @param string $responseType Response type information
     *
     * @return self
     * @throws InvalidArgumentException
     */
    public function setResponseType($responseType)
    {
        static $types = array(
            self::TYPE_PRIMITIVE => true,
            self::TYPE_CLASS => true,
            self::TYPE_MODEL => true,
            self::TYPE_DOCUMENTATION => true
        );
        if (!isset($types[$responseType])) {
            throw new InvalidArgumentException('responseType must be one of ' . implode(', ', array_keys($types)));
        }

        $this->responseType = $responseType;

        return $this;
    }

    public function getResponseNotes()
    {
        return $this->responseNotes;
    }

    /**
     * Set notes about the response of the operation
     *
     * @param string $notes Response notes
     *
     * @return self
     */
    public function setResponseNotes($notes)
    {
        $this->responseNotes = $notes;

        return $this;
    }

    public function getDeprecated()
    {
        return $this->deprecated;
    }

    /**
     * Set whether or not the command is deprecated
     *
     * @param bool $isDeprecated Set to true to mark as deprecated
     *
     * @return self
     */
    public function setDeprecated($isDeprecated)
    {
        $this->deprecated = $isDeprecated;

        return $this;
    }

    public function getUri()
    {
        return $this->uri;
    }

    /**
     * Set the URI template of the command
     *
     * @param string $uri URI template to set
     *
     * @return self
     */
    public function setUri($uri)
    {
        $this->uri = $uri;

        return $this;
    }

    public function getErrorResponses()
    {
        return $this->errorResponses;
    }

    /**
     * Add an error to the command
     *
     * @param string $code   HTTP response code
     * @param string $reason HTTP response reason phrase or information about the error
     * @param string $class  Exception class associated with the error
     *
     * @return self
     */
    public function addErrorResponse($code, $reason, $class)
    {
        $this->errorResponses[] = array('code' => $code, 'reason' => $reason, 'class' => $class);

        return $this;
    }

    /**
     * Set all of the error responses of the operation
     *
     * @param array $errorResponses Hash of error name to a hash containing a code, reason, class
     *
     * @return self
     */
    public function setErrorResponses(array $errorResponses)
    {
        $this->errorResponses = $errorResponses;

        return $this;
    }

    public function getData($name)
    {
        return isset($this->data[$name]) ? $this->data[$name] : null;
    }

    /**
     * Set a particular data point on the operation
     *
     * @param string $name  Name of the data value
     * @param mixed  $value Value to set
     *
     * @return self
     */
    public function setData($name, $value)
    {
        $this->data[$name] = $value;

        return $this;
    }

    /**
     * Get the additionalParameters of the operation
     *
     * @return Parameter|null
     */
    public function getAdditionalParameters()
    {
        return $this->additionalParameters;
    }

    /**
     * Set the additionalParameters of the operation
     *
     * @param Parameter|null $parameter Parameter to set
     *
     * @return self
     */
    public function setAdditionalParameters($parameter)
    {
        if ($this->additionalParameters = $parameter) {
            $this->additionalParameters->setParent($this);
        }

        return $this;
    }

    /**
     * Infer the response type from the responseClass value
     */
    protected function inferResponseType()
    {
        static $primitives = array('array' => 1, 'boolean' => 1, 'string' => 1, 'integer' => 1, '' => 1);
        if (isset($primitives[$this->responseClass])) {
            $this->responseType = self::TYPE_PRIMITIVE;
        } elseif ($this->description && $this->description->hasModel($this->responseClass)) {
            $this->responseType = self::TYPE_MODEL;
        } else {
            $this->responseType = self::TYPE_CLASS;
        }
    }
}
vendor/guzzle/guzzle/src/Guzzle/Service/AbstractConfigLoader.php000064400000013561151327705700021116 0ustar00<?php

namespace Guzzle\Service;

use Guzzle\Common\Exception\InvalidArgumentException;
use Guzzle\Common\Exception\RuntimeException;

/**
 * Abstract config loader
 */
abstract class AbstractConfigLoader implements ConfigLoaderInterface
{
    /** @var array Array of aliases for actual filenames */
    protected $aliases = array();

    /** @var array Hash of previously loaded filenames */
    protected $loadedFiles = array();

    /** @var array JSON error code mappings */
    protected static $jsonErrors = array(
        JSON_ERROR_NONE => 'JSON_ERROR_NONE - No errors',
        JSON_ERROR_DEPTH => 'JSON_ERROR_DEPTH - Maximum stack depth exceeded',
        JSON_ERROR_STATE_MISMATCH => 'JSON_ERROR_STATE_MISMATCH - Underflow or the modes mismatch',
        JSON_ERROR_CTRL_CHAR => 'JSON_ERROR_CTRL_CHAR - Unexpected control character found',
        JSON_ERROR_SYNTAX => 'JSON_ERROR_SYNTAX - Syntax error, malformed JSON',
        JSON_ERROR_UTF8 => 'JSON_ERROR_UTF8 - Malformed UTF-8 characters, possibly incorrectly encoded'
    );

    public function load($config, array $options = array())
    {
        // Reset the array of loaded files because this is a new config
        $this->loadedFiles = array();

        if (is_string($config)) {
            $config = $this->loadFile($config);
        } elseif (!is_array($config)) {
            throw new InvalidArgumentException('Unknown type passed to configuration loader: ' . gettype($config));
        } else {
            $this->mergeIncludes($config);
        }

        return $this->build($config, $options);
    }

    /**
     * Add an include alias to the loader
     *
     * @param string $filename Filename to alias (e.g. _foo)
     * @param string $alias    Actual file to use (e.g. /path/to/foo.json)
     *
     * @return self
     */
    public function addAlias($filename, $alias)
    {
        $this->aliases[$filename] = $alias;

        return $this;
    }

    /**
     * Remove an alias from the loader
     *
     * @param string $alias Alias to remove
     *
     * @return self
     */
    public function removeAlias($alias)
    {
        unset($this->aliases[$alias]);

        return $this;
    }

    /**
     * Perform the parsing of a config file and create the end result
     *
     * @param array $config  Configuration data
     * @param array $options Options to use when building
     *
     * @return mixed
     */
    protected abstract function build($config, array $options);

    /**
     * Load a configuration file (can load JSON or PHP files that return an array when included)
     *
     * @param string $filename File to load
     *
     * @return array
     * @throws InvalidArgumentException
     * @throws RuntimeException when the JSON cannot be parsed
     */
    protected function loadFile($filename)
    {
        if (isset($this->aliases[$filename])) {
            $filename = $this->aliases[$filename];
        }

        switch (pathinfo($filename, PATHINFO_EXTENSION)) {
            case 'js':
            case 'json':
                $level = error_reporting(0);
                $json = file_get_contents($filename);
                error_reporting($level);

                if ($json === false) {
                    $err = error_get_last();
                    throw new InvalidArgumentException("Unable to open {$filename}: " . $err['message']);
                }

                $config = json_decode($json, true);
                // Throw an exception if there was an error loading the file
                if ($error = json_last_error()) {
                    $message = isset(self::$jsonErrors[$error]) ? self::$jsonErrors[$error] : 'Unknown error';
                    throw new RuntimeException("Error loading JSON data from {$filename}: ({$error}) - {$message}");
                }
                break;
            case 'php':
                if (!is_readable($filename)) {
                    throw new InvalidArgumentException("Unable to open {$filename} for reading");
                }
                $config = require $filename;
                if (!is_array($config)) {
                    throw new InvalidArgumentException('PHP files must return an array of configuration data');
                }
                break;
            default:
                throw new InvalidArgumentException('Unknown file extension: ' . $filename);
        }

        // Keep track of this file being loaded to prevent infinite recursion
        $this->loadedFiles[$filename] = true;

        // Merge include files into the configuration array
        $this->mergeIncludes($config, dirname($filename));

        return $config;
    }

    /**
     * Merges in all include files
     *
     * @param array  $config   Config data that contains includes
     * @param string $basePath Base path to use when a relative path is encountered
     *
     * @return array Returns the merged and included data
     */
    protected function mergeIncludes(&$config, $basePath = null)
    {
        if (!empty($config['includes'])) {
            foreach ($config['includes'] as &$path) {
                // Account for relative paths
                if ($path[0] != DIRECTORY_SEPARATOR && !isset($this->aliases[$path]) && $basePath) {
                    $path = "{$basePath}/{$path}";
                }
                // Don't load the same files more than once
                if (!isset($this->loadedFiles[$path])) {
                    $this->loadedFiles[$path] = true;
                    $config = $this->mergeData($this->loadFile($path), $config);
                }
            }
        }
    }

    /**
     * Default implementation for merging two arrays of data (uses array_merge_recursive)
     *
     * @param array $a Original data
     * @param array $b Data to merge into the original and overwrite existing values
     *
     * @return array
     */
    protected function mergeData(array $a, array $b)
    {
        return array_merge_recursive($a, $b);
    }
}
vendor/guzzle/guzzle/src/Guzzle/Service/Client.php000064400000022427151327705700016315 0ustar00<?php

namespace Guzzle\Service;

use Guzzle\Common\Collection;
use Guzzle\Common\Exception\InvalidArgumentException;
use Guzzle\Common\Exception\BadMethodCallException;
use Guzzle\Common\Version;
use Guzzle\Inflection\InflectorInterface;
use Guzzle\Inflection\Inflector;
use Guzzle\Http\Client as HttpClient;
use Guzzle\Http\Exception\MultiTransferException;
use Guzzle\Service\Exception\CommandTransferException;
use Guzzle\Http\Message\RequestInterface;
use Guzzle\Service\Command\CommandInterface;
use Guzzle\Service\Command\Factory\CompositeFactory;
use Guzzle\Service\Command\Factory\FactoryInterface as CommandFactoryInterface;
use Guzzle\Service\Resource\ResourceIteratorClassFactory;
use Guzzle\Service\Resource\ResourceIteratorFactoryInterface;
use Guzzle\Service\Description\ServiceDescriptionInterface;

/**
 * Client object for executing commands on a web service.
 */
class Client extends HttpClient implements ClientInterface
{
    const COMMAND_PARAMS = 'command.params';

    /** @var ServiceDescriptionInterface Description of the service and possible commands */
    protected $serviceDescription;

    /** @var CommandFactoryInterface */
    protected $commandFactory;

    /** @var ResourceIteratorFactoryInterface */
    protected $resourceIteratorFactory;

    /** @var InflectorInterface Inflector associated with the service/client */
    protected $inflector;

    /**
     * Basic factory method to create a new client. Extend this method in subclasses to build more complex clients.
     *
     * @param array|Collection $config Configuration data
     *
     * @return Client
     */
    public static function factory($config = array())
    {
        return new static(isset($config['base_url']) ? $config['base_url'] : null, $config);
    }

    public static function getAllEvents()
    {
        return array_merge(HttpClient::getAllEvents(), array(
            'client.command.create',
            'command.before_prepare',
            'command.after_prepare',
            'command.before_send',
            'command.after_send',
            'command.parse_response'
        ));
    }

    /**
     * Magic method used to retrieve a command
     *
     * @param string $method Name of the command object to instantiate
     * @param array  $args   Arguments to pass to the command
     *
     * @return mixed Returns the result of the command
     * @throws BadMethodCallException when a command is not found
     */
    public function __call($method, $args)
    {
        return $this->getCommand($method, isset($args[0]) ? $args[0] : array())->getResult();
    }

    public function getCommand($name, array $args = array())
    {
        // Add global client options to the command
        if ($options = $this->getConfig(self::COMMAND_PARAMS)) {
            $args += $options;
        }

        if (!($command = $this->getCommandFactory()->factory($name, $args))) {
            throw new InvalidArgumentException("Command was not found matching {$name}");
        }

        $command->setClient($this);
        $this->dispatch('client.command.create', array('client' => $this, 'command' => $command));

        return $command;
    }

    /**
     * Set the command factory used to create commands by name
     *
     * @param CommandFactoryInterface $factory Command factory
     *
     * @return self
     */
    public function setCommandFactory(CommandFactoryInterface $factory)
    {
        $this->commandFactory = $factory;

        return $this;
    }

    /**
     * Set the resource iterator factory associated with the client
     *
     * @param ResourceIteratorFactoryInterface $factory Resource iterator factory
     *
     * @return self
     */
    public function setResourceIteratorFactory(ResourceIteratorFactoryInterface $factory)
    {
        $this->resourceIteratorFactory = $factory;

        return $this;
    }

    public function getIterator($command, array $commandOptions = null, array $iteratorOptions = array())
    {
        if (!($command instanceof CommandInterface)) {
            $command = $this->getCommand($command, $commandOptions ?: array());
        }

        return $this->getResourceIteratorFactory()->build($command, $iteratorOptions);
    }

    public function execute($command)
    {
        if ($command instanceof CommandInterface) {
            $this->send($this->prepareCommand($command));
            $this->dispatch('command.after_send', array('command' => $command));
            return $command->getResult();
        } elseif (is_array($command) || $command instanceof \Traversable) {
            return $this->executeMultiple($command);
        } else {
            throw new InvalidArgumentException('Command must be a command or array of commands');
        }
    }

    public function setDescription(ServiceDescriptionInterface $service)
    {
        $this->serviceDescription = $service;

        if ($this->getCommandFactory() && $this->getCommandFactory() instanceof CompositeFactory) {
            $this->commandFactory->add(new Command\Factory\ServiceDescriptionFactory($service));
        }

        // If a baseUrl was set on the description, then update the client
        if ($baseUrl = $service->getBaseUrl()) {
            $this->setBaseUrl($baseUrl);
        }

        return $this;
    }

    public function getDescription()
    {
        return $this->serviceDescription;
    }

    /**
     * Set the inflector used with the client
     *
     * @param InflectorInterface $inflector Inflection object
     *
     * @return self
     */
    public function setInflector(InflectorInterface $inflector)
    {
        $this->inflector = $inflector;

        return $this;
    }

    /**
     * Get the inflector used with the client
     *
     * @return self
     */
    public function getInflector()
    {
        if (!$this->inflector) {
            $this->inflector = Inflector::getDefault();
        }

        return $this->inflector;
    }

    /**
     * Prepare a command for sending and get the RequestInterface object created by the command
     *
     * @param CommandInterface $command Command to prepare
     *
     * @return RequestInterface
     */
    protected function prepareCommand(CommandInterface $command)
    {
        // Set the client and prepare the command
        $request = $command->setClient($this)->prepare();
        // Set the state to new if the command was previously executed
        $request->setState(RequestInterface::STATE_NEW);
        $this->dispatch('command.before_send', array('command' => $command));

        return $request;
    }

    /**
     * Execute multiple commands in parallel
     *
     * @param array|Traversable $commands Array of CommandInterface objects to execute
     *
     * @return array Returns an array of the executed commands
     * @throws Exception\CommandTransferException
     */
    protected function executeMultiple($commands)
    {
        $requests = array();
        $commandRequests = new \SplObjectStorage();

        foreach ($commands as $command) {
            $request = $this->prepareCommand($command);
            $commandRequests[$request] = $command;
            $requests[] = $request;
        }

        try {
            $this->send($requests);
            foreach ($commands as $command) {
                $this->dispatch('command.after_send', array('command' => $command));
            }
            return $commands;
        } catch (MultiTransferException $failureException) {
            // Throw a CommandTransferException using the successful and failed commands
            $e = CommandTransferException::fromMultiTransferException($failureException);

            // Remove failed requests from the successful requests array and add to the failures array
            foreach ($failureException->getFailedRequests() as $request) {
                if (isset($commandRequests[$request])) {
                    $e->addFailedCommand($commandRequests[$request]);
                    unset($commandRequests[$request]);
                }
            }

            // Always emit the command after_send events for successful commands
            foreach ($commandRequests as $success) {
                $e->addSuccessfulCommand($commandRequests[$success]);
                $this->dispatch('command.after_send', array('command' => $commandRequests[$success]));
            }

            throw $e;
        }
    }

    protected function getResourceIteratorFactory()
    {
        if (!$this->resourceIteratorFactory) {
            // Build the default resource iterator factory if one is not set
            $clientClass = get_class($this);
            $prefix = substr($clientClass, 0, strrpos($clientClass, '\\'));
            $this->resourceIteratorFactory = new ResourceIteratorClassFactory(array(
                "{$prefix}\\Iterator",
                "{$prefix}\\Model"
            ));
        }

        return $this->resourceIteratorFactory;
    }

    /**
     * Get the command factory associated with the client
     *
     * @return CommandFactoryInterface
     */
    protected function getCommandFactory()
    {
        if (!$this->commandFactory) {
            $this->commandFactory = CompositeFactory::getDefaultChain($this);
        }

        return $this->commandFactory;
    }

    /**
     * @deprecated
     * @codeCoverageIgnore
     */
    public function enableMagicMethods($isEnabled)
    {
        Version::warn(__METHOD__ . ' is deprecated');
    }
}
vendor/guzzle/guzzle/src/Guzzle/Service/Resource/MapResourceIteratorFactory.php000064400000001610151327705700024144 0ustar00<?php

namespace Guzzle\Service\Resource;

use Guzzle\Service\Command\CommandInterface;

/**
 * Resource iterator factory used when explicitly mapping strings to iterator classes
 */
class MapResourceIteratorFactory extends AbstractResourceIteratorFactory
{
    /** @var array Associative array mapping iterator names to class names */
    protected $map;

    /** @param array $map Associative array mapping iterator names to class names */
    public function __construct(array $map)
    {
        $this->map = $map;
    }

    public function getClassName(CommandInterface $command)
    {
        $className = $command->getName();

        if (isset($this->map[$className])) {
            return $this->map[$className];
        } elseif (isset($this->map['*'])) {
            // If a wildcard was added, then always use that
            return $this->map['*'];
        }

        return null;
    }
}
vendor/guzzle/guzzle/src/Guzzle/Service/Resource/ResourceIteratorInterface.php000064400000003403151327705700024001 0ustar00<?php

namespace Guzzle\Service\Resource;

use Guzzle\Common\HasDispatcherInterface;
use Guzzle\Common\ToArrayInterface;

/**
 * Iterates over a paginated resource using subsequent requests in order to retrieve the entire matching result set
 */
interface ResourceIteratorInterface extends ToArrayInterface, HasDispatcherInterface, \Iterator, \Countable
{
    /**
     * Retrieve the NextToken that can be used in other iterators.
     *
     * @return string Returns a NextToken
     */
    public function getNextToken();

    /**
     * Attempt to limit the total number of resources returned by the iterator.
     *
     * You may still receive more items than you specify. Set to 0 to specify no limit.
     *
     * @param int $limit Limit amount
     *
     * @return ResourceIteratorInterface
     */
    public function setLimit($limit);

    /**
     * Attempt to limit the total number of resources retrieved per request by  the iterator.
     *
     * The iterator may return more than you specify in the page size argument depending on the service and underlying
     * command implementation.  Set to 0 to specify no page size limitation.
     *
     * @param int $pageSize Limit amount
     *
     * @return ResourceIteratorInterface
     */
    public function setPageSize($pageSize);

    /**
     * Get a data option from the iterator
     *
     * @param string $key Key of the option to retrieve
     *
     * @return mixed|null Returns NULL if not set or the value if set
     */
    public function get($key);

    /**
     * Set a data option on the iterator
     *
     * @param string $key   Key of the option to set
     * @param mixed  $value Value to set for the option
     *
     * @return ResourceIteratorInterface
     */
    public function set($key, $value);
}
vendor/guzzle/guzzle/src/Guzzle/Service/Resource/ResourceIterator.php000064400000016534151327705700022171 0ustar00<?php

namespace Guzzle\Service\Resource;

use Guzzle\Common\AbstractHasDispatcher;
use Guzzle\Service\Command\CommandInterface;

abstract class ResourceIterator extends AbstractHasDispatcher implements ResourceIteratorInterface
{
    /** @var CommandInterface Command used to send requests */
    protected $command;

    /** @var CommandInterface First sent command */
    protected $originalCommand;

    /** @var array Currently loaded resources */
    protected $resources;

    /** @var int Total number of resources that have been retrieved */
    protected $retrievedCount = 0;

    /** @var int Total number of resources that have been iterated */
    protected $iteratedCount = 0;

    /** @var string NextToken/Marker for a subsequent request */
    protected $nextToken = false;

    /** @var int Maximum number of resources to fetch per request */
    protected $pageSize;

    /** @var int Maximum number of resources to retrieve in total */
    protected $limit;

    /** @var int Number of requests sent */
    protected $requestCount = 0;

    /** @var array Initial data passed to the constructor */
    protected $data = array();

    /** @var bool Whether or not the current value is known to be invalid */
    protected $invalid;

    public static function getAllEvents()
    {
        return array(
            // About to issue another command to get more results
            'resource_iterator.before_send',
            // Issued another command to get more results
            'resource_iterator.after_send'
        );
    }

    /**
     * @param CommandInterface $command Initial command used for iteration
     * @param array            $data    Associative array of additional parameters. You may specify any number of custom
     *     options for an iterator. Among these options, you may also specify the following values:
     *     - limit: Attempt to limit the maximum number of resources to this amount
     *     - page_size: Attempt to retrieve this number of resources per request
     */
    public function __construct(CommandInterface $command, array $data = array())
    {
        // Clone the command to keep track of the originating command for rewind
        $this->originalCommand = $command;

        // Parse options from the array of options
        $this->data = $data;
        $this->limit = array_key_exists('limit', $data) ? $data['limit'] : 0;
        $this->pageSize = array_key_exists('page_size', $data) ? $data['page_size'] : false;
    }

    /**
     * Get all of the resources as an array (Warning: this could issue a large number of requests)
     *
     * @return array
     */
    public function toArray()
    {
        return iterator_to_array($this, false);
    }

    public function setLimit($limit)
    {
        $this->limit = $limit;
        $this->resetState();

        return $this;
    }

    public function setPageSize($pageSize)
    {
        $this->pageSize = $pageSize;
        $this->resetState();

        return $this;
    }

    /**
     * Get an option from the iterator
     *
     * @param string $key Key of the option to retrieve
     *
     * @return mixed|null Returns NULL if not set or the value if set
     */
    public function get($key)
    {
        return array_key_exists($key, $this->data) ? $this->data[$key] : null;
    }

    /**
     * Set an option on the iterator
     *
     * @param string $key   Key of the option to set
     * @param mixed  $value Value to set for the option
     *
     * @return ResourceIterator
     */
    public function set($key, $value)
    {
        $this->data[$key] = $value;

        return $this;
    }

    public function current()
    {
        return $this->resources ? current($this->resources) : false;
    }

    public function key()
    {
        return max(0, $this->iteratedCount - 1);
    }

    public function count()
    {
        return $this->retrievedCount;
    }

    /**
     * Get the total number of requests sent
     *
     * @return int
     */
    public function getRequestCount()
    {
        return $this->requestCount;
    }

    /**
     * Rewind the Iterator to the first element and send the original command
     */
    public function rewind()
    {
        // Use the original command
        $this->command = clone $this->originalCommand;
        $this->resetState();
        $this->next();
    }

    public function valid()
    {
        return !$this->invalid && (!$this->resources || $this->current() || $this->nextToken)
            && (!$this->limit || $this->iteratedCount < $this->limit + 1);
    }

    public function next()
    {
        $this->iteratedCount++;

        // Check if a new set of resources needs to be retrieved
        $sendRequest = false;
        if (!$this->resources) {
            $sendRequest = true;
        } else {
            // iterate over the internal array
            $current = next($this->resources);
            $sendRequest = $current === false && $this->nextToken && (!$this->limit || $this->iteratedCount < $this->limit + 1);
        }

        if ($sendRequest) {

            $this->dispatch('resource_iterator.before_send', array(
                'iterator'  => $this,
                'resources' => $this->resources
            ));

            // Get a new command object from the original command
            $this->command = clone $this->originalCommand;
            // Send a request and retrieve the newly loaded resources
            $this->resources = $this->sendRequest();
            $this->requestCount++;

            // If no resources were found, then the last request was not needed
            // and iteration must stop
            if (empty($this->resources)) {
                $this->invalid = true;
            } else {
                // Add to the number of retrieved resources
                $this->retrievedCount += count($this->resources);
                // Ensure that we rewind to the beginning of the array
                reset($this->resources);
            }

            $this->dispatch('resource_iterator.after_send', array(
                'iterator'  => $this,
                'resources' => $this->resources
            ));
        }
    }

    /**
     * Retrieve the NextToken that can be used in other iterators.
     *
     * @return string Returns a NextToken
     */
    public function getNextToken()
    {
        return $this->nextToken;
    }

    /**
     * Returns the value that should be specified for the page size for a request that will maintain any hard limits,
     * but still honor the specified pageSize if the number of items retrieved + pageSize < hard limit
     *
     * @return int Returns the page size of the next request.
     */
    protected function calculatePageSize()
    {
        if ($this->limit && $this->iteratedCount + $this->pageSize > $this->limit) {
            return 1 + ($this->limit - $this->iteratedCount);
        }

        return (int) $this->pageSize;
    }

    /**
     * Reset the internal state of the iterator without triggering a rewind()
     */
    protected function resetState()
    {
        $this->iteratedCount = 0;
        $this->retrievedCount = 0;
        $this->nextToken = false;
        $this->resources = null;
        $this->invalid = false;
    }

    /**
     * Send a request to retrieve the next page of results. Hook for subclasses to implement.
     *
     * @return array Returns the newly loaded resources
     */
    abstract protected function sendRequest();
}
vendor/guzzle/guzzle/src/Guzzle/Service/Resource/AbstractResourceIteratorFactory.php000064400000002025151327705700025173 0ustar00<?php

namespace Guzzle\Service\Resource;

use Guzzle\Common\Exception\InvalidArgumentException;
use Guzzle\Service\Command\CommandInterface;

/**
 * Abstract resource iterator factory implementation
 */
abstract class AbstractResourceIteratorFactory implements ResourceIteratorFactoryInterface
{
    public function build(CommandInterface $command, array $options = array())
    {
        if (!$this->canBuild($command)) {
            throw new InvalidArgumentException('Iterator was not found for ' . $command->getName());
        }

        $className = $this->getClassName($command);

        return new $className($command, $options);
    }

    public function canBuild(CommandInterface $command)
    {
        return (bool) $this->getClassName($command);
    }

    /**
     * Get the name of the class to instantiate for the command
     *
     * @param CommandInterface $command Command that is associated with the iterator
     *
     * @return string
     */
    abstract protected function getClassName(CommandInterface $command);
}
vendor/guzzle/guzzle/src/Guzzle/Service/Resource/ResourceIteratorClassFactory.php000064400000003710151327705700024477 0ustar00<?php

namespace Guzzle\Service\Resource;

use Guzzle\Inflection\InflectorInterface;
use Guzzle\Inflection\Inflector;
use Guzzle\Service\Command\CommandInterface;

/**
 * Factory for creating {@see ResourceIteratorInterface} objects using a convention of storing iterator classes under a
 * root namespace using the name of a {@see CommandInterface} object as a convention for determining the name of an
 * iterator class. The command name is converted to CamelCase and Iterator is appended (e.g. abc_foo => AbcFoo).
 */
class ResourceIteratorClassFactory extends AbstractResourceIteratorFactory
{
    /** @var array List of namespaces used to look for classes */
    protected $namespaces;

    /** @var InflectorInterface Inflector used to determine class names */
    protected $inflector;

    /**
     * @param string|array       $namespaces List of namespaces for iterator objects
     * @param InflectorInterface $inflector  Inflector used to resolve class names
     */
    public function __construct($namespaces = array(), InflectorInterface $inflector = null)
    {
        $this->namespaces = (array) $namespaces;
        $this->inflector = $inflector ?: Inflector::getDefault();
    }

    /**
     * Registers a namespace to check for Iterators
     *
     * @param string $namespace Namespace which contains Iterator classes
     *
     * @return self
     */
    public function registerNamespace($namespace)
    {
        array_unshift($this->namespaces, $namespace);

        return $this;
    }

    protected function getClassName(CommandInterface $command)
    {
        $iteratorName = $this->inflector->camel($command->getName()) . 'Iterator';

        // Determine the name of the class to load
        foreach ($this->namespaces as $namespace) {
            $potentialClassName = $namespace . '\\' . $iteratorName;
            if (class_exists($potentialClassName)) {
                return $potentialClassName;
            }
        }

        return false;
    }
}
vendor/guzzle/guzzle/src/Guzzle/Service/Resource/CompositeResourceIteratorFactory.php000064400000003357151327705700025403 0ustar00<?php

namespace Guzzle\Service\Resource;

use Guzzle\Common\Exception\InvalidArgumentException;
use Guzzle\Service\Command\CommandInterface;

/**
 * Factory that utilizes multiple factories for creating iterators
 */
class CompositeResourceIteratorFactory implements ResourceIteratorFactoryInterface
{
    /** @var array Array of factories */
    protected $factories;

    /** @param array $factories Array of factories used to instantiate iterators */
    public function __construct(array $factories)
    {
        $this->factories = $factories;
    }

    public function build(CommandInterface $command, array $options = array())
    {
        if (!($factory = $this->getFactory($command))) {
            throw new InvalidArgumentException('Iterator was not found for ' . $command->getName());
        }

        return $factory->build($command, $options);
    }

    public function canBuild(CommandInterface $command)
    {
        return $this->getFactory($command) !== false;
    }

    /**
     * Add a factory to the composite factory
     *
     * @param ResourceIteratorFactoryInterface $factory Factory to add
     *
     * @return self
     */
    public function addFactory(ResourceIteratorFactoryInterface $factory)
    {
        $this->factories[] = $factory;

        return $this;
    }

    /**
     * Get the factory that matches the command object
     *
     * @param CommandInterface $command Command retrieving the iterator for
     *
     * @return ResourceIteratorFactoryInterface|bool
     */
    protected function getFactory(CommandInterface $command)
    {
        foreach ($this->factories as $factory) {
            if ($factory->canBuild($command)) {
                return $factory;
            }
        }

        return false;
    }
}
vendor/guzzle/guzzle/src/Guzzle/Service/Resource/Model.php000064400000004016151327705700017720 0ustar00<?php

namespace Guzzle\Service\Resource;

use Guzzle\Common\Collection;
use Guzzle\Service\Description\Parameter;

/**
 * Default model created when commands create service description model responses
 */
class Model extends Collection
{
    /** @var Parameter Structure of the model */
    protected $structure;

    /**
     * @param array     $data      Data contained by the model
     * @param Parameter $structure The structure of the model
     */
    public function __construct(array $data = array(), Parameter $structure = null)
    {
        $this->data = $data;
        $this->structure = $structure;
    }

    /**
     * Get the structure of the model
     *
     * @return Parameter
     */
    public function getStructure()
    {
        return $this->structure ?: new Parameter();
    }

    /**
     * Provides debug information about the model object
     *
     * @return string
     */
    public function __toString()
    {
        $output = 'Debug output of ';
        if ($this->structure) {
            $output .= $this->structure->getName() . ' ';
        }
        $output .= 'model';
        $output = str_repeat('=', strlen($output)) . "\n" . $output . "\n" . str_repeat('=', strlen($output)) . "\n\n";
        $output .= "Model data\n-----------\n\n";
        $output .= "This data can be retrieved from the model object using the get() method of the model "
            . "(e.g. \$model->get(\$key)) or accessing the model like an associative array (e.g. \$model['key']).\n\n";
        $lines = array_slice(explode("\n", trim(print_r($this->toArray(), true))), 2, -1);
        $output .=  implode("\n", $lines);

        if ($this->structure) {
            $output .= "\n\nModel structure\n---------------\n\n";
            $output .= "The following JSON document defines how the model was parsed from an HTTP response into the "
                . "associative array structure you see above.\n\n";
            $output .= '  ' . json_encode($this->structure->toArray()) . "\n\n";
        }

        return $output . "\n";
    }
}
vendor/guzzle/guzzle/src/Guzzle/Service/Resource/ResourceIteratorFactoryInterface.php000064400000001425151327705700025333 0ustar00<?php

namespace Guzzle\Service\Resource;

use Guzzle\Service\Command\CommandInterface;

/**
 * Factory for creating {@see ResourceIteratorInterface} objects
 */
interface ResourceIteratorFactoryInterface
{
    /**
     * Create a resource iterator
     *
     * @param CommandInterface $command Command to create an iterator for
     * @param array                 $options Iterator options that are exposed as data.
     *
     * @return ResourceIteratorInterface
     */
    public function build(CommandInterface $command, array $options = array());

    /**
     * Check if the factory can create an iterator
     *
     * @param CommandInterface $command Command to create an iterator for
     *
     * @return bool
     */
    public function canBuild(CommandInterface $command);
}
vendor/guzzle/guzzle/src/Guzzle/Service/Resource/ResourceIteratorApplyBatched.php000064400000006523151327705700024447 0ustar00<?php

namespace Guzzle\Service\Resource;

use Guzzle\Common\AbstractHasDispatcher;
use Guzzle\Batch\BatchBuilder;
use Guzzle\Batch\BatchSizeDivisor;
use Guzzle\Batch\BatchClosureTransfer;
use Guzzle\Common\Version;

/**
 * Apply a callback to the contents of a {@see ResourceIteratorInterface}
 * @deprecated Will be removed in a future version and is no longer maintained. Use the Batch\ abstractions instead.
 * @codeCoverageIgnore
 */
class ResourceIteratorApplyBatched extends AbstractHasDispatcher
{
    /** @var callable|array */
    protected $callback;

    /** @var ResourceIteratorInterface */
    protected $iterator;

    /** @var integer Total number of sent batches */
    protected $batches = 0;

    /** @var int Total number of iterated resources */
    protected $iterated = 0;

    public static function getAllEvents()
    {
        return array(
            // About to send a batch of requests to the callback
            'iterator_batch.before_batch',
            // Finished sending a batch of requests to the callback
            'iterator_batch.after_batch',
            // Created the batch object
            'iterator_batch.created_batch'
        );
    }

    /**
     * @param ResourceIteratorInterface $iterator Resource iterator to apply a callback to
     * @param array|callable            $callback Callback method accepting the resource iterator
     *                                            and an array of the iterator's current resources
     */
    public function __construct(ResourceIteratorInterface $iterator, $callback)
    {
        $this->iterator = $iterator;
        $this->callback = $callback;
        Version::warn(__CLASS__ . ' is deprecated');
    }

    /**
     * Apply the callback to the contents of the resource iterator
     *
     * @param int $perBatch The number of records to group per batch transfer
     *
     * @return int Returns the number of iterated resources
     */
    public function apply($perBatch = 50)
    {
        $this->iterated = $this->batches = $batches = 0;
        $that = $this;
        $it = $this->iterator;
        $callback = $this->callback;

        $batch = BatchBuilder::factory()
            ->createBatchesWith(new BatchSizeDivisor($perBatch))
            ->transferWith(new BatchClosureTransfer(function (array $batch) use ($that, $callback, &$batches, $it) {
                $batches++;
                $that->dispatch('iterator_batch.before_batch', array('iterator' => $it, 'batch' => $batch));
                call_user_func_array($callback, array($it, $batch));
                $that->dispatch('iterator_batch.after_batch', array('iterator' => $it, 'batch' => $batch));
            }))
            ->autoFlushAt($perBatch)
            ->build();

        $this->dispatch('iterator_batch.created_batch', array('batch' => $batch));

        foreach ($this->iterator as $resource) {
            $this->iterated++;
            $batch->add($resource);
        }

        $batch->flush();
        $this->batches = $batches;

        return $this->iterated;
    }

    /**
     * Get the total number of batches sent
     *
     * @return int
     */
    public function getBatchCount()
    {
        return $this->batches;
    }

    /**
     * Get the total number of iterated resources
     *
     * @return int
     */
    public function getIteratedCount()
    {
        return $this->iterated;
    }
}
vendor/guzzle/guzzle/src/Guzzle/Service/ClientInterface.php000064400000005223151327705700020131 0ustar00<?php

namespace Guzzle\Service;

use Guzzle\Common\FromConfigInterface;
use Guzzle\Common\Exception\InvalidArgumentException;
use Guzzle\Http\ClientInterface as HttpClientInterface;
use Guzzle\Service\Exception\CommandTransferException;
use Guzzle\Service\Command\CommandInterface;
use Guzzle\Service\Description\ServiceDescriptionInterface;
use Guzzle\Service\Resource\ResourceIteratorInterface;

/**
 * Client interface for executing commands on a web service.
 */
interface ClientInterface extends HttpClientInterface, FromConfigInterface
{
    /**
     * Get a command by name. First, the client will see if it has a service description and if the service description
     * defines a command by the supplied name. If no dynamic command is found, the client will look for a concrete
     * command class exists matching the name supplied. If neither are found, an InvalidArgumentException is thrown.
     *
     * @param string $name Name of the command to retrieve
     * @param array  $args Arguments to pass to the command
     *
     * @return CommandInterface
     * @throws InvalidArgumentException if no command can be found by name
     */
    public function getCommand($name, array $args = array());

    /**
     * Execute one or more commands
     *
     * @param CommandInterface|array|Traversable $command Command, array of commands or Traversable object containing commands to execute
     *
     * @return mixed Returns the result of the executed command or an array of commands if executing multiple commands
     * @throws InvalidArgumentException if an invalid command is passed
     * @throws CommandTransferException if an exception is encountered when transferring multiple commands
     */
    public function execute($command);

    /**
     * Set the service description of the client
     *
     * @param ServiceDescriptionInterface $service Service description
     *
     * @return ClientInterface
     */
    public function setDescription(ServiceDescriptionInterface $service);

    /**
     * Get the service description of the client
     *
     * @return ServiceDescriptionInterface|null
     */
    public function getDescription();

    /**
     * Get a resource iterator from the client.
     *
     * @param string|CommandInterface $command         Command class or command name.
     * @param array                   $commandOptions  Command options used when creating commands.
     * @param array                   $iteratorOptions Iterator options passed to the iterator when it is instantiated.
     *
     * @return ResourceIteratorInterface
     */
    public function getIterator($command, array $commandOptions = null, array $iteratorOptions = array());
}
vendor/guzzle/guzzle/src/Guzzle/Service/Exception/ServiceNotFoundException.php000064400000000156151327705700023764 0ustar00<?php

namespace Guzzle\Service\Exception;

class ServiceNotFoundException extends ServiceBuilderException {}
vendor/guzzle/guzzle/src/Guzzle/Service/Exception/ResponseClassException.php000064400000000225151327705700023470 0ustar00<?php

namespace Guzzle\Service\Exception;

use Guzzle\Common\Exception\RuntimeException;

class ResponseClassException extends RuntimeException
{
}
vendor/guzzle/guzzle/src/Guzzle/Service/Exception/InconsistentClientTransferException.php000064400000001626151327705700026236 0ustar00<?php

namespace Guzzle\Service\Exception;

use Guzzle\Common\Exception\RuntimeException;

/**
 * Command transfer exception when commands do not all use the same client
 */
class InconsistentClientTransferException extends RuntimeException
{
    /**
     * @var array Commands with an invalid client
     */
    private $invalidCommands = array();

    /**
     * @param array $commands Invalid commands
     */
    public function __construct(array $commands)
    {
        $this->invalidCommands = $commands;
        parent::__construct(
            'Encountered commands in a batch transfer that use inconsistent clients. The batching ' .
            'strategy you use with a command transfer must divide command batches by client.'
        );
    }

    /**
     * Get the invalid commands
     *
     * @return array
     */
    public function getCommands()
    {
        return $this->invalidCommands;
    }
}
vendor/guzzle/guzzle/src/Guzzle/Service/Exception/ServiceBuilderException.php000064400000000225151327705700023613 0ustar00<?php

namespace Guzzle\Service\Exception;

use Guzzle\Common\Exception\RuntimeException;

class ServiceBuilderException extends RuntimeException {}
vendor/guzzle/guzzle/src/Guzzle/Service/Exception/CommandTransferException.php000064400000005705151327705700023777 0ustar00<?php

namespace Guzzle\Service\Exception;

use Guzzle\Http\Exception\MultiTransferException;
use Guzzle\Service\Command\CommandInterface;

/**
 * Exception thrown when transferring commands in parallel
 */
class CommandTransferException extends MultiTransferException
{
    protected $successfulCommands = array();
    protected $failedCommands = array();

    /**
     * Creates a new CommandTransferException from a MultiTransferException
     *
     * @param MultiTransferException $e Exception to base a new exception on
     *
     * @return self
     */
    public static function fromMultiTransferException(MultiTransferException $e)
    {
        $ce = new self($e->getMessage(), $e->getCode(), $e->getPrevious());
        $ce->setSuccessfulRequests($e->getSuccessfulRequests());

        $alreadyAddedExceptions = array();
        foreach ($e->getFailedRequests() as $request) {
            if ($re = $e->getExceptionForFailedRequest($request)) {
                $alreadyAddedExceptions[] = $re;
                $ce->addFailedRequestWithException($request, $re);
            } else {
                $ce->addFailedRequest($request);
            }
        }

        // Add any exceptions that did not map to a request
        if (count($alreadyAddedExceptions) < count($e)) {
            foreach ($e as $ex) {
                if (!in_array($ex, $alreadyAddedExceptions)) {
                    $ce->add($ex);
                }
            }
        }

        return $ce;
    }

    /**
     * Get all of the commands in the transfer
     *
     * @return array
     */
    public function getAllCommands()
    {
        return array_merge($this->successfulCommands, $this->failedCommands);
    }

    /**
     * Add to the array of successful commands
     *
     * @param CommandInterface $command Successful command
     *
     * @return self
     */
    public function addSuccessfulCommand(CommandInterface $command)
    {
        $this->successfulCommands[] = $command;

        return $this;
    }

    /**
     * Add to the array of failed commands
     *
     * @param CommandInterface $command Failed command
     *
     * @return self
     */
    public function addFailedCommand(CommandInterface $command)
    {
        $this->failedCommands[] = $command;

        return $this;
    }

    /**
     * Get an array of successful commands
     *
     * @return array
     */
    public function getSuccessfulCommands()
    {
        return $this->successfulCommands;
    }

    /**
     * Get an array of failed commands
     *
     * @return array
     */
    public function getFailedCommands()
    {
        return $this->failedCommands;
    }

    /**
     * Get the Exception that caused the given $command to fail
     *
     * @param CommandInterface $command Failed command
     *
     * @return \Exception|null
     */
    public function getExceptionForFailedCommand(CommandInterface $command)
    {
        return $this->getExceptionForFailedRequest($command->getRequest());
    }
}
vendor/guzzle/guzzle/src/Guzzle/Service/Exception/DescriptionBuilderException.php000064400000000231151327705700024473 0ustar00<?php

namespace Guzzle\Service\Exception;

use Guzzle\Common\Exception\RuntimeException;

class DescriptionBuilderException extends RuntimeException {}
vendor/guzzle/guzzle/src/Guzzle/Service/Exception/ValidationException.php000064400000001035151327705700022776 0ustar00<?php

namespace Guzzle\Service\Exception;

use Guzzle\Common\Exception\RuntimeException;

class ValidationException extends RuntimeException
{
    protected $errors = array();

    /**
     * Set the validation error messages
     *
     * @param array $errors Array of validation errors
     */
    public function setErrors(array $errors)
    {
        $this->errors = $errors;
    }

    /**
     * Get any validation errors
     *
     * @return array
     */
    public function getErrors()
    {
        return $this->errors;
    }
}
vendor/guzzle/guzzle/src/Guzzle/Service/Exception/CommandException.php000064400000000216151327705700022262 0ustar00<?php

namespace Guzzle\Service\Exception;

use Guzzle\Common\Exception\RuntimeException;

class CommandException extends RuntimeException {}
vendor/guzzle/guzzle/src/Guzzle/Inflection/composer.json000064400000001162151327705700017573 0ustar00{
    "name": "guzzle/inflection",
    "description": "Guzzle inflection component",
    "homepage": "http://guzzlephp.org/",
    "keywords": ["inflection", "guzzle"],
    "license": "MIT",
    "authors": [
        {
            "name": "Michael Dowling",
            "email": "mtdowling@gmail.com",
            "homepage": "https://github.com/mtdowling"
        }
    ],
    "require": {
        "php": ">=5.3.2"
    },
    "autoload": {
        "psr-0": { "Guzzle\\Inflection": "" }
    },
    "target-dir": "Guzzle/Inflection",
    "extra": {
        "branch-alias": {
            "dev-master": "3.7-dev"
        }
    }
}
vendor/guzzle/guzzle/src/Guzzle/Inflection/InflectorInterface.php000064400000001076151327705700021334 0ustar00<?php

namespace Guzzle\Inflection;

/**
 * Inflector interface used to convert the casing of words
 */
interface InflectorInterface
{
    /**
     * Converts strings from camel case to snake case (e.g. CamelCase camel_case).
     *
     * @param string $word Word to convert to snake case
     *
     * @return string
     */
    public function snake($word);

    /**
     * Converts strings from snake_case to upper CamelCase
     *
     * @param string $word Value to convert into upper CamelCase
     *
     * @return string
     */
    public function camel($word);
}
vendor/guzzle/guzzle/src/Guzzle/Inflection/MemoizingInflector.php000064400000003655151327705700021377 0ustar00<?php

namespace Guzzle\Inflection;

/**
 * Decorator used to add memoization to previously inflected words
 */
class MemoizingInflector implements InflectorInterface
{
    /** @var array Array of cached inflections */
    protected $cache = array(
        'snake' => array(),
        'camel' => array()
    );

    /** @var int Max entries per cache */
    protected $maxCacheSize;

    /** @var InflectorInterface Decorated inflector */
    protected $decoratedInflector;

    /**
     * @param InflectorInterface $inflector    Inflector being decorated
     * @param int                $maxCacheSize Maximum number of cached items to hold per cache
     */
    public function __construct(InflectorInterface $inflector, $maxCacheSize = 500)
    {
        $this->decoratedInflector = $inflector;
        $this->maxCacheSize = $maxCacheSize;
    }

    public function snake($word)
    {
        if (!isset($this->cache['snake'][$word])) {
            $this->pruneCache('snake');
            $this->cache['snake'][$word] = $this->decoratedInflector->snake($word);
        }

        return $this->cache['snake'][$word];
    }

    /**
     * Converts strings from snake_case to upper CamelCase
     *
     * @param string $word Value to convert into upper CamelCase
     *
     * @return string
     */
    public function camel($word)
    {
        if (!isset($this->cache['camel'][$word])) {
            $this->pruneCache('camel');
            $this->cache['camel'][$word] = $this->decoratedInflector->camel($word);
        }

        return $this->cache['camel'][$word];
    }

    /**
     * Prune one of the named caches by removing 20% of the cache if it is full
     *
     * @param string $cache Type of cache to prune
     */
    protected function pruneCache($cache)
    {
        if (count($this->cache[$cache]) == $this->maxCacheSize) {
            $this->cache[$cache] = array_slice($this->cache[$cache], $this->maxCacheSize * 0.2);
        }
    }
}
vendor/guzzle/guzzle/src/Guzzle/Inflection/PreComputedInflector.php000064400000003353151327705700021663 0ustar00<?php

namespace Guzzle\Inflection;

/**
 * Decorator used to add pre-computed inflection mappings to an inflector
 */
class PreComputedInflector implements InflectorInterface
{
    /** @var array Array of pre-computed inflections */
    protected $mapping = array(
        'snake' => array(),
        'camel' => array()
    );

    /** @var InflectorInterface Decorated inflector */
    protected $decoratedInflector;

    /**
     * @param InflectorInterface $inflector Inflector being decorated
     * @param array              $snake     Hash of pre-computed camel to snake
     * @param array              $camel     Hash of pre-computed snake to camel
     * @param bool               $mirror    Mirror snake and camel reflections
     */
    public function __construct(InflectorInterface $inflector, array $snake = array(), array $camel = array(), $mirror = false)
    {
        if ($mirror) {
            $camel = array_merge(array_flip($snake), $camel);
            $snake = array_merge(array_flip($camel), $snake);
        }

        $this->decoratedInflector = $inflector;
        $this->mapping = array(
            'snake' => $snake,
            'camel' => $camel
        );
    }

    public function snake($word)
    {
        return isset($this->mapping['snake'][$word])
            ? $this->mapping['snake'][$word]
            : $this->decoratedInflector->snake($word);
    }

    /**
     * Converts strings from snake_case to upper CamelCase
     *
     * @param string $word Value to convert into upper CamelCase
     *
     * @return string
     */
    public function camel($word)
    {
        return isset($this->mapping['camel'][$word])
            ? $this->mapping['camel'][$word]
            : $this->decoratedInflector->camel($word);
    }
}
vendor/guzzle/guzzle/src/Guzzle/Inflection/Inflector.php000064400000001523151327705700017510 0ustar00<?php

namespace Guzzle\Inflection;

/**
 * Default inflection implementation
 */
class Inflector implements InflectorInterface
{
    /** @var InflectorInterface */
    protected static $default;

    /**
     * Get the default inflector object that has support for caching
     *
     * @return MemoizingInflector
     */
    public static function getDefault()
    {
        // @codeCoverageIgnoreStart
        if (!self::$default) {
            self::$default = new MemoizingInflector(new self());
        }
        // @codeCoverageIgnoreEnd

        return self::$default;
    }

    public function snake($word)
    {
        return ctype_lower($word) ? $word : strtolower(preg_replace('/(.)([A-Z])/', "$1_$2", $word));
    }

    public function camel($word)
    {
        return str_replace(' ', '', ucwords(strtr($word, '_-', '  ')));
    }
}
vendor/guzzle/guzzle/src/Guzzle/Stream/composer.json000064400000001376151327705700016743 0ustar00{
    "name": "guzzle/stream",
    "description": "Guzzle stream wrapper component",
    "homepage": "http://guzzlephp.org/",
    "keywords": ["stream", "component", "guzzle"],
    "license": "MIT",
    "authors": [
        {
            "name": "Michael Dowling",
            "email": "mtdowling@gmail.com",
            "homepage": "https://github.com/mtdowling"
        }
    ],
    "require": {
        "php": ">=5.3.2",
        "guzzle/common": "self.version"
    },
    "suggest": {
        "guzzle/http": "To convert Guzzle request objects to PHP streams"
    },
    "autoload": {
        "psr-0": { "Guzzle\\Stream": "" }
    },
    "target-dir": "Guzzle/Stream",
    "extra": {
        "branch-alias": {
            "dev-master": "3.7-dev"
        }
    }
}
vendor/guzzle/guzzle/src/Guzzle/Stream/Stream.php000064400000016435151327705700016167 0ustar00<?php

namespace Guzzle\Stream;

use Guzzle\Common\Exception\InvalidArgumentException;

/**
 * PHP stream implementation
 */
class Stream implements StreamInterface
{
    const STREAM_TYPE = 'stream_type';
    const WRAPPER_TYPE = 'wrapper_type';
    const IS_LOCAL = 'is_local';
    const IS_READABLE = 'is_readable';
    const IS_WRITABLE = 'is_writable';
    const SEEKABLE = 'seekable';

    /** @var resource Stream resource */
    protected $stream;

    /** @var int Size of the stream contents in bytes */
    protected $size;

    /** @var array Stream cached data */
    protected $cache = array();

    /** @var array Custom stream data */
    protected $customData = array();

    /** @var array Hash table of readable and writeable stream types for fast lookups */
    protected static $readWriteHash = array(
        'read' => array(
            'r' => true, 'w+' => true, 'r+' => true, 'x+' => true, 'c+' => true,
            'rb' => true, 'w+b' => true, 'r+b' => true, 'x+b' => true, 'c+b' => true,
            'rt' => true, 'w+t' => true, 'r+t' => true, 'x+t' => true, 'c+t' => true, 'a+' => true
        ),
        'write' => array(
            'w' => true, 'w+' => true, 'rw' => true, 'r+' => true, 'x+' => true, 'c+' => true,
            'wb' => true, 'w+b' => true, 'r+b' => true, 'x+b' => true, 'c+b' => true,
            'w+t' => true, 'r+t' => true, 'x+t' => true, 'c+t' => true, 'a' => true, 'a+' => true
        )
    );

    /**
     * @param resource $stream Stream resource to wrap
     * @param int      $size   Size of the stream in bytes. Only pass if the size cannot be obtained from the stream.
     *
     * @throws InvalidArgumentException if the stream is not a stream resource
     */
    public function __construct($stream, $size = null)
    {
        $this->setStream($stream, $size);
    }

    /**
     * Closes the stream when the helper is destructed
     */
    public function __destruct()
    {
        $this->close();
    }

    public function __toString()
    {
        if (!$this->isReadable() || (!$this->isSeekable() && $this->isConsumed())) {
            return '';
        }

        $originalPos = $this->ftell();
        $body = stream_get_contents($this->stream, -1, 0);
        $this->seek($originalPos);

        return $body;
    }

    public function close()
    {
        if (is_resource($this->stream)) {
            fclose($this->stream);
        }
        $this->cache[self::IS_READABLE] = false;
        $this->cache[self::IS_WRITABLE] = false;
    }

    /**
     * Calculate a hash of a Stream
     *
     * @param StreamInterface $stream    Stream to calculate the hash for
     * @param string          $algo      Hash algorithm (e.g. md5, crc32, etc)
     * @param bool            $rawOutput Whether or not to use raw output
     *
     * @return bool|string Returns false on failure or a hash string on success
     */
    public static function getHash(StreamInterface $stream, $algo, $rawOutput = false)
    {
        $pos = $stream->ftell();
        if (!$stream->seek(0)) {
            return false;
        }

        $ctx = hash_init($algo);
        while (!$stream->feof()) {
            hash_update($ctx, $stream->read(8192));
        }

        $out = hash_final($ctx, (bool) $rawOutput);
        $stream->seek($pos);

        return $out;
    }

    public function getMetaData($key = null)
    {
        $meta = stream_get_meta_data($this->stream);

        return !$key ? $meta : (array_key_exists($key, $meta) ? $meta[$key] : null);
    }

    public function getStream()
    {
        return $this->stream;
    }

    public function setStream($stream, $size = null)
    {
        if (!is_resource($stream)) {
            throw new InvalidArgumentException('Stream must be a resource');
        }

        $this->size = $size;
        $this->stream = $stream;
        $this->rebuildCache();

        return $this;
    }

    public function detachStream()
    {
        $this->stream = null;

        return $this;
    }

    public function getWrapper()
    {
        return $this->cache[self::WRAPPER_TYPE];
    }

    public function getWrapperData()
    {
        return $this->getMetaData('wrapper_data') ?: array();
    }

    public function getStreamType()
    {
        return $this->cache[self::STREAM_TYPE];
    }

    public function getUri()
    {
        return $this->cache['uri'];
    }

    public function getSize()
    {
        if ($this->size !== null) {
            return $this->size;
        }

        // If the stream is a file based stream and local, then use fstat
        clearstatcache(true, $this->cache['uri']);
        $stats = fstat($this->stream);
        if (isset($stats['size'])) {
            $this->size = $stats['size'];
            return $this->size;
        } elseif ($this->cache[self::IS_READABLE] && $this->cache[self::SEEKABLE]) {
            // Only get the size based on the content if the the stream is readable and seekable
            $pos = $this->ftell();
            $this->size = strlen((string) $this);
            $this->seek($pos);
            return $this->size;
        }

        return false;
    }

    public function isReadable()
    {
        return $this->cache[self::IS_READABLE];
    }

    public function isRepeatable()
    {
        return $this->cache[self::IS_READABLE] && $this->cache[self::SEEKABLE];
    }

    public function isWritable()
    {
        return $this->cache[self::IS_WRITABLE];
    }

    public function isConsumed()
    {
        return feof($this->stream);
    }

    public function feof()
    {
        return $this->isConsumed();
    }

    public function isLocal()
    {
        return $this->cache[self::IS_LOCAL];
    }

    public function isSeekable()
    {
        return $this->cache[self::SEEKABLE];
    }

    public function setSize($size)
    {
        $this->size = $size;

        return $this;
    }

    public function seek($offset, $whence = SEEK_SET)
    {
        return $this->cache[self::SEEKABLE] ? fseek($this->stream, $offset, $whence) === 0 : false;
    }

    public function read($length)
    {
        return fread($this->stream, $length);
    }

    public function write($string)
    {
        // We can't know the size after writing anything
        $this->size = null;

        return fwrite($this->stream, $string);
    }

    public function ftell()
    {
        return ftell($this->stream);
    }

    public function rewind()
    {
        return $this->seek(0);
    }

    public function readLine($maxLength = null)
    {
        if (!$this->cache[self::IS_READABLE]) {
            return false;
        } else {
            return $maxLength ? fgets($this->getStream(), $maxLength) : fgets($this->getStream());
        }
    }

    public function setCustomData($key, $value)
    {
        $this->customData[$key] = $value;

        return $this;
    }

    public function getCustomData($key)
    {
        return isset($this->customData[$key]) ? $this->customData[$key] : null;
    }

    /**
     * Reprocess stream metadata
     */
    protected function rebuildCache()
    {
        $this->cache = stream_get_meta_data($this->stream);
        $this->cache[self::IS_LOCAL] = stream_is_local($this->stream);
        $this->cache[self::IS_READABLE] = isset(self::$readWriteHash['read'][$this->cache['mode']]);
        $this->cache[self::IS_WRITABLE] = isset(self::$readWriteHash['write'][$this->cache['mode']]);
    }
}
vendor/guzzle/guzzle/src/Guzzle/Stream/StreamInterface.php000064400000011070151327705700017776 0ustar00<?php

namespace Guzzle\Stream;

/**
 * OO interface to PHP streams
 */
interface StreamInterface
{
    /**
     * Convert the stream to a string if the stream is readable and the stream is seekable.
     *
     * @return string
     */
    public function __toString();

    /**
     * Close the underlying stream
     */
    public function close();

    /**
     * Get stream metadata
     *
     * @param string $key Specific metadata to retrieve
     *
     * @return array|mixed|null
     */
    public function getMetaData($key = null);

    /**
     * Get the stream resource
     *
     * @return resource
     */
    public function getStream();

    /**
     * Set the stream that is wrapped by the object
     *
     * @param resource $stream Stream resource to wrap
     * @param int      $size   Size of the stream in bytes. Only pass if the size cannot be obtained from the stream.
     *
     * @return self
     */
    public function setStream($stream, $size = null);

    /**
     * Detach the current stream resource
     *
     * @return self
     */
    public function detachStream();

    /**
     * Get the stream wrapper type
     *
     * @return string
     */
    public function getWrapper();

    /**
     * Wrapper specific data attached to this stream.
     *
     * @return array
     */
    public function getWrapperData();

    /**
     * Get a label describing the underlying implementation of the stream
     *
     * @return string
     */
    public function getStreamType();

    /**
     * Get the URI/filename associated with this stream
     *
     * @return string
     */
    public function getUri();

    /**
     * Get the size of the stream if able
     *
     * @return int|bool
     */
    public function getSize();

    /**
     * Check if the stream is readable
     *
     * @return bool
     */
    public function isReadable();

    /**
     * Check if the stream is repeatable
     *
     * @return bool
     */
    public function isRepeatable();

    /**
     * Check if the stream is writable
     *
     * @return bool
     */
    public function isWritable();

    /**
     * Check if the stream has been consumed
     *
     * @return bool
     */
    public function isConsumed();

    /**
     * Alias of isConsumed
     *
     * @return bool
     */
    public function feof();

    /**
     * Check if the stream is a local stream vs a remote stream
     *
     * @return bool
     */
    public function isLocal();

    /**
     * Check if the string is repeatable
     *
     * @return bool
     */
    public function isSeekable();

    /**
     * Specify the size of the stream in bytes
     *
     * @param int $size Size of the stream contents in bytes
     *
     * @return self
     */
    public function setSize($size);

    /**
     * Seek to a position in the stream
     *
     * @param int $offset Stream offset
     * @param int $whence Where the offset is applied
     *
     * @return bool Returns TRUE on success or FALSE on failure
     * @link   http://www.php.net/manual/en/function.fseek.php
     */
    public function seek($offset, $whence = SEEK_SET);

    /**
     * Read data from the stream
     *
     * @param int $length Up to length number of bytes read.
     *
     * @return string|bool Returns the data read from the stream or FALSE on failure or EOF
     */
    public function read($length);

    /**
     * Write data to the stream
     *
     * @param string $string The string that is to be written.
     *
     * @return int|bool Returns the number of bytes written to the stream on success or FALSE on failure.
     */
    public function write($string);

    /**
     * Returns the current position of the file read/write pointer
     *
     * @return int|bool Returns the position of the file pointer or false on error
     */
    public function ftell();

    /**
     * Rewind to the beginning of the stream
     *
     * @return bool Returns true on success or false on failure
     */
    public function rewind();

    /**
     * Read a line from the stream up to the maximum allowed buffer length
     *
     * @param int $maxLength Maximum buffer length
     *
     * @return string|bool
     */
    public function readLine($maxLength = null);

    /**
     * Set custom data on the stream
     *
     * @param string $key   Key to set
     * @param mixed  $value Value to set
     *
     * @return self
     */
    public function setCustomData($key, $value);

    /**
     * Get custom data from the stream
     *
     * @param string $key Key to retrieve
     *
     * @return null|mixed
     */
    public function getCustomData($key);
}
vendor/guzzle/guzzle/src/Guzzle/Stream/PhpStreamRequestFactory.php000064400000022404151327705700021531 0ustar00<?php

namespace Guzzle\Stream;

use Guzzle\Common\Exception\InvalidArgumentException;
use Guzzle\Common\Exception\RuntimeException;
use Guzzle\Http\Message\EntityEnclosingRequestInterface;
use Guzzle\Http\Message\RequestInterface;
use Guzzle\Http\Url;

/**
 * Factory used to create fopen streams using PHP's http and https stream wrappers
 *
 * Note: PHP's http stream wrapper only supports streaming downloads. It does not support streaming uploads.
 */
class PhpStreamRequestFactory implements StreamRequestFactoryInterface
{
    /** @var resource Stream context options */
    protected $context;

    /** @var array Stream context */
    protected $contextOptions;

    /** @var Url Stream URL */
    protected $url;

    /** @var array Last response headers received by the HTTP request */
    protected $lastResponseHeaders;

    /**
     * {@inheritdoc}
     *
     * The $params array can contain the following custom keys specific to the PhpStreamRequestFactory:
     * - stream_class: The name of a class to create instead of a Guzzle\Stream\Stream object
     */
    public function fromRequest(RequestInterface $request, $context = array(), array $params = array())
    {
        if (is_resource($context)) {
            $this->contextOptions = stream_context_get_options($context);
            $this->context = $context;
        } elseif (is_array($context) || !$context) {
            $this->contextOptions = $context;
            $this->createContext($params);
        } elseif ($context) {
            throw new InvalidArgumentException('$context must be an array or resource');
        }

        // Dispatch the before send event
        $request->dispatch('request.before_send', array(
            'request'         => $request,
            'context'         => $this->context,
            'context_options' => $this->contextOptions
        ));

        $this->setUrl($request);
        $this->addDefaultContextOptions($request);
        $this->addSslOptions($request);
        $this->addBodyOptions($request);
        $this->addProxyOptions($request);

        // Create the file handle but silence errors
        return $this->createStream($params)
            ->setCustomData('request', $request)
            ->setCustomData('response_headers', $this->getLastResponseHeaders());
    }

    /**
     * Set an option on the context and the internal options array
     *
     * @param string $wrapper   Stream wrapper name of http
     * @param string $name      Context name
     * @param mixed  $value     Context value
     * @param bool   $overwrite Set to true to overwrite an existing value
     */
    protected function setContextValue($wrapper, $name, $value, $overwrite = false)
    {
        if (!isset($this->contextOptions[$wrapper])) {
            $this->contextOptions[$wrapper] = array($name => $value);
        } elseif (!$overwrite && isset($this->contextOptions[$wrapper][$name])) {
            return;
        }
        $this->contextOptions[$wrapper][$name] = $value;
        stream_context_set_option($this->context, $wrapper, $name, $value);
    }

    /**
     * Create a stream context
     *
     * @param array $params Parameter array
     */
    protected function createContext(array $params)
    {
        $options = $this->contextOptions;
        $this->context = $this->createResource(function () use ($params, $options) {
            return stream_context_create($options, $params);
        });
    }

    /**
     * Get the last response headers received by the HTTP request
     *
     * @return array
     */
    public function getLastResponseHeaders()
    {
        return $this->lastResponseHeaders;
    }

    /**
     * Adds the default context options to the stream context options
     *
     * @param RequestInterface $request Request
     */
    protected function addDefaultContextOptions(RequestInterface $request)
    {
        $this->setContextValue('http', 'method', $request->getMethod());
        $headers = $request->getHeaderLines();

        // "Connection: close" is required to get streams to work in HTTP 1.1
        if (!$request->hasHeader('Connection')) {
            $headers[] = 'Connection: close';
        }

        $this->setContextValue('http', 'header', $headers);
        $this->setContextValue('http', 'protocol_version', $request->getProtocolVersion());
        $this->setContextValue('http', 'ignore_errors', true);
    }

    /**
     * Set the URL to use with the factory
     *
     * @param RequestInterface $request Request that owns the URL
     */
    protected function setUrl(RequestInterface $request)
    {
        $this->url = $request->getUrl(true);

        // Check for basic Auth username
        if ($request->getUsername()) {
            $this->url->setUsername($request->getUsername());
        }

        // Check for basic Auth password
        if ($request->getPassword()) {
            $this->url->setPassword($request->getPassword());
        }
    }

    /**
     * Add SSL options to the stream context
     *
     * @param RequestInterface $request Request
     */
    protected function addSslOptions(RequestInterface $request)
    {
        if ($request->getCurlOptions()->get(CURLOPT_SSL_VERIFYPEER)) {
            $this->setContextValue('ssl', 'verify_peer', true, true);
            if ($cafile = $request->getCurlOptions()->get(CURLOPT_CAINFO)) {
                $this->setContextValue('ssl', 'cafile', $cafile, true);
            }
        } else {
            $this->setContextValue('ssl', 'verify_peer', false, true);
        }
    }

    /**
     * Add body (content) specific options to the context options
     *
     * @param RequestInterface $request
     */
    protected function addBodyOptions(RequestInterface $request)
    {
        // Add the content for the request if needed
        if (!($request instanceof EntityEnclosingRequestInterface)) {
            return;
        }

        if (count($request->getPostFields())) {
            $this->setContextValue('http', 'content', (string) $request->getPostFields(), true);
        } elseif ($request->getBody()) {
            $this->setContextValue('http', 'content', (string) $request->getBody(), true);
        }

        // Always ensure a content-length header is sent
        if (isset($this->contextOptions['http']['content'])) {
            $headers = isset($this->contextOptions['http']['header']) ? $this->contextOptions['http']['header'] : array();
            $headers[] = 'Content-Length: ' . strlen($this->contextOptions['http']['content']);
            $this->setContextValue('http', 'header', $headers, true);
        }
    }

    /**
     * Add proxy parameters to the context if needed
     *
     * @param RequestInterface $request Request
     */
    protected function addProxyOptions(RequestInterface $request)
    {
        if ($proxy = $request->getCurlOptions()->get(CURLOPT_PROXY)) {
            $this->setContextValue('http', 'proxy', $proxy);
        }
    }

    /**
     * Create the stream for the request with the context options
     *
     * @param array $params Parameters of the stream
     *
     * @return StreamInterface
     */
    protected function createStream(array $params)
    {
        $http_response_header = null;
        $url = $this->url;
        $context = $this->context;
        $fp = $this->createResource(function () use ($context, $url, &$http_response_header) {
            return fopen((string) $url, 'r', false, $context);
        });

        // Determine the class to instantiate
        $className = isset($params['stream_class']) ? $params['stream_class'] : __NAMESPACE__ . '\\Stream';

        /** @var $stream StreamInterface */
        $stream = new $className($fp);

        // Track the response headers of the request
        if (isset($http_response_header)) {
            $this->lastResponseHeaders = $http_response_header;
            $this->processResponseHeaders($stream);
        }

        return $stream;
    }

    /**
     * Process response headers
     *
     * @param StreamInterface $stream
     */
    protected function processResponseHeaders(StreamInterface $stream)
    {
        // Set the size on the stream if it was returned in the response
        foreach ($this->lastResponseHeaders as $header) {
            if ((stripos($header, 'Content-Length:')) === 0) {
                $stream->setSize(trim(substr($header, 15)));
            }
        }
    }

    /**
     * Create a resource and check to ensure it was created successfully
     *
     * @param callable $callback Closure to invoke that must return a valid resource
     *
     * @return resource
     * @throws RuntimeException on error
     */
    protected function createResource($callback)
    {
        $errors = null;
        set_error_handler(function ($_, $msg, $file, $line) use (&$errors) {
            $errors[] = array(
                'message' => $msg,
                'file'    => $file,
                'line'    => $line
            );
            return true;
        });
        $resource = call_user_func($callback);
        restore_error_handler();

        if (!$resource) {
            $message = 'Error creating resource. ';
            foreach ($errors as $err) {
                foreach ($err as $key => $value) {
                    $message .= "[$key] $value" . PHP_EOL;
                }
            }
            throw new RuntimeException(trim($message));
        }

        return $resource;
    }
}
vendor/guzzle/guzzle/src/Guzzle/Stream/StreamRequestFactoryInterface.php000064400000001533151327705700022702 0ustar00<?php

namespace Guzzle\Stream;

use Guzzle\Http\Message\RequestInterface;

/**
 * Interface used for creating streams from requests
 */
interface StreamRequestFactoryInterface
{
    /**
     * Create a stream based on a request object
     *
     * @param RequestInterface $request Base the stream on a request
     * @param array|resource   $context A stream_context_options resource or array of parameters used to create a
     *                                  stream context.
     * @param array            $params  Optional array of parameters specific to the factory
     *
     * @return StreamInterface Returns a stream object
     * @throws \Guzzle\Common\Exception\RuntimeException if the stream cannot be opened or an error occurs
     */
    public function fromRequest(RequestInterface $request, $context = array(), array $params = array());
}
vendor/guzzle/guzzle/src/Guzzle/Iterator/MapIterator.php000064400000001524151327705700017512 0ustar00<?php

namespace Guzzle\Iterator;

use Guzzle\Common\Exception\InvalidArgumentException;

/**
 * Maps values before yielding
 */
class MapIterator extends \IteratorIterator
{
    /** @var mixed Callback */
    protected $callback;

    /**
     * @param \Traversable   $iterator Traversable iterator
     * @param array|\Closure $callback Callback used for iterating
     *
     * @throws InvalidArgumentException if the callback if not callable
     */
    public function __construct(\Traversable $iterator, $callback)
    {
        parent::__construct($iterator);
        if (!is_callable($callback)) {
            throw new InvalidArgumentException('The callback must be callable');
        }
        $this->callback = $callback;
    }

    public function current()
    {
        return call_user_func($this->callback, parent::current());
    }
}
vendor/guzzle/guzzle/src/Guzzle/Iterator/MethodProxyIterator.php000064400000001142151327705700021253 0ustar00<?php

namespace Guzzle\Iterator;

/**
 * Proxies missing method calls to the innermost iterator
 */
class MethodProxyIterator extends \IteratorIterator
{
    /**
     * Proxy method calls to the wrapped iterator
     *
     * @param string $name Name of the method
     * @param array  $args Arguments to proxy
     *
     * @return mixed
     */
    public function __call($name, array $args)
    {
        $i = $this->getInnerIterator();
        while ($i instanceof \OuterIterator) {
            $i = $i->getInnerIterator();
        }

        return call_user_func_array(array($i, $name), $args);
    }
}
vendor/guzzle/guzzle/src/Guzzle/Iterator/README.md000064400000001232151327705700016025 0ustar00Guzzle Iterator
===============

Provides useful Iterators and Iterator decorators

- ChunkedIterator: Pulls out chunks from an inner iterator and yields the chunks as arrays
- FilterIterator: Used when PHP 5.4's CallbackFilterIterator is not available
- MapIterator: Maps values before yielding
- MethodProxyIterator: Proxies missing method calls to the innermost iterator

### Installing via Composer

```bash
# Install Composer
curl -sS https://getcomposer.org/installer | php

# Add Guzzle as a dependency
php composer.phar require guzzle/iterator:~3.0
```

After installing, you need to require Composer's autoloader:

```php
require 'vendor/autoload.php';
```
vendor/guzzle/guzzle/src/Guzzle/Iterator/AppendIterator.php000064400000000674151327705700020211 0ustar00<?php

namespace Guzzle\Iterator;

/**
 * AppendIterator that is not affected by https://bugs.php.net/bug.php?id=49104
 */
class AppendIterator extends \AppendIterator
{
    /**
     * Works around the bug in which PHP calls rewind() and next() when appending
     *
     * @param \Iterator $iterator Iterator to append
     */
    public function append(\Iterator $iterator)
    {
        $this->getArrayIterator()->append($iterator);
    }
}
vendor/guzzle/guzzle/src/Guzzle/Iterator/FilterIterator.php000064400000001732151327705700020223 0ustar00<?php

namespace Guzzle\Iterator;

use Guzzle\Common\Exception\InvalidArgumentException;

/**
 * Filters values using a callback
 *
 * Used when PHP 5.4's {@see \CallbackFilterIterator} is not available
 */
class FilterIterator extends \FilterIterator
{
    /** @var mixed Callback used for filtering */
    protected $callback;

    /**
     * @param \Iterator      $iterator Traversable iterator
     * @param array|\Closure $callback Callback used for filtering. Return true to keep or false to filter.
     *
     * @throws InvalidArgumentException if the callback if not callable
     */
    public function __construct(\Iterator $iterator, $callback)
    {
        parent::__construct($iterator);
        if (!is_callable($callback)) {
            throw new InvalidArgumentException('The callback must be callable');
        }
        $this->callback = $callback;
    }

    public function accept()
    {
        return call_user_func($this->callback, $this->current());
    }
}
vendor/guzzle/guzzle/src/Guzzle/Iterator/composer.json000064400000001246151327705700017275 0ustar00{
    "name": "guzzle/iterator",
    "description": "Provides helpful iterators and iterator decorators",
    "keywords": ["iterator", "guzzle"],
    "homepage": "http://guzzlephp.org/",
    "license": "MIT",
    "authors": [
        {
            "name": "Michael Dowling",
            "email": "mtdowling@gmail.com",
            "homepage": "https://github.com/mtdowling"
        }
    ],
    "require": {
        "php": ">=5.3.2",
        "guzzle/common": ">=2.8.0"
    },
    "autoload": {
        "psr-0": { "Guzzle\\Iterator": "/" }
    },
    "target-dir": "Guzzle/Iterator",
    "extra": {
        "branch-alias": {
            "dev-master": "3.7-dev"
        }
    }
}
vendor/guzzle/guzzle/src/Guzzle/Iterator/ChunkedIterator.php000064400000002426151327705700020360 0ustar00<?php

namespace Guzzle\Iterator;

/**
 * Pulls out chunks from an inner iterator and yields the chunks as arrays
 */
class ChunkedIterator extends \IteratorIterator
{
    /** @var int Size of each chunk */
    protected $chunkSize;

    /** @var array Current chunk */
    protected $chunk;

    /**
     * @param \Traversable $iterator  Traversable iterator
     * @param int          $chunkSize Size to make each chunk
     * @throws \InvalidArgumentException
     */
    public function __construct(\Traversable $iterator, $chunkSize)
    {
        $chunkSize = (int) $chunkSize;
        if ($chunkSize < 0 ) {
            throw new \InvalidArgumentException("The chunk size must be equal or greater than zero; $chunkSize given");
        }

        parent::__construct($iterator);
        $this->chunkSize = $chunkSize;
    }

    public function rewind()
    {
        parent::rewind();
        $this->next();
    }

    public function next()
    {
        $this->chunk = array();
        for ($i = 0; $i < $this->chunkSize && parent::valid(); $i++) {
            $this->chunk[] = parent::current();
            parent::next();
        }
    }

    public function current()
    {
        return $this->chunk;
    }

    public function valid()
    {
        return (bool) $this->chunk;
    }
}
vendor/guzzle/guzzle/src/Guzzle/Plugin/Md5/Md5ValidatorPlugin.php000064400000006565151327705700021041 0ustar00<?php

namespace Guzzle\Plugin\Md5;

use Guzzle\Common\Event;
use Guzzle\Common\Exception\UnexpectedValueException;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;

/**
 * Ensures that an the MD5 hash of an entity body matches the Content-MD5
 * header (if set) of an HTTP response.  An exception is thrown if the
 * calculated MD5 does not match the expected MD5.
 */
class Md5ValidatorPlugin implements EventSubscriberInterface
{
    /** @var int Maximum Content-Length in bytes to validate */
    protected $contentLengthCutoff;

    /** @var bool Whether or not to compare when a Content-Encoding is present */
    protected $contentEncoded;

    /**
     * @param bool     $contentEncoded      Calculating the MD5 hash of an entity body where a Content-Encoding was
     *                                      applied is a more expensive comparison because the entity body will need to
     *                                      be compressed in order to get the correct hash.  Set to FALSE to not
     *                                      validate the MD5 hash of an entity body with an applied Content-Encoding.
     * @param bool|int $contentLengthCutoff Maximum Content-Length (bytes) in which a MD5 hash will be validated. Any
     *                                      response with a Content-Length greater than this value will not be validated
     *                                      because it will be deemed too memory intensive.
     */
    public function __construct($contentEncoded = true, $contentLengthCutoff = false)
    {
        $this->contentLengthCutoff = $contentLengthCutoff;
        $this->contentEncoded = $contentEncoded;
    }

    public static function getSubscribedEvents()
    {
        return array('request.complete' => array('onRequestComplete', 255));
    }

    /**
     * {@inheritdoc}
     * @throws UnexpectedValueException
     */
    public function onRequestComplete(Event $event)
    {
        $response = $event['response'];

        if (!$contentMd5 = $response->getContentMd5()) {
            return;
        }

        $contentEncoding = $response->getContentEncoding();
        if ($contentEncoding && !$this->contentEncoded) {
            return false;
        }

        // Make sure that the size of the request is under the cutoff size
        if ($this->contentLengthCutoff) {
            $size = $response->getContentLength() ?: $response->getBody()->getSize();
            if (!$size || $size > $this->contentLengthCutoff) {
                return;
            }
        }

        if (!$contentEncoding) {
            $hash = $response->getBody()->getContentMd5();
        } elseif ($contentEncoding == 'gzip') {
            $response->getBody()->compress('zlib.deflate');
            $hash = $response->getBody()->getContentMd5();
            $response->getBody()->uncompress();
        } elseif ($contentEncoding == 'compress') {
            $response->getBody()->compress('bzip2.compress');
            $hash = $response->getBody()->getContentMd5();
            $response->getBody()->uncompress();
        } else {
            return;
        }

        if ($contentMd5 !== $hash) {
            throw new UnexpectedValueException(
                "The response entity body may have been modified over the wire.  The Content-MD5 "
                . "received ({$contentMd5}) did not match the calculated MD5 hash ({$hash})."
            );
        }
    }
}
vendor/guzzle/guzzle/src/Guzzle/Plugin/Md5/CommandContentMd5Plugin.php000064400000004235151327705700022015 0ustar00<?php

namespace Guzzle\Plugin\Md5;

use Guzzle\Common\Event;
use Guzzle\Http\Message\EntityEnclosingRequestInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;

/**
 * Listener used to add a ContentMD5 header to the body of a command and adds ContentMD5 validation if the
 * ValidateMD5 option is not set to false on a command
 */
class CommandContentMd5Plugin  implements EventSubscriberInterface
{
    /** @var string Parameter used to check if the ContentMD5 value is being added */
    protected $contentMd5Param;

    /** @var string Parameter used to check if validation should occur on the response */
    protected $validateMd5Param;

    /**
     * @param string $contentMd5Param  Parameter used to check if the ContentMD5 value is being added
     * @param string $validateMd5Param Parameter used to check if validation should occur on the response
     */
    public function __construct($contentMd5Param = 'ContentMD5', $validateMd5Param = 'ValidateMD5')
    {
        $this->contentMd5Param = $contentMd5Param;
        $this->validateMd5Param = $validateMd5Param;
    }

    public static function getSubscribedEvents()
    {
        return array('command.before_send' => array('onCommandBeforeSend', -255));
    }

    public function onCommandBeforeSend(Event $event)
    {
        $command = $event['command'];
        $request = $command->getRequest();

        // Only add an MD5 is there is a MD5 option on the operation and it has a payload
        if ($request instanceof EntityEnclosingRequestInterface && $request->getBody()
            && $command->getOperation()->hasParam($this->contentMd5Param)) {
            // Check if an MD5 checksum value should be passed along to the request
            if ($command[$this->contentMd5Param] === true) {
                if (false !== ($md5 = $request->getBody()->getContentMd5(true, true))) {
                    $request->setHeader('Content-MD5', $md5);
                }
            }
        }

        // Check if MD5 validation should be used with the response
        if ($command[$this->validateMd5Param] === true) {
            $request->addSubscriber(new Md5ValidatorPlugin(true, false));
        }
    }
}
vendor/guzzle/guzzle/src/Guzzle/Plugin/Md5/composer.json000064400000001215151327705700017363 0ustar00{
    "name": "guzzle/plugin-md5",
    "description": "Guzzle MD5 plugins",
    "homepage": "http://guzzlephp.org/",
    "keywords": ["plugin", "guzzle"],
    "license": "MIT",
    "authors": [
        {
            "name": "Michael Dowling",
            "email": "mtdowling@gmail.com",
            "homepage": "https://github.com/mtdowling"
        }
    ],
    "require": {
        "php": ">=5.3.2",
        "guzzle/http": "self.version"
    },
    "autoload": {
        "psr-0": { "Guzzle\\Plugin\\Md5": "" }
    },
    "target-dir": "Guzzle/Plugin/Md5",
    "extra": {
        "branch-alias": {
            "dev-master": "3.7-dev"
        }
    }
}
vendor/guzzle/guzzle/src/Guzzle/Plugin/Log/LogPlugin.php000064400000013375151327705700017360 0ustar00<?php

namespace Guzzle\Plugin\Log;

use Guzzle\Common\Event;
use Guzzle\Log\LogAdapterInterface;
use Guzzle\Log\MessageFormatter;
use Guzzle\Log\ClosureLogAdapter;
use Guzzle\Http\EntityBody;
use Guzzle\Http\Message\EntityEnclosingRequestInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;

/**
 * Plugin class that will add request and response logging to an HTTP request.
 *
 * The log plugin uses a message formatter that allows custom messages via template variable substitution.
 *
 * @see MessageLogger for a list of available log template variable substitutions
 */
class LogPlugin implements EventSubscriberInterface
{
    /** @var LogAdapterInterface Adapter responsible for writing log data */
    protected $logAdapter;

    /** @var MessageFormatter Formatter used to format messages before logging */
    protected $formatter;

    /** @var bool Whether or not to wire request and response bodies */
    protected $wireBodies;

    /**
     * @param LogAdapterInterface     $logAdapter Adapter object used to log message
     * @param string|MessageFormatter $formatter  Formatter used to format log messages or the formatter template
     * @param bool                    $wireBodies Set to true to track request and response bodies using a temporary
     *                                            buffer if the bodies are not repeatable.
     */
    public function __construct(
        LogAdapterInterface $logAdapter,
        $formatter = null,
        $wireBodies = false
    ) {
        $this->logAdapter = $logAdapter;
        $this->formatter = $formatter instanceof MessageFormatter ? $formatter : new MessageFormatter($formatter);
        $this->wireBodies = $wireBodies;
    }

    /**
     * Get a log plugin that outputs full request, response, and curl error information to stderr
     *
     * @param bool     $wireBodies Set to false to disable request/response body output when they use are not repeatable
     * @param resource $stream     Stream to write to when logging. Defaults to STDERR when it is available
     *
     * @return self
     */
    public static function getDebugPlugin($wireBodies = true, $stream = null)
    {
        if ($stream === null) {
            if (defined('STDERR')) {
                $stream = STDERR;
            } else {
                $stream = fopen('php://output', 'w');
            }
        }

        return new self(new ClosureLogAdapter(function ($m) use ($stream) {
            fwrite($stream, $m . PHP_EOL);
        }), "# Request:\n{request}\n\n# Response:\n{response}\n\n# Errors: {curl_code} {curl_error}", $wireBodies);
    }

    public static function getSubscribedEvents()
    {
        return array(
            'curl.callback.write' => array('onCurlWrite', 255),
            'curl.callback.read'  => array('onCurlRead', 255),
            'request.before_send' => array('onRequestBeforeSend', 255),
            'request.sent'        => array('onRequestSent', 255)
        );
    }

    /**
     * Event triggered when curl data is read from a request
     *
     * @param Event $event
     */
    public function onCurlRead(Event $event)
    {
        // Stream the request body to the log if the body is not repeatable
        if ($wire = $event['request']->getParams()->get('request_wire')) {
            $wire->write($event['read']);
        }
    }

    /**
     * Event triggered when curl data is written to a response
     *
     * @param Event $event
     */
    public function onCurlWrite(Event $event)
    {
        // Stream the response body to the log if the body is not repeatable
        if ($wire = $event['request']->getParams()->get('response_wire')) {
            $wire->write($event['write']);
        }
    }

    /**
     * Called before a request is sent
     *
     * @param Event $event
     */
    public function onRequestBeforeSend(Event $event)
    {
        if ($this->wireBodies) {
            $request = $event['request'];
            // Ensure that curl IO events are emitted
            $request->getCurlOptions()->set('emit_io', true);
            // We need to make special handling for content wiring and non-repeatable streams.
            if ($request instanceof EntityEnclosingRequestInterface && $request->getBody()
                && (!$request->getBody()->isSeekable() || !$request->getBody()->isReadable())
            ) {
                // The body of the request cannot be recalled so logging the body will require us to buffer it
                $request->getParams()->set('request_wire', EntityBody::factory());
            }
            if (!$request->getResponseBody()->isRepeatable()) {
                // The body of the response cannot be recalled so logging the body will require us to buffer it
                $request->getParams()->set('response_wire', EntityBody::factory());
            }
        }
    }

    /**
     * Triggers the actual log write when a request completes
     *
     * @param Event $event
     */
    public function onRequestSent(Event $event)
    {
        $request = $event['request'];
        $response = $event['response'];
        $handle = $event['handle'];

        if ($wire = $request->getParams()->get('request_wire')) {
            $request = clone $request;
            $request->setBody($wire);
        }

        if ($wire = $request->getParams()->get('response_wire')) {
            $response = clone $response;
            $response->setBody($wire);
        }

        // Send the log message to the adapter, adding a category and host
        $priority = $response && $response->isError() ? LOG_ERR : LOG_DEBUG;
        $message = $this->formatter->format($request, $response, $handle);
        $this->logAdapter->log($message, $priority, array(
            'request'  => $request,
            'response' => $response,
            'handle'   => $handle
        ));
    }
}
vendor/guzzle/guzzle/src/Guzzle/Plugin/Log/composer.json000064400000001323151327705700017457 0ustar00{
    "name": "guzzle/plugin-log",
    "description": "Guzzle log plugin for over the wire logging",
    "homepage": "http://guzzlephp.org/",
    "keywords": ["plugin", "log", "guzzle"],
    "license": "MIT",
    "authors": [
        {
            "name": "Michael Dowling",
            "email": "mtdowling@gmail.com",
            "homepage": "https://github.com/mtdowling"
        }
    ],
    "require": {
        "php": ">=5.3.2",
        "guzzle/http": "self.version",
        "guzzle/log": "self.version"
    },
    "autoload": {
        "psr-0": { "Guzzle\\Plugin\\Log": "" }
    },
    "target-dir": "Guzzle/Plugin/Log",
    "extra": {
        "branch-alias": {
            "dev-master": "3.7-dev"
        }
    }
}
vendor/guzzle/guzzle/src/Guzzle/Plugin/Oauth/OauthPlugin.php000064400000024050151327705700020246 0ustar00<?php

namespace Guzzle\Plugin\Oauth;

use Guzzle\Common\Event;
use Guzzle\Common\Collection;
use Guzzle\Http\Message\RequestInterface;
use Guzzle\Http\Message\EntityEnclosingRequestInterface;
use Guzzle\Http\QueryString;
use Guzzle\Http\Url;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;

/**
 * OAuth signing plugin
 * @link http://oauth.net/core/1.0/#rfc.section.9.1.1
 */
class OauthPlugin implements EventSubscriberInterface
{
    /**
     * Consumer request method constants. See http://oauth.net/core/1.0/#consumer_req_param
     */
    const REQUEST_METHOD_HEADER = 'header';
    const REQUEST_METHOD_QUERY  = 'query';

    /** @var Collection Configuration settings */
    protected $config;

    /**
     * Create a new OAuth 1.0 plugin
     *
     * @param array $config Configuration array containing these parameters:
     *     - string 'request_method'       Consumer request method. Use the class constants.
     *     - string 'callback'             OAuth callback
     *     - string 'consumer_key'         Consumer key
     *     - string 'consumer_secret'      Consumer secret
     *     - string 'token'                Token
     *     - string 'token_secret'         Token secret
     *     - string 'verifier'             OAuth verifier.
     *     - string 'version'              OAuth version.  Defaults to 1.0
     *     - string 'signature_method'     Custom signature method
     *     - bool   'disable_post_params'  Set to true to prevent POST parameters from being signed
     *     - array|Closure 'signature_callback' Custom signature callback that accepts a string to sign and a signing key
     */
    public function __construct($config)
    {
        $this->config = Collection::fromConfig($config, array(
            'version' => '1.0',
            'request_method' => self::REQUEST_METHOD_HEADER,
            'consumer_key' => 'anonymous',
            'consumer_secret' => 'anonymous',
            'signature_method' => 'HMAC-SHA1',
            'signature_callback' => function($stringToSign, $key) {
                return hash_hmac('sha1', $stringToSign, $key, true);
            }
        ), array(
            'signature_method', 'signature_callback', 'version',
            'consumer_key', 'consumer_secret'
        ));
    }

    public static function getSubscribedEvents()
    {
        return array(
            'request.before_send' => array('onRequestBeforeSend', -1000)
        );
    }

    /**
     * Request before-send event handler
     *
     * @param Event $event Event received
     * @return array
     * @throws \InvalidArgumentException
     */
    public function onRequestBeforeSend(Event $event)
    {
        $timestamp = $this->getTimestamp($event);
        $request = $event['request'];
        $nonce = $this->generateNonce($request);
        $authorizationParams = $this->getOauthParams($timestamp, $nonce);
        $authorizationParams['oauth_signature']  = $this->getSignature($request, $timestamp, $nonce);

        switch ($this->config['request_method']) {
            case self::REQUEST_METHOD_HEADER:
                $request->setHeader(
                    'Authorization',
                    $this->buildAuthorizationHeader($authorizationParams)
                );
                break;
            case self::REQUEST_METHOD_QUERY:
                foreach ($authorizationParams as $key => $value) {
                    $request->getQuery()->set($key, $value);
                }
                break;
            default:
                throw new \InvalidArgumentException(sprintf(
                    'Invalid consumer method "%s"',
                    $this->config['request_method']
                ));
        }

        return $authorizationParams;
    }

    /**
     * Builds the Authorization header for a request
     *
     * @param array $authorizationParams Associative array of authorization parameters
     *
     * @return string
     */
    private function buildAuthorizationHeader($authorizationParams)
    {
        $authorizationString = 'OAuth ';
        foreach ($authorizationParams as $key => $val) {
            if ($val) {
                $authorizationString .= $key . '="' . urlencode($val) . '", ';
            }
        }

        return substr($authorizationString, 0, -2);
    }

    /**
     * Calculate signature for request
     *
     * @param RequestInterface $request   Request to generate a signature for
     * @param integer          $timestamp Timestamp to use for nonce
     * @param string           $nonce
     *
     * @return string
     */
    public function getSignature(RequestInterface $request, $timestamp, $nonce)
    {
        $string = $this->getStringToSign($request, $timestamp, $nonce);
        $key = urlencode($this->config['consumer_secret']) . '&' . urlencode($this->config['token_secret']);

        return base64_encode(call_user_func($this->config['signature_callback'], $string, $key));
    }

    /**
     * Calculate string to sign
     *
     * @param RequestInterface $request   Request to generate a signature for
     * @param int              $timestamp Timestamp to use for nonce
     * @param string           $nonce
     *
     * @return string
     */
    public function getStringToSign(RequestInterface $request, $timestamp, $nonce)
    {
        $params = $this->getParamsToSign($request, $timestamp, $nonce);

        // Convert booleans to strings.
        $params = $this->prepareParameters($params);

        // Build signing string from combined params
        $parameterString = clone $request->getQuery();
        $parameterString->replace($params);

        $url = Url::factory($request->getUrl())->setQuery('')->setFragment(null);

        return strtoupper($request->getMethod()) . '&'
             . rawurlencode($url) . '&'
             . rawurlencode((string) $parameterString);
    }

    /**
     * Get the oauth parameters as named by the oauth spec
     *
     * @param $timestamp
     * @param $nonce
     * @return Collection
     */
    protected function getOauthParams($timestamp, $nonce)
    {
        $params = new Collection(array(
            'oauth_consumer_key'     => $this->config['consumer_key'],
            'oauth_nonce'            => $nonce,
            'oauth_signature_method' => $this->config['signature_method'],
            'oauth_timestamp'        => $timestamp,
        ));

        // Optional parameters should not be set if they have not been set in the config as
        // the parameter may be considered invalid by the Oauth service.
        $optionalParams = array(
            'callback'  => 'oauth_callback',
            'token'     => 'oauth_token',
            'verifier'  => 'oauth_verifier',
            'version'   => 'oauth_version'
        );

        foreach ($optionalParams as $optionName => $oauthName) {
            if (isset($this->config[$optionName]) == true) {
                $params[$oauthName] = $this->config[$optionName];
            }
        }

        return $params;
    }

    /**
     * Get all of the parameters required to sign a request including:
     * * The oauth params
     * * The request GET params
     * * The params passed in the POST body (with a content-type of application/x-www-form-urlencoded)
     *
     * @param RequestInterface $request   Request to generate a signature for
     * @param integer          $timestamp Timestamp to use for nonce
     * @param string           $nonce
     *
     * @return array
     */
    public function getParamsToSign(RequestInterface $request, $timestamp, $nonce)
    {
        $params = $this->getOauthParams($timestamp, $nonce);

        // Add query string parameters
        $params->merge($request->getQuery());

        // Add POST fields to signing string if required
        if ($this->shouldPostFieldsBeSigned($request))
        {
            $params->merge($request->getPostFields());
        }

        // Sort params
        $params = $params->toArray();
        uksort($params, 'strcmp');

        return $params;
    }

    /**
     * Decide whether the post fields should be added to the base string that Oauth signs.
     * This implementation is correct. Non-conformant APIs may require that this method be
     * overwritten e.g. the Flickr API incorrectly adds the post fields when the Content-Type
     * is 'application/x-www-form-urlencoded'
     *
     * @param $request
     * @return bool Whether the post fields should be signed or not
     */
    public function shouldPostFieldsBeSigned($request)
    {
        if (!$this->config->get('disable_post_params') &&
            $request instanceof EntityEnclosingRequestInterface &&
            false !== strpos($request->getHeader('Content-Type'), 'application/x-www-form-urlencoded'))
        {
            return true;
        }

        return false;
    }

    /**
     * Returns a Nonce Based on the unique id and URL. This will allow for multiple requests in parallel with the same
     * exact timestamp to use separate nonce's.
     *
     * @param RequestInterface $request Request to generate a nonce for
     *
     * @return string
     */
    public function generateNonce(RequestInterface $request)
    {
        return sha1(uniqid('', true) . $request->getUrl());
    }

    /**
     * Gets timestamp from event or create new timestamp
     *
     * @param Event $event Event containing contextual information
     *
     * @return int
     */
    public function getTimestamp(Event $event)
    {
       return $event['timestamp'] ?: time();
    }

    /**
     * Convert booleans to strings, removed unset parameters, and sorts the array
     *
     * @param array $data Data array
     *
     * @return array
     */
    protected function prepareParameters($data)
    {
        ksort($data);
        foreach ($data as $key => &$value) {
            switch (gettype($value)) {
                case 'NULL':
                    unset($data[$key]);
                    break;
                case 'array':
                    $data[$key] = self::prepareParameters($value);
                    break;
                case 'boolean':
                    $data[$key] = $value ? 'true' : 'false';
                    break;
            }
        }

        return $data;
    }
}
vendor/guzzle/guzzle/src/Guzzle/Plugin/Oauth/composer.json000064400000001235151327705700020020 0ustar00{
    "name": "guzzle/plugin-oauth",
    "description": "Guzzle OAuth plugin",
    "homepage": "http://guzzlephp.org/",
    "keywords": ["oauth", "plugin", "guzzle"],
    "license": "MIT",
    "authors": [
        {
            "name": "Michael Dowling",
            "email": "mtdowling@gmail.com",
            "homepage": "https://github.com/mtdowling"
        }
    ],
    "require": {
        "php": ">=5.3.2",
        "guzzle/http": "self.version"
    },
    "autoload": {
        "psr-0": { "Guzzle\\Plugin\\Oauth": "" }
    },
    "target-dir": "Guzzle/Plugin/Oauth",
    "extra": {
        "branch-alias": {
            "dev-master": "3.7-dev"
        }
    }
}
vendor/guzzle/guzzle/src/Guzzle/Plugin/History/HistoryPlugin.php000064400000007521151327705700021214 0ustar00<?php

namespace Guzzle\Plugin\History;

use Guzzle\Common\Event;
use Guzzle\Http\Message\RequestInterface;
use Guzzle\Http\Message\Response;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;

/**
 * Maintains a list of requests and responses sent using a request or client
 */
class HistoryPlugin implements EventSubscriberInterface, \IteratorAggregate, \Countable
{
    /** @var int The maximum number of requests to maintain in the history */
    protected $limit = 10;

    /** @var array Requests and responses that have passed through the plugin */
    protected $transactions = array();

    public static function getSubscribedEvents()
    {
        return array('request.sent' => array('onRequestSent', 9999));
    }

    /**
     * Convert to a string that contains all request and response headers
     *
     * @return string
     */
    public function __toString()
    {
        $lines = array();
        foreach ($this->transactions as $entry) {
            $response = isset($entry['response']) ? $entry['response'] : '';
            $lines[] = '> ' . trim($entry['request']) . "\n\n< " . trim($response) . "\n";
        }

        return implode("\n", $lines);
    }

    /**
     * Add a request to the history
     *
     * @param RequestInterface $request  Request to add
     * @param Response         $response Response of the request
     *
     * @return HistoryPlugin
     */
    public function add(RequestInterface $request, Response $response = null)
    {
        if (!$response && $request->getResponse()) {
            $response = $request->getResponse();
        }

        $this->transactions[] = array('request' => $request, 'response' => $response);
        if (count($this->transactions) > $this->getlimit()) {
            array_shift($this->transactions);
        }

        return $this;
    }

    /**
     * Set the max number of requests to store
     *
     * @param int $limit Limit
     *
     * @return HistoryPlugin
     */
    public function setLimit($limit)
    {
        $this->limit = (int) $limit;

        return $this;
    }

    /**
     * Get the request limit
     *
     * @return int
     */
    public function getLimit()
    {
        return $this->limit;
    }

    /**
     * Get all of the raw transactions in the form of an array of associative arrays containing
     * 'request' and 'response' keys.
     *
     * @return array
     */
    public function getAll()
    {
        return $this->transactions;
    }

    /**
     * Get the requests in the history
     *
     * @return \ArrayIterator
     */
    public function getIterator()
    {
        // Return an iterator just like the old iteration of the HistoryPlugin for BC compatibility (use getAll())
        return new \ArrayIterator(array_map(function ($entry) {
            $entry['request']->getParams()->set('actual_response', $entry['response']);
            return $entry['request'];
        }, $this->transactions));
    }

    /**
     * Get the number of requests in the history
     *
     * @return int
     */
    public function count()
    {
        return count($this->transactions);
    }

    /**
     * Get the last request sent
     *
     * @return RequestInterface
     */
    public function getLastRequest()
    {
        $last = end($this->transactions);

        return $last['request'];
    }

    /**
     * Get the last response in the history
     *
     * @return Response|null
     */
    public function getLastResponse()
    {
        $last = end($this->transactions);

        return isset($last['response']) ? $last['response'] : null;
    }

    /**
     * Clears the history
     *
     * @return HistoryPlugin
     */
    public function clear()
    {
        $this->transactions = array();

        return $this;
    }

    public function onRequestSent(Event $event)
    {
        $this->add($event['request'], $event['response']);
    }
}
vendor/guzzle/guzzle/src/Guzzle/Plugin/History/composer.json000064400000001234151327705700020400 0ustar00{
    "name": "guzzle/plugin-history",
    "description": "Guzzle history plugin",
    "homepage": "http://guzzlephp.org/",
    "keywords": ["plugin", "guzzle"],
    "license": "MIT",
    "authors": [
        {
            "name": "Michael Dowling",
            "email": "mtdowling@gmail.com",
            "homepage": "https://github.com/mtdowling"
        }
    ],
    "require": {
        "php": ">=5.3.2",
        "guzzle/http": "self.version"
    },
    "autoload": {
        "psr-0": { "Guzzle\\Plugin\\History": "" }
    },
    "target-dir": "Guzzle/Plugin/History",
    "extra": {
        "branch-alias": {
            "dev-master": "3.7-dev"
        }
    }
}
vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/CachePlugin.php000064400000031472151327705700020122 0ustar00<?php

namespace Guzzle\Plugin\Cache;

use Guzzle\Cache\CacheAdapterFactory;
use Guzzle\Cache\CacheAdapterInterface;
use Guzzle\Common\Event;
use Guzzle\Common\Exception\InvalidArgumentException;
use Guzzle\Common\Version;
use Guzzle\Http\Message\RequestInterface;
use Guzzle\Http\Message\Response;
use Guzzle\Cache\DoctrineCacheAdapter;
use Guzzle\Http\Exception\CurlException;
use Doctrine\Common\Cache\ArrayCache;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;

/**
 * Plugin to enable the caching of GET and HEAD requests.  Caching can be done on all requests passing through this
 * plugin or only after retrieving resources with cacheable response headers.
 *
 * This is a simple implementation of RFC 2616 and should be considered a private transparent proxy cache, meaning
 * authorization and private data can be cached.
 *
 * It also implements RFC 5861's `stale-if-error` Cache-Control extension, allowing stale cache responses to be used
 * when an error is encountered (such as a `500 Internal Server Error` or DNS failure).
 */
class CachePlugin implements EventSubscriberInterface
{
    /** @var RevalidationInterface Cache revalidation strategy */
    protected $revalidation;

    /** @var CanCacheStrategyInterface Object used to determine if a request can be cached */
    protected $canCache;

    /** @var CacheStorageInterface $cache Object used to cache responses */
    protected $storage;

    /** @var bool */
    protected $autoPurge;

    /**
     * @param array|CacheAdapterInterface|CacheStorageInterface $options Array of options for the cache plugin,
     *     cache adapter, or cache storage object.
     *     - CacheStorageInterface storage:      Adapter used to cache responses
     *     - RevalidationInterface revalidation: Cache revalidation strategy
     *     - CanCacheInterface     can_cache:    Object used to determine if a request can be cached
     *     - bool                  auto_purge    Set to true to automatically PURGE resources when non-idempotent
     *                                           requests are sent to a resource. Defaults to false.
     * @throws InvalidArgumentException if no cache is provided and Doctrine cache is not installed
     */
    public function __construct($options = null)
    {
        if (!is_array($options)) {
            if ($options instanceof CacheAdapterInterface) {
                $options = array('storage' => new DefaultCacheStorage($options));
            } elseif ($options instanceof CacheStorageInterface) {
                $options = array('storage' => $options);
            } elseif ($options) {
                $options = array('storage' => new DefaultCacheStorage(CacheAdapterFactory::fromCache($options)));
            } elseif (!class_exists('Doctrine\Common\Cache\ArrayCache')) {
                // @codeCoverageIgnoreStart
                throw new InvalidArgumentException('No cache was provided and Doctrine is not installed');
                // @codeCoverageIgnoreEnd
            }
        }

        $this->autoPurge = isset($options['auto_purge']) ? $options['auto_purge'] : false;

        // Add a cache storage if a cache adapter was provided
        $this->storage = isset($options['storage'])
            ? $options['storage']
            : new DefaultCacheStorage(new DoctrineCacheAdapter(new ArrayCache()));

        if (!isset($options['can_cache'])) {
            $this->canCache = new DefaultCanCacheStrategy();
        } else {
            $this->canCache = is_callable($options['can_cache'])
                ? new CallbackCanCacheStrategy($options['can_cache'])
                : $options['can_cache'];
        }

        // Use the provided revalidation strategy or the default
        $this->revalidation = isset($options['revalidation'])
            ? $options['revalidation']
            : new DefaultRevalidation($this->storage, $this->canCache);
    }

    public static function getSubscribedEvents()
    {
        return array(
            'request.before_send' => array('onRequestBeforeSend', -255),
            'request.sent'        => array('onRequestSent', 255),
            'request.error'       => array('onRequestError', 0),
            'request.exception'   => array('onRequestException', 0),
        );
    }

    /**
     * Check if a response in cache will satisfy the request before sending
     *
     * @param Event $event
     */
    public function onRequestBeforeSend(Event $event)
    {
        $request = $event['request'];
        $request->addHeader('Via', sprintf('%s GuzzleCache/%s', $request->getProtocolVersion(), Version::VERSION));

        if (!$this->canCache->canCacheRequest($request)) {
            switch ($request->getMethod()) {
                case 'PURGE':
                    $this->purge($request);
                    $request->setResponse(new Response(200, array(), 'purged'));
                    break;
                case 'PUT':
                case 'POST':
                case 'DELETE':
                case 'PATCH':
                    if ($this->autoPurge) {
                        $this->purge($request);
                    }
            }
            return;
        }

        if ($response = $this->storage->fetch($request)) {
            $params = $request->getParams();
            $params['cache.lookup'] = true;
            $response->setHeader(
                'Age',
                time() - strtotime($response->getDate() ? : $response->getLastModified() ?: 'now')
            );
            // Validate that the response satisfies the request
            if ($this->canResponseSatisfyRequest($request, $response)) {
                if (!isset($params['cache.hit'])) {
                    $params['cache.hit'] = true;
                }
                $request->setResponse($response);
            }
        }
    }

    /**
     * If possible, store a response in cache after sending
     *
     * @param Event $event
     */
    public function onRequestSent(Event $event)
    {
        $request = $event['request'];
        $response = $event['response'];

        if ($request->getParams()->get('cache.hit') === null &&
            $this->canCache->canCacheRequest($request) &&
            $this->canCache->canCacheResponse($response)
        ) {
            $this->storage->cache($request, $response);
        }

        $this->addResponseHeaders($request, $response);
    }

    /**
     * If possible, return a cache response on an error
     *
     * @param Event $event
     */
    public function onRequestError(Event $event)
    {
        $request = $event['request'];

        if (!$this->canCache->canCacheRequest($request)) {
            return;
        }

        if ($response = $this->storage->fetch($request)) {
            $response->setHeader(
                'Age',
                time() - strtotime($response->getLastModified() ? : $response->getDate() ?: 'now')
            );

            if ($this->canResponseSatisfyFailedRequest($request, $response)) {
                $request->getParams()->set('cache.hit', 'error');
                $this->addResponseHeaders($request, $response);
                $event['response'] = $response;
                $event->stopPropagation();
            }
        }
    }

    /**
     * If possible, set a cache response on a cURL exception
     *
     * @param Event $event
     *
     * @return null
     */
    public function onRequestException(Event $event)
    {
        if (!$event['exception'] instanceof CurlException) {
            return;
        }

        $request = $event['request'];
        if (!$this->canCache->canCacheRequest($request)) {
            return;
        }

        if ($response = $this->storage->fetch($request)) {
            $response->setHeader('Age', time() - strtotime($response->getDate() ? : 'now'));
            if (!$this->canResponseSatisfyFailedRequest($request, $response)) {
                return;
            }
            $request->getParams()->set('cache.hit', 'error');
            $request->setResponse($response);
            $this->addResponseHeaders($request, $response);
            $event->stopPropagation();
        }
    }

    /**
     * Check if a cache response satisfies a request's caching constraints
     *
     * @param RequestInterface $request  Request to validate
     * @param Response         $response Response to validate
     *
     * @return bool
     */
    public function canResponseSatisfyRequest(RequestInterface $request, Response $response)
    {
        $responseAge = $response->calculateAge();
        $reqc = $request->getHeader('Cache-Control');
        $resc = $response->getHeader('Cache-Control');

        // Check the request's max-age header against the age of the response
        if ($reqc && $reqc->hasDirective('max-age') &&
            $responseAge > $reqc->getDirective('max-age')) {
            return false;
        }

        // Check the response's max-age header
        if ($response->isFresh() === false) {
            $maxStale = $reqc ? $reqc->getDirective('max-stale') : null;
            if (null !== $maxStale) {
                if ($maxStale !== true && $response->getFreshness() < (-1 * $maxStale)) {
                    return false;
                }
            } elseif ($resc && $resc->hasDirective('max-age')
                && $responseAge > $resc->getDirective('max-age')
            ) {
                return false;
            }
        }

        if ($this->revalidation->shouldRevalidate($request, $response)) {
            try {
                return $this->revalidation->revalidate($request, $response);
            } catch (CurlException $e) {
                $request->getParams()->set('cache.hit', 'error');
                return $this->canResponseSatisfyFailedRequest($request, $response);
            }
        }

        return true;
    }

    /**
     * Check if a cache response satisfies a failed request's caching constraints
     *
     * @param RequestInterface $request  Request to validate
     * @param Response         $response Response to validate
     *
     * @return bool
     */
    public function canResponseSatisfyFailedRequest(RequestInterface $request, Response $response)
    {
        $reqc = $request->getHeader('Cache-Control');
        $resc = $response->getHeader('Cache-Control');
        $requestStaleIfError = $reqc ? $reqc->getDirective('stale-if-error') : null;
        $responseStaleIfError = $resc ? $resc->getDirective('stale-if-error') : null;

        if (!$requestStaleIfError && !$responseStaleIfError) {
            return false;
        }

        if (is_numeric($requestStaleIfError) && $response->getAge() - $response->getMaxAge() > $requestStaleIfError) {
            return false;
        }

        if (is_numeric($responseStaleIfError) && $response->getAge() - $response->getMaxAge() > $responseStaleIfError) {
            return false;
        }

        return true;
    }

    /**
     * Purge all cache entries for a given URL
     *
     * @param string $url URL to purge
     */
    public function purge($url)
    {
        // BC compatibility with previous version that accepted a Request object
        $url = $url instanceof RequestInterface ? $url->getUrl() : $url;
        $this->storage->purge($url);
    }

    /**
     * Add the plugin's headers to a response
     *
     * @param RequestInterface $request  Request
     * @param Response         $response Response to add headers to
     */
    protected function addResponseHeaders(RequestInterface $request, Response $response)
    {
        $params = $request->getParams();
        $response->setHeader('Via', sprintf('%s GuzzleCache/%s', $request->getProtocolVersion(), Version::VERSION));

        $lookup = ($params['cache.lookup'] === true ? 'HIT' : 'MISS') . ' from GuzzleCache';
        if ($header = $response->getHeader('X-Cache-Lookup')) {
            // Don't add duplicates
            $values = $header->toArray();
            $values[] = $lookup;
            $response->setHeader('X-Cache-Lookup', array_unique($values));
        } else {
            $response->setHeader('X-Cache-Lookup', $lookup);
        }

        if ($params['cache.hit'] === true) {
            $xcache = 'HIT from GuzzleCache';
        } elseif ($params['cache.hit'] == 'error') {
            $xcache = 'HIT_ERROR from GuzzleCache';
        } else {
            $xcache = 'MISS from GuzzleCache';
        }

        if ($header = $response->getHeader('X-Cache')) {
            // Don't add duplicates
            $values = $header->toArray();
            $values[] = $xcache;
            $response->setHeader('X-Cache', array_unique($values));
        } else {
            $response->setHeader('X-Cache', $xcache);
        }

        if ($response->isFresh() === false) {
            $response->addHeader('Warning', sprintf('110 GuzzleCache/%s "Response is stale"', Version::VERSION));
            if ($params['cache.hit'] === 'error') {
                $response->addHeader('Warning', sprintf('111 GuzzleCache/%s "Revalidation failed"', Version::VERSION));
            }
        }
    }
}
vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/DefaultRevalidation.php000064400000014321151327705700021660 0ustar00<?php

namespace Guzzle\Plugin\Cache;

use Guzzle\Http\Message\RequestInterface;
use Guzzle\Http\Message\Response;
use Guzzle\Http\Exception\BadResponseException;

/**
 * Default revalidation strategy
 */
class DefaultRevalidation implements RevalidationInterface
{
    /** @var CacheStorageInterface Cache object storing cache data */
    protected $storage;

    /** @var CanCacheStrategyInterface */
    protected $canCache;

    /**
     * @param CacheStorageInterface     $cache    Cache storage
     * @param CanCacheStrategyInterface $canCache Determines if a message can be cached
     */
    public function __construct(CacheStorageInterface $cache, CanCacheStrategyInterface $canCache = null)
    {
        $this->storage = $cache;
        $this->canCache = $canCache ?: new DefaultCanCacheStrategy();
    }

    public function revalidate(RequestInterface $request, Response $response)
    {
        try {
            $revalidate = $this->createRevalidationRequest($request, $response);
            $validateResponse = $revalidate->send();
            if ($validateResponse->getStatusCode() == 200) {
                return $this->handle200Response($request, $validateResponse);
            } elseif ($validateResponse->getStatusCode() == 304) {
                return $this->handle304Response($request, $validateResponse, $response);
            }
        } catch (BadResponseException $e) {
            $this->handleBadResponse($e);
        }

        // Other exceptions encountered in the revalidation request are ignored
        // in hopes that sending a request to the origin server will fix it
        return false;
    }

    public function shouldRevalidate(RequestInterface $request, Response $response)
    {
        if ($request->getMethod() != RequestInterface::GET) {
            return false;
        }

        $reqCache = $request->getHeader('Cache-Control');
        $resCache = $response->getHeader('Cache-Control');

        $revalidate = $request->getHeader('Pragma') == 'no-cache' ||
            ($reqCache && ($reqCache->hasDirective('no-cache') || $reqCache->hasDirective('must-revalidate'))) ||
            ($resCache && ($resCache->hasDirective('no-cache') || $resCache->hasDirective('must-revalidate')));

        // Use the strong ETag validator if available and the response contains no Cache-Control directive
        if (!$revalidate && !$resCache && $response->hasHeader('ETag')) {
            $revalidate = true;
        }

        return $revalidate;
    }

    /**
     * Handles a bad response when attempting to revalidate
     *
     * @param BadResponseException $e Exception encountered
     *
     * @throws BadResponseException
     */
    protected function handleBadResponse(BadResponseException $e)
    {
        // 404 errors mean the resource no longer exists, so remove from
        // cache, and prevent an additional request by throwing the exception
        if ($e->getResponse()->getStatusCode() == 404) {
            $this->storage->delete($e->getRequest());
            throw $e;
        }
    }

    /**
     * Creates a request to use for revalidation
     *
     * @param RequestInterface $request  Request
     * @param Response         $response Response to revalidate
     *
     * @return RequestInterface returns a revalidation request
     */
    protected function createRevalidationRequest(RequestInterface $request, Response $response)
    {
        $revalidate = clone $request;
        $revalidate->removeHeader('Pragma')->removeHeader('Cache-Control');

        if ($response->getLastModified()) {
            $revalidate->setHeader('If-Modified-Since', $response->getLastModified());
        }

        if ($response->getEtag()) {
            $revalidate->setHeader('If-None-Match', $response->getEtag());
        }

        // Remove any cache plugins that might be on the request to prevent infinite recursive revalidations
        $dispatcher = $revalidate->getEventDispatcher();
        foreach ($dispatcher->getListeners() as $eventName => $listeners) {
            foreach ($listeners as $listener) {
                if (is_array($listener) && $listener[0] instanceof CachePlugin) {
                    $dispatcher->removeListener($eventName, $listener);
                }
            }
        }

        return $revalidate;
    }

    /**
     * Handles a 200 response response from revalidating. The server does not support validation, so use this response.
     *
     * @param RequestInterface $request          Request that was sent
     * @param Response         $validateResponse Response received
     *
     * @return bool Returns true if valid, false if invalid
     */
    protected function handle200Response(RequestInterface $request, Response $validateResponse)
    {
        $request->setResponse($validateResponse);
        if ($this->canCache->canCacheResponse($validateResponse)) {
            $this->storage->cache($request, $validateResponse);
        }

        return false;
    }

    /**
     * Handle a 304 response and ensure that it is still valid
     *
     * @param RequestInterface $request          Request that was sent
     * @param Response         $validateResponse Response received
     * @param Response         $response         Original cached response
     *
     * @return bool Returns true if valid, false if invalid
     */
    protected function handle304Response(RequestInterface $request, Response $validateResponse, Response $response)
    {
        static $replaceHeaders = array('Date', 'Expires', 'Cache-Control', 'ETag', 'Last-Modified');

        // Make sure that this response has the same ETag
        if ($validateResponse->getEtag() != $response->getEtag()) {
            return false;
        }

        // Replace cached headers with any of these headers from the
        // origin server that might be more up to date
        $modified = false;
        foreach ($replaceHeaders as $name) {
            if ($validateResponse->hasHeader($name)) {
                $modified = true;
                $response->setHeader($name, $validateResponse->getHeader($name));
            }
        }

        // Store the updated response in cache
        if ($modified && $this->canCache->canCacheResponse($response)) {
            $this->storage->cache($request, $response);
        }

        return true;
    }
}
vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/DefaultCacheKeyProvider.php000064400000002736151327705700022435 0ustar00<?php

namespace Guzzle\Plugin\Cache;

use Guzzle\Http\Message\RequestInterface;

\Guzzle\Common\Version::warn('Guzzle\Plugin\Cache\DefaultCacheKeyProvider is no longer used');

/**
 * @deprecated This class is no longer used
 * @codeCoverageIgnore
 */
class DefaultCacheKeyProvider implements CacheKeyProviderInterface
{
    public function getCacheKey(RequestInterface $request)
    {
        // See if the key has already been calculated
        $key = $request->getParams()->get(self::CACHE_KEY);

        if (!$key) {

            $cloned = clone $request;
            $cloned->removeHeader('Cache-Control');

            // Check to see how and if the key should be filtered
            foreach (explode(';', $request->getParams()->get(self::CACHE_KEY_FILTER)) as $part) {
                $pieces = array_map('trim', explode('=', $part));
                if (isset($pieces[1])) {
                    foreach (array_map('trim', explode(',', $pieces[1])) as $remove) {
                        if ($pieces[0] == 'header') {
                            $cloned->removeHeader($remove);
                        } elseif ($pieces[0] == 'query') {
                            $cloned->getQuery()->remove($remove);
                        }
                    }
                }
            }

            $raw = (string) $cloned;
            $key = 'GZ' . md5($raw);
            $request->getParams()->set(self::CACHE_KEY, $key)->set(self::CACHE_KEY_RAW, $raw);
        }

        return $key;
    }
}
vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/DenyRevalidation.php000064400000000626151327705700021176 0ustar00<?php

namespace Guzzle\Plugin\Cache;

use Guzzle\Http\Message\RequestInterface;
use Guzzle\Http\Message\Response;

/**
 * Never performs cache revalidation and just assumes the request is invalid
 */
class DenyRevalidation extends DefaultRevalidation
{
    public function __construct() {}

    public function revalidate(RequestInterface $request, Response $response)
    {
        return false;
    }
}
vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/CacheKeyProviderInterface.php000064400000000365151327705700022745 0ustar00<?php

namespace Guzzle\Plugin\Cache;

\Guzzle\Common\Version::warn('Guzzle\Plugin\Cache\CacheKeyProviderInterface is no longer used');

/**
 * @deprecated This is no longer used
 * @codeCoverageIgnore
 */
interface CacheKeyProviderInterface {}
vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/RevalidationInterface.php000064400000001516151327705700022176 0ustar00<?php

namespace Guzzle\Plugin\Cache;

use Guzzle\Http\Message\RequestInterface;
use Guzzle\Http\Message\Response;

/**
 * Cache revalidation interface
 */
interface RevalidationInterface
{
    /**
     * Performs a cache revalidation
     *
     * @param RequestInterface $request    Request to revalidate
     * @param Response         $response   Response that was received
     *
     * @return bool Returns true if the request can be cached
     */
    public function revalidate(RequestInterface $request, Response $response);

    /**
     * Returns true if the response should be revalidated
     *
     * @param RequestInterface $request  Request to check
     * @param Response         $response Response to check
     *
     * @return bool
     */
    public function shouldRevalidate(RequestInterface $request, Response $response);
}
vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/DefaultCacheStorage.php000064400000020213151327705700021564 0ustar00<?php

namespace Guzzle\Plugin\Cache;

use Guzzle\Cache\CacheAdapterFactory;
use Guzzle\Cache\CacheAdapterInterface;
use Guzzle\Http\EntityBodyInterface;
use Guzzle\Http\Message\MessageInterface;
use Guzzle\Http\Message\Request;
use Guzzle\Http\Message\RequestInterface;
use Guzzle\Http\Message\Response;

/**
 * Default cache storage implementation
 */
class DefaultCacheStorage implements CacheStorageInterface
{
    /** @var string */
    protected $keyPrefix;

    /** @var CacheAdapterInterface Cache used to store cache data */
    protected $cache;

    /** @var int Default cache TTL */
    protected $defaultTtl;

    /**
     * @param mixed  $cache      Cache used to store cache data
     * @param string $keyPrefix  Provide an optional key prefix to prefix on all cache keys
     * @param int    $defaultTtl Default cache TTL
     */
    public function __construct($cache, $keyPrefix = '', $defaultTtl = 3600)
    {
        $this->cache = CacheAdapterFactory::fromCache($cache);
        $this->defaultTtl = $defaultTtl;
        $this->keyPrefix = $keyPrefix;
    }

    public function cache(RequestInterface $request, Response $response)
    {
        $currentTime = time();

        $overrideTtl = $request->getParams()->get('cache.override_ttl');
        if ($overrideTtl) {
            $ttl = $overrideTtl;
        } else {
            $maxAge = $response->getMaxAge();
            if ($maxAge !== null) {
                $ttl = $maxAge;
            } else {
                $ttl = $this->defaultTtl;
            }
        }

        if ($cacheControl = $response->getHeader('Cache-Control')) {
            $stale = $cacheControl->getDirective('stale-if-error');
            if ($stale === true) {
                $ttl += $ttl;
            } else if (is_numeric($stale)) {
                $ttl += $stale;
            }
        }

        // Determine which manifest key should be used
        $key = $this->getCacheKey($request);
        $persistedRequest = $this->persistHeaders($request);
        $entries = array();

        if ($manifest = $this->cache->fetch($key)) {
            // Determine which cache entries should still be in the cache
            $vary = $response->getVary();
            foreach (unserialize($manifest) as $entry) {
                // Check if the entry is expired
                if ($entry[4] < $currentTime) {
                    continue;
                }
                $entry[1]['vary'] = isset($entry[1]['vary']) ? $entry[1]['vary'] : '';
                if ($vary != $entry[1]['vary'] || !$this->requestsMatch($vary, $entry[0], $persistedRequest)) {
                    $entries[] = $entry;
                }
            }
        }

        // Persist the response body if needed
        $bodyDigest = null;
        if ($response->getBody() && $response->getBody()->getContentLength() > 0) {
            $bodyDigest = $this->getBodyKey($request->getUrl(), $response->getBody());
            $this->cache->save($bodyDigest, (string) $response->getBody(), $ttl);
        }

        array_unshift($entries, array(
            $persistedRequest,
            $this->persistHeaders($response),
            $response->getStatusCode(),
            $bodyDigest,
            $currentTime + $ttl
        ));

        $this->cache->save($key, serialize($entries));
    }

    public function delete(RequestInterface $request)
    {
        $key = $this->getCacheKey($request);
        if ($entries = $this->cache->fetch($key)) {
            // Delete each cached body
            foreach (unserialize($entries) as $entry) {
                if ($entry[3]) {
                    $this->cache->delete($entry[3]);
                }
            }
            $this->cache->delete($key);
        }
    }

    public function purge($url)
    {
        foreach (array('GET', 'HEAD', 'POST', 'PUT', 'DELETE') as $method) {
            $this->delete(new Request($method, $url));
        }
    }

    public function fetch(RequestInterface $request)
    {
        $key = $this->getCacheKey($request);
        if (!($entries = $this->cache->fetch($key))) {
            return null;
        }

        $match = null;
        $headers = $this->persistHeaders($request);
        $entries = unserialize($entries);
        foreach ($entries as $index => $entry) {
            if ($this->requestsMatch(isset($entry[1]['vary']) ? $entry[1]['vary'] : '', $headers, $entry[0])) {
                $match = $entry;
                break;
            }
        }

        if (!$match) {
            return null;
        }

        // Ensure that the response is not expired
        $response = null;
        if ($match[4] < time()) {
            $response = -1;
        } else {
            $response = new Response($match[2], $match[1]);
            if ($match[3]) {
                if ($body = $this->cache->fetch($match[3])) {
                    $response->setBody($body);
                } else {
                    // The response is not valid because the body was somehow deleted
                    $response = -1;
                }
            }
        }

        if ($response === -1) {
            // Remove the entry from the metadata and update the cache
            unset($entries[$index]);
            if ($entries) {
                $this->cache->save($key, serialize($entries));
            } else {
                $this->cache->delete($key);
            }
            return null;
        }

        return $response;
    }

    /**
     * Hash a request URL into a string that returns cache metadata
     *
     * @param RequestInterface $request
     *
     * @return string
     */
    protected function getCacheKey(RequestInterface $request)
    {
        // Allow cache.key_filter to trim down the URL cache key by removing generate query string values (e.g. auth)
        if ($filter = $request->getParams()->get('cache.key_filter')) {
            $url = $request->getUrl(true);
            foreach (explode(',', $filter) as $remove) {
                $url->getQuery()->remove(trim($remove));
            }
        } else {
            $url = $request->getUrl();
        }

        return $this->keyPrefix . md5($request->getMethod() . ' ' . $url);
    }

    /**
     * Create a cache key for a response's body
     *
     * @param string              $url  URL of the entry
     * @param EntityBodyInterface $body Response body
     *
     * @return string
     */
    protected function getBodyKey($url, EntityBodyInterface $body)
    {
        return $this->keyPrefix . md5($url) . $body->getContentMd5();
    }

    /**
     * Determines whether two Request HTTP header sets are non-varying
     *
     * @param string $vary Response vary header
     * @param array  $r1   HTTP header array
     * @param array  $r2   HTTP header array
     *
     * @return bool
     */
    private function requestsMatch($vary, $r1, $r2)
    {
        if ($vary) {
            foreach (explode(',', $vary) as $header) {
                $key = trim(strtolower($header));
                $v1 = isset($r1[$key]) ? $r1[$key] : null;
                $v2 = isset($r2[$key]) ? $r2[$key] : null;
                if ($v1 !== $v2) {
                    return false;
                }
            }
        }

        return true;
    }

    /**
     * Creates an array of cacheable and normalized message headers
     *
     * @param MessageInterface $message
     *
     * @return array
     */
    private function persistHeaders(MessageInterface $message)
    {
        // Headers are excluded from the caching (see RFC 2616:13.5.1)
        static $noCache = array(
            'age' => true,
            'connection' => true,
            'keep-alive' => true,
            'proxy-authenticate' => true,
            'proxy-authorization' => true,
            'te' => true,
            'trailers' => true,
            'transfer-encoding' => true,
            'upgrade' => true,
            'set-cookie' => true,
            'set-cookie2' => true
        );

        // Clone the response to not destroy any necessary headers when caching
        $headers = $message->getHeaders()->getAll();
        $headers = array_diff_key($headers, $noCache);
        // Cast the headers to a string
        $headers = array_map(function ($h) { return (string) $h; }, $headers);

        return $headers;
    }
}
vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/composer.json000064400000001301151327705700017735 0ustar00{
    "name": "guzzle/plugin-cache",
    "description": "Guzzle HTTP cache plugin",
    "homepage": "http://guzzlephp.org/",
    "keywords": ["plugin", "guzzle"],
    "license": "MIT",
    "authors": [
        {
            "name": "Michael Dowling",
            "email": "mtdowling@gmail.com",
            "homepage": "https://github.com/mtdowling"
        }
    ],
    "require": {
        "php": ">=5.3.2",
        "guzzle/http": "self.version",
        "guzzle/cache": "self.version"
    },
    "autoload": {
        "psr-0": { "Guzzle\\Plugin\\Cache": "" }
    },
    "target-dir": "Guzzle/Plugin/Cache",
    "extra": {
        "branch-alias": {
            "dev-master": "3.7-dev"
        }
    }
}
vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/SkipRevalidation.php000064400000000626151327705700021205 0ustar00<?php

namespace Guzzle\Plugin\Cache;

use Guzzle\Http\Message\RequestInterface;
use Guzzle\Http\Message\Response;

/**
 * Never performs cache revalidation and just assumes the request is still ok
 */
class SkipRevalidation extends DefaultRevalidation
{
    public function __construct() {}

    public function revalidate(RequestInterface $request, Response $response)
    {
        return true;
    }
}
vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/CanCacheStrategyInterface.php000064400000001211151327705700022715 0ustar00<?php

namespace Guzzle\Plugin\Cache;

use Guzzle\Http\Message\RequestInterface;
use Guzzle\Http\Message\Response;

/**
 * Strategy used to determine if a request can be cached
 */
interface CanCacheStrategyInterface
{
    /**
     * Determine if a request can be cached
     *
     * @param RequestInterface $request Request to determine
     *
     * @return bool
     */
    public function canCacheRequest(RequestInterface $request);

    /**
     * Determine if a response can be cached
     *
     * @param Response $response Response to determine
     *
     * @return bool
     */
    public function canCacheResponse(Response $response);
}
vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/DefaultCanCacheStrategy.php000064400000001614151327705700022410 0ustar00<?php

namespace Guzzle\Plugin\Cache;

use Guzzle\Http\Message\RequestInterface;
use Guzzle\Http\Message\Response;

/**
 * Default strategy used to determine of an HTTP request can be cached
 */
class DefaultCanCacheStrategy implements CanCacheStrategyInterface
{
    public function canCacheRequest(RequestInterface $request)
    {
        // Only GET and HEAD requests can be cached
        if ($request->getMethod() != RequestInterface::GET && $request->getMethod() != RequestInterface::HEAD) {
            return false;
        }

        // Never cache requests when using no-store
        if ($request->hasHeader('Cache-Control') && $request->getHeader('Cache-Control')->hasDirective('no-store')) {
            return false;
        }

        return true;
    }

    public function canCacheResponse(Response $response)
    {
        return $response->isSuccessful() && $response->canCache();
    }
}
vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/CallbackCanCacheStrategy.php000064400000003203151327705700022514 0ustar00<?php

namespace Guzzle\Plugin\Cache;

use Guzzle\Common\Exception\InvalidArgumentException;
use Guzzle\Http\Message\RequestInterface;
use Guzzle\Http\Message\Response;

/**
 * Determines if a request can be cached using a callback
 */
class CallbackCanCacheStrategy extends DefaultCanCacheStrategy
{
    /** @var callable Callback for request */
    protected $requestCallback;

    /** @var callable Callback for response */
    protected $responseCallback;

    /**
     * @param \Closure|array|mixed $requestCallback  Callable method to invoke for requests
     * @param \Closure|array|mixed $responseCallback Callable method to invoke for responses
     *
     * @throws InvalidArgumentException
     */
    public function __construct($requestCallback = null, $responseCallback = null)
    {
        if ($requestCallback && !is_callable($requestCallback)) {
            throw new InvalidArgumentException('Method must be callable');
        }

        if ($responseCallback && !is_callable($responseCallback)) {
            throw new InvalidArgumentException('Method must be callable');
        }

        $this->requestCallback = $requestCallback;
        $this->responseCallback = $responseCallback;
    }

    public function canCacheRequest(RequestInterface $request)
    {
        return $this->requestCallback
            ? call_user_func($this->requestCallback, $request)
            : parent::canCacheRequest($request);
    }

    public function canCacheResponse(Response $response)
    {
        return $this->responseCallback
            ? call_user_func($this->responseCallback, $response)
            : parent::canCacheResponse($response);
    }
}
vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/CacheStorageInterface.php000064400000001725151327705700022107 0ustar00<?php

namespace Guzzle\Plugin\Cache;

use Guzzle\Http\Message\RequestInterface;
use Guzzle\Http\Message\Response;

/**
 * Interface used to cache HTTP requests
 */
interface CacheStorageInterface
{
    /**
     * Get a Response from the cache for a request
     *
     * @param RequestInterface $request
     *
     * @return null|Response
     */
    public function fetch(RequestInterface $request);

    /**
     * Cache an HTTP request
     *
     * @param RequestInterface $request  Request being cached
     * @param Response         $response Response to cache
     */
    public function cache(RequestInterface $request, Response $response);

    /**
     * Deletes cache entries that match a request
     *
     * @param RequestInterface $request Request to delete from cache
     */
    public function delete(RequestInterface $request);

    /**
     * Purge all cache entries for a given URL
     *
     * @param string $url
     */
    public function purge($url);
}
vendor/guzzle/guzzle/src/Guzzle/Plugin/Async/composer.json000064400000001234151327705700020014 0ustar00{
    "name": "guzzle/plugin-async",
    "description": "Guzzle async request plugin",
    "homepage": "http://guzzlephp.org/",
    "keywords": ["plugin", "guzzle"],
    "license": "MIT",
    "authors": [
        {
            "name": "Michael Dowling",
            "email": "mtdowling@gmail.com",
            "homepage": "https://github.com/mtdowling"
        }
    ],
    "require": {
        "php": ">=5.3.2",
        "guzzle/http": "self.version"
    },
    "autoload": {
        "psr-0": { "Guzzle\\Plugin\\Async": "" }
    },
    "target-dir": "Guzzle/Plugin/Async",
    "extra": {
        "branch-alias": {
            "dev-master": "3.7-dev"
        }
    }
}
vendor/guzzle/guzzle/src/Guzzle/Plugin/Async/AsyncPlugin.php000064400000005631151327705700020244 0ustar00<?php

namespace Guzzle\Plugin\Async;

use Guzzle\Common\Event;
use Guzzle\Http\Message\Response;
use Guzzle\Http\Exception\CurlException;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;

/**
 * Sends requests but does not wait for the response
 */
class AsyncPlugin implements EventSubscriberInterface
{
    public static function getSubscribedEvents()
    {
        return array(
            'request.before_send'    => 'onBeforeSend',
            'request.exception'      => 'onRequestTimeout',
            'request.sent'           => 'onRequestSent',
            'curl.callback.progress' => 'onCurlProgress'
        );
    }

    /**
     * Event used to ensure that progress callback are emitted from the curl handle's request mediator.
     *
     * @param Event $event
     */
    public function onBeforeSend(Event $event)
    {
        // Ensure that progress callbacks are dispatched
        $event['request']->getCurlOptions()->set('progress', true);
    }

    /**
     * Event emitted when a curl progress function is called. When the amount of data uploaded == the amount of data to
     * upload OR any bytes have been downloaded, then time the request out after 1ms because we're done with
     * transmitting the request, and tell curl not download a body.
     *
     * @param Event $event
     */
    public function onCurlProgress(Event $event)
    {
        if ($event['handle'] &&
            ($event['downloaded'] || (isset($event['uploaded']) && $event['upload_size'] === $event['uploaded']))
        ) {
            // Timeout after 1ms
            curl_setopt($event['handle'], CURLOPT_TIMEOUT_MS, 1);
            // Even if the response is quick, tell curl not to download the body.
            // - Note that we can only perform this shortcut if the request transmitted a body so as to ensure that the
            //   request method is not converted to a HEAD request before the request was sent via curl.
            if ($event['uploaded']) {
                curl_setopt($event['handle'], CURLOPT_NOBODY, true);
            }
        }
    }

    /**
     * Event emitted when a curl exception occurs. Ignore the exception and set a mock response.
     *
     * @param Event $event
     */
    public function onRequestTimeout(Event $event)
    {
        if ($event['exception'] instanceof CurlException) {
            $event['request']->setResponse(new Response(200, array(
                'X-Guzzle-Async' => 'Did not wait for the response'
            )));
        }
    }

    /**
     * Event emitted when a request completes because it took less than 1ms. Add an X-Guzzle-Async header to notify the
     * caller that there is no body in the message.
     *
     * @param Event $event
     */
    public function onRequestSent(Event $event)
    {
        // Let the caller know this was meant to be async
        $event['request']->getResponse()->setHeader('X-Guzzle-Async', 'Did not wait for the response');
    }
}
vendor/guzzle/guzzle/src/Guzzle/Plugin/composer.json000064400000002524151327705700016742 0ustar00{
    "name": "guzzle/plugin",
    "description": "Guzzle plugin component containing all Guzzle HTTP plugins",
    "homepage": "http://guzzlephp.org/",
    "keywords": ["http", "client", "plugin", "extension", "guzzle"],
    "license": "MIT",
    "authors": [
        {
            "name": "Michael Dowling",
            "email": "mtdowling@gmail.com",
            "homepage": "https://github.com/mtdowling"
        }
    ],
    "require": {
        "php": ">=5.3.2",
        "guzzle/http": "self.version"
    },
    "suggest": {
        "guzzle/cache": "self.version",
        "guzzle/log": "self.version"
    },
    "autoload": {
        "psr-0": { "Guzzle\\Plugin": "" }
    },
    "target-dir": "Guzzle/Plugin",
    "replace": {
        "guzzle/plugin-async": "self.version",
        "guzzle/plugin-backoff": "self.version",
        "guzzle/plugin-cache": "self.version",
        "guzzle/plugin-cookie": "self.version",
        "guzzle/plugin-curlauth": "self.version",
        "guzzle/plugin-error-response": "self.version",
        "guzzle/plugin-history": "self.version",
        "guzzle/plugin-log": "self.version",
        "guzzle/plugin-md5": "self.version",
        "guzzle/plugin-mock": "self.version",
        "guzzle/plugin-oauth": "self.version"
    },
    "extra": {
        "branch-alias": {
            "dev-master": "3.7-dev"
        }
    }
}
vendor/guzzle/guzzle/src/Guzzle/Plugin/CurlAuth/composer.json000064400000001262151327705700020467 0ustar00{
    "name": "guzzle/plugin-curlauth",
    "description": "Guzzle cURL authorization plugin",
    "homepage": "http://guzzlephp.org/",
    "keywords": ["plugin", "curl", "guzzle"],
    "license": "MIT",
    "authors": [
        {
            "name": "Michael Dowling",
            "email": "mtdowling@gmail.com",
            "homepage": "https://github.com/mtdowling"
        }
    ],
    "require": {
        "php": ">=5.3.2",
        "guzzle/http": "self.version"
    },
    "autoload": {
        "psr-0": { "Guzzle\\Plugin\\CurlAuth": "" }
    },
    "target-dir": "Guzzle/Plugin/CurlAuth",
    "extra": {
        "branch-alias": {
            "dev-master": "3.7-dev"
        }
    }
}
vendor/guzzle/guzzle/src/Guzzle/Plugin/CurlAuth/CurlAuthPlugin.php000064400000002554151327705700021371 0ustar00<?php

namespace Guzzle\Plugin\CurlAuth;

use Guzzle\Common\Event;
use Guzzle\Common\Version;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;

/**
 * Adds specified curl auth to all requests sent from a client. Defaults to CURLAUTH_BASIC if none supplied.
 * @deprecated Use $client->getConfig()->setPath('request.options/auth', array('user', 'pass', 'Basic|Digest');
 */
class CurlAuthPlugin implements EventSubscriberInterface
{
    private $username;
    private $password;
    private $scheme;

    /**
     * @param string $username HTTP basic auth username
     * @param string $password Password
     * @param int    $scheme   Curl auth scheme
     */
    public function __construct($username, $password, $scheme=CURLAUTH_BASIC)
    {
        Version::warn(__CLASS__ . " is deprecated. Use \$client->getConfig()->setPath('request.options/auth', array('user', 'pass', 'Basic|Digest');");
        $this->username = $username;
        $this->password = $password;
        $this->scheme = $scheme;
    }

    public static function getSubscribedEvents()
    {
        return array('client.create_request' => array('onRequestCreate', 255));
    }

    /**
     * Add basic auth
     *
     * @param Event $event
     */
    public function onRequestCreate(Event $event)
    {
        $event['request']->setAuth($this->username, $this->password, $this->scheme);
    }
}
vendor/guzzle/guzzle/src/Guzzle/Plugin/Cookie/CookiePlugin.php000064400000003611151327705700020530 0ustar00<?php

namespace Guzzle\Plugin\Cookie;

use Guzzle\Common\Event;
use Guzzle\Plugin\Cookie\CookieJar\ArrayCookieJar;
use Guzzle\Plugin\Cookie\CookieJar\CookieJarInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;

/**
 * Adds, extracts, and persists cookies between HTTP requests
 */
class CookiePlugin implements EventSubscriberInterface
{
    /** @var CookieJarInterface Cookie cookieJar used to hold cookies */
    protected $cookieJar;

    /**
     * @param CookieJarInterface $cookieJar Cookie jar used to hold cookies. Creates an ArrayCookieJar by default.
     */
    public function __construct(CookieJarInterface $cookieJar = null)
    {
        $this->cookieJar = $cookieJar ?: new ArrayCookieJar();
    }

    public static function getSubscribedEvents()
    {
        return array(
            'request.before_send' => array('onRequestBeforeSend', 125),
            'request.sent'        => array('onRequestSent', 125)
        );
    }

    /**
     * Get the cookie cookieJar
     *
     * @return CookieJarInterface
     */
    public function getCookieJar()
    {
        return $this->cookieJar;
    }

    /**
     * Add cookies before a request is sent
     *
     * @param Event $event
     */
    public function onRequestBeforeSend(Event $event)
    {
        $request = $event['request'];
        if (!$request->getParams()->get('cookies.disable')) {
            $request->removeHeader('Cookie');
            // Find cookies that match this request
            foreach ($this->cookieJar->getMatchingCookies($request) as $cookie) {
                $request->addCookie($cookie->getName(), $cookie->getValue());
            }
        }
    }

    /**
     * Extract cookies from a sent request
     *
     * @param Event $event
     */
    public function onRequestSent(Event $event)
    {
        $this->cookieJar->addCookiesFromResponse($event['response'], $event['request']);
    }
}
vendor/guzzle/guzzle/src/Guzzle/Plugin/Cookie/Cookie.php000064400000030462151327705700017355 0ustar00<?php

namespace Guzzle\Plugin\Cookie;

use Guzzle\Common\ToArrayInterface;

/**
 * Set-Cookie object
 */
class Cookie implements ToArrayInterface
{
    /** @var array Cookie data */
    protected $data;

    /**
     * @var string ASCII codes not valid for for use in a cookie name
     *
     * Cookie names are defined as 'token', according to RFC 2616, Section 2.2
     * A valid token may contain any CHAR except CTLs (ASCII 0 - 31 or 127)
     * or any of the following separators
     */
    protected static $invalidCharString;

    /**
     * Gets an array of invalid cookie characters
     *
     * @return array
     */
    protected static function getInvalidCharacters()
    {
        if (!self::$invalidCharString) {
            self::$invalidCharString = implode('', array_map('chr', array_merge(
                range(0, 32),
                array(34, 40, 41, 44, 47),
                array(58, 59, 60, 61, 62, 63, 64, 91, 92, 93, 123, 125, 127)
            )));
        }

        return self::$invalidCharString;
    }

    /**
     * @param array $data Array of cookie data provided by a Cookie parser
     */
    public function __construct(array $data = array())
    {
        static $defaults = array(
            'name'        => '',
            'value'       => '',
            'domain'      => '',
            'path'        => '/',
            'expires'     => null,
            'max_age'     => 0,
            'comment'     => null,
            'comment_url' => null,
            'port'        => array(),
            'version'     => null,
            'secure'      => false,
            'discard'     => false,
            'http_only'   => false
        );

        $this->data = array_merge($defaults, $data);
        // Extract the expires value and turn it into a UNIX timestamp if needed
        if (!$this->getExpires() && $this->getMaxAge()) {
            // Calculate the expires date
            $this->setExpires(time() + (int) $this->getMaxAge());
        } elseif ($this->getExpires() && !is_numeric($this->getExpires())) {
            $this->setExpires(strtotime($this->getExpires()));
        }
    }

    /**
     * Get the cookie as an array
     *
     * @return array
     */
    public function toArray()
    {
        return $this->data;
    }

    /**
     * Get the cookie name
     *
     * @return string
     */
    public function getName()
    {
        return $this->data['name'];
    }

    /**
     * Set the cookie name
     *
     * @param string $name Cookie name
     *
     * @return Cookie
     */
    public function setName($name)
    {
        return $this->setData('name', $name);
    }

    /**
     * Get the cookie value
     *
     * @return string
     */
    public function getValue()
    {
        return $this->data['value'];
    }

    /**
     * Set the cookie value
     *
     * @param string $value Cookie value
     *
     * @return Cookie
     */
    public function setValue($value)
    {
        return $this->setData('value', $value);
    }

    /**
     * Get the domain
     *
     * @return string|null
     */
    public function getDomain()
    {
        return $this->data['domain'];
    }

    /**
     * Set the domain of the cookie
     *
     * @param string $domain
     *
     * @return Cookie
     */
    public function setDomain($domain)
    {
        return $this->setData('domain', $domain);
    }

    /**
     * Get the path
     *
     * @return string
     */
    public function getPath()
    {
        return $this->data['path'];
    }

    /**
     * Set the path of the cookie
     *
     * @param string $path Path of the cookie
     *
     * @return Cookie
     */
    public function setPath($path)
    {
        return $this->setData('path', $path);
    }

    /**
     * Maximum lifetime of the cookie in seconds
     *
     * @return int|null
     */
    public function getMaxAge()
    {
        return $this->data['max_age'];
    }

    /**
     * Set the max-age of the cookie
     *
     * @param int $maxAge Max age of the cookie in seconds
     *
     * @return Cookie
     */
    public function setMaxAge($maxAge)
    {
        return $this->setData('max_age', $maxAge);
    }

    /**
     * The UNIX timestamp when the cookie expires
     *
     * @return mixed
     */
    public function getExpires()
    {
        return $this->data['expires'];
    }

    /**
     * Set the unix timestamp for which the cookie will expire
     *
     * @param int $timestamp Unix timestamp
     *
     * @return Cookie
     */
    public function setExpires($timestamp)
    {
        return $this->setData('expires', $timestamp);
    }

    /**
     * Version of the cookie specification. RFC 2965 is 1
     *
     * @return mixed
     */
    public function getVersion()
    {
        return $this->data['version'];
    }

    /**
     * Set the cookie version
     *
     * @param string|int $version Version to set
     *
     * @return Cookie
     */
    public function setVersion($version)
    {
        return $this->setData('version', $version);
    }

    /**
     * Get whether or not this is a secure cookie
     *
     * @return null|bool
     */
    public function getSecure()
    {
        return $this->data['secure'];
    }

    /**
     * Set whether or not the cookie is secure
     *
     * @param bool $secure Set to true or false if secure
     *
     * @return Cookie
     */
    public function setSecure($secure)
    {
        return $this->setData('secure', (bool) $secure);
    }

    /**
     * Get whether or not this is a session cookie
     *
     * @return null|bool
     */
    public function getDiscard()
    {
        return $this->data['discard'];
    }

    /**
     * Set whether or not this is a session cookie
     *
     * @param bool $discard Set to true or false if this is a session cookie
     *
     * @return Cookie
     */
    public function setDiscard($discard)
    {
        return $this->setData('discard', $discard);
    }

    /**
     * Get the comment
     *
     * @return string|null
     */
    public function getComment()
    {
        return $this->data['comment'];
    }

    /**
     * Set the comment of the cookie
     *
     * @param string $comment Cookie comment
     *
     * @return Cookie
     */
    public function setComment($comment)
    {
        return $this->setData('comment', $comment);
    }

    /**
     * Get the comment URL of the cookie
     *
     * @return string|null
     */
    public function getCommentUrl()
    {
        return $this->data['comment_url'];
    }

    /**
     * Set the comment URL of the cookie
     *
     * @param string $commentUrl Cookie comment URL for more information
     *
     * @return Cookie
     */
    public function setCommentUrl($commentUrl)
    {
        return $this->setData('comment_url', $commentUrl);
    }

    /**
     * Get an array of acceptable ports this cookie can be used with
     *
     * @return array
     */
    public function getPorts()
    {
        return $this->data['port'];
    }

    /**
     * Set a list of acceptable ports this cookie can be used with
     *
     * @param array $ports Array of acceptable ports
     *
     * @return Cookie
     */
    public function setPorts(array $ports)
    {
        return $this->setData('port', $ports);
    }

    /**
     * Get whether or not this is an HTTP only cookie
     *
     * @return bool
     */
    public function getHttpOnly()
    {
        return $this->data['http_only'];
    }

    /**
     * Set whether or not this is an HTTP only cookie
     *
     * @param bool $httpOnly Set to true or false if this is HTTP only
     *
     * @return Cookie
     */
    public function setHttpOnly($httpOnly)
    {
        return $this->setData('http_only', $httpOnly);
    }

    /**
     * Get an array of extra cookie data
     *
     * @return array
     */
    public function getAttributes()
    {
        return $this->data['data'];
    }

    /**
     * Get a specific data point from the extra cookie data
     *
     * @param string $name Name of the data point to retrieve
     *
     * @return null|string
     */
    public function getAttribute($name)
    {
        return array_key_exists($name, $this->data['data']) ? $this->data['data'][$name] : null;
    }

    /**
     * Set a cookie data attribute
     *
     * @param string $name  Name of the attribute to set
     * @param string $value Value to set
     *
     * @return Cookie
     */
    public function setAttribute($name, $value)
    {
        $this->data['data'][$name] = $value;

        return $this;
    }

    /**
     * Check if the cookie matches a path value
     *
     * @param string $path Path to check against
     *
     * @return bool
     */
    public function matchesPath($path)
    {
        // RFC6265 http://tools.ietf.org/search/rfc6265#section-5.1.4
        // A request-path path-matches a given cookie-path if at least one of
        // the following conditions holds:

        // o  The cookie-path and the request-path are identical.
        if ($path == $this->getPath()) {
            return true;
        }

        $pos = stripos($path, $this->getPath());
        if ($pos === 0) {
            // o  The cookie-path is a prefix of the request-path, and the last
            // character of the cookie-path is %x2F ("/").
            if (substr($this->getPath(), -1, 1) === "/") {
                return true;
            }

            // o  The cookie-path is a prefix of the request-path, and the first
            // character of the request-path that is not included in the cookie-
            // path is a %x2F ("/") character.
            if (substr($path, strlen($this->getPath()), 1) === "/") {
                return true;
            }
        }

        return false;
    }

    /**
     * Check if the cookie matches a domain value
     *
     * @param string $domain Domain to check against
     *
     * @return bool
     */
    public function matchesDomain($domain)
    {
        // Remove the leading '.' as per spec in RFC 6265: http://tools.ietf.org/html/rfc6265#section-5.2.3
        $cookieDomain = ltrim($this->getDomain(), '.');

        // Domain not set or exact match.
        if (!$cookieDomain || !strcasecmp($domain, $cookieDomain)) {
            return true;
        }

        // Matching the subdomain according to RFC 6265: http://tools.ietf.org/html/rfc6265#section-5.1.3
        if (filter_var($domain, FILTER_VALIDATE_IP)) {
            return false;
        }

        return (bool) preg_match('/\.' . preg_quote($cookieDomain, '/') . '$/i', $domain);
    }

    /**
     * Check if the cookie is compatible with a specific port
     *
     * @param int $port Port to check
     *
     * @return bool
     */
    public function matchesPort($port)
    {
        return count($this->getPorts()) == 0 || in_array($port, $this->getPorts());
    }

    /**
     * Check if the cookie is expired
     *
     * @return bool
     */
    public function isExpired()
    {
        return $this->getExpires() && time() > $this->getExpires();
    }

    /**
     * Check if the cookie is valid according to RFC 6265
     *
     * @return bool|string Returns true if valid or an error message if invalid
     */
    public function validate()
    {
        // Names must not be empty, but can be 0
        $name = $this->getName();
        if (empty($name) && !is_numeric($name)) {
            return 'The cookie name must not be empty';
        }

        // Check if any of the invalid characters are present in the cookie name
        if (strpbrk($name, self::getInvalidCharacters()) !== false) {
            return 'The cookie name must not contain invalid characters: ' . $name;
        }

        // Value must not be empty, but can be 0
        $value = $this->getValue();
        if (empty($value) && !is_numeric($value)) {
            return 'The cookie value must not be empty';
        }

        // Domains must not be empty, but can be 0
        // A "0" is not a valid internet domain, but may be used as server name in a private network
        $domain = $this->getDomain();
        if (empty($domain) && !is_numeric($domain)) {
            return 'The cookie domain must not be empty';
        }

        return true;
    }

    /**
     * Set a value and return the cookie object
     *
     * @param string $key   Key to set
     * @param string $value Value to set
     *
     * @return Cookie
     */
    private function setData($key, $value)
    {
        $this->data[$key] = $value;

        return $this;
    }
}
vendor/guzzle/guzzle/src/Guzzle/Plugin/Cookie/composer.json000064400000001230151327705700020144 0ustar00{
    "name": "guzzle/plugin-cookie",
    "description": "Guzzle cookie plugin",
    "homepage": "http://guzzlephp.org/",
    "keywords": ["plugin", "guzzle"],
    "license": "MIT",
    "authors": [
        {
            "name": "Michael Dowling",
            "email": "mtdowling@gmail.com",
            "homepage": "https://github.com/mtdowling"
        }
    ],
    "require": {
        "php": ">=5.3.2",
        "guzzle/http": "self.version"
    },
    "autoload": {
        "psr-0": { "Guzzle\\Plugin\\Cookie": "" }
    },
    "target-dir": "Guzzle/Plugin/Cookie",
    "extra": {
        "branch-alias": {
            "dev-master": "3.7-dev"
        }
    }
}
vendor/guzzle/guzzle/src/Guzzle/Plugin/Cookie/Exception/InvalidCookieException.php000064400000000252151327705700024473 0ustar00<?php

namespace Guzzle\Plugin\Cookie\Exception;

use Guzzle\Common\Exception\InvalidArgumentException;

class InvalidCookieException extends InvalidArgumentException {}
vendor/guzzle/guzzle/src/Guzzle/Plugin/Cookie/CookieJar/CookieJarInterface.php000064400000005552151327705700023503 0ustar00<?php

namespace Guzzle\Plugin\Cookie\CookieJar;

use Guzzle\Plugin\Cookie\Cookie;
use Guzzle\Http\Message\RequestInterface;
use Guzzle\Http\Message\Response;

/**
 * Interface for persisting cookies
 */
interface CookieJarInterface extends \Countable, \IteratorAggregate
{
    /**
     * Remove cookies currently held in the Cookie cookieJar.
     *
     * Invoking this method without arguments will empty the whole Cookie cookieJar.  If given a $domain argument only
     * cookies belonging to that domain will be removed. If given a $domain and $path argument, cookies belonging to
     * the specified path within that domain are removed. If given all three arguments, then the cookie with the
     * specified name, path and domain is removed.
     *
     * @param string $domain Set to clear only cookies matching a domain
     * @param string $path   Set to clear only cookies matching a domain and path
     * @param string $name   Set to clear only cookies matching a domain, path, and name
     *
     * @return CookieJarInterface
     */
    public function remove($domain = null, $path = null, $name = null);

    /**
     * Discard all temporary cookies.
     *
     * Scans for all cookies in the cookieJar with either no expire field or a true discard flag. To be called when the
     * user agent shuts down according to RFC 2965.
     *
     * @return CookieJarInterface
     */
    public function removeTemporary();

    /**
     * Delete any expired cookies
     *
     * @return CookieJarInterface
     */
    public function removeExpired();

    /**
     * Add a cookie to the cookie cookieJar
     *
     * @param Cookie $cookie Cookie to add
     *
     * @return bool Returns true on success or false on failure
     */
    public function add(Cookie $cookie);

    /**
     * Add cookies from a {@see Guzzle\Http\Message\Response} object
     *
     * @param Response         $response Response object
     * @param RequestInterface $request  Request that received the response
     */
    public function addCookiesFromResponse(Response $response, RequestInterface $request = null);

    /**
     * Get cookies matching a request object
     *
     * @param RequestInterface $request Request object to match
     *
     * @return array
     */
    public function getMatchingCookies(RequestInterface $request);

    /**
     * Get all of the matching cookies
     *
     * @param string $domain          Domain of the cookie
     * @param string $path            Path of the cookie
     * @param string $name            Name of the cookie
     * @param bool   $skipDiscardable Set to TRUE to skip cookies with the Discard attribute.
     * @param bool   $skipExpired     Set to FALSE to include expired
     *
     * @return array Returns an array of Cookie objects
     */
    public function all($domain = null, $path = null, $name = null, $skipDiscardable = false, $skipExpired = true);
}
vendor/guzzle/guzzle/src/Guzzle/Plugin/Cookie/CookieJar/FileCookieJar.php000064400000003206151327705700022454 0ustar00<?php

namespace Guzzle\Plugin\Cookie\CookieJar;

use Guzzle\Common\Exception\RuntimeException;

/**
 * Persists non-session cookies using a JSON formatted file
 */
class FileCookieJar extends ArrayCookieJar
{
    /** @var string filename */
    protected $filename;

    /**
     * Create a new FileCookieJar object
     *
     * @param string $cookieFile File to store the cookie data
     *
     * @throws RuntimeException if the file cannot be found or created
     */
    public function __construct($cookieFile)
    {
        $this->filename = $cookieFile;
        $this->load();
    }

    /**
     * Saves the file when shutting down
     */
    public function __destruct()
    {
        $this->persist();
    }

    /**
     * Save the contents of the data array to the file
     *
     * @throws RuntimeException if the file cannot be found or created
     */
    protected function persist()
    {
        if (false === file_put_contents($this->filename, $this->serialize())) {
            // @codeCoverageIgnoreStart
            throw new RuntimeException('Unable to open file ' . $this->filename);
            // @codeCoverageIgnoreEnd
        }
    }

    /**
     * Load the contents of the json formatted file into the data array and discard any unsaved state
     */
    protected function load()
    {
        $json = file_get_contents($this->filename);
        if (false === $json) {
            // @codeCoverageIgnoreStart
            throw new RuntimeException('Unable to open file ' . $this->filename);
            // @codeCoverageIgnoreEnd
        }

        $this->unserialize($json);
        $this->cookies = $this->cookies ?: array();
    }
}
vendor/guzzle/guzzle/src/Guzzle/Plugin/Cookie/CookieJar/ArrayCookieJar.php000064400000016500151327705700022654 0ustar00<?php

namespace Guzzle\Plugin\Cookie\CookieJar;

use Guzzle\Plugin\Cookie\Cookie;
use Guzzle\Http\Message\RequestInterface;
use Guzzle\Http\Message\Response;
use Guzzle\Parser\ParserRegistry;
use Guzzle\Plugin\Cookie\Exception\InvalidCookieException;

/**
 * Cookie cookieJar that stores cookies an an array
 */
class ArrayCookieJar implements CookieJarInterface, \Serializable
{
    /** @var array Loaded cookie data */
    protected $cookies = array();

    /** @var bool Whether or not strict mode is enabled. When enabled, exceptions will be thrown for invalid cookies */
    protected $strictMode;

    /**
     * @param bool $strictMode Set to true to throw exceptions when invalid cookies are added to the cookie jar
     */
    public function __construct($strictMode = false)
    {
        $this->strictMode = $strictMode;
    }

    /**
     * Enable or disable strict mode on the cookie jar
     *
     * @param bool $strictMode Set to true to throw exceptions when invalid cookies are added. False to ignore them.
     *
     * @return self
     */
    public function setStrictMode($strictMode)
    {
        $this->strictMode = $strictMode;
    }

    public function remove($domain = null, $path = null, $name = null)
    {
        $cookies = $this->all($domain, $path, $name, false, false);
        $this->cookies = array_filter($this->cookies, function (Cookie $cookie) use ($cookies) {
            return !in_array($cookie, $cookies, true);
        });

        return $this;
    }

    public function removeTemporary()
    {
        $this->cookies = array_filter($this->cookies, function (Cookie $cookie) {
            return !$cookie->getDiscard() && $cookie->getExpires();
        });

        return $this;
    }

    public function removeExpired()
    {
        $currentTime = time();
        $this->cookies = array_filter($this->cookies, function (Cookie $cookie) use ($currentTime) {
            return !$cookie->getExpires() || $currentTime < $cookie->getExpires();
        });

        return $this;
    }

    public function all($domain = null, $path = null, $name = null, $skipDiscardable = false, $skipExpired = true)
    {
        return array_values(array_filter($this->cookies, function (Cookie $cookie) use (
            $domain,
            $path,
            $name,
            $skipDiscardable,
            $skipExpired
        ) {
            return false === (($name && $cookie->getName() != $name) ||
                ($skipExpired && $cookie->isExpired()) ||
                ($skipDiscardable && ($cookie->getDiscard() || !$cookie->getExpires())) ||
                ($path && !$cookie->matchesPath($path)) ||
                ($domain && !$cookie->matchesDomain($domain)));
        }));
    }

    public function add(Cookie $cookie)
    {
        // Only allow cookies with set and valid domain, name, value
        $result = $cookie->validate();
        if ($result !== true) {
            if ($this->strictMode) {
                throw new InvalidCookieException($result);
            } else {
                $this->removeCookieIfEmpty($cookie);
                return false;
            }
        }

        // Resolve conflicts with previously set cookies
        foreach ($this->cookies as $i => $c) {

            // Two cookies are identical, when their path, domain, port and name are identical
            if ($c->getPath() != $cookie->getPath() ||
                $c->getDomain() != $cookie->getDomain() ||
                $c->getPorts() != $cookie->getPorts() ||
                $c->getName() != $cookie->getName()
            ) {
                continue;
            }

            // The previously set cookie is a discard cookie and this one is not so allow the new cookie to be set
            if (!$cookie->getDiscard() && $c->getDiscard()) {
                unset($this->cookies[$i]);
                continue;
            }

            // If the new cookie's expiration is further into the future, then replace the old cookie
            if ($cookie->getExpires() > $c->getExpires()) {
                unset($this->cookies[$i]);
                continue;
            }

            // If the value has changed, we better change it
            if ($cookie->getValue() !== $c->getValue()) {
                unset($this->cookies[$i]);
                continue;
            }

            // The cookie exists, so no need to continue
            return false;
        }

        $this->cookies[] = $cookie;

        return true;
    }

    /**
     * Serializes the cookie cookieJar
     *
     * @return string
     */
    public function serialize()
    {
        // Only serialize long term cookies and unexpired cookies
        return json_encode(array_map(function (Cookie $cookie) {
            return $cookie->toArray();
        }, $this->all(null, null, null, true, true)));
    }

    /**
     * Unserializes the cookie cookieJar
     */
    public function unserialize($data)
    {
        $data = json_decode($data, true);
        if (empty($data)) {
            $this->cookies = array();
        } else {
            $this->cookies = array_map(function (array $cookie) {
                return new Cookie($cookie);
            }, $data);
        }
    }

    /**
     * Returns the total number of stored cookies
     *
     * @return int
     */
    public function count()
    {
        return count($this->cookies);
    }

    /**
     * Returns an iterator
     *
     * @return \ArrayIterator
     */
    public function getIterator()
    {
        return new \ArrayIterator($this->cookies);
    }

    public function addCookiesFromResponse(Response $response, RequestInterface $request = null)
    {
        if ($cookieHeader = $response->getHeader('Set-Cookie')) {
            $parser = ParserRegistry::getInstance()->getParser('cookie');
            foreach ($cookieHeader as $cookie) {
                if ($parsed = $request
                    ? $parser->parseCookie($cookie, $request->getHost(), $request->getPath())
                    : $parser->parseCookie($cookie)
                ) {
                    // Break up cookie v2 into multiple cookies
                    foreach ($parsed['cookies'] as $key => $value) {
                        $row = $parsed;
                        $row['name'] = $key;
                        $row['value'] = $value;
                        unset($row['cookies']);
                        $this->add(new Cookie($row));
                    }
                }
            }
        }
    }

    public function getMatchingCookies(RequestInterface $request)
    {
        // Find cookies that match this request
        $cookies = $this->all($request->getHost(), $request->getPath());
        // Remove ineligible cookies
        foreach ($cookies as $index => $cookie) {
            if (!$cookie->matchesPort($request->getPort()) || ($cookie->getSecure() && $request->getScheme() != 'https')) {
                unset($cookies[$index]);
            }
        };

        return $cookies;
    }

    /**
     * If a cookie already exists and the server asks to set it again with a null value, the
     * cookie must be deleted.
     *
     * @param \Guzzle\Plugin\Cookie\Cookie $cookie
     */
    private function removeCookieIfEmpty(Cookie $cookie)
    {
        $cookieValue = $cookie->getValue();
        if ($cookieValue === null || $cookieValue === '') {
            $this->remove($cookie->getDomain(), $cookie->getPath(), $cookie->getName());
        }
    }
}
vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/LinearBackoffStrategy.php000064400000001615151327705700022475 0ustar00<?php

namespace Guzzle\Plugin\Backoff;

use Guzzle\Http\Message\RequestInterface;
use Guzzle\Http\Message\Response;
use Guzzle\Http\Exception\HttpException;

/**
 * Implements a linear backoff retry strategy.
 *
 * Warning: If no decision making strategies precede this strategy in the the chain, then all requests will be retried
 */
class LinearBackoffStrategy extends AbstractBackoffStrategy
{
    /** @var int Amount of time to progress each delay */
    protected $step;

    /**
     * @param int $step Amount of time to increase the delay each additional backoff
     */
    public function __construct($step = 1)
    {
        $this->step = $step;
    }

    public function makesDecision()
    {
        return false;
    }

    protected function getDelay($retries, RequestInterface $request, Response $response = null, HttpException $e = null)
    {
        return $retries * $this->step;
    }
}
vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/TruncatedBackoffStrategy.php000064400000001717151327705700023217 0ustar00<?php

namespace Guzzle\Plugin\Backoff;

use Guzzle\Http\Message\RequestInterface;
use Guzzle\Http\Message\Response;
use Guzzle\Http\Exception\HttpException;

/**
 * Strategy that will not retry more than a certain number of times.
 */
class TruncatedBackoffStrategy extends AbstractBackoffStrategy
{
    /** @var int Maximum number of retries per request */
    protected $max;

    /**
     * @param int                      $maxRetries Maximum number of retries per request
     * @param BackoffStrategyInterface $next The optional next strategy
     */
    public function __construct($maxRetries, BackoffStrategyInterface $next = null)
    {
        $this->max = $maxRetries;
        $this->next = $next;
    }

    public function makesDecision()
    {
        return true;
    }

    protected function getDelay($retries, RequestInterface $request, Response $response = null, HttpException $e = null)
    {
        return $retries < $this->max ? null : false;
    }
}
vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/BackoffPlugin.php000064400000010475151327705700021002 0ustar00<?php

namespace Guzzle\Plugin\Backoff;

use Guzzle\Common\Event;
use Guzzle\Common\AbstractHasDispatcher;
use Guzzle\Http\Message\EntityEnclosingRequestInterface;
use Guzzle\Http\Message\RequestInterface;
use Guzzle\Http\Curl\CurlMultiInterface;
use Guzzle\Http\Exception\CurlException;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;

/**
 * Plugin to automatically retry failed HTTP requests using a backoff strategy
 */
class BackoffPlugin extends AbstractHasDispatcher implements EventSubscriberInterface
{
    const DELAY_PARAM = CurlMultiInterface::BLOCKING;
    const RETRY_PARAM = 'plugins.backoff.retry_count';
    const RETRY_EVENT = 'plugins.backoff.retry';

    /** @var BackoffStrategyInterface Backoff strategy */
    protected $strategy;

    /**
     * @param BackoffStrategyInterface $strategy The backoff strategy used to determine whether or not to retry and
     *                                           the amount of delay between retries.
     */
    public function __construct(BackoffStrategyInterface $strategy = null)
    {
        $this->strategy = $strategy;
    }

    /**
     * Retrieve a basic truncated exponential backoff plugin that will retry HTTP errors and cURL errors
     *
     * @param int   $maxRetries Maximum number of retries
     * @param array $httpCodes  HTTP response codes to retry
     * @param array $curlCodes  cURL error codes to retry
     *
     * @return self
     */
    public static function getExponentialBackoff(
        $maxRetries = 3,
        array $httpCodes = null,
        array $curlCodes = null
    ) {
        return new self(new TruncatedBackoffStrategy($maxRetries,
            new HttpBackoffStrategy($httpCodes,
                new CurlBackoffStrategy($curlCodes,
                    new ExponentialBackoffStrategy()
                )
            )
        ));
    }

    public static function getAllEvents()
    {
        return array(self::RETRY_EVENT);
    }

    public static function getSubscribedEvents()
    {
        return array(
            'request.sent'      => 'onRequestSent',
            'request.exception' => 'onRequestSent',
            CurlMultiInterface::POLLING_REQUEST => 'onRequestPoll'
        );
    }

    /**
     * Called when a request has been sent  and isn't finished processing
     *
     * @param Event $event
     */
    public function onRequestSent(Event $event)
    {
        $request = $event['request'];
        $response = $event['response'];
        $exception = $event['exception'];

        $params = $request->getParams();
        $retries = (int) $params->get(self::RETRY_PARAM);
        $delay = $this->strategy->getBackoffPeriod($retries, $request, $response, $exception);

        if ($delay !== false) {
            // Calculate how long to wait until the request should be retried
            $params->set(self::RETRY_PARAM, ++$retries)
                ->set(self::DELAY_PARAM, microtime(true) + $delay);
            // Send the request again
            $request->setState(RequestInterface::STATE_TRANSFER);
            $this->dispatch(self::RETRY_EVENT, array(
                'request'  => $request,
                'response' => $response,
                'handle'   => ($exception && $exception instanceof CurlException) ? $exception->getCurlHandle() : null,
                'retries'  => $retries,
                'delay'    => $delay
            ));
        }
    }

    /**
     * Called when a request is polling in the curl multi object
     *
     * @param Event $event
     */
    public function onRequestPoll(Event $event)
    {
        $request = $event['request'];
        $delay = $request->getParams()->get(self::DELAY_PARAM);

        // If the duration of the delay has passed, retry the request using the pool
        if (null !== $delay && microtime(true) >= $delay) {
            // Remove the request from the pool and then add it back again. This is required for cURL to know that we
            // want to retry sending the easy handle.
            $request->getParams()->remove(self::DELAY_PARAM);
            // Rewind the request body if possible
            if ($request instanceof EntityEnclosingRequestInterface && $request->getBody()) {
                $request->getBody()->seek(0);
            }
            $multi = $event['curl_multi'];
            $multi->remove($request);
            $multi->add($request);
        }
    }
}
vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/HttpBackoffStrategy.php000064400000001606151327705700022202 0ustar00<?php

namespace Guzzle\Plugin\Backoff;

use Guzzle\Http\Message\RequestInterface;
use Guzzle\Http\Message\Response;
use Guzzle\Http\Exception\HttpException;

/**
 * Strategy used to retry HTTP requests based on the response code.
 *
 * Retries 500 and 503 error by default.
 */
class HttpBackoffStrategy extends AbstractErrorCodeBackoffStrategy
{
    /** @var array Default cURL errors to retry */
    protected static $defaultErrorCodes = array(500, 503);

    protected function getDelay($retries, RequestInterface $request, Response $response = null, HttpException $e = null)
    {
        if ($response) {
            //Short circuit the rest of the checks if it was successful
            if ($response->isSuccessful()) {
                return false;
            } else {
                return isset($this->errorCodes[$response->getStatusCode()]) ? true : null;
            }
        }
    }
}
vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/CurlBackoffStrategy.php000064400000001737151327705700022175 0ustar00<?php

namespace Guzzle\Plugin\Backoff;

use Guzzle\Http\Message\RequestInterface;
use Guzzle\Http\Message\Response;
use Guzzle\Http\Exception\HttpException;
use Guzzle\Http\Exception\CurlException;

/**
 * Strategy used to retry when certain cURL error codes are encountered.
 */
class CurlBackoffStrategy extends AbstractErrorCodeBackoffStrategy
{
    /** @var array Default cURL errors to retry */
    protected static $defaultErrorCodes = array(
        CURLE_COULDNT_RESOLVE_HOST, CURLE_COULDNT_CONNECT, CURLE_PARTIAL_FILE, CURLE_WRITE_ERROR, CURLE_READ_ERROR,
        CURLE_OPERATION_TIMEOUTED, CURLE_SSL_CONNECT_ERROR, CURLE_HTTP_PORT_FAILED, CURLE_GOT_NOTHING,
        CURLE_SEND_ERROR, CURLE_RECV_ERROR
    );

    protected function getDelay($retries, RequestInterface $request, Response $response = null, HttpException $e = null)
    {
        if ($e && $e instanceof CurlException) {
            return isset($this->errorCodes[$e->getErrorNo()]) ? true : null;
        }
    }
}
vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/ReasonPhraseBackoffStrategy.php000064400000001256151327705700023656 0ustar00<?php

namespace Guzzle\Plugin\Backoff;

use Guzzle\Http\Message\RequestInterface;
use Guzzle\Http\Message\Response;
use Guzzle\Http\Exception\HttpException;

/**
 * Strategy used to retry HTTP requests when the response's reason phrase matches one of the registered phrases.
 */
class ReasonPhraseBackoffStrategy extends AbstractErrorCodeBackoffStrategy
{
    public function makesDecision()
    {
        return true;
    }

    protected function getDelay($retries, RequestInterface $request, Response $response = null, HttpException $e = null)
    {
        if ($response) {
            return isset($this->errorCodes[$response->getReasonPhrase()]) ? true : null;
        }
    }
}
vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/ExponentialBackoffStrategy.php000064400000001216151327705700023546 0ustar00<?php

namespace Guzzle\Plugin\Backoff;

use Guzzle\Http\Message\RequestInterface;
use Guzzle\Http\Message\Response;
use Guzzle\Http\Exception\HttpException;

/**
 * Implements an exponential backoff retry strategy.
 *
 * Warning: If no decision making strategies precede this strategy in the the chain, then all requests will be retried
 */
class ExponentialBackoffStrategy extends AbstractBackoffStrategy
{
    public function makesDecision()
    {
        return false;
    }

    protected function getDelay($retries, RequestInterface $request, Response $response = null, HttpException $e = null)
    {
        return (int) pow(2, $retries);
    }
}
vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/AbstractErrorCodeBackoffStrategy.php000064400000002025151327705700024627 0ustar00<?php

namespace Guzzle\Plugin\Backoff;

/**
 * Strategy used to retry when certain error codes are encountered
 */
abstract class AbstractErrorCodeBackoffStrategy extends AbstractBackoffStrategy
{
    /** @var array Default cURL errors to retry */
    protected static $defaultErrorCodes = array();

    /** @var array Error codes that can be retried */
    protected $errorCodes;

    /**
     * @param array                    $codes Array of codes that should be retried
     * @param BackoffStrategyInterface $next  The optional next strategy
     */
    public function __construct(array $codes = null, BackoffStrategyInterface $next = null)
    {
        $this->errorCodes = array_fill_keys($codes ?: static::$defaultErrorCodes, 1);
        $this->next = $next;
    }

    /**
     * Get the default failure codes to retry
     *
     * @return array
     */
    public static function getDefaultFailureCodes()
    {
        return static::$defaultErrorCodes;
    }

    public function makesDecision()
    {
        return true;
    }
}
vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/AbstractBackoffStrategy.php000064400000006111151327705700023022 0ustar00<?php

namespace Guzzle\Plugin\Backoff;

use Guzzle\Http\Message\RequestInterface;
use Guzzle\Http\Message\Response;
use Guzzle\Http\Exception\HttpException;

/**
 * Abstract backoff strategy that allows for a chain of responsibility
 */
abstract class AbstractBackoffStrategy implements BackoffStrategyInterface
{
    /** @var AbstractBackoffStrategy Next strategy in the chain */
    protected $next;

    /** @param AbstractBackoffStrategy $next Next strategy in the chain */
    public function setNext(AbstractBackoffStrategy $next)
    {
        $this->next = $next;
    }

    /**
     * Get the next backoff strategy in the chain
     *
     * @return AbstractBackoffStrategy|null
     */
    public function getNext()
    {
        return $this->next;
    }

    public function getBackoffPeriod(
        $retries,
        RequestInterface $request,
        Response $response = null,
        HttpException $e = null
    ) {
        $delay = $this->getDelay($retries, $request, $response, $e);
        if ($delay === false) {
            // The strategy knows that this must not be retried
            return false;
        } elseif ($delay === null) {
            // If the strategy is deferring a decision and the next strategy will not make a decision then return false
            return !$this->next || !$this->next->makesDecision()
                ? false
                : $this->next->getBackoffPeriod($retries, $request, $response, $e);
        } elseif ($delay === true) {
            // if the strategy knows that it must retry but is deferring to the next to determine the delay
            if (!$this->next) {
                return 0;
            } else {
                $next = $this->next;
                while ($next->makesDecision() && $next->getNext()) {
                    $next = $next->getNext();
                }
                return !$next->makesDecision() ? $next->getBackoffPeriod($retries, $request, $response, $e) : 0;
            }
        } else {
            return $delay;
        }
    }

    /**
     * Check if the strategy does filtering and makes decisions on whether or not to retry.
     *
     * Strategies that return false will never retry if all of the previous strategies in a chain defer on a backoff
     * decision.
     *
     * @return bool
     */
    abstract public function makesDecision();

    /**
     * Implement the concrete strategy
     *
     * @param int              $retries  Number of retries of the request
     * @param RequestInterface $request  Request that was sent
     * @param Response         $response Response that was received. Note that there may not be a response
     * @param HttpException    $e        Exception that was encountered if any
     *
     * @return bool|int|null Returns false to not retry or the number of seconds to delay between retries. Return true
     *                       or null to defer to the next strategy if available, and if not, return 0.
     */
    abstract protected function getDelay(
        $retries,
        RequestInterface $request,
        Response $response = null,
        HttpException $e = null
    );
}
vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/ConstantBackoffStrategy.php000064400000001607151327705700023055 0ustar00<?php

namespace Guzzle\Plugin\Backoff;

use Guzzle\Http\Message\RequestInterface;
use Guzzle\Http\Message\Response;
use Guzzle\Http\Exception\HttpException;

/**
 * Will retry the request using the same amount of delay for each retry.
 *
 * Warning: If no decision making strategies precede this strategy in the the chain, then all requests will be retried
 */
class ConstantBackoffStrategy extends AbstractBackoffStrategy
{
    /** @var int Amount of time for each delay */
    protected $delay;

    /** @param int $delay Amount of time to delay between each additional backoff */
    public function __construct($delay)
    {
        $this->delay = $delay;
    }

    public function makesDecision()
    {
        return false;
    }

    protected function getDelay($retries, RequestInterface $request, Response $response = null, HttpException $e = null)
    {
        return $this->delay;
    }
}
vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/BackoffStrategyInterface.php000064400000001743151327705700023165 0ustar00<?php

namespace Guzzle\Plugin\Backoff;

use Guzzle\Http\Message\RequestInterface;
use Guzzle\Http\Message\Response;
use Guzzle\Http\Exception\HttpException;

/**
 * Strategy to determine if a request should be retried and how long to delay between retries
 */
interface BackoffStrategyInterface
{
    /**
     * Get the amount of time to delay in seconds before retrying a request
     *
     * @param int              $retries  Number of retries of the request
     * @param RequestInterface $request  Request that was sent
     * @param Response         $response Response that was received. Note that there may not be a response
     * @param HttpException    $e        Exception that was encountered if any
     *
     * @return bool|int Returns false to not retry or the number of seconds to delay between retries
     */
    public function getBackoffPeriod(
        $retries,
        RequestInterface $request,
        Response $response = null,
        HttpException $e = null
    );
}
vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/BackoffLogger.php000064400000004421151327705700020755 0ustar00<?php

namespace Guzzle\Plugin\Backoff;

use Guzzle\Common\Event;
use Guzzle\Log\LogAdapterInterface;
use Guzzle\Log\MessageFormatter;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;

/**
 * Logs backoff retries triggered from the BackoffPlugin
 *
 * Format your log messages using a template that can contain template substitutions found in {@see MessageFormatter}.
 * In addition to the default template substitutions, there is also:
 *
 * - retries: The number of times the request has been retried
 * - delay:   The amount of time the request is being delayed
 */
class BackoffLogger implements EventSubscriberInterface
{
    /** @var string Default log message template */
    const DEFAULT_FORMAT = '[{ts}] {method} {url} - {code} {phrase} - Retries: {retries}, Delay: {delay}, Time: {connect_time}, {total_time}, cURL: {curl_code} {curl_error}';

    /** @var LogAdapterInterface Logger used to log retries */
    protected $logger;

    /** @var MessageFormatter Formatter used to format log messages */
    protected $formatter;

    /**
     * @param LogAdapterInterface $logger    Logger used to log the retries
     * @param MessageFormatter    $formatter Formatter used to format log messages
     */
    public function __construct(LogAdapterInterface $logger, MessageFormatter $formatter = null)
    {
        $this->logger = $logger;
        $this->formatter = $formatter ?: new MessageFormatter(self::DEFAULT_FORMAT);
    }

    public static function getSubscribedEvents()
    {
        return array(BackoffPlugin::RETRY_EVENT => 'onRequestRetry');
    }

    /**
     * Set the template to use for logging
     *
     * @param string $template Log message template
     *
     * @return self
     */
    public function setTemplate($template)
    {
        $this->formatter->setTemplate($template);

        return $this;
    }

    /**
     * Called when a request is being retried
     *
     * @param Event $event Event emitted
     */
    public function onRequestRetry(Event $event)
    {
        $this->logger->log($this->formatter->format(
            $event['request'],
            $event['response'],
            $event['handle'],
            array(
                'retries' => $event['retries'],
                'delay'   => $event['delay']
            )
        ));
    }
}
vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/CallbackBackoffStrategy.php000064400000002764151327705700022765 0ustar00<?php

namespace Guzzle\Plugin\Backoff;

use Guzzle\Common\Exception\InvalidArgumentException;
use Guzzle\Http\Message\RequestInterface;
use Guzzle\Http\Message\Response;
use Guzzle\Http\Exception\HttpException;

/**
 * Strategy that will invoke a closure to determine whether or not to retry with a delay
 */
class CallbackBackoffStrategy extends AbstractBackoffStrategy
{
    /** @var \Closure|array|mixed Callable method to invoke */
    protected $callback;

    /** @var bool Whether or not this strategy makes a retry decision */
    protected $decision;

    /**
     * @param \Closure|array|mixed     $callback Callable method to invoke
     * @param bool                     $decision Set to true if this strategy makes a backoff decision
     * @param BackoffStrategyInterface $next     The optional next strategy
     *
     * @throws InvalidArgumentException
     */
    public function __construct($callback, $decision, BackoffStrategyInterface $next = null)
    {
        if (!is_callable($callback)) {
            throw new InvalidArgumentException('The callback must be callable');
        }
        $this->callback = $callback;
        $this->decision = (bool) $decision;
        $this->next = $next;
    }

    public function makesDecision()
    {
        return $this->decision;
    }

    protected function getDelay($retries, RequestInterface $request, Response $response = null, HttpException $e = null)
    {
        return call_user_func($this->callback, $retries, $request, $response, $e);
    }
}
vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/composer.json000064400000001311151327705700020266 0ustar00{
    "name": "guzzle/plugin-backoff",
    "description": "Guzzle backoff retry plugins",
    "homepage": "http://guzzlephp.org/",
    "keywords": ["plugin", "guzzle"],
    "license": "MIT",
    "authors": [
        {
            "name": "Michael Dowling",
            "email": "mtdowling@gmail.com",
            "homepage": "https://github.com/mtdowling"
        }
    ],
    "require": {
        "php": ">=5.3.2",
        "guzzle/http": "self.version",
        "guzzle/log": "self.version"
    },
    "autoload": {
        "psr-0": { "Guzzle\\Plugin\\Backoff": "" }
    },
    "target-dir": "Guzzle/Plugin/Backoff",
    "extra": {
        "branch-alias": {
            "dev-master": "3.7-dev"
        }
    }
}
vendor/guzzle/guzzle/src/Guzzle/Plugin/ErrorResponse/ErrorResponseExceptionInterface.php000064400000001126151327705700026046 0ustar00<?php

namespace Guzzle\Plugin\ErrorResponse;

use Guzzle\Service\Command\CommandInterface;
use Guzzle\Http\Message\Response;

/**
 * Interface used to create an exception from an error response
 */
interface ErrorResponseExceptionInterface
{
    /**
     * Create an exception for a command based on a command and an error response definition
     *
     * @param CommandInterface $command  Command that was sent
     * @param Response         $response The error response
     *
     * @return self
     */
    public static function fromCommand(CommandInterface $command, Response $response);
}
vendor/guzzle/guzzle/src/Guzzle/Plugin/ErrorResponse/ErrorResponsePlugin.php000064400000005404151327705700023530 0ustar00<?php

namespace Guzzle\Plugin\ErrorResponse;

use Guzzle\Common\Event;
use Guzzle\Http\Message\RequestInterface;
use Guzzle\Service\Command\CommandInterface;
use Guzzle\Service\Description\Operation;
use Guzzle\Plugin\ErrorResponse\Exception\ErrorResponseException;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;

/**
 * Converts generic Guzzle response exceptions into errorResponse exceptions
 */
class ErrorResponsePlugin implements EventSubscriberInterface
{
    public static function getSubscribedEvents()
    {
        return array('command.before_send' => array('onCommandBeforeSend', -1));
    }

    /**
     * Adds a listener to requests before they sent from a command
     *
     * @param Event $event Event emitted
     */
    public function onCommandBeforeSend(Event $event)
    {
        $command = $event['command'];
        if ($operation = $command->getOperation()) {
            if ($operation->getErrorResponses()) {
                $request = $command->getRequest();
                $request->getEventDispatcher()
                    ->addListener('request.complete', $this->getErrorClosure($request, $command, $operation));
            }
        }
    }

    /**
     * @param RequestInterface $request   Request that received an error
     * @param CommandInterface $command   Command that created the request
     * @param Operation        $operation Operation that defines the request and errors
     *
     * @return \Closure Returns a closure
     * @throws ErrorResponseException
     */
    protected function getErrorClosure(RequestInterface $request, CommandInterface $command, Operation $operation)
    {
        return function (Event $event) use ($request, $command, $operation) {
            $response = $event['response'];
            foreach ($operation->getErrorResponses() as $error) {
                if (!isset($error['class'])) {
                    continue;
                }
                if (isset($error['code']) && $response->getStatusCode() != $error['code']) {
                    continue;
                }
                if (isset($error['reason']) && $response->getReasonPhrase() != $error['reason']) {
                    continue;
                }
                $className = $error['class'];
                $errorClassInterface = __NAMESPACE__ . '\\ErrorResponseExceptionInterface';
                if (!class_exists($className)) {
                    throw new ErrorResponseException("{$className} does not exist");
                } elseif (!(in_array($errorClassInterface, class_implements($className)))) {
                    throw new ErrorResponseException("{$className} must implement {$errorClassInterface}");
                }
                throw $className::fromCommand($command, $response);
            }
        };
    }
}
vendor/guzzle/guzzle/src/Guzzle/Plugin/ErrorResponse/composer.json000064400000001365151327705700021554 0ustar00{
    "name": "guzzle/plugin-error-response",
    "description": "Guzzle errorResponse plugin for creating error exceptions based on a service description",
    "homepage": "http://guzzlephp.org/",
    "keywords": ["plugin", "guzzle"],
    "license": "MIT",
    "authors": [
        {
            "name": "Michael Dowling",
            "email": "mtdowling@gmail.com",
            "homepage": "https://github.com/mtdowling"
        }
    ],
    "require": {
        "php": ">=5.3.2",
        "guzzle/service": "self.version"
    },
    "autoload": {
        "psr-0": { "Guzzle\\Plugin\\ErrorResponse": "" }
    },
    "target-dir": "Guzzle/Plugin/ErrorResponse",
    "extra": {
        "branch-alias": {
            "dev-master": "3.7-dev"
        }
    }
}
vendor/guzzle/guzzle/src/Guzzle/Plugin/ErrorResponse/Exception/ErrorResponseException.php000064400000000241151327705700026160 0ustar00<?php

namespace Guzzle\Plugin\ErrorResponse\Exception;

use Guzzle\Common\Exception\RuntimeException;

class ErrorResponseException extends RuntimeException {}
vendor/guzzle/guzzle/src/Guzzle/Plugin/Mock/MockPlugin.php000064400000015516151327705700017677 0ustar00<?php

namespace Guzzle\Plugin\Mock;

use Guzzle\Common\Event;
use Guzzle\Common\Exception\InvalidArgumentException;
use Guzzle\Common\AbstractHasDispatcher;
use Guzzle\Http\Exception\CurlException;
use Guzzle\Http\Message\RequestInterface;
use Guzzle\Http\Message\EntityEnclosingRequestInterface;
use Guzzle\Http\Message\Response;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;

/**
 * Queues mock responses or exceptions and delivers mock responses or exceptions in a fifo order.
 */
class MockPlugin extends AbstractHasDispatcher implements EventSubscriberInterface, \Countable
{
    /** @var array Array of mock responses / exceptions */
    protected $queue = array();

    /** @var bool Whether or not to remove the plugin when the queue is empty */
    protected $temporary = false;

    /** @var array Array of requests that were mocked */
    protected $received = array();

    /** @var bool Whether or not to consume an entity body when a mock response is served */
    protected $readBodies;

    /**
     * @param array $items      Array of responses or exceptions to queue
     * @param bool  $temporary  Set to TRUE to remove the plugin when the queue is empty
     * @param bool  $readBodies Set to TRUE to consume the entity body when a mock is served
     */
    public function __construct(array $items = null, $temporary = false, $readBodies = false)
    {
        $this->readBodies = $readBodies;
        $this->temporary = $temporary;
        if ($items) {
            foreach ($items as $item) {
                if ($item instanceof \Exception) {
                    $this->addException($item);
                } else {
                    $this->addResponse($item);
                }
            }
        }
    }

    public static function getSubscribedEvents()
    {
        // Use a number lower than the CachePlugin
        return array('request.before_send' => array('onRequestBeforeSend', -999));
    }

    public static function getAllEvents()
    {
        return array('mock.request');
    }

    /**
     * Get a mock response from a file
     *
     * @param string $path File to retrieve a mock response from
     *
     * @return Response
     * @throws InvalidArgumentException if the file is not found
     */
    public static function getMockFile($path)
    {
        if (!file_exists($path)) {
            throw new InvalidArgumentException('Unable to open mock file: ' . $path);
        }

        return Response::fromMessage(file_get_contents($path));
    }

    /**
     * Set whether or not to consume the entity body of a request when a mock
     * response is used
     *
     * @param bool $readBodies Set to true to read and consume entity bodies
     *
     * @return self
     */
    public function readBodies($readBodies)
    {
        $this->readBodies = $readBodies;

        return $this;
    }

    /**
     * Returns the number of remaining mock responses
     *
     * @return int
     */
    public function count()
    {
        return count($this->queue);
    }

    /**
     * Add a response to the end of the queue
     *
     * @param string|Response $response Response object or path to response file
     *
     * @return MockPlugin
     * @throws InvalidArgumentException if a string or Response is not passed
     */
    public function addResponse($response)
    {
        if (!($response instanceof Response)) {
            if (!is_string($response)) {
                throw new InvalidArgumentException('Invalid response');
            }
            $response = self::getMockFile($response);
        }

        $this->queue[] = $response;

        return $this;
    }

    /**
     * Add an exception to the end of the queue
     *
     * @param CurlException $e Exception to throw when the request is executed
     *
     * @return MockPlugin
     */
    public function addException(CurlException $e)
    {
        $this->queue[] = $e;

        return $this;
    }

    /**
     * Clear the queue
     *
     * @return MockPlugin
     */
    public function clearQueue()
    {
        $this->queue = array();

        return $this;
    }

    /**
     * Returns an array of mock responses remaining in the queue
     *
     * @return array
     */
    public function getQueue()
    {
        return $this->queue;
    }

    /**
     * Check if this is a temporary plugin
     *
     * @return bool
     */
    public function isTemporary()
    {
        return $this->temporary;
    }

    /**
     * Get a response from the front of the list and add it to a request
     *
     * @param RequestInterface $request Request to mock
     *
     * @return self
     * @throws CurlException When request.send is called and an exception is queued
     */
    public function dequeue(RequestInterface $request)
    {
        $this->dispatch('mock.request', array('plugin' => $this, 'request' => $request));

        $item = array_shift($this->queue);
        if ($item instanceof Response) {
            if ($this->readBodies && $request instanceof EntityEnclosingRequestInterface) {
                $request->getEventDispatcher()->addListener('request.sent', $f = function (Event $event) use (&$f) {
                    while ($data = $event['request']->getBody()->read(8096));
                    // Remove the listener after one-time use
                    $event['request']->getEventDispatcher()->removeListener('request.sent', $f);
                });
            }
            $request->setResponse($item);
        } elseif ($item instanceof CurlException) {
            // Emulates exceptions encountered while transferring requests
            $item->setRequest($request);
            $state = $request->setState(RequestInterface::STATE_ERROR, array('exception' => $item));
            // Only throw if the exception wasn't handled
            if ($state == RequestInterface::STATE_ERROR) {
                throw $item;
            }
        }

        return $this;
    }

    /**
     * Clear the array of received requests
     */
    public function flush()
    {
        $this->received = array();
    }

    /**
     * Get an array of requests that were mocked by this plugin
     *
     * @return array
     */
    public function getReceivedRequests()
    {
        return $this->received;
    }

    /**
     * Called when a request is about to be sent
     *
     * @param Event $event
     * @throws \OutOfBoundsException When queue is empty
     */
    public function onRequestBeforeSend(Event $event)
    {
        if (!$this->queue) {
            throw new \OutOfBoundsException('Mock queue is empty');
        }

        $request = $event['request'];
        $this->received[] = $request;
        // Detach the filter from the client so it's a one-time use
        if ($this->temporary && count($this->queue) == 1 && $request->getClient()) {
            $request->getClient()->getEventDispatcher()->removeSubscriber($this);
        }
        $this->dequeue($request);
    }
}
vendor/guzzle/guzzle/src/Guzzle/Plugin/Mock/composer.json000064400000001230151327705700017624 0ustar00{
    "name": "guzzle/plugin-mock",
    "description": "Guzzle Mock plugin",
    "homepage": "http://guzzlephp.org/",
    "keywords": ["mock", "plugin", "guzzle"],
    "license": "MIT",
    "authors": [
        {
            "name": "Michael Dowling",
            "email": "mtdowling@gmail.com",
            "homepage": "https://github.com/mtdowling"
        }
    ],
    "require": {
        "php": ">=5.3.2",
        "guzzle/http": "self.version"
    },
    "autoload": {
        "psr-0": { "Guzzle\\Plugin\\Mock": "" }
    },
    "target-dir": "Guzzle/Plugin/Mock",
    "extra": {
        "branch-alias": {
            "dev-master": "3.7-dev"
        }
    }
}
vendor/guzzle/guzzle/src/Guzzle/Batch/BatchBuilder.php000064400000012753151327705700017051 0ustar00<?php

namespace Guzzle\Batch;

use Guzzle\Common\Exception\InvalidArgumentException;
use Guzzle\Common\Exception\RuntimeException;

/**
 * Builder used to create custom batch objects
 */
class BatchBuilder
{
    /** @var bool Whether or not the batch should automatically flush*/
    protected $autoFlush = false;

    /** @var bool Whether or not to maintain a batch history */
    protected $history = false;

    /** @var bool Whether or not to buffer exceptions encountered in transfer */
    protected $exceptionBuffering = false;

    /** @var mixed Callable to invoke each time a flush completes */
    protected $afterFlush;

    /** @var BatchTransferInterface Object used to transfer items in the queue */
    protected $transferStrategy;

    /** @var BatchDivisorInterface Object used to divide the queue into batches */
    protected $divisorStrategy;

    /** @var array of Mapped transfer strategies by handle name */
    protected static $mapping = array(
        'request' => 'Guzzle\Batch\BatchRequestTransfer',
        'command' => 'Guzzle\Batch\BatchCommandTransfer'
    );

    /**
     * Create a new instance of the BatchBuilder
     *
     * @return BatchBuilder
     */
    public static function factory()
    {
        return new self();
    }

    /**
     * Automatically flush the batch when the size of the queue reaches a certain threshold. Adds {@see FlushingBatch}.
     *
     * @param $threshold Number of items to allow in the queue before a flush
     *
     * @return BatchBuilder
     */
    public function autoFlushAt($threshold)
    {
        $this->autoFlush = $threshold;

        return $this;
    }

    /**
     * Maintain a history of all items that have been transferred using the batch. Adds {@see HistoryBatch}.
     *
     * @return BatchBuilder
     */
    public function keepHistory()
    {
        $this->history = true;

        return $this;
    }

    /**
     * Buffer exceptions thrown during transfer so that you can transfer as much as possible, and after a transfer
     * completes, inspect each exception that was thrown. Enables the {@see ExceptionBufferingBatch} decorator.
     *
     * @return BatchBuilder
     */
    public function bufferExceptions()
    {
        $this->exceptionBuffering = true;

        return $this;
    }

    /**
     * Notify a callable each time a batch flush completes. Enables the {@see NotifyingBatch} decorator.
     *
     * @param mixed $callable Callable function to notify
     *
     * @return BatchBuilder
     * @throws InvalidArgumentException if the argument is not callable
     */
    public function notify($callable)
    {
        $this->afterFlush = $callable;

        return $this;
    }

    /**
     * Configures the batch to transfer batches of requests. Associates a {@see \Guzzle\Http\BatchRequestTransfer}
     * object as both the transfer and divisor strategy.
     *
     * @param int $batchSize Batch size for each batch of requests
     *
     * @return BatchBuilder
     */
    public function transferRequests($batchSize = 50)
    {
        $className = self::$mapping['request'];
        $this->transferStrategy = new $className($batchSize);
        $this->divisorStrategy = $this->transferStrategy;

        return $this;
    }

    /**
     * Configures the batch to transfer batches commands. Associates as
     * {@see \Guzzle\Service\Command\BatchCommandTransfer} as both the transfer and divisor strategy.
     *
     * @param int $batchSize Batch size for each batch of commands
     *
     * @return BatchBuilder
     */
    public function transferCommands($batchSize = 50)
    {
        $className = self::$mapping['command'];
        $this->transferStrategy = new $className($batchSize);
        $this->divisorStrategy = $this->transferStrategy;

        return $this;
    }

    /**
     * Specify the strategy used to divide the queue into an array of batches
     *
     * @param BatchDivisorInterface $divisorStrategy Strategy used to divide a batch queue into batches
     *
     * @return BatchBuilder
     */
    public function createBatchesWith(BatchDivisorInterface $divisorStrategy)
    {
        $this->divisorStrategy = $divisorStrategy;

        return $this;
    }

    /**
     * Specify the strategy used to transport the items when flush is called
     *
     * @param BatchTransferInterface $transferStrategy How items are transferred
     *
     * @return BatchBuilder
     */
    public function transferWith(BatchTransferInterface $transferStrategy)
    {
        $this->transferStrategy = $transferStrategy;

        return $this;
    }

    /**
     * Create and return the instantiated batch
     *
     * @return BatchInterface
     * @throws RuntimeException if no transfer strategy has been specified
     */
    public function build()
    {
        if (!$this->transferStrategy) {
            throw new RuntimeException('No transfer strategy has been specified');
        }

        if (!$this->divisorStrategy) {
            throw new RuntimeException('No divisor strategy has been specified');
        }

        $batch = new Batch($this->transferStrategy, $this->divisorStrategy);

        if ($this->exceptionBuffering) {
            $batch = new ExceptionBufferingBatch($batch);
        }

        if ($this->afterFlush) {
            $batch = new NotifyingBatch($batch, $this->afterFlush);
        }

        if ($this->autoFlush) {
            $batch = new FlushingBatch($batch, $this->autoFlush);
        }

        if ($this->history) {
            $batch = new HistoryBatch($batch);
        }

        return $batch;
    }
}
vendor/guzzle/guzzle/src/Guzzle/Batch/BatchInterface.php000064400000001115151327705700017351 0ustar00<?php

namespace Guzzle\Batch;

/**
 * Interface for efficiently transferring items in a queue using batches
 */
interface BatchInterface
{
    /**
     * Add an item to the queue
     *
     * @param mixed $item Item to add
     *
     * @return self
     */
    public function add($item);

    /**
     * Flush the batch and transfer the items
     *
     * @return array Returns an array flushed items
     */
    public function flush();

    /**
     * Check if the batch is empty and has further items to transfer
     *
     * @return bool
     */
    public function isEmpty();
}
vendor/guzzle/guzzle/src/Guzzle/Batch/AbstractBatchDecorator.php000064400000002737151327705700021072 0ustar00<?php

namespace Guzzle\Batch;

/**
 * Abstract decorator used when decorating a BatchInterface
 */
abstract class AbstractBatchDecorator implements BatchInterface
{
    /** @var BatchInterface Decorated batch object */
    protected $decoratedBatch;

    /**
     * @param BatchInterface $decoratedBatch  BatchInterface that is being decorated
     */
    public function __construct(BatchInterface $decoratedBatch)
    {
        $this->decoratedBatch = $decoratedBatch;
    }

    /**
     * Allow decorators to implement custom methods
     *
     * @param string $method Missing method name
     * @param array  $args   Method arguments
     *
     * @return mixed
     * @codeCoverageIgnore
     */
    public function __call($method, array $args)
    {
        return call_user_func_array(array($this->decoratedBatch, $method), $args);
    }

    public function add($item)
    {
        $this->decoratedBatch->add($item);

        return $this;
    }

    public function flush()
    {
        return $this->decoratedBatch->flush();
    }

    public function isEmpty()
    {
        return $this->decoratedBatch->isEmpty();
    }

    /**
     * Trace the decorators associated with the batch
     *
     * @return array
     */
    public function getDecorators()
    {
        $found = array($this);
        if (method_exists($this->decoratedBatch, 'getDecorators')) {
            $found = array_merge($found, $this->decoratedBatch->getDecorators());
        }

        return $found;
    }
}
vendor/guzzle/guzzle/src/Guzzle/Batch/BatchDivisorInterface.php000064400000000776151327705700020725 0ustar00<?php

namespace Guzzle\Batch;

/**
 * Interface used for dividing a queue of items into an array of batches
 */
interface BatchDivisorInterface
{
    /**
     * Divide a queue of items into an array batches
     *
     * @param \SplQueue $queue Queue of items to divide into batches. Items are removed as they are iterated.
     *
     * @return array|\Traversable Returns an array or Traversable object that contains arrays of items to transfer
     */
    public function createBatches(\SplQueue $queue);
}
vendor/guzzle/guzzle/src/Guzzle/Batch/NotifyingBatch.php000064400000001675151327705700017432 0ustar00<?php

namespace Guzzle\Batch;

use Guzzle\Common\Exception\InvalidArgumentException;

/**
 * BatchInterface decorator used to call a method each time flush is called
 */
class NotifyingBatch extends AbstractBatchDecorator
{
    /** @var mixed Callable to call */
    protected $callable;

    /**
     * @param BatchInterface $decoratedBatch Batch object to decorate
     * @param mixed          $callable       Callable to call
     *
     * @throws InvalidArgumentException
     */
    public function __construct(BatchInterface $decoratedBatch, $callable)
    {
        if (!is_callable($callable)) {
            throw new InvalidArgumentException('The passed argument is not callable');
        }

        $this->callable = $callable;
        parent::__construct($decoratedBatch);
    }

    public function flush()
    {
        $items = $this->decoratedBatch->flush();
        call_user_func($this->callable, $items);

        return $items;
    }
}
vendor/guzzle/guzzle/src/Guzzle/Batch/BatchTransferInterface.php000064400000000434151327705700021061 0ustar00<?php

namespace Guzzle\Batch;

/**
 * Interface used for transferring batches of items
 */
interface BatchTransferInterface
{
    /**
     * Transfer an array of items
     *
     * @param array $batch Array of items to transfer
     */
    public function transfer(array $batch);
}
vendor/guzzle/guzzle/src/Guzzle/Batch/BatchSizeDivisor.php000064400000001553151327705700017731 0ustar00<?php

namespace Guzzle\Batch;

/**
 * Divides batches into smaller batches under a certain size
 */
class BatchSizeDivisor implements BatchDivisorInterface
{
    /** @var int Size of each batch */
    protected $size;

    /** @param int $size Size of each batch */
    public function __construct($size)
    {
        $this->size = $size;
    }

    /**
     * Set the size of each batch
     *
     * @param int $size Size of each batch
     *
     * @return BatchSizeDivisor
     */
    public function setSize($size)
    {
        $this->size = $size;

        return $this;
    }

    /**
     * Get the size of each batch
     *
     * @return int
     */
    public function getSize()
    {
        return $this->size;
    }

    public function createBatches(\SplQueue $queue)
    {
        return array_chunk(iterator_to_array($queue, false), $this->size);
    }
}
vendor/guzzle/guzzle/src/Guzzle/Batch/Batch.php000064400000005723151327705700015541 0ustar00<?php

namespace Guzzle\Batch;

use Guzzle\Batch\Exception\BatchTransferException;

/**
 * Default batch implementation used to convert queued items into smaller chunks of batches using a
 * {@see BatchDivisorIterface} and transfers each batch using a {@see BatchTransferInterface}.
 *
 * Any exception encountered during a flush operation will throw a {@see BatchTransferException} object containing the
 * batch that failed. After an exception is encountered, you can flush the batch again to attempt to finish transferring
 * any previously created batches or queued items.
 */
class Batch implements BatchInterface
{
    /** @var \SplQueue Queue of items in the queue */
    protected $queue;

    /** @var array Divided batches to be transferred */
    protected $dividedBatches;

    /** @var BatchTransferInterface */
    protected $transferStrategy;

    /** @var BatchDivisorInterface */
    protected $divisionStrategy;

    /**
     * @param BatchTransferInterface $transferStrategy Strategy used to transfer items
     * @param BatchDivisorInterface  $divisionStrategy Divisor used to create batches
     */
    public function __construct(BatchTransferInterface $transferStrategy, BatchDivisorInterface $divisionStrategy)
    {
        $this->transferStrategy = $transferStrategy;
        $this->divisionStrategy = $divisionStrategy;
        $this->queue = new \SplQueue();
        $this->queue->setIteratorMode(\SplQueue::IT_MODE_DELETE);
        $this->dividedBatches = array();
    }

    public function add($item)
    {
        $this->queue->enqueue($item);

        return $this;
    }

    public function flush()
    {
        $this->createBatches();

        $items = array();
        foreach ($this->dividedBatches as $batchIndex => $dividedBatch) {
            while ($dividedBatch->valid()) {
                $batch = $dividedBatch->current();
                $dividedBatch->next();
                try {
                    $this->transferStrategy->transfer($batch);
                    $items = array_merge($items, $batch);
                } catch (\Exception $e) {
                    throw new BatchTransferException($batch, $items, $e, $this->transferStrategy, $this->divisionStrategy);
                }
            }
            // Keep the divided batch down to a minimum in case of a later exception
            unset($this->dividedBatches[$batchIndex]);
        }

        return $items;
    }

    public function isEmpty()
    {
        return count($this->queue) == 0 && count($this->dividedBatches) == 0;
    }

    /**
     * Create batches for any queued items
     */
    protected function createBatches()
    {
        if (count($this->queue)) {
            if ($batches = $this->divisionStrategy->createBatches($this->queue)) {
                // Convert arrays into iterators
                if (is_array($batches)) {
                    $batches = new \ArrayIterator($batches);
                }
                $this->dividedBatches[] = $batches;
            }
        }
    }
}
vendor/guzzle/guzzle/src/Guzzle/Batch/BatchRequestTransfer.php000064400000003602151327705700020611 0ustar00<?php

namespace Guzzle\Batch;

use Guzzle\Batch\BatchTransferInterface;
use Guzzle\Batch\BatchDivisorInterface;
use Guzzle\Common\Exception\InvalidArgumentException;
use Guzzle\Http\Message\RequestInterface;

/**
 * Batch transfer strategy used to efficiently transfer a batch of requests.
 * This class is to be used with {@see Guzzle\Batch\BatchInterface}
 */
class BatchRequestTransfer implements BatchTransferInterface, BatchDivisorInterface
{
    /** @var int Size of each command batch */
    protected $batchSize;

    /**
     * Constructor used to specify how large each batch should be
     *
     * @param int $batchSize Size of each batch
     */
    public function __construct($batchSize = 50)
    {
        $this->batchSize = $batchSize;
    }

    /**
     * Creates batches of requests by grouping requests by their associated curl multi object.
     * {@inheritdoc}
     */
    public function createBatches(\SplQueue $queue)
    {
        // Create batches by client objects
        $groups = new \SplObjectStorage();
        foreach ($queue as $item) {
            if (!$item instanceof RequestInterface) {
                throw new InvalidArgumentException('All items must implement Guzzle\Http\Message\RequestInterface');
            }
            $client = $item->getClient();
            if (!$groups->contains($client)) {
                $groups->attach($client, array($item));
            } else {
                $current = $groups[$client];
                $current[] = $item;
                $groups[$client] = $current;
            }
        }

        $batches = array();
        foreach ($groups as $batch) {
            $batches = array_merge($batches, array_chunk($groups[$batch], $this->batchSize));
        }

        return $batches;
    }

    public function transfer(array $batch)
    {
        if ($batch) {
            reset($batch)->getClient()->send($batch);
        }
    }
}
vendor/guzzle/guzzle/src/Guzzle/Batch/BatchClosureTransfer.php000064400000002276151327705700020603 0ustar00<?php

namespace Guzzle\Batch;

use Guzzle\Common\Exception\InvalidArgumentException;

/**
 * Batch transfer strategy where transfer logic can be defined via a Closure.
 * This class is to be used with {@see Guzzle\Batch\BatchInterface}
 */
class BatchClosureTransfer implements BatchTransferInterface
{
    /** @var callable A closure that performs the transfer */
    protected $callable;

    /** @var mixed $context Context passed to the callable */
    protected $context;

    /**
     * @param mixed $callable Callable that performs the transfer. This function should accept two arguments:
     *                        (array $batch, mixed $context).
     * @param mixed $context  Optional context to pass to the batch divisor
     *
     * @throws InvalidArgumentException
     */
    public function __construct($callable, $context = null)
    {
        if (!is_callable($callable)) {
            throw new InvalidArgumentException('Argument must be callable');
        }

        $this->callable = $callable;
        $this->context = $context;
    }

    public function transfer(array $batch)
    {
        return empty($batch) ? null : call_user_func($this->callable, $batch, $this->context);
    }
}
vendor/guzzle/guzzle/src/Guzzle/Batch/BatchClosureDivisor.php000064400000002164151327705700020432 0ustar00<?php

namespace Guzzle\Batch;

use Guzzle\Common\Exception\InvalidArgumentException;

/**
 * Divides batches using a callable
 */
class BatchClosureDivisor implements BatchDivisorInterface
{
    /** @var callable Method used to divide the batches */
    protected $callable;

    /** @var mixed $context Context passed to the callable */
    protected $context;

    /**
     * @param callable $callable Method used to divide the batches. The method must accept an \SplQueue and return an
     *                           array of arrays containing the divided items.
     * @param mixed    $context  Optional context to pass to the batch divisor
     *
     * @throws InvalidArgumentException if the callable is not callable
     */
    public function __construct($callable, $context = null)
    {
        if (!is_callable($callable)) {
            throw new InvalidArgumentException('Must pass a callable');
        }

        $this->callable = $callable;
        $this->context = $context;
    }

    public function createBatches(\SplQueue $queue)
    {
        return call_user_func($this->callable, $queue, $this->context);
    }
}
vendor/guzzle/guzzle/src/Guzzle/Batch/FlushingBatch.php000064400000002677151327705700017246 0ustar00<?php

namespace Guzzle\Batch;

/**
 * BatchInterface decorator used to add automatic flushing of the queue when the size of the queue reaches a threshold.
 */
class FlushingBatch extends AbstractBatchDecorator
{
    /** @var int The threshold for which to automatically flush */
    protected $threshold;

    /** @var int Current number of items known to be in the queue */
    protected $currentTotal = 0;

    /**
     * @param BatchInterface $decoratedBatch  BatchInterface that is being decorated
     * @param int            $threshold       Flush when the number in queue matches the threshold
     */
    public function __construct(BatchInterface $decoratedBatch, $threshold)
    {
        $this->threshold = $threshold;
        parent::__construct($decoratedBatch);
    }

    /**
     * Set the auto-flush threshold
     *
     * @param int $threshold The auto-flush threshold
     *
     * @return FlushingBatch
     */
    public function setThreshold($threshold)
    {
        $this->threshold = $threshold;

        return $this;
    }

    /**
     * Get the auto-flush threshold
     *
     * @return int
     */
    public function getThreshold()
    {
        return $this->threshold;
    }

    public function add($item)
    {
        $this->decoratedBatch->add($item);
        if (++$this->currentTotal >= $this->threshold) {
            $this->currentTotal = 0;
            $this->decoratedBatch->flush();
        }

        return $this;
    }
}
vendor/guzzle/guzzle/src/Guzzle/Batch/Exception/BatchTransferException.php000064400000005213151327705700023055 0ustar00<?php

namespace Guzzle\Batch\Exception;

use Guzzle\Common\Exception\GuzzleException;
use Guzzle\Batch\BatchTransferInterface as TransferStrategy;
use Guzzle\Batch\BatchDivisorInterface as DivisorStrategy;

/**
 * Exception thrown during a batch transfer
 */
class BatchTransferException extends \Exception implements GuzzleException
{
    /** @var array The batch being sent when the exception occurred */
    protected $batch;

    /** @var TransferStrategy The transfer strategy in use when the exception occurred */
    protected $transferStrategy;

    /** @var DivisorStrategy The divisor strategy in use when the exception occurred */
    protected $divisorStrategy;

    /** @var array Items transferred at the point in which the exception was encountered */
    protected $transferredItems;

    /**
     * @param array            $batch            The batch being sent when the exception occurred
     * @param array            $transferredItems Items transferred at the point in which the exception was encountered
     * @param \Exception       $exception        Exception encountered
     * @param TransferStrategy $transferStrategy The transfer strategy in use when the exception occurred
     * @param DivisorStrategy  $divisorStrategy  The divisor strategy in use when the exception occurred
     */
    public function __construct(
        array $batch,
        array $transferredItems,
        \Exception $exception,
        TransferStrategy $transferStrategy = null,
        DivisorStrategy $divisorStrategy = null
    ) {
        $this->batch = $batch;
        $this->transferredItems = $transferredItems;
        $this->transferStrategy = $transferStrategy;
        $this->divisorStrategy = $divisorStrategy;
        parent::__construct(
            'Exception encountered while transferring batch: ' . $exception->getMessage(),
            $exception->getCode(),
            $exception
        );
    }

    /**
     * Get the batch that we being sent when the exception occurred
     *
     * @return array
     */
    public function getBatch()
    {
        return $this->batch;
    }

    /**
     * Get the items transferred at the point in which the exception was encountered
     *
     * @return array
     */
    public function getTransferredItems()
    {
        return $this->transferredItems;
    }

    /**
     * Get the transfer strategy
     *
     * @return TransferStrategy
     */
    public function getTransferStrategy()
    {
        return $this->transferStrategy;
    }

    /**
     * Get the divisor strategy
     *
     * @return DivisorStrategy
     */
    public function getDivisorStrategy()
    {
        return $this->divisorStrategy;
    }
}
vendor/guzzle/guzzle/src/Guzzle/Batch/ExceptionBufferingBatch.php000064400000002327151327705700021245 0ustar00<?php

namespace Guzzle\Batch;

use Guzzle\Batch\Exception\BatchTransferException;

/**
 * BatchInterface decorator used to buffer exceptions encountered during a transfer.  The exceptions can then later be
 * processed after a batch flush has completed.
 */
class ExceptionBufferingBatch extends AbstractBatchDecorator
{
    /** @var array Array of BatchTransferException exceptions */
    protected $exceptions = array();

    public function flush()
    {
        $items = array();

        while (!$this->decoratedBatch->isEmpty()) {
            try {
                $transferredItems = $this->decoratedBatch->flush();
            } catch (BatchTransferException $e) {
                $this->exceptions[] = $e;
                $transferredItems = $e->getTransferredItems();
            }
            $items = array_merge($items, $transferredItems);
        }

        return $items;
    }

    /**
     * Get the buffered exceptions
     *
     * @return array Array of BatchTransferException objects
     */
    public function getExceptions()
    {
        return $this->exceptions;
    }

    /**
     * Clear the buffered exceptions
     */
    public function clearExceptions()
    {
        $this->exceptions = array();
    }
}
vendor/guzzle/guzzle/src/Guzzle/Batch/HistoryBatch.php000064400000001366151327705700017122 0ustar00<?php

namespace Guzzle\Batch;

/**
 * BatchInterface decorator used to keep a history of items that were added to the batch.  You must clear the history
 * manually to remove items from the history.
 */
class HistoryBatch extends AbstractBatchDecorator
{
    /** @var array Items in the history */
    protected $history = array();

    public function add($item)
    {
        $this->history[] = $item;
        $this->decoratedBatch->add($item);

        return $this;
    }

    /**
     * Get the batch history
     *
     * @return array
     */
    public function getHistory()
    {
        return $this->history;
    }

    /**
     * Clear the batch history
     */
    public function clearHistory()
    {
        $this->history = array();
    }
}
vendor/guzzle/guzzle/src/Guzzle/Batch/composer.json000064400000001457151327705700016531 0ustar00{
    "name": "guzzle/batch",
    "description": "Guzzle batch component for batching requests, commands, or custom transfers",
    "homepage": "http://guzzlephp.org/",
    "keywords": ["batch", "HTTP", "REST", "guzzle"],
    "license": "MIT",
    "authors": [
        {
            "name": "Michael Dowling",
            "email": "mtdowling@gmail.com",
            "homepage": "https://github.com/mtdowling"
        }
    ],
    "require": {
        "php": ">=5.3.2",
        "guzzle/common": "self.version"
    },
    "autoload": {
        "psr-0": { "Guzzle\\Batch": "" }
    },
    "suggest": {
        "guzzle/http": "self.version",
        "guzzle/service": "self.version"
    },
    "target-dir": "Guzzle/Batch",
    "extra": {
        "branch-alias": {
            "dev-master": "3.7-dev"
        }
    }
}
vendor/guzzle/guzzle/src/Guzzle/Batch/BatchCommandTransfer.php000064400000004256151327705700020545 0ustar00<?php

namespace Guzzle\Batch;

use Guzzle\Batch\BatchTransferInterface;
use Guzzle\Batch\BatchDivisorInterface;
use Guzzle\Common\Exception\InvalidArgumentException;
use Guzzle\Service\Command\CommandInterface;
use Guzzle\Service\Exception\InconsistentClientTransferException;

/**
 * Efficiently transfers multiple commands in parallel per client
 * This class is to be used with {@see Guzzle\Batch\BatchInterface}
 */
class BatchCommandTransfer implements BatchTransferInterface, BatchDivisorInterface
{
    /** @var int Size of each command batch */
    protected $batchSize;

    /**
     * @param int $batchSize Size of each batch
     */
    public function __construct($batchSize = 50)
    {
        $this->batchSize = $batchSize;
    }

    /**
     * Creates batches by grouping commands by their associated client
     * {@inheritdoc}
     */
    public function createBatches(\SplQueue $queue)
    {
        $groups = new \SplObjectStorage();
        foreach ($queue as $item) {
            if (!$item instanceof CommandInterface) {
                throw new InvalidArgumentException('All items must implement Guzzle\Service\Command\CommandInterface');
            }
            $client = $item->getClient();
            if (!$groups->contains($client)) {
                $groups->attach($client, new \ArrayObject(array($item)));
            } else {
                $groups[$client]->append($item);
            }
        }

        $batches = array();
        foreach ($groups as $batch) {
            $batches = array_merge($batches, array_chunk($groups[$batch]->getArrayCopy(), $this->batchSize));
        }

        return $batches;
    }

    public function transfer(array $batch)
    {
        if (empty($batch)) {
            return;
        }

        // Get the client of the first found command
        $client = reset($batch)->getClient();

        // Keep a list of all commands with invalid clients
        $invalid = array_filter($batch, function ($command) use ($client) {
            return $command->getClient() !== $client;
        });

        if (!empty($invalid)) {
            throw new InconsistentClientTransferException($invalid);
        }

        $client->execute($batch);
    }
}
vendor/guzzle/guzzle/src/Guzzle/Log/MessageFormatter.php000064400000015717151327705700017474 0ustar00<?php

namespace Guzzle\Log;

use Guzzle\Http\Curl\CurlHandle;
use Guzzle\Http\Message\RequestInterface;
use Guzzle\Http\Message\EntityEnclosingRequestInterface;
use Guzzle\Http\Message\Response;

/**
 * Message formatter used in various places in the framework
 *
 * Format messages using a template that can contain the the following variables:
 *
 * - {request}:       Full HTTP request message
 * - {response}:      Full HTTP response message
 * - {ts}:            Timestamp
 * - {host}:          Host of the request
 * - {method}:        Method of the request
 * - {url}:           URL of the request
 * - {host}:          Host of the request
 * - {protocol}:      Request protocol
 * - {version}:       Protocol version
 * - {resource}:      Resource of the request (path + query + fragment)
 * - {port}:          Port of the request
 * - {hostname}:      Hostname of the machine that sent the request
 * - {code}:          Status code of the response (if available)
 * - {phrase}:        Reason phrase of the response  (if available)
 * - {curl_error}:    Curl error message (if available)
 * - {curl_code}:     Curl error code (if available)
 * - {curl_stderr}:   Curl standard error (if available)
 * - {connect_time}:  Time in seconds it took to establish the connection (if available)
 * - {total_time}:    Total transaction time in seconds for last transfer (if available)
 * - {req_header_*}:  Replace `*` with the lowercased name of a request header to add to the message
 * - {res_header_*}:  Replace `*` with the lowercased name of a response header to add to the message
 * - {req_body}:      Request body
 * - {res_body}:      Response body
 */
class MessageFormatter
{
    const DEFAULT_FORMAT = "{hostname} {req_header_User-Agent} - [{ts}] \"{method} {resource} {protocol}/{version}\" {code} {res_header_Content-Length}";
    const DEBUG_FORMAT = ">>>>>>>>\n{request}\n<<<<<<<<\n{response}\n--------\n{curl_stderr}";
    const SHORT_FORMAT = '[{ts}] "{method} {resource} {protocol}/{version}" {code}';

    /**
     * @var string Template used to format log messages
     */
    protected $template;

    /**
     * @param string $template Log message template
     */
    public function __construct($template = self::DEFAULT_FORMAT)
    {
        $this->template = $template ?: self::DEFAULT_FORMAT;
    }

    /**
     * Set the template to use for logging
     *
     * @param string $template Log message template
     *
     * @return self
     */
    public function setTemplate($template)
    {
        $this->template = $template;

        return $this;
    }

    /**
     * Returns a formatted message
     *
     * @param RequestInterface $request    Request that was sent
     * @param Response         $response   Response that was received
     * @param CurlHandle       $handle     Curl handle associated with the message
     * @param array            $customData Associative array of custom template data
     *
     * @return string
     */
    public function format(
        RequestInterface $request,
        Response $response = null,
        CurlHandle $handle = null,
        array $customData = array()
    ) {
        $cache = $customData;

        return preg_replace_callback(
            '/{\s*([A-Za-z_\-\.0-9]+)\s*}/',
            function (array $matches) use ($request, $response, $handle, &$cache) {

                if (array_key_exists($matches[1], $cache)) {
                    return $cache[$matches[1]];
                }

                $result = '';
                switch ($matches[1]) {
                    case 'request':
                        $result = (string) $request;
                        break;
                    case 'response':
                        $result = (string) $response;
                        break;
                    case 'req_body':
                        $result = $request instanceof EntityEnclosingRequestInterface
                            ? (string) $request->getBody() : '';
                        break;
                    case 'res_body':
                        $result = $response ? $response->getBody(true) : '';
                        break;
                    case 'ts':
                        $result = gmdate('c');
                        break;
                    case 'method':
                        $result = $request->getMethod();
                        break;
                    case 'url':
                        $result = (string) $request->getUrl();
                        break;
                    case 'resource':
                        $result = $request->getResource();
                        break;
                    case 'protocol':
                        $result = 'HTTP';
                        break;
                    case 'version':
                        $result = $request->getProtocolVersion();
                        break;
                    case 'host':
                        $result = $request->getHost();
                        break;
                    case 'hostname':
                        $result = gethostname();
                        break;
                    case 'port':
                        $result = $request->getPort();
                        break;
                    case 'code':
                        $result = $response ? $response->getStatusCode() : '';
                        break;
                    case 'phrase':
                        $result = $response ? $response->getReasonPhrase() : '';
                        break;
                    case 'connect_time':
                        $result = $handle && $handle->getInfo(CURLINFO_CONNECT_TIME)
                            ? $handle->getInfo(CURLINFO_CONNECT_TIME)
                            : ($response ? $response->getInfo('connect_time') : '');
                        break;
                    case 'total_time':
                        $result = $handle && $handle->getInfo(CURLINFO_TOTAL_TIME)
                            ? $handle->getInfo(CURLINFO_TOTAL_TIME)
                            : ($response ? $response->getInfo('total_time') : '');
                        break;
                    case 'curl_error':
                        $result = $handle ? $handle->getError() : '';
                        break;
                    case 'curl_code':
                        $result = $handle ? $handle->getErrorNo() : '';
                        break;
                    case 'curl_stderr':
                        $result =  $handle ? $handle->getStderr() : '';
                        break;
                    default:
                        if (strpos($matches[1], 'req_header_') === 0) {
                            $result = $request->getHeader(substr($matches[1], 11));
                        } elseif ($response && strpos($matches[1], 'res_header_') === 0) {
                            $result = $response->getHeader(substr($matches[1], 11));
                        }
                }

                $cache[$matches[1]] = $result;
                return $result;
            },
            $this->template
        );
    }
}
vendor/guzzle/guzzle/src/Guzzle/Log/AbstractLogAdapter.php000064400000000440151327705700017715 0ustar00<?php

namespace Guzzle\Log;

/**
 * Adapter class that allows Guzzle to log data using various logging implementations
 */
abstract class AbstractLogAdapter implements LogAdapterInterface
{
    protected $log;

    public function getLogObject()
    {
        return $this->log;
    }
}
vendor/guzzle/guzzle/src/Guzzle/Log/MonologLogAdapter.php000064400000001352151327705700017567 0ustar00<?php

namespace Guzzle\Log;

use WPvividMonolog\Logger;

/**
 * @deprecated
 * @codeCoverageIgnore
 */
class MonologLogAdapter extends AbstractLogAdapter
{
    /**
     * syslog to Monolog mappings
     */
    private static $mapping = array(
        LOG_DEBUG   => Logger::DEBUG,
        LOG_INFO    => Logger::INFO,
        LOG_WARNING => Logger::WARNING,
        LOG_ERR     => Logger::ERROR,
        LOG_CRIT    => Logger::CRITICAL,
        LOG_ALERT   => Logger::ALERT
    );

    public function __construct(Logger $logObject)
    {
        $this->log = $logObject;
    }

    public function log($message, $priority = LOG_INFO, $extras = array())
    {
        $this->log->addRecord(self::$mapping[$priority], $message, $extras);
    }
}
vendor/guzzle/guzzle/src/Guzzle/Log/ArrayLogAdapter.php000064400000001147151327705700017235 0ustar00<?php

namespace Guzzle\Log;

/**
 * Stores all log messages in an array
 */
class ArrayLogAdapter implements LogAdapterInterface
{
    protected $logs = array();

    public function log($message, $priority = LOG_INFO, $extras = array())
    {
        $this->logs[] = array('message' => $message, 'priority' => $priority, 'extras' => $extras);
    }

    /**
     * Get logged entries
     *
     * @return array
     */
    public function getLogs()
    {
        return $this->logs;
    }

    /**
     * Clears logged entries
     */
    public function clearLogs()
    {
        $this->logs = array();
    }
}
vendor/guzzle/guzzle/src/Guzzle/Log/LogAdapterInterface.php000064400000000753151327705700020061 0ustar00<?php

namespace Guzzle\Log;

/**
 * Adapter class that allows Guzzle to log data to various logging implementations.
 */
interface LogAdapterInterface
{
    /**
     * Log a message at a priority
     *
     * @param string  $message  Message to log
     * @param integer $priority Priority of message (use the \LOG_* constants of 0 - 7)
     * @param array   $extras   Extra information to log in event
     */
    public function log($message, $priority = LOG_INFO, $extras = array());
}
vendor/guzzle/guzzle/src/Guzzle/Log/Zf1LogAdapter.php000064400000000755151327705700016623 0ustar00<?php

namespace Guzzle\Log;

use Guzzle\Common\Version;

/**
 * Adapts a Zend Framework 1 logger object
 * @deprecated
 * @codeCoverageIgnore
 */
class Zf1LogAdapter extends AbstractLogAdapter
{
    public function __construct(\Zend_Log $logObject)
    {
        $this->log = $logObject;
        Version::warn(__CLASS__ . ' is deprecated');
    }

    public function log($message, $priority = LOG_INFO, $extras = array())
    {
        $this->log->log($message, $priority, $extras);
    }
}
vendor/guzzle/guzzle/src/Guzzle/Log/PsrLogAdapter.php000064400000001554151327705700016725 0ustar00<?php

namespace Guzzle\Log;

use WPvividPsr\Log\LogLevel;
use WPvividPsr\Log\LoggerInterface;

/**
 * PSR-3 log adapter
 *
 * @link https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-3-logger-interface.md
 */
class PsrLogAdapter extends AbstractLogAdapter
{
    /**
     * syslog to PSR-3 mappings
     */
    private static $mapping = array(
        LOG_DEBUG   => LogLevel::DEBUG,
        LOG_INFO    => LogLevel::INFO,
        LOG_WARNING => LogLevel::WARNING,
        LOG_ERR     => LogLevel::ERROR,
        LOG_CRIT    => LogLevel::CRITICAL,
        LOG_ALERT   => LogLevel::ALERT
    );

    public function __construct(LoggerInterface $logObject)
    {
        $this->log = $logObject;
    }

    public function log($message, $priority = LOG_INFO, $extras = array())
    {
        $this->log->log(self::$mapping[$priority], $message, $extras);
    }
}
vendor/guzzle/guzzle/src/Guzzle/Log/ClosureLogAdapter.php000064400000001107151327705700017567 0ustar00<?php

namespace Guzzle\Log;

/**
 * Logs messages using Closures. Closures combined with filtering can trigger application events based on log messages.
 */
class ClosureLogAdapter extends AbstractLogAdapter
{
    public function __construct($logObject)
    {
        if (!is_callable($logObject)) {
            throw new \InvalidArgumentException('Object must be callable');
        }

        $this->log = $logObject;
    }

    public function log($message, $priority = LOG_INFO, $extras = array())
    {
        call_user_func($this->log, $message, $priority, $extras);
    }
}
vendor/guzzle/guzzle/src/Guzzle/Log/Zf2LogAdapter.php000064400000000611151327705700016613 0ustar00<?php

namespace Guzzle\Log;

use Zend\Log\Logger;

/**
 * Adapts a Zend Framework 2 logger object
 */
class Zf2LogAdapter extends AbstractLogAdapter
{
    public function __construct(Logger $logObject)
    {
        $this->log = $logObject;
    }

    public function log($message, $priority = LOG_INFO, $extras = array())
    {
        $this->log->log($priority, $message, $extras);
    }
}
vendor/guzzle/guzzle/src/Guzzle/Log/composer.json000064400000001240151327705700016217 0ustar00{
    "name": "guzzle/log",
    "description": "Guzzle log adapter component",
    "homepage": "http://guzzlephp.org/",
    "keywords": ["log", "adapter", "guzzle"],
    "license": "MIT",
    "authors": [
        {
            "name": "Michael Dowling",
            "email": "mtdowling@gmail.com",
            "homepage": "https://github.com/mtdowling"
        }
    ],
    "require": {
        "php": ">=5.3.2"
    },
    "autoload": {
        "psr-0": { "Guzzle\\Log": "" }
    },
    "suggest": {
        "guzzle/http": "self.version"
    },
    "target-dir": "Guzzle/Log",
    "extra": {
        "branch-alias": {
            "dev-master": "3.7-dev"
        }
    }
}
vendor/guzzle/guzzle/src/Guzzle/Cache/composer.json000064400000001247151327705700016510 0ustar00{
    "name": "guzzle/cache",
    "description": "Guzzle cache adapter component",
    "homepage": "http://guzzlephp.org/",
    "keywords": ["cache", "adapter", "zf", "doctrine", "guzzle"],
    "license": "MIT",
    "authors": [
        {
            "name": "Michael Dowling",
            "email": "mtdowling@gmail.com",
            "homepage": "https://github.com/mtdowling"
        }
    ],
    "require": {
        "php": ">=5.3.2",
        "guzzle/common": "self.version"
    },
    "autoload": {
        "psr-0": { "Guzzle\\Cache": "" }
    },
    "target-dir": "Guzzle/Cache",
    "extra": {
        "branch-alias": {
            "dev-master": "3.7-dev"
        }
    }
}
vendor/guzzle/guzzle/src/Guzzle/Cache/AbstractCacheAdapter.php000064400000000507151327705700020465 0ustar00<?php

namespace Guzzle\Cache;

/**
 * Abstract cache adapter
 */
abstract class AbstractCacheAdapter implements CacheAdapterInterface
{
    protected $cache;

    /**
     * Get the object owned by the adapter
     *
     * @return mixed
     */
    public function getCacheObject()
    {
        return $this->cache;
    }
}
vendor/guzzle/guzzle/src/Guzzle/Cache/ClosureCacheAdapter.php000064400000003536151327705700020343 0ustar00<?php

namespace Guzzle\Cache;

/**
 * Cache adapter that defers to closures for implementation
 */
class ClosureCacheAdapter implements CacheAdapterInterface
{
    /**
     * @var array Mapping of method names to callables
     */
    protected $callables;

    /**
     * The callables array is an array mapping the actions of the cache adapter to callables.
     * - contains: Callable that accepts an $id and $options argument
     * - delete:   Callable that accepts an $id and $options argument
     * - fetch:    Callable that accepts an $id and $options argument
     * - save:     Callable that accepts an $id, $data, $lifeTime, and $options argument
     *
     * @param array $callables array of action names to callable
     *
     * @throws \InvalidArgumentException if the callable is not callable
     */
    public function __construct(array $callables)
    {
        // Validate each key to ensure it exists and is callable
        foreach (array('contains', 'delete', 'fetch', 'save') as $key) {
            if (!array_key_exists($key, $callables) || !is_callable($callables[$key])) {
                throw new \InvalidArgumentException("callables must contain a callable {$key} key");
            }
        }

        $this->callables = $callables;
    }

    public function contains($id, array $options = null)
    {
        return call_user_func($this->callables['contains'], $id, $options);
    }

    public function delete($id, array $options = null)
    {
        return call_user_func($this->callables['delete'], $id, $options);
    }

    public function fetch($id, array $options = null)
    {
        return call_user_func($this->callables['fetch'], $id, $options);
    }

    public function save($id, $data, $lifeTime = false, array $options = null)
    {
        return call_user_func($this->callables['save'], $id, $data, $lifeTime, $options);
    }
}
vendor/guzzle/guzzle/src/Guzzle/Cache/CacheAdapterInterface.php000064400000003353151327705700020624 0ustar00<?php

namespace Guzzle\Cache;

/**
 * Interface for cache adapters.
 *
 * Cache adapters allow Guzzle to utilize various frameworks for caching HTTP responses.
 *
 * @link http://www.doctrine-project.org/ Inspired by Doctrine 2
 */
interface CacheAdapterInterface
{
    /**
     * Test if an entry exists in the cache.
     *
     * @param string $id      cache id The cache id of the entry to check for.
     * @param array  $options Array of cache adapter options
     *
     * @return bool Returns TRUE if a cache entry exists for the given cache id, FALSE otherwise.
     */
    public function contains($id, array $options = null);

    /**
     * Deletes a cache entry.
     *
     * @param string $id      cache id
     * @param array  $options Array of cache adapter options
     *
     * @return bool TRUE on success, FALSE on failure
     */
    public function delete($id, array $options = null);

    /**
     * Fetches an entry from the cache.
     *
     * @param string $id      cache id The id of the cache entry to fetch.
     * @param array  $options Array of cache adapter options
     *
     * @return string The cached data or FALSE, if no cache entry exists for the given id.
     */
    public function fetch($id, array $options = null);

    /**
     * Puts data into the cache.
     *
     * @param string   $id       The cache id
     * @param string   $data     The cache entry/data
     * @param int|bool $lifeTime The lifetime. If != false, sets a specific lifetime for this cache entry
     * @param array    $options  Array of cache adapter options
     *
     * @return bool TRUE if the entry was successfully stored in the cache, FALSE otherwise.
     */
    public function save($id, $data, $lifeTime = false, array $options = null);
}
vendor/guzzle/guzzle/src/Guzzle/Cache/Zf1CacheAdapter.php000064400000001772151327705700017367 0ustar00<?php

namespace Guzzle\Cache;

use Guzzle\Common\Version;

/**
 * Zend Framework 1 cache adapter
 *
 * @link http://framework.zend.com/manual/en/zend.cache.html
 * @deprecated
 * @codeCoverageIgnore
 */
class Zf1CacheAdapter extends AbstractCacheAdapter
{
    /**
     * @param \Zend_Cache_Backend $cache Cache object to wrap
     */
    public function __construct(\Zend_Cache_Backend $cache)
    {
        Version::warn(__CLASS__ . ' is deprecated. Upgrade to ZF2 or use PsrCacheAdapter');
        $this->cache = $cache;
    }

    public function contains($id, array $options = null)
    {
        return $this->cache->test($id);
    }

    public function delete($id, array $options = null)
    {
        return $this->cache->remove($id);
    }

    public function fetch($id, array $options = null)
    {
        return $this->cache->load($id);
    }

    public function save($id, $data, $lifeTime = false, array $options = null)
    {
        return $this->cache->save($data, $id, array(), $lifeTime);
    }
}
vendor/guzzle/guzzle/src/Guzzle/Cache/NullCacheAdapter.php000064400000001032151327705700017626 0ustar00<?php

namespace Guzzle\Cache;

/**
 * Null cache adapter
 */
class NullCacheAdapter extends AbstractCacheAdapter
{
    public function __construct() {}

    public function contains($id, array $options = null)
    {
        return false;
    }

    public function delete($id, array $options = null)
    {
        return true;
    }

    public function fetch($id, array $options = null)
    {
        return false;
    }

    public function save($id, $data, $lifeTime = false, array $options = null)
    {
        return true;
    }
}
vendor/guzzle/guzzle/src/Guzzle/Cache/CacheAdapterFactory.php000064400000010215151327705700020326 0ustar00<?php

namespace Guzzle\Cache;

use Doctrine\Common\Cache\Cache;
use Guzzle\Common\Version;
use Guzzle\Common\Exception\InvalidArgumentException;
use Guzzle\Common\Exception\RuntimeException;
use Guzzle\Common\FromConfigInterface;
use Zend\Cache\Storage\StorageInterface;

/**
 * Generates cache adapters from any number of known cache implementations
 */
class CacheAdapterFactory implements FromConfigInterface
{
    /**
     * Create a Guzzle cache adapter based on an array of options
     *
     * @param mixed $cache Cache value
     *
     * @return CacheAdapterInterface
     * @throws InvalidArgumentException
     */
    public static function fromCache($cache)
    {
        if (!is_object($cache)) {
            throw new InvalidArgumentException('Cache must be one of the known cache objects');
        }

        if ($cache instanceof CacheAdapterInterface) {
            return $cache;
        } elseif ($cache instanceof Cache) {
            return new DoctrineCacheAdapter($cache);
        } elseif ($cache instanceof StorageInterface) {
            return new Zf2CacheAdapter($cache);
        } else {
            throw new InvalidArgumentException('Unknown cache type: ' . get_class($cache));
        }
    }

    /**
     * Create a Guzzle cache adapter based on an array of options
     *
     * @param array $config Array of configuration options
     *
     * @return CacheAdapterInterface
     * @throws InvalidArgumentException
     * @deprecated This will be removed in a future version
     * @codeCoverageIgnore
     */
    public static function factory($config = array())
    {
        Version::warn(__METHOD__ . ' is deprecated');
        if (!is_array($config)) {
            throw new InvalidArgumentException('$config must be an array');
        }

        if (!isset($config['cache.adapter']) && !isset($config['cache.provider'])) {
            $config['cache.adapter'] = 'Guzzle\Cache\NullCacheAdapter';
            $config['cache.provider'] = null;
        } else {
            // Validate that the options are valid
            foreach (array('cache.adapter', 'cache.provider') as $required) {
                if (!isset($config[$required])) {
                    throw new InvalidArgumentException("{$required} is a required CacheAdapterFactory option");
                }
                if (is_string($config[$required])) {
                    // Convert dot notation to namespaces
                    $config[$required] = str_replace('.', '\\', $config[$required]);
                    if (!class_exists($config[$required])) {
                        throw new InvalidArgumentException("{$config[$required]} is not a valid class for {$required}");
                    }
                }
            }
            // Instantiate the cache provider
            if (is_string($config['cache.provider'])) {
                $args = isset($config['cache.provider.args']) ? $config['cache.provider.args'] : null;
                $config['cache.provider'] = self::createObject($config['cache.provider'], $args);
            }
        }

        // Instantiate the cache adapter using the provider and options
        if (is_string($config['cache.adapter'])) {
            $args = isset($config['cache.adapter.args']) ? $config['cache.adapter.args'] : array();
            array_unshift($args, $config['cache.provider']);
            $config['cache.adapter'] = self::createObject($config['cache.adapter'], $args);
        }

        return $config['cache.adapter'];
    }

    /**
     * Create a class using an array of constructor arguments
     *
     * @param string $className Class name
     * @param array  $args      Arguments for the class constructor
     *
     * @return mixed
     * @throws RuntimeException
     * @deprecated
     * @codeCoverageIgnore
     */
    private static function createObject($className, array $args = null)
    {
        try {
            if (!$args) {
                return new $className;
            } else {
                $c = new \ReflectionClass($className);
                return $c->newInstanceArgs($args);
            }
        } catch (\Exception $e) {
            throw new RuntimeException($e->getMessage(), $e->getCode(), $e);
        }
    }
}
vendor/guzzle/guzzle/src/Guzzle/Cache/DoctrineCacheAdapter.php000064400000001545151327705700020474 0ustar00<?php

namespace Guzzle\Cache;

use Doctrine\Common\Cache\Cache;

/**
 * Doctrine 2 cache adapter
 *
 * @link http://www.doctrine-project.org/
 */
class DoctrineCacheAdapter extends AbstractCacheAdapter
{
    /**
     * @param Cache $cache Doctrine cache object
     */
    public function __construct(Cache $cache)
    {
        $this->cache = $cache;
    }

    public function contains($id, array $options = null)
    {
        return $this->cache->contains($id);
    }

    public function delete($id, array $options = null)
    {
        return $this->cache->delete($id);
    }

    public function fetch($id, array $options = null)
    {
        return $this->cache->fetch($id);
    }

    public function save($id, $data, $lifeTime = false, array $options = null)
    {
        return $this->cache->save($id, $data, $lifeTime !== false ? $lifeTime : 0);
    }
}
vendor/guzzle/guzzle/src/Guzzle/Cache/Zf2CacheAdapter.php000064400000001627151327705700017367 0ustar00<?php

namespace Guzzle\Cache;

use Zend\Cache\Storage\StorageInterface;

/**
 * Zend Framework 2 cache adapter
 *
 * @link http://packages.zendframework.com/docs/latest/manual/en/zend.cache.html
 */
class Zf2CacheAdapter extends AbstractCacheAdapter
{
    /**
     * @param StorageInterface $cache Zend Framework 2 cache adapter
     */
    public function __construct(StorageInterface $cache)
    {
        $this->cache = $cache;
    }

    public function contains($id, array $options = null)
    {
        return $this->cache->hasItem($id);
    }

    public function delete($id, array $options = null)
    {
        return $this->cache->removeItem($id);
    }

    public function fetch($id, array $options = null)
    {
        return $this->cache->getItem($id);
    }

    public function save($id, $data, $lifeTime = false, array $options = null)
    {
        return $this->cache->setItem($id, $data);
    }
}
vendor/guzzle/guzzle/src/Guzzle/Common/FromConfigInterface.php000064400000000747151327705700020602 0ustar00<?php

namespace Guzzle\Common;

/**
 * Interfaces that adds a factory method which is used to instantiate a class from an array of configuration options.
 */
interface FromConfigInterface
{
    /**
     * Static factory method used to turn an array or collection of configuration data into an instantiated object.
     *
     * @param array|Collection $config Configuration data
     *
     * @return FromConfigInterface
     */
    public static function factory($config = array());
}
vendor/guzzle/guzzle/src/Guzzle/Common/HasDispatcherInterface.php000064400000002516151327705700021267 0ustar00<?php

namespace Guzzle\Common;

use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;

/**
 * Holds an event dispatcher
 */
interface HasDispatcherInterface
{
    /**
     * Get a list of all of the events emitted from the class
     *
     * @return array
     */
    public static function getAllEvents();

    /**
     * Set the EventDispatcher of the request
     *
     * @param EventDispatcherInterface $eventDispatcher
     *
     * @return self
     */
    public function setEventDispatcher(EventDispatcherInterface $eventDispatcher);

    /**
     * Get the EventDispatcher of the request
     *
     * @return EventDispatcherInterface
     */
    public function getEventDispatcher();

    /**
     * Helper to dispatch Guzzle events and set the event name on the event
     *
     * @param string $eventName Name of the event to dispatch
     * @param array  $context   Context of the event
     *
     * @return Event Returns the created event object
     */
    public function dispatch($eventName, array $context = array());

    /**
     * Add an event subscriber to the dispatcher
     *
     * @param EventSubscriberInterface $subscriber Event subscriber
     *
     * @return self
     */
    public function addSubscriber(EventSubscriberInterface $subscriber);
}
vendor/guzzle/guzzle/src/Guzzle/Common/Version.php000064400000001201151327705700016337 0ustar00<?php

namespace Guzzle\Common;

/**
 * Guzzle version information
 */
class Version
{
    const VERSION = '3.9.3';

    /**
     * @var bool Set this value to true to enable warnings for deprecated functionality use. This should be on in your
     *           unit tests, but probably not in production.
     */
    public static $emitWarnings = false;

    /**
     * Emit a deprecation warning
     *
     * @param string $message Warning message
     */
    public static function warn($message)
    {
        if (self::$emitWarnings) {
            trigger_error('Deprecation warning: ' . $message, E_USER_DEPRECATED);
        }
    }
}
vendor/guzzle/guzzle/src/Guzzle/Common/composer.json000064400000000767151327705700016743 0ustar00{
    "name": "guzzle/common",
    "homepage": "http://guzzlephp.org/",
    "description": "Common libraries used by Guzzle",
    "keywords": ["common", "event", "exception", "collection"],
    "license": "MIT",
    "require": {
        "php": ">=5.3.2",
        "symfony/event-dispatcher": ">=2.1"
    },
    "autoload": {
        "psr-0": { "Guzzle\\Common": "" }
    },
    "target-dir": "Guzzle/Common",
    "extra": {
        "branch-alias": {
            "dev-master": "3.7-dev"
        }
    }
}
vendor/guzzle/guzzle/src/Guzzle/Common/Exception/BadMethodCallException.php000064400000000206151327705700023156 0ustar00<?php

namespace Guzzle\Common\Exception;

class BadMethodCallException extends \BadMethodCallException implements GuzzleException {}
vendor/guzzle/guzzle/src/Guzzle/Common/Exception/InvalidArgumentException.php000064400000000212151327705700023621 0ustar00<?php

namespace Guzzle\Common\Exception;

class InvalidArgumentException extends \InvalidArgumentException implements GuzzleException {}
vendor/guzzle/guzzle/src/Guzzle/Common/Exception/ExceptionCollection.php000064400000005420151327705700022631 0ustar00<?php

namespace Guzzle\Common\Exception;

/**
 * Collection of exceptions
 */
class ExceptionCollection extends \Exception implements GuzzleException, \IteratorAggregate, \Countable
{
    /** @var array Array of Exceptions */
    protected $exceptions = array();

    /** @var string Succinct exception message not including sub-exceptions */
    private $shortMessage;

    public function __construct($message = '', $code = 0, \Exception $previous = null)
    {
        parent::__construct($message, $code, $previous);
        $this->shortMessage = $message;
    }

    /**
     * Set all of the exceptions
     *
     * @param array $exceptions Array of exceptions
     *
     * @return self
     */
    public function setExceptions(array $exceptions)
    {
        $this->exceptions = array();
        foreach ($exceptions as $exception) {
            $this->add($exception);
        }

        return $this;
    }

    /**
     * Add exceptions to the collection
     *
     * @param ExceptionCollection|\Exception $e Exception to add
     *
     * @return ExceptionCollection;
     */
    public function add($e)
    {
        $this->exceptions[] = $e;
        if ($this->message) {
            $this->message .= "\n";
        }

        $this->message .= $this->getExceptionMessage($e, 0);

        return $this;
    }

    /**
     * Get the total number of request exceptions
     *
     * @return int
     */
    public function count()
    {
        return count($this->exceptions);
    }

    /**
     * Allows array-like iteration over the request exceptions
     *
     * @return \ArrayIterator
     */
    public function getIterator()
    {
        return new \ArrayIterator($this->exceptions);
    }

    /**
     * Get the first exception in the collection
     *
     * @return \Exception
     */
    public function getFirst()
    {
        return $this->exceptions ? $this->exceptions[0] : null;
    }

    private function getExceptionMessage(\Exception $e, $depth = 0)
    {
        static $sp = '    ';
        $prefix = $depth ? str_repeat($sp, $depth) : '';
        $message = "{$prefix}(" . get_class($e) . ') ' . $e->getFile() . ' line ' . $e->getLine() . "\n";

        if ($e instanceof self) {
            if ($e->shortMessage) {
                $message .= "\n{$prefix}{$sp}" . str_replace("\n", "\n{$prefix}{$sp}", $e->shortMessage) . "\n";
            }
            foreach ($e as $ee) {
                $message .= "\n" . $this->getExceptionMessage($ee, $depth + 1);
            }
        }  else {
            $message .= "\n{$prefix}{$sp}" . str_replace("\n", "\n{$prefix}{$sp}", $e->getMessage()) . "\n";
            $message .= "\n{$prefix}{$sp}" . str_replace("\n", "\n{$prefix}{$sp}", $e->getTraceAsString()) . "\n";
        }

        return str_replace(getcwd(), '.', $message);
    }
}
vendor/guzzle/guzzle/src/Guzzle/Common/Exception/UnexpectedValueException.php000064400000000212151327705700023631 0ustar00<?php

namespace Guzzle\Common\Exception;

class UnexpectedValueException extends \UnexpectedValueException implements GuzzleException {}
vendor/guzzle/guzzle/src/Guzzle/Common/Exception/RuntimeException.php000064400000000172151327705700022160 0ustar00<?php

namespace Guzzle\Common\Exception;

class RuntimeException extends \RuntimeException implements GuzzleException {}
vendor/guzzle/guzzle/src/Guzzle/Common/Exception/GuzzleException.php000064400000000144151327705700022014 0ustar00<?php

namespace Guzzle\Common\Exception;

/**
 * Guzzle exception
 */
interface GuzzleException {}
vendor/guzzle/guzzle/src/Guzzle/Common/Collection.php000064400000026416151327705700017024 0ustar00<?php

namespace Guzzle\Common;

use Guzzle\Common\Exception\InvalidArgumentException;
use Guzzle\Common\Exception\RuntimeException;

/**
 * Key value pair collection object
 */
class Collection implements \ArrayAccess, \IteratorAggregate, \Countable, ToArrayInterface
{
    /** @var array Data associated with the object. */
    protected $data;

    /**
     * @param array $data Associative array of data to set
     */
    public function __construct(array $data = array())
    {
        $this->data = $data;
    }

    /**
     * Create a new collection from an array, validate the keys, and add default values where missing
     *
     * @param array $config   Configuration values to apply.
     * @param array $defaults Default parameters
     * @param array $required Required parameter names
     *
     * @return self
     * @throws InvalidArgumentException if a parameter is missing
     */
    public static function fromConfig(array $config = array(), array $defaults = array(), array $required = array())
    {
        $data = $config + $defaults;

        if ($missing = array_diff($required, array_keys($data))) {
            throw new InvalidArgumentException('Config is missing the following keys: ' . implode(', ', $missing));
        }

        return new self($data);
    }

    public function count()
    {
        return count($this->data);
    }

    public function getIterator()
    {
        return new \ArrayIterator($this->data);
    }

    public function toArray()
    {
        return $this->data;
    }

    /**
     * Removes all key value pairs
     *
     * @return Collection
     */
    public function clear()
    {
        $this->data = array();

        return $this;
    }

    /**
     * Get all or a subset of matching key value pairs
     *
     * @param array $keys Pass an array of keys to retrieve only a subset of key value pairs
     *
     * @return array Returns an array of all matching key value pairs
     */
    public function getAll(array $keys = null)
    {
        return $keys ? array_intersect_key($this->data, array_flip($keys)) : $this->data;
    }

    /**
     * Get a specific key value.
     *
     * @param string $key Key to retrieve.
     *
     * @return mixed|null Value of the key or NULL
     */
    public function get($key)
    {
        return isset($this->data[$key]) ? $this->data[$key] : null;
    }

    /**
     * Set a key value pair
     *
     * @param string $key   Key to set
     * @param mixed  $value Value to set
     *
     * @return Collection Returns a reference to the object
     */
    public function set($key, $value)
    {
        $this->data[$key] = $value;

        return $this;
    }

    /**
     * Add a value to a key.  If a key of the same name has already been added, the key value will be converted into an
     * array and the new value will be pushed to the end of the array.
     *
     * @param string $key   Key to add
     * @param mixed  $value Value to add to the key
     *
     * @return Collection Returns a reference to the object.
     */
    public function add($key, $value)
    {
        if (!array_key_exists($key, $this->data)) {
            $this->data[$key] = $value;
        } elseif (is_array($this->data[$key])) {
            $this->data[$key][] = $value;
        } else {
            $this->data[$key] = array($this->data[$key], $value);
        }

        return $this;
    }

    /**
     * Remove a specific key value pair
     *
     * @param string $key A key to remove
     *
     * @return Collection
     */
    public function remove($key)
    {
        unset($this->data[$key]);

        return $this;
    }

    /**
     * Get all keys in the collection
     *
     * @return array
     */
    public function getKeys()
    {
        return array_keys($this->data);
    }

    /**
     * Returns whether or not the specified key is present.
     *
     * @param string $key The key for which to check the existence.
     *
     * @return bool
     */
    public function hasKey($key)
    {
        return array_key_exists($key, $this->data);
    }

    /**
     * Case insensitive search the keys in the collection
     *
     * @param string $key Key to search for
     *
     * @return bool|string Returns false if not found, otherwise returns the key
     */
    public function keySearch($key)
    {
        foreach (array_keys($this->data) as $k) {
            if (!strcasecmp($k, $key)) {
                return $k;
            }
        }

        return false;
    }

    /**
     * Checks if any keys contains a certain value
     *
     * @param string $value Value to search for
     *
     * @return mixed Returns the key if the value was found FALSE if the value was not found.
     */
    public function hasValue($value)
    {
        return array_search($value, $this->data);
    }

    /**
     * Replace the data of the object with the value of an array
     *
     * @param array $data Associative array of data
     *
     * @return Collection Returns a reference to the object
     */
    public function replace(array $data)
    {
        $this->data = $data;

        return $this;
    }

    /**
     * Add and merge in a Collection or array of key value pair data.
     *
     * @param Collection|array $data Associative array of key value pair data
     *
     * @return Collection Returns a reference to the object.
     */
    public function merge($data)
    {
        foreach ($data as $key => $value) {
            $this->add($key, $value);
        }

        return $this;
    }

    /**
     * Over write key value pairs in this collection with all of the data from an array or collection.
     *
     * @param array|\Traversable $data Values to override over this config
     *
     * @return self
     */
    public function overwriteWith($data)
    {
        if (is_array($data)) {
            $this->data = $data + $this->data;
        } elseif ($data instanceof Collection) {
            $this->data = $data->toArray() + $this->data;
        } else {
            foreach ($data as $key => $value) {
                $this->data[$key] = $value;
            }
        }

        return $this;
    }

    /**
     * Returns a Collection containing all the elements of the collection after applying the callback function to each
     * one. The Closure should accept three parameters: (string) $key, (string) $value, (array) $context and return a
     * modified value
     *
     * @param \Closure $closure Closure to apply
     * @param array    $context Context to pass to the closure
     * @param bool     $static  Set to TRUE to use the same class as the return rather than returning a Collection
     *
     * @return Collection
     */
    public function map(\Closure $closure, array $context = array(), $static = true)
    {
        $collection = $static ? new static() : new self();
        foreach ($this as $key => $value) {
            $collection->add($key, $closure($key, $value, $context));
        }

        return $collection;
    }

    /**
     * Iterates over each key value pair in the collection passing them to the Closure. If the  Closure function returns
     * true, the current value from input is returned into the result Collection.  The Closure must accept three
     * parameters: (string) $key, (string) $value and return Boolean TRUE or FALSE for each value.
     *
     * @param \Closure $closure Closure evaluation function
     * @param bool     $static  Set to TRUE to use the same class as the return rather than returning a Collection
     *
     * @return Collection
     */
    public function filter(\Closure $closure, $static = true)
    {
        $collection = ($static) ? new static() : new self();
        foreach ($this->data as $key => $value) {
            if ($closure($key, $value)) {
                $collection->add($key, $value);
            }
        }

        return $collection;
    }

    public function offsetExists($offset)
    {
        return isset($this->data[$offset]);
    }

    public function offsetGet($offset)
    {
        return isset($this->data[$offset]) ? $this->data[$offset] : null;
    }

    public function offsetSet($offset, $value)
    {
        $this->data[$offset] = $value;
    }

    public function offsetUnset($offset)
    {
        unset($this->data[$offset]);
    }

    /**
     * Set a value into a nested array key. Keys will be created as needed to set the value.
     *
     * @param string $path  Path to set
     * @param mixed  $value Value to set at the key
     *
     * @return self
     * @throws RuntimeException when trying to setPath using a nested path that travels through a scalar value
     */
    public function setPath($path, $value)
    {
        $current =& $this->data;
        $queue = explode('/', $path);
        while (null !== ($key = array_shift($queue))) {
            if (!is_array($current)) {
                throw new RuntimeException("Trying to setPath {$path}, but {$key} is set and is not an array");
            } elseif (!$queue) {
                $current[$key] = $value;
            } elseif (isset($current[$key])) {
                $current =& $current[$key];
            } else {
                $current[$key] = array();
                $current =& $current[$key];
            }
        }

        return $this;
    }

    /**
     * Gets a value from the collection using an array path (e.g. foo/baz/bar would retrieve bar from two nested arrays)
     * Allows for wildcard searches which recursively combine matches up to the level at which the wildcard occurs. This
     * can be useful for accepting any key of a sub-array and combining matching keys from each diverging path.
     *
     * @param string $path      Path to traverse and retrieve a value from
     * @param string $separator Character used to add depth to the search
     * @param mixed  $data      Optional data to descend into (used when wildcards are encountered)
     *
     * @return mixed|null
     */
    public function getPath($path, $separator = '/', $data = null)
    {
        if ($data === null) {
            $data =& $this->data;
        }

        $path = is_array($path) ? $path : explode($separator, $path);
        while (null !== ($part = array_shift($path))) {
            if (!is_array($data)) {
                return null;
            } elseif (isset($data[$part])) {
                $data =& $data[$part];
            } elseif ($part != '*') {
                return null;
            } else {
                // Perform a wildcard search by diverging and merging paths
                $result = array();
                foreach ($data as $value) {
                    if (!$path) {
                        $result = array_merge_recursive($result, (array) $value);
                    } elseif (null !== ($test = $this->getPath($path, $separator, $value))) {
                        $result = array_merge_recursive($result, (array) $test);
                    }
                }
                return $result;
            }
        }

        return $data;
    }

    /**
     * Inject configuration settings into an input string
     *
     * @param string $input Input to inject
     *
     * @return string
     * @deprecated
     */
    public function inject($input)
    {
        Version::warn(__METHOD__ . ' is deprecated');
        $replace = array();
        foreach ($this->data as $key => $val) {
            $replace['{' . $key . '}'] = $val;
        }

        return strtr($input, $replace);
    }
}
vendor/guzzle/guzzle/src/Guzzle/Common/AbstractHasDispatcher.php000064400000002253151327705700021130 0ustar00<?php

namespace Guzzle\Common;

use Symfony\Component\EventDispatcher\EventDispatcher;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;

/**
 * Class that holds an event dispatcher
 */
class AbstractHasDispatcher implements HasDispatcherInterface
{
    /** @var EventDispatcherInterface */
    protected $eventDispatcher;

    public static function getAllEvents()
    {
        return array();
    }

    public function setEventDispatcher(EventDispatcherInterface $eventDispatcher)
    {
        $this->eventDispatcher = $eventDispatcher;

        return $this;
    }

    public function getEventDispatcher()
    {
        if (!$this->eventDispatcher) {
            $this->eventDispatcher = new EventDispatcher();
        }

        return $this->eventDispatcher;
    }

    public function dispatch($eventName, array $context = array())
    {
        return $this->getEventDispatcher()->dispatch($eventName, new Event($context));
    }

    public function addSubscriber(EventSubscriberInterface $subscriber)
    {
        $this->getEventDispatcher()->addSubscriber($subscriber);

        return $this;
    }
}
vendor/guzzle/guzzle/src/Guzzle/Common/Event.php000064400000002050151327705700015776 0ustar00<?php

namespace Guzzle\Common;

use Symfony\Component\EventDispatcher\Event as SymfonyEvent;

/**
 * Default event for Guzzle notifications
 */
class Event extends SymfonyEvent implements ToArrayInterface, \ArrayAccess, \IteratorAggregate
{
    /** @var array */
    private $context;

    /**
     * @param array $context Contextual information
     */
    public function __construct(array $context = array())
    {
        $this->context = $context;
    }

    public function getIterator()
    {
        return new \ArrayIterator($this->context);
    }

    public function offsetGet($offset)
    {
        return isset($this->context[$offset]) ? $this->context[$offset] : null;
    }

    public function offsetSet($offset, $value)
    {
        $this->context[$offset] = $value;
    }

    public function offsetExists($offset)
    {
        return isset($this->context[$offset]);
    }

    public function offsetUnset($offset)
    {
        unset($this->context[$offset]);
    }

    public function toArray()
    {
        return $this->context;
    }
}
vendor/guzzle/guzzle/src/Guzzle/Common/ToArrayInterface.php000064400000000365151327705700020126 0ustar00<?php

namespace Guzzle\Common;

/**
 * An object that can be represented as an array
 */
interface ToArrayInterface
{
    /**
     * Get the array representation of an object
     *
     * @return array
     */
    public function toArray();
}
vendor/guzzle/guzzle/src/Guzzle/Parser/Cookie/CookieParserInterface.php000064400000002714151327705700022350 0ustar00<?php

namespace Guzzle\Parser\Cookie;

/**
 * Cookie parser interface
 */
interface CookieParserInterface
{
    /**
     * Parse a cookie string as set in a Set-Cookie HTTP header and return an associative array of data.
     *
     * @param string $cookie Cookie header value to parse
     * @param string $host   Host of an associated request
     * @param string $path   Path of an associated request
     * @param bool   $decode Set to TRUE to urldecode cookie values
     *
     * @return array|bool Returns FALSE on failure or returns an array of arrays, with each of the sub arrays including:
     *     - domain  (string) - Domain of the cookie
     *     - path    (string) - Path of the cookie
     *     - cookies (array)  - Associative array of cookie names and values
     *     - max_age (int)    - Lifetime of the cookie in seconds
     *     - version (int)    - Version of the cookie specification. RFC 2965 is 1
     *     - secure  (bool)   - Whether or not this is a secure cookie
     *     - discard (bool)   - Whether or not this is a discardable cookie
     *     - custom (string)  - Custom cookie data array
     *     - comment (string) - How the cookie is intended to be used
     *     - comment_url (str)- URL that contains info on how it will be used
     *     - port (array|str) - Array of ports or null
     *     - http_only (bool) - HTTP only cookie
     */
    public function parseCookie($cookie, $host = null, $path = null, $decode = false);
}
vendor/guzzle/guzzle/src/Guzzle/Parser/Cookie/CookieParser.php000064400000011172151327705700020525 0ustar00<?php

namespace Guzzle\Parser\Cookie;

/**
 * Default Guzzle implementation of a Cookie parser
 */
class CookieParser implements CookieParserInterface
{
    /** @var array Cookie part names to snake_case array values */
    protected static $cookieParts = array(
        'domain'      => 'Domain',
        'path'        => 'Path',
        'max_age'     => 'Max-Age',
        'expires'     => 'Expires',
        'version'     => 'Version',
        'secure'      => 'Secure',
        'port'        => 'Port',
        'discard'     => 'Discard',
        'comment'     => 'Comment',
        'comment_url' => 'Comment-Url',
        'http_only'   => 'HttpOnly'
    );

    public function parseCookie($cookie, $host = null, $path = null, $decode = false)
    {
        // Explode the cookie string using a series of semicolons
        $pieces = array_filter(array_map('trim', explode(';', $cookie)));

        // The name of the cookie (first kvp) must include an equal sign.
        if (empty($pieces) || !strpos($pieces[0], '=')) {
            return false;
        }

        // Create the default return array
        $data = array_merge(array_fill_keys(array_keys(self::$cookieParts), null), array(
            'cookies'   => array(),
            'data'      => array(),
            'path'      => null,
            'http_only' => false,
            'discard'   => false,
            'domain'    => $host
        ));
        $foundNonCookies = 0;

        // Add the cookie pieces into the parsed data array
        foreach ($pieces as $part) {

            $cookieParts = explode('=', $part, 2);
            $key = trim($cookieParts[0]);

            if (count($cookieParts) == 1) {
                // Can be a single value (e.g. secure, httpOnly)
                $value = true;
            } else {
                // Be sure to strip wrapping quotes
                $value = trim($cookieParts[1], " \n\r\t\0\x0B\"");
                if ($decode) {
                    $value = urldecode($value);
                }
            }

            // Only check for non-cookies when cookies have been found
            if (!empty($data['cookies'])) {
                foreach (self::$cookieParts as $mapValue => $search) {
                    if (!strcasecmp($search, $key)) {
                        $data[$mapValue] = $mapValue == 'port' ? array_map('trim', explode(',', $value)) : $value;
                        $foundNonCookies++;
                        continue 2;
                    }
                }
            }

            // If cookies have not yet been retrieved, or this value was not found in the pieces array, treat it as a
            // cookie. IF non-cookies have been parsed, then this isn't a cookie, it's cookie data. Cookies then data.
            $data[$foundNonCookies ? 'data' : 'cookies'][$key] = $value;
        }

        // Calculate the expires date
        if (!$data['expires'] && $data['max_age']) {
            $data['expires'] = time() + (int) $data['max_age'];
        }

        // Check path attribute according RFC6265 http://tools.ietf.org/search/rfc6265#section-5.2.4
        // "If the attribute-value is empty or if the first character of the
        // attribute-value is not %x2F ("/"):
        //   Let cookie-path be the default-path.
        // Otherwise:
        //   Let cookie-path be the attribute-value."
        if (!$data['path'] || substr($data['path'], 0, 1) !== '/') {
            $data['path'] = $this->getDefaultPath($path);
        }

        return $data;
    }

    /**
     * Get default cookie path according to RFC 6265
     * http://tools.ietf.org/search/rfc6265#section-5.1.4 Paths and Path-Match
     *
     * @param string $path Request uri-path
     *
     * @return string
     */
    protected function getDefaultPath($path) {
        // "The user agent MUST use an algorithm equivalent to the following algorithm
        // to compute the default-path of a cookie:"

        // "2. If the uri-path is empty or if the first character of the uri-path is not
        // a %x2F ("/") character, output %x2F ("/") and skip the remaining steps.
        if (empty($path) || substr($path, 0, 1) !== '/') {
            return '/';
        }

        // "3. If the uri-path contains no more than one %x2F ("/") character, output
        // %x2F ("/") and skip the remaining step."
        if ($path === "/") {
            return $path;
        }

        $rightSlashPos = strrpos($path, '/');
        if ($rightSlashPos === 0) {
            return "/";
        }

        // "4. Output the characters of the uri-path from the first character up to,
        // but not including, the right-most %x2F ("/")."
        return substr($path, 0, $rightSlashPos);

    }
}
vendor/guzzle/guzzle/src/Guzzle/Parser/UriTemplate/UriTemplate.php000064400000020031151327705700021406 0ustar00<?php

namespace Guzzle\Parser\UriTemplate;

/**
 * Expands URI templates using an array of variables
 *
 * @link http://tools.ietf.org/html/draft-gregorio-uritemplate-08
 */
class UriTemplate implements UriTemplateInterface
{
    const DEFAULT_PATTERN = '/\{([^\}]+)\}/';

    /** @var string URI template */
    private $template;

    /** @var array Variables to use in the template expansion */
    private $variables;

    /** @var string Regex used to parse expressions */
    private $regex = self::DEFAULT_PATTERN;

    /** @var array Hash for quick operator lookups */
    private static $operatorHash = array(
        '+' => true, '#' => true, '.' => true, '/' => true, ';' => true, '?' => true, '&' => true
    );

    /** @var array Delimiters */
    private static $delims = array(
        ':', '/', '?', '#', '[', ']', '@', '!', '$', '&', '\'', '(', ')', '*', '+', ',', ';', '='
    );

    /** @var array Percent encoded delimiters */
    private static $delimsPct = array(
        '%3A', '%2F', '%3F', '%23', '%5B', '%5D', '%40', '%21', '%24', '%26', '%27', '%28', '%29', '%2A', '%2B', '%2C',
        '%3B', '%3D'
    );

    public function expand($template, array $variables)
    {
        if ($this->regex == self::DEFAULT_PATTERN && false === strpos($template, '{')) {
            return $template;
        }

        $this->template = $template;
        $this->variables = $variables;

        return preg_replace_callback($this->regex, array($this, 'expandMatch'), $this->template);
    }

    /**
     * Set the regex patten used to expand URI templates
     *
     * @param string $regexPattern
     */
    public function setRegex($regexPattern)
    {
        $this->regex = $regexPattern;
    }

    /**
     * Parse an expression into parts
     *
     * @param string $expression Expression to parse
     *
     * @return array Returns an associative array of parts
     */
    private function parseExpression($expression)
    {
        // Check for URI operators
        $operator = '';

        if (isset(self::$operatorHash[$expression[0]])) {
            $operator = $expression[0];
            $expression = substr($expression, 1);
        }

        $values = explode(',', $expression);
        foreach ($values as &$value) {
            $value = trim($value);
            $varspec = array();
            $substrPos = strpos($value, ':');
            if ($substrPos) {
                $varspec['value'] = substr($value, 0, $substrPos);
                $varspec['modifier'] = ':';
                $varspec['position'] = (int) substr($value, $substrPos + 1);
            } elseif (substr($value, -1) == '*') {
                $varspec['modifier'] = '*';
                $varspec['value'] = substr($value, 0, -1);
            } else {
                $varspec['value'] = (string) $value;
                $varspec['modifier'] = '';
            }
            $value = $varspec;
        }

        return array(
            'operator' => $operator,
            'values'   => $values
        );
    }

    /**
     * Process an expansion
     *
     * @param array $matches Matches met in the preg_replace_callback
     *
     * @return string Returns the replacement string
     */
    private function expandMatch(array $matches)
    {
        static $rfc1738to3986 = array(
            '+'   => '%20',
            '%7e' => '~'
        );

        $parsed = self::parseExpression($matches[1]);
        $replacements = array();

        $prefix = $parsed['operator'];
        $joiner = $parsed['operator'];
        $useQueryString = false;
        if ($parsed['operator'] == '?') {
            $joiner = '&';
            $useQueryString = true;
        } elseif ($parsed['operator'] == '&') {
            $useQueryString = true;
        } elseif ($parsed['operator'] == '#') {
            $joiner = ',';
        } elseif ($parsed['operator'] == ';') {
            $useQueryString = true;
        } elseif ($parsed['operator'] == '' || $parsed['operator'] == '+') {
            $joiner = ',';
            $prefix = '';
        }

        foreach ($parsed['values'] as $value) {

            if (!array_key_exists($value['value'], $this->variables) || $this->variables[$value['value']] === null) {
                continue;
            }

            $variable = $this->variables[$value['value']];
            $actuallyUseQueryString = $useQueryString;
            $expanded = '';

            if (is_array($variable)) {

                $isAssoc = $this->isAssoc($variable);
                $kvp = array();
                foreach ($variable as $key => $var) {

                    if ($isAssoc) {
                        $key = rawurlencode($key);
                        $isNestedArray = is_array($var);
                    } else {
                        $isNestedArray = false;
                    }

                    if (!$isNestedArray) {
                        $var = rawurlencode($var);
                        if ($parsed['operator'] == '+' || $parsed['operator'] == '#') {
                            $var = $this->decodeReserved($var);
                        }
                    }

                    if ($value['modifier'] == '*') {
                        if ($isAssoc) {
                            if ($isNestedArray) {
                                // Nested arrays must allow for deeply nested structures
                                $var = strtr(http_build_query(array($key => $var)), $rfc1738to3986);
                            } else {
                                $var = $key . '=' . $var;
                            }
                        } elseif ($key > 0 && $actuallyUseQueryString) {
                            $var = $value['value'] . '=' . $var;
                        }
                    }

                    $kvp[$key] = $var;
                }

                if (empty($variable)) {
                    $actuallyUseQueryString = false;
                } elseif ($value['modifier'] == '*') {
                    $expanded = implode($joiner, $kvp);
                    if ($isAssoc) {
                        // Don't prepend the value name when using the explode modifier with an associative array
                        $actuallyUseQueryString = false;
                    }
                } else {
                    if ($isAssoc) {
                        // When an associative array is encountered and the explode modifier is not set, then the
                        // result must be a comma separated list of keys followed by their respective values.
                        foreach ($kvp as $k => &$v) {
                            $v = $k . ',' . $v;
                        }
                    }
                    $expanded = implode(',', $kvp);
                }

            } else {
                if ($value['modifier'] == ':') {
                    $variable = substr($variable, 0, $value['position']);
                }
                $expanded = rawurlencode($variable);
                if ($parsed['operator'] == '+' || $parsed['operator'] == '#') {
                    $expanded = $this->decodeReserved($expanded);
                }
            }

            if ($actuallyUseQueryString) {
                if (!$expanded && $joiner != '&') {
                    $expanded = $value['value'];
                } else {
                    $expanded = $value['value'] . '=' . $expanded;
                }
            }

            $replacements[] = $expanded;
        }

        $ret = implode($joiner, $replacements);
        if ($ret && $prefix) {
            return $prefix . $ret;
        }

        return $ret;
    }

    /**
     * Determines if an array is associative
     *
     * @param array $array Array to check
     *
     * @return bool
     */
    private function isAssoc(array $array)
    {
        return (bool) count(array_filter(array_keys($array), 'is_string'));
    }

    /**
     * Removes percent encoding on reserved characters (used with + and # modifiers)
     *
     * @param string $string String to fix
     *
     * @return string
     */
    private function decodeReserved($string)
    {
        return str_replace(self::$delimsPct, self::$delims, $string);
    }
}
vendor/guzzle/guzzle/src/Guzzle/Parser/UriTemplate/PeclUriTemplate.php000064400000001264151327705700022221 0ustar00<?php

namespace Guzzle\Parser\UriTemplate;

use Guzzle\Common\Exception\RuntimeException;

/**
 * Expands URI templates using the uri_template pecl extension (pecl install uri_template-beta)
 *
 * @link http://pecl.php.net/package/uri_template
 * @link https://github.com/ioseb/uri-template
 */
class PeclUriTemplate implements UriTemplateInterface
{
    public function __construct()
    {
        if (!extension_loaded('uri_template')) {
            throw new RuntimeException('uri_template PECL extension must be installed to use PeclUriTemplate');
        }
    }

    public function expand($template, array $variables)
    {
        return uri_template($template, $variables);
    }
}
vendor/guzzle/guzzle/src/Guzzle/Parser/UriTemplate/UriTemplateInterface.php000064400000000776151327705700023245 0ustar00<?php

namespace Guzzle\Parser\UriTemplate;

/**
 * Expands URI templates using an array of variables
 *
 * @link http://tools.ietf.org/html/rfc6570
 */
interface UriTemplateInterface
{
    /**
     * Expand the URI template using the supplied variables
     *
     * @param string $template  URI Template to expand
     * @param array  $variables Variables to use with the expansion
     *
     * @return string Returns the expanded template
     */
    public function expand($template, array $variables);
}
vendor/guzzle/guzzle/src/Guzzle/Parser/ParserRegistry.php000064400000003635151327705700017720 0ustar00<?php

namespace Guzzle\Parser;

/**
 * Registry of parsers used by the application
 */
class ParserRegistry
{
    /** @var ParserRegistry Singleton instance */
    protected static $instance;

    /** @var array Array of parser instances */
    protected $instances = array();

    /** @var array Mapping of parser name to default class */
    protected $mapping = array(
        'message'      => 'Guzzle\\Parser\\Message\\MessageParser',
        'cookie'       => 'Guzzle\\Parser\\Cookie\\CookieParser',
        'url'          => 'Guzzle\\Parser\\Url\\UrlParser',
        'uri_template' => 'Guzzle\\Parser\\UriTemplate\\UriTemplate',
    );

    /**
     * @return self
     * @codeCoverageIgnore
     */
    public static function getInstance()
    {
        if (!self::$instance) {
            self::$instance = new static;
        }

        return self::$instance;
    }

    public function __construct()
    {
        // Use the PECL URI template parser if available
        if (extension_loaded('uri_template')) {
            $this->mapping['uri_template'] = 'Guzzle\\Parser\\UriTemplate\\PeclUriTemplate';
        }
    }

    /**
     * Get a parser by name from an instance
     *
     * @param string $name Name of the parser to retrieve
     *
     * @return mixed|null
     */
    public function getParser($name)
    {
        if (!isset($this->instances[$name])) {
            if (!isset($this->mapping[$name])) {
                return null;
            }
            $class = $this->mapping[$name];
            $this->instances[$name] = new $class();
        }

        return $this->instances[$name];
    }

    /**
     * Register a custom parser by name with the register
     *
     * @param string $name   Name or handle of the parser to register
     * @param mixed  $parser Instantiated parser to register
     */
    public function registerParser($name, $parser)
    {
        $this->instances[$name] = $parser;
    }
}
vendor/guzzle/guzzle/src/Guzzle/Parser/Message/MessageParserInterface.php000064400000001267151327705700022700 0ustar00<?php

namespace Guzzle\Parser\Message;

/**
 * HTTP message parser interface used to parse HTTP messages into an array
 */
interface MessageParserInterface
{
    /**
     * Parse an HTTP request message into an associative array of parts.
     *
     * @param string $message HTTP request to parse
     *
     * @return array|bool Returns false if the message is invalid
     */
    public function parseRequest($message);

    /**
     * Parse an HTTP response message into an associative array of parts.
     *
     * @param string $message HTTP response to parse
     *
     * @return array|bool Returns false if the message is invalid
     */
    public function parseResponse($message);
}
vendor/guzzle/guzzle/src/Guzzle/Parser/Message/MessageParser.php000064400000006453151327705700021061 0ustar00<?php

namespace Guzzle\Parser\Message;

/**
 * Default request and response parser used by Guzzle. Optimized for speed.
 */
class MessageParser extends AbstractMessageParser
{
    public function parseRequest($message)
    {
        if (!$message) {
            return false;
        }

        $parts = $this->parseMessage($message);

        // Parse the protocol and protocol version
        if (isset($parts['start_line'][2])) {
            $startParts = explode('/', $parts['start_line'][2]);
            $protocol = strtoupper($startParts[0]);
            $version = isset($startParts[1]) ? $startParts[1] : '1.1';
        } else {
            $protocol = 'HTTP';
            $version = '1.1';
        }

        $parsed = array(
            'method'   => strtoupper($parts['start_line'][0]),
            'protocol' => $protocol,
            'version'  => $version,
            'headers'  => $parts['headers'],
            'body'     => $parts['body']
        );

        $parsed['request_url'] = $this->getUrlPartsFromMessage(isset($parts['start_line'][1]) ? $parts['start_line'][1] : '' , $parsed);

        return $parsed;
    }

    public function parseResponse($message)
    {
        if (!$message) {
            return false;
        }

        $parts = $this->parseMessage($message);
        list($protocol, $version) = explode('/', trim($parts['start_line'][0]));

        return array(
            'protocol'      => $protocol,
            'version'       => $version,
            'code'          => $parts['start_line'][1],
            'reason_phrase' => isset($parts['start_line'][2]) ? $parts['start_line'][2] : '',
            'headers'       => $parts['headers'],
            'body'          => $parts['body']
        );
    }

    /**
     * Parse a message into parts
     *
     * @param string $message Message to parse
     *
     * @return array
     */
    protected function parseMessage($message)
    {
        $startLine = null;
        $headers = array();
        $body = '';

        // Iterate over each line in the message, accounting for line endings
        $lines = preg_split('/(\\r?\\n)/', $message, -1, PREG_SPLIT_DELIM_CAPTURE);
        for ($i = 0, $totalLines = count($lines); $i < $totalLines; $i += 2) {

            $line = $lines[$i];

            // If two line breaks were encountered, then this is the end of body
            if (empty($line)) {
                if ($i < $totalLines - 1) {
                    $body = implode('', array_slice($lines, $i + 2));
                }
                break;
            }

            // Parse message headers
            if (!$startLine) {
                $startLine = explode(' ', $line, 3);
            } elseif (strpos($line, ':')) {
                $parts = explode(':', $line, 2);
                $key = trim($parts[0]);
                $value = isset($parts[1]) ? trim($parts[1]) : '';
                if (!isset($headers[$key])) {
                    $headers[$key] = $value;
                } elseif (!is_array($headers[$key])) {
                    $headers[$key] = array($headers[$key], $value);
                } else {
                    $headers[$key][] = $value;
                }
            }
        }

        return array(
            'start_line' => $startLine,
            'headers'    => $headers,
            'body'       => $body
        );
    }
}
vendor/guzzle/guzzle/src/Guzzle/Parser/Message/PeclHttpMessageParser.php000064400000002256151327705700022522 0ustar00<?php

namespace Guzzle\Parser\Message;

/**
 * Pecl HTTP message parser
 */
class PeclHttpMessageParser extends AbstractMessageParser
{
    public function parseRequest($message)
    {
        if (!$message) {
            return false;
        }

        $parts = http_parse_message($message);

        $parsed = array(
            'method'   => $parts->requestMethod,
            'protocol' => 'HTTP',
            'version'  => number_format($parts->httpVersion, 1),
            'headers'  => $parts->headers,
            'body'     => $parts->body
        );

        $parsed['request_url'] = $this->getUrlPartsFromMessage($parts->requestUrl, $parsed);

        return $parsed;
    }

    public function parseResponse($message)
    {
        if (!$message) {
            return false;
        }

        $parts = http_parse_message($message);

        return array(
            'protocol'      => 'HTTP',
            'version'       => number_format($parts->httpVersion, 1),
            'code'          => $parts->responseCode,
            'reason_phrase' => $parts->responseStatus,
            'headers'       => $parts->headers,
            'body'          => $parts->body
        );
    }
}
vendor/guzzle/guzzle/src/Guzzle/Parser/Message/AbstractMessageParser.php000064400000003214151327705700022535 0ustar00<?php

namespace Guzzle\Parser\Message;

/**
 * Implements shared message parsing functionality
 */
abstract class AbstractMessageParser implements MessageParserInterface
{
    /**
     * Create URL parts from HTTP message parts
     *
     * @param string $requestUrl Associated URL
     * @param array  $parts      HTTP message parts
     *
     * @return array
     */
    protected function getUrlPartsFromMessage($requestUrl, array $parts)
    {
        // Parse the URL information from the message
        $urlParts = array(
            'path'   => $requestUrl,
            'scheme' => 'http'
        );

        // Check for the Host header
        if (isset($parts['headers']['Host'])) {
            $urlParts['host'] = $parts['headers']['Host'];
        } elseif (isset($parts['headers']['host'])) {
            $urlParts['host'] = $parts['headers']['host'];
        } else {
            $urlParts['host'] = null;
        }

        if (false === strpos($urlParts['host'], ':')) {
            $urlParts['port'] = '';
        } else {
            $hostParts = explode(':', $urlParts['host']);
            $urlParts['host'] = trim($hostParts[0]);
            $urlParts['port'] = (int) trim($hostParts[1]);
            if ($urlParts['port'] == 443) {
                $urlParts['scheme'] = 'https';
            }
        }

        // Check if a query is present
        $path = $urlParts['path'];
        $qpos = strpos($path, '?');
        if ($qpos) {
            $urlParts['query'] = substr($path, $qpos + 1);
            $urlParts['path'] = substr($path, 0, $qpos);
        } else {
            $urlParts['query'] = '';
        }

        return $urlParts;
    }
}
vendor/guzzle/guzzle/src/Guzzle/Parser/Url/UrlParser.php000064400000002624151327705700017411 0ustar00<?php

namespace Guzzle\Parser\Url;

use Guzzle\Common\Version;

/**
 * Parses URLs into parts using PHP's built-in parse_url() function
 * @deprecated Just use parse_url. UTF-8 characters should be percent encoded anyways.
 * @codeCoverageIgnore
 */
class UrlParser implements UrlParserInterface
{
    /** @var bool Whether or not to work with UTF-8 strings */
    protected $utf8 = false;

    /**
     * Set whether or not to attempt to handle UTF-8 strings (still WIP)
     *
     * @param bool $utf8 Set to TRUE to handle UTF string
     */
    public function setUtf8Support($utf8)
    {
        $this->utf8 = $utf8;
    }

    public function parseUrl($url)
    {
        Version::warn(__CLASS__ . ' is deprecated. Just use parse_url()');

        static $defaults = array('scheme' => null, 'host' => null, 'path' => null, 'port' => null, 'query' => null,
            'user' => null, 'pass' => null, 'fragment' => null);

        $parts = parse_url($url);

        // Need to handle query parsing specially for UTF-8 requirements
        if ($this->utf8 && isset($parts['query'])) {
            $queryPos = strpos($url, '?');
            if (isset($parts['fragment'])) {
                $parts['query'] = substr($url, $queryPos + 1, strpos($url, '#') - $queryPos - 1);
            } else {
                $parts['query'] = substr($url, $queryPos + 1);
            }
        }

        return $parts + $defaults;
    }
}
vendor/guzzle/guzzle/src/Guzzle/Parser/Url/UrlParserInterface.php000064400000001024151327705700021223 0ustar00<?php

namespace Guzzle\Parser\Url;

/**
 * URL parser interface
 */
interface UrlParserInterface
{
    /**
     * Parse a URL using special handling for a subset of UTF-8 characters in the query string if needed.
     *
     * @param string $url URL to parse
     *
     * @return array Returns an array identical to what is returned from parse_url().  When an array key is missing from
     *               this array, you must fill it in with NULL to avoid warnings in calling code.
     */
    public function parseUrl($url);
}
vendor/guzzle/guzzle/src/Guzzle/Parser/composer.json000064400000000727151327705700016743 0ustar00{
    "name": "guzzle/parser",
    "homepage": "http://guzzlephp.org/",
    "description": "Interchangeable parsers used by Guzzle",
    "keywords": ["HTTP", "message", "cookie", "URL", "URI Template"],
    "license": "MIT",
    "require": {
        "php": ">=5.3.2"
    },
    "autoload": {
        "psr-0": { "Guzzle\\Parser": "" }
    },
    "target-dir": "Guzzle/Parser",
    "extra": {
        "branch-alias": {
            "dev-master": "3.7-dev"
        }
    }
}
vendor/guzzle/guzzle/src/Guzzle/Http/RedirectPlugin.php000064400000023573151327705700017341 0ustar00<?php

namespace Guzzle\Http;

use Guzzle\Common\Event;
use Guzzle\Http\Exception\BadResponseException;
use Guzzle\Http\Url;
use Guzzle\Http\Message\Response;
use Guzzle\Http\Message\RequestInterface;
use Guzzle\Http\Message\RequestFactory;
use Guzzle\Http\Message\EntityEnclosingRequestInterface;
use Guzzle\Http\Exception\TooManyRedirectsException;
use Guzzle\Http\Exception\CouldNotRewindStreamException;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;

/**
 * Plugin to implement HTTP redirects. Can redirect like a web browser or using strict RFC 2616 compliance
 */
class RedirectPlugin implements EventSubscriberInterface
{
    const REDIRECT_COUNT = 'redirect.count';
    const MAX_REDIRECTS = 'redirect.max';
    const STRICT_REDIRECTS = 'redirect.strict';
    const PARENT_REQUEST = 'redirect.parent_request';
    const DISABLE = 'redirect.disable';

    /**
     * @var int Default number of redirects allowed when no setting is supplied by a request
     */
    protected $defaultMaxRedirects = 5;

    public static function getSubscribedEvents()
    {
        return array(
            'request.sent'        => array('onRequestSent', 100),
            'request.clone'       => 'cleanupRequest',
            'request.before_send' => 'cleanupRequest'
        );
    }

    /**
     * Clean up the parameters of a request when it is cloned
     *
     * @param Event $event Event emitted
     */
    public function cleanupRequest(Event $event)
    {
        $params = $event['request']->getParams();
        unset($params[self::REDIRECT_COUNT]);
        unset($params[self::PARENT_REQUEST]);
    }

    /**
     * Called when a request receives a redirect response
     *
     * @param Event $event Event emitted
     */
    public function onRequestSent(Event $event)
    {
        $response = $event['response'];
        $request = $event['request'];

        // Only act on redirect requests with Location headers
        if (!$response || $request->getParams()->get(self::DISABLE)) {
            return;
        }

        // Trace the original request based on parameter history
        $original = $this->getOriginalRequest($request);

        // Terminating condition to set the effective response on the original request
        if (!$response->isRedirect() || !$response->hasHeader('Location')) {
            if ($request !== $original) {
                // This is a terminating redirect response, so set it on the original request
                $response->getParams()->set(self::REDIRECT_COUNT, $original->getParams()->get(self::REDIRECT_COUNT));
                $original->setResponse($response);
                $response->setEffectiveUrl($request->getUrl());
            }
            return;
        }

        $this->sendRedirectRequest($original, $request, $response);
    }

    /**
     * Get the original request that initiated a series of redirects
     *
     * @param RequestInterface $request Request to get the original request from
     *
     * @return RequestInterface
     */
    protected function getOriginalRequest(RequestInterface $request)
    {
        $original = $request;
        // The number of redirects is held on the original request, so determine which request that is
        while ($parent = $original->getParams()->get(self::PARENT_REQUEST)) {
            $original = $parent;
        }

        return $original;
    }

    /**
     * Create a redirect request for a specific request object
     *
     * Takes into account strict RFC compliant redirection (e.g. redirect POST with POST) vs doing what most clients do
     * (e.g. redirect POST with GET).
     *
     * @param RequestInterface $request    Request being redirected
     * @param RequestInterface $original   Original request
     * @param int              $statusCode Status code of the redirect
     * @param string           $location   Location header of the redirect
     *
     * @return RequestInterface Returns a new redirect request
     * @throws CouldNotRewindStreamException If the body needs to be rewound but cannot
     */
    protected function createRedirectRequest(
        RequestInterface $request,
        $statusCode,
        $location,
        RequestInterface $original
    ) {
        $redirectRequest = null;
        $strict = $original->getParams()->get(self::STRICT_REDIRECTS);

        // Switch method to GET for 303 redirects.  301 and 302 redirects also switch to GET unless we are forcing RFC
        // compliance to emulate what most browsers do.  NOTE: IE only switches methods on 301/302 when coming from a POST.
        if ($request instanceof EntityEnclosingRequestInterface && ($statusCode == 303 || (!$strict && $statusCode <= 302))) {
            $redirectRequest = RequestFactory::getInstance()->cloneRequestWithMethod($request, 'GET');
        } else {
            $redirectRequest = clone $request;
        }

        $redirectRequest->setIsRedirect(true);
        // Always use the same response body when redirecting
        $redirectRequest->setResponseBody($request->getResponseBody());

        $location = Url::factory($location);
        // If the location is not absolute, then combine it with the original URL
        if (!$location->isAbsolute()) {
            $originalUrl = $redirectRequest->getUrl(true);
            // Remove query string parameters and just take what is present on the redirect Location header
            $originalUrl->getQuery()->clear();
            $location = $originalUrl->combine((string) $location, true);
        }

        $redirectRequest->setUrl($location);

        // Add the parent request to the request before it sends (make sure it's before the onRequestClone event too)
        $redirectRequest->getEventDispatcher()->addListener(
            'request.before_send',
            $func = function ($e) use (&$func, $request, $redirectRequest) {
                $redirectRequest->getEventDispatcher()->removeListener('request.before_send', $func);
                $e['request']->getParams()->set(RedirectPlugin::PARENT_REQUEST, $request);
            }
        );

        // Rewind the entity body of the request if needed
        if ($redirectRequest instanceof EntityEnclosingRequestInterface && $redirectRequest->getBody()) {
            $body = $redirectRequest->getBody();
            // Only rewind the body if some of it has been read already, and throw an exception if the rewind fails
            if ($body->ftell() && !$body->rewind()) {
                throw new CouldNotRewindStreamException(
                    'Unable to rewind the non-seekable entity body of the request after redirecting. cURL probably '
                    . 'sent part of body before the redirect occurred. Try adding acustom rewind function using on the '
                    . 'entity body of the request using setRewindFunction().'
                );
            }
        }

        return $redirectRequest;
    }

    /**
     * Prepare the request for redirection and enforce the maximum number of allowed redirects per client
     *
     * @param RequestInterface $original  Original request
     * @param RequestInterface $request   Request to prepare and validate
     * @param Response         $response  The current response
     *
     * @return RequestInterface
     */
    protected function prepareRedirection(RequestInterface $original, RequestInterface $request, Response $response)
    {
        $params = $original->getParams();
        // This is a new redirect, so increment the redirect counter
        $current = $params[self::REDIRECT_COUNT] + 1;
        $params[self::REDIRECT_COUNT] = $current;
        // Use a provided maximum value or default to a max redirect count of 5
        $max = isset($params[self::MAX_REDIRECTS]) ? $params[self::MAX_REDIRECTS] : $this->defaultMaxRedirects;

        // Throw an exception if the redirect count is exceeded
        if ($current > $max) {
            $this->throwTooManyRedirectsException($original, $max);
            return false;
        } else {
            // Create a redirect request based on the redirect rules set on the request
            return $this->createRedirectRequest(
                $request,
                $response->getStatusCode(),
                trim($response->getLocation()),
                $original
            );
        }
    }

    /**
     * Send a redirect request and handle any errors
     *
     * @param RequestInterface $original The originating request
     * @param RequestInterface $request  The current request being redirected
     * @param Response         $response The response of the current request
     *
     * @throws BadResponseException|\Exception
     */
    protected function sendRedirectRequest(RequestInterface $original, RequestInterface $request, Response $response)
    {
        // Validate and create a redirect request based on the original request and current response
        if ($redirectRequest = $this->prepareRedirection($original, $request, $response)) {
            try {
                $redirectRequest->send();
            } catch (BadResponseException $e) {
                $e->getResponse();
                if (!$e->getResponse()) {
                    throw $e;
                }
            }
        }
    }

    /**
     * Throw a too many redirects exception for a request
     *
     * @param RequestInterface $original Request
     * @param int              $max      Max allowed redirects
     *
     * @throws TooManyRedirectsException when too many redirects have been issued
     */
    protected function throwTooManyRedirectsException(RequestInterface $original, $max)
    {
        $original->getEventDispatcher()->addListener(
            'request.complete',
            $func = function ($e) use (&$func, $original, $max) {
                $original->getEventDispatcher()->removeListener('request.complete', $func);
                $str = "{$max} redirects were issued for this request:\n" . $e['request']->getRawHeaders();
                throw new TooManyRedirectsException($str);
            }
        );
    }
}
vendor/guzzle/guzzle/src/Guzzle/Http/AbstractEntityBodyDecorator.php000064400000010273151327705700022033 0ustar00<?php

namespace Guzzle\Http;

use Guzzle\Stream\Stream;

/**
 * Abstract decorator used to wrap entity bodies
 */
class AbstractEntityBodyDecorator implements EntityBodyInterface
{
    /** @var EntityBodyInterface Decorated entity body */
    protected $body;

    /**
     * @param EntityBodyInterface $body Entity body to decorate
     */
    public function __construct(EntityBodyInterface $body)
    {
        $this->body = $body;
    }

    public function __toString()
    {
        return (string) $this->body;
    }

    /**
     * Allow decorators to implement custom methods
     *
     * @param string $method Missing method name
     * @param array  $args   Method arguments
     *
     * @return mixed
     */
    public function __call($method, array $args)
    {
        return call_user_func_array(array($this->body, $method), $args);
    }

    public function close()
    {
        return $this->body->close();
    }

    public function setRewindFunction($callable)
    {
        $this->body->setRewindFunction($callable);

        return $this;
    }

    public function rewind()
    {
        return $this->body->rewind();
    }

    public function compress($filter = 'zlib.deflate')
    {
        return $this->body->compress($filter);
    }

    public function uncompress($filter = 'zlib.inflate')
    {
        return $this->body->uncompress($filter);
    }

    public function getContentLength()
    {
        return $this->getSize();
    }

    public function getContentType()
    {
        return $this->body->getContentType();
    }

    public function getContentMd5($rawOutput = false, $base64Encode = false)
    {
        $hash = Stream::getHash($this, 'md5', $rawOutput);

        return $hash && $base64Encode ? base64_encode($hash) : $hash;
    }

    public function getContentEncoding()
    {
        return $this->body->getContentEncoding();
    }

    public function getMetaData($key = null)
    {
        return $this->body->getMetaData($key);
    }

    public function getStream()
    {
        return $this->body->getStream();
    }

    public function setStream($stream, $size = 0)
    {
        $this->body->setStream($stream, $size);

        return $this;
    }

    public function detachStream()
    {
        $this->body->detachStream();

        return $this;
    }

    public function getWrapper()
    {
        return $this->body->getWrapper();
    }

    public function getWrapperData()
    {
        return $this->body->getWrapperData();
    }

    public function getStreamType()
    {
        return $this->body->getStreamType();
    }

    public function getUri()
    {
        return $this->body->getUri();
    }

    public function getSize()
    {
        return $this->body->getSize();
    }

    public function isReadable()
    {
        return $this->body->isReadable();
    }

    public function isRepeatable()
    {
        return $this->isSeekable() && $this->isReadable();
    }

    public function isWritable()
    {
        return $this->body->isWritable();
    }

    public function isConsumed()
    {
        return $this->body->isConsumed();
    }

    /**
     * Alias of isConsumed()
     * {@inheritdoc}
     */
    public function feof()
    {
        return $this->isConsumed();
    }

    public function isLocal()
    {
        return $this->body->isLocal();
    }

    public function isSeekable()
    {
        return $this->body->isSeekable();
    }

    public function setSize($size)
    {
        $this->body->setSize($size);

        return $this;
    }

    public function seek($offset, $whence = SEEK_SET)
    {
        return $this->body->seek($offset, $whence);
    }

    public function read($length)
    {
        return $this->body->read($length);
    }

    public function write($string)
    {
        return $this->body->write($string);
    }

    public function readLine($maxLength = null)
    {
        return $this->body->readLine($maxLength);
    }

    public function ftell()
    {
        return $this->body->ftell();
    }

    public function getCustomData($key)
    {
        return $this->body->getCustomData($key);
    }

    public function setCustomData($key, $value)
    {
        $this->body->setCustomData($key, $value);

        return $this;
    }
}
vendor/guzzle/guzzle/src/Guzzle/Http/composer.json000064400000001450151327705700016420 0ustar00{
    "name": "guzzle/http",
    "description": "HTTP libraries used by Guzzle",
    "homepage": "http://guzzlephp.org/",
    "keywords": ["http client", "http", "client", "Guzzle", "curl"],
    "license": "MIT",
    "authors": [
        {
            "name": "Michael Dowling",
            "email": "mtdowling@gmail.com",
            "homepage": "https://github.com/mtdowling"
        }
    ],
    "require": {
        "php": ">=5.3.2",
        "guzzle/common": "self.version",
        "guzzle/parser": "self.version",
        "guzzle/stream": "self.version"
    },
    "suggest": {
        "ext-curl": "*"
    },
    "autoload": {
        "psr-0": { "Guzzle\\Http": "" }
    },
    "target-dir": "Guzzle/Http",
    "extra": {
        "branch-alias": {
            "dev-master": "3.7-dev"
        }
    }
}
vendor/guzzle/guzzle/src/Guzzle/Http/IoEmittingEntityBody.php000064400000003760151327705700020500 0ustar00<?php

namespace Guzzle\Http;

use Guzzle\Common\Event;
use Guzzle\Common\HasDispatcherInterface;
use Symfony\Component\EventDispatcher\EventDispatcher;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;

/**
 * EntityBody decorator that emits events for read and write methods
 */
class IoEmittingEntityBody extends AbstractEntityBodyDecorator implements HasDispatcherInterface
{
    /** @var EventDispatcherInterface */
    protected $eventDispatcher;

    public static function getAllEvents()
    {
        return array('body.read', 'body.write');
    }

    /**
     * {@inheritdoc}
     * @codeCoverageIgnore
     */
    public function setEventDispatcher(EventDispatcherInterface $eventDispatcher)
    {
        $this->eventDispatcher = $eventDispatcher;

        return $this;
    }

    public function getEventDispatcher()
    {
        if (!$this->eventDispatcher) {
            $this->eventDispatcher = new EventDispatcher();
        }

        return $this->eventDispatcher;
    }

    public function dispatch($eventName, array $context = array())
    {
        return $this->getEventDispatcher()->dispatch($eventName, new Event($context));
    }

    /**
     * {@inheritdoc}
     * @codeCoverageIgnore
     */
    public function addSubscriber(EventSubscriberInterface $subscriber)
    {
        $this->getEventDispatcher()->addSubscriber($subscriber);

        return $this;
    }

    public function read($length)
    {
        $event = array(
            'body'   => $this,
            'length' => $length,
            'read'   => $this->body->read($length)
        );
        $this->dispatch('body.read', $event);

        return $event['read'];
    }

    public function write($string)
    {
        $event = array(
            'body'   => $this,
            'write'  => $string,
            'result' => $this->body->write($string)
        );
        $this->dispatch('body.write', $event);

        return $event['result'];
    }
}
vendor/guzzle/guzzle/src/Guzzle/Http/Url.php000064400000034442151327705700015160 0ustar00<?php

namespace Guzzle\Http;

use Guzzle\Common\Exception\InvalidArgumentException;

/**
 * Parses and generates URLs based on URL parts. In favor of performance, URL parts are not validated.
 */
class Url
{
    protected $scheme;
    protected $host;
    protected $port;
    protected $username;
    protected $password;
    protected $path = '';
    protected $fragment;

    /** @var QueryString Query part of the URL */
    protected $query;

    /**
     * Factory method to create a new URL from a URL string
     *
     * @param string $url Full URL used to create a Url object
     *
     * @return Url
     * @throws InvalidArgumentException
     */
    public static function factory($url)
    {
        static $defaults = array('scheme' => null, 'host' => null, 'path' => null, 'port' => null, 'query' => null,
            'user' => null, 'pass' => null, 'fragment' => null);

        if (false === ($parts = parse_url($url))) {
            throw new InvalidArgumentException('Was unable to parse malformed url: ' . $url);
        }

        $parts += $defaults;

        // Convert the query string into a QueryString object
        if ($parts['query'] || 0 !== strlen($parts['query'])) {
            $parts['query'] = QueryString::fromString($parts['query']);
        }

        return new static($parts['scheme'], $parts['host'], $parts['user'],
            $parts['pass'], $parts['port'], $parts['path'], $parts['query'],
            $parts['fragment']);
    }

    /**
     * Build a URL from parse_url parts. The generated URL will be a relative URL if a scheme or host are not provided.
     *
     * @param array $parts Array of parse_url parts
     *
     * @return string
     */
    public static function buildUrl(array $parts)
    {
        $url = $scheme = '';

        if (isset($parts['scheme'])) {
            $scheme = $parts['scheme'];
            $url .= $scheme . ':';
        }

        if (isset($parts['host'])) {
            $url .= '//';
            if (isset($parts['user'])) {
                $url .= $parts['user'];
                if (isset($parts['pass'])) {
                    $url .= ':' . $parts['pass'];
                }
                $url .=  '@';
            }

            $url .= $parts['host'];

            // Only include the port if it is not the default port of the scheme
            if (isset($parts['port'])
                && !(($scheme == 'http' && $parts['port'] == 80) || ($scheme == 'https' && $parts['port'] == 443))
            ) {
                $url .= ':' . $parts['port'];
            }
        }

        // Add the path component if present
        if (isset($parts['path']) && 0 !== strlen($parts['path'])) {
            // Always ensure that the path begins with '/' if set and something is before the path
            if ($url && $parts['path'][0] != '/' && substr($url, -1)  != '/') {
                $url .= '/';
            }
            $url .= $parts['path'];
        }

        // Add the query string if present
        if (isset($parts['query'])) {
            $url .= '?' . $parts['query'];
        }

        // Ensure that # is only added to the url if fragment contains anything.
        if (isset($parts['fragment'])) {
            $url .= '#' . $parts['fragment'];
        }

        return $url;
    }

    /**
     * Create a new URL from URL parts
     *
     * @param string                   $scheme   Scheme of the URL
     * @param string                   $host     Host of the URL
     * @param string                   $username Username of the URL
     * @param string                   $password Password of the URL
     * @param int                      $port     Port of the URL
     * @param string                   $path     Path of the URL
     * @param QueryString|array|string $query    Query string of the URL
     * @param string                   $fragment Fragment of the URL
     */
    public function __construct($scheme, $host, $username = null, $password = null, $port = null, $path = null, QueryString $query = null, $fragment = null)
    {
        $this->scheme = $scheme;
        $this->host = $host;
        $this->port = $port;
        $this->username = $username;
        $this->password = $password;
        $this->fragment = $fragment;
        if (!$query) {
            $this->query = new QueryString();
        } else {
            $this->setQuery($query);
        }
        $this->setPath($path);
    }

    /**
     * Clone the URL
     */
    public function __clone()
    {
        $this->query = clone $this->query;
    }

    /**
     * Returns the URL as a URL string
     *
     * @return string
     */
    public function __toString()
    {
        return self::buildUrl($this->getParts());
    }

    /**
     * Get the parts of the URL as an array
     *
     * @return array
     */
    public function getParts()
    {
        $query = (string) $this->query;

        return array(
            'scheme' => $this->scheme,
            'user' => $this->username,
            'pass' => $this->password,
            'host' => $this->host,
            'port' => $this->port,
            'path' => $this->getPath(),
            'query' => $query !== '' ? $query : null,
            'fragment' => $this->fragment,
        );
    }

    /**
     * Set the host of the request.
     *
     * @param string $host Host to set (e.g. www.yahoo.com, yahoo.com)
     *
     * @return Url
     */
    public function setHost($host)
    {
        if (strpos($host, ':') === false) {
            $this->host = $host;
        } else {
            list($host, $port) = explode(':', $host);
            $this->host = $host;
            $this->setPort($port);
        }

        return $this;
    }

    /**
     * Get the host part of the URL
     *
     * @return string
     */
    public function getHost()
    {
        return $this->host;
    }

    /**
     * Set the scheme part of the URL (http, https, ftp, etc)
     *
     * @param string $scheme Scheme to set
     *
     * @return Url
     */
    public function setScheme($scheme)
    {
        if ($this->scheme == 'http' && $this->port == 80) {
            $this->port = null;
        } elseif ($this->scheme == 'https' && $this->port == 443) {
            $this->port = null;
        }

        $this->scheme = $scheme;

        return $this;
    }

    /**
     * Get the scheme part of the URL
     *
     * @return string
     */
    public function getScheme()
    {
        return $this->scheme;
    }

    /**
     * Set the port part of the URL
     *
     * @param int $port Port to set
     *
     * @return Url
     */
    public function setPort($port)
    {
        $this->port = $port;

        return $this;
    }

    /**
     * Get the port part of the URl. Will return the default port for a given scheme if no port has been set.
     *
     * @return int|null
     */
    public function getPort()
    {
        if ($this->port) {
            return $this->port;
        } elseif ($this->scheme == 'http') {
            return 80;
        } elseif ($this->scheme == 'https') {
            return 443;
        }

        return null;
    }

    /**
     * Set the path part of the URL
     *
     * @param array|string $path Path string or array of path segments
     *
     * @return Url
     */
    public function setPath($path)
    {
        static $pathReplace = array(' ' => '%20', '?' => '%3F');
        if (is_array($path)) {
            $path = '/' . implode('/', $path);
        }

        $this->path = strtr($path, $pathReplace);

        return $this;
    }

    /**
     * Normalize the URL so that double slashes and relative paths are removed
     *
     * @return Url
     */
    public function normalizePath()
    {
        if (!$this->path || $this->path == '/' || $this->path == '*') {
            return $this;
        }

        $results = array();
        $segments = $this->getPathSegments();
        foreach ($segments as $segment) {
            if ($segment == '..') {
                array_pop($results);
            } elseif ($segment != '.' && $segment != '') {
                $results[] = $segment;
            }
        }

        // Combine the normalized parts and add the leading slash if needed
        $this->path = ($this->path[0] == '/' ? '/' : '') . implode('/', $results);

        // Add the trailing slash if necessary
        if ($this->path != '/' && end($segments) == '') {
            $this->path .= '/';
        }

        return $this;
    }

    /**
     * Add a relative path to the currently set path.
     *
     * @param string $relativePath Relative path to add
     *
     * @return Url
     */
    public function addPath($relativePath)
    {
        if ($relativePath != '/' && is_string($relativePath) && strlen($relativePath) > 0) {
            // Add a leading slash if needed
            if ($relativePath[0] != '/') {
                $relativePath = '/' . $relativePath;
            }
            $this->setPath(str_replace('//', '/', $this->path . $relativePath));
        }

        return $this;
    }

    /**
     * Get the path part of the URL
     *
     * @return string
     */
    public function getPath()
    {
        return $this->path;
    }

    /**
     * Get the path segments of the URL as an array
     *
     * @return array
     */
    public function getPathSegments()
    {
        return array_slice(explode('/', $this->getPath()), 1);
    }

    /**
     * Set the password part of the URL
     *
     * @param string $password Password to set
     *
     * @return Url
     */
    public function setPassword($password)
    {
        $this->password = $password;

        return $this;
    }

    /**
     * Get the password part of the URL
     *
     * @return null|string
     */
    public function getPassword()
    {
        return $this->password;
    }

    /**
     * Set the username part of the URL
     *
     * @param string $username Username to set
     *
     * @return Url
     */
    public function setUsername($username)
    {
        $this->username = $username;

        return $this;
    }

    /**
     * Get the username part of the URl
     *
     * @return null|string
     */
    public function getUsername()
    {
        return $this->username;
    }

    /**
     * Get the query part of the URL as a QueryString object
     *
     * @return QueryString
     */
    public function getQuery()
    {
        return $this->query;
    }

    /**
     * Set the query part of the URL
     *
     * @param QueryString|string|array $query Query to set
     *
     * @return Url
     */
    public function setQuery($query)
    {
        if (is_string($query)) {
            $output = null;
            parse_str($query, $output);
            $this->query = new QueryString($output);
        } elseif (is_array($query)) {
            $this->query = new QueryString($query);
        } elseif ($query instanceof QueryString) {
            $this->query = $query;
        }

        return $this;
    }

    /**
     * Get the fragment part of the URL
     *
     * @return null|string
     */
    public function getFragment()
    {
        return $this->fragment;
    }

    /**
     * Set the fragment part of the URL
     *
     * @param string $fragment Fragment to set
     *
     * @return Url
     */
    public function setFragment($fragment)
    {
        $this->fragment = $fragment;

        return $this;
    }

    /**
     * Check if this is an absolute URL
     *
     * @return bool
     */
    public function isAbsolute()
    {
        return $this->scheme && $this->host;
    }

    /**
     * Combine the URL with another URL. Follows the rules specific in RFC 3986 section 5.4.
     *
     * @param string $url           Relative URL to combine with
     * @param bool   $strictRfc3986 Set to true to use strict RFC 3986 compliance when merging paths. When first
     *                              released, Guzzle used an incorrect algorithm for combining relative URL paths. In
     *                              order to not break users, we introduced this flag to allow the merging of URLs based
     *                              on strict RFC 3986 section 5.4.1. This means that "http://a.com/foo/baz" merged with
     *                              "bar" would become "http://a.com/foo/bar". When this value is set to false, it would
     *                              become "http://a.com/foo/baz/bar".
     * @return Url
     * @throws InvalidArgumentException
     * @link http://tools.ietf.org/html/rfc3986#section-5.4
     */
    public function combine($url, $strictRfc3986 = false)
    {
        $url = self::factory($url);

        // Use the more absolute URL as the base URL
        if (!$this->isAbsolute() && $url->isAbsolute()) {
            $url = $url->combine($this);
        }

        // Passing a URL with a scheme overrides everything
        if ($buffer = $url->getScheme()) {
            $this->scheme = $buffer;
            $this->host = $url->getHost();
            $this->port = $url->getPort();
            $this->username = $url->getUsername();
            $this->password = $url->getPassword();
            $this->path = $url->getPath();
            $this->query = $url->getQuery();
            $this->fragment = $url->getFragment();
            return $this;
        }

        // Setting a host overrides the entire rest of the URL
        if ($buffer = $url->getHost()) {
            $this->host = $buffer;
            $this->port = $url->getPort();
            $this->username = $url->getUsername();
            $this->password = $url->getPassword();
            $this->path = $url->getPath();
            $this->query = $url->getQuery();
            $this->fragment = $url->getFragment();
            return $this;
        }

        $path = $url->getPath();
        $query = $url->getQuery();

        if (!$path) {
            if (count($query)) {
                $this->addQuery($query, $strictRfc3986);
            }
        } else {
            if ($path[0] == '/') {
                $this->path = $path;
            } elseif ($strictRfc3986) {
                $this->path .= '/../' . $path;
            } else {
                $this->path .= '/' . $path;
            }
            $this->normalizePath();
            $this->addQuery($query, $strictRfc3986);
        }

        $this->fragment = $url->getFragment();

        return $this;
    }

    private function addQuery(QueryString $new, $strictRfc386)
    {
        if (!$strictRfc386) {
            $new->merge($this->query);
        }

        $this->query = $new;
    }
}
vendor/guzzle/guzzle/src/Guzzle/Http/Exception/MultiTransferException.php000064400000006603151327705700023030 0ustar00<?php

namespace Guzzle\Http\Exception;

use Guzzle\Common\Exception\ExceptionCollection;
use Guzzle\Http\Message\RequestInterface;

/**
 * Exception encountered during a multi transfer
 */
class MultiTransferException extends ExceptionCollection
{
    protected $successfulRequests = array();
    protected $failedRequests = array();
    protected $exceptionForRequest = array();

    /**
     * Get all of the requests in the transfer
     *
     * @return array
     */
    public function getAllRequests()
    {
        return array_merge($this->successfulRequests, $this->failedRequests);
    }

    /**
     * Add to the array of successful requests
     *
     * @param RequestInterface $request Successful request
     *
     * @return self
     */
    public function addSuccessfulRequest(RequestInterface $request)
    {
        $this->successfulRequests[] = $request;

        return $this;
    }

    /**
     * Add to the array of failed requests
     *
     * @param RequestInterface $request Failed request
     *
     * @return self
     */
    public function addFailedRequest(RequestInterface $request)
    {
        $this->failedRequests[] = $request;

        return $this;
    }

    /**
     * Add to the array of failed requests and associate with exceptions
     *
     * @param RequestInterface $request   Failed request
     * @param \Exception       $exception Exception to add and associate with
     *
     * @return self
     */
    public function addFailedRequestWithException(RequestInterface $request, \Exception $exception)
    {
        $this->add($exception)
             ->addFailedRequest($request)
             ->exceptionForRequest[spl_object_hash($request)] = $exception;

        return $this;
    }

    /**
     * Get the Exception that caused the given $request to fail
     *
     * @param RequestInterface $request Failed command
     *
     * @return \Exception|null
     */
    public function getExceptionForFailedRequest(RequestInterface $request)
    {
        $oid = spl_object_hash($request);

        return isset($this->exceptionForRequest[$oid]) ? $this->exceptionForRequest[$oid] : null;
    }

    /**
     * Set all of the successful requests
     *
     * @param array Array of requests
     *
     * @return self
     */
    public function setSuccessfulRequests(array $requests)
    {
        $this->successfulRequests = $requests;

        return $this;
    }

    /**
     * Set all of the failed requests
     *
     * @param array Array of requests
     *
     * @return self
     */
    public function setFailedRequests(array $requests)
    {
        $this->failedRequests = $requests;

        return $this;
    }

    /**
     * Get an array of successful requests sent in the multi transfer
     *
     * @return array
     */
    public function getSuccessfulRequests()
    {
        return $this->successfulRequests;
    }

    /**
     * Get an array of failed requests sent in the multi transfer
     *
     * @return array
     */
    public function getFailedRequests()
    {
        return $this->failedRequests;
    }

    /**
     * Check if the exception object contains a request
     *
     * @param RequestInterface $request Request to check
     *
     * @return bool
     */
    public function containsRequest(RequestInterface $request)
    {
        return in_array($request, $this->failedRequests, true) || in_array($request, $this->successfulRequests, true);
    }
}
vendor/guzzle/guzzle/src/Guzzle/Http/Exception/HttpException.php000064400000000256151327705700021146 0ustar00<?php

namespace Guzzle\Http\Exception;

use Guzzle\Common\Exception\GuzzleException;

/**
 * Http exception interface
 */
interface HttpException extends GuzzleException {}
vendor/guzzle/guzzle/src/Guzzle/Http/Exception/TooManyRedirectsException.php000064400000000151151327705700023454 0ustar00<?php

namespace Guzzle\Http\Exception;

class TooManyRedirectsException extends BadResponseException {}
vendor/guzzle/guzzle/src/Guzzle/Http/Exception/ClientErrorResponseException.php000064400000000260151327705700024171 0ustar00<?php

namespace Guzzle\Http\Exception;

/**
 * Exception when a client error is encountered (4xx codes)
 */
class ClientErrorResponseException extends BadResponseException {}
vendor/guzzle/guzzle/src/Guzzle/Http/Exception/CurlException.php000064400000003503151327705700021132 0ustar00<?php

namespace Guzzle\Http\Exception;

use Guzzle\Http\Curl\CurlHandle;

/**
 * cURL request exception
 */
class CurlException extends RequestException
{
    private $curlError;
    private $curlErrorNo;
    private $handle;
    private $curlInfo = array();

    /**
     * Set the cURL error message
     *
     * @param string $error  Curl error
     * @param int    $number Curl error number
     *
     * @return self
     */
    public function setError($error, $number)
    {
        $this->curlError = $error;
        $this->curlErrorNo = $number;

        return $this;
    }

    /**
     * Set the associated curl handle
     *
     * @param CurlHandle $handle Curl handle
     *
     * @return self
     */
    public function setCurlHandle(CurlHandle $handle)
    {
        $this->handle = $handle;

        return $this;
    }

    /**
     * Get the associated cURL handle
     *
     * @return CurlHandle|null
     */
    public function getCurlHandle()
    {
        return $this->handle;
    }

    /**
     * Get the associated cURL error message
     *
     * @return string|null
     */
    public function getError()
    {
        return $this->curlError;
    }

    /**
     * Get the associated cURL error number
     *
     * @return int|null
     */
    public function getErrorNo()
    {
        return $this->curlErrorNo;
    }

    /**
     * Returns curl information about the transfer
     *
     * @return array
     */
    public function getCurlInfo()
    {
        return $this->curlInfo;
    }

    /**
     * Set curl transfer information
     *
     * @param array $info Array of curl transfer information
     *
     * @return self
     * @link http://php.net/manual/en/function.curl-getinfo.php
     */
    public function setCurlInfo(array $info)
    {
        $this->curlInfo = $info;

        return $this;
    }
}
vendor/guzzle/guzzle/src/Guzzle/Http/Exception/CouldNotRewindStreamException.php000064400000000261151327705700024277 0ustar00<?php

namespace Guzzle\Http\Exception;

use Guzzle\Common\Exception\RuntimeException;

class CouldNotRewindStreamException extends RuntimeException implements HttpException {}
vendor/guzzle/guzzle/src/Guzzle/Http/Exception/ServerErrorResponseException.php000064400000000260151327705700024221 0ustar00<?php

namespace Guzzle\Http\Exception;

/**
 * Exception when a server error is encountered (5xx codes)
 */
class ServerErrorResponseException extends BadResponseException {}
vendor/guzzle/guzzle/src/Guzzle/Http/Exception/RequestException.php000064400000001406151327705700021655 0ustar00<?php

namespace Guzzle\Http\Exception;

use Guzzle\Common\Exception\RuntimeException;
use Guzzle\Http\Message\RequestInterface;

/**
 * Http request exception
 */
class RequestException extends RuntimeException implements HttpException
{
    /** @var RequestInterface */
    protected $request;

    /**
     * Set the request that caused the exception
     *
     * @param RequestInterface $request Request to set
     *
     * @return RequestException
     */
    public function setRequest(RequestInterface $request)
    {
        $this->request = $request;

        return $this;
    }

    /**
     * Get the request that caused the exception
     *
     * @return RequestInterface
     */
    public function getRequest()
    {
        return $this->request;
    }
}
vendor/guzzle/guzzle/src/Guzzle/Http/Exception/BadResponseException.php000064400000003464151327705700022440 0ustar00<?php

namespace Guzzle\Http\Exception;

use Guzzle\Http\Message\RequestInterface;
use Guzzle\Http\Message\Response;

/**
 * Http request exception thrown when a bad response is received
 */
class BadResponseException extends RequestException
{
    /** @var Response */
    private $response;

    /**
     * Factory method to create a new response exception based on the response code.
     *
     * @param RequestInterface $request  Request
     * @param Response         $response Response received
     *
     * @return BadResponseException
     */
    public static function factory(RequestInterface $request, Response $response)
    {
        if ($response->isClientError()) {
            $label = 'Client error response';
            $class = __NAMESPACE__ . '\\ClientErrorResponseException';
        } elseif ($response->isServerError()) {
            $label = 'Server error response';
            $class = __NAMESPACE__ . '\\ServerErrorResponseException';
        } else {
            $label = 'Unsuccessful response';
            $class = __CLASS__;
        }

        $message = $label . PHP_EOL . implode(PHP_EOL, array(
            '[status code] ' . $response->getStatusCode(),
            '[reason phrase] ' . $response->getReasonPhrase(),
            '[url] ' . $request->getUrl(),
        ));

        $e = new $class($message);
        $e->setResponse($response);
        $e->setRequest($request);

        return $e;
    }

    /**
     * Set the response that caused the exception
     *
     * @param Response $response Response to set
     */
    public function setResponse(Response $response)
    {
        $this->response = $response;
    }

    /**
     * Get the response that caused the exception
     *
     * @return Response
     */
    public function getResponse()
    {
        return $this->response;
    }
}
vendor/guzzle/guzzle/src/Guzzle/Http/Message/EntityEnclosingRequest.php000064400000016655151327705700022477 0ustar00<?php

namespace Guzzle\Http\Message;

use Guzzle\Http\EntityBody;
use Guzzle\Http\EntityBodyInterface;
use Guzzle\Http\QueryString;
use Guzzle\Http\RedirectPlugin;
use Guzzle\Http\Exception\RequestException;

/**
 * HTTP request that sends an entity-body in the request message (POST, PUT, PATCH, DELETE)
 */
class EntityEnclosingRequest extends Request implements EntityEnclosingRequestInterface
{
    /** @var int When the size of the body is greater than 1MB, then send Expect: 100-Continue */
    protected $expectCutoff = 1048576;

    /** @var EntityBodyInterface $body Body of the request */
    protected $body;

    /** @var QueryString POST fields to use in the EntityBody */
    protected $postFields;

    /** @var array POST files to send with the request */
    protected $postFiles = array();

    public function __construct($method, $url, $headers = array())
    {
        $this->postFields = new QueryString();
        parent::__construct($method, $url, $headers);
    }

    /**
     * @return string
     */
    public function __toString()
    {
        // Only attempt to include the POST data if it's only fields
        if (count($this->postFields) && empty($this->postFiles)) {
            return parent::__toString() . (string) $this->postFields;
        }

        return parent::__toString() . $this->body;
    }

    public function setState($state, array $context = array())
    {
        parent::setState($state, $context);
        if ($state == self::STATE_TRANSFER && !$this->body && !count($this->postFields) && !count($this->postFiles)) {
            $this->setHeader('Content-Length', 0)->removeHeader('Transfer-Encoding');
        }

        return $this->state;
    }

    public function setBody($body, $contentType = null)
    {
        $this->body = EntityBody::factory($body);

        // Auto detect the Content-Type from the path of the request if possible
        if ($contentType === null && !$this->hasHeader('Content-Type')) {
            $contentType = $this->body->getContentType();
        }

        if ($contentType) {
            $this->setHeader('Content-Type', $contentType);
        }

        // Always add the Expect 100-Continue header if the body cannot be rewound. This helps with redirects.
        if (!$this->body->isSeekable() && $this->expectCutoff !== false) {
            $this->setHeader('Expect', '100-Continue');
        }

        // Set the Content-Length header if it can be determined
        $size = $this->body->getContentLength();
        if ($size !== null && $size !== false) {
            $this->setHeader('Content-Length', $size);
            if ($size > $this->expectCutoff) {
                $this->setHeader('Expect', '100-Continue');
            }
        } elseif (!$this->hasHeader('Content-Length')) {
            if ('1.1' == $this->protocolVersion) {
                $this->setHeader('Transfer-Encoding', 'chunked');
            } else {
                throw new RequestException(
                    'Cannot determine Content-Length and cannot use chunked Transfer-Encoding when using HTTP/1.0'
                );
            }
        }

        return $this;
    }

    public function getBody()
    {
        return $this->body;
    }

    /**
     * Set the size that the entity body of the request must exceed before adding the Expect: 100-Continue header.
     *
     * @param int|bool $size Cutoff in bytes. Set to false to never send the expect header (even with non-seekable data)
     *
     * @return self
     */
    public function setExpectHeaderCutoff($size)
    {
        $this->expectCutoff = $size;
        if ($size === false || !$this->body) {
            $this->removeHeader('Expect');
        } elseif ($this->body && $this->body->getSize() && $this->body->getSize() > $size) {
            $this->setHeader('Expect', '100-Continue');
        }

        return $this;
    }

    public function configureRedirects($strict = false, $maxRedirects = 5)
    {
        $this->getParams()->set(RedirectPlugin::STRICT_REDIRECTS, $strict);
        if ($maxRedirects == 0) {
            $this->getParams()->set(RedirectPlugin::DISABLE, true);
        } else {
            $this->getParams()->set(RedirectPlugin::MAX_REDIRECTS, $maxRedirects);
        }

        return $this;
    }

    public function getPostField($field)
    {
        return $this->postFields->get($field);
    }

    public function getPostFields()
    {
        return $this->postFields;
    }

    public function setPostField($key, $value)
    {
        $this->postFields->set($key, $value);
        $this->processPostFields();

        return $this;
    }

    public function addPostFields($fields)
    {
        $this->postFields->merge($fields);
        $this->processPostFields();

        return $this;
    }

    public function removePostField($field)
    {
        $this->postFields->remove($field);
        $this->processPostFields();

        return $this;
    }

    public function getPostFiles()
    {
        return $this->postFiles;
    }

    public function getPostFile($fieldName)
    {
        return isset($this->postFiles[$fieldName]) ? $this->postFiles[$fieldName] : null;
    }

    public function removePostFile($fieldName)
    {
        unset($this->postFiles[$fieldName]);
        $this->processPostFields();

        return $this;
    }

    public function addPostFile($field, $filename = null, $contentType = null, $postname = null)
    {
        $data = null;

        if ($field instanceof PostFileInterface) {
            $data = $field;
        } elseif (is_array($filename)) {
            // Allow multiple values to be set in a single key
            foreach ($filename as $file) {
                $this->addPostFile($field, $file, $contentType);
            }
            return $this;
        } elseif (!is_string($filename)) {
            throw new RequestException('The path to a file must be a string');
        } elseif (!empty($filename)) {
            // Adding an empty file will cause cURL to error out
            $data = new PostFile($field, $filename, $contentType, $postname);
        }

        if ($data) {
            if (!isset($this->postFiles[$data->getFieldName()])) {
                $this->postFiles[$data->getFieldName()] = array($data);
            } else {
                $this->postFiles[$data->getFieldName()][] = $data;
            }
            $this->processPostFields();
        }

        return $this;
    }

    public function addPostFiles(array $files)
    {
        foreach ($files as $key => $file) {
            if ($file instanceof PostFileInterface) {
                $this->addPostFile($file, null, null, false);
            } elseif (is_string($file)) {
                // Convert non-associative array keys into 'file'
                if (is_numeric($key)) {
                    $key = 'file';
                }
                $this->addPostFile($key, $file, null, false);
            } else {
                throw new RequestException('File must be a string or instance of PostFileInterface');
            }
        }

        return $this;
    }

    /**
     * Determine what type of request should be sent based on post fields
     */
    protected function processPostFields()
    {
        if (!$this->postFiles) {
            $this->removeHeader('Expect')->setHeader('Content-Type', self::URL_ENCODED);
        } else {
            $this->setHeader('Content-Type', self::MULTIPART);
            if ($this->expectCutoff !== false) {
                $this->setHeader('Expect', '100-Continue');
            }
        }
    }
}
vendor/guzzle/guzzle/src/Guzzle/Http/Message/RequestFactoryInterface.php000064400000012570151327705700022601 0ustar00<?php

namespace Guzzle\Http\Message;

use Guzzle\Common\Collection;
use Guzzle\Http\EntityBodyInterface;
use Guzzle\Http\Url;

/**
 * Request factory used to create HTTP requests
 */
interface RequestFactoryInterface
{
    const OPTIONS_NONE = 0;
    const OPTIONS_AS_DEFAULTS = 1;

    /**
     * Create a new request based on an HTTP message
     *
     * @param string $message HTTP message as a string
     *
     * @return RequestInterface
     */
    public function fromMessage($message);

    /**
     * Create a request from URL parts as returned from parse_url()
     *
     * @param string $method HTTP method (GET, POST, PUT, HEAD, DELETE, etc)
     *
     * @param array $urlParts URL parts containing the same keys as parse_url()
     *     - scheme: e.g. http
     *     - host:   e.g. www.guzzle-project.com
     *     - port:   e.g. 80
     *     - user:   e.g. michael
     *     - pass:   e.g. rocks
     *     - path:   e.g. / OR /index.html
     *     - query:  after the question mark ?
     * @param array|Collection                          $headers         HTTP headers
     * @param string|resource|array|EntityBodyInterface $body            Body to send in the request
     * @param string                                    $protocol        Protocol (HTTP, SPYDY, etc)
     * @param string                                    $protocolVersion 1.0, 1.1, etc
     *
     * @return RequestInterface
     */
    public function fromParts(
        $method,
        array $urlParts,
        $headers = null,
        $body = null,
        $protocol = 'HTTP',
        $protocolVersion = '1.1'
    );

    /**
     * Create a new request based on the HTTP method
     *
     * @param string                                    $method  HTTP method (GET, POST, PUT, PATCH, HEAD, DELETE, ...)
     * @param string|Url                                $url     HTTP URL to connect to
     * @param array|Collection                          $headers HTTP headers
     * @param string|resource|array|EntityBodyInterface $body    Body to send in the request
     * @param array                                     $options Array of options to apply to the request
     *
     * @return RequestInterface
     */
    public function create($method, $url, $headers = null, $body = null, array $options = array());

    /**
     * Apply an associative array of options to the request
     *
     * @param RequestInterface $request Request to update
     * @param array            $options Options to use with the request. Available options are:
     *        "headers": Associative array of headers
     *        "query": Associative array of query string values to add to the request
     *        "body": Body of a request, including an EntityBody, string, or array when sending POST requests.
     *        "auth": Array of HTTP authentication parameters to use with the request. The array must contain the
     *            username in index [0], the password in index [2], and can optionally contain the authentication type
     *            in index [3]. The authentication types are: "Basic", "Digest", "NTLM", "Any" (defaults to "Basic").
     *        "cookies": Associative array of cookies
     *        "allow_redirects": Set to false to disable redirects
     *        "save_to": String, fopen resource, or EntityBody object used to store the body of the response
     *        "events": Associative array mapping event names to a closure or array of (priority, closure)
     *        "plugins": Array of plugins to add to the request
     *        "exceptions": Set to false to disable throwing exceptions on an HTTP level error (e.g. 404, 500, etc)
     *        "params": Set custom request data parameters on a request. (Note: these are not query string parameters)
     *        "timeout": Float describing the timeout of the request in seconds
     *        "connect_timeout": Float describing the number of seconds to wait while trying to connect. Use 0 to wait
     *            indefinitely.
     *        "verify": Set to true to enable SSL cert validation (the default), false to disable, or supply the path
     *            to a CA bundle to enable verification using a custom certificate.
     *        "cert": Set to a string to specify the path to a file containing a PEM formatted certificate. If a
     *            password is required, then set an array containing the path to the PEM file followed by the the
     *            password required for the certificate.
     *        "ssl_key": Specify the path to a file containing a private SSL key in PEM format. If a password is
     *            required, then set an array containing the path to the SSL key followed by the password required for
     *            the certificate.
     *        "proxy": Specify an HTTP proxy (e.g. "http://username:password@192.168.16.1:10")
     *        "debug": Set to true to display all data sent over the wire
     * @param int $flags Bitwise flags to apply when applying the options to the request. Defaults to no special
     *                   options. `1` (OPTIONS_AS_DEFAULTS): When specified, options will only update a request when
     *                   the value does not already exist on the request. This is only supported by "query" and
     *                   "headers". Other bitwise options may be added in the future.
     */
    public function applyOptions(RequestInterface $request, array $options = array(), $flags = self::OPTIONS_NONE);
}
vendor/guzzle/guzzle/src/Guzzle/Http/Message/Header.php000064400000010364151327705700017167 0ustar00<?php

namespace Guzzle\Http\Message;

use Guzzle\Common\Version;
use Guzzle\Http\Message\Header\HeaderInterface;

/**
 * Represents a header and all of the values stored by that header
 */
class Header implements HeaderInterface
{
    protected $values = array();
    protected $header;
    protected $glue;

    /**
     * @param string       $header Name of the header
     * @param array|string $values Values of the header as an array or a scalar
     * @param string       $glue   Glue used to combine multiple values into a string
     */
    public function __construct($header, $values = array(), $glue = ',')
    {
        $this->header = trim($header);
        $this->glue = $glue;

        foreach ((array) $values as $value) {
            foreach ((array) $value as $v) {
                $this->values[] = $v;
            }
        }
    }

    public function __toString()
    {
        return implode($this->glue . ' ', $this->toArray());
    }

    public function add($value)
    {
        $this->values[] = $value;

        return $this;
    }

    public function getName()
    {
        return $this->header;
    }

    public function setName($name)
    {
        $this->header = $name;

        return $this;
    }

    public function setGlue($glue)
    {
        $this->glue = $glue;

        return $this;
    }

    public function getGlue()
    {
        return $this->glue;
    }

    /**
     * Normalize the header to be a single header with an array of values.
     *
     * If any values of the header contains the glue string value (e.g. ","), then the value will be exploded into
     * multiple entries in the header.
     *
     * @return self
     */
    public function normalize()
    {
        $values = $this->toArray();

        for ($i = 0, $total = count($values); $i < $total; $i++) {
            if (strpos($values[$i], $this->glue) !== false) {
                // Explode on glue when the glue is not inside of a comma
                foreach (preg_split('/' . preg_quote($this->glue) . '(?=([^"]*"[^"]*")*[^"]*$)/', $values[$i]) as $v) {
                    $values[] = trim($v);
                }
                unset($values[$i]);
            }
        }

        $this->values = array_values($values);

        return $this;
    }

    public function hasValue($searchValue)
    {
        return in_array($searchValue, $this->toArray());
    }

    public function removeValue($searchValue)
    {
        $this->values = array_values(array_filter($this->values, function ($value) use ($searchValue) {
            return $value != $searchValue;
        }));

        return $this;
    }

    public function toArray()
    {
        return $this->values;
    }

    public function count()
    {
        return count($this->toArray());
    }

    public function getIterator()
    {
        return new \ArrayIterator($this->toArray());
    }

    public function parseParams()
    {
        $params = $matches = array();
        $callback = array($this, 'trimHeader');

        // Normalize the header into a single array and iterate over all values
        foreach ($this->normalize()->toArray() as $val) {
            $part = array();
            foreach (preg_split('/;(?=([^"]*"[^"]*")*[^"]*$)/', $val) as $kvp) {
                if (!preg_match_all('/<[^>]+>|[^=]+/', $kvp, $matches)) {
                    continue;
                }
                $pieces = array_map($callback, $matches[0]);
                $part[$pieces[0]] = isset($pieces[1]) ? $pieces[1] : '';
            }
            if ($part) {
                $params[] = $part;
            }
        }

        return $params;
    }

    /**
     * @deprecated
     * @codeCoverageIgnore
     */
    public function hasExactHeader($header)
    {
        Version::warn(__METHOD__ . ' is deprecated');
        return $this->header == $header;
    }

    /**
     * @deprecated
     * @codeCoverageIgnore
     */
    public function raw()
    {
        Version::warn(__METHOD__ . ' is deprecated. Use toArray()');
        return $this->toArray();
    }

    /**
     * Trim a header by removing excess spaces and wrapping quotes
     *
     * @param $str
     *
     * @return string
     */
    protected function trimHeader($str)
    {
        static $trimmed = "\"'  \n\t";

        return trim($str, $trimmed);
    }
}
vendor/guzzle/guzzle/src/Guzzle/Http/Message/RequestInterface.php000064400000020467151327705700021255 0ustar00<?php

namespace Guzzle\Http\Message;

use Guzzle\Common\Collection;
use Guzzle\Common\HasDispatcherInterface;
use Guzzle\Http\Exception\RequestException;
use Guzzle\Http\ClientInterface;
use Guzzle\Http\EntityBodyInterface;
use Guzzle\Http\Url;
use Guzzle\Http\QueryString;

/**
 * Generic HTTP request interface
 */
interface RequestInterface extends MessageInterface, HasDispatcherInterface
{
    const STATE_NEW = 'new';
    const STATE_COMPLETE = 'complete';
    const STATE_TRANSFER = 'transfer';
    const STATE_ERROR = 'error';

    const GET = 'GET';
    const PUT = 'PUT';
    const POST = 'POST';
    const DELETE = 'DELETE';
    const HEAD = 'HEAD';
    const CONNECT = 'CONNECT';
    const OPTIONS = 'OPTIONS';
    const TRACE = 'TRACE';
    const PATCH = 'PATCH';

    /**
     * @return string
     */
    public function __toString();

    /**
     * Send the request
     *
     * @return Response
     * @throws RequestException on a request error
     */
    public function send();

    /**
     * Set the client used to transport the request
     *
     * @param ClientInterface $client
     *
     * @return self
     */
    public function setClient(ClientInterface $client);

    /**
     * Get the client used to transport the request
     *
     * @return ClientInterface $client
     */
    public function getClient();

    /**
     * Set the URL of the request
     *
     * @param string $url|Url Full URL to set including query string
     *
     * @return self
     */
    public function setUrl($url);

    /**
     * Get the full URL of the request (e.g. 'http://www.guzzle-project.com/')
     *
     * @param bool $asObject Set to TRUE to retrieve the URL as a clone of the URL object owned by the request.
     *
     * @return string|Url
     */
    public function getUrl($asObject = false);

    /**
     * Get the resource part of the the request, including the path, query string, and fragment
     *
     * @return string
     */
    public function getResource();

    /**
     * Get the collection of key value pairs that will be used as the query string in the request
     *
     * @return QueryString
     */
    public function getQuery();

    /**
     * Get the HTTP method of the request
     *
     * @return string
     */
    public function getMethod();

    /**
     * Get the URI scheme of the request (http, https, ftp, etc)
     *
     * @return string
     */
    public function getScheme();

    /**
     * Set the URI scheme of the request (http, https, ftp, etc)
     *
     * @param string $scheme Scheme to set
     *
     * @return self
     */
    public function setScheme($scheme);

    /**
     * Get the host of the request
     *
     * @return string
     */
    public function getHost();

    /**
     * Set the host of the request. Including a port in the host will modify the port of the request.
     *
     * @param string $host Host to set (e.g. www.yahoo.com, www.yahoo.com:80)
     *
     * @return self
     */
    public function setHost($host);

    /**
     * Get the path of the request (e.g. '/', '/index.html')
     *
     * @return string
     */
    public function getPath();

    /**
     * Set the path of the request (e.g. '/', '/index.html')
     *
     * @param string|array $path Path to set or array of segments to implode
     *
     * @return self
     */
    public function setPath($path);

    /**
     * Get the port that the request will be sent on if it has been set
     *
     * @return int|null
     */
    public function getPort();

    /**
     * Set the port that the request will be sent on
     *
     * @param int $port Port number to set
     *
     * @return self
     */
    public function setPort($port);

    /**
     * Get the username to pass in the URL if set
     *
     * @return string|null
     */
    public function getUsername();

    /**
     * Get the password to pass in the URL if set
     *
     * @return string|null
     */
    public function getPassword();

    /**
     * Set HTTP authorization parameters
     *
     * @param string|bool $user     User name or false disable authentication
     * @param string      $password Password
     * @param string      $scheme   Authentication scheme ('Basic', 'Digest', or a CURLAUTH_* constant (deprecated))
     *
     * @return self
     * @link http://www.ietf.org/rfc/rfc2617.txt
     * @link http://php.net/manual/en/function.curl-setopt.php See the available options for CURLOPT_HTTPAUTH
     * @throws RequestException
     */
    public function setAuth($user, $password = '', $scheme = 'Basic');

    /**
     * Get the HTTP protocol version of the request
     *
     * @return string
     */
    public function getProtocolVersion();

    /**
     * Set the HTTP protocol version of the request (e.g. 1.1 or 1.0)
     *
     * @param string $protocol HTTP protocol version to use with the request
     *
     * @return self
     */
    public function setProtocolVersion($protocol);

    /**
     * Get the previously received {@see Response} or NULL if the request has not been sent
     *
     * @return Response|null
     */
    public function getResponse();

    /**
     * Manually set a response for the request.
     *
     * This method is useful for specifying a mock response for the request or setting the response using a cache.
     * Manually setting a response will bypass the actual sending of a request.
     *
     * @param Response $response Response object to set
     * @param bool     $queued   Set to TRUE to keep the request in a state of not having been sent, but queue the
     *                           response for send()
     *
     * @return self Returns a reference to the object.
     */
    public function setResponse(Response $response, $queued = false);

    /**
     * The start of a response has been received for a request and the request is still in progress
     *
     * @param Response $response Response that has been received so far
     *
     * @return self
     */
    public function startResponse(Response $response);

    /**
     * Set the EntityBody that will hold a successful response message's entity body.
     *
     * This method should be invoked when you need to send the response's entity body somewhere other than the normal
     * php://temp buffer. For example, you can send the entity body to a socket, file, or some other custom stream.
     *
     * @param EntityBodyInterface|string|resource $body Response body object. Pass a string to attempt to store the
     *                                                  response body in a local file.
     * @return Request
     */
    public function setResponseBody($body);

    /**
     * Get the EntityBody that will hold the resulting response message's entity body. This response body will only
     * be used for successful responses. Intermediate responses (e.g. redirects) will not use the targeted response
     * body.
     *
     * @return EntityBodyInterface
     */
    public function getResponseBody();

    /**
     * Get the state of the request. One of 'complete', 'transfer', 'new', 'error'
     *
     * @return string
     */
    public function getState();

    /**
     * Set the state of the request
     *
     * @param string $state   State of the request ('complete', 'transfer', 'new', 'error')
     * @param array  $context Contextual information about the state change
     *
     * @return string Returns the current state of the request (which may have changed due to events being fired)
     */
    public function setState($state, array $context = array());

    /**
     * Get the cURL options that will be applied when the cURL handle is created
     *
     * @return Collection
     */
    public function getCurlOptions();

    /**
     * Get an array of Cookies
     *
     * @return array
     */
    public function getCookies();

    /**
     * Get a cookie value by name
     *
     * @param string $name Cookie to retrieve
     *
     * @return null|string
     */
    public function getCookie($name);

    /**
     * Add a Cookie value by name to the Cookie header
     *
     * @param string $name  Name of the cookie to add
     * @param string $value Value to set
     *
     * @return self
     */
    public function addCookie($name, $value);

    /**
     * Remove a specific cookie value by name
     *
     * @param string $name Cookie to remove by name
     *
     * @return self
     */
    public function removeCookie($name);
}
vendor/guzzle/guzzle/src/Guzzle/Http/Message/PostFile.php000064400000005662151327705700017531 0ustar00<?php

namespace Guzzle\Http\Message;

use Guzzle\Common\Version;
use Guzzle\Common\Exception\InvalidArgumentException;
use Guzzle\Http\Mimetypes;

/**
 * POST file upload
 */
class PostFile implements PostFileInterface
{
    protected $fieldName;
    protected $contentType;
    protected $filename;
    protected $postname;

    /**
     * @param string $fieldName   Name of the field
     * @param string $filename    Local path to the file
     * @param string $postname    Remote post file name
     * @param string $contentType Content-Type of the upload
     */
    public function __construct($fieldName, $filename, $contentType = null, $postname = null)
    {
        $this->fieldName = $fieldName;
        $this->setFilename($filename);
        $this->postname = $postname ? $postname : basename($filename);
        $this->contentType = $contentType ?: $this->guessContentType();
    }

    public function setFieldName($name)
    {
        $this->fieldName = $name;

        return $this;
    }

    public function getFieldName()
    {
        return $this->fieldName;
    }

    public function setFilename($filename)
    {
        // Remove leading @ symbol
        if (strpos($filename, '@') === 0) {
            $filename = substr($filename, 1);
        }

        if (!is_readable($filename)) {
            throw new InvalidArgumentException("Unable to open {$filename} for reading");
        }

        $this->filename = $filename;

        return $this;
    }

    public function setPostname($postname)
    {
        $this->postname = $postname;

        return $this;
    }

    public function getFilename()
    {
        return $this->filename;
    }

    public function getPostname()
    {
        return $this->postname;
    }

    public function setContentType($type)
    {
        $this->contentType = $type;

        return $this;
    }

    public function getContentType()
    {
        return $this->contentType;
    }

    public function getCurlValue()
    {
        // PHP 5.5 introduced a CurlFile object that deprecates the old @filename syntax
        // See: https://wiki.php.net/rfc/curl-file-upload
        if (function_exists('curl_file_create')) {
            return curl_file_create($this->filename, $this->contentType, $this->postname);
        }

        // Use the old style if using an older version of PHP
        $value = "@{$this->filename};filename=" . $this->postname;
        if ($this->contentType) {
            $value .= ';type=' . $this->contentType;
        }

        return $value;
    }

    /**
     * @deprecated
     * @codeCoverageIgnore
     */
    public function getCurlString()
    {
        Version::warn(__METHOD__ . ' is deprecated. Use getCurlValue()');
        return $this->getCurlValue();
    }

    /**
     * Determine the Content-Type of the file
     */
    protected function guessContentType()
    {
        return Mimetypes::getInstance()->fromFilename($this->filename) ?: 'application/octet-stream';
    }
}
vendor/guzzle/guzzle/src/Guzzle/Http/Message/AbstractMessage.php000064400000013043151327705700021044 0ustar00<?php

namespace Guzzle\Http\Message;

use Guzzle\Common\Version;
use Guzzle\Common\Collection;
use Guzzle\Http\Message\Header\HeaderCollection;
use Guzzle\Http\Message\Header\HeaderFactory;
use Guzzle\Http\Message\Header\HeaderFactoryInterface;
use Guzzle\Http\Message\Header\HeaderInterface;

/**
 * Abstract HTTP request/response message
 */
abstract class AbstractMessage implements MessageInterface
{
    /** @var array HTTP header collection */
    protected $headers;

    /** @var HeaderFactoryInterface $headerFactory */
    protected $headerFactory;

    /** @var Collection Custom message parameters that are extendable by plugins */
    protected $params;

    /** @var string Message protocol */
    protected $protocol = 'HTTP';

    /** @var string HTTP protocol version of the message */
    protected $protocolVersion = '1.1';

    public function __construct()
    {
        $this->params = new Collection();
        $this->headerFactory = new HeaderFactory();
        $this->headers = new HeaderCollection();
    }

    /**
     * Set the header factory to use to create headers
     *
     * @param HeaderFactoryInterface $factory
     *
     * @return self
     */
    public function setHeaderFactory(HeaderFactoryInterface $factory)
    {
        $this->headerFactory = $factory;

        return $this;
    }

    public function getParams()
    {
        return $this->params;
    }

    public function addHeader($header, $value)
    {
        if (isset($this->headers[$header])) {
            $this->headers[$header]->add($value);
        } elseif ($value instanceof HeaderInterface) {
            $this->headers[$header] = $value;
        } else {
            $this->headers[$header] = $this->headerFactory->createHeader($header, $value);
        }

        return $this;
    }

    public function addHeaders(array $headers)
    {
        foreach ($headers as $key => $value) {
            $this->addHeader($key, $value);
        }

        return $this;
    }

    public function getHeader($header)
    {
        return $this->headers[$header];
    }

    public function getHeaders()
    {
        return $this->headers;
    }

    public function getHeaderLines()
    {
        $headers = array();
        foreach ($this->headers as $value) {
            $headers[] = $value->getName() . ': ' . $value;
        }

        return $headers;
    }

    public function setHeader($header, $value)
    {
        unset($this->headers[$header]);
        $this->addHeader($header, $value);

        return $this;
    }

    public function setHeaders(array $headers)
    {
        $this->headers->clear();
        foreach ($headers as $key => $value) {
            $this->addHeader($key, $value);
        }

        return $this;
    }

    public function hasHeader($header)
    {
        return isset($this->headers[$header]);
    }

    public function removeHeader($header)
    {
        unset($this->headers[$header]);

        return $this;
    }

    /**
     * @deprecated Use $message->getHeader()->parseParams()
     * @codeCoverageIgnore
     */
    public function getTokenizedHeader($header, $token = ';')
    {
        Version::warn(__METHOD__ . ' is deprecated. Use $message->getHeader()->parseParams()');
        if ($this->hasHeader($header)) {
            $data = new Collection();
            foreach ($this->getHeader($header)->parseParams() as $values) {
                foreach ($values as $key => $value) {
                    if ($value === '') {
                        $data->set($data->count(), $key);
                    } else {
                        $data->add($key, $value);
                    }
                }
            }
            return $data;
        }
    }

    /**
     * @deprecated
     * @codeCoverageIgnore
     */
    public function setTokenizedHeader($header, $data, $token = ';')
    {
        Version::warn(__METHOD__ . ' is deprecated.');
        return $this;
    }

    /**
     * @deprecated
     * @codeCoverageIgnore
     */
    public function getCacheControlDirective($directive)
    {
        Version::warn(__METHOD__ . ' is deprecated. Use $message->getHeader(\'Cache-Control\')->getDirective()');
        if (!($header = $this->getHeader('Cache-Control'))) {
            return null;
        }

        return $header->getDirective($directive);
    }

    /**
     * @deprecated
     * @codeCoverageIgnore
     */
    public function hasCacheControlDirective($directive)
    {
        Version::warn(__METHOD__ . ' is deprecated. Use $message->getHeader(\'Cache-Control\')->hasDirective()');
        if ($header = $this->getHeader('Cache-Control')) {
            return $header->hasDirective($directive);
        } else {
            return false;
        }
    }

    /**
     * @deprecated
     * @codeCoverageIgnore
     */
    public function addCacheControlDirective($directive, $value = true)
    {
        Version::warn(__METHOD__ . ' is deprecated. Use $message->getHeader(\'Cache-Control\')->addDirective()');
        if (!($header = $this->getHeader('Cache-Control'))) {
            $this->addHeader('Cache-Control', '');
            $header = $this->getHeader('Cache-Control');
        }

        $header->addDirective($directive, $value);

        return $this;
    }

    /**
     * @deprecated
     * @codeCoverageIgnore
     */
    public function removeCacheControlDirective($directive)
    {
        Version::warn(__METHOD__ . ' is deprecated. Use $message->getHeader(\'Cache-Control\')->removeDirective()');
        if ($header = $this->getHeader('Cache-Control')) {
            $header->removeDirective($directive);
        }

        return $this;
    }
}
vendor/guzzle/guzzle/src/Guzzle/Http/Message/Response.php000064400000063463151327705700017605 0ustar00<?php

namespace Guzzle\Http\Message;

use Guzzle\Common\Version;
use Guzzle\Common\ToArrayInterface;
use Guzzle\Common\Exception\RuntimeException;
use Guzzle\Http\EntityBodyInterface;
use Guzzle\Http\EntityBody;
use Guzzle\Http\Exception\BadResponseException;
use Guzzle\Http\RedirectPlugin;
use Guzzle\Parser\ParserRegistry;

/**
 * Guzzle HTTP response object
 */
class Response extends AbstractMessage implements \Serializable
{
    /**
     * @var array Array of reason phrases and their corresponding status codes
     */
    private static $statusTexts = array(
        100 => 'Continue',
        101 => 'Switching Protocols',
        102 => 'Processing',
        200 => 'OK',
        201 => 'Created',
        202 => 'Accepted',
        203 => 'Non-Authoritative Information',
        204 => 'No Content',
        205 => 'Reset Content',
        206 => 'Partial Content',
        207 => 'Multi-Status',
        208 => 'Already Reported',
        226 => 'IM Used',
        300 => 'Multiple Choices',
        301 => 'Moved Permanently',
        302 => 'Found',
        303 => 'See Other',
        304 => 'Not Modified',
        305 => 'Use Proxy',
        307 => 'Temporary Redirect',
        308 => 'Permanent Redirect',
        400 => 'Bad Request',
        401 => 'Unauthorized',
        402 => 'Payment Required',
        403 => 'Forbidden',
        404 => 'Not Found',
        405 => 'Method Not Allowed',
        406 => 'Not Acceptable',
        407 => 'Proxy Authentication Required',
        408 => 'Request Timeout',
        409 => 'Conflict',
        410 => 'Gone',
        411 => 'Length Required',
        412 => 'Precondition Failed',
        413 => 'Request Entity Too Large',
        414 => 'Request-URI Too Long',
        415 => 'Unsupported Media Type',
        416 => 'Requested Range Not Satisfiable',
        417 => 'Expectation Failed',
        422 => 'Unprocessable Entity',
        423 => 'Locked',
        424 => 'Failed Dependency',
        425 => 'Reserved for WebDAV advanced collections expired proposal',
        426 => 'Upgrade required',
        428 => 'Precondition Required',
        429 => 'Too Many Requests',
        431 => 'Request Header Fields Too Large',
        500 => 'Internal Server Error',
        501 => 'Not Implemented',
        502 => 'Bad Gateway',
        503 => 'Service Unavailable',
        504 => 'Gateway Timeout',
        505 => 'HTTP Version Not Supported',
        506 => 'Variant Also Negotiates (Experimental)',
        507 => 'Insufficient Storage',
        508 => 'Loop Detected',
        510 => 'Not Extended',
        511 => 'Network Authentication Required',
    );

    /** @var EntityBodyInterface The response body */
    protected $body;

    /** @var string The reason phrase of the response (human readable code) */
    protected $reasonPhrase;

    /** @var string The status code of the response */
    protected $statusCode;

    /** @var array Information about the request */
    protected $info = array();

    /** @var string The effective URL that returned this response */
    protected $effectiveUrl;

    /** @var array Cacheable response codes (see RFC 2616:13.4) */
    protected static $cacheResponseCodes = array(200, 203, 206, 300, 301, 410);

    /**
     * Create a new Response based on a raw response message
     *
     * @param string $message Response message
     *
     * @return self|bool Returns false on error
     */
    public static function fromMessage($message)
    {
        $data = ParserRegistry::getInstance()->getParser('message')->parseResponse($message);
        if (!$data) {
            return false;
        }

        $response = new static($data['code'], $data['headers'], $data['body']);
        $response->setProtocol($data['protocol'], $data['version'])
                 ->setStatus($data['code'], $data['reason_phrase']);

        // Set the appropriate Content-Length if the one set is inaccurate (e.g. setting to X)
        $contentLength = (string) $response->getHeader('Content-Length');
        $actualLength = strlen($data['body']);
        if (strlen($data['body']) > 0 && $contentLength != $actualLength) {
            $response->setHeader('Content-Length', $actualLength);
        }

        return $response;
    }

    /**
     * Construct the response
     *
     * @param string                              $statusCode The response status code (e.g. 200, 404, etc)
     * @param ToArrayInterface|array              $headers    The response headers
     * @param string|resource|EntityBodyInterface $body       The body of the response
     *
     * @throws BadResponseException if an invalid response code is given
     */
    public function __construct($statusCode, $headers = null, $body = null)
    {
        parent::__construct();
        $this->setStatus($statusCode);
        $this->body = EntityBody::factory($body !== null ? $body : '');

        if ($headers) {
            if (is_array($headers)) {
                $this->setHeaders($headers);
            } elseif ($headers instanceof ToArrayInterface) {
                $this->setHeaders($headers->toArray());
            } else {
                throw new BadResponseException('Invalid headers argument received');
            }
        }
    }

    /**
     * @return string
     */
    public function __toString()
    {
        return $this->getMessage();
    }

    public function serialize()
    {
        return json_encode(array(
            'status'  => $this->statusCode,
            'body'    => (string) $this->body,
            'headers' => $this->headers->toArray()
        ));
    }

    public function unserialize($serialize)
    {
        $data = json_decode($serialize, true);
        $this->__construct($data['status'], $data['headers'], $data['body']);
    }

    /**
     * Get the response entity body
     *
     * @param bool $asString Set to TRUE to return a string of the body rather than a full body object
     *
     * @return EntityBodyInterface|string
     */
    public function getBody($asString = false)
    {
        return $asString ? (string) $this->body : $this->body;
    }

    /**
     * Set the response entity body
     *
     * @param EntityBodyInterface|string $body Body to set
     *
     * @return self
     */
    public function setBody($body)
    {
        $this->body = EntityBody::factory($body);

        return $this;
    }

    /**
     * Set the protocol and protocol version of the response
     *
     * @param string $protocol Response protocol
     * @param string $version  Protocol version
     *
     * @return self
     */
    public function setProtocol($protocol, $version)
    {
        $this->protocol = $protocol;
        $this->protocolVersion = $version;

        return $this;
    }

    /**
     * Get the protocol used for the response (e.g. HTTP)
     *
     * @return string
     */
    public function getProtocol()
    {
        return $this->protocol;
    }

    /**
     * Get the HTTP protocol version
     *
     * @return string
     */
    public function getProtocolVersion()
    {
        return $this->protocolVersion;
    }

    /**
     * Get a cURL transfer information
     *
     * @param string $key A single statistic to check
     *
     * @return array|string|null Returns all stats if no key is set, a single stat if a key is set, or null if a key
     *                           is set and not found
     * @link http://www.php.net/manual/en/function.curl-getinfo.php
     */
    public function getInfo($key = null)
    {
        if ($key === null) {
            return $this->info;
        } elseif (array_key_exists($key, $this->info)) {
            return $this->info[$key];
        } else {
            return null;
        }
    }

    /**
     * Set the transfer information
     *
     * @param array $info Array of cURL transfer stats
     *
     * @return self
     */
    public function setInfo(array $info)
    {
        $this->info = $info;

        return $this;
    }

    /**
     * Set the response status
     *
     * @param int    $statusCode   Response status code to set
     * @param string $reasonPhrase Response reason phrase
     *
     * @return self
     * @throws BadResponseException when an invalid response code is received
     */
    public function setStatus($statusCode, $reasonPhrase = '')
    {
        $this->statusCode = (int) $statusCode;

        if (!$reasonPhrase && isset(self::$statusTexts[$this->statusCode])) {
            $this->reasonPhrase = self::$statusTexts[$this->statusCode];
        } else {
            $this->reasonPhrase = $reasonPhrase;
        }

        return $this;
    }

    /**
     * Get the response status code
     *
     * @return integer
     */
    public function getStatusCode()
    {
        return $this->statusCode;
    }

    /**
     * Get the entire response as a string
     *
     * @return string
     */
    public function getMessage()
    {
        $message = $this->getRawHeaders();

        // Only include the body in the message if the size is < 2MB
        $size = $this->body->getSize();
        if ($size < 2097152) {
            $message .= (string) $this->body;
        }

        return $message;
    }

    /**
     * Get the the raw message headers as a string
     *
     * @return string
     */
    public function getRawHeaders()
    {
        $headers = 'HTTP/1.1 ' . $this->statusCode . ' ' . $this->reasonPhrase . "\r\n";
        $lines = $this->getHeaderLines();
        if (!empty($lines)) {
            $headers .= implode("\r\n", $lines) . "\r\n";
        }

        return $headers . "\r\n";
    }

    /**
     * Get the response reason phrase- a human readable version of the numeric
     * status code
     *
     * @return string
     */
    public function getReasonPhrase()
    {
        return $this->reasonPhrase;
    }

    /**
     * Get the Accept-Ranges HTTP header
     *
     * @return string Returns what partial content range types this server supports.
     */
    public function getAcceptRanges()
    {
        return (string) $this->getHeader('Accept-Ranges');
    }

    /**
     * Calculate the age of the response
     *
     * @return integer
     */
    public function calculateAge()
    {
        $age = $this->getHeader('Age');

        if ($age === null && $this->getDate()) {
            $age = time() - strtotime($this->getDate());
        }

        return $age === null ? null : (int) (string) $age;
    }

    /**
     * Get the Age HTTP header
     *
     * @return integer|null Returns the age the object has been in a proxy cache in seconds.
     */
    public function getAge()
    {
        return (string) $this->getHeader('Age');
    }

    /**
     * Get the Allow HTTP header
     *
     * @return string|null Returns valid actions for a specified resource. To be used for a 405 Method not allowed.
     */
    public function getAllow()
    {
        return (string) $this->getHeader('Allow');
    }

    /**
     * Check if an HTTP method is allowed by checking the Allow response header
     *
     * @param string $method Method to check
     *
     * @return bool
     */
    public function isMethodAllowed($method)
    {
        $allow = $this->getHeader('Allow');
        if ($allow) {
            foreach (explode(',', $allow) as $allowable) {
                if (!strcasecmp(trim($allowable), $method)) {
                    return true;
                }
            }
        }

        return false;
    }

    /**
     * Get the Cache-Control HTTP header
     *
     * @return string
     */
    public function getCacheControl()
    {
        return (string) $this->getHeader('Cache-Control');
    }

    /**
     * Get the Connection HTTP header
     *
     * @return string
     */
    public function getConnection()
    {
        return (string) $this->getHeader('Connection');
    }

    /**
     * Get the Content-Encoding HTTP header
     *
     * @return string|null
     */
    public function getContentEncoding()
    {
        return (string) $this->getHeader('Content-Encoding');
    }

    /**
     * Get the Content-Language HTTP header
     *
     * @return string|null Returns the language the content is in.
     */
    public function getContentLanguage()
    {
        return (string) $this->getHeader('Content-Language');
    }

    /**
     * Get the Content-Length HTTP header
     *
     * @return integer Returns the length of the response body in bytes
     */
    public function getContentLength()
    {
        return (int) (string) $this->getHeader('Content-Length');
    }

    /**
     * Get the Content-Location HTTP header
     *
     * @return string|null Returns an alternate location for the returned data (e.g /index.htm)
     */
    public function getContentLocation()
    {
        return (string) $this->getHeader('Content-Location');
    }

    /**
     * Get the Content-Disposition HTTP header
     *
     * @return string|null Returns the Content-Disposition header
     */
    public function getContentDisposition()
    {
        return (string) $this->getHeader('Content-Disposition');
    }

    /**
     * Get the Content-MD5 HTTP header
     *
     * @return string|null Returns a Base64-encoded binary MD5 sum of the content of the response.
     */
    public function getContentMd5()
    {
        return (string) $this->getHeader('Content-MD5');
    }

    /**
     * Get the Content-Range HTTP header
     *
     * @return string Returns where in a full body message this partial message belongs (e.g. bytes 21010-47021/47022).
     */
    public function getContentRange()
    {
        return (string) $this->getHeader('Content-Range');
    }

    /**
     * Get the Content-Type HTTP header
     *
     * @return string Returns the mime type of this content.
     */
    public function getContentType()
    {
        return (string) $this->getHeader('Content-Type');
    }

    /**
     * Checks if the Content-Type is of a certain type.  This is useful if the
     * Content-Type header contains charset information and you need to know if
     * the Content-Type matches a particular type.
     *
     * @param string $type Content type to check against
     *
     * @return bool
     */
    public function isContentType($type)
    {
        return stripos($this->getHeader('Content-Type'), $type) !== false;
    }

    /**
     * Get the Date HTTP header
     *
     * @return string|null Returns the date and time that the message was sent.
     */
    public function getDate()
    {
        return (string) $this->getHeader('Date');
    }

    /**
     * Get the ETag HTTP header
     *
     * @return string|null Returns an identifier for a specific version of a resource, often a Message digest.
     */
    public function getEtag()
    {
        return (string) $this->getHeader('ETag');
    }

    /**
     * Get the Expires HTTP header
     *
     * @return string|null Returns the date/time after which the response is considered stale.
     */
    public function getExpires()
    {
        return (string) $this->getHeader('Expires');
    }

    /**
     * Get the Last-Modified HTTP header
     *
     * @return string|null Returns the last modified date for the requested object, in RFC 2822 format
     *                     (e.g. Tue, 15 Nov 1994 12:45:26 GMT)
     */
    public function getLastModified()
    {
        return (string) $this->getHeader('Last-Modified');
    }

    /**
     * Get the Location HTTP header
     *
     * @return string|null Used in redirection, or when a new resource has been created.
     */
    public function getLocation()
    {
        return (string) $this->getHeader('Location');
    }

    /**
     * Get the Pragma HTTP header
     *
     * @return Header|null Returns the implementation-specific headers that may have various effects anywhere along
     *                     the request-response chain.
     */
    public function getPragma()
    {
        return (string) $this->getHeader('Pragma');
    }

    /**
     * Get the Proxy-Authenticate HTTP header
     *
     * @return string|null Authentication to access the proxy (e.g. Basic)
     */
    public function getProxyAuthenticate()
    {
        return (string) $this->getHeader('Proxy-Authenticate');
    }

    /**
     * Get the Retry-After HTTP header
     *
     * @return int|null If an entity is temporarily unavailable, this instructs the client to try again after a
     *                  specified period of time.
     */
    public function getRetryAfter()
    {
        return (string) $this->getHeader('Retry-After');
    }

    /**
     * Get the Server HTTP header
     *
     * @return string|null A name for the server
     */
    public function getServer()
    {
        return (string)  $this->getHeader('Server');
    }

    /**
     * Get the Set-Cookie HTTP header
     *
     * @return string|null An HTTP cookie.
     */
    public function getSetCookie()
    {
        return (string) $this->getHeader('Set-Cookie');
    }

    /**
     * Get the Trailer HTTP header
     *
     * @return string|null The Trailer general field value indicates that the given set of header fields is present in
     *                     the trailer of a message encoded with chunked transfer-coding.
     */
    public function getTrailer()
    {
        return (string) $this->getHeader('Trailer');
    }

    /**
     * Get the Transfer-Encoding HTTP header
     *
     * @return string|null The form of encoding used to safely transfer the entity to the user
     */
    public function getTransferEncoding()
    {
        return (string) $this->getHeader('Transfer-Encoding');
    }

    /**
     * Get the Vary HTTP header
     *
     * @return string|null Tells downstream proxies how to match future request headers to decide whether the cached
     *                     response can be used rather than requesting a fresh one from the origin server.
     */
    public function getVary()
    {
        return (string) $this->getHeader('Vary');
    }

    /**
     * Get the Via HTTP header
     *
     * @return string|null Informs the client of proxies through which the response was sent.
     */
    public function getVia()
    {
        return (string) $this->getHeader('Via');
    }

    /**
     * Get the Warning HTTP header
     *
     * @return string|null A general warning about possible problems with the entity body
     */
    public function getWarning()
    {
        return (string) $this->getHeader('Warning');
    }

    /**
     * Get the WWW-Authenticate HTTP header
     *
     * @return string|null Indicates the authentication scheme that should be used to access the requested entity
     */
    public function getWwwAuthenticate()
    {
        return (string) $this->getHeader('WWW-Authenticate');
    }

    /**
     * Checks if HTTP Status code is a Client Error (4xx)
     *
     * @return bool
     */
    public function isClientError()
    {
        return $this->statusCode >= 400 && $this->statusCode < 500;
    }

    /**
     * Checks if HTTP Status code is Server OR Client Error (4xx or 5xx)
     *
     * @return boolean
     */
    public function isError()
    {
        return $this->isClientError() || $this->isServerError();
    }

    /**
     * Checks if HTTP Status code is Information (1xx)
     *
     * @return bool
     */
    public function isInformational()
    {
        return $this->statusCode < 200;
    }

    /**
     * Checks if HTTP Status code is a Redirect (3xx)
     *
     * @return bool
     */
    public function isRedirect()
    {
        return $this->statusCode >= 300 && $this->statusCode < 400;
    }

    /**
     * Checks if HTTP Status code is Server Error (5xx)
     *
     * @return bool
     */
    public function isServerError()
    {
        return $this->statusCode >= 500 && $this->statusCode < 600;
    }

    /**
     * Checks if HTTP Status code is Successful (2xx | 304)
     *
     * @return bool
     */
    public function isSuccessful()
    {
        return ($this->statusCode >= 200 && $this->statusCode < 300) || $this->statusCode == 304;
    }

    /**
     * Check if the response can be cached based on the response headers
     *
     * @return bool Returns TRUE if the response can be cached or false if not
     */
    public function canCache()
    {
        // Check if the response is cacheable based on the code
        if (!in_array((int) $this->getStatusCode(), self::$cacheResponseCodes)) {
            return false;
        }

        // Make sure a valid body was returned and can be cached
        if ((!$this->getBody()->isReadable() || !$this->getBody()->isSeekable())
            && ($this->getContentLength() > 0 || $this->getTransferEncoding() == 'chunked')) {
            return false;
        }

        // Never cache no-store resources (this is a private cache, so private
        // can be cached)
        if ($this->getHeader('Cache-Control') && $this->getHeader('Cache-Control')->hasDirective('no-store')) {
            return false;
        }

        return $this->isFresh() || $this->getFreshness() === null || $this->canValidate();
    }

    /**
     * Gets the number of seconds from the current time in which this response is still considered fresh
     *
     * @return int|null Returns the number of seconds
     */
    public function getMaxAge()
    {
        if ($header = $this->getHeader('Cache-Control')) {
            // s-max-age, then max-age, then Expires
            if ($age = $header->getDirective('s-maxage')) {
                return $age;
            }
            if ($age = $header->getDirective('max-age')) {
                return $age;
            }
        }

        if ($this->getHeader('Expires')) {
            return strtotime($this->getExpires()) - time();
        }

        return null;
    }

    /**
     * Check if the response is considered fresh.
     *
     * A response is considered fresh when its age is less than or equal to the freshness lifetime (maximum age) of the
     * response.
     *
     * @return bool|null
     */
    public function isFresh()
    {
        $fresh = $this->getFreshness();

        return $fresh === null ? null : $fresh >= 0;
    }

    /**
     * Check if the response can be validated against the origin server using a conditional GET request.
     *
     * @return bool
     */
    public function canValidate()
    {
        return $this->getEtag() || $this->getLastModified();
    }

    /**
     * Get the freshness of the response by returning the difference of the maximum lifetime of the response and the
     * age of the response (max-age - age).
     *
     * Freshness values less than 0 mean that the response is no longer fresh and is ABS(freshness) seconds expired.
     * Freshness values of greater than zero is the number of seconds until the response is no longer fresh. A NULL
     * result means that no freshness information is available.
     *
     * @return int
     */
    public function getFreshness()
    {
        $maxAge = $this->getMaxAge();
        $age = $this->calculateAge();

        return $maxAge && $age ? ($maxAge - $age) : null;
    }

    /**
     * Parse the JSON response body and return an array
     *
     * @return array|string|int|bool|float
     * @throws RuntimeException if the response body is not in JSON format
     */
    public function json()
    {
        $data = json_decode((string) $this->body, true);
        if (JSON_ERROR_NONE !== json_last_error()) {
            throw new RuntimeException('Unable to parse response body into JSON: ' . json_last_error());
        }

        return $data === null ? array() : $data;
    }

    /**
     * Parse the XML response body and return a \SimpleXMLElement.
     *
     * In order to prevent XXE attacks, this method disables loading external
     * entities. If you rely on external entities, then you must parse the
     * XML response manually by accessing the response body directly.
     *
     * @return \SimpleXMLElement
     * @throws RuntimeException if the response body is not in XML format
     * @link http://websec.io/2012/08/27/Preventing-XXE-in-PHP.html
     */
    public function xml()
    {
        $errorMessage = null;
        $internalErrors = libxml_use_internal_errors(true);
        if (PHP_VERSION_ID < 80000)
            $disableEntities = libxml_disable_entity_loader(true);
        libxml_clear_errors();

        try {
            $xml = new \SimpleXMLElement((string) $this->body ?: '<root />', LIBXML_NONET);
            if ($error = libxml_get_last_error()) {
                $errorMessage = $error->message;
            }
        } catch (\Exception $e) {
            $errorMessage = $e->getMessage();
        }

        libxml_clear_errors();
        libxml_use_internal_errors($internalErrors);
        if (PHP_VERSION_ID < 80000)
            libxml_disable_entity_loader($disableEntities);

        if ($errorMessage) {
            throw new RuntimeException('Unable to parse response body into XML: ' . $errorMessage);
        }

        return $xml;
    }

    /**
     * Get the redirect count of this response
     *
     * @return int
     */
    public function getRedirectCount()
    {
        return (int) $this->params->get(RedirectPlugin::REDIRECT_COUNT);
    }

    /**
     * Set the effective URL that resulted in this response (e.g. the last redirect URL)
     *
     * @param string $url The effective URL
     *
     * @return self
     */
    public function setEffectiveUrl($url)
    {
        $this->effectiveUrl = $url;

        return $this;
    }

    /**
     * Get the effective URL that resulted in this response (e.g. the last redirect URL)
     *
     * @return string
     */
    public function getEffectiveUrl()
    {
        return $this->effectiveUrl;
    }

    /**
     * @deprecated
     * @codeCoverageIgnore
     */
    public function getPreviousResponse()
    {
        Version::warn(__METHOD__ . ' is deprecated. Use the HistoryPlugin.');
        return null;
    }

    /**
     * @deprecated
     * @codeCoverageIgnore
     */
    public function setRequest($request)
    {
        Version::warn(__METHOD__ . ' is deprecated');
        return $this;
    }

    /**
     * @deprecated
     * @codeCoverageIgnore
     */
    public function getRequest()
    {
        Version::warn(__METHOD__ . ' is deprecated');
        return null;
    }
}
vendor/guzzle/guzzle/src/Guzzle/Http/Message/Header/CacheControl.php000064400000005366151327705700021541 0ustar00<?php

namespace Guzzle\Http\Message\Header;

use Guzzle\Http\Message\Header;

/**
 * Provides helpful functionality for Cache-Control headers
 */
class CacheControl extends Header
{
    /** @var array */
    protected $directives;

    public function add($value)
    {
        parent::add($value);
        $this->directives = null;
    }

    public function removeValue($searchValue)
    {
        parent::removeValue($searchValue);
        $this->directives = null;
    }

    /**
     * Check if a specific cache control directive exists
     *
     * @param string $param Directive to retrieve
     *
     * @return bool
     */
    public function hasDirective($param)
    {
        $directives = $this->getDirectives();

        return isset($directives[$param]);
    }

    /**
     * Get a specific cache control directive
     *
     * @param string $param Directive to retrieve
     *
     * @return string|bool|null
     */
    public function getDirective($param)
    {
        $directives = $this->getDirectives();

        return isset($directives[$param]) ? $directives[$param] : null;
    }

    /**
     * Add a cache control directive
     *
     * @param string $param Directive to add
     * @param string $value Value to set
     *
     * @return self
     */
    public function addDirective($param, $value)
    {
        $directives = $this->getDirectives();
        $directives[$param] = $value;
        $this->updateFromDirectives($directives);

        return $this;
    }

    /**
     * Remove a cache control directive by name
     *
     * @param string $param Directive to remove
     *
     * @return self
     */
    public function removeDirective($param)
    {
        $directives = $this->getDirectives();
        unset($directives[$param]);
        $this->updateFromDirectives($directives);

        return $this;
    }

    /**
     * Get an associative array of cache control directives
     *
     * @return array
     */
    public function getDirectives()
    {
        if ($this->directives === null) {
            $this->directives = array();
            foreach ($this->parseParams() as $collection) {
                foreach ($collection as $key => $value) {
                    $this->directives[$key] = $value === '' ? true : $value;
                }
            }
        }

        return $this->directives;
    }

    /**
     * Updates the header value based on the parsed directives
     *
     * @param array $directives Array of cache control directives
     */
    protected function updateFromDirectives(array $directives)
    {
        $this->directives = $directives;
        $this->values = array();

        foreach ($directives as $key => $value) {
            $this->values[] = $value === true ? $key : "{$key}={$value}";
        }
    }
}
vendor/guzzle/guzzle/src/Guzzle/Http/Message/Header/HeaderFactory.php000064400000001206151327705700021702 0ustar00<?php

namespace Guzzle\Http\Message\Header;

use Guzzle\Http\Message\Header;

/**
 * Default header factory implementation
 */
class HeaderFactory implements HeaderFactoryInterface
{
    /** @var array */
    protected $mapping = array(
        'cache-control' => 'Guzzle\Http\Message\Header\CacheControl',
        'link'          => 'Guzzle\Http\Message\Header\Link',
    );

    public function createHeader($header, $value = null)
    {
        $lowercase = strtolower($header);

        return isset($this->mapping[$lowercase])
            ? new $this->mapping[$lowercase]($header, $value)
            : new Header($header, $value);
    }
}
vendor/guzzle/guzzle/src/Guzzle/Http/Message/Header/HeaderCollection.php000064400000004040151327705700022365 0ustar00<?php

namespace Guzzle\Http\Message\Header;

use Guzzle\Common\ToArrayInterface;

/**
 * Provides a case-insensitive collection of headers
 */
class HeaderCollection implements \IteratorAggregate, \Countable, \ArrayAccess, ToArrayInterface
{
    /** @var array */
    protected $headers;

    public function __construct($headers = array())
    {
        $this->headers = $headers;
    }

    public function __clone()
    {
        foreach ($this->headers as &$header) {
            $header = clone $header;
        }
    }

    /**
     * Clears the header collection
     */
    public function clear()
    {
        $this->headers = array();
    }

    /**
     * Set a header on the collection
     *
     * @param HeaderInterface $header Header to add
     *
     * @return self
     */
    public function add(HeaderInterface $header)
    {
        $this->headers[strtolower($header->getName())] = $header;

        return $this;
    }

    /**
     * Get an array of header objects
     *
     * @return array
     */
    public function getAll()
    {
        return $this->headers;
    }

    /**
     * Alias of offsetGet
     */
    public function get($key)
    {
        return $this->offsetGet($key);
    }

    public function count()
    {
        return count($this->headers);
    }

    public function offsetExists($offset)
    {
        return isset($this->headers[strtolower($offset)]);
    }

    public function offsetGet($offset)
    {
        $l = strtolower($offset);

        return isset($this->headers[$l]) ? $this->headers[$l] : null;
    }

    public function offsetSet($offset, $value)
    {
        $this->add($value);
    }

    public function offsetUnset($offset)
    {
        unset($this->headers[strtolower($offset)]);
    }

    public function getIterator()
    {
        return new \ArrayIterator($this->headers);
    }

    public function toArray()
    {
        $result = array();
        foreach ($this->headers as $header) {
            $result[$header->getName()] = $header->toArray();
        }

        return $result;
    }
}
vendor/guzzle/guzzle/src/Guzzle/Http/Message/Header/HeaderInterface.php000064400000003520151327705700022174 0ustar00<?php

namespace Guzzle\Http\Message\Header;

use Guzzle\Common\ToArrayInterface;

interface HeaderInterface extends ToArrayInterface, \Countable, \IteratorAggregate
{
    /**
     * Convert the header to a string
     *
     * @return string
     */
    public function __toString();

    /**
     * Add a value to the list of header values
     *
     * @param string $value Value to add to the header
     *
     * @return self
     */
    public function add($value);

    /**
     * Get the name of the header
     *
     * @return string
     */
    public function getName();

    /**
     * Change the name of the header
     *
     * @param string $name Name to change to
     *
     * @return self
     */
    public function setName($name);

    /**
     * Change the glue used to implode the values
     *
     * @param string $glue Glue used to implode multiple values
     *
     * @return self
     */
    public function setGlue($glue);

    /**
     * Get the glue used to implode multiple values into a string
     *
     * @return string
     */
    public function getGlue();

    /**
     * Check if the collection of headers has a particular value
     *
     * @param string $searchValue Value to search for
     *
     * @return bool
     */
    public function hasValue($searchValue);

    /**
     * Remove a specific value from the header
     *
     * @param string $searchValue Value to remove
     *
     * @return self
     */
    public function removeValue($searchValue);

    /**
     * Parse a header containing ";" separated data into an array of associative arrays representing the header
     * key value pair data of the header. When a parameter does not contain a value, but just contains a key, this
     * function will inject a key with a '' string value.
     *
     * @return array
     */
    public function parseParams();
}
vendor/guzzle/guzzle/src/Guzzle/Http/Message/Header/Link.php000064400000003745151327705700020071 0ustar00<?php

namespace Guzzle\Http\Message\Header;

use Guzzle\Http\Message\Header;

/**
 * Provides helpful functionality for link headers
 */
class Link extends Header
{
    /**
     * Add a link to the header
     *
     * @param string $url    Link URL
     * @param string $rel    Link rel
     * @param array  $params Other link parameters
     *
     * @return self
     */
    public function addLink($url, $rel, array $params = array())
    {
        $values = array("<{$url}>", "rel=\"{$rel}\"");

        foreach ($params as $k => $v) {
            $values[] = "{$k}=\"{$v}\"";
        }

        return $this->add(implode('; ', $values));
    }

    /**
     * Check if a specific link exists for a given rel attribute
     *
     * @param string $rel rel value
     *
     * @return bool
     */
    public function hasLink($rel)
    {
        return $this->getLink($rel) !== null;
    }

    /**
     * Get a specific link for a given rel attribute
     *
     * @param string $rel Rel value
     *
     * @return array|null
     */
    public function getLink($rel)
    {
        foreach ($this->getLinks() as $link) {
            if (isset($link['rel']) && $link['rel'] == $rel) {
                return $link;
            }
        }

        return null;
    }

    /**
     * Get an associative array of links
     *
     * For example:
     * Link: <http:/.../front.jpeg>; rel=front; type="image/jpeg", <http://.../back.jpeg>; rel=back; type="image/jpeg"
     *
     * <code>
     * var_export($response->getLinks());
     * array(
     *     array(
     *         'url' => 'http:/.../front.jpeg',
     *         'rel' => 'back',
     *         'type' => 'image/jpeg',
     *     )
     * )
     * </code>
     *
     * @return array
     */
    public function getLinks()
    {
        $links = $this->parseParams();

        foreach ($links as &$link) {
            $key = key($link);
            unset($link[$key]);
            $link['url'] = trim($key, '<> ');
        }

        return $links;
    }
}
vendor/guzzle/guzzle/src/Guzzle/Http/Message/Header/HeaderFactoryInterface.php000064400000000643151327705700023527 0ustar00<?php

namespace Guzzle\Http\Message\Header;

/**
 * Interface for creating headers
 */
interface HeaderFactoryInterface
{
    /**
     * Create a header from a header name and a single value
     *
     * @param string $header Name of the header to create
     * @param string $value  Value to set on the header
     *
     * @return HeaderInterface
     */
    public function createHeader($header, $value = null);
}
vendor/guzzle/guzzle/src/Guzzle/Http/Message/MessageInterface.php000064400000004420151327705700021200 0ustar00<?php

namespace Guzzle\Http\Message;

/**
 * Request and response message interface
 */
interface MessageInterface
{
    /**
     * Get application and plugin specific parameters set on the message.
     *
     * @return \Guzzle\Common\Collection
     */
    public function getParams();

    /**
     * Add a header to an existing collection of headers.
     *
     * @param string $header Header name to add
     * @param string $value  Value of the header
     *
     * @return self
     */
    public function addHeader($header, $value);

    /**
     * Add and merge in an array of HTTP headers.
     *
     * @param array $headers Associative array of header data.
     *
     * @return self
     */
    public function addHeaders(array $headers);

    /**
     * Retrieve an HTTP header by name. Performs a case-insensitive search of all headers.
     *
     * @param string $header Header to retrieve.
     *
     * @return Header|null
     */
    public function getHeader($header);

    /**
     * Get all headers as a collection
     *
     * @return \Guzzle\Http\Message\Header\HeaderCollection
     */
    public function getHeaders();

    /**
     * Check if the specified header is present.
     *
     * @param string $header The header to check.
     *
     * @return bool
     */
    public function hasHeader($header);

    /**
     * Remove a specific HTTP header.
     *
     * @param string $header HTTP header to remove.
     *
     * @return self
     */
    public function removeHeader($header);

    /**
     * Set an HTTP header and overwrite any existing value for the header
     *
     * @param string $header Name of the header to set.
     * @param mixed  $value  Value to set.
     *
     * @return self
     */
    public function setHeader($header, $value);

    /**
     * Overwrite all HTTP headers with the supplied array of headers
     *
     * @param array $headers Associative array of header data.
     *
     * @return self
     */
    public function setHeaders(array $headers);

    /**
     * Get an array of message header lines (e.g. ["Host: example.com", ...])
     *
     * @return array
     */
    public function getHeaderLines();

    /**
     * Get the raw message headers as a string
     *
     * @return string
     */
    public function getRawHeaders();
}
vendor/guzzle/guzzle/src/Guzzle/Http/Message/Request.php000064400000045617151327705700017440 0ustar00<?php

namespace Guzzle\Http\Message;

use Guzzle\Common\Version;
use Guzzle\Common\Event;
use Guzzle\Common\Collection;
use Guzzle\Common\Exception\RuntimeException;
use Guzzle\Common\Exception\InvalidArgumentException;
use Guzzle\Http\Exception\RequestException;
use Guzzle\Http\Exception\BadResponseException;
use Guzzle\Http\ClientInterface;
use Guzzle\Http\EntityBody;
use Guzzle\Http\EntityBodyInterface;
use Guzzle\Http\Message\Header\HeaderInterface;
use Guzzle\Http\Url;
use Guzzle\Parser\ParserRegistry;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\EventDispatcher\EventDispatcher;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;

/**
 * HTTP request class to send requests
 */
class Request extends AbstractMessage implements RequestInterface
{
    /** @var EventDispatcherInterface */
    protected $eventDispatcher;

    /** @var Url HTTP Url */
    protected $url;

    /** @var string HTTP method (GET, PUT, POST, DELETE, HEAD, OPTIONS, TRACE) */
    protected $method;

    /** @var ClientInterface */
    protected $client;

    /** @var Response Response of the request */
    protected $response;

    /** @var EntityBodyInterface Response body */
    protected $responseBody;

    /** @var string State of the request object */
    protected $state;

    /** @var string Authentication username */
    protected $username;

    /** @var string Auth password */
    protected $password;

    /** @var Collection cURL specific transfer options */
    protected $curlOptions;

    /** @var bool */
    protected $isRedirect = false;

    public static function getAllEvents()
    {
        return array(
            // Called when receiving or uploading data through cURL
            'curl.callback.read', 'curl.callback.write', 'curl.callback.progress',
            // Cloning a request
            'request.clone',
            // About to send the request, sent request, completed transaction
            'request.before_send', 'request.sent', 'request.complete',
            // A request received a successful response
            'request.success',
            // A request received an unsuccessful response
            'request.error',
            // An exception is being thrown because of an unsuccessful response
            'request.exception',
            // Received response status line
            'request.receive.status_line'
        );
    }

    /**
     * @param string           $method  HTTP method
     * @param string|Url       $url     HTTP URL to connect to. The URI scheme, host header, and URI are parsed from the
     *                                  full URL. If query string parameters are present they will be parsed as well.
     * @param array|Collection $headers HTTP headers
     */
    public function __construct($method, $url, $headers = array())
    {
        parent::__construct();
        $this->method = strtoupper($method);
        $this->curlOptions = new Collection();
        $this->setUrl($url);

        if ($headers) {
            // Special handling for multi-value headers
            foreach ($headers as $key => $value) {
                // Deal with collisions with Host and Authorization
                if ($key == 'host' || $key == 'Host') {
                    $this->setHeader($key, $value);
                } elseif ($value instanceof HeaderInterface) {
                    $this->addHeader($key, $value);
                } else {
                    foreach ((array) $value as $v) {
                        $this->addHeader($key, $v);
                    }
                }
            }
        }

        $this->setState(self::STATE_NEW);
    }

    public function __clone()
    {
        if ($this->eventDispatcher) {
            $this->eventDispatcher = clone $this->eventDispatcher;
        }
        $this->curlOptions = clone $this->curlOptions;
        $this->params = clone $this->params;
        $this->url = clone $this->url;
        $this->response = $this->responseBody = null;
        $this->headers = clone $this->headers;

        $this->setState(RequestInterface::STATE_NEW);
        $this->dispatch('request.clone', array('request' => $this));
    }

    /**
     * Get the HTTP request as a string
     *
     * @return string
     */
    public function __toString()
    {
        return $this->getRawHeaders() . "\r\n\r\n";
    }

    /**
     * Default method that will throw exceptions if an unsuccessful response is received.
     *
     * @param Event $event Received
     * @throws BadResponseException if the response is not successful
     */
    public static function onRequestError(Event $event)
    {
        $e = BadResponseException::factory($event['request'], $event['response']);
        $event['request']->setState(self::STATE_ERROR, array('exception' => $e) + $event->toArray());
        throw $e;
    }

    public function setClient(ClientInterface $client)
    {
        $this->client = $client;

        return $this;
    }

    public function getClient()
    {
        return $this->client;
    }

    public function getRawHeaders()
    {
        $protocolVersion = $this->protocolVersion ?: '1.1';

        return trim($this->method . ' ' . $this->getResource()) . ' '
            . strtoupper(str_replace('https', 'http', $this->url->getScheme()))
            . '/' . $protocolVersion . "\r\n" . implode("\r\n", $this->getHeaderLines());
    }

    public function setUrl($url)
    {
        if ($url instanceof Url) {
            $this->url = $url;
        } else {
            $this->url = Url::factory($url);
        }

        // Update the port and host header
        $this->setPort($this->url->getPort());

        if ($this->url->getUsername() || $this->url->getPassword()) {
            $this->setAuth($this->url->getUsername(), $this->url->getPassword());
            // Remove the auth info from the URL
            $this->url->setUsername(null);
            $this->url->setPassword(null);
        }

        return $this;
    }

    public function send()
    {
        if (!$this->client) {
            throw new RuntimeException('A client must be set on the request');
        }

        return $this->client->send($this);
    }

    public function getResponse()
    {
        return $this->response;
    }

    public function getQuery($asString = false)
    {
        return $asString
            ? (string) $this->url->getQuery()
            : $this->url->getQuery();
    }

    public function getMethod()
    {
        return $this->method;
    }

    public function getScheme()
    {
        return $this->url->getScheme();
    }

    public function setScheme($scheme)
    {
        $this->url->setScheme($scheme);

        return $this;
    }

    public function getHost()
    {
        return $this->url->getHost();
    }

    public function setHost($host)
    {
        $this->url->setHost($host);
        $this->setPort($this->url->getPort());

        return $this;
    }

    public function getProtocolVersion()
    {
        return $this->protocolVersion;
    }

    public function setProtocolVersion($protocol)
    {
        $this->protocolVersion = $protocol;

        return $this;
    }

    public function getPath()
    {
        return '/' . ltrim($this->url->getPath(), '/');
    }

    public function setPath($path)
    {
        $this->url->setPath($path);

        return $this;
    }

    public function getPort()
    {
        return $this->url->getPort();
    }

    public function setPort($port)
    {
        $this->url->setPort($port);

        // Include the port in the Host header if it is not the default port for the scheme of the URL
        $scheme = $this->url->getScheme();
        if ($port && (($scheme == 'http' && $port != 80) || ($scheme == 'https' && $port != 443))) {
            $this->headers['host'] = $this->headerFactory->createHeader('Host', $this->url->getHost() . ':' . $port);
        } else {
            $this->headers['host'] = $this->headerFactory->createHeader('Host', $this->url->getHost());
        }

        return $this;
    }

    public function getUsername()
    {
        return $this->username;
    }

    public function getPassword()
    {
        return $this->password;
    }

    public function setAuth($user, $password = '', $scheme = CURLAUTH_BASIC)
    {
        static $authMap = array(
            'basic'  => CURLAUTH_BASIC,
            'digest' => CURLAUTH_DIGEST,
            'ntlm'   => CURLAUTH_NTLM,
            'any'    => CURLAUTH_ANY
        );

        // If we got false or null, disable authentication
        if (!$user) {
            $this->password = $this->username = null;
            $this->removeHeader('Authorization');
            $this->getCurlOptions()->remove(CURLOPT_HTTPAUTH);
            return $this;
        }

        if (!is_numeric($scheme)) {
            $scheme = strtolower($scheme);
            if (!isset($authMap[$scheme])) {
                throw new InvalidArgumentException($scheme . ' is not a valid authentication type');
            }
            $scheme = $authMap[$scheme];
        }

        $this->username = $user;
        $this->password = $password;

        // Bypass CURL when using basic auth to promote connection reuse
        if ($scheme == CURLAUTH_BASIC) {
            $this->getCurlOptions()->remove(CURLOPT_HTTPAUTH);
            $this->setHeader('Authorization', 'Basic ' . base64_encode($this->username . ':' . $this->password));
        } else {
            $this->getCurlOptions()
                ->set(CURLOPT_HTTPAUTH, $scheme)
                ->set(CURLOPT_USERPWD, $this->username . ':' . $this->password);
        }

        return $this;
    }

    public function getResource()
    {
        $resource = $this->getPath();
        if ($query = (string) $this->url->getQuery()) {
            $resource .= '?' . $query;
        }

        return $resource;
    }

    public function getUrl($asObject = false)
    {
        return $asObject ? clone $this->url : (string) $this->url;
    }

    public function getState()
    {
        return $this->state;
    }

    public function setState($state, array $context = array())
    {
        $oldState = $this->state;
        $this->state = $state;

        switch ($state) {
            case self::STATE_NEW:
                $this->response = null;
                break;
            case self::STATE_TRANSFER:
                if ($oldState !== $state) {
                    // Fix Content-Length and Transfer-Encoding collisions
                    if ($this->hasHeader('Transfer-Encoding') && $this->hasHeader('Content-Length')) {
                        $this->removeHeader('Transfer-Encoding');
                    }
                    $this->dispatch('request.before_send', array('request' => $this));
                }
                break;
            case self::STATE_COMPLETE:
                if ($oldState !== $state) {
                    $this->processResponse($context);
                    $this->responseBody = null;
                }
                break;
            case self::STATE_ERROR:
                if (isset($context['exception'])) {
                    $this->dispatch('request.exception', array(
                        'request'   => $this,
                        'response'  => isset($context['response']) ? $context['response'] : $this->response,
                        'exception' => isset($context['exception']) ? $context['exception'] : null
                    ));
                }
        }

        return $this->state;
    }

    public function getCurlOptions()
    {
        return $this->curlOptions;
    }

    public function startResponse(Response $response)
    {
        $this->state = self::STATE_TRANSFER;
        $response->setEffectiveUrl((string) $this->getUrl());
        $this->response = $response;

        return $this;
    }

    public function setResponse(Response $response, $queued = false)
    {
        $response->setEffectiveUrl((string) $this->url);

        if ($queued) {
            $ed = $this->getEventDispatcher();
            $ed->addListener('request.before_send', $f = function ($e) use ($response, &$f, $ed) {
                $e['request']->setResponse($response);
                $ed->removeListener('request.before_send', $f);
            }, -9999);
        } else {
            $this->response = $response;
            // If a specific response body is specified, then use it instead of the response's body
            if ($this->responseBody && !$this->responseBody->getCustomData('default') && !$response->isRedirect()) {
                $this->getResponseBody()->write((string) $this->response->getBody());
            } else {
                $this->responseBody = $this->response->getBody();
            }
            $this->setState(self::STATE_COMPLETE);
        }

        return $this;
    }

    public function setResponseBody($body)
    {
        // Attempt to open a file for writing if a string was passed
        if (is_string($body)) {
            // @codeCoverageIgnoreStart
            if (!($body = fopen($body, 'w+'))) {
                throw new InvalidArgumentException('Could not open ' . $body . ' for writing');
            }
            // @codeCoverageIgnoreEnd
        }

        $this->responseBody = EntityBody::factory($body);

        return $this;
    }

    public function getResponseBody()
    {
        if ($this->responseBody === null) {
            $this->responseBody = EntityBody::factory()->setCustomData('default', true);
        }

        return $this->responseBody;
    }

    /**
     * Determine if the response body is repeatable (readable + seekable)
     *
     * @return bool
     * @deprecated Use getResponseBody()->isSeekable()
     * @codeCoverageIgnore
     */
    public function isResponseBodyRepeatable()
    {
        Version::warn(__METHOD__ . ' is deprecated. Use $request->getResponseBody()->isRepeatable()');
        return !$this->responseBody ? true : $this->responseBody->isRepeatable();
    }

    public function getCookies()
    {
        if ($cookie = $this->getHeader('Cookie')) {
            $data = ParserRegistry::getInstance()->getParser('cookie')->parseCookie($cookie);
            return $data['cookies'];
        }

        return array();
    }

    public function getCookie($name)
    {
        $cookies = $this->getCookies();

        return isset($cookies[$name]) ? $cookies[$name] : null;
    }

    public function addCookie($name, $value)
    {
        if (!$this->hasHeader('Cookie')) {
            $this->setHeader('Cookie', "{$name}={$value}");
        } else {
            $this->getHeader('Cookie')->add("{$name}={$value}");
        }

        // Always use semicolons to separate multiple cookie headers
        $this->getHeader('Cookie')->setGlue(';');

        return $this;
    }

    public function removeCookie($name)
    {
        if ($cookie = $this->getHeader('Cookie')) {
            foreach ($cookie as $cookieValue) {
                if (strpos($cookieValue, $name . '=') === 0) {
                    $cookie->removeValue($cookieValue);
                }
            }
        }

        return $this;
    }

    public function setEventDispatcher(EventDispatcherInterface $eventDispatcher)
    {
        $this->eventDispatcher = $eventDispatcher;
        $this->eventDispatcher->addListener('request.error', array(__CLASS__, 'onRequestError'), -255);

        return $this;
    }

    public function getEventDispatcher()
    {
        if (!$this->eventDispatcher) {
            $this->setEventDispatcher(new EventDispatcher());
        }

        return $this->eventDispatcher;
    }

    public function dispatch($eventName, array $context = array())
    {
        $context['request'] = $this;

        return $this->getEventDispatcher()->dispatch($eventName, new Event($context));
    }

    public function addSubscriber(EventSubscriberInterface $subscriber)
    {
        $this->getEventDispatcher()->addSubscriber($subscriber);

        return $this;
    }

    /**
     * Get an array containing the request and response for event notifications
     *
     * @return array
     */
    protected function getEventArray()
    {
        return array(
            'request'  => $this,
            'response' => $this->response
        );
    }

    /**
     * Process a received response
     *
     * @param array $context Contextual information
     * @throws RequestException|BadResponseException on unsuccessful responses
     */
    protected function processResponse(array $context = array())
    {
        if (!$this->response) {
            // If no response, then processResponse shouldn't have been called
            $e = new RequestException('Error completing request');
            $e->setRequest($this);
            throw $e;
        }

        $this->state = self::STATE_COMPLETE;

        // A request was sent, but we don't know if we'll send more or if the final response will be successful
        $this->dispatch('request.sent', $this->getEventArray() + $context);

        // Some response processors will remove the response or reset the state (example: ExponentialBackoffPlugin)
        if ($this->state == RequestInterface::STATE_COMPLETE) {

            // The request completed, so the HTTP transaction is complete
            $this->dispatch('request.complete', $this->getEventArray());

            // If the response is bad, allow listeners to modify it or throw exceptions. You can change the response by
            // modifying the Event object in your listeners or calling setResponse() on the request
            if ($this->response->isError()) {
                $event = new Event($this->getEventArray());
                $this->getEventDispatcher()->dispatch('request.error', $event);
                // Allow events of request.error to quietly change the response
                if ($event['response'] !== $this->response) {
                    $this->response = $event['response'];
                }
            }

            // If a successful response was received, dispatch an event
            if ($this->response->isSuccessful()) {
                $this->dispatch('request.success', $this->getEventArray());
            }
        }
    }

    /**
     * @deprecated Use Guzzle\Plugin\Cache\DefaultCanCacheStrategy
     * @codeCoverageIgnore
     */
    public function canCache()
    {
        Version::warn(__METHOD__ . ' is deprecated. Use Guzzle\Plugin\Cache\DefaultCanCacheStrategy.');
        if (class_exists('Guzzle\Plugin\Cache\DefaultCanCacheStrategy')) {
            $canCache = new \Guzzle\Plugin\Cache\DefaultCanCacheStrategy();
            return $canCache->canCacheRequest($this);
        } else {
            return false;
        }
    }

    /**
     * @deprecated Use the history plugin (not emitting a warning as this is built-into the RedirectPlugin for now)
     * @codeCoverageIgnore
     */
    public function setIsRedirect($isRedirect)
    {
        $this->isRedirect = $isRedirect;

        return $this;
    }

    /**
     * @deprecated Use the history plugin
     * @codeCoverageIgnore
     */
    public function isRedirect()
    {
        Version::warn(__METHOD__ . ' is deprecated. Use the HistoryPlugin to track this.');
        return $this->isRedirect;
    }
}
vendor/guzzle/guzzle/src/Guzzle/Http/Message/EntityEnclosingRequestInterface.php000064400000010353151327705700024305 0ustar00<?php

namespace Guzzle\Http\Message;

use Guzzle\Http\Exception\RequestException;
use Guzzle\Http\EntityBodyInterface;
use Guzzle\Http\QueryString;

/**
 * HTTP request that sends an entity-body in the request message (POST, PUT)
 */
interface EntityEnclosingRequestInterface extends RequestInterface
{
    const URL_ENCODED = 'application/x-www-form-urlencoded; charset=utf-8';
    const MULTIPART = 'multipart/form-data';

    /**
     * Set the body of the request
     *
     * @param string|resource|EntityBodyInterface $body        Body to use in the entity body of the request
     * @param string                              $contentType Content-Type to set. Leave null to use an existing
     *                                                         Content-Type or to guess the Content-Type
     * @return self
     * @throws RequestException if the protocol is < 1.1 and Content-Length can not be determined
     */
    public function setBody($body, $contentType = null);

    /**
     * Get the body of the request if set
     *
     * @return EntityBodyInterface|null
     */
    public function getBody();

    /**
     * Get a POST field from the request
     *
     * @param string $field Field to retrieve
     *
     * @return mixed|null
     */
    public function getPostField($field);

    /**
     * Get the post fields that will be used in the request
     *
     * @return QueryString
     */
    public function getPostFields();

    /**
     * Set a POST field value
     *
     * @param string $key   Key to set
     * @param string $value Value to set
     *
     * @return self
     */
    public function setPostField($key, $value);

    /**
     * Add POST fields to use in the request
     *
     * @param QueryString|array $fields POST fields
     *
     * @return self
     */
    public function addPostFields($fields);

    /**
     * Remove a POST field or file by name
     *
     * @param string $field Name of the POST field or file to remove
     *
     * @return self
     */
    public function removePostField($field);

    /**
     * Returns an associative array of POST field names to PostFileInterface objects
     *
     * @return array
     */
    public function getPostFiles();

    /**
     * Get a POST file from the request
     *
     * @param string $fieldName POST fields to retrieve
     *
     * @return array|null Returns an array wrapping an array of PostFileInterface objects
     */
    public function getPostFile($fieldName);

    /**
     * Remove a POST file from the request
     *
     * @param string $fieldName POST file field name to remove
     *
     * @return self
     */
    public function removePostFile($fieldName);

    /**
     * Add a POST file to the upload
     *
     * @param string $field       POST field to use (e.g. file). Used to reference content from the server.
     * @param string $filename    Full path to the file. Do not include the @ symbol.
     * @param string $contentType Optional Content-Type to add to the Content-Disposition.
     *                            Default behavior is to guess. Set to false to not specify.
     * @param string $postname    The name of the file, when posted. (e.g. rename the file)
     * @return self
     */
    public function addPostFile($field, $filename = null, $contentType = null, $postname = null);

    /**
     * Add POST files to use in the upload
     *
     * @param array $files An array of POST fields => filenames where filename can be a string or PostFileInterface
     *
     * @return self
     */
    public function addPostFiles(array $files);

    /**
     * Configure how redirects are handled for the request
     *
     * @param bool $strict       Set to true to follow strict RFC compliance when redirecting POST requests. Most
     *                           browsers with follow a 301-302 redirect for a POST request with a GET request. This is
     *                           the default behavior of Guzzle. Enable strict redirects to redirect these responses
     *                           with a POST rather than a GET request.
     * @param int  $maxRedirects Specify the maximum number of allowed redirects. Set to 0 to disable redirects.
     *
     * @return self
     */
    public function configureRedirects($strict = false, $maxRedirects = 5);
}
vendor/guzzle/guzzle/src/Guzzle/Http/Message/RequestFactory.php000064400000031304151327705700020754 0ustar00<?php

namespace Guzzle\Http\Message;

use Guzzle\Common\Collection;
use Guzzle\Common\Exception\InvalidArgumentException;
use Guzzle\Http\RedirectPlugin;
use Guzzle\Http\Url;
use Guzzle\Parser\ParserRegistry;

/**
 * Default HTTP request factory used to create the default {@see Request} and {@see EntityEnclosingRequest} objects.
 */
class RequestFactory implements RequestFactoryInterface
{
    /** @var RequestFactory Singleton instance of the default request factory */
    protected static $instance;

    /** @var array Hash of methods available to the class (provides fast isset() lookups) */
    protected $methods;

    /** @var string Class to instantiate for requests with no body */
    protected $requestClass = 'Guzzle\\Http\\Message\\Request';

    /** @var string Class to instantiate for requests with a body */
    protected $entityEnclosingRequestClass = 'Guzzle\\Http\\Message\\EntityEnclosingRequest';

    /**
     * Get a cached instance of the default request factory
     *
     * @return RequestFactory
     */
    public static function getInstance()
    {
        // @codeCoverageIgnoreStart
        if (!static::$instance) {
            static::$instance = new static();
        }
        // @codeCoverageIgnoreEnd

        return static::$instance;
    }

    public function __construct()
    {
        $this->methods = array_flip(get_class_methods(__CLASS__));
    }

    public function fromMessage($message)
    {
        $parsed = ParserRegistry::getInstance()->getParser('message')->parseRequest($message);

        if (!$parsed) {
            return false;
        }

        $request = $this->fromParts($parsed['method'], $parsed['request_url'],
            $parsed['headers'], $parsed['body'], $parsed['protocol'],
            $parsed['version']);

        // EntityEnclosingRequest adds an "Expect: 100-Continue" header when using a raw request body for PUT or POST
        // requests. This factory method should accurately reflect the message, so here we are removing the Expect
        // header if one was not supplied in the message.
        if (!isset($parsed['headers']['Expect']) && !isset($parsed['headers']['expect'])) {
            $request->removeHeader('Expect');
        }

        return $request;
    }

    public function fromParts(
        $method,
        array $urlParts,
        $headers = null,
        $body = null,
        $protocol = 'HTTP',
        $protocolVersion = '1.1'
    ) {
        return $this->create($method, Url::buildUrl($urlParts), $headers, $body)
                    ->setProtocolVersion($protocolVersion);
    }

    public function create($method, $url, $headers = null, $body = null, array $options = array())
    {
        $method = strtoupper($method);

        if ($method == 'GET' || $method == 'HEAD' || $method == 'TRACE') {
            // Handle non-entity-enclosing request methods
            $request = new $this->requestClass($method, $url, $headers);
            if ($body) {
                // The body is where the response body will be stored
                $type = gettype($body);
                if ($type == 'string' || $type == 'resource' || $type == 'object') {
                    $request->setResponseBody($body);
                }
            }
        } else {
            // Create an entity enclosing request by default
            $request = new $this->entityEnclosingRequestClass($method, $url, $headers);
            if ($body || $body === '0') {
                // Add POST fields and files to an entity enclosing request if an array is used
                if (is_array($body) || $body instanceof Collection) {
                    // Normalize PHP style cURL uploads with a leading '@' symbol
                    foreach ($body as $key => $value) {
                        if (is_string($value) && substr($value, 0, 1) == '@') {
                            $request->addPostFile($key, $value);
                            unset($body[$key]);
                        }
                    }
                    // Add the fields if they are still present and not all files
                    $request->addPostFields($body);
                } else {
                    // Add a raw entity body body to the request
                    $request->setBody($body, (string) $request->getHeader('Content-Type'));
                    if ((string) $request->getHeader('Transfer-Encoding') == 'chunked') {
                        $request->removeHeader('Content-Length');
                    }
                }
            }
        }

        if ($options) {
            $this->applyOptions($request, $options);
        }

        return $request;
    }

    /**
     * Clone a request while changing the method. Emulates the behavior of
     * {@see Guzzle\Http\Message\Request::clone}, but can change the HTTP method.
     *
     * @param RequestInterface $request Request to clone
     * @param string           $method  Method to set
     *
     * @return RequestInterface
     */
    public function cloneRequestWithMethod(RequestInterface $request, $method)
    {
        // Create the request with the same client if possible
        if ($request->getClient()) {
            $cloned = $request->getClient()->createRequest($method, $request->getUrl(), $request->getHeaders());
        } else {
            $cloned = $this->create($method, $request->getUrl(), $request->getHeaders());
        }

        $cloned->getCurlOptions()->replace($request->getCurlOptions()->toArray());
        $cloned->setEventDispatcher(clone $request->getEventDispatcher());
        // Ensure that that the Content-Length header is not copied if changing to GET or HEAD
        if (!($cloned instanceof EntityEnclosingRequestInterface)) {
            $cloned->removeHeader('Content-Length');
        } elseif ($request instanceof EntityEnclosingRequestInterface) {
            $cloned->setBody($request->getBody());
        }
        $cloned->getParams()->replace($request->getParams()->toArray());
        $cloned->dispatch('request.clone', array('request' => $cloned));

        return $cloned;
    }

    public function applyOptions(RequestInterface $request, array $options = array(), $flags = self::OPTIONS_NONE)
    {
        // Iterate over each key value pair and attempt to apply a config using function visitors
        foreach ($options as $key => $value) {
            $method = "visit_{$key}";
            if (isset($this->methods[$method])) {
                $this->{$method}($request, $value, $flags);
            }
        }
    }

    protected function visit_headers(RequestInterface $request, $value, $flags)
    {
        if (!is_array($value)) {
            throw new InvalidArgumentException('headers value must be an array');
        }

        if ($flags & self::OPTIONS_AS_DEFAULTS) {
            // Merge headers in but do not overwrite existing values
            foreach ($value as $key => $header) {
                if (!$request->hasHeader($key)) {
                    $request->setHeader($key, $header);
                }
            }
        } else {
            $request->addHeaders($value);
        }
    }

    protected function visit_body(RequestInterface $request, $value, $flags)
    {
        if ($request instanceof EntityEnclosingRequestInterface) {
            $request->setBody($value);
        } else {
            throw new InvalidArgumentException('Attempting to set a body on a non-entity-enclosing request');
        }
    }

    protected function visit_allow_redirects(RequestInterface $request, $value, $flags)
    {
        if ($value === false) {
            $request->getParams()->set(RedirectPlugin::DISABLE, true);
        }
    }

    protected function visit_auth(RequestInterface $request, $value, $flags)
    {
        if (!is_array($value)) {
            throw new InvalidArgumentException('auth value must be an array');
        }

        $request->setAuth($value[0], isset($value[1]) ? $value[1] : null, isset($value[2]) ? $value[2] : 'basic');
    }

    protected function visit_query(RequestInterface $request, $value, $flags)
    {
        if (!is_array($value)) {
            throw new InvalidArgumentException('query value must be an array');
        }

        if ($flags & self::OPTIONS_AS_DEFAULTS) {
            // Merge query string values in but do not overwrite existing values
            $query = $request->getQuery();
            $query->overwriteWith(array_diff_key($value, $query->toArray()));
        } else {
            $request->getQuery()->overwriteWith($value);
        }
    }

    protected function visit_cookies(RequestInterface $request, $value, $flags)
    {
        if (!is_array($value)) {
            throw new InvalidArgumentException('cookies value must be an array');
        }

        foreach ($value as $name => $v) {
            $request->addCookie($name, $v);
        }
    }

    protected function visit_events(RequestInterface $request, $value, $flags)
    {
        if (!is_array($value)) {
            throw new InvalidArgumentException('events value must be an array');
        }

        foreach ($value as $name => $method) {
            if (is_array($method)) {
                $request->getEventDispatcher()->addListener($name, $method[0], $method[1]);
            } else {
                $request->getEventDispatcher()->addListener($name, $method);
            }
        }
    }

    protected function visit_plugins(RequestInterface $request, $value, $flags)
    {
        if (!is_array($value)) {
            throw new InvalidArgumentException('plugins value must be an array');
        }

        foreach ($value as $plugin) {
            $request->addSubscriber($plugin);
        }
    }

    protected function visit_exceptions(RequestInterface $request, $value, $flags)
    {
        if ($value === false || $value === 0) {
            $dispatcher = $request->getEventDispatcher();
            foreach ($dispatcher->getListeners('request.error') as $listener) {
                if (is_array($listener) && $listener[0] == 'Guzzle\Http\Message\Request' && $listener[1] = 'onRequestError') {
                    $dispatcher->removeListener('request.error', $listener);
                    break;
                }
            }
        }
    }

    protected function visit_save_to(RequestInterface $request, $value, $flags)
    {
        $request->setResponseBody($value);
    }

    protected function visit_params(RequestInterface $request, $value, $flags)
    {
        if (!is_array($value)) {
            throw new InvalidArgumentException('params value must be an array');
        }

        $request->getParams()->overwriteWith($value);
    }

    protected function visit_timeout(RequestInterface $request, $value, $flags)
    {
        if (defined('CURLOPT_TIMEOUT_MS')) {
            $request->getCurlOptions()->set(CURLOPT_TIMEOUT_MS, $value * 1000);
        } else {
            $request->getCurlOptions()->set(CURLOPT_TIMEOUT, $value);
        }
    }

    protected function visit_connect_timeout(RequestInterface $request, $value, $flags)
    {
        if (defined('CURLOPT_CONNECTTIMEOUT_MS')) {
            $request->getCurlOptions()->set(CURLOPT_CONNECTTIMEOUT_MS, $value * 1000);
        } else {
            $request->getCurlOptions()->set(CURLOPT_CONNECTTIMEOUT, $value);
        }
    }

    protected function visit_debug(RequestInterface $request, $value, $flags)
    {
        if ($value) {
            $request->getCurlOptions()->set(CURLOPT_VERBOSE, true);
        }
    }

    protected function visit_verify(RequestInterface $request, $value, $flags)
    {
        $curl = $request->getCurlOptions();
        if ($value === true || is_string($value)) {
            $curl[CURLOPT_SSL_VERIFYHOST] = 2;
            $curl[CURLOPT_SSL_VERIFYPEER] = true;
            if ($value !== true) {
                $curl[CURLOPT_CAINFO] = $value;
            }
        } elseif ($value === false) {
            unset($curl[CURLOPT_CAINFO]);
            $curl[CURLOPT_SSL_VERIFYHOST] = 0;
            $curl[CURLOPT_SSL_VERIFYPEER] = false;
        }
    }

    protected function visit_proxy(RequestInterface $request, $value, $flags)
    {
        $request->getCurlOptions()->set(CURLOPT_PROXY, $value, $flags);
    }

    protected function visit_cert(RequestInterface $request, $value, $flags)
    {
        if (is_array($value)) {
            $request->getCurlOptions()->set(CURLOPT_SSLCERT, $value[0]);
            $request->getCurlOptions()->set(CURLOPT_SSLCERTPASSWD, $value[1]);
        } else {
            $request->getCurlOptions()->set(CURLOPT_SSLCERT, $value);
        }
    }

    protected function visit_ssl_key(RequestInterface $request, $value, $flags)
    {
        if (is_array($value)) {
            $request->getCurlOptions()->set(CURLOPT_SSLKEY, $value[0]);
            $request->getCurlOptions()->set(CURLOPT_SSLKEYPASSWD, $value[1]);
        } else {
            $request->getCurlOptions()->set(CURLOPT_SSLKEY, $value);
        }
    }
}
vendor/guzzle/guzzle/src/Guzzle/Http/Message/PostFileInterface.php000064400000002773151327705700021352 0ustar00<?php

namespace Guzzle\Http\Message;

use Guzzle\Common\Exception\InvalidArgumentException;

/**
 * POST file upload
 */
interface PostFileInterface
{
    /**
     * Set the name of the field
     *
     * @param string $name Field name
     *
     * @return self
     */
    public function setFieldName($name);

    /**
     * Get the name of the field
     *
     * @return string
     */
    public function getFieldName();

    /**
     * Set the path to the file
     *
     * @param string $path Full path to the file
     *
     * @return self
     * @throws InvalidArgumentException if the file cannot be read
     */
    public function setFilename($path);

    /**
     * Set the post name of the file
     *
     * @param string $name The new name of the file
     *
     * @return self
     */
    public function setPostname($name);

    /**
     * Get the full path to the file
     *
     * @return string
     */
    public function getFilename();

    /**
     * Get the post name of the file
     *
     * @return string
     */
    public function getPostname();

    /**
     * Set the Content-Type of the file
     *
     * @param string $type Content type
     *
     * @return self
     */
    public function setContentType($type);

    /**
     * Get the Content-Type of the file
     *
     * @return string
     */
    public function getContentType();

    /**
     * Get a cURL ready string or CurlFile object for the upload
     *
     * @return string
     */
    public function getCurlValue();
}
vendor/guzzle/guzzle/src/Guzzle/Http/ClientInterface.php000064400000023301151327705700017445 0ustar00<?php

namespace Guzzle\Http;

use Guzzle\Common\HasDispatcherInterface;
use Guzzle\Common\Collection;
use Guzzle\Common\Exception\InvalidArgumentException;
use Guzzle\Http\Message\EntityEnclosingRequestInterface;
use Guzzle\Http\Message\RequestInterface;

/**
 * Client interface for send HTTP requests
 */
interface ClientInterface extends HasDispatcherInterface
{
    const CREATE_REQUEST = 'client.create_request';

    /** @var string RFC 1123 HTTP-Date */
    const HTTP_DATE = 'D, d M Y H:i:s \G\M\T';

    /**
     * Set the configuration object to use with the client
     *
     * @param array|Collection $config Parameters that define how the client behaves
     *
     * @return self
     */
    public function setConfig($config);

    /**
     * Get a configuration setting or all of the configuration settings. The Collection result of this method can be
     * modified to change the configuration settings of a client.
     *
     * A client should honor the following special values:
     *
     * - request.options: Associative array of default RequestFactory options to apply to each request
     * - request.params: Associative array of request parameters (data values) to apply to each request
     * - curl.options: Associative array of cURL configuration settings to apply to each request
     * - ssl.certificate_authority: Path a CAINFO, CAPATH, true to use strict defaults, or false to disable verification
     * - redirect.disable: Set to true to disable redirects
     *
     * @param bool|string $key Configuration value to retrieve. Set to FALSE to retrieve all values of the client.
     *                         The object return can be modified, and modifications will affect the client's config.
     * @return mixed|Collection
     * @see \Guzzle\Http\Message\RequestFactoryInterface::applyOptions for a full list of request.options options
     */
    public function getConfig($key = false);

    /**
     * Create and return a new {@see RequestInterface} configured for the client.
     *
     * Use an absolute path to override the base path of the client, or a relative path to append to the base path of
     * the client. The URI can contain the query string as well. Use an array to provide a URI template and additional
     * variables to use in the URI template expansion.
     *
     * @param string                                    $method  HTTP method. Defaults to GET
     * @param string|array                              $uri     Resource URI.
     * @param array|Collection                          $headers HTTP headers
     * @param string|resource|array|EntityBodyInterface $body    Entity body of request (POST/PUT) or response (GET)
     * @param array                                     $options Array of options to apply to the request
     *
     * @return RequestInterface
     * @throws InvalidArgumentException if a URI array is passed that does not contain exactly two elements: the URI
     *                                  followed by template variables
     */
    public function createRequest(
        $method = RequestInterface::GET,
        $uri = null,
        $headers = null,
        $body = null,
        array $options = array()
    );

    /**
     * Create a GET request for the client
     *
     * @param string|array     $uri     Resource URI
     * @param array|Collection $headers HTTP headers
     * @param array            $options Options to apply to the request. For BC compatibility, you can also pass a
     *                                  string to tell Guzzle to download the body of the response to a particular
     *                                  location. Use the 'body' option instead for forward compatibility.
     * @return RequestInterface
     * @see    Guzzle\Http\ClientInterface::createRequest()
     */
    public function get($uri = null, $headers = null, $options = array());

    /**
     * Create a HEAD request for the client
     *
     * @param string|array     $uri     Resource URI
     * @param array|Collection $headers HTTP headers
     * @param array            $options Options to apply to the request
     *
     * @return RequestInterface
     * @see    Guzzle\Http\ClientInterface::createRequest()
     */
    public function head($uri = null, $headers = null, array $options = array());

    /**
     * Create a DELETE request for the client
     *
     * @param string|array                        $uri     Resource URI
     * @param array|Collection                    $headers HTTP headers
     * @param string|resource|EntityBodyInterface $body    Body to send in the request
     * @param array                               $options Options to apply to the request
     *
     * @return EntityEnclosingRequestInterface
     * @see    Guzzle\Http\ClientInterface::createRequest()
     */
    public function delete($uri = null, $headers = null, $body = null, array $options = array());

    /**
     * Create a PUT request for the client
     *
     * @param string|array                        $uri     Resource URI
     * @param array|Collection                    $headers HTTP headers
     * @param string|resource|EntityBodyInterface $body    Body to send in the request
     * @param array                               $options Options to apply to the request
     *
     * @return EntityEnclosingRequestInterface
     * @see    Guzzle\Http\ClientInterface::createRequest()
     */
    public function put($uri = null, $headers = null, $body = null, array $options = array());

    /**
     * Create a PATCH request for the client
     *
     * @param string|array                        $uri     Resource URI
     * @param array|Collection                    $headers HTTP headers
     * @param string|resource|EntityBodyInterface $body    Body to send in the request
     * @param array                               $options Options to apply to the request
     *
     * @return EntityEnclosingRequestInterface
     * @see    Guzzle\Http\ClientInterface::createRequest()
     */
    public function patch($uri = null, $headers = null, $body = null, array $options = array());

    /**
     * Create a POST request for the client
     *
     * @param string|array                                $uri      Resource URI
     * @param array|Collection                            $headers  HTTP headers
     * @param array|Collection|string|EntityBodyInterface $postBody POST body. Can be a string, EntityBody, or
     *                                                    associative array of POST fields to send in the body of the
     *                                                    request. Prefix a value in the array with the @ symbol to
     *                                                    reference a file.
     * @param array                                       $options Options to apply to the request
     *
     * @return EntityEnclosingRequestInterface
     * @see    Guzzle\Http\ClientInterface::createRequest()
     */
    public function post($uri = null, $headers = null, $postBody = null, array $options = array());

    /**
     * Create an OPTIONS request for the client
     *
     * @param string|array $uri     Resource URI
     * @param array        $options Options to apply to the request
     *
     * @return RequestInterface
     * @see    Guzzle\Http\ClientInterface::createRequest()
     */
    public function options($uri = null, array $options = array());

    /**
     * Sends a single request or an array of requests in parallel
     *
     * @param array|RequestInterface $requests One or more RequestInterface objects to send
     *
     * @return \Guzzle\Http\Message\Response|array Returns a single Response or an array of Response objects
     */
    public function send($requests);

    /**
     * Get the client's base URL as either an expanded or raw URI template
     *
     * @param bool $expand Set to FALSE to get the raw base URL without URI template expansion
     *
     * @return string|null
     */
    public function getBaseUrl($expand = true);

    /**
     * Set the base URL of the client
     *
     * @param string $url The base service endpoint URL of the webservice
     *
     * @return self
     */
    public function setBaseUrl($url);

    /**
     * Set the User-Agent header to be used on all requests from the client
     *
     * @param string $userAgent      User agent string
     * @param bool   $includeDefault Set to true to prepend the value to Guzzle's default user agent string
     *
     * @return self
     */
    public function setUserAgent($userAgent, $includeDefault = false);

    /**
     * Set SSL verification options.
     *
     * Setting $certificateAuthority to TRUE will result in the bundled cacert.pem being used to verify against the
     * remote host.
     *
     * Alternate certificates to verify against can be specified with the $certificateAuthority option set to the full
     * path to a certificate file, or the path to a directory containing certificates.
     *
     * Setting $certificateAuthority to FALSE will turn off peer verification, unset the bundled cacert.pem, and
     * disable host verification. Please don't do this unless you really know what you're doing, and why you're doing
     * it.
     *
     * @param string|bool $certificateAuthority bool, file path, or directory path
     * @param bool        $verifyPeer           FALSE to stop from verifying the peer's certificate.
     * @param int         $verifyHost           Set to 1 to check the existence of a common name in the SSL peer
     *                                          certificate. 2 to check the existence of a common name and also verify
     *                                          that it matches the hostname provided.
     * @return self
     */
    public function setSslVerification($certificateAuthority = true, $verifyPeer = true, $verifyHost = 2);
}
vendor/guzzle/guzzle/src/Guzzle/Http/EntityBodyInterface.php000064400000004563151327705700020332 0ustar00<?php

namespace Guzzle\Http;

use Guzzle\Stream\StreamInterface;

/**
 * Entity body used with an HTTP request or response
 */
interface EntityBodyInterface extends StreamInterface
{
    /**
     * Specify a custom callback used to rewind a non-seekable stream. This can be useful entity enclosing requests
     * that are redirected.
     *
     * @param mixed $callable Callable to invoke to rewind a non-seekable stream. The callback must accept an
     *                        EntityBodyInterface object, perform the rewind if possible, and return a boolean
     *                        representing whether or not the rewind was successful.
     * @return self
     */
    public function setRewindFunction($callable);

    /**
     * If the stream is readable, compress the data in the stream using deflate compression. The uncompressed stream is
     * then closed, and the compressed stream then becomes the wrapped stream.
     *
     * @param string $filter Compression filter
     *
     * @return bool Returns TRUE on success or FALSE on failure
     */
    public function compress($filter = 'zlib.deflate');

    /**
     * Decompress a deflated string. Once uncompressed, the uncompressed string is then used as the wrapped stream.
     *
     * @param string $filter De-compression filter
     *
     * @return bool Returns TRUE on success or FALSE on failure
     */
    public function uncompress($filter = 'zlib.inflate');

    /**
     * Get the Content-Length of the entity body if possible (alias of getSize)
     *
     * @return int|bool Returns the Content-Length or false on failure
     */
    public function getContentLength();

    /**
     * Guess the Content-Type of a local stream
     *
     * @return string|null
     * @see http://www.php.net/manual/en/function.finfo-open.php
     */
    public function getContentType();

    /**
     * Get an MD5 checksum of the stream's contents
     *
     * @param bool $rawOutput    Whether or not to use raw output
     * @param bool $base64Encode Whether or not to base64 encode raw output (only if raw output is true)
     *
     * @return bool|string Returns an MD5 string on success or FALSE on failure
     */
    public function getContentMd5($rawOutput = false, $base64Encode = false);

    /**
     * Get the Content-Encoding of the EntityBody
     *
     * @return bool|string
     */
    public function getContentEncoding();
}
vendor/guzzle/guzzle/src/Guzzle/Http/QueryString.php000064400000021052151327705700016703 0ustar00<?php

namespace Guzzle\Http;

use Guzzle\Common\Collection;
use Guzzle\Common\Exception\RuntimeException;
use Guzzle\Http\QueryAggregator\DuplicateAggregator;
use Guzzle\Http\QueryAggregator\QueryAggregatorInterface;
use Guzzle\Http\QueryAggregator\PhpAggregator;

/**
 * Query string object to handle managing query string parameters and aggregating those parameters together as a string.
 */
class QueryString extends Collection
{
    /** @var string Used to URL encode with rawurlencode */
    const RFC_3986 = 'RFC 3986';

    /** @var string Used to encode with urlencode */
    const FORM_URLENCODED = 'application/x-www-form-urlencoded';

    /** @var string Constant used to create blank query string values (e.g. ?foo) */
    const BLANK = "_guzzle_blank_";

    /** @var string The query string field separator (e.g. '&') */
    protected $fieldSeparator = '&';

    /** @var string The query string value separator (e.g. '=') */
    protected $valueSeparator = '=';

    /** @var bool URL encode fields and values */
    protected $urlEncode = 'RFC 3986';

    /** @var QueryAggregatorInterface */
    protected $aggregator;

    /** @var array Cached PHP aggregator */
    private static $defaultAggregator = null;

    /**
     * Parse a query string into a QueryString object
     *
     * @param string $query Query string to parse
     *
     * @return self
     */
    public static function fromString($query)
    {
        $q = new static();
        if ($query === '') {
            return $q;
        }

        $foundDuplicates = $foundPhpStyle = false;

        foreach (explode('&', $query) as $kvp) {
            $parts = explode('=', $kvp, 2);
            $key = rawurldecode($parts[0]);
            if ($paramIsPhpStyleArray = substr($key, -2) == '[]') {
                $foundPhpStyle = true;
                $key = substr($key, 0, -2);
            }
            if (isset($parts[1])) {
                $value = rawurldecode(str_replace('+', '%20', $parts[1]));
                if (isset($q[$key])) {
                    $q->add($key, $value);
                    $foundDuplicates = true;
                } elseif ($paramIsPhpStyleArray) {
                    $q[$key] = array($value);
                } else {
                    $q[$key] = $value;
                }
            } else {
                // Uses false by default to represent keys with no trailing "=" sign.
                $q->add($key, false);
            }
        }

        // Use the duplicate aggregator if duplicates were found and not using PHP style arrays
        if ($foundDuplicates && !$foundPhpStyle) {
            $q->setAggregator(new DuplicateAggregator());
        }

        return $q;
    }

    /**
     * Convert the query string parameters to a query string string
     *
     * @return string
     * @throws RuntimeException
     */
    public function __toString()
    {
        if (!$this->data) {
            return '';
        }

        $queryList = array();
        foreach ($this->prepareData($this->data) as $name => $value) {
            $queryList[] = $this->convertKvp($name, $value);
        }

        return implode($this->fieldSeparator, $queryList);
    }

    /**
     * Get the query string field separator
     *
     * @return string
     */
    public function getFieldSeparator()
    {
        return $this->fieldSeparator;
    }

    /**
     * Get the query string value separator
     *
     * @return string
     */
    public function getValueSeparator()
    {
        return $this->valueSeparator;
    }

    /**
     * Returns the type of URL encoding used by the query string
     *
     * One of: false, "RFC 3986", or "application/x-www-form-urlencoded"
     *
     * @return bool|string
     */
    public function getUrlEncoding()
    {
        return $this->urlEncode;
    }

    /**
     * Returns true or false if using URL encoding
     *
     * @return bool
     */
    public function isUrlEncoding()
    {
        return $this->urlEncode !== false;
    }

    /**
     * Provide a function for combining multi-valued query string parameters into a single or multiple fields
     *
     * @param null|QueryAggregatorInterface $aggregator Pass in a QueryAggregatorInterface object to handle converting
     *                                                  deeply nested query string variables into a flattened array.
     *                                                  Pass null to use the default PHP style aggregator. For legacy
     *                                                  reasons, this function accepts a callable that must accepts a
     *                                                  $key, $value, and query object.
     * @return self
     * @see \Guzzle\Http\QueryString::aggregateUsingComma()
     */
    public function setAggregator(QueryAggregatorInterface $aggregator = null)
    {
        // Use the default aggregator if none was set
        if (!$aggregator) {
            if (!self::$defaultAggregator) {
                self::$defaultAggregator = new PhpAggregator();
            }
            $aggregator = self::$defaultAggregator;
        }

        $this->aggregator = $aggregator;

        return $this;
    }

    /**
     * Set whether or not field names and values should be rawurlencoded
     *
     * @param bool|string $encode Set to TRUE to use RFC 3986 encoding (rawurlencode), false to disable encoding, or
     *                            form_urlencoding to use application/x-www-form-urlencoded encoding (urlencode)
     * @return self
     */
    public function useUrlEncoding($encode)
    {
        $this->urlEncode = ($encode === true) ? self::RFC_3986 : $encode;

        return $this;
    }

    /**
     * Set the query string separator
     *
     * @param string $separator The query string separator that will separate fields
     *
     * @return self
     */
    public function setFieldSeparator($separator)
    {
        $this->fieldSeparator = $separator;

        return $this;
    }

    /**
     * Set the query string value separator
     *
     * @param string $separator The query string separator that will separate values from fields
     *
     * @return self
     */
    public function setValueSeparator($separator)
    {
        $this->valueSeparator = $separator;

        return $this;
    }

    /**
     * Returns an array of url encoded field names and values
     *
     * @return array
     */
    public function urlEncode()
    {
        return $this->prepareData($this->data);
    }

    /**
     * URL encodes a value based on the url encoding type of the query string object
     *
     * @param string $value Value to encode
     *
     * @return string
     */
    public function encodeValue($value)
    {
        if ($this->urlEncode == self::RFC_3986) {
            return rawurlencode($value);
        } elseif ($this->urlEncode == self::FORM_URLENCODED) {
            return urlencode($value);
        } else {
            return (string) $value;
        }
    }

    /**
     * Url encode parameter data and convert nested query strings into a flattened hash.
     *
     * @param array $data The data to encode
     *
     * @return array Returns an array of encoded values and keys
     */
    protected function prepareData(array $data)
    {
        // If no aggregator is present then set the default
        if (!$this->aggregator) {
            $this->setAggregator(null);
        }

        $temp = array();
        foreach ($data as $key => $value) {
            if ($value === false || $value === null) {
                // False and null will not include the "=". Use an empty string to include the "=".
                $temp[$this->encodeValue($key)] = $value;
            } elseif (is_array($value)) {
                $temp = array_merge($temp, $this->aggregator->aggregate($key, $value, $this));
            } else {
                $temp[$this->encodeValue($key)] = $this->encodeValue($value);
            }
        }

        return $temp;
    }

    /**
     * Converts a key value pair that can contain strings, nulls, false, or arrays
     * into a single string.
     *
     * @param string $name  Name of the field
     * @param mixed  $value Value of the field
     * @return string
     */
    private function convertKvp($name, $value)
    {
        if ($value === self::BLANK || $value === null || $value === false) {
            return $name;
        } elseif (!is_array($value)) {
            return $name . $this->valueSeparator . $value;
        }

        $result = '';
        foreach ($value as $v) {
            $result .= $this->convertKvp($name, $v) . $this->fieldSeparator;
        }

        return rtrim($result, $this->fieldSeparator);
    }
}
vendor/guzzle/guzzle/src/Guzzle/Http/Resources/cacert.pem000064400000634342151327705700017630 0ustar00##
## Bundle of CA Root Certificates
##
## Certificate data from Mozilla as of: Fri Mar 18 12:29:51 2022 GMT
##
## This is a bundle of X.509 certificates of public Certificate Authorities
## (CA). These were automatically extracted from Mozilla's root certificates
## file (certdata.txt).  This file can be found in the mozilla source tree:
## https://hg.mozilla.org/releases/mozilla-release/raw-file/default/security/nss/lib/ckfw/builtins/certdata.txt
##
## It contains the certificates in PEM format and therefore
## can be directly used with curl / libcurl / php_curl, or with
## an Apache+mod_ssl webserver for SSL client authentication.
## Just configure this file as the SSLCACertificateFile.
##
## Conversion done with mk-ca-bundle.pl version 1.29.
## SHA256: 187ef9dc231135324fe78830cf4462f1ecdeab3e6c9d5e38d623391e88dc5d3c
##


GlobalSign Root CA
==================
-----BEGIN CERTIFICATE-----
MIIDdTCCAl2gAwIBAgILBAAAAAABFUtaw5QwDQYJKoZIhvcNAQEFBQAwVzELMAkGA1UEBhMCQkUx
GTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsTB1Jvb3QgQ0ExGzAZBgNVBAMTEkds
b2JhbFNpZ24gUm9vdCBDQTAeFw05ODA5MDExMjAwMDBaFw0yODAxMjgxMjAwMDBaMFcxCzAJBgNV
BAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMRAwDgYDVQQLEwdSb290IENBMRswGQYD
VQQDExJHbG9iYWxTaWduIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDa
DuaZjc6j40+Kfvvxi4Mla+pIH/EqsLmVEQS98GPR4mdmzxzdzxtIK+6NiY6arymAZavpxy0Sy6sc
THAHoT0KMM0VjU/43dSMUBUc71DuxC73/OlS8pF94G3VNTCOXkNz8kHp1Wrjsok6Vjk4bwY8iGlb
Kk3Fp1S4bInMm/k8yuX9ifUSPJJ4ltbcdG6TRGHRjcdGsnUOhugZitVtbNV4FpWi6cgKOOvyJBNP
c1STE4U6G7weNLWLBYy5d4ux2x8gkasJU26Qzns3dLlwR5EiUWMWea6xrkEmCMgZK9FGqkjWZCrX
gzT/LCrBbBlDSgeF59N89iFo7+ryUp9/k5DPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV
HRMBAf8EBTADAQH/MB0GA1UdDgQWBBRge2YaRQ2XyolQL30EzTSo//z9SzANBgkqhkiG9w0BAQUF
AAOCAQEA1nPnfE920I2/7LqivjTFKDK1fPxsnCwrvQmeU79rXqoRSLblCKOzyj1hTdNGCbM+w6Dj
Y1Ub8rrvrTnhQ7k4o+YviiY776BQVvnGCv04zcQLcFGUl5gE38NflNUVyRRBnMRddWQVDf9VMOyG
j/8N7yy5Y0b2qvzfvGn9LhJIZJrglfCm7ymPAbEVtQwdpf5pLGkkeB6zpxxxYu7KyJesF12KwvhH
hm4qxFYxldBniYUr+WymXUadDKqC5JlR3XC321Y9YeRq4VzW9v493kHMB65jUr9TU/Qr6cf9tveC
X4XSQRjbgbMEHMUfpIBvFSDJ3gyICh3WZlXi/EjJKSZp4A==
-----END CERTIFICATE-----

Entrust.net Premium 2048 Secure Server CA
=========================================
-----BEGIN CERTIFICATE-----
MIIEKjCCAxKgAwIBAgIEOGPe+DANBgkqhkiG9w0BAQUFADCBtDEUMBIGA1UEChMLRW50cnVzdC5u
ZXQxQDA+BgNVBAsUN3d3dy5lbnRydXN0Lm5ldC9DUFNfMjA0OCBpbmNvcnAuIGJ5IHJlZi4gKGxp
bWl0cyBsaWFiLikxJTAjBgNVBAsTHChjKSAxOTk5IEVudHJ1c3QubmV0IExpbWl0ZWQxMzAxBgNV
BAMTKkVudHJ1c3QubmV0IENlcnRpZmljYXRpb24gQXV0aG9yaXR5ICgyMDQ4KTAeFw05OTEyMjQx
NzUwNTFaFw0yOTA3MjQxNDE1MTJaMIG0MRQwEgYDVQQKEwtFbnRydXN0Lm5ldDFAMD4GA1UECxQ3
d3d3LmVudHJ1c3QubmV0L0NQU18yMDQ4IGluY29ycC4gYnkgcmVmLiAobGltaXRzIGxpYWIuKTEl
MCMGA1UECxMcKGMpIDE5OTkgRW50cnVzdC5uZXQgTGltaXRlZDEzMDEGA1UEAxMqRW50cnVzdC5u
ZXQgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgKDIwNDgpMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
MIIBCgKCAQEArU1LqRKGsuqjIAcVFmQqK0vRvwtKTY7tgHalZ7d4QMBzQshowNtTK91euHaYNZOL
Gp18EzoOH1u3Hs/lJBQesYGpjX24zGtLA/ECDNyrpUAkAH90lKGdCCmziAv1h3edVc3kw37XamSr
hRSGlVuXMlBvPci6Zgzj/L24ScF2iUkZ/cCovYmjZy/Gn7xxGWC4LeksyZB2ZnuU4q941mVTXTzW
nLLPKQP5L6RQstRIzgUyVYr9smRMDuSYB3Xbf9+5CFVghTAp+XtIpGmG4zU/HoZdenoVve8AjhUi
VBcAkCaTvA5JaJG/+EfTnZVCwQ5N328mz8MYIWJmQ3DW1cAH4QIDAQABo0IwQDAOBgNVHQ8BAf8E
BAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUVeSB0RGAvtiJuQijMfmhJAkWuXAwDQYJ
KoZIhvcNAQEFBQADggEBADubj1abMOdTmXx6eadNl9cZlZD7Bh/KM3xGY4+WZiT6QBshJ8rmcnPy
T/4xmf3IDExoU8aAghOY+rat2l098c5u9hURlIIM7j+VrxGrD9cv3h8Dj1csHsm7mhpElesYT6Yf
zX1XEC+bBAlahLVu2B064dae0Wx5XnkcFMXj0EyTO2U87d89vqbllRrDtRnDvV5bu/8j72gZyxKT
J1wDLW8w0B62GqzeWvfRqqgnpv55gcR5mTNXuhKwqeBCbJPKVt7+bYQLCIt+jerXmCHG8+c8eS9e
nNFMFY3h7CI3zJpDC5fcgJCNs2ebb0gIFVbPv/ErfF6adulZkMV8gzURZVE=
-----END CERTIFICATE-----

Baltimore CyberTrust Root
=========================
-----BEGIN CERTIFICATE-----
MIIDdzCCAl+gAwIBAgIEAgAAuTANBgkqhkiG9w0BAQUFADBaMQswCQYDVQQGEwJJRTESMBAGA1UE
ChMJQmFsdGltb3JlMRMwEQYDVQQLEwpDeWJlclRydXN0MSIwIAYDVQQDExlCYWx0aW1vcmUgQ3li
ZXJUcnVzdCBSb290MB4XDTAwMDUxMjE4NDYwMFoXDTI1MDUxMjIzNTkwMFowWjELMAkGA1UEBhMC
SUUxEjAQBgNVBAoTCUJhbHRpbW9yZTETMBEGA1UECxMKQ3liZXJUcnVzdDEiMCAGA1UEAxMZQmFs
dGltb3JlIEN5YmVyVHJ1c3QgUm9vdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKME
uyKrmD1X6CZymrV51Cni4eiVgLGw41uOKymaZN+hXe2wCQVt2yguzmKiYv60iNoS6zjrIZ3AQSsB
UnuId9Mcj8e6uYi1agnnc+gRQKfRzMpijS3ljwumUNKoUMMo6vWrJYeKmpYcqWe4PwzV9/lSEy/C
G9VwcPCPwBLKBsua4dnKM3p31vjsufFoREJIE9LAwqSuXmD+tqYF/LTdB1kC1FkYmGP1pWPgkAx9
XbIGevOF6uvUA65ehD5f/xXtabz5OTZydc93Uk3zyZAsuT3lySNTPx8kmCFcB5kpvcY67Oduhjpr
l3RjM71oGDHweI12v/yejl0qhqdNkNwnGjkCAwEAAaNFMEMwHQYDVR0OBBYEFOWdWTCCR1jMrPoI
VDaGezq1BE3wMBIGA1UdEwEB/wQIMAYBAf8CAQMwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEB
BQUAA4IBAQCFDF2O5G9RaEIFoN27TyclhAO992T9Ldcw46QQF+vaKSm2eT929hkTI7gQCvlYpNRh
cL0EYWoSihfVCr3FvDB81ukMJY2GQE/szKN+OMY3EU/t3WgxjkzSswF07r51XgdIGn9w/xZchMB5
hbgF/X++ZRGjD8ACtPhSNzkE1akxehi/oCr0Epn3o0WC4zxe9Z2etciefC7IpJ5OCBRLbf1wbWsa
Y71k5h+3zvDyny67G7fyUIhzksLi4xaNmjICq44Y3ekQEe5+NauQrz4wlHrQMz2nZQ/1/I6eYs9H
RCwBXbsdtTLSR9I4LtD+gdwyah617jzV/OeBHRnDJELqYzmp
-----END CERTIFICATE-----

Entrust Root Certification Authority
====================================
-----BEGIN CERTIFICATE-----
MIIEkTCCA3mgAwIBAgIERWtQVDANBgkqhkiG9w0BAQUFADCBsDELMAkGA1UEBhMCVVMxFjAUBgNV
BAoTDUVudHJ1c3QsIEluYy4xOTA3BgNVBAsTMHd3dy5lbnRydXN0Lm5ldC9DUFMgaXMgaW5jb3Jw
b3JhdGVkIGJ5IHJlZmVyZW5jZTEfMB0GA1UECxMWKGMpIDIwMDYgRW50cnVzdCwgSW5jLjEtMCsG
A1UEAxMkRW50cnVzdCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTA2MTEyNzIwMjM0
MloXDTI2MTEyNzIwNTM0MlowgbAxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1FbnRydXN0LCBJbmMu
MTkwNwYDVQQLEzB3d3cuZW50cnVzdC5uZXQvQ1BTIGlzIGluY29ycG9yYXRlZCBieSByZWZlcmVu
Y2UxHzAdBgNVBAsTFihjKSAyMDA2IEVudHJ1c3QsIEluYy4xLTArBgNVBAMTJEVudHJ1c3QgUm9v
dCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB
ALaVtkNC+sZtKm9I35RMOVcF7sN5EUFoNu3s/poBj6E4KPz3EEZmLk0eGrEaTsbRwJWIsMn/MYsz
A9u3g3s+IIRe7bJWKKf44LlAcTfFy0cOlypowCKVYhXbR9n10Cv/gkvJrT7eTNuQgFA/CYqEAOww
Cj0Yzfv9KlmaI5UXLEWeH25DeW0MXJj+SKfFI0dcXv1u5x609mhF0YaDW6KKjbHjKYD+JXGIrb68
j6xSlkuqUY3kEzEZ6E5Nn9uss2rVvDlUccp6en+Q3X0dgNmBu1kmwhH+5pPi94DkZfs0Nw4pgHBN
rziGLp5/V6+eF67rHMsoIV+2HNjnogQi+dPa2MsCAwEAAaOBsDCBrTAOBgNVHQ8BAf8EBAMCAQYw
DwYDVR0TAQH/BAUwAwEB/zArBgNVHRAEJDAigA8yMDA2MTEyNzIwMjM0MlqBDzIwMjYxMTI3MjA1
MzQyWjAfBgNVHSMEGDAWgBRokORnpKZTgMeGZqTx90tD+4S9bTAdBgNVHQ4EFgQUaJDkZ6SmU4DH
hmak8fdLQ/uEvW0wHQYJKoZIhvZ9B0EABBAwDhsIVjcuMTo0LjADAgSQMA0GCSqGSIb3DQEBBQUA
A4IBAQCT1DCw1wMgKtD5Y+iRDAUgqV8ZyntyTtSx29CW+1RaGSwMCPeyvIWonX9tO1KzKtvn1ISM
Y/YPyyYBkVBs9F8U4pN0wBOeMDpQ47RgxRzwIkSNcUesyBrJ6ZuaAGAT/3B+XxFNSRuzFVJ7yVTa
v52Vr2ua2J7p8eRDjeIRRDq/r72DQnNSi6q7pynP9WQcCk3RvKqsnyrQ/39/2n3qse0wJcGE2jTS
W3iDVuycNsMm4hH2Z0kdkquM++v/eu6FSqdQgPCnXEqULl8FmTxSQeDNtGPPAUO6nIPcj2A781q0
tHuu2guQOHXvgR1m0vdXcDazv/wor3ElhVsT/h5/WrQ8
-----END CERTIFICATE-----

Comodo AAA Services root
========================
-----BEGIN CERTIFICATE-----
MIIEMjCCAxqgAwIBAgIBATANBgkqhkiG9w0BAQUFADB7MQswCQYDVQQGEwJHQjEbMBkGA1UECAwS
R3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHDAdTYWxmb3JkMRowGAYDVQQKDBFDb21vZG8gQ0Eg
TGltaXRlZDEhMB8GA1UEAwwYQUFBIENlcnRpZmljYXRlIFNlcnZpY2VzMB4XDTA0MDEwMTAwMDAw
MFoXDTI4MTIzMTIzNTk1OVowezELMAkGA1UEBhMCR0IxGzAZBgNVBAgMEkdyZWF0ZXIgTWFuY2hl
c3RlcjEQMA4GA1UEBwwHU2FsZm9yZDEaMBgGA1UECgwRQ29tb2RvIENBIExpbWl0ZWQxITAfBgNV
BAMMGEFBQSBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
ggEBAL5AnfRu4ep2hxxNRUSOvkbIgwadwSr+GB+O5AL686tdUIoWMQuaBtDFcCLNSS1UY8y2bmhG
C1Pqy0wkwLxyTurxFa70VJoSCsN6sjNg4tqJVfMiWPPe3M/vg4aijJRPn2jymJBGhCfHdr/jzDUs
i14HZGWCwEiwqJH5YZ92IFCokcdmtet4YgNW8IoaE+oxox6gmf049vYnMlhvB/VruPsUK6+3qszW
Y19zjNoFmag4qMsXeDZRrOme9Hg6jc8P2ULimAyrL58OAd7vn5lJ8S3frHRNG5i1R8XlKdH5kBjH
Ypy+g8cmez6KJcfA3Z3mNWgQIJ2P2N7Sw4ScDV7oL8kCAwEAAaOBwDCBvTAdBgNVHQ4EFgQUoBEK
Iz6W8Qfs4q8p74Klf9AwpLQwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wewYDVR0f
BHQwcjA4oDagNIYyaHR0cDovL2NybC5jb21vZG9jYS5jb20vQUFBQ2VydGlmaWNhdGVTZXJ2aWNl
cy5jcmwwNqA0oDKGMGh0dHA6Ly9jcmwuY29tb2RvLm5ldC9BQUFDZXJ0aWZpY2F0ZVNlcnZpY2Vz
LmNybDANBgkqhkiG9w0BAQUFAAOCAQEACFb8AvCb6P+k+tZ7xkSAzk/ExfYAWMymtrwUSWgEdujm
7l3sAg9g1o1QGE8mTgHj5rCl7r+8dFRBv/38ErjHT1r0iWAFf2C3BUrz9vHCv8S5dIa2LX1rzNLz
Rt0vxuBqw8M0Ayx9lt1awg6nCpnBBYurDC/zXDrPbDdVCYfeU0BsWO/8tqtlbgT2G9w84FoVxp7Z
8VlIMCFlA2zs6SFz7JsDoeA3raAVGI/6ugLOpyypEBMs1OUIJqsil2D4kF501KKaU73yqWjgom7C
12yxow+ev+to51byrvLjKzg6CYG1a4XXvi3tPxq3smPi9WIsgtRqAEFQ8TmDn5XpNpaYbg==
-----END CERTIFICATE-----

QuoVadis Root CA 2
==================
-----BEGIN CERTIFICATE-----
MIIFtzCCA5+gAwIBAgICBQkwDQYJKoZIhvcNAQEFBQAwRTELMAkGA1UEBhMCQk0xGTAXBgNVBAoT
EFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMTElF1b1ZhZGlzIFJvb3QgQ0EgMjAeFw0wNjExMjQx
ODI3MDBaFw0zMTExMjQxODIzMzNaMEUxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBM
aW1pdGVkMRswGQYDVQQDExJRdW9WYWRpcyBSb290IENBIDIwggIiMA0GCSqGSIb3DQEBAQUAA4IC
DwAwggIKAoICAQCaGMpLlA0ALa8DKYrwD4HIrkwZhR0In6spRIXzL4GtMh6QRr+jhiYaHv5+HBg6
XJxgFyo6dIMzMH1hVBHL7avg5tKifvVrbxi3Cgst/ek+7wrGsxDp3MJGF/hd/aTa/55JWpzmM+Yk
lvc/ulsrHHo1wtZn/qtmUIttKGAr79dgw8eTvI02kfN/+NsRE8Scd3bBrrcCaoF6qUWD4gXmuVbB
lDePSHFjIuwXZQeVikvfj8ZaCuWw419eaxGrDPmF60Tp+ARz8un+XJiM9XOva7R+zdRcAitMOeGy
lZUtQofX1bOQQ7dsE/He3fbE+Ik/0XX1ksOR1YqI0JDs3G3eicJlcZaLDQP9nL9bFqyS2+r+eXyt
66/3FsvbzSUr5R/7mp/iUcw6UwxI5g69ybR2BlLmEROFcmMDBOAENisgGQLodKcftslWZvB1Jdxn
wQ5hYIizPtGo/KPaHbDRsSNU30R2be1B2MGyIrZTHN81Hdyhdyox5C315eXbyOD/5YDXC2Og/zOh
D7osFRXql7PSorW+8oyWHhqPHWykYTe5hnMz15eWniN9gqRMgeKh0bpnX5UHoycR7hYQe7xFSkyy
BNKr79X9DFHOUGoIMfmR2gyPZFwDwzqLID9ujWc9Otb+fVuIyV77zGHcizN300QyNQliBJIWENie
J0f7OyHj+OsdWwIDAQABo4GwMIGtMA8GA1UdEwEB/wQFMAMBAf8wCwYDVR0PBAQDAgEGMB0GA1Ud
DgQWBBQahGK8SEwzJQTU7tD2A8QZRtGUazBuBgNVHSMEZzBlgBQahGK8SEwzJQTU7tD2A8QZRtGU
a6FJpEcwRTELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMT
ElF1b1ZhZGlzIFJvb3QgQ0EgMoICBQkwDQYJKoZIhvcNAQEFBQADggIBAD4KFk2fBluornFdLwUv
Z+YTRYPENvbzwCYMDbVHZF34tHLJRqUDGCdViXh9duqWNIAXINzng/iN/Ae42l9NLmeyhP3ZRPx3
UIHmfLTJDQtyU/h2BwdBR5YM++CCJpNVjP4iH2BlfF/nJrP3MpCYUNQ3cVX2kiF495V5+vgtJodm
VjB3pjd4M1IQWK4/YY7yarHvGH5KWWPKjaJW1acvvFYfzznB4vsKqBUsfU16Y8Zsl0Q80m/DShcK
+JDSV6IZUaUtl0HaB0+pUNqQjZRG4T7wlP0QADj1O+hA4bRuVhogzG9Yje0uRY/W6ZM/57Es3zrW
IozchLsib9D45MY56QSIPMO661V6bYCZJPVsAfv4l7CUW+v90m/xd2gNNWQjrLhVoQPRTUIZ3Ph1
WVaj+ahJefivDrkRoHy3au000LYmYjgahwz46P0u05B/B5EqHdZ+XIWDmbA4CD/pXvk1B+TJYm5X
f6dQlfe6yJvmjqIBxdZmv3lh8zwc4bmCXF2gw+nYSL0ZohEUGW6yhhtoPkg3Goi3XZZenMfvJ2II
4pEZXNLxId26F0KCl3GBUzGpn/Z9Yr9y4aOTHcyKJloJONDO1w2AFrR4pTqHTI2KpdVGl/IsELm8
VCLAAVBpQ570su9t+Oza8eOx79+Rj1QqCyXBJhnEUhAFZdWCEOrCMc0u
-----END CERTIFICATE-----

QuoVadis Root CA 3
==================
-----BEGIN CERTIFICATE-----
MIIGnTCCBIWgAwIBAgICBcYwDQYJKoZIhvcNAQEFBQAwRTELMAkGA1UEBhMCQk0xGTAXBgNVBAoT
EFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMTElF1b1ZhZGlzIFJvb3QgQ0EgMzAeFw0wNjExMjQx
OTExMjNaFw0zMTExMjQxOTA2NDRaMEUxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBM
aW1pdGVkMRswGQYDVQQDExJRdW9WYWRpcyBSb290IENBIDMwggIiMA0GCSqGSIb3DQEBAQUAA4IC
DwAwggIKAoICAQDMV0IWVJzmmNPTTe7+7cefQzlKZbPoFog02w1ZkXTPkrgEQK0CSzGrvI2RaNgg
DhoB4hp7Thdd4oq3P5kazethq8Jlph+3t723j/z9cI8LoGe+AaJZz3HmDyl2/7FWeUUrH556VOij
KTVopAFPD6QuN+8bv+OPEKhyq1hX51SGyMnzW9os2l2ObjyjPtr7guXd8lyyBTNvijbO0BNO/79K
DDRMpsMhvVAEVeuxu537RR5kFd5VAYwCdrXLoT9CabwvvWhDFlaJKjdhkf2mrk7AyxRllDdLkgbv
BNDInIjbC3uBr7E9KsRlOni27tyAsdLTmZw67mtaa7ONt9XOnMK+pUsvFrGeaDsGb659n/je7Mwp
p5ijJUMv7/FfJuGITfhebtfZFG4ZM2mnO4SJk8RTVROhUXhA+LjJou57ulJCg54U7QVSWllWp5f8
nT8KKdjcT5EOE7zelaTfi5m+rJsziO+1ga8bxiJTyPbH7pcUsMV8eFLI8M5ud2CEpukqdiDtWAEX
MJPpGovgc2PZapKUSU60rUqFxKMiMPwJ7Wgic6aIDFUhWMXhOp8q3crhkODZc6tsgLjoC2SToJyM
Gf+z0gzskSaHirOi4XCPLArlzW1oUevaPwV/izLmE1xr/l9A4iLItLRkT9a6fUg+qGkM17uGcclz
uD87nSVL2v9A6wIDAQABo4IBlTCCAZEwDwYDVR0TAQH/BAUwAwEB/zCB4QYDVR0gBIHZMIHWMIHT
BgkrBgEEAb5YAAMwgcUwgZMGCCsGAQUFBwICMIGGGoGDQW55IHVzZSBvZiB0aGlzIENlcnRpZmlj
YXRlIGNvbnN0aXR1dGVzIGFjY2VwdGFuY2Ugb2YgdGhlIFF1b1ZhZGlzIFJvb3QgQ0EgMyBDZXJ0
aWZpY2F0ZSBQb2xpY3kgLyBDZXJ0aWZpY2F0aW9uIFByYWN0aWNlIFN0YXRlbWVudC4wLQYIKwYB
BQUHAgEWIWh0dHA6Ly93d3cucXVvdmFkaXNnbG9iYWwuY29tL2NwczALBgNVHQ8EBAMCAQYwHQYD
VR0OBBYEFPLAE+CCQz777i9nMpY1XNu4ywLQMG4GA1UdIwRnMGWAFPLAE+CCQz777i9nMpY1XNu4
ywLQoUmkRzBFMQswCQYDVQQGEwJCTTEZMBcGA1UEChMQUXVvVmFkaXMgTGltaXRlZDEbMBkGA1UE
AxMSUXVvVmFkaXMgUm9vdCBDQSAzggIFxjANBgkqhkiG9w0BAQUFAAOCAgEAT62gLEz6wPJv92ZV
qyM07ucp2sNbtrCD2dDQ4iH782CnO11gUyeim/YIIirnv6By5ZwkajGxkHon24QRiSemd1o417+s
hvzuXYO8BsbRd2sPbSQvS3pspweWyuOEn62Iix2rFo1bZhfZFvSLgNLd+LJ2w/w4E6oM3kJpK27z
POuAJ9v1pkQNn1pVWQvVDVJIxa6f8i+AxeoyUDUSly7B4f/xI4hROJ/yZlZ25w9Rl6VSDE1JUZU2
Pb+iSwwQHYaZTKrzchGT5Or2m9qoXadNt54CrnMAyNojA+j56hl0YgCUyyIgvpSnWbWCar6ZeXqp
8kokUvd0/bpO5qgdAm6xDYBEwa7TIzdfu4V8K5Iu6H6li92Z4b8nby1dqnuH/grdS/yO9SbkbnBC
bjPsMZ57k8HkyWkaPcBrTiJt7qtYTcbQQcEr6k8Sh17rRdhs9ZgC06DYVYoGmRmioHfRMJ6szHXu
g/WwYjnPbFfiTNKRCw51KBuav/0aQ/HKd/s7j2G4aSgWQgRecCocIdiP4b0jWy10QJLZYxkNc91p
vGJHvOB0K7Lrfb5BG7XARsWhIstfTsEokt4YutUqKLsRixeTmJlglFwjz1onl14LBQaTNx47aTbr
qZ5hHY8y2o4M1nQ+ewkk2gF3R8Q7zTSMmfXK4SVhM7JZG+Ju1zdXtg2pEto=
-----END CERTIFICATE-----

Security Communication Root CA
==============================
-----BEGIN CERTIFICATE-----
MIIDWjCCAkKgAwIBAgIBADANBgkqhkiG9w0BAQUFADBQMQswCQYDVQQGEwJKUDEYMBYGA1UEChMP
U0VDT00gVHJ1c3QubmV0MScwJQYDVQQLEx5TZWN1cml0eSBDb21tdW5pY2F0aW9uIFJvb3RDQTEw
HhcNMDMwOTMwMDQyMDQ5WhcNMjMwOTMwMDQyMDQ5WjBQMQswCQYDVQQGEwJKUDEYMBYGA1UEChMP
U0VDT00gVHJ1c3QubmV0MScwJQYDVQQLEx5TZWN1cml0eSBDb21tdW5pY2F0aW9uIFJvb3RDQTEw
ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCzs/5/022x7xZ8V6UMbXaKL0u/ZPtM7orw
8yl89f/uKuDp6bpbZCKamm8sOiZpUQWZJtzVHGpxxpp9Hp3dfGzGjGdnSj74cbAZJ6kJDKaVv0uM
DPpVmDvY6CKhS3E4eayXkmmziX7qIWgGmBSWh9JhNrxtJ1aeV+7AwFb9Ms+k2Y7CI9eNqPPYJayX
5HA49LY6tJ07lyZDo6G8SVlyTCMwhwFY9k6+HGhWZq/NQV3Is00qVUarH9oe4kA92819uZKAnDfd
DJZkndwi92SL32HeFZRSFaB9UslLqCHJxrHty8OVYNEP8Ktw+N/LTX7s1vqr2b1/VPKl6Xn62dZ2
JChzAgMBAAGjPzA9MB0GA1UdDgQWBBSgc0mZaNyFW2XjmygvV5+9M7wHSDALBgNVHQ8EBAMCAQYw
DwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQUFAAOCAQEAaECpqLvkT115swW1F7NgE+vGkl3g
0dNq/vu+m22/xwVtWSDEHPC32oRYAmP6SBbvT6UL90qY8j+eG61Ha2POCEfrUj94nK9NrvjVT8+a
mCoQQTlSxN3Zmw7vkwGusi7KaEIkQmywszo+zenaSMQVy+n5Bw+SUEmK3TGXX8npN6o7WWWXlDLJ
s58+OmJYxUmtYg5xpTKqL8aJdkNAExNnPaJUJRDL8Try2frbSVa7pv6nQTXD4IhhyYjH3zYQIphZ
6rBK+1YWc26sTfcioU+tHXotRSflMMFe8toTyyVCUZVHA4xsIcx0Qu1T/zOLjw9XARYvz6buyXAi
FL39vmwLAw==
-----END CERTIFICATE-----

XRamp Global CA Root
====================
-----BEGIN CERTIFICATE-----
MIIEMDCCAxigAwIBAgIQUJRs7Bjq1ZxN1ZfvdY+grTANBgkqhkiG9w0BAQUFADCBgjELMAkGA1UE
BhMCVVMxHjAcBgNVBAsTFXd3dy54cmFtcHNlY3VyaXR5LmNvbTEkMCIGA1UEChMbWFJhbXAgU2Vj
dXJpdHkgU2VydmljZXMgSW5jMS0wKwYDVQQDEyRYUmFtcCBHbG9iYWwgQ2VydGlmaWNhdGlvbiBB
dXRob3JpdHkwHhcNMDQxMTAxMTcxNDA0WhcNMzUwMTAxMDUzNzE5WjCBgjELMAkGA1UEBhMCVVMx
HjAcBgNVBAsTFXd3dy54cmFtcHNlY3VyaXR5LmNvbTEkMCIGA1UEChMbWFJhbXAgU2VjdXJpdHkg
U2VydmljZXMgSW5jMS0wKwYDVQQDEyRYUmFtcCBHbG9iYWwgQ2VydGlmaWNhdGlvbiBBdXRob3Jp
dHkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCYJB69FbS638eMpSe2OAtp87ZOqCwu
IR1cRN8hXX4jdP5efrRKt6atH67gBhbim1vZZ3RrXYCPKZ2GG9mcDZhtdhAoWORlsH9KmHmf4MMx
foArtYzAQDsRhtDLooY2YKTVMIJt2W7QDxIEM5dfT2Fa8OT5kavnHTu86M/0ay00fOJIYRyO82FE
zG+gSqmUsE3a56k0enI4qEHMPJQRfevIpoy3hsvKMzvZPTeL+3o+hiznc9cKV6xkmxnr9A8ECIqs
AxcZZPRaJSKNNCyy9mgdEm3Tih4U2sSPpuIjhdV6Db1q4Ons7Be7QhtnqiXtRYMh/MHJfNViPvry
xS3T/dRlAgMBAAGjgZ8wgZwwEwYJKwYBBAGCNxQCBAYeBABDAEEwCwYDVR0PBAQDAgGGMA8GA1Ud
EwEB/wQFMAMBAf8wHQYDVR0OBBYEFMZPoj0GY4QJnM5i5ASsjVy16bYbMDYGA1UdHwQvMC0wK6Ap
oCeGJWh0dHA6Ly9jcmwueHJhbXBzZWN1cml0eS5jb20vWEdDQS5jcmwwEAYJKwYBBAGCNxUBBAMC
AQEwDQYJKoZIhvcNAQEFBQADggEBAJEVOQMBG2f7Shz5CmBbodpNl2L5JFMn14JkTpAuw0kbK5rc
/Kh4ZzXxHfARvbdI4xD2Dd8/0sm2qlWkSLoC295ZLhVbO50WfUfXN+pfTXYSNrsf16GBBEYgoyxt
qZ4Bfj8pzgCT3/3JknOJiWSe5yvkHJEs0rnOfc5vMZnT5r7SHpDwCRR5XCOrTdLaIR9NmXmd4c8n
nxCbHIgNsIpkQTG4DmyQJKSbXHGPurt+HBvbaoAPIbzp26a3QPSyi6mx5O+aGtA9aZnuqCij4Tyz
8LIRnM98QObd50N9otg6tamN8jSZxNQQ4Qb9CYQQO+7ETPTsJ3xCwnR8gooJybQDJbw=
-----END CERTIFICATE-----

Go Daddy Class 2 CA
===================
-----BEGIN CERTIFICATE-----
MIIEADCCAuigAwIBAgIBADANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJVUzEhMB8GA1UEChMY
VGhlIEdvIERhZGR5IEdyb3VwLCBJbmMuMTEwLwYDVQQLEyhHbyBEYWRkeSBDbGFzcyAyIENlcnRp
ZmljYXRpb24gQXV0aG9yaXR5MB4XDTA0MDYyOTE3MDYyMFoXDTM0MDYyOTE3MDYyMFowYzELMAkG
A1UEBhMCVVMxITAfBgNVBAoTGFRoZSBHbyBEYWRkeSBHcm91cCwgSW5jLjExMC8GA1UECxMoR28g
RGFkZHkgQ2xhc3MgMiBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCASAwDQYJKoZIhvcNAQEBBQAD
ggENADCCAQgCggEBAN6d1+pXGEmhW+vXX0iG6r7d/+TvZxz0ZWizV3GgXne77ZtJ6XCAPVYYYwhv
2vLM0D9/AlQiVBDYsoHUwHU9S3/Hd8M+eKsaA7Ugay9qK7HFiH7Eux6wwdhFJ2+qN1j3hybX2C32
qRe3H3I2TqYXP2WYktsqbl2i/ojgC95/5Y0V4evLOtXiEqITLdiOr18SPaAIBQi2XKVlOARFmR6j
YGB0xUGlcmIbYsUfb18aQr4CUWWoriMYavx4A6lNf4DD+qta/KFApMoZFv6yyO9ecw3ud72a9nmY
vLEHZ6IVDd2gWMZEewo+YihfukEHU1jPEX44dMX4/7VpkI+EdOqXG68CAQOjgcAwgb0wHQYDVR0O
BBYEFNLEsNKR1EwRcbNhyz2h/t2oatTjMIGNBgNVHSMEgYUwgYKAFNLEsNKR1EwRcbNhyz2h/t2o
atTjoWekZTBjMQswCQYDVQQGEwJVUzEhMB8GA1UEChMYVGhlIEdvIERhZGR5IEdyb3VwLCBJbmMu
MTEwLwYDVQQLEyhHbyBEYWRkeSBDbGFzcyAyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5ggEAMAwG
A1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBADJL87LKPpH8EsahB4yOd6AzBhRckB4Y9wim
PQoZ+YeAEW5p5JYXMP80kWNyOO7MHAGjHZQopDH2esRU1/blMVgDoszOYtuURXO1v0XJJLXVggKt
I3lpjbi2Tc7PTMozI+gciKqdi0FuFskg5YmezTvacPd+mSYgFFQlq25zheabIZ0KbIIOqPjCDPoQ
HmyW74cNxA9hi63ugyuV+I6ShHI56yDqg+2DzZduCLzrTia2cyvk0/ZM/iZx4mERdEr/VxqHD3VI
Ls9RaRegAhJhldXRQLIQTO7ErBBDpqWeCtWVYpoNz4iCxTIM5CufReYNnyicsbkqWletNw+vHX/b
vZ8=
-----END CERTIFICATE-----

Starfield Class 2 CA
====================
-----BEGIN CERTIFICATE-----
MIIEDzCCAvegAwIBAgIBADANBgkqhkiG9w0BAQUFADBoMQswCQYDVQQGEwJVUzElMCMGA1UEChMc
U3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAGA1UECxMpU3RhcmZpZWxkIENsYXNzIDIg
Q2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDQwNjI5MTczOTE2WhcNMzQwNjI5MTczOTE2WjBo
MQswCQYDVQQGEwJVUzElMCMGA1UEChMcU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAG
A1UECxMpU3RhcmZpZWxkIENsYXNzIDIgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggEgMA0GCSqG
SIb3DQEBAQUAA4IBDQAwggEIAoIBAQC3Msj+6XGmBIWtDBFk385N78gDGIc/oav7PKaf8MOh2tTY
bitTkPskpD6E8J7oX+zlJ0T1KKY/e97gKvDIr1MvnsoFAZMej2YcOadN+lq2cwQlZut3f+dZxkqZ
JRRU6ybH838Z1TBwj6+wRir/resp7defqgSHo9T5iaU0X9tDkYI22WY8sbi5gv2cOj4QyDvvBmVm
epsZGD3/cVE8MC5fvj13c7JdBmzDI1aaK4UmkhynArPkPw2vCHmCuDY96pzTNbO8acr1zJ3o/WSN
F4Azbl5KXZnJHoe0nRrA1W4TNSNe35tfPe/W93bC6j67eA0cQmdrBNj41tpvi/JEoAGrAgEDo4HF
MIHCMB0GA1UdDgQWBBS/X7fRzt0fhvRbVazc1xDCDqmI5zCBkgYDVR0jBIGKMIGHgBS/X7fRzt0f
hvRbVazc1xDCDqmI56FspGowaDELMAkGA1UEBhMCVVMxJTAjBgNVBAoTHFN0YXJmaWVsZCBUZWNo
bm9sb2dpZXMsIEluYy4xMjAwBgNVBAsTKVN0YXJmaWVsZCBDbGFzcyAyIENlcnRpZmljYXRpb24g
QXV0aG9yaXR5ggEAMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAAWdP4id0ckaVaGs
afPzWdqbAYcaT1epoXkJKtv3L7IezMdeatiDh6GX70k1PncGQVhiv45YuApnP+yz3SFmH8lU+nLM
PUxA2IGvd56Deruix/U0F47ZEUD0/CwqTRV/p2JdLiXTAAsgGh1o+Re49L2L7ShZ3U0WixeDyLJl
xy16paq8U4Zt3VekyvggQQto8PT7dL5WXXp59fkdheMtlb71cZBDzI0fmgAKhynpVSJYACPq4xJD
KVtHCN2MQWplBqjlIapBtJUhlbl90TSrE9atvNziPTnNvT51cKEYWQPJIrSPnNVeKtelttQKbfi3
QBFGmh95DmK/D5fs4C8fF5Q=
-----END CERTIFICATE-----

DigiCert Assured ID Root CA
===========================
-----BEGIN CERTIFICATE-----
MIIDtzCCAp+gAwIBAgIQDOfg5RfYRv6P5WD8G/AwOTANBgkqhkiG9w0BAQUFADBlMQswCQYDVQQG
EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSQw
IgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgQ0EwHhcNMDYxMTEwMDAwMDAwWhcNMzEx
MTEwMDAwMDAwWjBlMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQL
ExB3d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgQ0Ew
ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCtDhXO5EOAXLGH87dg+XESpa7cJpSIqvTO
9SA5KFhgDPiA2qkVlTJhPLWxKISKityfCgyDF3qPkKyK53lTXDGEKvYPmDI2dsze3Tyoou9q+yHy
UmHfnyDXH+Kx2f4YZNISW1/5WBg1vEfNoTb5a3/UsDg+wRvDjDPZ2C8Y/igPs6eD1sNuRMBhNZYW
/lmci3Zt1/GiSw0r/wty2p5g0I6QNcZ4VYcgoc/lbQrISXwxmDNsIumH0DJaoroTghHtORedmTpy
oeb6pNnVFzF1roV9Iq4/AUaG9ih5yLHa5FcXxH4cDrC0kqZWs72yl+2qp/C3xag/lRbQ/6GW6whf
GHdPAgMBAAGjYzBhMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRF
66Kv9JLLgjEtUYunpyGd823IDzAfBgNVHSMEGDAWgBRF66Kv9JLLgjEtUYunpyGd823IDzANBgkq
hkiG9w0BAQUFAAOCAQEAog683+Lt8ONyc3pklL/3cmbYMuRCdWKuh+vy1dneVrOfzM4UKLkNl2Bc
EkxY5NM9g0lFWJc1aRqoR+pWxnmrEthngYTffwk8lOa4JiwgvT2zKIn3X/8i4peEH+ll74fg38Fn
SbNd67IJKusm7Xi+fT8r87cmNW1fiQG2SVufAQWbqz0lwcy2f8Lxb4bG+mRo64EtlOtCt/qMHt1i
8b5QZ7dsvfPxH2sMNgcWfzd8qVttevESRmCD1ycEvkvOl77DZypoEd+A5wwzZr8TDRRu838fYxAe
+o0bJW1sj6W3YQGx0qMmoRBxna3iw/nDmVG3KwcIzi7mULKn+gpFL6Lw8g==
-----END CERTIFICATE-----

DigiCert Global Root CA
=======================
-----BEGIN CERTIFICATE-----
MIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQUFADBhMQswCQYDVQQG
EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSAw
HgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBDQTAeFw0wNjExMTAwMDAwMDBaFw0zMTExMTAw
MDAwMDBaMGExCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3
dy5kaWdpY2VydC5jb20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IENBMIIBIjANBgkq
hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4jvhEXLeqKTTo1eqUKKPC3eQyaKl7hLOllsBCSDMAZOn
TjC3U/dDxGkAV53ijSLdhwZAAIEJzs4bg7/fzTtxRuLWZscFs3YnFo97nh6Vfe63SKMI2tavegw5
BmV/Sl0fvBf4q77uKNd0f3p4mVmFaG5cIzJLv07A6Fpt43C/dxC//AH2hdmoRBBYMql1GNXRor5H
4idq9Joz+EkIYIvUX7Q6hL+hqkpMfT7PT19sdl6gSzeRntwi5m3OFBqOasv+zbMUZBfHWymeMr/y
7vrTC0LUq7dBMtoM1O/4gdW7jVg/tRvoSSiicNoxBN33shbyTApOB6jtSj1etX+jkMOvJwIDAQAB
o2MwYTAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUA95QNVbRTLtm
8KPiGxvDl7I90VUwHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUwDQYJKoZIhvcNAQEF
BQADggEBAMucN6pIExIK+t1EnE9SsPTfrgT1eXkIoyQY/EsrhMAtudXH/vTBH1jLuG2cenTnmCmr
EbXjcKChzUyImZOMkXDiqw8cvpOp/2PV5Adg06O/nVsJ8dWO41P0jmP6P6fbtGbfYmbW0W5BjfIt
tep3Sp+dWOIrWcBAI+0tKIJFPnlUkiaY4IBIqDfv8NZ5YBberOgOzW6sRBc4L0na4UU+Krk2U886
UAb3LujEV0lsYSEY1QSteDwsOoBrp+uvFRTp2InBuThs4pFsiv9kuXclVzDAGySj4dzp30d8tbQk
CAUw7C29C79Fv1C5qfPrmAESrciIxpg0X40KPMbp1ZWVbd4=
-----END CERTIFICATE-----

DigiCert High Assurance EV Root CA
==================================
-----BEGIN CERTIFICATE-----
MIIDxTCCAq2gAwIBAgIQAqxcJmoLQJuPC3nyrkYldzANBgkqhkiG9w0BAQUFADBsMQswCQYDVQQG
EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSsw
KQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5jZSBFViBSb290IENBMB4XDTA2MTExMDAwMDAw
MFoXDTMxMTExMDAwMDAwMFowbDELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZ
MBcGA1UECxMQd3d3LmRpZ2ljZXJ0LmNvbTErMCkGA1UEAxMiRGlnaUNlcnQgSGlnaCBBc3N1cmFu
Y2UgRVYgUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMbM5XPm+9S75S0t
Mqbf5YE/yc0lSbZxKsPVlDRnogocsF9ppkCxxLeyj9CYpKlBWTrT3JTWPNt0OKRKzE0lgvdKpVMS
OO7zSW1xkX5jtqumX8OkhPhPYlG++MXs2ziS4wblCJEMxChBVfvLWokVfnHoNb9Ncgk9vjo4UFt3
MRuNs8ckRZqnrG0AFFoEt7oT61EKmEFBIk5lYYeBQVCmeVyJ3hlKV9Uu5l0cUyx+mM0aBhakaHPQ
NAQTXKFx01p8VdteZOE3hzBWBOURtCmAEvF5OYiiAhF8J2a3iLd48soKqDirCmTCv2ZdlYTBoSUe
h10aUAsgEsxBu24LUTi4S8sCAwEAAaNjMGEwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQFMAMB
Af8wHQYDVR0OBBYEFLE+w2kD+L9HAdSYJhoIAu9jZCvDMB8GA1UdIwQYMBaAFLE+w2kD+L9HAdSY
JhoIAu9jZCvDMA0GCSqGSIb3DQEBBQUAA4IBAQAcGgaX3NecnzyIZgYIVyHbIUf4KmeqvxgydkAQ
V8GK83rZEWWONfqe/EW1ntlMMUu4kehDLI6zeM7b41N5cdblIZQB2lWHmiRk9opmzN6cN82oNLFp
myPInngiK3BD41VHMWEZ71jFhS9OMPagMRYjyOfiZRYzy78aG6A9+MpeizGLYAiJLQwGXFK3xPkK
mNEVX58Svnw2Yzi9RKR/5CYrCsSXaQ3pjOLAEFe4yHYSkVXySGnYvCoCWw9E1CAx2/S6cCZdkGCe
vEsXCS+0yx5DaMkHJ8HSXPfqIbloEpw8nL+e/IBcm2PN7EeqJSdnoDfzAIJ9VNep+OkuE6N36B9K
-----END CERTIFICATE-----

SwissSign Gold CA - G2
======================
-----BEGIN CERTIFICATE-----
MIIFujCCA6KgAwIBAgIJALtAHEP1Xk+wMA0GCSqGSIb3DQEBBQUAMEUxCzAJBgNVBAYTAkNIMRUw
EwYDVQQKEwxTd2lzc1NpZ24gQUcxHzAdBgNVBAMTFlN3aXNzU2lnbiBHb2xkIENBIC0gRzIwHhcN
MDYxMDI1MDgzMDM1WhcNMzYxMDI1MDgzMDM1WjBFMQswCQYDVQQGEwJDSDEVMBMGA1UEChMMU3dp
c3NTaWduIEFHMR8wHQYDVQQDExZTd2lzc1NpZ24gR29sZCBDQSAtIEcyMIICIjANBgkqhkiG9w0B
AQEFAAOCAg8AMIICCgKCAgEAr+TufoskDhJuqVAtFkQ7kpJcyrhdhJJCEyq8ZVeCQD5XJM1QiyUq
t2/876LQwB8CJEoTlo8jE+YoWACjR8cGp4QjK7u9lit/VcyLwVcfDmJlD909Vopz2q5+bbqBHH5C
jCA12UNNhPqE21Is8w4ndwtrvxEvcnifLtg+5hg3Wipy+dpikJKVyh+c6bM8K8vzARO/Ws/BtQpg
vd21mWRTuKCWs2/iJneRjOBiEAKfNA+k1ZIzUd6+jbqEemA8atufK+ze3gE/bk3lUIbLtK/tREDF
ylqM2tIrfKjuvqblCqoOpd8FUrdVxyJdMmqXl2MT28nbeTZ7hTpKxVKJ+STnnXepgv9VHKVxaSvR
AiTysybUa9oEVeXBCsdtMDeQKuSeFDNeFhdVxVu1yzSJkvGdJo+hB9TGsnhQ2wwMC3wLjEHXuend
jIj3o02yMszYF9rNt85mndT9Xv+9lz4pded+p2JYryU0pUHHPbwNUMoDAw8IWh+Vc3hiv69yFGkO
peUDDniOJihC8AcLYiAQZzlG+qkDzAQ4embvIIO1jEpWjpEA/I5cgt6IoMPiaG59je883WX0XaxR
7ySArqpWl2/5rX3aYT+YdzylkbYcjCbaZaIJbcHiVOO5ykxMgI93e2CaHt+28kgeDrpOVG2Y4OGi
GqJ3UM/EY5LsRxmd6+ZrzsECAwEAAaOBrDCBqTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUw
AwEB/zAdBgNVHQ4EFgQUWyV7lqRlUX64OfPAeGZe6Drn8O4wHwYDVR0jBBgwFoAUWyV7lqRlUX64
OfPAeGZe6Drn8O4wRgYDVR0gBD8wPTA7BglghXQBWQECAQEwLjAsBggrBgEFBQcCARYgaHR0cDov
L3JlcG9zaXRvcnkuc3dpc3NzaWduLmNvbS8wDQYJKoZIhvcNAQEFBQADggIBACe645R88a7A3hfm
5djV9VSwg/S7zV4Fe0+fdWavPOhWfvxyeDgD2StiGwC5+OlgzczOUYrHUDFu4Up+GC9pWbY9ZIEr
44OE5iKHjn3g7gKZYbge9LgriBIWhMIxkziWMaa5O1M/wySTVltpkuzFwbs4AOPsF6m43Md8AYOf
Mke6UiI0HTJ6CVanfCU2qT1L2sCCbwq7EsiHSycR+R4tx5M/nttfJmtS2S6K8RTGRI0Vqbe/vd6m
Gu6uLftIdxf+u+yvGPUqUfA5hJeVbG4bwyvEdGB5JbAKJ9/fXtI5z0V9QkvfsywexcZdylU6oJxp
mo/a77KwPJ+HbBIrZXAVUjEaJM9vMSNQH4xPjyPDdEFjHFWoFN0+4FFQz/EbMFYOkrCChdiDyyJk
vC24JdVUorgG6q2SpCSgwYa1ShNqR88uC1aVVMvOmttqtKay20EIhid392qgQmwLOM7XdVAyksLf
KzAiSNDVQTglXaTpXZ/GlHXQRf0wl0OPkKsKx4ZzYEppLd6leNcG2mqeSz53OiATIgHQv2ieY2Br
NU0LbbqhPcCT4H8js1WtciVORvnSFu+wZMEBnunKoGqYDs/YYPIvSbjkQuE4NRb0yG5P94FW6Lqj
viOvrv1vA+ACOzB2+httQc8Bsem4yWb02ybzOqR08kkkW8mw0FfB+j564ZfJ
-----END CERTIFICATE-----

SwissSign Silver CA - G2
========================
-----BEGIN CERTIFICATE-----
MIIFvTCCA6WgAwIBAgIITxvUL1S7L0swDQYJKoZIhvcNAQEFBQAwRzELMAkGA1UEBhMCQ0gxFTAT
BgNVBAoTDFN3aXNzU2lnbiBBRzEhMB8GA1UEAxMYU3dpc3NTaWduIFNpbHZlciBDQSAtIEcyMB4X
DTA2MTAyNTA4MzI0NloXDTM2MTAyNTA4MzI0NlowRzELMAkGA1UEBhMCQ0gxFTATBgNVBAoTDFN3
aXNzU2lnbiBBRzEhMB8GA1UEAxMYU3dpc3NTaWduIFNpbHZlciBDQSAtIEcyMIICIjANBgkqhkiG
9w0BAQEFAAOCAg8AMIICCgKCAgEAxPGHf9N4Mfc4yfjDmUO8x/e8N+dOcbpLj6VzHVxumK4DV644
N0MvFz0fyM5oEMF4rhkDKxD6LHmD9ui5aLlV8gREpzn5/ASLHvGiTSf5YXu6t+WiE7brYT7QbNHm
+/pe7R20nqA1W6GSy/BJkv6FCgU+5tkL4k+73JU3/JHpMjUi0R86TieFnbAVlDLaYQ1HTWBCrpJH
6INaUFjpiou5XaHc3ZlKHzZnu0jkg7Y360g6rw9njxcH6ATK72oxh9TAtvmUcXtnZLi2kUpCe2Uu
MGoM9ZDulebyzYLs2aFK7PayS+VFheZteJMELpyCbTapxDFkH4aDCyr0NQp4yVXPQbBH6TCfmb5h
qAaEuSh6XzjZG6k4sIN/c8HDO0gqgg8hm7jMqDXDhBuDsz6+pJVpATqJAHgE2cn0mRmrVn5bi4Y5
FZGkECwJMoBgs5PAKrYYC51+jUnyEEp/+dVGLxmSo5mnJqy7jDzmDrxHB9xzUfFwZC8I+bRHHTBs
ROopN4WSaGa8gzj+ezku01DwH/teYLappvonQfGbGHLy9YR0SslnxFSuSGTfjNFusB3hB48IHpmc
celM2KX3RxIfdNFRnobzwqIjQAtz20um53MGjMGg6cFZrEb65i/4z3GcRm25xBWNOHkDRUjvxF3X
CO6HOSKGsg0PWEP3calILv3q1h8CAwEAAaOBrDCBqTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/
BAUwAwEB/zAdBgNVHQ4EFgQUF6DNweRBtjpbO8tFnb0cwpj6hlgwHwYDVR0jBBgwFoAUF6DNweRB
tjpbO8tFnb0cwpj6hlgwRgYDVR0gBD8wPTA7BglghXQBWQEDAQEwLjAsBggrBgEFBQcCARYgaHR0
cDovL3JlcG9zaXRvcnkuc3dpc3NzaWduLmNvbS8wDQYJKoZIhvcNAQEFBQADggIBAHPGgeAn0i0P
4JUw4ppBf1AsX19iYamGamkYDHRJ1l2E6kFSGG9YrVBWIGrGvShpWJHckRE1qTodvBqlYJ7YH39F
kWnZfrt4csEGDyrOj4VwYaygzQu4OSlWhDJOhrs9xCrZ1x9y7v5RoSJBsXECYxqCsGKrXlcSH9/L
3XWgwF15kIwb4FDm3jH+mHtwX6WQ2K34ArZv02DdQEsixT2tOnqfGhpHkXkzuoLcMmkDlm4fS/Bx
/uNncqCxv1yL5PqZIseEuRuNI5c/7SXgz2W79WEE790eslpBIlqhn10s6FvJbakMDHiqYMZWjwFa
DGi8aRl5xB9+lwW/xekkUV7U1UtT7dkjWjYDZaPBA61BMPNGG4WQr2W11bHkFlt4dR2Xem1ZqSqP
e97Dh4kQmUlzeMg9vVE1dCrV8X5pGyq7O70luJpaPXJhkGaH7gzWTdQRdAtq/gsD/KNVV4n+Ssuu
WxcFyPKNIzFTONItaj+CuY0IavdeQXRuwxF+B6wpYJE/OMpXEA29MC/HpeZBoNquBYeaoKRlbEwJ
DIm6uNO5wJOKMPqN5ZprFQFOZ6raYlY+hAhm0sQ2fac+EPyI4NSA5QC9qvNOBqN6avlicuMJT+ub
DgEj8Z+7fNzcbBGXJbLytGMU0gYqZ4yD9c7qB9iaah7s5Aq7KkzrCWA5zspi2C5u
-----END CERTIFICATE-----

SecureTrust CA
==============
-----BEGIN CERTIFICATE-----
MIIDuDCCAqCgAwIBAgIQDPCOXAgWpa1Cf/DrJxhZ0DANBgkqhkiG9w0BAQUFADBIMQswCQYDVQQG
EwJVUzEgMB4GA1UEChMXU2VjdXJlVHJ1c3QgQ29ycG9yYXRpb24xFzAVBgNVBAMTDlNlY3VyZVRy
dXN0IENBMB4XDTA2MTEwNzE5MzExOFoXDTI5MTIzMTE5NDA1NVowSDELMAkGA1UEBhMCVVMxIDAe
BgNVBAoTF1NlY3VyZVRydXN0IENvcnBvcmF0aW9uMRcwFQYDVQQDEw5TZWN1cmVUcnVzdCBDQTCC
ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKukgeWVzfX2FI7CT8rU4niVWJxB4Q2ZQCQX
OZEzZum+4YOvYlyJ0fwkW2Gz4BERQRwdbvC4u/jep4G6pkjGnx29vo6pQT64lO0pGtSO0gMdA+9t
DWccV9cGrcrI9f4Or2YlSASWC12juhbDCE/RRvgUXPLIXgGZbf2IzIaowW8xQmxSPmjL8xk037uH
GFaAJsTQ3MBv396gwpEWoGQRS0S8Hvbn+mPeZqx2pHGj7DaUaHp3pLHnDi+BeuK1cobvomuL8A/b
01k/unK8RCSc43Oz969XL0Imnal0ugBS8kvNU3xHCzaFDmapCJcWNFfBZveA4+1wVMeT4C4oFVmH
ursCAwEAAaOBnTCBmjATBgkrBgEEAYI3FAIEBh4EAEMAQTALBgNVHQ8EBAMCAYYwDwYDVR0TAQH/
BAUwAwEB/zAdBgNVHQ4EFgQUQjK2FvoE/f5dS3rD/fdMQB1aQ68wNAYDVR0fBC0wKzApoCegJYYj
aHR0cDovL2NybC5zZWN1cmV0cnVzdC5jb20vU1RDQS5jcmwwEAYJKwYBBAGCNxUBBAMCAQAwDQYJ
KoZIhvcNAQEFBQADggEBADDtT0rhWDpSclu1pqNlGKa7UTt36Z3q059c4EVlew3KW+JwULKUBRSu
SceNQQcSc5R+DCMh/bwQf2AQWnL1mA6s7Ll/3XpvXdMc9P+IBWlCqQVxyLesJugutIxq/3HcuLHf
mbx8IVQr5Fiiu1cprp6poxkmD5kuCLDv/WnPmRoJjeOnnyvJNjR7JLN4TJUXpAYmHrZkUjZfYGfZ
nMUFdAvnZyPSCPyI6a6Lf+Ew9Dd+/cYy2i2eRDAwbO4H3tI0/NL/QPZL9GZGBlSm8jIKYyYwa5vR
3ItHuuG51WLQoqD0ZwV4KWMabwTW+MZMo5qxN7SN5ShLHZ4swrhovO0C7jE=
-----END CERTIFICATE-----

Secure Global CA
================
-----BEGIN CERTIFICATE-----
MIIDvDCCAqSgAwIBAgIQB1YipOjUiolN9BPI8PjqpTANBgkqhkiG9w0BAQUFADBKMQswCQYDVQQG
EwJVUzEgMB4GA1UEChMXU2VjdXJlVHJ1c3QgQ29ycG9yYXRpb24xGTAXBgNVBAMTEFNlY3VyZSBH
bG9iYWwgQ0EwHhcNMDYxMTA3MTk0MjI4WhcNMjkxMjMxMTk1MjA2WjBKMQswCQYDVQQGEwJVUzEg
MB4GA1UEChMXU2VjdXJlVHJ1c3QgQ29ycG9yYXRpb24xGTAXBgNVBAMTEFNlY3VyZSBHbG9iYWwg
Q0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvNS7YrGxVaQZx5RNoJLNP2MwhR/jx
YDiJiQPpvepeRlMJ3Fz1Wuj3RSoC6zFh1ykzTM7HfAo3fg+6MpjhHZevj8fcyTiW89sa/FHtaMbQ
bqR8JNGuQsiWUGMu4P51/pinX0kuleM5M2SOHqRfkNJnPLLZ/kG5VacJjnIFHovdRIWCQtBJwB1g
8NEXLJXr9qXBkqPFwqcIYA1gBBCWeZ4WNOaptvolRTnIHmX5k/Wq8VLcmZg9pYYaDDUz+kulBAYV
HDGA76oYa8J719rO+TMg1fW9ajMtgQT7sFzUnKPiXB3jqUJ1XnvUd+85VLrJChgbEplJL4hL/VBi
0XPnj3pDAgMBAAGjgZ0wgZowEwYJKwYBBAGCNxQCBAYeBABDAEEwCwYDVR0PBAQDAgGGMA8GA1Ud
EwEB/wQFMAMBAf8wHQYDVR0OBBYEFK9EBMJBfkiD2045AuzshHrmzsmkMDQGA1UdHwQtMCswKaAn
oCWGI2h0dHA6Ly9jcmwuc2VjdXJldHJ1c3QuY29tL1NHQ0EuY3JsMBAGCSsGAQQBgjcVAQQDAgEA
MA0GCSqGSIb3DQEBBQUAA4IBAQBjGghAfaReUw132HquHw0LURYD7xh8yOOvaliTFGCRsoTciE6+
OYo68+aCiV0BN7OrJKQVDpI1WkpEXk5X+nXOH0jOZvQ8QCaSmGwb7iRGDBezUqXbpZGRzzfTb+cn
CDpOGR86p1hcF895P4vkp9MmI50mD1hp/Ed+stCNi5O/KU9DaXR2Z0vPB4zmAve14bRDtUstFJ/5
3CYNv6ZHdAbYiNE6KTCEztI5gGIbqMdXSbxqVVFnFUq+NQfk1XWYN3kwFNspnWzFacxHVaIw98xc
f8LDmBxrThaA63p4ZUWiABqvDA1VZDRIuJK58bRQKfJPIx/abKwfROHdI3hRW8cW
-----END CERTIFICATE-----

COMODO Certification Authority
==============================
-----BEGIN CERTIFICATE-----
MIIEHTCCAwWgAwIBAgIQToEtioJl4AsC7j41AkblPTANBgkqhkiG9w0BAQUFADCBgTELMAkGA1UE
BhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgG
A1UEChMRQ09NT0RPIENBIExpbWl0ZWQxJzAlBgNVBAMTHkNPTU9ETyBDZXJ0aWZpY2F0aW9uIEF1
dGhvcml0eTAeFw0wNjEyMDEwMDAwMDBaFw0yOTEyMzEyMzU5NTlaMIGBMQswCQYDVQQGEwJHQjEb
MBkGA1UECBMSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHEwdTYWxmb3JkMRowGAYDVQQKExFD
T01PRE8gQ0EgTGltaXRlZDEnMCUGA1UEAxMeQ09NT0RPIENlcnRpZmljYXRpb24gQXV0aG9yaXR5
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0ECLi3LjkRv3UcEbVASY06m/weaKXTuH
+7uIzg3jLz8GlvCiKVCZrts7oVewdFFxze1CkU1B/qnI2GqGd0S7WWaXUF601CxwRM/aN5VCaTww
xHGzUvAhTaHYujl8HJ6jJJ3ygxaYqhZ8Q5sVW7euNJH+1GImGEaaP+vB+fGQV+useg2L23IwambV
4EajcNxo2f8ESIl33rXp+2dtQem8Ob0y2WIC8bGoPW43nOIv4tOiJovGuFVDiOEjPqXSJDlqR6sA
1KGzqSX+DT+nHbrTUcELpNqsOO9VUCQFZUaTNE8tja3G1CEZ0o7KBWFxB3NH5YoZEr0ETc5OnKVI
rLsm9wIDAQABo4GOMIGLMB0GA1UdDgQWBBQLWOWLxkwVN6RAqTCpIb5HNlpW/zAOBgNVHQ8BAf8E
BAMCAQYwDwYDVR0TAQH/BAUwAwEB/zBJBgNVHR8EQjBAMD6gPKA6hjhodHRwOi8vY3JsLmNvbW9k
b2NhLmNvbS9DT01PRE9DZXJ0aWZpY2F0aW9uQXV0aG9yaXR5LmNybDANBgkqhkiG9w0BAQUFAAOC
AQEAPpiem/Yb6dc5t3iuHXIYSdOH5EOC6z/JqvWote9VfCFSZfnVDeFs9D6Mk3ORLgLETgdxb8CP
OGEIqB6BCsAvIC9Bi5HcSEW88cbeunZrM8gALTFGTO3nnc+IlP8zwFboJIYmuNg4ON8qa90SzMc/
RxdMosIGlgnW2/4/PEZB31jiVg88O8EckzXZOFKs7sjsLjBOlDW0JB9LeGna8gI4zJVSk/BwJVmc
IGfE7vmLV2H0knZ9P4SNVbfo5azV8fUZVqZa+5Acr5Pr5RzUZ5ddBA6+C4OmF4O5MBKgxTMVBbkN
+8cFduPYSo38NBejxiEovjBFMR7HeL5YYTisO+IBZQ==
-----END CERTIFICATE-----

Network Solutions Certificate Authority
=======================================
-----BEGIN CERTIFICATE-----
MIID5jCCAs6gAwIBAgIQV8szb8JcFuZHFhfjkDFo4DANBgkqhkiG9w0BAQUFADBiMQswCQYDVQQG
EwJVUzEhMB8GA1UEChMYTmV0d29yayBTb2x1dGlvbnMgTC5MLkMuMTAwLgYDVQQDEydOZXR3b3Jr
IFNvbHV0aW9ucyBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwHhcNMDYxMjAxMDAwMDAwWhcNMjkxMjMx
MjM1OTU5WjBiMQswCQYDVQQGEwJVUzEhMB8GA1UEChMYTmV0d29yayBTb2x1dGlvbnMgTC5MLkMu
MTAwLgYDVQQDEydOZXR3b3JrIFNvbHV0aW9ucyBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwggEiMA0G
CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDkvH6SMG3G2I4rC7xGzuAnlt7e+foS0zwzc7MEL7xx
jOWftiJgPl9dzgn/ggwbmlFQGiaJ3dVhXRncEg8tCqJDXRfQNJIg6nPPOCwGJgl6cvf6UDL4wpPT
aaIjzkGxzOTVHzbRijr4jGPiFFlp7Q3Tf2vouAPlT2rlmGNpSAW+Lv8ztumXWWn4Zxmuk2GWRBXT
crA/vGp97Eh/jcOrqnErU2lBUzS1sLnFBgrEsEX1QV1uiUV7PTsmjHTC5dLRfbIR1PtYMiKagMnc
/Qzpf14Dl847ABSHJ3A4qY5usyd2mFHgBeMhqxrVhSI8KbWaFsWAqPS7azCPL0YCorEMIuDTAgMB
AAGjgZcwgZQwHQYDVR0OBBYEFCEwyfsA106Y2oeqKtCnLrFAMadMMA4GA1UdDwEB/wQEAwIBBjAP
BgNVHRMBAf8EBTADAQH/MFIGA1UdHwRLMEkwR6BFoEOGQWh0dHA6Ly9jcmwubmV0c29sc3NsLmNv
bS9OZXR3b3JrU29sdXRpb25zQ2VydGlmaWNhdGVBdXRob3JpdHkuY3JsMA0GCSqGSIb3DQEBBQUA
A4IBAQC7rkvnt1frf6ott3NHhWrB5KUd5Oc86fRZZXe1eltajSU24HqXLjjAV2CDmAaDn7l2em5Q
4LqILPxFzBiwmZVRDuwduIj/h1AcgsLj4DKAv6ALR8jDMe+ZZzKATxcheQxpXN5eNK4CtSbqUN9/
GGUsyfJj4akH/nxxH2szJGoeBfcFaMBqEssuXmHLrijTfsK0ZpEmXzwuJF/LWA/rKOyvEZbz3Htv
wKeI8lN3s2Berq4o2jUsbzRF0ybh3uxbTydrFny9RAQYgrOJeRcQcT16ohZO9QHNpGxlaKFJdlxD
ydi8NmdspZS11My5vWo1ViHe2MPr+8ukYEywVaCge1ey
-----END CERTIFICATE-----

COMODO ECC Certification Authority
==================================
-----BEGIN CERTIFICATE-----
MIICiTCCAg+gAwIBAgIQH0evqmIAcFBUTAGem2OZKjAKBggqhkjOPQQDAzCBhTELMAkGA1UEBhMC
R0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UE
ChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBFQ0MgQ2VydGlmaWNhdGlvbiBB
dXRob3JpdHkwHhcNMDgwMzA2MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBhTELMAkGA1UEBhMCR0Ix
GzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UEChMR
Q09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBFQ0MgQ2VydGlmaWNhdGlvbiBBdXRo
b3JpdHkwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQDR3svdcmCFYX7deSRFtSrYpn1PlILBs5BAH+X
4QokPB0BBO490o0JlwzgdeT6+3eKKvUDYEs2ixYjFq0JcfRK9ChQtP6IHG4/bC8vCVlbpVsLM5ni
wz2J+Wos77LTBumjQjBAMB0GA1UdDgQWBBR1cacZSBm8nZ3qQUfflMRId5nTeTAOBgNVHQ8BAf8E
BAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAKBggqhkjOPQQDAwNoADBlAjEA7wNbeqy3eApyt4jf/7VG
FAkK+qDmfQjGGoe9GKhzvSbKYAydzpmfz1wPMOG+FDHqAjAU9JM8SaczepBGR7NjfRObTrdvGDeA
U/7dIOA1mjbRxwG55tzd8/8dLDoWV9mSOdY=
-----END CERTIFICATE-----

Certigna
========
-----BEGIN CERTIFICATE-----
MIIDqDCCApCgAwIBAgIJAP7c4wEPyUj/MA0GCSqGSIb3DQEBBQUAMDQxCzAJBgNVBAYTAkZSMRIw
EAYDVQQKDAlEaGlteW90aXMxETAPBgNVBAMMCENlcnRpZ25hMB4XDTA3MDYyOTE1MTMwNVoXDTI3
MDYyOTE1MTMwNVowNDELMAkGA1UEBhMCRlIxEjAQBgNVBAoMCURoaW15b3RpczERMA8GA1UEAwwI
Q2VydGlnbmEwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDIaPHJ1tazNHUmgh7stL7q
XOEm7RFHYeGifBZ4QCHkYJ5ayGPhxLGWkv8YbWkj4Sti993iNi+RB7lIzw7sebYs5zRLcAglozyH
GxnygQcPOJAZ0xH+hrTy0V4eHpbNgGzOOzGTtvKg0KmVEn2lmsxryIRWijOp5yIVUxbwzBfsV1/p
ogqYCd7jX5xv3EjjhQsVWqa6n6xI4wmy9/Qy3l40vhx4XUJbzg4ij02Q130yGLMLLGq/jj8UEYkg
DncUtT2UCIf3JR7VsmAA7G8qKCVuKj4YYxclPz5EIBb2JsglrgVKtOdjLPOMFlN+XPsRGgjBRmKf
Irjxwo1p3Po6WAbfAgMBAAGjgbwwgbkwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUGu3+QTmQ
tCRZvgHyUtVF9lo53BEwZAYDVR0jBF0wW4AUGu3+QTmQtCRZvgHyUtVF9lo53BGhOKQ2MDQxCzAJ
BgNVBAYTAkZSMRIwEAYDVQQKDAlEaGlteW90aXMxETAPBgNVBAMMCENlcnRpZ25hggkA/tzjAQ/J
SP8wDgYDVR0PAQH/BAQDAgEGMBEGCWCGSAGG+EIBAQQEAwIABzANBgkqhkiG9w0BAQUFAAOCAQEA
hQMeknH2Qq/ho2Ge6/PAD/Kl1NqV5ta+aDY9fm4fTIrv0Q8hbV6lUmPOEvjvKtpv6zf+EwLHyzs+
ImvaYS5/1HI93TDhHkxAGYwP15zRgzB7mFncfca5DClMoTOi62c6ZYTTluLtdkVwj7Ur3vkj1klu
PBS1xp81HlDQwY9qcEQCYsuuHWhBp6pX6FOqB9IG9tUUBguRA3UsbHK1YZWaDYu5Def131TN3ubY
1gkIl2PlwS6wt0QmwCbAr1UwnjvVNioZBPRcHv/PLLf/0P2HQBHVESO7SMAhqaQoLf0V+LBOK/Qw
WyH8EZE0vkHve52Xdf+XlcCWWC/qu0bXu+TZLg==
-----END CERTIFICATE-----

ePKI Root Certification Authority
=================================
-----BEGIN CERTIFICATE-----
MIIFsDCCA5igAwIBAgIQFci9ZUdcr7iXAF7kBtK8nTANBgkqhkiG9w0BAQUFADBeMQswCQYDVQQG
EwJUVzEjMCEGA1UECgwaQ2h1bmdod2EgVGVsZWNvbSBDby4sIEx0ZC4xKjAoBgNVBAsMIWVQS0kg
Um9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wNDEyMjAwMjMxMjdaFw0zNDEyMjAwMjMx
MjdaMF4xCzAJBgNVBAYTAlRXMSMwIQYDVQQKDBpDaHVuZ2h3YSBUZWxlY29tIENvLiwgTHRkLjEq
MCgGA1UECwwhZVBLSSBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIICIjANBgkqhkiG9w0B
AQEFAAOCAg8AMIICCgKCAgEA4SUP7o3biDN1Z82tH306Tm2d0y8U82N0ywEhajfqhFAHSyZbCUNs
IZ5qyNUD9WBpj8zwIuQf5/dqIjG3LBXy4P4AakP/h2XGtRrBp0xtInAhijHyl3SJCRImHJ7K2RKi
lTza6We/CKBk49ZCt0Xvl/T29de1ShUCWH2YWEtgvM3XDZoTM1PRYfl61dd4s5oz9wCGzh1NlDiv
qOx4UXCKXBCDUSH3ET00hl7lSM2XgYI1TBnsZfZrxQWh7kcT1rMhJ5QQCtkkO7q+RBNGMD+XPNjX
12ruOzjjK9SXDrkb5wdJfzcq+Xd4z1TtW0ado4AOkUPB1ltfFLqfpo0kR0BZv3I4sjZsN/+Z0V0O
WQqraffAsgRFelQArr5T9rXn4fg8ozHSqf4hUmTFpmfwdQcGlBSBVcYn5AGPF8Fqcde+S/uUWH1+
ETOxQvdibBjWzwloPn9s9h6PYq2lY9sJpx8iQkEeb5mKPtf5P0B6ebClAZLSnT0IFaUQAS2zMnao
lQ2zepr7BxB4EW/hj8e6DyUadCrlHJhBmd8hh+iVBmoKs2pHdmX2Os+PYhcZewoozRrSgx4hxyy/
vv9haLdnG7t4TY3OZ+XkwY63I2binZB1NJipNiuKmpS5nezMirH4JYlcWrYvjB9teSSnUmjDhDXi
Zo1jDiVN1Rmy5nk3pyKdVDECAwEAAaNqMGgwHQYDVR0OBBYEFB4M97Zn8uGSJglFwFU5Lnc/Qkqi
MAwGA1UdEwQFMAMBAf8wOQYEZyoHAAQxMC8wLQIBADAJBgUrDgMCGgUAMAcGBWcqAwAABBRFsMLH
ClZ87lt4DJX5GFPBphzYEDANBgkqhkiG9w0BAQUFAAOCAgEACbODU1kBPpVJufGBuvl2ICO1J2B0
1GqZNF5sAFPZn/KmsSQHRGoqxqWOeBLoR9lYGxMqXnmbnwoqZ6YlPwZpVnPDimZI+ymBV3QGypzq
KOg4ZyYr8dW1P2WT+DZdjo2NQCCHGervJ8A9tDkPJXtoUHRVnAxZfVo9QZQlUgjgRywVMRnVvwdV
xrsStZf0X4OFunHB2WyBEXYKCrC/gpf36j36+uwtqSiUO1bd0lEursC9CBWMd1I0ltabrNMdjmEP
NXubrjlpC2JgQCA2j6/7Nu4tCEoduL+bXPjqpRugc6bY+G7gMwRfaKonh+3ZwZCc7b3jajWvY9+r
GNm65ulK6lCKD2GTHuItGeIwlDWSXQ62B68ZgI9HkFFLLk3dheLSClIKF5r8GrBQAuUBo2M3IUxE
xJtRmREOc5wGj1QupyheRDmHVi03vYVElOEMSyycw5KFNGHLD7ibSkNS/jQ6fbjpKdx2qcgw+BRx
gMYeNkh0IkFch4LoGHGLQYlE535YW6i4jRPpp2zDR+2zGp1iro2C6pSe3VkQw63d4k3jMdXH7Ojy
sP6SHhYKGvzZ8/gntsm+HbRsZJB/9OTEW9c3rkIO3aQab3yIVMUWbuF6aC74Or8NpDyJO3inTmOD
BCEIZ43ygknQW/2xzQ+DhNQ+IIX3Sj0rnP0qCglN6oH4EZw=
-----END CERTIFICATE-----

certSIGN ROOT CA
================
-----BEGIN CERTIFICATE-----
MIIDODCCAiCgAwIBAgIGIAYFFnACMA0GCSqGSIb3DQEBBQUAMDsxCzAJBgNVBAYTAlJPMREwDwYD
VQQKEwhjZXJ0U0lHTjEZMBcGA1UECxMQY2VydFNJR04gUk9PVCBDQTAeFw0wNjA3MDQxNzIwMDRa
Fw0zMTA3MDQxNzIwMDRaMDsxCzAJBgNVBAYTAlJPMREwDwYDVQQKEwhjZXJ0U0lHTjEZMBcGA1UE
CxMQY2VydFNJR04gUk9PVCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALczuX7I
JUqOtdu0KBuqV5Do0SLTZLrTk+jUrIZhQGpgV2hUhE28alQCBf/fm5oqrl0Hj0rDKH/v+yv6efHH
rfAQUySQi2bJqIirr1qjAOm+ukbuW3N7LBeCgV5iLKECZbO9xSsAfsT8AzNXDe3i+s5dRdY4zTW2
ssHQnIFKquSyAVwdj1+ZxLGt24gh65AIgoDzMKND5pCCrlUoSe1b16kQOA7+j0xbm0bqQfWwCHTD
0IgztnzXdN/chNFDDnU5oSVAKOp4yw4sLjmdjItuFhwvJoIQ4uNllAoEwF73XVv4EOLQunpL+943
AAAaWyjj0pxzPjKHmKHJUS/X3qwzs08CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8B
Af8EBAMCAcYwHQYDVR0OBBYEFOCMm9slSbPxfIbWskKHC9BroNnkMA0GCSqGSIb3DQEBBQUAA4IB
AQA+0hyJLjX8+HXd5n9liPRyTMks1zJO890ZeUe9jjtbkw9QSSQTaxQGcu8J06Gh40CEyecYMnQ8
SG4Pn0vU9x7Tk4ZkVJdjclDVVc/6IJMCopvDI5NOFlV2oHB5bc0hH88vLbwZ44gx+FkagQnIl6Z0
x2DEW8xXjrJ1/RsCCdtZb3KTafcxQdaIOL+Hsr0Wefmq5L6IJd1hJyMctTEHBDa0GpC9oHRxUIlt
vBTjD4au8as+x6AJzKNI0eDbZOeStc+vckNwi/nDhDwTqn6Sm1dTk/pwwpEOMfmbZ13pljheX7Nz
TogVZ96edhBiIL5VaZVDADlN9u6wWk5JRFRYX0KD
-----END CERTIFICATE-----

NetLock Arany (Class Gold) Főtanúsítvány
========================================
-----BEGIN CERTIFICATE-----
MIIEFTCCAv2gAwIBAgIGSUEs5AAQMA0GCSqGSIb3DQEBCwUAMIGnMQswCQYDVQQGEwJIVTERMA8G
A1UEBwwIQnVkYXBlc3QxFTATBgNVBAoMDE5ldExvY2sgS2Z0LjE3MDUGA1UECwwuVGFuw7pzw610
dsOhbnlraWFkw7NrIChDZXJ0aWZpY2F0aW9uIFNlcnZpY2VzKTE1MDMGA1UEAwwsTmV0TG9jayBB
cmFueSAoQ2xhc3MgR29sZCkgRsWRdGFuw7pzw610dsOhbnkwHhcNMDgxMjExMTUwODIxWhcNMjgx
MjA2MTUwODIxWjCBpzELMAkGA1UEBhMCSFUxETAPBgNVBAcMCEJ1ZGFwZXN0MRUwEwYDVQQKDAxO
ZXRMb2NrIEtmdC4xNzA1BgNVBAsMLlRhbsO6c8OtdHbDoW55a2lhZMOzayAoQ2VydGlmaWNhdGlv
biBTZXJ2aWNlcykxNTAzBgNVBAMMLE5ldExvY2sgQXJhbnkgKENsYXNzIEdvbGQpIEbFkXRhbsO6
c8OtdHbDoW55MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxCRec75LbRTDofTjl5Bu
0jBFHjzuZ9lk4BqKf8owyoPjIMHj9DrTlF8afFttvzBPhCf2nx9JvMaZCpDyD/V/Q4Q3Y1GLeqVw
/HpYzY6b7cNGbIRwXdrzAZAj/E4wqX7hJ2Pn7WQ8oLjJM2P+FpD/sLj916jAwJRDC7bVWaaeVtAk
H3B5r9s5VA1lddkVQZQBr17s9o3x/61k/iCa11zr/qYfCGSji3ZVrR47KGAuhyXoqq8fxmRGILdw
fzzeSNuWU7c5d+Qa4scWhHaXWy+7GRWF+GmF9ZmnqfI0p6m2pgP8b4Y9VHx2BJtr+UBdADTHLpl1
neWIA6pN+APSQnbAGwIDAKiLo0UwQzASBgNVHRMBAf8ECDAGAQH/AgEEMA4GA1UdDwEB/wQEAwIB
BjAdBgNVHQ4EFgQUzPpnk/C2uNClwB7zU/2MU9+D15YwDQYJKoZIhvcNAQELBQADggEBAKt/7hwW
qZw8UQCgwBEIBaeZ5m8BiFRhbvG5GK1Krf6BQCOUL/t1fC8oS2IkgYIL9WHxHG64YTjrgfpioTta
YtOUZcTh5m2C+C8lcLIhJsFyUR+MLMOEkMNaj7rP9KdlpeuY0fsFskZ1FSNqb4VjMIDw1Z4fKRzC
bLBQWV2QWzuoDTDPv31/zvGdg73JRm4gpvlhUbohL3u+pRVjodSVh/GeufOJ8z2FuLjbvrW5Kfna
NwUASZQDhETnv0Mxz3WLJdH0pmT1kvarBes96aULNmLazAZfNou2XjG4Kvte9nHfRCaexOYNkbQu
dZWAUWpLMKawYqGT8ZvYzsRjdT9ZR7E=
-----END CERTIFICATE-----

Hongkong Post Root CA 1
=======================
-----BEGIN CERTIFICATE-----
MIIDMDCCAhigAwIBAgICA+gwDQYJKoZIhvcNAQEFBQAwRzELMAkGA1UEBhMCSEsxFjAUBgNVBAoT
DUhvbmdrb25nIFBvc3QxIDAeBgNVBAMTF0hvbmdrb25nIFBvc3QgUm9vdCBDQSAxMB4XDTAzMDUx
NTA1MTMxNFoXDTIzMDUxNTA0NTIyOVowRzELMAkGA1UEBhMCSEsxFjAUBgNVBAoTDUhvbmdrb25n
IFBvc3QxIDAeBgNVBAMTF0hvbmdrb25nIFBvc3QgUm9vdCBDQSAxMIIBIjANBgkqhkiG9w0BAQEF
AAOCAQ8AMIIBCgKCAQEArP84tulmAknjorThkPlAj3n54r15/gK97iSSHSL22oVyaf7XPwnU3ZG1
ApzQjVrhVcNQhrkpJsLj2aDxaQMoIIBFIi1WpztUlVYiWR8o3x8gPW2iNr4joLFutbEnPzlTCeqr
auh0ssJlXI6/fMN4hM2eFvz1Lk8gKgifd/PFHsSaUmYeSF7jEAaPIpjhZY4bXSNmO7ilMlHIhqqh
qZ5/dpTCpmy3QfDVyAY45tQM4vM7TG1QjMSDJ8EThFk9nnV0ttgCXjqQesBCNnLsak3c78QA3xMY
V18meMjWCnl3v/evt3a5pQuEF10Q6m/hq5URX208o1xNg1vysxmKgIsLhwIDAQABoyYwJDASBgNV
HRMBAf8ECDAGAQH/AgEDMA4GA1UdDwEB/wQEAwIBxjANBgkqhkiG9w0BAQUFAAOCAQEADkbVPK7i
h9legYsCmEEIjEy82tvuJxuC52pF7BaLT4Wg87JwvVqWuspube5Gi27nKi6Wsxkz67SfqLI37pio
l7Yutmcn1KZJ/RyTZXaeQi/cImyaT/JaFTmxcdcrUehtHJjA2Sr0oYJ71clBoiMBdDhViw+5Lmei
IAQ32pwL0xch4I+XeTRvhEgCIDMb5jREn5Fw9IBehEPCKdJsEhTkYY2sEJCehFC78JZvRZ+K88ps
T/oROhUVRsPNH4NbLUES7VBnQRM9IauUiqpOfMGx+6fWtScvl6tu4B3i0RwsH0Ti/L6RoZz71ilT
c4afU9hDDl3WY4JxHYB0yvbiAmvZWg==
-----END CERTIFICATE-----

SecureSign RootCA11
===================
-----BEGIN CERTIFICATE-----
MIIDbTCCAlWgAwIBAgIBATANBgkqhkiG9w0BAQUFADBYMQswCQYDVQQGEwJKUDErMCkGA1UEChMi
SmFwYW4gQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcywgSW5jLjEcMBoGA1UEAxMTU2VjdXJlU2lnbiBS
b290Q0ExMTAeFw0wOTA0MDgwNDU2NDdaFw0yOTA0MDgwNDU2NDdaMFgxCzAJBgNVBAYTAkpQMSsw
KQYDVQQKEyJKYXBhbiBDZXJ0aWZpY2F0aW9uIFNlcnZpY2VzLCBJbmMuMRwwGgYDVQQDExNTZWN1
cmVTaWduIFJvb3RDQTExMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA/XeqpRyQBTvL
TJszi1oURaTnkBbR31fSIRCkF/3frNYfp+TbfPfs37gD2pRY/V1yfIw/XwFndBWW4wI8h9uuywGO
wvNmxoVF9ALGOrVisq/6nL+k5tSAMJjzDbaTj6nU2DbysPyKyiyhFTOVMdrAG/LuYpmGYz+/3ZMq
g6h2uRMft85OQoWPIucuGvKVCbIFtUROd6EgvanyTgp9UK31BQ1FT0Zx/Sg+U/sE2C3XZR1KG/rP
O7AxmjVuyIsG0wCR8pQIZUyxNAYAeoni8McDWc/V1uinMrPmmECGxc0nEovMe863ETxiYAcjPitA
bpSACW22s293bzUIUPsCh8U+iQIDAQABo0IwQDAdBgNVHQ4EFgQUW/hNT7KlhtQ60vFjmqC+CfZX
t94wDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAKCh
OBZmLqdWHyGcBvod7bkixTgm2E5P7KN/ed5GIaGHd48HCJqypMWvDzKYC3xmKbabfSVSSUOrTC4r
bnpwrxYO4wJs+0LmGJ1F2FXI6Dvd5+H0LgscNFxsWEr7jIhQX5Ucv+2rIrVls4W6ng+4reV6G4pQ
Oh29Dbx7VFALuUKvVaAYga1lme++5Jy/xIWrQbJUb9wlze144o4MjQlJ3WN7WmmWAiGovVJZ6X01
y8hSyn+B/tlr0/cR7SXf+Of5pPpyl4RTDaXQMhhRdlkUbA/r7F+AjHVDg8OFmP9Mni0N5HeDk061
lgeLKBObjBmNQSdJQO7e5iNEOdyhIta6A/I=
-----END CERTIFICATE-----

Microsec e-Szigno Root CA 2009
==============================
-----BEGIN CERTIFICATE-----
MIIECjCCAvKgAwIBAgIJAMJ+QwRORz8ZMA0GCSqGSIb3DQEBCwUAMIGCMQswCQYDVQQGEwJIVTER
MA8GA1UEBwwIQnVkYXBlc3QxFjAUBgNVBAoMDU1pY3Jvc2VjIEx0ZC4xJzAlBgNVBAMMHk1pY3Jv
c2VjIGUtU3ppZ25vIFJvb3QgQ0EgMjAwOTEfMB0GCSqGSIb3DQEJARYQaW5mb0BlLXN6aWduby5o
dTAeFw0wOTA2MTYxMTMwMThaFw0yOTEyMzAxMTMwMThaMIGCMQswCQYDVQQGEwJIVTERMA8GA1UE
BwwIQnVkYXBlc3QxFjAUBgNVBAoMDU1pY3Jvc2VjIEx0ZC4xJzAlBgNVBAMMHk1pY3Jvc2VjIGUt
U3ppZ25vIFJvb3QgQ0EgMjAwOTEfMB0GCSqGSIb3DQEJARYQaW5mb0BlLXN6aWduby5odTCCASIw
DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOn4j/NjrdqG2KfgQvvPkd6mJviZpWNwrZuuyjNA
fW2WbqEORO7hE52UQlKavXWFdCyoDh2Tthi3jCyoz/tccbna7P7ofo/kLx2yqHWH2Leh5TvPmUpG
0IMZfcChEhyVbUr02MelTTMuhTlAdX4UfIASmFDHQWe4oIBhVKZsTh/gnQ4H6cm6M+f+wFUoLAKA
pxn1ntxVUwOXewdI/5n7N4okxFnMUBBjjqqpGrCEGob5X7uxUG6k0QrM1XF+H6cbfPVTbiJfyyvm
1HxdrtbCxkzlBQHZ7Vf8wSN5/PrIJIOV87VqUQHQd9bpEqH5GoP7ghu5sJf0dgYzQ0mg/wu1+rUC
AwEAAaOBgDB+MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBTLD8bf
QkPMPcu1SCOhGnqmKrs0aDAfBgNVHSMEGDAWgBTLD8bfQkPMPcu1SCOhGnqmKrs0aDAbBgNVHREE
FDASgRBpbmZvQGUtc3ppZ25vLmh1MA0GCSqGSIb3DQEBCwUAA4IBAQDJ0Q5eLtXMs3w+y/w9/w0o
lZMEyL/azXm4Q5DwpL7v8u8hmLzU1F0G9u5C7DBsoKqpyvGvivo/C3NqPuouQH4frlRheesuCDfX
I/OMn74dseGkddug4lQUsbocKaQY9hK6ohQU4zE1yED/t+AFdlfBHFny+L/k7SViXITwfn4fs775
tyERzAMBVnCnEJIeGzSBHq2cGsMEPO0CYdYeBvNfOofyK/FFh+U9rNHHV4S9a67c2Pm2G2JwCz02
yULyMtd6YebS2z3PyKnJm9zbWETXbzivf3jTo60adbocwTZ8jx5tHMN1Rq41Bab2XD0h7lbwyYIi
LXpUq3DDfSJlgnCW
-----END CERTIFICATE-----

GlobalSign Root CA - R3
=======================
-----BEGIN CERTIFICATE-----
MIIDXzCCAkegAwIBAgILBAAAAAABIVhTCKIwDQYJKoZIhvcNAQELBQAwTDEgMB4GA1UECxMXR2xv
YmFsU2lnbiBSb290IENBIC0gUjMxEzARBgNVBAoTCkdsb2JhbFNpZ24xEzARBgNVBAMTCkdsb2Jh
bFNpZ24wHhcNMDkwMzE4MTAwMDAwWhcNMjkwMzE4MTAwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxT
aWduIFJvb3QgQ0EgLSBSMzETMBEGA1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2ln
bjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMwldpB5BngiFvXAg7aEyiie/QV2EcWt
iHL8RgJDx7KKnQRfJMsuS+FggkbhUqsMgUdwbN1k0ev1LKMPgj0MK66X17YUhhB5uzsTgHeMCOFJ
0mpiLx9e+pZo34knlTifBtc+ycsmWQ1z3rDI6SYOgxXG71uL0gRgykmmKPZpO/bLyCiR5Z2KYVc3
rHQU3HTgOu5yLy6c+9C7v/U9AOEGM+iCK65TpjoWc4zdQQ4gOsC0p6Hpsk+QLjJg6VfLuQSSaGjl
OCZgdbKfd/+RFO+uIEn8rUAVSNECMWEZXriX7613t2Saer9fwRPvm2L7DWzgVGkWqQPabumDk3F2
xmmFghcCAwEAAaNCMEAwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYE
FI/wS3+oLkUkrk1Q+mOai97i3Ru8MA0GCSqGSIb3DQEBCwUAA4IBAQBLQNvAUKr+yAzv95ZURUm7
lgAJQayzE4aGKAczymvmdLm6AC2upArT9fHxD4q/c2dKg8dEe3jgr25sbwMpjjM5RcOO5LlXbKr8
EpbsU8Yt5CRsuZRj+9xTaGdWPoO4zzUhw8lo/s7awlOqzJCK6fBdRoyV3XpYKBovHd7NADdBj+1E
bddTKJd+82cEHhXXipa0095MJ6RMG3NzdvQXmcIfeg7jLQitChws/zyrVQ4PkX4268NXSb7hLi18
YIvDQVETI53O9zJrlAGomecsMx86OyXShkDOOyyGeMlhLxS67ttVb9+E7gUJTb0o2HLO02JQZR7r
kpeDMdmztcpHWD9f
-----END CERTIFICATE-----

Autoridad de Certificacion Firmaprofesional CIF A62634068
=========================================================
-----BEGIN CERTIFICATE-----
MIIGFDCCA/ygAwIBAgIIU+w77vuySF8wDQYJKoZIhvcNAQEFBQAwUTELMAkGA1UEBhMCRVMxQjBA
BgNVBAMMOUF1dG9yaWRhZCBkZSBDZXJ0aWZpY2FjaW9uIEZpcm1hcHJvZmVzaW9uYWwgQ0lGIEE2
MjYzNDA2ODAeFw0wOTA1MjAwODM4MTVaFw0zMDEyMzEwODM4MTVaMFExCzAJBgNVBAYTAkVTMUIw
QAYDVQQDDDlBdXRvcmlkYWQgZGUgQ2VydGlmaWNhY2lvbiBGaXJtYXByb2Zlc2lvbmFsIENJRiBB
NjI2MzQwNjgwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDKlmuO6vj78aI14H9M2uDD
Utd9thDIAl6zQyrET2qyyhxdKJp4ERppWVevtSBC5IsP5t9bpgOSL/UR5GLXMnE42QQMcas9UX4P
B99jBVzpv5RvwSmCwLTaUbDBPLutN0pcyvFLNg4kq7/DhHf9qFD0sefGL9ItWY16Ck6WaVICqjaY
7Pz6FIMMNx/Jkjd/14Et5cS54D40/mf0PmbR0/RAz15iNA9wBj4gGFrO93IbJWyTdBSTo3OxDqqH
ECNZXyAFGUftaI6SEspd/NYrspI8IM/hX68gvqB2f3bl7BqGYTM+53u0P6APjqK5am+5hyZvQWyI
plD9amML9ZMWGxmPsu2bm8mQ9QEM3xk9Dz44I8kvjwzRAv4bVdZO0I08r0+k8/6vKtMFnXkIoctX
MbScyJCyZ/QYFpM6/EfY0XiWMR+6KwxfXZmtY4laJCB22N/9q06mIqqdXuYnin1oKaPnirjaEbsX
LZmdEyRG98Xi2J+Of8ePdG1asuhy9azuJBCtLxTa/y2aRnFHvkLfuwHb9H/TKI8xWVvTyQKmtFLK
bpf7Q8UIJm+K9Lv9nyiqDdVF8xM6HdjAeI9BZzwelGSuewvF6NkBiDkal4ZkQdU7hwxu+g/GvUgU
vzlN1J5Bto+WHWOWk9mVBngxaJ43BjuAiUVhOSPHG0SjFeUc+JIwuwIDAQABo4HvMIHsMBIGA1Ud
EwEB/wQIMAYBAf8CAQEwDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBRlzeurNR4APn7VdMActHNH
DhpkLzCBpgYDVR0gBIGeMIGbMIGYBgRVHSAAMIGPMC8GCCsGAQUFBwIBFiNodHRwOi8vd3d3LmZp
cm1hcHJvZmVzaW9uYWwuY29tL2NwczBcBggrBgEFBQcCAjBQHk4AUABhAHMAZQBvACAAZABlACAA
bABhACAAQgBvAG4AYQBuAG8AdgBhACAANAA3ACAAQgBhAHIAYwBlAGwAbwBuAGEAIAAwADgAMAAx
ADcwDQYJKoZIhvcNAQEFBQADggIBABd9oPm03cXF661LJLWhAqvdpYhKsg9VSytXjDvlMd3+xDLx
51tkljYyGOylMnfX40S2wBEqgLk9am58m9Ot/MPWo+ZkKXzR4Tgegiv/J2Wv+xYVxC5xhOW1//qk
R71kMrv2JYSiJ0L1ILDCExARzRAVukKQKtJE4ZYm6zFIEv0q2skGz3QeqUvVhyj5eTSSPi5E6PaP
T481PyWzOdxjKpBrIF/EUhJOlywqrJ2X3kjyo2bbwtKDlaZmp54lD+kLM5FlClrD2VQS3a/DTg4f
Jl4N3LON7NWBcN7STyQF82xO9UxJZo3R/9ILJUFI/lGExkKvgATP0H5kSeTy36LssUzAKh3ntLFl
osS88Zj0qnAHY7S42jtM+kAiMFsRpvAFDsYCA0irhpuF3dvd6qJ2gHN99ZwExEWN57kci57q13XR
crHedUTnQn3iV2t93Jm8PYMo6oCTjcVMZcFwgbg4/EMxsvYDNEeyrPsiBsse3RdHHF9mudMaotoR
saS8I8nkvof/uZS2+F0gStRf571oe2XyFR7SOqkt6dhrJKyXWERHrVkY8SFlcN7ONGCoQPHzPKTD
KCOM/iczQ0CgFzzr6juwcqajuUpLXhZI9LK8yIySxZ2frHI2vDSANGupi5LAuBft7HZT9SQBjLMi
6Et8Vcad+qMUu2WFbm5PEn4KPJ2V
-----END CERTIFICATE-----

Izenpe.com
==========
-----BEGIN CERTIFICATE-----
MIIF8TCCA9mgAwIBAgIQALC3WhZIX7/hy/WL1xnmfTANBgkqhkiG9w0BAQsFADA4MQswCQYDVQQG
EwJFUzEUMBIGA1UECgwLSVpFTlBFIFMuQS4xEzARBgNVBAMMCkl6ZW5wZS5jb20wHhcNMDcxMjEz
MTMwODI4WhcNMzcxMjEzMDgyNzI1WjA4MQswCQYDVQQGEwJFUzEUMBIGA1UECgwLSVpFTlBFIFMu
QS4xEzARBgNVBAMMCkl6ZW5wZS5jb20wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDJ
03rKDx6sp4boFmVqscIbRTJxldn+EFvMr+eleQGPicPK8lVx93e+d5TzcqQsRNiekpsUOqHnJJAK
ClaOxdgmlOHZSOEtPtoKct2jmRXagaKH9HtuJneJWK3W6wyyQXpzbm3benhB6QiIEn6HLmYRY2xU
+zydcsC8Lv/Ct90NduM61/e0aL6i9eOBbsFGb12N4E3GVFWJGjMxCrFXuaOKmMPsOzTFlUFpfnXC
PCDFYbpRR6AgkJOhkEvzTnyFRVSa0QUmQbC1TR0zvsQDyCV8wXDbO/QJLVQnSKwv4cSsPsjLkkxT
OTcj7NMB+eAJRE1NZMDhDVqHIrytG6P+JrUV86f8hBnp7KGItERphIPzidF0BqnMC9bC3ieFUCbK
F7jJeodWLBoBHmy+E60QrLUk9TiRodZL2vG70t5HtfG8gfZZa88ZU+mNFctKy6lvROUbQc/hhqfK
0GqfvEyNBjNaooXlkDWgYlwWTvDjovoDGrQscbNYLN57C9saD+veIR8GdwYDsMnvmfzAuU8Lhij+
0rnq49qlw0dpEuDb8PYZi+17cNcC1u2HGCgsBCRMd+RIihrGO5rUD8r6ddIBQFqNeb+Lz0vPqhbB
leStTIo+F5HUsWLlguWABKQDfo2/2n+iD5dPDNMN+9fR5XJ+HMh3/1uaD7euBUbl8agW7EekFwID
AQABo4H2MIHzMIGwBgNVHREEgagwgaWBD2luZm9AaXplbnBlLmNvbaSBkTCBjjFHMEUGA1UECgw+
SVpFTlBFIFMuQS4gLSBDSUYgQTAxMzM3MjYwLVJNZXJjLlZpdG9yaWEtR2FzdGVpeiBUMTA1NSBG
NjIgUzgxQzBBBgNVBAkMOkF2ZGEgZGVsIE1lZGl0ZXJyYW5lbyBFdG9yYmlkZWEgMTQgLSAwMTAx
MCBWaXRvcmlhLUdhc3RlaXowDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0O
BBYEFB0cZQ6o8iV7tJHP5LGx5r1VdGwFMA0GCSqGSIb3DQEBCwUAA4ICAQB4pgwWSp9MiDrAyw6l
Fn2fuUhfGI8NYjb2zRlrrKvV9pF9rnHzP7MOeIWblaQnIUdCSnxIOvVFfLMMjlF4rJUT3sb9fbga
kEyrkgPH7UIBzg/YsfqikuFgba56awmqxinuaElnMIAkejEWOVt+8Rwu3WwJrfIxwYJOubv5vr8q
hT/AQKM6WfxZSzwoJNu0FXWuDYi6LnPAvViH5ULy617uHjAimcs30cQhbIHsvm0m5hzkQiCeR7Cs
g1lwLDXWrzY0tM07+DKo7+N4ifuNRSzanLh+QBxh5z6ikixL8s36mLYp//Pye6kfLqCTVyvehQP5
aTfLnnhqBbTFMXiJ7HqnheG5ezzevh55hM6fcA5ZwjUukCox2eRFekGkLhObNA5me0mrZJfQRsN5
nXJQY6aYWwa9SG3YOYNw6DXwBdGqvOPbyALqfP2C2sJbUjWumDqtujWTI6cfSN01RpiyEGjkpTHC
ClguGYEQyVB1/OpaFs4R1+7vUIgtYf8/QnMFlEPVjjxOAToZpR9GTnfQXeWBIiGH/pR9hNiTrdZo
Q0iy2+tzJOeRf1SktoA+naM8THLCV8Sg1Mw4J87VBp6iSNnpn86CcDaTmjvfliHjWbcM2pE38P1Z
WrOZyGlsQyYBNWNgVYkDOnXYukrZVP/u3oDYLdE41V4tC5h9Pmzb/CaIxw==
-----END CERTIFICATE-----

Go Daddy Root Certificate Authority - G2
========================================
-----BEGIN CERTIFICATE-----
MIIDxTCCAq2gAwIBAgIBADANBgkqhkiG9w0BAQsFADCBgzELMAkGA1UEBhMCVVMxEDAOBgNVBAgT
B0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxGjAYBgNVBAoTEUdvRGFkZHkuY29tLCBJbmMu
MTEwLwYDVQQDEyhHbyBEYWRkeSBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTA5
MDkwMTAwMDAwMFoXDTM3MTIzMTIzNTk1OVowgYMxCzAJBgNVBAYTAlVTMRAwDgYDVQQIEwdBcml6
b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMRowGAYDVQQKExFHb0RhZGR5LmNvbSwgSW5jLjExMC8G
A1UEAxMoR28gRGFkZHkgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgLSBHMjCCASIwDQYJKoZI
hvcNAQEBBQADggEPADCCAQoCggEBAL9xYgjx+lk09xvJGKP3gElY6SKDE6bFIEMBO4Tx5oVJnyfq
9oQbTqC023CYxzIBsQU+B07u9PpPL1kwIuerGVZr4oAH/PMWdYA5UXvl+TW2dE6pjYIT5LY/qQOD
+qK+ihVqf94Lw7YZFAXK6sOoBJQ7RnwyDfMAZiLIjWltNowRGLfTshxgtDj6AozO091GB94KPutd
fMh8+7ArU6SSYmlRJQVhGkSBjCypQ5Yj36w6gZoOKcUcqeldHraenjAKOc7xiID7S13MMuyFYkMl
NAJWJwGRtDtwKj9useiciAF9n9T521NtYJ2/LOdYq7hfRvzOxBsDPAnrSTFcaUaz4EcCAwEAAaNC
MEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFDqahQcQZyi27/a9
BUFuIMGU2g/eMA0GCSqGSIb3DQEBCwUAA4IBAQCZ21151fmXWWcDYfF+OwYxdS2hII5PZYe096ac
vNjpL9DbWu7PdIxztDhC2gV7+AJ1uP2lsdeu9tfeE8tTEH6KRtGX+rcuKxGrkLAngPnon1rpN5+r
5N9ss4UXnT3ZJE95kTXWXwTrgIOrmgIttRD02JDHBHNA7XIloKmf7J6raBKZV8aPEjoJpL1E/QYV
N8Gb5DKj7Tjo2GTzLH4U/ALqn83/B2gX2yKQOC16jdFU8WnjXzPKej17CuPKf1855eJ1usV2GDPO
LPAvTK33sefOT6jEm0pUBsV/fdUID+Ic/n4XuKxe9tQWskMJDE32p2u0mYRlynqI4uJEvlz36hz1
-----END CERTIFICATE-----

Starfield Root Certificate Authority - G2
=========================================
-----BEGIN CERTIFICATE-----
MIID3TCCAsWgAwIBAgIBADANBgkqhkiG9w0BAQsFADCBjzELMAkGA1UEBhMCVVMxEDAOBgNVBAgT
B0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoTHFN0YXJmaWVsZCBUZWNobm9s
b2dpZXMsIEluYy4xMjAwBgNVBAMTKVN0YXJmaWVsZCBSb290IENlcnRpZmljYXRlIEF1dGhvcml0
eSAtIEcyMB4XDTA5MDkwMTAwMDAwMFoXDTM3MTIzMTIzNTk1OVowgY8xCzAJBgNVBAYTAlVTMRAw
DgYDVQQIEwdBcml6b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMSUwIwYDVQQKExxTdGFyZmllbGQg
VGVjaG5vbG9naWVzLCBJbmMuMTIwMAYDVQQDEylTdGFyZmllbGQgUm9vdCBDZXJ0aWZpY2F0ZSBB
dXRob3JpdHkgLSBHMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL3twQP89o/8ArFv
W59I2Z154qK3A2FWGMNHttfKPTUuiUP3oWmb3ooa/RMgnLRJdzIpVv257IzdIvpy3Cdhl+72WoTs
bhm5iSzchFvVdPtrX8WJpRBSiUZV9Lh1HOZ/5FSuS/hVclcCGfgXcVnrHigHdMWdSL5stPSksPNk
N3mSwOxGXn/hbVNMYq/NHwtjuzqd+/x5AJhhdM8mgkBj87JyahkNmcrUDnXMN/uLicFZ8WJ/X7Nf
ZTD4p7dNdloedl40wOiWVpmKs/B/pM293DIxfJHP4F8R+GuqSVzRmZTRouNjWwl2tVZi4Ut0HZbU
JtQIBFnQmA4O5t78w+wfkPECAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC
AQYwHQYDVR0OBBYEFHwMMh+n2TB/xH1oo2Kooc6rB1snMA0GCSqGSIb3DQEBCwUAA4IBAQARWfol
TwNvlJk7mh+ChTnUdgWUXuEok21iXQnCoKjUsHU48TRqneSfioYmUeYs0cYtbpUgSpIB7LiKZ3sx
4mcujJUDJi5DnUox9g61DLu34jd/IroAow57UvtruzvE03lRTs2Q9GcHGcg8RnoNAX3FWOdt5oUw
F5okxBDgBPfg8n/Uqgr/Qh037ZTlZFkSIHc40zI+OIF1lnP6aI+xy84fxez6nH7PfrHxBy22/L/K
pL/QlwVKvOoYKAKQvVR4CSFx09F9HdkWsKlhPdAKACL8x3vLCWRFCztAgfd9fDL1mMpYjn0q7pBZ
c2T5NnReJaH1ZgUufzkVqSr7UIuOhWn0
-----END CERTIFICATE-----

Starfield Services Root Certificate Authority - G2
==================================================
-----BEGIN CERTIFICATE-----
MIID7zCCAtegAwIBAgIBADANBgkqhkiG9w0BAQsFADCBmDELMAkGA1UEBhMCVVMxEDAOBgNVBAgT
B0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoTHFN0YXJmaWVsZCBUZWNobm9s
b2dpZXMsIEluYy4xOzA5BgNVBAMTMlN0YXJmaWVsZCBTZXJ2aWNlcyBSb290IENlcnRpZmljYXRl
IEF1dGhvcml0eSAtIEcyMB4XDTA5MDkwMTAwMDAwMFoXDTM3MTIzMTIzNTk1OVowgZgxCzAJBgNV
BAYTAlVTMRAwDgYDVQQIEwdBcml6b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMSUwIwYDVQQKExxT
dGFyZmllbGQgVGVjaG5vbG9naWVzLCBJbmMuMTswOQYDVQQDEzJTdGFyZmllbGQgU2VydmljZXMg
Um9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgLSBHMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC
AQoCggEBANUMOsQq+U7i9b4Zl1+OiFOxHz/Lz58gE20pOsgPfTz3a3Y4Y9k2YKibXlwAgLIvWX/2
h/klQ4bnaRtSmpDhcePYLQ1Ob/bISdm28xpWriu2dBTrz/sm4xq6HZYuajtYlIlHVv8loJNwU4Pa
hHQUw2eeBGg6345AWh1KTs9DkTvnVtYAcMtS7nt9rjrnvDH5RfbCYM8TWQIrgMw0R9+53pBlbQLP
LJGmpufehRhJfGZOozptqbXuNC66DQO4M99H67FrjSXZm86B0UVGMpZwh94CDklDhbZsc7tk6mFB
rMnUVN+HL8cisibMn1lUaJ/8viovxFUcdUBgF4UCVTmLfwUCAwEAAaNCMEAwDwYDVR0TAQH/BAUw
AwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFJxfAN+qAdcwKziIorhtSpzyEZGDMA0GCSqG
SIb3DQEBCwUAA4IBAQBLNqaEd2ndOxmfZyMIbw5hyf2E3F/YNoHN2BtBLZ9g3ccaaNnRbobhiCPP
E95Dz+I0swSdHynVv/heyNXBve6SbzJ08pGCL72CQnqtKrcgfU28elUSwhXqvfdqlS5sdJ/PHLTy
xQGjhdByPq1zqwubdQxtRbeOlKyWN7Wg0I8VRw7j6IPdj/3vQQF3zCepYoUz8jcI73HPdwbeyBkd
iEDPfUYd/x7H4c7/I9vG+o1VTqkC50cRRj70/b17KSa7qWFiNyi2LSr2EIZkyXCn0q23KXB56jza
YyWf/Wi3MOxw+3WKt21gZ7IeyLnp2KhvAotnDU0mV3HaIPzBSlCNsSi6
-----END CERTIFICATE-----

AffirmTrust Commercial
======================
-----BEGIN CERTIFICATE-----
MIIDTDCCAjSgAwIBAgIId3cGJyapsXwwDQYJKoZIhvcNAQELBQAwRDELMAkGA1UEBhMCVVMxFDAS
BgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVzdCBDb21tZXJjaWFsMB4XDTEw
MDEyOTE0MDYwNloXDTMwMTIzMTE0MDYwNlowRDELMAkGA1UEBhMCVVMxFDASBgNVBAoMC0FmZmly
bVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVzdCBDb21tZXJjaWFsMIIBIjANBgkqhkiG9w0BAQEF
AAOCAQ8AMIIBCgKCAQEA9htPZwcroRX1BiLLHwGy43NFBkRJLLtJJRTWzsO3qyxPxkEylFf6Eqdb
DuKPHx6GGaeqtS25Xw2Kwq+FNXkyLbscYjfysVtKPcrNcV/pQr6U6Mje+SJIZMblq8Yrba0F8PrV
C8+a5fBQpIs7R6UjW3p6+DM/uO+Zl+MgwdYoic+U+7lF7eNAFxHUdPALMeIrJmqbTFeurCA+ukV6
BfO9m2kVrn1OIGPENXY6BwLJN/3HR+7o8XYdcxXyl6S1yHp52UKqK39c/s4mT6NmgTWvRLpUHhww
MmWd5jyTXlBOeuM61G7MGvv50jeuJCqrVwMiKA1JdX+3KNp1v47j3A55MQIDAQABo0IwQDAdBgNV
HQ4EFgQUnZPGU4teyq8/nx4P5ZmVvCT2lI8wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC
AQYwDQYJKoZIhvcNAQELBQADggEBAFis9AQOzcAN/wr91LoWXym9e2iZWEnStB03TX8nfUYGXUPG
hi4+c7ImfU+TqbbEKpqrIZcUsd6M06uJFdhrJNTxFq7YpFzUf1GO7RgBsZNjvbz4YYCanrHOQnDi
qX0GJX0nof5v7LMeJNrjS1UaADs1tDvZ110w/YETifLCBivtZ8SOyUOyXGsViQK8YvxO8rUzqrJv
0wqiUOP2O+guRMLbZjipM1ZI8W0bM40NjD9gN53Tym1+NH4Nn3J2ixufcv1SNUFFApYvHLKac0kh
sUlHRUe072o0EclNmsxZt9YCnlpOZbWUrhvfKbAW8b8Angc6F2S1BLUjIZkKlTuXfO8=
-----END CERTIFICATE-----

AffirmTrust Networking
======================
-----BEGIN CERTIFICATE-----
MIIDTDCCAjSgAwIBAgIIfE8EORzUmS0wDQYJKoZIhvcNAQEFBQAwRDELMAkGA1UEBhMCVVMxFDAS
BgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVzdCBOZXR3b3JraW5nMB4XDTEw
MDEyOTE0MDgyNFoXDTMwMTIzMTE0MDgyNFowRDELMAkGA1UEBhMCVVMxFDASBgNVBAoMC0FmZmly
bVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVzdCBOZXR3b3JraW5nMIIBIjANBgkqhkiG9w0BAQEF
AAOCAQ8AMIIBCgKCAQEAtITMMxcua5Rsa2FSoOujz3mUTOWUgJnLVWREZY9nZOIG41w3SfYvm4SE
Hi3yYJ0wTsyEheIszx6e/jarM3c1RNg1lho9Nuh6DtjVR6FqaYvZ/Ls6rnla1fTWcbuakCNrmreI
dIcMHl+5ni36q1Mr3Lt2PpNMCAiMHqIjHNRqrSK6mQEubWXLviRmVSRLQESxG9fhwoXA3hA/Pe24
/PHxI1Pcv2WXb9n5QHGNfb2V1M6+oF4nI979ptAmDgAp6zxG8D1gvz9Q0twmQVGeFDdCBKNwV6gb
h+0t+nvujArjqWaJGctB+d1ENmHP4ndGyH329JKBNv3bNPFyfvMMFr20FQIDAQABo0IwQDAdBgNV
HQ4EFgQUBx/S55zawm6iQLSwelAQUHTEyL0wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC
AQYwDQYJKoZIhvcNAQEFBQADggEBAIlXshZ6qML91tmbmzTCnLQyFE2npN/svqe++EPbkTfOtDIu
UFUaNU52Q3Eg75N3ThVwLofDwR1t3Mu1J9QsVtFSUzpE0nPIxBsFZVpikpzuQY0x2+c06lkh1QF6
12S4ZDnNye2v7UsDSKegmQGA3GWjNq5lWUhPgkvIZfFXHeVZLgo/bNjR9eUJtGxUAArgFU2HdW23
WJZa3W3SAKD0m0i+wzekujbgfIeFlxoVot4uolu9rxj5kFDNcFn4J2dHy8egBzp90SxdbBk6ZrV9
/ZFvgrG+CJPbFEfxojfHRZ48x3evZKiT3/Zpg4Jg8klCNO1aAFSFHBY2kgxc+qatv9s=
-----END CERTIFICATE-----

AffirmTrust Premium
===================
-----BEGIN CERTIFICATE-----
MIIFRjCCAy6gAwIBAgIIbYwURrGmCu4wDQYJKoZIhvcNAQEMBQAwQTELMAkGA1UEBhMCVVMxFDAS
BgNVBAoMC0FmZmlybVRydXN0MRwwGgYDVQQDDBNBZmZpcm1UcnVzdCBQcmVtaXVtMB4XDTEwMDEy
OTE0MTAzNloXDTQwMTIzMTE0MTAzNlowQTELMAkGA1UEBhMCVVMxFDASBgNVBAoMC0FmZmlybVRy
dXN0MRwwGgYDVQQDDBNBZmZpcm1UcnVzdCBQcmVtaXVtMIICIjANBgkqhkiG9w0BAQEFAAOCAg8A
MIICCgKCAgEAxBLfqV/+Qd3d9Z+K4/as4Tx4mrzY8H96oDMq3I0gW64tb+eT2TZwamjPjlGjhVtn
BKAQJG9dKILBl1fYSCkTtuG+kU3fhQxTGJoeJKJPj/CihQvL9Cl/0qRY7iZNyaqoe5rZ+jjeRFcV
5fiMyNlI4g0WJx0eyIOFJbe6qlVBzAMiSy2RjYvmia9mx+n/K+k8rNrSs8PhaJyJ+HoAVt70VZVs
+7pk3WKL3wt3MutizCaam7uqYoNMtAZ6MMgpv+0GTZe5HMQxK9VfvFMSF5yZVylmd2EhMQcuJUmd
GPLu8ytxjLW6OQdJd/zvLpKQBY0tL3d770O/Nbua2Plzpyzy0FfuKE4mX4+QaAkvuPjcBukumj5R
p9EixAqnOEhss/n/fauGV+O61oV4d7pD6kh/9ti+I20ev9E2bFhc8e6kGVQa9QPSdubhjL08s9NI
S+LI+H+SqHZGnEJlPqQewQcDWkYtuJfzt9WyVSHvutxMAJf7FJUnM7/oQ0dG0giZFmA7mn7S5u04
6uwBHjxIVkkJx0w3AJ6IDsBz4W9m6XJHMD4Q5QsDyZpCAGzFlH5hxIrff4IaC1nEWTJ3s7xgaVY5
/bQGeyzWZDbZvUjthB9+pSKPKrhC9IK31FOQeE4tGv2Bb0TXOwF0lkLgAOIua+rF7nKsu7/+6qqo
+Nz2snmKtmcCAwEAAaNCMEAwHQYDVR0OBBYEFJ3AZ6YMItkm9UWrpmVSESfYRaxjMA8GA1UdEwEB
/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEBDAUAA4ICAQCzV00QYk465KzquByv
MiPIs0laUZx2KI15qldGF9X1Uva3ROgIRL8YhNILgM3FEv0AVQVhh0HctSSePMTYyPtwni94loMg
Nt58D2kTiKV1NpgIpsbfrM7jWNa3Pt668+s0QNiigfV4Py/VpfzZotReBA4Xrf5B8OWycvpEgjNC
6C1Y91aMYj+6QrCcDFx+LmUmXFNPALJ4fqENmS2NuB2OosSw/WDQMKSOyARiqcTtNd56l+0OOF6S
L5Nwpamcb6d9Ex1+xghIsV5n61EIJenmJWtSKZGc0jlzCFfemQa0W50QBuHCAKi4HEoCChTQwUHK
+4w1IX2COPKpVJEZNZOUbWo6xbLQu4mGk+ibyQ86p3q4ofB4Rvr8Ny/lioTz3/4E2aFooC8k4gmV
BtWVyuEklut89pMFu+1z6S3RdTnX5yTb2E5fQ4+e0BQ5v1VwSJlXMbSc7kqYA5YwH2AG7hsj/oFg
IxpHYoWlzBk0gG+zrBrjn/B7SK3VAdlntqlyk+otZrWyuOQ9PLLvTIzq6we/qzWaVYa8GKa1qF60
g2xraUDTn9zxw2lrueFtCfTxqlB2Cnp9ehehVZZCmTEJ3WARjQUwfuaORtGdFNrHF+QFlozEJLUb
zxQHskD4o55BhrwE0GuWyCqANP2/7waj3VjFhT0+j/6eKeC2uAloGRwYQw==
-----END CERTIFICATE-----

AffirmTrust Premium ECC
=======================
-----BEGIN CERTIFICATE-----
MIIB/jCCAYWgAwIBAgIIdJclisc/elQwCgYIKoZIzj0EAwMwRTELMAkGA1UEBhMCVVMxFDASBgNV
BAoMC0FmZmlybVRydXN0MSAwHgYDVQQDDBdBZmZpcm1UcnVzdCBQcmVtaXVtIEVDQzAeFw0xMDAx
MjkxNDIwMjRaFw00MDEyMzExNDIwMjRaMEUxCzAJBgNVBAYTAlVTMRQwEgYDVQQKDAtBZmZpcm1U
cnVzdDEgMB4GA1UEAwwXQWZmaXJtVHJ1c3QgUHJlbWl1bSBFQ0MwdjAQBgcqhkjOPQIBBgUrgQQA
IgNiAAQNMF4bFZ0D0KF5Nbc6PJJ6yhUczWLznCZcBz3lVPqj1swS6vQUX+iOGasvLkjmrBhDeKzQ
N8O9ss0s5kfiGuZjuD0uL3jET9v0D6RoTFVya5UdThhClXjMNzyR4ptlKymjQjBAMB0GA1UdDgQW
BBSaryl6wBE1NSZRMADDav5A1a7WPDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAK
BggqhkjOPQQDAwNnADBkAjAXCfOHiFBar8jAQr9HX/VsaobgxCd05DhT1wV/GzTjxi+zygk8N53X
57hG8f2h4nECMEJZh0PUUd+60wkyWs6Iflc9nF9Ca/UHLbXwgpP5WW+uZPpY5Yse42O+tYHNbwKM
eQ==
-----END CERTIFICATE-----

Certum Trusted Network CA
=========================
-----BEGIN CERTIFICATE-----
MIIDuzCCAqOgAwIBAgIDBETAMA0GCSqGSIb3DQEBBQUAMH4xCzAJBgNVBAYTAlBMMSIwIAYDVQQK
ExlVbml6ZXRvIFRlY2hub2xvZ2llcyBTLkEuMScwJQYDVQQLEx5DZXJ0dW0gQ2VydGlmaWNhdGlv
biBBdXRob3JpdHkxIjAgBgNVBAMTGUNlcnR1bSBUcnVzdGVkIE5ldHdvcmsgQ0EwHhcNMDgxMDIy
MTIwNzM3WhcNMjkxMjMxMTIwNzM3WjB+MQswCQYDVQQGEwJQTDEiMCAGA1UEChMZVW5pemV0byBU
ZWNobm9sb2dpZXMgUy5BLjEnMCUGA1UECxMeQ2VydHVtIENlcnRpZmljYXRpb24gQXV0aG9yaXR5
MSIwIAYDVQQDExlDZXJ0dW0gVHJ1c3RlZCBOZXR3b3JrIENBMIIBIjANBgkqhkiG9w0BAQEFAAOC
AQ8AMIIBCgKCAQEA4/t9o3K6wvDJFIf1awFO4W5AB7ptJ11/91sts1rHUV+rpDKmYYe2bg+G0jAC
l/jXaVehGDldamR5xgFZrDwxSjh80gTSSyjoIF87B6LMTXPb865Px1bVWqeWifrzq2jUI4ZZJ88J
J7ysbnKDHDBy3+Ci6dLhdHUZvSqeexVUBBvXQzmtVSjF4hq79MDkrjhJM8x2hZ85RdKknvISjFH4
fOQtf/WsX+sWn7Et0brMkUJ3TCXJkDhv2/DM+44el1k+1WBO5gUo7Ul5E0u6SNsv+XLTOcr+H9g0
cvW0QM8xAcPs3hEtF10fuFDRXhmnad4HMyjKUJX5p1TLVIZQRan5SQIDAQABo0IwQDAPBgNVHRMB
Af8EBTADAQH/MB0GA1UdDgQWBBQIds3LB/8k9sXN7buQvOKEN0Z19zAOBgNVHQ8BAf8EBAMCAQYw
DQYJKoZIhvcNAQEFBQADggEBAKaorSLOAT2mo/9i0Eidi15ysHhE49wcrwn9I0j6vSrEuVUEtRCj
jSfeC4Jj0O7eDDd5QVsisrCaQVymcODU0HfLI9MA4GxWL+FpDQ3Zqr8hgVDZBqWo/5U30Kr+4rP1
mS1FhIrlQgnXdAIv94nYmem8J9RHjboNRhx3zxSkHLmkMcScKHQDNP8zGSal6Q10tz6XxnboJ5aj
Zt3hrvJBW8qYVoNzcOSGGtIxQbovvi0TWnZvTuhOgQ4/WwMioBK+ZlgRSssDxLQqKi2WF+A5VLxI
03YnnZotBqbJ7DnSq9ufmgsnAjUpsUCV5/nonFWIGUbWtzT1fs45mtk48VH3Tyw=
-----END CERTIFICATE-----

TWCA Root Certification Authority
=================================
-----BEGIN CERTIFICATE-----
MIIDezCCAmOgAwIBAgIBATANBgkqhkiG9w0BAQUFADBfMQswCQYDVQQGEwJUVzESMBAGA1UECgwJ
VEFJV0FOLUNBMRAwDgYDVQQLDAdSb290IENBMSowKAYDVQQDDCFUV0NBIFJvb3QgQ2VydGlmaWNh
dGlvbiBBdXRob3JpdHkwHhcNMDgwODI4MDcyNDMzWhcNMzAxMjMxMTU1OTU5WjBfMQswCQYDVQQG
EwJUVzESMBAGA1UECgwJVEFJV0FOLUNBMRAwDgYDVQQLDAdSb290IENBMSowKAYDVQQDDCFUV0NB
IFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
AoIBAQCwfnK4pAOU5qfeCTiRShFAh6d8WWQUe7UREN3+v9XAu1bihSX0NXIP+FPQQeFEAcK0HMMx
QhZHhTMidrIKbw/lJVBPhYa+v5guEGcevhEFhgWQxFnQfHgQsIBct+HHK3XLfJ+utdGdIzdjp9xC
oi2SBBtQwXu4PhvJVgSLL1KbralW6cH/ralYhzC2gfeXRfwZVzsrb+RH9JlF/h3x+JejiB03HFyP
4HYlmlD4oFT/RJB2I9IyxsOrBr/8+7/zrX2SYgJbKdM1o5OaQ2RgXbL6Mv87BK9NQGr5x+PvI/1r
y+UPizgN7gr8/g+YnzAx3WxSZfmLgb4i4RxYA7qRG4kHAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIB
BjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRqOFsmjd6LWvJPelSDGRjjCDWmujANBgkqhkiG
9w0BAQUFAAOCAQEAPNV3PdrfibqHDAhUaiBQkr6wQT25JmSDCi/oQMCXKCeCMErJk/9q56YAf4lC
mtYR5VPOL8zy2gXE/uJQxDqGfczafhAJO5I1KlOy/usrBdlsXebQ79NqZp4VKIV66IIArB6nCWlW
QtNoURi+VJq/REG6Sb4gumlc7rh3zc5sH62Dlhh9DrUUOYTxKOkto557HnpyWoOzeW/vtPzQCqVY
T0bf+215WfKEIlKuD8z7fDvnaspHYcN6+NOSBB+4IIThNlQWx0DeO4pz3N/GCUzf7Nr/1FNCocny
Yh0igzyXxfkZYiesZSLX0zzG5Y6yU8xJzrww/nsOM5D77dIUkR8Hrw==
-----END CERTIFICATE-----

Security Communication RootCA2
==============================
-----BEGIN CERTIFICATE-----
MIIDdzCCAl+gAwIBAgIBADANBgkqhkiG9w0BAQsFADBdMQswCQYDVQQGEwJKUDElMCMGA1UEChMc
U0VDT00gVHJ1c3QgU3lzdGVtcyBDTy4sTFRELjEnMCUGA1UECxMeU2VjdXJpdHkgQ29tbXVuaWNh
dGlvbiBSb290Q0EyMB4XDTA5MDUyOTA1MDAzOVoXDTI5MDUyOTA1MDAzOVowXTELMAkGA1UEBhMC
SlAxJTAjBgNVBAoTHFNFQ09NIFRydXN0IFN5c3RlbXMgQ08uLExURC4xJzAlBgNVBAsTHlNlY3Vy
aXR5IENvbW11bmljYXRpb24gUm9vdENBMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB
ANAVOVKxUrO6xVmCxF1SrjpDZYBLx/KWvNs2l9amZIyoXvDjChz335c9S672XewhtUGrzbl+dp++
+T42NKA7wfYxEUV0kz1XgMX5iZnK5atq1LXaQZAQwdbWQonCv/Q4EpVMVAX3NuRFg3sUZdbcDE3R
3n4MqzvEFb46VqZab3ZpUql6ucjrappdUtAtCms1FgkQhNBqyjoGADdH5H5XTz+L62e4iKrFvlNV
spHEfbmwhRkGeC7bYRr6hfVKkaHnFtWOojnflLhwHyg/i/xAXmODPIMqGplrz95Zajv8bxbXH/1K
EOtOghY6rCcMU/Gt1SSwawNQwS08Ft1ENCcadfsCAwEAAaNCMEAwHQYDVR0OBBYEFAqFqXdlBZh8
QIH4D5csOPEK7DzPMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEB
CwUAA4IBAQBMOqNErLlFsceTfsgLCkLfZOoc7llsCLqJX2rKSpWeeo8HxdpFcoJxDjrSzG+ntKEj
u/Ykn8sX/oymzsLS28yN/HH8AynBbF0zX2S2ZTuJbxh2ePXcokgfGT+Ok+vx+hfuzU7jBBJV1uXk
3fs+BXziHV7Gp7yXT2g69ekuCkO2r1dcYmh8t/2jioSgrGK+KwmHNPBqAbubKVY8/gA3zyNs8U6q
tnRGEmyR7jTV7JqR50S+kDFy1UkC9gLl9B/rfNmWVan/7Ir5mUf/NVoCqgTLiluHcSmRvaS0eg29
mvVXIwAHIRc/SjnRBUkLp7Y3gaVdjKozXoEofKd9J+sAro03
-----END CERTIFICATE-----

Hellenic Academic and Research Institutions RootCA 2011
=======================================================
-----BEGIN CERTIFICATE-----
MIIEMTCCAxmgAwIBAgIBADANBgkqhkiG9w0BAQUFADCBlTELMAkGA1UEBhMCR1IxRDBCBgNVBAoT
O0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgQ2VydC4gQXV0aG9y
aXR5MUAwPgYDVQQDEzdIZWxsZW5pYyBBY2FkZW1pYyBhbmQgUmVzZWFyY2ggSW5zdGl0dXRpb25z
IFJvb3RDQSAyMDExMB4XDTExMTIwNjEzNDk1MloXDTMxMTIwMTEzNDk1MlowgZUxCzAJBgNVBAYT
AkdSMUQwQgYDVQQKEztIZWxsZW5pYyBBY2FkZW1pYyBhbmQgUmVzZWFyY2ggSW5zdGl0dXRpb25z
IENlcnQuIEF1dGhvcml0eTFAMD4GA1UEAxM3SGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJlc2VhcmNo
IEluc3RpdHV0aW9ucyBSb290Q0EgMjAxMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB
AKlTAOMupvaO+mDYLZU++CwqVE7NuYRhlFhPjz2L5EPzdYmNUeTDN9KKiE15HrcS3UN4SoqS5tdI
1Q+kOilENbgH9mgdVc04UfCMJDGFr4PJfel3r+0ae50X+bOdOFAPplp5kYCvN66m0zH7tSYJnTxa
71HFK9+WXesyHgLacEnsbgzImjeN9/E2YEsmLIKe0HjzDQ9jpFEw4fkrJxIH2Oq9GGKYsFk3fb7u
8yBRQlqD75O6aRXxYp2fmTmCobd0LovUxQt7L/DICto9eQqakxylKHJzkUOap9FNhYS5qXSPFEDH
3N6sQWRstBmbAmNtJGSPRLIl6s5ddAxjMlyNh+UCAwEAAaOBiTCBhjAPBgNVHRMBAf8EBTADAQH/
MAsGA1UdDwQEAwIBBjAdBgNVHQ4EFgQUppFC/RNhSiOeCKQp5dgTBCPuQSUwRwYDVR0eBEAwPqA8
MAWCAy5ncjAFggMuZXUwBoIELmVkdTAGggQub3JnMAWBAy5ncjAFgQMuZXUwBoEELmVkdTAGgQQu
b3JnMA0GCSqGSIb3DQEBBQUAA4IBAQAf73lB4XtuP7KMhjdCSk4cNx6NZrokgclPEg8hwAOXhiVt
XdMiKahsog2p6z0GW5k6x8zDmjR/qw7IThzh+uTczQ2+vyT+bOdrwg3IBp5OjWEopmr95fZi6hg8
TqBTnbI6nOulnJEWtk2C4AwFSKls9cz4y51JtPACpf1wA+2KIaWuE4ZJwzNzvoc7dIsXRSZMFpGD
/md9zU1jZ/rzAxKWeAaNsWftjj++n08C9bMJL/NMh98qy5V8AcysNnq/onN694/BtZqhFLKPM58N
7yLcZnuEvUUXBj08yrl3NI/K6s8/MT7jiOOASSXIl7WdmplNsDz4SgCbZN2fOUvRJ9e4
-----END CERTIFICATE-----

Actalis Authentication Root CA
==============================
-----BEGIN CERTIFICATE-----
MIIFuzCCA6OgAwIBAgIIVwoRl0LE48wwDQYJKoZIhvcNAQELBQAwazELMAkGA1UEBhMCSVQxDjAM
BgNVBAcMBU1pbGFuMSMwIQYDVQQKDBpBY3RhbGlzIFMucC5BLi8wMzM1ODUyMDk2NzEnMCUGA1UE
AwweQWN0YWxpcyBBdXRoZW50aWNhdGlvbiBSb290IENBMB4XDTExMDkyMjExMjIwMloXDTMwMDky
MjExMjIwMlowazELMAkGA1UEBhMCSVQxDjAMBgNVBAcMBU1pbGFuMSMwIQYDVQQKDBpBY3RhbGlz
IFMucC5BLi8wMzM1ODUyMDk2NzEnMCUGA1UEAwweQWN0YWxpcyBBdXRoZW50aWNhdGlvbiBSb290
IENBMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAp8bEpSmkLO/lGMWwUKNvUTufClrJ
wkg4CsIcoBh/kbWHuUA/3R1oHwiD1S0eiKD4j1aPbZkCkpAW1V8IbInX4ay8IMKx4INRimlNAJZa
by/ARH6jDuSRzVju3PvHHkVH3Se5CAGfpiEd9UEtL0z9KK3giq0itFZljoZUj5NDKd45RnijMCO6
zfB9E1fAXdKDa0hMxKufgFpbOr3JpyI/gCczWw63igxdBzcIy2zSekciRDXFzMwujt0q7bd9Zg1f
YVEiVRvjRuPjPdA1YprbrxTIW6HMiRvhMCb8oJsfgadHHwTrozmSBp+Z07/T6k9QnBn+locePGX2
oxgkg4YQ51Q+qDp2JE+BIcXjDwL4k5RHILv+1A7TaLndxHqEguNTVHnd25zS8gebLra8Pu2Fbe8l
EfKXGkJh90qX6IuxEAf6ZYGyojnP9zz/GPvG8VqLWeICrHuS0E4UT1lF9gxeKF+w6D9Fz8+vm2/7
hNN3WpVvrJSEnu68wEqPSpP4RCHiMUVhUE4Q2OM1fEwZtN4Fv6MGn8i1zeQf1xcGDXqVdFUNaBr8
EBtiZJ1t4JWgw5QHVw0U5r0F+7if5t+L4sbnfpb2U8WANFAoWPASUHEXMLrmeGO89LKtmyuy/uE5
jF66CyCU3nuDuP/jVo23Eek7jPKxwV2dpAtMK9myGPW1n0sCAwEAAaNjMGEwHQYDVR0OBBYEFFLY
iDrIn3hm7YnzezhwlMkCAjbQMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUUtiIOsifeGbt
ifN7OHCUyQICNtAwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEBCwUAA4ICAQALe3KHwGCmSUyI
WOYdiPcUZEim2FgKDk8TNd81HdTtBjHIgT5q1d07GjLukD0R0i70jsNjLiNmsGe+b7bAEzlgqqI0
JZN1Ut6nna0Oh4lScWoWPBkdg/iaKWW+9D+a2fDzWochcYBNy+A4mz+7+uAwTc+G02UQGRjRlwKx
K3JCaKygvU5a2hi/a5iB0P2avl4VSM0RFbnAKVy06Ij3Pjaut2L9HmLecHgQHEhb2rykOLpn7VU+
Xlff1ANATIGk0k9jpwlCCRT8AKnCgHNPLsBA2RF7SOp6AsDT6ygBJlh0wcBzIm2Tlf05fbsq4/aC
4yyXX04fkZT6/iyj2HYauE2yOE+b+h1IYHkm4vP9qdCa6HCPSXrW5b0KDtst842/6+OkfcvHlXHo
2qN8xcL4dJIEG4aspCJTQLas/kx2z/uUMsA1n3Y/buWQbqCmJqK4LL7RK4X9p2jIugErsWx0Hbhz
lefut8cl8ABMALJ+tguLHPPAUJ4lueAI3jZm/zel0btUZCzJJ7VLkn5l/9Mt4blOvH+kQSGQQXem
OR/qnuOf0GZvBeyqdn6/axag67XH/JJULysRJyU3eExRarDzzFhdFPFqSBX/wge2sY0PjlxQRrM9
vwGYT7JZVEc+NHt4bVaTLnPqZih4zR0Uv6CPLy64Lo7yFIrM6bV8+2ydDKXhlg==
-----END CERTIFICATE-----

Buypass Class 2 Root CA
=======================
-----BEGIN CERTIFICATE-----
MIIFWTCCA0GgAwIBAgIBAjANBgkqhkiG9w0BAQsFADBOMQswCQYDVQQGEwJOTzEdMBsGA1UECgwU
QnV5cGFzcyBBUy05ODMxNjMzMjcxIDAeBgNVBAMMF0J1eXBhc3MgQ2xhc3MgMiBSb290IENBMB4X
DTEwMTAyNjA4MzgwM1oXDTQwMTAyNjA4MzgwM1owTjELMAkGA1UEBhMCTk8xHTAbBgNVBAoMFEJ1
eXBhc3MgQVMtOTgzMTYzMzI3MSAwHgYDVQQDDBdCdXlwYXNzIENsYXNzIDIgUm9vdCBDQTCCAiIw
DQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANfHXvfBB9R3+0Mh9PT1aeTuMgHbo4Yf5FkNuud1
g1Lr6hxhFUi7HQfKjK6w3Jad6sNgkoaCKHOcVgb/S2TwDCo3SbXlzwx87vFKu3MwZfPVL4O2fuPn
9Z6rYPnT8Z2SdIrkHJasW4DptfQxh6NR/Md+oW+OU3fUl8FVM5I+GC911K2GScuVr1QGbNgGE41b
/+EmGVnAJLqBcXmQRFBoJJRfuLMR8SlBYaNByyM21cHxMlAQTn/0hpPshNOOvEu/XAFOBz3cFIqU
CqTqc/sLUegTBxj6DvEr0VQVfTzh97QZQmdiXnfgolXsttlpF9U6r0TtSsWe5HonfOV116rLJeff
awrbD02TTqigzXsu8lkBarcNuAeBfos4GzjmCleZPe4h6KP1DBbdi+w0jpwqHAAVF41og9JwnxgI
zRFo1clrUs3ERo/ctfPYV3Me6ZQ5BL/T3jjetFPsaRyifsSP5BtwrfKi+fv3FmRmaZ9JUaLiFRhn
Bkp/1Wy1TbMz4GHrXb7pmA8y1x1LPC5aAVKRCfLf6o3YBkBjqhHk/sM3nhRSP/TizPJhk9H9Z2vX
Uq6/aKtAQ6BXNVN48FP4YUIHZMbXb5tMOA1jrGKvNouicwoN9SG9dKpN6nIDSdvHXx1iY8f93ZHs
M+71bbRuMGjeyNYmsHVee7QHIJihdjK4TWxPAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYD
VR0OBBYEFMmAd+BikoL1RpzzuvdMw964o605MA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsF
AAOCAgEAU18h9bqwOlI5LJKwbADJ784g7wbylp7ppHR/ehb8t/W2+xUbP6umwHJdELFx7rxP462s
A20ucS6vxOOto70MEae0/0qyexAQH6dXQbLArvQsWdZHEIjzIVEpMMpghq9Gqx3tOluwlN5E40EI
osHsHdb9T7bWR9AUC8rmyrV7d35BH16Dx7aMOZawP5aBQW9gkOLo+fsicdl9sz1Gv7SEr5AcD48S
aq/v7h56rgJKihcrdv6sVIkkLE8/trKnToyokZf7KcZ7XC25y2a2t6hbElGFtQl+Ynhw/qlqYLYd
DnkM/crqJIByw5c/8nerQyIKx+u2DISCLIBrQYoIwOula9+ZEsuK1V6ADJHgJgg2SMX6OBE1/yWD
LfJ6v9r9jv6ly0UsH8SIU653DtmadsWOLB2jutXsMq7Aqqz30XpN69QH4kj3Io6wpJ9qzo6ysmD0
oyLQI+uUWnpp3Q+/QFesa1lQ2aOZ4W7+jQF5JyMV3pKdewlNWudLSDBaGOYKbeaP4NK75t98biGC
wWg5TbSYWGZizEqQXsP6JwSxeRV0mcy+rSDeJmAc61ZRpqPq5KM/p/9h3PFaTWwyI0PurKju7koS
CTxdccK+efrCh2gdC/1cacwG0Jp9VJkqyTkaGa9LKkPzY11aWOIv4x3kqdbQCtCev9eBCfHJxyYN
rJgWVqA=
-----END CERTIFICATE-----

Buypass Class 3 Root CA
=======================
-----BEGIN CERTIFICATE-----
MIIFWTCCA0GgAwIBAgIBAjANBgkqhkiG9w0BAQsFADBOMQswCQYDVQQGEwJOTzEdMBsGA1UECgwU
QnV5cGFzcyBBUy05ODMxNjMzMjcxIDAeBgNVBAMMF0J1eXBhc3MgQ2xhc3MgMyBSb290IENBMB4X
DTEwMTAyNjA4Mjg1OFoXDTQwMTAyNjA4Mjg1OFowTjELMAkGA1UEBhMCTk8xHTAbBgNVBAoMFEJ1
eXBhc3MgQVMtOTgzMTYzMzI3MSAwHgYDVQQDDBdCdXlwYXNzIENsYXNzIDMgUm9vdCBDQTCCAiIw
DQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAKXaCpUWUOOV8l6ddjEGMnqb8RB2uACatVI2zSRH
sJ8YZLya9vrVediQYkwiL944PdbgqOkcLNt4EemOaFEVcsfzM4fkoF0LXOBXByow9c3EN3coTRiR
5r/VUv1xLXA+58bEiuPwKAv0dpihi4dVsjoT/Lc+JzeOIuOoTyrvYLs9tznDDgFHmV0ST9tD+leh
7fmdvhFHJlsTmKtdFoqwNxxXnUX/iJY2v7vKB3tvh2PX0DJq1l1sDPGzbjniazEuOQAnFN44wOwZ
ZoYS6J1yFhNkUsepNxz9gjDthBgd9K5c/3ATAOux9TN6S9ZV+AWNS2mw9bMoNlwUxFFzTWsL8TQH
2xc519woe2v1n/MuwU8XKhDzzMro6/1rqy6any2CbgTUUgGTLT2G/H783+9CHaZr77kgxve9oKeV
/afmiSTYzIw0bOIjL9kSGiG5VZFvC5F5GQytQIgLcOJ60g7YaEi7ghM5EFjp2CoHxhLbWNvSO1UQ
RwUVZ2J+GGOmRj8JDlQyXr8NYnon74Do29lLBlo3WiXQCBJ31G8JUJc9yB3D34xFMFbG02SrZvPA
Xpacw8Tvw3xrizp5f7NJzz3iiZ+gMEuFuZyUJHmPfWupRWgPK9Dx2hzLabjKSWJtyNBjYt1gD1iq
j6G8BaVmos8bdrKEZLFMOVLAMLrwjEsCsLa3AgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYD
VR0OBBYEFEe4zf/lb+74suwvTg75JbCOPGvDMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsF
AAOCAgEAACAjQTUEkMJAYmDv4jVM1z+s4jSQuKFvdvoWFqRINyzpkMLyPPgKn9iB5btb2iUspKdV
cSQy9sgL8rxq+JOssgfCX5/bzMiKqr5qb+FJEMwx14C7u8jYog5kV+qi9cKpMRXSIGrs/CIBKM+G
uIAeqcwRpTzyFrNHnfzSgCHEy9BHcEGhyoMZCCxt8l13nIoUE9Q2HJLw5QY33KbmkJs4j1xrG0aG
Q0JfPgEHU1RdZX33inOhmlRaHylDFCfChQ+1iHsaO5S3HWCntZznKWlXWpuTekMwGwPXYshApqr8
ZORK15FTAaggiG6cX0S5y2CBNOxv033aSF/rtJC8LakcC6wc1aJoIIAE1vyxjy+7SjENSoYc6+I2
KSb12tjE8nVhz36udmNKekBlk4f4HoCMhuWG1o8O/FMsYOgWYRqiPkN7zTlgVGr18okmAWiDSKIz
6MkEkbIRNBE+6tBDGR8Dk5AM/1E9V/RBbuHLoL7ryWPNbczk+DaqaJ3tvV2XcEQNtg413OEMXbug
UZTLfhbrES+jkkXITHHZvMmZUldGL1DPvTVp9D0VzgalLA8+9oG6lLvDu79leNKGef9JOxqDDPDe
eOzI8k1MGt6CKfjBWtrt7uYnXuhF0J0cUahoq0Tj0Itq4/g7u9xN12TyUb7mqqta6THuBrxzvxNi
Cp/HuZc=
-----END CERTIFICATE-----

T-TeleSec GlobalRoot Class 3
============================
-----BEGIN CERTIFICATE-----
MIIDwzCCAqugAwIBAgIBATANBgkqhkiG9w0BAQsFADCBgjELMAkGA1UEBhMCREUxKzApBgNVBAoM
IlQtU3lzdGVtcyBFbnRlcnByaXNlIFNlcnZpY2VzIEdtYkgxHzAdBgNVBAsMFlQtU3lzdGVtcyBU
cnVzdCBDZW50ZXIxJTAjBgNVBAMMHFQtVGVsZVNlYyBHbG9iYWxSb290IENsYXNzIDMwHhcNMDgx
MDAxMTAyOTU2WhcNMzMxMDAxMjM1OTU5WjCBgjELMAkGA1UEBhMCREUxKzApBgNVBAoMIlQtU3lz
dGVtcyBFbnRlcnByaXNlIFNlcnZpY2VzIEdtYkgxHzAdBgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBD
ZW50ZXIxJTAjBgNVBAMMHFQtVGVsZVNlYyBHbG9iYWxSb290IENsYXNzIDMwggEiMA0GCSqGSIb3
DQEBAQUAA4IBDwAwggEKAoIBAQC9dZPwYiJvJK7genasfb3ZJNW4t/zN8ELg63iIVl6bmlQdTQyK
9tPPcPRStdiTBONGhnFBSivwKixVA9ZIw+A5OO3yXDw/RLyTPWGrTs0NvvAgJ1gORH8EGoel15YU
NpDQSXuhdfsaa3Ox+M6pCSzyU9XDFES4hqX2iys52qMzVNn6chr3IhUciJFrf2blw2qAsCTz34ZF
iP0Zf3WHHx+xGwpzJFu5ZeAsVMhg02YXP+HMVDNzkQI6pn97djmiH5a2OK61yJN0HZ65tOVgnS9W
0eDrXltMEnAMbEQgqxHY9Bn20pxSN+f6tsIxO0rUFJmtxxr1XV/6B7h8DR/Wgx6zAgMBAAGjQjBA
MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBS1A/d2O2GCahKqGFPr
AyGUv/7OyjANBgkqhkiG9w0BAQsFAAOCAQEAVj3vlNW92nOyWL6ukK2YJ5f+AbGwUgC4TeQbIXQb
fsDuXmkqJa9c1h3a0nnJ85cp4IaH3gRZD/FZ1GSFS5mvJQQeyUapl96Cshtwn5z2r3Ex3XsFpSzT
ucpH9sry9uetuUg/vBa3wW306gmv7PO15wWeph6KU1HWk4HMdJP2udqmJQV0eVp+QD6CSyYRMG7h
P0HHRwA11fXT91Q+gT3aSWqas+8QPebrb9HIIkfLzM8BMZLZGOMivgkeGj5asuRrDFR6fUNOuIml
e9eiPZaGzPImNC1qkp2aGtAw4l1OBLBfiyB+d8E9lYLRRpo7PHi4b6HQDWSieB4pTpPDpFQUWw==
-----END CERTIFICATE-----

D-TRUST Root Class 3 CA 2 2009
==============================
-----BEGIN CERTIFICATE-----
MIIEMzCCAxugAwIBAgIDCYPzMA0GCSqGSIb3DQEBCwUAME0xCzAJBgNVBAYTAkRFMRUwEwYDVQQK
DAxELVRydXN0IEdtYkgxJzAlBgNVBAMMHkQtVFJVU1QgUm9vdCBDbGFzcyAzIENBIDIgMjAwOTAe
Fw0wOTExMDUwODM1NThaFw0yOTExMDUwODM1NThaME0xCzAJBgNVBAYTAkRFMRUwEwYDVQQKDAxE
LVRydXN0IEdtYkgxJzAlBgNVBAMMHkQtVFJVU1QgUm9vdCBDbGFzcyAzIENBIDIgMjAwOTCCASIw
DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANOySs96R+91myP6Oi/WUEWJNTrGa9v+2wBoqOAD
ER03UAifTUpolDWzU9GUY6cgVq/eUXjsKj3zSEhQPgrfRlWLJ23DEE0NkVJD2IfgXU42tSHKXzlA
BF9bfsyjxiupQB7ZNoTWSPOSHjRGICTBpFGOShrvUD9pXRl/RcPHAY9RySPocq60vFYJfxLLHLGv
KZAKyVXMD9O0Gu1HNVpK7ZxzBCHQqr0ME7UAyiZsxGsMlFqVlNpQmvH/pStmMaTJOKDfHR+4CS7z
p+hnUquVH+BGPtikw8paxTGA6Eian5Rp/hnd2HN8gcqW3o7tszIFZYQ05ub9VxC1X3a/L7AQDcUC
AwEAAaOCARowggEWMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFP3aFMSfMN4hvR5COfyrYyNJ
4PGEMA4GA1UdDwEB/wQEAwIBBjCB0wYDVR0fBIHLMIHIMIGAoH6gfIZ6bGRhcDovL2RpcmVjdG9y
eS5kLXRydXN0Lm5ldC9DTj1ELVRSVVNUJTIwUm9vdCUyMENsYXNzJTIwMyUyMENBJTIwMiUyMDIw
MDksTz1ELVRydXN0JTIwR21iSCxDPURFP2NlcnRpZmljYXRlcmV2b2NhdGlvbmxpc3QwQ6BBoD+G
PWh0dHA6Ly93d3cuZC10cnVzdC5uZXQvY3JsL2QtdHJ1c3Rfcm9vdF9jbGFzc18zX2NhXzJfMjAw
OS5jcmwwDQYJKoZIhvcNAQELBQADggEBAH+X2zDI36ScfSF6gHDOFBJpiBSVYEQBrLLpME+bUMJm
2H6NMLVwMeniacfzcNsgFYbQDfC+rAF1hM5+n02/t2A7nPPKHeJeaNijnZflQGDSNiH+0LS4F9p0
o3/U37CYAqxva2ssJSRyoWXuJVrl5jLn8t+rSfrzkGkj2wTZ51xY/GXUl77M/C4KzCUqNQT4YJEV
dT1B/yMfGchs64JTBKbkTCJNjYy6zltz7GRUUG3RnFX7acM2w4y8PIWmawomDeCTmGCufsYkl4ph
X5GOZpIJhzbNi5stPvZR1FDUWSi9g/LMKHtThm3YJohw1+qRzT65ysCQblrGXnRl11z+o+I=
-----END CERTIFICATE-----

D-TRUST Root Class 3 CA 2 EV 2009
=================================
-----BEGIN CERTIFICATE-----
MIIEQzCCAyugAwIBAgIDCYP0MA0GCSqGSIb3DQEBCwUAMFAxCzAJBgNVBAYTAkRFMRUwEwYDVQQK
DAxELVRydXN0IEdtYkgxKjAoBgNVBAMMIUQtVFJVU1QgUm9vdCBDbGFzcyAzIENBIDIgRVYgMjAw
OTAeFw0wOTExMDUwODUwNDZaFw0yOTExMDUwODUwNDZaMFAxCzAJBgNVBAYTAkRFMRUwEwYDVQQK
DAxELVRydXN0IEdtYkgxKjAoBgNVBAMMIUQtVFJVU1QgUm9vdCBDbGFzcyAzIENBIDIgRVYgMjAw
OTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJnxhDRwui+3MKCOvXwEz75ivJn9gpfS
egpnljgJ9hBOlSJzmY3aFS3nBfwZcyK3jpgAvDw9rKFs+9Z5JUut8Mxk2og+KbgPCdM03TP1YtHh
zRnp7hhPTFiu4h7WDFsVWtg6uMQYZB7jM7K1iXdODL/ZlGsTl28So/6ZqQTMFexgaDbtCHu39b+T
7WYxg4zGcTSHThfqr4uRjRxWQa4iN1438h3Z0S0NL2lRp75mpoo6Kr3HGrHhFPC+Oh25z1uxav60
sUYgovseO3Dvk5h9jHOW8sXvhXCtKSb8HgQ+HKDYD8tSg2J87otTlZCpV6LqYQXY+U3EJ/pure35
11H3a6UCAwEAAaOCASQwggEgMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFNOUikxiEyoZLsyv
cop9NteaHNxnMA4GA1UdDwEB/wQEAwIBBjCB3QYDVR0fBIHVMIHSMIGHoIGEoIGBhn9sZGFwOi8v
ZGlyZWN0b3J5LmQtdHJ1c3QubmV0L0NOPUQtVFJVU1QlMjBSb290JTIwQ2xhc3MlMjAzJTIwQ0El
MjAyJTIwRVYlMjAyMDA5LE89RC1UcnVzdCUyMEdtYkgsQz1ERT9jZXJ0aWZpY2F0ZXJldm9jYXRp
b25saXN0MEagRKBChkBodHRwOi8vd3d3LmQtdHJ1c3QubmV0L2NybC9kLXRydXN0X3Jvb3RfY2xh
c3NfM19jYV8yX2V2XzIwMDkuY3JsMA0GCSqGSIb3DQEBCwUAA4IBAQA07XtaPKSUiO8aEXUHL7P+
PPoeUSbrh/Yp3uDx1MYkCenBz1UbtDDZzhr+BlGmFaQt77JLvyAoJUnRpjZ3NOhk31KxEcdzes05
nsKtjHEh8lprr988TlWvsoRlFIm5d8sqMb7Po23Pb0iUMkZv53GMoKaEGTcH8gNFCSuGdXzfX2lX
ANtu2KZyIktQ1HWYVt+3GP9DQ1CuekR78HlR10M9p9OB0/DJT7naxpeG0ILD5EJt/rDiZE4OJudA
NCa1CInXCGNjOCd1HjPqbqjdn5lPdE2BiYBL3ZqXKVwvvoFBuYz/6n1gBp7N1z3TLqMVvKjmJuVv
w9y4AyHqnxbxLFS1
-----END CERTIFICATE-----

CA Disig Root R2
================
-----BEGIN CERTIFICATE-----
MIIFaTCCA1GgAwIBAgIJAJK4iNuwisFjMA0GCSqGSIb3DQEBCwUAMFIxCzAJBgNVBAYTAlNLMRMw
EQYDVQQHEwpCcmF0aXNsYXZhMRMwEQYDVQQKEwpEaXNpZyBhLnMuMRkwFwYDVQQDExBDQSBEaXNp
ZyBSb290IFIyMB4XDTEyMDcxOTA5MTUzMFoXDTQyMDcxOTA5MTUzMFowUjELMAkGA1UEBhMCU0sx
EzARBgNVBAcTCkJyYXRpc2xhdmExEzARBgNVBAoTCkRpc2lnIGEucy4xGTAXBgNVBAMTEENBIERp
c2lnIFJvb3QgUjIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCio8QACdaFXS1tFPbC
w3OeNcJxVX6B+6tGUODBfEl45qt5WDza/3wcn9iXAng+a0EE6UG9vgMsRfYvZNSrXaNHPWSb6Wia
xswbP7q+sos0Ai6YVRn8jG+qX9pMzk0DIaPY0jSTVpbLTAwAFjxfGs3Ix2ymrdMxp7zo5eFm1tL7
A7RBZckQrg4FY8aAamkw/dLukO8NJ9+flXP04SXabBbeQTg06ov80egEFGEtQX6sx3dOy1FU+16S
GBsEWmjGycT6txOgmLcRK7fWV8x8nhfRyyX+hk4kLlYMeE2eARKmK6cBZW58Yh2EhN/qwGu1pSqV
g8NTEQxzHQuyRpDRQjrOQG6Vrf/GlK1ul4SOfW+eioANSW1z4nuSHsPzwfPrLgVv2RvPN3YEyLRa
5Beny912H9AZdugsBbPWnDTYltxhh5EF5EQIM8HauQhl1K6yNg3ruji6DOWbnuuNZt2Zz9aJQfYE
koopKW1rOhzndX0CcQ7zwOe9yxndnWCywmZgtrEE7snmhrmaZkCo5xHtgUUDi/ZnWejBBhG93c+A
Ak9lQHhcR1DIm+YfgXvkRKhbhZri3lrVx/k6RGZL5DJUfORsnLMOPReisjQS1n6yqEm70XooQL6i
Fh/f5DcfEXP7kAplQ6INfPgGAVUzfbANuPT1rqVCV3w2EYx7XsQDnYx5nQIDAQABo0IwQDAPBgNV
HRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUtZn4r7CU9eMg1gqtzk5WpC5u
Qu0wDQYJKoZIhvcNAQELBQADggIBACYGXnDnZTPIgm7ZnBc6G3pmsgH2eDtpXi/q/075KMOYKmFM
tCQSin1tERT3nLXK5ryeJ45MGcipvXrA1zYObYVybqjGom32+nNjf7xueQgcnYqfGopTpti72TVV
sRHFqQOzVju5hJMiXn7B9hJSi+osZ7z+Nkz1uM/Rs0mSO9MpDpkblvdhuDvEK7Z4bLQjb/D907Je
dR+Zlais9trhxTF7+9FGs9K8Z7RiVLoJ92Owk6Ka+elSLotgEqv89WBW7xBci8QaQtyDW2QOy7W8
1k/BfDxujRNt+3vrMNDcTa/F1balTFtxyegxvug4BkihGuLq0t4SOVga/4AOgnXmt8kHbA7v/zjx
mHHEt38OFdAlab0inSvtBfZGR6ztwPDUO+Ls7pZbkBNOHlY667DvlruWIxG68kOGdGSVyCh13x01
utI3gzhTODY7z2zp+WsO0PsE6E9312UBeIYMej4hYvF/Y3EMyZ9E26gnonW+boE+18DrG5gPcFw0
sorMwIUY6256s/daoQe/qUKS82Ail+QUoQebTnbAjn39pCXHR+3/H3OszMOl6W8KjptlwlCFtaOg
UxLMVYdh84GuEEZhvUQhuMI9dM9+JDX6HAcOmz0iyu8xL4ysEr3vQCj8KWefshNPZiTEUxnpHikV
7+ZtsH8tZ/3zbBt1RqPlShfppNcL
-----END CERTIFICATE-----

ACCVRAIZ1
=========
-----BEGIN CERTIFICATE-----
MIIH0zCCBbugAwIBAgIIXsO3pkN/pOAwDQYJKoZIhvcNAQEFBQAwQjESMBAGA1UEAwwJQUNDVlJB
SVoxMRAwDgYDVQQLDAdQS0lBQ0NWMQ0wCwYDVQQKDARBQ0NWMQswCQYDVQQGEwJFUzAeFw0xMTA1
MDUwOTM3MzdaFw0zMDEyMzEwOTM3MzdaMEIxEjAQBgNVBAMMCUFDQ1ZSQUlaMTEQMA4GA1UECwwH
UEtJQUNDVjENMAsGA1UECgwEQUNDVjELMAkGA1UEBhMCRVMwggIiMA0GCSqGSIb3DQEBAQUAA4IC
DwAwggIKAoICAQCbqau/YUqXry+XZpp0X9DZlv3P4uRm7x8fRzPCRKPfmt4ftVTdFXxpNRFvu8gM
jmoYHtiP2Ra8EEg2XPBjs5BaXCQ316PWywlxufEBcoSwfdtNgM3802/J+Nq2DoLSRYWoG2ioPej0
RGy9ocLLA76MPhMAhN9KSMDjIgro6TenGEyxCQ0jVn8ETdkXhBilyNpAlHPrzg5XPAOBOp0KoVdD
aaxXbXmQeOW1tDvYvEyNKKGno6e6Ak4l0Squ7a4DIrhrIA8wKFSVf+DuzgpmndFALW4ir50awQUZ
0m/A8p/4e7MCQvtQqR0tkw8jq8bBD5L/0KIV9VMJcRz/RROE5iZe+OCIHAr8Fraocwa48GOEAqDG
WuzndN9wrqODJerWx5eHk6fGioozl2A3ED6XPm4pFdahD9GILBKfb6qkxkLrQaLjlUPTAYVtjrs7
8yM2x/474KElB0iryYl0/wiPgL/AlmXz7uxLaL2diMMxs0Dx6M/2OLuc5NF/1OVYm3z61PMOm3WR
5LpSLhl+0fXNWhn8ugb2+1KoS5kE3fj5tItQo05iifCHJPqDQsGH+tUtKSpacXpkatcnYGMN285J
9Y0fkIkyF/hzQ7jSWpOGYdbhdQrqeWZ2iE9x6wQl1gpaepPluUsXQA+xtrn13k/c4LOsOxFwYIRK
Q26ZIMApcQrAZQIDAQABo4ICyzCCAscwfQYIKwYBBQUHAQEEcTBvMEwGCCsGAQUFBzAChkBodHRw
Oi8vd3d3LmFjY3YuZXMvZmlsZWFkbWluL0FyY2hpdm9zL2NlcnRpZmljYWRvcy9yYWl6YWNjdjEu
Y3J0MB8GCCsGAQUFBzABhhNodHRwOi8vb2NzcC5hY2N2LmVzMB0GA1UdDgQWBBTSh7Tj3zcnk1X2
VuqB5TbMjB4/vTAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFNKHtOPfNyeTVfZW6oHlNsyM
Hj+9MIIBcwYDVR0gBIIBajCCAWYwggFiBgRVHSAAMIIBWDCCASIGCCsGAQUFBwICMIIBFB6CARAA
QQB1AHQAbwByAGkAZABhAGQAIABkAGUAIABDAGUAcgB0AGkAZgBpAGMAYQBjAGkA8wBuACAAUgBh
AO0AegAgAGQAZQAgAGwAYQAgAEEAQwBDAFYAIAAoAEEAZwBlAG4AYwBpAGEAIABkAGUAIABUAGUA
YwBuAG8AbABvAGcA7QBhACAAeQAgAEMAZQByAHQAaQBmAGkAYwBhAGMAaQDzAG4AIABFAGwAZQBj
AHQAcgDzAG4AaQBjAGEALAAgAEMASQBGACAAUQA0ADYAMAAxADEANQA2AEUAKQAuACAAQwBQAFMA
IABlAG4AIABoAHQAdABwADoALwAvAHcAdwB3AC4AYQBjAGMAdgAuAGUAczAwBggrBgEFBQcCARYk
aHR0cDovL3d3dy5hY2N2LmVzL2xlZ2lzbGFjaW9uX2MuaHRtMFUGA1UdHwROMEwwSqBIoEaGRGh0
dHA6Ly93d3cuYWNjdi5lcy9maWxlYWRtaW4vQXJjaGl2b3MvY2VydGlmaWNhZG9zL3JhaXphY2N2
MV9kZXIuY3JsMA4GA1UdDwEB/wQEAwIBBjAXBgNVHREEEDAOgQxhY2N2QGFjY3YuZXMwDQYJKoZI
hvcNAQEFBQADggIBAJcxAp/n/UNnSEQU5CmH7UwoZtCPNdpNYbdKl02125DgBS4OxnnQ8pdpD70E
R9m+27Up2pvZrqmZ1dM8MJP1jaGo/AaNRPTKFpV8M9xii6g3+CfYCS0b78gUJyCpZET/LtZ1qmxN
YEAZSUNUY9rizLpm5U9EelvZaoErQNV/+QEnWCzI7UiRfD+mAM/EKXMRNt6GGT6d7hmKG9Ww7Y49
nCrADdg9ZuM8Db3VlFzi4qc1GwQA9j9ajepDvV+JHanBsMyZ4k0ACtrJJ1vnE5Bc5PUzolVt3OAJ
TS+xJlsndQAJxGJ3KQhfnlmstn6tn1QwIgPBHnFk/vk4CpYY3QIUrCPLBhwepH2NDd4nQeit2hW3
sCPdK6jT2iWH7ehVRE2I9DZ+hJp4rPcOVkkO1jMl1oRQQmwgEh0q1b688nCBpHBgvgW1m54ERL5h
I6zppSSMEYCUWqKiuUnSwdzRp+0xESyeGabu4VXhwOrPDYTkF7eifKXeVSUG7szAh1xA2syVP1Xg
Nce4hL60Xc16gwFy7ofmXx2utYXGJt/mwZrpHgJHnyqobalbz+xFd3+YJ5oyXSrjhO7FmGYvliAd
3djDJ9ew+f7Zfc3Qn48LFFhRny+Lwzgt3uiP1o2HpPVWQxaZLPSkVrQ0uGE3ycJYgBugl6H8WY3p
EfbRD0tVNEYqi4Y7
-----END CERTIFICATE-----

TWCA Global Root CA
===================
-----BEGIN CERTIFICATE-----
MIIFQTCCAymgAwIBAgICDL4wDQYJKoZIhvcNAQELBQAwUTELMAkGA1UEBhMCVFcxEjAQBgNVBAoT
CVRBSVdBTi1DQTEQMA4GA1UECxMHUm9vdCBDQTEcMBoGA1UEAxMTVFdDQSBHbG9iYWwgUm9vdCBD
QTAeFw0xMjA2MjcwNjI4MzNaFw0zMDEyMzExNTU5NTlaMFExCzAJBgNVBAYTAlRXMRIwEAYDVQQK
EwlUQUlXQU4tQ0ExEDAOBgNVBAsTB1Jvb3QgQ0ExHDAaBgNVBAMTE1RXQ0EgR2xvYmFsIFJvb3Qg
Q0EwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCwBdvI64zEbooh745NnHEKH1Jw7W2C
nJfF10xORUnLQEK1EjRsGcJ0pDFfhQKX7EMzClPSnIyOt7h52yvVavKOZsTuKwEHktSz0ALfUPZV
r2YOy+BHYC8rMjk1Ujoog/h7FsYYuGLWRyWRzvAZEk2tY/XTP3VfKfChMBwqoJimFb3u/Rk28OKR
Q4/6ytYQJ0lM793B8YVwm8rqqFpD/G2Gb3PpN0Wp8DbHzIh1HrtsBv+baz4X7GGqcXzGHaL3SekV
tTzWoWH1EfcFbx39Eb7QMAfCKbAJTibc46KokWofwpFFiFzlmLhxpRUZyXx1EcxwdE8tmx2RRP1W
KKD+u4ZqyPpcC1jcxkt2yKsi2XMPpfRaAok/T54igu6idFMqPVMnaR1sjjIsZAAmY2E2TqNGtz99
sy2sbZCilaLOz9qC5wc0GZbpuCGqKX6mOL6OKUohZnkfs8O1CWfe1tQHRvMq2uYiN2DLgbYPoA/p
yJV/v1WRBXrPPRXAb94JlAGD1zQbzECl8LibZ9WYkTunhHiVJqRaCPgrdLQABDzfuBSO6N+pjWxn
kjMdwLfS7JLIvgm/LCkFbwJrnu+8vyq8W8BQj0FwcYeyTbcEqYSjMq+u7msXi7Kx/mzhkIyIqJdI
zshNy/MGz19qCkKxHh53L46g5pIOBvwFItIm4TFRfTLcDwIDAQABoyMwITAOBgNVHQ8BAf8EBAMC
AQYwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAgEAXzSBdu+WHdXltdkCY4QWwa6g
cFGn90xHNcgL1yg9iXHZqjNB6hQbbCEAwGxCGX6faVsgQt+i0trEfJdLjbDorMjupWkEmQqSpqsn
LhpNgb+E1HAerUf+/UqdM+DyucRFCCEK2mlpc3INvjT+lIutwx4116KD7+U4x6WFH6vPNOw/KP4M
8VeGTslV9xzU2KV9Bnpv1d8Q34FOIWWxtuEXeZVFBs5fzNxGiWNoRI2T9GRwoD2dKAXDOXC4Ynsg
/eTb6QihuJ49CcdP+yz4k3ZB3lLg4VfSnQO8d57+nile98FRYB/e2guyLXW3Q0iT5/Z5xoRdgFlg
lPx4mI88k1HtQJAH32RjJMtOcQWh15QaiDLxInQirqWm2BJpTGCjAu4r7NRjkgtevi92a6O2JryP
A9gK8kxkRr05YuWW6zRjESjMlfGt7+/cgFhI6Uu46mWs6fyAtbXIRfmswZ/ZuepiiI7E8UuDEq3m
i4TWnsLrgxifarsbJGAzcMzs9zLzXNl5fe+epP7JI8Mk7hWSsT2RTyaGvWZzJBPqpK5jwa19hAM8
EHiGG3njxPPyBJUgriOCxLM6AGK/5jYk4Ve6xx6QddVfP5VhK8E7zeWzaGHQRiapIVJpLesux+t3
zqY6tQMzT3bR51xUAV3LePTJDL/PEo4XLSNolOer/qmyKwbQBM0=
-----END CERTIFICATE-----

TeliaSonera Root CA v1
======================
-----BEGIN CERTIFICATE-----
MIIFODCCAyCgAwIBAgIRAJW+FqD3LkbxezmCcvqLzZYwDQYJKoZIhvcNAQEFBQAwNzEUMBIGA1UE
CgwLVGVsaWFTb25lcmExHzAdBgNVBAMMFlRlbGlhU29uZXJhIFJvb3QgQ0EgdjEwHhcNMDcxMDE4
MTIwMDUwWhcNMzIxMDE4MTIwMDUwWjA3MRQwEgYDVQQKDAtUZWxpYVNvbmVyYTEfMB0GA1UEAwwW
VGVsaWFTb25lcmEgUm9vdCBDQSB2MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMK+
6yfwIaPzaSZVfp3FVRaRXP3vIb9TgHot0pGMYzHw7CTww6XScnwQbfQ3t+XmfHnqjLWCi65ItqwA
3GV17CpNX8GH9SBlK4GoRz6JI5UwFpB/6FcHSOcZrr9FZ7E3GwYq/t75rH2D+1665I+XZ75Ljo1k
B1c4VWk0Nj0TSO9P4tNmHqTPGrdeNjPUtAa9GAH9d4RQAEX1jF3oI7x+/jXh7VB7qTCNGdMJjmhn
Xb88lxhTuylixcpecsHHltTbLaC0H2kD7OriUPEMPPCs81Mt8Bz17Ww5OXOAFshSsCPN4D7c3TxH
oLs1iuKYaIu+5b9y7tL6pe0S7fyYGKkmdtwoSxAgHNN/Fnct7W+A90m7UwW7XWjH1Mh1Fj+JWov3
F0fUTPHSiXk+TT2YqGHeOh7S+F4D4MHJHIzTjU3TlTazN19jY5szFPAtJmtTfImMMsJu7D0hADnJ
oWjiUIMusDor8zagrC/kb2HCUQk5PotTubtn2txTuXZZNp1D5SDgPTJghSJRt8czu90VL6R4pgd7
gUY2BIbdeTXHlSw7sKMXNeVzH7RcWe/a6hBle3rQf5+ztCo3O3CLm1u5K7fsslESl1MpWtTwEhDc
TwK7EpIvYtQ/aUN8Ddb8WHUBiJ1YFkveupD/RwGJBmr2X7KQarMCpgKIv7NHfirZ1fpoeDVNAgMB
AAGjPzA9MA8GA1UdEwEB/wQFMAMBAf8wCwYDVR0PBAQDAgEGMB0GA1UdDgQWBBTwj1k4ALP1j5qW
DNXr+nuqF+gTEjANBgkqhkiG9w0BAQUFAAOCAgEAvuRcYk4k9AwI//DTDGjkk0kiP0Qnb7tt3oNm
zqjMDfz1mgbldxSR651Be5kqhOX//CHBXfDkH1e3damhXwIm/9fH907eT/j3HEbAek9ALCI18Bmx
0GtnLLCo4MBANzX2hFxc469CeP6nyQ1Q6g2EdvZR74NTxnr/DlZJLo961gzmJ1TjTQpgcmLNkQfW
pb/ImWvtxBnmq0wROMVvMeJuScg/doAmAyYp4Db29iBT4xdwNBedY2gea+zDTYa4EzAvXUYNR0PV
G6pZDrlcjQZIrXSHX8f8MVRBE+LHIQ6e4B4N4cB7Q4WQxYpYxmUKeFfyxiMPAdkgS94P+5KFdSpc
c41teyWRyu5FrgZLAMzTsVlQ2jqIOylDRl6XK1TOU2+NSueW+r9xDkKLfP0ooNBIytrEgUy7onOT
JsjrDNYmiLbAJM+7vVvrdX3pCI6GMyx5dwlppYn8s3CQh3aP0yK7Qs69cwsgJirQmz1wHiRszYd2
qReWt88NkvuOGKmYSdGe/mBEciG5Ge3C9THxOUiIkCR1VBatzvT4aRRkOfujuLpwQMcnHL/EVlP6
Y2XQ8xwOFvVrhlhNGNTkDY6lnVuR3HYkUD/GKvvZt5y11ubQ2egZixVxSK236thZiNSQvxaz2ems
WWFUyBy6ysHK4bkgTI86k4mloMy/0/Z1pHWWbVY=
-----END CERTIFICATE-----

E-Tugra Certification Authority
===============================
-----BEGIN CERTIFICATE-----
MIIGSzCCBDOgAwIBAgIIamg+nFGby1MwDQYJKoZIhvcNAQELBQAwgbIxCzAJBgNVBAYTAlRSMQ8w
DQYDVQQHDAZBbmthcmExQDA+BgNVBAoMN0UtVHXEn3JhIEVCRyBCaWxpxZ9pbSBUZWtub2xvamls
ZXJpIHZlIEhpem1ldGxlcmkgQS7Fni4xJjAkBgNVBAsMHUUtVHVncmEgU2VydGlmaWthc3lvbiBN
ZXJrZXppMSgwJgYDVQQDDB9FLVR1Z3JhIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTEzMDMw
NTEyMDk0OFoXDTIzMDMwMzEyMDk0OFowgbIxCzAJBgNVBAYTAlRSMQ8wDQYDVQQHDAZBbmthcmEx
QDA+BgNVBAoMN0UtVHXEn3JhIEVCRyBCaWxpxZ9pbSBUZWtub2xvamlsZXJpIHZlIEhpem1ldGxl
cmkgQS7Fni4xJjAkBgNVBAsMHUUtVHVncmEgU2VydGlmaWthc3lvbiBNZXJrZXppMSgwJgYDVQQD
DB9FLVR1Z3JhIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIICIjANBgkqhkiG9w0BAQEFAAOCAg8A
MIICCgKCAgEA4vU/kwVRHoViVF56C/UYB4Oufq9899SKa6VjQzm5S/fDxmSJPZQuVIBSOTkHS0vd
hQd2h8y/L5VMzH2nPbxHD5hw+IyFHnSOkm0bQNGZDbt1bsipa5rAhDGvykPL6ys06I+XawGb1Q5K
CKpbknSFQ9OArqGIW66z6l7LFpp3RMih9lRozt6Plyu6W0ACDGQXwLWTzeHxE2bODHnv0ZEoq1+g
ElIwcxmOj+GMB6LDu0rw6h8VqO4lzKRG+Bsi77MOQ7osJLjFLFzUHPhdZL3Dk14opz8n8Y4e0ypQ
BaNV2cvnOVPAmJ6MVGKLJrD3fY185MaeZkJVgkfnsliNZvcHfC425lAcP9tDJMW/hkd5s3kc91r0
E+xs+D/iWR+V7kI+ua2oMoVJl0b+SzGPWsutdEcf6ZG33ygEIqDUD13ieU/qbIWGvaimzuT6w+Gz
rt48Ue7LE3wBf4QOXVGUnhMMti6lTPk5cDZvlsouDERVxcr6XQKj39ZkjFqzAQqptQpHF//vkUAq
jqFGOjGY5RH8zLtJVor8udBhmm9lbObDyz51Sf6Pp+KJxWfXnUYTTjF2OySznhFlhqt/7x3U+Lzn
rFpct1pHXFXOVbQicVtbC/DP3KBhZOqp12gKY6fgDT+gr9Oq0n7vUaDmUStVkhUXU8u3Zg5mTPj5
dUyQ5xJwx0UCAwEAAaNjMGEwHQYDVR0OBBYEFC7j27JJ0JxUeVz6Jyr+zE7S6E5UMA8GA1UdEwEB
/wQFMAMBAf8wHwYDVR0jBBgwFoAULuPbsknQnFR5XPonKv7MTtLoTlQwDgYDVR0PAQH/BAQDAgEG
MA0GCSqGSIb3DQEBCwUAA4ICAQAFNzr0TbdF4kV1JI+2d1LoHNgQk2Xz8lkGpD4eKexd0dCrfOAK
kEh47U6YA5n+KGCRHTAduGN8qOY1tfrTYXbm1gdLymmasoR6d5NFFxWfJNCYExL/u6Au/U5Mh/jO
XKqYGwXgAEZKgoClM4so3O0409/lPun++1ndYYRP0lSWE2ETPo+Aab6TR7U1Q9Jauz1c77NCR807
VRMGsAnb/WP2OogKmW9+4c4bU2pEZiNRCHu8W1Ki/QY3OEBhj0qWuJA3+GbHeJAAFS6LrVE1Uweo
a2iu+U48BybNCAVwzDk/dr2l02cmAYamU9JgO3xDf1WKvJUawSg5TB9D0pH0clmKuVb8P7Sd2nCc
dlqMQ1DujjByTd//SffGqWfZbawCEeI6FiWnWAjLb1NBnEg4R2gz0dfHj9R0IdTDBZB6/86WiLEV
KV0jq9BgoRJP3vQXzTLlyb/IQ639Lo7xr+L0mPoSHyDYwKcMhcWQ9DstliaxLL5Mq+ux0orJ23gT
Dx4JnW2PAJ8C2sH6H3p6CcRK5ogql5+Ji/03X186zjhZhkuvcQu02PJwT58yE+Owp1fl2tpDy4Q0
8ijE6m30Ku/Ba3ba+367hTzSU8JNvnHhRdH9I2cNE3X7z2VnIp2usAnRCf8dNL/+I5c30jn6PQ0G
C7TbO6Orb1wdtn7os4I07QZcJA==
-----END CERTIFICATE-----

T-TeleSec GlobalRoot Class 2
============================
-----BEGIN CERTIFICATE-----
MIIDwzCCAqugAwIBAgIBATANBgkqhkiG9w0BAQsFADCBgjELMAkGA1UEBhMCREUxKzApBgNVBAoM
IlQtU3lzdGVtcyBFbnRlcnByaXNlIFNlcnZpY2VzIEdtYkgxHzAdBgNVBAsMFlQtU3lzdGVtcyBU
cnVzdCBDZW50ZXIxJTAjBgNVBAMMHFQtVGVsZVNlYyBHbG9iYWxSb290IENsYXNzIDIwHhcNMDgx
MDAxMTA0MDE0WhcNMzMxMDAxMjM1OTU5WjCBgjELMAkGA1UEBhMCREUxKzApBgNVBAoMIlQtU3lz
dGVtcyBFbnRlcnByaXNlIFNlcnZpY2VzIEdtYkgxHzAdBgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBD
ZW50ZXIxJTAjBgNVBAMMHFQtVGVsZVNlYyBHbG9iYWxSb290IENsYXNzIDIwggEiMA0GCSqGSIb3
DQEBAQUAA4IBDwAwggEKAoIBAQCqX9obX+hzkeXaXPSi5kfl82hVYAUdAqSzm1nzHoqvNK38DcLZ
SBnuaY/JIPwhqgcZ7bBcrGXHX+0CfHt8LRvWurmAwhiCFoT6ZrAIxlQjgeTNuUk/9k9uN0goOA/F
vudocP05l03Sx5iRUKrERLMjfTlH6VJi1hKTXrcxlkIF+3anHqP1wvzpesVsqXFP6st4vGCvx970
2cu+fjOlbpSD8DT6IavqjnKgP6TeMFvvhk1qlVtDRKgQFRzlAVfFmPHmBiiRqiDFt1MmUUOyCxGV
WOHAD3bZwI18gfNycJ5v/hqO2V81xrJvNHy+SE/iWjnX2J14np+GPgNeGYtEotXHAgMBAAGjQjBA
MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBS/WSA2AHmgoCJrjNXy
YdK4LMuCSjANBgkqhkiG9w0BAQsFAAOCAQEAMQOiYQsfdOhyNsZt+U2e+iKo4YFWz827n+qrkRk4
r6p8FU3ztqONpfSO9kSpp+ghla0+AGIWiPACuvxhI+YzmzB6azZie60EI4RYZeLbK4rnJVM3YlNf
vNoBYimipidx5joifsFvHZVwIEoHNN/q/xWA5brXethbdXwFeilHfkCoMRN3zUA7tFFHei4R40cR
3p1m0IvVVGb6g1XqfMIpiRvpb7PO4gWEyS8+eIVibslfwXhjdFjASBgMmTnrpMwatXlajRWc2BQN
9noHV8cigwUtPJslJj0Ys6lDfMjIq2SPDqO/nBudMNva0Bkuqjzx+zOAduTNrRlPBSeOE6Fuwg==
-----END CERTIFICATE-----

Atos TrustedRoot 2011
=====================
-----BEGIN CERTIFICATE-----
MIIDdzCCAl+gAwIBAgIIXDPLYixfszIwDQYJKoZIhvcNAQELBQAwPDEeMBwGA1UEAwwVQXRvcyBU
cnVzdGVkUm9vdCAyMDExMQ0wCwYDVQQKDARBdG9zMQswCQYDVQQGEwJERTAeFw0xMTA3MDcxNDU4
MzBaFw0zMDEyMzEyMzU5NTlaMDwxHjAcBgNVBAMMFUF0b3MgVHJ1c3RlZFJvb3QgMjAxMTENMAsG
A1UECgwEQXRvczELMAkGA1UEBhMCREUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCV
hTuXbyo7LjvPpvMpNb7PGKw+qtn4TaA+Gke5vJrf8v7MPkfoepbCJI419KkM/IL9bcFyYie96mvr
54rMVD6QUM+A1JX76LWC1BTFtqlVJVfbsVD2sGBkWXppzwO3bw2+yj5vdHLqqjAqc2K+SZFhyBH+
DgMq92og3AIVDV4VavzjgsG1xZ1kCWyjWZgHJ8cblithdHFsQ/H3NYkQ4J7sVaE3IqKHBAUsR320
HLliKWYoyrfhk/WklAOZuXCFteZI6o1Q/NnezG8HDt0Lcp2AMBYHlT8oDv3FdU9T1nSatCQujgKR
z3bFmx5VdJx4IbHwLfELn8LVlhgf8FQieowHAgMBAAGjfTB7MB0GA1UdDgQWBBSnpQaxLKYJYO7R
l+lwrrw7GWzbITAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFKelBrEspglg7tGX6XCuvDsZ
bNshMBgGA1UdIAQRMA8wDQYLKwYBBAGwLQMEAQEwDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEB
CwUAA4IBAQAmdzTblEiGKkGdLD4GkGDEjKwLVLgfuXvTBznk+j57sj1O7Z8jvZfza1zv7v1Apt+h
k6EKhqzvINB5Ab149xnYJDE0BAGmuhWawyfc2E8PzBhj/5kPDpFrdRbhIfzYJsdHt6bPWHJxfrrh
TZVHO8mvbaG0weyJ9rQPOLXiZNwlz6bb65pcmaHFCN795trV1lpFDMS3wrUU77QR/w4VtfX128a9
61qn8FYiqTxlVMYVqL2Gns2Dlmh6cYGJ4Qvh6hEbaAjMaZ7snkGeRDImeuKHCnE96+RapNLbxc3G
3mB/ufNPRJLvKrcYPqcZ2Qt9sTdBQrC6YB3y/gkRsPCHe6ed
-----END CERTIFICATE-----

QuoVadis Root CA 1 G3
=====================
-----BEGIN CERTIFICATE-----
MIIFYDCCA0igAwIBAgIUeFhfLq0sGUvjNwc1NBMotZbUZZMwDQYJKoZIhvcNAQELBQAwSDELMAkG
A1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAcBgNVBAMTFVF1b1ZhZGlzIFJv
b3QgQ0EgMSBHMzAeFw0xMjAxMTIxNzI3NDRaFw00MjAxMTIxNzI3NDRaMEgxCzAJBgNVBAYTAkJN
MRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMR4wHAYDVQQDExVRdW9WYWRpcyBSb290IENBIDEg
RzMwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCgvlAQjunybEC0BJyFuTHK3C3kEakE
PBtVwedYMB0ktMPvhd6MLOHBPd+C5k+tR4ds7FtJwUrVu4/sh6x/gpqG7D0DmVIB0jWerNrwU8lm
PNSsAgHaJNM7qAJGr6Qc4/hzWHa39g6QDbXwz8z6+cZM5cOGMAqNF34168Xfuw6cwI2H44g4hWf6
Pser4BOcBRiYz5P1sZK0/CPTz9XEJ0ngnjybCKOLXSoh4Pw5qlPafX7PGglTvF0FBM+hSo+LdoIN
ofjSxxR3W5A2B4GbPgb6Ul5jxaYA/qXpUhtStZI5cgMJYr2wYBZupt0lwgNm3fME0UDiTouG9G/l
g6AnhF4EwfWQvTA9xO+oabw4m6SkltFi2mnAAZauy8RRNOoMqv8hjlmPSlzkYZqn0ukqeI1RPToV
7qJZjqlc3sX5kCLliEVx3ZGZbHqfPT2YfF72vhZooF6uCyP8Wg+qInYtyaEQHeTTRCOQiJ/GKubX
9ZqzWB4vMIkIG1SitZgj7Ah3HJVdYdHLiZxfokqRmu8hqkkWCKi9YSgxyXSthfbZxbGL0eUQMk1f
iyA6PEkfM4VZDdvLCXVDaXP7a3F98N/ETH3Goy7IlXnLc6KOTk0k+17kBL5yG6YnLUlamXrXXAkg
t3+UuU/xDRxeiEIbEbfnkduebPRq34wGmAOtzCjvpUfzUwIDAQABo0IwQDAPBgNVHRMBAf8EBTAD
AQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUo5fW816iEOGrRZ88F2Q87gFwnMwwDQYJKoZI
hvcNAQELBQADggIBABj6W3X8PnrHX3fHyt/PX8MSxEBd1DKquGrX1RUVRpgjpeaQWxiZTOOtQqOC
MTaIzen7xASWSIsBx40Bz1szBpZGZnQdT+3Btrm0DWHMY37XLneMlhwqI2hrhVd2cDMT/uFPpiN3
GPoajOi9ZcnPP/TJF9zrx7zABC4tRi9pZsMbj/7sPtPKlL92CiUNqXsCHKnQO18LwIE6PWThv6ct
Tr1NxNgpxiIY0MWscgKCP6o6ojoilzHdCGPDdRS5YCgtW2jgFqlmgiNR9etT2DGbe+m3nUvriBbP
+V04ikkwj+3x6xn0dxoxGE1nVGwvb2X52z3sIexe9PSLymBlVNFxZPT5pqOBMzYzcfCkeF9OrYMh
3jRJjehZrJ3ydlo28hP0r+AJx2EqbPfgna67hkooby7utHnNkDPDs3b69fBsnQGQ+p6Q9pxyz0fa
wx/kNSBT8lTR32GDpgLiJTjehTItXnOQUl1CxM49S+H5GYQd1aJQzEH7QRTDvdbJWqNjZgKAvQU6
O0ec7AAmTPWIUb+oI38YB7AL7YsmoWTTYUrrXJ/es69nA7Mf3W1daWhpq1467HxpvMc7hU6eFbm0
FU/DlXpY18ls6Wy58yljXrQs8C097Vpl4KlbQMJImYFtnh8GKjwStIsPm6Ik8KaN1nrgS7ZklmOV
hMJKzRwuJIczYOXD
-----END CERTIFICATE-----

QuoVadis Root CA 2 G3
=====================
-----BEGIN CERTIFICATE-----
MIIFYDCCA0igAwIBAgIURFc0JFuBiZs18s64KztbpybwdSgwDQYJKoZIhvcNAQELBQAwSDELMAkG
A1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAcBgNVBAMTFVF1b1ZhZGlzIFJv
b3QgQ0EgMiBHMzAeFw0xMjAxMTIxODU5MzJaFw00MjAxMTIxODU5MzJaMEgxCzAJBgNVBAYTAkJN
MRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMR4wHAYDVQQDExVRdW9WYWRpcyBSb290IENBIDIg
RzMwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQChriWyARjcV4g/Ruv5r+LrI3HimtFh
ZiFfqq8nUeVuGxbULX1QsFN3vXg6YOJkApt8hpvWGo6t/x8Vf9WVHhLL5hSEBMHfNrMWn4rjyduY
NM7YMxcoRvynyfDStNVNCXJJ+fKH46nafaF9a7I6JaltUkSs+L5u+9ymc5GQYaYDFCDy54ejiK2t
oIz/pgslUiXnFgHVy7g1gQyjO/Dh4fxaXc6AcW34Sas+O7q414AB+6XrW7PFXmAqMaCvN+ggOp+o
MiwMzAkd056OXbxMmO7FGmh77FOm6RQ1o9/NgJ8MSPsc9PG/Srj61YxxSscfrf5BmrODXfKEVu+l
V0POKa2Mq1W/xPtbAd0jIaFYAI7D0GoT7RPjEiuA3GfmlbLNHiJuKvhB1PLKFAeNilUSxmn1uIZo
L1NesNKqIcGY5jDjZ1XHm26sGahVpkUG0CM62+tlXSoREfA7T8pt9DTEceT/AFr2XK4jYIVz8eQQ
sSWu1ZK7E8EM4DnatDlXtas1qnIhO4M15zHfeiFuuDIIfR0ykRVKYnLP43ehvNURG3YBZwjgQQvD
6xVu+KQZ2aKrr+InUlYrAoosFCT5v0ICvybIxo/gbjh9Uy3l7ZizlWNof/k19N+IxWA1ksB8aRxh
lRbQ694Lrz4EEEVlWFA4r0jyWbYW8jwNkALGcC4BrTwV1wIDAQABo0IwQDAPBgNVHRMBAf8EBTAD
AQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQU7edvdlq/YOxJW8ald7tyFnGbxD0wDQYJKoZI
hvcNAQELBQADggIBAJHfgD9DCX5xwvfrs4iP4VGyvD11+ShdyLyZm3tdquXK4Qr36LLTn91nMX66
AarHakE7kNQIXLJgapDwyM4DYvmL7ftuKtwGTTwpD4kWilhMSA/ohGHqPHKmd+RCroijQ1h5fq7K
pVMNqT1wvSAZYaRsOPxDMuHBR//47PERIjKWnML2W2mWeyAMQ0GaW/ZZGYjeVYg3UQt4XAoeo0L9
x52ID8DyeAIkVJOviYeIyUqAHerQbj5hLja7NQ4nlv1mNDthcnPxFlxHBlRJAHpYErAK74X9sbgz
dWqTHBLmYF5vHX/JHyPLhGGfHoJE+V+tYlUkmlKY7VHnoX6XOuYvHxHaU4AshZ6rNRDbIl9qxV6X
U/IyAgkwo1jwDQHVcsaxfGl7w/U2Rcxhbl5MlMVerugOXou/983g7aEOGzPuVBj+D77vfoRrQ+Nw
mNtddbINWQeFFSM51vHfqSYP1kjHs6Yi9TM3WpVHn3u6GBVv/9YUZINJ0gpnIdsPNWNgKCLjsZWD
zYWm3S8P52dSbrsvhXz1SnPnxT7AvSESBT/8twNJAlvIJebiVDj1eYeMHVOyToV7BjjHLPj4sHKN
JeV3UvQDHEimUF+IIDBu8oJDqz2XhOdT+yHBTw8imoa4WSr2Rz0ZiC3oheGe7IUIarFsNMkd7Egr
O3jtZsSOeWmD3n+M
-----END CERTIFICATE-----

QuoVadis Root CA 3 G3
=====================
-----BEGIN CERTIFICATE-----
MIIFYDCCA0igAwIBAgIULvWbAiin23r/1aOp7r0DoM8Sah0wDQYJKoZIhvcNAQELBQAwSDELMAkG
A1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAcBgNVBAMTFVF1b1ZhZGlzIFJv
b3QgQ0EgMyBHMzAeFw0xMjAxMTIyMDI2MzJaFw00MjAxMTIyMDI2MzJaMEgxCzAJBgNVBAYTAkJN
MRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMR4wHAYDVQQDExVRdW9WYWRpcyBSb290IENBIDMg
RzMwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCzyw4QZ47qFJenMioKVjZ/aEzHs286
IxSR/xl/pcqs7rN2nXrpixurazHb+gtTTK/FpRp5PIpM/6zfJd5O2YIyC0TeytuMrKNuFoM7pmRL
Mon7FhY4futD4tN0SsJiCnMK3UmzV9KwCoWdcTzeo8vAMvMBOSBDGzXRU7Ox7sWTaYI+FrUoRqHe
6okJ7UO4BUaKhvVZR74bbwEhELn9qdIoyhA5CcoTNs+cra1AdHkrAj80//ogaX3T7mH1urPnMNA3
I4ZyYUUpSFlob3emLoG+B01vr87ERRORFHAGjx+f+IdpsQ7vw4kZ6+ocYfx6bIrc1gMLnia6Et3U
VDmrJqMz6nWB2i3ND0/kA9HvFZcba5DFApCTZgIhsUfei5pKgLlVj7WiL8DWM2fafsSntARE60f7
5li59wzweyuxwHApw0BiLTtIadwjPEjrewl5qW3aqDCYz4ByA4imW0aucnl8CAMhZa634RylsSqi
Md5mBPfAdOhx3v89WcyWJhKLhZVXGqtrdQtEPREoPHtht+KPZ0/l7DxMYIBpVzgeAVuNVejH38DM
dyM0SXV89pgR6y3e7UEuFAUCf+D+IOs15xGsIs5XPd7JMG0QA4XN8f+MFrXBsj6IbGB/kE+V9/Yt
rQE5BwT6dYB9v0lQ7e/JxHwc64B+27bQ3RP+ydOc17KXqQIDAQABo0IwQDAPBgNVHRMBAf8EBTAD
AQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUxhfQvKjqAkPyGwaZXSuQILnXnOQwDQYJKoZI
hvcNAQELBQADggIBADRh2Va1EodVTd2jNTFGu6QHcrxfYWLopfsLN7E8trP6KZ1/AvWkyaiTt3px
KGmPc+FSkNrVvjrlt3ZqVoAh313m6Tqe5T72omnHKgqwGEfcIHB9UqM+WXzBusnIFUBhynLWcKzS
t/Ac5IYp8M7vaGPQtSCKFWGafoaYtMnCdvvMujAWzKNhxnQT5WvvoxXqA/4Ti2Tk08HS6IT7SdEQ
TXlm66r99I0xHnAUrdzeZxNMgRVhvLfZkXdxGYFgu/BYpbWcC/ePIlUnwEsBbTuZDdQdm2NnL9Du
DcpmvJRPpq3t/O5jrFc/ZSXPsoaP0Aj/uHYUbt7lJ+yreLVTubY/6CD50qi+YUbKh4yE8/nxoGib
Ih6BJpsQBJFxwAYf3KDTuVan45gtf4Od34wrnDKOMpTwATwiKp9Dwi7DmDkHOHv8XgBCH/MyJnmD
hPbl8MFREsALHgQjDFSlTC9JxUrRtm5gDWv8a4uFJGS3iQ6rJUdbPM9+Sb3H6QrG2vd+DhcI00iX
0HGS8A85PjRqHH3Y8iKuu2n0M7SmSFXRDw4m6Oy2Cy2nhTXN/VnIn9HNPlopNLk9hM6xZdRZkZFW
dSHBd575euFgndOtBBj0fOtek49TSiIp+EgrPk2GrFt/ywaZWWDYWGWVjUTR939+J399roD1B0y2
PpxxVJkES/1Y+Zj0
-----END CERTIFICATE-----

DigiCert Assured ID Root G2
===========================
-----BEGIN CERTIFICATE-----
MIIDljCCAn6gAwIBAgIQC5McOtY5Z+pnI7/Dr5r0SzANBgkqhkiG9w0BAQsFADBlMQswCQYDVQQG
EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSQw
IgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgRzIwHhcNMTMwODAxMTIwMDAwWhcNMzgw
MTE1MTIwMDAwWjBlMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQL
ExB3d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgRzIw
ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDZ5ygvUj82ckmIkzTz+GoeMVSAn61UQbVH
35ao1K+ALbkKz3X9iaV9JPrjIgwrvJUXCzO/GU1BBpAAvQxNEP4HteccbiJVMWWXvdMX0h5i89vq
bFCMP4QMls+3ywPgym2hFEwbid3tALBSfK+RbLE4E9HpEgjAALAcKxHad3A2m67OeYfcgnDmCXRw
VWmvo2ifv922ebPynXApVfSr/5Vh88lAbx3RvpO704gqu52/clpWcTs/1PPRCv4o76Pu2ZmvA9OP
YLfykqGxvYmJHzDNw6YuYjOuFgJ3RFrngQo8p0Quebg/BLxcoIfhG69Rjs3sLPr4/m3wOnyqi+Rn
lTGNAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBTO
w0q5mVXyuNtgv6l+vVa1lzan1jANBgkqhkiG9w0BAQsFAAOCAQEAyqVVjOPIQW5pJ6d1Ee88hjZv
0p3GeDgdaZaikmkuOGybfQTUiaWxMTeKySHMq2zNixya1r9I0jJmwYrA8y8678Dj1JGG0VDjA9tz
d29KOVPt3ibHtX2vK0LRdWLjSisCx1BL4GnilmwORGYQRI+tBev4eaymG+g3NJ1TyWGqolKvSnAW
hsI6yLETcDbYz+70CjTVW0z9B5yiutkBclzzTcHdDrEcDcRjvq30FPuJ7KJBDkzMyFdA0G4Dqs0M
jomZmWzwPDCvON9vvKO+KSAnq3T/EyJ43pdSVR6DtVQgA+6uwE9W3jfMw3+qBCe703e4YtsXfJwo
IhNzbM8m9Yop5w==
-----END CERTIFICATE-----

DigiCert Assured ID Root G3
===========================
-----BEGIN CERTIFICATE-----
MIICRjCCAc2gAwIBAgIQC6Fa+h3foLVJRK/NJKBs7DAKBggqhkjOPQQDAzBlMQswCQYDVQQGEwJV
UzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSQwIgYD
VQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgRzMwHhcNMTMwODAxMTIwMDAwWhcNMzgwMTE1
MTIwMDAwWjBlMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgRzMwdjAQ
BgcqhkjOPQIBBgUrgQQAIgNiAAQZ57ysRGXtzbg/WPuNsVepRC0FFfLvC/8QdJ+1YlJfZn4f5dwb
RXkLzMZTCp2NXQLZqVneAlr2lSoOjThKiknGvMYDOAdfVdp+CW7if17QRSAPWXYQ1qAk8C3eNvJs
KTmjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBTL0L2p4ZgF
UaFNN6KDec6NHSrkhDAKBggqhkjOPQQDAwNnADBkAjAlpIFFAmsSS3V0T8gj43DydXLefInwz5Fy
YZ5eEJJZVrmDxxDnOOlYJjZ91eQ0hjkCMHw2U/Aw5WJjOpnitqM7mzT6HtoQknFekROn3aRukswy
1vUhZscv6pZjamVFkpUBtA==
-----END CERTIFICATE-----

DigiCert Global Root G2
=======================
-----BEGIN CERTIFICATE-----
MIIDjjCCAnagAwIBAgIQAzrx5qcRqaC7KGSxHQn65TANBgkqhkiG9w0BAQsFADBhMQswCQYDVQQG
EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSAw
HgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBHMjAeFw0xMzA4MDExMjAwMDBaFw0zODAxMTUx
MjAwMDBaMGExCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3
dy5kaWdpY2VydC5jb20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IEcyMIIBIjANBgkq
hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuzfNNNx7a8myaJCtSnX/RrohCgiN9RlUyfuI2/Ou8jqJ
kTx65qsGGmvPrC3oXgkkRLpimn7Wo6h+4FR1IAWsULecYxpsMNzaHxmx1x7e/dfgy5SDN67sH0NO
3Xss0r0upS/kqbitOtSZpLYl6ZtrAGCSYP9PIUkY92eQq2EGnI/yuum06ZIya7XzV+hdG82MHauV
BJVJ8zUtluNJbd134/tJS7SsVQepj5WztCO7TG1F8PapspUwtP1MVYwnSlcUfIKdzXOS0xZKBgyM
UNGPHgm+F6HmIcr9g+UQvIOlCsRnKPZzFBQ9RnbDhxSJITRNrw9FDKZJobq7nMWxM4MphQIDAQAB
o0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjAdBgNVHQ4EFgQUTiJUIBiV5uNu
5g/6+rkS7QYXjzkwDQYJKoZIhvcNAQELBQADggEBAGBnKJRvDkhj6zHd6mcY1Yl9PMWLSn/pvtsr
F9+wX3N3KjITOYFnQoQj8kVnNeyIv/iPsGEMNKSuIEyExtv4NeF22d+mQrvHRAiGfzZ0JFrabA0U
WTW98kndth/Jsw1HKj2ZL7tcu7XUIOGZX1NGFdtom/DzMNU+MeKNhJ7jitralj41E6Vf8PlwUHBH
QRFXGU7Aj64GxJUTFy8bJZ918rGOmaFvE7FBcf6IKshPECBV1/MUReXgRPTqh5Uykw7+U0b6LJ3/
iyK5S9kJRaTepLiaWN0bfVKfjllDiIGknibVb63dDcY3fe0Dkhvld1927jyNxF1WW6LZZm6zNTfl
MrY=
-----END CERTIFICATE-----

DigiCert Global Root G3
=======================
-----BEGIN CERTIFICATE-----
MIICPzCCAcWgAwIBAgIQBVVWvPJepDU1w6QP1atFcjAKBggqhkjOPQQDAzBhMQswCQYDVQQGEwJV
UzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSAwHgYD
VQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBHMzAeFw0xMzA4MDExMjAwMDBaFw0zODAxMTUxMjAw
MDBaMGExCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5k
aWdpY2VydC5jb20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IEczMHYwEAYHKoZIzj0C
AQYFK4EEACIDYgAE3afZu4q4C/sLfyHS8L6+c/MzXRq8NOrexpu80JX28MzQC7phW1FGfp4tn+6O
YwwX7Adw9c+ELkCDnOg/QW07rdOkFFk2eJ0DQ+4QE2xy3q6Ip6FrtUPOZ9wj/wMco+I+o0IwQDAP
BgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjAdBgNVHQ4EFgQUs9tIpPmhxdiuNkHMEWNp
Yim8S8YwCgYIKoZIzj0EAwMDaAAwZQIxAK288mw/EkrRLTnDCgmXc/SINoyIJ7vmiI1Qhadj+Z4y
3maTD/HMsQmP3Wyr+mt/oAIwOWZbwmSNuJ5Q3KjVSaLtx9zRSX8XAbjIho9OjIgrqJqpisXRAL34
VOKa5Vt8sycX
-----END CERTIFICATE-----

DigiCert Trusted Root G4
========================
-----BEGIN CERTIFICATE-----
MIIFkDCCA3igAwIBAgIQBZsbV56OITLiOQe9p3d1XDANBgkqhkiG9w0BAQwFADBiMQswCQYDVQQG
EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSEw
HwYDVQQDExhEaWdpQ2VydCBUcnVzdGVkIFJvb3QgRzQwHhcNMTMwODAxMTIwMDAwWhcNMzgwMTE1
MTIwMDAwWjBiMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
d3cuZGlnaWNlcnQuY29tMSEwHwYDVQQDExhEaWdpQ2VydCBUcnVzdGVkIFJvb3QgRzQwggIiMA0G
CSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC/5pBzaN675F1KPDAiMGkz7MKnJS7JIT3yithZwuEp
pz1Yq3aaza57G4QNxDAf8xukOBbrVsaXbR2rsnnyyhHS5F/WBTxSD1Ifxp4VpX6+n6lXFllVcq9o
k3DCsrp1mWpzMpTREEQQLt+C8weE5nQ7bXHiLQwb7iDVySAdYyktzuxeTsiT+CFhmzTrBcZe7Fsa
vOvJz82sNEBfsXpm7nfISKhmV1efVFiODCu3T6cw2Vbuyntd463JT17lNecxy9qTXtyOj4DatpGY
QJB5w3jHtrHEtWoYOAMQjdjUN6QuBX2I9YI+EJFwq1WCQTLX2wRzKm6RAXwhTNS8rhsDdV14Ztk6
MUSaM0C/CNdaSaTC5qmgZ92kJ7yhTzm1EVgX9yRcRo9k98FpiHaYdj1ZXUJ2h4mXaXpI8OCiEhtm
mnTK3kse5w5jrubU75KSOp493ADkRSWJtppEGSt+wJS00mFt6zPZxd9LBADMfRyVw4/3IbKyEbe7
f/LVjHAsQWCqsWMYRJUadmJ+9oCw++hkpjPRiQfhvbfmQ6QYuKZ3AeEPlAwhHbJUKSWJbOUOUlFH
dL4mrLZBdd56rF+NP8m800ERElvlEFDrMcXKchYiCd98THU/Y+whX8QgUWtvsauGi0/C1kVfnSD8
oR7FwI+isX4KJpn15GkvmB0t9dmpsh3lGwIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1Ud
DwEB/wQEAwIBhjAdBgNVHQ4EFgQU7NfjgtJxXWRM3y5nP+e6mK4cD08wDQYJKoZIhvcNAQEMBQAD
ggIBALth2X2pbL4XxJEbw6GiAI3jZGgPVs93rnD5/ZpKmbnJeFwMDF/k5hQpVgs2SV1EY+CtnJYY
ZhsjDT156W1r1lT40jzBQ0CuHVD1UvyQO7uYmWlrx8GnqGikJ9yd+SeuMIW59mdNOj6PWTkiU0Tr
yF0Dyu1Qen1iIQqAyHNm0aAFYF/opbSnr6j3bTWcfFqK1qI4mfN4i/RN0iAL3gTujJtHgXINwBQy
7zBZLq7gcfJW5GqXb5JQbZaNaHqasjYUegbyJLkJEVDXCLG4iXqEI2FCKeWjzaIgQdfRnGTZ6iah
ixTXTBmyUEFxPT9NcCOGDErcgdLMMpSEDQgJlxxPwO5rIHQw0uA5NBCFIRUBCOhVMt5xSdkoF1BN
5r5N0XWs0Mr7QbhDparTwwVETyw2m+L64kW4I1NsBm9nVX9GtUw/bihaeSbSpKhil9Ie4u1Ki7wb
/UdKDd9nZn6yW0HQO+T0O/QEY+nvwlQAUaCKKsnOeMzV6ocEGLPOr0mIr/OSmbaz5mEP0oUA51Aa
5BuVnRmhuZyxm7EAHu/QD09CbMkKvO5D+jpxpchNJqU1/YldvIViHTLSoCtU7ZpXwdv6EM8Zt4tK
G48BtieVU+i2iW1bvGjUI+iLUaJW+fCmgKDWHrO8Dw9TdSmq6hN35N6MgSGtBxBHEa2HPQfRdbzP
82Z+
-----END CERTIFICATE-----

COMODO RSA Certification Authority
==================================
-----BEGIN CERTIFICATE-----
MIIF2DCCA8CgAwIBAgIQTKr5yttjb+Af907YWwOGnTANBgkqhkiG9w0BAQwFADCBhTELMAkGA1UE
BhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgG
A1UEChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBSU0EgQ2VydGlmaWNhdGlv
biBBdXRob3JpdHkwHhcNMTAwMTE5MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBhTELMAkGA1UEBhMC
R0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UE
ChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBSU0EgQ2VydGlmaWNhdGlvbiBB
dXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCR6FSS0gpWsawNJN3Fz0Rn
dJkrN6N9I3AAcbxT38T6KhKPS38QVr2fcHK3YX/JSw8Xpz3jsARh7v8Rl8f0hj4K+j5c+ZPmNHrZ
FGvnnLOFoIJ6dq9xkNfs/Q36nGz637CC9BR++b7Epi9Pf5l/tfxnQ3K9DADWietrLNPtj5gcFKt+
5eNu/Nio5JIk2kNrYrhV/erBvGy2i/MOjZrkm2xpmfh4SDBF1a3hDTxFYPwyllEnvGfDyi62a+pG
x8cgoLEfZd5ICLqkTqnyg0Y3hOvozIFIQ2dOciqbXL1MGyiKXCJ7tKuY2e7gUYPDCUZObT6Z+pUX
2nwzV0E8jVHtC7ZcryxjGt9XyD+86V3Em69FmeKjWiS0uqlWPc9vqv9JWL7wqP/0uK3pN/u6uPQL
OvnoQ0IeidiEyxPx2bvhiWC4jChWrBQdnArncevPDt09qZahSL0896+1DSJMwBGB7FY79tOi4lu3
sgQiUpWAk2nojkxl8ZEDLXB0AuqLZxUpaVICu9ffUGpVRr+goyhhf3DQw6KqLCGqR84onAZFdr+C
GCe01a60y1Dma/RMhnEw6abfFobg2P9A3fvQQoh/ozM6LlweQRGBY84YcWsr7KaKtzFcOmpH4MN5
WdYgGq/yapiqcrxXStJLnbsQ/LBMQeXtHT1eKJ2czL+zUdqnR+WEUwIDAQABo0IwQDAdBgNVHQ4E
FgQUu69+Aj36pvE8hI6t7jiY7NkyMtQwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8w
DQYJKoZIhvcNAQEMBQADggIBAArx1UaEt65Ru2yyTUEUAJNMnMvlwFTPoCWOAvn9sKIN9SCYPBMt
rFaisNZ+EZLpLrqeLppysb0ZRGxhNaKatBYSaVqM4dc+pBroLwP0rmEdEBsqpIt6xf4FpuHA1sj+
nq6PK7o9mfjYcwlYRm6mnPTXJ9OV2jeDchzTc+CiR5kDOF3VSXkAKRzH7JsgHAckaVd4sjn8OoSg
tZx8jb8uk2IntznaFxiuvTwJaP+EmzzV1gsD41eeFPfR60/IvYcjt7ZJQ3mFXLrrkguhxuhoqEwW
sRqZCuhTLJK7oQkYdQxlqHvLI7cawiiFwxv/0Cti76R7CZGYZ4wUAc1oBmpjIXUDgIiKboHGhfKp
pC3n9KUkEEeDys30jXlYsQab5xoq2Z0B15R97QNKyvDb6KkBPvVWmckejkk9u+UJueBPSZI9FoJA
zMxZxuY67RIuaTxslbH9qh17f4a+Hg4yRvv7E491f0yLS0Zj/gA0QHDBw7mh3aZw4gSzQbzpgJHq
ZJx64SIDqZxubw5lT2yHh17zbqD5daWbQOhTsiedSrnAdyGN/4fy3ryM7xfft0kL0fJuMAsaDk52
7RH89elWsn2/x20Kk4yl0MC2Hb46TpSi125sC8KKfPog88Tk5c0NqMuRkrF8hey1FGlmDoLnzc7I
LaZRfyHBNVOFBkpdn627G190
-----END CERTIFICATE-----

USERTrust RSA Certification Authority
=====================================
-----BEGIN CERTIFICATE-----
MIIF3jCCA8agAwIBAgIQAf1tMPyjylGoG7xkDjUDLTANBgkqhkiG9w0BAQwFADCBiDELMAkGA1UE
BhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYDVQQK
ExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBSU0EgQ2VydGlmaWNh
dGlvbiBBdXRob3JpdHkwHhcNMTAwMjAxMDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBiDELMAkGA1UE
BhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYDVQQK
ExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBSU0EgQ2VydGlmaWNh
dGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCAEmUXNg7D2wiz
0KxXDXbtzSfTTK1Qg2HiqiBNCS1kCdzOiZ/MPans9s/B3PHTsdZ7NygRK0faOca8Ohm0X6a9fZ2j
Y0K2dvKpOyuR+OJv0OwWIJAJPuLodMkYtJHUYmTbf6MG8YgYapAiPLz+E/CHFHv25B+O1ORRxhFn
RghRy4YUVD+8M/5+bJz/Fp0YvVGONaanZshyZ9shZrHUm3gDwFA66Mzw3LyeTP6vBZY1H1dat//O
+T23LLb2VN3I5xI6Ta5MirdcmrS3ID3KfyI0rn47aGYBROcBTkZTmzNg95S+UzeQc0PzMsNT79uq
/nROacdrjGCT3sTHDN/hMq7MkztReJVni+49Vv4M0GkPGw/zJSZrM233bkf6c0Plfg6lZrEpfDKE
Y1WJxA3Bk1QwGROs0303p+tdOmw1XNtB1xLaqUkL39iAigmTYo61Zs8liM2EuLE/pDkP2QKe6xJM
lXzzawWpXhaDzLhn4ugTncxbgtNMs+1b/97lc6wjOy0AvzVVdAlJ2ElYGn+SNuZRkg7zJn0cTRe8
yexDJtC/QV9AqURE9JnnV4eeUB9XVKg+/XRjL7FQZQnmWEIuQxpMtPAlR1n6BB6T1CZGSlCBst6+
eLf8ZxXhyVeEHg9j1uliutZfVS7qXMYoCAQlObgOK6nyTJccBz8NUvXt7y+CDwIDAQABo0IwQDAd
BgNVHQ4EFgQUU3m/WqorSs9UgOHYm8Cd8rIDZsswDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQF
MAMBAf8wDQYJKoZIhvcNAQEMBQADggIBAFzUfA3P9wF9QZllDHPFUp/L+M+ZBn8b2kMVn54CVVeW
FPFSPCeHlCjtHzoBN6J2/FNQwISbxmtOuowhT6KOVWKR82kV2LyI48SqC/3vqOlLVSoGIG1VeCkZ
7l8wXEskEVX/JJpuXior7gtNn3/3ATiUFJVDBwn7YKnuHKsSjKCaXqeYalltiz8I+8jRRa8YFWSQ
Eg9zKC7F4iRO/Fjs8PRF/iKz6y+O0tlFYQXBl2+odnKPi4w2r78NBc5xjeambx9spnFixdjQg3IM
8WcRiQycE0xyNN+81XHfqnHd4blsjDwSXWXavVcStkNr/+XeTWYRUc+ZruwXtuhxkYzeSf7dNXGi
FSeUHM9h4ya7b6NnJSFd5t0dCy5oGzuCr+yDZ4XUmFF0sbmZgIn/f3gZXHlKYC6SQK5MNyosycdi
yA5d9zZbyuAlJQG03RoHnHcAP9Dc1ew91Pq7P8yF1m9/qS3fuQL39ZeatTXaw2ewh0qpKJ4jjv9c
J2vhsE/zB+4ALtRZh8tSQZXq9EfX7mRBVXyNWQKV3WKdwrnuWih0hKWbt5DHDAff9Yk2dDLWKMGw
sAvgnEzDHNb842m1R0aBL6KCq9NjRHDEjf8tM7qtj3u1cIiuPhnPQCjY/MiQu12ZIvVS5ljFH4gx
Q+6IHdfGjjxDah2nGN59PRbxYvnKkKj9
-----END CERTIFICATE-----

USERTrust ECC Certification Authority
=====================================
-----BEGIN CERTIFICATE-----
MIICjzCCAhWgAwIBAgIQXIuZxVqUxdJxVt7NiYDMJjAKBggqhkjOPQQDAzCBiDELMAkGA1UEBhMC
VVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYDVQQKExVU
aGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBFQ0MgQ2VydGlmaWNhdGlv
biBBdXRob3JpdHkwHhcNMTAwMjAxMDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBiDELMAkGA1UEBhMC
VVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYDVQQKExVU
aGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBFQ0MgQ2VydGlmaWNhdGlv
biBBdXRob3JpdHkwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQarFRaqfloI+d61SRvU8Za2EurxtW2
0eZzca7dnNYMYf3boIkDuAUU7FfO7l0/4iGzzvfUinngo4N+LZfQYcTxmdwlkWOrfzCjtHDix6Ez
nPO/LlxTsV+zfTJ/ijTjeXmjQjBAMB0GA1UdDgQWBBQ64QmG1M8ZwpZ2dEl23OA1xmNjmjAOBgNV
HQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAKBggqhkjOPQQDAwNoADBlAjA2Z6EWCNzklwBB
HU6+4WMBzzuqQhFkoJ2UOQIReVx7Hfpkue4WQrO/isIJxOzksU0CMQDpKmFHjFJKS04YcPbWRNZu
9YO6bVi9JNlWSOrvxKJGgYhqOkbRqZtNyWHa0V1Xahg=
-----END CERTIFICATE-----

GlobalSign ECC Root CA - R5
===========================
-----BEGIN CERTIFICATE-----
MIICHjCCAaSgAwIBAgIRYFlJ4CYuu1X5CneKcflK2GwwCgYIKoZIzj0EAwMwUDEkMCIGA1UECxMb
R2xvYmFsU2lnbiBFQ0MgUm9vdCBDQSAtIFI1MRMwEQYDVQQKEwpHbG9iYWxTaWduMRMwEQYDVQQD
EwpHbG9iYWxTaWduMB4XDTEyMTExMzAwMDAwMFoXDTM4MDExOTAzMTQwN1owUDEkMCIGA1UECxMb
R2xvYmFsU2lnbiBFQ0MgUm9vdCBDQSAtIFI1MRMwEQYDVQQKEwpHbG9iYWxTaWduMRMwEQYDVQQD
EwpHbG9iYWxTaWduMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAER0UOlvt9Xb/pOdEh+J8LttV7HpI6
SFkc8GIxLcB6KP4ap1yztsyX50XUWPrRd21DosCHZTQKH3rd6zwzocWdTaRvQZU4f8kehOvRnkmS
h5SHDDqFSmafnVmTTZdhBoZKo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAd
BgNVHQ4EFgQUPeYpSJvqB8ohREom3m7e0oPQn1kwCgYIKoZIzj0EAwMDaAAwZQIxAOVpEslu28Yx
uglB4Zf4+/2a4n0Sye18ZNPLBSWLVtmg515dTguDnFt2KaAJJiFqYgIwcdK1j1zqO+F4CYWodZI7
yFz9SO8NdCKoCOJuxUnOxwy8p2Fp8fc74SrL+SvzZpA3
-----END CERTIFICATE-----

Staat der Nederlanden EV Root CA
================================
-----BEGIN CERTIFICATE-----
MIIFcDCCA1igAwIBAgIEAJiWjTANBgkqhkiG9w0BAQsFADBYMQswCQYDVQQGEwJOTDEeMBwGA1UE
CgwVU3RhYXQgZGVyIE5lZGVybGFuZGVuMSkwJwYDVQQDDCBTdGFhdCBkZXIgTmVkZXJsYW5kZW4g
RVYgUm9vdCBDQTAeFw0xMDEyMDgxMTE5MjlaFw0yMjEyMDgxMTEwMjhaMFgxCzAJBgNVBAYTAk5M
MR4wHAYDVQQKDBVTdGFhdCBkZXIgTmVkZXJsYW5kZW4xKTAnBgNVBAMMIFN0YWF0IGRlciBOZWRl
cmxhbmRlbiBFViBSb290IENBMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA48d+ifkk
SzrSM4M1LGns3Amk41GoJSt5uAg94JG6hIXGhaTK5skuU6TJJB79VWZxXSzFYGgEt9nCUiY4iKTW
O0Cmws0/zZiTs1QUWJZV1VD+hq2kY39ch/aO5ieSZxeSAgMs3NZmdO3dZ//BYY1jTw+bbRcwJu+r
0h8QoPnFfxZpgQNH7R5ojXKhTbImxrpsX23Wr9GxE46prfNeaXUmGD5BKyF/7otdBwadQ8QpCiv8
Kj6GyzyDOvnJDdrFmeK8eEEzduG/L13lpJhQDBXd4Pqcfzho0LKmeqfRMb1+ilgnQ7O6M5HTp5gV
XJrm0w912fxBmJc+qiXbj5IusHsMX/FjqTf5m3VpTCgmJdrV8hJwRVXj33NeN/UhbJCONVrJ0yPr
08C+eKxCKFhmpUZtcALXEPlLVPxdhkqHz3/KRawRWrUgUY0viEeXOcDPusBCAUCZSCELa6fS/ZbV
0b5GnUngC6agIk440ME8MLxwjyx1zNDFjFE7PZQIZCZhfbnDZY8UnCHQqv0XcgOPvZuM5l5Tnrmd
74K74bzickFbIZTTRTeU0d8JOV3nI6qaHcptqAqGhYqCvkIH1vI4gnPah1vlPNOePqc7nvQDs/nx
fRN0Av+7oeX6AHkcpmZBiFxgV6YuCcS6/ZrPpx9Aw7vMWgpVSzs4dlG4Y4uElBbmVvMCAwEAAaNC
MEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFP6rAJCYniT8qcwa
ivsnuL8wbqg7MA0GCSqGSIb3DQEBCwUAA4ICAQDPdyxuVr5Os7aEAJSrR8kN0nbHhp8dB9O2tLsI
eK9p0gtJ3jPFrK3CiAJ9Brc1AsFgyb/E6JTe1NOpEyVa/m6irn0F3H3zbPB+po3u2dfOWBfoqSmu
c0iH55vKbimhZF8ZE/euBhD/UcabTVUlT5OZEAFTdfETzsemQUHSv4ilf0X8rLiltTMMgsT7B/Zq
5SWEXwbKwYY5EdtYzXc7LMJMD16a4/CrPmEbUCTCwPTxGfARKbalGAKb12NMcIxHowNDXLldRqAN
b/9Zjr7dn3LDWyvfjFvO5QxGbJKyCqNMVEIYFRIYvdr8unRu/8G2oGTYqV9Vrp9canaW2HNnh/tN
f1zuacpzEPuKqf2evTY4SUmH9A4U8OmHuD+nT3pajnnUk+S7aFKErGzp85hwVXIy+TSrK0m1zSBi
5Dp6Z2Orltxtrpfs/J92VoguZs9btsmksNcFuuEnL5O7Jiqik7Ab846+HUCjuTaPPoIaGl6I6lD4
WeKDRikL40Rc4ZW2aZCaFG+XroHPaO+Zmr615+F/+PoTRxZMzG0IQOeLeG9QgkRQP2YGiqtDhFZK
DyAthg710tvSeopLzaXoTvFeJiUBWSOgftL2fiFX1ye8FVdMpEbB4IMeDExNH08GGeL5qPQ6gqGy
eUN51q1veieQA6TqJIc/2b3Z6fJfUEkc7uzXLg==
-----END CERTIFICATE-----

IdenTrust Commercial Root CA 1
==============================
-----BEGIN CERTIFICATE-----
MIIFYDCCA0igAwIBAgIQCgFCgAAAAUUjyES1AAAAAjANBgkqhkiG9w0BAQsFADBKMQswCQYDVQQG
EwJVUzESMBAGA1UEChMJSWRlblRydXN0MScwJQYDVQQDEx5JZGVuVHJ1c3QgQ29tbWVyY2lhbCBS
b290IENBIDEwHhcNMTQwMTE2MTgxMjIzWhcNMzQwMTE2MTgxMjIzWjBKMQswCQYDVQQGEwJVUzES
MBAGA1UEChMJSWRlblRydXN0MScwJQYDVQQDEx5JZGVuVHJ1c3QgQ29tbWVyY2lhbCBSb290IENB
IDEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCnUBneP5k91DNG8W9RYYKyqU+PZ4ld
hNlT3Qwo2dfw/66VQ3KZ+bVdfIrBQuExUHTRgQ18zZshq0PirK1ehm7zCYofWjK9ouuU+ehcCuz/
mNKvcbO0U59Oh++SvL3sTzIwiEsXXlfEU8L2ApeN2WIrvyQfYo3fw7gpS0l4PJNgiCL8mdo2yMKi
1CxUAGc1bnO/AljwpN3lsKImesrgNqUZFvX9t++uP0D1bVoE/c40yiTcdCMbXTMTEl3EASX2MN0C
XZ/g1Ue9tOsbobtJSdifWwLziuQkkORiT0/Br4sOdBeo0XKIanoBScy0RnnGF7HamB4HWfp1IYVl
3ZBWzvurpWCdxJ35UrCLvYf5jysjCiN2O/cz4ckA82n5S6LgTrx+kzmEB/dEcH7+B1rlsazRGMzy
NeVJSQjKVsk9+w8YfYs7wRPCTY/JTw436R+hDmrfYi7LNQZReSzIJTj0+kuniVyc0uMNOYZKdHzV
WYfCP04MXFL0PfdSgvHqo6z9STQaKPNBiDoT7uje/5kdX7rL6B7yuVBgwDHTc+XvvqDtMwt0viAg
xGds8AgDelWAf0ZOlqf0Hj7h9tgJ4TNkK2PXMl6f+cB7D3hvl7yTmvmcEpB4eoCHFddydJxVdHix
uuFucAS6T6C6aMN7/zHwcz09lCqxC0EOoP5NiGVreTO01wIDAQABo0IwQDAOBgNVHQ8BAf8EBAMC
AQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU7UQZwNPwBovupHu+QucmVMiONnYwDQYJKoZI
hvcNAQELBQADggIBAA2ukDL2pkt8RHYZYR4nKM1eVO8lvOMIkPkp165oCOGUAFjvLi5+U1KMtlwH
6oi6mYtQlNeCgN9hCQCTrQ0U5s7B8jeUeLBfnLOic7iPBZM4zY0+sLj7wM+x8uwtLRvM7Kqas6pg
ghstO8OEPVeKlh6cdbjTMM1gCIOQ045U8U1mwF10A0Cj7oV+wh93nAbowacYXVKV7cndJZ5t+qnt
ozo00Fl72u1Q8zW/7esUTTHHYPTa8Yec4kjixsU3+wYQ+nVZZjFHKdp2mhzpgq7vmrlR94gjmmmV
YjzlVYA211QC//G5Xc7UI2/YRYRKW2XviQzdFKcgyxilJbQN+QHwotL0AMh0jqEqSI5l2xPE4iUX
feu+h1sXIFRRk0pTAwvsXcoz7WL9RccvW9xYoIA55vrX/hMUpu09lEpCdNTDd1lzzY9GvlU47/ro
kTLql1gEIt44w8y8bckzOmoKaT+gyOpyj4xjhiO9bTyWnpXgSUyqorkqG5w2gXjtw+hG4iZZRHUe
2XWJUc0QhJ1hYMtd+ZciTY6Y5uN/9lu7rs3KSoFrXgvzUeF0K+l+J6fZmUlO+KWA2yUPHGNiiskz
Z2s8EIPGrd6ozRaOjfAHN3Gf8qv8QfXBi+wAN10J5U6A7/qxXDgGpRtK4dw4LTzcqx+QGtVKnO7R
cGzM7vRX+Bi6hG6H
-----END CERTIFICATE-----

IdenTrust Public Sector Root CA 1
=================================
-----BEGIN CERTIFICATE-----
MIIFZjCCA06gAwIBAgIQCgFCgAAAAUUjz0Z8AAAAAjANBgkqhkiG9w0BAQsFADBNMQswCQYDVQQG
EwJVUzESMBAGA1UEChMJSWRlblRydXN0MSowKAYDVQQDEyFJZGVuVHJ1c3QgUHVibGljIFNlY3Rv
ciBSb290IENBIDEwHhcNMTQwMTE2MTc1MzMyWhcNMzQwMTE2MTc1MzMyWjBNMQswCQYDVQQGEwJV
UzESMBAGA1UEChMJSWRlblRydXN0MSowKAYDVQQDEyFJZGVuVHJ1c3QgUHVibGljIFNlY3RvciBS
b290IENBIDEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC2IpT8pEiv6EdrCvsnduTy
P4o7ekosMSqMjbCpwzFrqHd2hCa2rIFCDQjrVVi7evi8ZX3yoG2LqEfpYnYeEe4IFNGyRBb06tD6
Hi9e28tzQa68ALBKK0CyrOE7S8ItneShm+waOh7wCLPQ5CQ1B5+ctMlSbdsHyo+1W/CD80/HLaXI
rcuVIKQxKFdYWuSNG5qrng0M8gozOSI5Cpcu81N3uURF/YTLNiCBWS2ab21ISGHKTN9T0a9SvESf
qy9rg3LvdYDaBjMbXcjaY8ZNzaxmMc3R3j6HEDbhuaR672BQssvKplbgN6+rNBM5Jeg5ZuSYeqoS
mJxZZoY+rfGwyj4GD3vwEUs3oERte8uojHH01bWRNszwFcYr3lEXsZdMUD2xlVl8BX0tIdUAvwFn
ol57plzy9yLxkA2T26pEUWbMfXYD62qoKjgZl3YNa4ph+bz27nb9cCvdKTz4Ch5bQhyLVi9VGxyh
LrXHFub4qjySjmm2AcG1hp2JDws4lFTo6tyePSW8Uybt1as5qsVATFSrsrTZ2fjXctscvG29ZV/v
iDUqZi/u9rNl8DONfJhBaUYPQxxp+pu10GFqzcpL2UyQRqsVWaFHVCkugyhfHMKiq3IXAAaOReyL
4jM9f9oZRORicsPfIsbyVtTdX5Vy7W1f90gDW/3FKqD2cyOEEBsB5wIDAQABo0IwQDAOBgNVHQ8B
Af8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU43HgntinQtnbcZFrlJPrw6PRFKMw
DQYJKoZIhvcNAQELBQADggIBAEf63QqwEZE4rU1d9+UOl1QZgkiHVIyqZJnYWv6IAcVYpZmxI1Qj
t2odIFflAWJBF9MJ23XLblSQdf4an4EKwt3X9wnQW3IV5B4Jaj0z8yGa5hV+rVHVDRDtfULAj+7A
mgjVQdZcDiFpboBhDhXAuM/FSRJSzL46zNQuOAXeNf0fb7iAaJg9TaDKQGXSc3z1i9kKlT/YPyNt
GtEqJBnZhbMX73huqVjRI9PHE+1yJX9dsXNw0H8GlwmEKYBhHfpe/3OsoOOJuBxxFcbeMX8S3OFt
m6/n6J91eEyrRjuazr8FGF1NFTwWmhlQBJqymm9li1JfPFgEKCXAZmExfrngdbkaqIHWchezxQMx
NRF4eKLg6TCMf4DfWN88uieW4oA0beOY02QnrEh+KHdcxiVhJfiFDGX6xDIvpZgF5PgLZxYWxoK4
Mhn5+bl53B/N66+rDt0b20XkeucC4pVd/GnwU2lhlXV5C15V5jgclKlZM57IcXR5f1GJtshquDDI
ajjDbp7hNxbqBWJMWxJH7ae0s1hWx0nzfxJoCTFx8G34Tkf71oXuxVhAGaQdp/lLQzfcaFpPz+vC
ZHTetBXZ9FRUGi8c15dxVJCO2SCdUyt/q4/i6jC8UDfv8Ue1fXwsBOxonbRJRBD0ckscZOf85muQ
3Wl9af0AVqW3rLatt8o+Ae+c
-----END CERTIFICATE-----

Entrust Root Certification Authority - G2
=========================================
-----BEGIN CERTIFICATE-----
MIIEPjCCAyagAwIBAgIESlOMKDANBgkqhkiG9w0BAQsFADCBvjELMAkGA1UEBhMCVVMxFjAUBgNV
BAoTDUVudHJ1c3QsIEluYy4xKDAmBgNVBAsTH1NlZSB3d3cuZW50cnVzdC5uZXQvbGVnYWwtdGVy
bXMxOTA3BgNVBAsTMChjKSAyMDA5IEVudHJ1c3QsIEluYy4gLSBmb3IgYXV0aG9yaXplZCB1c2Ug
b25seTEyMDAGA1UEAxMpRW50cnVzdCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRzIw
HhcNMDkwNzA3MTcyNTU0WhcNMzAxMjA3MTc1NTU0WjCBvjELMAkGA1UEBhMCVVMxFjAUBgNVBAoT
DUVudHJ1c3QsIEluYy4xKDAmBgNVBAsTH1NlZSB3d3cuZW50cnVzdC5uZXQvbGVnYWwtdGVybXMx
OTA3BgNVBAsTMChjKSAyMDA5IEVudHJ1c3QsIEluYy4gLSBmb3IgYXV0aG9yaXplZCB1c2Ugb25s
eTEyMDAGA1UEAxMpRW50cnVzdCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRzIwggEi
MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC6hLZy254Ma+KZ6TABp3bqMriVQRrJ2mFOWHLP
/vaCeb9zYQYKpSfYs1/TRU4cctZOMvJyig/3gxnQaoCAAEUesMfnmr8SVycco2gvCoe9amsOXmXz
HHfV1IWNcCG0szLni6LVhjkCsbjSR87kyUnEO6fe+1R9V77w6G7CebI6C1XiUJgWMhNcL3hWwcKU
s/Ja5CeanyTXxuzQmyWC48zCxEXFjJd6BmsqEZ+pCm5IO2/b1BEZQvePB7/1U1+cPvQXLOZprE4y
TGJ36rfo5bs0vBmLrpxR57d+tVOxMyLlbc9wPBr64ptntoP0jaWvYkxN4FisZDQSA/i2jZRjJKRx
AgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRqciZ6
0B7vfec7aVHUbI2fkBJmqzANBgkqhkiG9w0BAQsFAAOCAQEAeZ8dlsa2eT8ijYfThwMEYGprmi5Z
iXMRrEPR9RP/jTkrwPK9T3CMqS/qF8QLVJ7UG5aYMzyorWKiAHarWWluBh1+xLlEjZivEtRh2woZ
Rkfz6/djwUAFQKXSt/S1mja/qYh2iARVBCuch38aNzx+LaUa2NSJXsq9rD1s2G2v1fN2D807iDgi
nWyTmsQ9v4IbZT+mD12q/OWyFcq1rca8PdCE6OoGcrBNOTJ4vz4RnAuknZoh8/CbCzB428Hch0P+
vGOaysXCHMnHjf87ElgI5rY97HosTvuDls4MPGmHVHOkc8KT/1EQrBVUAdj8BbGJoX90g5pJ19xO
e4pIb4tF9g==
-----END CERTIFICATE-----

Entrust Root Certification Authority - EC1
==========================================
-----BEGIN CERTIFICATE-----
MIIC+TCCAoCgAwIBAgINAKaLeSkAAAAAUNCR+TAKBggqhkjOPQQDAzCBvzELMAkGA1UEBhMCVVMx
FjAUBgNVBAoTDUVudHJ1c3QsIEluYy4xKDAmBgNVBAsTH1NlZSB3d3cuZW50cnVzdC5uZXQvbGVn
YWwtdGVybXMxOTA3BgNVBAsTMChjKSAyMDEyIEVudHJ1c3QsIEluYy4gLSBmb3IgYXV0aG9yaXpl
ZCB1c2Ugb25seTEzMDEGA1UEAxMqRW50cnVzdCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5
IC0gRUMxMB4XDTEyMTIxODE1MjUzNloXDTM3MTIxODE1NTUzNlowgb8xCzAJBgNVBAYTAlVTMRYw
FAYDVQQKEw1FbnRydXN0LCBJbmMuMSgwJgYDVQQLEx9TZWUgd3d3LmVudHJ1c3QubmV0L2xlZ2Fs
LXRlcm1zMTkwNwYDVQQLEzAoYykgMjAxMiBFbnRydXN0LCBJbmMuIC0gZm9yIGF1dGhvcml6ZWQg
dXNlIG9ubHkxMzAxBgNVBAMTKkVudHJ1c3QgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAt
IEVDMTB2MBAGByqGSM49AgEGBSuBBAAiA2IABIQTydC6bUF74mzQ61VfZgIaJPRbiWlH47jCffHy
AsWfoPZb1YsGGYZPUxBtByQnoaD41UcZYUx9ypMn6nQM72+WCf5j7HBdNq1nd67JnXxVRDqiY1Ef
9eNi1KlHBz7MIKNCMEAwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYE
FLdj5xrdjekIplWDpOBqUEFlEUJJMAoGCCqGSM49BAMDA2cAMGQCMGF52OVCR98crlOZF7ZvHH3h
vxGU0QOIdeSNiaSKd0bebWHvAvX7td/M/k7//qnmpwIwW5nXhTcGtXsI/esni0qU+eH6p44mCOh8
kmhtc9hvJqwhAriZtyZBWyVgrtBIGu4G
-----END CERTIFICATE-----

CFCA EV ROOT
============
-----BEGIN CERTIFICATE-----
MIIFjTCCA3WgAwIBAgIEGErM1jANBgkqhkiG9w0BAQsFADBWMQswCQYDVQQGEwJDTjEwMC4GA1UE
CgwnQ2hpbmEgRmluYW5jaWFsIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MRUwEwYDVQQDDAxDRkNB
IEVWIFJPT1QwHhcNMTIwODA4MDMwNzAxWhcNMjkxMjMxMDMwNzAxWjBWMQswCQYDVQQGEwJDTjEw
MC4GA1UECgwnQ2hpbmEgRmluYW5jaWFsIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MRUwEwYDVQQD
DAxDRkNBIEVWIFJPT1QwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDXXWvNED8fBVnV
BU03sQ7smCuOFR36k0sXgiFxEFLXUWRwFsJVaU2OFW2fvwwbwuCjZ9YMrM8irq93VCpLTIpTUnrD
7i7es3ElweldPe6hL6P3KjzJIx1qqx2hp/Hz7KDVRM8Vz3IvHWOX6Jn5/ZOkVIBMUtRSqy5J35DN
uF++P96hyk0g1CXohClTt7GIH//62pCfCqktQT+x8Rgp7hZZLDRJGqgG16iI0gNyejLi6mhNbiyW
ZXvKWfry4t3uMCz7zEasxGPrb382KzRzEpR/38wmnvFyXVBlWY9ps4deMm/DGIq1lY+wejfeWkU7
xzbh72fROdOXW3NiGUgthxwG+3SYIElz8AXSG7Ggo7cbcNOIabla1jj0Ytwli3i/+Oh+uFzJlU9f
py25IGvPa931DfSCt/SyZi4QKPaXWnuWFo8BGS1sbn85WAZkgwGDg8NNkt0yxoekN+kWzqotaK8K
gWU6cMGbrU1tVMoqLUuFG7OA5nBFDWteNfB/O7ic5ARwiRIlk9oKmSJgamNgTnYGmE69g60dWIol
hdLHZR4tjsbftsbhf4oEIRUpdPA+nJCdDC7xij5aqgwJHsfVPKPtl8MeNPo4+QgO48BdK4PRVmrJ
tqhUUy54Mmc9gn900PvhtgVguXDbjgv5E1hvcWAQUhC5wUEJ73IfZzF4/5YFjQIDAQABo2MwYTAf
BgNVHSMEGDAWgBTj/i39KNALtbq2osS/BqoFjJP7LzAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB
/wQEAwIBBjAdBgNVHQ4EFgQU4/4t/SjQC7W6tqLEvwaqBYyT+y8wDQYJKoZIhvcNAQELBQADggIB
ACXGumvrh8vegjmWPfBEp2uEcwPenStPuiB/vHiyz5ewG5zz13ku9Ui20vsXiObTej/tUxPQ4i9q
ecsAIyjmHjdXNYmEwnZPNDatZ8POQQaIxffu2Bq41gt/UP+TqhdLjOztUmCypAbqTuv0axn96/Ua
4CUqmtzHQTb3yHQFhDmVOdYLO6Qn+gjYXB74BGBSESgoA//vU2YApUo0FmZ8/Qmkrp5nGm9BC2sG
E5uPhnEFtC+NiWYzKXZUmhH4J/qyP5Hgzg0b8zAarb8iXRvTvyUFTeGSGn+ZnzxEk8rUQElsgIfX
BDrDMlI1Dlb4pd19xIsNER9Tyx6yF7Zod1rg1MvIB671Oi6ON7fQAUtDKXeMOZePglr4UeWJoBjn
aH9dCi77o0cOPaYjesYBx4/IXr9tgFa+iiS6M+qf4TIRnvHST4D2G0CvOJ4RUHlzEhLN5mydLIhy
PDCBBpEi6lmt2hkuIsKNuYyH4Ga8cyNfIWRjgEj1oDwYPZTISEEdQLpe/v5WOaHIz16eGWRGENoX
kbcFgKyLmZJ956LYBws2J+dIeWCKw9cTXPhyQN9Ky8+ZAAoACxGV2lZFA4gKn2fQ1XmxqI1AbQ3C
ekD6819kR5LLU7m7Wc5P/dAVUwHY3+vZ5nbv0CO7O6l5s9UCKc2Jo5YPSjXnTkLAdc0Hz+Ys63su
-----END CERTIFICATE-----

OISTE WISeKey Global Root GB CA
===============================
-----BEGIN CERTIFICATE-----
MIIDtTCCAp2gAwIBAgIQdrEgUnTwhYdGs/gjGvbCwDANBgkqhkiG9w0BAQsFADBtMQswCQYDVQQG
EwJDSDEQMA4GA1UEChMHV0lTZUtleTEiMCAGA1UECxMZT0lTVEUgRm91bmRhdGlvbiBFbmRvcnNl
ZDEoMCYGA1UEAxMfT0lTVEUgV0lTZUtleSBHbG9iYWwgUm9vdCBHQiBDQTAeFw0xNDEyMDExNTAw
MzJaFw0zOTEyMDExNTEwMzFaMG0xCzAJBgNVBAYTAkNIMRAwDgYDVQQKEwdXSVNlS2V5MSIwIAYD
VQQLExlPSVNURSBGb3VuZGF0aW9uIEVuZG9yc2VkMSgwJgYDVQQDEx9PSVNURSBXSVNlS2V5IEds
b2JhbCBSb290IEdCIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2Be3HEokKtaX
scriHvt9OO+Y9bI5mE4nuBFde9IllIiCFSZqGzG7qFshISvYD06fWvGxWuR51jIjK+FTzJlFXHtP
rby/h0oLS5daqPZI7H17Dc0hBt+eFf1Biki3IPShehtX1F1Q/7pn2COZH8g/497/b1t3sWtuuMlk
9+HKQUYOKXHQuSP8yYFfTvdv37+ErXNku7dCjmn21HYdfp2nuFeKUWdy19SouJVUQHMD9ur06/4o
Qnc/nSMbsrY9gBQHTC5P99UKFg29ZkM3fiNDecNAhvVMKdqOmq0NpQSHiB6F4+lT1ZvIiwNjeOvg
GUpuuy9rM2RYk61pv48b74JIxwIDAQABo1EwTzALBgNVHQ8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB
/zAdBgNVHQ4EFgQUNQ/INmNe4qPs+TtmFc5RUuORmj0wEAYJKwYBBAGCNxUBBAMCAQAwDQYJKoZI
hvcNAQELBQADggEBAEBM+4eymYGQfp3FsLAmzYh7KzKNbrghcViXfa43FK8+5/ea4n32cZiZBKpD
dHij40lhPnOMTZTg+XHEthYOU3gf1qKHLwI5gSk8rxWYITD+KJAAjNHhy/peyP34EEY7onhCkRd0
VQreUGdNZtGn//3ZwLWoo4rOZvUPQ82nK1d7Y0Zqqi5S2PTt4W2tKZB4SLrhI6qjiey1q5bAtEui
HZeeevJuQHHfaPFlTc58Bd9TZaml8LGXBHAVRgOY1NK/VLSgWH1Sb9pWJmLU2NuJMW8c8CLC02Ic
Nc1MaRVUGpCY3useX8p3x8uOPUNpnJpY0CQ73xtAln41rYHHTnG6iBM=
-----END CERTIFICATE-----

SZAFIR ROOT CA2
===============
-----BEGIN CERTIFICATE-----
MIIDcjCCAlqgAwIBAgIUPopdB+xV0jLVt+O2XwHrLdzk1uQwDQYJKoZIhvcNAQELBQAwUTELMAkG
A1UEBhMCUEwxKDAmBgNVBAoMH0tyYWpvd2EgSXpiYSBSb3psaWN6ZW5pb3dhIFMuQS4xGDAWBgNV
BAMMD1NaQUZJUiBST09UIENBMjAeFw0xNTEwMTkwNzQzMzBaFw0zNTEwMTkwNzQzMzBaMFExCzAJ
BgNVBAYTAlBMMSgwJgYDVQQKDB9LcmFqb3dhIEl6YmEgUm96bGljemVuaW93YSBTLkEuMRgwFgYD
VQQDDA9TWkFGSVIgUk9PVCBDQTIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC3vD5Q
qEvNQLXOYeeWyrSh2gwisPq1e3YAd4wLz32ohswmUeQgPYUM1ljj5/QqGJ3a0a4m7utT3PSQ1hNK
DJA8w/Ta0o4NkjrcsbH/ON7Dui1fgLkCvUqdGw+0w8LBZwPd3BucPbOw3gAeqDRHu5rr/gsUvTaE
2g0gv/pby6kWIK05YO4vdbbnl5z5Pv1+TW9NL++IDWr63fE9biCloBK0TXC5ztdyO4mTp4CEHCdJ
ckm1/zuVnsHMyAHs6A6KCpbns6aH5db5BSsNl0BwPLqsdVqc1U2dAgrSS5tmS0YHF2Wtn2yIANwi
ieDhZNRnvDF5YTy7ykHNXGoAyDw4jlivAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0P
AQH/BAQDAgEGMB0GA1UdDgQWBBQuFqlKGLXLzPVvUPMjX/hd56zwyDANBgkqhkiG9w0BAQsFAAOC
AQEAtXP4A9xZWx126aMqe5Aosk3AM0+qmrHUuOQn/6mWmc5G4G18TKI4pAZw8PRBEew/R40/cof5
O/2kbytTAOD/OblqBw7rHRz2onKQy4I9EYKL0rufKq8h5mOGnXkZ7/e7DDWQw4rtTw/1zBLZpD67
oPwglV9PJi8RI4NOdQcPv5vRtB3pEAT+ymCPoky4rc/hkA/NrgrHXXu3UNLUYfrVFdvXn4dRVOul
4+vJhaAlIDf7js4MNIThPIGyd05DpYhfhmehPea0XGG2Ptv+tyjFogeutcrKjSoS75ftwjCkySp6
+/NNIxuZMzSgLvWpCz/UXeHPhJ/iGcJfitYgHuNztw==
-----END CERTIFICATE-----

Certum Trusted Network CA 2
===========================
-----BEGIN CERTIFICATE-----
MIIF0jCCA7qgAwIBAgIQIdbQSk8lD8kyN/yqXhKN6TANBgkqhkiG9w0BAQ0FADCBgDELMAkGA1UE
BhMCUEwxIjAgBgNVBAoTGVVuaXpldG8gVGVjaG5vbG9naWVzIFMuQS4xJzAlBgNVBAsTHkNlcnR1
bSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTEkMCIGA1UEAxMbQ2VydHVtIFRydXN0ZWQgTmV0d29y
ayBDQSAyMCIYDzIwMTExMDA2MDgzOTU2WhgPMjA0NjEwMDYwODM5NTZaMIGAMQswCQYDVQQGEwJQ
TDEiMCAGA1UEChMZVW5pemV0byBUZWNobm9sb2dpZXMgUy5BLjEnMCUGA1UECxMeQ2VydHVtIENl
cnRpZmljYXRpb24gQXV0aG9yaXR5MSQwIgYDVQQDExtDZXJ0dW0gVHJ1c3RlZCBOZXR3b3JrIENB
IDIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC9+Xj45tWADGSdhhuWZGc/IjoedQF9
7/tcZ4zJzFxrqZHmuULlIEub2pt7uZld2ZuAS9eEQCsn0+i6MLs+CRqnSZXvK0AkwpfHp+6bJe+o
CgCXhVqqndwpyeI1B+twTUrWwbNWuKFBOJvR+zF/j+Bf4bE/D44WSWDXBo0Y+aomEKsq09DRZ40b
Rr5HMNUuctHFY9rnY3lEfktjJImGLjQ/KUxSiyqnwOKRKIm5wFv5HdnnJ63/mgKXwcZQkpsCLL2p
uTRZCr+ESv/f/rOf69me4Jgj7KZrdxYq28ytOxykh9xGc14ZYmhFV+SQgkK7QtbwYeDBoz1mo130
GO6IyY0XRSmZMnUCMe4pJshrAua1YkV/NxVaI2iJ1D7eTiew8EAMvE0Xy02isx7QBlrd9pPPV3WZ
9fqGGmd4s7+W/jTcvedSVuWz5XV710GRBdxdaeOVDUO5/IOWOZV7bIBaTxNyxtd9KXpEulKkKtVB
Rgkg/iKgtlswjbyJDNXXcPiHUv3a76xRLgezTv7QCdpw75j6VuZt27VXS9zlLCUVyJ4ueE742pye
hizKV/Ma5ciSixqClnrDvFASadgOWkaLOusm+iPJtrCBvkIApPjW/jAux9JG9uWOdf3yzLnQh1vM
BhBgu4M1t15n3kfsmUjxpKEV/q2MYo45VU85FrmxY53/twIDAQABo0IwQDAPBgNVHRMBAf8EBTAD
AQH/MB0GA1UdDgQWBBS2oVQ5AsOgP46KvPrU+Bym0ToO/TAOBgNVHQ8BAf8EBAMCAQYwDQYJKoZI
hvcNAQENBQADggIBAHGlDs7k6b8/ONWJWsQCYftMxRQXLYtPU2sQF/xlhMcQSZDe28cmk4gmb3DW
Al45oPePq5a1pRNcgRRtDoGCERuKTsZPpd1iHkTfCVn0W3cLN+mLIMb4Ck4uWBzrM9DPhmDJ2vuA
L55MYIR4PSFk1vtBHxgP58l1cb29XN40hz5BsA72udY/CROWFC/emh1auVbONTqwX3BNXuMp8SMo
clm2q8KMZiYcdywmdjWLKKdpoPk79SPdhRB0yZADVpHnr7pH1BKXESLjokmUbOe3lEu6LaTaM4tM
pkT/WjzGHWTYtTHkpjx6qFcL2+1hGsvxznN3Y6SHb0xRONbkX8eftoEq5IVIeVheO/jbAoJnwTnb
w3RLPTYe+SmTiGhbqEQZIfCn6IENLOiTNrQ3ssqwGyZ6miUfmpqAnksqP/ujmv5zMnHCnsZy4Ypo
J/HkD7TETKVhk/iXEAcqMCWpuchxuO9ozC1+9eB+D4Kob7a6bINDd82Kkhehnlt4Fj1F4jNy3eFm
ypnTycUm/Q1oBEauttmbjL4ZvrHG8hnjXALKLNhvSgfZyTXaQHXyxKcZb55CEJh15pWLYLztxRLX
is7VmFxWlgPF7ncGNf/P5O4/E2Hu29othfDNrp2yGAlFw5Khchf8R7agCyzxxN5DaAhqXzvwdmP7
zAYspsbiDrW5viSP
-----END CERTIFICATE-----

Hellenic Academic and Research Institutions RootCA 2015
=======================================================
-----BEGIN CERTIFICATE-----
MIIGCzCCA/OgAwIBAgIBADANBgkqhkiG9w0BAQsFADCBpjELMAkGA1UEBhMCR1IxDzANBgNVBAcT
BkF0aGVuczFEMEIGA1UEChM7SGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJlc2VhcmNoIEluc3RpdHV0
aW9ucyBDZXJ0LiBBdXRob3JpdHkxQDA+BgNVBAMTN0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNl
YXJjaCBJbnN0aXR1dGlvbnMgUm9vdENBIDIwMTUwHhcNMTUwNzA3MTAxMTIxWhcNNDAwNjMwMTAx
MTIxWjCBpjELMAkGA1UEBhMCR1IxDzANBgNVBAcTBkF0aGVuczFEMEIGA1UEChM7SGVsbGVuaWMg
QWNhZGVtaWMgYW5kIFJlc2VhcmNoIEluc3RpdHV0aW9ucyBDZXJ0LiBBdXRob3JpdHkxQDA+BgNV
BAMTN0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgUm9vdENBIDIw
MTUwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDC+Kk/G4n8PDwEXT2QNrCROnk8Zlrv
bTkBSRq0t89/TSNTt5AA4xMqKKYx8ZEA4yjsriFBzh/a/X0SWwGDD7mwX5nh8hKDgE0GPt+sr+eh
iGsxr/CL0BgzuNtFajT0AoAkKAoCFZVedioNmToUW/bLy1O8E00BiDeUJRtCvCLYjqOWXjrZMts+
6PAQZe104S+nfK8nNLspfZu2zwnI5dMK/IhlZXQK3HMcXM1AsRzUtoSMTFDPaI6oWa7CJ06CojXd
FPQf/7J31Ycvqm59JCfnxssm5uX+Zwdj2EUN3TpZZTlYepKZcj2chF6IIbjV9Cz82XBST3i4vTwr
i5WY9bPRaM8gFH5MXF/ni+X1NYEZN9cRCLdmvtNKzoNXADrDgfgXy5I2XdGj2HUb4Ysn6npIQf1F
GQatJ5lOwXBH3bWfgVMS5bGMSF0xQxfjjMZ6Y5ZLKTBOhE5iGV48zpeQpX8B653g+IuJ3SWYPZK2
fu/Z8VFRfS0myGlZYeCsargqNhEEelC9MoS+L9xy1dcdFkfkR2YgP/SWxa+OAXqlD3pk9Q0Yh9mu
iNX6hME6wGkoLfINaFGq46V3xqSQDqE3izEjR8EJCOtu93ib14L8hCCZSRm2Ekax+0VVFqmjZayc
Bw/qa9wfLgZy7IaIEuQt218FL+TwA9MmM+eAws1CoRc0CwIDAQABo0IwQDAPBgNVHRMBAf8EBTAD
AQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUcRVnyMjJvXVdctA4GGqd83EkVAswDQYJKoZI
hvcNAQELBQADggIBAHW7bVRLqhBYRjTyYtcWNl0IXtVsyIe9tC5G8jH4fOpCtZMWVdyhDBKg2mF+
D1hYc2Ryx+hFjtyp8iY/xnmMsVMIM4GwVhO+5lFc2JsKT0ucVlMC6U/2DWDqTUJV6HwbISHTGzrM
d/K4kPFox/la/vot9L/J9UUbzjgQKjeKeaO04wlshYaT/4mWJ3iBj2fjRnRUjtkNaeJK9E10A/+y
d+2VZ5fkscWrv2oj6NSU4kQoYsRL4vDY4ilrGnB+JGGTe08DMiUNRSQrlrRGar9KC/eaj8GsGsVn
82800vpzY4zvFrCopEYq+OsS7HK07/grfoxSwIuEVPkvPuNVqNxmsdnhX9izjFk0WaSrT2y7Hxjb
davYy5LNlDhhDgcGH0tGEPEVvo2FXDtKK4F5D7Rpn0lQl033DlZdwJVqwjbDG2jJ9SrcR5q+ss7F
Jej6A7na+RZukYT1HCjI/CbM1xyQVqdfbzoEvM14iQuODy+jqk+iGxI9FghAD/FGTNeqewjBCvVt
J94Cj8rDtSvK6evIIVM4pcw72Hc3MKJP2W/R8kCtQXoXxdZKNYm3QdV8hn9VTYNKpXMgwDqvkPGa
JI7ZjnHKe7iG2rKPmT4dEw0SEe7Uq/DpFXYC5ODfqiAeW2GFZECpkJcNrVPSWh2HagCXZWK0vm9q
p/UsQu0yrbYhnr68
-----END CERTIFICATE-----

Hellenic Academic and Research Institutions ECC RootCA 2015
===========================================================
-----BEGIN CERTIFICATE-----
MIICwzCCAkqgAwIBAgIBADAKBggqhkjOPQQDAjCBqjELMAkGA1UEBhMCR1IxDzANBgNVBAcTBkF0
aGVuczFEMEIGA1UEChM7SGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJlc2VhcmNoIEluc3RpdHV0aW9u
cyBDZXJ0LiBBdXRob3JpdHkxRDBCBgNVBAMTO0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJj
aCBJbnN0aXR1dGlvbnMgRUNDIFJvb3RDQSAyMDE1MB4XDTE1MDcwNzEwMzcxMloXDTQwMDYzMDEw
MzcxMlowgaoxCzAJBgNVBAYTAkdSMQ8wDQYDVQQHEwZBdGhlbnMxRDBCBgNVBAoTO0hlbGxlbmlj
IEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgQ2VydC4gQXV0aG9yaXR5MUQwQgYD
VQQDEztIZWxsZW5pYyBBY2FkZW1pYyBhbmQgUmVzZWFyY2ggSW5zdGl0dXRpb25zIEVDQyBSb290
Q0EgMjAxNTB2MBAGByqGSM49AgEGBSuBBAAiA2IABJKgQehLgoRc4vgxEZmGZE4JJS+dQS8KrjVP
dJWyUWRrjWvmP3CV8AVER6ZyOFB2lQJajq4onvktTpnvLEhvTCUp6NFxW98dwXU3tNf6e3pCnGoK
Vlp8aQuqgAkkbH7BRqNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0O
BBYEFLQiC4KZJAEOnLvkDv2/+5cgk5kqMAoGCCqGSM49BAMCA2cAMGQCMGfOFmI4oqxiRaeplSTA
GiecMjvAwNW6qef4BENThe5SId6d9SWDPp5YSy/XZxMOIQIwBeF1Ad5o7SofTUwJCA3sS61kFyjn
dc5FZXIhF8siQQ6ME5g4mlRtm8rifOoCWCKR
-----END CERTIFICATE-----

ISRG Root X1
============
-----BEGIN CERTIFICATE-----
MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAwTzELMAkGA1UE
BhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2VhcmNoIEdyb3VwMRUwEwYDVQQD
EwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQG
EwJVUzEpMCcGA1UEChMgSW50ZXJuZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMT
DElTUkcgUm9vdCBYMTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54r
Vygch77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+0TM8ukj1
3Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6UA5/TR5d8mUgjU+g4rk8K
b4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sWT8KOEUt+zwvo/7V3LvSye0rgTBIlDHCN
Aymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyHB5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ
4Q7e2RCOFvu396j3x+UCB5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf
1b0SHzUvKBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWnOlFu
hjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTnjh8BCNAw1FtxNrQH
usEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbwqHyGO0aoSCqI3Haadr8faqU9GY/r
OPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CIrU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4G
A1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY
9umbbjANBgkqhkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL
ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ3BebYhtF8GaV
0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KKNFtY2PwByVS5uCbMiogziUwt
hDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJw
TdwJx4nLCgdNbOhdjsnvzqvHu7UrTkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nx
e5AW0wdeRlN8NwdCjNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZA
JzVcoyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq4RgqsahD
YVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPAmRGunUHBcnWEvgJBQl9n
JEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57demyPxgcYxn/eR44/KJ4EBs+lVDR3veyJ
m+kXQ99b21/+jh5Xos1AnX5iItreGCc=
-----END CERTIFICATE-----

AC RAIZ FNMT-RCM
================
-----BEGIN CERTIFICATE-----
MIIFgzCCA2ugAwIBAgIPXZONMGc2yAYdGsdUhGkHMA0GCSqGSIb3DQEBCwUAMDsxCzAJBgNVBAYT
AkVTMREwDwYDVQQKDAhGTk1ULVJDTTEZMBcGA1UECwwQQUMgUkFJWiBGTk1ULVJDTTAeFw0wODEw
MjkxNTU5NTZaFw0zMDAxMDEwMDAwMDBaMDsxCzAJBgNVBAYTAkVTMREwDwYDVQQKDAhGTk1ULVJD
TTEZMBcGA1UECwwQQUMgUkFJWiBGTk1ULVJDTTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoC
ggIBALpxgHpMhm5/yBNtwMZ9HACXjywMI7sQmkCpGreHiPibVmr75nuOi5KOpyVdWRHbNi63URcf
qQgfBBckWKo3Shjf5TnUV/3XwSyRAZHiItQDwFj8d0fsjz50Q7qsNI1NOHZnjrDIbzAzWHFctPVr
btQBULgTfmxKo0nRIBnuvMApGGWn3v7v3QqQIecaZ5JCEJhfTzC8PhxFtBDXaEAUwED653cXeuYL
j2VbPNmaUtu1vZ5Gzz3rkQUCwJaydkxNEJY7kvqcfw+Z374jNUUeAlz+taibmSXaXvMiwzn15Cou
08YfxGyqxRxqAQVKL9LFwag0Jl1mpdICIfkYtwb1TplvqKtMUejPUBjFd8g5CSxJkjKZqLsXF3mw
WsXmo8RZZUc1g16p6DULmbvkzSDGm0oGObVo/CK67lWMK07q87Hj/LaZmtVC+nFNCM+HHmpxffnT
tOmlcYF7wk5HlqX2doWjKI/pgG6BU6VtX7hI+cL5NqYuSf+4lsKMB7ObiFj86xsc3i1w4peSMKGJ
47xVqCfWS+2QrYv6YyVZLag13cqXM7zlzced0ezvXg5KkAYmY6252TUtB7p2ZSysV4999AeU14EC
ll2jB0nVetBX+RvnU0Z1qrB5QstocQjpYL05ac70r8NWQMetUqIJ5G+GR4of6ygnXYMgrwTJbFaa
i0b1AgMBAAGjgYMwgYAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYE
FPd9xf3E6Jobd2Sn9R2gzL+HYJptMD4GA1UdIAQ3MDUwMwYEVR0gADArMCkGCCsGAQUFBwIBFh1o
dHRwOi8vd3d3LmNlcnQuZm5tdC5lcy9kcGNzLzANBgkqhkiG9w0BAQsFAAOCAgEAB5BK3/MjTvDD
nFFlm5wioooMhfNzKWtN/gHiqQxjAb8EZ6WdmF/9ARP67Jpi6Yb+tmLSbkyU+8B1RXxlDPiyN8+s
D8+Nb/kZ94/sHvJwnvDKuO+3/3Y3dlv2bojzr2IyIpMNOmqOFGYMLVN0V2Ue1bLdI4E7pWYjJ2cJ
j+F3qkPNZVEI7VFY/uY5+ctHhKQV8Xa7pO6kO8Rf77IzlhEYt8llvhjho6Tc+hj507wTmzl6NLrT
Qfv6MooqtyuGC2mDOL7Nii4LcK2NJpLuHvUBKwrZ1pebbuCoGRw6IYsMHkCtA+fdZn71uSANA+iW
+YJF1DngoABd15jmfZ5nc8OaKveri6E6FO80vFIOiZiaBECEHX5FaZNXzuvO+FB8TxxuBEOb+dY7
Ixjp6o7RTUaN8Tvkasq6+yO3m/qZASlaWFot4/nUbQ4mrcFuNLwy+AwF+mWj2zs3gyLp1txyM/1d
8iC9djwj2ij3+RvrWWTV3F9yfiD8zYm1kGdNYno/Tq0dwzn+evQoFt9B9kiABdcPUXmsEKvU7ANm
5mqwujGSQkBqvjrTcuFqN1W8rB2Vt2lh8kORdOag0wokRqEIr9baRRmW1FMdW4R58MD3R++Lj8UG
rp1MYp3/RgT408m2ECVAdf4WqslKYIYvuu8wd+RU4riEmViAqhOLUTpPSPaLtrM=
-----END CERTIFICATE-----

Amazon Root CA 1
================
-----BEGIN CERTIFICATE-----
MIIDQTCCAimgAwIBAgITBmyfz5m/jAo54vB4ikPmljZbyjANBgkqhkiG9w0BAQsFADA5MQswCQYD
VQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6b24gUm9vdCBDQSAxMB4XDTE1
MDUyNjAwMDAwMFoXDTM4MDExNzAwMDAwMFowOTELMAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpv
bjEZMBcGA1UEAxMQQW1hem9uIFJvb3QgQ0EgMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
ggEBALJ4gHHKeNXjca9HgFB0fW7Y14h29Jlo91ghYPl0hAEvrAIthtOgQ3pOsqTQNroBvo3bSMgH
FzZM9O6II8c+6zf1tRn4SWiw3te5djgdYZ6k/oI2peVKVuRF4fn9tBb6dNqcmzU5L/qwIFAGbHrQ
gLKm+a/sRxmPUDgH3KKHOVj4utWp+UhnMJbulHheb4mjUcAwhmahRWa6VOujw5H5SNz/0egwLX0t
dHA114gk957EWW67c4cX8jJGKLhD+rcdqsq08p8kDi1L93FcXmn/6pUCyziKrlA4b9v7LWIbxcce
VOF34GfID5yHI9Y/QCB/IIDEgEw+OyQmjgSubJrIqg0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB
/zAOBgNVHQ8BAf8EBAMCAYYwHQYDVR0OBBYEFIQYzIU07LwMlJQuCFmcx7IQTgoIMA0GCSqGSIb3
DQEBCwUAA4IBAQCY8jdaQZChGsV2USggNiMOruYou6r4lK5IpDB/G/wkjUu0yKGX9rbxenDIU5PM
CCjjmCXPI6T53iHTfIUJrU6adTrCC2qJeHZERxhlbI1Bjjt/msv0tadQ1wUsN+gDS63pYaACbvXy
8MWy7Vu33PqUXHeeE6V/Uq2V8viTO96LXFvKWlJbYK8U90vvo/ufQJVtMVT8QtPHRh8jrdkPSHCa
2XV4cdFyQzR1bldZwgJcJmApzyMZFo6IQ6XU5MsI+yMRQ+hDKXJioaldXgjUkK642M4UwtBV8ob2
xJNDd2ZhwLnoQdeXeGADbkpyrqXRfboQnoZsG4q5WTP468SQvvG5
-----END CERTIFICATE-----

Amazon Root CA 2
================
-----BEGIN CERTIFICATE-----
MIIFQTCCAymgAwIBAgITBmyf0pY1hp8KD+WGePhbJruKNzANBgkqhkiG9w0BAQwFADA5MQswCQYD
VQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6b24gUm9vdCBDQSAyMB4XDTE1
MDUyNjAwMDAwMFoXDTQwMDUyNjAwMDAwMFowOTELMAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpv
bjEZMBcGA1UEAxMQQW1hem9uIFJvb3QgQ0EgMjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoC
ggIBAK2Wny2cSkxKgXlRmeyKy2tgURO8TW0G/LAIjd0ZEGrHJgw12MBvIITplLGbhQPDW9tK6Mj4
kHbZW0/jTOgGNk3Mmqw9DJArktQGGWCsN0R5hYGCrVo34A3MnaZMUnbqQ523BNFQ9lXg1dKmSYXp
N+nKfq5clU1Imj+uIFptiJXZNLhSGkOQsL9sBbm2eLfq0OQ6PBJTYv9K8nu+NQWpEjTj82R0Yiw9
AElaKP4yRLuH3WUnAnE72kr3H9rN9yFVkE8P7K6C4Z9r2UXTu/Bfh+08LDmG2j/e7HJV63mjrdvd
fLC6HM783k81ds8P+HgfajZRRidhW+mez/CiVX18JYpvL7TFz4QuK/0NURBs+18bvBt+xa47mAEx
kv8LV/SasrlX6avvDXbR8O70zoan4G7ptGmh32n2M8ZpLpcTnqWHsFcQgTfJU7O7f/aS0ZzQGPSS
btqDT6ZjmUyl+17vIWR6IF9sZIUVyzfpYgwLKhbcAS4y2j5L9Z469hdAlO+ekQiG+r5jqFoz7Mt0
Q5X5bGlSNscpb/xVA1wf+5+9R+vnSUeVC06JIglJ4PVhHvG/LopyboBZ/1c6+XUyo05f7O0oYtlN
c/LMgRdg7c3r3NunysV+Ar3yVAhU/bQtCSwXVEqY0VThUWcI0u1ufm8/0i2BWSlmy5A5lREedCf+
3euvAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBSw
DPBMMPQFWAJI/TPlUq9LhONmUjANBgkqhkiG9w0BAQwFAAOCAgEAqqiAjw54o+Ci1M3m9Zh6O+oA
A7CXDpO8Wqj2LIxyh6mx/H9z/WNxeKWHWc8w4Q0QshNabYL1auaAn6AFC2jkR2vHat+2/XcycuUY
+gn0oJMsXdKMdYV2ZZAMA3m3MSNjrXiDCYZohMr/+c8mmpJ5581LxedhpxfL86kSk5Nrp+gvU5LE
YFiwzAJRGFuFjWJZY7attN6a+yb3ACfAXVU3dJnJUH/jWS5E4ywl7uxMMne0nxrpS10gxdr9HIcW
xkPo1LsmmkVwXqkLN1PiRnsn/eBG8om3zEK2yygmbtmlyTrIQRNg91CMFa6ybRoVGld45pIq2WWQ
gj9sAq+uEjonljYE1x2igGOpm/HlurR8FLBOybEfdF849lHqm/osohHUqS0nGkWxr7JOcQ3AWEbW
aQbLU8uz/mtBzUF+fUwPfHJ5elnNXkoOrJupmHN5fLT0zLm4BwyydFy4x2+IoZCn9Kr5v2c69BoV
Yh63n749sSmvZ6ES8lgQGVMDMBu4Gon2nL2XA46jCfMdiyHxtN/kHNGfZQIG6lzWE7OE76KlXIx3
KadowGuuQNKotOrN8I1LOJwZmhsoVLiJkO/KdYE+HvJkJMcYr07/R54H9jVlpNMKVv/1F2Rs76gi
JUmTtt8AF9pYfl3uxRuw0dFfIRDH+fO6AgonB8Xx1sfT4PsJYGw=
-----END CERTIFICATE-----

Amazon Root CA 3
================
-----BEGIN CERTIFICATE-----
MIIBtjCCAVugAwIBAgITBmyf1XSXNmY/Owua2eiedgPySjAKBggqhkjOPQQDAjA5MQswCQYDVQQG
EwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6b24gUm9vdCBDQSAzMB4XDTE1MDUy
NjAwMDAwMFoXDTQwMDUyNjAwMDAwMFowOTELMAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZ
MBcGA1UEAxMQQW1hem9uIFJvb3QgQ0EgMzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABCmXp8ZB
f8ANm+gBG1bG8lKlui2yEujSLtf6ycXYqm0fc4E7O5hrOXwzpcVOho6AF2hiRVd9RFgdszflZwjr
Zt6jQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBSrttvXBp43
rDCGB5Fwx5zEGbF4wDAKBggqhkjOPQQDAgNJADBGAiEA4IWSoxe3jfkrBqWTrBqYaGFy+uGh0Psc
eGCmQ5nFuMQCIQCcAu/xlJyzlvnrxir4tiz+OpAUFteMYyRIHN8wfdVoOw==
-----END CERTIFICATE-----

Amazon Root CA 4
================
-----BEGIN CERTIFICATE-----
MIIB8jCCAXigAwIBAgITBmyf18G7EEwpQ+Vxe3ssyBrBDjAKBggqhkjOPQQDAzA5MQswCQYDVQQG
EwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6b24gUm9vdCBDQSA0MB4XDTE1MDUy
NjAwMDAwMFoXDTQwMDUyNjAwMDAwMFowOTELMAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZ
MBcGA1UEAxMQQW1hem9uIFJvb3QgQ0EgNDB2MBAGByqGSM49AgEGBSuBBAAiA2IABNKrijdPo1MN
/sGKe0uoe0ZLY7Bi9i0b2whxIdIA6GO9mif78DluXeo9pcmBqqNbIJhFXRbb/egQbeOc4OO9X4Ri
83BkM6DLJC9wuoihKqB1+IGuYgbEgds5bimwHvouXKNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNV
HQ8BAf8EBAMCAYYwHQYDVR0OBBYEFNPsxzplbszh2naaVvuc84ZtV+WBMAoGCCqGSM49BAMDA2gA
MGUCMDqLIfG9fhGt0O9Yli/W651+kI0rz2ZVwyzjKKlwCkcO8DdZEv8tmZQoTipPNU0zWgIxAOp1
AE47xDqUEpHJWEadIRNyp4iciuRMStuW1KyLa2tJElMzrdfkviT8tQp21KW8EA==
-----END CERTIFICATE-----

TUBITAK Kamu SM SSL Kok Sertifikasi - Surum 1
=============================================
-----BEGIN CERTIFICATE-----
MIIEYzCCA0ugAwIBAgIBATANBgkqhkiG9w0BAQsFADCB0jELMAkGA1UEBhMCVFIxGDAWBgNVBAcT
D0dlYnplIC0gS29jYWVsaTFCMEAGA1UEChM5VHVya2l5ZSBCaWxpbXNlbCB2ZSBUZWtub2xvamlr
IEFyYXN0aXJtYSBLdXJ1bXUgLSBUVUJJVEFLMS0wKwYDVQQLEyRLYW11IFNlcnRpZmlrYXN5b24g
TWVya2V6aSAtIEthbXUgU00xNjA0BgNVBAMTLVRVQklUQUsgS2FtdSBTTSBTU0wgS29rIFNlcnRp
ZmlrYXNpIC0gU3VydW0gMTAeFw0xMzExMjUwODI1NTVaFw00MzEwMjUwODI1NTVaMIHSMQswCQYD
VQQGEwJUUjEYMBYGA1UEBxMPR2ViemUgLSBLb2NhZWxpMUIwQAYDVQQKEzlUdXJraXllIEJpbGlt
c2VsIHZlIFRla25vbG9qaWsgQXJhc3Rpcm1hIEt1cnVtdSAtIFRVQklUQUsxLTArBgNVBAsTJEth
bXUgU2VydGlmaWthc3lvbiBNZXJrZXppIC0gS2FtdSBTTTE2MDQGA1UEAxMtVFVCSVRBSyBLYW11
IFNNIFNTTCBLb2sgU2VydGlmaWthc2kgLSBTdXJ1bSAxMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
MIIBCgKCAQEAr3UwM6q7a9OZLBI3hNmNe5eA027n/5tQlT6QlVZC1xl8JoSNkvoBHToP4mQ4t4y8
6Ij5iySrLqP1N+RAjhgleYN1Hzv/bKjFxlb4tO2KRKOrbEz8HdDc72i9z+SqzvBV96I01INrN3wc
wv61A+xXzry0tcXtAA9TNypN9E8Mg/uGz8v+jE69h/mniyFXnHrfA2eJLJ2XYacQuFWQfw4tJzh0
3+f92k4S400VIgLI4OD8D62K18lUUMw7D8oWgITQUVbDjlZ/iSIzL+aFCr2lqBs23tPcLG07xxO9
WSMs5uWk99gL7eqQQESolbuT1dCANLZGeA4fAJNG4e7p+exPFwIDAQABo0IwQDAdBgNVHQ4EFgQU
ZT/HiobGPN08VFw1+DrtUgxHV8gwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJ
KoZIhvcNAQELBQADggEBACo/4fEyjq7hmFxLXs9rHmoJ0iKpEsdeV31zVmSAhHqT5Am5EM2fKifh
AHe+SMg1qIGf5LgsyX8OsNJLN13qudULXjS99HMpw+0mFZx+CFOKWI3QSyjfwbPfIPP54+M638yc
lNhOT8NrF7f3cuitZjO1JVOr4PhMqZ398g26rrnZqsZr+ZO7rqu4lzwDGrpDxpa5RXI4s6ehlj2R
e37AIVNMh+3yC1SVUZPVIqUNivGTDj5UDrDYyU7c8jEyVupk+eq1nRZmQnLzf9OxMUP8pI4X8W0j
q5Rm+K37DwhuJi1/FwcJsoz7UMCflo3Ptv0AnVoUmr8CRPXBwp8iXqIPoeM=
-----END CERTIFICATE-----

GDCA TrustAUTH R5 ROOT
======================
-----BEGIN CERTIFICATE-----
MIIFiDCCA3CgAwIBAgIIfQmX/vBH6nowDQYJKoZIhvcNAQELBQAwYjELMAkGA1UEBhMCQ04xMjAw
BgNVBAoMKUdVQU5HIERPTkcgQ0VSVElGSUNBVEUgQVVUSE9SSVRZIENPLixMVEQuMR8wHQYDVQQD
DBZHRENBIFRydXN0QVVUSCBSNSBST09UMB4XDTE0MTEyNjA1MTMxNVoXDTQwMTIzMTE1NTk1OVow
YjELMAkGA1UEBhMCQ04xMjAwBgNVBAoMKUdVQU5HIERPTkcgQ0VSVElGSUNBVEUgQVVUSE9SSVRZ
IENPLixMVEQuMR8wHQYDVQQDDBZHRENBIFRydXN0QVVUSCBSNSBST09UMIICIjANBgkqhkiG9w0B
AQEFAAOCAg8AMIICCgKCAgEA2aMW8Mh0dHeb7zMNOwZ+Vfy1YI92hhJCfVZmPoiC7XJjDp6L3TQs
AlFRwxn9WVSEyfFrs0yw6ehGXTjGoqcuEVe6ghWinI9tsJlKCvLriXBjTnnEt1u9ol2x8kECK62p
OqPseQrsXzrj/e+APK00mxqriCZ7VqKChh/rNYmDf1+uKU49tm7srsHwJ5uu4/Ts765/94Y9cnrr
pftZTqfrlYwiOXnhLQiPzLyRuEH3FMEjqcOtmkVEs7LXLM3GKeJQEK5cy4KOFxg2fZfmiJqwTTQJ
9Cy5WmYqsBebnh52nUpmMUHfP/vFBu8btn4aRjb3ZGM74zkYI+dndRTVdVeSN72+ahsmUPI2JgaQ
xXABZG12ZuGR224HwGGALrIuL4xwp9E7PLOR5G62xDtw8mySlwnNR30YwPO7ng/Wi64HtloPzgsM
R6flPri9fcebNaBhlzpBdRfMK5Z3KpIhHtmVdiBnaM8Nvd/WHwlqmuLMc3GkL30SgLdTMEZeS1SZ
D2fJpcjyIMGC7J0R38IC+xo70e0gmu9lZJIQDSri3nDxGGeCjGHeuLzRL5z7D9Ar7Rt2ueQ5Vfj4
oR24qoAATILnsn8JuLwwoC8N9VKejveSswoAHQBUlwbgsQfZxw9cZX08bVlX5O2ljelAU58VS6Bx
9hoh49pwBiFYFIeFd3mqgnkCAwEAAaNCMEAwHQYDVR0OBBYEFOLJQJ9NzuiaoXzPDj9lxSmIahlR
MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEBCwUAA4ICAQDRSVfg
p8xoWLoBDysZzY2wYUWsEe1jUGn4H3++Fo/9nesLqjJHdtJnJO29fDMylyrHBYZmDRd9FBUb1Ov9
H5r2XpdptxolpAqzkT9fNqyL7FeoPueBihhXOYV0GkLH6VsTX4/5COmSdI31R9KrO9b7eGZONn35
6ZLpBN79SWP8bfsUcZNnL0dKt7n/HipzcEYwv1ryL3ml4Y0M2fmyYzeMN2WFcGpcWwlyua1jPLHd
+PwyvzeG5LuOmCd+uh8W4XAR8gPfJWIyJyYYMoSf/wA6E7qaTfRPuBRwIrHKK5DOKcFw9C+df/KQ
HtZa37dG/OaG+svgIHZ6uqbL9XzeYqWxi+7egmaKTjowHz+Ay60nugxe19CxVsp3cbK1daFQqUBD
F8Io2c9Si1vIY9RCPqAzekYu9wogRlR+ak8x8YF+QnQ4ZXMn7sZ8uI7XpTrXmKGcjBBV09tL7ECQ
8s1uV9JiDnxXk7Gnbc2dg7sq5+W2O3FYrf3RRbxake5TFW/TRQl1brqQXR4EzzffHqhmsYzmIGrv
/EhOdJhCrylvLmrH+33RZjEizIYAfmaDDEL0vTSSwxrqT8p+ck0LcIymSLumoRT2+1hEmRSuqguT
aaApJUqlyyvdimYHFngVV3Eb7PVHhPOeMTd61X8kreS8/f3MboPoDKi3QWwH3b08hpcv0g==
-----END CERTIFICATE-----

TrustCor RootCert CA-1
======================
-----BEGIN CERTIFICATE-----
MIIEMDCCAxigAwIBAgIJANqb7HHzA7AZMA0GCSqGSIb3DQEBCwUAMIGkMQswCQYDVQQGEwJQQTEP
MA0GA1UECAwGUGFuYW1hMRQwEgYDVQQHDAtQYW5hbWEgQ2l0eTEkMCIGA1UECgwbVHJ1c3RDb3Ig
U3lzdGVtcyBTLiBkZSBSLkwuMScwJQYDVQQLDB5UcnVzdENvciBDZXJ0aWZpY2F0ZSBBdXRob3Jp
dHkxHzAdBgNVBAMMFlRydXN0Q29yIFJvb3RDZXJ0IENBLTEwHhcNMTYwMjA0MTIzMjE2WhcNMjkx
MjMxMTcyMzE2WjCBpDELMAkGA1UEBhMCUEExDzANBgNVBAgMBlBhbmFtYTEUMBIGA1UEBwwLUGFu
YW1hIENpdHkxJDAiBgNVBAoMG1RydXN0Q29yIFN5c3RlbXMgUy4gZGUgUi5MLjEnMCUGA1UECwwe
VHJ1c3RDb3IgQ2VydGlmaWNhdGUgQXV0aG9yaXR5MR8wHQYDVQQDDBZUcnVzdENvciBSb290Q2Vy
dCBDQS0xMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAv463leLCJhJrMxnHQFgKq1mq
jQCj/IDHUHuO1CAmujIS2CNUSSUQIpidRtLByZ5OGy4sDjjzGiVoHKZaBeYei0i/mJZ0PmnK6bV4
pQa81QBeCQryJ3pS/C3Vseq0iWEk8xoT26nPUu0MJLq5nux+AHT6k61sKZKuUbS701e/s/OojZz0
JEsq1pme9J7+wH5COucLlVPat2gOkEz7cD+PSiyU8ybdY2mplNgQTsVHCJCZGxdNuWxu72CVEY4h
gLW9oHPY0LJ3xEXqWib7ZnZ2+AYfYW0PVcWDtxBWcgYHpfOxGgMFZA6dWorWhnAbJN7+KIor0Gqw
/Hqi3LJ5DotlDwIDAQABo2MwYTAdBgNVHQ4EFgQU7mtJPHo/DeOxCbeKyKsZn3MzUOcwHwYDVR0j
BBgwFoAU7mtJPHo/DeOxCbeKyKsZn3MzUOcwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC
AYYwDQYJKoZIhvcNAQELBQADggEBACUY1JGPE+6PHh0RU9otRCkZoB5rMZ5NDp6tPVxBb5UrJKF5
mDo4Nvu7Zp5I/5CQ7z3UuJu0h3U/IJvOcs+hVcFNZKIZBqEHMwwLKeXx6quj7LUKdJDHfXLy11yf
ke+Ri7fc7Waiz45mO7yfOgLgJ90WmMCV1Aqk5IGadZQ1nJBfiDcGrVmVCrDRZ9MZyonnMlo2HD6C
qFqTvsbQZJG2z9m2GM/bftJlo6bEjhcxwft+dtvTheNYsnd6djtsL1Ac59v2Z3kf9YKVmgenFK+P
3CghZwnS1k1aHBkcjndcw5QkPTJrS37UeJSDvjdNzl/HHk484IkzlQsPpTLWPFp5LBk=
-----END CERTIFICATE-----

TrustCor RootCert CA-2
======================
-----BEGIN CERTIFICATE-----
MIIGLzCCBBegAwIBAgIIJaHfyjPLWQIwDQYJKoZIhvcNAQELBQAwgaQxCzAJBgNVBAYTAlBBMQ8w
DQYDVQQIDAZQYW5hbWExFDASBgNVBAcMC1BhbmFtYSBDaXR5MSQwIgYDVQQKDBtUcnVzdENvciBT
eXN0ZW1zIFMuIGRlIFIuTC4xJzAlBgNVBAsMHlRydXN0Q29yIENlcnRpZmljYXRlIEF1dGhvcml0
eTEfMB0GA1UEAwwWVHJ1c3RDb3IgUm9vdENlcnQgQ0EtMjAeFw0xNjAyMDQxMjMyMjNaFw0zNDEy
MzExNzI2MzlaMIGkMQswCQYDVQQGEwJQQTEPMA0GA1UECAwGUGFuYW1hMRQwEgYDVQQHDAtQYW5h
bWEgQ2l0eTEkMCIGA1UECgwbVHJ1c3RDb3IgU3lzdGVtcyBTLiBkZSBSLkwuMScwJQYDVQQLDB5U
cnVzdENvciBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkxHzAdBgNVBAMMFlRydXN0Q29yIFJvb3RDZXJ0
IENBLTIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCnIG7CKqJiJJWQdsg4foDSq8Gb
ZQWU9MEKENUCrO2fk8eHyLAnK0IMPQo+QVqedd2NyuCb7GgypGmSaIwLgQ5WoD4a3SwlFIIvl9Nk
RvRUqdw6VC0xK5mC8tkq1+9xALgxpL56JAfDQiDyitSSBBtlVkxs1Pu2YVpHI7TYabS3OtB0PAx1
oYxOdqHp2yqlO/rOsP9+aij9JxzIsekp8VduZLTQwRVtDr4uDkbIXvRR/u8OYzo7cbrPb1nKDOOb
XUm4TOJXsZiKQlecdu/vvdFoqNL0Cbt3Nb4lggjEFixEIFapRBF37120Hapeaz6LMvYHL1cEksr1
/p3C6eizjkxLAjHZ5DxIgif3GIJ2SDpxsROhOdUuxTTCHWKF3wP+TfSvPd9cW436cOGlfifHhi5q
jxLGhF5DUVCcGZt45vz27Ud+ez1m7xMTiF88oWP7+ayHNZ/zgp6kPwqcMWmLmaSISo5uZk3vFsQP
eSghYA2FFn3XVDjxklb9tTNMg9zXEJ9L/cb4Qr26fHMC4P99zVvh1Kxhe1fVSntb1IVYJ12/+Ctg
rKAmrhQhJ8Z3mjOAPF5GP/fDsaOGM8boXg25NSyqRsGFAnWAoOsk+xWq5Gd/bnc/9ASKL3x74xdh
8N0JqSDIvgmk0H5Ew7IwSjiqqewYmgeCK9u4nBit2uBGF6zPXQIDAQABo2MwYTAdBgNVHQ4EFgQU
2f4hQG6UnrybPZx9mCAZ5YwwYrIwHwYDVR0jBBgwFoAU2f4hQG6UnrybPZx9mCAZ5YwwYrIwDwYD
VR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAYYwDQYJKoZIhvcNAQELBQADggIBAJ5Fngw7tu/h
Osh80QA9z+LqBrWyOrsGS2h60COXdKcs8AjYeVrXWoSK2BKaG9l9XE1wxaX5q+WjiYndAfrs3fnp
kpfbsEZC89NiqpX+MWcUaViQCqoL7jcjx1BRtPV+nuN79+TMQjItSQzL/0kMmx40/W5ulop5A7Zv
2wnL/V9lFDfhOPXzYRZY5LVtDQsEGz9QLX+zx3oaFoBg+Iof6Rsqxvm6ARppv9JYx1RXCI/hOWB3
S6xZhBqI8d3LT3jX5+EzLfzuQfogsL7L9ziUwOHQhQ+77Sxzq+3+knYaZH9bDTMJBzN7Bj8RpFxw
PIXAz+OQqIN3+tvmxYxoZxBnpVIt8MSZj3+/0WvitUfW2dCFmU2Umw9Lje4AWkcdEQOsQRivh7dv
DDqPys/cA8GiCcjl/YBeyGBCARsaU1q7N6a3vLqE6R5sGtRk2tRD/pOLS/IseRYQ1JMLiI+h2IYU
RpFHmygk71dSTlxCnKr3Sewn6EAes6aJInKc9Q0ztFijMDvd1GpUk74aTfOTlPf8hAs/hCBcNANE
xdqtvArBAs8e5ZTZ845b2EzwnexhF7sUMlQMAimTHpKG9n/v55IFDlndmQguLvqcAFLTxWYp5KeX
RKQOKIETNcX2b2TmQcTVL8w0RSXPQQCWPUouwpaYT05KnJe32x+SMsj/D1Fu1uwJ
-----END CERTIFICATE-----

TrustCor ECA-1
==============
-----BEGIN CERTIFICATE-----
MIIEIDCCAwigAwIBAgIJAISCLF8cYtBAMA0GCSqGSIb3DQEBCwUAMIGcMQswCQYDVQQGEwJQQTEP
MA0GA1UECAwGUGFuYW1hMRQwEgYDVQQHDAtQYW5hbWEgQ2l0eTEkMCIGA1UECgwbVHJ1c3RDb3Ig
U3lzdGVtcyBTLiBkZSBSLkwuMScwJQYDVQQLDB5UcnVzdENvciBDZXJ0aWZpY2F0ZSBBdXRob3Jp
dHkxFzAVBgNVBAMMDlRydXN0Q29yIEVDQS0xMB4XDTE2MDIwNDEyMzIzM1oXDTI5MTIzMTE3Mjgw
N1owgZwxCzAJBgNVBAYTAlBBMQ8wDQYDVQQIDAZQYW5hbWExFDASBgNVBAcMC1BhbmFtYSBDaXR5
MSQwIgYDVQQKDBtUcnVzdENvciBTeXN0ZW1zIFMuIGRlIFIuTC4xJzAlBgNVBAsMHlRydXN0Q29y
IENlcnRpZmljYXRlIEF1dGhvcml0eTEXMBUGA1UEAwwOVHJ1c3RDb3IgRUNBLTEwggEiMA0GCSqG
SIb3DQEBAQUAA4IBDwAwggEKAoIBAQDPj+ARtZ+odnbb3w9U73NjKYKtR8aja+3+XzP4Q1HpGjOR
MRegdMTUpwHmspI+ap3tDvl0mEDTPwOABoJA6LHip1GnHYMma6ve+heRK9jGrB6xnhkB1Zem6g23
xFUfJ3zSCNV2HykVh0A53ThFEXXQmqc04L/NyFIduUd+Dbi7xgz2c1cWWn5DkR9VOsZtRASqnKmc
p0yJF4OuowReUoCLHhIlERnXDH19MURB6tuvsBzvgdAsxZohmz3tQjtQJvLsznFhBmIhVE5/wZ0+
fyCMgMsq2JdiyIMzkX2woloPV+g7zPIlstR8L+xNxqE6FXrntl019fZISjZFZtS6mFjBAgMBAAGj
YzBhMB0GA1UdDgQWBBREnkj1zG1I1KBLf/5ZJC+Dl5mahjAfBgNVHSMEGDAWgBREnkj1zG1I1KBL
f/5ZJC+Dl5mahjAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQsF
AAOCAQEABT41XBVwm8nHc2FvcivUwo/yQ10CzsSUuZQRg2dd4mdsdXa/uwyqNsatR5Nj3B5+1t4u
/ukZMjgDfxT2AHMsWbEhBuH7rBiVDKP/mZb3Kyeb1STMHd3BOuCYRLDE5D53sXOpZCz2HAF8P11F
hcCF5yWPldwX8zyfGm6wyuMdKulMY/okYWLW2n62HGz1Ah3UKt1VkOsqEUc8Ll50soIipX1TH0Xs
J5F95yIW6MBoNtjG8U+ARDL54dHRHareqKucBK+tIA5kmE2la8BIWJZpTdwHjFGTot+fDz2LYLSC
jaoITmJF4PkL0uDgPFveXHEnJcLmA4GLEFPjx1WitJ/X5g==
-----END CERTIFICATE-----

SSL.com Root Certification Authority RSA
========================================
-----BEGIN CERTIFICATE-----
MIIF3TCCA8WgAwIBAgIIeyyb0xaAMpkwDQYJKoZIhvcNAQELBQAwfDELMAkGA1UEBhMCVVMxDjAM
BgNVBAgMBVRleGFzMRAwDgYDVQQHDAdIb3VzdG9uMRgwFgYDVQQKDA9TU0wgQ29ycG9yYXRpb24x
MTAvBgNVBAMMKFNTTC5jb20gUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSBSU0EwHhcNMTYw
MjEyMTczOTM5WhcNNDEwMjEyMTczOTM5WjB8MQswCQYDVQQGEwJVUzEOMAwGA1UECAwFVGV4YXMx
EDAOBgNVBAcMB0hvdXN0b24xGDAWBgNVBAoMD1NTTCBDb3Jwb3JhdGlvbjExMC8GA1UEAwwoU1NM
LmNvbSBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IFJTQTCCAiIwDQYJKoZIhvcNAQEBBQAD
ggIPADCCAgoCggIBAPkP3aMrfcvQKv7sZ4Wm5y4bunfh4/WvpOz6Sl2RxFdHaxh3a3by/ZPkPQ/C
Fp4LZsNWlJ4Xg4XOVu/yFv0AYvUiCVToZRdOQbngT0aXqhvIuG5iXmmxX9sqAn78bMrzQdjt0Oj8
P2FI7bADFB0QDksZ4LtO7IZl/zbzXmcCC52GVWH9ejjt/uIZALdvoVBidXQ8oPrIJZK0bnoix/ge
oeOy3ZExqysdBP+lSgQ36YWkMyv94tZVNHwZpEpox7Ko07fKoZOI68GXvIz5HdkihCR0xwQ9aqkp
k8zruFvh/l8lqjRYyMEjVJ0bmBHDOJx+PYZspQ9AhnwC9FwCTyjLrnGfDzrIM/4RJTXq/LrFYD3Z
fBjVsqnTdXgDciLKOsMf7yzlLqn6niy2UUb9rwPW6mBo6oUWNmuF6R7As93EJNyAKoFBbZQ+yODJ
gUEAnl6/f8UImKIYLEJAs/lvOCdLToD0PYFH4Ih86hzOtXVcUS4cK38acijnALXRdMbX5J+tB5O2
UzU1/Dfkw/ZdFr4hc96SCvigY2q8lpJqPvi8ZVWb3vUNiSYE/CUapiVpy8JtynziWV+XrOvvLsi8
1xtZPCvM8hnIk2snYxnP/Okm+Mpxm3+T/jRnhE6Z6/yzeAkzcLpmpnbtG3PrGqUNxCITIJRWCk4s
bE6x/c+cCbqiM+2HAgMBAAGjYzBhMB0GA1UdDgQWBBTdBAkHovV6fVJTEpKV7jiAJQ2mWTAPBgNV
HRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFN0ECQei9Xp9UlMSkpXuOIAlDaZZMA4GA1UdDwEB/wQE
AwIBhjANBgkqhkiG9w0BAQsFAAOCAgEAIBgRlCn7Jp0cHh5wYfGVcpNxJK1ok1iOMq8bs3AD/CUr
dIWQPXhq9LmLpZc7tRiRux6n+UBbkflVma8eEdBcHadm47GUBwwyOabqG7B52B2ccETjit3E+ZUf
ijhDPwGFpUenPUayvOUiaPd7nNgsPgohyC0zrL/FgZkxdMF1ccW+sfAjRfSda/wZY52jvATGGAsl
u1OJD7OAUN5F7kR/q5R4ZJjT9ijdh9hwZXT7DrkT66cPYakylszeu+1jTBi7qUD3oFRuIIhxdRjq
erQ0cuAjJ3dctpDqhiVAq+8zD8ufgr6iIPv2tS0a5sKFsXQP+8hlAqRSAUfdSSLBv9jra6x+3uxj
MxW3IwiPxg+NQVrdjsW5j+VFP3jbutIbQLH+cU0/4IGiul607BXgk90IH37hVZkLId6Tngr75qNJ
vTYw/ud3sqB1l7UtgYgXZSD32pAAn8lSzDLKNXz1PQ/YK9f1JmzJBjSWFupwWRoyeXkLtoh/D1JI
Pb9s2KJELtFOt3JY04kTlf5Eq/jXixtunLwsoFvVagCvXzfh1foQC5ichucmj87w7G6KVwuA406y
wKBjYZC6VWg3dGq2ktufoYYitmUnDuy2n0Jg5GfCtdpBC8TTi2EbvPofkSvXRAdeuims2cXp71NI
WuuA8ShYIc2wBlX7Jz9TkHCpBB5XJ7k=
-----END CERTIFICATE-----

SSL.com Root Certification Authority ECC
========================================
-----BEGIN CERTIFICATE-----
MIICjTCCAhSgAwIBAgIIdebfy8FoW6gwCgYIKoZIzj0EAwIwfDELMAkGA1UEBhMCVVMxDjAMBgNV
BAgMBVRleGFzMRAwDgYDVQQHDAdIb3VzdG9uMRgwFgYDVQQKDA9TU0wgQ29ycG9yYXRpb24xMTAv
BgNVBAMMKFNTTC5jb20gUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSBFQ0MwHhcNMTYwMjEy
MTgxNDAzWhcNNDEwMjEyMTgxNDAzWjB8MQswCQYDVQQGEwJVUzEOMAwGA1UECAwFVGV4YXMxEDAO
BgNVBAcMB0hvdXN0b24xGDAWBgNVBAoMD1NTTCBDb3Jwb3JhdGlvbjExMC8GA1UEAwwoU1NMLmNv
bSBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IEVDQzB2MBAGByqGSM49AgEGBSuBBAAiA2IA
BEVuqVDEpiM2nl8ojRfLliJkP9x6jh3MCLOicSS6jkm5BBtHllirLZXI7Z4INcgn64mMU1jrYor+
8FsPazFSY0E7ic3s7LaNGdM0B9y7xgZ/wkWV7Mt/qCPgCemB+vNH06NjMGEwHQYDVR0OBBYEFILR
hXMw5zUE044CkvvlpNHEIejNMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUgtGFczDnNQTT
jgKS++Wk0cQh6M0wDgYDVR0PAQH/BAQDAgGGMAoGCCqGSM49BAMCA2cAMGQCMG/n61kRpGDPYbCW
e+0F+S8Tkdzt5fxQaxFGRrMcIQBiu77D5+jNB5n5DQtdcj7EqgIwH7y6C+IwJPt8bYBVCpk+gA0z
5Wajs6O7pdWLjwkspl1+4vAHCGht0nxpbl/f5Wpl
-----END CERTIFICATE-----

SSL.com EV Root Certification Authority RSA R2
==============================================
-----BEGIN CERTIFICATE-----
MIIF6zCCA9OgAwIBAgIIVrYpzTS8ePYwDQYJKoZIhvcNAQELBQAwgYIxCzAJBgNVBAYTAlVTMQ4w
DAYDVQQIDAVUZXhhczEQMA4GA1UEBwwHSG91c3RvbjEYMBYGA1UECgwPU1NMIENvcnBvcmF0aW9u
MTcwNQYDVQQDDC5TU0wuY29tIEVWIFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgUlNBIFIy
MB4XDTE3MDUzMTE4MTQzN1oXDTQyMDUzMDE4MTQzN1owgYIxCzAJBgNVBAYTAlVTMQ4wDAYDVQQI
DAVUZXhhczEQMA4GA1UEBwwHSG91c3RvbjEYMBYGA1UECgwPU1NMIENvcnBvcmF0aW9uMTcwNQYD
VQQDDC5TU0wuY29tIEVWIFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgUlNBIFIyMIICIjAN
BgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAjzZlQOHWTcDXtOlG2mvqM0fNTPl9fb69LT3w23jh
hqXZuglXaO1XPqDQCEGD5yhBJB/jchXQARr7XnAjssufOePPxU7Gkm0mxnu7s9onnQqG6YE3Bf7w
cXHswxzpY6IXFJ3vG2fThVUCAtZJycxa4bH3bzKfydQ7iEGonL3Lq9ttewkfokxykNorCPzPPFTO
Zw+oz12WGQvE43LrrdF9HSfvkusQv1vrO6/PgN3B0pYEW3p+pKk8OHakYo6gOV7qd89dAFmPZiw+
B6KjBSYRaZfqhbcPlgtLyEDhULouisv3D5oi53+aNxPN8k0TayHRwMwi8qFG9kRpnMphNQcAb9Zh
CBHqurj26bNg5U257J8UZslXWNvNh2n4ioYSA0e/ZhN2rHd9NCSFg83XqpyQGp8hLH94t2S42Oim
9HizVcuE0jLEeK6jj2HdzghTreyI/BXkmg3mnxp3zkyPuBQVPWKchjgGAGYS5Fl2WlPAApiiECto
RHuOec4zSnaqW4EWG7WK2NAAe15itAnWhmMOpgWVSbooi4iTsjQc2KRVbrcc0N6ZVTsj9CLg+Slm
JuwgUHfbSguPvuUCYHBBXtSuUDkiFCbLsjtzdFVHB3mBOagwE0TlBIqulhMlQg+5U8Sb/M3kHN48
+qvWBkofZ6aYMBzdLNvcGJVXZsb/XItW9XcCAwEAAaNjMGEwDwYDVR0TAQH/BAUwAwEB/zAfBgNV
HSMEGDAWgBT5YLvU49U09rj1BoAlp3PbRmmonjAdBgNVHQ4EFgQU+WC71OPVNPa49QaAJadz20Zp
qJ4wDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEBCwUAA4ICAQBWs47LCp1Jjr+kxJG7ZhcFUZh1
++VQLHqe8RT6q9OKPv+RKY9ji9i0qVQBDb6Thi/5Sm3HXvVX+cpVHBK+Rw82xd9qt9t1wkclf7nx
Y/hoLVUE0fKNsKTPvDxeH3jnpaAgcLAExbf3cqfeIg29MyVGjGSSJuM+LmOW2puMPfgYCdcDzH2G
guDKBAdRUNf/ktUM79qGn5nX67evaOI5JpS6aLe/g9Pqemc9YmeuJeVy6OLk7K4S9ksrPJ/psEDz
OFSz/bdoyNrGj1E8svuR3Bznm53htw1yj+KkxKl4+esUrMZDBcJlOSgYAsOCsp0FvmXtll9ldDz7
CTUue5wT/RsPXcdtgTpWD8w74a8CLyKsRspGPKAcTNZEtF4uXBVmCeEmKf7GUmG6sXP/wwyc5Wxq
lD8UykAWlYTzWamsX0xhk23RO8yilQwipmdnRC652dKKQbNmC1r7fSOl8hqw/96bg5Qu0T/fkreR
rwU7ZcegbLHNYhLDkBvjJc40vG93drEQw/cFGsDWr3RiSBd3kmmQYRzelYB0VI8YHMPzA9C/pEN1
hlMYegouCRw2n5H9gooiS9EOUCXdywMMF8mDAAhONU2Ki+3wApRmLER/y5UnlhetCTCstnEXbosX
9hwJ1C07mKVx01QT2WDz9UtmT/rx7iASjbSsV7FFY6GsdqnC+w==
-----END CERTIFICATE-----

SSL.com EV Root Certification Authority ECC
===========================================
-----BEGIN CERTIFICATE-----
MIIClDCCAhqgAwIBAgIILCmcWxbtBZUwCgYIKoZIzj0EAwIwfzELMAkGA1UEBhMCVVMxDjAMBgNV
BAgMBVRleGFzMRAwDgYDVQQHDAdIb3VzdG9uMRgwFgYDVQQKDA9TU0wgQ29ycG9yYXRpb24xNDAy
BgNVBAMMK1NTTC5jb20gRVYgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSBFQ0MwHhcNMTYw
MjEyMTgxNTIzWhcNNDEwMjEyMTgxNTIzWjB/MQswCQYDVQQGEwJVUzEOMAwGA1UECAwFVGV4YXMx
EDAOBgNVBAcMB0hvdXN0b24xGDAWBgNVBAoMD1NTTCBDb3Jwb3JhdGlvbjE0MDIGA1UEAwwrU1NM
LmNvbSBFViBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IEVDQzB2MBAGByqGSM49AgEGBSuB
BAAiA2IABKoSR5CYG/vvw0AHgyBO8TCCogbR8pKGYfL2IWjKAMTH6kMAVIbc/R/fALhBYlzccBYy
3h+Z1MzFB8gIH2EWB1E9fVwHU+M1OIzfzZ/ZLg1KthkuWnBaBu2+8KGwytAJKaNjMGEwHQYDVR0O
BBYEFFvKXuXe0oGqzagtZFG22XKbl+ZPMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUW8pe
5d7SgarNqC1kUbbZcpuX5k8wDgYDVR0PAQH/BAQDAgGGMAoGCCqGSM49BAMCA2gAMGUCMQCK5kCJ
N+vp1RPZytRrJPOwPYdGWBrssd9v+1a6cGvHOMzosYxPD/fxZ3YOg9AeUY8CMD32IygmTMZgh5Mm
m7I1HrrW9zzRHM76JTymGoEVW/MSD2zuZYrJh6j5B+BimoxcSg==
-----END CERTIFICATE-----

GlobalSign Root CA - R6
=======================
-----BEGIN CERTIFICATE-----
MIIFgzCCA2ugAwIBAgIORea7A4Mzw4VlSOb/RVEwDQYJKoZIhvcNAQEMBQAwTDEgMB4GA1UECxMX
R2xvYmFsU2lnbiBSb290IENBIC0gUjYxEzARBgNVBAoTCkdsb2JhbFNpZ24xEzARBgNVBAMTCkds
b2JhbFNpZ24wHhcNMTQxMjEwMDAwMDAwWhcNMzQxMjEwMDAwMDAwWjBMMSAwHgYDVQQLExdHbG9i
YWxTaWduIFJvb3QgQ0EgLSBSNjETMBEGA1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFs
U2lnbjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAJUH6HPKZvnsFMp7PPcNCPG0RQss
grRIxutbPK6DuEGSMxSkb3/pKszGsIhrxbaJ0cay/xTOURQh7ErdG1rG1ofuTToVBu1kZguSgMpE
3nOUTvOniX9PeGMIyBJQbUJmL025eShNUhqKGoC3GYEOfsSKvGRMIRxDaNc9PIrFsmbVkJq3MQbF
vuJtMgamHvm566qjuL++gmNQ0PAYid/kD3n16qIfKtJwLnvnvJO7bVPiSHyMEAc4/2ayd2F+4OqM
PKq0pPbzlUoSB239jLKJz9CgYXfIWHSw1CM69106yqLbnQneXUQtkPGBzVeS+n68UARjNN9rkxi+
azayOeSsJDa38O+2HBNXk7besvjihbdzorg1qkXy4J02oW9UivFyVm4uiMVRQkQVlO6jxTiWm05O
WgtH8wY2SXcwvHE35absIQh1/OZhFj931dmRl4QKbNQCTXTAFO39OfuD8l4UoQSwC+n+7o/hbguy
CLNhZglqsQY6ZZZZwPA1/cnaKI0aEYdwgQqomnUdnjqGBQCe24DWJfncBZ4nWUx2OVvq+aWh2IMP
0f/fMBH5hc8zSPXKbWQULHpYT9NLCEnFlWQaYw55PfWzjMpYrZxCRXluDocZXFSxZba/jJvcE+kN
b7gu3GduyYsRtYQUigAZcIN5kZeR1BonvzceMgfYFGM8KEyvAgMBAAGjYzBhMA4GA1UdDwEB/wQE
AwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBSubAWjkxPioufi1xzWx/B/yGdToDAfBgNV
HSMEGDAWgBSubAWjkxPioufi1xzWx/B/yGdToDANBgkqhkiG9w0BAQwFAAOCAgEAgyXt6NH9lVLN
nsAEoJFp5lzQhN7craJP6Ed41mWYqVuoPId8AorRbrcWc+ZfwFSY1XS+wc3iEZGtIxg93eFyRJa0
lV7Ae46ZeBZDE1ZXs6KzO7V33EByrKPrmzU+sQghoefEQzd5Mr6155wsTLxDKZmOMNOsIeDjHfrY
BzN2VAAiKrlNIC5waNrlU/yDXNOd8v9EDERm8tLjvUYAGm0CuiVdjaExUd1URhxN25mW7xocBFym
Fe944Hn+Xds+qkxV/ZoVqW/hpvvfcDDpw+5CRu3CkwWJ+n1jez/QcYF8AOiYrg54NMMl+68KnyBr
3TsTjxKM4kEaSHpzoHdpx7Zcf4LIHv5YGygrqGytXm3ABdJ7t+uA/iU3/gKbaKxCXcPu9czc8FB1
0jZpnOZ7BN9uBmm23goJSFmH63sUYHpkqmlD75HHTOwY3WzvUy2MmeFe8nI+z1TIvWfspA9MRf/T
uTAjB0yPEL+GltmZWrSZVxykzLsViVO6LAUP5MSeGbEYNNVMnbrt9x+vJJUEeKgDu+6B5dpffItK
oZB0JaezPkvILFa9x8jvOOJckvB595yEunQtYQEgfn7R8k8HWV+LLUNS60YMlOH1Zkd5d9VUWx+t
JDfLRVpOoERIyNiwmcUVhAn21klJwGW45hpxbqCo8YLoRT5s1gLXCmeDBVrJpBA=
-----END CERTIFICATE-----

OISTE WISeKey Global Root GC CA
===============================
-----BEGIN CERTIFICATE-----
MIICaTCCAe+gAwIBAgIQISpWDK7aDKtARb8roi066jAKBggqhkjOPQQDAzBtMQswCQYDVQQGEwJD
SDEQMA4GA1UEChMHV0lTZUtleTEiMCAGA1UECxMZT0lTVEUgRm91bmRhdGlvbiBFbmRvcnNlZDEo
MCYGA1UEAxMfT0lTVEUgV0lTZUtleSBHbG9iYWwgUm9vdCBHQyBDQTAeFw0xNzA1MDkwOTQ4MzRa
Fw00MjA1MDkwOTU4MzNaMG0xCzAJBgNVBAYTAkNIMRAwDgYDVQQKEwdXSVNlS2V5MSIwIAYDVQQL
ExlPSVNURSBGb3VuZGF0aW9uIEVuZG9yc2VkMSgwJgYDVQQDEx9PSVNURSBXSVNlS2V5IEdsb2Jh
bCBSb290IEdDIENBMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAETOlQwMYPchi82PG6s4nieUqjFqdr
VCTbUf/q9Akkwwsin8tqJ4KBDdLArzHkdIJuyiXZjHWd8dvQmqJLIX4Wp2OQ0jnUsYd4XxiWD1Ab
NTcPasbc2RNNpI6QN+a9WzGRo1QwUjAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAd
BgNVHQ4EFgQUSIcUrOPDnpBgOtfKie7TrYy0UGYwEAYJKwYBBAGCNxUBBAMCAQAwCgYIKoZIzj0E
AwMDaAAwZQIwJsdpW9zV57LnyAyMjMPdeYwbY9XJUpROTYJKcx6ygISpJcBMWm1JKWB4E+J+SOtk
AjEA2zQgMgj/mkkCtojeFK9dbJlxjRo/i9fgojaGHAeCOnZT/cKi7e97sIBPWA9LUzm9
-----END CERTIFICATE-----

UCA Global G2 Root
==================
-----BEGIN CERTIFICATE-----
MIIFRjCCAy6gAwIBAgIQXd+x2lqj7V2+WmUgZQOQ7zANBgkqhkiG9w0BAQsFADA9MQswCQYDVQQG
EwJDTjERMA8GA1UECgwIVW5pVHJ1c3QxGzAZBgNVBAMMElVDQSBHbG9iYWwgRzIgUm9vdDAeFw0x
NjAzMTEwMDAwMDBaFw00MDEyMzEwMDAwMDBaMD0xCzAJBgNVBAYTAkNOMREwDwYDVQQKDAhVbmlU
cnVzdDEbMBkGA1UEAwwSVUNBIEdsb2JhbCBHMiBSb290MIICIjANBgkqhkiG9w0BAQEFAAOCAg8A
MIICCgKCAgEAxeYrb3zvJgUno4Ek2m/LAfmZmqkywiKHYUGRO8vDaBsGxUypK8FnFyIdK+35KYmT
oni9kmugow2ifsqTs6bRjDXVdfkX9s9FxeV67HeToI8jrg4aA3++1NDtLnurRiNb/yzmVHqUwCoV
8MmNsHo7JOHXaOIxPAYzRrZUEaalLyJUKlgNAQLx+hVRZ2zA+te2G3/RVogvGjqNO7uCEeBHANBS
h6v7hn4PJGtAnTRnvI3HLYZveT6OqTwXS3+wmeOwcWDcC/Vkw85DvG1xudLeJ1uK6NjGruFZfc8o
LTW4lVYa8bJYS7cSN8h8s+1LgOGN+jIjtm+3SJUIsUROhYw6AlQgL9+/V087OpAh18EmNVQg7Mc/
R+zvWr9LesGtOxdQXGLYD0tK3Cv6brxzks3sx1DoQZbXqX5t2Okdj4q1uViSukqSKwxW/YDrCPBe
KW4bHAyvj5OJrdu9o54hyokZ7N+1wxrrFv54NkzWbtA+FxyQF2smuvt6L78RHBgOLXMDj6DlNaBa
4kx1HXHhOThTeEDMg5PXCp6dW4+K5OXgSORIskfNTip1KnvyIvbJvgmRlld6iIis7nCs+dwp4wwc
OxJORNanTrAmyPPZGpeRaOrvjUYG0lZFWJo8DA+DuAUlwznPO6Q0ibd5Ei9Hxeepl2n8pndntd97
8XplFeRhVmUCAwEAAaNCMEAwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0O
BBYEFIHEjMz15DD/pQwIX4wVZyF0Ad/fMA0GCSqGSIb3DQEBCwUAA4ICAQATZSL1jiutROTL/7lo
5sOASD0Ee/ojL3rtNtqyzm325p7lX1iPyzcyochltq44PTUbPrw7tgTQvPlJ9Zv3hcU2tsu8+Mg5
1eRfB70VVJd0ysrtT7q6ZHafgbiERUlMjW+i67HM0cOU2kTC5uLqGOiiHycFutfl1qnN3e92mI0A
Ds0b+gO3joBYDic/UvuUospeZcnWhNq5NXHzJsBPd+aBJ9J3O5oUb3n09tDh05S60FdRvScFDcH9
yBIw7m+NESsIndTUv4BFFJqIRNow6rSn4+7vW4LVPtateJLbXDzz2K36uGt/xDYotgIVilQsnLAX
c47QN6MUPJiVAAwpBVueSUmxX8fjy88nZY41F7dXyDDZQVu5FLbowg+UMaeUmMxq67XhJ/UQqAHo
jhJi6IjMtX9Gl8CbEGY4GjZGXyJoPd/JxhMnq1MGrKI8hgZlb7F+sSlEmqO6SWkoaY/X5V+tBIZk
bxqgDMUIYs6Ao9Dz7GjevjPHF1t/gMRMTLGmhIrDO7gJzRSBuhjjVFc2/tsvfEehOjPI+Vg7RE+x
ygKJBJYoaMVLuCaJu9YzL1DV/pqJuhgyklTGW+Cd+V7lDSKb9triyCGyYiGqhkCyLmTTX8jjfhFn
RR8F/uOi77Oos/N9j/gMHyIfLXC0uAE0djAA5SN4p1bXUB+K+wb1whnw0A==
-----END CERTIFICATE-----

UCA Extended Validation Root
============================
-----BEGIN CERTIFICATE-----
MIIFWjCCA0KgAwIBAgIQT9Irj/VkyDOeTzRYZiNwYDANBgkqhkiG9w0BAQsFADBHMQswCQYDVQQG
EwJDTjERMA8GA1UECgwIVW5pVHJ1c3QxJTAjBgNVBAMMHFVDQSBFeHRlbmRlZCBWYWxpZGF0aW9u
IFJvb3QwHhcNMTUwMzEzMDAwMDAwWhcNMzgxMjMxMDAwMDAwWjBHMQswCQYDVQQGEwJDTjERMA8G
A1UECgwIVW5pVHJ1c3QxJTAjBgNVBAMMHFVDQSBFeHRlbmRlZCBWYWxpZGF0aW9uIFJvb3QwggIi
MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCpCQcoEwKwmeBkqh5DFnpzsZGgdT6o+uM4AHrs
iWogD4vFsJszA1qGxliG1cGFu0/GnEBNyr7uaZa4rYEwmnySBesFK5pI0Lh2PpbIILvSsPGP2KxF
Rv+qZ2C0d35qHzwaUnoEPQc8hQ2E0B92CvdqFN9y4zR8V05WAT558aopO2z6+I9tTcg1367r3CTu
eUWnhbYFiN6IXSV8l2RnCdm/WhUFhvMJHuxYMjMR83dksHYf5BA1FxvyDrFspCqjc/wJHx4yGVMR
59mzLC52LqGj3n5qiAno8geK+LLNEOfic0CTuwjRP+H8C5SzJe98ptfRr5//lpr1kXuYC3fUfugH
0mK1lTnj8/FtDw5lhIpjVMWAtuCeS31HJqcBCF3RiJ7XwzJE+oJKCmhUfzhTA8ykADNkUVkLo4KR
el7sFsLzKuZi2irbWWIQJUoqgQtHB0MGcIfS+pMRKXpITeuUx3BNr2fVUbGAIAEBtHoIppB/TuDv
B0GHr2qlXov7z1CymlSvw4m6WC31MJixNnI5fkkE/SmnTHnkBVfblLkWU41Gsx2VYVdWf6/wFlth
WG82UBEL2KwrlRYaDh8IzTY0ZRBiZtWAXxQgXy0MoHgKaNYs1+lvK9JKBZP8nm9rZ/+I8U6laUpS
NwXqxhaN0sSZ0YIrO7o1dfdRUVjzyAfd5LQDfwIDAQABo0IwQDAdBgNVHQ4EFgQU2XQ65DA9DfcS
3H5aBZ8eNJr34RQwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAYYwDQYJKoZIhvcNAQEL
BQADggIBADaNl8xCFWQpN5smLNb7rhVpLGsaGvdftvkHTFnq88nIua7Mui563MD1sC3AO6+fcAUR
ap8lTwEpcOPlDOHqWnzcSbvBHiqB9RZLcpHIojG5qtr8nR/zXUACE/xOHAbKsxSQVBcZEhrxH9cM
aVr2cXj0lH2RC47skFSOvG+hTKv8dGT9cZr4QQehzZHkPJrgmzI5c6sq1WnIeJEmMX3ixzDx/BR4
dxIOE/TdFpS/S2d7cFOFyrC78zhNLJA5wA3CXWvp4uXViI3WLL+rG761KIcSF3Ru/H38j9CHJrAb
+7lsq+KePRXBOy5nAliRn+/4Qh8st2j1da3Ptfb/EX3C8CSlrdP6oDyp+l3cpaDvRKS+1ujl5BOW
F3sGPjLtx7dCvHaj2GU4Kzg1USEODm8uNBNA4StnDG1KQTAYI1oyVZnJF+A83vbsea0rWBmirSwi
GpWOvpaQXUJXxPkUAzUrHC1RVwinOt4/5Mi0A3PCwSaAuwtCH60NryZy2sy+s6ODWA2CxR9GUeOc
GMyNm43sSet1UNWMKFnKdDTajAshqx7qG+XH/RU+wBeq+yNuJkbL+vmxcmtpzyKEC2IPrNkZAJSi
djzULZrtBJ4tBmIQN1IchXIbJ+XMxjHsN+xjWZsLHXbMfjKaiJUINlK73nZfdklJrX+9ZSCyycEr
dhh2n1ax
-----END CERTIFICATE-----

Certigna Root CA
================
-----BEGIN CERTIFICATE-----
MIIGWzCCBEOgAwIBAgIRAMrpG4nxVQMNo+ZBbcTjpuEwDQYJKoZIhvcNAQELBQAwWjELMAkGA1UE
BhMCRlIxEjAQBgNVBAoMCURoaW15b3RpczEcMBoGA1UECwwTMDAwMiA0ODE0NjMwODEwMDAzNjEZ
MBcGA1UEAwwQQ2VydGlnbmEgUm9vdCBDQTAeFw0xMzEwMDEwODMyMjdaFw0zMzEwMDEwODMyMjda
MFoxCzAJBgNVBAYTAkZSMRIwEAYDVQQKDAlEaGlteW90aXMxHDAaBgNVBAsMEzAwMDIgNDgxNDYz
MDgxMDAwMzYxGTAXBgNVBAMMEENlcnRpZ25hIFJvb3QgQ0EwggIiMA0GCSqGSIb3DQEBAQUAA4IC
DwAwggIKAoICAQDNGDllGlmx6mQWDoyUJJV8g9PFOSbcDO8WV43X2KyjQn+Cyu3NW9sOty3tRQgX
stmzy9YXUnIo245Onoq2C/mehJpNdt4iKVzSs9IGPjA5qXSjklYcoW9MCiBtnyN6tMbaLOQdLNyz
KNAT8kxOAkmhVECe5uUFoC2EyP+YbNDrihqECB63aCPuI9Vwzm1RaRDuoXrC0SIxwoKF0vJVdlB8
JXrJhFwLrN1CTivngqIkicuQstDuI7pmTLtipPlTWmR7fJj6o0ieD5Wupxj0auwuA0Wv8HT4Ks16
XdG+RCYyKfHx9WzMfgIhC59vpD++nVPiz32pLHxYGpfhPTc3GGYo0kDFUYqMwy3OU4gkWGQwFsWq
4NYKpkDfePb1BHxpE4S80dGnBs8B92jAqFe7OmGtBIyT46388NtEbVncSVmurJqZNjBBe3YzIoej
wpKGbvlw7q6Hh5UbxHq9MfPU0uWZ/75I7HX1eBYdpnDBfzwboZL7z8g81sWTCo/1VTp2lc5ZmIoJ
lXcymoO6LAQ6l73UL77XbJuiyn1tJslV1c/DeVIICZkHJC1kJWumIWmbat10TWuXekG9qxf5kBdI
jzb5LdXF2+6qhUVB+s06RbFo5jZMm5BX7CO5hwjCxAnxl4YqKE3idMDaxIzb3+KhF1nOJFl0Mdp/
/TBt2dzhauH8XwIDAQABo4IBGjCCARYwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYw
HQYDVR0OBBYEFBiHVuBud+4kNTxOc5of1uHieX4rMB8GA1UdIwQYMBaAFBiHVuBud+4kNTxOc5of
1uHieX4rMEQGA1UdIAQ9MDswOQYEVR0gADAxMC8GCCsGAQUFBwIBFiNodHRwczovL3d3d3cuY2Vy
dGlnbmEuZnIvYXV0b3JpdGVzLzBtBgNVHR8EZjBkMC+gLaArhilodHRwOi8vY3JsLmNlcnRpZ25h
LmZyL2NlcnRpZ25hcm9vdGNhLmNybDAxoC+gLYYraHR0cDovL2NybC5kaGlteW90aXMuY29tL2Nl
cnRpZ25hcm9vdGNhLmNybDANBgkqhkiG9w0BAQsFAAOCAgEAlLieT/DjlQgi581oQfccVdV8AOIt
OoldaDgvUSILSo3L6btdPrtcPbEo/uRTVRPPoZAbAh1fZkYJMyjhDSSXcNMQH+pkV5a7XdrnxIxP
TGRGHVyH41neQtGbqH6mid2PHMkwgu07nM3A6RngatgCdTer9zQoKJHyBApPNeNgJgH60BGM+RFq
7q89w1DTj18zeTyGqHNFkIwgtnJzFyO+B2XleJINugHA64wcZr+shncBlA2c5uk5jR+mUYyZDDl3
4bSb+hxnV29qao6pK0xXeXpXIs/NX2NGjVxZOob4Mkdio2cNGJHc+6Zr9UhhcyNZjgKnvETq9Emd
8VRY+WCv2hikLyhF3HqgiIZd8zvn/yk1gPxkQ5Tm4xxvvq0OKmOZK8l+hfZx6AYDlf7ej0gcWtSS
6Cvu5zHbugRqh5jnxV/vfaci9wHYTfmJ0A6aBVmknpjZbyvKcL5kwlWj9Omvw5Ip3IgWJJk8jSaY
tlu3zM63Nwf9JtmYhST/WSMDmu2dnajkXjjO11INb9I/bbEFa0nOipFGc/T2L/Coc3cOZayhjWZS
aX5LaAzHHjcng6WMxwLkFM1JAbBzs/3GkDpv0mztO+7skb6iQ12LAEpmJURw3kAP+HwV96LOPNde
E4yBFxgX0b3xdxA61GU5wSesVywlVP+i2k+KYTlerj1KjL0=
-----END CERTIFICATE-----

emSign Root CA - G1
===================
-----BEGIN CERTIFICATE-----
MIIDlDCCAnygAwIBAgIKMfXkYgxsWO3W2DANBgkqhkiG9w0BAQsFADBnMQswCQYDVQQGEwJJTjET
MBEGA1UECxMKZW1TaWduIFBLSTElMCMGA1UEChMcZU11ZGhyYSBUZWNobm9sb2dpZXMgTGltaXRl
ZDEcMBoGA1UEAxMTZW1TaWduIFJvb3QgQ0EgLSBHMTAeFw0xODAyMTgxODMwMDBaFw00MzAyMTgx
ODMwMDBaMGcxCzAJBgNVBAYTAklOMRMwEQYDVQQLEwplbVNpZ24gUEtJMSUwIwYDVQQKExxlTXVk
aHJhIFRlY2hub2xvZ2llcyBMaW1pdGVkMRwwGgYDVQQDExNlbVNpZ24gUm9vdCBDQSAtIEcxMIIB
IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAk0u76WaK7p1b1TST0Bsew+eeuGQzf2N4aLTN
LnF115sgxk0pvLZoYIr3IZpWNVrzdr3YzZr/k1ZLpVkGoZM0Kd0WNHVO8oG0x5ZOrRkVUkr+PHB1
cM2vK6sVmjM8qrOLqs1D/fXqcP/tzxE7lM5OMhbTI0Aqd7OvPAEsbO2ZLIvZTmmYsvePQbAyeGHW
DV/D+qJAkh1cF+ZwPjXnorfCYuKrpDhMtTk1b+oDafo6VGiFbdbyL0NVHpENDtjVaqSW0RM8LHhQ
6DqS0hdW5TUaQBw+jSztOd9C4INBdN+jzcKGYEho42kLVACL5HZpIQ15TjQIXhTCzLG3rdd8cIrH
hQIDAQABo0IwQDAdBgNVHQ4EFgQU++8Nhp6w492pufEhF38+/PB3KxowDgYDVR0PAQH/BAQDAgEG
MA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAFn/8oz1h31xPaOfG1vR2vjTnGs2
vZupYeveFix0PZ7mddrXuqe8QhfnPZHr5X3dPpzxz5KsbEjMwiI/aTvFthUvozXGaCocV685743Q
NcMYDHsAVhzNixl03r4PEuDQqqE/AjSxcM6dGNYIAwlG7mDgfrbESQRRfXBgvKqy/3lyeqYdPV8q
+Mri/Tm3R7nrft8EI6/6nAYH6ftjk4BAtcZsCjEozgyfz7MjNYBBjWzEN3uBL4ChQEKF6dk4jeih
U80Bv2noWgbyRQuQ+q7hv53yrlc8pa6yVvSLZUDp/TGBLPQ5Cdjua6e0ph0VpZj3AYHYhX3zUVxx
iN66zB+Afko=
-----END CERTIFICATE-----

emSign ECC Root CA - G3
=======================
-----BEGIN CERTIFICATE-----
MIICTjCCAdOgAwIBAgIKPPYHqWhwDtqLhDAKBggqhkjOPQQDAzBrMQswCQYDVQQGEwJJTjETMBEG
A1UECxMKZW1TaWduIFBLSTElMCMGA1UEChMcZU11ZGhyYSBUZWNobm9sb2dpZXMgTGltaXRlZDEg
MB4GA1UEAxMXZW1TaWduIEVDQyBSb290IENBIC0gRzMwHhcNMTgwMjE4MTgzMDAwWhcNNDMwMjE4
MTgzMDAwWjBrMQswCQYDVQQGEwJJTjETMBEGA1UECxMKZW1TaWduIFBLSTElMCMGA1UEChMcZU11
ZGhyYSBUZWNobm9sb2dpZXMgTGltaXRlZDEgMB4GA1UEAxMXZW1TaWduIEVDQyBSb290IENBIC0g
RzMwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQjpQy4LRL1KPOxst3iAhKAnjlfSU2fySU0WXTsuwYc
58Byr+iuL+FBVIcUqEqy6HyC5ltqtdyzdc6LBtCGI79G1Y4PPwT01xySfvalY8L1X44uT6EYGQIr
MgqCZH0Wk9GjQjBAMB0GA1UdDgQWBBR8XQKEE9TMipuBzhccLikenEhjQjAOBgNVHQ8BAf8EBAMC
AQYwDwYDVR0TAQH/BAUwAwEB/zAKBggqhkjOPQQDAwNpADBmAjEAvvNhzwIQHWSVB7gYboiFBS+D
CBeQyh+KTOgNG3qxrdWBCUfvO6wIBHxcmbHtRwfSAjEAnbpV/KlK6O3t5nYBQnvI+GDZjVGLVTv7
jHvrZQnD+JbNR6iC8hZVdyR+EhCVBCyj
-----END CERTIFICATE-----

emSign Root CA - C1
===================
-----BEGIN CERTIFICATE-----
MIIDczCCAlugAwIBAgILAK7PALrEzzL4Q7IwDQYJKoZIhvcNAQELBQAwVjELMAkGA1UEBhMCVVMx
EzARBgNVBAsTCmVtU2lnbiBQS0kxFDASBgNVBAoTC2VNdWRocmEgSW5jMRwwGgYDVQQDExNlbVNp
Z24gUm9vdCBDQSAtIEMxMB4XDTE4MDIxODE4MzAwMFoXDTQzMDIxODE4MzAwMFowVjELMAkGA1UE
BhMCVVMxEzARBgNVBAsTCmVtU2lnbiBQS0kxFDASBgNVBAoTC2VNdWRocmEgSW5jMRwwGgYDVQQD
ExNlbVNpZ24gUm9vdCBDQSAtIEMxMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAz+up
ufGZBczYKCFK83M0UYRWEPWgTywS4/oTmifQz/l5GnRfHXk5/Fv4cI7gklL35CX5VIPZHdPIWoU/
Xse2B+4+wM6ar6xWQio5JXDWv7V7Nq2s9nPczdcdioOl+yuQFTdrHCZH3DspVpNqs8FqOp099cGX
OFgFixwR4+S0uF2FHYP+eF8LRWgYSKVGczQ7/g/IdrvHGPMF0Ybzhe3nudkyrVWIzqa2kbBPrH4V
I5b2P/AgNBbeCsbEBEV5f6f9vtKppa+cxSMq9zwhbL2vj07FOrLzNBL834AaSaTUqZX3noleooms
lMuoaJuvimUnzYnu3Yy1aylwQ6BpC+S5DwIDAQABo0IwQDAdBgNVHQ4EFgQU/qHgcB4qAzlSWkK+
XJGFehiqTbUwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQAD
ggEBAMJKVvoVIXsoounlHfv4LcQ5lkFMOycsxGwYFYDGrK9HWS8mC+M2sO87/kOXSTKZEhVb3xEp
/6tT+LvBeA+snFOvV71ojD1pM/CjoCNjO2RnIkSt1XHLVip4kqNPEjE2NuLe/gDEo2APJ62gsIq1
NnpSob0n9CAnYuhNlCQT5AoE6TyrLshDCUrGYQTlSTR+08TI9Q/Aqum6VF7zYytPT1DU/rl7mYw9
wC68AivTxEDkigcxHpvOJpkT+xHqmiIMERnHXhuBUDDIlhJu58tBf5E7oke3VIAb3ADMmpDqw8NQ
BmIMMMAVSKeoWXzhriKi4gp6D/piq1JM4fHfyr6DDUI=
-----END CERTIFICATE-----

emSign ECC Root CA - C3
=======================
-----BEGIN CERTIFICATE-----
MIICKzCCAbGgAwIBAgIKe3G2gla4EnycqDAKBggqhkjOPQQDAzBaMQswCQYDVQQGEwJVUzETMBEG
A1UECxMKZW1TaWduIFBLSTEUMBIGA1UEChMLZU11ZGhyYSBJbmMxIDAeBgNVBAMTF2VtU2lnbiBF
Q0MgUm9vdCBDQSAtIEMzMB4XDTE4MDIxODE4MzAwMFoXDTQzMDIxODE4MzAwMFowWjELMAkGA1UE
BhMCVVMxEzARBgNVBAsTCmVtU2lnbiBQS0kxFDASBgNVBAoTC2VNdWRocmEgSW5jMSAwHgYDVQQD
ExdlbVNpZ24gRUNDIFJvb3QgQ0EgLSBDMzB2MBAGByqGSM49AgEGBSuBBAAiA2IABP2lYa57JhAd
6bciMK4G9IGzsUJxlTm801Ljr6/58pc1kjZGDoeVjbk5Wum739D+yAdBPLtVb4OjavtisIGJAnB9
SMVK4+kiVCJNk7tCDK93nCOmfddhEc5lx/h//vXyqaNCMEAwHQYDVR0OBBYEFPtaSNCAIEDyqOkA
B2kZd6fmw/TPMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MAoGCCqGSM49BAMDA2gA
MGUCMQC02C8Cif22TGK6Q04ThHK1rt0c3ta13FaPWEBaLd4gTCKDypOofu4SQMfWh0/434UCMBwU
ZOR8loMRnLDRWmFLpg9J0wD8ofzkpf9/rdcw0Md3f76BB1UwUCAU9Vc4CqgxUQ==
-----END CERTIFICATE-----

Hongkong Post Root CA 3
=======================
-----BEGIN CERTIFICATE-----
MIIFzzCCA7egAwIBAgIUCBZfikyl7ADJk0DfxMauI7gcWqQwDQYJKoZIhvcNAQELBQAwbzELMAkG
A1UEBhMCSEsxEjAQBgNVBAgTCUhvbmcgS29uZzESMBAGA1UEBxMJSG9uZyBLb25nMRYwFAYDVQQK
Ew1Ib25na29uZyBQb3N0MSAwHgYDVQQDExdIb25na29uZyBQb3N0IFJvb3QgQ0EgMzAeFw0xNzA2
MDMwMjI5NDZaFw00MjA2MDMwMjI5NDZaMG8xCzAJBgNVBAYTAkhLMRIwEAYDVQQIEwlIb25nIEtv
bmcxEjAQBgNVBAcTCUhvbmcgS29uZzEWMBQGA1UEChMNSG9uZ2tvbmcgUG9zdDEgMB4GA1UEAxMX
SG9uZ2tvbmcgUG9zdCBSb290IENBIDMwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCz
iNfqzg8gTr7m1gNt7ln8wlffKWihgw4+aMdoWJwcYEuJQwy51BWy7sFOdem1p+/l6TWZ5Mwc50tf
jTMwIDNT2aa71T4Tjukfh0mtUC1Qyhi+AViiE3CWu4mIVoBc+L0sPOFMV4i707mV78vH9toxdCim
5lSJ9UExyuUmGs2C4HDaOym71QP1mbpV9WTRYA6ziUm4ii8F0oRFKHyPaFASePwLtVPLwpgchKOe
sL4jpNrcyCse2m5FHomY2vkALgbpDDtw1VAliJnLzXNg99X/NWfFobxeq81KuEXryGgeDQ0URhLj
0mRiikKYvLTGCAj4/ahMZJx2Ab0vqWwzD9g/KLg8aQFChn5pwckGyuV6RmXpwtZQQS4/t+TtbNe/
JgERohYpSms0BpDsE9K2+2p20jzt8NYt3eEV7KObLyzJPivkaTv/ciWxNoZbx39ri1UbSsUgYT2u
y1DhCDq+sI9jQVMwCFk8mB13umOResoQUGC/8Ne8lYePl8X+l2oBlKN8W4UdKjk60FSh0Tlxnf0h
+bV78OLgAo9uliQlLKAeLKjEiafv7ZkGL7YKTE/bosw3Gq9HhS2KX8Q0NEwA/RiTZxPRN+ZItIsG
xVd7GYYKecsAyVKvQv83j+GjHno9UKtjBucVtT+2RTeUN7F+8kjDf8V1/peNRY8apxpyKBpADwID
AQABo2MwYTAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAfBgNVHSMEGDAWgBQXnc0e
i9Y5K3DTXNSguB+wAPzFYTAdBgNVHQ4EFgQUF53NHovWOStw01zUoLgfsAD8xWEwDQYJKoZIhvcN
AQELBQADggIBAFbVe27mIgHSQpsY1Q7XZiNc4/6gx5LS6ZStS6LG7BJ8dNVI0lkUmcDrudHr9Egw
W62nV3OZqdPlt9EuWSRY3GguLmLYauRwCy0gUCCkMpXRAJi70/33MvJJrsZ64Ee+bs7Lo3I6LWld
y8joRTnU+kLBEUx3XZL7av9YROXrgZ6voJmtvqkBZss4HTzfQx/0TW60uhdG/H39h4F5ag0zD/ov
+BS5gLNdTaqX4fnkGMX41TiMJjz98iji7lpJiCzfeT2OnpA8vUFKOt1b9pq0zj8lMH8yfaIDlNDc
eqFS3m6TjRgm/VWsvY+b0s+v54Ysyx8Jb6NvqYTUc79NoXQbTiNg8swOqn+knEwlqLJmOzj/2ZQw
9nKEvmhVEA/GcywWaZMH/rFF7buiVWqw2rVKAiUnhde3t4ZEFolsgCs+l6mc1X5VTMbeRRAc6uk7
nwNT7u56AQIWeNTowr5GdogTPyK7SBIdUgC0An4hGh6cJfTzPV4e0hz5sy229zdcxsshTrD3mUcY
hcErulWuBurQB7Lcq9CClnXO0lD+mefPL5/ndtFhKvshuzHQqp9HpLIiyhY6UFfEW0NnxWViA0kB
60PZ2Pierc+xYw5F9KBaLJstxabArahH9CdMOA0uG0k7UvToiIMrVCjU8jVStDKDYmlkDJGcn5fq
dBb9HxEGmpv0
-----END CERTIFICATE-----

Entrust Root Certification Authority - G4
=========================================
-----BEGIN CERTIFICATE-----
MIIGSzCCBDOgAwIBAgIRANm1Q3+vqTkPAAAAAFVlrVgwDQYJKoZIhvcNAQELBQAwgb4xCzAJBgNV
BAYTAlVTMRYwFAYDVQQKEw1FbnRydXN0LCBJbmMuMSgwJgYDVQQLEx9TZWUgd3d3LmVudHJ1c3Qu
bmV0L2xlZ2FsLXRlcm1zMTkwNwYDVQQLEzAoYykgMjAxNSBFbnRydXN0LCBJbmMuIC0gZm9yIGF1
dGhvcml6ZWQgdXNlIG9ubHkxMjAwBgNVBAMTKUVudHJ1c3QgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1
dGhvcml0eSAtIEc0MB4XDTE1MDUyNzExMTExNloXDTM3MTIyNzExNDExNlowgb4xCzAJBgNVBAYT
AlVTMRYwFAYDVQQKEw1FbnRydXN0LCBJbmMuMSgwJgYDVQQLEx9TZWUgd3d3LmVudHJ1c3QubmV0
L2xlZ2FsLXRlcm1zMTkwNwYDVQQLEzAoYykgMjAxNSBFbnRydXN0LCBJbmMuIC0gZm9yIGF1dGhv
cml6ZWQgdXNlIG9ubHkxMjAwBgNVBAMTKUVudHJ1c3QgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhv
cml0eSAtIEc0MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAsewsQu7i0TD/pZJH4i3D
umSXbcr3DbVZwbPLqGgZ2K+EbTBwXX7zLtJTmeH+H17ZSK9dE43b/2MzTdMAArzE+NEGCJR5WIoV
3imz/f3ET+iq4qA7ec2/a0My3dl0ELn39GjUu9CH1apLiipvKgS1sqbHoHrmSKvS0VnM1n4j5pds
8ELl3FFLFUHtSUrJ3hCX1nbB76W1NhSXNdh4IjVS70O92yfbYVaCNNzLiGAMC1rlLAHGVK/XqsEQ
e9IFWrhAnoanw5CGAlZSCXqc0ieCU0plUmr1POeo8pyvi73TDtTUXm6Hnmo9RR3RXRv06QqsYJn7
ibT/mCzPfB3pAqoEmh643IhuJbNsZvc8kPNXwbMv9W3y+8qh+CmdRouzavbmZwe+LGcKKh9asj5X
xNMhIWNlUpEbsZmOeX7m640A2Vqq6nPopIICR5b+W45UYaPrL0swsIsjdXJ8ITzI9vF01Bx7owVV
7rtNOzK+mndmnqxpkCIHH2E6lr7lmk/MBTwoWdPBDFSoWWG9yHJM6Nyfh3+9nEg2XpWjDrk4JFX8
dWbrAuMINClKxuMrLzOg2qOGpRKX/YAr2hRC45K9PvJdXmd0LhyIRyk0X+IyqJwlN4y6mACXi0mW
Hv0liqzc2thddG5msP9E36EYxr5ILzeUePiVSj9/E15dWf10hkNjc0kCAwEAAaNCMEAwDwYDVR0T
AQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFJ84xFYjwznooHFs6FRM5Og6sb9n
MA0GCSqGSIb3DQEBCwUAA4ICAQAS5UKme4sPDORGpbZgQIeMJX6tuGguW8ZAdjwD+MlZ9POrYs4Q
jbRaZIxowLByQzTSGwv2LFPSypBLhmb8qoMi9IsabyZIrHZ3CL/FmFz0Jomee8O5ZDIBf9PD3Vht
7LGrhFV0d4QEJ1JrhkzO3bll/9bGXp+aEJlLdWr+aumXIOTkdnrG0CSqkM0gkLpHZPt/B7NTeLUK
YvJzQ85BK4FqLoUWlFPUa19yIqtRLULVAJyZv967lDtX/Zr1hstWO1uIAeV8KEsD+UmDfLJ/fOPt
jqF/YFOOVZ1QNBIPt5d7bIdKROf1beyAN/BYGW5KaHbwH5Lk6rWS02FREAutp9lfx1/cH6NcjKF+
m7ee01ZvZl4HliDtC3T7Zk6LERXpgUl+b7DUUH8i119lAg2m9IUe2K4GS0qn0jFmwvjO5QimpAKW
RGhXxNUzzxkvFMSUHHuk2fCfDrGA4tGeEWSpiBE6doLlYsKA2KSD7ZPvfC+QsDJMlhVoSFLUmQjA
JOgc47OlIQ6SwJAfzyBfyjs4x7dtOvPmRLgOMWuIjnDrnBdSqEGULoe256YSxXXfW8AKbnuk5F6G
+TaU33fD6Q3AOfF5u0aOq0NZJ7cguyPpVkAh7DE9ZapD8j3fcEThuk0mEDuYn/PIjhs4ViFqUZPT
kcpG2om3PVODLAgfi49T3f+sHw==
-----END CERTIFICATE-----

Microsoft ECC Root Certificate Authority 2017
=============================================
-----BEGIN CERTIFICATE-----
MIICWTCCAd+gAwIBAgIQZvI9r4fei7FK6gxXMQHC7DAKBggqhkjOPQQDAzBlMQswCQYDVQQGEwJV
UzEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMTYwNAYDVQQDEy1NaWNyb3NvZnQgRUND
IFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IDIwMTcwHhcNMTkxMjE4MjMwNjQ1WhcNNDIwNzE4
MjMxNjA0WjBlMQswCQYDVQQGEwJVUzEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMTYw
NAYDVQQDEy1NaWNyb3NvZnQgRUNDIFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IDIwMTcwdjAQ
BgcqhkjOPQIBBgUrgQQAIgNiAATUvD0CQnVBEyPNgASGAlEvaqiBYgtlzPbKnR5vSmZRogPZnZH6
thaxjG7efM3beaYvzrvOcS/lpaso7GMEZpn4+vKTEAXhgShC48Zo9OYbhGBKia/teQ87zvH2RPUB
eMCjVDBSMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTIy5lycFIM
+Oa+sgRXKSrPQhDtNTAQBgkrBgEEAYI3FQEEAwIBADAKBggqhkjOPQQDAwNoADBlAjBY8k3qDPlf
Xu5gKcs68tvWMoQZP3zVL8KxzJOuULsJMsbG7X7JNpQS5GiFBqIb0C8CMQCZ6Ra0DvpWSNSkMBaR
eNtUjGUBiudQZsIxtzm6uBoiB078a1QWIP8rtedMDE2mT3M=
-----END CERTIFICATE-----

Microsoft RSA Root Certificate Authority 2017
=============================================
-----BEGIN CERTIFICATE-----
MIIFqDCCA5CgAwIBAgIQHtOXCV/YtLNHcB6qvn9FszANBgkqhkiG9w0BAQwFADBlMQswCQYDVQQG
EwJVUzEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMTYwNAYDVQQDEy1NaWNyb3NvZnQg
UlNBIFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IDIwMTcwHhcNMTkxMjE4MjI1MTIyWhcNNDIw
NzE4MjMwMDIzWjBlMQswCQYDVQQGEwJVUzEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9u
MTYwNAYDVQQDEy1NaWNyb3NvZnQgUlNBIFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IDIwMTcw
ggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDKW76UM4wplZEWCpW9R2LBifOZNt9GkMml
7Xhqb0eRaPgnZ1AzHaGm++DlQ6OEAlcBXZxIQIJTELy/xztokLaCLeX0ZdDMbRnMlfl7rEqUrQ7e
S0MdhweSE5CAg2Q1OQT85elss7YfUJQ4ZVBcF0a5toW1HLUX6NZFndiyJrDKxHBKrmCk3bPZ7Pw7
1VdyvD/IybLeS2v4I2wDwAW9lcfNcztmgGTjGqwu+UcF8ga2m3P1eDNbx6H7JyqhtJqRjJHTOoI+
dkC0zVJhUXAoP8XFWvLJjEm7FFtNyP9nTUwSlq31/niol4fX/V4ggNyhSyL71Imtus5Hl0dVe49F
yGcohJUcaDDv70ngNXtk55iwlNpNhTs+VcQor1fznhPbRiefHqJeRIOkpcrVE7NLP8TjwuaGYaRS
MLl6IE9vDzhTyzMMEyuP1pq9KsgtsRx9S1HKR9FIJ3Jdh+vVReZIZZ2vUpC6W6IYZVcSn2i51BVr
lMRpIpj0M+Dt+VGOQVDJNE92kKz8OMHY4Xu54+OU4UZpyw4KUGsTuqwPN1q3ErWQgR5WrlcihtnJ
0tHXUeOrO8ZV/R4O03QK0dqq6mm4lyiPSMQH+FJDOvTKVTUssKZqwJz58oHhEmrARdlns87/I6KJ
ClTUFLkqqNfs+avNJVgyeY+QW5g5xAgGwax/Dj0ApQIDAQABo1QwUjAOBgNVHQ8BAf8EBAMCAYYw
DwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUCctZf4aycI8awznjwNnpv7tNsiMwEAYJKwYBBAGC
NxUBBAMCAQAwDQYJKoZIhvcNAQEMBQADggIBAKyvPl3CEZaJjqPnktaXFbgToqZCLgLNFgVZJ8og
6Lq46BrsTaiXVq5lQ7GPAJtSzVXNUzltYkyLDVt8LkS/gxCP81OCgMNPOsduET/m4xaRhPtthH80
dK2Jp86519efhGSSvpWhrQlTM93uCupKUY5vVau6tZRGrox/2KJQJWVggEbbMwSubLWYdFQl3JPk
+ONVFT24bcMKpBLBaYVu32TxU5nhSnUgnZUP5NbcA/FZGOhHibJXWpS2qdgXKxdJ5XbLwVaZOjex
/2kskZGT4d9Mozd2TaGf+G0eHdP67Pv0RR0Tbc/3WeUiJ3IrhvNXuzDtJE3cfVa7o7P4NHmJweDy
AmH3pvwPuxwXC65B2Xy9J6P9LjrRk5Sxcx0ki69bIImtt2dmefU6xqaWM/5TkshGsRGRxpl/j8nW
ZjEgQRCHLQzWwa80mMpkg/sTV9HB8Dx6jKXB/ZUhoHHBk2dxEuqPiAppGWSZI1b7rCoucL5mxAyE
7+WL85MB+GqQk2dLsmijtWKP6T+MejteD+eMuMZ87zf9dOLITzNy4ZQ5bb0Sr74MTnB8G2+NszKT
c0QWbej09+CVgI+WXTik9KveCjCHk9hNAHFiRSdLOkKEW39lt2c0Ui2cFmuqqNh7o0JMcccMyj6D
5KbvtwEwXlGjefVwaaZBRA+GsCyRxj3qrg+E
-----END CERTIFICATE-----

e-Szigno Root CA 2017
=====================
-----BEGIN CERTIFICATE-----
MIICQDCCAeWgAwIBAgIMAVRI7yH9l1kN9QQKMAoGCCqGSM49BAMCMHExCzAJBgNVBAYTAkhVMREw
DwYDVQQHDAhCdWRhcGVzdDEWMBQGA1UECgwNTWljcm9zZWMgTHRkLjEXMBUGA1UEYQwOVkFUSFUt
MjM1ODQ0OTcxHjAcBgNVBAMMFWUtU3ppZ25vIFJvb3QgQ0EgMjAxNzAeFw0xNzA4MjIxMjA3MDZa
Fw00MjA4MjIxMjA3MDZaMHExCzAJBgNVBAYTAkhVMREwDwYDVQQHDAhCdWRhcGVzdDEWMBQGA1UE
CgwNTWljcm9zZWMgTHRkLjEXMBUGA1UEYQwOVkFUSFUtMjM1ODQ0OTcxHjAcBgNVBAMMFWUtU3pp
Z25vIFJvb3QgQ0EgMjAxNzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABJbcPYrYsHtvxie+RJCx
s1YVe45DJH0ahFnuY2iyxl6H0BVIHqiQrb1TotreOpCmYF9oMrWGQd+HWyx7xf58etqjYzBhMA8G
A1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBSHERUI0arBeAyxr87GyZDv
vzAEwDAfBgNVHSMEGDAWgBSHERUI0arBeAyxr87GyZDvvzAEwDAKBggqhkjOPQQDAgNJADBGAiEA
tVfd14pVCzbhhkT61NlojbjcI4qKDdQvfepz7L9NbKgCIQDLpbQS+ue16M9+k/zzNY9vTlp8tLxO
svxyqltZ+efcMQ==
-----END CERTIFICATE-----

certSIGN Root CA G2
===================
-----BEGIN CERTIFICATE-----
MIIFRzCCAy+gAwIBAgIJEQA0tk7GNi02MA0GCSqGSIb3DQEBCwUAMEExCzAJBgNVBAYTAlJPMRQw
EgYDVQQKEwtDRVJUU0lHTiBTQTEcMBoGA1UECxMTY2VydFNJR04gUk9PVCBDQSBHMjAeFw0xNzAy
MDYwOTI3MzVaFw00MjAyMDYwOTI3MzVaMEExCzAJBgNVBAYTAlJPMRQwEgYDVQQKEwtDRVJUU0lH
TiBTQTEcMBoGA1UECxMTY2VydFNJR04gUk9PVCBDQSBHMjCCAiIwDQYJKoZIhvcNAQEBBQADggIP
ADCCAgoCggIBAMDFdRmRfUR0dIf+DjuW3NgBFszuY5HnC2/OOwppGnzC46+CjobXXo9X69MhWf05
N0IwvlDqtg+piNguLWkh59E3GE59kdUWX2tbAMI5Qw02hVK5U2UPHULlj88F0+7cDBrZuIt4Imfk
abBoxTzkbFpG583H+u/E7Eu9aqSs/cwoUe+StCmrqzWaTOTECMYmzPhpn+Sc8CnTXPnGFiWeI8Mg
wT0PPzhAsP6CRDiqWhqKa2NYOLQV07YRaXseVO6MGiKscpc/I1mbySKEwQdPzH/iV8oScLumZfNp
dWO9lfsbl83kqK/20U6o2YpxJM02PbyWxPFsqa7lzw1uKA2wDrXKUXt4FMMgL3/7FFXhEZn91Qqh
ngLjYl/rNUssuHLoPj1PrCy7Lobio3aP5ZMqz6WryFyNSwb/EkaseMsUBzXgqd+L6a8VTxaJW732
jcZZroiFDsGJ6x9nxUWO/203Nit4ZoORUSs9/1F3dmKh7Gc+PoGD4FapUB8fepmrY7+EF3fxDTvf
95xhszWYijqy7DwaNz9+j5LP2RIUZNoQAhVB/0/E6xyjyfqZ90bp4RjZsbgyLcsUDFDYg2WD7rlc
z8sFWkz6GZdr1l0T08JcVLwyc6B49fFtHsufpaafItzRUZ6CeWRgKRM+o/1Pcmqr4tTluCRVLERL
iohEnMqE0yo7AgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1Ud
DgQWBBSCIS1mxteg4BXrzkwJd8RgnlRuAzANBgkqhkiG9w0BAQsFAAOCAgEAYN4auOfyYILVAzOB
ywaK8SJJ6ejqkX/GM15oGQOGO0MBzwdw5AgeZYWR5hEit/UCI46uuR59H35s5r0l1ZUa8gWmr4UC
b6741jH/JclKyMeKqdmfS0mbEVeZkkMR3rYzpMzXjWR91M08KCy0mpbqTfXERMQlqiCA2ClV9+BB
/AYm/7k29UMUA2Z44RGx2iBfRgB4ACGlHgAoYXhvqAEBj500mv/0OJD7uNGzcgbJceaBxXntC6Z5
8hMLnPddDnskk7RI24Zf3lCGeOdA5jGokHZwYa+cNywRtYK3qq4kNFtyDGkNzVmf9nGvnAvRCjj5
BiKDUyUM/FHE5r7iOZULJK2v0ZXkltd0ZGtxTgI8qoXzIKNDOXZbbFD+mpwUHmUUihW9o4JFWklW
atKcsWMy5WHgUyIOpwpJ6st+H6jiYoD2EEVSmAYY3qXNL3+q1Ok+CHLsIwMCPKaq2LxndD0UF/tU
Sxfj03k9bWtJySgOLnRQvwzZRjoQhsmnP+mg7H/rpXdYaXHmgwo38oZJar55CJD2AhZkPuXaTH4M
NMn5X7azKFGnpyuqSfqNZSlO42sTp5SjLVFteAxEy9/eCG/Oo2Sr05WE1LlSVHJ7liXMvGnjSG4N
0MedJ5qq+BOS3R7fY581qRY27Iy4g/Q9iY/NtBde17MXQRBdJ3NghVdJIgc=
-----END CERTIFICATE-----

Trustwave Global Certification Authority
========================================
-----BEGIN CERTIFICATE-----
MIIF2jCCA8KgAwIBAgIMBfcOhtpJ80Y1LrqyMA0GCSqGSIb3DQEBCwUAMIGIMQswCQYDVQQGEwJV
UzERMA8GA1UECAwISWxsaW5vaXMxEDAOBgNVBAcMB0NoaWNhZ28xITAfBgNVBAoMGFRydXN0d2F2
ZSBIb2xkaW5ncywgSW5jLjExMC8GA1UEAwwoVHJ1c3R3YXZlIEdsb2JhbCBDZXJ0aWZpY2F0aW9u
IEF1dGhvcml0eTAeFw0xNzA4MjMxOTM0MTJaFw00MjA4MjMxOTM0MTJaMIGIMQswCQYDVQQGEwJV
UzERMA8GA1UECAwISWxsaW5vaXMxEDAOBgNVBAcMB0NoaWNhZ28xITAfBgNVBAoMGFRydXN0d2F2
ZSBIb2xkaW5ncywgSW5jLjExMC8GA1UEAwwoVHJ1c3R3YXZlIEdsb2JhbCBDZXJ0aWZpY2F0aW9u
IEF1dGhvcml0eTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBALldUShLPDeS0YLOvR29
zd24q88KPuFd5dyqCblXAj7mY2Hf8g+CY66j96xz0XznswuvCAAJWX/NKSqIk4cXGIDtiLK0thAf
LdZfVaITXdHG6wZWiYj+rDKd/VzDBcdu7oaJuogDnXIhhpCujwOl3J+IKMujkkkP7NAP4m1ET4Bq
stTnoApTAbqOl5F2brz81Ws25kCI1nsvXwXoLG0R8+eyvpJETNKXpP7ScoFDB5zpET71ixpZfR9o
WN0EACyW80OzfpgZdNmcc9kYvkHHNHnZ9GLCQ7mzJ7Aiy/k9UscwR7PJPrhq4ufogXBeQotPJqX+
OsIgbrv4Fo7NDKm0G2x2EOFYeUY+VM6AqFcJNykbmROPDMjWLBz7BegIlT1lRtzuzWniTY+HKE40
Cz7PFNm73bZQmq131BnW2hqIyE4bJ3XYsgjxroMwuREOzYfwhI0Vcnyh78zyiGG69Gm7DIwLdVcE
uE4qFC49DxweMqZiNu5m4iK4BUBjECLzMx10coos9TkpoNPnG4CELcU9402x/RpvumUHO1jsQkUm
+9jaJXLE9gCxInm943xZYkqcBW89zubWR2OZxiRvchLIrH+QtAuRcOi35hYQcRfO3gZPSEF9NUqj
ifLJS3tBEW1ntwiYTOURGa5CgNz7kAXU+FDKvuStx8KU1xad5hePrzb7AgMBAAGjQjBAMA8GA1Ud
EwEB/wQFMAMBAf8wHQYDVR0OBBYEFJngGWcNYtt2s9o9uFvo/ULSMQ6HMA4GA1UdDwEB/wQEAwIB
BjANBgkqhkiG9w0BAQsFAAOCAgEAmHNw4rDT7TnsTGDZqRKGFx6W0OhUKDtkLSGm+J1WE2pIPU/H
PinbbViDVD2HfSMF1OQc3Og4ZYbFdada2zUFvXfeuyk3QAUHw5RSn8pk3fEbK9xGChACMf1KaA0H
ZJDmHvUqoai7PF35owgLEQzxPy0QlG/+4jSHg9bP5Rs1bdID4bANqKCqRieCNqcVtgimQlRXtpla
4gt5kNdXElE1GYhBaCXUNxeEFfsBctyV3lImIJgm4nb1J2/6ADtKYdkNy1GTKv0WBpanI5ojSP5R
vbbEsLFUzt5sQa0WZ37b/TjNuThOssFgy50X31ieemKyJo90lZvkWx3SD92YHJtZuSPTMaCm/zjd
zyBP6VhWOmfD0faZmZ26NraAL4hHT4a/RDqA5Dccprrql5gR0IRiR2Qequ5AvzSxnI9O4fKSTx+O
856X3vOmeWqJcU9LJxdI/uz0UA9PSX3MReO9ekDFQdxhVicGaeVyQYHTtgGJoC86cnn+OjC/QezH
Yj6RS8fZMXZC+fc8Y+wmjHMMfRod6qh8h6jCJ3zhM0EPz8/8AKAigJ5Kp28AsEFFtyLKaEjFQqKu
3R3y4G5OBVixwJAWKqQ9EEC+j2Jjg6mcgn0tAumDMHzLJ8n9HmYAsC7TIS+OMxZsmO0QqAfWzJPP
29FpHOTKyeC2nOnOcXHebD8WpHk=
-----END CERTIFICATE-----

Trustwave Global ECC P256 Certification Authority
=================================================
-----BEGIN CERTIFICATE-----
MIICYDCCAgegAwIBAgIMDWpfCD8oXD5Rld9dMAoGCCqGSM49BAMCMIGRMQswCQYDVQQGEwJVUzER
MA8GA1UECBMISWxsaW5vaXMxEDAOBgNVBAcTB0NoaWNhZ28xITAfBgNVBAoTGFRydXN0d2F2ZSBI
b2xkaW5ncywgSW5jLjE6MDgGA1UEAxMxVHJ1c3R3YXZlIEdsb2JhbCBFQ0MgUDI1NiBDZXJ0aWZp
Y2F0aW9uIEF1dGhvcml0eTAeFw0xNzA4MjMxOTM1MTBaFw00MjA4MjMxOTM1MTBaMIGRMQswCQYD
VQQGEwJVUzERMA8GA1UECBMISWxsaW5vaXMxEDAOBgNVBAcTB0NoaWNhZ28xITAfBgNVBAoTGFRy
dXN0d2F2ZSBIb2xkaW5ncywgSW5jLjE6MDgGA1UEAxMxVHJ1c3R3YXZlIEdsb2JhbCBFQ0MgUDI1
NiBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABH77bOYj
43MyCMpg5lOcunSNGLB4kFKA3TjASh3RqMyTpJcGOMoNFWLGjgEqZZ2q3zSRLoHB5DOSMcT9CTqm
P62jQzBBMA8GA1UdEwEB/wQFMAMBAf8wDwYDVR0PAQH/BAUDAwcGADAdBgNVHQ4EFgQUo0EGrJBt
0UrrdaVKEJmzsaGLSvcwCgYIKoZIzj0EAwIDRwAwRAIgB+ZU2g6gWrKuEZ+Hxbb/ad4lvvigtwjz
RM4q3wghDDcCIC0mA6AFvWvR9lz4ZcyGbbOcNEhjhAnFjXca4syc4XR7
-----END CERTIFICATE-----

Trustwave Global ECC P384 Certification Authority
=================================================
-----BEGIN CERTIFICATE-----
MIICnTCCAiSgAwIBAgIMCL2Fl2yZJ6SAaEc7MAoGCCqGSM49BAMDMIGRMQswCQYDVQQGEwJVUzER
MA8GA1UECBMISWxsaW5vaXMxEDAOBgNVBAcTB0NoaWNhZ28xITAfBgNVBAoTGFRydXN0d2F2ZSBI
b2xkaW5ncywgSW5jLjE6MDgGA1UEAxMxVHJ1c3R3YXZlIEdsb2JhbCBFQ0MgUDM4NCBDZXJ0aWZp
Y2F0aW9uIEF1dGhvcml0eTAeFw0xNzA4MjMxOTM2NDNaFw00MjA4MjMxOTM2NDNaMIGRMQswCQYD
VQQGEwJVUzERMA8GA1UECBMISWxsaW5vaXMxEDAOBgNVBAcTB0NoaWNhZ28xITAfBgNVBAoTGFRy
dXN0d2F2ZSBIb2xkaW5ncywgSW5jLjE6MDgGA1UEAxMxVHJ1c3R3YXZlIEdsb2JhbCBFQ0MgUDM4
NCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTB2MBAGByqGSM49AgEGBSuBBAAiA2IABGvaDXU1CDFH
Ba5FmVXxERMuSvgQMSOjfoPTfygIOiYaOs+Xgh+AtycJj9GOMMQKmw6sWASr9zZ9lCOkmwqKi6vr
/TklZvFe/oyujUF5nQlgziip04pt89ZF1PKYhDhloKNDMEEwDwYDVR0TAQH/BAUwAwEB/zAPBgNV
HQ8BAf8EBQMDBwYAMB0GA1UdDgQWBBRVqYSJ0sEyvRjLbKYHTsjnnb6CkDAKBggqhkjOPQQDAwNn
ADBkAjA3AZKXRRJ+oPM+rRk6ct30UJMDEr5E0k9BpIycnR+j9sKS50gU/k6bpZFXrsY3crsCMGcl
CrEMXu6pY5Jv5ZAL/mYiykf9ijH3g/56vxC+GCsej/YpHpRZ744hN8tRmKVuSw==
-----END CERTIFICATE-----

NAVER Global Root Certification Authority
=========================================
-----BEGIN CERTIFICATE-----
MIIFojCCA4qgAwIBAgIUAZQwHqIL3fXFMyqxQ0Rx+NZQTQ0wDQYJKoZIhvcNAQEMBQAwaTELMAkG
A1UEBhMCS1IxJjAkBgNVBAoMHU5BVkVSIEJVU0lORVNTIFBMQVRGT1JNIENvcnAuMTIwMAYDVQQD
DClOQVZFUiBHbG9iYWwgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0xNzA4MTgwODU4
NDJaFw0zNzA4MTgyMzU5NTlaMGkxCzAJBgNVBAYTAktSMSYwJAYDVQQKDB1OQVZFUiBCVVNJTkVT
UyBQTEFURk9STSBDb3JwLjEyMDAGA1UEAwwpTkFWRVIgR2xvYmFsIFJvb3QgQ2VydGlmaWNhdGlv
biBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC21PGTXLVAiQqrDZBb
UGOukJR0F0Vy1ntlWilLp1agS7gvQnXp2XskWjFlqxcX0TM62RHcQDaH38dq6SZeWYp34+hInDEW
+j6RscrJo+KfziFTowI2MMtSAuXaMl3Dxeb57hHHi8lEHoSTGEq0n+USZGnQJoViAbbJAh2+g1G7
XNr4rRVqmfeSVPc0W+m/6imBEtRTkZazkVrd/pBzKPswRrXKCAfHcXLJZtM0l/aM9BhK4dA9WkW2
aacp+yPOiNgSnABIqKYPszuSjXEOdMWLyEz59JuOuDxp7W87UC9Y7cSw0BwbagzivESq2M0UXZR4
Yb8ObtoqvC8MC3GmsxY/nOb5zJ9TNeIDoKAYv7vxvvTWjIcNQvcGufFt7QSUqP620wbGQGHfnZ3z
VHbOUzoBppJB7ASjjw2i1QnK1sua8e9DXcCrpUHPXFNwcMmIpi3Ua2FzUCaGYQ5fG8Ir4ozVu53B
A0K6lNpfqbDKzE0K70dpAy8i+/Eozr9dUGWokG2zdLAIx6yo0es+nPxdGoMuK8u180SdOqcXYZai
cdNwlhVNt0xz7hlcxVs+Qf6sdWA7G2POAN3aCJBitOUt7kinaxeZVL6HSuOpXgRM6xBtVNbv8ejy
YhbLgGvtPe31HzClrkvJE+2KAQHJuFFYwGY6sWZLxNUxAmLpdIQM201GLQIDAQABo0IwQDAdBgNV
HQ4EFgQU0p+I36HNLL3s9TsBAZMzJ7LrYEswDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMB
Af8wDQYJKoZIhvcNAQEMBQADggIBADLKgLOdPVQG3dLSLvCkASELZ0jKbY7gyKoNqo0hV4/GPnrK
21HUUrPUloSlWGB/5QuOH/XcChWB5Tu2tyIvCZwTFrFsDDUIbatjcu3cvuzHV+YwIHHW1xDBE1UB
jCpD5EHxzzp6U5LOogMFDTjfArsQLtk70pt6wKGm+LUx5vR1yblTmXVHIloUFcd4G7ad6Qz4G3bx
hYTeodoS76TiEJd6eN4MUZeoIUCLhr0N8F5OSza7OyAfikJW4Qsav3vQIkMsRIz75Sq0bBwcupTg
E34h5prCy8VCZLQelHsIJchxzIdFV4XTnyliIoNRlwAYl3dqmJLJfGBs32x9SuRwTMKeuB330DTH
D8z7p/8Dvq1wkNoL3chtl1+afwkyQf3NosxabUzyqkn+Zvjp2DXrDige7kgvOtB5CTh8piKCk5XQ
A76+AqAF3SAi428diDRgxuYKuQl1C/AH6GmWNcf7I4GOODm4RStDeKLRLBT/DShycpWbXgnbiUSY
qqFJu3FS8r/2/yehNq+4tneI3TqkbZs0kNwUXTC/t+sX5Ie3cdCh13cV1ELX8vMxmV2b3RZtP+oG
I/hGoiLtk/bdmuYqh7GYVPEi92tF4+KOdh2ajcQGjTa3FPOdVGm3jjzVpG2Tgbet9r1ke8LJaDmg
kpzNNIaRkPpkUZ3+/uul9XXeifdy
-----END CERTIFICATE-----

AC RAIZ FNMT-RCM SERVIDORES SEGUROS
===================================
-----BEGIN CERTIFICATE-----
MIICbjCCAfOgAwIBAgIQYvYybOXE42hcG2LdnC6dlTAKBggqhkjOPQQDAzB4MQswCQYDVQQGEwJF
UzERMA8GA1UECgwIRk5NVC1SQ00xDjAMBgNVBAsMBUNlcmVzMRgwFgYDVQRhDA9WQVRFUy1RMjgy
NjAwNEoxLDAqBgNVBAMMI0FDIFJBSVogRk5NVC1SQ00gU0VSVklET1JFUyBTRUdVUk9TMB4XDTE4
MTIyMDA5MzczM1oXDTQzMTIyMDA5MzczM1oweDELMAkGA1UEBhMCRVMxETAPBgNVBAoMCEZOTVQt
UkNNMQ4wDAYDVQQLDAVDZXJlczEYMBYGA1UEYQwPVkFURVMtUTI4MjYwMDRKMSwwKgYDVQQDDCNB
QyBSQUlaIEZOTVQtUkNNIFNFUlZJRE9SRVMgU0VHVVJPUzB2MBAGByqGSM49AgEGBSuBBAAiA2IA
BPa6V1PIyqvfNkpSIeSX0oNnnvBlUdBeh8dHsVnyV0ebAAKTRBdp20LHsbI6GA60XYyzZl2hNPk2
LEnb80b8s0RpRBNm/dfF/a82Tc4DTQdxz69qBdKiQ1oKUm8BA06Oi6NCMEAwDwYDVR0TAQH/BAUw
AwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFAG5L++/EYZg8k/QQW6rcx/n0m5JMAoGCCqG
SM49BAMDA2kAMGYCMQCuSuMrQMN0EfKVrRYj3k4MGuZdpSRea0R7/DjiT8ucRRcRTBQnJlU5dUoD
zBOQn5ICMQD6SmxgiHPz7riYYqnOK8LZiqZwMR2vsJRM60/G49HzYqc8/5MuB1xJAWdpEgJyv+c=
-----END CERTIFICATE-----

GlobalSign Root R46
===================
-----BEGIN CERTIFICATE-----
MIIFWjCCA0KgAwIBAgISEdK7udcjGJ5AXwqdLdDfJWfRMA0GCSqGSIb3DQEBDAUAMEYxCzAJBgNV
BAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMRwwGgYDVQQDExNHbG9iYWxTaWduIFJv
b3QgUjQ2MB4XDTE5MDMyMDAwMDAwMFoXDTQ2MDMyMDAwMDAwMFowRjELMAkGA1UEBhMCQkUxGTAX
BgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExHDAaBgNVBAMTE0dsb2JhbFNpZ24gUm9vdCBSNDYwggIi
MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCsrHQy6LNl5brtQyYdpokNRbopiLKkHWPd08Es
CVeJOaFV6Wc0dwxu5FUdUiXSE2te4R2pt32JMl8Nnp8semNgQB+msLZ4j5lUlghYruQGvGIFAha/
r6gjA7aUD7xubMLL1aa7DOn2wQL7Id5m3RerdELv8HQvJfTqa1VbkNud316HCkD7rRlr+/fKYIje
2sGP1q7Vf9Q8g+7XFkyDRTNrJ9CG0Bwta/OrffGFqfUo0q3v84RLHIf8E6M6cqJaESvWJ3En7YEt
bWaBkoe0G1h6zD8K+kZPTXhc+CtI4wSEy132tGqzZfxCnlEmIyDLPRT5ge1lFgBPGmSXZgjPjHvj
K8Cd+RTyG/FWaha/LIWFzXg4mutCagI0GIMXTpRW+LaCtfOW3T3zvn8gdz57GSNrLNRyc0NXfeD4
12lPFzYE+cCQYDdF3uYM2HSNrpyibXRdQr4G9dlkbgIQrImwTDsHTUB+JMWKmIJ5jqSngiCNI/on
ccnfxkF0oE32kRbcRoxfKWMxWXEM2G/CtjJ9++ZdU6Z+Ffy7dXxd7Pj2Fxzsx2sZy/N78CsHpdls
eVR2bJ0cpm4O6XkMqCNqo98bMDGfsVR7/mrLZqrcZdCinkqaByFrgY/bxFn63iLABJzjqls2k+g9
vXqhnQt2sQvHnf3PmKgGwvgqo6GDoLclcqUC4wIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAYYwDwYD
VR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUA1yrc4GHqMywptWU4jaWSf8FmSwwDQYJKoZIhvcNAQEM
BQADggIBAHx47PYCLLtbfpIrXTncvtgdokIzTfnvpCo7RGkerNlFo048p9gkUbJUHJNOxO97k4Vg
JuoJSOD1u8fpaNK7ajFxzHmuEajwmf3lH7wvqMxX63bEIaZHU1VNaL8FpO7XJqti2kM3S+LGteWy
gxk6x9PbTZ4IevPuzz5i+6zoYMzRx6Fcg0XERczzF2sUyQQCPtIkpnnpHs6i58FZFZ8d4kuaPp92
CC1r2LpXFNqD6v6MVenQTqnMdzGxRBF6XLE+0xRFFRhiJBPSy03OXIPBNvIQtQ6IbbjhVp+J3pZm
OUdkLG5NrmJ7v2B0GbhWrJKsFjLtrWhV/pi60zTe9Mlhww6G9kuEYO4Ne7UyWHmRVSyBQ7N0H3qq
JZ4d16GLuc1CLgSkZoNNiTW2bKg2SnkheCLQQrzRQDGQob4Ez8pn7fXwgNNgyYMqIgXQBztSvwye
qiv5u+YfjyW6hY0XHgL+XVAEV8/+LbzvXMAaq7afJMbfc2hIkCwU9D9SGuTSyxTDYWnP4vkYxboz
nxSjBF25cfe1lNj2M8FawTSLfJvdkzrnE6JwYZ+vj+vYxXX4M2bUdGc6N3ec592kD3ZDZopD8p/7
DEJ4Y9HiD2971KE9dJeFt0g5QdYg/NA6s/rob8SKunE3vouXsXgxT7PntgMTzlSdriVZzH81Xwj3
QEUxeCp6
-----END CERTIFICATE-----

GlobalSign Root E46
===================
-----BEGIN CERTIFICATE-----
MIICCzCCAZGgAwIBAgISEdK7ujNu1LzmJGjFDYQdmOhDMAoGCCqGSM49BAMDMEYxCzAJBgNVBAYT
AkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMRwwGgYDVQQDExNHbG9iYWxTaWduIFJvb3Qg
RTQ2MB4XDTE5MDMyMDAwMDAwMFoXDTQ2MDMyMDAwMDAwMFowRjELMAkGA1UEBhMCQkUxGTAXBgNV
BAoTEEdsb2JhbFNpZ24gbnYtc2ExHDAaBgNVBAMTE0dsb2JhbFNpZ24gUm9vdCBFNDYwdjAQBgcq
hkjOPQIBBgUrgQQAIgNiAAScDrHPt+ieUnd1NPqlRqetMhkytAepJ8qUuwzSChDH2omwlwxwEwkB
jtjqR+q+soArzfwoDdusvKSGN+1wCAB16pMLey5SnCNoIwZD7JIvU4Tb+0cUB+hflGddyXqBPCCj
QjBAMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBQxCpCPtsad0kRL
gLWi5h+xEk8blTAKBggqhkjOPQQDAwNoADBlAjEA31SQ7Zvvi5QCkxeCmb6zniz2C5GMn0oUsfZk
vLtoURMMA/cVi4RguYv/Uo7njLwcAjA8+RHUjE7AwWHCFUyqqx0LMV87HOIAl0Qx5v5zli/altP+
CAezNIm8BZ/3Hobui3A=
-----END CERTIFICATE-----

GLOBALTRUST 2020
================
-----BEGIN CERTIFICATE-----
MIIFgjCCA2qgAwIBAgILWku9WvtPilv6ZeUwDQYJKoZIhvcNAQELBQAwTTELMAkGA1UEBhMCQVQx
IzAhBgNVBAoTGmUtY29tbWVyY2UgbW9uaXRvcmluZyBHbWJIMRkwFwYDVQQDExBHTE9CQUxUUlVT
VCAyMDIwMB4XDTIwMDIxMDAwMDAwMFoXDTQwMDYxMDAwMDAwMFowTTELMAkGA1UEBhMCQVQxIzAh
BgNVBAoTGmUtY29tbWVyY2UgbW9uaXRvcmluZyBHbWJIMRkwFwYDVQQDExBHTE9CQUxUUlVTVCAy
MDIwMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAri5WrRsc7/aVj6B3GyvTY4+ETUWi
D59bRatZe1E0+eyLinjF3WuvvcTfk0Uev5E4C64OFudBc/jbu9G4UeDLgztzOG53ig9ZYybNpyrO
VPu44sB8R85gfD+yc/LAGbaKkoc1DZAoouQVBGM+uq/ufF7MpotQsjj3QWPKzv9pj2gOlTblzLmM
CcpL3TGQlsjMH/1WljTbjhzqLL6FLmPdqqmV0/0plRPwyJiT2S0WR5ARg6I6IqIoV6Lr/sCMKKCm
fecqQjuCgGOlYx8ZzHyyZqjC0203b+J+BlHZRYQfEs4kUmSFC0iAToexIiIwquuuvuAC4EDosEKA
A1GqtH6qRNdDYfOiaxaJSaSjpCuKAsR49GiKweR6NrFvG5Ybd0mN1MkGco/PU+PcF4UgStyYJ9OR
JitHHmkHr96i5OTUawuzXnzUJIBHKWk7buis/UDr2O1xcSvy6Fgd60GXIsUf1DnQJ4+H4xj04KlG
DfV0OoIu0G4skaMxXDtG6nsEEFZegB31pWXogvziB4xiRfUg3kZwhqG8k9MedKZssCz3AwyIDMvU
clOGvGBG85hqwvG/Q/lwIHfKN0F5VVJjjVsSn8VoxIidrPIwq7ejMZdnrY8XD2zHc+0klGvIg5rQ
mjdJBKuxFshsSUktq6HQjJLyQUp5ISXbY9e2nKd+Qmn7OmMCAwEAAaNjMGEwDwYDVR0TAQH/BAUw
AwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFNwuH9FhN3nkq9XVsxJxaD1qaJwiMB8GA1Ud
IwQYMBaAFNwuH9FhN3nkq9XVsxJxaD1qaJwiMA0GCSqGSIb3DQEBCwUAA4ICAQCR8EICaEDuw2jA
VC/f7GLDw56KoDEoqoOOpFaWEhCGVrqXctJUMHytGdUdaG/7FELYjQ7ztdGl4wJCXtzoRlgHNQIw
4Lx0SsFDKv/bGtCwr2zD/cuz9X9tAy5ZVp0tLTWMstZDFyySCstd6IwPS3BD0IL/qMy/pJTAvoe9
iuOTe8aPmxadJ2W8esVCgmxcB9CpwYhgROmYhRZf+I/KARDOJcP5YBugxZfD0yyIMaK9MOzQ0MAS
8cE54+X1+NZK3TTN+2/BT+MAi1bikvcoskJ3ciNnxz8RFbLEAwW+uxF7Cr+obuf/WEPPm2eggAe2
HcqtbepBEX4tdJP7wry+UUTF72glJ4DjyKDUEuzZpTcdN3y0kcra1LGWge9oXHYQSa9+pTeAsRxS
vTOBTI/53WXZFM2KJVj04sWDpQmQ1GwUY7VA3+vA/MRYfg0UFodUJ25W5HCEuGwyEn6CMUO+1918
oa2u1qsgEu8KwxCMSZY13At1XrFP1U80DhEgB3VDRemjEdqso5nCtnkn4rnvyOL2NSl6dPrFf4IF
YqYK6miyeUcGbvJXqBUzxvd4Sj1Ce2t+/vdG6tHrju+IaFvowdlxfv1k7/9nR4hYJS8+hge9+6jl
gqispdNpQ80xiEmEU5LAsTkbOYMBMMTyqfrQA71yN2BWHzZ8vTmR9W0Nv3vXkg==
-----END CERTIFICATE-----

ANF Secure Server Root CA
=========================
-----BEGIN CERTIFICATE-----
MIIF7zCCA9egAwIBAgIIDdPjvGz5a7EwDQYJKoZIhvcNAQELBQAwgYQxEjAQBgNVBAUTCUc2MzI4
NzUxMDELMAkGA1UEBhMCRVMxJzAlBgNVBAoTHkFORiBBdXRvcmlkYWQgZGUgQ2VydGlmaWNhY2lv
bjEUMBIGA1UECxMLQU5GIENBIFJhaXoxIjAgBgNVBAMTGUFORiBTZWN1cmUgU2VydmVyIFJvb3Qg
Q0EwHhcNMTkwOTA0MTAwMDM4WhcNMzkwODMwMTAwMDM4WjCBhDESMBAGA1UEBRMJRzYzMjg3NTEw
MQswCQYDVQQGEwJFUzEnMCUGA1UEChMeQU5GIEF1dG9yaWRhZCBkZSBDZXJ0aWZpY2FjaW9uMRQw
EgYDVQQLEwtBTkYgQ0EgUmFpejEiMCAGA1UEAxMZQU5GIFNlY3VyZSBTZXJ2ZXIgUm9vdCBDQTCC
AiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANvrayvmZFSVgpCjcqQZAZ2cC4Ffc0m6p6zz
BE57lgvsEeBbphzOG9INgxwruJ4dfkUyYA8H6XdYfp9qyGFOtibBTI3/TO80sh9l2Ll49a2pcbnv
T1gdpd50IJeh7WhM3pIXS7yr/2WanvtH2Vdy8wmhrnZEE26cLUQ5vPnHO6RYPUG9tMJJo8gN0pcv
B2VSAKduyK9o7PQUlrZXH1bDOZ8rbeTzPvY1ZNoMHKGESy9LS+IsJJ1tk0DrtSOOMspvRdOoiXse
zx76W0OLzc2oD2rKDF65nkeP8Nm2CgtYZRczuSPkdxl9y0oukntPLxB3sY0vaJxizOBQ+OyRp1RM
VwnVdmPF6GUe7m1qzwmd+nxPrWAI/VaZDxUse6mAq4xhj0oHdkLePfTdsiQzW7i1o0TJrH93PB0j
7IKppuLIBkwC/qxcmZkLLxCKpvR/1Yd0DVlJRfbwcVw5Kda/SiOL9V8BY9KHcyi1Swr1+KuCLH5z
JTIdC2MKF4EA/7Z2Xue0sUDKIbvVgFHlSFJnLNJhiQcND85Cd8BEc5xEUKDbEAotlRyBr+Qc5RQe
8TZBAQIvfXOn3kLMTOmJDVb3n5HUA8ZsyY/b2BzgQJhdZpmYgG4t/wHFzstGH6wCxkPmrqKEPMVO
Hj1tyRRM4y5Bu8o5vzY8KhmqQYdOpc5LMnndkEl/AgMBAAGjYzBhMB8GA1UdIwQYMBaAFJxf0Gxj
o1+TypOYCK2Mh6UsXME3MB0GA1UdDgQWBBScX9BsY6Nfk8qTmAitjIelLFzBNzAOBgNVHQ8BAf8E
BAMCAYYwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAgEATh65isagmD9uw2nAalxJ
UqzLK114OMHVVISfk/CHGT0sZonrDUL8zPB1hT+L9IBdeeUXZ701guLyPI59WzbLWoAAKfLOKyzx
j6ptBZNscsdW699QIyjlRRA96Gejrw5VD5AJYu9LWaL2U/HANeQvwSS9eS9OICI7/RogsKQOLHDt
dD+4E5UGUcjohybKpFtqFiGS3XNgnhAY3jyB6ugYw3yJ8otQPr0R4hUDqDZ9MwFsSBXXiJCZBMXM
5gf0vPSQ7RPi6ovDj6MzD8EpTBNO2hVWcXNyglD2mjN8orGoGjR0ZVzO0eurU+AagNjqOknkJjCb
5RyKqKkVMoaZkgoQI1YS4PbOTOK7vtuNknMBZi9iPrJyJ0U27U1W45eZ/zo1PqVUSlJZS2Db7v54
EX9K3BR5YLZrZAPbFYPhor72I5dQ8AkzNqdxliXzuUJ92zg/LFis6ELhDtjTO0wugumDLmsx2d1H
hk9tl5EuT+IocTUW0fJz/iUrB0ckYyfI+PbZa/wSMVYIwFNCr5zQM378BvAxRAMU8Vjq8moNqRGy
g77FGr8H6lnco4g175x2MjxNBiLOFeXdntiP2t7SxDnlF4HPOEfrf4htWRvfn0IUrn7PqLBmZdo3
r5+qPeoott7VMVgWglvquxl1AnMaykgaIZOQCo6ThKd9OyMYkomgjaw=
-----END CERTIFICATE-----

Certum EC-384 CA
================
-----BEGIN CERTIFICATE-----
MIICZTCCAeugAwIBAgIQeI8nXIESUiClBNAt3bpz9DAKBggqhkjOPQQDAzB0MQswCQYDVQQGEwJQ
TDEhMB8GA1UEChMYQXNzZWNvIERhdGEgU3lzdGVtcyBTLkEuMScwJQYDVQQLEx5DZXJ0dW0gQ2Vy
dGlmaWNhdGlvbiBBdXRob3JpdHkxGTAXBgNVBAMTEENlcnR1bSBFQy0zODQgQ0EwHhcNMTgwMzI2
MDcyNDU0WhcNNDMwMzI2MDcyNDU0WjB0MQswCQYDVQQGEwJQTDEhMB8GA1UEChMYQXNzZWNvIERh
dGEgU3lzdGVtcyBTLkEuMScwJQYDVQQLEx5DZXJ0dW0gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkx
GTAXBgNVBAMTEENlcnR1bSBFQy0zODQgQ0EwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAATEKI6rGFtq
vm5kN2PkzeyrOvfMobgOgknXhimfoZTy42B4mIF4Bk3y7JoOV2CDn7TmFy8as10CW4kjPMIRBSqn
iBMY81CE1700LCeJVf/OTOffph8oxPBUw7l8t1Ot68KjQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYD
VR0OBBYEFI0GZnQkdjrzife81r1HfS+8EF9LMA4GA1UdDwEB/wQEAwIBBjAKBggqhkjOPQQDAwNo
ADBlAjADVS2m5hjEfO/JUG7BJw+ch69u1RsIGL2SKcHvlJF40jocVYli5RsJHrpka/F2tNQCMQC0
QoSZ/6vnnvuRlydd3LBbMHHOXjgaatkl5+r3YZJW+OraNsKHZZYuciUvf9/DE8k=
-----END CERTIFICATE-----

Certum Trusted Root CA
======================
-----BEGIN CERTIFICATE-----
MIIFwDCCA6igAwIBAgIQHr9ZULjJgDdMBvfrVU+17TANBgkqhkiG9w0BAQ0FADB6MQswCQYDVQQG
EwJQTDEhMB8GA1UEChMYQXNzZWNvIERhdGEgU3lzdGVtcyBTLkEuMScwJQYDVQQLEx5DZXJ0dW0g
Q2VydGlmaWNhdGlvbiBBdXRob3JpdHkxHzAdBgNVBAMTFkNlcnR1bSBUcnVzdGVkIFJvb3QgQ0Ew
HhcNMTgwMzE2MTIxMDEzWhcNNDMwMzE2MTIxMDEzWjB6MQswCQYDVQQGEwJQTDEhMB8GA1UEChMY
QXNzZWNvIERhdGEgU3lzdGVtcyBTLkEuMScwJQYDVQQLEx5DZXJ0dW0gQ2VydGlmaWNhdGlvbiBB
dXRob3JpdHkxHzAdBgNVBAMTFkNlcnR1bSBUcnVzdGVkIFJvb3QgQ0EwggIiMA0GCSqGSIb3DQEB
AQUAA4ICDwAwggIKAoICAQDRLY67tzbqbTeRn06TpwXkKQMlzhyC93yZn0EGze2jusDbCSzBfN8p
fktlL5On1AFrAygYo9idBcEq2EXxkd7fO9CAAozPOA/qp1x4EaTByIVcJdPTsuclzxFUl6s1wB52
HO8AU5853BSlLCIls3Jy/I2z5T4IHhQqNwuIPMqw9MjCoa68wb4pZ1Xi/K1ZXP69VyywkI3C7Te2
fJmItdUDmj0VDT06qKhF8JVOJVkdzZhpu9PMMsmN74H+rX2Ju7pgE8pllWeg8xn2A1bUatMn4qGt
g/BKEiJ3HAVz4hlxQsDsdUaakFjgao4rpUYwBI4Zshfjvqm6f1bxJAPXsiEodg42MEx51UGamqi4
NboMOvJEGyCI98Ul1z3G4z5D3Yf+xOr1Uz5MZf87Sst4WmsXXw3Hw09Omiqi7VdNIuJGmj8PkTQk
fVXjjJU30xrwCSss0smNtA0Aq2cpKNgB9RkEth2+dv5yXMSFytKAQd8FqKPVhJBPC/PgP5sZ0jeJ
P/J7UhyM9uH3PAeXjA6iWYEMspA90+NZRu0PqafegGtaqge2Gcu8V/OXIXoMsSt0Puvap2ctTMSY
njYJdmZm/Bo/6khUHL4wvYBQv3y1zgD2DGHZ5yQD4OMBgQ692IU0iL2yNqh7XAjlRICMb/gv1SHK
HRzQ+8S1h9E6Tsd2tTVItQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBSM+xx1
vALTn04uSNn5YFSqxLNP+jAOBgNVHQ8BAf8EBAMCAQYwDQYJKoZIhvcNAQENBQADggIBAEii1QAL
LtA/vBzVtVRJHlpr9OTy4EA34MwUe7nJ+jW1dReTagVphZzNTxl4WxmB82M+w85bj/UvXgF2Ez8s
ALnNllI5SW0ETsXpD4YN4fqzX4IS8TrOZgYkNCvozMrnadyHncI013nR03e4qllY/p0m+jiGPp2K
h2RX5Rc64vmNueMzeMGQ2Ljdt4NR5MTMI9UGfOZR0800McD2RrsLrfw9EAUqO0qRJe6M1ISHgCq8
CYyqOhNf6DR5UMEQGfnTKB7U0VEwKbOukGfWHwpjscWpxkIxYxeU72nLL/qMFH3EQxiJ2fAyQOaA
4kZf5ePBAFmo+eggvIksDkc0C+pXwlM2/KfUrzHN/gLldfq5Jwn58/U7yn2fqSLLiMmq0Uc9Nneo
WWRrJ8/vJ8HjJLWG965+Mk2weWjROeiQWMODvA8s1pfrzgzhIMfatz7DP78v3DSk+yshzWePS/Tj
6tQ/50+6uaWTRRxmHyH6ZF5v4HaUMst19W7l9o/HuKTMqJZ9ZPskWkoDbGs4xugDQ5r3V7mzKWmT
OPQD8rv7gmsHINFSH5pkAnuYZttcTVoP0ISVoDwUQwbKytu4QTbaakRnh6+v40URFWkIsr4WOZck
bxJF0WddCajJFdr60qZfE2Efv4WstK2tBZQIgx51F9NxO5NQI1mg7TyRVJ12AMXDuDjb
-----END CERTIFICATE-----

TunTrust Root CA
================
-----BEGIN CERTIFICATE-----
MIIFszCCA5ugAwIBAgIUEwLV4kBMkkaGFmddtLu7sms+/BMwDQYJKoZIhvcNAQELBQAwYTELMAkG
A1UEBhMCVE4xNzA1BgNVBAoMLkFnZW5jZSBOYXRpb25hbGUgZGUgQ2VydGlmaWNhdGlvbiBFbGVj
dHJvbmlxdWUxGTAXBgNVBAMMEFR1blRydXN0IFJvb3QgQ0EwHhcNMTkwNDI2MDg1NzU2WhcNNDQw
NDI2MDg1NzU2WjBhMQswCQYDVQQGEwJUTjE3MDUGA1UECgwuQWdlbmNlIE5hdGlvbmFsZSBkZSBD
ZXJ0aWZpY2F0aW9uIEVsZWN0cm9uaXF1ZTEZMBcGA1UEAwwQVHVuVHJ1c3QgUm9vdCBDQTCCAiIw
DQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMPN0/y9BFPdDCA61YguBUtB9YOCfvdZn56eY+hz
2vYGqU8ftPkLHzmMmiDQfgbU7DTZhrx1W4eI8NLZ1KMKsmwb60ksPqxd2JQDoOw05TDENX37Jk0b
bjBU2PWARZw5rZzJJQRNmpA+TkBuimvNKWfGzC3gdOgFVwpIUPp6Q9p+7FuaDmJ2/uqdHYVy7BG7
NegfJ7/Boce7SBbdVtfMTqDhuazb1YMZGoXRlJfXyqNlC/M4+QKu3fZnz8k/9YosRxqZbwUN/dAd
gjH8KcwAWJeRTIAAHDOFli/LQcKLEITDCSSJH7UP2dl3RxiSlGBcx5kDPP73lad9UKGAwqmDrViW
VSHbhlnUr8a83YFuB9tgYv7sEG7aaAH0gxupPqJbI9dkxt/con3YS7qC0lH4Zr8GRuR5KiY2eY8f
Tpkdso8MDhz/yV3A/ZAQprE38806JG60hZC/gLkMjNWb1sjxVj8agIl6qeIbMlEsPvLfe/ZdeikZ
juXIvTZxi11Mwh0/rViizz1wTaZQmCXcI/m4WEEIcb9PuISgjwBUFfyRbVinljvrS5YnzWuioYas
DXxU5mZMZl+QviGaAkYt5IPCgLnPSz7ofzwB7I9ezX/SKEIBlYrilz0QIX32nRzFNKHsLA4KUiwS
VXAkPcvCFDVDXSdOvsC9qnyW5/yeYa1E0wCXAgMBAAGjYzBhMB0GA1UdDgQWBBQGmpsfU33x9aTI
04Y+oXNZtPdEITAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFAaamx9TffH1pMjThj6hc1m0
90QhMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAgEAqgVutt0Vyb+zxiD2BkewhpMl
0425yAA/l/VSJ4hxyXT968pk21vvHl26v9Hr7lxpuhbI87mP0zYuQEkHDVneixCwSQXi/5E/S7fd
Ao74gShczNxtr18UnH1YeA32gAm56Q6XKRm4t+v4FstVEuTGfbvE7Pi1HE4+Z7/FXxttbUcoqgRY
YdZ2vyJ/0Adqp2RT8JeNnYA/u8EH22Wv5psymsNUk8QcCMNE+3tjEUPRahphanltkE8pjkcFwRJp
adbGNjHh/PqAulxPxOu3Mqz4dWEX1xAZufHSCe96Qp1bWgvUxpVOKs7/B9dPfhgGiPEZtdmYu65x
xBzndFlY7wyJz4sfdZMaBBSSSFCp61cpABbjNhzI+L/wM9VBD8TMPN3pM0MBkRArHtG5Xc0yGYuP
jCB31yLEQtyEFpslbei0VXF/sHyz03FJuc9SpAQ/3D2gu68zngowYI7bnV2UqL1g52KAdoGDDIzM
MEZJ4gzSqK/rYXHv5yJiqfdcZGyfFoxnNidF9Ql7v/YQCvGwjVRDjAS6oz/v4jXH+XTgbzRB0L9z
ZVcg+ZtnemZoJE6AZb0QmQZZ8mWvuMZHu/2QeItBcy6vVR/cO5JyboTT0GFMDcx2V+IthSIVNg3r
AZ3r2OvEhJn7wAzMMujjd9qDRIueVSjAi1jTkD5OGwDxFa2DK5o=
-----END CERTIFICATE-----

HARICA TLS RSA Root CA 2021
===========================
-----BEGIN CERTIFICATE-----
MIIFpDCCA4ygAwIBAgIQOcqTHO9D88aOk8f0ZIk4fjANBgkqhkiG9w0BAQsFADBsMQswCQYDVQQG
EwJHUjE3MDUGA1UECgwuSGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJlc2VhcmNoIEluc3RpdHV0aW9u
cyBDQTEkMCIGA1UEAwwbSEFSSUNBIFRMUyBSU0EgUm9vdCBDQSAyMDIxMB4XDTIxMDIxOTEwNTUz
OFoXDTQ1MDIxMzEwNTUzN1owbDELMAkGA1UEBhMCR1IxNzA1BgNVBAoMLkhlbGxlbmljIEFjYWRl
bWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgQ0ExJDAiBgNVBAMMG0hBUklDQSBUTFMgUlNB
IFJvb3QgQ0EgMjAyMTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAIvC569lmwVnlskN
JLnQDmT8zuIkGCyEf3dRywQRNrhe7Wlxp57kJQmXZ8FHws+RFjZiPTgE4VGC/6zStGndLuwRo0Xu
a2s7TL+MjaQenRG56Tj5eg4MmOIjHdFOY9TnuEFE+2uva9of08WRiFukiZLRgeaMOVig1mlDqa2Y
Ulhu2wr7a89o+uOkXjpFc5gH6l8Cct4MpbOfrqkdtx2z/IpZ525yZa31MJQjB/OCFks1mJxTuy/K
5FrZx40d/JiZ+yykgmvwKh+OC19xXFyuQnspiYHLA6OZyoieC0AJQTPb5lh6/a6ZcMBaD9YThnEv
dmn8kN3bLW7R8pv1GmuebxWMevBLKKAiOIAkbDakO/IwkfN4E8/BPzWr8R0RI7VDIp4BkrcYAuUR
0YLbFQDMYTfBKnya4dC6s1BG7oKsnTH4+yPiAwBIcKMJJnkVU2DzOFytOOqBAGMUuTNe3QvboEUH
GjMJ+E20pwKmafTCWQWIZYVWrkvL4N48fS0ayOn7H6NhStYqE613TBoYm5EPWNgGVMWX+Ko/IIqm
haZ39qb8HOLubpQzKoNQhArlT4b4UEV4AIHrW2jjJo3Me1xR9BQsQL4aYB16cmEdH2MtiKrOokWQ
CPxrvrNQKlr9qEgYRtaQQJKQCoReaDH46+0N0x3GfZkYVVYnZS6NRcUk7M7jAgMBAAGjQjBAMA8G
A1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFApII6ZgpJIKM+qTW8VX6iVNvRLuMA4GA1UdDwEB/wQE
AwIBhjANBgkqhkiG9w0BAQsFAAOCAgEAPpBIqm5iFSVmewzVjIuJndftTgfvnNAUX15QvWiWkKQU
EapobQk1OUAJ2vQJLDSle1mESSmXdMgHHkdt8s4cUCbjnj1AUz/3f5Z2EMVGpdAgS1D0NTsY9FVq
QRtHBmg8uwkIYtlfVUKqrFOFrJVWNlar5AWMxajaH6NpvVMPxP/cyuN+8kyIhkdGGvMA9YCRotxD
QpSbIPDRzbLrLFPCU3hKTwSUQZqPJzLB5UkZv/HywouoCjkxKLR9YjYsTewfM7Z+d21+UPCfDtcR
j88YxeMn/ibvBZ3PzzfF0HvaO7AWhAw6k9a+F9sPPg4ZeAnHqQJyIkv3N3a6dcSFA1pj1bF1BcK5
vZStjBWZp5N99sXzqnTPBIWUmAD04vnKJGW/4GKvyMX6ssmeVkjaef2WdhW+o45WxLM0/L5H9MG0
qPzVMIho7suuyWPEdr6sOBjhXlzPrjoiUevRi7PzKzMHVIf6tLITe7pTBGIBnfHAT+7hOtSLIBD6
Alfm78ELt5BGnBkpjNxvoEppaZS3JGWg/6w/zgH7IS79aPib8qXPMThcFarmlwDB31qlpzmq6YR/
PFGoOtmUW4y/Twhx5duoXNTSpv4Ao8YWxw/ogM4cKGR0GQjTQuPOAF1/sdwTsOEFy9EgqoZ0njnn
kf3/W9b3raYvAwtt41dU63ZTGI0RmLo=
-----END CERTIFICATE-----

HARICA TLS ECC Root CA 2021
===========================
-----BEGIN CERTIFICATE-----
MIICVDCCAdugAwIBAgIQZ3SdjXfYO2rbIvT/WeK/zjAKBggqhkjOPQQDAzBsMQswCQYDVQQGEwJH
UjE3MDUGA1UECgwuSGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJlc2VhcmNoIEluc3RpdHV0aW9ucyBD
QTEkMCIGA1UEAwwbSEFSSUNBIFRMUyBFQ0MgUm9vdCBDQSAyMDIxMB4XDTIxMDIxOTExMDExMFoX
DTQ1MDIxMzExMDEwOVowbDELMAkGA1UEBhMCR1IxNzA1BgNVBAoMLkhlbGxlbmljIEFjYWRlbWlj
IGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgQ0ExJDAiBgNVBAMMG0hBUklDQSBUTFMgRUNDIFJv
b3QgQ0EgMjAyMTB2MBAGByqGSM49AgEGBSuBBAAiA2IABDgI/rGgltJ6rK9JOtDA4MM7KKrxcm1l
AEeIhPyaJmuqS7psBAqIXhfyVYf8MLA04jRYVxqEU+kw2anylnTDUR9YSTHMmE5gEYd103KUkE+b
ECUqqHgtvpBBWJAVcqeht6NCMEAwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUyRtTgRL+BNUW
0aq8mm+3oJUZbsowDgYDVR0PAQH/BAQDAgGGMAoGCCqGSM49BAMDA2cAMGQCMBHervjcToiwqfAi
rcJRQO9gcS3ujwLEXQNwSaSS6sUUiHCm0w2wqsosQJz76YJumgIwK0eaB8bRwoF8yguWGEEbo/Qw
CZ61IygNnxS2PFOiTAZpffpskcYqSUXm7LcT4Tps
-----END CERTIFICATE-----

Autoridad de Certificacion Firmaprofesional CIF A62634068
=========================================================
-----BEGIN CERTIFICATE-----
MIIGFDCCA/ygAwIBAgIIG3Dp0v+ubHEwDQYJKoZIhvcNAQELBQAwUTELMAkGA1UEBhMCRVMxQjBA
BgNVBAMMOUF1dG9yaWRhZCBkZSBDZXJ0aWZpY2FjaW9uIEZpcm1hcHJvZmVzaW9uYWwgQ0lGIEE2
MjYzNDA2ODAeFw0xNDA5MjMxNTIyMDdaFw0zNjA1MDUxNTIyMDdaMFExCzAJBgNVBAYTAkVTMUIw
QAYDVQQDDDlBdXRvcmlkYWQgZGUgQ2VydGlmaWNhY2lvbiBGaXJtYXByb2Zlc2lvbmFsIENJRiBB
NjI2MzQwNjgwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDKlmuO6vj78aI14H9M2uDD
Utd9thDIAl6zQyrET2qyyhxdKJp4ERppWVevtSBC5IsP5t9bpgOSL/UR5GLXMnE42QQMcas9UX4P
B99jBVzpv5RvwSmCwLTaUbDBPLutN0pcyvFLNg4kq7/DhHf9qFD0sefGL9ItWY16Ck6WaVICqjaY
7Pz6FIMMNx/Jkjd/14Et5cS54D40/mf0PmbR0/RAz15iNA9wBj4gGFrO93IbJWyTdBSTo3OxDqqH
ECNZXyAFGUftaI6SEspd/NYrspI8IM/hX68gvqB2f3bl7BqGYTM+53u0P6APjqK5am+5hyZvQWyI
plD9amML9ZMWGxmPsu2bm8mQ9QEM3xk9Dz44I8kvjwzRAv4bVdZO0I08r0+k8/6vKtMFnXkIoctX
MbScyJCyZ/QYFpM6/EfY0XiWMR+6KwxfXZmtY4laJCB22N/9q06mIqqdXuYnin1oKaPnirjaEbsX
LZmdEyRG98Xi2J+Of8ePdG1asuhy9azuJBCtLxTa/y2aRnFHvkLfuwHb9H/TKI8xWVvTyQKmtFLK
bpf7Q8UIJm+K9Lv9nyiqDdVF8xM6HdjAeI9BZzwelGSuewvF6NkBiDkal4ZkQdU7hwxu+g/GvUgU
vzlN1J5Bto+WHWOWk9mVBngxaJ43BjuAiUVhOSPHG0SjFeUc+JIwuwIDAQABo4HvMIHsMB0GA1Ud
DgQWBBRlzeurNR4APn7VdMActHNHDhpkLzASBgNVHRMBAf8ECDAGAQH/AgEBMIGmBgNVHSAEgZ4w
gZswgZgGBFUdIAAwgY8wLwYIKwYBBQUHAgEWI2h0dHA6Ly93d3cuZmlybWFwcm9mZXNpb25hbC5j
b20vY3BzMFwGCCsGAQUFBwICMFAeTgBQAGEAcwBlAG8AIABkAGUAIABsAGEAIABCAG8AbgBhAG4A
bwB2AGEAIAA0ADcAIABCAGEAcgBjAGUAbABvAG4AYQAgADAAOAAwADEANzAOBgNVHQ8BAf8EBAMC
AQYwDQYJKoZIhvcNAQELBQADggIBAHSHKAIrdx9miWTtj3QuRhy7qPj4Cx2Dtjqn6EWKB7fgPiDL
4QjbEwj4KKE1soCzC1HA01aajTNFSa9J8OA9B3pFE1r/yJfY0xgsfZb43aJlQ3CTkBW6kN/oGbDb
LIpgD7dvlAceHabJhfa9NPhAeGIQcDq+fUs5gakQ1JZBu/hfHAsdCPKxsIl68veg4MSPi3i1O1il
I45PVf42O+AMt8oqMEEgtIDNrvx2ZnOorm7hfNoD6JQg5iKj0B+QXSBTFCZX2lSX3xZEEAEeiGaP
cjiT3SC3NL7X8e5jjkd5KAb881lFJWAiMxujX6i6KtoaPc1A6ozuBRWV1aUsIC+nmCjuRfzxuIgA
LI9C2lHVnOUTaHFFQ4ueCyE8S1wF3BqfmI7avSKecs2tCsvMo2ebKHTEm9caPARYpoKdrcd7b/+A
lun4jWq9GJAd/0kakFI3ky88Al2CdgtR5xbHV/g4+afNmyJU72OwFW1TZQNKXkqgsqeOSQBZONXH
9IBk9W6VULgRfhVwOEqwf9DEMnDAGf/JOC0ULGb0QkTmVXYbgBVX/8Cnp6o5qtjTcNAuuuuUavpf
NIbnYrX9ivAwhZTJryQCL2/W3Wf+47BVTwSYT6RBVuKT0Gro1vP7ZeDOdcQxWQzugsgMYDNKGbqE
ZycPvEJdvSRUDewdcAZfpLz6IHxV
-----END CERTIFICATE-----

vTrus ECC Root CA
=================
-----BEGIN CERTIFICATE-----
MIICDzCCAZWgAwIBAgIUbmq8WapTvpg5Z6LSa6Q75m0c1towCgYIKoZIzj0EAwMwRzELMAkGA1UE
BhMCQ04xHDAaBgNVBAoTE2lUcnVzQ2hpbmEgQ28uLEx0ZC4xGjAYBgNVBAMTEXZUcnVzIEVDQyBS
b290IENBMB4XDTE4MDczMTA3MjY0NFoXDTQzMDczMTA3MjY0NFowRzELMAkGA1UEBhMCQ04xHDAa
BgNVBAoTE2lUcnVzQ2hpbmEgQ28uLEx0ZC4xGjAYBgNVBAMTEXZUcnVzIEVDQyBSb290IENBMHYw
EAYHKoZIzj0CAQYFK4EEACIDYgAEZVBKrox5lkqqHAjDo6LN/llWQXf9JpRCux3NCNtzslt188+c
ToL0v/hhJoVs1oVbcnDS/dtitN9Ti72xRFhiQgnH+n9bEOf+QP3A2MMrMudwpremIFUde4BdS49n
TPEQo0IwQDAdBgNVHQ4EFgQUmDnNvtiyjPeyq+GtJK97fKHbH88wDwYDVR0TAQH/BAUwAwEB/zAO
BgNVHQ8BAf8EBAMCAQYwCgYIKoZIzj0EAwMDaAAwZQIwV53dVvHH4+m4SVBrm2nDb+zDfSXkV5UT
QJtS0zvzQBm8JsctBp61ezaf9SXUY2sAAjEA6dPGnlaaKsyh2j/IZivTWJwghfqrkYpwcBE4YGQL
YgmRWAD5Tfs0aNoJrSEGGJTO
-----END CERTIFICATE-----

vTrus Root CA
=============
-----BEGIN CERTIFICATE-----
MIIFVjCCAz6gAwIBAgIUQ+NxE9izWRRdt86M/TX9b7wFjUUwDQYJKoZIhvcNAQELBQAwQzELMAkG
A1UEBhMCQ04xHDAaBgNVBAoTE2lUcnVzQ2hpbmEgQ28uLEx0ZC4xFjAUBgNVBAMTDXZUcnVzIFJv
b3QgQ0EwHhcNMTgwNzMxMDcyNDA1WhcNNDMwNzMxMDcyNDA1WjBDMQswCQYDVQQGEwJDTjEcMBoG
A1UEChMTaVRydXNDaGluYSBDby4sTHRkLjEWMBQGA1UEAxMNdlRydXMgUm9vdCBDQTCCAiIwDQYJ
KoZIhvcNAQEBBQADggIPADCCAgoCggIBAL1VfGHTuB0EYgWgrmy3cLRB6ksDXhA/kFocizuwZots
SKYcIrrVQJLuM7IjWcmOvFjai57QGfIvWcaMY1q6n6MLsLOaXLoRuBLpDLvPbmyAhykUAyyNJJrI
ZIO1aqwTLDPxn9wsYTwaP3BVm60AUn/PBLn+NvqcwBauYv6WTEN+VRS+GrPSbcKvdmaVayqwlHeF
XgQPYh1jdfdr58tbmnDsPmcF8P4HCIDPKNsFxhQnL4Z98Cfe/+Z+M0jnCx5Y0ScrUw5XSmXX+6KA
YPxMvDVTAWqXcoKv8R1w6Jz1717CbMdHflqUhSZNO7rrTOiwCcJlwp2dCZtOtZcFrPUGoPc2BX70
kLJrxLT5ZOrpGgrIDajtJ8nU57O5q4IikCc9Kuh8kO+8T/3iCiSn3mUkpF3qwHYw03dQ+A0Em5Q2
AXPKBlim0zvc+gRGE1WKyURHuFE5Gi7oNOJ5y1lKCn+8pu8fA2dqWSslYpPZUxlmPCdiKYZNpGvu
/9ROutW04o5IWgAZCfEF2c6Rsffr6TlP9m8EQ5pV9T4FFL2/s1m02I4zhKOQUqqzApVg+QxMaPnu
1RcN+HFXtSXkKe5lXa/R7jwXC1pDxaWG6iSe4gUH3DRCEpHWOXSuTEGC2/KmSNGzm/MzqvOmwMVO
9fSddmPmAsYiS8GVP1BkLFTltvA8Kc9XAgMBAAGjQjBAMB0GA1UdDgQWBBRUYnBj8XWEQ1iO0RYg
scasGrz2iTAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsFAAOC
AgEAKbqSSaet8PFww+SX8J+pJdVrnjT+5hpk9jprUrIQeBqfTNqK2uwcN1LgQkv7bHbKJAs5EhWd
nxEt/Hlk3ODg9d3gV8mlsnZwUKT+twpw1aA08XXXTUm6EdGz2OyC/+sOxL9kLX1jbhd47F18iMjr
jld22VkE+rxSH0Ws8HqA7Oxvdq6R2xCOBNyS36D25q5J08FsEhvMKar5CKXiNxTKsbhm7xqC5PD4
8acWabfbqWE8n/Uxy+QARsIvdLGx14HuqCaVvIivTDUHKgLKeBRtRytAVunLKmChZwOgzoy8sHJn
xDHO2zTlJQNgJXtxmOTAGytfdELSS8VZCAeHvsXDf+eW2eHcKJfWjwXj9ZtOyh1QRwVTsMo554Wg
icEFOwE30z9J4nfrI8iIZjs9OXYhRvHsXyO466JmdXTBQPfYaJqT4i2pLr0cox7IdMakLXogqzu4
sEb9b91fUlV1YvCXoHzXOP0l382gmxDPi7g4Xl7FtKYCNqEeXxzP4padKar9mK5S4fNBUvupLnKW
nyfjqnN9+BojZns7q2WwMgFLFT49ok8MKzWixtlnEjUwzXYuFrOZnk1PTi07NEPhmg4NpGaXutIc
SkwsKouLgU9xGqndXHt7CMUADTdA43x7VF8vhV929vensBxXVsFy6K2ir40zSbofitzmdHxghm+H
l3s=
-----END CERTIFICATE-----

ISRG Root X2
============
-----BEGIN CERTIFICATE-----
MIICGzCCAaGgAwIBAgIQQdKd0XLq7qeAwSxs6S+HUjAKBggqhkjOPQQDAzBPMQswCQYDVQQGEwJV
UzEpMCcGA1UEChMgSW50ZXJuZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElT
UkcgUm9vdCBYMjAeFw0yMDA5MDQwMDAwMDBaFw00MDA5MTcxNjAwMDBaME8xCzAJBgNVBAYTAlVT
MSkwJwYDVQQKEyBJbnRlcm5ldCBTZWN1cml0eSBSZXNlYXJjaCBHcm91cDEVMBMGA1UEAxMMSVNS
RyBSb290IFgyMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEzZvVn4CDCuwJSvMWSj5cz3es3mcFDR0H
ttwW+1qLFNvicWDEukWVEYmO6gbf9yoWHKS5xcUy4APgHoIYOIvXRdgKam7mAHf7AlF9ItgKbppb
d9/w+kHsOdx1ymgHDB/qo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNV
HQ4EFgQUfEKWrt5LSDv6kviejM9ti6lyN5UwCgYIKoZIzj0EAwMDaAAwZQIwe3lORlCEwkSHRhtF
cP9Ymd70/aTSVaYgLXTWNLxBo1BfASdWtL4ndQavEi51mI38AjEAi/V3bNTIZargCyzuFJ0nN6T5
U6VR5CmD1/iQMVtCnwr1/q4AaOeMSQ+2b1tbFfLn
-----END CERTIFICATE-----

HiPKI Root CA - G1
==================
-----BEGIN CERTIFICATE-----
MIIFajCCA1KgAwIBAgIQLd2szmKXlKFD6LDNdmpeYDANBgkqhkiG9w0BAQsFADBPMQswCQYDVQQG
EwJUVzEjMCEGA1UECgwaQ2h1bmdod2EgVGVsZWNvbSBDby4sIEx0ZC4xGzAZBgNVBAMMEkhpUEtJ
IFJvb3QgQ0EgLSBHMTAeFw0xOTAyMjIwOTQ2MDRaFw0zNzEyMzExNTU5NTlaME8xCzAJBgNVBAYT
AlRXMSMwIQYDVQQKDBpDaHVuZ2h3YSBUZWxlY29tIENvLiwgTHRkLjEbMBkGA1UEAwwSSGlQS0kg
Um9vdCBDQSAtIEcxMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA9B5/UnMyDHPkvRN0
o9QwqNCuS9i233VHZvR85zkEHmpwINJaR3JnVfSl6J3VHiGh8Ge6zCFovkRTv4354twvVcg3Px+k
wJyz5HdcoEb+d/oaoDjq7Zpy3iu9lFc6uux55199QmQ5eiY29yTw1S+6lZgRZq2XNdZ1AYDgr/SE
YYwNHl98h5ZeQa/rh+r4XfEuiAU+TCK72h8q3VJGZDnzQs7ZngyzsHeXZJzA9KMuH5UHsBffMNsA
GJZMoYFL3QRtU6M9/Aes1MU3guvklQgZKILSQjqj2FPseYlgSGDIcpJQ3AOPgz+yQlda22rpEZfd
hSi8MEyr48KxRURHH+CKFgeW0iEPU8DtqX7UTuybCeyvQqww1r/REEXgphaypcXTT3OUM3ECoWqj
1jOXTyFjHluP2cFeRXF3D4FdXyGarYPM+l7WjSNfGz1BryB1ZlpK9p/7qxj3ccC2HTHsOyDry+K4
9a6SsvfhhEvyovKTmiKe0xRvNlS9H15ZFblzqMF8b3ti6RZsR1pl8w4Rm0bZ/W3c1pzAtH2lsN0/
Vm+h+fbkEkj9Bn8SV7apI09bA8PgcSojt/ewsTu8mL3WmKgMa/aOEmem8rJY5AIJEzypuxC00jBF
8ez3ABHfZfjcK0NVvxaXxA/VLGGEqnKG/uY6fsI/fe78LxQ+5oXdUG+3Se0CAwEAAaNCMEAwDwYD
VR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU8ncX+l6o/vY9cdVouslGDDjYr7AwDgYDVR0PAQH/BAQD
AgGGMA0GCSqGSIb3DQEBCwUAA4ICAQBQUfB13HAE4/+qddRxosuej6ip0691x1TPOhwEmSKsxBHi
7zNKpiMdDg1H2DfHb680f0+BazVP6XKlMeJ45/dOlBhbQH3PayFUhuaVevvGyuqcSE5XCV0vrPSl
tJczWNWseanMX/mF+lLFjfiRFOs6DRfQUsJ748JzjkZ4Bjgs6FzaZsT0pPBWGTMpWmWSBUdGSquE
wx4noR8RkpkndZMPvDY7l1ePJlsMu5wP1G4wB9TcXzZoZjmDlicmisjEOf6aIW/Vcobpf2Lll07Q
JNBAsNB1CI69aO4I1258EHBGG3zgiLKecoaZAeO/n0kZtCW+VmWuF2PlHt/o/0elv+EmBYTksMCv
5wiZqAxeJoBF1PhoL5aPruJKHJwWDBNvOIf2u8g0X5IDUXlwpt/L9ZlNec1OvFefQ05rLisY+Gpz
jLrFNe85akEez3GoorKGB1s6yeHvP2UEgEcyRHCVTjFnanRbEEV16rCf0OY1/k6fi8wrkkVbbiVg
hUbN0aqwdmaTd5a+g744tiROJgvM7XpWGuDpWsZkrUx6AEhEL7lAuxM+vhV4nYWBSipX3tUZQ9rb
yltHhoMLP7YNdnhzeSJesYAfz77RP1YQmCuVh6EfnWQUYDksswBVLuT1sw5XxJFBAJw/6KXf6vb/
yPCtbVKoF6ubYfwSUTXkJf2vqmqGOQ==
-----END CERTIFICATE-----

GlobalSign ECC Root CA - R4
===========================
-----BEGIN CERTIFICATE-----
MIIB3DCCAYOgAwIBAgINAgPlfvU/k/2lCSGypjAKBggqhkjOPQQDAjBQMSQwIgYDVQQLExtHbG9i
YWxTaWduIEVDQyBSb290IENBIC0gUjQxEzARBgNVBAoTCkdsb2JhbFNpZ24xEzARBgNVBAMTCkds
b2JhbFNpZ24wHhcNMTIxMTEzMDAwMDAwWhcNMzgwMTE5MDMxNDA3WjBQMSQwIgYDVQQLExtHbG9i
YWxTaWduIEVDQyBSb290IENBIC0gUjQxEzARBgNVBAoTCkdsb2JhbFNpZ24xEzARBgNVBAMTCkds
b2JhbFNpZ24wWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAS4xnnTj2wlDp8uORkcA6SumuU5BwkW
ymOxuYb4ilfBV85C+nOh92VC/x7BALJucw7/xyHlGKSq2XE/qNS5zowdo0IwQDAOBgNVHQ8BAf8E
BAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUVLB7rUW44kB/+wpu+74zyTyjhNUwCgYI
KoZIzj0EAwIDRwAwRAIgIk90crlgr/HmnKAWBVBfw147bmF0774BxL4YSFlhgjICICadVGNA3jdg
UM/I2O2dgq43mLyjj0xMqTQrbO/7lZsm
-----END CERTIFICATE-----

GTS Root R1
===========
-----BEGIN CERTIFICATE-----
MIIFVzCCAz+gAwIBAgINAgPlk28xsBNJiGuiFzANBgkqhkiG9w0BAQwFADBHMQswCQYDVQQGEwJV
UzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3Qg
UjEwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAwMDAwWjBHMQswCQYDVQQGEwJVUzEiMCAGA1UE
ChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjEwggIiMA0G
CSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC2EQKLHuOhd5s73L+UPreVp0A8of2C+X0yBoJx9vaM
f/vo27xqLpeXo4xL+Sv2sfnOhB2x+cWX3u+58qPpvBKJXqeqUqv4IyfLpLGcY9vXmX7wCl7raKb0
xlpHDU0QM+NOsROjyBhsS+z8CZDfnWQpJSMHobTSPS5g4M/SCYe7zUjwTcLCeoiKu7rPWRnWr4+w
B7CeMfGCwcDfLqZtbBkOtdh+JhpFAz2weaSUKK0PfyblqAj+lug8aJRT7oM6iCsVlgmy4HqMLnXW
nOunVmSPlk9orj2XwoSPwLxAwAtcvfaHszVsrBhQf4TgTM2S0yDpM7xSma8ytSmzJSq0SPly4cpk
9+aCEI3oncKKiPo4Zor8Y/kB+Xj9e1x3+naH+uzfsQ55lVe0vSbv1gHR6xYKu44LtcXFilWr06zq
kUspzBmkMiVOKvFlRNACzqrOSbTqn3yDsEB750Orp2yjj32JgfpMpf/VjsPOS+C12LOORc92wO1A
K/1TD7Cn1TsNsYqiA94xrcx36m97PtbfkSIS5r762DL8EGMUUXLeXdYWk70paDPvOmbsB4om3xPX
V2V4J95eSRQAogB/mqghtqmxlbCluQ0WEdrHbEg8QOB+DVrNVjzRlwW5y0vtOUucxD/SVRNuJLDW
cfr0wbrM7Rv1/oFB2ACYPTrIrnqYNxgFlQIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0T
AQH/BAUwAwEB/zAdBgNVHQ4EFgQU5K8rJnEaK0gnhS9SZizv8IkTcT4wDQYJKoZIhvcNAQEMBQAD
ggIBAJ+qQibbC5u+/x6Wki4+omVKapi6Ist9wTrYggoGxval3sBOh2Z5ofmmWJyq+bXmYOfg6LEe
QkEzCzc9zolwFcq1JKjPa7XSQCGYzyI0zzvFIoTgxQ6KfF2I5DUkzps+GlQebtuyh6f88/qBVRRi
ClmpIgUxPoLW7ttXNLwzldMXG+gnoot7TiYaelpkttGsN/H9oPM47HLwEXWdyzRSjeZ2axfG34ar
J45JK3VmgRAhpuo+9K4l/3wV3s6MJT/KYnAK9y8JZgfIPxz88NtFMN9iiMG1D53Dn0reWVlHxYci
NuaCp+0KueIHoI17eko8cdLiA6EfMgfdG+RCzgwARWGAtQsgWSl4vflVy2PFPEz0tv/bal8xa5me
LMFrUKTX5hgUvYU/Z6tGn6D/Qqc6f1zLXbBwHSs09dR2CQzreExZBfMzQsNhFRAbd03OIozUhfJF
fbdT6u9AWpQKXCBfTkBdYiJ23//OYb2MI3jSNwLgjt7RETeJ9r/tSQdirpLsQBqvFAnZ0E6yove+
7u7Y/9waLd64NnHi/Hm3lCXRSHNboTXns5lndcEZOitHTtNCjv0xyBZm2tIMPNuzjsmhDYAPexZ3
FL//2wmUspO8IFgV6dtxQ/PeEMMA3KgqlbbC1j+Qa3bbbP6MvPJwNQzcmRk13NfIRmPVNnGuV/u3
gm3c
-----END CERTIFICATE-----

GTS Root R2
===========
-----BEGIN CERTIFICATE-----
MIIFVzCCAz+gAwIBAgINAgPlrsWNBCUaqxElqjANBgkqhkiG9w0BAQwFADBHMQswCQYDVQQGEwJV
UzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3Qg
UjIwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAwMDAwWjBHMQswCQYDVQQGEwJVUzEiMCAGA1UE
ChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjIwggIiMA0G
CSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDO3v2m++zsFDQ8BwZabFn3GTXd98GdVarTzTukk3Lv
CvptnfbwhYBboUhSnznFt+4orO/LdmgUud+tAWyZH8QiHZ/+cnfgLFuv5AS/T3KgGjSY6Dlo7JUl
e3ah5mm5hRm9iYz+re026nO8/4Piy33B0s5Ks40FnotJk9/BW9BuXvAuMC6C/Pq8tBcKSOWIm8Wb
a96wyrQD8Nr0kLhlZPdcTK3ofmZemde4wj7I0BOdre7kRXuJVfeKH2JShBKzwkCX44ofR5GmdFrS
+LFjKBC4swm4VndAoiaYecb+3yXuPuWgf9RhD1FLPD+M2uFwdNjCaKH5wQzpoeJ/u1U8dgbuak7M
kogwTZq9TwtImoS1mKPV+3PBV2HdKFZ1E66HjucMUQkQdYhMvI35ezzUIkgfKtzra7tEscszcTJG
r61K8YzodDqs5xoic4DSMPclQsciOzsSrZYuxsN2B6ogtzVJV+mSSeh2FnIxZyuWfoqjx5RWIr9q
S34BIbIjMt/kmkRtWVtd9QCgHJvGeJeNkP+byKq0rxFROV7Z+2et1VsRnTKaG73VululycslaVNV
J1zgyjbLiGH7HrfQy+4W+9OmTN6SpdTi3/UGVN4unUu0kzCqgc7dGtxRcw1PcOnlthYhGXmy5okL
dWTK1au8CcEYof/UVKGFPP0UJAOyh9OktwIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0T
AQH/BAUwAwEB/zAdBgNVHQ4EFgQUu//KjiOfT5nK2+JopqUVJxce2Q4wDQYJKoZIhvcNAQEMBQAD
ggIBAB/Kzt3HvqGf2SdMC9wXmBFqiN495nFWcrKeGk6c1SuYJF2ba3uwM4IJvd8lRuqYnrYb/oM8
0mJhwQTtzuDFycgTE1XnqGOtjHsB/ncw4c5omwX4Eu55MaBBRTUoCnGkJE+M3DyCB19m3H0Q/gxh
swWV7uGugQ+o+MePTagjAiZrHYNSVc61LwDKgEDg4XSsYPWHgJ2uNmSRXbBoGOqKYcl3qJfEycel
/FVL8/B/uWU9J2jQzGv6U53hkRrJXRqWbTKH7QMgyALOWr7Z6v2yTcQvG99fevX4i8buMTolUVVn
jWQye+mew4K6Ki3pHrTgSAai/GevHyICc/sgCq+dVEuhzf9gR7A/Xe8bVr2XIZYtCtFenTgCR2y5
9PYjJbigapordwj6xLEokCZYCDzifqrXPW+6MYgKBesntaFJ7qBFVHvmJ2WZICGoo7z7GJa7Um8M
7YNRTOlZ4iBgxcJlkoKM8xAfDoqXvneCbT+PHV28SSe9zE8P4c52hgQjxcCMElv924SgJPFI/2R8
0L5cFtHvma3AH/vLrrw4IgYmZNralw4/KBVEqE8AyvCazM90arQ+POuV7LXTWtiBmelDGDfrs7vR
WGJB82bSj6p4lVQgw1oudCvV0b4YacCs1aTPObpRhANl6WLAYv7YTVWW4tAR+kg0Eeye7QUd5MjW
HYbL
-----END CERTIFICATE-----

GTS Root R3
===========
-----BEGIN CERTIFICATE-----
MIICCTCCAY6gAwIBAgINAgPluILrIPglJ209ZjAKBggqhkjOPQQDAzBHMQswCQYDVQQGEwJVUzEi
MCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjMw
HhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAwMDAwWjBHMQswCQYDVQQGEwJVUzEiMCAGA1UEChMZ
R29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjMwdjAQBgcqhkjO
PQIBBgUrgQQAIgNiAAQfTzOHMymKoYTey8chWEGJ6ladK0uFxh1MJ7x/JlFyb+Kf1qPKzEUURout
736GjOyxfi//qXGdGIRFBEFVbivqJn+7kAHjSxm65FSWRQmx1WyRRK2EE46ajA2ADDL24CejQjBA
MA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTB8Sa6oC2uhYHP0/Eq
Er24Cmf9vDAKBggqhkjOPQQDAwNpADBmAjEA9uEglRR7VKOQFhG/hMjqb2sXnh5GmCCbn9MN2azT
L818+FsuVbu/3ZL3pAzcMeGiAjEA/JdmZuVDFhOD3cffL74UOO0BzrEXGhF16b0DjyZ+hOXJYKaV
11RZt+cRLInUue4X
-----END CERTIFICATE-----

GTS Root R4
===========
-----BEGIN CERTIFICATE-----
MIICCTCCAY6gAwIBAgINAgPlwGjvYxqccpBQUjAKBggqhkjOPQQDAzBHMQswCQYDVQQGEwJVUzEi
MCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjQw
HhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAwMDAwWjBHMQswCQYDVQQGEwJVUzEiMCAGA1UEChMZ
R29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjQwdjAQBgcqhkjO
PQIBBgUrgQQAIgNiAATzdHOnaItgrkO4NcWBMHtLSZ37wWHO5t5GvWvVYRg1rkDdc/eJkTBa6zzu
hXyiQHY7qca4R9gq55KRanPpsXI5nymfopjTX15YhmUPoYRlBtHci8nHc8iMai/lxKvRHYqjQjBA
MA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBSATNbrdP9JNqPV2Py1
PsVq8JQdjDAKBggqhkjOPQQDAwNpADBmAjEA6ED/g94D9J+uHXqnLrmvT/aDHQ4thQEd0dlq7A/C
r8deVl5c1RxYIigL9zC2L7F8AjEA8GE8p/SgguMh1YQdc4acLa/KNJvxn7kjNuK8YAOdgLOaVsjh
4rsUecrNIdSUtUlD
-----END CERTIFICATE-----
vendor/guzzle/guzzle/src/Guzzle/Http/CachingEntityBody.php000064400000014172151327705700017763 0ustar00<?php

namespace Guzzle\Http;

use Guzzle\Common\Exception\RuntimeException;

/**
 * EntityBody decorator that can cache previously read bytes from a sequentially read tstream
 */
class CachingEntityBody extends AbstractEntityBodyDecorator
{
    /** @var EntityBody Remote stream used to actually pull data onto the buffer */
    protected $remoteStream;

    /** @var int The number of bytes to skip reading due to a write on the temporary buffer */
    protected $skipReadBytes = 0;

    /**
     * We will treat the buffer object as the body of the entity body
     * {@inheritdoc}
     */
    public function __construct(EntityBodyInterface $body)
    {
        $this->remoteStream = $body;
        $this->body = new EntityBody(fopen('php://temp', 'r+'));
    }

    /**
     * Will give the contents of the buffer followed by the exhausted remote stream.
     *
     * Warning: Loads the entire stream into memory
     *
     * @return string
     */
    public function __toString()
    {
        $pos = $this->ftell();
        $this->rewind();

        $str = '';
        while (!$this->isConsumed()) {
            $str .= $this->read(16384);
        }

        $this->seek($pos);

        return $str;
    }

    public function getSize()
    {
        return max($this->body->getSize(), $this->remoteStream->getSize());
    }

    /**
     * {@inheritdoc}
     * @throws RuntimeException When seeking with SEEK_END or when seeking past the total size of the buffer stream
     */
    public function seek($offset, $whence = SEEK_SET)
    {
        if ($whence == SEEK_SET) {
            $byte = $offset;
        } elseif ($whence == SEEK_CUR) {
            $byte = $offset + $this->ftell();
        } else {
            throw new RuntimeException(__CLASS__ . ' supports only SEEK_SET and SEEK_CUR seek operations');
        }

        // You cannot skip ahead past where you've read from the remote stream
        if ($byte > $this->body->getSize()) {
            throw new RuntimeException(
                "Cannot seek to byte {$byte} when the buffered stream only contains {$this->body->getSize()} bytes"
            );
        }

        return $this->body->seek($byte);
    }

    public function rewind()
    {
        return $this->seek(0);
    }

    /**
     * Does not support custom rewind functions
     *
     * @throws RuntimeException
     */
    public function setRewindFunction($callable)
    {
        throw new RuntimeException(__CLASS__ . ' does not support custom stream rewind functions');
    }

    public function read($length)
    {
        // Perform a regular read on any previously read data from the buffer
        $data = $this->body->read($length);
        $remaining = $length - strlen($data);

        // More data was requested so read from the remote stream
        if ($remaining) {
            // If data was written to the buffer in a position that would have been filled from the remote stream,
            // then we must skip bytes on the remote stream to emulate overwriting bytes from that position. This
            // mimics the behavior of other PHP stream wrappers.
            $remoteData = $this->remoteStream->read($remaining + $this->skipReadBytes);

            if ($this->skipReadBytes) {
                $len = strlen($remoteData);
                $remoteData = substr($remoteData, $this->skipReadBytes);
                $this->skipReadBytes = max(0, $this->skipReadBytes - $len);
            }

            $data .= $remoteData;
            $this->body->write($remoteData);
        }

        return $data;
    }

    public function write($string)
    {
        // When appending to the end of the currently read stream, you'll want to skip bytes from being read from
        // the remote stream to emulate other stream wrappers. Basically replacing bytes of data of a fixed length.
        $overflow = (strlen($string) + $this->ftell()) - $this->remoteStream->ftell();
        if ($overflow > 0) {
            $this->skipReadBytes += $overflow;
        }

        return $this->body->write($string);
    }

    /**
     * {@inheritdoc}
     * @link http://php.net/manual/en/function.fgets.php
     */
    public function readLine($maxLength = null)
    {
        $buffer = '';
        $size = 0;
        while (!$this->isConsumed()) {
            $byte = $this->read(1);
            $buffer .= $byte;
            // Break when a new line is found or the max length - 1 is reached
            if ($byte == PHP_EOL || ++$size == $maxLength - 1) {
                break;
            }
        }

        return $buffer;
    }

    public function isConsumed()
    {
        return $this->body->isConsumed() && $this->remoteStream->isConsumed();
    }

    /**
     * Close both the remote stream and buffer stream
     */
    public function close()
    {
        return $this->remoteStream->close() && $this->body->close();
    }

    public function setStream($stream, $size = 0)
    {
        $this->remoteStream->setStream($stream, $size);
    }

    public function getContentType()
    {
        return $this->remoteStream->getContentType();
    }

    public function getContentEncoding()
    {
        return $this->remoteStream->getContentEncoding();
    }

    public function getMetaData($key = null)
    {
        return $this->remoteStream->getMetaData($key);
    }

    public function getStream()
    {
        return $this->remoteStream->getStream();
    }

    public function getWrapper()
    {
        return $this->remoteStream->getWrapper();
    }

    public function getWrapperData()
    {
        return $this->remoteStream->getWrapperData();
    }

    public function getStreamType()
    {
        return $this->remoteStream->getStreamType();
    }

    public function getUri()
    {
        return $this->remoteStream->getUri();
    }

    /**
     * Always retrieve custom data from the remote stream
     * {@inheritdoc}
     */
    public function getCustomData($key)
    {
        return $this->remoteStream->getCustomData($key);
    }

    /**
     * Always set custom data on the remote stream
     * {@inheritdoc}
     */
    public function setCustomData($key, $value)
    {
        $this->remoteStream->setCustomData($key, $value);

        return $this;
    }
}
vendor/guzzle/guzzle/src/Guzzle/Http/StaticClient.php000064400000011047151327705700017000 0ustar00<?php

namespace Guzzle\Http;

use Guzzle\Http\Client;
use Guzzle\Http\ClientInterface;
use Guzzle\Stream\StreamRequestFactoryInterface;
use Guzzle\Stream\PhpStreamRequestFactory;

/**
 * Simplified interface to Guzzle that does not require a class to be instantiated
 */
final class StaticClient
{
    /** @var Client Guzzle client */
    private static $client;

    /**
     * Mount the client to a simpler class name for a specific client
     *
     * @param string          $className Class name to use to mount
     * @param ClientInterface $client    Client used to send requests
     */
    public static function mount($className = 'Guzzle', ClientInterface $client = null)
    {
        class_alias(__CLASS__, $className);
        if ($client) {
            self::$client = $client;
        }
    }

    /**
     * @param  string $method  HTTP request method (GET, POST, HEAD, DELETE, PUT, etc)
     * @param  string $url     URL of the request
     * @param  array  $options Options to use with the request. See: Guzzle\Http\Message\RequestFactory::applyOptions()
     * @return \Guzzle\Http\Message\Response|\Guzzle\Stream\Stream
     */
    public static function request($method, $url, $options = array())
    {
        // @codeCoverageIgnoreStart
        if (!self::$client) {
            self::$client = new Client();
        }
        // @codeCoverageIgnoreEnd

        $request = self::$client->createRequest($method, $url, null, null, $options);

        if (isset($options['stream'])) {
            if ($options['stream'] instanceof StreamRequestFactoryInterface) {
                return $options['stream']->fromRequest($request);
            } elseif ($options['stream'] == true) {
                $streamFactory = new PhpStreamRequestFactory();
                return $streamFactory->fromRequest($request);
            }
        }

        return $request->send();
    }

    /**
     * Send a GET request
     *
     * @param string $url     URL of the request
     * @param array  $options Array of request options
     *
     * @return \Guzzle\Http\Message\Response
     * @see Guzzle::request for a list of available options
     */
    public static function get($url, $options = array())
    {
        return self::request('GET', $url, $options);
    }

    /**
     * Send a HEAD request
     *
     * @param string $url     URL of the request
     * @param array  $options Array of request options
     *
     * @return \Guzzle\Http\Message\Response
     * @see Guzzle::request for a list of available options
     */
    public static function head($url, $options = array())
    {
        return self::request('HEAD', $url, $options);
    }

    /**
     * Send a DELETE request
     *
     * @param string $url     URL of the request
     * @param array  $options Array of request options
     *
     * @return \Guzzle\Http\Message\Response
     * @see Guzzle::request for a list of available options
     */
    public static function delete($url, $options = array())
    {
        return self::request('DELETE', $url, $options);
    }

    /**
     * Send a POST request
     *
     * @param string $url     URL of the request
     * @param array  $options Array of request options
     *
     * @return \Guzzle\Http\Message\Response
     * @see Guzzle::request for a list of available options
     */
    public static function post($url, $options = array())
    {
        return self::request('POST', $url, $options);
    }

    /**
     * Send a PUT request
     *
     * @param string $url     URL of the request
     * @param array  $options Array of request options
     *
     * @return \Guzzle\Http\Message\Response
     * @see Guzzle::request for a list of available options
     */
    public static function put($url, $options = array())
    {
        return self::request('PUT', $url, $options);
    }

    /**
     * Send a PATCH request
     *
     * @param string $url     URL of the request
     * @param array  $options Array of request options
     *
     * @return \Guzzle\Http\Message\Response
     * @see Guzzle::request for a list of available options
     */
    public static function patch($url, $options = array())
    {
        return self::request('PATCH', $url, $options);
    }

    /**
     * Send an OPTIONS request
     *
     * @param string $url     URL of the request
     * @param array  $options Array of request options
     *
     * @return \Guzzle\Http\Message\Response
     * @see Guzzle::request for a list of available options
     */
    public static function options($url, $options = array())
    {
        return self::request('OPTIONS', $url, $options);
    }
}
vendor/guzzle/guzzle/src/Guzzle/Http/EntityBody.php000064400000013602151327705700016503 0ustar00<?php

namespace Guzzle\Http;

use Guzzle\Common\Version;
use Guzzle\Stream\Stream;
use Guzzle\Common\Exception\InvalidArgumentException;
use Guzzle\Http\Mimetypes;

/**
 * Entity body used with an HTTP request or response
 */
class EntityBody extends Stream implements EntityBodyInterface
{
    /** @var bool Content-Encoding of the entity body if known */
    protected $contentEncoding = false;

    /** @var callable Method to invoke for rewinding a stream */
    protected $rewindFunction;

    /**
     * Create a new EntityBody based on the input type
     *
     * @param resource|string|EntityBody $resource Entity body data
     * @param int                        $size     Size of the data contained in the resource
     *
     * @return EntityBody
     * @throws InvalidArgumentException if the $resource arg is not a resource or string
     */
    public static function factory($resource = '', $size = null)
    {
        if ($resource instanceof EntityBodyInterface) {
            return $resource;
        }

        switch (gettype($resource)) {
            case 'string':
                return self::fromString($resource);
            case 'resource':
                return new static($resource, $size);
            case 'object':
                if (method_exists($resource, '__toString')) {
                    return self::fromString((string) $resource);
                }
                break;
            case 'array':
                return self::fromString(http_build_query($resource));
        }

        throw new InvalidArgumentException('Invalid resource type');
    }

    public function setRewindFunction($callable)
    {
        if (!is_callable($callable)) {
            throw new InvalidArgumentException('Must specify a callable');
        }

        $this->rewindFunction = $callable;

        return $this;
    }

    public function rewind()
    {
        return $this->rewindFunction ? call_user_func($this->rewindFunction, $this) : parent::rewind();
    }

    /**
     * Create a new EntityBody from a string
     *
     * @param string $string String of data
     *
     * @return EntityBody
     */
    public static function fromString($string)
    {
        $stream = fopen('php://temp', 'r+');
        if ($string !== '') {
            fwrite($stream, $string);
            rewind($stream);
        }

        return new static($stream);
    }

    public function compress($filter = 'zlib.deflate')
    {
        $result = $this->handleCompression($filter);
        $this->contentEncoding = $result ? $filter : false;

        return $result;
    }

    public function uncompress($filter = 'zlib.inflate')
    {
        $offsetStart = 0;

        // When inflating gzipped data, the first 10 bytes must be stripped
        // if a gzip header is present
        if ($filter == 'zlib.inflate') {
            // @codeCoverageIgnoreStart
            if (!$this->isReadable() || ($this->isConsumed() && !$this->isSeekable())) {
                return false;
            }
            // @codeCoverageIgnoreEnd
            if (stream_get_contents($this->stream, 3, 0) === "\x1f\x8b\x08") {
                $offsetStart = 10;
            }
        }

        $this->contentEncoding = false;

        return $this->handleCompression($filter, $offsetStart);
    }

    public function getContentLength()
    {
        return $this->getSize();
    }

    public function getContentType()
    {
        return $this->getUri() ? Mimetypes::getInstance()->fromFilename($this->getUri()) : null;
    }

    public function getContentMd5($rawOutput = false, $base64Encode = false)
    {
        if ($hash = self::getHash($this, 'md5', $rawOutput)) {
            return $hash && $base64Encode ? base64_encode($hash) : $hash;
        } else {
            return false;
        }
    }

    /**
     * Calculate the MD5 hash of an entity body
     *
     * @param EntityBodyInterface $body         Entity body to calculate the hash for
     * @param bool                $rawOutput    Whether or not to use raw output
     * @param bool                $base64Encode Whether or not to base64 encode raw output (only if raw output is true)
     *
     * @return bool|string Returns an MD5 string on success or FALSE on failure
     * @deprecated This will be deprecated soon
     * @codeCoverageIgnore
     */
    public static function calculateMd5(EntityBodyInterface $body, $rawOutput = false, $base64Encode = false)
    {
        Version::warn(__CLASS__ . ' is deprecated. Use getContentMd5()');
        return $body->getContentMd5($rawOutput, $base64Encode);
    }

    public function setStreamFilterContentEncoding($streamFilterContentEncoding)
    {
        $this->contentEncoding = $streamFilterContentEncoding;

        return $this;
    }

    public function getContentEncoding()
    {
        return strtr($this->contentEncoding, array(
            'zlib.deflate' => 'gzip',
            'bzip2.compress' => 'compress'
        )) ?: false;
    }

    protected function handleCompression($filter, $offsetStart = 0)
    {
        // @codeCoverageIgnoreStart
        if (!$this->isReadable() || ($this->isConsumed() && !$this->isSeekable())) {
            return false;
        }
        // @codeCoverageIgnoreEnd

        $handle = fopen('php://temp', 'r+');
        $filter = @stream_filter_append($handle, $filter, STREAM_FILTER_WRITE);
        if (!$filter) {
            return false;
        }

        // Seek to the offset start if possible
        $this->seek($offsetStart);
        while ($data = fread($this->stream, 8096)) {
            fwrite($handle, $data);
        }

        fclose($this->stream);
        $this->stream = $handle;
        stream_filter_remove($filter);
        $stat = fstat($this->stream);
        $this->size = $stat['size'];
        $this->rebuildCache();
        $this->seek(0);

        // Remove any existing rewind function as the underlying stream has been replaced
        $this->rewindFunction = null;

        return true;
    }
}
vendor/guzzle/guzzle/src/Guzzle/Http/Mimetypes.php000064400000122211151327705700016362 0ustar00<?php

namespace Guzzle\Http;

/**
 * Provides mappings of file extensions to mimetypes
 * @link http://svn.apache.org/repos/asf/httpd/httpd/branches/1.3.x/conf/mime.types
 */
class Mimetypes
{
    /** @var self */
    protected static $instance;

    /** @var array Mapping of extension to mimetype */
    protected $mimetypes = array(
        '3dml' => 'text/vnd.in3d.3dml',
        '3g2' => 'video/3gpp2',
        '3gp' => 'video/3gpp',
        '7z' => 'application/x-7z-compressed',
        'aab' => 'application/x-authorware-bin',
        'aac' => 'audio/x-aac',
        'aam' => 'application/x-authorware-map',
        'aas' => 'application/x-authorware-seg',
        'abw' => 'application/x-abiword',
        'ac' => 'application/pkix-attr-cert',
        'acc' => 'application/vnd.americandynamics.acc',
        'ace' => 'application/x-ace-compressed',
        'acu' => 'application/vnd.acucobol',
        'acutc' => 'application/vnd.acucorp',
        'adp' => 'audio/adpcm',
        'aep' => 'application/vnd.audiograph',
        'afm' => 'application/x-font-type1',
        'afp' => 'application/vnd.ibm.modcap',
        'ahead' => 'application/vnd.ahead.space',
        'ai' => 'application/postscript',
        'aif' => 'audio/x-aiff',
        'aifc' => 'audio/x-aiff',
        'aiff' => 'audio/x-aiff',
        'air' => 'application/vnd.adobe.air-application-installer-package+zip',
        'ait' => 'application/vnd.dvb.ait',
        'ami' => 'application/vnd.amiga.ami',
        'apk' => 'application/vnd.android.package-archive',
        'application' => 'application/x-ms-application',
        'apr' => 'application/vnd.lotus-approach',
        'asa' => 'text/plain',
        'asax' => 'application/octet-stream',
        'asc' => 'application/pgp-signature',
        'ascx' => 'text/plain',
        'asf' => 'video/x-ms-asf',
        'ashx' => 'text/plain',
        'asm' => 'text/x-asm',
        'asmx' => 'text/plain',
        'aso' => 'application/vnd.accpac.simply.aso',
        'asp' => 'text/plain',
        'aspx' => 'text/plain',
        'asx' => 'video/x-ms-asf',
        'atc' => 'application/vnd.acucorp',
        'atom' => 'application/atom+xml',
        'atomcat' => 'application/atomcat+xml',
        'atomsvc' => 'application/atomsvc+xml',
        'atx' => 'application/vnd.antix.game-component',
        'au' => 'audio/basic',
        'avi' => 'video/x-msvideo',
        'aw' => 'application/applixware',
        'axd' => 'text/plain',
        'azf' => 'application/vnd.airzip.filesecure.azf',
        'azs' => 'application/vnd.airzip.filesecure.azs',
        'azw' => 'application/vnd.amazon.ebook',
        'bat' => 'application/x-msdownload',
        'bcpio' => 'application/x-bcpio',
        'bdf' => 'application/x-font-bdf',
        'bdm' => 'application/vnd.syncml.dm+wbxml',
        'bed' => 'application/vnd.realvnc.bed',
        'bh2' => 'application/vnd.fujitsu.oasysprs',
        'bin' => 'application/octet-stream',
        'bmi' => 'application/vnd.bmi',
        'bmp' => 'image/bmp',
        'book' => 'application/vnd.framemaker',
        'box' => 'application/vnd.previewsystems.box',
        'boz' => 'application/x-bzip2',
        'bpk' => 'application/octet-stream',
        'btif' => 'image/prs.btif',
        'bz' => 'application/x-bzip',
        'bz2' => 'application/x-bzip2',
        'c' => 'text/x-c',
        'c11amc' => 'application/vnd.cluetrust.cartomobile-config',
        'c11amz' => 'application/vnd.cluetrust.cartomobile-config-pkg',
        'c4d' => 'application/vnd.clonk.c4group',
        'c4f' => 'application/vnd.clonk.c4group',
        'c4g' => 'application/vnd.clonk.c4group',
        'c4p' => 'application/vnd.clonk.c4group',
        'c4u' => 'application/vnd.clonk.c4group',
        'cab' => 'application/vnd.ms-cab-compressed',
        'car' => 'application/vnd.curl.car',
        'cat' => 'application/vnd.ms-pki.seccat',
        'cc' => 'text/x-c',
        'cct' => 'application/x-director',
        'ccxml' => 'application/ccxml+xml',
        'cdbcmsg' => 'application/vnd.contact.cmsg',
        'cdf' => 'application/x-netcdf',
        'cdkey' => 'application/vnd.mediastation.cdkey',
        'cdmia' => 'application/cdmi-capability',
        'cdmic' => 'application/cdmi-container',
        'cdmid' => 'application/cdmi-domain',
        'cdmio' => 'application/cdmi-object',
        'cdmiq' => 'application/cdmi-queue',
        'cdx' => 'chemical/x-cdx',
        'cdxml' => 'application/vnd.chemdraw+xml',
        'cdy' => 'application/vnd.cinderella',
        'cer' => 'application/pkix-cert',
        'cfc' => 'application/x-coldfusion',
        'cfm' => 'application/x-coldfusion',
        'cgm' => 'image/cgm',
        'chat' => 'application/x-chat',
        'chm' => 'application/vnd.ms-htmlhelp',
        'chrt' => 'application/vnd.kde.kchart',
        'cif' => 'chemical/x-cif',
        'cii' => 'application/vnd.anser-web-certificate-issue-initiation',
        'cil' => 'application/vnd.ms-artgalry',
        'cla' => 'application/vnd.claymore',
        'class' => 'application/java-vm',
        'clkk' => 'application/vnd.crick.clicker.keyboard',
        'clkp' => 'application/vnd.crick.clicker.palette',
        'clkt' => 'application/vnd.crick.clicker.template',
        'clkw' => 'application/vnd.crick.clicker.wordbank',
        'clkx' => 'application/vnd.crick.clicker',
        'clp' => 'application/x-msclip',
        'cmc' => 'application/vnd.cosmocaller',
        'cmdf' => 'chemical/x-cmdf',
        'cml' => 'chemical/x-cml',
        'cmp' => 'application/vnd.yellowriver-custom-menu',
        'cmx' => 'image/x-cmx',
        'cod' => 'application/vnd.rim.cod',
        'com' => 'application/x-msdownload',
        'conf' => 'text/plain',
        'cpio' => 'application/x-cpio',
        'cpp' => 'text/x-c',
        'cpt' => 'application/mac-compactpro',
        'crd' => 'application/x-mscardfile',
        'crl' => 'application/pkix-crl',
        'crt' => 'application/x-x509-ca-cert',
        'cryptonote' => 'application/vnd.rig.cryptonote',
        'cs' => 'text/plain',
        'csh' => 'application/x-csh',
        'csml' => 'chemical/x-csml',
        'csp' => 'application/vnd.commonspace',
        'css' => 'text/css',
        'cst' => 'application/x-director',
        'csv' => 'text/csv',
        'cu' => 'application/cu-seeme',
        'curl' => 'text/vnd.curl',
        'cww' => 'application/prs.cww',
        'cxt' => 'application/x-director',
        'cxx' => 'text/x-c',
        'dae' => 'model/vnd.collada+xml',
        'daf' => 'application/vnd.mobius.daf',
        'dataless' => 'application/vnd.fdsn.seed',
        'davmount' => 'application/davmount+xml',
        'dcr' => 'application/x-director',
        'dcurl' => 'text/vnd.curl.dcurl',
        'dd2' => 'application/vnd.oma.dd2+xml',
        'ddd' => 'application/vnd.fujixerox.ddd',
        'deb' => 'application/x-debian-package',
        'def' => 'text/plain',
        'deploy' => 'application/octet-stream',
        'der' => 'application/x-x509-ca-cert',
        'dfac' => 'application/vnd.dreamfactory',
        'dic' => 'text/x-c',
        'dir' => 'application/x-director',
        'dis' => 'application/vnd.mobius.dis',
        'dist' => 'application/octet-stream',
        'distz' => 'application/octet-stream',
        'djv' => 'image/vnd.djvu',
        'djvu' => 'image/vnd.djvu',
        'dll' => 'application/x-msdownload',
        'dmg' => 'application/octet-stream',
        'dms' => 'application/octet-stream',
        'dna' => 'application/vnd.dna',
        'doc' => 'application/msword',
        'docm' => 'application/vnd.ms-word.document.macroenabled.12',
        'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
        'dot' => 'application/msword',
        'dotm' => 'application/vnd.ms-word.template.macroenabled.12',
        'dotx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.template',
        'dp' => 'application/vnd.osgi.dp',
        'dpg' => 'application/vnd.dpgraph',
        'dra' => 'audio/vnd.dra',
        'dsc' => 'text/prs.lines.tag',
        'dssc' => 'application/dssc+der',
        'dtb' => 'application/x-dtbook+xml',
        'dtd' => 'application/xml-dtd',
        'dts' => 'audio/vnd.dts',
        'dtshd' => 'audio/vnd.dts.hd',
        'dump' => 'application/octet-stream',
        'dvi' => 'application/x-dvi',
        'dwf' => 'model/vnd.dwf',
        'dwg' => 'image/vnd.dwg',
        'dxf' => 'image/vnd.dxf',
        'dxp' => 'application/vnd.spotfire.dxp',
        'dxr' => 'application/x-director',
        'ecelp4800' => 'audio/vnd.nuera.ecelp4800',
        'ecelp7470' => 'audio/vnd.nuera.ecelp7470',
        'ecelp9600' => 'audio/vnd.nuera.ecelp9600',
        'ecma' => 'application/ecmascript',
        'edm' => 'application/vnd.novadigm.edm',
        'edx' => 'application/vnd.novadigm.edx',
        'efif' => 'application/vnd.picsel',
        'ei6' => 'application/vnd.pg.osasli',
        'elc' => 'application/octet-stream',
        'eml' => 'message/rfc822',
        'emma' => 'application/emma+xml',
        'eol' => 'audio/vnd.digital-winds',
        'eot' => 'application/vnd.ms-fontobject',
        'eps' => 'application/postscript',
        'epub' => 'application/epub+zip',
        'es3' => 'application/vnd.eszigno3+xml',
        'esf' => 'application/vnd.epson.esf',
        'et3' => 'application/vnd.eszigno3+xml',
        'etx' => 'text/x-setext',
        'exe' => 'application/x-msdownload',
        'exi' => 'application/exi',
        'ext' => 'application/vnd.novadigm.ext',
        'ez' => 'application/andrew-inset',
        'ez2' => 'application/vnd.ezpix-album',
        'ez3' => 'application/vnd.ezpix-package',
        'f' => 'text/x-fortran',
        'f4v' => 'video/x-f4v',
        'f77' => 'text/x-fortran',
        'f90' => 'text/x-fortran',
        'fbs' => 'image/vnd.fastbidsheet',
        'fcs' => 'application/vnd.isac.fcs',
        'fdf' => 'application/vnd.fdf',
        'fe_launch' => 'application/vnd.denovo.fcselayout-link',
        'fg5' => 'application/vnd.fujitsu.oasysgp',
        'fgd' => 'application/x-director',
        'fh' => 'image/x-freehand',
        'fh4' => 'image/x-freehand',
        'fh5' => 'image/x-freehand',
        'fh7' => 'image/x-freehand',
        'fhc' => 'image/x-freehand',
        'fig' => 'application/x-xfig',
        'fli' => 'video/x-fli',
        'flo' => 'application/vnd.micrografx.flo',
        'flv' => 'video/x-flv',
        'flw' => 'application/vnd.kde.kivio',
        'flx' => 'text/vnd.fmi.flexstor',
        'fly' => 'text/vnd.fly',
        'fm' => 'application/vnd.framemaker',
        'fnc' => 'application/vnd.frogans.fnc',
        'for' => 'text/x-fortran',
        'fpx' => 'image/vnd.fpx',
        'frame' => 'application/vnd.framemaker',
        'fsc' => 'application/vnd.fsc.weblaunch',
        'fst' => 'image/vnd.fst',
        'ftc' => 'application/vnd.fluxtime.clip',
        'fti' => 'application/vnd.anser-web-funds-transfer-initiation',
        'fvt' => 'video/vnd.fvt',
        'fxp' => 'application/vnd.adobe.fxp',
        'fxpl' => 'application/vnd.adobe.fxp',
        'fzs' => 'application/vnd.fuzzysheet',
        'g2w' => 'application/vnd.geoplan',
        'g3' => 'image/g3fax',
        'g3w' => 'application/vnd.geospace',
        'gac' => 'application/vnd.groove-account',
        'gdl' => 'model/vnd.gdl',
        'geo' => 'application/vnd.dynageo',
        'gex' => 'application/vnd.geometry-explorer',
        'ggb' => 'application/vnd.geogebra.file',
        'ggt' => 'application/vnd.geogebra.tool',
        'ghf' => 'application/vnd.groove-help',
        'gif' => 'image/gif',
        'gim' => 'application/vnd.groove-identity-message',
        'gmx' => 'application/vnd.gmx',
        'gnumeric' => 'application/x-gnumeric',
        'gph' => 'application/vnd.flographit',
        'gqf' => 'application/vnd.grafeq',
        'gqs' => 'application/vnd.grafeq',
        'gram' => 'application/srgs',
        'gre' => 'application/vnd.geometry-explorer',
        'grv' => 'application/vnd.groove-injector',
        'grxml' => 'application/srgs+xml',
        'gsf' => 'application/x-font-ghostscript',
        'gtar' => 'application/x-gtar',
        'gtm' => 'application/vnd.groove-tool-message',
        'gtw' => 'model/vnd.gtw',
        'gv' => 'text/vnd.graphviz',
        'gxt' => 'application/vnd.geonext',
        'h' => 'text/x-c',
        'h261' => 'video/h261',
        'h263' => 'video/h263',
        'h264' => 'video/h264',
        'hal' => 'application/vnd.hal+xml',
        'hbci' => 'application/vnd.hbci',
        'hdf' => 'application/x-hdf',
        'hh' => 'text/x-c',
        'hlp' => 'application/winhlp',
        'hpgl' => 'application/vnd.hp-hpgl',
        'hpid' => 'application/vnd.hp-hpid',
        'hps' => 'application/vnd.hp-hps',
        'hqx' => 'application/mac-binhex40',
        'hta' => 'application/octet-stream',
        'htc' => 'text/html',
        'htke' => 'application/vnd.kenameaapp',
        'htm' => 'text/html',
        'html' => 'text/html',
        'hvd' => 'application/vnd.yamaha.hv-dic',
        'hvp' => 'application/vnd.yamaha.hv-voice',
        'hvs' => 'application/vnd.yamaha.hv-script',
        'i2g' => 'application/vnd.intergeo',
        'icc' => 'application/vnd.iccprofile',
        'ice' => 'x-conference/x-cooltalk',
        'icm' => 'application/vnd.iccprofile',
        'ico' => 'image/x-icon',
        'ics' => 'text/calendar',
        'ief' => 'image/ief',
        'ifb' => 'text/calendar',
        'ifm' => 'application/vnd.shana.informed.formdata',
        'iges' => 'model/iges',
        'igl' => 'application/vnd.igloader',
        'igm' => 'application/vnd.insors.igm',
        'igs' => 'model/iges',
        'igx' => 'application/vnd.micrografx.igx',
        'iif' => 'application/vnd.shana.informed.interchange',
        'imp' => 'application/vnd.accpac.simply.imp',
        'ims' => 'application/vnd.ms-ims',
        'in' => 'text/plain',
        'ini' => 'text/plain',
        'ipfix' => 'application/ipfix',
        'ipk' => 'application/vnd.shana.informed.package',
        'irm' => 'application/vnd.ibm.rights-management',
        'irp' => 'application/vnd.irepository.package+xml',
        'iso' => 'application/octet-stream',
        'itp' => 'application/vnd.shana.informed.formtemplate',
        'ivp' => 'application/vnd.immervision-ivp',
        'ivu' => 'application/vnd.immervision-ivu',
        'jad' => 'text/vnd.sun.j2me.app-descriptor',
        'jam' => 'application/vnd.jam',
        'jar' => 'application/java-archive',
        'java' => 'text/x-java-source',
        'jisp' => 'application/vnd.jisp',
        'jlt' => 'application/vnd.hp-jlyt',
        'jnlp' => 'application/x-java-jnlp-file',
        'joda' => 'application/vnd.joost.joda-archive',
        'jpe' => 'image/jpeg',
        'jpeg' => 'image/jpeg',
        'jpg' => 'image/jpeg',
        'jpgm' => 'video/jpm',
        'jpgv' => 'video/jpeg',
        'jpm' => 'video/jpm',
        'js' => 'text/javascript',
        'json' => 'application/json',
        'kar' => 'audio/midi',
        'karbon' => 'application/vnd.kde.karbon',
        'kfo' => 'application/vnd.kde.kformula',
        'kia' => 'application/vnd.kidspiration',
        'kml' => 'application/vnd.google-earth.kml+xml',
        'kmz' => 'application/vnd.google-earth.kmz',
        'kne' => 'application/vnd.kinar',
        'knp' => 'application/vnd.kinar',
        'kon' => 'application/vnd.kde.kontour',
        'kpr' => 'application/vnd.kde.kpresenter',
        'kpt' => 'application/vnd.kde.kpresenter',
        'ksp' => 'application/vnd.kde.kspread',
        'ktr' => 'application/vnd.kahootz',
        'ktx' => 'image/ktx',
        'ktz' => 'application/vnd.kahootz',
        'kwd' => 'application/vnd.kde.kword',
        'kwt' => 'application/vnd.kde.kword',
        'lasxml' => 'application/vnd.las.las+xml',
        'latex' => 'application/x-latex',
        'lbd' => 'application/vnd.llamagraphics.life-balance.desktop',
        'lbe' => 'application/vnd.llamagraphics.life-balance.exchange+xml',
        'les' => 'application/vnd.hhe.lesson-player',
        'lha' => 'application/octet-stream',
        'link66' => 'application/vnd.route66.link66+xml',
        'list' => 'text/plain',
        'list3820' => 'application/vnd.ibm.modcap',
        'listafp' => 'application/vnd.ibm.modcap',
        'log' => 'text/plain',
        'lostxml' => 'application/lost+xml',
        'lrf' => 'application/octet-stream',
        'lrm' => 'application/vnd.ms-lrm',
        'ltf' => 'application/vnd.frogans.ltf',
        'lvp' => 'audio/vnd.lucent.voice',
        'lwp' => 'application/vnd.lotus-wordpro',
        'lzh' => 'application/octet-stream',
        'm13' => 'application/x-msmediaview',
        'm14' => 'application/x-msmediaview',
        'm1v' => 'video/mpeg',
        'm21' => 'application/mp21',
        'm2a' => 'audio/mpeg',
        'm2v' => 'video/mpeg',
        'm3a' => 'audio/mpeg',
        'm3u' => 'audio/x-mpegurl',
        'm3u8' => 'application/vnd.apple.mpegurl',
        'm4a' => 'audio/mp4',
        'm4u' => 'video/vnd.mpegurl',
        'm4v' => 'video/mp4',
        'ma' => 'application/mathematica',
        'mads' => 'application/mads+xml',
        'mag' => 'application/vnd.ecowin.chart',
        'maker' => 'application/vnd.framemaker',
        'man' => 'text/troff',
        'mathml' => 'application/mathml+xml',
        'mb' => 'application/mathematica',
        'mbk' => 'application/vnd.mobius.mbk',
        'mbox' => 'application/mbox',
        'mc1' => 'application/vnd.medcalcdata',
        'mcd' => 'application/vnd.mcd',
        'mcurl' => 'text/vnd.curl.mcurl',
        'mdb' => 'application/x-msaccess',
        'mdi' => 'image/vnd.ms-modi',
        'me' => 'text/troff',
        'mesh' => 'model/mesh',
        'meta4' => 'application/metalink4+xml',
        'mets' => 'application/mets+xml',
        'mfm' => 'application/vnd.mfmp',
        'mgp' => 'application/vnd.osgeo.mapguide.package',
        'mgz' => 'application/vnd.proteus.magazine',
        'mid' => 'audio/midi',
        'midi' => 'audio/midi',
        'mif' => 'application/vnd.mif',
        'mime' => 'message/rfc822',
        'mj2' => 'video/mj2',
        'mjp2' => 'video/mj2',
        'mlp' => 'application/vnd.dolby.mlp',
        'mmd' => 'application/vnd.chipnuts.karaoke-mmd',
        'mmf' => 'application/vnd.smaf',
        'mmr' => 'image/vnd.fujixerox.edmics-mmr',
        'mny' => 'application/x-msmoney',
        'mobi' => 'application/x-mobipocket-ebook',
        'mods' => 'application/mods+xml',
        'mov' => 'video/quicktime',
        'movie' => 'video/x-sgi-movie',
        'mp2' => 'audio/mpeg',
        'mp21' => 'application/mp21',
        'mp2a' => 'audio/mpeg',
        'mp3' => 'audio/mpeg',
        'mp4' => 'video/mp4',
        'mp4a' => 'audio/mp4',
        'mp4s' => 'application/mp4',
        'mp4v' => 'video/mp4',
        'mpc' => 'application/vnd.mophun.certificate',
        'mpe' => 'video/mpeg',
        'mpeg' => 'video/mpeg',
        'mpg' => 'video/mpeg',
        'mpg4' => 'video/mp4',
        'mpga' => 'audio/mpeg',
        'mpkg' => 'application/vnd.apple.installer+xml',
        'mpm' => 'application/vnd.blueice.multipass',
        'mpn' => 'application/vnd.mophun.application',
        'mpp' => 'application/vnd.ms-project',
        'mpt' => 'application/vnd.ms-project',
        'mpy' => 'application/vnd.ibm.minipay',
        'mqy' => 'application/vnd.mobius.mqy',
        'mrc' => 'application/marc',
        'mrcx' => 'application/marcxml+xml',
        'ms' => 'text/troff',
        'mscml' => 'application/mediaservercontrol+xml',
        'mseed' => 'application/vnd.fdsn.mseed',
        'mseq' => 'application/vnd.mseq',
        'msf' => 'application/vnd.epson.msf',
        'msh' => 'model/mesh',
        'msi' => 'application/x-msdownload',
        'msl' => 'application/vnd.mobius.msl',
        'msty' => 'application/vnd.muvee.style',
        'mts' => 'model/vnd.mts',
        'mus' => 'application/vnd.musician',
        'musicxml' => 'application/vnd.recordare.musicxml+xml',
        'mvb' => 'application/x-msmediaview',
        'mwf' => 'application/vnd.mfer',
        'mxf' => 'application/mxf',
        'mxl' => 'application/vnd.recordare.musicxml',
        'mxml' => 'application/xv+xml',
        'mxs' => 'application/vnd.triscape.mxs',
        'mxu' => 'video/vnd.mpegurl',
        'n-gage' => 'application/vnd.nokia.n-gage.symbian.install',
        'n3' => 'text/n3',
        'nb' => 'application/mathematica',
        'nbp' => 'application/vnd.wolfram.player',
        'nc' => 'application/x-netcdf',
        'ncx' => 'application/x-dtbncx+xml',
        'ngdat' => 'application/vnd.nokia.n-gage.data',
        'nlu' => 'application/vnd.neurolanguage.nlu',
        'nml' => 'application/vnd.enliven',
        'nnd' => 'application/vnd.noblenet-directory',
        'nns' => 'application/vnd.noblenet-sealer',
        'nnw' => 'application/vnd.noblenet-web',
        'npx' => 'image/vnd.net-fpx',
        'nsf' => 'application/vnd.lotus-notes',
        'oa2' => 'application/vnd.fujitsu.oasys2',
        'oa3' => 'application/vnd.fujitsu.oasys3',
        'oas' => 'application/vnd.fujitsu.oasys',
        'obd' => 'application/x-msbinder',
        'oda' => 'application/oda',
        'odb' => 'application/vnd.oasis.opendocument.database',
        'odc' => 'application/vnd.oasis.opendocument.chart',
        'odf' => 'application/vnd.oasis.opendocument.formula',
        'odft' => 'application/vnd.oasis.opendocument.formula-template',
        'odg' => 'application/vnd.oasis.opendocument.graphics',
        'odi' => 'application/vnd.oasis.opendocument.image',
        'odm' => 'application/vnd.oasis.opendocument.text-master',
        'odp' => 'application/vnd.oasis.opendocument.presentation',
        'ods' => 'application/vnd.oasis.opendocument.spreadsheet',
        'odt' => 'application/vnd.oasis.opendocument.text',
        'oga' => 'audio/ogg',
        'ogg' => 'audio/ogg',
        'ogv' => 'video/ogg',
        'ogx' => 'application/ogg',
        'onepkg' => 'application/onenote',
        'onetmp' => 'application/onenote',
        'onetoc' => 'application/onenote',
        'onetoc2' => 'application/onenote',
        'opf' => 'application/oebps-package+xml',
        'oprc' => 'application/vnd.palm',
        'org' => 'application/vnd.lotus-organizer',
        'osf' => 'application/vnd.yamaha.openscoreformat',
        'osfpvg' => 'application/vnd.yamaha.openscoreformat.osfpvg+xml',
        'otc' => 'application/vnd.oasis.opendocument.chart-template',
        'otf' => 'application/x-font-otf',
        'otg' => 'application/vnd.oasis.opendocument.graphics-template',
        'oth' => 'application/vnd.oasis.opendocument.text-web',
        'oti' => 'application/vnd.oasis.opendocument.image-template',
        'otp' => 'application/vnd.oasis.opendocument.presentation-template',
        'ots' => 'application/vnd.oasis.opendocument.spreadsheet-template',
        'ott' => 'application/vnd.oasis.opendocument.text-template',
        'oxt' => 'application/vnd.openofficeorg.extension',
        'p' => 'text/x-pascal',
        'p10' => 'application/pkcs10',
        'p12' => 'application/x-pkcs12',
        'p7b' => 'application/x-pkcs7-certificates',
        'p7c' => 'application/pkcs7-mime',
        'p7m' => 'application/pkcs7-mime',
        'p7r' => 'application/x-pkcs7-certreqresp',
        'p7s' => 'application/pkcs7-signature',
        'p8' => 'application/pkcs8',
        'pas' => 'text/x-pascal',
        'paw' => 'application/vnd.pawaafile',
        'pbd' => 'application/vnd.powerbuilder6',
        'pbm' => 'image/x-portable-bitmap',
        'pcf' => 'application/x-font-pcf',
        'pcl' => 'application/vnd.hp-pcl',
        'pclxl' => 'application/vnd.hp-pclxl',
        'pct' => 'image/x-pict',
        'pcurl' => 'application/vnd.curl.pcurl',
        'pcx' => 'image/x-pcx',
        'pdb' => 'application/vnd.palm',
        'pdf' => 'application/pdf',
        'pfa' => 'application/x-font-type1',
        'pfb' => 'application/x-font-type1',
        'pfm' => 'application/x-font-type1',
        'pfr' => 'application/font-tdpfr',
        'pfx' => 'application/x-pkcs12',
        'pgm' => 'image/x-portable-graymap',
        'pgn' => 'application/x-chess-pgn',
        'pgp' => 'application/pgp-encrypted',
        'php' => 'text/x-php',
        'phps' => 'application/x-httpd-phps',
        'pic' => 'image/x-pict',
        'pkg' => 'application/octet-stream',
        'pki' => 'application/pkixcmp',
        'pkipath' => 'application/pkix-pkipath',
        'plb' => 'application/vnd.3gpp.pic-bw-large',
        'plc' => 'application/vnd.mobius.plc',
        'plf' => 'application/vnd.pocketlearn',
        'pls' => 'application/pls+xml',
        'pml' => 'application/vnd.ctc-posml',
        'png' => 'image/png',
        'pnm' => 'image/x-portable-anymap',
        'portpkg' => 'application/vnd.macports.portpkg',
        'pot' => 'application/vnd.ms-powerpoint',
        'potm' => 'application/vnd.ms-powerpoint.template.macroenabled.12',
        'potx' => 'application/vnd.openxmlformats-officedocument.presentationml.template',
        'ppam' => 'application/vnd.ms-powerpoint.addin.macroenabled.12',
        'ppd' => 'application/vnd.cups-ppd',
        'ppm' => 'image/x-portable-pixmap',
        'pps' => 'application/vnd.ms-powerpoint',
        'ppsm' => 'application/vnd.ms-powerpoint.slideshow.macroenabled.12',
        'ppsx' => 'application/vnd.openxmlformats-officedocument.presentationml.slideshow',
        'ppt' => 'application/vnd.ms-powerpoint',
        'pptm' => 'application/vnd.ms-powerpoint.presentation.macroenabled.12',
        'pptx' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
        'pqa' => 'application/vnd.palm',
        'prc' => 'application/x-mobipocket-ebook',
        'pre' => 'application/vnd.lotus-freelance',
        'prf' => 'application/pics-rules',
        'ps' => 'application/postscript',
        'psb' => 'application/vnd.3gpp.pic-bw-small',
        'psd' => 'image/vnd.adobe.photoshop',
        'psf' => 'application/x-font-linux-psf',
        'pskcxml' => 'application/pskc+xml',
        'ptid' => 'application/vnd.pvi.ptid1',
        'pub' => 'application/x-mspublisher',
        'pvb' => 'application/vnd.3gpp.pic-bw-var',
        'pwn' => 'application/vnd.3m.post-it-notes',
        'pya' => 'audio/vnd.ms-playready.media.pya',
        'pyv' => 'video/vnd.ms-playready.media.pyv',
        'qam' => 'application/vnd.epson.quickanime',
        'qbo' => 'application/vnd.intu.qbo',
        'qfx' => 'application/vnd.intu.qfx',
        'qps' => 'application/vnd.publishare-delta-tree',
        'qt' => 'video/quicktime',
        'qwd' => 'application/vnd.quark.quarkxpress',
        'qwt' => 'application/vnd.quark.quarkxpress',
        'qxb' => 'application/vnd.quark.quarkxpress',
        'qxd' => 'application/vnd.quark.quarkxpress',
        'qxl' => 'application/vnd.quark.quarkxpress',
        'qxt' => 'application/vnd.quark.quarkxpress',
        'ra' => 'audio/x-pn-realaudio',
        'ram' => 'audio/x-pn-realaudio',
        'rar' => 'application/x-rar-compressed',
        'ras' => 'image/x-cmu-raster',
        'rb' => 'text/plain',
        'rcprofile' => 'application/vnd.ipunplugged.rcprofile',
        'rdf' => 'application/rdf+xml',
        'rdz' => 'application/vnd.data-vision.rdz',
        'rep' => 'application/vnd.businessobjects',
        'res' => 'application/x-dtbresource+xml',
        'resx' => 'text/xml',
        'rgb' => 'image/x-rgb',
        'rif' => 'application/reginfo+xml',
        'rip' => 'audio/vnd.rip',
        'rl' => 'application/resource-lists+xml',
        'rlc' => 'image/vnd.fujixerox.edmics-rlc',
        'rld' => 'application/resource-lists-diff+xml',
        'rm' => 'application/vnd.rn-realmedia',
        'rmi' => 'audio/midi',
        'rmp' => 'audio/x-pn-realaudio-plugin',
        'rms' => 'application/vnd.jcp.javame.midlet-rms',
        'rnc' => 'application/relax-ng-compact-syntax',
        'roff' => 'text/troff',
        'rp9' => 'application/vnd.cloanto.rp9',
        'rpss' => 'application/vnd.nokia.radio-presets',
        'rpst' => 'application/vnd.nokia.radio-preset',
        'rq' => 'application/sparql-query',
        'rs' => 'application/rls-services+xml',
        'rsd' => 'application/rsd+xml',
        'rss' => 'application/rss+xml',
        'rtf' => 'application/rtf',
        'rtx' => 'text/richtext',
        's' => 'text/x-asm',
        'saf' => 'application/vnd.yamaha.smaf-audio',
        'sbml' => 'application/sbml+xml',
        'sc' => 'application/vnd.ibm.secure-container',
        'scd' => 'application/x-msschedule',
        'scm' => 'application/vnd.lotus-screencam',
        'scq' => 'application/scvp-cv-request',
        'scs' => 'application/scvp-cv-response',
        'scurl' => 'text/vnd.curl.scurl',
        'sda' => 'application/vnd.stardivision.draw',
        'sdc' => 'application/vnd.stardivision.calc',
        'sdd' => 'application/vnd.stardivision.impress',
        'sdkd' => 'application/vnd.solent.sdkm+xml',
        'sdkm' => 'application/vnd.solent.sdkm+xml',
        'sdp' => 'application/sdp',
        'sdw' => 'application/vnd.stardivision.writer',
        'see' => 'application/vnd.seemail',
        'seed' => 'application/vnd.fdsn.seed',
        'sema' => 'application/vnd.sema',
        'semd' => 'application/vnd.semd',
        'semf' => 'application/vnd.semf',
        'ser' => 'application/java-serialized-object',
        'setpay' => 'application/set-payment-initiation',
        'setreg' => 'application/set-registration-initiation',
        'sfd-hdstx' => 'application/vnd.hydrostatix.sof-data',
        'sfs' => 'application/vnd.spotfire.sfs',
        'sgl' => 'application/vnd.stardivision.writer-global',
        'sgm' => 'text/sgml',
        'sgml' => 'text/sgml',
        'sh' => 'application/x-sh',
        'shar' => 'application/x-shar',
        'shf' => 'application/shf+xml',
        'sig' => 'application/pgp-signature',
        'silo' => 'model/mesh',
        'sis' => 'application/vnd.symbian.install',
        'sisx' => 'application/vnd.symbian.install',
        'sit' => 'application/x-stuffit',
        'sitx' => 'application/x-stuffitx',
        'skd' => 'application/vnd.koan',
        'skm' => 'application/vnd.koan',
        'skp' => 'application/vnd.koan',
        'skt' => 'application/vnd.koan',
        'sldm' => 'application/vnd.ms-powerpoint.slide.macroenabled.12',
        'sldx' => 'application/vnd.openxmlformats-officedocument.presentationml.slide',
        'slt' => 'application/vnd.epson.salt',
        'sm' => 'application/vnd.stepmania.stepchart',
        'smf' => 'application/vnd.stardivision.math',
        'smi' => 'application/smil+xml',
        'smil' => 'application/smil+xml',
        'snd' => 'audio/basic',
        'snf' => 'application/x-font-snf',
        'so' => 'application/octet-stream',
        'spc' => 'application/x-pkcs7-certificates',
        'spf' => 'application/vnd.yamaha.smaf-phrase',
        'spl' => 'application/x-futuresplash',
        'spot' => 'text/vnd.in3d.spot',
        'spp' => 'application/scvp-vp-response',
        'spq' => 'application/scvp-vp-request',
        'spx' => 'audio/ogg',
        'src' => 'application/x-wais-source',
        'sru' => 'application/sru+xml',
        'srx' => 'application/sparql-results+xml',
        'sse' => 'application/vnd.kodak-descriptor',
        'ssf' => 'application/vnd.epson.ssf',
        'ssml' => 'application/ssml+xml',
        'st' => 'application/vnd.sailingtracker.track',
        'stc' => 'application/vnd.sun.xml.calc.template',
        'std' => 'application/vnd.sun.xml.draw.template',
        'stf' => 'application/vnd.wt.stf',
        'sti' => 'application/vnd.sun.xml.impress.template',
        'stk' => 'application/hyperstudio',
        'stl' => 'application/vnd.ms-pki.stl',
        'str' => 'application/vnd.pg.format',
        'stw' => 'application/vnd.sun.xml.writer.template',
        'sub' => 'image/vnd.dvb.subtitle',
        'sus' => 'application/vnd.sus-calendar',
        'susp' => 'application/vnd.sus-calendar',
        'sv4cpio' => 'application/x-sv4cpio',
        'sv4crc' => 'application/x-sv4crc',
        'svc' => 'application/vnd.dvb.service',
        'svd' => 'application/vnd.svd',
        'svg' => 'image/svg+xml',
        'svgz' => 'image/svg+xml',
        'swa' => 'application/x-director',
        'swf' => 'application/x-shockwave-flash',
        'swi' => 'application/vnd.aristanetworks.swi',
        'sxc' => 'application/vnd.sun.xml.calc',
        'sxd' => 'application/vnd.sun.xml.draw',
        'sxg' => 'application/vnd.sun.xml.writer.global',
        'sxi' => 'application/vnd.sun.xml.impress',
        'sxm' => 'application/vnd.sun.xml.math',
        'sxw' => 'application/vnd.sun.xml.writer',
        't' => 'text/troff',
        'tao' => 'application/vnd.tao.intent-module-archive',
        'tar' => 'application/x-tar',
        'tcap' => 'application/vnd.3gpp2.tcap',
        'tcl' => 'application/x-tcl',
        'teacher' => 'application/vnd.smart.teacher',
        'tei' => 'application/tei+xml',
        'teicorpus' => 'application/tei+xml',
        'tex' => 'application/x-tex',
        'texi' => 'application/x-texinfo',
        'texinfo' => 'application/x-texinfo',
        'text' => 'text/plain',
        'tfi' => 'application/thraud+xml',
        'tfm' => 'application/x-tex-tfm',
        'thmx' => 'application/vnd.ms-officetheme',
        'tif' => 'image/tiff',
        'tiff' => 'image/tiff',
        'tmo' => 'application/vnd.tmobile-livetv',
        'torrent' => 'application/x-bittorrent',
        'tpl' => 'application/vnd.groove-tool-template',
        'tpt' => 'application/vnd.trid.tpt',
        'tr' => 'text/troff',
        'tra' => 'application/vnd.trueapp',
        'trm' => 'application/x-msterminal',
        'tsd' => 'application/timestamped-data',
        'tsv' => 'text/tab-separated-values',
        'ttc' => 'application/x-font-ttf',
        'ttf' => 'application/x-font-ttf',
        'ttl' => 'text/turtle',
        'twd' => 'application/vnd.simtech-mindmapper',
        'twds' => 'application/vnd.simtech-mindmapper',
        'txd' => 'application/vnd.genomatix.tuxedo',
        'txf' => 'application/vnd.mobius.txf',
        'txt' => 'text/plain',
        'u32' => 'application/x-authorware-bin',
        'udeb' => 'application/x-debian-package',
        'ufd' => 'application/vnd.ufdl',
        'ufdl' => 'application/vnd.ufdl',
        'umj' => 'application/vnd.umajin',
        'unityweb' => 'application/vnd.unity',
        'uoml' => 'application/vnd.uoml+xml',
        'uri' => 'text/uri-list',
        'uris' => 'text/uri-list',
        'urls' => 'text/uri-list',
        'ustar' => 'application/x-ustar',
        'utz' => 'application/vnd.uiq.theme',
        'uu' => 'text/x-uuencode',
        'uva' => 'audio/vnd.dece.audio',
        'uvd' => 'application/vnd.dece.data',
        'uvf' => 'application/vnd.dece.data',
        'uvg' => 'image/vnd.dece.graphic',
        'uvh' => 'video/vnd.dece.hd',
        'uvi' => 'image/vnd.dece.graphic',
        'uvm' => 'video/vnd.dece.mobile',
        'uvp' => 'video/vnd.dece.pd',
        'uvs' => 'video/vnd.dece.sd',
        'uvt' => 'application/vnd.dece.ttml+xml',
        'uvu' => 'video/vnd.uvvu.mp4',
        'uvv' => 'video/vnd.dece.video',
        'uvva' => 'audio/vnd.dece.audio',
        'uvvd' => 'application/vnd.dece.data',
        'uvvf' => 'application/vnd.dece.data',
        'uvvg' => 'image/vnd.dece.graphic',
        'uvvh' => 'video/vnd.dece.hd',
        'uvvi' => 'image/vnd.dece.graphic',
        'uvvm' => 'video/vnd.dece.mobile',
        'uvvp' => 'video/vnd.dece.pd',
        'uvvs' => 'video/vnd.dece.sd',
        'uvvt' => 'application/vnd.dece.ttml+xml',
        'uvvu' => 'video/vnd.uvvu.mp4',
        'uvvv' => 'video/vnd.dece.video',
        'uvvx' => 'application/vnd.dece.unspecified',
        'uvx' => 'application/vnd.dece.unspecified',
        'vcd' => 'application/x-cdlink',
        'vcf' => 'text/x-vcard',
        'vcg' => 'application/vnd.groove-vcard',
        'vcs' => 'text/x-vcalendar',
        'vcx' => 'application/vnd.vcx',
        'vis' => 'application/vnd.visionary',
        'viv' => 'video/vnd.vivo',
        'vor' => 'application/vnd.stardivision.writer',
        'vox' => 'application/x-authorware-bin',
        'vrml' => 'model/vrml',
        'vsd' => 'application/vnd.visio',
        'vsf' => 'application/vnd.vsf',
        'vss' => 'application/vnd.visio',
        'vst' => 'application/vnd.visio',
        'vsw' => 'application/vnd.visio',
        'vtu' => 'model/vnd.vtu',
        'vxml' => 'application/voicexml+xml',
        'w3d' => 'application/x-director',
        'wad' => 'application/x-doom',
        'wav' => 'audio/x-wav',
        'wax' => 'audio/x-ms-wax',
        'wbmp' => 'image/vnd.wap.wbmp',
        'wbs' => 'application/vnd.criticaltools.wbs+xml',
        'wbxml' => 'application/vnd.wap.wbxml',
        'wcm' => 'application/vnd.ms-works',
        'wdb' => 'application/vnd.ms-works',
        'weba' => 'audio/webm',
        'webm' => 'video/webm',
        'webp' => 'image/webp',
        'wg' => 'application/vnd.pmi.widget',
        'wgt' => 'application/widget',
        'wks' => 'application/vnd.ms-works',
        'wm' => 'video/x-ms-wm',
        'wma' => 'audio/x-ms-wma',
        'wmd' => 'application/x-ms-wmd',
        'wmf' => 'application/x-msmetafile',
        'wml' => 'text/vnd.wap.wml',
        'wmlc' => 'application/vnd.wap.wmlc',
        'wmls' => 'text/vnd.wap.wmlscript',
        'wmlsc' => 'application/vnd.wap.wmlscriptc',
        'wmv' => 'video/x-ms-wmv',
        'wmx' => 'video/x-ms-wmx',
        'wmz' => 'application/x-ms-wmz',
        'woff' => 'application/x-font-woff',
        'wpd' => 'application/vnd.wordperfect',
        'wpl' => 'application/vnd.ms-wpl',
        'wps' => 'application/vnd.ms-works',
        'wqd' => 'application/vnd.wqd',
        'wri' => 'application/x-mswrite',
        'wrl' => 'model/vrml',
        'wsdl' => 'application/wsdl+xml',
        'wspolicy' => 'application/wspolicy+xml',
        'wtb' => 'application/vnd.webturbo',
        'wvx' => 'video/x-ms-wvx',
        'x32' => 'application/x-authorware-bin',
        'x3d' => 'application/vnd.hzn-3d-crossword',
        'xap' => 'application/x-silverlight-app',
        'xar' => 'application/vnd.xara',
        'xbap' => 'application/x-ms-xbap',
        'xbd' => 'application/vnd.fujixerox.docuworks.binder',
        'xbm' => 'image/x-xbitmap',
        'xdf' => 'application/xcap-diff+xml',
        'xdm' => 'application/vnd.syncml.dm+xml',
        'xdp' => 'application/vnd.adobe.xdp+xml',
        'xdssc' => 'application/dssc+xml',
        'xdw' => 'application/vnd.fujixerox.docuworks',
        'xenc' => 'application/xenc+xml',
        'xer' => 'application/patch-ops-error+xml',
        'xfdf' => 'application/vnd.adobe.xfdf',
        'xfdl' => 'application/vnd.xfdl',
        'xht' => 'application/xhtml+xml',
        'xhtml' => 'application/xhtml+xml',
        'xhvml' => 'application/xv+xml',
        'xif' => 'image/vnd.xiff',
        'xla' => 'application/vnd.ms-excel',
        'xlam' => 'application/vnd.ms-excel.addin.macroenabled.12',
        'xlc' => 'application/vnd.ms-excel',
        'xlm' => 'application/vnd.ms-excel',
        'xls' => 'application/vnd.ms-excel',
        'xlsb' => 'application/vnd.ms-excel.sheet.binary.macroenabled.12',
        'xlsm' => 'application/vnd.ms-excel.sheet.macroenabled.12',
        'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
        'xlt' => 'application/vnd.ms-excel',
        'xltm' => 'application/vnd.ms-excel.template.macroenabled.12',
        'xltx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.template',
        'xlw' => 'application/vnd.ms-excel',
        'xml' => 'application/xml',
        'xo' => 'application/vnd.olpc-sugar',
        'xop' => 'application/xop+xml',
        'xpi' => 'application/x-xpinstall',
        'xpm' => 'image/x-xpixmap',
        'xpr' => 'application/vnd.is-xpr',
        'xps' => 'application/vnd.ms-xpsdocument',
        'xpw' => 'application/vnd.intercon.formnet',
        'xpx' => 'application/vnd.intercon.formnet',
        'xsl' => 'application/xml',
        'xslt' => 'application/xslt+xml',
        'xsm' => 'application/vnd.syncml+xml',
        'xspf' => 'application/xspf+xml',
        'xul' => 'application/vnd.mozilla.xul+xml',
        'xvm' => 'application/xv+xml',
        'xvml' => 'application/xv+xml',
        'xwd' => 'image/x-xwindowdump',
        'xyz' => 'chemical/x-xyz',
        'yaml' => 'text/yaml',
        'yang' => 'application/yang',
        'yin' => 'application/yin+xml',
        'yml' => 'text/yaml',
        'zaz' => 'application/vnd.zzazz.deck+xml',
        'zip' => 'application/zip',
        'zir' => 'application/vnd.zul',
        'zirz' => 'application/vnd.zul',
        'zmm' => 'application/vnd.handheld-entertainment+xml'
    );

    /**
     * Get a singleton instance of the class
     *
     * @return self
     * @codeCoverageIgnore
     */
    public static function getInstance()
    {
        if (!self::$instance) {
            self::$instance = new self();
        }

        return self::$instance;
    }

    /**
     * Get a mimetype value from a file extension
     *
     * @param string $extension File extension
     *
     * @return string|null
     *
     */
    public function fromExtension($extension)
    {
        $extension = strtolower($extension);

        return isset($this->mimetypes[$extension]) ? $this->mimetypes[$extension] : null;
    }

    /**
     * Get a mimetype from a filename
     *
     * @param string $filename Filename to generate a mimetype from
     *
     * @return string|null
     */
    public function fromFilename($filename)
    {
        return $this->fromExtension(pathinfo($filename, PATHINFO_EXTENSION));
    }
}
vendor/guzzle/guzzle/src/Guzzle/Http/Curl/CurlHandle.php000064400000037545151327705700017353 0ustar00<?php

namespace Guzzle\Http\Curl;

use Guzzle\Common\Exception\InvalidArgumentException;
use Guzzle\Common\Exception\RuntimeException;
use Guzzle\Common\Collection;
use Guzzle\Http\Message\EntityEnclosingRequest;
use Guzzle\Http\Message\RequestInterface;
use Guzzle\Parser\ParserRegistry;
use Guzzle\Http\Url;

/**
 * Immutable wrapper for a cURL handle
 */
class CurlHandle
{
    const BODY_AS_STRING = 'body_as_string';
    const PROGRESS = 'progress';
    const DEBUG = 'debug';

    /** @var Collection Curl options */
    protected $options;

    /** @var resource Curl resource handle */
    protected $handle;

    /** @var int CURLE_* error */
    protected $errorNo = CURLE_OK;

    /**
     * Factory method to create a new curl handle based on an HTTP request.
     *
     * There are some helpful options you can set to enable specific behavior:
     * - debug:    Set to true to enable cURL debug functionality to track the actual headers sent over the wire.
     * - progress: Set to true to enable progress function callbacks.
     *
     * @param RequestInterface $request Request
     *
     * @return CurlHandle
     * @throws RuntimeException
     */
    public static function factory(RequestInterface $request)
    {
        $requestCurlOptions = $request->getCurlOptions();
        $mediator = new RequestMediator($request, $requestCurlOptions->get('emit_io'));
        $tempContentLength = null;
        $method = $request->getMethod();
        $bodyAsString = $requestCurlOptions->get(self::BODY_AS_STRING);

        // Prepare url
        $url = (string)$request->getUrl();
        if(($pos = strpos($url, '#')) !== false ){
            // strip fragment from url
            $url = substr($url, 0, $pos);
        }

        // Array of default cURL options.
        $curlOptions = array(
            CURLOPT_URL            => $url,
            CURLOPT_CONNECTTIMEOUT => 150,
            CURLOPT_RETURNTRANSFER => false,
            CURLOPT_HEADER         => false,
            CURLOPT_PORT           => $request->getPort(),
            CURLOPT_HTTPHEADER     => array(),
            CURLOPT_WRITEFUNCTION  => array($mediator, 'writeResponseBody'),
            CURLOPT_HEADERFUNCTION => array($mediator, 'receiveResponseHeader'),
            CURLOPT_HTTP_VERSION   => $request->getProtocolVersion() === '1.0'
                ? CURL_HTTP_VERSION_1_0 : CURL_HTTP_VERSION_1_1,
            // Verifies the authenticity of the peer's certificate
            CURLOPT_SSL_VERIFYPEER => 1,
            // Certificate must indicate that the server is the server to which you meant to connect
            CURLOPT_SSL_VERIFYHOST => 2
        );

        if (defined('CURLOPT_PROTOCOLS')) {
            // Allow only HTTP and HTTPS protocols
            $curlOptions[CURLOPT_PROTOCOLS] = CURLPROTO_HTTP | CURLPROTO_HTTPS;
        }

        // Add CURLOPT_ENCODING if Accept-Encoding header is provided
        if ($acceptEncodingHeader = $request->getHeader('Accept-Encoding')) {
            $curlOptions[CURLOPT_ENCODING] = (string) $acceptEncodingHeader;
            // Let cURL set the Accept-Encoding header, prevents duplicate values
            $request->removeHeader('Accept-Encoding');
        }

        // Enable curl debug information if the 'debug' param was set
        if ($requestCurlOptions->get('debug')) {
            $curlOptions[CURLOPT_STDERR] = fopen('php://temp', 'r+');
            // @codeCoverageIgnoreStart
            if (false === $curlOptions[CURLOPT_STDERR]) {
                throw new RuntimeException('Unable to create a stream for CURLOPT_STDERR');
            }
            // @codeCoverageIgnoreEnd
            $curlOptions[CURLOPT_VERBOSE] = true;
        }

        // Specify settings according to the HTTP method
        if ($method == 'GET') {
            $curlOptions[CURLOPT_HTTPGET] = true;
        } elseif ($method == 'HEAD') {
            $curlOptions[CURLOPT_NOBODY] = true;
            // HEAD requests do not use a write function
            unset($curlOptions[CURLOPT_WRITEFUNCTION]);
        } elseif (!($request instanceof EntityEnclosingRequest)) {
            $curlOptions[CURLOPT_CUSTOMREQUEST] = $method;
        } else {

            $curlOptions[CURLOPT_CUSTOMREQUEST] = $method;

            // Handle sending raw bodies in a request
            if ($request->getBody()) {
                // You can send the body as a string using curl's CURLOPT_POSTFIELDS
                if ($bodyAsString) {
                    $curlOptions[CURLOPT_POSTFIELDS] = (string) $request->getBody();
                    // Allow curl to add the Content-Length for us to account for the times when
                    // POST redirects are followed by GET requests
                    if ($tempContentLength = $request->getHeader('Content-Length')) {
                        $tempContentLength = (int) (string) $tempContentLength;
                    }
                    // Remove the curl generated Content-Type header if none was set manually
                    if (!$request->hasHeader('Content-Type')) {
                        $curlOptions[CURLOPT_HTTPHEADER][] = 'Content-Type:';
                    }
                } else {
                    $curlOptions[CURLOPT_UPLOAD] = true;
                    // Let cURL handle setting the Content-Length header
                    if ($tempContentLength = $request->getHeader('Content-Length')) {
                        $tempContentLength = (int) (string) $tempContentLength;
                        $curlOptions[CURLOPT_INFILESIZE] = $tempContentLength;
                    }
                    // Add a callback for curl to read data to send with the request only if a body was specified
                    $curlOptions[CURLOPT_READFUNCTION] = array($mediator, 'readRequestBody');
                    // Attempt to seek to the start of the stream
                    $request->getBody()->seek(0);
                }

            } else {

                // Special handling for POST specific fields and files
                $postFields = false;
                if (count($request->getPostFiles())) {
                    $postFields = $request->getPostFields()->useUrlEncoding(false)->urlEncode();
                    foreach ($request->getPostFiles() as $key => $data) {
                        $prefixKeys = count($data) > 1;
                        foreach ($data as $index => $file) {
                            // Allow multiple files in the same key
                            $fieldKey = $prefixKeys ? "{$key}[{$index}]" : $key;
                            $postFields[$fieldKey] = $file->getCurlValue();
                        }
                    }
                } elseif (count($request->getPostFields())) {
                    $postFields = (string) $request->getPostFields()->useUrlEncoding(true);
                }

                if ($postFields !== false) {
                    if ($method == 'POST') {
                        unset($curlOptions[CURLOPT_CUSTOMREQUEST]);
                        $curlOptions[CURLOPT_POST] = true;
                    }
                    $curlOptions[CURLOPT_POSTFIELDS] = $postFields;
                    $request->removeHeader('Content-Length');
                }
            }

            // If the Expect header is not present, prevent curl from adding it
            if (!$request->hasHeader('Expect')) {
                $curlOptions[CURLOPT_HTTPHEADER][] = 'Expect:';
            }
        }

        // If a Content-Length header was specified but we want to allow curl to set one for us
        if (null !== $tempContentLength) {
            $request->removeHeader('Content-Length');
        }

        // Set custom cURL options
        foreach ($requestCurlOptions->toArray() as $key => $value) {
            if (is_numeric($key)) {
                $curlOptions[$key] = $value;
            }
        }

        // Do not set an Accept header by default
        if (!isset($curlOptions[CURLOPT_ENCODING])) {
            $curlOptions[CURLOPT_HTTPHEADER][] = 'Accept:';
        }

        // Add any custom headers to the request. Empty headers will cause curl to not send the header at all.
        foreach ($request->getHeaderLines() as $line) {
            $curlOptions[CURLOPT_HTTPHEADER][] = $line;
        }

        // Add the content-length header back if it was temporarily removed
        if (null !== $tempContentLength) {
            $request->setHeader('Content-Length', $tempContentLength);
        }

        // Apply the options to a new cURL handle.
        $handle = curl_init();

        // Enable the progress function if the 'progress' param was set
        if ($requestCurlOptions->get('progress')) {
            // Wrap the function in a function that provides the curl handle to the mediator's progress function
            // Using this rather than injecting the handle into the mediator prevents a circular reference
            $curlOptions[CURLOPT_PROGRESSFUNCTION] = function () use ($mediator, $handle) {
                $args = func_get_args();
                $args[] = $handle;

                // PHP 5.5 pushed the handle onto the start of the args
                if (false !== $args[0]) {
                    array_shift($args);
                }
                /*if (is_resource($args[0])) {
                    array_shift($args);
                }*/

                call_user_func_array(array($mediator, 'progress'), $args);
            };
            $curlOptions[CURLOPT_NOPROGRESS] = false;
        }

        curl_setopt_array($handle, $curlOptions);

        return new static($handle, $curlOptions);
    }

    /**
     * Construct a new CurlHandle object that wraps a cURL handle
     *
     * @param resource         $handle  Configured cURL handle resource
     * @param Collection|array $options Curl options to use with the handle
     *
     * @throws InvalidArgumentException
     */
    public function __construct($handle, $options)
    {
        /*if (!is_resource($handle)) {
            throw new InvalidArgumentException('Invalid handle provided');
        }*/
        if (false === $handle) {
            throw new InvalidArgumentException('Invalid handle provided');
        }
        if (is_array($options)) {
            $this->options = new Collection($options);
        } elseif ($options instanceof Collection) {
            $this->options = $options;
        } else {
            throw new InvalidArgumentException('Expected array or Collection');
        }
        $this->handle = $handle;
    }

    /**
     * Destructor
     */
    public function __destruct()
    {
        $this->close();
    }

    /**
     * Close the curl handle
     */
    public function close()
    {
        /*if (is_resource($this->handle)) {
            curl_close($this->handle);
        }*/
        if (false !== $this->handle && null !== $this->handle) {
            curl_close($this->handle);
            unset($this->handle);
        }
        $this->handle = null;
    }

    /**
     * Check if the handle is available and still OK
     *
     * @return bool
     */
    public function isAvailable()
    {
        return false !== $this->handle;
        //return is_resource($this->handle);
    }

    /**
     * Get the last error that occurred on the cURL handle
     *
     * @return string
     */
    public function getError()
    {
        return $this->isAvailable() ? curl_error($this->handle) : '';
    }

    /**
     * Get the last error number that occurred on the cURL handle
     *
     * @return int
     */
    public function getErrorNo()
    {
        if ($this->errorNo) {
            return $this->errorNo;
        }

        return $this->isAvailable() ? curl_errno($this->handle) : CURLE_OK;
    }

    /**
     * Set the curl error number
     *
     * @param int $error Error number to set
     *
     * @return CurlHandle
     */
    public function setErrorNo($error)
    {
        $this->errorNo = $error;

        return $this;
    }

    /**
     * Get cURL curl_getinfo data
     *
     * @param int $option Option to retrieve. Pass null to retrieve all data as an array.
     *
     * @return array|mixed
     */
    public function getInfo($option = null)
    {
        if (false === $this->handle) {
            return null;
        }
        /*if (!is_resource($this->handle)) {
            return null;
        }*/

        if (null !== $option) {
            return curl_getinfo($this->handle, $option) ?: null;
        }

        return curl_getinfo($this->handle) ?: array();
    }

    /**
     * Get the stderr output
     *
     * @param bool $asResource Set to TRUE to get an fopen resource
     *
     * @return string|resource|null
     */
    public function getStderr($asResource = false)
    {
        $stderr = $this->getOptions()->get(CURLOPT_STDERR);
        if (!$stderr) {
            return null;
        }

        if ($asResource) {
            return $stderr;
        }

        fseek($stderr, 0);
        $e = stream_get_contents($stderr);
        fseek($stderr, 0, SEEK_END);

        return $e;
    }

    /**
     * Get the URL that this handle is connecting to
     *
     * @return Url
     */
    public function getUrl()
    {
        return Url::factory($this->options->get(CURLOPT_URL));
    }

    /**
     * Get the wrapped curl handle
     *
     * @return resource|null Returns the cURL handle or null if it was closed
     */
    public function getHandle()
    {
        return $this->isAvailable() ? $this->handle : null;
    }

    /**
     * Get the cURL setopt options of the handle. Changing values in the return object will have no effect on the curl
     * handle after it is created.
     *
     * @return Collection
     */
    public function getOptions()
    {
        return $this->options;
    }

    /**
     * Update a request based on the log messages of the CurlHandle
     *
     * @param RequestInterface $request Request to update
     */
    public function updateRequestFromTransfer(RequestInterface $request)
    {
        if (!$request->getResponse()) {
            return;
        }

        // Update the transfer stats of the response
        $request->getResponse()->setInfo($this->getInfo());

        if (!$log = $this->getStderr(true)) {
            return;
        }

        // Parse the cURL stderr output for outgoing requests
        $headers = '';
        fseek($log, 0);
        while (($line = fgets($log)) !== false) {
            if ($line && $line[0] == '>') {
                $headers = substr(trim($line), 2) . "\r\n";
                while (($line = fgets($log)) !== false) {
                    if ($line[0] == '*' || $line[0] == '<') {
                        break;
                    } else {
                        $headers .= trim($line) . "\r\n";
                    }
                }
            }
        }

        // Add request headers to the request exactly as they were sent
        if ($headers) {
            $parsed = ParserRegistry::getInstance()->getParser('message')->parseRequest($headers);
            if (!empty($parsed['headers'])) {
                $request->setHeaders(array());
                foreach ($parsed['headers'] as $name => $value) {
                    $request->setHeader($name, $value);
                }
            }
            if (!empty($parsed['version'])) {
                $request->setProtocolVersion($parsed['version']);
            }
        }
    }

    /**
     * Parse the config and replace curl.* configurators into the constant based values so it can be used elsewhere
     *
     * @param array|Collection $config The configuration we want to parse
     *
     * @return array
     */
    public static function parseCurlConfig($config)
    {
        $curlOptions = array();
        foreach ($config as $key => $value) {
            if (is_string($key) && defined($key)) {
                // Convert constants represented as string to constant int values
                $key = constant($key);
            }
            if (is_string($value) && defined($value)) {
                $value = constant($value);
            }
            $curlOptions[$key] = $value;
        }

        return $curlOptions;
    }
}
vendor/guzzle/guzzle/src/Guzzle/Http/Curl/RequestMediator.php000064400000010535151327705700020435 0ustar00<?php

namespace Guzzle\Http\Curl;

use Guzzle\Http\Message\RequestInterface;
use Guzzle\Http\EntityBody;
use Guzzle\Http\Message\Response;

/**
 * Mediator between curl handles and request objects
 */
class RequestMediator
{
    /** @var RequestInterface */
    protected $request;

    /** @var bool Whether or not to emit read/write events */
    protected $emitIo;

    /**
     * @param RequestInterface $request Request to mediate
     * @param bool             $emitIo  Set to true to dispatch events on input and output
     */
    public function __construct(RequestInterface $request, $emitIo = false)
    {
        $this->request = $request;
        $this->emitIo = $emitIo;
    }

    /**
     * Receive a response header from curl
     *
     * @param resource $curl   Curl handle
     * @param string   $header Received header
     *
     * @return int
     */
    public function receiveResponseHeader($curl, $header)
    {
        static $normalize = array("\r", "\n");
        $length = strlen($header);
        $header = str_replace($normalize, '', $header);

        if (strpos($header, 'HTTP/') === 0) {

            $startLine = explode(' ', $header, 3);
            $code = $startLine[1];
            $status = isset($startLine[2]) ? $startLine[2] : '';

            // Only download the body of the response to the specified response
            // body when a successful response is received.
            if ($code >= 200 && $code < 300) {
                $body = $this->request->getResponseBody();
            } else {
                $body = EntityBody::factory();
            }

            $response = new Response($code, null, $body);
            $response->setStatus($code, $status);
            $this->request->startResponse($response);

            $this->request->dispatch('request.receive.status_line', array(
                'request'       => $this,
                'line'          => $header,
                'status_code'   => $code,
                'reason_phrase' => $status
            ));

        } elseif ($pos = strpos($header, ':')) {
            $this->request->getResponse()->addHeader(
                trim(substr($header, 0, $pos)),
                trim(substr($header, $pos + 1))
            );
        }

        return $length;
    }

    /**
     * Received a progress notification
     *
     * @param int        $downloadSize Total download size
     * @param int        $downloaded   Amount of bytes downloaded
     * @param int        $uploadSize   Total upload size
     * @param int        $uploaded     Amount of bytes uploaded
     * @param resource   $handle       CurlHandle object
     */
    public function progress($downloadSize, $downloaded, $uploadSize, $uploaded, $handle = null)
    {
        $this->request->dispatch('curl.callback.progress', array(
            'request'       => $this->request,
            'handle'        => $handle,
            'download_size' => $downloadSize,
            'downloaded'    => $downloaded,
            'upload_size'   => $uploadSize,
            'uploaded'      => $uploaded
        ));
    }

    /**
     * Write data to the response body of a request
     *
     * @param resource $curl  Curl handle
     * @param string   $write Data that was received
     *
     * @return int
     */
    public function writeResponseBody($curl, $write)
    {
        if ($this->emitIo) {
            $this->request->dispatch('curl.callback.write', array(
                'request' => $this->request,
                'write'   => $write
            ));
        }

        if ($response = $this->request->getResponse()) {
            return $response->getBody()->write($write);
        } else {
            // Unexpected data received before response headers - abort transfer
            return 0;
        }
    }

    /**
     * Read data from the request body and send it to curl
     *
     * @param resource $ch     Curl handle
     * @param resource $fd     File descriptor
     * @param int      $length Amount of data to read
     *
     * @return string
     */
    public function readRequestBody($ch, $fd, $length)
    {
        if (!($body = $this->request->getBody())) {
            return '';
        }

        $read = (string) $body->read($length);
        if ($this->emitIo) {
            $this->request->dispatch('curl.callback.read', array('request' => $this->request, 'read' => $read));
        }

        return $read;
    }
}
vendor/guzzle/guzzle/src/Guzzle/Http/Curl/CurlMultiInterface.php000064400000003075151327705700021062 0ustar00<?php

namespace Guzzle\Http\Curl;

use Guzzle\Common\HasDispatcherInterface;
use Guzzle\Common\Exception\ExceptionCollection;
use Guzzle\Http\Message\RequestInterface;

/**
 * Interface for sending a pool of {@see RequestInterface} objects in parallel
 */
interface CurlMultiInterface extends \Countable, HasDispatcherInterface
{
    const POLLING_REQUEST = 'curl_multi.polling_request';
    const ADD_REQUEST = 'curl_multi.add_request';
    const REMOVE_REQUEST = 'curl_multi.remove_request';
    const MULTI_EXCEPTION = 'curl_multi.exception';
    const BLOCKING = 'curl_multi.blocking';

    /**
     * Add a request to the pool.
     *
     * @param RequestInterface $request Request to add
     *
     * @return CurlMultiInterface
     */
    public function add(RequestInterface $request);

    /**
     * Get an array of attached {@see RequestInterface} objects
     *
     * @return array
     */
    public function all();

    /**
     * Remove a request from the pool.
     *
     * @param RequestInterface $request Request to remove
     *
     * @return bool Returns true on success or false on failure
     */
    public function remove(RequestInterface $request);

    /**
     * Reset the state and remove any attached RequestInterface objects
     *
     * @param bool $hard Set to true to close and reopen any open multi handles
     */
    public function reset($hard = false);

    /**
     * Send a pool of {@see RequestInterface} requests.
     *
     * @throws ExceptionCollection if any requests threw exceptions during the transfer.
     */
    public function send();
}
vendor/guzzle/guzzle/src/Guzzle/Http/Curl/CurlVersion.php000064400000003267151327705700017577 0ustar00<?php

namespace Guzzle\Http\Curl;

/**
 * Class used for querying curl_version data
 */
class CurlVersion
{
    /** @var array curl_version() information */
    protected $version;

    /** @var CurlVersion */
    protected static $instance;

    /** @var string Default user agent */
    protected $userAgent;

    /**
     * @return CurlVersion
     */
    public static function getInstance()
    {
        if (!self::$instance) {
            self::$instance = new self();
        }

        return self::$instance;
    }

    /**
     * Get all of the curl_version() data
     *
     * @return array
     */
    public function getAll()
    {
        if (!$this->version) {
            $this->version = curl_version();
        }

        return $this->version;
    }

    /**
     * Get a specific type of curl information
     *
     * @param string $type Version information to retrieve. This value is one of:
     *     - version_number:     cURL 24 bit version number
     *     - version:            cURL version number, as a string
     *     - ssl_version_number: OpenSSL 24 bit version number
     *     - ssl_version:        OpenSSL version number, as a string
     *     - libz_version:       zlib version number, as a string
     *     - host:               Information about the host where cURL was built
     *     - features:           A bitmask of the CURL_VERSION_XXX constants
     *     - protocols:          An array of protocols names supported by cURL
     *
     * @return string|float|bool if the $type is found, and false if not found
     */
    public function get($type)
    {
        $version = $this->getAll();

        return isset($version[$type]) ? $version[$type] : false;
    }
}
vendor/guzzle/guzzle/src/Guzzle/Http/Curl/CurlMulti.php000064400000034311151327705700017236 0ustar00<?php

namespace Guzzle\Http\Curl;

use Guzzle\Common\AbstractHasDispatcher;
use Guzzle\Common\Event;
use Guzzle\Http\Exception\MultiTransferException;
use Guzzle\Http\Exception\CurlException;
use Guzzle\Http\Message\RequestInterface;
use Guzzle\Http\Message\EntityEnclosingRequestInterface;
use Guzzle\Http\Exception\RequestException;

/**
 * Send {@see RequestInterface} objects in parallel using curl_multi
 */
class CurlMulti extends AbstractHasDispatcher implements CurlMultiInterface
{
    /** @var resource cURL multi handle. */
    protected $multiHandle;

    /** @var array Attached {@see RequestInterface} objects. */
    protected $requests;

    /** @var \SplObjectStorage RequestInterface to CurlHandle hash */
    protected $handles;

    /** @var array Hash mapping curl handle resource IDs to request objects */
    protected $resourceHash;

    /** @var array Queued exceptions */
    protected $exceptions = array();

    /** @var array Requests that succeeded */
    protected $successful = array();

    /** @var array cURL multi error values and codes */
    protected $multiErrors = array(
        CURLM_BAD_HANDLE      => array('CURLM_BAD_HANDLE', 'The passed-in handle is not a valid CURLM handle.'),
        CURLM_BAD_EASY_HANDLE => array('CURLM_BAD_EASY_HANDLE', "An easy handle was not good/valid. It could mean that it isn't an easy handle at all, or possibly that the handle already is in used by this or another multi handle."),
        CURLM_OUT_OF_MEMORY   => array('CURLM_OUT_OF_MEMORY', 'You are doomed.'),
        CURLM_INTERNAL_ERROR  => array('CURLM_INTERNAL_ERROR', 'This can only be returned if libcurl bugs. Please report it to us!')
    );

    /** @var float */
    protected $selectTimeout;

    public function __construct($selectTimeout = 1.0)
    {
        $this->selectTimeout = $selectTimeout;
        $this->multiHandle = curl_multi_init();
        // @codeCoverageIgnoreStart
        if ($this->multiHandle === false) {
            throw new CurlException('Unable to create multi handle');
        }
        // @codeCoverageIgnoreEnd
        $this->reset();
    }

    public function __destruct()
    {
        if (is_resource($this->multiHandle)) {
            curl_multi_close($this->multiHandle);
        }
    }

    public function add(RequestInterface $request)
    {
        $this->requests[] = $request;
        // If requests are currently transferring and this is async, then the
        // request must be prepared now as the send() method is not called.
        $this->beforeSend($request);
        $this->dispatch(self::ADD_REQUEST, array('request' => $request));

        return $this;
    }

    public function all()
    {
        return $this->requests;
    }

    public function remove(RequestInterface $request)
    {
        $this->removeHandle($request);
        if (($index = array_search($request, $this->requests, true)) !== false) {
            $request = $this->requests[$index];
            unset($this->requests[$index]);
            $this->requests = array_values($this->requests);
            $this->dispatch(self::REMOVE_REQUEST, array('request' => $request));
            return true;
        }

        return false;
    }

    public function reset($hard = false)
    {
        // Remove each request
        if ($this->requests) {
            foreach ($this->requests as $request) {
                $this->remove($request);
            }
        }

        $this->handles = new \SplObjectStorage();
        $this->requests = $this->resourceHash = $this->exceptions = $this->successful = array();
    }

    public function send()
    {
        $this->perform();
        $exceptions = $this->exceptions;
        $successful = $this->successful;
        $this->reset();

        if ($exceptions) {
            $this->throwMultiException($exceptions, $successful);
        }
    }

    public function count()
    {
        return count($this->requests);
    }

    /**
     * Build and throw a MultiTransferException
     *
     * @param array $exceptions Exceptions encountered
     * @param array $successful Successful requests
     * @throws MultiTransferException
     */
    protected function throwMultiException(array $exceptions, array $successful)
    {
        $multiException = new MultiTransferException('Errors during multi transfer');

        while ($e = array_shift($exceptions)) {
            $multiException->addFailedRequestWithException($e['request'], $e['exception']);
        }

        // Add successful requests
        foreach ($successful as $request) {
            if (!$multiException->containsRequest($request)) {
                $multiException->addSuccessfulRequest($request);
            }
        }

        throw $multiException;
    }

    /**
     * Prepare for sending
     *
     * @param RequestInterface $request Request to prepare
     * @throws \Exception on error preparing the request
     */
    protected function beforeSend(RequestInterface $request)
    {
        try {
            $state = $request->setState(RequestInterface::STATE_TRANSFER);
            if ($state == RequestInterface::STATE_TRANSFER) {
                $this->addHandle($request);
            } else {
                // Requests might decide they don't need to be sent just before
                // transfer (e.g. CachePlugin)
                $this->remove($request);
                if ($state == RequestInterface::STATE_COMPLETE) {
                    $this->successful[] = $request;
                }
            }
        } catch (\Exception $e) {
            // Queue the exception to be thrown when sent
            $this->removeErroredRequest($request, $e);
        }
    }

    private function addHandle(RequestInterface $request)
    {
        $handle = $this->createCurlHandle($request)->getHandle();
        $this->checkCurlResult(
            curl_multi_add_handle($this->multiHandle, $handle)
        );
    }

    /**
     * Create a curl handle for a request
     *
     * @param RequestInterface $request Request
     *
     * @return CurlHandle
     */
    protected function createCurlHandle(RequestInterface $request)
    {
        $wrapper = CurlHandle::factory($request);
        $this->handles[$request] = $wrapper;
        $this->resourceHash[(int) $wrapper->getHandle()] = $request;

        return $wrapper;
    }

    /**
     * Get the data from the multi handle
     */
    protected function perform()
    {
        $event = new Event(array('curl_multi' => $this));

        while ($this->requests) {
            // Notify each request as polling
            $blocking = $total = 0;
            foreach ($this->requests as $request) {
                ++$total;
                $event['request'] = $request;
                $request->getEventDispatcher()->dispatch(self::POLLING_REQUEST, $event);
                // The blocking variable just has to be non-falsey to block the loop
                if ($request->getParams()->hasKey(self::BLOCKING)) {
                    ++$blocking;
                }
            }
            if ($blocking == $total) {
                // Sleep to prevent eating CPU because no requests are actually pending a select call
                usleep(500);
            } else {
                $this->executeHandles();
            }
        }
    }

    /**
     * Execute and select curl handles
     */
    private function executeHandles()
    {
        // The first curl_multi_select often times out no matter what, but is usually required for fast transfers
        $selectTimeout = 0.001;
        $active = false;
        do {
            while (($mrc = curl_multi_exec($this->multiHandle, $active)) == CURLM_CALL_MULTI_PERFORM);
            $this->checkCurlResult($mrc);
            $this->processMessages();
            if ($active && curl_multi_select($this->multiHandle, $selectTimeout) === -1) {
                // Perform a usleep if a select returns -1: https://bugs.php.net/bug.php?id=61141
                usleep(150);
            }
            $selectTimeout = $this->selectTimeout;
        } while ($active);
    }

    /**
     * Process any received curl multi messages
     */
    private function processMessages()
    {
        while ($done = curl_multi_info_read($this->multiHandle)) {
            $request = $this->resourceHash[(int) $done['handle']];
            try {
                $this->processResponse($request, $this->handles[$request], $done);
                $this->successful[] = $request;
            } catch (\Exception $e) {
                $this->removeErroredRequest($request, $e);
            }
        }
    }

    /**
     * Remove a request that encountered an exception
     *
     * @param RequestInterface $request Request to remove
     * @param \Exception       $e       Exception encountered
     */
    protected function removeErroredRequest(RequestInterface $request, \Exception $e = null)
    {
        $this->exceptions[] = array('request' => $request, 'exception' => $e);
        $this->remove($request);
        $this->dispatch(self::MULTI_EXCEPTION, array('exception' => $e, 'all_exceptions' => $this->exceptions));
    }

    /**
     * Check for errors and fix headers of a request based on a curl response
     *
     * @param RequestInterface $request Request to process
     * @param CurlHandle       $handle  Curl handle object
     * @param array            $curl    Array returned from curl_multi_info_read
     *
     * @throws CurlException on Curl error
     */
    protected function processResponse(RequestInterface $request, CurlHandle $handle, array $curl)
    {
        // Set the transfer stats on the response
        $handle->updateRequestFromTransfer($request);
        // Check if a cURL exception occurred, and if so, notify things
        $curlException = $this->isCurlException($request, $handle, $curl);

        // Always remove completed curl handles.  They can be added back again
        // via events if needed (e.g. ExponentialBackoffPlugin)
        $this->removeHandle($request);

        if (!$curlException) {
            if ($this->validateResponseWasSet($request)) {
                $state = $request->setState(
                    RequestInterface::STATE_COMPLETE,
                    array('handle' => $handle)
                );
                // Only remove the request if it wasn't resent as a result of
                // the state change
                if ($state != RequestInterface::STATE_TRANSFER) {
                    $this->remove($request);
                }
            }
            return;
        }

        // Set the state of the request to an error
        $state = $request->setState(RequestInterface::STATE_ERROR, array('exception' => $curlException));
        // Allow things to ignore the error if possible
        if ($state != RequestInterface::STATE_TRANSFER) {
            $this->remove($request);
        }

        // The error was not handled, so fail
        if ($state == RequestInterface::STATE_ERROR) {
            /** @var CurlException $curlException */
            throw $curlException;
        }
    }

    /**
     * Remove a curl handle from the curl multi object
     *
     * @param RequestInterface $request Request that owns the handle
     */
    protected function removeHandle(RequestInterface $request)
    {
        if (isset($this->handles[$request])) {
            $handle = $this->handles[$request];
            curl_multi_remove_handle($this->multiHandle, $handle->getHandle());
            unset($this->handles[$request]);
            unset($this->resourceHash[(int) $handle->getHandle()]);
            $handle->close();
        }
    }

    /**
     * Check if a cURL transfer resulted in what should be an exception
     *
     * @param RequestInterface $request Request to check
     * @param CurlHandle       $handle  Curl handle object
     * @param array            $curl    Array returned from curl_multi_info_read
     *
     * @return CurlException|bool
     */
    private function isCurlException(RequestInterface $request, CurlHandle $handle, array $curl)
    {
        if (CURLM_OK == $curl['result'] || CURLM_CALL_MULTI_PERFORM == $curl['result']) {
            return false;
        }

        $handle->setErrorNo($curl['result']);
        $e = new CurlException(sprintf('[curl] %s: %s [url] %s',
            $handle->getErrorNo(), $handle->getError(), $handle->getUrl()));
        $e->setCurlHandle($handle)
            ->setRequest($request)
            ->setCurlInfo($handle->getInfo())
            ->setError($handle->getError(), $handle->getErrorNo());

        return $e;
    }

    /**
     * Throw an exception for a cURL multi response if needed
     *
     * @param int $code Curl response code
     * @throws CurlException
     */
    private function checkCurlResult($code)
    {
        if ($code != CURLM_OK && $code != CURLM_CALL_MULTI_PERFORM) {
            throw new CurlException(isset($this->multiErrors[$code])
                ? "cURL error: {$code} ({$this->multiErrors[$code][0]}): cURL message: {$this->multiErrors[$code][1]}"
                : 'Unexpected cURL error: ' . $code
            );
        }
    }

    /**
     * @link https://github.com/guzzle/guzzle/issues/710
     */
    private function validateResponseWasSet(RequestInterface $request)
    {
        if ($request->getResponse()) {
            return true;
        }

        $body = $request instanceof EntityEnclosingRequestInterface
            ? $request->getBody()
            : null;

        if (!$body) {
            $rex = new RequestException(
                'No response was received for a request with no body. This'
                . ' could mean that you are saturating your network.'
            );
            $rex->setRequest($request);
            $this->removeErroredRequest($request, $rex);
        } elseif (!$body->isSeekable() || !$body->seek(0)) {
            // Nothing we can do with this. Sorry!
            $rex = new RequestException(
                'The connection was unexpectedly closed. The request would'
                . ' have been retried, but attempting to rewind the'
                . ' request body failed.'
            );
            $rex->setRequest($request);
            $this->removeErroredRequest($request, $rex);
        } else {
            $this->remove($request);
            // Add the request back to the batch to retry automatically.
            $this->requests[] = $request;
            $this->addHandle($request);
        }

        return false;
    }
}
vendor/guzzle/guzzle/src/Guzzle/Http/Curl/CurlMultiProxy.php000064400000010401151327705700020272 0ustar00<?php

namespace Guzzle\Http\Curl;

use Guzzle\Common\AbstractHasDispatcher;
use Guzzle\Http\Message\RequestInterface;

/**
 * Proxies requests and connections to a pool of internal curl_multi handles. Each recursive call will add requests
 * to the next available CurlMulti handle.
 */
class CurlMultiProxy extends AbstractHasDispatcher implements CurlMultiInterface
{
    protected $handles = array();
    protected $groups = array();
    protected $queued = array();
    protected $maxHandles;
    protected $selectTimeout;

    /**
     * @param int   $maxHandles The maximum number of idle CurlMulti handles to allow to remain open
     * @param float $selectTimeout timeout for curl_multi_select
     */
    public function __construct($maxHandles = 3, $selectTimeout = 1.0)
    {
        $this->maxHandles = $maxHandles;
        $this->selectTimeout = $selectTimeout;
        // You can get some weird "Too many open files" errors when sending a large amount of requests in parallel.
        // These two statements autoload classes before a system runs out of file descriptors so that you can get back
        // valuable error messages if you run out.
        class_exists('Guzzle\Http\Message\Response');
        class_exists('Guzzle\Http\Exception\CurlException');
    }

    public function add(RequestInterface $request)
    {
        $this->queued[] = $request;

        return $this;
    }

    public function all()
    {
        $requests = $this->queued;
        foreach ($this->handles as $handle) {
            $requests = array_merge($requests, $handle->all());
        }

        return $requests;
    }

    public function remove(RequestInterface $request)
    {
        foreach ($this->queued as $i => $r) {
            if ($request === $r) {
                unset($this->queued[$i]);
                return true;
            }
        }

        foreach ($this->handles as $handle) {
            if ($handle->remove($request)) {
                return true;
            }
        }

        return false;
    }

    public function reset($hard = false)
    {
        $this->queued = array();
        $this->groups = array();
        foreach ($this->handles as $handle) {
            $handle->reset();
        }
        if ($hard) {
            $this->handles = array();
        }

        return $this;
    }

    public function send()
    {
        if ($this->queued) {
            $group = $this->getAvailableHandle();
            // Add this handle to a list of handles than is claimed
            $this->groups[] = $group;
            while ($request = array_shift($this->queued)) {
                $group->add($request);
            }
            try {
                $group->send();
                array_pop($this->groups);
                $this->cleanupHandles();
            } catch (\Exception $e) {
                // Remove the group and cleanup if an exception was encountered and no more requests in group
                if (!$group->count()) {
                    array_pop($this->groups);
                    $this->cleanupHandles();
                }
                throw $e;
            }
        }
    }

    public function count()
    {
        return count($this->all());
    }

    /**
     * Get an existing available CurlMulti handle or create a new one
     *
     * @return CurlMulti
     */
    protected function getAvailableHandle()
    {
        // Grab a handle that is not claimed
        foreach ($this->handles as $h) {
            if (!in_array($h, $this->groups, true)) {
                return $h;
            }
        }

        // All are claimed, so create one
        $handle = new CurlMulti($this->selectTimeout);
        $handle->setEventDispatcher($this->getEventDispatcher());
        $this->handles[] = $handle;

        return $handle;
    }

    /**
     * Trims down unused CurlMulti handles to limit the number of open connections
     */
    protected function cleanupHandles()
    {
        if ($diff = max(0, count($this->handles) - $this->maxHandles)) {
            for ($i = count($this->handles) - 1; $i > 0 && $diff > 0; $i--) {
                if (!count($this->handles[$i])) {
                    unset($this->handles[$i]);
                    $diff--;
                }
            }
            $this->handles = array_values($this->handles);
        }
    }
}
vendor/guzzle/guzzle/src/Guzzle/Http/ReadLimitEntityBody.php000064400000006443151327705700020303 0ustar00<?php

namespace Guzzle\Http;

use Guzzle\Stream\StreamInterface;

/**
 * EntityBody decorator used to return only a subset of an entity body
 */
class ReadLimitEntityBody extends AbstractEntityBodyDecorator
{
    /** @var int Limit the number of bytes that can be read */
    protected $limit;

    /** @var int Offset to start reading from */
    protected $offset;

    /**
     * @param EntityBodyInterface $body   Body to wrap
     * @param int                 $limit  Total number of bytes to allow to be read from the stream
     * @param int                 $offset Position to seek to before reading (only works on seekable streams)
     */
    public function __construct(EntityBodyInterface $body, $limit, $offset = 0)
    {
        parent::__construct($body);
        $this->setLimit($limit)->setOffset($offset);
    }

    /**
     * Returns only a subset of the decorated entity body when cast as a string
     * {@inheritdoc}
     */
    public function __toString()
    {
        if (!$this->body->isReadable() ||
            (!$this->body->isSeekable() && $this->body->isConsumed())
        ) {
            return '';
        }

        $originalPos = $this->body->ftell();
        $this->body->seek($this->offset);
        $data = '';
        while (!$this->feof()) {
            $data .= $this->read(1048576);
        }
        $this->body->seek($originalPos);

        return (string) $data ?: '';
    }

    public function isConsumed()
    {
        return $this->body->isConsumed() ||
            ($this->body->ftell() >= $this->offset + $this->limit);
    }

    /**
     * Returns the Content-Length of the limited subset of data
     * {@inheritdoc}
     */
    public function getContentLength()
    {
        $length = $this->body->getContentLength();

        return $length === false
            ? $this->limit
            : min($this->limit, min($length, $this->offset + $this->limit) - $this->offset);
    }

    /**
     * Allow for a bounded seek on the read limited entity body
     * {@inheritdoc}
     */
    public function seek($offset, $whence = SEEK_SET)
    {
        return $whence === SEEK_SET
            ? $this->body->seek(max($this->offset, min($this->offset + $this->limit, $offset)))
            : false;
    }

    /**
     * Set the offset to start limiting from
     *
     * @param int $offset Offset to seek to and begin byte limiting from
     *
     * @return self
     */
    public function setOffset($offset)
    {
        $this->body->seek($offset);
        $this->offset = $offset;

        return $this;
    }

    /**
     * Set the limit of bytes that the decorator allows to be read from the stream
     *
     * @param int $limit Total number of bytes to allow to be read from the stream
     *
     * @return self
     */
    public function setLimit($limit)
    {
        $this->limit = $limit;

        return $this;
    }

    public function read($length)
    {
        // Check if the current position is less than the total allowed bytes + original offset
        $remaining = ($this->offset + $this->limit) - $this->body->ftell();
        if ($remaining > 0) {
            // Only return the amount of requested data, ensuring that the byte limit is not exceeded
            return $this->body->read(min($remaining, $length));
        } else {
            return false;
        }
    }
}
vendor/guzzle/guzzle/src/Guzzle/Http/QueryAggregator/QueryAggregatorInterface.php000064400000001236151327705700024452 0ustar00<?php

namespace Guzzle\Http\QueryAggregator;

use Guzzle\Http\QueryString;

/**
 * Interface used for aggregating nested query string variables into a flattened array of key value pairs
 */
interface QueryAggregatorInterface
{
    /**
     * Aggregate multi-valued parameters into a flattened associative array
     *
     * @param string      $key   The name of the query string parameter
     * @param array       $value The values of the parameter
     * @param QueryString $query The query string that is being aggregated
     *
     * @return array Returns an array of the combined values
     */
    public function aggregate($key, $value, QueryString $query);
}
vendor/guzzle/guzzle/src/Guzzle/Http/QueryAggregator/CommaAggregator.php000064400000001010151327705700022546 0ustar00<?php

namespace Guzzle\Http\QueryAggregator;

use Guzzle\Http\QueryString;

/**
 * Aggregates nested query string variables using commas
 */
class CommaAggregator implements QueryAggregatorInterface
{
    public function aggregate($key, $value, QueryString $query)
    {
        if ($query->isUrlEncoding()) {
            return array($query->encodeValue($key) => implode(',', array_map(array($query, 'encodeValue'), $value)));
        } else {
            return array($key => implode(',', $value));
        }
    }
}
vendor/guzzle/guzzle/src/Guzzle/Http/QueryAggregator/DuplicateAggregator.php000064400000001074151327705700023436 0ustar00<?php

namespace Guzzle\Http\QueryAggregator;

use Guzzle\Http\QueryString;

/**
 * Does not aggregate nested query string values and allows duplicates in the resulting array
 *
 * Example: http://test.com?q=1&q=2
 */
class DuplicateAggregator implements QueryAggregatorInterface
{
    public function aggregate($key, $value, QueryString $query)
    {
        if ($query->isUrlEncoding()) {
            return array($query->encodeValue($key) => array_map(array($query, 'encodeValue'), $value));
        } else {
            return array($key => $value);
        }
    }
}
vendor/guzzle/guzzle/src/Guzzle/Http/QueryAggregator/PhpAggregator.php000064400000001164151327705700022253 0ustar00<?php

namespace Guzzle\Http\QueryAggregator;

use Guzzle\Http\QueryString;

/**
 * Aggregates nested query string variables using PHP style []
 */
class PhpAggregator implements QueryAggregatorInterface
{
    public function aggregate($key, $value, QueryString $query)
    {
        $ret = array();

        foreach ($value as $k => $v) {
            $k = "{$key}[{$k}]";
            if (is_array($v)) {
                $ret = array_merge($ret, self::aggregate($k, $v, $query));
            } else {
                $ret[$query->encodeValue($k)] = $query->encodeValue($v);
            }
        }

        return $ret;
    }
}
vendor/guzzle/guzzle/src/Guzzle/Http/Client.php000064400000040776151327705700015643 0ustar00<?php

namespace Guzzle\Http;

use Guzzle\Common\Collection;
use Guzzle\Common\AbstractHasDispatcher;
use Guzzle\Common\Exception\ExceptionCollection;
use Guzzle\Common\Exception\InvalidArgumentException;
use Guzzle\Common\Exception\RuntimeException;
use Guzzle\Common\Version;
use Guzzle\Parser\ParserRegistry;
use Guzzle\Parser\UriTemplate\UriTemplateInterface;
use Guzzle\Http\Message\RequestInterface;
use Guzzle\Http\Message\RequestFactory;
use Guzzle\Http\Message\RequestFactoryInterface;
use Guzzle\Http\Curl\CurlMultiInterface;
use Guzzle\Http\Curl\CurlMultiProxy;
use Guzzle\Http\Curl\CurlHandle;
use Guzzle\Http\Curl\CurlVersion;

/**
 * HTTP client
 */
class Client extends AbstractHasDispatcher implements ClientInterface
{
    /** @deprecated Use [request.options][params] */
    const REQUEST_PARAMS = 'request.params';

    const REQUEST_OPTIONS = 'request.options';
    const CURL_OPTIONS = 'curl.options';
    const SSL_CERT_AUTHORITY = 'ssl.certificate_authority';
    const DISABLE_REDIRECTS = RedirectPlugin::DISABLE;
    const DEFAULT_SELECT_TIMEOUT = 1.0;
    const MAX_HANDLES = 3;

    /** @var Collection Default HTTP headers to set on each request */
    protected $defaultHeaders;

    /** @var string The user agent string to set on each request */
    protected $userAgent;

    /** @var Collection Parameter object holding configuration data */
    private $config;

    /** @var Url Base URL of the client */
    private $baseUrl;

    /** @var CurlMultiInterface CurlMulti object used internally */
    private $curlMulti;

    /** @var UriTemplateInterface URI template owned by the client */
    private $uriTemplate;

    /** @var RequestFactoryInterface Request factory used by the client */
    protected $requestFactory;

    public static function getAllEvents()
    {
        return array(self::CREATE_REQUEST);
    }

    /**
     * @param string           $baseUrl Base URL of the web service
     * @param array|Collection $config  Configuration settings
     *
     * @throws RuntimeException if cURL is not installed
     */
    public function __construct($baseUrl = '', $config = null)
    {
        if (!extension_loaded('curl')) {
            // @codeCoverageIgnoreStart
            throw new RuntimeException('The PHP cURL extension must be installed to use Guzzle.');
            // @codeCoverageIgnoreEnd
        }
        $this->setConfig($config ?: new Collection());
        $this->initSsl();
        $this->setBaseUrl($baseUrl);
        $this->defaultHeaders = new Collection();
        $this->setRequestFactory(RequestFactory::getInstance());
        $this->userAgent = $this->getDefaultUserAgent();
        if (!$this->config[self::DISABLE_REDIRECTS]) {
            $this->addSubscriber(new RedirectPlugin());
        }
    }

    final public function setConfig($config)
    {
        if ($config instanceof Collection) {
            $this->config = $config;
        } elseif (is_array($config)) {
            $this->config = new Collection($config);
        } else {
            throw new InvalidArgumentException('Config must be an array or Collection');
        }

        return $this;
    }

    final public function getConfig($key = false)
    {
        return $key ? $this->config[$key] : $this->config;
    }

    /**
     * Set a default request option on the client that will be used as a default for each request
     *
     * @param string $keyOrPath request.options key (e.g. allow_redirects) or path to a nested key (e.g. headers/foo)
     * @param mixed  $value     Value to set
     *
     * @return $this
     */
    public function setDefaultOption($keyOrPath, $value)
    {
        $keyOrPath = self::REQUEST_OPTIONS . '/' . $keyOrPath;
        $this->config->setPath($keyOrPath, $value);

        return $this;
    }

    /**
     * Retrieve a default request option from the client
     *
     * @param string $keyOrPath request.options key (e.g. allow_redirects) or path to a nested key (e.g. headers/foo)
     *
     * @return mixed|null
     */
    public function getDefaultOption($keyOrPath)
    {
        $keyOrPath = self::REQUEST_OPTIONS . '/' . $keyOrPath;

        return $this->config->getPath($keyOrPath);
    }

    final public function setSslVerification($certificateAuthority = true, $verifyPeer = true, $verifyHost = 2)
    {
        $opts = $this->config[self::CURL_OPTIONS] ?: array();

        if ($certificateAuthority === true) {
            // use bundled CA bundle, set secure defaults
            $opts[CURLOPT_CAINFO] = __DIR__ . '/Resources/cacert.pem';
            $opts[CURLOPT_SSL_VERIFYPEER] = true;
            $opts[CURLOPT_SSL_VERIFYHOST] = 2;
        } elseif ($certificateAuthority === false) {
            unset($opts[CURLOPT_CAINFO]);
            $opts[CURLOPT_SSL_VERIFYPEER] = false;
            $opts[CURLOPT_SSL_VERIFYHOST] = 0;
        } elseif ($verifyPeer !== true && $verifyPeer !== false && $verifyPeer !== 1 && $verifyPeer !== 0) {
            throw new InvalidArgumentException('verifyPeer must be 1, 0 or boolean');
        } elseif ($verifyHost !== 0 && $verifyHost !== 1 && $verifyHost !== 2) {
            throw new InvalidArgumentException('verifyHost must be 0, 1 or 2');
        } else {
            $opts[CURLOPT_SSL_VERIFYPEER] = $verifyPeer;
            $opts[CURLOPT_SSL_VERIFYHOST] = $verifyHost;
            if (is_file($certificateAuthority)) {
                unset($opts[CURLOPT_CAPATH]);
                $opts[CURLOPT_CAINFO] = $certificateAuthority;
            } elseif (is_dir($certificateAuthority)) {
                unset($opts[CURLOPT_CAINFO]);
                $opts[CURLOPT_CAPATH] = $certificateAuthority;
            } else {
                throw new RuntimeException(
                    'Invalid option passed to ' . self::SSL_CERT_AUTHORITY . ': ' . $certificateAuthority
                );
            }
        }

        $this->config->set(self::CURL_OPTIONS, $opts);

        return $this;
    }

    public function createRequest($method = 'GET', $uri = null, $headers = null, $body = null, array $options = array())
    {
        if (!$uri) {
            $url = $this->getBaseUrl();
        } else {
            if (!is_array($uri)) {
                $templateVars = null;
            } else {
                list($uri, $templateVars) = $uri;
            }
            if (strpos($uri, '://')) {
                // Use absolute URLs as-is
                $url = $this->expandTemplate($uri, $templateVars);
            } else {
                $url = Url::factory($this->getBaseUrl())->combine($this->expandTemplate($uri, $templateVars));
            }
        }

        // If default headers are provided, then merge them under any explicitly provided headers for the request
        if (count($this->defaultHeaders)) {
            if (!$headers) {
                $headers = $this->defaultHeaders->toArray();
            } elseif (is_array($headers)) {
                $headers += $this->defaultHeaders->toArray();
            } elseif ($headers instanceof Collection) {
                $headers = $headers->toArray() + $this->defaultHeaders->toArray();
            }
        }

        return $this->prepareRequest($this->requestFactory->create($method, (string) $url, $headers, $body), $options);
    }

    public function getBaseUrl($expand = true)
    {
        return $expand ? $this->expandTemplate($this->baseUrl) : $this->baseUrl;
    }

    public function setBaseUrl($url)
    {
        $this->baseUrl = $url;

        return $this;
    }

    public function setUserAgent($userAgent, $includeDefault = false)
    {
        if ($includeDefault) {
            $userAgent .= ' ' . $this->getDefaultUserAgent();
        }
        $this->userAgent = $userAgent;

        return $this;
    }

    /**
     * Get the default User-Agent string to use with Guzzle
     *
     * @return string
     */
    public function getDefaultUserAgent()
    {
        return 'Guzzle/' . Version::VERSION
            . ' curl/' . CurlVersion::getInstance()->get('version')
            . ' PHP/' . PHP_VERSION;
    }

    public function get($uri = null, $headers = null, $options = array())
    {
        // BC compat: $options can be a string, resource, etc to specify where the response body is downloaded
        return is_array($options)
            ? $this->createRequest('GET', $uri, $headers, null, $options)
            : $this->createRequest('GET', $uri, $headers, $options);
    }

    public function head($uri = null, $headers = null, array $options = array())
    {
        return $this->createRequest('HEAD', $uri, $headers, null, $options);
    }

    public function delete($uri = null, $headers = null, $body = null, array $options = array())
    {
        return $this->createRequest('DELETE', $uri, $headers, $body, $options);
    }

    public function put($uri = null, $headers = null, $body = null, array $options = array())
    {
        return $this->createRequest('PUT', $uri, $headers, $body, $options);
    }

    public function patch($uri = null, $headers = null, $body = null, array $options = array())
    {
        return $this->createRequest('PATCH', $uri, $headers, $body, $options);
    }

    public function post($uri = null, $headers = null, $postBody = null, array $options = array())
    {
        return $this->createRequest('POST', $uri, $headers, $postBody, $options);
    }

    public function options($uri = null, array $options = array())
    {
        return $this->createRequest('OPTIONS', $uri, $options);
    }

    public function send($requests)
    {
        if (!($requests instanceof RequestInterface)) {
            return $this->sendMultiple($requests);
        }

        try {
            /** @var $requests RequestInterface  */
            $this->getCurlMulti()->add($requests)->send();
            return $requests->getResponse();
        } catch (ExceptionCollection $e) {
            throw $e->getFirst();
        }
    }

    /**
     * Set a curl multi object to be used internally by the client for transferring requests.
     *
     * @param CurlMultiInterface $curlMulti Multi object
     *
     * @return self
     */
    public function setCurlMulti(CurlMultiInterface $curlMulti)
    {
        $this->curlMulti = $curlMulti;

        return $this;
    }

    /**
     * @return CurlMultiInterface|CurlMultiProxy
     */
    public function getCurlMulti()
    {
        if (!$this->curlMulti) {
            $this->curlMulti = new CurlMultiProxy(
                self::MAX_HANDLES,
                $this->getConfig('select_timeout') ?: self::DEFAULT_SELECT_TIMEOUT
            );
        }

        return $this->curlMulti;
    }

    public function setRequestFactory(RequestFactoryInterface $factory)
    {
        $this->requestFactory = $factory;

        return $this;
    }

    /**
     * Set the URI template expander to use with the client
     *
     * @param UriTemplateInterface $uriTemplate URI template expander
     *
     * @return self
     */
    public function setUriTemplate(UriTemplateInterface $uriTemplate)
    {
        $this->uriTemplate = $uriTemplate;

        return $this;
    }

    /**
     * Expand a URI template while merging client config settings into the template variables
     *
     * @param string $template  Template to expand
     * @param array  $variables Variables to inject
     *
     * @return string
     */
    protected function expandTemplate($template, array $variables = null)
    {
        $expansionVars = $this->getConfig()->toArray();
        if ($variables) {
            $expansionVars = $variables + $expansionVars;
        }

        return $this->getUriTemplate()->expand($template, $expansionVars);
    }

    /**
     * Get the URI template expander used by the client
     *
     * @return UriTemplateInterface
     */
    protected function getUriTemplate()
    {
        if (!$this->uriTemplate) {
            $this->uriTemplate = ParserRegistry::getInstance()->getParser('uri_template');
        }

        return $this->uriTemplate;
    }

    /**
     * Send multiple requests in parallel
     *
     * @param array $requests Array of RequestInterface objects
     *
     * @return array Returns an array of Response objects
     */
    protected function sendMultiple(array $requests)
    {
        $curlMulti = $this->getCurlMulti();
        foreach ($requests as $request) {
            $curlMulti->add($request);
        }
        $curlMulti->send();

        /** @var $request RequestInterface */
        $result = array();
        foreach ($requests as $request) {
            $result[] = $request->getResponse();
        }

        return $result;
    }

    /**
     * Prepare a request to be sent from the Client by adding client specific behaviors and properties to the request.
     *
     * @param RequestInterface $request Request to prepare for the client
     * @param array            $options Options to apply to the request
     *
     * @return RequestInterface
     */
    protected function prepareRequest(RequestInterface $request, array $options = array())
    {
        $request->setClient($this)->setEventDispatcher(clone $this->getEventDispatcher());

        if ($curl = $this->config[self::CURL_OPTIONS]) {
            $request->getCurlOptions()->overwriteWith(CurlHandle::parseCurlConfig($curl));
        }

        if ($params = $this->config[self::REQUEST_PARAMS]) {
            Version::warn('request.params is deprecated. Use request.options to add default request options.');
            $request->getParams()->overwriteWith($params);
        }

        if ($this->userAgent && !$request->hasHeader('User-Agent')) {
            $request->setHeader('User-Agent', $this->userAgent);
        }

        if ($defaults = $this->config[self::REQUEST_OPTIONS]) {
            $this->requestFactory->applyOptions($request, $defaults, RequestFactoryInterface::OPTIONS_AS_DEFAULTS);
        }

        if ($options) {
            $this->requestFactory->applyOptions($request, $options);
        }

        $this->dispatch('client.create_request', array('client' => $this, 'request' => $request));

        return $request;
    }

    /**
     * Initializes SSL settings
     */
    protected function initSsl()
    {
        $authority = $this->config[self::SSL_CERT_AUTHORITY];

        if ($authority === 'system') {
            return;
        }

        if ($authority === null) {
            $authority = true;
        }

        if ($authority === true && substr(__FILE__, 0, 7) == 'phar://') {
            $authority = self::extractPharCacert(__DIR__ . '/Resources/cacert.pem');
        }

        $this->setSslVerification($authority);
    }

    /**
     * @deprecated
     */
    public function getDefaultHeaders()
    {
        Version::warn(__METHOD__ . ' is deprecated. Use the request.options array to retrieve default request options');
        return $this->defaultHeaders;
    }

    /**
     * @deprecated
     */
    public function setDefaultHeaders($headers)
    {
        Version::warn(__METHOD__ . ' is deprecated. Use the request.options array to specify default request options');
        if ($headers instanceof Collection) {
            $this->defaultHeaders = $headers;
        } elseif (is_array($headers)) {
            $this->defaultHeaders = new Collection($headers);
        } else {
            throw new InvalidArgumentException('Headers must be an array or Collection');
        }

        return $this;
    }

    /**
     * @deprecated
     */
    public function preparePharCacert($md5Check = true)
    {
        return sys_get_temp_dir() . '/guzzle-cacert.pem';
    }

    /**
     * Copies the phar cacert from a phar into the temp directory.
     *
     * @param string $pharCacertPath Path to the phar cacert. For example:
     *                               'phar://aws.phar/Guzzle/Http/Resources/cacert.pem'
     *
     * @return string Returns the path to the extracted cacert file.
     * @throws \RuntimeException Throws if the phar cacert cannot be found or
     *                           the file cannot be copied to the temp dir.
     */
    public static function extractPharCacert($pharCacertPath)
    {
        // Copy the cacert.pem file from the phar if it is not in the temp
        // folder.
        $certFile = sys_get_temp_dir() . '/guzzle-cacert.pem';

        if (!file_exists($pharCacertPath)) {
            throw new \RuntimeException("Could not find $pharCacertPath");
        }

        if (!file_exists($certFile) ||
            filesize($certFile) != filesize($pharCacertPath)
        ) {
            if (!copy($pharCacertPath, $certFile)) {
                throw new \RuntimeException(
                    "Could not copy {$pharCacertPath} to {$certFile}: "
                    . var_export(error_get_last(), true)
                );
            }
        }

        return $certFile;
    }
}
vendor/guzzle/guzzle/UPGRADING.md000064400000052026151327705700012557 0ustar00Guzzle Upgrade Guide
====================

3.6 to 3.7
----------

### Deprecations

- You can now enable E_USER_DEPRECATED warnings to see if you are using any deprecated methods.:

```php
\Guzzle\Common\Version::$emitWarnings = true;
```

The following APIs and options have been marked as deprecated:

- Marked `Guzzle\Http\Message\Request::isResponseBodyRepeatable()` as deprecated. Use `$request->getResponseBody()->isRepeatable()` instead.
- Marked `Guzzle\Http\Message\Request::canCache()` as deprecated. Use `Guzzle\Plugin\Cache\DefaultCanCacheStrategy->canCacheRequest()` instead.
- Marked `Guzzle\Http\Message\Request::canCache()` as deprecated. Use `Guzzle\Plugin\Cache\DefaultCanCacheStrategy->canCacheRequest()` instead.
- Marked `Guzzle\Http\Message\Request::setIsRedirect()` as deprecated. Use the HistoryPlugin instead.
- Marked `Guzzle\Http\Message\Request::isRedirect()` as deprecated. Use the HistoryPlugin instead.
- Marked `Guzzle\Cache\CacheAdapterFactory::factory()` as deprecated
- Marked `Guzzle\Service\Client::enableMagicMethods()` as deprecated. Magic methods can no longer be disabled on a Guzzle\Service\Client.
- Marked `Guzzle\Parser\Url\UrlParser` as deprecated. Just use PHP's `parse_url()` and percent encode your UTF-8.
- Marked `Guzzle\Common\Collection::inject()` as deprecated.
- Marked `Guzzle\Plugin\CurlAuth\CurlAuthPlugin` as deprecated. Use
  `$client->getConfig()->setPath('request.options/auth', array('user', 'pass', 'Basic|Digest|NTLM|Any'));` or
  `$client->setDefaultOption('auth', array('user', 'pass', 'Basic|Digest|NTLM|Any'));`

3.7 introduces `request.options` as a parameter for a client configuration and as an optional argument to all creational
request methods. When paired with a client's configuration settings, these options allow you to specify default settings
for various aspects of a request. Because these options make other previous configuration options redundant, several
configuration options and methods of a client and AbstractCommand have been deprecated.

- Marked `Guzzle\Service\Client::getDefaultHeaders()` as deprecated. Use `$client->getDefaultOption('headers')`.
- Marked `Guzzle\Service\Client::setDefaultHeaders()` as deprecated. Use `$client->setDefaultOption('headers/{header_name}', 'value')`.
- Marked 'request.params' for `Guzzle\Http\Client` as deprecated. Use `$client->setDefaultOption('params/{param_name}', 'value')`
- Marked 'command.headers', 'command.response_body' and 'command.on_complete' as deprecated for AbstractCommand. These will work through Guzzle 4.0

        $command = $client->getCommand('foo', array(
            'command.headers' => array('Test' => '123'),
            'command.response_body' => '/path/to/file'
        ));

        // Should be changed to:

        $command = $client->getCommand('foo', array(
            'command.request_options' => array(
                'headers' => array('Test' => '123'),
                'save_as' => '/path/to/file'
            )
        ));

### Interface changes

Additions and changes (you will need to update any implementations or subclasses you may have created):

- Added an `$options` argument to the end of the following methods of `Guzzle\Http\ClientInterface`:
  createRequest, head, delete, put, patch, post, options, prepareRequest
- Added an `$options` argument to the end of `Guzzle\Http\Message\Request\RequestFactoryInterface::createRequest()`
- Added an `applyOptions()` method to `Guzzle\Http\Message\Request\RequestFactoryInterface`
- Changed `Guzzle\Http\ClientInterface::get($uri = null, $headers = null, $body = null)` to
  `Guzzle\Http\ClientInterface::get($uri = null, $headers = null, $options = array())`. You can still pass in a
  resource, string, or EntityBody into the $options parameter to specify the download location of the response.
- Changed `Guzzle\Common\Collection::__construct($data)` to no longer accepts a null value for `$data` but a
  default `array()`
- Added `Guzzle\Stream\StreamInterface::isRepeatable`
- Made `Guzzle\Http\Client::expandTemplate` and `getUriTemplate` protected methods.

The following methods were removed from interfaces. All of these methods are still available in the concrete classes
that implement them, but you should update your code to use alternative methods:

- Removed `Guzzle\Http\ClientInterface::setDefaultHeaders(). Use
  `$client->getConfig()->setPath('request.options/headers/{header_name}', 'value')`. or
  `$client->getConfig()->setPath('request.options/headers', array('header_name' => 'value'))` or
  `$client->setDefaultOption('headers/{header_name}', 'value')`. or
  `$client->setDefaultOption('headers', array('header_name' => 'value'))`.
- Removed `Guzzle\Http\ClientInterface::getDefaultHeaders(). Use `$client->getConfig()->getPath('request.options/headers')`.
- Removed `Guzzle\Http\ClientInterface::expandTemplate()`. This is an implementation detail.
- Removed `Guzzle\Http\ClientInterface::setRequestFactory()`. This is an implementation detail.
- Removed `Guzzle\Http\ClientInterface::getCurlMulti()`. This is a very specific implementation detail.
- Removed `Guzzle\Http\Message\RequestInterface::canCache`. Use the CachePlugin.
- Removed `Guzzle\Http\Message\RequestInterface::setIsRedirect`. Use the HistoryPlugin.
- Removed `Guzzle\Http\Message\RequestInterface::isRedirect`. Use the HistoryPlugin.

### Cache plugin breaking changes

- CacheKeyProviderInterface and DefaultCacheKeyProvider are no longer used. All of this logic is handled in a
  CacheStorageInterface. These two objects and interface will be removed in a future version.
- Always setting X-cache headers on cached responses
- Default cache TTLs are now handled by the CacheStorageInterface of a CachePlugin
- `CacheStorageInterface::cache($key, Response $response, $ttl = null)` has changed to `cache(RequestInterface
  $request, Response $response);`
- `CacheStorageInterface::fetch($key)` has changed to `fetch(RequestInterface $request);`
- `CacheStorageInterface::delete($key)` has changed to `delete(RequestInterface $request);`
- Added `CacheStorageInterface::purge($url)`
- `DefaultRevalidation::__construct(CacheKeyProviderInterface $cacheKey, CacheStorageInterface $cache, CachePlugin
  $plugin)` has changed to `DefaultRevalidation::__construct(CacheStorageInterface $cache,
  CanCacheStrategyInterface $canCache = null)`
- Added `RevalidationInterface::shouldRevalidate(RequestInterface $request, Response $response)`

3.5 to 3.6
----------

* Mixed casing of headers are now forced to be a single consistent casing across all values for that header.
* Messages internally use a HeaderCollection object to delegate handling case-insensitive header resolution
* Removed the whole changedHeader() function system of messages because all header changes now go through addHeader().
  For example, setHeader() first removes the header using unset on a HeaderCollection and then calls addHeader().
  Keeping the Host header and URL host in sync is now handled by overriding the addHeader method in Request.
* Specific header implementations can be created for complex headers. When a message creates a header, it uses a
  HeaderFactory which can map specific headers to specific header classes. There is now a Link header and
  CacheControl header implementation.
* Moved getLinks() from Response to just be used on a Link header object.

If you previously relied on Guzzle\Http\Message\Header::raw(), then you will need to update your code to use the
HeaderInterface (e.g. toArray(), getAll(), etc).

### Interface changes

* Removed from interface: Guzzle\Http\ClientInterface::setUriTemplate
* Removed from interface: Guzzle\Http\ClientInterface::setCurlMulti()
* Removed Guzzle\Http\Message\Request::receivedRequestHeader() and implemented this functionality in
  Guzzle\Http\Curl\RequestMediator
* Removed the optional $asString parameter from MessageInterface::getHeader(). Just cast the header to a string.
* Removed the optional $tryChunkedTransfer option from Guzzle\Http\Message\EntityEnclosingRequestInterface
* Removed the $asObjects argument from Guzzle\Http\Message\MessageInterface::getHeaders()

### Removed deprecated functions

* Removed Guzzle\Parser\ParserRegister::get(). Use getParser()
* Removed Guzzle\Parser\ParserRegister::set(). Use registerParser().

### Deprecations

* The ability to case-insensitively search for header values
* Guzzle\Http\Message\Header::hasExactHeader
* Guzzle\Http\Message\Header::raw. Use getAll()
* Deprecated cache control specific methods on Guzzle\Http\Message\AbstractMessage. Use the CacheControl header object
  instead.

### Other changes

* All response header helper functions return a string rather than mixing Header objects and strings inconsistently
* Removed cURL blacklist support. This is no longer necessary now that Expect, Accept, etc are managed by Guzzle
  directly via interfaces
* Removed the injecting of a request object onto a response object. The methods to get and set a request still exist
  but are a no-op until removed.
* Most classes that used to require a ``Guzzle\Service\Command\CommandInterface` typehint now request a
  `Guzzle\Service\Command\ArrayCommandInterface`.
* Added `Guzzle\Http\Message\RequestInterface::startResponse()` to the RequestInterface to handle injecting a response
  on a request while the request is still being transferred
* `Guzzle\Service\Command\CommandInterface` now extends from ToArrayInterface and ArrayAccess

3.3 to 3.4
----------

Base URLs of a client now follow the rules of http://tools.ietf.org/html/rfc3986#section-5.2.2 when merging URLs.

3.2 to 3.3
----------

### Response::getEtag() quote stripping removed

`Guzzle\Http\Message\Response::getEtag()` no longer strips quotes around the ETag response header

### Removed `Guzzle\Http\Utils`

The `Guzzle\Http\Utils` class was removed. This class was only used for testing.

### Stream wrapper and type

`Guzzle\Stream\Stream::getWrapper()` and `Guzzle\Stream\Stream::getSteamType()` are no longer converted to lowercase.

### curl.emit_io became emit_io

Emitting IO events from a RequestMediator is now a parameter that must be set in a request's curl options using the
'emit_io' key. This was previously set under a request's parameters using 'curl.emit_io'

3.1 to 3.2
----------

### CurlMulti is no longer reused globally

Before 3.2, the same CurlMulti object was reused globally for each client. This can cause issue where plugins added
to a single client can pollute requests dispatched from other clients.

If you still wish to reuse the same CurlMulti object with each client, then you can add a listener to the
ServiceBuilder's `service_builder.create_client` event to inject a custom CurlMulti object into each client as it is
created.

```php
$multi = new Guzzle\Http\Curl\CurlMulti();
$builder = Guzzle\Service\Builder\ServiceBuilder::factory('/path/to/config.json');
$builder->addListener('service_builder.create_client', function ($event) use ($multi) {
    $event['client']->setCurlMulti($multi);
}
});
```

### No default path

URLs no longer have a default path value of '/' if no path was specified.

Before:

```php
$request = $client->get('http://www.foo.com');
echo $request->getUrl();
// >> http://www.foo.com/
```

After:

```php
$request = $client->get('http://www.foo.com');
echo $request->getUrl();
// >> http://www.foo.com
```

### Less verbose BadResponseException

The exception message for `Guzzle\Http\Exception\BadResponseException` no longer contains the full HTTP request and
response information. You can, however, get access to the request and response object by calling `getRequest()` or
`getResponse()` on the exception object.

### Query parameter aggregation

Multi-valued query parameters are no longer aggregated using a callback function. `Guzzle\Http\Query` now has a
setAggregator() method that accepts a `Guzzle\Http\QueryAggregator\QueryAggregatorInterface` object. This object is
responsible for handling the aggregation of multi-valued query string variables into a flattened hash.

2.8 to 3.x
----------

### Guzzle\Service\Inspector

Change `\Guzzle\Service\Inspector::fromConfig` to `\Guzzle\Common\Collection::fromConfig`

**Before**

```php
use Guzzle\Service\Inspector;

class YourClient extends \Guzzle\Service\Client
{
    public static function factory($config = array())
    {
        $default = array();
        $required = array('base_url', 'username', 'api_key');
        $config = Inspector::fromConfig($config, $default, $required);

        $client = new self(
            $config->get('base_url'),
            $config->get('username'),
            $config->get('api_key')
        );
        $client->setConfig($config);

        $client->setDescription(ServiceDescription::factory(__DIR__ . DIRECTORY_SEPARATOR . 'client.json'));

        return $client;
    }
```

**After**

```php
use Guzzle\Common\Collection;

class YourClient extends \Guzzle\Service\Client
{
    public static function factory($config = array())
    {
        $default = array();
        $required = array('base_url', 'username', 'api_key');
        $config = Collection::fromConfig($config, $default, $required);

        $client = new self(
            $config->get('base_url'),
            $config->get('username'),
            $config->get('api_key')
        );
        $client->setConfig($config);

        $client->setDescription(ServiceDescription::factory(__DIR__ . DIRECTORY_SEPARATOR . 'client.json'));

        return $client;
    }
```

### Convert XML Service Descriptions to JSON

**Before**

```xml
<?xml version="1.0" encoding="UTF-8"?>
<client>
    <commands>
        <!-- Groups -->
        <command name="list_groups" method="GET" uri="groups.json">
            <doc>Get a list of groups</doc>
        </command>
        <command name="search_groups" method="GET" uri='search.json?query="{{query}} type:group"'>
            <doc>Uses a search query to get a list of groups</doc>
            <param name="query" type="string" required="true" />
        </command>
        <command name="create_group" method="POST" uri="groups.json">
            <doc>Create a group</doc>
            <param name="data" type="array" location="body" filters="json_encode" doc="Group JSON"/>
            <param name="Content-Type" location="header" static="application/json"/>
        </command>
        <command name="delete_group" method="DELETE" uri="groups/{{id}}.json">
            <doc>Delete a group by ID</doc>
            <param name="id" type="integer" required="true"/>
        </command>
        <command name="get_group" method="GET" uri="groups/{{id}}.json">
            <param name="id" type="integer" required="true"/>
        </command>
        <command name="update_group" method="PUT" uri="groups/{{id}}.json">
            <doc>Update a group</doc>
            <param name="id" type="integer" required="true"/>
            <param name="data" type="array" location="body" filters="json_encode" doc="Group JSON"/>
            <param name="Content-Type" location="header" static="application/json"/>
        </command>
    </commands>
</client>
```

**After**

```json
{
    "name":       "Zendesk REST API v2",
    "apiVersion": "2012-12-31",
    "description":"Provides access to Zendesk views, groups, tickets, ticket fields, and users",
    "operations": {
        "list_groups":  {
            "httpMethod":"GET",
            "uri":       "groups.json",
            "summary":   "Get a list of groups"
        },
        "search_groups":{
            "httpMethod":"GET",
            "uri":       "search.json?query=\"{query} type:group\"",
            "summary":   "Uses a search query to get a list of groups",
            "parameters":{
                "query":{
                    "location":   "uri",
                    "description":"Zendesk Search Query",
                    "type":       "string",
                    "required":   true
                }
            }
        },
        "create_group": {
            "httpMethod":"POST",
            "uri":       "groups.json",
            "summary":   "Create a group",
            "parameters":{
                "data":        {
                    "type":       "array",
                    "location":   "body",
                    "description":"Group JSON",
                    "filters":    "json_encode",
                    "required":   true
                },
                "Content-Type":{
                    "type":    "string",
                    "location":"header",
                    "static":  "application/json"
                }
            }
        },
        "delete_group": {
            "httpMethod":"DELETE",
            "uri":       "groups/{id}.json",
            "summary":   "Delete a group",
            "parameters":{
                "id":{
                    "location":   "uri",
                    "description":"Group to delete by ID",
                    "type":       "integer",
                    "required":   true
                }
            }
        },
        "get_group":    {
            "httpMethod":"GET",
            "uri":       "groups/{id}.json",
            "summary":   "Get a ticket",
            "parameters":{
                "id":{
                    "location":   "uri",
                    "description":"Group to get by ID",
                    "type":       "integer",
                    "required":   true
                }
            }
        },
        "update_group": {
            "httpMethod":"PUT",
            "uri":       "groups/{id}.json",
            "summary":   "Update a group",
            "parameters":{
                "id":          {
                    "location":   "uri",
                    "description":"Group to update by ID",
                    "type":       "integer",
                    "required":   true
                },
                "data":        {
                    "type":       "array",
                    "location":   "body",
                    "description":"Group JSON",
                    "filters":    "json_encode",
                    "required":   true
                },
                "Content-Type":{
                    "type":    "string",
                    "location":"header",
                    "static":  "application/json"
                }
            }
        }
}
```

### Guzzle\Service\Description\ServiceDescription

Commands are now called Operations

**Before**

```php
use Guzzle\Service\Description\ServiceDescription;

$sd = new ServiceDescription();
$sd->getCommands();     // @returns ApiCommandInterface[]
$sd->hasCommand($name);
$sd->getCommand($name); // @returns ApiCommandInterface|null
$sd->addCommand($command); // @param ApiCommandInterface $command
```

**After**

```php
use Guzzle\Service\Description\ServiceDescription;

$sd = new ServiceDescription();
$sd->getOperations();           // @returns OperationInterface[]
$sd->hasOperation($name);
$sd->getOperation($name);       // @returns OperationInterface|null
$sd->addOperation($operation);  // @param OperationInterface $operation
```

### Guzzle\Common\Inflection\Inflector

Namespace is now `Guzzle\Inflection\Inflector`

### Guzzle\Http\Plugin

Namespace is now `Guzzle\Plugin`. Many other changes occur within this namespace and are detailed in their own sections below.

### Guzzle\Http\Plugin\LogPlugin and Guzzle\Common\Log

Now `Guzzle\Plugin\Log\LogPlugin` and `Guzzle\Log` respectively.

**Before**

```php
use Guzzle\Common\Log\ClosureLogAdapter;
use Guzzle\Http\Plugin\LogPlugin;

/** @var \Guzzle\Http\Client */
$client;

// $verbosity is an integer indicating desired message verbosity level
$client->addSubscriber(new LogPlugin(new ClosureLogAdapter(function($m) { echo $m; }, $verbosity = LogPlugin::LOG_VERBOSE);
```

**After**

```php
use Guzzle\Log\ClosureLogAdapter;
use Guzzle\Log\MessageFormatter;
use Guzzle\Plugin\Log\LogPlugin;

/** @var \Guzzle\Http\Client */
$client;

// $format is a string indicating desired message format -- @see MessageFormatter
$client->addSubscriber(new LogPlugin(new ClosureLogAdapter(function($m) { echo $m; }, $format = MessageFormatter::DEBUG_FORMAT);
```

### Guzzle\Http\Plugin\CurlAuthPlugin

Now `Guzzle\Plugin\CurlAuth\CurlAuthPlugin`.

### Guzzle\Http\Plugin\ExponentialBackoffPlugin

Now `Guzzle\Plugin\Backoff\BackoffPlugin`, and other changes.

**Before**

```php
use Guzzle\Http\Plugin\ExponentialBackoffPlugin;

$backoffPlugin = new ExponentialBackoffPlugin($maxRetries, array_merge(
        ExponentialBackoffPlugin::getDefaultFailureCodes(), array(429)
    ));

$client->addSubscriber($backoffPlugin);
```

**After**

```php
use Guzzle\Plugin\Backoff\BackoffPlugin;
use Guzzle\Plugin\Backoff\HttpBackoffStrategy;

// Use convenient factory method instead -- see implementation for ideas of what
// you can do with chaining backoff strategies
$backoffPlugin = BackoffPlugin::getExponentialBackoff($maxRetries, array_merge(
        HttpBackoffStrategy::getDefaultFailureCodes(), array(429)
    ));
$client->addSubscriber($backoffPlugin);
```

### Known Issues

#### [BUG] Accept-Encoding header behavior changed unintentionally.

(See #217) (Fixed in 09daeb8c666fb44499a0646d655a8ae36456575e)

In version 2.8 setting the `Accept-Encoding` header would set the CURLOPT_ENCODING option, which permitted cURL to
properly handle gzip/deflate compressed responses from the server. In versions affected by this bug this does not happen.
See issue #217 for a workaround, or use a version containing the fix.
vendor/guzzle/guzzle/CHANGELOG.md000064400000136351151327705700012532 0ustar00# CHANGELOG

## 3.9.3 - 2015-03-18

* Ensuring Content-Length is not stripped from a request when it is `0`.
* Added more information to stream wrapper exceptions.
* Message parser will no longer throw warnings for malformed messages.
* Giving a valid cache TTL when max-age is 0.

## 3.9.2 - 2014-09-10

* Retrying "Connection died, retrying a fresh connect" curl errors.
* Automatically extracting the cacert from the phar in client constructor.
* Added EntityBody support for OPTIONS requests.

## 3.9.1 - 2014-05-07

* Added a fix to ReadLimitEntityBody to ensure it doesn't infinitely loop.
* Added a fix to the stream checksum function so that when the first read
  returns a falsey value, it still continues to consume the stream until EOF.

## 3.9.0 - 2014-04-23

* `null`, `false`, and `"_guzzle_blank_"` all now serialize as an empty value
  with no trailing "=". See dc1d824277.
* No longer performing an MD5 check on the cacert each time the phar is used,
  but rather copying the cacert to the temp directory.
* `"0"` can now be added as a URL path
* Deleting cookies that are set to empty
* If-Modified-Since is no longer unnecessarily added to the CachePlugin
* Cookie path matching now follows RFC 6265 s5.1.4
* Updated service descriptions are now added to a service client's composite
  factory.
* MockPlugin now throws an exception if the queue is empty.
* Properly parsing URLs that start with "http" but are not absolute
* Added the ability to configure the curl_multi_select timeout setting
* OAuth parameters are now sorted using lexicographical byte value ordering
* Fixing invalid usage of an out of range PHP feature in the ErrorResponsePlugin

## 3.8.1 -2014-01-28

* Bug: Always using GET requests when redirecting from a 303 response
* Bug: CURLOPT_SSL_VERIFYHOST is now correctly set to false when setting `$certificateAuthority` to false in
  `Guzzle\Http\ClientInterface::setSslVerification()`
* Bug: RedirectPlugin now uses strict RFC 3986 compliance when combining a base URL with a relative URL
* Bug: The body of a request can now be set to `"0"`
* Sending PHP stream requests no longer forces `HTTP/1.0`
* Adding more information to ExceptionCollection exceptions so that users have more context, including a stack trace of
  each sub-exception
* Updated the `$ref` attribute in service descriptions to merge over any existing parameters of a schema (rather than
  clobbering everything).
* Merging URLs will now use the query string object from the relative URL (thus allowing custom query aggregators)
* Query strings are now parsed in a way that they do no convert empty keys with no value to have a dangling `=`.
  For example `foo&bar=baz` is now correctly parsed and recognized as `foo&bar=baz` rather than `foo=&bar=baz`.
* Now properly escaping the regular expression delimiter when matching Cookie domains.
* Network access is now disabled when loading XML documents

## 3.8.0 - 2013-12-05

* Added the ability to define a POST name for a file
* JSON response parsing now properly walks additionalProperties
* cURL error code 18 is now retried automatically in the BackoffPlugin
* Fixed a cURL error when URLs contain fragments
* Fixed an issue in the BackoffPlugin retry event where it was trying to access all exceptions as if they were
  CurlExceptions
* CURLOPT_PROGRESS function fix for PHP 5.5 (69fcc1e)
* Added the ability for Guzzle to work with older versions of cURL that do not support `CURLOPT_TIMEOUT_MS`
* Fixed a bug that was encountered when parsing empty header parameters
* UriTemplate now has a `setRegex()` method to match the docs
* The `debug` request parameter now checks if it is truthy rather than if it exists
* Setting the `debug` request parameter to true shows verbose cURL output instead of using the LogPlugin
* Added the ability to combine URLs using strict RFC 3986 compliance
* Command objects can now return the validation errors encountered by the command
* Various fixes to cache revalidation (#437 and 29797e5)
* Various fixes to the AsyncPlugin
* Cleaned up build scripts

## 3.7.4 - 2013-10-02

* Bug fix: 0 is now an allowed value in a description parameter that has a default value (#430)
* Bug fix: SchemaFormatter now returns an integer when formatting to a Unix timestamp
  (see https://github.com/aws/aws-sdk-php/issues/147)
* Bug fix: Cleaned up and fixed URL dot segment removal to properly resolve internal dots
* Minimum PHP version is now properly specified as 5.3.3 (up from 5.3.2) (#420)
* Updated the bundled cacert.pem (#419)
* OauthPlugin now supports adding authentication to headers or query string (#425)

## 3.7.3 - 2013-09-08

* Added the ability to get the exception associated with a request/command when using `MultiTransferException` and
  `CommandTransferException`.
* Setting `additionalParameters` of a response to false is now honored when parsing responses with a service description
* Schemas are only injected into response models when explicitly configured.
* No longer guessing Content-Type based on the path of a request. Content-Type is now only guessed based on the path of
  an EntityBody.
* Bug fix: ChunkedIterator can now properly chunk a \Traversable as well as an \Iterator.
* Bug fix: FilterIterator now relies on `\Iterator` instead of `\Traversable`.
* Bug fix: Gracefully handling malformed responses in RequestMediator::writeResponseBody()
* Bug fix: Replaced call to canCache with canCacheRequest in the CallbackCanCacheStrategy of the CachePlugin
* Bug fix: Visiting XML attributes first before visting XML children when serializing requests
* Bug fix: Properly parsing headers that contain commas contained in quotes
* Bug fix: mimetype guessing based on a filename is now case-insensitive

## 3.7.2 - 2013-08-02

* Bug fix: Properly URL encoding paths when using the PHP-only version of the UriTemplate expander
  See https://github.com/guzzle/guzzle/issues/371
* Bug fix: Cookie domains are now matched correctly according to RFC 6265
  See https://github.com/guzzle/guzzle/issues/377
* Bug fix: GET parameters are now used when calculating an OAuth signature
* Bug fix: Fixed an issue with cache revalidation where the If-None-Match header was being double quoted
* `Guzzle\Common\AbstractHasDispatcher::dispatch()` now returns the event that was dispatched
* `Guzzle\Http\QueryString::factory()` now guesses the most appropriate query aggregator to used based on the input.
  See https://github.com/guzzle/guzzle/issues/379
* Added a way to add custom domain objects to service description parsing using the `operation.parse_class` event. See
  https://github.com/guzzle/guzzle/pull/380
* cURL multi cleanup and optimizations

## 3.7.1 - 2013-07-05

* Bug fix: Setting default options on a client now works
* Bug fix: Setting options on HEAD requests now works. See #352
* Bug fix: Moving stream factory before send event to before building the stream. See #353
* Bug fix: Cookies no longer match on IP addresses per RFC 6265
* Bug fix: Correctly parsing header parameters that are in `<>` and quotes
* Added `cert` and `ssl_key` as request options
* `Host` header can now diverge from the host part of a URL if the header is set manually
* `Guzzle\Service\Command\LocationVisitor\Request\XmlVisitor` was rewritten to change from using SimpleXML to XMLWriter
* OAuth parameters are only added via the plugin if they aren't already set
* Exceptions are now thrown when a URL cannot be parsed
* Returning `false` if `Guzzle\Http\EntityBody::getContentMd5()` fails
* Not setting a `Content-MD5` on a command if calculating the Content-MD5 fails via the CommandContentMd5Plugin

## 3.7.0 - 2013-06-10

* See UPGRADING.md for more information on how to upgrade.
* Requests now support the ability to specify an array of $options when creating a request to more easily modify a
  request. You can pass a 'request.options' configuration setting to a client to apply default request options to
  every request created by a client (e.g. default query string variables, headers, curl options, etc).
* Added a static facade class that allows you to use Guzzle with static methods and mount the class to `\Guzzle`.
  See `Guzzle\Http\StaticClient::mount`.
* Added `command.request_options` to `Guzzle\Service\Command\AbstractCommand` to pass request options to requests
      created by a command (e.g. custom headers, query string variables, timeout settings, etc).
* Stream size in `Guzzle\Stream\PhpStreamRequestFactory` will now be set if Content-Length is returned in the
  headers of a response
* Added `Guzzle\Common\Collection::setPath($path, $value)` to set a value into an array using a nested key
  (e.g. `$collection->setPath('foo/baz/bar', 'test'); echo $collection['foo']['bar']['bar'];`)
* ServiceBuilders now support storing and retrieving arbitrary data
* CachePlugin can now purge all resources for a given URI
* CachePlugin can automatically purge matching cached items when a non-idempotent request is sent to a resource
* CachePlugin now uses the Vary header to determine if a resource is a cache hit
* `Guzzle\Http\Message\Response` now implements `\Serializable`
* Added `Guzzle\Cache\CacheAdapterFactory::fromCache()` to more easily create cache adapters
* `Guzzle\Service\ClientInterface::execute()` now accepts an array, single command, or Traversable
* Fixed a bug in `Guzzle\Http\Message\Header\Link::addLink()`
* Better handling of calculating the size of a stream in `Guzzle\Stream\Stream` using fstat() and caching the size
* `Guzzle\Common\Exception\ExceptionCollection` now creates a more readable exception message
* Fixing BC break: Added back the MonologLogAdapter implementation rather than extending from PsrLog so that older
  Symfony users can still use the old version of Monolog.
* Fixing BC break: Added the implementation back in for `Guzzle\Http\Message\AbstractMessage::getTokenizedHeader()`.
  Now triggering an E_USER_DEPRECATED warning when used. Use `$message->getHeader()->parseParams()`.
* Several performance improvements to `Guzzle\Common\Collection`
* Added an `$options` argument to the end of the following methods of `Guzzle\Http\ClientInterface`:
  createRequest, head, delete, put, patch, post, options, prepareRequest
* Added an `$options` argument to the end of `Guzzle\Http\Message\Request\RequestFactoryInterface::createRequest()`
* Added an `applyOptions()` method to `Guzzle\Http\Message\Request\RequestFactoryInterface`
* Changed `Guzzle\Http\ClientInterface::get($uri = null, $headers = null, $body = null)` to
  `Guzzle\Http\ClientInterface::get($uri = null, $headers = null, $options = array())`. You can still pass in a
  resource, string, or EntityBody into the $options parameter to specify the download location of the response.
* Changed `Guzzle\Common\Collection::__construct($data)` to no longer accepts a null value for `$data` but a
  default `array()`
* Added `Guzzle\Stream\StreamInterface::isRepeatable`
* Removed `Guzzle\Http\ClientInterface::setDefaultHeaders(). Use
  $client->getConfig()->setPath('request.options/headers/{header_name}', 'value')`. or
  $client->getConfig()->setPath('request.options/headers', array('header_name' => 'value'))`.
* Removed `Guzzle\Http\ClientInterface::getDefaultHeaders(). Use $client->getConfig()->getPath('request.options/headers')`.
* Removed `Guzzle\Http\ClientInterface::expandTemplate()`
* Removed `Guzzle\Http\ClientInterface::setRequestFactory()`
* Removed `Guzzle\Http\ClientInterface::getCurlMulti()`
* Removed `Guzzle\Http\Message\RequestInterface::canCache`
* Removed `Guzzle\Http\Message\RequestInterface::setIsRedirect`
* Removed `Guzzle\Http\Message\RequestInterface::isRedirect`
* Made `Guzzle\Http\Client::expandTemplate` and `getUriTemplate` protected methods.
* You can now enable E_USER_DEPRECATED warnings to see if you are using a deprecated method by setting
  `Guzzle\Common\Version::$emitWarnings` to true.
* Marked `Guzzle\Http\Message\Request::isResponseBodyRepeatable()` as deprecated. Use
      `$request->getResponseBody()->isRepeatable()` instead.
* Marked `Guzzle\Http\Message\Request::canCache()` as deprecated. Use
  `Guzzle\Plugin\Cache\DefaultCanCacheStrategy->canCacheRequest()` instead.
* Marked `Guzzle\Http\Message\Request::canCache()` as deprecated. Use
  `Guzzle\Plugin\Cache\DefaultCanCacheStrategy->canCacheRequest()` instead.
* Marked `Guzzle\Http\Message\Request::setIsRedirect()` as deprecated. Use the HistoryPlugin instead.
* Marked `Guzzle\Http\Message\Request::isRedirect()` as deprecated. Use the HistoryPlugin instead.
* Marked `Guzzle\Cache\CacheAdapterFactory::factory()` as deprecated
* Marked 'command.headers', 'command.response_body' and 'command.on_complete' as deprecated for AbstractCommand.
  These will work through Guzzle 4.0
* Marked 'request.params' for `Guzzle\Http\Client` as deprecated. Use [request.options][params].
* Marked `Guzzle\Service\Client::enableMagicMethods()` as deprecated. Magic methods can no longer be disabled on a Guzzle\Service\Client.
* Marked `Guzzle\Service\Client::getDefaultHeaders()` as deprecated. Use $client->getConfig()->getPath('request.options/headers')`.
* Marked `Guzzle\Service\Client::setDefaultHeaders()` as deprecated. Use $client->getConfig()->setPath('request.options/headers/{header_name}', 'value')`.
* Marked `Guzzle\Parser\Url\UrlParser` as deprecated. Just use PHP's `parse_url()` and percent encode your UTF-8.
* Marked `Guzzle\Common\Collection::inject()` as deprecated.
* Marked `Guzzle\Plugin\CurlAuth\CurlAuthPlugin` as deprecated. Use `$client->getConfig()->setPath('request.options/auth', array('user', 'pass', 'Basic|Digest');`
* CacheKeyProviderInterface and DefaultCacheKeyProvider are no longer used. All of this logic is handled in a
  CacheStorageInterface. These two objects and interface will be removed in a future version.
* Always setting X-cache headers on cached responses
* Default cache TTLs are now handled by the CacheStorageInterface of a CachePlugin
* `CacheStorageInterface::cache($key, Response $response, $ttl = null)` has changed to `cache(RequestInterface
  $request, Response $response);`
* `CacheStorageInterface::fetch($key)` has changed to `fetch(RequestInterface $request);`
* `CacheStorageInterface::delete($key)` has changed to `delete(RequestInterface $request);`
* Added `CacheStorageInterface::purge($url)`
* `DefaultRevalidation::__construct(CacheKeyProviderInterface $cacheKey, CacheStorageInterface $cache, CachePlugin
  $plugin)` has changed to `DefaultRevalidation::__construct(CacheStorageInterface $cache,
  CanCacheStrategyInterface $canCache = null)`
* Added `RevalidationInterface::shouldRevalidate(RequestInterface $request, Response $response)`

## 3.6.0 - 2013-05-29

* ServiceDescription now implements ToArrayInterface
* Added command.hidden_params to blacklist certain headers from being treated as additionalParameters
* Guzzle can now correctly parse incomplete URLs
* Mixed casing of headers are now forced to be a single consistent casing across all values for that header.
* Messages internally use a HeaderCollection object to delegate handling case-insensitive header resolution
* Removed the whole changedHeader() function system of messages because all header changes now go through addHeader().
* Specific header implementations can be created for complex headers. When a message creates a header, it uses a
  HeaderFactory which can map specific headers to specific header classes. There is now a Link header and
  CacheControl header implementation.
* Removed from interface: Guzzle\Http\ClientInterface::setUriTemplate
* Removed from interface: Guzzle\Http\ClientInterface::setCurlMulti()
* Removed Guzzle\Http\Message\Request::receivedRequestHeader() and implemented this functionality in
  Guzzle\Http\Curl\RequestMediator
* Removed the optional $asString parameter from MessageInterface::getHeader(). Just cast the header to a string.
* Removed the optional $tryChunkedTransfer option from Guzzle\Http\Message\EntityEnclosingRequestInterface
* Removed the $asObjects argument from Guzzle\Http\Message\MessageInterface::getHeaders()
* Removed Guzzle\Parser\ParserRegister::get(). Use getParser()
* Removed Guzzle\Parser\ParserRegister::set(). Use registerParser().
* All response header helper functions return a string rather than mixing Header objects and strings inconsistently
* Removed cURL blacklist support. This is no longer necessary now that Expect, Accept, etc are managed by Guzzle
  directly via interfaces
* Removed the injecting of a request object onto a response object. The methods to get and set a request still exist
  but are a no-op until removed.
* Most classes that used to require a ``Guzzle\Service\Command\CommandInterface` typehint now request a
  `Guzzle\Service\Command\ArrayCommandInterface`.
* Added `Guzzle\Http\Message\RequestInterface::startResponse()` to the RequestInterface to handle injecting a response
  on a request while the request is still being transferred
* The ability to case-insensitively search for header values
* Guzzle\Http\Message\Header::hasExactHeader
* Guzzle\Http\Message\Header::raw. Use getAll()
* Deprecated cache control specific methods on Guzzle\Http\Message\AbstractMessage. Use the CacheControl header object
  instead.
* `Guzzle\Service\Command\CommandInterface` now extends from ToArrayInterface and ArrayAccess
* Added the ability to cast Model objects to a string to view debug information.

## 3.5.0 - 2013-05-13

* Bug: Fixed a regression so that request responses are parsed only once per oncomplete event rather than multiple times
* Bug: Better cleanup of one-time events accross the board (when an event is meant to fire once, it will now remove
  itself from the EventDispatcher)
* Bug: `Guzzle\Log\MessageFormatter` now properly writes "total_time" and "connect_time" values
* Bug: Cloning an EntityEnclosingRequest now clones the EntityBody too
* Bug: Fixed an undefined index error when parsing nested JSON responses with a sentAs parameter that reference a
  non-existent key
* Bug: All __call() method arguments are now required (helps with mocking frameworks)
* Deprecating Response::getRequest() and now using a shallow clone of a request object to remove a circular reference
  to help with refcount based garbage collection of resources created by sending a request
* Deprecating ZF1 cache and log adapters. These will be removed in the next major version.
* Deprecating `Response::getPreviousResponse()` (method signature still exists, but it'sdeprecated). Use the
  HistoryPlugin for a history.
* Added a `responseBody` alias for the `response_body` location
* Refactored internals to no longer rely on Response::getRequest()
* HistoryPlugin can now be cast to a string
* HistoryPlugin now logs transactions rather than requests and responses to more accurately keep track of the requests
  and responses that are sent over the wire
* Added `getEffectiveUrl()` and `getRedirectCount()` to Response objects

## 3.4.3 - 2013-04-30

* Bug fix: Fixing bug introduced in 3.4.2 where redirect responses are duplicated on the final redirected response
* Added a check to re-extract the temp cacert bundle from the phar before sending each request

## 3.4.2 - 2013-04-29

* Bug fix: Stream objects now work correctly with "a" and "a+" modes
* Bug fix: Removing `Transfer-Encoding: chunked` header when a Content-Length is present
* Bug fix: AsyncPlugin no longer forces HEAD requests
* Bug fix: DateTime timezones are now properly handled when using the service description schema formatter
* Bug fix: CachePlugin now properly handles stale-if-error directives when a request to the origin server fails
* Setting a response on a request will write to the custom request body from the response body if one is specified
* LogPlugin now writes to php://output when STDERR is undefined
* Added the ability to set multiple POST files for the same key in a single call
* application/x-www-form-urlencoded POSTs now use the utf-8 charset by default
* Added the ability to queue CurlExceptions to the MockPlugin
* Cleaned up how manual responses are queued on requests (removed "queued_response" and now using request.before_send)
* Configuration loading now allows remote files

## 3.4.1 - 2013-04-16

* Large refactoring to how CurlMulti handles work. There is now a proxy that sits in front of a pool of CurlMulti
  handles. This greatly simplifies the implementation, fixes a couple bugs, and provides a small performance boost.
* Exceptions are now properly grouped when sending requests in parallel
* Redirects are now properly aggregated when a multi transaction fails
* Redirects now set the response on the original object even in the event of a failure
* Bug fix: Model names are now properly set even when using $refs
* Added support for PHP 5.5's CurlFile to prevent warnings with the deprecated @ syntax
* Added support for oauth_callback in OAuth signatures
* Added support for oauth_verifier in OAuth signatures
* Added support to attempt to retrieve a command first literally, then ucfirst, the with inflection

## 3.4.0 - 2013-04-11

* Bug fix: URLs are now resolved correctly based on http://tools.ietf.org/html/rfc3986#section-5.2. #289
* Bug fix: Absolute URLs with a path in a service description will now properly override the base URL. #289
* Bug fix: Parsing a query string with a single PHP array value will now result in an array. #263
* Bug fix: Better normalization of the User-Agent header to prevent duplicate headers. #264.
* Bug fix: Added `number` type to service descriptions.
* Bug fix: empty parameters are removed from an OAuth signature
* Bug fix: Revalidating a cache entry prefers the Last-Modified over the Date header
* Bug fix: Fixed "array to string" error when validating a union of types in a service description
* Bug fix: Removed code that attempted to determine the size of a stream when data is written to the stream
* Bug fix: Not including an `oauth_token` if the value is null in the OauthPlugin.
* Bug fix: Now correctly aggregating successful requests and failed requests in CurlMulti when a redirect occurs.
* The new default CURLOPT_TIMEOUT setting has been increased to 150 seconds so that Guzzle works on poor connections.
* Added a feature to EntityEnclosingRequest::setBody() that will automatically set the Content-Type of the request if
  the Content-Type can be determined based on the entity body or the path of the request.
* Added the ability to overwrite configuration settings in a client when grabbing a throwaway client from a builder.
* Added support for a PSR-3 LogAdapter.
* Added a `command.after_prepare` event
* Added `oauth_callback` parameter to the OauthPlugin
* Added the ability to create a custom stream class when using a stream factory
* Added a CachingEntityBody decorator
* Added support for `additionalParameters` in service descriptions to define how custom parameters are serialized.
* The bundled SSL certificate is now provided in the phar file and extracted when running Guzzle from a phar.
* You can now send any EntityEnclosingRequest with POST fields or POST files and cURL will handle creating bodies
* POST requests using a custom entity body are now treated exactly like PUT requests but with a custom cURL method. This
  means that the redirect behavior of POST requests with custom bodies will not be the same as POST requests that use
  POST fields or files (the latter is only used when emulating a form POST in the browser).
* Lots of cleanup to CurlHandle::factory and RequestFactory::createRequest

## 3.3.1 - 2013-03-10

* Added the ability to create PHP streaming responses from HTTP requests
* Bug fix: Running any filters when parsing response headers with service descriptions
* Bug fix: OauthPlugin fixes to allow for multi-dimensional array signing, and sorting parameters before signing
* Bug fix: Removed the adding of default empty arrays and false Booleans to responses in order to be consistent across
  response location visitors.
* Bug fix: Removed the possibility of creating configuration files with circular dependencies
* RequestFactory::create() now uses the key of a POST file when setting the POST file name
* Added xmlAllowEmpty to serialize an XML body even if no XML specific parameters are set

## 3.3.0 - 2013-03-03

* A large number of performance optimizations have been made
* Bug fix: Added 'wb' as a valid write mode for streams
* Bug fix: `Guzzle\Http\Message\Response::json()` now allows scalar values to be returned
* Bug fix: Fixed bug in `Guzzle\Http\Message\Response` where wrapping quotes were stripped from `getEtag()`
* BC: Removed `Guzzle\Http\Utils` class
* BC: Setting a service description on a client will no longer modify the client's command factories.
* BC: Emitting IO events from a RequestMediator is now a parameter that must be set in a request's curl options using
  the 'emit_io' key. This was previously set under a request's parameters using 'curl.emit_io'
* BC: `Guzzle\Stream\Stream::getWrapper()` and `Guzzle\Stream\Stream::getSteamType()` are no longer converted to
  lowercase
* Operation parameter objects are now lazy loaded internally
* Added ErrorResponsePlugin that can throw errors for responses defined in service description operations' errorResponses
* Added support for instantiating responseType=class responseClass classes. Classes must implement
  `Guzzle\Service\Command\ResponseClassInterface`
* Added support for additionalProperties for top-level parameters in responseType=model responseClasses. These
  additional properties also support locations and can be used to parse JSON responses where the outermost part of the
  JSON is an array
* Added support for nested renaming of JSON models (rename sentAs to name)
* CachePlugin
    * Added support for stale-if-error so that the CachePlugin can now serve stale content from the cache on error
    * Debug headers can now added to cached response in the CachePlugin

## 3.2.0 - 2013-02-14

* CurlMulti is no longer reused globally. A new multi object is created per-client. This helps to isolate clients.
* URLs with no path no longer contain a "/" by default
* Guzzle\Http\QueryString does no longer manages the leading "?". This is now handled in Guzzle\Http\Url.
* BadResponseException no longer includes the full request and response message
* Adding setData() to Guzzle\Service\Description\ServiceDescriptionInterface
* Adding getResponseBody() to Guzzle\Http\Message\RequestInterface
* Various updates to classes to use ServiceDescriptionInterface type hints rather than ServiceDescription
* Header values can now be normalized into distinct values when multiple headers are combined with a comma separated list
* xmlEncoding can now be customized for the XML declaration of a XML service description operation
* Guzzle\Http\QueryString now uses Guzzle\Http\QueryAggregator\QueryAggregatorInterface objects to add custom value
  aggregation and no longer uses callbacks
* The URL encoding implementation of Guzzle\Http\QueryString can now be customized
* Bug fix: Filters were not always invoked for array service description parameters
* Bug fix: Redirects now use a target response body rather than a temporary response body
* Bug fix: The default exponential backoff BackoffPlugin was not giving when the request threshold was exceeded
* Bug fix: Guzzle now takes the first found value when grabbing Cache-Control directives

## 3.1.2 - 2013-01-27

* Refactored how operation responses are parsed. Visitors now include a before() method responsible for parsing the
  response body. For example, the XmlVisitor now parses the XML response into an array in the before() method.
* Fixed an issue where cURL would not automatically decompress responses when the Accept-Encoding header was sent
* CURLOPT_SSL_VERIFYHOST is never set to 1 because it is deprecated (see 5e0ff2ef20f839e19d1eeb298f90ba3598784444)
* Fixed a bug where redirect responses were not chained correctly using getPreviousResponse()
* Setting default headers on a client after setting the user-agent will not erase the user-agent setting

## 3.1.1 - 2013-01-20

* Adding wildcard support to Guzzle\Common\Collection::getPath()
* Adding alias support to ServiceBuilder configs
* Adding Guzzle\Service\Resource\CompositeResourceIteratorFactory and cleaning up factory interface

## 3.1.0 - 2013-01-12

* BC: CurlException now extends from RequestException rather than BadResponseException
* BC: Renamed Guzzle\Plugin\Cache\CanCacheStrategyInterface::canCache() to canCacheRequest() and added CanCacheResponse()
* Added getData to ServiceDescriptionInterface
* Added context array to RequestInterface::setState()
* Bug: Removing hard dependency on the BackoffPlugin from Guzzle\Http
* Bug: Adding required content-type when JSON request visitor adds JSON to a command
* Bug: Fixing the serialization of a service description with custom data
* Made it easier to deal with exceptions thrown when transferring commands or requests in parallel by providing
  an array of successful and failed responses
* Moved getPath from Guzzle\Service\Resource\Model to Guzzle\Common\Collection
* Added Guzzle\Http\IoEmittingEntityBody
* Moved command filtration from validators to location visitors
* Added `extends` attributes to service description parameters
* Added getModels to ServiceDescriptionInterface

## 3.0.7 - 2012-12-19

* Fixing phar detection when forcing a cacert to system if null or true
* Allowing filename to be passed to `Guzzle\Http\Message\Request::setResponseBody()`
* Cleaning up `Guzzle\Common\Collection::inject` method
* Adding a response_body location to service descriptions

## 3.0.6 - 2012-12-09

* CurlMulti performance improvements
* Adding setErrorResponses() to Operation
* composer.json tweaks

## 3.0.5 - 2012-11-18

* Bug: Fixing an infinite recursion bug caused from revalidating with the CachePlugin
* Bug: Response body can now be a string containing "0"
* Bug: Using Guzzle inside of a phar uses system by default but now allows for a custom cacert
* Bug: QueryString::fromString now properly parses query string parameters that contain equal signs
* Added support for XML attributes in service description responses
* DefaultRequestSerializer now supports array URI parameter values for URI template expansion
* Added better mimetype guessing to requests and post files

## 3.0.4 - 2012-11-11

* Bug: Fixed a bug when adding multiple cookies to a request to use the correct glue value
* Bug: Cookies can now be added that have a name, domain, or value set to "0"
* Bug: Using the system cacert bundle when using the Phar
* Added json and xml methods to Response to make it easier to parse JSON and XML response data into data structures
* Enhanced cookie jar de-duplication
* Added the ability to enable strict cookie jars that throw exceptions when invalid cookies are added
* Added setStream to StreamInterface to actually make it possible to implement custom rewind behavior for entity bodies
* Added the ability to create any sort of hash for a stream rather than just an MD5 hash

## 3.0.3 - 2012-11-04

* Implementing redirects in PHP rather than cURL
* Added PECL URI template extension and using as default parser if available
* Bug: Fixed Content-Length parsing of Response factory
* Adding rewind() method to entity bodies and streams. Allows for custom rewinding of non-repeatable streams.
* Adding ToArrayInterface throughout library
* Fixing OauthPlugin to create unique nonce values per request

## 3.0.2 - 2012-10-25

* Magic methods are enabled by default on clients
* Magic methods return the result of a command
* Service clients no longer require a base_url option in the factory
* Bug: Fixed an issue with URI templates where null template variables were being expanded

## 3.0.1 - 2012-10-22

* Models can now be used like regular collection objects by calling filter, map, etc
* Models no longer require a Parameter structure or initial data in the constructor
* Added a custom AppendIterator to get around a PHP bug with the `\AppendIterator`

## 3.0.0 - 2012-10-15

* Rewrote service description format to be based on Swagger
    * Now based on JSON schema
    * Added nested input structures and nested response models
    * Support for JSON and XML input and output models
    * Renamed `commands` to `operations`
    * Removed dot class notation
    * Removed custom types
* Broke the project into smaller top-level namespaces to be more component friendly
* Removed support for XML configs and descriptions. Use arrays or JSON files.
* Removed the Validation component and Inspector
* Moved all cookie code to Guzzle\Plugin\Cookie
* Magic methods on a Guzzle\Service\Client now return the command un-executed.
* Calling getResult() or getResponse() on a command will lazily execute the command if needed.
* Now shipping with cURL's CA certs and using it by default
* Added previousResponse() method to response objects
* No longer sending Accept and Accept-Encoding headers on every request
* Only sending an Expect header by default when a payload is greater than 1MB
* Added/moved client options:
    * curl.blacklist to curl.option.blacklist
    * Added ssl.certificate_authority
* Added a Guzzle\Iterator component
* Moved plugins from Guzzle\Http\Plugin to Guzzle\Plugin
* Added a more robust backoff retry strategy (replaced the ExponentialBackoffPlugin)
* Added a more robust caching plugin
* Added setBody to response objects
* Updating LogPlugin to use a more flexible MessageFormatter
* Added a completely revamped build process
* Cleaning up Collection class and removing default values from the get method
* Fixed ZF2 cache adapters

## 2.8.8 - 2012-10-15

* Bug: Fixed a cookie issue that caused dot prefixed domains to not match where popular browsers did

## 2.8.7 - 2012-09-30

* Bug: Fixed config file aliases for JSON includes
* Bug: Fixed cookie bug on a request object by using CookieParser to parse cookies on requests
* Bug: Removing the path to a file when sending a Content-Disposition header on a POST upload
* Bug: Hardening request and response parsing to account for missing parts
* Bug: Fixed PEAR packaging
* Bug: Fixed Request::getInfo
* Bug: Fixed cases where CURLM_CALL_MULTI_PERFORM return codes were causing curl transactions to fail
* Adding the ability for the namespace Iterator factory to look in multiple directories
* Added more getters/setters/removers from service descriptions
* Added the ability to remove POST fields from OAuth signatures
* OAuth plugin now supports 2-legged OAuth

## 2.8.6 - 2012-09-05

* Added the ability to modify and build service descriptions
* Added the use of visitors to apply parameters to locations in service descriptions using the dynamic command
* Added a `json` parameter location
* Now allowing dot notation for classes in the CacheAdapterFactory
* Using the union of two arrays rather than an array_merge when extending service builder services and service params
* Ensuring that a service is a string before doing strpos() checks on it when substituting services for references
  in service builder config files.
* Services defined in two different config files that include one another will by default replace the previously
  defined service, but you can now create services that extend themselves and merge their settings over the previous
* The JsonLoader now supports aliasing filenames with different filenames. This allows you to alias something like
  '_default' with a default JSON configuration file.

## 2.8.5 - 2012-08-29

* Bug: Suppressed empty arrays from URI templates
* Bug: Added the missing $options argument from ServiceDescription::factory to enable caching
* Added support for HTTP responses that do not contain a reason phrase in the start-line
* AbstractCommand commands are now invokable
* Added a way to get the data used when signing an Oauth request before a request is sent

## 2.8.4 - 2012-08-15

* Bug: Custom delay time calculations are no longer ignored in the ExponentialBackoffPlugin
* Added the ability to transfer entity bodies as a string rather than streamed. This gets around curl error 65. Set `body_as_string` in a request's curl options to enable.
* Added a StreamInterface, EntityBodyInterface, and added ftell() to Guzzle\Common\Stream
* Added an AbstractEntityBodyDecorator and a ReadLimitEntityBody decorator to transfer only a subset of a decorated stream
* Stream and EntityBody objects will now return the file position to the previous position after a read required operation (e.g. getContentMd5())
* Added additional response status codes
* Removed SSL information from the default User-Agent header
* DELETE requests can now send an entity body
* Added an EventDispatcher to the ExponentialBackoffPlugin and added an ExponentialBackoffLogger to log backoff retries
* Added the ability of the MockPlugin to consume mocked request bodies
* LogPlugin now exposes request and response objects in the extras array

## 2.8.3 - 2012-07-30

* Bug: Fixed a case where empty POST requests were sent as GET requests
* Bug: Fixed a bug in ExponentialBackoffPlugin that caused fatal errors when retrying an EntityEnclosingRequest that does not have a body
* Bug: Setting the response body of a request to null after completing a request, not when setting the state of a request to new
* Added multiple inheritance to service description commands
* Added an ApiCommandInterface and added ``getParamNames()`` and ``hasParam()``
* Removed the default 2mb size cutoff from the Md5ValidatorPlugin so that it now defaults to validating everything
* Changed CurlMulti::perform to pass a smaller timeout to CurlMulti::executeHandles

## 2.8.2 - 2012-07-24

* Bug: Query string values set to 0 are no longer dropped from the query string
* Bug: A Collection object is no longer created each time a call is made to ``Guzzle\Service\Command\AbstractCommand::getRequestHeaders()``
* Bug: ``+`` is now treated as an encoded space when parsing query strings
* QueryString and Collection performance improvements
* Allowing dot notation for class paths in filters attribute of a service descriptions

## 2.8.1 - 2012-07-16

* Loosening Event Dispatcher dependency
* POST redirects can now be customized using CURLOPT_POSTREDIR

## 2.8.0 - 2012-07-15

* BC: Guzzle\Http\Query
    * Query strings with empty variables will always show an equal sign unless the variable is set to QueryString::BLANK (e.g. ?acl= vs ?acl)
    * Changed isEncodingValues() and isEncodingFields() to isUrlEncoding()
    * Changed setEncodeValues(bool) and setEncodeFields(bool) to useUrlEncoding(bool)
    * Changed the aggregation functions of QueryString to be static methods
    * Can now use fromString() with querystrings that have a leading ?
* cURL configuration values can be specified in service descriptions using ``curl.`` prefixed parameters
* Content-Length is set to 0 before emitting the request.before_send event when sending an empty request body
* Cookies are no longer URL decoded by default
* Bug: URI template variables set to null are no longer expanded

## 2.7.2 - 2012-07-02

* BC: Moving things to get ready for subtree splits. Moving Inflection into Common. Moving Guzzle\Http\Parser to Guzzle\Parser.
* BC: Removing Guzzle\Common\Batch\Batch::count() and replacing it with isEmpty()
* CachePlugin now allows for a custom request parameter function to check if a request can be cached
* Bug fix: CachePlugin now only caches GET and HEAD requests by default
* Bug fix: Using header glue when transferring headers over the wire
* Allowing deeply nested arrays for composite variables in URI templates
* Batch divisors can now return iterators or arrays

## 2.7.1 - 2012-06-26

* Minor patch to update version number in UA string
* Updating build process

## 2.7.0 - 2012-06-25

* BC: Inflection classes moved to Guzzle\Inflection. No longer static methods. Can now inject custom inflectors into classes.
* BC: Removed magic setX methods from commands
* BC: Magic methods mapped to service description commands are now inflected in the command factory rather than the client __call() method
* Verbose cURL options are no longer enabled by default. Set curl.debug to true on a client to enable.
* Bug: Now allowing colons in a response start-line (e.g. HTTP/1.1 503 Service Unavailable: Back-end server is at capacity)
* Guzzle\Service\Resource\ResourceIteratorApplyBatched now internally uses the Guzzle\Common\Batch namespace
* Added Guzzle\Service\Plugin namespace and a PluginCollectionPlugin
* Added the ability to set POST fields and files in a service description
* Guzzle\Http\EntityBody::factory() now accepts objects with a __toString() method
* Adding a command.before_prepare event to clients
* Added BatchClosureTransfer and BatchClosureDivisor
* BatchTransferException now includes references to the batch divisor and transfer strategies
* Fixed some tests so that they pass more reliably
* Added Guzzle\Common\Log\ArrayLogAdapter

## 2.6.6 - 2012-06-10

* BC: Removing Guzzle\Http\Plugin\BatchQueuePlugin
* BC: Removing Guzzle\Service\Command\CommandSet
* Adding generic batching system (replaces the batch queue plugin and command set)
* Updating ZF cache and log adapters and now using ZF's composer repository
* Bug: Setting the name of each ApiParam when creating through an ApiCommand
* Adding result_type, result_doc, deprecated, and doc_url to service descriptions
* Bug: Changed the default cookie header casing back to 'Cookie'

## 2.6.5 - 2012-06-03

* BC: Renaming Guzzle\Http\Message\RequestInterface::getResourceUri() to getResource()
* BC: Removing unused AUTH_BASIC and AUTH_DIGEST constants from
* BC: Guzzle\Http\Cookie is now used to manage Set-Cookie data, not Cookie data
* BC: Renaming methods in the CookieJarInterface
* Moving almost all cookie logic out of the CookiePlugin and into the Cookie or CookieJar implementations
* Making the default glue for HTTP headers ';' instead of ','
* Adding a removeValue to Guzzle\Http\Message\Header
* Adding getCookies() to request interface.
* Making it easier to add event subscribers to HasDispatcherInterface classes. Can now directly call addSubscriber()

## 2.6.4 - 2012-05-30

* BC: Cleaning up how POST files are stored in EntityEnclosingRequest objects. Adding PostFile class.
* BC: Moving ApiCommand specific functionality from the Inspector and on to the ApiCommand
* Bug: Fixing magic method command calls on clients
* Bug: Email constraint only validates strings
* Bug: Aggregate POST fields when POST files are present in curl handle
* Bug: Fixing default User-Agent header
* Bug: Only appending or prepending parameters in commands if they are specified
* Bug: Not requiring response reason phrases or status codes to match a predefined list of codes
* Allowing the use of dot notation for class namespaces when using instance_of constraint
* Added any_match validation constraint
* Added an AsyncPlugin
* Passing request object to the calculateWait method of the ExponentialBackoffPlugin
* Allowing the result of a command object to be changed
* Parsing location and type sub values when instantiating a service description rather than over and over at runtime

## 2.6.3 - 2012-05-23

* [BC] Guzzle\Common\FromConfigInterface no longer requires any config options.
* [BC] Refactoring how POST files are stored on an EntityEnclosingRequest. They are now separate from POST fields.
* You can now use an array of data when creating PUT request bodies in the request factory.
* Removing the requirement that HTTPS requests needed a Cache-Control: public directive to be cacheable.
* [Http] Adding support for Content-Type in multipart POST uploads per upload
* [Http] Added support for uploading multiple files using the same name (foo[0], foo[1])
* Adding more POST data operations for easier manipulation of POST data.
* You can now set empty POST fields.
* The body of a request is only shown on EntityEnclosingRequest objects that do not use POST files.
* Split the Guzzle\Service\Inspector::validateConfig method into two methods. One to initialize when a command is created, and one to validate.
* CS updates

## 2.6.2 - 2012-05-19

* [Http] Better handling of nested scope requests in CurlMulti.  Requests are now always prepares in the send() method rather than the addRequest() method.

## 2.6.1 - 2012-05-19

* [BC] Removing 'path' support in service descriptions.  Use 'uri'.
* [BC] Guzzle\Service\Inspector::parseDocBlock is now protected. Adding getApiParamsForClass() with cache.
* [BC] Removing Guzzle\Common\NullObject.  Use https://github.com/mtdowling/NullObject if you need it.
* [BC] Removing Guzzle\Common\XmlElement.
* All commands, both dynamic and concrete, have ApiCommand objects.
* Adding a fix for CurlMulti so that if all of the connections encounter some sort of curl error, then the loop exits.
* Adding checks to EntityEnclosingRequest so that empty POST files and fields are ignored.
* Making the method signature of Guzzle\Service\Builder\ServiceBuilder::factory more flexible.

## 2.6.0 - 2012-05-15

* [BC] Moving Guzzle\Service\Builder to Guzzle\Service\Builder\ServiceBuilder
* [BC] Executing a Command returns the result of the command rather than the command
* [BC] Moving all HTTP parsing logic to Guzzle\Http\Parsers. Allows for faster C implementations if needed.
* [BC] Changing the Guzzle\Http\Message\Response::setProtocol() method to accept a protocol and version in separate args.
* [BC] Moving ResourceIterator* to Guzzle\Service\Resource
* [BC] Completely refactored ResourceIterators to iterate over a cloned command object
* [BC] Moved Guzzle\Http\UriTemplate to Guzzle\Http\Parser\UriTemplate\UriTemplate
* [BC] Guzzle\Guzzle is now deprecated
* Moving Guzzle\Common\Guzzle::inject to Guzzle\Common\Collection::inject
* Adding Guzzle\Version class to give version information about Guzzle
* Adding Guzzle\Http\Utils class to provide getDefaultUserAgent() and getHttpDate()
* Adding Guzzle\Curl\CurlVersion to manage caching curl_version() data
* ServiceDescription and ServiceBuilder are now cacheable using similar configs
* Changing the format of XML and JSON service builder configs.  Backwards compatible.
* Cleaned up Cookie parsing
* Trimming the default Guzzle User-Agent header
* Adding a setOnComplete() method to Commands that is called when a command completes
* Keeping track of requests that were mocked in the MockPlugin
* Fixed a caching bug in the CacheAdapterFactory
* Inspector objects can be injected into a Command object
* Refactoring a lot of code and tests to be case insensitive when dealing with headers
* Adding Guzzle\Http\Message\HeaderComparison for easy comparison of HTTP headers using a DSL
* Adding the ability to set global option overrides to service builder configs
* Adding the ability to include other service builder config files from within XML and JSON files
* Moving the parseQuery method out of Url and on to QueryString::fromString() as a static factory method.

## 2.5.0 - 2012-05-08

* Major performance improvements
* [BC] Simplifying Guzzle\Common\Collection.  Please check to see if you are using features that are now deprecated.
* [BC] Using a custom validation system that allows a flyweight implementation for much faster validation. No longer using Symfony2 Validation component.
* [BC] No longer supporting "{{ }}" for injecting into command or UriTemplates.  Use "{}"
* Added the ability to passed parameters to all requests created by a client
* Added callback functionality to the ExponentialBackoffPlugin
* Using microtime in ExponentialBackoffPlugin to allow more granular backoff strategies.
* Rewinding request stream bodies when retrying requests
* Exception is thrown when JSON response body cannot be decoded
* Added configurable magic method calls to clients and commands.  This is off by default.
* Fixed a defect that added a hash to every parsed URL part
* Fixed duplicate none generation for OauthPlugin.
* Emitting an event each time a client is generated by a ServiceBuilder
* Using an ApiParams object instead of a Collection for parameters of an ApiCommand
* cache.* request parameters should be renamed to params.cache.*
* Added the ability to set arbitrary curl options on requests (disable_wire, progress, etc). See CurlHandle.
* Added the ability to disable type validation of service descriptions
* ServiceDescriptions and ServiceBuilders are now Serializable
vendor/guzzle/guzzle/LICENSE000064400000002127151327705700011717 0ustar00Copyright (c) 2011 Michael Dowling, https://github.com/mtdowling <mtdowling@gmail.com>

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
vendor/monolog/monolog/LICENSE000064400000002047151327705700012204 0ustar00Copyright (c) 2011-2016 Jordi Boggiano

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is furnished
to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
vendor/monolog/monolog/src/Monolog/Registry.php000064400000007677151327705700015737 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog;

use InvalidArgumentException;

/**
 * Monolog log registry
 *
 * Allows to get `Logger` instances in the global scope
 * via static method calls on this class.
 *
 * <code>
 * $application = new Monolog\Logger('application');
 * $api = new Monolog\Logger('api');
 *
 * Monolog\Registry::addLogger($application);
 * Monolog\Registry::addLogger($api);
 *
 * function testLogger()
 * {
 *     Monolog\Registry::api()->addError('Sent to $api Logger instance');
 *     Monolog\Registry::application()->addError('Sent to $application Logger instance');
 * }
 * </code>
 *
 * @author Tomas Tatarko <tomas@tatarko.sk>
 */
class Registry
{
    /**
     * List of all loggers in the registry (by named indexes)
     *
     * @var Logger[]
     */
    private static $loggers = array();

    /**
     * Adds new logging channel to the registry
     *
     * @param  Logger                    $logger    Instance of the logging channel
     * @param  string|null               $name      Name of the logging channel ($logger->getName() by default)
     * @param  bool                      $overwrite Overwrite instance in the registry if the given name already exists?
     * @throws \InvalidArgumentException If $overwrite set to false and named Logger instance already exists
     */
    public static function addLogger(Logger $logger, $name = null, $overwrite = false)
    {
        $name = $name ?: $logger->getName();

        if (isset(self::$loggers[$name]) && !$overwrite) {
            throw new InvalidArgumentException('Logger with the given name already exists');
        }

        self::$loggers[$name] = $logger;
    }

    /**
     * Checks if such logging channel exists by name or instance
     *
     * @param string|Logger $logger Name or logger instance
     */
    public static function hasLogger($logger)
    {
        if ($logger instanceof Logger) {
            $index = array_search($logger, self::$loggers, true);

            return false !== $index;
        } else {
            return isset(self::$loggers[$logger]);
        }
    }

    /**
     * Removes instance from registry by name or instance
     *
     * @param string|Logger $logger Name or logger instance
     */
    public static function removeLogger($logger)
    {
        if ($logger instanceof Logger) {
            if (false !== ($idx = array_search($logger, self::$loggers, true))) {
                unset(self::$loggers[$idx]);
            }
        } else {
            unset(self::$loggers[$logger]);
        }
    }

    /**
     * Clears the registry
     */
    public static function clear()
    {
        self::$loggers = array();
    }

    /**
     * Gets Logger instance from the registry
     *
     * @param  string                    $name Name of the requested Logger instance
     * @throws \InvalidArgumentException If named Logger instance is not in the registry
     * @return Logger                    Requested instance of Logger
     */
    public static function getInstance($name)
    {
        if (!isset(self::$loggers[$name])) {
            throw new InvalidArgumentException(sprintf('Requested "%s" logger instance is not in the registry', $name));
        }

        return self::$loggers[$name];
    }

    /**
     * Gets Logger instance from the registry via static method call
     *
     * @param  string                    $name      Name of the requested Logger instance
     * @param  array                     $arguments Arguments passed to static method call
     * @throws \InvalidArgumentException If named Logger instance is not in the registry
     * @return Logger                    Requested instance of Logger
     */
    public static function __callStatic($name, $arguments)
    {
        return self::getInstance($name);
    }
}
vendor/monolog/monolog/src/Monolog/Utils.php000064400000001030151327705700015200 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog;

class Utils
{
    /**
     * @internal
     */
    public static function getClass($object)
    {
        $class = \get_class($object);

        return 'c' === $class[0] && 0 === strpos($class, "class@anonymous\0") ? get_parent_class($class).'@anonymous' : $class;
    }
}
vendor/monolog/monolog/src/Monolog/Processor/MemoryProcessor.php000064400000003460151327705700021240 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog\Processor;

/**
 * Some methods that are common for all memory processors
 *
 * @author Rob Jensen
 */
abstract class MemoryProcessor implements ProcessorInterface
{
    /**
     * @var bool If true, get the real size of memory allocated from system. Else, only the memory used by emalloc() is reported.
     */
    protected $realUsage;

    /**
     * @var bool If true, then format memory size to human readable string (MB, KB, B depending on size)
     */
    protected $useFormatting;

    /**
     * @param bool $realUsage     Set this to true to get the real size of memory allocated from system.
     * @param bool $useFormatting If true, then format memory size to human readable string (MB, KB, B depending on size)
     */
    public function __construct($realUsage = true, $useFormatting = true)
    {
        $this->realUsage = (bool) $realUsage;
        $this->useFormatting = (bool) $useFormatting;
    }

    /**
     * Formats bytes into a human readable string if $this->useFormatting is true, otherwise return $bytes as is
     *
     * @param  int        $bytes
     * @return string|int Formatted string if $this->useFormatting is true, otherwise return $bytes as is
     */
    protected function formatBytes($bytes)
    {
        $bytes = (int) $bytes;

        if (!$this->useFormatting) {
            return $bytes;
        }

        if ($bytes > 1024 * 1024) {
            return round($bytes / 1024 / 1024, 2).' MB';
        } elseif ($bytes > 1024) {
            return round($bytes / 1024, 2).' KB';
        }

        return $bytes . ' B';
    }
}
vendor/monolog/monolog/src/Monolog/Processor/WebProcessor.php000064400000006261151327705700020507 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog\Processor;

/**
 * Injects url/method and remote IP of the current web request in all records
 *
 * @author Jordi Boggiano <j.boggiano@seld.be>
 */
class WebProcessor implements ProcessorInterface
{
    /**
     * @var array|\ArrayAccess
     */
    protected $serverData;

    /**
     * Default fields
     *
     * Array is structured as [key in record.extra => key in $serverData]
     *
     * @var array
     */
    protected $extraFields = array(
        'url'         => 'REQUEST_URI',
        'ip'          => 'REMOTE_ADDR',
        'http_method' => 'REQUEST_METHOD',
        'server'      => 'SERVER_NAME',
        'referrer'    => 'HTTP_REFERER',
    );

    /**
     * @param array|\ArrayAccess $serverData  Array or object w/ ArrayAccess that provides access to the $_SERVER data
     * @param array|null         $extraFields Field names and the related key inside $serverData to be added. If not provided it defaults to: url, ip, http_method, server, referrer
     */
    public function __construct($serverData = null, array $extraFields = null)
    {
        if (null === $serverData) {
            $this->serverData = &$_SERVER;
        } elseif (is_array($serverData) || $serverData instanceof \ArrayAccess) {
            $this->serverData = $serverData;
        } else {
            throw new \UnexpectedValueException('$serverData must be an array or object implementing ArrayAccess.');
        }

        if (null !== $extraFields) {
            if (isset($extraFields[0])) {
                foreach (array_keys($this->extraFields) as $fieldName) {
                    if (!in_array($fieldName, $extraFields)) {
                        unset($this->extraFields[$fieldName]);
                    }
                }
            } else {
                $this->extraFields = $extraFields;
            }
        }
    }

    /**
     * @param  array $record
     * @return array
     */
    public function __invoke(array $record)
    {
        // skip processing if for some reason request data
        // is not present (CLI or wonky SAPIs)
        if (!isset($this->serverData['REQUEST_URI'])) {
            return $record;
        }

        $record['extra'] = $this->appendExtraFields($record['extra']);

        return $record;
    }

    /**
     * @param  string $extraName
     * @param  string $serverName
     * @return $this
     */
    public function addExtraField($extraName, $serverName)
    {
        $this->extraFields[$extraName] = $serverName;

        return $this;
    }

    /**
     * @param  array $extra
     * @return array
     */
    private function appendExtraFields(array $extra)
    {
        foreach ($this->extraFields as $extraName => $serverName) {
            $extra[$extraName] = isset($this->serverData[$serverName]) ? $this->serverData[$serverName] : null;
        }

        if (isset($this->serverData['UNIQUE_ID'])) {
            $extra['unique_id'] = $this->serverData['UNIQUE_ID'];
        }

        return $extra;
    }
}
vendor/monolog/monolog/src/Monolog/Processor/ProcessorInterface.php000064400000001020151327705700021656 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog\Processor;

/**
 * An optional interface to allow labelling Monolog processors.
 *
 * @author Nicolas Grekas <p@tchwork.com>
 */
interface ProcessorInterface
{
    /**
     * @return array The processed records
     */
    public function __invoke(array $records);
}
vendor/monolog/monolog/src/Monolog/Processor/MemoryUsageProcessor.php000064400000001412151327705700022220 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog\Processor;

/**
 * Injects memory_get_usage in all records
 *
 * @see Monolog\Processor\MemoryProcessor::__construct() for options
 * @author Rob Jensen
 */
class MemoryUsageProcessor extends MemoryProcessor
{
    /**
     * @param  array $record
     * @return array
     */
    public function __invoke(array $record)
    {
        $bytes = memory_get_usage($this->realUsage);
        $formatted = $this->formatBytes($bytes);

        $record['extra']['memory_usage'] = $formatted;

        return $record;
    }
}
vendor/monolog/monolog/src/Monolog/Processor/IntrospectionProcessor.php000064400000007002151327705700022624 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog\Processor;

use WPvividMonolog\Logger;

/**
 * Injects line/file:class/function where the log message came from
 *
 * Warning: This only works if the handler processes the logs directly.
 * If you put the processor on a handler that is behind a FingersCrossedHandler
 * for example, the processor will only be called once the trigger level is reached,
 * and all the log records will have the same file/line/.. data from the call that
 * triggered the FingersCrossedHandler.
 *
 * @author Jordi Boggiano <j.boggiano@seld.be>
 */
class IntrospectionProcessor implements ProcessorInterface
{
    private $level;

    private $skipClassesPartials;

    private $skipStackFramesCount;

    private $skipFunctions = array(
        'call_user_func',
        'call_user_func_array',
    );

    public function __construct($level = Logger::DEBUG, array $skipClassesPartials = array(), $skipStackFramesCount = 0)
    {
        $this->level = Logger::toMonologLevel($level);
        $this->skipClassesPartials = array_merge(array('WPvividMonolog\\'), $skipClassesPartials);
        $this->skipStackFramesCount = $skipStackFramesCount;
    }

    /**
     * @param  array $record
     * @return array
     */
    public function __invoke(array $record)
    {
        // return if the level is not high enough
        if ($record['level'] < $this->level) {
            return $record;
        }

        /*
        * http://php.net/manual/en/function.debug-backtrace.php
        * As of 5.3.6, DEBUG_BACKTRACE_IGNORE_ARGS option was added.
        * Any version less than 5.3.6 must use the DEBUG_BACKTRACE_IGNORE_ARGS constant value '2'.
        */
        $trace = debug_backtrace((PHP_VERSION_ID < 50306) ? 2 : DEBUG_BACKTRACE_IGNORE_ARGS);

        // skip first since it's always the current method
        array_shift($trace);
        // the call_user_func call is also skipped
        array_shift($trace);

        $i = 0;

        while ($this->isTraceClassOrSkippedFunction($trace, $i)) {
            if (isset($trace[$i]['class'])) {
                foreach ($this->skipClassesPartials as $part) {
                    if (strpos($trace[$i]['class'], $part) !== false) {
                        $i++;
                        continue 2;
                    }
                }
            } elseif (in_array($trace[$i]['function'], $this->skipFunctions)) {
                $i++;
                continue;
            }

            break;
        }

        $i += $this->skipStackFramesCount;

        // we should have the call source now
        $record['extra'] = array_merge(
            $record['extra'],
            array(
                'file'      => isset($trace[$i - 1]['file']) ? $trace[$i - 1]['file'] : null,
                'line'      => isset($trace[$i - 1]['line']) ? $trace[$i - 1]['line'] : null,
                'class'     => isset($trace[$i]['class']) ? $trace[$i]['class'] : null,
                'function'  => isset($trace[$i]['function']) ? $trace[$i]['function'] : null,
            )
        );

        return $record;
    }

    private function isTraceClassOrSkippedFunction(array $trace, $index)
    {
        if (!isset($trace[$index])) {
            return false;
        }

        return isset($trace[$index]['class']) || in_array($trace[$index]['function'], $this->skipFunctions);
    }
}
vendor/monolog/monolog/src/Monolog/Processor/TagProcessor.php000064400000001531151327705700020500 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog\Processor;

/**
 * Adds a tags array into record
 *
 * @author Martijn Riemers
 */
class TagProcessor implements ProcessorInterface
{
    private $tags;

    public function __construct(array $tags = array())
    {
        $this->setTags($tags);
    }

    public function addTags(array $tags = array())
    {
        $this->tags = array_merge($this->tags, $tags);
    }

    public function setTags(array $tags = array())
    {
        $this->tags = $tags;
    }

    public function __invoke(array $record)
    {
        $record['extra']['tags'] = $this->tags;

        return $record;
    }
}
vendor/monolog/monolog/src/Monolog/Processor/GitProcessor.php000064400000002652151327705700020515 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog\Processor;

use WPvividMonolog\Logger;

/**
 * Injects Git branch and Git commit SHA in all records
 *
 * @author Nick Otter
 * @author Jordi Boggiano <j.boggiano@seld.be>
 */
class GitProcessor implements ProcessorInterface
{
    private $level;
    private static $cache;

    public function __construct($level = Logger::DEBUG)
    {
        $this->level = Logger::toMonologLevel($level);
    }

    /**
     * @param  array $record
     * @return array
     */
    public function __invoke(array $record)
    {
        // return if the level is not high enough
        if ($record['level'] < $this->level) {
            return $record;
        }

        $record['extra']['git'] = self::getGitInfo();

        return $record;
    }

    private static function getGitInfo()
    {
        if (self::$cache) {
            return self::$cache;
        }

        $branches = `git branch -v --no-abbrev`;
        if (preg_match('{^\* (.+?)\s+([a-f0-9]{40})(?:\s|$)}m', $branches, $matches)) {
            return self::$cache = array(
                'branch' => $matches[1],
                'commit' => $matches[2],
            );
        }

        return self::$cache = array();
    }
}
vendor/monolog/monolog/src/Monolog/Processor/ProcessIdProcessor.php000064400000001143151327705700021657 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog\Processor;

/**
 * Adds value of getmypid into records
 *
 * @author Andreas Hörnicke
 */
class ProcessIdProcessor implements ProcessorInterface
{
    /**
     * @param  array $record
     * @return array
     */
    public function __invoke(array $record)
    {
        $record['extra']['process_id'] = getmypid();

        return $record;
    }
}
vendor/monolog/monolog/src/Monolog/Processor/MercurialProcessor.php000064400000002621151327705700021711 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jonathan A. Schweder <jonathanschweder@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog\Processor;

use WPvividMonolog\Logger;

/**
 * Injects Hg branch and Hg revision number in all records
 *
 * @author Jonathan A. Schweder <jonathanschweder@gmail.com>
 */
class MercurialProcessor implements ProcessorInterface
{
    private $level;
    private static $cache;

    public function __construct($level = Logger::DEBUG)
    {
        $this->level = Logger::toMonologLevel($level);
    }

    /**
     * @param  array $record
     * @return array
     */
    public function __invoke(array $record)
    {
        // return if the level is not high enough
        if ($record['level'] < $this->level) {
            return $record;
        }

        $record['extra']['hg'] = self::getMercurialInfo();

        return $record;
    }

    private static function getMercurialInfo()
    {
        if (self::$cache) {
            return self::$cache;
        }

        $result = explode(' ', trim(`hg id -nb`));
        if (count($result) >= 3) {
            return self::$cache = array(
                'branch' => $result[1],
                'revision' => $result[2],
            );
        }

        return self::$cache = array();
    }
}
vendor/monolog/monolog/src/Monolog/Processor/MemoryPeakUsageProcessor.php000064400000001435151327705700023026 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog\Processor;

/**
 * Injects memory_get_peak_usage in all records
 *
 * @see Monolog\Processor\MemoryProcessor::__construct() for options
 * @author Rob Jensen
 */
class MemoryPeakUsageProcessor extends MemoryProcessor
{
    /**
     * @param  array $record
     * @return array
     */
    public function __invoke(array $record)
    {
        $bytes = memory_get_peak_usage($this->realUsage);
        $formatted = $this->formatBytes($bytes);

        $record['extra']['memory_peak_usage'] = $formatted;

        return $record;
    }
}
vendor/monolog/monolog/src/Monolog/Processor/PsrLogMessageProcessor.php000064400000002476151327705700022511 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog\Processor;

use WPvividMonolog\Utils;

/**
 * Processes a record's message according to PSR-3 rules
 *
 * It replaces {foo} with the value from $context['foo']
 *
 * @author Jordi Boggiano <j.boggiano@seld.be>
 */
class PsrLogMessageProcessor implements ProcessorInterface
{
    /**
     * @param  array $record
     * @return array
     */
    public function __invoke(array $record)
    {
        if (false === strpos($record['message'], '{')) {
            return $record;
        }

        $replacements = array();
        foreach ($record['context'] as $key => $val) {
            if (is_null($val) || is_scalar($val) || (is_object($val) && method_exists($val, "__toString"))) {
                $replacements['{'.$key.'}'] = $val;
            } elseif (is_object($val)) {
                $replacements['{'.$key.'}'] = '[object '.Utils::getClass($val).']';
            } else {
                $replacements['{'.$key.'}'] = '['.gettype($val).']';
            }
        }

        $record['message'] = strtr($record['message'], $replacements);

        return $record;
    }
}
vendor/monolog/monolog/src/Monolog/Processor/UidProcessor.php000064400000002335151327705700020511 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog\Processor;

use WPvividMonolog\ResettableInterface;

/**
 * Adds a unique identifier into records
 *
 * @author Simon Mönch <sm@webfactory.de>
 */
class UidProcessor implements ProcessorInterface, ResettableInterface
{
    private $uid;

    public function __construct($length = 7)
    {
        if (!is_int($length) || $length > 32 || $length < 1) {
            throw new \InvalidArgumentException('The uid length must be an integer between 1 and 32');
        }


        $this->uid = $this->generateUid($length);
    }

    public function __invoke(array $record)
    {
        $record['extra']['uid'] = $this->uid;

        return $record;
    }

    /**
     * @return string
     */
    public function getUid()
    {
        return $this->uid;
    }

    public function reset()
    {
        $this->uid = $this->generateUid(strlen($this->uid));
    }

    private function generateUid($length)
    {
        return substr(hash('md5', uniqid('', true)), 0, $length);
    }
}
vendor/monolog/monolog/src/Monolog/Logger.php000064400000053130151327705700015327 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog;

use WPvividMonolog\Handler\HandlerInterface;
use WPvividMonolog\Handler\StreamHandler;
use WPvividPsr\Log\LoggerInterface;
use WPvividPsr\Log\InvalidArgumentException;
use Exception;

/**
 * Monolog log channel
 *
 * It contains a stack of Handlers and a stack of Processors,
 * and uses them to store records that are added to it.
 *
 * @author Jordi Boggiano <j.boggiano@seld.be>
 */
class Logger implements LoggerInterface, ResettableInterface
{
    /**
     * Detailed debug information
     */
    const DEBUG = 100;

    /**
     * Interesting events
     *
     * Examples: User logs in, SQL logs.
     */
    const INFO = 200;

    /**
     * Uncommon events
     */
    const NOTICE = 250;

    /**
     * Exceptional occurrences that are not errors
     *
     * Examples: Use of deprecated APIs, poor use of an API,
     * undesirable things that are not necessarily wrong.
     */
    const WARNING = 300;

    /**
     * Runtime errors
     */
    const ERROR = 400;

    /**
     * Critical conditions
     *
     * Example: Application component unavailable, unexpected exception.
     */
    const CRITICAL = 500;

    /**
     * Action must be taken immediately
     *
     * Example: Entire website down, database unavailable, etc.
     * This should trigger the SMS alerts and wake you up.
     */
    const ALERT = 550;

    /**
     * Urgent alert.
     */
    const EMERGENCY = 600;

    /**
     * Monolog API version
     *
     * This is only bumped when API breaks are done and should
     * follow the major version of the library
     *
     * @var int
     */
    const API = 1;

    /**
     * Logging levels from syslog protocol defined in RFC 5424
     *
     * @var array $levels Logging levels
     */
    protected static $levels = array(
        self::DEBUG     => 'DEBUG',
        self::INFO      => 'INFO',
        self::NOTICE    => 'NOTICE',
        self::WARNING   => 'WARNING',
        self::ERROR     => 'ERROR',
        self::CRITICAL  => 'CRITICAL',
        self::ALERT     => 'ALERT',
        self::EMERGENCY => 'EMERGENCY',
    );

    /**
     * @var \DateTimeZone
     */
    protected static $timezone;

    /**
     * @var string
     */
    protected $name;

    /**
     * The handler stack
     *
     * @var HandlerInterface[]
     */
    protected $handlers;

    /**
     * Processors that will process all log records
     *
     * To process records of a single handler instead, add the processor on that specific handler
     *
     * @var callable[]
     */
    protected $processors;

    /**
     * @var bool
     */
    protected $microsecondTimestamps = true;

    /**
     * @var callable
     */
    protected $exceptionHandler;

    /**
     * @param string             $name       The logging channel
     * @param HandlerInterface[] $handlers   Optional stack of handlers, the first one in the array is called first, etc.
     * @param callable[]         $processors Optional array of processors
     */
    public function __construct($name, array $handlers = array(), array $processors = array())
    {
        $this->name = $name;
        $this->setHandlers($handlers);
        $this->processors = $processors;
    }

    /**
     * @return string
     */
    public function getName()
    {
        return $this->name;
    }

    /**
     * Return a new cloned instance with the name changed
     *
     * @return static
     */
    public function withName($name)
    {
        $new = clone $this;
        $new->name = $name;

        return $new;
    }

    /**
     * Pushes a handler on to the stack.
     *
     * @param  HandlerInterface $handler
     * @return $this
     */
    public function pushHandler(HandlerInterface $handler)
    {
        array_unshift($this->handlers, $handler);

        return $this;
    }

    /**
     * Pops a handler from the stack
     *
     * @return HandlerInterface
     */
    public function popHandler()
    {
        if (!$this->handlers) {
            throw new \LogicException('You tried to pop from an empty handler stack.');
        }

        return array_shift($this->handlers);
    }

    /**
     * Set handlers, replacing all existing ones.
     *
     * If a map is passed, keys will be ignored.
     *
     * @param  HandlerInterface[] $handlers
     * @return $this
     */
    public function setHandlers(array $handlers)
    {
        $this->handlers = array();
        foreach (array_reverse($handlers) as $handler) {
            $this->pushHandler($handler);
        }

        return $this;
    }

    /**
     * @return HandlerInterface[]
     */
    public function getHandlers()
    {
        return $this->handlers;
    }

    /**
     * Adds a processor on to the stack.
     *
     * @param  callable $callback
     * @return $this
     */
    public function pushProcessor($callback)
    {
        if (!is_callable($callback)) {
            throw new \InvalidArgumentException('Processors must be valid callables (callback or object with an __invoke method), '.var_export($callback, true).' given');
        }
        array_unshift($this->processors, $callback);

        return $this;
    }

    /**
     * Removes the processor on top of the stack and returns it.
     *
     * @return callable
     */
    public function popProcessor()
    {
        if (!$this->processors) {
            throw new \LogicException('You tried to pop from an empty processor stack.');
        }

        return array_shift($this->processors);
    }

    /**
     * @return callable[]
     */
    public function getProcessors()
    {
        return $this->processors;
    }

    /**
     * Control the use of microsecond resolution timestamps in the 'datetime'
     * member of new records.
     *
     * Generating microsecond resolution timestamps by calling
     * microtime(true), formatting the result via sprintf() and then parsing
     * the resulting string via \DateTime::createFromFormat() can incur
     * a measurable runtime overhead vs simple usage of DateTime to capture
     * a second resolution timestamp in systems which generate a large number
     * of log events.
     *
     * @param bool $micro True to use microtime() to create timestamps
     */
    public function useMicrosecondTimestamps($micro)
    {
        $this->microsecondTimestamps = (bool) $micro;
    }

    /**
     * Adds a log record.
     *
     * @param  int     $level   The logging level
     * @param  string  $message The log message
     * @param  array   $context The log context
     * @return bool Whether the record has been processed
     */
    public function addRecord($level, $message, array $context = array())
    {
        if (!$this->handlers) {
            $this->pushHandler(new StreamHandler('php://stderr', static::DEBUG));
        }

        $levelName = static::getLevelName($level);

        // check if any handler will handle this message so we can return early and save cycles
        $handlerKey = null;
        reset($this->handlers);
        while ($handler = current($this->handlers)) {
            if ($handler->isHandling(array('level' => $level))) {
                $handlerKey = key($this->handlers);
                break;
            }

            next($this->handlers);
        }

        if (null === $handlerKey) {
            return false;
        }

        if (!static::$timezone) {
            static::$timezone = new \DateTimeZone(date_default_timezone_get() ?: 'UTC');
        }

        // php7.1+ always has microseconds enabled, so we do not need this hack
        if ($this->microsecondTimestamps && PHP_VERSION_ID < 70100) {
            $ts = \DateTime::createFromFormat('U.u', sprintf('%.6F', microtime(true)), static::$timezone);
        } else {
            $ts = new \DateTime(null, static::$timezone);
        }
        $ts->setTimezone(static::$timezone);

        $record = array(
            'message' => (string) $message,
            'context' => $context,
            'level' => $level,
            'level_name' => $levelName,
            'channel' => $this->name,
            'datetime' => $ts,
            'extra' => array(),
        );

        try {
            foreach ($this->processors as $processor) {
                $record = call_user_func($processor, $record);
            }

            while ($handler = current($this->handlers)) {
                if (true === $handler->handle($record)) {
                    break;
                }

                next($this->handlers);
            }
        } catch (Exception $e) {
            $this->handleException($e, $record);
        }

        return true;
    }

    /**
     * Ends a log cycle and frees all resources used by handlers.
     *
     * Closing a Handler means flushing all buffers and freeing any open resources/handles.
     * Handlers that have been closed should be able to accept log records again and re-open
     * themselves on demand, but this may not always be possible depending on implementation.
     *
     * This is useful at the end of a request and will be called automatically on every handler
     * when they get destructed.
     */
    public function close()
    {
        foreach ($this->handlers as $handler) {
            if (method_exists($handler, 'close')) {
                $handler->close();
            }
        }
    }

    /**
     * Ends a log cycle and resets all handlers and processors to their initial state.
     *
     * Resetting a Handler or a Processor means flushing/cleaning all buffers, resetting internal
     * state, and getting it back to a state in which it can receive log records again.
     *
     * This is useful in case you want to avoid logs leaking between two requests or jobs when you
     * have a long running process like a worker or an application server serving multiple requests
     * in one process.
     */
    public function reset()
    {
        foreach ($this->handlers as $handler) {
            if ($handler instanceof ResettableInterface) {
                $handler->reset();
            }
        }

        foreach ($this->processors as $processor) {
            if ($processor instanceof ResettableInterface) {
                $processor->reset();
            }
        }
    }

    /**
     * Adds a log record at the DEBUG level.
     *
     * @param  string $message The log message
     * @param  array  $context The log context
     * @return bool   Whether the record has been processed
     */
    public function addDebug($message, array $context = array())
    {
        return $this->addRecord(static::DEBUG, $message, $context);
    }

    /**
     * Adds a log record at the INFO level.
     *
     * @param  string $message The log message
     * @param  array  $context The log context
     * @return bool   Whether the record has been processed
     */
    public function addInfo($message, array $context = array())
    {
        return $this->addRecord(static::INFO, $message, $context);
    }

    /**
     * Adds a log record at the NOTICE level.
     *
     * @param  string $message The log message
     * @param  array  $context The log context
     * @return bool   Whether the record has been processed
     */
    public function addNotice($message, array $context = array())
    {
        return $this->addRecord(static::NOTICE, $message, $context);
    }

    /**
     * Adds a log record at the WARNING level.
     *
     * @param  string $message The log message
     * @param  array  $context The log context
     * @return bool   Whether the record has been processed
     */
    public function addWarning($message, array $context = array())
    {
        return $this->addRecord(static::WARNING, $message, $context);
    }

    /**
     * Adds a log record at the ERROR level.
     *
     * @param  string $message The log message
     * @param  array  $context The log context
     * @return bool   Whether the record has been processed
     */
    public function addError($message, array $context = array())
    {
        return $this->addRecord(static::ERROR, $message, $context);
    }

    /**
     * Adds a log record at the CRITICAL level.
     *
     * @param  string $message The log message
     * @param  array  $context The log context
     * @return bool   Whether the record has been processed
     */
    public function addCritical($message, array $context = array())
    {
        return $this->addRecord(static::CRITICAL, $message, $context);
    }

    /**
     * Adds a log record at the ALERT level.
     *
     * @param  string $message The log message
     * @param  array  $context The log context
     * @return bool   Whether the record has been processed
     */
    public function addAlert($message, array $context = array())
    {
        return $this->addRecord(static::ALERT, $message, $context);
    }

    /**
     * Adds a log record at the EMERGENCY level.
     *
     * @param  string $message The log message
     * @param  array  $context The log context
     * @return bool   Whether the record has been processed
     */
    public function addEmergency($message, array $context = array())
    {
        return $this->addRecord(static::EMERGENCY, $message, $context);
    }

    /**
     * Gets all supported logging levels.
     *
     * @return array Assoc array with human-readable level names => level codes.
     */
    public static function getLevels()
    {
        return array_flip(static::$levels);
    }

    /**
     * Gets the name of the logging level.
     *
     * @param  int    $level
     * @return string
     */
    public static function getLevelName($level)
    {
        if (!isset(static::$levels[$level])) {
            throw new InvalidArgumentException('Level "'.$level.'" is not defined, use one of: '.implode(', ', array_keys(static::$levels)));
        }

        return static::$levels[$level];
    }

    /**
     * Converts PSR-3 levels to Monolog ones if necessary
     *
     * @param string|int Level number (monolog) or name (PSR-3)
     * @return int
     */
    public static function toMonologLevel($level)
    {
        if (is_string($level) && defined(__CLASS__.'::'.strtoupper($level))) {
            return constant(__CLASS__.'::'.strtoupper($level));
        }

        return $level;
    }

    /**
     * Checks whether the Logger has a handler that listens on the given level
     *
     * @param  int     $level
     * @return bool
     */
    public function isHandling($level)
    {
        $record = array(
            'level' => $level,
        );

        foreach ($this->handlers as $handler) {
            if ($handler->isHandling($record)) {
                return true;
            }
        }

        return false;
    }

    /**
     * Set a custom exception handler
     *
     * @param  callable $callback
     * @return $this
     */
    public function setExceptionHandler($callback)
    {
        if (!is_callable($callback)) {
            throw new \InvalidArgumentException('Exception handler must be valid callable (callback or object with an __invoke method), '.var_export($callback, true).' given');
        }
        $this->exceptionHandler = $callback;

        return $this;
    }

    /**
     * @return callable
     */
    public function getExceptionHandler()
    {
        return $this->exceptionHandler;
    }

    /**
     * Delegates exception management to the custom exception handler,
     * or throws the exception if no custom handler is set.
     */
    protected function handleException(Exception $e, array $record)
    {
        if (!$this->exceptionHandler) {
            throw $e;
        }

        call_user_func($this->exceptionHandler, $e, $record);
    }

    /**
     * Adds a log record at an arbitrary level.
     *
     * This method allows for compatibility with common interfaces.
     *
     * @param  mixed   $level   The log level
     * @param  string $message The log message
     * @param  array  $context The log context
     * @return bool   Whether the record has been processed
     */
    public function log($level, $message, array $context = array())
    {
        $level = static::toMonologLevel($level);

        return $this->addRecord($level, $message, $context);
    }

    /**
     * Adds a log record at the DEBUG level.
     *
     * This method allows for compatibility with common interfaces.
     *
     * @param  string $message The log message
     * @param  array  $context The log context
     * @return bool   Whether the record has been processed
     */
    public function debug($message, array $context = array())
    {
        return $this->addRecord(static::DEBUG, $message, $context);
    }

    /**
     * Adds a log record at the INFO level.
     *
     * This method allows for compatibility with common interfaces.
     *
     * @param  string $message The log message
     * @param  array  $context The log context
     * @return bool   Whether the record has been processed
     */
    public function info($message, array $context = array())
    {
        return $this->addRecord(static::INFO, $message, $context);
    }

    /**
     * Adds a log record at the NOTICE level.
     *
     * This method allows for compatibility with common interfaces.
     *
     * @param  string $message The log message
     * @param  array  $context The log context
     * @return bool   Whether the record has been processed
     */
    public function notice($message, array $context = array())
    {
        return $this->addRecord(static::NOTICE, $message, $context);
    }

    /**
     * Adds a log record at the WARNING level.
     *
     * This method allows for compatibility with common interfaces.
     *
     * @param  string $message The log message
     * @param  array  $context The log context
     * @return bool   Whether the record has been processed
     */
    public function warn($message, array $context = array())
    {
        return $this->addRecord(static::WARNING, $message, $context);
    }

    /**
     * Adds a log record at the WARNING level.
     *
     * This method allows for compatibility with common interfaces.
     *
     * @param  string $message The log message
     * @param  array  $context The log context
     * @return bool   Whether the record has been processed
     */
    public function warning($message, array $context = array())
    {
        return $this->addRecord(static::WARNING, $message, $context);
    }

    /**
     * Adds a log record at the ERROR level.
     *
     * This method allows for compatibility with common interfaces.
     *
     * @param  string $message The log message
     * @param  array  $context The log context
     * @return bool   Whether the record has been processed
     */
    public function err($message, array $context = array())
    {
        return $this->addRecord(static::ERROR, $message, $context);
    }

    /**
     * Adds a log record at the ERROR level.
     *
     * This method allows for compatibility with common interfaces.
     *
     * @param  string $message The log message
     * @param  array  $context The log context
     * @return bool   Whether the record has been processed
     */
    public function error($message, array $context = array())
    {
        return $this->addRecord(static::ERROR, $message, $context);
    }

    /**
     * Adds a log record at the CRITICAL level.
     *
     * This method allows for compatibility with common interfaces.
     *
     * @param  string $message The log message
     * @param  array  $context The log context
     * @return bool   Whether the record has been processed
     */
    public function crit($message, array $context = array())
    {
        return $this->addRecord(static::CRITICAL, $message, $context);
    }

    /**
     * Adds a log record at the CRITICAL level.
     *
     * This method allows for compatibility with common interfaces.
     *
     * @param  string $message The log message
     * @param  array  $context The log context
     * @return bool   Whether the record has been processed
     */
    public function critical($message, array $context = array())
    {
        return $this->addRecord(static::CRITICAL, $message, $context);
    }

    /**
     * Adds a log record at the ALERT level.
     *
     * This method allows for compatibility with common interfaces.
     *
     * @param  string $message The log message
     * @param  array  $context The log context
     * @return bool   Whether the record has been processed
     */
    public function alert($message, array $context = array())
    {
        return $this->addRecord(static::ALERT, $message, $context);
    }

    /**
     * Adds a log record at the EMERGENCY level.
     *
     * This method allows for compatibility with common interfaces.
     *
     * @param  string $message The log message
     * @param  array  $context The log context
     * @return bool   Whether the record has been processed
     */
    public function emerg($message, array $context = array())
    {
        return $this->addRecord(static::EMERGENCY, $message, $context);
    }

    /**
     * Adds a log record at the EMERGENCY level.
     *
     * This method allows for compatibility with common interfaces.
     *
     * @param  string $message The log message
     * @param  array  $context The log context
     * @return bool   Whether the record has been processed
     */
    public function emergency($message, array $context = array())
    {
        return $this->addRecord(static::EMERGENCY, $message, $context);
    }

    /**
     * Set the timezone to be used for the timestamp of log records.
     *
     * This is stored globally for all Logger instances
     *
     * @param \DateTimeZone $tz Timezone object
     */
    public static function setTimezone(\DateTimeZone $tz)
    {
        self::$timezone = $tz;
    }
}
vendor/monolog/monolog/src/Monolog/Handler/RollbarHandler.php000064400000007564151327705700020372 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog\Handler;

use RollbarNotifier;
use Exception;
use WPvividMonolog\Logger;

/**
 * Sends errors to Rollbar
 *
 * If the context data contains a `payload` key, that is used as an array
 * of payload options to RollbarNotifier's report_message/report_exception methods.
 *
 * Rollbar's context info will contain the context + extra keys from the log record
 * merged, and then on top of that a few keys:
 *
 *  - level (rollbar level name)
 *  - monolog_level (monolog level name, raw level, as rollbar only has 5 but monolog 8)
 *  - channel
 *  - datetime (unix timestamp)
 *
 * @author Paul Statezny <paulstatezny@gmail.com>
 */
class RollbarHandler extends AbstractProcessingHandler
{
    /**
     * Rollbar notifier
     *
     * @var RollbarNotifier
     */
    protected $rollbarNotifier;

    protected $levelMap = array(
        Logger::DEBUG     => 'debug',
        Logger::INFO      => 'info',
        Logger::NOTICE    => 'info',
        Logger::WARNING   => 'warning',
        Logger::ERROR     => 'error',
        Logger::CRITICAL  => 'critical',
        Logger::ALERT     => 'critical',
        Logger::EMERGENCY => 'critical',
    );

    /**
     * Records whether any log records have been added since the last flush of the rollbar notifier
     *
     * @var bool
     */
    private $hasRecords = false;

    protected $initialized = false;

    /**
     * @param RollbarNotifier $rollbarNotifier RollbarNotifier object constructed with valid token
     * @param int             $level           The minimum logging level at which this handler will be triggered
     * @param bool            $bubble          Whether the messages that are handled can bubble up the stack or not
     */
    public function __construct(RollbarNotifier $rollbarNotifier, $level = Logger::ERROR, $bubble = true)
    {
        $this->rollbarNotifier = $rollbarNotifier;

        parent::__construct($level, $bubble);
    }

    /**
     * {@inheritdoc}
     */
    protected function write(array $record)
    {
        if (!$this->initialized) {
            // __destructor() doesn't get called on Fatal errors
            register_shutdown_function(array($this, 'close'));
            $this->initialized = true;
        }

        $context = $record['context'];
        $payload = array();
        if (isset($context['payload'])) {
            $payload = $context['payload'];
            unset($context['payload']);
        }
        $context = array_merge($context, $record['extra'], array(
            'level' => $this->levelMap[$record['level']],
            'monolog_level' => $record['level_name'],
            'channel' => $record['channel'],
            'datetime' => $record['datetime']->format('U'),
        ));

        if (isset($context['exception']) && $context['exception'] instanceof Exception) {
            $payload['level'] = $context['level'];
            $exception = $context['exception'];
            unset($context['exception']);

            $this->rollbarNotifier->report_exception($exception, $context, $payload);
        } else {
            $this->rollbarNotifier->report_message(
                $record['message'],
                $context['level'],
                $context,
                $payload
            );
        }

        $this->hasRecords = true;
    }

    public function flush()
    {
        if ($this->hasRecords) {
            $this->rollbarNotifier->flush();
            $this->hasRecords = false;
        }
    }

    /**
     * {@inheritdoc}
     */
    public function close()
    {
        $this->flush();
    }

    /**
     * {@inheritdoc}
     */
    public function reset()
    {
        $this->flush();

        parent::reset();
    }


}
vendor/monolog/monolog/src/Monolog/Handler/SamplingHandler.php000064400000005171151327705700020537 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog\Handler;

/**
 * Sampling handler
 *
 * A sampled event stream can be useful for logging high frequency events in
 * a production environment where you only need an idea of what is happening
 * and are not concerned with capturing every occurrence. Since the decision to
 * handle or not handle a particular event is determined randomly, the
 * resulting sampled log is not guaranteed to contain 1/N of the events that
 * occurred in the application, but based on the Law of large numbers, it will
 * tend to be close to this ratio with a large number of attempts.
 *
 * @author Bryan Davis <bd808@wikimedia.org>
 * @author Kunal Mehta <legoktm@gmail.com>
 */
class SamplingHandler extends AbstractHandler
{
    /**
     * @var callable|HandlerInterface $handler
     */
    protected $handler;

    /**
     * @var int $factor
     */
    protected $factor;

    /**
     * @param callable|HandlerInterface $handler Handler or factory callable($record, $fingersCrossedHandler).
     * @param int                       $factor  Sample factor
     */
    public function __construct($handler, $factor)
    {
        parent::__construct();
        $this->handler = $handler;
        $this->factor = $factor;

        if (!$this->handler instanceof HandlerInterface && !is_callable($this->handler)) {
            throw new \RuntimeException("The given handler (".json_encode($this->handler).") is not a callable nor a Monolog\Handler\HandlerInterface object");
        }
    }

    public function isHandling(array $record)
    {
        return $this->handler->isHandling($record);
    }

    public function handle(array $record)
    {
        if ($this->isHandling($record) && mt_rand(1, $this->factor) === 1) {
            // The same logic as in FingersCrossedHandler
            if (!$this->handler instanceof HandlerInterface) {
                $this->handler = call_user_func($this->handler, $record, $this);
                if (!$this->handler instanceof HandlerInterface) {
                    throw new \RuntimeException("The factory callable should return a HandlerInterface");
                }
            }

            if ($this->processors) {
                foreach ($this->processors as $processor) {
                    $record = call_user_func($processor, $record);
                }
            }

            $this->handler->handle($record);
        }

        return false === $this->bubble;
    }
}
vendor/monolog/monolog/src/Monolog/Handler/NativeMailerHandler.php000064400000012153151327705700021343 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog\Handler;

use WPvividMonolog\Logger;
use WPvividMonolog\Formatter\LineFormatter;

/**
 * NativeMailerHandler uses the mail() function to send the emails
 *
 * @author Christophe Coevoet <stof@notk.org>
 * @author Mark Garrett <mark@moderndeveloperllc.com>
 */
class NativeMailerHandler extends MailHandler
{
    /**
     * The email addresses to which the message will be sent
     * @var array
     */
    protected $to;

    /**
     * The subject of the email
     * @var string
     */
    protected $subject;

    /**
     * Optional headers for the message
     * @var array
     */
    protected $headers = array();

    /**
     * Optional parameters for the message
     * @var array
     */
    protected $parameters = array();

    /**
     * The wordwrap length for the message
     * @var int
     */
    protected $maxColumnWidth;

    /**
     * The Content-type for the message
     * @var string
     */
    protected $contentType = 'text/plain';

    /**
     * The encoding for the message
     * @var string
     */
    protected $encoding = 'utf-8';

    /**
     * @param string|array $to             The receiver of the mail
     * @param string       $subject        The subject of the mail
     * @param string       $from           The sender of the mail
     * @param int          $level          The minimum logging level at which this handler will be triggered
     * @param bool         $bubble         Whether the messages that are handled can bubble up the stack or not
     * @param int          $maxColumnWidth The maximum column width that the message lines will have
     */
    public function __construct($to, $subject, $from, $level = Logger::ERROR, $bubble = true, $maxColumnWidth = 70)
    {
        parent::__construct($level, $bubble);
        $this->to = is_array($to) ? $to : array($to);
        $this->subject = $subject;
        $this->addHeader(sprintf('From: %s', $from));
        $this->maxColumnWidth = $maxColumnWidth;
    }

    /**
     * Add headers to the message
     *
     * @param  string|array $headers Custom added headers
     * @return self
     */
    public function addHeader($headers)
    {
        foreach ((array) $headers as $header) {
            if (strpos($header, "\n") !== false || strpos($header, "\r") !== false) {
                throw new \InvalidArgumentException('Headers can not contain newline characters for security reasons');
            }
            $this->headers[] = $header;
        }

        return $this;
    }

    /**
     * Add parameters to the message
     *
     * @param  string|array $parameters Custom added parameters
     * @return self
     */
    public function addParameter($parameters)
    {
        $this->parameters = array_merge($this->parameters, (array) $parameters);

        return $this;
    }

    /**
     * {@inheritdoc}
     */
    protected function send($content, array $records)
    {
        $content = wordwrap($content, $this->maxColumnWidth);
        $headers = ltrim(implode("\r\n", $this->headers) . "\r\n", "\r\n");
        $headers .= 'Content-type: ' . $this->getContentType() . '; charset=' . $this->getEncoding() . "\r\n";
        if ($this->getContentType() == 'text/html' && false === strpos($headers, 'MIME-Version:')) {
            $headers .= 'MIME-Version: 1.0' . "\r\n";
        }

        $subject = $this->subject;
        if ($records) {
            $subjectFormatter = new LineFormatter($this->subject);
            $subject = $subjectFormatter->format($this->getHighestRecord($records));
        }

        $parameters = implode(' ', $this->parameters);
        foreach ($this->to as $to) {
            mail($to, $subject, $content, $headers, $parameters);
        }
    }

    /**
     * @return string $contentType
     */
    public function getContentType()
    {
        return $this->contentType;
    }

    /**
     * @return string $encoding
     */
    public function getEncoding()
    {
        return $this->encoding;
    }

    /**
     * @param  string $contentType The content type of the email - Defaults to text/plain. Use text/html for HTML
     *                             messages.
     * @return self
     */
    public function setContentType($contentType)
    {
        if (strpos($contentType, "\n") !== false || strpos($contentType, "\r") !== false) {
            throw new \InvalidArgumentException('The content type can not contain newline characters to prevent email header injection');
        }

        $this->contentType = $contentType;

        return $this;
    }

    /**
     * @param  string $encoding
     * @return self
     */
    public function setEncoding($encoding)
    {
        if (strpos($encoding, "\n") !== false || strpos($encoding, "\r") !== false) {
            throw new \InvalidArgumentException('The encoding can not contain newline characters to prevent email header injection');
        }

        $this->encoding = $encoding;

        return $this;
    }
}
vendor/monolog/monolog/src/Monolog/Handler/GroupHandler.php000064400000005333151327705700020061 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog\Handler;

use WPvividMonolog\Formatter\FormatterInterface;
use WPvividMonolog\ResettableInterface;

/**
 * Forwards records to multiple handlers
 *
 * @author Lenar Lõhmus <lenar@city.ee>
 */
class GroupHandler extends AbstractHandler
{
    protected $handlers;

    /**
     * @param array $handlers Array of Handlers.
     * @param bool  $bubble   Whether the messages that are handled can bubble up the stack or not
     */
    public function __construct(array $handlers, $bubble = true)
    {
        foreach ($handlers as $handler) {
            if (!$handler instanceof HandlerInterface) {
                throw new \InvalidArgumentException('The first argument of the GroupHandler must be an array of HandlerInterface instances.');
            }
        }

        $this->handlers = $handlers;
        $this->bubble = $bubble;
    }

    /**
     * {@inheritdoc}
     */
    public function isHandling(array $record)
    {
        foreach ($this->handlers as $handler) {
            if ($handler->isHandling($record)) {
                return true;
            }
        }

        return false;
    }

    /**
     * {@inheritdoc}
     */
    public function handle(array $record)
    {
        if ($this->processors) {
            foreach ($this->processors as $processor) {
                $record = call_user_func($processor, $record);
            }
        }

        foreach ($this->handlers as $handler) {
            $handler->handle($record);
        }

        return false === $this->bubble;
    }

    /**
     * {@inheritdoc}
     */
    public function handleBatch(array $records)
    {
        if ($this->processors) {
            $processed = array();
            foreach ($records as $record) {
                foreach ($this->processors as $processor) {
                    $processed[] = call_user_func($processor, $record);
                }
            }
            $records = $processed;
        }

        foreach ($this->handlers as $handler) {
            $handler->handleBatch($records);
        }
    }

    public function reset()
    {
        parent::reset();

        foreach ($this->handlers as $handler) {
            if ($handler instanceof ResettableInterface) {
                $handler->reset();
            }
        }
    }

    /**
     * {@inheritdoc}
     */
    public function setFormatter(FormatterInterface $formatter)
    {
        foreach ($this->handlers as $handler) {
            $handler->setFormatter($formatter);
        }

        return $this;
    }
}
vendor/monolog/monolog/src/Monolog/Handler/FlowdockHandler.php000064400000006471151327705700020541 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog\Handler;

use WPvividMonolog\Logger;
use WPvividMonolog\Formatter\FlowdockFormatter;
use WPvividMonolog\Formatter\FormatterInterface;

/**
 * Sends notifications through the Flowdock push API
 *
 * This must be configured with a FlowdockFormatter instance via setFormatter()
 *
 * Notes:
 * API token - Flowdock API token
 *
 * @author Dominik Liebler <liebler.dominik@gmail.com>
 * @see https://www.flowdock.com/api/push
 */
class FlowdockHandler extends SocketHandler
{
    /**
     * @var string
     */
    protected $apiToken;

    /**
     * @param string   $apiToken
     * @param bool|int $level    The minimum logging level at which this handler will be triggered
     * @param bool     $bubble   Whether the messages that are handled can bubble up the stack or not
     *
     * @throws MissingExtensionException if OpenSSL is missing
     */
    public function __construct($apiToken, $level = Logger::DEBUG, $bubble = true)
    {
        if (!extension_loaded('openssl')) {
            throw new MissingExtensionException('The OpenSSL PHP extension is required to use the FlowdockHandler');
        }

        parent::__construct('ssl://api.flowdock.com:443', $level, $bubble);
        $this->apiToken = $apiToken;
    }

    /**
     * {@inheritdoc}
     */
    public function setFormatter(FormatterInterface $formatter)
    {
        if (!$formatter instanceof FlowdockFormatter) {
            throw new \InvalidArgumentException('The FlowdockHandler requires an instance of Monolog\Formatter\FlowdockFormatter to function correctly');
        }

        return parent::setFormatter($formatter);
    }

    /**
     * Gets the default formatter.
     *
     * @return FormatterInterface
     */
    protected function getDefaultFormatter()
    {
        throw new \InvalidArgumentException('The FlowdockHandler must be configured (via setFormatter) with an instance of Monolog\Formatter\FlowdockFormatter to function correctly');
    }

    /**
     * {@inheritdoc}
     *
     * @param array $record
     */
    protected function write(array $record)
    {
        parent::write($record);

        $this->closeSocket();
    }

    /**
     * {@inheritdoc}
     *
     * @param  array  $record
     * @return string
     */
    protected function generateDataStream($record)
    {
        $content = $this->buildContent($record);

        return $this->buildHeader($content) . $content;
    }

    /**
     * Builds the body of API call
     *
     * @param  array  $record
     * @return string
     */
    private function buildContent($record)
    {
        return json_encode($record['formatted']['flowdock']);
    }

    /**
     * Builds the header of the API Call
     *
     * @param  string $content
     * @return string
     */
    private function buildHeader($content)
    {
        $header = "POST /v1/messages/team_inbox/" . $this->apiToken . " HTTP/1.1\r\n";
        $header .= "Host: api.flowdock.com\r\n";
        $header .= "Content-Type: application/json\r\n";
        $header .= "Content-Length: " . strlen($content) . "\r\n";
        $header .= "\r\n";

        return $header;
    }
}
vendor/monolog/monolog/src/Monolog/Handler/PushoverHandler.php000064400000014760151327705700020604 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog\Handler;

use WPvividMonolog\Logger;

/**
 * Sends notifications through the pushover api to mobile phones
 *
 * @author Sebastian Göttschkes <sebastian.goettschkes@googlemail.com>
 * @see    https://www.pushover.net/api
 */
class PushoverHandler extends SocketHandler
{
    private $token;
    private $users;
    private $title;
    private $user;
    private $retry;
    private $expire;

    private $highPriorityLevel;
    private $emergencyLevel;
    private $useFormattedMessage = false;

    /**
     * All parameters that can be sent to Pushover
     * @see https://pushover.net/api
     * @var array
     */
    private $parameterNames = array(
        'token' => true,
        'user' => true,
        'message' => true,
        'device' => true,
        'title' => true,
        'url' => true,
        'url_title' => true,
        'priority' => true,
        'timestamp' => true,
        'sound' => true,
        'retry' => true,
        'expire' => true,
        'callback' => true,
    );

    /**
     * Sounds the api supports by default
     * @see https://pushover.net/api#sounds
     * @var array
     */
    private $sounds = array(
        'pushover', 'bike', 'bugle', 'cashregister', 'classical', 'cosmic', 'falling', 'gamelan', 'incoming',
        'intermission', 'magic', 'mechanical', 'pianobar', 'siren', 'spacealarm', 'tugboat', 'alien', 'climb',
        'persistent', 'echo', 'updown', 'none',
    );

    /**
     * @param string       $token             Pushover api token
     * @param string|array $users             Pushover user id or array of ids the message will be sent to
     * @param string       $title             Title sent to the Pushover API
     * @param int          $level             The minimum logging level at which this handler will be triggered
     * @param bool         $bubble            Whether the messages that are handled can bubble up the stack or not
     * @param bool         $useSSL            Whether to connect via SSL. Required when pushing messages to users that are not
     *                                        the pushover.net app owner. OpenSSL is required for this option.
     * @param int          $highPriorityLevel The minimum logging level at which this handler will start
     *                                        sending "high priority" requests to the Pushover API
     * @param int          $emergencyLevel    The minimum logging level at which this handler will start
     *                                        sending "emergency" requests to the Pushover API
     * @param int          $retry             The retry parameter specifies how often (in seconds) the Pushover servers will send the same notification to the user.
     * @param int          $expire            The expire parameter specifies how many seconds your notification will continue to be retried for (every retry seconds).
     */
    public function __construct($token, $users, $title = null, $level = Logger::CRITICAL, $bubble = true, $useSSL = true, $highPriorityLevel = Logger::CRITICAL, $emergencyLevel = Logger::EMERGENCY, $retry = 30, $expire = 25200)
    {
        $connectionString = $useSSL ? 'ssl://api.pushover.net:443' : 'api.pushover.net:80';
        parent::__construct($connectionString, $level, $bubble);

        $this->token = $token;
        $this->users = (array) $users;
        $this->title = $title ?: gethostname();
        $this->highPriorityLevel = Logger::toMonologLevel($highPriorityLevel);
        $this->emergencyLevel = Logger::toMonologLevel($emergencyLevel);
        $this->retry = $retry;
        $this->expire = $expire;
    }

    protected function generateDataStream($record)
    {
        $content = $this->buildContent($record);

        return $this->buildHeader($content) . $content;
    }

    private function buildContent($record)
    {
        // Pushover has a limit of 512 characters on title and message combined.
        $maxMessageLength = 512 - strlen($this->title);

        $message = ($this->useFormattedMessage) ? $record['formatted'] : $record['message'];
        $message = substr($message, 0, $maxMessageLength);

        $timestamp = $record['datetime']->getTimestamp();

        $dataArray = array(
            'token' => $this->token,
            'user' => $this->user,
            'message' => $message,
            'title' => $this->title,
            'timestamp' => $timestamp,
        );

        if (isset($record['level']) && $record['level'] >= $this->emergencyLevel) {
            $dataArray['priority'] = 2;
            $dataArray['retry'] = $this->retry;
            $dataArray['expire'] = $this->expire;
        } elseif (isset($record['level']) && $record['level'] >= $this->highPriorityLevel) {
            $dataArray['priority'] = 1;
        }

        // First determine the available parameters
        $context = array_intersect_key($record['context'], $this->parameterNames);
        $extra = array_intersect_key($record['extra'], $this->parameterNames);

        // Least important info should be merged with subsequent info
        $dataArray = array_merge($extra, $context, $dataArray);

        // Only pass sounds that are supported by the API
        if (isset($dataArray['sound']) && !in_array($dataArray['sound'], $this->sounds)) {
            unset($dataArray['sound']);
        }

        return http_build_query($dataArray);
    }

    private function buildHeader($content)
    {
        $header = "POST /1/messages.json HTTP/1.1\r\n";
        $header .= "Host: api.pushover.net\r\n";
        $header .= "Content-Type: application/x-www-form-urlencoded\r\n";
        $header .= "Content-Length: " . strlen($content) . "\r\n";
        $header .= "\r\n";

        return $header;
    }

    protected function write(array $record)
    {
        foreach ($this->users as $user) {
            $this->user = $user;

            parent::write($record);
            $this->closeSocket();
        }

        $this->user = null;
    }

    public function setHighPriorityLevel($value)
    {
        $this->highPriorityLevel = $value;
    }

    public function setEmergencyLevel($value)
    {
        $this->emergencyLevel = $value;
    }

    /**
     * Use the formatted message?
     * @param bool $value
     */
    public function useFormattedMessage($value)
    {
        $this->useFormattedMessage = (bool) $value;
    }
}
vendor/monolog/monolog/src/Monolog/Handler/MissingExtensionException.php000064400000000711151327705700022647 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog\Handler;

/**
 * Exception can be thrown if an extension for an handler is missing
 *
 * @author  Christian Bergau <cbergau86@gmail.com>
 */
class MissingExtensionException extends \Exception
{
}
vendor/monolog/monolog/src/Monolog/Handler/AbstractHandler.php000064400000010475151327705700020533 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog\Handler;

use WPvividMonolog\Formatter\FormatterInterface;
use WPvividMonolog\Formatter\LineFormatter;
use WPvividMonolog\Logger;
use WPvividMonolog\ResettableInterface;

/**
 * Base Handler class providing the Handler structure
 *
 * @author Jordi Boggiano <j.boggiano@seld.be>
 */
abstract class AbstractHandler implements HandlerInterface, ResettableInterface
{
    protected $level = Logger::DEBUG;
    protected $bubble = true;

    /**
     * @var FormatterInterface
     */
    protected $formatter;
    protected $processors = array();

    /**
     * @param int  $level  The minimum logging level at which this handler will be triggered
     * @param bool $bubble Whether the messages that are handled can bubble up the stack or not
     */
    public function __construct($level = Logger::DEBUG, $bubble = true)
    {
        $this->setLevel($level);
        $this->bubble = $bubble;
    }

    /**
     * {@inheritdoc}
     */
    public function isHandling(array $record)
    {
        return $record['level'] >= $this->level;
    }

    /**
     * {@inheritdoc}
     */
    public function handleBatch(array $records)
    {
        foreach ($records as $record) {
            $this->handle($record);
        }
    }

    /**
     * Closes the handler.
     *
     * This will be called automatically when the object is destroyed
     */
    public function close()
    {
    }

    /**
     * {@inheritdoc}
     */
    public function pushProcessor($callback)
    {
        if (!is_callable($callback)) {
            throw new \InvalidArgumentException('Processors must be valid callables (callback or object with an __invoke method), '.var_export($callback, true).' given');
        }
        array_unshift($this->processors, $callback);

        return $this;
    }

    /**
     * {@inheritdoc}
     */
    public function popProcessor()
    {
        if (!$this->processors) {
            throw new \LogicException('You tried to pop from an empty processor stack.');
        }

        return array_shift($this->processors);
    }

    /**
     * {@inheritdoc}
     */
    public function setFormatter(FormatterInterface $formatter)
    {
        $this->formatter = $formatter;

        return $this;
    }

    /**
     * {@inheritdoc}
     */
    public function getFormatter()
    {
        if (!$this->formatter) {
            $this->formatter = $this->getDefaultFormatter();
        }

        return $this->formatter;
    }

    /**
     * Sets minimum logging level at which this handler will be triggered.
     *
     * @param  int|string $level Level or level name
     * @return self
     */
    public function setLevel($level)
    {
        $this->level = Logger::toMonologLevel($level);

        return $this;
    }

    /**
     * Gets minimum logging level at which this handler will be triggered.
     *
     * @return int
     */
    public function getLevel()
    {
        return $this->level;
    }

    /**
     * Sets the bubbling behavior.
     *
     * @param  bool $bubble true means that this handler allows bubbling.
     *                      false means that bubbling is not permitted.
     * @return self
     */
    public function setBubble($bubble)
    {
        $this->bubble = $bubble;

        return $this;
    }

    /**
     * Gets the bubbling behavior.
     *
     * @return bool true means that this handler allows bubbling.
     *              false means that bubbling is not permitted.
     */
    public function getBubble()
    {
        return $this->bubble;
    }

    public function __destruct()
    {
        try {
            $this->close();
        } catch (\Exception $e) {
            // do nothing
        } catch (\Throwable $e) {
            // do nothing
        }
    }

    public function reset()
    {
        foreach ($this->processors as $processor) {
            if ($processor instanceof ResettableInterface) {
                $processor->reset();
            }
        }
    }

    /**
     * Gets the default formatter.
     *
     * @return FormatterInterface
     */
    protected function getDefaultFormatter()
    {
        return new LineFormatter();
    }
}
vendor/monolog/monolog/src/Monolog/Handler/IFTTTHandler.php000064400000004120151327705700017650 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog\Handler;

use WPvividMonolog\Logger;

/**
 * IFTTTHandler uses cURL to trigger IFTTT Maker actions
 *
 * Register a secret key and trigger/event name at https://ifttt.com/maker
 *
 * value1 will be the channel from monolog's Logger constructor,
 * value2 will be the level name (ERROR, WARNING, ..)
 * value3 will be the log record's message
 *
 * @author Nehal Patel <nehal@nehalpatel.me>
 */
class IFTTTHandler extends AbstractProcessingHandler
{
    private $eventName;
    private $secretKey;

    /**
     * @param string $eventName The name of the IFTTT Maker event that should be triggered
     * @param string $secretKey A valid IFTTT secret key
     * @param int    $level     The minimum logging level at which this handler will be triggered
     * @param bool   $bubble    Whether the messages that are handled can bubble up the stack or not
     */
    public function __construct($eventName, $secretKey, $level = Logger::ERROR, $bubble = true)
    {
        $this->eventName = $eventName;
        $this->secretKey = $secretKey;

        parent::__construct($level, $bubble);
    }

    /**
     * {@inheritdoc}
     */
    public function write(array $record)
    {
        $postData = array(
            "value1" => $record["channel"],
            "value2" => $record["level_name"],
            "value3" => $record["message"],
        );
        $postString = json_encode($postData);

        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, "https://maker.ifttt.com/trigger/" . $this->eventName . "/with/key/" . $this->secretKey);
        curl_setopt($ch, CURLOPT_POST, true);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $postString);
        curl_setopt($ch, CURLOPT_HTTPHEADER, array(
            "Content-Type: application/json",
        ));

        Curl\Util::execute($ch);
    }
}
vendor/monolog/monolog/src/Monolog/Handler/DeduplicationHandler.php000064400000012565151327705700021556 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog\Handler;

use WPvividMonolog\Logger;

/**
 * Simple handler wrapper that deduplicates log records across multiple requests
 *
 * It also includes the BufferHandler functionality and will buffer
 * all messages until the end of the request or flush() is called.
 *
 * This works by storing all log records' messages above $deduplicationLevel
 * to the file specified by $deduplicationStore. When further logs come in at the end of the
 * request (or when flush() is called), all those above $deduplicationLevel are checked
 * against the existing stored logs. If they match and the timestamps in the stored log is
 * not older than $time seconds, the new log record is discarded. If no log record is new, the
 * whole data set is discarded.
 *
 * This is mainly useful in combination with Mail handlers or things like Slack or HipChat handlers
 * that send messages to people, to avoid spamming with the same message over and over in case of
 * a major component failure like a database server being down which makes all requests fail in the
 * same way.
 *
 * @author Jordi Boggiano <j.boggiano@seld.be>
 */
class DeduplicationHandler extends BufferHandler
{
    /**
     * @var string
     */
    protected $deduplicationStore;

    /**
     * @var int
     */
    protected $deduplicationLevel;

    /**
     * @var int
     */
    protected $time;

    /**
     * @var bool
     */
    private $gc = false;

    /**
     * @param HandlerInterface $handler            Handler.
     * @param string           $deduplicationStore The file/path where the deduplication log should be kept
     * @param int              $deduplicationLevel The minimum logging level for log records to be looked at for deduplication purposes
     * @param int              $time               The period (in seconds) during which duplicate entries should be suppressed after a given log is sent through
     * @param bool             $bubble             Whether the messages that are handled can bubble up the stack or not
     */
    public function __construct(HandlerInterface $handler, $deduplicationStore = null, $deduplicationLevel = Logger::ERROR, $time = 60, $bubble = true)
    {
        parent::__construct($handler, 0, Logger::DEBUG, $bubble, false);

        $this->deduplicationStore = $deduplicationStore === null ? sys_get_temp_dir() . '/monolog-dedup-' . substr(md5(__FILE__), 0, 20) .'.log' : $deduplicationStore;
        $this->deduplicationLevel = Logger::toMonologLevel($deduplicationLevel);
        $this->time = $time;
    }

    public function flush()
    {
        if ($this->bufferSize === 0) {
            return;
        }

        $passthru = null;

        foreach ($this->buffer as $record) {
            if ($record['level'] >= $this->deduplicationLevel) {

                $passthru = $passthru || !$this->isDuplicate($record);
                if ($passthru) {
                    $this->appendRecord($record);
                }
            }
        }

        // default of null is valid as well as if no record matches duplicationLevel we just pass through
        if ($passthru === true || $passthru === null) {
            $this->handler->handleBatch($this->buffer);
        }

        $this->clear();

        if ($this->gc) {
            $this->collectLogs();
        }
    }

    private function isDuplicate(array $record)
    {
        if (!file_exists($this->deduplicationStore)) {
            return false;
        }

        $store = file($this->deduplicationStore, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
        if (!is_array($store)) {
            return false;
        }

        $yesterday = time() - 86400;
        $timestampValidity = $record['datetime']->getTimestamp() - $this->time;
        $expectedMessage = preg_replace('{[\r\n].*}', '', $record['message']);

        for ($i = count($store) - 1; $i >= 0; $i--) {
            list($timestamp, $level, $message) = explode(':', $store[$i], 3);

            if ($level === $record['level_name'] && $message === $expectedMessage && $timestamp > $timestampValidity) {
                return true;
            }

            if ($timestamp < $yesterday) {
                $this->gc = true;
            }
        }

        return false;
    }

    private function collectLogs()
    {
        if (!file_exists($this->deduplicationStore)) {
            return false;
        }

        $handle = fopen($this->deduplicationStore, 'rw+');
        flock($handle, LOCK_EX);
        $validLogs = array();

        $timestampValidity = time() - $this->time;

        while (!feof($handle)) {
            $log = fgets($handle);
            if (substr($log, 0, 10) >= $timestampValidity) {
                $validLogs[] = $log;
            }
        }

        ftruncate($handle, 0);
        rewind($handle);
        foreach ($validLogs as $log) {
            fwrite($handle, $log);
        }

        flock($handle, LOCK_UN);
        fclose($handle);

        $this->gc = false;
    }

    private function appendRecord(array $record)
    {
        file_put_contents($this->deduplicationStore, $record['datetime']->getTimestamp() . ':' . $record['level_name'] . ':' . preg_replace('{[\r\n].*}', '', $record['message']) . "\n", FILE_APPEND);
    }
}
vendor/monolog/monolog/src/Monolog/Handler/NewRelicHandler.php000064400000014127151327705700020476 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog\Handler;

use WPvividMonolog\Logger;
use WPvividMonolog\Formatter\NormalizerFormatter;

/**
 * Class to record a log on a NewRelic application.
 * Enabling New Relic High Security mode may prevent capture of useful information.
 *
 * This handler requires a NormalizerFormatter to function and expects an array in $record['formatted']
 *
 * @see https://docs.newrelic.com/docs/agents/php-agent
 * @see https://docs.newrelic.com/docs/accounts-partnerships/accounts/security/high-security
 */
class NewRelicHandler extends AbstractProcessingHandler
{
    /**
     * Name of the New Relic application that will receive logs from this handler.
     *
     * @var string
     */
    protected $appName;

    /**
     * Name of the current transaction
     *
     * @var string
     */
    protected $transactionName;

    /**
     * Some context and extra data is passed into the handler as arrays of values. Do we send them as is
     * (useful if we are using the API), or explode them for display on the NewRelic RPM website?
     *
     * @var bool
     */
    protected $explodeArrays;

    /**
     * {@inheritDoc}
     *
     * @param string $appName
     * @param bool   $explodeArrays
     * @param string $transactionName
     */
    public function __construct(
        $level = Logger::ERROR,
        $bubble = true,
        $appName = null,
        $explodeArrays = false,
        $transactionName = null
    ) {
        parent::__construct($level, $bubble);

        $this->appName       = $appName;
        $this->explodeArrays = $explodeArrays;
        $this->transactionName = $transactionName;
    }

    /**
     * {@inheritDoc}
     */
    protected function write(array $record)
    {
        if (!$this->isNewRelicEnabled()) {
            throw new MissingExtensionException('The newrelic PHP extension is required to use the NewRelicHandler');
        }

        if ($appName = $this->getAppName($record['context'])) {
            $this->setNewRelicAppName($appName);
        }

        if ($transactionName = $this->getTransactionName($record['context'])) {
            $this->setNewRelicTransactionName($transactionName);
            unset($record['formatted']['context']['transaction_name']);
        }

        if (isset($record['context']['exception']) && ($record['context']['exception'] instanceof \Exception || (PHP_VERSION_ID >= 70000 && $record['context']['exception'] instanceof \Throwable))) {
            newrelic_notice_error($record['message'], $record['context']['exception']);
            unset($record['formatted']['context']['exception']);
        } else {
            newrelic_notice_error($record['message']);
        }

        if (isset($record['formatted']['context']) && is_array($record['formatted']['context'])) {
            foreach ($record['formatted']['context'] as $key => $parameter) {
                if (is_array($parameter) && $this->explodeArrays) {
                    foreach ($parameter as $paramKey => $paramValue) {
                        $this->setNewRelicParameter('context_' . $key . '_' . $paramKey, $paramValue);
                    }
                } else {
                    $this->setNewRelicParameter('context_' . $key, $parameter);
                }
            }
        }

        if (isset($record['formatted']['extra']) && is_array($record['formatted']['extra'])) {
            foreach ($record['formatted']['extra'] as $key => $parameter) {
                if (is_array($parameter) && $this->explodeArrays) {
                    foreach ($parameter as $paramKey => $paramValue) {
                        $this->setNewRelicParameter('extra_' . $key . '_' . $paramKey, $paramValue);
                    }
                } else {
                    $this->setNewRelicParameter('extra_' . $key, $parameter);
                }
            }
        }
    }

    /**
     * Checks whether the NewRelic extension is enabled in the system.
     *
     * @return bool
     */
    protected function isNewRelicEnabled()
    {
        return extension_loaded('newrelic');
    }

    /**
     * Returns the appname where this log should be sent. Each log can override the default appname, set in this
     * handler's constructor, by providing the appname in it's context.
     *
     * @param  array       $context
     * @return null|string
     */
    protected function getAppName(array $context)
    {
        if (isset($context['appname'])) {
            return $context['appname'];
        }

        return $this->appName;
    }

    /**
     * Returns the name of the current transaction. Each log can override the default transaction name, set in this
     * handler's constructor, by providing the transaction_name in it's context
     *
     * @param array $context
     *
     * @return null|string
     */
    protected function getTransactionName(array $context)
    {
        if (isset($context['transaction_name'])) {
            return $context['transaction_name'];
        }

        return $this->transactionName;
    }

    /**
     * Sets the NewRelic application that should receive this log.
     *
     * @param string $appName
     */
    protected function setNewRelicAppName($appName)
    {
        newrelic_set_appname($appName);
    }

    /**
     * Overwrites the name of the current transaction
     *
     * @param string $transactionName
     */
    protected function setNewRelicTransactionName($transactionName)
    {
        newrelic_name_transaction($transactionName);
    }

    /**
     * @param string $key
     * @param mixed  $value
     */
    protected function setNewRelicParameter($key, $value)
    {
        if (null === $value || is_scalar($value)) {
            newrelic_add_custom_parameter($key, $value);
        } else {
            newrelic_add_custom_parameter($key, @json_encode($value));
        }
    }

    /**
     * {@inheritDoc}
     */
    protected function getDefaultFormatter()
    {
        return new NormalizerFormatter();
    }
}
vendor/monolog/monolog/src/Monolog/Handler/ElasticSearchHandler.php000064400000006561151327705700021503 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog\Handler;

use WPvividMonolog\Formatter\FormatterInterface;
use WPvividMonolog\Formatter\ElasticaFormatter;
use WPvividMonolog\Logger;
use Elastica\Client;
use Elastica\Exception\ExceptionInterface;

/**
 * Elastic Search handler
 *
 * Usage example:
 *
 *    $client = new \Elastica\Client();
 *    $options = array(
 *        'index' => 'elastic_index_name',
 *        'type' => 'elastic_doc_type',
 *    );
 *    $handler = new ElasticSearchHandler($client, $options);
 *    $log = new Logger('application');
 *    $log->pushHandler($handler);
 *
 * @author Jelle Vink <jelle.vink@gmail.com>
 */
class ElasticSearchHandler extends AbstractProcessingHandler
{
    /**
     * @var Client
     */
    protected $client;

    /**
     * @var array Handler config options
     */
    protected $options = array();

    /**
     * @param Client $client  Elastica Client object
     * @param array  $options Handler configuration
     * @param int    $level   The minimum logging level at which this handler will be triggered
     * @param bool   $bubble  Whether the messages that are handled can bubble up the stack or not
     */
    public function __construct(Client $client, array $options = array(), $level = Logger::DEBUG, $bubble = true)
    {
        parent::__construct($level, $bubble);
        $this->client = $client;
        $this->options = array_merge(
            array(
                'index'          => 'monolog',      // Elastic index name
                'type'           => 'record',       // Elastic document type
                'ignore_error'   => false,          // Suppress Elastica exceptions
            ),
            $options
        );
    }

    /**
     * {@inheritDoc}
     */
    protected function write(array $record)
    {
        $this->bulkSend(array($record['formatted']));
    }

    /**
     * {@inheritdoc}
     */
    public function setFormatter(FormatterInterface $formatter)
    {
        if ($formatter instanceof ElasticaFormatter) {
            return parent::setFormatter($formatter);
        }
        throw new \InvalidArgumentException('ElasticSearchHandler is only compatible with ElasticaFormatter');
    }

    /**
     * Getter options
     * @return array
     */
    public function getOptions()
    {
        return $this->options;
    }

    /**
     * {@inheritDoc}
     */
    protected function getDefaultFormatter()
    {
        return new ElasticaFormatter($this->options['index'], $this->options['type']);
    }

    /**
     * {@inheritdoc}
     */
    public function handleBatch(array $records)
    {
        $documents = $this->getFormatter()->formatBatch($records);
        $this->bulkSend($documents);
    }

    /**
     * Use Elasticsearch bulk API to send list of documents
     * @param  array             $documents
     * @throws \RuntimeException
     */
    protected function bulkSend(array $documents)
    {
        try {
            $this->client->addDocuments($documents);
        } catch (ExceptionInterface $e) {
            if (!$this->options['ignore_error']) {
                throw new \RuntimeException("Error sending messages to Elasticsearch", 0, $e);
            }
        }
    }
}
vendor/monolog/monolog/src/Monolog/Handler/InsightOpsHandler.php000064400000003540151327705700021052 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

 namespace WPvividMonolog\Handler;
 
 use WPvividMonolog\Logger;

/**
 * Inspired on LogEntriesHandler.
 *
 * @author Robert Kaufmann III <rok3@rok3.me>
 * @author Gabriel Machado <gabriel.ms1@hotmail.com>
 */
class InsightOpsHandler extends SocketHandler
{
    /**
     * @var string
     */
    protected $logToken;

    /**
     * @param string $token  Log token supplied by InsightOps
     * @param string $region Region where InsightOps account is hosted. Could be 'us' or 'eu'.
     * @param bool   $useSSL Whether or not SSL encryption should be used
     * @param int    $level  The minimum logging level to trigger this handler
     * @param bool   $bubble Whether or not messages that are handled should bubble up the stack.
     *
     * @throws MissingExtensionException If SSL encryption is set to true and OpenSSL is missing
     */
    public function __construct($token, $region = 'us', $useSSL = true, $level = Logger::DEBUG, $bubble = true)
    {
        if ($useSSL && !extension_loaded('openssl')) {
            throw new MissingExtensionException('The OpenSSL PHP plugin is required to use SSL encrypted connection for LogEntriesHandler');
        }

        $endpoint = $useSSL
            ? 'ssl://' . $region . '.data.logs.insight.rapid7.com:443'
            : $region . '.data.logs.insight.rapid7.com:80';

        parent::__construct($endpoint, $level, $bubble);
        $this->logToken = $token;
    }

    /**
     * {@inheritdoc}
     *
     * @param  array  $record
     * @return string
     */
    protected function generateDataStream($record)
    {
        return $this->logToken . ' ' . $record['formatted'];
    }
}
vendor/monolog/monolog/src/Monolog/Handler/BrowserConsoleHandler.php000064400000016340151327705700021733 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog\Handler;

use WPvividMonolog\Formatter\LineFormatter;

/**
 * Handler sending logs to browser's javascript console with no browser extension required
 *
 * @author Olivier Poitrey <rs@dailymotion.com>
 */
class BrowserConsoleHandler extends AbstractProcessingHandler
{
    protected static $initialized = false;
    protected static $records = array();

    /**
     * {@inheritDoc}
     *
     * Formatted output may contain some formatting markers to be transferred to `console.log` using the %c format.
     *
     * Example of formatted string:
     *
     *     You can do [[blue text]]{color: blue} or [[green background]]{background-color: green; color: white}
     */
    protected function getDefaultFormatter()
    {
        return new LineFormatter('[[%channel%]]{macro: autolabel} [[%level_name%]]{font-weight: bold} %message%');
    }

    /**
     * {@inheritDoc}
     */
    protected function write(array $record)
    {
        // Accumulate records
        static::$records[] = $record;

        // Register shutdown handler if not already done
        if (!static::$initialized) {
            static::$initialized = true;
            $this->registerShutdownFunction();
        }
    }

    /**
     * Convert records to javascript console commands and send it to the browser.
     * This method is automatically called on PHP shutdown if output is HTML or Javascript.
     */
    public static function send()
    {
        $format = static::getResponseFormat();
        if ($format === 'unknown') {
            return;
        }

        if (count(static::$records)) {
            if ($format === 'html') {
                static::writeOutput('<script>' . static::generateScript() . '</script>');
            } elseif ($format === 'js') {
                static::writeOutput(static::generateScript());
            }
            static::resetStatic();
        }
    }

    public function close()
    {
        self::resetStatic();
    }

    public function reset()
    {
        self::resetStatic();
    }

    /**
     * Forget all logged records
     */
    public static function resetStatic()
    {
        static::$records = array();
    }

    /**
     * Wrapper for register_shutdown_function to allow overriding
     */
    protected function registerShutdownFunction()
    {
        if (PHP_SAPI !== 'cli') {
            register_shutdown_function(array('Monolog\Handler\BrowserConsoleHandler', 'send'));
        }
    }

    /**
     * Wrapper for echo to allow overriding
     *
     * @param string $str
     */
    protected static function writeOutput($str)
    {
        echo $str;
    }

    /**
     * Checks the format of the response
     *
     * If Content-Type is set to application/javascript or text/javascript -> js
     * If Content-Type is set to text/html, or is unset -> html
     * If Content-Type is anything else -> unknown
     *
     * @return string One of 'js', 'html' or 'unknown'
     */
    protected static function getResponseFormat()
    {
        // Check content type
        foreach (headers_list() as $header) {
            if (stripos($header, 'content-type:') === 0) {
                // This handler only works with HTML and javascript outputs
                // text/javascript is obsolete in favour of application/javascript, but still used
                if (stripos($header, 'application/javascript') !== false || stripos($header, 'text/javascript') !== false) {
                    return 'js';
                }
                if (stripos($header, 'text/html') === false) {
                    return 'unknown';
                }
                break;
            }
        }

        return 'html';
    }

    private static function generateScript()
    {
        $script = array();
        foreach (static::$records as $record) {
            $context = static::dump('Context', $record['context']);
            $extra = static::dump('Extra', $record['extra']);

            if (empty($context) && empty($extra)) {
                $script[] = static::call_array('log', static::handleStyles($record['formatted']));
            } else {
                $script = array_merge($script,
                    array(static::call_array('groupCollapsed', static::handleStyles($record['formatted']))),
                    $context,
                    $extra,
                    array(static::call('groupEnd'))
                );
            }
        }

        return "(function (c) {if (c && c.groupCollapsed) {\n" . implode("\n", $script) . "\n}})(console);";
    }

    private static function handleStyles($formatted)
    {
        $args = array(static::quote('font-weight: normal'));
        $format = '%c' . $formatted;
        preg_match_all('/\[\[(.*?)\]\]\{([^}]*)\}/s', $format, $matches, PREG_OFFSET_CAPTURE | PREG_SET_ORDER);

        foreach (array_reverse($matches) as $match) {
            $args[] = static::quote(static::handleCustomStyles($match[2][0], $match[1][0]));
            $args[] = '"font-weight: normal"';

            $pos = $match[0][1];
            $format = substr($format, 0, $pos) . '%c' . $match[1][0] . '%c' . substr($format, $pos + strlen($match[0][0]));
        }

        array_unshift($args, static::quote($format));

        return $args;
    }

    private static function handleCustomStyles($style, $string)
    {
        static $colors = array('blue', 'green', 'red', 'magenta', 'orange', 'black', 'grey');
        static $labels = array();

        return preg_replace_callback('/macro\s*:(.*?)(?:;|$)/', function ($m) use ($string, &$colors, &$labels) {
            if (trim($m[1]) === 'autolabel') {
                // Format the string as a label with consistent auto assigned background color
                if (!isset($labels[$string])) {
                    $labels[$string] = $colors[count($labels) % count($colors)];
                }
                $color = $labels[$string];

                return "background-color: $color; color: white; border-radius: 3px; padding: 0 2px 0 2px";
            }

            return $m[1];
        }, $style);
    }

    private static function dump($title, array $dict)
    {
        $script = array();
        $dict = array_filter($dict);
        if (empty($dict)) {
            return $script;
        }
        $script[] = static::call('log', static::quote('%c%s'), static::quote('font-weight: bold'), static::quote($title));
        foreach ($dict as $key => $value) {
            $value = json_encode($value);
            if (empty($value)) {
                $value = static::quote('');
            }
            $script[] = static::call('log', static::quote('%s: %o'), static::quote($key), $value);
        }

        return $script;
    }

    private static function quote($arg)
    {
        return '"' . addcslashes($arg, "\"\n\\") . '"';
    }

    private static function call()
    {
        $args = func_get_args();
        $method = array_shift($args);

        return static::call_array($method, $args);
    }

    private static function call_array($method, array $args)
    {
        return 'c.' . $method . '(' . implode(', ', $args) . ');';
    }
}
vendor/monolog/monolog/src/Monolog/Handler/AbstractSyslogHandler.php000064400000006500151327705700021726 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog\Handler;

use WPvividMonolog\Logger;
use WPvividMonolog\Formatter\LineFormatter;

/**
 * Common syslog functionality
 */
abstract class AbstractSyslogHandler extends AbstractProcessingHandler
{
    protected $facility;

    /**
     * Translates Monolog log levels to syslog log priorities.
     */
    protected $logLevels = array(
        Logger::DEBUG     => LOG_DEBUG,
        Logger::INFO      => LOG_INFO,
        Logger::NOTICE    => LOG_NOTICE,
        Logger::WARNING   => LOG_WARNING,
        Logger::ERROR     => LOG_ERR,
        Logger::CRITICAL  => LOG_CRIT,
        Logger::ALERT     => LOG_ALERT,
        Logger::EMERGENCY => LOG_EMERG,
    );

    /**
     * List of valid log facility names.
     */
    protected $facilities = array(
        'auth'     => LOG_AUTH,
        'authpriv' => LOG_AUTHPRIV,
        'cron'     => LOG_CRON,
        'daemon'   => LOG_DAEMON,
        'kern'     => LOG_KERN,
        'lpr'      => LOG_LPR,
        'mail'     => LOG_MAIL,
        'news'     => LOG_NEWS,
        'syslog'   => LOG_SYSLOG,
        'user'     => LOG_USER,
        'uucp'     => LOG_UUCP,
    );

    /**
     * @param mixed $facility
     * @param int   $level The minimum logging level at which this handler will be triggered
     * @param bool  $bubble Whether the messages that are handled can bubble up the stack or not
     */
    public function __construct($facility = LOG_USER, $level = Logger::DEBUG, $bubble = true)
    {
        parent::__construct($level, $bubble);

        if (!defined('PHP_WINDOWS_VERSION_BUILD')) {
            $this->facilities['local0'] = LOG_LOCAL0;
            $this->facilities['local1'] = LOG_LOCAL1;
            $this->facilities['local2'] = LOG_LOCAL2;
            $this->facilities['local3'] = LOG_LOCAL3;
            $this->facilities['local4'] = LOG_LOCAL4;
            $this->facilities['local5'] = LOG_LOCAL5;
            $this->facilities['local6'] = LOG_LOCAL6;
            $this->facilities['local7'] = LOG_LOCAL7;
        } else {
            $this->facilities['local0'] = 128; // LOG_LOCAL0
            $this->facilities['local1'] = 136; // LOG_LOCAL1
            $this->facilities['local2'] = 144; // LOG_LOCAL2
            $this->facilities['local3'] = 152; // LOG_LOCAL3
            $this->facilities['local4'] = 160; // LOG_LOCAL4
            $this->facilities['local5'] = 168; // LOG_LOCAL5
            $this->facilities['local6'] = 176; // LOG_LOCAL6
            $this->facilities['local7'] = 184; // LOG_LOCAL7
        }

        // convert textual description of facility to syslog constant
        if (array_key_exists(strtolower($facility), $this->facilities)) {
            $facility = $this->facilities[strtolower($facility)];
        } elseif (!in_array($facility, array_values($this->facilities), true)) {
            throw new \UnexpectedValueException('Unknown facility value "'.$facility.'" given');
        }

        $this->facility = $facility;
    }

    /**
     * {@inheritdoc}
     */
    protected function getDefaultFormatter()
    {
        return new LineFormatter('%channel%.%level_name%: %message% %context% %extra%');
    }
}
vendor/monolog/monolog/src/Monolog/Handler/StreamHandler.php000064400000012162151327705700020216 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog\Handler;

use WPvividMonolog\Logger;

/**
 * Stores to any stream resource
 *
 * Can be used to store into php://stderr, remote and local files, etc.
 *
 * @author Jordi Boggiano <j.boggiano@seld.be>
 */
class StreamHandler extends AbstractProcessingHandler
{
    protected $stream;
    protected $url;
    private $errorMessage;
    protected $filePermission;
    protected $useLocking;
    private $dirCreated;

    /**
     * @param resource|string $stream
     * @param int             $level          The minimum logging level at which this handler will be triggered
     * @param bool            $bubble         Whether the messages that are handled can bubble up the stack or not
     * @param int|null        $filePermission Optional file permissions (default (0644) are only for owner read/write)
     * @param bool            $useLocking     Try to lock log file before doing any writes
     *
     * @throws \Exception                If a missing directory is not buildable
     * @throws \InvalidArgumentException If stream is not a resource or string
     */
    public function __construct($stream, $level = Logger::DEBUG, $bubble = true, $filePermission = null, $useLocking = false)
    {
        parent::__construct($level, $bubble);
        if (is_resource($stream)) {
            $this->stream = $stream;
        } elseif (is_string($stream)) {
            $this->url = $stream;
        } else {
            throw new \InvalidArgumentException('A stream must either be a resource or a string.');
        }

        $this->filePermission = $filePermission;
        $this->useLocking = $useLocking;
    }

    /**
     * {@inheritdoc}
     */
    public function close()
    {
        if ($this->url && is_resource($this->stream)) {
            fclose($this->stream);
        }
        $this->stream = null;
    }

    /**
     * Return the currently active stream if it is open
     *
     * @return resource|null
     */
    public function getStream()
    {
        return $this->stream;
    }

    /**
     * Return the stream URL if it was configured with a URL and not an active resource
     *
     * @return string|null
     */
    public function getUrl()
    {
        return $this->url;
    }

    /**
     * {@inheritdoc}
     */
    protected function write(array $record)
    {
        if (!is_resource($this->stream)) {
            if (null === $this->url || '' === $this->url) {
                throw new \LogicException('Missing stream url, the stream can not be opened. This may be caused by a premature call to close().');
            }
            $this->createDir();
            $this->errorMessage = null;
            set_error_handler(array($this, 'customErrorHandler'));
            $this->stream = fopen($this->url, 'a');
            if ($this->filePermission !== null) {
                @chmod($this->url, $this->filePermission);
            }
            restore_error_handler();
            if (!is_resource($this->stream)) {
                $this->stream = null;
                throw new \UnexpectedValueException(sprintf('The stream or file "%s" could not be opened: '.$this->errorMessage, $this->url));
            }
        }

        if ($this->useLocking) {
            // ignoring errors here, there's not much we can do about them
            flock($this->stream, LOCK_EX);
        }

        $this->streamWrite($this->stream, $record);

        if ($this->useLocking) {
            flock($this->stream, LOCK_UN);
        }
    }

    /**
     * Write to stream
     * @param resource $stream
     * @param array $record
     */
    protected function streamWrite($stream, array $record)
    {
        fwrite($stream, (string) $record['formatted']);
    }

    private function customErrorHandler($code, $msg)
    {
        $this->errorMessage = preg_replace('{^(fopen|mkdir)\(.*?\): }', '', $msg);
    }

    /**
     * @param string $stream
     *
     * @return null|string
     */
    private function getDirFromStream($stream)
    {
        $pos = strpos($stream, '://');
        if ($pos === false) {
            return dirname($stream);
        }

        if ('file://' === substr($stream, 0, 7)) {
            return dirname(substr($stream, 7));
        }

        return;
    }

    private function createDir()
    {
        // Do not try to create dir if it has already been tried.
        if ($this->dirCreated) {
            return;
        }

        $dir = $this->getDirFromStream($this->url);
        if (null !== $dir && !is_dir($dir)) {
            $this->errorMessage = null;
            set_error_handler(array($this, 'customErrorHandler'));
            $status = mkdir($dir, 0777, true);
            restore_error_handler();
            if (false === $status && !is_dir($dir)) {
                throw new \UnexpectedValueException(sprintf('There is no existing directory at "%s" and its not buildable: '.$this->errorMessage, $dir));
            }
        }
        $this->dirCreated = true;
    }
}
vendor/monolog/monolog/src/Monolog/Handler/RedisHandler.php000064400000005536151327705700020040 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog\Handler;

use WPvividMonolog\Formatter\LineFormatter;
use WPvividMonolog\Logger;

/**
 * Logs to a Redis key using rpush
 *
 * usage example:
 *
 *   $log = new Logger('application');
 *   $redis = new RedisHandler(new Predis\Client("tcp://localhost:6379"), "logs", "prod");
 *   $log->pushHandler($redis);
 *
 * @author Thomas Tourlourat <thomas@tourlourat.com>
 */
class RedisHandler extends AbstractProcessingHandler
{
    private $redisClient;
    private $redisKey;
    protected $capSize;

    /**
     * @param \Predis\Client|\Redis $redis   The redis instance
     * @param string                $key     The key name to push records to
     * @param int                   $level   The minimum logging level at which this handler will be triggered
     * @param bool                  $bubble  Whether the messages that are handled can bubble up the stack or not
     * @param int                   $capSize Number of entries to limit list size to
     */
    public function __construct($redis, $key, $level = Logger::DEBUG, $bubble = true, $capSize = false)
    {
        if (!(($redis instanceof \Predis\Client) || ($redis instanceof \Redis))) {
            throw new \InvalidArgumentException('Predis\Client or Redis instance required');
        }

        $this->redisClient = $redis;
        $this->redisKey = $key;
        $this->capSize = $capSize;

        parent::__construct($level, $bubble);
    }

    /**
     * {@inheritDoc}
     */
    protected function write(array $record)
    {
        if ($this->capSize) {
            $this->writeCapped($record);
        } else {
            $this->redisClient->rpush($this->redisKey, $record["formatted"]);
        }
    }

    /**
     * Write and cap the collection
     * Writes the record to the redis list and caps its
     *
     * @param  array $record associative record array
     * @return void
     */
    protected function writeCapped(array $record)
    {
        if ($this->redisClient instanceof \Redis) {
            $this->redisClient->multi()
                ->rpush($this->redisKey, $record["formatted"])
                ->ltrim($this->redisKey, -$this->capSize, -1)
                ->exec();
        } else {
            $redisKey = $this->redisKey;
            $capSize = $this->capSize;
            $this->redisClient->transaction(function ($tx) use ($record, $redisKey, $capSize) {
                $tx->rpush($redisKey, $record["formatted"]);
                $tx->ltrim($redisKey, -$capSize, -1);
            });
        }
    }

    /**
     * {@inheritDoc}
     */
    protected function getDefaultFormatter()
    {
        return new LineFormatter();
    }
}
vendor/monolog/monolog/src/Monolog/Handler/ZendMonitorHandler.php000064400000004325151327705700021235 0ustar00<?php
/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog\Handler;

use WPvividMonolog\Formatter\NormalizerFormatter;
use WPvividMonolog\Logger;

/**
 * Handler sending logs to Zend Monitor
 *
 * @author  Christian Bergau <cbergau86@gmail.com>
 */
class ZendMonitorHandler extends AbstractProcessingHandler
{
    /**
     * Monolog level / ZendMonitor Custom Event priority map
     *
     * @var array
     */
    protected $levelMap = array(
        Logger::DEBUG     => 1,
        Logger::INFO      => 2,
        Logger::NOTICE    => 3,
        Logger::WARNING   => 4,
        Logger::ERROR     => 5,
        Logger::CRITICAL  => 6,
        Logger::ALERT     => 7,
        Logger::EMERGENCY => 0,
    );

    /**
     * Construct
     *
     * @param  int                       $level
     * @param  bool                      $bubble
     * @throws MissingExtensionException
     */
    public function __construct($level = Logger::DEBUG, $bubble = true)
    {
        if (!function_exists('zend_monitor_custom_event')) {
            throw new MissingExtensionException('You must have Zend Server installed in order to use this handler');
        }
        parent::__construct($level, $bubble);
    }

    /**
     * {@inheritdoc}
     */
    protected function write(array $record)
    {
        $this->writeZendMonitorCustomEvent(
            $this->levelMap[$record['level']],
            $record['message'],
            $record['formatted']
        );
    }

    /**
     * Write a record to Zend Monitor
     *
     * @param int    $level
     * @param string $message
     * @param array  $formatted
     */
    protected function writeZendMonitorCustomEvent($level, $message, $formatted)
    {
        zend_monitor_custom_event($level, $message, $formatted);
    }

    /**
     * {@inheritdoc}
     */
    public function getDefaultFormatter()
    {
        return new NormalizerFormatter();
    }

    /**
     * Get the level map
     *
     * @return array
     */
    public function getLevelMap()
    {
        return $this->levelMap;
    }
}
vendor/monolog/monolog/src/Monolog/Handler/NullHandler.php000064400000001707151327705700017700 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog\Handler;

use WPvividMonolog\Logger;

/**
 * Blackhole
 *
 * Any record it can handle will be thrown away. This can be used
 * to put on top of an existing stack to override it temporarily.
 *
 * @author Jordi Boggiano <j.boggiano@seld.be>
 */
class NullHandler extends AbstractHandler
{
    /**
     * @param int $level The minimum logging level at which this handler will be triggered
     */
    public function __construct($level = Logger::DEBUG)
    {
        parent::__construct($level, false);
    }

    /**
     * {@inheritdoc}
     */
    public function handle(array $record)
    {
        if ($record['level'] < $this->level) {
            return false;
        }

        return true;
    }
}
vendor/monolog/monolog/src/Monolog/Handler/LogEntriesHandler.php000064400000003135151327705700021036 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog\Handler;

use WPvividMonolog\Logger;

/**
 * @author Robert Kaufmann III <rok3@rok3.me>
 */
class LogEntriesHandler extends SocketHandler
{
    /**
     * @var string
     */
    protected $logToken;

    /**
     * @param string $token  Log token supplied by LogEntries
     * @param bool   $useSSL Whether or not SSL encryption should be used.
     * @param int    $level  The minimum logging level to trigger this handler
     * @param bool   $bubble Whether or not messages that are handled should bubble up the stack.
     *
     * @throws MissingExtensionException If SSL encryption is set to true and OpenSSL is missing
     */
    public function __construct($token, $useSSL = true, $level = Logger::DEBUG, $bubble = true, $host = 'data.logentries.com')
    {
        if ($useSSL && !extension_loaded('openssl')) {
            throw new MissingExtensionException('The OpenSSL PHP plugin is required to use SSL encrypted connection for LogEntriesHandler');
        }

        $endpoint = $useSSL ? 'ssl://' . $host . ':443' : $host . ':80';
        parent::__construct($endpoint, $level, $bubble);
        $this->logToken = $token;
    }

    /**
     * {@inheritdoc}
     *
     * @param  array  $record
     * @return string
     */
    protected function generateDataStream($record)
    {
        return $this->logToken . ' ' . $record['formatted'];
    }
}
vendor/monolog/monolog/src/Monolog/Handler/SwiftMailerHandler.php000064400000006610151327705700021212 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog\Handler;

use WPvividMonolog\Logger;
use WPvividMonolog\Formatter\FormatterInterface;
use WPvividMonolog\Formatter\LineFormatter;
use Swift;

/**
 * SwiftMailerHandler uses Swift_Mailer to send the emails
 *
 * @author Gyula Sallai
 */
class SwiftMailerHandler extends MailHandler
{
    protected $mailer;
    private $messageTemplate;

    /**
     * @param \Swift_Mailer           $mailer  The mailer to use
     * @param callable|\Swift_Message $message An example message for real messages, only the body will be replaced
     * @param int                     $level   The minimum logging level at which this handler will be triggered
     * @param bool                    $bubble  Whether the messages that are handled can bubble up the stack or not
     */
    public function __construct(\Swift_Mailer $mailer, $message, $level = Logger::ERROR, $bubble = true)
    {
        parent::__construct($level, $bubble);

        $this->mailer = $mailer;
        $this->messageTemplate = $message;
    }

    /**
     * {@inheritdoc}
     */
    protected function send($content, array $records)
    {
        $this->mailer->send($this->buildMessage($content, $records));
    }

    /**
     * Gets the formatter for the Swift_Message subject.
     *
     * @param  string             $format The format of the subject
     * @return FormatterInterface
     */
    protected function getSubjectFormatter($format)
    {
        return new LineFormatter($format);
    }

    /**
     * Creates instance of Swift_Message to be sent
     *
     * @param  string         $content formatted email body to be sent
     * @param  array          $records Log records that formed the content
     * @return \Swift_Message
     */
    protected function buildMessage($content, array $records)
    {
        $message = null;
        if ($this->messageTemplate instanceof \Swift_Message) {
            $message = clone $this->messageTemplate;
            $message->generateId();
        } elseif (is_callable($this->messageTemplate)) {
            $message = call_user_func($this->messageTemplate, $content, $records);
        }

        if (!$message instanceof \Swift_Message) {
            throw new \InvalidArgumentException('Could not resolve message as instance of Swift_Message or a callable returning it');
        }

        if ($records) {
            $subjectFormatter = $this->getSubjectFormatter($message->getSubject());
            $message->setSubject($subjectFormatter->format($this->getHighestRecord($records)));
        }

        $message->setBody($content);
        if (version_compare(Swift::VERSION, '6.0.0', '>=')) {
            $message->setDate(new \DateTimeImmutable());
        } else {
            $message->setDate(time());
        }

        return $message;
    }

    /**
     * BC getter, to be removed in 2.0
     */
    public function __get($name)
    {
        if ($name === 'message') {
            trigger_error('SwiftMailerHandler->message is deprecated, use ->buildMessage() instead to retrieve the message', E_USER_DEPRECATED);

            return $this->buildMessage(null, array());
        }

        throw new \InvalidArgumentException('Invalid property '.$name);
    }
}
vendor/monolog/monolog/src/Monolog/Handler/FirePHPHandler.php000064400000012545151327705700020225 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog\Handler;

use WPvividMonolog\Formatter\WildfireFormatter;

/**
 * Simple FirePHP Handler (http://www.firephp.org/), which uses the Wildfire protocol.
 *
 * @author Eric Clemmons (@ericclemmons) <eric@uxdriven.com>
 */
class FirePHPHandler extends AbstractProcessingHandler
{
    /**
     * WildFire JSON header message format
     */
    const PROTOCOL_URI = 'http://meta.wildfirehq.org/Protocol/JsonStream/0.2';

    /**
     * FirePHP structure for parsing messages & their presentation
     */
    const STRUCTURE_URI = 'http://meta.firephp.org/Wildfire/Structure/FirePHP/FirebugConsole/0.1';

    /**
     * Must reference a "known" plugin, otherwise headers won't display in FirePHP
     */
    const PLUGIN_URI = 'http://meta.firephp.org/Wildfire/Plugin/FirePHP/Library-FirePHPCore/0.3';

    /**
     * Header prefix for Wildfire to recognize & parse headers
     */
    const HEADER_PREFIX = 'X-Wf';

    /**
     * Whether or not Wildfire vendor-specific headers have been generated & sent yet
     */
    protected static $initialized = false;

    /**
     * Shared static message index between potentially multiple handlers
     * @var int
     */
    protected static $messageIndex = 1;

    protected static $sendHeaders = true;

    /**
     * Base header creation function used by init headers & record headers
     *
     * @param  array  $meta    Wildfire Plugin, Protocol & Structure Indexes
     * @param  string $message Log message
     * @return array  Complete header string ready for the client as key and message as value
     */
    protected function createHeader(array $meta, $message)
    {
        $header = sprintf('%s-%s', self::HEADER_PREFIX, join('-', $meta));

        return array($header => $message);
    }

    /**
     * Creates message header from record
     *
     * @see createHeader()
     * @param  array  $record
     * @return string
     */
    protected function createRecordHeader(array $record)
    {
        // Wildfire is extensible to support multiple protocols & plugins in a single request,
        // but we're not taking advantage of that (yet), so we're using "1" for simplicity's sake.
        return $this->createHeader(
            array(1, 1, 1, self::$messageIndex++),
            $record['formatted']
        );
    }

    /**
     * {@inheritDoc}
     */
    protected function getDefaultFormatter()
    {
        return new WildfireFormatter();
    }

    /**
     * Wildfire initialization headers to enable message parsing
     *
     * @see createHeader()
     * @see sendHeader()
     * @return array
     */
    protected function getInitHeaders()
    {
        // Initial payload consists of required headers for Wildfire
        return array_merge(
            $this->createHeader(array('Protocol', 1), self::PROTOCOL_URI),
            $this->createHeader(array(1, 'Structure', 1), self::STRUCTURE_URI),
            $this->createHeader(array(1, 'Plugin', 1), self::PLUGIN_URI)
        );
    }

    /**
     * Send header string to the client
     *
     * @param string $header
     * @param string $content
     */
    protected function sendHeader($header, $content)
    {
        if (!headers_sent() && self::$sendHeaders) {
            header(sprintf('%s: %s', $header, $content));
        }
    }

    /**
     * Creates & sends header for a record, ensuring init headers have been sent prior
     *
     * @see sendHeader()
     * @see sendInitHeaders()
     * @param array $record
     */
    protected function write(array $record)
    {
        if (!self::$sendHeaders) {
            return;
        }

        // WildFire-specific headers must be sent prior to any messages
        if (!self::$initialized) {
            self::$initialized = true;

            self::$sendHeaders = $this->headersAccepted();
            if (!self::$sendHeaders) {
                return;
            }

            foreach ($this->getInitHeaders() as $header => $content) {
                $this->sendHeader($header, $content);
            }
        }

        $header = $this->createRecordHeader($record);
        if (trim(current($header)) !== '') {
            $this->sendHeader(key($header), current($header));
        }
    }

    /**
     * Verifies if the headers are accepted by the current user agent
     *
     * @return bool
     */
    protected function headersAccepted()
    {
        if (!empty($_SERVER['HTTP_USER_AGENT']) && preg_match('{\bFirePHP/\d+\.\d+\b}', $_SERVER['HTTP_USER_AGENT'])) {
            return true;
        }

        return isset($_SERVER['HTTP_X_FIREPHP_VERSION']);
    }

    /**
     * BC getter for the sendHeaders property that has been made static
     */
    public function __get($property)
    {
        if ('sendHeaders' !== $property) {
            throw new \InvalidArgumentException('Undefined property '.$property);
        }

        return static::$sendHeaders;
    }

    /**
     * BC setter for the sendHeaders property that has been made static
     */
    public function __set($property, $value)
    {
        if ('sendHeaders' !== $property) {
            throw new \InvalidArgumentException('Undefined property '.$property);
        }

        static::$sendHeaders = $value;
    }
}
vendor/monolog/monolog/src/Monolog/Handler/MandrillHandler.php000064400000004172151327705700020527 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog\Handler;

use WPvividMonolog\Logger;

/**
 * MandrillHandler uses cURL to send the emails to the Mandrill API
 *
 * @author Adam Nicholson <adamnicholson10@gmail.com>
 */
class MandrillHandler extends MailHandler
{
    protected $message;
    protected $apiKey;

    /**
     * @param string                  $apiKey  A valid Mandrill API key
     * @param callable|\Swift_Message $message An example message for real messages, only the body will be replaced
     * @param int                     $level   The minimum logging level at which this handler will be triggered
     * @param bool                    $bubble  Whether the messages that are handled can bubble up the stack or not
     */
    public function __construct($apiKey, $message, $level = Logger::ERROR, $bubble = true)
    {
        parent::__construct($level, $bubble);

        if (!$message instanceof \Swift_Message && is_callable($message)) {
            $message = call_user_func($message);
        }
        if (!$message instanceof \Swift_Message) {
            throw new \InvalidArgumentException('You must provide either a Swift_Message instance or a callable returning it');
        }
        $this->message = $message;
        $this->apiKey = $apiKey;
    }

    /**
     * {@inheritdoc}
     */
    protected function send($content, array $records)
    {
        $message = clone $this->message;
        $message->setBody($content);
        $message->setDate(time());

        $ch = curl_init();

        curl_setopt($ch, CURLOPT_URL, 'https://mandrillapp.com/api/1.0/messages/send-raw.json');
        curl_setopt($ch, CURLOPT_POST, 1);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query(array(
            'key' => $this->apiKey,
            'raw_message' => (string) $message,
            'async' => false,
        )));

        Curl\Util::execute($ch);
    }
}
vendor/monolog/monolog/src/Monolog/Handler/FingersCrossed/ChannelLevelActivationStrategy.php000064400000003642151327705700026515 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog\Handler\FingersCrossed;

use WPvividMonolog\Logger;

/**
 * Channel and Error level based monolog activation strategy. Allows to trigger activation
 * based on level per channel. e.g. trigger activation on level 'ERROR' by default, except
 * for records of the 'sql' channel; those should trigger activation on level 'WARN'.
 *
 * Example:
 *
 * <code>
 *   $activationStrategy = new ChannelLevelActivationStrategy(
 *       Logger::CRITICAL,
 *       array(
 *           'request' => Logger::ALERT,
 *           'sensitive' => Logger::ERROR,
 *       )
 *   );
 *   $handler = new FingersCrossedHandler(new StreamHandler('php://stderr'), $activationStrategy);
 * </code>
 *
 * @author Mike Meessen <netmikey@gmail.com>
 */
class ChannelLevelActivationStrategy implements ActivationStrategyInterface
{
    private $defaultActionLevel;
    private $channelToActionLevel;

    /**
     * @param int   $defaultActionLevel   The default action level to be used if the record's category doesn't match any
     * @param array $channelToActionLevel An array that maps channel names to action levels.
     */
    public function __construct($defaultActionLevel, $channelToActionLevel = array())
    {
        $this->defaultActionLevel = Logger::toMonologLevel($defaultActionLevel);
        $this->channelToActionLevel = array_map('WPvividMonolog\Logger::toMonologLevel', $channelToActionLevel);
    }

    public function isHandlerActivated(array $record)
    {
        if (isset($this->channelToActionLevel[$record['channel']])) {
            return $record['level'] >= $this->channelToActionLevel[$record['channel']];
        }

        return $record['level'] >= $this->defaultActionLevel;
    }
}
vendor/monolog/monolog/src/Monolog/Handler/FingersCrossed/ActivationStrategyInterface.php000064400000001217151327705700026051 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog\Handler\FingersCrossed;

/**
 * Interface for activation strategies for the FingersCrossedHandler.
 *
 * @author Johannes M. Schmitt <schmittjoh@gmail.com>
 */
interface ActivationStrategyInterface
{
    /**
     * Returns whether the given record activates the handler.
     *
     * @param  array   $record
     * @return bool
     */
    public function isHandlerActivated(array $record);
}
vendor/monolog/monolog/src/Monolog/Handler/FingersCrossed/ErrorLevelActivationStrategy.php000064400000001405151327705700026231 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog\Handler\FingersCrossed;

use WPvividMonolog\Logger;

/**
 * Error level based activation strategy.
 *
 * @author Johannes M. Schmitt <schmittjoh@gmail.com>
 */
class ErrorLevelActivationStrategy implements ActivationStrategyInterface
{
    private $actionLevel;

    public function __construct($actionLevel)
    {
        $this->actionLevel = Logger::toMonologLevel($actionLevel);
    }

    public function isHandlerActivated(array $record)
    {
        return $record['level'] >= $this->actionLevel;
    }
}
vendor/monolog/monolog/src/Monolog/Handler/SyslogHandler.php000064400000003501151327705700020240 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog\Handler;

use WPvividMonolog\Logger;

/**
 * Logs to syslog service.
 *
 * usage example:
 *
 *   $log = new Logger('application');
 *   $syslog = new SyslogHandler('myfacility', 'local6');
 *   $formatter = new LineFormatter("%channel%.%level_name%: %message% %extra%");
 *   $syslog->setFormatter($formatter);
 *   $log->pushHandler($syslog);
 *
 * @author Sven Paulus <sven@karlsruhe.org>
 */
class SyslogHandler extends AbstractSyslogHandler
{
    protected $ident;
    protected $logopts;

    /**
     * @param string $ident
     * @param mixed  $facility
     * @param int    $level    The minimum logging level at which this handler will be triggered
     * @param bool   $bubble   Whether the messages that are handled can bubble up the stack or not
     * @param int    $logopts  Option flags for the openlog() call, defaults to LOG_PID
     */
    public function __construct($ident, $facility = LOG_USER, $level = Logger::DEBUG, $bubble = true, $logopts = LOG_PID)
    {
        parent::__construct($facility, $level, $bubble);

        $this->ident = $ident;
        $this->logopts = $logopts;
    }

    /**
     * {@inheritdoc}
     */
    public function close()
    {
        closelog();
    }

    /**
     * {@inheritdoc}
     */
    protected function write(array $record)
    {
        if (!openlog($this->ident, $this->logopts, $this->facility)) {
            throw new \LogicException('Can\'t open syslog for ident "'.$this->ident.'" and facility "'.$this->facility.'"');
        }
        syslog($this->logLevels[$record['level']], (string) $record['formatted']);
    }
}
vendor/monolog/monolog/src/Monolog/Handler/SyslogUdp/UdpSocket.php000064400000002600151327705700021313 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog\Handler\SyslogUdp;

class UdpSocket
{
    const DATAGRAM_MAX_LENGTH = 65023;

    protected $ip;
    protected $port;
    protected $socket;

    public function __construct($ip, $port = 514)
    {
        $this->ip = $ip;
        $this->port = $port;
        $this->socket = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
    }

    public function write($line, $header = "")
    {
        $this->send($this->assembleMessage($line, $header));
    }

    public function close()
    {
        if (is_resource($this->socket)) {
            socket_close($this->socket);
            $this->socket = null;
        }
    }

    protected function send($chunk)
    {
        if (!is_resource($this->socket)) {
            throw new \LogicException('The UdpSocket to '.$this->ip.':'.$this->port.' has been closed and can not be written to anymore');
        }
        socket_sendto($this->socket, $chunk, strlen($chunk), $flags = 0, $this->ip, $this->port);
    }

    protected function assembleMessage($line, $header)
    {
        $chunkSize = self::DATAGRAM_MAX_LENGTH - strlen($header);

        return $header . substr($line, 0, $chunkSize);
    }
}
vendor/monolog/monolog/src/Monolog/Handler/DynamoDbHandler.php000064400000004635151327705700020466 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog\Handler;

use Aws\Sdk;
use Aws\DynamoDb\DynamoDbClient;
use Aws\DynamoDb\Marshaler;
use WPvividMonolog\Formatter\ScalarFormatter;
use WPvividMonolog\Logger;

/**
 * Amazon DynamoDB handler (http://aws.amazon.com/dynamodb/)
 *
 * @link https://github.com/aws/aws-sdk-php/
 * @author Andrew Lawson <adlawson@gmail.com>
 */
class DynamoDbHandler extends AbstractProcessingHandler
{
    const DATE_FORMAT = 'Y-m-d\TH:i:s.uO';

    /**
     * @var DynamoDbClient
     */
    protected $client;

    /**
     * @var string
     */
    protected $table;

    /**
     * @var int
     */
    protected $version;

    /**
     * @var Marshaler
     */
    protected $marshaler;

    /**
     * @param DynamoDbClient $client
     * @param string         $table
     * @param int            $level
     * @param bool           $bubble
     */
    public function __construct(DynamoDbClient $client, $table, $level = Logger::DEBUG, $bubble = true)
    {
        if (defined('Aws\Sdk::VERSION') && version_compare(Sdk::VERSION, '3.0', '>=')) {
            $this->version = 3;
            $this->marshaler = new Marshaler;
        } else {
            $this->version = 2;
        }

        $this->client = $client;
        $this->table = $table;

        parent::__construct($level, $bubble);
    }

    /**
     * {@inheritdoc}
     */
    protected function write(array $record)
    {
        $filtered = $this->filterEmptyFields($record['formatted']);
        if ($this->version === 3) {
            $formatted = $this->marshaler->marshalItem($filtered);
        } else {
            $formatted = $this->client->formatAttributes($filtered);
        }

        $this->client->putItem(array(
            'TableName' => $this->table,
            'Item' => $formatted,
        ));
    }

    /**
     * @param  array $record
     * @return array
     */
    protected function filterEmptyFields(array $record)
    {
        return array_filter($record, function ($value) {
            return !empty($value) || false === $value || 0 === $value;
        });
    }

    /**
     * {@inheritdoc}
     */
    protected function getDefaultFormatter()
    {
        return new ScalarFormatter(self::DATE_FORMAT);
    }
}
vendor/monolog/monolog/src/Monolog/Handler/MailHandler.php000064400000003135151327705700017645 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog\Handler;

/**
 * Base class for all mail handlers
 *
 * @author Gyula Sallai
 */
abstract class MailHandler extends AbstractProcessingHandler
{
    /**
     * {@inheritdoc}
     */
    public function handleBatch(array $records)
    {
        $messages = array();

        foreach ($records as $record) {
            if ($record['level'] < $this->level) {
                continue;
            }
            $messages[] = $this->processRecord($record);
        }

        if (!empty($messages)) {
            $this->send((string) $this->getFormatter()->formatBatch($messages), $messages);
        }
    }

    /**
     * Send a mail with the given content
     *
     * @param string $content formatted email body to be sent
     * @param array  $records the array of log records that formed this content
     */
    abstract protected function send($content, array $records);

    /**
     * {@inheritdoc}
     */
    protected function write(array $record)
    {
        $this->send((string) $record['formatted'], array($record));
    }

    protected function getHighestRecord(array $records)
    {
        $highestRecord = null;
        foreach ($records as $record) {
            if ($highestRecord === null || $highestRecord['level'] < $record['level']) {
                $highestRecord = $record;
            }
        }

        return $highestRecord;
    }
}
vendor/monolog/monolog/src/Monolog/Handler/SlackWebhookHandler.php000064400000007410151327705700021337 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog\Handler;

use WPvividMonolog\Formatter\FormatterInterface;
use WPvividMonolog\Logger;
use WPvividMonolog\Handler\Slack\SlackRecord;

/**
 * Sends notifications through Slack Webhooks
 *
 * @author Haralan Dobrev <hkdobrev@gmail.com>
 * @see    https://api.slack.com/incoming-webhooks
 */
class SlackWebhookHandler extends AbstractProcessingHandler
{
    /**
     * Slack Webhook token
     * @var string
     */
    private $webhookUrl;

    /**
     * Instance of the SlackRecord util class preparing data for Slack API.
     * @var SlackRecord
     */
    private $slackRecord;

    /**
     * @param  string      $webhookUrl             Slack Webhook URL
     * @param  string|null $channel                Slack channel (encoded ID or name)
     * @param  string|null $username               Name of a bot
     * @param  bool        $useAttachment          Whether the message should be added to Slack as attachment (plain text otherwise)
     * @param  string|null $iconEmoji              The emoji name to use (or null)
     * @param  bool        $useShortAttachment     Whether the the context/extra messages added to Slack as attachments are in a short style
     * @param  bool        $includeContextAndExtra Whether the attachment should include context and extra data
     * @param  int         $level                  The minimum logging level at which this handler will be triggered
     * @param  bool        $bubble                 Whether the messages that are handled can bubble up the stack or not
     * @param  array       $excludeFields          Dot separated list of fields to exclude from slack message. E.g. ['context.field1', 'extra.field2']
     */
    public function __construct($webhookUrl, $channel = null, $username = null, $useAttachment = true, $iconEmoji = null, $useShortAttachment = false, $includeContextAndExtra = false, $level = Logger::CRITICAL, $bubble = true, array $excludeFields = array())
    {
        parent::__construct($level, $bubble);

        $this->webhookUrl = $webhookUrl;

        $this->slackRecord = new SlackRecord(
            $channel,
            $username,
            $useAttachment,
            $iconEmoji,
            $useShortAttachment,
            $includeContextAndExtra,
            $excludeFields,
            $this->formatter
        );
    }

    public function getSlackRecord()
    {
        return $this->slackRecord;
    }

    public function getWebhookUrl()
    {
        return $this->webhookUrl;
    }

    /**
     * {@inheritdoc}
     *
     * @param array $record
     */
    protected function write(array $record)
    {
        $postData = $this->slackRecord->getSlackData($record);
        $postString = json_encode($postData);

        $ch = curl_init();
        $options = array(
            CURLOPT_URL => $this->webhookUrl,
            CURLOPT_POST => true,
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_HTTPHEADER => array('Content-type: application/json'),
            CURLOPT_POSTFIELDS => $postString
        );
        if (defined('CURLOPT_SAFE_UPLOAD')) {
            $options[CURLOPT_SAFE_UPLOAD] = true;
        }

        curl_setopt_array($ch, $options);

        Curl\Util::execute($ch);
    }

    public function setFormatter(FormatterInterface $formatter)
    {
        parent::setFormatter($formatter);
        $this->slackRecord->setFormatter($formatter);

        return $this;
    }

    public function getFormatter()
    {
        $formatter = parent::getFormatter();
        $this->slackRecord->setFormatter($formatter);

        return $formatter;
    }
}
vendor/monolog/monolog/src/Monolog/Handler/FilterHandler.php000064400000010524151327705700020210 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog\Handler;

use WPvividMonolog\Logger;

/**
 * Simple handler wrapper that filters records based on a list of levels
 *
 * It can be configured with an exact list of levels to allow, or a min/max level.
 *
 * @author Hennadiy Verkh
 * @author Jordi Boggiano <j.boggiano@seld.be>
 */
class FilterHandler extends AbstractHandler
{
    /**
     * Handler or factory callable($record, $this)
     *
     * @var callable|\Monolog\Handler\HandlerInterface
     */
    protected $handler;

    /**
     * Minimum level for logs that are passed to handler
     *
     * @var int[]
     */
    protected $acceptedLevels;

    /**
     * Whether the messages that are handled can bubble up the stack or not
     *
     * @var bool
     */
    protected $bubble;

    /**
     * @param callable|HandlerInterface $handler        Handler or factory callable($record, $this).
     * @param int|array                 $minLevelOrList A list of levels to accept or a minimum level if maxLevel is provided
     * @param int                       $maxLevel       Maximum level to accept, only used if $minLevelOrList is not an array
     * @param bool                      $bubble         Whether the messages that are handled can bubble up the stack or not
     */
    public function __construct($handler, $minLevelOrList = Logger::DEBUG, $maxLevel = Logger::EMERGENCY, $bubble = true)
    {
        $this->handler  = $handler;
        $this->bubble   = $bubble;
        $this->setAcceptedLevels($minLevelOrList, $maxLevel);

        if (!$this->handler instanceof HandlerInterface && !is_callable($this->handler)) {
            throw new \RuntimeException("The given handler (".json_encode($this->handler).") is not a callable nor a Monolog\Handler\HandlerInterface object");
        }
    }

    /**
     * @return array
     */
    public function getAcceptedLevels()
    {
        return array_flip($this->acceptedLevels);
    }

    /**
     * @param int|string|array $minLevelOrList A list of levels to accept or a minimum level or level name if maxLevel is provided
     * @param int|string       $maxLevel       Maximum level or level name to accept, only used if $minLevelOrList is not an array
     */
    public function setAcceptedLevels($minLevelOrList = Logger::DEBUG, $maxLevel = Logger::EMERGENCY)
    {
        if (is_array($minLevelOrList)) {
            $acceptedLevels = array_map('Monolog\Logger::toMonologLevel', $minLevelOrList);
        } else {
            $minLevelOrList = Logger::toMonologLevel($minLevelOrList);
            $maxLevel = Logger::toMonologLevel($maxLevel);
            $acceptedLevels = array_values(array_filter(Logger::getLevels(), function ($level) use ($minLevelOrList, $maxLevel) {
                return $level >= $minLevelOrList && $level <= $maxLevel;
            }));
        }
        $this->acceptedLevels = array_flip($acceptedLevels);
    }

    /**
     * {@inheritdoc}
     */
    public function isHandling(array $record)
    {
        return isset($this->acceptedLevels[$record['level']]);
    }

    /**
     * {@inheritdoc}
     */
    public function handle(array $record)
    {
        if (!$this->isHandling($record)) {
            return false;
        }

        // The same logic as in FingersCrossedHandler
        if (!$this->handler instanceof HandlerInterface) {
            $this->handler = call_user_func($this->handler, $record, $this);
            if (!$this->handler instanceof HandlerInterface) {
                throw new \RuntimeException("The factory callable should return a HandlerInterface");
            }
        }

        if ($this->processors) {
            foreach ($this->processors as $processor) {
                $record = call_user_func($processor, $record);
            }
        }

        $this->handler->handle($record);

        return false === $this->bubble;
    }

    /**
     * {@inheritdoc}
     */
    public function handleBatch(array $records)
    {
        $filtered = array();
        foreach ($records as $record) {
            if ($this->isHandling($record)) {
                $filtered[] = $record;
            }
        }

        $this->handler->handleBatch($filtered);
    }
}
vendor/monolog/monolog/src/Monolog/Handler/BufferHandler.php000064400000007152151327705700020177 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog\Handler;

use WPvividMonolog\Logger;
use WPvividMonolog\ResettableInterface;

/**
 * Buffers all records until closing the handler and then pass them as batch.
 *
 * This is useful for a MailHandler to send only one mail per request instead of
 * sending one per log message.
 *
 * @author Christophe Coevoet <stof@notk.org>
 */
class BufferHandler extends AbstractHandler
{
    protected $handler;
    protected $bufferSize = 0;
    protected $bufferLimit;
    protected $flushOnOverflow;
    protected $buffer = array();
    protected $initialized = false;

    /**
     * @param HandlerInterface $handler         Handler.
     * @param int              $bufferLimit     How many entries should be buffered at most, beyond that the oldest items are removed from the buffer.
     * @param int              $level           The minimum logging level at which this handler will be triggered
     * @param bool             $bubble          Whether the messages that are handled can bubble up the stack or not
     * @param bool             $flushOnOverflow If true, the buffer is flushed when the max size has been reached, by default oldest entries are discarded
     */
    public function __construct(HandlerInterface $handler, $bufferLimit = 0, $level = Logger::DEBUG, $bubble = true, $flushOnOverflow = false)
    {
        parent::__construct($level, $bubble);
        $this->handler = $handler;
        $this->bufferLimit = (int) $bufferLimit;
        $this->flushOnOverflow = $flushOnOverflow;
    }

    /**
     * {@inheritdoc}
     */
    public function handle(array $record)
    {
        if ($record['level'] < $this->level) {
            return false;
        }

        if (!$this->initialized) {
            // __destructor() doesn't get called on Fatal errors
            register_shutdown_function(array($this, 'close'));
            $this->initialized = true;
        }

        if ($this->bufferLimit > 0 && $this->bufferSize === $this->bufferLimit) {
            if ($this->flushOnOverflow) {
                $this->flush();
            } else {
                array_shift($this->buffer);
                $this->bufferSize--;
            }
        }

        if ($this->processors) {
            foreach ($this->processors as $processor) {
                $record = call_user_func($processor, $record);
            }
        }

        $this->buffer[] = $record;
        $this->bufferSize++;

        return false === $this->bubble;
    }

    public function flush()
    {
        if ($this->bufferSize === 0) {
            return;
        }

        $this->handler->handleBatch($this->buffer);
        $this->clear();
    }

    public function __destruct()
    {
        // suppress the parent behavior since we already have register_shutdown_function()
        // to call close(), and the reference contained there will prevent this from being
        // GC'd until the end of the request
    }

    /**
     * {@inheritdoc}
     */
    public function close()
    {
        $this->flush();
    }

    /**
     * Clears the buffer without flushing any messages down to the wrapped handler.
     */
    public function clear()
    {
        $this->bufferSize = 0;
        $this->buffer = array();
    }

    public function reset()
    {
        $this->flush();

        parent::reset();

        if ($this->handler instanceof ResettableInterface) {
            $this->handler->reset();
        }
    }
}
vendor/monolog/monolog/src/Monolog/Handler/ChromePHPHandler.php000064400000012774151327705700020561 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog\Handler;

use WPvividMonolog\Formatter\ChromePHPFormatter;
use WPvividMonolog\Logger;

/**
 * Handler sending logs to the ChromePHP extension (http://www.chromephp.com/)
 *
 * This also works out of the box with Firefox 43+
 *
 * @author Christophe Coevoet <stof@notk.org>
 */
class ChromePHPHandler extends AbstractProcessingHandler
{
    /**
     * Version of the extension
     */
    const VERSION = '4.0';

    /**
     * Header name
     */
    const HEADER_NAME = 'X-ChromeLogger-Data';

    /**
     * Regular expression to detect supported browsers (matches any Chrome, or Firefox 43+)
     */
    const USER_AGENT_REGEX = '{\b(?:Chrome/\d+(?:\.\d+)*|HeadlessChrome|Firefox/(?:4[3-9]|[5-9]\d|\d{3,})(?:\.\d)*)\b}';

    protected static $initialized = false;

    /**
     * Tracks whether we sent too much data
     *
     * Chrome limits the headers to 256KB, so when we sent 240KB we stop sending
     *
     * @var bool
     */
    protected static $overflowed = false;

    protected static $json = array(
        'version' => self::VERSION,
        'columns' => array('label', 'log', 'backtrace', 'type'),
        'rows' => array(),
    );

    protected static $sendHeaders = true;

    /**
     * @param int  $level  The minimum logging level at which this handler will be triggered
     * @param bool $bubble Whether the messages that are handled can bubble up the stack or not
     */
    public function __construct($level = Logger::DEBUG, $bubble = true)
    {
        parent::__construct($level, $bubble);
        if (!function_exists('json_encode')) {
            throw new \RuntimeException('PHP\'s json extension is required to use Monolog\'s ChromePHPHandler');
        }
    }

    /**
     * {@inheritdoc}
     */
    public function handleBatch(array $records)
    {
        $messages = array();

        foreach ($records as $record) {
            if ($record['level'] < $this->level) {
                continue;
            }
            $messages[] = $this->processRecord($record);
        }

        if (!empty($messages)) {
            $messages = $this->getFormatter()->formatBatch($messages);
            self::$json['rows'] = array_merge(self::$json['rows'], $messages);
            $this->send();
        }
    }

    /**
     * {@inheritDoc}
     */
    protected function getDefaultFormatter()
    {
        return new ChromePHPFormatter();
    }

    /**
     * Creates & sends header for a record
     *
     * @see sendHeader()
     * @see send()
     * @param array $record
     */
    protected function write(array $record)
    {
        self::$json['rows'][] = $record['formatted'];

        $this->send();
    }

    /**
     * Sends the log header
     *
     * @see sendHeader()
     */
    protected function send()
    {
        if (self::$overflowed || !self::$sendHeaders) {
            return;
        }

        if (!self::$initialized) {
            self::$initialized = true;

            self::$sendHeaders = $this->headersAccepted();
            if (!self::$sendHeaders) {
                return;
            }

            self::$json['request_uri'] = isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : '';
        }

        $json = @json_encode(self::$json);
        $data = base64_encode(utf8_encode($json));
        if (strlen($data) > 240 * 1024) {
            self::$overflowed = true;

            $record = array(
                'message' => 'Incomplete logs, chrome header size limit reached',
                'context' => array(),
                'level' => Logger::WARNING,
                'level_name' => Logger::getLevelName(Logger::WARNING),
                'channel' => 'monolog',
                'datetime' => new \DateTime(),
                'extra' => array(),
            );
            self::$json['rows'][count(self::$json['rows']) - 1] = $this->getFormatter()->format($record);
            $json = @json_encode(self::$json);
            $data = base64_encode(utf8_encode($json));
        }

        if (trim($data) !== '') {
            $this->sendHeader(self::HEADER_NAME, $data);
        }
    }

    /**
     * Send header string to the client
     *
     * @param string $header
     * @param string $content
     */
    protected function sendHeader($header, $content)
    {
        if (!headers_sent() && self::$sendHeaders) {
            header(sprintf('%s: %s', $header, $content));
        }
    }

    /**
     * Verifies if the headers are accepted by the current user agent
     *
     * @return bool
     */
    protected function headersAccepted()
    {
        if (empty($_SERVER['HTTP_USER_AGENT'])) {
            return false;
        }

        return preg_match(self::USER_AGENT_REGEX, $_SERVER['HTTP_USER_AGENT']);
    }

    /**
     * BC getter for the sendHeaders property that has been made static
     */
    public function __get($property)
    {
        if ('sendHeaders' !== $property) {
            throw new \InvalidArgumentException('Undefined property '.$property);
        }

        return static::$sendHeaders;
    }

    /**
     * BC setter for the sendHeaders property that has been made static
     */
    public function __set($property, $value)
    {
        if ('sendHeaders' !== $property) {
            throw new \InvalidArgumentException('Undefined property '.$property);
        }

        static::$sendHeaders = $value;
    }
}
vendor/monolog/monolog/src/Monolog/Handler/HipChatHandler.php000064400000025026151327705700020306 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog\Handler;

use WPvividMonolog\Logger;

/**
 * Sends notifications through the hipchat api to a hipchat room
 *
 * Notes:
 * API token - HipChat API token
 * Room      - HipChat Room Id or name, where messages are sent
 * Name      - Name used to send the message (from)
 * notify    - Should the message trigger a notification in the clients
 * version   - The API version to use (HipChatHandler::API_V1 | HipChatHandler::API_V2)
 *
 * @author Rafael Dohms <rafael@doh.ms>
 * @see    https://www.hipchat.com/docs/api
 */
class HipChatHandler extends SocketHandler
{
    /**
     * Use API version 1
     */
    const API_V1 = 'v1';

    /**
     * Use API version v2
     */
    const API_V2 = 'v2';

    /**
     * The maximum allowed length for the name used in the "from" field.
     */
    const MAXIMUM_NAME_LENGTH = 15;

    /**
     * The maximum allowed length for the message.
     */
    const MAXIMUM_MESSAGE_LENGTH = 9500;

    /**
     * @var string
     */
    private $token;

    /**
     * @var string
     */
    private $room;

    /**
     * @var string
     */
    private $name;

    /**
     * @var bool
     */
    private $notify;

    /**
     * @var string
     */
    private $format;

    /**
     * @var string
     */
    private $host;

    /**
     * @var string
     */
    private $version;

    /**
     * @param string $token   HipChat API Token
     * @param string $room    The room that should be alerted of the message (Id or Name)
     * @param string $name    Name used in the "from" field.
     * @param bool   $notify  Trigger a notification in clients or not
     * @param int    $level   The minimum logging level at which this handler will be triggered
     * @param bool   $bubble  Whether the messages that are handled can bubble up the stack or not
     * @param bool   $useSSL  Whether to connect via SSL.
     * @param string $format  The format of the messages (default to text, can be set to html if you have html in the messages)
     * @param string $host    The HipChat server hostname.
     * @param string $version The HipChat API version (default HipChatHandler::API_V1)
     */
    public function __construct($token, $room, $name = 'Monolog', $notify = false, $level = Logger::CRITICAL, $bubble = true, $useSSL = true, $format = 'text', $host = 'api.hipchat.com', $version = self::API_V1)
    {
        if ($version == self::API_V1 && !$this->validateStringLength($name, static::MAXIMUM_NAME_LENGTH)) {
            throw new \InvalidArgumentException('The supplied name is too long. HipChat\'s v1 API supports names up to 15 UTF-8 characters.');
        }

        $connectionString = $useSSL ? 'ssl://'.$host.':443' : $host.':80';
        parent::__construct($connectionString, $level, $bubble);

        $this->token = $token;
        $this->name = $name;
        $this->notify = $notify;
        $this->room = $room;
        $this->format = $format;
        $this->host = $host;
        $this->version = $version;
    }

    /**
     * {@inheritdoc}
     *
     * @param  array  $record
     * @return string
     */
    protected function generateDataStream($record)
    {
        $content = $this->buildContent($record);

        return $this->buildHeader($content) . $content;
    }

    /**
     * Builds the body of API call
     *
     * @param  array  $record
     * @return string
     */
    private function buildContent($record)
    {
        $dataArray = array(
            'notify' => $this->version == self::API_V1 ?
                ($this->notify ? 1 : 0) :
                ($this->notify ? 'true' : 'false'),
            'message' => $record['formatted'],
            'message_format' => $this->format,
            'color' => $this->getAlertColor($record['level']),
        );

        if (!$this->validateStringLength($dataArray['message'], static::MAXIMUM_MESSAGE_LENGTH)) {
            if (function_exists('mb_substr')) {
                $dataArray['message'] = mb_substr($dataArray['message'], 0, static::MAXIMUM_MESSAGE_LENGTH).' [truncated]';
            } else {
                $dataArray['message'] = substr($dataArray['message'], 0, static::MAXIMUM_MESSAGE_LENGTH).' [truncated]';
            }
        }

        // if we are using the legacy API then we need to send some additional information
        if ($this->version == self::API_V1) {
            $dataArray['room_id'] = $this->room;
        }

        // append the sender name if it is set
        // always append it if we use the v1 api (it is required in v1)
        if ($this->version == self::API_V1 || $this->name !== null) {
            $dataArray['from'] = (string) $this->name;
        }

        return http_build_query($dataArray);
    }

    /**
     * Builds the header of the API Call
     *
     * @param  string $content
     * @return string
     */
    private function buildHeader($content)
    {
        if ($this->version == self::API_V1) {
            $header = "POST /v1/rooms/message?format=json&auth_token={$this->token} HTTP/1.1\r\n";
        } else {
            // needed for rooms with special (spaces, etc) characters in the name
            $room = rawurlencode($this->room);
            $header = "POST /v2/room/{$room}/notification?auth_token={$this->token} HTTP/1.1\r\n";
        }

        $header .= "Host: {$this->host}\r\n";
        $header .= "Content-Type: application/x-www-form-urlencoded\r\n";
        $header .= "Content-Length: " . strlen($content) . "\r\n";
        $header .= "\r\n";

        return $header;
    }

    /**
     * Assigns a color to each level of log records.
     *
     * @param  int    $level
     * @return string
     */
    protected function getAlertColor($level)
    {
        switch (true) {
            case $level >= Logger::ERROR:
                return 'red';
            case $level >= Logger::WARNING:
                return 'yellow';
            case $level >= Logger::INFO:
                return 'green';
            case $level == Logger::DEBUG:
                return 'gray';
            default:
                return 'yellow';
        }
    }

    /**
     * {@inheritdoc}
     *
     * @param array $record
     */
    protected function write(array $record)
    {
        parent::write($record);
        $this->finalizeWrite();
    }

    /**
     * Finalizes the request by reading some bytes and then closing the socket
     *
     * If we do not read some but close the socket too early, hipchat sometimes
     * drops the request entirely.
     */
    protected function finalizeWrite()
    {
        $res = $this->getResource();
        if (is_resource($res)) {
            @fread($res, 2048);
        }
        $this->closeSocket();
    }

    /**
     * {@inheritdoc}
     */
    public function handleBatch(array $records)
    {
        if (count($records) == 0) {
            return true;
        }

        $batchRecords = $this->combineRecords($records);

        $handled = false;
        foreach ($batchRecords as $batchRecord) {
            if ($this->isHandling($batchRecord)) {
                $this->write($batchRecord);
                $handled = true;
            }
        }

        if (!$handled) {
            return false;
        }

        return false === $this->bubble;
    }

    /**
     * Combines multiple records into one. Error level of the combined record
     * will be the highest level from the given records. Datetime will be taken
     * from the first record.
     *
     * @param $records
     * @return array
     */
    private function combineRecords($records)
    {
        $batchRecord = null;
        $batchRecords = array();
        $messages = array();
        $formattedMessages = array();
        $level = 0;
        $levelName = null;
        $datetime = null;

        foreach ($records as $record) {
            $record = $this->processRecord($record);

            if ($record['level'] > $level) {
                $level = $record['level'];
                $levelName = $record['level_name'];
            }

            if (null === $datetime) {
                $datetime = $record['datetime'];
            }

            $messages[] = $record['message'];
            $messageStr = implode(PHP_EOL, $messages);
            $formattedMessages[] = $this->getFormatter()->format($record);
            $formattedMessageStr = implode('', $formattedMessages);

            $batchRecord = array(
                'message'   => $messageStr,
                'formatted' => $formattedMessageStr,
                'context'   => array(),
                'extra'     => array(),
            );

            if (!$this->validateStringLength($batchRecord['formatted'], static::MAXIMUM_MESSAGE_LENGTH)) {
                // Pop the last message and implode the remaining messages
                $lastMessage = array_pop($messages);
                $lastFormattedMessage = array_pop($formattedMessages);
                $batchRecord['message'] = implode(PHP_EOL, $messages);
                $batchRecord['formatted'] = implode('', $formattedMessages);

                $batchRecords[] = $batchRecord;
                $messages = array($lastMessage);
                $formattedMessages = array($lastFormattedMessage);

                $batchRecord = null;
            }
        }

        if (null !== $batchRecord) {
            $batchRecords[] = $batchRecord;
        }

        // Set the max level and datetime for all records
        foreach ($batchRecords as &$batchRecord) {
            $batchRecord = array_merge(
                $batchRecord,
                array(
                    'level'      => $level,
                    'level_name' => $levelName,
                    'datetime'   => $datetime,
                )
            );
        }

        return $batchRecords;
    }

    /**
     * Validates the length of a string.
     *
     * If the `mb_strlen()` function is available, it will use that, as HipChat
     * allows UTF-8 characters. Otherwise, it will fall back to `strlen()`.
     *
     * Note that this might cause false failures in the specific case of using
     * a valid name with less than 16 characters, but 16 or more bytes, on a
     * system where `mb_strlen()` is unavailable.
     *
     * @param string $str
     * @param int    $length
     *
     * @return bool
     */
    private function validateStringLength($str, $length)
    {
        if (function_exists('mb_strlen')) {
            return (mb_strlen($str) <= $length);
        }

        return (strlen($str) <= $length);
    }
}
vendor/monolog/monolog/src/Monolog/Handler/CouchDBHandler.php000064400000003666151327705700020243 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog\Handler;

use WPvividMonolog\Formatter\JsonFormatter;
use WPvividMonolog\Logger;

/**
 * CouchDB handler
 *
 * @author Markus Bachmann <markus.bachmann@bachi.biz>
 */
class CouchDBHandler extends AbstractProcessingHandler
{
    private $options;

    public function __construct(array $options = array(), $level = Logger::DEBUG, $bubble = true)
    {
        $this->options = array_merge(array(
            'host'     => 'localhost',
            'port'     => 5984,
            'dbname'   => 'logger',
            'username' => null,
            'password' => null,
        ), $options);

        parent::__construct($level, $bubble);
    }

    /**
     * {@inheritDoc}
     */
    protected function write(array $record)
    {
        $basicAuth = null;
        if ($this->options['username']) {
            $basicAuth = sprintf('%s:%s@', $this->options['username'], $this->options['password']);
        }

        $url = 'http://'.$basicAuth.$this->options['host'].':'.$this->options['port'].'/'.$this->options['dbname'];
        $context = stream_context_create(array(
            'http' => array(
                'method'        => 'POST',
                'content'       => $record['formatted'],
                'ignore_errors' => true,
                'max_redirects' => 0,
                'header'        => 'Content-type: application/json',
            ),
        ));

        if (false === @file_get_contents($url, null, $context)) {
            throw new \RuntimeException(sprintf('Could not connect to %s', $url));
        }
    }

    /**
     * {@inheritDoc}
     */
    protected function getDefaultFormatter()
    {
        return new JsonFormatter(JsonFormatter::BATCH_MODE_JSON, false);
    }
}
vendor/monolog/monolog/src/Monolog/Handler/CubeHandler.php000064400000011045151327705700017640 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog\Handler;

use WPvividMonolog\Logger;

/**
 * Logs to Cube.
 *
 * @link http://square.github.com/cube/
 * @author Wan Chen <kami@kamisama.me>
 */
class CubeHandler extends AbstractProcessingHandler
{
    private $udpConnection;
    private $httpConnection;
    private $scheme;
    private $host;
    private $port;
    private $acceptedSchemes = array('http', 'udp');

    /**
     * Create a Cube handler
     *
     * @throws \UnexpectedValueException when given url is not a valid url.
     *                                   A valid url must consist of three parts : protocol://host:port
     *                                   Only valid protocols used by Cube are http and udp
     */
    public function __construct($url, $level = Logger::DEBUG, $bubble = true)
    {
        $urlInfo = parse_url($url);

        if (!isset($urlInfo['scheme'], $urlInfo['host'], $urlInfo['port'])) {
            throw new \UnexpectedValueException('URL "'.$url.'" is not valid');
        }

        if (!in_array($urlInfo['scheme'], $this->acceptedSchemes)) {
            throw new \UnexpectedValueException(
                'Invalid protocol (' . $urlInfo['scheme']  . ').'
                . ' Valid options are ' . implode(', ', $this->acceptedSchemes));
        }

        $this->scheme = $urlInfo['scheme'];
        $this->host = $urlInfo['host'];
        $this->port = $urlInfo['port'];

        parent::__construct($level, $bubble);
    }

    /**
     * Establish a connection to an UDP socket
     *
     * @throws \LogicException           when unable to connect to the socket
     * @throws MissingExtensionException when there is no socket extension
     */
    protected function connectUdp()
    {
        if (!extension_loaded('sockets')) {
            throw new MissingExtensionException('The sockets extension is required to use udp URLs with the CubeHandler');
        }

        $this->udpConnection = socket_create(AF_INET, SOCK_DGRAM, 0);
        if (!$this->udpConnection) {
            throw new \LogicException('Unable to create a socket');
        }

        if (!socket_connect($this->udpConnection, $this->host, $this->port)) {
            throw new \LogicException('Unable to connect to the socket at ' . $this->host . ':' . $this->port);
        }
    }

    /**
     * Establish a connection to a http server
     * @throws \LogicException when no curl extension
     */
    protected function connectHttp()
    {
        if (!extension_loaded('curl')) {
            throw new \LogicException('The curl extension is needed to use http URLs with the CubeHandler');
        }

        $this->httpConnection = curl_init('http://'.$this->host.':'.$this->port.'/1.0/event/put');

        if (!$this->httpConnection) {
            throw new \LogicException('Unable to connect to ' . $this->host . ':' . $this->port);
        }

        curl_setopt($this->httpConnection, CURLOPT_CUSTOMREQUEST, "POST");
        curl_setopt($this->httpConnection, CURLOPT_RETURNTRANSFER, true);
    }

    /**
     * {@inheritdoc}
     */
    protected function write(array $record)
    {
        $date = $record['datetime'];

        $data = array('time' => $date->format('Y-m-d\TH:i:s.uO'));
        unset($record['datetime']);

        if (isset($record['context']['type'])) {
            $data['type'] = $record['context']['type'];
            unset($record['context']['type']);
        } else {
            $data['type'] = $record['channel'];
        }

        $data['data'] = $record['context'];
        $data['data']['level'] = $record['level'];

        if ($this->scheme === 'http') {
            $this->writeHttp(json_encode($data));
        } else {
            $this->writeUdp(json_encode($data));
        }
    }

    private function writeUdp($data)
    {
        if (!$this->udpConnection) {
            $this->connectUdp();
        }

        socket_send($this->udpConnection, $data, strlen($data), 0);
    }

    private function writeHttp($data)
    {
        if (!$this->httpConnection) {
            $this->connectHttp();
        }

        curl_setopt($this->httpConnection, CURLOPT_POSTFIELDS, '['.$data.']');
        curl_setopt($this->httpConnection, CURLOPT_HTTPHEADER, array(
            'Content-Type: application/json',
            'Content-Length: ' . strlen('['.$data.']'),
        ));

        Curl\Util::execute($this->httpConnection, 5, false);
    }
}
vendor/monolog/monolog/src/Monolog/Handler/LogglyHandler.php000064400000005120151327705700020214 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog\Handler;

use WPvividMonolog\Logger;
use WPvividMonolog\Formatter\LogglyFormatter;

/**
 * Sends errors to Loggly.
 *
 * @author Przemek Sobstel <przemek@sobstel.org>
 * @author Adam Pancutt <adam@pancutt.com>
 * @author Gregory Barchard <gregory@barchard.net>
 */
class LogglyHandler extends AbstractProcessingHandler
{
    const HOST = 'logs-01.loggly.com';
    const ENDPOINT_SINGLE = 'inputs';
    const ENDPOINT_BATCH = 'bulk';

    protected $token;

    protected $tag = array();

    public function __construct($token, $level = Logger::DEBUG, $bubble = true)
    {
        if (!extension_loaded('curl')) {
            throw new \LogicException('The curl extension is needed to use the LogglyHandler');
        }

        $this->token = $token;

        parent::__construct($level, $bubble);
    }

    public function setTag($tag)
    {
        $tag = !empty($tag) ? $tag : array();
        $this->tag = is_array($tag) ? $tag : array($tag);
    }

    public function addTag($tag)
    {
        if (!empty($tag)) {
            $tag = is_array($tag) ? $tag : array($tag);
            $this->tag = array_unique(array_merge($this->tag, $tag));
        }
    }

    protected function write(array $record)
    {
        $this->send($record["formatted"], self::ENDPOINT_SINGLE);
    }

    public function handleBatch(array $records)
    {
        $level = $this->level;

        $records = array_filter($records, function ($record) use ($level) {
            return ($record['level'] >= $level);
        });

        if ($records) {
            $this->send($this->getFormatter()->formatBatch($records), self::ENDPOINT_BATCH);
        }
    }

    protected function send($data, $endpoint)
    {
        $url = sprintf("https://%s/%s/%s/", self::HOST, $endpoint, $this->token);

        $headers = array('Content-Type: application/json');

        if (!empty($this->tag)) {
            $headers[] = 'X-LOGGLY-TAG: '.implode(',', $this->tag);
        }

        $ch = curl_init();

        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_POST, true);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
        curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

        Curl\Util::execute($ch);
    }

    protected function getDefaultFormatter()
    {
        return new LogglyFormatter();
    }
}
vendor/monolog/monolog/src/Monolog/Handler/TestHandler.php000064400000012035151327705700017701 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog\Handler;

/**
 * Used for testing purposes.
 *
 * It records all records and gives you access to them for verification.
 *
 * @author Jordi Boggiano <j.boggiano@seld.be>
 *
 * @method bool hasEmergency($record)
 * @method bool hasAlert($record)
 * @method bool hasCritical($record)
 * @method bool hasError($record)
 * @method bool hasWarning($record)
 * @method bool hasNotice($record)
 * @method bool hasInfo($record)
 * @method bool hasDebug($record)
 *
 * @method bool hasEmergencyRecords()
 * @method bool hasAlertRecords()
 * @method bool hasCriticalRecords()
 * @method bool hasErrorRecords()
 * @method bool hasWarningRecords()
 * @method bool hasNoticeRecords()
 * @method bool hasInfoRecords()
 * @method bool hasDebugRecords()
 *
 * @method bool hasEmergencyThatContains($message)
 * @method bool hasAlertThatContains($message)
 * @method bool hasCriticalThatContains($message)
 * @method bool hasErrorThatContains($message)
 * @method bool hasWarningThatContains($message)
 * @method bool hasNoticeThatContains($message)
 * @method bool hasInfoThatContains($message)
 * @method bool hasDebugThatContains($message)
 *
 * @method bool hasEmergencyThatMatches($message)
 * @method bool hasAlertThatMatches($message)
 * @method bool hasCriticalThatMatches($message)
 * @method bool hasErrorThatMatches($message)
 * @method bool hasWarningThatMatches($message)
 * @method bool hasNoticeThatMatches($message)
 * @method bool hasInfoThatMatches($message)
 * @method bool hasDebugThatMatches($message)
 *
 * @method bool hasEmergencyThatPasses($message)
 * @method bool hasAlertThatPasses($message)
 * @method bool hasCriticalThatPasses($message)
 * @method bool hasErrorThatPasses($message)
 * @method bool hasWarningThatPasses($message)
 * @method bool hasNoticeThatPasses($message)
 * @method bool hasInfoThatPasses($message)
 * @method bool hasDebugThatPasses($message)
 */
class TestHandler extends AbstractProcessingHandler
{
    protected $records = array();
    protected $recordsByLevel = array();

    public function getRecords()
    {
        return $this->records;
    }

    public function clear()
    {
        $this->records = array();
        $this->recordsByLevel = array();
    }

    public function hasRecords($level)
    {
        return isset($this->recordsByLevel[$level]);
    }

    /**
     * @param string|array $record Either a message string or an array containing message and optionally context keys that will be checked against all records
     * @param int          $level  Logger::LEVEL constant value
     */
    public function hasRecord($record, $level)
    {
        if (is_string($record)) {
            $record = array('message' => $record);
        }

        return $this->hasRecordThatPasses(function ($rec) use ($record) {
            if ($rec['message'] !== $record['message']) {
                return false;
            }
            if (isset($record['context']) && $rec['context'] !== $record['context']) {
                return false;
            }
            return true;
        }, $level);
    }

    public function hasRecordThatContains($message, $level)
    {
        return $this->hasRecordThatPasses(function ($rec) use ($message) {
            return strpos($rec['message'], $message) !== false;
        }, $level);
    }

    public function hasRecordThatMatches($regex, $level)
    {
        return $this->hasRecordThatPasses(function ($rec) use ($regex) {
            return preg_match($regex, $rec['message']) > 0;
        }, $level);
    }

    public function hasRecordThatPasses($predicate, $level)
    {
        if (!is_callable($predicate)) {
            throw new \InvalidArgumentException("Expected a callable for hasRecordThatSucceeds");
        }

        if (!isset($this->recordsByLevel[$level])) {
            return false;
        }

        foreach ($this->recordsByLevel[$level] as $i => $rec) {
            if (call_user_func($predicate, $rec, $i)) {
                return true;
            }
        }

        return false;
    }

    /**
     * {@inheritdoc}
     */
    protected function write(array $record)
    {
        $this->recordsByLevel[$record['level']][] = $record;
        $this->records[] = $record;
    }

    public function __call($method, $args)
    {
        if (preg_match('/(.*)(Debug|Info|Notice|Warning|Error|Critical|Alert|Emergency)(.*)/', $method, $matches) > 0) {
            $genericMethod = $matches[1] . ('Records' !== $matches[3] ? 'Record' : '') . $matches[3];
            $level = constant('WPvividMonolog\Logger::' . strtoupper($matches[2]));
            if (method_exists($this, $genericMethod)) {
                $args[] = $level;

                return call_user_func_array(array($this, $genericMethod), $args);
            }
        }

        throw new \BadMethodCallException('Call to undefined method ' . get_class($this) . '::' . $method . '()');
    }
}
vendor/monolog/monolog/src/Monolog/Handler/RavenHandler.php000064400000015760151327705700020045 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog\Handler;

use WPvividMonolog\Formatter\LineFormatter;
use WPvividMonolog\Formatter\FormatterInterface;
use WPvividMonolog\Logger;
use Raven_Client;

/**
 * Handler to send messages to a Sentry (https://github.com/getsentry/sentry) server
 * using sentry-php (https://github.com/getsentry/sentry-php)
 *
 * @author Marc Abramowitz <marc@marc-abramowitz.com>
 */
class RavenHandler extends AbstractProcessingHandler
{
    /**
     * Translates Monolog log levels to Raven log levels.
     */
    protected $logLevels = array(
        Logger::DEBUG     => Raven_Client::DEBUG,
        Logger::INFO      => Raven_Client::INFO,
        Logger::NOTICE    => Raven_Client::INFO,
        Logger::WARNING   => Raven_Client::WARNING,
        Logger::ERROR     => Raven_Client::ERROR,
        Logger::CRITICAL  => Raven_Client::FATAL,
        Logger::ALERT     => Raven_Client::FATAL,
        Logger::EMERGENCY => Raven_Client::FATAL,
    );

    /**
     * @var string should represent the current version of the calling
     *             software. Can be any string (git commit, version number)
     */
    protected $release;

    /**
     * @var Raven_Client the client object that sends the message to the server
     */
    protected $ravenClient;

    /**
     * @var LineFormatter The formatter to use for the logs generated via handleBatch()
     */
    protected $batchFormatter;

    /**
     * @param Raven_Client $ravenClient
     * @param int          $level       The minimum logging level at which this handler will be triggered
     * @param bool         $bubble      Whether the messages that are handled can bubble up the stack or not
     */
    public function __construct(Raven_Client $ravenClient, $level = Logger::DEBUG, $bubble = true)
    {
        parent::__construct($level, $bubble);

        $this->ravenClient = $ravenClient;
    }

    /**
     * {@inheritdoc}
     */
    public function handleBatch(array $records)
    {
        $level = $this->level;

        // filter records based on their level
        $records = array_filter($records, function ($record) use ($level) {
            return $record['level'] >= $level;
        });

        if (!$records) {
            return;
        }

        // the record with the highest severity is the "main" one
        $record = array_reduce($records, function ($highest, $record) {
            if ($record['level'] > $highest['level']) {
                return $record;
            }

            return $highest;
        });

        // the other ones are added as a context item
        $logs = array();
        foreach ($records as $r) {
            $logs[] = $this->processRecord($r);
        }

        if ($logs) {
            $record['context']['logs'] = (string) $this->getBatchFormatter()->formatBatch($logs);
        }

        $this->handle($record);
    }

    /**
     * Sets the formatter for the logs generated by handleBatch().
     *
     * @param FormatterInterface $formatter
     */
    public function setBatchFormatter(FormatterInterface $formatter)
    {
        $this->batchFormatter = $formatter;
    }

    /**
     * Gets the formatter for the logs generated by handleBatch().
     *
     * @return FormatterInterface
     */
    public function getBatchFormatter()
    {
        if (!$this->batchFormatter) {
            $this->batchFormatter = $this->getDefaultBatchFormatter();
        }

        return $this->batchFormatter;
    }

    /**
     * {@inheritdoc}
     */
    protected function write(array $record)
    {
        $previousUserContext = false;
        $options = array();
        $options['level'] = $this->logLevels[$record['level']];
        $options['tags'] = array();
        if (!empty($record['extra']['tags'])) {
            $options['tags'] = array_merge($options['tags'], $record['extra']['tags']);
            unset($record['extra']['tags']);
        }
        if (!empty($record['context']['tags'])) {
            $options['tags'] = array_merge($options['tags'], $record['context']['tags']);
            unset($record['context']['tags']);
        }
        if (!empty($record['context']['fingerprint'])) {
            $options['fingerprint'] = $record['context']['fingerprint'];
            unset($record['context']['fingerprint']);
        }
        if (!empty($record['context']['logger'])) {
            $options['logger'] = $record['context']['logger'];
            unset($record['context']['logger']);
        } else {
            $options['logger'] = $record['channel'];
        }
        foreach ($this->getExtraParameters() as $key) {
            foreach (array('extra', 'context') as $source) {
                if (!empty($record[$source][$key])) {
                    $options[$key] = $record[$source][$key];
                    unset($record[$source][$key]);
                }
            }
        }
        if (!empty($record['context'])) {
            $options['extra']['context'] = $record['context'];
            if (!empty($record['context']['user'])) {
                $previousUserContext = $this->ravenClient->context->user;
                $this->ravenClient->user_context($record['context']['user']);
                unset($options['extra']['context']['user']);
            }
        }
        if (!empty($record['extra'])) {
            $options['extra']['extra'] = $record['extra'];
        }

        if (!empty($this->release) && !isset($options['release'])) {
            $options['release'] = $this->release;
        }

        if (isset($record['context']['exception']) && ($record['context']['exception'] instanceof \Exception || (PHP_VERSION_ID >= 70000 && $record['context']['exception'] instanceof \Throwable))) {
            $options['message'] = $record['formatted'];
            $this->ravenClient->captureException($record['context']['exception'], $options);
        } else {
            $this->ravenClient->captureMessage($record['formatted'], array(), $options);
        }

        if ($previousUserContext !== false) {
            $this->ravenClient->user_context($previousUserContext);
        }
    }

    /**
     * {@inheritDoc}
     */
    protected function getDefaultFormatter()
    {
        return new LineFormatter('[%channel%] %message%');
    }

    /**
     * Gets the default formatter for the logs generated by handleBatch().
     *
     * @return FormatterInterface
     */
    protected function getDefaultBatchFormatter()
    {
        return new LineFormatter();
    }

    /**
     * Gets extra parameters supported by Raven that can be found in "extra" and "context"
     *
     * @return array
     */
    protected function getExtraParameters()
    {
        return array('contexts', 'checksum', 'release', 'event_id');
    }

    /**
     * @param string $value
     * @return self
     */
    public function setRelease($value)
    {
        $this->release = $value;

        return $this;
    }
}
vendor/monolog/monolog/src/Monolog/Handler/SyslogUdpHandler.php000064400000005043151327705700020714 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog\Handler;

use WPvividMonolog\Logger;
use WPvividMonolog\Handler\SyslogUdp\UdpSocket;

/**
 * A Handler for logging to a remote syslogd server.
 *
 * @author Jesper Skovgaard Nielsen <nulpunkt@gmail.com>
 */
class SyslogUdpHandler extends AbstractSyslogHandler
{
    protected $socket;
    protected $ident;

    /**
     * @param string $host
     * @param int    $port
     * @param mixed  $facility
     * @param int    $level    The minimum logging level at which this handler will be triggered
     * @param bool   $bubble   Whether the messages that are handled can bubble up the stack or not
     * @param string $ident    Program name or tag for each log message.
     */
    public function __construct($host, $port = 514, $facility = LOG_USER, $level = Logger::DEBUG, $bubble = true, $ident = 'php')
    {
        parent::__construct($facility, $level, $bubble);

        $this->ident = $ident;

        $this->socket = new UdpSocket($host, $port ?: 514);
    }

    protected function write(array $record)
    {
        $lines = $this->splitMessageIntoLines($record['formatted']);

        $header = $this->makeCommonSyslogHeader($this->logLevels[$record['level']]);

        foreach ($lines as $line) {
            $this->socket->write($line, $header);
        }
    }

    public function close()
    {
        $this->socket->close();
    }

    private function splitMessageIntoLines($message)
    {
        if (is_array($message)) {
            $message = implode("\n", $message);
        }

        return preg_split('/$\R?^/m', $message, -1, PREG_SPLIT_NO_EMPTY);
    }

    /**
     * Make common syslog header (see rfc5424)
     */
    protected function makeCommonSyslogHeader($severity)
    {
        $priority = $severity + $this->facility;

        if (!$pid = getmypid()) {
            $pid = '-';
        }

        if (!$hostname = gethostname()) {
            $hostname = '-';
        }

        return "<$priority>1 " .
            $this->getDateTime() . " " .
            $hostname . " " .
            $this->ident . " " .
            $pid . " - - ";
    }

    protected function getDateTime()
    {
        return date(\DateTime::RFC3339);
    }

    /**
     * Inject your own socket, mainly used for testing
     */
    public function setSocket($socket)
    {
        $this->socket = $socket;
    }
}
vendor/monolog/monolog/src/Monolog/Handler/RotatingFileHandler.php000064400000013422151327705700021352 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog\Handler;

use WPvividMonolog\Logger;

/**
 * Stores logs to files that are rotated every day and a limited number of files are kept.
 *
 * This rotation is only intended to be used as a workaround. Using logrotate to
 * handle the rotation is strongly encouraged when you can use it.
 *
 * @author Christophe Coevoet <stof@notk.org>
 * @author Jordi Boggiano <j.boggiano@seld.be>
 */
class RotatingFileHandler extends StreamHandler
{
    const FILE_PER_DAY = 'Y-m-d';
    const FILE_PER_MONTH = 'Y-m';
    const FILE_PER_YEAR = 'Y';

    protected $filename;
    protected $maxFiles;
    protected $mustRotate;
    protected $nextRotation;
    protected $filenameFormat;
    protected $dateFormat;

    /**
     * @param string   $filename
     * @param int      $maxFiles       The maximal amount of files to keep (0 means unlimited)
     * @param int      $level          The minimum logging level at which this handler will be triggered
     * @param bool     $bubble         Whether the messages that are handled can bubble up the stack or not
     * @param int|null $filePermission Optional file permissions (default (0644) are only for owner read/write)
     * @param bool     $useLocking     Try to lock log file before doing any writes
     */
    public function __construct($filename, $maxFiles = 0, $level = Logger::DEBUG, $bubble = true, $filePermission = null, $useLocking = false)
    {
        $this->filename = $filename;
        $this->maxFiles = (int) $maxFiles;
        $this->nextRotation = new \DateTime('tomorrow');
        $this->filenameFormat = '{filename}-{date}';
        $this->dateFormat = 'Y-m-d';

        parent::__construct($this->getTimedFilename(), $level, $bubble, $filePermission, $useLocking);
    }

    /**
     * {@inheritdoc}
     */
    public function close()
    {
        parent::close();

        if (true === $this->mustRotate) {
            $this->rotate();
        }
    }

    /**
     * {@inheritdoc}
     */
    public function reset()
    {
        parent::reset();

        if (true === $this->mustRotate) {
            $this->rotate();
        }
    }

    public function setFilenameFormat($filenameFormat, $dateFormat)
    {
        if (!preg_match('{^Y(([/_.-]?m)([/_.-]?d)?)?$}', $dateFormat)) {
            trigger_error(
                'Invalid date format - format must be one of '.
                'RotatingFileHandler::FILE_PER_DAY ("Y-m-d"), RotatingFileHandler::FILE_PER_MONTH ("Y-m") '.
                'or RotatingFileHandler::FILE_PER_YEAR ("Y"), or you can set one of the '.
                'date formats using slashes, underscores and/or dots instead of dashes.',
                E_USER_DEPRECATED
            );
        }
        if (substr_count($filenameFormat, '{date}') === 0) {
            trigger_error(
                'Invalid filename format - format should contain at least `{date}`, because otherwise rotating is impossible.',
                E_USER_DEPRECATED
            );
        }
        $this->filenameFormat = $filenameFormat;
        $this->dateFormat = $dateFormat;
        $this->url = $this->getTimedFilename();
        $this->close();
    }

    /**
     * {@inheritdoc}
     */
    protected function write(array $record)
    {
        // on the first record written, if the log is new, we should rotate (once per day)
        if (null === $this->mustRotate) {
            $this->mustRotate = !file_exists($this->url);
        }

        if ($this->nextRotation < $record['datetime']) {
            $this->mustRotate = true;
            $this->close();
        }

        parent::write($record);
    }

    /**
     * Rotates the files.
     */
    protected function rotate()
    {
        // update filename
        $this->url = $this->getTimedFilename();
        $this->nextRotation = new \DateTime('tomorrow');

        // skip GC of old logs if files are unlimited
        if (0 === $this->maxFiles) {
            return;
        }

        $logFiles = glob($this->getGlobPattern());
        if ($this->maxFiles >= count($logFiles)) {
            // no files to remove
            return;
        }

        // Sorting the files by name to remove the older ones
        usort($logFiles, function ($a, $b) {
            return strcmp($b, $a);
        });

        foreach (array_slice($logFiles, $this->maxFiles) as $file) {
            if (is_writable($file)) {
                // suppress errors here as unlink() might fail if two processes
                // are cleaning up/rotating at the same time
                set_error_handler(function ($errno, $errstr, $errfile, $errline) {});
                unlink($file);
                restore_error_handler();
            }
        }

        $this->mustRotate = false;
    }

    protected function getTimedFilename()
    {
        $fileInfo = pathinfo($this->filename);
        $timedFilename = str_replace(
            array('{filename}', '{date}'),
            array($fileInfo['filename'], date($this->dateFormat)),
            $fileInfo['dirname'] . '/' . $this->filenameFormat
        );

        if (!empty($fileInfo['extension'])) {
            $timedFilename .= '.'.$fileInfo['extension'];
        }

        return $timedFilename;
    }

    protected function getGlobPattern()
    {
        $fileInfo = pathinfo($this->filename);
        $glob = str_replace(
            array('{filename}', '{date}'),
            array($fileInfo['filename'], '[0-9][0-9][0-9][0-9]*'),
            $fileInfo['dirname'] . '/' . $this->filenameFormat
        );
        if (!empty($fileInfo['extension'])) {
            $glob .= '.'.$fileInfo['extension'];
        }

        return $glob;
    }
}
vendor/monolog/monolog/src/Monolog/Handler/HandlerInterface.php000064400000005055151327705700020666 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog\Handler;

use WPvividMonolog\Formatter\FormatterInterface;

/**
 * Interface that all Monolog Handlers must implement
 *
 * @author Jordi Boggiano <j.boggiano@seld.be>
 */
interface HandlerInterface
{
    /**
     * Checks whether the given record will be handled by this handler.
     *
     * This is mostly done for performance reasons, to avoid calling processors for nothing.
     *
     * Handlers should still check the record levels within handle(), returning false in isHandling()
     * is no guarantee that handle() will not be called, and isHandling() might not be called
     * for a given record.
     *
     * @param array $record Partial log record containing only a level key
     *
     * @return bool
     */
    public function isHandling(array $record);

    /**
     * Handles a record.
     *
     * All records may be passed to this method, and the handler should discard
     * those that it does not want to handle.
     *
     * The return value of this function controls the bubbling process of the handler stack.
     * Unless the bubbling is interrupted (by returning true), the Logger class will keep on
     * calling further handlers in the stack with a given log record.
     *
     * @param  array   $record The record to handle
     * @return bool true means that this handler handled the record, and that bubbling is not permitted.
     *                        false means the record was either not processed or that this handler allows bubbling.
     */
    public function handle(array $record);

    /**
     * Handles a set of records at once.
     *
     * @param array $records The records to handle (an array of record arrays)
     */
    public function handleBatch(array $records);

    /**
     * Adds a processor in the stack.
     *
     * @param  callable $callback
     * @return self
     */
    public function pushProcessor($callback);

    /**
     * Removes the processor on top of the stack and returns it.
     *
     * @return callable
     */
    public function popProcessor();

    /**
     * Sets the formatter.
     *
     * @param  FormatterInterface $formatter
     * @return self
     */
    public function setFormatter(FormatterInterface $formatter);

    /**
     * Gets the formatter.
     *
     * @return FormatterInterface
     */
    public function getFormatter();
}
vendor/monolog/monolog/src/Monolog/Handler/SlackbotHandler.php000064400000003773151327705700020535 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog\Handler;

use WPvividMonolog\Logger;

/**
 * Sends notifications through Slack's Slackbot
 *
 * @author Haralan Dobrev <hkdobrev@gmail.com>
 * @see    https://slack.com/apps/A0F81R8ET-slackbot
 */
class SlackbotHandler extends AbstractProcessingHandler
{
    /**
     * The slug of the Slack team
     * @var string
     */
    private $slackTeam;

    /**
     * Slackbot token
     * @var string
     */
    private $token;

    /**
     * Slack channel name
     * @var string
     */
    private $channel;

    /**
     * @param  string $slackTeam Slack team slug
     * @param  string $token     Slackbot token
     * @param  string $channel   Slack channel (encoded ID or name)
     * @param  int    $level     The minimum logging level at which this handler will be triggered
     * @param  bool   $bubble    Whether the messages that are handled can bubble up the stack or not
     */
    public function __construct($slackTeam, $token, $channel, $level = Logger::CRITICAL, $bubble = true)
    {
        parent::__construct($level, $bubble);

        $this->slackTeam = $slackTeam;
        $this->token = $token;
        $this->channel = $channel;
    }

    /**
     * {@inheritdoc}
     *
     * @param array $record
     */
    protected function write(array $record)
    {
        $slackbotUrl = sprintf(
            'https://%s.slack.com/services/hooks/slackbot?token=%s&channel=%s',
            $this->slackTeam,
            $this->token,
            $this->channel
        );

        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $slackbotUrl);
        curl_setopt($ch, CURLOPT_POST, true);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $record['message']);

        Curl\Util::execute($ch);
    }
}
vendor/monolog/monolog/src/Monolog/Handler/MongoDBHandler.php000064400000003133151327705700020246 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog\Handler;

use WPvividMonolog\Logger;
use WPvividMonolog\Formatter\NormalizerFormatter;

/**
 * Logs to a MongoDB database.
 *
 * usage example:
 *
 *   $log = new Logger('application');
 *   $mongodb = new MongoDBHandler(new \Mongo("mongodb://localhost:27017"), "logs", "prod");
 *   $log->pushHandler($mongodb);
 *
 * @author Thomas Tourlourat <thomas@tourlourat.com>
 */
class MongoDBHandler extends AbstractProcessingHandler
{
    protected $mongoCollection;

    public function __construct($mongo, $database, $collection, $level = Logger::DEBUG, $bubble = true)
    {
        if (!($mongo instanceof \MongoClient || $mongo instanceof \Mongo || $mongo instanceof \MongoDB\Client)) {
            throw new \InvalidArgumentException('MongoClient, Mongo or MongoDB\Client instance required');
        }

        $this->mongoCollection = $mongo->selectCollection($database, $collection);

        parent::__construct($level, $bubble);
    }

    protected function write(array $record)
    {
        if ($this->mongoCollection instanceof \MongoDB\Collection) {
            $this->mongoCollection->insertOne($record["formatted"]);
        } else {
            $this->mongoCollection->save($record["formatted"]);
        }
    }

    /**
     * {@inheritDoc}
     */
    protected function getDefaultFormatter()
    {
        return new NormalizerFormatter();
    }
}
vendor/monolog/monolog/src/Monolog/Handler/WhatFailureGroupHandler.php000064400000003425151327705700022215 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog\Handler;

/**
 * Forwards records to multiple handlers suppressing failures of each handler
 * and continuing through to give every handler a chance to succeed.
 *
 * @author Craig D'Amelio <craig@damelio.ca>
 */
class WhatFailureGroupHandler extends GroupHandler
{
    /**
     * {@inheritdoc}
     */
    public function handle(array $record)
    {
        if ($this->processors) {
            foreach ($this->processors as $processor) {
                $record = call_user_func($processor, $record);
            }
        }

        foreach ($this->handlers as $handler) {
            try {
                $handler->handle($record);
            } catch (\Exception $e) {
                // What failure?
            } catch (\Throwable $e) {
                // What failure?
            }
        }

        return false === $this->bubble;
    }

    /**
     * {@inheritdoc}
     */
    public function handleBatch(array $records)
    {
        if ($this->processors) {
            $processed = array();
            foreach ($records as $record) {
                foreach ($this->processors as $processor) {
                    $processed[] = call_user_func($processor, $record);
                }
            }
            $records = $processed;
        }

        foreach ($this->handlers as $handler) {
            try {
                $handler->handleBatch($records);
            } catch (\Exception $e) {
                // What failure?
            } catch (\Throwable $e) {
                // What failure?
            }
        }
    }
}
vendor/monolog/monolog/src/Monolog/Handler/Slack/SlackRecord.php000064400000020105151327705700020712 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog\Handler\Slack;

use WPvividMonolog\Logger;
use WPvividMonolog\Formatter\NormalizerFormatter;
use WPvividMonolog\Formatter\FormatterInterface;

/**
 * Slack record utility helping to log to Slack webhooks or API.
 *
 * @author Greg Kedzierski <greg@gregkedzierski.com>
 * @author Haralan Dobrev <hkdobrev@gmail.com>
 * @see    https://api.slack.com/incoming-webhooks
 * @see    https://api.slack.com/docs/message-attachments
 */
class SlackRecord
{
    const COLOR_DANGER = 'danger';

    const COLOR_WARNING = 'warning';

    const COLOR_GOOD = 'good';

    const COLOR_DEFAULT = '#e3e4e6';

    /**
     * Slack channel (encoded ID or name)
     * @var string|null
     */
    private $channel;

    /**
     * Name of a bot
     * @var string|null
     */
    private $username;

    /**
     * User icon e.g. 'ghost', 'http://example.com/user.png'
     * @var string
     */
    private $userIcon;

    /**
     * Whether the message should be added to Slack as attachment (plain text otherwise)
     * @var bool
     */
    private $useAttachment;

    /**
     * Whether the the context/extra messages added to Slack as attachments are in a short style
     * @var bool
     */
    private $useShortAttachment;

    /**
     * Whether the attachment should include context and extra data
     * @var bool
     */
    private $includeContextAndExtra;

    /**
     * Dot separated list of fields to exclude from slack message. E.g. ['context.field1', 'extra.field2']
     * @var array
     */
    private $excludeFields;

    /**
     * @var FormatterInterface
     */
    private $formatter;

    /**
     * @var NormalizerFormatter
     */
    private $normalizerFormatter;

    public function __construct($channel = null, $username = null, $useAttachment = true, $userIcon = null, $useShortAttachment = false, $includeContextAndExtra = false, array $excludeFields = array(), FormatterInterface $formatter = null)
    {
        $this->channel = $channel;
        $this->username = $username;
        $this->userIcon = trim($userIcon, ':');
        $this->useAttachment = $useAttachment;
        $this->useShortAttachment = $useShortAttachment;
        $this->includeContextAndExtra = $includeContextAndExtra;
        $this->excludeFields = $excludeFields;
        $this->formatter = $formatter;

        if ($this->includeContextAndExtra) {
            $this->normalizerFormatter = new NormalizerFormatter();
        }
    }

    public function getSlackData(array $record)
    {
        $dataArray = array();
        $record = $this->excludeFields($record);

        if ($this->username) {
            $dataArray['username'] = $this->username;
        }

        if ($this->channel) {
            $dataArray['channel'] = $this->channel;
        }

        if ($this->formatter && !$this->useAttachment) {
            $message = $this->formatter->format($record);
        } else {
            $message = $record['message'];
        }

        if ($this->useAttachment) {
            $attachment = array(
                'fallback'  => $message,
                'text'      => $message,
                'color'     => $this->getAttachmentColor($record['level']),
                'fields'    => array(),
                'mrkdwn_in' => array('fields'),
                'ts'        => $record['datetime']->getTimestamp()
            );

            if ($this->useShortAttachment) {
                $attachment['title'] = $record['level_name'];
            } else {
                $attachment['title'] = 'Message';
                $attachment['fields'][] = $this->generateAttachmentField('Level', $record['level_name']);
            }


            if ($this->includeContextAndExtra) {
                foreach (array('extra', 'context') as $key) {
                    if (empty($record[$key])) {
                        continue;
                    }

                    if ($this->useShortAttachment) {
                        $attachment['fields'][] = $this->generateAttachmentField(
                            $key,
                            $record[$key]
                        );
                    } else {
                        // Add all extra fields as individual fields in attachment
                        $attachment['fields'] = array_merge(
                            $attachment['fields'],
                            $this->generateAttachmentFields($record[$key])
                        );
                    }
                }
            }

            $dataArray['attachments'] = array($attachment);
        } else {
            $dataArray['text'] = $message;
        }

        if ($this->userIcon) {
            if (filter_var($this->userIcon, FILTER_VALIDATE_URL)) {
                $dataArray['icon_url'] = $this->userIcon;
            } else {
                $dataArray['icon_emoji'] = ":{$this->userIcon}:";
            }
        }

        return $dataArray;
    }

    /**
     * Returned a Slack message attachment color associated with
     * provided level.
     *
     * @param  int    $level
     * @return string
     */
    public function getAttachmentColor($level)
    {
        switch (true) {
            case $level >= Logger::ERROR:
                return self::COLOR_DANGER;
            case $level >= Logger::WARNING:
                return self::COLOR_WARNING;
            case $level >= Logger::INFO:
                return self::COLOR_GOOD;
            default:
                return self::COLOR_DEFAULT;
        }
    }

    /**
     * Stringifies an array of key/value pairs to be used in attachment fields
     *
     * @param array $fields
     *
     * @return string
     */
    public function stringify($fields)
    {
        $normalized = $this->normalizerFormatter->format($fields);
        $prettyPrintFlag = defined('JSON_PRETTY_PRINT') ? JSON_PRETTY_PRINT : 128;

        $hasSecondDimension = count(array_filter($normalized, 'is_array'));
        $hasNonNumericKeys = !count(array_filter(array_keys($normalized), 'is_numeric'));

        return $hasSecondDimension || $hasNonNumericKeys
            ? json_encode($normalized, $prettyPrintFlag)
            : json_encode($normalized);
    }

    /**
     * Sets the formatter
     *
     * @param FormatterInterface $formatter
     */
    public function setFormatter(FormatterInterface $formatter)
    {
        $this->formatter = $formatter;
    }

    /**
     * Generates attachment field
     *
     * @param string       $title
     * @param string|array $value
     *
     * @return array
     */
    private function generateAttachmentField($title, $value)
    {
        $value = is_array($value)
            ? sprintf('```%s```', $this->stringify($value))
            : $value;

        return array(
            'title' => ucfirst($title),
            'value' => $value,
            'short' => false
        );
    }

    /**
     * Generates a collection of attachment fields from array
     *
     * @param array $data
     *
     * @return array
     */
    private function generateAttachmentFields(array $data)
    {
        $fields = array();
        foreach ($this->normalizerFormatter->format($data) as $key => $value) {
            $fields[] = $this->generateAttachmentField($key, $value);
        }

        return $fields;
    }

    /**
     * Get a copy of record with fields excluded according to $this->excludeFields
     *
     * @param array $record
     *
     * @return array
     */
    private function excludeFields(array $record)
    {
        foreach ($this->excludeFields as $field) {
            $keys = explode('.', $field);
            $node = &$record;
            $lastKey = end($keys);
            foreach ($keys as $key) {
                if (!isset($node[$key])) {
                    break;
                }
                if ($lastKey === $key) {
                    unset($node[$key]);
                    break;
                }
                $node = &$node[$key];
            }
        }

        return $record;
    }
}
vendor/monolog/monolog/src/Monolog/Handler/PsrHandler.php000064400000002656151327705700017536 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog\Handler;

use WPvividMonolog\Logger;
use WPvividPsr\Log\LoggerInterface;

/**
 * Proxies log messages to an existing PSR-3 compliant logger.
 *
 * @author Michael Moussa <michael.moussa@gmail.com>
 */
class PsrHandler extends AbstractHandler
{
    /**
     * PSR-3 compliant logger
     *
     * @var LoggerInterface
     */
    protected $logger;

    /**
     * @param LoggerInterface $logger The underlying PSR-3 compliant logger to which messages will be proxied
     * @param int             $level  The minimum logging level at which this handler will be triggered
     * @param bool            $bubble Whether the messages that are handled can bubble up the stack or not
     */
    public function __construct(LoggerInterface $logger, $level = Logger::DEBUG, $bubble = true)
    {
        parent::__construct($level, $bubble);

        $this->logger = $logger;
    }

    /**
     * {@inheritDoc}
     */
    public function handle(array $record)
    {
        if (!$this->isHandling($record)) {
            return false;
        }

        $this->logger->log(strtolower($record['level_name']), $record['message'], $record['context']);

        return false === $this->bubble;
    }
}
vendor/monolog/monolog/src/Monolog/Handler/AmqpHandler.php000064400000007457151327705700017674 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog\Handler;

use WPvividMonolog\Logger;
use WPvividMonolog\Formatter\JsonFormatter;
use PhpAmqpLib\Message\AMQPMessage;
use PhpAmqpLib\Channel\AMQPChannel;
use AMQPExchange;

class AmqpHandler extends AbstractProcessingHandler
{
    /**
     * @var AMQPExchange|AMQPChannel $exchange
     */
    protected $exchange;

    /**
     * @var string
     */
    protected $exchangeName;

    /**
     * @param AMQPExchange|AMQPChannel $exchange     AMQPExchange (php AMQP ext) or PHP AMQP lib channel, ready for use
     * @param string                   $exchangeName
     * @param int                      $level
     * @param bool                     $bubble       Whether the messages that are handled can bubble up the stack or not
     */
    public function __construct($exchange, $exchangeName = 'log', $level = Logger::DEBUG, $bubble = true)
    {
        if ($exchange instanceof AMQPExchange) {
            $exchange->setName($exchangeName);
        } elseif ($exchange instanceof AMQPChannel) {
            $this->exchangeName = $exchangeName;
        } else {
            throw new \InvalidArgumentException('PhpAmqpLib\Channel\AMQPChannel or AMQPExchange instance required');
        }
        $this->exchange = $exchange;

        parent::__construct($level, $bubble);
    }

    /**
     * {@inheritDoc}
     */
    protected function write(array $record)
    {
        $data = $record["formatted"];
        $routingKey = $this->getRoutingKey($record);

        if ($this->exchange instanceof AMQPExchange) {
            $this->exchange->publish(
                $data,
                $routingKey,
                0,
                array(
                    'delivery_mode' => 2,
                    'content_type' => 'application/json',
                )
            );
        } else {
            $this->exchange->basic_publish(
                $this->createAmqpMessage($data),
                $this->exchangeName,
                $routingKey
            );
        }
    }

    /**
     * {@inheritDoc}
     */
    public function handleBatch(array $records)
    {
        if ($this->exchange instanceof AMQPExchange) {
            parent::handleBatch($records);

            return;
        }

        foreach ($records as $record) {
            if (!$this->isHandling($record)) {
                continue;
            }

            $record = $this->processRecord($record);
            $data = $this->getFormatter()->format($record);

            $this->exchange->batch_basic_publish(
                $this->createAmqpMessage($data),
                $this->exchangeName,
                $this->getRoutingKey($record)
            );
        }

        $this->exchange->publish_batch();
    }

    /**
     * Gets the routing key for the AMQP exchange
     *
     * @param  array  $record
     * @return string
     */
    protected function getRoutingKey(array $record)
    {
        $routingKey = sprintf(
            '%s.%s',
            // TODO 2.0 remove substr call
            substr($record['level_name'], 0, 4),
            $record['channel']
        );

        return strtolower($routingKey);
    }

    /**
     * @param  string      $data
     * @return AMQPMessage
     */
    private function createAmqpMessage($data)
    {
        return new AMQPMessage(
            (string) $data,
            array(
                'delivery_mode' => 2,
                'content_type' => 'application/json',
            )
        );
    }

    /**
     * {@inheritDoc}
     */
    protected function getDefaultFormatter()
    {
        return new JsonFormatter(JsonFormatter::BATCH_MODE_JSON, false);
    }
}
vendor/monolog/monolog/src/Monolog/Handler/HandlerWrapper.php000064400000004523151327705700020405 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog\Handler;

use WPvividMonolog\ResettableInterface;
use WPvividMonolog\Formatter\FormatterInterface;

/**
 * This simple wrapper class can be used to extend handlers functionality.
 *
 * Example: A custom filtering that can be applied to any handler.
 *
 * Inherit from this class and override handle() like this:
 *
 *   public function handle(array $record)
 *   {
 *        if ($record meets certain conditions) {
 *            return false;
 *        }
 *        return $this->handler->handle($record);
 *   }
 *
 * @author Alexey Karapetov <alexey@karapetov.com>
 */
class HandlerWrapper implements HandlerInterface, ResettableInterface
{
    /**
     * @var HandlerInterface
     */
    protected $handler;

    /**
     * HandlerWrapper constructor.
     * @param HandlerInterface $handler
     */
    public function __construct(HandlerInterface $handler)
    {
        $this->handler = $handler;
    }

    /**
     * {@inheritdoc}
     */
    public function isHandling(array $record)
    {
        return $this->handler->isHandling($record);
    }

    /**
     * {@inheritdoc}
     */
    public function handle(array $record)
    {
        return $this->handler->handle($record);
    }

    /**
     * {@inheritdoc}
     */
    public function handleBatch(array $records)
    {
        return $this->handler->handleBatch($records);
    }

    /**
     * {@inheritdoc}
     */
    public function pushProcessor($callback)
    {
        $this->handler->pushProcessor($callback);

        return $this;
    }

    /**
     * {@inheritdoc}
     */
    public function popProcessor()
    {
        return $this->handler->popProcessor();
    }

    /**
     * {@inheritdoc}
     */
    public function setFormatter(FormatterInterface $formatter)
    {
        $this->handler->setFormatter($formatter);

        return $this;
    }

    /**
     * {@inheritdoc}
     */
    public function getFormatter()
    {
        return $this->handler->getFormatter();
    }

    public function reset()
    {
        if ($this->handler instanceof ResettableInterface) {
            return $this->handler->reset();
        }
    }
}
vendor/monolog/monolog/src/Monolog/Handler/FleepHookHandler.php000064400000006470151327705700020644 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog\Handler;

use WPvividMonolog\Formatter\LineFormatter;
use WPvividMonolog\Logger;

/**
 * Sends logs to Fleep.io using Webhook integrations
 *
 * You'll need a Fleep.io account to use this handler.
 *
 * @see https://fleep.io/integrations/webhooks/ Fleep Webhooks Documentation
 * @author Ando Roots <ando@sqroot.eu>
 */
class FleepHookHandler extends SocketHandler
{
    const FLEEP_HOST = 'fleep.io';

    const FLEEP_HOOK_URI = '/hook/';

    /**
     * @var string Webhook token (specifies the conversation where logs are sent)
     */
    protected $token;

    /**
     * Construct a new Fleep.io Handler.
     *
     * For instructions on how to create a new web hook in your conversations
     * see https://fleep.io/integrations/webhooks/
     *
     * @param  string                    $token  Webhook token
     * @param  bool|int                  $level  The minimum logging level at which this handler will be triggered
     * @param  bool                      $bubble Whether the messages that are handled can bubble up the stack or not
     * @throws MissingExtensionException
     */
    public function __construct($token, $level = Logger::DEBUG, $bubble = true)
    {
        if (!extension_loaded('openssl')) {
            throw new MissingExtensionException('The OpenSSL PHP extension is required to use the FleepHookHandler');
        }

        $this->token = $token;

        $connectionString = 'ssl://' . self::FLEEP_HOST . ':443';
        parent::__construct($connectionString, $level, $bubble);
    }

    /**
     * Returns the default formatter to use with this handler
     *
     * Overloaded to remove empty context and extra arrays from the end of the log message.
     *
     * @return LineFormatter
     */
    protected function getDefaultFormatter()
    {
        return new LineFormatter(null, null, true, true);
    }

    /**
     * Handles a log record
     *
     * @param array $record
     */
    public function write(array $record)
    {
        parent::write($record);
        $this->closeSocket();
    }

    /**
     * {@inheritdoc}
     *
     * @param  array  $record
     * @return string
     */
    protected function generateDataStream($record)
    {
        $content = $this->buildContent($record);

        return $this->buildHeader($content) . $content;
    }

    /**
     * Builds the header of the API Call
     *
     * @param  string $content
     * @return string
     */
    private function buildHeader($content)
    {
        $header = "POST " . self::FLEEP_HOOK_URI . $this->token . " HTTP/1.1\r\n";
        $header .= "Host: " . self::FLEEP_HOST . "\r\n";
        $header .= "Content-Type: application/x-www-form-urlencoded\r\n";
        $header .= "Content-Length: " . strlen($content) . "\r\n";
        $header .= "\r\n";

        return $header;
    }

    /**
     * Builds the body of API call
     *
     * @param  array  $record
     * @return string
     */
    private function buildContent($record)
    {
        $dataArray = array(
            'message' => $record['formatted'],
        );

        return http_build_query($dataArray);
    }
}
vendor/monolog/monolog/src/Monolog/Handler/DoctrineCouchDBHandler.php000064400000001775151327705700021732 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog\Handler;

use WPvividMonolog\Logger;
use WPvividMonolog\Formatter\NormalizerFormatter;
use Doctrine\CouchDB\CouchDBClient;

/**
 * CouchDB handler for Doctrine CouchDB ODM
 *
 * @author Markus Bachmann <markus.bachmann@bachi.biz>
 */
class DoctrineCouchDBHandler extends AbstractProcessingHandler
{
    private $client;

    public function __construct(CouchDBClient $client, $level = Logger::DEBUG, $bubble = true)
    {
        $this->client = $client;
        parent::__construct($level, $bubble);
    }

    /**
     * {@inheritDoc}
     */
    protected function write(array $record)
    {
        $this->client->postDocument($record['formatted']);
    }

    protected function getDefaultFormatter()
    {
        return new NormalizerFormatter;
    }
}
vendor/monolog/monolog/src/Monolog/Handler/Curl/Util.php000064400000002740151327705700017310 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog\Handler\Curl;

class Util
{
    private static $retriableErrorCodes = array(
        CURLE_COULDNT_RESOLVE_HOST,
        CURLE_COULDNT_CONNECT,
        CURLE_HTTP_NOT_FOUND,
        CURLE_READ_ERROR,
        CURLE_OPERATION_TIMEOUTED,
        CURLE_HTTP_POST_ERROR,
        CURLE_SSL_CONNECT_ERROR,
    );

    /**
     * Executes a CURL request with optional retries and exception on failure
     *
     * @param  resource          $ch curl handler
     * @throws \RuntimeException
     */
    public static function execute($ch, $retries = 5, $closeAfterDone = true)
    {
        while ($retries--) {
            if (curl_exec($ch) === false) {
                $curlErrno = curl_errno($ch);

                if (false === in_array($curlErrno, self::$retriableErrorCodes, true) || !$retries) {
                    $curlError = curl_error($ch);

                    if ($closeAfterDone) {
                        curl_close($ch);
                    }

                    throw new \RuntimeException(sprintf('Curl error (code %s): %s', $curlErrno, $curlError));
                }

                continue;
            }

            if ($closeAfterDone) {
                curl_close($ch);
            }
            break;
        }
    }
}
vendor/monolog/monolog/src/Monolog/Handler/PHPConsoleHandler.php000064400000023442151327705700020740 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog\Handler;

use Exception;
use WPvividMonolog\Formatter\LineFormatter;
use WPvividMonolog\Logger;
use PhpConsole\Connector;
use PhpConsole\Handler;
use PhpConsole\Helper;

/**
 * Monolog handler for Google Chrome extension "PHP Console"
 *
 * Display PHP error/debug log messages in Google Chrome console and notification popups, executes PHP code remotely
 *
 * Usage:
 * 1. Install Google Chrome extension https://chrome.google.com/webstore/detail/php-console/nfhmhhlpfleoednkpnnnkolmclajemef
 * 2. See overview https://github.com/barbushin/php-console#overview
 * 3. Install PHP Console library https://github.com/barbushin/php-console#installation
 * 4. Example (result will looks like http://i.hizliresim.com/vg3Pz4.png)
 *
 *      $logger = new \Monolog\Logger('all', array(new \Monolog\Handler\PHPConsoleHandler()));
 *      \Monolog\ErrorHandler::register($logger);
 *      echo $undefinedVar;
 *      $logger->addDebug('SELECT * FROM users', array('db', 'time' => 0.012));
 *      PC::debug($_SERVER); // PHP Console debugger for any type of vars
 *
 * @author Sergey Barbushin https://www.linkedin.com/in/barbushin
 */
class PHPConsoleHandler extends AbstractProcessingHandler
{
    private $options = array(
        'enabled' => true, // bool Is PHP Console server enabled
        'classesPartialsTraceIgnore' => array('WPvividMonolog\\'), // array Hide calls of classes started with...
        'debugTagsKeysInContext' => array(0, 'tag'), // bool Is PHP Console server enabled
        'useOwnErrorsHandler' => false, // bool Enable errors handling
        'useOwnExceptionsHandler' => false, // bool Enable exceptions handling
        'sourcesBasePath' => null, // string Base path of all project sources to strip in errors source paths
        'registerHelper' => true, // bool Register PhpConsole\Helper that allows short debug calls like PC::debug($var, 'ta.g.s')
        'serverEncoding' => null, // string|null Server internal encoding
        'headersLimit' => null, // int|null Set headers size limit for your web-server
        'password' => null, // string|null Protect PHP Console connection by password
        'enableSslOnlyMode' => false, // bool Force connection by SSL for clients with PHP Console installed
        'ipMasks' => array(), // array Set IP masks of clients that will be allowed to connect to PHP Console: array('192.168.*.*', '127.0.0.1')
        'enableEvalListener' => false, // bool Enable eval request to be handled by eval dispatcher(if enabled, 'password' option is also required)
        'dumperDetectCallbacks' => false, // bool Convert callback items in dumper vars to (callback SomeClass::someMethod) strings
        'dumperLevelLimit' => 5, // int Maximum dumped vars array or object nested dump level
        'dumperItemsCountLimit' => 100, // int Maximum dumped var same level array items or object properties number
        'dumperItemSizeLimit' => 5000, // int Maximum length of any string or dumped array item
        'dumperDumpSizeLimit' => 500000, // int Maximum approximate size of dumped vars result formatted in JSON
        'detectDumpTraceAndSource' => false, // bool Autodetect and append trace data to debug
        'dataStorage' => null, // PhpConsole\Storage|null Fixes problem with custom $_SESSION handler(see http://goo.gl/Ne8juJ)
    );

    /** @var Connector */
    private $connector;

    /**
     * @param  array          $options   See \Monolog\Handler\PHPConsoleHandler::$options for more details
     * @param  Connector|null $connector Instance of \PhpConsole\Connector class (optional)
     * @param  int            $level
     * @param  bool           $bubble
     * @throws Exception
     */
    public function __construct(array $options = array(), Connector $connector = null, $level = Logger::DEBUG, $bubble = true)
    {
        if (!class_exists('PhpConsole\Connector')) {
            throw new Exception('PHP Console library not found. See https://github.com/barbushin/php-console#installation');
        }
        parent::__construct($level, $bubble);
        $this->options = $this->initOptions($options);
        $this->connector = $this->initConnector($connector);
    }

    private function initOptions(array $options)
    {
        $wrongOptions = array_diff(array_keys($options), array_keys($this->options));
        if ($wrongOptions) {
            throw new Exception('Unknown options: ' . implode(', ', $wrongOptions));
        }

        return array_replace($this->options, $options);
    }

    private function initConnector(Connector $connector = null)
    {
        if (!$connector) {
            if ($this->options['dataStorage']) {
                Connector::setPostponeStorage($this->options['dataStorage']);
            }
            $connector = Connector::getInstance();
        }

        if ($this->options['registerHelper'] && !Helper::isRegistered()) {
            Helper::register();
        }

        if ($this->options['enabled'] && $connector->isActiveClient()) {
            if ($this->options['useOwnErrorsHandler'] || $this->options['useOwnExceptionsHandler']) {
                $handler = Handler::getInstance();
                $handler->setHandleErrors($this->options['useOwnErrorsHandler']);
                $handler->setHandleExceptions($this->options['useOwnExceptionsHandler']);
                $handler->start();
            }
            if ($this->options['sourcesBasePath']) {
                $connector->setSourcesBasePath($this->options['sourcesBasePath']);
            }
            if ($this->options['serverEncoding']) {
                $connector->setServerEncoding($this->options['serverEncoding']);
            }
            if ($this->options['password']) {
                $connector->setPassword($this->options['password']);
            }
            if ($this->options['enableSslOnlyMode']) {
                $connector->enableSslOnlyMode();
            }
            if ($this->options['ipMasks']) {
                $connector->setAllowedIpMasks($this->options['ipMasks']);
            }
            if ($this->options['headersLimit']) {
                $connector->setHeadersLimit($this->options['headersLimit']);
            }
            if ($this->options['detectDumpTraceAndSource']) {
                $connector->getDebugDispatcher()->detectTraceAndSource = true;
            }
            $dumper = $connector->getDumper();
            $dumper->levelLimit = $this->options['dumperLevelLimit'];
            $dumper->itemsCountLimit = $this->options['dumperItemsCountLimit'];
            $dumper->itemSizeLimit = $this->options['dumperItemSizeLimit'];
            $dumper->dumpSizeLimit = $this->options['dumperDumpSizeLimit'];
            $dumper->detectCallbacks = $this->options['dumperDetectCallbacks'];
            if ($this->options['enableEvalListener']) {
                $connector->startEvalRequestsListener();
            }
        }

        return $connector;
    }

    public function getConnector()
    {
        return $this->connector;
    }

    public function getOptions()
    {
        return $this->options;
    }

    public function handle(array $record)
    {
        if ($this->options['enabled'] && $this->connector->isActiveClient()) {
            return parent::handle($record);
        }

        return !$this->bubble;
    }

    /**
     * Writes the record down to the log of the implementing handler
     *
     * @param  array $record
     * @return void
     */
    protected function write(array $record)
    {
        if ($record['level'] < Logger::NOTICE) {
            $this->handleDebugRecord($record);
        } elseif (isset($record['context']['exception']) && $record['context']['exception'] instanceof Exception) {
            $this->handleExceptionRecord($record);
        } else {
            $this->handleErrorRecord($record);
        }
    }

    private function handleDebugRecord(array $record)
    {
        $tags = $this->getRecordTags($record);
        $message = $record['message'];
        if ($record['context']) {
            $message .= ' ' . json_encode($this->connector->getDumper()->dump(array_filter($record['context'])));
        }
        $this->connector->getDebugDispatcher()->dispatchDebug($message, $tags, $this->options['classesPartialsTraceIgnore']);
    }

    private function handleExceptionRecord(array $record)
    {
        $this->connector->getErrorsDispatcher()->dispatchException($record['context']['exception']);
    }

    private function handleErrorRecord(array $record)
    {
        $context = $record['context'];

        $this->connector->getErrorsDispatcher()->dispatchError(
            isset($context['code']) ? $context['code'] : null,
            isset($context['message']) ? $context['message'] : $record['message'],
            isset($context['file']) ? $context['file'] : null,
            isset($context['line']) ? $context['line'] : null,
            $this->options['classesPartialsTraceIgnore']
        );
    }

    private function getRecordTags(array &$record)
    {
        $tags = null;
        if (!empty($record['context'])) {
            $context = & $record['context'];
            foreach ($this->options['debugTagsKeysInContext'] as $key) {
                if (!empty($context[$key])) {
                    $tags = $context[$key];
                    if ($key === 0) {
                        array_shift($context);
                    } else {
                        unset($context[$key]);
                    }
                    break;
                }
            }
        }

        return $tags ?: strtolower($record['level_name']);
    }

    /**
     * {@inheritDoc}
     */
    protected function getDefaultFormatter()
    {
        return new LineFormatter('%message%');
    }
}
vendor/monolog/monolog/src/Monolog/Handler/ErrorLogHandler.php000064400000004525151327705700020522 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog\Handler;

use WPvividMonolog\Formatter\LineFormatter;
use WPvividMonolog\Logger;

/**
 * Stores to PHP error_log() handler.
 *
 * @author Elan Ruusamäe <glen@delfi.ee>
 */
class ErrorLogHandler extends AbstractProcessingHandler
{
    const OPERATING_SYSTEM = 0;
    const SAPI = 4;

    protected $messageType;
    protected $expandNewlines;

    /**
     * @param int  $messageType    Says where the error should go.
     * @param int  $level          The minimum logging level at which this handler will be triggered
     * @param bool $bubble         Whether the messages that are handled can bubble up the stack or not
     * @param bool $expandNewlines If set to true, newlines in the message will be expanded to be take multiple log entries
     */
    public function __construct($messageType = self::OPERATING_SYSTEM, $level = Logger::DEBUG, $bubble = true, $expandNewlines = false)
    {
        parent::__construct($level, $bubble);

        if (false === in_array($messageType, self::getAvailableTypes())) {
            $message = sprintf('The given message type "%s" is not supported', print_r($messageType, true));
            throw new \InvalidArgumentException($message);
        }

        $this->messageType = $messageType;
        $this->expandNewlines = $expandNewlines;
    }

    /**
     * @return array With all available types
     */
    public static function getAvailableTypes()
    {
        return array(
            self::OPERATING_SYSTEM,
            self::SAPI,
        );
    }

    /**
     * {@inheritDoc}
     */
    protected function getDefaultFormatter()
    {
        return new LineFormatter('[%datetime%] %channel%.%level_name%: %message% %context% %extra%');
    }

    /**
     * {@inheritdoc}
     */
    protected function write(array $record)
    {
        if ($this->expandNewlines) {
            $lines = preg_split('{[\r\n]+}', (string) $record['formatted']);
            foreach ($lines as $line) {
                error_log($line, $this->messageType);
            }
        } else {
            error_log((string) $record['formatted'], $this->messageType);
        }
    }
}
vendor/monolog/monolog/src/Monolog/Handler/GelfHandler.php000064400000003672151327705700017646 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog\Handler;

use Gelf\IMessagePublisher;
use Gelf\PublisherInterface;
use Gelf\Publisher;
use InvalidArgumentException;
use WPvividMonolog\Logger;
use WPvividMonolog\Formatter\GelfMessageFormatter;

/**
 * Handler to send messages to a Graylog2 (http://www.graylog2.org) server
 *
 * @author Matt Lehner <mlehner@gmail.com>
 * @author Benjamin Zikarsky <benjamin@zikarsky.de>
 */
class GelfHandler extends AbstractProcessingHandler
{
    /**
     * @var Publisher the publisher object that sends the message to the server
     */
    protected $publisher;

    /**
     * @param PublisherInterface|IMessagePublisher|Publisher $publisher a publisher object
     * @param int                                            $level     The minimum logging level at which this handler will be triggered
     * @param bool                                           $bubble    Whether the messages that are handled can bubble up the stack or not
     */
    public function __construct($publisher, $level = Logger::DEBUG, $bubble = true)
    {
        parent::__construct($level, $bubble);

        if (!$publisher instanceof Publisher && !$publisher instanceof IMessagePublisher && !$publisher instanceof PublisherInterface) {
            throw new InvalidArgumentException('Invalid publisher, expected a Gelf\Publisher, Gelf\IMessagePublisher or Gelf\PublisherInterface instance');
        }

        $this->publisher = $publisher;
    }

    /**
     * {@inheritdoc}
     */
    protected function write(array $record)
    {
        $this->publisher->publish($record['formatted']);
    }

    /**
     * {@inheritDoc}
     */
    protected function getDefaultFormatter()
    {
        return new GelfMessageFormatter();
    }
}
vendor/monolog/monolog/src/Monolog/Handler/AbstractProcessingHandler.php000064400000003012151327705700022555 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog\Handler;

use WPvividMonolog\ResettableInterface;

/**
 * Base Handler class providing the Handler structure
 *
 * Classes extending it should (in most cases) only implement write($record)
 *
 * @author Jordi Boggiano <j.boggiano@seld.be>
 * @author Christophe Coevoet <stof@notk.org>
 */
abstract class AbstractProcessingHandler extends AbstractHandler
{
    /**
     * {@inheritdoc}
     */
    public function handle(array $record)
    {
        if (!$this->isHandling($record)) {
            return false;
        }

        $record = $this->processRecord($record);

        $record['formatted'] = $this->getFormatter()->format($record);

        $this->write($record);

        return false === $this->bubble;
    }

    /**
     * Writes the record down to the log of the implementing handler
     *
     * @param  array $record
     * @return void
     */
    abstract protected function write(array $record);

    /**
     * Processes a record.
     *
     * @param  array $record
     * @return array
     */
    protected function processRecord(array $record)
    {
        if ($this->processors) {
            foreach ($this->processors as $processor) {
                $record = call_user_func($processor, $record);
            }
        }

        return $record;
    }
}
vendor/monolog/monolog/src/Monolog/Handler/FingersCrossedHandler.php000064400000013465151327705700021712 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog\Handler;

use WPvividMonolog\Handler\FingersCrossed\ErrorLevelActivationStrategy;
use WPvividMonolog\Handler\FingersCrossed\ActivationStrategyInterface;
use WPvividMonolog\Logger;
use WPvividMonolog\ResettableInterface;

/**
 * Buffers all records until a certain level is reached
 *
 * The advantage of this approach is that you don't get any clutter in your log files.
 * Only requests which actually trigger an error (or whatever your actionLevel is) will be
 * in the logs, but they will contain all records, not only those above the level threshold.
 *
 * You can find the various activation strategies in the
 * Monolog\Handler\FingersCrossed\ namespace.
 *
 * @author Jordi Boggiano <j.boggiano@seld.be>
 */
class FingersCrossedHandler extends AbstractHandler
{
    protected $handler;
    protected $activationStrategy;
    protected $buffering = true;
    protected $bufferSize;
    protected $buffer = array();
    protected $stopBuffering;
    protected $passthruLevel;

    /**
     * @param callable|HandlerInterface       $handler            Handler or factory callable($record, $fingersCrossedHandler).
     * @param int|ActivationStrategyInterface $activationStrategy Strategy which determines when this handler takes action
     * @param int                             $bufferSize         How many entries should be buffered at most, beyond that the oldest items are removed from the buffer.
     * @param bool                            $bubble             Whether the messages that are handled can bubble up the stack or not
     * @param bool                            $stopBuffering      Whether the handler should stop buffering after being triggered (default true)
     * @param int                             $passthruLevel      Minimum level to always flush to handler on close, even if strategy not triggered
     */
    public function __construct($handler, $activationStrategy = null, $bufferSize = 0, $bubble = true, $stopBuffering = true, $passthruLevel = null)
    {
        if (null === $activationStrategy) {
            $activationStrategy = new ErrorLevelActivationStrategy(Logger::WARNING);
        }

        // convert simple int activationStrategy to an object
        if (!$activationStrategy instanceof ActivationStrategyInterface) {
            $activationStrategy = new ErrorLevelActivationStrategy($activationStrategy);
        }

        $this->handler = $handler;
        $this->activationStrategy = $activationStrategy;
        $this->bufferSize = $bufferSize;
        $this->bubble = $bubble;
        $this->stopBuffering = $stopBuffering;

        if ($passthruLevel !== null) {
            $this->passthruLevel = Logger::toMonologLevel($passthruLevel);
        }

        if (!$this->handler instanceof HandlerInterface && !is_callable($this->handler)) {
            throw new \RuntimeException("The given handler (".json_encode($this->handler).") is not a callable nor a Monolog\Handler\HandlerInterface object");
        }
    }

    /**
     * {@inheritdoc}
     */
    public function isHandling(array $record)
    {
        return true;
    }

    /**
     * Manually activate this logger regardless of the activation strategy
     */
    public function activate()
    {
        if ($this->stopBuffering) {
            $this->buffering = false;
        }
        if (!$this->handler instanceof HandlerInterface) {
            $record = end($this->buffer) ?: null;

            $this->handler = call_user_func($this->handler, $record, $this);
            if (!$this->handler instanceof HandlerInterface) {
                throw new \RuntimeException("The factory callable should return a HandlerInterface");
            }
        }
        $this->handler->handleBatch($this->buffer);
        $this->buffer = array();
    }

    /**
     * {@inheritdoc}
     */
    public function handle(array $record)
    {
        if ($this->processors) {
            foreach ($this->processors as $processor) {
                $record = call_user_func($processor, $record);
            }
        }

        if ($this->buffering) {
            $this->buffer[] = $record;
            if ($this->bufferSize > 0 && count($this->buffer) > $this->bufferSize) {
                array_shift($this->buffer);
            }
            if ($this->activationStrategy->isHandlerActivated($record)) {
                $this->activate();
            }
        } else {
            $this->handler->handle($record);
        }

        return false === $this->bubble;
    }

    /**
     * {@inheritdoc}
     */
    public function close()
    {
        $this->flushBuffer();
    }

    public function reset()
    {
        $this->flushBuffer();

        parent::reset();

        if ($this->handler instanceof ResettableInterface) {
            $this->handler->reset();
        }
    }

    /**
     * Clears the buffer without flushing any messages down to the wrapped handler.
     *
     * It also resets the handler to its initial buffering state.
     */
    public function clear()
    {
        $this->buffer = array();
        $this->reset();
    }

    /**
     * Resets the state of the handler. Stops forwarding records to the wrapped handler.
     */
    private function flushBuffer()
    {
        if (null !== $this->passthruLevel) {
            $level = $this->passthruLevel;
            $this->buffer = array_filter($this->buffer, function ($record) use ($level) {
                return $record['level'] >= $level;
            });
            if (count($this->buffer) > 0) {
                $this->handler->handleBatch($this->buffer);
            }
        }

        $this->buffer = array();
        $this->buffering = true;
    }
}
vendor/monolog/monolog/src/Monolog/Handler/SocketHandler.php000064400000023142151327705700020213 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog\Handler;

use WPvividMonolog\Logger;

/**
 * Stores to any socket - uses fsockopen() or pfsockopen().
 *
 * @author Pablo de Leon Belloc <pablolb@gmail.com>
 * @see    http://php.net/manual/en/function.fsockopen.php
 */
class SocketHandler extends AbstractProcessingHandler
{
    private $connectionString;
    private $connectionTimeout;
    private $resource;
    private $timeout = 0;
    private $writingTimeout = 10;
    private $lastSentBytes = null;
    private $chunkSize = null;
    private $persistent = false;
    private $errno;
    private $errstr;
    private $lastWritingAt;

    /**
     * @param string $connectionString Socket connection string
     * @param int    $level            The minimum logging level at which this handler will be triggered
     * @param bool   $bubble           Whether the messages that are handled can bubble up the stack or not
     */
    public function __construct($connectionString, $level = Logger::DEBUG, $bubble = true)
    {
        parent::__construct($level, $bubble);
        $this->connectionString = $connectionString;
        $this->connectionTimeout = (float) ini_get('default_socket_timeout');
    }

    /**
     * Connect (if necessary) and write to the socket
     *
     * @param array $record
     *
     * @throws \UnexpectedValueException
     * @throws \RuntimeException
     */
    protected function write(array $record)
    {
        $this->connectIfNotConnected();
        $data = $this->generateDataStream($record);
        $this->writeToSocket($data);
    }

    /**
     * We will not close a PersistentSocket instance so it can be reused in other requests.
     */
    public function close()
    {
        if (!$this->isPersistent()) {
            $this->closeSocket();
        }
    }

    /**
     * Close socket, if open
     */
    public function closeSocket()
    {
        if (is_resource($this->resource)) {
            fclose($this->resource);
            $this->resource = null;
        }
    }

    /**
     * Set socket connection to nbe persistent. It only has effect before the connection is initiated.
     *
     * @param bool $persistent
     */
    public function setPersistent($persistent)
    {
        $this->persistent = (bool) $persistent;
    }

    /**
     * Set connection timeout.  Only has effect before we connect.
     *
     * @param float $seconds
     *
     * @see http://php.net/manual/en/function.fsockopen.php
     */
    public function setConnectionTimeout($seconds)
    {
        $this->validateTimeout($seconds);
        $this->connectionTimeout = (float) $seconds;
    }

    /**
     * Set write timeout. Only has effect before we connect.
     *
     * @param float $seconds
     *
     * @see http://php.net/manual/en/function.stream-set-timeout.php
     */
    public function setTimeout($seconds)
    {
        $this->validateTimeout($seconds);
        $this->timeout = (float) $seconds;
    }

    /**
     * Set writing timeout. Only has effect during connection in the writing cycle.
     *
     * @param float $seconds 0 for no timeout
     */
    public function setWritingTimeout($seconds)
    {
        $this->validateTimeout($seconds);
        $this->writingTimeout = (float) $seconds;
    }

    /**
     * Set chunk size. Only has effect during connection in the writing cycle.
     *
     * @param float $bytes
     */
    public function setChunkSize($bytes)
    {
        $this->chunkSize = $bytes;
    }

    /**
     * Get current connection string
     *
     * @return string
     */
    public function getConnectionString()
    {
        return $this->connectionString;
    }

    /**
     * Get persistent setting
     *
     * @return bool
     */
    public function isPersistent()
    {
        return $this->persistent;
    }

    /**
     * Get current connection timeout setting
     *
     * @return float
     */
    public function getConnectionTimeout()
    {
        return $this->connectionTimeout;
    }

    /**
     * Get current in-transfer timeout
     *
     * @return float
     */
    public function getTimeout()
    {
        return $this->timeout;
    }

    /**
     * Get current local writing timeout
     *
     * @return float
     */
    public function getWritingTimeout()
    {
        return $this->writingTimeout;
    }

    /**
     * Get current chunk size
     *
     * @return float
     */
    public function getChunkSize()
    {
        return $this->chunkSize;
    }

    /**
     * Check to see if the socket is currently available.
     *
     * UDP might appear to be connected but might fail when writing.  See http://php.net/fsockopen for details.
     *
     * @return bool
     */
    public function isConnected()
    {
        return is_resource($this->resource)
            && !feof($this->resource);  // on TCP - other party can close connection.
    }

    /**
     * Wrapper to allow mocking
     */
    protected function pfsockopen()
    {
        return @pfsockopen($this->connectionString, -1, $this->errno, $this->errstr, $this->connectionTimeout);
    }

    /**
     * Wrapper to allow mocking
     */
    protected function fsockopen()
    {
        return @fsockopen($this->connectionString, -1, $this->errno, $this->errstr, $this->connectionTimeout);
    }

    /**
     * Wrapper to allow mocking
     *
     * @see http://php.net/manual/en/function.stream-set-timeout.php
     */
    protected function streamSetTimeout()
    {
        $seconds = floor($this->timeout);
        $microseconds = round(($this->timeout - $seconds) * 1e6);

        return stream_set_timeout($this->resource, $seconds, $microseconds);
    }

    /**
     * Wrapper to allow mocking
     *
     * @see http://php.net/manual/en/function.stream-set-chunk-size.php
     */
    protected function streamSetChunkSize()
    {
        return stream_set_chunk_size($this->resource, $this->chunkSize);
    }

    /**
     * Wrapper to allow mocking
     */
    protected function fwrite($data)
    {
        return @fwrite($this->resource, $data);
    }

    /**
     * Wrapper to allow mocking
     */
    protected function streamGetMetadata()
    {
        return stream_get_meta_data($this->resource);
    }

    private function validateTimeout($value)
    {
        $ok = filter_var($value, FILTER_VALIDATE_FLOAT);
        if ($ok === false || $value < 0) {
            throw new \InvalidArgumentException("Timeout must be 0 or a positive float (got $value)");
        }
    }

    private function connectIfNotConnected()
    {
        if ($this->isConnected()) {
            return;
        }
        $this->connect();
    }

    protected function generateDataStream($record)
    {
        return (string) $record['formatted'];
    }

    /**
     * @return resource|null
     */
    protected function getResource()
    {
        return $this->resource;
    }

    private function connect()
    {
        $this->createSocketResource();
        $this->setSocketTimeout();
        $this->setStreamChunkSize();
    }

    private function createSocketResource()
    {
        if ($this->isPersistent()) {
            $resource = $this->pfsockopen();
        } else {
            $resource = $this->fsockopen();
        }
        if (!$resource) {
            throw new \UnexpectedValueException("Failed connecting to $this->connectionString ($this->errno: $this->errstr)");
        }
        $this->resource = $resource;
    }

    private function setSocketTimeout()
    {
        if (!$this->streamSetTimeout()) {
            throw new \UnexpectedValueException("Failed setting timeout with stream_set_timeout()");
        }
    }

    private function setStreamChunkSize()
    {
        if ($this->chunkSize && !$this->streamSetChunkSize()) {
            throw new \UnexpectedValueException("Failed setting chunk size with stream_set_chunk_size()");
        }
    }

    private function writeToSocket($data)
    {
        $length = strlen($data);
        $sent = 0;
        $this->lastSentBytes = $sent;
        while ($this->isConnected() && $sent < $length) {
            if (0 == $sent) {
                $chunk = $this->fwrite($data);
            } else {
                $chunk = $this->fwrite(substr($data, $sent));
            }
            if ($chunk === false) {
                throw new \RuntimeException("Could not write to socket");
            }
            $sent += $chunk;
            $socketInfo = $this->streamGetMetadata();
            if ($socketInfo['timed_out']) {
                throw new \RuntimeException("Write timed-out");
            }

            if ($this->writingIsTimedOut($sent)) {
                throw new \RuntimeException("Write timed-out, no data sent for `{$this->writingTimeout}` seconds, probably we got disconnected (sent $sent of $length)");
            }
        }
        if (!$this->isConnected() && $sent < $length) {
            throw new \RuntimeException("End-of-file reached, probably we got disconnected (sent $sent of $length)");
        }
    }

    private function writingIsTimedOut($sent)
    {
        $writingTimeout = (int) floor($this->writingTimeout);
        if (0 === $writingTimeout) {
            return false;
        }

        if ($sent !== $this->lastSentBytes) {
            $this->lastWritingAt = time();
            $this->lastSentBytes = $sent;

            return false;
        } else {
            usleep(100);
        }

        if ((time() - $this->lastWritingAt) >= $writingTimeout) {
            $this->closeSocket();

            return true;
        }

        return false;
    }
}
vendor/monolog/monolog/src/Monolog/Handler/SlackHandler.php000064400000014522151327705700020022 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog\Handler;

use WPvividMonolog\Formatter\FormatterInterface;
use WPvividMonolog\Logger;
use WPvividMonolog\Handler\Slack\SlackRecord;

/**
 * Sends notifications through Slack API
 *
 * @author Greg Kedzierski <greg@gregkedzierski.com>
 * @see    https://api.slack.com/
 */
class SlackHandler extends SocketHandler
{
    /**
     * Slack API token
     * @var string
     */
    private $token;

    /**
     * Instance of the SlackRecord util class preparing data for Slack API.
     * @var SlackRecord
     */
    private $slackRecord;

    /**
     * @param  string                    $token                  Slack API token
     * @param  string                    $channel                Slack channel (encoded ID or name)
     * @param  string|null               $username               Name of a bot
     * @param  bool                      $useAttachment          Whether the message should be added to Slack as attachment (plain text otherwise)
     * @param  string|null               $iconEmoji              The emoji name to use (or null)
     * @param  int                       $level                  The minimum logging level at which this handler will be triggered
     * @param  bool                      $bubble                 Whether the messages that are handled can bubble up the stack or not
     * @param  bool                      $useShortAttachment     Whether the the context/extra messages added to Slack as attachments are in a short style
     * @param  bool                      $includeContextAndExtra Whether the attachment should include context and extra data
     * @param  array                     $excludeFields          Dot separated list of fields to exclude from slack message. E.g. ['context.field1', 'extra.field2']
     * @throws MissingExtensionException If no OpenSSL PHP extension configured
     */
    public function __construct($token, $channel, $username = null, $useAttachment = true, $iconEmoji = null, $level = Logger::CRITICAL, $bubble = true, $useShortAttachment = false, $includeContextAndExtra = false, array $excludeFields = array())
    {
        if (!extension_loaded('openssl')) {
            throw new MissingExtensionException('The OpenSSL PHP extension is required to use the SlackHandler');
        }

        parent::__construct('ssl://slack.com:443', $level, $bubble);

        $this->slackRecord = new SlackRecord(
            $channel,
            $username,
            $useAttachment,
            $iconEmoji,
            $useShortAttachment,
            $includeContextAndExtra,
            $excludeFields,
            $this->formatter
        );

        $this->token = $token;
    }

    public function getSlackRecord()
    {
        return $this->slackRecord;
    }

    public function getToken()
    {
        return $this->token;
    }

    /**
     * {@inheritdoc}
     *
     * @param  array  $record
     * @return string
     */
    protected function generateDataStream($record)
    {
        $content = $this->buildContent($record);

        return $this->buildHeader($content) . $content;
    }

    /**
     * Builds the body of API call
     *
     * @param  array  $record
     * @return string
     */
    private function buildContent($record)
    {
        $dataArray = $this->prepareContentData($record);

        return http_build_query($dataArray);
    }

    /**
     * Prepares content data
     *
     * @param  array $record
     * @return array
     */
    protected function prepareContentData($record)
    {
        $dataArray = $this->slackRecord->getSlackData($record);
        $dataArray['token'] = $this->token;

        if (!empty($dataArray['attachments'])) {
            $dataArray['attachments'] = json_encode($dataArray['attachments']);
        }

        return $dataArray;
    }

    /**
     * Builds the header of the API Call
     *
     * @param  string $content
     * @return string
     */
    private function buildHeader($content)
    {
        $header = "POST /api/chat.postMessage HTTP/1.1\r\n";
        $header .= "Host: slack.com\r\n";
        $header .= "Content-Type: application/x-www-form-urlencoded\r\n";
        $header .= "Content-Length: " . strlen($content) . "\r\n";
        $header .= "\r\n";

        return $header;
    }

    /**
     * {@inheritdoc}
     *
     * @param array $record
     */
    protected function write(array $record)
    {
        parent::write($record);
        $this->finalizeWrite();
    }

    /**
     * Finalizes the request by reading some bytes and then closing the socket
     *
     * If we do not read some but close the socket too early, slack sometimes
     * drops the request entirely.
     */
    protected function finalizeWrite()
    {
        $res = $this->getResource();
        if (is_resource($res)) {
            @fread($res, 2048);
        }
        $this->closeSocket();
    }

    /**
     * Returned a Slack message attachment color associated with
     * provided level.
     *
     * @param  int    $level
     * @return string
     * @deprecated Use underlying SlackRecord instead
     */
    protected function getAttachmentColor($level)
    {
        trigger_error(
            'SlackHandler::getAttachmentColor() is deprecated. Use underlying SlackRecord instead.',
            E_USER_DEPRECATED
        );

        return $this->slackRecord->getAttachmentColor($level);
    }

    /**
     * Stringifies an array of key/value pairs to be used in attachment fields
     *
     * @param  array  $fields
     * @return string
     * @deprecated Use underlying SlackRecord instead
     */
    protected function stringify($fields)
    {
        trigger_error(
            'SlackHandler::stringify() is deprecated. Use underlying SlackRecord instead.',
            E_USER_DEPRECATED
        );

        return $this->slackRecord->stringify($fields);
    }

    public function setFormatter(FormatterInterface $formatter)
    {
        parent::setFormatter($formatter);
        $this->slackRecord->setFormatter($formatter);

        return $this;
    }

    public function getFormatter()
    {
        $formatter = parent::getFormatter();
        $this->slackRecord->setFormatter($formatter);

        return $formatter;
    }
}
vendor/monolog/monolog/src/Monolog/SignalHandler.php000064400000010166151327705700016625 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog;

use WPvividPsr\Log\LoggerInterface;
use WPvividPsr\Log\LogLevel;
use ReflectionExtension;

/**
 * Monolog POSIX signal handler
 *
 * @author Robert Gust-Bardon <robert@gust-bardon.org>
 */
class SignalHandler
{
    private $logger;

    private $previousSignalHandler = array();
    private $signalLevelMap = array();
    private $signalRestartSyscalls = array();

    public function __construct(LoggerInterface $logger)
    {
        $this->logger = $logger;
    }

    public function registerSignalHandler($signo, $level = LogLevel::CRITICAL, $callPrevious = true, $restartSyscalls = true, $async = true)
    {
        if (!extension_loaded('pcntl') || !function_exists('pcntl_signal')) {
            return $this;
        }

        if ($callPrevious) {
            if (function_exists('pcntl_signal_get_handler')) {
                $handler = pcntl_signal_get_handler($signo);
                if ($handler === false) {
                    return $this;
                }
                $this->previousSignalHandler[$signo] = $handler;
            } else {
                $this->previousSignalHandler[$signo] = true;
            }
        } else {
            unset($this->previousSignalHandler[$signo]);
        }
        $this->signalLevelMap[$signo] = $level;
        $this->signalRestartSyscalls[$signo] = $restartSyscalls;

        if (function_exists('pcntl_async_signals') && $async !== null) {
            pcntl_async_signals($async);
        }

        pcntl_signal($signo, array($this, 'handleSignal'), $restartSyscalls);

        return $this;
    }

    public function handleSignal($signo, array $siginfo = null)
    {
        static $signals = array();

        if (!$signals && extension_loaded('pcntl')) {
            $pcntl = new ReflectionExtension('pcntl');
            $constants = $pcntl->getConstants();
            if (!$constants) {
                // HHVM 3.24.2 returns an empty array.
                $constants = get_defined_constants(true);
                $constants = $constants['Core'];
            }
            foreach ($constants as $name => $value) {
                if (substr($name, 0, 3) === 'SIG' && $name[3] !== '_' && is_int($value)) {
                    $signals[$value] = $name;
                }
            }
            unset($constants);
        }

        $level = isset($this->signalLevelMap[$signo]) ? $this->signalLevelMap[$signo] : LogLevel::CRITICAL;
        $signal = isset($signals[$signo]) ? $signals[$signo] : $signo;
        $context = isset($siginfo) ? $siginfo : array();
        $this->logger->log($level, sprintf('Program received signal %s', $signal), $context);

        if (!isset($this->previousSignalHandler[$signo])) {
            return;
        }

        if ($this->previousSignalHandler[$signo] === true || $this->previousSignalHandler[$signo] === SIG_DFL) {
            if (extension_loaded('pcntl') && function_exists('pcntl_signal') && function_exists('pcntl_sigprocmask') && function_exists('pcntl_signal_dispatch')
                && extension_loaded('posix') && function_exists('posix_getpid') && function_exists('posix_kill')) {
                    $restartSyscalls = isset($this->restartSyscalls[$signo]) ? $this->restartSyscalls[$signo] : true;
                    pcntl_signal($signo, SIG_DFL, $restartSyscalls);
                    pcntl_sigprocmask(SIG_UNBLOCK, array($signo), $oldset);
                    posix_kill(posix_getpid(), $signo);
                    pcntl_signal_dispatch();
                    pcntl_sigprocmask(SIG_SETMASK, $oldset);
                    pcntl_signal($signo, array($this, 'handleSignal'), $restartSyscalls);
                }
        } elseif (is_callable($this->previousSignalHandler[$signo])) {
            if (PHP_VERSION_ID >= 70100) {
                $this->previousSignalHandler[$signo]($signo, $siginfo);
            } else {
                $this->previousSignalHandler[$signo]($signo);
            }
        }
    }
}
vendor/monolog/monolog/src/Monolog/ErrorHandler.php000064400000020727151327705700016505 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog;

use WPvividPsr\Log\LoggerInterface;
use WPvividPsr\Log\LogLevel;
use WPvividMonolog\Handler\AbstractHandler;
use WPvividMonolog\Registry;

/**
 * Monolog error handler
 *
 * A facility to enable logging of runtime errors, exceptions and fatal errors.
 *
 * Quick setup: <code>ErrorHandler::register($logger);</code>
 *
 * @author Jordi Boggiano <j.boggiano@seld.be>
 */
class ErrorHandler
{
    private $logger;

    private $previousExceptionHandler;
    private $uncaughtExceptionLevel;

    private $previousErrorHandler;
    private $errorLevelMap;
    private $handleOnlyReportedErrors;

    private $hasFatalErrorHandler;
    private $fatalLevel;
    private $reservedMemory;
    private $lastFatalTrace;
    private static $fatalErrors = array(E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR, E_USER_ERROR);

    public function __construct(LoggerInterface $logger)
    {
        $this->logger = $logger;
    }

    /**
     * Registers a new ErrorHandler for a given Logger
     *
     * By default it will handle errors, exceptions and fatal errors
     *
     * @param  LoggerInterface $logger
     * @param  array|false     $errorLevelMap  an array of E_* constant to LogLevel::* constant mapping, or false to disable error handling
     * @param  int|false       $exceptionLevel a LogLevel::* constant, or false to disable exception handling
     * @param  int|false       $fatalLevel     a LogLevel::* constant, or false to disable fatal error handling
     * @return ErrorHandler
     */
    public static function register(LoggerInterface $logger, $errorLevelMap = array(), $exceptionLevel = null, $fatalLevel = null)
    {
        //Forces the autoloader to run for LogLevel. Fixes an autoload issue at compile-time on PHP5.3. See https://github.com/Seldaek/monolog/pull/929
        class_exists('\\WPvividPsr\\Log\\LogLevel', true);

        $handler = new static($logger);
        if ($errorLevelMap !== false) {
            $handler->registerErrorHandler($errorLevelMap);
        }
        if ($exceptionLevel !== false) {
            $handler->registerExceptionHandler($exceptionLevel);
        }
        if ($fatalLevel !== false) {
            $handler->registerFatalHandler($fatalLevel);
        }

        return $handler;
    }

    public function registerExceptionHandler($level = null, $callPrevious = true)
    {
        $prev = set_exception_handler(array($this, 'handleException'));
        $this->uncaughtExceptionLevel = $level;
        if ($callPrevious && $prev) {
            $this->previousExceptionHandler = $prev;
        }
    }

    public function registerErrorHandler(array $levelMap = array(), $callPrevious = true, $errorTypes = -1, $handleOnlyReportedErrors = true)
    {
        $prev = set_error_handler(array($this, 'handleError'), $errorTypes);
        $this->errorLevelMap = array_replace($this->defaultErrorLevelMap(), $levelMap);
        if ($callPrevious) {
            $this->previousErrorHandler = $prev ?: true;
        }

        $this->handleOnlyReportedErrors = $handleOnlyReportedErrors;
    }

    public function registerFatalHandler($level = null, $reservedMemorySize = 20)
    {
        register_shutdown_function(array($this, 'handleFatalError'));

        $this->reservedMemory = str_repeat(' ', 1024 * $reservedMemorySize);
        $this->fatalLevel = $level;
        $this->hasFatalErrorHandler = true;
    }

    protected function defaultErrorLevelMap()
    {
        return array(
            E_ERROR             => LogLevel::CRITICAL,
            E_WARNING           => LogLevel::WARNING,
            E_PARSE             => LogLevel::ALERT,
            E_NOTICE            => LogLevel::NOTICE,
            E_CORE_ERROR        => LogLevel::CRITICAL,
            E_CORE_WARNING      => LogLevel::WARNING,
            E_COMPILE_ERROR     => LogLevel::ALERT,
            E_COMPILE_WARNING   => LogLevel::WARNING,
            E_USER_ERROR        => LogLevel::ERROR,
            E_USER_WARNING      => LogLevel::WARNING,
            E_USER_NOTICE       => LogLevel::NOTICE,
            E_STRICT            => LogLevel::NOTICE,
            E_RECOVERABLE_ERROR => LogLevel::ERROR,
            E_DEPRECATED        => LogLevel::NOTICE,
            E_USER_DEPRECATED   => LogLevel::NOTICE,
        );
    }

    /**
     * @private
     */
    public function handleException($e)
    {
        $this->logger->log(
            $this->uncaughtExceptionLevel === null ? LogLevel::ERROR : $this->uncaughtExceptionLevel,
            sprintf('Uncaught Exception %s: "%s" at %s line %s', Utils::getClass($e), $e->getMessage(), $e->getFile(), $e->getLine()),
            array('exception' => $e)
        );

        if ($this->previousExceptionHandler) {
            call_user_func($this->previousExceptionHandler, $e);
        }

        exit(255);
    }

    /**
     * @private
     */
    public function handleError($code, $message, $file = '', $line = 0, $context = array())
    {
        if ($this->handleOnlyReportedErrors && !(error_reporting() & $code)) {
            return;
        }

        // fatal error codes are ignored if a fatal error handler is present as well to avoid duplicate log entries
        if (!$this->hasFatalErrorHandler || !in_array($code, self::$fatalErrors, true)) {
            $level = isset($this->errorLevelMap[$code]) ? $this->errorLevelMap[$code] : LogLevel::CRITICAL;
            $this->logger->log($level, self::codeToString($code).': '.$message, array('code' => $code, 'message' => $message, 'file' => $file, 'line' => $line));
        } else {
            // http://php.net/manual/en/function.debug-backtrace.php
            // As of 5.3.6, DEBUG_BACKTRACE_IGNORE_ARGS option was added.
            // Any version less than 5.3.6 must use the DEBUG_BACKTRACE_IGNORE_ARGS constant value '2'.
            $trace = debug_backtrace((PHP_VERSION_ID < 50306) ? 2 : DEBUG_BACKTRACE_IGNORE_ARGS);
            array_shift($trace); // Exclude handleError from trace
            $this->lastFatalTrace = $trace;
        }

        if ($this->previousErrorHandler === true) {
            return false;
        } elseif ($this->previousErrorHandler) {
            return call_user_func($this->previousErrorHandler, $code, $message, $file, $line, $context);
        }
    }

    /**
     * @private
     */
    public function handleFatalError()
    {
        $this->reservedMemory = null;

        $lastError = error_get_last();
        if ($lastError && in_array($lastError['type'], self::$fatalErrors, true)) {
            $this->logger->log(
                $this->fatalLevel === null ? LogLevel::ALERT : $this->fatalLevel,
                'Fatal Error ('.self::codeToString($lastError['type']).'): '.$lastError['message'],
                array('code' => $lastError['type'], 'message' => $lastError['message'], 'file' => $lastError['file'], 'line' => $lastError['line'], 'trace' => $this->lastFatalTrace)
            );

            if ($this->logger instanceof Logger) {
                foreach ($this->logger->getHandlers() as $handler) {
                    if ($handler instanceof AbstractHandler) {
                        $handler->close();
                    }
                }
            }
        }
    }

    private static function codeToString($code)
    {
        switch ($code) {
            case E_ERROR:
                return 'E_ERROR';
            case E_WARNING:
                return 'E_WARNING';
            case E_PARSE:
                return 'E_PARSE';
            case E_NOTICE:
                return 'E_NOTICE';
            case E_CORE_ERROR:
                return 'E_CORE_ERROR';
            case E_CORE_WARNING:
                return 'E_CORE_WARNING';
            case E_COMPILE_ERROR:
                return 'E_COMPILE_ERROR';
            case E_COMPILE_WARNING:
                return 'E_COMPILE_WARNING';
            case E_USER_ERROR:
                return 'E_USER_ERROR';
            case E_USER_WARNING:
                return 'E_USER_WARNING';
            case E_USER_NOTICE:
                return 'E_USER_NOTICE';
            case E_STRICT:
                return 'E_STRICT';
            case E_RECOVERABLE_ERROR:
                return 'E_RECOVERABLE_ERROR';
            case E_DEPRECATED:
                return 'E_DEPRECATED';
            case E_USER_DEPRECATED:
                return 'E_USER_DEPRECATED';
        }

        return 'Unknown PHP error';
    }
}
vendor/monolog/monolog/src/Monolog/ResettableInterface.php000064400000001666151327705700020032 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog;

/**
 * Handler or Processor implementing this interface will be reset when Logger::reset() is called.
 *
 * Resetting ends a log cycle gets them back to their initial state.
 *
 * Resetting a Handler or a Processor means flushing/cleaning all buffers, resetting internal
 * state, and getting it back to a state in which it can receive log records again.
 *
 * This is useful in case you want to avoid logs leaking between two requests or jobs when you
 * have a long running process like a worker or an application server serving multiple requests
 * in one process.
 *
 * @author Grégoire Pineau <lyrixx@lyrixx.info>
 */
interface ResettableInterface
{
    public function reset();
}
vendor/monolog/monolog/src/Monolog/Formatter/WildfireFormatter.php000064400000006304151327705700021505 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog\Formatter;

use WPvividMonolog\Logger;

/**
 * Serializes a log message according to Wildfire's header requirements
 *
 * @author Eric Clemmons (@ericclemmons) <eric@uxdriven.com>
 * @author Christophe Coevoet <stof@notk.org>
 * @author Kirill chEbba Chebunin <iam@chebba.org>
 */
class WildfireFormatter extends NormalizerFormatter
{
    const TABLE = 'table';

    /**
     * Translates Monolog log levels to Wildfire levels.
     */
    private $logLevels = array(
        Logger::DEBUG     => 'LOG',
        Logger::INFO      => 'INFO',
        Logger::NOTICE    => 'INFO',
        Logger::WARNING   => 'WARN',
        Logger::ERROR     => 'ERROR',
        Logger::CRITICAL  => 'ERROR',
        Logger::ALERT     => 'ERROR',
        Logger::EMERGENCY => 'ERROR',
    );

    /**
     * {@inheritdoc}
     */
    public function format(array $record)
    {
        // Retrieve the line and file if set and remove them from the formatted extra
        $file = $line = '';
        if (isset($record['extra']['file'])) {
            $file = $record['extra']['file'];
            unset($record['extra']['file']);
        }
        if (isset($record['extra']['line'])) {
            $line = $record['extra']['line'];
            unset($record['extra']['line']);
        }

        $record = $this->normalize($record);
        $message = array('message' => $record['message']);
        $handleError = false;
        if ($record['context']) {
            $message['context'] = $record['context'];
            $handleError = true;
        }
        if ($record['extra']) {
            $message['extra'] = $record['extra'];
            $handleError = true;
        }
        if (count($message) === 1) {
            $message = reset($message);
        }

        if (isset($record['context'][self::TABLE])) {
            $type  = 'TABLE';
            $label = $record['channel'] .': '. $record['message'];
            $message = $record['context'][self::TABLE];
        } else {
            $type  = $this->logLevels[$record['level']];
            $label = $record['channel'];
        }

        // Create JSON object describing the appearance of the message in the console
        $json = $this->toJson(array(
            array(
                'Type'  => $type,
                'File'  => $file,
                'Line'  => $line,
                'Label' => $label,
            ),
            $message,
        ), $handleError);

        // The message itself is a serialization of the above JSON object + it's length
        return sprintf(
            '%s|%s|',
            strlen($json),
            $json
        );
    }

    public function formatBatch(array $records)
    {
        throw new \BadMethodCallException('Batch formatting does not make sense for the WildfireFormatter');
    }

    protected function normalize($data, $depth = 0)
    {
        if (is_object($data) && !$data instanceof \DateTime) {
            return $data;
        }

        return parent::normalize($data, $depth);
    }
}
vendor/monolog/monolog/src/Monolog/Formatter/LogstashFormatter.php000064400000012302151327705700021517 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog\Formatter;

/**
 * Serializes a log message to Logstash Event Format
 *
 * @see http://logstash.net/
 * @see https://github.com/logstash/logstash/blob/master/lib/logstash/event.rb
 *
 * @author Tim Mower <timothy.mower@gmail.com>
 */
class LogstashFormatter extends NormalizerFormatter
{
    const V0 = 0;
    const V1 = 1;

    /**
     * @var string the name of the system for the Logstash log message, used to fill the @source field
     */
    protected $systemName;

    /**
     * @var string an application name for the Logstash log message, used to fill the @type field
     */
    protected $applicationName;

    /**
     * @var string a prefix for 'extra' fields from the Monolog record (optional)
     */
    protected $extraPrefix;

    /**
     * @var string a prefix for 'context' fields from the Monolog record (optional)
     */
    protected $contextPrefix;

    /**
     * @var int logstash format version to use
     */
    protected $version;

    /**
     * @param string $applicationName the application that sends the data, used as the "type" field of logstash
     * @param string $systemName      the system/machine name, used as the "source" field of logstash, defaults to the hostname of the machine
     * @param string $extraPrefix     prefix for extra keys inside logstash "fields"
     * @param string $contextPrefix   prefix for context keys inside logstash "fields", defaults to ctxt_
     * @param int    $version         the logstash format version to use, defaults to 0
     */
    public function __construct($applicationName, $systemName = null, $extraPrefix = null, $contextPrefix = 'ctxt_', $version = self::V0)
    {
        // logstash requires a ISO 8601 format date with optional millisecond precision.
        parent::__construct('Y-m-d\TH:i:s.uP');

        $this->systemName = $systemName ?: gethostname();
        $this->applicationName = $applicationName;
        $this->extraPrefix = $extraPrefix;
        $this->contextPrefix = $contextPrefix;
        $this->version = $version;
    }

    /**
     * {@inheritdoc}
     */
    public function format(array $record)
    {
        $record = parent::format($record);

        if ($this->version === self::V1) {
            $message = $this->formatV1($record);
        } else {
            $message = $this->formatV0($record);
        }

        return $this->toJson($message) . "\n";
    }

    protected function formatV0(array $record)
    {
        if (empty($record['datetime'])) {
            $record['datetime'] = gmdate('c');
        }
        $message = array(
            '@timestamp' => $record['datetime'],
            '@source' => $this->systemName,
            '@fields' => array(),
        );
        if (isset($record['message'])) {
            $message['@message'] = $record['message'];
        }
        if (isset($record['channel'])) {
            $message['@tags'] = array($record['channel']);
            $message['@fields']['channel'] = $record['channel'];
        }
        if (isset($record['level'])) {
            $message['@fields']['level'] = $record['level'];
        }
        if ($this->applicationName) {
            $message['@type'] = $this->applicationName;
        }
        if (isset($record['extra']['server'])) {
            $message['@source_host'] = $record['extra']['server'];
        }
        if (isset($record['extra']['url'])) {
            $message['@source_path'] = $record['extra']['url'];
        }
        if (!empty($record['extra'])) {
            foreach ($record['extra'] as $key => $val) {
                $message['@fields'][$this->extraPrefix . $key] = $val;
            }
        }
        if (!empty($record['context'])) {
            foreach ($record['context'] as $key => $val) {
                $message['@fields'][$this->contextPrefix . $key] = $val;
            }
        }

        return $message;
    }

    protected function formatV1(array $record)
    {
        if (empty($record['datetime'])) {
            $record['datetime'] = gmdate('c');
        }
        $message = array(
            '@timestamp' => $record['datetime'],
            '@version' => 1,
            'host' => $this->systemName,
        );
        if (isset($record['message'])) {
            $message['message'] = $record['message'];
        }
        if (isset($record['channel'])) {
            $message['type'] = $record['channel'];
            $message['channel'] = $record['channel'];
        }
        if (isset($record['level_name'])) {
            $message['level'] = $record['level_name'];
        }
        if ($this->applicationName) {
            $message['type'] = $this->applicationName;
        }
        if (!empty($record['extra'])) {
            foreach ($record['extra'] as $key => $val) {
                $message[$this->extraPrefix . $key] = $val;
            }
        }
        if (!empty($record['context'])) {
            foreach ($record['context'] as $key => $val) {
                $message[$this->contextPrefix . $key] = $val;
            }
        }

        return $message;
    }
}
vendor/monolog/monolog/src/Monolog/Formatter/LineFormatter.php000064400000012714151327705700020631 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog\Formatter;

use WPvividMonolog\Utils;

/**
 * Formats incoming records into a one-line string
 *
 * This is especially useful for logging to files
 *
 * @author Jordi Boggiano <j.boggiano@seld.be>
 * @author Christophe Coevoet <stof@notk.org>
 */
class LineFormatter extends NormalizerFormatter
{
    const SIMPLE_FORMAT = "[%datetime%] %channel%.%level_name%: %message% %context% %extra%\n";

    protected $format;
    protected $allowInlineLineBreaks;
    protected $ignoreEmptyContextAndExtra;
    protected $includeStacktraces;

    /**
     * @param string $format                     The format of the message
     * @param string $dateFormat                 The format of the timestamp: one supported by DateTime::format
     * @param bool   $allowInlineLineBreaks      Whether to allow inline line breaks in log entries
     * @param bool   $ignoreEmptyContextAndExtra
     */
    public function __construct($format = null, $dateFormat = null, $allowInlineLineBreaks = false, $ignoreEmptyContextAndExtra = false)
    {
        $this->format = $format ?: static::SIMPLE_FORMAT;
        $this->allowInlineLineBreaks = $allowInlineLineBreaks;
        $this->ignoreEmptyContextAndExtra = $ignoreEmptyContextAndExtra;
        parent::__construct($dateFormat);
    }

    public function includeStacktraces($include = true)
    {
        $this->includeStacktraces = $include;
        if ($this->includeStacktraces) {
            $this->allowInlineLineBreaks = true;
        }
    }

    public function allowInlineLineBreaks($allow = true)
    {
        $this->allowInlineLineBreaks = $allow;
    }

    public function ignoreEmptyContextAndExtra($ignore = true)
    {
        $this->ignoreEmptyContextAndExtra = $ignore;
    }

    /**
     * {@inheritdoc}
     */
    public function format(array $record)
    {
        $vars = parent::format($record);

        $output = $this->format;

        foreach ($vars['extra'] as $var => $val) {
            if (false !== strpos($output, '%extra.'.$var.'%')) {
                $output = str_replace('%extra.'.$var.'%', $this->stringify($val), $output);
                unset($vars['extra'][$var]);
            }
        }


        foreach ($vars['context'] as $var => $val) {
            if (false !== strpos($output, '%context.'.$var.'%')) {
                $output = str_replace('%context.'.$var.'%', $this->stringify($val), $output);
                unset($vars['context'][$var]);
            }
        }

        if ($this->ignoreEmptyContextAndExtra) {
            if (empty($vars['context'])) {
                unset($vars['context']);
                $output = str_replace('%context%', '', $output);
            }

            if (empty($vars['extra'])) {
                unset($vars['extra']);
                $output = str_replace('%extra%', '', $output);
            }
        }

        foreach ($vars as $var => $val) {
            if (false !== strpos($output, '%'.$var.'%')) {
                $output = str_replace('%'.$var.'%', $this->stringify($val), $output);
            }
        }

        // remove leftover %extra.xxx% and %context.xxx% if any
        if (false !== strpos($output, '%')) {
            $output = preg_replace('/%(?:extra|context)\..+?%/', '', $output);
        }

        return $output;
    }

    public function formatBatch(array $records)
    {
        $message = '';
        foreach ($records as $record) {
            $message .= $this->format($record);
        }

        return $message;
    }

    public function stringify($value)
    {
        return $this->replaceNewlines($this->convertToString($value));
    }

    protected function normalizeException($e)
    {
        // TODO 2.0 only check for Throwable
        if (!$e instanceof \Exception && !$e instanceof \Throwable) {
            throw new \InvalidArgumentException('Exception/Throwable expected, got '.gettype($e).' / '.Utils::getClass($e));
        }

        $previousText = '';
        if ($previous = $e->getPrevious()) {
            do {
                $previousText .= ', '.Utils::getClass($previous).'(code: '.$previous->getCode().'): '.$previous->getMessage().' at '.$previous->getFile().':'.$previous->getLine();
            } while ($previous = $previous->getPrevious());
        }

        $str = '[object] ('.Utils::getClass($e).'(code: '.$e->getCode().'): '.$e->getMessage().' at '.$e->getFile().':'.$e->getLine().$previousText.')';
        if ($this->includeStacktraces) {
            $str .= "\n[stacktrace]\n".$e->getTraceAsString()."\n";
        }

        return $str;
    }

    protected function convertToString($data)
    {
        if (null === $data || is_bool($data)) {
            return var_export($data, true);
        }

        if (is_scalar($data)) {
            return (string) $data;
        }

        if (version_compare(PHP_VERSION, '5.4.0', '>=')) {
            return $this->toJson($data, true);
        }

        return str_replace('\\/', '/', @json_encode($data));
    }

    protected function replaceNewlines($str)
    {
        if ($this->allowInlineLineBreaks) {
            if (0 === strpos($str, '{')) {
                return str_replace(array('\r', '\n'), array("\r", "\n"), $str);
            }

            return $str;
        }

        return str_replace(array("\r\n", "\r", "\n"), ' ', $str);
    }
}
vendor/monolog/monolog/src/Monolog/Formatter/LogglyFormatter.php000064400000002461151327705700021175 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog\Formatter;

/**
 * Encodes message information into JSON in a format compatible with Loggly.
 *
 * @author Adam Pancutt <adam@pancutt.com>
 */
class LogglyFormatter extends JsonFormatter
{
    /**
     * Overrides the default batch mode to new lines for compatibility with the
     * Loggly bulk API.
     *
     * @param int $batchMode
     */
    public function __construct($batchMode = self::BATCH_MODE_NEWLINES, $appendNewline = false)
    {
        parent::__construct($batchMode, $appendNewline);
    }

    /**
     * Appends the 'timestamp' parameter for indexing by Loggly.
     *
     * @see https://www.loggly.com/docs/automated-parsing/#json
     * @see \Monolog\Formatter\JsonFormatter::format()
     */
    public function format(array $record)
    {
        if (isset($record["datetime"]) && ($record["datetime"] instanceof \DateTime)) {
            $record["timestamp"] = $record["datetime"]->format("Y-m-d\TH:i:s.uO");
            // TODO 2.0 unset the 'datetime' parameter, retained for BC
        }

        return parent::format($record);
    }
}
vendor/monolog/monolog/src/Monolog/Formatter/FlowdockFormatter.php000064400000004773151327705700021520 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog\Formatter;

/**
 * formats the record to be used in the FlowdockHandler
 *
 * @author Dominik Liebler <liebler.dominik@gmail.com>
 */
class FlowdockFormatter implements FormatterInterface
{
    /**
     * @var string
     */
    private $source;

    /**
     * @var string
     */
    private $sourceEmail;

    /**
     * @param string $source
     * @param string $sourceEmail
     */
    public function __construct($source, $sourceEmail)
    {
        $this->source = $source;
        $this->sourceEmail = $sourceEmail;
    }

    /**
     * {@inheritdoc}
     */
    public function format(array $record)
    {
        $tags = array(
            '#logs',
            '#' . strtolower($record['level_name']),
            '#' . $record['channel'],
        );

        foreach ($record['extra'] as $value) {
            $tags[] = '#' . $value;
        }

        $subject = sprintf(
            'in %s: %s - %s',
            $this->source,
            $record['level_name'],
            $this->getShortMessage($record['message'])
        );

        $record['flowdock'] = array(
            'source' => $this->source,
            'from_address' => $this->sourceEmail,
            'subject' => $subject,
            'content' => $record['message'],
            'tags' => $tags,
            'project' => $this->source,
        );

        return $record;
    }

    /**
     * {@inheritdoc}
     */
    public function formatBatch(array $records)
    {
        $formatted = array();

        foreach ($records as $record) {
            $formatted[] = $this->format($record);
        }

        return $formatted;
    }

    /**
     * @param string $message
     *
     * @return string
     */
    public function getShortMessage($message)
    {
        static $hasMbString;

        if (null === $hasMbString) {
            $hasMbString = function_exists('mb_strlen');
        }

        $maxLength = 45;

        if ($hasMbString) {
            if (mb_strlen($message, 'UTF-8') > $maxLength) {
                $message = mb_substr($message, 0, $maxLength - 4, 'UTF-8') . ' ...';
            }
        } else {
            if (strlen($message) > $maxLength) {
                $message = substr($message, 0, $maxLength - 4) . ' ...';
            }
        }

        return $message;
    }
}
vendor/monolog/monolog/src/Monolog/Formatter/ElasticaFormatter.php000064400000003464151327705700021471 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog\Formatter;

use Elastica\Document;

/**
 * Format a log message into an Elastica Document
 *
 * @author Jelle Vink <jelle.vink@gmail.com>
 */
class ElasticaFormatter extends NormalizerFormatter
{
    /**
     * @var string Elastic search index name
     */
    protected $index;

    /**
     * @var string Elastic search document type
     */
    protected $type;

    /**
     * @param string $index Elastic Search index name
     * @param string $type  Elastic Search document type
     */
    public function __construct($index, $type)
    {
        // elasticsearch requires a ISO 8601 format date with optional millisecond precision.
        parent::__construct('Y-m-d\TH:i:s.uP');

        $this->index = $index;
        $this->type = $type;
    }

    /**
     * {@inheritdoc}
     */
    public function format(array $record)
    {
        $record = parent::format($record);

        return $this->getDocument($record);
    }

    /**
     * Getter index
     * @return string
     */
    public function getIndex()
    {
        return $this->index;
    }

    /**
     * Getter type
     * @return string
     */
    public function getType()
    {
        return $this->type;
    }

    /**
     * Convert a log message into an Elastica Document
     *
     * @param  array    $record Log message
     * @return Document
     */
    protected function getDocument($record)
    {
        $document = new Document();
        $document->setData($record);
        $document->setType($this->type);
        $document->setIndex($this->index);

        return $document;
    }
}
vendor/monolog/monolog/src/Monolog/Formatter/FluentdFormatter.php000064400000004172151327705700021342 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog\Formatter;

/**
 * Class FluentdFormatter
 *
 * Serializes a log message to Fluentd unix socket protocol
 *
 * Fluentd config:
 *
 * <source>
 *  type unix
 *  path /var/run/td-agent/td-agent.sock
 * </source>
 *
 * Monolog setup:
 *
 * $logger = new Monolog\Logger('fluent.tag');
 * $fluentHandler = new Monolog\Handler\SocketHandler('unix:///var/run/td-agent/td-agent.sock');
 * $fluentHandler->setFormatter(new Monolog\Formatter\FluentdFormatter());
 * $logger->pushHandler($fluentHandler);
 *
 * @author Andrius Putna <fordnox@gmail.com>
 */
class FluentdFormatter implements FormatterInterface
{
    /**
     * @var bool $levelTag should message level be a part of the fluentd tag
     */
    protected $levelTag = false;

    public function __construct($levelTag = false)
    {
        if (!function_exists('json_encode')) {
            throw new \RuntimeException('PHP\'s json extension is required to use Monolog\'s FluentdUnixFormatter');
        }

        $this->levelTag = (bool) $levelTag;
    }

    public function isUsingLevelsInTag()
    {
        return $this->levelTag;
    }

    public function format(array $record)
    {
        $tag = $record['channel'];
        if ($this->levelTag) {
            $tag .= '.' . strtolower($record['level_name']);
        }

        $message = array(
            'message' => $record['message'],
            'context' => $record['context'],
            'extra' => $record['extra'],
        );

        if (!$this->levelTag) {
            $message['level'] = $record['level'];
            $message['level_name'] = $record['level_name'];
        }

        return json_encode(array($tag, $record['datetime']->getTimestamp(), $message));
    }

    public function formatBatch(array $records)
    {
        $message = '';
        foreach ($records as $record) {
            $message .= $this->format($record);
        }

        return $message;
    }
}
vendor/monolog/monolog/src/Monolog/Formatter/NormalizerFormatter.php000064400000023662151327705700022070 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog\Formatter;

use Exception;
use WPvividMonolog\Utils;

/**
 * Normalizes incoming records to remove objects/resources so it's easier to dump to various targets
 *
 * @author Jordi Boggiano <j.boggiano@seld.be>
 */
class NormalizerFormatter implements FormatterInterface
{
    const SIMPLE_DATE = "Y-m-d H:i:s";

    protected $dateFormat;

    /**
     * @param string $dateFormat The format of the timestamp: one supported by DateTime::format
     */
    public function __construct($dateFormat = null)
    {
        $this->dateFormat = $dateFormat ?: static::SIMPLE_DATE;
        if (!function_exists('json_encode')) {
            throw new \RuntimeException('PHP\'s json extension is required to use Monolog\'s NormalizerFormatter');
        }
    }

    /**
     * {@inheritdoc}
     */
    public function format(array $record)
    {
        return $this->normalize($record);
    }

    /**
     * {@inheritdoc}
     */
    public function formatBatch(array $records)
    {
        foreach ($records as $key => $record) {
            $records[$key] = $this->format($record);
        }

        return $records;
    }

    protected function normalize($data, $depth = 0)
    {
        if ($depth > 9) {
            return 'Over 9 levels deep, aborting normalization';
        }

        if (null === $data || is_scalar($data)) {
            if (is_float($data)) {
                if (is_infinite($data)) {
                    return ($data > 0 ? '' : '-') . 'INF';
                }
                if (is_nan($data)) {
                    return 'NaN';
                }
            }

            return $data;
        }

        if (is_array($data)) {
            $normalized = array();

            $count = 1;
            foreach ($data as $key => $value) {
                if ($count++ > 1000) {
                    $normalized['...'] = 'Over 1000 items ('.count($data).' total), aborting normalization';
                    break;
                }

                $normalized[$key] = $this->normalize($value, $depth+1);
            }

            return $normalized;
        }

        if ($data instanceof \DateTime) {
            return $data->format($this->dateFormat);
        }

        if (is_object($data)) {
            // TODO 2.0 only check for Throwable
            if ($data instanceof Exception || (PHP_VERSION_ID > 70000 && $data instanceof \Throwable)) {
                return $this->normalizeException($data);
            }

            // non-serializable objects that implement __toString stringified
            if (method_exists($data, '__toString') && !$data instanceof \JsonSerializable) {
                $value = $data->__toString();
            } else {
                // the rest is json-serialized in some way
                $value = $this->toJson($data, true);
            }

            return sprintf("[object] (%s: %s)", Utils::getClass($data), $value);
        }

        if (is_resource($data)) {
            return sprintf('[resource] (%s)', get_resource_type($data));
        }

        return '[unknown('.gettype($data).')]';
    }

    protected function normalizeException($e)
    {
        // TODO 2.0 only check for Throwable
        if (!$e instanceof Exception && !$e instanceof \Throwable) {
            throw new \InvalidArgumentException('Exception/Throwable expected, got '.gettype($e).' / '.Utils::getClass($e));
        }

        $data = array(
            'class' => Utils::getClass($e),
            'message' => $e->getMessage(),
            'code' => $e->getCode(),
            'file' => $e->getFile().':'.$e->getLine(),
        );

        if ($e instanceof \SoapFault) {
            if (isset($e->faultcode)) {
                $data['faultcode'] = $e->faultcode;
            }

            if (isset($e->faultactor)) {
                $data['faultactor'] = $e->faultactor;
            }

            if (isset($e->detail)) {
                $data['detail'] = $e->detail;
            }
        }

        $trace = $e->getTrace();
        foreach ($trace as $frame) {
            if (isset($frame['file'])) {
                $data['trace'][] = $frame['file'].':'.$frame['line'];
            } elseif (isset($frame['function']) && $frame['function'] === '{closure}') {
                // Simplify closures handling
                $data['trace'][] = $frame['function'];
            } else {
                if (isset($frame['args'])) {
                    // Make sure that objects present as arguments are not serialized nicely but rather only
                    // as a class name to avoid any unexpected leak of sensitive information
                    $frame['args'] = array_map(function ($arg) {
                        if (is_object($arg) && !($arg instanceof \DateTime || $arg instanceof \DateTimeInterface)) {
                            return sprintf("[object] (%s)", Utils::getClass($arg));
                        }

                        return $arg;
                    }, $frame['args']);
                }
                // We should again normalize the frames, because it might contain invalid items
                $data['trace'][] = $this->toJson($this->normalize($frame), true);
            }
        }

        if ($previous = $e->getPrevious()) {
            $data['previous'] = $this->normalizeException($previous);
        }

        return $data;
    }

    /**
     * Return the JSON representation of a value
     *
     * @param  mixed             $data
     * @param  bool              $ignoreErrors
     * @throws \RuntimeException if encoding fails and errors are not ignored
     * @return string
     */
    protected function toJson($data, $ignoreErrors = false)
    {
        // suppress json_encode errors since it's twitchy with some inputs
        if ($ignoreErrors) {
            return @$this->jsonEncode($data);
        }

        $json = $this->jsonEncode($data);

        if ($json === false) {
            $json = $this->handleJsonError(json_last_error(), $data);
        }

        return $json;
    }

    /**
     * @param  mixed  $data
     * @return string JSON encoded data or null on failure
     */
    private function jsonEncode($data)
    {
        if (version_compare(PHP_VERSION, '5.4.0', '>=')) {
            return json_encode($data, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
        }

        return json_encode($data);
    }

    /**
     * Handle a json_encode failure.
     *
     * If the failure is due to invalid string encoding, try to clean the
     * input and encode again. If the second encoding attempt fails, the
     * inital error is not encoding related or the input can't be cleaned then
     * raise a descriptive exception.
     *
     * @param  int               $code return code of json_last_error function
     * @param  mixed             $data data that was meant to be encoded
     * @throws \RuntimeException if failure can't be corrected
     * @return string            JSON encoded data after error correction
     */
    private function handleJsonError($code, $data)
    {
        if ($code !== JSON_ERROR_UTF8) {
            $this->throwEncodeError($code, $data);
        }

        if (is_string($data)) {
            $this->detectAndCleanUtf8($data);
        } elseif (is_array($data)) {
            array_walk_recursive($data, array($this, 'detectAndCleanUtf8'));
        } else {
            $this->throwEncodeError($code, $data);
        }

        $json = $this->jsonEncode($data);

        if ($json === false) {
            $this->throwEncodeError(json_last_error(), $data);
        }

        return $json;
    }

    /**
     * Throws an exception according to a given code with a customized message
     *
     * @param  int               $code return code of json_last_error function
     * @param  mixed             $data data that was meant to be encoded
     * @throws \RuntimeException
     */
    private function throwEncodeError($code, $data)
    {
        switch ($code) {
            case JSON_ERROR_DEPTH:
                $msg = 'Maximum stack depth exceeded';
                break;
            case JSON_ERROR_STATE_MISMATCH:
                $msg = 'Underflow or the modes mismatch';
                break;
            case JSON_ERROR_CTRL_CHAR:
                $msg = 'Unexpected control character found';
                break;
            case JSON_ERROR_UTF8:
                $msg = 'Malformed UTF-8 characters, possibly incorrectly encoded';
                break;
            default:
                $msg = 'Unknown error';
        }

        throw new \RuntimeException('JSON encoding failed: '.$msg.'. Encoding: '.var_export($data, true));
    }

    /**
     * Detect invalid UTF-8 string characters and convert to valid UTF-8.
     *
     * Valid UTF-8 input will be left unmodified, but strings containing
     * invalid UTF-8 codepoints will be reencoded as UTF-8 with an assumed
     * original encoding of ISO-8859-15. This conversion may result in
     * incorrect output if the actual encoding was not ISO-8859-15, but it
     * will be clean UTF-8 output and will not rely on expensive and fragile
     * detection algorithms.
     *
     * Function converts the input in place in the passed variable so that it
     * can be used as a callback for array_walk_recursive.
     *
     * @param mixed &$data Input to check and convert if needed
     * @private
     */
    public function detectAndCleanUtf8(&$data)
    {
        if (is_string($data) && !preg_match('//u', $data)) {
            $data = preg_replace_callback(
                '/[\x80-\xFF]+/',
                function ($m) { return utf8_encode($m[0]); },
                $data
            );
            $data = str_replace(
                array('¤', '¦', '¨', '´', '¸', '¼', '½', '¾'),
                array('€', 'Š', 'š', 'Ž', 'ž', 'Œ', 'œ', 'Ÿ'),
                $data
            );
        }
    }
}
vendor/monolog/monolog/src/Monolog/Formatter/FormatterInterface.php000064400000001432151327705700021635 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog\Formatter;

/**
 * Interface for formatters
 *
 * @author Jordi Boggiano <j.boggiano@seld.be>
 */
interface FormatterInterface
{
    /**
     * Formats a log record.
     *
     * @param  array $record A record to format
     * @return mixed The formatted record
     */
    public function format(array $record);

    /**
     * Formats a set of log records.
     *
     * @param  array $records A set of records to format
     * @return mixed The formatted set of records
     */
    public function formatBatch(array $records);
}
vendor/monolog/monolog/src/Monolog/Formatter/HtmlFormatter.php000064400000010751151327705700020645 0ustar00<?php
/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog\Formatter;

use WPvividMonolog\Logger;

/**
 * Formats incoming records into an HTML table
 *
 * This is especially useful for html email logging
 *
 * @author Tiago Brito <tlfbrito@gmail.com>
 */
class HtmlFormatter extends NormalizerFormatter
{
    /**
     * Translates Monolog log levels to html color priorities.
     */
    protected $logLevels = array(
        Logger::DEBUG     => '#cccccc',
        Logger::INFO      => '#468847',
        Logger::NOTICE    => '#3a87ad',
        Logger::WARNING   => '#c09853',
        Logger::ERROR     => '#f0ad4e',
        Logger::CRITICAL  => '#FF7708',
        Logger::ALERT     => '#C12A19',
        Logger::EMERGENCY => '#000000',
    );

    /**
     * @param string $dateFormat The format of the timestamp: one supported by DateTime::format
     */
    public function __construct($dateFormat = null)
    {
        parent::__construct($dateFormat);
    }

    /**
     * Creates an HTML table row
     *
     * @param  string $th       Row header content
     * @param  string $td       Row standard cell content
     * @param  bool   $escapeTd false if td content must not be html escaped
     * @return string
     */
    protected function addRow($th, $td = ' ', $escapeTd = true)
    {
        $th = htmlspecialchars($th, ENT_NOQUOTES, 'UTF-8');
        if ($escapeTd) {
            $td = '<pre>'.htmlspecialchars($td, ENT_NOQUOTES, 'UTF-8').'</pre>';
        }

        return "<tr style=\"padding: 4px;text-align: left;\">\n<th style=\"vertical-align: top;background: #ccc;color: #000\" width=\"100\">$th:</th>\n<td style=\"padding: 4px;text-align: left;vertical-align: top;background: #eee;color: #000\">".$td."</td>\n</tr>";
    }

    /**
     * Create a HTML h1 tag
     *
     * @param  string $title Text to be in the h1
     * @param  int    $level Error level
     * @return string
     */
    protected function addTitle($title, $level)
    {
        $title = htmlspecialchars($title, ENT_NOQUOTES, 'UTF-8');

        return '<h1 style="background: '.$this->logLevels[$level].';color: #ffffff;padding: 5px;" class="monolog-output">'.$title.'</h1>';
    }

    /**
     * Formats a log record.
     *
     * @param  array $record A record to format
     * @return mixed The formatted record
     */
    public function format(array $record)
    {
        $output = $this->addTitle($record['level_name'], $record['level']);
        $output .= '<table cellspacing="1" width="100%" class="monolog-output">';

        $output .= $this->addRow('Message', (string) $record['message']);
        $output .= $this->addRow('Time', $record['datetime']->format($this->dateFormat));
        $output .= $this->addRow('Channel', $record['channel']);
        if ($record['context']) {
            $embeddedTable = '<table cellspacing="1" width="100%">';
            foreach ($record['context'] as $key => $value) {
                $embeddedTable .= $this->addRow($key, $this->convertToString($value));
            }
            $embeddedTable .= '</table>';
            $output .= $this->addRow('Context', $embeddedTable, false);
        }
        if ($record['extra']) {
            $embeddedTable = '<table cellspacing="1" width="100%">';
            foreach ($record['extra'] as $key => $value) {
                $embeddedTable .= $this->addRow($key, $this->convertToString($value));
            }
            $embeddedTable .= '</table>';
            $output .= $this->addRow('Extra', $embeddedTable, false);
        }

        return $output.'</table>';
    }

    /**
     * Formats a set of log records.
     *
     * @param  array $records A set of records to format
     * @return mixed The formatted set of records
     */
    public function formatBatch(array $records)
    {
        $message = '';
        foreach ($records as $record) {
            $message .= $this->format($record);
        }

        return $message;
    }

    protected function convertToString($data)
    {
        if (null === $data || is_scalar($data)) {
            return (string) $data;
        }

        $data = $this->normalize($data);
        if (version_compare(PHP_VERSION, '5.4.0', '>=')) {
            return json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
        }

        return str_replace('\\/', '/', json_encode($data));
    }
}
vendor/monolog/monolog/src/Monolog/Formatter/MongoDBFormatter.php000064400000006352151327705700021230 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog\Formatter;

use WPvividMonolog\Utils;

/**
 * Formats a record for use with the MongoDBHandler.
 *
 * @author Florian Plattner <me@florianplattner.de>
 */
class MongoDBFormatter implements FormatterInterface
{
    private $exceptionTraceAsString;
    private $maxNestingLevel;

    /**
     * @param int  $maxNestingLevel        0 means infinite nesting, the $record itself is level 1, $record['context'] is 2
     * @param bool $exceptionTraceAsString set to false to log exception traces as a sub documents instead of strings
     */
    public function __construct($maxNestingLevel = 3, $exceptionTraceAsString = true)
    {
        $this->maxNestingLevel = max($maxNestingLevel, 0);
        $this->exceptionTraceAsString = (bool) $exceptionTraceAsString;
    }

    /**
     * {@inheritDoc}
     */
    public function format(array $record)
    {
        return $this->formatArray($record);
    }

    /**
     * {@inheritDoc}
     */
    public function formatBatch(array $records)
    {
        foreach ($records as $key => $record) {
            $records[$key] = $this->format($record);
        }

        return $records;
    }

    protected function formatArray(array $record, $nestingLevel = 0)
    {
        if ($this->maxNestingLevel == 0 || $nestingLevel <= $this->maxNestingLevel) {
            foreach ($record as $name => $value) {
                if ($value instanceof \DateTime) {
                    $record[$name] = $this->formatDate($value, $nestingLevel + 1);
                } elseif ($value instanceof \Exception) {
                    $record[$name] = $this->formatException($value, $nestingLevel + 1);
                } elseif (is_array($value)) {
                    $record[$name] = $this->formatArray($value, $nestingLevel + 1);
                } elseif (is_object($value)) {
                    $record[$name] = $this->formatObject($value, $nestingLevel + 1);
                }
            }
        } else {
            $record = '[...]';
        }

        return $record;
    }

    protected function formatObject($value, $nestingLevel)
    {
        $objectVars = get_object_vars($value);
        $objectVars['class'] = Utils::getClass($value);

        return $this->formatArray($objectVars, $nestingLevel);
    }

    protected function formatException(\Exception $exception, $nestingLevel)
    {
        $formattedException = array(
            'class' => Utils::getClass($exception),
            'message' => $exception->getMessage(),
            'code' => $exception->getCode(),
            'file' => $exception->getFile() . ':' . $exception->getLine(),
        );

        if ($this->exceptionTraceAsString === true) {
            $formattedException['trace'] = $exception->getTraceAsString();
        } else {
            $formattedException['trace'] = $exception->getTrace();
        }

        return $this->formatArray($formattedException, $nestingLevel);
    }

    protected function formatDate(\DateTime $value, $nestingLevel)
    {
        return new \MongoDate($value->getTimestamp());
    }
}
vendor/monolog/monolog/src/Monolog/Formatter/JsonFormatter.php000064400000013054151327705700020651 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog\Formatter;

use Exception;
use WPvividMonolog\Utils;
use Throwable;

/**
 * Encodes whatever record data is passed to it as json
 *
 * This can be useful to log to databases or remote APIs
 *
 * @author Jordi Boggiano <j.boggiano@seld.be>
 */
class JsonFormatter extends NormalizerFormatter
{
    const BATCH_MODE_JSON = 1;
    const BATCH_MODE_NEWLINES = 2;

    protected $batchMode;
    protected $appendNewline;

    /**
     * @var bool
     */
    protected $includeStacktraces = false;

    /**
     * @param int $batchMode
     * @param bool $appendNewline
     */
    public function __construct($batchMode = self::BATCH_MODE_JSON, $appendNewline = true)
    {
        $this->batchMode = $batchMode;
        $this->appendNewline = $appendNewline;
    }

    /**
     * The batch mode option configures the formatting style for
     * multiple records. By default, multiple records will be
     * formatted as a JSON-encoded array. However, for
     * compatibility with some API endpoints, alternative styles
     * are available.
     *
     * @return int
     */
    public function getBatchMode()
    {
        return $this->batchMode;
    }

    /**
     * True if newlines are appended to every formatted record
     *
     * @return bool
     */
    public function isAppendingNewlines()
    {
        return $this->appendNewline;
    }

    /**
     * {@inheritdoc}
     */
    public function format(array $record)
    {
        return $this->toJson($this->normalize($record), true) . ($this->appendNewline ? "\n" : '');
    }

    /**
     * {@inheritdoc}
     */
    public function formatBatch(array $records)
    {
        switch ($this->batchMode) {
            case static::BATCH_MODE_NEWLINES:
                return $this->formatBatchNewlines($records);

            case static::BATCH_MODE_JSON:
            default:
                return $this->formatBatchJson($records);
        }
    }

    /**
     * @param bool $include
     */
    public function includeStacktraces($include = true)
    {
        $this->includeStacktraces = $include;
    }

    /**
     * Return a JSON-encoded array of records.
     *
     * @param  array  $records
     * @return string
     */
    protected function formatBatchJson(array $records)
    {
        return $this->toJson($this->normalize($records), true);
    }

    /**
     * Use new lines to separate records instead of a
     * JSON-encoded array.
     *
     * @param  array  $records
     * @return string
     */
    protected function formatBatchNewlines(array $records)
    {
        $instance = $this;

        $oldNewline = $this->appendNewline;
        $this->appendNewline = false;
        array_walk($records, function (&$value, $key) use ($instance) {
            $value = $instance->format($value);
        });
        $this->appendNewline = $oldNewline;

        return implode("\n", $records);
    }

    /**
     * Normalizes given $data.
     *
     * @param mixed $data
     *
     * @return mixed
     */
    protected function normalize($data, $depth = 0)
    {
        if ($depth > 9) {
            return 'Over 9 levels deep, aborting normalization';
        }

        if (is_array($data) || $data instanceof \Traversable) {
            $normalized = array();

            $count = 1;
            foreach ($data as $key => $value) {
                if ($count++ > 1000) {
                    $normalized['...'] = 'Over 1000 items ('.count($data).' total), aborting normalization';
                    break;
                }

                $normalized[$key] = $this->normalize($value, $depth+1);
            }

            return $normalized;
        }

        if ($data instanceof Exception || $data instanceof Throwable) {
            return $this->normalizeException($data);
        }

        return $data;
    }

    /**
     * Normalizes given exception with or without its own stack trace based on
     * `includeStacktraces` property.
     *
     * @param Exception|Throwable $e
     *
     * @return array
     */
    protected function normalizeException($e)
    {
        // TODO 2.0 only check for Throwable
        if (!$e instanceof Exception && !$e instanceof Throwable) {
            throw new \InvalidArgumentException('Exception/Throwable expected, got '.gettype($e).' / '.Utils::getClass($e));
        }

        $data = array(
            'class' => Utils::getClass($e),
            'message' => $e->getMessage(),
            'code' => $e->getCode(),
            'file' => $e->getFile().':'.$e->getLine(),
        );

        if ($this->includeStacktraces) {
            $trace = $e->getTrace();
            foreach ($trace as $frame) {
                if (isset($frame['file'])) {
                    $data['trace'][] = $frame['file'].':'.$frame['line'];
                } elseif (isset($frame['function']) && $frame['function'] === '{closure}') {
                    // We should again normalize the frames, because it might contain invalid items
                    $data['trace'][] = $frame['function'];
                } else {
                    // We should again normalize the frames, because it might contain invalid items
                    $data['trace'][] = $this->normalize($frame);
                }
            }
        }

        if ($previous = $e->getPrevious()) {
            $data['previous'] = $this->normalizeException($previous);
        }

        return $data;
    }
}
vendor/monolog/monolog/src/Monolog/Formatter/ScalarFormatter.php000064400000002027151327705700021143 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog\Formatter;

/**
 * Formats data into an associative array of scalar values.
 * Objects and arrays will be JSON encoded.
 *
 * @author Andrew Lawson <adlawson@gmail.com>
 */
class ScalarFormatter extends NormalizerFormatter
{
    /**
     * {@inheritdoc}
     */
    public function format(array $record)
    {
        foreach ($record as $key => $value) {
            $record[$key] = $this->normalizeValue($value);
        }

        return $record;
    }

    /**
     * @param  mixed $value
     * @return mixed
     */
    protected function normalizeValue($value)
    {
        $normalized = $this->normalize($value);

        if (is_array($normalized) || is_object($normalized)) {
            return $this->toJson($normalized, true);
        }

        return $normalized;
    }
}
vendor/monolog/monolog/src/Monolog/Formatter/GelfMessageFormatter.php000064400000010506151327705700022121 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog\Formatter;

use WPvividMonolog\Logger;
use Gelf\Message;

/**
 * Serializes a log message to GELF
 * @see http://www.graylog2.org/about/gelf
 *
 * @author Matt Lehner <mlehner@gmail.com>
 */
class GelfMessageFormatter extends NormalizerFormatter
{
    const DEFAULT_MAX_LENGTH = 32766;

    /**
     * @var string the name of the system for the Gelf log message
     */
    protected $systemName;

    /**
     * @var string a prefix for 'extra' fields from the Monolog record (optional)
     */
    protected $extraPrefix;

    /**
     * @var string a prefix for 'context' fields from the Monolog record (optional)
     */
    protected $contextPrefix;

    /**
     * @var int max length per field
     */
    protected $maxLength;

    /**
     * Translates Monolog log levels to Graylog2 log priorities.
     */
    private $logLevels = array(
        Logger::DEBUG     => 7,
        Logger::INFO      => 6,
        Logger::NOTICE    => 5,
        Logger::WARNING   => 4,
        Logger::ERROR     => 3,
        Logger::CRITICAL  => 2,
        Logger::ALERT     => 1,
        Logger::EMERGENCY => 0,
    );

    public function __construct($systemName = null, $extraPrefix = null, $contextPrefix = 'ctxt_', $maxLength = null)
    {
        parent::__construct('U.u');

        $this->systemName = $systemName ?: gethostname();

        $this->extraPrefix = $extraPrefix;
        $this->contextPrefix = $contextPrefix;
        $this->maxLength = is_null($maxLength) ? self::DEFAULT_MAX_LENGTH : $maxLength;
    }

    /**
     * {@inheritdoc}
     */
    public function format(array $record)
    {
        $record = parent::format($record);

        if (!isset($record['datetime'], $record['message'], $record['level'])) {
            throw new \InvalidArgumentException('The record should at least contain datetime, message and level keys, '.var_export($record, true).' given');
        }

        $message = new Message();
        $message
            ->setTimestamp($record['datetime'])
            ->setShortMessage((string) $record['message'])
            ->setHost($this->systemName)
            ->setLevel($this->logLevels[$record['level']]);

        // message length + system name length + 200 for padding / metadata 
        $len = 200 + strlen((string) $record['message']) + strlen($this->systemName);

        if ($len > $this->maxLength) {
            $message->setShortMessage(substr($record['message'], 0, $this->maxLength));
        }

        if (isset($record['channel'])) {
            $message->setFacility($record['channel']);
        }
        if (isset($record['extra']['line'])) {
            $message->setLine($record['extra']['line']);
            unset($record['extra']['line']);
        }
        if (isset($record['extra']['file'])) {
            $message->setFile($record['extra']['file']);
            unset($record['extra']['file']);
        }

        foreach ($record['extra'] as $key => $val) {
            $val = is_scalar($val) || null === $val ? $val : $this->toJson($val);
            $len = strlen($this->extraPrefix . $key . $val);
            if ($len > $this->maxLength) {
                $message->setAdditional($this->extraPrefix . $key, substr($val, 0, $this->maxLength));
                break;
            }
            $message->setAdditional($this->extraPrefix . $key, $val);
        }

        foreach ($record['context'] as $key => $val) {
            $val = is_scalar($val) || null === $val ? $val : $this->toJson($val);
            $len = strlen($this->contextPrefix . $key . $val);
            if ($len > $this->maxLength) {
                $message->setAdditional($this->contextPrefix . $key, substr($val, 0, $this->maxLength));
                break;
            }
            $message->setAdditional($this->contextPrefix . $key, $val);
        }

        if (null === $message->getFile() && isset($record['context']['exception']['file'])) {
            if (preg_match("/^(.+):([0-9]+)$/", $record['context']['exception']['file'], $matches)) {
                $message->setFile($matches[1]);
                $message->setLine($matches[2]);
            }
        }

        return $message;
    }
}
vendor/monolog/monolog/src/Monolog/Formatter/ChromePHPFormatter.php000064400000004011151327705700021516 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog\Formatter;

use WPvividMonolog\Logger;

/**
 * Formats a log message according to the ChromePHP array format
 *
 * @author Christophe Coevoet <stof@notk.org>
 */
class ChromePHPFormatter implements FormatterInterface
{
    /**
     * Translates Monolog log levels to Wildfire levels.
     */
    private $logLevels = array(
        Logger::DEBUG     => 'log',
        Logger::INFO      => 'info',
        Logger::NOTICE    => 'info',
        Logger::WARNING   => 'warn',
        Logger::ERROR     => 'error',
        Logger::CRITICAL  => 'error',
        Logger::ALERT     => 'error',
        Logger::EMERGENCY => 'error',
    );

    /**
     * {@inheritdoc}
     */
    public function format(array $record)
    {
        // Retrieve the line and file if set and remove them from the formatted extra
        $backtrace = 'unknown';
        if (isset($record['extra']['file'], $record['extra']['line'])) {
            $backtrace = $record['extra']['file'].' : '.$record['extra']['line'];
            unset($record['extra']['file'], $record['extra']['line']);
        }

        $message = array('message' => $record['message']);
        if ($record['context']) {
            $message['context'] = $record['context'];
        }
        if ($record['extra']) {
            $message['extra'] = $record['extra'];
        }
        if (count($message) === 1) {
            $message = reset($message);
        }

        return array(
            $record['channel'],
            $message,
            $backtrace,
            $this->logLevels[$record['level']],
        );
    }

    public function formatBatch(array $records)
    {
        $formatted = array();

        foreach ($records as $record) {
            $formatted[] = $this->format($record);
        }

        return $formatted;
    }
}
vendor/monolog/monolog/CHANGELOG.md000064400000051452151327705700013014 0ustar00### 1.24.0 (2018-11-05)

  * Added a `ResettableInterface` in order to reset/reset/clear/flush handlers and processors
  * Added a `ProcessorInterface` as an optional way to label a class as being a processor (mostly useful for autowiring dependency containers)
  * Added a way to log signals being received using Monolog\SignalHandler
  * Added ability to customize error handling at the Logger level using Logger::setExceptionHandler
  * Added InsightOpsHandler to migrate users of the LogEntriesHandler
  * Added protection to NormalizerHandler against circular and very deep structures, it now stops normalizing at a depth of 9
  * Added capture of stack traces to ErrorHandler when logging PHP errors
  * Added RavenHandler support for a `contexts` context or extra key to forward that to Sentry's contexts
  * Added forwarding of context info to FluentdFormatter
  * Added SocketHandler::setChunkSize to override the default chunk size in case you must send large log lines to rsyslog for example
  * Added ability to extend/override BrowserConsoleHandler
  * Added SlackWebhookHandler::getWebhookUrl and SlackHandler::getToken to enable class extensibility
  * Added SwiftMailerHandler::getSubjectFormatter to enable class extensibility
  * Dropped official support for HHVM in test builds
  * Fixed normalization of exception traces when call_user_func is used to avoid serializing objects and the data they contain
  * Fixed naming of fields in Slack handler, all field names are now capitalized in all cases
  * Fixed HipChatHandler bug where slack dropped messages randomly
  * Fixed normalization of objects in Slack handlers
  * Fixed support for PHP7's Throwable in NewRelicHandler
  * Fixed race bug when StreamHandler sometimes incorrectly reported it failed to create a directory
  * Fixed table row styling issues in HtmlFormatter
  * Fixed RavenHandler dropping the message when logging exception
  * Fixed WhatFailureGroupHandler skipping processors when using handleBatch
    and implement it where possible
  * Fixed display of anonymous class names

### 1.23.0 (2017-06-19)

  * Improved SyslogUdpHandler's support for RFC5424 and added optional `$ident` argument
  * Fixed GelfHandler truncation to be per field and not per message
  * Fixed compatibility issue with PHP <5.3.6
  * Fixed support for headless Chrome in ChromePHPHandler
  * Fixed support for latest Aws SDK in DynamoDbHandler
  * Fixed support for SwiftMailer 6.0+ in SwiftMailerHandler

### 1.22.1 (2017-03-13)

  * Fixed lots of minor issues in the new Slack integrations
  * Fixed support for allowInlineLineBreaks in LineFormatter when formatting exception backtraces

### 1.22.0 (2016-11-26)

  * Added SlackbotHandler and SlackWebhookHandler to set up Slack integration more easily
  * Added MercurialProcessor to add mercurial revision and branch names to log records
  * Added support for AWS SDK v3 in DynamoDbHandler
  * Fixed fatal errors occuring when normalizing generators that have been fully consumed
  * Fixed RollbarHandler to include a level (rollbar level), monolog_level (original name), channel and datetime (unix)
  * Fixed RollbarHandler not flushing records automatically, calling close() explicitly is not necessary anymore
  * Fixed SyslogUdpHandler to avoid sending empty frames
  * Fixed a few PHP 7.0 and 7.1 compatibility issues

### 1.21.0 (2016-07-29)

  * Break: Reverted the addition of $context when the ErrorHandler handles regular php errors from 1.20.0 as it was causing issues
  * Added support for more formats in RotatingFileHandler::setFilenameFormat as long as they have Y, m and d in order
  * Added ability to format the main line of text the SlackHandler sends by explictly setting a formatter on the handler
  * Added information about SoapFault instances in NormalizerFormatter
  * Added $handleOnlyReportedErrors option on ErrorHandler::registerErrorHandler (default true) to allow logging of all errors no matter the error_reporting level

### 1.20.0 (2016-07-02)

  * Added FingersCrossedHandler::activate() to manually trigger the handler regardless of the activation policy
  * Added StreamHandler::getUrl to retrieve the stream's URL
  * Added ability to override addRow/addTitle in HtmlFormatter
  * Added the $context to context information when the ErrorHandler handles a regular php error
  * Deprecated RotatingFileHandler::setFilenameFormat to only support 3 formats: Y, Y-m and Y-m-d
  * Fixed WhatFailureGroupHandler to work with PHP7 throwables
  * Fixed a few minor bugs

### 1.19.0 (2016-04-12)

  * Break: StreamHandler will not close streams automatically that it does not own. If you pass in a stream (not a path/url), then it will not close it for you. You can retrieve those using getStream() if needed
  * Added DeduplicationHandler to remove duplicate records from notifications across multiple requests, useful for email or other notifications on errors
  * Added ability to use `%message%` and other LineFormatter replacements in the subject line of emails sent with NativeMailHandler and SwiftMailerHandler
  * Fixed HipChatHandler handling of long messages

### 1.18.2 (2016-04-02)

  * Fixed ElasticaFormatter to use more precise dates
  * Fixed GelfMessageFormatter sending too long messages

### 1.18.1 (2016-03-13)

  * Fixed SlackHandler bug where slack dropped messages randomly
  * Fixed RedisHandler issue when using with the PHPRedis extension
  * Fixed AmqpHandler content-type being incorrectly set when using with the AMQP extension
  * Fixed BrowserConsoleHandler regression

### 1.18.0 (2016-03-01)

  * Added optional reduction of timestamp precision via `Logger->useMicrosecondTimestamps(false)`, disabling it gets you a bit of performance boost but reduces the precision to the second instead of microsecond
  * Added possibility to skip some extra stack frames in IntrospectionProcessor if you have some library wrapping Monolog that is always adding frames
  * Added `Logger->withName` to clone a logger (keeping all handlers) with a new name
  * Added FluentdFormatter for the Fluentd unix socket protocol
  * Added HandlerWrapper base class to ease the creation of handler wrappers, just extend it and override as needed
  * Added support for replacing context sub-keys using `%context.*%` in LineFormatter
  * Added support for `payload` context value in RollbarHandler
  * Added setRelease to RavenHandler to describe the application version, sent with every log
  * Added support for `fingerprint` context value in RavenHandler
  * Fixed JSON encoding errors that would gobble up the whole log record, we now handle those more gracefully by dropping chars as needed
  * Fixed write timeouts in SocketHandler and derivatives, set to 10sec by default, lower it with `setWritingTimeout()`
  * Fixed PHP7 compatibility with regard to Exception/Throwable handling in a few places

### 1.17.2 (2015-10-14)

  * Fixed ErrorHandler compatibility with non-Monolog PSR-3 loggers
  * Fixed SlackHandler handling to use slack functionalities better
  * Fixed SwiftMailerHandler bug when sending multiple emails they all had the same id
  * Fixed 5.3 compatibility regression

### 1.17.1 (2015-08-31)

  * Fixed RollbarHandler triggering PHP notices

### 1.17.0 (2015-08-30)

  * Added support for `checksum` and `release` context/extra values in RavenHandler
  * Added better support for exceptions in RollbarHandler
  * Added UidProcessor::getUid
  * Added support for showing the resource type in NormalizedFormatter
  * Fixed IntrospectionProcessor triggering PHP notices

### 1.16.0 (2015-08-09)

  * Added IFTTTHandler to notify ifttt.com triggers
  * Added Logger::setHandlers() to allow setting/replacing all handlers
  * Added $capSize in RedisHandler to cap the log size
  * Fixed StreamHandler creation of directory to only trigger when the first log write happens
  * Fixed bug in the handling of curl failures
  * Fixed duplicate logging of fatal errors when both error and fatal error handlers are registered in monolog's ErrorHandler
  * Fixed missing fatal errors records with handlers that need to be closed to flush log records
  * Fixed TagProcessor::addTags support for associative arrays

### 1.15.0 (2015-07-12)

  * Added addTags and setTags methods to change a TagProcessor
  * Added automatic creation of directories if they are missing for a StreamHandler to open a log file
  * Added retry functionality to Loggly, Cube and Mandrill handlers so they retry up to 5 times in case of network failure
  * Fixed process exit code being incorrectly reset to 0 if ErrorHandler::registerExceptionHandler was used
  * Fixed HTML/JS escaping in BrowserConsoleHandler
  * Fixed JSON encoding errors being silently suppressed (PHP 5.5+ only)

### 1.14.0 (2015-06-19)

  * Added PHPConsoleHandler to send record to Chrome's PHP Console extension and library
  * Added support for objects implementing __toString in the NormalizerFormatter
  * Added support for HipChat's v2 API in HipChatHandler
  * Added Logger::setTimezone() to initialize the timezone monolog should use in case date.timezone isn't correct for your app
  * Added an option to send formatted message instead of the raw record on PushoverHandler via ->useFormattedMessage(true)
  * Fixed curl errors being silently suppressed

### 1.13.1 (2015-03-09)

  * Fixed regression in HipChat requiring a new token to be created

### 1.13.0 (2015-03-05)

  * Added Registry::hasLogger to check for the presence of a logger instance
  * Added context.user support to RavenHandler
  * Added HipChat API v2 support in the HipChatHandler
  * Added NativeMailerHandler::addParameter to pass params to the mail() process
  * Added context data to SlackHandler when $includeContextAndExtra is true
  * Added ability to customize the Swift_Message per-email in SwiftMailerHandler
  * Fixed SwiftMailerHandler to lazily create message instances if a callback is provided
  * Fixed serialization of INF and NaN values in Normalizer and LineFormatter

### 1.12.0 (2014-12-29)

  * Break: HandlerInterface::isHandling now receives a partial record containing only a level key. This was always the intent and does not break any Monolog handler but is strictly speaking a BC break and you should check if you relied on any other field in your own handlers.
  * Added PsrHandler to forward records to another PSR-3 logger
  * Added SamplingHandler to wrap around a handler and include only every Nth record
  * Added MongoDBFormatter to support better storage with MongoDBHandler (it must be enabled manually for now)
  * Added exception codes in the output of most formatters
  * Added LineFormatter::includeStacktraces to enable exception stack traces in logs (uses more than one line)
  * Added $useShortAttachment to SlackHandler to minify attachment size and $includeExtra to append extra data
  * Added $host to HipChatHandler for users of private instances
  * Added $transactionName to NewRelicHandler and support for a transaction_name context value
  * Fixed MandrillHandler to avoid outputing API call responses
  * Fixed some non-standard behaviors in SyslogUdpHandler

### 1.11.0 (2014-09-30)

  * Break: The NewRelicHandler extra and context data are now prefixed with extra_ and context_ to avoid clashes. Watch out if you have scripts reading those from the API and rely on names
  * Added WhatFailureGroupHandler to suppress any exception coming from the wrapped handlers and avoid chain failures if a logging service fails
  * Added MandrillHandler to send emails via the Mandrillapp.com API
  * Added SlackHandler to log records to a Slack.com account
  * Added FleepHookHandler to log records to a Fleep.io account
  * Added LogglyHandler::addTag to allow adding tags to an existing handler
  * Added $ignoreEmptyContextAndExtra to LineFormatter to avoid empty [] at the end
  * Added $useLocking to StreamHandler and RotatingFileHandler to enable flock() while writing
  * Added support for PhpAmqpLib in the AmqpHandler
  * Added FingersCrossedHandler::clear and BufferHandler::clear to reset them between batches in long running jobs
  * Added support for adding extra fields from $_SERVER in the WebProcessor
  * Fixed support for non-string values in PrsLogMessageProcessor
  * Fixed SwiftMailer messages being sent with the wrong date in long running scripts
  * Fixed minor PHP 5.6 compatibility issues
  * Fixed BufferHandler::close being called twice

### 1.10.0 (2014-06-04)

  * Added Logger::getHandlers() and Logger::getProcessors() methods
  * Added $passthruLevel argument to FingersCrossedHandler to let it always pass some records through even if the trigger level is not reached
  * Added support for extra data in NewRelicHandler
  * Added $expandNewlines flag to the ErrorLogHandler to create multiple log entries when a message has multiple lines

### 1.9.1 (2014-04-24)

  * Fixed regression in RotatingFileHandler file permissions
  * Fixed initialization of the BufferHandler to make sure it gets flushed after receiving records
  * Fixed ChromePHPHandler and FirePHPHandler's activation strategies to be more conservative

### 1.9.0 (2014-04-20)

  * Added LogEntriesHandler to send logs to a LogEntries account
  * Added $filePermissions to tweak file mode on StreamHandler and RotatingFileHandler
  * Added $useFormatting flag to MemoryProcessor to make it send raw data in bytes
  * Added support for table formatting in FirePHPHandler via the table context key
  * Added a TagProcessor to add tags to records, and support for tags in RavenHandler
  * Added $appendNewline flag to the JsonFormatter to enable using it when logging to files
  * Added sound support to the PushoverHandler
  * Fixed multi-threading support in StreamHandler
  * Fixed empty headers issue when ChromePHPHandler received no records
  * Fixed default format of the ErrorLogHandler

### 1.8.0 (2014-03-23)

  * Break: the LineFormatter now strips newlines by default because this was a bug, set $allowInlineLineBreaks to true if you need them
  * Added BrowserConsoleHandler to send logs to any browser's console via console.log() injection in the output
  * Added FilterHandler to filter records and only allow those of a given list of levels through to the wrapped handler
  * Added FlowdockHandler to send logs to a Flowdock account
  * Added RollbarHandler to send logs to a Rollbar account
  * Added HtmlFormatter to send prettier log emails with colors for each log level
  * Added GitProcessor to add the current branch/commit to extra record data
  * Added a Monolog\Registry class to allow easier global access to pre-configured loggers
  * Added support for the new official graylog2/gelf-php lib for GelfHandler, upgrade if you can by replacing the mlehner/gelf-php requirement
  * Added support for HHVM
  * Added support for Loggly batch uploads
  * Added support for tweaking the content type and encoding in NativeMailerHandler
  * Added $skipClassesPartials to tweak the ignored classes in the IntrospectionProcessor
  * Fixed batch request support in GelfHandler

### 1.7.0 (2013-11-14)

  * Added ElasticSearchHandler to send logs to an Elastic Search server
  * Added DynamoDbHandler and ScalarFormatter to send logs to Amazon's Dynamo DB
  * Added SyslogUdpHandler to send logs to a remote syslogd server
  * Added LogglyHandler to send logs to a Loggly account
  * Added $level to IntrospectionProcessor so it only adds backtraces when needed
  * Added $version to LogstashFormatter to allow using the new v1 Logstash format
  * Added $appName to NewRelicHandler
  * Added configuration of Pushover notification retries/expiry
  * Added $maxColumnWidth to NativeMailerHandler to change the 70 chars default
  * Added chainability to most setters for all handlers
  * Fixed RavenHandler batch processing so it takes the message from the record with highest priority
  * Fixed HipChatHandler batch processing so it sends all messages at once
  * Fixed issues with eAccelerator
  * Fixed and improved many small things

### 1.6.0 (2013-07-29)

  * Added HipChatHandler to send logs to a HipChat chat room
  * Added ErrorLogHandler to send logs to PHP's error_log function
  * Added NewRelicHandler to send logs to NewRelic's service
  * Added Monolog\ErrorHandler helper class to register a Logger as exception/error/fatal handler
  * Added ChannelLevelActivationStrategy for the FingersCrossedHandler to customize levels by channel
  * Added stack traces output when normalizing exceptions (json output & co)
  * Added Monolog\Logger::API constant (currently 1)
  * Added support for ChromePHP's v4.0 extension
  * Added support for message priorities in PushoverHandler, see $highPriorityLevel and $emergencyLevel
  * Added support for sending messages to multiple users at once with the PushoverHandler
  * Fixed RavenHandler's support for batch sending of messages (when behind a Buffer or FingersCrossedHandler)
  * Fixed normalization of Traversables with very large data sets, only the first 1000 items are shown now
  * Fixed issue in RotatingFileHandler when an open_basedir restriction is active
  * Fixed minor issues in RavenHandler and bumped the API to Raven 0.5.0
  * Fixed SyslogHandler issue when many were used concurrently with different facilities

### 1.5.0 (2013-04-23)

  * Added ProcessIdProcessor to inject the PID in log records
  * Added UidProcessor to inject a unique identifier to all log records of one request/run
  * Added support for previous exceptions in the LineFormatter exception serialization
  * Added Monolog\Logger::getLevels() to get all available levels
  * Fixed ChromePHPHandler so it avoids sending headers larger than Chrome can handle

### 1.4.1 (2013-04-01)

  * Fixed exception formatting in the LineFormatter to be more minimalistic
  * Fixed RavenHandler's handling of context/extra data, requires Raven client >0.1.0
  * Fixed log rotation in RotatingFileHandler to work with long running scripts spanning multiple days
  * Fixed WebProcessor array access so it checks for data presence
  * Fixed Buffer, Group and FingersCrossed handlers to make use of their processors

### 1.4.0 (2013-02-13)

  * Added RedisHandler to log to Redis via the Predis library or the phpredis extension
  * Added ZendMonitorHandler to log to the Zend Server monitor
  * Added the possibility to pass arrays of handlers and processors directly in the Logger constructor
  * Added `$useSSL` option to the PushoverHandler which is enabled by default
  * Fixed ChromePHPHandler and FirePHPHandler issue when multiple instances are used simultaneously
  * Fixed header injection capability in the NativeMailHandler

### 1.3.1 (2013-01-11)

  * Fixed LogstashFormatter to be usable with stream handlers
  * Fixed GelfMessageFormatter levels on Windows

### 1.3.0 (2013-01-08)

  * Added PSR-3 compliance, the `Monolog\Logger` class is now an instance of `Psr\Log\LoggerInterface`
  * Added PsrLogMessageProcessor that you can selectively enable for full PSR-3 compliance
  * Added LogstashFormatter (combine with SocketHandler or StreamHandler to send logs to Logstash)
  * Added PushoverHandler to send mobile notifications
  * Added CouchDBHandler and DoctrineCouchDBHandler
  * Added RavenHandler to send data to Sentry servers
  * Added support for the new MongoClient class in MongoDBHandler
  * Added microsecond precision to log records' timestamps
  * Added `$flushOnOverflow` param to BufferHandler to flush by batches instead of losing
    the oldest entries
  * Fixed normalization of objects with cyclic references

### 1.2.1 (2012-08-29)

  * Added new $logopts arg to SyslogHandler to provide custom openlog options
  * Fixed fatal error in SyslogHandler

### 1.2.0 (2012-08-18)

  * Added AmqpHandler (for use with AMQP servers)
  * Added CubeHandler
  * Added NativeMailerHandler::addHeader() to send custom headers in mails
  * Added the possibility to specify more than one recipient in NativeMailerHandler
  * Added the possibility to specify float timeouts in SocketHandler
  * Added NOTICE and EMERGENCY levels to conform with RFC 5424
  * Fixed the log records to use the php default timezone instead of UTC
  * Fixed BufferHandler not being flushed properly on PHP fatal errors
  * Fixed normalization of exotic resource types
  * Fixed the default format of the SyslogHandler to avoid duplicating datetimes in syslog

### 1.1.0 (2012-04-23)

  * Added Monolog\Logger::isHandling() to check if a handler will
    handle the given log level
  * Added ChromePHPHandler
  * Added MongoDBHandler
  * Added GelfHandler (for use with Graylog2 servers)
  * Added SocketHandler (for use with syslog-ng for example)
  * Added NormalizerFormatter
  * Added the possibility to change the activation strategy of the FingersCrossedHandler
  * Added possibility to show microseconds in logs
  * Added `server` and `referer` to WebProcessor output

### 1.0.2 (2011-10-24)

  * Fixed bug in IE with large response headers and FirePHPHandler

### 1.0.1 (2011-08-25)

  * Added MemoryPeakUsageProcessor and MemoryUsageProcessor
  * Added Monolog\Logger::getName() to get a logger's channel name

### 1.0.0 (2011-07-06)

  * Added IntrospectionProcessor to get info from where the logger was called
  * Fixed WebProcessor in CLI

### 1.0.0-RC1 (2011-07-01)

  * Initial release
vendor/monolog/monolog/.php_cs000064400000003514151327705700012454 0ustar00<?php

$header = <<<EOF
This file is part of the Monolog package.

(c) Jordi Boggiano <j.boggiano@seld.be>

For the full copyright and license information, please view the LICENSE
file that was distributed with this source code.
EOF;

$finder = Symfony\CS\Finder::create()
    ->files()
    ->name('*.php')
    ->exclude('Fixtures')
    ->in(__DIR__.'/src')
    ->in(__DIR__.'/tests')
;

return Symfony\CS\Config::create()
    ->setUsingCache(true)
    //->setUsingLinter(false)
    ->setRiskyAllowed(true)
    ->setRules(array(
        '@PSR2' => true,
        'binary_operator_spaces' => true,
        'blank_line_before_return' => true,
        'header_comment' => array('header' => $header),
        'include' => true,
        'long_array_syntax' => true,
        'method_separation' => true,
        'no_blank_lines_after_class_opening' => true,
        'no_blank_lines_after_phpdoc' => true,
        'no_blank_lines_between_uses' => true,
        'no_duplicate_semicolons' => true,
        'no_extra_consecutive_blank_lines' => true,
        'no_leading_import_slash' => true,
        'no_leading_namespace_whitespace' => true,
        'no_trailing_comma_in_singleline_array' => true,
        'no_unused_imports' => true,
        'object_operator_without_whitespace' => true,
        'phpdoc_align' => true,
        'phpdoc_indent' => true,
        'phpdoc_no_access' => true,
        'phpdoc_no_package' => true,
        'phpdoc_order' => true,
        'phpdoc_scalar' => true,
        'phpdoc_trim' => true,
        'phpdoc_type_to_var' => true,
        'psr0' => true,
        'single_blank_line_before_namespace' => true,
        'spaces_cast' => true,
        'standardize_not_equals' => true,
        'ternary_operator_spaces' => true,
        'trailing_comma_in_multiline_array' => true,
        'whitespacy_lines' => true,
    ))
    ->finder($finder)
;
vendor/monolog/monolog/README.md000064400000007472151327705700012465 0ustar00# Monolog - Logging for PHP [![Build Status](https://img.shields.io/travis/Seldaek/monolog.svg)](https://travis-ci.org/Seldaek/monolog)

[![Total Downloads](https://img.shields.io/packagist/dt/monolog/monolog.svg)](https://packagist.org/packages/monolog/monolog)
[![Latest Stable Version](https://img.shields.io/packagist/v/monolog/monolog.svg)](https://packagist.org/packages/monolog/monolog)


Monolog sends your logs to files, sockets, inboxes, databases and various
web services. See the complete list of handlers below. Special handlers
allow you to build advanced logging strategies.

This library implements the [PSR-3](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-3-logger-interface.md)
interface that you can type-hint against in your own libraries to keep
a maximum of interoperability. You can also use it in your applications to
make sure you can always use another compatible logger at a later time.
As of 1.11.0 Monolog public APIs will also accept PSR-3 log levels.
Internally Monolog still uses its own level scheme since it predates PSR-3.

## Installation

Install the latest version with

```bash
$ composer require monolog/monolog
```

## Basic Usage

```php
<?php

use Monolog\Logger;
use Monolog\Handler\StreamHandler;

// create a log channel
$log = new Logger('name');
$log->pushHandler(new StreamHandler('path/to/your.log', Logger::WARNING));

// add records to the log
$log->addWarning('Foo');
$log->addError('Bar');
```

## Documentation

- [Usage Instructions](doc/01-usage.md)
- [Handlers, Formatters and Processors](doc/02-handlers-formatters-processors.md)
- [Utility classes](doc/03-utilities.md)
- [Extending Monolog](doc/04-extending.md)

## Third Party Packages

Third party handlers, formatters and processors are
[listed in the wiki](https://github.com/Seldaek/monolog/wiki/Third-Party-Packages). You
can also add your own there if you publish one.

## About

### Requirements

- Monolog works with PHP 5.3 or above, and is also tested to work with HHVM.

### Submitting bugs and feature requests

Bugs and feature request are tracked on [GitHub](https://github.com/Seldaek/monolog/issues)

### Framework Integrations

- Frameworks and libraries using [PSR-3](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-3-logger-interface.md)
  can be used very easily with Monolog since it implements the interface.
- [Symfony2](http://symfony.com) comes out of the box with Monolog.
- [Silex](http://silex.sensiolabs.org/) comes out of the box with Monolog.
- [Laravel 4 & 5](http://laravel.com/) come out of the box with Monolog.
- [Lumen](http://lumen.laravel.com/) comes out of the box with Monolog.
- [PPI](http://www.ppi.io/) comes out of the box with Monolog.
- [CakePHP](http://cakephp.org/) is usable with Monolog via the [cakephp-monolog](https://github.com/jadb/cakephp-monolog) plugin.
- [Slim](http://www.slimframework.com/) is usable with Monolog via the [Slim-Monolog](https://github.com/Flynsarmy/Slim-Monolog) log writer.
- [XOOPS 2.6](http://xoops.org/) comes out of the box with Monolog.
- [Aura.Web_Project](https://github.com/auraphp/Aura.Web_Project) comes out of the box with Monolog.
- [Nette Framework](http://nette.org/en/) can be used with Monolog via [Kdyby/Monolog](https://github.com/Kdyby/Monolog) extension.
- [Proton Micro Framework](https://github.com/alexbilbie/Proton) comes out of the box with Monolog.

### Author

Jordi Boggiano - <j.boggiano@seld.be> - <http://twitter.com/seldaek><br />
See also the list of [contributors](https://github.com/Seldaek/monolog/contributors) which participated in this project.

### License

Monolog is licensed under the MIT License - see the `LICENSE` file for details

### Acknowledgements

This library is heavily inspired by Python's [Logbook](http://packages.python.org/Logbook/)
library, although most concepts have been adjusted to fit to the PHP world.
vendor/monolog/monolog/doc/sockets.md000064400000001777151327705700013752 0ustar00Sockets Handler
===============

This handler allows you to write your logs to sockets using [fsockopen](http://php.net/fsockopen)
or [pfsockopen](http://php.net/pfsockopen).

Persistent sockets are mainly useful in web environments where you gain some performance not closing/opening
the connections between requests.

You can use a `unix://` prefix to access unix sockets and `udp://` to open UDP sockets instead of the default TCP.

Basic Example
-------------

```php
<?php

use Monolog\Logger;
use Monolog\Handler\SocketHandler;

// Create the logger
$logger = new Logger('my_logger');

// Create the handler
$handler = new SocketHandler('unix:///var/log/httpd_app_log.socket');
$handler->setPersistent(true);

// Now add the handler
$logger->pushHandler($handler, Logger::DEBUG);

// You can now use your logger
$logger->addInfo('My logger is now ready');

```

In this example, using syslog-ng, you should see the log on the log server:

    cweb1 [2012-02-26 00:12:03] my_logger.INFO: My logger is now ready [] []

vendor/monolog/monolog/doc/01-usage.md000064400000020434151327705700013610 0ustar00# Using Monolog

- [Installation](#installation)
- [Core Concepts](#core-concepts)
- [Log Levels](#log-levels)
- [Configuring a logger](#configuring-a-logger)
- [Adding extra data in the records](#adding-extra-data-in-the-records)
- [Leveraging channels](#leveraging-channels)
- [Customizing the log format](#customizing-the-log-format)

## Installation

Monolog is available on Packagist ([monolog/monolog](http://packagist.org/packages/monolog/monolog))
and as such installable via [Composer](http://getcomposer.org/).

```bash
composer require monolog/monolog
```

If you do not use Composer, you can grab the code from GitHub, and use any
PSR-0 compatible autoloader (e.g. the [Symfony2 ClassLoader component](https://github.com/symfony/ClassLoader))
to load Monolog classes.

## Core Concepts

Every `Logger` instance has a channel (name) and a stack of handlers. Whenever
you add a record to the logger, it traverses the handler stack. Each handler
decides whether it fully handled the record, and if so, the propagation of the
record ends there.

This allows for flexible logging setups, for example having a `StreamHandler` at
the bottom of the stack that will log anything to disk, and on top of that add
a `MailHandler` that will send emails only when an error message is logged.
Handlers also have a `$bubble` property which defines whether they block the
record or not if they handled it. In this example, setting the `MailHandler`'s
`$bubble` argument to false means that records handled by the `MailHandler` will
not propagate to the `StreamHandler` anymore.

You can create many `Logger`s, each defining a channel (e.g.: db, request,
router, ..) and each of them combining various handlers, which can be shared
or not. The channel is reflected in the logs and allows you to easily see or
filter records.

Each Handler also has a Formatter, a default one with settings that make sense
will be created if you don't set one. The formatters normalize and format
incoming records so that they can be used by the handlers to output useful
information.

Custom severity levels are not available. Only the eight
[RFC 5424](http://tools.ietf.org/html/rfc5424) levels (debug, info, notice,
warning, error, critical, alert, emergency) are present for basic filtering
purposes, but for sorting and other use cases that would require
flexibility, you should add Processors to the Logger that can add extra
information (tags, user ip, ..) to the records before they are handled.

## Log Levels

Monolog supports the logging levels described by [RFC 5424](http://tools.ietf.org/html/rfc5424).

- **DEBUG** (100): Detailed debug information.

- **INFO** (200): Interesting events. Examples: User logs in, SQL logs.

- **NOTICE** (250): Normal but significant events.

- **WARNING** (300): Exceptional occurrences that are not errors. Examples:
  Use of deprecated APIs, poor use of an API, undesirable things that are not
  necessarily wrong.

- **ERROR** (400): Runtime errors that do not require immediate action but
  should typically be logged and monitored.

- **CRITICAL** (500): Critical conditions. Example: Application component
  unavailable, unexpected exception.

- **ALERT** (550): Action must be taken immediately. Example: Entire website
  down, database unavailable, etc. This should trigger the SMS alerts and wake
  you up.

- **EMERGENCY** (600): Emergency: system is unusable.

## Configuring a logger

Here is a basic setup to log to a file and to firephp on the DEBUG level:

```php
<?php

use Monolog\Logger;
use Monolog\Handler\StreamHandler;
use Monolog\Handler\FirePHPHandler;

// Create the logger
$logger = new Logger('my_logger');
// Now add some handlers
$logger->pushHandler(new StreamHandler(__DIR__.'/my_app.log', Logger::DEBUG));
$logger->pushHandler(new FirePHPHandler());

// You can now use your logger
$logger->addInfo('My logger is now ready');
```

Let's explain it. The first step is to create the logger instance which will
be used in your code. The argument is a channel name, which is useful when
you use several loggers (see below for more details about it).

The logger itself does not know how to handle a record. It delegates it to
some handlers. The code above registers two handlers in the stack to allow
handling records in two different ways.

Note that the FirePHPHandler is called first as it is added on top of the
stack. This allows you to temporarily add a logger with bubbling disabled if
you want to override other configured loggers.

> If you use Monolog standalone and are looking for an easy way to
> configure many handlers, the [theorchard/monolog-cascade](https://github.com/theorchard/monolog-cascade)
> can help you build complex logging configs via PHP arrays, yaml or json configs.

## Adding extra data in the records

Monolog provides two different ways to add extra informations along the simple
textual message.

### Using the logging context

The first way is the context, allowing to pass an array of data along the
record:

```php
<?php

$logger->addInfo('Adding a new user', array('username' => 'Seldaek'));
```

Simple handlers (like the StreamHandler for instance) will simply format
the array to a string but richer handlers can take advantage of the context
(FirePHP is able to display arrays in pretty way for instance).

### Using processors

The second way is to add extra data for all records by using a processor.
Processors can be any callable. They will get the record as parameter and
must return it after having eventually changed the `extra` part of it. Let's
write a processor adding some dummy data in the record:

```php
<?php

$logger->pushProcessor(function ($record) {
    $record['extra']['dummy'] = 'Hello world!';

    return $record;
});
```

Monolog provides some built-in processors that can be used in your project.
Look at the [dedicated chapter](https://github.com/Seldaek/monolog/blob/master/doc/02-handlers-formatters-processors.md#processors) for the list.

> Tip: processors can also be registered on a specific handler instead of
  the logger to apply only for this handler.

## Leveraging channels

Channels are a great way to identify to which part of the application a record
is related. This is useful in big applications (and is leveraged by
MonologBundle in Symfony2).

Picture two loggers sharing a handler that writes to a single log file.
Channels would allow you to identify the logger that issued every record.
You can easily grep through the log files filtering this or that channel.

```php
<?php

use Monolog\Logger;
use Monolog\Handler\StreamHandler;
use Monolog\Handler\FirePHPHandler;

// Create some handlers
$stream = new StreamHandler(__DIR__.'/my_app.log', Logger::DEBUG);
$firephp = new FirePHPHandler();

// Create the main logger of the app
$logger = new Logger('my_logger');
$logger->pushHandler($stream);
$logger->pushHandler($firephp);

// Create a logger for the security-related stuff with a different channel
$securityLogger = new Logger('security');
$securityLogger->pushHandler($stream);
$securityLogger->pushHandler($firephp);

// Or clone the first one to only change the channel
$securityLogger = $logger->withName('security');
```

## Customizing the log format

In Monolog it's easy to customize the format of the logs written into files,
sockets, mails, databases and other handlers. Most of the handlers use the

```php
$record['formatted']
```

value to be automatically put into the log device. This value depends on the
formatter settings. You can choose between predefined formatter classes or
write your own (e.g. a multiline text file for human-readable output).

To configure a predefined formatter class, just set it as the handler's field:

```php
// the default date format is "Y-m-d H:i:s"
$dateFormat = "Y n j, g:i a";
// the default output format is "[%datetime%] %channel%.%level_name%: %message% %context% %extra%\n"
$output = "%datetime% > %level_name% > %message% %context% %extra%\n";
// finally, create a formatter
$formatter = new LineFormatter($output, $dateFormat);

// Create a handler
$stream = new StreamHandler(__DIR__.'/my_app.log', Logger::DEBUG);
$stream->setFormatter($formatter);
// bind it to a logger object
$securityLogger = new Logger('security');
$securityLogger->pushHandler($stream);
```

You may also reuse the same formatter between multiple handlers and share those
handlers between multiple loggers.

[Handlers, Formatters and Processors](02-handlers-formatters-processors.md) &rarr;
vendor/monolog/monolog/doc/04-extending.md000064400000004157151327705700014500 0ustar00# Extending Monolog

Monolog is fully extensible, allowing you to adapt your logger to your needs.

## Writing your own handler

Monolog provides many built-in handlers. But if the one you need does not
exist, you can write it and use it in your logger. The only requirement is
to implement `Monolog\Handler\HandlerInterface`.

Let's write a PDOHandler to log records to a database. We will extend the
abstract class provided by Monolog to keep things DRY.

```php
<?php

use Monolog\Logger;
use Monolog\Handler\AbstractProcessingHandler;

class PDOHandler extends AbstractProcessingHandler
{
    private $initialized = false;
    private $pdo;
    private $statement;

    public function __construct(PDO $pdo, $level = Logger::DEBUG, $bubble = true)
    {
        $this->pdo = $pdo;
        parent::__construct($level, $bubble);
    }

    protected function write(array $record)
    {
        if (!$this->initialized) {
            $this->initialize();
        }

        $this->statement->execute(array(
            'channel' => $record['channel'],
            'level' => $record['level'],
            'message' => $record['formatted'],
            'time' => $record['datetime']->format('U'),
        ));
    }

    private function initialize()
    {
        $this->pdo->exec(
            'CREATE TABLE IF NOT EXISTS monolog '
            .'(channel VARCHAR(255), level INTEGER, message LONGTEXT, time INTEGER UNSIGNED)'
        );
        $this->statement = $this->pdo->prepare(
            'INSERT INTO monolog (channel, level, message, time) VALUES (:channel, :level, :message, :time)'
        );

        $this->initialized = true;
    }
}
```

You can now use this handler in your logger:

```php
<?php

$logger->pushHandler(new PDOHandler(new PDO('sqlite:logs.sqlite')));

// You can now use your logger
$logger->addInfo('My logger is now ready');
```

The `Monolog\Handler\AbstractProcessingHandler` class provides most of the
logic needed for the handler, including the use of processors and the formatting
of the record (which is why we use ``$record['formatted']`` instead of ``$record['message']``).

&larr; [Utility classes](03-utilities.md)
vendor/monolog/monolog/doc/02-handlers-formatters-processors.md000064400000023513151327705700020672 0ustar00# Handlers, Formatters and Processors

- [Handlers](#handlers)
  - [Log to files and syslog](#log-to-files-and-syslog)
  - [Send alerts and emails](#send-alerts-and-emails)
  - [Log specific servers and networked logging](#log-specific-servers-and-networked-logging)
  - [Logging in development](#logging-in-development)
  - [Log to databases](#log-to-databases)
  - [Wrappers / Special Handlers](#wrappers--special-handlers)
- [Formatters](#formatters)
- [Processors](#processors)
- [Third Party Packages](#third-party-packages)

## Handlers

### Log to files and syslog

- _StreamHandler_: Logs records into any PHP stream, use this for log files.
- _RotatingFileHandler_: Logs records to a file and creates one logfile per day.
  It will also delete files older than `$maxFiles`. You should use
  [logrotate](http://linuxcommand.org/man_pages/logrotate8.html) for high profile
  setups though, this is just meant as a quick and dirty solution.
- _SyslogHandler_: Logs records to the syslog.
- _ErrorLogHandler_: Logs records to PHP's
  [`error_log()`](http://docs.php.net/manual/en/function.error-log.php) function.

### Send alerts and emails

- _NativeMailerHandler_: Sends emails using PHP's
  [`mail()`](http://php.net/manual/en/function.mail.php) function.
- _SwiftMailerHandler_: Sends emails using a [`Swift_Mailer`](http://swiftmailer.org/) instance.
- _PushoverHandler_: Sends mobile notifications via the [Pushover](https://www.pushover.net/) API.
- _HipChatHandler_: Logs records to a [HipChat](http://hipchat.com) chat room using its API.
- _FlowdockHandler_: Logs records to a [Flowdock](https://www.flowdock.com/) account.
- _SlackHandler_: Logs records to a [Slack](https://www.slack.com/) account using the Slack API.
- _SlackbotHandler_: Logs records to a [Slack](https://www.slack.com/) account using the Slackbot incoming hook.
- _SlackWebhookHandler_: Logs records to a [Slack](https://www.slack.com/) account using Slack Webhooks.
- _MandrillHandler_: Sends emails via the Mandrill API using a [`Swift_Message`](http://swiftmailer.org/) instance.
- _FleepHookHandler_: Logs records to a [Fleep](https://fleep.io/) conversation using Webhooks.
- _IFTTTHandler_: Notifies an [IFTTT](https://ifttt.com/maker) trigger with the log channel, level name and message.

### Log specific servers and networked logging

- _SocketHandler_: Logs records to [sockets](http://php.net/fsockopen), use this
  for UNIX and TCP sockets. See an [example](sockets.md).
- _AmqpHandler_: Logs records to an [amqp](http://www.amqp.org/) compatible
  server. Requires the [php-amqp](http://pecl.php.net/package/amqp) extension (1.0+).
- _GelfHandler_: Logs records to a [Graylog2](http://www.graylog2.org) server.
- _CubeHandler_: Logs records to a [Cube](http://square.github.com/cube/) server.
- _RavenHandler_: Logs records to a [Sentry](http://getsentry.com/) server using
  [raven](https://packagist.org/packages/raven/raven).
- _ZendMonitorHandler_: Logs records to the Zend Monitor present in Zend Server.
- _NewRelicHandler_: Logs records to a [NewRelic](http://newrelic.com/) application.
- _LogglyHandler_: Logs records to a [Loggly](http://www.loggly.com/) account.
- _RollbarHandler_: Logs records to a [Rollbar](https://rollbar.com/) account.
- _SyslogUdpHandler_: Logs records to a remote [Syslogd](http://www.rsyslog.com/) server.
- _LogEntriesHandler_: Logs records to a [LogEntries](http://logentries.com/) account.
- _InsightOpsHandler_: Logs records to a [InsightOps](https://www.rapid7.com/products/insightops/) account.

### Logging in development

- _FirePHPHandler_: Handler for [FirePHP](http://www.firephp.org/), providing
  inline `console` messages within [FireBug](http://getfirebug.com/).
- _ChromePHPHandler_: Handler for [ChromePHP](http://www.chromephp.com/), providing
  inline `console` messages within Chrome.
- _BrowserConsoleHandler_: Handler to send logs to browser's Javascript `console` with
  no browser extension required. Most browsers supporting `console` API are supported.
- _PHPConsoleHandler_: Handler for [PHP Console](https://chrome.google.com/webstore/detail/php-console/nfhmhhlpfleoednkpnnnkolmclajemef), providing
  inline `console` and notification popup messages within Chrome.

### Log to databases

- _RedisHandler_: Logs records to a [redis](http://redis.io) server.
- _MongoDBHandler_: Handler to write records in MongoDB via a
  [Mongo](http://pecl.php.net/package/mongo) extension connection.
- _CouchDBHandler_: Logs records to a CouchDB server.
- _DoctrineCouchDBHandler_: Logs records to a CouchDB server via the Doctrine CouchDB ODM.
- _ElasticSearchHandler_: Logs records to an Elastic Search server.
- _DynamoDbHandler_: Logs records to a DynamoDB table with the [AWS SDK](https://github.com/aws/aws-sdk-php).

### Wrappers / Special Handlers

- _FingersCrossedHandler_: A very interesting wrapper. It takes a logger as
  parameter and will accumulate log records of all levels until a record
  exceeds the defined severity level. At which point it delivers all records,
  including those of lower severity, to the handler it wraps. This means that
  until an error actually happens you will not see anything in your logs, but
  when it happens you will have the full information, including debug and info
  records. This provides you with all the information you need, but only when
  you need it.
- _DeduplicationHandler_: Useful if you are sending notifications or emails
  when critical errors occur. It takes a logger as parameter and will
  accumulate log records of all levels until the end of the request (or
  `flush()` is called). At that point it delivers all records to the handler
  it wraps, but only if the records are unique over a given time period
  (60seconds by default). If the records are duplicates they are simply
  discarded. The main use of this is in case of critical failure like if your
  database is unreachable for example all your requests will fail and that
  can result in a lot of notifications being sent. Adding this handler reduces
  the amount of notifications to a manageable level.
- _WhatFailureGroupHandler_: This handler extends the _GroupHandler_ ignoring
   exceptions raised by each child handler. This allows you to ignore issues
   where a remote tcp connection may have died but you do not want your entire
   application to crash and may wish to continue to log to other handlers.
- _BufferHandler_: This handler will buffer all the log records it receives
  until `close()` is called at which point it will call `handleBatch()` on the
  handler it wraps with all the log messages at once. This is very useful to
  send an email with all records at once for example instead of having one mail
  for every log record.
- _GroupHandler_: This handler groups other handlers. Every record received is
  sent to all the handlers it is configured with.
- _FilterHandler_: This handler only lets records of the given levels through
   to the wrapped handler.
- _SamplingHandler_: Wraps around another handler and lets you sample records
   if you only want to store some of them.
- _NullHandler_: Any record it can handle will be thrown away. This can be used
  to put on top of an existing handler stack to disable it temporarily.
- _PsrHandler_: Can be used to forward log records to an existing PSR-3 logger
- _TestHandler_: Used for testing, it records everything that is sent to it and
  has accessors to read out the information.
- _HandlerWrapper_: A simple handler wrapper you can inherit from to create
 your own wrappers easily.

## Formatters

- _LineFormatter_: Formats a log record into a one-line string.
- _HtmlFormatter_: Used to format log records into a human readable html table, mainly suitable for emails.
- _NormalizerFormatter_: Normalizes objects/resources down to strings so a record can easily be serialized/encoded.
- _ScalarFormatter_: Used to format log records into an associative array of scalar values.
- _JsonFormatter_: Encodes a log record into json.
- _WildfireFormatter_: Used to format log records into the Wildfire/FirePHP protocol, only useful for the FirePHPHandler.
- _ChromePHPFormatter_: Used to format log records into the ChromePHP format, only useful for the ChromePHPHandler.
- _GelfMessageFormatter_: Used to format log records into Gelf message instances, only useful for the GelfHandler.
- _LogstashFormatter_: Used to format log records into [logstash](http://logstash.net/) event json, useful for any handler listed under inputs [here](http://logstash.net/docs/latest).
- _ElasticaFormatter_: Used to format log records into an Elastica\Document object, only useful for the ElasticSearchHandler.
- _LogglyFormatter_: Used to format log records into Loggly messages, only useful for the LogglyHandler.
- _FlowdockFormatter_: Used to format log records into Flowdock messages, only useful for the FlowdockHandler.
- _MongoDBFormatter_: Converts \DateTime instances to \MongoDate and objects recursively to arrays, only useful with the MongoDBHandler.

## Processors

- _PsrLogMessageProcessor_: Processes a log record's message according to PSR-3 rules, replacing `{foo}` with the value from `$context['foo']`.
- _IntrospectionProcessor_: Adds the line/file/class/method from which the log call originated.
- _WebProcessor_: Adds the current request URI, request method and client IP to a log record.
- _MemoryUsageProcessor_: Adds the current memory usage to a log record.
- _MemoryPeakUsageProcessor_: Adds the peak memory usage to a log record.
- _ProcessIdProcessor_: Adds the process id to a log record.
- _UidProcessor_: Adds a unique identifier to a log record.
- _GitProcessor_: Adds the current git branch and commit to a log record.
- _TagProcessor_: Adds an array of predefined tags to a log record.

## Third Party Packages

Third party handlers, formatters and processors are
[listed in the wiki](https://github.com/Seldaek/monolog/wiki/Third-Party-Packages). You
can also add your own there if you publish one.

&larr; [Usage](01-usage.md) |  [Utility classes](03-utilities.md) &rarr;
vendor/monolog/monolog/doc/03-utilities.md000064400000001640151327705700014517 0ustar00# Utilities

- _Registry_: The `Monolog\Registry` class lets you configure global loggers that you
  can then statically access from anywhere. It is not really a best practice but can
  help in some older codebases or for ease of use.
- _ErrorHandler_: The `Monolog\ErrorHandler` class allows you to easily register
  a Logger instance as an exception handler, error handler or fatal error handler.
- _SignalHandler_: The `Monolog\SignalHandler` class allows you to easily register
  a Logger instance as a POSIX signal handler.
- _ErrorLevelActivationStrategy_: Activates a FingersCrossedHandler when a certain log
  level is reached.
- _ChannelLevelActivationStrategy_: Activates a FingersCrossedHandler when a certain
  log level is reached, depending on which channel received the log record.

&larr; [Handlers, Formatters and Processors](02-handlers-formatters-processors.md) |  [Extending Monolog](04-extending.md) &rarr;
vendor/monolog/monolog/tests/Monolog/LoggerTest.php000064400000053327151327705700016552 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog;

use WPvividMonolog\Processor\WebProcessor;
use WPvividMonolog\Handler\TestHandler;

class LoggerTest extends \PHPUnit_Framework_TestCase
{
    /**
     * @covers Monolog\Logger::getName
     */
    public function testGetName()
    {
        $logger = new Logger('foo');
        $this->assertEquals('foo', $logger->getName());
    }

    /**
     * @covers Monolog\Logger::getLevelName
     */
    public function testGetLevelName()
    {
        $this->assertEquals('ERROR', Logger::getLevelName(Logger::ERROR));
    }

    /**
     * @covers Monolog\Logger::withName
     */
    public function testWithName()
    {
        $first = new Logger('first', array($handler = new TestHandler()));
        $second = $first->withName('second');

        $this->assertSame('first', $first->getName());
        $this->assertSame('second', $second->getName());
        $this->assertSame($handler, $second->popHandler());
    }

    /**
     * @covers Monolog\Logger::toMonologLevel
     */
    public function testConvertPSR3ToMonologLevel()
    {
        $this->assertEquals(Logger::toMonologLevel('debug'), 100);
        $this->assertEquals(Logger::toMonologLevel('info'), 200);
        $this->assertEquals(Logger::toMonologLevel('notice'), 250);
        $this->assertEquals(Logger::toMonologLevel('warning'), 300);
        $this->assertEquals(Logger::toMonologLevel('error'), 400);
        $this->assertEquals(Logger::toMonologLevel('critical'), 500);
        $this->assertEquals(Logger::toMonologLevel('alert'), 550);
        $this->assertEquals(Logger::toMonologLevel('emergency'), 600);
    }

    /**
     * @covers Monolog\Logger::getLevelName
     * @expectedException InvalidArgumentException
     */
    public function testGetLevelNameThrows()
    {
        Logger::getLevelName(5);
    }

    /**
     * @covers Monolog\Logger::__construct
     */
    public function testChannel()
    {
        $logger = new Logger('foo');
        $handler = new TestHandler;
        $logger->pushHandler($handler);
        $logger->addWarning('test');
        list($record) = $handler->getRecords();
        $this->assertEquals('foo', $record['channel']);
    }

    /**
     * @covers Monolog\Logger::addRecord
     */
    public function testLog()
    {
        $logger = new Logger(__METHOD__);

        $handler = $this->getMock('WPvividMonolog\Handler\NullHandler', array('handle'));
        $handler->expects($this->once())
            ->method('handle');
        $logger->pushHandler($handler);

        $this->assertTrue($logger->addWarning('test'));
    }

    /**
     * @covers Monolog\Logger::addRecord
     */
    public function testLogNotHandled()
    {
        $logger = new Logger(__METHOD__);

        $handler = $this->getMock('WPvividMonolog\Handler\NullHandler', array('handle'), array(Logger::ERROR));
        $handler->expects($this->never())
            ->method('handle');
        $logger->pushHandler($handler);

        $this->assertFalse($logger->addWarning('test'));
    }

    public function testHandlersInCtor()
    {
        $handler1 = new TestHandler;
        $handler2 = new TestHandler;
        $logger = new Logger(__METHOD__, array($handler1, $handler2));

        $this->assertEquals($handler1, $logger->popHandler());
        $this->assertEquals($handler2, $logger->popHandler());
    }

    public function testProcessorsInCtor()
    {
        $processor1 = new WebProcessor;
        $processor2 = new WebProcessor;
        $logger = new Logger(__METHOD__, array(), array($processor1, $processor2));

        $this->assertEquals($processor1, $logger->popProcessor());
        $this->assertEquals($processor2, $logger->popProcessor());
    }

    /**
     * @covers Monolog\Logger::pushHandler
     * @covers Monolog\Logger::popHandler
     * @expectedException LogicException
     */
    public function testPushPopHandler()
    {
        $logger = new Logger(__METHOD__);
        $handler1 = new TestHandler;
        $handler2 = new TestHandler;

        $logger->pushHandler($handler1);
        $logger->pushHandler($handler2);

        $this->assertEquals($handler2, $logger->popHandler());
        $this->assertEquals($handler1, $logger->popHandler());
        $logger->popHandler();
    }

    /**
     * @covers Monolog\Logger::setHandlers
     */
    public function testSetHandlers()
    {
        $logger = new Logger(__METHOD__);
        $handler1 = new TestHandler;
        $handler2 = new TestHandler;

        $logger->pushHandler($handler1);
        $logger->setHandlers(array($handler2));

        // handler1 has been removed
        $this->assertEquals(array($handler2), $logger->getHandlers());

        $logger->setHandlers(array(
            "AMapKey" => $handler1,
            "Woop" => $handler2,
        ));

        // Keys have been scrubbed
        $this->assertEquals(array($handler1, $handler2), $logger->getHandlers());
    }

    /**
     * @covers Monolog\Logger::pushProcessor
     * @covers Monolog\Logger::popProcessor
     * @expectedException LogicException
     */
    public function testPushPopProcessor()
    {
        $logger = new Logger(__METHOD__);
        $processor1 = new WebProcessor;
        $processor2 = new WebProcessor;

        $logger->pushProcessor($processor1);
        $logger->pushProcessor($processor2);

        $this->assertEquals($processor2, $logger->popProcessor());
        $this->assertEquals($processor1, $logger->popProcessor());
        $logger->popProcessor();
    }

    /**
     * @covers Monolog\Logger::pushProcessor
     * @expectedException InvalidArgumentException
     */
    public function testPushProcessorWithNonCallable()
    {
        $logger = new Logger(__METHOD__);

        $logger->pushProcessor(new \stdClass());
    }

    /**
     * @covers Monolog\Logger::addRecord
     */
    public function testProcessorsAreExecuted()
    {
        $logger = new Logger(__METHOD__);
        $handler = new TestHandler;
        $logger->pushHandler($handler);
        $logger->pushProcessor(function ($record) {
            $record['extra']['win'] = true;

            return $record;
        });
        $logger->addError('test');
        list($record) = $handler->getRecords();
        $this->assertTrue($record['extra']['win']);
    }

    /**
     * @covers Monolog\Logger::addRecord
     */
    public function testProcessorsAreCalledOnlyOnce()
    {
        $logger = new Logger(__METHOD__);
        $handler = $this->getMock('WPvividMonolog\Handler\HandlerInterface');
        $handler->expects($this->any())
            ->method('isHandling')
            ->will($this->returnValue(true))
        ;
        $handler->expects($this->any())
            ->method('handle')
            ->will($this->returnValue(true))
        ;
        $logger->pushHandler($handler);

        $processor = $this->getMockBuilder('WPvividMonolog\Processor\WebProcessor')
            ->disableOriginalConstructor()
            ->setMethods(array('__invoke'))
            ->getMock()
        ;
        $processor->expects($this->once())
            ->method('__invoke')
            ->will($this->returnArgument(0))
        ;
        $logger->pushProcessor($processor);

        $logger->addError('test');
    }

    /**
     * @covers Monolog\Logger::addRecord
     */
    public function testProcessorsNotCalledWhenNotHandled()
    {
        $logger = new Logger(__METHOD__);
        $handler = $this->getMock('WPvividMonolog\Handler\HandlerInterface');
        $handler->expects($this->once())
            ->method('isHandling')
            ->will($this->returnValue(false))
        ;
        $logger->pushHandler($handler);
        $that = $this;
        $logger->pushProcessor(function ($record) use ($that) {
            $that->fail('The processor should not be called');
        });
        $logger->addAlert('test');
    }

    /**
     * @covers Monolog\Logger::addRecord
     */
    public function testHandlersNotCalledBeforeFirstHandling()
    {
        $logger = new Logger(__METHOD__);

        $handler1 = $this->getMock('WPvividMonolog\Handler\HandlerInterface');
        $handler1->expects($this->never())
            ->method('isHandling')
            ->will($this->returnValue(false))
        ;
        $handler1->expects($this->once())
            ->method('handle')
            ->will($this->returnValue(false))
        ;
        $logger->pushHandler($handler1);

        $handler2 = $this->getMock('WPvividMonolog\Handler\HandlerInterface');
        $handler2->expects($this->once())
            ->method('isHandling')
            ->will($this->returnValue(true))
        ;
        $handler2->expects($this->once())
            ->method('handle')
            ->will($this->returnValue(false))
        ;
        $logger->pushHandler($handler2);

        $handler3 = $this->getMock('WPvividMonolog\Handler\HandlerInterface');
        $handler3->expects($this->once())
            ->method('isHandling')
            ->will($this->returnValue(false))
        ;
        $handler3->expects($this->never())
            ->method('handle')
        ;
        $logger->pushHandler($handler3);

        $logger->debug('test');
    }

    /**
     * @covers Monolog\Logger::addRecord
     */
    public function testHandlersNotCalledBeforeFirstHandlingWithAssocArray()
    {
        $handler1 = $this->getMock('WPvividMonolog\Handler\HandlerInterface');
        $handler1->expects($this->never())
            ->method('isHandling')
            ->will($this->returnValue(false))
        ;
        $handler1->expects($this->once())
            ->method('handle')
            ->will($this->returnValue(false))
        ;

        $handler2 = $this->getMock('WPvividMonolog\Handler\HandlerInterface');
        $handler2->expects($this->once())
            ->method('isHandling')
            ->will($this->returnValue(true))
        ;
        $handler2->expects($this->once())
            ->method('handle')
            ->will($this->returnValue(false))
        ;

        $handler3 = $this->getMock('WPvividMonolog\Handler\HandlerInterface');
        $handler3->expects($this->once())
            ->method('isHandling')
            ->will($this->returnValue(false))
        ;
        $handler3->expects($this->never())
            ->method('handle')
        ;

        $logger = new Logger(__METHOD__, array('last' => $handler3, 'second' => $handler2, 'first' => $handler1));

        $logger->debug('test');
    }

    /**
     * @covers Monolog\Logger::addRecord
     */
    public function testBubblingWhenTheHandlerReturnsFalse()
    {
        $logger = new Logger(__METHOD__);

        $handler1 = $this->getMock('WPvividMonolog\Handler\HandlerInterface');
        $handler1->expects($this->any())
            ->method('isHandling')
            ->will($this->returnValue(true))
        ;
        $handler1->expects($this->once())
            ->method('handle')
            ->will($this->returnValue(false))
        ;
        $logger->pushHandler($handler1);

        $handler2 = $this->getMock('WPvividMonolog\Handler\HandlerInterface');
        $handler2->expects($this->any())
            ->method('isHandling')
            ->will($this->returnValue(true))
        ;
        $handler2->expects($this->once())
            ->method('handle')
            ->will($this->returnValue(false))
        ;
        $logger->pushHandler($handler2);

        $logger->debug('test');
    }

    /**
     * @covers Monolog\Logger::addRecord
     */
    public function testNotBubblingWhenTheHandlerReturnsTrue()
    {
        $logger = new Logger(__METHOD__);

        $handler1 = $this->getMock('WPvividMonolog\Handler\HandlerInterface');
        $handler1->expects($this->any())
            ->method('isHandling')
            ->will($this->returnValue(true))
        ;
        $handler1->expects($this->never())
            ->method('handle')
        ;
        $logger->pushHandler($handler1);

        $handler2 = $this->getMock('WPvividMonolog\Handler\HandlerInterface');
        $handler2->expects($this->any())
            ->method('isHandling')
            ->will($this->returnValue(true))
        ;
        $handler2->expects($this->once())
            ->method('handle')
            ->will($this->returnValue(true))
        ;
        $logger->pushHandler($handler2);

        $logger->debug('test');
    }

    /**
     * @covers Monolog\Logger::isHandling
     */
    public function testIsHandling()
    {
        $logger = new Logger(__METHOD__);

        $handler1 = $this->getMock('WPvividMonolog\Handler\HandlerInterface');
        $handler1->expects($this->any())
            ->method('isHandling')
            ->will($this->returnValue(false))
        ;

        $logger->pushHandler($handler1);
        $this->assertFalse($logger->isHandling(Logger::DEBUG));

        $handler2 = $this->getMock('WPvividMonolog\Handler\HandlerInterface');
        $handler2->expects($this->any())
            ->method('isHandling')
            ->will($this->returnValue(true))
        ;

        $logger->pushHandler($handler2);
        $this->assertTrue($logger->isHandling(Logger::DEBUG));
    }

    /**
     * @dataProvider logMethodProvider
     * @covers Monolog\Logger::addDebug
     * @covers Monolog\Logger::addInfo
     * @covers Monolog\Logger::addNotice
     * @covers Monolog\Logger::addWarning
     * @covers Monolog\Logger::addError
     * @covers Monolog\Logger::addCritical
     * @covers Monolog\Logger::addAlert
     * @covers Monolog\Logger::addEmergency
     * @covers Monolog\Logger::debug
     * @covers Monolog\Logger::info
     * @covers Monolog\Logger::notice
     * @covers Monolog\Logger::warn
     * @covers Monolog\Logger::err
     * @covers Monolog\Logger::crit
     * @covers Monolog\Logger::alert
     * @covers Monolog\Logger::emerg
     */
    public function testLogMethods($method, $expectedLevel)
    {
        $logger = new Logger('foo');
        $handler = new TestHandler;
        $logger->pushHandler($handler);
        $logger->{$method}('test');
        list($record) = $handler->getRecords();
        $this->assertEquals($expectedLevel, $record['level']);
    }

    public function logMethodProvider()
    {
        return array(
            // monolog methods
            array('addDebug',     Logger::DEBUG),
            array('addInfo',      Logger::INFO),
            array('addNotice',    Logger::NOTICE),
            array('addWarning',   Logger::WARNING),
            array('addError',     Logger::ERROR),
            array('addCritical',  Logger::CRITICAL),
            array('addAlert',     Logger::ALERT),
            array('addEmergency', Logger::EMERGENCY),

            // ZF/Sf2 compat methods
            array('debug',  Logger::DEBUG),
            array('info',   Logger::INFO),
            array('notice', Logger::NOTICE),
            array('warn',   Logger::WARNING),
            array('err',    Logger::ERROR),
            array('crit',   Logger::CRITICAL),
            array('alert',  Logger::ALERT),
            array('emerg',  Logger::EMERGENCY),
        );
    }

    /**
     * @dataProvider setTimezoneProvider
     * @covers Monolog\Logger::setTimezone
     */
    public function testSetTimezone($tz)
    {
        Logger::setTimezone($tz);
        $logger = new Logger('foo');
        $handler = new TestHandler;
        $logger->pushHandler($handler);
        $logger->info('test');
        list($record) = $handler->getRecords();
        $this->assertEquals($tz, $record['datetime']->getTimezone());
    }

    public function setTimezoneProvider()
    {
        return array_map(
            function ($tz) { return array(new \DateTimeZone($tz)); },
            \DateTimeZone::listIdentifiers()
        );
    }

    /**
     * @dataProvider useMicrosecondTimestampsProvider
     * @covers Monolog\Logger::useMicrosecondTimestamps
     * @covers Monolog\Logger::addRecord
     */
    public function testUseMicrosecondTimestamps($micro, $assert)
    {
        $logger = new Logger('foo');
        $logger->useMicrosecondTimestamps($micro);
        $handler = new TestHandler;
        $logger->pushHandler($handler);
        $logger->info('test');
        list($record) = $handler->getRecords();
        $this->{$assert}('000000', $record['datetime']->format('u'));
    }

    public function useMicrosecondTimestampsProvider()
    {
        return array(
            // this has a very small chance of a false negative (1/10^6)
            'with microseconds' => array(true, 'assertNotSame'),
            'without microseconds' => array(false, PHP_VERSION_ID >= 70100 ? 'assertNotSame' : 'assertSame'),
        );
    }

    /**
     * @covers Monolog\Logger::setExceptionHandler
     */
    public function testSetExceptionHandler()
    {
        $logger = new Logger(__METHOD__);
        $this->assertNull($logger->getExceptionHandler());
        $callback = function ($ex) {
        };
        $logger->setExceptionHandler($callback);
        $this->assertEquals($callback, $logger->getExceptionHandler());
    }

    /**
     * @covers Monolog\Logger::setExceptionHandler
     * @expectedException InvalidArgumentException
     */
    public function testBadExceptionHandlerType()
    {
        $logger = new Logger(__METHOD__);
        $logger->setExceptionHandler(false);
    }

    /**
     * @covers Monolog\Logger::handleException
     * @expectedException Exception
     */
    public function testDefaultHandleException()
    {
        $logger = new Logger(__METHOD__);
        $handler = $this->getMock('WPvividMonolog\Handler\HandlerInterface');
        $handler->expects($this->any())
            ->method('isHandling')
            ->will($this->returnValue(true))
        ;
        $handler->expects($this->any())
            ->method('handle')
            ->will($this->throwException(new \Exception('Some handler exception')))
        ;
        $logger->pushHandler($handler);
        $logger->info('test');
    }

    /**
     * @covers Monolog\Logger::handleException
     * @covers Monolog\Logger::addRecord
     */
    public function testCustomHandleException()
    {
        $logger = new Logger(__METHOD__);
        $that = $this;
        $logger->setExceptionHandler(function ($e, $record) use ($that) {
            $that->assertEquals($e->getMessage(), 'Some handler exception');
            $that->assertTrue(is_array($record));
            $that->assertEquals($record['message'], 'test');
        });
        $handler = $this->getMock('WPvividMonolog\Handler\HandlerInterface');
        $handler->expects($this->any())
            ->method('isHandling')
            ->will($this->returnValue(true))
        ;
        $handler->expects($this->any())
            ->method('handle')
            ->will($this->throwException(new \Exception('Some handler exception')))
        ;
        $logger->pushHandler($handler);
        $logger->info('test');
    }

    public function testReset()
    {
        $logger = new Logger('app');

        $testHandler = new Handler\TestHandler();
        $bufferHandler = new Handler\BufferHandler($testHandler);
        $groupHandler = new Handler\GroupHandler(array($bufferHandler));
        $fingersCrossedHandler = new Handler\FingersCrossedHandler($groupHandler);

        $logger->pushHandler($fingersCrossedHandler);

        $processorUid1 = new Processor\UidProcessor(10);
        $uid1 = $processorUid1->getUid();
        $groupHandler->pushProcessor($processorUid1);

        $processorUid2 = new Processor\UidProcessor(5);
        $uid2 = $processorUid2->getUid();
        $logger->pushProcessor($processorUid2);

        $getProperty = function ($object, $property) {
            $reflectionProperty = new \ReflectionProperty(get_class($object), $property);
            $reflectionProperty->setAccessible(true);

            return $reflectionProperty->getValue($object);
        };
        $that = $this;
        $assertBufferOfBufferHandlerEmpty = function () use ($getProperty, $bufferHandler, $that) {
            $that->assertEmpty($getProperty($bufferHandler, 'buffer'));
        };
        $assertBuffersEmpty = function() use ($assertBufferOfBufferHandlerEmpty, $getProperty, $fingersCrossedHandler, $that) {
            $assertBufferOfBufferHandlerEmpty();
            $that->assertEmpty($getProperty($fingersCrossedHandler, 'buffer'));
        };

        $logger->debug('debug');
        $logger->reset();
        $assertBuffersEmpty();
        $this->assertFalse($testHandler->hasDebugRecords());
        $this->assertFalse($testHandler->hasErrorRecords());
        $this->assertNotSame($uid1, $uid1 = $processorUid1->getUid());
        $this->assertNotSame($uid2, $uid2 = $processorUid2->getUid());

        $logger->debug('debug');
        $logger->error('error');
        $logger->reset();
        $assertBuffersEmpty();
        $this->assertTrue($testHandler->hasDebugRecords());
        $this->assertTrue($testHandler->hasErrorRecords());
        $this->assertNotSame($uid1, $uid1 = $processorUid1->getUid());
        $this->assertNotSame($uid2, $uid2 = $processorUid2->getUid());

        $logger->info('info');
        $this->assertNotEmpty($getProperty($fingersCrossedHandler, 'buffer'));
        $assertBufferOfBufferHandlerEmpty();
        $this->assertFalse($testHandler->hasInfoRecords());

        $logger->reset();
        $assertBuffersEmpty();
        $this->assertFalse($testHandler->hasInfoRecords());
        $this->assertNotSame($uid1, $uid1 = $processorUid1->getUid());
        $this->assertNotSame($uid2, $uid2 = $processorUid2->getUid());

        $logger->notice('notice');
        $logger->emergency('emergency');
        $logger->reset();
        $assertBuffersEmpty();
        $this->assertFalse($testHandler->hasInfoRecords());
        $this->assertTrue($testHandler->hasNoticeRecords());
        $this->assertTrue($testHandler->hasEmergencyRecords());
        $this->assertNotSame($uid1, $processorUid1->getUid());
        $this->assertNotSame($uid2, $processorUid2->getUid());
    }
}
vendor/monolog/monolog/tests/Monolog/RegistryTest.php000064400000007613151327705700017140 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog;

class RegistryTest extends \PHPUnit_Framework_TestCase
{
    protected function setUp()
    {
        Registry::clear();
    }

    /**
     * @dataProvider hasLoggerProvider
     * @covers Monolog\Registry::hasLogger
     */
    public function testHasLogger(array $loggersToAdd, array $loggersToCheck, array $expectedResult)
    {
        foreach ($loggersToAdd as $loggerToAdd) {
            Registry::addLogger($loggerToAdd);
        }
        foreach ($loggersToCheck as $index => $loggerToCheck) {
            $this->assertSame($expectedResult[$index], Registry::hasLogger($loggerToCheck));
        }
    }

    public function hasLoggerProvider()
    {
        $logger1 = new Logger('test1');
        $logger2 = new Logger('test2');
        $logger3 = new Logger('test3');

        return array(
            // only instances
            array(
                array($logger1),
                array($logger1, $logger2),
                array(true, false),
            ),
            // only names
            array(
                array($logger1),
                array('test1', 'test2'),
                array(true, false),
            ),
            // mixed case
            array(
                array($logger1, $logger2),
                array('test1', $logger2, 'test3', $logger3),
                array(true, true, false, false),
            ),
        );
    }

    /**
     * @covers Monolog\Registry::clear
     */
    public function testClearClears()
    {
        Registry::addLogger(new Logger('test1'), 'log');
        Registry::clear();

        $this->setExpectedException('\InvalidArgumentException');
        Registry::getInstance('log');
    }

    /**
     * @dataProvider removedLoggerProvider
     * @covers Monolog\Registry::addLogger
     * @covers Monolog\Registry::removeLogger
     */
    public function testRemovesLogger($loggerToAdd, $remove)
    {
        Registry::addLogger($loggerToAdd);
        Registry::removeLogger($remove);

        $this->setExpectedException('\InvalidArgumentException');
        Registry::getInstance($loggerToAdd->getName());
    }

    public function removedLoggerProvider()
    {
        $logger1 = new Logger('test1');

        return array(
            array($logger1, $logger1),
            array($logger1, 'test1'),
        );
    }

    /**
     * @covers Monolog\Registry::addLogger
     * @covers Monolog\Registry::getInstance
     * @covers Monolog\Registry::__callStatic
     */
    public function testGetsSameLogger()
    {
        $logger1 = new Logger('test1');
        $logger2 = new Logger('test2');

        Registry::addLogger($logger1, 'test1');
        Registry::addLogger($logger2);

        $this->assertSame($logger1, Registry::getInstance('test1'));
        $this->assertSame($logger2, Registry::test2());
    }

    /**
     * @expectedException \InvalidArgumentException
     * @covers Monolog\Registry::getInstance
     */
    public function testFailsOnNonExistantLogger()
    {
        Registry::getInstance('test1');
    }

    /**
     * @covers Monolog\Registry::addLogger
     */
    public function testReplacesLogger()
    {
        $log1 = new Logger('test1');
        $log2 = new Logger('test2');

        Registry::addLogger($log1, 'log');

        Registry::addLogger($log2, 'log', true);

        $this->assertSame($log2, Registry::getInstance('log'));
    }

    /**
     * @expectedException \InvalidArgumentException
     * @covers Monolog\Registry::addLogger
     */
    public function testFailsOnUnspecifiedReplacement()
    {
        $log1 = new Logger('test1');
        $log2 = new Logger('test2');

        Registry::addLogger($log1, 'log');

        Registry::addLogger($log2, 'log');
    }
}
vendor/monolog/monolog/tests/Monolog/Formatter/GelfMessageFormatterTest.php000064400000020034151327705700023331 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog\Formatter;

use WPvividMonolog\Logger;

class GelfMessageFormatterTest extends \PHPUnit_Framework_TestCase
{
    public function setUp()
    {
        if (!class_exists('\Gelf\Message')) {
            $this->markTestSkipped("graylog2/gelf-php or mlehner/gelf-php is not installed");
        }
    }

    /**
     * @covers Monolog\Formatter\GelfMessageFormatter::format
     */
    public function testDefaultFormatter()
    {
        $formatter = new GelfMessageFormatter();
        $record = array(
            'level' => Logger::ERROR,
            'level_name' => 'ERROR',
            'channel' => 'meh',
            'context' => array(),
            'datetime' => new \DateTime("@0"),
            'extra' => array(),
            'message' => 'log',
        );

        $message = $formatter->format($record);

        $this->assertInstanceOf('Gelf\Message', $message);
        $this->assertEquals(0, $message->getTimestamp());
        $this->assertEquals('log', $message->getShortMessage());
        $this->assertEquals('meh', $message->getFacility());
        $this->assertEquals(null, $message->getLine());
        $this->assertEquals(null, $message->getFile());
        $this->assertEquals($this->isLegacy() ? 3 : 'error', $message->getLevel());
        $this->assertNotEmpty($message->getHost());

        $formatter = new GelfMessageFormatter('mysystem');

        $message = $formatter->format($record);

        $this->assertInstanceOf('Gelf\Message', $message);
        $this->assertEquals('mysystem', $message->getHost());
    }

    /**
     * @covers Monolog\Formatter\GelfMessageFormatter::format
     */
    public function testFormatWithFileAndLine()
    {
        $formatter = new GelfMessageFormatter();
        $record = array(
            'level' => Logger::ERROR,
            'level_name' => 'ERROR',
            'channel' => 'meh',
            'context' => array('from' => 'logger'),
            'datetime' => new \DateTime("@0"),
            'extra' => array('file' => 'test', 'line' => 14),
            'message' => 'log',
        );

        $message = $formatter->format($record);

        $this->assertInstanceOf('Gelf\Message', $message);
        $this->assertEquals('test', $message->getFile());
        $this->assertEquals(14, $message->getLine());
    }

    /**
     * @covers Monolog\Formatter\GelfMessageFormatter::format
     * @expectedException InvalidArgumentException
     */
    public function testFormatInvalidFails()
    {
        $formatter = new GelfMessageFormatter();
        $record = array(
            'level' => Logger::ERROR,
            'level_name' => 'ERROR',
        );

        $formatter->format($record);
    }

    /**
     * @covers Monolog\Formatter\GelfMessageFormatter::format
     */
    public function testFormatWithContext()
    {
        $formatter = new GelfMessageFormatter();
        $record = array(
            'level' => Logger::ERROR,
            'level_name' => 'ERROR',
            'channel' => 'meh',
            'context' => array('from' => 'logger'),
            'datetime' => new \DateTime("@0"),
            'extra' => array('key' => 'pair'),
            'message' => 'log',
        );

        $message = $formatter->format($record);

        $this->assertInstanceOf('Gelf\Message', $message);

        $message_array = $message->toArray();

        $this->assertArrayHasKey('_ctxt_from', $message_array);
        $this->assertEquals('logger', $message_array['_ctxt_from']);

        // Test with extraPrefix
        $formatter = new GelfMessageFormatter(null, null, 'CTX');
        $message = $formatter->format($record);

        $this->assertInstanceOf('Gelf\Message', $message);

        $message_array = $message->toArray();

        $this->assertArrayHasKey('_CTXfrom', $message_array);
        $this->assertEquals('logger', $message_array['_CTXfrom']);
    }

    /**
     * @covers Monolog\Formatter\GelfMessageFormatter::format
     */
    public function testFormatWithContextContainingException()
    {
        $formatter = new GelfMessageFormatter();
        $record = array(
            'level' => Logger::ERROR,
            'level_name' => 'ERROR',
            'channel' => 'meh',
            'context' => array('from' => 'logger', 'exception' => array(
                'class' => '\Exception',
                'file'  => '/some/file/in/dir.php:56',
                'trace' => array('/some/file/1.php:23', '/some/file/2.php:3'),
            )),
            'datetime' => new \DateTime("@0"),
            'extra' => array(),
            'message' => 'log',
        );

        $message = $formatter->format($record);

        $this->assertInstanceOf('Gelf\Message', $message);

        $this->assertEquals("/some/file/in/dir.php", $message->getFile());
        $this->assertEquals("56", $message->getLine());
    }

    /**
     * @covers Monolog\Formatter\GelfMessageFormatter::format
     */
    public function testFormatWithExtra()
    {
        $formatter = new GelfMessageFormatter();
        $record = array(
            'level' => Logger::ERROR,
            'level_name' => 'ERROR',
            'channel' => 'meh',
            'context' => array('from' => 'logger'),
            'datetime' => new \DateTime("@0"),
            'extra' => array('key' => 'pair'),
            'message' => 'log',
        );

        $message = $formatter->format($record);

        $this->assertInstanceOf('Gelf\Message', $message);

        $message_array = $message->toArray();

        $this->assertArrayHasKey('_key', $message_array);
        $this->assertEquals('pair', $message_array['_key']);

        // Test with extraPrefix
        $formatter = new GelfMessageFormatter(null, 'EXT');
        $message = $formatter->format($record);

        $this->assertInstanceOf('Gelf\Message', $message);

        $message_array = $message->toArray();

        $this->assertArrayHasKey('_EXTkey', $message_array);
        $this->assertEquals('pair', $message_array['_EXTkey']);
    }

    public function testFormatWithLargeData()
    {
        $formatter = new GelfMessageFormatter();
        $record = array(
            'level' => Logger::ERROR,
            'level_name' => 'ERROR',
            'channel' => 'meh',
            'context' => array('exception' => str_repeat(' ', 32767)),
            'datetime' => new \DateTime("@0"),
            'extra' => array('key' => str_repeat(' ', 32767)),
            'message' => 'log'
        );
        $message = $formatter->format($record);
        $messageArray = $message->toArray();

        // 200 for padding + metadata
        $length = 200;

        foreach ($messageArray as $key => $value) {
            if (!in_array($key, array('level', 'timestamp'))) {
                $length += strlen($value);
            }
        }

        $this->assertLessThanOrEqual(65792, $length, 'The message length is no longer than the maximum allowed length');
    }

    public function testFormatWithUnlimitedLength()
    {
        $formatter = new GelfMessageFormatter('LONG_SYSTEM_NAME', null, 'ctxt_', PHP_INT_MAX);
        $record = array(
            'level' => Logger::ERROR,
            'level_name' => 'ERROR',
            'channel' => 'meh',
            'context' => array('exception' => str_repeat(' ', 32767 * 2)),
            'datetime' => new \DateTime("@0"),
            'extra' => array('key' => str_repeat(' ', 32767 * 2)),
            'message' => 'log'
        );
        $message = $formatter->format($record);
        $messageArray = $message->toArray();

        // 200 for padding + metadata
        $length = 200;

        foreach ($messageArray as $key => $value) {
            if (!in_array($key, array('level', 'timestamp'))) {
                $length += strlen($value);
            }
        }

        $this->assertGreaterThanOrEqual(131289, $length, 'The message should not be truncated');
    }

    private function isLegacy()
    {
        return interface_exists('\Gelf\IMessagePublisher');
    }
}
vendor/monolog/monolog/tests/Monolog/Formatter/NormalizerFormatterTest.php000064400000036537151327705700023310 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog\Formatter;

/**
 * @covers Monolog\Formatter\NormalizerFormatter
 */
class NormalizerFormatterTest extends \PHPUnit_Framework_TestCase
{
    public function tearDown()
    {
        \PHPUnit_Framework_Error_Warning::$enabled = true;

        return parent::tearDown();
    }

    public function testFormat()
    {
        $formatter = new NormalizerFormatter('Y-m-d');
        $formatted = $formatter->format(array(
            'level_name' => 'ERROR',
            'channel' => 'meh',
            'message' => 'foo',
            'datetime' => new \DateTime,
            'extra' => array('foo' => new TestFooNorm, 'bar' => new TestBarNorm, 'baz' => array(), 'res' => fopen('php://memory', 'rb')),
            'context' => array(
                'foo' => 'bar',
                'baz' => 'qux',
                'inf' => INF,
                '-inf' => -INF,
                'nan' => acos(4),
            ),
        ));

        $this->assertEquals(array(
            'level_name' => 'ERROR',
            'channel' => 'meh',
            'message' => 'foo',
            'datetime' => date('Y-m-d'),
            'extra' => array(
                'foo' => '[object] (WPvividMonolog\\Formatter\\TestFooNorm: {"foo":"foo"})',
                'bar' => '[object] (WPvividMonolog\\Formatter\\TestBarNorm: bar)',
                'baz' => array(),
                'res' => '[resource] (stream)',
            ),
            'context' => array(
                'foo' => 'bar',
                'baz' => 'qux',
                'inf' => 'INF',
                '-inf' => '-INF',
                'nan' => 'NaN',
            ),
        ), $formatted);
    }

    public function testFormatExceptions()
    {
        $formatter = new NormalizerFormatter('Y-m-d');
        $e = new \LogicException('bar');
        $e2 = new \RuntimeException('foo', 0, $e);
        $formatted = $formatter->format(array(
            'exception' => $e2,
        ));

        $this->assertGreaterThan(5, count($formatted['exception']['trace']));
        $this->assertTrue(isset($formatted['exception']['previous']));
        unset($formatted['exception']['trace'], $formatted['exception']['previous']);

        $this->assertEquals(array(
            'exception' => array(
                'class'   => get_class($e2),
                'message' => $e2->getMessage(),
                'code'    => $e2->getCode(),
                'file'    => $e2->getFile().':'.$e2->getLine(),
            ),
        ), $formatted);
    }

    public function testFormatSoapFaultException()
    {
        if (!class_exists('SoapFault')) {
            $this->markTestSkipped('Requires the soap extension');
        }

        $formatter = new NormalizerFormatter('Y-m-d');
        $e = new \SoapFault('foo', 'bar', 'hello', 'world');
        $formatted = $formatter->format(array(
            'exception' => $e,
        ));

        unset($formatted['exception']['trace']);

        $this->assertEquals(array(
            'exception' => array(
                'class' => 'SoapFault',
                'message' => 'bar',
                'code' => 0,
                'file' => $e->getFile().':'.$e->getLine(),
                'faultcode' => 'foo',
                'faultactor' => 'hello',
                'detail' => 'world',
            ),
        ), $formatted);
    }

    public function testFormatToStringExceptionHandle()
    {
        $formatter = new NormalizerFormatter('Y-m-d');
        $this->setExpectedException('RuntimeException', 'Could not convert to string');
        $formatter->format(array(
            'myObject' => new TestToStringError(),
        ));
    }

    public function testBatchFormat()
    {
        $formatter = new NormalizerFormatter('Y-m-d');
        $formatted = $formatter->formatBatch(array(
            array(
                'level_name' => 'CRITICAL',
                'channel' => 'test',
                'message' => 'bar',
                'context' => array(),
                'datetime' => new \DateTime,
                'extra' => array(),
            ),
            array(
                'level_name' => 'WARNING',
                'channel' => 'log',
                'message' => 'foo',
                'context' => array(),
                'datetime' => new \DateTime,
                'extra' => array(),
            ),
        ));
        $this->assertEquals(array(
            array(
                'level_name' => 'CRITICAL',
                'channel' => 'test',
                'message' => 'bar',
                'context' => array(),
                'datetime' => date('Y-m-d'),
                'extra' => array(),
            ),
            array(
                'level_name' => 'WARNING',
                'channel' => 'log',
                'message' => 'foo',
                'context' => array(),
                'datetime' => date('Y-m-d'),
                'extra' => array(),
            ),
        ), $formatted);
    }

    /**
     * Test issue #137
     */
    public function testIgnoresRecursiveObjectReferences()
    {
        // set up the recursion
        $foo = new \stdClass();
        $bar = new \stdClass();

        $foo->bar = $bar;
        $bar->foo = $foo;

        // set an error handler to assert that the error is not raised anymore
        $that = $this;
        set_error_handler(function ($level, $message, $file, $line, $context) use ($that) {
            if (error_reporting() & $level) {
                restore_error_handler();
                $that->fail("$message should not be raised");
            }
        });

        $formatter = new NormalizerFormatter();
        $reflMethod = new \ReflectionMethod($formatter, 'toJson');
        $reflMethod->setAccessible(true);
        $res = $reflMethod->invoke($formatter, array($foo, $bar), true);

        restore_error_handler();

        $this->assertEquals(@json_encode(array($foo, $bar)), $res);
    }

    public function testCanNormalizeReferences()
    {
        $formatter = new NormalizerFormatter();
        $x = array('foo' => 'bar');
        $y = array('x' => &$x);
        $x['y'] = &$y;
        $formatter->format($y);
    }

    public function testIgnoresInvalidTypes()
    {
        // set up the recursion
        $resource = fopen(__FILE__, 'r');

        // set an error handler to assert that the error is not raised anymore
        $that = $this;
        set_error_handler(function ($level, $message, $file, $line, $context) use ($that) {
            if (error_reporting() & $level) {
                restore_error_handler();
                $that->fail("$message should not be raised");
            }
        });

        $formatter = new NormalizerFormatter();
        $reflMethod = new \ReflectionMethod($formatter, 'toJson');
        $reflMethod->setAccessible(true);
        $res = $reflMethod->invoke($formatter, array($resource), true);

        restore_error_handler();

        $this->assertEquals(@json_encode(array($resource)), $res);
    }

    public function testNormalizeHandleLargeArraysWithExactly1000Items()
    {
        $formatter = new NormalizerFormatter();
        $largeArray = range(1, 1000);

        $res = $formatter->format(array(
            'level_name' => 'CRITICAL',
            'channel' => 'test',
            'message' => 'bar',
            'context' => array($largeArray),
            'datetime' => new \DateTime,
            'extra' => array(),
        ));

        $this->assertCount(1000, $res['context'][0]);
        $this->assertArrayNotHasKey('...', $res['context'][0]);
    }

    public function testNormalizeHandleLargeArrays()
    {
        $formatter = new NormalizerFormatter();
        $largeArray = range(1, 2000);

        $res = $formatter->format(array(
            'level_name' => 'CRITICAL',
            'channel' => 'test',
            'message' => 'bar',
            'context' => array($largeArray),
            'datetime' => new \DateTime,
            'extra' => array(),
        ));

        $this->assertCount(1001, $res['context'][0]);
        $this->assertEquals('Over 1000 items (2000 total), aborting normalization', $res['context'][0]['...']);
    }

    /**
     * @expectedException RuntimeException
     */
    public function testThrowsOnInvalidEncoding()
    {
        if (version_compare(PHP_VERSION, '5.5.0', '<')) {
            // Ignore the warning that will be emitted by PHP <5.5.0
            \PHPUnit_Framework_Error_Warning::$enabled = false;
        }
        $formatter = new NormalizerFormatter();
        $reflMethod = new \ReflectionMethod($formatter, 'toJson');
        $reflMethod->setAccessible(true);

        // send an invalid unicode sequence as a object that can't be cleaned
        $record = new \stdClass;
        $record->message = "\xB1\x31";
        $res = $reflMethod->invoke($formatter, $record);
        if (PHP_VERSION_ID < 50500 && $res === '{"message":null}') {
            throw new \RuntimeException('PHP 5.3/5.4 throw a warning and null the value instead of returning false entirely');
        }
    }

    public function testConvertsInvalidEncodingAsLatin9()
    {
        if (version_compare(PHP_VERSION, '5.5.0', '<')) {
            // Ignore the warning that will be emitted by PHP <5.5.0
            \PHPUnit_Framework_Error_Warning::$enabled = false;
        }
        $formatter = new NormalizerFormatter();
        $reflMethod = new \ReflectionMethod($formatter, 'toJson');
        $reflMethod->setAccessible(true);

        $res = $reflMethod->invoke($formatter, array('message' => "\xA4\xA6\xA8\xB4\xB8\xBC\xBD\xBE"));

        if (version_compare(PHP_VERSION, '5.5.0', '>=')) {
            $this->assertSame('{"message":"€ŠšŽžŒœŸ"}', $res);
        } else {
            // PHP <5.5 does not return false for an element encoding failure,
            // instead it emits a warning (possibly) and nulls the value.
            $this->assertSame('{"message":null}', $res);
        }
    }

    /**
     * @param mixed $in     Input
     * @param mixed $expect Expected output
     * @covers Monolog\Formatter\NormalizerFormatter::detectAndCleanUtf8
     * @dataProvider providesDetectAndCleanUtf8
     */
    public function testDetectAndCleanUtf8($in, $expect)
    {
        $formatter = new NormalizerFormatter();
        $formatter->detectAndCleanUtf8($in);
        $this->assertSame($expect, $in);
    }

    public function providesDetectAndCleanUtf8()
    {
        $obj = new \stdClass;

        return array(
            'null' => array(null, null),
            'int' => array(123, 123),
            'float' => array(123.45, 123.45),
            'bool false' => array(false, false),
            'bool true' => array(true, true),
            'ascii string' => array('abcdef', 'abcdef'),
            'latin9 string' => array("\xB1\x31\xA4\xA6\xA8\xB4\xB8\xBC\xBD\xBE\xFF", '±1€ŠšŽžŒœŸÿ'),
            'unicode string' => array('¤¦¨´¸¼½¾€ŠšŽžŒœŸ', '¤¦¨´¸¼½¾€ŠšŽžŒœŸ'),
            'empty array' => array(array(), array()),
            'array' => array(array('abcdef'), array('abcdef')),
            'object' => array($obj, $obj),
        );
    }

    /**
     * @param int    $code
     * @param string $msg
     * @dataProvider providesHandleJsonErrorFailure
     */
    public function testHandleJsonErrorFailure($code, $msg)
    {
        $formatter = new NormalizerFormatter();
        $reflMethod = new \ReflectionMethod($formatter, 'handleJsonError');
        $reflMethod->setAccessible(true);

        $this->setExpectedException('RuntimeException', $msg);
        $reflMethod->invoke($formatter, $code, 'faked');
    }

    public function providesHandleJsonErrorFailure()
    {
        return array(
            'depth' => array(JSON_ERROR_DEPTH, 'Maximum stack depth exceeded'),
            'state' => array(JSON_ERROR_STATE_MISMATCH, 'Underflow or the modes mismatch'),
            'ctrl' => array(JSON_ERROR_CTRL_CHAR, 'Unexpected control character found'),
            'default' => array(-1, 'Unknown error'),
        );
    }

    public function testExceptionTraceWithArgs()
    {
        if (defined('HHVM_VERSION')) {
            $this->markTestSkipped('Not supported in HHVM since it detects errors differently');
        }

        // This happens i.e. in React promises or Guzzle streams where stream wrappers are registered
        // and no file or line are included in the trace because it's treated as internal function
        set_error_handler(function ($errno, $errstr, $errfile, $errline) {
            throw new \ErrorException($errstr, 0, $errno, $errfile, $errline);
        });

        try {
            // This will contain $resource and $wrappedResource as arguments in the trace item
            $resource = fopen('php://memory', 'rw+');
            fwrite($resource, 'test_resource');
            $wrappedResource = new TestFooNorm;
            $wrappedResource->foo = $resource;
            // Just do something stupid with a resource/wrapped resource as argument
            array_keys($wrappedResource);
        } catch (\Exception $e) {
            restore_error_handler();
        }

        $formatter = new NormalizerFormatter();
        $record = array('context' => array('exception' => $e));
        $result = $formatter->format($record);

        $this->assertRegExp(
            '%"resource":"\[resource\] \(stream\)"%',
            $result['context']['exception']['trace'][0]
        );

        if (version_compare(PHP_VERSION, '5.5.0', '>=')) {
            $pattern = '%"wrappedResource":"\[object\] \(WPvividMonolog\\\\\\\\Formatter\\\\\\\\TestFooNorm: \)"%';
        } else {
            $pattern = '%\\\\"foo\\\\":null%';
        }

        // Tests that the wrapped resource is ignored while encoding, only works for PHP <= 5.4
        $this->assertRegExp(
            $pattern,
            $result['context']['exception']['trace'][0]
        );
    }

    public function testExceptionTraceDoesNotLeakCallUserFuncArgs()
    {
        try {
            $arg = new TestInfoLeak;
            call_user_func(array($this, 'throwHelper'), $arg, $dt = new \DateTime());
        } catch (\Exception $e) {
        }

        $formatter = new NormalizerFormatter();
        $record = array('context' => array('exception' => $e));
        $result = $formatter->format($record);

        $this->assertSame(
            '{"function":"throwHelper","class":"WPvividMonolog\\\\Formatter\\\\NormalizerFormatterTest","type":"->","args":["[object] (Monolog\\\\Formatter\\\\TestInfoLeak)","'.$dt->format('Y-m-d H:i:s').'"]}',
            $result['context']['exception']['trace'][0]
        );
    }

    private function throwHelper($arg)
    {
        throw new \RuntimeException('Thrown');
    }
}

class TestFooNorm
{
    public $foo = 'foo';
}

class TestBarNorm
{
    public function __toString()
    {
        return 'bar';
    }
}

class TestStreamFoo
{
    public $foo;
    public $resource;

    public function __construct($resource)
    {
        $this->resource = $resource;
        $this->foo = 'BAR';
    }

    public function __toString()
    {
        fseek($this->resource, 0);

        return $this->foo . ' - ' . (string) stream_get_contents($this->resource);
    }
}

class TestToStringError
{
    public function __toString()
    {
        throw new \RuntimeException('Could not convert to string');
    }
}

class TestInfoLeak
{
    public function __toString()
    {
        return 'Sensitive information';
    }
}
vendor/monolog/monolog/tests/Monolog/Formatter/ScalarFormatterTest.php000064400000006323151327705700022361 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog\Formatter;

class ScalarFormatterTest extends \PHPUnit_Framework_TestCase
{
    private $formatter;

    public function setUp()
    {
        $this->formatter = new ScalarFormatter();
    }

    public function buildTrace(\Exception $e)
    {
        $data = array();
        $trace = $e->getTrace();
        foreach ($trace as $frame) {
            if (isset($frame['file'])) {
                $data[] = $frame['file'].':'.$frame['line'];
            } else {
                $data[] = json_encode($frame);
            }
        }

        return $data;
    }

    public function encodeJson($data)
    {
        if (version_compare(PHP_VERSION, '5.4.0', '>=')) {
            return json_encode($data, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
        }

        return json_encode($data);
    }

    public function testFormat()
    {
        $exception = new \Exception('foo');
        $formatted = $this->formatter->format(array(
            'foo' => 'string',
            'bar' => 1,
            'baz' => false,
            'bam' => array(1, 2, 3),
            'bat' => array('foo' => 'bar'),
            'bap' => \DateTime::createFromFormat(\DateTime::ISO8601, '1970-01-01T00:00:00+0000'),
            'ban' => $exception,
        ));

        $this->assertSame(array(
            'foo' => 'string',
            'bar' => 1,
            'baz' => false,
            'bam' => $this->encodeJson(array(1, 2, 3)),
            'bat' => $this->encodeJson(array('foo' => 'bar')),
            'bap' => '1970-01-01 00:00:00',
            'ban' => $this->encodeJson(array(
                'class'   => get_class($exception),
                'message' => $exception->getMessage(),
                'code'    => $exception->getCode(),
                'file'    => $exception->getFile() . ':' . $exception->getLine(),
                'trace'   => $this->buildTrace($exception),
            )),
        ), $formatted);
    }

    public function testFormatWithErrorContext()
    {
        $context = array('file' => 'foo', 'line' => 1);
        $formatted = $this->formatter->format(array(
            'context' => $context,
        ));

        $this->assertSame(array(
            'context' => $this->encodeJson($context),
        ), $formatted);
    }

    public function testFormatWithExceptionContext()
    {
        $exception = new \Exception('foo');
        $formatted = $this->formatter->format(array(
            'context' => array(
                'exception' => $exception,
            ),
        ));

        $this->assertSame(array(
            'context' => $this->encodeJson(array(
                'exception' => array(
                    'class'   => get_class($exception),
                    'message' => $exception->getMessage(),
                    'code'    => $exception->getCode(),
                    'file'    => $exception->getFile() . ':' . $exception->getLine(),
                    'trace'   => $this->buildTrace($exception),
                ),
            )),
        ), $formatted);
    }
}
vendor/monolog/monolog/tests/Monolog/Formatter/LineFormatterTest.php000064400000016731151327705700022047 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog\Formatter;

/**
 * @covers Monolog\Formatter\LineFormatter
 */
class LineFormatterTest extends \PHPUnit_Framework_TestCase
{
    public function testDefFormatWithString()
    {
        $formatter = new LineFormatter(null, 'Y-m-d');
        $message = $formatter->format(array(
            'level_name' => 'WARNING',
            'channel' => 'log',
            'context' => array(),
            'message' => 'foo',
            'datetime' => new \DateTime,
            'extra' => array(),
        ));
        $this->assertEquals('['.date('Y-m-d').'] log.WARNING: foo [] []'."\n", $message);
    }

    public function testDefFormatWithArrayContext()
    {
        $formatter = new LineFormatter(null, 'Y-m-d');
        $message = $formatter->format(array(
            'level_name' => 'ERROR',
            'channel' => 'meh',
            'message' => 'foo',
            'datetime' => new \DateTime,
            'extra' => array(),
            'context' => array(
                'foo' => 'bar',
                'baz' => 'qux',
                'bool' => false,
                'null' => null,
            ),
        ));
        $this->assertEquals('['.date('Y-m-d').'] meh.ERROR: foo {"foo":"bar","baz":"qux","bool":false,"null":null} []'."\n", $message);
    }

    public function testDefFormatExtras()
    {
        $formatter = new LineFormatter(null, 'Y-m-d');
        $message = $formatter->format(array(
            'level_name' => 'ERROR',
            'channel' => 'meh',
            'context' => array(),
            'datetime' => new \DateTime,
            'extra' => array('ip' => '127.0.0.1'),
            'message' => 'log',
        ));
        $this->assertEquals('['.date('Y-m-d').'] meh.ERROR: log [] {"ip":"127.0.0.1"}'."\n", $message);
    }

    public function testFormatExtras()
    {
        $formatter = new LineFormatter("[%datetime%] %channel%.%level_name%: %message% %context% %extra.file% %extra%\n", 'Y-m-d');
        $message = $formatter->format(array(
            'level_name' => 'ERROR',
            'channel' => 'meh',
            'context' => array(),
            'datetime' => new \DateTime,
            'extra' => array('ip' => '127.0.0.1', 'file' => 'test'),
            'message' => 'log',
        ));
        $this->assertEquals('['.date('Y-m-d').'] meh.ERROR: log [] test {"ip":"127.0.0.1"}'."\n", $message);
    }

    public function testContextAndExtraOptionallyNotShownIfEmpty()
    {
        $formatter = new LineFormatter(null, 'Y-m-d', false, true);
        $message = $formatter->format(array(
            'level_name' => 'ERROR',
            'channel' => 'meh',
            'context' => array(),
            'datetime' => new \DateTime,
            'extra' => array(),
            'message' => 'log',
        ));
        $this->assertEquals('['.date('Y-m-d').'] meh.ERROR: log  '."\n", $message);
    }

    public function testContextAndExtraReplacement()
    {
        $formatter = new LineFormatter('%context.foo% => %extra.foo%');
        $message = $formatter->format(array(
            'level_name' => 'ERROR',
            'channel' => 'meh',
            'context' => array('foo' => 'bar'),
            'datetime' => new \DateTime,
            'extra' => array('foo' => 'xbar'),
            'message' => 'log',
        ));
        $this->assertEquals('bar => xbar', $message);
    }

    public function testDefFormatWithObject()
    {
        $formatter = new LineFormatter(null, 'Y-m-d');
        $message = $formatter->format(array(
            'level_name' => 'ERROR',
            'channel' => 'meh',
            'context' => array(),
            'datetime' => new \DateTime,
            'extra' => array('foo' => new TestFoo, 'bar' => new TestBar, 'baz' => array(), 'res' => fopen('php://memory', 'rb')),
            'message' => 'foobar',
        ));

        $this->assertEquals('['.date('Y-m-d').'] meh.ERROR: foobar [] {"foo":"[object] (WPvividMonolog\\\\Formatter\\\\TestFoo: {\\"foo\\":\\"foo\\"})","bar":"[object] (Monolog\\\\Formatter\\\\TestBar: bar)","baz":[],"res":"[resource] (stream)"}'."\n", $message);
    }

    public function testDefFormatWithException()
    {
        $formatter = new LineFormatter(null, 'Y-m-d');
        $message = $formatter->format(array(
            'level_name' => 'CRITICAL',
            'channel' => 'core',
            'context' => array('exception' => new \RuntimeException('Foo')),
            'datetime' => new \DateTime,
            'extra' => array(),
            'message' => 'foobar',
        ));

        $path = str_replace('\\/', '/', json_encode(__FILE__));

        $this->assertEquals('['.date('Y-m-d').'] core.CRITICAL: foobar {"exception":"[object] (RuntimeException(code: 0): Foo at '.substr($path, 1, -1).':'.(__LINE__ - 8).')"} []'."\n", $message);
    }

    public function testDefFormatWithPreviousException()
    {
        $formatter = new LineFormatter(null, 'Y-m-d');
        $previous = new \LogicException('Wut?');
        $message = $formatter->format(array(
            'level_name' => 'CRITICAL',
            'channel' => 'core',
            'context' => array('exception' => new \RuntimeException('Foo', 0, $previous)),
            'datetime' => new \DateTime,
            'extra' => array(),
            'message' => 'foobar',
        ));

        $path = str_replace('\\/', '/', json_encode(__FILE__));

        $this->assertEquals('['.date('Y-m-d').'] core.CRITICAL: foobar {"exception":"[object] (RuntimeException(code: 0): Foo at '.substr($path, 1, -1).':'.(__LINE__ - 8).', LogicException(code: 0): Wut? at '.substr($path, 1, -1).':'.(__LINE__ - 12).')"} []'."\n", $message);
    }

    public function testBatchFormat()
    {
        $formatter = new LineFormatter(null, 'Y-m-d');
        $message = $formatter->formatBatch(array(
            array(
                'level_name' => 'CRITICAL',
                'channel' => 'test',
                'message' => 'bar',
                'context' => array(),
                'datetime' => new \DateTime,
                'extra' => array(),
            ),
            array(
                'level_name' => 'WARNING',
                'channel' => 'log',
                'message' => 'foo',
                'context' => array(),
                'datetime' => new \DateTime,
                'extra' => array(),
            ),
        ));
        $this->assertEquals('['.date('Y-m-d').'] test.CRITICAL: bar [] []'."\n".'['.date('Y-m-d').'] log.WARNING: foo [] []'."\n", $message);
    }

    public function testFormatShouldStripInlineLineBreaks()
    {
        $formatter = new LineFormatter(null, 'Y-m-d');
        $message = $formatter->format(
            array(
                'message' => "foo\nbar",
                'context' => array(),
                'extra' => array(),
            )
        );

        $this->assertRegExp('/foo bar/', $message);
    }

    public function testFormatShouldNotStripInlineLineBreaksWhenFlagIsSet()
    {
        $formatter = new LineFormatter(null, 'Y-m-d', true);
        $message = $formatter->format(
            array(
                'message' => "foo\nbar",
                'context' => array(),
                'extra' => array(),
            )
        );

        $this->assertRegExp('/foo\nbar/', $message);
    }
}

class TestFoo
{
    public $foo = 'foo';
}

class TestBar
{
    public function __toString()
    {
        return 'bar';
    }
}
vendor/monolog/monolog/tests/Monolog/Formatter/FlowdockFormatterTest.php000064400000003036151327705700022722 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog\Formatter;

use WPvividMonolog\Logger;
use WPvividMonolog\TestCase;

class FlowdockFormatterTest extends TestCase
{
    /**
     * @covers Monolog\Formatter\FlowdockFormatter::format
     */
    public function testFormat()
    {
        $formatter = new FlowdockFormatter('test_source', 'source@test.com');
        $record = $this->getRecord();

        $expected = array(
            'source' => 'test_source',
            'from_address' => 'source@test.com',
            'subject' => 'in test_source: WARNING - test',
            'content' => 'test',
            'tags' => array('#logs', '#warning', '#test'),
            'project' => 'test_source',
        );
        $formatted = $formatter->format($record);

        $this->assertEquals($expected, $formatted['flowdock']);
    }

    /**
     * @ covers Monolog\Formatter\FlowdockFormatter::formatBatch
     */
    public function testFormatBatch()
    {
        $formatter = new FlowdockFormatter('test_source', 'source@test.com');
        $records = array(
            $this->getRecord(Logger::WARNING),
            $this->getRecord(Logger::DEBUG),
        );
        $formatted = $formatter->formatBatch($records);

        $this->assertArrayHasKey('flowdock', $formatted[0]);
        $this->assertArrayHasKey('flowdock', $formatted[1]);
    }
}
vendor/monolog/monolog/tests/Monolog/Formatter/LogglyFormatterTest.php000064400000002311151327705700022402 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog\Formatter;

use WPvividMonolog\TestCase;

class LogglyFormatterTest extends TestCase
{
    /**
     * @covers Monolog\Formatter\LogglyFormatter::__construct
     */
    public function testConstruct()
    {
        $formatter = new LogglyFormatter();
        $this->assertEquals(LogglyFormatter::BATCH_MODE_NEWLINES, $formatter->getBatchMode());
        $formatter = new LogglyFormatter(LogglyFormatter::BATCH_MODE_JSON);
        $this->assertEquals(LogglyFormatter::BATCH_MODE_JSON, $formatter->getBatchMode());
    }

    /**
     * @covers Monolog\Formatter\LogglyFormatter::format
     */
    public function testFormat()
    {
        $formatter = new LogglyFormatter();
        $record = $this->getRecord();
        $formatted_decoded = json_decode($formatter->format($record), true);
        $this->assertArrayHasKey("timestamp", $formatted_decoded);
        $this->assertEquals(new \DateTime($formatted_decoded["timestamp"]), $record["datetime"]);
    }
}
vendor/monolog/monolog/tests/Monolog/Formatter/JsonFormatterTest.php000064400000016202151327705700022062 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog\Formatter;

use WPvividMonolog\Logger;
use WPvividMonolog\TestCase;

class JsonFormatterTest extends TestCase
{
    /**
     * @covers Monolog\Formatter\JsonFormatter::__construct
     * @covers Monolog\Formatter\JsonFormatter::getBatchMode
     * @covers Monolog\Formatter\JsonFormatter::isAppendingNewlines
     */
    public function testConstruct()
    {
        $formatter = new JsonFormatter();
        $this->assertEquals(JsonFormatter::BATCH_MODE_JSON, $formatter->getBatchMode());
        $this->assertEquals(true, $formatter->isAppendingNewlines());
        $formatter = new JsonFormatter(JsonFormatter::BATCH_MODE_NEWLINES, false);
        $this->assertEquals(JsonFormatter::BATCH_MODE_NEWLINES, $formatter->getBatchMode());
        $this->assertEquals(false, $formatter->isAppendingNewlines());
    }

    /**
     * @covers Monolog\Formatter\JsonFormatter::format
     */
    public function testFormat()
    {
        $formatter = new JsonFormatter();
        $record = $this->getRecord();
        $this->assertEquals(json_encode($record)."\n", $formatter->format($record));

        $formatter = new JsonFormatter(JsonFormatter::BATCH_MODE_JSON, false);
        $record = $this->getRecord();
        $this->assertEquals(json_encode($record), $formatter->format($record));
    }

    /**
     * @covers Monolog\Formatter\JsonFormatter::formatBatch
     * @covers Monolog\Formatter\JsonFormatter::formatBatchJson
     */
    public function testFormatBatch()
    {
        $formatter = new JsonFormatter();
        $records = array(
            $this->getRecord(Logger::WARNING),
            $this->getRecord(Logger::DEBUG),
        );
        $this->assertEquals(json_encode($records), $formatter->formatBatch($records));
    }

    /**
     * @covers Monolog\Formatter\JsonFormatter::formatBatch
     * @covers Monolog\Formatter\JsonFormatter::formatBatchNewlines
     */
    public function testFormatBatchNewlines()
    {
        $formatter = new JsonFormatter(JsonFormatter::BATCH_MODE_NEWLINES);
        $records = $expected = array(
            $this->getRecord(Logger::WARNING),
            $this->getRecord(Logger::DEBUG),
        );
        array_walk($expected, function (&$value, $key) {
            $value = json_encode($value);
        });
        $this->assertEquals(implode("\n", $expected), $formatter->formatBatch($records));
    }

    public function testDefFormatWithException()
    {
        $formatter = new JsonFormatter();
        $exception = new \RuntimeException('Foo');
        $formattedException = $this->formatException($exception);

        $message = $this->formatRecordWithExceptionInContext($formatter, $exception);

        $this->assertContextContainsFormattedException($formattedException, $message);
    }

    public function testDefFormatWithPreviousException()
    {
        $formatter = new JsonFormatter();
        $exception = new \RuntimeException('Foo', 0, new \LogicException('Wut?'));
        $formattedPrevException = $this->formatException($exception->getPrevious());
        $formattedException = $this->formatException($exception, $formattedPrevException);

        $message = $this->formatRecordWithExceptionInContext($formatter, $exception);

        $this->assertContextContainsFormattedException($formattedException, $message);
    }

    public function testDefFormatWithThrowable()
    {
        if (!class_exists('Error') || !is_subclass_of('Error', 'Throwable')) {
            $this->markTestSkipped('Requires PHP >=7');
        }

        $formatter = new JsonFormatter();
        $throwable = new \Error('Foo');
        $formattedThrowable = $this->formatException($throwable);

        $message = $this->formatRecordWithExceptionInContext($formatter, $throwable);

        $this->assertContextContainsFormattedException($formattedThrowable, $message);
    }

    /**
     * @param string $expected
     * @param string $actual
     *
     * @internal param string $exception
     */
    private function assertContextContainsFormattedException($expected, $actual)
    {
        $this->assertEquals(
            '{"level_name":"CRITICAL","channel":"core","context":{"exception":'.$expected.'},"datetime":null,"extra":[],"message":"foobar"}'."\n",
            $actual
        );
    }

    /**
     * @param JsonFormatter $formatter
     * @param \Exception|\Throwable $exception
     *
     * @return string
     */
    private function formatRecordWithExceptionInContext(JsonFormatter $formatter, $exception)
    {
        $message = $formatter->format(array(
            'level_name' => 'CRITICAL',
            'channel' => 'core',
            'context' => array('exception' => $exception),
            'datetime' => null,
            'extra' => array(),
            'message' => 'foobar',
        ));
        return $message;
    }

    /**
     * @param \Exception|\Throwable $exception
     *
     * @return string
     */
    private function formatExceptionFilePathWithLine($exception)
    {
        $options = 0;
        if (version_compare(PHP_VERSION, '5.4.0', '>=')) {
            $options = JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE;
        }
        $path = substr(json_encode($exception->getFile(), $options), 1, -1);
        return $path . ':' . $exception->getLine();
    }

    /**
     * @param \Exception|\Throwable $exception
     *
     * @param null|string $previous
     *
     * @return string
     */
    private function formatException($exception, $previous = null)
    {
        $formattedException =
            '{"class":"' . get_class($exception) .
            '","message":"' . $exception->getMessage() .
            '","code":' . $exception->getCode() .
            ',"file":"' . $this->formatExceptionFilePathWithLine($exception) .
            ($previous ? '","previous":' . $previous : '"') .
            '}';
        return $formattedException;
    }

    public function testNormalizeHandleLargeArraysWithExactly1000Items()
    {
        $formatter = new NormalizerFormatter();
        $largeArray = range(1, 1000);

        $res = $formatter->format(array(
            'level_name' => 'CRITICAL',
            'channel' => 'test',
            'message' => 'bar',
            'context' => array($largeArray),
            'datetime' => new \DateTime,
            'extra' => array(),
        ));

        $this->assertCount(1000, $res['context'][0]);
        $this->assertArrayNotHasKey('...', $res['context'][0]);
    }

    public function testNormalizeHandleLargeArrays()
    {
        $formatter = new NormalizerFormatter();
        $largeArray = range(1, 2000);

        $res = $formatter->format(array(
            'level_name' => 'CRITICAL',
            'channel' => 'test',
            'message' => 'bar',
            'context' => array($largeArray),
            'datetime' => new \DateTime,
            'extra' => array(),
        ));

        $this->assertCount(1001, $res['context'][0]);
        $this->assertEquals('Over 1000 items (2000 total), aborting normalization', $res['context'][0]['...']);
    }
}
vendor/monolog/monolog/tests/Monolog/Formatter/LogstashFormatterTest.php000064400000026631151327705700022744 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog\Formatter;

use WPvividMonolog\Logger;

class LogstashFormatterTest extends \PHPUnit_Framework_TestCase
{
    public function tearDown()
    {
        \PHPUnit_Framework_Error_Warning::$enabled = true;

        return parent::tearDown();
    }

    /**
     * @covers Monolog\Formatter\LogstashFormatter::format
     */
    public function testDefaultFormatter()
    {
        $formatter = new LogstashFormatter('test', 'hostname');
        $record = array(
            'level' => Logger::ERROR,
            'level_name' => 'ERROR',
            'channel' => 'meh',
            'context' => array(),
            'datetime' => new \DateTime("@0"),
            'extra' => array(),
            'message' => 'log',
        );

        $message = json_decode($formatter->format($record), true);

        $this->assertEquals("1970-01-01T00:00:00.000000+00:00", $message['@timestamp']);
        $this->assertEquals('log', $message['@message']);
        $this->assertEquals('meh', $message['@fields']['channel']);
        $this->assertContains('meh', $message['@tags']);
        $this->assertEquals(Logger::ERROR, $message['@fields']['level']);
        $this->assertEquals('test', $message['@type']);
        $this->assertEquals('hostname', $message['@source']);

        $formatter = new LogstashFormatter('mysystem');

        $message = json_decode($formatter->format($record), true);

        $this->assertEquals('mysystem', $message['@type']);
    }

    /**
     * @covers Monolog\Formatter\LogstashFormatter::format
     */
    public function testFormatWithFileAndLine()
    {
        $formatter = new LogstashFormatter('test');
        $record = array(
            'level' => Logger::ERROR,
            'level_name' => 'ERROR',
            'channel' => 'meh',
            'context' => array('from' => 'logger'),
            'datetime' => new \DateTime("@0"),
            'extra' => array('file' => 'test', 'line' => 14),
            'message' => 'log',
        );

        $message = json_decode($formatter->format($record), true);

        $this->assertEquals('test', $message['@fields']['file']);
        $this->assertEquals(14, $message['@fields']['line']);
    }

    /**
     * @covers Monolog\Formatter\LogstashFormatter::format
     */
    public function testFormatWithContext()
    {
        $formatter = new LogstashFormatter('test');
        $record = array(
            'level' => Logger::ERROR,
            'level_name' => 'ERROR',
            'channel' => 'meh',
            'context' => array('from' => 'logger'),
            'datetime' => new \DateTime("@0"),
            'extra' => array('key' => 'pair'),
            'message' => 'log',
        );

        $message = json_decode($formatter->format($record), true);

        $message_array = $message['@fields'];

        $this->assertArrayHasKey('ctxt_from', $message_array);
        $this->assertEquals('logger', $message_array['ctxt_from']);

        // Test with extraPrefix
        $formatter = new LogstashFormatter('test', null, null, 'CTX');
        $message = json_decode($formatter->format($record), true);

        $message_array = $message['@fields'];

        $this->assertArrayHasKey('CTXfrom', $message_array);
        $this->assertEquals('logger', $message_array['CTXfrom']);
    }

    /**
     * @covers Monolog\Formatter\LogstashFormatter::format
     */
    public function testFormatWithExtra()
    {
        $formatter = new LogstashFormatter('test');
        $record = array(
            'level' => Logger::ERROR,
            'level_name' => 'ERROR',
            'channel' => 'meh',
            'context' => array('from' => 'logger'),
            'datetime' => new \DateTime("@0"),
            'extra' => array('key' => 'pair'),
            'message' => 'log',
        );

        $message = json_decode($formatter->format($record), true);

        $message_array = $message['@fields'];

        $this->assertArrayHasKey('key', $message_array);
        $this->assertEquals('pair', $message_array['key']);

        // Test with extraPrefix
        $formatter = new LogstashFormatter('test', null, 'EXT');
        $message = json_decode($formatter->format($record), true);

        $message_array = $message['@fields'];

        $this->assertArrayHasKey('EXTkey', $message_array);
        $this->assertEquals('pair', $message_array['EXTkey']);
    }

    public function testFormatWithApplicationName()
    {
        $formatter = new LogstashFormatter('app', 'test');
        $record = array(
            'level' => Logger::ERROR,
            'level_name' => 'ERROR',
            'channel' => 'meh',
            'context' => array('from' => 'logger'),
            'datetime' => new \DateTime("@0"),
            'extra' => array('key' => 'pair'),
            'message' => 'log',
        );

        $message = json_decode($formatter->format($record), true);

        $this->assertArrayHasKey('@type', $message);
        $this->assertEquals('app', $message['@type']);
    }

    /**
     * @covers Monolog\Formatter\LogstashFormatter::format
     */
    public function testDefaultFormatterV1()
    {
        $formatter = new LogstashFormatter('test', 'hostname', null, 'ctxt_', LogstashFormatter::V1);
        $record = array(
            'level' => Logger::ERROR,
            'level_name' => 'ERROR',
            'channel' => 'meh',
            'context' => array(),
            'datetime' => new \DateTime("@0"),
            'extra' => array(),
            'message' => 'log',
        );

        $message = json_decode($formatter->format($record), true);

        $this->assertEquals("1970-01-01T00:00:00.000000+00:00", $message['@timestamp']);
        $this->assertEquals("1", $message['@version']);
        $this->assertEquals('log', $message['message']);
        $this->assertEquals('meh', $message['channel']);
        $this->assertEquals('ERROR', $message['level']);
        $this->assertEquals('test', $message['type']);
        $this->assertEquals('hostname', $message['host']);

        $formatter = new LogstashFormatter('mysystem', null, null, 'ctxt_', LogstashFormatter::V1);

        $message = json_decode($formatter->format($record), true);

        $this->assertEquals('mysystem', $message['type']);
    }

    /**
     * @covers Monolog\Formatter\LogstashFormatter::format
     */
    public function testFormatWithFileAndLineV1()
    {
        $formatter = new LogstashFormatter('test', null, null, 'ctxt_', LogstashFormatter::V1);
        $record = array(
            'level' => Logger::ERROR,
            'level_name' => 'ERROR',
            'channel' => 'meh',
            'context' => array('from' => 'logger'),
            'datetime' => new \DateTime("@0"),
            'extra' => array('file' => 'test', 'line' => 14),
            'message' => 'log',
        );

        $message = json_decode($formatter->format($record), true);

        $this->assertEquals('test', $message['file']);
        $this->assertEquals(14, $message['line']);
    }

    /**
     * @covers Monolog\Formatter\LogstashFormatter::format
     */
    public function testFormatWithContextV1()
    {
        $formatter = new LogstashFormatter('test', null, null, 'ctxt_', LogstashFormatter::V1);
        $record = array(
            'level' => Logger::ERROR,
            'level_name' => 'ERROR',
            'channel' => 'meh',
            'context' => array('from' => 'logger'),
            'datetime' => new \DateTime("@0"),
            'extra' => array('key' => 'pair'),
            'message' => 'log',
        );

        $message = json_decode($formatter->format($record), true);

        $this->assertArrayHasKey('ctxt_from', $message);
        $this->assertEquals('logger', $message['ctxt_from']);

        // Test with extraPrefix
        $formatter = new LogstashFormatter('test', null, null, 'CTX', LogstashFormatter::V1);
        $message = json_decode($formatter->format($record), true);

        $this->assertArrayHasKey('CTXfrom', $message);
        $this->assertEquals('logger', $message['CTXfrom']);
    }

    /**
     * @covers Monolog\Formatter\LogstashFormatter::format
     */
    public function testFormatWithExtraV1()
    {
        $formatter = new LogstashFormatter('test', null, null, 'ctxt_', LogstashFormatter::V1);
        $record = array(
            'level' => Logger::ERROR,
            'level_name' => 'ERROR',
            'channel' => 'meh',
            'context' => array('from' => 'logger'),
            'datetime' => new \DateTime("@0"),
            'extra' => array('key' => 'pair'),
            'message' => 'log',
        );

        $message = json_decode($formatter->format($record), true);

        $this->assertArrayHasKey('key', $message);
        $this->assertEquals('pair', $message['key']);

        // Test with extraPrefix
        $formatter = new LogstashFormatter('test', null, 'EXT', 'ctxt_', LogstashFormatter::V1);
        $message = json_decode($formatter->format($record), true);

        $this->assertArrayHasKey('EXTkey', $message);
        $this->assertEquals('pair', $message['EXTkey']);
    }

    public function testFormatWithApplicationNameV1()
    {
        $formatter = new LogstashFormatter('app', 'test', null, 'ctxt_', LogstashFormatter::V1);
        $record = array(
            'level' => Logger::ERROR,
            'level_name' => 'ERROR',
            'channel' => 'meh',
            'context' => array('from' => 'logger'),
            'datetime' => new \DateTime("@0"),
            'extra' => array('key' => 'pair'),
            'message' => 'log',
        );

        $message = json_decode($formatter->format($record), true);

        $this->assertArrayHasKey('type', $message);
        $this->assertEquals('app', $message['type']);
    }

    public function testFormatWithLatin9Data()
    {
        if (version_compare(PHP_VERSION, '5.5.0', '<')) {
            // Ignore the warning that will be emitted by PHP <5.5.0
            \PHPUnit_Framework_Error_Warning::$enabled = false;
        }
        $formatter = new LogstashFormatter('test', 'hostname');
        $record = array(
            'level' => Logger::ERROR,
            'level_name' => 'ERROR',
            'channel' => '¯\_(ツ)_/¯',
            'context' => array(),
            'datetime' => new \DateTime("@0"),
            'extra' => array(
                'user_agent' => "\xD6WN; FBCR/OrangeEspa\xF1a; Vers\xE3o/4.0; F\xE4rist",
            ),
            'message' => 'log',
        );

        $message = json_decode($formatter->format($record), true);

        $this->assertEquals("1970-01-01T00:00:00.000000+00:00", $message['@timestamp']);
        $this->assertEquals('log', $message['@message']);
        $this->assertEquals('¯\_(ツ)_/¯', $message['@fields']['channel']);
        $this->assertContains('¯\_(ツ)_/¯', $message['@tags']);
        $this->assertEquals(Logger::ERROR, $message['@fields']['level']);
        $this->assertEquals('test', $message['@type']);
        $this->assertEquals('hostname', $message['@source']);
        if (version_compare(PHP_VERSION, '5.5.0', '>=')) {
            $this->assertEquals('ÖWN; FBCR/OrangeEspaña; Versão/4.0; Färist', $message['@fields']['user_agent']);
        } else {
            // PHP <5.5 does not return false for an element encoding failure,
            // instead it emits a warning (possibly) and nulls the value.
            $this->assertEquals(null, $message['@fields']['user_agent']);
        }
    }
}
vendor/monolog/monolog/tests/Monolog/Formatter/FluentdFormatterTest.php000064400000003430151327705700022551 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog\Formatter;

use WPvividMonolog\Logger;
use WPvividMonolog\TestCase;

class FluentdFormatterTest extends TestCase
{
    /**
     * @covers Monolog\Formatter\FluentdFormatter::__construct
     * @covers Monolog\Formatter\FluentdFormatter::isUsingLevelsInTag
     */
    public function testConstruct()
    {
        $formatter = new FluentdFormatter();
        $this->assertEquals(false, $formatter->isUsingLevelsInTag());
        $formatter = new FluentdFormatter(false);
        $this->assertEquals(false, $formatter->isUsingLevelsInTag());
        $formatter = new FluentdFormatter(true);
        $this->assertEquals(true, $formatter->isUsingLevelsInTag());
    }

    /**
     * @covers Monolog\Formatter\FluentdFormatter::format
     */
    public function testFormat()
    {
        $record = $this->getRecord(Logger::WARNING);
        $record['datetime'] = new \DateTime("@0");

        $formatter = new FluentdFormatter();
        $this->assertEquals(
            '["test",0,{"message":"test","context":[],"extra":[],"level":300,"level_name":"WARNING"}]',
            $formatter->format($record)
        );
    }

    /**
     * @covers Monolog\Formatter\FluentdFormatter::format
     */
    public function testFormatWithTag()
    {
        $record = $this->getRecord(Logger::ERROR);
        $record['datetime'] = new \DateTime("@0");

        $formatter = new FluentdFormatter(true);
        $this->assertEquals(
            '["test.error",0,{"message":"test","context":[],"extra":[]}]',
            $formatter->format($record)
        );
    }
}
vendor/monolog/monolog/tests/Monolog/Formatter/WildfireFormatterTest.php000064400000010240151327705700022712 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog\Formatter;

use WPvividMonolog\Logger;

class WildfireFormatterTest extends \PHPUnit_Framework_TestCase
{
    /**
     * @covers Monolog\Formatter\WildfireFormatter::format
     */
    public function testDefaultFormat()
    {
        $wildfire = new WildfireFormatter();
        $record = array(
            'level' => Logger::ERROR,
            'level_name' => 'ERROR',
            'channel' => 'meh',
            'context' => array('from' => 'logger'),
            'datetime' => new \DateTime("@0"),
            'extra' => array('ip' => '127.0.0.1'),
            'message' => 'log',
        );

        $message = $wildfire->format($record);

        $this->assertEquals(
            '125|[{"Type":"ERROR","File":"","Line":"","Label":"meh"},'
                .'{"message":"log","context":{"from":"logger"},"extra":{"ip":"127.0.0.1"}}]|',
            $message
        );
    }

    /**
     * @covers Monolog\Formatter\WildfireFormatter::format
     */
    public function testFormatWithFileAndLine()
    {
        $wildfire = new WildfireFormatter();
        $record = array(
            'level' => Logger::ERROR,
            'level_name' => 'ERROR',
            'channel' => 'meh',
            'context' => array('from' => 'logger'),
            'datetime' => new \DateTime("@0"),
            'extra' => array('ip' => '127.0.0.1', 'file' => 'test', 'line' => 14),
            'message' => 'log',
        );

        $message = $wildfire->format($record);

        $this->assertEquals(
            '129|[{"Type":"ERROR","File":"test","Line":14,"Label":"meh"},'
                .'{"message":"log","context":{"from":"logger"},"extra":{"ip":"127.0.0.1"}}]|',
            $message
        );
    }

    /**
     * @covers Monolog\Formatter\WildfireFormatter::format
     */
    public function testFormatWithoutContext()
    {
        $wildfire = new WildfireFormatter();
        $record = array(
            'level' => Logger::ERROR,
            'level_name' => 'ERROR',
            'channel' => 'meh',
            'context' => array(),
            'datetime' => new \DateTime("@0"),
            'extra' => array(),
            'message' => 'log',
        );

        $message = $wildfire->format($record);

        $this->assertEquals(
            '58|[{"Type":"ERROR","File":"","Line":"","Label":"meh"},"log"]|',
            $message
        );
    }

    /**
     * @covers Monolog\Formatter\WildfireFormatter::formatBatch
     * @expectedException BadMethodCallException
     */
    public function testBatchFormatThrowException()
    {
        $wildfire = new WildfireFormatter();
        $record = array(
            'level' => Logger::ERROR,
            'level_name' => 'ERROR',
            'channel' => 'meh',
            'context' => array(),
            'datetime' => new \DateTime("@0"),
            'extra' => array(),
            'message' => 'log',
        );

        $wildfire->formatBatch(array($record));
    }

    /**
     * @covers Monolog\Formatter\WildfireFormatter::format
     */
    public function testTableFormat()
    {
        $wildfire = new WildfireFormatter();
        $record = array(
            'level' => Logger::ERROR,
            'level_name' => 'ERROR',
            'channel' => 'table-channel',
            'context' => array(
            WildfireFormatter::TABLE => array(
                    array('col1', 'col2', 'col3'),
                    array('val1', 'val2', 'val3'),
                    array('foo1', 'foo2', 'foo3'),
                    array('bar1', 'bar2', 'bar3'),
                ),
            ),
            'datetime' => new \DateTime("@0"),
            'extra' => array(),
            'message' => 'table-message',
        );

        $message = $wildfire->format($record);

        $this->assertEquals(
            '171|[{"Type":"TABLE","File":"","Line":"","Label":"table-channel: table-message"},[["col1","col2","col3"],["val1","val2","val3"],["foo1","foo2","foo3"],["bar1","bar2","bar3"]]]|',
            $message
        );
    }
}
vendor/monolog/monolog/tests/Monolog/Formatter/MongoDBFormatterTest.php000064400000022042151327705700022435 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog\Formatter;

use WPvividMonolog\Logger;

/**
 * @author Florian Plattner <me@florianplattner.de>
 */
class MongoDBFormatterTest extends \PHPUnit_Framework_TestCase
{
    public function setUp()
    {
        if (!class_exists('MongoDate')) {
            $this->markTestSkipped('mongo extension not installed');
        }
    }

    public function constructArgumentProvider()
    {
        return array(
            array(1, true, 1, true),
            array(0, false, 0, false),
        );
    }

    /**
     * @param $traceDepth
     * @param $traceAsString
     * @param $expectedTraceDepth
     * @param $expectedTraceAsString
     *
     * @dataProvider constructArgumentProvider
     */
    public function testConstruct($traceDepth, $traceAsString, $expectedTraceDepth, $expectedTraceAsString)
    {
        $formatter = new MongoDBFormatter($traceDepth, $traceAsString);

        $reflTrace = new \ReflectionProperty($formatter, 'exceptionTraceAsString');
        $reflTrace->setAccessible(true);
        $this->assertEquals($expectedTraceAsString, $reflTrace->getValue($formatter));

        $reflDepth = new\ReflectionProperty($formatter, 'maxNestingLevel');
        $reflDepth->setAccessible(true);
        $this->assertEquals($expectedTraceDepth, $reflDepth->getValue($formatter));
    }

    public function testSimpleFormat()
    {
        $record = array(
            'message' => 'some log message',
            'context' => array(),
            'level' => Logger::WARNING,
            'level_name' => Logger::getLevelName(Logger::WARNING),
            'channel' => 'test',
            'datetime' => new \DateTime('2014-02-01 00:00:00'),
            'extra' => array(),
        );

        $formatter = new MongoDBFormatter();
        $formattedRecord = $formatter->format($record);

        $this->assertCount(7, $formattedRecord);
        $this->assertEquals('some log message', $formattedRecord['message']);
        $this->assertEquals(array(), $formattedRecord['context']);
        $this->assertEquals(Logger::WARNING, $formattedRecord['level']);
        $this->assertEquals(Logger::getLevelName(Logger::WARNING), $formattedRecord['level_name']);
        $this->assertEquals('test', $formattedRecord['channel']);
        $this->assertInstanceOf('\MongoDate', $formattedRecord['datetime']);
        $this->assertEquals('0.00000000 1391212800', $formattedRecord['datetime']->__toString());
        $this->assertEquals(array(), $formattedRecord['extra']);
    }

    public function testRecursiveFormat()
    {
        $someObject = new \stdClass();
        $someObject->foo = 'something';
        $someObject->bar = 'stuff';

        $record = array(
            'message' => 'some log message',
            'context' => array(
                'stuff' => new \DateTime('2014-02-01 02:31:33'),
                'some_object' => $someObject,
                'context_string' => 'some string',
                'context_int' => 123456,
                'except' => new \Exception('exception message', 987),
            ),
            'level' => Logger::WARNING,
            'level_name' => Logger::getLevelName(Logger::WARNING),
            'channel' => 'test',
            'datetime' => new \DateTime('2014-02-01 00:00:00'),
            'extra' => array(),
        );

        $formatter = new MongoDBFormatter();
        $formattedRecord = $formatter->format($record);

        $this->assertCount(5, $formattedRecord['context']);
        $this->assertInstanceOf('\MongoDate', $formattedRecord['context']['stuff']);
        $this->assertEquals('0.00000000 1391221893', $formattedRecord['context']['stuff']->__toString());
        $this->assertEquals(
            array(
                'foo' => 'something',
                'bar' => 'stuff',
                'class' => 'stdClass',
            ),
            $formattedRecord['context']['some_object']
        );
        $this->assertEquals('some string', $formattedRecord['context']['context_string']);
        $this->assertEquals(123456, $formattedRecord['context']['context_int']);

        $this->assertCount(5, $formattedRecord['context']['except']);
        $this->assertEquals('exception message', $formattedRecord['context']['except']['message']);
        $this->assertEquals(987, $formattedRecord['context']['except']['code']);
        $this->assertInternalType('string', $formattedRecord['context']['except']['file']);
        $this->assertInternalType('integer', $formattedRecord['context']['except']['code']);
        $this->assertInternalType('string', $formattedRecord['context']['except']['trace']);
        $this->assertEquals('Exception', $formattedRecord['context']['except']['class']);
    }

    public function testFormatDepthArray()
    {
        $record = array(
            'message' => 'some log message',
            'context' => array(
                'nest2' => array(
                    'property' => 'anything',
                    'nest3' => array(
                        'nest4' => 'value',
                        'property' => 'nothing',
                    ),
                ),
            ),
            'level' => Logger::WARNING,
            'level_name' => Logger::getLevelName(Logger::WARNING),
            'channel' => 'test',
            'datetime' => new \DateTime('2014-02-01 00:00:00'),
            'extra' => array(),
        );

        $formatter = new MongoDBFormatter(2);
        $formattedResult = $formatter->format($record);

        $this->assertEquals(
            array(
                'nest2' => array(
                    'property' => 'anything',
                    'nest3' => '[...]',
                ),
            ),
            $formattedResult['context']
        );
    }

    public function testFormatDepthArrayInfiniteNesting()
    {
        $record = array(
            'message' => 'some log message',
            'context' => array(
                'nest2' => array(
                    'property' => 'something',
                    'nest3' => array(
                        'property' => 'anything',
                        'nest4' => array(
                            'property' => 'nothing',
                        ),
                    ),
                ),
            ),
            'level' => Logger::WARNING,
            'level_name' => Logger::getLevelName(Logger::WARNING),
            'channel' => 'test',
            'datetime' => new \DateTime('2014-02-01 00:00:00'),
            'extra' => array(),
        );

        $formatter = new MongoDBFormatter(0);
        $formattedResult = $formatter->format($record);

        $this->assertEquals(
            array(
                'nest2' => array(
                    'property' => 'something',
                    'nest3' => array(
                        'property' => 'anything',
                        'nest4' => array(
                            'property' => 'nothing',
                        ),
                    ),
                ),
            ),
            $formattedResult['context']
        );
    }

    public function testFormatDepthObjects()
    {
        $someObject = new \stdClass();
        $someObject->property = 'anything';
        $someObject->nest3 = new \stdClass();
        $someObject->nest3->property = 'nothing';
        $someObject->nest3->nest4 = 'invisible';

        $record = array(
            'message' => 'some log message',
            'context' => array(
                'nest2' => $someObject,
            ),
            'level' => Logger::WARNING,
            'level_name' => Logger::getLevelName(Logger::WARNING),
            'channel' => 'test',
            'datetime' => new \DateTime('2014-02-01 00:00:00'),
            'extra' => array(),
        );

        $formatter = new MongoDBFormatter(2, true);
        $formattedResult = $formatter->format($record);

        $this->assertEquals(
            array(
                'nest2' => array(
                    'property' => 'anything',
                    'nest3' => '[...]',
                    'class' => 'stdClass',
                ),
            ),
            $formattedResult['context']
        );
    }

    public function testFormatDepthException()
    {
        $record = array(
            'message' => 'some log message',
            'context' => array(
                'nest2' => new \Exception('exception message', 987),
            ),
            'level' => Logger::WARNING,
            'level_name' => Logger::getLevelName(Logger::WARNING),
            'channel' => 'test',
            'datetime' => new \DateTime('2014-02-01 00:00:00'),
            'extra' => array(),
        );

        $formatter = new MongoDBFormatter(2, false);
        $formattedRecord = $formatter->format($record);

        $this->assertEquals('exception message', $formattedRecord['context']['nest2']['message']);
        $this->assertEquals(987, $formattedRecord['context']['nest2']['code']);
        $this->assertEquals('[...]', $formattedRecord['context']['nest2']['trace']);
    }
}
vendor/monolog/monolog/tests/Monolog/Formatter/ElasticaFormatterTest.php000064400000004502151327705700022676 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog\Formatter;

use WPvividMonolog\Logger;

class ElasticaFormatterTest extends \PHPUnit_Framework_TestCase
{
    public function setUp()
    {
        if (!class_exists("Elastica\Document")) {
            $this->markTestSkipped("ruflin/elastica not installed");
        }
    }

    /**
     * @covers Monolog\Formatter\ElasticaFormatter::__construct
     * @covers Monolog\Formatter\ElasticaFormatter::format
     * @covers Monolog\Formatter\ElasticaFormatter::getDocument
     */
    public function testFormat()
    {
        // test log message
        $msg = array(
            'level' => Logger::ERROR,
            'level_name' => 'ERROR',
            'channel' => 'meh',
            'context' => array('foo' => 7, 'bar', 'class' => new \stdClass),
            'datetime' => new \DateTime("@0"),
            'extra' => array(),
            'message' => 'log',
        );

        // expected values
        $expected = $msg;
        $expected['datetime'] = '1970-01-01T00:00:00.000000+00:00';
        $expected['context'] = array(
            'class' => '[object] (stdClass: {})',
            'foo' => 7,
            0 => 'bar',
        );

        // format log message
        $formatter = new ElasticaFormatter('my_index', 'doc_type');
        $doc = $formatter->format($msg);
        $this->assertInstanceOf('Elastica\Document', $doc);

        // Document parameters
        $params = $doc->getParams();
        $this->assertEquals('my_index', $params['_index']);
        $this->assertEquals('doc_type', $params['_type']);

        // Document data values
        $data = $doc->getData();
        foreach (array_keys($expected) as $key) {
            $this->assertEquals($expected[$key], $data[$key]);
        }
    }

    /**
     * @covers Monolog\Formatter\ElasticaFormatter::getIndex
     * @covers Monolog\Formatter\ElasticaFormatter::getType
     */
    public function testGetters()
    {
        $formatter = new ElasticaFormatter('my_index', 'doc_type');
        $this->assertEquals('my_index', $formatter->getIndex());
        $this->assertEquals('doc_type', $formatter->getType());
    }
}
vendor/monolog/monolog/tests/Monolog/Formatter/ChromePHPFormatterTest.php000064400000010252151327705700022735 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog\Formatter;

use WPvividMonolog\Logger;

class ChromePHPFormatterTest extends \PHPUnit_Framework_TestCase
{
    /**
     * @covers Monolog\Formatter\ChromePHPFormatter::format
     */
    public function testDefaultFormat()
    {
        $formatter = new ChromePHPFormatter();
        $record = array(
            'level' => Logger::ERROR,
            'level_name' => 'ERROR',
            'channel' => 'meh',
            'context' => array('from' => 'logger'),
            'datetime' => new \DateTime("@0"),
            'extra' => array('ip' => '127.0.0.1'),
            'message' => 'log',
        );

        $message = $formatter->format($record);

        $this->assertEquals(
            array(
                'meh',
                array(
                    'message' => 'log',
                    'context' => array('from' => 'logger'),
                    'extra' => array('ip' => '127.0.0.1'),
                ),
                'unknown',
                'error',
            ),
            $message
        );
    }

    /**
     * @covers Monolog\Formatter\ChromePHPFormatter::format
     */
    public function testFormatWithFileAndLine()
    {
        $formatter = new ChromePHPFormatter();
        $record = array(
            'level' => Logger::CRITICAL,
            'level_name' => 'CRITICAL',
            'channel' => 'meh',
            'context' => array('from' => 'logger'),
            'datetime' => new \DateTime("@0"),
            'extra' => array('ip' => '127.0.0.1', 'file' => 'test', 'line' => 14),
            'message' => 'log',
        );

        $message = $formatter->format($record);

        $this->assertEquals(
            array(
                'meh',
                array(
                    'message' => 'log',
                    'context' => array('from' => 'logger'),
                    'extra' => array('ip' => '127.0.0.1'),
                ),
                'test : 14',
                'error',
            ),
            $message
        );
    }

    /**
     * @covers Monolog\Formatter\ChromePHPFormatter::format
     */
    public function testFormatWithoutContext()
    {
        $formatter = new ChromePHPFormatter();
        $record = array(
            'level' => Logger::DEBUG,
            'level_name' => 'DEBUG',
            'channel' => 'meh',
            'context' => array(),
            'datetime' => new \DateTime("@0"),
            'extra' => array(),
            'message' => 'log',
        );

        $message = $formatter->format($record);

        $this->assertEquals(
            array(
                'meh',
                'log',
                'unknown',
                'log',
            ),
            $message
        );
    }

    /**
     * @covers Monolog\Formatter\ChromePHPFormatter::formatBatch
     */
    public function testBatchFormatThrowException()
    {
        $formatter = new ChromePHPFormatter();
        $records = array(
            array(
                'level' => Logger::INFO,
                'level_name' => 'INFO',
                'channel' => 'meh',
                'context' => array(),
                'datetime' => new \DateTime("@0"),
                'extra' => array(),
                'message' => 'log',
            ),
            array(
                'level' => Logger::WARNING,
                'level_name' => 'WARNING',
                'channel' => 'foo',
                'context' => array(),
                'datetime' => new \DateTime("@0"),
                'extra' => array(),
                'message' => 'log2',
            ),
        );

        $this->assertEquals(
            array(
                array(
                    'meh',
                    'log',
                    'unknown',
                    'info',
                ),
                array(
                    'foo',
                    'log2',
                    'unknown',
                    'warn',
                ),
            ),
            $formatter->formatBatch($records)
        );
    }
}
vendor/monolog/monolog/tests/Monolog/PsrLogCompatTest.php000064400000002272151327705700017676 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog;

use WPvividMonolog\Handler\TestHandler;
use WPvividMonolog\Formatter\LineFormatter;
use WPvividMonolog\Processor\PsrLogMessageProcessor;
use WPvividPsr\Log\Test\LoggerInterfaceTest;

class PsrLogCompatTest extends LoggerInterfaceTest
{
    private $handler;

    public function getLogger()
    {
        $logger = new Logger('foo');
        $logger->pushHandler($handler = new TestHandler);
        $logger->pushProcessor(new PsrLogMessageProcessor);
        $handler->setFormatter(new LineFormatter('%level_name% %message%'));

        $this->handler = $handler;

        return $logger;
    }

    public function getLogs()
    {
        $convert = function ($record) {
            $lower = function ($match) {
                return strtolower($match[0]);
            };

            return preg_replace_callback('{^[A-Z]+}', $lower, $record['formatted']);
        };

        return array_map($convert, $this->handler->getRecords());
    }
}
vendor/monolog/monolog/tests/Monolog/ErrorHandlerTest.php000064400000001664151327705700017717 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog;

use WPvividMonolog\Handler\TestHandler;

class ErrorHandlerTest extends \PHPUnit_Framework_TestCase
{
    public function testHandleError()
    {
        $logger = new Logger('test', array($handler = new TestHandler));
        $errHandler = new ErrorHandler($logger);

        $errHandler->registerErrorHandler(array(E_USER_NOTICE => Logger::EMERGENCY), false);
        trigger_error('Foo', E_USER_ERROR);
        $this->assertCount(1, $handler->getRecords());
        $this->assertTrue($handler->hasErrorRecords());
        trigger_error('Foo', E_USER_NOTICE);
        $this->assertCount(2, $handler->getRecords());
        $this->assertTrue($handler->hasEmergencyRecords());
    }
}
vendor/monolog/monolog/tests/Monolog/Handler/MockRavenClient.php000064400000001104151327705700021056 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog\Handler;

use Raven_Client;

class MockRavenClient extends Raven_Client
{
    public function capture($data, $stack, $vars = null)
    {
        $data = array_merge($this->get_user_data(), $data);
        $this->lastData = $data;
        $this->lastStack = $stack;
    }

    public $lastData;
    public $lastStack;
}
vendor/monolog/monolog/tests/Monolog/Handler/HipChatHandlerTest.php000064400000024513151327705700021521 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog\Handler;

use WPvividMonolog\TestCase;
use WPvividMonolog\Logger;

/**
 * @author Rafael Dohms <rafael@doh.ms>
 * @see    https://www.hipchat.com/docs/api
 */
class HipChatHandlerTest extends TestCase
{
    private $res;
    /** @var  HipChatHandler */
    private $handler;

    public function testWriteHeader()
    {
        $this->createHandler();
        $this->handler->handle($this->getRecord(Logger::CRITICAL, 'test1'));
        fseek($this->res, 0);
        $content = fread($this->res, 1024);

        $this->assertRegexp('/POST \/v1\/rooms\/message\?format=json&auth_token=.* HTTP\/1.1\\r\\nHost: api.hipchat.com\\r\\nContent-Type: application\/x-www-form-urlencoded\\r\\nContent-Length: \d{2,4}\\r\\n\\r\\n/', $content);

        return $content;
    }

    public function testWriteCustomHostHeader()
    {
        $this->createHandler('myToken', 'room1', 'WPvividMonolog', true, 'hipchat.foo.bar');
        $this->handler->handle($this->getRecord(Logger::CRITICAL, 'test1'));
        fseek($this->res, 0);
        $content = fread($this->res, 1024);

        $this->assertRegexp('/POST \/v1\/rooms\/message\?format=json&auth_token=.* HTTP\/1.1\\r\\nHost: hipchat.foo.bar\\r\\nContent-Type: application\/x-www-form-urlencoded\\r\\nContent-Length: \d{2,4}\\r\\n\\r\\n/', $content);

        return $content;
    }

    public function testWriteV2()
    {
        $this->createHandler('myToken', 'room1', 'WPvividMonolog', false, 'hipchat.foo.bar', 'v2');
        $this->handler->handle($this->getRecord(Logger::CRITICAL, 'test1'));
        fseek($this->res, 0);
        $content = fread($this->res, 1024);

        $this->assertRegexp('/POST \/v2\/room\/room1\/notification\?auth_token=.* HTTP\/1.1\\r\\nHost: hipchat.foo.bar\\r\\nContent-Type: application\/x-www-form-urlencoded\\r\\nContent-Length: \d{2,4}\\r\\n\\r\\n/', $content);

        return $content;
    }

    public function testWriteV2Notify()
    {
        $this->createHandler('myToken', 'room1', 'WPvividMonolog', true, 'hipchat.foo.bar', 'v2');
        $this->handler->handle($this->getRecord(Logger::CRITICAL, 'test1'));
        fseek($this->res, 0);
        $content = fread($this->res, 1024);

        $this->assertRegexp('/POST \/v2\/room\/room1\/notification\?auth_token=.* HTTP\/1.1\\r\\nHost: hipchat.foo.bar\\r\\nContent-Type: application\/x-www-form-urlencoded\\r\\nContent-Length: \d{2,4}\\r\\n\\r\\n/', $content);

        return $content;
    }

    public function testRoomSpaces()
    {
        $this->createHandler('myToken', 'room name', 'WPvividMonolog', false, 'hipchat.foo.bar', 'v2');
        $this->handler->handle($this->getRecord(Logger::CRITICAL, 'test1'));
        fseek($this->res, 0);
        $content = fread($this->res, 1024);

        $this->assertRegexp('/POST \/v2\/room\/room%20name\/notification\?auth_token=.* HTTP\/1.1\\r\\nHost: hipchat.foo.bar\\r\\nContent-Type: application\/x-www-form-urlencoded\\r\\nContent-Length: \d{2,4}\\r\\n\\r\\n/', $content);

        return $content;
    }

    /**
     * @depends testWriteHeader
     */
    public function testWriteContent($content)
    {
        $this->assertRegexp('/notify=0&message=test1&message_format=text&color=red&room_id=room1&from=WPvividMonolog$/', $content);
    }

    public function testWriteContentV1WithoutName()
    {
        $this->createHandler('myToken', 'room1', null, false, 'hipchat.foo.bar', 'v1');
        $this->handler->handle($this->getRecord(Logger::CRITICAL, 'test1'));
        fseek($this->res, 0);
        $content = fread($this->res, 1024);

        $this->assertRegexp('/notify=0&message=test1&message_format=text&color=red&room_id=room1&from=$/', $content);

        return $content;
    }

    /**
     * @depends testWriteCustomHostHeader
     */
    public function testWriteContentNotify($content)
    {
        $this->assertRegexp('/notify=1&message=test1&message_format=text&color=red&room_id=room1&from=WPvividMonolog$/', $content);
    }

    /**
     * @depends testWriteV2
     */
    public function testWriteContentV2($content)
    {
        $this->assertRegexp('/notify=false&message=test1&message_format=text&color=red&from=WPvividMonolog$/', $content);
    }

    /**
     * @depends testWriteV2Notify
     */
    public function testWriteContentV2Notify($content)
    {
        $this->assertRegexp('/notify=true&message=test1&message_format=text&color=red&from=WPvividMonolog$/', $content);
    }

    public function testWriteContentV2WithoutName()
    {
        $this->createHandler('myToken', 'room1', null, false, 'hipchat.foo.bar', 'v2');
        $this->handler->handle($this->getRecord(Logger::CRITICAL, 'test1'));
        fseek($this->res, 0);
        $content = fread($this->res, 1024);

        $this->assertRegexp('/notify=false&message=test1&message_format=text&color=red$/', $content);

        return $content;
    }

    public function testWriteWithComplexMessage()
    {
        $this->createHandler();
        $this->handler->handle($this->getRecord(Logger::CRITICAL, 'Backup of database "example" finished in 16 minutes.'));
        fseek($this->res, 0);
        $content = fread($this->res, 1024);

        $this->assertRegexp('/message=Backup\+of\+database\+%22example%22\+finished\+in\+16\+minutes\./', $content);
    }

    public function testWriteTruncatesLongMessage()
    {
        $this->createHandler();
        $this->handler->handle($this->getRecord(Logger::CRITICAL, str_repeat('abcde', 2000)));
        fseek($this->res, 0);
        $content = fread($this->res, 12000);

        $this->assertRegexp('/message='.str_repeat('abcde', 1900).'\+%5Btruncated%5D/', $content);
    }

    /**
     * @dataProvider provideLevelColors
     */
    public function testWriteWithErrorLevelsAndColors($level, $expectedColor)
    {
        $this->createHandler();
        $this->handler->handle($this->getRecord($level, 'Backup of database "example" finished in 16 minutes.'));
        fseek($this->res, 0);
        $content = fread($this->res, 1024);

        $this->assertRegexp('/color='.$expectedColor.'/', $content);
    }

    public function provideLevelColors()
    {
        return array(
            array(Logger::DEBUG,    'gray'),
            array(Logger::INFO,     'green'),
            array(Logger::WARNING,  'yellow'),
            array(Logger::ERROR,    'red'),
            array(Logger::CRITICAL, 'red'),
            array(Logger::ALERT,    'red'),
            array(Logger::EMERGENCY,'red'),
            array(Logger::NOTICE,   'green'),
        );
    }

    /**
     * @dataProvider provideBatchRecords
     */
    public function testHandleBatch($records, $expectedColor)
    {
        $this->createHandler();

        $this->handler->handleBatch($records);

        fseek($this->res, 0);
        $content = fread($this->res, 1024);

        $this->assertRegexp('/color='.$expectedColor.'/', $content);
    }

    public function provideBatchRecords()
    {
        return array(
            array(
                array(
                    array('level' => Logger::WARNING, 'message' => 'Oh bugger!', 'level_name' => 'warning', 'datetime' => new \DateTime()),
                    array('level' => Logger::NOTICE, 'message' => 'Something noticeable happened.', 'level_name' => 'notice', 'datetime' => new \DateTime()),
                    array('level' => Logger::CRITICAL, 'message' => 'Everything is broken!', 'level_name' => 'critical', 'datetime' => new \DateTime()),
                ),
                'red',
            ),
            array(
                array(
                    array('level' => Logger::WARNING, 'message' => 'Oh bugger!', 'level_name' => 'warning', 'datetime' => new \DateTime()),
                    array('level' => Logger::NOTICE, 'message' => 'Something noticeable happened.', 'level_name' => 'notice', 'datetime' => new \DateTime()),
                ),
                'yellow',
            ),
            array(
                array(
                    array('level' => Logger::DEBUG, 'message' => 'Just debugging.', 'level_name' => 'debug', 'datetime' => new \DateTime()),
                    array('level' => Logger::NOTICE, 'message' => 'Something noticeable happened.', 'level_name' => 'notice', 'datetime' => new \DateTime()),
                ),
                'green',
            ),
            array(
                array(
                    array('level' => Logger::DEBUG, 'message' => 'Just debugging.', 'level_name' => 'debug', 'datetime' => new \DateTime()),
                ),
                'gray',
            ),
        );
    }

    private function createHandler($token = 'myToken', $room = 'room1', $name = 'WPvividMonolog', $notify = false, $host = 'api.hipchat.com', $version = 'v1')
    {
        $constructorArgs = array($token, $room, $name, $notify, Logger::DEBUG, true, true, 'text', $host, $version);
        $this->res = fopen('php://memory', 'a');
        $this->handler = $this->getMock(
            '\WPvividMonolog\Handler\HipChatHandler',
            array('fsockopen', 'streamSetTimeout', 'closeSocket'),
            $constructorArgs
        );

        $reflectionProperty = new \ReflectionProperty('\WPvividMonolog\Handler\SocketHandler', 'connectionString');
        $reflectionProperty->setAccessible(true);
        $reflectionProperty->setValue($this->handler, 'localhost:1234');

        $this->handler->expects($this->any())
            ->method('fsockopen')
            ->will($this->returnValue($this->res));
        $this->handler->expects($this->any())
            ->method('streamSetTimeout')
            ->will($this->returnValue(true));
        $this->handler->expects($this->any())
            ->method('closeSocket')
            ->will($this->returnValue(true));

        $this->handler->setFormatter($this->getIdentityFormatter());
    }

    /**
     * @expectedException InvalidArgumentException
     */
    public function testCreateWithTooLongName()
    {
        $hipChatHandler = new HipChatHandler('token', 'room', 'SixteenCharsHere');
    }

    public function testCreateWithTooLongNameV2()
    {
        // creating a handler with too long of a name but using the v2 api doesn't matter.
        $hipChatHandler = new HipChatHandler('token', 'room', 'SixteenCharsHere', false, Logger::CRITICAL, true, true, 'test', 'api.hipchat.com', 'v2');
    }
}
vendor/monolog/monolog/tests/Monolog/Handler/MailHandlerTest.php000064400000004314151327705700021060 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog\Handler;

use WPvividMonolog\Logger;
use WPvividMonolog\TestCase;

class MailHandlerTest extends TestCase
{
    /**
     * @covers Monolog\Handler\MailHandler::handleBatch
     */
    public function testHandleBatch()
    {
        $formatter = $this->getMock('WPvividMonolog\\Formatter\\FormatterInterface');
        $formatter->expects($this->once())
            ->method('formatBatch'); // Each record is formatted

        $handler = $this->getMockForAbstractClass('WPvividMonolog\\Handler\\MailHandler');
        $handler->expects($this->once())
            ->method('send');
        $handler->expects($this->never())
            ->method('write'); // write is for individual records

        $handler->setFormatter($formatter);

        $handler->handleBatch($this->getMultipleRecords());
    }

    /**
     * @covers Monolog\Handler\MailHandler::handleBatch
     */
    public function testHandleBatchNotSendsMailIfMessagesAreBelowLevel()
    {
        $records = array(
            $this->getRecord(Logger::DEBUG, 'debug message 1'),
            $this->getRecord(Logger::DEBUG, 'debug message 2'),
            $this->getRecord(Logger::INFO, 'information'),
        );

        $handler = $this->getMockForAbstractClass('WPvividMonolog\\Handler\\MailHandler');
        $handler->expects($this->never())
            ->method('send');
        $handler->setLevel(Logger::ERROR);

        $handler->handleBatch($records);
    }

    /**
     * @covers Monolog\Handler\MailHandler::write
     */
    public function testHandle()
    {
        $handler = $this->getMockForAbstractClass('WPvividMonolog\\Handler\\MailHandler');

        $record = $this->getRecord();
        $records = array($record);
        $records[0]['formatted'] = '['.$record['datetime']->format('Y-m-d H:i:s').'] test.WARNING: test [] []'."\n";

        $handler->expects($this->once())
            ->method('send')
            ->with($records[0]['formatted'], $records);

        $handler->handle($record);
    }
}
vendor/monolog/monolog/tests/Monolog/Handler/GelfHandlerTest.php000064400000006536151327705700021063 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog\Handler;

use Gelf\Message;
use WPvividMonolog\TestCase;
use WPvividMonolog\Logger;
use WPvividMonolog\Formatter\GelfMessageFormatter;

class GelfHandlerTest extends TestCase
{
    public function setUp()
    {
        if (!class_exists('Gelf\Publisher') || !class_exists('Gelf\Message')) {
            $this->markTestSkipped("graylog2/gelf-php not installed");
        }
    }

    /**
     * @covers Monolog\Handler\GelfHandler::__construct
     */
    public function testConstruct()
    {
        $handler = new GelfHandler($this->getMessagePublisher());
        $this->assertInstanceOf('WPvividMonolog\Handler\GelfHandler', $handler);
    }

    protected function getHandler($messagePublisher)
    {
        $handler = new GelfHandler($messagePublisher);

        return $handler;
    }

    protected function getMessagePublisher()
    {
        return $this->getMock('Gelf\Publisher', array('publish'), array(), '', false);
    }

    public function testDebug()
    {
        $record = $this->getRecord(Logger::DEBUG, "A test debug message");
        $expectedMessage = new Message();
        $expectedMessage
            ->setLevel(7)
            ->setFacility("test")
            ->setShortMessage($record['message'])
            ->setTimestamp($record['datetime'])
        ;

        $messagePublisher = $this->getMessagePublisher();
        $messagePublisher->expects($this->once())
            ->method('publish')
            ->with($expectedMessage);

        $handler = $this->getHandler($messagePublisher);

        $handler->handle($record);
    }

    public function testWarning()
    {
        $record = $this->getRecord(Logger::WARNING, "A test warning message");
        $expectedMessage = new Message();
        $expectedMessage
            ->setLevel(4)
            ->setFacility("test")
            ->setShortMessage($record['message'])
            ->setTimestamp($record['datetime'])
        ;

        $messagePublisher = $this->getMessagePublisher();
        $messagePublisher->expects($this->once())
            ->method('publish')
            ->with($expectedMessage);

        $handler = $this->getHandler($messagePublisher);

        $handler->handle($record);
    }

    public function testInjectedGelfMessageFormatter()
    {
        $record = $this->getRecord(Logger::WARNING, "A test warning message");
        $record['extra']['blarg'] = 'yep';
        $record['context']['from'] = 'logger';

        $expectedMessage = new Message();
        $expectedMessage
            ->setLevel(4)
            ->setFacility("test")
            ->setHost("mysystem")
            ->setShortMessage($record['message'])
            ->setTimestamp($record['datetime'])
            ->setAdditional("EXTblarg", 'yep')
            ->setAdditional("CTXfrom", 'logger')
        ;

        $messagePublisher = $this->getMessagePublisher();
        $messagePublisher->expects($this->once())
            ->method('publish')
            ->with($expectedMessage);

        $handler = $this->getHandler($messagePublisher);
        $handler->setFormatter(new GelfMessageFormatter('mysystem', 'EXT', 'CTX'));
        $handler->handle($record);
    }
}
vendor/monolog/monolog/tests/Monolog/Handler/ZendMonitorHandlerTest.php000064400000004032151327705700022443 0ustar00<?php
/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog\Handler;

use WPvividMonolog\TestCase;

class ZendMonitorHandlerTest extends TestCase
{
    protected $zendMonitorHandler;

    public function setUp()
    {
        if (!function_exists('zend_monitor_custom_event')) {
            $this->markTestSkipped('ZendServer is not installed');
        }
    }

    /**
     * @covers  Monolog\Handler\ZendMonitorHandler::write
     */
    public function testWrite()
    {
        $record = $this->getRecord();
        $formatterResult = array(
            'message' => $record['message'],
        );

        $zendMonitor = $this->getMockBuilder('WPvividMonolog\Handler\ZendMonitorHandler')
            ->setMethods(array('writeZendMonitorCustomEvent', 'getDefaultFormatter'))
            ->getMock();

        $formatterMock = $this->getMockBuilder('WPvividMonolog\Formatter\NormalizerFormatter')
            ->disableOriginalConstructor()
            ->getMock();

        $formatterMock->expects($this->once())
            ->method('format')
            ->will($this->returnValue($formatterResult));

        $zendMonitor->expects($this->once())
            ->method('getDefaultFormatter')
            ->will($this->returnValue($formatterMock));

        $levelMap = $zendMonitor->getLevelMap();

        $zendMonitor->expects($this->once())
            ->method('writeZendMonitorCustomEvent')
            ->with($levelMap[$record['level']], $record['message'], $formatterResult);

        $zendMonitor->handle($record);
    }

    /**
     * @covers Monolog\Handler\ZendMonitorHandler::getDefaultFormatter
     */
    public function testGetDefaultFormatterReturnsNormalizerFormatter()
    {
        $zendMonitor = new ZendMonitorHandler();
        $this->assertInstanceOf('WPvividMonolog\Formatter\NormalizerFormatter', $zendMonitor->getDefaultFormatter());
    }
}
vendor/monolog/monolog/tests/Monolog/Handler/FlowdockHandlerTest.php000064400000005115151327705700021746 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog\Handler;

use WPvividMonolog\Formatter\FlowdockFormatter;
use WPvividMonolog\TestCase;
use WPvividMonolog\Logger;

/**
 * @author Dominik Liebler <liebler.dominik@gmail.com>
 * @see    https://www.hipchat.com/docs/api
 */
class FlowdockHandlerTest extends TestCase
{
    /**
     * @var resource
     */
    private $res;

    /**
     * @var FlowdockHandler
     */
    private $handler;

    public function setUp()
    {
        if (!extension_loaded('openssl')) {
            $this->markTestSkipped('This test requires openssl to run');
        }
    }

    public function testWriteHeader()
    {
        $this->createHandler();
        $this->handler->handle($this->getRecord(Logger::CRITICAL, 'test1'));
        fseek($this->res, 0);
        $content = fread($this->res, 1024);

        $this->assertRegexp('/POST \/v1\/messages\/team_inbox\/.* HTTP\/1.1\\r\\nHost: api.flowdock.com\\r\\nContent-Type: application\/json\\r\\nContent-Length: \d{2,4}\\r\\n\\r\\n/', $content);

        return $content;
    }

    /**
     * @depends testWriteHeader
     */
    public function testWriteContent($content)
    {
        $this->assertRegexp('/"source":"test_source"/', $content);
        $this->assertRegexp('/"from_address":"source@test\.com"/', $content);
    }

    private function createHandler($token = 'myToken')
    {
        $constructorArgs = array($token, Logger::DEBUG);
        $this->res = fopen('php://memory', 'a');
        $this->handler = $this->getMock(
            '\WPvividMonolog\Handler\FlowdockHandler',
            array('fsockopen', 'streamSetTimeout', 'closeSocket'),
            $constructorArgs
        );

        $reflectionProperty = new \ReflectionProperty('\WPvividMonolog\Handler\SocketHandler', 'connectionString');
        $reflectionProperty->setAccessible(true);
        $reflectionProperty->setValue($this->handler, 'localhost:1234');

        $this->handler->expects($this->any())
            ->method('fsockopen')
            ->will($this->returnValue($this->res));
        $this->handler->expects($this->any())
            ->method('streamSetTimeout')
            ->will($this->returnValue(true));
        $this->handler->expects($this->any())
            ->method('closeSocket')
            ->will($this->returnValue(true));

        $this->handler->setFormatter(new FlowdockFormatter('test_source', 'source@test.com'));
    }
}
vendor/monolog/monolog/tests/Monolog/Handler/AbstractHandlerTest.php000064400000010124151327705700021735 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog\Handler;

use WPvividMonolog\TestCase;
use WPvividMonolog\Logger;
use WPvividMonolog\Formatter\LineFormatter;
use WPvividMonolog\Processor\WebProcessor;

class AbstractHandlerTest extends TestCase
{
    /**
     * @covers Monolog\Handler\AbstractHandler::__construct
     * @covers Monolog\Handler\AbstractHandler::getLevel
     * @covers Monolog\Handler\AbstractHandler::setLevel
     * @covers Monolog\Handler\AbstractHandler::getBubble
     * @covers Monolog\Handler\AbstractHandler::setBubble
     * @covers Monolog\Handler\AbstractHandler::getFormatter
     * @covers Monolog\Handler\AbstractHandler::setFormatter
     */
    public function testConstructAndGetSet()
    {
        $handler = $this->getMockForAbstractClass('WPvividMonolog\Handler\AbstractHandler', array(Logger::WARNING, false));
        $this->assertEquals(Logger::WARNING, $handler->getLevel());
        $this->assertEquals(false, $handler->getBubble());

        $handler->setLevel(Logger::ERROR);
        $handler->setBubble(true);
        $handler->setFormatter($formatter = new LineFormatter);
        $this->assertEquals(Logger::ERROR, $handler->getLevel());
        $this->assertEquals(true, $handler->getBubble());
        $this->assertSame($formatter, $handler->getFormatter());
    }

    /**
     * @covers Monolog\Handler\AbstractHandler::handleBatch
     */
    public function testHandleBatch()
    {
        $handler = $this->getMockForAbstractClass('WPvividMonolog\Handler\AbstractHandler');
        $handler->expects($this->exactly(2))
            ->method('handle');
        $handler->handleBatch(array($this->getRecord(), $this->getRecord()));
    }

    /**
     * @covers Monolog\Handler\AbstractHandler::isHandling
     */
    public function testIsHandling()
    {
        $handler = $this->getMockForAbstractClass('WPvividMonolog\Handler\AbstractHandler', array(Logger::WARNING, false));
        $this->assertTrue($handler->isHandling($this->getRecord()));
        $this->assertFalse($handler->isHandling($this->getRecord(Logger::DEBUG)));
    }

    /**
     * @covers Monolog\Handler\AbstractHandler::__construct
     */
    public function testHandlesPsrStyleLevels()
    {
        $handler = $this->getMockForAbstractClass('WPvividMonolog\Handler\AbstractHandler', array('warning', false));
        $this->assertFalse($handler->isHandling($this->getRecord(Logger::DEBUG)));
        $handler->setLevel('debug');
        $this->assertTrue($handler->isHandling($this->getRecord(Logger::DEBUG)));
    }

    /**
     * @covers Monolog\Handler\AbstractHandler::getFormatter
     * @covers Monolog\Handler\AbstractHandler::getDefaultFormatter
     */
    public function testGetFormatterInitializesDefault()
    {
        $handler = $this->getMockForAbstractClass('WPvividMonolog\Handler\AbstractHandler');
        $this->assertInstanceOf('WPvividMonolog\Formatter\LineFormatter', $handler->getFormatter());
    }

    /**
     * @covers Monolog\Handler\AbstractHandler::pushProcessor
     * @covers Monolog\Handler\AbstractHandler::popProcessor
     * @expectedException LogicException
     */
    public function testPushPopProcessor()
    {
        $logger = $this->getMockForAbstractClass('WPvividMonolog\Handler\AbstractHandler');
        $processor1 = new WebProcessor;
        $processor2 = new WebProcessor;

        $logger->pushProcessor($processor1);
        $logger->pushProcessor($processor2);

        $this->assertEquals($processor2, $logger->popProcessor());
        $this->assertEquals($processor1, $logger->popProcessor());
        $logger->popProcessor();
    }

    /**
     * @covers Monolog\Handler\AbstractHandler::pushProcessor
     * @expectedException InvalidArgumentException
     */
    public function testPushProcessorWithNonCallable()
    {
        $handler = $this->getMockForAbstractClass('WPvividMonolog\Handler\AbstractHandler');

        $handler->pushProcessor(new \stdClass());
    }
}
vendor/monolog/monolog/tests/Monolog/Handler/PsrHandlerTest.php000064400000002441151327705700020741 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog\Handler;

use WPvividMonolog\TestCase;
use WPvividMonolog\Logger;

/**
 * @covers Monolog\Handler\PsrHandler::handle
 */
class PsrHandlerTest extends TestCase
{
    public function logLevelProvider()
    {
        $levels = array();
        $monologLogger = new Logger('');

        foreach ($monologLogger->getLevels() as $levelName => $level) {
            $levels[] = array($levelName, $level);
        }

        return $levels;
    }

    /**
     * @dataProvider logLevelProvider
     */
    public function testHandlesAllLevels($levelName, $level)
    {
        $message = 'Hello, world! ' . $level;
        $context = array('foo' => 'bar', 'level' => $level);

        $psrLogger = $this->getMock('WPvividPsr\Log\NullLogger');
        $psrLogger->expects($this->once())
            ->method('log')
            ->with(strtolower($levelName), $message, $context);

        $handler = new PsrHandler($psrLogger);
        $handler->handle(array('level' => $level, 'level_name' => $levelName, 'message' => $message, 'context' => $context));
    }
}
vendor/monolog/monolog/tests/Monolog/Handler/RedisHandlerTest.php000064400000007574151327705700021257 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog\Handler;

use WPvividMonolog\TestCase;
use WPvividMonolog\Logger;
use WPvividMonolog\Formatter\LineFormatter;

class RedisHandlerTest extends TestCase
{
    /**
     * @expectedException InvalidArgumentException
     */
    public function testConstructorShouldThrowExceptionForInvalidRedis()
    {
        new RedisHandler(new \stdClass(), 'key');
    }

    public function testConstructorShouldWorkWithPredis()
    {
        $redis = $this->getMock('Predis\Client');
        $this->assertInstanceof('WPvividMonolog\Handler\RedisHandler', new RedisHandler($redis, 'key'));
    }

    public function testConstructorShouldWorkWithRedis()
    {
        $redis = $this->getMock('Redis');
        $this->assertInstanceof('WPvividMonolog\Handler\RedisHandler', new RedisHandler($redis, 'key'));
    }

    public function testPredisHandle()
    {
        $redis = $this->getMock('Predis\Client', array('rpush'));

        // Predis\Client uses rpush
        $redis->expects($this->once())
            ->method('rpush')
            ->with('key', 'test');

        $record = $this->getRecord(Logger::WARNING, 'test', array('data' => new \stdClass, 'foo' => 34));

        $handler = new RedisHandler($redis, 'key');
        $handler->setFormatter(new LineFormatter("%message%"));
        $handler->handle($record);
    }

    public function testRedisHandle()
    {
        $redis = $this->getMock('Redis', array('rpush'));

        // Redis uses rPush
        $redis->expects($this->once())
            ->method('rPush')
            ->with('key', 'test');

        $record = $this->getRecord(Logger::WARNING, 'test', array('data' => new \stdClass, 'foo' => 34));

        $handler = new RedisHandler($redis, 'key');
        $handler->setFormatter(new LineFormatter("%message%"));
        $handler->handle($record);
    }

    public function testRedisHandleCapped()
    {
        $redis = $this->getMock('Redis', array('multi', 'rpush', 'ltrim', 'exec'));

        // Redis uses multi
        $redis->expects($this->once())
            ->method('multi')
            ->will($this->returnSelf());

        $redis->expects($this->once())
            ->method('rpush')
            ->will($this->returnSelf());

        $redis->expects($this->once())
            ->method('ltrim')
            ->will($this->returnSelf());

        $redis->expects($this->once())
            ->method('exec')
            ->will($this->returnSelf());

        $record = $this->getRecord(Logger::WARNING, 'test', array('data' => new \stdClass, 'foo' => 34));

        $handler = new RedisHandler($redis, 'key', Logger::DEBUG, true, 10);
        $handler->setFormatter(new LineFormatter("%message%"));
        $handler->handle($record);
    }

    public function testPredisHandleCapped()
    {
        $redis = $this->getMock('Predis\Client', array('transaction'));

        $redisTransaction = $this->getMock('Predis\Client', array('rpush', 'ltrim'));

        $redisTransaction->expects($this->once())
            ->method('rpush')
            ->will($this->returnSelf());

        $redisTransaction->expects($this->once())
            ->method('ltrim')
            ->will($this->returnSelf());

        // Redis uses multi
        $redis->expects($this->once())
            ->method('transaction')
            ->will($this->returnCallback(function ($cb) use ($redisTransaction) {
                $cb($redisTransaction);
            }));

        $record = $this->getRecord(Logger::WARNING, 'test', array('data' => new \stdClass, 'foo' => 34));

        $handler = new RedisHandler($redis, 'key', Logger::DEBUG, true, 10);
        $handler->setFormatter(new LineFormatter("%message%"));
        $handler->handle($record);
    }
}
vendor/monolog/monolog/tests/Monolog/Handler/NativeMailerHandlerTest.php000064400000007255151327705700022565 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog\Handler;

use WPvividMonolog\TestCase;
use WPvividMonolog\Logger;
use InvalidArgumentException;

function mail($to, $subject, $message, $additional_headers = null, $additional_parameters = null)
{
    $GLOBALS['mail'][] = func_get_args();
}

class NativeMailerHandlerTest extends TestCase
{
    protected function setUp()
    {
        $GLOBALS['mail'] = array();
    }

    /**
     * @expectedException InvalidArgumentException
     */
    public function testConstructorHeaderInjection()
    {
        $mailer = new NativeMailerHandler('spammer@example.org', 'dear victim', "receiver@example.org\r\nFrom: faked@attacker.org");
    }

    /**
     * @expectedException InvalidArgumentException
     */
    public function testSetterHeaderInjection()
    {
        $mailer = new NativeMailerHandler('spammer@example.org', 'dear victim', 'receiver@example.org');
        $mailer->addHeader("Content-Type: text/html\r\nFrom: faked@attacker.org");
    }

    /**
     * @expectedException InvalidArgumentException
     */
    public function testSetterArrayHeaderInjection()
    {
        $mailer = new NativeMailerHandler('spammer@example.org', 'dear victim', 'receiver@example.org');
        $mailer->addHeader(array("Content-Type: text/html\r\nFrom: faked@attacker.org"));
    }

    /**
     * @expectedException InvalidArgumentException
     */
    public function testSetterContentTypeInjection()
    {
        $mailer = new NativeMailerHandler('spammer@example.org', 'dear victim', 'receiver@example.org');
        $mailer->setContentType("text/html\r\nFrom: faked@attacker.org");
    }

    /**
     * @expectedException InvalidArgumentException
     */
    public function testSetterEncodingInjection()
    {
        $mailer = new NativeMailerHandler('spammer@example.org', 'dear victim', 'receiver@example.org');
        $mailer->setEncoding("utf-8\r\nFrom: faked@attacker.org");
    }

    public function testSend()
    {
        $to = 'spammer@example.org';
        $subject = 'dear victim';
        $from = 'receiver@example.org';

        $mailer = new NativeMailerHandler($to, $subject, $from);
        $mailer->handleBatch(array());

        // batch is empty, nothing sent
        $this->assertEmpty($GLOBALS['mail']);

        // non-empty batch
        $mailer->handle($this->getRecord(Logger::ERROR, "Foo\nBar\r\n\r\nBaz"));
        $this->assertNotEmpty($GLOBALS['mail']);
        $this->assertInternalType('array', $GLOBALS['mail']);
        $this->assertArrayHasKey('0', $GLOBALS['mail']);
        $params = $GLOBALS['mail'][0];
        $this->assertCount(5, $params);
        $this->assertSame($to, $params[0]);
        $this->assertSame($subject, $params[1]);
        $this->assertStringEndsWith(" test.ERROR: Foo Bar  Baz [] []\n", $params[2]);
        $this->assertSame("From: $from\r\nContent-type: text/plain; charset=utf-8\r\n", $params[3]);
        $this->assertSame('', $params[4]);
    }

    public function testMessageSubjectFormatting()
    {
        $mailer = new NativeMailerHandler('to@example.org', 'Alert: %level_name% %message%', 'from@example.org');
        $mailer->handle($this->getRecord(Logger::ERROR, "Foo\nBar\r\n\r\nBaz"));
        $this->assertNotEmpty($GLOBALS['mail']);
        $this->assertInternalType('array', $GLOBALS['mail']);
        $this->assertArrayHasKey('0', $GLOBALS['mail']);
        $params = $GLOBALS['mail'][0];
        $this->assertCount(5, $params);
        $this->assertSame('Alert: ERROR Foo Bar  Baz', $params[1]);
    }
}
vendor/monolog/monolog/tests/Monolog/Handler/DoctrineCouchDBHandlerTest.php000064400000002733151327705700023140 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog\Handler;

use WPvividMonolog\TestCase;
use WPvividMonolog\Logger;

class DoctrineCouchDBHandlerTest extends TestCase
{
    protected function setup()
    {
        if (!class_exists('Doctrine\CouchDB\CouchDBClient')) {
            $this->markTestSkipped('The "doctrine/couchdb" package is not installed');
        }
    }

    public function testHandle()
    {
        $client = $this->getMockBuilder('Doctrine\\CouchDB\\CouchDBClient')
            ->setMethods(array('postDocument'))
            ->disableOriginalConstructor()
            ->getMock();

        $record = $this->getRecord(Logger::WARNING, 'test', array('data' => new \stdClass, 'foo' => 34));

        $expected = array(
            'message' => 'test',
            'context' => array('data' => '[object] (stdClass: {})', 'foo' => 34),
            'level' => Logger::WARNING,
            'level_name' => 'WARNING',
            'channel' => 'test',
            'datetime' => $record['datetime']->format('Y-m-d H:i:s'),
            'extra' => array(),
        );

        $client->expects($this->once())
            ->method('postDocument')
            ->with($expected);

        $handler = new DoctrineCouchDBHandler($client);
        $handler->handle($record);
    }
}
vendor/monolog/monolog/tests/Monolog/Handler/RotatingFileHandlerTest.php000064400000021731151327705700022567 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog\Handler;

use WPvividMonolog\TestCase;
use PHPUnit_Framework_Error_Deprecated;

/**
 * @covers Monolog\Handler\RotatingFileHandler
 */
class RotatingFileHandlerTest extends TestCase
{
    /**
     * This var should be private but then the anonymous function
     * in the `setUp` method won't be able to set it. `$this` cant't
     * be used in the anonymous function in `setUp` because PHP 5.3
     * does not support it.
     */
    public $lastError;

    public function setUp()
    {
        $dir = __DIR__.'/Fixtures';
        chmod($dir, 0777);
        if (!is_writable($dir)) {
            $this->markTestSkipped($dir.' must be writable to test the RotatingFileHandler.');
        }
        $this->lastError = null;
        $self = $this;
        // workaround with &$self used for PHP 5.3
        set_error_handler(function($code, $message) use (&$self) {
            $self->lastError = array(
                'code' => $code,
                'message' => $message,
            );
        });
    }

    private function assertErrorWasTriggered($code, $message)
    {
        if (empty($this->lastError)) {
            $this->fail(
                sprintf(
                    'Failed asserting that error with code `%d` and message `%s` was triggered',
                    $code,
                    $message
                )
            );
        }
        $this->assertEquals($code, $this->lastError['code'], sprintf('Expected an error with code %d to be triggered, got `%s` instead', $code, $this->lastError['code']));
        $this->assertEquals($message, $this->lastError['message'], sprintf('Expected an error with message `%d` to be triggered, got `%s` instead', $message, $this->lastError['message']));
    }

    public function testRotationCreatesNewFile()
    {
        touch(__DIR__.'/Fixtures/foo-'.date('Y-m-d', time() - 86400).'.rot');

        $handler = new RotatingFileHandler(__DIR__.'/Fixtures/foo.rot');
        $handler->setFormatter($this->getIdentityFormatter());
        $handler->handle($this->getRecord());

        $log = __DIR__.'/Fixtures/foo-'.date('Y-m-d').'.rot';
        $this->assertTrue(file_exists($log));
        $this->assertEquals('test', file_get_contents($log));
    }

    /**
     * @dataProvider rotationTests
     */
    public function testRotation($createFile, $dateFormat, $timeCallback)
    {
        touch($old1 = __DIR__.'/Fixtures/foo-'.date($dateFormat, $timeCallback(-1)).'.rot');
        touch($old2 = __DIR__.'/Fixtures/foo-'.date($dateFormat, $timeCallback(-2)).'.rot');
        touch($old3 = __DIR__.'/Fixtures/foo-'.date($dateFormat, $timeCallback(-3)).'.rot');
        touch($old4 = __DIR__.'/Fixtures/foo-'.date($dateFormat, $timeCallback(-4)).'.rot');

        $log = __DIR__.'/Fixtures/foo-'.date($dateFormat).'.rot';

        if ($createFile) {
            touch($log);
        }

        $handler = new RotatingFileHandler(__DIR__.'/Fixtures/foo.rot', 2);
        $handler->setFormatter($this->getIdentityFormatter());
        $handler->setFilenameFormat('{filename}-{date}', $dateFormat);
        $handler->handle($this->getRecord());

        $handler->close();

        $this->assertTrue(file_exists($log));
        $this->assertTrue(file_exists($old1));
        $this->assertEquals($createFile, file_exists($old2));
        $this->assertEquals($createFile, file_exists($old3));
        $this->assertEquals($createFile, file_exists($old4));
        $this->assertEquals('test', file_get_contents($log));
    }

    public function rotationTests()
    {
        $now = time();
        $dayCallback = function($ago) use ($now) {
            return $now + 86400 * $ago;
        };
        $monthCallback = function($ago) {
            return gmmktime(0, 0, 0, date('n') + $ago, 1, date('Y'));
        };
        $yearCallback = function($ago) {
            return gmmktime(0, 0, 0, 1, 1, date('Y') + $ago);
        };

        return array(
            'Rotation is triggered when the file of the current day is not present'
                => array(true, RotatingFileHandler::FILE_PER_DAY, $dayCallback),
            'Rotation is not triggered when the file of the current day is already present'
                => array(false, RotatingFileHandler::FILE_PER_DAY, $dayCallback),

            'Rotation is triggered when the file of the current month is not present'
                => array(true, RotatingFileHandler::FILE_PER_MONTH, $monthCallback),
            'Rotation is not triggered when the file of the current month is already present'
                => array(false, RotatingFileHandler::FILE_PER_MONTH, $monthCallback),

            'Rotation is triggered when the file of the current year is not present'
                => array(true, RotatingFileHandler::FILE_PER_YEAR, $yearCallback),
            'Rotation is not triggered when the file of the current year is already present'
                => array(false, RotatingFileHandler::FILE_PER_YEAR, $yearCallback),
        );
    }

    /**
     * @dataProvider dateFormatProvider
     */
    public function testAllowOnlyFixedDefinedDateFormats($dateFormat, $valid)
    {
        $handler = new RotatingFileHandler(__DIR__.'/Fixtures/foo.rot', 2);
        $handler->setFilenameFormat('{filename}-{date}', $dateFormat);
        if (!$valid) {
            $this->assertErrorWasTriggered(
                E_USER_DEPRECATED,
                'Invalid date format - format must be one of RotatingFileHandler::FILE_PER_DAY ("Y-m-d"), '.
                'RotatingFileHandler::FILE_PER_MONTH ("Y-m") or RotatingFileHandler::FILE_PER_YEAR ("Y"), '.
                'or you can set one of the date formats using slashes, underscores and/or dots instead of dashes.'
            );
        }
    }

    public function dateFormatProvider()
    {
        return array(
            array(RotatingFileHandler::FILE_PER_DAY, true),
            array(RotatingFileHandler::FILE_PER_MONTH, true),
            array(RotatingFileHandler::FILE_PER_YEAR, true),
            array('m-d-Y', false),
            array('Y-m-d-h-i', false)
        );
    }

    /**
     * @dataProvider filenameFormatProvider
     */
    public function testDisallowFilenameFormatsWithoutDate($filenameFormat, $valid)
    {
        $handler = new RotatingFileHandler(__DIR__.'/Fixtures/foo.rot', 2);
        $handler->setFilenameFormat($filenameFormat, RotatingFileHandler::FILE_PER_DAY);
        if (!$valid) {
            $this->assertErrorWasTriggered(
                E_USER_DEPRECATED,
                'Invalid filename format - format should contain at least `{date}`, because otherwise rotating is impossible.'
            );
        }
    }

    public function filenameFormatProvider()
    {
        return array(
            array('{filename}', false),
            array('{filename}-{date}', true),
            array('{date}', true),
            array('foobar-{date}', true),
            array('foo-{date}-bar', true),
            array('{date}-foobar', true),
            array('foobar', false),
        );
    }

    /**
     * @dataProvider rotationWhenSimilarFilesExistTests
     */
    public function testRotationWhenSimilarFileNamesExist($dateFormat)
    {
        touch($old1 = __DIR__.'/Fixtures/foo-foo-'.date($dateFormat).'.rot');
        touch($old2 = __DIR__.'/Fixtures/foo-bar-'.date($dateFormat).'.rot');

        $log = __DIR__.'/Fixtures/foo-'.date($dateFormat).'.rot';

        $handler = new RotatingFileHandler(__DIR__.'/Fixtures/foo.rot', 2);
        $handler->setFormatter($this->getIdentityFormatter());
        $handler->setFilenameFormat('{filename}-{date}', $dateFormat);
        $handler->handle($this->getRecord());
        $handler->close();

        $this->assertTrue(file_exists($log));
    }

    public function rotationWhenSimilarFilesExistTests()
    {

        return array(
            'Rotation is triggered when the file of the current day is not present but similar exists'
                => array(RotatingFileHandler::FILE_PER_DAY),

            'Rotation is triggered when the file of the current month is not present but similar exists'
                => array(RotatingFileHandler::FILE_PER_MONTH),

            'Rotation is triggered when the file of the current year is not present but similar exists'
                => array(RotatingFileHandler::FILE_PER_YEAR),
        );
    }

    public function testReuseCurrentFile()
    {
        $log = __DIR__.'/Fixtures/foo-'.date('Y-m-d').'.rot';
        file_put_contents($log, "foo");
        $handler = new RotatingFileHandler(__DIR__.'/Fixtures/foo.rot');
        $handler->setFormatter($this->getIdentityFormatter());
        $handler->handle($this->getRecord());
        $this->assertEquals('footest', file_get_contents($log));
    }

    public function tearDown()
    {
        foreach (glob(__DIR__.'/Fixtures/*.rot') as $file) {
            unlink($file);
        }
        restore_error_handler();
    }
}
vendor/monolog/monolog/tests/Monolog/Handler/SamplingHandlerTest.php000064400000001533151327705700021750 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog\Handler;

use WPvividMonolog\TestCase;

/**
 * @covers Monolog\Handler\SamplingHandler::handle
 */
class SamplingHandlerTest extends TestCase
{
    public function testHandle()
    {
        $testHandler = new TestHandler();
        $handler = new SamplingHandler($testHandler, 2);
        for ($i = 0; $i < 10000; $i++) {
            $handler->handle($this->getRecord());
        }
        $count = count($testHandler->getRecords());
        // $count should be half of 10k, so between 4k and 6k
        $this->assertLessThan(6000, $count);
        $this->assertGreaterThan(4000, $count);
    }
}
vendor/monolog/monolog/tests/Monolog/Handler/DynamoDbHandlerTest.php000064400000005001151327705700021665 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog\Handler;

use WPvividMonolog\TestCase;

class DynamoDbHandlerTest extends TestCase
{
    private $client;

    public function setUp()
    {
        if (!class_exists('Aws\DynamoDb\DynamoDbClient')) {
            $this->markTestSkipped('aws/aws-sdk-php not installed');
        }

        $this->client = $this->getMockBuilder('Aws\DynamoDb\DynamoDbClient')
            ->setMethods(array('formatAttributes', '__call'))
            ->disableOriginalConstructor()->getMock();
    }

    public function testConstruct()
    {
        $this->assertInstanceOf('WPvividMonolog\Handler\DynamoDbHandler', new DynamoDbHandler($this->client, 'foo'));
    }

    public function testInterface()
    {
        $this->assertInstanceOf('WPvividMonolog\Handler\HandlerInterface', new DynamoDbHandler($this->client, 'foo'));
    }

    public function testGetFormatter()
    {
        $handler = new DynamoDbHandler($this->client, 'foo');
        $this->assertInstanceOf('WPvividMonolog\Formatter\ScalarFormatter', $handler->getFormatter());
    }

    public function testHandle()
    {
        $record = $this->getRecord();
        $formatter = $this->getMock('WPvividMonolog\Formatter\FormatterInterface');
        $formatted = array('foo' => 1, 'bar' => 2);
        $handler = new DynamoDbHandler($this->client, 'foo');
        $handler->setFormatter($formatter);

        $isV3 = defined('Aws\Sdk::VERSION') && version_compare(\Aws\Sdk::VERSION, '3.0', '>=');
        if ($isV3) {
            $expFormatted = array('foo' => array('N' => 1), 'bar' => array('N' => 2));
        } else {
            $expFormatted = $formatted;
        }

        $formatter
             ->expects($this->once())
             ->method('format')
             ->with($record)
             ->will($this->returnValue($formatted));
        $this->client
             ->expects($isV3 ? $this->never() : $this->once())
             ->method('formatAttributes')
             ->with($this->isType('array'))
             ->will($this->returnValue($formatted));
        $this->client
             ->expects($this->once())
             ->method('__call')
             ->with('putItem', array(array(
                 'TableName' => 'foo',
                 'Item' => $expFormatted,
             )));

        $handler->handle($record);
    }
}
vendor/monolog/monolog/tests/Monolog/Handler/AmqpHandlerTest.php000064400000010166151327705700021076 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog\Handler;

use WPvividMonolog\TestCase;
use WPvividMonolog\Logger;
use PhpAmqpLib\Message\AMQPMessage;
use PhpAmqpLib\Connection\AMQPConnection;

/**
 * @covers Monolog\Handler\RotatingFileHandler
 */
class AmqpHandlerTest extends TestCase
{
    public function testHandleAmqpExt()
    {
        if (!class_exists('AMQPConnection') || !class_exists('AMQPExchange')) {
            $this->markTestSkipped("amqp-php not installed");
        }

        if (!class_exists('AMQPChannel')) {
            $this->markTestSkipped("Please update AMQP to version >= 1.0");
        }

        $messages = array();

        $exchange = $this->getMock('AMQPExchange', array('publish', 'setName'), array(), '', false);
        $exchange->expects($this->once())
            ->method('setName')
            ->with('log')
        ;
        $exchange->expects($this->any())
            ->method('publish')
            ->will($this->returnCallback(function ($message, $routing_key, $flags = 0, $attributes = array()) use (&$messages) {
                $messages[] = array($message, $routing_key, $flags, $attributes);
            }))
        ;

        $handler = new AmqpHandler($exchange, 'log');

        $record = $this->getRecord(Logger::WARNING, 'test', array('data' => new \stdClass, 'foo' => 34));

        $expected = array(
            array(
                'message' => 'test',
                'context' => array(
                    'data' => array(),
                    'foo' => 34,
                ),
                'level' => 300,
                'level_name' => 'WARNING',
                'channel' => 'test',
                'extra' => array(),
            ),
            'warn.test',
            0,
            array(
                'delivery_mode' => 2,
                'content_type' => 'application/json',
            ),
        );

        $handler->handle($record);

        $this->assertCount(1, $messages);
        $messages[0][0] = json_decode($messages[0][0], true);
        unset($messages[0][0]['datetime']);
        $this->assertEquals($expected, $messages[0]);
    }

    public function testHandlePhpAmqpLib()
    {
        if (!class_exists('PhpAmqpLib\Connection\AMQPConnection')) {
            $this->markTestSkipped("php-amqplib not installed");
        }

        $messages = array();

        $exchange = $this->getMock('PhpAmqpLib\Channel\AMQPChannel', array('basic_publish', '__destruct'), array(), '', false);

        $exchange->expects($this->any())
            ->method('basic_publish')
            ->will($this->returnCallback(function (AMQPMessage $msg, $exchange = "", $routing_key = "", $mandatory = false, $immediate = false, $ticket = null) use (&$messages) {
                $messages[] = array($msg, $exchange, $routing_key, $mandatory, $immediate, $ticket);
            }))
        ;

        $handler = new AmqpHandler($exchange, 'log');

        $record = $this->getRecord(Logger::WARNING, 'test', array('data' => new \stdClass, 'foo' => 34));

        $expected = array(
            array(
                'message' => 'test',
                'context' => array(
                    'data' => array(),
                    'foo' => 34,
                ),
                'level' => 300,
                'level_name' => 'WARNING',
                'channel' => 'test',
                'extra' => array(),
            ),
            'log',
            'warn.test',
            false,
            false,
            null,
            array(
                'delivery_mode' => 2,
                'content_type' => 'application/json',
            ),
        );

        $handler->handle($record);

        $this->assertCount(1, $messages);

        /* @var $msg AMQPMessage */
        $msg = $messages[0][0];
        $messages[0][0] = json_decode($msg->body, true);
        $messages[0][] = $msg->get_properties();
        unset($messages[0][0]['datetime']);

        $this->assertEquals($expected, $messages[0]);
    }
}
vendor/monolog/monolog/tests/Monolog/Handler/SocketHandlerTest.php000064400000022560151327705700021431 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog\Handler;

use WPvividMonolog\TestCase;
use WPvividMonolog\Logger;

/**
 * @author Pablo de Leon Belloc <pablolb@gmail.com>
 */
class SocketHandlerTest extends TestCase
{
    /**
     * @var Monolog\Handler\SocketHandler
     */
    private $handler;

    /**
     * @var resource
     */
    private $res;

    /**
     * @expectedException UnexpectedValueException
     */
    public function testInvalidHostname()
    {
        $this->createHandler('garbage://here');
        $this->writeRecord('data');
    }

    /**
     * @expectedException \InvalidArgumentException
     */
    public function testBadConnectionTimeout()
    {
        $this->createHandler('localhost:1234');
        $this->handler->setConnectionTimeout(-1);
    }

    public function testSetConnectionTimeout()
    {
        $this->createHandler('localhost:1234');
        $this->handler->setConnectionTimeout(10.1);
        $this->assertEquals(10.1, $this->handler->getConnectionTimeout());
    }

    /**
     * @expectedException \InvalidArgumentException
     */
    public function testBadTimeout()
    {
        $this->createHandler('localhost:1234');
        $this->handler->setTimeout(-1);
    }

    public function testSetTimeout()
    {
        $this->createHandler('localhost:1234');
        $this->handler->setTimeout(10.25);
        $this->assertEquals(10.25, $this->handler->getTimeout());
    }

    public function testSetWritingTimeout()
    {
        $this->createHandler('localhost:1234');
        $this->handler->setWritingTimeout(10.25);
        $this->assertEquals(10.25, $this->handler->getWritingTimeout());
    }

    public function testSetChunkSize()
    {
        $this->createHandler('localhost:1234');
        $this->handler->setChunkSize(1025);
        $this->assertEquals(1025, $this->handler->getChunkSize());
    }

    public function testSetConnectionString()
    {
        $this->createHandler('tcp://localhost:9090');
        $this->assertEquals('tcp://localhost:9090', $this->handler->getConnectionString());
    }

    /**
     * @expectedException UnexpectedValueException
     */
    public function testExceptionIsThrownOnFsockopenError()
    {
        $this->setMockHandler(array('fsockopen'));
        $this->handler->expects($this->once())
            ->method('fsockopen')
            ->will($this->returnValue(false));
        $this->writeRecord('Hello world');
    }

    /**
     * @expectedException UnexpectedValueException
     */
    public function testExceptionIsThrownOnPfsockopenError()
    {
        $this->setMockHandler(array('pfsockopen'));
        $this->handler->expects($this->once())
            ->method('pfsockopen')
            ->will($this->returnValue(false));
        $this->handler->setPersistent(true);
        $this->writeRecord('Hello world');
    }

    /**
     * @expectedException UnexpectedValueException
     */
    public function testExceptionIsThrownIfCannotSetTimeout()
    {
        $this->setMockHandler(array('streamSetTimeout'));
        $this->handler->expects($this->once())
            ->method('streamSetTimeout')
            ->will($this->returnValue(false));
        $this->writeRecord('Hello world');
    }

    /**
     * @expectedException UnexpectedValueException
     */
    public function testExceptionIsThrownIfCannotSetChunkSize()
    {
        $this->setMockHandler(array('streamSetChunkSize'));
        $this->handler->setChunkSize(8192);
        $this->handler->expects($this->once())
            ->method('streamSetChunkSize')
            ->will($this->returnValue(false));
        $this->writeRecord('Hello world');
    }

    /**
     * @expectedException RuntimeException
     */
    public function testWriteFailsOnIfFwriteReturnsFalse()
    {
        $this->setMockHandler(array('fwrite'));

        $callback = function ($arg) {
            $map = array(
                'Hello world' => 6,
                'world' => false,
            );

            return $map[$arg];
        };

        $this->handler->expects($this->exactly(2))
            ->method('fwrite')
            ->will($this->returnCallback($callback));

        $this->writeRecord('Hello world');
    }

    /**
     * @expectedException RuntimeException
     */
    public function testWriteFailsIfStreamTimesOut()
    {
        $this->setMockHandler(array('fwrite', 'streamGetMetadata'));

        $callback = function ($arg) {
            $map = array(
                'Hello world' => 6,
                'world' => 5,
            );

            return $map[$arg];
        };

        $this->handler->expects($this->exactly(1))
            ->method('fwrite')
            ->will($this->returnCallback($callback));
        $this->handler->expects($this->exactly(1))
            ->method('streamGetMetadata')
            ->will($this->returnValue(array('timed_out' => true)));

        $this->writeRecord('Hello world');
    }

    /**
     * @expectedException RuntimeException
     */
    public function testWriteFailsOnIncompleteWrite()
    {
        $this->setMockHandler(array('fwrite', 'streamGetMetadata'));

        $res = $this->res;
        $callback = function ($string) use ($res) {
            fclose($res);

            return strlen('Hello');
        };

        $this->handler->expects($this->exactly(1))
            ->method('fwrite')
            ->will($this->returnCallback($callback));
        $this->handler->expects($this->exactly(1))
            ->method('streamGetMetadata')
            ->will($this->returnValue(array('timed_out' => false)));

        $this->writeRecord('Hello world');
    }

    public function testWriteWithMemoryFile()
    {
        $this->setMockHandler();
        $this->writeRecord('test1');
        $this->writeRecord('test2');
        $this->writeRecord('test3');
        fseek($this->res, 0);
        $this->assertEquals('test1test2test3', fread($this->res, 1024));
    }

    public function testWriteWithMock()
    {
        $this->setMockHandler(array('fwrite'));

        $callback = function ($arg) {
            $map = array(
                'Hello world' => 6,
                'world' => 5,
            );

            return $map[$arg];
        };

        $this->handler->expects($this->exactly(2))
            ->method('fwrite')
            ->will($this->returnCallback($callback));

        $this->writeRecord('Hello world');
    }

    public function testClose()
    {
        $this->setMockHandler();
        $this->writeRecord('Hello world');
        $this->assertInternalType('resource', $this->res);
        $this->handler->close();
        $this->assertFalse(is_resource($this->res), "Expected resource to be closed after closing handler");
    }

    public function testCloseDoesNotClosePersistentSocket()
    {
        $this->setMockHandler();
        $this->handler->setPersistent(true);
        $this->writeRecord('Hello world');
        $this->assertTrue(is_resource($this->res));
        $this->handler->close();
        $this->assertTrue(is_resource($this->res));
    }

    /**
     * @expectedException \RuntimeException
     */
    public function testAvoidInfiniteLoopWhenNoDataIsWrittenForAWritingTimeoutSeconds()
    {
        $this->setMockHandler(array('fwrite', 'streamGetMetadata'));

        $this->handler->expects($this->any())
            ->method('fwrite')
            ->will($this->returnValue(0));

        $this->handler->expects($this->any())
            ->method('streamGetMetadata')
            ->will($this->returnValue(array('timed_out' => false)));

        $this->handler->setWritingTimeout(1);

        $this->writeRecord('Hello world');
    }

    private function createHandler($connectionString)
    {
        $this->handler = new SocketHandler($connectionString);
        $this->handler->setFormatter($this->getIdentityFormatter());
    }

    private function writeRecord($string)
    {
        $this->handler->handle($this->getRecord(Logger::WARNING, $string));
    }

    private function setMockHandler(array $methods = array())
    {
        $this->res = fopen('php://memory', 'a');

        $defaultMethods = array('fsockopen', 'pfsockopen', 'streamSetTimeout');
        $newMethods = array_diff($methods, $defaultMethods);

        $finalMethods = array_merge($defaultMethods, $newMethods);

        $this->handler = $this->getMock(
            '\WPvividMonolog\Handler\SocketHandler', $finalMethods, array('localhost:1234')
        );

        if (!in_array('fsockopen', $methods)) {
            $this->handler->expects($this->any())
                ->method('fsockopen')
                ->will($this->returnValue($this->res));
        }

        if (!in_array('pfsockopen', $methods)) {
            $this->handler->expects($this->any())
                ->method('pfsockopen')
                ->will($this->returnValue($this->res));
        }

        if (!in_array('streamSetTimeout', $methods)) {
            $this->handler->expects($this->any())
                ->method('streamSetTimeout')
                ->will($this->returnValue(true));
        }

        if (!in_array('streamSetChunkSize', $methods)) {
            $this->handler->expects($this->any())
                ->method('streamSetChunkSize')
                ->will($this->returnValue(8192));
        }

        $this->handler->setFormatter($this->getIdentityFormatter());
    }
}
vendor/monolog/monolog/tests/Monolog/Handler/LogEntriesHandlerTest.php000064400000004602151327705700022251 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog\Handler;

use WPvividMonolog\TestCase;
use WPvividMonolog\Logger;

/**
 * @author Robert Kaufmann III <rok3@rok3.me>
 */
class LogEntriesHandlerTest extends TestCase
{
    /**
     * @var resource
     */
    private $res;

    /**
     * @var LogEntriesHandler
     */
    private $handler;

    public function testWriteContent()
    {
        $this->createHandler();
        $this->handler->handle($this->getRecord(Logger::CRITICAL, 'Critical write test'));

        fseek($this->res, 0);
        $content = fread($this->res, 1024);

        $this->assertRegexp('/testToken \[\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\] test.CRITICAL: Critical write test/', $content);
    }

    public function testWriteBatchContent()
    {
        $records = array(
            $this->getRecord(),
            $this->getRecord(),
            $this->getRecord(),
        );
        $this->createHandler();
        $this->handler->handleBatch($records);

        fseek($this->res, 0);
        $content = fread($this->res, 1024);

        $this->assertRegexp('/(testToken \[\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\] .* \[\] \[\]\n){3}/', $content);
    }

    private function createHandler()
    {
        $useSSL = extension_loaded('openssl');
        $args = array('testToken', $useSSL, Logger::DEBUG, true);
        $this->res = fopen('php://memory', 'a');
        $this->handler = $this->getMock(
            '\WPvividMonolog\Handler\LogEntriesHandler',
            array('fsockopen', 'streamSetTimeout', 'closeSocket'),
            $args
        );

        $reflectionProperty = new \ReflectionProperty('\WPvividMonolog\Handler\SocketHandler', 'connectionString');
        $reflectionProperty->setAccessible(true);
        $reflectionProperty->setValue($this->handler, 'localhost:1234');

        $this->handler->expects($this->any())
            ->method('fsockopen')
            ->will($this->returnValue($this->res));
        $this->handler->expects($this->any())
            ->method('streamSetTimeout')
            ->will($this->returnValue(true));
        $this->handler->expects($this->any())
            ->method('closeSocket')
            ->will($this->returnValue(true));
    }
}
vendor/monolog/monolog/tests/Monolog/Handler/FirePHPHandlerTest.php000064400000005660151327705700021440 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog\Handler;

use WPvividMonolog\TestCase;
use WPvividMonolog\Logger;

/**
 * @covers Monolog\Handler\FirePHPHandler
 */
class FirePHPHandlerTest extends TestCase
{
    public function setUp()
    {
        TestFirePHPHandler::resetStatic();
        $_SERVER['HTTP_USER_AGENT'] = 'Monolog Test; FirePHP/1.0';
    }

    public function testHeaders()
    {
        $handler = new TestFirePHPHandler;
        $handler->setFormatter($this->getIdentityFormatter());
        $handler->handle($this->getRecord(Logger::DEBUG));
        $handler->handle($this->getRecord(Logger::WARNING));

        $expected = array(
            'X-Wf-Protocol-1'    => 'http://meta.wildfirehq.org/Protocol/JsonStream/0.2',
            'X-Wf-1-Structure-1' => 'http://meta.firephp.org/Wildfire/Structure/FirePHP/FirebugConsole/0.1',
            'X-Wf-1-Plugin-1'    => 'http://meta.firephp.org/Wildfire/Plugin/FirePHP/Library-FirePHPCore/0.3',
            'X-Wf-1-1-1-1'       => 'test',
            'X-Wf-1-1-1-2'       => 'test',
        );

        $this->assertEquals($expected, $handler->getHeaders());
    }

    public function testConcurrentHandlers()
    {
        $handler = new TestFirePHPHandler;
        $handler->setFormatter($this->getIdentityFormatter());
        $handler->handle($this->getRecord(Logger::DEBUG));
        $handler->handle($this->getRecord(Logger::WARNING));

        $handler2 = new TestFirePHPHandler;
        $handler2->setFormatter($this->getIdentityFormatter());
        $handler2->handle($this->getRecord(Logger::DEBUG));
        $handler2->handle($this->getRecord(Logger::WARNING));

        $expected = array(
            'X-Wf-Protocol-1'    => 'http://meta.wildfirehq.org/Protocol/JsonStream/0.2',
            'X-Wf-1-Structure-1' => 'http://meta.firephp.org/Wildfire/Structure/FirePHP/FirebugConsole/0.1',
            'X-Wf-1-Plugin-1'    => 'http://meta.firephp.org/Wildfire/Plugin/FirePHP/Library-FirePHPCore/0.3',
            'X-Wf-1-1-1-1'       => 'test',
            'X-Wf-1-1-1-2'       => 'test',
        );

        $expected2 = array(
            'X-Wf-1-1-1-3'       => 'test',
            'X-Wf-1-1-1-4'       => 'test',
        );

        $this->assertEquals($expected, $handler->getHeaders());
        $this->assertEquals($expected2, $handler2->getHeaders());
    }
}

class TestFirePHPHandler extends FirePHPHandler
{
    protected $headers = array();

    public static function resetStatic()
    {
        self::$initialized = false;
        self::$sendHeaders = true;
        self::$messageIndex = 1;
    }

    protected function sendHeader($header, $content)
    {
        $this->headers[$header] = $content;
    }

    public function getHeaders()
    {
        return $this->headers;
    }
}
vendor/monolog/monolog/tests/Monolog/Handler/GelfHandlerLegacyTest.php000064400000006232151327705700022201 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog\Handler;

use Gelf\Message;
use WPvividMonolog\TestCase;
use WPvividMonolog\Logger;
use WPvividMonolog\Formatter\GelfMessageFormatter;

class GelfHandlerLegacyTest extends TestCase
{
    public function setUp()
    {
        if (!class_exists('Gelf\MessagePublisher') || !class_exists('Gelf\Message')) {
            $this->markTestSkipped("mlehner/gelf-php not installed");
        }

        require_once __DIR__ . '/GelfMockMessagePublisher.php';
    }

    /**
     * @covers Monolog\Handler\GelfHandler::__construct
     */
    public function testConstruct()
    {
        $handler = new GelfHandler($this->getMessagePublisher());
        $this->assertInstanceOf('WPvividMonolog\Handler\GelfHandler', $handler);
    }

    protected function getHandler($messagePublisher)
    {
        $handler = new GelfHandler($messagePublisher);

        return $handler;
    }

    protected function getMessagePublisher()
    {
        return new GelfMockMessagePublisher('localhost');
    }

    public function testDebug()
    {
        $messagePublisher = $this->getMessagePublisher();
        $handler = $this->getHandler($messagePublisher);

        $record = $this->getRecord(Logger::DEBUG, "A test debug message");
        $handler->handle($record);

        $this->assertEquals(7, $messagePublisher->lastMessage->getLevel());
        $this->assertEquals('test', $messagePublisher->lastMessage->getFacility());
        $this->assertEquals($record['message'], $messagePublisher->lastMessage->getShortMessage());
        $this->assertEquals(null, $messagePublisher->lastMessage->getFullMessage());
    }

    public function testWarning()
    {
        $messagePublisher = $this->getMessagePublisher();
        $handler = $this->getHandler($messagePublisher);

        $record = $this->getRecord(Logger::WARNING, "A test warning message");
        $handler->handle($record);

        $this->assertEquals(4, $messagePublisher->lastMessage->getLevel());
        $this->assertEquals('test', $messagePublisher->lastMessage->getFacility());
        $this->assertEquals($record['message'], $messagePublisher->lastMessage->getShortMessage());
        $this->assertEquals(null, $messagePublisher->lastMessage->getFullMessage());
    }

    public function testInjectedGelfMessageFormatter()
    {
        $messagePublisher = $this->getMessagePublisher();
        $handler = $this->getHandler($messagePublisher);

        $handler->setFormatter(new GelfMessageFormatter('mysystem', 'EXT', 'CTX'));

        $record = $this->getRecord(Logger::WARNING, "A test warning message");
        $record['extra']['blarg'] = 'yep';
        $record['context']['from'] = 'logger';
        $handler->handle($record);

        $this->assertEquals('mysystem', $messagePublisher->lastMessage->getHost());
        $this->assertArrayHasKey('_EXTblarg', $messagePublisher->lastMessage->toArray());
        $this->assertArrayHasKey('_CTXfrom', $messagePublisher->lastMessage->toArray());
    }
}
vendor/monolog/monolog/tests/Monolog/Handler/CouchDBHandlerTest.php000064400000001404151327705700021442 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog\Handler;

use WPvividMonolog\TestCase;
use WPvividMonolog\Logger;

class CouchDBHandlerTest extends TestCase
{
    public function testHandle()
    {
        $record = $this->getRecord(Logger::WARNING, 'test', array('data' => new \stdClass, 'foo' => 34));

        $handler = new CouchDBHandler();

        try {
            $handler->handle($record);
        } catch (\RuntimeException $e) {
            $this->markTestSkipped('Could not connect to couchdb server on http://localhost:5984');
        }
    }
}
vendor/monolog/monolog/tests/Monolog/Handler/SyslogHandlerTest.php000064400000002455151327705700021462 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog\Handler;

use WPvividMonolog\Logger;

class SyslogHandlerTest extends \PHPUnit_Framework_TestCase
{
    /**
     * @covers Monolog\Handler\SyslogHandler::__construct
     */
    public function testConstruct()
    {
        $handler = new SyslogHandler('test');
        $this->assertInstanceOf('WPvividMonolog\Handler\SyslogHandler', $handler);

        $handler = new SyslogHandler('test', LOG_USER);
        $this->assertInstanceOf('WPvividMonolog\Handler\SyslogHandler', $handler);

        $handler = new SyslogHandler('test', 'user');
        $this->assertInstanceOf('WPvividMonolog\Handler\SyslogHandler', $handler);

        $handler = new SyslogHandler('test', LOG_USER, Logger::DEBUG, true, LOG_PERROR);
        $this->assertInstanceOf('WPvividMonolog\Handler\SyslogHandler', $handler);
    }

    /**
     * @covers Monolog\Handler\SyslogHandler::__construct
     */
    public function testConstructInvalidFacility()
    {
        $this->setExpectedException('UnexpectedValueException');
        $handler = new SyslogHandler('test', 'unknown');
    }
}
vendor/monolog/monolog/tests/Monolog/Handler/ElasticSearchHandlerTest.php000064400000017062151327705700022714 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog\Handler;

use WPvividMonolog\Formatter\ElasticaFormatter;
use WPvividMonolog\Formatter\NormalizerFormatter;
use WPvividMonolog\TestCase;
use WPvividMonolog\Logger;
use Elastica\Client;
use Elastica\Request;
use Elastica\Response;

class ElasticSearchHandlerTest extends TestCase
{
    /**
     * @var Client mock
     */
    protected $client;

    /**
     * @var array Default handler options
     */
    protected $options = array(
        'index' => 'my_index',
        'type'  => 'doc_type',
    );

    public function setUp()
    {
        // Elastica lib required
        if (!class_exists("Elastica\Client")) {
            $this->markTestSkipped("ruflin/elastica not installed");
        }

        // base mock Elastica Client object
        $this->client = $this->getMockBuilder('Elastica\Client')
            ->setMethods(array('addDocuments'))
            ->disableOriginalConstructor()
            ->getMock();
    }

    /**
     * @covers Monolog\Handler\ElasticSearchHandler::write
     * @covers Monolog\Handler\ElasticSearchHandler::handleBatch
     * @covers Monolog\Handler\ElasticSearchHandler::bulkSend
     * @covers Monolog\Handler\ElasticSearchHandler::getDefaultFormatter
     */
    public function testHandle()
    {
        // log message
        $msg = array(
            'level' => Logger::ERROR,
            'level_name' => 'ERROR',
            'channel' => 'meh',
            'context' => array('foo' => 7, 'bar', 'class' => new \stdClass),
            'datetime' => new \DateTime("@0"),
            'extra' => array(),
            'message' => 'log',
        );

        // format expected result
        $formatter = new ElasticaFormatter($this->options['index'], $this->options['type']);
        $expected = array($formatter->format($msg));

        // setup ES client mock
        $this->client->expects($this->any())
            ->method('addDocuments')
            ->with($expected);

        // perform tests
        $handler = new ElasticSearchHandler($this->client, $this->options);
        $handler->handle($msg);
        $handler->handleBatch(array($msg));
    }

    /**
     * @covers Monolog\Handler\ElasticSearchHandler::setFormatter
     */
    public function testSetFormatter()
    {
        $handler = new ElasticSearchHandler($this->client);
        $formatter = new ElasticaFormatter('index_new', 'type_new');
        $handler->setFormatter($formatter);
        $this->assertInstanceOf('WPvividMonolog\Formatter\ElasticaFormatter', $handler->getFormatter());
        $this->assertEquals('index_new', $handler->getFormatter()->getIndex());
        $this->assertEquals('type_new', $handler->getFormatter()->getType());
    }

    /**
     * @covers                   Monolog\Handler\ElasticSearchHandler::setFormatter
     * @expectedException        InvalidArgumentException
     * @expectedExceptionMessage ElasticSearchHandler is only compatible with ElasticaFormatter
     */
    public function testSetFormatterInvalid()
    {
        $handler = new ElasticSearchHandler($this->client);
        $formatter = new NormalizerFormatter();
        $handler->setFormatter($formatter);
    }

    /**
     * @covers Monolog\Handler\ElasticSearchHandler::__construct
     * @covers Monolog\Handler\ElasticSearchHandler::getOptions
     */
    public function testOptions()
    {
        $expected = array(
            'index' => $this->options['index'],
            'type' => $this->options['type'],
            'ignore_error' => false,
        );
        $handler = new ElasticSearchHandler($this->client, $this->options);
        $this->assertEquals($expected, $handler->getOptions());
    }

    /**
     * @covers       Monolog\Handler\ElasticSearchHandler::bulkSend
     * @dataProvider providerTestConnectionErrors
     */
    public function testConnectionErrors($ignore, $expectedError)
    {
        $clientOpts = array('host' => '127.0.0.1', 'port' => 1);
        $client = new Client($clientOpts);
        $handlerOpts = array('ignore_error' => $ignore);
        $handler = new ElasticSearchHandler($client, $handlerOpts);

        if ($expectedError) {
            $this->setExpectedException($expectedError[0], $expectedError[1]);
            $handler->handle($this->getRecord());
        } else {
            $this->assertFalse($handler->handle($this->getRecord()));
        }
    }

    /**
     * @return array
     */
    public function providerTestConnectionErrors()
    {
        return array(
            array(false, array('RuntimeException', 'Error sending messages to Elasticsearch')),
            array(true, false),
        );
    }

    /**
     * Integration test using localhost Elastic Search server
     *
     * @covers Monolog\Handler\ElasticSearchHandler::__construct
     * @covers Monolog\Handler\ElasticSearchHandler::handleBatch
     * @covers Monolog\Handler\ElasticSearchHandler::bulkSend
     * @covers Monolog\Handler\ElasticSearchHandler::getDefaultFormatter
     */
    public function testHandleIntegration()
    {
        $msg = array(
            'level' => Logger::ERROR,
            'level_name' => 'ERROR',
            'channel' => 'meh',
            'context' => array('foo' => 7, 'bar', 'class' => new \stdClass),
            'datetime' => new \DateTime("@0"),
            'extra' => array(),
            'message' => 'log',
        );

        $expected = $msg;
        $expected['datetime'] = $msg['datetime']->format(\DateTime::ISO8601);
        $expected['context'] = array(
            'class' => '[object] (stdClass: {})',
            'foo' => 7,
            0 => 'bar',
        );

        $client = new Client();
        $handler = new ElasticSearchHandler($client, $this->options);
        try {
            $handler->handleBatch(array($msg));
        } catch (\RuntimeException $e) {
            $this->markTestSkipped("Cannot connect to Elastic Search server on localhost");
        }

        // check document id from ES server response
        $documentId = $this->getCreatedDocId($client->getLastResponse());
        $this->assertNotEmpty($documentId, 'No elastic document id received');

        // retrieve document source from ES and validate
        $document = $this->getDocSourceFromElastic(
            $client,
            $this->options['index'],
            $this->options['type'],
            $documentId
        );
        $this->assertEquals($expected, $document);

        // remove test index from ES
        $client->request("/{$this->options['index']}", Request::DELETE);
    }

    /**
     * Return last created document id from ES response
     * @param  Response    $response Elastica Response object
     * @return string|null
     */
    protected function getCreatedDocId(Response $response)
    {
        $data = $response->getData();
        if (!empty($data['items'][0]['create']['_id'])) {
            return $data['items'][0]['create']['_id'];
        }
    }

    /**
     * Retrieve document by id from Elasticsearch
     * @param  Client $client     Elastica client
     * @param  string $index
     * @param  string $type
     * @param  string $documentId
     * @return array
     */
    protected function getDocSourceFromElastic(Client $client, $index, $type, $documentId)
    {
        $resp = $client->request("/{$index}/{$type}/{$documentId}", Request::GET);
        $data = $resp->getData();
        if (!empty($data['_source'])) {
            return $data['_source'];
        }

        return array();
    }
}
vendor/monolog/monolog/tests/Monolog/Handler/BrowserConsoleHandlerTest.php000064400000010215151327705700023141 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog\Handler;

use WPvividMonolog\TestCase;
use WPvividMonolog\Logger;

/**
 * @covers Monolog\Handler\BrowserConsoleHandlerTest
 */
class BrowserConsoleHandlerTest extends TestCase
{
    protected function setUp()
    {
        BrowserConsoleHandler::resetStatic();
    }

    protected function generateScript()
    {
        $reflMethod = new \ReflectionMethod('WPvividMonolog\Handler\BrowserConsoleHandler', 'generateScript');
        $reflMethod->setAccessible(true);

        return $reflMethod->invoke(null);
    }

    public function testStyling()
    {
        $handler = new BrowserConsoleHandler();
        $handler->setFormatter($this->getIdentityFormatter());

        $handler->handle($this->getRecord(Logger::DEBUG, 'foo[[bar]]{color: red}'));

        $expected = <<<EOF
(function (c) {if (c && c.groupCollapsed) {
c.log("%cfoo%cbar%c", "font-weight: normal", "color: red", "font-weight: normal");
}})(console);
EOF;

        $this->assertEquals($expected, $this->generateScript());
    }

    public function testEscaping()
    {
        $handler = new BrowserConsoleHandler();
        $handler->setFormatter($this->getIdentityFormatter());

        $handler->handle($this->getRecord(Logger::DEBUG, "[foo] [[\"bar\n[baz]\"]]{color: red}"));

        $expected = <<<EOF
(function (c) {if (c && c.groupCollapsed) {
c.log("%c[foo] %c\"bar\\n[baz]\"%c", "font-weight: normal", "color: red", "font-weight: normal");
}})(console);
EOF;

        $this->assertEquals($expected, $this->generateScript());
    }

    public function testAutolabel()
    {
        $handler = new BrowserConsoleHandler();
        $handler->setFormatter($this->getIdentityFormatter());

        $handler->handle($this->getRecord(Logger::DEBUG, '[[foo]]{macro: autolabel}'));
        $handler->handle($this->getRecord(Logger::DEBUG, '[[bar]]{macro: autolabel}'));
        $handler->handle($this->getRecord(Logger::DEBUG, '[[foo]]{macro: autolabel}'));

        $expected = <<<EOF
(function (c) {if (c && c.groupCollapsed) {
c.log("%c%cfoo%c", "font-weight: normal", "background-color: blue; color: white; border-radius: 3px; padding: 0 2px 0 2px", "font-weight: normal");
c.log("%c%cbar%c", "font-weight: normal", "background-color: green; color: white; border-radius: 3px; padding: 0 2px 0 2px", "font-weight: normal");
c.log("%c%cfoo%c", "font-weight: normal", "background-color: blue; color: white; border-radius: 3px; padding: 0 2px 0 2px", "font-weight: normal");
}})(console);
EOF;

        $this->assertEquals($expected, $this->generateScript());
    }

    public function testContext()
    {
        $handler = new BrowserConsoleHandler();
        $handler->setFormatter($this->getIdentityFormatter());

        $handler->handle($this->getRecord(Logger::DEBUG, 'test', array('foo' => 'bar')));

        $expected = <<<EOF
(function (c) {if (c && c.groupCollapsed) {
c.groupCollapsed("%ctest", "font-weight: normal");
c.log("%c%s", "font-weight: bold", "Context");
c.log("%s: %o", "foo", "bar");
c.groupEnd();
}})(console);
EOF;

        $this->assertEquals($expected, $this->generateScript());
    }

    public function testConcurrentHandlers()
    {
        $handler1 = new BrowserConsoleHandler();
        $handler1->setFormatter($this->getIdentityFormatter());

        $handler2 = new BrowserConsoleHandler();
        $handler2->setFormatter($this->getIdentityFormatter());

        $handler1->handle($this->getRecord(Logger::DEBUG, 'test1'));
        $handler2->handle($this->getRecord(Logger::DEBUG, 'test2'));
        $handler1->handle($this->getRecord(Logger::DEBUG, 'test3'));
        $handler2->handle($this->getRecord(Logger::DEBUG, 'test4'));

        $expected = <<<EOF
(function (c) {if (c && c.groupCollapsed) {
c.log("%ctest1", "font-weight: normal");
c.log("%ctest2", "font-weight: normal");
c.log("%ctest3", "font-weight: normal");
c.log("%ctest4", "font-weight: normal");
}})(console);
EOF;

        $this->assertEquals($expected, $this->generateScript());
    }
}
vendor/monolog/monolog/tests/Monolog/Handler/SlackHandlerTest.php000064400000012625151327705700021237 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog\Handler;

use WPvividMonolog\TestCase;
use WPvividMonolog\Logger;
use WPvividMonolog\Formatter\LineFormatter;
use WPvividMonolog\Handler\Slack\SlackRecord;

/**
 * @author Greg Kedzierski <greg@gregkedzierski.com>
 * @see    https://api.slack.com/
 */
class SlackHandlerTest extends TestCase
{
    /**
     * @var resource
     */
    private $res;

    /**
     * @var SlackHandler
     */
    private $handler;

    public function setUp()
    {
        if (!extension_loaded('openssl')) {
            $this->markTestSkipped('This test requires openssl to run');
        }
    }

    public function testWriteHeader()
    {
        $this->createHandler();
        $this->handler->handle($this->getRecord(Logger::CRITICAL, 'test1'));
        fseek($this->res, 0);
        $content = fread($this->res, 1024);

        $this->assertRegexp('/POST \/api\/chat.postMessage HTTP\/1.1\\r\\nHost: slack.com\\r\\nContent-Type: application\/x-www-form-urlencoded\\r\\nContent-Length: \d{2,4}\\r\\n\\r\\n/', $content);
    }

    public function testWriteContent()
    {
        $this->createHandler();
        $this->handler->handle($this->getRecord(Logger::CRITICAL, 'test1'));
        fseek($this->res, 0);
        $content = fread($this->res, 1024);

        $this->assertRegExp('/username=WPvividMonolog/', $content);
        $this->assertRegExp('/channel=channel1/', $content);
        $this->assertRegExp('/token=myToken/', $content);
        $this->assertRegExp('/attachments/', $content);
    }

    public function testWriteContentUsesFormatterIfProvided()
    {
        $this->createHandler('myToken', 'channel1', 'WPvividMonolog', false);
        $this->handler->handle($this->getRecord(Logger::CRITICAL, 'test1'));
        fseek($this->res, 0);
        $content = fread($this->res, 1024);

        $this->createHandler('myToken', 'channel1', 'WPvividMonolog', false);
        $this->handler->setFormatter(new LineFormatter('foo--%message%'));
        $this->handler->handle($this->getRecord(Logger::CRITICAL, 'test2'));
        fseek($this->res, 0);
        $content2 = fread($this->res, 1024);

        $this->assertRegexp('/text=test1/', $content);
        $this->assertRegexp('/text=foo--test2/', $content2);
    }

    public function testWriteContentWithEmoji()
    {
        $this->createHandler('myToken', 'channel1', 'WPvividMonolog', true, 'alien');
        $this->handler->handle($this->getRecord(Logger::CRITICAL, 'test1'));
        fseek($this->res, 0);
        $content = fread($this->res, 1024);

        $this->assertRegexp('/icon_emoji=%3Aalien%3A/', $content);
    }

    /**
     * @dataProvider provideLevelColors
     */
    public function testWriteContentWithColors($level, $expectedColor)
    {
        $this->createHandler();
        $this->handler->handle($this->getRecord($level, 'test1'));
        fseek($this->res, 0);
        $content = fread($this->res, 1024);

        $this->assertRegexp('/%22color%22%3A%22'.$expectedColor.'/', $content);
    }

    public function testWriteContentWithPlainTextMessage()
    {
        $this->createHandler('myToken', 'channel1', 'WPvividMonolog', false);
        $this->handler->handle($this->getRecord(Logger::CRITICAL, 'test1'));
        fseek($this->res, 0);
        $content = fread($this->res, 1024);

        $this->assertRegexp('/text=test1/', $content);
    }

    public function provideLevelColors()
    {
        return array(
            array(Logger::DEBUG,    urlencode(SlackRecord::COLOR_DEFAULT)),
            array(Logger::INFO,     SlackRecord::COLOR_GOOD),
            array(Logger::NOTICE,   SlackRecord::COLOR_GOOD),
            array(Logger::WARNING,  SlackRecord::COLOR_WARNING),
            array(Logger::ERROR,    SlackRecord::COLOR_DANGER),
            array(Logger::CRITICAL, SlackRecord::COLOR_DANGER),
            array(Logger::ALERT,    SlackRecord::COLOR_DANGER),
            array(Logger::EMERGENCY,SlackRecord::COLOR_DANGER),
        );
    }

    private function createHandler($token = 'myToken', $channel = 'channel1', $username = 'WPvividMonolog', $useAttachment = true, $iconEmoji = null, $useShortAttachment = false, $includeExtra = false)
    {
        $constructorArgs = array($token, $channel, $username, $useAttachment, $iconEmoji, Logger::DEBUG, true, $useShortAttachment, $includeExtra);
        $this->res = fopen('php://memory', 'a');
        $this->handler = $this->getMock(
            '\WPvividMonolog\Handler\SlackHandler',
            array('fsockopen', 'streamSetTimeout', 'closeSocket'),
            $constructorArgs
        );

        $reflectionProperty = new \ReflectionProperty('\WPvividMonolog\Handler\SocketHandler', 'connectionString');
        $reflectionProperty->setAccessible(true);
        $reflectionProperty->setValue($this->handler, 'localhost:1234');

        $this->handler->expects($this->any())
            ->method('fsockopen')
            ->will($this->returnValue($this->res));
        $this->handler->expects($this->any())
            ->method('streamSetTimeout')
            ->will($this->returnValue(true));
        $this->handler->expects($this->any())
            ->method('closeSocket')
            ->will($this->returnValue(true));

        $this->handler->setFormatter($this->getIdentityFormatter());
    }
}
vendor/monolog/monolog/tests/Monolog/Handler/GroupHandlerTest.php000064400000007346151327705700021302 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog\Handler;

use WPvividMonolog\TestCase;
use WPvividMonolog\Logger;

class GroupHandlerTest extends TestCase
{
    /**
     * @covers Monolog\Handler\GroupHandler::__construct
     * @expectedException InvalidArgumentException
     */
    public function testConstructorOnlyTakesHandler()
    {
        new GroupHandler(array(new TestHandler(), "foo"));
    }

    /**
     * @covers Monolog\Handler\GroupHandler::__construct
     * @covers Monolog\Handler\GroupHandler::handle
     */
    public function testHandle()
    {
        $testHandlers = array(new TestHandler(), new TestHandler());
        $handler = new GroupHandler($testHandlers);
        $handler->handle($this->getRecord(Logger::DEBUG));
        $handler->handle($this->getRecord(Logger::INFO));
        foreach ($testHandlers as $test) {
            $this->assertTrue($test->hasDebugRecords());
            $this->assertTrue($test->hasInfoRecords());
            $this->assertTrue(count($test->getRecords()) === 2);
        }
    }

    /**
     * @covers Monolog\Handler\GroupHandler::handleBatch
     */
    public function testHandleBatch()
    {
        $testHandlers = array(new TestHandler(), new TestHandler());
        $handler = new GroupHandler($testHandlers);
        $handler->handleBatch(array($this->getRecord(Logger::DEBUG), $this->getRecord(Logger::INFO)));
        foreach ($testHandlers as $test) {
            $this->assertTrue($test->hasDebugRecords());
            $this->assertTrue($test->hasInfoRecords());
            $this->assertTrue(count($test->getRecords()) === 2);
        }
    }

    /**
     * @covers Monolog\Handler\GroupHandler::isHandling
     */
    public function testIsHandling()
    {
        $testHandlers = array(new TestHandler(Logger::ERROR), new TestHandler(Logger::WARNING));
        $handler = new GroupHandler($testHandlers);
        $this->assertTrue($handler->isHandling($this->getRecord(Logger::ERROR)));
        $this->assertTrue($handler->isHandling($this->getRecord(Logger::WARNING)));
        $this->assertFalse($handler->isHandling($this->getRecord(Logger::DEBUG)));
    }

    /**
     * @covers Monolog\Handler\GroupHandler::handle
     */
    public function testHandleUsesProcessors()
    {
        $test = new TestHandler();
        $handler = new GroupHandler(array($test));
        $handler->pushProcessor(function ($record) {
            $record['extra']['foo'] = true;

            return $record;
        });
        $handler->handle($this->getRecord(Logger::WARNING));
        $this->assertTrue($test->hasWarningRecords());
        $records = $test->getRecords();
        $this->assertTrue($records[0]['extra']['foo']);
    }

    /**
     * @covers Monolog\Handler\GroupHandler::handle
     */
    public function testHandleBatchUsesProcessors()
    {
        $testHandlers = array(new TestHandler(), new TestHandler());
        $handler = new GroupHandler($testHandlers);
        $handler->pushProcessor(function ($record) {
            $record['extra']['foo'] = true;

            return $record;
        });
        $handler->handleBatch(array($this->getRecord(Logger::DEBUG), $this->getRecord(Logger::INFO)));
        foreach ($testHandlers as $test) {
            $this->assertTrue($test->hasDebugRecords());
            $this->assertTrue($test->hasInfoRecords());
            $this->assertTrue(count($test->getRecords()) === 2);
            $records = $test->getRecords();
            $this->assertTrue($records[0]['extra']['foo']);
            $this->assertTrue($records[1]['extra']['foo']);
        }
    }
}
vendor/monolog/monolog/tests/Monolog/Handler/FleepHookHandlerTest.php000064400000004336151327705700022056 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog\Handler;

use WPvividMonolog\Formatter\LineFormatter;
use WPvividMonolog\Logger;
use WPvividMonolog\TestCase;

/**
 * @coversDefaultClass \Monolog\Handler\FleepHookHandler
 */
class FleepHookHandlerTest extends TestCase
{
    /**
     * Default token to use in tests
     */
    const TOKEN = '123abc';

    /**
     * @var FleepHookHandler
     */
    private $handler;

    public function setUp()
    {
        parent::setUp();

        if (!extension_loaded('openssl')) {
            $this->markTestSkipped('This test requires openssl extension to run');
        }

        // Create instances of the handler and logger for convenience
        $this->handler = new FleepHookHandler(self::TOKEN);
    }

    /**
     * @covers ::__construct
     */
    public function testConstructorSetsExpectedDefaults()
    {
        $this->assertEquals(Logger::DEBUG, $this->handler->getLevel());
        $this->assertEquals(true, $this->handler->getBubble());
    }

    /**
     * @covers ::getDefaultFormatter
     */
    public function testHandlerUsesLineFormatterWhichIgnoresEmptyArrays()
    {
        $record = array(
            'message' => 'msg',
            'context' => array(),
            'level' => Logger::DEBUG,
            'level_name' => Logger::getLevelName(Logger::DEBUG),
            'channel' => 'channel',
            'datetime' => new \DateTime(),
            'extra' => array(),
        );

        $expectedFormatter = new LineFormatter(null, null, true, true);
        $expected = $expectedFormatter->format($record);

        $handlerFormatter = $this->handler->getFormatter();
        $actual = $handlerFormatter->format($record);

        $this->assertEquals($expected, $actual, 'Empty context and extra arrays should not be rendered');
    }

    /**
     * @covers ::__construct
     */
    public function testConnectionStringisConstructedCorrectly()
    {
        $this->assertEquals('ssl://' . FleepHookHandler::FLEEP_HOST . ':443', $this->handler->getConnectionString());
    }
}
vendor/monolog/monolog/tests/Monolog/Handler/BufferHandlerTest.php000064400000012404151327705700021406 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog\Handler;

use WPvividMonolog\TestCase;
use WPvividMonolog\Logger;

class BufferHandlerTest extends TestCase
{
    private $shutdownCheckHandler;

    /**
     * @covers Monolog\Handler\BufferHandler::__construct
     * @covers Monolog\Handler\BufferHandler::handle
     * @covers Monolog\Handler\BufferHandler::close
     */
    public function testHandleBuffers()
    {
        $test = new TestHandler();
        $handler = new BufferHandler($test);
        $handler->handle($this->getRecord(Logger::DEBUG));
        $handler->handle($this->getRecord(Logger::INFO));
        $this->assertFalse($test->hasDebugRecords());
        $this->assertFalse($test->hasInfoRecords());
        $handler->close();
        $this->assertTrue($test->hasInfoRecords());
        $this->assertTrue(count($test->getRecords()) === 2);
    }

    /**
     * @covers Monolog\Handler\BufferHandler::close
     * @covers Monolog\Handler\BufferHandler::flush
     */
    public function testPropagatesRecordsAtEndOfRequest()
    {
        $test = new TestHandler();
        $handler = new BufferHandler($test);
        $handler->handle($this->getRecord(Logger::WARNING));
        $handler->handle($this->getRecord(Logger::DEBUG));
        $this->shutdownCheckHandler = $test;
        register_shutdown_function(array($this, 'checkPropagation'));
    }

    public function checkPropagation()
    {
        if (!$this->shutdownCheckHandler->hasWarningRecords() || !$this->shutdownCheckHandler->hasDebugRecords()) {
            echo '!!! BufferHandlerTest::testPropagatesRecordsAtEndOfRequest failed to verify that the messages have been propagated' . PHP_EOL;
            exit(1);
        }
    }

    /**
     * @covers Monolog\Handler\BufferHandler::handle
     */
    public function testHandleBufferLimit()
    {
        $test = new TestHandler();
        $handler = new BufferHandler($test, 2);
        $handler->handle($this->getRecord(Logger::DEBUG));
        $handler->handle($this->getRecord(Logger::DEBUG));
        $handler->handle($this->getRecord(Logger::INFO));
        $handler->handle($this->getRecord(Logger::WARNING));
        $handler->close();
        $this->assertTrue($test->hasWarningRecords());
        $this->assertTrue($test->hasInfoRecords());
        $this->assertFalse($test->hasDebugRecords());
    }

    /**
     * @covers Monolog\Handler\BufferHandler::handle
     */
    public function testHandleBufferLimitWithFlushOnOverflow()
    {
        $test = new TestHandler();
        $handler = new BufferHandler($test, 3, Logger::DEBUG, true, true);

        // send two records
        $handler->handle($this->getRecord(Logger::DEBUG));
        $handler->handle($this->getRecord(Logger::DEBUG));
        $handler->handle($this->getRecord(Logger::DEBUG));
        $this->assertFalse($test->hasDebugRecords());
        $this->assertCount(0, $test->getRecords());

        // overflow
        $handler->handle($this->getRecord(Logger::INFO));
        $this->assertTrue($test->hasDebugRecords());
        $this->assertCount(3, $test->getRecords());

        // should buffer again
        $handler->handle($this->getRecord(Logger::WARNING));
        $this->assertCount(3, $test->getRecords());

        $handler->close();
        $this->assertCount(5, $test->getRecords());
        $this->assertTrue($test->hasWarningRecords());
        $this->assertTrue($test->hasInfoRecords());
    }

    /**
     * @covers Monolog\Handler\BufferHandler::handle
     */
    public function testHandleLevel()
    {
        $test = new TestHandler();
        $handler = new BufferHandler($test, 0, Logger::INFO);
        $handler->handle($this->getRecord(Logger::DEBUG));
        $handler->handle($this->getRecord(Logger::INFO));
        $handler->handle($this->getRecord(Logger::WARNING));
        $handler->handle($this->getRecord(Logger::DEBUG));
        $handler->close();
        $this->assertTrue($test->hasWarningRecords());
        $this->assertTrue($test->hasInfoRecords());
        $this->assertFalse($test->hasDebugRecords());
    }

    /**
     * @covers Monolog\Handler\BufferHandler::flush
     */
    public function testFlush()
    {
        $test = new TestHandler();
        $handler = new BufferHandler($test, 0);
        $handler->handle($this->getRecord(Logger::DEBUG));
        $handler->handle($this->getRecord(Logger::INFO));
        $handler->flush();
        $this->assertTrue($test->hasInfoRecords());
        $this->assertTrue($test->hasDebugRecords());
        $this->assertFalse($test->hasWarningRecords());
    }

    /**
     * @covers Monolog\Handler\BufferHandler::handle
     */
    public function testHandleUsesProcessors()
    {
        $test = new TestHandler();
        $handler = new BufferHandler($test);
        $handler->pushProcessor(function ($record) {
            $record['extra']['foo'] = true;

            return $record;
        });
        $handler->handle($this->getRecord(Logger::WARNING));
        $handler->flush();
        $this->assertTrue($test->hasWarningRecords());
        $records = $test->getRecords();
        $this->assertTrue($records[0]['extra']['foo']);
    }
}
vendor/monolog/monolog/tests/Monolog/Handler/ErrorLogHandlerTest.php000064400000004177151327705700021740 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog\Handler;

use WPvividMonolog\TestCase;
use WPvividMonolog\Logger;
use WPvividMonolog\Formatter\LineFormatter;

function error_log()
{
    $GLOBALS['error_log'][] = func_get_args();
}

class ErrorLogHandlerTest extends TestCase
{
    protected function setUp()
    {
        $GLOBALS['error_log'] = array();
    }

    /**
     * @covers Monolog\Handler\ErrorLogHandler::__construct
     * @expectedException InvalidArgumentException
     * @expectedExceptionMessage The given message type "42" is not supported
     */
    public function testShouldNotAcceptAnInvalidTypeOnContructor()
    {
        new ErrorLogHandler(42);
    }

    /**
     * @covers Monolog\Handler\ErrorLogHandler::write
     */
    public function testShouldLogMessagesUsingErrorLogFuncion()
    {
        $type = ErrorLogHandler::OPERATING_SYSTEM;
        $handler = new ErrorLogHandler($type);
        $handler->setFormatter(new LineFormatter('%channel%.%level_name%: %message% %context% %extra%', null, true));
        $handler->handle($this->getRecord(Logger::ERROR, "Foo\nBar\r\n\r\nBaz"));

        $this->assertSame("test.ERROR: Foo\nBar\r\n\r\nBaz [] []", $GLOBALS['error_log'][0][0]);
        $this->assertSame($GLOBALS['error_log'][0][1], $type);

        $handler = new ErrorLogHandler($type, Logger::DEBUG, true, true);
        $handler->setFormatter(new LineFormatter(null, null, true));
        $handler->handle($this->getRecord(Logger::ERROR, "Foo\nBar\r\n\r\nBaz"));

        $this->assertStringMatchesFormat('[%s] test.ERROR: Foo', $GLOBALS['error_log'][1][0]);
        $this->assertSame($GLOBALS['error_log'][1][1], $type);

        $this->assertStringMatchesFormat('Bar', $GLOBALS['error_log'][2][0]);
        $this->assertSame($GLOBALS['error_log'][2][1], $type);

        $this->assertStringMatchesFormat('Baz [] []', $GLOBALS['error_log'][3][0]);
        $this->assertSame($GLOBALS['error_log'][3][1], $type);
    }
}
vendor/monolog/monolog/tests/Monolog/Handler/SlackbotHandlerTest.php000064400000002244151327705700021740 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog\Handler;

use WPvividMonolog\TestCase;
use WPvividMonolog\Logger;

/**
 * @author Haralan Dobrev <hkdobrev@gmail.com>
 * @see    https://slack.com/apps/A0F81R8ET-slackbot
 * @coversDefaultClass Monolog\Handler\SlackbotHandler
 */
class SlackbotHandlerTest extends TestCase
{
    /**
     * @covers ::__construct
     */
    public function testConstructorMinimal()
    {
        $handler = new SlackbotHandler('test-team', 'test-token', 'test-channel');
        $this->assertInstanceOf('WPvividMonolog\Handler\AbstractProcessingHandler', $handler);
    }

    /**
     * @covers ::__construct
     */
    public function testConstructorFull()
    {
        $handler = new SlackbotHandler(
            'test-team',
            'test-token',
            'test-channel',
            Logger::DEBUG,
            false
        );
        $this->assertInstanceOf('WPvividMonolog\Handler\AbstractProcessingHandler', $handler);
    }
}
vendor/monolog/monolog/tests/Monolog/Handler/SwiftMailerHandlerTest.php000064400000007423151327705700022430 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog\Handler;

use WPvividMonolog\Logger;
use WPvividMonolog\TestCase;

class SwiftMailerHandlerTest extends TestCase
{
    /** @var \Swift_Mailer|\PHPUnit_Framework_MockObject_MockObject */
    private $mailer;

    public function setUp()
    {
        $this->mailer = $this
            ->getMockBuilder('Swift_Mailer')
            ->disableOriginalConstructor()
            ->getMock();
    }

    public function testMessageCreationIsLazyWhenUsingCallback()
    {
        $this->mailer->expects($this->never())
            ->method('send');

        $callback = function () {
            throw new \RuntimeException('Swift_Message creation callback should not have been called in this test');
        };
        $handler = new SwiftMailerHandler($this->mailer, $callback);

        $records = array(
            $this->getRecord(Logger::DEBUG),
            $this->getRecord(Logger::INFO),
        );
        $handler->handleBatch($records);
    }

    public function testMessageCanBeCustomizedGivenLoggedData()
    {
        // Wire Mailer to expect a specific Swift_Message with a customized Subject
        $expectedMessage = new \Swift_Message();
        $this->mailer->expects($this->once())
            ->method('send')
            ->with($this->callback(function ($value) use ($expectedMessage) {
                return $value instanceof \Swift_Message
                    && $value->getSubject() === 'Emergency'
                    && $value === $expectedMessage;
            }));

        // Callback dynamically changes subject based on number of logged records
        $callback = function ($content, array $records) use ($expectedMessage) {
            $subject = count($records) > 0 ? 'Emergency' : 'Normal';
            $expectedMessage->setSubject($subject);

            return $expectedMessage;
        };
        $handler = new SwiftMailerHandler($this->mailer, $callback);

        // Logging 1 record makes this an Emergency
        $records = array(
            $this->getRecord(Logger::EMERGENCY),
        );
        $handler->handleBatch($records);
    }

    public function testMessageSubjectFormatting()
    {
        // Wire Mailer to expect a specific Swift_Message with a customized Subject
        $messageTemplate = new \Swift_Message();
        $messageTemplate->setSubject('Alert: %level_name% %message%');
        $receivedMessage = null;

        $this->mailer->expects($this->once())
            ->method('send')
            ->with($this->callback(function ($value) use (&$receivedMessage) {
                $receivedMessage = $value;
                return true;
            }));

        $handler = new SwiftMailerHandler($this->mailer, $messageTemplate);

        $records = array(
            $this->getRecord(Logger::EMERGENCY),
        );
        $handler->handleBatch($records);

        $this->assertEquals('Alert: EMERGENCY test', $receivedMessage->getSubject());
    }

    public function testMessageHaveUniqueId()
    {
        $messageTemplate = new \Swift_Message();
        $handler = new SwiftMailerHandler($this->mailer, $messageTemplate);

        $method = new \ReflectionMethod('WPvividMonolog\Handler\SwiftMailerHandler', 'buildMessage');
        $method->setAccessible(true);
        $method->invokeArgs($handler, array($messageTemplate, array()));

        $builtMessage1 = $method->invoke($handler, $messageTemplate, array());
        $builtMessage2 = $method->invoke($handler, $messageTemplate, array());

        $this->assertFalse($builtMessage1->getId() === $builtMessage2->getId(), 'Two different messages have the same id');
    }
}
vendor/monolog/monolog/tests/Monolog/Handler/RavenHandlerTest.php000064400000021412151327705700021247 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog\Handler;

use WPvividMonolog\TestCase;
use WPvividMonolog\Logger;
use WPvividMonolog\Formatter\LineFormatter;

class RavenHandlerTest extends TestCase
{
    public function setUp()
    {
        if (!class_exists('Raven_Client')) {
            $this->markTestSkipped('raven/raven not installed');
        }

        require_once __DIR__ . '/MockRavenClient.php';
    }

    /**
     * @covers Monolog\Handler\RavenHandler::__construct
     */
    public function testConstruct()
    {
        $handler = new RavenHandler($this->getRavenClient());
        $this->assertInstanceOf('WPvividMonolog\Handler\RavenHandler', $handler);
    }

    protected function getHandler($ravenClient)
    {
        $handler = new RavenHandler($ravenClient);

        return $handler;
    }

    protected function getRavenClient()
    {
        $dsn = 'http://43f6017361224d098402974103bfc53d:a6a0538fc2934ba2bed32e08741b2cd3@marca.python.live.cheggnet.com:9000/1';

        return new MockRavenClient($dsn);
    }

    public function testDebug()
    {
        $ravenClient = $this->getRavenClient();
        $handler = $this->getHandler($ravenClient);

        $record = $this->getRecord(Logger::DEBUG, 'A test debug message');
        $handler->handle($record);

        $this->assertEquals($ravenClient::DEBUG, $ravenClient->lastData['level']);
        $this->assertContains($record['message'], $ravenClient->lastData['message']);
    }

    public function testWarning()
    {
        $ravenClient = $this->getRavenClient();
        $handler = $this->getHandler($ravenClient);

        $record = $this->getRecord(Logger::WARNING, 'A test warning message');
        $handler->handle($record);

        $this->assertEquals($ravenClient::WARNING, $ravenClient->lastData['level']);
        $this->assertContains($record['message'], $ravenClient->lastData['message']);
    }

    public function testTag()
    {
        $ravenClient = $this->getRavenClient();
        $handler = $this->getHandler($ravenClient);

        $tags = array(1, 2, 'foo');
        $record = $this->getRecord(Logger::INFO, 'test', array('tags' => $tags));
        $handler->handle($record);

        $this->assertEquals($tags, $ravenClient->lastData['tags']);
    }

    public function testExtraParameters()
    {
        $ravenClient = $this->getRavenClient();
        $handler = $this->getHandler($ravenClient);

        $checksum = '098f6bcd4621d373cade4e832627b4f6';
        $release = '05a671c66aefea124cc08b76ea6d30bb';
        $eventId = '31423';
        $record = $this->getRecord(Logger::INFO, 'test', array('checksum' => $checksum, 'release' => $release, 'event_id' => $eventId));
        $handler->handle($record);

        $this->assertEquals($checksum, $ravenClient->lastData['checksum']);
        $this->assertEquals($release, $ravenClient->lastData['release']);
        $this->assertEquals($eventId, $ravenClient->lastData['event_id']);
    }

    public function testFingerprint()
    {
        $ravenClient = $this->getRavenClient();
        $handler = $this->getHandler($ravenClient);

        $fingerprint = array('{{ default }}', 'other value');
        $record = $this->getRecord(Logger::INFO, 'test', array('fingerprint' => $fingerprint));
        $handler->handle($record);

        $this->assertEquals($fingerprint, $ravenClient->lastData['fingerprint']);
    }

    public function testUserContext()
    {
        $ravenClient = $this->getRavenClient();
        $handler = $this->getHandler($ravenClient);

        $recordWithNoContext = $this->getRecord(Logger::INFO, 'test with default user context');
        // set user context 'externally'

        $user = array(
            'id' => '123',
            'email' => 'test@test.com',
        );

        $recordWithContext = $this->getRecord(Logger::INFO, 'test', array('user' => $user));

        $ravenClient->user_context(array('id' => 'test_user_id'));
        // handle context
        $handler->handle($recordWithContext);
        $this->assertEquals($user, $ravenClient->lastData['user']);

        // check to see if its reset
        $handler->handle($recordWithNoContext);
        $this->assertInternalType('array', $ravenClient->context->user);
        $this->assertSame('test_user_id', $ravenClient->context->user['id']);

        // handle with null context
        $ravenClient->user_context(null);
        $handler->handle($recordWithContext);
        $this->assertEquals($user, $ravenClient->lastData['user']);

        // check to see if its reset
        $handler->handle($recordWithNoContext);
        $this->assertNull($ravenClient->context->user);
    }

    public function testException()
    {
        $ravenClient = $this->getRavenClient();
        $handler = $this->getHandler($ravenClient);

        try {
            $this->methodThatThrowsAnException();
        } catch (\Exception $e) {
            $record = $this->getRecord(Logger::ERROR, $e->getMessage(), array('exception' => $e));
            $handler->handle($record);
        }

        $this->assertEquals($record['message'], $ravenClient->lastData['message']);
    }

    public function testHandleBatch()
    {
        $records = $this->getMultipleRecords();
        $records[] = $this->getRecord(Logger::WARNING, 'warning');
        $records[] = $this->getRecord(Logger::WARNING, 'warning');

        $logFormatter = $this->getMock('WPvividMonolog\\Formatter\\FormatterInterface');
        $logFormatter->expects($this->once())->method('formatBatch');

        $formatter = $this->getMock('WPvividMonolog\\Formatter\\FormatterInterface');
        $formatter->expects($this->once())->method('format')->with($this->callback(function ($record) {
            return $record['level'] == 400;
        }));

        $handler = $this->getHandler($this->getRavenClient());
        $handler->setBatchFormatter($logFormatter);
        $handler->setFormatter($formatter);
        $handler->handleBatch($records);
    }

    public function testHandleBatchDoNothingIfRecordsAreBelowLevel()
    {
        $records = array(
            $this->getRecord(Logger::DEBUG, 'debug message 1'),
            $this->getRecord(Logger::DEBUG, 'debug message 2'),
            $this->getRecord(Logger::INFO, 'information'),
        );

        $handler = $this->getMock('WPvividMonolog\Handler\RavenHandler', null, array($this->getRavenClient()));
        $handler->expects($this->never())->method('handle');
        $handler->setLevel(Logger::ERROR);
        $handler->handleBatch($records);
    }

    public function testHandleBatchPicksProperMessage()
    {
        $records = array(
            $this->getRecord(Logger::DEBUG, 'debug message 1'),
            $this->getRecord(Logger::DEBUG, 'debug message 2'),
            $this->getRecord(Logger::INFO, 'information 1'),
            $this->getRecord(Logger::ERROR, 'error 1'),
            $this->getRecord(Logger::WARNING, 'warning'),
            $this->getRecord(Logger::ERROR, 'error 2'),
            $this->getRecord(Logger::INFO, 'information 2'),
        );

        $logFormatter = $this->getMock('WPvividMonolog\\Formatter\\FormatterInterface');
        $logFormatter->expects($this->once())->method('formatBatch');

        $formatter = $this->getMock('WPvividMonolog\\Formatter\\FormatterInterface');
        $formatter->expects($this->once())->method('format')->with($this->callback(function ($record) use ($records) {
            return $record['message'] == 'error 1';
        }));

        $handler = $this->getHandler($this->getRavenClient());
        $handler->setBatchFormatter($logFormatter);
        $handler->setFormatter($formatter);
        $handler->handleBatch($records);
    }

    public function testGetSetBatchFormatter()
    {
        $ravenClient = $this->getRavenClient();
        $handler = $this->getHandler($ravenClient);

        $handler->setBatchFormatter($formatter = new LineFormatter());
        $this->assertSame($formatter, $handler->getBatchFormatter());
    }

    public function testRelease()
    {
        $ravenClient = $this->getRavenClient();
        $handler = $this->getHandler($ravenClient);
        $release = 'v42.42.42';
        $handler->setRelease($release);
        $record = $this->getRecord(Logger::INFO, 'test');
        $handler->handle($record);
        $this->assertEquals($release, $ravenClient->lastData['release']);

        $localRelease = 'v41.41.41';
        $record = $this->getRecord(Logger::INFO, 'test', array('release' => $localRelease));
        $handler->handle($record);
        $this->assertEquals($localRelease, $ravenClient->lastData['release']);
    }

    private function methodThatThrowsAnException()
    {
        throw new \Exception('This is an exception');
    }
}
vendor/monolog/monolog/tests/Monolog/Handler/HandlerWrapperTest.php000064400000006410151327705700021615 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog\Handler;

use WPvividMonolog\TestCase;

/**
 * @author Alexey Karapetov <alexey@karapetov.com>
 */
class HandlerWrapperTest extends TestCase
{
    /**
     * @var HandlerWrapper
     */
    private $wrapper;

    private $handler;

    public function setUp()
    {
        parent::setUp();
        $this->handler = $this->getMock('WPvividMonolog\\Handler\\HandlerInterface');
        $this->wrapper = new HandlerWrapper($this->handler);
    }

    /**
     * @return array
     */
    public function trueFalseDataProvider()
    {
        return array(
            array(true),
            array(false),
        );
    }

    /**
     * @param $result
     * @dataProvider trueFalseDataProvider
     */
    public function testIsHandling($result)
    {
        $record = $this->getRecord();
        $this->handler->expects($this->once())
            ->method('isHandling')
            ->with($record)
            ->willReturn($result);

        $this->assertEquals($result, $this->wrapper->isHandling($record));
    }

    /**
     * @param $result
     * @dataProvider trueFalseDataProvider
     */
    public function testHandle($result)
    {
        $record = $this->getRecord();
        $this->handler->expects($this->once())
            ->method('handle')
            ->with($record)
            ->willReturn($result);

        $this->assertEquals($result, $this->wrapper->handle($record));
    }

    /**
     * @param $result
     * @dataProvider trueFalseDataProvider
     */
    public function testHandleBatch($result)
    {
        $records = $this->getMultipleRecords();
        $this->handler->expects($this->once())
            ->method('handleBatch')
            ->with($records)
            ->willReturn($result);

        $this->assertEquals($result, $this->wrapper->handleBatch($records));
    }

    public function testPushProcessor()
    {
        $processor = function () {};
        $this->handler->expects($this->once())
            ->method('pushProcessor')
            ->with($processor);

        $this->assertEquals($this->wrapper, $this->wrapper->pushProcessor($processor));
    }

    public function testPopProcessor()
    {
        $processor = function () {};
        $this->handler->expects($this->once())
            ->method('popProcessor')
            ->willReturn($processor);

        $this->assertEquals($processor, $this->wrapper->popProcessor());
    }

    public function testSetFormatter()
    {
        $formatter = $this->getMock('WPvividMonolog\\Formatter\\FormatterInterface');
        $this->handler->expects($this->once())
            ->method('setFormatter')
            ->with($formatter);

        $this->assertEquals($this->wrapper, $this->wrapper->setFormatter($formatter));
    }

    public function testGetFormatter()
    {
        $formatter = $this->getMock('WPvividMonolog\\Formatter\\FormatterInterface');
        $this->handler->expects($this->once())
            ->method('getFormatter')
            ->willReturn($formatter);

        $this->assertEquals($formatter, $this->wrapper->getFormatter());
    }
}
vendor/monolog/monolog/tests/Monolog/Handler/UdpSocketTest.php000064400000003250151327705700020577 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog\Handler;

use WPvividMonolog\TestCase;
use WPvividMonolog\Handler\SyslogUdp\UdpSocket;

/**
 * @requires extension sockets
 */
class UdpSocketTest extends TestCase
{
    public function testWeDoNotTruncateShortMessages()
    {
        $socket = $this->getMock('\WPvividMonolog\Handler\SyslogUdp\UdpSocket', array('send'), array('lol', 'lol'));

        $socket->expects($this->at(0))
            ->method('send')
            ->with("HEADER: The quick brown fox jumps over the lazy dog");

        $socket->write("The quick brown fox jumps over the lazy dog", "HEADER: ");
    }

    public function testLongMessagesAreTruncated()
    {
        $socket = $this->getMock('\WPvividMonolog\Handler\SyslogUdp\UdpSocket', array('send'), array('lol', 'lol'));

        $truncatedString = str_repeat("derp", 16254).'d';

        $socket->expects($this->exactly(1))
            ->method('send')
            ->with("HEADER" . $truncatedString);

        $longString = str_repeat("derp", 20000);

        $socket->write($longString, "HEADER");
    }

    public function testDoubleCloseDoesNotError()
    {
        $socket = new UdpSocket('127.0.0.1', 514);
        $socket->close();
        $socket->close();
    }

    /**
     * @expectedException LogicException
     */
    public function testWriteAfterCloseErrors()
    {
        $socket = new UdpSocket('127.0.0.1', 514);
        $socket->close();
        $socket->write('foo', "HEADER");
    }
}
vendor/monolog/monolog/tests/Monolog/Handler/GelfMockMessagePublisher.php000064400000000773151327705700022717 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog\Handler;

use Gelf\MessagePublisher;
use Gelf\Message;

class GelfMockMessagePublisher extends MessagePublisher
{
    public function publish(Message $message)
    {
        $this->lastMessage = $message;
    }

    public $lastMessage = null;
}
vendor/monolog/monolog/tests/Monolog/Handler/AbstractProcessingHandlerTest.php000064400000005237151327705700024003 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog\Handler;

use WPvividMonolog\TestCase;
use WPvividMonolog\Logger;
use WPvividMonolog\Processor\WebProcessor;

class AbstractProcessingHandlerTest extends TestCase
{
    /**
     * @covers Monolog\Handler\AbstractProcessingHandler::handle
     */
    public function testHandleLowerLevelMessage()
    {
        $handler = $this->getMockForAbstractClass('WPvividMonolog\Handler\AbstractProcessingHandler', array(Logger::WARNING, true));
        $this->assertFalse($handler->handle($this->getRecord(Logger::DEBUG)));
    }

    /**
     * @covers Monolog\Handler\AbstractProcessingHandler::handle
     */
    public function testHandleBubbling()
    {
        $handler = $this->getMockForAbstractClass('WPvividMonolog\Handler\AbstractProcessingHandler', array(Logger::DEBUG, true));
        $this->assertFalse($handler->handle($this->getRecord()));
    }

    /**
     * @covers Monolog\Handler\AbstractProcessingHandler::handle
     */
    public function testHandleNotBubbling()
    {
        $handler = $this->getMockForAbstractClass('WPvividMonolog\Handler\AbstractProcessingHandler', array(Logger::DEBUG, false));
        $this->assertTrue($handler->handle($this->getRecord()));
    }

    /**
     * @covers Monolog\Handler\AbstractProcessingHandler::handle
     */
    public function testHandleIsFalseWhenNotHandled()
    {
        $handler = $this->getMockForAbstractClass('WPvividMonolog\Handler\AbstractProcessingHandler', array(Logger::WARNING, false));
        $this->assertTrue($handler->handle($this->getRecord()));
        $this->assertFalse($handler->handle($this->getRecord(Logger::DEBUG)));
    }

    /**
     * @covers Monolog\Handler\AbstractProcessingHandler::processRecord
     */
    public function testProcessRecord()
    {
        $handler = $this->getMockForAbstractClass('WPvividMonolog\Handler\AbstractProcessingHandler');
        $handler->pushProcessor(new WebProcessor(array(
            'REQUEST_URI' => '',
            'REQUEST_METHOD' => '',
            'REMOTE_ADDR' => '',
            'SERVER_NAME' => '',
            'UNIQUE_ID' => '',
        )));
        $handledRecord = null;
        $handler->expects($this->once())
            ->method('write')
            ->will($this->returnCallback(function ($record) use (&$handledRecord) {
                $handledRecord = $record;
            }))
        ;
        $handler->handle($this->getRecord());
        $this->assertEquals(6, count($handledRecord['extra']));
    }
}
vendor/monolog/monolog/tests/Monolog/Handler/Fixtures/.gitkeep000064400000000000151327705700020555 0ustar00vendor/monolog/monolog/tests/Monolog/Handler/PushoverHandlerTest.php000064400000012317151327705700022013 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog\Handler;

use WPvividMonolog\TestCase;
use WPvividMonolog\Logger;

/**
 * Almost all examples (expected header, titles, messages) taken from
 * https://www.pushover.net/api
 * @author Sebastian Göttschkes <sebastian.goettschkes@googlemail.com>
 * @see https://www.pushover.net/api
 */
class PushoverHandlerTest extends TestCase
{
    private $res;
    private $handler;

    public function testWriteHeader()
    {
        $this->createHandler();
        $this->handler->setHighPriorityLevel(Logger::EMERGENCY); // skip priority notifications
        $this->handler->handle($this->getRecord(Logger::CRITICAL, 'test1'));
        fseek($this->res, 0);
        $content = fread($this->res, 1024);

        $this->assertRegexp('/POST \/1\/messages.json HTTP\/1.1\\r\\nHost: api.pushover.net\\r\\nContent-Type: application\/x-www-form-urlencoded\\r\\nContent-Length: \d{2,4}\\r\\n\\r\\n/', $content);

        return $content;
    }

    /**
     * @depends testWriteHeader
     */
    public function testWriteContent($content)
    {
        $this->assertRegexp('/token=myToken&user=myUser&message=test1&title=WPvividMonolog&timestamp=\d{10}$/', $content);
    }

    public function testWriteWithComplexTitle()
    {
        $this->createHandler('myToken', 'myUser', 'Backup finished - SQL1');
        $this->handler->handle($this->getRecord(Logger::CRITICAL, 'test1'));
        fseek($this->res, 0);
        $content = fread($this->res, 1024);

        $this->assertRegexp('/title=Backup\+finished\+-\+SQL1/', $content);
    }

    public function testWriteWithComplexMessage()
    {
        $this->createHandler();
        $this->handler->setHighPriorityLevel(Logger::EMERGENCY); // skip priority notifications
        $this->handler->handle($this->getRecord(Logger::CRITICAL, 'Backup of database "example" finished in 16 minutes.'));
        fseek($this->res, 0);
        $content = fread($this->res, 1024);

        $this->assertRegexp('/message=Backup\+of\+database\+%22example%22\+finished\+in\+16\+minutes\./', $content);
    }

    public function testWriteWithTooLongMessage()
    {
        $message = str_pad('test', 520, 'a');
        $this->createHandler();
        $this->handler->setHighPriorityLevel(Logger::EMERGENCY); // skip priority notifications
        $this->handler->handle($this->getRecord(Logger::CRITICAL, $message));
        fseek($this->res, 0);
        $content = fread($this->res, 1024);

        $expectedMessage = substr($message, 0, 505);

        $this->assertRegexp('/message=' . $expectedMessage . '&title/', $content);
    }

    public function testWriteWithHighPriority()
    {
        $this->createHandler();
        $this->handler->handle($this->getRecord(Logger::CRITICAL, 'test1'));
        fseek($this->res, 0);
        $content = fread($this->res, 1024);

        $this->assertRegexp('/token=myToken&user=myUser&message=test1&title=WPvividMonolog&timestamp=\d{10}&priority=1$/', $content);
    }

    public function testWriteWithEmergencyPriority()
    {
        $this->createHandler();
        $this->handler->handle($this->getRecord(Logger::EMERGENCY, 'test1'));
        fseek($this->res, 0);
        $content = fread($this->res, 1024);

        $this->assertRegexp('/token=myToken&user=myUser&message=test1&title=WPvividMonolog&timestamp=\d{10}&priority=2&retry=30&expire=25200$/', $content);
    }

    public function testWriteToMultipleUsers()
    {
        $this->createHandler('myToken', array('userA', 'userB'));
        $this->handler->handle($this->getRecord(Logger::EMERGENCY, 'test1'));
        fseek($this->res, 0);
        $content = fread($this->res, 1024);

        $this->assertRegexp('/token=myToken&user=userA&message=test1&title=WPvividMonolog&timestamp=\d{10}&priority=2&retry=30&expire=25200POST/', $content);
        $this->assertRegexp('/token=myToken&user=userB&message=test1&title=WPvividMonolog&timestamp=\d{10}&priority=2&retry=30&expire=25200$/', $content);
    }

    private function createHandler($token = 'myToken', $user = 'myUser', $title = 'WPvividMonolog')
    {
        $constructorArgs = array($token, $user, $title);
        $this->res = fopen('php://memory', 'a');
        $this->handler = $this->getMock(
            '\WPvividMonolog\Handler\PushoverHandler',
            array('fsockopen', 'streamSetTimeout', 'closeSocket'),
            $constructorArgs
        );

        $reflectionProperty = new \ReflectionProperty('\WPvividMonolog\Handler\SocketHandler', 'connectionString');
        $reflectionProperty->setAccessible(true);
        $reflectionProperty->setValue($this->handler, 'localhost:1234');

        $this->handler->expects($this->any())
            ->method('fsockopen')
            ->will($this->returnValue($this->res));
        $this->handler->expects($this->any())
            ->method('streamSetTimeout')
            ->will($this->returnValue(true));
        $this->handler->expects($this->any())
            ->method('closeSocket')
            ->will($this->returnValue(true));

        $this->handler->setFormatter($this->getIdentityFormatter());
    }
}
vendor/monolog/monolog/tests/Monolog/Handler/StreamHandlerTest.php000064400000013517151327705700021436 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog\Handler;

use WPvividMonolog\TestCase;
use WPvividMonolog\Logger;

class StreamHandlerTest extends TestCase
{
    /**
     * @covers Monolog\Handler\StreamHandler::__construct
     * @covers Monolog\Handler\StreamHandler::write
     */
    public function testWrite()
    {
        $handle = fopen('php://memory', 'a+');
        $handler = new StreamHandler($handle);
        $handler->setFormatter($this->getIdentityFormatter());
        $handler->handle($this->getRecord(Logger::WARNING, 'test'));
        $handler->handle($this->getRecord(Logger::WARNING, 'test2'));
        $handler->handle($this->getRecord(Logger::WARNING, 'test3'));
        fseek($handle, 0);
        $this->assertEquals('testtest2test3', fread($handle, 100));
    }

    /**
     * @covers Monolog\Handler\StreamHandler::close
     */
    public function testCloseKeepsExternalHandlersOpen()
    {
        $handle = fopen('php://memory', 'a+');
        $handler = new StreamHandler($handle);
        $this->assertTrue(is_resource($handle));
        $handler->close();
        $this->assertTrue(is_resource($handle));
    }

    /**
     * @covers Monolog\Handler\StreamHandler::close
     */
    public function testClose()
    {
        $handler = new StreamHandler('php://memory');
        $handler->handle($this->getRecord(Logger::WARNING, 'test'));
        $streamProp = new \ReflectionProperty('WPvividMonolog\Handler\StreamHandler', 'stream');
        $streamProp->setAccessible(true);
        $handle = $streamProp->getValue($handler);

        $this->assertTrue(is_resource($handle));
        $handler->close();
        $this->assertFalse(is_resource($handle));
    }

    /**
     * @covers Monolog\Handler\StreamHandler::write
     */
    public function testWriteCreatesTheStreamResource()
    {
        $handler = new StreamHandler('php://memory');
        $handler->handle($this->getRecord());
    }

    /**
     * @covers Monolog\Handler\StreamHandler::__construct
     * @covers Monolog\Handler\StreamHandler::write
     */
    public function testWriteLocking()
    {
        $temp = sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'monolog_locked_log';
        $handler = new StreamHandler($temp, Logger::DEBUG, true, null, true);
        $handler->handle($this->getRecord());
    }

    /**
     * @expectedException LogicException
     * @covers Monolog\Handler\StreamHandler::__construct
     * @covers Monolog\Handler\StreamHandler::write
     */
    public function testWriteMissingResource()
    {
        $handler = new StreamHandler(null);
        $handler->handle($this->getRecord());
    }

    public function invalidArgumentProvider()
    {
        return array(
            array(1),
            array(array()),
            array(array('bogus://url')),
        );
    }

    /**
     * @dataProvider invalidArgumentProvider
     * @expectedException InvalidArgumentException
     * @covers Monolog\Handler\StreamHandler::__construct
     */
    public function testWriteInvalidArgument($invalidArgument)
    {
        $handler = new StreamHandler($invalidArgument);
    }

    /**
     * @expectedException UnexpectedValueException
     * @covers Monolog\Handler\StreamHandler::__construct
     * @covers Monolog\Handler\StreamHandler::write
     */
    public function testWriteInvalidResource()
    {
        $handler = new StreamHandler('bogus://url');
        $handler->handle($this->getRecord());
    }

    /**
     * @expectedException UnexpectedValueException
     * @covers Monolog\Handler\StreamHandler::__construct
     * @covers Monolog\Handler\StreamHandler::write
     */
    public function testWriteNonExistingResource()
    {
        $handler = new StreamHandler('ftp://foo/bar/baz/'.rand(0, 10000));
        $handler->handle($this->getRecord());
    }

    /**
     * @covers Monolog\Handler\StreamHandler::__construct
     * @covers Monolog\Handler\StreamHandler::write
     */
    public function testWriteNonExistingPath()
    {
        $handler = new StreamHandler(sys_get_temp_dir().'/bar/'.rand(0, 10000).DIRECTORY_SEPARATOR.rand(0, 10000));
        $handler->handle($this->getRecord());
    }

    /**
     * @covers Monolog\Handler\StreamHandler::__construct
     * @covers Monolog\Handler\StreamHandler::write
     */
    public function testWriteNonExistingFileResource()
    {
        $handler = new StreamHandler('file://'.sys_get_temp_dir().'/bar/'.rand(0, 10000).DIRECTORY_SEPARATOR.rand(0, 10000));
        $handler->handle($this->getRecord());
    }

    /**
     * @expectedException Exception
     * @expectedExceptionMessageRegExp /There is no existing directory at/
     * @covers Monolog\Handler\StreamHandler::__construct
     * @covers Monolog\Handler\StreamHandler::write
     */
    public function testWriteNonExistingAndNotCreatablePath()
    {
        if (defined('PHP_WINDOWS_VERSION_BUILD')) {
            $this->markTestSkipped('Permissions checks can not run on windows');
        }
        $handler = new StreamHandler('/foo/bar/'.rand(0, 10000).DIRECTORY_SEPARATOR.rand(0, 10000));
        $handler->handle($this->getRecord());
    }

    /**
     * @expectedException Exception
     * @expectedExceptionMessageRegExp /There is no existing directory at/
     * @covers Monolog\Handler\StreamHandler::__construct
     * @covers Monolog\Handler\StreamHandler::write
     */
    public function testWriteNonExistingAndNotCreatableFileResource()
    {
        if (defined('PHP_WINDOWS_VERSION_BUILD')) {
            $this->markTestSkipped('Permissions checks can not run on windows');
        }
        $handler = new StreamHandler('file:///foo/bar/'.rand(0, 10000).DIRECTORY_SEPARATOR.rand(0, 10000));
        $handler->handle($this->getRecord());
    }
}
vendor/monolog/monolog/tests/Monolog/Handler/NewRelicHandlerTest.php000064400000013711151327705700021707 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog\Handler;

use WPvividMonolog\Formatter\LineFormatter;
use WPvividMonolog\TestCase;
use WPvividMonolog\Logger;

class NewRelicHandlerTest extends TestCase
{
    public static $appname;
    public static $customParameters;
    public static $transactionName;

    public function setUp()
    {
        self::$appname = null;
        self::$customParameters = array();
        self::$transactionName = null;
    }

    /**
     * @expectedException Monolog\Handler\MissingExtensionException
     */
    public function testThehandlerThrowsAnExceptionIfTheNRExtensionIsNotLoaded()
    {
        $handler = new StubNewRelicHandlerWithoutExtension();
        $handler->handle($this->getRecord(Logger::ERROR));
    }

    public function testThehandlerCanHandleTheRecord()
    {
        $handler = new StubNewRelicHandler();
        $handler->handle($this->getRecord(Logger::ERROR));
    }

    public function testThehandlerCanAddContextParamsToTheNewRelicTrace()
    {
        $handler = new StubNewRelicHandler();
        $handler->handle($this->getRecord(Logger::ERROR, 'log message', array('a' => 'b')));
        $this->assertEquals(array('context_a' => 'b'), self::$customParameters);
    }

    public function testThehandlerCanAddExplodedContextParamsToTheNewRelicTrace()
    {
        $handler = new StubNewRelicHandler(Logger::ERROR, true, self::$appname, true);
        $handler->handle($this->getRecord(
            Logger::ERROR,
            'log message',
            array('a' => array('key1' => 'value1', 'key2' => 'value2'))
        ));
        $this->assertEquals(
            array('context_a_key1' => 'value1', 'context_a_key2' => 'value2'),
            self::$customParameters
        );
    }

    public function testThehandlerCanAddExtraParamsToTheNewRelicTrace()
    {
        $record = $this->getRecord(Logger::ERROR, 'log message');
        $record['extra'] = array('c' => 'd');

        $handler = new StubNewRelicHandler();
        $handler->handle($record);

        $this->assertEquals(array('extra_c' => 'd'), self::$customParameters);
    }

    public function testThehandlerCanAddExplodedExtraParamsToTheNewRelicTrace()
    {
        $record = $this->getRecord(Logger::ERROR, 'log message');
        $record['extra'] = array('c' => array('key1' => 'value1', 'key2' => 'value2'));

        $handler = new StubNewRelicHandler(Logger::ERROR, true, self::$appname, true);
        $handler->handle($record);

        $this->assertEquals(
            array('extra_c_key1' => 'value1', 'extra_c_key2' => 'value2'),
            self::$customParameters
        );
    }

    public function testThehandlerCanAddExtraContextAndParamsToTheNewRelicTrace()
    {
        $record = $this->getRecord(Logger::ERROR, 'log message', array('a' => 'b'));
        $record['extra'] = array('c' => 'd');

        $handler = new StubNewRelicHandler();
        $handler->handle($record);

        $expected = array(
            'context_a' => 'b',
            'extra_c' => 'd',
        );

        $this->assertEquals($expected, self::$customParameters);
    }

    public function testThehandlerCanHandleTheRecordsFormattedUsingTheLineFormatter()
    {
        $handler = new StubNewRelicHandler();
        $handler->setFormatter(new LineFormatter());
        $handler->handle($this->getRecord(Logger::ERROR));
    }

    public function testTheAppNameIsNullByDefault()
    {
        $handler = new StubNewRelicHandler();
        $handler->handle($this->getRecord(Logger::ERROR, 'log message'));

        $this->assertEquals(null, self::$appname);
    }

    public function testTheAppNameCanBeInjectedFromtheConstructor()
    {
        $handler = new StubNewRelicHandler(Logger::DEBUG, false, 'myAppName');
        $handler->handle($this->getRecord(Logger::ERROR, 'log message'));

        $this->assertEquals('myAppName', self::$appname);
    }

    public function testTheAppNameCanBeOverriddenFromEachLog()
    {
        $handler = new StubNewRelicHandler(Logger::DEBUG, false, 'myAppName');
        $handler->handle($this->getRecord(Logger::ERROR, 'log message', array('appname' => 'logAppName')));

        $this->assertEquals('logAppName', self::$appname);
    }

    public function testTheTransactionNameIsNullByDefault()
    {
        $handler = new StubNewRelicHandler();
        $handler->handle($this->getRecord(Logger::ERROR, 'log message'));

        $this->assertEquals(null, self::$transactionName);
    }

    public function testTheTransactionNameCanBeInjectedFromTheConstructor()
    {
        $handler = new StubNewRelicHandler(Logger::DEBUG, false, null, false, 'myTransaction');
        $handler->handle($this->getRecord(Logger::ERROR, 'log message'));

        $this->assertEquals('myTransaction', self::$transactionName);
    }

    public function testTheTransactionNameCanBeOverriddenFromEachLog()
    {
        $handler = new StubNewRelicHandler(Logger::DEBUG, false, null, false, 'myTransaction');
        $handler->handle($this->getRecord(Logger::ERROR, 'log message', array('transaction_name' => 'logTransactName')));

        $this->assertEquals('logTransactName', self::$transactionName);
    }
}

class StubNewRelicHandlerWithoutExtension extends NewRelicHandler
{
    protected function isNewRelicEnabled()
    {
        return false;
    }
}

class StubNewRelicHandler extends NewRelicHandler
{
    protected function isNewRelicEnabled()
    {
        return true;
    }
}

function newrelic_notice_error()
{
    return true;
}

function newrelic_set_appname($appname)
{
    return NewRelicHandlerTest::$appname = $appname;
}

function newrelic_name_transaction($transactionName)
{
    return NewRelicHandlerTest::$transactionName = $transactionName;
}

function newrelic_add_custom_parameter($key, $value)
{
    NewRelicHandlerTest::$customParameters[$key] = $value;

    return true;
}
vendor/monolog/monolog/tests/Monolog/Handler/InsightOpsHandlerTest.php000064400000004562151327705700022272 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

 namespace WPvividMonolog\Handler;
 
 use WPvividMonolog\TestCase;
 use WPvividMonolog\Logger;

/**
 * @author Robert Kaufmann III <rok3@rok3.me>
 * @author Gabriel Machado <gabriel.ms1@hotmail.com>
 */
class InsightOpsHandlerTest extends TestCase
{
    /**
     * @var resource
     */
    private $resource;

    /**
     * @var LogEntriesHandler
     */
    private $handler;

    public function testWriteContent()
    {
        $this->createHandler();
        $this->handler->handle($this->getRecord(Logger::CRITICAL, 'Critical write test'));

        fseek($this->resource, 0);
        $content = fread($this->resource, 1024);

        $this->assertRegexp('/testToken \[\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\] test.CRITICAL: Critical write test/', $content);
    }

    public function testWriteBatchContent()
    {
        $this->createHandler();
        $this->handler->handleBatch($this->getMultipleRecords());

        fseek($this->resource, 0);
        $content = fread($this->resource, 1024);

        $this->assertRegexp('/(testToken \[\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\] .* \[\] \[\]\n){3}/', $content);
    }

    private function createHandler()
    {
        $useSSL = extension_loaded('openssl');
        $args = array('testToken', 'us', $useSSL, Logger::DEBUG, true);
        $this->resource = fopen('php://memory', 'a');
        $this->handler = $this->getMock(
            '\WPvividMonolog\Handler\InsightOpsHandler',
            array('fsockopen', 'streamSetTimeout', 'closeSocket'),
            $args
        );

        $reflectionProperty = new \ReflectionProperty('\WPvividMonolog\Handler\SocketHandler', 'connectionString');
        $reflectionProperty->setAccessible(true);
        $reflectionProperty->setValue($this->handler, 'localhost:1234');

        $this->handler->expects($this->any())
            ->method('fsockopen')
            ->will($this->returnValue($this->resource));
        $this->handler->expects($this->any())
            ->method('streamSetTimeout')
            ->will($this->returnValue(true));
        $this->handler->expects($this->any())
            ->method('closeSocket')
            ->will($this->returnValue(true));
    }
}
vendor/monolog/monolog/tests/Monolog/Handler/WhatFailureGroupHandlerTest.php000064400000011402151327705700023422 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog\Handler;

use WPvividMonolog\TestCase;
use WPvividMonolog\Logger;

class WhatFailureGroupHandlerTest extends TestCase
{
    /**
     * @covers Monolog\Handler\WhatFailureGroupHandler::__construct
     * @expectedException InvalidArgumentException
     */
    public function testConstructorOnlyTakesHandler()
    {
        new WhatFailureGroupHandler(array(new TestHandler(), "foo"));
    }

    /**
     * @covers Monolog\Handler\WhatFailureGroupHandler::__construct
     * @covers Monolog\Handler\WhatFailureGroupHandler::handle
     */
    public function testHandle()
    {
        $testHandlers = array(new TestHandler(), new TestHandler());
        $handler = new WhatFailureGroupHandler($testHandlers);
        $handler->handle($this->getRecord(Logger::DEBUG));
        $handler->handle($this->getRecord(Logger::INFO));
        foreach ($testHandlers as $test) {
            $this->assertTrue($test->hasDebugRecords());
            $this->assertTrue($test->hasInfoRecords());
            $this->assertTrue(count($test->getRecords()) === 2);
        }
    }

    /**
     * @covers Monolog\Handler\WhatFailureGroupHandler::handleBatch
     */
    public function testHandleBatch()
    {
        $testHandlers = array(new TestHandler(), new TestHandler());
        $handler = new WhatFailureGroupHandler($testHandlers);
        $handler->handleBatch(array($this->getRecord(Logger::DEBUG), $this->getRecord(Logger::INFO)));
        foreach ($testHandlers as $test) {
            $this->assertTrue($test->hasDebugRecords());
            $this->assertTrue($test->hasInfoRecords());
            $this->assertTrue(count($test->getRecords()) === 2);
        }
    }

    /**
     * @covers Monolog\Handler\WhatFailureGroupHandler::isHandling
     */
    public function testIsHandling()
    {
        $testHandlers = array(new TestHandler(Logger::ERROR), new TestHandler(Logger::WARNING));
        $handler = new WhatFailureGroupHandler($testHandlers);
        $this->assertTrue($handler->isHandling($this->getRecord(Logger::ERROR)));
        $this->assertTrue($handler->isHandling($this->getRecord(Logger::WARNING)));
        $this->assertFalse($handler->isHandling($this->getRecord(Logger::DEBUG)));
    }

    /**
     * @covers Monolog\Handler\WhatFailureGroupHandler::handle
     */
    public function testHandleUsesProcessors()
    {
        $test = new TestHandler();
        $handler = new WhatFailureGroupHandler(array($test));
        $handler->pushProcessor(function ($record) {
            $record['extra']['foo'] = true;

            return $record;
        });
        $handler->handle($this->getRecord(Logger::WARNING));
        $this->assertTrue($test->hasWarningRecords());
        $records = $test->getRecords();
        $this->assertTrue($records[0]['extra']['foo']);
    }

    /**
     * @covers Monolog\Handler\WhatFailureGroupHandler::handleBatch
     */
    public function testHandleBatchUsesProcessors()
    {
        $testHandlers = array(new TestHandler(), new TestHandler());
        $handler = new WhatFailureGroupHandler($testHandlers);
        $handler->pushProcessor(function ($record) {
            $record['extra']['foo'] = true;

            return $record;
        });
        $handler->handleBatch(array($this->getRecord(Logger::DEBUG), $this->getRecord(Logger::INFO)));
        foreach ($testHandlers as $test) {
            $this->assertTrue($test->hasDebugRecords());
            $this->assertTrue($test->hasInfoRecords());
            $this->assertTrue(count($test->getRecords()) === 2);
            $records = $test->getRecords();
            $this->assertTrue($records[0]['extra']['foo']);
            $this->assertTrue($records[1]['extra']['foo']);
        }
    }

    /**
     * @covers Monolog\Handler\WhatFailureGroupHandler::handle
     */
    public function testHandleException()
    {
        $test = new TestHandler();
        $exception = new ExceptionTestHandler();
        $handler = new WhatFailureGroupHandler(array($exception, $test, $exception));
        $handler->pushProcessor(function ($record) {
            $record['extra']['foo'] = true;

            return $record;
        });
        $handler->handle($this->getRecord(Logger::WARNING));
        $this->assertTrue($test->hasWarningRecords());
        $records = $test->getRecords();
        $this->assertTrue($records[0]['extra']['foo']);
    }
}

class ExceptionTestHandler extends TestHandler
{
    /**
     * {@inheritdoc}
     */
    public function handle(array $record)
    {
        parent::handle($record);

        throw new \Exception("ExceptionTestHandler::handle");
    }
}
vendor/monolog/monolog/tests/Monolog/Handler/Slack/SlackRecordTest.php000064400000033234151327705700022134 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog\Handler\Slack;

use WPvividMonolog\Logger;
use WPvividMonolog\TestCase;

/**
 * @coversDefaultClass Monolog\Handler\Slack\SlackRecord
 */
class SlackRecordTest extends TestCase
{
    private $jsonPrettyPrintFlag;

    protected function setUp()
    {
        $this->jsonPrettyPrintFlag = defined('JSON_PRETTY_PRINT') ? JSON_PRETTY_PRINT : 128;
    }

    public function dataGetAttachmentColor()
    {
        return array(
            array(Logger::DEBUG, SlackRecord::COLOR_DEFAULT),
            array(Logger::INFO, SlackRecord::COLOR_GOOD),
            array(Logger::NOTICE, SlackRecord::COLOR_GOOD),
            array(Logger::WARNING, SlackRecord::COLOR_WARNING),
            array(Logger::ERROR, SlackRecord::COLOR_DANGER),
            array(Logger::CRITICAL, SlackRecord::COLOR_DANGER),
            array(Logger::ALERT, SlackRecord::COLOR_DANGER),
            array(Logger::EMERGENCY, SlackRecord::COLOR_DANGER),
        );
    }

    /**
     * @dataProvider dataGetAttachmentColor
     * @param  int $logLevel
     * @param  string $expectedColour RGB hex color or name of Slack color
     * @covers ::getAttachmentColor
     */
    public function testGetAttachmentColor($logLevel, $expectedColour)
    {
        $slackRecord = new SlackRecord();
        $this->assertSame(
            $expectedColour,
            $slackRecord->getAttachmentColor($logLevel)
        );
    }

    public function testAddsChannel()
    {
        $channel = '#test';
        $record = new SlackRecord($channel);
        $data = $record->getSlackData($this->getRecord());

        $this->assertArrayHasKey('channel', $data);
        $this->assertSame($channel, $data['channel']);
    }

    public function testNoUsernameByDefault()
    {
        $record = new SlackRecord();
        $data = $record->getSlackData($this->getRecord());

        $this->assertArrayNotHasKey('username', $data);
    }

    /**
     * @return array
     */
    public function dataStringify()
    {
        $jsonPrettyPrintFlag = defined('JSON_PRETTY_PRINT') ? JSON_PRETTY_PRINT : 128;

        $multipleDimensions = array(array(1, 2));
        $numericKeys = array('library' => 'monolog');
        $singleDimension = array(1, 'Hello', 'Jordi');

        return array(
            array(array(), '[]'),
            array($multipleDimensions, json_encode($multipleDimensions, $jsonPrettyPrintFlag)),
            array($numericKeys, json_encode($numericKeys, $jsonPrettyPrintFlag)),
            array($singleDimension, json_encode($singleDimension))
        );
    }

    /**
     * @dataProvider dataStringify
     */
    public function testStringify($fields, $expectedResult)
    {
        $slackRecord = new SlackRecord(
            '#test',
            'test',
            true,
            null,
            true,
            true
        );

        $this->assertSame($expectedResult, $slackRecord->stringify($fields));
    }

    public function testAddsCustomUsername()
    {
        $username = 'WPvividMonolog bot';
        $record = new SlackRecord(null, $username);
        $data = $record->getSlackData($this->getRecord());

        $this->assertArrayHasKey('username', $data);
        $this->assertSame($username, $data['username']);
    }

    public function testNoIcon()
    {
        $record = new SlackRecord();
        $data = $record->getSlackData($this->getRecord());

        $this->assertArrayNotHasKey('icon_emoji', $data);
    }

    public function testAddsIcon()
    {
        $record = $this->getRecord();
        $slackRecord = new SlackRecord(null, null, false, 'ghost');
        $data = $slackRecord->getSlackData($record);

        $slackRecord2 = new SlackRecord(null, null, false, 'http://github.com/Seldaek/monolog');
        $data2 = $slackRecord2->getSlackData($record);

        $this->assertArrayHasKey('icon_emoji', $data);
        $this->assertSame(':ghost:', $data['icon_emoji']);
        $this->assertArrayHasKey('icon_url', $data2);
        $this->assertSame('http://github.com/Seldaek/monolog', $data2['icon_url']);
    }

    public function testAttachmentsNotPresentIfNoAttachment()
    {
        $record = new SlackRecord(null, null, false);
        $data = $record->getSlackData($this->getRecord());

        $this->assertArrayNotHasKey('attachments', $data);
    }

    public function testAddsOneAttachment()
    {
        $record = new SlackRecord();
        $data = $record->getSlackData($this->getRecord());

        $this->assertArrayHasKey('attachments', $data);
        $this->assertArrayHasKey(0, $data['attachments']);
        $this->assertInternalType('array', $data['attachments'][0]);
    }

    public function testTextEqualsMessageIfNoAttachment()
    {
        $message = 'Test message';
        $record = new SlackRecord(null, null, false);
        $data = $record->getSlackData($this->getRecord(Logger::WARNING, $message));

        $this->assertArrayHasKey('text', $data);
        $this->assertSame($message, $data['text']);
    }

    public function testTextEqualsFormatterOutput()
    {
        $formatter = $this->getMock('WPvividMonolog\\Formatter\\FormatterInterface');
        $formatter
            ->expects($this->any())
            ->method('format')
            ->will($this->returnCallback(function ($record) { return $record['message'] . 'test'; }));

        $formatter2 = $this->getMock('WPvividMonolog\\Formatter\\FormatterInterface');
        $formatter2
            ->expects($this->any())
            ->method('format')
            ->will($this->returnCallback(function ($record) { return $record['message'] . 'test1'; }));

        $message = 'Test message';
        $record = new SlackRecord(null, null, false, null, false, false, array(), $formatter);
        $data = $record->getSlackData($this->getRecord(Logger::WARNING, $message));

        $this->assertArrayHasKey('text', $data);
        $this->assertSame($message . 'test', $data['text']);

        $record->setFormatter($formatter2);
        $data = $record->getSlackData($this->getRecord(Logger::WARNING, $message));

        $this->assertArrayHasKey('text', $data);
        $this->assertSame($message . 'test1', $data['text']);
    }

    public function testAddsFallbackAndTextToAttachment()
    {
        $message = 'Test message';
        $record = new SlackRecord(null);
        $data = $record->getSlackData($this->getRecord(Logger::WARNING, $message));

        $this->assertSame($message, $data['attachments'][0]['text']);
        $this->assertSame($message, $data['attachments'][0]['fallback']);
    }

    public function testMapsLevelToColorAttachmentColor()
    {
        $record = new SlackRecord(null);
        $errorLoggerRecord = $this->getRecord(Logger::ERROR);
        $emergencyLoggerRecord = $this->getRecord(Logger::EMERGENCY);
        $warningLoggerRecord = $this->getRecord(Logger::WARNING);
        $infoLoggerRecord = $this->getRecord(Logger::INFO);
        $debugLoggerRecord = $this->getRecord(Logger::DEBUG);

        $data = $record->getSlackData($errorLoggerRecord);
        $this->assertSame(SlackRecord::COLOR_DANGER, $data['attachments'][0]['color']);

        $data = $record->getSlackData($emergencyLoggerRecord);
        $this->assertSame(SlackRecord::COLOR_DANGER, $data['attachments'][0]['color']);

        $data = $record->getSlackData($warningLoggerRecord);
        $this->assertSame(SlackRecord::COLOR_WARNING, $data['attachments'][0]['color']);

        $data = $record->getSlackData($infoLoggerRecord);
        $this->assertSame(SlackRecord::COLOR_GOOD, $data['attachments'][0]['color']);

        $data = $record->getSlackData($debugLoggerRecord);
        $this->assertSame(SlackRecord::COLOR_DEFAULT, $data['attachments'][0]['color']);
    }

    public function testAddsShortAttachmentWithoutContextAndExtra()
    {
        $level = Logger::ERROR;
        $levelName = Logger::getLevelName($level);
        $record = new SlackRecord(null, null, true, null, true);
        $data = $record->getSlackData($this->getRecord($level, 'test', array('test' => 1)));

        $attachment = $data['attachments'][0];
        $this->assertArrayHasKey('title', $attachment);
        $this->assertArrayHasKey('fields', $attachment);
        $this->assertSame($levelName, $attachment['title']);
        $this->assertSame(array(), $attachment['fields']);
    }

    public function testAddsShortAttachmentWithContextAndExtra()
    {
        $level = Logger::ERROR;
        $levelName = Logger::getLevelName($level);
        $context = array('test' => 1);
        $extra = array('tags' => array('web'));
        $record = new SlackRecord(null, null, true, null, true, true);
        $loggerRecord = $this->getRecord($level, 'test', $context);
        $loggerRecord['extra'] = $extra;
        $data = $record->getSlackData($loggerRecord);

        $attachment = $data['attachments'][0];
        $this->assertArrayHasKey('title', $attachment);
        $this->assertArrayHasKey('fields', $attachment);
        $this->assertCount(2, $attachment['fields']);
        $this->assertSame($levelName, $attachment['title']);
        $this->assertSame(
            array(
                array(
                    'title' => 'Extra',
                    'value' => sprintf('```%s```', json_encode($extra, $this->jsonPrettyPrintFlag)),
                    'short' => false
                ),
                array(
                    'title' => 'Context',
                    'value' => sprintf('```%s```', json_encode($context, $this->jsonPrettyPrintFlag)),
                    'short' => false
                )
            ),
            $attachment['fields']
        );
    }

    public function testAddsLongAttachmentWithoutContextAndExtra()
    {
        $level = Logger::ERROR;
        $levelName = Logger::getLevelName($level);
        $record = new SlackRecord(null, null, true, null);
        $data = $record->getSlackData($this->getRecord($level, 'test', array('test' => 1)));

        $attachment = $data['attachments'][0];
        $this->assertArrayHasKey('title', $attachment);
        $this->assertArrayHasKey('fields', $attachment);
        $this->assertCount(1, $attachment['fields']);
        $this->assertSame('Message', $attachment['title']);
        $this->assertSame(
            array(array(
                'title' => 'Level',
                'value' => $levelName,
                'short' => false
            )),
            $attachment['fields']
        );
    }

    public function testAddsLongAttachmentWithContextAndExtra()
    {
        $level = Logger::ERROR;
        $levelName = Logger::getLevelName($level);
        $context = array('test' => 1);
        $extra = array('tags' => array('web'));
        $record = new SlackRecord(null, null, true, null, false, true);
        $loggerRecord = $this->getRecord($level, 'test', $context);
        $loggerRecord['extra'] = $extra;
        $data = $record->getSlackData($loggerRecord);

        $expectedFields = array(
            array(
                'title' => 'Level',
                'value' => $levelName,
                'short' => false,
            ),
            array(
                'title' => 'Tags',
                'value' => sprintf('```%s```', json_encode($extra['tags'])),
                'short' => false
            ),
            array(
                'title' => 'Test',
                'value' => $context['test'],
                'short' => false
            )
        );

        $attachment = $data['attachments'][0];
        $this->assertArrayHasKey('title', $attachment);
        $this->assertArrayHasKey('fields', $attachment);
        $this->assertCount(3, $attachment['fields']);
        $this->assertSame('Message', $attachment['title']);
        $this->assertSame(
            $expectedFields,
            $attachment['fields']
        );
    }

    public function testAddsTimestampToAttachment()
    {
        $record = $this->getRecord();
        $slackRecord = new SlackRecord();
        $data = $slackRecord->getSlackData($this->getRecord());

        $attachment = $data['attachments'][0];
        $this->assertArrayHasKey('ts', $attachment);
        $this->assertSame($record['datetime']->getTimestamp(), $attachment['ts']);
    }

    public function testContextHasException()
    {
        $record = $this->getRecord(Logger::CRITICAL, 'This is a critical message.', array('exception' => new \Exception()));
        $slackRecord = new SlackRecord(null, null, true, null, false, true);
        $data = $slackRecord->getSlackData($record);
        $this->assertInternalType('string', $data['attachments'][0]['fields'][1]['value']);
    }

    public function testExcludeExtraAndContextFields()
    {
        $record = $this->getRecord(
            Logger::WARNING,
            'test',
            array('info' => array('library' => 'monolog', 'author' => 'Jordi'))
        );
        $record['extra'] = array('tags' => array('web', 'cli'));

        $slackRecord = new SlackRecord(null, null, true, null, false, true, array('context.info.library', 'extra.tags.1'));
        $data = $slackRecord->getSlackData($record);
        $attachment = $data['attachments'][0];

        $expected = array(
            array(
                'title' => 'Info',
                'value' => sprintf('```%s```', json_encode(array('author' => 'Jordi'), $this->jsonPrettyPrintFlag)),
                'short' => false
            ),
            array(
                'title' => 'Tags',
                'value' => sprintf('```%s```', json_encode(array('web'))),
                'short' => false
            ),
        );

        foreach ($expected as $field) {
            $this->assertNotFalse(array_search($field, $attachment['fields']));
            break;
        }
    }
}
vendor/monolog/monolog/tests/Monolog/Handler/TestHandlerTest.php000064400000007737151327705700021131 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog\Handler;

use WPvividMonolog\TestCase;
use WPvividMonolog\Logger;

/**
 * @covers Monolog\Handler\TestHandler
 */
class TestHandlerTest extends TestCase
{
    /**
     * @dataProvider methodProvider
     */
    public function testHandler($method, $level)
    {
        $handler = new TestHandler;
        $record = $this->getRecord($level, 'test'.$method);
        $this->assertFalse($handler->hasRecords($level));
        $this->assertFalse($handler->hasRecord($record, $level));
        $this->assertFalse($handler->{'has'.$method}($record), 'has'.$method);
        $this->assertFalse($handler->{'has'.$method.'ThatContains'}('test'), 'has'.$method.'ThatContains');
        $this->assertFalse($handler->{'has'.$method.'ThatPasses'}(function ($rec) {
            return true;
        }), 'has'.$method.'ThatPasses');
        $this->assertFalse($handler->{'has'.$method.'ThatMatches'}('/test\w+/'));
        $this->assertFalse($handler->{'has'.$method.'Records'}(), 'has'.$method.'Records');
        $handler->handle($record);

        $this->assertFalse($handler->{'has'.$method}('bar'), 'has'.$method);
        $this->assertTrue($handler->hasRecords($level));
        $this->assertTrue($handler->hasRecord($record, $level));
        $this->assertTrue($handler->{'has'.$method}($record), 'has'.$method);
        $this->assertTrue($handler->{'has'.$method}('test'.$method), 'has'.$method);
        $this->assertTrue($handler->{'has'.$method.'ThatContains'}('test'), 'has'.$method.'ThatContains');
        $this->assertTrue($handler->{'has'.$method.'ThatPasses'}(function ($rec) {
            return true;
        }), 'has'.$method.'ThatPasses');
        $this->assertTrue($handler->{'has'.$method.'ThatMatches'}('/test\w+/'));
        $this->assertTrue($handler->{'has'.$method.'Records'}(), 'has'.$method.'Records');

        $records = $handler->getRecords();
        unset($records[0]['formatted']);
        $this->assertEquals(array($record), $records);
    }

    public function testHandlerAssertEmptyContext() {
        $handler = new TestHandler;
        $record  = $this->getRecord(Logger::WARNING, 'test', array());
        $this->assertFalse($handler->hasWarning(array(
            'message' => 'test',
            'context' => array(),
        )));

        $handler->handle($record);

        $this->assertTrue($handler->hasWarning(array(
            'message' => 'test',
            'context' => array(),
        )));
        $this->assertFalse($handler->hasWarning(array(
            'message' => 'test',
            'context' => array(
                'foo' => 'bar'
            ),
        )));
    }

    public function testHandlerAssertNonEmptyContext() {
        $handler = new TestHandler;
        $record  = $this->getRecord(Logger::WARNING, 'test', array('foo' => 'bar'));
        $this->assertFalse($handler->hasWarning(array(
            'message' => 'test',
            'context' => array(
                'foo' => 'bar'
            ),
        )));

        $handler->handle($record);

        $this->assertTrue($handler->hasWarning(array(
            'message' => 'test',
            'context' => array(
                'foo' => 'bar'
            ),
        )));
        $this->assertFalse($handler->hasWarning(array(
            'message' => 'test',
            'context' => array(),
        )));
    }

    public function methodProvider()
    {
        return array(
            array('Emergency', Logger::EMERGENCY),
            array('Alert'    , Logger::ALERT),
            array('Critical' , Logger::CRITICAL),
            array('Error'    , Logger::ERROR),
            array('Warning'  , Logger::WARNING),
            array('Info'     , Logger::INFO),
            array('Notice'   , Logger::NOTICE),
            array('Debug'    , Logger::DEBUG),
        );
    }
}
vendor/monolog/monolog/tests/Monolog/Handler/SyslogUdpHandlerTest.php000064400000004474151327705700022136 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog\Handler;

use WPvividMonolog\TestCase;

/**
 * @requires extension sockets
 */
class SyslogUdpHandlerTest extends TestCase
{
    /**
     * @expectedException UnexpectedValueException
     */
    public function testWeValidateFacilities()
    {
        $handler = new SyslogUdpHandler("ip", null, "invalidFacility");
    }

    public function testWeSplitIntoLines()
    {
        $time = '2014-01-07T12:34';
        $pid = getmypid();
        $host = gethostname();

        $handler = $this->getMockBuilder('\WPvividMonolog\Handler\SyslogUdpHandler')
            ->setConstructorArgs(array("127.0.0.1", 514, "authpriv"))
            ->setMethods(array('getDateTime'))
            ->getMock();

        $handler->method('getDateTime')
            ->willReturn($time);

        $handler->setFormatter(new \WPvividMonolog\Formatter\ChromePHPFormatter());

        $socket = $this->getMock('\WPvividMonolog\Handler\SyslogUdp\UdpSocket', array('write'), array('lol', 'lol'));
        $socket->expects($this->at(0))
            ->method('write')
            ->with("lol", "<".(LOG_AUTHPRIV + LOG_WARNING).">1 $time $host php $pid - - ");
        $socket->expects($this->at(1))
            ->method('write')
            ->with("hej", "<".(LOG_AUTHPRIV + LOG_WARNING).">1 $time $host php $pid - - ");

        $handler->setSocket($socket);

        $handler->handle($this->getRecordWithMessage("hej\nlol"));
    }

    public function testSplitWorksOnEmptyMsg()
    {
        $handler = new SyslogUdpHandler("127.0.0.1", 514, "authpriv");
        $handler->setFormatter($this->getIdentityFormatter());

        $socket = $this->getMock('\WPvividMonolog\Handler\SyslogUdp\UdpSocket', array('write'), array('lol', 'lol'));
        $socket->expects($this->never())
            ->method('write');

        $handler->setSocket($socket);

        $handler->handle($this->getRecordWithMessage(null));
    }

    protected function getRecordWithMessage($msg)
    {
        return array('message' => $msg, 'level' => \WPvividMonolog\Logger::WARNING, 'context' => null, 'extra' => array(), 'channel' => 'lol');
    }
}
vendor/monolog/monolog/tests/Monolog/Handler/NullHandlerTest.php000064400000001400151327705700021101 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog\Handler;

use WPvividMonolog\TestCase;
use WPvividMonolog\Logger;

/**
 * @covers Monolog\Handler\NullHandler::handle
 */
class NullHandlerTest extends TestCase
{
    public function testHandle()
    {
        $handler = new NullHandler();
        $this->assertTrue($handler->handle($this->getRecord()));
    }

    public function testHandleLowerLevelRecord()
    {
        $handler = new NullHandler(Logger::WARNING);
        $this->assertFalse($handler->handle($this->getRecord(Logger::DEBUG)));
    }
}
vendor/monolog/monolog/tests/Monolog/Handler/RollbarHandlerTest.php000064400000004241151327705700021572 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog\Handler;

use Exception;
use WPvividMonolog\TestCase;
use WPvividMonolog\Logger;
use PHPUnit_Framework_MockObject_MockObject as MockObject;

/**
 * @author Erik Johansson <erik.pm.johansson@gmail.com>
 * @see    https://rollbar.com/docs/notifier/rollbar-php/
 *
 * @coversDefaultClass Monolog\Handler\RollbarHandler
 */
class RollbarHandlerTest extends TestCase
{
    /**
     * @var MockObject
     */
    private $rollbarNotifier;

    /**
     * @var array
     */
    public $reportedExceptionArguments = null;

    protected function setUp()
    {
        parent::setUp();

        $this->setupRollbarNotifierMock();
    }

    /**
     * When reporting exceptions to Rollbar the
     * level has to be set in the payload data
     */
    public function testExceptionLogLevel()
    {
        $handler = $this->createHandler();

        $handler->handle($this->createExceptionRecord(Logger::DEBUG));

        $this->assertEquals('debug', $this->reportedExceptionArguments['payload']['level']);
    }

    private function setupRollbarNotifierMock()
    {
        $this->rollbarNotifier = $this->getMockBuilder('RollbarNotifier')
            ->setMethods(array('report_message', 'report_exception', 'flush'))
            ->getMock();

        $that = $this;

        $this->rollbarNotifier
            ->expects($this->any())
            ->method('report_exception')
            ->willReturnCallback(function ($exception, $context, $payload) use ($that) {
                $that->reportedExceptionArguments = compact('exception', 'context', 'payload');
            });
    }

    private function createHandler()
    {
        return new RollbarHandler($this->rollbarNotifier, Logger::DEBUG);
    }

    private function createExceptionRecord($level = Logger::DEBUG, $message = 'test', $exception = null)
    {
        return $this->getRecord($level, $message, array(
            'exception' => $exception ?: new Exception()
        ));
    }
}
vendor/monolog/monolog/tests/Monolog/Handler/SlackWebhookHandlerTest.php000064400000006274151327705700022561 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog\Handler;

use WPvividMonolog\TestCase;
use WPvividMonolog\Logger;
use WPvividMonolog\Formatter\LineFormatter;
use WPvividMonolog\Handler\Slack\SlackRecord;

/**
 * @author Haralan Dobrev <hkdobrev@gmail.com>
 * @see    https://api.slack.com/incoming-webhooks
 * @coversDefaultClass Monolog\Handler\SlackWebhookHandler
 */
class SlackWebhookHandlerTest extends TestCase
{
    const WEBHOOK_URL = 'https://hooks.slack.com/services/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E';

    /**
     * @covers ::__construct
     * @covers ::getSlackRecord
     */
    public function testConstructorMinimal()
    {
        $handler = new SlackWebhookHandler(self::WEBHOOK_URL);
        $record = $this->getRecord();
        $slackRecord = $handler->getSlackRecord();
        $this->assertInstanceOf('WPvividMonolog\Handler\Slack\SlackRecord', $slackRecord);
        $this->assertEquals(array(
            'attachments' => array(
                array(
                    'fallback' => 'test',
                    'text' => 'test',
                    'color' => SlackRecord::COLOR_WARNING,
                    'fields' => array(
                        array(
                            'title' => 'Level',
                            'value' => 'WARNING',
                            'short' => false,
                        ),
                    ),
                    'title' => 'Message',
                    'mrkdwn_in' => array('fields'),
                    'ts' => $record['datetime']->getTimestamp(),
                ),
            ),
        ), $slackRecord->getSlackData($record));
    }

    /**
     * @covers ::__construct
     * @covers ::getSlackRecord
     */
    public function testConstructorFull()
    {
        $handler = new SlackWebhookHandler(
            self::WEBHOOK_URL,
            'test-channel',
            'test-username',
            false,
            ':ghost:',
            false,
            false,
            Logger::DEBUG,
            false
        );

        $slackRecord = $handler->getSlackRecord();
        $this->assertInstanceOf('WPvividMonolog\Handler\Slack\SlackRecord', $slackRecord);
        $this->assertEquals(array(
            'username' => 'test-username',
            'text' => 'test',
            'channel' => 'test-channel',
            'icon_emoji' => ':ghost:',
        ), $slackRecord->getSlackData($this->getRecord()));
    }

    /**
     * @covers ::getFormatter
     */
    public function testGetFormatter()
    {
        $handler = new SlackWebhookHandler(self::WEBHOOK_URL);
        $formatter = $handler->getFormatter();
        $this->assertInstanceOf('WPvividMonolog\Formatter\FormatterInterface', $formatter);
    }

    /**
     * @covers ::setFormatter
     */
    public function testSetFormatter()
    {
        $handler = new SlackWebhookHandler(self::WEBHOOK_URL);
        $formatter = new LineFormatter();
        $handler->setFormatter($formatter);
        $this->assertSame($formatter, $handler->getFormatter());
    }
}
vendor/monolog/monolog/tests/Monolog/Handler/FilterHandlerTest.php000064400000014434151327705700021427 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog\Handler;

use WPvividMonolog\Logger;
use WPvividMonolog\TestCase;

class FilterHandlerTest extends TestCase
{
    /**
     * @covers Monolog\Handler\FilterHandler::isHandling
     */
    public function testIsHandling()
    {
        $test    = new TestHandler();
        $handler = new FilterHandler($test, Logger::INFO, Logger::NOTICE);
        $this->assertFalse($handler->isHandling($this->getRecord(Logger::DEBUG)));
        $this->assertTrue($handler->isHandling($this->getRecord(Logger::INFO)));
        $this->assertTrue($handler->isHandling($this->getRecord(Logger::NOTICE)));
        $this->assertFalse($handler->isHandling($this->getRecord(Logger::WARNING)));
        $this->assertFalse($handler->isHandling($this->getRecord(Logger::ERROR)));
        $this->assertFalse($handler->isHandling($this->getRecord(Logger::CRITICAL)));
        $this->assertFalse($handler->isHandling($this->getRecord(Logger::ALERT)));
        $this->assertFalse($handler->isHandling($this->getRecord(Logger::EMERGENCY)));
    }

    /**
     * @covers Monolog\Handler\FilterHandler::handle
     * @covers Monolog\Handler\FilterHandler::setAcceptedLevels
     * @covers Monolog\Handler\FilterHandler::isHandling
     */
    public function testHandleProcessOnlyNeededLevels()
    {
        $test    = new TestHandler();
        $handler = new FilterHandler($test, Logger::INFO, Logger::NOTICE);

        $handler->handle($this->getRecord(Logger::DEBUG));
        $this->assertFalse($test->hasDebugRecords());

        $handler->handle($this->getRecord(Logger::INFO));
        $this->assertTrue($test->hasInfoRecords());
        $handler->handle($this->getRecord(Logger::NOTICE));
        $this->assertTrue($test->hasNoticeRecords());

        $handler->handle($this->getRecord(Logger::WARNING));
        $this->assertFalse($test->hasWarningRecords());
        $handler->handle($this->getRecord(Logger::ERROR));
        $this->assertFalse($test->hasErrorRecords());
        $handler->handle($this->getRecord(Logger::CRITICAL));
        $this->assertFalse($test->hasCriticalRecords());
        $handler->handle($this->getRecord(Logger::ALERT));
        $this->assertFalse($test->hasAlertRecords());
        $handler->handle($this->getRecord(Logger::EMERGENCY));
        $this->assertFalse($test->hasEmergencyRecords());

        $test    = new TestHandler();
        $handler = new FilterHandler($test, array(Logger::INFO, Logger::ERROR));

        $handler->handle($this->getRecord(Logger::DEBUG));
        $this->assertFalse($test->hasDebugRecords());
        $handler->handle($this->getRecord(Logger::INFO));
        $this->assertTrue($test->hasInfoRecords());
        $handler->handle($this->getRecord(Logger::NOTICE));
        $this->assertFalse($test->hasNoticeRecords());
        $handler->handle($this->getRecord(Logger::ERROR));
        $this->assertTrue($test->hasErrorRecords());
        $handler->handle($this->getRecord(Logger::CRITICAL));
        $this->assertFalse($test->hasCriticalRecords());
    }

    /**
     * @covers Monolog\Handler\FilterHandler::setAcceptedLevels
     * @covers Monolog\Handler\FilterHandler::getAcceptedLevels
     */
    public function testAcceptedLevelApi()
    {
        $test    = new TestHandler();
        $handler = new FilterHandler($test);

        $levels = array(Logger::INFO, Logger::ERROR);
        $handler->setAcceptedLevels($levels);
        $this->assertSame($levels, $handler->getAcceptedLevels());

        $handler->setAcceptedLevels(array('info', 'error'));
        $this->assertSame($levels, $handler->getAcceptedLevels());

        $levels = array(Logger::CRITICAL, Logger::ALERT, Logger::EMERGENCY);
        $handler->setAcceptedLevels(Logger::CRITICAL, Logger::EMERGENCY);
        $this->assertSame($levels, $handler->getAcceptedLevels());

        $handler->setAcceptedLevels('critical', 'emergency');
        $this->assertSame($levels, $handler->getAcceptedLevels());
    }

    /**
     * @covers Monolog\Handler\FilterHandler::handle
     */
    public function testHandleUsesProcessors()
    {
        $test    = new TestHandler();
        $handler = new FilterHandler($test, Logger::DEBUG, Logger::EMERGENCY);
        $handler->pushProcessor(
            function ($record) {
                $record['extra']['foo'] = true;

                return $record;
            }
        );
        $handler->handle($this->getRecord(Logger::WARNING));
        $this->assertTrue($test->hasWarningRecords());
        $records = $test->getRecords();
        $this->assertTrue($records[0]['extra']['foo']);
    }

    /**
     * @covers Monolog\Handler\FilterHandler::handle
     */
    public function testHandleRespectsBubble()
    {
        $test = new TestHandler();

        $handler = new FilterHandler($test, Logger::INFO, Logger::NOTICE, false);
        $this->assertTrue($handler->handle($this->getRecord(Logger::INFO)));
        $this->assertFalse($handler->handle($this->getRecord(Logger::WARNING)));

        $handler = new FilterHandler($test, Logger::INFO, Logger::NOTICE, true);
        $this->assertFalse($handler->handle($this->getRecord(Logger::INFO)));
        $this->assertFalse($handler->handle($this->getRecord(Logger::WARNING)));
    }

    /**
     * @covers Monolog\Handler\FilterHandler::handle
     */
    public function testHandleWithCallback()
    {
        $test    = new TestHandler();
        $handler = new FilterHandler(
            function ($record, $handler) use ($test) {
                return $test;
            }, Logger::INFO, Logger::NOTICE, false
        );
        $handler->handle($this->getRecord(Logger::DEBUG));
        $handler->handle($this->getRecord(Logger::INFO));
        $this->assertFalse($test->hasDebugRecords());
        $this->assertTrue($test->hasInfoRecords());
    }

    /**
     * @covers Monolog\Handler\FilterHandler::handle
     * @expectedException \RuntimeException
     */
    public function testHandleWithBadCallbackThrowsException()
    {
        $handler = new FilterHandler(
            function ($record, $handler) {
                return 'foo';
            }
        );
        $handler->handle($this->getRecord(Logger::WARNING));
    }
}
vendor/monolog/monolog/tests/Monolog/Handler/DeduplicationHandlerTest.php000064400000014013151327705700022757 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog\Handler;

use WPvividMonolog\TestCase;
use WPvividMonolog\Logger;

class DeduplicationHandlerTest extends TestCase
{
    /**
     * @covers Monolog\Handler\DeduplicationHandler::flush
     */
    public function testFlushPassthruIfAllRecordsUnderTrigger()
    {
        $test = new TestHandler();
        @unlink(sys_get_temp_dir().'/monolog_dedup.log');
        $handler = new DeduplicationHandler($test, sys_get_temp_dir().'/monolog_dedup.log', 0);

        $handler->handle($this->getRecord(Logger::DEBUG));
        $handler->handle($this->getRecord(Logger::INFO));

        $handler->flush();

        $this->assertTrue($test->hasInfoRecords());
        $this->assertTrue($test->hasDebugRecords());
        $this->assertFalse($test->hasWarningRecords());
    }

    /**
     * @covers Monolog\Handler\DeduplicationHandler::flush
     * @covers Monolog\Handler\DeduplicationHandler::appendRecord
     */
    public function testFlushPassthruIfEmptyLog()
    {
        $test = new TestHandler();
        @unlink(sys_get_temp_dir().'/monolog_dedup.log');
        $handler = new DeduplicationHandler($test, sys_get_temp_dir().'/monolog_dedup.log', 0);

        $handler->handle($this->getRecord(Logger::ERROR, 'Foo:bar'));
        $handler->handle($this->getRecord(Logger::CRITICAL, "Foo\nbar"));

        $handler->flush();

        $this->assertTrue($test->hasErrorRecords());
        $this->assertTrue($test->hasCriticalRecords());
        $this->assertFalse($test->hasWarningRecords());
    }

    /**
     * @covers Monolog\Handler\DeduplicationHandler::flush
     * @covers Monolog\Handler\DeduplicationHandler::appendRecord
     * @covers Monolog\Handler\DeduplicationHandler::isDuplicate
     * @depends testFlushPassthruIfEmptyLog
     */
    public function testFlushSkipsIfLogExists()
    {
        $test = new TestHandler();
        $handler = new DeduplicationHandler($test, sys_get_temp_dir().'/monolog_dedup.log', 0);

        $handler->handle($this->getRecord(Logger::ERROR, 'Foo:bar'));
        $handler->handle($this->getRecord(Logger::CRITICAL, "Foo\nbar"));

        $handler->flush();

        $this->assertFalse($test->hasErrorRecords());
        $this->assertFalse($test->hasCriticalRecords());
        $this->assertFalse($test->hasWarningRecords());
    }

    /**
     * @covers Monolog\Handler\DeduplicationHandler::flush
     * @covers Monolog\Handler\DeduplicationHandler::appendRecord
     * @covers Monolog\Handler\DeduplicationHandler::isDuplicate
     * @depends testFlushPassthruIfEmptyLog
     */
    public function testFlushPassthruIfLogTooOld()
    {
        $test = new TestHandler();
        $handler = new DeduplicationHandler($test, sys_get_temp_dir().'/monolog_dedup.log', 0);

        $record = $this->getRecord(Logger::ERROR);
        $record['datetime']->modify('+62seconds');
        $handler->handle($record);
        $record = $this->getRecord(Logger::CRITICAL);
        $record['datetime']->modify('+62seconds');
        $handler->handle($record);

        $handler->flush();

        $this->assertTrue($test->hasErrorRecords());
        $this->assertTrue($test->hasCriticalRecords());
        $this->assertFalse($test->hasWarningRecords());
    }

    /**
     * @covers Monolog\Handler\DeduplicationHandler::flush
     * @covers Monolog\Handler\DeduplicationHandler::appendRecord
     * @covers Monolog\Handler\DeduplicationHandler::isDuplicate
     * @covers Monolog\Handler\DeduplicationHandler::collectLogs
     */
    public function testGcOldLogs()
    {
        $test = new TestHandler();
        @unlink(sys_get_temp_dir().'/monolog_dedup.log');
        $handler = new DeduplicationHandler($test, sys_get_temp_dir().'/monolog_dedup.log', 0);

        // handle two records from yesterday, and one recent
        $record = $this->getRecord(Logger::ERROR);
        $record['datetime']->modify('-1day -10seconds');
        $handler->handle($record);
        $record2 = $this->getRecord(Logger::CRITICAL);
        $record2['datetime']->modify('-1day -10seconds');
        $handler->handle($record2);
        $record3 = $this->getRecord(Logger::CRITICAL);
        $record3['datetime']->modify('-30seconds');
        $handler->handle($record3);

        // log is written as none of them are duplicate
        $handler->flush();
        $this->assertSame(
            $record['datetime']->getTimestamp() . ":ERROR:test\n" .
            $record2['datetime']->getTimestamp() . ":CRITICAL:test\n" .
            $record3['datetime']->getTimestamp() . ":CRITICAL:test\n",
            file_get_contents(sys_get_temp_dir() . '/monolog_dedup.log')
        );
        $this->assertTrue($test->hasErrorRecords());
        $this->assertTrue($test->hasCriticalRecords());
        $this->assertFalse($test->hasWarningRecords());

        // clear test handler
        $test->clear();
        $this->assertFalse($test->hasErrorRecords());
        $this->assertFalse($test->hasCriticalRecords());

        // log new records, duplicate log gets GC'd at the end of this flush call
        $handler->handle($record = $this->getRecord(Logger::ERROR));
        $handler->handle($record2 = $this->getRecord(Logger::CRITICAL));
        $handler->flush();

        // log should now contain the new errors and the previous one that was recent enough
        $this->assertSame(
            $record3['datetime']->getTimestamp() . ":CRITICAL:test\n" .
            $record['datetime']->getTimestamp() . ":ERROR:test\n" .
            $record2['datetime']->getTimestamp() . ":CRITICAL:test\n",
            file_get_contents(sys_get_temp_dir() . '/monolog_dedup.log')
        );
        $this->assertTrue($test->hasErrorRecords());
        $this->assertTrue($test->hasCriticalRecords());
        $this->assertFalse($test->hasWarningRecords());
    }

    public static function tearDownAfterClass()
    {
        @unlink(sys_get_temp_dir().'/monolog_dedup.log');
    }
}
vendor/monolog/monolog/tests/Monolog/Handler/MongoDBHandlerTest.php000064400000003353151327705700021465 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog\Handler;

use WPvividMonolog\TestCase;
use WPvividMonolog\Logger;

class MongoDBHandlerTest extends TestCase
{
    /**
     * @expectedException InvalidArgumentException
     */
    public function testConstructorShouldThrowExceptionForInvalidMongo()
    {
        new MongoDBHandler(new \stdClass(), 'DB', 'Collection');
    }

    public function testHandle()
    {
        $mongo = $this->getMock('Mongo', array('selectCollection'), array(), '', false);
        $collection = $this->getMock('stdClass', array('save'));

        $mongo->expects($this->once())
            ->method('selectCollection')
            ->with('DB', 'Collection')
            ->will($this->returnValue($collection));

        $record = $this->getRecord(Logger::WARNING, 'test', array('data' => new \stdClass, 'foo' => 34));

        $expected = array(
            'message' => 'test',
            'context' => array('data' => '[object] (stdClass: {})', 'foo' => 34),
            'level' => Logger::WARNING,
            'level_name' => 'WARNING',
            'channel' => 'test',
            'datetime' => $record['datetime']->format('Y-m-d H:i:s'),
            'extra' => array(),
        );

        $collection->expects($this->once())
            ->method('save')
            ->with($expected);

        $handler = new MongoDBHandler($mongo, 'DB', 'Collection');
        $handler->handle($record);
    }
}

if (!class_exists('Mongo')) {
    class Mongo
    {
        public function selectCollection()
        {
        }
    }
}
vendor/monolog/monolog/tests/Monolog/Handler/FingersCrossedHandlerTest.php000064400000025610151327705700023120 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog\Handler;

use WPvividMonolog\TestCase;
use WPvividMonolog\Logger;
use WPvividMonolog\Handler\FingersCrossed\ErrorLevelActivationStrategy;
use WPvividMonolog\Handler\FingersCrossed\ChannelLevelActivationStrategy;
use WPvividPsr\Log\LogLevel;

class FingersCrossedHandlerTest extends TestCase
{
    /**
     * @covers Monolog\Handler\FingersCrossedHandler::__construct
     * @covers Monolog\Handler\FingersCrossedHandler::handle
     * @covers Monolog\Handler\FingersCrossedHandler::activate
     */
    public function testHandleBuffers()
    {
        $test = new TestHandler();
        $handler = new FingersCrossedHandler($test);
        $handler->handle($this->getRecord(Logger::DEBUG));
        $handler->handle($this->getRecord(Logger::INFO));
        $this->assertFalse($test->hasDebugRecords());
        $this->assertFalse($test->hasInfoRecords());
        $handler->handle($this->getRecord(Logger::WARNING));
        $handler->close();
        $this->assertTrue($test->hasInfoRecords());
        $this->assertTrue(count($test->getRecords()) === 3);
    }

    /**
     * @covers Monolog\Handler\FingersCrossedHandler::handle
     * @covers Monolog\Handler\FingersCrossedHandler::activate
     */
    public function testHandleStopsBufferingAfterTrigger()
    {
        $test = new TestHandler();
        $handler = new FingersCrossedHandler($test);
        $handler->handle($this->getRecord(Logger::WARNING));
        $handler->handle($this->getRecord(Logger::DEBUG));
        $handler->close();
        $this->assertTrue($test->hasWarningRecords());
        $this->assertTrue($test->hasDebugRecords());
    }

    /**
     * @covers Monolog\Handler\FingersCrossedHandler::handle
     * @covers Monolog\Handler\FingersCrossedHandler::activate
     * @covers Monolog\Handler\FingersCrossedHandler::reset
     */
    public function testHandleResetBufferingAfterReset()
    {
        $test = new TestHandler();
        $handler = new FingersCrossedHandler($test);
        $handler->handle($this->getRecord(Logger::WARNING));
        $handler->handle($this->getRecord(Logger::DEBUG));
        $handler->reset();
        $handler->handle($this->getRecord(Logger::INFO));
        $handler->close();
        $this->assertTrue($test->hasWarningRecords());
        $this->assertTrue($test->hasDebugRecords());
        $this->assertFalse($test->hasInfoRecords());
    }

    /**
     * @covers Monolog\Handler\FingersCrossedHandler::handle
     * @covers Monolog\Handler\FingersCrossedHandler::activate
     */
    public function testHandleResetBufferingAfterBeingTriggeredWhenStopBufferingIsDisabled()
    {
        $test = new TestHandler();
        $handler = new FingersCrossedHandler($test, Logger::WARNING, 0, false, false);
        $handler->handle($this->getRecord(Logger::DEBUG));
        $handler->handle($this->getRecord(Logger::WARNING));
        $handler->handle($this->getRecord(Logger::INFO));
        $handler->close();
        $this->assertTrue($test->hasWarningRecords());
        $this->assertTrue($test->hasDebugRecords());
        $this->assertFalse($test->hasInfoRecords());
    }

    /**
     * @covers Monolog\Handler\FingersCrossedHandler::handle
     * @covers Monolog\Handler\FingersCrossedHandler::activate
     */
    public function testHandleBufferLimit()
    {
        $test = new TestHandler();
        $handler = new FingersCrossedHandler($test, Logger::WARNING, 2);
        $handler->handle($this->getRecord(Logger::DEBUG));
        $handler->handle($this->getRecord(Logger::DEBUG));
        $handler->handle($this->getRecord(Logger::INFO));
        $handler->handle($this->getRecord(Logger::WARNING));
        $this->assertTrue($test->hasWarningRecords());
        $this->assertTrue($test->hasInfoRecords());
        $this->assertFalse($test->hasDebugRecords());
    }

    /**
     * @covers Monolog\Handler\FingersCrossedHandler::handle
     * @covers Monolog\Handler\FingersCrossedHandler::activate
     */
    public function testHandleWithCallback()
    {
        $test = new TestHandler();
        $handler = new FingersCrossedHandler(function ($record, $handler) use ($test) {
                    return $test;
                });
        $handler->handle($this->getRecord(Logger::DEBUG));
        $handler->handle($this->getRecord(Logger::INFO));
        $this->assertFalse($test->hasDebugRecords());
        $this->assertFalse($test->hasInfoRecords());
        $handler->handle($this->getRecord(Logger::WARNING));
        $this->assertTrue($test->hasInfoRecords());
        $this->assertTrue(count($test->getRecords()) === 3);
    }

    /**
     * @covers Monolog\Handler\FingersCrossedHandler::handle
     * @covers Monolog\Handler\FingersCrossedHandler::activate
     * @expectedException RuntimeException
     */
    public function testHandleWithBadCallbackThrowsException()
    {
        $handler = new FingersCrossedHandler(function ($record, $handler) {
                    return 'foo';
                });
        $handler->handle($this->getRecord(Logger::WARNING));
    }

    /**
     * @covers Monolog\Handler\FingersCrossedHandler::isHandling
     */
    public function testIsHandlingAlways()
    {
        $test = new TestHandler();
        $handler = new FingersCrossedHandler($test, Logger::ERROR);
        $this->assertTrue($handler->isHandling($this->getRecord(Logger::DEBUG)));
    }

    /**
     * @covers Monolog\Handler\FingersCrossedHandler::__construct
     * @covers Monolog\Handler\FingersCrossed\ErrorLevelActivationStrategy::__construct
     * @covers Monolog\Handler\FingersCrossed\ErrorLevelActivationStrategy::isHandlerActivated
     */
    public function testErrorLevelActivationStrategy()
    {
        $test = new TestHandler();
        $handler = new FingersCrossedHandler($test, new ErrorLevelActivationStrategy(Logger::WARNING));
        $handler->handle($this->getRecord(Logger::DEBUG));
        $this->assertFalse($test->hasDebugRecords());
        $handler->handle($this->getRecord(Logger::WARNING));
        $this->assertTrue($test->hasDebugRecords());
        $this->assertTrue($test->hasWarningRecords());
    }

    /**
     * @covers Monolog\Handler\FingersCrossedHandler::__construct
     * @covers Monolog\Handler\FingersCrossed\ErrorLevelActivationStrategy::__construct
     * @covers Monolog\Handler\FingersCrossed\ErrorLevelActivationStrategy::isHandlerActivated
     */
    public function testErrorLevelActivationStrategyWithPsrLevel()
    {
        $test = new TestHandler();
        $handler = new FingersCrossedHandler($test, new ErrorLevelActivationStrategy('warning'));
        $handler->handle($this->getRecord(Logger::DEBUG));
        $this->assertFalse($test->hasDebugRecords());
        $handler->handle($this->getRecord(Logger::WARNING));
        $this->assertTrue($test->hasDebugRecords());
        $this->assertTrue($test->hasWarningRecords());
    }

    /**
     * @covers Monolog\Handler\FingersCrossedHandler::__construct
     * @covers Monolog\Handler\FingersCrossedHandler::activate
     */
    public function testOverrideActivationStrategy()
    {
        $test = new TestHandler();
        $handler = new FingersCrossedHandler($test, new ErrorLevelActivationStrategy('warning'));
        $handler->handle($this->getRecord(Logger::DEBUG));
        $this->assertFalse($test->hasDebugRecords());
        $handler->activate();
        $this->assertTrue($test->hasDebugRecords());
        $handler->handle($this->getRecord(Logger::INFO));
        $this->assertTrue($test->hasInfoRecords());
    }

    /**
     * @covers Monolog\Handler\FingersCrossed\ChannelLevelActivationStrategy::__construct
     * @covers Monolog\Handler\FingersCrossed\ChannelLevelActivationStrategy::isHandlerActivated
     */
    public function testChannelLevelActivationStrategy()
    {
        $test = new TestHandler();
        $handler = new FingersCrossedHandler($test, new ChannelLevelActivationStrategy(Logger::ERROR, array('othertest' => Logger::DEBUG)));
        $handler->handle($this->getRecord(Logger::WARNING));
        $this->assertFalse($test->hasWarningRecords());
        $record = $this->getRecord(Logger::DEBUG);
        $record['channel'] = 'othertest';
        $handler->handle($record);
        $this->assertTrue($test->hasDebugRecords());
        $this->assertTrue($test->hasWarningRecords());
    }

    /**
     * @covers Monolog\Handler\FingersCrossed\ChannelLevelActivationStrategy::__construct
     * @covers Monolog\Handler\FingersCrossed\ChannelLevelActivationStrategy::isHandlerActivated
     */
    public function testChannelLevelActivationStrategyWithPsrLevels()
    {
        $test = new TestHandler();
        $handler = new FingersCrossedHandler($test, new ChannelLevelActivationStrategy('error', array('othertest' => 'debug')));
        $handler->handle($this->getRecord(Logger::WARNING));
        $this->assertFalse($test->hasWarningRecords());
        $record = $this->getRecord(Logger::DEBUG);
        $record['channel'] = 'othertest';
        $handler->handle($record);
        $this->assertTrue($test->hasDebugRecords());
        $this->assertTrue($test->hasWarningRecords());
    }

    /**
     * @covers Monolog\Handler\FingersCrossedHandler::handle
     * @covers Monolog\Handler\FingersCrossedHandler::activate
     */
    public function testHandleUsesProcessors()
    {
        $test = new TestHandler();
        $handler = new FingersCrossedHandler($test, Logger::INFO);
        $handler->pushProcessor(function ($record) {
            $record['extra']['foo'] = true;

            return $record;
        });
        $handler->handle($this->getRecord(Logger::WARNING));
        $this->assertTrue($test->hasWarningRecords());
        $records = $test->getRecords();
        $this->assertTrue($records[0]['extra']['foo']);
    }

    /**
     * @covers Monolog\Handler\FingersCrossedHandler::close
     */
    public function testPassthruOnClose()
    {
        $test = new TestHandler();
        $handler = new FingersCrossedHandler($test, new ErrorLevelActivationStrategy(Logger::WARNING), 0, true, true, Logger::INFO);
        $handler->handle($this->getRecord(Logger::DEBUG));
        $handler->handle($this->getRecord(Logger::INFO));
        $handler->close();
        $this->assertFalse($test->hasDebugRecords());
        $this->assertTrue($test->hasInfoRecords());
    }

    /**
     * @covers Monolog\Handler\FingersCrossedHandler::close
     */
    public function testPsrLevelPassthruOnClose()
    {
        $test = new TestHandler();
        $handler = new FingersCrossedHandler($test, new ErrorLevelActivationStrategy(Logger::WARNING), 0, true, true, LogLevel::INFO);
        $handler->handle($this->getRecord(Logger::DEBUG));
        $handler->handle($this->getRecord(Logger::INFO));
        $handler->close();
        $this->assertFalse($test->hasDebugRecords());
        $this->assertTrue($test->hasInfoRecords());
    }
}
vendor/monolog/monolog/tests/Monolog/Handler/PHPConsoleHandlerTest.php000064400000023307151327705700022153 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog\Handler;

use Exception;
use WPvividMonolog\ErrorHandler;
use WPvividMonolog\Logger;
use WPvividMonolog\TestCase;
use PhpConsole\Connector;
use PhpConsole\Dispatcher\Debug as DebugDispatcher;
use PhpConsole\Dispatcher\Errors as ErrorDispatcher;
use PhpConsole\Handler;
use PHPUnit_Framework_MockObject_MockObject;

/**
 * @covers Monolog\Handler\PHPConsoleHandler
 * @author Sergey Barbushin https://www.linkedin.com/in/barbushin
 */
class PHPConsoleHandlerTest extends TestCase
{
    /** @var  Connector|PHPUnit_Framework_MockObject_MockObject */
    protected $connector;
    /** @var  DebugDispatcher|PHPUnit_Framework_MockObject_MockObject */
    protected $debugDispatcher;
    /** @var  ErrorDispatcher|PHPUnit_Framework_MockObject_MockObject */
    protected $errorDispatcher;

    protected function setUp()
    {
        if (!class_exists('PhpConsole\Connector')) {
            $this->markTestSkipped('PHP Console library not found. See https://github.com/barbushin/php-console#installation');
        }
        $this->connector = $this->initConnectorMock();

        $this->debugDispatcher = $this->initDebugDispatcherMock($this->connector);
        $this->connector->setDebugDispatcher($this->debugDispatcher);

        $this->errorDispatcher = $this->initErrorDispatcherMock($this->connector);
        $this->connector->setErrorsDispatcher($this->errorDispatcher);
    }

    protected function initDebugDispatcherMock(Connector $connector)
    {
        return $this->getMockBuilder('PhpConsole\Dispatcher\Debug')
            ->disableOriginalConstructor()
            ->setMethods(array('dispatchDebug'))
            ->setConstructorArgs(array($connector, $connector->getDumper()))
            ->getMock();
    }

    protected function initErrorDispatcherMock(Connector $connector)
    {
        return $this->getMockBuilder('PhpConsole\Dispatcher\Errors')
            ->disableOriginalConstructor()
            ->setMethods(array('dispatchError', 'dispatchException'))
            ->setConstructorArgs(array($connector, $connector->getDumper()))
            ->getMock();
    }

    protected function initConnectorMock()
    {
        $connector = $this->getMockBuilder('PhpConsole\Connector')
            ->disableOriginalConstructor()
            ->setMethods(array(
                'sendMessage',
                'onShutDown',
                'isActiveClient',
                'setSourcesBasePath',
                'setServerEncoding',
                'setPassword',
                'enableSslOnlyMode',
                'setAllowedIpMasks',
                'setHeadersLimit',
                'startEvalRequestsListener',
            ))
            ->getMock();

        $connector->expects($this->any())
            ->method('isActiveClient')
            ->will($this->returnValue(true));

        return $connector;
    }

    protected function getHandlerDefaultOption($name)
    {
        $handler = new PHPConsoleHandler(array(), $this->connector);
        $options = $handler->getOptions();

        return $options[$name];
    }

    protected function initLogger($handlerOptions = array(), $level = Logger::DEBUG)
    {
        return new Logger('test', array(
            new PHPConsoleHandler($handlerOptions, $this->connector, $level),
        ));
    }

    public function testInitWithDefaultConnector()
    {
        $handler = new PHPConsoleHandler();
        $this->assertEquals(spl_object_hash(Connector::getInstance()), spl_object_hash($handler->getConnector()));
    }

    public function testInitWithCustomConnector()
    {
        $handler = new PHPConsoleHandler(array(), $this->connector);
        $this->assertEquals(spl_object_hash($this->connector), spl_object_hash($handler->getConnector()));
    }

    public function testDebug()
    {
        $this->debugDispatcher->expects($this->once())->method('dispatchDebug')->with($this->equalTo('test'));
        $this->initLogger()->addDebug('test');
    }

    public function testDebugContextInMessage()
    {
        $message = 'test';
        $tag = 'tag';
        $context = array($tag, 'custom' => mt_rand());
        $expectedMessage = $message . ' ' . json_encode(array_slice($context, 1));
        $this->debugDispatcher->expects($this->once())->method('dispatchDebug')->with(
            $this->equalTo($expectedMessage),
            $this->equalTo($tag)
        );
        $this->initLogger()->addDebug($message, $context);
    }

    public function testDebugTags($tagsContextKeys = null)
    {
        $expectedTags = mt_rand();
        $logger = $this->initLogger($tagsContextKeys ? array('debugTagsKeysInContext' => $tagsContextKeys) : array());
        if (!$tagsContextKeys) {
            $tagsContextKeys = $this->getHandlerDefaultOption('debugTagsKeysInContext');
        }
        foreach ($tagsContextKeys as $key) {
            $debugDispatcher = $this->initDebugDispatcherMock($this->connector);
            $debugDispatcher->expects($this->once())->method('dispatchDebug')->with(
                $this->anything(),
                $this->equalTo($expectedTags)
            );
            $this->connector->setDebugDispatcher($debugDispatcher);
            $logger->addDebug('test', array($key => $expectedTags));
        }
    }

    public function testError($classesPartialsTraceIgnore = null)
    {
        $code = E_USER_NOTICE;
        $message = 'message';
        $file = __FILE__;
        $line = __LINE__;
        $this->errorDispatcher->expects($this->once())->method('dispatchError')->with(
            $this->equalTo($code),
            $this->equalTo($message),
            $this->equalTo($file),
            $this->equalTo($line),
            $classesPartialsTraceIgnore ?: $this->equalTo($this->getHandlerDefaultOption('classesPartialsTraceIgnore'))
        );
        $errorHandler = ErrorHandler::register($this->initLogger($classesPartialsTraceIgnore ? array('classesPartialsTraceIgnore' => $classesPartialsTraceIgnore) : array()), false);
        $errorHandler->registerErrorHandler(array(), false, E_USER_WARNING);
        $errorHandler->handleError($code, $message, $file, $line);
    }

    public function testException()
    {
        $e = new Exception();
        $this->errorDispatcher->expects($this->once())->method('dispatchException')->with(
            $this->equalTo($e)
        );
        $handler = $this->initLogger();
        $handler->log(
            \WPvividPsr\Log\LogLevel::ERROR,
            sprintf('Uncaught Exception %s: "%s" at %s line %s', get_class($e), $e->getMessage(), $e->getFile(), $e->getLine()),
            array('exception' => $e)
        );
    }

    /**
     * @expectedException Exception
     */
    public function testWrongOptionsThrowsException()
    {
        new PHPConsoleHandler(array('xxx' => 1));
    }

    public function testOptionEnabled()
    {
        $this->debugDispatcher->expects($this->never())->method('dispatchDebug');
        $this->initLogger(array('enabled' => false))->addDebug('test');
    }

    public function testOptionClassesPartialsTraceIgnore()
    {
        $this->testError(array('Class', 'Namespace\\'));
    }

    public function testOptionDebugTagsKeysInContext()
    {
        $this->testDebugTags(array('key1', 'key2'));
    }

    public function testOptionUseOwnErrorsAndExceptionsHandler()
    {
        $this->initLogger(array('useOwnErrorsHandler' => true, 'useOwnExceptionsHandler' => true));
        $this->assertEquals(array(Handler::getInstance(), 'handleError'), set_error_handler(function () {
        }));
        $this->assertEquals(array(Handler::getInstance(), 'handleException'), set_exception_handler(function () {
        }));
    }

    public static function provideConnectorMethodsOptionsSets()
    {
        return array(
            array('sourcesBasePath', 'setSourcesBasePath', __DIR__),
            array('serverEncoding', 'setServerEncoding', 'cp1251'),
            array('password', 'setPassword', '******'),
            array('enableSslOnlyMode', 'enableSslOnlyMode', true, false),
            array('ipMasks', 'setAllowedIpMasks', array('127.0.0.*')),
            array('headersLimit', 'setHeadersLimit', 2500),
            array('enableEvalListener', 'startEvalRequestsListener', true, false),
        );
    }

    /**
     * @dataProvider provideConnectorMethodsOptionsSets
     */
    public function testOptionCallsConnectorMethod($option, $method, $value, $isArgument = true)
    {
        $expectCall = $this->connector->expects($this->once())->method($method);
        if ($isArgument) {
            $expectCall->with($value);
        }
        new PHPConsoleHandler(array($option => $value), $this->connector);
    }

    public function testOptionDetectDumpTraceAndSource()
    {
        new PHPConsoleHandler(array('detectDumpTraceAndSource' => true), $this->connector);
        $this->assertTrue($this->connector->getDebugDispatcher()->detectTraceAndSource);
    }

    public static function provideDumperOptionsValues()
    {
        return array(
            array('dumperLevelLimit', 'levelLimit', 1001),
            array('dumperItemsCountLimit', 'itemsCountLimit', 1002),
            array('dumperItemSizeLimit', 'itemSizeLimit', 1003),
            array('dumperDumpSizeLimit', 'dumpSizeLimit', 1004),
            array('dumperDetectCallbacks', 'detectCallbacks', true),
        );
    }

    /**
     * @dataProvider provideDumperOptionsValues
     */
    public function testDumperOptions($option, $dumperProperty, $value)
    {
        new PHPConsoleHandler(array($option => $value), $this->connector);
        $this->assertEquals($value, $this->connector->getDumper()->$dumperProperty);
    }
}
vendor/monolog/monolog/tests/Monolog/Handler/ChromePHPHandlerTest.php000064400000011461151327705700021764 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog\Handler;

use WPvividMonolog\TestCase;
use WPvividMonolog\Logger;

/**
 * @covers Monolog\Handler\ChromePHPHandler
 */
class ChromePHPHandlerTest extends TestCase
{
    protected function setUp()
    {
        TestChromePHPHandler::resetStatic();
        $_SERVER['HTTP_USER_AGENT'] = 'Monolog Test; Chrome/1.0';
    }

    /**
     * @dataProvider agentsProvider
     */
    public function testHeaders($agent)
    {
        $_SERVER['HTTP_USER_AGENT'] = $agent;

        $handler = new TestChromePHPHandler();
        $handler->setFormatter($this->getIdentityFormatter());
        $handler->handle($this->getRecord(Logger::DEBUG));
        $handler->handle($this->getRecord(Logger::WARNING));

        $expected = array(
            'X-ChromeLogger-Data'   => base64_encode(utf8_encode(json_encode(array(
                'version' => ChromePHPHandler::VERSION,
                'columns' => array('label', 'log', 'backtrace', 'type'),
                'rows' => array(
                    'test',
                    'test',
                ),
                'request_uri' => '',
            )))),
        );

        $this->assertEquals($expected, $handler->getHeaders());
    }

    public static function agentsProvider()
    {
        return array(
            array('Monolog Test; Chrome/1.0'),
            array('Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:52.0) Gecko/20100101 Firefox/52.0'),
            array('Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Ubuntu Chromium/56.0.2924.76 Chrome/56.0.2924.76 Safari/537.36'),
            array('Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome Safari/537.36'),
        );
    }

    public function testHeadersOverflow()
    {
        $handler = new TestChromePHPHandler();
        $handler->handle($this->getRecord(Logger::DEBUG));
        $handler->handle($this->getRecord(Logger::WARNING, str_repeat('a', 150 * 1024)));

        // overflow chrome headers limit
        $handler->handle($this->getRecord(Logger::WARNING, str_repeat('a', 100 * 1024)));

        $expected = array(
            'X-ChromeLogger-Data'   => base64_encode(utf8_encode(json_encode(array(
                'version' => ChromePHPHandler::VERSION,
                'columns' => array('label', 'log', 'backtrace', 'type'),
                'rows' => array(
                    array(
                        'test',
                        'test',
                        'unknown',
                        'log',
                    ),
                    array(
                        'test',
                        str_repeat('a', 150 * 1024),
                        'unknown',
                        'warn',
                    ),
                    array(
                        'monolog',
                        'Incomplete logs, chrome header size limit reached',
                        'unknown',
                        'warn',
                    ),
                ),
                'request_uri' => '',
            )))),
        );

        $this->assertEquals($expected, $handler->getHeaders());
    }

    public function testConcurrentHandlers()
    {
        $handler = new TestChromePHPHandler();
        $handler->setFormatter($this->getIdentityFormatter());
        $handler->handle($this->getRecord(Logger::DEBUG));
        $handler->handle($this->getRecord(Logger::WARNING));

        $handler2 = new TestChromePHPHandler();
        $handler2->setFormatter($this->getIdentityFormatter());
        $handler2->handle($this->getRecord(Logger::DEBUG));
        $handler2->handle($this->getRecord(Logger::WARNING));

        $expected = array(
            'X-ChromeLogger-Data'   => base64_encode(utf8_encode(json_encode(array(
                'version' => ChromePHPHandler::VERSION,
                'columns' => array('label', 'log', 'backtrace', 'type'),
                'rows' => array(
                    'test',
                    'test',
                    'test',
                    'test',
                ),
                'request_uri' => '',
            )))),
        );

        $this->assertEquals($expected, $handler2->getHeaders());
    }
}

class TestChromePHPHandler extends ChromePHPHandler
{
    protected $headers = array();

    public static function resetStatic()
    {
        self::$initialized = false;
        self::$overflowed = false;
        self::$sendHeaders = true;
        self::$json['rows'] = array();
    }

    protected function sendHeader($header, $content)
    {
        $this->headers[$header] = $content;
    }

    public function getHeaders()
    {
        return $this->headers;
    }
}
vendor/monolog/monolog/tests/Monolog/SignalHandlerTest.php000064400000024047151327705700020043 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog;

use WPvividMonolog\Handler\StreamHandler;
use WPvividMonolog\Handler\TestHandler;
use WPvividPsr\Log\LogLevel;

/**
 * @author Robert Gust-Bardon <robert@gust-bardon.org>
 * @covers Monolog\SignalHandler
 */
class SignalHandlerTest extends TestCase
{

    private $asyncSignalHandling;
    private $blockedSignals;
    private $signalHandlers;

    protected function setUp()
    {
        $this->signalHandlers = array();
        if (extension_loaded('pcntl')) {
            if (function_exists('pcntl_async_signals')) {
                $this->asyncSignalHandling = pcntl_async_signals();
            }
            if (function_exists('pcntl_sigprocmask')) {
                pcntl_sigprocmask(SIG_BLOCK, array(), $this->blockedSignals);
            }
        }
    }

    protected function tearDown()
    {
        if ($this->asyncSignalHandling !== null) {
            pcntl_async_signals($this->asyncSignalHandling);
        }
        if ($this->blockedSignals !== null) {
            pcntl_sigprocmask(SIG_SETMASK, $this->blockedSignals);
        }
        if ($this->signalHandlers) {
            pcntl_signal_dispatch();
            foreach ($this->signalHandlers as $signo => $handler) {
                pcntl_signal($signo, $handler);
            }
        }
    }

    private function setSignalHandler($signo, $handler = SIG_DFL) {
        if (function_exists('pcntl_signal_get_handler')) {
            $this->signalHandlers[$signo] = pcntl_signal_get_handler($signo);
        } else {
            $this->signalHandlers[$signo] = SIG_DFL;
        }
        $this->assertTrue(pcntl_signal($signo, $handler));
    }

    public function testHandleSignal()
    {
        $logger = new Logger('test', array($handler = new TestHandler));
        $errHandler = new SignalHandler($logger);
        $signo = 2;  // SIGINT.
        $siginfo = array('signo' => $signo, 'errno' => 0, 'code' => 0);
        $errHandler->handleSignal($signo, $siginfo);
        $this->assertCount(1, $handler->getRecords());
        $this->assertTrue($handler->hasCriticalRecords());
        $records = $handler->getRecords();
        $this->assertSame($siginfo, $records[0]['context']);
    }

    /**
     * @depends testHandleSignal
     * @requires extension pcntl
     * @requires extension posix
     * @requires function pcntl_signal
     * @requires function pcntl_signal_dispatch
     * @requires function posix_getpid
     * @requires function posix_kill
     */
    public function testRegisterSignalHandler()
    {
        // SIGCONT and SIGURG should be ignored by default.
        if (!defined('SIGCONT') || !defined('SIGURG')) {
            $this->markTestSkipped('This test requires the SIGCONT and SIGURG pcntl constants.');
        }

        $this->setSignalHandler(SIGCONT, SIG_IGN);
        $this->setSignalHandler(SIGURG, SIG_IGN);

        $logger = new Logger('test', array($handler = new TestHandler));
        $errHandler = new SignalHandler($logger);
        $pid = posix_getpid();

        $this->assertTrue(posix_kill($pid, SIGURG));
        $this->assertTrue(pcntl_signal_dispatch());
        $this->assertCount(0, $handler->getRecords());

        $errHandler->registerSignalHandler(SIGURG, LogLevel::INFO, false, false, false);

        $this->assertTrue(posix_kill($pid, SIGCONT));
        $this->assertTrue(pcntl_signal_dispatch());
        $this->assertCount(0, $handler->getRecords());

        $this->assertTrue(posix_kill($pid, SIGURG));
        $this->assertTrue(pcntl_signal_dispatch());
        $this->assertCount(1, $handler->getRecords());
        $this->assertTrue($handler->hasInfoThatContains('SIGURG'));
    }

    /**
     * @dataProvider defaultPreviousProvider
     * @depends testRegisterSignalHandler
     * @requires function pcntl_fork
     * @requires function pcntl_sigprocmask
     * @requires function pcntl_waitpid
     */
    public function testRegisterDefaultPreviousSignalHandler($signo, $callPrevious, $expected)
    {
        $this->setSignalHandler($signo, SIG_DFL);

        $path = tempnam(sys_get_temp_dir(), 'monolog-');
        $this->assertNotFalse($path);

        $pid = pcntl_fork();
        if ($pid === 0) {  // Child.
            $streamHandler = new StreamHandler($path);
            $streamHandler->setFormatter($this->getIdentityFormatter());
            $logger = new Logger('test', array($streamHandler));
            $errHandler = new SignalHandler($logger);
            $errHandler->registerSignalHandler($signo, LogLevel::INFO, $callPrevious, false, false);
            pcntl_sigprocmask(SIG_SETMASK, array(SIGCONT));
            posix_kill(posix_getpid(), $signo);
            pcntl_signal_dispatch();
            // If $callPrevious is true, SIGINT should terminate by this line.
            pcntl_sigprocmask(SIG_BLOCK, array(), $oldset);
            file_put_contents($path, implode(' ', $oldset), FILE_APPEND);
            posix_kill(posix_getpid(), $signo);
            pcntl_signal_dispatch();
            exit();
        }

        $this->assertNotSame(-1, $pid);
        $this->assertNotSame(-1, pcntl_waitpid($pid, $status));
        $this->assertNotSame(-1, $status);
        $this->assertSame($expected, file_get_contents($path));
    }

    public function defaultPreviousProvider()
    {
        if (!defined('SIGCONT') || !defined('SIGINT') || !defined('SIGURG')) {
            return array();
        }

        return array(
            array(SIGINT, false, 'Program received signal SIGINT'.SIGCONT.'Program received signal SIGINT'),
            array(SIGINT, true, 'Program received signal SIGINT'),
            array(SIGURG, false, 'Program received signal SIGURG'.SIGCONT.'Program received signal SIGURG'),
            array(SIGURG, true, 'Program received signal SIGURG'.SIGCONT.'Program received signal SIGURG'),
        );
    }

    /**
     * @dataProvider callablePreviousProvider
     * @depends testRegisterSignalHandler
     * @requires function pcntl_signal_get_handler
     */
    public function testRegisterCallablePreviousSignalHandler($callPrevious)
    {
        $this->setSignalHandler(SIGURG, SIG_IGN);

        $logger = new Logger('test', array($handler = new TestHandler));
        $errHandler = new SignalHandler($logger);
        $previousCalled = 0;
        pcntl_signal(SIGURG, function ($signo, array $siginfo = null) use (&$previousCalled) {
            ++$previousCalled;
        });
        $errHandler->registerSignalHandler(SIGURG, LogLevel::INFO, $callPrevious, false, false);
        $this->assertTrue(posix_kill(posix_getpid(), SIGURG));
        $this->assertTrue(pcntl_signal_dispatch());
        $this->assertCount(1, $handler->getRecords());
        $this->assertTrue($handler->hasInfoThatContains('SIGURG'));
        $this->assertSame($callPrevious ? 1 : 0, $previousCalled);
    }

    public function callablePreviousProvider()
    {
        return array(
            array(false),
            array(true),
        );
    }

    /**
     * @dataProvider restartSyscallsProvider
     * @depends testRegisterDefaultPreviousSignalHandler
     * @requires function pcntl_fork
     * @requires function pcntl_waitpid
     */
    public function testRegisterSyscallRestartingSignalHandler($restartSyscalls)
    {
        $this->setSignalHandler(SIGURG, SIG_IGN);

        $parentPid = posix_getpid();
        $microtime = microtime(true);

        $pid = pcntl_fork();
        if ($pid === 0) {  // Child.
            usleep(100000);
            posix_kill($parentPid, SIGURG);
            usleep(100000);
            exit();
        }

        $this->assertNotSame(-1, $pid);
        $logger = new Logger('test', array($handler = new TestHandler));
        $errHandler = new SignalHandler($logger);
        $errHandler->registerSignalHandler(SIGURG, LogLevel::INFO, false, $restartSyscalls, false);
        if ($restartSyscalls) {
            // pcntl_wait is expected to be restarted after the signal handler.
            $this->assertNotSame(-1, pcntl_waitpid($pid, $status));
        } else {
            // pcntl_wait is expected to be interrupted when the signal handler is invoked.
            $this->assertSame(-1, pcntl_waitpid($pid, $status));
        }
        $this->assertSame($restartSyscalls, microtime(true) - $microtime > 0.15);
        $this->assertTrue(pcntl_signal_dispatch());
        $this->assertCount(1, $handler->getRecords());
        if ($restartSyscalls) {
            // The child has already exited.
            $this->assertSame(-1, pcntl_waitpid($pid, $status));
        } else {
            // The child has not exited yet.
            $this->assertNotSame(-1, pcntl_waitpid($pid, $status));
        }
    }

    public function restartSyscallsProvider()
    {
        return array(
            array(false),
            array(true),
            array(false),
            array(true),
        );
    }

    /**
     * @dataProvider asyncProvider
     * @depends testRegisterDefaultPreviousSignalHandler
     * @requires function pcntl_async_signals
     */
    public function testRegisterAsyncSignalHandler($initialAsync, $desiredAsync, $expectedBefore, $expectedAfter)
    {
        $this->setSignalHandler(SIGURG, SIG_IGN);
        pcntl_async_signals($initialAsync);

        $logger = new Logger('test', array($handler = new TestHandler));
        $errHandler = new SignalHandler($logger);
        $errHandler->registerSignalHandler(SIGURG, LogLevel::INFO, false, false, $desiredAsync);
        $this->assertTrue(posix_kill(posix_getpid(), SIGURG));
        $this->assertCount($expectedBefore, $handler->getRecords());
        $this->assertTrue(pcntl_signal_dispatch());
        $this->assertCount($expectedAfter, $handler->getRecords());
    }

    public function asyncProvider()
    {
        return array(
            array(false, false, 0, 1),
            array(false, null, 0, 1),
            array(false, true, 1, 1),
            array(true, false, 0, 1),
            array(true, null, 1, 1),
            array(true, true, 1, 1),
        );
    }

}
vendor/monolog/monolog/tests/Monolog/TestCase.php000064400000003173151327705700016200 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog;

class TestCase extends \PHPUnit_Framework_TestCase
{
    /**
     * @return array Record
     */
    protected function getRecord($level = Logger::WARNING, $message = 'test', $context = array())
    {
        return array(
            'message' => $message,
            'context' => $context,
            'level' => $level,
            'level_name' => Logger::getLevelName($level),
            'channel' => 'test',
            'datetime' => \DateTime::createFromFormat('U.u', sprintf('%.6F', microtime(true))),
            'extra' => array(),
        );
    }

    /**
     * @return array
     */
    protected function getMultipleRecords()
    {
        return array(
            $this->getRecord(Logger::DEBUG, 'debug message 1'),
            $this->getRecord(Logger::DEBUG, 'debug message 2'),
            $this->getRecord(Logger::INFO, 'information'),
            $this->getRecord(Logger::WARNING, 'warning'),
            $this->getRecord(Logger::ERROR, 'error'),
        );
    }

    /**
     * @return Monolog\Formatter\FormatterInterface
     */
    protected function getIdentityFormatter()
    {
        $formatter = $this->getMock('WPvividMonolog\\Formatter\\FormatterInterface');
        $formatter->expects($this->any())
            ->method('format')
            ->will($this->returnCallback(function ($record) { return $record['message']; }));

        return $formatter;
    }
}
vendor/monolog/monolog/tests/Monolog/Processor/IntrospectionProcessorTest.php000064400000006155151327705700024047 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Acme;

class Tester
{
    public function test($handler, $record)
    {
        $handler->handle($record);
    }
}

function tester($handler, $record)
{
    $handler->handle($record);
}

namespace WPvividMonolog\Processor;

use WPvividMonolog\Logger;
use WPvividMonolog\TestCase;
use WPvividMonolog\Handler\TestHandler;

class IntrospectionProcessorTest extends TestCase
{
    public function getHandler()
    {
        $processor = new IntrospectionProcessor();
        $handler = new TestHandler();
        $handler->pushProcessor($processor);

        return $handler;
    }

    public function testProcessorFromClass()
    {
        $handler = $this->getHandler();
        $tester = new \Acme\Tester;
        $tester->test($handler, $this->getRecord());
        list($record) = $handler->getRecords();
        $this->assertEquals(__FILE__, $record['extra']['file']);
        $this->assertEquals(18, $record['extra']['line']);
        $this->assertEquals('Acme\Tester', $record['extra']['class']);
        $this->assertEquals('test', $record['extra']['function']);
    }

    public function testProcessorFromFunc()
    {
        $handler = $this->getHandler();
        \Acme\tester($handler, $this->getRecord());
        list($record) = $handler->getRecords();
        $this->assertEquals(__FILE__, $record['extra']['file']);
        $this->assertEquals(24, $record['extra']['line']);
        $this->assertEquals(null, $record['extra']['class']);
        $this->assertEquals('Acme\tester', $record['extra']['function']);
    }

    public function testLevelTooLow()
    {
        $input = array(
            'level' => Logger::DEBUG,
            'extra' => array(),
        );

        $expected = $input;

        $processor = new IntrospectionProcessor(Logger::CRITICAL);
        $actual = $processor($input);

        $this->assertEquals($expected, $actual);
    }

    public function testLevelEqual()
    {
        $input = array(
            'level' => Logger::CRITICAL,
            'extra' => array(),
        );

        $expected = $input;
        $expected['extra'] = array(
            'file' => null,
            'line' => null,
            'class' => 'ReflectionMethod',
            'function' => 'invokeArgs',
        );

        $processor = new IntrospectionProcessor(Logger::CRITICAL);
        $actual = $processor($input);

        $this->assertEquals($expected, $actual);
    }

    public function testLevelHigher()
    {
        $input = array(
            'level' => Logger::EMERGENCY,
            'extra' => array(),
        );

        $expected = $input;
        $expected['extra'] = array(
            'file' => null,
            'line' => null,
            'class' => 'ReflectionMethod',
            'function' => 'invokeArgs',
        );

        $processor = new IntrospectionProcessor(Logger::CRITICAL);
        $actual = $processor($input);

        $this->assertEquals($expected, $actual);
    }
}
vendor/monolog/monolog/tests/Monolog/Processor/MemoryUsageProcessorTest.php000064400000002462151327705700023441 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog\Processor;

use WPvividMonolog\TestCase;

class MemoryUsageProcessorTest extends TestCase
{
    /**
     * @covers Monolog\Processor\MemoryUsageProcessor::__invoke
     * @covers Monolog\Processor\MemoryProcessor::formatBytes
     */
    public function testProcessor()
    {
        $processor = new MemoryUsageProcessor();
        $record = $processor($this->getRecord());
        $this->assertArrayHasKey('memory_usage', $record['extra']);
        $this->assertRegExp('#[0-9.]+ (M|K)?B$#', $record['extra']['memory_usage']);
    }

    /**
     * @covers Monolog\Processor\MemoryUsageProcessor::__invoke
     * @covers Monolog\Processor\MemoryProcessor::formatBytes
     */
    public function testProcessorWithoutFormatting()
    {
        $processor = new MemoryUsageProcessor(true, false);
        $record = $processor($this->getRecord());
        $this->assertArrayHasKey('memory_usage', $record['extra']);
        $this->assertInternalType('int', $record['extra']['memory_usage']);
        $this->assertGreaterThan(0, $record['extra']['memory_usage']);
    }
}
vendor/monolog/monolog/tests/Monolog/Processor/GitProcessorTest.php000064400000001263151327705700021725 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog\Processor;

use WPvividMonolog\TestCase;

class GitProcessorTest extends TestCase
{
    /**
     * @covers Monolog\Processor\GitProcessor::__invoke
     */
    public function testProcessor()
    {
        $processor = new GitProcessor();
        $record = $processor($this->getRecord());

        $this->assertArrayHasKey('git', $record['extra']);
        $this->assertTrue(!is_array($record['extra']['git']['branch']));
    }
}
vendor/monolog/monolog/tests/Monolog/Processor/UidProcessorTest.php000064400000001401151327705700021715 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog\Processor;

use WPvividMonolog\TestCase;

class UidProcessorTest extends TestCase
{
    /**
     * @covers Monolog\Processor\UidProcessor::__invoke
     */
    public function testProcessor()
    {
        $processor = new UidProcessor();
        $record = $processor($this->getRecord());
        $this->assertArrayHasKey('uid', $record['extra']);
    }

    public function testGetUid()
    {
        $processor = new UidProcessor(10);
        $this->assertEquals(10, strlen($processor->getUid()));
    }
}
vendor/monolog/monolog/tests/Monolog/Processor/MemoryPeakUsageProcessorTest.php000064400000002537151327705700024245 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog\Processor;

use WPvividMonolog\TestCase;

class MemoryPeakUsageProcessorTest extends TestCase
{
    /**
     * @covers Monolog\Processor\MemoryPeakUsageProcessor::__invoke
     * @covers Monolog\Processor\MemoryProcessor::formatBytes
     */
    public function testProcessor()
    {
        $processor = new MemoryPeakUsageProcessor();
        $record = $processor($this->getRecord());
        $this->assertArrayHasKey('memory_peak_usage', $record['extra']);
        $this->assertRegExp('#[0-9.]+ (M|K)?B$#', $record['extra']['memory_peak_usage']);
    }

    /**
     * @covers Monolog\Processor\MemoryPeakUsageProcessor::__invoke
     * @covers Monolog\Processor\MemoryProcessor::formatBytes
     */
    public function testProcessorWithoutFormatting()
    {
        $processor = new MemoryPeakUsageProcessor(true, false);
        $record = $processor($this->getRecord());
        $this->assertArrayHasKey('memory_peak_usage', $record['extra']);
        $this->assertInternalType('int', $record['extra']['memory_peak_usage']);
        $this->assertGreaterThan(0, $record['extra']['memory_peak_usage']);
    }
}
vendor/monolog/monolog/tests/Monolog/Processor/TagProcessorTest.php000064400000002525151327705700021717 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog\Processor;

use WPvividMonolog\TestCase;

class TagProcessorTest extends TestCase
{
    /**
     * @covers Monolog\Processor\TagProcessor::__invoke
     */
    public function testProcessor()
    {
        $tags = array(1, 2, 3);
        $processor = new TagProcessor($tags);
        $record = $processor($this->getRecord());

        $this->assertEquals($tags, $record['extra']['tags']);
    }

    /**
     * @covers Monolog\Processor\TagProcessor::__invoke
     */
    public function testProcessorTagModification()
    {
        $tags = array(1, 2, 3);
        $processor = new TagProcessor($tags);

        $record = $processor($this->getRecord());
        $this->assertEquals($tags, $record['extra']['tags']);

        $processor->setTags(array('a', 'b'));
        $record = $processor($this->getRecord());
        $this->assertEquals(array('a', 'b'), $record['extra']['tags']);

        $processor->addTags(array('a', 'c', 'foo' => 'bar'));
        $record = $processor($this->getRecord());
        $this->assertEquals(array('a', 'b', 'a', 'c', 'foo' => 'bar'), $record['extra']['tags']);
    }
}
vendor/monolog/monolog/tests/Monolog/Processor/MercurialProcessorTest.php000064400000002152151327705700023123 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jonathan A. Schweder <jonathanschweder@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog\Processor;

use WPvividMonolog\TestCase;

class MercurialProcessorTest extends TestCase
{
    /**
     * @covers Monolog\Processor\MercurialProcessor::__invoke
     */
    public function testProcessor()
    {
        if (defined('PHP_WINDOWS_VERSION_BUILD')) {
            exec("where hg 2>NUL", $output, $result);
        } else {
            exec("which hg 2>/dev/null >/dev/null", $output, $result);
        }
        if ($result != 0) {
            $this->markTestSkipped('hg is missing');
            return;
        }

        `hg init`;
        $processor = new MercurialProcessor();
        $record = $processor($this->getRecord());

        $this->assertArrayHasKey('hg', $record['extra']);
        $this->assertTrue(!is_array($record['extra']['hg']['branch']));
        $this->assertTrue(!is_array($record['extra']['hg']['revision']));
    }
}
vendor/monolog/monolog/tests/Monolog/Processor/PsrLogMessageProcessorTest.php000064400000002047151327705700023716 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog\Processor;

class PsrLogMessageProcessorTest extends \PHPUnit_Framework_TestCase
{
    /**
     * @dataProvider getPairs
     */
    public function testReplacement($val, $expected)
    {
        $proc = new PsrLogMessageProcessor;

        $message = $proc(array(
            'message' => '{foo}',
            'context' => array('foo' => $val),
        ));
        $this->assertEquals($expected, $message['message']);
    }

    public function getPairs()
    {
        return array(
            array('foo',    'foo'),
            array('3',      '3'),
            array(3,        '3'),
            array(null,     ''),
            array(true,     '1'),
            array(false,    ''),
            array(new \stdClass, '[object stdClass]'),
            array(array(), '[array]'),
        );
    }
}
vendor/monolog/monolog/tests/Monolog/Processor/WebProcessorTest.php000064400000006654151327705700021730 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog\Processor;

use WPvividMonolog\TestCase;

class WebProcessorTest extends TestCase
{
    public function testProcessor()
    {
        $server = array(
            'REQUEST_URI'    => 'A',
            'REMOTE_ADDR'    => 'B',
            'REQUEST_METHOD' => 'C',
            'HTTP_REFERER'   => 'D',
            'SERVER_NAME'    => 'F',
            'UNIQUE_ID'      => 'G',
        );

        $processor = new WebProcessor($server);
        $record = $processor($this->getRecord());
        $this->assertEquals($server['REQUEST_URI'], $record['extra']['url']);
        $this->assertEquals($server['REMOTE_ADDR'], $record['extra']['ip']);
        $this->assertEquals($server['REQUEST_METHOD'], $record['extra']['http_method']);
        $this->assertEquals($server['HTTP_REFERER'], $record['extra']['referrer']);
        $this->assertEquals($server['SERVER_NAME'], $record['extra']['server']);
        $this->assertEquals($server['UNIQUE_ID'], $record['extra']['unique_id']);
    }

    public function testProcessorDoNothingIfNoRequestUri()
    {
        $server = array(
            'REMOTE_ADDR'    => 'B',
            'REQUEST_METHOD' => 'C',
        );
        $processor = new WebProcessor($server);
        $record = $processor($this->getRecord());
        $this->assertEmpty($record['extra']);
    }

    public function testProcessorReturnNullIfNoHttpReferer()
    {
        $server = array(
            'REQUEST_URI'    => 'A',
            'REMOTE_ADDR'    => 'B',
            'REQUEST_METHOD' => 'C',
            'SERVER_NAME'    => 'F',
        );
        $processor = new WebProcessor($server);
        $record = $processor($this->getRecord());
        $this->assertNull($record['extra']['referrer']);
    }

    public function testProcessorDoesNotAddUniqueIdIfNotPresent()
    {
        $server = array(
            'REQUEST_URI'    => 'A',
            'REMOTE_ADDR'    => 'B',
            'REQUEST_METHOD' => 'C',
            'SERVER_NAME'    => 'F',
        );
        $processor = new WebProcessor($server);
        $record = $processor($this->getRecord());
        $this->assertFalse(isset($record['extra']['unique_id']));
    }

    public function testProcessorAddsOnlyRequestedExtraFields()
    {
        $server = array(
            'REQUEST_URI'    => 'A',
            'REMOTE_ADDR'    => 'B',
            'REQUEST_METHOD' => 'C',
            'SERVER_NAME'    => 'F',
        );

        $processor = new WebProcessor($server, array('url', 'http_method'));
        $record = $processor($this->getRecord());

        $this->assertSame(array('url' => 'A', 'http_method' => 'C'), $record['extra']);
    }

    public function testProcessorConfiguringOfExtraFields()
    {
        $server = array(
            'REQUEST_URI'    => 'A',
            'REMOTE_ADDR'    => 'B',
            'REQUEST_METHOD' => 'C',
            'SERVER_NAME'    => 'F',
        );

        $processor = new WebProcessor($server, array('url' => 'REMOTE_ADDR'));
        $record = $processor($this->getRecord());

        $this->assertSame(array('url' => 'B'), $record['extra']);
    }

    /**
     * @expectedException UnexpectedValueException
     */
    public function testInvalidData()
    {
        new WebProcessor(new \stdClass);
    }
}
vendor/monolog/monolog/tests/Monolog/Processor/ProcessIdProcessorTest.php000064400000001532151327705700023074 0ustar00<?php

/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace WPvividMonolog\Processor;

use WPvividMonolog\TestCase;

class ProcessIdProcessorTest extends TestCase
{
    /**
     * @covers Monolog\Processor\ProcessIdProcessor::__invoke
     */
    public function testProcessor()
    {
        $processor = new ProcessIdProcessor();
        $record = $processor($this->getRecord());
        $this->assertArrayHasKey('process_id', $record['extra']);
        $this->assertInternalType('int', $record['extra']['process_id']);
        $this->assertGreaterThan(0, $record['extra']['process_id']);
        $this->assertEquals(getmypid(), $record['extra']['process_id']);
    }
}
vendor/monolog/monolog/composer.json000064400000004747151327705700013732 0ustar00{
    "name": "monolog/monolog",
    "description": "Sends your logs to files, sockets, inboxes, databases and various web services",
    "keywords": ["log", "logging", "psr-3"],
    "homepage": "http://github.com/Seldaek/monolog",
    "type": "library",
    "license": "MIT",
    "authors": [
        {
            "name": "Jordi Boggiano",
            "email": "j.boggiano@seld.be",
            "homepage": "http://seld.be"
        }
    ],
    "require": {
        "php": ">=5.3.0",
        "psr/log": "~1.0"
    },
    "require-dev": {
        "phpunit/phpunit": "~4.5",
        "graylog2/gelf-php": "~1.0",
        "sentry/sentry": "^0.13",
        "ruflin/elastica": ">=0.90 <3.0",
        "doctrine/couchdb": "~1.0@dev",
        "aws/aws-sdk-php": "^2.4.9 || ^3.0",
        "php-amqplib/php-amqplib": "~2.4",
        "swiftmailer/swiftmailer": "^5.3|^6.0",
        "php-console/php-console": "^3.1.3",
        "phpunit/phpunit-mock-objects": "2.3.0",
        "jakub-onderka/php-parallel-lint": "0.9"
    },
    "_": "phpunit/phpunit-mock-objects required in 2.3.0 due to https://github.com/sebastianbergmann/phpunit-mock-objects/issues/223 - needs hhvm 3.8+ on travis",
    "suggest": {
        "graylog2/gelf-php": "Allow sending log messages to a GrayLog2 server",
        "sentry/sentry": "Allow sending log messages to a Sentry server",
        "doctrine/couchdb": "Allow sending log messages to a CouchDB server",
        "ruflin/elastica": "Allow sending log messages to an Elastic Search server",
        "php-amqplib/php-amqplib": "Allow sending log messages to an AMQP server using php-amqplib",
        "ext-amqp": "Allow sending log messages to an AMQP server (1.0+ required)",
        "ext-mongo": "Allow sending log messages to a MongoDB server",
        "mongodb/mongodb": "Allow sending log messages to a MongoDB server via PHP Driver",
        "aws/aws-sdk-php": "Allow sending log messages to AWS services like DynamoDB",
        "rollbar/rollbar": "Allow sending log messages to Rollbar",
        "php-console/php-console": "Allow sending log messages to Google Chrome"
    },
    "autoload": {
        "psr-4": {"Monolog\\": "src/Monolog"}
    },
    "autoload-dev": {
        "psr-4": {"Monolog\\": "tests/Monolog"}
    },
    "provide": {
        "psr/log-implementation": "1.0.0"
    },
    "extra": {
        "branch-alias": {
            "dev-master": "2.0.x-dev"
        }
    },
    "scripts": {
        "test": [
            "parallel-lint . --exclude vendor",
            "phpunit"
        ]
    }
}
vendor/symfony/event-dispatcher/EventDispatcherInterface.php000064400000005310151327705700020442 0ustar00<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\EventDispatcher;

/**
 * The EventDispatcherInterface is the central point of Symfony's event listener system.
 * Listeners are registered on the manager and events are dispatched through the
 * manager.
 *
 * @author Bernhard Schussek <bschussek@gmail.com>
 */
interface EventDispatcherInterface
{
    /**
     * Dispatches an event to all registered listeners.
     *
     * @param string $eventName The name of the event to dispatch. The name of
     *                          the event is the name of the method that is
     *                          invoked on listeners.
     * @param Event  $event     The event to pass to the event handlers/listeners
     *                          If not supplied, an empty Event instance is created
     *
     * @return Event
     */
    public function dispatch($eventName, Event $event = null);

    /**
     * Adds an event listener that listens on the specified events.
     *
     * @param string   $eventName The event to listen on
     * @param callable $listener  The listener
     * @param int      $priority  The higher this value, the earlier an event
     *                            listener will be triggered in the chain (defaults to 0)
     */
    public function addListener($eventName, $listener, $priority = 0);

    /**
     * Adds an event subscriber.
     *
     * The subscriber is asked for all the events he is
     * interested in and added as a listener for these events.
     */
    public function addSubscriber(EventSubscriberInterface $subscriber);

    /**
     * Removes an event listener from the specified events.
     *
     * @param string   $eventName The event to remove a listener from
     * @param callable $listener  The listener to remove
     */
    public function removeListener($eventName, $listener);

    public function removeSubscriber(EventSubscriberInterface $subscriber);

    /**
     * Gets the listeners of a specific event or all listeners sorted by descending priority.
     *
     * @param string $eventName The name of the event
     *
     * @return array The event listeners for the specified event, or all event listeners by event name
     */
    public function getListeners($eventName = null);

    /**
     * Checks whether an event has any registered listeners.
     *
     * @param string $eventName The name of the event
     *
     * @return bool true if the specified event has any listeners, false otherwise
     */
    public function hasListeners($eventName = null);
}
vendor/symfony/event-dispatcher/composer.json000064400000002157151327705700015550 0ustar00{
    "name": "symfony/event-dispatcher",
    "type": "library",
    "description": "Symfony EventDispatcher Component",
    "keywords": [],
    "homepage": "https://symfony.com",
    "license": "MIT",
    "authors": [
        {
            "name": "Fabien Potencier",
            "email": "fabien@symfony.com"
        },
        {
            "name": "Symfony Community",
            "homepage": "https://symfony.com/contributors"
        }
    ],
    "require": {
        "php": ">=5.3.9"
    },
    "require-dev": {
        "symfony/dependency-injection": "~2.6|~3.0.0",
        "symfony/expression-language": "~2.6|~3.0.0",
        "symfony/config": "^2.0.5|~3.0.0",
        "symfony/stopwatch": "~2.3|~3.0.0",
        "psr/log": "~1.0"
    },
    "suggest": {
        "symfony/dependency-injection": "",
        "symfony/http-kernel": ""
    },
    "autoload": {
        "psr-4": { "Symfony\\Component\\EventDispatcher\\": "" },
        "exclude-from-classmap": [
            "/Tests/"
        ]
    },
    "minimum-stability": "dev",
    "extra": {
        "branch-alias": {
            "dev-master": "2.8-dev"
        }
    }
}
vendor/symfony/event-dispatcher/ContainerAwareEventDispatcher.php000064400000013657151327705700021461 0ustar00<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\EventDispatcher;

use Symfony\Component\DependencyInjection\ContainerInterface;

/**
 * Lazily loads listeners and subscribers from the dependency injection
 * container.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 * @author Bernhard Schussek <bschussek@gmail.com>
 * @author Jordan Alliot <jordan.alliot@gmail.com>
 */
class ContainerAwareEventDispatcher extends EventDispatcher
{
    private $container;

    /**
     * The service IDs of the event listeners and subscribers.
     */
    private $listenerIds = array();

    /**
     * The services registered as listeners.
     */
    private $listeners = array();

    public function __construct(ContainerInterface $container)
    {
        $this->container = $container;
    }

    /**
     * Adds a service as event listener.
     *
     * @param string $eventName Event for which the listener is added
     * @param array  $callback  The service ID of the listener service & the method
     *                          name that has to be called
     * @param int    $priority  The higher this value, the earlier an event listener
     *                          will be triggered in the chain.
     *                          Defaults to 0.
     *
     * @throws \InvalidArgumentException
     */
    public function addListenerService($eventName, $callback, $priority = 0)
    {
        if (!\is_array($callback) || 2 !== \count($callback)) {
            throw new \InvalidArgumentException('Expected an array("service", "method") argument');
        }

        $this->listenerIds[$eventName][] = array($callback[0], $callback[1], $priority);
    }

    public function removeListener($eventName, $listener)
    {
        $this->lazyLoad($eventName);

        if (isset($this->listenerIds[$eventName])) {
            foreach ($this->listenerIds[$eventName] as $i => $args) {
                list($serviceId, $method) = $args;
                $key = $serviceId.'.'.$method;
                if (isset($this->listeners[$eventName][$key]) && $listener === array($this->listeners[$eventName][$key], $method)) {
                    unset($this->listeners[$eventName][$key]);
                    if (empty($this->listeners[$eventName])) {
                        unset($this->listeners[$eventName]);
                    }
                    unset($this->listenerIds[$eventName][$i]);
                    if (empty($this->listenerIds[$eventName])) {
                        unset($this->listenerIds[$eventName]);
                    }
                }
            }
        }

        parent::removeListener($eventName, $listener);
    }

    /**
     * {@inheritdoc}
     */
    public function hasListeners($eventName = null)
    {
        if (null === $eventName) {
            return $this->listenerIds || $this->listeners || parent::hasListeners();
        }

        if (isset($this->listenerIds[$eventName])) {
            return true;
        }

        return parent::hasListeners($eventName);
    }

    /**
     * {@inheritdoc}
     */
    public function getListeners($eventName = null)
    {
        if (null === $eventName) {
            foreach ($this->listenerIds as $serviceEventName => $args) {
                $this->lazyLoad($serviceEventName);
            }
        } else {
            $this->lazyLoad($eventName);
        }

        return parent::getListeners($eventName);
    }

    /**
     * {@inheritdoc}
     */
    public function getListenerPriority($eventName, $listener)
    {
        $this->lazyLoad($eventName);

        return parent::getListenerPriority($eventName, $listener);
    }

    /**
     * Adds a service as event subscriber.
     *
     * @param string $serviceId The service ID of the subscriber service
     * @param string $class     The service's class name (which must implement EventSubscriberInterface)
     */
    public function addSubscriberService($serviceId, $class)
    {
        foreach ($class::getSubscribedEvents() as $eventName => $params) {
            if (\is_string($params)) {
                $this->listenerIds[$eventName][] = array($serviceId, $params, 0);
            } elseif (\is_string($params[0])) {
                $this->listenerIds[$eventName][] = array($serviceId, $params[0], isset($params[1]) ? $params[1] : 0);
            } else {
                foreach ($params as $listener) {
                    $this->listenerIds[$eventName][] = array($serviceId, $listener[0], isset($listener[1]) ? $listener[1] : 0);
                }
            }
        }
    }

    public function getContainer()
    {
        return $this->container;
    }

    /**
     * Lazily loads listeners for this event from the dependency injection
     * container.
     *
     * @param string $eventName The name of the event to dispatch. The name of
     *                          the event is the name of the method that is
     *                          invoked on listeners.
     */
    protected function lazyLoad($eventName)
    {
        if (isset($this->listenerIds[$eventName])) {
            foreach ($this->listenerIds[$eventName] as $args) {
                list($serviceId, $method, $priority) = $args;
                $listener = $this->container->get($serviceId);

                $key = $serviceId.'.'.$method;
                if (!isset($this->listeners[$eventName][$key])) {
                    $this->addListener($eventName, array($listener, $method), $priority);
                } elseif ($this->listeners[$eventName][$key] !== $listener) {
                    parent::removeListener($eventName, array($this->listeners[$eventName][$key], $method));
                    $this->addListener($eventName, array($listener, $method), $priority);
                }

                $this->listeners[$eventName][$key] = $listener;
            }
        }
    }
}
vendor/symfony/event-dispatcher/Tests/ImmutableEventDispatcherTest.php000064400000005644151327705700022435 0ustar00<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\EventDispatcher\Tests;

use PHPUnit\Framework\TestCase;
use Symfony\Component\EventDispatcher\Event;
use Symfony\Component\EventDispatcher\ImmutableEventDispatcher;

/**
 * @author Bernhard Schussek <bschussek@gmail.com>
 */
class ImmutableEventDispatcherTest extends TestCase
{
    /**
     * @var \PHPUnit_Framework_MockObject_MockObject
     */
    private $innerDispatcher;

    /**
     * @var ImmutableEventDispatcher
     */
    private $dispatcher;

    protected function setUp()
    {
        $this->innerDispatcher = $this->getMockBuilder('Symfony\Component\EventDispatcher\EventDispatcherInterface')->getMock();
        $this->dispatcher = new ImmutableEventDispatcher($this->innerDispatcher);
    }

    public function testDispatchDelegates()
    {
        $event = new Event();

        $this->innerDispatcher->expects($this->once())
            ->method('dispatch')
            ->with('event', $event)
            ->will($this->returnValue('result'));

        $this->assertSame('result', $this->dispatcher->dispatch('event', $event));
    }

    public function testGetListenersDelegates()
    {
        $this->innerDispatcher->expects($this->once())
            ->method('getListeners')
            ->with('event')
            ->will($this->returnValue('result'));

        $this->assertSame('result', $this->dispatcher->getListeners('event'));
    }

    public function testHasListenersDelegates()
    {
        $this->innerDispatcher->expects($this->once())
            ->method('hasListeners')
            ->with('event')
            ->will($this->returnValue('result'));

        $this->assertSame('result', $this->dispatcher->hasListeners('event'));
    }

    /**
     * @expectedException \BadMethodCallException
     */
    public function testAddListenerDisallowed()
    {
        $this->dispatcher->addListener('event', function () { return 'foo'; });
    }

    /**
     * @expectedException \BadMethodCallException
     */
    public function testAddSubscriberDisallowed()
    {
        $subscriber = $this->getMockBuilder('Symfony\Component\EventDispatcher\EventSubscriberInterface')->getMock();

        $this->dispatcher->addSubscriber($subscriber);
    }

    /**
     * @expectedException \BadMethodCallException
     */
    public function testRemoveListenerDisallowed()
    {
        $this->dispatcher->removeListener('event', function () { return 'foo'; });
    }

    /**
     * @expectedException \BadMethodCallException
     */
    public function testRemoveSubscriberDisallowed()
    {
        $subscriber = $this->getMockBuilder('Symfony\Component\EventDispatcher\EventSubscriberInterface')->getMock();

        $this->dispatcher->removeSubscriber($subscriber);
    }
}
vendor/symfony/event-dispatcher/Tests/EventTest.php000064400000004301151327705700016553 0ustar00<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\EventDispatcher\Tests;

use PHPUnit\Framework\TestCase;
use Symfony\Component\EventDispatcher\Event;
use Symfony\Component\EventDispatcher\EventDispatcher;

/**
 * Test class for Event.
 */
class EventTest extends TestCase
{
    /**
     * @var \Symfony\Component\EventDispatcher\Event
     */
    protected $event;

    /**
     * @var \Symfony\Component\EventDispatcher\EventDispatcher
     */
    protected $dispatcher;

    /**
     * Sets up the fixture, for example, opens a network connection.
     * This method is called before a test is executed.
     */
    protected function setUp()
    {
        $this->event = new Event();
        $this->dispatcher = new EventDispatcher();
    }

    /**
     * Tears down the fixture, for example, closes a network connection.
     * This method is called after a test is executed.
     */
    protected function tearDown()
    {
        $this->event = null;
        $this->dispatcher = null;
    }

    public function testIsPropagationStopped()
    {
        $this->assertFalse($this->event->isPropagationStopped());
    }

    public function testStopPropagationAndIsPropagationStopped()
    {
        $this->event->stopPropagation();
        $this->assertTrue($this->event->isPropagationStopped());
    }

    /**
     * @group legacy
     */
    public function testLegacySetDispatcher()
    {
        $this->event->setDispatcher($this->dispatcher);
        $this->assertSame($this->dispatcher, $this->event->getDispatcher());
    }

    /**
     * @group legacy
     */
    public function testLegacyGetDispatcher()
    {
        $this->assertNull($this->event->getDispatcher());
    }

    /**
     * @group legacy
     */
    public function testLegacyGetName()
    {
        $this->assertNull($this->event->getName());
    }

    /**
     * @group legacy
     */
    public function testLegacySetName()
    {
        $this->event->setName('foo');
        $this->assertEquals('foo', $this->event->getName());
    }
}
vendor/symfony/event-dispatcher/Tests/AbstractEventDispatcherTest.php000064400000033400151327705700022250 0ustar00<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\EventDispatcher\Tests;

use PHPUnit\Framework\TestCase;
use Symfony\Component\EventDispatcher\Event;
use Symfony\Component\EventDispatcher\EventDispatcher;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;

abstract class AbstractEventDispatcherTest extends TestCase
{
    /* Some pseudo events */
    const preFoo = 'pre.foo';
    const postFoo = 'post.foo';
    const preBar = 'pre.bar';
    const postBar = 'post.bar';

    /**
     * @var EventDispatcher
     */
    private $dispatcher;

    private $listener;

    protected function setUp()
    {
        $this->dispatcher = $this->createEventDispatcher();
        $this->listener = new TestEventListener();
    }

    protected function tearDown()
    {
        $this->dispatcher = null;
        $this->listener = null;
    }

    abstract protected function createEventDispatcher();

    public function testInitialState()
    {
        $this->assertEquals(array(), $this->dispatcher->getListeners());
        $this->assertFalse($this->dispatcher->hasListeners(self::preFoo));
        $this->assertFalse($this->dispatcher->hasListeners(self::postFoo));
    }

    public function testAddListener()
    {
        $this->dispatcher->addListener('pre.foo', array($this->listener, 'preFoo'));
        $this->dispatcher->addListener('post.foo', array($this->listener, 'postFoo'));
        $this->assertTrue($this->dispatcher->hasListeners());
        $this->assertTrue($this->dispatcher->hasListeners(self::preFoo));
        $this->assertTrue($this->dispatcher->hasListeners(self::postFoo));
        $this->assertCount(1, $this->dispatcher->getListeners(self::preFoo));
        $this->assertCount(1, $this->dispatcher->getListeners(self::postFoo));
        $this->assertCount(2, $this->dispatcher->getListeners());
    }

    public function testGetListenersSortsByPriority()
    {
        $listener1 = new TestEventListener();
        $listener2 = new TestEventListener();
        $listener3 = new TestEventListener();
        $listener1->name = '1';
        $listener2->name = '2';
        $listener3->name = '3';

        $this->dispatcher->addListener('pre.foo', array($listener1, 'preFoo'), -10);
        $this->dispatcher->addListener('pre.foo', array($listener2, 'preFoo'), 10);
        $this->dispatcher->addListener('pre.foo', array($listener3, 'preFoo'));

        $expected = array(
            array($listener2, 'preFoo'),
            array($listener3, 'preFoo'),
            array($listener1, 'preFoo'),
        );

        $this->assertSame($expected, $this->dispatcher->getListeners('pre.foo'));
    }

    public function testGetAllListenersSortsByPriority()
    {
        $listener1 = new TestEventListener();
        $listener2 = new TestEventListener();
        $listener3 = new TestEventListener();
        $listener4 = new TestEventListener();
        $listener5 = new TestEventListener();
        $listener6 = new TestEventListener();

        $this->dispatcher->addListener('pre.foo', $listener1, -10);
        $this->dispatcher->addListener('pre.foo', $listener2);
        $this->dispatcher->addListener('pre.foo', $listener3, 10);
        $this->dispatcher->addListener('post.foo', $listener4, -10);
        $this->dispatcher->addListener('post.foo', $listener5);
        $this->dispatcher->addListener('post.foo', $listener6, 10);

        $expected = array(
            'pre.foo' => array($listener3, $listener2, $listener1),
            'post.foo' => array($listener6, $listener5, $listener4),
        );

        $this->assertSame($expected, $this->dispatcher->getListeners());
    }

    public function testGetListenerPriority()
    {
        $listener1 = new TestEventListener();
        $listener2 = new TestEventListener();

        $this->dispatcher->addListener('pre.foo', $listener1, -10);
        $this->dispatcher->addListener('pre.foo', $listener2);

        $this->assertSame(-10, $this->dispatcher->getListenerPriority('pre.foo', $listener1));
        $this->assertSame(0, $this->dispatcher->getListenerPriority('pre.foo', $listener2));
        $this->assertNull($this->dispatcher->getListenerPriority('pre.bar', $listener2));
        $this->assertNull($this->dispatcher->getListenerPriority('pre.foo', function () {}));
    }

    public function testDispatch()
    {
        $this->dispatcher->addListener('pre.foo', array($this->listener, 'preFoo'));
        $this->dispatcher->addListener('post.foo', array($this->listener, 'postFoo'));
        $this->dispatcher->dispatch(self::preFoo);
        $this->assertTrue($this->listener->preFooInvoked);
        $this->assertFalse($this->listener->postFooInvoked);
        $this->assertInstanceOf('Symfony\Component\EventDispatcher\Event', $this->dispatcher->dispatch('noevent'));
        $this->assertInstanceOf('Symfony\Component\EventDispatcher\Event', $this->dispatcher->dispatch(self::preFoo));
        $event = new Event();
        $return = $this->dispatcher->dispatch(self::preFoo, $event);
        $this->assertSame($event, $return);
    }

    /**
     * @group legacy
     */
    public function testLegacyDispatch()
    {
        $event = new Event();
        $this->dispatcher->dispatch(self::preFoo, $event);
        $this->assertEquals('pre.foo', $event->getName());
    }

    public function testDispatchForClosure()
    {
        $invoked = 0;
        $listener = function () use (&$invoked) {
            ++$invoked;
        };
        $this->dispatcher->addListener('pre.foo', $listener);
        $this->dispatcher->addListener('post.foo', $listener);
        $this->dispatcher->dispatch(self::preFoo);
        $this->assertEquals(1, $invoked);
    }

    public function testStopEventPropagation()
    {
        $otherListener = new TestEventListener();

        // postFoo() stops the propagation, so only one listener should
        // be executed
        // Manually set priority to enforce $this->listener to be called first
        $this->dispatcher->addListener('post.foo', array($this->listener, 'postFoo'), 10);
        $this->dispatcher->addListener('post.foo', array($otherListener, 'postFoo'));
        $this->dispatcher->dispatch(self::postFoo);
        $this->assertTrue($this->listener->postFooInvoked);
        $this->assertFalse($otherListener->postFooInvoked);
    }

    public function testDispatchByPriority()
    {
        $invoked = array();
        $listener1 = function () use (&$invoked) {
            $invoked[] = '1';
        };
        $listener2 = function () use (&$invoked) {
            $invoked[] = '2';
        };
        $listener3 = function () use (&$invoked) {
            $invoked[] = '3';
        };
        $this->dispatcher->addListener('pre.foo', $listener1, -10);
        $this->dispatcher->addListener('pre.foo', $listener2);
        $this->dispatcher->addListener('pre.foo', $listener3, 10);
        $this->dispatcher->dispatch(self::preFoo);
        $this->assertEquals(array('3', '2', '1'), $invoked);
    }

    public function testRemoveListener()
    {
        $this->dispatcher->addListener('pre.bar', $this->listener);
        $this->assertTrue($this->dispatcher->hasListeners(self::preBar));
        $this->dispatcher->removeListener('pre.bar', $this->listener);
        $this->assertFalse($this->dispatcher->hasListeners(self::preBar));
        $this->dispatcher->removeListener('notExists', $this->listener);
    }

    public function testAddSubscriber()
    {
        $eventSubscriber = new TestEventSubscriber();
        $this->dispatcher->addSubscriber($eventSubscriber);
        $this->assertTrue($this->dispatcher->hasListeners(self::preFoo));
        $this->assertTrue($this->dispatcher->hasListeners(self::postFoo));
    }

    public function testAddSubscriberWithPriorities()
    {
        $eventSubscriber = new TestEventSubscriber();
        $this->dispatcher->addSubscriber($eventSubscriber);

        $eventSubscriber = new TestEventSubscriberWithPriorities();
        $this->dispatcher->addSubscriber($eventSubscriber);

        $listeners = $this->dispatcher->getListeners('pre.foo');
        $this->assertTrue($this->dispatcher->hasListeners(self::preFoo));
        $this->assertCount(2, $listeners);
        $this->assertInstanceOf('Symfony\Component\EventDispatcher\Tests\TestEventSubscriberWithPriorities', $listeners[0][0]);
    }

    public function testAddSubscriberWithMultipleListeners()
    {
        $eventSubscriber = new TestEventSubscriberWithMultipleListeners();
        $this->dispatcher->addSubscriber($eventSubscriber);

        $listeners = $this->dispatcher->getListeners('pre.foo');
        $this->assertTrue($this->dispatcher->hasListeners(self::preFoo));
        $this->assertCount(2, $listeners);
        $this->assertEquals('preFoo2', $listeners[0][1]);
    }

    public function testRemoveSubscriber()
    {
        $eventSubscriber = new TestEventSubscriber();
        $this->dispatcher->addSubscriber($eventSubscriber);
        $this->assertTrue($this->dispatcher->hasListeners(self::preFoo));
        $this->assertTrue($this->dispatcher->hasListeners(self::postFoo));
        $this->dispatcher->removeSubscriber($eventSubscriber);
        $this->assertFalse($this->dispatcher->hasListeners(self::preFoo));
        $this->assertFalse($this->dispatcher->hasListeners(self::postFoo));
    }

    public function testRemoveSubscriberWithPriorities()
    {
        $eventSubscriber = new TestEventSubscriberWithPriorities();
        $this->dispatcher->addSubscriber($eventSubscriber);
        $this->assertTrue($this->dispatcher->hasListeners(self::preFoo));
        $this->dispatcher->removeSubscriber($eventSubscriber);
        $this->assertFalse($this->dispatcher->hasListeners(self::preFoo));
    }

    public function testRemoveSubscriberWithMultipleListeners()
    {
        $eventSubscriber = new TestEventSubscriberWithMultipleListeners();
        $this->dispatcher->addSubscriber($eventSubscriber);
        $this->assertTrue($this->dispatcher->hasListeners(self::preFoo));
        $this->assertCount(2, $this->dispatcher->getListeners(self::preFoo));
        $this->dispatcher->removeSubscriber($eventSubscriber);
        $this->assertFalse($this->dispatcher->hasListeners(self::preFoo));
    }

    /**
     * @group legacy
     */
    public function testLegacyEventReceivesTheDispatcherInstance()
    {
        $dispatcher = null;
        $this->dispatcher->addListener('test', function ($event) use (&$dispatcher) {
            $dispatcher = $event->getDispatcher();
        });
        $this->dispatcher->dispatch('test');
        $this->assertSame($this->dispatcher, $dispatcher);
    }

    public function testEventReceivesTheDispatcherInstanceAsArgument()
    {
        $listener = new TestWithDispatcher();
        $this->dispatcher->addListener('test', array($listener, 'foo'));
        $this->assertNull($listener->name);
        $this->assertNull($listener->dispatcher);
        $this->dispatcher->dispatch('test');
        $this->assertEquals('test', $listener->name);
        $this->assertSame($this->dispatcher, $listener->dispatcher);
    }

    /**
     * @see https://bugs.php.net/bug.php?id=62976
     *
     * This bug affects:
     *  - The PHP 5.3 branch for versions < 5.3.18
     *  - The PHP 5.4 branch for versions < 5.4.8
     *  - The PHP 5.5 branch is not affected
     */
    public function testWorkaroundForPhpBug62976()
    {
        $dispatcher = $this->createEventDispatcher();
        $dispatcher->addListener('bug.62976', new CallableClass());
        $dispatcher->removeListener('bug.62976', function () {});
        $this->assertTrue($dispatcher->hasListeners('bug.62976'));
    }

    public function testHasListenersWhenAddedCallbackListenerIsRemoved()
    {
        $listener = function () {};
        $this->dispatcher->addListener('foo', $listener);
        $this->dispatcher->removeListener('foo', $listener);
        $this->assertFalse($this->dispatcher->hasListeners());
    }

    public function testGetListenersWhenAddedCallbackListenerIsRemoved()
    {
        $listener = function () {};
        $this->dispatcher->addListener('foo', $listener);
        $this->dispatcher->removeListener('foo', $listener);
        $this->assertSame(array(), $this->dispatcher->getListeners());
    }

    public function testHasListenersWithoutEventsReturnsFalseAfterHasListenersWithEventHasBeenCalled()
    {
        $this->assertFalse($this->dispatcher->hasListeners('foo'));
        $this->assertFalse($this->dispatcher->hasListeners());
    }
}

class CallableClass
{
    public function __invoke()
    {
    }
}

class TestEventListener
{
    public $preFooInvoked = false;
    public $postFooInvoked = false;

    /* Listener methods */

    public function preFoo(Event $e)
    {
        $this->preFooInvoked = true;
    }

    public function postFoo(Event $e)
    {
        $this->postFooInvoked = true;

        $e->stopPropagation();
    }
}

class TestWithDispatcher
{
    public $name;
    public $dispatcher;

    public function foo(Event $e, $name, $dispatcher)
    {
        $this->name = $name;
        $this->dispatcher = $dispatcher;
    }
}

class TestEventSubscriber implements EventSubscriberInterface
{
    public static function getSubscribedEvents()
    {
        return array('pre.foo' => 'preFoo', 'post.foo' => 'postFoo');
    }
}

class TestEventSubscriberWithPriorities implements EventSubscriberInterface
{
    public static function getSubscribedEvents()
    {
        return array(
            'pre.foo' => array('preFoo', 10),
            'post.foo' => array('postFoo'),
        );
    }
}

class TestEventSubscriberWithMultipleListeners implements EventSubscriberInterface
{
    public static function getSubscribedEvents()
    {
        return array('pre.foo' => array(
            array('preFoo1'),
            array('preFoo2', 10),
        ));
    }
}
vendor/symfony/event-dispatcher/Tests/DependencyInjection/RegisterListenersPassTest.php000064400000013361151327705700025725 0ustar00<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\EventDispatcher\Tests\DependencyInjection;

use PHPUnit\Framework\TestCase;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\EventDispatcher\DependencyInjection\RegisterListenersPass;

class RegisterListenersPassTest extends TestCase
{
    /**
     * Tests that event subscribers not implementing EventSubscriberInterface
     * trigger an exception.
     *
     * @expectedException \InvalidArgumentException
     */
    public function testEventSubscriberWithoutInterface()
    {
        $builder = new ContainerBuilder();
        $builder->register('event_dispatcher');
        $builder->register('my_event_subscriber', 'stdClass')
            ->addTag('kernel.event_subscriber');

        $registerListenersPass = new RegisterListenersPass();
        $registerListenersPass->process($builder);
    }

    public function testValidEventSubscriber()
    {
        $services = array(
            'my_event_subscriber' => array(0 => array()),
        );

        $builder = new ContainerBuilder();
        $eventDispatcherDefinition = $builder->register('event_dispatcher');
        $builder->register('my_event_subscriber', 'Symfony\Component\EventDispatcher\Tests\DependencyInjection\SubscriberService')
            ->addTag('kernel.event_subscriber');

        $registerListenersPass = new RegisterListenersPass();
        $registerListenersPass->process($builder);

        $this->assertEquals(array(array('addSubscriberService', array('my_event_subscriber', 'Symfony\Component\EventDispatcher\Tests\DependencyInjection\SubscriberService'))), $eventDispatcherDefinition->getMethodCalls());
    }

    /**
     * @expectedException \InvalidArgumentException
     * @expectedExceptionMessage The service "foo" must be public as event listeners are lazy-loaded.
     */
    public function testPrivateEventListener()
    {
        $container = new ContainerBuilder();
        $container->register('foo', 'stdClass')->setPublic(false)->addTag('kernel.event_listener', array());
        $container->register('event_dispatcher', 'stdClass');

        $registerListenersPass = new RegisterListenersPass();
        $registerListenersPass->process($container);
    }

    /**
     * @expectedException \InvalidArgumentException
     * @expectedExceptionMessage The service "foo" must be public as event subscribers are lazy-loaded.
     */
    public function testPrivateEventSubscriber()
    {
        $container = new ContainerBuilder();
        $container->register('foo', 'stdClass')->setPublic(false)->addTag('kernel.event_subscriber', array());
        $container->register('event_dispatcher', 'stdClass');

        $registerListenersPass = new RegisterListenersPass();
        $registerListenersPass->process($container);
    }

    /**
     * @expectedException \InvalidArgumentException
     * @expectedExceptionMessage The service "foo" must not be abstract as event listeners are lazy-loaded.
     */
    public function testAbstractEventListener()
    {
        $container = new ContainerBuilder();
        $container->register('foo', 'stdClass')->setAbstract(true)->addTag('kernel.event_listener', array());
        $container->register('event_dispatcher', 'stdClass');

        $registerListenersPass = new RegisterListenersPass();
        $registerListenersPass->process($container);
    }

    /**
     * @expectedException \InvalidArgumentException
     * @expectedExceptionMessage The service "foo" must not be abstract as event subscribers are lazy-loaded.
     */
    public function testAbstractEventSubscriber()
    {
        $container = new ContainerBuilder();
        $container->register('foo', 'stdClass')->setAbstract(true)->addTag('kernel.event_subscriber', array());
        $container->register('event_dispatcher', 'stdClass');

        $registerListenersPass = new RegisterListenersPass();
        $registerListenersPass->process($container);
    }

    public function testEventSubscriberResolvableClassName()
    {
        $container = new ContainerBuilder();

        $container->setParameter('subscriber.class', 'Symfony\Component\EventDispatcher\Tests\DependencyInjection\SubscriberService');
        $container->register('foo', '%subscriber.class%')->addTag('kernel.event_subscriber', array());
        $container->register('event_dispatcher', 'stdClass');

        $registerListenersPass = new RegisterListenersPass();
        $registerListenersPass->process($container);

        $definition = $container->getDefinition('event_dispatcher');
        $expected_calls = array(
            array(
                'addSubscriberService',
                array(
                    'foo',
                    'Symfony\Component\EventDispatcher\Tests\DependencyInjection\SubscriberService',
                ),
            ),
        );
        $this->assertSame($expected_calls, $definition->getMethodCalls());
    }

    /**
     * @expectedException \InvalidArgumentException
     * @expectedExceptionMessage You have requested a non-existent parameter "subscriber.class"
     */
    public function testEventSubscriberUnresolvableClassName()
    {
        $container = new ContainerBuilder();
        $container->register('foo', '%subscriber.class%')->addTag('kernel.event_subscriber', array());
        $container->register('event_dispatcher', 'stdClass');

        $registerListenersPass = new RegisterListenersPass();
        $registerListenersPass->process($container);
    }
}

class SubscriberService implements \Symfony\Component\EventDispatcher\EventSubscriberInterface
{
    public static function getSubscribedEvents()
    {
    }
}
vendor/symfony/event-dispatcher/Tests/ContainerAwareEventDispatcherTest.php000064400000020363151327705700023413 0ustar00<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\EventDispatcher\Tests;

use Symfony\Component\DependencyInjection\Container;
use Symfony\Component\DependencyInjection\Scope;
use Symfony\Component\EventDispatcher\ContainerAwareEventDispatcher;
use Symfony\Component\EventDispatcher\Event;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;

class ContainerAwareEventDispatcherTest extends AbstractEventDispatcherTest
{
    protected function createEventDispatcher()
    {
        $container = new Container();

        return new ContainerAwareEventDispatcher($container);
    }

    public function testAddAListenerService()
    {
        $event = new Event();

        $service = $this->getMockBuilder('Symfony\Component\EventDispatcher\Tests\Service')->getMock();

        $service
            ->expects($this->once())
            ->method('onEvent')
            ->with($event)
        ;

        $container = new Container();
        $container->set('service.listener', $service);

        $dispatcher = new ContainerAwareEventDispatcher($container);
        $dispatcher->addListenerService('onEvent', array('service.listener', 'onEvent'));

        $dispatcher->dispatch('onEvent', $event);
    }

    public function testAddASubscriberService()
    {
        $event = new Event();

        $service = $this->getMockBuilder('Symfony\Component\EventDispatcher\Tests\SubscriberService')->getMock();

        $service
            ->expects($this->once())
            ->method('onEvent')
            ->with($event)
        ;

        $service
            ->expects($this->once())
            ->method('onEventWithPriority')
            ->with($event)
        ;

        $service
            ->expects($this->once())
            ->method('onEventNested')
            ->with($event)
        ;

        $container = new Container();
        $container->set('service.subscriber', $service);

        $dispatcher = new ContainerAwareEventDispatcher($container);
        $dispatcher->addSubscriberService('service.subscriber', 'Symfony\Component\EventDispatcher\Tests\SubscriberService');

        $dispatcher->dispatch('onEvent', $event);
        $dispatcher->dispatch('onEventWithPriority', $event);
        $dispatcher->dispatch('onEventNested', $event);
    }

    public function testPreventDuplicateListenerService()
    {
        $event = new Event();

        $service = $this->getMockBuilder('Symfony\Component\EventDispatcher\Tests\Service')->getMock();

        $service
            ->expects($this->once())
            ->method('onEvent')
            ->with($event)
        ;

        $container = new Container();
        $container->set('service.listener', $service);

        $dispatcher = new ContainerAwareEventDispatcher($container);
        $dispatcher->addListenerService('onEvent', array('service.listener', 'onEvent'), 5);
        $dispatcher->addListenerService('onEvent', array('service.listener', 'onEvent'), 10);

        $dispatcher->dispatch('onEvent', $event);
    }

    /**
     * @expectedException \InvalidArgumentException
     * @group legacy
     */
    public function testTriggerAListenerServiceOutOfScope()
    {
        $service = $this->getMockBuilder('Symfony\Component\EventDispatcher\Tests\Service')->getMock();

        $scope = new Scope('scope');
        $container = new Container();
        $container->addScope($scope);
        $container->enterScope('scope');

        $container->set('service.listener', $service, 'scope');

        $dispatcher = new ContainerAwareEventDispatcher($container);
        $dispatcher->addListenerService('onEvent', array('service.listener', 'onEvent'));

        $container->leaveScope('scope');
        $dispatcher->dispatch('onEvent');
    }

    /**
     * @group legacy
     */
    public function testReEnteringAScope()
    {
        $event = new Event();

        $service1 = $this->getMockBuilder('Symfony\Component\EventDispatcher\Tests\Service')->getMock();

        $service1
            ->expects($this->exactly(2))
            ->method('onEvent')
            ->with($event)
        ;

        $scope = new Scope('scope');
        $container = new Container();
        $container->addScope($scope);
        $container->enterScope('scope');

        $container->set('service.listener', $service1, 'scope');

        $dispatcher = new ContainerAwareEventDispatcher($container);
        $dispatcher->addListenerService('onEvent', array('service.listener', 'onEvent'));
        $dispatcher->dispatch('onEvent', $event);

        $service2 = $this->getMockBuilder('Symfony\Component\EventDispatcher\Tests\Service')->getMock();

        $service2
            ->expects($this->once())
            ->method('onEvent')
            ->with($event)
        ;

        $container->enterScope('scope');
        $container->set('service.listener', $service2, 'scope');

        $dispatcher->dispatch('onEvent', $event);

        $container->leaveScope('scope');

        $dispatcher->dispatch('onEvent');
    }

    public function testHasListenersOnLazyLoad()
    {
        $event = new Event();

        $service = $this->getMockBuilder('Symfony\Component\EventDispatcher\Tests\Service')->getMock();

        $container = new Container();
        $container->set('service.listener', $service);

        $dispatcher = new ContainerAwareEventDispatcher($container);
        $dispatcher->addListenerService('onEvent', array('service.listener', 'onEvent'));

        $event->setDispatcher($dispatcher);
        $event->setName('onEvent');

        $service
            ->expects($this->once())
            ->method('onEvent')
            ->with($event)
        ;

        $this->assertTrue($dispatcher->hasListeners());

        if ($dispatcher->hasListeners('onEvent')) {
            $dispatcher->dispatch('onEvent');
        }
    }

    public function testGetListenersOnLazyLoad()
    {
        $service = $this->getMockBuilder('Symfony\Component\EventDispatcher\Tests\Service')->getMock();

        $container = new Container();
        $container->set('service.listener', $service);

        $dispatcher = new ContainerAwareEventDispatcher($container);
        $dispatcher->addListenerService('onEvent', array('service.listener', 'onEvent'));

        $listeners = $dispatcher->getListeners();

        $this->assertArrayHasKey('onEvent', $listeners);

        $this->assertCount(1, $dispatcher->getListeners('onEvent'));
    }

    public function testRemoveAfterDispatch()
    {
        $service = $this->getMockBuilder('Symfony\Component\EventDispatcher\Tests\Service')->getMock();

        $container = new Container();
        $container->set('service.listener', $service);

        $dispatcher = new ContainerAwareEventDispatcher($container);
        $dispatcher->addListenerService('onEvent', array('service.listener', 'onEvent'));

        $dispatcher->dispatch('onEvent', new Event());
        $dispatcher->removeListener('onEvent', array($container->get('service.listener'), 'onEvent'));
        $this->assertFalse($dispatcher->hasListeners('onEvent'));
    }

    public function testRemoveBeforeDispatch()
    {
        $service = $this->getMockBuilder('Symfony\Component\EventDispatcher\Tests\Service')->getMock();

        $container = new Container();
        $container->set('service.listener', $service);

        $dispatcher = new ContainerAwareEventDispatcher($container);
        $dispatcher->addListenerService('onEvent', array('service.listener', 'onEvent'));

        $dispatcher->removeListener('onEvent', array($container->get('service.listener'), 'onEvent'));
        $this->assertFalse($dispatcher->hasListeners('onEvent'));
    }
}

class Service
{
    public function onEvent(Event $e)
    {
    }
}

class SubscriberService implements EventSubscriberInterface
{
    public static function getSubscribedEvents()
    {
        return array(
            'onEvent' => 'onEvent',
            'onEventWithPriority' => array('onEventWithPriority', 10),
            'onEventNested' => array(array('onEventNested')),
        );
    }

    public function onEvent(Event $e)
    {
    }

    public function onEventWithPriority(Event $e)
    {
    }

    public function onEventNested(Event $e)
    {
    }
}
vendor/symfony/event-dispatcher/Tests/Debug/TraceableEventDispatcherTest.php000064400000023237151327705700023424 0ustar00<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\EventDispatcher\Tests\Debug;

use PHPUnit\Framework\TestCase;
use Symfony\Component\EventDispatcher\Debug\TraceableEventDispatcher;
use Symfony\Component\EventDispatcher\Debug\WrappedListener;
use Symfony\Component\EventDispatcher\Event;
use Symfony\Component\EventDispatcher\EventDispatcher;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\Stopwatch\Stopwatch;

class TraceableEventDispatcherTest extends TestCase
{
    public function testAddRemoveListener()
    {
        $dispatcher = new EventDispatcher();
        $tdispatcher = new TraceableEventDispatcher($dispatcher, new Stopwatch());

        $tdispatcher->addListener('foo', $listener = function () {});
        $listeners = $dispatcher->getListeners('foo');
        $this->assertCount(1, $listeners);
        $this->assertSame($listener, $listeners[0]);

        $tdispatcher->removeListener('foo', $listener);
        $this->assertCount(0, $dispatcher->getListeners('foo'));
    }

    public function testGetListeners()
    {
        $dispatcher = new EventDispatcher();
        $tdispatcher = new TraceableEventDispatcher($dispatcher, new Stopwatch());

        $tdispatcher->addListener('foo', $listener = function () {});
        $this->assertSame($dispatcher->getListeners('foo'), $tdispatcher->getListeners('foo'));
    }

    public function testHasListeners()
    {
        $dispatcher = new EventDispatcher();
        $tdispatcher = new TraceableEventDispatcher($dispatcher, new Stopwatch());

        $this->assertFalse($dispatcher->hasListeners('foo'));
        $this->assertFalse($tdispatcher->hasListeners('foo'));

        $tdispatcher->addListener('foo', $listener = function () {});
        $this->assertTrue($dispatcher->hasListeners('foo'));
        $this->assertTrue($tdispatcher->hasListeners('foo'));
    }

    public function testGetListenerPriority()
    {
        $dispatcher = new EventDispatcher();
        $tdispatcher = new TraceableEventDispatcher($dispatcher, new Stopwatch());

        $tdispatcher->addListener('foo', function () {}, 123);

        $listeners = $dispatcher->getListeners('foo');
        $this->assertSame(123, $tdispatcher->getListenerPriority('foo', $listeners[0]));

        // Verify that priority is preserved when listener is removed and re-added
        // in preProcess() and postProcess().
        $tdispatcher->dispatch('foo', new Event());
        $listeners = $dispatcher->getListeners('foo');
        $this->assertSame(123, $tdispatcher->getListenerPriority('foo', $listeners[0]));
    }

    public function testGetListenerPriorityReturnsZeroWhenWrappedMethodDoesNotExist()
    {
        $dispatcher = $this->getMockBuilder('Symfony\Component\EventDispatcher\EventDispatcherInterface')->getMock();
        $traceableEventDispatcher = new TraceableEventDispatcher($dispatcher, new Stopwatch());
        $traceableEventDispatcher->addListener('foo', function () {}, 123);
        $listeners = $traceableEventDispatcher->getListeners('foo');

        $this->assertSame(0, $traceableEventDispatcher->getListenerPriority('foo', $listeners[0]));
    }

    public function testAddRemoveSubscriber()
    {
        $dispatcher = new EventDispatcher();
        $tdispatcher = new TraceableEventDispatcher($dispatcher, new Stopwatch());

        $subscriber = new EventSubscriber();

        $tdispatcher->addSubscriber($subscriber);
        $listeners = $dispatcher->getListeners('foo');
        $this->assertCount(1, $listeners);
        $this->assertSame(array($subscriber, 'call'), $listeners[0]);

        $tdispatcher->removeSubscriber($subscriber);
        $this->assertCount(0, $dispatcher->getListeners('foo'));
    }

    /**
     * @dataProvider isWrappedDataProvider
     *
     * @param bool $isWrapped
     */
    public function testGetCalledListeners($isWrapped)
    {
        $dispatcher = new EventDispatcher();
        $stopWatch = new Stopwatch();
        $tdispatcher = new TraceableEventDispatcher($dispatcher, $stopWatch);

        $listener = function () {};
        if ($isWrapped) {
            $listener = new WrappedListener($listener, 'foo', $stopWatch, $dispatcher);
        }

        $tdispatcher->addListener('foo', $listener, 5);

        $this->assertEquals(array(), $tdispatcher->getCalledListeners());
        $this->assertEquals(array('foo.closure' => array('event' => 'foo', 'type' => 'Closure', 'pretty' => 'closure', 'priority' => 5)), $tdispatcher->getNotCalledListeners());

        $tdispatcher->dispatch('foo');

        $this->assertEquals(array('foo.closure' => array('event' => 'foo', 'type' => 'Closure', 'pretty' => 'closure', 'priority' => 5)), $tdispatcher->getCalledListeners());
        $this->assertEquals(array(), $tdispatcher->getNotCalledListeners());
    }

    public function isWrappedDataProvider()
    {
        return array(
            array(false),
            array(true),
        );
    }

    public function testGetCalledListenersNested()
    {
        $tdispatcher = null;
        $dispatcher = new TraceableEventDispatcher(new EventDispatcher(), new Stopwatch());
        $dispatcher->addListener('foo', function (Event $event, $eventName, $dispatcher) use (&$tdispatcher) {
            $tdispatcher = $dispatcher;
            $dispatcher->dispatch('bar');
        });
        $dispatcher->addListener('bar', function (Event $event) {});
        $dispatcher->dispatch('foo');
        $this->assertSame($dispatcher, $tdispatcher);
        $this->assertCount(2, $dispatcher->getCalledListeners());
    }

    public function testLogger()
    {
        $logger = $this->getMockBuilder('WPvividPsr\Log\LoggerInterface')->getMock();

        $dispatcher = new EventDispatcher();
        $tdispatcher = new TraceableEventDispatcher($dispatcher, new Stopwatch(), $logger);
        $tdispatcher->addListener('foo', $listener1 = function () {});
        $tdispatcher->addListener('foo', $listener2 = function () {});

        $logger->expects($this->at(0))->method('debug')->with('Notified event "foo" to listener "closure".');
        $logger->expects($this->at(1))->method('debug')->with('Notified event "foo" to listener "closure".');

        $tdispatcher->dispatch('foo');
    }

    public function testLoggerWithStoppedEvent()
    {
        $logger = $this->getMockBuilder('WPvividPsr\Log\LoggerInterface')->getMock();

        $dispatcher = new EventDispatcher();
        $tdispatcher = new TraceableEventDispatcher($dispatcher, new Stopwatch(), $logger);
        $tdispatcher->addListener('foo', $listener1 = function (Event $event) { $event->stopPropagation(); });
        $tdispatcher->addListener('foo', $listener2 = function () {});

        $logger->expects($this->at(0))->method('debug')->with('Notified event "foo" to listener "closure".');
        $logger->expects($this->at(1))->method('debug')->with('Listener "closure" stopped propagation of the event "foo".');
        $logger->expects($this->at(2))->method('debug')->with('Listener "closure" was not called for event "foo".');

        $tdispatcher->dispatch('foo');
    }

    public function testDispatchCallListeners()
    {
        $called = array();

        $dispatcher = new EventDispatcher();
        $tdispatcher = new TraceableEventDispatcher($dispatcher, new Stopwatch());
        $tdispatcher->addListener('foo', function () use (&$called) { $called[] = 'foo1'; }, 10);
        $tdispatcher->addListener('foo', function () use (&$called) { $called[] = 'foo2'; }, 20);

        $tdispatcher->dispatch('foo');

        $this->assertSame(array('foo2', 'foo1'), $called);
    }

    public function testDispatchNested()
    {
        $dispatcher = new TraceableEventDispatcher(new EventDispatcher(), new Stopwatch());
        $loop = 1;
        $dispatchedEvents = 0;
        $dispatcher->addListener('foo', $listener1 = function () use ($dispatcher, &$loop) {
            ++$loop;
            if (2 == $loop) {
                $dispatcher->dispatch('foo');
            }
        });
        $dispatcher->addListener('foo', function () use (&$dispatchedEvents) {
            ++$dispatchedEvents;
        });

        $dispatcher->dispatch('foo');

        $this->assertSame(2, $dispatchedEvents);
    }

    public function testDispatchReusedEventNested()
    {
        $nestedCall = false;
        $dispatcher = new TraceableEventDispatcher(new EventDispatcher(), new Stopwatch());
        $dispatcher->addListener('foo', function (Event $e) use ($dispatcher) {
            $dispatcher->dispatch('bar', $e);
        });
        $dispatcher->addListener('bar', function (Event $e) use (&$nestedCall) {
            $nestedCall = true;
        });

        $this->assertFalse($nestedCall);
        $dispatcher->dispatch('foo');
        $this->assertTrue($nestedCall);
    }

    public function testListenerCanRemoveItselfWhenExecuted()
    {
        $eventDispatcher = new TraceableEventDispatcher(new EventDispatcher(), new Stopwatch());
        $listener1 = function ($event, $eventName, EventDispatcherInterface $dispatcher) use (&$listener1) {
            $dispatcher->removeListener('foo', $listener1);
        };
        $eventDispatcher->addListener('foo', $listener1);
        $eventDispatcher->addListener('foo', function () {});
        $eventDispatcher->dispatch('foo');

        $this->assertCount(1, $eventDispatcher->getListeners('foo'), 'expected listener1 to be removed');
    }
}

class EventSubscriber implements EventSubscriberInterface
{
    public static function getSubscribedEvents()
    {
        return array('foo' => 'call');
    }
}
vendor/symfony/event-dispatcher/Tests/EventDispatcherTest.php000064400000000775151327705700020575 0ustar00<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\EventDispatcher\Tests;

use Symfony\Component\EventDispatcher\EventDispatcher;

class EventDispatcherTest extends AbstractEventDispatcherTest
{
    protected function createEventDispatcher()
    {
        return new EventDispatcher();
    }
}
vendor/symfony/event-dispatcher/Tests/GenericEventTest.php000064400000007006151327705700020055 0ustar00<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\EventDispatcher\Tests;

use PHPUnit\Framework\TestCase;
use Symfony\Component\EventDispatcher\GenericEvent;

/**
 * Test class for Event.
 */
class GenericEventTest extends TestCase
{
    /**
     * @var GenericEvent
     */
    private $event;

    private $subject;

    /**
     * Prepares the environment before running a test.
     */
    protected function setUp()
    {
        $this->subject = new \stdClass();
        $this->event = new GenericEvent($this->subject, array('name' => 'Event'));
    }

    /**
     * Cleans up the environment after running a test.
     */
    protected function tearDown()
    {
        $this->subject = null;
        $this->event = null;
    }

    public function testConstruct()
    {
        $this->assertEquals($this->event, new GenericEvent($this->subject, array('name' => 'Event')));
    }

    /**
     * Tests Event->getArgs().
     */
    public function testGetArguments()
    {
        // test getting all
        $this->assertSame(array('name' => 'Event'), $this->event->getArguments());
    }

    public function testSetArguments()
    {
        $result = $this->event->setArguments(array('foo' => 'bar'));
        $this->assertAttributeSame(array('foo' => 'bar'), 'arguments', $this->event);
        $this->assertSame($this->event, $result);
    }

    public function testSetArgument()
    {
        $result = $this->event->setArgument('foo2', 'bar2');
        $this->assertAttributeSame(array('name' => 'Event', 'foo2' => 'bar2'), 'arguments', $this->event);
        $this->assertEquals($this->event, $result);
    }

    public function testGetArgument()
    {
        // test getting key
        $this->assertEquals('Event', $this->event->getArgument('name'));
    }

    /**
     * @expectedException \InvalidArgumentException
     */
    public function testGetArgException()
    {
        $this->event->getArgument('nameNotExist');
    }

    public function testOffsetGet()
    {
        // test getting key
        $this->assertEquals('Event', $this->event['name']);

        // test getting invalid arg
        $this->{method_exists($this, $_ = 'expectException') ? $_ : 'setExpectedException'}('InvalidArgumentException');
        $this->assertFalse($this->event['nameNotExist']);
    }

    public function testOffsetSet()
    {
        $this->event['foo2'] = 'bar2';
        $this->assertAttributeSame(array('name' => 'Event', 'foo2' => 'bar2'), 'arguments', $this->event);
    }

    public function testOffsetUnset()
    {
        unset($this->event['name']);
        $this->assertAttributeSame(array(), 'arguments', $this->event);
    }

    public function testOffsetIsset()
    {
        $this->assertArrayHasKey('name', $this->event);
        $this->assertArrayNotHasKey('nameNotExist', $this->event);
    }

    public function testHasArgument()
    {
        $this->assertTrue($this->event->hasArgument('name'));
        $this->assertFalse($this->event->hasArgument('nameNotExist'));
    }

    public function testGetSubject()
    {
        $this->assertSame($this->subject, $this->event->getSubject());
    }

    public function testHasIterator()
    {
        $data = array();
        foreach ($this->event as $key => $value) {
            $data[$key] = $value;
        }
        $this->assertEquals(array('name' => 'Event'), $data);
    }
}
vendor/symfony/event-dispatcher/Debug/WrappedListener.php000064400000003326151327705700017674 0ustar00<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\EventDispatcher\Debug;

use Symfony\Component\EventDispatcher\Event;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\Stopwatch\Stopwatch;

/**
 * @author Fabien Potencier <fabien@symfony.com>
 */
class WrappedListener
{
    private $listener;
    private $name;
    private $called;
    private $stoppedPropagation;
    private $stopwatch;
    private $dispatcher;

    public function __construct($listener, $name, Stopwatch $stopwatch, EventDispatcherInterface $dispatcher = null)
    {
        $this->listener = $listener;
        $this->name = $name;
        $this->stopwatch = $stopwatch;
        $this->dispatcher = $dispatcher;
        $this->called = false;
        $this->stoppedPropagation = false;
    }

    public function getWrappedListener()
    {
        return $this->listener;
    }

    public function wasCalled()
    {
        return $this->called;
    }

    public function stoppedPropagation()
    {
        return $this->stoppedPropagation;
    }

    public function __invoke(Event $event, $eventName, EventDispatcherInterface $dispatcher)
    {
        $this->called = true;

        $e = $this->stopwatch->start($this->name, 'event_listener');

        \call_user_func($this->listener, $event, $eventName, $this->dispatcher ?: $dispatcher);

        if ($e->isStarted()) {
            $e->stop();
        }

        if ($event->isPropagationStopped()) {
            $this->stoppedPropagation = true;
        }
    }
}
vendor/symfony/event-dispatcher/Debug/TraceableEventDispatcherInterface.php000064400000001443151327705700023276 0ustar00<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\EventDispatcher\Debug;

use Symfony\Component\EventDispatcher\EventDispatcherInterface;

/**
 * @author Fabien Potencier <fabien@symfony.com>
 */
interface TraceableEventDispatcherInterface extends EventDispatcherInterface
{
    /**
     * Gets the called listeners.
     *
     * @return array An array of called listeners
     */
    public function getCalledListeners();

    /**
     * Gets the not called listeners.
     *
     * @return array An array of not called listeners
     */
    public function getNotCalledListeners();
}
vendor/symfony/event-dispatcher/Debug/TraceableEventDispatcher.php000064400000026120151327705700021454 0ustar00<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\EventDispatcher\Debug;

use WPvividPsr\Log\LoggerInterface;
use Symfony\Component\EventDispatcher\Event;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\Stopwatch\Stopwatch;

/**
 * Collects some data about event listeners.
 *
 * This event dispatcher delegates the dispatching to another one.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
class TraceableEventDispatcher implements TraceableEventDispatcherInterface
{
    protected $logger;
    protected $stopwatch;

    private $called;
    private $dispatcher;
    private $wrappedListeners;

    public function __construct(EventDispatcherInterface $dispatcher, Stopwatch $stopwatch, LoggerInterface $logger = null)
    {
        $this->dispatcher = $dispatcher;
        $this->stopwatch = $stopwatch;
        $this->logger = $logger;
        $this->called = array();
        $this->wrappedListeners = array();
    }

    /**
     * {@inheritdoc}
     */
    public function addListener($eventName, $listener, $priority = 0)
    {
        $this->dispatcher->addListener($eventName, $listener, $priority);
    }

    /**
     * {@inheritdoc}
     */
    public function addSubscriber(EventSubscriberInterface $subscriber)
    {
        $this->dispatcher->addSubscriber($subscriber);
    }

    /**
     * {@inheritdoc}
     */
    public function removeListener($eventName, $listener)
    {
        if (isset($this->wrappedListeners[$eventName])) {
            foreach ($this->wrappedListeners[$eventName] as $index => $wrappedListener) {
                if ($wrappedListener->getWrappedListener() === $listener) {
                    $listener = $wrappedListener;
                    unset($this->wrappedListeners[$eventName][$index]);
                    break;
                }
            }
        }

        return $this->dispatcher->removeListener($eventName, $listener);
    }

    /**
     * {@inheritdoc}
     */
    public function removeSubscriber(EventSubscriberInterface $subscriber)
    {
        return $this->dispatcher->removeSubscriber($subscriber);
    }

    /**
     * {@inheritdoc}
     */
    public function getListeners($eventName = null)
    {
        return $this->dispatcher->getListeners($eventName);
    }

    /**
     * {@inheritdoc}
     */
    public function getListenerPriority($eventName, $listener)
    {
        if (!method_exists($this->dispatcher, 'getListenerPriority')) {
            return 0;
        }

        return $this->dispatcher->getListenerPriority($eventName, $listener);
    }

    /**
     * {@inheritdoc}
     */
    public function hasListeners($eventName = null)
    {
        return $this->dispatcher->hasListeners($eventName);
    }

    /**
     * {@inheritdoc}
     */
    public function dispatch($eventName, Event $event = null)
    {
        if (null === $event) {
            $event = new Event();
        }

        if (null !== $this->logger && $event->isPropagationStopped()) {
            $this->logger->debug(sprintf('The "%s" event is already stopped. No listeners have been called.', $eventName));
        }

        $this->preProcess($eventName);
        $this->preDispatch($eventName, $event);

        $e = $this->stopwatch->start($eventName, 'section');

        $this->dispatcher->dispatch($eventName, $event);

        if ($e->isStarted()) {
            $e->stop();
        }

        $this->postDispatch($eventName, $event);
        $this->postProcess($eventName);

        return $event;
    }

    /**
     * {@inheritdoc}
     */
    public function getCalledListeners()
    {
        $called = array();
        foreach ($this->called as $eventName => $listeners) {
            foreach ($listeners as $listener) {
                $info = $this->getListenerInfo($listener->getWrappedListener(), $eventName);
                $called[$eventName.'.'.$info['pretty']] = $info;
            }
        }

        return $called;
    }

    /**
     * {@inheritdoc}
     */
    public function getNotCalledListeners()
    {
        try {
            $allListeners = $this->getListeners();
        } catch (\Exception $e) {
            if (null !== $this->logger) {
                $this->logger->info('An exception was thrown while getting the uncalled listeners.', array('exception' => $e));
            }

            // unable to retrieve the uncalled listeners
            return array();
        }

        $notCalled = array();
        foreach ($allListeners as $eventName => $listeners) {
            foreach ($listeners as $listener) {
                $called = false;
                if (isset($this->called[$eventName])) {
                    foreach ($this->called[$eventName] as $l) {
                        if ($l->getWrappedListener() === $listener) {
                            $called = true;

                            break;
                        }
                    }
                }

                if (!$called) {
                    $info = $this->getListenerInfo($listener, $eventName);
                    $notCalled[$eventName.'.'.$info['pretty']] = $info;
                }
            }
        }

        uasort($notCalled, array($this, 'sortListenersByPriority'));

        return $notCalled;
    }

    /**
     * Proxies all method calls to the original event dispatcher.
     *
     * @param string $method    The method name
     * @param array  $arguments The method arguments
     *
     * @return mixed
     */
    public function __call($method, $arguments)
    {
        return \call_user_func_array(array($this->dispatcher, $method), $arguments);
    }

    /**
     * Called before dispatching the event.
     *
     * @param string $eventName The event name
     * @param Event  $event     The event
     */
    protected function preDispatch($eventName, Event $event)
    {
    }

    /**
     * Called after dispatching the event.
     *
     * @param string $eventName The event name
     * @param Event  $event     The event
     */
    protected function postDispatch($eventName, Event $event)
    {
    }

    private function preProcess($eventName)
    {
        foreach ($this->dispatcher->getListeners($eventName) as $listener) {
            $info = $this->getListenerInfo($listener, $eventName);
            $name = isset($info['class']) ? $info['class'] : $info['type'];
            $wrappedListener = new WrappedListener($listener, $name, $this->stopwatch, $this);
            $this->wrappedListeners[$eventName][] = $wrappedListener;
            $this->dispatcher->removeListener($eventName, $listener);
            $this->dispatcher->addListener($eventName, $wrappedListener, $info['priority']);
        }
    }

    private function postProcess($eventName)
    {
        unset($this->wrappedListeners[$eventName]);
        $skipped = false;
        foreach ($this->dispatcher->getListeners($eventName) as $listener) {
            if (!$listener instanceof WrappedListener) { // #12845: a new listener was added during dispatch.
                continue;
            }
            // Unwrap listener
            $priority = $this->getListenerPriority($eventName, $listener);
            $this->dispatcher->removeListener($eventName, $listener);
            $this->dispatcher->addListener($eventName, $listener->getWrappedListener(), $priority);

            $info = $this->getListenerInfo($listener->getWrappedListener(), $eventName);
            if ($listener->wasCalled()) {
                if (null !== $this->logger) {
                    $this->logger->debug(sprintf('Notified event "%s" to listener "%s".', $eventName, $info['pretty']));
                }

                if (!isset($this->called[$eventName])) {
                    $this->called[$eventName] = new \SplObjectStorage();
                }

                $this->called[$eventName]->attach($listener);
            }

            if (null !== $this->logger && $skipped) {
                $this->logger->debug(sprintf('Listener "%s" was not called for event "%s".', $info['pretty'], $eventName));
            }

            if ($listener->stoppedPropagation()) {
                if (null !== $this->logger) {
                    $this->logger->debug(sprintf('Listener "%s" stopped propagation of the event "%s".', $info['pretty'], $eventName));
                }

                $skipped = true;
            }
        }
    }

    /**
     * Returns information about the listener.
     *
     * @param object $listener  The listener
     * @param string $eventName The event name
     *
     * @return array Information about the listener
     */
    private function getListenerInfo($listener, $eventName)
    {
        $info = array(
            'event' => $eventName,
            'priority' => $this->getListenerPriority($eventName, $listener),
        );

        // unwrap for correct listener info
        if ($listener instanceof WrappedListener) {
            $listener = $listener->getWrappedListener();
        }

        if ($listener instanceof \Closure) {
            $info += array(
                'type' => 'Closure',
                'pretty' => 'closure',
            );
        } elseif (\is_string($listener)) {
            try {
                $r = new \ReflectionFunction($listener);
                $file = $r->getFileName();
                $line = $r->getStartLine();
            } catch (\ReflectionException $e) {
                $file = null;
                $line = null;
            }
            $info += array(
                'type' => 'Function',
                'function' => $listener,
                'file' => $file,
                'line' => $line,
                'pretty' => $listener,
            );
        } elseif (\is_array($listener) || (\is_object($listener) && \is_callable($listener))) {
            if (!\is_array($listener)) {
                $listener = array($listener, '__invoke');
            }
            $class = \is_object($listener[0]) ? \get_class($listener[0]) : $listener[0];
            try {
                $r = new \ReflectionMethod($class, $listener[1]);
                $file = $r->getFileName();
                $line = $r->getStartLine();
            } catch (\ReflectionException $e) {
                $file = null;
                $line = null;
            }
            $info += array(
                'type' => 'Method',
                'class' => $class,
                'method' => $listener[1],
                'file' => $file,
                'line' => $line,
                'pretty' => $class.'::'.$listener[1],
            );
        }

        return $info;
    }

    private function sortListenersByPriority($a, $b)
    {
        if (\is_int($a['priority']) && !\is_int($b['priority'])) {
            return 1;
        }

        if (!\is_int($a['priority']) && \is_int($b['priority'])) {
            return -1;
        }

        if ($a['priority'] === $b['priority']) {
            return 0;
        }

        if ($a['priority'] > $b['priority']) {
            return -1;
        }

        return 1;
    }
}
vendor/symfony/event-dispatcher/GenericEvent.php000064400000007173151327705700016120 0ustar00<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\EventDispatcher;

/**
 * Event encapsulation class.
 *
 * Encapsulates events thus decoupling the observer from the subject they encapsulate.
 *
 * @author Drak <drak@zikula.org>
 */
class GenericEvent extends Event implements \ArrayAccess, \IteratorAggregate
{
    protected $subject;
    protected $arguments;

    /**
     * Encapsulate an event with $subject and $args.
     *
     * @param mixed $subject   The subject of the event, usually an object or a callable
     * @param array $arguments Arguments to store in the event
     */
    public function __construct($subject = null, array $arguments = array())
    {
        $this->subject = $subject;
        $this->arguments = $arguments;
    }

    /**
     * Getter for subject property.
     *
     * @return mixed The observer subject
     */
    public function getSubject()
    {
        return $this->subject;
    }

    /**
     * Get argument by key.
     *
     * @param string $key Key
     *
     * @return mixed Contents of array key
     *
     * @throws \InvalidArgumentException if key is not found
     */
    public function getArgument($key)
    {
        if ($this->hasArgument($key)) {
            return $this->arguments[$key];
        }

        throw new \InvalidArgumentException(sprintf('Argument "%s" not found.', $key));
    }

    /**
     * Add argument to event.
     *
     * @param string $key   Argument name
     * @param mixed  $value Value
     *
     * @return $this
     */
    public function setArgument($key, $value)
    {
        $this->arguments[$key] = $value;

        return $this;
    }

    /**
     * Getter for all arguments.
     *
     * @return array
     */
    public function getArguments()
    {
        return $this->arguments;
    }

    /**
     * Set args property.
     *
     * @param array $args Arguments
     *
     * @return $this
     */
    public function setArguments(array $args = array())
    {
        $this->arguments = $args;

        return $this;
    }

    /**
     * Has argument.
     *
     * @param string $key Key of arguments array
     *
     * @return bool
     */
    public function hasArgument($key)
    {
        return array_key_exists($key, $this->arguments);
    }

    /**
     * ArrayAccess for argument getter.
     *
     * @param string $key Array key
     *
     * @return mixed
     *
     * @throws \InvalidArgumentException if key does not exist in $this->args
     */
    public function offsetGet($key)
    {
        return $this->getArgument($key);
    }

    /**
     * ArrayAccess for argument setter.
     *
     * @param string $key   Array key to set
     * @param mixed  $value Value
     */
    public function offsetSet($key, $value)
    {
        $this->setArgument($key, $value);
    }

    /**
     * ArrayAccess for unset argument.
     *
     * @param string $key Array key
     */
    public function offsetUnset($key)
    {
        if ($this->hasArgument($key)) {
            unset($this->arguments[$key]);
        }
    }

    /**
     * ArrayAccess has argument.
     *
     * @param string $key Array key
     *
     * @return bool
     */
    public function offsetExists($key)
    {
        return $this->hasArgument($key);
    }

    /**
     * IteratorAggregate for iterating over the object like an array.
     *
     * @return \ArrayIterator
     */
    public function getIterator()
    {
        return new \ArrayIterator($this->arguments);
    }
}
vendor/symfony/event-dispatcher/DependencyInjection/RegisterListenersPass.php000064400000010322151327705700023755 0ustar00<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\EventDispatcher\DependencyInjection;

use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;

/**
 * Compiler pass to register tagged services for an event dispatcher.
 */
class RegisterListenersPass implements CompilerPassInterface
{
    protected $dispatcherService;
    protected $listenerTag;
    protected $subscriberTag;

    /**
     * @param string $dispatcherService Service name of the event dispatcher in processed container
     * @param string $listenerTag       Tag name used for listener
     * @param string $subscriberTag     Tag name used for subscribers
     */
    public function __construct($dispatcherService = 'event_dispatcher', $listenerTag = 'kernel.event_listener', $subscriberTag = 'kernel.event_subscriber')
    {
        $this->dispatcherService = $dispatcherService;
        $this->listenerTag = $listenerTag;
        $this->subscriberTag = $subscriberTag;
    }

    public function process(ContainerBuilder $container)
    {
        if (!$container->hasDefinition($this->dispatcherService) && !$container->hasAlias($this->dispatcherService)) {
            return;
        }

        $definition = $container->findDefinition($this->dispatcherService);

        foreach ($container->findTaggedServiceIds($this->listenerTag) as $id => $events) {
            $def = $container->getDefinition($id);
            if (!$def->isPublic()) {
                throw new \InvalidArgumentException(sprintf('The service "%s" must be public as event listeners are lazy-loaded.', $id));
            }

            if ($def->isAbstract()) {
                throw new \InvalidArgumentException(sprintf('The service "%s" must not be abstract as event listeners are lazy-loaded.', $id));
            }

            foreach ($events as $event) {
                $priority = isset($event['priority']) ? $event['priority'] : 0;

                if (!isset($event['event'])) {
                    throw new \InvalidArgumentException(sprintf('Service "%s" must define the "event" attribute on "%s" tags.', $id, $this->listenerTag));
                }

                if (!isset($event['method'])) {
                    $event['method'] = 'on'.preg_replace_callback(array(
                        '/(?<=\b)[a-z]/i',
                        '/[^a-z0-9]/i',
                    ), function ($matches) { return strtoupper($matches[0]); }, $event['event']);
                    $event['method'] = preg_replace('/[^a-z0-9]/i', '', $event['method']);
                }

                $definition->addMethodCall('addListenerService', array($event['event'], array($id, $event['method']), $priority));
            }
        }

        foreach ($container->findTaggedServiceIds($this->subscriberTag) as $id => $attributes) {
            $def = $container->getDefinition($id);
            if (!$def->isPublic()) {
                throw new \InvalidArgumentException(sprintf('The service "%s" must be public as event subscribers are lazy-loaded.', $id));
            }

            if ($def->isAbstract()) {
                throw new \InvalidArgumentException(sprintf('The service "%s" must not be abstract as event subscribers are lazy-loaded.', $id));
            }

            // We must assume that the class value has been correctly filled, even if the service is created by a factory
            $class = $container->getParameterBag()->resolveValue($def->getClass());
            $interface = 'Symfony\Component\EventDispatcher\EventSubscriberInterface';

            if (!is_subclass_of($class, $interface)) {
                if (!class_exists($class, false)) {
                    throw new \InvalidArgumentException(sprintf('Class "%s" used for service "%s" cannot be found.', $class, $id));
                }

                throw new \InvalidArgumentException(sprintf('Service "%s" must implement interface "%s".', $id, $interface));
            }

            $definition->addMethodCall('addSubscriberService', array($id, $class));
        }
    }
}
vendor/symfony/event-dispatcher/CHANGELOG.md000064400000001270151327705700014632 0ustar00CHANGELOG
=========

2.5.0
-----

 * added Debug\TraceableEventDispatcher (originally in HttpKernel)
 * changed Debug\TraceableEventDispatcherInterface to extend EventDispatcherInterface
 * added RegisterListenersPass (originally in HttpKernel)

2.1.0
-----

 * added TraceableEventDispatcherInterface
 * added ContainerAwareEventDispatcher
 * added a reference to the EventDispatcher on the Event
 * added a reference to the Event name on the event
 * added fluid interface to the dispatch() method which now returns the Event
   object
 * added GenericEvent event class
 * added the possibility for subscribers to subscribe several times for the
   same event
 * added ImmutableEventDispatcher
vendor/symfony/event-dispatcher/.gitignore000064400000000042151327705700015005 0ustar00vendor/
composer.lock
phpunit.xml
vendor/symfony/event-dispatcher/EventDispatcher.php000064400000013433151327705700016626 0ustar00<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\EventDispatcher;

/**
 * The EventDispatcherInterface is the central point of Symfony's event listener system.
 *
 * Listeners are registered on the manager and events are dispatched through the
 * manager.
 *
 * @author Guilherme Blanco <guilhermeblanco@hotmail.com>
 * @author Jonathan Wage <jonwage@gmail.com>
 * @author Roman Borschel <roman@code-factory.org>
 * @author Bernhard Schussek <bschussek@gmail.com>
 * @author Fabien Potencier <fabien@symfony.com>
 * @author Jordi Boggiano <j.boggiano@seld.be>
 * @author Jordan Alliot <jordan.alliot@gmail.com>
 */
class EventDispatcher implements EventDispatcherInterface
{
    private $listeners = array();
    private $sorted = array();

    /**
     * {@inheritdoc}
     */
    public function dispatch($eventName, Event $event = null)
    {
        if (null === $event) {
            $event = new Event();
        }

        $event->setDispatcher($this);
        $event->setName($eventName);

        if ($listeners = $this->getListeners($eventName)) {
            $this->doDispatch($listeners, $eventName, $event);
        }

        return $event;
    }

    /**
     * {@inheritdoc}
     */
    public function getListeners($eventName = null)
    {
        if (null !== $eventName) {
            if (!isset($this->listeners[$eventName])) {
                return array();
            }

            if (!isset($this->sorted[$eventName])) {
                $this->sortListeners($eventName);
            }

            return $this->sorted[$eventName];
        }

        foreach ($this->listeners as $eventName => $eventListeners) {
            if (!isset($this->sorted[$eventName])) {
                $this->sortListeners($eventName);
            }
        }

        return array_filter($this->sorted);
    }

    /**
     * Gets the listener priority for a specific event.
     *
     * Returns null if the event or the listener does not exist.
     *
     * @param string   $eventName The name of the event
     * @param callable $listener  The listener
     *
     * @return int|null The event listener priority
     */
    public function getListenerPriority($eventName, $listener)
    {
        if (!isset($this->listeners[$eventName])) {
            return;
        }

        foreach ($this->listeners[$eventName] as $priority => $listeners) {
            if (false !== \in_array($listener, $listeners, true)) {
                return $priority;
            }
        }
    }

    /**
     * {@inheritdoc}
     */
    public function hasListeners($eventName = null)
    {
        return (bool) $this->getListeners($eventName);
    }

    /**
     * {@inheritdoc}
     */
    public function addListener($eventName, $listener, $priority = 0)
    {
        $this->listeners[$eventName][$priority][] = $listener;
        unset($this->sorted[$eventName]);
    }

    /**
     * {@inheritdoc}
     */
    public function removeListener($eventName, $listener)
    {
        if (!isset($this->listeners[$eventName])) {
            return;
        }

        foreach ($this->listeners[$eventName] as $priority => $listeners) {
            if (false !== ($key = array_search($listener, $listeners, true))) {
                unset($this->listeners[$eventName][$priority][$key], $this->sorted[$eventName]);
            }
        }
    }

    /**
     * {@inheritdoc}
     */
    public function addSubscriber(EventSubscriberInterface $subscriber)
    {
        foreach ($subscriber->getSubscribedEvents() as $eventName => $params) {
            if (\is_string($params)) {
                $this->addListener($eventName, array($subscriber, $params));
            } elseif (\is_string($params[0])) {
                $this->addListener($eventName, array($subscriber, $params[0]), isset($params[1]) ? $params[1] : 0);
            } else {
                foreach ($params as $listener) {
                    $this->addListener($eventName, array($subscriber, $listener[0]), isset($listener[1]) ? $listener[1] : 0);
                }
            }
        }
    }

    /**
     * {@inheritdoc}
     */
    public function removeSubscriber(EventSubscriberInterface $subscriber)
    {
        foreach ($subscriber->getSubscribedEvents() as $eventName => $params) {
            if (\is_array($params) && \is_array($params[0])) {
                foreach ($params as $listener) {
                    $this->removeListener($eventName, array($subscriber, $listener[0]));
                }
            } else {
                $this->removeListener($eventName, array($subscriber, \is_string($params) ? $params : $params[0]));
            }
        }
    }

    /**
     * Triggers the listeners of an event.
     *
     * This method can be overridden to add functionality that is executed
     * for each listener.
     *
     * @param callable[] $listeners The event listeners
     * @param string     $eventName The name of the event to dispatch
     * @param Event      $event     The event object to pass to the event handlers/listeners
     */
    protected function doDispatch($listeners, $eventName, Event $event)
    {
        foreach ($listeners as $listener) {
            if ($event->isPropagationStopped()) {
                break;
            }
            \call_user_func($listener, $event, $eventName, $this);
        }
    }

    /**
     * Sorts the internal list of listeners for the given event by priority.
     *
     * @param string $eventName The name of the event
     */
    private function sortListeners($eventName)
    {
        krsort($this->listeners[$eventName]);
        $this->sorted[$eventName] = \call_user_func_array('array_merge', $this->listeners[$eventName]);
    }
}
vendor/symfony/event-dispatcher/README.md000064400000001142151327705700014276 0ustar00EventDispatcher Component
=========================

The EventDispatcher component provides tools that allow your application
components to communicate with each other by dispatching events and listening to
them.

Resources
---------

  * [Documentation](https://symfony.com/doc/current/components/event_dispatcher/index.html)
  * [Contributing](https://symfony.com/doc/current/contributing/index.html)
  * [Report issues](https://github.com/symfony/symfony/issues) and
    [send Pull Requests](https://github.com/symfony/symfony/pulls)
    in the [main Symfony repository](https://github.com/symfony/symfony)
vendor/symfony/event-dispatcher/Event.php000064400000006644151327705700014625 0ustar00<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\EventDispatcher;

/**
 * Event is the base class for classes containing event data.
 *
 * This class contains no event data. It is used by events that do not pass
 * state information to an event handler when an event is raised.
 *
 * You can call the method stopPropagation() to abort the execution of
 * further listeners in your event listener.
 *
 * @author Guilherme Blanco <guilhermeblanco@hotmail.com>
 * @author Jonathan Wage <jonwage@gmail.com>
 * @author Roman Borschel <roman@code-factory.org>
 * @author Bernhard Schussek <bschussek@gmail.com>
 */
class Event
{
    /**
     * @var bool Whether no further event listeners should be triggered
     */
    private $propagationStopped = false;

    /**
     * @var EventDispatcherInterface Dispatcher that dispatched this event
     */
    private $dispatcher;

    /**
     * @var string This event's name
     */
    private $name;

    /**
     * Returns whether further event listeners should be triggered.
     *
     * @see Event::stopPropagation()
     *
     * @return bool Whether propagation was already stopped for this event
     */
    public function isPropagationStopped()
    {
        return $this->propagationStopped;
    }

    /**
     * Stops the propagation of the event to further event listeners.
     *
     * If multiple event listeners are connected to the same event, no
     * further event listener will be triggered once any trigger calls
     * stopPropagation().
     */
    public function stopPropagation()
    {
        $this->propagationStopped = true;
    }

    /**
     * Stores the EventDispatcher that dispatches this Event.
     *
     * @param EventDispatcherInterface $dispatcher
     *
     * @deprecated since version 2.4, to be removed in 3.0. The event dispatcher is passed to the listener call.
     */
    public function setDispatcher(EventDispatcherInterface $dispatcher)
    {
        $this->dispatcher = $dispatcher;
    }

    /**
     * Returns the EventDispatcher that dispatches this Event.
     *
     * @return EventDispatcherInterface
     *
     * @deprecated since version 2.4, to be removed in 3.0. The event dispatcher is passed to the listener call.
     */
    public function getDispatcher()
    {
        @trigger_error('The '.__METHOD__.' method is deprecated since Symfony 2.4 and will be removed in 3.0. The event dispatcher instance can be received in the listener call instead.', E_USER_DEPRECATED);

        return $this->dispatcher;
    }

    /**
     * Gets the event's name.
     *
     * @return string
     *
     * @deprecated since version 2.4, to be removed in 3.0. The event name is passed to the listener call.
     */
    public function getName()
    {
        @trigger_error('The '.__METHOD__.' method is deprecated since Symfony 2.4 and will be removed in 3.0. The event name can be received in the listener call instead.', E_USER_DEPRECATED);

        return $this->name;
    }

    /**
     * Sets the event's name property.
     *
     * @param string $name The event name
     *
     * @deprecated since version 2.4, to be removed in 3.0. The event name is passed to the listener call.
     */
    public function setName($name)
    {
        $this->name = $name;
    }
}
vendor/symfony/event-dispatcher/ImmutableEventDispatcher.php000064400000004176151327705700020472 0ustar00<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\EventDispatcher;

/**
 * A read-only proxy for an event dispatcher.
 *
 * @author Bernhard Schussek <bschussek@gmail.com>
 */
class ImmutableEventDispatcher implements EventDispatcherInterface
{
    private $dispatcher;

    public function __construct(EventDispatcherInterface $dispatcher)
    {
        $this->dispatcher = $dispatcher;
    }

    /**
     * {@inheritdoc}
     */
    public function dispatch($eventName, Event $event = null)
    {
        return $this->dispatcher->dispatch($eventName, $event);
    }

    /**
     * {@inheritdoc}
     */
    public function addListener($eventName, $listener, $priority = 0)
    {
        throw new \BadMethodCallException('Unmodifiable event dispatchers must not be modified.');
    }

    /**
     * {@inheritdoc}
     */
    public function addSubscriber(EventSubscriberInterface $subscriber)
    {
        throw new \BadMethodCallException('Unmodifiable event dispatchers must not be modified.');
    }

    /**
     * {@inheritdoc}
     */
    public function removeListener($eventName, $listener)
    {
        throw new \BadMethodCallException('Unmodifiable event dispatchers must not be modified.');
    }

    /**
     * {@inheritdoc}
     */
    public function removeSubscriber(EventSubscriberInterface $subscriber)
    {
        throw new \BadMethodCallException('Unmodifiable event dispatchers must not be modified.');
    }

    /**
     * {@inheritdoc}
     */
    public function getListeners($eventName = null)
    {
        return $this->dispatcher->getListeners($eventName);
    }

    /**
     * {@inheritdoc}
     */
    public function getListenerPriority($eventName, $listener)
    {
        return $this->dispatcher->getListenerPriority($eventName, $listener);
    }

    /**
     * {@inheritdoc}
     */
    public function hasListeners($eventName = null)
    {
        return $this->dispatcher->hasListeners($eventName);
    }
}
vendor/symfony/event-dispatcher/LICENSE000064400000002051151327705700014024 0ustar00Copyright (c) 2004-2018 Fabien Potencier

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is furnished
to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
vendor/symfony/event-dispatcher/EventSubscriberInterface.php000064400000003020151327705700020453 0ustar00<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\EventDispatcher;

/**
 * An EventSubscriber knows himself what events he is interested in.
 * If an EventSubscriber is added to an EventDispatcherInterface, the manager invokes
 * {@link getSubscribedEvents} and registers the subscriber as a listener for all
 * returned events.
 *
 * @author Guilherme Blanco <guilhermeblanco@hotmail.com>
 * @author Jonathan Wage <jonwage@gmail.com>
 * @author Roman Borschel <roman@code-factory.org>
 * @author Bernhard Schussek <bschussek@gmail.com>
 */
interface EventSubscriberInterface
{
    /**
     * Returns an array of event names this subscriber wants to listen to.
     *
     * The array keys are event names and the value can be:
     *
     *  * The method name to call (priority defaults to 0)
     *  * An array composed of the method name to call and the priority
     *  * An array of arrays composed of the method names to call and respective
     *    priorities, or 0 if unset
     *
     * For instance:
     *
     *  * array('eventName' => 'methodName')
     *  * array('eventName' => array('methodName', $priority))
     *  * array('eventName' => array(array('methodName1', $priority), array('methodName2')))
     *
     * @return array The event names to listen to
     */
    public static function getSubscribedEvents();
}
vendor/firebase/php-jwt/composer.json000064400000001344151327705700013745 0ustar00{
    "name": "firebase/php-jwt",
    "description": "A simple library to encode and decode JSON Web Tokens (JWT) in PHP. Should conform to the current spec.",
    "homepage": "https://github.com/firebase/php-jwt",
    "authors": [
        {
            "name": "Neuman Vong",
            "email": "neuman+pear@twilio.com",
            "role": "Developer"
        },
        {
            "name": "Anant Narayanan",
            "email": "anant@php.net",
            "role": "Developer"
        }
    ],
    "license": "BSD-3-Clause",
    "require": {
        "php": ">=5.3.0"
    },
    "autoload": {
        "psr-4": {
            "Firebase\\JWT\\": "src"
        }
    },
    "require-dev": {
        "phpunit/phpunit": " 4.8.35"
    }
}
vendor/firebase/php-jwt/README.md000064400000017406151327705700012510 0ustar00[![Build Status](https://travis-ci.org/firebase/php-jwt.png?branch=master)](https://travis-ci.org/firebase/php-jwt)
[![Latest Stable Version](https://poser.pugx.org/firebase/php-jwt/v/stable)](https://packagist.org/packages/firebase/php-jwt)
[![Total Downloads](https://poser.pugx.org/firebase/php-jwt/downloads)](https://packagist.org/packages/firebase/php-jwt)
[![License](https://poser.pugx.org/firebase/php-jwt/license)](https://packagist.org/packages/firebase/php-jwt)

PHP-JWT
=======
A simple library to encode and decode JSON Web Tokens (JWT) in PHP, conforming to [RFC 7519](https://tools.ietf.org/html/rfc7519).

Installation
------------

Use composer to manage your dependencies and download PHP-JWT:

```bash
composer require firebase/php-jwt
```

Example
-------
```php
<?php
use \Firebase\JWT\JWT;

$key = "example_key";
$token = array(
    "iss" => "http://example.org",
    "aud" => "http://example.com",
    "iat" => 1356999524,
    "nbf" => 1357000000
);

/**
 * IMPORTANT:
 * You must specify supported algorithms for your application. See
 * https://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-40
 * for a list of spec-compliant algorithms.
 */
$jwt = JWT::encode($token, $key);
$decoded = JWT::decode($jwt, $key, array('HS256'));

print_r($decoded);

/*
 NOTE: This will now be an object instead of an associative array. To get
 an associative array, you will need to cast it as such:
*/

$decoded_array = (array) $decoded;

/**
 * You can add a leeway to account for when there is a clock skew times between
 * the signing and verifying servers. It is recommended that this leeway should
 * not be bigger than a few minutes.
 *
 * Source: http://self-issued.info/docs/draft-ietf-oauth-json-web-token.html#nbfDef
 */
JWT::$leeway = 60; // $leeway in seconds
$decoded = JWT::decode($jwt, $key, array('HS256'));

?>
```
Example with RS256 (openssl)
----------------------------
```php
<?php
use \Firebase\JWT\JWT;

$privateKey = <<<EOD
-----BEGIN RSA PRIVATE KEY-----
MIICXAIBAAKBgQC8kGa1pSjbSYZVebtTRBLxBz5H4i2p/llLCrEeQhta5kaQu/Rn
vuER4W8oDH3+3iuIYW4VQAzyqFpwuzjkDI+17t5t0tyazyZ8JXw+KgXTxldMPEL9
5+qVhgXvwtihXC1c5oGbRlEDvDF6Sa53rcFVsYJ4ehde/zUxo6UvS7UrBQIDAQAB
AoGAb/MXV46XxCFRxNuB8LyAtmLDgi/xRnTAlMHjSACddwkyKem8//8eZtw9fzxz
bWZ/1/doQOuHBGYZU8aDzzj59FZ78dyzNFoF91hbvZKkg+6wGyd/LrGVEB+Xre0J
Nil0GReM2AHDNZUYRv+HYJPIOrB0CRczLQsgFJ8K6aAD6F0CQQDzbpjYdx10qgK1
cP59UHiHjPZYC0loEsk7s+hUmT3QHerAQJMZWC11Qrn2N+ybwwNblDKv+s5qgMQ5
5tNoQ9IfAkEAxkyffU6ythpg/H0Ixe1I2rd0GbF05biIzO/i77Det3n4YsJVlDck
ZkcvY3SK2iRIL4c9yY6hlIhs+K9wXTtGWwJBAO9Dskl48mO7woPR9uD22jDpNSwe
k90OMepTjzSvlhjbfuPN1IdhqvSJTDychRwn1kIJ7LQZgQ8fVz9OCFZ/6qMCQGOb
qaGwHmUK6xzpUbbacnYrIM6nLSkXgOAwv7XXCojvY614ILTK3iXiLBOxPu5Eu13k
eUz9sHyD6vkgZzjtxXECQAkp4Xerf5TGfQXGXhxIX52yH+N2LtujCdkQZjXAsGdm
B2zNzvrlgRmgBrklMTrMYgm1NPcW+bRLGcwgW2PTvNM=
-----END RSA PRIVATE KEY-----
EOD;

$publicKey = <<<EOD
-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC8kGa1pSjbSYZVebtTRBLxBz5H
4i2p/llLCrEeQhta5kaQu/RnvuER4W8oDH3+3iuIYW4VQAzyqFpwuzjkDI+17t5t
0tyazyZ8JXw+KgXTxldMPEL95+qVhgXvwtihXC1c5oGbRlEDvDF6Sa53rcFVsYJ4
ehde/zUxo6UvS7UrBQIDAQAB
-----END PUBLIC KEY-----
EOD;

$token = array(
    "iss" => "example.org",
    "aud" => "example.com",
    "iat" => 1356999524,
    "nbf" => 1357000000
);

$jwt = JWT::encode($token, $privateKey, 'RS256');
echo "Encode:\n" . print_r($jwt, true) . "\n";

$decoded = JWT::decode($jwt, $publicKey, array('RS256'));

/*
 NOTE: This will now be an object instead of an associative array. To get
 an associative array, you will need to cast it as such:
*/

$decoded_array = (array) $decoded;
echo "Decode:\n" . print_r($decoded_array, true) . "\n";
?>
```

Changelog
---------

#### 5.0.0 / 2017-06-26
- Support RS384 and RS512.
  See [#117](https://github.com/firebase/php-jwt/pull/117). Thanks [@joostfaassen](https://github.com/joostfaassen)!
- Add an example for RS256 openssl.
  See [#125](https://github.com/firebase/php-jwt/pull/125). Thanks [@akeeman](https://github.com/akeeman)!
- Detect invalid Base64 encoding in signature.
  See [#162](https://github.com/firebase/php-jwt/pull/162). Thanks [@psignoret](https://github.com/psignoret)!
- Update `JWT::verify` to handle OpenSSL errors.
  See [#159](https://github.com/firebase/php-jwt/pull/159). Thanks [@bshaffer](https://github.com/bshaffer)!
- Add `array` type hinting to `decode` method
  See [#101](https://github.com/firebase/php-jwt/pull/101). Thanks [@hywak](https://github.com/hywak)!
- Add all JSON error types.
  See [#110](https://github.com/firebase/php-jwt/pull/110). Thanks [@gbalduzzi](https://github.com/gbalduzzi)!
- Bugfix 'kid' not in given key list.
  See [#129](https://github.com/firebase/php-jwt/pull/129). Thanks [@stampycode](https://github.com/stampycode)!
- Miscellaneous cleanup, documentation and test fixes.
  See [#107](https://github.com/firebase/php-jwt/pull/107), [#115](https://github.com/firebase/php-jwt/pull/115),
  [#160](https://github.com/firebase/php-jwt/pull/160), [#161](https://github.com/firebase/php-jwt/pull/161), and
  [#165](https://github.com/firebase/php-jwt/pull/165). Thanks [@akeeman](https://github.com/akeeman),
  [@chinedufn](https://github.com/chinedufn), and [@bshaffer](https://github.com/bshaffer)!

#### 4.0.0 / 2016-07-17
- Add support for late static binding. See [#88](https://github.com/firebase/php-jwt/pull/88) for details. Thanks to [@chappy84](https://github.com/chappy84)!
- Use static `$timestamp` instead of `time()` to improve unit testing. See [#93](https://github.com/firebase/php-jwt/pull/93) for details. Thanks to [@josephmcdermott](https://github.com/josephmcdermott)!
- Fixes to exceptions classes. See [#81](https://github.com/firebase/php-jwt/pull/81) for details. Thanks to [@Maks3w](https://github.com/Maks3w)!
- Fixes to PHPDoc. See [#76](https://github.com/firebase/php-jwt/pull/76) for details. Thanks to [@akeeman](https://github.com/akeeman)!

#### 3.0.0 / 2015-07-22
- Minimum PHP version updated from `5.2.0` to `5.3.0`.
- Add `\Firebase\JWT` namespace. See
[#59](https://github.com/firebase/php-jwt/pull/59) for details. Thanks to
[@Dashron](https://github.com/Dashron)!
- Require a non-empty key to decode and verify a JWT. See
[#60](https://github.com/firebase/php-jwt/pull/60) for details. Thanks to
[@sjones608](https://github.com/sjones608)!
- Cleaner documentation blocks in the code. See
[#62](https://github.com/firebase/php-jwt/pull/62) for details. Thanks to
[@johanderuijter](https://github.com/johanderuijter)!

#### 2.2.0 / 2015-06-22
- Add support for adding custom, optional JWT headers to `JWT::encode()`. See
[#53](https://github.com/firebase/php-jwt/pull/53/files) for details. Thanks to
[@mcocaro](https://github.com/mcocaro)!

#### 2.1.0 / 2015-05-20
- Add support for adding a leeway to `JWT:decode()` that accounts for clock skew
between signing and verifying entities. Thanks to [@lcabral](https://github.com/lcabral)!
- Add support for passing an object implementing the `ArrayAccess` interface for
`$keys` argument in `JWT::decode()`. Thanks to [@aztech-dev](https://github.com/aztech-dev)!

#### 2.0.0 / 2015-04-01
- **Note**: It is strongly recommended that you update to > v2.0.0 to address
  known security vulnerabilities in prior versions when both symmetric and
  asymmetric keys are used together.
- Update signature for `JWT::decode(...)` to require an array of supported
  algorithms to use when verifying token signatures.


Tests
-----
Run the tests using phpunit:

```bash
$ pear install PHPUnit
$ phpunit --configuration phpunit.xml.dist
PHPUnit 3.7.10 by Sebastian Bergmann.
.....
Time: 0 seconds, Memory: 2.50Mb
OK (5 tests, 5 assertions)
```

New Lines in private keys
-----

If your private key contains `\n` characters, be sure to wrap it in double quotes `""`
and not single quotes `''` in order to properly interpret the escaped characters.

License
-------
[3-Clause BSD](http://opensource.org/licenses/BSD-3-Clause).
vendor/firebase/php-jwt/src/BeforeValidException.php000064400000000141151327705700016556 0ustar00<?php
namespace Firebase\JWT;

class BeforeValidException extends \UnexpectedValueException
{

}
vendor/firebase/php-jwt/src/ExpiredException.php000064400000000135151327705700015777 0ustar00<?php
namespace Firebase\JWT;

class ExpiredException extends \UnexpectedValueException
{

}
vendor/firebase/php-jwt/src/JWT.php000064400000034014151327705700013167 0ustar00<?php

namespace Firebase\JWT;
use \DomainException;
use \InvalidArgumentException;
use \UnexpectedValueException;
use \DateTime;

/**
 * JSON Web Token implementation, based on this spec:
 * https://tools.ietf.org/html/rfc7519
 *
 * PHP version 5
 *
 * @category Authentication
 * @package  Authentication_JWT
 * @author   Neuman Vong <neuman@twilio.com>
 * @author   Anant Narayanan <anant@php.net>
 * @license  http://opensource.org/licenses/BSD-3-Clause 3-clause BSD
 * @link     https://github.com/firebase/php-jwt
 */
class JWT
{

    /**
     * When checking nbf, iat or expiration times,
     * we want to provide some extra leeway time to
     * account for clock skew.
     */
    public static $leeway = 0;

    /**
     * Allow the current timestamp to be specified.
     * Useful for fixing a value within unit testing.
     *
     * Will default to PHP time() value if null.
     */
    public static $timestamp = null;

    public static $supported_algs = array(
        'HS256' => array('hash_hmac', 'SHA256'),
        'HS512' => array('hash_hmac', 'SHA512'),
        'HS384' => array('hash_hmac', 'SHA384'),
        'RS256' => array('openssl', 'SHA256'),
        'RS384' => array('openssl', 'SHA384'),
        'RS512' => array('openssl', 'SHA512'),
    );

    /**
     * Decodes a JWT string into a PHP object.
     *
     * @param string        $jwt            The JWT
     * @param string|array  $key            The key, or map of keys.
     *                                      If the algorithm used is asymmetric, this is the public key
     * @param array         $allowed_algs   List of supported verification algorithms
     *                                      Supported algorithms are 'HS256', 'HS384', 'HS512' and 'RS256'
     *
     * @return object The JWT's payload as a PHP object
     *
     * @throws UnexpectedValueException     Provided JWT was invalid
     * @throws SignatureInvalidException    Provided JWT was invalid because the signature verification failed
     * @throws BeforeValidException         Provided JWT is trying to be used before it's eligible as defined by 'nbf'
     * @throws BeforeValidException         Provided JWT is trying to be used before it's been created as defined by 'iat'
     * @throws ExpiredException             Provided JWT has since expired, as defined by the 'exp' claim
     *
     * @uses jsonDecode
     * @uses urlsafeB64Decode
     */
    public static function decode($jwt, $key, array $allowed_algs = array())
    {
        $timestamp = is_null(static::$timestamp) ? time() : static::$timestamp;

        if (empty($key)) {
            throw new InvalidArgumentException('Key may not be empty');
        }
        $tks = explode('.', $jwt);
        if (count($tks) != 3) {
            throw new UnexpectedValueException('Wrong number of segments');
        }
        list($headb64, $bodyb64, $cryptob64) = $tks;
        if (null === ($header = static::jsonDecode(static::urlsafeB64Decode($headb64)))) {
            throw new UnexpectedValueException('Invalid header encoding');
        }
        if (null === $payload = static::jsonDecode(static::urlsafeB64Decode($bodyb64))) {
            throw new UnexpectedValueException('Invalid claims encoding');
        }
        if (false === ($sig = static::urlsafeB64Decode($cryptob64))) {
            throw new UnexpectedValueException('Invalid signature encoding');
        }
        if (empty($header->alg)) {
            throw new UnexpectedValueException('Empty algorithm');
        }
        if (empty(static::$supported_algs[$header->alg])) {
            throw new UnexpectedValueException('Algorithm not supported');
        }
        if (!in_array($header->alg, $allowed_algs)) {
            throw new UnexpectedValueException('Algorithm not allowed');
        }
        if (is_array($key) || $key instanceof \ArrayAccess) {
            if (isset($header->kid)) {
                if (!isset($key[$header->kid])) {
                    throw new UnexpectedValueException('"kid" invalid, unable to lookup correct key');
                }
                $key = $key[$header->kid];
            } else {
                throw new UnexpectedValueException('"kid" empty, unable to lookup correct key');
            }
        }

        // Check the signature
        if (!static::verify("$headb64.$bodyb64", $sig, $key, $header->alg)) {
            throw new SignatureInvalidException('Signature verification failed');
        }

        // Check if the nbf if it is defined. This is the time that the
        // token can actually be used. If it's not yet that time, abort.
        if (isset($payload->nbf) && $payload->nbf > ($timestamp + static::$leeway)) {
            throw new BeforeValidException(
                'Cannot handle token prior to ' . date(DateTime::ISO8601, $payload->nbf)
            );
        }

        // Check that this token has been created before 'now'. This prevents
        // using tokens that have been created for later use (and haven't
        // correctly used the nbf claim).
        if (isset($payload->iat) && $payload->iat > ($timestamp + static::$leeway)) {
            throw new BeforeValidException(
                'Cannot handle token prior to ' . date(DateTime::ISO8601, $payload->iat)
            );
        }

        // Check if this token has expired.
        if (isset($payload->exp) && ($timestamp - static::$leeway) >= $payload->exp) {
            throw new ExpiredException('Expired token');
        }

        return $payload;
    }

    /**
     * Converts and signs a PHP object or array into a JWT string.
     *
     * @param object|array  $payload    PHP object or array
     * @param string        $key        The secret key.
     *                                  If the algorithm used is asymmetric, this is the private key
     * @param string        $alg        The signing algorithm.
     *                                  Supported algorithms are 'HS256', 'HS384', 'HS512' and 'RS256'
     * @param mixed         $keyId
     * @param array         $head       An array with header elements to attach
     *
     * @return string A signed JWT
     *
     * @uses jsonEncode
     * @uses urlsafeB64Encode
     */
    public static function encode($payload, $key, $alg = 'HS256', $keyId = null, $head = null)
    {
        $header = array('typ' => 'JWT', 'alg' => $alg);
        if ($keyId !== null) {
            $header['kid'] = $keyId;
        }
        if ( isset($head) && is_array($head) ) {
            $header = array_merge($head, $header);
        }
        $segments = array();
        $segments[] = static::urlsafeB64Encode(static::jsonEncode($header));
        $segments[] = static::urlsafeB64Encode(static::jsonEncode($payload));
        $signing_input = implode('.', $segments);

        $signature = static::sign($signing_input, $key, $alg);
        $segments[] = static::urlsafeB64Encode($signature);

        return implode('.', $segments);
    }

    /**
     * Sign a string with a given key and algorithm.
     *
     * @param string            $msg    The message to sign
     * @param string|resource   $key    The secret key
     * @param string            $alg    The signing algorithm.
     *                                  Supported algorithms are 'HS256', 'HS384', 'HS512' and 'RS256'
     *
     * @return string An encrypted message
     *
     * @throws DomainException Unsupported algorithm was specified
     */
    public static function sign($msg, $key, $alg = 'HS256')
    {
        if (empty(static::$supported_algs[$alg])) {
            throw new DomainException('Algorithm not supported');
        }
        list($function, $algorithm) = static::$supported_algs[$alg];
        switch($function) {
            case 'hash_hmac':
                return hash_hmac($algorithm, $msg, $key, true);
            case 'openssl':
                $signature = '';
                $success = openssl_sign($msg, $signature, $key, $algorithm);
                if (!$success) {
                    throw new DomainException("OpenSSL unable to sign data");
                } else {
                    return $signature;
                }
        }
    }

    /**
     * Verify a signature with the message, key and method. Not all methods
     * are symmetric, so we must have a separate verify and sign method.
     *
     * @param string            $msg        The original message (header and body)
     * @param string            $signature  The original signature
     * @param string|resource   $key        For HS*, a string key works. for RS*, must be a resource of an openssl public key
     * @param string            $alg        The algorithm
     *
     * @return bool
     *
     * @throws DomainException Invalid Algorithm or OpenSSL failure
     */
    private static function verify($msg, $signature, $key, $alg)
    {
        if (empty(static::$supported_algs[$alg])) {
            throw new DomainException('Algorithm not supported');
        }

        list($function, $algorithm) = static::$supported_algs[$alg];
        switch($function) {
            case 'openssl':
                $success = openssl_verify($msg, $signature, $key, $algorithm);
                if ($success === 1) {
                    return true;
                } elseif ($success === 0) {
                    return false;
                }
                // returns 1 on success, 0 on failure, -1 on error.
                throw new DomainException(
                    'OpenSSL error: ' . openssl_error_string()
                );
            case 'hash_hmac':
            default:
                $hash = hash_hmac($algorithm, $msg, $key, true);
                if (function_exists('hash_equals')) {
                    return hash_equals($signature, $hash);
                }
                $len = min(static::safeStrlen($signature), static::safeStrlen($hash));

                $status = 0;
                for ($i = 0; $i < $len; $i++) {
                    $status |= (ord($signature[$i]) ^ ord($hash[$i]));
                }
                $status |= (static::safeStrlen($signature) ^ static::safeStrlen($hash));

                return ($status === 0);
        }
    }

    /**
     * Decode a JSON string into a PHP object.
     *
     * @param string $input JSON string
     *
     * @return object Object representation of JSON string
     *
     * @throws DomainException Provided string was invalid JSON
     */
    public static function jsonDecode($input)
    {
        if (version_compare(PHP_VERSION, '5.4.0', '>=') && !(defined('JSON_C_VERSION') && PHP_INT_SIZE > 4)) {
            /** In PHP >=5.4.0, json_decode() accepts an options parameter, that allows you
             * to specify that large ints (like Steam Transaction IDs) should be treated as
             * strings, rather than the PHP default behaviour of converting them to floats.
             */
            $obj = json_decode($input, false, 512, JSON_BIGINT_AS_STRING);
        } else {
            /** Not all servers will support that, however, so for older versions we must
             * manually detect large ints in the JSON string and quote them (thus converting
             *them to strings) before decoding, hence the preg_replace() call.
             */
            $max_int_length = strlen((string) PHP_INT_MAX) - 1;
            $json_without_bigints = preg_replace('/:\s*(-?\d{'.$max_int_length.',})/', ': "$1"', $input);
            $obj = json_decode($json_without_bigints);
        }

        if (function_exists('json_last_error') && $errno = json_last_error()) {
            static::handleJsonError($errno);
        } elseif ($obj === null && $input !== 'null') {
            throw new DomainException('Null result with non-null input');
        }
        return $obj;
    }

    /**
     * Encode a PHP object into a JSON string.
     *
     * @param object|array $input A PHP object or array
     *
     * @return string JSON representation of the PHP object or array
     *
     * @throws DomainException Provided object could not be encoded to valid JSON
     */
    public static function jsonEncode($input)
    {
        $json = json_encode($input);
        if (function_exists('json_last_error') && $errno = json_last_error()) {
            static::handleJsonError($errno);
        } elseif ($json === 'null' && $input !== null) {
            throw new DomainException('Null result with non-null input');
        }
        return $json;
    }

    /**
     * Decode a string with URL-safe Base64.
     *
     * @param string $input A Base64 encoded string
     *
     * @return string A decoded string
     */
    public static function urlsafeB64Decode($input)
    {
        $remainder = strlen($input) % 4;
        if ($remainder) {
            $padlen = 4 - $remainder;
            $input .= str_repeat('=', $padlen);
        }
        return base64_decode(strtr($input, '-_', '+/'));
    }

    /**
     * Encode a string with URL-safe Base64.
     *
     * @param string $input The string you want encoded
     *
     * @return string The base64 encode of what you passed in
     */
    public static function urlsafeB64Encode($input)
    {
        return str_replace('=', '', strtr(base64_encode($input), '+/', '-_'));
    }

    /**
     * Helper method to create a JSON error.
     *
     * @param int $errno An error number from json_last_error()
     *
     * @return void
     */
    private static function handleJsonError($errno)
    {
        $messages = array(
            JSON_ERROR_DEPTH => 'Maximum stack depth exceeded',
            JSON_ERROR_STATE_MISMATCH => 'Invalid or malformed JSON',
            JSON_ERROR_CTRL_CHAR => 'Unexpected control character found',
            JSON_ERROR_SYNTAX => 'Syntax error, malformed JSON',
            JSON_ERROR_UTF8 => 'Malformed UTF-8 characters' //PHP >= 5.3.3
        );
        throw new DomainException(
            isset($messages[$errno])
            ? $messages[$errno]
            : 'Unknown JSON error: ' . $errno
        );
    }

    /**
     * Get the number of bytes in cryptographic strings.
     *
     * @param string
     *
     * @return int
     */
    private static function safeStrlen($str)
    {
        if (function_exists('mb_strlen')) {
            return mb_strlen($str, '8bit');
        }
        return strlen($str);
    }
}
vendor/firebase/php-jwt/src/SignatureInvalidException.php000064400000000146151327705700017651 0ustar00<?php
namespace Firebase\JWT;

class SignatureInvalidException extends \UnexpectedValueException
{

}
vendor/firebase/php-jwt/LICENSE000064400000002760151327705700012233 0ustar00Copyright (c) 2011, Neuman Vong

All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.

    * Redistributions in binary form must reproduce the above
      copyright notice, this list of conditions and the following
      disclaimer in the documentation and/or other materials provided
      with the distribution.

    * Neither the name of Neuman Vong nor the names of other
      contributors may be used to endorse or promote products derived
      from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
vendor/ralouphie/getallheaders/tests/GetAllHeadersTest.php000064400000007013151327705700020024 0ustar00<?php

class GetAllHeadersTest extends \PHPUnit_Framework_TestCase
{

    /**
     * @dataProvider testWorksData
     */
    public function testWorks($test_type, $expected, $server)
    {
        foreach ($server as $key => $val) {
            $_SERVER[$key] = $val;
        }
        $result = getallheaders();
        $this->assertEquals($expected, $result, "Error testing $test_type works.");
    }

    public function testWorksData()
    {
        return array(
            array(
                'normal case',
                array(
                    'Key-One'                 => 'foo',
                    'Key-Two'                 => 'bar',
                    'Another-Key-For-Testing' => 'baz'
                ),
                array(
                    'HTTP_KEY_ONE'                 => 'foo',
                    'HTTP_KEY_TWO'                 => 'bar',
                    'HTTP_ANOTHER_KEY_FOR_TESTING' => 'baz'
                )
            ),
            array(
                'Content-Type',
                array(
                    'Content-Type' => 'two'
                ),
                array(
                    'HTTP_CONTENT_TYPE' => 'one',
                    'CONTENT_TYPE'      => 'two'
                )
            ),
            array(
                'Content-Length',
                array(
                    'Content-Length' => '222'
                ),
                array(
                    'CONTENT_LENGTH'      => '222',
                    'HTTP_CONTENT_LENGTH' => '111'
                )
            ),
            array(
                'Content-Length (HTTP_CONTENT_LENGTH only)',
                array(
                    'Content-Length' => '111'
                ),
                array(
                    'HTTP_CONTENT_LENGTH' => '111'
                )
            ),
            array(
                'Content-MD5',
                array(
                    'Content-Md5' => 'aef123'
                ),
                array(
                    'CONTENT_MD5'      => 'aef123',
                    'HTTP_CONTENT_MD5' => 'fea321'
                )
            ),
            array(
                'Content-MD5 (HTTP_CONTENT_MD5 only)',
                array(
                    'Content-Md5' => 'f123'
                ),
                array(
                    'HTTP_CONTENT_MD5' => 'f123'
                )
            ),
            array(
                'Authorization (normal)',
                array(
                    'Authorization' => 'testing'
                ),
                array(
                    'HTTP_AUTHORIZATION' => 'testing',
                )
            ),
            array(
                'Authorization (redirect)',
                array(
                    'Authorization' => 'testing redirect'
                ),
                array(
                    'REDIRECT_HTTP_AUTHORIZATION' => 'testing redirect',
                )
            ),
            array(
                'Authorization (PHP_AUTH_USER + PHP_AUTH_PW)',
                array(
                    'Authorization' => 'Basic ' . base64_encode('foo:bar')
                ),
                array(
                    'PHP_AUTH_USER' => 'foo',
                    'PHP_AUTH_PW'   => 'bar'
                )
            ),
            array(
                'Authorization (PHP_AUTH_DIGEST)',
                array(
                    'Authorization' => 'example-digest'
                ),
                array(
                    'PHP_AUTH_DIGEST' => 'example-digest'
                )
            )
        );
    }
}
vendor/ralouphie/getallheaders/composer.json000064400000000572151327705700015372 0ustar00{
	"name": "ralouphie/getallheaders",
	"description": "A polyfill for getallheaders.",
	"license": "MIT",
	"authors": [
		{
			"name": "Ralph Khattar",
			"email": "ralph.khattar@gmail.com"
		}
	],
	"require": {
		"php": ">=5.3"
	},
	"require-dev": {
		"phpunit/phpunit": "~3.7.0",
		"satooshi/php-coveralls": ">=1.0"
	},
	"autoload": {
		"files": ["src/getallheaders.php"]
	}
}vendor/ralouphie/getallheaders/src/getallheaders.php000064400000003150151327705700016747 0ustar00<?php

if (!function_exists('getallheaders')) {

    /**
     * Get all HTTP header key/values as an associative array for the current request.
     *
     * @return string[string] The HTTP header key/value pairs.
     */
    function getallheaders()
    {
        $headers = array();

        $copy_server = array(
            'CONTENT_TYPE'   => 'Content-Type',
            'CONTENT_LENGTH' => 'Content-Length',
            'CONTENT_MD5'    => 'Content-Md5',
        );

        foreach ($_SERVER as $key => $value) {
            if (substr($key, 0, 5) === 'HTTP_') {
                $key = substr($key, 5);
                if (!isset($copy_server[$key]) || !isset($_SERVER[$key])) {
                    $key = str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', $key))));
                    $headers[$key] = $value;
                }
            } elseif (isset($copy_server[$key])) {
                $headers[$copy_server[$key]] = $value;
            }
        }

        if (!isset($headers['Authorization'])) {
            if (isset($_SERVER['REDIRECT_HTTP_AUTHORIZATION'])) {
                $headers['Authorization'] = $_SERVER['REDIRECT_HTTP_AUTHORIZATION'];
            } elseif (isset($_SERVER['PHP_AUTH_USER'])) {
                $basic_pass = isset($_SERVER['PHP_AUTH_PW']) ? $_SERVER['PHP_AUTH_PW'] : '';
                $headers['Authorization'] = 'Basic ' . base64_encode($_SERVER['PHP_AUTH_USER'] . ':' . $basic_pass);
            } elseif (isset($_SERVER['PHP_AUTH_DIGEST'])) {
                $headers['Authorization'] = $_SERVER['PHP_AUTH_DIGEST'];
            }
        }

        return $headers;
    }

}
vendor/ralouphie/getallheaders/.travis.yml000064400000000332151327705700014753 0ustar00language: php

php:
  - 5.3
  - 5.4
  - 5.5
  - 5.6
  - 7.0

before_script:
  - composer install

script:
  - mkdir -p build/logs
  - php vendor/bin/phpunit -c phpunit.xml

after_script:
  - php vendor/bin/coveralls -vvendor/ralouphie/getallheaders/.gitignore000064400000000065151327705700014635 0ustar00.idea
.DS_store
/vendor/
composer.phar
composer.lock
vendor/ralouphie/getallheaders/phpunit.xml000064400000000641151327705700015056 0ustar00<phpunit
	bootstrap="vendor/autoload.php"
	convertErrorsToExceptions="true"
	convertNoticesToExceptions="true"
	convertWarningsToExceptions="true"
	strict="true">

	<testsuite>
		<directory>./tests</directory>
	</testsuite>

	<filter>
		<whitelist>
			<directory suffix=".php">src</directory>
		</whitelist>
	</filter>

	<logging>
		<log type="coverage-clover" target="build/logs/clover.xml"/>
	</logging>

</phpunit>vendor/ralouphie/getallheaders/README.md000064400000001714151327705700014126 0ustar00getallheaders
=============

PHP `getallheaders()` polyfill. Compatible with PHP >= 5.3.

[![Build Status](https://travis-ci.org/ralouphie/getallheaders.svg?branch=master)](https://travis-ci.org/ralouphie/getallheaders)
[![Coverage Status](https://coveralls.io/repos/ralouphie/getallheaders/badge.png?branch=master)](https://coveralls.io/r/ralouphie/getallheaders?branch=master)
[![Latest Stable Version](https://poser.pugx.org/ralouphie/getallheaders/v/stable.png)](https://packagist.org/packages/ralouphie/getallheaders)
[![Latest Unstable Version](https://poser.pugx.org/ralouphie/getallheaders/v/unstable.png)](https://packagist.org/packages/ralouphie/getallheaders)
[![License](https://poser.pugx.org/ralouphie/getallheaders/license.png)](https://packagist.org/packages/ralouphie/getallheaders)


This is a simple polyfill for [`getallheaders()`](http://www.php.net/manual/en/function.getallheaders.php).

## Install

```
composer require ralouphie/getallheaders
```
vendor/ralouphie/getallheaders/LICENSE000064400000002070151327705700013650 0ustar00The MIT License (MIT)

Copyright (c) 2014 Ralph Khattar

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
vendor/autoload.php000064400000000262151327705700010371 0ustar00<?php

// autoload.php @generated by Composer

require_once __DIR__ . '/composer/autoload_real.php';

return ComposerAutoloaderInit381ef710b1ae857a1b0cca4550c7cd7e::getLoader();
vendor/phpseclib/phpseclib/AUTHORS000064400000000427151327705700013045 0ustar00phpseclib Lead Developer:  TerraFrost (Jim Wigginton)

phpseclib Developers:      monnerat (Patrick Monnerat)
                           bantu (Andreas Fischer)
                           petrich (Hans-Jürgen Petrich)
                           GrahamCampbell (Graham Campbell)
vendor/phpseclib/phpseclib/phpseclib/openssl.cnf000064400000000150151327705700016112 0ustar00# minimalist openssl.cnf file for use with phpseclib

HOME			= .
RANDFILE		= $ENV::HOME/.rnd

[ v3_ca ]
vendor/phpseclib/phpseclib/phpseclib/Crypt/Hash.php000064400000074407151327705700016454 0ustar00<?php

/**
 * Pure-PHP implementations of keyed-hash message authentication codes (HMACs) and various cryptographic hashing functions.
 *
 * Uses hash() or mhash() if available and an internal implementation, otherwise.  Currently supports the following:
 *
 * md2, md5, md5-96, sha1, sha1-96, sha256, sha256-96, sha384, and sha512, sha512-96
 *
 * If {@link self::setKey() setKey()} is called, {@link self::hash() hash()} will return the HMAC as opposed to
 * the hash.  If no valid algorithm is provided, sha1 will be used.
 *
 * PHP versions 4 and 5
 *
 * {@internal The variable names are the same as those in
 * {@link http://tools.ietf.org/html/rfc2104#section-2 RFC2104}.}}
 *
 * Here's a short example of how to use this library:
 * <code>
 * <?php
 *    include 'Crypt/Hash.php';
 *
 *    $hash = new Crypt_Hash('sha1');
 *
 *    $hash->setKey('abcdefg');
 *
 *    echo base64_encode($hash->hash('abcdefg'));
 * ?>
 * </code>
 *
 * LICENSE: Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 *
 * @category  Crypt
 * @package   Crypt_Hash
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2007 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      http://phpseclib.sourceforge.net
 */

/**#@+
 * @access private
 * @see self::Crypt_Hash()
 */
/**
 * Toggles the internal implementation
 */
define('CRYPT_HASH_MODE_INTERNAL', 1);
/**
 * Toggles the mhash() implementation, which has been deprecated on PHP 5.3.0+.
 */
define('CRYPT_HASH_MODE_MHASH',    2);
/**
 * Toggles the hash() implementation, which works on PHP 5.1.2+.
 */
define('CRYPT_HASH_MODE_HASH',     3);
/**#@-*/

/**
 * Pure-PHP implementations of keyed-hash message authentication codes (HMACs) and various cryptographic hashing functions.
 *
 * @package Crypt_Hash
 * @author  Jim Wigginton <terrafrost@php.net>
 * @access  public
 */
class Crypt_Hash
{
    /**
     * Hash Parameter
     *
     * @see self::setHash()
     * @var int
     * @access private
     */
    var $hashParam;

    /**
     * Byte-length of compression blocks / key (Internal HMAC)
     *
     * @see self::setAlgorithm()
     * @var int
     * @access private
     */
    var $b;

    /**
     * Byte-length of hash output (Internal HMAC)
     *
     * @see self::setHash()
     * @var int
     * @access private
     */
    var $l = false;

    /**
     * Hash Algorithm
     *
     * @see self::setHash()
     * @var string
     * @access private
     */
    var $hash;

    /**
     * Key
     *
     * @see self::setKey()
     * @var string
     * @access private
     */
    var $key = false;

    /**
     * Computed Key
     *
     * @see self::_computeKey()
     * @var string
     * @access private
     */
    var $computedKey = false;

    /**
     * Outer XOR (Internal HMAC)
     *
     * @see self::setKey()
     * @var string
     * @access private
     */
    var $opad;

    /**
     * Inner XOR (Internal HMAC)
     *
     * @see self::setKey()
     * @var string
     * @access private
     */
    var $ipad;

    /**
     * Default Constructor.
     *
     * @param string $hash
     * @return Crypt_Hash
     * @access public
     */
    function __construct($hash = 'sha1')
    {
        if (!defined('CRYPT_HASH_MODE')) {
            switch (true) {
                case extension_loaded('hash'):
                    define('CRYPT_HASH_MODE', CRYPT_HASH_MODE_HASH);
                    break;
                case extension_loaded('mhash'):
                    define('CRYPT_HASH_MODE', CRYPT_HASH_MODE_MHASH);
                    break;
                default:
                    define('CRYPT_HASH_MODE', CRYPT_HASH_MODE_INTERNAL);
            }
        }

        $this->setHash($hash);
    }

    /**
     * PHP4 compatible Default Constructor.
     *
     * @see self::__construct()
     * @param int $mode
     * @access public
     */
    function Crypt_Hash($hash = 'sha1')
    {
        $this->__construct($hash);
    }

    /**
     * Sets the key for HMACs
     *
     * Keys can be of any length.
     *
     * @access public
     * @param string $key
     */
    function setKey($key = false)
    {
        $this->key = $key;
        $this->_computeKey();
    }

    /**
     * Pre-compute the key used by the HMAC
     *
     * Quoting http://tools.ietf.org/html/rfc2104#section-2, "Applications that use keys longer than B bytes
     * will first hash the key using H and then use the resultant L byte string as the actual key to HMAC."
     *
     * As documented in https://www.reddit.com/r/PHP/comments/9nct2l/symfonypolyfill_hash_pbkdf2_correct_fix_for/
     * when doing an HMAC multiple times it's faster to compute the hash once instead of computing it during
     * every call
     *
     * @access private
     */
    function _computeKey()
    {
        if ($this->key === false) {
            $this->computedKey = false;
            return;
        }

        if (strlen($this->key) <= $this->b) {
            $this->computedKey = $this->key;
            return;
        }

        switch ($mode) {
            case CRYPT_HASH_MODE_MHASH:
                $this->computedKey = mhash($this->hash, $this->key);
                break;
            case CRYPT_HASH_MODE_HASH:
                $this->computedKey = hash($this->hash, $this->key, true);
                break;
            case CRYPT_HASH_MODE_INTERNAL:
                $this->computedKey = call_user_func($this->hash, $this->key);
        }
    }

    /**
     * Gets the hash function.
     *
     * As set by the constructor or by the setHash() method.
     *
     * @access public
     * @return string
     */
    function getHash()
    {
        return $this->hashParam;
    }

    /**
     * Sets the hash function.
     *
     * @access public
     * @param string $hash
     */
    function setHash($hash)
    {
        $this->hashParam = $hash = strtolower($hash);
        switch ($hash) {
            case 'md5-96':
            case 'sha1-96':
            case 'sha256-96':
            case 'sha512-96':
                $hash = substr($hash, 0, -3);
                $this->l = 12; // 96 / 8 = 12
                break;
            case 'md2':
            case 'md5':
                $this->l = 16;
                break;
            case 'sha1':
                $this->l = 20;
                break;
            case 'sha256':
                $this->l = 32;
                break;
            case 'sha384':
                $this->l = 48;
                break;
            case 'sha512':
                $this->l = 64;
        }

        switch ($hash) {
            case 'md2-96':
            case 'md2':
                $this->b = 16;
            case 'md5-96':
            case 'sha1-96':
            case 'sha224-96':
            case 'sha256-96':
            case 'md2':
            case 'md5':
            case 'sha1':
            case 'sha224':
            case 'sha256':
                $this->b = 64;
                break;
            default:
                $this->b = 128;
        }

        switch ($hash) {
            case 'md2':
                $mode = CRYPT_HASH_MODE == CRYPT_HASH_MODE_HASH && in_array('md2', hash_algos()) ?
                    CRYPT_HASH_MODE_HASH : CRYPT_HASH_MODE_INTERNAL;
                break;
            case 'sha384':
            case 'sha512':
                $mode = CRYPT_HASH_MODE == CRYPT_HASH_MODE_MHASH ? CRYPT_HASH_MODE_INTERNAL : CRYPT_HASH_MODE;
                break;
            default:
                $mode = CRYPT_HASH_MODE;
        }

        switch ($mode) {
            case CRYPT_HASH_MODE_MHASH:
                switch ($hash) {
                    case 'md5':
                        $this->hash = MHASH_MD5;
                        break;
                    case 'sha256':
                        $this->hash = MHASH_SHA256;
                        break;
                    case 'sha1':
                    default:
                        $this->hash = MHASH_SHA1;
                }
                $this->_computeKey();
                return;
            case CRYPT_HASH_MODE_HASH:
                switch ($hash) {
                    case 'md5':
                        $this->hash = 'md5';
                        return;
                    case 'md2':
                    case 'sha256':
                    case 'sha384':
                    case 'sha512':
                        $this->hash = $hash;
                        return;
                    case 'sha1':
                    default:
                        $this->hash = 'sha1';
                }
                $this->_computeKey();
                return;
        }

        switch ($hash) {
            case 'md2':
                $this->hash = array($this, '_md2');
                break;
            case 'md5':
                $this->hash = array($this, '_md5');
                break;
            case 'sha256':
                $this->hash = array($this, '_sha256');
                break;
            case 'sha384':
            case 'sha512':
                $this->hash = array($this, '_sha512');
                break;
            case 'sha1':
            default:
                $this->hash = array($this, '_sha1');
        }

        $this->ipad = str_repeat(chr(0x36), $this->b);
        $this->opad = str_repeat(chr(0x5C), $this->b);

        $this->_computeKey();
    }

    /**
     * Compute the HMAC.
     *
     * @access public
     * @param string $text
     * @return string
     */
    function hash($text)
    {
        $mode = is_array($this->hash) ? CRYPT_HASH_MODE_INTERNAL : CRYPT_HASH_MODE;

        if (!empty($this->key) || is_string($this->key)) {
            switch ($mode) {
                case CRYPT_HASH_MODE_MHASH:
                    $output = mhash($this->hash, $text, $this->computedKey);
                    break;
                case CRYPT_HASH_MODE_HASH:
                    $output = hash_hmac($this->hash, $text, $this->computedKey, true);
                    break;
                case CRYPT_HASH_MODE_INTERNAL:
                    $key    = str_pad($this->computedKey, $this->b, chr(0)); // step 1
                    $temp   = $this->ipad ^ $key;                            // step 2
                    $temp  .= $text;                                         // step 3
                    $temp   = call_user_func($this->hash, $temp);            // step 4
                    $output = $this->opad ^ $key;                            // step 5
                    $output.= $temp;                                         // step 6
                    $output = call_user_func($this->hash, $output);          // step 7
            }
        } else {
            switch ($mode) {
                case CRYPT_HASH_MODE_MHASH:
                    $output = mhash($this->hash, $text);
                    break;
                case CRYPT_HASH_MODE_HASH:
                    $output = hash($this->hash, $text, true);
                    break;
                case CRYPT_HASH_MODE_INTERNAL:
                    $output = call_user_func($this->hash, $text);
            }
        }

        return substr($output, 0, $this->l);
    }

    /**
     * Returns the hash length (in bytes)
     *
     * @access public
     * @return int
     */
    function getLength()
    {
        return $this->l;
    }

    /**
     * Wrapper for MD5
     *
     * @access private
     * @param string $m
     */
    function _md5($m)
    {
        return pack('H*', md5($m));
    }

    /**
     * Wrapper for SHA1
     *
     * @access private
     * @param string $m
     */
    function _sha1($m)
    {
        return pack('H*', sha1($m));
    }

    /**
     * Pure-PHP implementation of MD2
     *
     * See {@link http://tools.ietf.org/html/rfc1319 RFC1319}.
     *
     * @access private
     * @param string $m
     */
    function _md2($m)
    {
        static $s = array(
             41,  46,  67, 201, 162, 216, 124,   1,  61,  54,  84, 161, 236, 240, 6,
             19,  98, 167,   5, 243, 192, 199, 115, 140, 152, 147,  43, 217, 188,
             76, 130, 202,  30, 155,  87,  60, 253, 212, 224,  22, 103,  66, 111, 24,
            138,  23, 229,  18, 190,  78, 196, 214, 218, 158, 222,  73, 160, 251,
            245, 142, 187,  47, 238, 122, 169, 104, 121, 145,  21, 178,   7,  63,
            148, 194,  16, 137,  11,  34,  95,  33, 128, 127,  93, 154,  90, 144, 50,
             39,  53,  62, 204, 231, 191, 247, 151,   3, 255,  25,  48, 179,  72, 165,
            181, 209, 215,  94, 146,  42, 172,  86, 170, 198,  79, 184,  56, 210,
            150, 164, 125, 182, 118, 252, 107, 226, 156, 116,   4, 241,  69, 157,
            112,  89, 100, 113, 135,  32, 134,  91, 207, 101, 230,  45, 168,   2, 27,
             96,  37, 173, 174, 176, 185, 246,  28,  70,  97, 105,  52,  64, 126, 15,
             85,  71, 163,  35, 221,  81, 175,  58, 195,  92, 249, 206, 186, 197,
            234,  38,  44,  83,  13, 110, 133,  40, 132,   9, 211, 223, 205, 244, 65,
            129,  77,  82, 106, 220,  55, 200, 108, 193, 171, 250,  36, 225, 123,
              8,  12, 189, 177,  74, 120, 136, 149, 139, 227,  99, 232, 109, 233,
            203, 213, 254,  59,   0,  29,  57, 242, 239, 183,  14, 102,  88, 208, 228,
            166, 119, 114, 248, 235, 117,  75,  10,  49,  68,  80, 180, 143, 237,
             31,  26, 219, 153, 141,  51, 159,  17, 131, 20
        );

        // Step 1. Append Padding Bytes
        $pad = 16 - (strlen($m) & 0xF);
        $m.= str_repeat(chr($pad), $pad);

        $length = strlen($m);

        // Step 2. Append Checksum
        $c = str_repeat(chr(0), 16);
        $l = chr(0);
        for ($i = 0; $i < $length; $i+= 16) {
            for ($j = 0; $j < 16; $j++) {
                // RFC1319 incorrectly states that C[j] should be set to S[c xor L]
                //$c[$j] = chr($s[ord($m[$i + $j] ^ $l)]);
                // per <http://www.rfc-editor.org/errata_search.php?rfc=1319>, however, C[j] should be set to S[c xor L] xor C[j]
                $c[$j] = chr($s[ord($m[$i + $j] ^ $l)] ^ ord($c[$j]));
                $l = $c[$j];
            }
        }
        $m.= $c;

        $length+= 16;

        // Step 3. Initialize MD Buffer
        $x = str_repeat(chr(0), 48);

        // Step 4. Process Message in 16-Byte Blocks
        for ($i = 0; $i < $length; $i+= 16) {
            for ($j = 0; $j < 16; $j++) {
                $x[$j + 16] = $m[$i + $j];
                $x[$j + 32] = $x[$j + 16] ^ $x[$j];
            }
            $t = chr(0);
            for ($j = 0; $j < 18; $j++) {
                for ($k = 0; $k < 48; $k++) {
                    $x[$k] = $t = $x[$k] ^ chr($s[ord($t)]);
                    //$t = $x[$k] = $x[$k] ^ chr($s[ord($t)]);
                }
                $t = chr(ord($t) + $j);
            }
        }

        // Step 5. Output
        return substr($x, 0, 16);
    }

    /**
     * Pure-PHP implementation of SHA256
     *
     * See {@link http://en.wikipedia.org/wiki/SHA_hash_functions#SHA-256_.28a_SHA-2_variant.29_pseudocode SHA-256 (a SHA-2 variant) pseudocode - Wikipedia}.
     *
     * @access private
     * @param string $m
     */
    function _sha256($m)
    {
        if (extension_loaded('suhosin')) {
            return pack('H*', sha256($m));
        }

        // Initialize variables
        $hash = array(
            0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19
        );
        // Initialize table of round constants
        // (first 32 bits of the fractional parts of the cube roots of the first 64 primes 2..311)
        static $k = array(
            0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
            0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
            0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
            0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
            0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
            0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
            0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
            0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
        );

        // Pre-processing
        $length = strlen($m);
        // to round to nearest 56 mod 64, we'll add 64 - (length + (64 - 56)) % 64
        $m.= str_repeat(chr(0), 64 - (($length + 8) & 0x3F));
        $m[$length] = chr(0x80);
        // we don't support hashing strings 512MB long
        $m.= pack('N2', 0, $length << 3);

        // Process the message in successive 512-bit chunks
        $chunks = str_split($m, 64);
        foreach ($chunks as $chunk) {
            $w = array();
            for ($i = 0; $i < 16; $i++) {
                extract(unpack('Ntemp', $this->_string_shift($chunk, 4)));
                $w[] = $temp;
            }

            // Extend the sixteen 32-bit words into sixty-four 32-bit words
            for ($i = 16; $i < 64; $i++) {
                // @codingStandardsIgnoreStart
                $s0 = $this->_rightRotate($w[$i - 15],  7) ^
                      $this->_rightRotate($w[$i - 15], 18) ^
                      $this->_rightShift( $w[$i - 15],  3);
                $s1 = $this->_rightRotate($w[$i - 2], 17) ^
                      $this->_rightRotate($w[$i - 2], 19) ^
                      $this->_rightShift( $w[$i - 2], 10);
                // @codingStandardsIgnoreEnd
                $w[$i] = $this->_add($w[$i - 16], $s0, $w[$i - 7], $s1);
            }

            // Initialize hash value for this chunk
            list($a, $b, $c, $d, $e, $f, $g, $h) = $hash;

            // Main loop
            for ($i = 0; $i < 64; $i++) {
                $s0 = $this->_rightRotate($a,  2) ^
                      $this->_rightRotate($a, 13) ^
                      $this->_rightRotate($a, 22);
                $maj = ($a & $b) ^
                       ($a & $c) ^
                       ($b & $c);
                $t2 = $this->_add($s0, $maj);

                $s1 = $this->_rightRotate($e,  6) ^
                      $this->_rightRotate($e, 11) ^
                      $this->_rightRotate($e, 25);
                $ch = ($e & $f) ^
                      ($this->_not($e) & $g);
                $t1 = $this->_add($h, $s1, $ch, $k[$i], $w[$i]);

                $h = $g;
                $g = $f;
                $f = $e;
                $e = $this->_add($d, $t1);
                $d = $c;
                $c = $b;
                $b = $a;
                $a = $this->_add($t1, $t2);
            }

            // Add this chunk's hash to result so far
            $hash = array(
                $this->_add($hash[0], $a),
                $this->_add($hash[1], $b),
                $this->_add($hash[2], $c),
                $this->_add($hash[3], $d),
                $this->_add($hash[4], $e),
                $this->_add($hash[5], $f),
                $this->_add($hash[6], $g),
                $this->_add($hash[7], $h)
            );
        }

        // Produce the final hash value (big-endian)
        return pack('N8', $hash[0], $hash[1], $hash[2], $hash[3], $hash[4], $hash[5], $hash[6], $hash[7]);
    }

    /**
     * Pure-PHP implementation of SHA384 and SHA512
     *
     * @access private
     * @param string $m
     */
    function _sha512($m)
    {
        if (!class_exists('Math_BigInteger')) {
            include_once 'Math/BigInteger.php';
        }

        static $init384, $init512, $k;

        if (!isset($k)) {
            // Initialize variables
            $init384 = array( // initial values for SHA384
                'cbbb9d5dc1059ed8', '629a292a367cd507', '9159015a3070dd17', '152fecd8f70e5939',
                '67332667ffc00b31', '8eb44a8768581511', 'db0c2e0d64f98fa7', '47b5481dbefa4fa4'
            );
            $init512 = array( // initial values for SHA512
                '6a09e667f3bcc908', 'bb67ae8584caa73b', '3c6ef372fe94f82b', 'a54ff53a5f1d36f1',
                '510e527fade682d1', '9b05688c2b3e6c1f', '1f83d9abfb41bd6b', '5be0cd19137e2179'
            );

            for ($i = 0; $i < 8; $i++) {
                $init384[$i] = new Math_BigInteger($init384[$i], 16);
                $init384[$i]->setPrecision(64);
                $init512[$i] = new Math_BigInteger($init512[$i], 16);
                $init512[$i]->setPrecision(64);
            }

            // Initialize table of round constants
            // (first 64 bits of the fractional parts of the cube roots of the first 80 primes 2..409)
            $k = array(
                '428a2f98d728ae22', '7137449123ef65cd', 'b5c0fbcfec4d3b2f', 'e9b5dba58189dbbc',
                '3956c25bf348b538', '59f111f1b605d019', '923f82a4af194f9b', 'ab1c5ed5da6d8118',
                'd807aa98a3030242', '12835b0145706fbe', '243185be4ee4b28c', '550c7dc3d5ffb4e2',
                '72be5d74f27b896f', '80deb1fe3b1696b1', '9bdc06a725c71235', 'c19bf174cf692694',
                'e49b69c19ef14ad2', 'efbe4786384f25e3', '0fc19dc68b8cd5b5', '240ca1cc77ac9c65',
                '2de92c6f592b0275', '4a7484aa6ea6e483', '5cb0a9dcbd41fbd4', '76f988da831153b5',
                '983e5152ee66dfab', 'a831c66d2db43210', 'b00327c898fb213f', 'bf597fc7beef0ee4',
                'c6e00bf33da88fc2', 'd5a79147930aa725', '06ca6351e003826f', '142929670a0e6e70',
                '27b70a8546d22ffc', '2e1b21385c26c926', '4d2c6dfc5ac42aed', '53380d139d95b3df',
                '650a73548baf63de', '766a0abb3c77b2a8', '81c2c92e47edaee6', '92722c851482353b',
                'a2bfe8a14cf10364', 'a81a664bbc423001', 'c24b8b70d0f89791', 'c76c51a30654be30',
                'd192e819d6ef5218', 'd69906245565a910', 'f40e35855771202a', '106aa07032bbd1b8',
                '19a4c116b8d2d0c8', '1e376c085141ab53', '2748774cdf8eeb99', '34b0bcb5e19b48a8',
                '391c0cb3c5c95a63', '4ed8aa4ae3418acb', '5b9cca4f7763e373', '682e6ff3d6b2b8a3',
                '748f82ee5defb2fc', '78a5636f43172f60', '84c87814a1f0ab72', '8cc702081a6439ec',
                '90befffa23631e28', 'a4506cebde82bde9', 'bef9a3f7b2c67915', 'c67178f2e372532b',
                'ca273eceea26619c', 'd186b8c721c0c207', 'eada7dd6cde0eb1e', 'f57d4f7fee6ed178',
                '06f067aa72176fba', '0a637dc5a2c898a6', '113f9804bef90dae', '1b710b35131c471b',
                '28db77f523047d84', '32caab7b40c72493', '3c9ebe0a15c9bebc', '431d67c49c100d4c',
                '4cc5d4becb3e42b6', '597f299cfc657e2a', '5fcb6fab3ad6faec', '6c44198c4a475817'
            );

            for ($i = 0; $i < 80; $i++) {
                $k[$i] = new Math_BigInteger($k[$i], 16);
            }
        }

        $hash = $this->l == 48 ? $init384 : $init512;

        // Pre-processing
        $length = strlen($m);
        // to round to nearest 112 mod 128, we'll add 128 - (length + (128 - 112)) % 128
        $m.= str_repeat(chr(0), 128 - (($length + 16) & 0x7F));
        $m[$length] = chr(0x80);
        // we don't support hashing strings 512MB long
        $m.= pack('N4', 0, 0, 0, $length << 3);

        // Process the message in successive 1024-bit chunks
        $chunks = str_split($m, 128);
        foreach ($chunks as $chunk) {
            $w = array();
            for ($i = 0; $i < 16; $i++) {
                $temp = new Math_BigInteger($this->_string_shift($chunk, 8), 256);
                $temp->setPrecision(64);
                $w[] = $temp;
            }

            // Extend the sixteen 32-bit words into eighty 32-bit words
            for ($i = 16; $i < 80; $i++) {
                $temp = array(
                          $w[$i - 15]->bitwise_rightRotate(1),
                          $w[$i - 15]->bitwise_rightRotate(8),
                          $w[$i - 15]->bitwise_rightShift(7)
                );
                $s0 = $temp[0]->bitwise_xor($temp[1]);
                $s0 = $s0->bitwise_xor($temp[2]);
                $temp = array(
                          $w[$i - 2]->bitwise_rightRotate(19),
                          $w[$i - 2]->bitwise_rightRotate(61),
                          $w[$i - 2]->bitwise_rightShift(6)
                );
                $s1 = $temp[0]->bitwise_xor($temp[1]);
                $s1 = $s1->bitwise_xor($temp[2]);
                $w[$i] = $w[$i - 16]->copy();
                $w[$i] = $w[$i]->add($s0);
                $w[$i] = $w[$i]->add($w[$i - 7]);
                $w[$i] = $w[$i]->add($s1);
            }

            // Initialize hash value for this chunk
            $a = $hash[0]->copy();
            $b = $hash[1]->copy();
            $c = $hash[2]->copy();
            $d = $hash[3]->copy();
            $e = $hash[4]->copy();
            $f = $hash[5]->copy();
            $g = $hash[6]->copy();
            $h = $hash[7]->copy();

            // Main loop
            for ($i = 0; $i < 80; $i++) {
                $temp = array(
                    $a->bitwise_rightRotate(28),
                    $a->bitwise_rightRotate(34),
                    $a->bitwise_rightRotate(39)
                );
                $s0 = $temp[0]->bitwise_xor($temp[1]);
                $s0 = $s0->bitwise_xor($temp[2]);
                $temp = array(
                    $a->bitwise_and($b),
                    $a->bitwise_and($c),
                    $b->bitwise_and($c)
                );
                $maj = $temp[0]->bitwise_xor($temp[1]);
                $maj = $maj->bitwise_xor($temp[2]);
                $t2 = $s0->add($maj);

                $temp = array(
                    $e->bitwise_rightRotate(14),
                    $e->bitwise_rightRotate(18),
                    $e->bitwise_rightRotate(41)
                );
                $s1 = $temp[0]->bitwise_xor($temp[1]);
                $s1 = $s1->bitwise_xor($temp[2]);
                $temp = array(
                    $e->bitwise_and($f),
                    $g->bitwise_and($e->bitwise_not())
                );
                $ch = $temp[0]->bitwise_xor($temp[1]);
                $t1 = $h->add($s1);
                $t1 = $t1->add($ch);
                $t1 = $t1->add($k[$i]);
                $t1 = $t1->add($w[$i]);

                $h = $g->copy();
                $g = $f->copy();
                $f = $e->copy();
                $e = $d->add($t1);
                $d = $c->copy();
                $c = $b->copy();
                $b = $a->copy();
                $a = $t1->add($t2);
            }

            // Add this chunk's hash to result so far
            $hash = array(
                $hash[0]->add($a),
                $hash[1]->add($b),
                $hash[2]->add($c),
                $hash[3]->add($d),
                $hash[4]->add($e),
                $hash[5]->add($f),
                $hash[6]->add($g),
                $hash[7]->add($h)
            );
        }

        // Produce the final hash value (big-endian)
        // (Crypt_Hash::hash() trims the output for hashes but not for HMACs.  as such, we trim the output here)
        $temp = $hash[0]->toBytes() . $hash[1]->toBytes() . $hash[2]->toBytes() . $hash[3]->toBytes() .
                $hash[4]->toBytes() . $hash[5]->toBytes();
        if ($this->l != 48) {
            $temp.= $hash[6]->toBytes() . $hash[7]->toBytes();
        }

        return $temp;
    }

    /**
     * Right Rotate
     *
     * @access private
     * @param int $int
     * @param int $amt
     * @see self::_sha256()
     * @return int
     */
    function _rightRotate($int, $amt)
    {
        $invamt = 32 - $amt;
        $mask = (1 << $invamt) - 1;
        return (($int << $invamt) & 0xFFFFFFFF) | (($int >> $amt) & $mask);
    }

    /**
     * Right Shift
     *
     * @access private
     * @param int $int
     * @param int $amt
     * @see self::_sha256()
     * @return int
     */
    function _rightShift($int, $amt)
    {
        $mask = (1 << (32 - $amt)) - 1;
        return ($int >> $amt) & $mask;
    }

    /**
     * Not
     *
     * @access private
     * @param int $int
     * @see self::_sha256()
     * @return int
     */
    function _not($int)
    {
        return ~$int & 0xFFFFFFFF;
    }

    /**
     * Add
     *
     * _sha256() adds multiple unsigned 32-bit integers.  Since PHP doesn't support unsigned integers and since the
     * possibility of overflow exists, care has to be taken.  Math_BigInteger() could be used but this should be faster.
     *
     * @param int $...
     * @return int
     * @see self::_sha256()
     * @access private
     */
    function _add()
    {
        static $mod;
        if (!isset($mod)) {
            $mod = pow(2, 32);
        }

        $result = 0;
        $arguments = func_get_args();
        foreach ($arguments as $argument) {
            $result+= $argument < 0 ? ($argument & 0x7FFFFFFF) + 0x80000000 : $argument;
        }

        switch (true) {
            case is_int($result):
            // PHP 5.3, per http://php.net/releases/5_3_0.php, introduced "more consistent float rounding"
            case version_compare(PHP_VERSION, '5.3.0') >= 0 && (php_uname('m') & "\xDF\xDF\xDF") != 'ARM':
            // PHP_OS & "\xDF\xDF\xDF" == strtoupper(substr(PHP_OS, 0, 3)), but a lot faster
            case (PHP_OS & "\xDF\xDF\xDF") === 'WIN':
                return fmod($result, $mod);
        }

        return (fmod($result, 0x80000000) & 0x7FFFFFFF) |
            ((fmod(floor($result / 0x80000000), 2) & 1) << 31);
    }

    /**
     * String Shift
     *
     * Inspired by array_shift
     *
     * @param string $string
     * @param int $index
     * @return string
     * @access private
     */
    function _string_shift(&$string, $index = 1)
    {
        $substr = substr($string, 0, $index);
        $string = substr($string, $index);
        return $substr;
    }
}
vendor/phpseclib/phpseclib/phpseclib/Crypt/Blowfish.php000064400000070733151327705700017344 0ustar00<?php

/**
 * Pure-PHP implementation of Blowfish.
 *
 * Uses mcrypt, if available, and an internal implementation, otherwise.
 *
 * PHP versions 4 and 5
 *
 * Useful resources are as follows:
 *
 *  - {@link http://en.wikipedia.org/wiki/Blowfish_(cipher) Wikipedia description of Blowfish}
 *
 * Here's a short example of how to use this library:
 * <code>
 * <?php
 *    include 'Crypt/Blowfish.php';
 *
 *    $blowfish = new Crypt_Blowfish();
 *
 *    $blowfish->setKey('12345678901234567890123456789012');
 *
 *    $plaintext = str_repeat('a', 1024);
 *
 *    echo $blowfish->decrypt($blowfish->encrypt($plaintext));
 * ?>
 * </code>
 *
 * LICENSE: Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 *
 * @category  Crypt
 * @package   Crypt_Blowfish
 * @author    Jim Wigginton <terrafrost@php.net>
 * @author    Hans-Juergen Petrich <petrich@tronic-media.com>
 * @copyright 2007 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      http://phpseclib.sourceforge.net
 */

/**
 * Include Crypt_Base
 *
 * Base cipher class
 */
if (!class_exists('Crypt_Base')) {
    include_once 'Base.php';
}

/**#@+
 * @access public
 * @see self::encrypt()
 * @see self::decrypt()
 */
/**
 * Encrypt / decrypt using the Counter mode.
 *
 * Set to -1 since that's what Crypt/Random.php uses to index the CTR mode.
 *
 * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Counter_.28CTR.29
 */
define('CRYPT_BLOWFISH_MODE_CTR', CRYPT_MODE_CTR);
/**
 * Encrypt / decrypt using the Electronic Code Book mode.
 *
 * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Electronic_codebook_.28ECB.29
 */
define('CRYPT_BLOWFISH_MODE_ECB', CRYPT_MODE_ECB);
/**
 * Encrypt / decrypt using the Code Book Chaining mode.
 *
 * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Cipher-block_chaining_.28CBC.29
 */
define('CRYPT_BLOWFISH_MODE_CBC', CRYPT_MODE_CBC);
/**
 * Encrypt / decrypt using the Cipher Feedback mode.
 *
 * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Cipher_feedback_.28CFB.29
 */
define('CRYPT_BLOWFISH_MODE_CFB', CRYPT_MODE_CFB);
/**
 * Encrypt / decrypt using the Cipher Feedback mode.
 *
 * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Output_feedback_.28OFB.29
 */
define('CRYPT_BLOWFISH_MODE_OFB', CRYPT_MODE_OFB);
/**#@-*/

/**
 * Pure-PHP implementation of Blowfish.
 *
 * @package Crypt_Blowfish
 * @author  Jim Wigginton <terrafrost@php.net>
 * @author  Hans-Juergen Petrich <petrich@tronic-media.com>
 * @access  public
 */
class Crypt_Blowfish extends Crypt_Base
{
    /**
     * Block Length of the cipher
     *
     * @see Crypt_Base::block_size
     * @var int
     * @access private
     */
    var $block_size = 8;

    /**
     * The namespace used by the cipher for its constants.
     *
     * @see Crypt_Base::const_namespace
     * @var string
     * @access private
     */
    var $const_namespace = 'BLOWFISH';

    /**
     * The mcrypt specific name of the cipher
     *
     * @see Crypt_Base::cipher_name_mcrypt
     * @var string
     * @access private
     */
    var $cipher_name_mcrypt = 'blowfish';

    /**
     * Optimizing value while CFB-encrypting
     *
     * @see Crypt_Base::cfb_init_len
     * @var int
     * @access private
     */
    var $cfb_init_len = 500;

    /**
     * The fixed subkeys boxes ($sbox0 - $sbox3) with 256 entries each
     *
     * S-Box 0
     *
     * @access private
     * @var    array
     */
    var $sbox0 = array(
        0xd1310ba6, 0x98dfb5ac, 0x2ffd72db, 0xd01adfb7, 0xb8e1afed, 0x6a267e96, 0xba7c9045, 0xf12c7f99,
        0x24a19947, 0xb3916cf7, 0x0801f2e2, 0x858efc16, 0x636920d8, 0x71574e69, 0xa458fea3, 0xf4933d7e,
        0x0d95748f, 0x728eb658, 0x718bcd58, 0x82154aee, 0x7b54a41d, 0xc25a59b5, 0x9c30d539, 0x2af26013,
        0xc5d1b023, 0x286085f0, 0xca417918, 0xb8db38ef, 0x8e79dcb0, 0x603a180e, 0x6c9e0e8b, 0xb01e8a3e,
        0xd71577c1, 0xbd314b27, 0x78af2fda, 0x55605c60, 0xe65525f3, 0xaa55ab94, 0x57489862, 0x63e81440,
        0x55ca396a, 0x2aab10b6, 0xb4cc5c34, 0x1141e8ce, 0xa15486af, 0x7c72e993, 0xb3ee1411, 0x636fbc2a,
        0x2ba9c55d, 0x741831f6, 0xce5c3e16, 0x9b87931e, 0xafd6ba33, 0x6c24cf5c, 0x7a325381, 0x28958677,
        0x3b8f4898, 0x6b4bb9af, 0xc4bfe81b, 0x66282193, 0x61d809cc, 0xfb21a991, 0x487cac60, 0x5dec8032,
        0xef845d5d, 0xe98575b1, 0xdc262302, 0xeb651b88, 0x23893e81, 0xd396acc5, 0x0f6d6ff3, 0x83f44239,
        0x2e0b4482, 0xa4842004, 0x69c8f04a, 0x9e1f9b5e, 0x21c66842, 0xf6e96c9a, 0x670c9c61, 0xabd388f0,
        0x6a51a0d2, 0xd8542f68, 0x960fa728, 0xab5133a3, 0x6eef0b6c, 0x137a3be4, 0xba3bf050, 0x7efb2a98,
        0xa1f1651d, 0x39af0176, 0x66ca593e, 0x82430e88, 0x8cee8619, 0x456f9fb4, 0x7d84a5c3, 0x3b8b5ebe,
        0xe06f75d8, 0x85c12073, 0x401a449f, 0x56c16aa6, 0x4ed3aa62, 0x363f7706, 0x1bfedf72, 0x429b023d,
        0x37d0d724, 0xd00a1248, 0xdb0fead3, 0x49f1c09b, 0x075372c9, 0x80991b7b, 0x25d479d8, 0xf6e8def7,
        0xe3fe501a, 0xb6794c3b, 0x976ce0bd, 0x04c006ba, 0xc1a94fb6, 0x409f60c4, 0x5e5c9ec2, 0x196a2463,
        0x68fb6faf, 0x3e6c53b5, 0x1339b2eb, 0x3b52ec6f, 0x6dfc511f, 0x9b30952c, 0xcc814544, 0xaf5ebd09,
        0xbee3d004, 0xde334afd, 0x660f2807, 0x192e4bb3, 0xc0cba857, 0x45c8740f, 0xd20b5f39, 0xb9d3fbdb,
        0x5579c0bd, 0x1a60320a, 0xd6a100c6, 0x402c7279, 0x679f25fe, 0xfb1fa3cc, 0x8ea5e9f8, 0xdb3222f8,
        0x3c7516df, 0xfd616b15, 0x2f501ec8, 0xad0552ab, 0x323db5fa, 0xfd238760, 0x53317b48, 0x3e00df82,
        0x9e5c57bb, 0xca6f8ca0, 0x1a87562e, 0xdf1769db, 0xd542a8f6, 0x287effc3, 0xac6732c6, 0x8c4f5573,
        0x695b27b0, 0xbbca58c8, 0xe1ffa35d, 0xb8f011a0, 0x10fa3d98, 0xfd2183b8, 0x4afcb56c, 0x2dd1d35b,
        0x9a53e479, 0xb6f84565, 0xd28e49bc, 0x4bfb9790, 0xe1ddf2da, 0xa4cb7e33, 0x62fb1341, 0xcee4c6e8,
        0xef20cada, 0x36774c01, 0xd07e9efe, 0x2bf11fb4, 0x95dbda4d, 0xae909198, 0xeaad8e71, 0x6b93d5a0,
        0xd08ed1d0, 0xafc725e0, 0x8e3c5b2f, 0x8e7594b7, 0x8ff6e2fb, 0xf2122b64, 0x8888b812, 0x900df01c,
        0x4fad5ea0, 0x688fc31c, 0xd1cff191, 0xb3a8c1ad, 0x2f2f2218, 0xbe0e1777, 0xea752dfe, 0x8b021fa1,
        0xe5a0cc0f, 0xb56f74e8, 0x18acf3d6, 0xce89e299, 0xb4a84fe0, 0xfd13e0b7, 0x7cc43b81, 0xd2ada8d9,
        0x165fa266, 0x80957705, 0x93cc7314, 0x211a1477, 0xe6ad2065, 0x77b5fa86, 0xc75442f5, 0xfb9d35cf,
        0xebcdaf0c, 0x7b3e89a0, 0xd6411bd3, 0xae1e7e49, 0x00250e2d, 0x2071b35e, 0x226800bb, 0x57b8e0af,
        0x2464369b, 0xf009b91e, 0x5563911d, 0x59dfa6aa, 0x78c14389, 0xd95a537f, 0x207d5ba2, 0x02e5b9c5,
        0x83260376, 0x6295cfa9, 0x11c81968, 0x4e734a41, 0xb3472dca, 0x7b14a94a, 0x1b510052, 0x9a532915,
        0xd60f573f, 0xbc9bc6e4, 0x2b60a476, 0x81e67400, 0x08ba6fb5, 0x571be91f, 0xf296ec6b, 0x2a0dd915,
        0xb6636521, 0xe7b9f9b6, 0xff34052e, 0xc5855664, 0x53b02d5d, 0xa99f8fa1, 0x08ba4799, 0x6e85076a
    );

    /**
     * S-Box 1
     *
     * @access private
     * @var    array
     */
    var $sbox1 = array(
        0x4b7a70e9, 0xb5b32944, 0xdb75092e, 0xc4192623, 0xad6ea6b0, 0x49a7df7d, 0x9cee60b8, 0x8fedb266,
        0xecaa8c71, 0x699a17ff, 0x5664526c, 0xc2b19ee1, 0x193602a5, 0x75094c29, 0xa0591340, 0xe4183a3e,
        0x3f54989a, 0x5b429d65, 0x6b8fe4d6, 0x99f73fd6, 0xa1d29c07, 0xefe830f5, 0x4d2d38e6, 0xf0255dc1,
        0x4cdd2086, 0x8470eb26, 0x6382e9c6, 0x021ecc5e, 0x09686b3f, 0x3ebaefc9, 0x3c971814, 0x6b6a70a1,
        0x687f3584, 0x52a0e286, 0xb79c5305, 0xaa500737, 0x3e07841c, 0x7fdeae5c, 0x8e7d44ec, 0x5716f2b8,
        0xb03ada37, 0xf0500c0d, 0xf01c1f04, 0x0200b3ff, 0xae0cf51a, 0x3cb574b2, 0x25837a58, 0xdc0921bd,
        0xd19113f9, 0x7ca92ff6, 0x94324773, 0x22f54701, 0x3ae5e581, 0x37c2dadc, 0xc8b57634, 0x9af3dda7,
        0xa9446146, 0x0fd0030e, 0xecc8c73e, 0xa4751e41, 0xe238cd99, 0x3bea0e2f, 0x3280bba1, 0x183eb331,
        0x4e548b38, 0x4f6db908, 0x6f420d03, 0xf60a04bf, 0x2cb81290, 0x24977c79, 0x5679b072, 0xbcaf89af,
        0xde9a771f, 0xd9930810, 0xb38bae12, 0xdccf3f2e, 0x5512721f, 0x2e6b7124, 0x501adde6, 0x9f84cd87,
        0x7a584718, 0x7408da17, 0xbc9f9abc, 0xe94b7d8c, 0xec7aec3a, 0xdb851dfa, 0x63094366, 0xc464c3d2,
        0xef1c1847, 0x3215d908, 0xdd433b37, 0x24c2ba16, 0x12a14d43, 0x2a65c451, 0x50940002, 0x133ae4dd,
        0x71dff89e, 0x10314e55, 0x81ac77d6, 0x5f11199b, 0x043556f1, 0xd7a3c76b, 0x3c11183b, 0x5924a509,
        0xf28fe6ed, 0x97f1fbfa, 0x9ebabf2c, 0x1e153c6e, 0x86e34570, 0xeae96fb1, 0x860e5e0a, 0x5a3e2ab3,
        0x771fe71c, 0x4e3d06fa, 0x2965dcb9, 0x99e71d0f, 0x803e89d6, 0x5266c825, 0x2e4cc978, 0x9c10b36a,
        0xc6150eba, 0x94e2ea78, 0xa5fc3c53, 0x1e0a2df4, 0xf2f74ea7, 0x361d2b3d, 0x1939260f, 0x19c27960,
        0x5223a708, 0xf71312b6, 0xebadfe6e, 0xeac31f66, 0xe3bc4595, 0xa67bc883, 0xb17f37d1, 0x018cff28,
        0xc332ddef, 0xbe6c5aa5, 0x65582185, 0x68ab9802, 0xeecea50f, 0xdb2f953b, 0x2aef7dad, 0x5b6e2f84,
        0x1521b628, 0x29076170, 0xecdd4775, 0x619f1510, 0x13cca830, 0xeb61bd96, 0x0334fe1e, 0xaa0363cf,
        0xb5735c90, 0x4c70a239, 0xd59e9e0b, 0xcbaade14, 0xeecc86bc, 0x60622ca7, 0x9cab5cab, 0xb2f3846e,
        0x648b1eaf, 0x19bdf0ca, 0xa02369b9, 0x655abb50, 0x40685a32, 0x3c2ab4b3, 0x319ee9d5, 0xc021b8f7,
        0x9b540b19, 0x875fa099, 0x95f7997e, 0x623d7da8, 0xf837889a, 0x97e32d77, 0x11ed935f, 0x16681281,
        0x0e358829, 0xc7e61fd6, 0x96dedfa1, 0x7858ba99, 0x57f584a5, 0x1b227263, 0x9b83c3ff, 0x1ac24696,
        0xcdb30aeb, 0x532e3054, 0x8fd948e4, 0x6dbc3128, 0x58ebf2ef, 0x34c6ffea, 0xfe28ed61, 0xee7c3c73,
        0x5d4a14d9, 0xe864b7e3, 0x42105d14, 0x203e13e0, 0x45eee2b6, 0xa3aaabea, 0xdb6c4f15, 0xfacb4fd0,
        0xc742f442, 0xef6abbb5, 0x654f3b1d, 0x41cd2105, 0xd81e799e, 0x86854dc7, 0xe44b476a, 0x3d816250,
        0xcf62a1f2, 0x5b8d2646, 0xfc8883a0, 0xc1c7b6a3, 0x7f1524c3, 0x69cb7492, 0x47848a0b, 0x5692b285,
        0x095bbf00, 0xad19489d, 0x1462b174, 0x23820e00, 0x58428d2a, 0x0c55f5ea, 0x1dadf43e, 0x233f7061,
        0x3372f092, 0x8d937e41, 0xd65fecf1, 0x6c223bdb, 0x7cde3759, 0xcbee7460, 0x4085f2a7, 0xce77326e,
        0xa6078084, 0x19f8509e, 0xe8efd855, 0x61d99735, 0xa969a7aa, 0xc50c06c2, 0x5a04abfc, 0x800bcadc,
        0x9e447a2e, 0xc3453484, 0xfdd56705, 0x0e1e9ec9, 0xdb73dbd3, 0x105588cd, 0x675fda79, 0xe3674340,
        0xc5c43465, 0x713e38d8, 0x3d28f89e, 0xf16dff20, 0x153e21e7, 0x8fb03d4a, 0xe6e39f2b, 0xdb83adf7
    );

    /**
     * S-Box 2
     *
     * @access private
     * @var    array
     */
    var $sbox2 = array(
        0xe93d5a68, 0x948140f7, 0xf64c261c, 0x94692934, 0x411520f7, 0x7602d4f7, 0xbcf46b2e, 0xd4a20068,
        0xd4082471, 0x3320f46a, 0x43b7d4b7, 0x500061af, 0x1e39f62e, 0x97244546, 0x14214f74, 0xbf8b8840,
        0x4d95fc1d, 0x96b591af, 0x70f4ddd3, 0x66a02f45, 0xbfbc09ec, 0x03bd9785, 0x7fac6dd0, 0x31cb8504,
        0x96eb27b3, 0x55fd3941, 0xda2547e6, 0xabca0a9a, 0x28507825, 0x530429f4, 0x0a2c86da, 0xe9b66dfb,
        0x68dc1462, 0xd7486900, 0x680ec0a4, 0x27a18dee, 0x4f3ffea2, 0xe887ad8c, 0xb58ce006, 0x7af4d6b6,
        0xaace1e7c, 0xd3375fec, 0xce78a399, 0x406b2a42, 0x20fe9e35, 0xd9f385b9, 0xee39d7ab, 0x3b124e8b,
        0x1dc9faf7, 0x4b6d1856, 0x26a36631, 0xeae397b2, 0x3a6efa74, 0xdd5b4332, 0x6841e7f7, 0xca7820fb,
        0xfb0af54e, 0xd8feb397, 0x454056ac, 0xba489527, 0x55533a3a, 0x20838d87, 0xfe6ba9b7, 0xd096954b,
        0x55a867bc, 0xa1159a58, 0xcca92963, 0x99e1db33, 0xa62a4a56, 0x3f3125f9, 0x5ef47e1c, 0x9029317c,
        0xfdf8e802, 0x04272f70, 0x80bb155c, 0x05282ce3, 0x95c11548, 0xe4c66d22, 0x48c1133f, 0xc70f86dc,
        0x07f9c9ee, 0x41041f0f, 0x404779a4, 0x5d886e17, 0x325f51eb, 0xd59bc0d1, 0xf2bcc18f, 0x41113564,
        0x257b7834, 0x602a9c60, 0xdff8e8a3, 0x1f636c1b, 0x0e12b4c2, 0x02e1329e, 0xaf664fd1, 0xcad18115,
        0x6b2395e0, 0x333e92e1, 0x3b240b62, 0xeebeb922, 0x85b2a20e, 0xe6ba0d99, 0xde720c8c, 0x2da2f728,
        0xd0127845, 0x95b794fd, 0x647d0862, 0xe7ccf5f0, 0x5449a36f, 0x877d48fa, 0xc39dfd27, 0xf33e8d1e,
        0x0a476341, 0x992eff74, 0x3a6f6eab, 0xf4f8fd37, 0xa812dc60, 0xa1ebddf8, 0x991be14c, 0xdb6e6b0d,
        0xc67b5510, 0x6d672c37, 0x2765d43b, 0xdcd0e804, 0xf1290dc7, 0xcc00ffa3, 0xb5390f92, 0x690fed0b,
        0x667b9ffb, 0xcedb7d9c, 0xa091cf0b, 0xd9155ea3, 0xbb132f88, 0x515bad24, 0x7b9479bf, 0x763bd6eb,
        0x37392eb3, 0xcc115979, 0x8026e297, 0xf42e312d, 0x6842ada7, 0xc66a2b3b, 0x12754ccc, 0x782ef11c,
        0x6a124237, 0xb79251e7, 0x06a1bbe6, 0x4bfb6350, 0x1a6b1018, 0x11caedfa, 0x3d25bdd8, 0xe2e1c3c9,
        0x44421659, 0x0a121386, 0xd90cec6e, 0xd5abea2a, 0x64af674e, 0xda86a85f, 0xbebfe988, 0x64e4c3fe,
        0x9dbc8057, 0xf0f7c086, 0x60787bf8, 0x6003604d, 0xd1fd8346, 0xf6381fb0, 0x7745ae04, 0xd736fccc,
        0x83426b33, 0xf01eab71, 0xb0804187, 0x3c005e5f, 0x77a057be, 0xbde8ae24, 0x55464299, 0xbf582e61,
        0x4e58f48f, 0xf2ddfda2, 0xf474ef38, 0x8789bdc2, 0x5366f9c3, 0xc8b38e74, 0xb475f255, 0x46fcd9b9,
        0x7aeb2661, 0x8b1ddf84, 0x846a0e79, 0x915f95e2, 0x466e598e, 0x20b45770, 0x8cd55591, 0xc902de4c,
        0xb90bace1, 0xbb8205d0, 0x11a86248, 0x7574a99e, 0xb77f19b6, 0xe0a9dc09, 0x662d09a1, 0xc4324633,
        0xe85a1f02, 0x09f0be8c, 0x4a99a025, 0x1d6efe10, 0x1ab93d1d, 0x0ba5a4df, 0xa186f20f, 0x2868f169,
        0xdcb7da83, 0x573906fe, 0xa1e2ce9b, 0x4fcd7f52, 0x50115e01, 0xa70683fa, 0xa002b5c4, 0x0de6d027,
        0x9af88c27, 0x773f8641, 0xc3604c06, 0x61a806b5, 0xf0177a28, 0xc0f586e0, 0x006058aa, 0x30dc7d62,
        0x11e69ed7, 0x2338ea63, 0x53c2dd94, 0xc2c21634, 0xbbcbee56, 0x90bcb6de, 0xebfc7da1, 0xce591d76,
        0x6f05e409, 0x4b7c0188, 0x39720a3d, 0x7c927c24, 0x86e3725f, 0x724d9db9, 0x1ac15bb4, 0xd39eb8fc,
        0xed545578, 0x08fca5b5, 0xd83d7cd3, 0x4dad0fc4, 0x1e50ef5e, 0xb161e6f8, 0xa28514d9, 0x6c51133c,
        0x6fd5c7e7, 0x56e14ec4, 0x362abfce, 0xddc6c837, 0xd79a3234, 0x92638212, 0x670efa8e, 0x406000e0
    );

    /**
     * S-Box 3
     *
     * @access private
     * @var    array
     */
    var $sbox3 = array(
        0x3a39ce37, 0xd3faf5cf, 0xabc27737, 0x5ac52d1b, 0x5cb0679e, 0x4fa33742, 0xd3822740, 0x99bc9bbe,
        0xd5118e9d, 0xbf0f7315, 0xd62d1c7e, 0xc700c47b, 0xb78c1b6b, 0x21a19045, 0xb26eb1be, 0x6a366eb4,
        0x5748ab2f, 0xbc946e79, 0xc6a376d2, 0x6549c2c8, 0x530ff8ee, 0x468dde7d, 0xd5730a1d, 0x4cd04dc6,
        0x2939bbdb, 0xa9ba4650, 0xac9526e8, 0xbe5ee304, 0xa1fad5f0, 0x6a2d519a, 0x63ef8ce2, 0x9a86ee22,
        0xc089c2b8, 0x43242ef6, 0xa51e03aa, 0x9cf2d0a4, 0x83c061ba, 0x9be96a4d, 0x8fe51550, 0xba645bd6,
        0x2826a2f9, 0xa73a3ae1, 0x4ba99586, 0xef5562e9, 0xc72fefd3, 0xf752f7da, 0x3f046f69, 0x77fa0a59,
        0x80e4a915, 0x87b08601, 0x9b09e6ad, 0x3b3ee593, 0xe990fd5a, 0x9e34d797, 0x2cf0b7d9, 0x022b8b51,
        0x96d5ac3a, 0x017da67d, 0xd1cf3ed6, 0x7c7d2d28, 0x1f9f25cf, 0xadf2b89b, 0x5ad6b472, 0x5a88f54c,
        0xe029ac71, 0xe019a5e6, 0x47b0acfd, 0xed93fa9b, 0xe8d3c48d, 0x283b57cc, 0xf8d56629, 0x79132e28,
        0x785f0191, 0xed756055, 0xf7960e44, 0xe3d35e8c, 0x15056dd4, 0x88f46dba, 0x03a16125, 0x0564f0bd,
        0xc3eb9e15, 0x3c9057a2, 0x97271aec, 0xa93a072a, 0x1b3f6d9b, 0x1e6321f5, 0xf59c66fb, 0x26dcf319,
        0x7533d928, 0xb155fdf5, 0x03563482, 0x8aba3cbb, 0x28517711, 0xc20ad9f8, 0xabcc5167, 0xccad925f,
        0x4de81751, 0x3830dc8e, 0x379d5862, 0x9320f991, 0xea7a90c2, 0xfb3e7bce, 0x5121ce64, 0x774fbe32,
        0xa8b6e37e, 0xc3293d46, 0x48de5369, 0x6413e680, 0xa2ae0810, 0xdd6db224, 0x69852dfd, 0x09072166,
        0xb39a460a, 0x6445c0dd, 0x586cdecf, 0x1c20c8ae, 0x5bbef7dd, 0x1b588d40, 0xccd2017f, 0x6bb4e3bb,
        0xdda26a7e, 0x3a59ff45, 0x3e350a44, 0xbcb4cdd5, 0x72eacea8, 0xfa6484bb, 0x8d6612ae, 0xbf3c6f47,
        0xd29be463, 0x542f5d9e, 0xaec2771b, 0xf64e6370, 0x740e0d8d, 0xe75b1357, 0xf8721671, 0xaf537d5d,
        0x4040cb08, 0x4eb4e2cc, 0x34d2466a, 0x0115af84, 0xe1b00428, 0x95983a1d, 0x06b89fb4, 0xce6ea048,
        0x6f3f3b82, 0x3520ab82, 0x011a1d4b, 0x277227f8, 0x611560b1, 0xe7933fdc, 0xbb3a792b, 0x344525bd,
        0xa08839e1, 0x51ce794b, 0x2f32c9b7, 0xa01fbac9, 0xe01cc87e, 0xbcc7d1f6, 0xcf0111c3, 0xa1e8aac7,
        0x1a908749, 0xd44fbd9a, 0xd0dadecb, 0xd50ada38, 0x0339c32a, 0xc6913667, 0x8df9317c, 0xe0b12b4f,
        0xf79e59b7, 0x43f5bb3a, 0xf2d519ff, 0x27d9459c, 0xbf97222c, 0x15e6fc2a, 0x0f91fc71, 0x9b941525,
        0xfae59361, 0xceb69ceb, 0xc2a86459, 0x12baa8d1, 0xb6c1075e, 0xe3056a0c, 0x10d25065, 0xcb03a442,
        0xe0ec6e0e, 0x1698db3b, 0x4c98a0be, 0x3278e964, 0x9f1f9532, 0xe0d392df, 0xd3a0342b, 0x8971f21e,
        0x1b0a7441, 0x4ba3348c, 0xc5be7120, 0xc37632d8, 0xdf359f8d, 0x9b992f2e, 0xe60b6f47, 0x0fe3f11d,
        0xe54cda54, 0x1edad891, 0xce6279cf, 0xcd3e7e6f, 0x1618b166, 0xfd2c1d05, 0x848fd2c5, 0xf6fb2299,
        0xf523f357, 0xa6327623, 0x93a83531, 0x56cccd02, 0xacf08162, 0x5a75ebb5, 0x6e163697, 0x88d273cc,
        0xde966292, 0x81b949d0, 0x4c50901b, 0x71c65614, 0xe6c6c7bd, 0x327a140a, 0x45e1d006, 0xc3f27b9a,
        0xc9aa53fd, 0x62a80f00, 0xbb25bfe2, 0x35bdd2f6, 0x71126905, 0xb2040222, 0xb6cbcf7c, 0xcd769c2b,
        0x53113ec0, 0x1640e3d3, 0x38abbd60, 0x2547adf0, 0xba38209c, 0xf746ce76, 0x77afa1c5, 0x20756060,
        0x85cbfe4e, 0x8ae88dd8, 0x7aaaf9b0, 0x4cf9aa7e, 0x1948c25c, 0x02fb8a8c, 0x01c36ae4, 0xd6ebe1f9,
        0x90d4f869, 0xa65cdea0, 0x3f09252d, 0xc208e69f, 0xb74e6132, 0xce77e25b, 0x578fdfe3, 0x3ac372e6
    );

    /**
     * P-Array consists of 18 32-bit subkeys
     *
     * @var array
     * @access private
     */
    var $parray = array(
        0x243f6a88, 0x85a308d3, 0x13198a2e, 0x03707344, 0xa4093822, 0x299f31d0,
        0x082efa98, 0xec4e6c89, 0x452821e6, 0x38d01377, 0xbe5466cf, 0x34e90c6c,
        0xc0ac29b7, 0xc97c50dd, 0x3f84d5b5, 0xb5470917, 0x9216d5d9, 0x8979fb1b
    );

    /**
     * The BCTX-working Array
     *
     * Holds the expanded key [p] and the key-depended s-boxes [sb]
     *
     * @var array
     * @access private
     */
    var $bctx;

    /**
     * Holds the last used key
     *
     * @var array
     * @access private
     */
    var $kl;

    /**
     * The Key Length (in bytes)
     *
     * @see Crypt_Base::setKeyLength()
     * @var int
     * @access private
     * @internal The max value is 256 / 8 = 32, the min value is 128 / 8 = 16.  Exists in conjunction with $Nk
     *    because the encryption / decryption / key schedule creation requires this number and not $key_length.  We could
     *    derive this from $key_length or vice versa, but that'd mean we'd have to do multiple shift operations, so in lieu
     *    of that, we'll just precompute it once.
     */
    var $key_length = 16;

    /**
     * Sets the key length.
     *
     * Key lengths can be between 32 and 448 bits.
     *
     * @access public
     * @param int $length
     */
    function setKeyLength($length)
    {
        if ($length < 32) {
            $this->key_length = 4;
        } elseif ($length > 448) {
            $this->key_length = 56;
        } else {
            $this->key_length = $length >> 3;
        }

        parent::setKeyLength($length);
    }

    /**
     * Test for engine validity
     *
     * This is mainly just a wrapper to set things up for Crypt_Base::isValidEngine()
     *
     * @see Crypt_Base::isValidEngine()
     * @param int $engine
     * @access public
     * @return bool
     */
    function isValidEngine($engine)
    {
        if ($engine == CRYPT_ENGINE_OPENSSL) {
            if (version_compare(PHP_VERSION, '5.3.7') < 0 && $this->key_length != 16) {
                return false;
            }
            if ($this->key_length < 16) {
                return false;
            }
            $this->cipher_name_openssl_ecb = 'bf-ecb';
            $this->cipher_name_openssl = 'bf-' . $this->_openssl_translate_mode();
        }

        return parent::isValidEngine($engine);
    }

    /**
     * Setup the key (expansion)
     *
     * @see Crypt_Base::_setupKey()
     * @access private
     */
    function _setupKey()
    {
        if (isset($this->kl['key']) && $this->key === $this->kl['key']) {
            // already expanded
            return;
        }
        $this->kl = array('key' => $this->key);

        /* key-expanding p[] and S-Box building sb[] */
        $this->bctx = array(
            'p'  => array(),
            'sb' => array(
                $this->sbox0,
                $this->sbox1,
                $this->sbox2,
                $this->sbox3
            )
        );

        // unpack binary string in unsigned chars
        $key  = array_values(unpack('C*', $this->key));
        $keyl = count($key);
        for ($j = 0, $i = 0; $i < 18; ++$i) {
            // xor P1 with the first 32-bits of the key, xor P2 with the second 32-bits ...
            for ($data = 0, $k = 0; $k < 4; ++$k) {
                $data = ($data << 8) | $key[$j];
                if (++$j >= $keyl) {
                    $j = 0;
                }
            }
            $this->bctx['p'][] = $this->parray[$i] ^ $data;
        }

        // encrypt the zero-string, replace P1 and P2 with the encrypted data,
        // encrypt P3 and P4 with the new P1 and P2, do it with all P-array and subkeys
        $data = "\0\0\0\0\0\0\0\0";
        for ($i = 0; $i < 18; $i += 2) {
            list($l, $r) = array_values(unpack('N*', $data = $this->_encryptBlock($data)));
            $this->bctx['p'][$i    ] = $l;
            $this->bctx['p'][$i + 1] = $r;
        }
        for ($i = 0; $i < 4; ++$i) {
            for ($j = 0; $j < 256; $j += 2) {
                list($l, $r) = array_values(unpack('N*', $data = $this->_encryptBlock($data)));
                $this->bctx['sb'][$i][$j    ] = $l;
                $this->bctx['sb'][$i][$j + 1] = $r;
            }
        }
    }

    /**
     * Encrypts a block
     *
     * @access private
     * @param string $in
     * @return string
     */
    function _encryptBlock($in)
    {
        $p = $this->bctx["p"];
        // extract($this->bctx["sb"], EXTR_PREFIX_ALL, "sb"); // slower
        $sb_0 = $this->bctx["sb"][0];
        $sb_1 = $this->bctx["sb"][1];
        $sb_2 = $this->bctx["sb"][2];
        $sb_3 = $this->bctx["sb"][3];

        $in = unpack("N*", $in);
        $l = $in[1];
        $r = $in[2];

        for ($i = 0; $i < 16; $i+= 2) {
            $l^= $p[$i];
            $r^= $this->safe_intval(($this->safe_intval($sb_0[$l >> 24 & 0xff]  + $sb_1[$l >> 16 & 0xff]) ^
                  $sb_2[$l >>  8 & 0xff]) +
                  $sb_3[$l       & 0xff]);

            $r^= $p[$i + 1];
            $l^= $this->safe_intval(($this->safe_intval($sb_0[$r >> 24 & 0xff]  + $sb_1[$r >> 16 & 0xff]) ^
                  $sb_2[$r >>  8 & 0xff]) +
                  $sb_3[$r       & 0xff]);
        }
        return pack("N*", $r ^ $p[17], $l ^ $p[16]);
    }

    /**
     * Decrypts a block
     *
     * @access private
     * @param string $in
     * @return string
     */
    function _decryptBlock($in)
    {
        $p = $this->bctx["p"];
        $sb_0 = $this->bctx["sb"][0];
        $sb_1 = $this->bctx["sb"][1];
        $sb_2 = $this->bctx["sb"][2];
        $sb_3 = $this->bctx["sb"][3];

        $in = unpack("N*", $in);
        $l = $in[1];
        $r = $in[2];

        for ($i = 17; $i > 2; $i-= 2) {
            $l^= $p[$i];
            $r^= $this->safe_intval(($this->safe_intval($sb_0[$l >> 24 & 0xff] + $sb_1[$l >> 16 & 0xff]) ^
                  $sb_2[$l >>  8 & 0xff]) +
                  $sb_3[$l       & 0xff]);

            $r^= $p[$i - 1];
            $l^= $this->safe_intval(($this->safe_intval($sb_0[$r >> 24 & 0xff] + $sb_1[$r >> 16 & 0xff]) ^
                  $sb_2[$r >>  8 & 0xff]) +
                  $sb_3[$r       & 0xff]);
        }
        return pack("N*", $r ^ $p[0], $l ^ $p[1]);
    }

    /**
     * Setup the performance-optimized function for de/encrypt()
     *
     * @see Crypt_Base::_setupInlineCrypt()
     * @access private
     */
    function _setupInlineCrypt()
    {
        $lambda_functions =& Crypt_Blowfish::_getLambdaFunctions();

        // We create max. 10 hi-optimized code for memory reason. Means: For each $key one ultra fast inline-crypt function.
        // (Currently, for Crypt_Blowfish, one generated $lambda_function cost on php5.5@32bit ~100kb unfreeable mem and ~180kb on php5.5@64bit)
        // After that, we'll still create very fast optimized code but not the hi-ultimative code, for each $mode one.
        $gen_hi_opt_code = (bool)(count($lambda_functions) < 10);

        // Generation of a unique hash for our generated code
        $code_hash = "Crypt_Blowfish, {$this->mode}";
        if ($gen_hi_opt_code) {
            $code_hash = str_pad($code_hash, 32) . $this->_hashInlineCryptFunction($this->key);
        }

        $safeint = $this->safe_intval_inline();

        if (!isset($lambda_functions[$code_hash])) {
            switch (true) {
                case $gen_hi_opt_code:
                    $p = $this->bctx['p'];
                    $init_crypt = '
                        static $sb_0, $sb_1, $sb_2, $sb_3;
                        if (!$sb_0) {
                            $sb_0 = $self->bctx["sb"][0];
                            $sb_1 = $self->bctx["sb"][1];
                            $sb_2 = $self->bctx["sb"][2];
                            $sb_3 = $self->bctx["sb"][3];
                        }
                    ';
                    break;
                default:
                    $p   = array();
                    for ($i = 0; $i < 18; ++$i) {
                        $p[] = '$p_' . $i;
                    }
                    $init_crypt = '
                        list($sb_0, $sb_1, $sb_2, $sb_3) = $self->bctx["sb"];
                        list(' . implode(',', $p) . ') = $self->bctx["p"];

                    ';
            }

            // Generating encrypt code:
            $encrypt_block = '
                $in = unpack("N*", $in);
                $l = $in[1];
                $r = $in[2];
            ';
            for ($i = 0; $i < 16; $i+= 2) {
                $encrypt_block.= '
                    $l^= ' . $p[$i] . ';
                    $r^= ' . sprintf($safeint, '(' . sprintf($safeint, '$sb_0[$l >> 24 & 0xff] + $sb_1[$l >> 16 & 0xff]') . ' ^
                          $sb_2[$l >>  8 & 0xff]) +
                          $sb_3[$l       & 0xff]') . ';

                    $r^= ' . $p[$i + 1] . ';
                    $l^= ' . sprintf($safeint, '(' . sprintf($safeint, '$sb_0[$r >> 24 & 0xff] + $sb_1[$r >> 16 & 0xff]') . '  ^
                          $sb_2[$r >>  8 & 0xff]) +
                          $sb_3[$r       & 0xff]') . ';
                ';
            }
            $encrypt_block.= '
                $in = pack("N*",
                    $r ^ ' . $p[17] . ',
                    $l ^ ' . $p[16] . '
                );
            ';

            // Generating decrypt code:
            $decrypt_block = '
                $in = unpack("N*", $in);
                $l = $in[1];
                $r = $in[2];
            ';

            for ($i = 17; $i > 2; $i-= 2) {
                $decrypt_block.= '
                    $l^= ' . $p[$i] . ';
                    $r^= ' . sprintf($safeint, '(' . sprintf($safeint, '$sb_0[$l >> 24 & 0xff] + $sb_1[$l >> 16 & 0xff]') . ' ^
                          $sb_2[$l >>  8 & 0xff]) +
                          $sb_3[$l       & 0xff]') . ';

                    $r^= ' . $p[$i - 1] . ';
                    $l^= ' . sprintf($safeint, '(' . sprintf($safeint, '$sb_0[$r >> 24 & 0xff] + $sb_1[$r >> 16 & 0xff]') . ' ^
                          $sb_2[$r >>  8 & 0xff]) +
                          $sb_3[$r       & 0xff]') . ';
                ';
            }

            $decrypt_block.= '
                $in = pack("N*",
                    $r ^ ' . $p[0] . ',
                    $l ^ ' . $p[1] . '
                );
            ';

            $lambda_functions[$code_hash] = $this->_createInlineCryptFunction(
                array(
                   'init_crypt'    => $init_crypt,
                   'init_encrypt'  => '',
                   'init_decrypt'  => '',
                   'encrypt_block' => $encrypt_block,
                   'decrypt_block' => $decrypt_block
                )
            );
        }
        $this->inline_crypt = $lambda_functions[$code_hash];
    }
}
vendor/phpseclib/phpseclib/phpseclib/Crypt/Random.php000064400000035454151327705700017010 0ustar00<?php

/**
 * Random Number Generator
 *
 * The idea behind this function is that it can be easily replaced with your own crypt_random_string()
 * function. eg. maybe you have a better source of entropy for creating the initial states or whatever.
 *
 * PHP versions 4 and 5
 *
 * Here's a short example of how to use this library:
 * <code>
 * <?php
 *    include 'Crypt/Random.php';
 *
 *    echo bin2hex(crypt_random_string(8));
 * ?>
 * </code>
 *
 * LICENSE: Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 *
 * @category  Crypt
 * @package   Crypt_Random
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2007 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      http://phpseclib.sourceforge.net
 */

// laravel is a PHP framework that utilizes phpseclib. laravel workbenches may, independently,
// have phpseclib as a requirement as well. if you're developing such a program you may encounter
// a "Cannot redeclare crypt_random_string()" error.
if (!function_exists('crypt_random_string')) {
    /**
     * "Is Windows" test
     *
     * @access private
     */
    define('CRYPT_RANDOM_IS_WINDOWS', strtoupper(substr(PHP_OS, 0, 3)) === 'WIN');

    /**
     * Generate a random string.
     *
     * Although microoptimizations are generally discouraged as they impair readability this function is ripe with
     * microoptimizations because this function has the potential of being called a huge number of times.
     * eg. for RSA key generation.
     *
     * @param int $length
     * @return string
     * @access public
     */
    function crypt_random_string($length)
    {
        if (!$length) {
            return '';
        }

        if (CRYPT_RANDOM_IS_WINDOWS) {
            // method 1. prior to PHP 5.3, mcrypt_create_iv() would call rand() on windows
            if (extension_loaded('mcrypt') && version_compare(PHP_VERSION, '5.3.0', '>=')) {
                return @mcrypt_create_iv($length);
            }
            // method 2. openssl_random_pseudo_bytes was introduced in PHP 5.3.0 but prior to PHP 5.3.4 there was,
            // to quote <http://php.net/ChangeLog-5.php#5.3.4>, "possible blocking behavior". as of 5.3.4
            // openssl_random_pseudo_bytes and mcrypt_create_iv do the exact same thing on Windows. ie. they both
            // call php_win32_get_random_bytes():
            //
            // https://github.com/php/php-src/blob/7014a0eb6d1611151a286c0ff4f2238f92c120d6/ext/openssl/openssl.c#L5008
            // https://github.com/php/php-src/blob/7014a0eb6d1611151a286c0ff4f2238f92c120d6/ext/mcrypt/mcrypt.c#L1392
            //
            // php_win32_get_random_bytes() is defined thusly:
            //
            // https://github.com/php/php-src/blob/7014a0eb6d1611151a286c0ff4f2238f92c120d6/win32/winutil.c#L80
            //
            // we're calling it, all the same, in the off chance that the mcrypt extension is not available
            if (extension_loaded('openssl') && version_compare(PHP_VERSION, '5.3.4', '>=')) {
                return openssl_random_pseudo_bytes($length);
            }
        } else {
            // method 1. the fastest
            if (extension_loaded('openssl') && version_compare(PHP_VERSION, '5.3.0', '>=')) {
                return openssl_random_pseudo_bytes($length);
            }
            // method 2
            static $fp = true;
            if ($fp === true) {
                // warning's will be output unles the error suppression operator is used. errors such as
                // "open_basedir restriction in effect", "Permission denied", "No such file or directory", etc.
                $fp = @fopen('/dev/urandom', 'rb');
            }
            if ($fp !== true && $fp !== false) { // surprisingly faster than !is_bool() or is_resource()
                return fread($fp, $length);
            }
            // method 3. pretty much does the same thing as method 2 per the following url:
            // https://github.com/php/php-src/blob/7014a0eb6d1611151a286c0ff4f2238f92c120d6/ext/mcrypt/mcrypt.c#L1391
            // surprisingly slower than method 2. maybe that's because mcrypt_create_iv does a bunch of error checking that we're
            // not doing. regardless, this'll only be called if this PHP script couldn't open /dev/urandom due to open_basedir
            // restrictions or some such
            if (extension_loaded('mcrypt')) {
                return @mcrypt_create_iv($length, MCRYPT_DEV_URANDOM);
            }
        }
        // at this point we have no choice but to use a pure-PHP CSPRNG

        // cascade entropy across multiple PHP instances by fixing the session and collecting all
        // environmental variables, including the previous session data and the current session
        // data.
        //
        // mt_rand seeds itself by looking at the PID and the time, both of which are (relatively)
        // easy to guess at. linux uses mouse clicks, keyboard timings, etc, as entropy sources, but
        // PHP isn't low level to be able to use those as sources and on a web server there's not likely
        // going to be a ton of keyboard or mouse action. web servers do have one thing that we can use
        // however, a ton of people visiting the website. obviously you don't want to base your seeding
        // soley on parameters a potential attacker sends but (1) not everything in $_SERVER is controlled
        // by the user and (2) this isn't just looking at the data sent by the current user - it's based
        // on the data sent by all users. one user requests the page and a hash of their info is saved.
        // another user visits the page and the serialization of their data is utilized along with the
        // server envirnment stuff and a hash of the previous http request data (which itself utilizes
        // a hash of the session data before that). certainly an attacker should be assumed to have
        // full control over his own http requests. he, however, is not going to have control over
        // everyone's http requests.
        static $crypto = false, $v;
        if ($crypto === false) {
            // save old session data
            $old_session_id = session_id();
            $old_use_cookies = ini_get('session.use_cookies');
            $old_session_cache_limiter = session_cache_limiter();
            $_OLD_SESSION = isset($_SESSION) ? $_SESSION : false;
            if ($old_session_id != '') {
                session_write_close();
            }

            session_id(1);
            ini_set('session.use_cookies', 0);
            session_cache_limiter('');
            session_start();

            $v = $seed = $_SESSION['seed'] = pack('H*', sha1(
                (isset($_SERVER) ? phpseclib_safe_serialize($_SERVER) : '') .
                (isset($_POST) ? phpseclib_safe_serialize($_POST) : '') .
                (isset($_GET) ? phpseclib_safe_serialize($_GET) : '') .
                (isset($_COOKIE) ? phpseclib_safe_serialize($_COOKIE) : '') .
                phpseclib_safe_serialize($GLOBALS) .
                phpseclib_safe_serialize($_SESSION) .
                phpseclib_safe_serialize($_OLD_SESSION)
            ));
            if (!isset($_SESSION['count'])) {
                $_SESSION['count'] = 0;
            }
            $_SESSION['count']++;

            session_write_close();

            // restore old session data
            if ($old_session_id != '') {
                session_id($old_session_id);
                session_start();
                ini_set('session.use_cookies', $old_use_cookies);
                session_cache_limiter($old_session_cache_limiter);
            } else {
                if ($_OLD_SESSION !== false) {
                    $_SESSION = $_OLD_SESSION;
                    unset($_OLD_SESSION);
                } else {
                    unset($_SESSION);
                }
            }

            // in SSH2 a shared secret and an exchange hash are generated through the key exchange process.
            // the IV client to server is the hash of that "nonce" with the letter A and for the encryption key it's the letter C.
            // if the hash doesn't produce enough a key or an IV that's long enough concat successive hashes of the
            // original hash and the current hash. we'll be emulating that. for more info see the following URL:
            //
            // http://tools.ietf.org/html/rfc4253#section-7.2
            //
            // see the is_string($crypto) part for an example of how to expand the keys
            $key = pack('H*', sha1($seed . 'A'));
            $iv = pack('H*', sha1($seed . 'C'));

            // ciphers are used as per the nist.gov link below. also, see this link:
            //
            // http://en.wikipedia.org/wiki/Cryptographically_secure_pseudorandom_number_generator#Designs_based_on_cryptographic_primitives
            switch (true) {
                case phpseclib_resolve_include_path('Crypt/AES.php'):
                    if (!class_exists('Crypt_AES')) {
                        include_once 'AES.php';
                    }
                    $crypto = new Crypt_AES(CRYPT_AES_MODE_CTR);
                    break;
                case phpseclib_resolve_include_path('Crypt/Twofish.php'):
                    if (!class_exists('Crypt_Twofish')) {
                        include_once 'Twofish.php';
                    }
                    $crypto = new Crypt_Twofish(CRYPT_TWOFISH_MODE_CTR);
                    break;
                case phpseclib_resolve_include_path('Crypt/Blowfish.php'):
                    if (!class_exists('Crypt_Blowfish')) {
                        include_once 'Blowfish.php';
                    }
                    $crypto = new Crypt_Blowfish(CRYPT_BLOWFISH_MODE_CTR);
                    break;
                case phpseclib_resolve_include_path('Crypt/TripleDES.php'):
                    if (!class_exists('Crypt_TripleDES')) {
                        include_once 'TripleDES.php';
                    }
                    $crypto = new Crypt_TripleDES(CRYPT_DES_MODE_CTR);
                    break;
                case phpseclib_resolve_include_path('Crypt/DES.php'):
                    if (!class_exists('Crypt_DES')) {
                        include_once 'DES.php';
                    }
                    $crypto = new Crypt_DES(CRYPT_DES_MODE_CTR);
                    break;
                case phpseclib_resolve_include_path('Crypt/RC4.php'):
                    if (!class_exists('Crypt_RC4')) {
                        include_once 'RC4.php';
                    }
                    $crypto = new Crypt_RC4();
                    break;
                default:
                    user_error('crypt_random_string requires at least one symmetric cipher be loaded');
                    return false;
            }

            $crypto->setKey($key);
            $crypto->setIV($iv);
            $crypto->enableContinuousBuffer();
        }

        //return $crypto->encrypt(str_repeat("\0", $length));

        // the following is based off of ANSI X9.31:
        //
        // http://csrc.nist.gov/groups/STM/cavp/documents/rng/931rngext.pdf
        //
        // OpenSSL uses that same standard for it's random numbers:
        //
        // http://www.opensource.apple.com/source/OpenSSL/OpenSSL-38/openssl/fips-1.0/rand/fips_rand.c
        // (do a search for "ANS X9.31 A.2.4")
        $result = '';
        while (strlen($result) < $length) {
            $i = $crypto->encrypt(microtime()); // strlen(microtime()) == 21
            $r = $crypto->encrypt($i ^ $v); // strlen($v) == 20
            $v = $crypto->encrypt($r ^ $i); // strlen($r) == 20
            $result.= $r;
        }
        return substr($result, 0, $length);
    }
}

if (!function_exists('phpseclib_safe_serialize')) {
    /**
     * Safely serialize variables
     *
     * If a class has a private __sleep() method it'll give a fatal error on PHP 5.2 and earlier.
     * PHP 5.3 will emit a warning.
     *
     * @param mixed $arr
     * @access public
     */
    function phpseclib_safe_serialize(&$arr)
    {
        if (is_object($arr)) {
            return '';
        }
        if (!is_array($arr)) {
            return serialize($arr);
        }
        // prevent circular array recursion
        if (isset($arr['__phpseclib_marker'])) {
            return '';
        }
        $safearr = array();
        $arr['__phpseclib_marker'] = true;
        foreach (array_keys($arr) as $key) {
            // do not recurse on the '__phpseclib_marker' key itself, for smaller memory usage
            if ($key !== '__phpseclib_marker') {
                $safearr[$key] = phpseclib_safe_serialize($arr[$key]);
            }
        }
        unset($arr['__phpseclib_marker']);
        return serialize($safearr);
    }
}

if (!function_exists('phpseclib_resolve_include_path')) {
    /**
     * Resolve filename against the include path.
     *
     * Wrapper around stream_resolve_include_path() (which was introduced in
     * PHP 5.3.2) with fallback implementation for earlier PHP versions.
     *
     * @param string $filename
     * @return string|false
     * @access public
     */
    function phpseclib_resolve_include_path($filename)
    {
        if (function_exists('stream_resolve_include_path')) {
            return stream_resolve_include_path($filename);
        }

        // handle non-relative paths
        if (file_exists($filename)) {
            return realpath($filename);
        }

        $paths = PATH_SEPARATOR == ':' ?
            preg_split('#(?<!phar):#', get_include_path()) :
            explode(PATH_SEPARATOR, get_include_path());
        foreach ($paths as $prefix) {
            // path's specified in include_path don't always end in /
            $ds = substr($prefix, -1) == DIRECTORY_SEPARATOR ? '' : DIRECTORY_SEPARATOR;
            $file = $prefix . $ds . $filename;
            if (file_exists($file)) {
                return realpath($file);
            }
        }

        return false;
    }
}
vendor/phpseclib/phpseclib/phpseclib/Crypt/RC4.php000064400000023162151327705700016151 0ustar00<?php

/**
 * Pure-PHP implementation of RC4.
 *
 * Uses mcrypt, if available, and an internal implementation, otherwise.
 *
 * PHP versions 4 and 5
 *
 * Useful resources are as follows:
 *
 *  - {@link http://www.mozilla.org/projects/security/pki/nss/draft-kaukonen-cipher-arcfour-03.txt ARCFOUR Algorithm}
 *  - {@link http://en.wikipedia.org/wiki/RC4 - Wikipedia: RC4}
 *
 * RC4 is also known as ARCFOUR or ARC4.  The reason is elaborated upon at Wikipedia.  This class is named RC4 and not
 * ARCFOUR or ARC4 because RC4 is how it is referred to in the SSH1 specification.
 *
 * Here's a short example of how to use this library:
 * <code>
 * <?php
 *    include 'Crypt/RC4.php';
 *
 *    $rc4 = new Crypt_RC4();
 *
 *    $rc4->setKey('abcdefgh');
 *
 *    $size = 10 * 1024;
 *    $plaintext = '';
 *    for ($i = 0; $i < $size; $i++) {
 *        $plaintext.= 'a';
 *    }
 *
 *    echo $rc4->decrypt($rc4->encrypt($plaintext));
 * ?>
 * </code>
 *
 * LICENSE: Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 *
 * @category  Crypt
 * @package   Crypt_RC4
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2007 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      http://phpseclib.sourceforge.net
 */

/**
 * Include Crypt_Base
 *
 * Base cipher class
 */
if (!class_exists('Crypt_Base')) {
    include_once 'Base.php';
}

/**#@+
 * @access private
 * @see self::_crypt()
 */
define('CRYPT_RC4_ENCRYPT', 0);
define('CRYPT_RC4_DECRYPT', 1);
/**#@-*/

/**
 * Pure-PHP implementation of RC4.
 *
 * @package Crypt_RC4
 * @author  Jim Wigginton <terrafrost@php.net>
 * @access  public
 */
class Crypt_RC4 extends Crypt_Base
{
    /**
     * Block Length of the cipher
     *
     * RC4 is a stream cipher
     * so we the block_size to 0
     *
     * @see Crypt_Base::block_size
     * @var int
     * @access private
     */
    var $block_size = 0;

    /**
     * Key Length (in bytes)
     *
     * @see Crypt_RC4::setKeyLength()
     * @var int
     * @access private
     */
    var $key_length = 128; // = 1024 bits

    /**
     * The namespace used by the cipher for its constants.
     *
     * @see Crypt_Base::const_namespace
     * @var string
     * @access private
     */
    var $const_namespace = 'RC4';

    /**
     * The mcrypt specific name of the cipher
     *
     * @see Crypt_Base::cipher_name_mcrypt
     * @var string
     * @access private
     */
    var $cipher_name_mcrypt = 'arcfour';

    /**
     * Holds whether performance-optimized $inline_crypt() can/should be used.
     *
     * @see Crypt_Base::inline_crypt
     * @var mixed
     * @access private
     */
    var $use_inline_crypt = false; // currently not available

    /**
     * The Key
     *
     * @see self::setKey()
     * @var string
     * @access private
     */
    var $key;

    /**
     * The Key Stream for decryption and encryption
     *
     * @see self::setKey()
     * @var array
     * @access private
     */
    var $stream;

    /**
     * Default Constructor.
     *
     * Determines whether or not the mcrypt extension should be used.
     *
     * @see Crypt_Base::Crypt_Base()
     * @return Crypt_RC4
     * @access public
     */
    function __construct()
    {
        parent::__construct(CRYPT_MODE_STREAM);
    }

    /**
     * PHP4 compatible Default Constructor.
     *
     * @see self::__construct()
     * @access public
     */
    function Crypt_RC4()
    {
        $this->__construct();
    }

    /**
     * Test for engine validity
     *
     * This is mainly just a wrapper to set things up for Crypt_Base::isValidEngine()
     *
     * @see Crypt_Base::Crypt_Base()
     * @param int $engine
     * @access public
     * @return bool
     */
    function isValidEngine($engine)
    {
        if ($engine == CRYPT_ENGINE_OPENSSL) {
            if (version_compare(PHP_VERSION, '5.3.7') >= 0) {
                $this->cipher_name_openssl = 'rc4-40';
            } else {
                switch (strlen($this->key)) {
                    case 5:
                        $this->cipher_name_openssl = 'rc4-40';
                        break;
                    case 8:
                        $this->cipher_name_openssl = 'rc4-64';
                        break;
                    case 16:
                        $this->cipher_name_openssl = 'rc4';
                        break;
                    default:
                        return false;
                }
            }
        }

        return parent::isValidEngine($engine);
    }

    /**
     * Dummy function.
     *
     * Some protocols, such as WEP, prepend an "initialization vector" to the key, effectively creating a new key [1].
     * If you need to use an initialization vector in this manner, feel free to prepend it to the key, yourself, before
     * calling setKey().
     *
     * [1] WEP's initialization vectors (IV's) are used in a somewhat insecure way.  Since, in that protocol,
     * the IV's are relatively easy to predict, an attack described by
     * {@link http://www.drizzle.com/~aboba/IEEE/rc4_ksaproc.pdf Scott Fluhrer, Itsik Mantin, and Adi Shamir}
     * can be used to quickly guess at the rest of the key.  The following links elaborate:
     *
     * {@link http://www.rsa.com/rsalabs/node.asp?id=2009 http://www.rsa.com/rsalabs/node.asp?id=2009}
     * {@link http://en.wikipedia.org/wiki/Related_key_attack http://en.wikipedia.org/wiki/Related_key_attack}
     *
     * @param string $iv
     * @see self::setKey()
     * @access public
     */
    function setIV($iv)
    {
    }

    /**
     * Sets the key length
     *
     * Keys can be between 1 and 256 bytes long.
     *
     * @access public
     * @param int $length
     */
    function setKeyLength($length)
    {
        if ($length < 8) {
            $this->key_length = 1;
        } elseif ($length > 2048) {
            $this->key_length = 256;
        } else {
            $this->key_length = $length >> 3;
        }

        parent::setKeyLength($length);
    }

    /**
     * Encrypts a message.
     *
     * @see Crypt_Base::decrypt()
     * @see self::_crypt()
     * @access public
     * @param string $plaintext
     * @return string $ciphertext
     */
    function encrypt($plaintext)
    {
        if ($this->engine != CRYPT_ENGINE_INTERNAL) {
            return parent::encrypt($plaintext);
        }
        return $this->_crypt($plaintext, CRYPT_RC4_ENCRYPT);
    }

    /**
     * Decrypts a message.
     *
     * $this->decrypt($this->encrypt($plaintext)) == $this->encrypt($this->encrypt($plaintext)).
     * At least if the continuous buffer is disabled.
     *
     * @see Crypt_Base::encrypt()
     * @see self::_crypt()
     * @access public
     * @param string $ciphertext
     * @return string $plaintext
     */
    function decrypt($ciphertext)
    {
        if ($this->engine != CRYPT_ENGINE_INTERNAL) {
            return parent::decrypt($ciphertext);
        }
        return $this->_crypt($ciphertext, CRYPT_RC4_DECRYPT);
    }


    /**
     * Setup the key (expansion)
     *
     * @see Crypt_Base::_setupKey()
     * @access private
     */
    function _setupKey()
    {
        $key = $this->key;
        $keyLength = strlen($key);
        $keyStream = range(0, 255);
        $j = 0;
        for ($i = 0; $i < 256; $i++) {
            $j = ($j + $keyStream[$i] + ord($key[$i % $keyLength])) & 255;
            $temp = $keyStream[$i];
            $keyStream[$i] = $keyStream[$j];
            $keyStream[$j] = $temp;
        }

        $this->stream = array();
        $this->stream[CRYPT_RC4_DECRYPT] = $this->stream[CRYPT_RC4_ENCRYPT] = array(
            0, // index $i
            0, // index $j
            $keyStream
        );
    }

    /**
     * Encrypts or decrypts a message.
     *
     * @see self::encrypt()
     * @see self::decrypt()
     * @access private
     * @param string $text
     * @param int $mode
     * @return string $text
     */
    function _crypt($text, $mode)
    {
        if ($this->changed) {
            $this->_setup();
            $this->changed = false;
        }

        $stream = &$this->stream[$mode];
        if ($this->continuousBuffer) {
            $i = &$stream[0];
            $j = &$stream[1];
            $keyStream = &$stream[2];
        } else {
            $i = $stream[0];
            $j = $stream[1];
            $keyStream = $stream[2];
        }

        $len = strlen($text);
        for ($k = 0; $k < $len; ++$k) {
            $i = ($i + 1) & 255;
            $ksi = $keyStream[$i];
            $j = ($j + $ksi) & 255;
            $ksj = $keyStream[$j];

            $keyStream[$i] = $ksj;
            $keyStream[$j] = $ksi;
            $text[$k] = $text[$k] ^ chr($keyStream[($ksj + $ksi) & 255]);
        }

        return $text;
    }
}
vendor/phpseclib/phpseclib/phpseclib/Crypt/Base.php000064400000305574151327705700016445 0ustar00<?php

/**
 * Base Class for all Crypt_* cipher classes
 *
 * PHP versions 4 and 5
 *
 * Internally for phpseclib developers:
 *  If you plan to add a new cipher class, please note following rules:
 *
 *  - The new Crypt_* cipher class should extend Crypt_Base
 *
 *  - Following methods are then required to be overridden/overloaded:
 *
 *    - _encryptBlock()
 *
 *    - _decryptBlock()
 *
 *    - _setupKey()
 *
 *  - All other methods are optional to be overridden/overloaded
 *
 *  - Look at the source code of the current ciphers how they extend Crypt_Base
 *    and take one of them as a start up for the new cipher class.
 *
 *  - Please read all the other comments/notes/hints here also for each class var/method
 *
 * LICENSE: Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 *
 * @category  Crypt
 * @package   Crypt_Base
 * @author    Jim Wigginton <terrafrost@php.net>
 * @author    Hans-Juergen Petrich <petrich@tronic-media.com>
 * @copyright 2007 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      http://phpseclib.sourceforge.net
 */

/**#@+
 * @access public
 * @see self::encrypt()
 * @see self::decrypt()
 */
/**
 * Encrypt / decrypt using the Counter mode.
 *
 * Set to -1 since that's what Crypt/Random.php uses to index the CTR mode.
 *
 * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Counter_.28CTR.29
 */
define('CRYPT_MODE_CTR', -1);
/**
 * Encrypt / decrypt using the Electronic Code Book mode.
 *
 * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Electronic_codebook_.28ECB.29
 */
define('CRYPT_MODE_ECB', 1);
/**
 * Encrypt / decrypt using the Code Book Chaining mode.
 *
 * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Cipher-block_chaining_.28CBC.29
 */
define('CRYPT_MODE_CBC', 2);
/**
 * Encrypt / decrypt using the Cipher Feedback mode.
 *
 * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Cipher_feedback_.28CFB.29
 */
define('CRYPT_MODE_CFB', 3);
/**
 * Encrypt / decrypt using the Output Feedback mode.
 *
 * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Output_feedback_.28OFB.29
 */
define('CRYPT_MODE_OFB', 4);
/**
 * Encrypt / decrypt using streaming mode.
 */
define('CRYPT_MODE_STREAM', 5);
/**#@-*/

/**#@+
 * @access private
 * @see self::Crypt_Base()
 * @internal These constants are for internal use only
 */
/**
 * Base value for the internal implementation $engine switch
 */
define('CRYPT_ENGINE_INTERNAL', 1);
/**
 * Base value for the mcrypt implementation $engine switch
 */
define('CRYPT_ENGINE_MCRYPT', 2);
/**
 * Base value for the OpenSSL implementation $engine switch
 */
define('CRYPT_ENGINE_OPENSSL', 3);
/**#@-*/

/**
 * Base Class for all Crypt_* cipher classes
 *
 * @package Crypt_Base
 * @author  Jim Wigginton <terrafrost@php.net>
 * @author  Hans-Juergen Petrich <petrich@tronic-media.com>
 * @access  public
 */
class Crypt_Base
{
    /**
     * The Encryption Mode
     *
     * @see self::Crypt_Base()
     * @var int
     * @access private
     */
    var $mode;

    /**
     * The Block Length of the block cipher
     *
     * @var int
     * @access private
     */
    var $block_size = 16;

    /**
     * The Key
     *
     * @see self::setKey()
     * @var string
     * @access private
     */
    var $key = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";

    /**
     * The Initialization Vector
     *
     * @see self::setIV()
     * @var string
     * @access private
     */
    var $iv;

    /**
     * A "sliding" Initialization Vector
     *
     * @see self::enableContinuousBuffer()
     * @see self::_clearBuffers()
     * @var string
     * @access private
     */
    var $encryptIV;

    /**
     * A "sliding" Initialization Vector
     *
     * @see self::enableContinuousBuffer()
     * @see self::_clearBuffers()
     * @var string
     * @access private
     */
    var $decryptIV;

    /**
     * Continuous Buffer status
     *
     * @see self::enableContinuousBuffer()
     * @var bool
     * @access private
     */
    var $continuousBuffer = false;

    /**
     * Encryption buffer for CTR, OFB and CFB modes
     *
     * @see self::encrypt()
     * @see self::_clearBuffers()
     * @var array
     * @access private
     */
    var $enbuffer;

    /**
     * Decryption buffer for CTR, OFB and CFB modes
     *
     * @see self::decrypt()
     * @see self::_clearBuffers()
     * @var array
     * @access private
     */
    var $debuffer;

    /**
     * mcrypt resource for encryption
     *
     * The mcrypt resource can be recreated every time something needs to be created or it can be created just once.
     * Since mcrypt operates in continuous mode, by default, it'll need to be recreated when in non-continuous mode.
     *
     * @see self::encrypt()
     * @var resource
     * @access private
     */
    var $enmcrypt;

    /**
     * mcrypt resource for decryption
     *
     * The mcrypt resource can be recreated every time something needs to be created or it can be created just once.
     * Since mcrypt operates in continuous mode, by default, it'll need to be recreated when in non-continuous mode.
     *
     * @see self::decrypt()
     * @var resource
     * @access private
     */
    var $demcrypt;

    /**
     * Does the enmcrypt resource need to be (re)initialized?
     *
     * @see Crypt_Twofish::setKey()
     * @see Crypt_Twofish::setIV()
     * @var bool
     * @access private
     */
    var $enchanged = true;

    /**
     * Does the demcrypt resource need to be (re)initialized?
     *
     * @see Crypt_Twofish::setKey()
     * @see Crypt_Twofish::setIV()
     * @var bool
     * @access private
     */
    var $dechanged = true;

    /**
     * mcrypt resource for CFB mode
     *
     * mcrypt's CFB mode, in (and only in) buffered context,
     * is broken, so phpseclib implements the CFB mode by it self,
     * even when the mcrypt php extension is available.
     *
     * In order to do the CFB-mode work (fast) phpseclib
     * use a separate ECB-mode mcrypt resource.
     *
     * @link http://phpseclib.sourceforge.net/cfb-demo.phps
     * @see self::encrypt()
     * @see self::decrypt()
     * @see self::_setupMcrypt()
     * @var resource
     * @access private
     */
    var $ecb;

    /**
     * Optimizing value while CFB-encrypting
     *
     * Only relevant if $continuousBuffer enabled
     * and $engine == CRYPT_ENGINE_MCRYPT
     *
     * It's faster to re-init $enmcrypt if
     * $buffer bytes > $cfb_init_len than
     * using the $ecb resource furthermore.
     *
     * This value depends of the chosen cipher
     * and the time it would be needed for it's
     * initialization [by mcrypt_generic_init()]
     * which, typically, depends on the complexity
     * on its internaly Key-expanding algorithm.
     *
     * @see self::encrypt()
     * @var int
     * @access private
     */
    var $cfb_init_len = 600;

    /**
     * Does internal cipher state need to be (re)initialized?
     *
     * @see self::setKey()
     * @see self::setIV()
     * @see self::disableContinuousBuffer()
     * @var bool
     * @access private
     */
    var $changed = true;

    /**
     * Padding status
     *
     * @see self::enablePadding()
     * @var bool
     * @access private
     */
    var $padding = true;

    /**
     * Is the mode one that is paddable?
     *
     * @see self::Crypt_Base()
     * @var bool
     * @access private
     */
    var $paddable = false;

    /**
     * Holds which crypt engine internaly should be use,
     * which will be determined automatically on __construct()
     *
     * Currently available $engines are:
     * - CRYPT_ENGINE_OPENSSL  (very fast, php-extension: openssl, extension_loaded('openssl') required)
     * - CRYPT_ENGINE_MCRYPT   (fast, php-extension: mcrypt, extension_loaded('mcrypt') required)
     * - CRYPT_ENGINE_INTERNAL (slower, pure php-engine, no php-extension required)
     *
     * @see self::_setEngine()
     * @see self::encrypt()
     * @see self::decrypt()
     * @var int
     * @access private
     */
    var $engine;

    /**
     * Holds the preferred crypt engine
     *
     * @see self::_setEngine()
     * @see self::setPreferredEngine()
     * @var int
     * @access private
     */
    var $preferredEngine;

    /**
     * The mcrypt specific name of the cipher
     *
     * Only used if $engine == CRYPT_ENGINE_MCRYPT
     *
     * @link http://www.php.net/mcrypt_module_open
     * @link http://www.php.net/mcrypt_list_algorithms
     * @see self::_setupMcrypt()
     * @var string
     * @access private
     */
    var $cipher_name_mcrypt;

    /**
     * The openssl specific name of the cipher
     *
     * Only used if $engine == CRYPT_ENGINE_OPENSSL
     *
     * @link http://www.php.net/openssl-get-cipher-methods
     * @var string
     * @access private
     */
    var $cipher_name_openssl;

    /**
     * The openssl specific name of the cipher in ECB mode
     *
     * If OpenSSL does not support the mode we're trying to use (CTR)
     * it can still be emulated with ECB mode.
     *
     * @link http://www.php.net/openssl-get-cipher-methods
     * @var string
     * @access private
     */
    var $cipher_name_openssl_ecb;

    /**
     * The default salt used by setPassword()
     *
     * @see self::setPassword()
     * @var string
     * @access private
     */
    var $password_default_salt = 'phpseclib/salt';

    /**
     * The namespace used by the cipher for its constants.
     *
     * ie: AES.php is using CRYPT_AES_MODE_* for its constants
     *     so $const_namespace is AES
     *
     *     DES.php is using CRYPT_DES_MODE_* for its constants
     *     so $const_namespace is DES... and so on
     *
     * All CRYPT_<$const_namespace>_MODE_* are aliases of
     * the generic CRYPT_MODE_* constants, so both could be used
     * for each cipher.
     *
     * Example:
     * $aes = new Crypt_AES(CRYPT_AES_MODE_CFB); // $aes will operate in cfb mode
     * $aes = new Crypt_AES(CRYPT_MODE_CFB);     // identical
     *
     * @see self::Crypt_Base()
     * @var string
     * @access private
     */
    var $const_namespace;

    /**
     * The name of the performance-optimized callback function
     *
     * Used by encrypt() / decrypt()
     * only if $engine == CRYPT_ENGINE_INTERNAL
     *
     * @see self::encrypt()
     * @see self::decrypt()
     * @see self::_setupInlineCrypt()
     * @see self::$use_inline_crypt
     * @var Callback
     * @access private
     */
    var $inline_crypt;

    /**
     * Holds whether performance-optimized $inline_crypt() can/should be used.
     *
     * @see self::encrypt()
     * @see self::decrypt()
     * @see self::inline_crypt
     * @var mixed
     * @access private
     */
    var $use_inline_crypt;

    /**
     * If OpenSSL can be used in ECB but not in CTR we can emulate CTR
     *
     * @see self::_openssl_ctr_process()
     * @var bool
     * @access private
     */
    var $openssl_emulate_ctr = false;

    /**
     * Determines what options are passed to openssl_encrypt/decrypt
     *
     * @see self::isValidEngine()
     * @var mixed
     * @access private
     */
    var $openssl_options;

    /**
     * Has the key length explicitly been set or should it be derived from the key, itself?
     *
     * @see self::setKeyLength()
     * @var bool
     * @access private
     */
    var $explicit_key_length = false;

    /**
     * Don't truncate / null pad key
     *
     * @see self::_clearBuffers()
     * @var bool
     * @access private
     */
    var $skip_key_adjustment = false;

    /**
     * Default Constructor.
     *
     * Determines whether or not the mcrypt extension should be used.
     *
     * $mode could be:
     *
     * - CRYPT_MODE_ECB
     *
     * - CRYPT_MODE_CBC
     *
     * - CRYPT_MODE_CTR
     *
     * - CRYPT_MODE_CFB
     *
     * - CRYPT_MODE_OFB
     *
     * (or the alias constants of the chosen cipher, for example for AES: CRYPT_AES_MODE_ECB or CRYPT_AES_MODE_CBC ...)
     *
     * If not explicitly set, CRYPT_MODE_CBC will be used.
     *
     * @param int $mode
     * @access public
     */
    function __construct($mode = CRYPT_MODE_CBC)
    {
        // $mode dependent settings
        switch ($mode) {
            case CRYPT_MODE_ECB:
                $this->paddable = true;
                $this->mode = CRYPT_MODE_ECB;
                break;
            case CRYPT_MODE_CTR:
            case CRYPT_MODE_CFB:
            case CRYPT_MODE_OFB:
            case CRYPT_MODE_STREAM:
                $this->mode = $mode;
                break;
            case CRYPT_MODE_CBC:
            default:
                $this->paddable = true;
                $this->mode = CRYPT_MODE_CBC;
        }

        $this->_setEngine();

        // Determining whether inline crypting can be used by the cipher
        if ($this->use_inline_crypt !== false) {
            $this->use_inline_crypt = version_compare(PHP_VERSION, '5.3.0') >= 0 || function_exists('create_function');
        }
    }

    /**
     * PHP4 compatible Default Constructor.
     *
     * @see self::__construct()
     * @param int $mode
     * @access public
     */
    function Crypt_Base($mode = CRYPT_MODE_CBC)
    {
        $this->__construct($mode);
    }

    /**
     * Sets the initialization vector. (optional)
     *
     * SetIV is not required when CRYPT_MODE_ECB (or ie for AES: CRYPT_AES_MODE_ECB) is being used.  If not explicitly set, it'll be assumed
     * to be all zero's.
     *
     * @access public
     * @param string $iv
     * @internal Can be overwritten by a sub class, but does not have to be
     */
    function setIV($iv)
    {
        if ($this->mode == CRYPT_MODE_ECB) {
            return;
        }

        $this->iv = $iv;
        $this->changed = true;
    }

    /**
     * Sets the key length.
     *
     * Keys with explicitly set lengths need to be treated accordingly
     *
     * @access public
     * @param int $length
     */
    function setKeyLength($length)
    {
        $this->explicit_key_length = true;
        $this->changed = true;
        $this->_setEngine();
    }

    /**
     * Returns the current key length in bits
     *
     * @access public
     * @return int
     */
    function getKeyLength()
    {
        return $this->key_length << 3;
    }

    /**
     * Returns the current block length in bits
     *
     * @access public
     * @return int
     */
    function getBlockLength()
    {
        return $this->block_size << 3;
    }

    /**
     * Sets the key.
     *
     * The min/max length(s) of the key depends on the cipher which is used.
     * If the key not fits the length(s) of the cipher it will paded with null bytes
     * up to the closest valid key length.  If the key is more than max length,
     * we trim the excess bits.
     *
     * If the key is not explicitly set, it'll be assumed to be all null bytes.
     *
     * @access public
     * @param string $key
     * @internal Could, but not must, extend by the child Crypt_* class
     */
    function setKey($key)
    {
        if (!$this->explicit_key_length) {
            $this->setKeyLength(strlen($key) << 3);
            $this->explicit_key_length = false;
        }

        $this->key = $key;
        $this->changed = true;
        $this->_setEngine();
    }

    /**
     * Sets the password.
     *
     * Depending on what $method is set to, setPassword()'s (optional) parameters are as follows:
     *     {@link http://en.wikipedia.org/wiki/PBKDF2 pbkdf2} or pbkdf1:
     *         $hash, $salt, $count, $dkLen
     *
     *         Where $hash (default = sha1) currently supports the following hashes: see: Crypt/Hash.php
     *
     * @see Crypt/Hash.php
     * @param string $password
     * @param string $method
     * @return bool
     * @access public
     * @internal Could, but not must, extend by the child Crypt_* class
     */
    function setPassword($password, $method = 'pbkdf2')
    {
        $key = '';

        switch ($method) {
            default: // 'pbkdf2' or 'pbkdf1'
                $func_args = func_get_args();

                // Hash function
                $hash = isset($func_args[2]) ? $func_args[2] : 'sha1';

                // WPA and WPA2 use the SSID as the salt
                $salt = isset($func_args[3]) ? $func_args[3] : $this->password_default_salt;

                // RFC2898#section-4.2 uses 1,000 iterations by default
                // WPA and WPA2 use 4,096.
                $count = isset($func_args[4]) ? $func_args[4] : 1000;

                // Keylength
                if (isset($func_args[5]) && $func_args[5] > 0) {
                    $dkLen = $func_args[5];
                } else {
                    $dkLen = $method == 'pbkdf1' ? 2 * $this->key_length : $this->key_length;
                }

                switch (true) {
                    case $method == 'pbkdf1':
                        if (!class_exists('Crypt_Hash')) {
                            include_once 'Crypt/Hash.php';
                        }
                        $hashObj = new Crypt_Hash();
                        $hashObj->setHash($hash);
                        if ($dkLen > $hashObj->getLength()) {
                            user_error('Derived key too long');
                            return false;
                        }
                        $t = $password . $salt;
                        for ($i = 0; $i < $count; ++$i) {
                            $t = $hashObj->hash($t);
                        }
                        $key = substr($t, 0, $dkLen);

                        $this->setKey(substr($key, 0, $dkLen >> 1));
                        $this->setIV(substr($key, $dkLen >> 1));

                        return true;
                    // Determining if php[>=5.5.0]'s hash_pbkdf2() function avail- and useable
                    case !function_exists('hash_pbkdf2'):
                    case !function_exists('hash_algos'):
                    case !in_array($hash, hash_algos()):
                        if (!class_exists('Crypt_Hash')) {
                            include_once 'Crypt/Hash.php';
                        }
                        $i = 1;
                        $hmac = new Crypt_Hash();
                        $hmac->setHash($hash);
                        $hmac->setKey($password);
                        while (strlen($key) < $dkLen) {
                            $f = $u = $hmac->hash($salt . pack('N', $i++));
                            for ($j = 2; $j <= $count; ++$j) {
                                $u = $hmac->hash($u);
                                $f^= $u;
                            }
                            $key.= $f;
                        }
                        $key = substr($key, 0, $dkLen);
                        break;
                    default:
                        $key = hash_pbkdf2($hash, $password, $salt, $count, $dkLen, true);
                }
        }

        $this->setKey($key);

        return true;
    }

    /**
     * Encrypts a message.
     *
     * $plaintext will be padded with additional bytes such that it's length is a multiple of the block size. Other cipher
     * implementations may or may not pad in the same manner.  Other common approaches to padding and the reasons why it's
     * necessary are discussed in the following
     * URL:
     *
     * {@link http://www.di-mgt.com.au/cryptopad.html http://www.di-mgt.com.au/cryptopad.html}
     *
     * An alternative to padding is to, separately, send the length of the file.  This is what SSH, in fact, does.
     * strlen($plaintext) will still need to be a multiple of the block size, however, arbitrary values can be added to make it that
     * length.
     *
     * @see self::decrypt()
     * @access public
     * @param string $plaintext
     * @return string $ciphertext
     * @internal Could, but not must, extend by the child Crypt_* class
     */
    function encrypt($plaintext)
    {
        if ($this->paddable) {
            $plaintext = $this->_pad($plaintext);
        }

        if ($this->engine === CRYPT_ENGINE_OPENSSL) {
            if ($this->changed) {
                $this->_clearBuffers();
                $this->changed = false;
            }
            switch ($this->mode) {
                case CRYPT_MODE_STREAM:
                    return openssl_encrypt($plaintext, $this->cipher_name_openssl, $this->key, $this->openssl_options);
                case CRYPT_MODE_ECB:
                    $result = openssl_encrypt($plaintext, $this->cipher_name_openssl, $this->key, $this->openssl_options);
                    return !defined('OPENSSL_RAW_DATA') ? substr($result, 0, -$this->block_size) : $result;
                case CRYPT_MODE_CBC:
                    $result = openssl_encrypt($plaintext, $this->cipher_name_openssl, $this->key, $this->openssl_options, $this->encryptIV);
                    if (!defined('OPENSSL_RAW_DATA')) {
                        $result = substr($result, 0, -$this->block_size);
                    }
                    if ($this->continuousBuffer) {
                        $this->encryptIV = substr($result, -$this->block_size);
                    }
                    return $result;
                case CRYPT_MODE_CTR:
                    return $this->_openssl_ctr_process($plaintext, $this->encryptIV, $this->enbuffer);
                case CRYPT_MODE_CFB:
                    // cfb loosely routines inspired by openssl's:
                    // {@link http://cvs.openssl.org/fileview?f=openssl/crypto/modes/cfb128.c&v=1.3.2.2.2.1}
                    $ciphertext = '';
                    if ($this->continuousBuffer) {
                        $iv = &$this->encryptIV;
                        $pos = &$this->enbuffer['pos'];
                    } else {
                        $iv = $this->encryptIV;
                        $pos = 0;
                    }
                    $len = strlen($plaintext);
                    $i = 0;
                    if ($pos) {
                        $orig_pos = $pos;
                        $max = $this->block_size - $pos;
                        if ($len >= $max) {
                            $i = $max;
                            $len-= $max;
                            $pos = 0;
                        } else {
                            $i = $len;
                            $pos+= $len;
                            $len = 0;
                        }
                        // ie. $i = min($max, $len), $len-= $i, $pos+= $i, $pos%= $blocksize
                        $ciphertext = substr($iv, $orig_pos) ^ $plaintext;
                        $iv = substr_replace($iv, $ciphertext, $orig_pos, $i);
                        $plaintext = substr($plaintext, $i);
                    }

                    $overflow = $len % $this->block_size;

                    if ($overflow) {
                        $ciphertext.= openssl_encrypt(substr($plaintext, 0, -$overflow) . str_repeat("\0", $this->block_size), $this->cipher_name_openssl, $this->key, $this->openssl_options, $iv);
                        $iv = $this->_string_pop($ciphertext, $this->block_size);

                        $size = $len - $overflow;
                        $block = $iv ^ substr($plaintext, -$overflow);
                        $iv = substr_replace($iv, $block, 0, $overflow);
                        $ciphertext.= $block;
                        $pos = $overflow;
                    } elseif ($len) {
                        $ciphertext = openssl_encrypt($plaintext, $this->cipher_name_openssl, $this->key, $this->openssl_options, $iv);
                        $iv = substr($ciphertext, -$this->block_size);
                    }

                    return $ciphertext;
                case CRYPT_MODE_OFB:
                    return $this->_openssl_ofb_process($plaintext, $this->encryptIV, $this->enbuffer);
            }
        }

        if ($this->engine === CRYPT_ENGINE_MCRYPT) {
            if ($this->changed) {
                $this->_setupMcrypt();
                $this->changed = false;
            }
            if ($this->enchanged) {
                @mcrypt_generic_init($this->enmcrypt, $this->key, $this->encryptIV);
                $this->enchanged = false;
            }

            // re: {@link http://phpseclib.sourceforge.net/cfb-demo.phps}
            // using mcrypt's default handing of CFB the above would output two different things.  using phpseclib's
            // rewritten CFB implementation the above outputs the same thing twice.
            if ($this->mode == CRYPT_MODE_CFB && $this->continuousBuffer) {
                $block_size = $this->block_size;
                $iv = &$this->encryptIV;
                $pos = &$this->enbuffer['pos'];
                $len = strlen($plaintext);
                $ciphertext = '';
                $i = 0;
                if ($pos) {
                    $orig_pos = $pos;
                    $max = $block_size - $pos;
                    if ($len >= $max) {
                        $i = $max;
                        $len-= $max;
                        $pos = 0;
                    } else {
                        $i = $len;
                        $pos+= $len;
                        $len = 0;
                    }
                    $ciphertext = substr($iv, $orig_pos) ^ $plaintext;
                    $iv = substr_replace($iv, $ciphertext, $orig_pos, $i);
                    $this->enbuffer['enmcrypt_init'] = true;
                }
                if ($len >= $block_size) {
                    if ($this->enbuffer['enmcrypt_init'] === false || $len > $this->cfb_init_len) {
                        if ($this->enbuffer['enmcrypt_init'] === true) {
                            @mcrypt_generic_init($this->enmcrypt, $this->key, $iv);
                            $this->enbuffer['enmcrypt_init'] = false;
                        }
                        $ciphertext.= @mcrypt_generic($this->enmcrypt, substr($plaintext, $i, $len - $len % $block_size));
                        $iv = substr($ciphertext, -$block_size);
                        $len%= $block_size;
                    } else {
                        while ($len >= $block_size) {
                            $iv = @mcrypt_generic($this->ecb, $iv) ^ substr($plaintext, $i, $block_size);
                            $ciphertext.= $iv;
                            $len-= $block_size;
                            $i+= $block_size;
                        }
                    }
                }

                if ($len) {
                    $iv = @mcrypt_generic($this->ecb, $iv);
                    $block = $iv ^ substr($plaintext, -$len);
                    $iv = substr_replace($iv, $block, 0, $len);
                    $ciphertext.= $block;
                    $pos = $len;
                }

                return $ciphertext;
            }

            $ciphertext = @mcrypt_generic($this->enmcrypt, $plaintext);

            if (!$this->continuousBuffer) {
                @mcrypt_generic_init($this->enmcrypt, $this->key, $this->encryptIV);
            }

            return $ciphertext;
        }

        if ($this->changed) {
            $this->_setup();
            $this->changed = false;
        }
        if ($this->use_inline_crypt) {
            $inline = $this->inline_crypt;
            return $inline('encrypt', $this, $plaintext);
        }

        $buffer = &$this->enbuffer;
        $block_size = $this->block_size;
        $ciphertext = '';
        switch ($this->mode) {
            case CRYPT_MODE_ECB:
                for ($i = 0; $i < strlen($plaintext); $i+=$block_size) {
                    $ciphertext.= $this->_encryptBlock(substr($plaintext, $i, $block_size));
                }
                break;
            case CRYPT_MODE_CBC:
                $xor = $this->encryptIV;
                for ($i = 0; $i < strlen($plaintext); $i+=$block_size) {
                    $block = substr($plaintext, $i, $block_size);
                    $block = $this->_encryptBlock($block ^ $xor);
                    $xor = $block;
                    $ciphertext.= $block;
                }
                if ($this->continuousBuffer) {
                    $this->encryptIV = $xor;
                }
                break;
            case CRYPT_MODE_CTR:
                $xor = $this->encryptIV;
                if (strlen($buffer['ciphertext'])) {
                    for ($i = 0; $i < strlen($plaintext); $i+=$block_size) {
                        $block = substr($plaintext, $i, $block_size);
                        if (strlen($block) > strlen($buffer['ciphertext'])) {
                            $buffer['ciphertext'].= $this->_encryptBlock($xor);
                        }
                        $this->_increment_str($xor);
                        $key = $this->_string_shift($buffer['ciphertext'], $block_size);
                        $ciphertext.= $block ^ $key;
                    }
                } else {
                    for ($i = 0; $i < strlen($plaintext); $i+=$block_size) {
                        $block = substr($plaintext, $i, $block_size);
                        $key = $this->_encryptBlock($xor);
                        $this->_increment_str($xor);
                        $ciphertext.= $block ^ $key;
                    }
                }
                if ($this->continuousBuffer) {
                    $this->encryptIV = $xor;
                    if ($start = strlen($plaintext) % $block_size) {
                        $buffer['ciphertext'] = substr($key, $start) . $buffer['ciphertext'];
                    }
                }
                break;
            case CRYPT_MODE_CFB:
                // cfb loosely routines inspired by openssl's:
                // {@link http://cvs.openssl.org/fileview?f=openssl/crypto/modes/cfb128.c&v=1.3.2.2.2.1}
                if ($this->continuousBuffer) {
                    $iv = &$this->encryptIV;
                    $pos = &$buffer['pos'];
                } else {
                    $iv = $this->encryptIV;
                    $pos = 0;
                }
                $len = strlen($plaintext);
                $i = 0;
                if ($pos) {
                    $orig_pos = $pos;
                    $max = $block_size - $pos;
                    if ($len >= $max) {
                        $i = $max;
                        $len-= $max;
                        $pos = 0;
                    } else {
                        $i = $len;
                        $pos+= $len;
                        $len = 0;
                    }
                    // ie. $i = min($max, $len), $len-= $i, $pos+= $i, $pos%= $blocksize
                    $ciphertext = substr($iv, $orig_pos) ^ $plaintext;
                    $iv = substr_replace($iv, $ciphertext, $orig_pos, $i);
                }
                while ($len >= $block_size) {
                    $iv = $this->_encryptBlock($iv) ^ substr($plaintext, $i, $block_size);
                    $ciphertext.= $iv;
                    $len-= $block_size;
                    $i+= $block_size;
                }
                if ($len) {
                    $iv = $this->_encryptBlock($iv);
                    $block = $iv ^ substr($plaintext, $i);
                    $iv = substr_replace($iv, $block, 0, $len);
                    $ciphertext.= $block;
                    $pos = $len;
                }
                break;
            case CRYPT_MODE_OFB:
                $xor = $this->encryptIV;
                if (strlen($buffer['xor'])) {
                    for ($i = 0; $i < strlen($plaintext); $i+=$block_size) {
                        $block = substr($plaintext, $i, $block_size);
                        if (strlen($block) > strlen($buffer['xor'])) {
                            $xor = $this->_encryptBlock($xor);
                            $buffer['xor'].= $xor;
                        }
                        $key = $this->_string_shift($buffer['xor'], $block_size);
                        $ciphertext.= $block ^ $key;
                    }
                } else {
                    for ($i = 0; $i < strlen($plaintext); $i+=$block_size) {
                        $xor = $this->_encryptBlock($xor);
                        $ciphertext.= substr($plaintext, $i, $block_size) ^ $xor;
                    }
                    $key = $xor;
                }
                if ($this->continuousBuffer) {
                    $this->encryptIV = $xor;
                    if ($start = strlen($plaintext) % $block_size) {
                        $buffer['xor'] = substr($key, $start) . $buffer['xor'];
                    }
                }
                break;
            case CRYPT_MODE_STREAM:
                $ciphertext = $this->_encryptBlock($plaintext);
                break;
        }

        return $ciphertext;
    }

    /**
     * Decrypts a message.
     *
     * If strlen($ciphertext) is not a multiple of the block size, null bytes will be added to the end of the string until
     * it is.
     *
     * @see self::encrypt()
     * @access public
     * @param string $ciphertext
     * @return string $plaintext
     * @internal Could, but not must, extend by the child Crypt_* class
     */
    function decrypt($ciphertext)
    {
        if ($this->paddable) {
            // we pad with chr(0) since that's what mcrypt_generic does.  to quote from {@link http://www.php.net/function.mcrypt-generic}:
            // "The data is padded with "\0" to make sure the length of the data is n * blocksize."
            $ciphertext = str_pad($ciphertext, strlen($ciphertext) + ($this->block_size - strlen($ciphertext) % $this->block_size) % $this->block_size, chr(0));
        }

        if ($this->engine === CRYPT_ENGINE_OPENSSL) {
            if ($this->changed) {
                $this->_clearBuffers();
                $this->changed = false;
            }
            switch ($this->mode) {
                case CRYPT_MODE_STREAM:
                    $plaintext = openssl_decrypt($ciphertext, $this->cipher_name_openssl, $this->key, $this->openssl_options);
                    break;
                case CRYPT_MODE_ECB:
                    if (!defined('OPENSSL_RAW_DATA')) {
                        $ciphertext.= openssl_encrypt('', $this->cipher_name_openssl_ecb, $this->key, true);
                    }
                    $plaintext = openssl_decrypt($ciphertext, $this->cipher_name_openssl, $this->key, $this->openssl_options);
                    break;
                case CRYPT_MODE_CBC:
                    if (!defined('OPENSSL_RAW_DATA')) {
                        $padding = str_repeat(chr($this->block_size), $this->block_size) ^ substr($ciphertext, -$this->block_size);
                        $ciphertext.= substr(openssl_encrypt($padding, $this->cipher_name_openssl_ecb, $this->key, true), 0, $this->block_size);
                        $offset = 2 * $this->block_size;
                    } else {
                        $offset = $this->block_size;
                    }
                    $plaintext = openssl_decrypt($ciphertext, $this->cipher_name_openssl, $this->key, $this->openssl_options, $this->decryptIV);
                    if ($this->continuousBuffer) {
                        $this->decryptIV = substr($ciphertext, -$offset, $this->block_size);
                    }
                    break;
                case CRYPT_MODE_CTR:
                    $plaintext = $this->_openssl_ctr_process($ciphertext, $this->decryptIV, $this->debuffer);
                    break;
                case CRYPT_MODE_CFB:
                    // cfb loosely routines inspired by openssl's:
                    // {@link http://cvs.openssl.org/fileview?f=openssl/crypto/modes/cfb128.c&v=1.3.2.2.2.1}
                    $plaintext = '';
                    if ($this->continuousBuffer) {
                        $iv = &$this->decryptIV;
                        $pos = &$this->buffer['pos'];
                    } else {
                        $iv = $this->decryptIV;
                        $pos = 0;
                    }
                    $len = strlen($ciphertext);
                    $i = 0;
                    if ($pos) {
                        $orig_pos = $pos;
                        $max = $this->block_size - $pos;
                        if ($len >= $max) {
                            $i = $max;
                            $len-= $max;
                            $pos = 0;
                        } else {
                            $i = $len;
                            $pos+= $len;
                            $len = 0;
                        }
                        // ie. $i = min($max, $len), $len-= $i, $pos+= $i, $pos%= $this->blocksize
                        $plaintext = substr($iv, $orig_pos) ^ $ciphertext;
                        $iv = substr_replace($iv, substr($ciphertext, 0, $i), $orig_pos, $i);
                        $ciphertext = substr($ciphertext, $i);
                    }
                    $overflow = $len % $this->block_size;
                    if ($overflow) {
                        $plaintext.= openssl_decrypt(substr($ciphertext, 0, -$overflow), $this->cipher_name_openssl, $this->key, $this->openssl_options, $iv);
                        if ($len - $overflow) {
                            $iv = substr($ciphertext, -$overflow - $this->block_size, -$overflow);
                        }
                        $iv = openssl_encrypt(str_repeat("\0", $this->block_size), $this->cipher_name_openssl, $this->key, $this->openssl_options, $iv);
                        $plaintext.= $iv ^ substr($ciphertext, -$overflow);
                        $iv = substr_replace($iv, substr($ciphertext, -$overflow), 0, $overflow);
                        $pos = $overflow;
                    } elseif ($len) {
                        $plaintext.= openssl_decrypt($ciphertext, $this->cipher_name_openssl, $this->key, $this->openssl_options, $iv);
                        $iv = substr($ciphertext, -$this->block_size);
                    }
                    break;
                case CRYPT_MODE_OFB:
                    $plaintext = $this->_openssl_ofb_process($ciphertext, $this->decryptIV, $this->debuffer);
            }

            return $this->paddable ? $this->_unpad($plaintext) : $plaintext;
        }

        if ($this->engine === CRYPT_ENGINE_MCRYPT) {
            $block_size = $this->block_size;
            if ($this->changed) {
                $this->_setupMcrypt();
                $this->changed = false;
            }
            if ($this->dechanged) {
                @mcrypt_generic_init($this->demcrypt, $this->key, $this->decryptIV);
                $this->dechanged = false;
            }

            if ($this->mode == CRYPT_MODE_CFB && $this->continuousBuffer) {
                $iv = &$this->decryptIV;
                $pos = &$this->debuffer['pos'];
                $len = strlen($ciphertext);
                $plaintext = '';
                $i = 0;
                if ($pos) {
                    $orig_pos = $pos;
                    $max = $block_size - $pos;
                    if ($len >= $max) {
                        $i = $max;
                        $len-= $max;
                        $pos = 0;
                    } else {
                        $i = $len;
                        $pos+= $len;
                        $len = 0;
                    }
                    // ie. $i = min($max, $len), $len-= $i, $pos+= $i, $pos%= $blocksize
                    $plaintext = substr($iv, $orig_pos) ^ $ciphertext;
                    $iv = substr_replace($iv, substr($ciphertext, 0, $i), $orig_pos, $i);
                }
                if ($len >= $block_size) {
                    $cb = substr($ciphertext, $i, $len - $len % $block_size);
                    $plaintext.= @mcrypt_generic($this->ecb, $iv . $cb) ^ $cb;
                    $iv = substr($cb, -$block_size);
                    $len%= $block_size;
                }
                if ($len) {
                    $iv = @mcrypt_generic($this->ecb, $iv);
                    $plaintext.= $iv ^ substr($ciphertext, -$len);
                    $iv = substr_replace($iv, substr($ciphertext, -$len), 0, $len);
                    $pos = $len;
                }

                return $plaintext;
            }

            $plaintext = @mdecrypt_generic($this->demcrypt, $ciphertext);

            if (!$this->continuousBuffer) {
                @mcrypt_generic_init($this->demcrypt, $this->key, $this->decryptIV);
            }

            return $this->paddable ? $this->_unpad($plaintext) : $plaintext;
        }

        if ($this->changed) {
            $this->_setup();
            $this->changed = false;
        }
        if ($this->use_inline_crypt) {
            $inline = $this->inline_crypt;
            return $inline('decrypt', $this, $ciphertext);
        }

        $block_size = $this->block_size;

        $buffer = &$this->debuffer;
        $plaintext = '';
        switch ($this->mode) {
            case CRYPT_MODE_ECB:
                for ($i = 0; $i < strlen($ciphertext); $i+=$block_size) {
                    $plaintext.= $this->_decryptBlock(substr($ciphertext, $i, $block_size));
                }
                break;
            case CRYPT_MODE_CBC:
                $xor = $this->decryptIV;
                for ($i = 0; $i < strlen($ciphertext); $i+=$block_size) {
                    $block = substr($ciphertext, $i, $block_size);
                    $plaintext.= $this->_decryptBlock($block) ^ $xor;
                    $xor = $block;
                }
                if ($this->continuousBuffer) {
                    $this->decryptIV = $xor;
                }
                break;
            case CRYPT_MODE_CTR:
                $xor = $this->decryptIV;
                if (strlen($buffer['ciphertext'])) {
                    for ($i = 0; $i < strlen($ciphertext); $i+=$block_size) {
                        $block = substr($ciphertext, $i, $block_size);
                        if (strlen($block) > strlen($buffer['ciphertext'])) {
                            $buffer['ciphertext'].= $this->_encryptBlock($xor);
                            $this->_increment_str($xor);
                        }
                        $key = $this->_string_shift($buffer['ciphertext'], $block_size);
                        $plaintext.= $block ^ $key;
                    }
                } else {
                    for ($i = 0; $i < strlen($ciphertext); $i+=$block_size) {
                        $block = substr($ciphertext, $i, $block_size);
                        $key = $this->_encryptBlock($xor);
                        $this->_increment_str($xor);
                        $plaintext.= $block ^ $key;
                    }
                }
                if ($this->continuousBuffer) {
                    $this->decryptIV = $xor;
                    if ($start = strlen($ciphertext) % $block_size) {
                        $buffer['ciphertext'] = substr($key, $start) . $buffer['ciphertext'];
                    }
                }
                break;
            case CRYPT_MODE_CFB:
                if ($this->continuousBuffer) {
                    $iv = &$this->decryptIV;
                    $pos = &$buffer['pos'];
                } else {
                    $iv = $this->decryptIV;
                    $pos = 0;
                }
                $len = strlen($ciphertext);
                $i = 0;
                if ($pos) {
                    $orig_pos = $pos;
                    $max = $block_size - $pos;
                    if ($len >= $max) {
                        $i = $max;
                        $len-= $max;
                        $pos = 0;
                    } else {
                        $i = $len;
                        $pos+= $len;
                        $len = 0;
                    }
                    // ie. $i = min($max, $len), $len-= $i, $pos+= $i, $pos%= $blocksize
                    $plaintext = substr($iv, $orig_pos) ^ $ciphertext;
                    $iv = substr_replace($iv, substr($ciphertext, 0, $i), $orig_pos, $i);
                }
                while ($len >= $block_size) {
                    $iv = $this->_encryptBlock($iv);
                    $cb = substr($ciphertext, $i, $block_size);
                    $plaintext.= $iv ^ $cb;
                    $iv = $cb;
                    $len-= $block_size;
                    $i+= $block_size;
                }
                if ($len) {
                    $iv = $this->_encryptBlock($iv);
                    $plaintext.= $iv ^ substr($ciphertext, $i);
                    $iv = substr_replace($iv, substr($ciphertext, $i), 0, $len);
                    $pos = $len;
                }
                break;
            case CRYPT_MODE_OFB:
                $xor = $this->decryptIV;
                if (strlen($buffer['xor'])) {
                    for ($i = 0; $i < strlen($ciphertext); $i+=$block_size) {
                        $block = substr($ciphertext, $i, $block_size);
                        if (strlen($block) > strlen($buffer['xor'])) {
                            $xor = $this->_encryptBlock($xor);
                            $buffer['xor'].= $xor;
                        }
                        $key = $this->_string_shift($buffer['xor'], $block_size);
                        $plaintext.= $block ^ $key;
                    }
                } else {
                    for ($i = 0; $i < strlen($ciphertext); $i+=$block_size) {
                        $xor = $this->_encryptBlock($xor);
                        $plaintext.= substr($ciphertext, $i, $block_size) ^ $xor;
                    }
                    $key = $xor;
                }
                if ($this->continuousBuffer) {
                    $this->decryptIV = $xor;
                    if ($start = strlen($ciphertext) % $block_size) {
                        $buffer['xor'] = substr($key, $start) . $buffer['xor'];
                    }
                }
                break;
            case CRYPT_MODE_STREAM:
                $plaintext = $this->_decryptBlock($ciphertext);
                break;
        }
        return $this->paddable ? $this->_unpad($plaintext) : $plaintext;
    }

    /**
     * OpenSSL CTR Processor
     *
     * PHP's OpenSSL bindings do not operate in continuous mode so we'll wrap around it. Since the keystream
     * for CTR is the same for both encrypting and decrypting this function is re-used by both Crypt_Base::encrypt()
     * and Crypt_Base::decrypt(). Also, OpenSSL doesn't implement CTR for all of it's symmetric ciphers so this
     * function will emulate CTR with ECB when necessary.
     *
     * @see self::encrypt()
     * @see self::decrypt()
     * @param string $plaintext
     * @param string $encryptIV
     * @param array $buffer
     * @return string
     * @access private
     */
    function _openssl_ctr_process($plaintext, &$encryptIV, &$buffer)
    {
        $ciphertext = '';

        $block_size = $this->block_size;
        $key = $this->key;

        if ($this->openssl_emulate_ctr) {
            $xor = $encryptIV;
            if (strlen($buffer['ciphertext'])) {
                for ($i = 0; $i < strlen($plaintext); $i+=$block_size) {
                    $block = substr($plaintext, $i, $block_size);
                    if (strlen($block) > strlen($buffer['ciphertext'])) {
                        $result = openssl_encrypt($xor, $this->cipher_name_openssl_ecb, $key, $this->openssl_options);
                        $result = !defined('OPENSSL_RAW_DATA') ? substr($result, 0, -$this->block_size) : $result;
                        $buffer['ciphertext'].= $result;
                    }
                    $this->_increment_str($xor);
                    $otp = $this->_string_shift($buffer['ciphertext'], $block_size);
                    $ciphertext.= $block ^ $otp;
                }
            } else {
                for ($i = 0; $i < strlen($plaintext); $i+=$block_size) {
                    $block = substr($plaintext, $i, $block_size);
                    $otp = openssl_encrypt($xor, $this->cipher_name_openssl_ecb, $key, $this->openssl_options);
                    $otp = !defined('OPENSSL_RAW_DATA') ? substr($otp, 0, -$this->block_size) : $otp;
                    $this->_increment_str($xor);
                    $ciphertext.= $block ^ $otp;
                }
            }
            if ($this->continuousBuffer) {
                $encryptIV = $xor;
                if ($start = strlen($plaintext) % $block_size) {
                    $buffer['ciphertext'] = substr($key, $start) . $buffer['ciphertext'];
                }
            }

            return $ciphertext;
        }

        if (strlen($buffer['ciphertext'])) {
            $ciphertext = $plaintext ^ $this->_string_shift($buffer['ciphertext'], strlen($plaintext));
            $plaintext = substr($plaintext, strlen($ciphertext));

            if (!strlen($plaintext)) {
                return $ciphertext;
            }
        }

        $overflow = strlen($plaintext) % $block_size;
        if ($overflow) {
            $plaintext2 = $this->_string_pop($plaintext, $overflow); // ie. trim $plaintext to a multiple of $block_size and put rest of $plaintext in $plaintext2
            $encrypted = openssl_encrypt($plaintext . str_repeat("\0", $block_size), $this->cipher_name_openssl, $key, $this->openssl_options, $encryptIV);
            $temp = $this->_string_pop($encrypted, $block_size);
            $ciphertext.= $encrypted . ($plaintext2 ^ $temp);
            if ($this->continuousBuffer) {
                $buffer['ciphertext'] = substr($temp, $overflow);
                $encryptIV = $temp;
            }
        } elseif (!strlen($buffer['ciphertext'])) {
            $ciphertext.= openssl_encrypt($plaintext . str_repeat("\0", $block_size), $this->cipher_name_openssl, $key, $this->openssl_options, $encryptIV);
            $temp = $this->_string_pop($ciphertext, $block_size);
            if ($this->continuousBuffer) {
                $encryptIV = $temp;
            }
        }
        if ($this->continuousBuffer) {
            if (!defined('OPENSSL_RAW_DATA')) {
                $encryptIV.= openssl_encrypt('', $this->cipher_name_openssl_ecb, $key, $this->openssl_options);
            }
            $encryptIV = openssl_decrypt($encryptIV, $this->cipher_name_openssl_ecb, $key, $this->openssl_options);
            if ($overflow) {
                $this->_increment_str($encryptIV);
            }
        }

        return $ciphertext;
    }

    /**
     * OpenSSL OFB Processor
     *
     * PHP's OpenSSL bindings do not operate in continuous mode so we'll wrap around it. Since the keystream
     * for OFB is the same for both encrypting and decrypting this function is re-used by both Crypt_Base::encrypt()
     * and Crypt_Base::decrypt().
     *
     * @see self::encrypt()
     * @see self::decrypt()
     * @param string $plaintext
     * @param string $encryptIV
     * @param array $buffer
     * @return string
     * @access private
     */
    function _openssl_ofb_process($plaintext, &$encryptIV, &$buffer)
    {
        if (strlen($buffer['xor'])) {
            $ciphertext = $plaintext ^ $buffer['xor'];
            $buffer['xor'] = substr($buffer['xor'], strlen($ciphertext));
            $plaintext = substr($plaintext, strlen($ciphertext));
        } else {
            $ciphertext = '';
        }

        $block_size = $this->block_size;

        $len = strlen($plaintext);
        $key = $this->key;
        $overflow = $len % $block_size;

        if (strlen($plaintext)) {
            if ($overflow) {
                $ciphertext.= openssl_encrypt(substr($plaintext, 0, -$overflow) . str_repeat("\0", $block_size), $this->cipher_name_openssl, $key, $this->openssl_options, $encryptIV);
                $xor = $this->_string_pop($ciphertext, $block_size);
                if ($this->continuousBuffer) {
                    $encryptIV = $xor;
                }
                $ciphertext.= $this->_string_shift($xor, $overflow) ^ substr($plaintext, -$overflow);
                if ($this->continuousBuffer) {
                    $buffer['xor'] = $xor;
                }
            } else {
                $ciphertext = openssl_encrypt($plaintext, $this->cipher_name_openssl, $key, $this->openssl_options, $encryptIV);
                if ($this->continuousBuffer) {
                    $encryptIV = substr($ciphertext, -$block_size) ^ substr($plaintext, -$block_size);
                }
            }
        }

        return $ciphertext;
    }

    /**
     * phpseclib <-> OpenSSL Mode Mapper
     *
     * May need to be overwritten by classes extending this one in some cases
     *
     * @return int
     * @access private
     */
    function _openssl_translate_mode()
    {
        switch ($this->mode) {
            case CRYPT_MODE_ECB:
                return 'ecb';
            case CRYPT_MODE_CBC:
                return 'cbc';
            case CRYPT_MODE_CTR:
                return 'ctr';
            case CRYPT_MODE_CFB:
                return 'cfb';
            case CRYPT_MODE_OFB:
                return 'ofb';
        }
    }

    /**
     * Pad "packets".
     *
     * Block ciphers working by encrypting between their specified [$this->]block_size at a time
     * If you ever need to encrypt or decrypt something that isn't of the proper length, it becomes necessary to
     * pad the input so that it is of the proper length.
     *
     * Padding is enabled by default.  Sometimes, however, it is undesirable to pad strings.  Such is the case in SSH,
     * where "packets" are padded with random bytes before being encrypted.  Unpad these packets and you risk stripping
     * away characters that shouldn't be stripped away. (SSH knows how many bytes are added because the length is
     * transmitted separately)
     *
     * @see self::disablePadding()
     * @access public
     */
    function enablePadding()
    {
        $this->padding = true;
    }

    /**
     * Do not pad packets.
     *
     * @see self::enablePadding()
     * @access public
     */
    function disablePadding()
    {
        $this->padding = false;
    }

    /**
     * Treat consecutive "packets" as if they are a continuous buffer.
     *
     * Say you have a 32-byte plaintext $plaintext.  Using the default behavior, the two following code snippets
     * will yield different outputs:
     *
     * <code>
     *    echo $rijndael->encrypt(substr($plaintext,  0, 16));
     *    echo $rijndael->encrypt(substr($plaintext, 16, 16));
     * </code>
     * <code>
     *    echo $rijndael->encrypt($plaintext);
     * </code>
     *
     * The solution is to enable the continuous buffer.  Although this will resolve the above discrepancy, it creates
     * another, as demonstrated with the following:
     *
     * <code>
     *    $rijndael->encrypt(substr($plaintext, 0, 16));
     *    echo $rijndael->decrypt($rijndael->encrypt(substr($plaintext, 16, 16)));
     * </code>
     * <code>
     *    echo $rijndael->decrypt($rijndael->encrypt(substr($plaintext, 16, 16)));
     * </code>
     *
     * With the continuous buffer disabled, these would yield the same output.  With it enabled, they yield different
     * outputs.  The reason is due to the fact that the initialization vector's change after every encryption /
     * decryption round when the continuous buffer is enabled.  When it's disabled, they remain constant.
     *
     * Put another way, when the continuous buffer is enabled, the state of the Crypt_*() object changes after each
     * encryption / decryption round, whereas otherwise, it'd remain constant.  For this reason, it's recommended that
     * continuous buffers not be used.  They do offer better security and are, in fact, sometimes required (SSH uses them),
     * however, they are also less intuitive and more likely to cause you problems.
     *
     * @see self::disableContinuousBuffer()
     * @access public
     * @internal Could, but not must, extend by the child Crypt_* class
     */
    function enableContinuousBuffer()
    {
        if ($this->mode == CRYPT_MODE_ECB) {
            return;
        }

        $this->continuousBuffer = true;

        $this->_setEngine();
    }

    /**
     * Treat consecutive packets as if they are a discontinuous buffer.
     *
     * The default behavior.
     *
     * @see self::enableContinuousBuffer()
     * @access public
     * @internal Could, but not must, extend by the child Crypt_* class
     */
    function disableContinuousBuffer()
    {
        if ($this->mode == CRYPT_MODE_ECB) {
            return;
        }
        if (!$this->continuousBuffer) {
            return;
        }

        $this->continuousBuffer = false;
        $this->changed = true;

        $this->_setEngine();
    }

    /**
     * Test for engine validity
     *
     * @see self::Crypt_Base()
     * @param int $engine
     * @access public
     * @return bool
     */
    function isValidEngine($engine)
    {
        switch ($engine) {
            case CRYPT_ENGINE_OPENSSL:
                if ($this->mode == CRYPT_MODE_STREAM && $this->continuousBuffer) {
                    return false;
                }
                $this->openssl_emulate_ctr = false;
                $result = $this->cipher_name_openssl &&
                          extension_loaded('openssl') &&
                          // PHP 5.3.0 - 5.3.2 did not let you set IV's
                          version_compare(PHP_VERSION, '5.3.3', '>=');
                if (!$result) {
                    return false;
                }

                // prior to PHP 5.4.0 OPENSSL_RAW_DATA and OPENSSL_ZERO_PADDING were not defined. instead of expecting an integer
                // $options openssl_encrypt expected a boolean $raw_data.
                if (!defined('OPENSSL_RAW_DATA')) {
                    $this->openssl_options = true;
                } else {
                    $this->openssl_options = OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING;
                }

                $methods = openssl_get_cipher_methods();
                if (in_array($this->cipher_name_openssl, $methods)) {
                    return true;
                }
                // not all of openssl's symmetric cipher's support ctr. for those
                // that don't we'll emulate it
                switch ($this->mode) {
                    case CRYPT_MODE_CTR:
                        if (in_array($this->cipher_name_openssl_ecb, $methods)) {
                            $this->openssl_emulate_ctr = true;
                            return true;
                        }
                }
                return false;
            case CRYPT_ENGINE_MCRYPT:
                return $this->cipher_name_mcrypt &&
                       extension_loaded('mcrypt') &&
                       in_array($this->cipher_name_mcrypt, @mcrypt_list_algorithms());
            case CRYPT_ENGINE_INTERNAL:
                return true;
        }

        return false;
    }

    /**
     * Sets the preferred crypt engine
     *
     * Currently, $engine could be:
     *
     * - CRYPT_ENGINE_OPENSSL  [very fast]
     *
     * - CRYPT_ENGINE_MCRYPT   [fast]
     *
     * - CRYPT_ENGINE_INTERNAL [slow]
     *
     * If the preferred crypt engine is not available the fastest available one will be used
     *
     * @see self::Crypt_Base()
     * @param int $engine
     * @access public
     */
    function setPreferredEngine($engine)
    {
        switch ($engine) {
            //case CRYPT_ENGINE_OPENSSL:
            case CRYPT_ENGINE_MCRYPT:
            case CRYPT_ENGINE_INTERNAL:
                $this->preferredEngine = $engine;
                break;
            default:
                $this->preferredEngine = CRYPT_ENGINE_OPENSSL;
        }

        $this->_setEngine();
    }

    /**
     * Returns the engine currently being utilized
     *
     * @see self::_setEngine()
     * @access public
     */
    function getEngine()
    {
        return $this->engine;
    }

    /**
     * Sets the engine as appropriate
     *
     * @see self::Crypt_Base()
     * @access private
     */
    function _setEngine()
    {
        $this->engine = null;

        $candidateEngines = array(
            $this->preferredEngine,
            CRYPT_ENGINE_OPENSSL,
            CRYPT_ENGINE_MCRYPT
        );
        foreach ($candidateEngines as $engine) {
            if ($this->isValidEngine($engine)) {
                $this->engine = $engine;
                break;
            }
        }
        if (!$this->engine) {
            $this->engine = CRYPT_ENGINE_INTERNAL;
        }

        if ($this->engine != CRYPT_ENGINE_MCRYPT && $this->enmcrypt) {
            // Closing the current mcrypt resource(s). _mcryptSetup() will, if needed,
            // (re)open them with the module named in $this->cipher_name_mcrypt
            @mcrypt_module_close($this->enmcrypt);
            @mcrypt_module_close($this->demcrypt);
            $this->enmcrypt = null;
            $this->demcrypt = null;

            if ($this->ecb) {
                @mcrypt_module_close($this->ecb);
                $this->ecb = null;
            }
        }

        $this->changed = true;
    }

    /**
     * Encrypts a block
     *
     * @access private
     * @param string $in
     * @return string
     * @internal Must be extended by the child Crypt_* class
     */
    function _encryptBlock($in)
    {
        user_error((version_compare(PHP_VERSION, '5.0.0', '>=')  ? __METHOD__ : __FUNCTION__)  . '() must extend by class ' . get_class($this), E_USER_ERROR);
    }

    /**
     * Decrypts a block
     *
     * @access private
     * @param string $in
     * @return string
     * @internal Must be extended by the child Crypt_* class
     */
    function _decryptBlock($in)
    {
        user_error((version_compare(PHP_VERSION, '5.0.0', '>=')  ? __METHOD__ : __FUNCTION__)  . '() must extend by class ' . get_class($this), E_USER_ERROR);
    }

    /**
     * Setup the key (expansion)
     *
     * Only used if $engine == CRYPT_ENGINE_INTERNAL
     *
     * @see self::_setup()
     * @access private
     * @internal Must be extended by the child Crypt_* class
     */
    function _setupKey()
    {
        user_error((version_compare(PHP_VERSION, '5.0.0', '>=')  ? __METHOD__ : __FUNCTION__)  . '() must extend by class ' . get_class($this), E_USER_ERROR);
    }

    /**
     * Setup the CRYPT_ENGINE_INTERNAL $engine
     *
     * (re)init, if necessary, the internal cipher $engine and flush all $buffers
     * Used (only) if $engine == CRYPT_ENGINE_INTERNAL
     *
     * _setup() will be called each time if $changed === true
     * typically this happens when using one or more of following public methods:
     *
     * - setKey()
     *
     * - setIV()
     *
     * - disableContinuousBuffer()
     *
     * - First run of encrypt() / decrypt() with no init-settings
     *
     * @see self::setKey()
     * @see self::setIV()
     * @see self::disableContinuousBuffer()
     * @access private
     * @internal _setup() is always called before en/decryption.
     * @internal Could, but not must, extend by the child Crypt_* class
     */
    function _setup()
    {
        $this->_clearBuffers();
        $this->_setupKey();

        if ($this->use_inline_crypt) {
            $this->_setupInlineCrypt();
        }
    }

    /**
     * Setup the CRYPT_ENGINE_MCRYPT $engine
     *
     * (re)init, if necessary, the (ext)mcrypt resources and flush all $buffers
     * Used (only) if $engine = CRYPT_ENGINE_MCRYPT
     *
     * _setupMcrypt() will be called each time if $changed === true
     * typically this happens when using one or more of following public methods:
     *
     * - setKey()
     *
     * - setIV()
     *
     * - disableContinuousBuffer()
     *
     * - First run of encrypt() / decrypt()
     *
     * @see self::setKey()
     * @see self::setIV()
     * @see self::disableContinuousBuffer()
     * @access private
     * @internal Could, but not must, extend by the child Crypt_* class
     */
    function _setupMcrypt()
    {
        $this->_clearBuffers();
        $this->enchanged = $this->dechanged = true;

        if (!isset($this->enmcrypt)) {
            static $mcrypt_modes = array(
                CRYPT_MODE_CTR    => 'ctr',
                CRYPT_MODE_ECB    => MCRYPT_MODE_ECB,
                CRYPT_MODE_CBC    => MCRYPT_MODE_CBC,
                CRYPT_MODE_CFB    => 'ncfb',
                CRYPT_MODE_OFB    => MCRYPT_MODE_NOFB,
                CRYPT_MODE_STREAM => MCRYPT_MODE_STREAM,
            );

            $this->demcrypt = @mcrypt_module_open($this->cipher_name_mcrypt, '', $mcrypt_modes[$this->mode], '');
            $this->enmcrypt = @mcrypt_module_open($this->cipher_name_mcrypt, '', $mcrypt_modes[$this->mode], '');

            // we need the $ecb mcrypt resource (only) in MODE_CFB with enableContinuousBuffer()
            // to workaround mcrypt's broken ncfb implementation in buffered mode
            // see: {@link http://phpseclib.sourceforge.net/cfb-demo.phps}
            if ($this->mode == CRYPT_MODE_CFB) {
                $this->ecb = @mcrypt_module_open($this->cipher_name_mcrypt, '', MCRYPT_MODE_ECB, '');
            }
        } // else should mcrypt_generic_deinit be called?

        if ($this->mode == CRYPT_MODE_CFB) {
            @mcrypt_generic_init($this->ecb, $this->key, str_repeat("\0", $this->block_size));
        }
    }

    /**
     * Pads a string
     *
     * Pads a string using the RSA PKCS padding standards so that its length is a multiple of the blocksize.
     * $this->block_size - (strlen($text) % $this->block_size) bytes are added, each of which is equal to
     * chr($this->block_size - (strlen($text) % $this->block_size)
     *
     * If padding is disabled and $text is not a multiple of the blocksize, the string will be padded regardless
     * and padding will, hence forth, be enabled.
     *
     * @see self::_unpad()
     * @param string $text
     * @access private
     * @return string
     */
    function _pad($text)
    {
        $length = strlen($text);

        if (!$this->padding) {
            if ($length % $this->block_size == 0) {
                return $text;
            } else {
                user_error("The plaintext's length ($length) is not a multiple of the block size ({$this->block_size})");
                $this->padding = true;
            }
        }

        $pad = $this->block_size - ($length % $this->block_size);

        return str_pad($text, $length + $pad, chr($pad));
    }

    /**
     * Unpads a string.
     *
     * If padding is enabled and the reported padding length is invalid the encryption key will be assumed to be wrong
     * and false will be returned.
     *
     * @see self::_pad()
     * @param string $text
     * @access private
     * @return string
     */
    function _unpad($text)
    {
        if (!$this->padding) {
            return $text;
        }

        $length = ord($text[strlen($text) - 1]);

        if (!$length || $length > $this->block_size) {
            return false;
        }

        return substr($text, 0, -$length);
    }

    /**
     * Clears internal buffers
     *
     * Clearing/resetting the internal buffers is done everytime
     * after disableContinuousBuffer() or on cipher $engine (re)init
     * ie after setKey() or setIV()
     *
     * @access public
     * @internal Could, but not must, extend by the child Crypt_* class
     */
    function _clearBuffers()
    {
        $this->enbuffer = $this->debuffer = array('ciphertext' => '', 'xor' => '', 'pos' => 0, 'enmcrypt_init' => true);

        // mcrypt's handling of invalid's $iv:
        // $this->encryptIV = $this->decryptIV = strlen($this->iv) == $this->block_size ? $this->iv : str_repeat("\0", $this->block_size);
        $this->encryptIV = $this->decryptIV = str_pad(substr($this->iv, 0, $this->block_size), $this->block_size, "\0");

        if (!$this->skip_key_adjustment) {
            $this->key = str_pad(substr($this->key, 0, $this->key_length), $this->key_length, "\0");
        }
    }

    /**
     * String Shift
     *
     * Inspired by array_shift
     *
     * @param string $string
     * @param int $index
     * @access private
     * @return string
     */
    function _string_shift(&$string, $index = 1)
    {
        $substr = substr($string, 0, $index);
        $string = substr($string, $index);
        return $substr;
    }

    /**
     * String Pop
     *
     * Inspired by array_pop
     *
     * @param string $string
     * @param int $index
     * @access private
     * @return string
     */
    function _string_pop(&$string, $index = 1)
    {
        $substr = substr($string, -$index);
        $string = substr($string, 0, -$index);
        return $substr;
    }

    /**
     * Increment the current string
     *
     * @see self::decrypt()
     * @see self::encrypt()
     * @param string $var
     * @access private
     */
    function _increment_str(&$var)
    {
        for ($i = 4; $i <= strlen($var); $i+= 4) {
            $temp = substr($var, -$i, 4);
            switch ($temp) {
                case "\xFF\xFF\xFF\xFF":
                    $var = substr_replace($var, "\x00\x00\x00\x00", -$i, 4);
                    break;
                case "\x7F\xFF\xFF\xFF":
                    $var = substr_replace($var, "\x80\x00\x00\x00", -$i, 4);
                    return;
                default:
                    $temp = unpack('Nnum', $temp);
                    $var = substr_replace($var, pack('N', $temp['num'] + 1), -$i, 4);
                    return;
            }
        }

        $remainder = strlen($var) % 4;

        if ($remainder == 0) {
            return;
        }

        $temp = unpack('Nnum', str_pad(substr($var, 0, $remainder), 4, "\0", STR_PAD_LEFT));
        $temp = substr(pack('N', $temp['num'] + 1), -$remainder);
        $var = substr_replace($var, $temp, 0, $remainder);
    }

    /**
     * Setup the performance-optimized function for de/encrypt()
     *
     * Stores the created (or existing) callback function-name
     * in $this->inline_crypt
     *
     * Internally for phpseclib developers:
     *
     *     _setupInlineCrypt() would be called only if:
     *
     *     - $engine == CRYPT_ENGINE_INTERNAL and
     *
     *     - $use_inline_crypt === true
     *
     *     - each time on _setup(), after(!) _setupKey()
     *
     *
     *     This ensures that _setupInlineCrypt() has always a
     *     full ready2go initializated internal cipher $engine state
     *     where, for example, the keys allready expanded,
     *     keys/block_size calculated and such.
     *
     *     It is, each time if called, the responsibility of _setupInlineCrypt():
     *
     *     - to set $this->inline_crypt to a valid and fully working callback function
     *       as a (faster) replacement for encrypt() / decrypt()
     *
     *     - NOT to create unlimited callback functions (for memory reasons!)
     *       no matter how often _setupInlineCrypt() would be called. At some
     *       point of amount they must be generic re-useable.
     *
     *     - the code of _setupInlineCrypt() it self,
     *       and the generated callback code,
     *       must be, in following order:
     *       - 100% safe
     *       - 100% compatible to encrypt()/decrypt()
     *       - using only php5+ features/lang-constructs/php-extensions if
     *         compatibility (down to php4) or fallback is provided
     *       - readable/maintainable/understandable/commented and... not-cryptic-styled-code :-)
     *       - >= 10% faster than encrypt()/decrypt() [which is, by the way,
     *         the reason for the existence of _setupInlineCrypt() :-)]
     *       - memory-nice
     *       - short (as good as possible)
     *
     * Note: - _setupInlineCrypt() is using _createInlineCryptFunction() to create the full callback function code.
     *       - In case of using inline crypting, _setupInlineCrypt() must extend by the child Crypt_* class.
     *       - The following variable names are reserved:
     *         - $_*  (all variable names prefixed with an underscore)
     *         - $self (object reference to it self. Do not use $this, but $self instead)
     *         - $in (the content of $in has to en/decrypt by the generated code)
     *       - The callback function should not use the 'return' statement, but en/decrypt'ing the content of $in only
     *
     *
     * @see self::_setup()
     * @see self::_createInlineCryptFunction()
     * @see self::encrypt()
     * @see self::decrypt()
     * @access private
     * @internal If a Crypt_* class providing inline crypting it must extend _setupInlineCrypt()
     */
    function _setupInlineCrypt()
    {
        // If, for any reason, an extending Crypt_Base() Crypt_* class
        // not using inline crypting then it must be ensured that: $this->use_inline_crypt = false
        // ie in the class var declaration of $use_inline_crypt in general for the Crypt_* class,
        // in the constructor at object instance-time
        // or, if it's runtime-specific, at runtime

        $this->use_inline_crypt = false;
    }

    /**
     * Creates the performance-optimized function for en/decrypt()
     *
     * Internally for phpseclib developers:
     *
     *    _createInlineCryptFunction():
     *
     *    - merge the $cipher_code [setup'ed by _setupInlineCrypt()]
     *      with the current [$this->]mode of operation code
     *
     *    - create the $inline function, which called by encrypt() / decrypt()
     *      as its replacement to speed up the en/decryption operations.
     *
     *    - return the name of the created $inline callback function
     *
     *    - used to speed up en/decryption
     *
     *
     *
     *    The main reason why can speed up things [up to 50%] this way are:
     *
     *    - using variables more effective then regular.
     *      (ie no use of expensive arrays but integers $k_0, $k_1 ...
     *      or even, for example, the pure $key[] values hardcoded)
     *
     *    - avoiding 1000's of function calls of ie _encryptBlock()
     *      but inlining the crypt operations.
     *      in the mode of operation for() loop.
     *
     *    - full loop unroll the (sometimes key-dependent) rounds
     *      avoiding this way ++$i counters and runtime-if's etc...
     *
     *    The basic code architectur of the generated $inline en/decrypt()
     *    lambda function, in pseudo php, is:
     *
     *    <code>
     *    +----------------------------------------------------------------------------------------------+
     *    | callback $inline = create_function:                                                          |
     *    | lambda_function_0001_crypt_ECB($action, $text)                                               |
     *    | {                                                                                            |
     *    |     INSERT PHP CODE OF:                                                                      |
     *    |     $cipher_code['init_crypt'];                  // general init code.                       |
     *    |                                                  // ie: $sbox'es declarations used for       |
     *    |                                                  //     encrypt and decrypt'ing.             |
     *    |                                                                                              |
     *    |     switch ($action) {                                                                       |
     *    |         case 'encrypt':                                                                      |
     *    |             INSERT PHP CODE OF:                                                              |
     *    |             $cipher_code['init_encrypt'];       // encrypt sepcific init code.               |
     *    |                                                    ie: specified $key or $box                |
     *    |                                                        declarations for encrypt'ing.         |
     *    |                                                                                              |
     *    |             foreach ($ciphertext) {                                                          |
     *    |                 $in = $block_size of $ciphertext;                                            |
     *    |                                                                                              |
     *    |                 INSERT PHP CODE OF:                                                          |
     *    |                 $cipher_code['encrypt_block'];  // encrypt's (string) $in, which is always:  |
     *    |                                                 // strlen($in) == $this->block_size          |
     *    |                                                 // here comes the cipher algorithm in action |
     *    |                                                 // for encryption.                           |
     *    |                                                 // $cipher_code['encrypt_block'] has to      |
     *    |                                                 // encrypt the content of the $in variable   |
     *    |                                                                                              |
     *    |                 $plaintext .= $in;                                                           |
     *    |             }                                                                                |
     *    |             return $plaintext;                                                               |
     *    |                                                                                              |
     *    |         case 'decrypt':                                                                      |
     *    |             INSERT PHP CODE OF:                                                              |
     *    |             $cipher_code['init_decrypt'];       // decrypt sepcific init code                |
     *    |                                                    ie: specified $key or $box                |
     *    |                                                        declarations for decrypt'ing.         |
     *    |             foreach ($plaintext) {                                                           |
     *    |                 $in = $block_size of $plaintext;                                             |
     *    |                                                                                              |
     *    |                 INSERT PHP CODE OF:                                                          |
     *    |                 $cipher_code['decrypt_block'];  // decrypt's (string) $in, which is always   |
     *    |                                                 // strlen($in) == $this->block_size          |
     *    |                                                 // here comes the cipher algorithm in action |
     *    |                                                 // for decryption.                           |
     *    |                                                 // $cipher_code['decrypt_block'] has to      |
     *    |                                                 // decrypt the content of the $in variable   |
     *    |                 $ciphertext .= $in;                                                          |
     *    |             }                                                                                |
     *    |             return $ciphertext;                                                              |
     *    |     }                                                                                        |
     *    | }                                                                                            |
     *    +----------------------------------------------------------------------------------------------+
     *    </code>
     *
     *    See also the Crypt_*::_setupInlineCrypt()'s for
     *    productive inline $cipher_code's how they works.
     *
     *    Structure of:
     *    <code>
     *    $cipher_code = array(
     *        'init_crypt'    => (string) '', // optional
     *        'init_encrypt'  => (string) '', // optional
     *        'init_decrypt'  => (string) '', // optional
     *        'encrypt_block' => (string) '', // required
     *        'decrypt_block' => (string) ''  // required
     *    );
     *    </code>
     *
     * @see self::_setupInlineCrypt()
     * @see self::encrypt()
     * @see self::decrypt()
     * @param array $cipher_code
     * @access private
     * @return string (the name of the created callback function)
     */
    function _createInlineCryptFunction($cipher_code)
    {
        $block_size = $this->block_size;

        // optional
        $init_crypt    = isset($cipher_code['init_crypt'])    ? $cipher_code['init_crypt']    : '';
        $init_encrypt  = isset($cipher_code['init_encrypt'])  ? $cipher_code['init_encrypt']  : '';
        $init_decrypt  = isset($cipher_code['init_decrypt'])  ? $cipher_code['init_decrypt']  : '';
        // required
        $encrypt_block = $cipher_code['encrypt_block'];
        $decrypt_block = $cipher_code['decrypt_block'];

        // Generating mode of operation inline code,
        // merged with the $cipher_code algorithm
        // for encrypt- and decryption.
        switch ($this->mode) {
            case CRYPT_MODE_ECB:
                $encrypt = $init_encrypt . '
                    $_ciphertext = "";
                    $_plaintext_len = strlen($_text);

                    for ($_i = 0; $_i < $_plaintext_len; $_i+= '.$block_size.') {
                        $in = substr($_text, $_i, '.$block_size.');
                        '.$encrypt_block.'
                        $_ciphertext.= $in;
                    }

                    return $_ciphertext;
                    ';

                $decrypt = $init_decrypt . '
                    $_plaintext = "";
                    $_text = str_pad($_text, strlen($_text) + ('.$block_size.' - strlen($_text) % '.$block_size.') % '.$block_size.', chr(0));
                    $_ciphertext_len = strlen($_text);

                    for ($_i = 0; $_i < $_ciphertext_len; $_i+= '.$block_size.') {
                        $in = substr($_text, $_i, '.$block_size.');
                        '.$decrypt_block.'
                        $_plaintext.= $in;
                    }

                    return $self->_unpad($_plaintext);
                    ';
                break;
            case CRYPT_MODE_CTR:
                $encrypt = $init_encrypt . '
                    $_ciphertext = "";
                    $_plaintext_len = strlen($_text);
                    $_xor = $self->encryptIV;
                    $_buffer = &$self->enbuffer;
                    if (strlen($_buffer["ciphertext"])) {
                        for ($_i = 0; $_i < $_plaintext_len; $_i+= '.$block_size.') {
                            $_block = substr($_text, $_i, '.$block_size.');
                            if (strlen($_block) > strlen($_buffer["ciphertext"])) {
                                $in = $_xor;
                                '.$encrypt_block.'
                                $self->_increment_str($_xor);
                                $_buffer["ciphertext"].= $in;
                            }
                            $_key = $self->_string_shift($_buffer["ciphertext"], '.$block_size.');
                            $_ciphertext.= $_block ^ $_key;
                        }
                    } else {
                        for ($_i = 0; $_i < $_plaintext_len; $_i+= '.$block_size.') {
                            $_block = substr($_text, $_i, '.$block_size.');
                            $in = $_xor;
                            '.$encrypt_block.'
                            $self->_increment_str($_xor);
                            $_key = $in;
                            $_ciphertext.= $_block ^ $_key;
                        }
                    }
                    if ($self->continuousBuffer) {
                        $self->encryptIV = $_xor;
                        if ($_start = $_plaintext_len % '.$block_size.') {
                            $_buffer["ciphertext"] = substr($_key, $_start) . $_buffer["ciphertext"];
                        }
                    }

                    return $_ciphertext;
                ';

                $decrypt = $init_encrypt . '
                    $_plaintext = "";
                    $_ciphertext_len = strlen($_text);
                    $_xor = $self->decryptIV;
                    $_buffer = &$self->debuffer;

                    if (strlen($_buffer["ciphertext"])) {
                        for ($_i = 0; $_i < $_ciphertext_len; $_i+= '.$block_size.') {
                            $_block = substr($_text, $_i, '.$block_size.');
                            if (strlen($_block) > strlen($_buffer["ciphertext"])) {
                                $in = $_xor;
                                '.$encrypt_block.'
                                $self->_increment_str($_xor);
                                $_buffer["ciphertext"].= $in;
                            }
                            $_key = $self->_string_shift($_buffer["ciphertext"], '.$block_size.');
                            $_plaintext.= $_block ^ $_key;
                        }
                    } else {
                        for ($_i = 0; $_i < $_ciphertext_len; $_i+= '.$block_size.') {
                            $_block = substr($_text, $_i, '.$block_size.');
                            $in = $_xor;
                            '.$encrypt_block.'
                            $self->_increment_str($_xor);
                            $_key = $in;
                            $_plaintext.= $_block ^ $_key;
                        }
                    }
                    if ($self->continuousBuffer) {
                        $self->decryptIV = $_xor;
                        if ($_start = $_ciphertext_len % '.$block_size.') {
                            $_buffer["ciphertext"] = substr($_key, $_start) . $_buffer["ciphertext"];
                        }
                    }

                    return $_plaintext;
                    ';
                break;
            case CRYPT_MODE_CFB:
                $encrypt = $init_encrypt . '
                    $_ciphertext = "";
                    $_buffer = &$self->enbuffer;

                    if ($self->continuousBuffer) {
                        $_iv = &$self->encryptIV;
                        $_pos = &$_buffer["pos"];
                    } else {
                        $_iv = $self->encryptIV;
                        $_pos = 0;
                    }
                    $_len = strlen($_text);
                    $_i = 0;
                    if ($_pos) {
                        $_orig_pos = $_pos;
                        $_max = '.$block_size.' - $_pos;
                        if ($_len >= $_max) {
                            $_i = $_max;
                            $_len-= $_max;
                            $_pos = 0;
                        } else {
                            $_i = $_len;
                            $_pos+= $_len;
                            $_len = 0;
                        }
                        $_ciphertext = substr($_iv, $_orig_pos) ^ $_text;
                        $_iv = substr_replace($_iv, $_ciphertext, $_orig_pos, $_i);
                    }
                    while ($_len >= '.$block_size.') {
                        $in = $_iv;
                        '.$encrypt_block.';
                        $_iv = $in ^ substr($_text, $_i, '.$block_size.');
                        $_ciphertext.= $_iv;
                        $_len-= '.$block_size.';
                        $_i+= '.$block_size.';
                    }
                    if ($_len) {
                        $in = $_iv;
                        '.$encrypt_block.'
                        $_iv = $in;
                        $_block = $_iv ^ substr($_text, $_i);
                        $_iv = substr_replace($_iv, $_block, 0, $_len);
                        $_ciphertext.= $_block;
                        $_pos = $_len;
                    }
                    return $_ciphertext;
                ';

                $decrypt = $init_encrypt . '
                    $_plaintext = "";
                    $_buffer = &$self->debuffer;

                    if ($self->continuousBuffer) {
                        $_iv = &$self->decryptIV;
                        $_pos = &$_buffer["pos"];
                    } else {
                        $_iv = $self->decryptIV;
                        $_pos = 0;
                    }
                    $_len = strlen($_text);
                    $_i = 0;
                    if ($_pos) {
                        $_orig_pos = $_pos;
                        $_max = '.$block_size.' - $_pos;
                        if ($_len >= $_max) {
                            $_i = $_max;
                            $_len-= $_max;
                            $_pos = 0;
                        } else {
                            $_i = $_len;
                            $_pos+= $_len;
                            $_len = 0;
                        }
                        $_plaintext = substr($_iv, $_orig_pos) ^ $_text;
                        $_iv = substr_replace($_iv, substr($_text, 0, $_i), $_orig_pos, $_i);
                    }
                    while ($_len >= '.$block_size.') {
                        $in = $_iv;
                        '.$encrypt_block.'
                        $_iv = $in;
                        $cb = substr($_text, $_i, '.$block_size.');
                        $_plaintext.= $_iv ^ $cb;
                        $_iv = $cb;
                        $_len-= '.$block_size.';
                        $_i+= '.$block_size.';
                    }
                    if ($_len) {
                        $in = $_iv;
                        '.$encrypt_block.'
                        $_iv = $in;
                        $_plaintext.= $_iv ^ substr($_text, $_i);
                        $_iv = substr_replace($_iv, substr($_text, $_i), 0, $_len);
                        $_pos = $_len;
                    }

                    return $_plaintext;
                    ';
                break;
            case CRYPT_MODE_OFB:
                $encrypt = $init_encrypt . '
                    $_ciphertext = "";
                    $_plaintext_len = strlen($_text);
                    $_xor = $self->encryptIV;
                    $_buffer = &$self->enbuffer;

                    if (strlen($_buffer["xor"])) {
                        for ($_i = 0; $_i < $_plaintext_len; $_i+= '.$block_size.') {
                            $_block = substr($_text, $_i, '.$block_size.');
                            if (strlen($_block) > strlen($_buffer["xor"])) {
                                $in = $_xor;
                                '.$encrypt_block.'
                                $_xor = $in;
                                $_buffer["xor"].= $_xor;
                            }
                            $_key = $self->_string_shift($_buffer["xor"], '.$block_size.');
                            $_ciphertext.= $_block ^ $_key;
                        }
                    } else {
                        for ($_i = 0; $_i < $_plaintext_len; $_i+= '.$block_size.') {
                            $in = $_xor;
                            '.$encrypt_block.'
                            $_xor = $in;
                            $_ciphertext.= substr($_text, $_i, '.$block_size.') ^ $_xor;
                        }
                        $_key = $_xor;
                    }
                    if ($self->continuousBuffer) {
                        $self->encryptIV = $_xor;
                        if ($_start = $_plaintext_len % '.$block_size.') {
                             $_buffer["xor"] = substr($_key, $_start) . $_buffer["xor"];
                        }
                    }
                    return $_ciphertext;
                    ';

                $decrypt = $init_encrypt . '
                    $_plaintext = "";
                    $_ciphertext_len = strlen($_text);
                    $_xor = $self->decryptIV;
                    $_buffer = &$self->debuffer;

                    if (strlen($_buffer["xor"])) {
                        for ($_i = 0; $_i < $_ciphertext_len; $_i+= '.$block_size.') {
                            $_block = substr($_text, $_i, '.$block_size.');
                            if (strlen($_block) > strlen($_buffer["xor"])) {
                                $in = $_xor;
                                '.$encrypt_block.'
                                $_xor = $in;
                                $_buffer["xor"].= $_xor;
                            }
                            $_key = $self->_string_shift($_buffer["xor"], '.$block_size.');
                            $_plaintext.= $_block ^ $_key;
                        }
                    } else {
                        for ($_i = 0; $_i < $_ciphertext_len; $_i+= '.$block_size.') {
                            $in = $_xor;
                            '.$encrypt_block.'
                            $_xor = $in;
                            $_plaintext.= substr($_text, $_i, '.$block_size.') ^ $_xor;
                        }
                        $_key = $_xor;
                    }
                    if ($self->continuousBuffer) {
                        $self->decryptIV = $_xor;
                        if ($_start = $_ciphertext_len % '.$block_size.') {
                             $_buffer["xor"] = substr($_key, $_start) . $_buffer["xor"];
                        }
                    }
                    return $_plaintext;
                    ';
                break;
            case CRYPT_MODE_STREAM:
                $encrypt = $init_encrypt . '
                    $_ciphertext = "";
                    '.$encrypt_block.'
                    return $_ciphertext;
                    ';
                $decrypt = $init_decrypt . '
                    $_plaintext = "";
                    '.$decrypt_block.'
                    return $_plaintext;
                    ';
                break;
            // case CRYPT_MODE_CBC:
            default:
                $encrypt = $init_encrypt . '
                    $_ciphertext = "";
                    $_plaintext_len = strlen($_text);

                    $in = $self->encryptIV;

                    for ($_i = 0; $_i < $_plaintext_len; $_i+= '.$block_size.') {
                        $in = substr($_text, $_i, '.$block_size.') ^ $in;
                        '.$encrypt_block.'
                        $_ciphertext.= $in;
                    }

                    if ($self->continuousBuffer) {
                        $self->encryptIV = $in;
                    }

                    return $_ciphertext;
                    ';

                $decrypt = $init_decrypt . '
                    $_plaintext = "";
                    $_text = str_pad($_text, strlen($_text) + ('.$block_size.' - strlen($_text) % '.$block_size.') % '.$block_size.', chr(0));
                    $_ciphertext_len = strlen($_text);

                    $_iv = $self->decryptIV;

                    for ($_i = 0; $_i < $_ciphertext_len; $_i+= '.$block_size.') {
                        $in = $_block = substr($_text, $_i, '.$block_size.');
                        '.$decrypt_block.'
                        $_plaintext.= $in ^ $_iv;
                        $_iv = $_block;
                    }

                    if ($self->continuousBuffer) {
                        $self->decryptIV = $_iv;
                    }

                    return $self->_unpad($_plaintext);
                    ';
                break;
        }

        // Create the $inline function and return its name as string. Ready to run!
        if (version_compare(PHP_VERSION, '5.3.0') >= 0) {
            eval('$func = function ($_action, &$self, $_text) { ' . $init_crypt . 'if ($_action == "encrypt") { ' . $encrypt . ' } else { ' . $decrypt . ' } };');
            return $func;
        }

        return create_function('$_action, &$self, $_text', $init_crypt . 'if ($_action == "encrypt") { ' . $encrypt . ' } else { ' . $decrypt . ' }');
    }

    /**
     * Holds the lambda_functions table (classwide)
     *
     * Each name of the lambda function, created from
     * _setupInlineCrypt() && _createInlineCryptFunction()
     * is stored, classwide (!), here for reusing.
     *
     * The string-based index of $function is a classwide
     * unique value representing, at least, the $mode of
     * operation (or more... depends of the optimizing level)
     * for which $mode the lambda function was created.
     *
     * @access private
     * @return array &$functions
     */
    function &_getLambdaFunctions()
    {
        static $functions = array();
        return $functions;
    }

    /**
     * Generates a digest from $bytes
     *
     * @see self::_setupInlineCrypt()
     * @access private
     * @param $bytes
     * @return string
     */
    function _hashInlineCryptFunction($bytes)
    {
        if (!defined('CRYPT_BASE_WHIRLPOOL_AVAILABLE')) {
            define('CRYPT_BASE_WHIRLPOOL_AVAILABLE', (bool)(extension_loaded('hash') && in_array('whirlpool', hash_algos())));
        }

        $result = '';
        $hash = $bytes;

        switch (true) {
            case CRYPT_BASE_WHIRLPOOL_AVAILABLE:
                foreach (str_split($bytes, 64) as $t) {
                    $hash = hash('whirlpool', $hash, true);
                    $result .= $t ^ $hash;
                }
                return $result . hash('whirlpool', $hash, true);
            default:
                $len = strlen($bytes);
                for ($i = 0; $i < $len; $i+=20) {
                    $t = substr($bytes, $i, 20);
                    $hash = pack('H*', sha1($hash));
                    $result .= $t ^ $hash;
                }
                return $result . pack('H*', sha1($hash));
        }
    }

    /**
     * Convert float to int
     *
     * On 32-bit Linux installs running PHP < 5.3 converting floats to ints doesn't always work
     *
     * @access private
     * @param string $x
     * @return int
     */
    function safe_intval($x)
    {
        switch (true) {
            case is_int($x):
            // PHP 5.3, per http://php.net/releases/5_3_0.php, introduced "more consistent float rounding"
            case version_compare(PHP_VERSION, '5.3.0') >= 0 && (php_uname('m') & "\xDF\xDF\xDF") != 'ARM':
            // PHP_OS & "\xDF\xDF\xDF" == strtoupper(substr(PHP_OS, 0, 3)), but a lot faster
            case (PHP_OS & "\xDF\xDF\xDF") === 'WIN':
                return $x;
        }
        return (fmod($x, 0x80000000) & 0x7FFFFFFF) |
            ((fmod(floor($x / 0x80000000), 2) & 1) << 31);
    }

    /**
     * eval()'able string for in-line float to int
     *
     * @access private
     * @return string
     */
    function safe_intval_inline()
    {
        // on 32-bit linux systems with PHP < 5.3 float to integer conversion is bad
        switch (true) {
            case defined('PHP_INT_SIZE') && PHP_INT_SIZE == 8:
            case version_compare(PHP_VERSION, '5.3.0') >= 0 && (php_uname('m') & "\xDF\xDF\xDF") != 'ARM':
            case (PHP_OS & "\xDF\xDF\xDF") === 'WIN':
                return '%s';
                break;
            default:
                $safeint = '(is_int($temp = %s) ? $temp : (fmod($temp, 0x80000000) & 0x7FFFFFFF) | ';
                return $safeint . '((fmod(floor($temp / 0x80000000), 2) & 1) << 31))';
        }
    }
}
vendor/phpseclib/phpseclib/phpseclib/Crypt/DES.php000064400000220472151327705700016177 0ustar00<?php

/**
 * Pure-PHP implementation of DES.
 *
 * Uses mcrypt, if available, and an internal implementation, otherwise.
 *
 * PHP versions 4 and 5
 *
 * Useful resources are as follows:
 *
 *  - {@link http://en.wikipedia.org/wiki/DES_supplementary_material Wikipedia: DES supplementary material}
 *  - {@link http://www.itl.nist.gov/fipspubs/fip46-2.htm FIPS 46-2 - (DES), Data Encryption Standard}
 *  - {@link http://www.cs.eku.edu/faculty/styer/460/Encrypt/JS-DES.html JavaScript DES Example}
 *
 * Here's a short example of how to use this library:
 * <code>
 * <?php
 *    include 'Crypt/DES.php';
 *
 *    $des = new Crypt_DES();
 *
 *    $des->setKey('abcdefgh');
 *
 *    $size = 10 * 1024;
 *    $plaintext = '';
 *    for ($i = 0; $i < $size; $i++) {
 *        $plaintext.= 'a';
 *    }
 *
 *    echo $des->decrypt($des->encrypt($plaintext));
 * ?>
 * </code>
 *
 * LICENSE: Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 *
 * @category  Crypt
 * @package   Crypt_DES
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2007 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      http://phpseclib.sourceforge.net
 */

/**
 * Include Crypt_Base
 *
 * Base cipher class
 */
if (!class_exists('Crypt_Base')) {
    include_once 'Base.php';
}

/**#@+
 * @access private
 * @see self::_setupKey()
 * @see self::_processBlock()
 */
/**
 * Contains $keys[CRYPT_DES_ENCRYPT]
 */
define('CRYPT_DES_ENCRYPT', 0);
/**
 * Contains $keys[CRYPT_DES_DECRYPT]
 */
define('CRYPT_DES_DECRYPT', 1);
/**#@-*/

/**#@+
 * @access public
 * @see self::encrypt()
 * @see self::decrypt()
 */
/**
 * Encrypt / decrypt using the Counter mode.
 *
 * Set to -1 since that's what Crypt/Random.php uses to index the CTR mode.
 *
 * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Counter_.28CTR.29
 */
define('CRYPT_DES_MODE_CTR', CRYPT_MODE_CTR);
/**
 * Encrypt / decrypt using the Electronic Code Book mode.
 *
 * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Electronic_codebook_.28ECB.29
 */
define('CRYPT_DES_MODE_ECB', CRYPT_MODE_ECB);
/**
 * Encrypt / decrypt using the Code Book Chaining mode.
 *
 * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Cipher-block_chaining_.28CBC.29
 */
define('CRYPT_DES_MODE_CBC', CRYPT_MODE_CBC);
/**
 * Encrypt / decrypt using the Cipher Feedback mode.
 *
 * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Cipher_feedback_.28CFB.29
 */
define('CRYPT_DES_MODE_CFB', CRYPT_MODE_CFB);
/**
 * Encrypt / decrypt using the Cipher Feedback mode.
 *
 * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Output_feedback_.28OFB.29
 */
define('CRYPT_DES_MODE_OFB', CRYPT_MODE_OFB);
/**#@-*/

/**
 * Pure-PHP implementation of DES.
 *
 * @package Crypt_DES
 * @author  Jim Wigginton <terrafrost@php.net>
 * @access  public
 */
class Crypt_DES extends Crypt_Base
{
    /**
     * Block Length of the cipher
     *
     * @see Crypt_Base::block_size
     * @var int
     * @access private
     */
    var $block_size = 8;

    /**
     * Key Length (in bytes)
     *
     * @see Crypt_Base::setKeyLength()
     * @var int
     * @access private
     */
    var $key_length = 8;

    /**
     * The namespace used by the cipher for its constants.
     *
     * @see Crypt_Base::const_namespace
     * @var string
     * @access private
     */
    var $const_namespace = 'DES';

    /**
     * The mcrypt specific name of the cipher
     *
     * @see Crypt_Base::cipher_name_mcrypt
     * @var string
     * @access private
     */
    var $cipher_name_mcrypt = 'des';

    /**
     * The OpenSSL names of the cipher / modes
     *
     * @see Crypt_Base::openssl_mode_names
     * @var array
     * @access private
     */
    var $openssl_mode_names = array(
        CRYPT_MODE_ECB => 'des-ecb',
        CRYPT_MODE_CBC => 'des-cbc',
        CRYPT_MODE_CFB => 'des-cfb',
        CRYPT_MODE_OFB => 'des-ofb'
        // CRYPT_MODE_CTR is undefined for DES
    );

    /**
     * Optimizing value while CFB-encrypting
     *
     * @see Crypt_Base::cfb_init_len
     * @var int
     * @access private
     */
    var $cfb_init_len = 500;

    /**
     * Switch for DES/3DES encryption
     *
     * Used only if $engine == CRYPT_DES_MODE_INTERNAL
     *
     * @see self::_setupKey()
     * @see self::_processBlock()
     * @var int
     * @access private
     */
    var $des_rounds = 1;

    /**
     * max possible size of $key
     *
     * @see self::setKey()
     * @var string
     * @access private
     */
    var $key_length_max = 8;

    /**
     * The Key Schedule
     *
     * @see self::_setupKey()
     * @var array
     * @access private
     */
    var $keys;

    /**
     * Shuffle table.
     *
     * For each byte value index, the entry holds an 8-byte string
     * with each byte containing all bits in the same state as the
     * corresponding bit in the index value.
     *
     * @see self::_processBlock()
     * @see self::_setupKey()
     * @var array
     * @access private
     */
    var $shuffle = array(
        "\x00\x00\x00\x00\x00\x00\x00\x00", "\x00\x00\x00\x00\x00\x00\x00\xFF",
        "\x00\x00\x00\x00\x00\x00\xFF\x00", "\x00\x00\x00\x00\x00\x00\xFF\xFF",
        "\x00\x00\x00\x00\x00\xFF\x00\x00", "\x00\x00\x00\x00\x00\xFF\x00\xFF",
        "\x00\x00\x00\x00\x00\xFF\xFF\x00", "\x00\x00\x00\x00\x00\xFF\xFF\xFF",
        "\x00\x00\x00\x00\xFF\x00\x00\x00", "\x00\x00\x00\x00\xFF\x00\x00\xFF",
        "\x00\x00\x00\x00\xFF\x00\xFF\x00", "\x00\x00\x00\x00\xFF\x00\xFF\xFF",
        "\x00\x00\x00\x00\xFF\xFF\x00\x00", "\x00\x00\x00\x00\xFF\xFF\x00\xFF",
        "\x00\x00\x00\x00\xFF\xFF\xFF\x00", "\x00\x00\x00\x00\xFF\xFF\xFF\xFF",
        "\x00\x00\x00\xFF\x00\x00\x00\x00", "\x00\x00\x00\xFF\x00\x00\x00\xFF",
        "\x00\x00\x00\xFF\x00\x00\xFF\x00", "\x00\x00\x00\xFF\x00\x00\xFF\xFF",
        "\x00\x00\x00\xFF\x00\xFF\x00\x00", "\x00\x00\x00\xFF\x00\xFF\x00\xFF",
        "\x00\x00\x00\xFF\x00\xFF\xFF\x00", "\x00\x00\x00\xFF\x00\xFF\xFF\xFF",
        "\x00\x00\x00\xFF\xFF\x00\x00\x00", "\x00\x00\x00\xFF\xFF\x00\x00\xFF",
        "\x00\x00\x00\xFF\xFF\x00\xFF\x00", "\x00\x00\x00\xFF\xFF\x00\xFF\xFF",
        "\x00\x00\x00\xFF\xFF\xFF\x00\x00", "\x00\x00\x00\xFF\xFF\xFF\x00\xFF",
        "\x00\x00\x00\xFF\xFF\xFF\xFF\x00", "\x00\x00\x00\xFF\xFF\xFF\xFF\xFF",
        "\x00\x00\xFF\x00\x00\x00\x00\x00", "\x00\x00\xFF\x00\x00\x00\x00\xFF",
        "\x00\x00\xFF\x00\x00\x00\xFF\x00", "\x00\x00\xFF\x00\x00\x00\xFF\xFF",
        "\x00\x00\xFF\x00\x00\xFF\x00\x00", "\x00\x00\xFF\x00\x00\xFF\x00\xFF",
        "\x00\x00\xFF\x00\x00\xFF\xFF\x00", "\x00\x00\xFF\x00\x00\xFF\xFF\xFF",
        "\x00\x00\xFF\x00\xFF\x00\x00\x00", "\x00\x00\xFF\x00\xFF\x00\x00\xFF",
        "\x00\x00\xFF\x00\xFF\x00\xFF\x00", "\x00\x00\xFF\x00\xFF\x00\xFF\xFF",
        "\x00\x00\xFF\x00\xFF\xFF\x00\x00", "\x00\x00\xFF\x00\xFF\xFF\x00\xFF",
        "\x00\x00\xFF\x00\xFF\xFF\xFF\x00", "\x00\x00\xFF\x00\xFF\xFF\xFF\xFF",
        "\x00\x00\xFF\xFF\x00\x00\x00\x00", "\x00\x00\xFF\xFF\x00\x00\x00\xFF",
        "\x00\x00\xFF\xFF\x00\x00\xFF\x00", "\x00\x00\xFF\xFF\x00\x00\xFF\xFF",
        "\x00\x00\xFF\xFF\x00\xFF\x00\x00", "\x00\x00\xFF\xFF\x00\xFF\x00\xFF",
        "\x00\x00\xFF\xFF\x00\xFF\xFF\x00", "\x00\x00\xFF\xFF\x00\xFF\xFF\xFF",
        "\x00\x00\xFF\xFF\xFF\x00\x00\x00", "\x00\x00\xFF\xFF\xFF\x00\x00\xFF",
        "\x00\x00\xFF\xFF\xFF\x00\xFF\x00", "\x00\x00\xFF\xFF\xFF\x00\xFF\xFF",
        "\x00\x00\xFF\xFF\xFF\xFF\x00\x00", "\x00\x00\xFF\xFF\xFF\xFF\x00\xFF",
        "\x00\x00\xFF\xFF\xFF\xFF\xFF\x00", "\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF",
        "\x00\xFF\x00\x00\x00\x00\x00\x00", "\x00\xFF\x00\x00\x00\x00\x00\xFF",
        "\x00\xFF\x00\x00\x00\x00\xFF\x00", "\x00\xFF\x00\x00\x00\x00\xFF\xFF",
        "\x00\xFF\x00\x00\x00\xFF\x00\x00", "\x00\xFF\x00\x00\x00\xFF\x00\xFF",
        "\x00\xFF\x00\x00\x00\xFF\xFF\x00", "\x00\xFF\x00\x00\x00\xFF\xFF\xFF",
        "\x00\xFF\x00\x00\xFF\x00\x00\x00", "\x00\xFF\x00\x00\xFF\x00\x00\xFF",
        "\x00\xFF\x00\x00\xFF\x00\xFF\x00", "\x00\xFF\x00\x00\xFF\x00\xFF\xFF",
        "\x00\xFF\x00\x00\xFF\xFF\x00\x00", "\x00\xFF\x00\x00\xFF\xFF\x00\xFF",
        "\x00\xFF\x00\x00\xFF\xFF\xFF\x00", "\x00\xFF\x00\x00\xFF\xFF\xFF\xFF",
        "\x00\xFF\x00\xFF\x00\x00\x00\x00", "\x00\xFF\x00\xFF\x00\x00\x00\xFF",
        "\x00\xFF\x00\xFF\x00\x00\xFF\x00", "\x00\xFF\x00\xFF\x00\x00\xFF\xFF",
        "\x00\xFF\x00\xFF\x00\xFF\x00\x00", "\x00\xFF\x00\xFF\x00\xFF\x00\xFF",
        "\x00\xFF\x00\xFF\x00\xFF\xFF\x00", "\x00\xFF\x00\xFF\x00\xFF\xFF\xFF",
        "\x00\xFF\x00\xFF\xFF\x00\x00\x00", "\x00\xFF\x00\xFF\xFF\x00\x00\xFF",
        "\x00\xFF\x00\xFF\xFF\x00\xFF\x00", "\x00\xFF\x00\xFF\xFF\x00\xFF\xFF",
        "\x00\xFF\x00\xFF\xFF\xFF\x00\x00", "\x00\xFF\x00\xFF\xFF\xFF\x00\xFF",
        "\x00\xFF\x00\xFF\xFF\xFF\xFF\x00", "\x00\xFF\x00\xFF\xFF\xFF\xFF\xFF",
        "\x00\xFF\xFF\x00\x00\x00\x00\x00", "\x00\xFF\xFF\x00\x00\x00\x00\xFF",
        "\x00\xFF\xFF\x00\x00\x00\xFF\x00", "\x00\xFF\xFF\x00\x00\x00\xFF\xFF",
        "\x00\xFF\xFF\x00\x00\xFF\x00\x00", "\x00\xFF\xFF\x00\x00\xFF\x00\xFF",
        "\x00\xFF\xFF\x00\x00\xFF\xFF\x00", "\x00\xFF\xFF\x00\x00\xFF\xFF\xFF",
        "\x00\xFF\xFF\x00\xFF\x00\x00\x00", "\x00\xFF\xFF\x00\xFF\x00\x00\xFF",
        "\x00\xFF\xFF\x00\xFF\x00\xFF\x00", "\x00\xFF\xFF\x00\xFF\x00\xFF\xFF",
        "\x00\xFF\xFF\x00\xFF\xFF\x00\x00", "\x00\xFF\xFF\x00\xFF\xFF\x00\xFF",
        "\x00\xFF\xFF\x00\xFF\xFF\xFF\x00", "\x00\xFF\xFF\x00\xFF\xFF\xFF\xFF",
        "\x00\xFF\xFF\xFF\x00\x00\x00\x00", "\x00\xFF\xFF\xFF\x00\x00\x00\xFF",
        "\x00\xFF\xFF\xFF\x00\x00\xFF\x00", "\x00\xFF\xFF\xFF\x00\x00\xFF\xFF",
        "\x00\xFF\xFF\xFF\x00\xFF\x00\x00", "\x00\xFF\xFF\xFF\x00\xFF\x00\xFF",
        "\x00\xFF\xFF\xFF\x00\xFF\xFF\x00", "\x00\xFF\xFF\xFF\x00\xFF\xFF\xFF",
        "\x00\xFF\xFF\xFF\xFF\x00\x00\x00", "\x00\xFF\xFF\xFF\xFF\x00\x00\xFF",
        "\x00\xFF\xFF\xFF\xFF\x00\xFF\x00", "\x00\xFF\xFF\xFF\xFF\x00\xFF\xFF",
        "\x00\xFF\xFF\xFF\xFF\xFF\x00\x00", "\x00\xFF\xFF\xFF\xFF\xFF\x00\xFF",
        "\x00\xFF\xFF\xFF\xFF\xFF\xFF\x00", "\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF",
        "\xFF\x00\x00\x00\x00\x00\x00\x00", "\xFF\x00\x00\x00\x00\x00\x00\xFF",
        "\xFF\x00\x00\x00\x00\x00\xFF\x00", "\xFF\x00\x00\x00\x00\x00\xFF\xFF",
        "\xFF\x00\x00\x00\x00\xFF\x00\x00", "\xFF\x00\x00\x00\x00\xFF\x00\xFF",
        "\xFF\x00\x00\x00\x00\xFF\xFF\x00", "\xFF\x00\x00\x00\x00\xFF\xFF\xFF",
        "\xFF\x00\x00\x00\xFF\x00\x00\x00", "\xFF\x00\x00\x00\xFF\x00\x00\xFF",
        "\xFF\x00\x00\x00\xFF\x00\xFF\x00", "\xFF\x00\x00\x00\xFF\x00\xFF\xFF",
        "\xFF\x00\x00\x00\xFF\xFF\x00\x00", "\xFF\x00\x00\x00\xFF\xFF\x00\xFF",
        "\xFF\x00\x00\x00\xFF\xFF\xFF\x00", "\xFF\x00\x00\x00\xFF\xFF\xFF\xFF",
        "\xFF\x00\x00\xFF\x00\x00\x00\x00", "\xFF\x00\x00\xFF\x00\x00\x00\xFF",
        "\xFF\x00\x00\xFF\x00\x00\xFF\x00", "\xFF\x00\x00\xFF\x00\x00\xFF\xFF",
        "\xFF\x00\x00\xFF\x00\xFF\x00\x00", "\xFF\x00\x00\xFF\x00\xFF\x00\xFF",
        "\xFF\x00\x00\xFF\x00\xFF\xFF\x00", "\xFF\x00\x00\xFF\x00\xFF\xFF\xFF",
        "\xFF\x00\x00\xFF\xFF\x00\x00\x00", "\xFF\x00\x00\xFF\xFF\x00\x00\xFF",
        "\xFF\x00\x00\xFF\xFF\x00\xFF\x00", "\xFF\x00\x00\xFF\xFF\x00\xFF\xFF",
        "\xFF\x00\x00\xFF\xFF\xFF\x00\x00", "\xFF\x00\x00\xFF\xFF\xFF\x00\xFF",
        "\xFF\x00\x00\xFF\xFF\xFF\xFF\x00", "\xFF\x00\x00\xFF\xFF\xFF\xFF\xFF",
        "\xFF\x00\xFF\x00\x00\x00\x00\x00", "\xFF\x00\xFF\x00\x00\x00\x00\xFF",
        "\xFF\x00\xFF\x00\x00\x00\xFF\x00", "\xFF\x00\xFF\x00\x00\x00\xFF\xFF",
        "\xFF\x00\xFF\x00\x00\xFF\x00\x00", "\xFF\x00\xFF\x00\x00\xFF\x00\xFF",
        "\xFF\x00\xFF\x00\x00\xFF\xFF\x00", "\xFF\x00\xFF\x00\x00\xFF\xFF\xFF",
        "\xFF\x00\xFF\x00\xFF\x00\x00\x00", "\xFF\x00\xFF\x00\xFF\x00\x00\xFF",
        "\xFF\x00\xFF\x00\xFF\x00\xFF\x00", "\xFF\x00\xFF\x00\xFF\x00\xFF\xFF",
        "\xFF\x00\xFF\x00\xFF\xFF\x00\x00", "\xFF\x00\xFF\x00\xFF\xFF\x00\xFF",
        "\xFF\x00\xFF\x00\xFF\xFF\xFF\x00", "\xFF\x00\xFF\x00\xFF\xFF\xFF\xFF",
        "\xFF\x00\xFF\xFF\x00\x00\x00\x00", "\xFF\x00\xFF\xFF\x00\x00\x00\xFF",
        "\xFF\x00\xFF\xFF\x00\x00\xFF\x00", "\xFF\x00\xFF\xFF\x00\x00\xFF\xFF",
        "\xFF\x00\xFF\xFF\x00\xFF\x00\x00", "\xFF\x00\xFF\xFF\x00\xFF\x00\xFF",
        "\xFF\x00\xFF\xFF\x00\xFF\xFF\x00", "\xFF\x00\xFF\xFF\x00\xFF\xFF\xFF",
        "\xFF\x00\xFF\xFF\xFF\x00\x00\x00", "\xFF\x00\xFF\xFF\xFF\x00\x00\xFF",
        "\xFF\x00\xFF\xFF\xFF\x00\xFF\x00", "\xFF\x00\xFF\xFF\xFF\x00\xFF\xFF",
        "\xFF\x00\xFF\xFF\xFF\xFF\x00\x00", "\xFF\x00\xFF\xFF\xFF\xFF\x00\xFF",
        "\xFF\x00\xFF\xFF\xFF\xFF\xFF\x00", "\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF",
        "\xFF\xFF\x00\x00\x00\x00\x00\x00", "\xFF\xFF\x00\x00\x00\x00\x00\xFF",
        "\xFF\xFF\x00\x00\x00\x00\xFF\x00", "\xFF\xFF\x00\x00\x00\x00\xFF\xFF",
        "\xFF\xFF\x00\x00\x00\xFF\x00\x00", "\xFF\xFF\x00\x00\x00\xFF\x00\xFF",
        "\xFF\xFF\x00\x00\x00\xFF\xFF\x00", "\xFF\xFF\x00\x00\x00\xFF\xFF\xFF",
        "\xFF\xFF\x00\x00\xFF\x00\x00\x00", "\xFF\xFF\x00\x00\xFF\x00\x00\xFF",
        "\xFF\xFF\x00\x00\xFF\x00\xFF\x00", "\xFF\xFF\x00\x00\xFF\x00\xFF\xFF",
        "\xFF\xFF\x00\x00\xFF\xFF\x00\x00", "\xFF\xFF\x00\x00\xFF\xFF\x00\xFF",
        "\xFF\xFF\x00\x00\xFF\xFF\xFF\x00", "\xFF\xFF\x00\x00\xFF\xFF\xFF\xFF",
        "\xFF\xFF\x00\xFF\x00\x00\x00\x00", "\xFF\xFF\x00\xFF\x00\x00\x00\xFF",
        "\xFF\xFF\x00\xFF\x00\x00\xFF\x00", "\xFF\xFF\x00\xFF\x00\x00\xFF\xFF",
        "\xFF\xFF\x00\xFF\x00\xFF\x00\x00", "\xFF\xFF\x00\xFF\x00\xFF\x00\xFF",
        "\xFF\xFF\x00\xFF\x00\xFF\xFF\x00", "\xFF\xFF\x00\xFF\x00\xFF\xFF\xFF",
        "\xFF\xFF\x00\xFF\xFF\x00\x00\x00", "\xFF\xFF\x00\xFF\xFF\x00\x00\xFF",
        "\xFF\xFF\x00\xFF\xFF\x00\xFF\x00", "\xFF\xFF\x00\xFF\xFF\x00\xFF\xFF",
        "\xFF\xFF\x00\xFF\xFF\xFF\x00\x00", "\xFF\xFF\x00\xFF\xFF\xFF\x00\xFF",
        "\xFF\xFF\x00\xFF\xFF\xFF\xFF\x00", "\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF",
        "\xFF\xFF\xFF\x00\x00\x00\x00\x00", "\xFF\xFF\xFF\x00\x00\x00\x00\xFF",
        "\xFF\xFF\xFF\x00\x00\x00\xFF\x00", "\xFF\xFF\xFF\x00\x00\x00\xFF\xFF",
        "\xFF\xFF\xFF\x00\x00\xFF\x00\x00", "\xFF\xFF\xFF\x00\x00\xFF\x00\xFF",
        "\xFF\xFF\xFF\x00\x00\xFF\xFF\x00", "\xFF\xFF\xFF\x00\x00\xFF\xFF\xFF",
        "\xFF\xFF\xFF\x00\xFF\x00\x00\x00", "\xFF\xFF\xFF\x00\xFF\x00\x00\xFF",
        "\xFF\xFF\xFF\x00\xFF\x00\xFF\x00", "\xFF\xFF\xFF\x00\xFF\x00\xFF\xFF",
        "\xFF\xFF\xFF\x00\xFF\xFF\x00\x00", "\xFF\xFF\xFF\x00\xFF\xFF\x00\xFF",
        "\xFF\xFF\xFF\x00\xFF\xFF\xFF\x00", "\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF",
        "\xFF\xFF\xFF\xFF\x00\x00\x00\x00", "\xFF\xFF\xFF\xFF\x00\x00\x00\xFF",
        "\xFF\xFF\xFF\xFF\x00\x00\xFF\x00", "\xFF\xFF\xFF\xFF\x00\x00\xFF\xFF",
        "\xFF\xFF\xFF\xFF\x00\xFF\x00\x00", "\xFF\xFF\xFF\xFF\x00\xFF\x00\xFF",
        "\xFF\xFF\xFF\xFF\x00\xFF\xFF\x00", "\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF",
        "\xFF\xFF\xFF\xFF\xFF\x00\x00\x00", "\xFF\xFF\xFF\xFF\xFF\x00\x00\xFF",
        "\xFF\xFF\xFF\xFF\xFF\x00\xFF\x00", "\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF",
        "\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00", "\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF",
        "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00", "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"
    );

    /**
     * IP mapping helper table.
     *
     * Indexing this table with each source byte performs the initial bit permutation.
     *
     * @var array
     * @access private
     */
    var $ipmap = array(
        0x00, 0x10, 0x01, 0x11, 0x20, 0x30, 0x21, 0x31,
        0x02, 0x12, 0x03, 0x13, 0x22, 0x32, 0x23, 0x33,
        0x40, 0x50, 0x41, 0x51, 0x60, 0x70, 0x61, 0x71,
        0x42, 0x52, 0x43, 0x53, 0x62, 0x72, 0x63, 0x73,
        0x04, 0x14, 0x05, 0x15, 0x24, 0x34, 0x25, 0x35,
        0x06, 0x16, 0x07, 0x17, 0x26, 0x36, 0x27, 0x37,
        0x44, 0x54, 0x45, 0x55, 0x64, 0x74, 0x65, 0x75,
        0x46, 0x56, 0x47, 0x57, 0x66, 0x76, 0x67, 0x77,
        0x80, 0x90, 0x81, 0x91, 0xA0, 0xB0, 0xA1, 0xB1,
        0x82, 0x92, 0x83, 0x93, 0xA2, 0xB2, 0xA3, 0xB3,
        0xC0, 0xD0, 0xC1, 0xD1, 0xE0, 0xF0, 0xE1, 0xF1,
        0xC2, 0xD2, 0xC3, 0xD3, 0xE2, 0xF2, 0xE3, 0xF3,
        0x84, 0x94, 0x85, 0x95, 0xA4, 0xB4, 0xA5, 0xB5,
        0x86, 0x96, 0x87, 0x97, 0xA6, 0xB6, 0xA7, 0xB7,
        0xC4, 0xD4, 0xC5, 0xD5, 0xE4, 0xF4, 0xE5, 0xF5,
        0xC6, 0xD6, 0xC7, 0xD7, 0xE6, 0xF6, 0xE7, 0xF7,
        0x08, 0x18, 0x09, 0x19, 0x28, 0x38, 0x29, 0x39,
        0x0A, 0x1A, 0x0B, 0x1B, 0x2A, 0x3A, 0x2B, 0x3B,
        0x48, 0x58, 0x49, 0x59, 0x68, 0x78, 0x69, 0x79,
        0x4A, 0x5A, 0x4B, 0x5B, 0x6A, 0x7A, 0x6B, 0x7B,
        0x0C, 0x1C, 0x0D, 0x1D, 0x2C, 0x3C, 0x2D, 0x3D,
        0x0E, 0x1E, 0x0F, 0x1F, 0x2E, 0x3E, 0x2F, 0x3F,
        0x4C, 0x5C, 0x4D, 0x5D, 0x6C, 0x7C, 0x6D, 0x7D,
        0x4E, 0x5E, 0x4F, 0x5F, 0x6E, 0x7E, 0x6F, 0x7F,
        0x88, 0x98, 0x89, 0x99, 0xA8, 0xB8, 0xA9, 0xB9,
        0x8A, 0x9A, 0x8B, 0x9B, 0xAA, 0xBA, 0xAB, 0xBB,
        0xC8, 0xD8, 0xC9, 0xD9, 0xE8, 0xF8, 0xE9, 0xF9,
        0xCA, 0xDA, 0xCB, 0xDB, 0xEA, 0xFA, 0xEB, 0xFB,
        0x8C, 0x9C, 0x8D, 0x9D, 0xAC, 0xBC, 0xAD, 0xBD,
        0x8E, 0x9E, 0x8F, 0x9F, 0xAE, 0xBE, 0xAF, 0xBF,
        0xCC, 0xDC, 0xCD, 0xDD, 0xEC, 0xFC, 0xED, 0xFD,
        0xCE, 0xDE, 0xCF, 0xDF, 0xEE, 0xFE, 0xEF, 0xFF
    );

    /**
     * Inverse IP mapping helper table.
     * Indexing this table with a byte value reverses the bit order.
     *
     * @var array
     * @access private
     */
    var $invipmap = array(
        0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0,
        0x10, 0x90, 0x50, 0xD0, 0x30, 0xB0, 0x70, 0xF0,
        0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8,
        0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8,
        0x04, 0x84, 0x44, 0xC4, 0x24, 0xA4, 0x64, 0xE4,
        0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4,
        0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC,
        0x1C, 0x9C, 0x5C, 0xDC, 0x3C, 0xBC, 0x7C, 0xFC,
        0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2,
        0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2,
        0x0A, 0x8A, 0x4A, 0xCA, 0x2A, 0xAA, 0x6A, 0xEA,
        0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA,
        0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6,
        0x16, 0x96, 0x56, 0xD6, 0x36, 0xB6, 0x76, 0xF6,
        0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE,
        0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE,
        0x01, 0x81, 0x41, 0xC1, 0x21, 0xA1, 0x61, 0xE1,
        0x11, 0x91, 0x51, 0xD1, 0x31, 0xB1, 0x71, 0xF1,
        0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9,
        0x19, 0x99, 0x59, 0xD9, 0x39, 0xB9, 0x79, 0xF9,
        0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5,
        0x15, 0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5,
        0x0D, 0x8D, 0x4D, 0xCD, 0x2D, 0xAD, 0x6D, 0xED,
        0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD,
        0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3,
        0x13, 0x93, 0x53, 0xD3, 0x33, 0xB3, 0x73, 0xF3,
        0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB,
        0x1B, 0x9B, 0x5B, 0xDB, 0x3B, 0xBB, 0x7B, 0xFB,
        0x07, 0x87, 0x47, 0xC7, 0x27, 0xA7, 0x67, 0xE7,
        0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7,
        0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF,
        0x1F, 0x9F, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFF
    );

    /**
     * Pre-permuted S-box1
     *
     * Each box ($sbox1-$sbox8) has been vectorized, then each value pre-permuted using the
     * P table: concatenation can then be replaced by exclusive ORs.
     *
     * @var array
     * @access private
     */
    var $sbox1 = array(
        0x00808200, 0x00000000, 0x00008000, 0x00808202,
        0x00808002, 0x00008202, 0x00000002, 0x00008000,
        0x00000200, 0x00808200, 0x00808202, 0x00000200,
        0x00800202, 0x00808002, 0x00800000, 0x00000002,
        0x00000202, 0x00800200, 0x00800200, 0x00008200,
        0x00008200, 0x00808000, 0x00808000, 0x00800202,
        0x00008002, 0x00800002, 0x00800002, 0x00008002,
        0x00000000, 0x00000202, 0x00008202, 0x00800000,
        0x00008000, 0x00808202, 0x00000002, 0x00808000,
        0x00808200, 0x00800000, 0x00800000, 0x00000200,
        0x00808002, 0x00008000, 0x00008200, 0x00800002,
        0x00000200, 0x00000002, 0x00800202, 0x00008202,
        0x00808202, 0x00008002, 0x00808000, 0x00800202,
        0x00800002, 0x00000202, 0x00008202, 0x00808200,
        0x00000202, 0x00800200, 0x00800200, 0x00000000,
        0x00008002, 0x00008200, 0x00000000, 0x00808002
    );

    /**
     * Pre-permuted S-box2
     *
     * @var array
     * @access private
     */
    var $sbox2 = array(
        0x40084010, 0x40004000, 0x00004000, 0x00084010,
        0x00080000, 0x00000010, 0x40080010, 0x40004010,
        0x40000010, 0x40084010, 0x40084000, 0x40000000,
        0x40004000, 0x00080000, 0x00000010, 0x40080010,
        0x00084000, 0x00080010, 0x40004010, 0x00000000,
        0x40000000, 0x00004000, 0x00084010, 0x40080000,
        0x00080010, 0x40000010, 0x00000000, 0x00084000,
        0x00004010, 0x40084000, 0x40080000, 0x00004010,
        0x00000000, 0x00084010, 0x40080010, 0x00080000,
        0x40004010, 0x40080000, 0x40084000, 0x00004000,
        0x40080000, 0x40004000, 0x00000010, 0x40084010,
        0x00084010, 0x00000010, 0x00004000, 0x40000000,
        0x00004010, 0x40084000, 0x00080000, 0x40000010,
        0x00080010, 0x40004010, 0x40000010, 0x00080010,
        0x00084000, 0x00000000, 0x40004000, 0x00004010,
        0x40000000, 0x40080010, 0x40084010, 0x00084000
    );

    /**
     * Pre-permuted S-box3
     *
     * @var array
     * @access private
     */
    var $sbox3 = array(
        0x00000104, 0x04010100, 0x00000000, 0x04010004,
        0x04000100, 0x00000000, 0x00010104, 0x04000100,
        0x00010004, 0x04000004, 0x04000004, 0x00010000,
        0x04010104, 0x00010004, 0x04010000, 0x00000104,
        0x04000000, 0x00000004, 0x04010100, 0x00000100,
        0x00010100, 0x04010000, 0x04010004, 0x00010104,
        0x04000104, 0x00010100, 0x00010000, 0x04000104,
        0x00000004, 0x04010104, 0x00000100, 0x04000000,
        0x04010100, 0x04000000, 0x00010004, 0x00000104,
        0x00010000, 0x04010100, 0x04000100, 0x00000000,
        0x00000100, 0x00010004, 0x04010104, 0x04000100,
        0x04000004, 0x00000100, 0x00000000, 0x04010004,
        0x04000104, 0x00010000, 0x04000000, 0x04010104,
        0x00000004, 0x00010104, 0x00010100, 0x04000004,
        0x04010000, 0x04000104, 0x00000104, 0x04010000,
        0x00010104, 0x00000004, 0x04010004, 0x00010100
    );

    /**
     * Pre-permuted S-box4
     *
     * @var array
     * @access private
     */
    var $sbox4 = array(
        0x80401000, 0x80001040, 0x80001040, 0x00000040,
        0x00401040, 0x80400040, 0x80400000, 0x80001000,
        0x00000000, 0x00401000, 0x00401000, 0x80401040,
        0x80000040, 0x00000000, 0x00400040, 0x80400000,
        0x80000000, 0x00001000, 0x00400000, 0x80401000,
        0x00000040, 0x00400000, 0x80001000, 0x00001040,
        0x80400040, 0x80000000, 0x00001040, 0x00400040,
        0x00001000, 0x00401040, 0x80401040, 0x80000040,
        0x00400040, 0x80400000, 0x00401000, 0x80401040,
        0x80000040, 0x00000000, 0x00000000, 0x00401000,
        0x00001040, 0x00400040, 0x80400040, 0x80000000,
        0x80401000, 0x80001040, 0x80001040, 0x00000040,
        0x80401040, 0x80000040, 0x80000000, 0x00001000,
        0x80400000, 0x80001000, 0x00401040, 0x80400040,
        0x80001000, 0x00001040, 0x00400000, 0x80401000,
        0x00000040, 0x00400000, 0x00001000, 0x00401040
    );

    /**
     * Pre-permuted S-box5
     *
     * @var array
     * @access private
     */
    var $sbox5 = array(
        0x00000080, 0x01040080, 0x01040000, 0x21000080,
        0x00040000, 0x00000080, 0x20000000, 0x01040000,
        0x20040080, 0x00040000, 0x01000080, 0x20040080,
        0x21000080, 0x21040000, 0x00040080, 0x20000000,
        0x01000000, 0x20040000, 0x20040000, 0x00000000,
        0x20000080, 0x21040080, 0x21040080, 0x01000080,
        0x21040000, 0x20000080, 0x00000000, 0x21000000,
        0x01040080, 0x01000000, 0x21000000, 0x00040080,
        0x00040000, 0x21000080, 0x00000080, 0x01000000,
        0x20000000, 0x01040000, 0x21000080, 0x20040080,
        0x01000080, 0x20000000, 0x21040000, 0x01040080,
        0x20040080, 0x00000080, 0x01000000, 0x21040000,
        0x21040080, 0x00040080, 0x21000000, 0x21040080,
        0x01040000, 0x00000000, 0x20040000, 0x21000000,
        0x00040080, 0x01000080, 0x20000080, 0x00040000,
        0x00000000, 0x20040000, 0x01040080, 0x20000080
    );

    /**
     * Pre-permuted S-box6
     *
     * @var array
     * @access private
     */
    var $sbox6 = array(
        0x10000008, 0x10200000, 0x00002000, 0x10202008,
        0x10200000, 0x00000008, 0x10202008, 0x00200000,
        0x10002000, 0x00202008, 0x00200000, 0x10000008,
        0x00200008, 0x10002000, 0x10000000, 0x00002008,
        0x00000000, 0x00200008, 0x10002008, 0x00002000,
        0x00202000, 0x10002008, 0x00000008, 0x10200008,
        0x10200008, 0x00000000, 0x00202008, 0x10202000,
        0x00002008, 0x00202000, 0x10202000, 0x10000000,
        0x10002000, 0x00000008, 0x10200008, 0x00202000,
        0x10202008, 0x00200000, 0x00002008, 0x10000008,
        0x00200000, 0x10002000, 0x10000000, 0x00002008,
        0x10000008, 0x10202008, 0x00202000, 0x10200000,
        0x00202008, 0x10202000, 0x00000000, 0x10200008,
        0x00000008, 0x00002000, 0x10200000, 0x00202008,
        0x00002000, 0x00200008, 0x10002008, 0x00000000,
        0x10202000, 0x10000000, 0x00200008, 0x10002008
    );

    /**
     * Pre-permuted S-box7
     *
     * @var array
     * @access private
     */
    var $sbox7 = array(
        0x00100000, 0x02100001, 0x02000401, 0x00000000,
        0x00000400, 0x02000401, 0x00100401, 0x02100400,
        0x02100401, 0x00100000, 0x00000000, 0x02000001,
        0x00000001, 0x02000000, 0x02100001, 0x00000401,
        0x02000400, 0x00100401, 0x00100001, 0x02000400,
        0x02000001, 0x02100000, 0x02100400, 0x00100001,
        0x02100000, 0x00000400, 0x00000401, 0x02100401,
        0x00100400, 0x00000001, 0x02000000, 0x00100400,
        0x02000000, 0x00100400, 0x00100000, 0x02000401,
        0x02000401, 0x02100001, 0x02100001, 0x00000001,
        0x00100001, 0x02000000, 0x02000400, 0x00100000,
        0x02100400, 0x00000401, 0x00100401, 0x02100400,
        0x00000401, 0x02000001, 0x02100401, 0x02100000,
        0x00100400, 0x00000000, 0x00000001, 0x02100401,
        0x00000000, 0x00100401, 0x02100000, 0x00000400,
        0x02000001, 0x02000400, 0x00000400, 0x00100001
    );

    /**
     * Pre-permuted S-box8
     *
     * @var array
     * @access private
     */
    var $sbox8 = array(
        0x08000820, 0x00000800, 0x00020000, 0x08020820,
        0x08000000, 0x08000820, 0x00000020, 0x08000000,
        0x00020020, 0x08020000, 0x08020820, 0x00020800,
        0x08020800, 0x00020820, 0x00000800, 0x00000020,
        0x08020000, 0x08000020, 0x08000800, 0x00000820,
        0x00020800, 0x00020020, 0x08020020, 0x08020800,
        0x00000820, 0x00000000, 0x00000000, 0x08020020,
        0x08000020, 0x08000800, 0x00020820, 0x00020000,
        0x00020820, 0x00020000, 0x08020800, 0x00000800,
        0x00000020, 0x08020020, 0x00000800, 0x00020820,
        0x08000800, 0x00000020, 0x08000020, 0x08020000,
        0x08020020, 0x08000000, 0x00020000, 0x08000820,
        0x00000000, 0x08020820, 0x00020020, 0x08000020,
        0x08020000, 0x08000800, 0x08000820, 0x00000000,
        0x08020820, 0x00020800, 0x00020800, 0x00000820,
        0x00000820, 0x00020020, 0x08000000, 0x08020800
    );

    /**
     * Test for engine validity
     *
     * This is mainly just a wrapper to set things up for Crypt_Base::isValidEngine()
     *
     * @see Crypt_Base::isValidEngine()
     * @param int $engine
     * @access public
     * @return bool
     */
    function isValidEngine($engine)
    {
        if ($this->key_length_max == 8) {
            if ($engine == CRYPT_ENGINE_OPENSSL) {
                $this->cipher_name_openssl_ecb = 'des-ecb';
                $this->cipher_name_openssl = 'des-' . $this->_openssl_translate_mode();
            }
        }

        return parent::isValidEngine($engine);
    }

    /**
     * Sets the key.
     *
     * Keys can be of any length.  DES, itself, uses 64-bit keys (eg. strlen($key) == 8), however, we
     * only use the first eight, if $key has more then eight characters in it, and pad $key with the
     * null byte if it is less then eight characters long.
     *
     * DES also requires that every eighth bit be a parity bit, however, we'll ignore that.
     *
     * If the key is not explicitly set, it'll be assumed to be all zero's.
     *
     * @see Crypt_Base::setKey()
     * @access public
     * @param string $key
     */
    function setKey($key)
    {
        // We check/cut here only up to max length of the key.
        // Key padding to the proper length will be done in _setupKey()
        if (strlen($key) > $this->key_length_max) {
            $key = substr($key, 0, $this->key_length_max);
        }

        // Sets the key
        parent::setKey($key);
    }

    /**
     * Encrypts a block
     *
     * @see Crypt_Base::_encryptBlock()
     * @see Crypt_Base::encrypt()
     * @see self::encrypt()
     * @access private
     * @param string $in
     * @return string
     */
    function _encryptBlock($in)
    {
        return $this->_processBlock($in, CRYPT_DES_ENCRYPT);
    }

    /**
     * Decrypts a block
     *
     * @see Crypt_Base::_decryptBlock()
     * @see Crypt_Base::decrypt()
     * @see self::decrypt()
     * @access private
     * @param string $in
     * @return string
     */
    function _decryptBlock($in)
    {
        return $this->_processBlock($in, CRYPT_DES_DECRYPT);
    }

    /**
     * Encrypts or decrypts a 64-bit block
     *
     * $mode should be either CRYPT_DES_ENCRYPT or CRYPT_DES_DECRYPT.  See
     * {@link http://en.wikipedia.org/wiki/Image:Feistel.png Feistel.png} to get a general
     * idea of what this function does.
     *
     * @see self::_encryptBlock()
     * @see self::_decryptBlock()
     * @access private
     * @param string $block
     * @param int $mode
     * @return string
     */
    function _processBlock($block, $mode)
    {
        static $sbox1, $sbox2, $sbox3, $sbox4, $sbox5, $sbox6, $sbox7, $sbox8, $shuffleip, $shuffleinvip;
        if (!$sbox1) {
            $sbox1 = array_map("intval", $this->sbox1);
            $sbox2 = array_map("intval", $this->sbox2);
            $sbox3 = array_map("intval", $this->sbox3);
            $sbox4 = array_map("intval", $this->sbox4);
            $sbox5 = array_map("intval", $this->sbox5);
            $sbox6 = array_map("intval", $this->sbox6);
            $sbox7 = array_map("intval", $this->sbox7);
            $sbox8 = array_map("intval", $this->sbox8);
            /* Merge $shuffle with $[inv]ipmap */
            for ($i = 0; $i < 256; ++$i) {
                $shuffleip[]    =  $this->shuffle[$this->ipmap[$i]];
                $shuffleinvip[] =  $this->shuffle[$this->invipmap[$i]];
            }
        }

        $keys  = $this->keys[$mode];
        $ki    = -1;

        // Do the initial IP permutation.
        $t = unpack('Nl/Nr', $block);
        list($l, $r) = array($t['l'], $t['r']);
        $block = ($shuffleip[ $r        & 0xFF] & "\x80\x80\x80\x80\x80\x80\x80\x80") |
                 ($shuffleip[($r >>  8) & 0xFF] & "\x40\x40\x40\x40\x40\x40\x40\x40") |
                 ($shuffleip[($r >> 16) & 0xFF] & "\x20\x20\x20\x20\x20\x20\x20\x20") |
                 ($shuffleip[($r >> 24) & 0xFF] & "\x10\x10\x10\x10\x10\x10\x10\x10") |
                 ($shuffleip[ $l        & 0xFF] & "\x08\x08\x08\x08\x08\x08\x08\x08") |
                 ($shuffleip[($l >>  8) & 0xFF] & "\x04\x04\x04\x04\x04\x04\x04\x04") |
                 ($shuffleip[($l >> 16) & 0xFF] & "\x02\x02\x02\x02\x02\x02\x02\x02") |
                 ($shuffleip[($l >> 24) & 0xFF] & "\x01\x01\x01\x01\x01\x01\x01\x01");

        // Extract L0 and R0.
        $t = unpack('Nl/Nr', $block);
        list($l, $r) = array($t['l'], $t['r']);

        for ($des_round = 0; $des_round < $this->des_rounds; ++$des_round) {
            // Perform the 16 steps.
            for ($i = 0; $i < 16; $i++) {
                // start of "the Feistel (F) function" - see the following URL:
                // http://en.wikipedia.org/wiki/Image:Data_Encryption_Standard_InfoBox_Diagram.png
                // Merge key schedule.
                $b1 = (($r >>  3) & 0x1FFFFFFF) ^ ($r << 29) ^ $keys[++$ki];
                $b2 = (($r >> 31) & 0x00000001) ^ ($r <<  1) ^ $keys[++$ki];

                // S-box indexing.
                $t = $sbox1[($b1 >> 24) & 0x3F] ^ $sbox2[($b2 >> 24) & 0x3F] ^
                     $sbox3[($b1 >> 16) & 0x3F] ^ $sbox4[($b2 >> 16) & 0x3F] ^
                     $sbox5[($b1 >>  8) & 0x3F] ^ $sbox6[($b2 >>  8) & 0x3F] ^
                     $sbox7[ $b1        & 0x3F] ^ $sbox8[ $b2        & 0x3F] ^ $l;
                // end of "the Feistel (F) function"

                $l = $r;
                $r = $t;
            }

            // Last step should not permute L & R.
            $t = $l;
            $l = $r;
            $r = $t;
        }

        // Perform the inverse IP permutation.
        return ($shuffleinvip[($r >> 24) & 0xFF] & "\x80\x80\x80\x80\x80\x80\x80\x80") |
               ($shuffleinvip[($l >> 24) & 0xFF] & "\x40\x40\x40\x40\x40\x40\x40\x40") |
               ($shuffleinvip[($r >> 16) & 0xFF] & "\x20\x20\x20\x20\x20\x20\x20\x20") |
               ($shuffleinvip[($l >> 16) & 0xFF] & "\x10\x10\x10\x10\x10\x10\x10\x10") |
               ($shuffleinvip[($r >>  8) & 0xFF] & "\x08\x08\x08\x08\x08\x08\x08\x08") |
               ($shuffleinvip[($l >>  8) & 0xFF] & "\x04\x04\x04\x04\x04\x04\x04\x04") |
               ($shuffleinvip[ $r        & 0xFF] & "\x02\x02\x02\x02\x02\x02\x02\x02") |
               ($shuffleinvip[ $l        & 0xFF] & "\x01\x01\x01\x01\x01\x01\x01\x01");
    }

    /**
     * Creates the key schedule
     *
     * @see Crypt_Base::_setupKey()
     * @access private
     */
    function _setupKey()
    {
        if (isset($this->kl['key']) && $this->key === $this->kl['key'] && $this->des_rounds === $this->kl['des_rounds']) {
            // already expanded
            return;
        }
        $this->kl = array('key' => $this->key, 'des_rounds' => $this->des_rounds);

        static $shifts = array( // number of key bits shifted per round
            1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1
        );

        static $pc1map = array(
            0x00, 0x00, 0x08, 0x08, 0x04, 0x04, 0x0C, 0x0C,
            0x02, 0x02, 0x0A, 0x0A, 0x06, 0x06, 0x0E, 0x0E,
            0x10, 0x10, 0x18, 0x18, 0x14, 0x14, 0x1C, 0x1C,
            0x12, 0x12, 0x1A, 0x1A, 0x16, 0x16, 0x1E, 0x1E,
            0x20, 0x20, 0x28, 0x28, 0x24, 0x24, 0x2C, 0x2C,
            0x22, 0x22, 0x2A, 0x2A, 0x26, 0x26, 0x2E, 0x2E,
            0x30, 0x30, 0x38, 0x38, 0x34, 0x34, 0x3C, 0x3C,
            0x32, 0x32, 0x3A, 0x3A, 0x36, 0x36, 0x3E, 0x3E,
            0x40, 0x40, 0x48, 0x48, 0x44, 0x44, 0x4C, 0x4C,
            0x42, 0x42, 0x4A, 0x4A, 0x46, 0x46, 0x4E, 0x4E,
            0x50, 0x50, 0x58, 0x58, 0x54, 0x54, 0x5C, 0x5C,
            0x52, 0x52, 0x5A, 0x5A, 0x56, 0x56, 0x5E, 0x5E,
            0x60, 0x60, 0x68, 0x68, 0x64, 0x64, 0x6C, 0x6C,
            0x62, 0x62, 0x6A, 0x6A, 0x66, 0x66, 0x6E, 0x6E,
            0x70, 0x70, 0x78, 0x78, 0x74, 0x74, 0x7C, 0x7C,
            0x72, 0x72, 0x7A, 0x7A, 0x76, 0x76, 0x7E, 0x7E,
            0x80, 0x80, 0x88, 0x88, 0x84, 0x84, 0x8C, 0x8C,
            0x82, 0x82, 0x8A, 0x8A, 0x86, 0x86, 0x8E, 0x8E,
            0x90, 0x90, 0x98, 0x98, 0x94, 0x94, 0x9C, 0x9C,
            0x92, 0x92, 0x9A, 0x9A, 0x96, 0x96, 0x9E, 0x9E,
            0xA0, 0xA0, 0xA8, 0xA8, 0xA4, 0xA4, 0xAC, 0xAC,
            0xA2, 0xA2, 0xAA, 0xAA, 0xA6, 0xA6, 0xAE, 0xAE,
            0xB0, 0xB0, 0xB8, 0xB8, 0xB4, 0xB4, 0xBC, 0xBC,
            0xB2, 0xB2, 0xBA, 0xBA, 0xB6, 0xB6, 0xBE, 0xBE,
            0xC0, 0xC0, 0xC8, 0xC8, 0xC4, 0xC4, 0xCC, 0xCC,
            0xC2, 0xC2, 0xCA, 0xCA, 0xC6, 0xC6, 0xCE, 0xCE,
            0xD0, 0xD0, 0xD8, 0xD8, 0xD4, 0xD4, 0xDC, 0xDC,
            0xD2, 0xD2, 0xDA, 0xDA, 0xD6, 0xD6, 0xDE, 0xDE,
            0xE0, 0xE0, 0xE8, 0xE8, 0xE4, 0xE4, 0xEC, 0xEC,
            0xE2, 0xE2, 0xEA, 0xEA, 0xE6, 0xE6, 0xEE, 0xEE,
            0xF0, 0xF0, 0xF8, 0xF8, 0xF4, 0xF4, 0xFC, 0xFC,
            0xF2, 0xF2, 0xFA, 0xFA, 0xF6, 0xF6, 0xFE, 0xFE
        );

        // Mapping tables for the PC-2 transformation.
        static $pc2mapc1 = array(
            0x00000000, 0x00000400, 0x00200000, 0x00200400,
            0x00000001, 0x00000401, 0x00200001, 0x00200401,
            0x02000000, 0x02000400, 0x02200000, 0x02200400,
            0x02000001, 0x02000401, 0x02200001, 0x02200401
        );
        static $pc2mapc2 = array(
            0x00000000, 0x00000800, 0x08000000, 0x08000800,
            0x00010000, 0x00010800, 0x08010000, 0x08010800,
            0x00000000, 0x00000800, 0x08000000, 0x08000800,
            0x00010000, 0x00010800, 0x08010000, 0x08010800,
            0x00000100, 0x00000900, 0x08000100, 0x08000900,
            0x00010100, 0x00010900, 0x08010100, 0x08010900,
            0x00000100, 0x00000900, 0x08000100, 0x08000900,
            0x00010100, 0x00010900, 0x08010100, 0x08010900,
            0x00000010, 0x00000810, 0x08000010, 0x08000810,
            0x00010010, 0x00010810, 0x08010010, 0x08010810,
            0x00000010, 0x00000810, 0x08000010, 0x08000810,
            0x00010010, 0x00010810, 0x08010010, 0x08010810,
            0x00000110, 0x00000910, 0x08000110, 0x08000910,
            0x00010110, 0x00010910, 0x08010110, 0x08010910,
            0x00000110, 0x00000910, 0x08000110, 0x08000910,
            0x00010110, 0x00010910, 0x08010110, 0x08010910,
            0x00040000, 0x00040800, 0x08040000, 0x08040800,
            0x00050000, 0x00050800, 0x08050000, 0x08050800,
            0x00040000, 0x00040800, 0x08040000, 0x08040800,
            0x00050000, 0x00050800, 0x08050000, 0x08050800,
            0x00040100, 0x00040900, 0x08040100, 0x08040900,
            0x00050100, 0x00050900, 0x08050100, 0x08050900,
            0x00040100, 0x00040900, 0x08040100, 0x08040900,
            0x00050100, 0x00050900, 0x08050100, 0x08050900,
            0x00040010, 0x00040810, 0x08040010, 0x08040810,
            0x00050010, 0x00050810, 0x08050010, 0x08050810,
            0x00040010, 0x00040810, 0x08040010, 0x08040810,
            0x00050010, 0x00050810, 0x08050010, 0x08050810,
            0x00040110, 0x00040910, 0x08040110, 0x08040910,
            0x00050110, 0x00050910, 0x08050110, 0x08050910,
            0x00040110, 0x00040910, 0x08040110, 0x08040910,
            0x00050110, 0x00050910, 0x08050110, 0x08050910,
            0x01000000, 0x01000800, 0x09000000, 0x09000800,
            0x01010000, 0x01010800, 0x09010000, 0x09010800,
            0x01000000, 0x01000800, 0x09000000, 0x09000800,
            0x01010000, 0x01010800, 0x09010000, 0x09010800,
            0x01000100, 0x01000900, 0x09000100, 0x09000900,
            0x01010100, 0x01010900, 0x09010100, 0x09010900,
            0x01000100, 0x01000900, 0x09000100, 0x09000900,
            0x01010100, 0x01010900, 0x09010100, 0x09010900,
            0x01000010, 0x01000810, 0x09000010, 0x09000810,
            0x01010010, 0x01010810, 0x09010010, 0x09010810,
            0x01000010, 0x01000810, 0x09000010, 0x09000810,
            0x01010010, 0x01010810, 0x09010010, 0x09010810,
            0x01000110, 0x01000910, 0x09000110, 0x09000910,
            0x01010110, 0x01010910, 0x09010110, 0x09010910,
            0x01000110, 0x01000910, 0x09000110, 0x09000910,
            0x01010110, 0x01010910, 0x09010110, 0x09010910,
            0x01040000, 0x01040800, 0x09040000, 0x09040800,
            0x01050000, 0x01050800, 0x09050000, 0x09050800,
            0x01040000, 0x01040800, 0x09040000, 0x09040800,
            0x01050000, 0x01050800, 0x09050000, 0x09050800,
            0x01040100, 0x01040900, 0x09040100, 0x09040900,
            0x01050100, 0x01050900, 0x09050100, 0x09050900,
            0x01040100, 0x01040900, 0x09040100, 0x09040900,
            0x01050100, 0x01050900, 0x09050100, 0x09050900,
            0x01040010, 0x01040810, 0x09040010, 0x09040810,
            0x01050010, 0x01050810, 0x09050010, 0x09050810,
            0x01040010, 0x01040810, 0x09040010, 0x09040810,
            0x01050010, 0x01050810, 0x09050010, 0x09050810,
            0x01040110, 0x01040910, 0x09040110, 0x09040910,
            0x01050110, 0x01050910, 0x09050110, 0x09050910,
            0x01040110, 0x01040910, 0x09040110, 0x09040910,
            0x01050110, 0x01050910, 0x09050110, 0x09050910
        );
        static $pc2mapc3 = array(
            0x00000000, 0x00000004, 0x00001000, 0x00001004,
            0x00000000, 0x00000004, 0x00001000, 0x00001004,
            0x10000000, 0x10000004, 0x10001000, 0x10001004,
            0x10000000, 0x10000004, 0x10001000, 0x10001004,
            0x00000020, 0x00000024, 0x00001020, 0x00001024,
            0x00000020, 0x00000024, 0x00001020, 0x00001024,
            0x10000020, 0x10000024, 0x10001020, 0x10001024,
            0x10000020, 0x10000024, 0x10001020, 0x10001024,
            0x00080000, 0x00080004, 0x00081000, 0x00081004,
            0x00080000, 0x00080004, 0x00081000, 0x00081004,
            0x10080000, 0x10080004, 0x10081000, 0x10081004,
            0x10080000, 0x10080004, 0x10081000, 0x10081004,
            0x00080020, 0x00080024, 0x00081020, 0x00081024,
            0x00080020, 0x00080024, 0x00081020, 0x00081024,
            0x10080020, 0x10080024, 0x10081020, 0x10081024,
            0x10080020, 0x10080024, 0x10081020, 0x10081024,
            0x20000000, 0x20000004, 0x20001000, 0x20001004,
            0x20000000, 0x20000004, 0x20001000, 0x20001004,
            0x30000000, 0x30000004, 0x30001000, 0x30001004,
            0x30000000, 0x30000004, 0x30001000, 0x30001004,
            0x20000020, 0x20000024, 0x20001020, 0x20001024,
            0x20000020, 0x20000024, 0x20001020, 0x20001024,
            0x30000020, 0x30000024, 0x30001020, 0x30001024,
            0x30000020, 0x30000024, 0x30001020, 0x30001024,
            0x20080000, 0x20080004, 0x20081000, 0x20081004,
            0x20080000, 0x20080004, 0x20081000, 0x20081004,
            0x30080000, 0x30080004, 0x30081000, 0x30081004,
            0x30080000, 0x30080004, 0x30081000, 0x30081004,
            0x20080020, 0x20080024, 0x20081020, 0x20081024,
            0x20080020, 0x20080024, 0x20081020, 0x20081024,
            0x30080020, 0x30080024, 0x30081020, 0x30081024,
            0x30080020, 0x30080024, 0x30081020, 0x30081024,
            0x00000002, 0x00000006, 0x00001002, 0x00001006,
            0x00000002, 0x00000006, 0x00001002, 0x00001006,
            0x10000002, 0x10000006, 0x10001002, 0x10001006,
            0x10000002, 0x10000006, 0x10001002, 0x10001006,
            0x00000022, 0x00000026, 0x00001022, 0x00001026,
            0x00000022, 0x00000026, 0x00001022, 0x00001026,
            0x10000022, 0x10000026, 0x10001022, 0x10001026,
            0x10000022, 0x10000026, 0x10001022, 0x10001026,
            0x00080002, 0x00080006, 0x00081002, 0x00081006,
            0x00080002, 0x00080006, 0x00081002, 0x00081006,
            0x10080002, 0x10080006, 0x10081002, 0x10081006,
            0x10080002, 0x10080006, 0x10081002, 0x10081006,
            0x00080022, 0x00080026, 0x00081022, 0x00081026,
            0x00080022, 0x00080026, 0x00081022, 0x00081026,
            0x10080022, 0x10080026, 0x10081022, 0x10081026,
            0x10080022, 0x10080026, 0x10081022, 0x10081026,
            0x20000002, 0x20000006, 0x20001002, 0x20001006,
            0x20000002, 0x20000006, 0x20001002, 0x20001006,
            0x30000002, 0x30000006, 0x30001002, 0x30001006,
            0x30000002, 0x30000006, 0x30001002, 0x30001006,
            0x20000022, 0x20000026, 0x20001022, 0x20001026,
            0x20000022, 0x20000026, 0x20001022, 0x20001026,
            0x30000022, 0x30000026, 0x30001022, 0x30001026,
            0x30000022, 0x30000026, 0x30001022, 0x30001026,
            0x20080002, 0x20080006, 0x20081002, 0x20081006,
            0x20080002, 0x20080006, 0x20081002, 0x20081006,
            0x30080002, 0x30080006, 0x30081002, 0x30081006,
            0x30080002, 0x30080006, 0x30081002, 0x30081006,
            0x20080022, 0x20080026, 0x20081022, 0x20081026,
            0x20080022, 0x20080026, 0x20081022, 0x20081026,
            0x30080022, 0x30080026, 0x30081022, 0x30081026,
            0x30080022, 0x30080026, 0x30081022, 0x30081026
        );
        static $pc2mapc4 = array(
            0x00000000, 0x00100000, 0x00000008, 0x00100008,
            0x00000200, 0x00100200, 0x00000208, 0x00100208,
            0x00000000, 0x00100000, 0x00000008, 0x00100008,
            0x00000200, 0x00100200, 0x00000208, 0x00100208,
            0x04000000, 0x04100000, 0x04000008, 0x04100008,
            0x04000200, 0x04100200, 0x04000208, 0x04100208,
            0x04000000, 0x04100000, 0x04000008, 0x04100008,
            0x04000200, 0x04100200, 0x04000208, 0x04100208,
            0x00002000, 0x00102000, 0x00002008, 0x00102008,
            0x00002200, 0x00102200, 0x00002208, 0x00102208,
            0x00002000, 0x00102000, 0x00002008, 0x00102008,
            0x00002200, 0x00102200, 0x00002208, 0x00102208,
            0x04002000, 0x04102000, 0x04002008, 0x04102008,
            0x04002200, 0x04102200, 0x04002208, 0x04102208,
            0x04002000, 0x04102000, 0x04002008, 0x04102008,
            0x04002200, 0x04102200, 0x04002208, 0x04102208,
            0x00000000, 0x00100000, 0x00000008, 0x00100008,
            0x00000200, 0x00100200, 0x00000208, 0x00100208,
            0x00000000, 0x00100000, 0x00000008, 0x00100008,
            0x00000200, 0x00100200, 0x00000208, 0x00100208,
            0x04000000, 0x04100000, 0x04000008, 0x04100008,
            0x04000200, 0x04100200, 0x04000208, 0x04100208,
            0x04000000, 0x04100000, 0x04000008, 0x04100008,
            0x04000200, 0x04100200, 0x04000208, 0x04100208,
            0x00002000, 0x00102000, 0x00002008, 0x00102008,
            0x00002200, 0x00102200, 0x00002208, 0x00102208,
            0x00002000, 0x00102000, 0x00002008, 0x00102008,
            0x00002200, 0x00102200, 0x00002208, 0x00102208,
            0x04002000, 0x04102000, 0x04002008, 0x04102008,
            0x04002200, 0x04102200, 0x04002208, 0x04102208,
            0x04002000, 0x04102000, 0x04002008, 0x04102008,
            0x04002200, 0x04102200, 0x04002208, 0x04102208,
            0x00020000, 0x00120000, 0x00020008, 0x00120008,
            0x00020200, 0x00120200, 0x00020208, 0x00120208,
            0x00020000, 0x00120000, 0x00020008, 0x00120008,
            0x00020200, 0x00120200, 0x00020208, 0x00120208,
            0x04020000, 0x04120000, 0x04020008, 0x04120008,
            0x04020200, 0x04120200, 0x04020208, 0x04120208,
            0x04020000, 0x04120000, 0x04020008, 0x04120008,
            0x04020200, 0x04120200, 0x04020208, 0x04120208,
            0x00022000, 0x00122000, 0x00022008, 0x00122008,
            0x00022200, 0x00122200, 0x00022208, 0x00122208,
            0x00022000, 0x00122000, 0x00022008, 0x00122008,
            0x00022200, 0x00122200, 0x00022208, 0x00122208,
            0x04022000, 0x04122000, 0x04022008, 0x04122008,
            0x04022200, 0x04122200, 0x04022208, 0x04122208,
            0x04022000, 0x04122000, 0x04022008, 0x04122008,
            0x04022200, 0x04122200, 0x04022208, 0x04122208,
            0x00020000, 0x00120000, 0x00020008, 0x00120008,
            0x00020200, 0x00120200, 0x00020208, 0x00120208,
            0x00020000, 0x00120000, 0x00020008, 0x00120008,
            0x00020200, 0x00120200, 0x00020208, 0x00120208,
            0x04020000, 0x04120000, 0x04020008, 0x04120008,
            0x04020200, 0x04120200, 0x04020208, 0x04120208,
            0x04020000, 0x04120000, 0x04020008, 0x04120008,
            0x04020200, 0x04120200, 0x04020208, 0x04120208,
            0x00022000, 0x00122000, 0x00022008, 0x00122008,
            0x00022200, 0x00122200, 0x00022208, 0x00122208,
            0x00022000, 0x00122000, 0x00022008, 0x00122008,
            0x00022200, 0x00122200, 0x00022208, 0x00122208,
            0x04022000, 0x04122000, 0x04022008, 0x04122008,
            0x04022200, 0x04122200, 0x04022208, 0x04122208,
            0x04022000, 0x04122000, 0x04022008, 0x04122008,
            0x04022200, 0x04122200, 0x04022208, 0x04122208
        );
        static $pc2mapd1 = array(
            0x00000000, 0x00000001, 0x08000000, 0x08000001,
            0x00200000, 0x00200001, 0x08200000, 0x08200001,
            0x00000002, 0x00000003, 0x08000002, 0x08000003,
            0x00200002, 0x00200003, 0x08200002, 0x08200003
        );
        static $pc2mapd2 = array(
            0x00000000, 0x00100000, 0x00000800, 0x00100800,
            0x00000000, 0x00100000, 0x00000800, 0x00100800,
            0x04000000, 0x04100000, 0x04000800, 0x04100800,
            0x04000000, 0x04100000, 0x04000800, 0x04100800,
            0x00000004, 0x00100004, 0x00000804, 0x00100804,
            0x00000004, 0x00100004, 0x00000804, 0x00100804,
            0x04000004, 0x04100004, 0x04000804, 0x04100804,
            0x04000004, 0x04100004, 0x04000804, 0x04100804,
            0x00000000, 0x00100000, 0x00000800, 0x00100800,
            0x00000000, 0x00100000, 0x00000800, 0x00100800,
            0x04000000, 0x04100000, 0x04000800, 0x04100800,
            0x04000000, 0x04100000, 0x04000800, 0x04100800,
            0x00000004, 0x00100004, 0x00000804, 0x00100804,
            0x00000004, 0x00100004, 0x00000804, 0x00100804,
            0x04000004, 0x04100004, 0x04000804, 0x04100804,
            0x04000004, 0x04100004, 0x04000804, 0x04100804,
            0x00000200, 0x00100200, 0x00000A00, 0x00100A00,
            0x00000200, 0x00100200, 0x00000A00, 0x00100A00,
            0x04000200, 0x04100200, 0x04000A00, 0x04100A00,
            0x04000200, 0x04100200, 0x04000A00, 0x04100A00,
            0x00000204, 0x00100204, 0x00000A04, 0x00100A04,
            0x00000204, 0x00100204, 0x00000A04, 0x00100A04,
            0x04000204, 0x04100204, 0x04000A04, 0x04100A04,
            0x04000204, 0x04100204, 0x04000A04, 0x04100A04,
            0x00000200, 0x00100200, 0x00000A00, 0x00100A00,
            0x00000200, 0x00100200, 0x00000A00, 0x00100A00,
            0x04000200, 0x04100200, 0x04000A00, 0x04100A00,
            0x04000200, 0x04100200, 0x04000A00, 0x04100A00,
            0x00000204, 0x00100204, 0x00000A04, 0x00100A04,
            0x00000204, 0x00100204, 0x00000A04, 0x00100A04,
            0x04000204, 0x04100204, 0x04000A04, 0x04100A04,
            0x04000204, 0x04100204, 0x04000A04, 0x04100A04,
            0x00020000, 0x00120000, 0x00020800, 0x00120800,
            0x00020000, 0x00120000, 0x00020800, 0x00120800,
            0x04020000, 0x04120000, 0x04020800, 0x04120800,
            0x04020000, 0x04120000, 0x04020800, 0x04120800,
            0x00020004, 0x00120004, 0x00020804, 0x00120804,
            0x00020004, 0x00120004, 0x00020804, 0x00120804,
            0x04020004, 0x04120004, 0x04020804, 0x04120804,
            0x04020004, 0x04120004, 0x04020804, 0x04120804,
            0x00020000, 0x00120000, 0x00020800, 0x00120800,
            0x00020000, 0x00120000, 0x00020800, 0x00120800,
            0x04020000, 0x04120000, 0x04020800, 0x04120800,
            0x04020000, 0x04120000, 0x04020800, 0x04120800,
            0x00020004, 0x00120004, 0x00020804, 0x00120804,
            0x00020004, 0x00120004, 0x00020804, 0x00120804,
            0x04020004, 0x04120004, 0x04020804, 0x04120804,
            0x04020004, 0x04120004, 0x04020804, 0x04120804,
            0x00020200, 0x00120200, 0x00020A00, 0x00120A00,
            0x00020200, 0x00120200, 0x00020A00, 0x00120A00,
            0x04020200, 0x04120200, 0x04020A00, 0x04120A00,
            0x04020200, 0x04120200, 0x04020A00, 0x04120A00,
            0x00020204, 0x00120204, 0x00020A04, 0x00120A04,
            0x00020204, 0x00120204, 0x00020A04, 0x00120A04,
            0x04020204, 0x04120204, 0x04020A04, 0x04120A04,
            0x04020204, 0x04120204, 0x04020A04, 0x04120A04,
            0x00020200, 0x00120200, 0x00020A00, 0x00120A00,
            0x00020200, 0x00120200, 0x00020A00, 0x00120A00,
            0x04020200, 0x04120200, 0x04020A00, 0x04120A00,
            0x04020200, 0x04120200, 0x04020A00, 0x04120A00,
            0x00020204, 0x00120204, 0x00020A04, 0x00120A04,
            0x00020204, 0x00120204, 0x00020A04, 0x00120A04,
            0x04020204, 0x04120204, 0x04020A04, 0x04120A04,
            0x04020204, 0x04120204, 0x04020A04, 0x04120A04
        );
        static $pc2mapd3 = array(
            0x00000000, 0x00010000, 0x02000000, 0x02010000,
            0x00000020, 0x00010020, 0x02000020, 0x02010020,
            0x00040000, 0x00050000, 0x02040000, 0x02050000,
            0x00040020, 0x00050020, 0x02040020, 0x02050020,
            0x00002000, 0x00012000, 0x02002000, 0x02012000,
            0x00002020, 0x00012020, 0x02002020, 0x02012020,
            0x00042000, 0x00052000, 0x02042000, 0x02052000,
            0x00042020, 0x00052020, 0x02042020, 0x02052020,
            0x00000000, 0x00010000, 0x02000000, 0x02010000,
            0x00000020, 0x00010020, 0x02000020, 0x02010020,
            0x00040000, 0x00050000, 0x02040000, 0x02050000,
            0x00040020, 0x00050020, 0x02040020, 0x02050020,
            0x00002000, 0x00012000, 0x02002000, 0x02012000,
            0x00002020, 0x00012020, 0x02002020, 0x02012020,
            0x00042000, 0x00052000, 0x02042000, 0x02052000,
            0x00042020, 0x00052020, 0x02042020, 0x02052020,
            0x00000010, 0x00010010, 0x02000010, 0x02010010,
            0x00000030, 0x00010030, 0x02000030, 0x02010030,
            0x00040010, 0x00050010, 0x02040010, 0x02050010,
            0x00040030, 0x00050030, 0x02040030, 0x02050030,
            0x00002010, 0x00012010, 0x02002010, 0x02012010,
            0x00002030, 0x00012030, 0x02002030, 0x02012030,
            0x00042010, 0x00052010, 0x02042010, 0x02052010,
            0x00042030, 0x00052030, 0x02042030, 0x02052030,
            0x00000010, 0x00010010, 0x02000010, 0x02010010,
            0x00000030, 0x00010030, 0x02000030, 0x02010030,
            0x00040010, 0x00050010, 0x02040010, 0x02050010,
            0x00040030, 0x00050030, 0x02040030, 0x02050030,
            0x00002010, 0x00012010, 0x02002010, 0x02012010,
            0x00002030, 0x00012030, 0x02002030, 0x02012030,
            0x00042010, 0x00052010, 0x02042010, 0x02052010,
            0x00042030, 0x00052030, 0x02042030, 0x02052030,
            0x20000000, 0x20010000, 0x22000000, 0x22010000,
            0x20000020, 0x20010020, 0x22000020, 0x22010020,
            0x20040000, 0x20050000, 0x22040000, 0x22050000,
            0x20040020, 0x20050020, 0x22040020, 0x22050020,
            0x20002000, 0x20012000, 0x22002000, 0x22012000,
            0x20002020, 0x20012020, 0x22002020, 0x22012020,
            0x20042000, 0x20052000, 0x22042000, 0x22052000,
            0x20042020, 0x20052020, 0x22042020, 0x22052020,
            0x20000000, 0x20010000, 0x22000000, 0x22010000,
            0x20000020, 0x20010020, 0x22000020, 0x22010020,
            0x20040000, 0x20050000, 0x22040000, 0x22050000,
            0x20040020, 0x20050020, 0x22040020, 0x22050020,
            0x20002000, 0x20012000, 0x22002000, 0x22012000,
            0x20002020, 0x20012020, 0x22002020, 0x22012020,
            0x20042000, 0x20052000, 0x22042000, 0x22052000,
            0x20042020, 0x20052020, 0x22042020, 0x22052020,
            0x20000010, 0x20010010, 0x22000010, 0x22010010,
            0x20000030, 0x20010030, 0x22000030, 0x22010030,
            0x20040010, 0x20050010, 0x22040010, 0x22050010,
            0x20040030, 0x20050030, 0x22040030, 0x22050030,
            0x20002010, 0x20012010, 0x22002010, 0x22012010,
            0x20002030, 0x20012030, 0x22002030, 0x22012030,
            0x20042010, 0x20052010, 0x22042010, 0x22052010,
            0x20042030, 0x20052030, 0x22042030, 0x22052030,
            0x20000010, 0x20010010, 0x22000010, 0x22010010,
            0x20000030, 0x20010030, 0x22000030, 0x22010030,
            0x20040010, 0x20050010, 0x22040010, 0x22050010,
            0x20040030, 0x20050030, 0x22040030, 0x22050030,
            0x20002010, 0x20012010, 0x22002010, 0x22012010,
            0x20002030, 0x20012030, 0x22002030, 0x22012030,
            0x20042010, 0x20052010, 0x22042010, 0x22052010,
            0x20042030, 0x20052030, 0x22042030, 0x22052030
        );
        static $pc2mapd4 = array(
            0x00000000, 0x00000400, 0x01000000, 0x01000400,
            0x00000000, 0x00000400, 0x01000000, 0x01000400,
            0x00000100, 0x00000500, 0x01000100, 0x01000500,
            0x00000100, 0x00000500, 0x01000100, 0x01000500,
            0x10000000, 0x10000400, 0x11000000, 0x11000400,
            0x10000000, 0x10000400, 0x11000000, 0x11000400,
            0x10000100, 0x10000500, 0x11000100, 0x11000500,
            0x10000100, 0x10000500, 0x11000100, 0x11000500,
            0x00080000, 0x00080400, 0x01080000, 0x01080400,
            0x00080000, 0x00080400, 0x01080000, 0x01080400,
            0x00080100, 0x00080500, 0x01080100, 0x01080500,
            0x00080100, 0x00080500, 0x01080100, 0x01080500,
            0x10080000, 0x10080400, 0x11080000, 0x11080400,
            0x10080000, 0x10080400, 0x11080000, 0x11080400,
            0x10080100, 0x10080500, 0x11080100, 0x11080500,
            0x10080100, 0x10080500, 0x11080100, 0x11080500,
            0x00000008, 0x00000408, 0x01000008, 0x01000408,
            0x00000008, 0x00000408, 0x01000008, 0x01000408,
            0x00000108, 0x00000508, 0x01000108, 0x01000508,
            0x00000108, 0x00000508, 0x01000108, 0x01000508,
            0x10000008, 0x10000408, 0x11000008, 0x11000408,
            0x10000008, 0x10000408, 0x11000008, 0x11000408,
            0x10000108, 0x10000508, 0x11000108, 0x11000508,
            0x10000108, 0x10000508, 0x11000108, 0x11000508,
            0x00080008, 0x00080408, 0x01080008, 0x01080408,
            0x00080008, 0x00080408, 0x01080008, 0x01080408,
            0x00080108, 0x00080508, 0x01080108, 0x01080508,
            0x00080108, 0x00080508, 0x01080108, 0x01080508,
            0x10080008, 0x10080408, 0x11080008, 0x11080408,
            0x10080008, 0x10080408, 0x11080008, 0x11080408,
            0x10080108, 0x10080508, 0x11080108, 0x11080508,
            0x10080108, 0x10080508, 0x11080108, 0x11080508,
            0x00001000, 0x00001400, 0x01001000, 0x01001400,
            0x00001000, 0x00001400, 0x01001000, 0x01001400,
            0x00001100, 0x00001500, 0x01001100, 0x01001500,
            0x00001100, 0x00001500, 0x01001100, 0x01001500,
            0x10001000, 0x10001400, 0x11001000, 0x11001400,
            0x10001000, 0x10001400, 0x11001000, 0x11001400,
            0x10001100, 0x10001500, 0x11001100, 0x11001500,
            0x10001100, 0x10001500, 0x11001100, 0x11001500,
            0x00081000, 0x00081400, 0x01081000, 0x01081400,
            0x00081000, 0x00081400, 0x01081000, 0x01081400,
            0x00081100, 0x00081500, 0x01081100, 0x01081500,
            0x00081100, 0x00081500, 0x01081100, 0x01081500,
            0x10081000, 0x10081400, 0x11081000, 0x11081400,
            0x10081000, 0x10081400, 0x11081000, 0x11081400,
            0x10081100, 0x10081500, 0x11081100, 0x11081500,
            0x10081100, 0x10081500, 0x11081100, 0x11081500,
            0x00001008, 0x00001408, 0x01001008, 0x01001408,
            0x00001008, 0x00001408, 0x01001008, 0x01001408,
            0x00001108, 0x00001508, 0x01001108, 0x01001508,
            0x00001108, 0x00001508, 0x01001108, 0x01001508,
            0x10001008, 0x10001408, 0x11001008, 0x11001408,
            0x10001008, 0x10001408, 0x11001008, 0x11001408,
            0x10001108, 0x10001508, 0x11001108, 0x11001508,
            0x10001108, 0x10001508, 0x11001108, 0x11001508,
            0x00081008, 0x00081408, 0x01081008, 0x01081408,
            0x00081008, 0x00081408, 0x01081008, 0x01081408,
            0x00081108, 0x00081508, 0x01081108, 0x01081508,
            0x00081108, 0x00081508, 0x01081108, 0x01081508,
            0x10081008, 0x10081408, 0x11081008, 0x11081408,
            0x10081008, 0x10081408, 0x11081008, 0x11081408,
            0x10081108, 0x10081508, 0x11081108, 0x11081508,
            0x10081108, 0x10081508, 0x11081108, 0x11081508
        );

        $keys = array();
        for ($des_round = 0; $des_round < $this->des_rounds; ++$des_round) {
            // pad the key and remove extra characters as appropriate.
            $key = str_pad(substr($this->key, $des_round * 8, 8), 8, "\0");

            // Perform the PC/1 transformation and compute C and D.
            $t = unpack('Nl/Nr', $key);
            list($l, $r) = array($t['l'], $t['r']);
            $key = ($this->shuffle[$pc1map[ $r        & 0xFF]] & "\x80\x80\x80\x80\x80\x80\x80\x00") |
                   ($this->shuffle[$pc1map[($r >>  8) & 0xFF]] & "\x40\x40\x40\x40\x40\x40\x40\x00") |
                   ($this->shuffle[$pc1map[($r >> 16) & 0xFF]] & "\x20\x20\x20\x20\x20\x20\x20\x00") |
                   ($this->shuffle[$pc1map[($r >> 24) & 0xFF]] & "\x10\x10\x10\x10\x10\x10\x10\x00") |
                   ($this->shuffle[$pc1map[ $l        & 0xFF]] & "\x08\x08\x08\x08\x08\x08\x08\x00") |
                   ($this->shuffle[$pc1map[($l >>  8) & 0xFF]] & "\x04\x04\x04\x04\x04\x04\x04\x00") |
                   ($this->shuffle[$pc1map[($l >> 16) & 0xFF]] & "\x02\x02\x02\x02\x02\x02\x02\x00") |
                   ($this->shuffle[$pc1map[($l >> 24) & 0xFF]] & "\x01\x01\x01\x01\x01\x01\x01\x00");
            $key = unpack('Nc/Nd', $key);
            $c = ( $key['c'] >> 4) & 0x0FFFFFFF;
            $d = (($key['d'] >> 4) & 0x0FFFFFF0) | ($key['c'] & 0x0F);

            $keys[$des_round] = array(
                CRYPT_DES_ENCRYPT => array(),
                CRYPT_DES_DECRYPT => array_fill(0, 32, 0)
            );
            for ($i = 0, $ki = 31; $i < 16; ++$i, $ki-= 2) {
                $c <<= $shifts[$i];
                $c = ($c | ($c >> 28)) & 0x0FFFFFFF;
                $d <<= $shifts[$i];
                $d = ($d | ($d >> 28)) & 0x0FFFFFFF;

                // Perform the PC-2 transformation.
                $cp = $pc2mapc1[ $c >> 24        ] | $pc2mapc2[($c >> 16) & 0xFF] |
                      $pc2mapc3[($c >>  8) & 0xFF] | $pc2mapc4[ $c        & 0xFF];
                $dp = $pc2mapd1[ $d >> 24        ] | $pc2mapd2[($d >> 16) & 0xFF] |
                      $pc2mapd3[($d >>  8) & 0xFF] | $pc2mapd4[ $d        & 0xFF];

                // Reorder: odd bytes/even bytes. Push the result in key schedule.
                $val1 = ( $cp        & 0xFF000000) | (($cp <<  8) & 0x00FF0000) |
                        (($dp >> 16) & 0x0000FF00) | (($dp >>  8) & 0x000000FF);
                $val2 = (($cp <<  8) & 0xFF000000) | (($cp << 16) & 0x00FF0000) |
                        (($dp >>  8) & 0x0000FF00) | ( $dp        & 0x000000FF);
                $keys[$des_round][CRYPT_DES_ENCRYPT][       ] = $val1;
                $keys[$des_round][CRYPT_DES_DECRYPT][$ki - 1] = $val1;
                $keys[$des_round][CRYPT_DES_ENCRYPT][       ] = $val2;
                $keys[$des_round][CRYPT_DES_DECRYPT][$ki    ] = $val2;
            }
        }

        switch ($this->des_rounds) {
            case 3: // 3DES keys
                $this->keys = array(
                    CRYPT_DES_ENCRYPT => array_merge(
                        $keys[0][CRYPT_DES_ENCRYPT],
                        $keys[1][CRYPT_DES_DECRYPT],
                        $keys[2][CRYPT_DES_ENCRYPT]
                    ),
                    CRYPT_DES_DECRYPT => array_merge(
                        $keys[2][CRYPT_DES_DECRYPT],
                        $keys[1][CRYPT_DES_ENCRYPT],
                        $keys[0][CRYPT_DES_DECRYPT]
                    )
                );
                break;
            // case 1: // DES keys
            default:
                $this->keys = array(
                    CRYPT_DES_ENCRYPT => $keys[0][CRYPT_DES_ENCRYPT],
                    CRYPT_DES_DECRYPT => $keys[0][CRYPT_DES_DECRYPT]
                );
        }
    }

    /**
     * Setup the performance-optimized function for de/encrypt()
     *
     * @see Crypt_Base::_setupInlineCrypt()
     * @access private
     */
    function _setupInlineCrypt()
    {
        $lambda_functions =& Crypt_DES::_getLambdaFunctions();

        // Engine configuration for:
        // -  DES ($des_rounds == 1) or
        // - 3DES ($des_rounds == 3)
        $des_rounds = $this->des_rounds;

        // We create max. 10 hi-optimized code for memory reason. Means: For each $key one ultra fast inline-crypt function.
        // (Currently, for Crypt_DES,       one generated $lambda_function cost on php5.5@32bit ~135kb unfreeable mem and ~230kb on php5.5@64bit)
        // (Currently, for Crypt_TripleDES, one generated $lambda_function cost on php5.5@32bit ~240kb unfreeable mem and ~340kb on php5.5@64bit)
        // After that, we'll still create very fast optimized code but not the hi-ultimative code, for each $mode one
        $gen_hi_opt_code = (bool)( count($lambda_functions) < 10 );

        // Generation of a unique hash for our generated code
        $code_hash = "Crypt_DES, $des_rounds, {$this->mode}";
        if ($gen_hi_opt_code) {
            // For hi-optimized code, we create for each combination of
            // $mode, $des_rounds and $this->key its own encrypt/decrypt function.
            // After max 10 hi-optimized functions, we create generic
            // (still very fast.. but not ultra) functions for each $mode/$des_rounds
            // Currently 2 * 5 generic functions will be then max. possible.
            $code_hash = str_pad($code_hash, 32) . $this->_hashInlineCryptFunction($this->key);
        }

        // Is there a re-usable $lambda_functions in there? If not, we have to create it.
        if (!isset($lambda_functions[$code_hash])) {
            // Init code for both, encrypt and decrypt.
            $init_crypt = 'static $sbox1, $sbox2, $sbox3, $sbox4, $sbox5, $sbox6, $sbox7, $sbox8, $shuffleip, $shuffleinvip;
                if (!$sbox1) {
                    $sbox1 = array_map("intval", $self->sbox1);
                    $sbox2 = array_map("intval", $self->sbox2);
                    $sbox3 = array_map("intval", $self->sbox3);
                    $sbox4 = array_map("intval", $self->sbox4);
                    $sbox5 = array_map("intval", $self->sbox5);
                    $sbox6 = array_map("intval", $self->sbox6);
                    $sbox7 = array_map("intval", $self->sbox7);
                    $sbox8 = array_map("intval", $self->sbox8);'
                    /* Merge $shuffle with $[inv]ipmap */ . '
                    for ($i = 0; $i < 256; ++$i) {
                        $shuffleip[]    =  $self->shuffle[$self->ipmap[$i]];
                        $shuffleinvip[] =  $self->shuffle[$self->invipmap[$i]];
                    }
                }
            ';

            switch (true) {
                case $gen_hi_opt_code:
                    // In Hi-optimized code mode, we use our [3]DES key schedule as hardcoded integers.
                    // No futher initialisation of the $keys schedule is necessary.
                    // That is the extra performance boost.
                    $k = array(
                        CRYPT_DES_ENCRYPT => $this->keys[CRYPT_DES_ENCRYPT],
                        CRYPT_DES_DECRYPT => $this->keys[CRYPT_DES_DECRYPT]
                    );
                    $init_encrypt = '';
                    $init_decrypt = '';
                    break;
                default:
                    // In generic optimized code mode, we have to use, as the best compromise [currently],
                    // our key schedule as $ke/$kd arrays. (with hardcoded indexes...)
                    $k = array(
                        CRYPT_DES_ENCRYPT => array(),
                        CRYPT_DES_DECRYPT => array()
                    );
                    for ($i = 0, $c = count($this->keys[CRYPT_DES_ENCRYPT]); $i < $c; ++$i) {
                        $k[CRYPT_DES_ENCRYPT][$i] = '$ke[' . $i . ']';
                        $k[CRYPT_DES_DECRYPT][$i] = '$kd[' . $i . ']';
                    }
                    $init_encrypt = '$ke = $self->keys[CRYPT_DES_ENCRYPT];';
                    $init_decrypt = '$kd = $self->keys[CRYPT_DES_DECRYPT];';
                    break;
            }

            // Creating code for en- and decryption.
            $crypt_block = array();
            foreach (array(CRYPT_DES_ENCRYPT, CRYPT_DES_DECRYPT) as $c) {
                /* Do the initial IP permutation. */
                $crypt_block[$c] = '
                    $in = unpack("N*", $in);
                    $l  = $in[1];
                    $r  = $in[2];
                    $in = unpack("N*",
                        ($shuffleip[ $r        & 0xFF] & "\x80\x80\x80\x80\x80\x80\x80\x80") |
                        ($shuffleip[($r >>  8) & 0xFF] & "\x40\x40\x40\x40\x40\x40\x40\x40") |
                        ($shuffleip[($r >> 16) & 0xFF] & "\x20\x20\x20\x20\x20\x20\x20\x20") |
                        ($shuffleip[($r >> 24) & 0xFF] & "\x10\x10\x10\x10\x10\x10\x10\x10") |
                        ($shuffleip[ $l        & 0xFF] & "\x08\x08\x08\x08\x08\x08\x08\x08") |
                        ($shuffleip[($l >>  8) & 0xFF] & "\x04\x04\x04\x04\x04\x04\x04\x04") |
                        ($shuffleip[($l >> 16) & 0xFF] & "\x02\x02\x02\x02\x02\x02\x02\x02") |
                        ($shuffleip[($l >> 24) & 0xFF] & "\x01\x01\x01\x01\x01\x01\x01\x01")
                    );
                    ' . /* Extract L0 and R0 */ '
                    $l = $in[1];
                    $r = $in[2];
                ';

                $l = '$l';
                $r = '$r';

                // Perform DES or 3DES.
                for ($ki = -1, $des_round = 0; $des_round < $des_rounds; ++$des_round) {
                    // Perform the 16 steps.
                    for ($i = 0; $i < 16; ++$i) {
                        // start of "the Feistel (F) function" - see the following URL:
                        // http://en.wikipedia.org/wiki/Image:Data_Encryption_Standard_InfoBox_Diagram.png
                        // Merge key schedule.
                        $crypt_block[$c].= '
                            $b1 = ((' . $r . ' >>  3) & 0x1FFFFFFF)  ^ (' . $r . ' << 29) ^ ' . $k[$c][++$ki] . ';
                            $b2 = ((' . $r . ' >> 31) & 0x00000001)  ^ (' . $r . ' <<  1) ^ ' . $k[$c][++$ki] . ';' .
                            /* S-box indexing. */
                            $l . ' = $sbox1[($b1 >> 24) & 0x3F] ^ $sbox2[($b2 >> 24) & 0x3F] ^
                                     $sbox3[($b1 >> 16) & 0x3F] ^ $sbox4[($b2 >> 16) & 0x3F] ^
                                     $sbox5[($b1 >>  8) & 0x3F] ^ $sbox6[($b2 >>  8) & 0x3F] ^
                                     $sbox7[ $b1        & 0x3F] ^ $sbox8[ $b2        & 0x3F] ^ ' . $l . ';
                        ';
                        // end of "the Feistel (F) function"

                        // swap L & R
                        list($l, $r) = array($r, $l);
                    }
                    list($l, $r) = array($r, $l);
                }

                // Perform the inverse IP permutation.
                $crypt_block[$c].= '$in =
                    ($shuffleinvip[($l >> 24) & 0xFF] & "\x80\x80\x80\x80\x80\x80\x80\x80") |
                    ($shuffleinvip[($r >> 24) & 0xFF] & "\x40\x40\x40\x40\x40\x40\x40\x40") |
                    ($shuffleinvip[($l >> 16) & 0xFF] & "\x20\x20\x20\x20\x20\x20\x20\x20") |
                    ($shuffleinvip[($r >> 16) & 0xFF] & "\x10\x10\x10\x10\x10\x10\x10\x10") |
                    ($shuffleinvip[($l >>  8) & 0xFF] & "\x08\x08\x08\x08\x08\x08\x08\x08") |
                    ($shuffleinvip[($r >>  8) & 0xFF] & "\x04\x04\x04\x04\x04\x04\x04\x04") |
                    ($shuffleinvip[ $l        & 0xFF] & "\x02\x02\x02\x02\x02\x02\x02\x02") |
                    ($shuffleinvip[ $r        & 0xFF] & "\x01\x01\x01\x01\x01\x01\x01\x01");
                ';
            }

            // Creates the inline-crypt function
            $lambda_functions[$code_hash] = $this->_createInlineCryptFunction(
                array(
                   'init_crypt'    => $init_crypt,
                   'init_encrypt'  => $init_encrypt,
                   'init_decrypt'  => $init_decrypt,
                   'encrypt_block' => $crypt_block[CRYPT_DES_ENCRYPT],
                   'decrypt_block' => $crypt_block[CRYPT_DES_DECRYPT]
                )
            );
        }

        // Set the inline-crypt function as callback in: $this->inline_crypt
        $this->inline_crypt = $lambda_functions[$code_hash];
    }
}
vendor/phpseclib/phpseclib/phpseclib/Crypt/RC2.php000064400000060470151327705700016152 0ustar00<?php

/**
 * Pure-PHP implementation of RC2.
 *
 * Uses mcrypt, if available, and an internal implementation, otherwise.
 *
 * PHP versions 4 and 5
 *
 * Useful resources are as follows:
 *
 *  - {@link http://tools.ietf.org/html/rfc2268}
 *
 * Here's a short example of how to use this library:
 * <code>
 * <?php
 *    include 'Crypt/RC2.php';
 *
 *    $rc2 = new Crypt_RC2();
 *
 *    $rc2->setKey('abcdefgh');
 *
 *    $plaintext = str_repeat('a', 1024);
 *
 *    echo $rc2->decrypt($rc2->encrypt($plaintext));
 * ?>
 * </code>
 *
 * LICENSE: Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 *
 * @category Crypt
 * @package  Crypt_RC2
 * @author   Patrick Monnerat <pm@datasphere.ch>
 * @license  http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link     http://phpseclib.sourceforge.net
 */

/**
 * Include Crypt_Base
 *
 * Base cipher class
 */
if (!class_exists('Crypt_Base')) {
    include_once 'Base.php';
}

/**#@+
 * @access public
 * @see self::encrypt()
 * @see self::decrypt()
 */
/**
 * Encrypt / decrypt using the Counter mode.
 *
 * Set to -1 since that's what Crypt/Random.php uses to index the CTR mode.
 *
 * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Counter_.28CTR.29
 */
define('CRYPT_RC2_MODE_CTR', CRYPT_MODE_CTR);
/**
 * Encrypt / decrypt using the Electronic Code Book mode.
 *
 * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Electronic_codebook_.28ECB.29
 */
define('CRYPT_RC2_MODE_ECB', CRYPT_MODE_ECB);
/**
 * Encrypt / decrypt using the Code Book Chaining mode.
 *
 * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Cipher-block_chaining_.28CBC.29
 */
define('CRYPT_RC2_MODE_CBC', CRYPT_MODE_CBC);
/**
 * Encrypt / decrypt using the Cipher Feedback mode.
 *
 * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Cipher_feedback_.28CFB.29
 */
define('CRYPT_RC2_MODE_CFB', CRYPT_MODE_CFB);
/**
 * Encrypt / decrypt using the Cipher Feedback mode.
 *
 * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Output_feedback_.28OFB.29
 */
define('CRYPT_RC2_MODE_OFB', CRYPT_MODE_OFB);
/**#@-*/

/**
 * Pure-PHP implementation of RC2.
 *
 * @package Crypt_RC2
 * @access  public
 */
class Crypt_RC2 extends Crypt_Base
{
    /**
     * Block Length of the cipher
     *
     * @see Crypt_Base::block_size
     * @var int
     * @access private
     */
    var $block_size = 8;

    /**
     * The Key
     *
     * @see Crypt_Base::key
     * @see self::setKey()
     * @var string
     * @access private
     */
    var $key;

    /**
     * The Original (unpadded) Key
     *
     * @see Crypt_Base::key
     * @see self::setKey()
     * @see self::encrypt()
     * @see self::decrypt()
     * @var string
     * @access private
     */
    var $orig_key;

    /**
     * Don't truncate / null pad key
     *
     * @see Crypt_Base::_clearBuffers()
     * @var bool
     * @access private
     */
    var $skip_key_adjustment = true;

    /**
     * Key Length (in bytes)
     *
     * @see Crypt_RC2::setKeyLength()
     * @var int
     * @access private
     */
    var $key_length = 16; // = 128 bits

    /**
     * The namespace used by the cipher for its constants.
     *
     * @see Crypt_Base::const_namespace
     * @var string
     * @access private
     */
    var $const_namespace = 'RC2';

    /**
     * The mcrypt specific name of the cipher
     *
     * @see Crypt_Base::cipher_name_mcrypt
     * @var string
     * @access private
     */
    var $cipher_name_mcrypt = 'rc2';

    /**
     * Optimizing value while CFB-encrypting
     *
     * @see Crypt_Base::cfb_init_len
     * @var int
     * @access private
     */
    var $cfb_init_len = 500;

    /**
     * The key length in bits.
     *
     * @see self::setKeyLength()
     * @see self::setKey()
     * @var int
     * @access private
     * @internal Should be in range [1..1024].
     * @internal Changing this value after setting the key has no effect.
     */
    var $default_key_length = 1024;

    /**
     * The key length in bits.
     *
     * @see self::isValidEnine()
     * @see self::setKey()
     * @var int
     * @access private
     * @internal Should be in range [1..1024].
     */
    var $current_key_length;

    /**
     * The Key Schedule
     *
     * @see self::_setupKey()
     * @var array
     * @access private
     */
    var $keys;

    /**
     * Key expansion randomization table.
     * Twice the same 256-value sequence to save a modulus in key expansion.
     *
     * @see self::setKey()
     * @var array
     * @access private
     */
    var $pitable = array(
        0xD9, 0x78, 0xF9, 0xC4, 0x19, 0xDD, 0xB5, 0xED,
        0x28, 0xE9, 0xFD, 0x79, 0x4A, 0xA0, 0xD8, 0x9D,
        0xC6, 0x7E, 0x37, 0x83, 0x2B, 0x76, 0x53, 0x8E,
        0x62, 0x4C, 0x64, 0x88, 0x44, 0x8B, 0xFB, 0xA2,
        0x17, 0x9A, 0x59, 0xF5, 0x87, 0xB3, 0x4F, 0x13,
        0x61, 0x45, 0x6D, 0x8D, 0x09, 0x81, 0x7D, 0x32,
        0xBD, 0x8F, 0x40, 0xEB, 0x86, 0xB7, 0x7B, 0x0B,
        0xF0, 0x95, 0x21, 0x22, 0x5C, 0x6B, 0x4E, 0x82,
        0x54, 0xD6, 0x65, 0x93, 0xCE, 0x60, 0xB2, 0x1C,
        0x73, 0x56, 0xC0, 0x14, 0xA7, 0x8C, 0xF1, 0xDC,
        0x12, 0x75, 0xCA, 0x1F, 0x3B, 0xBE, 0xE4, 0xD1,
        0x42, 0x3D, 0xD4, 0x30, 0xA3, 0x3C, 0xB6, 0x26,
        0x6F, 0xBF, 0x0E, 0xDA, 0x46, 0x69, 0x07, 0x57,
        0x27, 0xF2, 0x1D, 0x9B, 0xBC, 0x94, 0x43, 0x03,
        0xF8, 0x11, 0xC7, 0xF6, 0x90, 0xEF, 0x3E, 0xE7,
        0x06, 0xC3, 0xD5, 0x2F, 0xC8, 0x66, 0x1E, 0xD7,
        0x08, 0xE8, 0xEA, 0xDE, 0x80, 0x52, 0xEE, 0xF7,
        0x84, 0xAA, 0x72, 0xAC, 0x35, 0x4D, 0x6A, 0x2A,
        0x96, 0x1A, 0xD2, 0x71, 0x5A, 0x15, 0x49, 0x74,
        0x4B, 0x9F, 0xD0, 0x5E, 0x04, 0x18, 0xA4, 0xEC,
        0xC2, 0xE0, 0x41, 0x6E, 0x0F, 0x51, 0xCB, 0xCC,
        0x24, 0x91, 0xAF, 0x50, 0xA1, 0xF4, 0x70, 0x39,
        0x99, 0x7C, 0x3A, 0x85, 0x23, 0xB8, 0xB4, 0x7A,
        0xFC, 0x02, 0x36, 0x5B, 0x25, 0x55, 0x97, 0x31,
        0x2D, 0x5D, 0xFA, 0x98, 0xE3, 0x8A, 0x92, 0xAE,
        0x05, 0xDF, 0x29, 0x10, 0x67, 0x6C, 0xBA, 0xC9,
        0xD3, 0x00, 0xE6, 0xCF, 0xE1, 0x9E, 0xA8, 0x2C,
        0x63, 0x16, 0x01, 0x3F, 0x58, 0xE2, 0x89, 0xA9,
        0x0D, 0x38, 0x34, 0x1B, 0xAB, 0x33, 0xFF, 0xB0,
        0xBB, 0x48, 0x0C, 0x5F, 0xB9, 0xB1, 0xCD, 0x2E,
        0xC5, 0xF3, 0xDB, 0x47, 0xE5, 0xA5, 0x9C, 0x77,
        0x0A, 0xA6, 0x20, 0x68, 0xFE, 0x7F, 0xC1, 0xAD,
        0xD9, 0x78, 0xF9, 0xC4, 0x19, 0xDD, 0xB5, 0xED,
        0x28, 0xE9, 0xFD, 0x79, 0x4A, 0xA0, 0xD8, 0x9D,
        0xC6, 0x7E, 0x37, 0x83, 0x2B, 0x76, 0x53, 0x8E,
        0x62, 0x4C, 0x64, 0x88, 0x44, 0x8B, 0xFB, 0xA2,
        0x17, 0x9A, 0x59, 0xF5, 0x87, 0xB3, 0x4F, 0x13,
        0x61, 0x45, 0x6D, 0x8D, 0x09, 0x81, 0x7D, 0x32,
        0xBD, 0x8F, 0x40, 0xEB, 0x86, 0xB7, 0x7B, 0x0B,
        0xF0, 0x95, 0x21, 0x22, 0x5C, 0x6B, 0x4E, 0x82,
        0x54, 0xD6, 0x65, 0x93, 0xCE, 0x60, 0xB2, 0x1C,
        0x73, 0x56, 0xC0, 0x14, 0xA7, 0x8C, 0xF1, 0xDC,
        0x12, 0x75, 0xCA, 0x1F, 0x3B, 0xBE, 0xE4, 0xD1,
        0x42, 0x3D, 0xD4, 0x30, 0xA3, 0x3C, 0xB6, 0x26,
        0x6F, 0xBF, 0x0E, 0xDA, 0x46, 0x69, 0x07, 0x57,
        0x27, 0xF2, 0x1D, 0x9B, 0xBC, 0x94, 0x43, 0x03,
        0xF8, 0x11, 0xC7, 0xF6, 0x90, 0xEF, 0x3E, 0xE7,
        0x06, 0xC3, 0xD5, 0x2F, 0xC8, 0x66, 0x1E, 0xD7,
        0x08, 0xE8, 0xEA, 0xDE, 0x80, 0x52, 0xEE, 0xF7,
        0x84, 0xAA, 0x72, 0xAC, 0x35, 0x4D, 0x6A, 0x2A,
        0x96, 0x1A, 0xD2, 0x71, 0x5A, 0x15, 0x49, 0x74,
        0x4B, 0x9F, 0xD0, 0x5E, 0x04, 0x18, 0xA4, 0xEC,
        0xC2, 0xE0, 0x41, 0x6E, 0x0F, 0x51, 0xCB, 0xCC,
        0x24, 0x91, 0xAF, 0x50, 0xA1, 0xF4, 0x70, 0x39,
        0x99, 0x7C, 0x3A, 0x85, 0x23, 0xB8, 0xB4, 0x7A,
        0xFC, 0x02, 0x36, 0x5B, 0x25, 0x55, 0x97, 0x31,
        0x2D, 0x5D, 0xFA, 0x98, 0xE3, 0x8A, 0x92, 0xAE,
        0x05, 0xDF, 0x29, 0x10, 0x67, 0x6C, 0xBA, 0xC9,
        0xD3, 0x00, 0xE6, 0xCF, 0xE1, 0x9E, 0xA8, 0x2C,
        0x63, 0x16, 0x01, 0x3F, 0x58, 0xE2, 0x89, 0xA9,
        0x0D, 0x38, 0x34, 0x1B, 0xAB, 0x33, 0xFF, 0xB0,
        0xBB, 0x48, 0x0C, 0x5F, 0xB9, 0xB1, 0xCD, 0x2E,
        0xC5, 0xF3, 0xDB, 0x47, 0xE5, 0xA5, 0x9C, 0x77,
        0x0A, 0xA6, 0x20, 0x68, 0xFE, 0x7F, 0xC1, 0xAD
    );

    /**
     * Inverse key expansion randomization table.
     *
     * @see self::setKey()
     * @var array
     * @access private
     */
    var $invpitable = array(
        0xD1, 0xDA, 0xB9, 0x6F, 0x9C, 0xC8, 0x78, 0x66,
        0x80, 0x2C, 0xF8, 0x37, 0xEA, 0xE0, 0x62, 0xA4,
        0xCB, 0x71, 0x50, 0x27, 0x4B, 0x95, 0xD9, 0x20,
        0x9D, 0x04, 0x91, 0xE3, 0x47, 0x6A, 0x7E, 0x53,
        0xFA, 0x3A, 0x3B, 0xB4, 0xA8, 0xBC, 0x5F, 0x68,
        0x08, 0xCA, 0x8F, 0x14, 0xD7, 0xC0, 0xEF, 0x7B,
        0x5B, 0xBF, 0x2F, 0xE5, 0xE2, 0x8C, 0xBA, 0x12,
        0xE1, 0xAF, 0xB2, 0x54, 0x5D, 0x59, 0x76, 0xDB,
        0x32, 0xA2, 0x58, 0x6E, 0x1C, 0x29, 0x64, 0xF3,
        0xE9, 0x96, 0x0C, 0x98, 0x19, 0x8D, 0x3E, 0x26,
        0xAB, 0xA5, 0x85, 0x16, 0x40, 0xBD, 0x49, 0x67,
        0xDC, 0x22, 0x94, 0xBB, 0x3C, 0xC1, 0x9B, 0xEB,
        0x45, 0x28, 0x18, 0xD8, 0x1A, 0x42, 0x7D, 0xCC,
        0xFB, 0x65, 0x8E, 0x3D, 0xCD, 0x2A, 0xA3, 0x60,
        0xAE, 0x93, 0x8A, 0x48, 0x97, 0x51, 0x15, 0xF7,
        0x01, 0x0B, 0xB7, 0x36, 0xB1, 0x2E, 0x11, 0xFD,
        0x84, 0x2D, 0x3F, 0x13, 0x88, 0xB3, 0x34, 0x24,
        0x1B, 0xDE, 0xC5, 0x1D, 0x4D, 0x2B, 0x17, 0x31,
        0x74, 0xA9, 0xC6, 0x43, 0x6D, 0x39, 0x90, 0xBE,
        0xC3, 0xB0, 0x21, 0x6B, 0xF6, 0x0F, 0xD5, 0x99,
        0x0D, 0xAC, 0x1F, 0x5C, 0x9E, 0xF5, 0xF9, 0x4C,
        0xD6, 0xDF, 0x89, 0xE4, 0x8B, 0xFF, 0xC7, 0xAA,
        0xE7, 0xED, 0x46, 0x25, 0xB6, 0x06, 0x5E, 0x35,
        0xB5, 0xEC, 0xCE, 0xE8, 0x6C, 0x30, 0x55, 0x61,
        0x4A, 0xFE, 0xA0, 0x79, 0x03, 0xF0, 0x10, 0x72,
        0x7C, 0xCF, 0x52, 0xA6, 0xA7, 0xEE, 0x44, 0xD3,
        0x9A, 0x57, 0x92, 0xD0, 0x5A, 0x7A, 0x41, 0x7F,
        0x0E, 0x00, 0x63, 0xF2, 0x4F, 0x05, 0x83, 0xC9,
        0xA1, 0xD4, 0xDD, 0xC4, 0x56, 0xF4, 0xD2, 0x77,
        0x81, 0x09, 0x82, 0x33, 0x9F, 0x07, 0x86, 0x75,
        0x38, 0x4E, 0x69, 0xF1, 0xAD, 0x23, 0x73, 0x87,
        0x70, 0x02, 0xC2, 0x1E, 0xB8, 0x0A, 0xFC, 0xE6
    );

    /**
     * Test for engine validity
     *
     * This is mainly just a wrapper to set things up for Crypt_Base::isValidEngine()
     *
     * @see Crypt_Base::Crypt_Base()
     * @param int $engine
     * @access public
     * @return bool
     */
    function isValidEngine($engine)
    {
        switch ($engine) {
            case CRYPT_ENGINE_OPENSSL:
                if ($this->current_key_length != 128 || strlen($this->orig_key) < 16) {
                    return false;
                }
                $this->cipher_name_openssl_ecb = 'rc2-ecb';
                $this->cipher_name_openssl = 'rc2-' . $this->_openssl_translate_mode();
        }

        return parent::isValidEngine($engine);
    }

    /**
     * Sets the key length.
     *
     * Valid key lengths are 8 to 1024.
     * Calling this function after setting the key has no effect until the next
     *  Crypt_RC2::setKey() call.
     *
     * @access public
     * @param int $length in bits
     */
    function setKeyLength($length)
    {
        if ($length < 8) {
            $this->default_key_length = 1;
        } elseif ($length > 1024) {
            $this->default_key_length = 128;
        } else {
            $this->default_key_length = $length;
        }
        $this->current_key_length = $this->default_key_length;

        parent::setKeyLength($length);
    }

    /**
     * Returns the current key length
     *
     * @access public
     * @return int
     */
    function getKeyLength()
    {
        return $this->current_key_length;
    }

    /**
     * Sets the key.
     *
     * Keys can be of any length. RC2, itself, uses 8 to 1024 bit keys (eg.
     * strlen($key) <= 128), however, we only use the first 128 bytes if $key
     * has more then 128 bytes in it, and set $key to a single null byte if
     * it is empty.
     *
     * If the key is not explicitly set, it'll be assumed to be a single
     * null byte.
     *
     * @see Crypt_Base::setKey()
     * @access public
     * @param string $key
     * @param int $t1 optional Effective key length in bits.
     */
    function setKey($key, $t1 = 0)
    {
        $this->orig_key = $key;

        if ($t1 <= 0) {
            $t1 = $this->default_key_length;
        } elseif ($t1 > 1024) {
            $t1 = 1024;
        }
        $this->current_key_length = $t1;
        // Key byte count should be 1..128.
        $key = strlen($key) ? substr($key, 0, 128) : "\x00";
        $t = strlen($key);

        // The mcrypt RC2 implementation only supports effective key length
        // of 1024 bits. It is however possible to handle effective key
        // lengths in range 1..1024 by expanding the key and applying
        // inverse pitable mapping to the first byte before submitting it
        // to mcrypt.

        // Key expansion.
        $l = array_values(unpack('C*', $key));
        $t8 = ($t1 + 7) >> 3;
        $tm = 0xFF >> (8 * $t8 - $t1);

        // Expand key.
        $pitable = $this->pitable;
        for ($i = $t; $i < 128; $i++) {
            $l[$i] = $pitable[$l[$i - 1] + $l[$i - $t]];
        }
        $i = 128 - $t8;
        $l[$i] = $pitable[$l[$i] & $tm];
        while ($i--) {
            $l[$i] = $pitable[$l[$i + 1] ^ $l[$i + $t8]];
        }

        // Prepare the key for mcrypt.
        $l[0] = $this->invpitable[$l[0]];
        array_unshift($l, 'C*');

        parent::setKey(call_user_func_array('pack', $l));
    }

    /**
     * Encrypts a message.
     *
     * Mostly a wrapper for Crypt_Base::encrypt, with some additional OpenSSL handling code
     *
     * @see self::decrypt()
     * @access public
     * @param string $plaintext
     * @return string $ciphertext
     */
    function encrypt($plaintext)
    {
        if ($this->engine == CRYPT_ENGINE_OPENSSL) {
            $temp = $this->key;
            $this->key = $this->orig_key;
            $result = parent::encrypt($plaintext);
            $this->key = $temp;
            return $result;
        }

        return parent::encrypt($plaintext);
    }

    /**
     * Decrypts a message.
     *
     * Mostly a wrapper for Crypt_Base::decrypt, with some additional OpenSSL handling code
     *
     * @see self::encrypt()
     * @access public
     * @param string $ciphertext
     * @return string $plaintext
     */
    function decrypt($ciphertext)
    {
        if ($this->engine == CRYPT_ENGINE_OPENSSL) {
            $temp = $this->key;
            $this->key = $this->orig_key;
            $result = parent::decrypt($ciphertext);
            $this->key = $temp;
            return $result;
        }

        return parent::decrypt($ciphertext);
    }

    /**
     * Encrypts a block
     *
     * @see Crypt_Base::_encryptBlock()
     * @see Crypt_Base::encrypt()
     * @access private
     * @param string $in
     * @return string
     */
    function _encryptBlock($in)
    {
        list($r0, $r1, $r2, $r3) = array_values(unpack('v*', $in));
        $keys = $this->keys;
        $limit = 20;
        $actions = array($limit => 44, 44 => 64);
        $j = 0;

        for (;;) {
            // Mixing round.
            $r0 = (($r0 + $keys[$j++] + ((($r1 ^ $r2) & $r3) ^ $r1)) & 0xFFFF) << 1;
            $r0 |= $r0 >> 16;
            $r1 = (($r1 + $keys[$j++] + ((($r2 ^ $r3) & $r0) ^ $r2)) & 0xFFFF) << 2;
            $r1 |= $r1 >> 16;
            $r2 = (($r2 + $keys[$j++] + ((($r3 ^ $r0) & $r1) ^ $r3)) & 0xFFFF) << 3;
            $r2 |= $r2 >> 16;
            $r3 = (($r3 + $keys[$j++] + ((($r0 ^ $r1) & $r2) ^ $r0)) & 0xFFFF) << 5;
            $r3 |= $r3 >> 16;

            if ($j === $limit) {
                if ($limit === 64) {
                    break;
                }

                // Mashing round.
                $r0 += $keys[$r3 & 0x3F];
                $r1 += $keys[$r0 & 0x3F];
                $r2 += $keys[$r1 & 0x3F];
                $r3 += $keys[$r2 & 0x3F];
                $limit = $actions[$limit];
            }
        }

        return pack('vvvv', $r0, $r1, $r2, $r3);
    }

    /**
     * Decrypts a block
     *
     * @see Crypt_Base::_decryptBlock()
     * @see Crypt_Base::decrypt()
     * @access private
     * @param string $in
     * @return string
     */
    function _decryptBlock($in)
    {
        list($r0, $r1, $r2, $r3) = array_values(unpack('v*', $in));
        $keys = $this->keys;
        $limit = 44;
        $actions = array($limit => 20, 20 => 0);
        $j = 64;

        for (;;) {
            // R-mixing round.
            $r3 = ($r3 | ($r3 << 16)) >> 5;
            $r3 = ($r3 - $keys[--$j] - ((($r0 ^ $r1) & $r2) ^ $r0)) & 0xFFFF;
            $r2 = ($r2 | ($r2 << 16)) >> 3;
            $r2 = ($r2 - $keys[--$j] - ((($r3 ^ $r0) & $r1) ^ $r3)) & 0xFFFF;
            $r1 = ($r1 | ($r1 << 16)) >> 2;
            $r1 = ($r1 - $keys[--$j] - ((($r2 ^ $r3) & $r0) ^ $r2)) & 0xFFFF;
            $r0 = ($r0 | ($r0 << 16)) >> 1;
            $r0 = ($r0 - $keys[--$j] - ((($r1 ^ $r2) & $r3) ^ $r1)) & 0xFFFF;

            if ($j === $limit) {
                if ($limit === 0) {
                    break;
                }

                // R-mashing round.
                $r3 = ($r3 - $keys[$r2 & 0x3F]) & 0xFFFF;
                $r2 = ($r2 - $keys[$r1 & 0x3F]) & 0xFFFF;
                $r1 = ($r1 - $keys[$r0 & 0x3F]) & 0xFFFF;
                $r0 = ($r0 - $keys[$r3 & 0x3F]) & 0xFFFF;
                $limit = $actions[$limit];
            }
        }

        return pack('vvvv', $r0, $r1, $r2, $r3);
    }

    /**
     * Setup the CRYPT_ENGINE_MCRYPT $engine
     *
     * @see Crypt_Base::_setupMcrypt()
     * @access private
     */
    function _setupMcrypt()
    {
        if (!isset($this->key)) {
            $this->setKey('');
        }

        parent::_setupMcrypt();
    }

    /**
     * Creates the key schedule
     *
     * @see Crypt_Base::_setupKey()
     * @access private
     */
    function _setupKey()
    {
        if (!isset($this->key)) {
            $this->setKey('');
        }

        // Key has already been expanded in Crypt_RC2::setKey():
        // Only the first value must be altered.
        $l = unpack('Ca/Cb/v*', $this->key);
        array_unshift($l, $this->pitable[$l['a']] | ($l['b'] << 8));
        unset($l['a']);
        unset($l['b']);
        $this->keys = $l;
    }

    /**
     * Setup the performance-optimized function for de/encrypt()
     *
     * @see Crypt_Base::_setupInlineCrypt()
     * @access private
     */
    function _setupInlineCrypt()
    {
        $lambda_functions = &Crypt_RC2::_getLambdaFunctions();

        // The first 10 generated $lambda_functions will use the $keys hardcoded as integers
        // for the mixing rounds, for better inline crypt performance [~20% faster].
        // But for memory reason we have to limit those ultra-optimized $lambda_functions to an amount of 10.
        // (Currently, for Crypt_RC2, one generated $lambda_function cost on php5.5@32bit ~60kb unfreeable mem and ~100kb on php5.5@64bit)
        $gen_hi_opt_code = (bool)(count($lambda_functions) < 10);

        // Generation of a unique hash for our generated code
        $code_hash = "Crypt_RC2, {$this->mode}";
        if ($gen_hi_opt_code) {
            $code_hash = str_pad($code_hash, 32) . $this->_hashInlineCryptFunction($this->key);
        }

        // Is there a re-usable $lambda_functions in there?
        // If not, we have to create it.
        if (!isset($lambda_functions[$code_hash])) {
            // Init code for both, encrypt and decrypt.
            $init_crypt = '$keys = $self->keys;';

            switch (true) {
                case $gen_hi_opt_code:
                    $keys = $this->keys;
                default:
                    $keys = array();
                    foreach ($this->keys as $k => $v) {
                        $keys[$k] = '$keys[' . $k . ']';
                    }
            }

            // $in is the current 8 bytes block which has to be en/decrypt
            $encrypt_block = $decrypt_block = '
                $in = unpack("v4", $in);
                $r0 = $in[1];
                $r1 = $in[2];
                $r2 = $in[3];
                $r3 = $in[4];
            ';

            // Create code for encryption.
            $limit = 20;
            $actions = array($limit => 44, 44 => 64);
            $j = 0;

            for (;;) {
                // Mixing round.
                $encrypt_block .= '
                    $r0 = (($r0 + ' . $keys[$j++] . ' +
                           ((($r1 ^ $r2) & $r3) ^ $r1)) & 0xFFFF) << 1;
                    $r0 |= $r0 >> 16;
                    $r1 = (($r1 + ' . $keys[$j++] . ' +
                           ((($r2 ^ $r3) & $r0) ^ $r2)) & 0xFFFF) << 2;
                    $r1 |= $r1 >> 16;
                    $r2 = (($r2 + ' . $keys[$j++] . ' +
                           ((($r3 ^ $r0) & $r1) ^ $r3)) & 0xFFFF) << 3;
                    $r2 |= $r2 >> 16;
                    $r3 = (($r3 + ' . $keys[$j++] . ' +
                           ((($r0 ^ $r1) & $r2) ^ $r0)) & 0xFFFF) << 5;
                    $r3 |= $r3 >> 16;';

                if ($j === $limit) {
                    if ($limit === 64) {
                        break;
                    }

                    // Mashing round.
                    $encrypt_block .= '
                        $r0 += $keys[$r3 & 0x3F];
                        $r1 += $keys[$r0 & 0x3F];
                        $r2 += $keys[$r1 & 0x3F];
                        $r3 += $keys[$r2 & 0x3F];';
                    $limit = $actions[$limit];
                }
            }

            $encrypt_block .= '$in = pack("v4", $r0, $r1, $r2, $r3);';

            // Create code for decryption.
            $limit = 44;
            $actions = array($limit => 20, 20 => 0);
            $j = 64;

            for (;;) {
                // R-mixing round.
                $decrypt_block .= '
                    $r3 = ($r3 | ($r3 << 16)) >> 5;
                    $r3 = ($r3 - ' . $keys[--$j] . ' -
                           ((($r0 ^ $r1) & $r2) ^ $r0)) & 0xFFFF;
                    $r2 = ($r2 | ($r2 << 16)) >> 3;
                    $r2 = ($r2 - ' . $keys[--$j] . ' -
                           ((($r3 ^ $r0) & $r1) ^ $r3)) & 0xFFFF;
                    $r1 = ($r1 | ($r1 << 16)) >> 2;
                    $r1 = ($r1 - ' . $keys[--$j] . ' -
                           ((($r2 ^ $r3) & $r0) ^ $r2)) & 0xFFFF;
                    $r0 = ($r0 | ($r0 << 16)) >> 1;
                    $r0 = ($r0 - ' . $keys[--$j] . ' -
                           ((($r1 ^ $r2) & $r3) ^ $r1)) & 0xFFFF;';

                if ($j === $limit) {
                    if ($limit === 0) {
                        break;
                    }

                    // R-mashing round.
                    $decrypt_block .= '
                        $r3 = ($r3 - $keys[$r2 & 0x3F]) & 0xFFFF;
                        $r2 = ($r2 - $keys[$r1 & 0x3F]) & 0xFFFF;
                        $r1 = ($r1 - $keys[$r0 & 0x3F]) & 0xFFFF;
                        $r0 = ($r0 - $keys[$r3 & 0x3F]) & 0xFFFF;';
                    $limit = $actions[$limit];
                }
            }

            $decrypt_block .= '$in = pack("v4", $r0, $r1, $r2, $r3);';

            // Creates the inline-crypt function
            $lambda_functions[$code_hash] = $this->_createInlineCryptFunction(
                array(
                   'init_crypt'    => $init_crypt,
                   'encrypt_block' => $encrypt_block,
                   'decrypt_block' => $decrypt_block
                )
            );
        }

        // Set the inline-crypt function as callback in: $this->inline_crypt
        $this->inline_crypt = $lambda_functions[$code_hash];
    }
}
vendor/phpseclib/phpseclib/phpseclib/Crypt/Twofish.php000064400000116725151327705700017214 0ustar00<?php

/**
 * Pure-PHP implementation of Twofish.
 *
 * Uses mcrypt, if available, and an internal implementation, otherwise.
 *
 * PHP versions 4 and 5
 *
 * Useful resources are as follows:
 *
 *  - {@link http://en.wikipedia.org/wiki/Twofish Wikipedia description of Twofish}
 *
 * Here's a short example of how to use this library:
 * <code>
 * <?php
 *    include 'Crypt/Twofish.php';
 *
 *    $twofish = new Crypt_Twofish();
 *
 *    $twofish->setKey('12345678901234567890123456789012');
 *
 *    $plaintext = str_repeat('a', 1024);
 *
 *    echo $twofish->decrypt($twofish->encrypt($plaintext));
 * ?>
 * </code>
 *
 * LICENSE: Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 *
 * @category  Crypt
 * @package   Crypt_Twofish
 * @author    Jim Wigginton <terrafrost@php.net>
 * @author    Hans-Juergen Petrich <petrich@tronic-media.com>
 * @copyright 2007 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      http://phpseclib.sourceforge.net
 */

/**
 * Include Crypt_Base
 *
 * Base cipher class
 */
if (!class_exists('Crypt_Base')) {
    include_once 'Base.php';
}

/**#@+
 * @access public
 * @see self::encrypt()
 * @see self::decrypt()
 */
/**
 * Encrypt / decrypt using the Counter mode.
 *
 * Set to -1 since that's what Crypt/Random.php uses to index the CTR mode.
 *
 * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Counter_.28CTR.29
 */
define('CRYPT_TWOFISH_MODE_CTR', CRYPT_MODE_CTR);
/**
 * Encrypt / decrypt using the Electronic Code Book mode.
 *
 * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Electronic_codebook_.28ECB.29
 */
define('CRYPT_TWOFISH_MODE_ECB', CRYPT_MODE_ECB);
/**
 * Encrypt / decrypt using the Code Book Chaining mode.
 *
 * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Cipher-block_chaining_.28CBC.29
 */
define('CRYPT_TWOFISH_MODE_CBC', CRYPT_MODE_CBC);
/**
 * Encrypt / decrypt using the Cipher Feedback mode.
 *
 * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Cipher_feedback_.28CFB.29
 */
define('CRYPT_TWOFISH_MODE_CFB', CRYPT_MODE_CFB);
/**
 * Encrypt / decrypt using the Cipher Feedback mode.
 *
 * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Output_feedback_.28OFB.29
 */
define('CRYPT_TWOFISH_MODE_OFB', CRYPT_MODE_OFB);
/**#@-*/

/**
 * Pure-PHP implementation of Twofish.
 *
 * @package Crypt_Twofish
 * @author  Jim Wigginton <terrafrost@php.net>
 * @author  Hans-Juergen Petrich <petrich@tronic-media.com>
 * @access  public
 */
class Crypt_Twofish extends Crypt_Base
{
    /**
     * The namespace used by the cipher for its constants.
     *
     * @see Crypt_Base::const_namespace
     * @var string
     * @access private
     */
    var $const_namespace = 'TWOFISH';

    /**
     * The mcrypt specific name of the cipher
     *
     * @see Crypt_Base::cipher_name_mcrypt
     * @var string
     * @access private
     */
    var $cipher_name_mcrypt = 'twofish';

    /**
     * Optimizing value while CFB-encrypting
     *
     * @see Crypt_Base::cfb_init_len
     * @var int
     * @access private
     */
    var $cfb_init_len = 800;

    /**
     * Q-Table
     *
     * @var array
     * @access private
     */
    var $q0 = array(
        0xA9, 0x67, 0xB3, 0xE8, 0x04, 0xFD, 0xA3, 0x76,
        0x9A, 0x92, 0x80, 0x78, 0xE4, 0xDD, 0xD1, 0x38,
        0x0D, 0xC6, 0x35, 0x98, 0x18, 0xF7, 0xEC, 0x6C,
        0x43, 0x75, 0x37, 0x26, 0xFA, 0x13, 0x94, 0x48,
        0xF2, 0xD0, 0x8B, 0x30, 0x84, 0x54, 0xDF, 0x23,
        0x19, 0x5B, 0x3D, 0x59, 0xF3, 0xAE, 0xA2, 0x82,
        0x63, 0x01, 0x83, 0x2E, 0xD9, 0x51, 0x9B, 0x7C,
        0xA6, 0xEB, 0xA5, 0xBE, 0x16, 0x0C, 0xE3, 0x61,
        0xC0, 0x8C, 0x3A, 0xF5, 0x73, 0x2C, 0x25, 0x0B,
        0xBB, 0x4E, 0x89, 0x6B, 0x53, 0x6A, 0xB4, 0xF1,
        0xE1, 0xE6, 0xBD, 0x45, 0xE2, 0xF4, 0xB6, 0x66,
        0xCC, 0x95, 0x03, 0x56, 0xD4, 0x1C, 0x1E, 0xD7,
        0xFB, 0xC3, 0x8E, 0xB5, 0xE9, 0xCF, 0xBF, 0xBA,
        0xEA, 0x77, 0x39, 0xAF, 0x33, 0xC9, 0x62, 0x71,
        0x81, 0x79, 0x09, 0xAD, 0x24, 0xCD, 0xF9, 0xD8,
        0xE5, 0xC5, 0xB9, 0x4D, 0x44, 0x08, 0x86, 0xE7,
        0xA1, 0x1D, 0xAA, 0xED, 0x06, 0x70, 0xB2, 0xD2,
        0x41, 0x7B, 0xA0, 0x11, 0x31, 0xC2, 0x27, 0x90,
        0x20, 0xF6, 0x60, 0xFF, 0x96, 0x5C, 0xB1, 0xAB,
        0x9E, 0x9C, 0x52, 0x1B, 0x5F, 0x93, 0x0A, 0xEF,
        0x91, 0x85, 0x49, 0xEE, 0x2D, 0x4F, 0x8F, 0x3B,
        0x47, 0x87, 0x6D, 0x46, 0xD6, 0x3E, 0x69, 0x64,
        0x2A, 0xCE, 0xCB, 0x2F, 0xFC, 0x97, 0x05, 0x7A,
        0xAC, 0x7F, 0xD5, 0x1A, 0x4B, 0x0E, 0xA7, 0x5A,
        0x28, 0x14, 0x3F, 0x29, 0x88, 0x3C, 0x4C, 0x02,
        0xB8, 0xDA, 0xB0, 0x17, 0x55, 0x1F, 0x8A, 0x7D,
        0x57, 0xC7, 0x8D, 0x74, 0xB7, 0xC4, 0x9F, 0x72,
        0x7E, 0x15, 0x22, 0x12, 0x58, 0x07, 0x99, 0x34,
        0x6E, 0x50, 0xDE, 0x68, 0x65, 0xBC, 0xDB, 0xF8,
        0xC8, 0xA8, 0x2B, 0x40, 0xDC, 0xFE, 0x32, 0xA4,
        0xCA, 0x10, 0x21, 0xF0, 0xD3, 0x5D, 0x0F, 0x00,
        0x6F, 0x9D, 0x36, 0x42, 0x4A, 0x5E, 0xC1, 0xE0
    );

    /**
     * Q-Table
     *
     * @var array
     * @access private
     */
    var $q1 = array(
        0x75, 0xF3, 0xC6, 0xF4, 0xDB, 0x7B, 0xFB, 0xC8,
        0x4A, 0xD3, 0xE6, 0x6B, 0x45, 0x7D, 0xE8, 0x4B,
        0xD6, 0x32, 0xD8, 0xFD, 0x37, 0x71, 0xF1, 0xE1,
        0x30, 0x0F, 0xF8, 0x1B, 0x87, 0xFA, 0x06, 0x3F,
        0x5E, 0xBA, 0xAE, 0x5B, 0x8A, 0x00, 0xBC, 0x9D,
        0x6D, 0xC1, 0xB1, 0x0E, 0x80, 0x5D, 0xD2, 0xD5,
        0xA0, 0x84, 0x07, 0x14, 0xB5, 0x90, 0x2C, 0xA3,
        0xB2, 0x73, 0x4C, 0x54, 0x92, 0x74, 0x36, 0x51,
        0x38, 0xB0, 0xBD, 0x5A, 0xFC, 0x60, 0x62, 0x96,
        0x6C, 0x42, 0xF7, 0x10, 0x7C, 0x28, 0x27, 0x8C,
        0x13, 0x95, 0x9C, 0xC7, 0x24, 0x46, 0x3B, 0x70,
        0xCA, 0xE3, 0x85, 0xCB, 0x11, 0xD0, 0x93, 0xB8,
        0xA6, 0x83, 0x20, 0xFF, 0x9F, 0x77, 0xC3, 0xCC,
        0x03, 0x6F, 0x08, 0xBF, 0x40, 0xE7, 0x2B, 0xE2,
        0x79, 0x0C, 0xAA, 0x82, 0x41, 0x3A, 0xEA, 0xB9,
        0xE4, 0x9A, 0xA4, 0x97, 0x7E, 0xDA, 0x7A, 0x17,
        0x66, 0x94, 0xA1, 0x1D, 0x3D, 0xF0, 0xDE, 0xB3,
        0x0B, 0x72, 0xA7, 0x1C, 0xEF, 0xD1, 0x53, 0x3E,
        0x8F, 0x33, 0x26, 0x5F, 0xEC, 0x76, 0x2A, 0x49,
        0x81, 0x88, 0xEE, 0x21, 0xC4, 0x1A, 0xEB, 0xD9,
        0xC5, 0x39, 0x99, 0xCD, 0xAD, 0x31, 0x8B, 0x01,
        0x18, 0x23, 0xDD, 0x1F, 0x4E, 0x2D, 0xF9, 0x48,
        0x4F, 0xF2, 0x65, 0x8E, 0x78, 0x5C, 0x58, 0x19,
        0x8D, 0xE5, 0x98, 0x57, 0x67, 0x7F, 0x05, 0x64,
        0xAF, 0x63, 0xB6, 0xFE, 0xF5, 0xB7, 0x3C, 0xA5,
        0xCE, 0xE9, 0x68, 0x44, 0xE0, 0x4D, 0x43, 0x69,
        0x29, 0x2E, 0xAC, 0x15, 0x59, 0xA8, 0x0A, 0x9E,
        0x6E, 0x47, 0xDF, 0x34, 0x35, 0x6A, 0xCF, 0xDC,
        0x22, 0xC9, 0xC0, 0x9B, 0x89, 0xD4, 0xED, 0xAB,
        0x12, 0xA2, 0x0D, 0x52, 0xBB, 0x02, 0x2F, 0xA9,
        0xD7, 0x61, 0x1E, 0xB4, 0x50, 0x04, 0xF6, 0xC2,
        0x16, 0x25, 0x86, 0x56, 0x55, 0x09, 0xBE, 0x91
    );

    /**
     * M-Table
     *
     * @var array
     * @access private
     */
    var $m0 = array(
        0xBCBC3275, 0xECEC21F3, 0x202043C6, 0xB3B3C9F4, 0xDADA03DB, 0x02028B7B, 0xE2E22BFB, 0x9E9EFAC8,
        0xC9C9EC4A, 0xD4D409D3, 0x18186BE6, 0x1E1E9F6B, 0x98980E45, 0xB2B2387D, 0xA6A6D2E8, 0x2626B74B,
        0x3C3C57D6, 0x93938A32, 0x8282EED8, 0x525298FD, 0x7B7BD437, 0xBBBB3771, 0x5B5B97F1, 0x474783E1,
        0x24243C30, 0x5151E20F, 0xBABAC6F8, 0x4A4AF31B, 0xBFBF4887, 0x0D0D70FA, 0xB0B0B306, 0x7575DE3F,
        0xD2D2FD5E, 0x7D7D20BA, 0x666631AE, 0x3A3AA35B, 0x59591C8A, 0x00000000, 0xCDCD93BC, 0x1A1AE09D,
        0xAEAE2C6D, 0x7F7FABC1, 0x2B2BC7B1, 0xBEBEB90E, 0xE0E0A080, 0x8A8A105D, 0x3B3B52D2, 0x6464BAD5,
        0xD8D888A0, 0xE7E7A584, 0x5F5FE807, 0x1B1B1114, 0x2C2CC2B5, 0xFCFCB490, 0x3131272C, 0x808065A3,
        0x73732AB2, 0x0C0C8173, 0x79795F4C, 0x6B6B4154, 0x4B4B0292, 0x53536974, 0x94948F36, 0x83831F51,
        0x2A2A3638, 0xC4C49CB0, 0x2222C8BD, 0xD5D5F85A, 0xBDBDC3FC, 0x48487860, 0xFFFFCE62, 0x4C4C0796,
        0x4141776C, 0xC7C7E642, 0xEBEB24F7, 0x1C1C1410, 0x5D5D637C, 0x36362228, 0x6767C027, 0xE9E9AF8C,
        0x4444F913, 0x1414EA95, 0xF5F5BB9C, 0xCFCF18C7, 0x3F3F2D24, 0xC0C0E346, 0x7272DB3B, 0x54546C70,
        0x29294CCA, 0xF0F035E3, 0x0808FE85, 0xC6C617CB, 0xF3F34F11, 0x8C8CE4D0, 0xA4A45993, 0xCACA96B8,
        0x68683BA6, 0xB8B84D83, 0x38382820, 0xE5E52EFF, 0xADAD569F, 0x0B0B8477, 0xC8C81DC3, 0x9999FFCC,
        0x5858ED03, 0x19199A6F, 0x0E0E0A08, 0x95957EBF, 0x70705040, 0xF7F730E7, 0x6E6ECF2B, 0x1F1F6EE2,
        0xB5B53D79, 0x09090F0C, 0x616134AA, 0x57571682, 0x9F9F0B41, 0x9D9D803A, 0x111164EA, 0x2525CDB9,
        0xAFAFDDE4, 0x4545089A, 0xDFDF8DA4, 0xA3A35C97, 0xEAEAD57E, 0x353558DA, 0xEDEDD07A, 0x4343FC17,
        0xF8F8CB66, 0xFBFBB194, 0x3737D3A1, 0xFAFA401D, 0xC2C2683D, 0xB4B4CCF0, 0x32325DDE, 0x9C9C71B3,
        0x5656E70B, 0xE3E3DA72, 0x878760A7, 0x15151B1C, 0xF9F93AEF, 0x6363BFD1, 0x3434A953, 0x9A9A853E,
        0xB1B1428F, 0x7C7CD133, 0x88889B26, 0x3D3DA65F, 0xA1A1D7EC, 0xE4E4DF76, 0x8181942A, 0x91910149,
        0x0F0FFB81, 0xEEEEAA88, 0x161661EE, 0xD7D77321, 0x9797F5C4, 0xA5A5A81A, 0xFEFE3FEB, 0x6D6DB5D9,
        0x7878AEC5, 0xC5C56D39, 0x1D1DE599, 0x7676A4CD, 0x3E3EDCAD, 0xCBCB6731, 0xB6B6478B, 0xEFEF5B01,
        0x12121E18, 0x6060C523, 0x6A6AB0DD, 0x4D4DF61F, 0xCECEE94E, 0xDEDE7C2D, 0x55559DF9, 0x7E7E5A48,
        0x2121B24F, 0x03037AF2, 0xA0A02665, 0x5E5E198E, 0x5A5A6678, 0x65654B5C, 0x62624E58, 0xFDFD4519,
        0x0606F48D, 0x404086E5, 0xF2F2BE98, 0x3333AC57, 0x17179067, 0x05058E7F, 0xE8E85E05, 0x4F4F7D64,
        0x89896AAF, 0x10109563, 0x74742FB6, 0x0A0A75FE, 0x5C5C92F5, 0x9B9B74B7, 0x2D2D333C, 0x3030D6A5,
        0x2E2E49CE, 0x494989E9, 0x46467268, 0x77775544, 0xA8A8D8E0, 0x9696044D, 0x2828BD43, 0xA9A92969,
        0xD9D97929, 0x8686912E, 0xD1D187AC, 0xF4F44A15, 0x8D8D1559, 0xD6D682A8, 0xB9B9BC0A, 0x42420D9E,
        0xF6F6C16E, 0x2F2FB847, 0xDDDD06DF, 0x23233934, 0xCCCC6235, 0xF1F1C46A, 0xC1C112CF, 0x8585EBDC,
        0x8F8F9E22, 0x7171A1C9, 0x9090F0C0, 0xAAAA539B, 0x0101F189, 0x8B8BE1D4, 0x4E4E8CED, 0x8E8E6FAB,
        0xABABA212, 0x6F6F3EA2, 0xE6E6540D, 0xDBDBF252, 0x92927BBB, 0xB7B7B602, 0x6969CA2F, 0x3939D9A9,
        0xD3D30CD7, 0xA7A72361, 0xA2A2AD1E, 0xC3C399B4, 0x6C6C4450, 0x07070504, 0x04047FF6, 0x272746C2,
        0xACACA716, 0xD0D07625, 0x50501386, 0xDCDCF756, 0x84841A55, 0xE1E15109, 0x7A7A25BE, 0x1313EF91
    );

    /**
     * M-Table
     *
     * @var array
     * @access private
     */
    var $m1 = array(
        0xA9D93939, 0x67901717, 0xB3719C9C, 0xE8D2A6A6, 0x04050707, 0xFD985252, 0xA3658080, 0x76DFE4E4,
        0x9A084545, 0x92024B4B, 0x80A0E0E0, 0x78665A5A, 0xE4DDAFAF, 0xDDB06A6A, 0xD1BF6363, 0x38362A2A,
        0x0D54E6E6, 0xC6432020, 0x3562CCCC, 0x98BEF2F2, 0x181E1212, 0xF724EBEB, 0xECD7A1A1, 0x6C774141,
        0x43BD2828, 0x7532BCBC, 0x37D47B7B, 0x269B8888, 0xFA700D0D, 0x13F94444, 0x94B1FBFB, 0x485A7E7E,
        0xF27A0303, 0xD0E48C8C, 0x8B47B6B6, 0x303C2424, 0x84A5E7E7, 0x54416B6B, 0xDF06DDDD, 0x23C56060,
        0x1945FDFD, 0x5BA33A3A, 0x3D68C2C2, 0x59158D8D, 0xF321ECEC, 0xAE316666, 0xA23E6F6F, 0x82165757,
        0x63951010, 0x015BEFEF, 0x834DB8B8, 0x2E918686, 0xD9B56D6D, 0x511F8383, 0x9B53AAAA, 0x7C635D5D,
        0xA63B6868, 0xEB3FFEFE, 0xA5D63030, 0xBE257A7A, 0x16A7ACAC, 0x0C0F0909, 0xE335F0F0, 0x6123A7A7,
        0xC0F09090, 0x8CAFE9E9, 0x3A809D9D, 0xF5925C5C, 0x73810C0C, 0x2C273131, 0x2576D0D0, 0x0BE75656,
        0xBB7B9292, 0x4EE9CECE, 0x89F10101, 0x6B9F1E1E, 0x53A93434, 0x6AC4F1F1, 0xB499C3C3, 0xF1975B5B,
        0xE1834747, 0xE66B1818, 0xBDC82222, 0x450E9898, 0xE26E1F1F, 0xF4C9B3B3, 0xB62F7474, 0x66CBF8F8,
        0xCCFF9999, 0x95EA1414, 0x03ED5858, 0x56F7DCDC, 0xD4E18B8B, 0x1C1B1515, 0x1EADA2A2, 0xD70CD3D3,
        0xFB2BE2E2, 0xC31DC8C8, 0x8E195E5E, 0xB5C22C2C, 0xE9894949, 0xCF12C1C1, 0xBF7E9595, 0xBA207D7D,
        0xEA641111, 0x77840B0B, 0x396DC5C5, 0xAF6A8989, 0x33D17C7C, 0xC9A17171, 0x62CEFFFF, 0x7137BBBB,
        0x81FB0F0F, 0x793DB5B5, 0x0951E1E1, 0xADDC3E3E, 0x242D3F3F, 0xCDA47676, 0xF99D5555, 0xD8EE8282,
        0xE5864040, 0xC5AE7878, 0xB9CD2525, 0x4D049696, 0x44557777, 0x080A0E0E, 0x86135050, 0xE730F7F7,
        0xA1D33737, 0x1D40FAFA, 0xAA346161, 0xED8C4E4E, 0x06B3B0B0, 0x706C5454, 0xB22A7373, 0xD2523B3B,
        0x410B9F9F, 0x7B8B0202, 0xA088D8D8, 0x114FF3F3, 0x3167CBCB, 0xC2462727, 0x27C06767, 0x90B4FCFC,
        0x20283838, 0xF67F0404, 0x60784848, 0xFF2EE5E5, 0x96074C4C, 0x5C4B6565, 0xB1C72B2B, 0xAB6F8E8E,
        0x9E0D4242, 0x9CBBF5F5, 0x52F2DBDB, 0x1BF34A4A, 0x5FA63D3D, 0x9359A4A4, 0x0ABCB9B9, 0xEF3AF9F9,
        0x91EF1313, 0x85FE0808, 0x49019191, 0xEE611616, 0x2D7CDEDE, 0x4FB22121, 0x8F42B1B1, 0x3BDB7272,
        0x47B82F2F, 0x8748BFBF, 0x6D2CAEAE, 0x46E3C0C0, 0xD6573C3C, 0x3E859A9A, 0x6929A9A9, 0x647D4F4F,
        0x2A948181, 0xCE492E2E, 0xCB17C6C6, 0x2FCA6969, 0xFCC3BDBD, 0x975CA3A3, 0x055EE8E8, 0x7AD0EDED,
        0xAC87D1D1, 0x7F8E0505, 0xD5BA6464, 0x1AA8A5A5, 0x4BB72626, 0x0EB9BEBE, 0xA7608787, 0x5AF8D5D5,
        0x28223636, 0x14111B1B, 0x3FDE7575, 0x2979D9D9, 0x88AAEEEE, 0x3C332D2D, 0x4C5F7979, 0x02B6B7B7,
        0xB896CACA, 0xDA583535, 0xB09CC4C4, 0x17FC4343, 0x551A8484, 0x1FF64D4D, 0x8A1C5959, 0x7D38B2B2,
        0x57AC3333, 0xC718CFCF, 0x8DF40606, 0x74695353, 0xB7749B9B, 0xC4F59797, 0x9F56ADAD, 0x72DAE3E3,
        0x7ED5EAEA, 0x154AF4F4, 0x229E8F8F, 0x12A2ABAB, 0x584E6262, 0x07E85F5F, 0x99E51D1D, 0x34392323,
        0x6EC1F6F6, 0x50446C6C, 0xDE5D3232, 0x68724646, 0x6526A0A0, 0xBC93CDCD, 0xDB03DADA, 0xF8C6BABA,
        0xC8FA9E9E, 0xA882D6D6, 0x2BCF6E6E, 0x40507070, 0xDCEB8585, 0xFE750A0A, 0x328A9393, 0xA48DDFDF,
        0xCA4C2929, 0x10141C1C, 0x2173D7D7, 0xF0CCB4B4, 0xD309D4D4, 0x5D108A8A, 0x0FE25151, 0x00000000,
        0x6F9A1919, 0x9DE01A1A, 0x368F9494, 0x42E6C7C7, 0x4AECC9C9, 0x5EFDD2D2, 0xC1AB7F7F, 0xE0D8A8A8
    );

    /**
     * M-Table
     *
     * @var array
     * @access private
     */
    var $m2 = array(
        0xBC75BC32, 0xECF3EC21, 0x20C62043, 0xB3F4B3C9, 0xDADBDA03, 0x027B028B, 0xE2FBE22B, 0x9EC89EFA,
        0xC94AC9EC, 0xD4D3D409, 0x18E6186B, 0x1E6B1E9F, 0x9845980E, 0xB27DB238, 0xA6E8A6D2, 0x264B26B7,
        0x3CD63C57, 0x9332938A, 0x82D882EE, 0x52FD5298, 0x7B377BD4, 0xBB71BB37, 0x5BF15B97, 0x47E14783,
        0x2430243C, 0x510F51E2, 0xBAF8BAC6, 0x4A1B4AF3, 0xBF87BF48, 0x0DFA0D70, 0xB006B0B3, 0x753F75DE,
        0xD25ED2FD, 0x7DBA7D20, 0x66AE6631, 0x3A5B3AA3, 0x598A591C, 0x00000000, 0xCDBCCD93, 0x1A9D1AE0,
        0xAE6DAE2C, 0x7FC17FAB, 0x2BB12BC7, 0xBE0EBEB9, 0xE080E0A0, 0x8A5D8A10, 0x3BD23B52, 0x64D564BA,
        0xD8A0D888, 0xE784E7A5, 0x5F075FE8, 0x1B141B11, 0x2CB52CC2, 0xFC90FCB4, 0x312C3127, 0x80A38065,
        0x73B2732A, 0x0C730C81, 0x794C795F, 0x6B546B41, 0x4B924B02, 0x53745369, 0x9436948F, 0x8351831F,
        0x2A382A36, 0xC4B0C49C, 0x22BD22C8, 0xD55AD5F8, 0xBDFCBDC3, 0x48604878, 0xFF62FFCE, 0x4C964C07,
        0x416C4177, 0xC742C7E6, 0xEBF7EB24, 0x1C101C14, 0x5D7C5D63, 0x36283622, 0x672767C0, 0xE98CE9AF,
        0x441344F9, 0x149514EA, 0xF59CF5BB, 0xCFC7CF18, 0x3F243F2D, 0xC046C0E3, 0x723B72DB, 0x5470546C,
        0x29CA294C, 0xF0E3F035, 0x088508FE, 0xC6CBC617, 0xF311F34F, 0x8CD08CE4, 0xA493A459, 0xCAB8CA96,
        0x68A6683B, 0xB883B84D, 0x38203828, 0xE5FFE52E, 0xAD9FAD56, 0x0B770B84, 0xC8C3C81D, 0x99CC99FF,
        0x580358ED, 0x196F199A, 0x0E080E0A, 0x95BF957E, 0x70407050, 0xF7E7F730, 0x6E2B6ECF, 0x1FE21F6E,
        0xB579B53D, 0x090C090F, 0x61AA6134, 0x57825716, 0x9F419F0B, 0x9D3A9D80, 0x11EA1164, 0x25B925CD,
        0xAFE4AFDD, 0x459A4508, 0xDFA4DF8D, 0xA397A35C, 0xEA7EEAD5, 0x35DA3558, 0xED7AEDD0, 0x431743FC,
        0xF866F8CB, 0xFB94FBB1, 0x37A137D3, 0xFA1DFA40, 0xC23DC268, 0xB4F0B4CC, 0x32DE325D, 0x9CB39C71,
        0x560B56E7, 0xE372E3DA, 0x87A78760, 0x151C151B, 0xF9EFF93A, 0x63D163BF, 0x345334A9, 0x9A3E9A85,
        0xB18FB142, 0x7C337CD1, 0x8826889B, 0x3D5F3DA6, 0xA1ECA1D7, 0xE476E4DF, 0x812A8194, 0x91499101,
        0x0F810FFB, 0xEE88EEAA, 0x16EE1661, 0xD721D773, 0x97C497F5, 0xA51AA5A8, 0xFEEBFE3F, 0x6DD96DB5,
        0x78C578AE, 0xC539C56D, 0x1D991DE5, 0x76CD76A4, 0x3EAD3EDC, 0xCB31CB67, 0xB68BB647, 0xEF01EF5B,
        0x1218121E, 0x602360C5, 0x6ADD6AB0, 0x4D1F4DF6, 0xCE4ECEE9, 0xDE2DDE7C, 0x55F9559D, 0x7E487E5A,
        0x214F21B2, 0x03F2037A, 0xA065A026, 0x5E8E5E19, 0x5A785A66, 0x655C654B, 0x6258624E, 0xFD19FD45,
        0x068D06F4, 0x40E54086, 0xF298F2BE, 0x335733AC, 0x17671790, 0x057F058E, 0xE805E85E, 0x4F644F7D,
        0x89AF896A, 0x10631095, 0x74B6742F, 0x0AFE0A75, 0x5CF55C92, 0x9BB79B74, 0x2D3C2D33, 0x30A530D6,
        0x2ECE2E49, 0x49E94989, 0x46684672, 0x77447755, 0xA8E0A8D8, 0x964D9604, 0x284328BD, 0xA969A929,
        0xD929D979, 0x862E8691, 0xD1ACD187, 0xF415F44A, 0x8D598D15, 0xD6A8D682, 0xB90AB9BC, 0x429E420D,
        0xF66EF6C1, 0x2F472FB8, 0xDDDFDD06, 0x23342339, 0xCC35CC62, 0xF16AF1C4, 0xC1CFC112, 0x85DC85EB,
        0x8F228F9E, 0x71C971A1, 0x90C090F0, 0xAA9BAA53, 0x018901F1, 0x8BD48BE1, 0x4EED4E8C, 0x8EAB8E6F,
        0xAB12ABA2, 0x6FA26F3E, 0xE60DE654, 0xDB52DBF2, 0x92BB927B, 0xB702B7B6, 0x692F69CA, 0x39A939D9,
        0xD3D7D30C, 0xA761A723, 0xA21EA2AD, 0xC3B4C399, 0x6C506C44, 0x07040705, 0x04F6047F, 0x27C22746,
        0xAC16ACA7, 0xD025D076, 0x50865013, 0xDC56DCF7, 0x8455841A, 0xE109E151, 0x7ABE7A25, 0x139113EF
    );

    /**
     * M-Table
     *
     * @var array
     * @access private
     */
    var $m3 = array(
        0xD939A9D9, 0x90176790, 0x719CB371, 0xD2A6E8D2, 0x05070405, 0x9852FD98, 0x6580A365, 0xDFE476DF,
        0x08459A08, 0x024B9202, 0xA0E080A0, 0x665A7866, 0xDDAFE4DD, 0xB06ADDB0, 0xBF63D1BF, 0x362A3836,
        0x54E60D54, 0x4320C643, 0x62CC3562, 0xBEF298BE, 0x1E12181E, 0x24EBF724, 0xD7A1ECD7, 0x77416C77,
        0xBD2843BD, 0x32BC7532, 0xD47B37D4, 0x9B88269B, 0x700DFA70, 0xF94413F9, 0xB1FB94B1, 0x5A7E485A,
        0x7A03F27A, 0xE48CD0E4, 0x47B68B47, 0x3C24303C, 0xA5E784A5, 0x416B5441, 0x06DDDF06, 0xC56023C5,
        0x45FD1945, 0xA33A5BA3, 0x68C23D68, 0x158D5915, 0x21ECF321, 0x3166AE31, 0x3E6FA23E, 0x16578216,
        0x95106395, 0x5BEF015B, 0x4DB8834D, 0x91862E91, 0xB56DD9B5, 0x1F83511F, 0x53AA9B53, 0x635D7C63,
        0x3B68A63B, 0x3FFEEB3F, 0xD630A5D6, 0x257ABE25, 0xA7AC16A7, 0x0F090C0F, 0x35F0E335, 0x23A76123,
        0xF090C0F0, 0xAFE98CAF, 0x809D3A80, 0x925CF592, 0x810C7381, 0x27312C27, 0x76D02576, 0xE7560BE7,
        0x7B92BB7B, 0xE9CE4EE9, 0xF10189F1, 0x9F1E6B9F, 0xA93453A9, 0xC4F16AC4, 0x99C3B499, 0x975BF197,
        0x8347E183, 0x6B18E66B, 0xC822BDC8, 0x0E98450E, 0x6E1FE26E, 0xC9B3F4C9, 0x2F74B62F, 0xCBF866CB,
        0xFF99CCFF, 0xEA1495EA, 0xED5803ED, 0xF7DC56F7, 0xE18BD4E1, 0x1B151C1B, 0xADA21EAD, 0x0CD3D70C,
        0x2BE2FB2B, 0x1DC8C31D, 0x195E8E19, 0xC22CB5C2, 0x8949E989, 0x12C1CF12, 0x7E95BF7E, 0x207DBA20,
        0x6411EA64, 0x840B7784, 0x6DC5396D, 0x6A89AF6A, 0xD17C33D1, 0xA171C9A1, 0xCEFF62CE, 0x37BB7137,
        0xFB0F81FB, 0x3DB5793D, 0x51E10951, 0xDC3EADDC, 0x2D3F242D, 0xA476CDA4, 0x9D55F99D, 0xEE82D8EE,
        0x8640E586, 0xAE78C5AE, 0xCD25B9CD, 0x04964D04, 0x55774455, 0x0A0E080A, 0x13508613, 0x30F7E730,
        0xD337A1D3, 0x40FA1D40, 0x3461AA34, 0x8C4EED8C, 0xB3B006B3, 0x6C54706C, 0x2A73B22A, 0x523BD252,
        0x0B9F410B, 0x8B027B8B, 0x88D8A088, 0x4FF3114F, 0x67CB3167, 0x4627C246, 0xC06727C0, 0xB4FC90B4,
        0x28382028, 0x7F04F67F, 0x78486078, 0x2EE5FF2E, 0x074C9607, 0x4B655C4B, 0xC72BB1C7, 0x6F8EAB6F,
        0x0D429E0D, 0xBBF59CBB, 0xF2DB52F2, 0xF34A1BF3, 0xA63D5FA6, 0x59A49359, 0xBCB90ABC, 0x3AF9EF3A,
        0xEF1391EF, 0xFE0885FE, 0x01914901, 0x6116EE61, 0x7CDE2D7C, 0xB2214FB2, 0x42B18F42, 0xDB723BDB,
        0xB82F47B8, 0x48BF8748, 0x2CAE6D2C, 0xE3C046E3, 0x573CD657, 0x859A3E85, 0x29A96929, 0x7D4F647D,
        0x94812A94, 0x492ECE49, 0x17C6CB17, 0xCA692FCA, 0xC3BDFCC3, 0x5CA3975C, 0x5EE8055E, 0xD0ED7AD0,
        0x87D1AC87, 0x8E057F8E, 0xBA64D5BA, 0xA8A51AA8, 0xB7264BB7, 0xB9BE0EB9, 0x6087A760, 0xF8D55AF8,
        0x22362822, 0x111B1411, 0xDE753FDE, 0x79D92979, 0xAAEE88AA, 0x332D3C33, 0x5F794C5F, 0xB6B702B6,
        0x96CAB896, 0x5835DA58, 0x9CC4B09C, 0xFC4317FC, 0x1A84551A, 0xF64D1FF6, 0x1C598A1C, 0x38B27D38,
        0xAC3357AC, 0x18CFC718, 0xF4068DF4, 0x69537469, 0x749BB774, 0xF597C4F5, 0x56AD9F56, 0xDAE372DA,
        0xD5EA7ED5, 0x4AF4154A, 0x9E8F229E, 0xA2AB12A2, 0x4E62584E, 0xE85F07E8, 0xE51D99E5, 0x39233439,
        0xC1F66EC1, 0x446C5044, 0x5D32DE5D, 0x72466872, 0x26A06526, 0x93CDBC93, 0x03DADB03, 0xC6BAF8C6,
        0xFA9EC8FA, 0x82D6A882, 0xCF6E2BCF, 0x50704050, 0xEB85DCEB, 0x750AFE75, 0x8A93328A, 0x8DDFA48D,
        0x4C29CA4C, 0x141C1014, 0x73D72173, 0xCCB4F0CC, 0x09D4D309, 0x108A5D10, 0xE2510FE2, 0x00000000,
        0x9A196F9A, 0xE01A9DE0, 0x8F94368F, 0xE6C742E6, 0xECC94AEC, 0xFDD25EFD, 0xAB7FC1AB, 0xD8A8E0D8
    );

    /**
     * The Key Schedule Array
     *
     * @var array
     * @access private
     */
    var $K = array();

    /**
     * The Key depended S-Table 0
     *
     * @var array
     * @access private
     */
    var $S0 = array();

    /**
     * The Key depended S-Table 1
     *
     * @var array
     * @access private
     */
    var $S1 = array();

    /**
     * The Key depended S-Table 2
     *
     * @var array
     * @access private
     */
    var $S2 = array();

    /**
     * The Key depended S-Table 3
     *
     * @var array
     * @access private
     */
    var $S3 = array();

    /**
     * Holds the last used key
     *
     * @var array
     * @access private
     */
    var $kl;

    /**
     * The Key Length (in bytes)
     *
     * @see Crypt_Twofish::setKeyLength()
     * @var int
     * @access private
     */
    var $key_length = 16;

    /**
     * Sets the key length.
     *
     * Valid key lengths are 128, 192 or 256 bits
     *
     * @access public
     * @param int $length
     */
    function setKeyLength($length)
    {
        switch (true) {
            case $length <= 128:
                $this->key_length = 16;
                break;
            case $length <= 192:
                $this->key_length = 24;
                break;
            default:
                $this->key_length = 32;
        }

        parent::setKeyLength($length);
    }

    /**
     * Setup the key (expansion)
     *
     * @see Crypt_Base::_setupKey()
     * @access private
     */
    function _setupKey()
    {
        if (isset($this->kl['key']) && $this->key === $this->kl['key']) {
            // already expanded
            return;
        }
        $this->kl = array('key' => $this->key);

        /* Key expanding and generating the key-depended s-boxes */
        $le_longs = unpack('V*', $this->key);
        $key = unpack('C*', $this->key);
        $m0 = $this->m0;
        $m1 = $this->m1;
        $m2 = $this->m2;
        $m3 = $this->m3;
        $q0 = $this->q0;
        $q1 = $this->q1;

        $K = $S0 = $S1 = $S2 = $S3 = array();

        switch (strlen($this->key)) {
            case 16:
                list($s7, $s6, $s5, $s4) = $this->_mdsrem($le_longs[1], $le_longs[2]);
                list($s3, $s2, $s1, $s0) = $this->_mdsrem($le_longs[3], $le_longs[4]);
                for ($i = 0, $j = 1; $i < 40; $i+= 2, $j+= 2) {
                    $A = $m0[$q0[$q0[$i] ^ $key[ 9]] ^ $key[1]] ^
                         $m1[$q0[$q1[$i] ^ $key[10]] ^ $key[2]] ^
                         $m2[$q1[$q0[$i] ^ $key[11]] ^ $key[3]] ^
                         $m3[$q1[$q1[$i] ^ $key[12]] ^ $key[4]];
                    $B = $m0[$q0[$q0[$j] ^ $key[13]] ^ $key[5]] ^
                         $m1[$q0[$q1[$j] ^ $key[14]] ^ $key[6]] ^
                         $m2[$q1[$q0[$j] ^ $key[15]] ^ $key[7]] ^
                         $m3[$q1[$q1[$j] ^ $key[16]] ^ $key[8]];
                    $B = ($B << 8) | ($B >> 24 & 0xff);
                    $A = $this->safe_intval($A + $B);
                    $K[] = $A;
                    $A = $this->safe_intval($A + $B);
                    $K[] = ($A << 9 | $A >> 23 & 0x1ff);
                }
                for ($i = 0; $i < 256; ++$i) {
                    $S0[$i] = $m0[$q0[$q0[$i] ^ $s4] ^ $s0];
                    $S1[$i] = $m1[$q0[$q1[$i] ^ $s5] ^ $s1];
                    $S2[$i] = $m2[$q1[$q0[$i] ^ $s6] ^ $s2];
                    $S3[$i] = $m3[$q1[$q1[$i] ^ $s7] ^ $s3];
                }
                break;
            case 24:
                list($sb, $sa, $s9, $s8) = $this->_mdsrem($le_longs[1], $le_longs[2]);
                list($s7, $s6, $s5, $s4) = $this->_mdsrem($le_longs[3], $le_longs[4]);
                list($s3, $s2, $s1, $s0) = $this->_mdsrem($le_longs[5], $le_longs[6]);
                for ($i = 0, $j = 1; $i < 40; $i+= 2, $j+= 2) {
                    $A = $m0[$q0[$q0[$q1[$i] ^ $key[17]] ^ $key[ 9]] ^ $key[1]] ^
                         $m1[$q0[$q1[$q1[$i] ^ $key[18]] ^ $key[10]] ^ $key[2]] ^
                         $m2[$q1[$q0[$q0[$i] ^ $key[19]] ^ $key[11]] ^ $key[3]] ^
                         $m3[$q1[$q1[$q0[$i] ^ $key[20]] ^ $key[12]] ^ $key[4]];
                    $B = $m0[$q0[$q0[$q1[$j] ^ $key[21]] ^ $key[13]] ^ $key[5]] ^
                         $m1[$q0[$q1[$q1[$j] ^ $key[22]] ^ $key[14]] ^ $key[6]] ^
                         $m2[$q1[$q0[$q0[$j] ^ $key[23]] ^ $key[15]] ^ $key[7]] ^
                         $m3[$q1[$q1[$q0[$j] ^ $key[24]] ^ $key[16]] ^ $key[8]];
                    $B = ($B << 8) | ($B >> 24 & 0xff);
                    $A = $this->safe_intval($A + $B);
                    $K[] = $A;
                    $A = $this->safe_intval($A + $B);
                    $K[] = ($A << 9 | $A >> 23 & 0x1ff);
                }
                for ($i = 0; $i < 256; ++$i) {
                    $S0[$i] = $m0[$q0[$q0[$q1[$i] ^ $s8] ^ $s4] ^ $s0];
                    $S1[$i] = $m1[$q0[$q1[$q1[$i] ^ $s9] ^ $s5] ^ $s1];
                    $S2[$i] = $m2[$q1[$q0[$q0[$i] ^ $sa] ^ $s6] ^ $s2];
                    $S3[$i] = $m3[$q1[$q1[$q0[$i] ^ $sb] ^ $s7] ^ $s3];
                }
                break;
            default: // 32
                list($sf, $se, $sd, $sc) = $this->_mdsrem($le_longs[1], $le_longs[2]);
                list($sb, $sa, $s9, $s8) = $this->_mdsrem($le_longs[3], $le_longs[4]);
                list($s7, $s6, $s5, $s4) = $this->_mdsrem($le_longs[5], $le_longs[6]);
                list($s3, $s2, $s1, $s0) = $this->_mdsrem($le_longs[7], $le_longs[8]);
                for ($i = 0, $j = 1; $i < 40; $i+= 2, $j+= 2) {
                    $A = $m0[$q0[$q0[$q1[$q1[$i] ^ $key[25]] ^ $key[17]] ^ $key[ 9]] ^ $key[1]] ^
                         $m1[$q0[$q1[$q1[$q0[$i] ^ $key[26]] ^ $key[18]] ^ $key[10]] ^ $key[2]] ^
                         $m2[$q1[$q0[$q0[$q0[$i] ^ $key[27]] ^ $key[19]] ^ $key[11]] ^ $key[3]] ^
                         $m3[$q1[$q1[$q0[$q1[$i] ^ $key[28]] ^ $key[20]] ^ $key[12]] ^ $key[4]];
                    $B = $m0[$q0[$q0[$q1[$q1[$j] ^ $key[29]] ^ $key[21]] ^ $key[13]] ^ $key[5]] ^
                         $m1[$q0[$q1[$q1[$q0[$j] ^ $key[30]] ^ $key[22]] ^ $key[14]] ^ $key[6]] ^
                         $m2[$q1[$q0[$q0[$q0[$j] ^ $key[31]] ^ $key[23]] ^ $key[15]] ^ $key[7]] ^
                         $m3[$q1[$q1[$q0[$q1[$j] ^ $key[32]] ^ $key[24]] ^ $key[16]] ^ $key[8]];
                    $B = ($B << 8) | ($B >> 24 & 0xff);
                    $A = $this->safe_intval($A + $B);
                    $K[] = $A;
                    $A = $this->safe_intval($A + $B);
                    $K[] = ($A << 9 | $A >> 23 & 0x1ff);
                }
                for ($i = 0; $i < 256; ++$i) {
                    $S0[$i] = $m0[$q0[$q0[$q1[$q1[$i] ^ $sc] ^ $s8] ^ $s4] ^ $s0];
                    $S1[$i] = $m1[$q0[$q1[$q1[$q0[$i] ^ $sd] ^ $s9] ^ $s5] ^ $s1];
                    $S2[$i] = $m2[$q1[$q0[$q0[$q0[$i] ^ $se] ^ $sa] ^ $s6] ^ $s2];
                    $S3[$i] = $m3[$q1[$q1[$q0[$q1[$i] ^ $sf] ^ $sb] ^ $s7] ^ $s3];
                }
        }

        $this->K  = $K;
        $this->S0 = $S0;
        $this->S1 = $S1;
        $this->S2 = $S2;
        $this->S3 = $S3;
    }

    /**
     * _mdsrem function using by the twofish cipher algorithm
     *
     * @access private
     * @param string $A
     * @param string $B
     * @return array
     */
    function _mdsrem($A, $B)
    {
        // No gain by unrolling this loop.
        for ($i = 0; $i < 8; ++$i) {
            // Get most significant coefficient.
            $t = 0xff & ($B >> 24);

            // Shift the others up.
            $B = ($B << 8) | (0xff & ($A >> 24));
            $A<<= 8;

            $u = $t << 1;

            // Subtract the modular polynomial on overflow.
            if ($t & 0x80) {
                $u^= 0x14d;
            }

            // Remove t * (a * x^2 + 1).
            $B ^= $t ^ ($u << 16);

            // Form u = a*t + t/a = t*(a + 1/a).
            $u^= 0x7fffffff & ($t >> 1);

            // Add the modular polynomial on underflow.
            if ($t & 0x01) {
                $u^= 0xa6 ;
            }

            // Remove t * (a + 1/a) * (x^3 + x).
            $B^= ($u << 24) | ($u << 8);
        }

        return array(
            0xff & $B >> 24,
            0xff & $B >> 16,
            0xff & $B >>  8,
            0xff & $B);
    }

    /**
     * Encrypts a block
     *
     * @access private
     * @param string $in
     * @return string
     */
    function _encryptBlock($in)
    {
        $S0 = $this->S0;
        $S1 = $this->S1;
        $S2 = $this->S2;
        $S3 = $this->S3;
        $K  = $this->K;

        $in = unpack("V4", $in);
        $R0 = $K[0] ^ $in[1];
        $R1 = $K[1] ^ $in[2];
        $R2 = $K[2] ^ $in[3];
        $R3 = $K[3] ^ $in[4];

        $ki = 7;
        while ($ki < 39) {
            $t0 = $S0[ $R0        & 0xff] ^
                  $S1[($R0 >>  8) & 0xff] ^
                  $S2[($R0 >> 16) & 0xff] ^
                  $S3[($R0 >> 24) & 0xff];
            $t1 = $S0[($R1 >> 24) & 0xff] ^
                  $S1[ $R1        & 0xff] ^
                  $S2[($R1 >>  8) & 0xff] ^
                  $S3[($R1 >> 16) & 0xff];
            $R2^= $this->safe_intval($t0 + $t1 + $K[++$ki]);
            $R2 = ($R2 >> 1 & 0x7fffffff) | ($R2 << 31);
            $R3 = ((($R3 >> 31) & 1) | ($R3 << 1)) ^ $this->safe_intval($t0 + ($t1 << 1) + $K[++$ki]);

            $t0 = $S0[ $R2        & 0xff] ^
                  $S1[($R2 >>  8) & 0xff] ^
                  $S2[($R2 >> 16) & 0xff] ^
                  $S3[($R2 >> 24) & 0xff];
            $t1 = $S0[($R3 >> 24) & 0xff] ^
                  $S1[ $R3        & 0xff] ^
                  $S2[($R3 >>  8) & 0xff] ^
                  $S3[($R3 >> 16) & 0xff];
            $R0^= $this->safe_intval($t0 + $t1 + $K[++$ki]);
            $R0 = ($R0 >> 1 & 0x7fffffff) | ($R0 << 31);
            $R1 = ((($R1 >> 31) & 1) | ($R1 << 1)) ^ $this->safe_intval($t0 + ($t1 << 1) + $K[++$ki]);
        }

        // @codingStandardsIgnoreStart
        return pack("V4", $K[4] ^ $R2,
                          $K[5] ^ $R3,
                          $K[6] ^ $R0,
                          $K[7] ^ $R1);
        // @codingStandardsIgnoreEnd
    }

    /**
     * Decrypts a block
     *
     * @access private
     * @param string $in
     * @return string
     */
    function _decryptBlock($in)
    {
        $S0 = $this->S0;
        $S1 = $this->S1;
        $S2 = $this->S2;
        $S3 = $this->S3;
        $K  = $this->K;

        $in = unpack("V4", $in);
        $R0 = $K[4] ^ $in[1];
        $R1 = $K[5] ^ $in[2];
        $R2 = $K[6] ^ $in[3];
        $R3 = $K[7] ^ $in[4];

        $ki = 40;
        while ($ki > 8) {
            $t0 = $S0[$R0       & 0xff] ^
                  $S1[$R0 >>  8 & 0xff] ^
                  $S2[$R0 >> 16 & 0xff] ^
                  $S3[$R0 >> 24 & 0xff];
            $t1 = $S0[$R1 >> 24 & 0xff] ^
                  $S1[$R1       & 0xff] ^
                  $S2[$R1 >>  8 & 0xff] ^
                  $S3[$R1 >> 16 & 0xff];
            $R3^= $this->safe_intval($t0 + ($t1 << 1) + $K[--$ki]);
            $R3 = $R3 >> 1 & 0x7fffffff | $R3 << 31;
            $R2 = ($R2 >> 31 & 0x1 | $R2 << 1) ^ $this->safe_intval($t0 + $t1 + $K[--$ki]);

            $t0 = $S0[$R2       & 0xff] ^
                  $S1[$R2 >>  8 & 0xff] ^
                  $S2[$R2 >> 16 & 0xff] ^
                  $S3[$R2 >> 24 & 0xff];
            $t1 = $S0[$R3 >> 24 & 0xff] ^
                  $S1[$R3       & 0xff] ^
                  $S2[$R3 >>  8 & 0xff] ^
                  $S3[$R3 >> 16 & 0xff];
            $R1^= $this->safe_intval($t0 + ($t1 << 1) + $K[--$ki]);
            $R1 = $R1 >> 1 & 0x7fffffff | $R1 << 31;
            $R0 = ($R0 >> 31 & 0x1 | $R0 << 1) ^ $this->safe_intval($t0 + $t1 + $K[--$ki]);
        }

        // @codingStandardsIgnoreStart
        return pack("V4", $K[0] ^ $R2,
                          $K[1] ^ $R3,
                          $K[2] ^ $R0,
                          $K[3] ^ $R1);
        // @codingStandardsIgnoreEnd
    }

    /**
     * Setup the performance-optimized function for de/encrypt()
     *
     * @see Crypt_Base::_setupInlineCrypt()
     * @access private
     */
    function _setupInlineCrypt()
    {
        $lambda_functions =& Crypt_Twofish::_getLambdaFunctions();

        // Max. 10 Ultra-Hi-optimized inline-crypt functions. After that, we'll (still) create very fast code, but not the ultimate fast one.
        // (Currently, for Crypt_Twofish, one generated $lambda_function cost on php5.5@32bit ~140kb unfreeable mem and ~240kb on php5.5@64bit)
        $gen_hi_opt_code = (bool)(count($lambda_functions) < 10);

        // Generation of a unique hash for our generated code
        $code_hash = "Crypt_Twofish, {$this->mode}";
        if ($gen_hi_opt_code) {
            $code_hash = str_pad($code_hash, 32) . $this->_hashInlineCryptFunction($this->key);
        }

        $safeint = $this->safe_intval_inline();

        if (!isset($lambda_functions[$code_hash])) {
            switch (true) {
                case $gen_hi_opt_code:
                    $K = $this->K;
                    $init_crypt = '
                        static $S0, $S1, $S2, $S3;
                        if (!$S0) {
                            for ($i = 0; $i < 256; ++$i) {
                                $S0[] = (int)$self->S0[$i];
                                $S1[] = (int)$self->S1[$i];
                                $S2[] = (int)$self->S2[$i];
                                $S3[] = (int)$self->S3[$i];
                            }
                        }
                    ';
                    break;
                default:
                    $K   = array();
                    for ($i = 0; $i < 40; ++$i) {
                        $K[] = '$K_' . $i;
                    }
                    $init_crypt = '
                        $S0 = $self->S0;
                        $S1 = $self->S1;
                        $S2 = $self->S2;
                        $S3 = $self->S3;
                        list(' . implode(',', $K) . ') = $self->K;
                    ';
            }

            // Generating encrypt code:
            $encrypt_block = '
                $in = unpack("V4", $in);
                $R0 = '.$K[0].' ^ $in[1];
                $R1 = '.$K[1].' ^ $in[2];
                $R2 = '.$K[2].' ^ $in[3];
                $R3 = '.$K[3].' ^ $in[4];
            ';
            for ($ki = 7, $i = 0; $i < 8; ++$i) {
                $encrypt_block.= '
                    $t0 = $S0[ $R0        & 0xff] ^
                          $S1[($R0 >>  8) & 0xff] ^
                          $S2[($R0 >> 16) & 0xff] ^
                          $S3[($R0 >> 24) & 0xff];
                    $t1 = $S0[($R1 >> 24) & 0xff] ^
                          $S1[ $R1        & 0xff] ^
                          $S2[($R1 >>  8) & 0xff] ^
                          $S3[($R1 >> 16) & 0xff];
                    $R2^= ' . sprintf($safeint, '$t0 + $t1 + ' . $K[++$ki]) . ';
                    $R2 = ($R2 >> 1 & 0x7fffffff) | ($R2 << 31);
                    $R3 = ((($R3 >> 31) & 1) | ($R3 << 1)) ^ ' . sprintf($safeint, '($t0 + ($t1 << 1) + ' . $K[++$ki] . ')') . ';

                    $t0 = $S0[ $R2        & 0xff] ^
                          $S1[($R2 >>  8) & 0xff] ^
                          $S2[($R2 >> 16) & 0xff] ^
                          $S3[($R2 >> 24) & 0xff];
                    $t1 = $S0[($R3 >> 24) & 0xff] ^
                          $S1[ $R3        & 0xff] ^
                          $S2[($R3 >>  8) & 0xff] ^
                          $S3[($R3 >> 16) & 0xff];
                    $R0^= ' . sprintf($safeint, '($t0 + $t1 + ' . $K[++$ki] . ')') . ';
                    $R0 = ($R0 >> 1 & 0x7fffffff) | ($R0 << 31);
                    $R1 = ((($R1 >> 31) & 1) | ($R1 << 1)) ^ ' . sprintf($safeint, '($t0 + ($t1 << 1) + ' . $K[++$ki] . ')') . ';
                ';
            }
            $encrypt_block.= '
                $in = pack("V4", ' . $K[4] . ' ^ $R2,
                                 ' . $K[5] . ' ^ $R3,
                                 ' . $K[6] . ' ^ $R0,
                                 ' . $K[7] . ' ^ $R1);
            ';

            // Generating decrypt code:
            $decrypt_block = '
                $in = unpack("V4", $in);
                $R0 = '.$K[4].' ^ $in[1];
                $R1 = '.$K[5].' ^ $in[2];
                $R2 = '.$K[6].' ^ $in[3];
                $R3 = '.$K[7].' ^ $in[4];
            ';
            for ($ki = 40, $i = 0; $i < 8; ++$i) {
                $decrypt_block.= '
                    $t0 = $S0[$R0       & 0xff] ^
                          $S1[$R0 >>  8 & 0xff] ^
                          $S2[$R0 >> 16 & 0xff] ^
                          $S3[$R0 >> 24 & 0xff];
                    $t1 = $S0[$R1 >> 24 & 0xff] ^
                          $S1[$R1       & 0xff] ^
                          $S2[$R1 >>  8 & 0xff] ^
                          $S3[$R1 >> 16 & 0xff];
                    $R3^= ' . sprintf($safeint, '$t0 + ($t1 << 1) + ' . $K[--$ki]) . ';
                    $R3 = $R3 >> 1 & 0x7fffffff | $R3 << 31;
                    $R2 = ($R2 >> 31 & 0x1 | $R2 << 1) ^ ' . sprintf($safeint, '($t0 + $t1 + '.$K[--$ki] . ')') . ';

                    $t0 = $S0[$R2       & 0xff] ^
                          $S1[$R2 >>  8 & 0xff] ^
                          $S2[$R2 >> 16 & 0xff] ^
                          $S3[$R2 >> 24 & 0xff];
                    $t1 = $S0[$R3 >> 24 & 0xff] ^
                          $S1[$R3       & 0xff] ^
                          $S2[$R3 >>  8 & 0xff] ^
                          $S3[$R3 >> 16 & 0xff];
                    $R1^= ' . sprintf($safeint, '$t0 + ($t1 << 1) + ' . $K[--$ki]) . ';
                    $R1 = $R1 >> 1 & 0x7fffffff | $R1 << 31;
                    $R0 = ($R0 >> 31 & 0x1 | $R0 << 1) ^ ' . sprintf($safeint, '($t0 + $t1 + '.$K[--$ki] . ')') . ';
                ';
            }
            $decrypt_block.= '
                $in = pack("V4", ' . $K[0] . ' ^ $R2,
                                 ' . $K[1] . ' ^ $R3,
                                 ' . $K[2] . ' ^ $R0,
                                 ' . $K[3] . ' ^ $R1);
            ';

            $lambda_functions[$code_hash] = $this->_createInlineCryptFunction(
                array(
                   'init_crypt'    => $init_crypt,
                   'init_encrypt'  => '',
                   'init_decrypt'  => '',
                   'encrypt_block' => $encrypt_block,
                   'decrypt_block' => $decrypt_block
                )
            );
        }
        $this->inline_crypt = $lambda_functions[$code_hash];
    }
}
vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php000064400000326662151327705700016221 0ustar00<?php

/**
 * Pure-PHP PKCS#1 (v2.1) compliant implementation of RSA.
 *
 * PHP versions 4 and 5
 *
 * Here's an example of how to encrypt and decrypt text with this library:
 * <code>
 * <?php
 *    include 'Crypt/RSA.php';
 *
 *    $rsa = new Crypt_RSA();
 *    extract($rsa->createKey());
 *
 *    $plaintext = 'terrafrost';
 *
 *    $rsa->loadKey($privatekey);
 *    $ciphertext = $rsa->encrypt($plaintext);
 *
 *    $rsa->loadKey($publickey);
 *    echo $rsa->decrypt($ciphertext);
 * ?>
 * </code>
 *
 * Here's an example of how to create signatures and verify signatures with this library:
 * <code>
 * <?php
 *    include 'Crypt/RSA.php';
 *
 *    $rsa = new Crypt_RSA();
 *    extract($rsa->createKey());
 *
 *    $plaintext = 'terrafrost';
 *
 *    $rsa->loadKey($privatekey);
 *    $signature = $rsa->sign($plaintext);
 *
 *    $rsa->loadKey($publickey);
 *    echo $rsa->verify($plaintext, $signature) ? 'verified' : 'unverified';
 * ?>
 * </code>
 *
 * LICENSE: Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 *
 * @category  Crypt
 * @package   Crypt_RSA
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2009 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      http://phpseclib.sourceforge.net
 */

/**
 * Include Crypt_Random
 */
// the class_exists() will only be called if the crypt_random_string function hasn't been defined and
// will trigger a call to __autoload() if you're wanting to auto-load classes
// call function_exists() a second time to stop the include_once from being called outside
// of the auto loader
if (!function_exists('crypt_random_string')) {
    include_once 'Random.php';
}

/**
 * Include Crypt_Hash
 */
if (!class_exists('Crypt_Hash')) {
    include_once 'Hash.php';
}

/**#@+
 * @access public
 * @see self::encrypt()
 * @see self::decrypt()
 */
/**
 * Use {@link http://en.wikipedia.org/wiki/Optimal_Asymmetric_Encryption_Padding Optimal Asymmetric Encryption Padding}
 * (OAEP) for encryption / decryption.
 *
 * Uses sha1 by default.
 *
 * @see self::setHash()
 * @see self::setMGFHash()
 */
define('CRYPT_RSA_ENCRYPTION_OAEP',  1);
/**
 * Use PKCS#1 padding.
 *
 * Although CRYPT_RSA_ENCRYPTION_OAEP offers more security, including PKCS#1 padding is necessary for purposes of backwards
 * compatibility with protocols (like SSH-1) written before OAEP's introduction.
 */
define('CRYPT_RSA_ENCRYPTION_PKCS1', 2);
/**
 * Do not use any padding
 *
 * Although this method is not recommended it can none-the-less sometimes be useful if you're trying to decrypt some legacy
 * stuff, if you're trying to diagnose why an encrypted message isn't decrypting, etc.
 */
define('CRYPT_RSA_ENCRYPTION_NONE', 3);
/**#@-*/

/**#@+
 * @access public
 * @see self::sign()
 * @see self::verify()
 * @see self::setHash()
 */
/**
 * Use the Probabilistic Signature Scheme for signing
 *
 * Uses sha1 by default.
 *
 * @see self::setSaltLength()
 * @see self::setMGFHash()
 */
define('CRYPT_RSA_SIGNATURE_PSS',  1);
/**
 * Use the PKCS#1 scheme by default.
 *
 * Although CRYPT_RSA_SIGNATURE_PSS offers more security, including PKCS#1 signing is necessary for purposes of backwards
 * compatibility with protocols (like SSH-2) written before PSS's introduction.
 */
define('CRYPT_RSA_SIGNATURE_PKCS1', 2);
/**#@-*/

/**#@+
 * @access private
 * @see self::createKey()
 */
/**
 * ASN1 Integer
 */
define('CRYPT_RSA_ASN1_INTEGER',     2);
/**
 * ASN1 Bit String
 */
define('CRYPT_RSA_ASN1_BITSTRING',   3);
/**
 * ASN1 Octet String
 */
define('CRYPT_RSA_ASN1_OCTETSTRING', 4);
/**
 * ASN1 Object Identifier
 */
define('CRYPT_RSA_ASN1_OBJECT',      6);
/**
 * ASN1 Sequence (with the constucted bit set)
 */
define('CRYPT_RSA_ASN1_SEQUENCE',   48);
/**#@-*/

/**#@+
 * @access private
 * @see self::Crypt_RSA()
 */
/**
 * To use the pure-PHP implementation
 */
define('CRYPT_RSA_MODE_INTERNAL', 1);
/**
 * To use the OpenSSL library
 *
 * (if enabled; otherwise, the internal implementation will be used)
 */
define('CRYPT_RSA_MODE_OPENSSL', 2);
/**#@-*/

/**
 * Default openSSL configuration file.
 */
define('CRYPT_RSA_OPENSSL_CONFIG', dirname(__FILE__) . '/../openssl.cnf');

/**#@+
 * @access public
 * @see self::createKey()
 * @see self::setPrivateKeyFormat()
 */
/**
 * PKCS#1 formatted private key
 *
 * Used by OpenSSH
 */
define('CRYPT_RSA_PRIVATE_FORMAT_PKCS1', 0);
/**
 * PuTTY formatted private key
 */
define('CRYPT_RSA_PRIVATE_FORMAT_PUTTY', 1);
/**
 * XML formatted private key
 */
define('CRYPT_RSA_PRIVATE_FORMAT_XML', 2);
/**
 * PKCS#8 formatted private key
 */
define('CRYPT_RSA_PRIVATE_FORMAT_PKCS8', 8);
/**#@-*/

/**#@+
 * @access public
 * @see self::createKey()
 * @see self::setPublicKeyFormat()
 */
/**
 * Raw public key
 *
 * An array containing two Math_BigInteger objects.
 *
 * The exponent can be indexed with any of the following:
 *
 * 0, e, exponent, publicExponent
 *
 * The modulus can be indexed with any of the following:
 *
 * 1, n, modulo, modulus
 */
define('CRYPT_RSA_PUBLIC_FORMAT_RAW', 3);
/**
 * PKCS#1 formatted public key (raw)
 *
 * Used by File/X509.php
 *
 * Has the following header:
 *
 * -----BEGIN RSA PUBLIC KEY-----
 *
 * Analogous to ssh-keygen's pem format (as specified by -m)
 */
define('CRYPT_RSA_PUBLIC_FORMAT_PKCS1', 4);
define('CRYPT_RSA_PUBLIC_FORMAT_PKCS1_RAW', 4);
/**
 * XML formatted public key
 */
define('CRYPT_RSA_PUBLIC_FORMAT_XML', 5);
/**
 * OpenSSH formatted public key
 *
 * Place in $HOME/.ssh/authorized_keys
 */
define('CRYPT_RSA_PUBLIC_FORMAT_OPENSSH', 6);
/**
 * PKCS#1 formatted public key (encapsulated)
 *
 * Used by PHP's openssl_public_encrypt() and openssl's rsautl (when -pubin is set)
 *
 * Has the following header:
 *
 * -----BEGIN PUBLIC KEY-----
 *
 * Analogous to ssh-keygen's pkcs8 format (as specified by -m). Although PKCS8
 * is specific to private keys it's basically creating a DER-encoded wrapper
 * for keys. This just extends that same concept to public keys (much like ssh-keygen)
 */
define('CRYPT_RSA_PUBLIC_FORMAT_PKCS8', 7);
/**#@-*/

/**
 * Pure-PHP PKCS#1 compliant implementation of RSA.
 *
 * @package Crypt_RSA
 * @author  Jim Wigginton <terrafrost@php.net>
 * @access  public
 */
class Crypt_RSA
{
    /**
     * Precomputed Zero
     *
     * @var Math_BigInteger
     * @access private
     */
    var $zero;

    /**
     * Precomputed One
     *
     * @var Math_BigInteger
     * @access private
     */
    var $one;

    /**
     * Private Key Format
     *
     * @var int
     * @access private
     */
    var $privateKeyFormat = CRYPT_RSA_PRIVATE_FORMAT_PKCS1;

    /**
     * Public Key Format
     *
     * @var int
     * @access public
     */
    var $publicKeyFormat = CRYPT_RSA_PUBLIC_FORMAT_PKCS8;

    /**
     * Modulus (ie. n)
     *
     * @var Math_BigInteger
     * @access private
     */
    var $modulus;

    /**
     * Modulus length
     *
     * @var Math_BigInteger
     * @access private
     */
    var $k;

    /**
     * Exponent (ie. e or d)
     *
     * @var Math_BigInteger
     * @access private
     */
    var $exponent;

    /**
     * Primes for Chinese Remainder Theorem (ie. p and q)
     *
     * @var array
     * @access private
     */
    var $primes;

    /**
     * Exponents for Chinese Remainder Theorem (ie. dP and dQ)
     *
     * @var array
     * @access private
     */
    var $exponents;

    /**
     * Coefficients for Chinese Remainder Theorem (ie. qInv)
     *
     * @var array
     * @access private
     */
    var $coefficients;

    /**
     * Hash name
     *
     * @var string
     * @access private
     */
    var $hashName;

    /**
     * Hash function
     *
     * @var Crypt_Hash
     * @access private
     */
    var $hash;

    /**
     * Length of hash function output
     *
     * @var int
     * @access private
     */
    var $hLen;

    /**
     * Length of salt
     *
     * @var int
     * @access private
     */
    var $sLen;

    /**
     * Hash function for the Mask Generation Function
     *
     * @var Crypt_Hash
     * @access private
     */
    var $mgfHash;

    /**
     * Length of MGF hash function output
     *
     * @var int
     * @access private
     */
    var $mgfHLen;

    /**
     * Encryption mode
     *
     * @var int
     * @access private
     */
    var $encryptionMode = CRYPT_RSA_ENCRYPTION_OAEP;

    /**
     * Signature mode
     *
     * @var int
     * @access private
     */
    var $signatureMode = CRYPT_RSA_SIGNATURE_PSS;

    /**
     * Public Exponent
     *
     * @var mixed
     * @access private
     */
    var $publicExponent = false;

    /**
     * Password
     *
     * @var string
     * @access private
     */
    var $password = false;

    /**
     * Components
     *
     * For use with parsing XML formatted keys.  PHP's XML Parser functions use utilized - instead of PHP's DOM functions -
     * because PHP's XML Parser functions work on PHP4 whereas PHP's DOM functions - although surperior - don't.
     *
     * @see self::_start_element_handler()
     * @var array
     * @access private
     */
    var $components = array();

    /**
     * Current String
     *
     * For use with parsing XML formatted keys.
     *
     * @see self::_character_handler()
     * @see self::_stop_element_handler()
     * @var mixed
     * @access private
     */
    var $current;

    /**
     * OpenSSL configuration file name.
     *
     * Set to null to use system configuration file.
     * @see self::createKey()
     * @var mixed
     * @Access public
     */
    var $configFile;

    /**
     * Public key comment field.
     *
     * @var string
     * @access private
     */
    var $comment = 'phpseclib-generated-key';

    /**
     * The constructor
     *
     * If you want to make use of the openssl extension, you'll need to set the mode manually, yourself.  The reason
     * Crypt_RSA doesn't do it is because OpenSSL doesn't fail gracefully.  openssl_pkey_new(), in particular, requires
     * openssl.cnf be present somewhere and, unfortunately, the only real way to find out is too late.
     *
     * @return Crypt_RSA
     * @access public
     */
    function __construct()
    {
        if (!class_exists('Math_BigInteger')) {
            include_once 'Math/BigInteger.php';
        }

        $this->configFile = CRYPT_RSA_OPENSSL_CONFIG;

        if (!defined('CRYPT_RSA_MODE')) {
            switch (true) {
                // Math/BigInteger's openssl requirements are a little less stringent than Crypt/RSA's. in particular,
                // Math/BigInteger doesn't require an openssl.cfg file whereas Crypt/RSA does. so if Math/BigInteger
                // can't use OpenSSL it can be pretty trivially assumed, then, that Crypt/RSA can't either.
                case defined('MATH_BIGINTEGER_OPENSSL_DISABLE'):
                    define('CRYPT_RSA_MODE', CRYPT_RSA_MODE_INTERNAL);
                    break;
                // openssl_pkey_get_details - which is used in the only place Crypt/RSA.php uses OpenSSL - was introduced in PHP 5.2.0
                case !function_exists('openssl_pkey_get_details'):
                    define('CRYPT_RSA_MODE', CRYPT_RSA_MODE_INTERNAL);
                    break;
                case extension_loaded('openssl') && version_compare(PHP_VERSION, '4.2.0', '>=') && file_exists($this->configFile):
                    // some versions of XAMPP have mismatched versions of OpenSSL which causes it not to work
                    $versions = array();

                    // avoid generating errors (even with suppression) when phpinfo() is disabled (common in production systems)
                    if (strpos(ini_get('disable_functions'), 'phpinfo') === false) {
                        ob_start();
                        @phpinfo();
                        $content = ob_get_contents();
                        ob_end_clean();

                        preg_match_all('#OpenSSL (Header|Library) Version(.*)#im', $content, $matches);

                        if (!empty($matches[1])) {
                            for ($i = 0; $i < count($matches[1]); $i++) {
                                $fullVersion = trim(str_replace('=>', '', strip_tags($matches[2][$i])));

                                // Remove letter part in OpenSSL version
                                if (!preg_match('/(\d+\.\d+\.\d+)/i', $fullVersion, $m)) {
                                    $versions[$matches[1][$i]] = $fullVersion;
                                } else {
                                    $versions[$matches[1][$i]] = $m[0];
                                }
                            }
                        }
                    }

                    // it doesn't appear that OpenSSL versions were reported upon until PHP 5.3+
                    switch (true) {
                        case !isset($versions['Header']):
                        case !isset($versions['Library']):
                        case $versions['Header'] == $versions['Library']:
                        case version_compare($versions['Header'], '1.0.0') >= 0 && version_compare($versions['Library'], '1.0.0') >= 0:
                            define('CRYPT_RSA_MODE', CRYPT_RSA_MODE_OPENSSL);
                            break;
                        default:
                            define('CRYPT_RSA_MODE', CRYPT_RSA_MODE_INTERNAL);
                            define('MATH_BIGINTEGER_OPENSSL_DISABLE', true);
                    }
                    break;
                default:
                    define('CRYPT_RSA_MODE', CRYPT_RSA_MODE_INTERNAL);
            }
        }

        $this->zero = new Math_BigInteger();
        $this->one = new Math_BigInteger(1);

        $this->hash = new Crypt_Hash('sha1');
        $this->hLen = $this->hash->getLength();
        $this->hashName = 'sha1';
        $this->mgfHash = new Crypt_Hash('sha1');
        $this->mgfHLen = $this->mgfHash->getLength();
    }

    /**
     * PHP4 compatible Default Constructor.
     *
     * @see self::__construct()
     * @access public
     */
    function Crypt_RSA()
    {
        $this->__construct();
    }

    /**
     * Create public / private key pair
     *
     * Returns an array with the following three elements:
     *  - 'privatekey': The private key.
     *  - 'publickey':  The public key.
     *  - 'partialkey': A partially computed key (if the execution time exceeded $timeout).
     *                  Will need to be passed back to Crypt_RSA::createKey() as the third parameter for further processing.
     *
     * @access public
     * @param int $bits
     * @param int $timeout
     * @param Math_BigInteger $p
     */
    function createKey($bits = 1024, $timeout = false, $partial = array())
    {
        if (!defined('CRYPT_RSA_EXPONENT')) {
            // http://en.wikipedia.org/wiki/65537_%28number%29
            define('CRYPT_RSA_EXPONENT', '65537');
        }
        // per <http://cseweb.ucsd.edu/~hovav/dist/survey.pdf#page=5>, this number ought not result in primes smaller
        // than 256 bits. as a consequence if the key you're trying to create is 1024 bits and you've set CRYPT_RSA_SMALLEST_PRIME
        // to 384 bits then you're going to get a 384 bit prime and a 640 bit prime (384 + 1024 % 384). at least if
        // CRYPT_RSA_MODE is set to CRYPT_RSA_MODE_INTERNAL. if CRYPT_RSA_MODE is set to CRYPT_RSA_MODE_OPENSSL then
        // CRYPT_RSA_SMALLEST_PRIME is ignored (ie. multi-prime RSA support is more intended as a way to speed up RSA key
        // generation when there's a chance neither gmp nor OpenSSL are installed)
        if (!defined('CRYPT_RSA_SMALLEST_PRIME')) {
            define('CRYPT_RSA_SMALLEST_PRIME', 4096);
        }

        // OpenSSL uses 65537 as the exponent and requires RSA keys be 384 bits minimum
        if (CRYPT_RSA_MODE == CRYPT_RSA_MODE_OPENSSL && $bits >= 384 && CRYPT_RSA_EXPONENT == 65537) {
            $config = array();
            if (isset($this->configFile)) {
                $config['config'] = $this->configFile;
            }
            $rsa = openssl_pkey_new(array('private_key_bits' => $bits) + $config);
            openssl_pkey_export($rsa, $privatekey, null, $config);
            $publickey = openssl_pkey_get_details($rsa);
            $publickey = $publickey['key'];

            $privatekey = call_user_func_array(array($this, '_convertPrivateKey'), array_values($this->_parseKey($privatekey, CRYPT_RSA_PRIVATE_FORMAT_PKCS1)));
            $publickey = call_user_func_array(array($this, '_convertPublicKey'), array_values($this->_parseKey($publickey, CRYPT_RSA_PUBLIC_FORMAT_PKCS1)));

            // clear the buffer of error strings stemming from a minimalistic openssl.cnf
            while (openssl_error_string() !== false) {
            }

            return array(
                'privatekey' => $privatekey,
                'publickey' => $publickey,
                'partialkey' => false
            );
        }

        static $e;
        if (!isset($e)) {
            $e = new Math_BigInteger(CRYPT_RSA_EXPONENT);
        }

        extract($this->_generateMinMax($bits));
        $absoluteMin = $min;
        $temp = $bits >> 1; // divide by two to see how many bits P and Q would be
        if ($temp > CRYPT_RSA_SMALLEST_PRIME) {
            $num_primes = floor($bits / CRYPT_RSA_SMALLEST_PRIME);
            $temp = CRYPT_RSA_SMALLEST_PRIME;
        } else {
            $num_primes = 2;
        }
        extract($this->_generateMinMax($temp + $bits % $temp));
        $finalMax = $max;
        extract($this->_generateMinMax($temp));

        $generator = new Math_BigInteger();

        $n = $this->one->copy();
        if (!empty($partial)) {
            extract(unserialize($partial));
        } else {
            $exponents = $coefficients = $primes = array();
            $lcm = array(
                'top' => $this->one->copy(),
                'bottom' => false
            );
        }

        $start = time();
        $i0 = count($primes) + 1;

        do {
            for ($i = $i0; $i <= $num_primes; $i++) {
                if ($timeout !== false) {
                    $timeout-= time() - $start;
                    $start = time();
                    if ($timeout <= 0) {
                        return array(
                            'privatekey' => '',
                            'publickey'  => '',
                            'partialkey' => serialize(array(
                                'primes' => $primes,
                                'coefficients' => $coefficients,
                                'lcm' => $lcm,
                                'exponents' => $exponents
                            ))
                        );
                    }
                }

                if ($i == $num_primes) {
                    list($min, $temp) = $absoluteMin->divide($n);
                    if (!$temp->equals($this->zero)) {
                        $min = $min->add($this->one); // ie. ceil()
                    }
                    $primes[$i] = $generator->randomPrime($min, $finalMax, $timeout);
                } else {
                    $primes[$i] = $generator->randomPrime($min, $max, $timeout);
                }

                if ($primes[$i] === false) { // if we've reached the timeout
                    if (count($primes) > 1) {
                        $partialkey = '';
                    } else {
                        array_pop($primes);
                        $partialkey = serialize(array(
                            'primes' => $primes,
                            'coefficients' => $coefficients,
                            'lcm' => $lcm,
                            'exponents' => $exponents
                        ));
                    }

                    return array(
                        'privatekey' => '',
                        'publickey'  => '',
                        'partialkey' => $partialkey
                    );
                }

                // the first coefficient is calculated differently from the rest
                // ie. instead of being $primes[1]->modInverse($primes[2]), it's $primes[2]->modInverse($primes[1])
                if ($i > 2) {
                    $coefficients[$i] = $n->modInverse($primes[$i]);
                }

                $n = $n->multiply($primes[$i]);

                $temp = $primes[$i]->subtract($this->one);

                // textbook RSA implementations use Euler's totient function instead of the least common multiple.
                // see http://en.wikipedia.org/wiki/Euler%27s_totient_function
                $lcm['top'] = $lcm['top']->multiply($temp);
                $lcm['bottom'] = $lcm['bottom'] === false ? $temp : $lcm['bottom']->gcd($temp);

                $exponents[$i] = $e->modInverse($temp);
            }

            list($temp) = $lcm['top']->divide($lcm['bottom']);
            $gcd = $temp->gcd($e);
            $i0 = 1;
        } while (!$gcd->equals($this->one));

        $d = $e->modInverse($temp);

        $coefficients[2] = $primes[2]->modInverse($primes[1]);

        // from <http://tools.ietf.org/html/rfc3447#appendix-A.1.2>:
        // RSAPrivateKey ::= SEQUENCE {
        //     version           Version,
        //     modulus           INTEGER,  -- n
        //     publicExponent    INTEGER,  -- e
        //     privateExponent   INTEGER,  -- d
        //     prime1            INTEGER,  -- p
        //     prime2            INTEGER,  -- q
        //     exponent1         INTEGER,  -- d mod (p-1)
        //     exponent2         INTEGER,  -- d mod (q-1)
        //     coefficient       INTEGER,  -- (inverse of q) mod p
        //     otherPrimeInfos   OtherPrimeInfos OPTIONAL
        // }

        return array(
            'privatekey' => $this->_convertPrivateKey($n, $e, $d, $primes, $exponents, $coefficients),
            'publickey'  => $this->_convertPublicKey($n, $e),
            'partialkey' => false
        );
    }

    /**
     * Convert a private key to the appropriate format.
     *
     * @access private
     * @see self::setPrivateKeyFormat()
     * @param string $RSAPrivateKey
     * @return string
     */
    function _convertPrivateKey($n, $e, $d, $primes, $exponents, $coefficients)
    {
        $signed = $this->privateKeyFormat != CRYPT_RSA_PRIVATE_FORMAT_XML;
        $num_primes = count($primes);
        $raw = array(
            'version' => $num_primes == 2 ? chr(0) : chr(1), // two-prime vs. multi
            'modulus' => $n->toBytes($signed),
            'publicExponent' => $e->toBytes($signed),
            'privateExponent' => $d->toBytes($signed),
            'prime1' => $primes[1]->toBytes($signed),
            'prime2' => $primes[2]->toBytes($signed),
            'exponent1' => $exponents[1]->toBytes($signed),
            'exponent2' => $exponents[2]->toBytes($signed),
            'coefficient' => $coefficients[2]->toBytes($signed)
        );

        // if the format in question does not support multi-prime rsa and multi-prime rsa was used,
        // call _convertPublicKey() instead.
        switch ($this->privateKeyFormat) {
            case CRYPT_RSA_PRIVATE_FORMAT_XML:
                if ($num_primes != 2) {
                    return false;
                }
                return "<RSAKeyValue>\r\n" .
                       '  <Modulus>' . base64_encode($raw['modulus']) . "</Modulus>\r\n" .
                       '  <Exponent>' . base64_encode($raw['publicExponent']) . "</Exponent>\r\n" .
                       '  <P>' . base64_encode($raw['prime1']) . "</P>\r\n" .
                       '  <Q>' . base64_encode($raw['prime2']) . "</Q>\r\n" .
                       '  <DP>' . base64_encode($raw['exponent1']) . "</DP>\r\n" .
                       '  <DQ>' . base64_encode($raw['exponent2']) . "</DQ>\r\n" .
                       '  <InverseQ>' . base64_encode($raw['coefficient']) . "</InverseQ>\r\n" .
                       '  <D>' . base64_encode($raw['privateExponent']) . "</D>\r\n" .
                       '</RSAKeyValue>';
                break;
            case CRYPT_RSA_PRIVATE_FORMAT_PUTTY:
                if ($num_primes != 2) {
                    return false;
                }
                $key = "PuTTY-User-Key-File-2: ssh-rsa\r\nEncryption: ";
                $encryption = (!empty($this->password) || is_string($this->password)) ? 'aes256-cbc' : 'none';
                $key.= $encryption;
                $key.= "\r\nComment: " . $this->comment . "\r\n";
                $public = pack(
                    'Na*Na*Na*',
                    strlen('ssh-rsa'),
                    'ssh-rsa',
                    strlen($raw['publicExponent']),
                    $raw['publicExponent'],
                    strlen($raw['modulus']),
                    $raw['modulus']
                );
                $source = pack(
                    'Na*Na*Na*Na*',
                    strlen('ssh-rsa'),
                    'ssh-rsa',
                    strlen($encryption),
                    $encryption,
                    strlen($this->comment),
                    $this->comment,
                    strlen($public),
                    $public
                );
                $public = base64_encode($public);
                $key.= "Public-Lines: " . ((strlen($public) + 63) >> 6) . "\r\n";
                $key.= chunk_split($public, 64);
                $private = pack(
                    'Na*Na*Na*Na*',
                    strlen($raw['privateExponent']),
                    $raw['privateExponent'],
                    strlen($raw['prime1']),
                    $raw['prime1'],
                    strlen($raw['prime2']),
                    $raw['prime2'],
                    strlen($raw['coefficient']),
                    $raw['coefficient']
                );
                if (empty($this->password) && !is_string($this->password)) {
                    $source.= pack('Na*', strlen($private), $private);
                    $hashkey = 'putty-private-key-file-mac-key';
                } else {
                    $private.= crypt_random_string(16 - (strlen($private) & 15));
                    $source.= pack('Na*', strlen($private), $private);
                    if (!class_exists('Crypt_AES')) {
                        include_once 'Crypt/AES.php';
                    }
                    $sequence = 0;
                    $symkey = '';
                    while (strlen($symkey) < 32) {
                        $temp = pack('Na*', $sequence++, $this->password);
                        $symkey.= pack('H*', sha1($temp));
                    }
                    $symkey = substr($symkey, 0, 32);
                    $crypto = new Crypt_AES();

                    $crypto->setKey($symkey);
                    $crypto->disablePadding();
                    $private = $crypto->encrypt($private);
                    $hashkey = 'putty-private-key-file-mac-key' . $this->password;
                }

                $private = base64_encode($private);
                $key.= 'Private-Lines: ' . ((strlen($private) + 63) >> 6) . "\r\n";
                $key.= chunk_split($private, 64);
                if (!class_exists('Crypt_Hash')) {
                    include_once 'Crypt/Hash.php';
                }
                $hash = new Crypt_Hash('sha1');
                $hash->setKey(pack('H*', sha1($hashkey)));
                $key.= 'Private-MAC: ' . bin2hex($hash->hash($source)) . "\r\n";

                return $key;
            default: // eg. CRYPT_RSA_PRIVATE_FORMAT_PKCS1
                $components = array();
                foreach ($raw as $name => $value) {
                    $components[$name] = pack('Ca*a*', CRYPT_RSA_ASN1_INTEGER, $this->_encodeLength(strlen($value)), $value);
                }

                $RSAPrivateKey = implode('', $components);

                if ($num_primes > 2) {
                    $OtherPrimeInfos = '';
                    for ($i = 3; $i <= $num_primes; $i++) {
                        // OtherPrimeInfos ::= SEQUENCE SIZE(1..MAX) OF OtherPrimeInfo
                        //
                        // OtherPrimeInfo ::= SEQUENCE {
                        //     prime             INTEGER,  -- ri
                        //     exponent          INTEGER,  -- di
                        //     coefficient       INTEGER   -- ti
                        // }
                        $OtherPrimeInfo = pack('Ca*a*', CRYPT_RSA_ASN1_INTEGER, $this->_encodeLength(strlen($primes[$i]->toBytes(true))), $primes[$i]->toBytes(true));
                        $OtherPrimeInfo.= pack('Ca*a*', CRYPT_RSA_ASN1_INTEGER, $this->_encodeLength(strlen($exponents[$i]->toBytes(true))), $exponents[$i]->toBytes(true));
                        $OtherPrimeInfo.= pack('Ca*a*', CRYPT_RSA_ASN1_INTEGER, $this->_encodeLength(strlen($coefficients[$i]->toBytes(true))), $coefficients[$i]->toBytes(true));
                        $OtherPrimeInfos.= pack('Ca*a*', CRYPT_RSA_ASN1_SEQUENCE, $this->_encodeLength(strlen($OtherPrimeInfo)), $OtherPrimeInfo);
                    }
                    $RSAPrivateKey.= pack('Ca*a*', CRYPT_RSA_ASN1_SEQUENCE, $this->_encodeLength(strlen($OtherPrimeInfos)), $OtherPrimeInfos);
                }

                $RSAPrivateKey = pack('Ca*a*', CRYPT_RSA_ASN1_SEQUENCE, $this->_encodeLength(strlen($RSAPrivateKey)), $RSAPrivateKey);

                if ($this->privateKeyFormat == CRYPT_RSA_PRIVATE_FORMAT_PKCS8) {
                    $rsaOID = pack('H*', '300d06092a864886f70d0101010500'); // hex version of MA0GCSqGSIb3DQEBAQUA
                    $RSAPrivateKey = pack(
                        'Ca*a*Ca*a*',
                        CRYPT_RSA_ASN1_INTEGER,
                        "\01\00",
                        $rsaOID,
                        4,
                        $this->_encodeLength(strlen($RSAPrivateKey)),
                        $RSAPrivateKey
                    );
                    $RSAPrivateKey = pack('Ca*a*', CRYPT_RSA_ASN1_SEQUENCE, $this->_encodeLength(strlen($RSAPrivateKey)), $RSAPrivateKey);
                    if (!empty($this->password) || is_string($this->password)) {
                        $salt = crypt_random_string(8);
                        $iterationCount = 2048;

                        if (!class_exists('Crypt_DES')) {
                            include_once 'Crypt/DES.php';
                        }
                        $crypto = new Crypt_DES();
                        $crypto->setPassword($this->password, 'pbkdf1', 'md5', $salt, $iterationCount);
                        $RSAPrivateKey = $crypto->encrypt($RSAPrivateKey);

                        $parameters = pack(
                            'Ca*a*Ca*N',
                            CRYPT_RSA_ASN1_OCTETSTRING,
                            $this->_encodeLength(strlen($salt)),
                            $salt,
                            CRYPT_RSA_ASN1_INTEGER,
                            $this->_encodeLength(4),
                            $iterationCount
                        );
                        $pbeWithMD5AndDES_CBC = "\x2a\x86\x48\x86\xf7\x0d\x01\x05\x03";

                        $encryptionAlgorithm = pack(
                            'Ca*a*Ca*a*',
                            CRYPT_RSA_ASN1_OBJECT,
                            $this->_encodeLength(strlen($pbeWithMD5AndDES_CBC)),
                            $pbeWithMD5AndDES_CBC,
                            CRYPT_RSA_ASN1_SEQUENCE,
                            $this->_encodeLength(strlen($parameters)),
                            $parameters
                        );

                        $RSAPrivateKey = pack(
                            'Ca*a*Ca*a*',
                            CRYPT_RSA_ASN1_SEQUENCE,
                            $this->_encodeLength(strlen($encryptionAlgorithm)),
                            $encryptionAlgorithm,
                            CRYPT_RSA_ASN1_OCTETSTRING,
                            $this->_encodeLength(strlen($RSAPrivateKey)),
                            $RSAPrivateKey
                        );

                        $RSAPrivateKey = pack('Ca*a*', CRYPT_RSA_ASN1_SEQUENCE, $this->_encodeLength(strlen($RSAPrivateKey)), $RSAPrivateKey);

                        $RSAPrivateKey = "-----BEGIN ENCRYPTED PRIVATE KEY-----\r\n" .
                                         chunk_split(base64_encode($RSAPrivateKey), 64) .
                                         '-----END ENCRYPTED PRIVATE KEY-----';
                    } else {
                        $RSAPrivateKey = "-----BEGIN PRIVATE KEY-----\r\n" .
                                         chunk_split(base64_encode($RSAPrivateKey), 64) .
                                         '-----END PRIVATE KEY-----';
                    }
                    return $RSAPrivateKey;
                }

                if (!empty($this->password) || is_string($this->password)) {
                    $iv = crypt_random_string(8);
                    $symkey = pack('H*', md5($this->password . $iv)); // symkey is short for symmetric key
                    $symkey.= substr(pack('H*', md5($symkey . $this->password . $iv)), 0, 8);
                    if (!class_exists('Crypt_TripleDES')) {
                        include_once 'Crypt/TripleDES.php';
                    }
                    $des = new Crypt_TripleDES();
                    $des->setKey($symkey);
                    $des->setIV($iv);
                    $iv = strtoupper(bin2hex($iv));
                    $RSAPrivateKey = "-----BEGIN RSA PRIVATE KEY-----\r\n" .
                                     "Proc-Type: 4,ENCRYPTED\r\n" .
                                     "DEK-Info: DES-EDE3-CBC,$iv\r\n" .
                                     "\r\n" .
                                     chunk_split(base64_encode($des->encrypt($RSAPrivateKey)), 64) .
                                     '-----END RSA PRIVATE KEY-----';
                } else {
                    $RSAPrivateKey = "-----BEGIN RSA PRIVATE KEY-----\r\n" .
                                     chunk_split(base64_encode($RSAPrivateKey), 64) .
                                     '-----END RSA PRIVATE KEY-----';
                }

                return $RSAPrivateKey;
        }
    }

    /**
     * Convert a public key to the appropriate format
     *
     * @access private
     * @see self::setPublicKeyFormat()
     * @param string $RSAPrivateKey
     * @return string
     */
    function _convertPublicKey($n, $e)
    {
        $signed = $this->publicKeyFormat != CRYPT_RSA_PUBLIC_FORMAT_XML;

        $modulus = $n->toBytes($signed);
        $publicExponent = $e->toBytes($signed);

        switch ($this->publicKeyFormat) {
            case CRYPT_RSA_PUBLIC_FORMAT_RAW:
                return array('e' => $e->copy(), 'n' => $n->copy());
            case CRYPT_RSA_PUBLIC_FORMAT_XML:
                return "<RSAKeyValue>\r\n" .
                       '  <Modulus>' . base64_encode($modulus) . "</Modulus>\r\n" .
                       '  <Exponent>' . base64_encode($publicExponent) . "</Exponent>\r\n" .
                       '</RSAKeyValue>';
                break;
            case CRYPT_RSA_PUBLIC_FORMAT_OPENSSH:
                // from <http://tools.ietf.org/html/rfc4253#page-15>:
                // string    "ssh-rsa"
                // mpint     e
                // mpint     n
                $RSAPublicKey = pack('Na*Na*Na*', strlen('ssh-rsa'), 'ssh-rsa', strlen($publicExponent), $publicExponent, strlen($modulus), $modulus);
                $RSAPublicKey = 'ssh-rsa ' . base64_encode($RSAPublicKey) . ' ' . $this->comment;

                return $RSAPublicKey;
            default: // eg. CRYPT_RSA_PUBLIC_FORMAT_PKCS1_RAW or CRYPT_RSA_PUBLIC_FORMAT_PKCS1
                // from <http://tools.ietf.org/html/rfc3447#appendix-A.1.1>:
                // RSAPublicKey ::= SEQUENCE {
                //     modulus           INTEGER,  -- n
                //     publicExponent    INTEGER   -- e
                // }
                $components = array(
                    'modulus' => pack('Ca*a*', CRYPT_RSA_ASN1_INTEGER, $this->_encodeLength(strlen($modulus)), $modulus),
                    'publicExponent' => pack('Ca*a*', CRYPT_RSA_ASN1_INTEGER, $this->_encodeLength(strlen($publicExponent)), $publicExponent)
                );

                $RSAPublicKey = pack(
                    'Ca*a*a*',
                    CRYPT_RSA_ASN1_SEQUENCE,
                    $this->_encodeLength(strlen($components['modulus']) + strlen($components['publicExponent'])),
                    $components['modulus'],
                    $components['publicExponent']
                );

                if ($this->publicKeyFormat == CRYPT_RSA_PUBLIC_FORMAT_PKCS1_RAW) {
                    $RSAPublicKey = "-----BEGIN RSA PUBLIC KEY-----\r\n" .
                                    chunk_split(base64_encode($RSAPublicKey), 64) .
                                    '-----END RSA PUBLIC KEY-----';
                } else {
                    // sequence(oid(1.2.840.113549.1.1.1), null)) = rsaEncryption.
                    $rsaOID = pack('H*', '300d06092a864886f70d0101010500'); // hex version of MA0GCSqGSIb3DQEBAQUA
                    $RSAPublicKey = chr(0) . $RSAPublicKey;
                    $RSAPublicKey = chr(3) . $this->_encodeLength(strlen($RSAPublicKey)) . $RSAPublicKey;

                    $RSAPublicKey = pack(
                        'Ca*a*',
                        CRYPT_RSA_ASN1_SEQUENCE,
                        $this->_encodeLength(strlen($rsaOID . $RSAPublicKey)),
                        $rsaOID . $RSAPublicKey
                    );

                    $RSAPublicKey = "-----BEGIN PUBLIC KEY-----\r\n" .
                                     chunk_split(base64_encode($RSAPublicKey), 64) .
                                     '-----END PUBLIC KEY-----';
                }

                return $RSAPublicKey;
        }
    }

    /**
     * Break a public or private key down into its constituant components
     *
     * @access private
     * @see self::_convertPublicKey()
     * @see self::_convertPrivateKey()
     * @param string $key
     * @param int $type
     * @return array
     */
    function _parseKey($key, $type)
    {
        if ($type != CRYPT_RSA_PUBLIC_FORMAT_RAW && !is_string($key)) {
            return false;
        }

        switch ($type) {
            case CRYPT_RSA_PUBLIC_FORMAT_RAW:
                if (!is_array($key)) {
                    return false;
                }
                $components = array();
                switch (true) {
                    case isset($key['e']):
                        $components['publicExponent'] = $key['e']->copy();
                        break;
                    case isset($key['exponent']):
                        $components['publicExponent'] = $key['exponent']->copy();
                        break;
                    case isset($key['publicExponent']):
                        $components['publicExponent'] = $key['publicExponent']->copy();
                        break;
                    case isset($key[0]):
                        $components['publicExponent'] = $key[0]->copy();
                }
                switch (true) {
                    case isset($key['n']):
                        $components['modulus'] = $key['n']->copy();
                        break;
                    case isset($key['modulo']):
                        $components['modulus'] = $key['modulo']->copy();
                        break;
                    case isset($key['modulus']):
                        $components['modulus'] = $key['modulus']->copy();
                        break;
                    case isset($key[1]):
                        $components['modulus'] = $key[1]->copy();
                }
                return isset($components['modulus']) && isset($components['publicExponent']) ? $components : false;
            case CRYPT_RSA_PRIVATE_FORMAT_PKCS1:
            case CRYPT_RSA_PRIVATE_FORMAT_PKCS8:
            case CRYPT_RSA_PUBLIC_FORMAT_PKCS1:
                /* Although PKCS#1 proposes a format that public and private keys can use, encrypting them is
                   "outside the scope" of PKCS#1.  PKCS#1 then refers you to PKCS#12 and PKCS#15 if you're wanting to
                   protect private keys, however, that's not what OpenSSL* does.  OpenSSL protects private keys by adding
                   two new "fields" to the key - DEK-Info and Proc-Type.  These fields are discussed here:

                   http://tools.ietf.org/html/rfc1421#section-4.6.1.1
                   http://tools.ietf.org/html/rfc1421#section-4.6.1.3

                   DES-EDE3-CBC as an algorithm, however, is not discussed anywhere, near as I can tell.
                   DES-CBC and DES-EDE are discussed in RFC1423, however, DES-EDE3-CBC isn't, nor is its key derivation
                   function.  As is, the definitive authority on this encoding scheme isn't the IETF but rather OpenSSL's
                   own implementation.  ie. the implementation *is* the standard and any bugs that may exist in that
                   implementation are part of the standard, as well.

                   * OpenSSL is the de facto standard.  It's utilized by OpenSSH and other projects */
                if (preg_match('#DEK-Info: (.+),(.+)#', $key, $matches)) {
                    $iv = pack('H*', trim($matches[2]));
                    $symkey = pack('H*', md5($this->password . substr($iv, 0, 8))); // symkey is short for symmetric key
                    $symkey.= pack('H*', md5($symkey . $this->password . substr($iv, 0, 8)));
                    // remove the Proc-Type / DEK-Info sections as they're no longer needed
                    $key = preg_replace('#^(?:Proc-Type|DEK-Info): .*#m', '', $key);
                    $ciphertext = $this->_extractBER($key);
                    if ($ciphertext === false) {
                        $ciphertext = $key;
                    }
                    switch ($matches[1]) {
                        case 'AES-256-CBC':
                            if (!class_exists('Crypt_AES')) {
                                include_once 'Crypt/AES.php';
                            }
                            $crypto = new Crypt_AES();
                            break;
                        case 'AES-128-CBC':
                            if (!class_exists('Crypt_AES')) {
                                include_once 'Crypt/AES.php';
                            }
                            $symkey = substr($symkey, 0, 16);
                            $crypto = new Crypt_AES();
                            break;
                        case 'DES-EDE3-CFB':
                            if (!class_exists('Crypt_TripleDES')) {
                                include_once 'Crypt/TripleDES.php';
                            }
                            $crypto = new Crypt_TripleDES(CRYPT_DES_MODE_CFB);
                            break;
                        case 'DES-EDE3-CBC':
                            if (!class_exists('Crypt_TripleDES')) {
                                include_once 'Crypt/TripleDES.php';
                            }
                            $symkey = substr($symkey, 0, 24);
                            $crypto = new Crypt_TripleDES();
                            break;
                        case 'DES-CBC':
                            if (!class_exists('Crypt_DES')) {
                                include_once 'Crypt/DES.php';
                            }
                            $crypto = new Crypt_DES();
                            break;
                        default:
                            return false;
                    }
                    $crypto->setKey($symkey);
                    $crypto->setIV($iv);
                    $decoded = $crypto->decrypt($ciphertext);
                } else {
                    $decoded = $this->_extractBER($key);
                }

                if ($decoded !== false) {
                    $key = $decoded;
                }

                $components = array();

                if (ord($this->_string_shift($key)) != CRYPT_RSA_ASN1_SEQUENCE) {
                    return false;
                }
                if ($this->_decodeLength($key) != strlen($key)) {
                    return false;
                }

                $tag = ord($this->_string_shift($key));
                /* intended for keys for which OpenSSL's asn1parse returns the following:

                    0:d=0  hl=4 l= 631 cons: SEQUENCE
                    4:d=1  hl=2 l=   1 prim:  INTEGER           :00
                    7:d=1  hl=2 l=  13 cons:  SEQUENCE
                    9:d=2  hl=2 l=   9 prim:   OBJECT            :rsaEncryption
                   20:d=2  hl=2 l=   0 prim:   NULL
                   22:d=1  hl=4 l= 609 prim:  OCTET STRING

                   ie. PKCS8 keys*/

                if ($tag == CRYPT_RSA_ASN1_INTEGER && substr($key, 0, 3) == "\x01\x00\x30") {
                    $this->_string_shift($key, 3);
                    $tag = CRYPT_RSA_ASN1_SEQUENCE;
                }

                if ($tag == CRYPT_RSA_ASN1_SEQUENCE) {
                    $temp = $this->_string_shift($key, $this->_decodeLength($key));
                    if (ord($this->_string_shift($temp)) != CRYPT_RSA_ASN1_OBJECT) {
                        return false;
                    }
                    $length = $this->_decodeLength($temp);
                    switch ($this->_string_shift($temp, $length)) {
                        case "\x2a\x86\x48\x86\xf7\x0d\x01\x01\x01": // rsaEncryption
                            break;
                        case "\x2a\x86\x48\x86\xf7\x0d\x01\x05\x03": // pbeWithMD5AndDES-CBC
                            /*
                               PBEParameter ::= SEQUENCE {
                                   salt OCTET STRING (SIZE(8)),
                                   iterationCount INTEGER }
                            */
                            if (ord($this->_string_shift($temp)) != CRYPT_RSA_ASN1_SEQUENCE) {
                                return false;
                            }
                            if ($this->_decodeLength($temp) != strlen($temp)) {
                                return false;
                            }
                            $this->_string_shift($temp); // assume it's an octet string
                            $salt = $this->_string_shift($temp, $this->_decodeLength($temp));
                            if (ord($this->_string_shift($temp)) != CRYPT_RSA_ASN1_INTEGER) {
                                return false;
                            }
                            $this->_decodeLength($temp);
                            list(, $iterationCount) = unpack('N', str_pad($temp, 4, chr(0), STR_PAD_LEFT));
                            $this->_string_shift($key); // assume it's an octet string
                            $length = $this->_decodeLength($key);
                            if (strlen($key) != $length) {
                                return false;
                            }

                            if (!class_exists('Crypt_DES')) {
                                include_once 'Crypt/DES.php';
                            }
                            $crypto = new Crypt_DES();
                            $crypto->setPassword($this->password, 'pbkdf1', 'md5', $salt, $iterationCount);
                            $key = $crypto->decrypt($key);
                            if ($key === false) {
                                return false;
                            }
                            return $this->_parseKey($key, CRYPT_RSA_PRIVATE_FORMAT_PKCS1);
                        default:
                            return false;
                    }
                    /* intended for keys for which OpenSSL's asn1parse returns the following:

                        0:d=0  hl=4 l= 290 cons: SEQUENCE
                        4:d=1  hl=2 l=  13 cons:  SEQUENCE
                        6:d=2  hl=2 l=   9 prim:   OBJECT            :rsaEncryption
                       17:d=2  hl=2 l=   0 prim:   NULL
                       19:d=1  hl=4 l= 271 prim:  BIT STRING */
                    $tag = ord($this->_string_shift($key)); // skip over the BIT STRING / OCTET STRING tag
                    $this->_decodeLength($key); // skip over the BIT STRING / OCTET STRING length
                    // "The initial octet shall encode, as an unsigned binary integer wtih bit 1 as the least significant bit, the number of
                    //  unused bits in the final subsequent octet. The number shall be in the range zero to seven."
                    //  -- http://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf (section 8.6.2.2)
                    if ($tag == CRYPT_RSA_ASN1_BITSTRING) {
                        $this->_string_shift($key);
                    }
                    if (ord($this->_string_shift($key)) != CRYPT_RSA_ASN1_SEQUENCE) {
                        return false;
                    }
                    if ($this->_decodeLength($key) != strlen($key)) {
                        return false;
                    }
                    $tag = ord($this->_string_shift($key));
                }
                if ($tag != CRYPT_RSA_ASN1_INTEGER) {
                    return false;
                }

                $length = $this->_decodeLength($key);
                $temp = $this->_string_shift($key, $length);
                if (strlen($temp) != 1 || ord($temp) > 2) {
                    $components['modulus'] = new Math_BigInteger($temp, 256);
                    $this->_string_shift($key); // skip over CRYPT_RSA_ASN1_INTEGER
                    $length = $this->_decodeLength($key);
                    $components[$type == CRYPT_RSA_PUBLIC_FORMAT_PKCS1 ? 'publicExponent' : 'privateExponent'] = new Math_BigInteger($this->_string_shift($key, $length), 256);

                    return $components;
                }
                if (ord($this->_string_shift($key)) != CRYPT_RSA_ASN1_INTEGER) {
                    return false;
                }
                $length = $this->_decodeLength($key);
                $components['modulus'] = new Math_BigInteger($this->_string_shift($key, $length), 256);
                $this->_string_shift($key);
                $length = $this->_decodeLength($key);
                $components['publicExponent'] = new Math_BigInteger($this->_string_shift($key, $length), 256);
                $this->_string_shift($key);
                $length = $this->_decodeLength($key);
                $components['privateExponent'] = new Math_BigInteger($this->_string_shift($key, $length), 256);
                $this->_string_shift($key);
                $length = $this->_decodeLength($key);
                $components['primes'] = array(1 => new Math_BigInteger($this->_string_shift($key, $length), 256));
                $this->_string_shift($key);
                $length = $this->_decodeLength($key);
                $components['primes'][] = new Math_BigInteger($this->_string_shift($key, $length), 256);
                $this->_string_shift($key);
                $length = $this->_decodeLength($key);
                $components['exponents'] = array(1 => new Math_BigInteger($this->_string_shift($key, $length), 256));
                $this->_string_shift($key);
                $length = $this->_decodeLength($key);
                $components['exponents'][] = new Math_BigInteger($this->_string_shift($key, $length), 256);
                $this->_string_shift($key);
                $length = $this->_decodeLength($key);
                $components['coefficients'] = array(2 => new Math_BigInteger($this->_string_shift($key, $length), 256));

                if (!empty($key)) {
                    if (ord($this->_string_shift($key)) != CRYPT_RSA_ASN1_SEQUENCE) {
                        return false;
                    }
                    $this->_decodeLength($key);
                    while (!empty($key)) {
                        if (ord($this->_string_shift($key)) != CRYPT_RSA_ASN1_SEQUENCE) {
                            return false;
                        }
                        $this->_decodeLength($key);
                        $key = substr($key, 1);
                        $length = $this->_decodeLength($key);
                        $components['primes'][] = new Math_BigInteger($this->_string_shift($key, $length), 256);
                        $this->_string_shift($key);
                        $length = $this->_decodeLength($key);
                        $components['exponents'][] = new Math_BigInteger($this->_string_shift($key, $length), 256);
                        $this->_string_shift($key);
                        $length = $this->_decodeLength($key);
                        $components['coefficients'][] = new Math_BigInteger($this->_string_shift($key, $length), 256);
                    }
                }

                return $components;
            case CRYPT_RSA_PUBLIC_FORMAT_OPENSSH:
                $parts = explode(' ', $key, 3);

                $key = isset($parts[1]) ? base64_decode($parts[1]) : false;
                if ($key === false) {
                    return false;
                }

                $comment = isset($parts[2]) ? $parts[2] : false;

                $cleanup = substr($key, 0, 11) == "\0\0\0\7ssh-rsa";

                if (strlen($key) <= 4) {
                    return false;
                }
                extract(unpack('Nlength', $this->_string_shift($key, 4)));
                $publicExponent = new Math_BigInteger($this->_string_shift($key, $length), -256);
                if (strlen($key) <= 4) {
                    return false;
                }
                extract(unpack('Nlength', $this->_string_shift($key, 4)));
                $modulus = new Math_BigInteger($this->_string_shift($key, $length), -256);

                if ($cleanup && strlen($key)) {
                    if (strlen($key) <= 4) {
                        return false;
                    }
                    extract(unpack('Nlength', $this->_string_shift($key, 4)));
                    $realModulus = new Math_BigInteger($this->_string_shift($key, $length), -256);
                    return strlen($key) ? false : array(
                        'modulus' => $realModulus,
                        'publicExponent' => $modulus,
                        'comment' => $comment
                    );
                } else {
                    return strlen($key) ? false : array(
                        'modulus' => $modulus,
                        'publicExponent' => $publicExponent,
                        'comment' => $comment
                    );
                }
            // http://www.w3.org/TR/xmldsig-core/#sec-RSAKeyValue
            // http://en.wikipedia.org/wiki/XML_Signature
            case CRYPT_RSA_PRIVATE_FORMAT_XML:
            case CRYPT_RSA_PUBLIC_FORMAT_XML:
                $this->components = array();

                $xml = xml_parser_create('UTF-8');
                xml_set_object($xml, $this);
                xml_set_element_handler($xml, '_start_element_handler', '_stop_element_handler');
                xml_set_character_data_handler($xml, '_data_handler');
                // add <xml></xml> to account for "dangling" tags like <BitStrength>...</BitStrength> that are sometimes added
                if (!xml_parse($xml, '<xml>' . $key . '</xml>')) {
                    return false;
                }

                return isset($this->components['modulus']) && isset($this->components['publicExponent']) ? $this->components : false;
            // from PuTTY's SSHPUBK.C
            case CRYPT_RSA_PRIVATE_FORMAT_PUTTY:
                $components = array();
                $key = preg_split('#\r\n|\r|\n#', $key);
                $type = trim(preg_replace('#PuTTY-User-Key-File-2: (.+)#', '$1', $key[0]));
                if ($type != 'ssh-rsa') {
                    return false;
                }
                $encryption = trim(preg_replace('#Encryption: (.+)#', '$1', $key[1]));
                $comment = trim(preg_replace('#Comment: (.+)#', '$1', $key[2]));

                $publicLength = trim(preg_replace('#Public-Lines: (\d+)#', '$1', $key[3]));
                $public = base64_decode(implode('', array_map('trim', array_slice($key, 4, $publicLength))));
                $public = substr($public, 11);
                extract(unpack('Nlength', $this->_string_shift($public, 4)));
                $components['publicExponent'] = new Math_BigInteger($this->_string_shift($public, $length), -256);
                extract(unpack('Nlength', $this->_string_shift($public, 4)));
                $components['modulus'] = new Math_BigInteger($this->_string_shift($public, $length), -256);

                $privateLength = trim(preg_replace('#Private-Lines: (\d+)#', '$1', $key[$publicLength + 4]));
                $private = base64_decode(implode('', array_map('trim', array_slice($key, $publicLength + 5, $privateLength))));

                switch ($encryption) {
                    case 'aes256-cbc':
                        if (!class_exists('Crypt_AES')) {
                            include_once 'Crypt/AES.php';
                        }
                        $symkey = '';
                        $sequence = 0;
                        while (strlen($symkey) < 32) {
                            $temp = pack('Na*', $sequence++, $this->password);
                            $symkey.= pack('H*', sha1($temp));
                        }
                        $symkey = substr($symkey, 0, 32);
                        $crypto = new Crypt_AES();
                }

                if ($encryption != 'none') {
                    $crypto->setKey($symkey);
                    $crypto->disablePadding();
                    $private = $crypto->decrypt($private);
                    if ($private === false) {
                        return false;
                    }
                }

                extract(unpack('Nlength', $this->_string_shift($private, 4)));
                if (strlen($private) < $length) {
                    return false;
                }
                $components['privateExponent'] = new Math_BigInteger($this->_string_shift($private, $length), -256);
                extract(unpack('Nlength', $this->_string_shift($private, 4)));
                if (strlen($private) < $length) {
                    return false;
                }
                $components['primes'] = array(1 => new Math_BigInteger($this->_string_shift($private, $length), -256));
                extract(unpack('Nlength', $this->_string_shift($private, 4)));
                if (strlen($private) < $length) {
                    return false;
                }
                $components['primes'][] = new Math_BigInteger($this->_string_shift($private, $length), -256);

                $temp = $components['primes'][1]->subtract($this->one);
                $components['exponents'] = array(1 => $components['publicExponent']->modInverse($temp));
                $temp = $components['primes'][2]->subtract($this->one);
                $components['exponents'][] = $components['publicExponent']->modInverse($temp);

                extract(unpack('Nlength', $this->_string_shift($private, 4)));
                if (strlen($private) < $length) {
                    return false;
                }
                $components['coefficients'] = array(2 => new Math_BigInteger($this->_string_shift($private, $length), -256));

                return $components;
        }
    }

    /**
     * Returns the key size
     *
     * More specifically, this returns the size of the modulo in bits.
     *
     * @access public
     * @return int
     */
    function getSize()
    {
        return !isset($this->modulus) ? 0 : strlen($this->modulus->toBits());
    }

    /**
     * Start Element Handler
     *
     * Called by xml_set_element_handler()
     *
     * @access private
     * @param resource $parser
     * @param string $name
     * @param array $attribs
     */
    function _start_element_handler($parser, $name, $attribs)
    {
        //$name = strtoupper($name);
        switch ($name) {
            case 'MODULUS':
                $this->current = &$this->components['modulus'];
                break;
            case 'EXPONENT':
                $this->current = &$this->components['publicExponent'];
                break;
            case 'P':
                $this->current = &$this->components['primes'][1];
                break;
            case 'Q':
                $this->current = &$this->components['primes'][2];
                break;
            case 'DP':
                $this->current = &$this->components['exponents'][1];
                break;
            case 'DQ':
                $this->current = &$this->components['exponents'][2];
                break;
            case 'INVERSEQ':
                $this->current = &$this->components['coefficients'][2];
                break;
            case 'D':
                $this->current = &$this->components['privateExponent'];
        }
        $this->current = '';
    }

    /**
     * Stop Element Handler
     *
     * Called by xml_set_element_handler()
     *
     * @access private
     * @param resource $parser
     * @param string $name
     */
    function _stop_element_handler($parser, $name)
    {
        if (isset($this->current)) {
            $this->current = new Math_BigInteger(base64_decode($this->current), 256);
            unset($this->current);
        }
    }

    /**
     * Data Handler
     *
     * Called by xml_set_character_data_handler()
     *
     * @access private
     * @param resource $parser
     * @param string $data
     */
    function _data_handler($parser, $data)
    {
        if (!isset($this->current) || is_object($this->current)) {
            return;
        }
        $this->current.= trim($data);
    }

    /**
     * Loads a public or private key
     *
     * Returns true on success and false on failure (ie. an incorrect password was provided or the key was malformed)
     *
     * @access public
     * @param string $key
     * @param int $type optional
     */
    function loadKey($key, $type = false)
    {
        if (is_object($key) && strtolower(get_class($key)) == 'crypt_rsa') {
            $this->privateKeyFormat = $key->privateKeyFormat;
            $this->publicKeyFormat = $key->publicKeyFormat;
            $this->k = $key->k;
            $this->hLen = $key->hLen;
            $this->sLen = $key->sLen;
            $this->mgfHLen = $key->mgfHLen;
            $this->encryptionMode = $key->encryptionMode;
            $this->signatureMode = $key->signatureMode;
            $this->password = $key->password;
            $this->configFile = $key->configFile;
            $this->comment = $key->comment;

            if (is_object($key->hash)) {
                $this->hash = new Crypt_Hash($key->hash->getHash());
            }
            if (is_object($key->mgfHash)) {
                $this->mgfHash = new Crypt_Hash($key->mgfHash->getHash());
            }

            if (is_object($key->modulus)) {
                $this->modulus = $key->modulus->copy();
            }
            if (is_object($key->exponent)) {
                $this->exponent = $key->exponent->copy();
            }
            if (is_object($key->publicExponent)) {
                $this->publicExponent = $key->publicExponent->copy();
            }

            $this->primes = array();
            $this->exponents = array();
            $this->coefficients = array();

            foreach ($this->primes as $prime) {
                $this->primes[] = $prime->copy();
            }
            foreach ($this->exponents as $exponent) {
                $this->exponents[] = $exponent->copy();
            }
            foreach ($this->coefficients as $coefficient) {
                $this->coefficients[] = $coefficient->copy();
            }

            return true;
        }

        if ($type === false) {
            $types = array(
                CRYPT_RSA_PUBLIC_FORMAT_RAW,
                CRYPT_RSA_PRIVATE_FORMAT_PKCS1,
                CRYPT_RSA_PRIVATE_FORMAT_XML,
                CRYPT_RSA_PRIVATE_FORMAT_PUTTY,
                CRYPT_RSA_PUBLIC_FORMAT_OPENSSH
            );
            foreach ($types as $type) {
                $components = $this->_parseKey($key, $type);
                if ($components !== false) {
                    break;
                }
            }
        } else {
            $components = $this->_parseKey($key, $type);
        }

        if ($components === false) {
            $this->comment = null;
            $this->modulus = null;
            $this->k = null;
            $this->exponent = null;
            $this->primes = null;
            $this->exponents = null;
            $this->coefficients = null;
            $this->publicExponent = null;

            return false;
        }

        if (isset($components['comment']) && $components['comment'] !== false) {
            $this->comment = $components['comment'];
        }
        $this->modulus = $components['modulus'];
        $this->k = strlen($this->modulus->toBytes());
        $this->exponent = isset($components['privateExponent']) ? $components['privateExponent'] : $components['publicExponent'];
        if (isset($components['primes'])) {
            $this->primes = $components['primes'];
            $this->exponents = $components['exponents'];
            $this->coefficients = $components['coefficients'];
            $this->publicExponent = $components['publicExponent'];
        } else {
            $this->primes = array();
            $this->exponents = array();
            $this->coefficients = array();
            $this->publicExponent = false;
        }

        switch ($type) {
            case CRYPT_RSA_PUBLIC_FORMAT_OPENSSH:
            case CRYPT_RSA_PUBLIC_FORMAT_RAW:
                $this->setPublicKey();
                break;
            case CRYPT_RSA_PRIVATE_FORMAT_PKCS1:
                switch (true) {
                    case strpos($key, '-BEGIN PUBLIC KEY-') !== false:
                    case strpos($key, '-BEGIN RSA PUBLIC KEY-') !== false:
                        $this->setPublicKey();
                }
        }

        return true;
    }

    /**
     * Sets the password
     *
     * Private keys can be encrypted with a password.  To unset the password, pass in the empty string or false.
     * Or rather, pass in $password such that empty($password) && !is_string($password) is true.
     *
     * @see self::createKey()
     * @see self::loadKey()
     * @access public
     * @param string $password
     */
    function setPassword($password = false)
    {
        $this->password = $password;
    }

    /**
     * Defines the public key
     *
     * Some private key formats define the public exponent and some don't.  Those that don't define it are problematic when
     * used in certain contexts.  For example, in SSH-2, RSA authentication works by sending the public key along with a
     * message signed by the private key to the server.  The SSH-2 server looks the public key up in an index of public keys
     * and if it's present then proceeds to verify the signature.  Problem is, if your private key doesn't include the public
     * exponent this won't work unless you manually add the public exponent. phpseclib tries to guess if the key being used
     * is the public key but in the event that it guesses incorrectly you might still want to explicitly set the key as being
     * public.
     *
     * Do note that when a new key is loaded the index will be cleared.
     *
     * Returns true on success, false on failure
     *
     * @see self::getPublicKey()
     * @access public
     * @param string $key optional
     * @param int $type optional
     * @return bool
     */
    function setPublicKey($key = false, $type = false)
    {
        // if a public key has already been loaded return false
        if (!empty($this->publicExponent)) {
            return false;
        }

        if ($key === false && !empty($this->modulus)) {
            $this->publicExponent = $this->exponent;
            return true;
        }

        if ($type === false) {
            $types = array(
                CRYPT_RSA_PUBLIC_FORMAT_RAW,
                CRYPT_RSA_PUBLIC_FORMAT_PKCS1,
                CRYPT_RSA_PUBLIC_FORMAT_XML,
                CRYPT_RSA_PUBLIC_FORMAT_OPENSSH
            );
            foreach ($types as $type) {
                $components = $this->_parseKey($key, $type);
                if ($components !== false) {
                    break;
                }
            }
        } else {
            $components = $this->_parseKey($key, $type);
        }

        if ($components === false) {
            return false;
        }

        if (empty($this->modulus) || !$this->modulus->equals($components['modulus'])) {
            $this->modulus = $components['modulus'];
            $this->exponent = $this->publicExponent = $components['publicExponent'];
            return true;
        }

        $this->publicExponent = $components['publicExponent'];

        return true;
    }

    /**
     * Defines the private key
     *
     * If phpseclib guessed a private key was a public key and loaded it as such it might be desirable to force
     * phpseclib to treat the key as a private key. This function will do that.
     *
     * Do note that when a new key is loaded the index will be cleared.
     *
     * Returns true on success, false on failure
     *
     * @see self::getPublicKey()
     * @access public
     * @param string $key optional
     * @param int $type optional
     * @return bool
     */
    function setPrivateKey($key = false, $type = false)
    {
        if ($key === false && !empty($this->publicExponent)) {
            $this->publicExponent = false;
            return true;
        }

        $rsa = new Crypt_RSA();
        if (!$rsa->loadKey($key, $type)) {
            return false;
        }
        $rsa->publicExponent = false;

        // don't overwrite the old key if the new key is invalid
        $this->loadKey($rsa);
        return true;
    }

    /**
     * Returns the public key
     *
     * The public key is only returned under two circumstances - if the private key had the public key embedded within it
     * or if the public key was set via setPublicKey().  If the currently loaded key is supposed to be the public key this
     * function won't return it since this library, for the most part, doesn't distinguish between public and private keys.
     *
     * @see self::getPublicKey()
     * @access public
     * @param string $key
     * @param int $type optional
     */
    function getPublicKey($type = CRYPT_RSA_PUBLIC_FORMAT_PKCS8)
    {
        if (empty($this->modulus) || empty($this->publicExponent)) {
            return false;
        }

        $oldFormat = $this->publicKeyFormat;
        $this->publicKeyFormat = $type;
        $temp = $this->_convertPublicKey($this->modulus, $this->publicExponent);
        $this->publicKeyFormat = $oldFormat;
        return $temp;
    }

    /**
     * Returns the public key's fingerprint
     *
     * The public key's fingerprint is returned, which is equivalent to running `ssh-keygen -lf rsa.pub`. If there is
     * no public key currently loaded, false is returned.
     * Example output (md5): "c1:b1:30:29:d7:b8:de:6c:97:77:10:d7:46:41:63:87" (as specified by RFC 4716)
     *
     * @access public
     * @param string $algorithm The hashing algorithm to be used. Valid options are 'md5' and 'sha256'. False is returned
     * for invalid values.
     * @return mixed
     */
    function getPublicKeyFingerprint($algorithm = 'md5')
    {
        if (empty($this->modulus) || empty($this->publicExponent)) {
            return false;
        }

        $modulus = $this->modulus->toBytes(true);
        $publicExponent = $this->publicExponent->toBytes(true);

        $RSAPublicKey = pack('Na*Na*Na*', strlen('ssh-rsa'), 'ssh-rsa', strlen($publicExponent), $publicExponent, strlen($modulus), $modulus);

        switch ($algorithm) {
            case 'sha256':
                $hash = new Crypt_Hash('sha256');
                $base = base64_encode($hash->hash($RSAPublicKey));
                return substr($base, 0, strlen($base) - 1);
            case 'md5':
                return substr(chunk_split(md5($RSAPublicKey), 2, ':'), 0, -1);
            default:
                return false;
        }
    }

    /**
     * Returns the private key
     *
     * The private key is only returned if the currently loaded key contains the constituent prime numbers.
     *
     * @see self::getPublicKey()
     * @access public
     * @param string $key
     * @param int $type optional
     * @return mixed
     */
    function getPrivateKey($type = CRYPT_RSA_PUBLIC_FORMAT_PKCS1)
    {
        if (empty($this->primes)) {
            return false;
        }

        $oldFormat = $this->privateKeyFormat;
        $this->privateKeyFormat = $type;
        $temp = $this->_convertPrivateKey($this->modulus, $this->publicExponent, $this->exponent, $this->primes, $this->exponents, $this->coefficients);
        $this->privateKeyFormat = $oldFormat;
        return $temp;
    }

    /**
     * Returns a minimalistic private key
     *
     * Returns the private key without the prime number constituants.  Structurally identical to a public key that
     * hasn't been set as the public key
     *
     * @see self::getPrivateKey()
     * @access private
     * @param string $key
     * @param int $type optional
     */
    function _getPrivatePublicKey($mode = CRYPT_RSA_PUBLIC_FORMAT_PKCS8)
    {
        if (empty($this->modulus) || empty($this->exponent)) {
            return false;
        }

        $oldFormat = $this->publicKeyFormat;
        $this->publicKeyFormat = $mode;
        $temp = $this->_convertPublicKey($this->modulus, $this->exponent);
        $this->publicKeyFormat = $oldFormat;
        return $temp;
    }

    /**
     *  __toString() magic method
     *
     * @access public
     * @return string
     */
    function __toString()
    {
        $key = $this->getPrivateKey($this->privateKeyFormat);
        if ($key !== false) {
            return $key;
        }
        $key = $this->_getPrivatePublicKey($this->publicKeyFormat);
        return $key !== false ? $key : '';
    }

    /**
     *  __clone() magic method
     *
     * @access public
     * @return Crypt_RSA
     */
    function __clone()
    {
        $key = new Crypt_RSA();
        $key->loadKey($this);
        return $key;
    }

    /**
     * Generates the smallest and largest numbers requiring $bits bits
     *
     * @access private
     * @param int $bits
     * @return array
     */
    function _generateMinMax($bits)
    {
        $bytes = $bits >> 3;
        $min = str_repeat(chr(0), $bytes);
        $max = str_repeat(chr(0xFF), $bytes);
        $msb = $bits & 7;
        if ($msb) {
            $min = chr(1 << ($msb - 1)) . $min;
            $max = chr((1 << $msb) - 1) . $max;
        } else {
            $min[0] = chr(0x80);
        }

        return array(
            'min' => new Math_BigInteger($min, 256),
            'max' => new Math_BigInteger($max, 256)
        );
    }

    /**
     * DER-decode the length
     *
     * DER supports lengths up to (2**8)**127, however, we'll only support lengths up to (2**8)**4.  See
     * {@link http://itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#p=13 X.690 paragraph 8.1.3} for more information.
     *
     * @access private
     * @param string $string
     * @return int
     */
    function _decodeLength(&$string)
    {
        $length = ord($this->_string_shift($string));
        if ($length & 0x80) { // definite length, long form
            $length&= 0x7F;
            $temp = $this->_string_shift($string, $length);
            list(, $length) = unpack('N', substr(str_pad($temp, 4, chr(0), STR_PAD_LEFT), -4));
        }
        return $length;
    }

    /**
     * DER-encode the length
     *
     * DER supports lengths up to (2**8)**127, however, we'll only support lengths up to (2**8)**4.  See
     * {@link http://itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#p=13 X.690 paragraph 8.1.3} for more information.
     *
     * @access private
     * @param int $length
     * @return string
     */
    function _encodeLength($length)
    {
        if ($length <= 0x7F) {
            return chr($length);
        }

        $temp = ltrim(pack('N', $length), chr(0));
        return pack('Ca*', 0x80 | strlen($temp), $temp);
    }

    /**
     * String Shift
     *
     * Inspired by array_shift
     *
     * @param string $string
     * @param int $index
     * @return string
     * @access private
     */
    function _string_shift(&$string, $index = 1)
    {
        $substr = substr($string, 0, $index);
        $string = substr($string, $index);
        return $substr;
    }

    /**
     * Determines the private key format
     *
     * @see self::createKey()
     * @access public
     * @param int $format
     */
    function setPrivateKeyFormat($format)
    {
        $this->privateKeyFormat = $format;
    }

    /**
     * Determines the public key format
     *
     * @see self::createKey()
     * @access public
     * @param int $format
     */
    function setPublicKeyFormat($format)
    {
        $this->publicKeyFormat = $format;
    }

    /**
     * Determines which hashing function should be used
     *
     * Used with signature production / verification and (if the encryption mode is CRYPT_RSA_ENCRYPTION_OAEP) encryption and
     * decryption.  If $hash isn't supported, sha1 is used.
     *
     * @access public
     * @param string $hash
     */
    function setHash($hash)
    {
        // Crypt_Hash supports algorithms that PKCS#1 doesn't support.  md5-96 and sha1-96, for example.
        switch ($hash) {
            case 'md2':
            case 'md5':
            case 'sha1':
            case 'sha256':
            case 'sha384':
            case 'sha512':
                $this->hash = new Crypt_Hash($hash);
                $this->hashName = $hash;
                break;
            default:
                $this->hash = new Crypt_Hash('sha1');
                $this->hashName = 'sha1';
        }
        $this->hLen = $this->hash->getLength();
    }

    /**
     * Determines which hashing function should be used for the mask generation function
     *
     * The mask generation function is used by CRYPT_RSA_ENCRYPTION_OAEP and CRYPT_RSA_SIGNATURE_PSS and although it's
     * best if Hash and MGFHash are set to the same thing this is not a requirement.
     *
     * @access public
     * @param string $hash
     */
    function setMGFHash($hash)
    {
        // Crypt_Hash supports algorithms that PKCS#1 doesn't support.  md5-96 and sha1-96, for example.
        switch ($hash) {
            case 'md2':
            case 'md5':
            case 'sha1':
            case 'sha256':
            case 'sha384':
            case 'sha512':
                $this->mgfHash = new Crypt_Hash($hash);
                break;
            default:
                $this->mgfHash = new Crypt_Hash('sha1');
        }
        $this->mgfHLen = $this->mgfHash->getLength();
    }

    /**
     * Determines the salt length
     *
     * To quote from {@link http://tools.ietf.org/html/rfc3447#page-38 RFC3447#page-38}:
     *
     *    Typical salt lengths in octets are hLen (the length of the output
     *    of the hash function Hash) and 0.
     *
     * @access public
     * @param int $format
     */
    function setSaltLength($sLen)
    {
        $this->sLen = $sLen;
    }

    /**
     * Integer-to-Octet-String primitive
     *
     * See {@link http://tools.ietf.org/html/rfc3447#section-4.1 RFC3447#section-4.1}.
     *
     * @access private
     * @param Math_BigInteger $x
     * @param int $xLen
     * @return string
     */
    function _i2osp($x, $xLen)
    {
        $x = $x->toBytes();
        if (strlen($x) > $xLen) {
            user_error('Integer too large');
            return false;
        }
        return str_pad($x, $xLen, chr(0), STR_PAD_LEFT);
    }

    /**
     * Octet-String-to-Integer primitive
     *
     * See {@link http://tools.ietf.org/html/rfc3447#section-4.2 RFC3447#section-4.2}.
     *
     * @access private
     * @param string $x
     * @return Math_BigInteger
     */
    function _os2ip($x)
    {
        return new Math_BigInteger($x, 256);
    }

    /**
     * Exponentiate with or without Chinese Remainder Theorem
     *
     * See {@link http://tools.ietf.org/html/rfc3447#section-5.1.1 RFC3447#section-5.1.2}.
     *
     * @access private
     * @param Math_BigInteger $x
     * @return Math_BigInteger
     */
    function _exponentiate($x)
    {
        switch (true) {
            case empty($this->primes):
            case $this->primes[1]->equals($this->zero):
            case empty($this->coefficients):
            case $this->coefficients[2]->equals($this->zero):
            case empty($this->exponents):
            case $this->exponents[1]->equals($this->zero):
                return $x->modPow($this->exponent, $this->modulus);
        }

        $num_primes = count($this->primes);

        if (defined('CRYPT_RSA_DISABLE_BLINDING')) {
            $m_i = array(
                1 => $x->modPow($this->exponents[1], $this->primes[1]),
                2 => $x->modPow($this->exponents[2], $this->primes[2])
            );
            $h = $m_i[1]->subtract($m_i[2]);
            $h = $h->multiply($this->coefficients[2]);
            list(, $h) = $h->divide($this->primes[1]);
            $m = $m_i[2]->add($h->multiply($this->primes[2]));

            $r = $this->primes[1];
            for ($i = 3; $i <= $num_primes; $i++) {
                $m_i = $x->modPow($this->exponents[$i], $this->primes[$i]);

                $r = $r->multiply($this->primes[$i - 1]);

                $h = $m_i->subtract($m);
                $h = $h->multiply($this->coefficients[$i]);
                list(, $h) = $h->divide($this->primes[$i]);

                $m = $m->add($r->multiply($h));
            }
        } else {
            $smallest = $this->primes[1];
            for ($i = 2; $i <= $num_primes; $i++) {
                if ($smallest->compare($this->primes[$i]) > 0) {
                    $smallest = $this->primes[$i];
                }
            }

            $one = new Math_BigInteger(1);

            $r = $one->random($one, $smallest->subtract($one));

            $m_i = array(
                1 => $this->_blind($x, $r, 1),
                2 => $this->_blind($x, $r, 2)
            );
            $h = $m_i[1]->subtract($m_i[2]);
            $h = $h->multiply($this->coefficients[2]);
            list(, $h) = $h->divide($this->primes[1]);
            $m = $m_i[2]->add($h->multiply($this->primes[2]));

            $r = $this->primes[1];
            for ($i = 3; $i <= $num_primes; $i++) {
                $m_i = $this->_blind($x, $r, $i);

                $r = $r->multiply($this->primes[$i - 1]);

                $h = $m_i->subtract($m);
                $h = $h->multiply($this->coefficients[$i]);
                list(, $h) = $h->divide($this->primes[$i]);

                $m = $m->add($r->multiply($h));
            }
        }

        return $m;
    }

    /**
     * Performs RSA Blinding
     *
     * Protects against timing attacks by employing RSA Blinding.
     * Returns $x->modPow($this->exponents[$i], $this->primes[$i])
     *
     * @access private
     * @param Math_BigInteger $x
     * @param Math_BigInteger $r
     * @param int $i
     * @return Math_BigInteger
     */
    function _blind($x, $r, $i)
    {
        $x = $x->multiply($r->modPow($this->publicExponent, $this->primes[$i]));
        $x = $x->modPow($this->exponents[$i], $this->primes[$i]);

        $r = $r->modInverse($this->primes[$i]);
        $x = $x->multiply($r);
        list(, $x) = $x->divide($this->primes[$i]);

        return $x;
    }

    /**
     * Performs blinded RSA equality testing
     *
     * Protects against a particular type of timing attack described.
     *
     * See {@link http://codahale.com/a-lesson-in-timing-attacks/ A Lesson In Timing Attacks (or, Don't use MessageDigest.isEquals)}
     *
     * Thanks for the heads up singpolyma!
     *
     * @access private
     * @param string $x
     * @param string $y
     * @return bool
     */
    function _equals($x, $y)
    {
        if (strlen($x) != strlen($y)) {
            return false;
        }

        $result = 0;
        for ($i = 0; $i < strlen($x); $i++) {
            $result |= ord($x[$i]) ^ ord($y[$i]);
        }

        return $result == 0;
    }

    /**
     * RSAEP
     *
     * See {@link http://tools.ietf.org/html/rfc3447#section-5.1.1 RFC3447#section-5.1.1}.
     *
     * @access private
     * @param Math_BigInteger $m
     * @return Math_BigInteger
     */
    function _rsaep($m)
    {
        if ($m->compare($this->zero) < 0 || $m->compare($this->modulus) > 0) {
            user_error('Message representative out of range');
            return false;
        }
        return $this->_exponentiate($m);
    }

    /**
     * RSADP
     *
     * See {@link http://tools.ietf.org/html/rfc3447#section-5.1.2 RFC3447#section-5.1.2}.
     *
     * @access private
     * @param Math_BigInteger $c
     * @return Math_BigInteger
     */
    function _rsadp($c)
    {
        if ($c->compare($this->zero) < 0 || $c->compare($this->modulus) > 0) {
            user_error('Ciphertext representative out of range');
            return false;
        }
        return $this->_exponentiate($c);
    }

    /**
     * RSASP1
     *
     * See {@link http://tools.ietf.org/html/rfc3447#section-5.2.1 RFC3447#section-5.2.1}.
     *
     * @access private
     * @param Math_BigInteger $m
     * @return Math_BigInteger
     */
    function _rsasp1($m)
    {
        if ($m->compare($this->zero) < 0 || $m->compare($this->modulus) > 0) {
            user_error('Message representative out of range');
            return false;
        }
        return $this->_exponentiate($m);
    }

    /**
     * RSAVP1
     *
     * See {@link http://tools.ietf.org/html/rfc3447#section-5.2.2 RFC3447#section-5.2.2}.
     *
     * @access private
     * @param Math_BigInteger $s
     * @return Math_BigInteger
     */
    function _rsavp1($s)
    {
        if ($s->compare($this->zero) < 0 || $s->compare($this->modulus) > 0) {
            user_error('Signature representative out of range');
            return false;
        }
        return $this->_exponentiate($s);
    }

    /**
     * MGF1
     *
     * See {@link http://tools.ietf.org/html/rfc3447#appendix-B.2.1 RFC3447#appendix-B.2.1}.
     *
     * @access private
     * @param string $mgfSeed
     * @param int $mgfLen
     * @return string
     */
    function _mgf1($mgfSeed, $maskLen)
    {
        // if $maskLen would yield strings larger than 4GB, PKCS#1 suggests a "Mask too long" error be output.

        $t = '';
        $count = ceil($maskLen / $this->mgfHLen);
        for ($i = 0; $i < $count; $i++) {
            $c = pack('N', $i);
            $t.= $this->mgfHash->hash($mgfSeed . $c);
        }

        return substr($t, 0, $maskLen);
    }

    /**
     * RSAES-OAEP-ENCRYPT
     *
     * See {@link http://tools.ietf.org/html/rfc3447#section-7.1.1 RFC3447#section-7.1.1} and
     * {http://en.wikipedia.org/wiki/Optimal_Asymmetric_Encryption_Padding OAES}.
     *
     * @access private
     * @param string $m
     * @param string $l
     * @return string
     */
    function _rsaes_oaep_encrypt($m, $l = '')
    {
        $mLen = strlen($m);

        // Length checking

        // if $l is larger than two million terrabytes and you're using sha1, PKCS#1 suggests a "Label too long" error
        // be output.

        if ($mLen > $this->k - 2 * $this->hLen - 2) {
            user_error('Message too long');
            return false;
        }

        // EME-OAEP encoding

        $lHash = $this->hash->hash($l);
        $ps = str_repeat(chr(0), $this->k - $mLen - 2 * $this->hLen - 2);
        $db = $lHash . $ps . chr(1) . $m;
        $seed = crypt_random_string($this->hLen);
        $dbMask = $this->_mgf1($seed, $this->k - $this->hLen - 1);
        $maskedDB = $db ^ $dbMask;
        $seedMask = $this->_mgf1($maskedDB, $this->hLen);
        $maskedSeed = $seed ^ $seedMask;
        $em = chr(0) . $maskedSeed . $maskedDB;

        // RSA encryption

        $m = $this->_os2ip($em);
        $c = $this->_rsaep($m);
        $c = $this->_i2osp($c, $this->k);

        // Output the ciphertext C

        return $c;
    }

    /**
     * RSAES-OAEP-DECRYPT
     *
     * See {@link http://tools.ietf.org/html/rfc3447#section-7.1.2 RFC3447#section-7.1.2}.  The fact that the error
     * messages aren't distinguishable from one another hinders debugging, but, to quote from RFC3447#section-7.1.2:
     *
     *    Note.  Care must be taken to ensure that an opponent cannot
     *    distinguish the different error conditions in Step 3.g, whether by
     *    error message or timing, or, more generally, learn partial
     *    information about the encoded message EM.  Otherwise an opponent may
     *    be able to obtain useful information about the decryption of the
     *    ciphertext C, leading to a chosen-ciphertext attack such as the one
     *    observed by Manger [36].
     *
     * As for $l...  to quote from {@link http://tools.ietf.org/html/rfc3447#page-17 RFC3447#page-17}:
     *
     *    Both the encryption and the decryption operations of RSAES-OAEP take
     *    the value of a label L as input.  In this version of PKCS #1, L is
     *    the empty string; other uses of the label are outside the scope of
     *    this document.
     *
     * @access private
     * @param string $c
     * @param string $l
     * @return string
     */
    function _rsaes_oaep_decrypt($c, $l = '')
    {
        // Length checking

        // if $l is larger than two million terrabytes and you're using sha1, PKCS#1 suggests a "Label too long" error
        // be output.

        if (strlen($c) != $this->k || $this->k < 2 * $this->hLen + 2) {
            user_error('Decryption error');
            return false;
        }

        // RSA decryption

        $c = $this->_os2ip($c);
        $m = $this->_rsadp($c);
        if ($m === false) {
            user_error('Decryption error');
            return false;
        }
        $em = $this->_i2osp($m, $this->k);

        // EME-OAEP decoding

        $lHash = $this->hash->hash($l);
        $y = ord($em[0]);
        $maskedSeed = substr($em, 1, $this->hLen);
        $maskedDB = substr($em, $this->hLen + 1);
        $seedMask = $this->_mgf1($maskedDB, $this->hLen);
        $seed = $maskedSeed ^ $seedMask;
        $dbMask = $this->_mgf1($seed, $this->k - $this->hLen - 1);
        $db = $maskedDB ^ $dbMask;
        $lHash2 = substr($db, 0, $this->hLen);
        $m = substr($db, $this->hLen);
        if (!$this->_equals($lHash, $lHash2)) {
            user_error('Decryption error');
            return false;
        }
        $m = ltrim($m, chr(0));
        if (ord($m[0]) != 1) {
            user_error('Decryption error');
            return false;
        }

        // Output the message M

        return substr($m, 1);
    }

    /**
     * Raw Encryption / Decryption
     *
     * Doesn't use padding and is not recommended.
     *
     * @access private
     * @param string $m
     * @return string
     */
    function _raw_encrypt($m)
    {
        $temp = $this->_os2ip($m);
        $temp = $this->_rsaep($temp);
        return  $this->_i2osp($temp, $this->k);
    }

    /**
     * RSAES-PKCS1-V1_5-ENCRYPT
     *
     * See {@link http://tools.ietf.org/html/rfc3447#section-7.2.1 RFC3447#section-7.2.1}.
     *
     * @access private
     * @param string $m
     * @return string
     */
    function _rsaes_pkcs1_v1_5_encrypt($m)
    {
        $mLen = strlen($m);

        // Length checking

        if ($mLen > $this->k - 11) {
            user_error('Message too long');
            return false;
        }

        // EME-PKCS1-v1_5 encoding

        $psLen = $this->k - $mLen - 3;
        $ps = '';
        while (strlen($ps) != $psLen) {
            $temp = crypt_random_string($psLen - strlen($ps));
            $temp = str_replace("\x00", '', $temp);
            $ps.= $temp;
        }
        $type = 2;
        // see the comments of _rsaes_pkcs1_v1_5_decrypt() to understand why this is being done
        if (defined('CRYPT_RSA_PKCS15_COMPAT') && (!isset($this->publicExponent) || $this->exponent !== $this->publicExponent)) {
            $type = 1;
            // "The padding string PS shall consist of k-3-||D|| octets. ... for block type 01, they shall have value FF"
            $ps = str_repeat("\xFF", $psLen);
        }
        $em = chr(0) . chr($type) . $ps . chr(0) . $m;

        // RSA encryption
        $m = $this->_os2ip($em);
        $c = $this->_rsaep($m);
        $c = $this->_i2osp($c, $this->k);

        // Output the ciphertext C

        return $c;
    }

    /**
     * RSAES-PKCS1-V1_5-DECRYPT
     *
     * See {@link http://tools.ietf.org/html/rfc3447#section-7.2.2 RFC3447#section-7.2.2}.
     *
     * For compatibility purposes, this function departs slightly from the description given in RFC3447.
     * The reason being that RFC2313#section-8.1 (PKCS#1 v1.5) states that ciphertext's encrypted by the
     * private key should have the second byte set to either 0 or 1 and that ciphertext's encrypted by the
     * public key should have the second byte set to 2.  In RFC3447 (PKCS#1 v2.1), the second byte is supposed
     * to be 2 regardless of which key is used.  For compatibility purposes, we'll just check to make sure the
     * second byte is 2 or less.  If it is, we'll accept the decrypted string as valid.
     *
     * As a consequence of this, a private key encrypted ciphertext produced with Crypt_RSA may not decrypt
     * with a strictly PKCS#1 v1.5 compliant RSA implementation.  Public key encrypted ciphertext's should but
     * not private key encrypted ciphertext's.
     *
     * @access private
     * @param string $c
     * @return string
     */
    function _rsaes_pkcs1_v1_5_decrypt($c)
    {
        // Length checking

        if (strlen($c) != $this->k) { // or if k < 11
            user_error('Decryption error');
            return false;
        }

        // RSA decryption

        $c = $this->_os2ip($c);
        $m = $this->_rsadp($c);

        if ($m === false) {
            user_error('Decryption error');
            return false;
        }
        $em = $this->_i2osp($m, $this->k);

        // EME-PKCS1-v1_5 decoding

        if (ord($em[0]) != 0 || ord($em[1]) > 2) {
            user_error('Decryption error');
            return false;
        }

        $ps = substr($em, 2, strpos($em, chr(0), 2) - 2);
        $m = substr($em, strlen($ps) + 3);

        if (strlen($ps) < 8) {
            user_error('Decryption error');
            return false;
        }

        // Output M

        return $m;
    }

    /**
     * EMSA-PSS-ENCODE
     *
     * See {@link http://tools.ietf.org/html/rfc3447#section-9.1.1 RFC3447#section-9.1.1}.
     *
     * @access private
     * @param string $m
     * @param int $emBits
     */
    function _emsa_pss_encode($m, $emBits)
    {
        // if $m is larger than two million terrabytes and you're using sha1, PKCS#1 suggests a "Label too long" error
        // be output.

        $emLen = ($emBits + 1) >> 3; // ie. ceil($emBits / 8)
        $sLen = $this->sLen !== null ? $this->sLen : $this->hLen;

        $mHash = $this->hash->hash($m);
        if ($emLen < $this->hLen + $sLen + 2) {
            user_error('Encoding error');
            return false;
        }

        $salt = crypt_random_string($sLen);
        $m2 = "\0\0\0\0\0\0\0\0" . $mHash . $salt;
        $h = $this->hash->hash($m2);
        $ps = str_repeat(chr(0), $emLen - $sLen - $this->hLen - 2);
        $db = $ps . chr(1) . $salt;
        $dbMask = $this->_mgf1($h, $emLen - $this->hLen - 1);
        $maskedDB = $db ^ $dbMask;
        $maskedDB[0] = ~chr(0xFF << ($emBits & 7)) & $maskedDB[0];
        $em = $maskedDB . $h . chr(0xBC);

        return $em;
    }

    /**
     * EMSA-PSS-VERIFY
     *
     * See {@link http://tools.ietf.org/html/rfc3447#section-9.1.2 RFC3447#section-9.1.2}.
     *
     * @access private
     * @param string $m
     * @param string $em
     * @param int $emBits
     * @return string
     */
    function _emsa_pss_verify($m, $em, $emBits)
    {
        // if $m is larger than two million terrabytes and you're using sha1, PKCS#1 suggests a "Label too long" error
        // be output.

        $emLen = ($emBits + 1) >> 3; // ie. ceil($emBits / 8);
        $sLen = $this->sLen !== null ? $this->sLen : $this->hLen;

        $mHash = $this->hash->hash($m);
        if ($emLen < $this->hLen + $sLen + 2) {
            return false;
        }

        if ($em[strlen($em) - 1] != chr(0xBC)) {
            return false;
        }

        $maskedDB = substr($em, 0, -$this->hLen - 1);
        $h = substr($em, -$this->hLen - 1, $this->hLen);
        $temp = chr(0xFF << ($emBits & 7));
        if ((~$maskedDB[0] & $temp) != $temp) {
            return false;
        }
        $dbMask = $this->_mgf1($h, $emLen - $this->hLen - 1);
        $db = $maskedDB ^ $dbMask;
        $db[0] = ~chr(0xFF << ($emBits & 7)) & $db[0];
        $temp = $emLen - $this->hLen - $sLen - 2;
        if (substr($db, 0, $temp) != str_repeat(chr(0), $temp) || ord($db[$temp]) != 1) {
            return false;
        }
        $salt = substr($db, $temp + 1); // should be $sLen long
        $m2 = "\0\0\0\0\0\0\0\0" . $mHash . $salt;
        $h2 = $this->hash->hash($m2);
        return $this->_equals($h, $h2);
    }

    /**
     * RSASSA-PSS-SIGN
     *
     * See {@link http://tools.ietf.org/html/rfc3447#section-8.1.1 RFC3447#section-8.1.1}.
     *
     * @access private
     * @param string $m
     * @return string
     */
    function _rsassa_pss_sign($m)
    {
        // EMSA-PSS encoding

        $em = $this->_emsa_pss_encode($m, 8 * $this->k - 1);

        // RSA signature

        $m = $this->_os2ip($em);
        $s = $this->_rsasp1($m);
        $s = $this->_i2osp($s, $this->k);

        // Output the signature S

        return $s;
    }

    /**
     * RSASSA-PSS-VERIFY
     *
     * See {@link http://tools.ietf.org/html/rfc3447#section-8.1.2 RFC3447#section-8.1.2}.
     *
     * @access private
     * @param string $m
     * @param string $s
     * @return string
     */
    function _rsassa_pss_verify($m, $s)
    {
        // Length checking

        if (strlen($s) != $this->k) {
            user_error('Invalid signature');
            return false;
        }

        // RSA verification

        $modBits = 8 * $this->k;

        $s2 = $this->_os2ip($s);
        $m2 = $this->_rsavp1($s2);
        if ($m2 === false) {
            user_error('Invalid signature');
            return false;
        }
        $em = $this->_i2osp($m2, $modBits >> 3);
        if ($em === false) {
            user_error('Invalid signature');
            return false;
        }

        // EMSA-PSS verification

        return $this->_emsa_pss_verify($m, $em, $modBits - 1);
    }

    /**
     * EMSA-PKCS1-V1_5-ENCODE
     *
     * See {@link http://tools.ietf.org/html/rfc3447#section-9.2 RFC3447#section-9.2}.
     *
     * @access private
     * @param string $m
     * @param int $emLen
     * @return string
     */
    function _emsa_pkcs1_v1_5_encode($m, $emLen)
    {
        $h = $this->hash->hash($m);
        if ($h === false) {
            return false;
        }

        // see http://tools.ietf.org/html/rfc3447#page-43
        switch ($this->hashName) {
            case 'md2':
                $t = pack('H*', '3020300c06082a864886f70d020205000410');
                break;
            case 'md5':
                $t = pack('H*', '3020300c06082a864886f70d020505000410');
                break;
            case 'sha1':
                $t = pack('H*', '3021300906052b0e03021a05000414');
                break;
            case 'sha256':
                $t = pack('H*', '3031300d060960864801650304020105000420');
                break;
            case 'sha384':
                $t = pack('H*', '3041300d060960864801650304020205000430');
                break;
            case 'sha512':
                $t = pack('H*', '3051300d060960864801650304020305000440');
        }
        $t.= $h;
        $tLen = strlen($t);

        if ($emLen < $tLen + 11) {
            user_error('Intended encoded message length too short');
            return false;
        }

        $ps = str_repeat(chr(0xFF), $emLen - $tLen - 3);

        $em = "\0\1$ps\0$t";

        return $em;
    }

    /**
     * RSASSA-PKCS1-V1_5-SIGN
     *
     * See {@link http://tools.ietf.org/html/rfc3447#section-8.2.1 RFC3447#section-8.2.1}.
     *
     * @access private
     * @param string $m
     * @return string
     */
    function _rsassa_pkcs1_v1_5_sign($m)
    {
        // EMSA-PKCS1-v1_5 encoding

        $em = $this->_emsa_pkcs1_v1_5_encode($m, $this->k);
        if ($em === false) {
            user_error('RSA modulus too short');
            return false;
        }

        // RSA signature

        $m = $this->_os2ip($em);
        $s = $this->_rsasp1($m);
        $s = $this->_i2osp($s, $this->k);

        // Output the signature S

        return $s;
    }

    /**
     * RSASSA-PKCS1-V1_5-VERIFY
     *
     * See {@link http://tools.ietf.org/html/rfc3447#section-8.2.2 RFC3447#section-8.2.2}.
     *
     * @access private
     * @param string $m
     * @return string
     */
    function _rsassa_pkcs1_v1_5_verify($m, $s)
    {
        // Length checking

        if (strlen($s) != $this->k) {
            user_error('Invalid signature');
            return false;
        }

        // RSA verification

        $s = $this->_os2ip($s);
        $m2 = $this->_rsavp1($s);
        if ($m2 === false) {
            user_error('Invalid signature');
            return false;
        }
        $em = $this->_i2osp($m2, $this->k);
        if ($em === false) {
            user_error('Invalid signature');
            return false;
        }

        // EMSA-PKCS1-v1_5 encoding

        $em2 = $this->_emsa_pkcs1_v1_5_encode($m, $this->k);
        if ($em2 === false) {
            user_error('RSA modulus too short');
            return false;
        }

        // Compare
        return $this->_equals($em, $em2);
    }

    /**
     * Set Encryption Mode
     *
     * Valid values include CRYPT_RSA_ENCRYPTION_OAEP and CRYPT_RSA_ENCRYPTION_PKCS1.
     *
     * @access public
     * @param int $mode
     */
    function setEncryptionMode($mode)
    {
        $this->encryptionMode = $mode;
    }

    /**
     * Set Signature Mode
     *
     * Valid values include CRYPT_RSA_SIGNATURE_PSS and CRYPT_RSA_SIGNATURE_PKCS1
     *
     * @access public
     * @param int $mode
     */
    function setSignatureMode($mode)
    {
        $this->signatureMode = $mode;
    }

    /**
     * Set public key comment.
     *
     * @access public
     * @param string $comment
     */
    function setComment($comment)
    {
        $this->comment = $comment;
    }

    /**
     * Get public key comment.
     *
     * @access public
     * @return string
     */
    function getComment()
    {
        return $this->comment;
    }

    /**
     * Encryption
     *
     * Both CRYPT_RSA_ENCRYPTION_OAEP and CRYPT_RSA_ENCRYPTION_PKCS1 both place limits on how long $plaintext can be.
     * If $plaintext exceeds those limits it will be broken up so that it does and the resultant ciphertext's will
     * be concatenated together.
     *
     * @see self::decrypt()
     * @access public
     * @param string $plaintext
     * @return string
     */
    function encrypt($plaintext)
    {
        switch ($this->encryptionMode) {
            case CRYPT_RSA_ENCRYPTION_NONE:
                $plaintext = str_split($plaintext, $this->k);
                $ciphertext = '';
                foreach ($plaintext as $m) {
                    $ciphertext.= $this->_raw_encrypt($m);
                }
                return $ciphertext;
            case CRYPT_RSA_ENCRYPTION_PKCS1:
                $length = $this->k - 11;
                if ($length <= 0) {
                    return false;
                }

                $plaintext = str_split($plaintext, $length);
                $ciphertext = '';
                foreach ($plaintext as $m) {
                    $ciphertext.= $this->_rsaes_pkcs1_v1_5_encrypt($m);
                }
                return $ciphertext;
            //case CRYPT_RSA_ENCRYPTION_OAEP:
            default:
                $length = $this->k - 2 * $this->hLen - 2;
                if ($length <= 0) {
                    return false;
                }

                $plaintext = str_split($plaintext, $length);
                $ciphertext = '';
                foreach ($plaintext as $m) {
                    $ciphertext.= $this->_rsaes_oaep_encrypt($m);
                }
                return $ciphertext;
        }
    }

    /**
     * Decryption
     *
     * @see self::encrypt()
     * @access public
     * @param string $plaintext
     * @return string
     */
    function decrypt($ciphertext)
    {
        if ($this->k <= 0) {
            return false;
        }

        $ciphertext = str_split($ciphertext, $this->k);
        $ciphertext[count($ciphertext) - 1] = str_pad($ciphertext[count($ciphertext) - 1], $this->k, chr(0), STR_PAD_LEFT);

        $plaintext = '';

        switch ($this->encryptionMode) {
            case CRYPT_RSA_ENCRYPTION_NONE:
                $decrypt = '_raw_encrypt';
                break;
            case CRYPT_RSA_ENCRYPTION_PKCS1:
                $decrypt = '_rsaes_pkcs1_v1_5_decrypt';
                break;
            //case CRYPT_RSA_ENCRYPTION_OAEP:
            default:
                $decrypt = '_rsaes_oaep_decrypt';
        }

        foreach ($ciphertext as $c) {
            $temp = $this->$decrypt($c);
            if ($temp === false) {
                return false;
            }
            $plaintext.= $temp;
        }

        return $plaintext;
    }

    /**
     * Create a signature
     *
     * @see self::verify()
     * @access public
     * @param string $message
     * @return string
     */
    function sign($message)
    {
        if (empty($this->modulus) || empty($this->exponent)) {
            return false;
        }

        switch ($this->signatureMode) {
            case CRYPT_RSA_SIGNATURE_PKCS1:
                return $this->_rsassa_pkcs1_v1_5_sign($message);
            //case CRYPT_RSA_SIGNATURE_PSS:
            default:
                return $this->_rsassa_pss_sign($message);
        }
    }

    /**
     * Verifies a signature
     *
     * @see self::sign()
     * @access public
     * @param string $message
     * @param string $signature
     * @return bool
     */
    function verify($message, $signature)
    {
        if (empty($this->modulus) || empty($this->exponent)) {
            return false;
        }

        switch ($this->signatureMode) {
            case CRYPT_RSA_SIGNATURE_PKCS1:
                return $this->_rsassa_pkcs1_v1_5_verify($message, $signature);
            //case CRYPT_RSA_SIGNATURE_PSS:
            default:
                return $this->_rsassa_pss_verify($message, $signature);
        }
    }

    /**
     * Extract raw BER from Base64 encoding
     *
     * @access private
     * @param string $str
     * @return string
     */
    function _extractBER($str)
    {
        /* X.509 certs are assumed to be base64 encoded but sometimes they'll have additional things in them
         * above and beyond the ceritificate.
         * ie. some may have the following preceding the -----BEGIN CERTIFICATE----- line:
         *
         * Bag Attributes
         *     localKeyID: 01 00 00 00
         * subject=/O=organization/OU=org unit/CN=common name
         * issuer=/O=organization/CN=common name
         */
        $temp = preg_replace('#.*?^-+[^-]+-+[\r\n ]*$#ms', '', $str, 1);
        // remove the -----BEGIN CERTIFICATE----- and -----END CERTIFICATE----- stuff
        $temp = preg_replace('#-+[^-]+-+#', '', $temp);
        // remove new lines
        $temp = str_replace(array("\r", "\n", ' '), '', $temp);
        $temp = preg_match('#^[a-zA-Z\d/+]*={0,2}$#', $temp) ? base64_decode($temp) : false;
        return $temp != false ? $temp : $str;
    }
}
vendor/phpseclib/phpseclib/phpseclib/Crypt/AES.php000064400000014070151327705700016167 0ustar00<?php

/**
 * Pure-PHP implementation of AES.
 *
 * Uses mcrypt, if available/possible, and an internal implementation, otherwise.
 *
 * PHP versions 4 and 5
 *
 * NOTE: Since AES.php is (for compatibility and phpseclib-historical reasons) virtually
 * just a wrapper to Rijndael.php you may consider using Rijndael.php instead of
 * to save one include_once().
 *
 * If {@link self::setKeyLength() setKeyLength()} isn't called, it'll be calculated from
 * {@link self::setKey() setKey()}.  ie. if the key is 128-bits, the key length will be 128-bits.  If it's 136-bits
 * it'll be null-padded to 192-bits and 192 bits will be the key length until {@link self::setKey() setKey()}
 * is called, again, at which point, it'll be recalculated.
 *
 * Since Crypt_AES extends Crypt_Rijndael, some functions are available to be called that, in the context of AES, don't
 * make a whole lot of sense.  {@link self::setBlockLength() setBlockLength()}, for instance.  Calling that function,
 * however possible, won't do anything (AES has a fixed block length whereas Rijndael has a variable one).
 *
 * Here's a short example of how to use this library:
 * <code>
 * <?php
 *    include 'Crypt/AES.php';
 *
 *    $aes = new Crypt_AES();
 *
 *    $aes->setKey('abcdefghijklmnop');
 *
 *    $size = 10 * 1024;
 *    $plaintext = '';
 *    for ($i = 0; $i < $size; $i++) {
 *        $plaintext.= 'a';
 *    }
 *
 *    echo $aes->decrypt($aes->encrypt($plaintext));
 * ?>
 * </code>
 *
 * LICENSE: Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 *
 * @category  Crypt
 * @package   Crypt_AES
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2008 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      http://phpseclib.sourceforge.net
 */

/**
 * Include Crypt_Rijndael
 */
if (!class_exists('Crypt_Rijndael')) {
    include_once 'Rijndael.php';
}

/**#@+
 * @access public
 * @see self::encrypt()
 * @see self::decrypt()
 */
/**
 * Encrypt / decrypt using the Counter mode.
 *
 * Set to -1 since that's what Crypt/Random.php uses to index the CTR mode.
 *
 * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Counter_.28CTR.29
 */
define('CRYPT_AES_MODE_CTR', CRYPT_MODE_CTR);
/**
 * Encrypt / decrypt using the Electronic Code Book mode.
 *
 * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Electronic_codebook_.28ECB.29
 */
define('CRYPT_AES_MODE_ECB', CRYPT_MODE_ECB);
/**
 * Encrypt / decrypt using the Code Book Chaining mode.
 *
 * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Cipher-block_chaining_.28CBC.29
 */
define('CRYPT_AES_MODE_CBC', CRYPT_MODE_CBC);
/**
 * Encrypt / decrypt using the Cipher Feedback mode.
 *
 * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Cipher_feedback_.28CFB.29
 */
define('CRYPT_AES_MODE_CFB', CRYPT_MODE_CFB);
/**
 * Encrypt / decrypt using the Cipher Feedback mode.
 *
 * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Output_feedback_.28OFB.29
 */
define('CRYPT_AES_MODE_OFB', CRYPT_MODE_OFB);
/**#@-*/

/**
 * Pure-PHP implementation of AES.
 *
 * @package Crypt_AES
 * @author  Jim Wigginton <terrafrost@php.net>
 * @access  public
 */
class Crypt_AES extends Crypt_Rijndael
{
    /**
     * The namespace used by the cipher for its constants.
     *
     * @see Crypt_Base::const_namespace
     * @var string
     * @access private
     */
    var $const_namespace = 'AES';

    /**
     * Dummy function
     *
     * Since Crypt_AES extends Crypt_Rijndael, this function is, technically, available, but it doesn't do anything.
     *
     * @see Crypt_Rijndael::setBlockLength()
     * @access public
     * @param int $length
     */
    function setBlockLength($length)
    {
        return;
    }

    /**
     * Sets the key length
     *
     * Valid key lengths are 128, 192, and 256.  If the length is less than 128, it will be rounded up to
     * 128.  If the length is greater than 128 and invalid, it will be rounded down to the closest valid amount.
     *
     * @see Crypt_Rijndael:setKeyLength()
     * @access public
     * @param int $length
     */
    function setKeyLength($length)
    {
        switch ($length) {
            case 160:
                $length = 192;
                break;
            case 224:
                $length = 256;
        }
        parent::setKeyLength($length);
    }

    /**
     * Sets the key.
     *
     * Rijndael supports five different key lengths, AES only supports three.
     *
     * @see Crypt_Rijndael:setKey()
     * @see setKeyLength()
     * @access public
     * @param string $key
     */
    function setKey($key)
    {
        parent::setKey($key);

        if (!$this->explicit_key_length) {
            $length = strlen($key);
            switch (true) {
                case $length <= 16:
                    $this->key_length = 16;
                    break;
                case $length <= 24:
                    $this->key_length = 24;
                    break;
                default:
                    $this->key_length = 32;
            }
            $this->_setEngine();
        }
    }
}
vendor/phpseclib/phpseclib/phpseclib/Crypt/Rijndael.php000064400000131424151327705700017312 0ustar00<?php

/**
 * Pure-PHP implementation of Rijndael.
 *
 * Uses mcrypt, if available/possible, and an internal implementation, otherwise.
 *
 * PHP versions 4 and 5
 *
 * If {@link self::setBlockLength() setBlockLength()} isn't called, it'll be assumed to be 128 bits.  If
 * {@link self::setKeyLength() setKeyLength()} isn't called, it'll be calculated from
 * {@link self::setKey() setKey()}.  ie. if the key is 128-bits, the key length will be 128-bits.  If it's
 * 136-bits it'll be null-padded to 192-bits and 192 bits will be the key length until
 * {@link self::setKey() setKey()} is called, again, at which point, it'll be recalculated.
 *
 * Not all Rijndael implementations may support 160-bits or 224-bits as the block length / key length.  mcrypt, for example,
 * does not.  AES, itself, only supports block lengths of 128 and key lengths of 128, 192, and 256.
 * {@link http://csrc.nist.gov/archive/aes/rijndael/Rijndael-ammended.pdf#page=10 Rijndael-ammended.pdf#page=10} defines the
 * algorithm for block lengths of 192 and 256 but not for block lengths / key lengths of 160 and 224.  Indeed, 160 and 224
 * are first defined as valid key / block lengths in
 * {@link http://csrc.nist.gov/archive/aes/rijndael/Rijndael-ammended.pdf#page=44 Rijndael-ammended.pdf#page=44}:
 * Extensions: Other block and Cipher Key lengths.
 * Note: Use of 160/224-bit Keys must be explicitly set by setKeyLength(160) respectively setKeyLength(224).
 *
 * {@internal The variable names are the same as those in
 * {@link http://www.csrc.nist.gov/publications/fips/fips197/fips-197.pdf#page=10 fips-197.pdf#page=10}.}}
 *
 * Here's a short example of how to use this library:
 * <code>
 * <?php
 *    include 'Crypt/Rijndael.php';
 *
 *    $rijndael = new Crypt_Rijndael();
 *
 *    $rijndael->setKey('abcdefghijklmnop');
 *
 *    $size = 10 * 1024;
 *    $plaintext = '';
 *    for ($i = 0; $i < $size; $i++) {
 *        $plaintext.= 'a';
 *    }
 *
 *    echo $rijndael->decrypt($rijndael->encrypt($plaintext));
 * ?>
 * </code>
 *
 * LICENSE: Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 *
 * @category  Crypt
 * @package   Crypt_Rijndael
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2008 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      http://phpseclib.sourceforge.net
 */

/**
 * Include Crypt_Base
 *
 * Base cipher class
 */
if (!class_exists('Crypt_Base')) {
    include_once 'Base.php';
}

/**#@+
 * @access public
 * @see self::encrypt()
 * @see self::decrypt()
 */
/**
 * Encrypt / decrypt using the Counter mode.
 *
 * Set to -1 since that's what Crypt/Random.php uses to index the CTR mode.
 *
 * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Counter_.28CTR.29
 */
define('CRYPT_RIJNDAEL_MODE_CTR', CRYPT_MODE_CTR);
/**
 * Encrypt / decrypt using the Electronic Code Book mode.
 *
 * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Electronic_codebook_.28ECB.29
 */
define('CRYPT_RIJNDAEL_MODE_ECB', CRYPT_MODE_ECB);
/**
 * Encrypt / decrypt using the Code Book Chaining mode.
 *
 * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Cipher-block_chaining_.28CBC.29
 */
define('CRYPT_RIJNDAEL_MODE_CBC', CRYPT_MODE_CBC);
/**
 * Encrypt / decrypt using the Cipher Feedback mode.
 *
 * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Cipher_feedback_.28CFB.29
 */
define('CRYPT_RIJNDAEL_MODE_CFB', CRYPT_MODE_CFB);
/**
 * Encrypt / decrypt using the Cipher Feedback mode.
 *
 * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Output_feedback_.28OFB.29
 */
define('CRYPT_RIJNDAEL_MODE_OFB', CRYPT_MODE_OFB);
/**#@-*/

/**
 * Pure-PHP implementation of Rijndael.
 *
 * @package Crypt_Rijndael
 * @author  Jim Wigginton <terrafrost@php.net>
 * @access  public
 */
class Crypt_Rijndael extends Crypt_Base
{
    /**
     * The namespace used by the cipher for its constants.
     *
     * @see Crypt_Base::const_namespace
     * @var string
     * @access private
     */
    var $const_namespace = 'RIJNDAEL';

    /**
     * The mcrypt specific name of the cipher
     *
     * Mcrypt is useable for 128/192/256-bit $block_size/$key_length. For 160/224 not.
     * Crypt_Rijndael determines automatically whether mcrypt is useable
     * or not for the current $block_size/$key_length.
     * In case of, $cipher_name_mcrypt will be set dynamically at run time accordingly.
     *
     * @see Crypt_Base::cipher_name_mcrypt
     * @see Crypt_Base::engine
     * @see self::isValidEngine()
     * @var string
     * @access private
     */
    var $cipher_name_mcrypt = 'rijndael-128';

    /**
     * The default salt used by setPassword()
     *
     * @see Crypt_Base::password_default_salt
     * @see Crypt_Base::setPassword()
     * @var string
     * @access private
     */
    var $password_default_salt = 'phpseclib';

    /**
     * The Key Schedule
     *
     * @see self::_setup()
     * @var array
     * @access private
     */
    var $w;

    /**
     * The Inverse Key Schedule
     *
     * @see self::_setup()
     * @var array
     * @access private
     */
    var $dw;

    /**
     * The Block Length divided by 32
     *
     * @see self::setBlockLength()
     * @var int
     * @access private
     * @internal The max value is 256 / 32 = 8, the min value is 128 / 32 = 4.  Exists in conjunction with $block_size
     *    because the encryption / decryption / key schedule creation requires this number and not $block_size.  We could
     *    derive this from $block_size or vice versa, but that'd mean we'd have to do multiple shift operations, so in lieu
     *    of that, we'll just precompute it once.
     */
    var $Nb = 4;

    /**
     * The Key Length (in bytes)
     *
     * @see self::setKeyLength()
     * @var int
     * @access private
     * @internal The max value is 256 / 8 = 32, the min value is 128 / 8 = 16.  Exists in conjunction with $Nk
     *    because the encryption / decryption / key schedule creation requires this number and not $key_length.  We could
     *    derive this from $key_length or vice versa, but that'd mean we'd have to do multiple shift operations, so in lieu
     *    of that, we'll just precompute it once.
     */
    var $key_length = 16;

    /**
     * The Key Length divided by 32
     *
     * @see self::setKeyLength()
     * @var int
     * @access private
     * @internal The max value is 256 / 32 = 8, the min value is 128 / 32 = 4
     */
    var $Nk = 4;

    /**
     * The Number of Rounds
     *
     * @var int
     * @access private
     * @internal The max value is 14, the min value is 10.
     */
    var $Nr;

    /**
     * Shift offsets
     *
     * @var array
     * @access private
     */
    var $c;

    /**
     * Holds the last used key- and block_size information
     *
     * @var array
     * @access private
     */
    var $kl;

    var $key_size;

    /**
     * Sets the key.
     *
     * Keys can be of any length.  Rijndael, itself, requires the use of a key that's between 128-bits and 256-bits long and
     * whose length is a multiple of 32.  If the key is less than 256-bits and the key length isn't set, we round the length
     * up to the closest valid key length, padding $key with null bytes.  If the key is more than 256-bits, we trim the
     * excess bits.
     *
     * If the key is not explicitly set, it'll be assumed to be all null bytes.
     *
     * Note: 160/224-bit keys must explicitly set by setKeyLength(), otherwise they will be round/pad up to 192/256 bits.
     *
     * @see Crypt_Base:setKey()
     * @see self::setKeyLength()
     * @access public
     * @param string $key
     */
    function setKey($key)
    {
        if (!$this->explicit_key_length) {
            $length = strlen($key);
            switch (true) {
                case $length <= 16:
                    $this->key_size = 16;
                    break;
                case $length <= 20:
                    $this->key_size = 20;
                    break;
                case $length <= 24:
                    $this->key_size = 24;
                    break;
                case $length <= 28:
                    $this->key_size = 28;
                    break;
                default:
                    $this->key_size = 32;
            }
        }
        parent::setKey($key);
    }

    /**
     * Sets the key length
     *
     * Valid key lengths are 128, 160, 192, 224, and 256.  If the length is less than 128, it will be rounded up to
     * 128.  If the length is greater than 128 and invalid, it will be rounded down to the closest valid amount.
     *
     * Note: phpseclib extends Rijndael (and AES) for using 160- and 224-bit keys but they are officially not defined
     *       and the most (if not all) implementations are not able using 160/224-bit keys but round/pad them up to
     *       192/256 bits as, for example, mcrypt will do.
     *
     *       That said, if you want be compatible with other Rijndael and AES implementations,
     *       you should not setKeyLength(160) or setKeyLength(224).
     *
     * Additional: In case of 160- and 224-bit keys, phpseclib will/can, for that reason, not use
     *             the mcrypt php extension, even if available.
     *             This results then in slower encryption.
     *
     * @access public
     * @param int $length
     */
    function setKeyLength($length)
    {
        switch (true) {
            case $length <= 128:
                $this->key_length = 16;
                break;
            case $length <= 160:
                $this->key_length = 20;
                break;
            case $length <= 192:
                $this->key_length = 24;
                break;
            case $length <= 224:
                $this->key_length = 28;
                break;
            default:
                $this->key_length = 32;
        }

        parent::setKeyLength($length);
    }

    /**
     * Sets the block length
     *
     * Valid block lengths are 128, 160, 192, 224, and 256.  If the length is less than 128, it will be rounded up to
     * 128.  If the length is greater than 128 and invalid, it will be rounded down to the closest valid amount.
     *
     * @access public
     * @param int $length
     */
    function setBlockLength($length)
    {
        $length >>= 5;
        if ($length > 8) {
            $length = 8;
        } elseif ($length < 4) {
            $length = 4;
        }
        $this->Nb = $length;
        $this->block_size = $length << 2;
        $this->changed = true;
        $this->_setEngine();
    }

    /**
     * Test for engine validity
     *
     * This is mainly just a wrapper to set things up for Crypt_Base::isValidEngine()
     *
     * @see Crypt_Base::Crypt_Base()
     * @param int $engine
     * @access public
     * @return bool
     */
    function isValidEngine($engine)
    {
        switch ($engine) {
            case CRYPT_ENGINE_OPENSSL:
                if ($this->block_size != 16) {
                    return false;
                }
                $this->cipher_name_openssl_ecb = 'aes-' . ($this->key_length << 3) . '-ecb';
                $this->cipher_name_openssl = 'aes-' . ($this->key_length << 3) . '-' . $this->_openssl_translate_mode();
                break;
            case CRYPT_ENGINE_MCRYPT:
                $this->cipher_name_mcrypt = 'rijndael-' . ($this->block_size << 3);
                if ($this->key_length % 8) { // is it a 160/224-bit key?
                    // mcrypt is not usable for them, only for 128/192/256-bit keys
                    return false;
                }
        }

        return parent::isValidEngine($engine);
    }

    /**
     * Encrypts a block
     *
     * @access private
     * @param string $in
     * @return string
     */
    function _encryptBlock($in)
    {
        static $tables;
        if (empty($tables)) {
            $tables = &$this->_getTables();
        }
        $t0   = $tables[0];
        $t1   = $tables[1];
        $t2   = $tables[2];
        $t3   = $tables[3];
        $sbox = $tables[4];

        $state = array();
        $words = unpack('N*', $in);

        $c = $this->c;
        $w = $this->w;
        $Nb = $this->Nb;
        $Nr = $this->Nr;

        // addRoundKey
        $wc = $Nb - 1;
        foreach ($words as $word) {
            $state[] = $word ^ $w[++$wc];
        }

        // fips-197.pdf#page=19, "Figure 5. Pseudo Code for the Cipher", states that this loop has four components -
        // subBytes, shiftRows, mixColumns, and addRoundKey. fips-197.pdf#page=30, "Implementation Suggestions Regarding
        // Various Platforms" suggests that performs enhanced implementations are described in Rijndael-ammended.pdf.
        // Rijndael-ammended.pdf#page=20, "Implementation aspects / 32-bit processor", discusses such an optimization.
        // Unfortunately, the description given there is not quite correct.  Per aes.spec.v316.pdf#page=19 [1],
        // equation (7.4.7) is supposed to use addition instead of subtraction, so we'll do that here, as well.

        // [1] http://fp.gladman.plus.com/cryptography_technology/rijndael/aes.spec.v316.pdf
        $temp = array();
        for ($round = 1; $round < $Nr; ++$round) {
            $i = 0; // $c[0] == 0
            $j = $c[1];
            $k = $c[2];
            $l = $c[3];

            while ($i < $Nb) {
                $temp[$i] = $t0[$state[$i] >> 24 & 0x000000FF] ^
                            $t1[$state[$j] >> 16 & 0x000000FF] ^
                            $t2[$state[$k] >>  8 & 0x000000FF] ^
                            $t3[$state[$l]       & 0x000000FF] ^
                            $w[++$wc];
                ++$i;
                $j = ($j + 1) % $Nb;
                $k = ($k + 1) % $Nb;
                $l = ($l + 1) % $Nb;
            }
            $state = $temp;
        }

        // subWord
        for ($i = 0; $i < $Nb; ++$i) {
            $state[$i] =   $sbox[$state[$i]       & 0x000000FF]        |
                          ($sbox[$state[$i] >>  8 & 0x000000FF] <<  8) |
                          ($sbox[$state[$i] >> 16 & 0x000000FF] << 16) |
                          ($sbox[$state[$i] >> 24 & 0x000000FF] << 24);
        }

        // shiftRows + addRoundKey
        $i = 0; // $c[0] == 0
        $j = $c[1];
        $k = $c[2];
        $l = $c[3];
        while ($i < $Nb) {
            $temp[$i] = ($state[$i] & 0xFF000000) ^
                        ($state[$j] & 0x00FF0000) ^
                        ($state[$k] & 0x0000FF00) ^
                        ($state[$l] & 0x000000FF) ^
                         $w[$i];
            ++$i;
            $j = ($j + 1) % $Nb;
            $k = ($k + 1) % $Nb;
            $l = ($l + 1) % $Nb;
        }

        switch ($Nb) {
            case 8:
                return pack('N*', $temp[0], $temp[1], $temp[2], $temp[3], $temp[4], $temp[5], $temp[6], $temp[7]);
            case 7:
                return pack('N*', $temp[0], $temp[1], $temp[2], $temp[3], $temp[4], $temp[5], $temp[6]);
            case 6:
                return pack('N*', $temp[0], $temp[1], $temp[2], $temp[3], $temp[4], $temp[5]);
            case 5:
                return pack('N*', $temp[0], $temp[1], $temp[2], $temp[3], $temp[4]);
            default:
                return pack('N*', $temp[0], $temp[1], $temp[2], $temp[3]);
        }
    }

    /**
     * Decrypts a block
     *
     * @access private
     * @param string $in
     * @return string
     */
    function _decryptBlock($in)
    {
        static $invtables;
        if (empty($invtables)) {
            $invtables = &$this->_getInvTables();
        }
        $dt0   = $invtables[0];
        $dt1   = $invtables[1];
        $dt2   = $invtables[2];
        $dt3   = $invtables[3];
        $isbox = $invtables[4];

        $state = array();
        $words = unpack('N*', $in);

        $c  = $this->c;
        $dw = $this->dw;
        $Nb = $this->Nb;
        $Nr = $this->Nr;

        // addRoundKey
        $wc = $Nb - 1;
        foreach ($words as $word) {
            $state[] = $word ^ $dw[++$wc];
        }

        $temp = array();
        for ($round = $Nr - 1; $round > 0; --$round) {
            $i = 0; // $c[0] == 0
            $j = $Nb - $c[1];
            $k = $Nb - $c[2];
            $l = $Nb - $c[3];

            while ($i < $Nb) {
                $temp[$i] = $dt0[$state[$i] >> 24 & 0x000000FF] ^
                            $dt1[$state[$j] >> 16 & 0x000000FF] ^
                            $dt2[$state[$k] >>  8 & 0x000000FF] ^
                            $dt3[$state[$l]       & 0x000000FF] ^
                            $dw[++$wc];
                ++$i;
                $j = ($j + 1) % $Nb;
                $k = ($k + 1) % $Nb;
                $l = ($l + 1) % $Nb;
            }
            $state = $temp;
        }

        // invShiftRows + invSubWord + addRoundKey
        $i = 0; // $c[0] == 0
        $j = $Nb - $c[1];
        $k = $Nb - $c[2];
        $l = $Nb - $c[3];

        while ($i < $Nb) {
            $word = ($state[$i] & 0xFF000000) |
                    ($state[$j] & 0x00FF0000) |
                    ($state[$k] & 0x0000FF00) |
                    ($state[$l] & 0x000000FF);

            $temp[$i] = $dw[$i] ^ ($isbox[$word       & 0x000000FF]        |
                                  ($isbox[$word >>  8 & 0x000000FF] <<  8) |
                                  ($isbox[$word >> 16 & 0x000000FF] << 16) |
                                  ($isbox[$word >> 24 & 0x000000FF] << 24));
            ++$i;
            $j = ($j + 1) % $Nb;
            $k = ($k + 1) % $Nb;
            $l = ($l + 1) % $Nb;
        }

        switch ($Nb) {
            case 8:
                return pack('N*', $temp[0], $temp[1], $temp[2], $temp[3], $temp[4], $temp[5], $temp[6], $temp[7]);
            case 7:
                return pack('N*', $temp[0], $temp[1], $temp[2], $temp[3], $temp[4], $temp[5], $temp[6]);
            case 6:
                return pack('N*', $temp[0], $temp[1], $temp[2], $temp[3], $temp[4], $temp[5]);
            case 5:
                return pack('N*', $temp[0], $temp[1], $temp[2], $temp[3], $temp[4]);
            default:
                return pack('N*', $temp[0], $temp[1], $temp[2], $temp[3]);
        }
    }

    /**
     * Setup the key (expansion)
     *
     * @see Crypt_Base::_setupKey()
     * @access private
     */
    function _setupKey()
    {
        // Each number in $rcon is equal to the previous number multiplied by two in Rijndael's finite field.
        // See http://en.wikipedia.org/wiki/Finite_field_arithmetic#Multiplicative_inverse
        static $rcon = array(0,
            0x01000000, 0x02000000, 0x04000000, 0x08000000, 0x10000000,
            0x20000000, 0x40000000, 0x80000000, 0x1B000000, 0x36000000,
            0x6C000000, 0xD8000000, 0xAB000000, 0x4D000000, 0x9A000000,
            0x2F000000, 0x5E000000, 0xBC000000, 0x63000000, 0xC6000000,
            0x97000000, 0x35000000, 0x6A000000, 0xD4000000, 0xB3000000,
            0x7D000000, 0xFA000000, 0xEF000000, 0xC5000000, 0x91000000
        );

        if (isset($this->kl['key']) && $this->key === $this->kl['key'] && $this->key_length === $this->kl['key_length'] && $this->block_size === $this->kl['block_size']) {
            // already expanded
            return;
        }
        $this->kl = array('key' => $this->key, 'key_length' => $this->key_length, 'block_size' => $this->block_size);

        $this->Nk = $this->key_length >> 2;
        // see Rijndael-ammended.pdf#page=44
        $this->Nr = max($this->Nk, $this->Nb) + 6;

        // shift offsets for Nb = 5, 7 are defined in Rijndael-ammended.pdf#page=44,
        //     "Table 8: Shift offsets in Shiftrow for the alternative block lengths"
        // shift offsets for Nb = 4, 6, 8 are defined in Rijndael-ammended.pdf#page=14,
        //     "Table 2: Shift offsets for different block lengths"
        switch ($this->Nb) {
            case 4:
            case 5:
            case 6:
                $this->c = array(0, 1, 2, 3);
                break;
            case 7:
                $this->c = array(0, 1, 2, 4);
                break;
            case 8:
                $this->c = array(0, 1, 3, 4);
        }

        $w = array_values(unpack('N*words', $this->key));

        $length = $this->Nb * ($this->Nr + 1);
        for ($i = $this->Nk; $i < $length; $i++) {
            $temp = $w[$i - 1];
            if ($i % $this->Nk == 0) {
                // according to <http://php.net/language.types.integer>, "the size of an integer is platform-dependent".
                // on a 32-bit machine, it's 32-bits, and on a 64-bit machine, it's 64-bits. on a 32-bit machine,
                // 0xFFFFFFFF << 8 == 0xFFFFFF00, but on a 64-bit machine, it equals 0xFFFFFFFF00. as such, doing 'and'
                // with 0xFFFFFFFF (or 0xFFFFFF00) on a 32-bit machine is unnecessary, but on a 64-bit machine, it is.
                $temp = (($temp << 8) & 0xFFFFFF00) | (($temp >> 24) & 0x000000FF); // rotWord
                $temp = $this->_subWord($temp) ^ $rcon[$i / $this->Nk];
            } elseif ($this->Nk > 6 && $i % $this->Nk == 4) {
                $temp = $this->_subWord($temp);
            }
            $w[$i] = $w[$i - $this->Nk] ^ $temp;
        }

        // convert the key schedule from a vector of $Nb * ($Nr + 1) length to a matrix with $Nr + 1 rows and $Nb columns
        // and generate the inverse key schedule.  more specifically,
        // according to <http://csrc.nist.gov/archive/aes/rijndael/Rijndael-ammended.pdf#page=23> (section 5.3.3),
        // "The key expansion for the Inverse Cipher is defined as follows:
        //        1. Apply the Key Expansion.
        //        2. Apply InvMixColumn to all Round Keys except the first and the last one."
        // also, see fips-197.pdf#page=27, "5.3.5 Equivalent Inverse Cipher"
        list($dt0, $dt1, $dt2, $dt3) = $this->_getInvTables();
        $temp = $this->w = $this->dw = array();
        for ($i = $row = $col = 0; $i < $length; $i++, $col++) {
            if ($col == $this->Nb) {
                if ($row == 0) {
                    $this->dw[0] = $this->w[0];
                } else {
                    // subWord + invMixColumn + invSubWord = invMixColumn
                    $j = 0;
                    while ($j < $this->Nb) {
                        $dw = $this->_subWord($this->w[$row][$j]);
                        $temp[$j] = $dt0[$dw >> 24 & 0x000000FF] ^
                                    $dt1[$dw >> 16 & 0x000000FF] ^
                                    $dt2[$dw >>  8 & 0x000000FF] ^
                                    $dt3[$dw       & 0x000000FF];
                        $j++;
                    }
                    $this->dw[$row] = $temp;
                }

                $col = 0;
                $row++;
            }
            $this->w[$row][$col] = $w[$i];
        }

        $this->dw[$row] = $this->w[$row];

        // Converting to 1-dim key arrays (both ascending)
        $this->dw = array_reverse($this->dw);
        $w  = array_pop($this->w);
        $dw = array_pop($this->dw);
        foreach ($this->w as $r => $wr) {
            foreach ($wr as $c => $wc) {
                $w[]  = $wc;
                $dw[] = $this->dw[$r][$c];
            }
        }
        $this->w  = $w;
        $this->dw = $dw;
    }

    /**
     * Performs S-Box substitutions
     *
     * @access private
     * @param int $word
     */
    function _subWord($word)
    {
        static $sbox;
        if (empty($sbox)) {
            list(, , , , $sbox) = $this->_getTables();
        }

        return  $sbox[$word       & 0x000000FF]        |
               ($sbox[$word >>  8 & 0x000000FF] <<  8) |
               ($sbox[$word >> 16 & 0x000000FF] << 16) |
               ($sbox[$word >> 24 & 0x000000FF] << 24);
    }

    /**
     * Provides the mixColumns and sboxes tables
     *
     * @see Crypt_Rijndael:_encryptBlock()
     * @see Crypt_Rijndael:_setupInlineCrypt()
     * @see Crypt_Rijndael:_subWord()
     * @access private
     * @return array &$tables
     */
    function &_getTables()
    {
        static $tables;
        if (empty($tables)) {
            // according to <http://csrc.nist.gov/archive/aes/rijndael/Rijndael-ammended.pdf#page=19> (section 5.2.1),
            // precomputed tables can be used in the mixColumns phase. in that example, they're assigned t0...t3, so
            // those are the names we'll use.
            $t3 = array_map('intval', array(
                // with array_map('intval', ...) we ensure we have only int's and not
                // some slower floats converted by php automatically on high values
                0x6363A5C6, 0x7C7C84F8, 0x777799EE, 0x7B7B8DF6, 0xF2F20DFF, 0x6B6BBDD6, 0x6F6FB1DE, 0xC5C55491,
                0x30305060, 0x01010302, 0x6767A9CE, 0x2B2B7D56, 0xFEFE19E7, 0xD7D762B5, 0xABABE64D, 0x76769AEC,
                0xCACA458F, 0x82829D1F, 0xC9C94089, 0x7D7D87FA, 0xFAFA15EF, 0x5959EBB2, 0x4747C98E, 0xF0F00BFB,
                0xADADEC41, 0xD4D467B3, 0xA2A2FD5F, 0xAFAFEA45, 0x9C9CBF23, 0xA4A4F753, 0x727296E4, 0xC0C05B9B,
                0xB7B7C275, 0xFDFD1CE1, 0x9393AE3D, 0x26266A4C, 0x36365A6C, 0x3F3F417E, 0xF7F702F5, 0xCCCC4F83,
                0x34345C68, 0xA5A5F451, 0xE5E534D1, 0xF1F108F9, 0x717193E2, 0xD8D873AB, 0x31315362, 0x15153F2A,
                0x04040C08, 0xC7C75295, 0x23236546, 0xC3C35E9D, 0x18182830, 0x9696A137, 0x05050F0A, 0x9A9AB52F,
                0x0707090E, 0x12123624, 0x80809B1B, 0xE2E23DDF, 0xEBEB26CD, 0x2727694E, 0xB2B2CD7F, 0x75759FEA,
                0x09091B12, 0x83839E1D, 0x2C2C7458, 0x1A1A2E34, 0x1B1B2D36, 0x6E6EB2DC, 0x5A5AEEB4, 0xA0A0FB5B,
                0x5252F6A4, 0x3B3B4D76, 0xD6D661B7, 0xB3B3CE7D, 0x29297B52, 0xE3E33EDD, 0x2F2F715E, 0x84849713,
                0x5353F5A6, 0xD1D168B9, 0x00000000, 0xEDED2CC1, 0x20206040, 0xFCFC1FE3, 0xB1B1C879, 0x5B5BEDB6,
                0x6A6ABED4, 0xCBCB468D, 0xBEBED967, 0x39394B72, 0x4A4ADE94, 0x4C4CD498, 0x5858E8B0, 0xCFCF4A85,
                0xD0D06BBB, 0xEFEF2AC5, 0xAAAAE54F, 0xFBFB16ED, 0x4343C586, 0x4D4DD79A, 0x33335566, 0x85859411,
                0x4545CF8A, 0xF9F910E9, 0x02020604, 0x7F7F81FE, 0x5050F0A0, 0x3C3C4478, 0x9F9FBA25, 0xA8A8E34B,
                0x5151F3A2, 0xA3A3FE5D, 0x4040C080, 0x8F8F8A05, 0x9292AD3F, 0x9D9DBC21, 0x38384870, 0xF5F504F1,
                0xBCBCDF63, 0xB6B6C177, 0xDADA75AF, 0x21216342, 0x10103020, 0xFFFF1AE5, 0xF3F30EFD, 0xD2D26DBF,
                0xCDCD4C81, 0x0C0C1418, 0x13133526, 0xECEC2FC3, 0x5F5FE1BE, 0x9797A235, 0x4444CC88, 0x1717392E,
                0xC4C45793, 0xA7A7F255, 0x7E7E82FC, 0x3D3D477A, 0x6464ACC8, 0x5D5DE7BA, 0x19192B32, 0x737395E6,
                0x6060A0C0, 0x81819819, 0x4F4FD19E, 0xDCDC7FA3, 0x22226644, 0x2A2A7E54, 0x9090AB3B, 0x8888830B,
                0x4646CA8C, 0xEEEE29C7, 0xB8B8D36B, 0x14143C28, 0xDEDE79A7, 0x5E5EE2BC, 0x0B0B1D16, 0xDBDB76AD,
                0xE0E03BDB, 0x32325664, 0x3A3A4E74, 0x0A0A1E14, 0x4949DB92, 0x06060A0C, 0x24246C48, 0x5C5CE4B8,
                0xC2C25D9F, 0xD3D36EBD, 0xACACEF43, 0x6262A6C4, 0x9191A839, 0x9595A431, 0xE4E437D3, 0x79798BF2,
                0xE7E732D5, 0xC8C8438B, 0x3737596E, 0x6D6DB7DA, 0x8D8D8C01, 0xD5D564B1, 0x4E4ED29C, 0xA9A9E049,
                0x6C6CB4D8, 0x5656FAAC, 0xF4F407F3, 0xEAEA25CF, 0x6565AFCA, 0x7A7A8EF4, 0xAEAEE947, 0x08081810,
                0xBABAD56F, 0x787888F0, 0x25256F4A, 0x2E2E725C, 0x1C1C2438, 0xA6A6F157, 0xB4B4C773, 0xC6C65197,
                0xE8E823CB, 0xDDDD7CA1, 0x74749CE8, 0x1F1F213E, 0x4B4BDD96, 0xBDBDDC61, 0x8B8B860D, 0x8A8A850F,
                0x707090E0, 0x3E3E427C, 0xB5B5C471, 0x6666AACC, 0x4848D890, 0x03030506, 0xF6F601F7, 0x0E0E121C,
                0x6161A3C2, 0x35355F6A, 0x5757F9AE, 0xB9B9D069, 0x86869117, 0xC1C15899, 0x1D1D273A, 0x9E9EB927,
                0xE1E138D9, 0xF8F813EB, 0x9898B32B, 0x11113322, 0x6969BBD2, 0xD9D970A9, 0x8E8E8907, 0x9494A733,
                0x9B9BB62D, 0x1E1E223C, 0x87879215, 0xE9E920C9, 0xCECE4987, 0x5555FFAA, 0x28287850, 0xDFDF7AA5,
                0x8C8C8F03, 0xA1A1F859, 0x89898009, 0x0D0D171A, 0xBFBFDA65, 0xE6E631D7, 0x4242C684, 0x6868B8D0,
                0x4141C382, 0x9999B029, 0x2D2D775A, 0x0F0F111E, 0xB0B0CB7B, 0x5454FCA8, 0xBBBBD66D, 0x16163A2C
            ));

            foreach ($t3 as $t3i) {
                $t0[] = (($t3i << 24) & 0xFF000000) | (($t3i >>  8) & 0x00FFFFFF);
                $t1[] = (($t3i << 16) & 0xFFFF0000) | (($t3i >> 16) & 0x0000FFFF);
                $t2[] = (($t3i <<  8) & 0xFFFFFF00) | (($t3i >> 24) & 0x000000FF);
            }

            $tables = array(
                // The Precomputed mixColumns tables t0 - t3
                $t0,
                $t1,
                $t2,
                $t3,
                // The SubByte S-Box
                array(
                    0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5, 0x30, 0x01, 0x67, 0x2B, 0xFE, 0xD7, 0xAB, 0x76,
                    0xCA, 0x82, 0xC9, 0x7D, 0xFA, 0x59, 0x47, 0xF0, 0xAD, 0xD4, 0xA2, 0xAF, 0x9C, 0xA4, 0x72, 0xC0,
                    0xB7, 0xFD, 0x93, 0x26, 0x36, 0x3F, 0xF7, 0xCC, 0x34, 0xA5, 0xE5, 0xF1, 0x71, 0xD8, 0x31, 0x15,
                    0x04, 0xC7, 0x23, 0xC3, 0x18, 0x96, 0x05, 0x9A, 0x07, 0x12, 0x80, 0xE2, 0xEB, 0x27, 0xB2, 0x75,
                    0x09, 0x83, 0x2C, 0x1A, 0x1B, 0x6E, 0x5A, 0xA0, 0x52, 0x3B, 0xD6, 0xB3, 0x29, 0xE3, 0x2F, 0x84,
                    0x53, 0xD1, 0x00, 0xED, 0x20, 0xFC, 0xB1, 0x5B, 0x6A, 0xCB, 0xBE, 0x39, 0x4A, 0x4C, 0x58, 0xCF,
                    0xD0, 0xEF, 0xAA, 0xFB, 0x43, 0x4D, 0x33, 0x85, 0x45, 0xF9, 0x02, 0x7F, 0x50, 0x3C, 0x9F, 0xA8,
                    0x51, 0xA3, 0x40, 0x8F, 0x92, 0x9D, 0x38, 0xF5, 0xBC, 0xB6, 0xDA, 0x21, 0x10, 0xFF, 0xF3, 0xD2,
                    0xCD, 0x0C, 0x13, 0xEC, 0x5F, 0x97, 0x44, 0x17, 0xC4, 0xA7, 0x7E, 0x3D, 0x64, 0x5D, 0x19, 0x73,
                    0x60, 0x81, 0x4F, 0xDC, 0x22, 0x2A, 0x90, 0x88, 0x46, 0xEE, 0xB8, 0x14, 0xDE, 0x5E, 0x0B, 0xDB,
                    0xE0, 0x32, 0x3A, 0x0A, 0x49, 0x06, 0x24, 0x5C, 0xC2, 0xD3, 0xAC, 0x62, 0x91, 0x95, 0xE4, 0x79,
                    0xE7, 0xC8, 0x37, 0x6D, 0x8D, 0xD5, 0x4E, 0xA9, 0x6C, 0x56, 0xF4, 0xEA, 0x65, 0x7A, 0xAE, 0x08,
                    0xBA, 0x78, 0x25, 0x2E, 0x1C, 0xA6, 0xB4, 0xC6, 0xE8, 0xDD, 0x74, 0x1F, 0x4B, 0xBD, 0x8B, 0x8A,
                    0x70, 0x3E, 0xB5, 0x66, 0x48, 0x03, 0xF6, 0x0E, 0x61, 0x35, 0x57, 0xB9, 0x86, 0xC1, 0x1D, 0x9E,
                    0xE1, 0xF8, 0x98, 0x11, 0x69, 0xD9, 0x8E, 0x94, 0x9B, 0x1E, 0x87, 0xE9, 0xCE, 0x55, 0x28, 0xDF,
                    0x8C, 0xA1, 0x89, 0x0D, 0xBF, 0xE6, 0x42, 0x68, 0x41, 0x99, 0x2D, 0x0F, 0xB0, 0x54, 0xBB, 0x16
                )
            );
        }
        return $tables;
    }

    /**
     * Provides the inverse mixColumns and inverse sboxes tables
     *
     * @see Crypt_Rijndael:_decryptBlock()
     * @see Crypt_Rijndael:_setupInlineCrypt()
     * @see Crypt_Rijndael:_setupKey()
     * @access private
     * @return array &$tables
     */
    function &_getInvTables()
    {
        static $tables;
        if (empty($tables)) {
            $dt3 = array_map('intval', array(
                0xF4A75051, 0x4165537E, 0x17A4C31A, 0x275E963A, 0xAB6BCB3B, 0x9D45F11F, 0xFA58ABAC, 0xE303934B,
                0x30FA5520, 0x766DF6AD, 0xCC769188, 0x024C25F5, 0xE5D7FC4F, 0x2ACBD7C5, 0x35448026, 0x62A38FB5,
                0xB15A49DE, 0xBA1B6725, 0xEA0E9845, 0xFEC0E15D, 0x2F7502C3, 0x4CF01281, 0x4697A38D, 0xD3F9C66B,
                0x8F5FE703, 0x929C9515, 0x6D7AEBBF, 0x5259DA95, 0xBE832DD4, 0x7421D358, 0xE0692949, 0xC9C8448E,
                0xC2896A75, 0x8E7978F4, 0x583E6B99, 0xB971DD27, 0xE14FB6BE, 0x88AD17F0, 0x20AC66C9, 0xCE3AB47D,
                0xDF4A1863, 0x1A3182E5, 0x51336097, 0x537F4562, 0x6477E0B1, 0x6BAE84BB, 0x81A01CFE, 0x082B94F9,
                0x48685870, 0x45FD198F, 0xDE6C8794, 0x7BF8B752, 0x73D323AB, 0x4B02E272, 0x1F8F57E3, 0x55AB2A66,
                0xEB2807B2, 0xB5C2032F, 0xC57B9A86, 0x3708A5D3, 0x2887F230, 0xBFA5B223, 0x036ABA02, 0x16825CED,
                0xCF1C2B8A, 0x79B492A7, 0x07F2F0F3, 0x69E2A14E, 0xDAF4CD65, 0x05BED506, 0x34621FD1, 0xA6FE8AC4,
                0x2E539D34, 0xF355A0A2, 0x8AE13205, 0xF6EB75A4, 0x83EC390B, 0x60EFAA40, 0x719F065E, 0x6E1051BD,
                0x218AF93E, 0xDD063D96, 0x3E05AEDD, 0xE6BD464D, 0x548DB591, 0xC45D0571, 0x06D46F04, 0x5015FF60,
                0x98FB2419, 0xBDE997D6, 0x4043CC89, 0xD99E7767, 0xE842BDB0, 0x898B8807, 0x195B38E7, 0xC8EEDB79,
                0x7C0A47A1, 0x420FE97C, 0x841EC9F8, 0x00000000, 0x80868309, 0x2BED4832, 0x1170AC1E, 0x5A724E6C,
                0x0EFFFBFD, 0x8538560F, 0xAED51E3D, 0x2D392736, 0x0FD9640A, 0x5CA62168, 0x5B54D19B, 0x362E3A24,
                0x0A67B10C, 0x57E70F93, 0xEE96D2B4, 0x9B919E1B, 0xC0C54F80, 0xDC20A261, 0x774B695A, 0x121A161C,
                0x93BA0AE2, 0xA02AE5C0, 0x22E0433C, 0x1B171D12, 0x090D0B0E, 0x8BC7ADF2, 0xB6A8B92D, 0x1EA9C814,
                0xF1198557, 0x75074CAF, 0x99DDBBEE, 0x7F60FDA3, 0x01269FF7, 0x72F5BC5C, 0x663BC544, 0xFB7E345B,
                0x4329768B, 0x23C6DCCB, 0xEDFC68B6, 0xE4F163B8, 0x31DCCAD7, 0x63851042, 0x97224013, 0xC6112084,
                0x4A247D85, 0xBB3DF8D2, 0xF93211AE, 0x29A16DC7, 0x9E2F4B1D, 0xB230F3DC, 0x8652EC0D, 0xC1E3D077,
                0xB3166C2B, 0x70B999A9, 0x9448FA11, 0xE9642247, 0xFC8CC4A8, 0xF03F1AA0, 0x7D2CD856, 0x3390EF22,
                0x494EC787, 0x38D1C1D9, 0xCAA2FE8C, 0xD40B3698, 0xF581CFA6, 0x7ADE28A5, 0xB78E26DA, 0xADBFA43F,
                0x3A9DE42C, 0x78920D50, 0x5FCC9B6A, 0x7E466254, 0x8D13C2F6, 0xD8B8E890, 0x39F75E2E, 0xC3AFF582,
                0x5D80BE9F, 0xD0937C69, 0xD52DA96F, 0x2512B3CF, 0xAC993BC8, 0x187DA710, 0x9C636EE8, 0x3BBB7BDB,
                0x267809CD, 0x5918F46E, 0x9AB701EC, 0x4F9AA883, 0x956E65E6, 0xFFE67EAA, 0xBCCF0821, 0x15E8E6EF,
                0xE79BD9BA, 0x6F36CE4A, 0x9F09D4EA, 0xB07CD629, 0xA4B2AF31, 0x3F23312A, 0xA59430C6, 0xA266C035,
                0x4EBC3774, 0x82CAA6FC, 0x90D0B0E0, 0xA7D81533, 0x04984AF1, 0xECDAF741, 0xCD500E7F, 0x91F62F17,
                0x4DD68D76, 0xEFB04D43, 0xAA4D54CC, 0x9604DFE4, 0xD1B5E39E, 0x6A881B4C, 0x2C1FB8C1, 0x65517F46,
                0x5EEA049D, 0x8C355D01, 0x877473FA, 0x0B412EFB, 0x671D5AB3, 0xDBD25292, 0x105633E9, 0xD647136D,
                0xD7618C9A, 0xA10C7A37, 0xF8148E59, 0x133C89EB, 0xA927EECE, 0x61C935B7, 0x1CE5EDE1, 0x47B13C7A,
                0xD2DF599C, 0xF2733F55, 0x14CE7918, 0xC737BF73, 0xF7CDEA53, 0xFDAA5B5F, 0x3D6F14DF, 0x44DB8678,
                0xAFF381CA, 0x68C43EB9, 0x24342C38, 0xA3405FC2, 0x1DC37216, 0xE2250CBC, 0x3C498B28, 0x0D9541FF,
                0xA8017139, 0x0CB3DE08, 0xB4E49CD8, 0x56C19064, 0xCB84617B, 0x32B670D5, 0x6C5C7448, 0xB85742D0
            ));

            foreach ($dt3 as $dt3i) {
                $dt0[] = (($dt3i << 24) & 0xFF000000) | (($dt3i >>  8) & 0x00FFFFFF);
                $dt1[] = (($dt3i << 16) & 0xFFFF0000) | (($dt3i >> 16) & 0x0000FFFF);
                $dt2[] = (($dt3i <<  8) & 0xFFFFFF00) | (($dt3i >> 24) & 0x000000FF);
            };

            $tables = array(
                // The Precomputed inverse mixColumns tables dt0 - dt3
                $dt0,
                $dt1,
                $dt2,
                $dt3,
                // The inverse SubByte S-Box
                array(
                    0x52, 0x09, 0x6A, 0xD5, 0x30, 0x36, 0xA5, 0x38, 0xBF, 0x40, 0xA3, 0x9E, 0x81, 0xF3, 0xD7, 0xFB,
                    0x7C, 0xE3, 0x39, 0x82, 0x9B, 0x2F, 0xFF, 0x87, 0x34, 0x8E, 0x43, 0x44, 0xC4, 0xDE, 0xE9, 0xCB,
                    0x54, 0x7B, 0x94, 0x32, 0xA6, 0xC2, 0x23, 0x3D, 0xEE, 0x4C, 0x95, 0x0B, 0x42, 0xFA, 0xC3, 0x4E,
                    0x08, 0x2E, 0xA1, 0x66, 0x28, 0xD9, 0x24, 0xB2, 0x76, 0x5B, 0xA2, 0x49, 0x6D, 0x8B, 0xD1, 0x25,
                    0x72, 0xF8, 0xF6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xD4, 0xA4, 0x5C, 0xCC, 0x5D, 0x65, 0xB6, 0x92,
                    0x6C, 0x70, 0x48, 0x50, 0xFD, 0xED, 0xB9, 0xDA, 0x5E, 0x15, 0x46, 0x57, 0xA7, 0x8D, 0x9D, 0x84,
                    0x90, 0xD8, 0xAB, 0x00, 0x8C, 0xBC, 0xD3, 0x0A, 0xF7, 0xE4, 0x58, 0x05, 0xB8, 0xB3, 0x45, 0x06,
                    0xD0, 0x2C, 0x1E, 0x8F, 0xCA, 0x3F, 0x0F, 0x02, 0xC1, 0xAF, 0xBD, 0x03, 0x01, 0x13, 0x8A, 0x6B,
                    0x3A, 0x91, 0x11, 0x41, 0x4F, 0x67, 0xDC, 0xEA, 0x97, 0xF2, 0xCF, 0xCE, 0xF0, 0xB4, 0xE6, 0x73,
                    0x96, 0xAC, 0x74, 0x22, 0xE7, 0xAD, 0x35, 0x85, 0xE2, 0xF9, 0x37, 0xE8, 0x1C, 0x75, 0xDF, 0x6E,
                    0x47, 0xF1, 0x1A, 0x71, 0x1D, 0x29, 0xC5, 0x89, 0x6F, 0xB7, 0x62, 0x0E, 0xAA, 0x18, 0xBE, 0x1B,
                    0xFC, 0x56, 0x3E, 0x4B, 0xC6, 0xD2, 0x79, 0x20, 0x9A, 0xDB, 0xC0, 0xFE, 0x78, 0xCD, 0x5A, 0xF4,
                    0x1F, 0xDD, 0xA8, 0x33, 0x88, 0x07, 0xC7, 0x31, 0xB1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xEC, 0x5F,
                    0x60, 0x51, 0x7F, 0xA9, 0x19, 0xB5, 0x4A, 0x0D, 0x2D, 0xE5, 0x7A, 0x9F, 0x93, 0xC9, 0x9C, 0xEF,
                    0xA0, 0xE0, 0x3B, 0x4D, 0xAE, 0x2A, 0xF5, 0xB0, 0xC8, 0xEB, 0xBB, 0x3C, 0x83, 0x53, 0x99, 0x61,
                    0x17, 0x2B, 0x04, 0x7E, 0xBA, 0x77, 0xD6, 0x26, 0xE1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0C, 0x7D
                )
            );
        }
        return $tables;
    }

    /**
     * Setup the performance-optimized function for de/encrypt()
     *
     * @see Crypt_Base::_setupInlineCrypt()
     * @access private
     */
    function _setupInlineCrypt()
    {
        // Note: _setupInlineCrypt() will be called only if $this->changed === true
        // So here we are'nt under the same heavy timing-stress as we are in _de/encryptBlock() or de/encrypt().
        // However...the here generated function- $code, stored as php callback in $this->inline_crypt, must work as fast as even possible.

        $lambda_functions =& Crypt_Rijndael::_getLambdaFunctions();

        // We create max. 10 hi-optimized code for memory reason. Means: For each $key one ultra fast inline-crypt function.
        // (Currently, for Crypt_Rijndael/AES, one generated $lambda_function cost on php5.5@32bit ~80kb unfreeable mem and ~130kb on php5.5@64bit)
        // After that, we'll still create very fast optimized code but not the hi-ultimative code, for each $mode one.
        $gen_hi_opt_code = (bool)(count($lambda_functions) < 10);

        // Generation of a uniqe hash for our generated code
        $code_hash = "Crypt_Rijndael, {$this->mode}, {$this->Nr}, {$this->Nb}";
        if ($gen_hi_opt_code) {
            $code_hash = str_pad($code_hash, 32) . $this->_hashInlineCryptFunction($this->key);
        }

        if (!isset($lambda_functions[$code_hash])) {
            switch (true) {
                case $gen_hi_opt_code:
                    // The hi-optimized $lambda_functions will use the key-words hardcoded for better performance.
                    $w  = $this->w;
                    $dw = $this->dw;
                    $init_encrypt = '';
                    $init_decrypt = '';
                    break;
                default:
                    for ($i = 0, $cw = count($this->w); $i < $cw; ++$i) {
                        $w[]  = '$w['  . $i . ']';
                        $dw[] = '$dw[' . $i . ']';
                    }
                    $init_encrypt = '$w  = $self->w;';
                    $init_decrypt = '$dw = $self->dw;';
            }

            $Nr = $this->Nr;
            $Nb = $this->Nb;
            $c  = $this->c;

            // Generating encrypt code:
            $init_encrypt.= '
                static $tables;
                if (empty($tables)) {
                    $tables = &$self->_getTables();
                }
                $t0   = $tables[0];
                $t1   = $tables[1];
                $t2   = $tables[2];
                $t3   = $tables[3];
                $sbox = $tables[4];
            ';

            $s  = 'e';
            $e  = 's';
            $wc = $Nb - 1;

            // Preround: addRoundKey
            $encrypt_block = '$in = unpack("N*", $in);'."\n";
            for ($i = 0; $i < $Nb; ++$i) {
                $encrypt_block .= '$s'.$i.' = $in['.($i + 1).'] ^ '.$w[++$wc].";\n";
            }

            // Mainrounds: shiftRows + subWord + mixColumns + addRoundKey
            for ($round = 1; $round < $Nr; ++$round) {
                list($s, $e) = array($e, $s);
                for ($i = 0; $i < $Nb; ++$i) {
                    $encrypt_block.=
                        '$'.$e.$i.' =
                        $t0[($'.$s.$i                  .' >> 24) & 0xff] ^
                        $t1[($'.$s.(($i + $c[1]) % $Nb).' >> 16) & 0xff] ^
                        $t2[($'.$s.(($i + $c[2]) % $Nb).' >>  8) & 0xff] ^
                        $t3[ $'.$s.(($i + $c[3]) % $Nb).'        & 0xff] ^
                        '.$w[++$wc].";\n";
                }
            }

            // Finalround: subWord + shiftRows + addRoundKey
            for ($i = 0; $i < $Nb; ++$i) {
                $encrypt_block.=
                    '$'.$e.$i.' =
                     $sbox[ $'.$e.$i.'        & 0xff]        |
                    ($sbox[($'.$e.$i.' >>  8) & 0xff] <<  8) |
                    ($sbox[($'.$e.$i.' >> 16) & 0xff] << 16) |
                    ($sbox[($'.$e.$i.' >> 24) & 0xff] << 24);'."\n";
            }
            $encrypt_block .= '$in = pack("N*"'."\n";
            for ($i = 0; $i < $Nb; ++$i) {
                $encrypt_block.= ',
                    ($'.$e.$i                  .' & '.((int)0xFF000000).') ^
                    ($'.$e.(($i + $c[1]) % $Nb).' &         0x00FF0000   ) ^
                    ($'.$e.(($i + $c[2]) % $Nb).' &         0x0000FF00   ) ^
                    ($'.$e.(($i + $c[3]) % $Nb).' &         0x000000FF   ) ^
                    '.$w[$i]."\n";
            }
            $encrypt_block .= ');';

            // Generating decrypt code:
            $init_decrypt.= '
                static $invtables;
                if (empty($invtables)) {
                    $invtables = &$self->_getInvTables();
                }
                $dt0   = $invtables[0];
                $dt1   = $invtables[1];
                $dt2   = $invtables[2];
                $dt3   = $invtables[3];
                $isbox = $invtables[4];
            ';

            $s  = 'e';
            $e  = 's';
            $wc = $Nb - 1;

            // Preround: addRoundKey
            $decrypt_block = '$in = unpack("N*", $in);'."\n";
            for ($i = 0; $i < $Nb; ++$i) {
                $decrypt_block .= '$s'.$i.' = $in['.($i + 1).'] ^ '.$dw[++$wc].';'."\n";
            }

            // Mainrounds: shiftRows + subWord + mixColumns + addRoundKey
            for ($round = 1; $round < $Nr; ++$round) {
                list($s, $e) = array($e, $s);
                for ($i = 0; $i < $Nb; ++$i) {
                    $decrypt_block.=
                        '$'.$e.$i.' =
                        $dt0[($'.$s.$i                        .' >> 24) & 0xff] ^
                        $dt1[($'.$s.(($Nb + $i - $c[1]) % $Nb).' >> 16) & 0xff] ^
                        $dt2[($'.$s.(($Nb + $i - $c[2]) % $Nb).' >>  8) & 0xff] ^
                        $dt3[ $'.$s.(($Nb + $i - $c[3]) % $Nb).'        & 0xff] ^
                        '.$dw[++$wc].";\n";
                }
            }

            // Finalround: subWord + shiftRows + addRoundKey
            for ($i = 0; $i < $Nb; ++$i) {
                $decrypt_block.=
                    '$'.$e.$i.' =
                     $isbox[ $'.$e.$i.'        & 0xff]        |
                    ($isbox[($'.$e.$i.' >>  8) & 0xff] <<  8) |
                    ($isbox[($'.$e.$i.' >> 16) & 0xff] << 16) |
                    ($isbox[($'.$e.$i.' >> 24) & 0xff] << 24);'."\n";
            }
            $decrypt_block .= '$in = pack("N*"'."\n";
            for ($i = 0; $i < $Nb; ++$i) {
                $decrypt_block.= ',
                    ($'.$e.$i.                        ' & '.((int)0xFF000000).') ^
                    ($'.$e.(($Nb + $i - $c[1]) % $Nb).' &         0x00FF0000   ) ^
                    ($'.$e.(($Nb + $i - $c[2]) % $Nb).' &         0x0000FF00   ) ^
                    ($'.$e.(($Nb + $i - $c[3]) % $Nb).' &         0x000000FF   ) ^
                    '.$dw[$i]."\n";
            }
            $decrypt_block .= ');';

            $lambda_functions[$code_hash] = $this->_createInlineCryptFunction(
                array(
                   'init_crypt'    => '',
                   'init_encrypt'  => $init_encrypt,
                   'init_decrypt'  => $init_decrypt,
                   'encrypt_block' => $encrypt_block,
                   'decrypt_block' => $decrypt_block
                )
            );
        }
        $this->inline_crypt = $lambda_functions[$code_hash];
    }
}
vendor/phpseclib/phpseclib/phpseclib/Crypt/TripleDES.php000064400000036132151327705700017355 0ustar00<?php

/**
 * Pure-PHP implementation of Triple DES.
 *
 * Uses mcrypt, if available, and an internal implementation, otherwise.  Operates in the EDE3 mode (encrypt-decrypt-encrypt).
 *
 * PHP versions 4 and 5
 *
 * Here's a short example of how to use this library:
 * <code>
 * <?php
 *    include 'Crypt/TripleDES.php';
 *
 *    $des = new Crypt_TripleDES();
 *
 *    $des->setKey('abcdefghijklmnopqrstuvwx');
 *
 *    $size = 10 * 1024;
 *    $plaintext = '';
 *    for ($i = 0; $i < $size; $i++) {
 *        $plaintext.= 'a';
 *    }
 *
 *    echo $des->decrypt($des->encrypt($plaintext));
 * ?>
 * </code>
 *
 * LICENSE: Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 *
 * @category  Crypt
 * @package   Crypt_TripleDES
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2007 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      http://phpseclib.sourceforge.net
 */

/**
 * Include Crypt_DES
 */
if (!class_exists('Crypt_DES')) {
    include_once 'DES.php';
}

/**#@+
 * @access public
 * @see self::Crypt_TripleDES()
 */
/**
 * Encrypt / decrypt using inner chaining
 *
 * Inner chaining is used by SSH-1 and is generally considered to be less secure then outer chaining (CRYPT_DES_MODE_CBC3).
 */
define('CRYPT_MODE_3CBC', -2);
/**
 * BC version of the above.
 */
define('CRYPT_DES_MODE_3CBC', -2);
/**
 * Encrypt / decrypt using outer chaining
 *
 * Outer chaining is used by SSH-2 and when the mode is set to CRYPT_DES_MODE_CBC.
 */
define('CRYPT_MODE_CBC3', CRYPT_MODE_CBC);
/**
 * BC version of the above.
 */
define('CRYPT_DES_MODE_CBC3', CRYPT_MODE_CBC3);
/**#@-*/

/**
 * Pure-PHP implementation of Triple DES.
 *
 * @package Crypt_TripleDES
 * @author  Jim Wigginton <terrafrost@php.net>
 * @access  public
 */
class Crypt_TripleDES extends Crypt_DES
{
    /**
     * Key Length (in bytes)
     *
     * @see Crypt_TripleDES::setKeyLength()
     * @var int
     * @access private
     */
    var $key_length = 24;

    /**
     * The default salt used by setPassword()
     *
     * @see Crypt_Base::password_default_salt
     * @see Crypt_Base::setPassword()
     * @var string
     * @access private
     */
    var $password_default_salt = 'phpseclib';

    /**
     * The namespace used by the cipher for its constants.
     *
     * @see Crypt_DES::const_namespace
     * @see Crypt_Base::const_namespace
     * @var string
     * @access private
     */
    var $const_namespace = 'DES';

    /**
     * The mcrypt specific name of the cipher
     *
     * @see Crypt_DES::cipher_name_mcrypt
     * @see Crypt_Base::cipher_name_mcrypt
     * @var string
     * @access private
     */
    var $cipher_name_mcrypt = 'tripledes';

    /**
     * Optimizing value while CFB-encrypting
     *
     * @see Crypt_Base::cfb_init_len
     * @var int
     * @access private
     */
    var $cfb_init_len = 750;

    /**
     * max possible size of $key
     *
     * @see self::setKey()
     * @see Crypt_DES::setKey()
     * @var string
     * @access private
     */
    var $key_length_max = 24;

    /**
     * Internal flag whether using CRYPT_DES_MODE_3CBC or not
     *
     * @var bool
     * @access private
     */
    var $mode_3cbc;

    /**
     * The Crypt_DES objects
     *
     * Used only if $mode_3cbc === true
     *
     * @var array
     * @access private
     */
    var $des;

    /**
     * Default Constructor.
     *
     * Determines whether or not the mcrypt extension should be used.
     *
     * $mode could be:
     *
     * - CRYPT_DES_MODE_ECB
     *
     * - CRYPT_DES_MODE_CBC
     *
     * - CRYPT_DES_MODE_CTR
     *
     * - CRYPT_DES_MODE_CFB
     *
     * - CRYPT_DES_MODE_OFB
     *
     * - CRYPT_DES_MODE_3CBC
     *
     * If not explicitly set, CRYPT_DES_MODE_CBC will be used.
     *
     * @see Crypt_DES::Crypt_DES()
     * @see Crypt_Base::Crypt_Base()
     * @param int $mode
     * @access public
     */
    function __construct($mode = CRYPT_MODE_CBC)
    {
        switch ($mode) {
            // In case of CRYPT_DES_MODE_3CBC, we init as CRYPT_DES_MODE_CBC
            // and additional flag us internally as 3CBC
            case CRYPT_DES_MODE_3CBC:
                parent::Crypt_Base(CRYPT_MODE_CBC);
                $this->mode_3cbc = true;

                // This three $des'es will do the 3CBC work (if $key > 64bits)
                $this->des = array(
                    new Crypt_DES(CRYPT_MODE_CBC),
                    new Crypt_DES(CRYPT_MODE_CBC),
                    new Crypt_DES(CRYPT_MODE_CBC),
                );

                // we're going to be doing the padding, ourselves, so disable it in the Crypt_DES objects
                $this->des[0]->disablePadding();
                $this->des[1]->disablePadding();
                $this->des[2]->disablePadding();
                break;
            // If not 3CBC, we init as usual
            default:
                parent::__construct($mode);
        }
    }

    /**
     * PHP4 compatible Default Constructor.
     *
     * @see self::__construct()
     * @param int $mode
     * @access public
     */
    function Crypt_TripleDES($mode = CRYPT_MODE_CBC)
    {
        $this->__construct($mode);
    }

    /**
     * Test for engine validity
     *
     * This is mainly just a wrapper to set things up for Crypt_Base::isValidEngine()
     *
     * @see Crypt_Base::Crypt_Base()
     * @param int $engine
     * @access public
     * @return bool
     */
    function isValidEngine($engine)
    {
        if ($engine == CRYPT_ENGINE_OPENSSL) {
            $this->cipher_name_openssl_ecb = 'des-ede3';
            $mode = $this->_openssl_translate_mode();
            $this->cipher_name_openssl = $mode == 'ecb' ? 'des-ede3' : 'des-ede3-' . $mode;
        }

        return parent::isValidEngine($engine);
    }

    /**
     * Sets the initialization vector. (optional)
     *
     * SetIV is not required when CRYPT_DES_MODE_ECB is being used.  If not explicitly set, it'll be assumed
     * to be all zero's.
     *
     * @see Crypt_Base::setIV()
     * @access public
     * @param string $iv
     */
    function setIV($iv)
    {
        parent::setIV($iv);
        if ($this->mode_3cbc) {
            $this->des[0]->setIV($iv);
            $this->des[1]->setIV($iv);
            $this->des[2]->setIV($iv);
        }
    }

    /**
     * Sets the key length.
     *
     * Valid key lengths are 64, 128 and 192
     *
     * @see Crypt_Base:setKeyLength()
     * @access public
     * @param int $length
     */
    function setKeyLength($length)
    {
        $length >>= 3;
        switch (true) {
            case $length <= 8:
                $this->key_length = 8;
                break;
            case $length <= 16:
                $this->key_length = 16;
                break;
            default:
                $this->key_length = 24;
        }

        parent::setKeyLength($length);
    }

    /**
     * Sets the key.
     *
     * Keys can be of any length.  Triple DES, itself, can use 128-bit (eg. strlen($key) == 16) or
     * 192-bit (eg. strlen($key) == 24) keys.  This function pads and truncates $key as appropriate.
     *
     * DES also requires that every eighth bit be a parity bit, however, we'll ignore that.
     *
     * If the key is not explicitly set, it'll be assumed to be all null bytes.
     *
     * @access public
     * @see Crypt_DES::setKey()
     * @see Crypt_Base::setKey()
     * @param string $key
     */
    function setKey($key)
    {
        $length = $this->explicit_key_length ? $this->key_length : strlen($key);
        if ($length > 8) {
            $key = str_pad(substr($key, 0, 24), 24, chr(0));
            // if $key is between 64 and 128-bits, use the first 64-bits as the last, per this:
            // http://php.net/function.mcrypt-encrypt#47973
            $key = $length <= 16 ? substr_replace($key, substr($key, 0, 8), 16) : substr($key, 0, 24);
        } else {
            $key = str_pad($key, 8, chr(0));
        }
        parent::setKey($key);

        // And in case of CRYPT_DES_MODE_3CBC:
        // if key <= 64bits we not need the 3 $des to work,
        // because we will then act as regular DES-CBC with just a <= 64bit key.
        // So only if the key > 64bits (> 8 bytes) we will call setKey() for the 3 $des.
        if ($this->mode_3cbc && $length > 8) {
            $this->des[0]->setKey(substr($key,  0, 8));
            $this->des[1]->setKey(substr($key,  8, 8));
            $this->des[2]->setKey(substr($key, 16, 8));
        }
    }

    /**
     * Encrypts a message.
     *
     * @see Crypt_Base::encrypt()
     * @access public
     * @param string $plaintext
     * @return string $cipertext
     */
    function encrypt($plaintext)
    {
        // parent::en/decrypt() is able to do all the work for all modes and keylengths,
        // except for: CRYPT_MODE_3CBC (inner chaining CBC) with a key > 64bits

        // if the key is smaller then 8, do what we'd normally do
        if ($this->mode_3cbc && strlen($this->key) > 8) {
            return $this->des[2]->encrypt(
                $this->des[1]->decrypt(
                    $this->des[0]->encrypt(
                        $this->_pad($plaintext)
                    )
                )
            );
        }

        return parent::encrypt($plaintext);
    }

    /**
     * Decrypts a message.
     *
     * @see Crypt_Base::decrypt()
     * @access public
     * @param string $ciphertext
     * @return string $plaintext
     */
    function decrypt($ciphertext)
    {
        if ($this->mode_3cbc && strlen($this->key) > 8) {
            return $this->_unpad(
                $this->des[0]->decrypt(
                    $this->des[1]->encrypt(
                        $this->des[2]->decrypt(
                            str_pad($ciphertext, (strlen($ciphertext) + 7) & 0xFFFFFFF8, "\0")
                        )
                    )
                )
            );
        }

        return parent::decrypt($ciphertext);
    }

    /**
     * Treat consecutive "packets" as if they are a continuous buffer.
     *
     * Say you have a 16-byte plaintext $plaintext.  Using the default behavior, the two following code snippets
     * will yield different outputs:
     *
     * <code>
     *    echo $des->encrypt(substr($plaintext, 0, 8));
     *    echo $des->encrypt(substr($plaintext, 8, 8));
     * </code>
     * <code>
     *    echo $des->encrypt($plaintext);
     * </code>
     *
     * The solution is to enable the continuous buffer.  Although this will resolve the above discrepancy, it creates
     * another, as demonstrated with the following:
     *
     * <code>
     *    $des->encrypt(substr($plaintext, 0, 8));
     *    echo $des->decrypt($des->encrypt(substr($plaintext, 8, 8)));
     * </code>
     * <code>
     *    echo $des->decrypt($des->encrypt(substr($plaintext, 8, 8)));
     * </code>
     *
     * With the continuous buffer disabled, these would yield the same output.  With it enabled, they yield different
     * outputs.  The reason is due to the fact that the initialization vector's change after every encryption /
     * decryption round when the continuous buffer is enabled.  When it's disabled, they remain constant.
     *
     * Put another way, when the continuous buffer is enabled, the state of the Crypt_DES() object changes after each
     * encryption / decryption round, whereas otherwise, it'd remain constant.  For this reason, it's recommended that
     * continuous buffers not be used.  They do offer better security and are, in fact, sometimes required (SSH uses them),
     * however, they are also less intuitive and more likely to cause you problems.
     *
     * @see Crypt_Base::enableContinuousBuffer()
     * @see self::disableContinuousBuffer()
     * @access public
     */
    function enableContinuousBuffer()
    {
        parent::enableContinuousBuffer();
        if ($this->mode_3cbc) {
            $this->des[0]->enableContinuousBuffer();
            $this->des[1]->enableContinuousBuffer();
            $this->des[2]->enableContinuousBuffer();
        }
    }

    /**
     * Treat consecutive packets as if they are a discontinuous buffer.
     *
     * The default behavior.
     *
     * @see Crypt_Base::disableContinuousBuffer()
     * @see self::enableContinuousBuffer()
     * @access public
     */
    function disableContinuousBuffer()
    {
        parent::disableContinuousBuffer();
        if ($this->mode_3cbc) {
            $this->des[0]->disableContinuousBuffer();
            $this->des[1]->disableContinuousBuffer();
            $this->des[2]->disableContinuousBuffer();
        }
    }

    /**
     * Creates the key schedule
     *
     * @see Crypt_DES::_setupKey()
     * @see Crypt_Base::_setupKey()
     * @access private
     */
    function _setupKey()
    {
        switch (true) {
            // if $key <= 64bits we configure our internal pure-php cipher engine
            // to act as regular [1]DES, not as 3DES. mcrypt.so::tripledes does the same.
            case strlen($this->key) <= 8:
                $this->des_rounds = 1;
                break;

            // otherwise, if $key > 64bits, we configure our engine to work as 3DES.
            default:
                $this->des_rounds = 3;

                // (only) if 3CBC is used we have, of course, to setup the $des[0-2] keys also separately.
                if ($this->mode_3cbc) {
                    $this->des[0]->_setupKey();
                    $this->des[1]->_setupKey();
                    $this->des[2]->_setupKey();

                    // because $des[0-2] will, now, do all the work we can return here
                    // not need unnecessary stress parent::_setupKey() with our, now unused, $key.
                    return;
                }
        }
        // setup our key
        parent::_setupKey();
    }

    /**
     * Sets the internal crypt engine
     *
     * @see Crypt_Base::Crypt_Base()
     * @see Crypt_Base::setPreferredEngine()
     * @param int $engine
     * @access public
     * @return int
     */
    function setPreferredEngine($engine)
    {
        if ($this->mode_3cbc) {
            $this->des[0]->setPreferredEngine($engine);
            $this->des[1]->setPreferredEngine($engine);
            $this->des[2]->setPreferredEngine($engine);
        }

        return parent::setPreferredEngine($engine);
    }
}
vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger.php000064400000400270151327705700017367 0ustar00<?php

/**
 * Pure-PHP arbitrary precision integer arithmetic library.
 *
 * Supports base-2, base-10, base-16, and base-256 numbers.  Uses the GMP or BCMath extensions, if available,
 * and an internal implementation, otherwise.
 *
 * PHP versions 4 and 5
 *
 * {@internal (all DocBlock comments regarding implementation - such as the one that follows - refer to the
 * {@link MATH_BIGINTEGER_MODE_INTERNAL MATH_BIGINTEGER_MODE_INTERNAL} mode)
 *
 * Math_BigInteger uses base-2**26 to perform operations such as multiplication and division and
 * base-2**52 (ie. two base 2**26 digits) to perform addition and subtraction.  Because the largest possible
 * value when multiplying two base-2**26 numbers together is a base-2**52 number, double precision floating
 * point numbers - numbers that should be supported on most hardware and whose significand is 53 bits - are
 * used.  As a consequence, bitwise operators such as >> and << cannot be used, nor can the modulo operator %,
 * which only supports integers.  Although this fact will slow this library down, the fact that such a high
 * base is being used should more than compensate.
 *
 * Numbers are stored in {@link http://en.wikipedia.org/wiki/Endianness little endian} format.  ie.
 * (new Math_BigInteger(pow(2, 26)))->value = array(0, 1)
 *
 * Useful resources are as follows:
 *
 *  - {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf Handbook of Applied Cryptography (HAC)}
 *  - {@link http://math.libtomcrypt.com/files/tommath.pdf Multi-Precision Math (MPM)}
 *  - Java's BigInteger classes.  See /j2se/src/share/classes/java/math in jdk-1_5_0-src-jrl.zip
 *
 * Here's an example of how to use this library:
 * <code>
 * <?php
 *    include 'Math/BigInteger.php';
 *
 *    $a = new Math_BigInteger(2);
 *    $b = new Math_BigInteger(3);
 *
 *    $c = $a->add($b);
 *
 *    echo $c->toString(); // outputs 5
 * ?>
 * </code>
 *
 * LICENSE: Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 *
 * @category  Math
 * @package   Math_BigInteger
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2006 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 */

/**#@+
 * Reduction constants
 *
 * @access private
 * @see self::_reduce()
 */
/**
 * @see self::_montgomery()
 * @see self::_prepMontgomery()
 */
define('MATH_BIGINTEGER_MONTGOMERY', 0);
/**
 * @see self::_barrett()
 */
define('MATH_BIGINTEGER_BARRETT', 1);
/**
 * @see self::_mod2()
 */
define('MATH_BIGINTEGER_POWEROF2', 2);
/**
 * @see self::_remainder()
 */
define('MATH_BIGINTEGER_CLASSIC', 3);
/**
 * @see self::__clone()
 */
define('MATH_BIGINTEGER_NONE', 4);
/**#@-*/

/**#@+
 * Array constants
 *
 * Rather than create a thousands and thousands of new Math_BigInteger objects in repeated function calls to add() and
 * multiply() or whatever, we'll just work directly on arrays, taking them in as parameters and returning them.
 *
 * @access private
 */
/**
 * $result[MATH_BIGINTEGER_VALUE] contains the value.
 */
define('MATH_BIGINTEGER_VALUE', 0);
/**
 * $result[MATH_BIGINTEGER_SIGN] contains the sign.
 */
define('MATH_BIGINTEGER_SIGN', 1);
/**#@-*/

/**#@+
 * @access private
 * @see self::_montgomery()
 * @see self::_barrett()
 */
/**
 * Cache constants
 *
 * $cache[MATH_BIGINTEGER_VARIABLE] tells us whether or not the cached data is still valid.
 */
define('MATH_BIGINTEGER_VARIABLE', 0);
/**
 * $cache[MATH_BIGINTEGER_DATA] contains the cached data.
 */
define('MATH_BIGINTEGER_DATA', 1);
/**#@-*/

/**#@+
 * Mode constants.
 *
 * @access private
 * @see self::Math_BigInteger()
 */
/**
 * To use the pure-PHP implementation
 */
define('MATH_BIGINTEGER_MODE_INTERNAL', 1);
/**
 * To use the BCMath library
 *
 * (if enabled; otherwise, the internal implementation will be used)
 */
define('MATH_BIGINTEGER_MODE_BCMATH', 2);
/**
 * To use the GMP library
 *
 * (if present; otherwise, either the BCMath or the internal implementation will be used)
 */
define('MATH_BIGINTEGER_MODE_GMP', 3);
/**#@-*/

/**
 * Karatsuba Cutoff
 *
 * At what point do we switch between Karatsuba multiplication and schoolbook long multiplication?
 *
 * @access private
 */
define('MATH_BIGINTEGER_KARATSUBA_CUTOFF', 25);

/**
 * Pure-PHP arbitrary precision integer arithmetic library. Supports base-2, base-10, base-16, and base-256
 * numbers.
 *
 * @package Math_BigInteger
 * @author  Jim Wigginton <terrafrost@php.net>
 * @access  public
 */
class Math_BigInteger
{
    /**
     * Holds the BigInteger's value.
     *
     * @var array
     * @access private
     */
    var $value;

    /**
     * Holds the BigInteger's magnitude.
     *
     * @var bool
     * @access private
     */
    var $is_negative = false;

    /**
     * Precision
     *
     * @see self::setPrecision()
     * @access private
     */
    var $precision = -1;

    /**
     * Precision Bitmask
     *
     * @see self::setPrecision()
     * @access private
     */
    var $bitmask = false;

    /**
     * Mode independent value used for serialization.
     *
     * If the bcmath or gmp extensions are installed $this->value will be a non-serializable resource, hence the need for
     * a variable that'll be serializable regardless of whether or not extensions are being used.  Unlike $this->value,
     * however, $this->hex is only calculated when $this->__sleep() is called.
     *
     * @see self::__sleep()
     * @see self::__wakeup()
     * @var string
     * @access private
     */
    var $hex;

    /**
     * Converts base-2, base-10, base-16, and binary strings (base-256) to BigIntegers.
     *
     * If the second parameter - $base - is negative, then it will be assumed that the number's are encoded using
     * two's compliment.  The sole exception to this is -10, which is treated the same as 10 is.
     *
     * Here's an example:
     * <code>
     * <?php
     *    include 'Math/BigInteger.php';
     *
     *    $a = new Math_BigInteger('0x32', 16); // 50 in base-16
     *
     *    echo $a->toString(); // outputs 50
     * ?>
     * </code>
     *
     * @param $x base-10 number or base-$base number if $base set.
     * @param int $base
     * @return Math_BigInteger
     * @access public
     */
    function __construct($x = 0, $base = 10)
    {
        if (!defined('MATH_BIGINTEGER_MODE')) {
            if(preg_match('/\d\.\d\.\d{2}/',PHP_VERSION,$matches))
            {
                $php_version=$matches[0];
            }
            else
            {
                $php_version=PHP_VERSION;
            }
            if((substr($php_version, 0, 3) === '8.2') && version_compare($php_version, '8.2.26', '>=') ||
                (substr($php_version, 0, 3) === '8.3') && version_compare($php_version, '8.3.14', '>='))
            {
                switch (true) {
                    case extension_loaded('bcmath'):
                        define('MATH_BIGINTEGER_MODE', MATH_BIGINTEGER_MODE_BCMATH);
                        break;
                    default:
                        define('MATH_BIGINTEGER_MODE', MATH_BIGINTEGER_MODE_INTERNAL);
                }
            }
            else
            {
                switch (true) {
                    case extension_loaded('gmp'):
                        define('MATH_BIGINTEGER_MODE', MATH_BIGINTEGER_MODE_GMP);
                        break;
                    case extension_loaded('bcmath'):
                        define('MATH_BIGINTEGER_MODE', MATH_BIGINTEGER_MODE_BCMATH);
                        break;
                    default:
                        define('MATH_BIGINTEGER_MODE', MATH_BIGINTEGER_MODE_INTERNAL);
                }
            }
        }

        if (extension_loaded('openssl') && !defined('MATH_BIGINTEGER_OPENSSL_DISABLE') && !defined('MATH_BIGINTEGER_OPENSSL_ENABLED')) {
            // some versions of XAMPP have mismatched versions of OpenSSL which causes it not to work
            $versions = array();

            // avoid generating errors (even with suppression) when phpinfo() is disabled (common in production systems)
            if (strpos(ini_get('disable_functions'), 'phpinfo') === false) {
                ob_start();
                @phpinfo();
                $content = ob_get_contents();
                ob_end_clean();

                preg_match_all('#OpenSSL (Header|Library) Version(.*)#im', $content, $matches);

                if (!empty($matches[1])) {
                    for ($i = 0; $i < count($matches[1]); $i++) {
                        $fullVersion = trim(str_replace('=>', '', strip_tags($matches[2][$i])));

                        // Remove letter part in OpenSSL version
                        if (!preg_match('/(\d+\.\d+\.\d+)/i', $fullVersion, $m)) {
                            $versions[$matches[1][$i]] = $fullVersion;
                        } else {
                            $versions[$matches[1][$i]] = $m[0];
                        }
                    }
                }
            }

            // it doesn't appear that OpenSSL versions were reported upon until PHP 5.3+
            switch (true) {
                case !isset($versions['Header']):
                case !isset($versions['Library']):
                case $versions['Header'] == $versions['Library']:
                case version_compare($versions['Header'], '1.0.0') >= 0 && version_compare($versions['Library'], '1.0.0') >= 0:
                    define('MATH_BIGINTEGER_OPENSSL_ENABLED', true);
                    break;
                default:
                    define('MATH_BIGINTEGER_OPENSSL_DISABLE', true);
            }
        }

        if (!defined('PHP_INT_SIZE')) {
            define('PHP_INT_SIZE', 4);
        }

        if (!defined('MATH_BIGINTEGER_BASE') && MATH_BIGINTEGER_MODE == MATH_BIGINTEGER_MODE_INTERNAL) {
            switch (PHP_INT_SIZE) {
                case 8: // use 64-bit integers if int size is 8 bytes
                    define('MATH_BIGINTEGER_BASE',       31);
                    define('MATH_BIGINTEGER_BASE_FULL',  0x80000000);
                    define('MATH_BIGINTEGER_MAX_DIGIT',  0x7FFFFFFF);
                    define('MATH_BIGINTEGER_MSB',        0x40000000);
                    // 10**9 is the closest we can get to 2**31 without passing it
                    define('MATH_BIGINTEGER_MAX10',      1000000000);
                    define('MATH_BIGINTEGER_MAX10_LEN',  9);
                    // the largest digit that may be used in addition / subtraction
                    define('MATH_BIGINTEGER_MAX_DIGIT2', pow(2, 62));
                    break;
                //case 4: // use 64-bit floats if int size is 4 bytes
                default:
                    define('MATH_BIGINTEGER_BASE',       26);
                    define('MATH_BIGINTEGER_BASE_FULL',  0x4000000);
                    define('MATH_BIGINTEGER_MAX_DIGIT',  0x3FFFFFF);
                    define('MATH_BIGINTEGER_MSB',        0x2000000);
                    // 10**7 is the closest to 2**26 without passing it
                    define('MATH_BIGINTEGER_MAX10',      10000000);
                    define('MATH_BIGINTEGER_MAX10_LEN',  7);
                    // the largest digit that may be used in addition / subtraction
                    // we do pow(2, 52) instead of using 4503599627370496 directly because some
                    // PHP installations will truncate 4503599627370496.
                    define('MATH_BIGINTEGER_MAX_DIGIT2', pow(2, 52));
            }
        }

        switch (MATH_BIGINTEGER_MODE) {
            case MATH_BIGINTEGER_MODE_GMP:
                switch (true) {
                    case is_resource($x) && get_resource_type($x) == 'GMP integer':
                    // PHP 5.6 switched GMP from using resources to objects
                    case is_object($x) && get_class($x) == 'GMP':
                        $this->value = $x;
                        return;
                }
                $this->value = gmp_init(0);
                break;
            case MATH_BIGINTEGER_MODE_BCMATH:
                $this->value = '0';
                break;
            default:
                $this->value = array();
        }

        // '0' counts as empty() but when the base is 256 '0' is equal to ord('0') or 48
        // '0' is the only value like this per http://php.net/empty
        if (empty($x) && (abs($base) != 256 || $x !== '0')) {
            return;
        }

        switch ($base) {
            case -256:
                if (ord($x[0]) & 0x80) {
                    $x = ~$x;
                    $this->is_negative = true;
                }
            case 256:
                switch (MATH_BIGINTEGER_MODE) {
                    case MATH_BIGINTEGER_MODE_GMP:
                        $this->value = function_exists('gmp_import') ?
                            gmp_import($x) :
                            gmp_init('0x' . bin2hex($x));
                        if ($this->is_negative) {
                            $this->value = gmp_neg($this->value);
                        }
                        break;
                    case MATH_BIGINTEGER_MODE_BCMATH:
                        // round $len to the nearest 4 (thanks, DavidMJ!)
                        $len = (strlen($x) + 3) & 0xFFFFFFFC;

                        $x = str_pad($x, $len, chr(0), STR_PAD_LEFT);

                        for ($i = 0; $i < $len; $i+= 4) {
                            $this->value = bcmul($this->value, '4294967296', 0); // 4294967296 == 2**32
                            $this->value = bcadd($this->value, 0x1000000 * ord($x[$i]) + ((ord($x[$i + 1]) << 16) | (ord($x[$i + 2]) << 8) | ord($x[$i + 3])), 0);
                        }

                        if ($this->is_negative) {
                            $this->value = '-' . $this->value;
                        }

                        break;
                    // converts a base-2**8 (big endian / msb) number to base-2**26 (little endian / lsb)
                    default:
                        while (strlen($x)) {
                            $this->value[] = $this->_bytes2int($this->_base256_rshift($x, MATH_BIGINTEGER_BASE));
                        }
                }

                if ($this->is_negative) {
                    if (MATH_BIGINTEGER_MODE != MATH_BIGINTEGER_MODE_INTERNAL) {
                        $this->is_negative = false;
                    }
                    $temp = $this->add(new Math_BigInteger('-1'));
                    $this->value = $temp->value;
                }
                break;
            case 16:
            case -16:
                if ($base > 0 && $x[0] == '-') {
                    $this->is_negative = true;
                    $x = substr($x, 1);
                }

                $x = preg_replace('#^(?:0x)?([A-Fa-f0-9]*).*#', '$1', $x);

                $is_negative = false;
                if ($base < 0 && hexdec($x[0]) >= 8) {
                    $this->is_negative = $is_negative = true;
                    $x = bin2hex(~pack('H*', $x));
                }

                switch (MATH_BIGINTEGER_MODE) {
                    case MATH_BIGINTEGER_MODE_GMP:
                        $temp = $this->is_negative ? '-0x' . $x : '0x' . $x;
                        $this->value = gmp_init($temp);
                        $this->is_negative = false;
                        break;
                    case MATH_BIGINTEGER_MODE_BCMATH:
                        $x = (strlen($x) & 1) ? '0' . $x : $x;
                        $temp = new Math_BigInteger(pack('H*', $x), 256);
                        $this->value = $this->is_negative ? '-' . $temp->value : $temp->value;
                        $this->is_negative = false;
                        break;
                    default:
                        $x = (strlen($x) & 1) ? '0' . $x : $x;
                        $temp = new Math_BigInteger(pack('H*', $x), 256);
                        $this->value = $temp->value;
                }

                if ($is_negative) {
                    $temp = $this->add(new Math_BigInteger('-1'));
                    $this->value = $temp->value;
                }
                break;
            case 10:
            case -10:
                // (?<!^)(?:-).*: find any -'s that aren't at the beginning and then any characters that follow that
                // (?<=^|-)0*: find any 0's that are preceded by the start of the string or by a - (ie. octals)
                // [^-0-9].*: find any non-numeric characters and then any characters that follow that
                $x = preg_replace('#(?<!^)(?:-).*|(?<=^|-)0*|[^-0-9].*#', '', $x);

                switch (MATH_BIGINTEGER_MODE) {
                    case MATH_BIGINTEGER_MODE_GMP:
                        $this->value = gmp_init($x);
                        break;
                    case MATH_BIGINTEGER_MODE_BCMATH:
                        // explicitly casting $x to a string is necessary, here, since doing $x[0] on -1 yields different
                        // results then doing it on '-1' does (modInverse does $x[0])
                        $this->value = $x === '-' ? '0' : (string) $x;
                        break;
                    default:
                        $temp = new Math_BigInteger();

                        $multiplier = new Math_BigInteger();
                        $multiplier->value = array(MATH_BIGINTEGER_MAX10);

                        if ($x[0] == '-') {
                            $this->is_negative = true;
                            $x = substr($x, 1);
                        }

                        $x = str_pad($x, strlen($x) + ((MATH_BIGINTEGER_MAX10_LEN - 1) * strlen($x)) % MATH_BIGINTEGER_MAX10_LEN, 0, STR_PAD_LEFT);
                        while (strlen($x)) {
                            $temp = $temp->multiply($multiplier);
                            $temp = $temp->add(new Math_BigInteger($this->_int2bytes(substr($x, 0, MATH_BIGINTEGER_MAX10_LEN)), 256));
                            $x = substr($x, MATH_BIGINTEGER_MAX10_LEN);
                        }

                        $this->value = $temp->value;
                }
                break;
            case 2: // base-2 support originally implemented by Lluis Pamies - thanks!
            case -2:
                if ($base > 0 && $x[0] == '-') {
                    $this->is_negative = true;
                    $x = substr($x, 1);
                }

                $x = preg_replace('#^([01]*).*#', '$1', $x);
                $x = str_pad($x, strlen($x) + (3 * strlen($x)) % 4, 0, STR_PAD_LEFT);

                $str = '0x';
                while (strlen($x)) {
                    $part = substr($x, 0, 4);
                    $str.= dechex(bindec($part));
                    $x = substr($x, 4);
                }

                if ($this->is_negative) {
                    $str = '-' . $str;
                }

                $temp = new Math_BigInteger($str, 8 * $base); // ie. either -16 or +16
                $this->value = $temp->value;
                $this->is_negative = $temp->is_negative;

                break;
            default:
                // base not supported, so we'll let $this == 0
        }
    }

    /**
     * PHP4 compatible Default Constructor.
     *
     * @see self::__construct()
     * @param $x base-10 number or base-$base number if $base set.
     * @param int $base
     * @access public
     */
    function Math_BigInteger($x = 0, $base = 10)
    {
        $this->__construct($x, $base);
    }

    /**
     * Converts a BigInteger to a byte string (eg. base-256).
     *
     * Negative numbers are saved as positive numbers, unless $twos_compliment is set to true, at which point, they're
     * saved as two's compliment.
     *
     * Here's an example:
     * <code>
     * <?php
     *    include 'Math/BigInteger.php';
     *
     *    $a = new Math_BigInteger('65');
     *
     *    echo $a->toBytes(); // outputs chr(65)
     * ?>
     * </code>
     *
     * @param bool $twos_compliment
     * @return string
     * @access public
     * @internal Converts a base-2**26 number to base-2**8
     */
    function toBytes($twos_compliment = false)
    {
        if ($twos_compliment) {
            $comparison = $this->compare(new Math_BigInteger());
            if ($comparison == 0) {
                return $this->precision > 0 ? str_repeat(chr(0), ($this->precision + 1) >> 3) : '';
            }

            $temp = $comparison < 0 ? $this->add(new Math_BigInteger(1)) : $this->copy();
            $bytes = $temp->toBytes();

            if (!strlen($bytes)) { // eg. if the number we're trying to convert is -1
                $bytes = chr(0);
            }

            if (ord($bytes[0]) & 0x80) {
                $bytes = chr(0) . $bytes;
            }

            return $comparison < 0 ? ~$bytes : $bytes;
        }

        switch (MATH_BIGINTEGER_MODE) {
            case MATH_BIGINTEGER_MODE_GMP:
                if (gmp_cmp($this->value, gmp_init(0)) == 0) {
                    return $this->precision > 0 ? str_repeat(chr(0), ($this->precision + 1) >> 3) : '';
                }

                if (function_exists('gmp_export')) {
                    $temp = gmp_export($this->value);
                } else {
                    $temp = gmp_strval(gmp_abs($this->value), 16);
                    $temp = (strlen($temp) & 1) ? '0' . $temp : $temp;
                    $temp = pack('H*', $temp);
                }

                return $this->precision > 0 ?
                    substr(str_pad($temp, $this->precision >> 3, chr(0), STR_PAD_LEFT), -($this->precision >> 3)) :
                    ltrim($temp, chr(0));
            case MATH_BIGINTEGER_MODE_BCMATH:
                if ($this->value === '0') {
                    return $this->precision > 0 ? str_repeat(chr(0), ($this->precision + 1) >> 3) : '';
                }

                $value = '';
                $current = $this->value;

                if ($current[0] == '-') {
                    $current = substr($current, 1);
                }

                while (bccomp($current, '0', 0) > 0) {
                    $temp = bcmod($current, '16777216');
                    $value = chr($temp >> 16) . chr($temp >> 8) . chr($temp) . $value;
                    $current = bcdiv($current, '16777216', 0);
                }

                return $this->precision > 0 ?
                    substr(str_pad($value, $this->precision >> 3, chr(0), STR_PAD_LEFT), -($this->precision >> 3)) :
                    ltrim($value, chr(0));
        }

        if (!count($this->value)) {
            return $this->precision > 0 ? str_repeat(chr(0), ($this->precision + 1) >> 3) : '';
        }
        $result = $this->_int2bytes($this->value[count($this->value) - 1]);

        $temp = $this->copy();

        for ($i = count($temp->value) - 2; $i >= 0; --$i) {
            $temp->_base256_lshift($result, MATH_BIGINTEGER_BASE);
            $result = $result | str_pad($temp->_int2bytes($temp->value[$i]), strlen($result), chr(0), STR_PAD_LEFT);
        }

        return $this->precision > 0 ?
            str_pad(substr($result, -(($this->precision + 7) >> 3)), ($this->precision + 7) >> 3, chr(0), STR_PAD_LEFT) :
            $result;
    }

    /**
     * Converts a BigInteger to a hex string (eg. base-16)).
     *
     * Negative numbers are saved as positive numbers, unless $twos_compliment is set to true, at which point, they're
     * saved as two's compliment.
     *
     * Here's an example:
     * <code>
     * <?php
     *    include 'Math/BigInteger.php';
     *
     *    $a = new Math_BigInteger('65');
     *
     *    echo $a->toHex(); // outputs '41'
     * ?>
     * </code>
     *
     * @param bool $twos_compliment
     * @return string
     * @access public
     * @internal Converts a base-2**26 number to base-2**8
     */
    function toHex($twos_compliment = false)
    {
        return bin2hex($this->toBytes($twos_compliment));
    }

    /**
     * Converts a BigInteger to a bit string (eg. base-2).
     *
     * Negative numbers are saved as positive numbers, unless $twos_compliment is set to true, at which point, they're
     * saved as two's compliment.
     *
     * Here's an example:
     * <code>
     * <?php
     *    include 'Math/BigInteger.php';
     *
     *    $a = new Math_BigInteger('65');
     *
     *    echo $a->toBits(); // outputs '1000001'
     * ?>
     * </code>
     *
     * @param bool $twos_compliment
     * @return string
     * @access public
     * @internal Converts a base-2**26 number to base-2**2
     */
    function toBits($twos_compliment = false)
    {
        $hex = $this->toHex($twos_compliment);
        $bits = '';
        for ($i = strlen($hex) - 8, $start = strlen($hex) & 7; $i >= $start; $i-=8) {
            $bits = str_pad(decbin(hexdec(substr($hex, $i, 8))), 32, '0', STR_PAD_LEFT) . $bits;
        }
        if ($start) { // hexdec('') == 0
            $bits = str_pad(decbin(hexdec(substr($hex, 0, $start))), 8, '0', STR_PAD_LEFT) . $bits;
        }
        $result = $this->precision > 0 ? substr($bits, -$this->precision) : ltrim($bits, '0');

        if ($twos_compliment && $this->compare(new Math_BigInteger()) > 0 && $this->precision <= 0) {
            return '0' . $result;
        }

        return $result;
    }

    /**
     * Converts a BigInteger to a base-10 number.
     *
     * Here's an example:
     * <code>
     * <?php
     *    include 'Math/BigInteger.php';
     *
     *    $a = new Math_BigInteger('50');
     *
     *    echo $a->toString(); // outputs 50
     * ?>
     * </code>
     *
     * @return string
     * @access public
     * @internal Converts a base-2**26 number to base-10**7 (which is pretty much base-10)
     */
    function toString()
    {
        switch (MATH_BIGINTEGER_MODE) {
            case MATH_BIGINTEGER_MODE_GMP:
                return gmp_strval($this->value);
            case MATH_BIGINTEGER_MODE_BCMATH:
                if ($this->value === '0') {
                    return '0';
                }

                return ltrim($this->value, '0');
        }

        if (!count($this->value)) {
            return '0';
        }

        $temp = $this->copy();
        $temp->is_negative = false;

        $divisor = new Math_BigInteger();
        $divisor->value = array(MATH_BIGINTEGER_MAX10);
        $result = '';
        while (count($temp->value)) {
            list($temp, $mod) = $temp->divide($divisor);
            $result = str_pad(isset($mod->value[0]) ? $mod->value[0] : '', MATH_BIGINTEGER_MAX10_LEN, '0', STR_PAD_LEFT) . $result;
        }
        $result = ltrim($result, '0');
        if (empty($result)) {
            $result = '0';
        }

        if ($this->is_negative) {
            $result = '-' . $result;
        }

        return $result;
    }

    /**
     * Copy an object
     *
     * PHP5 passes objects by reference while PHP4 passes by value.  As such, we need a function to guarantee
     * that all objects are passed by value, when appropriate.  More information can be found here:
     *
     * {@link http://php.net/language.oop5.basic#51624}
     *
     * @access public
     * @see self::__clone()
     * @return Math_BigInteger
     */
    function copy()
    {
        $temp = new Math_BigInteger();
        $temp->value = $this->value;
        $temp->is_negative = $this->is_negative;
        $temp->precision = $this->precision;
        $temp->bitmask = $this->bitmask;
        return $temp;
    }

    /**
     *  __toString() magic method
     *
     * Will be called, automatically, if you're supporting just PHP5.  If you're supporting PHP4, you'll need to call
     * toString().
     *
     * @access public
     * @internal Implemented per a suggestion by Techie-Michael - thanks!
     */
    function __toString()
    {
        return $this->toString();
    }

    /**
     * __clone() magic method
     *
     * Although you can call Math_BigInteger::__toString() directly in PHP5, you cannot call Math_BigInteger::__clone()
     * directly in PHP5.  You can in PHP4 since it's not a magic method, but in PHP5, you have to call it by using the PHP5
     * only syntax of $y = clone $x.  As such, if you're trying to write an application that works on both PHP4 and PHP5,
     * call Math_BigInteger::copy(), instead.
     *
     * @access public
     * @see self::copy()
     * @return Math_BigInteger
     */
    function __clone()
    {
        return $this->copy();
    }

    /**
     *  __sleep() magic method
     *
     * Will be called, automatically, when serialize() is called on a Math_BigInteger object.
     *
     * @see self::__wakeup()
     * @access public
     */
    function __sleep()
    {
        $this->hex = $this->toHex(true);
        $vars = array('hex');
        if ($this->precision > 0) {
            $vars[] = 'precision';
        }
        return $vars;
    }

    /**
     *  __wakeup() magic method
     *
     * Will be called, automatically, when unserialize() is called on a Math_BigInteger object.
     *
     * @see self::__sleep()
     * @access public
     */
    function __wakeup()
    {
        $temp = new Math_BigInteger($this->hex, -16);
        $this->value = $temp->value;
        $this->is_negative = $temp->is_negative;
        if ($this->precision > 0) {
            // recalculate $this->bitmask
            $this->setPrecision($this->precision);
        }
    }

    /**
     *  __debugInfo() magic method
     *
     * Will be called, automatically, when print_r() or var_dump() are called
     *
     * @access public
     */
    function __debugInfo()
    {
        $opts = array();
        switch (MATH_BIGINTEGER_MODE) {
            case MATH_BIGINTEGER_MODE_GMP:
                $engine = 'gmp';
                break;
            case MATH_BIGINTEGER_MODE_BCMATH:
                $engine = 'bcmath';
                break;
            case MATH_BIGINTEGER_MODE_INTERNAL:
                $engine = 'internal';
                $opts[] = PHP_INT_SIZE == 8 ? '64-bit' : '32-bit';
        }
        if (MATH_BIGINTEGER_MODE != MATH_BIGINTEGER_MODE_GMP && defined('MATH_BIGINTEGER_OPENSSL_ENABLED')) {
            $opts[] = 'OpenSSL';
        }
        if (!empty($opts)) {
            $engine.= ' (' . implode($opts, ', ') . ')';
        }
        return array(
            'value' => '0x' . $this->toHex(true),
            'engine' => $engine
        );
    }

    /**
     * Adds two BigIntegers.
     *
     * Here's an example:
     * <code>
     * <?php
     *    include 'Math/BigInteger.php';
     *
     *    $a = new Math_BigInteger('10');
     *    $b = new Math_BigInteger('20');
     *
     *    $c = $a->add($b);
     *
     *    echo $c->toString(); // outputs 30
     * ?>
     * </code>
     *
     * @param Math_BigInteger $y
     * @return Math_BigInteger
     * @access public
     * @internal Performs base-2**52 addition
     */
    function add($y)
    {
        switch (MATH_BIGINTEGER_MODE) {
            case MATH_BIGINTEGER_MODE_GMP:
                $temp = new Math_BigInteger();
                $temp->value = gmp_add($this->value, $y->value);

                return $this->_normalize($temp);
            case MATH_BIGINTEGER_MODE_BCMATH:
                $temp = new Math_BigInteger();
                $temp->value = bcadd($this->value, $y->value, 0);

                return $this->_normalize($temp);
        }

        $temp = $this->_add($this->value, $this->is_negative, $y->value, $y->is_negative);

        $result = new Math_BigInteger();
        $result->value = $temp[MATH_BIGINTEGER_VALUE];
        $result->is_negative = $temp[MATH_BIGINTEGER_SIGN];

        return $this->_normalize($result);
    }

    /**
     * Performs addition.
     *
     * @param array $x_value
     * @param bool $x_negative
     * @param array $y_value
     * @param bool $y_negative
     * @return array
     * @access private
     */
    function _add($x_value, $x_negative, $y_value, $y_negative)
    {
        $x_size = count($x_value);
        $y_size = count($y_value);

        if ($x_size == 0) {
            return array(
                MATH_BIGINTEGER_VALUE => $y_value,
                MATH_BIGINTEGER_SIGN => $y_negative
            );
        } elseif ($y_size == 0) {
            return array(
                MATH_BIGINTEGER_VALUE => $x_value,
                MATH_BIGINTEGER_SIGN => $x_negative
            );
        }

        // subtract, if appropriate
        if ($x_negative != $y_negative) {
            if ($x_value == $y_value) {
                return array(
                    MATH_BIGINTEGER_VALUE => array(),
                    MATH_BIGINTEGER_SIGN => false
                );
            }

            $temp = $this->_subtract($x_value, false, $y_value, false);
            $temp[MATH_BIGINTEGER_SIGN] = $this->_compare($x_value, false, $y_value, false) > 0 ?
                                          $x_negative : $y_negative;

            return $temp;
        }

        if ($x_size < $y_size) {
            $size = $x_size;
            $value = $y_value;
        } else {
            $size = $y_size;
            $value = $x_value;
        }

        $value[count($value)] = 0; // just in case the carry adds an extra digit

        $carry = 0;
        for ($i = 0, $j = 1; $j < $size; $i+=2, $j+=2) {
            $sum = $x_value[$j] * MATH_BIGINTEGER_BASE_FULL + $x_value[$i] + $y_value[$j] * MATH_BIGINTEGER_BASE_FULL + $y_value[$i] + $carry;
            $carry = $sum >= MATH_BIGINTEGER_MAX_DIGIT2; // eg. floor($sum / 2**52); only possible values (in any base) are 0 and 1
            $sum = $carry ? $sum - MATH_BIGINTEGER_MAX_DIGIT2 : $sum;

            $temp = MATH_BIGINTEGER_BASE === 26 ? intval($sum / 0x4000000) : ($sum >> 31);

            $value[$i] = (int) ($sum - MATH_BIGINTEGER_BASE_FULL * $temp); // eg. a faster alternative to fmod($sum, 0x4000000)
            $value[$j] = $temp;
        }

        if ($j == $size) { // ie. if $y_size is odd
            $sum = $x_value[$i] + $y_value[$i] + $carry;
            $carry = $sum >= MATH_BIGINTEGER_BASE_FULL;
            $value[$i] = $carry ? $sum - MATH_BIGINTEGER_BASE_FULL : $sum;
            ++$i; // ie. let $i = $j since we've just done $value[$i]
        }

        if ($carry) {
            for (; $value[$i] == MATH_BIGINTEGER_MAX_DIGIT; ++$i) {
                $value[$i] = 0;
            }
            ++$value[$i];
        }

        return array(
            MATH_BIGINTEGER_VALUE => $this->_trim($value),
            MATH_BIGINTEGER_SIGN => $x_negative
        );
    }

    /**
     * Subtracts two BigIntegers.
     *
     * Here's an example:
     * <code>
     * <?php
     *    include 'Math/BigInteger.php';
     *
     *    $a = new Math_BigInteger('10');
     *    $b = new Math_BigInteger('20');
     *
     *    $c = $a->subtract($b);
     *
     *    echo $c->toString(); // outputs -10
     * ?>
     * </code>
     *
     * @param Math_BigInteger $y
     * @return Math_BigInteger
     * @access public
     * @internal Performs base-2**52 subtraction
     */
    function subtract($y)
    {
        switch (MATH_BIGINTEGER_MODE) {
            case MATH_BIGINTEGER_MODE_GMP:
                $temp = new Math_BigInteger();
                $temp->value = gmp_sub($this->value, $y->value);

                return $this->_normalize($temp);
            case MATH_BIGINTEGER_MODE_BCMATH:
                $temp = new Math_BigInteger();
                $temp->value = bcsub($this->value, $y->value, 0);

                return $this->_normalize($temp);
        }

        $temp = $this->_subtract($this->value, $this->is_negative, $y->value, $y->is_negative);

        $result = new Math_BigInteger();
        $result->value = $temp[MATH_BIGINTEGER_VALUE];
        $result->is_negative = $temp[MATH_BIGINTEGER_SIGN];

        return $this->_normalize($result);
    }

    /**
     * Performs subtraction.
     *
     * @param array $x_value
     * @param bool $x_negative
     * @param array $y_value
     * @param bool $y_negative
     * @return array
     * @access private
     */
    function _subtract($x_value, $x_negative, $y_value, $y_negative)
    {
        $x_size = count($x_value);
        $y_size = count($y_value);

        if ($x_size == 0) {
            return array(
                MATH_BIGINTEGER_VALUE => $y_value,
                MATH_BIGINTEGER_SIGN => !$y_negative
            );
        } elseif ($y_size == 0) {
            return array(
                MATH_BIGINTEGER_VALUE => $x_value,
                MATH_BIGINTEGER_SIGN => $x_negative
            );
        }

        // add, if appropriate (ie. -$x - +$y or +$x - -$y)
        if ($x_negative != $y_negative) {
            $temp = $this->_add($x_value, false, $y_value, false);
            $temp[MATH_BIGINTEGER_SIGN] = $x_negative;

            return $temp;
        }

        $diff = $this->_compare($x_value, $x_negative, $y_value, $y_negative);

        if (!$diff) {
            return array(
                MATH_BIGINTEGER_VALUE => array(),
                MATH_BIGINTEGER_SIGN => false
            );
        }

        // switch $x and $y around, if appropriate.
        if ((!$x_negative && $diff < 0) || ($x_negative && $diff > 0)) {
            $temp = $x_value;
            $x_value = $y_value;
            $y_value = $temp;

            $x_negative = !$x_negative;

            $x_size = count($x_value);
            $y_size = count($y_value);
        }

        // at this point, $x_value should be at least as big as - if not bigger than - $y_value

        $carry = 0;
        for ($i = 0, $j = 1; $j < $y_size; $i+=2, $j+=2) {
            $sum = $x_value[$j] * MATH_BIGINTEGER_BASE_FULL + $x_value[$i] - $y_value[$j] * MATH_BIGINTEGER_BASE_FULL - $y_value[$i] - $carry;
            $carry = $sum < 0; // eg. floor($sum / 2**52); only possible values (in any base) are 0 and 1
            $sum = $carry ? $sum + MATH_BIGINTEGER_MAX_DIGIT2 : $sum;

            $temp = MATH_BIGINTEGER_BASE === 26 ? intval($sum / 0x4000000) : ($sum >> 31);

            $x_value[$i] = (int) ($sum - MATH_BIGINTEGER_BASE_FULL * $temp);
            $x_value[$j] = $temp;
        }

        if ($j == $y_size) { // ie. if $y_size is odd
            $sum = $x_value[$i] - $y_value[$i] - $carry;
            $carry = $sum < 0;
            $x_value[$i] = $carry ? $sum + MATH_BIGINTEGER_BASE_FULL : $sum;
            ++$i;
        }

        if ($carry) {
            for (; !$x_value[$i]; ++$i) {
                $x_value[$i] = MATH_BIGINTEGER_MAX_DIGIT;
            }
            --$x_value[$i];
        }

        return array(
            MATH_BIGINTEGER_VALUE => $this->_trim($x_value),
            MATH_BIGINTEGER_SIGN => $x_negative
        );
    }

    /**
     * Multiplies two BigIntegers
     *
     * Here's an example:
     * <code>
     * <?php
     *    include 'Math/BigInteger.php';
     *
     *    $a = new Math_BigInteger('10');
     *    $b = new Math_BigInteger('20');
     *
     *    $c = $a->multiply($b);
     *
     *    echo $c->toString(); // outputs 200
     * ?>
     * </code>
     *
     * @param Math_BigInteger $x
     * @return Math_BigInteger
     * @access public
     */
    function multiply($x)
    {
        switch (MATH_BIGINTEGER_MODE) {
            case MATH_BIGINTEGER_MODE_GMP:
                $temp = new Math_BigInteger();
                $temp->value = gmp_mul($this->value, $x->value);

                return $this->_normalize($temp);
            case MATH_BIGINTEGER_MODE_BCMATH:
                $temp = new Math_BigInteger();
                $temp->value = bcmul($this->value, $x->value, 0);

                return $this->_normalize($temp);
        }

        $temp = $this->_multiply($this->value, $this->is_negative, $x->value, $x->is_negative);

        $product = new Math_BigInteger();
        $product->value = $temp[MATH_BIGINTEGER_VALUE];
        $product->is_negative = $temp[MATH_BIGINTEGER_SIGN];

        return $this->_normalize($product);
    }

    /**
     * Performs multiplication.
     *
     * @param array $x_value
     * @param bool $x_negative
     * @param array $y_value
     * @param bool $y_negative
     * @return array
     * @access private
     */
    function _multiply($x_value, $x_negative, $y_value, $y_negative)
    {
        //if ( $x_value == $y_value ) {
        //    return array(
        //        MATH_BIGINTEGER_VALUE => $this->_square($x_value),
        //        MATH_BIGINTEGER_SIGN => $x_sign != $y_value
        //    );
        //}

        $x_length = count($x_value);
        $y_length = count($y_value);

        if (!$x_length || !$y_length) { // a 0 is being multiplied
            return array(
                MATH_BIGINTEGER_VALUE => array(),
                MATH_BIGINTEGER_SIGN => false
            );
        }

        return array(
            MATH_BIGINTEGER_VALUE => min($x_length, $y_length) < 2 * MATH_BIGINTEGER_KARATSUBA_CUTOFF ?
                $this->_trim($this->_regularMultiply($x_value, $y_value)) :
                $this->_trim($this->_karatsuba($x_value, $y_value)),
            MATH_BIGINTEGER_SIGN => $x_negative != $y_negative
        );
    }

    /**
     * Performs long multiplication on two BigIntegers
     *
     * Modeled after 'multiply' in MutableBigInteger.java.
     *
     * @param array $x_value
     * @param array $y_value
     * @return array
     * @access private
     */
    function _regularMultiply($x_value, $y_value)
    {
        $x_length = count($x_value);
        $y_length = count($y_value);

        if (!$x_length || !$y_length) { // a 0 is being multiplied
            return array();
        }

        if ($x_length < $y_length) {
            $temp = $x_value;
            $x_value = $y_value;
            $y_value = $temp;

            $x_length = count($x_value);
            $y_length = count($y_value);
        }

        $product_value = $this->_array_repeat(0, $x_length + $y_length);

        // the following for loop could be removed if the for loop following it
        // (the one with nested for loops) initially set $i to 0, but
        // doing so would also make the result in one set of unnecessary adds,
        // since on the outermost loops first pass, $product->value[$k] is going
        // to always be 0

        $carry = 0;

        for ($j = 0; $j < $x_length; ++$j) { // ie. $i = 0
            $temp = $x_value[$j] * $y_value[0] + $carry; // $product_value[$k] == 0
            $carry = MATH_BIGINTEGER_BASE === 26 ? intval($temp / 0x4000000) : ($temp >> 31);
            $product_value[$j] = (int) ($temp - MATH_BIGINTEGER_BASE_FULL * $carry);
        }

        $product_value[$j] = $carry;

        // the above for loop is what the previous comment was talking about.  the
        // following for loop is the "one with nested for loops"
        for ($i = 1; $i < $y_length; ++$i) {
            $carry = 0;

            for ($j = 0, $k = $i; $j < $x_length; ++$j, ++$k) {
                $temp = $product_value[$k] + $x_value[$j] * $y_value[$i] + $carry;
                $carry = MATH_BIGINTEGER_BASE === 26 ? intval($temp / 0x4000000) : ($temp >> 31);
                $product_value[$k] = (int) ($temp - MATH_BIGINTEGER_BASE_FULL * $carry);
            }

            $product_value[$k] = $carry;
        }

        return $product_value;
    }

    /**
     * Performs Karatsuba multiplication on two BigIntegers
     *
     * See {@link http://en.wikipedia.org/wiki/Karatsuba_algorithm Karatsuba algorithm} and
     * {@link http://math.libtomcrypt.com/files/tommath.pdf#page=120 MPM 5.2.3}.
     *
     * @param array $x_value
     * @param array $y_value
     * @return array
     * @access private
     */
    function _karatsuba($x_value, $y_value)
    {
        $m = min(count($x_value) >> 1, count($y_value) >> 1);

        if ($m < MATH_BIGINTEGER_KARATSUBA_CUTOFF) {
            return $this->_regularMultiply($x_value, $y_value);
        }

        $x1 = array_slice($x_value, $m);
        $x0 = array_slice($x_value, 0, $m);
        $y1 = array_slice($y_value, $m);
        $y0 = array_slice($y_value, 0, $m);

        $z2 = $this->_karatsuba($x1, $y1);
        $z0 = $this->_karatsuba($x0, $y0);

        $z1 = $this->_add($x1, false, $x0, false);
        $temp = $this->_add($y1, false, $y0, false);
        $z1 = $this->_karatsuba($z1[MATH_BIGINTEGER_VALUE], $temp[MATH_BIGINTEGER_VALUE]);
        $temp = $this->_add($z2, false, $z0, false);
        $z1 = $this->_subtract($z1, false, $temp[MATH_BIGINTEGER_VALUE], false);

        $z2 = array_merge(array_fill(0, 2 * $m, 0), $z2);
        $z1[MATH_BIGINTEGER_VALUE] = array_merge(array_fill(0, $m, 0), $z1[MATH_BIGINTEGER_VALUE]);

        $xy = $this->_add($z2, false, $z1[MATH_BIGINTEGER_VALUE], $z1[MATH_BIGINTEGER_SIGN]);
        $xy = $this->_add($xy[MATH_BIGINTEGER_VALUE], $xy[MATH_BIGINTEGER_SIGN], $z0, false);

        return $xy[MATH_BIGINTEGER_VALUE];
    }

    /**
     * Performs squaring
     *
     * @param array $x
     * @return array
     * @access private
     */
    function _square($x = false)
    {
        return count($x) < 2 * MATH_BIGINTEGER_KARATSUBA_CUTOFF ?
            $this->_trim($this->_baseSquare($x)) :
            $this->_trim($this->_karatsubaSquare($x));
    }

    /**
     * Performs traditional squaring on two BigIntegers
     *
     * Squaring can be done faster than multiplying a number by itself can be.  See
     * {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=7 HAC 14.2.4} /
     * {@link http://math.libtomcrypt.com/files/tommath.pdf#page=141 MPM 5.3} for more information.
     *
     * @param array $value
     * @return array
     * @access private
     */
    function _baseSquare($value)
    {
        if (empty($value)) {
            return array();
        }
        $square_value = $this->_array_repeat(0, 2 * count($value));

        for ($i = 0, $max_index = count($value) - 1; $i <= $max_index; ++$i) {
            $i2 = $i << 1;

            $temp = $square_value[$i2] + $value[$i] * $value[$i];
            $carry = MATH_BIGINTEGER_BASE === 26 ? intval($temp / 0x4000000) : ($temp >> 31);
            $square_value[$i2] = (int) ($temp - MATH_BIGINTEGER_BASE_FULL * $carry);

            // note how we start from $i+1 instead of 0 as we do in multiplication.
            for ($j = $i + 1, $k = $i2 + 1; $j <= $max_index; ++$j, ++$k) {
                $temp = $square_value[$k] + 2 * $value[$j] * $value[$i] + $carry;
                $carry = MATH_BIGINTEGER_BASE === 26 ? intval($temp / 0x4000000) : ($temp >> 31);
                $square_value[$k] = (int) ($temp - MATH_BIGINTEGER_BASE_FULL * $carry);
            }

            // the following line can yield values larger 2**15.  at this point, PHP should switch
            // over to floats.
            $square_value[$i + $max_index + 1] = $carry;
        }

        return $square_value;
    }

    /**
     * Performs Karatsuba "squaring" on two BigIntegers
     *
     * See {@link http://en.wikipedia.org/wiki/Karatsuba_algorithm Karatsuba algorithm} and
     * {@link http://math.libtomcrypt.com/files/tommath.pdf#page=151 MPM 5.3.4}.
     *
     * @param array $value
     * @return array
     * @access private
     */
    function _karatsubaSquare($value)
    {
        $m = count($value) >> 1;

        if ($m < MATH_BIGINTEGER_KARATSUBA_CUTOFF) {
            return $this->_baseSquare($value);
        }

        $x1 = array_slice($value, $m);
        $x0 = array_slice($value, 0, $m);

        $z2 = $this->_karatsubaSquare($x1);
        $z0 = $this->_karatsubaSquare($x0);

        $z1 = $this->_add($x1, false, $x0, false);
        $z1 = $this->_karatsubaSquare($z1[MATH_BIGINTEGER_VALUE]);
        $temp = $this->_add($z2, false, $z0, false);
        $z1 = $this->_subtract($z1, false, $temp[MATH_BIGINTEGER_VALUE], false);

        $z2 = array_merge(array_fill(0, 2 * $m, 0), $z2);
        $z1[MATH_BIGINTEGER_VALUE] = array_merge(array_fill(0, $m, 0), $z1[MATH_BIGINTEGER_VALUE]);

        $xx = $this->_add($z2, false, $z1[MATH_BIGINTEGER_VALUE], $z1[MATH_BIGINTEGER_SIGN]);
        $xx = $this->_add($xx[MATH_BIGINTEGER_VALUE], $xx[MATH_BIGINTEGER_SIGN], $z0, false);

        return $xx[MATH_BIGINTEGER_VALUE];
    }

    /**
     * Divides two BigIntegers.
     *
     * Returns an array whose first element contains the quotient and whose second element contains the
     * "common residue".  If the remainder would be positive, the "common residue" and the remainder are the
     * same.  If the remainder would be negative, the "common residue" is equal to the sum of the remainder
     * and the divisor (basically, the "common residue" is the first positive modulo).
     *
     * Here's an example:
     * <code>
     * <?php
     *    include 'Math/BigInteger.php';
     *
     *    $a = new Math_BigInteger('10');
     *    $b = new Math_BigInteger('20');
     *
     *    list($quotient, $remainder) = $a->divide($b);
     *
     *    echo $quotient->toString(); // outputs 0
     *    echo "\r\n";
     *    echo $remainder->toString(); // outputs 10
     * ?>
     * </code>
     *
     * @param Math_BigInteger $y
     * @return array
     * @access public
     * @internal This function is based off of {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=9 HAC 14.20}.
     */
    function divide($y)
    {
        switch (MATH_BIGINTEGER_MODE) {
            case MATH_BIGINTEGER_MODE_GMP:
                $quotient = new Math_BigInteger();
                $remainder = new Math_BigInteger();

                list($quotient->value, $remainder->value) = gmp_div_qr($this->value, $y->value);

                if (gmp_sign($remainder->value) < 0) {
                    $remainder->value = gmp_add($remainder->value, gmp_abs($y->value));
                }

                return array($this->_normalize($quotient), $this->_normalize($remainder));
            case MATH_BIGINTEGER_MODE_BCMATH:
                $quotient = new Math_BigInteger();
                $remainder = new Math_BigInteger();

                $quotient->value = bcdiv($this->value, $y->value, 0);
                $remainder->value = bcmod($this->value, $y->value);

                if ($remainder->value[0] == '-') {
                    $remainder->value = bcadd($remainder->value, $y->value[0] == '-' ? substr($y->value, 1) : $y->value, 0);
                }

                return array($this->_normalize($quotient), $this->_normalize($remainder));
        }

        if (count($y->value) == 1) {
            list($q, $r) = $this->_divide_digit($this->value, $y->value[0]);
            $quotient = new Math_BigInteger();
            $remainder = new Math_BigInteger();
            $quotient->value = $q;
            $remainder->value = array($r);
            $quotient->is_negative = $this->is_negative != $y->is_negative;
            return array($this->_normalize($quotient), $this->_normalize($remainder));
        }

        static $zero;
        if (!isset($zero)) {
            $zero = new Math_BigInteger();
        }

        $x = $this->copy();
        $y = $y->copy();

        $x_sign = $x->is_negative;
        $y_sign = $y->is_negative;

        $x->is_negative = $y->is_negative = false;

        $diff = $x->compare($y);

        if (!$diff) {
            $temp = new Math_BigInteger();
            $temp->value = array(1);
            $temp->is_negative = $x_sign != $y_sign;
            return array($this->_normalize($temp), $this->_normalize(new Math_BigInteger()));
        }

        if ($diff < 0) {
            // if $x is negative, "add" $y.
            if ($x_sign) {
                $x = $y->subtract($x);
            }
            return array($this->_normalize(new Math_BigInteger()), $this->_normalize($x));
        }

        // normalize $x and $y as described in HAC 14.23 / 14.24
        $msb = $y->value[count($y->value) - 1];
        for ($shift = 0; !($msb & MATH_BIGINTEGER_MSB); ++$shift) {
            $msb <<= 1;
        }
        $x->_lshift($shift);
        $y->_lshift($shift);
        $y_value = &$y->value;

        $x_max = count($x->value) - 1;
        $y_max = count($y->value) - 1;

        $quotient = new Math_BigInteger();
        $quotient_value = &$quotient->value;
        $quotient_value = $this->_array_repeat(0, $x_max - $y_max + 1);

        static $temp, $lhs, $rhs;
        if (!isset($temp)) {
            $temp = new Math_BigInteger();
            $lhs =  new Math_BigInteger();
            $rhs =  new Math_BigInteger();
        }
        $temp_value = &$temp->value;
        $rhs_value =  &$rhs->value;

        // $temp = $y << ($x_max - $y_max-1) in base 2**26
        $temp_value = array_merge($this->_array_repeat(0, $x_max - $y_max), $y_value);

        while ($x->compare($temp) >= 0) {
            // calculate the "common residue"
            ++$quotient_value[$x_max - $y_max];
            $x = $x->subtract($temp);
            $x_max = count($x->value) - 1;
        }

        for ($i = $x_max; $i >= $y_max + 1; --$i) {
            $x_value = &$x->value;
            $x_window = array(
                isset($x_value[$i]) ? $x_value[$i] : 0,
                isset($x_value[$i - 1]) ? $x_value[$i - 1] : 0,
                isset($x_value[$i - 2]) ? $x_value[$i - 2] : 0
            );
            $y_window = array(
                $y_value[$y_max],
                ($y_max > 0) ? $y_value[$y_max - 1] : 0
            );

            $q_index = $i - $y_max - 1;
            if ($x_window[0] == $y_window[0]) {
                $quotient_value[$q_index] = MATH_BIGINTEGER_MAX_DIGIT;
            } else {
                $quotient_value[$q_index] = $this->_safe_divide(
                    $x_window[0] * MATH_BIGINTEGER_BASE_FULL + $x_window[1],
                    $y_window[0]
                );
            }

            $temp_value = array($y_window[1], $y_window[0]);

            $lhs->value = array($quotient_value[$q_index]);
            $lhs = $lhs->multiply($temp);

            $rhs_value = array($x_window[2], $x_window[1], $x_window[0]);

            while ($lhs->compare($rhs) > 0) {
                --$quotient_value[$q_index];

                $lhs->value = array($quotient_value[$q_index]);
                $lhs = $lhs->multiply($temp);
            }

            $adjust = $this->_array_repeat(0, $q_index);
            $temp_value = array($quotient_value[$q_index]);
            $temp = $temp->multiply($y);
            $temp_value = &$temp->value;
            $temp_value = array_merge($adjust, $temp_value);

            $x = $x->subtract($temp);

            if ($x->compare($zero) < 0) {
                $temp_value = array_merge($adjust, $y_value);
                $x = $x->add($temp);

                --$quotient_value[$q_index];
            }

            $x_max = count($x_value) - 1;
        }

        // unnormalize the remainder
        $x->_rshift($shift);

        $quotient->is_negative = $x_sign != $y_sign;

        // calculate the "common residue", if appropriate
        if ($x_sign) {
            $y->_rshift($shift);
            $x = $y->subtract($x);
        }

        return array($this->_normalize($quotient), $this->_normalize($x));
    }

    /**
     * Divides a BigInteger by a regular integer
     *
     * abc / x = a00 / x + b0 / x + c / x
     *
     * @param array $dividend
     * @param array $divisor
     * @return array
     * @access private
     */
    function _divide_digit($dividend, $divisor)
    {
        $carry = 0;
        $result = array();

        for ($i = count($dividend) - 1; $i >= 0; --$i) {
            $temp = MATH_BIGINTEGER_BASE_FULL * $carry + $dividend[$i];
            $result[$i] = $this->_safe_divide($temp, $divisor);
            $carry = (int) ($temp - $divisor * $result[$i]);
        }

        return array($result, $carry);
    }

    /**
     * Performs modular exponentiation.
     *
     * Here's an example:
     * <code>
     * <?php
     *    include 'Math/BigInteger.php';
     *
     *    $a = new Math_BigInteger('10');
     *    $b = new Math_BigInteger('20');
     *    $c = new Math_BigInteger('30');
     *
     *    $c = $a->modPow($b, $c);
     *
     *    echo $c->toString(); // outputs 10
     * ?>
     * </code>
     *
     * @param Math_BigInteger $e
     * @param Math_BigInteger $n
     * @return Math_BigInteger
     * @access public
     * @internal The most naive approach to modular exponentiation has very unreasonable requirements, and
     *    and although the approach involving repeated squaring does vastly better, it, too, is impractical
     *    for our purposes.  The reason being that division - by far the most complicated and time-consuming
     *    of the basic operations (eg. +,-,*,/) - occurs multiple times within it.
     *
     *    Modular reductions resolve this issue.  Although an individual modular reduction takes more time
     *    then an individual division, when performed in succession (with the same modulo), they're a lot faster.
     *
     *    The two most commonly used modular reductions are Barrett and Montgomery reduction.  Montgomery reduction,
     *    although faster, only works when the gcd of the modulo and of the base being used is 1.  In RSA, when the
     *    base is a power of two, the modulo - a product of two primes - is always going to have a gcd of 1 (because
     *    the product of two odd numbers is odd), but what about when RSA isn't used?
     *
     *    In contrast, Barrett reduction has no such constraint.  As such, some bigint implementations perform a
     *    Barrett reduction after every operation in the modpow function.  Others perform Barrett reductions when the
     *    modulo is even and Montgomery reductions when the modulo is odd.  BigInteger.java's modPow method, however,
     *    uses a trick involving the Chinese Remainder Theorem to factor the even modulo into two numbers - one odd and
     *    the other, a power of two - and recombine them, later.  This is the method that this modPow function uses.
     *    {@link http://islab.oregonstate.edu/papers/j34monex.pdf Montgomery Reduction with Even Modulus} elaborates.
     */
    function modPow($e, $n)
    {
        $n = $this->bitmask !== false && $this->bitmask->compare($n) < 0 ? $this->bitmask : $n->abs();

        if ($e->compare(new Math_BigInteger()) < 0) {
            $e = $e->abs();

            $temp = $this->modInverse($n);
            if ($temp === false) {
                return false;
            }

            return $this->_normalize($temp->modPow($e, $n));
        }

        if (MATH_BIGINTEGER_MODE == MATH_BIGINTEGER_MODE_GMP) {
            $temp = new Math_BigInteger();
            $temp->value = gmp_powm($this->value, $e->value, $n->value);

            return $this->_normalize($temp);
        }

        if ($this->compare(new Math_BigInteger()) < 0 || $this->compare($n) > 0) {
            list(, $temp) = $this->divide($n);
            return $temp->modPow($e, $n);
        }

        if (defined('MATH_BIGINTEGER_OPENSSL_ENABLED')) {
            $components = array(
                'modulus' => $n->toBytes(true),
                'publicExponent' => $e->toBytes(true)
            );

            $components = array(
                'modulus' => pack('Ca*a*', 2, $this->_encodeASN1Length(strlen($components['modulus'])), $components['modulus']),
                'publicExponent' => pack('Ca*a*', 2, $this->_encodeASN1Length(strlen($components['publicExponent'])), $components['publicExponent'])
            );

            $RSAPublicKey = pack(
                'Ca*a*a*',
                48,
                $this->_encodeASN1Length(strlen($components['modulus']) + strlen($components['publicExponent'])),
                $components['modulus'],
                $components['publicExponent']
            );

            $rsaOID = pack('H*', '300d06092a864886f70d0101010500'); // hex version of MA0GCSqGSIb3DQEBAQUA
            $RSAPublicKey = chr(0) . $RSAPublicKey;
            $RSAPublicKey = chr(3) . $this->_encodeASN1Length(strlen($RSAPublicKey)) . $RSAPublicKey;

            $encapsulated = pack(
                'Ca*a*',
                48,
                $this->_encodeASN1Length(strlen($rsaOID . $RSAPublicKey)),
                $rsaOID . $RSAPublicKey
            );

            $RSAPublicKey = "-----BEGIN PUBLIC KEY-----\r\n" .
                             chunk_split(base64_encode($encapsulated)) .
                             '-----END PUBLIC KEY-----';

            $plaintext = str_pad($this->toBytes(), strlen($n->toBytes(true)) - 1, "\0", STR_PAD_LEFT);

            if (openssl_public_encrypt($plaintext, $result, $RSAPublicKey, OPENSSL_NO_PADDING)) {
                return new Math_BigInteger($result, 256);
            }
        }

        if (MATH_BIGINTEGER_MODE == MATH_BIGINTEGER_MODE_BCMATH) {
            $temp = new Math_BigInteger();
            $temp->value = bcpowmod($this->value, $e->value, $n->value, 0);

            return $this->_normalize($temp);
        }

        if (empty($e->value)) {
            $temp = new Math_BigInteger();
            $temp->value = array(1);
            return $this->_normalize($temp);
        }

        if ($e->value == array(1)) {
            list(, $temp) = $this->divide($n);
            return $this->_normalize($temp);
        }

        if ($e->value == array(2)) {
            $temp = new Math_BigInteger();
            $temp->value = $this->_square($this->value);
            list(, $temp) = $temp->divide($n);
            return $this->_normalize($temp);
        }

        return $this->_normalize($this->_slidingWindow($e, $n, MATH_BIGINTEGER_BARRETT));

        // the following code, although not callable, can be run independently of the above code
        // although the above code performed better in my benchmarks the following could might
        // perform better under different circumstances. in lieu of deleting it it's just been
        // made uncallable

        // is the modulo odd?
        if ($n->value[0] & 1) {
            return $this->_normalize($this->_slidingWindow($e, $n, MATH_BIGINTEGER_MONTGOMERY));
        }
        // if it's not, it's even

        // find the lowest set bit (eg. the max pow of 2 that divides $n)
        for ($i = 0; $i < count($n->value); ++$i) {
            if ($n->value[$i]) {
                $temp = decbin($n->value[$i]);
                $j = strlen($temp) - strrpos($temp, '1') - 1;
                $j+= 26 * $i;
                break;
            }
        }
        // at this point, 2^$j * $n/(2^$j) == $n

        $mod1 = $n->copy();
        $mod1->_rshift($j);
        $mod2 = new Math_BigInteger();
        $mod2->value = array(1);
        $mod2->_lshift($j);

        $part1 = ($mod1->value != array(1)) ? $this->_slidingWindow($e, $mod1, MATH_BIGINTEGER_MONTGOMERY) : new Math_BigInteger();
        $part2 = $this->_slidingWindow($e, $mod2, MATH_BIGINTEGER_POWEROF2);

        $y1 = $mod2->modInverse($mod1);
        $y2 = $mod1->modInverse($mod2);

        $result = $part1->multiply($mod2);
        $result = $result->multiply($y1);

        $temp = $part2->multiply($mod1);
        $temp = $temp->multiply($y2);

        $result = $result->add($temp);
        list(, $result) = $result->divide($n);

        return $this->_normalize($result);
    }

    /**
     * Performs modular exponentiation.
     *
     * Alias for Math_BigInteger::modPow()
     *
     * @param Math_BigInteger $e
     * @param Math_BigInteger $n
     * @return Math_BigInteger
     * @access public
     */
    function powMod($e, $n)
    {
        return $this->modPow($e, $n);
    }

    /**
     * Sliding Window k-ary Modular Exponentiation
     *
     * Based on {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=27 HAC 14.85} /
     * {@link http://math.libtomcrypt.com/files/tommath.pdf#page=210 MPM 7.7}.  In a departure from those algorithims,
     * however, this function performs a modular reduction after every multiplication and squaring operation.
     * As such, this function has the same preconditions that the reductions being used do.
     *
     * @param Math_BigInteger $e
     * @param Math_BigInteger $n
     * @param int $mode
     * @return Math_BigInteger
     * @access private
     */
    function _slidingWindow($e, $n, $mode)
    {
        static $window_ranges = array(7, 25, 81, 241, 673, 1793); // from BigInteger.java's oddModPow function
        //static $window_ranges = array(0, 7, 36, 140, 450, 1303, 3529); // from MPM 7.3.1

        $e_value = $e->value;
        $e_length = count($e_value) - 1;
        $e_bits = decbin($e_value[$e_length]);
        for ($i = $e_length - 1; $i >= 0; --$i) {
            $e_bits.= str_pad(decbin($e_value[$i]), MATH_BIGINTEGER_BASE, '0', STR_PAD_LEFT);
        }

        $e_length = strlen($e_bits);

        // calculate the appropriate window size.
        // $window_size == 3 if $window_ranges is between 25 and 81, for example.
        for ($i = 0, $window_size = 1; $i < count($window_ranges) && $e_length > $window_ranges[$i]; ++$window_size, ++$i) {
        }

        $n_value = $n->value;

        // precompute $this^0 through $this^$window_size
        $powers = array();
        $powers[1] = $this->_prepareReduce($this->value, $n_value, $mode);
        $powers[2] = $this->_squareReduce($powers[1], $n_value, $mode);

        // we do every other number since substr($e_bits, $i, $j+1) (see below) is supposed to end
        // in a 1.  ie. it's supposed to be odd.
        $temp = 1 << ($window_size - 1);
        for ($i = 1; $i < $temp; ++$i) {
            $i2 = $i << 1;
            $powers[$i2 + 1] = $this->_multiplyReduce($powers[$i2 - 1], $powers[2], $n_value, $mode);
        }

        $result = array(1);
        $result = $this->_prepareReduce($result, $n_value, $mode);

        for ($i = 0; $i < $e_length;) {
            if (!$e_bits[$i]) {
                $result = $this->_squareReduce($result, $n_value, $mode);
                ++$i;
            } else {
                for ($j = $window_size - 1; $j > 0; --$j) {
                    if (!empty($e_bits[$i + $j])) {
                        break;
                    }
                }

                // eg. the length of substr($e_bits, $i, $j + 1)
                for ($k = 0; $k <= $j; ++$k) {
                    $result = $this->_squareReduce($result, $n_value, $mode);
                }

                $result = $this->_multiplyReduce($result, $powers[bindec(substr($e_bits, $i, $j + 1))], $n_value, $mode);

                $i += $j + 1;
            }
        }

        $temp = new Math_BigInteger();
        $temp->value = $this->_reduce($result, $n_value, $mode);

        return $temp;
    }

    /**
     * Modular reduction
     *
     * For most $modes this will return the remainder.
     *
     * @see self::_slidingWindow()
     * @access private
     * @param array $x
     * @param array $n
     * @param int $mode
     * @return array
     */
    function _reduce($x, $n, $mode)
    {
        switch ($mode) {
            case MATH_BIGINTEGER_MONTGOMERY:
                return $this->_montgomery($x, $n);
            case MATH_BIGINTEGER_BARRETT:
                return $this->_barrett($x, $n);
            case MATH_BIGINTEGER_POWEROF2:
                $lhs = new Math_BigInteger();
                $lhs->value = $x;
                $rhs = new Math_BigInteger();
                $rhs->value = $n;
                return $x->_mod2($n);
            case MATH_BIGINTEGER_CLASSIC:
                $lhs = new Math_BigInteger();
                $lhs->value = $x;
                $rhs = new Math_BigInteger();
                $rhs->value = $n;
                list(, $temp) = $lhs->divide($rhs);
                return $temp->value;
            case MATH_BIGINTEGER_NONE:
                return $x;
            default:
                // an invalid $mode was provided
        }
    }

    /**
     * Modular reduction preperation
     *
     * @see self::_slidingWindow()
     * @access private
     * @param array $x
     * @param array $n
     * @param int $mode
     * @return array
     */
    function _prepareReduce($x, $n, $mode)
    {
        if ($mode == MATH_BIGINTEGER_MONTGOMERY) {
            return $this->_prepMontgomery($x, $n);
        }
        return $this->_reduce($x, $n, $mode);
    }

    /**
     * Modular multiply
     *
     * @see self::_slidingWindow()
     * @access private
     * @param array $x
     * @param array $y
     * @param array $n
     * @param int $mode
     * @return array
     */
    function _multiplyReduce($x, $y, $n, $mode)
    {
        if ($mode == MATH_BIGINTEGER_MONTGOMERY) {
            return $this->_montgomeryMultiply($x, $y, $n);
        }
        $temp = $this->_multiply($x, false, $y, false);
        return $this->_reduce($temp[MATH_BIGINTEGER_VALUE], $n, $mode);
    }

    /**
     * Modular square
     *
     * @see self::_slidingWindow()
     * @access private
     * @param array $x
     * @param array $n
     * @param int $mode
     * @return array
     */
    function _squareReduce($x, $n, $mode)
    {
        if ($mode == MATH_BIGINTEGER_MONTGOMERY) {
            return $this->_montgomeryMultiply($x, $x, $n);
        }
        return $this->_reduce($this->_square($x), $n, $mode);
    }

    /**
     * Modulos for Powers of Two
     *
     * Calculates $x%$n, where $n = 2**$e, for some $e.  Since this is basically the same as doing $x & ($n-1),
     * we'll just use this function as a wrapper for doing that.
     *
     * @see self::_slidingWindow()
     * @access private
     * @param Math_BigInteger
     * @return Math_BigInteger
     */
    function _mod2($n)
    {
        $temp = new Math_BigInteger();
        $temp->value = array(1);
        return $this->bitwise_and($n->subtract($temp));
    }

    /**
     * Barrett Modular Reduction
     *
     * See {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=14 HAC 14.3.3} /
     * {@link http://math.libtomcrypt.com/files/tommath.pdf#page=165 MPM 6.2.5} for more information.  Modified slightly,
     * so as not to require negative numbers (initially, this script didn't support negative numbers).
     *
     * Employs "folding", as described at
     * {@link http://www.cosic.esat.kuleuven.be/publications/thesis-149.pdf#page=66 thesis-149.pdf#page=66}.  To quote from
     * it, "the idea [behind folding] is to find a value x' such that x (mod m) = x' (mod m), with x' being smaller than x."
     *
     * Unfortunately, the "Barrett Reduction with Folding" algorithm described in thesis-149.pdf is not, as written, all that
     * usable on account of (1) its not using reasonable radix points as discussed in
     * {@link http://math.libtomcrypt.com/files/tommath.pdf#page=162 MPM 6.2.2} and (2) the fact that, even with reasonable
     * radix points, it only works when there are an even number of digits in the denominator.  The reason for (2) is that
     * (x >> 1) + (x >> 1) != x / 2 + x / 2.  If x is even, they're the same, but if x is odd, they're not.  See the in-line
     * comments for details.
     *
     * @see self::_slidingWindow()
     * @access private
     * @param array $n
     * @param array $m
     * @return array
     */
    function _barrett($n, $m)
    {
        static $cache = array(
            MATH_BIGINTEGER_VARIABLE => array(),
            MATH_BIGINTEGER_DATA => array()
        );

        $m_length = count($m);

        // if ($this->_compare($n, $this->_square($m)) >= 0) {
        if (count($n) > 2 * $m_length) {
            $lhs = new Math_BigInteger();
            $rhs = new Math_BigInteger();
            $lhs->value = $n;
            $rhs->value = $m;
            list(, $temp) = $lhs->divide($rhs);
            return $temp->value;
        }

        // if (m.length >> 1) + 2 <= m.length then m is too small and n can't be reduced
        if ($m_length < 5) {
            return $this->_regularBarrett($n, $m);
        }

        // n = 2 * m.length

        if (($key = array_search($m, $cache[MATH_BIGINTEGER_VARIABLE])) === false) {
            $key = count($cache[MATH_BIGINTEGER_VARIABLE]);
            $cache[MATH_BIGINTEGER_VARIABLE][] = $m;

            $lhs = new Math_BigInteger();
            $lhs_value = &$lhs->value;
            $lhs_value = $this->_array_repeat(0, $m_length + ($m_length >> 1));
            $lhs_value[] = 1;
            $rhs = new Math_BigInteger();
            $rhs->value = $m;

            list($u, $m1) = $lhs->divide($rhs);
            $u = $u->value;
            $m1 = $m1->value;

            $cache[MATH_BIGINTEGER_DATA][] = array(
                'u' => $u, // m.length >> 1 (technically (m.length >> 1) + 1)
                'm1'=> $m1 // m.length
            );
        } else {
            extract($cache[MATH_BIGINTEGER_DATA][$key]);
        }

        $cutoff = $m_length + ($m_length >> 1);
        $lsd = array_slice($n, 0, $cutoff); // m.length + (m.length >> 1)
        $msd = array_slice($n, $cutoff);    // m.length >> 1
        $lsd = $this->_trim($lsd);
        $temp = $this->_multiply($msd, false, $m1, false);
        $n = $this->_add($lsd, false, $temp[MATH_BIGINTEGER_VALUE], false); // m.length + (m.length >> 1) + 1

        if ($m_length & 1) {
            return $this->_regularBarrett($n[MATH_BIGINTEGER_VALUE], $m);
        }

        // (m.length + (m.length >> 1) + 1) - (m.length - 1) == (m.length >> 1) + 2
        $temp = array_slice($n[MATH_BIGINTEGER_VALUE], $m_length - 1);
        // if even: ((m.length >> 1) + 2) + (m.length >> 1) == m.length + 2
        // if odd:  ((m.length >> 1) + 2) + (m.length >> 1) == (m.length - 1) + 2 == m.length + 1
        $temp = $this->_multiply($temp, false, $u, false);
        // if even: (m.length + 2) - ((m.length >> 1) + 1) = m.length - (m.length >> 1) + 1
        // if odd:  (m.length + 1) - ((m.length >> 1) + 1) = m.length - (m.length >> 1)
        $temp = array_slice($temp[MATH_BIGINTEGER_VALUE], ($m_length >> 1) + 1);
        // if even: (m.length - (m.length >> 1) + 1) + m.length = 2 * m.length - (m.length >> 1) + 1
        // if odd:  (m.length - (m.length >> 1)) + m.length     = 2 * m.length - (m.length >> 1)
        $temp = $this->_multiply($temp, false, $m, false);

        // at this point, if m had an odd number of digits, we'd be subtracting a 2 * m.length - (m.length >> 1) digit
        // number from a m.length + (m.length >> 1) + 1 digit number.  ie. there'd be an extra digit and the while loop
        // following this comment would loop a lot (hence our calling _regularBarrett() in that situation).

        $result = $this->_subtract($n[MATH_BIGINTEGER_VALUE], false, $temp[MATH_BIGINTEGER_VALUE], false);

        while ($this->_compare($result[MATH_BIGINTEGER_VALUE], $result[MATH_BIGINTEGER_SIGN], $m, false) >= 0) {
            $result = $this->_subtract($result[MATH_BIGINTEGER_VALUE], $result[MATH_BIGINTEGER_SIGN], $m, false);
        }

        return $result[MATH_BIGINTEGER_VALUE];
    }

    /**
     * (Regular) Barrett Modular Reduction
     *
     * For numbers with more than four digits Math_BigInteger::_barrett() is faster.  The difference between that and this
     * is that this function does not fold the denominator into a smaller form.
     *
     * @see self::_slidingWindow()
     * @access private
     * @param array $x
     * @param array $n
     * @return array
     */
    function _regularBarrett($x, $n)
    {
        static $cache = array(
            MATH_BIGINTEGER_VARIABLE => array(),
            MATH_BIGINTEGER_DATA => array()
        );

        $n_length = count($n);

        if (count($x) > 2 * $n_length) {
            $lhs = new Math_BigInteger();
            $rhs = new Math_BigInteger();
            $lhs->value = $x;
            $rhs->value = $n;
            list(, $temp) = $lhs->divide($rhs);
            return $temp->value;
        }

        if (($key = array_search($n, $cache[MATH_BIGINTEGER_VARIABLE])) === false) {
            $key = count($cache[MATH_BIGINTEGER_VARIABLE]);
            $cache[MATH_BIGINTEGER_VARIABLE][] = $n;
            $lhs = new Math_BigInteger();
            $lhs_value = &$lhs->value;
            $lhs_value = $this->_array_repeat(0, 2 * $n_length);
            $lhs_value[] = 1;
            $rhs = new Math_BigInteger();
            $rhs->value = $n;
            list($temp, ) = $lhs->divide($rhs); // m.length
            $cache[MATH_BIGINTEGER_DATA][] = $temp->value;
        }

        // 2 * m.length - (m.length - 1) = m.length + 1
        $temp = array_slice($x, $n_length - 1);
        // (m.length + 1) + m.length = 2 * m.length + 1
        $temp = $this->_multiply($temp, false, $cache[MATH_BIGINTEGER_DATA][$key], false);
        // (2 * m.length + 1) - (m.length - 1) = m.length + 2
        $temp = array_slice($temp[MATH_BIGINTEGER_VALUE], $n_length + 1);

        // m.length + 1
        $result = array_slice($x, 0, $n_length + 1);
        // m.length + 1
        $temp = $this->_multiplyLower($temp, false, $n, false, $n_length + 1);
        // $temp == array_slice($temp->_multiply($temp, false, $n, false)->value, 0, $n_length + 1)

        if ($this->_compare($result, false, $temp[MATH_BIGINTEGER_VALUE], $temp[MATH_BIGINTEGER_SIGN]) < 0) {
            $corrector_value = $this->_array_repeat(0, $n_length + 1);
            $corrector_value[count($corrector_value)] = 1;
            $result = $this->_add($result, false, $corrector_value, false);
            $result = $result[MATH_BIGINTEGER_VALUE];
        }

        // at this point, we're subtracting a number with m.length + 1 digits from another number with m.length + 1 digits
        $result = $this->_subtract($result, false, $temp[MATH_BIGINTEGER_VALUE], $temp[MATH_BIGINTEGER_SIGN]);
        while ($this->_compare($result[MATH_BIGINTEGER_VALUE], $result[MATH_BIGINTEGER_SIGN], $n, false) > 0) {
            $result = $this->_subtract($result[MATH_BIGINTEGER_VALUE], $result[MATH_BIGINTEGER_SIGN], $n, false);
        }

        return $result[MATH_BIGINTEGER_VALUE];
    }

    /**
     * Performs long multiplication up to $stop digits
     *
     * If you're going to be doing array_slice($product->value, 0, $stop), some cycles can be saved.
     *
     * @see self::_regularBarrett()
     * @param array $x_value
     * @param bool $x_negative
     * @param array $y_value
     * @param bool $y_negative
     * @param int $stop
     * @return array
     * @access private
     */
    function _multiplyLower($x_value, $x_negative, $y_value, $y_negative, $stop)
    {
        $x_length = count($x_value);
        $y_length = count($y_value);

        if (!$x_length || !$y_length) { // a 0 is being multiplied
            return array(
                MATH_BIGINTEGER_VALUE => array(),
                MATH_BIGINTEGER_SIGN => false
            );
        }

        if ($x_length < $y_length) {
            $temp = $x_value;
            $x_value = $y_value;
            $y_value = $temp;

            $x_length = count($x_value);
            $y_length = count($y_value);
        }

        $product_value = $this->_array_repeat(0, $x_length + $y_length);

        // the following for loop could be removed if the for loop following it
        // (the one with nested for loops) initially set $i to 0, but
        // doing so would also make the result in one set of unnecessary adds,
        // since on the outermost loops first pass, $product->value[$k] is going
        // to always be 0

        $carry = 0;

        for ($j = 0; $j < $x_length; ++$j) { // ie. $i = 0, $k = $i
            $temp = $x_value[$j] * $y_value[0] + $carry; // $product_value[$k] == 0
            $carry = MATH_BIGINTEGER_BASE === 26 ? intval($temp / 0x4000000) : ($temp >> 31);
            $product_value[$j] = (int) ($temp - MATH_BIGINTEGER_BASE_FULL * $carry);
        }

        if ($j < $stop) {
            $product_value[$j] = $carry;
        }

        // the above for loop is what the previous comment was talking about.  the
        // following for loop is the "one with nested for loops"

        for ($i = 1; $i < $y_length; ++$i) {
            $carry = 0;

            for ($j = 0, $k = $i; $j < $x_length && $k < $stop; ++$j, ++$k) {
                $temp = $product_value[$k] + $x_value[$j] * $y_value[$i] + $carry;
                $carry = MATH_BIGINTEGER_BASE === 26 ? intval($temp / 0x4000000) : ($temp >> 31);
                $product_value[$k] = (int) ($temp - MATH_BIGINTEGER_BASE_FULL * $carry);
            }

            if ($k < $stop) {
                $product_value[$k] = $carry;
            }
        }

        return array(
            MATH_BIGINTEGER_VALUE => $this->_trim($product_value),
            MATH_BIGINTEGER_SIGN => $x_negative != $y_negative
        );
    }

    /**
     * Montgomery Modular Reduction
     *
     * ($x->_prepMontgomery($n))->_montgomery($n) yields $x % $n.
     * {@link http://math.libtomcrypt.com/files/tommath.pdf#page=170 MPM 6.3} provides insights on how this can be
     * improved upon (basically, by using the comba method).  gcd($n, 2) must be equal to one for this function
     * to work correctly.
     *
     * @see self::_prepMontgomery()
     * @see self::_slidingWindow()
     * @access private
     * @param array $x
     * @param array $n
     * @return array
     */
    function _montgomery($x, $n)
    {
        static $cache = array(
            MATH_BIGINTEGER_VARIABLE => array(),
            MATH_BIGINTEGER_DATA => array()
        );

        if (($key = array_search($n, $cache[MATH_BIGINTEGER_VARIABLE])) === false) {
            $key = count($cache[MATH_BIGINTEGER_VARIABLE]);
            $cache[MATH_BIGINTEGER_VARIABLE][] = $x;
            $cache[MATH_BIGINTEGER_DATA][] = $this->_modInverse67108864($n);
        }

        $k = count($n);

        $result = array(MATH_BIGINTEGER_VALUE => $x);

        for ($i = 0; $i < $k; ++$i) {
            $temp = $result[MATH_BIGINTEGER_VALUE][$i] * $cache[MATH_BIGINTEGER_DATA][$key];
            $temp = $temp - MATH_BIGINTEGER_BASE_FULL * (MATH_BIGINTEGER_BASE === 26 ? intval($temp / 0x4000000) : ($temp >> 31));
            $temp = $this->_regularMultiply(array($temp), $n);
            $temp = array_merge($this->_array_repeat(0, $i), $temp);
            $result = $this->_add($result[MATH_BIGINTEGER_VALUE], false, $temp, false);
        }

        $result[MATH_BIGINTEGER_VALUE] = array_slice($result[MATH_BIGINTEGER_VALUE], $k);

        if ($this->_compare($result, false, $n, false) >= 0) {
            $result = $this->_subtract($result[MATH_BIGINTEGER_VALUE], false, $n, false);
        }

        return $result[MATH_BIGINTEGER_VALUE];
    }

    /**
     * Montgomery Multiply
     *
     * Interleaves the montgomery reduction and long multiplication algorithms together as described in
     * {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=13 HAC 14.36}
     *
     * @see self::_prepMontgomery()
     * @see self::_montgomery()
     * @access private
     * @param array $x
     * @param array $y
     * @param array $m
     * @return array
     */
    function _montgomeryMultiply($x, $y, $m)
    {
        $temp = $this->_multiply($x, false, $y, false);
        return $this->_montgomery($temp[MATH_BIGINTEGER_VALUE], $m);

        // the following code, although not callable, can be run independently of the above code
        // although the above code performed better in my benchmarks the following could might
        // perform better under different circumstances. in lieu of deleting it it's just been
        // made uncallable

        static $cache = array(
            MATH_BIGINTEGER_VARIABLE => array(),
            MATH_BIGINTEGER_DATA => array()
        );

        if (($key = array_search($m, $cache[MATH_BIGINTEGER_VARIABLE])) === false) {
            $key = count($cache[MATH_BIGINTEGER_VARIABLE]);
            $cache[MATH_BIGINTEGER_VARIABLE][] = $m;
            $cache[MATH_BIGINTEGER_DATA][] = $this->_modInverse67108864($m);
        }

        $n = max(count($x), count($y), count($m));
        $x = array_pad($x, $n, 0);
        $y = array_pad($y, $n, 0);
        $m = array_pad($m, $n, 0);
        $a = array(MATH_BIGINTEGER_VALUE => $this->_array_repeat(0, $n + 1));
        for ($i = 0; $i < $n; ++$i) {
            $temp = $a[MATH_BIGINTEGER_VALUE][0] + $x[$i] * $y[0];
            $temp = $temp - MATH_BIGINTEGER_BASE_FULL * (MATH_BIGINTEGER_BASE === 26 ? intval($temp / 0x4000000) : ($temp >> 31));
            $temp = $temp * $cache[MATH_BIGINTEGER_DATA][$key];
            $temp = $temp - MATH_BIGINTEGER_BASE_FULL * (MATH_BIGINTEGER_BASE === 26 ? intval($temp / 0x4000000) : ($temp >> 31));
            $temp = $this->_add($this->_regularMultiply(array($x[$i]), $y), false, $this->_regularMultiply(array($temp), $m), false);
            $a = $this->_add($a[MATH_BIGINTEGER_VALUE], false, $temp[MATH_BIGINTEGER_VALUE], false);
            $a[MATH_BIGINTEGER_VALUE] = array_slice($a[MATH_BIGINTEGER_VALUE], 1);
        }
        if ($this->_compare($a[MATH_BIGINTEGER_VALUE], false, $m, false) >= 0) {
            $a = $this->_subtract($a[MATH_BIGINTEGER_VALUE], false, $m, false);
        }
        return $a[MATH_BIGINTEGER_VALUE];
    }

    /**
     * Prepare a number for use in Montgomery Modular Reductions
     *
     * @see self::_montgomery()
     * @see self::_slidingWindow()
     * @access private
     * @param array $x
     * @param array $n
     * @return array
     */
    function _prepMontgomery($x, $n)
    {
        $lhs = new Math_BigInteger();
        $lhs->value = array_merge($this->_array_repeat(0, count($n)), $x);
        $rhs = new Math_BigInteger();
        $rhs->value = $n;

        list(, $temp) = $lhs->divide($rhs);
        return $temp->value;
    }

    /**
     * Modular Inverse of a number mod 2**26 (eg. 67108864)
     *
     * Based off of the bnpInvDigit function implemented and justified in the following URL:
     *
     * {@link http://www-cs-students.stanford.edu/~tjw/jsbn/jsbn.js}
     *
     * The following URL provides more info:
     *
     * {@link http://groups.google.com/group/sci.crypt/msg/7a137205c1be7d85}
     *
     * As for why we do all the bitmasking...  strange things can happen when converting from floats to ints. For
     * instance, on some computers, var_dump((int) -4294967297) yields int(-1) and on others, it yields
     * int(-2147483648).  To avoid problems stemming from this, we use bitmasks to guarantee that ints aren't
     * auto-converted to floats.  The outermost bitmask is present because without it, there's no guarantee that
     * the "residue" returned would be the so-called "common residue".  We use fmod, in the last step, because the
     * maximum possible $x is 26 bits and the maximum $result is 16 bits.  Thus, we have to be able to handle up to
     * 40 bits, which only 64-bit floating points will support.
     *
     * Thanks to Pedro Gimeno Fortea for input!
     *
     * @see self::_montgomery()
     * @access private
     * @param array $x
     * @return int
     */
    function _modInverse67108864($x) // 2**26 == 67,108,864
    {
        $x = -$x[0];
        $result = $x & 0x3; // x**-1 mod 2**2
        $result = ($result * (2 - $x * $result)) & 0xF; // x**-1 mod 2**4
        $result = ($result * (2 - ($x & 0xFF) * $result))  & 0xFF; // x**-1 mod 2**8
        $result = ($result * ((2 - ($x & 0xFFFF) * $result) & 0xFFFF)) & 0xFFFF; // x**-1 mod 2**16
        $result = fmod($result * (2 - fmod($x * $result, MATH_BIGINTEGER_BASE_FULL)), MATH_BIGINTEGER_BASE_FULL); // x**-1 mod 2**26
        return $result & MATH_BIGINTEGER_MAX_DIGIT;
    }

    /**
     * Calculates modular inverses.
     *
     * Say you have (30 mod 17 * x mod 17) mod 17 == 1.  x can be found using modular inverses.
     *
     * Here's an example:
     * <code>
     * <?php
     *    include 'Math/BigInteger.php';
     *
     *    $a = new Math_BigInteger(30);
     *    $b = new Math_BigInteger(17);
     *
     *    $c = $a->modInverse($b);
     *    echo $c->toString(); // outputs 4
     *
     *    echo "\r\n";
     *
     *    $d = $a->multiply($c);
     *    list(, $d) = $d->divide($b);
     *    echo $d; // outputs 1 (as per the definition of modular inverse)
     * ?>
     * </code>
     *
     * @param Math_BigInteger $n
     * @return Math_BigInteger|false
     * @access public
     * @internal See {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=21 HAC 14.64} for more information.
     */
    function modInverse($n)
    {
        switch (MATH_BIGINTEGER_MODE) {
            case MATH_BIGINTEGER_MODE_GMP:
                $temp = new Math_BigInteger();
                $temp->value = gmp_invert($this->value, $n->value);

                return ($temp->value === false) ? false : $this->_normalize($temp);
        }

        static $zero, $one;
        if (!isset($zero)) {
            $zero = new Math_BigInteger();
            $one = new Math_BigInteger(1);
        }

        // $x mod -$n == $x mod $n.
        $n = $n->abs();

        if ($this->compare($zero) < 0) {
            $temp = $this->abs();
            $temp = $temp->modInverse($n);
            return $this->_normalize($n->subtract($temp));
        }

        extract($this->extendedGCD($n));

        if (!$gcd->equals($one)) {
            return false;
        }

        $x = $x->compare($zero) < 0 ? $x->add($n) : $x;

        return $this->compare($zero) < 0 ? $this->_normalize($n->subtract($x)) : $this->_normalize($x);
    }

    /**
     * Calculates the greatest common divisor and Bezout's identity.
     *
     * Say you have 693 and 609.  The GCD is 21.  Bezout's identity states that there exist integers x and y such that
     * 693*x + 609*y == 21.  In point of fact, there are actually an infinite number of x and y combinations and which
     * combination is returned is dependent upon which mode is in use.  See
     * {@link http://en.wikipedia.org/wiki/B%C3%A9zout%27s_identity Bezout's identity - Wikipedia} for more information.
     *
     * Here's an example:
     * <code>
     * <?php
     *    include 'Math/BigInteger.php';
     *
     *    $a = new Math_BigInteger(693);
     *    $b = new Math_BigInteger(609);
     *
     *    extract($a->extendedGCD($b));
     *
     *    echo $gcd->toString() . "\r\n"; // outputs 21
     *    echo $a->toString() * $x->toString() + $b->toString() * $y->toString(); // outputs 21
     * ?>
     * </code>
     *
     * @param Math_BigInteger $n
     * @return Math_BigInteger
     * @access public
     * @internal Calculates the GCD using the binary xGCD algorithim described in
     *    {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=19 HAC 14.61}.  As the text above 14.61 notes,
     *    the more traditional algorithim requires "relatively costly multiple-precision divisions".
     */
    function extendedGCD($n)
    {
        switch (MATH_BIGINTEGER_MODE) {
            case MATH_BIGINTEGER_MODE_GMP:
                extract(gmp_gcdext($this->value, $n->value));

                return array(
                    'gcd' => $this->_normalize(new Math_BigInteger($g)),
                    'x'   => $this->_normalize(new Math_BigInteger($s)),
                    'y'   => $this->_normalize(new Math_BigInteger($t))
                );
            case MATH_BIGINTEGER_MODE_BCMATH:
                // it might be faster to use the binary xGCD algorithim here, as well, but (1) that algorithim works
                // best when the base is a power of 2 and (2) i don't think it'd make much difference, anyway.  as is,
                // the basic extended euclidean algorithim is what we're using.

                $u = $this->value;
                $v = $n->value;

                $a = '1';
                $b = '0';
                $c = '0';
                $d = '1';

                while (bccomp($v, '0', 0) != 0) {
                    $q = bcdiv($u, $v, 0);

                    $temp = $u;
                    $u = $v;
                    $v = bcsub($temp, bcmul($v, $q, 0), 0);

                    $temp = $a;
                    $a = $c;
                    $c = bcsub($temp, bcmul($a, $q, 0), 0);

                    $temp = $b;
                    $b = $d;
                    $d = bcsub($temp, bcmul($b, $q, 0), 0);
                }

                return array(
                    'gcd' => $this->_normalize(new Math_BigInteger($u)),
                    'x'   => $this->_normalize(new Math_BigInteger($a)),
                    'y'   => $this->_normalize(new Math_BigInteger($b))
                );
        }

        $y = $n->copy();
        $x = $this->copy();
        $g = new Math_BigInteger();
        $g->value = array(1);

        while (!(($x->value[0] & 1)|| ($y->value[0] & 1))) {
            $x->_rshift(1);
            $y->_rshift(1);
            $g->_lshift(1);
        }

        $u = $x->copy();
        $v = $y->copy();

        $a = new Math_BigInteger();
        $b = new Math_BigInteger();
        $c = new Math_BigInteger();
        $d = new Math_BigInteger();

        $a->value = $d->value = $g->value = array(1);
        $b->value = $c->value = array();

        while (!empty($u->value)) {
            while (!($u->value[0] & 1)) {
                $u->_rshift(1);
                if ((!empty($a->value) && ($a->value[0] & 1)) || (!empty($b->value) && ($b->value[0] & 1))) {
                    $a = $a->add($y);
                    $b = $b->subtract($x);
                }
                $a->_rshift(1);
                $b->_rshift(1);
            }

            while (!($v->value[0] & 1)) {
                $v->_rshift(1);
                if ((!empty($d->value) && ($d->value[0] & 1)) || (!empty($c->value) && ($c->value[0] & 1))) {
                    $c = $c->add($y);
                    $d = $d->subtract($x);
                }
                $c->_rshift(1);
                $d->_rshift(1);
            }

            if ($u->compare($v) >= 0) {
                $u = $u->subtract($v);
                $a = $a->subtract($c);
                $b = $b->subtract($d);
            } else {
                $v = $v->subtract($u);
                $c = $c->subtract($a);
                $d = $d->subtract($b);
            }
        }

        return array(
            'gcd' => $this->_normalize($g->multiply($v)),
            'x'   => $this->_normalize($c),
            'y'   => $this->_normalize($d)
        );
    }

    /**
     * Calculates the greatest common divisor
     *
     * Say you have 693 and 609.  The GCD is 21.
     *
     * Here's an example:
     * <code>
     * <?php
     *    include 'Math/BigInteger.php';
     *
     *    $a = new Math_BigInteger(693);
     *    $b = new Math_BigInteger(609);
     *
     *    $gcd = a->extendedGCD($b);
     *
     *    echo $gcd->toString() . "\r\n"; // outputs 21
     * ?>
     * </code>
     *
     * @param Math_BigInteger $n
     * @return Math_BigInteger
     * @access public
     */
    function gcd($n)
    {
        extract($this->extendedGCD($n));
        return $gcd;
    }

    /**
     * Absolute value.
     *
     * @return Math_BigInteger
     * @access public
     */
    function abs()
    {
        $temp = new Math_BigInteger();

        switch (MATH_BIGINTEGER_MODE) {
            case MATH_BIGINTEGER_MODE_GMP:
                $temp->value = gmp_abs($this->value);
                break;
            case MATH_BIGINTEGER_MODE_BCMATH:
                $temp->value = (bccomp($this->value, '0', 0) < 0) ? substr($this->value, 1) : $this->value;
                break;
            default:
                $temp->value = $this->value;
        }

        return $temp;
    }

    /**
     * Compares two numbers.
     *
     * Although one might think !$x->compare($y) means $x != $y, it, in fact, means the opposite.  The reason for this is
     * demonstrated thusly:
     *
     * $x  > $y: $x->compare($y)  > 0
     * $x  < $y: $x->compare($y)  < 0
     * $x == $y: $x->compare($y) == 0
     *
     * Note how the same comparison operator is used.  If you want to test for equality, use $x->equals($y).
     *
     * @param Math_BigInteger $y
     * @return int < 0 if $this is less than $y; > 0 if $this is greater than $y, and 0 if they are equal.
     * @access public
     * @see self::equals()
     * @internal Could return $this->subtract($x), but that's not as fast as what we do do.
     */
    function compare($y)
    {
        switch (MATH_BIGINTEGER_MODE) {
            case MATH_BIGINTEGER_MODE_GMP:
                return gmp_cmp($this->value, $y->value);
            case MATH_BIGINTEGER_MODE_BCMATH:
                return bccomp($this->value, $y->value, 0);
        }

        return $this->_compare($this->value, $this->is_negative, $y->value, $y->is_negative);
    }

    /**
     * Compares two numbers.
     *
     * @param array $x_value
     * @param bool $x_negative
     * @param array $y_value
     * @param bool $y_negative
     * @return int
     * @see self::compare()
     * @access private
     */
    function _compare($x_value, $x_negative, $y_value, $y_negative)
    {
        if ($x_negative != $y_negative) {
            return (!$x_negative && $y_negative) ? 1 : -1;
        }

        $result = $x_negative ? -1 : 1;

        if (count($x_value) != count($y_value)) {
            return (count($x_value) > count($y_value)) ? $result : -$result;
        }
        $size = max(count($x_value), count($y_value));

        $x_value = array_pad($x_value, $size, 0);
        $y_value = array_pad($y_value, $size, 0);

        for ($i = count($x_value) - 1; $i >= 0; --$i) {
            if ($x_value[$i] != $y_value[$i]) {
                return ($x_value[$i] > $y_value[$i]) ? $result : -$result;
            }
        }

        return 0;
    }

    /**
     * Tests the equality of two numbers.
     *
     * If you need to see if one number is greater than or less than another number, use Math_BigInteger::compare()
     *
     * @param Math_BigInteger $x
     * @return bool
     * @access public
     * @see self::compare()
     */
    function equals($x)
    {
        switch (MATH_BIGINTEGER_MODE) {
            case MATH_BIGINTEGER_MODE_GMP:
                return gmp_cmp($this->value, $x->value) == 0;
            default:
                return $this->value === $x->value && $this->is_negative == $x->is_negative;
        }
    }

    /**
     * Set Precision
     *
     * Some bitwise operations give different results depending on the precision being used.  Examples include left
     * shift, not, and rotates.
     *
     * @param int $bits
     * @access public
     */
    function setPrecision($bits)
    {
        $this->precision = $bits;
        if (MATH_BIGINTEGER_MODE != MATH_BIGINTEGER_MODE_BCMATH) {
            $this->bitmask = new Math_BigInteger(chr((1 << ($bits & 0x7)) - 1) . str_repeat(chr(0xFF), $bits >> 3), 256);
        } else {
            $this->bitmask = new Math_BigInteger(bcpow('2', $bits, 0));
        }

        $temp = $this->_normalize($this);
        $this->value = $temp->value;
    }

    /**
     * Logical And
     *
     * @param Math_BigInteger $x
     * @access public
     * @internal Implemented per a request by Lluis Pamies i Juarez <lluis _a_ pamies.cat>
     * @return Math_BigInteger
     */
    function bitwise_and($x)
    {
        switch (MATH_BIGINTEGER_MODE) {
            case MATH_BIGINTEGER_MODE_GMP:
                $temp = new Math_BigInteger();
                $temp->value = gmp_and($this->value, $x->value);

                return $this->_normalize($temp);
            case MATH_BIGINTEGER_MODE_BCMATH:
                $left = $this->toBytes();
                $right = $x->toBytes();

                $length = max(strlen($left), strlen($right));

                $left = str_pad($left, $length, chr(0), STR_PAD_LEFT);
                $right = str_pad($right, $length, chr(0), STR_PAD_LEFT);

                return $this->_normalize(new Math_BigInteger($left & $right, 256));
        }

        $result = $this->copy();

        $length = min(count($x->value), count($this->value));

        $result->value = array_slice($result->value, 0, $length);

        for ($i = 0; $i < $length; ++$i) {
            $result->value[$i]&= $x->value[$i];
        }

        return $this->_normalize($result);
    }

    /**
     * Logical Or
     *
     * @param Math_BigInteger $x
     * @access public
     * @internal Implemented per a request by Lluis Pamies i Juarez <lluis _a_ pamies.cat>
     * @return Math_BigInteger
     */
    function bitwise_or($x)
    {
        switch (MATH_BIGINTEGER_MODE) {
            case MATH_BIGINTEGER_MODE_GMP:
                $temp = new Math_BigInteger();
                $temp->value = gmp_or($this->value, $x->value);

                return $this->_normalize($temp);
            case MATH_BIGINTEGER_MODE_BCMATH:
                $left = $this->toBytes();
                $right = $x->toBytes();

                $length = max(strlen($left), strlen($right));

                $left = str_pad($left, $length, chr(0), STR_PAD_LEFT);
                $right = str_pad($right, $length, chr(0), STR_PAD_LEFT);

                return $this->_normalize(new Math_BigInteger($left | $right, 256));
        }

        $length = max(count($this->value), count($x->value));
        $result = $this->copy();
        $result->value = array_pad($result->value, $length, 0);
        $x->value = array_pad($x->value, $length, 0);

        for ($i = 0; $i < $length; ++$i) {
            $result->value[$i]|= $x->value[$i];
        }

        return $this->_normalize($result);
    }

    /**
     * Logical Exclusive-Or
     *
     * @param Math_BigInteger $x
     * @access public
     * @internal Implemented per a request by Lluis Pamies i Juarez <lluis _a_ pamies.cat>
     * @return Math_BigInteger
     */
    function bitwise_xor($x)
    {
        switch (MATH_BIGINTEGER_MODE) {
            case MATH_BIGINTEGER_MODE_GMP:
                $temp = new Math_BigInteger();
                $temp->value = gmp_xor(gmp_abs($this->value), gmp_abs($x->value));

                return $this->_normalize($temp);
            case MATH_BIGINTEGER_MODE_BCMATH:
                $left = $this->toBytes();
                $right = $x->toBytes();

                $length = max(strlen($left), strlen($right));

                $left = str_pad($left, $length, chr(0), STR_PAD_LEFT);
                $right = str_pad($right, $length, chr(0), STR_PAD_LEFT);

                return $this->_normalize(new Math_BigInteger($left ^ $right, 256));
        }

        $length = max(count($this->value), count($x->value));
        $result = $this->copy();
        $result->is_negative = false;
        $result->value = array_pad($result->value, $length, 0);
        $x->value = array_pad($x->value, $length, 0);

        for ($i = 0; $i < $length; ++$i) {
            $result->value[$i]^= $x->value[$i];
        }

        return $this->_normalize($result);
    }

    /**
     * Logical Not
     *
     * @access public
     * @internal Implemented per a request by Lluis Pamies i Juarez <lluis _a_ pamies.cat>
     * @return Math_BigInteger
     */
    function bitwise_not()
    {
        // calculuate "not" without regard to $this->precision
        // (will always result in a smaller number.  ie. ~1 isn't 1111 1110 - it's 0)
        $temp = $this->toBytes();
        if ($temp == '') {
            return $this->_normalize(new Math_BigInteger());
        }
        $pre_msb = decbin(ord($temp[0]));
        $temp = ~$temp;
        $msb = decbin(ord($temp[0]));
        if (strlen($msb) == 8) {
            $msb = substr($msb, strpos($msb, '0'));
        }
        $temp[0] = chr(bindec($msb));

        // see if we need to add extra leading 1's
        $current_bits = strlen($pre_msb) + 8 * strlen($temp) - 8;
        $new_bits = $this->precision - $current_bits;
        if ($new_bits <= 0) {
            return $this->_normalize(new Math_BigInteger($temp, 256));
        }

        // generate as many leading 1's as we need to.
        $leading_ones = chr((1 << ($new_bits & 0x7)) - 1) . str_repeat(chr(0xFF), $new_bits >> 3);
        $this->_base256_lshift($leading_ones, $current_bits);

        $temp = str_pad($temp, strlen($leading_ones), chr(0), STR_PAD_LEFT);

        return $this->_normalize(new Math_BigInteger($leading_ones | $temp, 256));
    }

    /**
     * Logical Right Shift
     *
     * Shifts BigInteger's by $shift bits, effectively dividing by 2**$shift.
     *
     * @param int $shift
     * @return Math_BigInteger
     * @access public
     * @internal The only version that yields any speed increases is the internal version.
     */
    function bitwise_rightShift($shift)
    {
        $temp = new Math_BigInteger();

        switch (MATH_BIGINTEGER_MODE) {
            case MATH_BIGINTEGER_MODE_GMP:
                static $two;

                if (!isset($two)) {
                    $two = gmp_init('2');
                }

                $temp->value = gmp_div_q($this->value, gmp_pow($two, $shift));

                break;
            case MATH_BIGINTEGER_MODE_BCMATH:
                $temp->value = bcdiv($this->value, bcpow('2', $shift, 0), 0);

                break;
            default: // could just replace _lshift with this, but then all _lshift() calls would need to be rewritten
                     // and I don't want to do that...
                $temp->value = $this->value;
                $temp->_rshift($shift);
        }

        return $this->_normalize($temp);
    }

    /**
     * Logical Left Shift
     *
     * Shifts BigInteger's by $shift bits, effectively multiplying by 2**$shift.
     *
     * @param int $shift
     * @return Math_BigInteger
     * @access public
     * @internal The only version that yields any speed increases is the internal version.
     */
    function bitwise_leftShift($shift)
    {
        $temp = new Math_BigInteger();

        switch (MATH_BIGINTEGER_MODE) {
            case MATH_BIGINTEGER_MODE_GMP:
                static $two;

                if (!isset($two)) {
                    $two = gmp_init('2');
                }

                $temp->value = gmp_mul($this->value, gmp_pow($two, $shift));

                break;
            case MATH_BIGINTEGER_MODE_BCMATH:
                $temp->value = bcmul($this->value, bcpow('2', $shift, 0), 0);

                break;
            default: // could just replace _rshift with this, but then all _lshift() calls would need to be rewritten
                     // and I don't want to do that...
                $temp->value = $this->value;
                $temp->_lshift($shift);
        }

        return $this->_normalize($temp);
    }

    /**
     * Logical Left Rotate
     *
     * Instead of the top x bits being dropped they're appended to the shifted bit string.
     *
     * @param int $shift
     * @return Math_BigInteger
     * @access public
     */
    function bitwise_leftRotate($shift)
    {
        $bits = $this->toBytes();

        if ($this->precision > 0) {
            $precision = $this->precision;
            if (MATH_BIGINTEGER_MODE == MATH_BIGINTEGER_MODE_BCMATH) {
                $mask = $this->bitmask->subtract(new Math_BigInteger(1));
                $mask = $mask->toBytes();
            } else {
                $mask = $this->bitmask->toBytes();
            }
        } else {
            $temp = ord($bits[0]);
            for ($i = 0; $temp >> $i; ++$i) {
            }
            $precision = 8 * strlen($bits) - 8 + $i;
            $mask = chr((1 << ($precision & 0x7)) - 1) . str_repeat(chr(0xFF), $precision >> 3);
        }

        if ($shift < 0) {
            $shift+= $precision;
        }
        $shift%= $precision;

        if (!$shift) {
            return $this->copy();
        }

        $left = $this->bitwise_leftShift($shift);
        $left = $left->bitwise_and(new Math_BigInteger($mask, 256));
        $right = $this->bitwise_rightShift($precision - $shift);
        $result = MATH_BIGINTEGER_MODE != MATH_BIGINTEGER_MODE_BCMATH ? $left->bitwise_or($right) : $left->add($right);
        return $this->_normalize($result);
    }

    /**
     * Logical Right Rotate
     *
     * Instead of the bottom x bits being dropped they're prepended to the shifted bit string.
     *
     * @param int $shift
     * @return Math_BigInteger
     * @access public
     */
    function bitwise_rightRotate($shift)
    {
        return $this->bitwise_leftRotate(-$shift);
    }

    /**
     * Set random number generator function
     *
     * This function is deprecated.
     *
     * @param string $generator
     * @access public
     */
    function setRandomGenerator($generator)
    {
    }

    /**
     * Generates a random BigInteger
     *
     * Byte length is equal to $length. Uses crypt_random if it's loaded and mt_rand if it's not.
     *
     * @param int $length
     * @return Math_BigInteger
     * @access private
     */
    function _random_number_helper($size)
    {
        if (function_exists('crypt_random_string')) {
            $random = crypt_random_string($size);
        } else {
            $random = '';

            if ($size & 1) {
                $random.= chr(mt_rand(0, 255));
            }

            $blocks = $size >> 1;
            for ($i = 0; $i < $blocks; ++$i) {
                // mt_rand(-2147483648, 0x7FFFFFFF) always produces -2147483648 on some systems
                $random.= pack('n', mt_rand(0, 0xFFFF));
            }
        }

        return new Math_BigInteger($random, 256);
    }

    /**
     * Generate a random number
     *
     * Returns a random number between $min and $max where $min and $max
     * can be defined using one of the two methods:
     *
     * $min->random($max)
     * $max->random($min)
     *
     * @param Math_BigInteger $arg1
     * @param Math_BigInteger $arg2
     * @return Math_BigInteger
     * @access public
     * @internal The API for creating random numbers used to be $a->random($min, $max), where $a was a Math_BigInteger object.
     *           That method is still supported for BC purposes.
     */
    function random($arg1, $arg2 = false)
    {
        if ($arg1 === false) {
            return false;
        }

        if ($arg2 === false) {
            $max = $arg1;
            $min = $this;
        } else {
            $min = $arg1;
            $max = $arg2;
        }

        $compare = $max->compare($min);

        if (!$compare) {
            return $this->_normalize($min);
        } elseif ($compare < 0) {
            // if $min is bigger then $max, swap $min and $max
            $temp = $max;
            $max = $min;
            $min = $temp;
        }

        static $one;
        if (!isset($one)) {
            $one = new Math_BigInteger(1);
        }

        $max = $max->subtract($min->subtract($one));
        $size = strlen(ltrim($max->toBytes(), chr(0)));

        /*
            doing $random % $max doesn't work because some numbers will be more likely to occur than others.
            eg. if $max is 140 and $random's max is 255 then that'd mean both $random = 5 and $random = 145
            would produce 5 whereas the only value of random that could produce 139 would be 139. ie.
            not all numbers would be equally likely. some would be more likely than others.

            creating a whole new random number until you find one that is within the range doesn't work
            because, for sufficiently small ranges, the likelihood that you'd get a number within that range
            would be pretty small. eg. with $random's max being 255 and if your $max being 1 the probability
            would be pretty high that $random would be greater than $max.

            phpseclib works around this using the technique described here:

            http://crypto.stackexchange.com/questions/5708/creating-a-small-number-from-a-cryptographically-secure-random-string
        */
        $random_max = new Math_BigInteger(chr(1) . str_repeat("\0", $size), 256);
        $random = $this->_random_number_helper($size);

        list($max_multiple) = $random_max->divide($max);
        $max_multiple = $max_multiple->multiply($max);

        while ($random->compare($max_multiple) >= 0) {
            $random = $random->subtract($max_multiple);
            $random_max = $random_max->subtract($max_multiple);
            $random = $random->bitwise_leftShift(8);
            $random = $random->add($this->_random_number_helper(1));
            $random_max = $random_max->bitwise_leftShift(8);
            list($max_multiple) = $random_max->divide($max);
            $max_multiple = $max_multiple->multiply($max);
        }
        list(, $random) = $random->divide($max);

        return $this->_normalize($random->add($min));
    }

    /**
     * Generate a random prime number.
     *
     * If there's not a prime within the given range, false will be returned.
     * If more than $timeout seconds have elapsed, give up and return false.
     *
     * @param Math_BigInteger $arg1
     * @param Math_BigInteger $arg2
     * @param int $timeout
     * @return Math_BigInteger|false
     * @access public
     * @internal See {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap4.pdf#page=15 HAC 4.44}.
     */
    function randomPrime($arg1, $arg2 = false, $timeout = false)
    {
        if ($arg1 === false) {
            return false;
        }

        if ($arg2 === false) {
            $max = $arg1;
            $min = $this;
        } else {
            $min = $arg1;
            $max = $arg2;
        }

        $compare = $max->compare($min);

        if (!$compare) {
            return $min->isPrime() ? $min : false;
        } elseif ($compare < 0) {
            // if $min is bigger then $max, swap $min and $max
            $temp = $max;
            $max = $min;
            $min = $temp;
        }

        static $one, $two;
        if (!isset($one)) {
            $one = new Math_BigInteger(1);
            $two = new Math_BigInteger(2);
        }

        $start = time();

        $x = $this->random($min, $max);

        // gmp_nextprime() requires PHP 5 >= 5.2.0 per <http://php.net/gmp-nextprime>.
        if (MATH_BIGINTEGER_MODE == MATH_BIGINTEGER_MODE_GMP && extension_loaded('gmp') && version_compare(PHP_VERSION, '5.2.0', '>=')) {
            $p = new Math_BigInteger();
            $p->value = gmp_nextprime($x->value);

            if ($p->compare($max) <= 0) {
                return $p;
            }

            if (!$min->equals($x)) {
                $x = $x->subtract($one);
            }

            return $x->randomPrime($min, $x);
        }

        if ($x->equals($two)) {
            return $x;
        }

        $x->_make_odd();
        if ($x->compare($max) > 0) {
            // if $x > $max then $max is even and if $min == $max then no prime number exists between the specified range
            if ($min->equals($max)) {
                return false;
            }
            $x = $min->copy();
            $x->_make_odd();
        }

        $initial_x = $x->copy();

        while (true) {
            if ($timeout !== false && time() - $start > $timeout) {
                return false;
            }

            if ($x->isPrime()) {
                return $x;
            }

            $x = $x->add($two);

            if ($x->compare($max) > 0) {
                $x = $min->copy();
                if ($x->equals($two)) {
                    return $x;
                }
                $x->_make_odd();
            }

            if ($x->equals($initial_x)) {
                return false;
            }
        }
    }

    /**
     * Make the current number odd
     *
     * If the current number is odd it'll be unchanged.  If it's even, one will be added to it.
     *
     * @see self::randomPrime()
     * @access private
     */
    function _make_odd()
    {
        switch (MATH_BIGINTEGER_MODE) {
            case MATH_BIGINTEGER_MODE_GMP:
                gmp_setbit($this->value, 0);
                break;
            case MATH_BIGINTEGER_MODE_BCMATH:
                if ($this->value[strlen($this->value) - 1] % 2 == 0) {
                    $this->value = bcadd($this->value, '1');
                }
                break;
            default:
                $this->value[0] |= 1;
        }
    }

    /**
     * Checks a numer to see if it's prime
     *
     * Assuming the $t parameter is not set, this function has an error rate of 2**-80.  The main motivation for the
     * $t parameter is distributability.  Math_BigInteger::randomPrime() can be distributed across multiple pageloads
     * on a website instead of just one.
     *
     * @param Math_BigInteger $t
     * @return bool
     * @access public
     * @internal Uses the
     *     {@link http://en.wikipedia.org/wiki/Miller%E2%80%93Rabin_primality_test Miller-Rabin primality test}.  See
     *     {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap4.pdf#page=8 HAC 4.24}.
     */
    function isPrime($t = false)
    {
        $length = strlen($this->toBytes());

        if (!$t) {
            // see HAC 4.49 "Note (controlling the error probability)"
            // @codingStandardsIgnoreStart
                 if ($length >= 163) { $t =  2; } // floor(1300 / 8)
            else if ($length >= 106) { $t =  3; } // floor( 850 / 8)
            else if ($length >= 81 ) { $t =  4; } // floor( 650 / 8)
            else if ($length >= 68 ) { $t =  5; } // floor( 550 / 8)
            else if ($length >= 56 ) { $t =  6; } // floor( 450 / 8)
            else if ($length >= 50 ) { $t =  7; } // floor( 400 / 8)
            else if ($length >= 43 ) { $t =  8; } // floor( 350 / 8)
            else if ($length >= 37 ) { $t =  9; } // floor( 300 / 8)
            else if ($length >= 31 ) { $t = 12; } // floor( 250 / 8)
            else if ($length >= 25 ) { $t = 15; } // floor( 200 / 8)
            else if ($length >= 18 ) { $t = 18; } // floor( 150 / 8)
            else                     { $t = 27; }
            // @codingStandardsIgnoreEnd
        }

        // ie. gmp_testbit($this, 0)
        // ie. isEven() or !isOdd()
        switch (MATH_BIGINTEGER_MODE) {
            case MATH_BIGINTEGER_MODE_GMP:
                return gmp_prob_prime($this->value, $t) != 0;
            case MATH_BIGINTEGER_MODE_BCMATH:
                if ($this->value === '2') {
                    return true;
                }
                if ($this->value[strlen($this->value) - 1] % 2 == 0) {
                    return false;
                }
                break;
            default:
                if ($this->value == array(2)) {
                    return true;
                }
                if (~$this->value[0] & 1) {
                    return false;
                }
        }

        static $primes, $zero, $one, $two;

        if (!isset($primes)) {
            $primes = array(
                3,    5,    7,    11,   13,   17,   19,   23,   29,   31,   37,   41,   43,   47,   53,   59,
                61,   67,   71,   73,   79,   83,   89,   97,   101,  103,  107,  109,  113,  127,  131,  137,
                139,  149,  151,  157,  163,  167,  173,  179,  181,  191,  193,  197,  199,  211,  223,  227,
                229,  233,  239,  241,  251,  257,  263,  269,  271,  277,  281,  283,  293,  307,  311,  313,
                317,  331,  337,  347,  349,  353,  359,  367,  373,  379,  383,  389,  397,  401,  409,  419,
                421,  431,  433,  439,  443,  449,  457,  461,  463,  467,  479,  487,  491,  499,  503,  509,
                521,  523,  541,  547,  557,  563,  569,  571,  577,  587,  593,  599,  601,  607,  613,  617,
                619,  631,  641,  643,  647,  653,  659,  661,  673,  677,  683,  691,  701,  709,  719,  727,
                733,  739,  743,  751,  757,  761,  769,  773,  787,  797,  809,  811,  821,  823,  827,  829,
                839,  853,  857,  859,  863,  877,  881,  883,  887,  907,  911,  919,  929,  937,  941,  947,
                953,  967,  971,  977,  983,  991,  997
            );

            if (MATH_BIGINTEGER_MODE != MATH_BIGINTEGER_MODE_INTERNAL) {
                for ($i = 0; $i < count($primes); ++$i) {
                    $primes[$i] = new Math_BigInteger($primes[$i]);
                }
            }

            $zero = new Math_BigInteger();
            $one = new Math_BigInteger(1);
            $two = new Math_BigInteger(2);
        }

        if ($this->equals($one)) {
            return false;
        }

        // see HAC 4.4.1 "Random search for probable primes"
        if (MATH_BIGINTEGER_MODE != MATH_BIGINTEGER_MODE_INTERNAL) {
            foreach ($primes as $prime) {
                list(, $r) = $this->divide($prime);
                if ($r->equals($zero)) {
                    return $this->equals($prime);
                }
            }
        } else {
            $value = $this->value;
            foreach ($primes as $prime) {
                list(, $r) = $this->_divide_digit($value, $prime);
                if (!$r) {
                    return count($value) == 1 && $value[0] == $prime;
                }
            }
        }

        $n   = $this->copy();
        $n_1 = $n->subtract($one);
        $n_2 = $n->subtract($two);

        $r = $n_1->copy();
        $r_value = $r->value;
        // ie. $s = gmp_scan1($n, 0) and $r = gmp_div_q($n, gmp_pow(gmp_init('2'), $s));
        if (MATH_BIGINTEGER_MODE == MATH_BIGINTEGER_MODE_BCMATH) {
            $s = 0;
            // if $n was 1, $r would be 0 and this would be an infinite loop, hence our $this->equals($one) check earlier
            while ($r->value[strlen($r->value) - 1] % 2 == 0) {
                $r->value = bcdiv($r->value, '2', 0);
                ++$s;
            }
        } else {
            for ($i = 0, $r_length = count($r_value); $i < $r_length; ++$i) {
                $temp = ~$r_value[$i] & 0xFFFFFF;
                for ($j = 1; ($temp >> $j) & 1; ++$j) {
                }
                if ($j != 25) {
                    break;
                }
            }
            $s = 26 * $i + $j;
            $r->_rshift($s);
        }

        for ($i = 0; $i < $t; ++$i) {
            $a = $this->random($two, $n_2);
            $y = $a->modPow($r, $n);

            if (!$y->equals($one) && !$y->equals($n_1)) {
                for ($j = 1; $j < $s && !$y->equals($n_1); ++$j) {
                    $y = $y->modPow($two, $n);
                    if ($y->equals($one)) {
                        return false;
                    }
                }

                if (!$y->equals($n_1)) {
                    return false;
                }
            }
        }
        return true;
    }

    /**
     * Logical Left Shift
     *
     * Shifts BigInteger's by $shift bits.
     *
     * @param int $shift
     * @access private
     */
    function _lshift($shift)
    {
        if ($shift == 0) {
            return;
        }

        $num_digits = (int) ($shift / MATH_BIGINTEGER_BASE);
        $shift %= MATH_BIGINTEGER_BASE;
        $shift = 1 << $shift;

        $carry = 0;

        for ($i = 0; $i < count($this->value); ++$i) {
            $temp = $this->value[$i] * $shift + $carry;
            $carry = MATH_BIGINTEGER_BASE === 26 ? intval($temp / 0x4000000) : ($temp >> 31);
            $this->value[$i] = (int) ($temp - $carry * MATH_BIGINTEGER_BASE_FULL);
        }

        if ($carry) {
            $this->value[count($this->value)] = $carry;
        }

        while ($num_digits--) {
            array_unshift($this->value, 0);
        }
    }

    /**
     * Logical Right Shift
     *
     * Shifts BigInteger's by $shift bits.
     *
     * @param int $shift
     * @access private
     */
    function _rshift($shift)
    {
        if ($shift == 0) {
            return;
        }

        $num_digits = (int) ($shift / MATH_BIGINTEGER_BASE);
        $shift %= MATH_BIGINTEGER_BASE;
        $carry_shift = MATH_BIGINTEGER_BASE - $shift;
        $carry_mask = (1 << $shift) - 1;

        if ($num_digits) {
            $this->value = array_slice($this->value, $num_digits);
        }

        $carry = 0;

        for ($i = count($this->value) - 1; $i >= 0; --$i) {
            $temp = $this->value[$i] >> $shift | $carry;
            $carry = ($this->value[$i] & $carry_mask) << $carry_shift;
            $this->value[$i] = $temp;
        }

        $this->value = $this->_trim($this->value);
    }

    /**
     * Normalize
     *
     * Removes leading zeros and truncates (if necessary) to maintain the appropriate precision
     *
     * @param Math_BigInteger
     * @return Math_BigInteger
     * @see self::_trim()
     * @access private
     */
    function _normalize($result)
    {
        $result->precision = $this->precision;
        $result->bitmask = $this->bitmask;

        switch (MATH_BIGINTEGER_MODE) {
            case MATH_BIGINTEGER_MODE_GMP:
                if ($this->bitmask !== false) {
                    $result->value = gmp_and($result->value, $result->bitmask->value);
                }

                return $result;
            case MATH_BIGINTEGER_MODE_BCMATH:
                if (!empty($result->bitmask->value)) {
                    $result->value = bcmod($result->value, $result->bitmask->value);
                }

                return $result;
        }

        $value = &$result->value;

        if (!count($value)) {
            return $result;
        }

        $value = $this->_trim($value);

        if (!empty($result->bitmask->value)) {
            $length = min(count($value), count($this->bitmask->value));
            $value = array_slice($value, 0, $length);

            for ($i = 0; $i < $length; ++$i) {
                $value[$i] = $value[$i] & $this->bitmask->value[$i];
            }
        }

        return $result;
    }

    /**
     * Trim
     *
     * Removes leading zeros
     *
     * @param array $value
     * @return Math_BigInteger
     * @access private
     */
    function _trim($value)
    {
        for ($i = count($value) - 1; $i >= 0; --$i) {
            if ($value[$i]) {
                break;
            }
            unset($value[$i]);
        }

        return $value;
    }

    /**
     * Array Repeat
     *
     * @param $input Array
     * @param $multiplier mixed
     * @return array
     * @access private
     */
    function _array_repeat($input, $multiplier)
    {
        return ($multiplier) ? array_fill(0, $multiplier, $input) : array();
    }

    /**
     * Logical Left Shift
     *
     * Shifts binary strings $shift bits, essentially multiplying by 2**$shift.
     *
     * @param $x String
     * @param $shift Integer
     * @return string
     * @access private
     */
    function _base256_lshift(&$x, $shift)
    {
        if ($shift == 0) {
            return;
        }

        $num_bytes = $shift >> 3; // eg. floor($shift/8)
        $shift &= 7; // eg. $shift % 8

        $carry = 0;
        for ($i = strlen($x) - 1; $i >= 0; --$i) {
            $temp = ord($x[$i]) << $shift | $carry;
            $x[$i] = chr($temp);
            $carry = $temp >> 8;
        }
        $carry = ($carry != 0) ? chr($carry) : '';
        $x = $carry . $x . str_repeat(chr(0), $num_bytes);
    }

    /**
     * Logical Right Shift
     *
     * Shifts binary strings $shift bits, essentially dividing by 2**$shift and returning the remainder.
     *
     * @param $x String
     * @param $shift Integer
     * @return string
     * @access private
     */
    function _base256_rshift(&$x, $shift)
    {
        if ($shift == 0) {
            $x = ltrim($x, chr(0));
            return '';
        }

        $num_bytes = $shift >> 3; // eg. floor($shift/8)
        $shift &= 7; // eg. $shift % 8

        $remainder = '';
        if ($num_bytes) {
            $start = $num_bytes > strlen($x) ? -strlen($x) : -$num_bytes;
            $remainder = substr($x, $start);
            $x = substr($x, 0, -$num_bytes);
        }

        $carry = 0;
        $carry_shift = 8 - $shift;
        for ($i = 0; $i < strlen($x); ++$i) {
            $temp = (ord($x[$i]) >> $shift) | $carry;
            $carry = (ord($x[$i]) << $carry_shift) & 0xFF;
            $x[$i] = chr($temp);
        }
        $x = ltrim($x, chr(0));

        $remainder = chr($carry >> $carry_shift) . $remainder;

        return ltrim($remainder, chr(0));
    }

    // one quirk about how the following functions are implemented is that PHP defines N to be an unsigned long
    // at 32-bits, while java's longs are 64-bits.

    /**
     * Converts 32-bit integers to bytes.
     *
     * @param int $x
     * @return string
     * @access private
     */
    function _int2bytes($x)
    {
        return ltrim(pack('N', $x), chr(0));
    }

    /**
     * Converts bytes to 32-bit integers
     *
     * @param string $x
     * @return int
     * @access private
     */
    function _bytes2int($x)
    {
        $temp = unpack('Nint', str_pad($x, 4, chr(0), STR_PAD_LEFT));
        return $temp['int'];
    }

    /**
     * DER-encode an integer
     *
     * The ability to DER-encode integers is needed to create RSA public keys for use with OpenSSL
     *
     * @see self::modPow()
     * @access private
     * @param int $length
     * @return string
     */
    function _encodeASN1Length($length)
    {
        if ($length <= 0x7F) {
            return chr($length);
        }

        $temp = ltrim(pack('N', $length), chr(0));
        return pack('Ca*', 0x80 | strlen($temp), $temp);
    }

    /**
     * Single digit division
     *
     * Even if int64 is being used the division operator will return a float64 value
     * if the dividend is not evenly divisible by the divisor. Since a float64 doesn't
     * have the precision of int64 this is a problem so, when int64 is being used,
     * we'll guarantee that the dividend is divisible by first subtracting the remainder.
     *
     * @access private
     * @param int $x
     * @param int $y
     * @return int
     */
    function _safe_divide($x, $y)
    {
        if (MATH_BIGINTEGER_BASE === 26) {
            return (int) ($x / $y);
        }

        // MATH_BIGINTEGER_BASE === 31
        return ($x - ($x % $y)) / $y;
    }
}
vendor/phpseclib/phpseclib/phpseclib/Net/SSH2.php000064400000506161151327705700015732 0ustar00<?php

/**
 * Pure-PHP implementation of SSHv2.
 *
 * PHP versions 4 and 5
 *
 * Here are some examples of how to use this library:
 * <code>
 * <?php
 *    include 'Net/SSH2.php';
 *
 *    $ssh = new Net_SSH2('www.domain.tld');
 *    if (!$ssh->login('username', 'password')) {
 *        exit('Login Failed');
 *    }
 *
 *    echo $ssh->exec('pwd');
 *    echo $ssh->exec('ls -la');
 * ?>
 * </code>
 *
 * <code>
 * <?php
 *    include 'Crypt/RSA.php';
 *    include 'Net/SSH2.php';
 *
 *    $key = new Crypt_RSA();
 *    //$key->setPassword('whatever');
 *    $key->loadKey(file_get_contents('privatekey'));
 *
 *    $ssh = new Net_SSH2('www.domain.tld');
 *    if (!$ssh->login('username', $key)) {
 *        exit('Login Failed');
 *    }
 *
 *    echo $ssh->read('username@username:~$');
 *    $ssh->write("ls -la\n");
 *    echo $ssh->read('username@username:~$');
 * ?>
 * </code>
 *
 * LICENSE: Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 *
 * @category  Net
 * @package   Net_SSH2
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2007 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      http://phpseclib.sourceforge.net
 */

/**#@+
 * Execution Bitmap Masks
 *
 * @see self::bitmap
 * @access private
 */
define('NET_SSH2_MASK_CONSTRUCTOR',   0x00000001);
define('NET_SSH2_MASK_CONNECTED',     0x00000002);
define('NET_SSH2_MASK_LOGIN_REQ',     0x00000004);
define('NET_SSH2_MASK_LOGIN',         0x00000008);
define('NET_SSH2_MASK_SHELL',         0x00000010);
define('NET_SSH2_MASK_WINDOW_ADJUST', 0x00000020);
/**#@-*/

/**#@+
 * Channel constants
 *
 * RFC4254 refers not to client and server channels but rather to sender and recipient channels.  we don't refer
 * to them in that way because RFC4254 toggles the meaning. the client sends a SSH_MSG_CHANNEL_OPEN message with
 * a sender channel and the server sends a SSH_MSG_CHANNEL_OPEN_CONFIRMATION in response, with a sender and a
 * recepient channel.  at first glance, you might conclude that SSH_MSG_CHANNEL_OPEN_CONFIRMATION's sender channel
 * would be the same thing as SSH_MSG_CHANNEL_OPEN's sender channel, but it's not, per this snipet:
 *     The 'recipient channel' is the channel number given in the original
 *     open request, and 'sender channel' is the channel number allocated by
 *     the other side.
 *
 * @see self::_send_channel_packet()
 * @see self::_get_channel_packet()
 * @access private
 */
define('NET_SSH2_CHANNEL_EXEC',          1); // PuTTy uses 0x100
define('NET_SSH2_CHANNEL_SHELL',         2);
define('NET_SSH2_CHANNEL_SUBSYSTEM',     3);
define('NET_SSH2_CHANNEL_AGENT_FORWARD', 4);
define('NET_SSH2_CHANNEL_KEEP_ALIVE',    5);
/**#@-*/

/**#@+
 * @access public
 * @see self::getLog()
 */
/**
 * Returns the message numbers
 */
define('NET_SSH2_LOG_SIMPLE',  1);
/**
 * Returns the message content
 */
define('NET_SSH2_LOG_COMPLEX', 2);
/**
 * Outputs the content real-time
 */
define('NET_SSH2_LOG_REALTIME', 3);
/**
 * Dumps the content real-time to a file
 */
define('NET_SSH2_LOG_REALTIME_FILE', 4);
/**
 * Make sure that the log never gets larger than this
 */
define('NET_SSH2_LOG_MAX_SIZE', 1024 * 1024);
/**#@-*/

/**#@+
 * @access public
 * @see self::read()
 */
/**
 * Returns when a string matching $expect exactly is found
 */
define('NET_SSH2_READ_SIMPLE',  1);
/**
 * Returns when a string matching the regular expression $expect is found
 */
define('NET_SSH2_READ_REGEX', 2);
/**
 * Returns when a string matching the regular expression $expect is found
 */
define('NET_SSH2_READ_NEXT', 3);
/**#@-*/

/**
 * Pure-PHP implementation of SSHv2.
 *
 * @package Net_SSH2
 * @author  Jim Wigginton <terrafrost@php.net>
 * @access  public
 */
class Net_SSH2
{
    /**
     * The SSH identifier
     *
     * @var string
     * @access private
     */
    var $identifier;

    /**
     * The Socket Object
     *
     * @var object
     * @access private
     */
    var $fsock;

    /**
     * Execution Bitmap
     *
     * The bits that are set represent functions that have been called already.  This is used to determine
     * if a requisite function has been successfully executed.  If not, an error should be thrown.
     *
     * @var int
     * @access private
     */
    var $bitmap = 0;

    /**
     * Error information
     *
     * @see self::getErrors()
     * @see self::getLastError()
     * @var string
     * @access private
     */
    var $errors = array();

    /**
     * Server Identifier
     *
     * @see self::getServerIdentification()
     * @var array|false
     * @access private
     */
    var $server_identifier = false;

    /**
     * Key Exchange Algorithms
     *
     * @see self::getKexAlgorithims()
     * @var array|false
     * @access private
     */
    var $kex_algorithms = false;

    /**
     * Minimum Diffie-Hellman Group Bit Size in RFC 4419 Key Exchange Methods
     *
     * @see self::_key_exchange()
     * @var int
     * @access private
     */
    var $kex_dh_group_size_min = 1536;

    /**
     * Preferred Diffie-Hellman Group Bit Size in RFC 4419 Key Exchange Methods
     *
     * @see self::_key_exchange()
     * @var int
     * @access private
     */
    var $kex_dh_group_size_preferred = 2048;

    /**
     * Maximum Diffie-Hellman Group Bit Size in RFC 4419 Key Exchange Methods
     *
     * @see self::_key_exchange()
     * @var int
     * @access private
     */
    var $kex_dh_group_size_max = 4096;

    /**
     * Server Host Key Algorithms
     *
     * @see self::getServerHostKeyAlgorithms()
     * @var array|false
     * @access private
     */
    var $server_host_key_algorithms = false;

    /**
     * Encryption Algorithms: Client to Server
     *
     * @see self::getEncryptionAlgorithmsClient2Server()
     * @var array|false
     * @access private
     */
    var $encryption_algorithms_client_to_server = false;

    /**
     * Encryption Algorithms: Server to Client
     *
     * @see self::getEncryptionAlgorithmsServer2Client()
     * @var array|false
     * @access private
     */
    var $encryption_algorithms_server_to_client = false;

    /**
     * MAC Algorithms: Client to Server
     *
     * @see self::getMACAlgorithmsClient2Server()
     * @var array|false
     * @access private
     */
    var $mac_algorithms_client_to_server = false;

    /**
     * MAC Algorithms: Server to Client
     *
     * @see self::getMACAlgorithmsServer2Client()
     * @var array|false
     * @access private
     */
    var $mac_algorithms_server_to_client = false;

    /**
     * Compression Algorithms: Client to Server
     *
     * @see self::getCompressionAlgorithmsClient2Server()
     * @var array|false
     * @access private
     */
    var $compression_algorithms_client_to_server = false;

    /**
     * Compression Algorithms: Server to Client
     *
     * @see self::getCompressionAlgorithmsServer2Client()
     * @var array|false
     * @access private
     */
    var $compression_algorithms_server_to_client = false;

    /**
     * Languages: Server to Client
     *
     * @see self::getLanguagesServer2Client()
     * @var array|false
     * @access private
     */
    var $languages_server_to_client = false;

    /**
     * Languages: Client to Server
     *
     * @see self::getLanguagesClient2Server()
     * @var array|false
     * @access private
     */
    var $languages_client_to_server = false;

    /**
     * Block Size for Server to Client Encryption
     *
     * "Note that the length of the concatenation of 'packet_length',
     *  'padding_length', 'payload', and 'random padding' MUST be a multiple
     *  of the cipher block size or 8, whichever is larger.  This constraint
     *  MUST be enforced, even when using stream ciphers."
     *
     *  -- http://tools.ietf.org/html/rfc4253#section-6
     *
     * @see self::Net_SSH2()
     * @see self::_send_binary_packet()
     * @var int
     * @access private
     */
    var $encrypt_block_size = 8;

    /**
     * Block Size for Client to Server Encryption
     *
     * @see self::Net_SSH2()
     * @see self::_get_binary_packet()
     * @var int
     * @access private
     */
    var $decrypt_block_size = 8;

    /**
     * Server to Client Encryption Object
     *
     * @see self::_get_binary_packet()
     * @var object
     * @access private
     */
    var $decrypt = false;

    /**
     * Client to Server Encryption Object
     *
     * @see self::_send_binary_packet()
     * @var object
     * @access private
     */
    var $encrypt = false;

    /**
     * Client to Server HMAC Object
     *
     * @see self::_send_binary_packet()
     * @var object
     * @access private
     */
    var $hmac_create = false;

    /**
     * Server to Client HMAC Object
     *
     * @see self::_get_binary_packet()
     * @var object
     * @access private
     */
    var $hmac_check = false;

    /**
     * Size of server to client HMAC
     *
     * We need to know how big the HMAC will be for the server to client direction so that we know how many bytes to read.
     * For the client to server side, the HMAC object will make the HMAC as long as it needs to be.  All we need to do is
     * append it.
     *
     * @see self::_get_binary_packet()
     * @var int
     * @access private
     */
    var $hmac_size = false;

    /**
     * Server Public Host Key
     *
     * @see self::getServerPublicHostKey()
     * @var string
     * @access private
     */
    var $server_public_host_key;

    /**
     * Session identifier
     *
     * "The exchange hash H from the first key exchange is additionally
     *  used as the session identifier, which is a unique identifier for
     *  this connection."
     *
     *  -- http://tools.ietf.org/html/rfc4253#section-7.2
     *
     * @see self::_key_exchange()
     * @var string
     * @access private
     */
    var $session_id = false;

    /**
     * Exchange hash
     *
     * The current exchange hash
     *
     * @see self::_key_exchange()
     * @var string
     * @access private
     */
    var $exchange_hash = false;

    /**
     * Message Numbers
     *
     * @see self::Net_SSH2()
     * @var array
     * @access private
     */
    var $message_numbers = array();

    /**
     * Disconnection Message 'reason codes' defined in RFC4253
     *
     * @see self::Net_SSH2()
     * @var array
     * @access private
     */
    var $disconnect_reasons = array();

    /**
     * SSH_MSG_CHANNEL_OPEN_FAILURE 'reason codes', defined in RFC4254
     *
     * @see self::Net_SSH2()
     * @var array
     * @access private
     */
    var $channel_open_failure_reasons = array();

    /**
     * Terminal Modes
     *
     * @link http://tools.ietf.org/html/rfc4254#section-8
     * @see self::Net_SSH2()
     * @var array
     * @access private
     */
    var $terminal_modes = array();

    /**
     * SSH_MSG_CHANNEL_EXTENDED_DATA's data_type_codes
     *
     * @link http://tools.ietf.org/html/rfc4254#section-5.2
     * @see self::Net_SSH2()
     * @var array
     * @access private
     */
    var $channel_extended_data_type_codes = array();

    /**
     * Send Sequence Number
     *
     * See 'Section 6.4.  Data Integrity' of rfc4253 for more info.
     *
     * @see self::_send_binary_packet()
     * @var int
     * @access private
     */
    var $send_seq_no = 0;

    /**
     * Get Sequence Number
     *
     * See 'Section 6.4.  Data Integrity' of rfc4253 for more info.
     *
     * @see self::_get_binary_packet()
     * @var int
     * @access private
     */
    var $get_seq_no = 0;

    /**
     * Server Channels
     *
     * Maps client channels to server channels
     *
     * @see self::_get_channel_packet()
     * @see self::exec()
     * @var array
     * @access private
     */
    var $server_channels = array();

    /**
     * Channel Buffers
     *
     * If a client requests a packet from one channel but receives two packets from another those packets should
     * be placed in a buffer
     *
     * @see self::_get_channel_packet()
     * @see self::exec()
     * @var array
     * @access private
     */
    var $channel_buffers = array();

    /**
     * Channel Status
     *
     * Contains the type of the last sent message
     *
     * @see self::_get_channel_packet()
     * @var array
     * @access private
     */
    var $channel_status = array();

    /**
     * Packet Size
     *
     * Maximum packet size indexed by channel
     *
     * @see self::_send_channel_packet()
     * @var array
     * @access private
     */
    var $packet_size_client_to_server = array();

    /**
     * Message Number Log
     *
     * @see self::getLog()
     * @var array
     * @access private
     */
    var $message_number_log = array();

    /**
     * Message Log
     *
     * @see self::getLog()
     * @var array
     * @access private
     */
    var $message_log = array();

    /**
     * The Window Size
     *
     * Bytes the other party can send before it must wait for the window to be adjusted (0x7FFFFFFF = 2GB)
     *
     * @var int
     * @see self::_send_channel_packet()
     * @see self::exec()
     * @access private
     */
    var $window_size = 0x7FFFFFFF;

    /**
     * Window size, server to client
     *
     * Window size indexed by channel
     *
     * @see self::_send_channel_packet()
     * @var array
     * @access private
     */
    var $window_size_server_to_client = array();

    /**
     * Window size, client to server
     *
     * Window size indexed by channel
     *
     * @see self::_get_channel_packet()
     * @var array
     * @access private
     */
    var $window_size_client_to_server = array();

    /**
     * Server signature
     *
     * Verified against $this->session_id
     *
     * @see self::getServerPublicHostKey()
     * @var string
     * @access private
     */
    var $signature = '';

    /**
     * Server signature format
     *
     * ssh-rsa or ssh-dss.
     *
     * @see self::getServerPublicHostKey()
     * @var string
     * @access private
     */
    var $signature_format = '';

    /**
     * Interactive Buffer
     *
     * @see self::read()
     * @var array
     * @access private
     */
    var $interactiveBuffer = '';

    /**
     * Current log size
     *
     * Should never exceed NET_SSH2_LOG_MAX_SIZE
     *
     * @see self::_send_binary_packet()
     * @see self::_get_binary_packet()
     * @var int
     * @access private
     */
    var $log_size;

    /**
     * Timeout
     *
     * @see self::setTimeout()
     * @access private
     */
    var $timeout;

    /**
     * Current Timeout
     *
     * @see self::_get_channel_packet()
     * @access private
     */
    var $curTimeout;

    /**
     * Real-time log file pointer
     *
     * @see self::_append_log()
     * @var resource
     * @access private
     */
    var $realtime_log_file;

    /**
     * Real-time log file size
     *
     * @see self::_append_log()
     * @var int
     * @access private
     */
    var $realtime_log_size;

    /**
     * Has the signature been validated?
     *
     * @see self::getServerPublicHostKey()
     * @var bool
     * @access private
     */
    var $signature_validated = false;

    /**
     * Real-time log file wrap boolean
     *
     * @see self::_append_log()
     * @access private
     */
    var $realtime_log_wrap;

    /**
     * Flag to suppress stderr from output
     *
     * @see self::enableQuietMode()
     * @access private
     */
    var $quiet_mode = false;

    /**
     * Time of first network activity
     *
     * @var int
     * @access private
     */
    var $last_packet;

    /**
     * Exit status returned from ssh if any
     *
     * @var int
     * @access private
     */
    var $exit_status;

    /**
     * Flag to request a PTY when using exec()
     *
     * @var bool
     * @see self::enablePTY()
     * @access private
     */
    var $request_pty = false;

    /**
     * Flag set while exec() is running when using enablePTY()
     *
     * @var bool
     * @access private
     */
    var $in_request_pty_exec = false;

    /**
     * Flag set after startSubsystem() is called
     *
     * @var bool
     * @access private
     */
    var $in_subsystem;

    /**
     * Contents of stdError
     *
     * @var string
     * @access private
     */
    var $stdErrorLog;

    /**
     * The Last Interactive Response
     *
     * @see self::_keyboard_interactive_process()
     * @var string
     * @access private
     */
    var $last_interactive_response = '';

    /**
     * Keyboard Interactive Request / Responses
     *
     * @see self::_keyboard_interactive_process()
     * @var array
     * @access private
     */
    var $keyboard_requests_responses = array();

    /**
     * Banner Message
     *
     * Quoting from the RFC, "in some jurisdictions, sending a warning message before
     * authentication may be relevant for getting legal protection."
     *
     * @see self::_filter()
     * @see self::getBannerMessage()
     * @var string
     * @access private
     */
    var $banner_message = '';

    /**
     * Did read() timeout or return normally?
     *
     * @see self::isTimeout()
     * @var bool
     * @access private
     */
    var $is_timeout = false;

    /**
     * Log Boundary
     *
     * @see self::_format_log()
     * @var string
     * @access private
     */
    var $log_boundary = ':';

    /**
     * Log Long Width
     *
     * @see self::_format_log()
     * @var int
     * @access private
     */
    var $log_long_width = 65;

    /**
     * Log Short Width
     *
     * @see self::_format_log()
     * @var int
     * @access private
     */
    var $log_short_width = 16;

    /**
     * Hostname
     *
     * @see self::Net_SSH2()
     * @see self::_connect()
     * @var string
     * @access private
     */
    var $host;

    /**
     * Port Number
     *
     * @see self::Net_SSH2()
     * @see self::_connect()
     * @var int
     * @access private
     */
    var $port;

    /**
     * Number of columns for terminal window size
     *
     * @see self::getWindowColumns()
     * @see self::setWindowColumns()
     * @see self::setWindowSize()
     * @var int
     * @access private
     */
    var $windowColumns = 80;

    /**
     * Number of columns for terminal window size
     *
     * @see self::getWindowRows()
     * @see self::setWindowRows()
     * @see self::setWindowSize()
     * @var int
     * @access private
     */
    var $windowRows = 24;

    /**
     * Crypto Engine
     *
     * @see self::setCryptoEngine()
     * @see self::_key_exchange()
     * @var int
     * @access private
     */
    var $crypto_engine = false;

    /**
     * A System_SSH_Agent for use in the SSH2 Agent Forwarding scenario
     *
     * @var System_SSH_Agent
     * @access private
     */
    var $agent;

    /**
     * Send the identification string first?
     *
     * @var bool
     * @access private
     */
    var $send_id_string_first = true;

    /**
     * Send the key exchange initiation packet first?
     *
     * @var bool
     * @access private
     */
    var $send_kex_first = true;

    /**
     * Some versions of OpenSSH incorrectly calculate the key size
     *
     * @var bool
     * @access private
     */
    var $bad_key_size_fix = false;

    /**
     * The selected decryption algorithm
     *
     * @var string
     * @access private
     */
    var $decrypt_algorithm = '';

    /**
     * Should we try to re-connect to re-establish keys?
     *
     * @var bool
     * @access private
     */
    var $retry_connect = false;

    /**
     * Binary Packet Buffer
     *
     * @var string|false
     * @access private
     */
    var $binary_packet_buffer = false;

    /**
     * Preferred Signature Format
     *
     * @var string|false
     * @access private
     */
    var $preferred_signature_format = false;

    /**
     * Authentication Credentials
     *
     * @var array
     * @access private
     */
    var $auth = array();

    var $decompress = false;
    var $compress   = false;

    /**
     * Default Constructor.
     *
     * $host can either be a string, representing the host, or a stream resource.
     *
     * @param mixed $host
     * @param int $port
     * @param int $timeout
     * @see self::login()
     * @return Net_SSH2
     * @access public
     */
    function __construct($host, $port = 22, $timeout = 10)
    {
        // Include Math_BigInteger
        // Used to do Diffie-Hellman key exchange and DSA/RSA signature verification.
        if (!class_exists('Math_BigInteger')) {
            include_once 'Math/BigInteger.php';
        }

        if (!function_exists('crypt_random_string')) {
            include_once 'Crypt/Random.php';
        }

        if (!class_exists('Crypt_Hash')) {
            include_once 'Crypt/Hash.php';
        }

        // include Crypt_Base so constants can be defined for setCryptoEngine()
        if (!class_exists('Crypt_Base')) {
            include_once 'Crypt/Base.php';
        }

        $this->message_numbers = array(
            1 => 'NET_SSH2_MSG_DISCONNECT',
            2 => 'NET_SSH2_MSG_IGNORE',
            3 => 'NET_SSH2_MSG_UNIMPLEMENTED',
            4 => 'NET_SSH2_MSG_DEBUG',
            5 => 'NET_SSH2_MSG_SERVICE_REQUEST',
            6 => 'NET_SSH2_MSG_SERVICE_ACCEPT',
            20 => 'NET_SSH2_MSG_KEXINIT',
            21 => 'NET_SSH2_MSG_NEWKEYS',
            30 => 'NET_SSH2_MSG_KEXDH_INIT',
            31 => 'NET_SSH2_MSG_KEXDH_REPLY',
            50 => 'NET_SSH2_MSG_USERAUTH_REQUEST',
            51 => 'NET_SSH2_MSG_USERAUTH_FAILURE',
            52 => 'NET_SSH2_MSG_USERAUTH_SUCCESS',
            53 => 'NET_SSH2_MSG_USERAUTH_BANNER',

            80 => 'NET_SSH2_MSG_GLOBAL_REQUEST',
            81 => 'NET_SSH2_MSG_REQUEST_SUCCESS',
            82 => 'NET_SSH2_MSG_REQUEST_FAILURE',
            90 => 'NET_SSH2_MSG_CHANNEL_OPEN',
            91 => 'NET_SSH2_MSG_CHANNEL_OPEN_CONFIRMATION',
            92 => 'NET_SSH2_MSG_CHANNEL_OPEN_FAILURE',
            93 => 'NET_SSH2_MSG_CHANNEL_WINDOW_ADJUST',
            94 => 'NET_SSH2_MSG_CHANNEL_DATA',
            95 => 'NET_SSH2_MSG_CHANNEL_EXTENDED_DATA',
            96 => 'NET_SSH2_MSG_CHANNEL_EOF',
            97 => 'NET_SSH2_MSG_CHANNEL_CLOSE',
            98 => 'NET_SSH2_MSG_CHANNEL_REQUEST',
            99 => 'NET_SSH2_MSG_CHANNEL_SUCCESS',
            100 => 'NET_SSH2_MSG_CHANNEL_FAILURE'
        );
        $this->disconnect_reasons = array(
            1 => 'NET_SSH2_DISCONNECT_HOST_NOT_ALLOWED_TO_CONNECT',
            2 => 'NET_SSH2_DISCONNECT_PROTOCOL_ERROR',
            3 => 'NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED',
            4 => 'NET_SSH2_DISCONNECT_RESERVED',
            5 => 'NET_SSH2_DISCONNECT_MAC_ERROR',
            6 => 'NET_SSH2_DISCONNECT_COMPRESSION_ERROR',
            7 => 'NET_SSH2_DISCONNECT_SERVICE_NOT_AVAILABLE',
            8 => 'NET_SSH2_DISCONNECT_PROTOCOL_VERSION_NOT_SUPPORTED',
            9 => 'NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE',
            10 => 'NET_SSH2_DISCONNECT_CONNECTION_LOST',
            11 => 'NET_SSH2_DISCONNECT_BY_APPLICATION',
            12 => 'NET_SSH2_DISCONNECT_TOO_MANY_CONNECTIONS',
            13 => 'NET_SSH2_DISCONNECT_AUTH_CANCELLED_BY_USER',
            14 => 'NET_SSH2_DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE',
            15 => 'NET_SSH2_DISCONNECT_ILLEGAL_USER_NAME'
        );
        $this->channel_open_failure_reasons = array(
            1 => 'NET_SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED'
        );
        $this->terminal_modes = array(
            0 => 'NET_SSH2_TTY_OP_END'
        );
        $this->channel_extended_data_type_codes = array(
            1 => 'NET_SSH2_EXTENDED_DATA_STDERR'
        );

        $this->_define_array(
            $this->message_numbers,
            $this->disconnect_reasons,
            $this->channel_open_failure_reasons,
            $this->terminal_modes,
            $this->channel_extended_data_type_codes,
            array(60 => 'NET_SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ'),
            array(60 => 'NET_SSH2_MSG_USERAUTH_PK_OK'),
            array(60 => 'NET_SSH2_MSG_USERAUTH_INFO_REQUEST',
                  61 => 'NET_SSH2_MSG_USERAUTH_INFO_RESPONSE'),
            // RFC 4419 - diffie-hellman-group-exchange-sha{1,256}
            array(30 => 'NET_SSH2_MSG_KEXDH_GEX_REQUEST_OLD',
                  31 => 'NET_SSH2_MSG_KEXDH_GEX_GROUP',
                  32 => 'NET_SSH2_MSG_KEXDH_GEX_INIT',
                  33 => 'NET_SSH2_MSG_KEXDH_GEX_REPLY',
                  34 => 'NET_SSH2_MSG_KEXDH_GEX_REQUEST')
        );

        if (is_resource($host)) {
            $this->fsock = $host;
            return;
        }

        if (is_string($host)) {
            $this->host = $host;
            $this->port = $port;
            $this->timeout = $timeout;
        }
    }

    /**
     * PHP4 compatible Default Constructor.
     *
     * @see self::__construct()
     * @param mixed $host
     * @param int $port
     * @param int $timeout
     * @access public
     */
    function Net_SSH2($host, $port = 22, $timeout = 10)
    {
        $this->__construct($host, $port, $timeout);
    }

    /**
     * Set Crypto Engine Mode
     *
     * Possible $engine values:
     * CRYPT_MODE_INTERNAL, CRYPT_MODE_MCRYPT
     *
     * @param int $engine
     * @access public
     */
    function setCryptoEngine($engine)
    {
        $this->crypto_engine = $engine;
    }

    /**
     * Send Identification String First
     *
     * https://tools.ietf.org/html/rfc4253#section-4.2 says "when the connection has been established,
     * both sides MUST send an identification string". It does not say which side sends it first. In
     * theory it shouldn't matter but it is a fact of life that some SSH servers are simply buggy
     *
     * @access public
     */
    function sendIdentificationStringFirst()
    {
        $this->send_id_string_first = true;
    }

    /**
     * Send Identification String Last
     *
     * https://tools.ietf.org/html/rfc4253#section-4.2 says "when the connection has been established,
     * both sides MUST send an identification string". It does not say which side sends it first. In
     * theory it shouldn't matter but it is a fact of life that some SSH servers are simply buggy
     *
     * @access public
     */
    function sendIdentificationStringLast()
    {
        $this->send_id_string_first = false;
    }

    /**
     * Send SSH_MSG_KEXINIT First
     *
     * https://tools.ietf.org/html/rfc4253#section-7.1 says "key exchange begins by each sending
     * sending the [SSH_MSG_KEXINIT] packet". It does not say which side sends it first. In theory
     * it shouldn't matter but it is a fact of life that some SSH servers are simply buggy
     *
     * @access public
     */
    function sendKEXINITFirst()
    {
        $this->send_kex_first = true;
    }

    /**
     * Send SSH_MSG_KEXINIT Last
     *
     * https://tools.ietf.org/html/rfc4253#section-7.1 says "key exchange begins by each sending
     * sending the [SSH_MSG_KEXINIT] packet". It does not say which side sends it first. In theory
     * it shouldn't matter but it is a fact of life that some SSH servers are simply buggy
     *
     * @access public
     */
    function sendKEXINITLast()
    {
        $this->send_kex_first = false;
    }

    /**
     * Connect to an SSHv2 server
     *
     * @return bool
     * @access private
     */
    function _connect()
    {
        if ($this->bitmap & NET_SSH2_MASK_CONSTRUCTOR) {
            return false;
        }

        $this->bitmap |= NET_SSH2_MASK_CONSTRUCTOR;

        $this->curTimeout = $this->timeout;

        $this->last_packet = strtok(microtime(), ' ') + strtok(''); // == microtime(true) in PHP5

        if (!is_resource($this->fsock)) {
            $start = strtok(microtime(), ' ') + strtok(''); // http://php.net/microtime#61838
            // with stream_select a timeout of 0 means that no timeout takes place;
            // with fsockopen a timeout of 0 means that you instantly timeout
            // to resolve this incompatibility a timeout of 100,000 will be used for fsockopen if timeout is 0
            $this->fsock = @fsockopen($this->host, $this->port, $errno, $errstr, $this->curTimeout == 0 ? 100000 : $this->curTimeout);
            if (!$this->fsock) {
                $host = $this->host . ':' . $this->port;
                user_error(rtrim("Cannot connect to $host. Error $errno. $errstr"));
                return false;
            }
            $elapsed = strtok(microtime(), ' ') + strtok('') - $start;

            if ($this->curTimeout) {
                $this->curTimeout-= $elapsed;
                if ($this->curTimeout < 0) {
                    $this->is_timeout = true;
                    return false;
                }
            }
        }

        $this->identifier = $this->_generate_identifier();

        if ($this->send_id_string_first) {
            fputs($this->fsock, $this->identifier . "\r\n");
        }

        /* According to the SSH2 specs,

          "The server MAY send other lines of data before sending the version
           string.  Each line SHOULD be terminated by a Carriage Return and Line
           Feed.  Such lines MUST NOT begin with "SSH-", and SHOULD be encoded
           in ISO-10646 UTF-8 [RFC3629] (language is not specified).  Clients
           MUST be able to process such lines." */
        $temp = '';
        $extra = '';
        while (!feof($this->fsock) && !preg_match('#^SSH-(\d\.\d+)#', $temp, $matches)) {
            if (substr($temp, -2) == "\r\n") {
                $extra.= $temp;
                $temp = '';
            }

            if ($this->curTimeout) {
                if ($this->curTimeout < 0) {
                    $this->is_timeout = true;
                    return false;
                }
                $read = array($this->fsock);
                $write = $except = null;
                $start = strtok(microtime(), ' ') + strtok('');
                $sec = (int)floor($this->curTimeout);
                $usec = (int)(1000000 * ($this->curTimeout - $sec));
                // on windows this returns a "Warning: Invalid CRT parameters detected" error
                // the !count() is done as a workaround for <https://bugs.php.net/42682>
                if (!@stream_select($read, $write, $except, $sec, $usec) && !count($read)) {
                    $this->is_timeout = true;
                    return false;
                }
                $elapsed = strtok(microtime(), ' ') + strtok('') - $start;
                $this->curTimeout-= $elapsed;
            }

            $temp.= fgets($this->fsock, 255);
        }

        if (feof($this->fsock)) {
            $this->bitmap = 0;
            user_error('Connection closed by server');
            return false;
        }

        if (defined('NET_SSH2_LOGGING')) {
            $this->_append_log('<-', $extra . $temp);
            $this->_append_log('->', $this->identifier . "\r\n");
        }

        $this->server_identifier = trim($temp, "\r\n");
        if (strlen($extra)) {
            $this->errors[] = $extra;
        }

        if (version_compare($matches[1], '1.99', '<')) {
            user_error("Cannot connect to SSH $matches[1] servers");
            return false;
        }

        if (!$this->send_id_string_first) {
            fputs($this->fsock, $this->identifier . "\r\n");
        }

        if (!$this->send_kex_first) {
            $response = $this->_get_binary_packet();
            if ($response === false) {
                $this->bitmap = 0;
                user_error('Connection closed by server');
                return false;
            }

            if (!strlen($response) || ord($response[0]) != NET_SSH2_MSG_KEXINIT) {
                user_error('Expected SSH_MSG_KEXINIT');
                return false;
            }

            if (!$this->_key_exchange($response)) {
                return false;
            }
        }

        if ($this->send_kex_first && !$this->_key_exchange()) {
            return false;
        }

        $this->bitmap|= NET_SSH2_MASK_CONNECTED;

        return true;
    }

    /**
     * Generates the SSH identifier
     *
     * You should overwrite this method in your own class if you want to use another identifier
     *
     * @access protected
     * @return string
     */
    function _generate_identifier()
    {
        $identifier = 'SSH-2.0-phpseclib_1.0';

        $ext = array();
        if (extension_loaded('openssl')) {
            $ext[] = 'openssl';
        } elseif (extension_loaded('mcrypt')) {
            $ext[] = 'mcrypt';
        }

        if (extension_loaded('gmp')) {
            $ext[] = 'gmp';
        } elseif (extension_loaded('bcmath')) {
            $ext[] = 'bcmath';
        }

        if (!empty($ext)) {
            $identifier .= ' (' . implode(', ', $ext) . ')';
        }

        return $identifier;
    }

    /**
     * Key Exchange
     *
     * @param string $kexinit_payload_server optional
     * @access private
     */
    function _key_exchange($kexinit_payload_server = false)
    {
        static $kex_algorithms = array(
            'diffie-hellman-group1-sha1', // REQUIRED
            'diffie-hellman-group14-sha1', // REQUIRED
            'diffie-hellman-group-exchange-sha1', // RFC 4419
            'diffie-hellman-group-exchange-sha256', // RFC 4419
        );

        static $server_host_key_algorithms = array(
            'rsa-sha2-256', // RFC 8332
            'rsa-sha2-512', // RFC 8332
            'ssh-rsa', // RECOMMENDED  sign   Raw RSA Key
            'ssh-dss'  // REQUIRED     sign   Raw DSS Key
        );

        static $encryption_algorithms = false;
        if ($encryption_algorithms === false) {
            $encryption_algorithms = array(
                // from <http://tools.ietf.org/html/rfc4345#section-4>:
                'arcfour256',
                'arcfour128',

                //'arcfour',      // OPTIONAL          the ARCFOUR stream cipher with a 128-bit key

                // CTR modes from <http://tools.ietf.org/html/rfc4344#section-4>:
                'aes128-ctr',     // RECOMMENDED       AES (Rijndael) in SDCTR mode, with 128-bit key
                'aes192-ctr',     // RECOMMENDED       AES with 192-bit key
                'aes256-ctr',     // RECOMMENDED       AES with 256-bit key

                'twofish128-ctr', // OPTIONAL          Twofish in SDCTR mode, with 128-bit key
                'twofish192-ctr', // OPTIONAL          Twofish with 192-bit key
                'twofish256-ctr', // OPTIONAL          Twofish with 256-bit key

                'aes128-cbc',     // RECOMMENDED       AES with a 128-bit key
                'aes192-cbc',     // OPTIONAL          AES with a 192-bit key
                'aes256-cbc',     // OPTIONAL          AES in CBC mode, with a 256-bit key

                'twofish128-cbc', // OPTIONAL          Twofish with a 128-bit key
                'twofish192-cbc', // OPTIONAL          Twofish with a 192-bit key
                'twofish256-cbc',
                'twofish-cbc',    // OPTIONAL          alias for "twofish256-cbc"
                                  //                   (this is being retained for historical reasons)

                'blowfish-ctr',   // OPTIONAL          Blowfish in SDCTR mode

                'blowfish-cbc',   // OPTIONAL          Blowfish in CBC mode

                '3des-ctr',       // RECOMMENDED       Three-key 3DES in SDCTR mode

                '3des-cbc',       // REQUIRED          three-key 3DES in CBC mode
                 //'none'         // OPTIONAL          no encryption; NOT RECOMMENDED
            );

            if (extension_loaded('openssl') && !extension_loaded('mcrypt')) {
                // OpenSSL does not support arcfour256 in any capacity and arcfour128 / arcfour support is limited to
                // instances that do not use continuous buffers
                $encryption_algorithms = array_diff(
                    $encryption_algorithms,
                    array('arcfour256', 'arcfour128', 'arcfour')
                );
            }

            if (phpseclib_resolve_include_path('Crypt/RC4.php') === false) {
                $encryption_algorithms = array_diff(
                    $encryption_algorithms,
                    array('arcfour256', 'arcfour128', 'arcfour')
                );
            }
            if (phpseclib_resolve_include_path('Crypt/Rijndael.php') === false) {
                $encryption_algorithms = array_diff(
                    $encryption_algorithms,
                    array('aes128-ctr', 'aes192-ctr', 'aes256-ctr', 'aes128-cbc', 'aes192-cbc', 'aes256-cbc')
                );
            }
            if (phpseclib_resolve_include_path('Crypt/Twofish.php') === false) {
                $encryption_algorithms = array_diff(
                    $encryption_algorithms,
                    array('twofish128-ctr', 'twofish192-ctr', 'twofish256-ctr', 'twofish128-cbc', 'twofish192-cbc', 'twofish256-cbc', 'twofish-cbc')
                );
            }
            if (phpseclib_resolve_include_path('Crypt/Blowfish.php') === false) {
                $encryption_algorithms = array_diff(
                    $encryption_algorithms,
                    array('blowfish-ctr', 'blowfish-cbc')
                );
            }
            if (phpseclib_resolve_include_path('Crypt/TripleDES.php') === false) {
                $encryption_algorithms = array_diff(
                    $encryption_algorithms,
                    array('3des-ctr', '3des-cbc')
                );
            }
            $encryption_algorithms = array_values($encryption_algorithms);
        }

        $mac_algorithms = array(
            // from <http://www.ietf.org/rfc/rfc6668.txt>:
            'hmac-sha2-256',// RECOMMENDED     HMAC-SHA256 (digest length = key length = 32)

            'hmac-sha1-96', // RECOMMENDED     first 96 bits of HMAC-SHA1 (digest length = 12, key length = 20)
            'hmac-sha1',    // REQUIRED        HMAC-SHA1 (digest length = key length = 20)
            'hmac-md5-96',  // OPTIONAL        first 96 bits of HMAC-MD5 (digest length = 12, key length = 16)
            'hmac-md5',     // OPTIONAL        HMAC-MD5 (digest length = key length = 16)
            //'none'          // OPTIONAL        no MAC; NOT RECOMMENDED
        );

        static $compression_algorithms = array(
            'none'   // REQUIRED        no compression
            //'zlib' // OPTIONAL        ZLIB (LZ77) compression
        );

        // some SSH servers have buggy implementations of some of the above algorithms
        switch (true) {
            case $this->server_identifier == 'SSH-2.0-SSHD':
            case substr($this->server_identifier, 0, 13) == 'SSH-2.0-DLINK':
                $mac_algorithms = array_values(array_diff(
                    $mac_algorithms,
                    array('hmac-sha1-96', 'hmac-md5-96')
                ));
        }

        static $str_kex_algorithms, $str_server_host_key_algorithms,
               $encryption_algorithms_server_to_client, $mac_algorithms_server_to_client, $compression_algorithms_server_to_client,
               $encryption_algorithms_client_to_server, $mac_algorithms_client_to_server, $compression_algorithms_client_to_server;

        if (empty($str_kex_algorithms)) {
            $str_kex_algorithms = implode(',', $kex_algorithms);
            $str_server_host_key_algorithms = implode(',', $server_host_key_algorithms);
            $encryption_algorithms_server_to_client = $encryption_algorithms_client_to_server = implode(',', $encryption_algorithms);
            $mac_algorithms_server_to_client = $mac_algorithms_client_to_server = implode(',', $mac_algorithms);
            $compression_algorithms_server_to_client = $compression_algorithms_client_to_server = implode(',', $compression_algorithms);
        }

        $client_cookie = crypt_random_string(16);

        $kexinit_payload_client = pack(
            'Ca*Na*Na*Na*Na*Na*Na*Na*Na*Na*Na*CN',
            NET_SSH2_MSG_KEXINIT,
            $client_cookie,
            strlen($str_kex_algorithms),
            $str_kex_algorithms,
            strlen($str_server_host_key_algorithms),
            $str_server_host_key_algorithms,
            strlen($encryption_algorithms_client_to_server),
            $encryption_algorithms_client_to_server,
            strlen($encryption_algorithms_server_to_client),
            $encryption_algorithms_server_to_client,
            strlen($mac_algorithms_client_to_server),
            $mac_algorithms_client_to_server,
            strlen($mac_algorithms_server_to_client),
            $mac_algorithms_server_to_client,
            strlen($compression_algorithms_client_to_server),
            $compression_algorithms_client_to_server,
            strlen($compression_algorithms_server_to_client),
            $compression_algorithms_server_to_client,
            0,
            '',
            0,
            '',
            0,
            0
        );

        if ($this->send_kex_first) {
            if (!$this->_send_binary_packet($kexinit_payload_client)) {
                return false;
            }

            $kexinit_payload_server = $this->_get_binary_packet();
            if ($kexinit_payload_server === false) {
                $this->bitmap = 0;
                user_error('Connection closed by server');
                return false;
            }

            if (!strlen($kexinit_payload_server) || ord($kexinit_payload_server[0]) != NET_SSH2_MSG_KEXINIT) {
                user_error('Expected SSH_MSG_KEXINIT');
                return false;
            }
        }

        $response = $kexinit_payload_server;
        $this->_string_shift($response, 1); // skip past the message number (it should be SSH_MSG_KEXINIT)
        $server_cookie = $this->_string_shift($response, 16);

        if (strlen($response) < 4) {
            return false;
        }
        $temp = unpack('Nlength', $this->_string_shift($response, 4));
        $this->kex_algorithms = explode(',', $this->_string_shift($response, $temp['length']));

        if (strlen($response) < 4) {
            return false;
        }
        $temp = unpack('Nlength', $this->_string_shift($response, 4));
        $this->server_host_key_algorithms = explode(',', $this->_string_shift($response, $temp['length']));

        if (strlen($response) < 4) {
            return false;
        }
        $temp = unpack('Nlength', $this->_string_shift($response, 4));
        $this->encryption_algorithms_client_to_server = explode(',', $this->_string_shift($response, $temp['length']));

        if (strlen($response) < 4) {
            return false;
        }
        $temp = unpack('Nlength', $this->_string_shift($response, 4));
        $this->encryption_algorithms_server_to_client = explode(',', $this->_string_shift($response, $temp['length']));

        if (strlen($response) < 4) {
            return false;
        }
        $temp = unpack('Nlength', $this->_string_shift($response, 4));
        $this->mac_algorithms_client_to_server = explode(',', $this->_string_shift($response, $temp['length']));

        if (strlen($response) < 4) {
            return false;
        }
        $temp = unpack('Nlength', $this->_string_shift($response, 4));
        $this->mac_algorithms_server_to_client = explode(',', $this->_string_shift($response, $temp['length']));

        if (strlen($response) < 4) {
            return false;
        }
        $temp = unpack('Nlength', $this->_string_shift($response, 4));
        $this->compression_algorithms_client_to_server = explode(',', $this->_string_shift($response, $temp['length']));

        if (strlen($response) < 4) {
            return false;
        }
        $temp = unpack('Nlength', $this->_string_shift($response, 4));
        $this->compression_algorithms_server_to_client = explode(',', $this->_string_shift($response, $temp['length']));

        if (strlen($response) < 4) {
            return false;
        }
        $temp = unpack('Nlength', $this->_string_shift($response, 4));
        $this->languages_client_to_server = explode(',', $this->_string_shift($response, $temp['length']));

        if (strlen($response) < 4) {
            return false;
        }
        $temp = unpack('Nlength', $this->_string_shift($response, 4));
        $this->languages_server_to_client = explode(',', $this->_string_shift($response, $temp['length']));

        if (!strlen($response)) {
            return false;
        }
        extract(unpack('Cfirst_kex_packet_follows', $this->_string_shift($response, 1)));
        $first_kex_packet_follows = $first_kex_packet_follows != 0;

        if (!$this->send_kex_first && !$this->_send_binary_packet($kexinit_payload_client)) {
            return false;
        }

        // we need to decide upon the symmetric encryption algorithms before we do the diffie-hellman key exchange
        // we don't initialize any crypto-objects, yet - we do that, later. for now, we need the lengths to make the
        // diffie-hellman key exchange as fast as possible
        $decrypt = $this->_array_intersect_first($encryption_algorithms, $this->encryption_algorithms_server_to_client);
        $decryptKeyLength = $this->_encryption_algorithm_to_key_size($decrypt);
        if ($decryptKeyLength === null) {
            user_error('No compatible server to client encryption algorithms found');
            return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
        }

        $encrypt = $this->_array_intersect_first($encryption_algorithms, $this->encryption_algorithms_client_to_server);
        $encryptKeyLength = $this->_encryption_algorithm_to_key_size($encrypt);
        if ($encryptKeyLength === null) {
            user_error('No compatible client to server encryption algorithms found');
            return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
        }

        $keyLength = $decryptKeyLength > $encryptKeyLength ? $decryptKeyLength : $encryptKeyLength;

        // through diffie-hellman key exchange a symmetric key is obtained
        $kex_algorithm = $this->_array_intersect_first($kex_algorithms, $this->kex_algorithms);
        if ($kex_algorithm === false) {
            user_error('No compatible key exchange algorithms found');
            return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
        }
        if (strpos($kex_algorithm, 'diffie-hellman-group-exchange') === 0) {
            $dh_group_sizes_packed = pack(
                'NNN',
                $this->kex_dh_group_size_min,
                $this->kex_dh_group_size_preferred,
                $this->kex_dh_group_size_max
            );
            $packet = pack(
                'Ca*',
                NET_SSH2_MSG_KEXDH_GEX_REQUEST,
                $dh_group_sizes_packed
            );
            if (!$this->_send_binary_packet($packet)) {
                return false;
            }

            $response = $this->_get_binary_packet();
            if ($response === false) {
                $this->bitmap = 0;
                user_error('Connection closed by server');
                return false;
            }
            if (!strlen($response)) {
                return false;
            }
            extract(unpack('Ctype', $this->_string_shift($response, 1)));
            if ($type != NET_SSH2_MSG_KEXDH_GEX_GROUP) {
                user_error('Expected SSH_MSG_KEX_DH_GEX_GROUP');
                return false;
            }

            if (strlen($response) < 4) {
                return false;
            }
            extract(unpack('NprimeLength', $this->_string_shift($response, 4)));
            $primeBytes = $this->_string_shift($response, $primeLength);
            $prime = new Math_BigInteger($primeBytes, -256);

            if (strlen($response) < 4) {
                return false;
            }
            extract(unpack('NgLength', $this->_string_shift($response, 4)));
            $gBytes = $this->_string_shift($response, $gLength);
            $g = new Math_BigInteger($gBytes, -256);

            $exchange_hash_rfc4419 = pack(
                'a*Na*Na*',
                $dh_group_sizes_packed,
                $primeLength,
                $primeBytes,
                $gLength,
                $gBytes
            );

            $clientKexInitMessage = NET_SSH2_MSG_KEXDH_GEX_INIT;
            $serverKexReplyMessage = NET_SSH2_MSG_KEXDH_GEX_REPLY;
        } else {
            switch ($kex_algorithm) {
                // see http://tools.ietf.org/html/rfc2409#section-6.2 and
                // http://tools.ietf.org/html/rfc2412, appendex E
                case 'diffie-hellman-group1-sha1':
                    $prime = 'FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74' .
                            '020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F1437' .
                            '4FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED' .
                            'EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381FFFFFFFFFFFFFFFF';
                    break;
                // see http://tools.ietf.org/html/rfc3526#section-3
                case 'diffie-hellman-group14-sha1':
                    $prime = 'FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74' .
                            '020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F1437' .
                            '4FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED' .
                            'EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF05' .
                            '98DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB' .
                            '9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B' .
                            'E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF695581718' .
                            '3995497CEA956AE515D2261898FA051015728E5A8AACAA68FFFFFFFFFFFFFFFF';
                    break;
            }
            // For both diffie-hellman-group1-sha1 and diffie-hellman-group14-sha1
            // the generator field element is 2 (decimal) and the hash function is sha1.
            $g = new Math_BigInteger(2);
            $prime = new Math_BigInteger($prime, 16);
            $exchange_hash_rfc4419 = '';
            $clientKexInitMessage = NET_SSH2_MSG_KEXDH_INIT;
            $serverKexReplyMessage = NET_SSH2_MSG_KEXDH_REPLY;
        }

        switch ($kex_algorithm) {
            case 'diffie-hellman-group-exchange-sha256':
                $kexHash = new Crypt_Hash('sha256');
                break;
            default:
                $kexHash = new Crypt_Hash('sha1');
        }

        /* To increase the speed of the key exchange, both client and server may
           reduce the size of their private exponents.  It should be at least
           twice as long as the key material that is generated from the shared
           secret.  For more details, see the paper by van Oorschot and Wiener
           [VAN-OORSCHOT].

           -- http://tools.ietf.org/html/rfc4419#section-6.2 */
        $one = new Math_BigInteger(1);
        $keyLength = min($keyLength, $kexHash->getLength());
        $max = $one->bitwise_leftShift(16 * $keyLength); // 2 * 8 * $keyLength
        $max = $max->subtract($one);

        $x = $one->random($one, $max);
        $e = $g->modPow($x, $prime);

        $eBytes = $e->toBytes(true);
        $data = pack('CNa*', $clientKexInitMessage, strlen($eBytes), $eBytes);

        if (!$this->_send_binary_packet($data)) {
            $this->bitmap = 0;
            user_error('Connection closed by server');
            return false;
        }

        $response = $this->_get_binary_packet();
        if ($response === false) {
            $this->bitmap = 0;
            user_error('Connection closed by server');
            return false;
        }
        if (!strlen($response)) {
            return false;
        }
        extract(unpack('Ctype', $this->_string_shift($response, 1)));

        if ($type != $serverKexReplyMessage) {
            user_error('Expected SSH_MSG_KEXDH_REPLY');
            return false;
        }

        if (strlen($response) < 4) {
            return false;
        }
        $temp = unpack('Nlength', $this->_string_shift($response, 4));
        $this->server_public_host_key = $server_public_host_key = $this->_string_shift($response, $temp['length']);

        if (strlen($server_public_host_key) < 4) {
            return false;
        }
        $temp = unpack('Nlength', $this->_string_shift($server_public_host_key, 4));
        $public_key_format = $this->_string_shift($server_public_host_key, $temp['length']);

        if (strlen($response) < 4) {
            return false;
        }
        $temp = unpack('Nlength', $this->_string_shift($response, 4));
        $fBytes = $this->_string_shift($response, $temp['length']);
        $f = new Math_BigInteger($fBytes, -256);

        if (strlen($response) < 4) {
            return false;
        }
        $temp = unpack('Nlength', $this->_string_shift($response, 4));
        $this->signature = $this->_string_shift($response, $temp['length']);

        if (strlen($this->signature) < 4) {
            return false;
        }
        $temp = unpack('Nlength', $this->_string_shift($this->signature, 4));
        $this->signature_format = $this->_string_shift($this->signature, $temp['length']);

        $key = $f->modPow($x, $prime);
        $keyBytes = $key->toBytes(true);

        $this->exchange_hash = pack(
            'Na*Na*Na*Na*Na*a*Na*Na*Na*',
            strlen($this->identifier),
            $this->identifier,
            strlen($this->server_identifier),
            $this->server_identifier,
            strlen($kexinit_payload_client),
            $kexinit_payload_client,
            strlen($kexinit_payload_server),
            $kexinit_payload_server,
            strlen($this->server_public_host_key),
            $this->server_public_host_key,
            $exchange_hash_rfc4419,
            strlen($eBytes),
            $eBytes,
            strlen($fBytes),
            $fBytes,
            strlen($keyBytes),
            $keyBytes
        );

        $this->exchange_hash = $kexHash->hash($this->exchange_hash);

        if ($this->session_id === false) {
            $this->session_id = $this->exchange_hash;
        }

        $server_host_key_algorithm = $this->_array_intersect_first($server_host_key_algorithms, $this->server_host_key_algorithms);
        if ($server_host_key_algorithm === false) {
            user_error('No compatible server host key algorithms found');
            return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
        }

        switch ($server_host_key_algorithm) {
            case 'ssh-dss':
                $expected_key_format = 'ssh-dss';
                break;
            //case 'rsa-sha2-256':
            //case 'rsa-sha2-512':
            //case 'ssh-rsa':
            default:
                $expected_key_format = 'ssh-rsa';
        }

        if ($public_key_format != $expected_key_format || $this->signature_format != $server_host_key_algorithm) {
            switch (true) {
                case $this->signature_format == $server_host_key_algorithm:
                case $server_host_key_algorithm != 'rsa-sha2-256' && $server_host_key_algorithm != 'rsa-sha2-512':
                case $this->signature_format != 'ssh-rsa':
                    user_error('Server Host Key Algorithm Mismatch');
                    return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
            }
        }

        $packet = pack(
            'C',
            NET_SSH2_MSG_NEWKEYS
        );

        if (!$this->_send_binary_packet($packet)) {
            return false;
        }

        $response = $this->_get_binary_packet();

        if ($response === false) {
            $this->bitmap = 0;
            user_error('Connection closed by server');
            return false;
        }

        if (!strlen($response)) {
            return false;
        }
        extract(unpack('Ctype', $this->_string_shift($response, 1)));

        if ($type != NET_SSH2_MSG_NEWKEYS) {
            user_error('Expected SSH_MSG_NEWKEYS');
            return false;
        }

        switch ($encrypt) {
            case '3des-cbc':
                if (!class_exists('Crypt_TripleDES')) {
                    include_once 'Crypt/TripleDES.php';
                }
                $this->encrypt = new Crypt_TripleDES();
                // $this->encrypt_block_size = 64 / 8 == the default
                break;
            case '3des-ctr':
                if (!class_exists('Crypt_TripleDES')) {
                    include_once 'Crypt/TripleDES.php';
                }
                $this->encrypt = new Crypt_TripleDES(CRYPT_DES_MODE_CTR);
                // $this->encrypt_block_size = 64 / 8 == the default
                break;
            case 'aes256-cbc':
            case 'aes192-cbc':
            case 'aes128-cbc':
                if (!class_exists('Crypt_Rijndael')) {
                    include_once 'Crypt/Rijndael.php';
                }
                $this->encrypt = new Crypt_Rijndael();
                $this->encrypt_block_size = 16; // eg. 128 / 8
                break;
            case 'aes256-ctr':
            case 'aes192-ctr':
            case 'aes128-ctr':
                if (!class_exists('Crypt_Rijndael')) {
                    include_once 'Crypt/Rijndael.php';
                }
                $this->encrypt = new Crypt_Rijndael(CRYPT_RIJNDAEL_MODE_CTR);
                $this->encrypt_block_size = 16; // eg. 128 / 8
                break;
            case 'blowfish-cbc':
                if (!class_exists('Crypt_Blowfish')) {
                    include_once 'Crypt/Blowfish.php';
                }
                $this->encrypt = new Crypt_Blowfish();
                $this->encrypt_block_size = 8;
                break;
            case 'blowfish-ctr':
                if (!class_exists('Crypt_Blowfish')) {
                    include_once 'Crypt/Blowfish.php';
                }
                $this->encrypt = new Crypt_Blowfish(CRYPT_BLOWFISH_MODE_CTR);
                $this->encrypt_block_size = 8;
                break;
            case 'twofish128-cbc':
            case 'twofish192-cbc':
            case 'twofish256-cbc':
            case 'twofish-cbc':
                if (!class_exists('Crypt_Twofish')) {
                    include_once 'Crypt/Twofish.php';
                }
                $this->encrypt = new Crypt_Twofish();
                $this->encrypt_block_size = 16;
                break;
            case 'twofish128-ctr':
            case 'twofish192-ctr':
            case 'twofish256-ctr':
                if (!class_exists('Crypt_Twofish')) {
                    include_once 'Crypt/Twofish.php';
                }
                $this->encrypt = new Crypt_Twofish(CRYPT_TWOFISH_MODE_CTR);
                $this->encrypt_block_size = 16;
                break;
            case 'arcfour':
            case 'arcfour128':
            case 'arcfour256':
                if (!class_exists('Crypt_RC4')) {
                    include_once 'Crypt/RC4.php';
                }
                $this->encrypt = new Crypt_RC4();
                break;
            case 'none':
                //$this->encrypt = new Crypt_Null();
        }

        switch ($decrypt) {
            case '3des-cbc':
                if (!class_exists('Crypt_TripleDES')) {
                    include_once 'Crypt/TripleDES.php';
                }
                $this->decrypt = new Crypt_TripleDES();
                break;
            case '3des-ctr':
                if (!class_exists('Crypt_TripleDES')) {
                    include_once 'Crypt/TripleDES.php';
                }
                $this->decrypt = new Crypt_TripleDES(CRYPT_DES_MODE_CTR);
                break;
            case 'aes256-cbc':
            case 'aes192-cbc':
            case 'aes128-cbc':
                if (!class_exists('Crypt_Rijndael')) {
                    include_once 'Crypt/Rijndael.php';
                }
                $this->decrypt = new Crypt_Rijndael();
                $this->decrypt_block_size = 16;
                break;
            case 'aes256-ctr':
            case 'aes192-ctr':
            case 'aes128-ctr':
                if (!class_exists('Crypt_Rijndael')) {
                    include_once 'Crypt/Rijndael.php';
                }
                $this->decrypt = new Crypt_Rijndael(CRYPT_RIJNDAEL_MODE_CTR);
                $this->decrypt_block_size = 16;
                break;
            case 'blowfish-cbc':
                if (!class_exists('Crypt_Blowfish')) {
                    include_once 'Crypt/Blowfish.php';
                }
                $this->decrypt = new Crypt_Blowfish();
                $this->decrypt_block_size = 8;
                break;
            case 'blowfish-ctr':
                if (!class_exists('Crypt_Blowfish')) {
                    include_once 'Crypt/Blowfish.php';
                }
                $this->decrypt = new Crypt_Blowfish(CRYPT_BLOWFISH_MODE_CTR);
                $this->decrypt_block_size = 8;
                break;
            case 'twofish128-cbc':
            case 'twofish192-cbc':
            case 'twofish256-cbc':
            case 'twofish-cbc':
                if (!class_exists('Crypt_Twofish')) {
                    include_once 'Crypt/Twofish.php';
                }
                $this->decrypt = new Crypt_Twofish();
                $this->decrypt_block_size = 16;
                break;
            case 'twofish128-ctr':
            case 'twofish192-ctr':
            case 'twofish256-ctr':
                if (!class_exists('Crypt_Twofish')) {
                    include_once 'Crypt/Twofish.php';
                }
                $this->decrypt = new Crypt_Twofish(CRYPT_TWOFISH_MODE_CTR);
                $this->decrypt_block_size = 16;
                break;
            case 'arcfour':
            case 'arcfour128':
            case 'arcfour256':
                if (!class_exists('Crypt_RC4')) {
                    include_once 'Crypt/RC4.php';
                }
                $this->decrypt = new Crypt_RC4();
                break;
            case 'none':
                //$this->decrypt = new Crypt_Null();
        }

        $this->decrypt_algorithm = $decrypt;

        $keyBytes = pack('Na*', strlen($keyBytes), $keyBytes);

        if ($this->encrypt) {
            if ($this->crypto_engine) {
                $this->encrypt->setPreferredEngine($this->crypto_engine);
            }
            $this->encrypt->enableContinuousBuffer();
            $this->encrypt->disablePadding();

            $iv = $kexHash->hash($keyBytes . $this->exchange_hash . 'A' . $this->session_id);
            while ($this->encrypt_block_size > strlen($iv)) {
                $iv.= $kexHash->hash($keyBytes . $this->exchange_hash . $iv);
            }
            $this->encrypt->setIV(substr($iv, 0, $this->encrypt_block_size));

            $key = $kexHash->hash($keyBytes . $this->exchange_hash . 'C' . $this->session_id);
            while ($encryptKeyLength > strlen($key)) {
                $key.= $kexHash->hash($keyBytes . $this->exchange_hash . $key);
            }
            $this->encrypt->setKey(substr($key, 0, $encryptKeyLength));
        }

        if ($this->decrypt) {
            if ($this->crypto_engine) {
                $this->decrypt->setPreferredEngine($this->crypto_engine);
            }
            $this->decrypt->enableContinuousBuffer();
            $this->decrypt->disablePadding();

            $iv = $kexHash->hash($keyBytes . $this->exchange_hash . 'B' . $this->session_id);
            while ($this->decrypt_block_size > strlen($iv)) {
                $iv.= $kexHash->hash($keyBytes . $this->exchange_hash . $iv);
            }
            $this->decrypt->setIV(substr($iv, 0, $this->decrypt_block_size));

            $key = $kexHash->hash($keyBytes . $this->exchange_hash . 'D' . $this->session_id);
            while ($decryptKeyLength > strlen($key)) {
                $key.= $kexHash->hash($keyBytes . $this->exchange_hash . $key);
            }
            $this->decrypt->setKey(substr($key, 0, $decryptKeyLength));
        }

        /* The "arcfour128" algorithm is the RC4 cipher, as described in
           [SCHNEIER], using a 128-bit key.  The first 1536 bytes of keystream
           generated by the cipher MUST be discarded, and the first byte of the
           first encrypted packet MUST be encrypted using the 1537th byte of
           keystream.

           -- http://tools.ietf.org/html/rfc4345#section-4 */
        if ($encrypt == 'arcfour128' || $encrypt == 'arcfour256') {
            $this->encrypt->encrypt(str_repeat("\0", 1536));
        }
        if ($decrypt == 'arcfour128' || $decrypt == 'arcfour256') {
            $this->decrypt->decrypt(str_repeat("\0", 1536));
        }

        $mac_algorithm = $this->_array_intersect_first($mac_algorithms, $this->mac_algorithms_client_to_server);
        if ($mac_algorithm === false) {
            user_error('No compatible client to server message authentication algorithms found');
            return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
        }

        $createKeyLength = 0; // ie. $mac_algorithm == 'none'
        switch ($mac_algorithm) {
            case 'hmac-sha2-256':
                $this->hmac_create = new Crypt_Hash('sha256');
                $createKeyLength = 32;
                break;
            case 'hmac-sha1':
                $this->hmac_create = new Crypt_Hash('sha1');
                $createKeyLength = 20;
                break;
            case 'hmac-sha1-96':
                $this->hmac_create = new Crypt_Hash('sha1-96');
                $createKeyLength = 20;
                break;
            case 'hmac-md5':
                $this->hmac_create = new Crypt_Hash('md5');
                $createKeyLength = 16;
                break;
            case 'hmac-md5-96':
                $this->hmac_create = new Crypt_Hash('md5-96');
                $createKeyLength = 16;
        }

        $mac_algorithm = $this->_array_intersect_first($mac_algorithms, $this->mac_algorithms_server_to_client);
        if ($mac_algorithm === false) {
            user_error('No compatible server to client message authentication algorithms found');
            return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
        }

        $checkKeyLength = 0;
        $this->hmac_size = 0;
        switch ($mac_algorithm) {
            case 'hmac-sha2-256':
                $this->hmac_check = new Crypt_Hash('sha256');
                $checkKeyLength = 32;
                $this->hmac_size = 32;
                break;
            case 'hmac-sha1':
                $this->hmac_check = new Crypt_Hash('sha1');
                $checkKeyLength = 20;
                $this->hmac_size = 20;
                break;
            case 'hmac-sha1-96':
                $this->hmac_check = new Crypt_Hash('sha1-96');
                $checkKeyLength = 20;
                $this->hmac_size = 12;
                break;
            case 'hmac-md5':
                $this->hmac_check = new Crypt_Hash('md5');
                $checkKeyLength = 16;
                $this->hmac_size = 16;
                break;
            case 'hmac-md5-96':
                $this->hmac_check = new Crypt_Hash('md5-96');
                $checkKeyLength = 16;
                $this->hmac_size = 12;
        }

        $key = $kexHash->hash($keyBytes . $this->exchange_hash . 'E' . $this->session_id);
        while ($createKeyLength > strlen($key)) {
            $key.= $kexHash->hash($keyBytes . $this->exchange_hash . $key);
        }
        $this->hmac_create->setKey(substr($key, 0, $createKeyLength));

        $key = $kexHash->hash($keyBytes . $this->exchange_hash . 'F' . $this->session_id);
        while ($checkKeyLength > strlen($key)) {
            $key.= $kexHash->hash($keyBytes . $this->exchange_hash . $key);
        }
        $this->hmac_check->setKey(substr($key, 0, $checkKeyLength));

        $compression_algorithm = $this->_array_intersect_first($compression_algorithms, $this->compression_algorithms_server_to_client);
        if ($compression_algorithm === false) {
            user_error('No compatible server to client compression algorithms found');
            return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
        }
        $this->decompress = $compression_algorithm == 'zlib';

        $compression_algorithm = $this->_array_intersect_first($compression_algorithms, $this->compression_algorithms_client_to_server);
        if ($compression_algorithm === false) {
            user_error('No compatible client to server compression algorithms found');
            return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
        }
        $this->compress = $compression_algorithm == 'zlib';

        return true;
    }

    /**
     * Maps an encryption algorithm name to the number of key bytes.
     *
     * @param string $algorithm Name of the encryption algorithm
     * @return int|null Number of bytes as an integer or null for unknown
     * @access private
     */
    function _encryption_algorithm_to_key_size($algorithm)
    {
        if ($this->bad_key_size_fix && $this->_bad_algorithm_candidate($algorithm)) {
            return 16;
        }

        switch ($algorithm) {
            case 'none':
                return 0;
            case 'aes128-cbc':
            case 'aes128-ctr':
            case 'arcfour':
            case 'arcfour128':
            case 'blowfish-cbc':
            case 'blowfish-ctr':
            case 'twofish128-cbc':
            case 'twofish128-ctr':
                return 16;
            case '3des-cbc':
            case '3des-ctr':
            case 'aes192-cbc':
            case 'aes192-ctr':
            case 'twofish192-cbc':
            case 'twofish192-ctr':
                return 24;
            case 'aes256-cbc':
            case 'aes256-ctr':
            case 'arcfour256':
            case 'twofish-cbc':
            case 'twofish256-cbc':
            case 'twofish256-ctr':
                return 32;
        }
        return null;
    }

    /**
     * Tests whether or not proposed algorithm has a potential for issues
     *
     * @link https://www.chiark.greenend.org.uk/~sgtatham/putty/wishlist/ssh2-aesctr-openssh.html
     * @link https://bugzilla.mindrot.org/show_bug.cgi?id=1291
     * @param string $algorithm Name of the encryption algorithm
     * @return bool
     * @access private
     */
    function _bad_algorithm_candidate($algorithm)
    {
        switch ($algorithm) {
            case 'arcfour256':
            case 'aes192-ctr':
            case 'aes256-ctr':
                return true;
        }

        return false;
    }

    /**
     * Login
     *
     * The $password parameter can be a plaintext password, a Crypt_RSA object or an array
     *
     * @param string $username
     * @param mixed $password
     * @param mixed $...
     * @return bool
     * @see self::_login()
     * @access public
     */
    function login($username)
    {
        $args = func_get_args();
        $this->auth[] = $args;
        return call_user_func_array(array(&$this, '_login'), $args);
    }

    /**
     * Login Helper
     *
     * @param string $username
     * @param mixed $password
     * @param mixed $...
     * @return bool
     * @see self::_login_helper()
     * @access private
     */
    function _login($username)
    {
        if (!($this->bitmap & NET_SSH2_MASK_CONSTRUCTOR)) {
            if (!$this->_connect()) {
                return false;
            }
        }

        $args = array_slice(func_get_args(), 1);
        if (empty($args)) {
            return $this->_login_helper($username);
        }

        foreach ($args as $arg) {
            if ($this->_login_helper($username, $arg)) {
                return true;
            }
        }
        return false;
    }

    /**
     * Login Helper
     *
     * @param string $username
     * @param string $password
     * @return bool
     * @access private
     * @internal It might be worthwhile, at some point, to protect against {@link http://tools.ietf.org/html/rfc4251#section-9.3.9 traffic analysis}
     *           by sending dummy SSH_MSG_IGNORE messages.
     */
    function _login_helper($username, $password = null)
    {
        if (!($this->bitmap & NET_SSH2_MASK_CONNECTED)) {
            return false;
        }

        if (!($this->bitmap & NET_SSH2_MASK_LOGIN_REQ)) {
            $packet = pack(
                'CNa*',
                NET_SSH2_MSG_SERVICE_REQUEST,
                strlen('ssh-userauth'),
                'ssh-userauth'
            );

            if (!$this->_send_binary_packet($packet)) {
                return false;
            }

            $response = $this->_get_binary_packet();
            if ($response === false) {
                if ($this->retry_connect) {
                    $this->retry_connect = false;
                    if (!$this->_connect()) {
                        return false;
                    }
                    return $this->_login_helper($username, $password);
                }
                $this->bitmap = 0;
                user_error('Connection closed by server');
                return false;
            }

            if (strlen($response) < 4) {
                return false;
            }
            extract(unpack('Ctype', $this->_string_shift($response, 1)));

            if ($type != NET_SSH2_MSG_SERVICE_ACCEPT) {
                user_error('Expected SSH_MSG_SERVICE_ACCEPT');
                return false;
            }
            $this->bitmap |= NET_SSH2_MASK_LOGIN_REQ;
        }

        if (strlen($this->last_interactive_response)) {
            return !is_string($password) && !is_array($password) ? false : $this->_keyboard_interactive_process($password);
        }

        // although PHP5's get_class() preserves the case, PHP4's does not
        if (is_object($password)) {
            switch (strtolower(get_class($password))) {
                case 'crypt_rsa':
                    return $this->_privatekey_login($username, $password);
                case 'system_ssh_agent':
                    return $this->_ssh_agent_login($username, $password);
            }
        }

        if (is_array($password)) {
            if ($this->_keyboard_interactive_login($username, $password)) {
                $this->bitmap |= NET_SSH2_MASK_LOGIN;
                return true;
            }
            return false;
        }

        if (!isset($password)) {
            $packet = pack(
                'CNa*Na*Na*',
                NET_SSH2_MSG_USERAUTH_REQUEST,
                strlen($username),
                $username,
                strlen('ssh-connection'),
                'ssh-connection',
                strlen('none'),
                'none'
            );

            if (!$this->_send_binary_packet($packet)) {
                return false;
            }

            $response = $this->_get_binary_packet();
            if ($response === false) {
                $this->bitmap = 0;
                user_error('Connection closed by server');
                return false;
            }

            if (!strlen($response)) {
                return false;
            }
            extract(unpack('Ctype', $this->_string_shift($response, 1)));

            switch ($type) {
                case NET_SSH2_MSG_USERAUTH_SUCCESS:
                    $this->bitmap |= NET_SSH2_MASK_LOGIN;
                    return true;
                //case NET_SSH2_MSG_USERAUTH_FAILURE:
                default:
                    return false;
            }
        }

        $packet = pack(
            'CNa*Na*Na*CNa*',
            NET_SSH2_MSG_USERAUTH_REQUEST,
            strlen($username),
            $username,
            strlen('ssh-connection'),
            'ssh-connection',
            strlen('password'),
            'password',
            0,
            strlen($password),
            $password
        );

        // remove the username and password from the logged packet
        if (!defined('NET_SSH2_LOGGING')) {
            $logged = null;
        } else {
            $logged = pack(
                'CNa*Na*Na*CNa*',
                NET_SSH2_MSG_USERAUTH_REQUEST,
                strlen('username'),
                'username',
                strlen('ssh-connection'),
                'ssh-connection',
                strlen('password'),
                'password',
                0,
                strlen('password'),
                'password'
            );
        }

        if (!$this->_send_binary_packet($packet, $logged)) {
            return false;
        }

        $response = $this->_get_binary_packet();
        if ($response === false) {
            $this->bitmap = 0;
            user_error('Connection closed by server');
            return false;
        }

        if (!strlen($response)) {
            return false;
        }
        extract(unpack('Ctype', $this->_string_shift($response, 1)));

        switch ($type) {
            case NET_SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ: // in theory, the password can be changed
                if (defined('NET_SSH2_LOGGING')) {
                    $this->message_number_log[count($this->message_number_log) - 1] = 'NET_SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ';
                }
                if (strlen($response) < 4) {
                    return false;
                }
                extract(unpack('Nlength', $this->_string_shift($response, 4)));
                $this->errors[] = 'SSH_MSG_USERAUTH_PASSWD_CHANGEREQ: ' . $this->_string_shift($response, $length);
                return $this->_disconnect(NET_SSH2_DISCONNECT_AUTH_CANCELLED_BY_USER);
            case NET_SSH2_MSG_USERAUTH_FAILURE:
                // can we use keyboard-interactive authentication?  if not then either the login is bad or the server employees
                // multi-factor authentication
                if (strlen($response) < 4) {
                    return false;
                }
                extract(unpack('Nlength', $this->_string_shift($response, 4)));
                $auth_methods = explode(',', $this->_string_shift($response, $length));
                if (!strlen($response)) {
                    return false;
                }
                extract(unpack('Cpartial_success', $this->_string_shift($response, 1)));
                $partial_success = $partial_success != 0;

                if (!$partial_success && in_array('keyboard-interactive', $auth_methods)) {
                    if ($this->_keyboard_interactive_login($username, $password)) {
                        $this->bitmap |= NET_SSH2_MASK_LOGIN;
                        return true;
                    }
                    return false;
                }
                return false;
            case NET_SSH2_MSG_USERAUTH_SUCCESS:
                $this->bitmap |= NET_SSH2_MASK_LOGIN;
                return true;
        }

        return false;
    }

    /**
     * Login via keyboard-interactive authentication
     *
     * See {@link http://tools.ietf.org/html/rfc4256 RFC4256} for details.  This is not a full-featured keyboard-interactive authenticator.
     *
     * @param string $username
     * @param string $password
     * @return bool
     * @access private
     */
    function _keyboard_interactive_login($username, $password)
    {
        $packet = pack(
            'CNa*Na*Na*Na*Na*',
            NET_SSH2_MSG_USERAUTH_REQUEST,
            strlen($username),
            $username,
            strlen('ssh-connection'),
            'ssh-connection',
            strlen('keyboard-interactive'),
            'keyboard-interactive',
            0,
            '',
            0,
            ''
        );

        if (!$this->_send_binary_packet($packet)) {
            return false;
        }

        return $this->_keyboard_interactive_process($password);
    }

    /**
     * Handle the keyboard-interactive requests / responses.
     *
     * @param string $responses...
     * @return bool
     * @access private
     */
    function _keyboard_interactive_process()
    {
        $responses = func_get_args();

        if (strlen($this->last_interactive_response)) {
            $response = $this->last_interactive_response;
        } else {
            $orig = $response = $this->_get_binary_packet();
            if ($response === false) {
                $this->bitmap = 0;
                user_error('Connection closed by server');
                return false;
            }
        }

        if (!strlen($response)) {
            return false;
        }
        extract(unpack('Ctype', $this->_string_shift($response, 1)));

        switch ($type) {
            case NET_SSH2_MSG_USERAUTH_INFO_REQUEST:
                if (strlen($response) < 4) {
                    return false;
                }
                extract(unpack('Nlength', $this->_string_shift($response, 4)));
                $this->_string_shift($response, $length); // name; may be empty
                if (strlen($response) < 4) {
                    return false;
                }
                extract(unpack('Nlength', $this->_string_shift($response, 4)));
                $this->_string_shift($response, $length); // instruction; may be empty
                if (strlen($response) < 4) {
                    return false;
                }
                extract(unpack('Nlength', $this->_string_shift($response, 4)));
                $this->_string_shift($response, $length); // language tag; may be empty
                if (strlen($response) < 4) {
                    return false;
                }
                extract(unpack('Nnum_prompts', $this->_string_shift($response, 4)));

                for ($i = 0; $i < count($responses); $i++) {
                    if (is_array($responses[$i])) {
                        foreach ($responses[$i] as $key => $value) {
                            $this->keyboard_requests_responses[$key] = $value;
                        }
                        unset($responses[$i]);
                    }
                }
                $responses = array_values($responses);

                if (isset($this->keyboard_requests_responses)) {
                    for ($i = 0; $i < $num_prompts; $i++) {
                        if (strlen($response) < 4) {
                            return false;
                        }
                        extract(unpack('Nlength', $this->_string_shift($response, 4)));
                        // prompt - ie. "Password: "; must not be empty
                        $prompt = $this->_string_shift($response, $length);
                        //$echo = $this->_string_shift($response) != chr(0);
                        foreach ($this->keyboard_requests_responses as $key => $value) {
                            if (substr($prompt, 0, strlen($key)) == $key) {
                                $responses[] = $value;
                                break;
                            }
                        }
                    }
                }

                // see http://tools.ietf.org/html/rfc4256#section-3.2
                if (strlen($this->last_interactive_response)) {
                    $this->last_interactive_response = '';
                } elseif (defined('NET_SSH2_LOGGING')) {
                    $this->message_number_log[count($this->message_number_log) - 1] = str_replace(
                        'UNKNOWN',
                        'NET_SSH2_MSG_USERAUTH_INFO_REQUEST',
                        $this->message_number_log[count($this->message_number_log) - 1]
                    );
                }

                if (!count($responses) && $num_prompts) {
                    $this->last_interactive_response = $orig;
                    return false;
                }

                /*
                   After obtaining the requested information from the user, the client
                   MUST respond with an SSH_MSG_USERAUTH_INFO_RESPONSE message.
                */
                // see http://tools.ietf.org/html/rfc4256#section-3.4
                $packet = $logged = pack('CN', NET_SSH2_MSG_USERAUTH_INFO_RESPONSE, count($responses));
                for ($i = 0; $i < count($responses); $i++) {
                    $packet.= pack('Na*', strlen($responses[$i]), $responses[$i]);
                    $logged.= pack('Na*', strlen('dummy-answer'), 'dummy-answer');
                }

                if (!$this->_send_binary_packet($packet, $logged)) {
                    return false;
                }

                if (defined('NET_SSH2_LOGGING') && NET_SSH2_LOGGING == NET_SSH2_LOG_COMPLEX) {
                    $this->message_number_log[count($this->message_number_log) - 1] = str_replace(
                        'UNKNOWN',
                        'NET_SSH2_MSG_USERAUTH_INFO_RESPONSE',
                        $this->message_number_log[count($this->message_number_log) - 1]
                    );
                }

                /*
                   After receiving the response, the server MUST send either an
                   SSH_MSG_USERAUTH_SUCCESS, SSH_MSG_USERAUTH_FAILURE, or another
                   SSH_MSG_USERAUTH_INFO_REQUEST message.
                */
                // maybe phpseclib should force close the connection after x request / responses?  unless something like that is done
                // there could be an infinite loop of request / responses.
                return $this->_keyboard_interactive_process();
            case NET_SSH2_MSG_USERAUTH_SUCCESS:
                return true;
            case NET_SSH2_MSG_USERAUTH_FAILURE:
                return false;
        }

        return false;
    }

    /**
     * Login with an ssh-agent provided key
     *
     * @param string $username
     * @param System_SSH_Agent $agent
     * @return bool
     * @access private
     */
    function _ssh_agent_login($username, $agent)
    {
        $this->agent = $agent;
        $keys = $agent->requestIdentities();
        foreach ($keys as $key) {
            if ($this->_privatekey_login($username, $key)) {
                return true;
            }
        }

        return false;
    }

    /**
     * Login with an RSA private key
     *
     * @param string $username
     * @param Crypt_RSA $password
     * @return bool
     * @access private
     * @internal It might be worthwhile, at some point, to protect against {@link http://tools.ietf.org/html/rfc4251#section-9.3.9 traffic analysis}
     *           by sending dummy SSH_MSG_IGNORE messages.
     */
    function _privatekey_login($username, $privatekey)
    {
        // see http://tools.ietf.org/html/rfc4253#page-15
        $publickey = $privatekey->getPublicKey(CRYPT_RSA_PUBLIC_FORMAT_RAW);
        if ($publickey === false) {
            return false;
        }

        $publickey = array(
            'e' => $publickey['e']->toBytes(true),
            'n' => $publickey['n']->toBytes(true)
        );
        $publickey = pack(
            'Na*Na*Na*',
            strlen('ssh-rsa'),
            'ssh-rsa',
            strlen($publickey['e']),
            $publickey['e'],
            strlen($publickey['n']),
            $publickey['n']
        );

        switch ($this->signature_format) {
            case 'rsa-sha2-512':
                $hash = 'sha512';
                $signatureType = 'rsa-sha2-512';
                break;
            case 'rsa-sha2-256':
                $hash = 'sha256';
                $signatureType = 'rsa-sha2-256';
                break;
            //case 'ssh-rsa':
            default:
                $hash = 'sha1';
                $signatureType = 'ssh-rsa';
        }

        $part1 = pack(
            'CNa*Na*Na*',
            NET_SSH2_MSG_USERAUTH_REQUEST,
            strlen($username),
            $username,
            strlen('ssh-connection'),
            'ssh-connection',
            strlen('publickey'),
            'publickey'
        );
        $part2 = pack('Na*Na*', strlen($signatureType), $signatureType, strlen($publickey), $publickey);

        $packet = $part1 . chr(0) . $part2;
        if (!$this->_send_binary_packet($packet)) {
            return false;
        }

        $response = $this->_get_binary_packet();
        if ($response === false) {
            $this->bitmap = 0;
            user_error('Connection closed by server');
            return false;
        }

        if (!strlen($response)) {
            return false;
        }
        extract(unpack('Ctype', $this->_string_shift($response, 1)));

        switch ($type) {
            case NET_SSH2_MSG_USERAUTH_FAILURE:
                if (strlen($response) < 4) {
                    return false;
                }
                extract(unpack('Nlength', $this->_string_shift($response, 4)));
                $this->errors[] = 'SSH_MSG_USERAUTH_FAILURE: ' . $this->_string_shift($response, $length);
                return false;
            case NET_SSH2_MSG_USERAUTH_PK_OK:
                // we'll just take it on faith that the public key blob and the public key algorithm name are as
                // they should be
                if (defined('NET_SSH2_LOGGING') && NET_SSH2_LOGGING == NET_SSH2_LOG_COMPLEX) {
                    $this->message_number_log[count($this->message_number_log) - 1] = str_replace(
                        'UNKNOWN',
                        'NET_SSH2_MSG_USERAUTH_PK_OK',
                        $this->message_number_log[count($this->message_number_log) - 1]
                    );
                }
        }

        $packet = $part1 . chr(1) . $part2;
        $privatekey->setSignatureMode(CRYPT_RSA_SIGNATURE_PKCS1);
        $privatekey->setHash($hash);
        $signature = $privatekey->sign(pack('Na*a*', strlen($this->session_id), $this->session_id, $packet));
        $signature = pack('Na*Na*', strlen($signatureType), $signatureType, strlen($signature), $signature);
        $packet.= pack('Na*', strlen($signature), $signature);

        if (!$this->_send_binary_packet($packet)) {
            return false;
        }

        $response = $this->_get_binary_packet();
        if ($response === false) {
            $this->bitmap = 0;
            user_error('Connection closed by server');
            return false;
        }

        if (!strlen($response)) {
            return false;
        }
        extract(unpack('Ctype', $this->_string_shift($response, 1)));

        switch ($type) {
            case NET_SSH2_MSG_USERAUTH_FAILURE:
                // either the login is bad or the server employs multi-factor authentication
                return false;
            case NET_SSH2_MSG_USERAUTH_SUCCESS:
                $this->bitmap |= NET_SSH2_MASK_LOGIN;
                return true;
        }

        return false;
    }

    /**
     * Set Timeout
     *
     * $ssh->exec('ping 127.0.0.1'); on a Linux host will never return and will run indefinitely.  setTimeout() makes it so it'll timeout.
     * Setting $timeout to false or 0 will mean there is no timeout.
     *
     * @param mixed $timeout
     * @access public
     */
    function setTimeout($timeout)
    {
        $this->timeout = $this->curTimeout = $timeout;
    }

    /**
     * Get the output from stdError
     *
     * @access public
     */
    function getStdError()
    {
        return $this->stdErrorLog;
    }

    /**
     * Execute Command
     *
     * If $callback is set to false then Net_SSH2::_get_channel_packet(NET_SSH2_CHANNEL_EXEC) will need to be called manually.
     * In all likelihood, this is not a feature you want to be taking advantage of.
     *
     * @param string $command
     * @param Callback $callback
     * @return string
     * @access public
     */
    function exec($command, $callback = null)
    {
        $this->curTimeout = $this->timeout;
        $this->is_timeout = false;
        $this->stdErrorLog = '';

        if (!$this->isAuthenticated()) {
            return false;
        }

        if ($this->in_request_pty_exec) {
            user_error('If you want to run multiple exec()\'s you will need to disable (and re-enable if appropriate) a PTY for each one.');
            return false;
        }

        // RFC4254 defines the (client) window size as "bytes the other party can send before it must wait for the window to
        // be adjusted".  0x7FFFFFFF is, at 2GB, the max size.  technically, it should probably be decremented, but,
        // honestly, if you're transferring more than 2GB, you probably shouldn't be using phpseclib, anyway.
        // see http://tools.ietf.org/html/rfc4254#section-5.2 for more info
        $this->window_size_server_to_client[NET_SSH2_CHANNEL_EXEC] = $this->window_size;
        // 0x8000 is the maximum max packet size, per http://tools.ietf.org/html/rfc4253#section-6.1, although since PuTTy
        // uses 0x4000, that's what will be used here, as well.
        $packet_size = 0x4000;

        $packet = pack(
            'CNa*N3',
            NET_SSH2_MSG_CHANNEL_OPEN,
            strlen('session'),
            'session',
            NET_SSH2_CHANNEL_EXEC,
            $this->window_size_server_to_client[NET_SSH2_CHANNEL_EXEC],
            $packet_size
        );

        if (!$this->_send_binary_packet($packet)) {
            return false;
        }

        $this->channel_status[NET_SSH2_CHANNEL_EXEC] = NET_SSH2_MSG_CHANNEL_OPEN;

        $response = $this->_get_channel_packet(NET_SSH2_CHANNEL_EXEC);
        if ($response === false) {
            return false;
        }

        if ($this->request_pty === true) {
            $terminal_modes = pack('C', NET_SSH2_TTY_OP_END);
            $packet = pack(
                'CNNa*CNa*N5a*',
                NET_SSH2_MSG_CHANNEL_REQUEST,
                $this->server_channels[NET_SSH2_CHANNEL_EXEC],
                strlen('pty-req'),
                'pty-req',
                1,
                strlen('vt100'),
                'vt100',
                $this->windowColumns,
                $this->windowRows,
                0,
                0,
                strlen($terminal_modes),
                $terminal_modes
            );

            if (!$this->_send_binary_packet($packet)) {
                return false;
            }

            $response = $this->_get_binary_packet();
            if ($response === false) {
                $this->bitmap = 0;
                user_error('Connection closed by server');
                return false;
            }

            if (!strlen($response)) {
                return false;
            }
            list(, $type) = unpack('C', $this->_string_shift($response, 1));

            switch ($type) {
                case NET_SSH2_MSG_CHANNEL_SUCCESS:
                    break;
                case NET_SSH2_MSG_CHANNEL_FAILURE:
                default:
                    user_error('Unable to request pseudo-terminal');
                    return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
            }
            $this->in_request_pty_exec = true;
        }

        // sending a pty-req SSH_MSG_CHANNEL_REQUEST message is unnecessary and, in fact, in most cases, slows things
        // down.  the one place where it might be desirable is if you're doing something like Net_SSH2::exec('ping localhost &').
        // with a pty-req SSH_MSG_CHANNEL_REQUEST, exec() will return immediately and the ping process will then
        // then immediately terminate.  without such a request exec() will loop indefinitely.  the ping process won't end but
        // neither will your script.

        // although, in theory, the size of SSH_MSG_CHANNEL_REQUEST could exceed the maximum packet size established by
        // SSH_MSG_CHANNEL_OPEN_CONFIRMATION, RFC4254#section-5.1 states that the "maximum packet size" refers to the
        // "maximum size of an individual data packet". ie. SSH_MSG_CHANNEL_DATA.  RFC4254#section-5.2 corroborates.
        $packet = pack(
            'CNNa*CNa*',
            NET_SSH2_MSG_CHANNEL_REQUEST,
            $this->server_channels[NET_SSH2_CHANNEL_EXEC],
            strlen('exec'),
            'exec',
            1,
            strlen($command),
            $command
        );

        if (!$this->_send_binary_packet($packet)) {
            return false;
        }

        $this->channel_status[NET_SSH2_CHANNEL_EXEC] = NET_SSH2_MSG_CHANNEL_REQUEST;

        $response = $this->_get_channel_packet(NET_SSH2_CHANNEL_EXEC);
        if ($response === false) {
            return false;
        }

        $this->channel_status[NET_SSH2_CHANNEL_EXEC] = NET_SSH2_MSG_CHANNEL_DATA;

        if ($callback === false || $this->in_request_pty_exec) {
            return true;
        }

        $output = '';
        while (true) {
            $temp = $this->_get_channel_packet(NET_SSH2_CHANNEL_EXEC);
            switch (true) {
                case $temp === true:
                    return is_callable($callback) ? true : $output;
                case $temp === false:
                    return false;
                default:
                    if (is_callable($callback)) {
                        if (call_user_func($callback, $temp) === true) {
                            $this->_close_channel(NET_SSH2_CHANNEL_EXEC);
                            return true;
                        }
                    } else {
                        $output.= $temp;
                    }
            }
        }
    }

    /**
     * Creates an interactive shell
     *
     * @see self::read()
     * @see self::write()
     * @return bool
     * @access private
     */
    function _initShell()
    {
        if ($this->in_request_pty_exec === true) {
            return true;
        }

        $this->window_size_server_to_client[NET_SSH2_CHANNEL_SHELL] = $this->window_size;
        $packet_size = 0x4000;

        $packet = pack(
            'CNa*N3',
            NET_SSH2_MSG_CHANNEL_OPEN,
            strlen('session'),
            'session',
            NET_SSH2_CHANNEL_SHELL,
            $this->window_size_server_to_client[NET_SSH2_CHANNEL_SHELL],
            $packet_size
        );

        if (!$this->_send_binary_packet($packet)) {
            return false;
        }

        $this->channel_status[NET_SSH2_CHANNEL_SHELL] = NET_SSH2_MSG_CHANNEL_OPEN;

        $response = $this->_get_channel_packet(NET_SSH2_CHANNEL_SHELL);
        if ($response === false) {
            return false;
        }

        $terminal_modes = pack('C', NET_SSH2_TTY_OP_END);
        $packet = pack(
            'CNNa*CNa*N5a*',
            NET_SSH2_MSG_CHANNEL_REQUEST,
            $this->server_channels[NET_SSH2_CHANNEL_SHELL],
            strlen('pty-req'),
            'pty-req',
            1,
            strlen('vt100'),
            'vt100',
            $this->windowColumns,
            $this->windowRows,
            0,
            0,
            strlen($terminal_modes),
            $terminal_modes
        );

        if (!$this->_send_binary_packet($packet)) {
            return false;
        }

        $response = $this->_get_binary_packet();
        if ($response === false) {
            $this->bitmap = 0;
            user_error('Connection closed by server');
            return false;
        }

        if (!strlen($response)) {
            return false;
        }
        list(, $type) = unpack('C', $this->_string_shift($response, 1));

        switch ($type) {
            case NET_SSH2_MSG_CHANNEL_SUCCESS:
            // if a pty can't be opened maybe commands can still be executed
            case NET_SSH2_MSG_CHANNEL_FAILURE:
                break;
            default:
                user_error('Unable to request pseudo-terminal');
                return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
        }

        $packet = pack(
            'CNNa*C',
            NET_SSH2_MSG_CHANNEL_REQUEST,
            $this->server_channels[NET_SSH2_CHANNEL_SHELL],
            strlen('shell'),
            'shell',
            1
        );
        if (!$this->_send_binary_packet($packet)) {
            return false;
        }

        $this->channel_status[NET_SSH2_CHANNEL_SHELL] = NET_SSH2_MSG_CHANNEL_REQUEST;

        $response = $this->_get_channel_packet(NET_SSH2_CHANNEL_SHELL);
        if ($response === false) {
            return false;
        }

        $this->channel_status[NET_SSH2_CHANNEL_SHELL] = NET_SSH2_MSG_CHANNEL_DATA;

        $this->bitmap |= NET_SSH2_MASK_SHELL;

        return true;
    }

    /**
     * Return the channel to be used with read() / write()
     *
     * @see self::read()
     * @see self::write()
     * @return int
     * @access public
     */
    function _get_interactive_channel()
    {
        switch (true) {
            case $this->in_subsystem:
                return NET_SSH2_CHANNEL_SUBSYSTEM;
            case $this->in_request_pty_exec:
                return NET_SSH2_CHANNEL_EXEC;
            default:
                return NET_SSH2_CHANNEL_SHELL;
        }
    }

    /**
     * Return an available open channel
     *
     * @return int
     * @access public
     */
    function _get_open_channel()
    {
        $channel = NET_SSH2_CHANNEL_EXEC;
        do {
            if (isset($this->channel_status[$channel]) && $this->channel_status[$channel] == NET_SSH2_MSG_CHANNEL_OPEN) {
                return $channel;
            }
        } while ($channel++ < NET_SSH2_CHANNEL_SUBSYSTEM);

        return false;
    }

    /**
     * Returns the output of an interactive shell
     *
     * Returns when there's a match for $expect, which can take the form of a string literal or,
     * if $mode == NET_SSH2_READ_REGEX, a regular expression.
     *
     * @see self::write()
     * @param string $expect
     * @param int $mode
     * @return string
     * @access public
     */
    function read($expect = '', $mode = NET_SSH2_READ_SIMPLE)
    {
        $this->curTimeout = $this->timeout;
        $this->is_timeout = false;

        if (!$this->isAuthenticated()) {
            user_error('Operation disallowed prior to login()');
            return false;
        }

        if (!($this->bitmap & NET_SSH2_MASK_SHELL) && !$this->_initShell()) {
            user_error('Unable to initiate an interactive shell session');
            return false;
        }

        $channel = $this->_get_interactive_channel();

        if ($mode == NET_SSH2_READ_NEXT) {
            return $this->_get_channel_packet($channel);
        }

        $match = $expect;
        while (true) {
            if ($mode == NET_SSH2_READ_REGEX) {
                preg_match($expect, substr($this->interactiveBuffer, -1024), $matches);
                $match = isset($matches[0]) ? $matches[0] : '';
            }
            $pos = strlen($match) ? strpos($this->interactiveBuffer, $match) : false;
            if ($pos !== false) {
                return $this->_string_shift($this->interactiveBuffer, $pos + strlen($match));
            }
            $response = $this->_get_channel_packet($channel);
            if (is_bool($response)) {
                $this->in_request_pty_exec = false;
                return $response ? $this->_string_shift($this->interactiveBuffer, strlen($this->interactiveBuffer)) : false;
            }

            $this->interactiveBuffer.= $response;
        }
    }

    /**
     * Inputs a command into an interactive shell.
     *
     * @see self::read()
     * @param string $cmd
     * @return bool
     * @access public
     */
    function write($cmd)
    {
        if (!$this->isAuthenticated()) {
            user_error('Operation disallowed prior to login()');
            return false;
        }

        if (!($this->bitmap & NET_SSH2_MASK_SHELL) && !$this->_initShell()) {
            user_error('Unable to initiate an interactive shell session');
            return false;
        }

        return $this->_send_channel_packet($this->_get_interactive_channel(), $cmd);
    }

    /**
     * Start a subsystem.
     *
     * Right now only one subsystem at a time is supported. To support multiple subsystem's stopSubsystem() could accept
     * a string that contained the name of the subsystem, but at that point, only one subsystem of each type could be opened.
     * To support multiple subsystem's of the same name maybe it'd be best if startSubsystem() generated a new channel id and
     * returns that and then that that was passed into stopSubsystem() but that'll be saved for a future date and implemented
     * if there's sufficient demand for such a feature.
     *
     * @see self::stopSubsystem()
     * @param string $subsystem
     * @return bool
     * @access public
     */
    function startSubsystem($subsystem)
    {
        $this->window_size_server_to_client[NET_SSH2_CHANNEL_SUBSYSTEM] = $this->window_size;

        $packet = pack(
            'CNa*N3',
            NET_SSH2_MSG_CHANNEL_OPEN,
            strlen('session'),
            'session',
            NET_SSH2_CHANNEL_SUBSYSTEM,
            $this->window_size,
            0x4000
        );

        if (!$this->_send_binary_packet($packet)) {
            return false;
        }

        $this->channel_status[NET_SSH2_CHANNEL_SUBSYSTEM] = NET_SSH2_MSG_CHANNEL_OPEN;

        $response = $this->_get_channel_packet(NET_SSH2_CHANNEL_SUBSYSTEM);
        if ($response === false) {
            return false;
        }

        $packet = pack(
            'CNNa*CNa*',
            NET_SSH2_MSG_CHANNEL_REQUEST,
            $this->server_channels[NET_SSH2_CHANNEL_SUBSYSTEM],
            strlen('subsystem'),
            'subsystem',
            1,
            strlen($subsystem),
            $subsystem
        );
        if (!$this->_send_binary_packet($packet)) {
            return false;
        }

        $this->channel_status[NET_SSH2_CHANNEL_SUBSYSTEM] = NET_SSH2_MSG_CHANNEL_REQUEST;

        $response = $this->_get_channel_packet(NET_SSH2_CHANNEL_SUBSYSTEM);

        if ($response === false) {
            return false;
        }

        $this->channel_status[NET_SSH2_CHANNEL_SUBSYSTEM] = NET_SSH2_MSG_CHANNEL_DATA;

        $this->bitmap |= NET_SSH2_MASK_SHELL;
        $this->in_subsystem = true;

        return true;
    }

    /**
     * Stops a subsystem.
     *
     * @see self::startSubsystem()
     * @return bool
     * @access public
     */
    function stopSubsystem()
    {
        $this->in_subsystem = false;
        $this->_close_channel(NET_SSH2_CHANNEL_SUBSYSTEM);
        return true;
    }

    /**
     * Closes a channel
     *
     * If read() timed out you might want to just close the channel and have it auto-restart on the next read() call
     *
     * @access public
     */
    function reset()
    {
        $this->_close_channel($this->_get_interactive_channel());
    }

    /**
     * Is timeout?
     *
     * Did exec() or read() return because they timed out or because they encountered the end?
     *
     * @access public
     */
    function isTimeout()
    {
        return $this->is_timeout;
    }

    /**
     * Disconnect
     *
     * @access public
     */
    function disconnect()
    {
        $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
        if (isset($this->realtime_log_file) && is_resource($this->realtime_log_file)) {
            fclose($this->realtime_log_file);
        }
    }

    /**
     * Destructor.
     *
     * Will be called, automatically, if you're supporting just PHP5.  If you're supporting PHP4, you'll need to call
     * disconnect().
     *
     * @access public
     */
    function __destruct()
    {
        $this->disconnect();
    }

    /**
     * Is the connection still active?
     *
     * @return bool
     * @access public
     */
    function isConnected()
    {
        return (bool) ($this->bitmap & NET_SSH2_MASK_CONNECTED);
    }

    /**
     * Have you successfully been logged in?
     *
     * @return bool
     * @access public
     */
    function isAuthenticated()
    {
        return (bool) ($this->bitmap & NET_SSH2_MASK_LOGIN);
    }

    /**
     * Pings a server connection, or tries to reconnect if the connection has gone down
     *
     * Inspired by http://php.net/manual/en/mysqli.ping.php
     *
     * @return bool
     * @access public
     */
    function ping()
    {
        if (!$this->isAuthenticated()) {
            return false;
        }

        $this->window_size_server_to_client[NET_SSH2_CHANNEL_KEEP_ALIVE] = $this->window_size;
        $packet_size = 0x4000;
        $packet = pack(
            'CNa*N3',
            NET_SSH2_MSG_CHANNEL_OPEN,
            strlen('session'),
            'session',
            NET_SSH2_CHANNEL_KEEP_ALIVE,
            $this->window_size_server_to_client[NET_SSH2_CHANNEL_KEEP_ALIVE],
            $packet_size
        );

        if (!@$this->_send_binary_packet($packet)) {
            return $this->_reconnect();
        }

        $this->channel_status[NET_SSH2_CHANNEL_KEEP_ALIVE] = NET_SSH2_MSG_CHANNEL_OPEN;

        $response = @$this->_get_channel_packet(NET_SSH2_CHANNEL_KEEP_ALIVE);
        if ($response !== false) {
            $this->_close_channel(NET_SSH2_CHANNEL_KEEP_ALIVE);
            return true;
        }

        return $this->_reconnect();
    }

    /**
     * In situ reconnect method
     *
     * @return boolean
     * @access private
     */
    function _reconnect()
    {
        $this->_reset_connection(NET_SSH2_DISCONNECT_CONNECTION_LOST);
        $this->retry_connect = true;
        if (!$this->_connect()) {
            return false;
        }
        foreach ($this->auth as $auth) {
            $result = call_user_func_array(array(&$this, 'parent::login'), $auth);
        }
        return $result;
    }

    /**
     * Resets a connection for re-use
     *
     * @param int $reason
     * @access private
     */
    function _reset_connection($reason)
    {
        $this->_disconnect($reason);
        $this->decrypt = $this->encrypt = false;
        $this->decrypt_block_size = $this->encrypt_block_size = 8;
        $this->hmac_check = $this->hmac_create = false;
        $this->hmac_size = false;
        $this->session_id = false;
        $this->retry_connect = true;
        $this->get_seq_no = $this->send_seq_no = 0;
    }

    /**
     * Gets Binary Packets
     *
     * See '6. Binary Packet Protocol' of rfc4253 for more info.
     *
     * @see self::_send_binary_packet()
     * @return string
     * @access private
     */
    function _get_binary_packet($skip_channel_filter = false)
    {
        if (!is_resource($this->fsock) || feof($this->fsock)) {
            $this->bitmap = 0;
            user_error('Connection closed prematurely');
            return false;
        }

        $start = strtok(microtime(), ' ') + strtok(''); // http://php.net/microtime#61838
        $raw = fread($this->fsock, $this->decrypt_block_size);

        if (!strlen($raw)) {
            return '';
        }

        if ($this->decrypt !== false) {
            $raw = $this->decrypt->decrypt($raw);
        }
        if ($raw === false) {
            user_error('Unable to decrypt content');
            return false;
        }

        if (strlen($raw) < 5) {
            return false;
        }
        extract(unpack('Npacket_length/Cpadding_length', $this->_string_shift($raw, 5)));

        $remaining_length = $packet_length + 4 - $this->decrypt_block_size;

        // quoting <http://tools.ietf.org/html/rfc4253#section-6.1>,
        // "implementations SHOULD check that the packet length is reasonable"
        // PuTTY uses 0x9000 as the actual max packet size and so to shall we
        if ($remaining_length < -$this->decrypt_block_size || $remaining_length > 0x9000 || $remaining_length % $this->decrypt_block_size != 0) {
            if (!$this->bad_key_size_fix && $this->_bad_algorithm_candidate($this->decrypt_algorithm) && !($this->bitmap & NET_SSH2_MASK_LOGIN)) {
                $this->bad_key_size_fix = true;
                $this->_reset_connection(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
                return false;
            }
            user_error('Invalid size');
            return false;
        }

        $buffer = '';
        while ($remaining_length > 0) {
            $temp = fread($this->fsock, $remaining_length);
            if ($temp === false || feof($this->fsock)) {
                $this->bitmap = 0;
                user_error('Error reading from socket');
                return false;
            }
            $buffer.= $temp;
            $remaining_length-= strlen($temp);
        }

        $stop = strtok(microtime(), ' ') + strtok('');
        if (strlen($buffer)) {
            $raw.= $this->decrypt !== false ? $this->decrypt->decrypt($buffer) : $buffer;
        }

        $payload = $this->_string_shift($raw, $packet_length - $padding_length - 1);
        $padding = $this->_string_shift($raw, $padding_length); // should leave $raw empty

        if ($this->hmac_check !== false) {
            $hmac = fread($this->fsock, $this->hmac_size);
            if ($hmac === false || strlen($hmac) != $this->hmac_size) {
                $this->bitmap = 0;
                user_error('Error reading socket');
                return false;
            } elseif ($hmac != $this->hmac_check->hash(pack('NNCa*', $this->get_seq_no, $packet_length, $padding_length, $payload . $padding))) {
                user_error('Invalid HMAC');
                return false;
            }
        }

        //if ($this->decompress) {
        //    $payload = gzinflate(substr($payload, 2));
        //}

        $this->get_seq_no++;

        if (defined('NET_SSH2_LOGGING')) {
            $current = strtok(microtime(), ' ') + strtok('');
            $message_number = isset($this->message_numbers[ord($payload[0])]) ? $this->message_numbers[ord($payload[0])] : 'UNKNOWN (' . ord($payload[0]) . ')';
            $message_number = '<- ' . $message_number .
                              ' (since last: ' . round($current - $this->last_packet, 4) . ', network: ' . round($stop - $start, 4) . 's)';
            $this->_append_log($message_number, $payload);
            $this->last_packet = $current;
        }

        return $this->_filter($payload, $skip_channel_filter);
    }

    /**
     * Filter Binary Packets
     *
     * Because some binary packets need to be ignored...
     *
     * @see self::_get_binary_packet()
     * @return string
     * @access private
     */
    function _filter($payload, $skip_channel_filter)
    {
        switch (ord($payload[0])) {
            case NET_SSH2_MSG_DISCONNECT:
                $this->_string_shift($payload, 1);
                if (strlen($payload) < 8) {
                    return false;
                }
                extract(unpack('Nreason_code/Nlength', $this->_string_shift($payload, 8)));
                $this->errors[] = 'SSH_MSG_DISCONNECT: ' . $this->disconnect_reasons[$reason_code] . "\r\n" . $this->_string_shift($payload, $length);
                $this->bitmap = 0;
                return false;
            case NET_SSH2_MSG_IGNORE:
                $payload = $this->_get_binary_packet($skip_channel_filter);
                break;
            case NET_SSH2_MSG_DEBUG:
                $this->_string_shift($payload, 2);
                if (strlen($payload) < 4) {
                    return false;
                }
                extract(unpack('Nlength', $this->_string_shift($payload, 4)));
                $this->errors[] = 'SSH_MSG_DEBUG: ' . $this->_string_shift($payload, $length);
                $payload = $this->_get_binary_packet($skip_channel_filter);
                break;
            case NET_SSH2_MSG_UNIMPLEMENTED:
                return false;
            case NET_SSH2_MSG_KEXINIT:
                if ($this->session_id !== false) {
                    $this->send_kex_first = false;
                    if (!$this->_key_exchange($payload)) {
                        $this->bitmap = 0;
                        return false;
                    }
                    $payload = $this->_get_binary_packet($skip_channel_filter);
                }
        }

        // see http://tools.ietf.org/html/rfc4252#section-5.4; only called when the encryption has been activated and when we haven't already logged in
        if (($this->bitmap & NET_SSH2_MASK_CONNECTED) && !$this->isAuthenticated() && ord($payload[0]) == NET_SSH2_MSG_USERAUTH_BANNER) {
            $this->_string_shift($payload, 1);
            if (strlen($payload) < 4) {
                return false;
            }
            extract(unpack('Nlength', $this->_string_shift($payload, 4)));
            $this->banner_message = $this->_string_shift($payload, $length);
            $payload = $this->_get_binary_packet();
        }

        // only called when we've already logged in
        if (($this->bitmap & NET_SSH2_MASK_CONNECTED) && $this->isAuthenticated()) {
            switch (ord($payload[0])) {
                case NET_SSH2_MSG_CHANNEL_DATA:
                case NET_SSH2_MSG_CHANNEL_EXTENDED_DATA:
                case NET_SSH2_MSG_CHANNEL_REQUEST:
                case NET_SSH2_MSG_CHANNEL_CLOSE:
                case NET_SSH2_MSG_CHANNEL_EOF:
                    if (!$skip_channel_filter && !empty($this->server_channels)) {
                        $this->binary_packet_buffer = $payload;
                        $this->_get_channel_packet(true);
                        $payload = $this->_get_binary_packet();
                    }
                    break;
                case NET_SSH2_MSG_GLOBAL_REQUEST: // see http://tools.ietf.org/html/rfc4254#section-4
                    if (strlen($payload) < 4) {
                        return false;
                    }
                    extract(unpack('Nlength', $this->_string_shift($payload, 4)));
                    $this->errors[] = 'SSH_MSG_GLOBAL_REQUEST: ' . $this->_string_shift($payload, $length);

                    if (!$this->_send_binary_packet(pack('C', NET_SSH2_MSG_REQUEST_FAILURE))) {
                        return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
                    }

                    $payload = $this->_get_binary_packet($skip_channel_filter);
                    break;
                case NET_SSH2_MSG_CHANNEL_OPEN: // see http://tools.ietf.org/html/rfc4254#section-5.1
                    $this->_string_shift($payload, 1);
                    if (strlen($payload) < 4) {
                        return false;
                    }
                    extract(unpack('Nlength', $this->_string_shift($payload, 4)));
                    $data = $this->_string_shift($payload, $length);
                    if (strlen($payload) < 4) {
                        return false;
                    }
                    extract(unpack('Nserver_channel', $this->_string_shift($payload, 4)));
                    switch ($data) {
                        case 'auth-agent':
                        case 'auth-agent@openssh.com':
                            if (isset($this->agent)) {
                                $new_channel = NET_SSH2_CHANNEL_AGENT_FORWARD;

                                if (strlen($payload) < 8) {
                                    return false;
                                }
                                extract(unpack('Nremote_window_size', $this->_string_shift($payload, 4)));
                                extract(unpack('Nremote_maximum_packet_size', $this->_string_shift($payload, 4)));

                                $this->packet_size_client_to_server[$new_channel] = $remote_window_size;
                                $this->window_size_server_to_client[$new_channel] = $remote_maximum_packet_size;
                                $this->window_size_client_to_server[$new_channel] = $this->window_size;

                                $packet_size = 0x4000;

                                $packet = pack(
                                    'CN4',
                                    NET_SSH2_MSG_CHANNEL_OPEN_CONFIRMATION,
                                    $server_channel,
                                    $new_channel,
                                    $packet_size,
                                    $packet_size
                                );

                                $this->server_channels[$new_channel] = $server_channel;
                                $this->channel_status[$new_channel] = NET_SSH2_MSG_CHANNEL_OPEN_CONFIRMATION;
                                if (!$this->_send_binary_packet($packet)) {
                                    return false;
                                }
                            }
                            break;
                        default:
                            $packet = pack(
                                'CN3a*Na*',
                                NET_SSH2_MSG_REQUEST_FAILURE,
                                $server_channel,
                                NET_SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED,
                                0,
                                '',
                                0,
                                ''
                            );

                            if (!$this->_send_binary_packet($packet)) {
                                return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
                            }
                    }
                    $payload = $this->_get_binary_packet($skip_channel_filter);
                    break;
                case NET_SSH2_MSG_CHANNEL_WINDOW_ADJUST:
                    $this->_string_shift($payload, 1);
                    if (strlen($payload) < 8) {
                        return false;
                    }
                    extract(unpack('Nchannel', $this->_string_shift($payload, 4)));
                    extract(unpack('Nwindow_size', $this->_string_shift($payload, 4)));
                    $this->window_size_client_to_server[$channel]+= $window_size;

                    $payload = ($this->bitmap & NET_SSH2_MASK_WINDOW_ADJUST) ? true : $this->_get_binary_packet($skip_channel_filter);
            }
        }

        return $payload;
    }

    /**
     * Enable Quiet Mode
     *
     * Suppress stderr from output
     *
     * @access public
     */
    function enableQuietMode()
    {
        $this->quiet_mode = true;
    }

    /**
     * Disable Quiet Mode
     *
     * Show stderr in output
     *
     * @access public
     */
    function disableQuietMode()
    {
        $this->quiet_mode = false;
    }

    /**
     * Returns whether Quiet Mode is enabled or not
     *
     * @see self::enableQuietMode()
     * @see self::disableQuietMode()
     *
     * @access public
     * @return bool
     */
    function isQuietModeEnabled()
    {
        return $this->quiet_mode;
    }

    /**
     * Enable request-pty when using exec()
     *
     * @access public
     */
    function enablePTY()
    {
        $this->request_pty = true;
    }

    /**
     * Disable request-pty when using exec()
     *
     * @access public
     */
    function disablePTY()
    {
        if ($this->in_request_pty_exec) {
            $this->_close_channel(NET_SSH2_CHANNEL_EXEC);
            $this->in_request_pty_exec = false;
        }
        $this->request_pty = false;
    }

    /**
     * Returns whether request-pty is enabled or not
     *
     * @see self::enablePTY()
     * @see self::disablePTY()
     *
     * @access public
     * @return bool
     */
    function isPTYEnabled()
    {
        return $this->request_pty;
    }

    /**
     * Gets channel data
     *
     * Returns the data as a string if it's available and false if not.
     *
     * @param $client_channel
     * @return mixed
     * @access private
     */
    function _get_channel_packet($client_channel, $skip_extended = false)
    {
        if (!empty($this->channel_buffers[$client_channel])) {
            return array_shift($this->channel_buffers[$client_channel]);
        }

        while (true) {
            if ($this->binary_packet_buffer !== false) {
                $response = $this->binary_packet_buffer;
                $this->binary_packet_buffer = false;
            } else {
                $read = array($this->fsock);
                $write = $except = null;

                if (!$this->curTimeout) {
                    @stream_select($read, $write, $except, null);
                } else {
                    if ($this->curTimeout < 0) {
                        $this->is_timeout = true;
                        return true;
                    }

                    $read = array($this->fsock);
                    $write = $except = null;

                    $start = strtok(microtime(), ' ') + strtok(''); // http://php.net/microtime#61838
                    $sec = (int) floor($this->curTimeout);
                    $usec = (int) (1000000 * ($this->curTimeout - $sec));
                    // on windows this returns a "Warning: Invalid CRT parameters detected" error
                    if (!@stream_select($read, $write, $except, $sec, $usec) && !count($read)) {
                        $this->is_timeout = true;
                        return true;
                    }
                    $elapsed = strtok(microtime(), ' ') + strtok('') - $start;
                    $this->curTimeout-= $elapsed;
                }

                $response = $this->_get_binary_packet(true);
                if ($response === false) {
                    $this->bitmap = 0;
                    user_error('Connection closed by server');
                    return false;
                }
            }

            if ($client_channel == -1 && $response === true) {
                return true;
            }
            if (!strlen($response)) {
                return false;
            }
            extract(unpack('Ctype', $this->_string_shift($response, 1)));

            if (strlen($response) < 4) {
                return false;
            }
            if ($type == NET_SSH2_MSG_CHANNEL_OPEN) {
                extract(unpack('Nlength', $this->_string_shift($response, 4)));
            } else {
                extract(unpack('Nchannel', $this->_string_shift($response, 4)));
            }

            // will not be setup yet on incoming channel open request
            if (isset($channel) && isset($this->channel_status[$channel]) && isset($this->window_size_server_to_client[$channel])) {
                $this->window_size_server_to_client[$channel]-= strlen($response);

                // resize the window, if appropriate
                if ($this->window_size_server_to_client[$channel] < 0) {
                    $packet = pack('CNN', NET_SSH2_MSG_CHANNEL_WINDOW_ADJUST, $this->server_channels[$channel], $this->window_size);
                    if (!$this->_send_binary_packet($packet)) {
                        return false;
                    }
                    $this->window_size_server_to_client[$channel]+= $this->window_size;
                }

                switch ($type) {
                    case NET_SSH2_MSG_CHANNEL_EXTENDED_DATA:
                        /*
                        if ($client_channel == NET_SSH2_CHANNEL_EXEC) {
                            $this->_send_channel_packet($client_channel, chr(0));
                        }
                        */
                        // currently, there's only one possible value for $data_type_code: NET_SSH2_EXTENDED_DATA_STDERR
                        if (strlen($response) < 8) {
                            return false;
                        }
                        extract(unpack('Ndata_type_code/Nlength', $this->_string_shift($response, 8)));
                        $data = $this->_string_shift($response, $length);
                        $this->stdErrorLog.= $data;
                        if ($skip_extended || $this->quiet_mode) {
                            continue 2;
                        }
                        if ($client_channel == $channel && $this->channel_status[$channel] == NET_SSH2_MSG_CHANNEL_DATA) {
                            return $data;
                        }
                        if (!isset($this->channel_buffers[$channel])) {
                            $this->channel_buffers[$channel] = array();
                        }
                        $this->channel_buffers[$channel][] = $data;

                        continue 2;
                    case NET_SSH2_MSG_CHANNEL_REQUEST:
                        if ($this->channel_status[$channel] == NET_SSH2_MSG_CHANNEL_CLOSE) {
                            continue 2;
                        }
                        if (strlen($response) < 4) {
                            return false;
                        }
                        extract(unpack('Nlength', $this->_string_shift($response, 4)));
                        $value = $this->_string_shift($response, $length);
                        switch ($value) {
                            case 'exit-signal':
                                $this->_string_shift($response, 1);
                                if (strlen($response) < 4) {
                                    return false;
                                }
                                extract(unpack('Nlength', $this->_string_shift($response, 4)));
                                $this->errors[] = 'SSH_MSG_CHANNEL_REQUEST (exit-signal): ' . $this->_string_shift($response, $length);
                                $this->_string_shift($response, 1);
                                if (strlen($response) < 4) {
                                    return false;
                                }
                                extract(unpack('Nlength', $this->_string_shift($response, 4)));
                                if ($length) {
                                    $this->errors[count($this->errors)].= "\r\n" . $this->_string_shift($response, $length);
                                }

                                $this->_send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_EOF, $this->server_channels[$client_channel]));
                                $this->_send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_CLOSE, $this->server_channels[$channel]));

                                $this->channel_status[$channel] = NET_SSH2_MSG_CHANNEL_EOF;

                                continue 3;
                            case 'exit-status':
                                if (strlen($response) < 5) {
                                    return false;
                                }
                                extract(unpack('Cfalse/Nexit_status', $this->_string_shift($response, 5)));
                                $this->exit_status = $exit_status;

                                // "The client MAY ignore these messages."
                                // -- http://tools.ietf.org/html/rfc4254#section-6.10

                                continue 3;
                            default:
                                // "Some systems may not implement signals, in which case they SHOULD ignore this message."
                                //  -- http://tools.ietf.org/html/rfc4254#section-6.9
                                continue 3;
                        }
                }

                switch ($this->channel_status[$channel]) {
                    case NET_SSH2_MSG_CHANNEL_OPEN:
                        switch ($type) {
                            case NET_SSH2_MSG_CHANNEL_OPEN_CONFIRMATION:
                                if (strlen($response) < 4) {
                                    return false;
                                }
                                extract(unpack('Nserver_channel', $this->_string_shift($response, 4)));
                                $this->server_channels[$channel] = $server_channel;
                                if (strlen($response) < 4) {
                                    return false;
                                }
                                extract(unpack('Nwindow_size', $this->_string_shift($response, 4)));
                                if ($window_size < 0) {
                                    $window_size&= 0x7FFFFFFF;
                                    $window_size+= 0x80000000;
                                }
                                $this->window_size_client_to_server[$channel] = $window_size;
                                if (strlen($response) < 4) {
                                     return false;
                                }
                                $temp = unpack('Npacket_size_client_to_server', $this->_string_shift($response, 4));
                                $this->packet_size_client_to_server[$channel] = $temp['packet_size_client_to_server'];
                                $result = $client_channel == $channel ? true : $this->_get_channel_packet($client_channel, $skip_extended);
                                $this->_on_channel_open();
                                return $result;
                            //case NET_SSH2_MSG_CHANNEL_OPEN_FAILURE:
                            default:
                                user_error('Unable to open channel');
                                return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
                        }
                        break;
                    case NET_SSH2_MSG_CHANNEL_REQUEST:
                        switch ($type) {
                            case NET_SSH2_MSG_CHANNEL_SUCCESS:
                                return true;
                            case NET_SSH2_MSG_CHANNEL_FAILURE:
                                return false;
                            default:
                                user_error('Unable to fulfill channel request');
                                return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
                        }
                    case NET_SSH2_MSG_CHANNEL_CLOSE:
                        return $type == NET_SSH2_MSG_CHANNEL_CLOSE ? true : $this->_get_channel_packet($client_channel, $skip_extended);
                }
            }

            // ie. $this->channel_status[$channel] == NET_SSH2_MSG_CHANNEL_DATA

            switch ($type) {
                case NET_SSH2_MSG_CHANNEL_DATA:
                    /*
                    if ($channel == NET_SSH2_CHANNEL_EXEC) {
                        // SCP requires null packets, such as this, be sent.  further, in the case of the ssh.com SSH server
                        // this actually seems to make things twice as fast.  more to the point, the message right after
                        // SSH_MSG_CHANNEL_DATA (usually SSH_MSG_IGNORE) won't block for as long as it would have otherwise.
                        // in OpenSSH it slows things down but only by a couple thousandths of a second.
                        $this->_send_channel_packet($channel, chr(0));
                    }
                    */
                    if (strlen($response) < 4) {
                        return false;
                    }
                    extract(unpack('Nlength', $this->_string_shift($response, 4)));
                    $data = $this->_string_shift($response, $length);

                    if ($channel == NET_SSH2_CHANNEL_AGENT_FORWARD) {
                        $agent_response = $this->agent->_forward_data($data);
                        if (!is_bool($agent_response)) {
                            $this->_send_channel_packet($channel, $agent_response);
                        }
                        break;
                    }

                    if ($client_channel == $channel) {
                        return $data;
                    }
                    if (!isset($this->channel_buffers[$channel])) {
                        $this->channel_buffers[$channel] = array();
                    }
                    $this->channel_buffers[$channel][] = $data;
                    break;
                case NET_SSH2_MSG_CHANNEL_CLOSE:
                    $this->curTimeout = 0;

                    if ($this->bitmap & NET_SSH2_MASK_SHELL) {
                        $this->bitmap&= ~NET_SSH2_MASK_SHELL;
                    }
                    if ($this->channel_status[$channel] != NET_SSH2_MSG_CHANNEL_EOF) {
                        $this->_send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_CLOSE, $this->server_channels[$channel]));
                    }

                    $this->channel_status[$channel] = NET_SSH2_MSG_CHANNEL_CLOSE;
                    if ($client_channel == $channel) {
                        return true;
                    }
                case NET_SSH2_MSG_CHANNEL_EOF:
                    break;
                default:
                    user_error('Error reading channel data');
                    return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
            }
        }
    }

    /**
     * Sends Binary Packets
     *
     * See '6. Binary Packet Protocol' of rfc4253 for more info.
     *
     * @param string $data
     * @param string $logged
     * @see self::_get_binary_packet()
     * @return bool
     * @access private
     */
    function _send_binary_packet($data, $logged = null)
    {
        if (!is_resource($this->fsock) || feof($this->fsock)) {
            $this->bitmap = 0;
            user_error('Connection closed prematurely');
            return false;
        }

        //if ($this->compress) {
        //    // the -4 removes the checksum:
        //    // http://php.net/function.gzcompress#57710
        //    $data = substr(gzcompress($data), 0, -4);
        //}

        // 4 (packet length) + 1 (padding length) + 4 (minimal padding amount) == 9
        $packet_length = strlen($data) + 9;
        // round up to the nearest $this->encrypt_block_size
        $packet_length+= (($this->encrypt_block_size - 1) * $packet_length) % $this->encrypt_block_size;
        // subtracting strlen($data) is obvious - subtracting 5 is necessary because of packet_length and padding_length
        $padding_length = $packet_length - strlen($data) - 5;
        $padding = crypt_random_string($padding_length);

        // we subtract 4 from packet_length because the packet_length field isn't supposed to include itself
        $packet = pack('NCa*', $packet_length - 4, $padding_length, $data . $padding);

        $hmac = $this->hmac_create !== false ? $this->hmac_create->hash(pack('Na*', $this->send_seq_no, $packet)) : '';
        $this->send_seq_no++;

        if ($this->encrypt !== false) {
            $packet = $this->encrypt->encrypt($packet);
        }

        $packet.= $hmac;

        $start = strtok(microtime(), ' ') + strtok(''); // http://php.net/microtime#61838
        $result = strlen($packet) == fputs($this->fsock, $packet);
        $stop = strtok(microtime(), ' ') + strtok('');

        if (defined('NET_SSH2_LOGGING')) {
            $current = strtok(microtime(), ' ') + strtok('');
            $message_number = isset($this->message_numbers[ord($data[0])]) ? $this->message_numbers[ord($data[0])] : 'UNKNOWN (' . ord($data[0]) . ')';
            $message_number = '-> ' . $message_number .
                              ' (since last: ' . round($current - $this->last_packet, 4) . ', network: ' . round($stop - $start, 4) . 's)';
            $this->_append_log($message_number, isset($logged) ? $logged : $data);
            $this->last_packet = $current;
        }

        return $result;
    }

    /**
     * Logs data packets
     *
     * Makes sure that only the last 1MB worth of packets will be logged
     *
     * @param string $data
     * @access private
     */
    function _append_log($message_number, $message)
    {
        // remove the byte identifying the message type from all but the first two messages (ie. the identification strings)
        if (strlen($message_number) > 2) {
            $this->_string_shift($message);
        }

        switch (NET_SSH2_LOGGING) {
            // useful for benchmarks
            case NET_SSH2_LOG_SIMPLE:
                $this->message_number_log[] = $message_number;
                break;
            // the most useful log for SSH2
            case NET_SSH2_LOG_COMPLEX:
                $this->message_number_log[] = $message_number;
                $this->log_size+= strlen($message);
                $this->message_log[] = $message;
                while ($this->log_size > NET_SSH2_LOG_MAX_SIZE) {
                    $this->log_size-= strlen(array_shift($this->message_log));
                    array_shift($this->message_number_log);
                }
                break;
            // dump the output out realtime; packets may be interspersed with non packets,
            // passwords won't be filtered out and select other packets may not be correctly
            // identified
            case NET_SSH2_LOG_REALTIME:
                switch (PHP_SAPI) {
                    case 'cli':
                        $start = $stop = "\r\n";
                        break;
                    default:
                        $start = '<pre>';
                        $stop = '</pre>';
                }
                echo $start . $this->_format_log(array($message), array($message_number)) . $stop;
                @flush();
                @ob_flush();
                break;
            // basically the same thing as NET_SSH2_LOG_REALTIME with the caveat that NET_SSH2_LOG_REALTIME_FILE
            // needs to be defined and that the resultant log file will be capped out at NET_SSH2_LOG_MAX_SIZE.
            // the earliest part of the log file is denoted by the first <<< START >>> and is not going to necessarily
            // at the beginning of the file
            case NET_SSH2_LOG_REALTIME_FILE:
                if (!isset($this->realtime_log_file)) {
                    // PHP doesn't seem to like using constants in fopen()
                    $filename = NET_SSH2_LOG_REALTIME_FILENAME;
                    $fp = fopen($filename, 'w');
                    $this->realtime_log_file = $fp;
                }
                if (!is_resource($this->realtime_log_file)) {
                    break;
                }
                $entry = $this->_format_log(array($message), array($message_number));
                if ($this->realtime_log_wrap) {
                    $temp = "<<< START >>>\r\n";
                    $entry.= $temp;
                    fseek($this->realtime_log_file, ftell($this->realtime_log_file) - strlen($temp));
                }
                $this->realtime_log_size+= strlen($entry);
                if ($this->realtime_log_size > NET_SSH2_LOG_MAX_SIZE) {
                    fseek($this->realtime_log_file, 0);
                    $this->realtime_log_size = strlen($entry);
                    $this->realtime_log_wrap = true;
                }
                fputs($this->realtime_log_file, $entry);
        }
    }

    /**
     * Sends channel data
     *
     * Spans multiple SSH_MSG_CHANNEL_DATAs if appropriate
     *
     * @param int $client_channel
     * @param string $data
     * @return bool
     * @access private
     */
    function _send_channel_packet($client_channel, $data)
    {
        while (strlen($data)) {
            if (!$this->window_size_client_to_server[$client_channel]) {
                $this->bitmap^= NET_SSH2_MASK_WINDOW_ADJUST;
                // using an invalid channel will let the buffers be built up for the valid channels
                $this->_get_channel_packet(-1);
                $this->bitmap^= NET_SSH2_MASK_WINDOW_ADJUST;
            }

            /* The maximum amount of data allowed is determined by the maximum
               packet size for the channel, and the current window size, whichever
               is smaller.
                 -- http://tools.ietf.org/html/rfc4254#section-5.2 */
            $max_size = min(
                $this->packet_size_client_to_server[$client_channel],
                $this->window_size_client_to_server[$client_channel]
            );

            $temp = $this->_string_shift($data, $max_size);
            $packet = pack(
                'CN2a*',
                NET_SSH2_MSG_CHANNEL_DATA,
                $this->server_channels[$client_channel],
                strlen($temp),
                $temp
            );
            $this->window_size_client_to_server[$client_channel]-= strlen($temp);
            if (!$this->_send_binary_packet($packet)) {
                return false;
            }
        }

        return true;
    }

    /**
     * Closes and flushes a channel
     *
     * Net_SSH2 doesn't properly close most channels.  For exec() channels are normally closed by the server
     * and for SFTP channels are presumably closed when the client disconnects.  This functions is intended
     * for SCP more than anything.
     *
     * @param int $client_channel
     * @param bool $want_reply
     * @return bool
     * @access private
     */
    function _close_channel($client_channel, $want_reply = false)
    {
        // see http://tools.ietf.org/html/rfc4254#section-5.3

        $this->_send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_EOF, $this->server_channels[$client_channel]));

        if (!$want_reply) {
            $this->_send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_CLOSE, $this->server_channels[$client_channel]));
        }

        $this->channel_status[$client_channel] = NET_SSH2_MSG_CHANNEL_CLOSE;

        $this->curTimeout = 0;

        while (!is_bool($this->_get_channel_packet($client_channel))) {
        }

        if ($want_reply) {
            $this->_send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_CLOSE, $this->server_channels[$client_channel]));
        }

        if ($this->bitmap & NET_SSH2_MASK_SHELL) {
            $this->bitmap&= ~NET_SSH2_MASK_SHELL;
        }
    }

    /**
     * Disconnect
     *
     * @param int $reason
     * @return bool
     * @access private
     */
    function _disconnect($reason)
    {
        if ($this->bitmap & NET_SSH2_MASK_CONNECTED) {
            $data = pack('CNNa*Na*', NET_SSH2_MSG_DISCONNECT, $reason, 0, '', 0, '');
            $this->_send_binary_packet($data);
            $this->bitmap = 0;
            fclose($this->fsock);
            return false;
        }
    }

    /**
     * String Shift
     *
     * Inspired by array_shift
     *
     * @param string $string
     * @param int $index
     * @return string
     * @access private
     */
    function _string_shift(&$string, $index = 1)
    {
        $substr = substr($string, 0, $index);
        $string = substr($string, $index);
        return $substr;
    }

    /**
     * Define Array
     *
     * Takes any number of arrays whose indices are integers and whose values are strings and defines a bunch of
     * named constants from it, using the value as the name of the constant and the index as the value of the constant.
     * If any of the constants that would be defined already exists, none of the constants will be defined.
     *
     * @param array $array
     * @access private
     */
    function _define_array()
    {
        $args = func_get_args();
        foreach ($args as $arg) {
            foreach ($arg as $key => $value) {
                if (!defined($value)) {
                    define($value, $key);
                } else {
                    break 2;
                }
            }
        }
    }

    /**
     * Returns a log of the packets that have been sent and received.
     *
     * Returns a string if NET_SSH2_LOGGING == NET_SSH2_LOG_COMPLEX, an array if NET_SSH2_LOGGING == NET_SSH2_LOG_SIMPLE and false if !defined('NET_SSH2_LOGGING')
     *
     * @access public
     * @return array|false|string
     */
    function getLog()
    {
        if (!defined('NET_SSH2_LOGGING')) {
            return false;
        }

        switch (NET_SSH2_LOGGING) {
            case NET_SSH2_LOG_SIMPLE:
                return $this->message_number_log;
            case NET_SSH2_LOG_COMPLEX:
                $log = $this->_format_log($this->message_log, $this->message_number_log);
                return PHP_SAPI == 'cli' ? $log : '<pre>' . $log . '</pre>';
            default:
                return false;
        }
    }

    /**
     * Formats a log for printing
     *
     * @param array $message_log
     * @param array $message_number_log
     * @access private
     * @return string
     */
    function _format_log($message_log, $message_number_log)
    {
        $output = '';
        for ($i = 0; $i < count($message_log); $i++) {
            $output.= $message_number_log[$i] . "\r\n";
            $current_log = $message_log[$i];
            $j = 0;
            do {
                if (strlen($current_log)) {
                    $output.= str_pad(dechex($j), 7, '0', STR_PAD_LEFT) . '0  ';
                }
                $fragment = $this->_string_shift($current_log, $this->log_short_width);
                $hex = substr(preg_replace_callback('#.#s', array($this, '_format_log_helper'), $fragment), strlen($this->log_boundary));
                // replace non ASCII printable characters with dots
                // http://en.wikipedia.org/wiki/ASCII#ASCII_printable_characters
                // also replace < with a . since < messes up the output on web browsers
                $raw = preg_replace('#[^\x20-\x7E]|<#', '.', $fragment);
                $output.= str_pad($hex, $this->log_long_width - $this->log_short_width, ' ') . $raw . "\r\n";
                $j++;
            } while (strlen($current_log));
            $output.= "\r\n";
        }

        return $output;
    }

    /**
     * Helper function for _format_log
     *
     * For use with preg_replace_callback()
     *
     * @param array $matches
     * @access private
     * @return string
     */
    function _format_log_helper($matches)
    {
        return $this->log_boundary . str_pad(dechex(ord($matches[0])), 2, '0', STR_PAD_LEFT);
    }

    /**
     * Helper function for agent->_on_channel_open()
     *
     * Used when channels are created to inform agent
     * of said channel opening. Must be called after
     * channel open confirmation received
     *
     * @access private
     */
    function _on_channel_open()
    {
        if (isset($this->agent)) {
            $this->agent->_on_channel_open($this);
        }
    }

    /**
     * Returns the first value of the intersection of two arrays or false if
     * the intersection is empty. The order is defined by the first parameter.
     *
     * @param array $array1
     * @param array $array2
     * @return mixed False if intersection is empty, else intersected value.
     * @access private
     */
    function _array_intersect_first($array1, $array2)
    {
        foreach ($array1 as $value) {
            if (in_array($value, $array2)) {
                return $value;
            }
        }
        return false;
    }

    /**
     * Returns all errors
     *
     * @return string[]
     * @access public
     */
    function getErrors()
    {
        return $this->errors;
    }

    /**
     * Returns the last error
     *
     * @return string
     * @access public
     */
    function getLastError()
    {
        $count = count($this->errors);

        if ($count > 0) {
            return $this->errors[$count - 1];
        }
    }

    /**
     * Return the server identification.
     *
     * @return string
     * @access public
     */
    function getServerIdentification()
    {
        $this->_connect();

        return $this->server_identifier;
    }

    /**
     * Return a list of the key exchange algorithms the server supports.
     *
     * @return array
     * @access public
     */
    function getKexAlgorithms()
    {
        $this->_connect();

        return $this->kex_algorithms;
    }

    /**
     * Return a list of the host key (public key) algorithms the server supports.
     *
     * @return array
     * @access public
     */
    function getServerHostKeyAlgorithms()
    {
        $this->_connect();

        return $this->server_host_key_algorithms;
    }

    /**
     * Return a list of the (symmetric key) encryption algorithms the server supports, when receiving stuff from the client.
     *
     * @return array
     * @access public
     */
    function getEncryptionAlgorithmsClient2Server()
    {
        $this->_connect();

        return $this->encryption_algorithms_client_to_server;
    }

    /**
     * Return a list of the (symmetric key) encryption algorithms the server supports, when sending stuff to the client.
     *
     * @return array
     * @access public
     */
    function getEncryptionAlgorithmsServer2Client()
    {
        $this->_connect();

        return $this->encryption_algorithms_server_to_client;
    }

    /**
     * Return a list of the MAC algorithms the server supports, when receiving stuff from the client.
     *
     * @return array
     * @access public
     */
    function getMACAlgorithmsClient2Server()
    {
        $this->_connect();

        return $this->mac_algorithms_client_to_server;
    }

    /**
     * Return a list of the MAC algorithms the server supports, when sending stuff to the client.
     *
     * @return array
     * @access public
     */
    function getMACAlgorithmsServer2Client()
    {
        $this->_connect();

        return $this->mac_algorithms_server_to_client;
    }

    /**
     * Return a list of the compression algorithms the server supports, when receiving stuff from the client.
     *
     * @return array
     * @access public
     */
    function getCompressionAlgorithmsClient2Server()
    {
        $this->_connect();

        return $this->compression_algorithms_client_to_server;
    }

    /**
     * Return a list of the compression algorithms the server supports, when sending stuff to the client.
     *
     * @return array
     * @access public
     */
    function getCompressionAlgorithmsServer2Client()
    {
        $this->_connect();

        return $this->compression_algorithms_server_to_client;
    }

    /**
     * Return a list of the languages the server supports, when sending stuff to the client.
     *
     * @return array
     * @access public
     */
    function getLanguagesServer2Client()
    {
        $this->_connect();

        return $this->languages_server_to_client;
    }

    /**
     * Return a list of the languages the server supports, when receiving stuff from the client.
     *
     * @return array
     * @access public
     */
    function getLanguagesClient2Server()
    {
        $this->_connect();

        return $this->languages_client_to_server;
    }

    /**
     * Returns the banner message.
     *
     * Quoting from the RFC, "in some jurisdictions, sending a warning message before
     * authentication may be relevant for getting legal protection."
     *
     * @return string
     * @access public
     */
    function getBannerMessage()
    {
        return $this->banner_message;
    }

    /**
     * Returns the server public host key.
     *
     * Caching this the first time you connect to a server and checking the result on subsequent connections
     * is recommended.  Returns false if the server signature is not signed correctly with the public host key.
     *
     * @return mixed
     * @access public
     */
    function getServerPublicHostKey()
    {
        if (!($this->bitmap & NET_SSH2_MASK_CONSTRUCTOR)) {
            if (!$this->_connect()) {
                return false;
            }
        }

        $signature = $this->signature;
        $server_public_host_key = $this->server_public_host_key;

        if (strlen($server_public_host_key) < 4) {
            return false;
        }
        extract(unpack('Nlength', $this->_string_shift($server_public_host_key, 4)));
        $this->_string_shift($server_public_host_key, $length);

        if ($this->signature_validated) {
            return $this->bitmap ?
                $this->signature_format . ' ' . base64_encode($this->server_public_host_key) :
                false;
        }

        $this->signature_validated = true;

        switch ($this->signature_format) {
            case 'ssh-dss':
                $zero = new Math_BigInteger();

                if (strlen($server_public_host_key) < 4) {
                    return false;
                }
                $temp = unpack('Nlength', $this->_string_shift($server_public_host_key, 4));
                $p = new Math_BigInteger($this->_string_shift($server_public_host_key, $temp['length']), -256);

                if (strlen($server_public_host_key) < 4) {
                    return false;
                }
                $temp = unpack('Nlength', $this->_string_shift($server_public_host_key, 4));
                $q = new Math_BigInteger($this->_string_shift($server_public_host_key, $temp['length']), -256);

                if (strlen($server_public_host_key) < 4) {
                    return false;
                }
                $temp = unpack('Nlength', $this->_string_shift($server_public_host_key, 4));
                $g = new Math_BigInteger($this->_string_shift($server_public_host_key, $temp['length']), -256);

                if (strlen($server_public_host_key) < 4) {
                    return false;
                }
                $temp = unpack('Nlength', $this->_string_shift($server_public_host_key, 4));
                $y = new Math_BigInteger($this->_string_shift($server_public_host_key, $temp['length']), -256);

                /* The value for 'dss_signature_blob' is encoded as a string containing
                   r, followed by s (which are 160-bit integers, without lengths or
                   padding, unsigned, and in network byte order). */
                $temp = unpack('Nlength', $this->_string_shift($signature, 4));
                if ($temp['length'] != 40) {
                    user_error('Invalid signature');
                    return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
                }

                $r = new Math_BigInteger($this->_string_shift($signature, 20), 256);
                $s = new Math_BigInteger($this->_string_shift($signature, 20), 256);

                switch (true) {
                    case $r->equals($zero):
                    case $r->compare($q) >= 0:
                    case $s->equals($zero):
                    case $s->compare($q) >= 0:
                        user_error('Invalid signature');
                        return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
                }

                $w = $s->modInverse($q);

                $u1 = $w->multiply(new Math_BigInteger(sha1($this->exchange_hash), 16));
                list(, $u1) = $u1->divide($q);

                $u2 = $w->multiply($r);
                list(, $u2) = $u2->divide($q);

                $g = $g->modPow($u1, $p);
                $y = $y->modPow($u2, $p);

                $v = $g->multiply($y);
                list(, $v) = $v->divide($p);
                list(, $v) = $v->divide($q);

                if (!$v->equals($r)) {
                    user_error('Bad server signature');
                    return $this->_disconnect(NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE);
                }

                break;
            case 'ssh-rsa':
            case 'rsa-sha2-256':
            case 'rsa-sha2-512':
                if (strlen($server_public_host_key) < 4) {
                    return false;
                }
                $temp = unpack('Nlength', $this->_string_shift($server_public_host_key, 4));
                $e = new Math_BigInteger($this->_string_shift($server_public_host_key, $temp['length']), -256);

                if (strlen($server_public_host_key) < 4) {
                    return false;
                }
                $temp = unpack('Nlength', $this->_string_shift($server_public_host_key, 4));
                $rawN = $this->_string_shift($server_public_host_key, $temp['length']);
                $n = new Math_BigInteger($rawN, -256);
                $nLength = strlen(ltrim($rawN, "\0"));

                /*
                if (strlen($signature) < 4) {
                    return false;
                }
                $temp = unpack('Nlength', $this->_string_shift($signature, 4));
                $signature = $this->_string_shift($signature, $temp['length']);

                if (!class_exists('Crypt_RSA')) {
                    include_once 'Crypt/RSA.php';
                }

                $rsa = new Crypt_RSA();
                switch ($this->signature_format) {
                    case 'rsa-sha2-512':
                        $hash = 'sha512';
                        break;
                    case 'rsa-sha2-256':
                        $hash = 'sha256';
                        break;
                    //case 'ssh-rsa':
                    default:
                        $hash = 'sha1';
                }
                $rsa->setHash($hash);
                $rsa->setSignatureMode(CRYPT_RSA_SIGNATURE_PKCS1);
                $rsa->loadKey(array('e' => $e, 'n' => $n), CRYPT_RSA_PUBLIC_FORMAT_RAW);
                if (!$rsa->verify($this->exchange_hash, $signature)) {
                    user_error('Bad server signature');
                    return $this->_disconnect(NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE);
                }
                */

                if (strlen($signature) < 4) {
                    return false;
                }
                $temp = unpack('Nlength', $this->_string_shift($signature, 4));
                $s = new Math_BigInteger($this->_string_shift($signature, $temp['length']), 256);

                // validate an RSA signature per "8.2 RSASSA-PKCS1-v1_5", "5.2.2 RSAVP1", and "9.1 EMSA-PSS" in the
                // following URL:
                // ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-1/pkcs-1v2-1.pdf

                // also, see SSHRSA.c (rsa2_verifysig) in PuTTy's source.

                if ($s->compare(new Math_BigInteger()) < 0 || $s->compare($n->subtract(new Math_BigInteger(1))) > 0) {
                    user_error('Invalid signature');
                    return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
                }

                $s = $s->modPow($e, $n);
                $s = $s->toBytes();

                switch ($this->signature_format) {
                    case 'rsa-sha2-512':
                        $hash = 'sha512';
                        break;
                    case 'rsa-sha2-256':
                        $hash = 'sha256';
                        break;
                    //case 'ssh-rsa':
                    default:
                        $hash = 'sha1';
                }
                $hashObj = new Crypt_Hash($hash);
                switch ($this->signature_format) {
                    case 'rsa-sha2-512':
                        $h = pack('N5a*', 0x00305130, 0x0D060960, 0x86480165, 0x03040203, 0x05000440, $hashObj->hash($this->exchange_hash));
                        break;
                    case 'rsa-sha2-256':
                        $h = pack('N5a*', 0x00303130, 0x0D060960, 0x86480165, 0x03040201, 0x05000420, $hashObj->hash($this->exchange_hash));
                        break;
                    //case 'ssh-rsa':
                    default:
                        $hash = 'sha1';
                        $h = pack('N4a*', 0x00302130, 0x0906052B, 0x0E03021A, 0x05000414, $hashObj->hash($this->exchange_hash));
                }
                $h = chr(0x01) . str_repeat(chr(0xFF), $nLength - 2 - strlen($h)) . $h;

                if ($s != $h) {
                    user_error('Bad server signature');
                    return $this->_disconnect(NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE);
                }
                break;
            default:
                user_error('Unsupported signature format');
                return $this->_disconnect(NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE);
        }

        return $this->signature_format . ' ' . base64_encode($this->server_public_host_key);
    }

    /**
     * Returns the exit status of an SSH command or false.
     *
     * @return false|int
     * @access public
     */
    function getExitStatus()
    {
        if (is_null($this->exit_status)) {
            return false;
        }
        return $this->exit_status;
    }

    /**
     * Returns the number of columns for the terminal window size.
     *
     * @return int
     * @access public
     */
    function getWindowColumns()
    {
        return $this->windowColumns;
    }

    /**
     * Returns the number of rows for the terminal window size.
     *
     * @return int
     * @access public
     */
    function getWindowRows()
    {
        return $this->windowRows;
    }

    /**
     * Sets the number of columns for the terminal window size.
     *
     * @param int $value
     * @access public
     */
    function setWindowColumns($value)
    {
        $this->windowColumns = $value;
    }

    /**
     * Sets the number of rows for the terminal window size.
     *
     * @param int $value
     * @access public
     */
    function setWindowRows($value)
    {
        $this->windowRows = $value;
    }

    /**
     * Sets the number of columns and rows for the terminal window size.
     *
     * @param int $columns
     * @param int $rows
     * @access public
     */
    function setWindowSize($columns = 80, $rows = 24)
    {
        $this->windowColumns = $columns;
        $this->windowRows = $rows;
    }
}
vendor/phpseclib/phpseclib/phpseclib/Net/SFTP.php000064400000303751151327705700015767 0ustar00<?php

/**
 * Pure-PHP implementation of SFTP.
 *
 * PHP versions 4 and 5
 *
 * Currently only supports SFTPv2 and v3, which, according to wikipedia.org, "is the most widely used version,
 * implemented by the popular OpenSSH SFTP server".  If you want SFTPv4/5/6 support, provide me with access
 * to an SFTPv4/5/6 server.
 *
 * The API for this library is modeled after the API from PHP's {@link http://php.net/book.ftp FTP extension}.
 *
 * Here's a short example of how to use this library:
 * <code>
 * <?php
 *    include 'Net/SFTP.php';
 *
 *    $sftp = new Net_SFTP('www.domain.tld');
 *    if (!$sftp->login('username', 'password')) {
 *        exit('Login Failed');
 *    }
 *
 *    echo $sftp->pwd() . "\r\n";
 *    $sftp->put('filename.ext', 'hello, world!');
 *    print_r($sftp->nlist());
 * ?>
 * </code>
 *
 * LICENSE: Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 *
 * @category  Net
 * @package   Net_SFTP
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2009 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      http://phpseclib.sourceforge.net
 */

/**
 * Include Net_SSH2
 */
if (!class_exists('Net_SSH2')) {
    include_once 'SSH2.php';
}

/**#@+
 * @access public
 * @see self::getLog()
 */
/**
 * Returns the message numbers
 */
define('NET_SFTP_LOG_SIMPLE',  NET_SSH2_LOG_SIMPLE);
/**
 * Returns the message content
 */
define('NET_SFTP_LOG_COMPLEX', NET_SSH2_LOG_COMPLEX);
/**
 * Outputs the message content in real-time.
 */
define('NET_SFTP_LOG_REALTIME', 3);
/**#@-*/

/**
 * SFTP channel constant
 *
 * Net_SSH2::exec() uses 0 and Net_SSH2::read() / Net_SSH2::write() use 1.
 *
 * @see Net_SSH2::_send_channel_packet()
 * @see Net_SSH2::_get_channel_packet()
 * @access private
 */
define('NET_SFTP_CHANNEL', 0x100);

/**#@+
 * @access public
 * @see self::put()
 */
/**
 * Reads data from a local file.
 */
define('NET_SFTP_LOCAL_FILE',    1);
/**
 * Reads data from a string.
 */
// this value isn't really used anymore but i'm keeping it reserved for historical reasons
define('NET_SFTP_STRING',        2);
/**
 * Reads data from callback:
 * function callback($length) returns string to proceed, null for EOF
 */
define('NET_SFTP_CALLBACK',     16);
/**
 * Resumes an upload
 */
define('NET_SFTP_RESUME',        4);
/**
 * Append a local file to an already existing remote file
 */
define('NET_SFTP_RESUME_START',  8);
/**#@-*/

/**
 * Pure-PHP implementations of SFTP.
 *
 * @package Net_SFTP
 * @author  Jim Wigginton <terrafrost@php.net>
 * @access  public
 */
class Net_SFTP extends Net_SSH2
{
    /**
     * Packet Types
     *
     * @see self::Net_SFTP()
     * @var array
     * @access private
     */
    var $packet_types = array();

    /**
     * Status Codes
     *
     * @see self::Net_SFTP()
     * @var array
     * @access private
     */
    var $status_codes = array();

    /**
     * The Request ID
     *
     * The request ID exists in the off chance that a packet is sent out-of-order.  Of course, this library doesn't support
     * concurrent actions, so it's somewhat academic, here.
     *
     * @var int
     * @see self::_send_sftp_packet()
     * @access private
     */
    var $request_id = false;

    /**
     * The Packet Type
     *
     * The request ID exists in the off chance that a packet is sent out-of-order.  Of course, this library doesn't support
     * concurrent actions, so it's somewhat academic, here.
     *
     * @var int
     * @see self::_get_sftp_packet()
     * @access private
     */
    var $packet_type = -1;

    /**
     * Packet Buffer
     *
     * @var string
     * @see self::_get_sftp_packet()
     * @access private
     */
    var $packet_buffer = '';

    /**
     * Extensions supported by the server
     *
     * @var array
     * @see self::_initChannel()
     * @access private
     */
    var $extensions = array();

    /**
     * Server SFTP version
     *
     * @var int
     * @see self::_initChannel()
     * @access private
     */
    var $version;

    /**
     * Current working directory
     *
     * @var string
     * @see self::realpath()
     * @see self::chdir()
     * @access private
     */
    var $pwd = false;

    /**
     * Packet Type Log
     *
     * @see self::getLog()
     * @var array
     * @access private
     */
    var $packet_type_log = array();

    /**
     * Packet Log
     *
     * @see self::getLog()
     * @var array
     * @access private
     */
    var $packet_log = array();

    /**
     * Error information
     *
     * @see self::getSFTPErrors()
     * @see self::getLastSFTPError()
     * @var array
     * @access private
     */
    var $sftp_errors = array();

    /**
     * Stat Cache
     *
     * Rather than always having to open a directory and close it immediately there after to see if a file is a directory
     * we'll cache the results.
     *
     * @see self::_update_stat_cache()
     * @see self::_remove_from_stat_cache()
     * @see self::_query_stat_cache()
     * @var array
     * @access private
     */
    var $stat_cache = array();

    /**
     * Max SFTP Packet Size
     *
     * @see self::Net_SFTP()
     * @see self::get()
     * @var array
     * @access private
     */
    var $max_sftp_packet;

    /**
     * Stat Cache Flag
     *
     * @see self::disableStatCache()
     * @see self::enableStatCache()
     * @var bool
     * @access private
     */
    var $use_stat_cache = true;

    /**
     * Sort Options
     *
     * @see self::_comparator()
     * @see self::setListOrder()
     * @var array
     * @access private
     */
    var $sortOptions = array();

    /**
     * Canonicalization Flag
     *
     * Determines whether or not paths should be canonicalized before being
     * passed on to the remote server.
     *
     * @see self::enablePathCanonicalization()
     * @see self::disablePathCanonicalization()
     * @see self::realpath()
     * @var bool
     * @access private
     */
    var $canonicalize_paths = true;

    var $file_types = array();
    var $attributes = array();
    var $open_flags = array();

    /**
     * Default Constructor.
     *
     * Connects to an SFTP server
     *
     * @param string $host
     * @param int $port
     * @param int $timeout
     * @return Net_SFTP
     * @access public
     */
    function __construct($host, $port = 22, $timeout = 10)
    {
        parent::__construct($host, $port, $timeout);

        $this->max_sftp_packet = 1 << 15;

        $this->packet_types = array(
            1  => 'NET_SFTP_INIT',
            2  => 'NET_SFTP_VERSION',
            /* the format of SSH_FXP_OPEN changed between SFTPv4 and SFTPv5+:
                   SFTPv5+: http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.1.1
               pre-SFTPv5 : http://tools.ietf.org/html/draft-ietf-secsh-filexfer-04#section-6.3 */
            3  => 'NET_SFTP_OPEN',
            4  => 'NET_SFTP_CLOSE',
            5  => 'NET_SFTP_READ',
            6  => 'NET_SFTP_WRITE',
            7  => 'NET_SFTP_LSTAT',
            9  => 'NET_SFTP_SETSTAT',
            11 => 'NET_SFTP_OPENDIR',
            12 => 'NET_SFTP_READDIR',
            13 => 'NET_SFTP_REMOVE',
            14 => 'NET_SFTP_MKDIR',
            15 => 'NET_SFTP_RMDIR',
            16 => 'NET_SFTP_REALPATH',
            17 => 'NET_SFTP_STAT',
            /* the format of SSH_FXP_RENAME changed between SFTPv4 and SFTPv5+:
                   SFTPv5+: http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.3
               pre-SFTPv5 : http://tools.ietf.org/html/draft-ietf-secsh-filexfer-04#section-6.5 */
            18 => 'NET_SFTP_RENAME',
            19 => 'NET_SFTP_READLINK',
            20 => 'NET_SFTP_SYMLINK',

            101=> 'NET_SFTP_STATUS',
            102=> 'NET_SFTP_HANDLE',
            /* the format of SSH_FXP_NAME changed between SFTPv3 and SFTPv4+:
                   SFTPv4+: http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-9.4
               pre-SFTPv4 : http://tools.ietf.org/html/draft-ietf-secsh-filexfer-02#section-7 */
            103=> 'NET_SFTP_DATA',
            104=> 'NET_SFTP_NAME',
            105=> 'NET_SFTP_ATTRS',

            200=> 'NET_SFTP_EXTENDED'
        );
        $this->status_codes = array(
            0 => 'NET_SFTP_STATUS_OK',
            1 => 'NET_SFTP_STATUS_EOF',
            2 => 'NET_SFTP_STATUS_NO_SUCH_FILE',
            3 => 'NET_SFTP_STATUS_PERMISSION_DENIED',
            4 => 'NET_SFTP_STATUS_FAILURE',
            5 => 'NET_SFTP_STATUS_BAD_MESSAGE',
            6 => 'NET_SFTP_STATUS_NO_CONNECTION',
            7 => 'NET_SFTP_STATUS_CONNECTION_LOST',
            8 => 'NET_SFTP_STATUS_OP_UNSUPPORTED',
            9 => 'NET_SFTP_STATUS_INVALID_HANDLE',
            10 => 'NET_SFTP_STATUS_NO_SUCH_PATH',
            11 => 'NET_SFTP_STATUS_FILE_ALREADY_EXISTS',
            12 => 'NET_SFTP_STATUS_WRITE_PROTECT',
            13 => 'NET_SFTP_STATUS_NO_MEDIA',
            14 => 'NET_SFTP_STATUS_NO_SPACE_ON_FILESYSTEM',
            15 => 'NET_SFTP_STATUS_QUOTA_EXCEEDED',
            16 => 'NET_SFTP_STATUS_UNKNOWN_PRINCIPAL',
            17 => 'NET_SFTP_STATUS_LOCK_CONFLICT',
            18 => 'NET_SFTP_STATUS_DIR_NOT_EMPTY',
            19 => 'NET_SFTP_STATUS_NOT_A_DIRECTORY',
            20 => 'NET_SFTP_STATUS_INVALID_FILENAME',
            21 => 'NET_SFTP_STATUS_LINK_LOOP',
            22 => 'NET_SFTP_STATUS_CANNOT_DELETE',
            23 => 'NET_SFTP_STATUS_INVALID_PARAMETER',
            24 => 'NET_SFTP_STATUS_FILE_IS_A_DIRECTORY',
            25 => 'NET_SFTP_STATUS_BYTE_RANGE_LOCK_CONFLICT',
            26 => 'NET_SFTP_STATUS_BYTE_RANGE_LOCK_REFUSED',
            27 => 'NET_SFTP_STATUS_DELETE_PENDING',
            28 => 'NET_SFTP_STATUS_FILE_CORRUPT',
            29 => 'NET_SFTP_STATUS_OWNER_INVALID',
            30 => 'NET_SFTP_STATUS_GROUP_INVALID',
            31 => 'NET_SFTP_STATUS_NO_MATCHING_BYTE_RANGE_LOCK'
        );
        // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-7.1
        // the order, in this case, matters quite a lot - see Net_SFTP::_parseAttributes() to understand why
        $this->attributes = array(
            0x00000001 => 'NET_SFTP_ATTR_SIZE',
            0x00000002 => 'NET_SFTP_ATTR_UIDGID', // defined in SFTPv3, removed in SFTPv4+
            0x00000004 => 'NET_SFTP_ATTR_PERMISSIONS',
            0x00000008 => 'NET_SFTP_ATTR_ACCESSTIME',
            // 0x80000000 will yield a floating point on 32-bit systems and converting floating points to integers
            // yields inconsistent behavior depending on how php is compiled.  so we left shift -1 (which, in
            // two's compliment, consists of all 1 bits) by 31.  on 64-bit systems this'll yield 0xFFFFFFFF80000000.
            // that's not a problem, however, and 'anded' and a 32-bit number, as all the leading 1 bits are ignored.
            (-1 << 31) & 0xFFFFFFFF => 'NET_SFTP_ATTR_EXTENDED'
        );
        // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-04#section-6.3
        // the flag definitions change somewhat in SFTPv5+.  if SFTPv5+ support is added to this library, maybe name
        // the array for that $this->open5_flags and similarly alter the constant names.
        $this->open_flags = array(
            0x00000001 => 'NET_SFTP_OPEN_READ',
            0x00000002 => 'NET_SFTP_OPEN_WRITE',
            0x00000004 => 'NET_SFTP_OPEN_APPEND',
            0x00000008 => 'NET_SFTP_OPEN_CREATE',
            0x00000010 => 'NET_SFTP_OPEN_TRUNCATE',
            0x00000020 => 'NET_SFTP_OPEN_EXCL'
        );
        // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-04#section-5.2
        // see Net_SFTP::_parseLongname() for an explanation
        $this->file_types = array(
            1 => 'NET_SFTP_TYPE_REGULAR',
            2 => 'NET_SFTP_TYPE_DIRECTORY',
            3 => 'NET_SFTP_TYPE_SYMLINK',
            4 => 'NET_SFTP_TYPE_SPECIAL',
            5 => 'NET_SFTP_TYPE_UNKNOWN',
            // the followin types were first defined for use in SFTPv5+
            // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-05#section-5.2
            6 => 'NET_SFTP_TYPE_SOCKET',
            7 => 'NET_SFTP_TYPE_CHAR_DEVICE',
            8 => 'NET_SFTP_TYPE_BLOCK_DEVICE',
            9 => 'NET_SFTP_TYPE_FIFO'
        );
        $this->_define_array(
            $this->packet_types,
            $this->status_codes,
            $this->attributes,
            $this->open_flags,
            $this->file_types
        );

        if (!defined('NET_SFTP_QUEUE_SIZE')) {
            define('NET_SFTP_QUEUE_SIZE', 32);
        }
    }

    /**
     * PHP4 compatible Default Constructor.
     *
     * @see self::__construct()
     * @param string $host
     * @param int $port
     * @param int $timeout
     * @access public
     */
    function Net_SFTP($host, $port = 22, $timeout = 10)
    {
        $this->__construct($host, $port, $timeout);
    }

    /**
     * Login
     *
     * @param string $username
     * @param string $password
     * @return bool
     * @access public
     */
    function login($username)
    {
        $args = func_get_args();
        if (!call_user_func_array(array(&$this, '_login'), $args)) {
            return false;
        }

        $this->window_size_server_to_client[NET_SFTP_CHANNEL] = $this->window_size;

        $packet = pack(
            'CNa*N3',
            NET_SSH2_MSG_CHANNEL_OPEN,
            strlen('session'),
            'session',
            NET_SFTP_CHANNEL,
            $this->window_size,
            0x4000
        );

        if (!$this->_send_binary_packet($packet)) {
            return false;
        }

        $this->channel_status[NET_SFTP_CHANNEL] = NET_SSH2_MSG_CHANNEL_OPEN;

        $response = $this->_get_channel_packet(NET_SFTP_CHANNEL, true);
        if ($response === false) {
            return false;
        }

        $packet = pack(
            'CNNa*CNa*',
            NET_SSH2_MSG_CHANNEL_REQUEST,
            $this->server_channels[NET_SFTP_CHANNEL],
            strlen('subsystem'),
            'subsystem',
            1,
            strlen('sftp'),
            'sftp'
        );
        if (!$this->_send_binary_packet($packet)) {
            return false;
        }

        $this->channel_status[NET_SFTP_CHANNEL] = NET_SSH2_MSG_CHANNEL_REQUEST;

        $response = $this->_get_channel_packet(NET_SFTP_CHANNEL, true);
        if ($response === false) {
            // from PuTTY's psftp.exe
            $command = "test -x /usr/lib/sftp-server && exec /usr/lib/sftp-server\n" .
                       "test -x /usr/local/lib/sftp-server && exec /usr/local/lib/sftp-server\n" .
                       "exec sftp-server";
            // we don't do $this->exec($command, false) because exec() operates on a different channel and plus the SSH_MSG_CHANNEL_OPEN that exec() does
            // is redundant
            $packet = pack(
                'CNNa*CNa*',
                NET_SSH2_MSG_CHANNEL_REQUEST,
                $this->server_channels[NET_SFTP_CHANNEL],
                strlen('exec'),
                'exec',
                1,
                strlen($command),
                $command
            );
            if (!$this->_send_binary_packet($packet)) {
                return false;
            }

            $this->channel_status[NET_SFTP_CHANNEL] = NET_SSH2_MSG_CHANNEL_REQUEST;

            $response = $this->_get_channel_packet(NET_SFTP_CHANNEL, true);
            if ($response === false) {
                return false;
            }
        }

        $this->channel_status[NET_SFTP_CHANNEL] = NET_SSH2_MSG_CHANNEL_DATA;

        if (!$this->_send_sftp_packet(NET_SFTP_INIT, "\0\0\0\3")) {
            return false;
        }

        $response = $this->_get_sftp_packet();
        if ($this->packet_type != NET_SFTP_VERSION) {
            user_error('Expected SSH_FXP_VERSION');
            return false;
        }

        if (strlen($response) < 4) {
            return false;
        }
        extract(unpack('Nversion', $this->_string_shift($response, 4)));
        $this->version = $version;
        while (!empty($response)) {
            if (strlen($response) < 4) {
                return false;
            }
            extract(unpack('Nlength', $this->_string_shift($response, 4)));
            $key = $this->_string_shift($response, $length);
            if (strlen($response) < 4) {
                return false;
            }
            extract(unpack('Nlength', $this->_string_shift($response, 4)));
            $value = $this->_string_shift($response, $length);
            $this->extensions[$key] = $value;
        }

        /*
         SFTPv4+ defines a 'newline' extension.  SFTPv3 seems to have unofficial support for it via 'newline@vandyke.com',
         however, I'm not sure what 'newline@vandyke.com' is supposed to do (the fact that it's unofficial means that it's
         not in the official SFTPv3 specs) and 'newline@vandyke.com' / 'newline' are likely not drop-in substitutes for
         one another due to the fact that 'newline' comes with a SSH_FXF_TEXT bitmask whereas it seems unlikely that
         'newline@vandyke.com' would.
        */
        /*
        if (isset($this->extensions['newline@vandyke.com'])) {
            $this->extensions['newline'] = $this->extensions['newline@vandyke.com'];
            unset($this->extensions['newline@vandyke.com']);
        }
        */

        $this->request_id = 1;

        /*
         A Note on SFTPv4/5/6 support:
         <http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-5.1> states the following:

         "If the client wishes to interoperate with servers that support noncontiguous version
          numbers it SHOULD send '3'"

         Given that the server only sends its version number after the client has already done so, the above
         seems to be suggesting that v3 should be the default version.  This makes sense given that v3 is the
         most popular.

         <http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-5.5> states the following;

         "If the server did not send the "versions" extension, or the version-from-list was not included, the
          server MAY send a status response describing the failure, but MUST then close the channel without
          processing any further requests."

         So what do you do if you have a client whose initial SSH_FXP_INIT packet says it implements v3 and
         a server whose initial SSH_FXP_VERSION reply says it implements v4 and only v4?  If it only implements
         v4, the "versions" extension is likely not going to have been sent so version re-negotiation as discussed
         in draft-ietf-secsh-filexfer-13 would be quite impossible.  As such, what Net_SFTP would do is close the
         channel and reopen it with a new and updated SSH_FXP_INIT packet.
        */
        switch ($this->version) {
            case 2:
            case 3:
                break;
            default:
                return false;
        }

        $this->pwd = $this->_realpath('.');

        $this->_update_stat_cache($this->pwd, array());

        return true;
    }

    /**
     * Disable the stat cache
     *
     * @access public
     */
    function disableStatCache()
    {
        $this->use_stat_cache = false;
    }

    /**
     * Enable the stat cache
     *
     * @access public
     */
    function enableStatCache()
    {
        $this->use_stat_cache = true;
    }

    /**
     * Clear the stat cache
     *
     * @access public
     */
    function clearStatCache()
    {
        $this->stat_cache = array();
    }

    /**
     * Enable path canonicalization
     *
     * @access public
     */
    function enablePathCanonicalization()
    {
        $this->canonicalize_paths = true;
    }

    /**
     * Enable path canonicalization
     *
     * @access public
     */
    function disablePathCanonicalization()
    {
        $this->canonicalize_paths = false;
    }

    /**
     * Returns the current directory name
     *
     * @return mixed
     * @access public
     */
    function pwd()
    {
        return $this->pwd;
    }

    /**
     * Logs errors
     *
     * @param string $response
     * @param int $status
     * @access public
     */
    function _logError($response, $status = -1)
    {
        if ($status == -1) {
            if (strlen($response) < 4) {
                return;
            }
            extract(unpack('Nstatus', $this->_string_shift($response, 4)));
        }

        $error = $this->status_codes[$status];

        if ($this->version > 2 || strlen($response) < 4) {
            extract(unpack('Nlength', $this->_string_shift($response, 4)));
            $this->sftp_errors[] = $error . ': ' . $this->_string_shift($response, $length);
        } else {
            $this->sftp_errors[] = $error;
        }
    }

    /**
     * Returns canonicalized absolute pathname
     *
     * realpath() expands all symbolic links and resolves references to '/./', '/../' and extra '/' characters in the input
     * path and returns the canonicalized absolute pathname.
     *
     * @param string $path
     * @return mixed
     * @access public
     */
    function realpath($path)
    {
        return $this->_realpath($path);
    }

    /**
     * Canonicalize the Server-Side Path Name
     *
     * SFTP doesn't provide a mechanism by which the current working directory can be changed, so we'll emulate it.  Returns
     * the absolute (canonicalized) path.
     *
     * If canonicalize_paths has been disabled using disablePathCanonicalization(), $path is returned as-is.
     *
     * @see self::chdir()
     * @see self::disablePathCanonicalization()
     * @param string $path
     * @return mixed
     * @access private
     */
    function _realpath($path)
    {
        if (!$this->canonicalize_paths) {
            return $path;
        }

        if ($this->pwd === false) {
            // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.9
            if (!$this->_send_sftp_packet(NET_SFTP_REALPATH, pack('Na*', strlen($path), $path))) {
                return false;
            }

            $response = $this->_get_sftp_packet();
            switch ($this->packet_type) {
                case NET_SFTP_NAME:
                    // although SSH_FXP_NAME is implemented differently in SFTPv3 than it is in SFTPv4+, the following
                    // should work on all SFTP versions since the only part of the SSH_FXP_NAME packet the following looks
                    // at is the first part and that part is defined the same in SFTP versions 3 through 6.
                    $this->_string_shift($response, 4); // skip over the count - it should be 1, anyway
                    if (strlen($response) < 4) {
                        return false;
                    }
                    extract(unpack('Nlength', $this->_string_shift($response, 4)));
                    return $this->_string_shift($response, $length);
                case NET_SFTP_STATUS:
                    $this->_logError($response);
                    return false;
                default:
                    user_error('Expected SSH_FXP_NAME or SSH_FXP_STATUS');
                    return false;
            }
        }

        if ($path[0] != '/') {
            $path = $this->pwd . '/' . $path;
        }

        $path = explode('/', $path);
        $new = array();
        foreach ($path as $dir) {
            if (!strlen($dir)) {
                continue;
            }
            switch ($dir) {
                case '..':
                    array_pop($new);
                case '.':
                    break;
                default:
                    $new[] = $dir;
            }
        }

        return '/' . implode('/', $new);
    }

    /**
     * Changes the current directory
     *
     * @param string $dir
     * @return bool
     * @access public
     */
    function chdir($dir)
    {
        if (!($this->bitmap & NET_SSH2_MASK_LOGIN)) {
            return false;
        }

        // assume current dir if $dir is empty
        if ($dir === '') {
            $dir = './';
        // suffix a slash if needed
        } elseif ($dir[strlen($dir) - 1] != '/') {
            $dir.= '/';
        }

        $dir = $this->_realpath($dir);

        // confirm that $dir is, in fact, a valid directory
        if ($this->use_stat_cache && is_array($this->_query_stat_cache($dir))) {
            $this->pwd = $dir;
            return true;
        }

        // we could do a stat on the alleged $dir to see if it's a directory but that doesn't tell us
        // the currently logged in user has the appropriate permissions or not. maybe you could see if
        // the file's uid / gid match the currently logged in user's uid / gid but how there's no easy
        // way to get those with SFTP

        if (!$this->_send_sftp_packet(NET_SFTP_OPENDIR, pack('Na*', strlen($dir), $dir))) {
            return false;
        }

        // see Net_SFTP::nlist() for a more thorough explanation of the following
        $response = $this->_get_sftp_packet();
        switch ($this->packet_type) {
            case NET_SFTP_HANDLE:
                $handle = substr($response, 4);
                break;
            case NET_SFTP_STATUS:
                $this->_logError($response);
                return false;
            default:
                user_error('Expected SSH_FXP_HANDLE or SSH_FXP_STATUS');
                return false;
        }

        if (!$this->_close_handle($handle)) {
            return false;
        }

        $this->_update_stat_cache($dir, array());

        $this->pwd = $dir;
        return true;
    }

    /**
     * Returns a list of files in the given directory
     *
     * @param string $dir
     * @param bool $recursive
     * @return mixed
     * @access public
     */
    function nlist($dir = '.', $recursive = false)
    {
        return $this->_nlist_helper($dir, $recursive, '');
    }

    /**
     * Helper method for nlist
     *
     * @param string $dir
     * @param bool $recursive
     * @param string $relativeDir
     * @return mixed
     * @access private
     */
    function _nlist_helper($dir, $recursive, $relativeDir)
    {
        $files = $this->_list($dir, false);

        if (!$recursive || $files === false) {
            return $files;
        }

        $result = array();
        foreach ($files as $value) {
            if ($value == '.' || $value == '..') {
                if ($relativeDir == '') {
                    $result[] = $value;
                }
                continue;
            }
            if (is_array($this->_query_stat_cache($this->_realpath($dir . '/' . $value)))) {
                $temp = $this->_nlist_helper($dir . '/' . $value, true, $relativeDir . $value . '/');
                $result = array_merge($result, $temp);
            } else {
                $result[] = $relativeDir . $value;
            }
        }

        return $result;
    }

    /**
     * Returns a detailed list of files in the given directory
     *
     * @param string $dir
     * @param bool $recursive
     * @return mixed
     * @access public
     */
    function rawlist($dir = '.', $recursive = false)
    {
        $files = $this->_list($dir, true);
        if (!$recursive || $files === false) {
            return $files;
        }

        static $depth = 0;

        foreach ($files as $key => $value) {
            if ($depth != 0 && $key == '..') {
                unset($files[$key]);
                continue;
            }
            $is_directory = false;
            if ($key != '.' && $key != '..') {
                if ($this->use_stat_cache) {
                    $is_directory = is_array($this->_query_stat_cache($this->_realpath($dir . '/' . $key)));
                } else {
                    $stat = $this->lstat($dir . '/' . $key);
                    $is_directory = $stat && $stat['type'] === NET_SFTP_TYPE_DIRECTORY;
                }
            }

            if ($is_directory) {
                $depth++;
                $files[$key] = $this->rawlist($dir . '/' . $key, true);
                $depth--;
            } else {
                $files[$key] = (object) $value;
            }
        }

        return $files;
    }

    /**
     * Reads a list, be it detailed or not, of files in the given directory
     *
     * @param string $dir
     * @param bool $raw
     * @return mixed
     * @access private
     */
    function _list($dir, $raw = true)
    {
        if (!($this->bitmap & NET_SSH2_MASK_LOGIN)) {
            return false;
        }

        $dir = $this->_realpath($dir . '/');
        if ($dir === false) {
            return false;
        }

        // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.1.2
        if (!$this->_send_sftp_packet(NET_SFTP_OPENDIR, pack('Na*', strlen($dir), $dir))) {
            return false;
        }

        $response = $this->_get_sftp_packet();
        switch ($this->packet_type) {
            case NET_SFTP_HANDLE:
                // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-9.2
                // since 'handle' is the last field in the SSH_FXP_HANDLE packet, we'll just remove the first four bytes that
                // represent the length of the string and leave it at that
                $handle = substr($response, 4);
                break;
            case NET_SFTP_STATUS:
                // presumably SSH_FX_NO_SUCH_FILE or SSH_FX_PERMISSION_DENIED
                $this->_logError($response);
                return false;
            default:
                user_error('Expected SSH_FXP_HANDLE or SSH_FXP_STATUS');
                return false;
        }

        $this->_update_stat_cache($dir, array());

        $contents = array();
        while (true) {
            // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.2.2
            // why multiple SSH_FXP_READDIR packets would be sent when the response to a single one can span arbitrarily many
            // SSH_MSG_CHANNEL_DATA messages is not known to me.
            if (!$this->_send_sftp_packet(NET_SFTP_READDIR, pack('Na*', strlen($handle), $handle))) {
                return false;
            }

            $response = $this->_get_sftp_packet();
            switch ($this->packet_type) {
                case NET_SFTP_NAME:
                    if (strlen($response) < 4) {
                        return false;
                    }
                    extract(unpack('Ncount', $this->_string_shift($response, 4)));
                    for ($i = 0; $i < $count; $i++) {
                        if (strlen($response) < 4) {
                            return false;
                        }
                        extract(unpack('Nlength', $this->_string_shift($response, 4)));
                        $shortname = $this->_string_shift($response, $length);
                        if (strlen($response) < 4) {
                            return false;
                        }
                        extract(unpack('Nlength', $this->_string_shift($response, 4)));
                        $longname = $this->_string_shift($response, $length);
                        $attributes = $this->_parseAttributes($response);
                        if (!isset($attributes['type'])) {
                            $fileType = $this->_parseLongname($longname);
                            if ($fileType) {
                                $attributes['type'] = $fileType;
                            }
                        }
                        $contents[$shortname] = $attributes + array('filename' => $shortname);

                        if (isset($attributes['type']) && $attributes['type'] == NET_SFTP_TYPE_DIRECTORY && ($shortname != '.' && $shortname != '..')) {
                            $this->_update_stat_cache($dir . '/' . $shortname, array());
                        } else {
                            if ($shortname == '..') {
                                $temp = $this->_realpath($dir . '/..') . '/.';
                            } else {
                                $temp = $dir . '/' . $shortname;
                            }
                            $this->_update_stat_cache($temp, (object) array('lstat' => $attributes));
                        }
                        // SFTPv6 has an optional boolean end-of-list field, but we'll ignore that, since the
                        // final SSH_FXP_STATUS packet should tell us that, already.
                    }
                    break;
                case NET_SFTP_STATUS:
                    if (strlen($response) < 4) {
                        return false;
                    }
                    extract(unpack('Nstatus', $this->_string_shift($response, 4)));
                    if ($status != NET_SFTP_STATUS_EOF) {
                        $this->_logError($response, $status);
                        return false;
                    }
                    break 2;
                default:
                    user_error('Expected SSH_FXP_NAME or SSH_FXP_STATUS');
                    return false;
            }
        }

        if (!$this->_close_handle($handle)) {
            return false;
        }

        if (count($this->sortOptions)) {
            uasort($contents, array(&$this, '_comparator'));
        }

        return $raw ? $contents : array_keys($contents);
    }

    /**
     * Compares two rawlist entries using parameters set by setListOrder()
     *
     * Intended for use with uasort()
     *
     * @param array $a
     * @param array $b
     * @return int
     * @access private
     */
    function _comparator($a, $b)
    {
        switch (true) {
            case $a['filename'] === '.' || $b['filename'] === '.':
                if ($a['filename'] === $b['filename']) {
                    return 0;
                }
                return $a['filename'] === '.' ? -1 : 1;
            case $a['filename'] === '..' || $b['filename'] === '..':
                if ($a['filename'] === $b['filename']) {
                    return 0;
                }
                return $a['filename'] === '..' ? -1 : 1;
            case isset($a['type']) && $a['type'] === NET_SFTP_TYPE_DIRECTORY:
                if (!isset($b['type'])) {
                    return 1;
                }
                if ($b['type'] !== $a['type']) {
                    return -1;
                }
                break;
            case isset($b['type']) && $b['type'] === NET_SFTP_TYPE_DIRECTORY:
                return 1;
        }
        foreach ($this->sortOptions as $sort => $order) {
            if (!isset($a[$sort]) || !isset($b[$sort])) {
                if (isset($a[$sort])) {
                    return -1;
                }
                if (isset($b[$sort])) {
                    return 1;
                }
                return 0;
            }
            switch ($sort) {
                case 'filename':
                    $result = strcasecmp($a['filename'], $b['filename']);
                    if ($result) {
                        return $order === SORT_DESC ? -$result : $result;
                    }
                    break;
                case 'permissions':
                case 'mode':
                    $a[$sort]&= 07777;
                    $b[$sort]&= 07777;
                default:
                    if ($a[$sort] === $b[$sort]) {
                        break;
                    }
                    return $order === SORT_ASC ? $a[$sort] - $b[$sort] : $b[$sort] - $a[$sort];
            }
        }
    }

    /**
     * Defines how nlist() and rawlist() will be sorted - if at all.
     *
     * If sorting is enabled directories and files will be sorted independently with
     * directories appearing before files in the resultant array that is returned.
     *
     * Any parameter returned by stat is a valid sort parameter for this function.
     * Filename comparisons are case insensitive.
     *
     * Examples:
     *
     * $sftp->setListOrder('filename', SORT_ASC);
     * $sftp->setListOrder('size', SORT_DESC, 'filename', SORT_ASC);
     * $sftp->setListOrder(true);
     *    Separates directories from files but doesn't do any sorting beyond that
     * $sftp->setListOrder();
     *    Don't do any sort of sorting
     *
     * @access public
     */
    function setListOrder()
    {
        $this->sortOptions = array();
        $args = func_get_args();
        if (empty($args)) {
            return;
        }
        $len = count($args) & 0x7FFFFFFE;
        for ($i = 0; $i < $len; $i+=2) {
            $this->sortOptions[$args[$i]] = $args[$i + 1];
        }
        if (!count($this->sortOptions)) {
            $this->sortOptions = array('bogus' => true);
        }
    }

    /**
     * Returns the file size, in bytes, or false, on failure
     *
     * Files larger than 4GB will show up as being exactly 4GB.
     *
     * @param string $filename
     * @return mixed
     * @access public
     */
    function size($filename)
    {
        if (!($this->bitmap & NET_SSH2_MASK_LOGIN)) {
            return false;
        }

        $result = $this->stat($filename);
        if ($result === false) {
            return false;
        }
        return isset($result['size']) ? $result['size'] : -1;
    }

    /**
     * Save files / directories to cache
     *
     * @param string $path
     * @param mixed $value
     * @access private
     */
    function _update_stat_cache($path, $value)
    {
        if ($this->use_stat_cache === false) {
            return;
        }

        // preg_replace('#^/|/(?=/)|/$#', '', $dir) == str_replace('//', '/', trim($path, '/'))
        $dirs = explode('/', preg_replace('#^/|/(?=/)|/$#', '', $path));

        $temp = &$this->stat_cache;
        $max = count($dirs) - 1;
        foreach ($dirs as $i => $dir) {
            // if $temp is an object that means one of two things.
            //  1. a file was deleted and changed to a directory behind phpseclib's back
            //  2. it's a symlink. when lstat is done it's unclear what it's a symlink to
            if (is_object($temp)) {
                $temp = array();
            }
            if (!isset($temp[$dir])) {
                $temp[$dir] = array();
            }
            if ($i === $max) {
                if (is_object($temp[$dir]) && is_object($value)) {
                    if (!isset($value->stat) && isset($temp[$dir]->stat)) {
                        $value->stat = $temp[$dir]->stat;
                    }
                    if (!isset($value->lstat) && isset($temp[$dir]->lstat)) {
                        $value->lstat = $temp[$dir]->lstat;
                    }
                }
                $temp[$dir] = $value;
                break;
            }
            $temp = &$temp[$dir];
        }
    }

    /**
     * Remove files / directories from cache
     *
     * @param string $path
     * @return bool
     * @access private
     */
    function _remove_from_stat_cache($path)
    {
        $dirs = explode('/', preg_replace('#^/|/(?=/)|/$#', '', $path));

        $temp = &$this->stat_cache;
        $max = count($dirs) - 1;
        foreach ($dirs as $i => $dir) {
            if ($i === $max) {
                unset($temp[$dir]);
                return true;
            }
            if (!isset($temp[$dir])) {
                return false;
            }
            $temp = &$temp[$dir];
        }
    }

    /**
     * Checks cache for path
     *
     * Mainly used by file_exists
     *
     * @param string $dir
     * @return mixed
     * @access private
     */
    function _query_stat_cache($path)
    {
        $dirs = explode('/', preg_replace('#^/|/(?=/)|/$#', '', $path));

        $temp = &$this->stat_cache;
        foreach ($dirs as $dir) {
            if (!isset($temp[$dir])) {
                return null;
            }
            $temp = &$temp[$dir];
        }
        return $temp;
    }

    /**
     * Returns general information about a file.
     *
     * Returns an array on success and false otherwise.
     *
     * @param string $filename
     * @return mixed
     * @access public
     */
    function stat($filename)
    {
        if (!($this->bitmap & NET_SSH2_MASK_LOGIN)) {
            return false;
        }

        $filename = $this->_realpath($filename);
        if ($filename === false) {
            return false;
        }

        if ($this->use_stat_cache) {
            $result = $this->_query_stat_cache($filename);
            if (is_array($result) && isset($result['.']) && isset($result['.']->stat)) {
                return $result['.']->stat;
            }
            if (is_object($result) && isset($result->stat)) {
                return $result->stat;
            }
        }

        $stat = $this->_stat($filename, NET_SFTP_STAT);
        if ($stat === false) {
            $this->_remove_from_stat_cache($filename);
            return false;
        }
        if (isset($stat['type'])) {
            if ($stat['type'] == NET_SFTP_TYPE_DIRECTORY) {
                $filename.= '/.';
            }
            $this->_update_stat_cache($filename, (object) array('stat' => $stat));
            return $stat;
        }

        $pwd = $this->pwd;
        $stat['type'] = $this->chdir($filename) ?
            NET_SFTP_TYPE_DIRECTORY :
            NET_SFTP_TYPE_REGULAR;
        $this->pwd = $pwd;

        if ($stat['type'] == NET_SFTP_TYPE_DIRECTORY) {
            $filename.= '/.';
        }
        $this->_update_stat_cache($filename, (object) array('stat' => $stat));

        return $stat;
    }

    /**
     * Returns general information about a file or symbolic link.
     *
     * Returns an array on success and false otherwise.
     *
     * @param string $filename
     * @return mixed
     * @access public
     */
    function lstat($filename)
    {
        if (!($this->bitmap & NET_SSH2_MASK_LOGIN)) {
            return false;
        }

        $filename = $this->_realpath($filename);
        if ($filename === false) {
            return false;
        }

        if ($this->use_stat_cache) {
            $result = $this->_query_stat_cache($filename);
            if (is_array($result) && isset($result['.']) && isset($result['.']->lstat)) {
                return $result['.']->lstat;
            }
            if (is_object($result) && isset($result->lstat)) {
                return $result->lstat;
            }
        }

        $lstat = $this->_stat($filename, NET_SFTP_LSTAT);
        if ($lstat === false) {
            $this->_remove_from_stat_cache($filename);
            return false;
        }
        if (isset($lstat['type'])) {
            if ($lstat['type'] == NET_SFTP_TYPE_DIRECTORY) {
                $filename.= '/.';
            }
            $this->_update_stat_cache($filename, (object) array('lstat' => $lstat));
            return $lstat;
        }

        $stat = $this->_stat($filename, NET_SFTP_STAT);

        if ($lstat != $stat) {
            $lstat = array_merge($lstat, array('type' => NET_SFTP_TYPE_SYMLINK));
            $this->_update_stat_cache($filename, (object) array('lstat' => $lstat));
            return $stat;
        }

        $pwd = $this->pwd;
        $lstat['type'] = $this->chdir($filename) ?
            NET_SFTP_TYPE_DIRECTORY :
            NET_SFTP_TYPE_REGULAR;
        $this->pwd = $pwd;

        if ($lstat['type'] == NET_SFTP_TYPE_DIRECTORY) {
            $filename.= '/.';
        }
        $this->_update_stat_cache($filename, (object) array('lstat' => $lstat));

        return $lstat;
    }

    /**
     * Returns general information about a file or symbolic link
     *
     * Determines information without calling Net_SFTP::realpath().
     * The second parameter can be either NET_SFTP_STAT or NET_SFTP_LSTAT.
     *
     * @param string $filename
     * @param int $type
     * @return mixed
     * @access private
     */
    function _stat($filename, $type)
    {
        // SFTPv4+ adds an additional 32-bit integer field - flags - to the following:
        $packet = pack('Na*', strlen($filename), $filename);
        if (!$this->_send_sftp_packet($type, $packet)) {
            return false;
        }

        $response = $this->_get_sftp_packet();
        switch ($this->packet_type) {
            case NET_SFTP_ATTRS:
                return $this->_parseAttributes($response);
            case NET_SFTP_STATUS:
                $this->_logError($response);
                return false;
        }

        user_error('Expected SSH_FXP_ATTRS or SSH_FXP_STATUS');
        return false;
    }

    /**
     * Truncates a file to a given length
     *
     * @param string $filename
     * @param int $new_size
     * @return bool
     * @access public
     */
    function truncate($filename, $new_size)
    {
        $attr = pack('N3', NET_SFTP_ATTR_SIZE, $new_size / 4294967296, $new_size); // 4294967296 == 0x100000000 == 1<<32

        return $this->_setstat($filename, $attr, false);
    }

    /**
     * Sets access and modification time of file.
     *
     * If the file does not exist, it will be created.
     *
     * @param string $filename
     * @param int $time
     * @param int $atime
     * @return bool
     * @access public
     */
    function touch($filename, $time = null, $atime = null)
    {
        if (!($this->bitmap & NET_SSH2_MASK_LOGIN)) {
            return false;
        }

        $filename = $this->_realpath($filename);
        if ($filename === false) {
            return false;
        }

        if (!isset($time)) {
            $time = time();
        }
        if (!isset($atime)) {
            $atime = $time;
        }

        $flags = NET_SFTP_OPEN_WRITE | NET_SFTP_OPEN_CREATE | NET_SFTP_OPEN_EXCL;
        $attr = pack('N3', NET_SFTP_ATTR_ACCESSTIME, $time, $atime);
        $packet = pack('Na*Na*', strlen($filename), $filename, $flags, $attr);
        if (!$this->_send_sftp_packet(NET_SFTP_OPEN, $packet)) {
            return false;
        }

        $response = $this->_get_sftp_packet();
        switch ($this->packet_type) {
            case NET_SFTP_HANDLE:
                return $this->_close_handle(substr($response, 4));
            case NET_SFTP_STATUS:
                $this->_logError($response);
                break;
            default:
                user_error('Expected SSH_FXP_HANDLE or SSH_FXP_STATUS');
                return false;
        }

        return $this->_setstat($filename, $attr, false);
    }

    /**
     * Changes file or directory owner
     *
     * Returns true on success or false on error.
     *
     * @param string $filename
     * @param int $uid
     * @param bool $recursive
     * @return bool
     * @access public
     */
    function chown($filename, $uid, $recursive = false)
    {
        // quoting from <http://www.kernel.org/doc/man-pages/online/pages/man2/chown.2.html>,
        // "if the owner or group is specified as -1, then that ID is not changed"
        $attr = pack('N3', NET_SFTP_ATTR_UIDGID, $uid, -1);

        return $this->_setstat($filename, $attr, $recursive);
    }

    /**
     * Changes file or directory group
     *
     * Returns true on success or false on error.
     *
     * @param string $filename
     * @param int $gid
     * @param bool $recursive
     * @return bool
     * @access public
     */
    function chgrp($filename, $gid, $recursive = false)
    {
        $attr = pack('N3', NET_SFTP_ATTR_UIDGID, -1, $gid);

        return $this->_setstat($filename, $attr, $recursive);
    }

    /**
     * Set permissions on a file.
     *
     * Returns the new file permissions on success or false on error.
     * If $recursive is true than this just returns true or false.
     *
     * @param int $mode
     * @param string $filename
     * @param bool $recursive
     * @return mixed
     * @access public
     */
    function chmod($mode, $filename, $recursive = false)
    {
        if (is_string($mode) && is_int($filename)) {
            $temp = $mode;
            $mode = $filename;
            $filename = $temp;
        }

        $attr = pack('N2', NET_SFTP_ATTR_PERMISSIONS, $mode & 07777);
        if (!$this->_setstat($filename, $attr, $recursive)) {
            return false;
        }
        if ($recursive) {
            return true;
        }

        $filename = $this->realpath($filename);
        // rather than return what the permissions *should* be, we'll return what they actually are.  this will also
        // tell us if the file actually exists.
        // incidentally, SFTPv4+ adds an additional 32-bit integer field - flags - to the following:
        $packet = pack('Na*', strlen($filename), $filename);
        if (!$this->_send_sftp_packet(NET_SFTP_STAT, $packet)) {
            return false;
        }

        $response = $this->_get_sftp_packet();
        switch ($this->packet_type) {
            case NET_SFTP_ATTRS:
                $attrs = $this->_parseAttributes($response);
                return $attrs['permissions'];
            case NET_SFTP_STATUS:
                $this->_logError($response);
                return false;
        }

        user_error('Expected SSH_FXP_ATTRS or SSH_FXP_STATUS');
        return false;
    }

    /**
     * Sets information about a file
     *
     * @param string $filename
     * @param string $attr
     * @param bool $recursive
     * @return bool
     * @access private
     */
    function _setstat($filename, $attr, $recursive)
    {
        if (!($this->bitmap & NET_SSH2_MASK_LOGIN)) {
            return false;
        }

        $filename = $this->_realpath($filename);
        if ($filename === false) {
            return false;
        }

        $this->_remove_from_stat_cache($filename);

        if ($recursive) {
            $i = 0;
            $result = $this->_setstat_recursive($filename, $attr, $i);
            $this->_read_put_responses($i);
            return $result;
        }

        // SFTPv4+ has an additional byte field - type - that would need to be sent, as well. setting it to
        // SSH_FILEXFER_TYPE_UNKNOWN might work. if not, we'd have to do an SSH_FXP_STAT before doing an SSH_FXP_SETSTAT.
        if (!$this->_send_sftp_packet(NET_SFTP_SETSTAT, pack('Na*a*', strlen($filename), $filename, $attr))) {
            return false;
        }

        /*
         "Because some systems must use separate system calls to set various attributes, it is possible that a failure
          response will be returned, but yet some of the attributes may be have been successfully modified.  If possible,
          servers SHOULD avoid this situation; however, clients MUST be aware that this is possible."

          -- http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.6
        */
        $response = $this->_get_sftp_packet();
        if ($this->packet_type != NET_SFTP_STATUS) {
            user_error('Expected SSH_FXP_STATUS');
            return false;
        }

        if (strlen($response) < 4) {
            return false;
        }
        extract(unpack('Nstatus', $this->_string_shift($response, 4)));
        if ($status != NET_SFTP_STATUS_OK) {
            $this->_logError($response, $status);
            return false;
        }

        return true;
    }

    /**
     * Recursively sets information on directories on the SFTP server
     *
     * Minimizes directory lookups and SSH_FXP_STATUS requests for speed.
     *
     * @param string $path
     * @param string $attr
     * @param int $i
     * @return bool
     * @access private
     */
    function _setstat_recursive($path, $attr, &$i)
    {
        if (!$this->_read_put_responses($i)) {
            return false;
        }
        $i = 0;
        $entries = $this->_list($path, true);

        if ($entries === false) {
            return $this->_setstat($path, $attr, false);
        }

        // normally $entries would have at least . and .. but it might not if the directories
        // permissions didn't allow reading
        if (empty($entries)) {
            return false;
        }

        unset($entries['.'], $entries['..']);
        foreach ($entries as $filename => $props) {
            if (!isset($props['type'])) {
                return false;
            }

            $temp = $path . '/' . $filename;
            if ($props['type'] == NET_SFTP_TYPE_DIRECTORY) {
                if (!$this->_setstat_recursive($temp, $attr, $i)) {
                    return false;
                }
            } else {
                if (!$this->_send_sftp_packet(NET_SFTP_SETSTAT, pack('Na*a*', strlen($temp), $temp, $attr))) {
                    return false;
                }

                $i++;

                if ($i >= NET_SFTP_QUEUE_SIZE) {
                    if (!$this->_read_put_responses($i)) {
                        return false;
                    }
                    $i = 0;
                }
            }
        }

        if (!$this->_send_sftp_packet(NET_SFTP_SETSTAT, pack('Na*a*', strlen($path), $path, $attr))) {
            return false;
        }

        $i++;

        if ($i >= NET_SFTP_QUEUE_SIZE) {
            if (!$this->_read_put_responses($i)) {
                return false;
            }
            $i = 0;
        }

        return true;
    }

    /**
     * Return the target of a symbolic link
     *
     * @param string $link
     * @return mixed
     * @access public
     */
    function readlink($link)
    {
        if (!($this->bitmap & NET_SSH2_MASK_LOGIN)) {
            return false;
        }

        $link = $this->_realpath($link);

        if (!$this->_send_sftp_packet(NET_SFTP_READLINK, pack('Na*', strlen($link), $link))) {
            return false;
        }

        $response = $this->_get_sftp_packet();
        switch ($this->packet_type) {
            case NET_SFTP_NAME:
                break;
            case NET_SFTP_STATUS:
                $this->_logError($response);
                return false;
            default:
                user_error('Expected SSH_FXP_NAME or SSH_FXP_STATUS');
                return false;
        }

        if (strlen($response) < 4) {
            return false;
        }
        extract(unpack('Ncount', $this->_string_shift($response, 4)));
        // the file isn't a symlink
        if (!$count) {
            return false;
        }

        if (strlen($response) < 4) {
            return false;
        }
        extract(unpack('Nlength', $this->_string_shift($response, 4)));
        return $this->_string_shift($response, $length);
    }

    /**
     * Create a symlink
     *
     * symlink() creates a symbolic link to the existing target with the specified name link.
     *
     * @param string $target
     * @param string $link
     * @return bool
     * @access public
     */
    function symlink($target, $link)
    {
        if (!($this->bitmap & NET_SSH2_MASK_LOGIN)) {
            return false;
        }

        //$target = $this->_realpath($target);
        $link = $this->_realpath($link);

        $packet = pack('Na*Na*', strlen($target), $target, strlen($link), $link);
        if (!$this->_send_sftp_packet(NET_SFTP_SYMLINK, $packet)) {
            return false;
        }

        $response = $this->_get_sftp_packet();
        if ($this->packet_type != NET_SFTP_STATUS) {
            user_error('Expected SSH_FXP_STATUS');
            return false;
        }

        if (strlen($response) < 4) {
            return false;
        }
        extract(unpack('Nstatus', $this->_string_shift($response, 4)));
        if ($status != NET_SFTP_STATUS_OK) {
            $this->_logError($response, $status);
            return false;
        }

        return true;
    }

    /**
     * Creates a directory.
     *
     * @param string $dir
     * @return bool
     * @access public
     */
    function mkdir($dir, $mode = -1, $recursive = false)
    {
        if (!($this->bitmap & NET_SSH2_MASK_LOGIN)) {
            return false;
        }

        $dir = $this->_realpath($dir);
        // by not providing any permissions, hopefully the server will use the logged in users umask - their
        // default permissions.
        $attr = $mode == -1 ? "\0\0\0\0" : pack('N2', NET_SFTP_ATTR_PERMISSIONS, $mode & 07777);

        if ($recursive) {
            $dirs = explode('/', preg_replace('#/(?=/)|/$#', '', $dir));
            if (empty($dirs[0])) {
                array_shift($dirs);
                $dirs[0] = '/' . $dirs[0];
            }
            for ($i = 0; $i < count($dirs); $i++) {
                $temp = array_slice($dirs, 0, $i + 1);
                $temp = implode('/', $temp);
                $result = $this->_mkdir_helper($temp, $attr);
            }
            return $result;
        }

        return $this->_mkdir_helper($dir, $attr);
    }

    /**
     * Helper function for directory creation
     *
     * @param string $dir
     * @return bool
     * @access private
     */
    function _mkdir_helper($dir, $attr)
    {
        if (!$this->_send_sftp_packet(NET_SFTP_MKDIR, pack('Na*a*', strlen($dir), $dir, $attr))) {
            return false;
        }

        $response = $this->_get_sftp_packet();
        if ($this->packet_type != NET_SFTP_STATUS) {
            user_error('Expected SSH_FXP_STATUS');
            return false;
        }

        if (strlen($response) < 4) {
            return false;
        }
        extract(unpack('Nstatus', $this->_string_shift($response, 4)));
        if ($status != NET_SFTP_STATUS_OK) {
            $this->_logError($response, $status);
            return false;
        }

        return true;
    }

    /**
     * Removes a directory.
     *
     * @param string $dir
     * @return bool
     * @access public
     */
    function rmdir($dir)
    {
        if (!($this->bitmap & NET_SSH2_MASK_LOGIN)) {
            return false;
        }

        $dir = $this->_realpath($dir);
        if ($dir === false) {
            return false;
        }

        if (!$this->_send_sftp_packet(NET_SFTP_RMDIR, pack('Na*', strlen($dir), $dir))) {
            return false;
        }

        $response = $this->_get_sftp_packet();
        if ($this->packet_type != NET_SFTP_STATUS) {
            user_error('Expected SSH_FXP_STATUS');
            return false;
        }

        if (strlen($response) < 4) {
            return false;
        }
        extract(unpack('Nstatus', $this->_string_shift($response, 4)));
        if ($status != NET_SFTP_STATUS_OK) {
            // presumably SSH_FX_NO_SUCH_FILE or SSH_FX_PERMISSION_DENIED?
            $this->_logError($response, $status);
            return false;
        }

        $this->_remove_from_stat_cache($dir);
        // the following will do a soft delete, which would be useful if you deleted a file
        // and then tried to do a stat on the deleted file. the above, in contrast, does
        // a hard delete
        //$this->_update_stat_cache($dir, false);

        return true;
    }

    /**
     * Uploads a file to the SFTP server.
     *
     * By default, Net_SFTP::put() does not read from the local filesystem.  $data is dumped directly into $remote_file.
     * So, for example, if you set $data to 'filename.ext' and then do Net_SFTP::get(), you will get a file, twelve bytes
     * long, containing 'filename.ext' as its contents.
     *
     * Setting $mode to NET_SFTP_LOCAL_FILE will change the above behavior.  With NET_SFTP_LOCAL_FILE, $remote_file will
     * contain as many bytes as filename.ext does on your local filesystem.  If your filename.ext is 1MB then that is how
     * large $remote_file will be, as well.
     *
     * If $data is a resource then it'll be used as a resource instead.
     *
     *
     * Setting $mode to NET_SFTP_CALLBACK will use $data as callback function, which gets only one parameter -- number
     * of bytes to return, and returns a string if there is some data or null if there is no more data
     *
     * Currently, only binary mode is supported.  As such, if the line endings need to be adjusted, you will need to take
     * care of that, yourself.
     *
     * $mode can take an additional two parameters - NET_SFTP_RESUME and NET_SFTP_RESUME_START. These are bitwise AND'd with
     * $mode. So if you want to resume upload of a 300mb file on the local file system you'd set $mode to the following:
     *
     * NET_SFTP_LOCAL_FILE | NET_SFTP_RESUME
     *
     * If you wanted to simply append the full contents of a local file to the full contents of a remote file you'd replace
     * NET_SFTP_RESUME with NET_SFTP_RESUME_START.
     *
     * If $mode & (NET_SFTP_RESUME | NET_SFTP_RESUME_START) then NET_SFTP_RESUME_START will be assumed.
     *
     * $start and $local_start give you more fine grained control over this process and take precident over NET_SFTP_RESUME
     * when they're non-negative. ie. $start could let you write at the end of a file (like NET_SFTP_RESUME) or in the middle
     * of one. $local_start could let you start your reading from the end of a file (like NET_SFTP_RESUME_START) or in the
     * middle of one.
     *
     * Setting $local_start to > 0 or $mode | NET_SFTP_RESUME_START doesn't do anything unless $mode | NET_SFTP_LOCAL_FILE.
     *
     * @param string $remote_file
     * @param string|resource $data
     * @param int $mode
     * @param int $start
     * @param int $local_start
     * @param callable|null $progressCallback
     * @return bool
     * @access public
     * @internal ASCII mode for SFTPv4/5/6 can be supported by adding a new function - Net_SFTP::setMode().
     */
    function put($remote_file, $data, $mode = NET_SFTP_STRING, $start = -1, $local_start = -1, $progressCallback = null)
    {
        if (!($this->bitmap & NET_SSH2_MASK_LOGIN)) {
            return false;
        }

        $remote_file = $this->_realpath($remote_file);
        if ($remote_file === false) {
            return false;
        }

        $this->_remove_from_stat_cache($remote_file);

        $flags = NET_SFTP_OPEN_WRITE | NET_SFTP_OPEN_CREATE;
        // according to the SFTP specs, NET_SFTP_OPEN_APPEND should "force all writes to append data at the end of the file."
        // in practice, it doesn't seem to do that.
        //$flags|= ($mode & NET_SFTP_RESUME) ? NET_SFTP_OPEN_APPEND : NET_SFTP_OPEN_TRUNCATE;

        if ($start >= 0) {
            $offset = $start;
        } elseif ($mode & NET_SFTP_RESUME) {
            // if NET_SFTP_OPEN_APPEND worked as it should _size() wouldn't need to be called
            $size = $this->size($remote_file);
            $offset = $size !== false ? $size : 0;
        } else {
            $offset = 0;
            $flags|= NET_SFTP_OPEN_TRUNCATE;
        }

        $packet = pack('Na*N2', strlen($remote_file), $remote_file, $flags, 0);
        if (!$this->_send_sftp_packet(NET_SFTP_OPEN, $packet)) {
            return false;
        }

        $response = $this->_get_sftp_packet();
        switch ($this->packet_type) {
            case NET_SFTP_HANDLE:
                $handle = substr($response, 4);
                break;
            case NET_SFTP_STATUS:
                $this->_logError($response);
                return false;
            default:
                user_error('Expected SSH_FXP_HANDLE or SSH_FXP_STATUS');
                return false;
        }

        // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.2.3
        $dataCallback = false;
        switch (true) {
            case $mode & NET_SFTP_CALLBACK:
                if (!is_callable($data)) {
                    user_error("\$data should be is_callable if you set NET_SFTP_CALLBACK flag");
                }
                $dataCallback = $data;
                // do nothing
                break;
            case is_resource($data):
                $mode = $mode & ~NET_SFTP_LOCAL_FILE;
                $info = stream_get_meta_data($data);
                if ($info['wrapper_type'] == 'PHP' && $info['stream_type'] == 'Input') {
                    $fp = fopen('php://memory', 'w+');
                    stream_copy_to_stream($data, $fp);
                    rewind($fp);
                } else {
                    $fp = $data;
                }
                break;
            case $mode & NET_SFTP_LOCAL_FILE:
                if (!is_file($data)) {
                    user_error("$data is not a valid file");
                    return false;
                }
                $fp = @fopen($data, 'rb');
                if (!$fp) {
                    return false;
                }
        }

        if (isset($fp)) {
            $stat = fstat($fp);
            $size = !empty($stat) ? $stat['size'] : 0;

            if ($local_start >= 0) {
                fseek($fp, $local_start);
                $size-= $local_start;
            }
        } elseif ($dataCallback) {
            $size = 0;
        } else {
            $size = strlen($data);
        }

        $sent = 0;
        $size = $size < 0 ? ($size & 0x7FFFFFFF) + 0x80000000 : $size;

        $sftp_packet_size = 4096; // PuTTY uses 4096
        // make the SFTP packet be exactly 4096 bytes by including the bytes in the NET_SFTP_WRITE packets "header"
        $sftp_packet_size-= strlen($handle) + 25;
        $i = 0;
        while ($dataCallback || ($size === 0 || $sent < $size)) {
            if ($dataCallback) {
                $temp = call_user_func($dataCallback, $sftp_packet_size);
                if (is_null($temp)) {
                    break;
                }
            } else {
                $temp = isset($fp) ? fread($fp, $sftp_packet_size) : substr($data, $sent, $sftp_packet_size);
                if ($temp === false || $temp === '') {
                    break;
                }
            }

            $subtemp = $offset + $sent;
            $packet = pack('Na*N3a*', strlen($handle), $handle, $subtemp / 4294967296, $subtemp, strlen($temp), $temp);
            if (!$this->_send_sftp_packet(NET_SFTP_WRITE, $packet)) {
                if ($mode & NET_SFTP_LOCAL_FILE) {
                    fclose($fp);
                }
                return false;
            }
            $sent+= strlen($temp);
            if (is_callable($progressCallback)) {
                call_user_func($progressCallback, $sent);
            }

            $i++;

            if ($i == NET_SFTP_QUEUE_SIZE) {
                if (!$this->_read_put_responses($i)) {
                    $i = 0;
                    break;
                }
                $i = 0;
            }
        }

        if (!$this->_read_put_responses($i)) {
            if ($mode & NET_SFTP_LOCAL_FILE) {
                fclose($fp);
            }
            $this->_close_handle($handle);
            return false;
        }

        if ($mode & NET_SFTP_LOCAL_FILE) {
            fclose($fp);
        }

        return $this->_close_handle($handle);
    }

    /**
     * Reads multiple successive SSH_FXP_WRITE responses
     *
     * Sending an SSH_FXP_WRITE packet and immediately reading its response isn't as efficient as blindly sending out $i
     * SSH_FXP_WRITEs, in succession, and then reading $i responses.
     *
     * @param int $i
     * @return bool
     * @access private
     */
    function _read_put_responses($i)
    {
        while ($i--) {
            $response = $this->_get_sftp_packet();
            if ($this->packet_type != NET_SFTP_STATUS) {
                user_error('Expected SSH_FXP_STATUS');
                return false;
            }

            if (strlen($response) < 4) {
                return false;
            }
            extract(unpack('Nstatus', $this->_string_shift($response, 4)));
            if ($status != NET_SFTP_STATUS_OK) {
                $this->_logError($response, $status);
                break;
            }
        }

        return $i < 0;
    }

    /**
     * Close handle
     *
     * @param string $handle
     * @return bool
     * @access private
     */
    function _close_handle($handle)
    {
        if (!$this->_send_sftp_packet(NET_SFTP_CLOSE, pack('Na*', strlen($handle), $handle))) {
            return false;
        }

        // "The client MUST release all resources associated with the handle regardless of the status."
        //  -- http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.1.3
        $response = $this->_get_sftp_packet();
        if ($this->packet_type != NET_SFTP_STATUS) {
            user_error('Expected SSH_FXP_STATUS');
            return false;
        }

        if (strlen($response) < 4) {
            return false;
        }
        extract(unpack('Nstatus', $this->_string_shift($response, 4)));
        if ($status != NET_SFTP_STATUS_OK) {
            $this->_logError($response, $status);
            return false;
        }

        return true;
    }

    /**
     * Downloads a file from the SFTP server.
     *
     * Returns a string containing the contents of $remote_file if $local_file is left undefined or a boolean false if
     * the operation was unsuccessful.  If $local_file is defined, returns true or false depending on the success of the
     * operation.
     *
     * $offset and $length can be used to download files in chunks.
     *
     * @param string $remote_file
     * @param string $local_file
     * @param int $offset
     * @param int $length
     * @return mixed
     * @access public
     */
    function get($remote_file, $local_file = false, $offset = 0, $length = -1)
    {
        if (!($this->bitmap & NET_SSH2_MASK_LOGIN)) {
            return false;
        }

        $remote_file = $this->_realpath($remote_file);
        if ($remote_file === false) {
            return false;
        }

        $packet = pack('Na*N2', strlen($remote_file), $remote_file, NET_SFTP_OPEN_READ, 0);
        if (!$this->_send_sftp_packet(NET_SFTP_OPEN, $packet)) {
            return false;
        }

        $response = $this->_get_sftp_packet();
        switch ($this->packet_type) {
            case NET_SFTP_HANDLE:
                $handle = substr($response, 4);
                break;
            case NET_SFTP_STATUS: // presumably SSH_FX_NO_SUCH_FILE or SSH_FX_PERMISSION_DENIED
                $this->_logError($response);
                return false;
            default:
                user_error('Expected SSH_FXP_HANDLE or SSH_FXP_STATUS');
                return false;
        }

        if (is_resource($local_file)) {
            $fp = $local_file;
            $stat = fstat($fp);
            $res_offset = $stat['size'];
        } else {
            $res_offset = 0;
            if ($local_file !== false) {
                $fp = fopen($local_file, 'wb');
                if (!$fp) {
                    return false;
                }
            } else {
                $content = '';
            }
        }

        $fclose_check = $local_file !== false && !is_resource($local_file);

        $start = $offset;
        $read = 0;
        while (true) {
            $i = 0;

            while ($i < NET_SFTP_QUEUE_SIZE && ($length < 0 || $read < $length)) {
                $tempoffset = $start + $read;

                $packet_size = $length > 0 ? min($this->max_sftp_packet, $length - $read) : $this->max_sftp_packet;

                $packet = pack('Na*N3', strlen($handle), $handle, $tempoffset / 4294967296, $tempoffset, $packet_size);
                if (!$this->_send_sftp_packet(NET_SFTP_READ, $packet)) {
                    if ($fclose_check) {
                        fclose($fp);
                    }
                    return false;
                }
                $packet = null;
                $read+= $packet_size;
                $i++;
            }

            if (!$i) {
                break;
            }

            $clear_responses = false;
            while ($i > 0) {
                $i--;

                if ($clear_responses) {
                    $this->_get_sftp_packet();
                    continue;
                } else {
                    $response = $this->_get_sftp_packet();
                }

                switch ($this->packet_type) {
                    case NET_SFTP_DATA:
                        $temp = substr($response, 4);
                        $offset+= strlen($temp);
                        if ($local_file === false) {
                            $content.= $temp;
                        } else {
                            fputs($fp, $temp);
                        }
                        $temp = null;
                        break;
                    case NET_SFTP_STATUS:
                        // could, in theory, return false if !strlen($content) but we'll hold off for the time being
                        $this->_logError($response);
                        $clear_responses = true; // don't break out of the loop yet, so we can read the remaining responses
                        break;
                    default:
                        if ($fclose_check) {
                            fclose($fp);
                        }
                        user_error('Expected SSH_FX_DATA or SSH_FXP_STATUS');
                }
                $response = null;
            }

            if ($clear_responses) {
                break;
            }
        }

        if ($length > 0 && $length <= $offset - $start) {
            if ($local_file === false) {
                $content = substr($content, 0, $length);
            } else {
                ftruncate($fp, $length + $res_offset);
            }
        }

        if ($fclose_check) {
            fclose($fp);
        }

        if (!$this->_close_handle($handle)) {
            return false;
        }

        // if $content isn't set that means a file was written to
        return isset($content) ? $content : true;
    }

    /**
     * Deletes a file on the SFTP server.
     *
     * @param string $path
     * @param bool $recursive
     * @return bool
     * @access public
     */
    function delete($path, $recursive = true)
    {
        if (!($this->bitmap & NET_SSH2_MASK_LOGIN)) {
            return false;
        }

        if (is_object($path)) {
            // It's an object. Cast it as string before we check anything else.
            $path = (string) $path;
        }

        if (!is_string($path) || $path == '') {
            return false;
        }

        $path = $this->_realpath($path);
        if ($path === false) {
            return false;
        }

        // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.3
        if (!$this->_send_sftp_packet(NET_SFTP_REMOVE, pack('Na*', strlen($path), $path))) {
            return false;
        }

        $response = $this->_get_sftp_packet();
        if ($this->packet_type != NET_SFTP_STATUS) {
            user_error('Expected SSH_FXP_STATUS');
            return false;
        }

        // if $status isn't SSH_FX_OK it's probably SSH_FX_NO_SUCH_FILE or SSH_FX_PERMISSION_DENIED
        if (strlen($response) < 4) {
            return false;
        }
        extract(unpack('Nstatus', $this->_string_shift($response, 4)));
        if ($status != NET_SFTP_STATUS_OK) {
            $this->_logError($response, $status);
            if (!$recursive) {
                return false;
            }
            $i = 0;
            $result = $this->_delete_recursive($path, $i);
            $this->_read_put_responses($i);
            return $result;
        }

        $this->_remove_from_stat_cache($path);

        return true;
    }

    /**
     * Recursively deletes directories on the SFTP server
     *
     * Minimizes directory lookups and SSH_FXP_STATUS requests for speed.
     *
     * @param string $path
     * @param int $i
     * @return bool
     * @access private
     */
    function _delete_recursive($path, &$i)
    {
        if (!$this->_read_put_responses($i)) {
            return false;
        }
        $i = 0;
        $entries = $this->_list($path, true);

        // normally $entries would have at least . and .. but it might not if the directories
        // permissions didn't allow reading
        if (empty($entries)) {
            return false;
        }

        unset($entries['.'], $entries['..']);
        foreach ($entries as $filename => $props) {
            if (!isset($props['type'])) {
                return false;
            }

            $temp = $path . '/' . $filename;
            if ($props['type'] == NET_SFTP_TYPE_DIRECTORY) {
                if (!$this->_delete_recursive($temp, $i)) {
                    return false;
                }
            } else {
                if (!$this->_send_sftp_packet(NET_SFTP_REMOVE, pack('Na*', strlen($temp), $temp))) {
                    return false;
                }
                $this->_remove_from_stat_cache($temp);

                $i++;

                if ($i >= NET_SFTP_QUEUE_SIZE) {
                    if (!$this->_read_put_responses($i)) {
                        return false;
                    }
                    $i = 0;
                }
            }
        }

        if (!$this->_send_sftp_packet(NET_SFTP_RMDIR, pack('Na*', strlen($path), $path))) {
            return false;
        }
        $this->_remove_from_stat_cache($path);

        $i++;

        if ($i >= NET_SFTP_QUEUE_SIZE) {
            if (!$this->_read_put_responses($i)) {
                return false;
            }
            $i = 0;
        }

        return true;
    }

    /**
     * Checks whether a file or directory exists
     *
     * @param string $path
     * @return bool
     * @access public
     */
    function file_exists($path)
    {
        if ($this->use_stat_cache) {
            $path = $this->_realpath($path);

            $result = $this->_query_stat_cache($path);

            if (isset($result)) {
                // return true if $result is an array or if it's an stdClass object
                return $result !== false;
            }
        }

        return $this->stat($path) !== false;
    }

    /**
     * Tells whether the filename is a directory
     *
     * @param string $path
     * @return bool
     * @access public
     */
    function is_dir($path)
    {
        $result = $this->_get_stat_cache_prop($path, 'type');
        if ($result === false) {
            return false;
        }
        return $result === NET_SFTP_TYPE_DIRECTORY;
    }

    /**
     * Tells whether the filename is a regular file
     *
     * @param string $path
     * @return bool
     * @access public
     */
    function is_file($path)
    {
        $result = $this->_get_stat_cache_prop($path, 'type');
        if ($result === false) {
            return false;
        }
        return $result === NET_SFTP_TYPE_REGULAR;
    }

    /**
     * Tells whether the filename is a symbolic link
     *
     * @param string $path
     * @return bool
     * @access public
     */
    function is_link($path)
    {
        $result = $this->_get_lstat_cache_prop($path, 'type');
        if ($result === false) {
            return false;
        }
        return $result === NET_SFTP_TYPE_SYMLINK;
    }

    /**
     * Tells whether a file exists and is readable
     *
     * @param string $path
     * @return bool
     * @access public
     */
    function is_readable($path)
    {
        $path = $this->_realpath($path);

        $packet = pack('Na*N2', strlen($path), $path, NET_SFTP_OPEN_READ, 0);
        if (!$this->_send_sftp_packet(NET_SFTP_OPEN, $packet)) {
            return false;
        }

        $response = $this->_get_sftp_packet();
        switch ($this->packet_type) {
            case NET_SFTP_HANDLE:
                return true;
            case NET_SFTP_STATUS: // presumably SSH_FX_NO_SUCH_FILE or SSH_FX_PERMISSION_DENIED
                return false;
            default:
                user_error('Expected SSH_FXP_HANDLE or SSH_FXP_STATUS');
                return false;
        }
    }

    /**
     * Tells whether the filename is writable
     *
     * @param string $path
     * @return bool
     * @access public
     */
    function is_writable($path)
    {
        $path = $this->_realpath($path);

        $packet = pack('Na*N2', strlen($path), $path, NET_SFTP_OPEN_WRITE, 0);
        if (!$this->_send_sftp_packet(NET_SFTP_OPEN, $packet)) {
            return false;
        }

        $response = $this->_get_sftp_packet();
        switch ($this->packet_type) {
            case NET_SFTP_HANDLE:
                return true;
            case NET_SFTP_STATUS: // presumably SSH_FX_NO_SUCH_FILE or SSH_FX_PERMISSION_DENIED
                return false;
            default:
                user_error('Expected SSH_FXP_HANDLE or SSH_FXP_STATUS');
                return false;
        }
    }

    /**
     * Tells whether the filename is writeable
     *
     * Alias of is_writable
     *
     * @param string $path
     * @return bool
     * @access public
     */
    function is_writeable($path)
    {
        return $this->is_writable($path);
    }

    /**
     * Gets last access time of file
     *
     * @param string $path
     * @return mixed
     * @access public
     */
    function fileatime($path)
    {
        return $this->_get_stat_cache_prop($path, 'atime');
    }

    /**
     * Gets file modification time
     *
     * @param string $path
     * @return mixed
     * @access public
     */
    function filemtime($path)
    {
        return $this->_get_stat_cache_prop($path, 'mtime');
    }

    /**
     * Gets file permissions
     *
     * @param string $path
     * @return mixed
     * @access public
     */
    function fileperms($path)
    {
        return $this->_get_stat_cache_prop($path, 'permissions');
    }

    /**
     * Gets file owner
     *
     * @param string $path
     * @return mixed
     * @access public
     */
    function fileowner($path)
    {
        return $this->_get_stat_cache_prop($path, 'uid');
    }

    /**
     * Gets file group
     *
     * @param string $path
     * @return mixed
     * @access public
     */
    function filegroup($path)
    {
        return $this->_get_stat_cache_prop($path, 'gid');
    }

    /**
     * Gets file size
     *
     * @param string $path
     * @return mixed
     * @access public
     */
    function filesize($path)
    {
        return $this->_get_stat_cache_prop($path, 'size');
    }

    /**
     * Gets file type
     *
     * @param string $path
     * @return mixed
     * @access public
     */
    function filetype($path)
    {
        $type = $this->_get_stat_cache_prop($path, 'type');
        if ($type === false) {
            return false;
        }

        switch ($type) {
            case NET_SFTP_TYPE_BLOCK_DEVICE:
                return 'block';
            case NET_SFTP_TYPE_CHAR_DEVICE:
                return 'char';
            case NET_SFTP_TYPE_DIRECTORY:
                return 'dir';
            case NET_SFTP_TYPE_FIFO:
                return 'fifo';
            case NET_SFTP_TYPE_REGULAR:
                return 'file';
            case NET_SFTP_TYPE_SYMLINK:
                return 'link';
            default:
                return false;
        }
    }

    /**
     * Return a stat properity
     *
     * Uses cache if appropriate.
     *
     * @param string $path
     * @param string $prop
     * @return mixed
     * @access private
     */
    function _get_stat_cache_prop($path, $prop)
    {
        return $this->_get_xstat_cache_prop($path, $prop, 'stat');
    }

    /**
     * Return an lstat properity
     *
     * Uses cache if appropriate.
     *
     * @param string $path
     * @param string $prop
     * @return mixed
     * @access private
     */
    function _get_lstat_cache_prop($path, $prop)
    {
        return $this->_get_xstat_cache_prop($path, $prop, 'lstat');
    }

    /**
     * Return a stat or lstat properity
     *
     * Uses cache if appropriate.
     *
     * @param string $path
     * @param string $prop
     * @return mixed
     * @access private
     */
    function _get_xstat_cache_prop($path, $prop, $type)
    {
        if ($this->use_stat_cache) {
            $path = $this->_realpath($path);

            $result = $this->_query_stat_cache($path);

            if (is_object($result) && isset($result->$type)) {
                return $result->{$type}[$prop];
            }
        }

        $result = $this->$type($path);

        if ($result === false || !isset($result[$prop])) {
            return false;
        }

        return $result[$prop];
    }

    /**
     * Renames a file or a directory on the SFTP server
     *
     * @param string $oldname
     * @param string $newname
     * @return bool
     * @access public
     */
    function rename($oldname, $newname)
    {
        if (!($this->bitmap & NET_SSH2_MASK_LOGIN)) {
            return false;
        }

        $oldname = $this->_realpath($oldname);
        $newname = $this->_realpath($newname);
        if ($oldname === false || $newname === false) {
            return false;
        }

        // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.3
        $packet = pack('Na*Na*', strlen($oldname), $oldname, strlen($newname), $newname);
        if (!$this->_send_sftp_packet(NET_SFTP_RENAME, $packet)) {
            return false;
        }

        $response = $this->_get_sftp_packet();
        if ($this->packet_type != NET_SFTP_STATUS) {
            user_error('Expected SSH_FXP_STATUS');
            return false;
        }

        // if $status isn't SSH_FX_OK it's probably SSH_FX_NO_SUCH_FILE or SSH_FX_PERMISSION_DENIED
        if (strlen($response) < 4) {
            return false;
        }
        extract(unpack('Nstatus', $this->_string_shift($response, 4)));
        if ($status != NET_SFTP_STATUS_OK) {
            $this->_logError($response, $status);
            return false;
        }

        // don't move the stat cache entry over since this operation could very well change the
        // atime and mtime attributes
        //$this->_update_stat_cache($newname, $this->_query_stat_cache($oldname));
        $this->_remove_from_stat_cache($oldname);
        $this->_remove_from_stat_cache($newname);

        return true;
    }

    /**
     * Parse Attributes
     *
     * See '7.  File Attributes' of draft-ietf-secsh-filexfer-13 for more info.
     *
     * @param string $response
     * @return array
     * @access private
     */
    function _parseAttributes(&$response)
    {
        $attr = array();
        if (strlen($response) < 4) {
            user_error('Malformed file attributes');
            return array();
        }
        extract(unpack('Nflags', $this->_string_shift($response, 4)));
        // SFTPv4+ have a type field (a byte) that follows the above flag field
        foreach ($this->attributes as $key => $value) {
            switch ($flags & $key) {
                case NET_SFTP_ATTR_SIZE: // 0x00000001
                    // The size attribute is defined as an unsigned 64-bit integer.
                    // The following will use floats on 32-bit platforms, if necessary.
                    // As can be seen in the BigInteger class, floats are generally
                    // IEEE 754 binary64 "double precision" on such platforms and
                    // as such can represent integers of at least 2^50 without loss
                    // of precision. Interpreted in filesize, 2^50 bytes = 1024 TiB.
                    $attr['size'] = hexdec(bin2hex($this->_string_shift($response, 8)));
                    break;
                case NET_SFTP_ATTR_UIDGID: // 0x00000002 (SFTPv3 only)
                    if (strlen($response) < 8) {
                        user_error('Malformed file attributes');
                        return $attr;
                    }
                    $attr+= unpack('Nuid/Ngid', $this->_string_shift($response, 8));
                    break;
                case NET_SFTP_ATTR_PERMISSIONS: // 0x00000004
                    if (strlen($response) < 4) {
                        user_error('Malformed file attributes');
                        return $attr;
                    }
                    $attr+= unpack('Npermissions', $this->_string_shift($response, 4));
                    // mode == permissions; permissions was the original array key and is retained for bc purposes.
                    // mode was added because that's the more industry standard terminology
                    $attr+= array('mode' => $attr['permissions']);
                    $fileType = $this->_parseMode($attr['permissions']);
                    if ($fileType !== false) {
                        $attr+= array('type' => $fileType);
                    }
                    break;
                case NET_SFTP_ATTR_ACCESSTIME: // 0x00000008
                    if (strlen($response) < 8) {
                        user_error('Malformed file attributes');
                        return $attr;
                    }
                    $attr+= unpack('Natime/Nmtime', $this->_string_shift($response, 8));
                    break;
                case NET_SFTP_ATTR_EXTENDED: // 0x80000000
                    if (strlen($response) < 4) {
                        user_error('Malformed file attributes');
                        return $attr;
                    }
                    extract(unpack('Ncount', $this->_string_shift($response, 4)));
                    for ($i = 0; $i < $count; $i++) {
                        if (strlen($response) < 4) {
                            user_error('Malformed file attributes');
                            return $attr;
                        }
                        extract(unpack('Nlength', $this->_string_shift($response, 4)));
                        $key = $this->_string_shift($response, $length);
                        if (strlen($response) < 4) {
                            user_error('Malformed file attributes');
                            return $attr;
                        }
                        extract(unpack('Nlength', $this->_string_shift($response, 4)));
                        $attr[$key] = $this->_string_shift($response, $length);
                    }
            }
        }
        return $attr;
    }

    /**
     * Attempt to identify the file type
     *
     * Quoting the SFTP RFC, "Implementations MUST NOT send bits that are not defined" but they seem to anyway
     *
     * @param int $mode
     * @return int
     * @access private
     */
    function _parseMode($mode)
    {
        // values come from http://lxr.free-electrons.com/source/include/uapi/linux/stat.h#L12
        // see, also, http://linux.die.net/man/2/stat
        switch ($mode & 0170000) {// ie. 1111 0000 0000 0000
            case 0000000: // no file type specified - figure out the file type using alternative means
                return false;
            case 0040000:
                return NET_SFTP_TYPE_DIRECTORY;
            case 0100000:
                return NET_SFTP_TYPE_REGULAR;
            case 0120000:
                return NET_SFTP_TYPE_SYMLINK;
            // new types introduced in SFTPv5+
            // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-05#section-5.2
            case 0010000: // named pipe (fifo)
                return NET_SFTP_TYPE_FIFO;
            case 0020000: // character special
                return NET_SFTP_TYPE_CHAR_DEVICE;
            case 0060000: // block special
                return NET_SFTP_TYPE_BLOCK_DEVICE;
            case 0140000: // socket
                return NET_SFTP_TYPE_SOCKET;
            case 0160000: // whiteout
                // "SPECIAL should be used for files that are of
                //  a known type which cannot be expressed in the protocol"
                return NET_SFTP_TYPE_SPECIAL;
            default:
                return NET_SFTP_TYPE_UNKNOWN;
        }
    }

    /**
     * Parse Longname
     *
     * SFTPv3 doesn't provide any easy way of identifying a file type.  You could try to open
     * a file as a directory and see if an error is returned or you could try to parse the
     * SFTPv3-specific longname field of the SSH_FXP_NAME packet.  That's what this function does.
     * The result is returned using the
     * {@link http://tools.ietf.org/html/draft-ietf-secsh-filexfer-04#section-5.2 SFTPv4 type constants}.
     *
     * If the longname is in an unrecognized format bool(false) is returned.
     *
     * @param string $longname
     * @return mixed
     * @access private
     */
    function _parseLongname($longname)
    {
        // http://en.wikipedia.org/wiki/Unix_file_types
        // http://en.wikipedia.org/wiki/Filesystem_permissions#Notation_of_traditional_Unix_permissions
        if (preg_match('#^[^/]([r-][w-][xstST-]){3}#', $longname)) {
            switch ($longname[0]) {
                case '-':
                    return NET_SFTP_TYPE_REGULAR;
                case 'd':
                    return NET_SFTP_TYPE_DIRECTORY;
                case 'l':
                    return NET_SFTP_TYPE_SYMLINK;
                default:
                    return NET_SFTP_TYPE_SPECIAL;
            }
        }

        return false;
    }

    /**
     * Sends SFTP Packets
     *
     * See '6. General Packet Format' of draft-ietf-secsh-filexfer-13 for more info.
     *
     * @param int $type
     * @param string $data
     * @see self::_get_sftp_packet()
     * @see Net_SSH2::_send_channel_packet()
     * @return bool
     * @access private
     */
    function _send_sftp_packet($type, $data)
    {
        $packet = $this->request_id !== false ?
            pack('NCNa*', strlen($data) + 5, $type, $this->request_id, $data) :
            pack('NCa*', strlen($data) + 1, $type, $data);

        $start = strtok(microtime(), ' ') + strtok(''); // http://php.net/microtime#61838
        $result = $this->_send_channel_packet(NET_SFTP_CHANNEL, $packet);
        $stop = strtok(microtime(), ' ') + strtok('');

        if (defined('NET_SFTP_LOGGING')) {
            $packet_type = '-> ' . $this->packet_types[$type] .
                           ' (' . round($stop - $start, 4) . 's)';
            if (NET_SFTP_LOGGING == NET_SFTP_LOG_REALTIME) {
                echo "<pre>\r\n" . $this->_format_log(array($data), array($packet_type)) . "\r\n</pre>\r\n";
                flush();
                ob_flush();
            } else {
                $this->packet_type_log[] = $packet_type;
                if (NET_SFTP_LOGGING == NET_SFTP_LOG_COMPLEX) {
                    $this->packet_log[] = $data;
                }
            }
        }

        return $result;
    }

    /**
     * Receives SFTP Packets
     *
     * See '6. General Packet Format' of draft-ietf-secsh-filexfer-13 for more info.
     *
     * Incidentally, the number of SSH_MSG_CHANNEL_DATA messages has no bearing on the number of SFTP packets present.
     * There can be one SSH_MSG_CHANNEL_DATA messages containing two SFTP packets or there can be two SSH_MSG_CHANNEL_DATA
     * messages containing one SFTP packet.
     *
     * @see self::_send_sftp_packet()
     * @return string
     * @access private
     */
    function _get_sftp_packet()
    {
        $this->curTimeout = false;

        $start = strtok(microtime(), ' ') + strtok(''); // http://php.net/microtime#61838

        // SFTP packet length
        while (strlen($this->packet_buffer) < 4) {
            $temp = $this->_get_channel_packet(NET_SFTP_CHANNEL, true);
            if (is_bool($temp)) {
                $this->packet_type = false;
                $this->packet_buffer = '';
                return false;
            }
            $this->packet_buffer.= $temp;
        }
        if (strlen($this->packet_buffer) < 4) {
            return false;
        }
        extract(unpack('Nlength', $this->_string_shift($this->packet_buffer, 4)));
        $tempLength = $length;
        $tempLength-= strlen($this->packet_buffer);

        // SFTP packet type and data payload
        while ($tempLength > 0) {
            $temp = $this->_get_channel_packet(NET_SFTP_CHANNEL, true);
            if (is_bool($temp)) {
                $this->packet_type = false;
                $this->packet_buffer = '';
                return false;
            }
            $this->packet_buffer.= $temp;
            $tempLength-= strlen($temp);
        }

        $stop = strtok(microtime(), ' ') + strtok('');

        $this->packet_type = ord($this->_string_shift($this->packet_buffer));

        if ($this->request_id !== false) {
            $this->_string_shift($this->packet_buffer, 4); // remove the request id
            $length-= 5; // account for the request id and the packet type
        } else {
            $length-= 1; // account for the packet type
        }

        $packet = $this->_string_shift($this->packet_buffer, $length);

        if (defined('NET_SFTP_LOGGING')) {
            $packet_type = '<- ' . $this->packet_types[$this->packet_type] .
                           ' (' . round($stop - $start, 4) . 's)';
            if (NET_SFTP_LOGGING == NET_SFTP_LOG_REALTIME) {
                echo "<pre>\r\n" . $this->_format_log(array($packet), array($packet_type)) . "\r\n</pre>\r\n";
                flush();
                ob_flush();
            } else {
                $this->packet_type_log[] = $packet_type;
                if (NET_SFTP_LOGGING == NET_SFTP_LOG_COMPLEX) {
                    $this->packet_log[] = $packet;
                }
            }
        }

        return $packet;
    }

    /**
     * Returns a log of the packets that have been sent and received.
     *
     * Returns a string if NET_SFTP_LOGGING == NET_SFTP_LOG_COMPLEX, an array if NET_SFTP_LOGGING == NET_SFTP_LOG_SIMPLE and false if !defined('NET_SFTP_LOGGING')
     *
     * @access public
     * @return string or Array
     */
    function getSFTPLog()
    {
        if (!defined('NET_SFTP_LOGGING')) {
            return false;
        }

        switch (NET_SFTP_LOGGING) {
            case NET_SFTP_LOG_COMPLEX:
                return $this->_format_log($this->packet_log, $this->packet_type_log);
                break;
            //case NET_SFTP_LOG_SIMPLE:
            default:
                return $this->packet_type_log;
        }
    }

    /**
     * Returns all errors
     *
     * @return array
     * @access public
     */
    function getSFTPErrors()
    {
        return $this->sftp_errors;
    }

    /**
     * Returns the last error
     *
     * @return string
     * @access public
     */
    function getLastSFTPError()
    {
        return count($this->sftp_errors) ? $this->sftp_errors[count($this->sftp_errors) - 1] : '';
    }

    /**
     * Get supported SFTP versions
     *
     * @return array
     * @access public
     */
    function getSupportedVersions()
    {
        $temp = array('version' => $this->version);
        if (isset($this->extensions['versions'])) {
            $temp['extensions'] = $this->extensions['versions'];
        }
        return $temp;
    }

    /**
     * Disconnect
     *
     * @param int $reason
     * @return bool
     * @access private
     */
    function _disconnect($reason)
    {
        $this->pwd = false;
        parent::_disconnect($reason);
    }
}
vendor/phpseclib/phpseclib/phpseclib/Net/SCP.php000064400000024122151327705700015630 0ustar00<?php

/**
 * Pure-PHP implementation of SCP.
 *
 * PHP versions 4 and 5
 *
 * The API for this library is modeled after the API from PHP's {@link http://php.net/book.ftp FTP extension}.
 *
 * Here's a short example of how to use this library:
 * <code>
 * <?php
 *    include 'Net/SCP.php';
 *    include 'Net/SSH2.php';
 *
 *    $ssh = new Net_SSH2('www.domain.tld');
 *    if (!$ssh->login('username', 'password')) {
 *        exit('bad login');
 *    }
 *
 *    $scp = new Net_SCP($ssh);
 *    $scp->put('abcd', str_repeat('x', 1024*1024));
 * ?>
 * </code>
 *
 * LICENSE: Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 *
 * @category  Net
 * @package   Net_SCP
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2010 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      http://phpseclib.sourceforge.net
 */

/**#@+
 * @access public
 * @see self::put()
 */
/**
 * Reads data from a local file.
 */
define('NET_SCP_LOCAL_FILE', 1);
/**
 * Reads data from a string.
 */
define('NET_SCP_STRING',  2);
/**#@-*/

/**#@+
 * @access private
 * @see self::_send()
 * @see self::_receive()
 */
/**
 * SSH1 is being used.
 */
define('NET_SCP_SSH1', 1);
/**
 * SSH2 is being used.
 */
define('NET_SCP_SSH2',  2);
/**#@-*/

/**
 * Pure-PHP implementations of SCP.
 *
 * @package Net_SCP
 * @author  Jim Wigginton <terrafrost@php.net>
 * @access  public
 */
class Net_SCP
{
    /**
     * SSH Object
     *
     * @var object
     * @access private
     */
    var $ssh;

    /**
     * Packet Size
     *
     * @var int
     * @access private
     */
    var $packet_size;

    /**
     * Mode
     *
     * @var int
     * @access private
     */
    var $mode;

    /**
     * Default Constructor.
     *
     * Connects to an SSH server
     *
     * @param Net_SSH1|Net_SSH2 $ssh
     * @return Net_SCP
     * @access public
     */
    function __construct($ssh)
    {
        if (!is_object($ssh)) {
            return;
        }

        switch (strtolower(get_class($ssh))) {
            case 'net_ssh2':
                $this->mode = NET_SCP_SSH2;
                break;
            case 'net_ssh1':
                $this->packet_size = 50000;
                $this->mode = NET_SCP_SSH1;
                break;
            default:
                return;
        }

        $this->ssh = $ssh;
    }

    /**
     * PHP4 compatible Default Constructor.
     *
     * @see self::__construct()
     * @param Net_SSH1|Net_SSH2 $ssh
     * @access public
     */
    function Net_SCP($ssh)
    {
        $this->__construct($ssh);
    }

    /**
     * Uploads a file to the SCP server.
     *
     * By default, Net_SCP::put() does not read from the local filesystem.  $data is dumped directly into $remote_file.
     * So, for example, if you set $data to 'filename.ext' and then do Net_SCP::get(), you will get a file, twelve bytes
     * long, containing 'filename.ext' as its contents.
     *
     * Setting $mode to NET_SCP_LOCAL_FILE will change the above behavior.  With NET_SCP_LOCAL_FILE, $remote_file will
     * contain as many bytes as filename.ext does on your local filesystem.  If your filename.ext is 1MB then that is how
     * large $remote_file will be, as well.
     *
     * Currently, only binary mode is supported.  As such, if the line endings need to be adjusted, you will need to take
     * care of that, yourself.
     *
     * @param string $remote_file
     * @param string $data
     * @param int $mode
     * @param callable $callback
     * @return bool
     * @access public
     */
    function put($remote_file, $data, $mode = NET_SCP_STRING, $callback = null)
    {
        if (!isset($this->ssh)) {
            return false;
        }

        if (!$this->ssh->exec('scp -t ' . escapeshellarg($remote_file), false)) { // -t = to
            return false;
        }

        $temp = $this->_receive();
        if ($temp !== chr(0)) {
            return false;
        }

        if ($this->mode == NET_SCP_SSH2) {
            $this->packet_size = $this->ssh->packet_size_client_to_server[NET_SSH2_CHANNEL_EXEC] - 4;
        }

        $remote_file = basename($remote_file);

        if ($mode == NET_SCP_STRING) {
            $size = strlen($data);
        } else {
            if (!is_file($data)) {
                user_error("$data is not a valid file", E_USER_NOTICE);
                return false;
            }

            $fp = @fopen($data, 'rb');
            if (!$fp) {
                return false;
            }
            $size = filesize($data);
        }

        $this->_send('C0644 ' . $size . ' ' . $remote_file . "\n");

        $temp = $this->_receive();
        if ($temp !== chr(0)) {
            return false;
        }

        $sent = 0;
        while ($sent < $size) {
            $temp = $mode & NET_SCP_STRING ? substr($data, $sent, $this->packet_size) : fread($fp, $this->packet_size);
            $this->_send($temp);
            $sent+= strlen($temp);

            if (is_callable($callback)) {
                call_user_func($callback, $sent);
            }
        }
        $this->_close();

        if ($mode != NET_SCP_STRING) {
            fclose($fp);
        }

        return true;
    }

    /**
     * Downloads a file from the SCP server.
     *
     * Returns a string containing the contents of $remote_file if $local_file is left undefined or a boolean false if
     * the operation was unsuccessful.  If $local_file is defined, returns true or false depending on the success of the
     * operation
     *
     * @param string $remote_file
     * @param string $local_file
     * @return mixed
     * @access public
     */
    function get($remote_file, $local_file = false)
    {
        if (!isset($this->ssh)) {
            return false;
        }

        if (!$this->ssh->exec('scp -f ' . escapeshellarg($remote_file), false)) { // -f = from
            return false;
        }

        $this->_send("\0");

        if (!preg_match('#(?<perms>[^ ]+) (?<size>\d+) (?<name>.+)#', rtrim($this->_receive()), $info)) {
            return false;
        }

        $this->_send("\0");

        $size = 0;

        if ($local_file !== false) {
            $fp = @fopen($local_file, 'wb');
            if (!$fp) {
                return false;
            }
        }

        $content = '';
        while ($size < $info['size']) {
            $data = $this->_receive();
            // SCP usually seems to split stuff out into 16k chunks
            $size+= strlen($data);

            if ($local_file === false) {
                $content.= $data;
            } else {
                fputs($fp, $data);
            }
        }

        $this->_close();

        if ($local_file !== false) {
            fclose($fp);
            return true;
        }

        return $content;
    }

    /**
     * Sends a packet to an SSH server
     *
     * @param string $data
     * @access private
     */
    function _send($data)
    {
        switch ($this->mode) {
            case NET_SCP_SSH2:
                $this->ssh->_send_channel_packet(NET_SSH2_CHANNEL_EXEC, $data);
                break;
            case NET_SCP_SSH1:
                $data = pack('CNa*', NET_SSH1_CMSG_STDIN_DATA, strlen($data), $data);
                $this->ssh->_send_binary_packet($data);
        }
    }

    /**
     * Receives a packet from an SSH server
     *
     * @return string
     * @access private
     */
    function _receive()
    {
        switch ($this->mode) {
            case NET_SCP_SSH2:
                return $this->ssh->_get_channel_packet(NET_SSH2_CHANNEL_EXEC, true);
            case NET_SCP_SSH1:
                if (!$this->ssh->bitmap) {
                    return false;
                }
                while (true) {
                    $response = $this->ssh->_get_binary_packet();
                    switch ($response[NET_SSH1_RESPONSE_TYPE]) {
                        case NET_SSH1_SMSG_STDOUT_DATA:
                            if (strlen($response[NET_SSH1_RESPONSE_DATA]) < 4) {
                                return false;
                            }
                            extract(unpack('Nlength', $response[NET_SSH1_RESPONSE_DATA]));
                            return $this->ssh->_string_shift($response[NET_SSH1_RESPONSE_DATA], $length);
                        case NET_SSH1_SMSG_STDERR_DATA:
                            break;
                        case NET_SSH1_SMSG_EXITSTATUS:
                            $this->ssh->_send_binary_packet(chr(NET_SSH1_CMSG_EXIT_CONFIRMATION));
                            fclose($this->ssh->fsock);
                            $this->ssh->bitmap = 0;
                            return false;
                        default:
                            user_error('Unknown packet received', E_USER_NOTICE);
                            return false;
                    }
                }
        }
    }

    /**
     * Closes the connection to an SSH server
     *
     * @access private
     */
    function _close()
    {
        switch ($this->mode) {
            case NET_SCP_SSH2:
                $this->ssh->_close_channel(NET_SSH2_CHANNEL_EXEC, true);
                break;
            case NET_SCP_SSH1:
                $this->ssh->disconnect();
        }
    }
}
vendor/phpseclib/phpseclib/phpseclib/Net/SFTP/Stream.php000064400000055040151327705700017215 0ustar00<?php

/**
 * SFTP Stream Wrapper
 *
 * Creates an sftp:// protocol handler that can be used with, for example, fopen(), dir(), etc.
 *
 * PHP version 5
 *
 * LICENSE: Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 *
 * @category  Net
 * @package   Net_SFTP_Stream
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2013 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      http://phpseclib.sourceforge.net
 */

/**
 * SFTP Stream Wrapper
 *
 * @package Net_SFTP_Stream
 * @author  Jim Wigginton <terrafrost@php.net>
 * @access  public
 */
class Net_SFTP_Stream
{
    /**
     * SFTP instances
     *
     * Rather than re-create the connection we re-use instances if possible
     *
     * @var array
     */
    static $instances;

    /**
     * SFTP instance
     *
     * @var object
     * @access private
     */
    var $sftp;

    /**
     * Path
     *
     * @var string
     * @access private
     */
    var $path;

    /**
     * Mode
     *
     * @var string
     * @access private
     */
    var $mode;

    /**
     * Position
     *
     * @var int
     * @access private
     */
    var $pos;

    /**
     * Size
     *
     * @var int
     * @access private
     */
    var $size;

    /**
     * Directory entries
     *
     * @var array
     * @access private
     */
    var $entries;

    /**
     * EOF flag
     *
     * @var bool
     * @access private
     */
    var $eof;

    /**
     * Context resource
     *
     * Technically this needs to be publically accessible so PHP can set it directly
     *
     * @var resource
     * @access public
     */
    var $context;

    /**
     * Notification callback function
     *
     * @var callable
     * @access public
     */
    var $notification;

    /**
     * Registers this class as a URL wrapper.
     *
     * @param string $protocol The wrapper name to be registered.
     * @return bool True on success, false otherwise.
     * @access public
     */
    static function register($protocol = 'sftp')
    {
        if (in_array($protocol, stream_get_wrappers(), true)) {
            return false;
        }
        $class = function_exists('get_called_class') ? get_called_class() : __CLASS__;
        return stream_wrapper_register($protocol, $class);
    }

    /**
     * The Constructor
     *
     * @access public
     */
    function __construct()
    {
        if (defined('NET_SFTP_STREAM_LOGGING')) {
            echo "__construct()\r\n";
        }

        if (!class_exists('Net_SFTP')) {
            include_once 'Net/SFTP.php';
        }
    }

    /**
     * Path Parser
     *
     * Extract a path from a URI and actually connect to an SSH server if appropriate
     *
     * If "notification" is set as a context parameter the message code for successful login is
     * NET_SSH2_MSG_USERAUTH_SUCCESS. For a failed login it's NET_SSH2_MSG_USERAUTH_FAILURE.
     *
     * @param string $path
     * @return string
     * @access private
     */
    function _parse_path($path)
    {
        $orig = $path;
        extract(parse_url($path) + array('port' => 22));
        if (isset($query)) {
            $path.= '?' . $query;
        } elseif (preg_match('/(\?|\?#)$/', $orig)) {
            $path.= '?';
        }
        if (isset($fragment)) {
            $path.= '#' . $fragment;
        } elseif ($orig[strlen($orig) - 1] == '#') {
            $path.= '#';
        }

        if (!isset($host)) {
            return false;
        }

        if (isset($this->context)) {
            $context = stream_context_get_params($this->context);
            if (isset($context['notification'])) {
                $this->notification = $context['notification'];
            }
        }

        if ($host[0] == '$') {
            $host = substr($host, 1);
            global ${$host};
            if (!is_object($$host) || get_class($$host) != 'Net_SFTP') {
                return false;
            }
            $this->sftp = $$host;
        } else {
            if (isset($this->context)) {
                $context = stream_context_get_options($this->context);
            }
            if (isset($context[$scheme]['session'])) {
                $sftp = $context[$scheme]['session'];
            }
            if (isset($context[$scheme]['sftp'])) {
                $sftp = $context[$scheme]['sftp'];
            }
            if (isset($sftp) && is_object($sftp) && get_class($sftp) == 'Net_SFTP') {
                $this->sftp = $sftp;
                return $path;
            }
            if (isset($context[$scheme]['username'])) {
                $user = $context[$scheme]['username'];
            }
            if (isset($context[$scheme]['password'])) {
                $pass = $context[$scheme]['password'];
            }
            if (isset($context[$scheme]['privkey']) && is_object($context[$scheme]['privkey']) && get_Class($context[$scheme]['privkey']) == 'Crypt_RSA') {
                $pass = $context[$scheme]['privkey'];
            }

            if (!isset($user) || !isset($pass)) {
                return false;
            }

            // casting $pass to a string is necessary in the event that it's a Crypt_RSA object
            if (isset(self::$instances[$host][$port][$user][(string) $pass])) {
                $this->sftp = self::$instances[$host][$port][$user][(string) $pass];
            } else {
                $this->sftp = new Net_SFTP($host, $port);
                $this->sftp->disableStatCache();
                if (isset($this->notification) && is_callable($this->notification)) {
                    /* if !is_callable($this->notification) we could do this:

                       user_error('fopen(): failed to call user notifier', E_USER_WARNING);

                       the ftp wrapper gives errors like that when the notifier isn't callable.
                       i've opted not to do that, however, since the ftp wrapper gives the line
                       on which the fopen occurred as the line number - not the line that the
                       user_error is on.
                    */
                    call_user_func($this->notification, STREAM_NOTIFY_CONNECT, STREAM_NOTIFY_SEVERITY_INFO, '', 0, 0, 0);
                    call_user_func($this->notification, STREAM_NOTIFY_AUTH_REQUIRED, STREAM_NOTIFY_SEVERITY_INFO, '', 0, 0, 0);
                    if (!$this->sftp->login($user, $pass)) {
                        call_user_func($this->notification, STREAM_NOTIFY_AUTH_RESULT, STREAM_NOTIFY_SEVERITY_ERR, 'Login Failure', NET_SSH2_MSG_USERAUTH_FAILURE, 0, 0);
                        return false;
                    }
                    call_user_func($this->notification, STREAM_NOTIFY_AUTH_RESULT, STREAM_NOTIFY_SEVERITY_INFO, 'Login Success', NET_SSH2_MSG_USERAUTH_SUCCESS, 0, 0);
                } else {
                    if (!$this->sftp->login($user, $pass)) {
                        return false;
                    }
                }
                self::$instances[$host][$port][$user][(string) $pass] = $this->sftp;
            }
        }

        return $path;
    }

    /**
     * Opens file or URL
     *
     * @param string $path
     * @param string $mode
     * @param int $options
     * @param string $opened_path
     * @return bool
     * @access public
     */
    function _stream_open($path, $mode, $options, &$opened_path)
    {
        $path = $this->_parse_path($path);

        if ($path === false) {
            return false;
        }
        $this->path = $path;

        $this->size = $this->sftp->size($path);
        $this->mode = preg_replace('#[bt]$#', '', $mode);
        $this->eof = false;

        if ($this->size === false) {
            if ($this->mode[0] == 'r') {
                return false;
            } else {
                $this->sftp->touch($path);
                $this->size = 0;
            }
        } else {
            switch ($this->mode[0]) {
                case 'x':
                    return false;
                case 'w':
                    $this->sftp->truncate($path, 0);
                    $this->size = 0;
            }
        }

        $this->pos = $this->mode[0] != 'a' ? 0 : $this->size;

        return true;
    }

    /**
     * Read from stream
     *
     * @param int $count
     * @return mixed
     * @access public
     */
    function _stream_read($count)
    {
        switch ($this->mode) {
            case 'w':
            case 'a':
            case 'x':
            case 'c':
                return false;
        }

        // commented out because some files - eg. /dev/urandom - will say their size is 0 when in fact it's kinda infinite
        //if ($this->pos >= $this->size) {
        //    $this->eof = true;
        //    return false;
        //}

        $result = $this->sftp->get($this->path, false, $this->pos, $count);
        if (isset($this->notification) && is_callable($this->notification)) {
            if ($result === false) {
                call_user_func($this->notification, STREAM_NOTIFY_FAILURE, STREAM_NOTIFY_SEVERITY_ERR, $this->sftp->getLastSFTPError(), NET_SFTP_OPEN, 0, 0);
                return 0;
            }
            // seems that PHP calls stream_read in 8k chunks
            call_user_func($this->notification, STREAM_NOTIFY_PROGRESS, STREAM_NOTIFY_SEVERITY_INFO, '', 0, strlen($result), $this->size);
        }

        if (empty($result)) { // ie. false or empty string
            $this->eof = true;
            return false;
        }
        $this->pos+= strlen($result);

        return $result;
    }

    /**
     * Write to stream
     *
     * @param string $data
     * @return mixed
     * @access public
     */
    function _stream_write($data)
    {
        switch ($this->mode) {
            case 'r':
                return false;
        }

        $result = $this->sftp->put($this->path, $data, NET_SFTP_STRING, $this->pos);
        if (isset($this->notification) && is_callable($this->notification)) {
            if (!$result) {
                call_user_func($this->notification, STREAM_NOTIFY_FAILURE, STREAM_NOTIFY_SEVERITY_ERR, $this->sftp->getLastSFTPError(), NET_SFTP_OPEN, 0, 0);
                return 0;
            }
            // seems that PHP splits up strings into 8k blocks before calling stream_write
            call_user_func($this->notification, STREAM_NOTIFY_PROGRESS, STREAM_NOTIFY_SEVERITY_INFO, '', 0, strlen($data), strlen($data));
        }

        if ($result === false) {
            return false;
        }
        $this->pos+= strlen($data);
        if ($this->pos > $this->size) {
            $this->size = $this->pos;
        }
        $this->eof = false;
        return strlen($data);
    }

    /**
     * Retrieve the current position of a stream
     *
     * @return int
     * @access public
     */
    function _stream_tell()
    {
        return $this->pos;
    }

    /**
     * Tests for end-of-file on a file pointer
     *
     * In my testing there are four classes functions that normally effect the pointer:
     * fseek, fputs  / fwrite, fgets / fread and ftruncate.
     *
     * Only fgets / fread, however, results in feof() returning true. do fputs($fp, 'aaa') on a blank file and feof()
     * will return false. do fread($fp, 1) and feof() will then return true. do fseek($fp, 10) on ablank file and feof()
     * will return false. do fread($fp, 1) and feof() will then return true.
     *
     * @return bool
     * @access public
     */
    function _stream_eof()
    {
        return $this->eof;
    }

    /**
     * Seeks to specific location in a stream
     *
     * @param int $offset
     * @param int $whence
     * @return bool
     * @access public
     */
    function _stream_seek($offset, $whence)
    {
        switch ($whence) {
            case SEEK_SET:
                if ($offset >= $this->size || $offset < 0) {
                    return false;
                }
                break;
            case SEEK_CUR:
                $offset+= $this->pos;
                break;
            case SEEK_END:
                $offset+= $this->size;
        }

        $this->pos = $offset;
        $this->eof = false;
        return true;
    }

    /**
     * Change stream options
     *
     * @param string $path
     * @param int $option
     * @param mixed $var
     * @return bool
     * @access public
     */
    function _stream_metadata($path, $option, $var)
    {
        $path = $this->_parse_path($path);
        if ($path === false) {
            return false;
        }

        // stream_metadata was introduced in PHP 5.4.0 but as of 5.4.11 the constants haven't been defined
        // see http://www.php.net/streamwrapper.stream-metadata and https://bugs.php.net/64246
        //     and https://github.com/php/php-src/blob/master/main/php_streams.h#L592
        switch ($option) {
            case 1: // PHP_STREAM_META_TOUCH
                return $this->sftp->touch($path, $var[0], $var[1]);
            case 2: // PHP_STREAM_OWNER_NAME
            case 3: // PHP_STREAM_GROUP_NAME
                return false;
            case 4: // PHP_STREAM_META_OWNER
                return $this->sftp->chown($path, $var);
            case 5: // PHP_STREAM_META_GROUP
                return $this->sftp->chgrp($path, $var);
            case 6: // PHP_STREAM_META_ACCESS
                return $this->sftp->chmod($path, $var) !== false;
        }
    }

    /**
     * Retrieve the underlaying resource
     *
     * @param int $cast_as
     * @return resource
     * @access public
     */
    function _stream_cast($cast_as)
    {
        return $this->sftp->fsock;
    }

    /**
     * Advisory file locking
     *
     * @param int $operation
     * @return bool
     * @access public
     */
    function _stream_lock($operation)
    {
        return false;
    }

    /**
     * Renames a file or directory
     *
     * Attempts to rename oldname to newname, moving it between directories if necessary.
     * If newname exists, it will be overwritten.  This is a departure from what Net_SFTP
     * does.
     *
     * @param string $path_from
     * @param string $path_to
     * @return bool
     * @access public
     */
    function _rename($path_from, $path_to)
    {
        $path1 = parse_url($path_from);
        $path2 = parse_url($path_to);
        unset($path1['path'], $path2['path']);
        if ($path1 != $path2) {
            return false;
        }

        $path_from = $this->_parse_path($path_from);
        $path_to = parse_url($path_to);
        if ($path_from === false) {
            return false;
        }

        $path_to = $path_to['path']; // the $component part of parse_url() was added in PHP 5.1.2
        // "It is an error if there already exists a file with the name specified by newpath."
        //  -- http://tools.ietf.org/html/draft-ietf-secsh-filexfer-02#section-6.5
        if (!$this->sftp->rename($path_from, $path_to)) {
            if ($this->sftp->stat($path_to)) {
                return $this->sftp->delete($path_to, true) && $this->sftp->rename($path_from, $path_to);
            }
            return false;
        }

        return true;
    }

    /**
     * Open directory handle
     *
     * The only $options is "whether or not to enforce safe_mode (0x04)". Since safe mode was deprecated in 5.3 and
     * removed in 5.4 I'm just going to ignore it.
     *
     * Also, nlist() is the best that this function is realistically going to be able to do. When an SFTP client
     * sends a SSH_FXP_READDIR packet you don't generally get info on just one file but on multiple files. Quoting
     * the SFTP specs:
     *
     *    The SSH_FXP_NAME response has the following format:
     *
     *        uint32     id
     *        uint32     count
     *        repeats count times:
     *                string     filename
     *                string     longname
     *                ATTRS      attrs
     *
     * @param string $path
     * @param int $options
     * @return bool
     * @access public
     */
    function _dir_opendir($path, $options)
    {
        $path = $this->_parse_path($path);
        if ($path === false) {
            return false;
        }
        $this->pos = 0;
        $this->entries = $this->sftp->nlist($path);
        return $this->entries !== false;
    }

    /**
     * Read entry from directory handle
     *
     * @return mixed
     * @access public
     */
    function _dir_readdir()
    {
        if (isset($this->entries[$this->pos])) {
            return $this->entries[$this->pos++];
        }
        return false;
    }

    /**
     * Rewind directory handle
     *
     * @return bool
     * @access public
     */
    function _dir_rewinddir()
    {
        $this->pos = 0;
        return true;
    }

    /**
     * Close directory handle
     *
     * @return bool
     * @access public
     */
    function _dir_closedir()
    {
        return true;
    }

    /**
     * Create a directory
     *
     * Only valid $options is STREAM_MKDIR_RECURSIVE
     *
     * @param string $path
     * @param int $mode
     * @param int $options
     * @return bool
     * @access public
     */
    function _mkdir($path, $mode, $options)
    {
        $path = $this->_parse_path($path);
        if ($path === false) {
            return false;
        }

        return $this->sftp->mkdir($path, $mode, $options & STREAM_MKDIR_RECURSIVE);
    }

    /**
     * Removes a directory
     *
     * Only valid $options is STREAM_MKDIR_RECURSIVE per <http://php.net/streamwrapper.rmdir>, however,
     * <http://php.net/rmdir>  does not have a $recursive parameter as mkdir() does so I don't know how
     * STREAM_MKDIR_RECURSIVE is supposed to be set. Also, when I try it out with rmdir() I get 8 as
     * $options. What does 8 correspond to?
     *
     * @param string $path
     * @param int $mode
     * @param int $options
     * @return bool
     * @access public
     */
    function _rmdir($path, $options)
    {
        $path = $this->_parse_path($path);
        if ($path === false) {
            return false;
        }

        return $this->sftp->rmdir($path);
    }

    /**
     * Flushes the output
     *
     * See <http://php.net/fflush>. Always returns true because Net_SFTP doesn't cache stuff before writing
     *
     * @return bool
     * @access public
     */
    function _stream_flush()
    {
        return true;
    }

    /**
     * Retrieve information about a file resource
     *
     * @return mixed
     * @access public
     */
    function _stream_stat()
    {
        $results = $this->sftp->stat($this->path);
        if ($results === false) {
            return false;
        }
        return $results;
    }

    /**
     * Delete a file
     *
     * @param string $path
     * @return bool
     * @access public
     */
    function _unlink($path)
    {
        $path = $this->_parse_path($path);
        if ($path === false) {
            return false;
        }

        return $this->sftp->delete($path, false);
    }

    /**
     * Retrieve information about a file
     *
     * Ignores the STREAM_URL_STAT_QUIET flag because the entirety of Net_SFTP_Stream is quiet by default
     * might be worthwhile to reconstruct bits 12-16 (ie. the file type) if mode doesn't have them but we'll
     * cross that bridge when and if it's reached
     *
     * @param string $path
     * @param int $flags
     * @return mixed
     * @access public
     */
    function _url_stat($path, $flags)
    {
        $path = $this->_parse_path($path);
        if ($path === false) {
            return false;
        }

        $results = $flags & STREAM_URL_STAT_LINK ? $this->sftp->lstat($path) : $this->sftp->stat($path);
        if ($results === false) {
            return false;
        }

        return $results;
    }

    /**
     * Truncate stream
     *
     * @param int $new_size
     * @return bool
     * @access public
     */
    function _stream_truncate($new_size)
    {
        if (!$this->sftp->truncate($this->path, $new_size)) {
            return false;
        }

        $this->eof = false;
        $this->size = $new_size;

        return true;
    }

    /**
     * Change stream options
     *
     * STREAM_OPTION_WRITE_BUFFER isn't supported for the same reason stream_flush isn't.
     * The other two aren't supported because of limitations in Net_SFTP.
     *
     * @param int $option
     * @param int $arg1
     * @param int $arg2
     * @return bool
     * @access public
     */
    function _stream_set_option($option, $arg1, $arg2)
    {
        return false;
    }

    /**
     * Close an resource
     *
     * @access public
     */
    function _stream_close()
    {
    }

    /**
     * __call Magic Method
     *
     * When you're utilizing an SFTP stream you're not calling the methods in this class directly - PHP is calling them for you.
     * Which kinda begs the question... what methods is PHP calling and what parameters is it passing to them? This function
     * lets you figure that out.
     *
     * If NET_SFTP_STREAM_LOGGING is defined all calls will be output on the screen and then (regardless of whether or not
     * NET_SFTP_STREAM_LOGGING is enabled) the parameters will be passed through to the appropriate method.
     *
     * @param string
     * @param array
     * @return mixed
     * @access public
     */
    function __call($name, $arguments)
    {
        if (defined('NET_SFTP_STREAM_LOGGING')) {
            echo $name . '(';
            $last = count($arguments) - 1;
            foreach ($arguments as $i => $argument) {
                var_export($argument);
                if ($i != $last) {
                    echo ',';
                }
            }
            echo ")\r\n";
        }
        $name = '_' . $name;
        if (!method_exists($this, $name)) {
            return false;
        }
        return call_user_func_array(array($this, $name), $arguments);
    }
}

Net_SFTP_Stream::register();
vendor/phpseclib/phpseclib/phpseclib/Net/SSH1.php000064400000153202151327705700015723 0ustar00<?php

/**
 * Pure-PHP implementation of SSHv1.
 *
 * PHP versions 4 and 5
 *
 * Here's a short example of how to use this library:
 * <code>
 * <?php
 *    include 'Net/SSH1.php';
 *
 *    $ssh = new Net_SSH1('www.domain.tld');
 *    if (!$ssh->login('username', 'password')) {
 *        exit('Login Failed');
 *    }
 *
 *    echo $ssh->exec('ls -la');
 * ?>
 * </code>
 *
 * Here's another short example:
 * <code>
 * <?php
 *    include 'Net/SSH1.php';
 *
 *    $ssh = new Net_SSH1('www.domain.tld');
 *    if (!$ssh->login('username', 'password')) {
 *        exit('Login Failed');
 *    }
 *
 *    echo $ssh->read('username@username:~$');
 *    $ssh->write("ls -la\n");
 *    echo $ssh->read('username@username:~$');
 * ?>
 * </code>
 *
 * More information on the SSHv1 specification can be found by reading
 * {@link http://www.snailbook.com/docs/protocol-1.5.txt protocol-1.5.txt}.
 *
 * LICENSE: Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 *
 * @category  Net
 * @package   Net_SSH1
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2007 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      http://phpseclib.sourceforge.net
 */

/**#@+
 * Encryption Methods
 *
 * @see self::getSupportedCiphers()
 * @access public
 */
/**
 * No encryption
 *
 * Not supported.
 */
define('NET_SSH1_CIPHER_NONE',       0);
/**
 * IDEA in CFB mode
 *
 * Not supported.
 */
define('NET_SSH1_CIPHER_IDEA',       1);
/**
 * DES in CBC mode
 */
define('NET_SSH1_CIPHER_DES',        2);
/**
 * Triple-DES in CBC mode
 *
 * All implementations are required to support this
 */
define('NET_SSH1_CIPHER_3DES',       3);
/**
 * TRI's Simple Stream encryption CBC
 *
 * Not supported nor is it defined in the official SSH1 specs.  OpenSSH, however, does define it (see cipher.h),
 * although it doesn't use it (see cipher.c)
 */
define('NET_SSH1_CIPHER_BROKEN_TSS', 4);
/**
 * RC4
 *
 * Not supported.
 *
 * @internal According to the SSH1 specs:
 *
 *        "The first 16 bytes of the session key are used as the key for
 *         the server to client direction.  The remaining 16 bytes are used
 *         as the key for the client to server direction.  This gives
 *         independent 128-bit keys for each direction."
 *
 *     This library currently only supports encryption when the same key is being used for both directions.  This is
 *     because there's only one $crypto object.  Two could be added ($encrypt and $decrypt, perhaps).
 */
define('NET_SSH1_CIPHER_RC4',        5);
/**
 * Blowfish
 *
 * Not supported nor is it defined in the official SSH1 specs.  OpenSSH, however, defines it (see cipher.h) and
 * uses it (see cipher.c)
 */
define('NET_SSH1_CIPHER_BLOWFISH',   6);
/**#@-*/

/**#@+
 * Authentication Methods
 *
 * @see self::getSupportedAuthentications()
 * @access public
 */
/**
 * .rhosts or /etc/hosts.equiv
 */
define('NET_SSH1_AUTH_RHOSTS',     1);
/**
 * pure RSA authentication
 */
define('NET_SSH1_AUTH_RSA',        2);
/**
 * password authentication
 *
 * This is the only method that is supported by this library.
 */
define('NET_SSH1_AUTH_PASSWORD',   3);
/**
 * .rhosts with RSA host authentication
 */
define('NET_SSH1_AUTH_RHOSTS_RSA', 4);
/**#@-*/

/**#@+
 * Terminal Modes
 *
 * @link http://3sp.com/content/developer/maverick-net/docs/Maverick.SSH.PseudoTerminalModesMembers.html
 * @access private
 */
define('NET_SSH1_TTY_OP_END',  0);
/**#@-*/

/**
 * The Response Type
 *
 * @see self::_get_binary_packet()
 * @access private
 */
define('NET_SSH1_RESPONSE_TYPE', 1);

/**
 * The Response Data
 *
 * @see self::_get_binary_packet()
 * @access private
 */
define('NET_SSH1_RESPONSE_DATA', 2);

/**#@+
 * Execution Bitmap Masks
 *
 * @see self::bitmap
 * @access private
 */
define('NET_SSH1_MASK_CONSTRUCTOR', 0x00000001);
define('NET_SSH1_MASK_CONNECTED',   0x00000002);
define('NET_SSH1_MASK_LOGIN',       0x00000004);
define('NET_SSH1_MASK_SHELL',       0x00000008);
/**#@-*/

/**#@+
 * @access public
 * @see self::getLog()
 */
/**
 * Returns the message numbers
 */
define('NET_SSH1_LOG_SIMPLE',  1);
/**
 * Returns the message content
 */
define('NET_SSH1_LOG_COMPLEX', 2);
/**
 * Outputs the content real-time
 */
define('NET_SSH1_LOG_REALTIME', 3);
/**
 * Dumps the content real-time to a file
 */
define('NET_SSH1_LOG_REALTIME_FILE', 4);
/**#@-*/

/**#@+
 * @access public
 * @see self::read()
 */
/**
 * Returns when a string matching $expect exactly is found
 */
define('NET_SSH1_READ_SIMPLE',  1);
/**
 * Returns when a string matching the regular expression $expect is found
 */
define('NET_SSH1_READ_REGEX', 2);
/**#@-*/

/**
 * Pure-PHP implementation of SSHv1.
 *
 * @package Net_SSH1
 * @author  Jim Wigginton <terrafrost@php.net>
 * @access  public
 */
class Net_SSH1
{
    /**
     * The SSH identifier
     *
     * @var string
     * @access private
     */
    var $identifier = 'SSH-1.5-phpseclib';

    /**
     * The Socket Object
     *
     * @var object
     * @access private
     */
    var $fsock;

    /**
     * The cryptography object
     *
     * @var object
     * @access private
     */
    var $crypto = false;

    /**
     * Execution Bitmap
     *
     * The bits that are set represent functions that have been called already.  This is used to determine
     * if a requisite function has been successfully executed.  If not, an error should be thrown.
     *
     * @var int
     * @access private
     */
    var $bitmap = 0;

    /**
     * The Server Key Public Exponent
     *
     * Logged for debug purposes
     *
     * @see self::getServerKeyPublicExponent()
     * @var string
     * @access private
     */
    var $server_key_public_exponent;

    /**
     * The Server Key Public Modulus
     *
     * Logged for debug purposes
     *
     * @see self::getServerKeyPublicModulus()
     * @var string
     * @access private
     */
    var $server_key_public_modulus;

    /**
     * The Host Key Public Exponent
     *
     * Logged for debug purposes
     *
     * @see self::getHostKeyPublicExponent()
     * @var string
     * @access private
     */
    var $host_key_public_exponent;

    /**
     * The Host Key Public Modulus
     *
     * Logged for debug purposes
     *
     * @see self::getHostKeyPublicModulus()
     * @var string
     * @access private
     */
    var $host_key_public_modulus;

    /**
     * Supported Ciphers
     *
     * Logged for debug purposes
     *
     * @see self::getSupportedCiphers()
     * @var array
     * @access private
     */
    var $supported_ciphers = array(
        NET_SSH1_CIPHER_NONE       => 'No encryption',
        NET_SSH1_CIPHER_IDEA       => 'IDEA in CFB mode',
        NET_SSH1_CIPHER_DES        => 'DES in CBC mode',
        NET_SSH1_CIPHER_3DES       => 'Triple-DES in CBC mode',
        NET_SSH1_CIPHER_BROKEN_TSS => 'TRI\'s Simple Stream encryption CBC',
        NET_SSH1_CIPHER_RC4        => 'RC4',
        NET_SSH1_CIPHER_BLOWFISH   => 'Blowfish'
    );

    /**
     * Supported Authentications
     *
     * Logged for debug purposes
     *
     * @see self::getSupportedAuthentications()
     * @var array
     * @access private
     */
    var $supported_authentications = array(
        NET_SSH1_AUTH_RHOSTS     => '.rhosts or /etc/hosts.equiv',
        NET_SSH1_AUTH_RSA        => 'pure RSA authentication',
        NET_SSH1_AUTH_PASSWORD   => 'password authentication',
        NET_SSH1_AUTH_RHOSTS_RSA => '.rhosts with RSA host authentication'
    );

    /**
     * Server Identification
     *
     * @see self::getServerIdentification()
     * @var string
     * @access private
     */
    var $server_identification = '';

    /**
     * Protocol Flags
     *
     * @see self::Net_SSH1()
     * @var array
     * @access private
     */
    var $protocol_flags = array();

    /**
     * Protocol Flag Log
     *
     * @see self::getLog()
     * @var array
     * @access private
     */
    var $protocol_flag_log = array();

    /**
     * Message Log
     *
     * @see self::getLog()
     * @var array
     * @access private
     */
    var $message_log = array();

    /**
     * Real-time log file pointer
     *
     * @see self::_append_log()
     * @var resource
     * @access private
     */
    var $realtime_log_file;

    /**
     * Real-time log file size
     *
     * @see self::_append_log()
     * @var int
     * @access private
     */
    var $realtime_log_size;

    /**
     * Real-time log file wrap boolean
     *
     * @see self::_append_log()
     * @var bool
     * @access private
     */
    var $realtime_log_wrap;

    /**
     * Interactive Buffer
     *
     * @see self::read()
     * @var array
     * @access private
     */
    var $interactiveBuffer = '';

    /**
     * Timeout
     *
     * @see self::setTimeout()
     * @access private
     */
    var $timeout;

    /**
     * Current Timeout
     *
     * @see self::_get_channel_packet()
     * @access private
     */
    var $curTimeout;

    /**
     * Log Boundary
     *
     * @see self::_format_log()
     * @access private
     */
    var $log_boundary = ':';

    /**
     * Log Long Width
     *
     * @see self::_format_log()
     * @access private
     */
    var $log_long_width = 65;

    /**
     * Log Short Width
     *
     * @see self::_format_log()
     * @access private
     */
    var $log_short_width = 16;

    /**
     * Hostname
     *
     * @see self::Net_SSH1()
     * @see self::_connect()
     * @var string
     * @access private
     */
    var $host;

    /**
     * Port Number
     *
     * @see self::Net_SSH1()
     * @see self::_connect()
     * @var int
     * @access private
     */
    var $port;

    /**
     * Timeout for initial connection
     *
     * Set by the constructor call. Calling setTimeout() is optional. If it's not called functions like
     * exec() won't timeout unless some PHP setting forces it too. The timeout specified in the constructor,
     * however, is non-optional. There will be a timeout, whether or not you set it. If you don't it'll be
     * 10 seconds. It is used by fsockopen() in that function.
     *
     * @see self::Net_SSH1()
     * @see self::_connect()
     * @var int
     * @access private
     */
    var $connectionTimeout;

    /**
     * Default cipher
     *
     * @see self::Net_SSH1()
     * @see self::_connect()
     * @var int
     * @access private
     */
    var $cipher;

    /**
     * Default Constructor.
     *
     * Connects to an SSHv1 server
     *
     * @param string $host
     * @param int $port
     * @param int $timeout
     * @param int $cipher
     * @return Net_SSH1
     * @access public
     */
    function __construct($host, $port = 22, $timeout = 10, $cipher = NET_SSH1_CIPHER_3DES)
    {
        if (!class_exists('Math_BigInteger')) {
            include_once 'Math/BigInteger.php';
        }

        // Include Crypt_Random
        // the class_exists() will only be called if the crypt_random_string function hasn't been defined and
        // will trigger a call to __autoload() if you're wanting to auto-load classes
        // call function_exists() a second time to stop the include_once from being called outside
        // of the auto loader
        if (!function_exists('crypt_random_string') && !class_exists('Crypt_Random') && !function_exists('crypt_random_string')) {
            include_once 'Crypt/Random.php';
        }

        $this->protocol_flags = array(
            1  => 'NET_SSH1_MSG_DISCONNECT',
            2  => 'NET_SSH1_SMSG_PUBLIC_KEY',
            3  => 'NET_SSH1_CMSG_SESSION_KEY',
            4  => 'NET_SSH1_CMSG_USER',
            9  => 'NET_SSH1_CMSG_AUTH_PASSWORD',
            10 => 'NET_SSH1_CMSG_REQUEST_PTY',
            12 => 'NET_SSH1_CMSG_EXEC_SHELL',
            13 => 'NET_SSH1_CMSG_EXEC_CMD',
            14 => 'NET_SSH1_SMSG_SUCCESS',
            15 => 'NET_SSH1_SMSG_FAILURE',
            16 => 'NET_SSH1_CMSG_STDIN_DATA',
            17 => 'NET_SSH1_SMSG_STDOUT_DATA',
            18 => 'NET_SSH1_SMSG_STDERR_DATA',
            19 => 'NET_SSH1_CMSG_EOF',
            20 => 'NET_SSH1_SMSG_EXITSTATUS',
            33 => 'NET_SSH1_CMSG_EXIT_CONFIRMATION'
        );

        $this->_define_array($this->protocol_flags);

        $this->host = $host;
        $this->port = $port;
        $this->connectionTimeout = $timeout;
        $this->cipher = $cipher;
    }

    /**
     * PHP4 compatible Default Constructor.
     *
     * @see self::__construct()
     * @param string $host
     * @param int $port
     * @param int $timeout
     * @param int $cipher
     * @access public
     */
    function Net_SSH1($host, $port = 22, $timeout = 10, $cipher = NET_SSH1_CIPHER_3DES)
    {
        $this->__construct($host, $port, $timeout, $cipher);
    }

    /**
     * Connect to an SSHv1 server
     *
     * @return bool
     * @access private
     */
    function _connect()
    {
        $this->fsock = @fsockopen($this->host, $this->port, $errno, $errstr, $this->connectionTimeout);
        if (!$this->fsock) {
            user_error(rtrim("Cannot connect to {$this->host}:{$this->port}. Error $errno. $errstr"));
            return false;
        }

        $this->server_identification = $init_line = fgets($this->fsock, 255);

        if (defined('NET_SSH1_LOGGING')) {
            $this->_append_log('<-', $this->server_identification);
            $this->_append_log('->', $this->identifier . "\r\n");
        }

        if (!preg_match('#SSH-([0-9\.]+)-(.+)#', $init_line, $parts)) {
            user_error('Can only connect to SSH servers');
            return false;
        }
        if ($parts[1][0] != 1) {
            user_error("Cannot connect to SSH $parts[1] servers");
            return false;
        }

        fputs($this->fsock, $this->identifier."\r\n");

        $response = $this->_get_binary_packet();
        if ($response[NET_SSH1_RESPONSE_TYPE] != NET_SSH1_SMSG_PUBLIC_KEY) {
            user_error('Expected SSH_SMSG_PUBLIC_KEY');
            return false;
        }

        $anti_spoofing_cookie = $this->_string_shift($response[NET_SSH1_RESPONSE_DATA], 8);

        $this->_string_shift($response[NET_SSH1_RESPONSE_DATA], 4);

        if (strlen($response[NET_SSH1_RESPONSE_DATA]) < 2) {
            return false;
        }
        $temp = unpack('nlen', $this->_string_shift($response[NET_SSH1_RESPONSE_DATA], 2));
        $server_key_public_exponent = new Math_BigInteger($this->_string_shift($response[NET_SSH1_RESPONSE_DATA], ceil($temp['len'] / 8)), 256);
        $this->server_key_public_exponent = $server_key_public_exponent;

        if (strlen($response[NET_SSH1_RESPONSE_DATA]) < 2) {
            return false;
        }
        $temp = unpack('nlen', $this->_string_shift($response[NET_SSH1_RESPONSE_DATA], 2));
        $server_key_public_modulus = new Math_BigInteger($this->_string_shift($response[NET_SSH1_RESPONSE_DATA], ceil($temp['len'] / 8)), 256);
        $this->server_key_public_modulus = $server_key_public_modulus;

        $this->_string_shift($response[NET_SSH1_RESPONSE_DATA], 4);

        if (strlen($response[NET_SSH1_RESPONSE_DATA]) < 2) {
            return false;
        }
        $temp = unpack('nlen', $this->_string_shift($response[NET_SSH1_RESPONSE_DATA], 2));
        $host_key_public_exponent = new Math_BigInteger($this->_string_shift($response[NET_SSH1_RESPONSE_DATA], ceil($temp['len'] / 8)), 256);
        $this->host_key_public_exponent = $host_key_public_exponent;

        if (strlen($response[NET_SSH1_RESPONSE_DATA]) < 2) {
            return false;
        }
        $temp = unpack('nlen', $this->_string_shift($response[NET_SSH1_RESPONSE_DATA], 2));
        $host_key_public_modulus = new Math_BigInteger($this->_string_shift($response[NET_SSH1_RESPONSE_DATA], ceil($temp['len'] / 8)), 256);
        $this->host_key_public_modulus = $host_key_public_modulus;

        $this->_string_shift($response[NET_SSH1_RESPONSE_DATA], 4);

        // get a list of the supported ciphers
        if (strlen($response[NET_SSH1_RESPONSE_DATA]) < 4) {
            return false;
        }
        extract(unpack('Nsupported_ciphers_mask', $this->_string_shift($response[NET_SSH1_RESPONSE_DATA], 4)));
        foreach ($this->supported_ciphers as $mask => $name) {
            if (($supported_ciphers_mask & (1 << $mask)) == 0) {
                unset($this->supported_ciphers[$mask]);
            }
        }

        // get a list of the supported authentications
        if (strlen($response[NET_SSH1_RESPONSE_DATA]) < 4) {
            return false;
        }
        extract(unpack('Nsupported_authentications_mask', $this->_string_shift($response[NET_SSH1_RESPONSE_DATA], 4)));
        foreach ($this->supported_authentications as $mask => $name) {
            if (($supported_authentications_mask & (1 << $mask)) == 0) {
                unset($this->supported_authentications[$mask]);
            }
        }

        $session_id = pack('H*', md5($host_key_public_modulus->toBytes() . $server_key_public_modulus->toBytes() . $anti_spoofing_cookie));

        $session_key = crypt_random_string(32);
        $double_encrypted_session_key = $session_key ^ str_pad($session_id, 32, chr(0));

        if ($server_key_public_modulus->compare($host_key_public_modulus) < 0) {
            $double_encrypted_session_key = $this->_rsa_crypt(
                $double_encrypted_session_key,
                array(
                    $server_key_public_exponent,
                    $server_key_public_modulus
                )
            );
            $double_encrypted_session_key = $this->_rsa_crypt(
                $double_encrypted_session_key,
                array(
                    $host_key_public_exponent,
                    $host_key_public_modulus
                )
            );
        } else {
            $double_encrypted_session_key = $this->_rsa_crypt(
                $double_encrypted_session_key,
                array(
                    $host_key_public_exponent,
                    $host_key_public_modulus
                )
            );
            $double_encrypted_session_key = $this->_rsa_crypt(
                $double_encrypted_session_key,
                array(
                    $server_key_public_exponent,
                    $server_key_public_modulus
                )
            );
        }

        $cipher = isset($this->supported_ciphers[$this->cipher]) ? $this->cipher : NET_SSH1_CIPHER_3DES;
        $data = pack('C2a*na*N', NET_SSH1_CMSG_SESSION_KEY, $cipher, $anti_spoofing_cookie, 8 * strlen($double_encrypted_session_key), $double_encrypted_session_key, 0);

        if (!$this->_send_binary_packet($data)) {
            user_error('Error sending SSH_CMSG_SESSION_KEY');
            return false;
        }

        switch ($cipher) {
            //case NET_SSH1_CIPHER_NONE:
            //    $this->crypto = new Crypt_Null();
            //    break;
            case NET_SSH1_CIPHER_DES:
                if (!class_exists('Crypt_DES')) {
                    include_once 'Crypt/DES.php';
                }
                $this->crypto = new Crypt_DES();
                $this->crypto->disablePadding();
                $this->crypto->enableContinuousBuffer();
                $this->crypto->setKey(substr($session_key, 0, 8));
                break;
            case NET_SSH1_CIPHER_3DES:
                if (!class_exists('Crypt_TripleDES')) {
                    include_once 'Crypt/TripleDES.php';
                }
                $this->crypto = new Crypt_TripleDES(CRYPT_DES_MODE_3CBC);
                $this->crypto->disablePadding();
                $this->crypto->enableContinuousBuffer();
                $this->crypto->setKey(substr($session_key, 0, 24));
                break;
            //case NET_SSH1_CIPHER_RC4:
            //    if (!class_exists('Crypt_RC4')) {
            //        include_once 'Crypt/RC4.php';
            //    }
            //    $this->crypto = new Crypt_RC4();
            //    $this->crypto->enableContinuousBuffer();
            //    $this->crypto->setKey(substr($session_key, 0,  16));
            //    break;
        }

        $response = $this->_get_binary_packet();

        if ($response[NET_SSH1_RESPONSE_TYPE] != NET_SSH1_SMSG_SUCCESS) {
            user_error('Expected SSH_SMSG_SUCCESS');
            return false;
        }

        $this->bitmap = NET_SSH1_MASK_CONNECTED;

        return true;
    }

    /**
     * Login
     *
     * @param string $username
     * @param string $password
     * @return bool
     * @access public
     */
    function login($username, $password = '')
    {
        if (!($this->bitmap & NET_SSH1_MASK_CONSTRUCTOR)) {
            $this->bitmap |= NET_SSH1_MASK_CONSTRUCTOR;
            if (!$this->_connect()) {
                return false;
            }
        }

        if (!($this->bitmap & NET_SSH1_MASK_CONNECTED)) {
            return false;
        }

        $data = pack('CNa*', NET_SSH1_CMSG_USER, strlen($username), $username);

        if (!$this->_send_binary_packet($data)) {
            user_error('Error sending SSH_CMSG_USER');
            return false;
        }

        $response = $this->_get_binary_packet();

        if ($response === true) {
            return false;
        }
        if ($response[NET_SSH1_RESPONSE_TYPE] == NET_SSH1_SMSG_SUCCESS) {
            $this->bitmap |= NET_SSH1_MASK_LOGIN;
            return true;
        } elseif ($response[NET_SSH1_RESPONSE_TYPE] != NET_SSH1_SMSG_FAILURE) {
            user_error('Expected SSH_SMSG_SUCCESS or SSH_SMSG_FAILURE');
            return false;
        }

        $data = pack('CNa*', NET_SSH1_CMSG_AUTH_PASSWORD, strlen($password), $password);

        if (!$this->_send_binary_packet($data)) {
            user_error('Error sending SSH_CMSG_AUTH_PASSWORD');
            return false;
        }

        // remove the username and password from the last logged packet
        if (defined('NET_SSH1_LOGGING') && NET_SSH1_LOGGING == NET_SSH1_LOG_COMPLEX) {
            $data = pack('CNa*', NET_SSH1_CMSG_AUTH_PASSWORD, strlen('password'), 'password');
            $this->message_log[count($this->message_log) - 1] = $data;
        }

        $response = $this->_get_binary_packet();

        if ($response === true) {
            return false;
        }
        if ($response[NET_SSH1_RESPONSE_TYPE] == NET_SSH1_SMSG_SUCCESS) {
            $this->bitmap |= NET_SSH1_MASK_LOGIN;
            return true;
        } elseif ($response[NET_SSH1_RESPONSE_TYPE] == NET_SSH1_SMSG_FAILURE) {
            return false;
        } else {
            user_error('Expected SSH_SMSG_SUCCESS or SSH_SMSG_FAILURE');
            return false;
        }
    }

    /**
     * Set Timeout
     *
     * $ssh->exec('ping 127.0.0.1'); on a Linux host will never return and will run indefinitely.  setTimeout() makes it so it'll timeout.
     * Setting $timeout to false or 0 will mean there is no timeout.
     *
     * @param mixed $timeout
     */
    function setTimeout($timeout)
    {
        $this->timeout = $this->curTimeout = $timeout;
    }

    /**
     * Executes a command on a non-interactive shell, returns the output, and quits.
     *
     * An SSH1 server will close the connection after a command has been executed on a non-interactive shell.  SSH2
     * servers don't, however, this isn't an SSH2 client.  The way this works, on the server, is by initiating a
     * shell with the -s option, as discussed in the following links:
     *
     * {@link http://www.faqs.org/docs/bashman/bashref_65.html http://www.faqs.org/docs/bashman/bashref_65.html}
     * {@link http://www.faqs.org/docs/bashman/bashref_62.html http://www.faqs.org/docs/bashman/bashref_62.html}
     *
     * To execute further commands, a new Net_SSH1 object will need to be created.
     *
     * Returns false on failure and the output, otherwise.
     *
     * @see self::interactiveRead()
     * @see self::interactiveWrite()
     * @param string $cmd
     * @return mixed
     * @access public
     */
    function exec($cmd, $block = true)
    {
        if (!($this->bitmap & NET_SSH1_MASK_LOGIN)) {
            user_error('Operation disallowed prior to login()');
            return false;
        }

        $data = pack('CNa*', NET_SSH1_CMSG_EXEC_CMD, strlen($cmd), $cmd);

        if (!$this->_send_binary_packet($data)) {
            user_error('Error sending SSH_CMSG_EXEC_CMD');
            return false;
        }

        if (!$block) {
            return true;
        }

        $output = '';
        $response = $this->_get_binary_packet();

        if ($response !== false) {
            do {
                $output.= substr($response[NET_SSH1_RESPONSE_DATA], 4);
                $response = $this->_get_binary_packet();
            } while (is_array($response) && $response[NET_SSH1_RESPONSE_TYPE] != NET_SSH1_SMSG_EXITSTATUS);
        }

        $data = pack('C', NET_SSH1_CMSG_EXIT_CONFIRMATION);

        // i don't think it's really all that important if this packet gets sent or not.
        $this->_send_binary_packet($data);

        fclose($this->fsock);

        // reset the execution bitmap - a new Net_SSH1 object needs to be created.
        $this->bitmap = 0;

        return $output;
    }

    /**
     * Creates an interactive shell
     *
     * @see self::interactiveRead()
     * @see self::interactiveWrite()
     * @return bool
     * @access private
     */
    function _initShell()
    {
        // connect using the sample parameters in protocol-1.5.txt.
        // according to wikipedia.org's entry on text terminals, "the fundamental type of application running on a text
        // terminal is a command line interpreter or shell".  thus, opening a terminal session to run the shell.
        $data = pack('CNa*N4C', NET_SSH1_CMSG_REQUEST_PTY, strlen('vt100'), 'vt100', 24, 80, 0, 0, NET_SSH1_TTY_OP_END);

        if (!$this->_send_binary_packet($data)) {
            user_error('Error sending SSH_CMSG_REQUEST_PTY');
            return false;
        }

        $response = $this->_get_binary_packet();

        if ($response === true) {
            return false;
        }
        if ($response[NET_SSH1_RESPONSE_TYPE] != NET_SSH1_SMSG_SUCCESS) {
            user_error('Expected SSH_SMSG_SUCCESS');
            return false;
        }

        $data = pack('C', NET_SSH1_CMSG_EXEC_SHELL);

        if (!$this->_send_binary_packet($data)) {
            user_error('Error sending SSH_CMSG_EXEC_SHELL');
            return false;
        }

        $this->bitmap |= NET_SSH1_MASK_SHELL;

        //stream_set_blocking($this->fsock, 0);

        return true;
    }

    /**
     * Inputs a command into an interactive shell.
     *
     * @see self::interactiveWrite()
     * @param string $cmd
     * @return bool
     * @access public
     */
    function write($cmd)
    {
        return $this->interactiveWrite($cmd);
    }

    /**
     * Returns the output of an interactive shell when there's a match for $expect
     *
     * $expect can take the form of a string literal or, if $mode == NET_SSH1_READ_REGEX,
     * a regular expression.
     *
     * @see self::write()
     * @param string $expect
     * @param int $mode
     * @return bool
     * @access public
     */
    function read($expect, $mode = NET_SSH1_READ_SIMPLE)
    {
        if (!($this->bitmap & NET_SSH1_MASK_LOGIN)) {
            user_error('Operation disallowed prior to login()');
            return false;
        }

        if (!($this->bitmap & NET_SSH1_MASK_SHELL) && !$this->_initShell()) {
            user_error('Unable to initiate an interactive shell session');
            return false;
        }

        $match = $expect;
        while (true) {
            if ($mode == NET_SSH1_READ_REGEX) {
                preg_match($expect, $this->interactiveBuffer, $matches);
                $match = isset($matches[0]) ? $matches[0] : '';
            }
            $pos = strlen($match) ? strpos($this->interactiveBuffer, $match) : false;
            if ($pos !== false) {
                return $this->_string_shift($this->interactiveBuffer, $pos + strlen($match));
            }
            $response = $this->_get_binary_packet();

            if ($response === true) {
                return $this->_string_shift($this->interactiveBuffer, strlen($this->interactiveBuffer));
            }
            $this->interactiveBuffer.= substr($response[NET_SSH1_RESPONSE_DATA], 4);
        }
    }

    /**
     * Inputs a command into an interactive shell.
     *
     * @see self::interactiveRead()
     * @param string $cmd
     * @return bool
     * @access public
     */
    function interactiveWrite($cmd)
    {
        if (!($this->bitmap & NET_SSH1_MASK_LOGIN)) {
            user_error('Operation disallowed prior to login()');
            return false;
        }

        if (!($this->bitmap & NET_SSH1_MASK_SHELL) && !$this->_initShell()) {
            user_error('Unable to initiate an interactive shell session');
            return false;
        }

        $data = pack('CNa*', NET_SSH1_CMSG_STDIN_DATA, strlen($cmd), $cmd);

        if (!$this->_send_binary_packet($data)) {
            user_error('Error sending SSH_CMSG_STDIN');
            return false;
        }

        return true;
    }

    /**
     * Returns the output of an interactive shell when no more output is available.
     *
     * Requires PHP 4.3.0 or later due to the use of the stream_select() function.  If you see stuff like
     * "^[[00m", you're seeing ANSI escape codes.  According to
     * {@link http://support.microsoft.com/kb/101875 How to Enable ANSI.SYS in a Command Window}, "Windows NT
     * does not support ANSI escape sequences in Win32 Console applications", so if you're a Windows user,
     * there's not going to be much recourse.
     *
     * @see self::interactiveRead()
     * @return string
     * @access public
     */
    function interactiveRead()
    {
        if (!($this->bitmap & NET_SSH1_MASK_LOGIN)) {
            user_error('Operation disallowed prior to login()');
            return false;
        }

        if (!($this->bitmap & NET_SSH1_MASK_SHELL) && !$this->_initShell()) {
            user_error('Unable to initiate an interactive shell session');
            return false;
        }

        $read = array($this->fsock);
        $write = $except = null;
        if (stream_select($read, $write, $except, 0)) {
            $response = $this->_get_binary_packet();
            return substr($response[NET_SSH1_RESPONSE_DATA], 4);
        } else {
            return '';
        }
    }

    /**
     * Disconnect
     *
     * @access public
     */
    function disconnect()
    {
        $this->_disconnect();
    }

    /**
     * Destructor.
     *
     * Will be called, automatically, if you're supporting just PHP5.  If you're supporting PHP4, you'll need to call
     * disconnect().
     *
     * @access public
     */
    function __destruct()
    {
        $this->_disconnect();
    }

    /**
     * Disconnect
     *
     * @param string $msg
     * @access private
     */
    function _disconnect($msg = 'Client Quit')
    {
        if ($this->bitmap) {
            $data = pack('C', NET_SSH1_CMSG_EOF);
            $this->_send_binary_packet($data);
            /*
            $response = $this->_get_binary_packet();
            if ($response === true) {
                $response = array(NET_SSH1_RESPONSE_TYPE => -1);
            }
            switch ($response[NET_SSH1_RESPONSE_TYPE]) {
                case NET_SSH1_SMSG_EXITSTATUS:
                    $data = pack('C', NET_SSH1_CMSG_EXIT_CONFIRMATION);
                    break;
                default:
                    $data = pack('CNa*', NET_SSH1_MSG_DISCONNECT, strlen($msg), $msg);
            }
            */
            $data = pack('CNa*', NET_SSH1_MSG_DISCONNECT, strlen($msg), $msg);

            $this->_send_binary_packet($data);
            fclose($this->fsock);
            $this->bitmap = 0;
        }
    }

    /**
     * Gets Binary Packets
     *
     * See 'The Binary Packet Protocol' of protocol-1.5.txt for more info.
     *
     * Also, this function could be improved upon by adding detection for the following exploit:
     * http://www.securiteam.com/securitynews/5LP042K3FY.html
     *
     * @see self::_send_binary_packet()
     * @return array
     * @access private
     */
    function _get_binary_packet()
    {
        if (feof($this->fsock)) {
            //user_error('connection closed prematurely');
            return false;
        }

        if ($this->curTimeout) {
            $read = array($this->fsock);
            $write = $except = null;

            $start = strtok(microtime(), ' ') + strtok(''); // http://php.net/microtime#61838
            $sec = floor($this->curTimeout);
            $usec = 1000000 * ($this->curTimeout - $sec);
            // on windows this returns a "Warning: Invalid CRT parameters detected" error
            if (!@stream_select($read, $write, $except, $sec, $usec) && !count($read)) {
                //$this->_disconnect('Timeout');
                return true;
            }
            $elapsed = strtok(microtime(), ' ') + strtok('') - $start;
            $this->curTimeout-= $elapsed;
        }

        $start = strtok(microtime(), ' ') + strtok(''); // http://php.net/microtime#61838
        $data = fread($this->fsock, 4);
        if (strlen($data) < 4) {
            return false;
        }
        $temp = unpack('Nlength', $data);

        $padding_length = 8 - ($temp['length'] & 7);
        $length = $temp['length'] + $padding_length;
        $raw = '';

        while ($length > 0) {
            $temp = fread($this->fsock, $length);
            $raw.= $temp;
            $length-= strlen($temp);
        }
        $stop = strtok(microtime(), ' ') + strtok('');

        if (strlen($raw) && $this->crypto !== false) {
            $raw = $this->crypto->decrypt($raw);
        }

        $padding = substr($raw, 0, $padding_length);
        $type = $raw[$padding_length];
        $data = substr($raw, $padding_length + 1, -4);

        if (strlen($raw) < 4) {
            return false;
        }
        $temp = unpack('Ncrc', substr($raw, -4));

        //if ( $temp['crc'] != $this->_crc($padding . $type . $data) ) {
        //    user_error('Bad CRC in packet from server');
        //    return false;
        //}

        $type = ord($type);

        if (defined('NET_SSH1_LOGGING')) {
            $temp = isset($this->protocol_flags[$type]) ? $this->protocol_flags[$type] : 'UNKNOWN';
            $temp = '<- ' . $temp .
                    ' (' . round($stop - $start, 4) . 's)';
            $this->_append_log($temp, $data);
        }

        return array(
            NET_SSH1_RESPONSE_TYPE => $type,
            NET_SSH1_RESPONSE_DATA => $data
        );
    }

    /**
     * Sends Binary Packets
     *
     * Returns true on success, false on failure.
     *
     * @see self::_get_binary_packet()
     * @param string $data
     * @return bool
     * @access private
     */
    function _send_binary_packet($data)
    {
        if (feof($this->fsock)) {
            //user_error('connection closed prematurely');
            return false;
        }

        $length = strlen($data) + 4;

        $padding = crypt_random_string(8 - ($length & 7));

        $orig = $data;
        $data = $padding . $data;
        $data.= pack('N', $this->_crc($data));

        if ($this->crypto !== false) {
            $data = $this->crypto->encrypt($data);
        }

        $packet = pack('Na*', $length, $data);

        $start = strtok(microtime(), ' ') + strtok(''); // http://php.net/microtime#61838
        $result = strlen($packet) == fputs($this->fsock, $packet);
        $stop = strtok(microtime(), ' ') + strtok('');

        if (defined('NET_SSH1_LOGGING')) {
            $temp = isset($this->protocol_flags[ord($orig[0])]) ? $this->protocol_flags[ord($orig[0])] : 'UNKNOWN';
            $temp = '-> ' . $temp .
                    ' (' . round($stop - $start, 4) . 's)';
            $this->_append_log($temp, $orig);
        }

        return $result;
    }

    /**
     * Cyclic Redundancy Check (CRC)
     *
     * PHP's crc32 function is implemented slightly differently than the one that SSH v1 uses, so
     * we've reimplemented it. A more detailed discussion of the differences can be found after
     * $crc_lookup_table's initialization.
     *
     * @see self::_get_binary_packet()
     * @see self::_send_binary_packet()
     * @param string $data
     * @return int
     * @access private
     */
    function _crc($data)
    {
        static $crc_lookup_table = array(
            0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA,
            0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3,
            0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988,
            0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91,
            0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE,
            0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7,
            0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC,
            0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5,
            0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172,
            0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B,
            0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940,
            0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59,
            0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116,
            0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F,
            0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924,
            0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D,
            0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A,
            0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433,
            0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818,
            0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01,
            0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E,
            0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457,
            0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C,
            0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65,
            0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2,
            0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB,
            0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0,
            0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9,
            0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086,
            0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F,
            0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4,
            0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD,
            0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A,
            0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683,
            0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8,
            0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1,
            0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE,
            0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7,
            0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC,
            0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5,
            0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252,
            0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B,
            0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60,
            0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79,
            0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236,
            0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F,
            0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04,
            0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D,
            0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A,
            0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713,
            0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38,
            0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21,
            0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E,
            0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777,
            0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C,
            0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45,
            0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2,
            0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB,
            0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0,
            0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9,
            0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6,
            0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF,
            0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94,
            0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D
        );

        // For this function to yield the same output as PHP's crc32 function, $crc would have to be
        // set to 0xFFFFFFFF, initially - not 0x00000000 as it currently is.
        $crc = 0x00000000;
        $length = strlen($data);

        for ($i=0; $i<$length; $i++) {
            // We AND $crc >> 8 with 0x00FFFFFF because we want the eight newly added bits to all
            // be zero.  PHP, unfortunately, doesn't always do this.  0x80000000 >> 8, as an example,
            // yields 0xFF800000 - not 0x00800000.  The following link elaborates:
            // http://www.php.net/manual/en/language.operators.bitwise.php#57281
            $crc = (($crc >> 8) & 0x00FFFFFF) ^ $crc_lookup_table[($crc & 0xFF) ^ ord($data[$i])];
        }

        // In addition to having to set $crc to 0xFFFFFFFF, initially, the return value must be XOR'd with
        // 0xFFFFFFFF for this function to return the same thing that PHP's crc32 function would.
        return $crc;
    }

    /**
     * String Shift
     *
     * Inspired by array_shift
     *
     * @param string $string
     * @param int $index
     * @return string
     * @access private
     */
    function _string_shift(&$string, $index = 1)
    {
        $substr = substr($string, 0, $index);
        $string = substr($string, $index);
        return $substr;
    }

    /**
     * RSA Encrypt
     *
     * Returns mod(pow($m, $e), $n), where $n should be the product of two (large) primes $p and $q and where $e
     * should be a number with the property that gcd($e, ($p - 1) * ($q - 1)) == 1.  Could just make anything that
     * calls this call modexp, instead, but I think this makes things clearer, maybe...
     *
     * @see self::Net_SSH1()
     * @param Math_BigInteger $m
     * @param array $key
     * @return Math_BigInteger
     * @access private
     */
    function _rsa_crypt($m, $key)
    {
        /*
        if (!class_exists('Crypt_RSA')) {
            include_once 'Crypt/RSA.php';
        }

        $rsa = new Crypt_RSA();
        $rsa->loadKey($key, CRYPT_RSA_PUBLIC_FORMAT_RAW);
        $rsa->setEncryptionMode(CRYPT_RSA_ENCRYPTION_PKCS1);
        return $rsa->encrypt($m);
        */

        // To quote from protocol-1.5.txt:
        // The most significant byte (which is only partial as the value must be
        // less than the public modulus, which is never a power of two) is zero.
        //
        // The next byte contains the value 2 (which stands for public-key
        // encrypted data in the PKCS standard [PKCS#1]).  Then, there are non-
        // zero random bytes to fill any unused space, a zero byte, and the data
        // to be encrypted in the least significant bytes, the last byte of the
        // data in the least significant byte.

        // Presumably the part of PKCS#1 they're refering to is "Section 7.2.1 Encryption Operation",
        // under "7.2 RSAES-PKCS1-v1.5" and "7 Encryption schemes" of the following URL:
        // ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-1/pkcs-1v2-1.pdf
        $modulus = $key[1]->toBytes();
        $length = strlen($modulus) - strlen($m) - 3;
        $random = '';
        while (strlen($random) != $length) {
            $block = crypt_random_string($length - strlen($random));
            $block = str_replace("\x00", '', $block);
            $random.= $block;
        }
        $temp = chr(0) . chr(2) . $random . chr(0) . $m;

        $m = new Math_BigInteger($temp, 256);
        $m = $m->modPow($key[0], $key[1]);

        return $m->toBytes();
    }

    /**
     * Define Array
     *
     * Takes any number of arrays whose indices are integers and whose values are strings and defines a bunch of
     * named constants from it, using the value as the name of the constant and the index as the value of the constant.
     * If any of the constants that would be defined already exists, none of the constants will be defined.
     *
     * @param array $array
     * @access private
     */
    function _define_array()
    {
        $args = func_get_args();
        foreach ($args as $arg) {
            foreach ($arg as $key => $value) {
                if (!defined($value)) {
                    define($value, $key);
                } else {
                    break 2;
                }
            }
        }
    }

    /**
     * Returns a log of the packets that have been sent and received.
     *
     * Returns a string if NET_SSH1_LOGGING == NET_SSH1_LOG_COMPLEX, an array if NET_SSH1_LOGGING == NET_SSH1_LOG_SIMPLE and false if !defined('NET_SSH1_LOGGING')
     *
     * @access public
     * @return array|false|string
     */
    function getLog()
    {
        if (!defined('NET_SSH1_LOGGING')) {
            return false;
        }

        switch (NET_SSH1_LOGGING) {
            case NET_SSH1_LOG_SIMPLE:
                return $this->message_number_log;
                break;
            case NET_SSH1_LOG_COMPLEX:
                return $this->_format_log($this->message_log, $this->protocol_flags_log);
                break;
            default:
                return false;
        }
    }

    /**
     * Formats a log for printing
     *
     * @param array $message_log
     * @param array $message_number_log
     * @access private
     * @return string
     */
    function _format_log($message_log, $message_number_log)
    {
        $output = '';
        for ($i = 0; $i < count($message_log); $i++) {
            $output.= $message_number_log[$i] . "\r\n";
            $current_log = $message_log[$i];
            $j = 0;
            do {
                if (strlen($current_log)) {
                    $output.= str_pad(dechex($j), 7, '0', STR_PAD_LEFT) . '0  ';
                }
                $fragment = $this->_string_shift($current_log, $this->log_short_width);
                $hex = substr(preg_replace_callback('#.#s', array($this, '_format_log_helper'), $fragment), strlen($this->log_boundary));
                // replace non ASCII printable characters with dots
                // http://en.wikipedia.org/wiki/ASCII#ASCII_printable_characters
                // also replace < with a . since < messes up the output on web browsers
                $raw = preg_replace('#[^\x20-\x7E]|<#', '.', $fragment);
                $output.= str_pad($hex, $this->log_long_width - $this->log_short_width, ' ') . $raw . "\r\n";
                $j++;
            } while (strlen($current_log));
            $output.= "\r\n";
        }

        return $output;
    }

    /**
     * Helper function for _format_log
     *
     * For use with preg_replace_callback()
     *
     * @param array $matches
     * @access private
     * @return string
     */
    function _format_log_helper($matches)
    {
        return $this->log_boundary . str_pad(dechex(ord($matches[0])), 2, '0', STR_PAD_LEFT);
    }

    /**
     * Return the server key public exponent
     *
     * Returns, by default, the base-10 representation.  If $raw_output is set to true, returns, instead,
     * the raw bytes.  This behavior is similar to PHP's md5() function.
     *
     * @param bool $raw_output
     * @return string
     * @access public
     */
    function getServerKeyPublicExponent($raw_output = false)
    {
        return $raw_output ? $this->server_key_public_exponent->toBytes() : $this->server_key_public_exponent->toString();
    }

    /**
     * Return the server key public modulus
     *
     * Returns, by default, the base-10 representation.  If $raw_output is set to true, returns, instead,
     * the raw bytes.  This behavior is similar to PHP's md5() function.
     *
     * @param bool $raw_output
     * @return string
     * @access public
     */
    function getServerKeyPublicModulus($raw_output = false)
    {
        return $raw_output ? $this->server_key_public_modulus->toBytes() : $this->server_key_public_modulus->toString();
    }

    /**
     * Return the host key public exponent
     *
     * Returns, by default, the base-10 representation.  If $raw_output is set to true, returns, instead,
     * the raw bytes.  This behavior is similar to PHP's md5() function.
     *
     * @param bool $raw_output
     * @return string
     * @access public
     */
    function getHostKeyPublicExponent($raw_output = false)
    {
        return $raw_output ? $this->host_key_public_exponent->toBytes() : $this->host_key_public_exponent->toString();
    }

    /**
     * Return the host key public modulus
     *
     * Returns, by default, the base-10 representation.  If $raw_output is set to true, returns, instead,
     * the raw bytes.  This behavior is similar to PHP's md5() function.
     *
     * @param bool $raw_output
     * @return string
     * @access public
     */
    function getHostKeyPublicModulus($raw_output = false)
    {
        return $raw_output ? $this->host_key_public_modulus->toBytes() : $this->host_key_public_modulus->toString();
    }

    /**
     * Return a list of ciphers supported by SSH1 server.
     *
     * Just because a cipher is supported by an SSH1 server doesn't mean it's supported by this library. If $raw_output
     * is set to true, returns, instead, an array of constants.  ie. instead of array('Triple-DES in CBC mode'), you'll
     * get array(NET_SSH1_CIPHER_3DES).
     *
     * @param bool $raw_output
     * @return array
     * @access public
     */
    function getSupportedCiphers($raw_output = false)
    {
        return $raw_output ? array_keys($this->supported_ciphers) : array_values($this->supported_ciphers);
    }

    /**
     * Return a list of authentications supported by SSH1 server.
     *
     * Just because a cipher is supported by an SSH1 server doesn't mean it's supported by this library. If $raw_output
     * is set to true, returns, instead, an array of constants.  ie. instead of array('password authentication'), you'll
     * get array(NET_SSH1_AUTH_PASSWORD).
     *
     * @param bool $raw_output
     * @return array
     * @access public
     */
    function getSupportedAuthentications($raw_output = false)
    {
        return $raw_output ? array_keys($this->supported_authentications) : array_values($this->supported_authentications);
    }

    /**
     * Return the server identification.
     *
     * @return string
     * @access public
     */
    function getServerIdentification()
    {
        return rtrim($this->server_identification);
    }

    /**
     * Logs data packets
     *
     * Makes sure that only the last 1MB worth of packets will be logged
     *
     * @param string $data
     * @access private
     */
    function _append_log($protocol_flags, $message)
    {
        switch (NET_SSH1_LOGGING) {
            // useful for benchmarks
            case NET_SSH1_LOG_SIMPLE:
                $this->protocol_flags_log[] = $protocol_flags;
                break;
            // the most useful log for SSH1
            case NET_SSH1_LOG_COMPLEX:
                $this->protocol_flags_log[] = $protocol_flags;
                $this->_string_shift($message);
                $this->log_size+= strlen($message);
                $this->message_log[] = $message;
                while ($this->log_size > NET_SSH1_LOG_MAX_SIZE) {
                    $this->log_size-= strlen(array_shift($this->message_log));
                    array_shift($this->protocol_flags_log);
                }
                break;
            // dump the output out realtime; packets may be interspersed with non packets,
            // passwords won't be filtered out and select other packets may not be correctly
            // identified
            case NET_SSH1_LOG_REALTIME:
                echo "<pre>\r\n" . $this->_format_log(array($message), array($protocol_flags)) . "\r\n</pre>\r\n";
                @flush();
                @ob_flush();
                break;
            // basically the same thing as NET_SSH1_LOG_REALTIME with the caveat that NET_SSH1_LOG_REALTIME_FILE
            // needs to be defined and that the resultant log file will be capped out at NET_SSH1_LOG_MAX_SIZE.
            // the earliest part of the log file is denoted by the first <<< START >>> and is not going to necessarily
            // at the beginning of the file
            case NET_SSH1_LOG_REALTIME_FILE:
                if (!isset($this->realtime_log_file)) {
                    // PHP doesn't seem to like using constants in fopen()
                    $filename = NET_SSH1_LOG_REALTIME_FILE;
                    $fp = fopen($filename, 'w');
                    $this->realtime_log_file = $fp;
                }
                if (!is_resource($this->realtime_log_file)) {
                    break;
                }
                $entry = $this->_format_log(array($message), array($protocol_flags));
                if ($this->realtime_log_wrap) {
                    $temp = "<<< START >>>\r\n";
                    $entry.= $temp;
                    fseek($this->realtime_log_file, ftell($this->realtime_log_file) - strlen($temp));
                }
                $this->realtime_log_size+= strlen($entry);
                if ($this->realtime_log_size > NET_SSH1_LOG_MAX_SIZE) {
                    fseek($this->realtime_log_file, 0);
                    $this->realtime_log_size = strlen($entry);
                    $this->realtime_log_wrap = true;
                }
                fputs($this->realtime_log_file, $entry);
        }
    }
}
vendor/phpseclib/phpseclib/phpseclib/File/ASN1.php000064400000163422151327705700016045 0ustar00<?php

/**
 * Pure-PHP ASN.1 Parser
 *
 * PHP versions 4 and 5
 *
 * ASN.1 provides the semantics for data encoded using various schemes.  The most commonly
 * utilized scheme is DER or the "Distinguished Encoding Rules".  PEM's are base64 encoded
 * DER blobs.
 *
 * File_ASN1 decodes and encodes DER formatted messages and places them in a semantic context.
 *
 * Uses the 1988 ASN.1 syntax.
 *
 * LICENSE: Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 *
 * @category  File
 * @package   File_ASN1
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2012 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      http://phpseclib.sourceforge.net
 */

/**#@+
 * Tag Classes
 *
 * @access private
 * @link http://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#page=12
 */
define('FILE_ASN1_CLASS_UNIVERSAL',        0);
define('FILE_ASN1_CLASS_APPLICATION',      1);
define('FILE_ASN1_CLASS_CONTEXT_SPECIFIC', 2);
define('FILE_ASN1_CLASS_PRIVATE',          3);
/**#@-*/

/**#@+
 * Tag Classes
 *
 * @access private
 * @link http://www.obj-sys.com/asn1tutorial/node124.html
 */
define('FILE_ASN1_TYPE_BOOLEAN',           1);
define('FILE_ASN1_TYPE_INTEGER',           2);
define('FILE_ASN1_TYPE_BIT_STRING',        3);
define('FILE_ASN1_TYPE_OCTET_STRING',      4);
define('FILE_ASN1_TYPE_NULL',              5);
define('FILE_ASN1_TYPE_OBJECT_IDENTIFIER', 6);
//define('FILE_ASN1_TYPE_OBJECT_DESCRIPTOR', 7);
//define('FILE_ASN1_TYPE_INSTANCE_OF',       8); // EXTERNAL
define('FILE_ASN1_TYPE_REAL',              9);
define('FILE_ASN1_TYPE_ENUMERATED',       10);
//define('FILE_ASN1_TYPE_EMBEDDED',         11);
define('FILE_ASN1_TYPE_UTF8_STRING',      12);
//define('FILE_ASN1_TYPE_RELATIVE_OID',     13);
define('FILE_ASN1_TYPE_SEQUENCE',         16); // SEQUENCE OF
define('FILE_ASN1_TYPE_SET',              17); // SET OF
/**#@-*/
/**#@+
 * More Tag Classes
 *
 * @access private
 * @link http://www.obj-sys.com/asn1tutorial/node10.html
 */
define('FILE_ASN1_TYPE_NUMERIC_STRING',   18);
define('FILE_ASN1_TYPE_PRINTABLE_STRING', 19);
define('FILE_ASN1_TYPE_TELETEX_STRING',   20); // T61String
define('FILE_ASN1_TYPE_VIDEOTEX_STRING',  21);
define('FILE_ASN1_TYPE_IA5_STRING',       22);
define('FILE_ASN1_TYPE_UTC_TIME',         23);
define('FILE_ASN1_TYPE_GENERALIZED_TIME', 24);
define('FILE_ASN1_TYPE_GRAPHIC_STRING',   25);
define('FILE_ASN1_TYPE_VISIBLE_STRING',   26); // ISO646String
define('FILE_ASN1_TYPE_GENERAL_STRING',   27);
define('FILE_ASN1_TYPE_UNIVERSAL_STRING', 28);
//define('FILE_ASN1_TYPE_CHARACTER_STRING', 29);
define('FILE_ASN1_TYPE_BMP_STRING',       30);
/**#@-*/

/**#@+
 * Tag Aliases
 *
 * These tags are kinda place holders for other tags.
 *
 * @access private
 */
define('FILE_ASN1_TYPE_CHOICE',          -1);
define('FILE_ASN1_TYPE_ANY',             -2);
/**#@-*/

/**
 * ASN.1 Element
 *
 * Bypass normal encoding rules in File_ASN1::encodeDER()
 *
 * @package File_ASN1
 * @author  Jim Wigginton <terrafrost@php.net>
 * @access  public
 */
class File_ASN1_Element
{
    /**
     * Raw element value
     *
     * @var string
     * @access private
     */
    var $element;

    /**
     * Constructor
     *
     * @param string $encoded
     * @return File_ASN1_Element
     * @access public
     */
    function __construct($encoded)
    {
        $this->element = $encoded;
    }

    /**
     * PHP4 compatible Default Constructor.
     *
     * @see self::__construct()
     * @param int $mode
     * @access public
     */
    function File_ASN1_Element($encoded)
    {
        $this->__construct($encoded);
    }
}

/**
 * Pure-PHP ASN.1 Parser
 *
 * @package File_ASN1
 * @author  Jim Wigginton <terrafrost@php.net>
 * @access  public
 */
class File_ASN1
{
    /**
     * ASN.1 object identifier
     *
     * @var array
     * @access private
     * @link http://en.wikipedia.org/wiki/Object_identifier
     */
    var $oids = array();

    /**
     * Default date format
     *
     * @var string
     * @access private
     * @link http://php.net/class.datetime
     */
    var $format = 'D, d M Y H:i:s O';

    /**
     * Default date format
     *
     * @var array
     * @access private
     * @see self::setTimeFormat()
     * @see self::asn1map()
     * @link http://php.net/class.datetime
     */
    var $encoded;

    /**
     * Filters
     *
     * If the mapping type is FILE_ASN1_TYPE_ANY what do we actually encode it as?
     *
     * @var array
     * @access private
     * @see self::_encode_der()
     */
    var $filters;

    /**
     * Type mapping table for the ANY type.
     *
     * Structured or unknown types are mapped to a FILE_ASN1_Element.
     * Unambiguous types get the direct mapping (int/real/bool).
     * Others are mapped as a choice, with an extra indexing level.
     *
     * @var array
     * @access public
     */
    var $ANYmap = array(
        FILE_ASN1_TYPE_BOOLEAN              => true,
        FILE_ASN1_TYPE_INTEGER              => true,
        FILE_ASN1_TYPE_BIT_STRING           => 'bitString',
        FILE_ASN1_TYPE_OCTET_STRING         => 'octetString',
        FILE_ASN1_TYPE_NULL                 => 'null',
        FILE_ASN1_TYPE_OBJECT_IDENTIFIER    => 'objectIdentifier',
        FILE_ASN1_TYPE_REAL                 => true,
        FILE_ASN1_TYPE_ENUMERATED           => 'enumerated',
        FILE_ASN1_TYPE_UTF8_STRING          => 'utf8String',
        FILE_ASN1_TYPE_NUMERIC_STRING       => 'numericString',
        FILE_ASN1_TYPE_PRINTABLE_STRING     => 'printableString',
        FILE_ASN1_TYPE_TELETEX_STRING       => 'teletexString',
        FILE_ASN1_TYPE_VIDEOTEX_STRING      => 'videotexString',
        FILE_ASN1_TYPE_IA5_STRING           => 'ia5String',
        FILE_ASN1_TYPE_UTC_TIME             => 'utcTime',
        FILE_ASN1_TYPE_GENERALIZED_TIME     => 'generalTime',
        FILE_ASN1_TYPE_GRAPHIC_STRING       => 'graphicString',
        FILE_ASN1_TYPE_VISIBLE_STRING       => 'visibleString',
        FILE_ASN1_TYPE_GENERAL_STRING       => 'generalString',
        FILE_ASN1_TYPE_UNIVERSAL_STRING     => 'universalString',
        //FILE_ASN1_TYPE_CHARACTER_STRING     => 'characterString',
        FILE_ASN1_TYPE_BMP_STRING           => 'bmpString'
    );

    /**
     * String type to character size mapping table.
     *
     * Non-convertable types are absent from this table.
     * size == 0 indicates variable length encoding.
     *
     * @var array
     * @access public
     */
    var $stringTypeSize = array(
        FILE_ASN1_TYPE_UTF8_STRING      => 0,
        FILE_ASN1_TYPE_BMP_STRING       => 2,
        FILE_ASN1_TYPE_UNIVERSAL_STRING => 4,
        FILE_ASN1_TYPE_PRINTABLE_STRING => 1,
        FILE_ASN1_TYPE_TELETEX_STRING   => 1,
        FILE_ASN1_TYPE_IA5_STRING       => 1,
        FILE_ASN1_TYPE_VISIBLE_STRING   => 1,
    );

    /**
     * Default Constructor.
     *
     * @access public
     */
    function __construct()
    {
        static $static_init = null;
        if (!$static_init) {
            $static_init = true;
            if (!class_exists('Math_BigInteger')) {
                include_once 'Math/BigInteger.php';
            }
        }
    }

    /**
     * PHP4 compatible Default Constructor.
     *
     * @see self::__construct()
     * @access public
     */
    function File_ASN1()
    {
        $this->__construct($mode);
    }

    /**
     * Parse BER-encoding
     *
     * Serves a similar purpose to openssl's asn1parse
     *
     * @param string $encoded
     * @return array
     * @access public
     */
    function decodeBER($encoded)
    {
        if (is_object($encoded) && strtolower(get_class($encoded)) == 'file_asn1_element') {
            $encoded = $encoded->element;
        }

        $this->encoded = $encoded;
        // encapsulate in an array for BC with the old decodeBER
        return array($this->_decode_ber($encoded));
    }

    /**
     * Parse BER-encoding (Helper function)
     *
     * Sometimes we want to get the BER encoding of a particular tag.  $start lets us do that without having to reencode.
     * $encoded is passed by reference for the recursive calls done for FILE_ASN1_TYPE_BIT_STRING and
     * FILE_ASN1_TYPE_OCTET_STRING. In those cases, the indefinite length is used.
     *
     * @param string $encoded
     * @param int $start
     * @param int $encoded_pos
     * @return array
     * @access private
     */
    function _decode_ber($encoded, $start = 0, $encoded_pos = 0)
    {
        $current = array('start' => $start);

        $type = ord($encoded[$encoded_pos++]);
        $start++;

        $constructed = ($type >> 5) & 1;

        $tag = $type & 0x1F;
        if ($tag == 0x1F) {
            $tag = 0;
            // process septets (since the eighth bit is ignored, it's not an octet)
            do {
                $loop = ord($encoded[0]) >> 7;
                $tag <<= 7;
                $tag |= ord($encoded[$encoded_pos++]) & 0x7F;
                $start++;
            } while ($loop);
        }

        // Length, as discussed in paragraph 8.1.3 of X.690-0207.pdf#page=13
        $length = ord($encoded[$encoded_pos++]);
        $start++;
        if ($length == 0x80) { // indefinite length
            // "[A sender shall] use the indefinite form (see 8.1.3.6) if the encoding is constructed and is not all
            //  immediately available." -- paragraph 8.1.3.2.c
            $length = strlen($encoded) - $encoded_pos;
        } elseif ($length & 0x80) { // definite length, long form
            // technically, the long form of the length can be represented by up to 126 octets (bytes), but we'll only
            // support it up to four.
            $length&= 0x7F;
            $temp = substr($encoded, $encoded_pos, $length);
            $encoded_pos += $length;
            // tags of indefinte length don't really have a header length; this length includes the tag
            $current+= array('headerlength' => $length + 2);
            $start+= $length;
            extract(unpack('Nlength', substr(str_pad($temp, 4, chr(0), STR_PAD_LEFT), -4)));
        } else {
            $current+= array('headerlength' => 2);
        }

        if ($length > (strlen($encoded) - $encoded_pos)) {
            return false;
        }

        $content = substr($encoded, $encoded_pos, $length);
        $content_pos = 0;

        // at this point $length can be overwritten. it's only accurate for definite length things as is

        /* Class is UNIVERSAL, APPLICATION, PRIVATE, or CONTEXT-SPECIFIC. The UNIVERSAL class is restricted to the ASN.1
           built-in types. It defines an application-independent data type that must be distinguishable from all other
           data types. The other three classes are user defined. The APPLICATION class distinguishes data types that
           have a wide, scattered use within a particular presentation context. PRIVATE distinguishes data types within
           a particular organization or country. CONTEXT-SPECIFIC distinguishes members of a sequence or set, the
           alternatives of a CHOICE, or universally tagged set members. Only the class number appears in braces for this
           data type; the term CONTEXT-SPECIFIC does not appear.

             -- http://www.obj-sys.com/asn1tutorial/node12.html */
        $class = ($type >> 6) & 3;
        switch ($class) {
            case FILE_ASN1_CLASS_APPLICATION:
            case FILE_ASN1_CLASS_PRIVATE:
            case FILE_ASN1_CLASS_CONTEXT_SPECIFIC:
                if (!$constructed) {
                    return array(
                        'type'     => $class,
                        'constant' => $tag,
                        'content'  => $content,
                        'length'   => $length + $start - $current['start']
                    );
                }

                $newcontent = array();
                $remainingLength = $length;
                while ($remainingLength > 0) {
                    $temp = $this->_decode_ber($content, $start, $content_pos);
                    if ($temp === false) {
                        break;
                    }
                    $length = $temp['length'];
                    // end-of-content octets - see paragraph 8.1.5
                    if (substr($content, $content_pos + $length, 2) == "\0\0") {
                        $length+= 2;
                        $start+= $length;
                        $newcontent[] = $temp;
                        break;
                    }
                    $start+= $length;
                    $remainingLength-= $length;
                    $newcontent[] = $temp;
                    $content_pos += $length;
                }

                return array(
                    'type'     => $class,
                    'constant' => $tag,
                    // the array encapsulation is for BC with the old format
                    'content'  => $newcontent,
                    // the only time when $content['headerlength'] isn't defined is when the length is indefinite.
                    // the absence of $content['headerlength'] is how we know if something is indefinite or not.
                    // technically, it could be defined to be 2 and then another indicator could be used but whatever.
                    'length'   => $start - $current['start']
                ) + $current;
        }

        $current+= array('type' => $tag);

        // decode UNIVERSAL tags
        switch ($tag) {
            case FILE_ASN1_TYPE_BOOLEAN:
                // "The contents octets shall consist of a single octet." -- paragraph 8.2.1
                //if (strlen($content) != 1) {
                //    return false;
                //}
                $current['content'] = (bool) ord($content[$content_pos]);
                break;
            case FILE_ASN1_TYPE_INTEGER:
            case FILE_ASN1_TYPE_ENUMERATED:
                $current['content'] = new Math_BigInteger(substr($content, $content_pos), -256);
                break;
            case FILE_ASN1_TYPE_REAL: // not currently supported
                return false;
            case FILE_ASN1_TYPE_BIT_STRING:
                // The initial octet shall encode, as an unsigned binary integer with bit 1 as the least significant bit,
                // the number of unused bits in the final subsequent octet. The number shall be in the range zero to
                // seven.
                if (!$constructed) {
                    $current['content'] = substr($content, $content_pos);
                } else {
                    $temp = $this->_decode_ber($content, $start, $content_pos);
                    if ($temp === false) {
                        return false;
                    }
                    $length-= (strlen($content) - $content_pos);
                    $last = count($temp) - 1;
                    for ($i = 0; $i < $last; $i++) {
                        // all subtags should be bit strings
                        //if ($temp[$i]['type'] != FILE_ASN1_TYPE_BIT_STRING) {
                        //    return false;
                        //}
                        $current['content'].= substr($temp[$i]['content'], 1);
                    }
                    // all subtags should be bit strings
                    //if ($temp[$last]['type'] != FILE_ASN1_TYPE_BIT_STRING) {
                    //    return false;
                    //}
                    $current['content'] = $temp[$last]['content'][0] . $current['content'] . substr($temp[$i]['content'], 1);
                }
                break;
            case FILE_ASN1_TYPE_OCTET_STRING:
                if (!$constructed) {
                    $current['content'] = substr($content, $content_pos);
                } else {
                    $current['content'] = '';
                    $length = 0;
                    while (substr($content, $content_pos, 2) != "\0\0") {
                        $temp = $this->_decode_ber($content, $length + $start, $content_pos);
                        if ($temp === false) {
                            return false;
                        }
                        $content_pos += $temp['length'];
                        // all subtags should be octet strings
                        //if ($temp['type'] != FILE_ASN1_TYPE_OCTET_STRING) {
                        //    return false;
                        //}
                        $current['content'].= $temp['content'];
                        $length+= $temp['length'];
                    }
                    if (substr($content, $content_pos, 2) == "\0\0") {
                        $length+= 2; // +2 for the EOC
                    }
                }
                break;
            case FILE_ASN1_TYPE_NULL:
                // "The contents octets shall not contain any octets." -- paragraph 8.8.2
                //if (strlen($content)) {
                //    return false;
                //}
                break;
            case FILE_ASN1_TYPE_SEQUENCE:
            case FILE_ASN1_TYPE_SET:
                $offset = 0;
                $current['content'] = array();
                $content_len = strlen($content);
                while ($content_pos < $content_len) {
                    // if indefinite length construction was used and we have an end-of-content string next
                    // see paragraphs 8.1.1.3, 8.1.3.2, 8.1.3.6, 8.1.5, and (for an example) 8.6.4.2
                    if (!isset($current['headerlength']) && substr($content, $content_pos, 2) == "\0\0") {
                        $length = $offset + 2; // +2 for the EOC
                        break 2;
                    }
                    $temp = $this->_decode_ber($content, $start + $offset, $content_pos);
                    if ($temp === false) {
                        return false;
                    }
                    $content_pos += $temp['length'];
                    $current['content'][] = $temp;
                    $offset+= $temp['length'];
                }
                break;
            case FILE_ASN1_TYPE_OBJECT_IDENTIFIER:
                $temp = ord($content[$content_pos++]);
                $current['content'] = sprintf('%d.%d', floor($temp / 40), $temp % 40);
                $valuen = 0;
                // process septets
                $content_len = strlen($content);
                while ($content_pos < $content_len) {
                    $temp = ord($content[$content_pos++]);
                    $valuen <<= 7;
                    $valuen |= $temp & 0x7F;
                    if (~$temp & 0x80) {
                        $current['content'].= ".$valuen";
                        $valuen = 0;
                    }
                }
                // the eighth bit of the last byte should not be 1
                //if ($temp >> 7) {
                //    return false;
                //}
                break;
            /* Each character string type shall be encoded as if it had been declared:
               [UNIVERSAL x] IMPLICIT OCTET STRING

                 -- X.690-0207.pdf#page=23 (paragraph 8.21.3)

               Per that, we're not going to do any validation.  If there are any illegal characters in the string,
               we don't really care */
            case FILE_ASN1_TYPE_NUMERIC_STRING:
                // 0,1,2,3,4,5,6,7,8,9, and space
            case FILE_ASN1_TYPE_PRINTABLE_STRING:
                // Upper and lower case letters, digits, space, apostrophe, left/right parenthesis, plus sign, comma,
                // hyphen, full stop, solidus, colon, equal sign, question mark
            case FILE_ASN1_TYPE_TELETEX_STRING:
                // The Teletex character set in CCITT's T61, space, and delete
                // see http://en.wikipedia.org/wiki/Teletex#Character_sets
            case FILE_ASN1_TYPE_VIDEOTEX_STRING:
                // The Videotex character set in CCITT's T.100 and T.101, space, and delete
            case FILE_ASN1_TYPE_VISIBLE_STRING:
                // Printing character sets of international ASCII, and space
            case FILE_ASN1_TYPE_IA5_STRING:
                // International Alphabet 5 (International ASCII)
            case FILE_ASN1_TYPE_GRAPHIC_STRING:
                // All registered G sets, and space
            case FILE_ASN1_TYPE_GENERAL_STRING:
                // All registered C and G sets, space and delete
            case FILE_ASN1_TYPE_UTF8_STRING:
                // ????
            case FILE_ASN1_TYPE_BMP_STRING:
                $current['content'] = substr($content, $content_pos);
                break;
            case FILE_ASN1_TYPE_UTC_TIME:
            case FILE_ASN1_TYPE_GENERALIZED_TIME:
                $current['content'] = class_exists('DateTime') ?
                    $this->_decodeDateTime(substr($content, $content_pos), $tag) :
                    $this->_decodeUnixTime(substr($content, $content_pos), $tag);
            default:
        }

        $start+= $length;

        // ie. length is the length of the full TLV encoding - it's not just the length of the value
        return $current + array('length' => $start - $current['start']);
    }

    /**
     * ASN.1 Map
     *
     * Provides an ASN.1 semantic mapping ($mapping) from a parsed BER-encoding to a human readable format.
     *
     * "Special" mappings may be applied on a per tag-name basis via $special.
     *
     * @param array $decoded
     * @param array $mapping
     * @param array $special
     * @return array
     * @access public
     */
    function asn1map($decoded, $mapping, $special = array())
    {
        if (isset($mapping['explicit']) && is_array($decoded['content'])) {
            $decoded = $decoded['content'][0];
        }

        switch (true) {
            case $mapping['type'] == FILE_ASN1_TYPE_ANY:
                $intype = $decoded['type'];
                if (isset($decoded['constant']) || !isset($this->ANYmap[$intype]) || (ord($this->encoded[$decoded['start']]) & 0x20)) {
                    return new File_ASN1_Element(substr($this->encoded, $decoded['start'], $decoded['length']));
                }
                $inmap = $this->ANYmap[$intype];
                if (is_string($inmap)) {
                    return array($inmap => $this->asn1map($decoded, array('type' => $intype) + $mapping, $special));
                }
                break;
            case $mapping['type'] == FILE_ASN1_TYPE_CHOICE:
                foreach ($mapping['children'] as $key => $option) {
                    switch (true) {
                        case isset($option['constant']) && $option['constant'] == $decoded['constant']:
                        case !isset($option['constant']) && $option['type'] == $decoded['type']:
                            $value = $this->asn1map($decoded, $option, $special);
                            break;
                        case !isset($option['constant']) && $option['type'] == FILE_ASN1_TYPE_CHOICE:
                            $v = $this->asn1map($decoded, $option, $special);
                            if (isset($v)) {
                                $value = $v;
                            }
                    }
                    if (isset($value)) {
                        if (isset($special[$key])) {
                            $value = call_user_func($special[$key], $value);
                        }
                        return array($key => $value);
                    }
                }
                return null;
            case isset($mapping['implicit']):
            case isset($mapping['explicit']):
            case $decoded['type'] == $mapping['type']:
                break;
            default:
                // if $decoded['type'] and $mapping['type'] are both strings, but different types of strings,
                // let it through
                switch (true) {
                    case $decoded['type'] < 18: // FILE_ASN1_TYPE_NUMERIC_STRING == 18
                    case $decoded['type'] > 30: // FILE_ASN1_TYPE_BMP_STRING == 30
                    case $mapping['type'] < 18:
                    case $mapping['type'] > 30:
                        return null;
                }
        }

        if (isset($mapping['implicit'])) {
            $decoded['type'] = $mapping['type'];
        }

        switch ($decoded['type']) {
            case FILE_ASN1_TYPE_SEQUENCE:
                $map = array();

                // ignore the min and max
                if (isset($mapping['min']) && isset($mapping['max'])) {
                    $child = $mapping['children'];
                    foreach ($decoded['content'] as $content) {
                        if (($map[] = $this->asn1map($content, $child, $special)) === null) {
                            return null;
                        }
                    }

                    return $map;
                }

                $n = count($decoded['content']);
                $i = 0;

                foreach ($mapping['children'] as $key => $child) {
                    $maymatch = $i < $n; // Match only existing input.
                    if ($maymatch) {
                        $temp = $decoded['content'][$i];

                        if ($child['type'] != FILE_ASN1_TYPE_CHOICE) {
                            // Get the mapping and input class & constant.
                            $childClass = $tempClass = FILE_ASN1_CLASS_UNIVERSAL;
                            $constant = null;
                            if (isset($temp['constant'])) {
                                $tempClass = $temp['type'];
                            }
                            if (isset($child['class'])) {
                                $childClass = $child['class'];
                                $constant = $child['cast'];
                            } elseif (isset($child['constant'])) {
                                $childClass = FILE_ASN1_CLASS_CONTEXT_SPECIFIC;
                                $constant = $child['constant'];
                            }

                            if (isset($constant) && isset($temp['constant'])) {
                                // Can only match if constants and class match.
                                $maymatch = $constant == $temp['constant'] && $childClass == $tempClass;
                            } else {
                                // Can only match if no constant expected and type matches or is generic.
                                $maymatch = !isset($child['constant']) && array_search($child['type'], array($temp['type'], FILE_ASN1_TYPE_ANY, FILE_ASN1_TYPE_CHOICE)) !== false;
                            }
                        }
                    }

                    if ($maymatch) {
                        // Attempt submapping.
                        $candidate = $this->asn1map($temp, $child, $special);
                        $maymatch = $candidate !== null;
                    }

                    if ($maymatch) {
                        // Got the match: use it.
                        if (isset($special[$key])) {
                            $candidate = call_user_func($special[$key], $candidate);
                        }
                        $map[$key] = $candidate;
                        $i++;
                    } elseif (isset($child['default'])) {
                        $map[$key] = $child['default']; // Use default.
                    } elseif (!isset($child['optional'])) {
                        return null; // Syntax error.
                    }
                }

                // Fail mapping if all input items have not been consumed.
                return $i < $n ? null: $map;

            // the main diff between sets and sequences is the encapsulation of the foreach in another for loop
            case FILE_ASN1_TYPE_SET:
                $map = array();

                // ignore the min and max
                if (isset($mapping['min']) && isset($mapping['max'])) {
                    $child = $mapping['children'];
                    foreach ($decoded['content'] as $content) {
                        if (($map[] = $this->asn1map($content, $child, $special)) === null) {
                            return null;
                        }
                    }

                    return $map;
                }

                for ($i = 0; $i < count($decoded['content']); $i++) {
                    $temp = $decoded['content'][$i];
                    $tempClass = FILE_ASN1_CLASS_UNIVERSAL;
                    if (isset($temp['constant'])) {
                        $tempClass = $temp['type'];
                    }

                    foreach ($mapping['children'] as $key => $child) {
                        if (isset($map[$key])) {
                            continue;
                        }
                        $maymatch = true;
                        if ($child['type'] != FILE_ASN1_TYPE_CHOICE) {
                            $childClass = FILE_ASN1_CLASS_UNIVERSAL;
                            $constant = null;
                            if (isset($child['class'])) {
                                $childClass = $child['class'];
                                $constant = $child['cast'];
                            } elseif (isset($child['constant'])) {
                                $childClass = FILE_ASN1_CLASS_CONTEXT_SPECIFIC;
                                $constant = $child['constant'];
                            }

                            if (isset($constant) && isset($temp['constant'])) {
                                // Can only match if constants and class match.
                                $maymatch = $constant == $temp['constant'] && $childClass == $tempClass;
                            } else {
                                // Can only match if no constant expected and type matches or is generic.
                                $maymatch = !isset($child['constant']) && array_search($child['type'], array($temp['type'], FILE_ASN1_TYPE_ANY, FILE_ASN1_TYPE_CHOICE)) !== false;
                            }
                        }

                        if ($maymatch) {
                            // Attempt submapping.
                            $candidate = $this->asn1map($temp, $child, $special);
                            $maymatch = $candidate !== null;
                        }

                        if (!$maymatch) {
                            break;
                        }

                        // Got the match: use it.
                        if (isset($special[$key])) {
                            $candidate = call_user_func($special[$key], $candidate);
                        }
                        $map[$key] = $candidate;
                        break;
                    }
                }

                foreach ($mapping['children'] as $key => $child) {
                    if (!isset($map[$key])) {
                        if (isset($child['default'])) {
                            $map[$key] = $child['default'];
                        } elseif (!isset($child['optional'])) {
                            return null;
                        }
                    }
                }
                return $map;
            case FILE_ASN1_TYPE_OBJECT_IDENTIFIER:
                return isset($this->oids[$decoded['content']]) ? $this->oids[$decoded['content']] : $decoded['content'];
            case FILE_ASN1_TYPE_UTC_TIME:
            case FILE_ASN1_TYPE_GENERALIZED_TIME:
                if (class_exists('DateTime')) {
                    if (isset($mapping['implicit'])) {
                        $decoded['content'] = $this->_decodeDateTime($decoded['content'], $decoded['type']);
                    }
                    if (!$decoded['content']) {
                        return false;
                    }
                    return $decoded['content']->format($this->format);
                } else {
                    if (isset($mapping['implicit'])) {
                        $decoded['content'] = $this->_decodeUnixTime($decoded['content'], $decoded['type']);
                    }
                    return @date($this->format, $decoded['content']);
                }
            case FILE_ASN1_TYPE_BIT_STRING:
                if (isset($mapping['mapping'])) {
                    $offset = ord($decoded['content'][0]);
                    $size = (strlen($decoded['content']) - 1) * 8 - $offset;
                    /*
                       From X.680-0207.pdf#page=46 (21.7):

                       "When a "NamedBitList" is used in defining a bitstring type ASN.1 encoding rules are free to add (or remove)
                        arbitrarily any trailing 0 bits to (or from) values that are being encoded or decoded. Application designers should
                        therefore ensure that different semantics are not associated with such values which differ only in the number of trailing
                        0 bits."
                    */
                    $bits = count($mapping['mapping']) == $size ? array() : array_fill(0, count($mapping['mapping']) - $size, false);
                    for ($i = strlen($decoded['content']) - 1; $i > 0; $i--) {
                        $current = ord($decoded['content'][$i]);
                        for ($j = $offset; $j < 8; $j++) {
                            $bits[] = (bool) ($current & (1 << $j));
                        }
                        $offset = 0;
                    }
                    $values = array();
                    $map = array_reverse($mapping['mapping']);
                    foreach ($map as $i => $value) {
                        if ($bits[$i]) {
                            $values[] = $value;
                        }
                    }
                    return $values;
                }
            case FILE_ASN1_TYPE_OCTET_STRING:
                return base64_encode($decoded['content']);
            case FILE_ASN1_TYPE_NULL:
                return '';
            case FILE_ASN1_TYPE_BOOLEAN:
                return $decoded['content'];
            case FILE_ASN1_TYPE_NUMERIC_STRING:
            case FILE_ASN1_TYPE_PRINTABLE_STRING:
            case FILE_ASN1_TYPE_TELETEX_STRING:
            case FILE_ASN1_TYPE_VIDEOTEX_STRING:
            case FILE_ASN1_TYPE_IA5_STRING:
            case FILE_ASN1_TYPE_GRAPHIC_STRING:
            case FILE_ASN1_TYPE_VISIBLE_STRING:
            case FILE_ASN1_TYPE_GENERAL_STRING:
            case FILE_ASN1_TYPE_UNIVERSAL_STRING:
            case FILE_ASN1_TYPE_UTF8_STRING:
            case FILE_ASN1_TYPE_BMP_STRING:
                return $decoded['content'];
            case FILE_ASN1_TYPE_INTEGER:
            case FILE_ASN1_TYPE_ENUMERATED:
                $temp = $decoded['content'];
                if (isset($mapping['implicit'])) {
                    $temp = new Math_BigInteger($decoded['content'], -256);
                }
                if (isset($mapping['mapping'])) {
                    $temp = (int) $temp->toString();
                    return isset($mapping['mapping'][$temp]) ?
                        $mapping['mapping'][$temp] :
                        false;
                }
                return $temp;
        }
    }

    /**
     * ASN.1 Encode
     *
     * DER-encodes an ASN.1 semantic mapping ($mapping).  Some libraries would probably call this function
     * an ASN.1 compiler.
     *
     * "Special" mappings can be applied via $special.
     *
     * @param string $source
     * @param string $mapping
     * @param int $idx
     * @return string
     * @access public
     */
    function encodeDER($source, $mapping, $special = array())
    {
        $this->location = array();
        return $this->_encode_der($source, $mapping, null, $special);
    }

    /**
     * ASN.1 Encode (Helper function)
     *
     * @param string $source
     * @param string $mapping
     * @param int $idx
     * @return string
     * @access private
     */
    function _encode_der($source, $mapping, $idx = null, $special = array())
    {
        if (is_object($source) && strtolower(get_class($source)) == 'file_asn1_element') {
            return $source->element;
        }

        // do not encode (implicitly optional) fields with value set to default
        if (isset($mapping['default']) && $source === $mapping['default']) {
            return '';
        }

        if (isset($idx)) {
            if (isset($special[$idx])) {
                $source = call_user_func($special[$idx], $source);
            }
            $this->location[] = $idx;
        }

        $tag = $mapping['type'];

        switch ($tag) {
            case FILE_ASN1_TYPE_SET:    // Children order is not important, thus process in sequence.
            case FILE_ASN1_TYPE_SEQUENCE:
                $tag|= 0x20; // set the constructed bit

                // ignore the min and max
                if (isset($mapping['min']) && isset($mapping['max'])) {
                    $value = array();
                    $child = $mapping['children'];

                    foreach ($source as $content) {
                        $temp = $this->_encode_der($content, $child, null, $special);
                        if ($temp === false) {
                            return false;
                        }
                        $value[]= $temp;
                    }
                    /* "The encodings of the component values of a set-of value shall appear in ascending order, the encodings being compared
                        as octet strings with the shorter components being padded at their trailing end with 0-octets.
                        NOTE - The padding octets are for comparison purposes only and do not appear in the encodings."

                       -- sec 11.6 of http://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf  */
                    if ($mapping['type'] == FILE_ASN1_TYPE_SET) {
                        sort($value);
                    }
                    $value = implode($value, '');
                    break;
                }

                $value = '';
                foreach ($mapping['children'] as $key => $child) {
                    if (!array_key_exists($key, $source)) {
                        if (!isset($child['optional'])) {
                            return false;
                        }
                        continue;
                    }

                    $temp = $this->_encode_der($source[$key], $child, $key, $special);
                    if ($temp === false) {
                        return false;
                    }

                    // An empty child encoding means it has been optimized out.
                    // Else we should have at least one tag byte.
                    if ($temp === '') {
                        continue;
                    }

                    // if isset($child['constant']) is true then isset($child['optional']) should be true as well
                    if (isset($child['constant'])) {
                        /*
                           From X.680-0207.pdf#page=58 (30.6):

                           "The tagging construction specifies explicit tagging if any of the following holds:
                            ...
                            c) the "Tag Type" alternative is used and the value of "TagDefault" for the module is IMPLICIT TAGS or
                            AUTOMATIC TAGS, but the type defined by "Type" is an untagged choice type, an untagged open type, or
                            an untagged "DummyReference" (see ITU-T Rec. X.683 | ISO/IEC 8824-4, 8.3)."
                         */
                        if (isset($child['explicit']) || $child['type'] == FILE_ASN1_TYPE_CHOICE) {
                            $subtag = chr((FILE_ASN1_CLASS_CONTEXT_SPECIFIC << 6) | 0x20 | $child['constant']);
                            $temp = $subtag . $this->_encodeLength(strlen($temp)) . $temp;
                        } else {
                            $subtag = chr((FILE_ASN1_CLASS_CONTEXT_SPECIFIC << 6) | (ord($temp[0]) & 0x20) | $child['constant']);
                            $temp = $subtag . substr($temp, 1);
                        }
                    }
                    $value.= $temp;
                }
                break;
            case FILE_ASN1_TYPE_CHOICE:
                $temp = false;

                foreach ($mapping['children'] as $key => $child) {
                    if (!isset($source[$key])) {
                        continue;
                    }

                    $temp = $this->_encode_der($source[$key], $child, $key, $special);
                    if ($temp === false) {
                        return false;
                    }

                    // An empty child encoding means it has been optimized out.
                    // Else we should have at least one tag byte.
                    if ($temp === '') {
                        continue;
                    }

                    $tag = ord($temp[0]);

                    // if isset($child['constant']) is true then isset($child['optional']) should be true as well
                    if (isset($child['constant'])) {
                        if (isset($child['explicit']) || $child['type'] == FILE_ASN1_TYPE_CHOICE) {
                            $subtag = chr((FILE_ASN1_CLASS_CONTEXT_SPECIFIC << 6) | 0x20 | $child['constant']);
                            $temp = $subtag . $this->_encodeLength(strlen($temp)) . $temp;
                        } else {
                            $subtag = chr((FILE_ASN1_CLASS_CONTEXT_SPECIFIC << 6) | (ord($temp[0]) & 0x20) | $child['constant']);
                            $temp = $subtag . substr($temp, 1);
                        }
                    }
                }

                if (isset($idx)) {
                    array_pop($this->location);
                }

                if ($temp && isset($mapping['cast'])) {
                    $temp[0] = chr(($mapping['class'] << 6) | ($tag & 0x20) | $mapping['cast']);
                }

                return $temp;
            case FILE_ASN1_TYPE_INTEGER:
            case FILE_ASN1_TYPE_ENUMERATED:
                if (!isset($mapping['mapping'])) {
                    if (is_numeric($source)) {
                        $source = new Math_BigInteger($source);
                    }
                    $value = $source->toBytes(true);
                } else {
                    $value = array_search($source, $mapping['mapping']);
                    if ($value === false) {
                        return false;
                    }
                    $value = new Math_BigInteger($value);
                    $value = $value->toBytes(true);
                }
                if (!strlen($value)) {
                    $value = chr(0);
                }
                break;
            case FILE_ASN1_TYPE_UTC_TIME:
            case FILE_ASN1_TYPE_GENERALIZED_TIME:
                $format = $mapping['type'] == FILE_ASN1_TYPE_UTC_TIME ? 'y' : 'Y';
                $format.= 'mdHis';
                if (!class_exists('DateTime')) {
                    $value = @gmdate($format, strtotime($source)) . 'Z';
                } else {
                    $date = new DateTime($source, new DateTimeZone('GMT'));
                    $value = $date->format($format) . 'Z';
                }
                break;
            case FILE_ASN1_TYPE_BIT_STRING:
                if (isset($mapping['mapping'])) {
                    $bits = array_fill(0, count($mapping['mapping']), 0);
                    $size = 0;
                    for ($i = 0; $i < count($mapping['mapping']); $i++) {
                        if (in_array($mapping['mapping'][$i], $source)) {
                            $bits[$i] = 1;
                            $size = $i;
                        }
                    }

                    if (isset($mapping['min']) && $mapping['min'] >= 1 && $size < $mapping['min']) {
                        $size = $mapping['min'] - 1;
                    }

                    $offset = 8 - (($size + 1) & 7);
                    $offset = $offset !== 8 ? $offset : 0;

                    $value = chr($offset);

                    for ($i = $size + 1; $i < count($mapping['mapping']); $i++) {
                        unset($bits[$i]);
                    }

                    $bits = implode('', array_pad($bits, $size + $offset + 1, 0));
                    $bytes = explode(' ', rtrim(chunk_split($bits, 8, ' ')));
                    foreach ($bytes as $byte) {
                        $value.= chr(bindec($byte));
                    }

                    break;
                }
            case FILE_ASN1_TYPE_OCTET_STRING:
                /* The initial octet shall encode, as an unsigned binary integer with bit 1 as the least significant bit,
                   the number of unused bits in the final subsequent octet. The number shall be in the range zero to seven.

                   -- http://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#page=16 */
                $value = base64_decode($source);
                break;
            case FILE_ASN1_TYPE_OBJECT_IDENTIFIER:
                $oid = preg_match('#(?:\d+\.)+#', $source) ? $source : array_search($source, $this->oids);
                if ($oid === false) {
                    user_error('Invalid OID');
                    return false;
                }
                $value = '';
                $parts = explode('.', $oid);
                $value = chr(40 * $parts[0] + $parts[1]);
                for ($i = 2; $i < count($parts); $i++) {
                    $temp = '';
                    if (!$parts[$i]) {
                        $temp = "\0";
                    } else {
                        while ($parts[$i]) {
                            $temp = chr(0x80 | ($parts[$i] & 0x7F)) . $temp;
                            $parts[$i] >>= 7;
                        }
                        $temp[strlen($temp) - 1] = $temp[strlen($temp) - 1] & chr(0x7F);
                    }
                    $value.= $temp;
                }
                break;
            case FILE_ASN1_TYPE_ANY:
                $loc = $this->location;
                if (isset($idx)) {
                    array_pop($this->location);
                }

                switch (true) {
                    case !isset($source):
                        return $this->_encode_der(null, array('type' => FILE_ASN1_TYPE_NULL) + $mapping, null, $special);
                    case is_int($source):
                    case is_object($source) && strtolower(get_class($source)) == 'math_biginteger':
                        return $this->_encode_der($source, array('type' => FILE_ASN1_TYPE_INTEGER) + $mapping, null, $special);
                    case is_float($source):
                        return $this->_encode_der($source, array('type' => FILE_ASN1_TYPE_REAL) + $mapping, null, $special);
                    case is_bool($source):
                        return $this->_encode_der($source, array('type' => FILE_ASN1_TYPE_BOOLEAN) + $mapping, null, $special);
                    case is_array($source) && count($source) == 1:
                        $typename = implode('', array_keys($source));
                        $outtype = array_search($typename, $this->ANYmap, true);
                        if ($outtype !== false) {
                            return $this->_encode_der($source[$typename], array('type' => $outtype) + $mapping, null, $special);
                        }
                }

                $filters = $this->filters;
                foreach ($loc as $part) {
                    if (!isset($filters[$part])) {
                        $filters = false;
                        break;
                    }
                    $filters = $filters[$part];
                }
                if ($filters === false) {
                    user_error('No filters defined for ' . implode('/', $loc));
                    return false;
                }
                return $this->_encode_der($source, $filters + $mapping, null, $special);
            case FILE_ASN1_TYPE_NULL:
                $value = '';
                break;
            case FILE_ASN1_TYPE_NUMERIC_STRING:
            case FILE_ASN1_TYPE_TELETEX_STRING:
            case FILE_ASN1_TYPE_PRINTABLE_STRING:
            case FILE_ASN1_TYPE_UNIVERSAL_STRING:
            case FILE_ASN1_TYPE_UTF8_STRING:
            case FILE_ASN1_TYPE_BMP_STRING:
            case FILE_ASN1_TYPE_IA5_STRING:
            case FILE_ASN1_TYPE_VISIBLE_STRING:
            case FILE_ASN1_TYPE_VIDEOTEX_STRING:
            case FILE_ASN1_TYPE_GRAPHIC_STRING:
            case FILE_ASN1_TYPE_GENERAL_STRING:
                $value = $source;
                break;
            case FILE_ASN1_TYPE_BOOLEAN:
                $value = $source ? "\xFF" : "\x00";
                break;
            default:
                user_error('Mapping provides no type definition for ' . implode('/', $this->location));
                return false;
        }

        if (isset($idx)) {
            array_pop($this->location);
        }

        if (isset($mapping['cast'])) {
            if (isset($mapping['explicit']) || $mapping['type'] == FILE_ASN1_TYPE_CHOICE) {
                $value = chr($tag) . $this->_encodeLength(strlen($value)) . $value;
                $tag = ($mapping['class'] << 6) | 0x20 | $mapping['cast'];
            } else {
                $tag = ($mapping['class'] << 6) | (ord($temp[0]) & 0x20) | $mapping['cast'];
            }
        }

        return chr($tag) . $this->_encodeLength(strlen($value)) . $value;
    }

    /**
     * DER-encode the length
     *
     * DER supports lengths up to (2**8)**127, however, we'll only support lengths up to (2**8)**4.  See
     * {@link http://itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#p=13 X.690 paragraph 8.1.3} for more information.
     *
     * @access private
     * @param int $length
     * @return string
     */
    function _encodeLength($length)
    {
        if ($length <= 0x7F) {
            return chr($length);
        }

        $temp = ltrim(pack('N', $length), chr(0));
        return pack('Ca*', 0x80 | strlen($temp), $temp);
    }

    /**
     * BER-decode the time (using UNIX time)
     *
     * Called by _decode_ber() and in the case of implicit tags asn1map().
     *
     * @access private
     * @param string $content
     * @param int $tag
     * @return string
     */
    function _decodeUnixTime($content, $tag)
    {
        /* UTCTime:
           http://tools.ietf.org/html/rfc5280#section-4.1.2.5.1
           http://www.obj-sys.com/asn1tutorial/node15.html

           GeneralizedTime:
           http://tools.ietf.org/html/rfc5280#section-4.1.2.5.2
           http://www.obj-sys.com/asn1tutorial/node14.html */

        $pattern = $tag == FILE_ASN1_TYPE_UTC_TIME ?
            '#^(..)(..)(..)(..)(..)(..)?(.*)$#' :
            '#(....)(..)(..)(..)(..)(..).*([Z+-].*)$#';

        preg_match($pattern, $content, $matches);

        list(, $year, $month, $day, $hour, $minute, $second, $timezone) = $matches;

        if ($tag == FILE_ASN1_TYPE_UTC_TIME) {
            $year = $year >= 50 ? "19$year" : "20$year";
        }

        if ($timezone == 'Z') {
            $mktime = 'gmmktime';
            $timezone = 0;
        } elseif (preg_match('#([+-])(\d\d)(\d\d)#', $timezone, $matches)) {
            $mktime = 'gmmktime';
            $timezone = 60 * $matches[3] + 3600 * $matches[2];
            if ($matches[1] == '-') {
                $timezone = -$timezone;
            }
        } else {
            $mktime = 'mktime';
            $timezone = 0;
        }

        return @$mktime((int)$hour, (int)$minute, (int)$second, (int)$month, (int)$day, (int)$year) + $timezone;
    }


    /**
     * BER-decode the time (using DateTime)
     *
     * Called by _decode_ber() and in the case of implicit tags asn1map().
     *
     * @access private
     * @param string $content
     * @param int $tag
     * @return string
     */
    function _decodeDateTime($content, $tag)
    {
        /* UTCTime:
           http://tools.ietf.org/html/rfc5280#section-4.1.2.5.1
           http://www.obj-sys.com/asn1tutorial/node15.html

           GeneralizedTime:
           http://tools.ietf.org/html/rfc5280#section-4.1.2.5.2
           http://www.obj-sys.com/asn1tutorial/node14.html */

        $format = 'YmdHis';

        if ($tag == FILE_ASN1_TYPE_UTC_TIME) {
            // https://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#page=28 says "the seconds
            // element shall always be present" but none-the-less I've seen X509 certs where it isn't and if the
            // browsers parse it phpseclib ought to too
            if (preg_match('#^(\d{10})(Z|[+-]\d{4})$#', $content, $matches)) {
                $content = $matches[1] . '00' . $matches[2];
            }
            $prefix = substr($content, 0, 2) >= 50 ? '19' : '20';
            $content = $prefix . $content;
        } elseif (strpos($content, '.') !== false) {
            $format.= '.u';
        }

        if ($content[strlen($content) - 1] == 'Z') {
            $content = substr($content, 0, -1) . '+0000';
        }

        if (strpos($content, '-') !== false || strpos($content, '+') !== false) {
            $format.= 'O';
        }

        // error supression isn't necessary as of PHP 7.0:
        // http://php.net/manual/en/migration70.other-changes.php
        return @DateTime::createFromFormat($format, $content);
    }

    /**
     * Set the time format
     *
     * Sets the time / date format for asn1map().
     *
     * @access public
     * @param string $format
     */
    function setTimeFormat($format)
    {
        $this->format = $format;
    }

    /**
     * Load OIDs
     *
     * Load the relevant OIDs for a particular ASN.1 semantic mapping.
     *
     * @access public
     * @param array $oids
     */
    function loadOIDs($oids)
    {
        $this->oids = $oids;
    }

    /**
     * Load filters
     *
     * See File_X509, etc, for an example.
     *
     * @access public
     * @param array $filters
     */
    function loadFilters($filters)
    {
        $this->filters = $filters;
    }

    /**
     * String Shift
     *
     * Inspired by array_shift
     *
     * @param string $string
     * @param int $index
     * @return string
     * @access private
     */
    function _string_shift(&$string, $index = 1)
    {
        $substr = substr($string, 0, $index);
        $string = substr($string, $index);
        return $substr;
    }

    /**
     * String type conversion
     *
     * This is a lazy conversion, dealing only with character size.
     * No real conversion table is used.
     *
     * @param string $in
     * @param int $from
     * @param int $to
     * @return string
     * @access public
     */
    function convert($in, $from = FILE_ASN1_TYPE_UTF8_STRING, $to = FILE_ASN1_TYPE_UTF8_STRING)
    {
        if (!isset($this->stringTypeSize[$from]) || !isset($this->stringTypeSize[$to])) {
            return false;
        }
        $insize = $this->stringTypeSize[$from];
        $outsize = $this->stringTypeSize[$to];
        $inlength = strlen($in);
        $out = '';

        for ($i = 0; $i < $inlength;) {
            if ($inlength - $i < $insize) {
                return false;
            }

            // Get an input character as a 32-bit value.
            $c = ord($in[$i++]);
            switch (true) {
                case $insize == 4:
                    $c = ($c << 8) | ord($in[$i++]);
                    $c = ($c << 8) | ord($in[$i++]);
                case $insize == 2:
                    $c = ($c << 8) | ord($in[$i++]);
                case $insize == 1:
                    break;
                case ($c & 0x80) == 0x00:
                    break;
                case ($c & 0x40) == 0x00:
                    return false;
                default:
                    $bit = 6;
                    do {
                        if ($bit > 25 || $i >= $inlength || (ord($in[$i]) & 0xC0) != 0x80) {
                            return false;
                        }
                        $c = ($c << 6) | (ord($in[$i++]) & 0x3F);
                        $bit += 5;
                        $mask = 1 << $bit;
                    } while ($c & $bit);
                    $c &= $mask - 1;
                    break;
            }

            // Convert and append the character to output string.
            $v = '';
            switch (true) {
                case $outsize == 4:
                    $v .= chr($c & 0xFF);
                    $c >>= 8;
                    $v .= chr($c & 0xFF);
                    $c >>= 8;
                case $outsize == 2:
                    $v .= chr($c & 0xFF);
                    $c >>= 8;
                case $outsize == 1:
                    $v .= chr($c & 0xFF);
                    $c >>= 8;
                    if ($c) {
                        return false;
                    }
                    break;
                case ($c & 0x80000000) != 0:
                    return false;
                case $c >= 0x04000000:
                    $v .= chr(0x80 | ($c & 0x3F));
                    $c = ($c >> 6) | 0x04000000;
                case $c >= 0x00200000:
                    $v .= chr(0x80 | ($c & 0x3F));
                    $c = ($c >> 6) | 0x00200000;
                case $c >= 0x00010000:
                    $v .= chr(0x80 | ($c & 0x3F));
                    $c = ($c >> 6) | 0x00010000;
                case $c >= 0x00000800:
                    $v .= chr(0x80 | ($c & 0x3F));
                    $c = ($c >> 6) | 0x00000800;
                case $c >= 0x00000080:
                    $v .= chr(0x80 | ($c & 0x3F));
                    $c = ($c >> 6) | 0x000000C0;
                default:
                    $v .= chr($c);
                    break;
            }
            $out .= strrev($v);
        }
        return $out;
    }
}
vendor/phpseclib/phpseclib/phpseclib/File/ANSI.php000064400000052006151327705700016070 0ustar00<?php

/**
 * Pure-PHP ANSI Decoder
 *
 * PHP versions 4 and 5
 *
 * If you call read() in Net_SSH2 you may get {@link http://en.wikipedia.org/wiki/ANSI_escape_code ANSI escape codes} back.
 * They'd look like chr(0x1B) . '[00m' or whatever (0x1B = ESC).  They tell a
 * {@link http://en.wikipedia.org/wiki/Terminal_emulator terminal emulator} how to format the characters, what
 * color to display them in, etc. File_ANSI is a {@link http://en.wikipedia.org/wiki/VT100 VT100} terminal emulator.
 *
 * LICENSE: Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 *
 * @category  File
 * @package   File_ANSI
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2012 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      http://phpseclib.sourceforge.net
 */

/**
 * Pure-PHP ANSI Decoder
 *
 * @package File_ANSI
 * @author  Jim Wigginton <terrafrost@php.net>
 * @access  public
 */
class File_ANSI
{
    /**
     * Max Width
     *
     * @var int
     * @access private
     */
    var $max_x;

    /**
     * Max Height
     *
     * @var int
     * @access private
     */
    var $max_y;

    /**
     * Max History
     *
     * @var int
     * @access private
     */
    var $max_history;

    /**
     * History
     *
     * @var array
     * @access private
     */
    var $history;

    /**
     * History Attributes
     *
     * @var array
     * @access private
     */
    var $history_attrs;

    /**
     * Current Column
     *
     * @var int
     * @access private
     */
    var $x;

    /**
     * Current Row
     *
     * @var int
     * @access private
     */
    var $y;

    /**
     * Old Column
     *
     * @var int
     * @access private
     */
    var $old_x;

    /**
     * Old Row
     *
     * @var int
     * @access private
     */
    var $old_y;

    /**
     * An empty attribute cell
     *
     * @var object
     * @access private
     */
    var $base_attr_cell;

    /**
     * The current attribute cell
     *
     * @var object
     * @access private
     */
    var $attr_cell;

    /**
     * An empty attribute row
     *
     * @var array
     * @access private
     */
    var $attr_row;

    /**
     * The current screen text
     *
     * @var array
     * @access private
     */
    var $screen;

    /**
     * The current screen attributes
     *
     * @var array
     * @access private
     */
    var $attrs;

    /**
     * Current ANSI code
     *
     * @var string
     * @access private
     */
    var $ansi;

    /**
     * Tokenization
     *
     * @var array
     * @access private
     */
    var $tokenization;

    /**
     * Default Constructor.
     *
     * @return File_ANSI
     * @access public
     */
    function __construct()
    {
        $attr_cell = new stdClass();
        $attr_cell->bold = false;
        $attr_cell->underline = false;
        $attr_cell->blink = false;
        $attr_cell->background = 'black';
        $attr_cell->foreground = 'white';
        $attr_cell->reverse = false;
        $this->base_attr_cell = clone($attr_cell);
        $this->attr_cell = clone($attr_cell);

        $this->setHistory(200);
        $this->setDimensions(80, 24);
    }

    /**
     * PHP4 compatible Default Constructor.
     *
     * @see self::__construct()
     * @access public
     */
    function File_ANSI()
    {
        $this->__construct($mode);
    }

    /**
     * Set terminal width and height
     *
     * Resets the screen as well
     *
     * @param int $x
     * @param int $y
     * @access public
     */
    function setDimensions($x, $y)
    {
        $this->max_x = $x - 1;
        $this->max_y = $y - 1;
        $this->x = $this->y = 0;
        $this->history = $this->history_attrs = array();
        $this->attr_row = array_fill(0, $this->max_x + 2, $this->base_attr_cell);
        $this->screen = array_fill(0, $this->max_y + 1, '');
        $this->attrs = array_fill(0, $this->max_y + 1, $this->attr_row);
        $this->ansi = '';
    }

    /**
     * Set the number of lines that should be logged past the terminal height
     *
     * @param int $x
     * @param int $y
     * @access public
     */
    function setHistory($history)
    {
        $this->max_history = $history;
    }

    /**
     * Load a string
     *
     * @param string $source
     * @access public
     */
    function loadString($source)
    {
        $this->setDimensions($this->max_x + 1, $this->max_y + 1);
        $this->appendString($source);
    }

    /**
     * Appdend a string
     *
     * @param string $source
     * @access public
     */
    function appendString($source)
    {
        $this->tokenization = array('');
        for ($i = 0; $i < strlen($source); $i++) {
            if (strlen($this->ansi)) {
                $this->ansi.= $source[$i];
                $chr = ord($source[$i]);
                // http://en.wikipedia.org/wiki/ANSI_escape_code#Sequence_elements
                // single character CSI's not currently supported
                switch (true) {
                    case $this->ansi == "\x1B=":
                        $this->ansi = '';
                        continue 2;
                    case strlen($this->ansi) == 2 && $chr >= 64 && $chr <= 95 && $chr != ord('['):
                    case strlen($this->ansi) > 2 && $chr >= 64 && $chr <= 126:
                        break;
                    default:
                        continue 2;
                }
                $this->tokenization[] = $this->ansi;
                $this->tokenization[] = '';
                // http://ascii-table.com/ansi-escape-sequences-vt-100.php
                switch ($this->ansi) {
                    case "\x1B[H": // Move cursor to upper left corner
                        $this->old_x = $this->x;
                        $this->old_y = $this->y;
                        $this->x = $this->y = 0;
                        break;
                    case "\x1B[J": // Clear screen from cursor down
                        $this->history = array_merge($this->history, array_slice(array_splice($this->screen, $this->y + 1), 0, $this->old_y));
                        $this->screen = array_merge($this->screen, array_fill($this->y, $this->max_y, ''));

                        $this->history_attrs = array_merge($this->history_attrs, array_slice(array_splice($this->attrs, $this->y + 1), 0, $this->old_y));
                        $this->attrs = array_merge($this->attrs, array_fill($this->y, $this->max_y, $this->attr_row));

                        if (count($this->history) == $this->max_history) {
                            array_shift($this->history);
                            array_shift($this->history_attrs);
                        }
                    case "\x1B[K": // Clear screen from cursor right
                        $this->screen[$this->y] = substr($this->screen[$this->y], 0, $this->x);

                        array_splice($this->attrs[$this->y], $this->x + 1, $this->max_x - $this->x, array_fill($this->x, $this->max_x - $this->x - 1, $this->base_attr_cell));
                        break;
                    case "\x1B[2K": // Clear entire line
                        $this->screen[$this->y] = str_repeat(' ', $this->x);
                        $this->attrs[$this->y] = $this->attr_row;
                        break;
                    case "\x1B[?1h": // set cursor key to application
                    case "\x1B[?25h": // show the cursor
                    case "\x1B(B": // set united states g0 character set
                        break;
                    case "\x1BE": // Move to next line
                        $this->_newLine();
                        $this->x = 0;
                        break;
                    default:
                        switch (true) {
                            case preg_match('#\x1B\[(\d+)B#', $this->ansi, $match): // Move cursor down n lines
                                $this->old_y = $this->y;
                                $this->y+= $match[1];
                                break;
                            case preg_match('#\x1B\[(\d+);(\d+)H#', $this->ansi, $match): // Move cursor to screen location v,h
                                $this->old_x = $this->x;
                                $this->old_y = $this->y;
                                $this->x = $match[2] - 1;
                                $this->y = $match[1] - 1;
                                break;
                            case preg_match('#\x1B\[(\d+)C#', $this->ansi, $match): // Move cursor right n lines
                                $this->old_x = $this->x;
                                $this->x+= $match[1];
                                break;
                            case preg_match('#\x1B\[(\d+)D#', $this->ansi, $match): // Move cursor left n lines
                                $this->old_x = $this->x;
                                $this->x-= $match[1];
                                if ($this->x < 0) {
                                    $this->x = 0;
                                }
                                break;
                            case preg_match('#\x1B\[(\d+);(\d+)r#', $this->ansi, $match): // Set top and bottom lines of a window
                                break;
                            case preg_match('#\x1B\[(\d*(?:;\d*)*)m#', $this->ansi, $match): // character attributes
                                $attr_cell = &$this->attr_cell;
                                $mods = explode(';', $match[1]);
                                foreach ($mods as $mod) {
                                    switch ($mod) {
                                        case 0: // Turn off character attributes
                                            $attr_cell = clone($this->base_attr_cell);
                                            break;
                                        case 1: // Turn bold mode on
                                            $attr_cell->bold = true;
                                            break;
                                        case 4: // Turn underline mode on
                                            $attr_cell->underline = true;
                                            break;
                                        case 5: // Turn blinking mode on
                                            $attr_cell->blink = true;
                                            break;
                                        case 7: // Turn reverse video on
                                            $attr_cell->reverse = !$attr_cell->reverse;
                                            $temp = $attr_cell->background;
                                            $attr_cell->background = $attr_cell->foreground;
                                            $attr_cell->foreground = $temp;
                                            break;
                                        default: // set colors
                                            //$front = $attr_cell->reverse ? &$attr_cell->background : &$attr_cell->foreground;
                                            $front = &$attr_cell->{ $attr_cell->reverse ? 'background' : 'foreground' };
                                            //$back = $attr_cell->reverse ? &$attr_cell->foreground : &$attr_cell->background;
                                            $back = &$attr_cell->{ $attr_cell->reverse ? 'foreground' : 'background' };
                                            switch ($mod) {
                                                // @codingStandardsIgnoreStart
                                                case 30: $front = 'black'; break;
                                                case 31: $front = 'red'; break;
                                                case 32: $front = 'green'; break;
                                                case 33: $front = 'yellow'; break;
                                                case 34: $front = 'blue'; break;
                                                case 35: $front = 'magenta'; break;
                                                case 36: $front = 'cyan'; break;
                                                case 37: $front = 'white'; break;

                                                case 40: $back = 'black'; break;
                                                case 41: $back = 'red'; break;
                                                case 42: $back = 'green'; break;
                                                case 43: $back = 'yellow'; break;
                                                case 44: $back = 'blue'; break;
                                                case 45: $back = 'magenta'; break;
                                                case 46: $back = 'cyan'; break;
                                                case 47: $back = 'white'; break;
                                                // @codingStandardsIgnoreEnd

                                                default:
                                                    //user_error('Unsupported attribute: ' . $mod);
                                                    $this->ansi = '';
                                                    break 2;
                                            }
                                    }
                                }
                                break;
                            default:
                                //user_error("{$this->ansi} is unsupported\r\n");
                        }
                }
                $this->ansi = '';
                continue;
            }

            $this->tokenization[count($this->tokenization) - 1].= $source[$i];
            switch ($source[$i]) {
                case "\r":
                    $this->x = 0;
                    break;
                case "\n":
                    $this->_newLine();
                    break;
                case "\x08": // backspace
                    if ($this->x) {
                        $this->x--;
                        $this->attrs[$this->y][$this->x] = clone($this->base_attr_cell);
                        $this->screen[$this->y] = substr_replace(
                            $this->screen[$this->y],
                            $source[$i],
                            $this->x,
                            1
                        );
                    }
                    break;
                case "\x0F": // shift
                    break;
                case "\x1B": // start ANSI escape code
                    $this->tokenization[count($this->tokenization) - 1] = substr($this->tokenization[count($this->tokenization) - 1], 0, -1);
                    //if (!strlen($this->tokenization[count($this->tokenization) - 1])) {
                    //    array_pop($this->tokenization);
                    //}
                    $this->ansi.= "\x1B";
                    break;
                default:
                    $this->attrs[$this->y][$this->x] = clone($this->attr_cell);
                    if ($this->x > strlen($this->screen[$this->y])) {
                        $this->screen[$this->y] = str_repeat(' ', $this->x);
                    }
                    $this->screen[$this->y] = substr_replace(
                        $this->screen[$this->y],
                        $source[$i],
                        $this->x,
                        1
                    );

                    if ($this->x > $this->max_x) {
                        $this->x = 0;
                        $this->_newLine();
                    } else {
                        $this->x++;
                    }
            }
        }
    }

    /**
     * Add a new line
     *
     * Also update the $this->screen and $this->history buffers
     *
     * @access private
     */
    function _newLine()
    {
        //if ($this->y < $this->max_y) {
        //    $this->y++;
        //}

        while ($this->y >= $this->max_y) {
            $this->history = array_merge($this->history, array(array_shift($this->screen)));
            $this->screen[] = '';

            $this->history_attrs = array_merge($this->history_attrs, array(array_shift($this->attrs)));
            $this->attrs[] = $this->attr_row;

            if (count($this->history) >= $this->max_history) {
                array_shift($this->history);
                array_shift($this->history_attrs);
            }

            $this->y--;
        }
        $this->y++;
    }

    /**
     * Returns the current coordinate without preformating
     *
     * @access private
     * @return string
     */
    function _processCoordinate($last_attr, $cur_attr, $char)
    {
        $output = '';

        if ($last_attr != $cur_attr) {
            $close = $open = '';
            if ($last_attr->foreground != $cur_attr->foreground) {
                if ($cur_attr->foreground != 'white') {
                    $open.= '<span style="color: ' . $cur_attr->foreground . '">';
                }
                if ($last_attr->foreground != 'white') {
                    $close = '</span>' . $close;
                }
            }
            if ($last_attr->background != $cur_attr->background) {
                if ($cur_attr->background != 'black') {
                    $open.= '<span style="background: ' . $cur_attr->background . '">';
                }
                if ($last_attr->background != 'black') {
                    $close = '</span>' . $close;
                }
            }
            if ($last_attr->bold != $cur_attr->bold) {
                if ($cur_attr->bold) {
                    $open.= '<b>';
                } else {
                    $close = '</b>' . $close;
                }
            }
            if ($last_attr->underline != $cur_attr->underline) {
                if ($cur_attr->underline) {
                    $open.= '<u>';
                } else {
                    $close = '</u>' . $close;
                }
            }
            if ($last_attr->blink != $cur_attr->blink) {
                if ($cur_attr->blink) {
                    $open.= '<blink>';
                } else {
                    $close = '</blink>' . $close;
                }
            }
            $output.= $close . $open;
        }

        $output.= htmlspecialchars($char);

        return $output;
    }

    /**
     * Returns the current screen without preformating
     *
     * @access private
     * @return string
     */
    function _getScreen()
    {
        $output = '';
        $last_attr = $this->base_attr_cell;
        for ($i = 0; $i <= $this->max_y; $i++) {
            for ($j = 0; $j <= $this->max_x; $j++) {
                $cur_attr = $this->attrs[$i][$j];
                $output.= $this->_processCoordinate($last_attr, $cur_attr, isset($this->screen[$i][$j]) ? $this->screen[$i][$j] : '');
                $last_attr = $this->attrs[$i][$j];
            }
            $output.= "\r\n";
        }
        $output = substr($output, 0, -2);
        // close any remaining open tags
        $output.= $this->_processCoordinate($last_attr, $this->base_attr_cell, '');
        return rtrim($output);
    }

    /**
     * Returns the current screen
     *
     * @access public
     * @return string
     */
    function getScreen()
    {
        return '<pre width="' . ($this->max_x + 1) . '" style="color: white; background: black">' . $this->_getScreen() . '</pre>';
    }

    /**
     * Returns the current screen and the x previous lines
     *
     * @access public
     * @return string
     */
    function getHistory()
    {
        $scrollback = '';
        $last_attr = $this->base_attr_cell;
        for ($i = 0; $i < count($this->history); $i++) {
            for ($j = 0; $j <= $this->max_x + 1; $j++) {
                $cur_attr = $this->history_attrs[$i][$j];
                $scrollback.= $this->_processCoordinate($last_attr, $cur_attr, isset($this->history[$i][$j]) ? $this->history[$i][$j] : '');
                $last_attr = $this->history_attrs[$i][$j];
            }
            $scrollback.= "\r\n";
        }
        $base_attr_cell = $this->base_attr_cell;
        $this->base_attr_cell = $last_attr;
        $scrollback.= $this->_getScreen();
        $this->base_attr_cell = $base_attr_cell;

        return '<pre width="' . ($this->max_x + 1) . '" style="color: white; background: black">' . $scrollback . '</span></pre>';
    }
}
vendor/phpseclib/phpseclib/phpseclib/File/X509.php000064400000565767151327705700016032 0ustar00<?php

/**
 * Pure-PHP X.509 Parser
 *
 * PHP versions 4 and 5
 *
 * Encode and decode X.509 certificates.
 *
 * The extensions are from {@link http://tools.ietf.org/html/rfc5280 RFC5280} and
 * {@link http://web.archive.org/web/19961027104704/http://www3.netscape.com/eng/security/cert-exts.html Netscape Certificate Extensions}.
 *
 * Note that loading an X.509 certificate and resaving it may invalidate the signature.  The reason being that the signature is based on a
 * portion of the certificate that contains optional parameters with default values.  ie. if the parameter isn't there the default value is
 * used.  Problem is, if the parameter is there and it just so happens to have the default value there are two ways that that parameter can
 * be encoded.  It can be encoded explicitly or left out all together.  This would effect the signature value and thus may invalidate the
 * the certificate all together unless the certificate is re-signed.
 *
 * LICENSE: Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 *
 * @category  File
 * @package   File_X509
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2012 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      http://phpseclib.sourceforge.net
 */

/**
 * Include File_ASN1
 */
if (!class_exists('File_ASN1')) {
    include_once 'ASN1.php';
}

/**
 * Flag to only accept signatures signed by certificate authorities
 *
 * Not really used anymore but retained all the same to suppress E_NOTICEs from old installs
 *
 * @access public
 */
define('FILE_X509_VALIDATE_SIGNATURE_BY_CA', 1);

/**#@+
 * @access public
 * @see self::getDN()
 */
/**
 * Return internal array representation
 */
define('FILE_X509_DN_ARRAY', 0);
/**
 * Return string
 */
define('FILE_X509_DN_STRING', 1);
/**
 * Return ASN.1 name string
 */
define('FILE_X509_DN_ASN1', 2);
/**
 * Return OpenSSL compatible array
 */
define('FILE_X509_DN_OPENSSL', 3);
/**
 * Return canonical ASN.1 RDNs string
 */
define('FILE_X509_DN_CANON', 4);
/**
 * Return name hash for file indexing
 */
define('FILE_X509_DN_HASH', 5);
/**#@-*/

/**#@+
 * @access public
 * @see self::saveX509()
 * @see self::saveCSR()
 * @see self::saveCRL()
 */
/**
 * Save as PEM
 *
 * ie. a base64-encoded PEM with a header and a footer
 */
define('FILE_X509_FORMAT_PEM', 0);
/**
 * Save as DER
 */
define('FILE_X509_FORMAT_DER', 1);
/**
 * Save as a SPKAC
 *
 * Only works on CSRs. Not currently supported.
 */
define('FILE_X509_FORMAT_SPKAC', 2);
/**
 * Auto-detect the format
 *
 * Used only by the load*() functions
 */
define('FILE_X509_FORMAT_AUTO_DETECT', 3);
/**#@-*/

/**
 * Attribute value disposition.
 * If disposition is >= 0, this is the index of the target value.
 */
define('FILE_X509_ATTR_ALL', -1); // All attribute values (array).
define('FILE_X509_ATTR_APPEND', -2); // Add a value.
define('FILE_X509_ATTR_REPLACE', -3); // Clear first, then add a value.

/**
 * Pure-PHP X.509 Parser
 *
 * @package File_X509
 * @author  Jim Wigginton <terrafrost@php.net>
 * @access  public
 */
class File_X509
{
    /**
     * ASN.1 syntax for X.509 certificates
     *
     * @var array
     * @access private
     */
    var $Certificate;

    /**#@+
     * ASN.1 syntax for various extensions
     *
     * @access private
     */
    var $DirectoryString;
    var $PKCS9String;
    var $AttributeValue;
    var $Extensions;
    var $KeyUsage;
    var $ExtKeyUsageSyntax;
    var $BasicConstraints;
    var $KeyIdentifier;
    var $CRLDistributionPoints;
    var $AuthorityKeyIdentifier;
    var $CertificatePolicies;
    var $AuthorityInfoAccessSyntax;
    var $SubjectAltName;
    var $SubjectDirectoryAttributes;
    var $PrivateKeyUsagePeriod;
    var $IssuerAltName;
    var $PolicyMappings;
    var $NameConstraints;

    var $CPSuri;
    var $UserNotice;

    var $netscape_cert_type;
    var $netscape_comment;
    var $netscape_ca_policy_url;

    var $Name;
    var $RelativeDistinguishedName;
    var $CRLNumber;
    var $CRLReason;
    var $IssuingDistributionPoint;
    var $InvalidityDate;
    var $CertificateIssuer;
    var $HoldInstructionCode;
    var $SignedPublicKeyAndChallenge;
    /**#@-*/

    /**#@+
     * ASN.1 syntax for various DN attributes
     *
     * @access private
     */
    var $PostalAddress;
    /**#@-*/

    /**
     * ASN.1 syntax for Certificate Signing Requests (RFC2986)
     *
     * @var array
     * @access private
     */
    var $CertificationRequest;

    /**
     * ASN.1 syntax for Certificate Revocation Lists (RFC5280)
     *
     * @var array
     * @access private
     */
    var $CertificateList;

    /**
     * Distinguished Name
     *
     * @var array
     * @access private
     */
    var $dn;

    /**
     * Public key
     *
     * @var string
     * @access private
     */
    var $publicKey;

    /**
     * Private key
     *
     * @var string
     * @access private
     */
    var $privateKey;

    /**
     * Object identifiers for X.509 certificates
     *
     * @var array
     * @access private
     * @link http://en.wikipedia.org/wiki/Object_identifier
     */
    var $oids;

    /**
     * The certificate authorities
     *
     * @var array
     * @access private
     */
    var $CAs;

    /**
     * The currently loaded certificate
     *
     * @var array
     * @access private
     */
    var $currentCert;

    /**
     * The signature subject
     *
     * There's no guarantee File_X509 is going to re-encode an X.509 cert in the same way it was originally
     * encoded so we take save the portion of the original cert that the signature would have made for.
     *
     * @var string
     * @access private
     */
    var $signatureSubject;

    /**
     * Certificate Start Date
     *
     * @var string
     * @access private
     */
    var $startDate;

    /**
     * Certificate End Date
     *
     * @var string
     * @access private
     */
    var $endDate;

    /**
     * Serial Number
     *
     * @var string
     * @access private
     */
    var $serialNumber;

    /**
     * Key Identifier
     *
     * See {@link http://tools.ietf.org/html/rfc5280#section-4.2.1.1 RFC5280#section-4.2.1.1} and
     * {@link http://tools.ietf.org/html/rfc5280#section-4.2.1.2 RFC5280#section-4.2.1.2}.
     *
     * @var string
     * @access private
     */
    var $currentKeyIdentifier;

    /**
     * CA Flag
     *
     * @var bool
     * @access private
     */
    var $caFlag = false;

    /**
     * SPKAC Challenge
     *
     * @var string
     * @access private
     */
    var $challenge;

    /**
     * Recursion Limit
     *
     * @var int
     * @access private
     */
    var $recur_limit = 5;

    /**
     * URL fetch flag
     *
     * @var bool
     * @access private
     */
    var $disable_url_fetch = false;

    /**
     * Default Constructor.
     *
     * @return File_X509
     * @access public
     */
    function __construct()
    {
        if (!class_exists('Math_BigInteger')) {
            include_once 'Math/BigInteger.php';
        }

        // Explicitly Tagged Module, 1988 Syntax
        // http://tools.ietf.org/html/rfc5280#appendix-A.1

        $this->DirectoryString = array(
            'type'     => FILE_ASN1_TYPE_CHOICE,
            'children' => array(
                'teletexString'   => array('type' => FILE_ASN1_TYPE_TELETEX_STRING),
                'printableString' => array('type' => FILE_ASN1_TYPE_PRINTABLE_STRING),
                'universalString' => array('type' => FILE_ASN1_TYPE_UNIVERSAL_STRING),
                'utf8String'      => array('type' => FILE_ASN1_TYPE_UTF8_STRING),
                'bmpString'       => array('type' => FILE_ASN1_TYPE_BMP_STRING)
            )
        );

        $this->PKCS9String = array(
            'type'     => FILE_ASN1_TYPE_CHOICE,
            'children' => array(
                'ia5String'       => array('type' => FILE_ASN1_TYPE_IA5_STRING),
                'directoryString' => $this->DirectoryString
            )
        );

        $this->AttributeValue = array('type' => FILE_ASN1_TYPE_ANY);

        $AttributeType = array('type' => FILE_ASN1_TYPE_OBJECT_IDENTIFIER);

        $AttributeTypeAndValue = array(
            'type'     => FILE_ASN1_TYPE_SEQUENCE,
            'children' => array(
                'type' => $AttributeType,
                'value'=> $this->AttributeValue
            )
        );

        /*
        In practice, RDNs containing multiple name-value pairs (called "multivalued RDNs") are rare,
        but they can be useful at times when either there is no unique attribute in the entry or you
        want to ensure that the entry's DN contains some useful identifying information.

        - https://www.opends.org/wiki/page/DefinitionRelativeDistinguishedName
        */
        $this->RelativeDistinguishedName = array(
            'type'     => FILE_ASN1_TYPE_SET,
            'min'      => 1,
            'max'      => -1,
            'children' => $AttributeTypeAndValue
        );

        // http://tools.ietf.org/html/rfc5280#section-4.1.2.4
        $RDNSequence = array(
            'type'     => FILE_ASN1_TYPE_SEQUENCE,
            // RDNSequence does not define a min or a max, which means it doesn't have one
            'min'      => 0,
            'max'      => -1,
            'children' => $this->RelativeDistinguishedName
        );

        $this->Name = array(
            'type'     => FILE_ASN1_TYPE_CHOICE,
            'children' => array(
                'rdnSequence' => $RDNSequence
            )
        );

        // http://tools.ietf.org/html/rfc5280#section-4.1.1.2
        $AlgorithmIdentifier = array(
            'type'     => FILE_ASN1_TYPE_SEQUENCE,
            'children' => array(
                'algorithm'  => array('type' => FILE_ASN1_TYPE_OBJECT_IDENTIFIER),
                'parameters' => array(
                                    'type'     => FILE_ASN1_TYPE_ANY,
                                    'optional' => true
                                )
            )
        );

        /*
           A certificate using system MUST reject the certificate if it encounters
           a critical extension it does not recognize; however, a non-critical
           extension may be ignored if it is not recognized.

           http://tools.ietf.org/html/rfc5280#section-4.2
        */
        $Extension = array(
            'type'     => FILE_ASN1_TYPE_SEQUENCE,
            'children' => array(
                'extnId'   => array('type' => FILE_ASN1_TYPE_OBJECT_IDENTIFIER),
                'critical' => array(
                                  'type'     => FILE_ASN1_TYPE_BOOLEAN,
                                  'optional' => true,
                                  'default'  => false
                              ),
                'extnValue' => array('type' => FILE_ASN1_TYPE_OCTET_STRING)
            )
        );

        $this->Extensions = array(
            'type'     => FILE_ASN1_TYPE_SEQUENCE,
            'min'      => 1,
            // technically, it's MAX, but we'll assume anything < 0 is MAX
            'max'      => -1,
            // if 'children' isn't an array then 'min' and 'max' must be defined
            'children' => $Extension
        );

        $SubjectPublicKeyInfo = array(
            'type'     => FILE_ASN1_TYPE_SEQUENCE,
            'children' => array(
                'algorithm'        => $AlgorithmIdentifier,
                'subjectPublicKey' => array('type' => FILE_ASN1_TYPE_BIT_STRING)
            )
        );

        $UniqueIdentifier = array('type' => FILE_ASN1_TYPE_BIT_STRING);

        $Time = array(
            'type'     => FILE_ASN1_TYPE_CHOICE,
            'children' => array(
                'utcTime'     => array('type' => FILE_ASN1_TYPE_UTC_TIME),
                'generalTime' => array('type' => FILE_ASN1_TYPE_GENERALIZED_TIME)
            )
        );

        // http://tools.ietf.org/html/rfc5280#section-4.1.2.5
        $Validity = array(
            'type'     => FILE_ASN1_TYPE_SEQUENCE,
            'children' => array(
                'notBefore' => $Time,
                'notAfter'  => $Time
            )
        );

        $CertificateSerialNumber = array('type' => FILE_ASN1_TYPE_INTEGER);

        $Version = array(
            'type'    => FILE_ASN1_TYPE_INTEGER,
            'mapping' => array('v1', 'v2', 'v3')
        );

        // assert($TBSCertificate['children']['signature'] == $Certificate['children']['signatureAlgorithm'])
        $TBSCertificate = array(
            'type'     => FILE_ASN1_TYPE_SEQUENCE,
            'children' => array(
                // technically, default implies optional, but we'll define it as being optional, none-the-less, just to
                // reenforce that fact
                'version'             => array(
                                             'constant' => 0,
                                             'optional' => true,
                                             'explicit' => true,
                                             'default'  => 'v1'
                                         ) + $Version,
                'serialNumber'         => $CertificateSerialNumber,
                'signature'            => $AlgorithmIdentifier,
                'issuer'               => $this->Name,
                'validity'             => $Validity,
                'subject'              => $this->Name,
                'subjectPublicKeyInfo' => $SubjectPublicKeyInfo,
                // implicit means that the T in the TLV structure is to be rewritten, regardless of the type
                'issuerUniqueID'       => array(
                                               'constant' => 1,
                                               'optional' => true,
                                               'implicit' => true
                                           ) + $UniqueIdentifier,
                'subjectUniqueID'       => array(
                                               'constant' => 2,
                                               'optional' => true,
                                               'implicit' => true
                                           ) + $UniqueIdentifier,
                // <http://tools.ietf.org/html/rfc2459#page-74> doesn't use the EXPLICIT keyword but if
                // it's not IMPLICIT, it's EXPLICIT
                'extensions'            => array(
                                               'constant' => 3,
                                               'optional' => true,
                                               'explicit' => true
                                           ) + $this->Extensions
            )
        );

        $this->Certificate = array(
            'type'     => FILE_ASN1_TYPE_SEQUENCE,
            'children' => array(
                 'tbsCertificate'     => $TBSCertificate,
                 'signatureAlgorithm' => $AlgorithmIdentifier,
                 'signature'          => array('type' => FILE_ASN1_TYPE_BIT_STRING)
            )
        );

        $this->KeyUsage = array(
            'type'    => FILE_ASN1_TYPE_BIT_STRING,
            'mapping' => array(
                'digitalSignature',
                'nonRepudiation',
                'keyEncipherment',
                'dataEncipherment',
                'keyAgreement',
                'keyCertSign',
                'cRLSign',
                'encipherOnly',
                'decipherOnly'
            )
        );

        $this->BasicConstraints = array(
            'type'     => FILE_ASN1_TYPE_SEQUENCE,
            'children' => array(
                'cA'                => array(
                                                 'type'     => FILE_ASN1_TYPE_BOOLEAN,
                                                 'optional' => true,
                                                 'default'  => false
                                       ),
                'pathLenConstraint' => array(
                                                 'type' => FILE_ASN1_TYPE_INTEGER,
                                                 'optional' => true
                                       )
            )
        );

        $this->KeyIdentifier = array('type' => FILE_ASN1_TYPE_OCTET_STRING);

        $OrganizationalUnitNames = array(
            'type'     => FILE_ASN1_TYPE_SEQUENCE,
            'min'      => 1,
            'max'      => 4, // ub-organizational-units
            'children' => array('type' => FILE_ASN1_TYPE_PRINTABLE_STRING)
        );

        $PersonalName = array(
            'type'     => FILE_ASN1_TYPE_SET,
            'children' => array(
                'surname'              => array(
                                           'type' => FILE_ASN1_TYPE_PRINTABLE_STRING,
                                           'constant' => 0,
                                           'optional' => true,
                                           'implicit' => true
                                         ),
                'given-name'           => array(
                                           'type' => FILE_ASN1_TYPE_PRINTABLE_STRING,
                                           'constant' => 1,
                                           'optional' => true,
                                           'implicit' => true
                                         ),
                'initials'             => array(
                                           'type' => FILE_ASN1_TYPE_PRINTABLE_STRING,
                                           'constant' => 2,
                                           'optional' => true,
                                           'implicit' => true
                                         ),
                'generation-qualifier' => array(
                                           'type' => FILE_ASN1_TYPE_PRINTABLE_STRING,
                                           'constant' => 3,
                                           'optional' => true,
                                           'implicit' => true
                                         )
            )
        );

        $NumericUserIdentifier = array('type' => FILE_ASN1_TYPE_NUMERIC_STRING);

        $OrganizationName = array('type' => FILE_ASN1_TYPE_PRINTABLE_STRING);

        $PrivateDomainName = array(
            'type'     => FILE_ASN1_TYPE_CHOICE,
            'children' => array(
                'numeric'   => array('type' => FILE_ASN1_TYPE_NUMERIC_STRING),
                'printable' => array('type' => FILE_ASN1_TYPE_PRINTABLE_STRING)
            )
        );

        $TerminalIdentifier = array('type' => FILE_ASN1_TYPE_PRINTABLE_STRING);

        $NetworkAddress = array('type' => FILE_ASN1_TYPE_NUMERIC_STRING);

        $AdministrationDomainName = array(
            'type'     => FILE_ASN1_TYPE_CHOICE,
            // if class isn't present it's assumed to be FILE_ASN1_CLASS_UNIVERSAL or
            // (if constant is present) FILE_ASN1_CLASS_CONTEXT_SPECIFIC
            'class'    => FILE_ASN1_CLASS_APPLICATION,
            'cast'     => 2,
            'children' => array(
                'numeric'   => array('type' => FILE_ASN1_TYPE_NUMERIC_STRING),
                'printable' => array('type' => FILE_ASN1_TYPE_PRINTABLE_STRING)
            )
        );

        $CountryName = array(
            'type'     => FILE_ASN1_TYPE_CHOICE,
            // if class isn't present it's assumed to be FILE_ASN1_CLASS_UNIVERSAL or
            // (if constant is present) FILE_ASN1_CLASS_CONTEXT_SPECIFIC
            'class'    => FILE_ASN1_CLASS_APPLICATION,
            'cast'     => 1,
            'children' => array(
                'x121-dcc-code'        => array('type' => FILE_ASN1_TYPE_NUMERIC_STRING),
                'iso-3166-alpha2-code' => array('type' => FILE_ASN1_TYPE_PRINTABLE_STRING)
            )
        );

        $AnotherName = array(
            'type'     => FILE_ASN1_TYPE_SEQUENCE,
            'children' => array(
                 'type-id' => array('type' => FILE_ASN1_TYPE_OBJECT_IDENTIFIER),
                 'value'   => array(
                                  'type' => FILE_ASN1_TYPE_ANY,
                                  'constant' => 0,
                                  'optional' => true,
                                  'explicit' => true
                              )
            )
        );

        $ExtensionAttribute = array(
            'type'     => FILE_ASN1_TYPE_SEQUENCE,
            'children' => array(
                 'extension-attribute-type'  => array(
                                                    'type' => FILE_ASN1_TYPE_PRINTABLE_STRING,
                                                    'constant' => 0,
                                                    'optional' => true,
                                                    'implicit' => true
                                                ),
                 'extension-attribute-value' => array(
                                                    'type' => FILE_ASN1_TYPE_ANY,
                                                    'constant' => 1,
                                                    'optional' => true,
                                                    'explicit' => true
                                                )
            )
        );

        $ExtensionAttributes = array(
            'type'     => FILE_ASN1_TYPE_SET,
            'min'      => 1,
            'max'      => 256, // ub-extension-attributes
            'children' => $ExtensionAttribute
        );

        $BuiltInDomainDefinedAttribute = array(
            'type'     => FILE_ASN1_TYPE_SEQUENCE,
            'children' => array(
                 'type'  => array('type' => FILE_ASN1_TYPE_PRINTABLE_STRING),
                 'value' => array('type' => FILE_ASN1_TYPE_PRINTABLE_STRING)
            )
        );

        $BuiltInDomainDefinedAttributes = array(
            'type'     => FILE_ASN1_TYPE_SEQUENCE,
            'min'      => 1,
            'max'      => 4, // ub-domain-defined-attributes
            'children' => $BuiltInDomainDefinedAttribute
        );

        $BuiltInStandardAttributes =  array(
            'type'     => FILE_ASN1_TYPE_SEQUENCE,
            'children' => array(
                'country-name'               => array('optional' => true) + $CountryName,
                'administration-domain-name' => array('optional' => true) + $AdministrationDomainName,
                'network-address'            => array(
                                                 'constant' => 0,
                                                 'optional' => true,
                                                 'implicit' => true
                                               ) + $NetworkAddress,
                'terminal-identifier'        => array(
                                                 'constant' => 1,
                                                 'optional' => true,
                                                 'implicit' => true
                                               ) + $TerminalIdentifier,
                'private-domain-name'        => array(
                                                 'constant' => 2,
                                                 'optional' => true,
                                                 'explicit' => true
                                               ) + $PrivateDomainName,
                'organization-name'          => array(
                                                 'constant' => 3,
                                                 'optional' => true,
                                                 'implicit' => true
                                               ) + $OrganizationName,
                'numeric-user-identifier'    => array(
                                                 'constant' => 4,
                                                 'optional' => true,
                                                 'implicit' => true
                                               ) + $NumericUserIdentifier,
                'personal-name'              => array(
                                                 'constant' => 5,
                                                 'optional' => true,
                                                 'implicit' => true
                                               ) + $PersonalName,
                'organizational-unit-names'  => array(
                                                 'constant' => 6,
                                                 'optional' => true,
                                                 'implicit' => true
                                               ) + $OrganizationalUnitNames
            )
        );

        $ORAddress = array(
            'type'     => FILE_ASN1_TYPE_SEQUENCE,
            'children' => array(
                 'built-in-standard-attributes'       => $BuiltInStandardAttributes,
                 'built-in-domain-defined-attributes' => array('optional' => true) + $BuiltInDomainDefinedAttributes,
                 'extension-attributes'               => array('optional' => true) + $ExtensionAttributes
            )
        );

        $EDIPartyName = array(
            'type'     => FILE_ASN1_TYPE_SEQUENCE,
            'children' => array(
                 'nameAssigner' => array(
                                    'constant' => 0,
                                    'optional' => true,
                                    'implicit' => true
                                ) + $this->DirectoryString,
                 // partyName is technically required but File_ASN1 doesn't currently support non-optional constants and
                 // setting it to optional gets the job done in any event.
                 'partyName'    => array(
                                    'constant' => 1,
                                    'optional' => true,
                                    'implicit' => true
                                ) + $this->DirectoryString
            )
        );

        $GeneralName = array(
            'type'     => FILE_ASN1_TYPE_CHOICE,
            'children' => array(
                'otherName'                 => array(
                                                 'constant' => 0,
                                                 'optional' => true,
                                                 'implicit' => true
                                               ) + $AnotherName,
                'rfc822Name'                => array(
                                                 'type' => FILE_ASN1_TYPE_IA5_STRING,
                                                 'constant' => 1,
                                                 'optional' => true,
                                                 'implicit' => true
                                               ),
                'dNSName'                   => array(
                                                 'type' => FILE_ASN1_TYPE_IA5_STRING,
                                                 'constant' => 2,
                                                 'optional' => true,
                                                 'implicit' => true
                                               ),
                'x400Address'               => array(
                                                 'constant' => 3,
                                                 'optional' => true,
                                                 'implicit' => true
                                               ) + $ORAddress,
                'directoryName'             => array(
                                                 'constant' => 4,
                                                 'optional' => true,
                                                 'explicit' => true
                                               ) + $this->Name,
                'ediPartyName'              => array(
                                                 'constant' => 5,
                                                 'optional' => true,
                                                 'implicit' => true
                                               ) + $EDIPartyName,
                'uniformResourceIdentifier' => array(
                                                 'type' => FILE_ASN1_TYPE_IA5_STRING,
                                                 'constant' => 6,
                                                 'optional' => true,
                                                 'implicit' => true
                                               ),
                'iPAddress'                 => array(
                                                 'type' => FILE_ASN1_TYPE_OCTET_STRING,
                                                 'constant' => 7,
                                                 'optional' => true,
                                                 'implicit' => true
                                               ),
                'registeredID'              => array(
                                                 'type' => FILE_ASN1_TYPE_OBJECT_IDENTIFIER,
                                                 'constant' => 8,
                                                 'optional' => true,
                                                 'implicit' => true
                                               )
            )
        );

        $GeneralNames = array(
            'type'     => FILE_ASN1_TYPE_SEQUENCE,
            'min'      => 1,
            'max'      => -1,
            'children' => $GeneralName
        );

        $this->IssuerAltName = $GeneralNames;

        $ReasonFlags = array(
            'type'    => FILE_ASN1_TYPE_BIT_STRING,
            'mapping' => array(
                'unused',
                'keyCompromise',
                'cACompromise',
                'affiliationChanged',
                'superseded',
                'cessationOfOperation',
                'certificateHold',
                'privilegeWithdrawn',
                'aACompromise'
            )
        );

        $DistributionPointName = array(
            'type'     => FILE_ASN1_TYPE_CHOICE,
            'children' => array(
                'fullName'                => array(
                                                 'constant' => 0,
                                                 'optional' => true,
                                                 'implicit' => true
                                       ) + $GeneralNames,
                'nameRelativeToCRLIssuer' => array(
                                                 'constant' => 1,
                                                 'optional' => true,
                                                 'implicit' => true
                                       ) + $this->RelativeDistinguishedName
            )
        );

        $DistributionPoint = array(
            'type'     => FILE_ASN1_TYPE_SEQUENCE,
            'children' => array(
                'distributionPoint' => array(
                                                 'constant' => 0,
                                                 'optional' => true,
                                                 'explicit' => true
                                       ) + $DistributionPointName,
                'reasons'           => array(
                                                 'constant' => 1,
                                                 'optional' => true,
                                                 'implicit' => true
                                       ) + $ReasonFlags,
                'cRLIssuer'         => array(
                                                 'constant' => 2,
                                                 'optional' => true,
                                                 'implicit' => true
                                       ) + $GeneralNames
            )
        );

        $this->CRLDistributionPoints = array(
            'type'     => FILE_ASN1_TYPE_SEQUENCE,
            'min'      => 1,
            'max'      => -1,
            'children' => $DistributionPoint
        );

        $this->AuthorityKeyIdentifier = array(
            'type'     => FILE_ASN1_TYPE_SEQUENCE,
            'children' => array(
                'keyIdentifier'             => array(
                                                 'constant' => 0,
                                                 'optional' => true,
                                                 'implicit' => true
                                               ) + $this->KeyIdentifier,
                'authorityCertIssuer'       => array(
                                                 'constant' => 1,
                                                 'optional' => true,
                                                 'implicit' => true
                                               ) + $GeneralNames,
                'authorityCertSerialNumber' => array(
                                                 'constant' => 2,
                                                 'optional' => true,
                                                 'implicit' => true
                                               ) + $CertificateSerialNumber
            )
        );

        $PolicyQualifierId = array('type' => FILE_ASN1_TYPE_OBJECT_IDENTIFIER);

        $PolicyQualifierInfo = array(
            'type'     => FILE_ASN1_TYPE_SEQUENCE,
            'children' => array(
                'policyQualifierId' => $PolicyQualifierId,
                'qualifier'         => array('type' => FILE_ASN1_TYPE_ANY)
            )
        );

        $CertPolicyId = array('type' => FILE_ASN1_TYPE_OBJECT_IDENTIFIER);

        $PolicyInformation = array(
            'type'     => FILE_ASN1_TYPE_SEQUENCE,
            'children' => array(
                'policyIdentifier' => $CertPolicyId,
                'policyQualifiers' => array(
                                          'type'     => FILE_ASN1_TYPE_SEQUENCE,
                                          'min'      => 0,
                                          'max'      => -1,
                                          'optional' => true,
                                          'children' => $PolicyQualifierInfo
                                      )
            )
        );

        $this->CertificatePolicies = array(
            'type'     => FILE_ASN1_TYPE_SEQUENCE,
            'min'      => 1,
            'max'      => -1,
            'children' => $PolicyInformation
        );

        $this->PolicyMappings = array(
            'type'     => FILE_ASN1_TYPE_SEQUENCE,
            'min'      => 1,
            'max'      => -1,
            'children' => array(
                              'type'     => FILE_ASN1_TYPE_SEQUENCE,
                              'children' => array(
                                  'issuerDomainPolicy' => $CertPolicyId,
                                  'subjectDomainPolicy' => $CertPolicyId
                              )
                       )
        );

        $KeyPurposeId = array('type' => FILE_ASN1_TYPE_OBJECT_IDENTIFIER);

        $this->ExtKeyUsageSyntax = array(
            'type'     => FILE_ASN1_TYPE_SEQUENCE,
            'min'      => 1,
            'max'      => -1,
            'children' => $KeyPurposeId
        );

        $AccessDescription = array(
            'type'     => FILE_ASN1_TYPE_SEQUENCE,
            'children' => array(
                'accessMethod'   => array('type' => FILE_ASN1_TYPE_OBJECT_IDENTIFIER),
                'accessLocation' => $GeneralName
            )
        );

        $this->AuthorityInfoAccessSyntax = array(
            'type'     => FILE_ASN1_TYPE_SEQUENCE,
            'min'      => 1,
            'max'      => -1,
            'children' => $AccessDescription
        );

        $this->SubjectAltName = $GeneralNames;

        $this->PrivateKeyUsagePeriod = array(
            'type'     => FILE_ASN1_TYPE_SEQUENCE,
            'children' => array(
                'notBefore' => array(
                                                 'constant' => 0,
                                                 'optional' => true,
                                                 'implicit' => true,
                                                 'type' => FILE_ASN1_TYPE_GENERALIZED_TIME),
                'notAfter'  => array(
                                                 'constant' => 1,
                                                 'optional' => true,
                                                 'implicit' => true,
                                                 'type' => FILE_ASN1_TYPE_GENERALIZED_TIME)
            )
        );

        $BaseDistance = array('type' => FILE_ASN1_TYPE_INTEGER);

        $GeneralSubtree = array(
            'type'     => FILE_ASN1_TYPE_SEQUENCE,
            'children' => array(
                'base'    => $GeneralName,
                'minimum' => array(
                                 'constant' => 0,
                                 'optional' => true,
                                 'implicit' => true,
                                 'default' => new Math_BigInteger(0)
                             ) + $BaseDistance,
                'maximum' => array(
                                 'constant' => 1,
                                 'optional' => true,
                                 'implicit' => true,
                             ) + $BaseDistance
            )
        );

        $GeneralSubtrees = array(
            'type'     => FILE_ASN1_TYPE_SEQUENCE,
            'min'      => 1,
            'max'      => -1,
            'children' => $GeneralSubtree
        );

        $this->NameConstraints = array(
            'type'     => FILE_ASN1_TYPE_SEQUENCE,
            'children' => array(
                'permittedSubtrees' => array(
                                           'constant' => 0,
                                           'optional' => true,
                                           'implicit' => true
                                       ) + $GeneralSubtrees,
                'excludedSubtrees'  => array(
                                           'constant' => 1,
                                           'optional' => true,
                                           'implicit' => true
                                       ) + $GeneralSubtrees
            )
        );

        $this->CPSuri = array('type' => FILE_ASN1_TYPE_IA5_STRING);

        $DisplayText = array(
            'type'     => FILE_ASN1_TYPE_CHOICE,
            'children' => array(
                'ia5String'     => array('type' => FILE_ASN1_TYPE_IA5_STRING),
                'visibleString' => array('type' => FILE_ASN1_TYPE_VISIBLE_STRING),
                'bmpString'     => array('type' => FILE_ASN1_TYPE_BMP_STRING),
                'utf8String'    => array('type' => FILE_ASN1_TYPE_UTF8_STRING)
            )
        );

        $NoticeReference = array(
            'type'     => FILE_ASN1_TYPE_SEQUENCE,
            'children' => array(
                'organization'  => $DisplayText,
                'noticeNumbers' => array(
                                       'type'     => FILE_ASN1_TYPE_SEQUENCE,
                                       'min'      => 1,
                                       'max'      => 200,
                                       'children' => array('type' => FILE_ASN1_TYPE_INTEGER)
                                   )
            )
        );

        $this->UserNotice = array(
            'type'     => FILE_ASN1_TYPE_SEQUENCE,
            'children' => array(
                'noticeRef' => array(
                                           'optional' => true,
                                           'implicit' => true
                                       ) + $NoticeReference,
                'explicitText'  => array(
                                           'optional' => true,
                                           'implicit' => true
                                       ) + $DisplayText
            )
        );

        // mapping is from <http://www.mozilla.org/projects/security/pki/nss/tech-notes/tn3.html>
        $this->netscape_cert_type = array(
            'type'    => FILE_ASN1_TYPE_BIT_STRING,
            'mapping' => array(
                'SSLClient',
                'SSLServer',
                'Email',
                'ObjectSigning',
                'Reserved',
                'SSLCA',
                'EmailCA',
                'ObjectSigningCA'
            )
        );

        $this->netscape_comment = array('type' => FILE_ASN1_TYPE_IA5_STRING);
        $this->netscape_ca_policy_url = array('type' => FILE_ASN1_TYPE_IA5_STRING);

        // attribute is used in RFC2986 but we're using the RFC5280 definition

        $Attribute = array(
            'type'     => FILE_ASN1_TYPE_SEQUENCE,
            'children' => array(
                'type' => $AttributeType,
                'value'=> array(
                              'type'     => FILE_ASN1_TYPE_SET,
                              'min'      => 1,
                              'max'      => -1,
                              'children' => $this->AttributeValue
                          )
            )
        );

        $this->SubjectDirectoryAttributes = array(
            'type'     => FILE_ASN1_TYPE_SEQUENCE,
            'min'      => 1,
            'max'      => -1,
            'children' => $Attribute
        );

        // adapted from <http://tools.ietf.org/html/rfc2986>

        $Attributes = array(
            'type'     => FILE_ASN1_TYPE_SET,
            'min'      => 1,
            'max'      => -1,
            'children' => $Attribute
        );

        $CertificationRequestInfo = array(
            'type'     => FILE_ASN1_TYPE_SEQUENCE,
            'children' => array(
                'version'       => array(
                                       'type' => FILE_ASN1_TYPE_INTEGER,
                                       'mapping' => array('v1')
                                   ),
                'subject'       => $this->Name,
                'subjectPKInfo' => $SubjectPublicKeyInfo,
                'attributes'    => array(
                                       'constant' => 0,
                                       'optional' => true,
                                       'implicit' => true
                                   ) + $Attributes,
            )
        );

        $this->CertificationRequest = array(
            'type'     => FILE_ASN1_TYPE_SEQUENCE,
            'children' => array(
                'certificationRequestInfo' => $CertificationRequestInfo,
                'signatureAlgorithm'       => $AlgorithmIdentifier,
                'signature'                => array('type' => FILE_ASN1_TYPE_BIT_STRING)
            )
        );

        $RevokedCertificate = array(
            'type'     => FILE_ASN1_TYPE_SEQUENCE,
            'children' => array(
                              'userCertificate'    => $CertificateSerialNumber,
                              'revocationDate'     => $Time,
                              'crlEntryExtensions' => array(
                                                          'optional' => true
                                                      ) + $this->Extensions
                          )
        );

        $TBSCertList = array(
            'type'     => FILE_ASN1_TYPE_SEQUENCE,
            'children' => array(
                'version'             => array(
                                             'optional' => true,
                                             'default'  => 'v1'
                                         ) + $Version,
                'signature'           => $AlgorithmIdentifier,
                'issuer'              => $this->Name,
                'thisUpdate'          => $Time,
                'nextUpdate'          => array(
                                             'optional' => true
                                         ) + $Time,
                'revokedCertificates' => array(
                                             'type'     => FILE_ASN1_TYPE_SEQUENCE,
                                             'optional' => true,
                                             'min'      => 0,
                                             'max'      => -1,
                                             'children' => $RevokedCertificate
                                         ),
                'crlExtensions'       => array(
                                             'constant' => 0,
                                             'optional' => true,
                                             'explicit' => true
                                         ) + $this->Extensions
            )
        );

        $this->CertificateList = array(
            'type'     => FILE_ASN1_TYPE_SEQUENCE,
            'children' => array(
                'tbsCertList'        => $TBSCertList,
                'signatureAlgorithm' => $AlgorithmIdentifier,
                'signature'          => array('type' => FILE_ASN1_TYPE_BIT_STRING)
            )
        );

        $this->CRLNumber = array('type' => FILE_ASN1_TYPE_INTEGER);

        $this->CRLReason = array('type' => FILE_ASN1_TYPE_ENUMERATED,
           'mapping' => array(
                            'unspecified',
                            'keyCompromise',
                            'cACompromise',
                            'affiliationChanged',
                            'superseded',
                            'cessationOfOperation',
                            'certificateHold',
                            // Value 7 is not used.
                            8 => 'removeFromCRL',
                            'privilegeWithdrawn',
                            'aACompromise'
            )
        );

        $this->IssuingDistributionPoint = array('type' => FILE_ASN1_TYPE_SEQUENCE,
            'children' => array(
                'distributionPoint'          => array(
                                                    'constant' => 0,
                                                    'optional' => true,
                                                    'explicit' => true
                                                ) + $DistributionPointName,
                'onlyContainsUserCerts'      => array(
                                                    'type'     => FILE_ASN1_TYPE_BOOLEAN,
                                                    'constant' => 1,
                                                    'optional' => true,
                                                    'default'  => false,
                                                    'implicit' => true
                                                ),
                'onlyContainsCACerts'        => array(
                                                    'type'     => FILE_ASN1_TYPE_BOOLEAN,
                                                    'constant' => 2,
                                                    'optional' => true,
                                                    'default'  => false,
                                                    'implicit' => true
                                                ),
                'onlySomeReasons'           => array(
                                                    'constant' => 3,
                                                    'optional' => true,
                                                    'implicit' => true
                                                ) + $ReasonFlags,
                'indirectCRL'               => array(
                                                    'type'     => FILE_ASN1_TYPE_BOOLEAN,
                                                    'constant' => 4,
                                                    'optional' => true,
                                                    'default'  => false,
                                                    'implicit' => true
                                                ),
                'onlyContainsAttributeCerts' => array(
                                                    'type'     => FILE_ASN1_TYPE_BOOLEAN,
                                                    'constant' => 5,
                                                    'optional' => true,
                                                    'default'  => false,
                                                    'implicit' => true
                                                )
                          )
        );

        $this->InvalidityDate = array('type' => FILE_ASN1_TYPE_GENERALIZED_TIME);

        $this->CertificateIssuer = $GeneralNames;

        $this->HoldInstructionCode = array('type' => FILE_ASN1_TYPE_OBJECT_IDENTIFIER);

        $PublicKeyAndChallenge = array(
            'type'     => FILE_ASN1_TYPE_SEQUENCE,
            'children' => array(
                'spki'      => $SubjectPublicKeyInfo,
                'challenge' => array('type' => FILE_ASN1_TYPE_IA5_STRING)
            )
        );

        $this->SignedPublicKeyAndChallenge = array(
            'type'     => FILE_ASN1_TYPE_SEQUENCE,
            'children' => array(
                'publicKeyAndChallenge' => $PublicKeyAndChallenge,
                'signatureAlgorithm'    => $AlgorithmIdentifier,
                'signature'             => array('type' => FILE_ASN1_TYPE_BIT_STRING)
            )
        );

        $this->PostalAddress = array(
            'type'     => FILE_ASN1_TYPE_SEQUENCE,
            'optional' => true,
            'min'      => 1,
            'max'      => -1,
            'children' => $this->DirectoryString
        );

        // OIDs from RFC5280 and those RFCs mentioned in RFC5280#section-4.1.1.2
        $this->oids = array(
            '1.3.6.1.5.5.7' => 'id-pkix',
            '1.3.6.1.5.5.7.1' => 'id-pe',
            '1.3.6.1.5.5.7.2' => 'id-qt',
            '1.3.6.1.5.5.7.3' => 'id-kp',
            '1.3.6.1.5.5.7.48' => 'id-ad',
            '1.3.6.1.5.5.7.2.1' => 'id-qt-cps',
            '1.3.6.1.5.5.7.2.2' => 'id-qt-unotice',
            '1.3.6.1.5.5.7.48.1' =>'id-ad-ocsp',
            '1.3.6.1.5.5.7.48.2' => 'id-ad-caIssuers',
            '1.3.6.1.5.5.7.48.3' => 'id-ad-timeStamping',
            '1.3.6.1.5.5.7.48.5' => 'id-ad-caRepository',
            '2.5.4' => 'id-at',
            '2.5.4.41' => 'id-at-name',
            '2.5.4.4' => 'id-at-surname',
            '2.5.4.42' => 'id-at-givenName',
            '2.5.4.43' => 'id-at-initials',
            '2.5.4.44' => 'id-at-generationQualifier',
            '2.5.4.3' => 'id-at-commonName',
            '2.5.4.7' => 'id-at-localityName',
            '2.5.4.8' => 'id-at-stateOrProvinceName',
            '2.5.4.10' => 'id-at-organizationName',
            '2.5.4.11' => 'id-at-organizationalUnitName',
            '2.5.4.12' => 'id-at-title',
            '2.5.4.13' => 'id-at-description',
            '2.5.4.46' => 'id-at-dnQualifier',
            '2.5.4.6' => 'id-at-countryName',
            '2.5.4.5' => 'id-at-serialNumber',
            '2.5.4.65' => 'id-at-pseudonym',
            '2.5.4.17' => 'id-at-postalCode',
            '2.5.4.9' => 'id-at-streetAddress',
            '2.5.4.45' => 'id-at-uniqueIdentifier',
            '2.5.4.72' => 'id-at-role',
            '2.5.4.16' => 'id-at-postalAddress',

            '0.9.2342.19200300.100.1.25' => 'id-domainComponent',
            '1.2.840.113549.1.9' => 'pkcs-9',
            '1.2.840.113549.1.9.1' => 'pkcs-9-at-emailAddress',
            '2.5.29' => 'id-ce',
            '2.5.29.35' => 'id-ce-authorityKeyIdentifier',
            '2.5.29.14' => 'id-ce-subjectKeyIdentifier',
            '2.5.29.15' => 'id-ce-keyUsage',
            '2.5.29.16' => 'id-ce-privateKeyUsagePeriod',
            '2.5.29.32' => 'id-ce-certificatePolicies',
            '2.5.29.32.0' => 'anyPolicy',

            '2.5.29.33' => 'id-ce-policyMappings',
            '2.5.29.17' => 'id-ce-subjectAltName',
            '2.5.29.18' => 'id-ce-issuerAltName',
            '2.5.29.9' => 'id-ce-subjectDirectoryAttributes',
            '2.5.29.19' => 'id-ce-basicConstraints',
            '2.5.29.30' => 'id-ce-nameConstraints',
            '2.5.29.36' => 'id-ce-policyConstraints',
            '2.5.29.31' => 'id-ce-cRLDistributionPoints',
            '2.5.29.37' => 'id-ce-extKeyUsage',
            '2.5.29.37.0' => 'anyExtendedKeyUsage',
            '1.3.6.1.5.5.7.3.1' => 'id-kp-serverAuth',
            '1.3.6.1.5.5.7.3.2' => 'id-kp-clientAuth',
            '1.3.6.1.5.5.7.3.3' => 'id-kp-codeSigning',
            '1.3.6.1.5.5.7.3.4' => 'id-kp-emailProtection',
            '1.3.6.1.5.5.7.3.8' => 'id-kp-timeStamping',
            '1.3.6.1.5.5.7.3.9' => 'id-kp-OCSPSigning',
            '2.5.29.54' => 'id-ce-inhibitAnyPolicy',
            '2.5.29.46' => 'id-ce-freshestCRL',
            '1.3.6.1.5.5.7.1.1' => 'id-pe-authorityInfoAccess',
            '1.3.6.1.5.5.7.1.11' => 'id-pe-subjectInfoAccess',
            '2.5.29.20' => 'id-ce-cRLNumber',
            '2.5.29.28' => 'id-ce-issuingDistributionPoint',
            '2.5.29.27' => 'id-ce-deltaCRLIndicator',
            '2.5.29.21' => 'id-ce-cRLReasons',
            '2.5.29.29' => 'id-ce-certificateIssuer',
            '2.5.29.23' => 'id-ce-holdInstructionCode',
            '1.2.840.10040.2' => 'holdInstruction',
            '1.2.840.10040.2.1' => 'id-holdinstruction-none',
            '1.2.840.10040.2.2' => 'id-holdinstruction-callissuer',
            '1.2.840.10040.2.3' => 'id-holdinstruction-reject',
            '2.5.29.24' => 'id-ce-invalidityDate',

            '1.2.840.113549.2.2' => 'md2',
            '1.2.840.113549.2.5' => 'md5',
            '1.3.14.3.2.26' => 'id-sha1',
            '1.2.840.10040.4.1' => 'id-dsa',
            '1.2.840.10040.4.3' => 'id-dsa-with-sha1',
            '1.2.840.113549.1.1' => 'pkcs-1',
            '1.2.840.113549.1.1.1' => 'rsaEncryption',
            '1.2.840.113549.1.1.2' => 'md2WithRSAEncryption',
            '1.2.840.113549.1.1.4' => 'md5WithRSAEncryption',
            '1.2.840.113549.1.1.5' => 'sha1WithRSAEncryption',
            '1.2.840.10046.2.1' => 'dhpublicnumber',
            '2.16.840.1.101.2.1.1.22' => 'id-keyExchangeAlgorithm',
            '1.2.840.10045' => 'ansi-X9-62',
            '1.2.840.10045.4' => 'id-ecSigType',
            '1.2.840.10045.4.1' => 'ecdsa-with-SHA1',
            '1.2.840.10045.1' => 'id-fieldType',
            '1.2.840.10045.1.1' => 'prime-field',
            '1.2.840.10045.1.2' => 'characteristic-two-field',
            '1.2.840.10045.1.2.3' => 'id-characteristic-two-basis',
            '1.2.840.10045.1.2.3.1' => 'gnBasis',
            '1.2.840.10045.1.2.3.2' => 'tpBasis',
            '1.2.840.10045.1.2.3.3' => 'ppBasis',
            '1.2.840.10045.2' => 'id-publicKeyType',
            '1.2.840.10045.2.1' => 'id-ecPublicKey',
            '1.2.840.10045.3' => 'ellipticCurve',
            '1.2.840.10045.3.0' => 'c-TwoCurve',
            '1.2.840.10045.3.0.1' => 'c2pnb163v1',
            '1.2.840.10045.3.0.2' => 'c2pnb163v2',
            '1.2.840.10045.3.0.3' => 'c2pnb163v3',
            '1.2.840.10045.3.0.4' => 'c2pnb176w1',
            '1.2.840.10045.3.0.5' => 'c2pnb191v1',
            '1.2.840.10045.3.0.6' => 'c2pnb191v2',
            '1.2.840.10045.3.0.7' => 'c2pnb191v3',
            '1.2.840.10045.3.0.8' => 'c2pnb191v4',
            '1.2.840.10045.3.0.9' => 'c2pnb191v5',
            '1.2.840.10045.3.0.10' => 'c2pnb208w1',
            '1.2.840.10045.3.0.11' => 'c2pnb239v1',
            '1.2.840.10045.3.0.12' => 'c2pnb239v2',
            '1.2.840.10045.3.0.13' => 'c2pnb239v3',
            '1.2.840.10045.3.0.14' => 'c2pnb239v4',
            '1.2.840.10045.3.0.15' => 'c2pnb239v5',
            '1.2.840.10045.3.0.16' => 'c2pnb272w1',
            '1.2.840.10045.3.0.17' => 'c2pnb304w1',
            '1.2.840.10045.3.0.18' => 'c2pnb359v1',
            '1.2.840.10045.3.0.19' => 'c2pnb368w1',
            '1.2.840.10045.3.0.20' => 'c2pnb431r1',
            '1.2.840.10045.3.1' => 'primeCurve',
            '1.2.840.10045.3.1.1' => 'prime192v1',
            '1.2.840.10045.3.1.2' => 'prime192v2',
            '1.2.840.10045.3.1.3' => 'prime192v3',
            '1.2.840.10045.3.1.4' => 'prime239v1',
            '1.2.840.10045.3.1.5' => 'prime239v2',
            '1.2.840.10045.3.1.6' => 'prime239v3',
            '1.2.840.10045.3.1.7' => 'prime256v1',
            '1.2.840.113549.1.1.7' => 'id-RSAES-OAEP',
            '1.2.840.113549.1.1.9' => 'id-pSpecified',
            '1.2.840.113549.1.1.10' => 'id-RSASSA-PSS',
            '1.2.840.113549.1.1.8' => 'id-mgf1',
            '1.2.840.113549.1.1.14' => 'sha224WithRSAEncryption',
            '1.2.840.113549.1.1.11' => 'sha256WithRSAEncryption',
            '1.2.840.113549.1.1.12' => 'sha384WithRSAEncryption',
            '1.2.840.113549.1.1.13' => 'sha512WithRSAEncryption',
            '2.16.840.1.101.3.4.2.4' => 'id-sha224',
            '2.16.840.1.101.3.4.2.1' => 'id-sha256',
            '2.16.840.1.101.3.4.2.2' => 'id-sha384',
            '2.16.840.1.101.3.4.2.3' => 'id-sha512',
            '1.2.643.2.2.4' => 'id-GostR3411-94-with-GostR3410-94',
            '1.2.643.2.2.3' => 'id-GostR3411-94-with-GostR3410-2001',
            '1.2.643.2.2.20' => 'id-GostR3410-2001',
            '1.2.643.2.2.19' => 'id-GostR3410-94',
            // Netscape Object Identifiers from "Netscape Certificate Extensions"
            '2.16.840.1.113730' => 'netscape',
            '2.16.840.1.113730.1' => 'netscape-cert-extension',
            '2.16.840.1.113730.1.1' => 'netscape-cert-type',
            '2.16.840.1.113730.1.13' => 'netscape-comment',
            '2.16.840.1.113730.1.8' => 'netscape-ca-policy-url',
            // the following are X.509 extensions not supported by phpseclib
            '1.3.6.1.5.5.7.1.12' => 'id-pe-logotype',
            '1.2.840.113533.7.65.0' => 'entrustVersInfo',
            '2.16.840.1.113733.1.6.9' => 'verisignPrivate',
            // for Certificate Signing Requests
            // see http://tools.ietf.org/html/rfc2985
            '1.2.840.113549.1.9.2' => 'pkcs-9-at-unstructuredName', // PKCS #9 unstructured name
            '1.2.840.113549.1.9.7' => 'pkcs-9-at-challengePassword', // Challenge password for certificate revocations
            '1.2.840.113549.1.9.14' => 'pkcs-9-at-extensionRequest' // Certificate extension request
        );
    }

    /**
     * PHP4 compatible Default Constructor.
     *
     * @see self::__construct()
     * @access public
     */
    function File_X509()
    {
        $this->__construct();
    }

    /**
     * Load X.509 certificate
     *
     * Returns an associative array describing the X.509 cert or a false if the cert failed to load
     *
     * @param string $cert
     * @param int $mode
     * @access public
     * @return mixed
     */
    function loadX509($cert, $mode = FILE_X509_FORMAT_AUTO_DETECT)
    {
        if (is_array($cert) && isset($cert['tbsCertificate'])) {
            unset($this->currentCert);
            unset($this->currentKeyIdentifier);
            $this->dn = $cert['tbsCertificate']['subject'];
            if (!isset($this->dn)) {
                return false;
            }
            $this->currentCert = $cert;

            $currentKeyIdentifier = $this->getExtension('id-ce-subjectKeyIdentifier');
            $this->currentKeyIdentifier = is_string($currentKeyIdentifier) ? $currentKeyIdentifier : null;

            unset($this->signatureSubject);

            return $cert;
        }

        $asn1 = new File_ASN1();

        if ($mode != FILE_X509_FORMAT_DER) {
            $newcert = $this->_extractBER($cert);
            if ($mode == FILE_X509_FORMAT_PEM && $cert == $newcert) {
                return false;
            }
            $cert = $newcert;
        }

        if ($cert === false) {
            $this->currentCert = false;
            return false;
        }

        $asn1->loadOIDs($this->oids);
        $decoded = $asn1->decodeBER($cert);

        if (!empty($decoded)) {
            $x509 = $asn1->asn1map($decoded[0], $this->Certificate);
        }
        if (!isset($x509) || $x509 === false) {
            $this->currentCert = false;
            return false;
        }

        $this->signatureSubject = substr($cert, $decoded[0]['content'][0]['start'], $decoded[0]['content'][0]['length']);

        if ($this->_isSubArrayValid($x509, 'tbsCertificate/extensions')) {
            $this->_mapInExtensions($x509, 'tbsCertificate/extensions', $asn1);
        }
        $this->_mapInDNs($x509, 'tbsCertificate/issuer/rdnSequence', $asn1);
        $this->_mapInDNs($x509, 'tbsCertificate/subject/rdnSequence', $asn1);

        $key = &$x509['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey'];
        $key = $this->_reformatKey($x509['tbsCertificate']['subjectPublicKeyInfo']['algorithm']['algorithm'], $key);

        $this->currentCert = $x509;
        $this->dn = $x509['tbsCertificate']['subject'];

        $currentKeyIdentifier = $this->getExtension('id-ce-subjectKeyIdentifier');
        $this->currentKeyIdentifier = is_string($currentKeyIdentifier) ? $currentKeyIdentifier : null;

        return $x509;
    }

    /**
     * Save X.509 certificate
     *
     * @param array $cert
     * @param int $format optional
     * @access public
     * @return string
     */
    function saveX509($cert, $format = FILE_X509_FORMAT_PEM)
    {
        if (!is_array($cert) || !isset($cert['tbsCertificate'])) {
            return false;
        }

        switch (true) {
            // "case !$a: case !$b: break; default: whatever();" is the same thing as "if ($a && $b) whatever()"
            case !($algorithm = $this->_subArray($cert, 'tbsCertificate/subjectPublicKeyInfo/algorithm/algorithm')):
            case is_object($cert['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey']):
                break;
            default:
                switch ($algorithm) {
                    case 'rsaEncryption':
                        $cert['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey']
                            = base64_encode("\0" . base64_decode(preg_replace('#-.+-|[\r\n]#', '', $cert['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey'])));
                        /* "[For RSA keys] the parameters field MUST have ASN.1 type NULL for this algorithm identifier."
                           -- https://tools.ietf.org/html/rfc3279#section-2.3.1

                           given that and the fact that RSA keys appear ot be the only key type for which the parameters field can be blank,
                           it seems like perhaps the ASN.1 description ought not say the parameters field is OPTIONAL, but whatever.
                         */
                        $cert['tbsCertificate']['subjectPublicKeyInfo']['algorithm']['parameters'] = null;
                        // https://tools.ietf.org/html/rfc3279#section-2.2.1
                        $cert['signatureAlgorithm']['parameters'] = null;
                        $cert['tbsCertificate']['signature']['parameters'] = null;
                }
        }

        $asn1 = new File_ASN1();
        $asn1->loadOIDs($this->oids);

        $filters = array();
        $type_utf8_string = array('type' => FILE_ASN1_TYPE_UTF8_STRING);
        $filters['tbsCertificate']['signature']['parameters'] = $type_utf8_string;
        $filters['tbsCertificate']['signature']['issuer']['rdnSequence']['value'] = $type_utf8_string;
        $filters['tbsCertificate']['issuer']['rdnSequence']['value'] = $type_utf8_string;
        $filters['tbsCertificate']['subject']['rdnSequence']['value'] = $type_utf8_string;
        $filters['tbsCertificate']['subjectPublicKeyInfo']['algorithm']['parameters'] = $type_utf8_string;
        $filters['signatureAlgorithm']['parameters'] = $type_utf8_string;
        $filters['authorityCertIssuer']['directoryName']['rdnSequence']['value'] = $type_utf8_string;
        //$filters['policyQualifiers']['qualifier'] = $type_utf8_string;
        $filters['distributionPoint']['fullName']['directoryName']['rdnSequence']['value'] = $type_utf8_string;
        $filters['directoryName']['rdnSequence']['value'] = $type_utf8_string;

        /* in the case of policyQualifiers/qualifier, the type has to be FILE_ASN1_TYPE_IA5_STRING.
           FILE_ASN1_TYPE_PRINTABLE_STRING will cause OpenSSL's X.509 parser to spit out random
           characters.
         */
        $filters['policyQualifiers']['qualifier']
            = array('type' => FILE_ASN1_TYPE_IA5_STRING);

        $asn1->loadFilters($filters);

        $this->_mapOutExtensions($cert, 'tbsCertificate/extensions', $asn1);
        $this->_mapOutDNs($cert, 'tbsCertificate/issuer/rdnSequence', $asn1);
        $this->_mapOutDNs($cert, 'tbsCertificate/subject/rdnSequence', $asn1);

        $cert = $asn1->encodeDER($cert, $this->Certificate);

        switch ($format) {
            case FILE_X509_FORMAT_DER:
                return $cert;
            // case FILE_X509_FORMAT_PEM:
            default:
                return "-----BEGIN CERTIFICATE-----\r\n" . chunk_split(base64_encode($cert), 64) . '-----END CERTIFICATE-----';
        }
    }

    /**
     * Map extension values from octet string to extension-specific internal
     *   format.
     *
     * @param array ref $root
     * @param string $path
     * @param object $asn1
     * @access private
     */
    function _mapInExtensions(&$root, $path, $asn1)
    {
        $extensions = &$this->_subArrayUnchecked($root, $path);

        if ($extensions) {
            for ($i = 0; $i < count($extensions); $i++) {
                $id = $extensions[$i]['extnId'];
                $value = &$extensions[$i]['extnValue'];
                $value = base64_decode($value);
                $decoded = $asn1->decodeBER($value);
                /* [extnValue] contains the DER encoding of an ASN.1 value
                   corresponding to the extension type identified by extnID */
                $map = $this->_getMapping($id);
                if (!is_bool($map)) {
                    $mapped = $asn1->asn1map($decoded[0], $map, array('iPAddress' => array($this, '_decodeIP')));
                    $value = $mapped === false ? $decoded[0] : $mapped;

                    if ($id == 'id-ce-certificatePolicies') {
                        for ($j = 0; $j < count($value); $j++) {
                            if (!isset($value[$j]['policyQualifiers'])) {
                                continue;
                            }
                            for ($k = 0; $k < count($value[$j]['policyQualifiers']); $k++) {
                                $subid = $value[$j]['policyQualifiers'][$k]['policyQualifierId'];
                                $map = $this->_getMapping($subid);
                                $subvalue = &$value[$j]['policyQualifiers'][$k]['qualifier'];
                                if ($map !== false) {
                                    $decoded = $asn1->decodeBER($subvalue);
                                    $mapped = $asn1->asn1map($decoded[0], $map);
                                    $subvalue = $mapped === false ? $decoded[0] : $mapped;
                                }
                            }
                        }
                    }
                } else {
                    $value = base64_encode($value);
                }
            }
        }
    }

    /**
     * Map extension values from extension-specific internal format to
     *   octet string.
     *
     * @param array ref $root
     * @param string $path
     * @param object $asn1
     * @access private
     */
    function _mapOutExtensions(&$root, $path, $asn1)
    {
        $extensions = &$this->_subArray($root, $path);

        if (is_array($extensions)) {
            $size = count($extensions);
            for ($i = 0; $i < $size; $i++) {
                if (is_object($extensions[$i]) && strtolower(get_class($extensions[$i])) == 'file_asn1_element') {
                    continue;
                }

                $id = $extensions[$i]['extnId'];
                $value = &$extensions[$i]['extnValue'];

                switch ($id) {
                    case 'id-ce-certificatePolicies':
                        for ($j = 0; $j < count($value); $j++) {
                            if (!isset($value[$j]['policyQualifiers'])) {
                                continue;
                            }
                            for ($k = 0; $k < count($value[$j]['policyQualifiers']); $k++) {
                                $subid = $value[$j]['policyQualifiers'][$k]['policyQualifierId'];
                                $map = $this->_getMapping($subid);
                                $subvalue = &$value[$j]['policyQualifiers'][$k]['qualifier'];
                                if ($map !== false) {
                                    // by default File_ASN1 will try to render qualifier as a FILE_ASN1_TYPE_IA5_STRING since it's
                                    // actual type is FILE_ASN1_TYPE_ANY
                                    $subvalue = new File_ASN1_Element($asn1->encodeDER($subvalue, $map));
                                }
                            }
                        }
                        break;
                    case 'id-ce-authorityKeyIdentifier': // use 00 as the serial number instead of an empty string
                        if (isset($value['authorityCertSerialNumber'])) {
                            if ($value['authorityCertSerialNumber']->toBytes() == '') {
                                $temp = chr((FILE_ASN1_CLASS_CONTEXT_SPECIFIC << 6) | 2) . "\1\0";
                                $value['authorityCertSerialNumber'] = new File_ASN1_Element($temp);
                            }
                        }
                }

                /* [extnValue] contains the DER encoding of an ASN.1 value
                   corresponding to the extension type identified by extnID */
                $map = $this->_getMapping($id);
                if (is_bool($map)) {
                    if (!$map) {
                        user_error($id . ' is not a currently supported extension');
                        unset($extensions[$i]);
                    }
                } else {
                    $temp = $asn1->encodeDER($value, $map, array('iPAddress' => array($this, '_encodeIP')));
                    $value = base64_encode($temp);
                }
            }
        }
    }

    /**
     * Map attribute values from ANY type to attribute-specific internal
     *   format.
     *
     * @param array ref $root
     * @param string $path
     * @param object $asn1
     * @access private
     */
    function _mapInAttributes(&$root, $path, $asn1)
    {
        $attributes = &$this->_subArray($root, $path);

        if (is_array($attributes)) {
            for ($i = 0; $i < count($attributes); $i++) {
                $id = $attributes[$i]['type'];
                /* $value contains the DER encoding of an ASN.1 value
                   corresponding to the attribute type identified by type */
                $map = $this->_getMapping($id);
                if (is_array($attributes[$i]['value'])) {
                    $values = &$attributes[$i]['value'];
                    for ($j = 0; $j < count($values); $j++) {
                        $value = $asn1->encodeDER($values[$j], $this->AttributeValue);
                        $decoded = $asn1->decodeBER($value);
                        if (!is_bool($map)) {
                            $mapped = $asn1->asn1map($decoded[0], $map);
                            if ($mapped !== false) {
                                $values[$j] = $mapped;
                            }
                            if ($id == 'pkcs-9-at-extensionRequest' && $this->_isSubArrayValid($values, $j)) {
                                $this->_mapInExtensions($values, $j, $asn1);
                            }
                        } elseif ($map) {
                            $values[$j] = base64_encode($value);
                        }
                    }
                }
            }
        }
    }

    /**
     * Map attribute values from attribute-specific internal format to
     *   ANY type.
     *
     * @param array ref $root
     * @param string $path
     * @param object $asn1
     * @access private
     */
    function _mapOutAttributes(&$root, $path, $asn1)
    {
        $attributes = &$this->_subArray($root, $path);

        if (is_array($attributes)) {
            $size = count($attributes);
            for ($i = 0; $i < $size; $i++) {
                /* [value] contains the DER encoding of an ASN.1 value
                   corresponding to the attribute type identified by type */
                $id = $attributes[$i]['type'];
                $map = $this->_getMapping($id);
                if ($map === false) {
                    user_error($id . ' is not a currently supported attribute', E_USER_NOTICE);
                    unset($attributes[$i]);
                } elseif (is_array($attributes[$i]['value'])) {
                    $values = &$attributes[$i]['value'];
                    for ($j = 0; $j < count($values); $j++) {
                        switch ($id) {
                            case 'pkcs-9-at-extensionRequest':
                                $this->_mapOutExtensions($values, $j, $asn1);
                                break;
                        }

                        if (!is_bool($map)) {
                            $temp = $asn1->encodeDER($values[$j], $map);
                            $decoded = $asn1->decodeBER($temp);
                            $values[$j] = $asn1->asn1map($decoded[0], $this->AttributeValue);
                        }
                    }
                }
            }
        }
    }

    /**
     * Map DN values from ANY type to DN-specific internal
     *   format.
     *
     * @param array ref $root
     * @param string $path
     * @param object $asn1
     * @access private
     */
    function _mapInDNs(&$root, $path, $asn1)
    {
        $dns = &$this->_subArray($root, $path);

        if (is_array($dns)) {
            for ($i = 0; $i < count($dns); $i++) {
                for ($j = 0; $j < count($dns[$i]); $j++) {
                    $type = $dns[$i][$j]['type'];
                    $value = &$dns[$i][$j]['value'];
                    if (is_object($value) && strtolower(get_class($value)) == 'file_asn1_element') {
                        $map = $this->_getMapping($type);
                        if (!is_bool($map)) {
                            $decoded = $asn1->decodeBER($value);
                            $value = $asn1->asn1map($decoded[0], $map);
                        }
                    }
                }
            }
        }
    }

    /**
     * Map DN values from DN-specific internal format to
     *   ANY type.
     *
     * @param array ref $root
     * @param string $path
     * @param object $asn1
     * @access private
     */
    function _mapOutDNs(&$root, $path, $asn1)
    {
        $dns = &$this->_subArray($root, $path);

        if (is_array($dns)) {
            $size = count($dns);
            for ($i = 0; $i < $size; $i++) {
                for ($j = 0; $j < count($dns[$i]); $j++) {
                    $type = $dns[$i][$j]['type'];
                    $value = &$dns[$i][$j]['value'];
                    if (is_object($value) && strtolower(get_class($value)) == 'file_asn1_element') {
                        continue;
                    }

                    $map = $this->_getMapping($type);
                    if (!is_bool($map)) {
                        $value = new File_ASN1_Element($asn1->encodeDER($value, $map));
                    }
                }
            }
        }
    }

    /**
     * Associate an extension ID to an extension mapping
     *
     * @param string $extnId
     * @access private
     * @return mixed
     */
    function _getMapping($extnId)
    {
        if (!is_string($extnId)) { // eg. if it's a File_ASN1_Element object
            return true;
        }

        switch ($extnId) {
            case 'id-ce-keyUsage':
                return $this->KeyUsage;
            case 'id-ce-basicConstraints':
                return $this->BasicConstraints;
            case 'id-ce-subjectKeyIdentifier':
                return $this->KeyIdentifier;
            case 'id-ce-cRLDistributionPoints':
                return $this->CRLDistributionPoints;
            case 'id-ce-authorityKeyIdentifier':
                return $this->AuthorityKeyIdentifier;
            case 'id-ce-certificatePolicies':
                return $this->CertificatePolicies;
            case 'id-ce-extKeyUsage':
                return $this->ExtKeyUsageSyntax;
            case 'id-pe-authorityInfoAccess':
                return $this->AuthorityInfoAccessSyntax;
            case 'id-ce-subjectAltName':
                return $this->SubjectAltName;
            case 'id-ce-subjectDirectoryAttributes':
                return $this->SubjectDirectoryAttributes;
            case 'id-ce-privateKeyUsagePeriod':
                return $this->PrivateKeyUsagePeriod;
            case 'id-ce-issuerAltName':
                return $this->IssuerAltName;
            case 'id-ce-policyMappings':
                return $this->PolicyMappings;
            case 'id-ce-nameConstraints':
                return $this->NameConstraints;

            case 'netscape-cert-type':
                return $this->netscape_cert_type;
            case 'netscape-comment':
                return $this->netscape_comment;
            case 'netscape-ca-policy-url':
                return $this->netscape_ca_policy_url;

            // since id-qt-cps isn't a constructed type it will have already been decoded as a string by the time it gets
            // back around to asn1map() and we don't want it decoded again.
            //case 'id-qt-cps':
            //    return $this->CPSuri;
            case 'id-qt-unotice':
                return $this->UserNotice;

            // the following OIDs are unsupported but we don't want them to give notices when calling saveX509().
            case 'id-pe-logotype': // http://www.ietf.org/rfc/rfc3709.txt
            case 'entrustVersInfo':
            // http://support.microsoft.com/kb/287547
            case '1.3.6.1.4.1.311.20.2': // szOID_ENROLL_CERTTYPE_EXTENSION
            case '1.3.6.1.4.1.311.21.1': // szOID_CERTSRV_CA_VERSION
            // "SET Secure Electronic Transaction Specification"
            // http://www.maithean.com/docs/set_bk3.pdf
            case '2.23.42.7.0': // id-set-hashedRootKey
            // "Certificate Transparency"
            // https://tools.ietf.org/html/rfc6962
            case '1.3.6.1.4.1.11129.2.4.2':
                return true;

            // CSR attributes
            case 'pkcs-9-at-unstructuredName':
                return $this->PKCS9String;
            case 'pkcs-9-at-challengePassword':
                return $this->DirectoryString;
            case 'pkcs-9-at-extensionRequest':
                return $this->Extensions;

            // CRL extensions.
            case 'id-ce-cRLNumber':
                return $this->CRLNumber;
            case 'id-ce-deltaCRLIndicator':
                return $this->CRLNumber;
            case 'id-ce-issuingDistributionPoint':
                return $this->IssuingDistributionPoint;
            case 'id-ce-freshestCRL':
                return $this->CRLDistributionPoints;
            case 'id-ce-cRLReasons':
                return $this->CRLReason;
            case 'id-ce-invalidityDate':
                return $this->InvalidityDate;
            case 'id-ce-certificateIssuer':
                return $this->CertificateIssuer;
            case 'id-ce-holdInstructionCode':
                return $this->HoldInstructionCode;
            case 'id-at-postalAddress':
                return $this->PostalAddress;
        }

        return false;
    }

    /**
     * Load an X.509 certificate as a certificate authority
     *
     * @param string $cert
     * @access public
     * @return bool
     */
    function loadCA($cert)
    {
        $olddn = $this->dn;
        $oldcert = $this->currentCert;
        $oldsigsubj = $this->signatureSubject;
        $oldkeyid = $this->currentKeyIdentifier;

        $cert = $this->loadX509($cert);
        if (!$cert) {
            $this->dn = $olddn;
            $this->currentCert = $oldcert;
            $this->signatureSubject = $oldsigsubj;
            $this->currentKeyIdentifier = $oldkeyid;

            return false;
        }

        /* From RFC5280 "PKIX Certificate and CRL Profile":

           If the keyUsage extension is present, then the subject public key
           MUST NOT be used to verify signatures on certificates or CRLs unless
           the corresponding keyCertSign or cRLSign bit is set. */
        //$keyUsage = $this->getExtension('id-ce-keyUsage');
        //if ($keyUsage && !in_array('keyCertSign', $keyUsage)) {
        //    return false;
        //}

        /* From RFC5280 "PKIX Certificate and CRL Profile":

           The cA boolean indicates whether the certified public key may be used
           to verify certificate signatures.  If the cA boolean is not asserted,
           then the keyCertSign bit in the key usage extension MUST NOT be
           asserted.  If the basic constraints extension is not present in a
           version 3 certificate, or the extension is present but the cA boolean
           is not asserted, then the certified public key MUST NOT be used to
           verify certificate signatures. */
        //$basicConstraints = $this->getExtension('id-ce-basicConstraints');
        //if (!$basicConstraints || !$basicConstraints['cA']) {
        //    return false;
        //}

        $this->CAs[] = $cert;

        $this->dn = $olddn;
        $this->currentCert = $oldcert;
        $this->signatureSubject = $oldsigsubj;

        return true;
    }

    /**
     * Validate an X.509 certificate against a URL
     *
     * From RFC2818 "HTTP over TLS":
     *
     * Matching is performed using the matching rules specified by
     * [RFC2459].  If more than one identity of a given type is present in
     * the certificate (e.g., more than one dNSName name, a match in any one
     * of the set is considered acceptable.) Names may contain the wildcard
     * character * which is considered to match any single domain name
     * component or component fragment. E.g., *.a.com matches foo.a.com but
     * not bar.foo.a.com. f*.com matches foo.com but not bar.com.
     *
     * @param string $url
     * @access public
     * @return bool
     */
    function validateURL($url)
    {
        if (!is_array($this->currentCert) || !isset($this->currentCert['tbsCertificate'])) {
            return false;
        }

        $components = parse_url($url);
        if (!isset($components['host'])) {
            return false;
        }

        if ($names = $this->getExtension('id-ce-subjectAltName')) {
            foreach ($names as $name) {
                foreach ($name as $key => $value) {
                    $value = str_replace(array('.', '*'), array('\.', '[^.]*'), $value);
                    switch ($key) {
                        case 'dNSName':
                            /* From RFC2818 "HTTP over TLS":

                               If a subjectAltName extension of type dNSName is present, that MUST
                               be used as the identity. Otherwise, the (most specific) Common Name
                               field in the Subject field of the certificate MUST be used. Although
                               the use of the Common Name is existing practice, it is deprecated and
                               Certification Authorities are encouraged to use the dNSName instead. */
                            if (preg_match('#^' . $value . '$#', $components['host'])) {
                                return true;
                            }
                            break;
                        case 'iPAddress':
                            /* From RFC2818 "HTTP over TLS":

                               In some cases, the URI is specified as an IP address rather than a
                               hostname. In this case, the iPAddress subjectAltName must be present
                               in the certificate and must exactly match the IP in the URI. */
                            if (preg_match('#(?:\d{1-3}\.){4}#', $components['host'] . '.') && preg_match('#^' . $value . '$#', $components['host'])) {
                                return true;
                            }
                    }
                }
            }
            return false;
        }

        if ($value = $this->getDNProp('id-at-commonName')) {
            $value = str_replace(array('.', '*'), array('\.', '[^.]*'), $value[0]);
            return preg_match('#^' . $value . '$#', $components['host']);
        }

        return false;
    }

    /**
     * Validate a date
     *
     * If $date isn't defined it is assumed to be the current date.
     *
     * @param \DateTime|int|string $date optional
     * @access public
     */
    function validateDate($date = null)
    {
        if (!is_array($this->currentCert) || !isset($this->currentCert['tbsCertificate'])) {
            return false;
        }

        if (!isset($date)) {
            $date = class_exists('DateTime') ?
                new DateTime(null, new DateTimeZone(@date_default_timezone_get())) :
                time();
        }

        $notBefore = $this->currentCert['tbsCertificate']['validity']['notBefore'];
        $notBefore = isset($notBefore['generalTime']) ? $notBefore['generalTime'] : $notBefore['utcTime'];

        $notAfter = $this->currentCert['tbsCertificate']['validity']['notAfter'];
        $notAfter = isset($notAfter['generalTime']) ? $notAfter['generalTime'] : $notAfter['utcTime'];

        switch (true) {
            case is_string($date) && class_exists('DateTime'):
                $date = new DateTime($date, new DateTimeZone(@date_default_timezone_get()));
            case is_object($date) && strtolower(get_class($date)) == 'datetime':
                $notBefore = new DateTime($notBefore, new DateTimeZone(@date_default_timezone_get()));
                $notAfter = new DateTime($notAfter, new DateTimeZone(@date_default_timezone_get()));
                break;
            case is_string($date):
                $date = @strtotime($date);
            default:
                $notBefore = @strtotime($notBefore);
                $notAfter = @strtotime($notAfter);
        }

        switch (true) {
            case $date < $notBefore:
            case $date > $notAfter:
                return false;
        }

        return true;
    }

    /**
     * Fetches a URL
     *
     * @param string $url
     * @access private
     * @return bool|string
     */
    function _fetchURL($url)
    {
        if ($this->disable_url_fetch) {
            return false;
        }

        $parts = parse_url($url);
        $data = '';
        switch ($parts['scheme']) {
            case 'http':
                $fsock = @fsockopen($parts['host'], isset($parts['port']) ? $parts['port'] : 80);
                if (!$fsock) {
                    return false;
                }
                fputs($fsock, "GET $parts[path] HTTP/1.0\r\n");
                fputs($fsock, "Host: $parts[host]\r\n\r\n");
                $line = fgets($fsock, 1024);
                if (strlen($line) < 3) {
                    return false;
                }
                preg_match('#HTTP/1.\d (\d{3})#', $line, $temp);
                if ($temp[1] != '200') {
                    return false;
                }

                // skip the rest of the headers in the http response
                while (!feof($fsock) && fgets($fsock, 1024) != "\r\n") {
                }

                while (!feof($fsock)) {
                    $data.= fread($fsock, 1024);
                }

                break;
            //case 'ftp':
            //case 'ldap':
            //default:
        }

        return $data;
    }

    /**
     * Validates an intermediate cert as identified via authority info access extension
     *
     * See https://tools.ietf.org/html/rfc4325 for more info
     *
     * @param bool $caonly
     * @param int $count
     * @access private
     * @return bool
     */
    function _testForIntermediate($caonly, $count)
    {
        $opts = $this->getExtension('id-pe-authorityInfoAccess');
        if (!is_array($opts)) {
            return false;
        }
        foreach ($opts as $opt) {
            if ($opt['accessMethod'] == 'id-ad-caIssuers') {
                // accessLocation is a GeneralName. GeneralName fields support stuff like email addresses, IP addresses, LDAP,
                // etc, but we're only supporting URI's. URI's and LDAP are the only thing https://tools.ietf.org/html/rfc4325
                // discusses
                if (isset($opt['accessLocation']['uniformResourceIdentifier'])) {
                    $url = $opt['accessLocation']['uniformResourceIdentifier'];
                    break;
                }
            }
        }

        if (!isset($url)) {
            return false;
        }

        $cert = $this->_fetchURL($url);
        if (!is_string($cert)) {
            return false;
        }

        $parent = new static();
        $parent->CAs = $this->CAs;
        /*
         "Conforming applications that support HTTP or FTP for accessing
          certificates MUST be able to accept .cer files and SHOULD be able
          to accept .p7c files." -- https://tools.ietf.org/html/rfc4325

         A .p7c file is 'a "certs-only" CMS message as specified in RFC 2797"

         These are currently unsupported
        */
        if (!is_array($parent->loadX509($cert))) {
            return false;
        }

        if (!$parent->_validateSignatureCountable($caonly, ++$count)) {
            return false;
        }

        $this->CAs[] = $parent->currentCert;
        //$this->loadCA($cert);

        return true;
    }

    /**
     * Validate a signature
     *
     * Works on X.509 certs, CSR's and CRL's.
     * Returns true if the signature is verified, false if it is not correct or null on error
     *
     * By default returns false for self-signed certs. Call validateSignature(false) to make this support
     * self-signed.
     *
     * The behavior of this function is inspired by {@link http://php.net/openssl-verify openssl_verify}.
     *
     * @param bool $caonly optional
     * @access public
     * @return mixed
     */
    function validateSignature($caonly = true)
    {
        return $this->_validateSignatureCountable($caonly, 0);
    }

    /**
     * Validate a signature
     *
     * Performs said validation whilst keeping track of how many times validation method is called
     *
     * @param bool $caonly
     * @param int $count
     * @access private
     * @return mixed
     */
    function _validateSignatureCountable($caonly, $count)
    {
        if (!is_array($this->currentCert) || !isset($this->signatureSubject)) {
            return null;
        }

        if ($count == $this->recur_limit) {
            return false;
        }

        /* TODO:
           "emailAddress attribute values are not case-sensitive (e.g., "subscriber@example.com" is the same as "SUBSCRIBER@EXAMPLE.COM")."
            -- http://tools.ietf.org/html/rfc5280#section-4.1.2.6

           implement pathLenConstraint in the id-ce-basicConstraints extension */

        switch (true) {
            case isset($this->currentCert['tbsCertificate']):
                // self-signed cert
                switch (true) {
                    case !defined('FILE_X509_IGNORE_TYPE') && $this->currentCert['tbsCertificate']['issuer'] === $this->currentCert['tbsCertificate']['subject']:
                    case defined('FILE_X509_IGNORE_TYPE') && $this->getIssuerDN(FILE_X509_DN_STRING) === $this->getDN(FILE_X509_DN_STRING):
                        $authorityKey = $this->getExtension('id-ce-authorityKeyIdentifier');
                        $subjectKeyID = $this->getExtension('id-ce-subjectKeyIdentifier');
                        switch (true) {
                            case !is_array($authorityKey):
                            case !$subjectKeyID:
                            case isset($authorityKey['keyIdentifier']) && $authorityKey['keyIdentifier'] === $subjectKeyID:
                                $signingCert = $this->currentCert; // working cert
                        }
                }

                if (!empty($this->CAs)) {
                    for ($i = 0; $i < count($this->CAs); $i++) {
                        // even if the cert is a self-signed one we still want to see if it's a CA;
                        // if not, we'll conditionally return an error
                        $ca = $this->CAs[$i];
                        switch (true) {
                            case !defined('FILE_X509_IGNORE_TYPE') && $this->currentCert['tbsCertificate']['issuer'] === $ca['tbsCertificate']['subject']:
                            case defined('FILE_X509_IGNORE_TYPE') && $this->getDN(FILE_X509_DN_STRING, $this->currentCert['tbsCertificate']['issuer']) === $this->getDN(FILE_X509_DN_STRING, $ca['tbsCertificate']['subject']):
                                $authorityKey = $this->getExtension('id-ce-authorityKeyIdentifier');
                                $subjectKeyID = $this->getExtension('id-ce-subjectKeyIdentifier', $ca);
                                switch (true) {
                                    case !is_array($authorityKey):
                                    case !$subjectKeyID:
                                    case isset($authorityKey['keyIdentifier']) && $authorityKey['keyIdentifier'] === $subjectKeyID:
                                        if (is_array($authorityKey) && isset($authorityKey['authorityCertSerialNumber']) && !$authorityKey['authorityCertSerialNumber']->equals($ca['tbsCertificate']['serialNumber'])) {
                                            break 2; // serial mismatch - check other ca
                                        }
                                        $signingCert = $ca; // working cert
                                        break 3;
                                }
                        }
                    }
                    if (count($this->CAs) == $i && $caonly) {
                        return $this->_testForIntermediate($caonly, $count) && $this->validateSignature($caonly);
                    }
                } elseif (!isset($signingCert) || $caonly) {
                    return $this->_testForIntermediate($caonly, $count) && $this->validateSignature($caonly);
                }
                return $this->_validateSignature(
                    $signingCert['tbsCertificate']['subjectPublicKeyInfo']['algorithm']['algorithm'],
                    $signingCert['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey'],
                    $this->currentCert['signatureAlgorithm']['algorithm'],
                    substr(base64_decode($this->currentCert['signature']), 1),
                    $this->signatureSubject
                );
            case isset($this->currentCert['certificationRequestInfo']):
                return $this->_validateSignature(
                    $this->currentCert['certificationRequestInfo']['subjectPKInfo']['algorithm']['algorithm'],
                    $this->currentCert['certificationRequestInfo']['subjectPKInfo']['subjectPublicKey'],
                    $this->currentCert['signatureAlgorithm']['algorithm'],
                    substr(base64_decode($this->currentCert['signature']), 1),
                    $this->signatureSubject
                );
            case isset($this->currentCert['publicKeyAndChallenge']):
                return $this->_validateSignature(
                    $this->currentCert['publicKeyAndChallenge']['spki']['algorithm']['algorithm'],
                    $this->currentCert['publicKeyAndChallenge']['spki']['subjectPublicKey'],
                    $this->currentCert['signatureAlgorithm']['algorithm'],
                    substr(base64_decode($this->currentCert['signature']), 1),
                    $this->signatureSubject
                );
            case isset($this->currentCert['tbsCertList']):
                if (!empty($this->CAs)) {
                    for ($i = 0; $i < count($this->CAs); $i++) {
                        $ca = $this->CAs[$i];
                        switch (true) {
                            case !defined('FILE_X509_IGNORE_TYPE') && $this->currentCert['tbsCertList']['issuer'] === $ca['tbsCertificate']['subject']:
                            case defined('FILE_X509_IGNORE_TYPE') && $this->getDN(FILE_X509_DN_STRING, $this->currentCert['tbsCertList']['issuer']) === $this->getDN(FILE_X509_DN_STRING, $ca['tbsCertificate']['subject']):
                                $authorityKey = $this->getExtension('id-ce-authorityKeyIdentifier');
                                $subjectKeyID = $this->getExtension('id-ce-subjectKeyIdentifier', $ca);
                                switch (true) {
                                    case !is_array($authorityKey):
                                    case !$subjectKeyID:
                                    case isset($authorityKey['keyIdentifier']) && $authorityKey['keyIdentifier'] === $subjectKeyID:
                                        if (is_array($authorityKey) && isset($authorityKey['authorityCertSerialNumber']) && !$authorityKey['authorityCertSerialNumber']->equals($ca['tbsCertificate']['serialNumber'])) {
                                            break 2; // serial mismatch - check other ca
                                        }
                                        $signingCert = $ca; // working cert
                                        break 3;
                                }
                        }
                    }
                }
                if (!isset($signingCert)) {
                    return false;
                }
                return $this->_validateSignature(
                    $signingCert['tbsCertificate']['subjectPublicKeyInfo']['algorithm']['algorithm'],
                    $signingCert['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey'],
                    $this->currentCert['signatureAlgorithm']['algorithm'],
                    substr(base64_decode($this->currentCert['signature']), 1),
                    $this->signatureSubject
                );
            default:
                return false;
        }
    }

    /**
     * Validates a signature
     *
     * Returns true if the signature is verified, false if it is not correct or null on error
     *
     * @param string $publicKeyAlgorithm
     * @param string $publicKey
     * @param string $signatureAlgorithm
     * @param string $signature
     * @param string $signatureSubject
     * @access private
     * @return int
     */
    function _validateSignature($publicKeyAlgorithm, $publicKey, $signatureAlgorithm, $signature, $signatureSubject)
    {
        switch ($publicKeyAlgorithm) {
            case 'rsaEncryption':
                if (!class_exists('Crypt_RSA')) {
                    include_once 'Crypt/RSA.php';
                }
                $rsa = new Crypt_RSA();
                $rsa->loadKey($publicKey);

                switch ($signatureAlgorithm) {
                    case 'md2WithRSAEncryption':
                    case 'md5WithRSAEncryption':
                    case 'sha1WithRSAEncryption':
                    case 'sha224WithRSAEncryption':
                    case 'sha256WithRSAEncryption':
                    case 'sha384WithRSAEncryption':
                    case 'sha512WithRSAEncryption':
                        $rsa->setHash(preg_replace('#WithRSAEncryption$#', '', $signatureAlgorithm));
                        $rsa->setSignatureMode(CRYPT_RSA_SIGNATURE_PKCS1);
                        if (!@$rsa->verify($signatureSubject, $signature)) {
                            return false;
                        }
                        break;
                    default:
                        return null;
                }
                break;
            default:
                return null;
        }

        return true;
    }

    /**
     * Sets the recursion limit
     *
     * When validating a signature it may be necessary to download intermediate certs from URI's.
     * An intermediate cert that linked to itself would result in an infinite loop so to prevent
     * that we set a recursion limit. A negative number means that there is no recursion limit.
     *
     * @param int $count
     * @access public
     */
    function setRecurLimit($count)
    {
        $this->recur_limit = $count;
    }

    /**
     * Prevents URIs from being automatically retrieved
     *
     * @access public
     */
    function disableURLFetch()
    {
        $this->disable_url_fetch = true;
    }

    /**
     * Allows URIs to be automatically retrieved
     *
     * @access public
     */
    function enableURLFetch()
    {
        $this->disable_url_fetch = false;
    }

    /**
     * Reformat public keys
     *
     * Reformats a public key to a format supported by phpseclib (if applicable)
     *
     * @param string $algorithm
     * @param string $key
     * @access private
     * @return string
     */
    function _reformatKey($algorithm, $key)
    {
        switch ($algorithm) {
            case 'rsaEncryption':
                return
                    "-----BEGIN RSA PUBLIC KEY-----\r\n" .
                    // subjectPublicKey is stored as a bit string in X.509 certs.  the first byte of a bit string represents how many bits
                    // in the last byte should be ignored.  the following only supports non-zero stuff but as none of the X.509 certs Firefox
                    // uses as a cert authority actually use a non-zero bit I think it's safe to assume that none do.
                    chunk_split(base64_encode(substr(base64_decode($key), 1)), 64) .
                    '-----END RSA PUBLIC KEY-----';
            default:
                return $key;
        }
    }

    /**
     * Decodes an IP address
     *
     * Takes in a base64 encoded "blob" and returns a human readable IP address
     *
     * @param string $ip
     * @access private
     * @return string
     */
    function _decodeIP($ip)
    {
        $ip = base64_decode($ip);
        list(, $ip) = unpack('N', $ip);
        return long2ip($ip);
    }

    /**
     * Encodes an IP address
     *
     * Takes a human readable IP address into a base64-encoded "blob"
     *
     * @param string $ip
     * @access private
     * @return string
     */
    function _encodeIP($ip)
    {
        return base64_encode(pack('N', ip2long($ip)));
    }

    /**
     * "Normalizes" a Distinguished Name property
     *
     * @param string $propName
     * @access private
     * @return mixed
     */
    function _translateDNProp($propName)
    {
        switch (strtolower($propName)) {
            case 'id-at-countryname':
            case 'countryname':
            case 'c':
                return 'id-at-countryName';
            case 'id-at-organizationname':
            case 'organizationname':
            case 'o':
                return 'id-at-organizationName';
            case 'id-at-dnqualifier':
            case 'dnqualifier':
                return 'id-at-dnQualifier';
            case 'id-at-commonname':
            case 'commonname':
            case 'cn':
                return 'id-at-commonName';
            case 'id-at-stateorprovincename':
            case 'stateorprovincename':
            case 'state':
            case 'province':
            case 'provincename':
            case 'st':
                return 'id-at-stateOrProvinceName';
            case 'id-at-localityname':
            case 'localityname':
            case 'l':
                return 'id-at-localityName';
            case 'id-emailaddress':
            case 'emailaddress':
                return 'pkcs-9-at-emailAddress';
            case 'id-at-serialnumber':
            case 'serialnumber':
                return 'id-at-serialNumber';
            case 'id-at-postalcode':
            case 'postalcode':
                return 'id-at-postalCode';
            case 'id-at-streetaddress':
            case 'streetaddress':
                return 'id-at-streetAddress';
            case 'id-at-name':
            case 'name':
                return 'id-at-name';
            case 'id-at-givenname':
            case 'givenname':
                return 'id-at-givenName';
            case 'id-at-surname':
            case 'surname':
            case 'sn':
                return 'id-at-surname';
            case 'id-at-initials':
            case 'initials':
                return 'id-at-initials';
            case 'id-at-generationqualifier':
            case 'generationqualifier':
                return 'id-at-generationQualifier';
            case 'id-at-organizationalunitname':
            case 'organizationalunitname':
            case 'ou':
                return 'id-at-organizationalUnitName';
            case 'id-at-pseudonym':
            case 'pseudonym':
                return 'id-at-pseudonym';
            case 'id-at-title':
            case 'title':
                return 'id-at-title';
            case 'id-at-description':
            case 'description':
                return 'id-at-description';
            case 'id-at-role':
            case 'role':
                return 'id-at-role';
            case 'id-at-uniqueidentifier':
            case 'uniqueidentifier':
            case 'x500uniqueidentifier':
                return 'id-at-uniqueIdentifier';
            case 'postaladdress':
            case 'id-at-postaladdress':
                return 'id-at-postalAddress';
            default:
                return false;
        }
    }

    /**
     * Set a Distinguished Name property
     *
     * @param string $propName
     * @param mixed $propValue
     * @param string $type optional
     * @access public
     * @return bool
     */
    function setDNProp($propName, $propValue, $type = 'utf8String')
    {
        if (empty($this->dn)) {
            $this->dn = array('rdnSequence' => array());
        }

        if (($propName = $this->_translateDNProp($propName)) === false) {
            return false;
        }

        foreach ((array) $propValue as $v) {
            if (!is_array($v) && isset($type)) {
                $v = array($type => $v);
            }
            $this->dn['rdnSequence'][] = array(
                array(
                    'type' => $propName,
                    'value'=> $v
                )
            );
        }

        return true;
    }

    /**
     * Remove Distinguished Name properties
     *
     * @param string $propName
     * @access public
     */
    function removeDNProp($propName)
    {
        if (empty($this->dn)) {
            return;
        }

        if (($propName = $this->_translateDNProp($propName)) === false) {
            return;
        }

        $dn = &$this->dn['rdnSequence'];
        $size = count($dn);
        for ($i = 0; $i < $size; $i++) {
            if ($dn[$i][0]['type'] == $propName) {
                unset($dn[$i]);
            }
        }

        $dn = array_values($dn);
        // fix for https://bugs.php.net/75433 affecting PHP 7.2
        if (!isset($dn[0])) {
            $dn = array_splice($dn, 0, 0);
        }
    }

    /**
     * Get Distinguished Name properties
     *
     * @param string $propName
     * @param array $dn optional
     * @param bool $withType optional
     * @return mixed
     * @access public
     */
    function getDNProp($propName, $dn = null, $withType = false)
    {
        if (!isset($dn)) {
            $dn = $this->dn;
        }

        if (empty($dn)) {
            return false;
        }

        if (($propName = $this->_translateDNProp($propName)) === false) {
            return false;
        }

        $asn1 = new File_ASN1();
        $asn1->loadOIDs($this->oids);
        $filters = array();
        $filters['value'] = array('type' => FILE_ASN1_TYPE_UTF8_STRING);
        $asn1->loadFilters($filters);
        $this->_mapOutDNs($dn, 'rdnSequence', $asn1);
        $dn = $dn['rdnSequence'];
        $result = array();
        for ($i = 0; $i < count($dn); $i++) {
            if ($dn[$i][0]['type'] == $propName) {
                $v = $dn[$i][0]['value'];
                if (!$withType) {
                    if (is_array($v)) {
                        foreach ($v as $type => $s) {
                            $type = array_search($type, $asn1->ANYmap, true);
                            if ($type !== false && isset($asn1->stringTypeSize[$type])) {
                                $s = $asn1->convert($s, $type);
                                if ($s !== false) {
                                    $v = $s;
                                    break;
                                }
                            }
                        }
                        if (is_array($v)) {
                            $v = array_pop($v); // Always strip data type.
                        }
                    } elseif (is_object($v) && strtolower(get_class($v)) == 'file_asn1_element') {
                        $map = $this->_getMapping($propName);
                        if (!is_bool($map)) {
                            $decoded = $asn1->decodeBER($v);
                            $v = $asn1->asn1map($decoded[0], $map);
                        }
                    }
                }
                $result[] = $v;
            }
        }

        return $result;
    }

    /**
     * Set a Distinguished Name
     *
     * @param mixed $dn
     * @param bool $merge optional
     * @param string $type optional
     * @access public
     * @return bool
     */
    function setDN($dn, $merge = false, $type = 'utf8String')
    {
        if (!$merge) {
            $this->dn = null;
        }

        if (is_array($dn)) {
            if (isset($dn['rdnSequence'])) {
                $this->dn = $dn; // No merge here.
                return true;
            }

            // handles stuff generated by openssl_x509_parse()
            foreach ($dn as $prop => $value) {
                if (!$this->setDNProp($prop, $value, $type)) {
                    return false;
                }
            }
            return true;
        }

        // handles everything else
        $results = preg_split('#((?:^|, *|/)(?:C=|O=|OU=|CN=|L=|ST=|SN=|postalCode=|streetAddress=|emailAddress=|serialNumber=|organizationalUnitName=|title=|description=|role=|x500UniqueIdentifier=|postalAddress=))#', $dn, -1, PREG_SPLIT_DELIM_CAPTURE);
        for ($i = 1; $i < count($results); $i+=2) {
            $prop = trim($results[$i], ', =/');
            $value = $results[$i + 1];
            if (!$this->setDNProp($prop, $value, $type)) {
                return false;
            }
        }

        return true;
    }

    /**
     * Get the Distinguished Name for a certificates subject
     *
     * @param mixed $format optional
     * @param array $dn optional
     * @access public
     * @return bool
     */
    function getDN($format = FILE_X509_DN_ARRAY, $dn = null)
    {
        if (!isset($dn)) {
            $dn = isset($this->currentCert['tbsCertList']) ? $this->currentCert['tbsCertList']['issuer'] : $this->dn;
        }

        switch ((int) $format) {
            case FILE_X509_DN_ARRAY:
                return $dn;
            case FILE_X509_DN_ASN1:
                $asn1 = new File_ASN1();
                $asn1->loadOIDs($this->oids);
                $filters = array();
                $filters['rdnSequence']['value'] = array('type' => FILE_ASN1_TYPE_UTF8_STRING);
                $asn1->loadFilters($filters);
                $this->_mapOutDNs($dn, 'rdnSequence', $asn1);
                return $asn1->encodeDER($dn, $this->Name);
            case FILE_X509_DN_CANON:
                //  No SEQUENCE around RDNs and all string values normalized as
                // trimmed lowercase UTF-8 with all spacing as one blank.
                // constructed RDNs will not be canonicalized
                $asn1 = new File_ASN1();
                $asn1->loadOIDs($this->oids);
                $filters = array();
                $filters['value'] = array('type' => FILE_ASN1_TYPE_UTF8_STRING);
                $asn1->loadFilters($filters);
                $result = '';
                $this->_mapOutDNs($dn, 'rdnSequence', $asn1);
                foreach ($dn['rdnSequence'] as $rdn) {
                    foreach ($rdn as $i => $attr) {
                        $attr = &$rdn[$i];
                        if (is_array($attr['value'])) {
                            foreach ($attr['value'] as $type => $v) {
                                $type = array_search($type, $asn1->ANYmap, true);
                                if ($type !== false && isset($asn1->stringTypeSize[$type])) {
                                    $v = $asn1->convert($v, $type);
                                    if ($v !== false) {
                                        $v = preg_replace('/\s+/', ' ', $v);
                                        $attr['value'] = strtolower(trim($v));
                                        break;
                                    }
                                }
                            }
                        }
                    }
                    $result .= $asn1->encodeDER($rdn, $this->RelativeDistinguishedName);
                }
                return $result;
            case FILE_X509_DN_HASH:
                $dn = $this->getDN(FILE_X509_DN_CANON, $dn);
                if (!class_exists('Crypt_Hash')) {
                    include_once 'Crypt/Hash.php';
                }
                $hash = new Crypt_Hash('sha1');
                $hash = $hash->hash($dn);
                extract(unpack('Vhash', $hash));
                return strtolower(bin2hex(pack('N', $hash)));
        }

        // Default is to return a string.
        $start = true;
        $output = '';
        $result = array();
        $asn1 = new File_ASN1();
        $asn1->loadOIDs($this->oids);
        $filters = array();
        $filters['rdnSequence']['value'] = array('type' => FILE_ASN1_TYPE_UTF8_STRING);
        $asn1->loadFilters($filters);
        $this->_mapOutDNs($dn, 'rdnSequence', $asn1);
        foreach ($dn['rdnSequence'] as $field) {
            $prop = $field[0]['type'];
            $value = $field[0]['value'];

            $delim = ', ';
            switch ($prop) {
                case 'id-at-countryName':
                    $desc = 'C';
                    break;
                case 'id-at-stateOrProvinceName':
                    $desc = 'ST';
                    break;
                case 'id-at-organizationName':
                    $desc = 'O';
                    break;
                case 'id-at-organizationalUnitName':
                    $desc = 'OU';
                    break;
                case 'id-at-commonName':
                    $desc = 'CN';
                    break;
                case 'id-at-localityName':
                    $desc = 'L';
                    break;
                case 'id-at-surname':
                    $desc = 'SN';
                    break;
                case 'id-at-uniqueIdentifier':
                    $delim = '/';
                    $desc = 'x500UniqueIdentifier';
                    break;
                case 'id-at-postalAddress':
                    $delim = '/';
                    $desc = 'postalAddress';
                    break;
                default:
                    $delim = '/';
                    $desc = preg_replace('#.+-([^-]+)$#', '$1', $prop);
            }

            if (!$start) {
                $output.= $delim;
            }
            if (is_array($value)) {
                foreach ($value as $type => $v) {
                    $type = array_search($type, $asn1->ANYmap, true);
                    if ($type !== false && isset($asn1->stringTypeSize[$type])) {
                        $v = $asn1->convert($v, $type);
                        if ($v !== false) {
                            $value = $v;
                            break;
                        }
                    }
                }
                if (is_array($value)) {
                    $value = array_pop($value); // Always strip data type.
                }
            } elseif (is_object($value) && strtolower(get_class($value)) == 'file_asn1_element') {
                // @codingStandardsIgnoreStart
                $callback = version_compare(PHP_VERSION, '5.3.0') >= 0 ?
                    function ($x) { return "\x" . bin2hex($x[0]); } :
                    create_function('$x', 'return "\x" . bin2hex($x[0]);');
                // @codingStandardsIgnoreEnd
                $value = strtoupper(preg_replace_callback('#[^\x20-\x7E]#', $callback, $value->element));
            }
            $output.= $desc . '=' . $value;
            $result[$desc] = isset($result[$desc]) ?
                array_merge((array) $dn[$prop], array($value)) :
                $value;
            $start = false;
        }

        return $format == FILE_X509_DN_OPENSSL ? $result : $output;
    }

    /**
     * Get the Distinguished Name for a certificate/crl issuer
     *
     * @param int $format optional
     * @access public
     * @return mixed
     */
    function getIssuerDN($format = FILE_X509_DN_ARRAY)
    {
        switch (true) {
            case !isset($this->currentCert) || !is_array($this->currentCert):
                break;
            case isset($this->currentCert['tbsCertificate']):
                return $this->getDN($format, $this->currentCert['tbsCertificate']['issuer']);
            case isset($this->currentCert['tbsCertList']):
                return $this->getDN($format, $this->currentCert['tbsCertList']['issuer']);
        }

        return false;
    }

    /**
     * Get the Distinguished Name for a certificate/csr subject
     * Alias of getDN()
     *
     * @param int $format optional
     * @access public
     * @return mixed
     */
    function getSubjectDN($format = FILE_X509_DN_ARRAY)
    {
        switch (true) {
            case !empty($this->dn):
                return $this->getDN($format);
            case !isset($this->currentCert) || !is_array($this->currentCert):
                break;
            case isset($this->currentCert['tbsCertificate']):
                return $this->getDN($format, $this->currentCert['tbsCertificate']['subject']);
            case isset($this->currentCert['certificationRequestInfo']):
                return $this->getDN($format, $this->currentCert['certificationRequestInfo']['subject']);
        }

        return false;
    }

    /**
     * Get an individual Distinguished Name property for a certificate/crl issuer
     *
     * @param string $propName
     * @param bool $withType optional
     * @access public
     * @return mixed
     */
    function getIssuerDNProp($propName, $withType = false)
    {
        switch (true) {
            case !isset($this->currentCert) || !is_array($this->currentCert):
                break;
            case isset($this->currentCert['tbsCertificate']):
                return $this->getDNProp($propName, $this->currentCert['tbsCertificate']['issuer'], $withType);
            case isset($this->currentCert['tbsCertList']):
                return $this->getDNProp($propName, $this->currentCert['tbsCertList']['issuer'], $withType);
        }

        return false;
    }

    /**
     * Get an individual Distinguished Name property for a certificate/csr subject
     *
     * @param string $propName
     * @param bool $withType optional
     * @access public
     * @return mixed
     */
    function getSubjectDNProp($propName, $withType = false)
    {
        switch (true) {
            case !empty($this->dn):
                return $this->getDNProp($propName, null, $withType);
            case !isset($this->currentCert) || !is_array($this->currentCert):
                break;
            case isset($this->currentCert['tbsCertificate']):
                return $this->getDNProp($propName, $this->currentCert['tbsCertificate']['subject'], $withType);
            case isset($this->currentCert['certificationRequestInfo']):
                return $this->getDNProp($propName, $this->currentCert['certificationRequestInfo']['subject'], $withType);
        }

        return false;
    }

    /**
     * Get the certificate chain for the current cert
     *
     * @access public
     * @return mixed
     */
    function getChain()
    {
        $chain = array($this->currentCert);

        if (!is_array($this->currentCert) || !isset($this->currentCert['tbsCertificate'])) {
            return false;
        }
        if (empty($this->CAs)) {
            return $chain;
        }
        while (true) {
            $currentCert = $chain[count($chain) - 1];
            for ($i = 0; $i < count($this->CAs); $i++) {
                $ca = $this->CAs[$i];
                if ($currentCert['tbsCertificate']['issuer'] === $ca['tbsCertificate']['subject']) {
                    $authorityKey = $this->getExtension('id-ce-authorityKeyIdentifier', $currentCert);
                    $subjectKeyID = $this->getExtension('id-ce-subjectKeyIdentifier', $ca);
                    switch (true) {
                        case !is_array($authorityKey):
                        case is_array($authorityKey) && isset($authorityKey['keyIdentifier']) && $authorityKey['keyIdentifier'] === $subjectKeyID:
                            if ($currentCert === $ca) {
                                break 3;
                            }
                            $chain[] = $ca;
                            break 2;
                    }
                }
            }
            if ($i == count($this->CAs)) {
                break;
            }
        }
        foreach ($chain as $key => $value) {
            $chain[$key] = new File_X509();
            $chain[$key]->loadX509($value);
        }
        return $chain;
    }

    /**
     * Set public key
     *
     * Key needs to be a Crypt_RSA object
     *
     * @param object $key
     * @access public
     * @return bool
     */
    function setPublicKey($key)
    {
        $key->setPublicKey();
        $this->publicKey = $key;
    }

    /**
     * Set private key
     *
     * Key needs to be a Crypt_RSA object
     *
     * @param object $key
     * @access public
     */
    function setPrivateKey($key)
    {
        $this->privateKey = $key;
    }

    /**
     * Set challenge
     *
     * Used for SPKAC CSR's
     *
     * @param string $challenge
     * @access public
     */
    function setChallenge($challenge)
    {
        $this->challenge = $challenge;
    }

    /**
     * Gets the public key
     *
     * Returns a Crypt_RSA object or a false.
     *
     * @access public
     * @return mixed
     */
    function getPublicKey()
    {
        if (isset($this->publicKey)) {
            return $this->publicKey;
        }

        if (isset($this->currentCert) && is_array($this->currentCert)) {
            foreach (array('tbsCertificate/subjectPublicKeyInfo', 'certificationRequestInfo/subjectPKInfo') as $path) {
                $keyinfo = $this->_subArray($this->currentCert, $path);
                if (!empty($keyinfo)) {
                    break;
                }
            }
        }
        if (empty($keyinfo)) {
            return false;
        }

        $key = $keyinfo['subjectPublicKey'];

        switch ($keyinfo['algorithm']['algorithm']) {
            case 'rsaEncryption':
                if (!class_exists('Crypt_RSA')) {
                    include_once 'Crypt/RSA.php';
                }
                $publicKey = new Crypt_RSA();
                $publicKey->loadKey($key);
                $publicKey->setPublicKey();
                break;
            default:
                return false;
        }

        return $publicKey;
    }

    /**
     * Load a Certificate Signing Request
     *
     * @param string $csr
     * @access public
     * @return mixed
     */
    function loadCSR($csr, $mode = FILE_X509_FORMAT_AUTO_DETECT)
    {
        if (is_array($csr) && isset($csr['certificationRequestInfo'])) {
            unset($this->currentCert);
            unset($this->currentKeyIdentifier);
            unset($this->signatureSubject);
            $this->dn = $csr['certificationRequestInfo']['subject'];
            if (!isset($this->dn)) {
                return false;
            }

            $this->currentCert = $csr;
            return $csr;
        }

        // see http://tools.ietf.org/html/rfc2986

        $asn1 = new File_ASN1();

        if ($mode != FILE_X509_FORMAT_DER) {
            $newcsr = $this->_extractBER($csr);
            if ($mode == FILE_X509_FORMAT_PEM && $csr == $newcsr) {
                return false;
            }
            $csr = $newcsr;
        }
        $orig = $csr;

        if ($csr === false) {
            $this->currentCert = false;
            return false;
        }

        $asn1->loadOIDs($this->oids);
        $decoded = $asn1->decodeBER($csr);

        if (empty($decoded)) {
            $this->currentCert = false;
            return false;
        }

        $csr = $asn1->asn1map($decoded[0], $this->CertificationRequest);
        if (!isset($csr) || $csr === false) {
            $this->currentCert = false;
            return false;
        }

        $this->_mapInAttributes($csr, 'certificationRequestInfo/attributes', $asn1);
        $this->_mapInDNs($csr, 'certificationRequestInfo/subject/rdnSequence', $asn1);

        $this->dn = $csr['certificationRequestInfo']['subject'];

        $this->signatureSubject = substr($orig, $decoded[0]['content'][0]['start'], $decoded[0]['content'][0]['length']);

        $algorithm = &$csr['certificationRequestInfo']['subjectPKInfo']['algorithm']['algorithm'];
        $key = &$csr['certificationRequestInfo']['subjectPKInfo']['subjectPublicKey'];
        $key = $this->_reformatKey($algorithm, $key);

        switch ($algorithm) {
            case 'rsaEncryption':
                if (!class_exists('Crypt_RSA')) {
                    include_once 'Crypt/RSA.php';
                }
                $this->publicKey = new Crypt_RSA();
                $this->publicKey->loadKey($key);
                $this->publicKey->setPublicKey();
                break;
            default:
                $this->publicKey = null;
        }

        $this->currentKeyIdentifier = null;
        $this->currentCert = $csr;

        return $csr;
    }

    /**
     * Save CSR request
     *
     * @param array $csr
     * @param int $format optional
     * @access public
     * @return string
     */
    function saveCSR($csr, $format = FILE_X509_FORMAT_PEM)
    {
        if (!is_array($csr) || !isset($csr['certificationRequestInfo'])) {
            return false;
        }

        switch (true) {
            case !($algorithm = $this->_subArray($csr, 'certificationRequestInfo/subjectPKInfo/algorithm/algorithm')):
            case is_object($csr['certificationRequestInfo']['subjectPKInfo']['subjectPublicKey']):
                break;
            default:
                switch ($algorithm) {
                    case 'rsaEncryption':
                        $csr['certificationRequestInfo']['subjectPKInfo']['subjectPublicKey']
                            = base64_encode("\0" . base64_decode(preg_replace('#-.+-|[\r\n]#', '', $csr['certificationRequestInfo']['subjectPKInfo']['subjectPublicKey'])));
                        $csr['certificationRequestInfo']['subjectPKInfo']['algorithm']['parameters'] = null;
                        $csr['signatureAlgorithm']['parameters'] = null;
                        $csr['certificationRequestInfo']['signature']['parameters'] = null;
                }
        }

        $asn1 = new File_ASN1();

        $asn1->loadOIDs($this->oids);

        $filters = array();
        $filters['certificationRequestInfo']['subject']['rdnSequence']['value']
            = array('type' => FILE_ASN1_TYPE_UTF8_STRING);

        $asn1->loadFilters($filters);

        $this->_mapOutDNs($csr, 'certificationRequestInfo/subject/rdnSequence', $asn1);
        $this->_mapOutAttributes($csr, 'certificationRequestInfo/attributes', $asn1);
        $csr = $asn1->encodeDER($csr, $this->CertificationRequest);

        switch ($format) {
            case FILE_X509_FORMAT_DER:
                return $csr;
            // case FILE_X509_FORMAT_PEM:
            default:
                return "-----BEGIN CERTIFICATE REQUEST-----\r\n" . chunk_split(base64_encode($csr), 64) . '-----END CERTIFICATE REQUEST-----';
        }
    }

    /**
     * Load a SPKAC CSR
     *
     * SPKAC's are produced by the HTML5 keygen element:
     *
     * https://developer.mozilla.org/en-US/docs/HTML/Element/keygen
     *
     * @param string $csr
     * @access public
     * @return mixed
     */
    function loadSPKAC($spkac)
    {
        if (is_array($spkac) && isset($spkac['publicKeyAndChallenge'])) {
            unset($this->currentCert);
            unset($this->currentKeyIdentifier);
            unset($this->signatureSubject);
            $this->currentCert = $spkac;
            return $spkac;
        }

        // see http://www.w3.org/html/wg/drafts/html/master/forms.html#signedpublickeyandchallenge

        $asn1 = new File_ASN1();

        // OpenSSL produces SPKAC's that are preceded by the string SPKAC=
        $temp = preg_replace('#(?:SPKAC=)|[ \r\n\\\]#', '', $spkac);
        $temp = preg_match('#^[a-zA-Z\d/+]*={0,2}$#', $temp) ? base64_decode($temp) : false;
        if ($temp != false) {
            $spkac = $temp;
        }
        $orig = $spkac;

        if ($spkac === false) {
            $this->currentCert = false;
            return false;
        }

        $asn1->loadOIDs($this->oids);
        $decoded = $asn1->decodeBER($spkac);

        if (empty($decoded)) {
            $this->currentCert = false;
            return false;
        }

        $spkac = $asn1->asn1map($decoded[0], $this->SignedPublicKeyAndChallenge);

        if (!isset($spkac) || $spkac === false) {
            $this->currentCert = false;
            return false;
        }

        $this->signatureSubject = substr($orig, $decoded[0]['content'][0]['start'], $decoded[0]['content'][0]['length']);

        $algorithm = &$spkac['publicKeyAndChallenge']['spki']['algorithm']['algorithm'];
        $key = &$spkac['publicKeyAndChallenge']['spki']['subjectPublicKey'];
        $key = $this->_reformatKey($algorithm, $key);

        switch ($algorithm) {
            case 'rsaEncryption':
                if (!class_exists('Crypt_RSA')) {
                    include_once 'Crypt/RSA.php';
                }
                $this->publicKey = new Crypt_RSA();
                $this->publicKey->loadKey($key);
                $this->publicKey->setPublicKey();
                break;
            default:
                $this->publicKey = null;
        }

        $this->currentKeyIdentifier = null;
        $this->currentCert = $spkac;

        return $spkac;
    }

    /**
     * Save a SPKAC CSR request
     *
     * @param array $csr
     * @param int $format optional
     * @access public
     * @return string
     */
    function saveSPKAC($spkac, $format = FILE_X509_FORMAT_PEM)
    {
        if (!is_array($spkac) || !isset($spkac['publicKeyAndChallenge'])) {
            return false;
        }

        $algorithm = $this->_subArray($spkac, 'publicKeyAndChallenge/spki/algorithm/algorithm');
        switch (true) {
            case !$algorithm:
            case is_object($spkac['publicKeyAndChallenge']['spki']['subjectPublicKey']):
                break;
            default:
                switch ($algorithm) {
                    case 'rsaEncryption':
                        $spkac['publicKeyAndChallenge']['spki']['subjectPublicKey']
                            = base64_encode("\0" . base64_decode(preg_replace('#-.+-|[\r\n]#', '', $spkac['publicKeyAndChallenge']['spki']['subjectPublicKey'])));
                }
        }

        $asn1 = new File_ASN1();

        $asn1->loadOIDs($this->oids);
        $spkac = $asn1->encodeDER($spkac, $this->SignedPublicKeyAndChallenge);

        switch ($format) {
            case FILE_X509_FORMAT_DER:
                return $spkac;
            // case FILE_X509_FORMAT_PEM:
            default:
                // OpenSSL's implementation of SPKAC requires the SPKAC be preceded by SPKAC= and since there are pretty much
                // no other SPKAC decoders phpseclib will use that same format
                return 'SPKAC=' . base64_encode($spkac);
        }
    }

    /**
     * Load a Certificate Revocation List
     *
     * @param string $crl
     * @access public
     * @return mixed
     */
    function loadCRL($crl, $mode = FILE_X509_FORMAT_AUTO_DETECT)
    {
        if (is_array($crl) && isset($crl['tbsCertList'])) {
            $this->currentCert = $crl;
            unset($this->signatureSubject);
            return $crl;
        }

        $asn1 = new File_ASN1();

        if ($mode != FILE_X509_FORMAT_DER) {
            $newcrl = $this->_extractBER($crl);
            if ($mode == FILE_X509_FORMAT_PEM && $crl == $newcrl) {
                return false;
            }
            $crl = $newcrl;
        }
        $orig = $crl;

        if ($crl === false) {
            $this->currentCert = false;
            return false;
        }

        $asn1->loadOIDs($this->oids);
        $decoded = $asn1->decodeBER($crl);

        if (empty($decoded)) {
            $this->currentCert = false;
            return false;
        }

        $crl = $asn1->asn1map($decoded[0], $this->CertificateList);
        if (!isset($crl) || $crl === false) {
            $this->currentCert = false;
            return false;
        }

        $this->signatureSubject = substr($orig, $decoded[0]['content'][0]['start'], $decoded[0]['content'][0]['length']);

        $this->_mapInDNs($crl, 'tbsCertList/issuer/rdnSequence', $asn1);
        if ($this->_isSubArrayValid($crl, 'tbsCertList/crlExtensions')) {
            $this->_mapInExtensions($crl, 'tbsCertList/crlExtensions', $asn1);
        }
        if ($this->_isSubArrayValid($crl, 'tbsCertList/revokedCertificates')) {
            $rclist_ref = &$this->_subArrayUnchecked($crl, 'tbsCertList/revokedCertificates');
            if ($rclist_ref) {
                $rclist = $crl['tbsCertList']['revokedCertificates'];
                foreach ($rclist as $i => $extension) {
                    if ($this->_isSubArrayValid($rclist, "$i/crlEntryExtensions", $asn1)) {
                        $this->_mapInExtensions($rclist_ref, "$i/crlEntryExtensions", $asn1);
                    }
                }
            }
        }

        $this->currentKeyIdentifier = null;
        $this->currentCert = $crl;

        return $crl;
    }

    /**
     * Save Certificate Revocation List.
     *
     * @param array $crl
     * @param int $format optional
     * @access public
     * @return string
     */
    function saveCRL($crl, $format = FILE_X509_FORMAT_PEM)
    {
        if (!is_array($crl) || !isset($crl['tbsCertList'])) {
            return false;
        }

        $asn1 = new File_ASN1();

        $asn1->loadOIDs($this->oids);

        $filters = array();
        $filters['tbsCertList']['issuer']['rdnSequence']['value']
            = array('type' => FILE_ASN1_TYPE_UTF8_STRING);
        $filters['tbsCertList']['signature']['parameters']
            = array('type' => FILE_ASN1_TYPE_UTF8_STRING);
        $filters['signatureAlgorithm']['parameters']
            = array('type' => FILE_ASN1_TYPE_UTF8_STRING);

        if (empty($crl['tbsCertList']['signature']['parameters'])) {
            $filters['tbsCertList']['signature']['parameters']
                = array('type' => FILE_ASN1_TYPE_NULL);
        }

        if (empty($crl['signatureAlgorithm']['parameters'])) {
            $filters['signatureAlgorithm']['parameters']
                = array('type' => FILE_ASN1_TYPE_NULL);
        }

        $asn1->loadFilters($filters);

        $this->_mapOutDNs($crl, 'tbsCertList/issuer/rdnSequence', $asn1);
        $this->_mapOutExtensions($crl, 'tbsCertList/crlExtensions', $asn1);
        $rclist = &$this->_subArray($crl, 'tbsCertList/revokedCertificates');
        if (is_array($rclist)) {
            foreach ($rclist as $i => $extension) {
                $this->_mapOutExtensions($rclist, "$i/crlEntryExtensions", $asn1);
            }
        }

        $crl = $asn1->encodeDER($crl, $this->CertificateList);

        switch ($format) {
            case FILE_X509_FORMAT_DER:
                return $crl;
            // case FILE_X509_FORMAT_PEM:
            default:
                return "-----BEGIN X509 CRL-----\r\n" . chunk_split(base64_encode($crl), 64) . '-----END X509 CRL-----';
        }
    }

    /**
     * Helper function to build a time field according to RFC 3280 section
     *  - 4.1.2.5 Validity
     *  - 5.1.2.4 This Update
     *  - 5.1.2.5 Next Update
     *  - 5.1.2.6 Revoked Certificates
     * by choosing utcTime iff year of date given is before 2050 and generalTime else.
     *
     * @param string $date in format date('D, d M Y H:i:s O')
     * @access private
     * @return array
     */
    function _timeField($date)
    {
        if (is_object($date) && strtolower(get_class($date)) == 'file_asn1_element') {
            return $date;
        }
        if (!class_exists('DateTime')) {
            $year = @gmdate("Y", @strtotime($date)); // the same way ASN1.php parses this
        } else {
            $dateObj = new DateTime($date, new DateTimeZone('GMT'));
            $year = $dateObj->format('Y');
        }
        if ($year < 2050) {
            return array('utcTime' => $date);
        } else {
            return array('generalTime' => $date);
        }
    }

    /**
     * Sign an X.509 certificate
     *
     * $issuer's private key needs to be loaded.
     * $subject can be either an existing X.509 cert (if you want to resign it),
     * a CSR or something with the DN and public key explicitly set.
     *
     * @param File_X509 $issuer
     * @param File_X509 $subject
     * @param string $signatureAlgorithm optional
     * @access public
     * @return mixed
     */
    function sign($issuer, $subject, $signatureAlgorithm = 'sha1WithRSAEncryption')
    {
        if (!is_object($issuer->privateKey) || empty($issuer->dn)) {
            return false;
        }

        if (isset($subject->publicKey) && !($subjectPublicKey = $subject->_formatSubjectPublicKey())) {
            return false;
        }

        $currentCert = isset($this->currentCert) ? $this->currentCert : null;
        $signatureSubject = isset($this->signatureSubject) ? $this->signatureSubject: null;

        if (isset($subject->currentCert) && is_array($subject->currentCert) && isset($subject->currentCert['tbsCertificate'])) {
            $this->currentCert = $subject->currentCert;
            $this->currentCert['tbsCertificate']['signature']['algorithm'] = $signatureAlgorithm;
            $this->currentCert['signatureAlgorithm']['algorithm'] = $signatureAlgorithm;

            if (!empty($this->startDate)) {
                $this->currentCert['tbsCertificate']['validity']['notBefore'] = $this->_timeField($this->startDate);
            }
            if (!empty($this->endDate)) {
                $this->currentCert['tbsCertificate']['validity']['notAfter'] = $this->_timeField($this->endDate);
            }
            if (!empty($this->serialNumber)) {
                $this->currentCert['tbsCertificate']['serialNumber'] = $this->serialNumber;
            }
            if (!empty($subject->dn)) {
                $this->currentCert['tbsCertificate']['subject'] = $subject->dn;
            }
            if (!empty($subject->publicKey)) {
                $this->currentCert['tbsCertificate']['subjectPublicKeyInfo'] = $subjectPublicKey;
            }
            $this->removeExtension('id-ce-authorityKeyIdentifier');
            if (isset($subject->domains)) {
                $this->removeExtension('id-ce-subjectAltName');
            }
        } elseif (isset($subject->currentCert) && is_array($subject->currentCert) && isset($subject->currentCert['tbsCertList'])) {
            return false;
        } else {
            if (!isset($subject->publicKey)) {
                return false;
            }

            if (!class_exists('DateTime')) {
                $startDate = !empty($this->startDate) ? $this->startDate : @date('D, d M Y H:i:s O');
                $endDate = !empty($this->endDate) ? $this->endDate : @date('D, d M Y H:i:s O', strtotime('+1 year'));
            } else {
                $startDate = new DateTime('now', new DateTimeZone(@date_default_timezone_get()));
                $startDate = !empty($this->startDate) ? $this->startDate : $startDate->format('D, d M Y H:i:s O');

                $endDate = new DateTime('+1 year', new DateTimeZone(@date_default_timezone_get()));
                $endDate = !empty($this->endDate) ? $this->endDate : $endDate->format('D, d M Y H:i:s O');
            }
            if (!empty($this->serialNumber)) {
                $serialNumber = $this->serialNumber;
            } else {
                if (!function_exists('crypt_random_string')) {
                    include_once 'Crypt/Random.php';
                }
                /* "The serial number MUST be a positive integer"
                   "Conforming CAs MUST NOT use serialNumber values longer than 20 octets."
                    -- https://tools.ietf.org/html/rfc5280#section-4.1.2.2

                   for the integer to be positive the leading bit needs to be 0 hence the
                   application of a bitmap
                */
                $serialNumber = new Math_BigInteger(crypt_random_string(20) & ("\x7F" . str_repeat("\xFF", 19)), 256);
            }

            $this->currentCert = array(
                'tbsCertificate' =>
                    array(
                        'version' => 'v3',
                        'serialNumber' => $serialNumber, // $this->setSerialNumber()
                        'signature' => array('algorithm' => $signatureAlgorithm),
                        'issuer' => false, // this is going to be overwritten later
                        'validity' => array(
                            'notBefore' => $this->_timeField($startDate), // $this->setStartDate()
                            'notAfter' => $this->_timeField($endDate)   // $this->setEndDate()
                        ),
                        'subject' => $subject->dn,
                        'subjectPublicKeyInfo' => $subjectPublicKey
                    ),
                    'signatureAlgorithm' => array('algorithm' => $signatureAlgorithm),
                    'signature'          => false // this is going to be overwritten later
            );

            // Copy extensions from CSR.
            $csrexts = $subject->getAttribute('pkcs-9-at-extensionRequest', 0);

            if (!empty($csrexts)) {
                $this->currentCert['tbsCertificate']['extensions'] = $csrexts;
            }
        }

        $this->currentCert['tbsCertificate']['issuer'] = $issuer->dn;

        if (isset($issuer->currentKeyIdentifier)) {
            $this->setExtension('id-ce-authorityKeyIdentifier', array(
                    //'authorityCertIssuer' => array(
                    //    array(
                    //        'directoryName' => $issuer->dn
                    //    )
                    //),
                    'keyIdentifier' => $issuer->currentKeyIdentifier
                ));
            //$extensions = &$this->currentCert['tbsCertificate']['extensions'];
            //if (isset($issuer->serialNumber)) {
            //    $extensions[count($extensions) - 1]['authorityCertSerialNumber'] = $issuer->serialNumber;
            //}
            //unset($extensions);
        }

        if (isset($subject->currentKeyIdentifier)) {
            $this->setExtension('id-ce-subjectKeyIdentifier', $subject->currentKeyIdentifier);
        }

        $altName = array();

        if (isset($subject->domains) && count($subject->domains)) {
            $altName = array_map(array('File_X509', '_dnsName'), $subject->domains);
        }

        if (isset($subject->ipAddresses) && count($subject->ipAddresses)) {
            // should an IP address appear as the CN if no domain name is specified? idk
            //$ips = count($subject->domains) ? $subject->ipAddresses : array_slice($subject->ipAddresses, 1);
            $ipAddresses = array();
            foreach ($subject->ipAddresses as $ipAddress) {
                $encoded = $subject->_ipAddress($ipAddress);
                if ($encoded !== false) {
                    $ipAddresses[] = $encoded;
                }
            }
            if (count($ipAddresses)) {
                $altName = array_merge($altName, $ipAddresses);
            }
        }

        if (!empty($altName)) {
            $this->setExtension('id-ce-subjectAltName', $altName);
        }

        if ($this->caFlag) {
            $keyUsage = $this->getExtension('id-ce-keyUsage');
            if (!$keyUsage) {
                $keyUsage = array();
            }

            $this->setExtension(
                'id-ce-keyUsage',
                array_values(array_unique(array_merge($keyUsage, array('cRLSign', 'keyCertSign'))))
            );

            $basicConstraints = $this->getExtension('id-ce-basicConstraints');
            if (!$basicConstraints) {
                $basicConstraints = array();
            }

            $this->setExtension(
                'id-ce-basicConstraints',
                array_unique(array_merge(array('cA' => true), $basicConstraints)),
                true
            );

            if (!isset($subject->currentKeyIdentifier)) {
                $this->setExtension('id-ce-subjectKeyIdentifier', base64_encode($this->computeKeyIdentifier($this->currentCert)), false, false);
            }
        }

        // resync $this->signatureSubject
        // save $tbsCertificate in case there are any File_ASN1_Element objects in it
        $tbsCertificate = $this->currentCert['tbsCertificate'];
        $this->loadX509($this->saveX509($this->currentCert));

        $result = $this->_sign($issuer->privateKey, $signatureAlgorithm);
        $result['tbsCertificate'] = $tbsCertificate;

        $this->currentCert = $currentCert;
        $this->signatureSubject = $signatureSubject;

        return $result;
    }

    /**
     * Sign a CSR
     *
     * @access public
     * @return mixed
     */
    function signCSR($signatureAlgorithm = 'sha1WithRSAEncryption')
    {
        if (!is_object($this->privateKey) || empty($this->dn)) {
            return false;
        }

        $origPublicKey = $this->publicKey;
        $class = get_class($this->privateKey);
        $this->publicKey = new $class();
        $this->publicKey->loadKey($this->privateKey->getPublicKey());
        $this->publicKey->setPublicKey();
        if (!($publicKey = $this->_formatSubjectPublicKey())) {
            return false;
        }
        $this->publicKey = $origPublicKey;

        $currentCert = isset($this->currentCert) ? $this->currentCert : null;
        $signatureSubject = isset($this->signatureSubject) ? $this->signatureSubject: null;

        if (isset($this->currentCert) && is_array($this->currentCert) && isset($this->currentCert['certificationRequestInfo'])) {
            $this->currentCert['signatureAlgorithm']['algorithm'] = $signatureAlgorithm;
            if (!empty($this->dn)) {
                $this->currentCert['certificationRequestInfo']['subject'] = $this->dn;
            }
            $this->currentCert['certificationRequestInfo']['subjectPKInfo'] = $publicKey;
        } else {
            $this->currentCert = array(
                'certificationRequestInfo' =>
                    array(
                        'version' => 'v1',
                        'subject' => $this->dn,
                        'subjectPKInfo' => $publicKey
                    ),
                    'signatureAlgorithm' => array('algorithm' => $signatureAlgorithm),
                    'signature'          => false // this is going to be overwritten later
            );
        }

        // resync $this->signatureSubject
        // save $certificationRequestInfo in case there are any File_ASN1_Element objects in it
        $certificationRequestInfo = $this->currentCert['certificationRequestInfo'];
        $this->loadCSR($this->saveCSR($this->currentCert));

        $result = $this->_sign($this->privateKey, $signatureAlgorithm);
        $result['certificationRequestInfo'] = $certificationRequestInfo;

        $this->currentCert = $currentCert;
        $this->signatureSubject = $signatureSubject;

        return $result;
    }

    /**
     * Sign a SPKAC
     *
     * @access public
     * @return mixed
     */
    function signSPKAC($signatureAlgorithm = 'sha1WithRSAEncryption')
    {
        if (!is_object($this->privateKey)) {
            return false;
        }

        $origPublicKey = $this->publicKey;
        $class = get_class($this->privateKey);
        $this->publicKey = new $class();
        $this->publicKey->loadKey($this->privateKey->getPublicKey());
        $this->publicKey->setPublicKey();
        $publicKey = $this->_formatSubjectPublicKey();
        if (!$publicKey) {
            return false;
        }
        $this->publicKey = $origPublicKey;

        $currentCert = isset($this->currentCert) ? $this->currentCert : null;
        $signatureSubject = isset($this->signatureSubject) ? $this->signatureSubject: null;

        // re-signing a SPKAC seems silly but since everything else supports re-signing why not?
        if (isset($this->currentCert) && is_array($this->currentCert) && isset($this->currentCert['publicKeyAndChallenge'])) {
            $this->currentCert['signatureAlgorithm']['algorithm'] = $signatureAlgorithm;
            $this->currentCert['publicKeyAndChallenge']['spki'] = $publicKey;
            if (!empty($this->challenge)) {
                // the bitwise AND ensures that the output is a valid IA5String
                $this->currentCert['publicKeyAndChallenge']['challenge'] = $this->challenge & str_repeat("\x7F", strlen($this->challenge));
            }
        } else {
            $this->currentCert = array(
                'publicKeyAndChallenge' =>
                    array(
                        'spki' => $publicKey,
                        // quoting <https://developer.mozilla.org/en-US/docs/Web/HTML/Element/keygen>,
                        // "A challenge string that is submitted along with the public key. Defaults to an empty string if not specified."
                        // both Firefox and OpenSSL ("openssl spkac -key private.key") behave this way
                        // we could alternatively do this instead if we ignored the specs:
                        // crypt_random_string(8) & str_repeat("\x7F", 8)
                        'challenge' => !empty($this->challenge) ? $this->challenge : ''
                    ),
                    'signatureAlgorithm' => array('algorithm' => $signatureAlgorithm),
                    'signature'          => false // this is going to be overwritten later
            );
        }

        // resync $this->signatureSubject
        // save $publicKeyAndChallenge in case there are any File_ASN1_Element objects in it
        $publicKeyAndChallenge = $this->currentCert['publicKeyAndChallenge'];
        $this->loadSPKAC($this->saveSPKAC($this->currentCert));

        $result = $this->_sign($this->privateKey, $signatureAlgorithm);
        $result['publicKeyAndChallenge'] = $publicKeyAndChallenge;

        $this->currentCert = $currentCert;
        $this->signatureSubject = $signatureSubject;

        return $result;
    }

    /**
     * Sign a CRL
     *
     * $issuer's private key needs to be loaded.
     *
     * @param File_X509 $issuer
     * @param File_X509 $crl
     * @param string $signatureAlgorithm optional
     * @access public
     * @return mixed
     */
    function signCRL($issuer, $crl, $signatureAlgorithm = 'sha1WithRSAEncryption')
    {
        if (!is_object($issuer->privateKey) || empty($issuer->dn)) {
            return false;
        }

        $currentCert = isset($this->currentCert) ? $this->currentCert : null;
        $signatureSubject = isset($this->signatureSubject) ? $this->signatureSubject : null;
        if (!class_exists('DateTime')) {
            $thisUpdate = !empty($this->startDate) ? $this->startDate : @date('D, d M Y H:i:s O');
        } else {
            $thisUpdate = new DateTime('now', new DateTimeZone(@date_default_timezone_get()));
            $thisUpdate = !empty($this->startDate) ? $this->startDate : $thisUpdate->format('D, d M Y H:i:s O');
        }

        if (isset($crl->currentCert) && is_array($crl->currentCert) && isset($crl->currentCert['tbsCertList'])) {
            $this->currentCert = $crl->currentCert;
            $this->currentCert['tbsCertList']['signature']['algorithm'] = $signatureAlgorithm;
            $this->currentCert['signatureAlgorithm']['algorithm'] = $signatureAlgorithm;
        } else {
            $this->currentCert = array(
                'tbsCertList' =>
                    array(
                        'version' => 'v2',
                        'signature' => array('algorithm' => $signatureAlgorithm),
                        'issuer' => false, // this is going to be overwritten later
                        'thisUpdate' => $this->_timeField($thisUpdate) // $this->setStartDate()
                    ),
                    'signatureAlgorithm' => array('algorithm' => $signatureAlgorithm),
                    'signature'          => false // this is going to be overwritten later
            );
        }

        $tbsCertList = &$this->currentCert['tbsCertList'];
        $tbsCertList['issuer'] = $issuer->dn;
        $tbsCertList['thisUpdate'] = $this->_timeField($thisUpdate);

        if (!empty($this->endDate)) {
            $tbsCertList['nextUpdate'] = $this->_timeField($this->endDate); // $this->setEndDate()
        } else {
            unset($tbsCertList['nextUpdate']);
        }

        if (!empty($this->serialNumber)) {
            $crlNumber = $this->serialNumber;
        } else {
            $crlNumber = $this->getExtension('id-ce-cRLNumber');
            // "The CRL number is a non-critical CRL extension that conveys a
            //  monotonically increasing sequence number for a given CRL scope and
            //  CRL issuer.  This extension allows users to easily determine when a
            //  particular CRL supersedes another CRL."
            // -- https://tools.ietf.org/html/rfc5280#section-5.2.3
            $crlNumber = $crlNumber !== false ? $crlNumber->add(new Math_BigInteger(1)) : null;
        }

        $this->removeExtension('id-ce-authorityKeyIdentifier');
        $this->removeExtension('id-ce-issuerAltName');

        // Be sure version >= v2 if some extension found.
        $version = isset($tbsCertList['version']) ? $tbsCertList['version'] : 0;
        if (!$version) {
            if (!empty($tbsCertList['crlExtensions'])) {
                $version = 1; // v2.
            } elseif (!empty($tbsCertList['revokedCertificates'])) {
                foreach ($tbsCertList['revokedCertificates'] as $cert) {
                    if (!empty($cert['crlEntryExtensions'])) {
                        $version = 1; // v2.
                    }
                }
            }

            if ($version) {
                $tbsCertList['version'] = $version;
            }
        }

        // Store additional extensions.
        if (!empty($tbsCertList['version'])) { // At least v2.
            if (!empty($crlNumber)) {
                $this->setExtension('id-ce-cRLNumber', $crlNumber);
            }

            if (isset($issuer->currentKeyIdentifier)) {
                $this->setExtension('id-ce-authorityKeyIdentifier', array(
                        //'authorityCertIssuer' => array(
                        //    array(
                        //        'directoryName' => $issuer->dn
                        //    )
                        //),
                        'keyIdentifier' => $issuer->currentKeyIdentifier
                    ));
                //$extensions = &$tbsCertList['crlExtensions'];
                //if (isset($issuer->serialNumber)) {
                //    $extensions[count($extensions) - 1]['authorityCertSerialNumber'] = $issuer->serialNumber;
                //}
                //unset($extensions);
            }

            $issuerAltName = $this->getExtension('id-ce-subjectAltName', $issuer->currentCert);

            if ($issuerAltName !== false) {
                $this->setExtension('id-ce-issuerAltName', $issuerAltName);
            }
        }

        if (empty($tbsCertList['revokedCertificates'])) {
            unset($tbsCertList['revokedCertificates']);
        }

        unset($tbsCertList);

        // resync $this->signatureSubject
        // save $tbsCertList in case there are any File_ASN1_Element objects in it
        $tbsCertList = $this->currentCert['tbsCertList'];
        $this->loadCRL($this->saveCRL($this->currentCert));

        $result = $this->_sign($issuer->privateKey, $signatureAlgorithm);
        $result['tbsCertList'] = $tbsCertList;

        $this->currentCert = $currentCert;
        $this->signatureSubject = $signatureSubject;

        return $result;
    }

    /**
     * X.509 certificate signing helper function.
     *
     * @param object $key
     * @param File_X509 $subject
     * @param string $signatureAlgorithm
     * @access public
     * @return mixed
     */
    function _sign($key, $signatureAlgorithm)
    {
        switch (strtolower(get_class($key))) {
            case 'crypt_rsa':
                switch ($signatureAlgorithm) {
                    case 'md2WithRSAEncryption':
                    case 'md5WithRSAEncryption':
                    case 'sha1WithRSAEncryption':
                    case 'sha224WithRSAEncryption':
                    case 'sha256WithRSAEncryption':
                    case 'sha384WithRSAEncryption':
                    case 'sha512WithRSAEncryption':
                        $key->setHash(preg_replace('#WithRSAEncryption$#', '', $signatureAlgorithm));
                        $key->setSignatureMode(CRYPT_RSA_SIGNATURE_PKCS1);

                        $this->currentCert['signature'] = base64_encode("\0" . $key->sign($this->signatureSubject));
                        return $this->currentCert;
                }
            default:
                return false;
        }
    }

    /**
     * Set certificate start date
     *
     * @param string $date
     * @access public
     */
    function setStartDate($date)
    {
        if (class_exists('DateTime')) {
            $date = new DateTime($date, new DateTimeZone(@date_default_timezone_get()));
            $this->startDate = $date->format('D, d M Y H:i:s O');
        } else {
            $this->startDate = @date('D, d M Y H:i:s O', @strtotime($date));
        }
    }

    /**
     * Set certificate end date
     *
     * @param string $date
     * @access public
     */
    function setEndDate($date)
    {
        /*
          To indicate that a certificate has no well-defined expiration date,
          the notAfter SHOULD be assigned the GeneralizedTime value of
          99991231235959Z.

          -- http://tools.ietf.org/html/rfc5280#section-4.1.2.5
        */
        if (strtolower($date) == 'lifetime') {
            $temp = '99991231235959Z';
            $asn1 = new File_ASN1();
            $temp = chr(FILE_ASN1_TYPE_GENERALIZED_TIME) . $asn1->_encodeLength(strlen($temp)) . $temp;
            $this->endDate = new File_ASN1_Element($temp);
        } else {
            if (class_exists('DateTime')) {
                $date = new DateTime($date, new DateTimeZone(@date_default_timezone_get()));
                $this->endDate = $date->format('D, d M Y H:i:s O');
            } else {
                $this->endDate = @date('D, d M Y H:i:s O', @strtotime($date));
            }
        }
    }

    /**
     * Set Serial Number
     *
     * @param string $serial
     * @param $base optional
     * @access public
     */
    function setSerialNumber($serial, $base = -256)
    {
        $this->serialNumber = new Math_BigInteger($serial, $base);
    }

    /**
     * Turns the certificate into a certificate authority
     *
     * @access public
     */
    function makeCA()
    {
        $this->caFlag = true;
    }

    /**
     * Check for validity of subarray
     *
     * This is intended for use in conjunction with _subArrayUnchecked(),
     * implementing the checks included in _subArray() but without copying
     * a potentially large array by passing its reference by-value to is_array().
     *
     * @param array $root
     * @param string $path
     * @return boolean
     * @access private
     */
    function _isSubArrayValid($root, $path)
    {
        if (!is_array($root)) {
            return false;
        }

        foreach (explode('/', $path) as $i) {
            if (!is_array($root)) {
                return false;
            }

            if (!isset($root[$i])) {
                return true;
            }

            $root = $root[$i];
        }

        return true;
    }

    /**
     * Get a reference to a subarray
     *
     * This variant of _subArray() does no is_array() checking,
     * so $root should be checked with _isSubArrayValid() first.
     *
     * This is here for performance reasons:
     * Passing a reference (i.e. $root) by-value (i.e. to is_array())
     * creates a copy. If $root is an especially large array, this is expensive.
     *
     * @param array $root
     * @param string $path  absolute path with / as component separator
     * @param bool $create optional
     * @access private
     * @return array|false
     */
    function &_subArrayUnchecked(&$root, $path, $create = false)
    {
        $false = false;

        foreach (explode('/', $path) as $i) {
            if (!isset($root[$i])) {
                if (!$create) {
                    return $false;
                }

                $root[$i] = array();
            }

            $root = &$root[$i];
        }

        return $root;
    }

    /**
     * Get a reference to a subarray
     *
     * @param array $root
     * @param string $path  absolute path with / as component separator
     * @param bool $create optional
     * @access private
     * @return array|false
     */
    function &_subArray(&$root, $path, $create = false)
    {
        $false = false;

        if (!is_array($root)) {
            return $false;
        }

        foreach (explode('/', $path) as $i) {
            if (!is_array($root)) {
                return $false;
            }

            if (!isset($root[$i])) {
                if (!$create) {
                    return $false;
                }

                $root[$i] = array();
            }

            $root = &$root[$i];
        }

        return $root;
    }

    /**
     * Get a reference to an extension subarray
     *
     * @param array $root
     * @param string $path optional absolute path with / as component separator
     * @param bool $create optional
     * @access private
     * @return array|false
     */
    function &_extensions(&$root, $path = null, $create = false)
    {
        if (!isset($root)) {
            $root = $this->currentCert;
        }

        switch (true) {
            case !empty($path):
            case !is_array($root):
                break;
            case isset($root['tbsCertificate']):
                $path = 'tbsCertificate/extensions';
                break;
            case isset($root['tbsCertList']):
                $path = 'tbsCertList/crlExtensions';
                break;
            case isset($root['certificationRequestInfo']):
                $pth = 'certificationRequestInfo/attributes';
                $attributes = &$this->_subArray($root, $pth, $create);

                if (is_array($attributes)) {
                    foreach ($attributes as $key => $value) {
                        if ($value['type'] == 'pkcs-9-at-extensionRequest') {
                            $path = "$pth/$key/value/0";
                            break 2;
                        }
                    }
                    if ($create) {
                        $key = count($attributes);
                        $attributes[] = array('type' => 'pkcs-9-at-extensionRequest', 'value' => array());
                        $path = "$pth/$key/value/0";
                    }
                }
                break;
        }

        $extensions = &$this->_subArray($root, $path, $create);

        if (!is_array($extensions)) {
            $false = false;
            return $false;
        }

        return $extensions;
    }

    /**
     * Remove an Extension
     *
     * @param string $id
     * @param string $path optional
     * @access private
     * @return bool
     */
    function _removeExtension($id, $path = null)
    {
        $extensions = &$this->_extensions($this->currentCert, $path);

        if (!is_array($extensions)) {
            return false;
        }

        $result = false;
        foreach ($extensions as $key => $value) {
            if ($value['extnId'] == $id) {
                unset($extensions[$key]);
                $result = true;
            }
        }

        $extensions = array_values($extensions);
        // fix for https://bugs.php.net/75433 affecting PHP 7.2
        if (!isset($extensions[0])) {
            $extensions = array_splice($extensions, 0, 0);
        }
        return $result;
    }

    /**
     * Get an Extension
     *
     * Returns the extension if it exists and false if not
     *
     * @param string $id
     * @param array $cert optional
     * @param string $path optional
     * @access private
     * @return mixed
     */
    function _getExtension($id, $cert = null, $path = null)
    {
        $extensions = $this->_extensions($cert, $path);

        if (!is_array($extensions)) {
            return false;
        }

        foreach ($extensions as $key => $value) {
            if ($value['extnId'] == $id) {
                return $value['extnValue'];
            }
        }

        return false;
    }

    /**
     * Returns a list of all extensions in use
     *
     * @param array $cert optional
     * @param string $path optional
     * @access private
     * @return array
     */
    function _getExtensions($cert = null, $path = null)
    {
        $exts = $this->_extensions($cert, $path);
        $extensions = array();

        if (is_array($exts)) {
            foreach ($exts as $extension) {
                $extensions[] = $extension['extnId'];
            }
        }

        return $extensions;
    }

    /**
     * Set an Extension
     *
     * @param string $id
     * @param mixed $value
     * @param bool $critical optional
     * @param bool $replace optional
     * @param string $path optional
     * @access private
     * @return bool
     */
    function _setExtension($id, $value, $critical = false, $replace = true, $path = null)
    {
        $extensions = &$this->_extensions($this->currentCert, $path, true);

        if (!is_array($extensions)) {
            return false;
        }

        $newext = array('extnId'  => $id, 'critical' => $critical, 'extnValue' => $value);

        foreach ($extensions as $key => $value) {
            if ($value['extnId'] == $id) {
                if (!$replace) {
                    return false;
                }

                $extensions[$key] = $newext;
                return true;
            }
        }

        $extensions[] = $newext;
        return true;
    }

    /**
     * Remove a certificate, CSR or CRL Extension
     *
     * @param string $id
     * @access public
     * @return bool
     */
    function removeExtension($id)
    {
        return $this->_removeExtension($id);
    }

    /**
     * Get a certificate, CSR or CRL Extension
     *
     * Returns the extension if it exists and false if not
     *
     * @param string $id
     * @param array $cert optional
     * @access public
     * @return mixed
     */
    function getExtension($id, $cert = null)
    {
        return $this->_getExtension($id, $cert);
    }

    /**
     * Returns a list of all extensions in use in certificate, CSR or CRL
     *
     * @param array $cert optional
     * @access public
     * @return array
     */
    function getExtensions($cert = null)
    {
        return $this->_getExtensions($cert);
    }

    /**
     * Set a certificate, CSR or CRL Extension
     *
     * @param string $id
     * @param mixed $value
     * @param bool $critical optional
     * @param bool $replace optional
     * @access public
     * @return bool
     */
    function setExtension($id, $value, $critical = false, $replace = true)
    {
        return $this->_setExtension($id, $value, $critical, $replace);
    }

    /**
     * Remove a CSR attribute.
     *
     * @param string $id
     * @param int $disposition optional
     * @access public
     * @return bool
     */
    function removeAttribute($id, $disposition = FILE_X509_ATTR_ALL)
    {
        $attributes = &$this->_subArray($this->currentCert, 'certificationRequestInfo/attributes');

        if (!is_array($attributes)) {
            return false;
        }

        $result = false;
        foreach ($attributes as $key => $attribute) {
            if ($attribute['type'] == $id) {
                $n = count($attribute['value']);
                switch (true) {
                    case $disposition == FILE_X509_ATTR_APPEND:
                    case $disposition == FILE_X509_ATTR_REPLACE:
                        return false;
                    case $disposition >= $n:
                        $disposition -= $n;
                        break;
                    case $disposition == FILE_X509_ATTR_ALL:
                    case $n == 1:
                        unset($attributes[$key]);
                        $result = true;
                        break;
                    default:
                        unset($attributes[$key]['value'][$disposition]);
                        $attributes[$key]['value'] = array_values($attributes[$key]['value']);
                        $result = true;
                        break;
                }
                if ($result && $disposition != FILE_X509_ATTR_ALL) {
                    break;
                }
            }
        }

        $attributes = array_values($attributes);
        return $result;
    }

    /**
     * Get a CSR attribute
     *
     * Returns the attribute if it exists and false if not
     *
     * @param string $id
     * @param int $disposition optional
     * @param array $csr optional
     * @access public
     * @return mixed
     */
    function getAttribute($id, $disposition = FILE_X509_ATTR_ALL, $csr = null)
    {
        if (empty($csr)) {
            $csr = $this->currentCert;
        }

        $attributes = $this->_subArray($csr, 'certificationRequestInfo/attributes');

        if (!is_array($attributes)) {
            return false;
        }

        foreach ($attributes as $key => $attribute) {
            if ($attribute['type'] == $id) {
                $n = count($attribute['value']);
                switch (true) {
                    case $disposition == FILE_X509_ATTR_APPEND:
                    case $disposition == FILE_X509_ATTR_REPLACE:
                        return false;
                    case $disposition == FILE_X509_ATTR_ALL:
                        return $attribute['value'];
                    case $disposition >= $n:
                        $disposition -= $n;
                        break;
                    default:
                        return $attribute['value'][$disposition];
                }
            }
        }

        return false;
    }

    /**
     * Returns a list of all CSR attributes in use
     *
     * @param array $csr optional
     * @access public
     * @return array
     */
    function getAttributes($csr = null)
    {
        if (empty($csr)) {
            $csr = $this->currentCert;
        }

        $attributes = $this->_subArray($csr, 'certificationRequestInfo/attributes');
        $attrs = array();

        if (is_array($attributes)) {
            foreach ($attributes as $attribute) {
                $attrs[] = $attribute['type'];
            }
        }

        return $attrs;
    }

    /**
     * Set a CSR attribute
     *
     * @param string $id
     * @param mixed $value
     * @param bool $disposition optional
     * @access public
     * @return bool
     */
    function setAttribute($id, $value, $disposition = FILE_X509_ATTR_ALL)
    {
        $attributes = &$this->_subArray($this->currentCert, 'certificationRequestInfo/attributes', true);

        if (!is_array($attributes)) {
            return false;
        }

        switch ($disposition) {
            case FILE_X509_ATTR_REPLACE:
                $disposition = FILE_X509_ATTR_APPEND;
            case FILE_X509_ATTR_ALL:
                $this->removeAttribute($id);
                break;
        }

        foreach ($attributes as $key => $attribute) {
            if ($attribute['type'] == $id) {
                $n = count($attribute['value']);
                switch (true) {
                    case $disposition == FILE_X509_ATTR_APPEND:
                        $last = $key;
                        break;
                    case $disposition >= $n:
                        $disposition -= $n;
                        break;
                    default:
                        $attributes[$key]['value'][$disposition] = $value;
                        return true;
                }
            }
        }

        switch (true) {
            case $disposition >= 0:
                return false;
            case isset($last):
                $attributes[$last]['value'][] = $value;
                break;
            default:
                $attributes[] = array('type' => $id, 'value' => $disposition == FILE_X509_ATTR_ALL ? $value: array($value));
                break;
        }

        return true;
    }

    /**
     * Sets the subject key identifier
     *
     * This is used by the id-ce-authorityKeyIdentifier and the id-ce-subjectKeyIdentifier extensions.
     *
     * @param string $value
     * @access public
     */
    function setKeyIdentifier($value)
    {
        if (empty($value)) {
            unset($this->currentKeyIdentifier);
        } else {
            $this->currentKeyIdentifier = base64_encode($value);
        }
    }

    /**
     * Compute a public key identifier.
     *
     * Although key identifiers may be set to any unique value, this function
     * computes key identifiers from public key according to the two
     * recommended methods (4.2.1.2 RFC 3280).
     * Highly polymorphic: try to accept all possible forms of key:
     * - Key object
     * - File_X509 object with public or private key defined
     * - Certificate or CSR array
     * - File_ASN1_Element object
     * - PEM or DER string
     *
     * @param mixed $key optional
     * @param int $method optional
     * @access public
     * @return string binary key identifier
     */
    function computeKeyIdentifier($key = null, $method = 1)
    {
        if (is_null($key)) {
            $key = $this;
        }

        switch (true) {
            case is_string($key):
                break;
            case is_array($key) && isset($key['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey']):
                return $this->computeKeyIdentifier($key['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey'], $method);
            case is_array($key) && isset($key['certificationRequestInfo']['subjectPKInfo']['subjectPublicKey']):
                return $this->computeKeyIdentifier($key['certificationRequestInfo']['subjectPKInfo']['subjectPublicKey'], $method);
            case !is_object($key):
                return false;
            case strtolower(get_class($key)) == 'file_asn1_element':
                // Assume the element is a bitstring-packed key.
                $asn1 = new File_ASN1();
                $decoded = $asn1->decodeBER($key->element);
                if (empty($decoded)) {
                    return false;
                }
                $raw = $asn1->asn1map($decoded[0], array('type' => FILE_ASN1_TYPE_BIT_STRING));
                if (empty($raw)) {
                    return false;
                }
                $raw = base64_decode($raw);
                // If the key is private, compute identifier from its corresponding public key.
                if (!class_exists('Crypt_RSA')) {
                    include_once 'Crypt/RSA.php';
                }
                $key = new Crypt_RSA();
                if (!$key->loadKey($raw)) {
                    return false;   // Not an unencrypted RSA key.
                }
                if ($key->getPrivateKey() !== false) {  // If private.
                    return $this->computeKeyIdentifier($key, $method);
                }
                $key = $raw;    // Is a public key.
                break;
            case strtolower(get_class($key)) == 'file_x509':
                if (isset($key->publicKey)) {
                    return $this->computeKeyIdentifier($key->publicKey, $method);
                }
                if (isset($key->privateKey)) {
                    return $this->computeKeyIdentifier($key->privateKey, $method);
                }
                if (isset($key->currentCert['tbsCertificate']) || isset($key->currentCert['certificationRequestInfo'])) {
                    return $this->computeKeyIdentifier($key->currentCert, $method);
                }
                return false;
            default: // Should be a key object (i.e.: Crypt_RSA).
                $key = $key->getPublicKey(CRYPT_RSA_PUBLIC_FORMAT_PKCS1);
                break;
        }

        // If in PEM format, convert to binary.
        $key = $this->_extractBER($key);

        // Now we have the key string: compute its sha-1 sum.
        if (!class_exists('Crypt_Hash')) {
            include_once 'Crypt/Hash.php';
        }
        $hash = new Crypt_Hash('sha1');
        $hash = $hash->hash($key);

        if ($method == 2) {
            $hash = substr($hash, -8);
            $hash[0] = chr((ord($hash[0]) & 0x0F) | 0x40);
        }

        return $hash;
    }

    /**
     * Format a public key as appropriate
     *
     * @access private
     * @return array
     */
    function _formatSubjectPublicKey()
    {
        if (!isset($this->publicKey) || !is_object($this->publicKey)) {
            return false;
        }

        switch (strtolower(get_class($this->publicKey))) {
            case 'crypt_rsa':
                // the following two return statements do the same thing. i dunno.. i just prefer the later for some reason.
                // the former is a good example of how to do fuzzing on the public key
                //return new File_ASN1_Element(base64_decode(preg_replace('#-.+-|[\r\n]#', '', $this->publicKey->getPublicKey())));
                return array(
                    'algorithm' => array('algorithm' => 'rsaEncryption'),
                    'subjectPublicKey' => $this->publicKey->getPublicKey(CRYPT_RSA_PUBLIC_FORMAT_PKCS1)
                );
            default:
                return false;
        }
    }

    /**
     * Set the domain name's which the cert is to be valid for
     *
     * @access public
     * @return array
     */
    function setDomain()
    {
        $this->domains = func_get_args();
        $this->removeDNProp('id-at-commonName');
        $this->setDNProp('id-at-commonName', $this->domains[0]);
    }

    /**
     * Set the IP Addresses's which the cert is to be valid for
     *
     * @access public
     * @param string $ipAddress optional
     */
    function setIPAddress()
    {
        $this->ipAddresses = func_get_args();
        /*
        if (!isset($this->domains)) {
            $this->removeDNProp('id-at-commonName');
            $this->setDNProp('id-at-commonName', $this->ipAddresses[0]);
        }
        */
    }

    /**
     * Helper function to build domain array
     *
     * @access private
     * @param string $domain
     * @return array
     */
    function _dnsName($domain)
    {
        return array('dNSName' => $domain);
    }

    /**
     * Helper function to build IP Address array
     *
     * (IPv6 is not currently supported)
     *
     * @access private
     * @param string $address
     * @return array
     */
    function _iPAddress($address)
    {
        return array('iPAddress' => $address);
    }

    /**
     * Get the index of a revoked certificate.
     *
     * @param array $rclist
     * @param string $serial
     * @param bool $create optional
     * @access private
     * @return int|false
     */
    function _revokedCertificate(&$rclist, $serial, $create = false)
    {
        $serial = new Math_BigInteger($serial);

        foreach ($rclist as $i => $rc) {
            if (!($serial->compare($rc['userCertificate']))) {
                return $i;
            }
        }

        if (!$create) {
            return false;
        }

        if (!class_exists('DateTime')) {
            $revocationDate = @date('D, d M Y H:i:s O');
        } else {
            $revocationDate = new DateTime('now', new DateTimeZone(@date_default_timezone_get()));
            $revocationDate = $revocationDate->format('D, d M Y H:i:s O');
        }

        $i = count($rclist);
        $rclist[] = array('userCertificate' => $serial,
                          'revocationDate'  => $this->_timeField($revocationDate));
        return $i;
    }

    /**
     * Revoke a certificate.
     *
     * @param string $serial
     * @param string $date optional
     * @access public
     * @return bool
     */
    function revoke($serial, $date = null)
    {
        if (isset($this->currentCert['tbsCertList'])) {
            if (is_array($rclist = &$this->_subArray($this->currentCert, 'tbsCertList/revokedCertificates', true))) {
                if ($this->_revokedCertificate($rclist, $serial) === false) { // If not yet revoked
                    if (($i = $this->_revokedCertificate($rclist, $serial, true)) !== false) {
                        if (!empty($date)) {
                            $rclist[$i]['revocationDate'] = $this->_timeField($date);
                        }

                        return true;
                    }
                }
            }
        }

        return false;
    }

    /**
     * Unrevoke a certificate.
     *
     * @param string $serial
     * @access public
     * @return bool
     */
    function unrevoke($serial)
    {
        if (is_array($rclist = &$this->_subArray($this->currentCert, 'tbsCertList/revokedCertificates'))) {
            if (($i = $this->_revokedCertificate($rclist, $serial)) !== false) {
                unset($rclist[$i]);
                $rclist = array_values($rclist);
                return true;
            }
        }

        return false;
    }

    /**
     * Get a revoked certificate.
     *
     * @param string $serial
     * @access public
     * @return mixed
     */
    function getRevoked($serial)
    {
        if (is_array($rclist = $this->_subArray($this->currentCert, 'tbsCertList/revokedCertificates'))) {
            if (($i = $this->_revokedCertificate($rclist, $serial)) !== false) {
                return $rclist[$i];
            }
        }

        return false;
    }

    /**
     * List revoked certificates
     *
     * @param array $crl optional
     * @access public
     * @return array
     */
    function listRevoked($crl = null)
    {
        if (!isset($crl)) {
            $crl = $this->currentCert;
        }

        if (!isset($crl['tbsCertList'])) {
            return false;
        }

        $result = array();

        if (is_array($rclist = $this->_subArray($crl, 'tbsCertList/revokedCertificates'))) {
            foreach ($rclist as $rc) {
                $result[] = $rc['userCertificate']->toString();
            }
        }

        return $result;
    }

    /**
     * Remove a Revoked Certificate Extension
     *
     * @param string $serial
     * @param string $id
     * @access public
     * @return bool
     */
    function removeRevokedCertificateExtension($serial, $id)
    {
        if (is_array($rclist = &$this->_subArray($this->currentCert, 'tbsCertList/revokedCertificates'))) {
            if (($i = $this->_revokedCertificate($rclist, $serial)) !== false) {
                return $this->_removeExtension($id, "tbsCertList/revokedCertificates/$i/crlEntryExtensions");
            }
        }

        return false;
    }

    /**
     * Get a Revoked Certificate Extension
     *
     * Returns the extension if it exists and false if not
     *
     * @param string $serial
     * @param string $id
     * @param array $crl optional
     * @access public
     * @return mixed
     */
    function getRevokedCertificateExtension($serial, $id, $crl = null)
    {
        if (!isset($crl)) {
            $crl = $this->currentCert;
        }

        if (is_array($rclist = $this->_subArray($crl, 'tbsCertList/revokedCertificates'))) {
            if (($i = $this->_revokedCertificate($rclist, $serial)) !== false) {
                return $this->_getExtension($id, $crl, "tbsCertList/revokedCertificates/$i/crlEntryExtensions");
            }
        }

        return false;
    }

    /**
     * Returns a list of all extensions in use for a given revoked certificate
     *
     * @param string $serial
     * @param array $crl optional
     * @access public
     * @return array
     */
    function getRevokedCertificateExtensions($serial, $crl = null)
    {
        if (!isset($crl)) {
            $crl = $this->currentCert;
        }

        if (is_array($rclist = $this->_subArray($crl, 'tbsCertList/revokedCertificates'))) {
            if (($i = $this->_revokedCertificate($rclist, $serial)) !== false) {
                return $this->_getExtensions($crl, "tbsCertList/revokedCertificates/$i/crlEntryExtensions");
            }
        }

        return false;
    }

    /**
     * Set a Revoked Certificate Extension
     *
     * @param string $serial
     * @param string $id
     * @param mixed $value
     * @param bool $critical optional
     * @param bool $replace optional
     * @access public
     * @return bool
     */
    function setRevokedCertificateExtension($serial, $id, $value, $critical = false, $replace = true)
    {
        if (isset($this->currentCert['tbsCertList'])) {
            if (is_array($rclist = &$this->_subArray($this->currentCert, 'tbsCertList/revokedCertificates', true))) {
                if (($i = $this->_revokedCertificate($rclist, $serial, true)) !== false) {
                    return $this->_setExtension($id, $value, $critical, $replace, "tbsCertList/revokedCertificates/$i/crlEntryExtensions");
                }
            }
        }

        return false;
    }

    /**
     * Extract raw BER from Base64 encoding
     *
     * @access private
     * @param string $str
     * @return string
     */
    function _extractBER($str)
    {
        /* X.509 certs are assumed to be base64 encoded but sometimes they'll have additional things in them
         * above and beyond the ceritificate.
         * ie. some may have the following preceding the -----BEGIN CERTIFICATE----- line:
         *
         * Bag Attributes
         *     localKeyID: 01 00 00 00
         * subject=/O=organization/OU=org unit/CN=common name
         * issuer=/O=organization/CN=common name
         */
        $temp = preg_replace('#.*?^-+[^-]+-+[\r\n ]*$#ms', '', $str, 1);
        // remove the -----BEGIN CERTIFICATE----- and -----END CERTIFICATE----- stuff
        $temp = preg_replace('#-+[^-]+-+#', '', $temp);
        // remove new lines
        $temp = str_replace(array("\r", "\n", ' '), '', $temp);
        $temp = preg_match('#^[a-zA-Z\d/+]*={0,2}$#', $temp) ? base64_decode($temp) : false;
        return $temp != false ? $temp : $str;
    }

    /**
     * Returns the OID corresponding to a name
     *
     * What's returned in the associative array returned by loadX509() (or load*()) is either a name or an OID if
     * no OID to name mapping is available. The problem with this is that what may be an unmapped OID in one version
     * of phpseclib may not be unmapped in the next version, so apps that are looking at this OID may not be able
     * to work from version to version.
     *
     * This method will return the OID if a name is passed to it and if no mapping is avialable it'll assume that
     * what's being passed to it already is an OID and return that instead. A few examples.
     *
     * getOID('2.16.840.1.101.3.4.2.1') == '2.16.840.1.101.3.4.2.1'
     * getOID('id-sha256') == '2.16.840.1.101.3.4.2.1'
     * getOID('zzz') == 'zzz'
     *
     * @access public
     * @return string
     */
    function getOID($name)
    {
        static $reverseMap;
        if (!isset($reverseMap)) {
            $reverseMap = array_flip($this->oids);
        }
        return isset($reverseMap[$name]) ? $reverseMap[$name] : $name;
    }
}
vendor/phpseclib/phpseclib/phpseclib/bootstrap.php000064400000000660151327705700016473 0ustar00<?php
/**
 * Bootstrapping File for phpseclib
 *
 * @license http://www.opensource.org/licenses/mit-license.html MIT License
 */

if (extension_loaded('mbstring')) {
    // 2 - MB_OVERLOAD_STRING
    if (ini_get('mbstring.func_overload') & 2) {
        throw new \UnexpectedValueException(
            'Overloading of string functions using mbstring.func_overload ' .
            'is not supported by phpseclib.'
        );
    }
}
vendor/phpseclib/phpseclib/phpseclib/System/SSH/Agent.php000064400000036651151327705700017446 0ustar00<?php

/**
 * Pure-PHP ssh-agent client.
 *
 * PHP versions 4 and 5
 *
 * Here are some examples of how to use this library:
 * <code>
 * <?php
 *    include 'System/SSH/Agent.php';
 *    include 'Net/SSH2.php';
 *
 *    $agent = new System_SSH_Agent();
 *
 *    $ssh = new Net_SSH2('www.domain.tld');
 *    if (!$ssh->login('username', $agent)) {
 *        exit('Login Failed');
 *    }
 *
 *    echo $ssh->exec('pwd');
 *    echo $ssh->exec('ls -la');
 * ?>
 * </code>
 *
 * LICENSE: Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 *
 * @category  System
 * @package   System_SSH_Agent
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2014 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      http://phpseclib.sourceforge.net
 * @internal  See http://api.libssh.org/rfc/PROTOCOL.agent
 */

/**#@+
 * Message numbers
 *
 * @access private
 */
// to request SSH1 keys you have to use SSH_AGENTC_REQUEST_RSA_IDENTITIES (1)
define('SYSTEM_SSH_AGENTC_REQUEST_IDENTITIES', 11);
// this is the SSH2 response; the SSH1 response is SSH_AGENT_RSA_IDENTITIES_ANSWER (2).
define('SYSTEM_SSH_AGENT_IDENTITIES_ANSWER', 12);
define('SYSTEM_SSH_AGENT_FAILURE', 5);
// the SSH1 request is SSH_AGENTC_RSA_CHALLENGE (3)
define('SYSTEM_SSH_AGENTC_SIGN_REQUEST', 13);
// the SSH1 response is SSH_AGENT_RSA_RESPONSE (4)
define('SYSTEM_SSH_AGENT_SIGN_RESPONSE', 14);
/**#@-*/

/**@+
 * Agent forwarding status
 *
 * @access private
 */
// no forwarding requested and not active
define('SYSTEM_SSH_AGENT_FORWARD_NONE', 0);
// request agent forwarding when opportune
define('SYSTEM_SSH_AGENT_FORWARD_REQUEST', 1);
// forwarding has been request and is active
define('SYSTEM_SSH_AGENT_FORWARD_ACTIVE', 2);
/**#@-*/

/**@+
 * Signature Flags
 *
 * See https://tools.ietf.org/html/draft-miller-ssh-agent-00#section-5.3
 *
 * @access private
 */
define('SYSTEM_SSH_AGENT_RSA2_256', 2);
define('SYSTEM_SSH_AGENT_RSA2_512', 4);
/**#@-*/

/**
 * Pure-PHP ssh-agent client identity object
 *
 * Instantiation should only be performed by System_SSH_Agent class.
 * This could be thought of as implementing an interface that Crypt_RSA
 * implements. ie. maybe a Net_SSH_Auth_PublicKey interface or something.
 * The methods in this interface would be getPublicKey, setSignatureMode
 * and sign since those are the methods phpseclib looks for to perform
 * public key authentication.
 *
 * @package System_SSH_Agent
 * @author  Jim Wigginton <terrafrost@php.net>
 * @access  internal
 */
class System_SSH_Agent_Identity
{
    /**
     * Key Object
     *
     * @var Crypt_RSA
     * @access private
     * @see self::getPublicKey()
     */
    var $key;

    /**
     * Key Blob
     *
     * @var string
     * @access private
     * @see self::sign()
     */
    var $key_blob;

    /**
     * Socket Resource
     *
     * @var resource
     * @access private
     * @see self::sign()
     */
    var $fsock;

    /**
     * Signature flags
     *
     * @var int
     * @access private
     * @see self::sign()
     * @see self::setHash()
     */
    var $flags = 0;

    /**
     * Default Constructor.
     *
     * @param resource $fsock
     * @return System_SSH_Agent_Identity
     * @access private
     */
    function __construct($fsock)
    {
        $this->fsock = $fsock;
    }

    /**
     * PHP4 compatible Default Constructor.
     *
     * @see self::__construct()
     * @param resource $fsock
     * @access public
     */
    function System_SSH_Agent_Identity($fsock)
    {
        $this->__construct($fsock);
    }

    /**
     * Set Public Key
     *
     * Called by System_SSH_Agent::requestIdentities()
     *
     * @param Crypt_RSA $key
     * @access private
     */
    function setPublicKey($key)
    {
        $this->key = $key;
        $this->key->setPublicKey();
    }

    /**
     * Set Public Key
     *
     * Called by System_SSH_Agent::requestIdentities(). The key blob could be extracted from $this->key
     * but this saves a small amount of computation.
     *
     * @param string $key_blob
     * @access private
     */
    function setPublicKeyBlob($key_blob)
    {
        $this->key_blob = $key_blob;
    }

    /**
     * Get Public Key
     *
     * Wrapper for $this->key->getPublicKey()
     *
     * @param int $format optional
     * @return mixed
     * @access public
     */
    function getPublicKey($format = null)
    {
        return !isset($format) ? $this->key->getPublicKey() : $this->key->getPublicKey($format);
    }

    /**
     * Set Signature Mode
     *
     * Doesn't do anything as ssh-agent doesn't let you pick and choose the signature mode. ie.
     * ssh-agent's only supported mode is CRYPT_RSA_SIGNATURE_PKCS1
     *
     * @param int $mode
     * @access public
     */
    function setSignatureMode($mode)
    {
    }

    /**
     * Set Hash
     *
     * ssh-agent doesn't support using hashes for RSA other than SHA1
     *
     * @param string $hash
     * @access public
     */
    function setHash($hash)
    {
        $this->flags = 0;
        switch ($hash) {
            case 'sha1':
                break;
            case 'sha256':
                $this->flags = SYSTEM_SSH_AGENT_RSA2_256;
                break;
            case 'sha512':
                $this->flags = SYSTEM_SSH_AGENT_RSA2_512;
                break;
            default:
                user_error('The only supported hashes for RSA are sha1, sha256 and sha512');
        }
    }

    /**
     * Create a signature
     *
     * See "2.6.2 Protocol 2 private key signature request"
     *
     * @param string $message
     * @return string
     * @access public
     */
    function sign($message)
    {
        // the last parameter (currently 0) is for flags and ssh-agent only defines one flag (for ssh-dss): SSH_AGENT_OLD_SIGNATURE
        $packet = pack('CNa*Na*N', SYSTEM_SSH_AGENTC_SIGN_REQUEST, strlen($this->key_blob), $this->key_blob, strlen($message), $message, $this->flags);
        $packet = pack('Na*', strlen($packet), $packet);
        if (strlen($packet) != fputs($this->fsock, $packet)) {
            user_error('Connection closed during signing');
        }

        $length = current(unpack('N', fread($this->fsock, 4)));
        $type = ord(fread($this->fsock, 1));
        if ($type != SYSTEM_SSH_AGENT_SIGN_RESPONSE) {
            user_error('Unable to retreive signature');
        }

        $signature_blob = fread($this->fsock, $length - 1);
        $length = current(unpack('N', $this->_string_shift($signature_blob, 4)));
        if ($length != strlen($signature_blob)) {
            user_error('Malformed signature blob');
        }
        $length = current(unpack('N', $this->_string_shift($signature_blob, 4)));
        if ($length > strlen($signature_blob) + 4) {
            user_error('Malformed signature blob');
        }
        $type = $this->_string_shift($signature_blob, $length);
        $this->_string_shift($signature_blob, 4);

        return $signature_blob;
    }

    /**
     * String Shift
     *
     * Inspired by array_shift
     *
     * @param string $string
     * @param int $index
     * @return string
     * @access private
     */
    function _string_shift(&$string, $index = 1)
    {
        $substr = substr($string, 0, $index);
        $string = substr($string, $index);
        return $substr;
    }
}

/**
 * Pure-PHP ssh-agent client identity factory
 *
 * requestIdentities() method pumps out System_SSH_Agent_Identity objects
 *
 * @package System_SSH_Agent
 * @author  Jim Wigginton <terrafrost@php.net>
 * @access  public
 */
class System_SSH_Agent
{
    /**
     * Socket Resource
     *
     * @var resource
     * @access private
     */
    var $fsock;

    /**
     * Agent forwarding status
     *
     * @access private
     */
    var $forward_status = SYSTEM_SSH_AGENT_FORWARD_NONE;

    /**
     * Buffer for accumulating forwarded authentication
     * agent data arriving on SSH data channel destined
     * for agent unix socket
     *
     * @access private
     */
    var $socket_buffer = '';

    /**
     * Tracking the number of bytes we are expecting
     * to arrive for the agent socket on the SSH data
     * channel
     */
    var $expected_bytes = 0;

    /**
     * Default Constructor
     *
     * @return System_SSH_Agent
     * @access public
     */
    function __construct($address = null)
    {
        if (!$address) {
            switch (true) {
                case isset($_SERVER['SSH_AUTH_SOCK']):
                    $address = $_SERVER['SSH_AUTH_SOCK'];
                    break;
                case isset($_ENV['SSH_AUTH_SOCK']):
                    $address = $_ENV['SSH_AUTH_SOCK'];
                    break;
                default:
                    user_error('SSH_AUTH_SOCK not found');
                    return false;
            }
        }

        $this->fsock = fsockopen('unix://' . $address, 0, $errno, $errstr);
        if (!$this->fsock) {
            user_error("Unable to connect to ssh-agent (Error $errno: $errstr)");
        }
    }

    /**
     * PHP4 compatible Default Constructor.
     *
     * @see self::__construct()
     * @access public
     */
    function System_SSH_Agent($address = null)
    {
        $this->__construct($address);
    }

    /**
     * Request Identities
     *
     * See "2.5.2 Requesting a list of protocol 2 keys"
     * Returns an array containing zero or more System_SSH_Agent_Identity objects
     *
     * @return array
     * @access public
     */
    function requestIdentities()
    {
        if (!$this->fsock) {
            return array();
        }

        $packet = pack('NC', 1, SYSTEM_SSH_AGENTC_REQUEST_IDENTITIES);
        if (strlen($packet) != fputs($this->fsock, $packet)) {
            user_error('Connection closed while requesting identities');
            return array();
        }

        $length = current(unpack('N', fread($this->fsock, 4)));
        $type = ord(fread($this->fsock, 1));
        if ($type != SYSTEM_SSH_AGENT_IDENTITIES_ANSWER) {
            user_error('Unable to request identities');
            return array();
        }

        $identities = array();
        $keyCount = current(unpack('N', fread($this->fsock, 4)));
        for ($i = 0; $i < $keyCount; $i++) {
            $length = current(unpack('N', fread($this->fsock, 4)));
            $key_blob = fread($this->fsock, $length);
            $key_str = 'ssh-rsa ' . base64_encode($key_blob);
            $length = current(unpack('N', fread($this->fsock, 4)));
            if ($length) {
                $key_str.= ' ' . fread($this->fsock, $length);
            }
            $length = current(unpack('N', substr($key_blob, 0, 4)));
            $key_type = substr($key_blob, 4, $length);
            switch ($key_type) {
                case 'ssh-rsa':
                    if (!class_exists('Crypt_RSA')) {
                        include_once 'Crypt/RSA.php';
                    }
                    $key = new Crypt_RSA();
                    $key->loadKey($key_str);
                    break;
                case 'ssh-dss':
                    // not currently supported
                    break;
            }
            // resources are passed by reference by default
            if (isset($key)) {
                $identity = new System_SSH_Agent_Identity($this->fsock);
                $identity->setPublicKey($key);
                $identity->setPublicKeyBlob($key_blob);
                $identities[] = $identity;
                unset($key);
            }
        }

        return $identities;
    }

    /**
     * Signal that agent forwarding should
     * be requested when a channel is opened
     *
     * @param Net_SSH2 $ssh
     * @return bool
     * @access public
     */
    function startSSHForwarding($ssh)
    {
        if ($this->forward_status == SYSTEM_SSH_AGENT_FORWARD_NONE) {
            $this->forward_status = SYSTEM_SSH_AGENT_FORWARD_REQUEST;
        }
    }

    /**
     * Request agent forwarding of remote server
     *
     * @param Net_SSH2 $ssh
     * @return bool
     * @access private
     */
    function _request_forwarding($ssh)
    {
        $request_channel = $ssh->_get_open_channel();
        if ($request_channel === false) {
            return false;
        }

        $packet = pack(
            'CNNa*C',
            NET_SSH2_MSG_CHANNEL_REQUEST,
            $ssh->server_channels[$request_channel],
            strlen('auth-agent-req@openssh.com'),
            'auth-agent-req@openssh.com',
            1
        );

        $ssh->channel_status[$request_channel] = NET_SSH2_MSG_CHANNEL_REQUEST;

        if (!$ssh->_send_binary_packet($packet)) {
            return false;
        }

        $response = $ssh->_get_channel_packet($request_channel);
        if ($response === false) {
            return false;
        }

        $ssh->channel_status[$request_channel] = NET_SSH2_MSG_CHANNEL_OPEN;
        $this->forward_status = SYSTEM_SSH_AGENT_FORWARD_ACTIVE;

        return true;
    }

    /**
     * On successful channel open
     *
     * This method is called upon successful channel
     * open to give the SSH Agent an opportunity
     * to take further action. i.e. request agent forwarding
     *
     * @param Net_SSH2 $ssh
     * @access private
     */
    function _on_channel_open($ssh)
    {
        if ($this->forward_status == SYSTEM_SSH_AGENT_FORWARD_REQUEST) {
            $this->_request_forwarding($ssh);
        }
    }

    /**
     * Forward data to SSH Agent and return data reply
     *
     * @param string $data
     * @return data from SSH Agent
     * @access private
     */
    function _forward_data($data)
    {
        if ($this->expected_bytes > 0) {
            $this->socket_buffer.= $data;
            $this->expected_bytes -= strlen($data);
        } else {
            $agent_data_bytes = current(unpack('N', $data));
            $current_data_bytes = strlen($data);
            $this->socket_buffer = $data;
            if ($current_data_bytes != $agent_data_bytes + 4) {
                $this->expected_bytes = ($agent_data_bytes + 4) - $current_data_bytes;
                return false;
            }
        }

        if (strlen($this->socket_buffer) != fwrite($this->fsock, $this->socket_buffer)) {
            user_error('Connection closed attempting to forward data to SSH agent');
        }

        $this->socket_buffer = '';
        $this->expected_bytes = 0;

        $agent_reply_bytes = current(unpack('N', fread($this->fsock, 4)));

        $agent_reply_data = fread($this->fsock, $agent_reply_bytes);
        $agent_reply_data = current(unpack('a*', $agent_reply_data));

        return pack('Na*', $agent_reply_bytes, $agent_reply_data);
    }
}
vendor/phpseclib/phpseclib/phpseclib/System/SSH_Agent.php000064400000003444151327705700017520 0ustar00<?php
/**
 * Pure-PHP ssh-agent client wrapper
 *
 * PHP versions 4 and 5
 *
 * Originally System_SSH_Agent was accessed as System/SSH_Agent.php instead of
 * System/SSH/Agent.php. The problem with this is that PSR0 compatible autoloaders
 * don't support that kind of directory layout hence the package being moved and
 * this "alias" being created to maintain backwards compatibility.
 *
 * LICENSE: Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 *
 * @category  System
 * @package   System_SSH_Agent
 * @author    Jim Wigginton <terrafrost@php.net>
 * @copyright 2014 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      http://phpseclib.sourceforge.net
 * @internal  See http://api.libssh.org/rfc/PROTOCOL.agent
 */

require_once 'SSH/Agent.php';
vendor/phpseclib/phpseclib/composer.json000064400000004356151327705700014524 0ustar00{
    "name": "phpseclib/phpseclib",
    "type": "library",
    "description": "PHP Secure Communications Library - Pure-PHP implementations of RSA, AES, SSH2, SFTP, X.509 etc.",
    "keywords": [
        "security",
        "crypto",
        "cryptography",
        "encryption",
        "signature",
        "signing",
        "rsa",
        "aes",
        "blowfish",
        "twofish",
        "ssh",
        "sftp",
        "x509",
        "x.509",
        "asn1",
        "asn.1",
        "BigInteger"
        ],
    "homepage": "http://phpseclib.sourceforge.net",
    "license": "MIT",
    "authors": [
        {
            "name": "Jim Wigginton",
            "email": "terrafrost@php.net",
            "role": "Lead Developer"
        },
        {
            "name": "Patrick Monnerat",
            "email": "pm@datasphere.ch",
            "role": "Developer"
        },
        {
            "name": "Andreas Fischer",
            "email": "bantu@phpbb.com",
            "role": "Developer"
        },
        {
            "name": "Hans-Jürgen Petrich",
            "email": "petrich@tronic-media.com",
            "role": "Developer"
        },
        {
            "name": "Graham Campbell",
            "email": "graham@alt-three.com",
            "role": "Developer"
        }
    ],
    "require": {
        "php": ">=5.0.0"
    },
    "require-dev": {
        "phing/phing": "~2.7",
        "phpunit/phpunit": "^4.8.35|^5.7|^6.0",
        "sami/sami": "~2.0",
        "squizlabs/php_codesniffer": "~2.0"
    },
    "suggest": {
        "ext-mcrypt": "Install the Mcrypt extension in order to speed up a wide variety of cryptographic operations.",
        "ext-gmp": "Install the GMP (GNU Multiple Precision) extension in order to speed up arbitrary precision integer arithmetic operations.",
        "pear-pear/PHP_Compat": "Install PHP_Compat to get phpseclib working on PHP < 5.0.0."
    },
    "include-path": ["phpseclib/"],
    "autoload": {
        "psr-0": {
            "Crypt": "phpseclib/",
            "File": "phpseclib/",
            "Math": "phpseclib/",
            "Net": "phpseclib/",
            "System": "phpseclib/"
        },
        "files": [
            "phpseclib/bootstrap.php",
            "phpseclib/Crypt/Random.php"
        ]
    }
}
vendor/phpseclib/phpseclib/README.md000064400000004224151327705700013253 0ustar00# phpseclib - PHP Secure Communications Library

[![Build Status](https://travis-ci.org/phpseclib/phpseclib.svg?branch=1.0)](https://travis-ci.org/phpseclib/phpseclib)

MIT-licensed pure-PHP implementations of an arbitrary-precision integer
arithmetic library, fully PKCS#1 (v2.1) compliant RSA, DES, 3DES, RC4, Rijndael,
AES, Blowfish, Twofish, SSH-1, SSH-2, SFTP, and X.509

* [Browse Git](https://github.com/phpseclib/phpseclib)
* [Code Coverage Report](https://coverage.phpseclib.org/1.0/latest/)

## Documentation

* [Documentation / Manual](http://phpseclib.sourceforge.net/)
* [API Documentation](https://api.phpseclib.org/1.0/) (generated by Sami)

## Branches

### master

* Development Branch
* Unstable API
* Do not use in production

### 2.0

* Long term support (LTS) release
* Modernized version of 1.0
* Minimum PHP version: 5.3.3
* PSR-4 autoloading with namespace rooted at `\phpseclib`
* Install via Composer: `composer require phpseclib/phpseclib ~2.0`

### 1.0

* Long term support (LTS) release
* PHP4 compatible
* Composer compatible (PSR-0 autoloading)
* Install using Composer: `composer require phpseclib/phpseclib ~1.0`
* Install using PEAR: See [phpseclib PEAR Channel Documentation](http://phpseclib.sourceforge.net/pear.htm)
* [Download 1.0.14 as ZIP](http://sourceforge.net/projects/phpseclib/files/phpseclib1.0.14.zip/download)

## Support

Need Support?

* [Checkout Questions and Answers on Stack Overflow](http://stackoverflow.com/questions/tagged/phpseclib)
* [Create a Support Ticket on GitHub](https://github.com/phpseclib/phpseclib/issues/new)
* [Browse the Support Forum](http://www.frostjedi.com/phpbb/viewforum.php?f=46) (no longer in use)

## Contributing

1. Fork the Project

2. Ensure you have Composer installed (see [Composer Download Instructions](https://getcomposer.org/download/))

3. Install Development Dependencies

    ``` sh
    composer install
    ```

4. Create a Feature Branch

5. (Recommended) Run the Test Suite

    ``` sh
    vendor/bin/phpunit
    ```
6. (Recommended) Check whether your code conforms to our Coding Standards by running

    ``` sh
    vendor/bin/phing -f build/build.xml sniff
    ```

7. Send us a Pull Request
vendor/phpseclib/phpseclib/appveyor.yml000064400000001427151327705700014366 0ustar00build: false
shallow_clone: false
platform:
  - x86
  - x64
clone_folder: C:\projects\phpseclib

install:
  - cinst -y OpenSSL.Light
  - SET PATH=C:\Program Files\OpenSSL;%PATH%
  - sc config wuauserv start= auto
  - net start wuauserv
  - cinst -y php --version 5.6.30
  - cd c:\tools\php56
  - copy php.ini-production php.ini
  - echo date.timezone="UTC" >> php.ini
  - echo extension_dir=ext >> php.ini
  - echo extension=php_openssl.dll >> php.ini
  - echo extension=php_gmp.dll >> php.ini
  - cd C:\projects\phpseclib
  - SET PATH=C:\tools\php56;%PATH%
  - php.exe -r "readfile('http://getcomposer.org/installer');" | php.exe
  - php.exe composer.phar install --prefer-source --no-interaction

test_script:
  - cd C:\projects\phpseclib
  - vendor\bin\phpunit.bat tests/Windows32Test.phpvendor/phpseclib/phpseclib/LICENSE000064400000002130151327705700012773 0ustar00Copyright 2007-2016 TerraFrost and other contributors
http://phpseclib.sourceforge.net/

Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:

The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
vendor/guzzlehttp/psr7/CHANGELOG.md000064400000020004151327705700012770 0ustar00# Change Log


All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).


## Unreleased

## 1.8.4 - 2022-03-20

### Fixed

- Validate header values properly

## 1.8.3 - 2021-10-05

### Fixed

- Return `null` in caching stream size if remote size is `null`

## 1.8.2 - 2021-04-26

### Fixed

- Handle possibly unset `url` in `stream_get_meta_data`

## 1.8.1 - 2021-03-21

### Fixed

- Issue parsing IPv6 URLs
- Issue modifying ServerRequest lost all its attributes

## 1.8.0 - 2021-03-21

### Added

- Locale independent URL parsing
- Most classes got a `@final` annotation to prepare for 2.0

### Fixed

- Issue when creating stream from `php://input` and curl-ext is not installed
- Broken `Utils::tryFopen()` on PHP 8

## 1.7.0 - 2020-09-30

### Added

- Replaced functions by static methods

### Fixed

- Converting a non-seekable stream to a string
- Handle multiple Set-Cookie correctly
- Ignore array keys in header values when merging
- Allow multibyte characters to be parsed in `Message:bodySummary()`

### Changed

- Restored partial HHVM 3 support


## [1.6.1] - 2019-07-02

### Fixed

- Accept null and bool header values again


## [1.6.0] - 2019-06-30

### Added

- Allowed version `^3.0` of `ralouphie/getallheaders` dependency (#244)
- Added MIME type for WEBP image format (#246)
- Added more validation of values according to PSR-7 and RFC standards, e.g. status code range (#250, #272)

### Changed

- Tests don't pass with HHVM 4.0, so HHVM support got dropped. Other libraries like composer have done the same. (#262)
- Accept port number 0 to be valid (#270)

### Fixed

- Fixed subsequent reads from `php://input` in ServerRequest (#247)
- Fixed readable/writable detection for certain stream modes (#248)
- Fixed encoding of special characters in the `userInfo` component of an URI (#253)


## [1.5.2] - 2018-12-04

### Fixed

- Check body size when getting the message summary


## [1.5.1] - 2018-12-04

### Fixed

- Get the summary of a body only if it is readable


## [1.5.0] - 2018-12-03

### Added

- Response first-line to response string exception (fixes #145)
- A test for #129 behavior
- `get_message_body_summary` function in order to get the message summary
- `3gp` and `mkv` mime types

### Changed

- Clarify exception message when stream is detached

### Deprecated

- Deprecated parsing folded header lines as per RFC 7230

### Fixed

- Fix `AppendStream::detach` to not close streams
- `InflateStream` preserves `isSeekable` attribute of the underlying stream
- `ServerRequest::getUriFromGlobals` to support URLs in query parameters


Several other fixes and improvements.


## [1.4.2] - 2017-03-20

### Fixed

- Reverted BC break to `Uri::resolve` and `Uri::removeDotSegments` by removing
  calls to `trigger_error` when deprecated methods are invoked.


## [1.4.1] - 2017-02-27

### Added

- Rriggering of silenced deprecation warnings.

### Fixed

- Reverted BC break by reintroducing behavior to automagically fix a URI with a
  relative path and an authority by adding a leading slash to the path. It's only
  deprecated now.


## [1.4.0] - 2017-02-21

### Added

- Added common URI utility methods based on RFC 3986 (see documentation in the readme):
  - `Uri::isDefaultPort`
  - `Uri::isAbsolute`
  - `Uri::isNetworkPathReference`
  - `Uri::isAbsolutePathReference`
  - `Uri::isRelativePathReference`
  - `Uri::isSameDocumentReference`
  - `Uri::composeComponents`
  - `UriNormalizer::normalize`
  - `UriNormalizer::isEquivalent`
  - `UriResolver::relativize`

### Changed

- Ensure `ServerRequest::getUriFromGlobals` returns a URI in absolute form.
- Allow `parse_response` to parse a response without delimiting space and reason.
- Ensure each URI modification results in a valid URI according to PSR-7 discussions.
  Invalid modifications will throw an exception instead of returning a wrong URI or
  doing some magic.
  - `(new Uri)->withPath('foo')->withHost('example.com')` will throw an exception
    because the path of a URI with an authority must start with a slash "/" or be empty
  - `(new Uri())->withScheme('http')` will return `'http://localhost'`

### Deprecated

- `Uri::resolve` in favor of `UriResolver::resolve`
- `Uri::removeDotSegments` in favor of `UriResolver::removeDotSegments`

### Fixed

- `Stream::read` when length parameter <= 0.
- `copy_to_stream` reads bytes in chunks instead of `maxLen` into memory.
- `ServerRequest::getUriFromGlobals` when `Host` header contains port.
- Compatibility of URIs with `file` scheme and empty host.


## [1.3.1] - 2016-06-25

### Fixed

- `Uri::__toString` for network path references, e.g. `//example.org`.
- Missing lowercase normalization for host.
- Handling of URI components in case they are `'0'` in a lot of places,
  e.g. as a user info password.
- `Uri::withAddedHeader` to correctly merge headers with different case.
- Trimming of header values in `Uri::withAddedHeader`. Header values may
  be surrounded by whitespace which should be ignored according to RFC 7230
  Section 3.2.4. This does not apply to header names.
- `Uri::withAddedHeader` with an array of header values.
- `Uri::resolve` when base path has no slash and handling of fragment.
- Handling of encoding in `Uri::with(out)QueryValue` so one can pass the
  key/value both in encoded as well as decoded form to those methods. This is
  consistent with withPath, withQuery etc.
- `ServerRequest::withoutAttribute` when attribute value is null.


## [1.3.0] - 2016-04-13

### Added

- Remaining interfaces needed for full PSR7 compatibility
  (ServerRequestInterface, UploadedFileInterface, etc.).
- Support for stream_for from scalars.

### Changed

- Can now extend Uri.

### Fixed
- A bug in validating request methods by making it more permissive.


## [1.2.3] - 2016-02-18

### Fixed

- Support in `GuzzleHttp\Psr7\CachingStream` for seeking forward on remote
  streams, which can sometimes return fewer bytes than requested with `fread`.
- Handling of gzipped responses with FNAME headers.


## [1.2.2] - 2016-01-22

### Added

- Support for URIs without any authority.
- Support for HTTP 451 'Unavailable For Legal Reasons.'
- Support for using '0' as a filename.
- Support for including non-standard ports in Host headers.


## [1.2.1] - 2015-11-02

### Changes

- Now supporting negative offsets when seeking to SEEK_END.


## [1.2.0] - 2015-08-15

### Changed

- Body as `"0"` is now properly added to a response.
- Now allowing forward seeking in CachingStream.
- Now properly parsing HTTP requests that contain proxy targets in
  `parse_request`.
- functions.php is now conditionally required.
- user-info is no longer dropped when resolving URIs.


## [1.1.0] - 2015-06-24

### Changed

- URIs can now be relative.
- `multipart/form-data` headers are now overridden case-insensitively.
- URI paths no longer encode the following characters because they are allowed
  in URIs: "(", ")", "*", "!", "'"
- A port is no longer added to a URI when the scheme is missing and no port is
  present.


## 1.0.0 - 2015-05-19

Initial release.

Currently unsupported:

- `Psr\Http\Message\ServerRequestInterface`
- `Psr\Http\Message\UploadedFileInterface`



[1.6.0]: https://github.com/guzzle/psr7/compare/1.5.2...1.6.0
[1.5.2]: https://github.com/guzzle/psr7/compare/1.5.1...1.5.2
[1.5.1]: https://github.com/guzzle/psr7/compare/1.5.0...1.5.1
[1.5.0]: https://github.com/guzzle/psr7/compare/1.4.2...1.5.0
[1.4.2]: https://github.com/guzzle/psr7/compare/1.4.1...1.4.2
[1.4.1]: https://github.com/guzzle/psr7/compare/1.4.0...1.4.1
[1.4.0]: https://github.com/guzzle/psr7/compare/1.3.1...1.4.0
[1.3.1]: https://github.com/guzzle/psr7/compare/1.3.0...1.3.1
[1.3.0]: https://github.com/guzzle/psr7/compare/1.2.3...1.3.0
[1.2.3]: https://github.com/guzzle/psr7/compare/1.2.2...1.2.3
[1.2.2]: https://github.com/guzzle/psr7/compare/1.2.1...1.2.2
[1.2.1]: https://github.com/guzzle/psr7/compare/1.2.0...1.2.1
[1.2.0]: https://github.com/guzzle/psr7/compare/1.1.0...1.2.0
[1.1.0]: https://github.com/guzzle/psr7/compare/1.0.0...1.1.0
vendor/guzzlehttp/psr7/LICENSE000064400000002572151327705700012176 0ustar00The MIT License (MIT)

Copyright (c) 2015 Michael Dowling <mtdowling@gmail.com>
Copyright (c) 2015 Márk Sági-Kazár <mark.sagikazar@gmail.com>
Copyright (c) 2015 Graham Campbell <hello@gjcampbell.co.uk>
Copyright (c) 2016 Tobias Schultze <webmaster@tubo-world.de>
Copyright (c) 2016 George Mponos <gmponos@gmail.com>
Copyright (c) 2018 Tobias Nyholm <tobias.nyholm@gmail.com>

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
vendor/guzzlehttp/psr7/.editorconfig000064400000000223151327705700013635 0ustar00root = true

[*]
charset = utf-8
end_of_line = lf
indent_size = 4
indent_style = space
insert_final_newline = true
trim_trailing_whitespace = true
vendor/guzzlehttp/psr7/src/UriNormalizer.php000064400000020231151327705700015263 0ustar00<?php

namespace WPvividGuzzleHttp\Psr7;

use WPvividPsr\Http\Message\UriInterface;

/**
 * Provides methods to normalize and compare URIs.
 *
 * @author Tobias Schultze
 *
 * @link https://tools.ietf.org/html/rfc3986#section-6
 */
final class UriNormalizer
{
    /**
     * Default normalizations which only include the ones that preserve semantics.
     *
     * self::CAPITALIZE_PERCENT_ENCODING | self::DECODE_UNRESERVED_CHARACTERS | self::CONVERT_EMPTY_PATH |
     * self::REMOVE_DEFAULT_HOST | self::REMOVE_DEFAULT_PORT | self::REMOVE_DOT_SEGMENTS
     */
    const PRESERVING_NORMALIZATIONS = 63;

    /**
     * All letters within a percent-encoding triplet (e.g., "%3A") are case-insensitive, and should be capitalized.
     *
     * Example: http://example.org/a%c2%b1b → http://example.org/a%C2%B1b
     */
    const CAPITALIZE_PERCENT_ENCODING = 1;

    /**
     * Decodes percent-encoded octets of unreserved characters.
     *
     * For consistency, percent-encoded octets in the ranges of ALPHA (%41–%5A and %61–%7A), DIGIT (%30–%39),
     * hyphen (%2D), period (%2E), underscore (%5F), or tilde (%7E) should not be created by URI producers and,
     * when found in a URI, should be decoded to their corresponding unreserved characters by URI normalizers.
     *
     * Example: http://example.org/%7Eusern%61me/ → http://example.org/~username/
     */
    const DECODE_UNRESERVED_CHARACTERS = 2;

    /**
     * Converts the empty path to "/" for http and https URIs.
     *
     * Example: http://example.org → http://example.org/
     */
    const CONVERT_EMPTY_PATH = 4;

    /**
     * Removes the default host of the given URI scheme from the URI.
     *
     * Only the "file" scheme defines the default host "localhost".
     * All of `file:/myfile`, `file:///myfile`, and `file://localhost/myfile`
     * are equivalent according to RFC 3986. The first format is not accepted
     * by PHPs stream functions and thus already normalized implicitly to the
     * second format in the Uri class. See `GuzzleHttp\Psr7\Uri::composeComponents`.
     *
     * Example: file://localhost/myfile → file:///myfile
     */
    const REMOVE_DEFAULT_HOST = 8;

    /**
     * Removes the default port of the given URI scheme from the URI.
     *
     * Example: http://example.org:80/ → http://example.org/
     */
    const REMOVE_DEFAULT_PORT = 16;

    /**
     * Removes unnecessary dot-segments.
     *
     * Dot-segments in relative-path references are not removed as it would
     * change the semantics of the URI reference.
     *
     * Example: http://example.org/../a/b/../c/./d.html → http://example.org/a/c/d.html
     */
    const REMOVE_DOT_SEGMENTS = 32;

    /**
     * Paths which include two or more adjacent slashes are converted to one.
     *
     * Webservers usually ignore duplicate slashes and treat those URIs equivalent.
     * But in theory those URIs do not need to be equivalent. So this normalization
     * may change the semantics. Encoded slashes (%2F) are not removed.
     *
     * Example: http://example.org//foo///bar.html → http://example.org/foo/bar.html
     */
    const REMOVE_DUPLICATE_SLASHES = 64;

    /**
     * Sort query parameters with their values in alphabetical order.
     *
     * However, the order of parameters in a URI may be significant (this is not defined by the standard).
     * So this normalization is not safe and may change the semantics of the URI.
     *
     * Example: ?lang=en&article=fred → ?article=fred&lang=en
     *
     * Note: The sorting is neither locale nor Unicode aware (the URI query does not get decoded at all) as the
     * purpose is to be able to compare URIs in a reproducible way, not to have the params sorted perfectly.
     */
    const SORT_QUERY_PARAMETERS = 128;

    /**
     * Returns a normalized URI.
     *
     * The scheme and host component are already normalized to lowercase per PSR-7 UriInterface.
     * This methods adds additional normalizations that can be configured with the $flags parameter.
     *
     * PSR-7 UriInterface cannot distinguish between an empty component and a missing component as
     * getQuery(), getFragment() etc. always return a string. This means the URIs "/?#" and "/" are
     * treated equivalent which is not necessarily true according to RFC 3986. But that difference
     * is highly uncommon in reality. So this potential normalization is implied in PSR-7 as well.
     *
     * @param UriInterface $uri   The URI to normalize
     * @param int          $flags A bitmask of normalizations to apply, see constants
     *
     * @return UriInterface The normalized URI
     *
     * @link https://tools.ietf.org/html/rfc3986#section-6.2
     */
    public static function normalize(UriInterface $uri, $flags = self::PRESERVING_NORMALIZATIONS)
    {
        if ($flags & self::CAPITALIZE_PERCENT_ENCODING) {
            $uri = self::capitalizePercentEncoding($uri);
        }

        if ($flags & self::DECODE_UNRESERVED_CHARACTERS) {
            $uri = self::decodeUnreservedCharacters($uri);
        }

        if ($flags & self::CONVERT_EMPTY_PATH && $uri->getPath() === '' &&
            ($uri->getScheme() === 'http' || $uri->getScheme() === 'https')
        ) {
            $uri = $uri->withPath('/');
        }

        if ($flags & self::REMOVE_DEFAULT_HOST && $uri->getScheme() === 'file' && $uri->getHost() === 'localhost') {
            $uri = $uri->withHost('');
        }

        if ($flags & self::REMOVE_DEFAULT_PORT && $uri->getPort() !== null && Uri::isDefaultPort($uri)) {
            $uri = $uri->withPort(null);
        }

        if ($flags & self::REMOVE_DOT_SEGMENTS && !Uri::isRelativePathReference($uri)) {
            $uri = $uri->withPath(UriResolver::removeDotSegments($uri->getPath()));
        }

        if ($flags & self::REMOVE_DUPLICATE_SLASHES) {
            $uri = $uri->withPath(preg_replace('#//++#', '/', $uri->getPath()));
        }

        if ($flags & self::SORT_QUERY_PARAMETERS && $uri->getQuery() !== '') {
            $queryKeyValues = explode('&', $uri->getQuery());
            sort($queryKeyValues);
            $uri = $uri->withQuery(implode('&', $queryKeyValues));
        }

        return $uri;
    }

    /**
     * Whether two URIs can be considered equivalent.
     *
     * Both URIs are normalized automatically before comparison with the given $normalizations bitmask. The method also
     * accepts relative URI references and returns true when they are equivalent. This of course assumes they will be
     * resolved against the same base URI. If this is not the case, determination of equivalence or difference of
     * relative references does not mean anything.
     *
     * @param UriInterface $uri1           An URI to compare
     * @param UriInterface $uri2           An URI to compare
     * @param int          $normalizations A bitmask of normalizations to apply, see constants
     *
     * @return bool
     *
     * @link https://tools.ietf.org/html/rfc3986#section-6.1
     */
    public static function isEquivalent(UriInterface $uri1, UriInterface $uri2, $normalizations = self::PRESERVING_NORMALIZATIONS)
    {
        return (string) self::normalize($uri1, $normalizations) === (string) self::normalize($uri2, $normalizations);
    }

    private static function capitalizePercentEncoding(UriInterface $uri)
    {
        $regex = '/(?:%[A-Fa-f0-9]{2})++/';

        $callback = function (array $match) {
            return strtoupper($match[0]);
        };

        return
            $uri->withPath(
                preg_replace_callback($regex, $callback, $uri->getPath())
            )->withQuery(
                preg_replace_callback($regex, $callback, $uri->getQuery())
            );
    }

    private static function decodeUnreservedCharacters(UriInterface $uri)
    {
        $regex = '/%(?:2D|2E|5F|7E|3[0-9]|[46][1-9A-F]|[57][0-9A])/i';

        $callback = function (array $match) {
            return rawurldecode($match[0]);
        };

        return
            $uri->withPath(
                preg_replace_callback($regex, $callback, $uri->getPath())
            )->withQuery(
                preg_replace_callback($regex, $callback, $uri->getQuery())
            );
    }

    private function __construct()
    {
        // cannot be instantiated
    }
}
vendor/guzzlehttp/psr7/src/NoSeekStream.php000064400000000705151327705700015025 0ustar00<?php

namespace WPvividGuzzleHttp\Psr7;

use WPvividPsr\Http\Message\StreamInterface;

/**
 * Stream decorator that prevents a stream from being seeked.
 *
 * @final
 */
class NoSeekStream implements StreamInterface
{
    use StreamDecoratorTrait;

    public function seek($offset, $whence = SEEK_SET)
    {
        throw new \RuntimeException('Cannot seek a NoSeekStream');
    }

    public function isSeekable()
    {
        return false;
    }
}
vendor/guzzlehttp/psr7/src/BufferStream.php000064400000006025151327705700015053 0ustar00<?php

namespace WPvividGuzzleHttp\Psr7;

use WPvividPsr\Http\Message\StreamInterface;

/**
 * Provides a buffer stream that can be written to to fill a buffer, and read
 * from to remove bytes from the buffer.
 *
 * This stream returns a "hwm" metadata value that tells upstream consumers
 * what the configured high water mark of the stream is, or the maximum
 * preferred size of the buffer.
 *
 * @final
 */
class BufferStream implements StreamInterface
{
    private $hwm;
    private $buffer = '';

    /**
     * @param int $hwm High water mark, representing the preferred maximum
     *                 buffer size. If the size of the buffer exceeds the high
     *                 water mark, then calls to write will continue to succeed
     *                 but will return false to inform writers to slow down
     *                 until the buffer has been drained by reading from it.
     */
    public function __construct($hwm = 16384)
    {
        $this->hwm = $hwm;
    }

    public function __toString()
    {
        return $this->getContents();
    }

    public function getContents()
    {
        $buffer = $this->buffer;
        $this->buffer = '';

        return $buffer;
    }

    public function close()
    {
        $this->buffer = '';
    }

    public function detach()
    {
        $this->close();

        return null;
    }

    public function getSize()
    {
        return strlen($this->buffer);
    }

    public function isReadable()
    {
        return true;
    }

    public function isWritable()
    {
        return true;
    }

    public function isSeekable()
    {
        return false;
    }

    public function rewind()
    {
        $this->seek(0);
    }

    public function seek($offset, $whence = SEEK_SET)
    {
        throw new \RuntimeException('Cannot seek a BufferStream');
    }

    public function eof()
    {
        return strlen($this->buffer) === 0;
    }

    public function tell()
    {
        throw new \RuntimeException('Cannot determine the position of a BufferStream');
    }

    /**
     * Reads data from the buffer.
     */
    public function read($length)
    {
        $currentLength = strlen($this->buffer);

        if ($length >= $currentLength) {
            // No need to slice the buffer because we don't have enough data.
            $result = $this->buffer;
            $this->buffer = '';
        } else {
            // Slice up the result to provide a subset of the buffer.
            $result = substr($this->buffer, 0, $length);
            $this->buffer = substr($this->buffer, $length);
        }

        return $result;
    }

    /**
     * Writes data to the buffer.
     */
    public function write($string)
    {
        $this->buffer .= $string;

        // TODO: What should happen here?
        if (strlen($this->buffer) >= $this->hwm) {
            return false;
        }

        return strlen($string);
    }

    public function getMetadata($key = null)
    {
        if ($key == 'hwm') {
            return $this->hwm;
        }

        return $key ? null : [];
    }
}
vendor/guzzlehttp/psr7/src/ServerRequest.php000064400000023237151327705700015311 0ustar00<?php

namespace WPvividGuzzleHttp\Psr7;

use InvalidArgumentException;
use WPvividPsr\Http\Message\ServerRequestInterface;
use WPvividPsr\Http\Message\StreamInterface;
use WPvividPsr\Http\Message\UploadedFileInterface;
use WPvividPsr\Http\Message\UriInterface;

/**
 * Server-side HTTP request
 *
 * Extends the Request definition to add methods for accessing incoming data,
 * specifically server parameters, cookies, matched path parameters, query
 * string arguments, body parameters, and upload file information.
 *
 * "Attributes" are discovered via decomposing the request (and usually
 * specifically the URI path), and typically will be injected by the application.
 *
 * Requests are considered immutable; all methods that might change state are
 * implemented such that they retain the internal state of the current
 * message and return a new instance that contains the changed state.
 */
class ServerRequest extends Request implements ServerRequestInterface
{
    /**
     * @var array
     */
    private $attributes = [];

    /**
     * @var array
     */
    private $cookieParams = [];

    /**
     * @var array|object|null
     */
    private $parsedBody;

    /**
     * @var array
     */
    private $queryParams = [];

    /**
     * @var array
     */
    private $serverParams;

    /**
     * @var array
     */
    private $uploadedFiles = [];

    /**
     * @param string                               $method       HTTP method
     * @param string|UriInterface                  $uri          URI
     * @param array                                $headers      Request headers
     * @param string|resource|StreamInterface|null $body         Request body
     * @param string                               $version      Protocol version
     * @param array                                $serverParams Typically the $_SERVER superglobal
     */
    public function __construct(
        $method,
        $uri,
        array $headers = [],
        $body = null,
        $version = '1.1',
        array $serverParams = []
    ) {
        $this->serverParams = $serverParams;

        parent::__construct($method, $uri, $headers, $body, $version);
    }

    /**
     * Return an UploadedFile instance array.
     *
     * @param array $files A array which respect $_FILES structure
     *
     * @return array
     *
     * @throws InvalidArgumentException for unrecognized values
     */
    public static function normalizeFiles(array $files)
    {
        $normalized = [];

        foreach ($files as $key => $value) {
            if ($value instanceof UploadedFileInterface) {
                $normalized[$key] = $value;
            } elseif (is_array($value) && isset($value['tmp_name'])) {
                $normalized[$key] = self::createUploadedFileFromSpec($value);
            } elseif (is_array($value)) {
                $normalized[$key] = self::normalizeFiles($value);
                continue;
            } else {
                throw new InvalidArgumentException('Invalid value in files specification');
            }
        }

        return $normalized;
    }

    /**
     * Create and return an UploadedFile instance from a $_FILES specification.
     *
     * If the specification represents an array of values, this method will
     * delegate to normalizeNestedFileSpec() and return that return value.
     *
     * @param array $value $_FILES struct
     *
     * @return array|UploadedFileInterface
     */
    private static function createUploadedFileFromSpec(array $value)
    {
        if (is_array($value['tmp_name'])) {
            return self::normalizeNestedFileSpec($value);
        }

        return new UploadedFile(
            $value['tmp_name'],
            (int) $value['size'],
            (int) $value['error'],
            $value['name'],
            $value['type']
        );
    }

    /**
     * Normalize an array of file specifications.
     *
     * Loops through all nested files and returns a normalized array of
     * UploadedFileInterface instances.
     *
     * @param array $files
     *
     * @return UploadedFileInterface[]
     */
    private static function normalizeNestedFileSpec(array $files = [])
    {
        $normalizedFiles = [];

        foreach (array_keys($files['tmp_name']) as $key) {
            $spec = [
                'tmp_name' => $files['tmp_name'][$key],
                'size'     => $files['size'][$key],
                'error'    => $files['error'][$key],
                'name'     => $files['name'][$key],
                'type'     => $files['type'][$key],
            ];
            $normalizedFiles[$key] = self::createUploadedFileFromSpec($spec);
        }

        return $normalizedFiles;
    }

    /**
     * Return a ServerRequest populated with superglobals:
     * $_GET
     * $_POST
     * $_COOKIE
     * $_FILES
     * $_SERVER
     *
     * @return ServerRequestInterface
     */
    public static function fromGlobals()
    {
        $method = isset($_SERVER['REQUEST_METHOD']) ? $_SERVER['REQUEST_METHOD'] : 'GET';
        $headers = getallheaders();
        $uri = self::getUriFromGlobals();
        $body = new CachingStream(new LazyOpenStream('php://input', 'r+'));
        $protocol = isset($_SERVER['SERVER_PROTOCOL']) ? str_replace('HTTP/', '', $_SERVER['SERVER_PROTOCOL']) : '1.1';

        $serverRequest = new ServerRequest($method, $uri, $headers, $body, $protocol, $_SERVER);

        return $serverRequest
            ->withCookieParams($_COOKIE)
            ->withQueryParams($_GET)
            ->withParsedBody($_POST)
            ->withUploadedFiles(self::normalizeFiles($_FILES));
    }

    private static function extractHostAndPortFromAuthority($authority)
    {
        $uri = 'http://' . $authority;
        $parts = parse_url($uri);
        if (false === $parts) {
            return [null, null];
        }

        $host = isset($parts['host']) ? $parts['host'] : null;
        $port = isset($parts['port']) ? $parts['port'] : null;

        return [$host, $port];
    }

    /**
     * Get a Uri populated with values from $_SERVER.
     *
     * @return UriInterface
     */
    public static function getUriFromGlobals()
    {
        $uri = new Uri('');

        $uri = $uri->withScheme(!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off' ? 'https' : 'http');

        $hasPort = false;
        if (isset($_SERVER['HTTP_HOST'])) {
            list($host, $port) = self::extractHostAndPortFromAuthority($_SERVER['HTTP_HOST']);
            if ($host !== null) {
                $uri = $uri->withHost($host);
            }

            if ($port !== null) {
                $hasPort = true;
                $uri = $uri->withPort($port);
            }
        } elseif (isset($_SERVER['SERVER_NAME'])) {
            $uri = $uri->withHost($_SERVER['SERVER_NAME']);
        } elseif (isset($_SERVER['SERVER_ADDR'])) {
            $uri = $uri->withHost($_SERVER['SERVER_ADDR']);
        }

        if (!$hasPort && isset($_SERVER['SERVER_PORT'])) {
            $uri = $uri->withPort($_SERVER['SERVER_PORT']);
        }

        $hasQuery = false;
        if (isset($_SERVER['REQUEST_URI'])) {
            $requestUriParts = explode('?', $_SERVER['REQUEST_URI'], 2);
            $uri = $uri->withPath($requestUriParts[0]);
            if (isset($requestUriParts[1])) {
                $hasQuery = true;
                $uri = $uri->withQuery($requestUriParts[1]);
            }
        }

        if (!$hasQuery && isset($_SERVER['QUERY_STRING'])) {
            $uri = $uri->withQuery($_SERVER['QUERY_STRING']);
        }

        return $uri;
    }

    /**
     * {@inheritdoc}
     */
    public function getServerParams()
    {
        return $this->serverParams;
    }

    /**
     * {@inheritdoc}
     */
    public function getUploadedFiles()
    {
        return $this->uploadedFiles;
    }

    /**
     * {@inheritdoc}
     */
    public function withUploadedFiles(array $uploadedFiles)
    {
        $new = clone $this;
        $new->uploadedFiles = $uploadedFiles;

        return $new;
    }

    /**
     * {@inheritdoc}
     */
    public function getCookieParams()
    {
        return $this->cookieParams;
    }

    /**
     * {@inheritdoc}
     */
    public function withCookieParams(array $cookies)
    {
        $new = clone $this;
        $new->cookieParams = $cookies;

        return $new;
    }

    /**
     * {@inheritdoc}
     */
    public function getQueryParams()
    {
        return $this->queryParams;
    }

    /**
     * {@inheritdoc}
     */
    public function withQueryParams(array $query)
    {
        $new = clone $this;
        $new->queryParams = $query;

        return $new;
    }

    /**
     * {@inheritdoc}
     */
    public function getParsedBody()
    {
        return $this->parsedBody;
    }

    /**
     * {@inheritdoc}
     */
    public function withParsedBody($data)
    {
        $new = clone $this;
        $new->parsedBody = $data;

        return $new;
    }

    /**
     * {@inheritdoc}
     */
    public function getAttributes()
    {
        return $this->attributes;
    }

    /**
     * {@inheritdoc}
     */
    public function getAttribute($attribute, $default = null)
    {
        if (false === array_key_exists($attribute, $this->attributes)) {
            return $default;
        }

        return $this->attributes[$attribute];
    }

    /**
     * {@inheritdoc}
     */
    public function withAttribute($attribute, $value)
    {
        $new = clone $this;
        $new->attributes[$attribute] = $value;

        return $new;
    }

    /**
     * {@inheritdoc}
     */
    public function withoutAttribute($attribute)
    {
        if (false === array_key_exists($attribute, $this->attributes)) {
            return $this;
        }

        $new = clone $this;
        unset($new->attributes[$attribute]);

        return $new;
    }
}
vendor/guzzlehttp/psr7/src/StreamWrapper.php000064400000007321151327705700015262 0ustar00<?php

namespace WPvividGuzzleHttp\Psr7;

use WPvividPsr\Http\Message\StreamInterface;

/**
 * Converts Guzzle streams into PHP stream resources.
 *
 * @final
 */
class StreamWrapper
{
    /** @var resource */
    public $context;

    /** @var StreamInterface */
    private $stream;

    /** @var string r, r+, or w */
    private $mode;

    /**
     * Returns a resource representing the stream.
     *
     * @param StreamInterface $stream The stream to get a resource for
     *
     * @return resource
     *
     * @throws \InvalidArgumentException if stream is not readable or writable
     */
    public static function getResource(StreamInterface $stream)
    {
        self::register();

        if ($stream->isReadable()) {
            $mode = $stream->isWritable() ? 'r+' : 'r';
        } elseif ($stream->isWritable()) {
            $mode = 'w';
        } else {
            throw new \InvalidArgumentException('The stream must be readable, '
                . 'writable, or both.');
        }

        return fopen('guzzle://stream', $mode, null, self::createStreamContext($stream));
    }

    /**
     * Creates a stream context that can be used to open a stream as a php stream resource.
     *
     * @param StreamInterface $stream
     *
     * @return resource
     */
    public static function createStreamContext(StreamInterface $stream)
    {
        return stream_context_create([
            'guzzle' => ['stream' => $stream]
        ]);
    }

    /**
     * Registers the stream wrapper if needed
     */
    public static function register()
    {
        if (!in_array('guzzle', stream_get_wrappers())) {
            stream_wrapper_register('guzzle', __CLASS__);
        }
    }

    public function stream_open($path, $mode, $options, &$opened_path)
    {
        $options = stream_context_get_options($this->context);

        if (!isset($options['guzzle']['stream'])) {
            return false;
        }

        $this->mode = $mode;
        $this->stream = $options['guzzle']['stream'];

        return true;
    }

    public function stream_read($count)
    {
        return $this->stream->read($count);
    }

    public function stream_write($data)
    {
        return (int) $this->stream->write($data);
    }

    public function stream_tell()
    {
        return $this->stream->tell();
    }

    public function stream_eof()
    {
        return $this->stream->eof();
    }

    public function stream_seek($offset, $whence)
    {
        $this->stream->seek($offset, $whence);

        return true;
    }

    public function stream_cast($cast_as)
    {
        $stream = clone($this->stream);

        return $stream->detach();
    }

    public function stream_stat()
    {
        static $modeMap = [
            'r'  => 33060,
            'rb' => 33060,
            'r+' => 33206,
            'w'  => 33188,
            'wb' => 33188
        ];

        return [
            'dev'     => 0,
            'ino'     => 0,
            'mode'    => $modeMap[$this->mode],
            'nlink'   => 0,
            'uid'     => 0,
            'gid'     => 0,
            'rdev'    => 0,
            'size'    => $this->stream->getSize() ?: 0,
            'atime'   => 0,
            'mtime'   => 0,
            'ctime'   => 0,
            'blksize' => 0,
            'blocks'  => 0
        ];
    }

    public function url_stat($path, $flags)
    {
        return [
            'dev'     => 0,
            'ino'     => 0,
            'mode'    => 0,
            'nlink'   => 0,
            'uid'     => 0,
            'gid'     => 0,
            'rdev'    => 0,
            'size'    => 0,
            'atime'   => 0,
            'mtime'   => 0,
            'ctime'   => 0,
            'blksize' => 0,
            'blocks'  => 0
        ];
    }
}
vendor/guzzlehttp/psr7/src/Header.php000064400000004211151327705700013651 0ustar00<?php

namespace WPvividGuzzleHttp\Psr7;

final class Header
{
    /**
     * Parse an array of header values containing ";" separated data into an
     * array of associative arrays representing the header key value pair data
     * of the header. When a parameter does not contain a value, but just
     * contains a key, this function will inject a key with a '' string value.
     *
     * @param string|array $header Header to parse into components.
     *
     * @return array Returns the parsed header values.
     */
    public static function parse($header)
    {
        static $trimmed = "\"'  \n\t\r";
        $params = $matches = [];

        foreach (self::normalize($header) as $val) {
            $part = [];
            foreach (preg_split('/;(?=([^"]*"[^"]*")*[^"]*$)/', $val) as $kvp) {
                if (preg_match_all('/<[^>]+>|[^=]+/', $kvp, $matches)) {
                    $m = $matches[0];
                    if (isset($m[1])) {
                        $part[trim($m[0], $trimmed)] = trim($m[1], $trimmed);
                    } else {
                        $part[] = trim($m[0], $trimmed);
                    }
                }
            }
            if ($part) {
                $params[] = $part;
            }
        }

        return $params;
    }

    /**
     * Converts an array of header values that may contain comma separated
     * headers into an array of headers with no comma separated values.
     *
     * @param string|array $header Header to normalize.
     *
     * @return array Returns the normalized header field values.
     */
    public static function normalize($header)
    {
        if (!is_array($header)) {
            return array_map('trim', explode(',', $header));
        }

        $result = [];
        foreach ($header as $value) {
            foreach ((array) $value as $v) {
                if (strpos($v, ',') === false) {
                    $result[] = $v;
                    continue;
                }
                foreach (preg_split('/,(?=([^"]*"[^"]*")*[^"]*$)/', $v) as $vv) {
                    $result[] = trim($vv);
                }
            }
        }

        return $result;
    }
}
vendor/guzzlehttp/psr7/src/AppendStream.php000064400000013226151327705700015052 0ustar00<?php

namespace WPvividGuzzleHttp\Psr7;

use WPvividPsr\Http\Message\StreamInterface;

/**
 * Reads from multiple streams, one after the other.
 *
 * This is a read-only stream decorator.
 *
 * @final
 */
class AppendStream implements StreamInterface
{
    /** @var StreamInterface[] Streams being decorated */
    private $streams = [];

    private $seekable = true;
    private $current = 0;
    private $pos = 0;

    /**
     * @param StreamInterface[] $streams Streams to decorate. Each stream must
     *                                   be readable.
     */
    public function __construct(array $streams = [])
    {
        foreach ($streams as $stream) {
            $this->addStream($stream);
        }
    }

    public function __toString()
    {
        try {
            $this->rewind();
            return $this->getContents();
        } catch (\Exception $e) {
            return '';
        }
    }

    /**
     * Add a stream to the AppendStream
     *
     * @param StreamInterface $stream Stream to append. Must be readable.
     *
     * @throws \InvalidArgumentException if the stream is not readable
     */
    public function addStream(StreamInterface $stream)
    {
        if (!$stream->isReadable()) {
            throw new \InvalidArgumentException('Each stream must be readable');
        }

        // The stream is only seekable if all streams are seekable
        if (!$stream->isSeekable()) {
            $this->seekable = false;
        }

        $this->streams[] = $stream;
    }

    public function getContents()
    {
        return Utils::copyToString($this);
    }

    /**
     * Closes each attached stream.
     *
     * {@inheritdoc}
     */
    public function close()
    {
        $this->pos = $this->current = 0;
        $this->seekable = true;

        foreach ($this->streams as $stream) {
            $stream->close();
        }

        $this->streams = [];
    }

    /**
     * Detaches each attached stream.
     *
     * Returns null as it's not clear which underlying stream resource to return.
     *
     * {@inheritdoc}
     */
    public function detach()
    {
        $this->pos = $this->current = 0;
        $this->seekable = true;

        foreach ($this->streams as $stream) {
            $stream->detach();
        }

        $this->streams = [];

        return null;
    }

    public function tell()
    {
        return $this->pos;
    }

    /**
     * Tries to calculate the size by adding the size of each stream.
     *
     * If any of the streams do not return a valid number, then the size of the
     * append stream cannot be determined and null is returned.
     *
     * {@inheritdoc}
     */
    public function getSize()
    {
        $size = 0;

        foreach ($this->streams as $stream) {
            $s = $stream->getSize();
            if ($s === null) {
                return null;
            }
            $size += $s;
        }

        return $size;
    }

    public function eof()
    {
        return !$this->streams ||
            ($this->current >= count($this->streams) - 1 &&
             $this->streams[$this->current]->eof());
    }

    public function rewind()
    {
        $this->seek(0);
    }

    /**
     * Attempts to seek to the given position. Only supports SEEK_SET.
     *
     * {@inheritdoc}
     */
    public function seek($offset, $whence = SEEK_SET)
    {
        if (!$this->seekable) {
            throw new \RuntimeException('This AppendStream is not seekable');
        } elseif ($whence !== SEEK_SET) {
            throw new \RuntimeException('The AppendStream can only seek with SEEK_SET');
        }

        $this->pos = $this->current = 0;

        // Rewind each stream
        foreach ($this->streams as $i => $stream) {
            try {
                $stream->rewind();
            } catch (\Exception $e) {
                throw new \RuntimeException('Unable to seek stream '
                    . $i . ' of the AppendStream', 0, $e);
            }
        }

        // Seek to the actual position by reading from each stream
        while ($this->pos < $offset && !$this->eof()) {
            $result = $this->read(min(8096, $offset - $this->pos));
            if ($result === '') {
                break;
            }
        }
    }

    /**
     * Reads from all of the appended streams until the length is met or EOF.
     *
     * {@inheritdoc}
     */
    public function read($length)
    {
        $buffer = '';
        $total = count($this->streams) - 1;
        $remaining = $length;
        $progressToNext = false;

        while ($remaining > 0) {

            // Progress to the next stream if needed.
            if ($progressToNext || $this->streams[$this->current]->eof()) {
                $progressToNext = false;
                if ($this->current === $total) {
                    break;
                }
                $this->current++;
            }

            $result = $this->streams[$this->current]->read($remaining);

            // Using a loose comparison here to match on '', false, and null
            if ($result == null) {
                $progressToNext = true;
                continue;
            }

            $buffer .= $result;
            $remaining = $length - strlen($buffer);
        }

        $this->pos += strlen($buffer);

        return $buffer;
    }

    public function isReadable()
    {
        return true;
    }

    public function isWritable()
    {
        return false;
    }

    public function isSeekable()
    {
        return $this->seekable;
    }

    public function write($string)
    {
        throw new \RuntimeException('Cannot write to an AppendStream');
    }

    public function getMetadata($key = null)
    {
        return $key ? null : [];
    }
}
vendor/guzzlehttp/psr7/src/Utils.php000064400000034564151327705700013577 0ustar00<?php

namespace WPvividGuzzleHttp\Psr7;

use WPvividPsr\Http\Message\RequestInterface;
use WPvividPsr\Http\Message\ServerRequestInterface;
use WPvividPsr\Http\Message\StreamInterface;
use WPvividPsr\Http\Message\UriInterface;

final class Utils
{
    /**
     * Remove the items given by the keys, case insensitively from the data.
     *
     * @param iterable<string> $keys
     *
     * @return array
     */
    public static function caselessRemove($keys, array $data)
    {
        $result = [];

        foreach ($keys as &$key) {
            $key = strtolower($key);
        }

        foreach ($data as $k => $v) {
            if (!in_array(strtolower($k), $keys)) {
                $result[$k] = $v;
            }
        }

        return $result;
    }

    /**
     * Copy the contents of a stream into another stream until the given number
     * of bytes have been read.
     *
     * @param StreamInterface $source Stream to read from
     * @param StreamInterface $dest   Stream to write to
     * @param int             $maxLen Maximum number of bytes to read. Pass -1
     *                                to read the entire stream.
     *
     * @throws \RuntimeException on error.
     */
    public static function copyToStream(StreamInterface $source, StreamInterface $dest, $maxLen = -1)
    {
        $bufferSize = 8192;

        if ($maxLen === -1) {
            while (!$source->eof()) {
                if (!$dest->write($source->read($bufferSize))) {
                    break;
                }
            }
        } else {
            $remaining = $maxLen;
            while ($remaining > 0 && !$source->eof()) {
                $buf = $source->read(min($bufferSize, $remaining));
                $len = strlen($buf);
                if (!$len) {
                    break;
                }
                $remaining -= $len;
                $dest->write($buf);
            }
        }
    }

    /**
     * Copy the contents of a stream into a string until the given number of
     * bytes have been read.
     *
     * @param StreamInterface $stream Stream to read
     * @param int             $maxLen Maximum number of bytes to read. Pass -1
     *                                to read the entire stream.
     *
     * @return string
     *
     * @throws \RuntimeException on error.
     */
    public static function copyToString(StreamInterface $stream, $maxLen = -1)
    {
        $buffer = '';

        if ($maxLen === -1) {
            while (!$stream->eof()) {
                $buf = $stream->read(1048576);
                // Using a loose equality here to match on '' and false.
                if ($buf == null) {
                    break;
                }
                $buffer .= $buf;
            }
            return $buffer;
        }

        $len = 0;
        while (!$stream->eof() && $len < $maxLen) {
            $buf = $stream->read($maxLen - $len);
            // Using a loose equality here to match on '' and false.
            if ($buf == null) {
                break;
            }
            $buffer .= $buf;
            $len = strlen($buffer);
        }

        return $buffer;
    }

    /**
     * Calculate a hash of a stream.
     *
     * This method reads the entire stream to calculate a rolling hash, based
     * on PHP's `hash_init` functions.
     *
     * @param StreamInterface $stream    Stream to calculate the hash for
     * @param string          $algo      Hash algorithm (e.g. md5, crc32, etc)
     * @param bool            $rawOutput Whether or not to use raw output
     *
     * @return string Returns the hash of the stream
     *
     * @throws \RuntimeException on error.
     */
    public static function hash(StreamInterface $stream, $algo, $rawOutput = false)
    {
        $pos = $stream->tell();

        if ($pos > 0) {
            $stream->rewind();
        }

        $ctx = hash_init($algo);
        while (!$stream->eof()) {
            hash_update($ctx, $stream->read(1048576));
        }

        $out = hash_final($ctx, (bool) $rawOutput);
        $stream->seek($pos);

        return $out;
    }

    /**
     * Clone and modify a request with the given changes.
     *
     * This method is useful for reducing the number of clones needed to mutate
     * a message.
     *
     * The changes can be one of:
     * - method: (string) Changes the HTTP method.
     * - set_headers: (array) Sets the given headers.
     * - remove_headers: (array) Remove the given headers.
     * - body: (mixed) Sets the given body.
     * - uri: (UriInterface) Set the URI.
     * - query: (string) Set the query string value of the URI.
     * - version: (string) Set the protocol version.
     *
     * @param RequestInterface $request Request to clone and modify.
     * @param array            $changes Changes to apply.
     *
     * @return RequestInterface
     */
    public static function modifyRequest(RequestInterface $request, array $changes)
    {
        if (!$changes) {
            return $request;
        }

        $headers = $request->getHeaders();

        if (!isset($changes['uri'])) {
            $uri = $request->getUri();
        } else {
            // Remove the host header if one is on the URI
            if ($host = $changes['uri']->getHost()) {
                $changes['set_headers']['Host'] = $host;

                if ($port = $changes['uri']->getPort()) {
                    $standardPorts = ['http' => 80, 'https' => 443];
                    $scheme = $changes['uri']->getScheme();
                    if (isset($standardPorts[$scheme]) && $port != $standardPorts[$scheme]) {
                        $changes['set_headers']['Host'] .= ':' . $port;
                    }
                }
            }
            $uri = $changes['uri'];
        }

        if (!empty($changes['remove_headers'])) {
            $headers = self::caselessRemove($changes['remove_headers'], $headers);
        }

        if (!empty($changes['set_headers'])) {
            $headers = self::caselessRemove(array_keys($changes['set_headers']), $headers);
            $headers = $changes['set_headers'] + $headers;
        }

        if (isset($changes['query'])) {
            $uri = $uri->withQuery($changes['query']);
        }

        if ($request instanceof ServerRequestInterface) {
            $new = (new ServerRequest(
                isset($changes['method']) ? $changes['method'] : $request->getMethod(),
                $uri,
                $headers,
                isset($changes['body']) ? $changes['body'] : $request->getBody(),
                isset($changes['version'])
                    ? $changes['version']
                    : $request->getProtocolVersion(),
                $request->getServerParams()
            ))
            ->withParsedBody($request->getParsedBody())
            ->withQueryParams($request->getQueryParams())
            ->withCookieParams($request->getCookieParams())
            ->withUploadedFiles($request->getUploadedFiles());

            foreach ($request->getAttributes() as $key => $value) {
                $new = $new->withAttribute($key, $value);
            }

            return $new;
        }

        return new Request(
            isset($changes['method']) ? $changes['method'] : $request->getMethod(),
            $uri,
            $headers,
            isset($changes['body']) ? $changes['body'] : $request->getBody(),
            isset($changes['version'])
                ? $changes['version']
                : $request->getProtocolVersion()
        );
    }

    /**
     * Read a line from the stream up to the maximum allowed buffer length.
     *
     * @param StreamInterface $stream    Stream to read from
     * @param int|null        $maxLength Maximum buffer length
     *
     * @return string
     */
    public static function readLine(StreamInterface $stream, $maxLength = null)
    {
        $buffer = '';
        $size = 0;

        while (!$stream->eof()) {
            // Using a loose equality here to match on '' and false.
            if (null == ($byte = $stream->read(1))) {
                return $buffer;
            }
            $buffer .= $byte;
            // Break when a new line is found or the max length - 1 is reached
            if ($byte === "\n" || ++$size === $maxLength - 1) {
                break;
            }
        }

        return $buffer;
    }

    /**
     * Create a new stream based on the input type.
     *
     * Options is an associative array that can contain the following keys:
     * - metadata: Array of custom metadata.
     * - size: Size of the stream.
     *
     * This method accepts the following `$resource` types:
     * - `Psr\Http\Message\StreamInterface`: Returns the value as-is.
     * - `string`: Creates a stream object that uses the given string as the contents.
     * - `resource`: Creates a stream object that wraps the given PHP stream resource.
     * - `Iterator`: If the provided value implements `Iterator`, then a read-only
     *   stream object will be created that wraps the given iterable. Each time the
     *   stream is read from, data from the iterator will fill a buffer and will be
     *   continuously called until the buffer is equal to the requested read size.
     *   Subsequent read calls will first read from the buffer and then call `next`
     *   on the underlying iterator until it is exhausted.
     * - `object` with `__toString()`: If the object has the `__toString()` method,
     *   the object will be cast to a string and then a stream will be returned that
     *   uses the string value.
     * - `NULL`: When `null` is passed, an empty stream object is returned.
     * - `callable` When a callable is passed, a read-only stream object will be
     *   created that invokes the given callable. The callable is invoked with the
     *   number of suggested bytes to read. The callable can return any number of
     *   bytes, but MUST return `false` when there is no more data to return. The
     *   stream object that wraps the callable will invoke the callable until the
     *   number of requested bytes are available. Any additional bytes will be
     *   buffered and used in subsequent reads.
     *
     * @param resource|string|int|float|bool|StreamInterface|callable|\Iterator|null $resource Entity body data
     * @param array                                                                  $options  Additional options
     *
     * @return StreamInterface
     *
     * @throws \InvalidArgumentException if the $resource arg is not valid.
     */
    public static function streamFor($resource = '', array $options = [])
    {
        if (is_scalar($resource)) {
            $stream = self::tryFopen('php://temp', 'r+');
            if ($resource !== '') {
                fwrite($stream, $resource);
                fseek($stream, 0);
            }
            return new Stream($stream, $options);
        }

        switch (gettype($resource)) {
            case 'resource':
                /*
                 * The 'php://input' is a special stream with quirks and inconsistencies.
                 * We avoid using that stream by reading it into php://temp
                 */
                $metaData = \stream_get_meta_data($resource);
                if (isset($metaData['uri']) && $metaData['uri'] === 'php://input') {
                    $stream = self::tryFopen('php://temp', 'w+');
                    fwrite($stream, stream_get_contents($resource));
                    fseek($stream, 0);
                    $resource = $stream;
                }
                return new Stream($resource, $options);
            case 'object':
                if ($resource instanceof StreamInterface) {
                    return $resource;
                } elseif ($resource instanceof \Iterator) {
                    return new PumpStream(function () use ($resource) {
                        if (!$resource->valid()) {
                            return false;
                        }
                        $result = $resource->current();
                        $resource->next();
                        return $result;
                    }, $options);
                } elseif (method_exists($resource, '__toString')) {
                    return Utils::streamFor((string) $resource, $options);
                }
                break;
            case 'NULL':
                return new Stream(self::tryFopen('php://temp', 'r+'), $options);
        }

        if (is_callable($resource)) {
            return new PumpStream($resource, $options);
        }

        throw new \InvalidArgumentException('Invalid resource type: ' . gettype($resource));
    }

    /**
     * Safely opens a PHP stream resource using a filename.
     *
     * When fopen fails, PHP normally raises a warning. This function adds an
     * error handler that checks for errors and throws an exception instead.
     *
     * @param string $filename File to open
     * @param string $mode     Mode used to open the file
     *
     * @return resource
     *
     * @throws \RuntimeException if the file cannot be opened
     */
    public static function tryFopen($filename, $mode)
    {
        $ex = null;
        set_error_handler(function () use ($filename, $mode, &$ex) {
            $ex = new \RuntimeException(sprintf(
                'Unable to open "%s" using mode "%s": %s',
                $filename,
                $mode,
                func_get_args()[1]
            ));

            return true;
        });

        try {
            $handle = fopen($filename, $mode);
        } catch (\Throwable $e) {
            $ex = new \RuntimeException(sprintf(
                'Unable to open "%s" using mode "%s": %s',
                $filename,
                $mode,
                $e->getMessage()
            ), 0, $e);
        }

        restore_error_handler();

        if ($ex) {
            /** @var $ex \RuntimeException */
            throw $ex;
        }

        return $handle;
    }

    /**
     * Returns a UriInterface for the given value.
     *
     * This function accepts a string or UriInterface and returns a
     * UriInterface for the given value. If the value is already a
     * UriInterface, it is returned as-is.
     *
     * @param string|UriInterface $uri
     *
     * @return UriInterface
     *
     * @throws \InvalidArgumentException
     */
    public static function uriFor($uri)
    {
        if ($uri instanceof UriInterface) {
            return $uri;
        }

        if (is_string($uri)) {
            return new Uri($uri);
        }

        throw new \InvalidArgumentException('URI must be a string or UriInterface');
    }
}
vendor/guzzlehttp/psr7/src/UriResolver.php000064400000021143151327705700014745 0ustar00<?php

namespace WPvividGuzzleHttp\Psr7;

use WPvividPsr\Http\Message\UriInterface;

/**
 * Resolves a URI reference in the context of a base URI and the opposite way.
 *
 * @author Tobias Schultze
 *
 * @link https://tools.ietf.org/html/rfc3986#section-5
 */
final class UriResolver
{
    /**
     * Removes dot segments from a path and returns the new path.
     *
     * @param string $path
     *
     * @return string
     *
     * @link http://tools.ietf.org/html/rfc3986#section-5.2.4
     */
    public static function removeDotSegments($path)
    {
        if ($path === '' || $path === '/') {
            return $path;
        }

        $results = [];
        $segments = explode('/', $path);
        foreach ($segments as $segment) {
            if ($segment === '..') {
                array_pop($results);
            } elseif ($segment !== '.') {
                $results[] = $segment;
            }
        }

        $newPath = implode('/', $results);

        if ($path[0] === '/' && (!isset($newPath[0]) || $newPath[0] !== '/')) {
            // Re-add the leading slash if necessary for cases like "/.."
            $newPath = '/' . $newPath;
        } elseif ($newPath !== '' && ($segment === '.' || $segment === '..')) {
            // Add the trailing slash if necessary
            // If newPath is not empty, then $segment must be set and is the last segment from the foreach
            $newPath .= '/';
        }

        return $newPath;
    }

    /**
     * Converts the relative URI into a new URI that is resolved against the base URI.
     *
     * @param UriInterface $base Base URI
     * @param UriInterface $rel  Relative URI
     *
     * @return UriInterface
     *
     * @link http://tools.ietf.org/html/rfc3986#section-5.2
     */
    public static function resolve(UriInterface $base, UriInterface $rel)
    {
        if ((string) $rel === '') {
            // we can simply return the same base URI instance for this same-document reference
            return $base;
        }

        if ($rel->getScheme() != '') {
            return $rel->withPath(self::removeDotSegments($rel->getPath()));
        }

        if ($rel->getAuthority() != '') {
            $targetAuthority = $rel->getAuthority();
            $targetPath = self::removeDotSegments($rel->getPath());
            $targetQuery = $rel->getQuery();
        } else {
            $targetAuthority = $base->getAuthority();
            if ($rel->getPath() === '') {
                $targetPath = $base->getPath();
                $targetQuery = $rel->getQuery() != '' ? $rel->getQuery() : $base->getQuery();
            } else {
                if ($rel->getPath()[0] === '/') {
                    $targetPath = $rel->getPath();
                } else {
                    if ($targetAuthority != '' && $base->getPath() === '') {
                        $targetPath = '/' . $rel->getPath();
                    } else {
                        $lastSlashPos = strrpos($base->getPath(), '/');
                        if ($lastSlashPos === false) {
                            $targetPath = $rel->getPath();
                        } else {
                            $targetPath = substr($base->getPath(), 0, $lastSlashPos + 1) . $rel->getPath();
                        }
                    }
                }
                $targetPath = self::removeDotSegments($targetPath);
                $targetQuery = $rel->getQuery();
            }
        }

        return new Uri(Uri::composeComponents(
            $base->getScheme(),
            $targetAuthority,
            $targetPath,
            $targetQuery,
            $rel->getFragment()
        ));
    }

    /**
     * Returns the target URI as a relative reference from the base URI.
     *
     * This method is the counterpart to resolve():
     *
     *    (string) $target === (string) UriResolver::resolve($base, UriResolver::relativize($base, $target))
     *
     * One use-case is to use the current request URI as base URI and then generate relative links in your documents
     * to reduce the document size or offer self-contained downloadable document archives.
     *
     *    $base = new Uri('http://example.com/a/b/');
     *    echo UriResolver::relativize($base, new Uri('http://example.com/a/b/c'));  // prints 'c'.
     *    echo UriResolver::relativize($base, new Uri('http://example.com/a/x/y'));  // prints '../x/y'.
     *    echo UriResolver::relativize($base, new Uri('http://example.com/a/b/?q')); // prints '?q'.
     *    echo UriResolver::relativize($base, new Uri('http://example.org/a/b/'));   // prints '//example.org/a/b/'.
     *
     * This method also accepts a target that is already relative and will try to relativize it further. Only a
     * relative-path reference will be returned as-is.
     *
     *    echo UriResolver::relativize($base, new Uri('/a/b/c'));  // prints 'c' as well
     *
     * @param UriInterface $base   Base URI
     * @param UriInterface $target Target URI
     *
     * @return UriInterface The relative URI reference
     */
    public static function relativize(UriInterface $base, UriInterface $target)
    {
        if ($target->getScheme() !== '' &&
            ($base->getScheme() !== $target->getScheme() || $target->getAuthority() === '' && $base->getAuthority() !== '')
        ) {
            return $target;
        }

        if (Uri::isRelativePathReference($target)) {
            // As the target is already highly relative we return it as-is. It would be possible to resolve
            // the target with `$target = self::resolve($base, $target);` and then try make it more relative
            // by removing a duplicate query. But let's not do that automatically.
            return $target;
        }

        if ($target->getAuthority() !== '' && $base->getAuthority() !== $target->getAuthority()) {
            return $target->withScheme('');
        }

        // We must remove the path before removing the authority because if the path starts with two slashes, the URI
        // would turn invalid. And we also cannot set a relative path before removing the authority, as that is also
        // invalid.
        $emptyPathUri = $target->withScheme('')->withPath('')->withUserInfo('')->withPort(null)->withHost('');

        if ($base->getPath() !== $target->getPath()) {
            return $emptyPathUri->withPath(self::getRelativePath($base, $target));
        }

        if ($base->getQuery() === $target->getQuery()) {
            // Only the target fragment is left. And it must be returned even if base and target fragment are the same.
            return $emptyPathUri->withQuery('');
        }

        // If the base URI has a query but the target has none, we cannot return an empty path reference as it would
        // inherit the base query component when resolving.
        if ($target->getQuery() === '') {
            $segments = explode('/', $target->getPath());
            $lastSegment = end($segments);

            return $emptyPathUri->withPath($lastSegment === '' ? './' : $lastSegment);
        }

        return $emptyPathUri;
    }

    private static function getRelativePath(UriInterface $base, UriInterface $target)
    {
        $sourceSegments = explode('/', $base->getPath());
        $targetSegments = explode('/', $target->getPath());
        array_pop($sourceSegments);
        $targetLastSegment = array_pop($targetSegments);
        foreach ($sourceSegments as $i => $segment) {
            if (isset($targetSegments[$i]) && $segment === $targetSegments[$i]) {
                unset($sourceSegments[$i], $targetSegments[$i]);
            } else {
                break;
            }
        }
        $targetSegments[] = $targetLastSegment;
        $relativePath = str_repeat('../', count($sourceSegments)) . implode('/', $targetSegments);

        // A reference to am empty last segment or an empty first sub-segment must be prefixed with "./".
        // This also applies to a segment with a colon character (e.g., "file:colon") that cannot be used
        // as the first segment of a relative-path reference, as it would be mistaken for a scheme name.
        if ('' === $relativePath || false !== strpos(explode('/', $relativePath, 2)[0], ':')) {
            $relativePath = "./$relativePath";
        } elseif ('/' === $relativePath[0]) {
            if ($base->getAuthority() != '' && $base->getPath() === '') {
                // In this case an extra slash is added by resolve() automatically. So we must not add one here.
                $relativePath = ".$relativePath";
            } else {
                $relativePath = "./$relativePath";
            }
        }

        return $relativePath;
    }

    private function __construct()
    {
        // cannot be instantiated
    }
}
vendor/guzzlehttp/psr7/src/DroppingStream.php000064400000002124151327705700015420 0ustar00<?php

namespace WPvividGuzzleHttp\Psr7;

use WPvividPsr\Http\Message\StreamInterface;

/**
 * Stream decorator that begins dropping data once the size of the underlying
 * stream becomes too full.
 *
 * @final
 */
class DroppingStream implements StreamInterface
{
    use StreamDecoratorTrait;

    private $maxLength;

    /**
     * @param StreamInterface $stream    Underlying stream to decorate.
     * @param int             $maxLength Maximum size before dropping data.
     */
    public function __construct(StreamInterface $stream, $maxLength)
    {
        $this->stream = $stream;
        $this->maxLength = $maxLength;
    }

    public function write($string)
    {
        $diff = $this->maxLength - $this->stream->getSize();

        // Begin returning 0 when the underlying stream is too large.
        if ($diff <= 0) {
            return 0;
        }

        // Write the stream or a subset of the stream if needed.
        if (strlen($string) < $diff) {
            return $this->stream->write($string);
        }

        return $this->stream->write(substr($string, 0, $diff));
    }
}
vendor/guzzlehttp/psr7/src/MimeType.php000064400000011774151327705700014226 0ustar00<?php

namespace WPvividGuzzleHttp\Psr7;

final class MimeType
{
    /**
     * Determines the mimetype of a file by looking at its extension.
     *
     * @param string $filename
     *
     * @return string|null
     */
    public static function fromFilename($filename)
    {
        return self::fromExtension(pathinfo($filename, PATHINFO_EXTENSION));
    }

    /**
     * Maps a file extensions to a mimetype.
     *
     * @param string $extension string The file extension.
     *
     * @return string|null
     *
     * @link http://svn.apache.org/repos/asf/httpd/httpd/branches/1.3.x/conf/mime.types
     */
    public static function fromExtension($extension)
    {
        static $mimetypes = [
            '3gp' => 'video/3gpp',
            '7z' => 'application/x-7z-compressed',
            'aac' => 'audio/x-aac',
            'ai' => 'application/postscript',
            'aif' => 'audio/x-aiff',
            'asc' => 'text/plain',
            'asf' => 'video/x-ms-asf',
            'atom' => 'application/atom+xml',
            'avi' => 'video/x-msvideo',
            'bmp' => 'image/bmp',
            'bz2' => 'application/x-bzip2',
            'cer' => 'application/pkix-cert',
            'crl' => 'application/pkix-crl',
            'crt' => 'application/x-x509-ca-cert',
            'css' => 'text/css',
            'csv' => 'text/csv',
            'cu' => 'application/cu-seeme',
            'deb' => 'application/x-debian-package',
            'doc' => 'application/msword',
            'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
            'dvi' => 'application/x-dvi',
            'eot' => 'application/vnd.ms-fontobject',
            'eps' => 'application/postscript',
            'epub' => 'application/epub+zip',
            'etx' => 'text/x-setext',
            'flac' => 'audio/flac',
            'flv' => 'video/x-flv',
            'gif' => 'image/gif',
            'gz' => 'application/gzip',
            'htm' => 'text/html',
            'html' => 'text/html',
            'ico' => 'image/x-icon',
            'ics' => 'text/calendar',
            'ini' => 'text/plain',
            'iso' => 'application/x-iso9660-image',
            'jar' => 'application/java-archive',
            'jpe' => 'image/jpeg',
            'jpeg' => 'image/jpeg',
            'jpg' => 'image/jpeg',
            'js' => 'text/javascript',
            'json' => 'application/json',
            'latex' => 'application/x-latex',
            'log' => 'text/plain',
            'm4a' => 'audio/mp4',
            'm4v' => 'video/mp4',
            'mid' => 'audio/midi',
            'midi' => 'audio/midi',
            'mov' => 'video/quicktime',
            'mkv' => 'video/x-matroska',
            'mp3' => 'audio/mpeg',
            'mp4' => 'video/mp4',
            'mp4a' => 'audio/mp4',
            'mp4v' => 'video/mp4',
            'mpe' => 'video/mpeg',
            'mpeg' => 'video/mpeg',
            'mpg' => 'video/mpeg',
            'mpg4' => 'video/mp4',
            'oga' => 'audio/ogg',
            'ogg' => 'audio/ogg',
            'ogv' => 'video/ogg',
            'ogx' => 'application/ogg',
            'pbm' => 'image/x-portable-bitmap',
            'pdf' => 'application/pdf',
            'pgm' => 'image/x-portable-graymap',
            'png' => 'image/png',
            'pnm' => 'image/x-portable-anymap',
            'ppm' => 'image/x-portable-pixmap',
            'ppt' => 'application/vnd.ms-powerpoint',
            'pptx' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
            'ps' => 'application/postscript',
            'qt' => 'video/quicktime',
            'rar' => 'application/x-rar-compressed',
            'ras' => 'image/x-cmu-raster',
            'rss' => 'application/rss+xml',
            'rtf' => 'application/rtf',
            'sgm' => 'text/sgml',
            'sgml' => 'text/sgml',
            'svg' => 'image/svg+xml',
            'swf' => 'application/x-shockwave-flash',
            'tar' => 'application/x-tar',
            'tif' => 'image/tiff',
            'tiff' => 'image/tiff',
            'torrent' => 'application/x-bittorrent',
            'ttf' => 'application/x-font-ttf',
            'txt' => 'text/plain',
            'wav' => 'audio/x-wav',
            'webm' => 'video/webm',
            'webp' => 'image/webp',
            'wma' => 'audio/x-ms-wma',
            'wmv' => 'video/x-ms-wmv',
            'woff' => 'application/x-font-woff',
            'wsdl' => 'application/wsdl+xml',
            'xbm' => 'image/x-xbitmap',
            'xls' => 'application/vnd.ms-excel',
            'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
            'xml' => 'application/xml',
            'xpm' => 'image/x-xpixmap',
            'xwd' => 'image/x-xwindowdump',
            'yaml' => 'text/yaml',
            'yml' => 'text/yaml',
            'zip' => 'application/zip',
        ];

        $extension = strtolower($extension);

        return isset($mimetypes[$extension])
            ? $mimetypes[$extension]
            : null;
    }
}
vendor/guzzlehttp/psr7/src/CachingStream.php000064400000010560151327705700015175 0ustar00<?php

namespace WPvividGuzzleHttp\Psr7;

use WPvividPsr\Http\Message\StreamInterface;

/**
 * Stream decorator that can cache previously read bytes from a sequentially
 * read stream.
 *
 * @final
 */
class CachingStream implements StreamInterface
{
    use StreamDecoratorTrait;

    /** @var StreamInterface Stream being wrapped */
    private $remoteStream;

    /** @var int Number of bytes to skip reading due to a write on the buffer */
    private $skipReadBytes = 0;

    /**
     * We will treat the buffer object as the body of the stream
     *
     * @param StreamInterface $stream Stream to cache. The cursor is assumed to be at the beginning of the stream.
     * @param StreamInterface $target Optionally specify where data is cached
     */
    public function __construct(
        StreamInterface $stream,
        StreamInterface $target = null
    ) {
        $this->remoteStream = $stream;
        $this->stream = $target ?: new Stream(Utils::tryFopen('php://temp', 'r+'));
    }

    public function getSize()
    {
        $remoteSize = $this->remoteStream->getSize();

        if (null === $remoteSize) {
            return null;
        }

        return max($this->stream->getSize(), $remoteSize);
    }

    public function rewind()
    {
        $this->seek(0);
    }

    public function seek($offset, $whence = SEEK_SET)
    {
        if ($whence == SEEK_SET) {
            $byte = $offset;
        } elseif ($whence == SEEK_CUR) {
            $byte = $offset + $this->tell();
        } elseif ($whence == SEEK_END) {
            $size = $this->remoteStream->getSize();
            if ($size === null) {
                $size = $this->cacheEntireStream();
            }
            $byte = $size + $offset;
        } else {
            throw new \InvalidArgumentException('Invalid whence');
        }

        $diff = $byte - $this->stream->getSize();

        if ($diff > 0) {
            // Read the remoteStream until we have read in at least the amount
            // of bytes requested, or we reach the end of the file.
            while ($diff > 0 && !$this->remoteStream->eof()) {
                $this->read($diff);
                $diff = $byte - $this->stream->getSize();
            }
        } else {
            // We can just do a normal seek since we've already seen this byte.
            $this->stream->seek($byte);
        }
    }

    public function read($length)
    {
        // Perform a regular read on any previously read data from the buffer
        $data = $this->stream->read($length);
        $remaining = $length - strlen($data);

        // More data was requested so read from the remote stream
        if ($remaining) {
            // If data was written to the buffer in a position that would have
            // been filled from the remote stream, then we must skip bytes on
            // the remote stream to emulate overwriting bytes from that
            // position. This mimics the behavior of other PHP stream wrappers.
            $remoteData = $this->remoteStream->read(
                $remaining + $this->skipReadBytes
            );

            if ($this->skipReadBytes) {
                $len = strlen($remoteData);
                $remoteData = substr($remoteData, $this->skipReadBytes);
                $this->skipReadBytes = max(0, $this->skipReadBytes - $len);
            }

            $data .= $remoteData;
            $this->stream->write($remoteData);
        }

        return $data;
    }

    public function write($string)
    {
        // When appending to the end of the currently read stream, you'll want
        // to skip bytes from being read from the remote stream to emulate
        // other stream wrappers. Basically replacing bytes of data of a fixed
        // length.
        $overflow = (strlen($string) + $this->tell()) - $this->remoteStream->tell();
        if ($overflow > 0) {
            $this->skipReadBytes += $overflow;
        }

        return $this->stream->write($string);
    }

    public function eof()
    {
        return $this->stream->eof() && $this->remoteStream->eof();
    }

    /**
     * Close both the remote stream and buffer stream
     */
    public function close()
    {
        $this->remoteStream->close() && $this->stream->close();
    }

    private function cacheEntireStream()
    {
        $target = new FnStream(['write' => 'strlen']);
        Utils::copyToStream($this, $target);

        return $this->tell();
    }
}
vendor/guzzlehttp/psr7/src/Stream.php000064400000015250151327705700013721 0ustar00<?php

namespace WPvividGuzzleHttp\Psr7;

use WPvividPsr\Http\Message\StreamInterface;

/**
 * PHP stream implementation.
 *
 * @var $stream
 */
class Stream implements StreamInterface
{
    /**
     * Resource modes.
     *
     * @var string
     *
     * @see http://php.net/manual/function.fopen.php
     * @see http://php.net/manual/en/function.gzopen.php
     */
    const READABLE_MODES = '/r|a\+|ab\+|w\+|wb\+|x\+|xb\+|c\+|cb\+/';
    const WRITABLE_MODES = '/a|w|r\+|rb\+|rw|x|c/';

    private $stream;
    private $size;
    private $seekable;
    private $readable;
    private $writable;
    private $uri;
    private $customMetadata;

    /**
     * This constructor accepts an associative array of options.
     *
     * - size: (int) If a read stream would otherwise have an indeterminate
     *   size, but the size is known due to foreknowledge, then you can
     *   provide that size, in bytes.
     * - metadata: (array) Any additional metadata to return when the metadata
     *   of the stream is accessed.
     *
     * @param resource $stream  Stream resource to wrap.
     * @param array    $options Associative array of options.
     *
     * @throws \InvalidArgumentException if the stream is not a stream resource
     */
    public function __construct($stream, $options = [])
    {
        if (!is_resource($stream)) {
            throw new \InvalidArgumentException('Stream must be a resource');
        }

        if (isset($options['size'])) {
            $this->size = $options['size'];
        }

        $this->customMetadata = isset($options['metadata'])
            ? $options['metadata']
            : [];

        $this->stream = $stream;
        $meta = stream_get_meta_data($this->stream);
        $this->seekable = $meta['seekable'];
        $this->readable = (bool)preg_match(self::READABLE_MODES, $meta['mode']);
        $this->writable = (bool)preg_match(self::WRITABLE_MODES, $meta['mode']);
        $this->uri = $this->getMetadata('uri');
    }

    /**
     * Closes the stream when the destructed
     */
    public function __destruct()
    {
        $this->close();
    }

    public function __toString()
    {
        try {
            if ($this->isSeekable()) {
                $this->seek(0);
            }
            return $this->getContents();
        } catch (\Exception $e) {
            return '';
        }
    }

    public function getContents()
    {
        if (!isset($this->stream)) {
            throw new \RuntimeException('Stream is detached');
        }

        $contents = stream_get_contents($this->stream);

        if ($contents === false) {
            throw new \RuntimeException('Unable to read stream contents');
        }

        return $contents;
    }

    public function close()
    {
        if (isset($this->stream)) {
            if (is_resource($this->stream)) {
                fclose($this->stream);
            }
            $this->detach();
        }
    }

    public function detach()
    {
        if (!isset($this->stream)) {
            return null;
        }

        $result = $this->stream;
        unset($this->stream);
        $this->size = $this->uri = null;
        $this->readable = $this->writable = $this->seekable = false;

        return $result;
    }

    public function getSize()
    {
        if ($this->size !== null) {
            return $this->size;
        }

        if (!isset($this->stream)) {
            return null;
        }

        // Clear the stat cache if the stream has a URI
        if ($this->uri) {
            clearstatcache(true, $this->uri);
        }

        $stats = fstat($this->stream);
        if (isset($stats['size'])) {
            $this->size = $stats['size'];
            return $this->size;
        }

        return null;
    }

    public function isReadable()
    {
        return $this->readable;
    }

    public function isWritable()
    {
        return $this->writable;
    }

    public function isSeekable()
    {
        return $this->seekable;
    }

    public function eof()
    {
        if (!isset($this->stream)) {
            throw new \RuntimeException('Stream is detached');
        }

        return feof($this->stream);
    }

    public function tell()
    {
        if (!isset($this->stream)) {
            throw new \RuntimeException('Stream is detached');
        }

        $result = ftell($this->stream);

        if ($result === false) {
            throw new \RuntimeException('Unable to determine stream position');
        }

        return $result;
    }

    public function rewind()
    {
        $this->seek(0);
    }

    public function seek($offset, $whence = SEEK_SET)
    {
        $whence = (int) $whence;

        if (!isset($this->stream)) {
            throw new \RuntimeException('Stream is detached');
        }
        if (!$this->seekable) {
            throw new \RuntimeException('Stream is not seekable');
        }
        if (fseek($this->stream, $offset, $whence) === -1) {
            throw new \RuntimeException('Unable to seek to stream position '
                . $offset . ' with whence ' . var_export($whence, true));
        }
    }

    public function read($length)
    {
        if (!isset($this->stream)) {
            throw new \RuntimeException('Stream is detached');
        }
        if (!$this->readable) {
            throw new \RuntimeException('Cannot read from non-readable stream');
        }
        if ($length < 0) {
            throw new \RuntimeException('Length parameter cannot be negative');
        }

        if (0 === $length) {
            return '';
        }

        $string = fread($this->stream, $length);
        if (false === $string) {
            throw new \RuntimeException('Unable to read from stream');
        }

        return $string;
    }

    public function write($string)
    {
        if (!isset($this->stream)) {
            throw new \RuntimeException('Stream is detached');
        }
        if (!$this->writable) {
            throw new \RuntimeException('Cannot write to a non-writable stream');
        }

        // We can't know the size after writing anything
        $this->size = null;
        $result = fwrite($this->stream, $string);

        if ($result === false) {
            throw new \RuntimeException('Unable to write to stream');
        }

        return $result;
    }

    public function getMetadata($key = null)
    {
        if (!isset($this->stream)) {
            return $key ? null : [];
        } elseif (!$key) {
            return $this->customMetadata + stream_get_meta_data($this->stream);
        } elseif (isset($this->customMetadata[$key])) {
            return $this->customMetadata[$key];
        }

        $meta = stream_get_meta_data($this->stream);

        return isset($meta[$key]) ? $meta[$key] : null;
    }
}
vendor/guzzlehttp/psr7/src/LazyOpenStream.php000064400000001622151327705700015401 0ustar00<?php

namespace WPvividGuzzleHttp\Psr7;

use WPvividPsr\Http\Message\StreamInterface;

/**
 * Lazily reads or writes to a file that is opened only after an IO operation
 * take place on the stream.
 *
 * @final
 */
class LazyOpenStream implements StreamInterface
{
    use StreamDecoratorTrait;

    /** @var string File to open */
    private $filename;

    /** @var string */
    private $mode;

    /**
     * @param string $filename File to lazily open
     * @param string $mode     fopen mode to use when opening the stream
     */
    public function __construct($filename, $mode)
    {
        $this->filename = $filename;
        $this->mode = $mode;
    }

    /**
     * Creates the underlying stream lazily when required.
     *
     * @return StreamInterface
     */
    protected function createStream()
    {
        return Utils::streamFor(Utils::tryFopen($this->filename, $this->mode));
    }
}
vendor/guzzlehttp/psr7/src/Rfc7230.php000064400000001272151327705700013513 0ustar00<?php

namespace WPvividGuzzleHttp\Psr7;

final class Rfc7230
{
    /**
     * Header related regular expressions (copied from amphp/http package)
     * (Note: once we require PHP 7.x we could just depend on the upstream package)
     *
     * Note: header delimiter (\r\n) is modified to \r?\n to accept line feed only delimiters for BC reasons.
     *
     * @link    https://github.com/amphp/http/blob/v1.0.1/src/Rfc7230.php#L12-L15
     *
     * @license https://github.com/amphp/http/blob/v1.0.1/LICENSE
     */
    const HEADER_REGEX = "(^([^()<>@,;:\\\"/[\]?={}\x01-\x20\x7F]++):[ \t]*+((?:[ \t]*+[\x21-\x7E\x80-\xFF]++)*+)[ \t]*+\r?\n)m";
    const HEADER_FOLD_REGEX = "(\r?\n[ \t]++)";
}
vendor/guzzlehttp/psr7/src/Uri.php000064400000054600151327705700013227 0ustar00<?php

namespace WPvividGuzzleHttp\Psr7;

use WPvividPsr\Http\Message\UriInterface;

/**
 * PSR-7 URI implementation.
 *
 * @author Michael Dowling
 * @author Tobias Schultze
 * @author Matthew Weier O'Phinney
 */
class Uri implements UriInterface
{
    /**
     * Absolute http and https URIs require a host per RFC 7230 Section 2.7
     * but in generic URIs the host can be empty. So for http(s) URIs
     * we apply this default host when no host is given yet to form a
     * valid URI.
     */
    const HTTP_DEFAULT_HOST = 'localhost';

    private static $defaultPorts = [
        'http'  => 80,
        'https' => 443,
        'ftp' => 21,
        'gopher' => 70,
        'nntp' => 119,
        'news' => 119,
        'telnet' => 23,
        'tn3270' => 23,
        'imap' => 143,
        'pop' => 110,
        'ldap' => 389,
    ];

    private static $charUnreserved = 'a-zA-Z0-9_\-\.~';
    private static $charSubDelims = '!\$&\'\(\)\*\+,;=';
    private static $replaceQuery = ['=' => '%3D', '&' => '%26'];

    /** @var string Uri scheme. */
    private $scheme = '';

    /** @var string Uri user info. */
    private $userInfo = '';

    /** @var string Uri host. */
    private $host = '';

    /** @var int|null Uri port. */
    private $port;

    /** @var string Uri path. */
    private $path = '';

    /** @var string Uri query string. */
    private $query = '';

    /** @var string Uri fragment. */
    private $fragment = '';

    /**
     * @param string $uri URI to parse
     */
    public function __construct($uri = '')
    {
        // weak type check to also accept null until we can add scalar type hints
        if ($uri != '') {
            $parts = self::parse($uri);
            if ($parts === false) {
                throw new \InvalidArgumentException("Unable to parse URI: $uri");
            }
            $this->applyParts($parts);
        }
    }

    /**
     * UTF-8 aware \parse_url() replacement.
     *
     * The internal function produces broken output for non ASCII domain names
     * (IDN) when used with locales other than "C".
     *
     * On the other hand, cURL understands IDN correctly only when UTF-8 locale
     * is configured ("C.UTF-8", "en_US.UTF-8", etc.).
     *
     * @see https://bugs.php.net/bug.php?id=52923
     * @see https://www.php.net/manual/en/function.parse-url.php#114817
     * @see https://curl.haxx.se/libcurl/c/CURLOPT_URL.html#ENCODING
     *
     * @param string $url
     *
     * @return array|false
     */
    private static function parse($url)
    {
        // If IPv6
        $prefix = '';
        if (preg_match('%^(.*://\[[0-9:a-f]+\])(.*?)$%', $url, $matches)) {
            $prefix = $matches[1];
            $url = $matches[2];
        }

        $encodedUrl = preg_replace_callback(
            '%[^:/@?&=#]+%usD',
            static function ($matches) {
                return urlencode($matches[0]);
            },
            $url
        );

        $result = parse_url($prefix . $encodedUrl);

        if ($result === false) {
            return false;
        }

        return array_map('urldecode', $result);
    }

    public function __toString()
    {
        return self::composeComponents(
            $this->scheme,
            $this->getAuthority(),
            $this->path,
            $this->query,
            $this->fragment
        );
    }

    /**
     * Composes a URI reference string from its various components.
     *
     * Usually this method does not need to be called manually but instead is used indirectly via
     * `Psr\Http\Message\UriInterface::__toString`.
     *
     * PSR-7 UriInterface treats an empty component the same as a missing component as
     * getQuery(), getFragment() etc. always return a string. This explains the slight
     * difference to RFC 3986 Section 5.3.
     *
     * Another adjustment is that the authority separator is added even when the authority is missing/empty
     * for the "file" scheme. This is because PHP stream functions like `file_get_contents` only work with
     * `file:///myfile` but not with `file:/myfile` although they are equivalent according to RFC 3986. But
     * `file:///` is the more common syntax for the file scheme anyway (Chrome for example redirects to
     * that format).
     *
     * @param string $scheme
     * @param string $authority
     * @param string $path
     * @param string $query
     * @param string $fragment
     *
     * @return string
     *
     * @link https://tools.ietf.org/html/rfc3986#section-5.3
     */
    public static function composeComponents($scheme, $authority, $path, $query, $fragment)
    {
        $uri = '';

        // weak type checks to also accept null until we can add scalar type hints
        if ($scheme != '') {
            $uri .= $scheme . ':';
        }

        if ($authority != ''|| $scheme === 'file') {
            $uri .= '//' . $authority;
        }

        $uri .= $path;

        if ($query != '') {
            $uri .= '?' . $query;
        }

        if ($fragment != '') {
            $uri .= '#' . $fragment;
        }

        return $uri;
    }

    /**
     * Whether the URI has the default port of the current scheme.
     *
     * `Psr\Http\Message\UriInterface::getPort` may return null or the standard port. This method can be used
     * independently of the implementation.
     *
     * @param UriInterface $uri
     *
     * @return bool
     */
    public static function isDefaultPort(UriInterface $uri)
    {
        return $uri->getPort() === null
            || (isset(self::$defaultPorts[$uri->getScheme()]) && $uri->getPort() === self::$defaultPorts[$uri->getScheme()]);
    }

    /**
     * Whether the URI is absolute, i.e. it has a scheme.
     *
     * An instance of UriInterface can either be an absolute URI or a relative reference. This method returns true
     * if it is the former. An absolute URI has a scheme. A relative reference is used to express a URI relative
     * to another URI, the base URI. Relative references can be divided into several forms:
     * - network-path references, e.g. '//example.com/path'
     * - absolute-path references, e.g. '/path'
     * - relative-path references, e.g. 'subpath'
     *
     * @param UriInterface $uri
     *
     * @return bool
     *
     * @see Uri::isNetworkPathReference
     * @see Uri::isAbsolutePathReference
     * @see Uri::isRelativePathReference
     * @link https://tools.ietf.org/html/rfc3986#section-4
     */
    public static function isAbsolute(UriInterface $uri)
    {
        return $uri->getScheme() !== '';
    }

    /**
     * Whether the URI is a network-path reference.
     *
     * A relative reference that begins with two slash characters is termed an network-path reference.
     *
     * @param UriInterface $uri
     *
     * @return bool
     *
     * @link https://tools.ietf.org/html/rfc3986#section-4.2
     */
    public static function isNetworkPathReference(UriInterface $uri)
    {
        return $uri->getScheme() === '' && $uri->getAuthority() !== '';
    }

    /**
     * Whether the URI is a absolute-path reference.
     *
     * A relative reference that begins with a single slash character is termed an absolute-path reference.
     *
     * @param UriInterface $uri
     *
     * @return bool
     *
     * @link https://tools.ietf.org/html/rfc3986#section-4.2
     */
    public static function isAbsolutePathReference(UriInterface $uri)
    {
        return $uri->getScheme() === ''
            && $uri->getAuthority() === ''
            && isset($uri->getPath()[0])
            && $uri->getPath()[0] === '/';
    }

    /**
     * Whether the URI is a relative-path reference.
     *
     * A relative reference that does not begin with a slash character is termed a relative-path reference.
     *
     * @param UriInterface $uri
     *
     * @return bool
     *
     * @link https://tools.ietf.org/html/rfc3986#section-4.2
     */
    public static function isRelativePathReference(UriInterface $uri)
    {
        return $uri->getScheme() === ''
            && $uri->getAuthority() === ''
            && (!isset($uri->getPath()[0]) || $uri->getPath()[0] !== '/');
    }

    /**
     * Whether the URI is a same-document reference.
     *
     * A same-document reference refers to a URI that is, aside from its fragment
     * component, identical to the base URI. When no base URI is given, only an empty
     * URI reference (apart from its fragment) is considered a same-document reference.
     *
     * @param UriInterface      $uri  The URI to check
     * @param UriInterface|null $base An optional base URI to compare against
     *
     * @return bool
     *
     * @link https://tools.ietf.org/html/rfc3986#section-4.4
     */
    public static function isSameDocumentReference(UriInterface $uri, UriInterface $base = null)
    {
        if ($base !== null) {
            $uri = UriResolver::resolve($base, $uri);

            return ($uri->getScheme() === $base->getScheme())
                && ($uri->getAuthority() === $base->getAuthority())
                && ($uri->getPath() === $base->getPath())
                && ($uri->getQuery() === $base->getQuery());
        }

        return $uri->getScheme() === '' && $uri->getAuthority() === '' && $uri->getPath() === '' && $uri->getQuery() === '';
    }

    /**
     * Removes dot segments from a path and returns the new path.
     *
     * @param string $path
     *
     * @return string
     *
     * @deprecated since version 1.4. Use UriResolver::removeDotSegments instead.
     * @see UriResolver::removeDotSegments
     */
    public static function removeDotSegments($path)
    {
        return UriResolver::removeDotSegments($path);
    }

    /**
     * Converts the relative URI into a new URI that is resolved against the base URI.
     *
     * @param UriInterface        $base Base URI
     * @param string|UriInterface $rel  Relative URI
     *
     * @return UriInterface
     *
     * @deprecated since version 1.4. Use UriResolver::resolve instead.
     * @see UriResolver::resolve
     */
    public static function resolve(UriInterface $base, $rel)
    {
        if (!($rel instanceof UriInterface)) {
            $rel = new self($rel);
        }

        return UriResolver::resolve($base, $rel);
    }

    /**
     * Creates a new URI with a specific query string value removed.
     *
     * Any existing query string values that exactly match the provided key are
     * removed.
     *
     * @param UriInterface $uri URI to use as a base.
     * @param string       $key Query string key to remove.
     *
     * @return UriInterface
     */
    public static function withoutQueryValue(UriInterface $uri, $key)
    {
        $result = self::getFilteredQueryString($uri, [$key]);

        return $uri->withQuery(implode('&', $result));
    }

    /**
     * Creates a new URI with a specific query string value.
     *
     * Any existing query string values that exactly match the provided key are
     * removed and replaced with the given key value pair.
     *
     * A value of null will set the query string key without a value, e.g. "key"
     * instead of "key=value".
     *
     * @param UriInterface $uri   URI to use as a base.
     * @param string       $key   Key to set.
     * @param string|null  $value Value to set
     *
     * @return UriInterface
     */
    public static function withQueryValue(UriInterface $uri, $key, $value)
    {
        $result = self::getFilteredQueryString($uri, [$key]);

        $result[] = self::generateQueryString($key, $value);

        return $uri->withQuery(implode('&', $result));
    }

    /**
     * Creates a new URI with multiple specific query string values.
     *
     * It has the same behavior as withQueryValue() but for an associative array of key => value.
     *
     * @param UriInterface $uri           URI to use as a base.
     * @param array        $keyValueArray Associative array of key and values
     *
     * @return UriInterface
     */
    public static function withQueryValues(UriInterface $uri, array $keyValueArray)
    {
        $result = self::getFilteredQueryString($uri, array_keys($keyValueArray));

        foreach ($keyValueArray as $key => $value) {
            $result[] = self::generateQueryString($key, $value);
        }

        return $uri->withQuery(implode('&', $result));
    }

    /**
     * Creates a URI from a hash of `parse_url` components.
     *
     * @param array $parts
     *
     * @return UriInterface
     *
     * @link http://php.net/manual/en/function.parse-url.php
     *
     * @throws \InvalidArgumentException If the components do not form a valid URI.
     */
    public static function fromParts(array $parts)
    {
        $uri = new self();
        $uri->applyParts($parts);
        $uri->validateState();

        return $uri;
    }

    public function getScheme()
    {
        return $this->scheme;
    }

    public function getAuthority()
    {
        $authority = $this->host;
        if ($this->userInfo !== '') {
            $authority = $this->userInfo . '@' . $authority;
        }

        if ($this->port !== null) {
            $authority .= ':' . $this->port;
        }

        return $authority;
    }

    public function getUserInfo()
    {
        return $this->userInfo;
    }

    public function getHost()
    {
        return $this->host;
    }

    public function getPort()
    {
        return $this->port;
    }

    public function getPath()
    {
        return $this->path;
    }

    public function getQuery()
    {
        return $this->query;
    }

    public function getFragment()
    {
        return $this->fragment;
    }

    public function withScheme($scheme)
    {
        $scheme = $this->filterScheme($scheme);

        if ($this->scheme === $scheme) {
            return $this;
        }

        $new = clone $this;
        $new->scheme = $scheme;
        $new->removeDefaultPort();
        $new->validateState();

        return $new;
    }

    public function withUserInfo($user, $password = null)
    {
        $info = $this->filterUserInfoComponent($user);
        if ($password !== null) {
            $info .= ':' . $this->filterUserInfoComponent($password);
        }

        if ($this->userInfo === $info) {
            return $this;
        }

        $new = clone $this;
        $new->userInfo = $info;
        $new->validateState();

        return $new;
    }

    public function withHost($host)
    {
        $host = $this->filterHost($host);

        if ($this->host === $host) {
            return $this;
        }

        $new = clone $this;
        $new->host = $host;
        $new->validateState();

        return $new;
    }

    public function withPort($port)
    {
        $port = $this->filterPort($port);

        if ($this->port === $port) {
            return $this;
        }

        $new = clone $this;
        $new->port = $port;
        $new->removeDefaultPort();
        $new->validateState();

        return $new;
    }

    public function withPath($path)
    {
        $path = $this->filterPath($path);

        if ($this->path === $path) {
            return $this;
        }

        $new = clone $this;
        $new->path = $path;
        $new->validateState();

        return $new;
    }

    public function withQuery($query)
    {
        $query = $this->filterQueryAndFragment($query);

        if ($this->query === $query) {
            return $this;
        }

        $new = clone $this;
        $new->query = $query;

        return $new;
    }

    public function withFragment($fragment)
    {
        $fragment = $this->filterQueryAndFragment($fragment);

        if ($this->fragment === $fragment) {
            return $this;
        }

        $new = clone $this;
        $new->fragment = $fragment;

        return $new;
    }

    /**
     * Apply parse_url parts to a URI.
     *
     * @param array $parts Array of parse_url parts to apply.
     */
    private function applyParts(array $parts)
    {
        $this->scheme = isset($parts['scheme'])
            ? $this->filterScheme($parts['scheme'])
            : '';
        $this->userInfo = isset($parts['user'])
            ? $this->filterUserInfoComponent($parts['user'])
            : '';
        $this->host = isset($parts['host'])
            ? $this->filterHost($parts['host'])
            : '';
        $this->port = isset($parts['port'])
            ? $this->filterPort($parts['port'])
            : null;
        $this->path = isset($parts['path'])
            ? $this->filterPath($parts['path'])
            : '';
        $this->query = isset($parts['query'])
            ? $this->filterQueryAndFragment($parts['query'])
            : '';
        $this->fragment = isset($parts['fragment'])
            ? $this->filterQueryAndFragment($parts['fragment'])
            : '';
        if (isset($parts['pass'])) {
            $this->userInfo .= ':' . $this->filterUserInfoComponent($parts['pass']);
        }

        $this->removeDefaultPort();
    }

    /**
     * @param string $scheme
     *
     * @return string
     *
     * @throws \InvalidArgumentException If the scheme is invalid.
     */
    private function filterScheme($scheme)
    {
        if (!is_string($scheme)) {
            throw new \InvalidArgumentException('Scheme must be a string');
        }

        return \strtr($scheme, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz');
    }

    /**
     * @param string $component
     *
     * @return string
     *
     * @throws \InvalidArgumentException If the user info is invalid.
     */
    private function filterUserInfoComponent($component)
    {
        if (!is_string($component)) {
            throw new \InvalidArgumentException('User info must be a string');
        }

        return preg_replace_callback(
            '/(?:[^%' . self::$charUnreserved . self::$charSubDelims . ']+|%(?![A-Fa-f0-9]{2}))/',
            [$this, 'rawurlencodeMatchZero'],
            $component
        );
    }

    /**
     * @param string $host
     *
     * @return string
     *
     * @throws \InvalidArgumentException If the host is invalid.
     */
    private function filterHost($host)
    {
        if (!is_string($host)) {
            throw new \InvalidArgumentException('Host must be a string');
        }

        return \strtr($host, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz');
    }

    /**
     * @param int|null $port
     *
     * @return int|null
     *
     * @throws \InvalidArgumentException If the port is invalid.
     */
    private function filterPort($port)
    {
        if ($port === null) {
            return null;
        }

        $port = (int) $port;
        if (0 > $port || 0xffff < $port) {
            throw new \InvalidArgumentException(
                sprintf('Invalid port: %d. Must be between 0 and 65535', $port)
            );
        }

        return $port;
    }

    /**
     * @param UriInterface $uri
     * @param array        $keys
     *
     * @return array
     */
    private static function getFilteredQueryString(UriInterface $uri, array $keys)
    {
        $current = $uri->getQuery();

        if ($current === '') {
            return [];
        }

        $decodedKeys = array_map('rawurldecode', $keys);

        return array_filter(explode('&', $current), function ($part) use ($decodedKeys) {
            return !in_array(rawurldecode(explode('=', $part)[0]), $decodedKeys, true);
        });
    }

    /**
     * @param string      $key
     * @param string|null $value
     *
     * @return string
     */
    private static function generateQueryString($key, $value)
    {
        // Query string separators ("=", "&") within the key or value need to be encoded
        // (while preventing double-encoding) before setting the query string. All other
        // chars that need percent-encoding will be encoded by withQuery().
        $queryString = strtr($key, self::$replaceQuery);

        if ($value !== null) {
            $queryString .= '=' . strtr($value, self::$replaceQuery);
        }

        return $queryString;
    }

    private function removeDefaultPort()
    {
        if ($this->port !== null && self::isDefaultPort($this)) {
            $this->port = null;
        }
    }

    /**
     * Filters the path of a URI
     *
     * @param string $path
     *
     * @return string
     *
     * @throws \InvalidArgumentException If the path is invalid.
     */
    private function filterPath($path)
    {
        if (!is_string($path)) {
            throw new \InvalidArgumentException('Path must be a string');
        }

        return preg_replace_callback(
            '/(?:[^' . self::$charUnreserved . self::$charSubDelims . '%:@\/]++|%(?![A-Fa-f0-9]{2}))/',
            [$this, 'rawurlencodeMatchZero'],
            $path
        );
    }

    /**
     * Filters the query string or fragment of a URI.
     *
     * @param string $str
     *
     * @return string
     *
     * @throws \InvalidArgumentException If the query or fragment is invalid.
     */
    private function filterQueryAndFragment($str)
    {
        if (!is_string($str)) {
            throw new \InvalidArgumentException('Query and fragment must be a string');
        }

        return preg_replace_callback(
            '/(?:[^' . self::$charUnreserved . self::$charSubDelims . '%:@\/\?]++|%(?![A-Fa-f0-9]{2}))/',
            [$this, 'rawurlencodeMatchZero'],
            $str
        );
    }

    private function rawurlencodeMatchZero(array $match)
    {
        return rawurlencode($match[0]);
    }

    private function validateState()
    {
        if ($this->host === '' && ($this->scheme === 'http' || $this->scheme === 'https')) {
            $this->host = self::HTTP_DEFAULT_HOST;
        }

        if ($this->getAuthority() === '') {
            if (0 === strpos($this->path, '//')) {
                throw new \InvalidArgumentException('The path of a URI without an authority must not start with two slashes "//"');
            }
            if ($this->scheme === '' && false !== strpos(explode('/', $this->path, 2)[0], ':')) {
                throw new \InvalidArgumentException('A relative URI must not have a path beginning with a segment containing a colon');
            }
        } elseif (isset($this->path[0]) && $this->path[0] !== '/') {
            @trigger_error(
                'The path of a URI with an authority must start with a slash "/" or be empty. Automagically fixing the URI ' .
                'by adding a leading slash to the path is deprecated since version 1.4 and will throw an exception instead.',
                E_USER_DEPRECATED
            );
            $this->path = '/' . $this->path;
            //throw new \InvalidArgumentException('The path of a URI with an authority must start with a slash "/" or be empty');
        }
    }
}
vendor/guzzlehttp/psr7/src/MessageTrait.php000064400000017366151327705700015070 0ustar00<?php

namespace WPvividGuzzleHttp\Psr7;

use WPvividPsr\Http\Message\StreamInterface;

/**
 * Trait implementing functionality common to requests and responses.
 */
trait MessageTrait
{
    /** @var array Map of all registered headers, as original name => array of values */
    private $headers = [];

    /** @var array Map of lowercase header name => original name at registration */
    private $headerNames  = [];

    /** @var string */
    private $protocol = '1.1';

    /** @var StreamInterface|null */
    private $stream;

    public function getProtocolVersion()
    {
        return $this->protocol;
    }

    public function withProtocolVersion($version)
    {
        if ($this->protocol === $version) {
            return $this;
        }

        $new = clone $this;
        $new->protocol = $version;
        return $new;
    }

    public function getHeaders()
    {
        return $this->headers;
    }

    public function hasHeader($header)
    {
        return isset($this->headerNames[strtolower($header)]);
    }

    public function getHeader($header)
    {
        $header = strtolower($header);

        if (!isset($this->headerNames[$header])) {
            return [];
        }

        $header = $this->headerNames[$header];

        return $this->headers[$header];
    }

    public function getHeaderLine($header)
    {
        return implode(', ', $this->getHeader($header));
    }

    public function withHeader($header, $value)
    {
        $this->assertHeader($header);
        $value = $this->normalizeHeaderValue($value);
        $normalized = strtolower($header);

        $new = clone $this;
        if (isset($new->headerNames[$normalized])) {
            unset($new->headers[$new->headerNames[$normalized]]);
        }
        $new->headerNames[$normalized] = $header;
        $new->headers[$header] = $value;

        return $new;
    }

    public function withAddedHeader($header, $value)
    {
        $this->assertHeader($header);
        $value = $this->normalizeHeaderValue($value);
        $normalized = strtolower($header);

        $new = clone $this;
        if (isset($new->headerNames[$normalized])) {
            $header = $this->headerNames[$normalized];
            $new->headers[$header] = array_merge($this->headers[$header], $value);
        } else {
            $new->headerNames[$normalized] = $header;
            $new->headers[$header] = $value;
        }

        return $new;
    }

    public function withoutHeader($header)
    {
        $normalized = strtolower($header);

        if (!isset($this->headerNames[$normalized])) {
            return $this;
        }

        $header = $this->headerNames[$normalized];

        $new = clone $this;
        unset($new->headers[$header], $new->headerNames[$normalized]);

        return $new;
    }

    public function getBody()
    {
        if (!$this->stream) {
            $this->stream = Utils::streamFor('');
        }

        return $this->stream;
    }

    public function withBody(StreamInterface $body)
    {
        if ($body === $this->stream) {
            return $this;
        }

        $new = clone $this;
        $new->stream = $body;
        return $new;
    }

    private function setHeaders(array $headers)
    {
        $this->headerNames = $this->headers = [];
        foreach ($headers as $header => $value) {
            if (is_int($header)) {
                // Numeric array keys are converted to int by PHP but having a header name '123' is not forbidden by the spec
                // and also allowed in withHeader(). So we need to cast it to string again for the following assertion to pass.
                $header = (string) $header;
            }
            $this->assertHeader($header);
            $value = $this->normalizeHeaderValue($value);
            $normalized = strtolower($header);
            if (isset($this->headerNames[$normalized])) {
                $header = $this->headerNames[$normalized];
                $this->headers[$header] = array_merge($this->headers[$header], $value);
            } else {
                $this->headerNames[$normalized] = $header;
                $this->headers[$header] = $value;
            }
        }
    }

    /**
     * @param mixed $value
     *
     * @return string[]
     */
    private function normalizeHeaderValue($value)
    {
        if (!is_array($value)) {
            return $this->trimAndValidateHeaderValues([$value]);
        }

        if (count($value) === 0) {
            throw new \InvalidArgumentException('Header value can not be an empty array.');
        }

        return $this->trimAndValidateHeaderValues($value);
    }

    /**
     * Trims whitespace from the header values.
     *
     * Spaces and tabs ought to be excluded by parsers when extracting the field value from a header field.
     *
     * header-field = field-name ":" OWS field-value OWS
     * OWS          = *( SP / HTAB )
     *
     * @param mixed[] $values Header values
     *
     * @return string[] Trimmed header values
     *
     * @see https://tools.ietf.org/html/rfc7230#section-3.2.4
     */
    private function trimAndValidateHeaderValues(array $values)
    {
        return array_map(function ($value) {
            if (!is_scalar($value) && null !== $value) {
                throw new \InvalidArgumentException(sprintf(
                    'Header value must be scalar or null but %s provided.',
                    is_object($value) ? get_class($value) : gettype($value)
                ));
            }

            $trimmed = trim((string) $value, " \t");
            $this->assertValue($trimmed);

            return $trimmed;
        }, array_values($values));
    }

    /**
     * @see https://tools.ietf.org/html/rfc7230#section-3.2
     *
     * @param mixed $header
     *
     * @return void
     */
    private function assertHeader($header)
    {
        if (!is_string($header)) {
            throw new \InvalidArgumentException(sprintf(
                'Header name must be a string but %s provided.',
                is_object($header) ? get_class($header) : gettype($header)
            ));
        }

        if ($header === '') {
            throw new \InvalidArgumentException('Header name can not be empty.');
        }

        if (! preg_match('/^[a-zA-Z0-9\'`#$%&*+.^_|~!-]+$/', $header)) {
            throw new \InvalidArgumentException(
                sprintf(
                    '"%s" is not valid header name',
                    $header
                )
            );
        }
    }

    /**
     * @param string $value
     *
     * @return void
     *
     * @see https://tools.ietf.org/html/rfc7230#section-3.2
     *
     * field-value    = *( field-content / obs-fold )
     * field-content  = field-vchar [ 1*( SP / HTAB ) field-vchar ]
     * field-vchar    = VCHAR / obs-text
     * VCHAR          = %x21-7E
     * obs-text       = %x80-FF
     * obs-fold       = CRLF 1*( SP / HTAB )
     */
    private function assertValue($value)
    {
        // The regular expression intentionally does not support the obs-fold production, because as
        // per RFC 7230#3.2.4:
        //
        // A sender MUST NOT generate a message that includes
        // line folding (i.e., that has any field-value that contains a match to
        // the obs-fold rule) unless the message is intended for packaging
        // within the message/http media type.
        //
        // Clients must not send a request with line folding and a server sending folded headers is
        // likely very rare. Line folding is a fairly obscure feature of HTTP/1.1 and thus not accepting
        // folding is not likely to break any legitimate use case.
        if (! preg_match('/^(?:[\x21-\x7E\x80-\xFF](?:[\x20\x09]+[\x21-\x7E\x80-\xFF])?)*$/', $value)) {
            throw new \InvalidArgumentException(sprintf('"%s" is not valid header value', $value));
        }
    }
}
vendor/guzzlehttp/psr7/src/Message.php000064400000020160151327705700014046 0ustar00<?php

namespace WPvividGuzzleHttp\Psr7;

use WPvividPsr\Http\Message\MessageInterface;
use WPvividPsr\Http\Message\RequestInterface;
use WPvividPsr\Http\Message\ResponseInterface;

final class Message
{
    /**
     * Returns the string representation of an HTTP message.
     *
     * @param MessageInterface $message Message to convert to a string.
     *
     * @return string
     */
    public static function toString(MessageInterface $message)
    {
        if ($message instanceof RequestInterface) {
            $msg = trim($message->getMethod() . ' '
                    . $message->getRequestTarget())
                . ' HTTP/' . $message->getProtocolVersion();
            if (!$message->hasHeader('host')) {
                $msg .= "\r\nHost: " . $message->getUri()->getHost();
            }
        } elseif ($message instanceof ResponseInterface) {
            $msg = 'HTTP/' . $message->getProtocolVersion() . ' '
                . $message->getStatusCode() . ' '
                . $message->getReasonPhrase();
        } else {
            throw new \InvalidArgumentException('Unknown message type');
        }

        foreach ($message->getHeaders() as $name => $values) {
            if (strtolower($name) === 'set-cookie') {
                foreach ($values as $value) {
                    $msg .= "\r\n{$name}: " . $value;
                }
            } else {
                $msg .= "\r\n{$name}: " . implode(', ', $values);
            }
        }

        return "{$msg}\r\n\r\n" . $message->getBody();
    }

    /**
     * Get a short summary of the message body.
     *
     * Will return `null` if the response is not printable.
     *
     * @param MessageInterface $message    The message to get the body summary
     * @param int              $truncateAt The maximum allowed size of the summary
     *
     * @return string|null
     */
    public static function bodySummary(MessageInterface $message, $truncateAt = 120)
    {
        $body = $message->getBody();

        if (!$body->isSeekable() || !$body->isReadable()) {
            return null;
        }

        $size = $body->getSize();

        if ($size === 0) {
            return null;
        }

        $summary = $body->read($truncateAt);
        $body->rewind();

        if ($size > $truncateAt) {
            $summary .= ' (truncated...)';
        }

        // Matches any printable character, including unicode characters:
        // letters, marks, numbers, punctuation, spacing, and separators.
        if (preg_match('/[^\pL\pM\pN\pP\pS\pZ\n\r\t]/u', $summary)) {
            return null;
        }

        return $summary;
    }

    /**
     * Attempts to rewind a message body and throws an exception on failure.
     *
     * The body of the message will only be rewound if a call to `tell()`
     * returns a value other than `0`.
     *
     * @param MessageInterface $message Message to rewind
     *
     * @throws \RuntimeException
     */
    public static function rewindBody(MessageInterface $message)
    {
        $body = $message->getBody();

        if ($body->tell()) {
            $body->rewind();
        }
    }

    /**
     * Parses an HTTP message into an associative array.
     *
     * The array contains the "start-line" key containing the start line of
     * the message, "headers" key containing an associative array of header
     * array values, and a "body" key containing the body of the message.
     *
     * @param string $message HTTP request or response to parse.
     *
     * @return array
     */
    public static function parseMessage($message)
    {
        if (!$message) {
            throw new \InvalidArgumentException('Invalid message');
        }

        $message = ltrim($message, "\r\n");

        $messageParts = preg_split("/\r?\n\r?\n/", $message, 2);

        if ($messageParts === false || count($messageParts) !== 2) {
            throw new \InvalidArgumentException('Invalid message: Missing header delimiter');
        }

        list($rawHeaders, $body) = $messageParts;
        $rawHeaders .= "\r\n"; // Put back the delimiter we split previously
        $headerParts = preg_split("/\r?\n/", $rawHeaders, 2);

        if ($headerParts === false || count($headerParts) !== 2) {
            throw new \InvalidArgumentException('Invalid message: Missing status line');
        }

        list($startLine, $rawHeaders) = $headerParts;

        if (preg_match("/(?:^HTTP\/|^[A-Z]+ \S+ HTTP\/)(\d+(?:\.\d+)?)/i", $startLine, $matches) && $matches[1] === '1.0') {
            // Header folding is deprecated for HTTP/1.1, but allowed in HTTP/1.0
            $rawHeaders = preg_replace(Rfc7230::HEADER_FOLD_REGEX, ' ', $rawHeaders);
        }

        /** @var array[] $headerLines */
        $count = preg_match_all(Rfc7230::HEADER_REGEX, $rawHeaders, $headerLines, PREG_SET_ORDER);

        // If these aren't the same, then one line didn't match and there's an invalid header.
        if ($count !== substr_count($rawHeaders, "\n")) {
            // Folding is deprecated, see https://tools.ietf.org/html/rfc7230#section-3.2.4
            if (preg_match(Rfc7230::HEADER_FOLD_REGEX, $rawHeaders)) {
                throw new \InvalidArgumentException('Invalid header syntax: Obsolete line folding');
            }

            throw new \InvalidArgumentException('Invalid header syntax');
        }

        $headers = [];

        foreach ($headerLines as $headerLine) {
            $headers[$headerLine[1]][] = $headerLine[2];
        }

        return [
            'start-line' => $startLine,
            'headers' => $headers,
            'body' => $body,
        ];
    }

    /**
     * Constructs a URI for an HTTP request message.
     *
     * @param string $path    Path from the start-line
     * @param array  $headers Array of headers (each value an array).
     *
     * @return string
     */
    public static function parseRequestUri($path, array $headers)
    {
        $hostKey = array_filter(array_keys($headers), function ($k) {
            return strtolower($k) === 'host';
        });

        // If no host is found, then a full URI cannot be constructed.
        if (!$hostKey) {
            return $path;
        }

        $host = $headers[reset($hostKey)][0];
        $scheme = substr($host, -4) === ':443' ? 'https' : 'http';

        return $scheme . '://' . $host . '/' . ltrim($path, '/');
    }

    /**
     * Parses a request message string into a request object.
     *
     * @param string $message Request message string.
     *
     * @return Request
     */
    public static function parseRequest($message)
    {
        $data = self::parseMessage($message);
        $matches = [];
        if (!preg_match('/^[\S]+\s+([a-zA-Z]+:\/\/|\/).*/', $data['start-line'], $matches)) {
            throw new \InvalidArgumentException('Invalid request string');
        }
        $parts = explode(' ', $data['start-line'], 3);
        $version = isset($parts[2]) ? explode('/', $parts[2])[1] : '1.1';

        $request = new Request(
            $parts[0],
            $matches[1] === '/' ? self::parseRequestUri($parts[1], $data['headers']) : $parts[1],
            $data['headers'],
            $data['body'],
            $version
        );

        return $matches[1] === '/' ? $request : $request->withRequestTarget($parts[1]);
    }

    /**
     * Parses a response message string into a response object.
     *
     * @param string $message Response message string.
     *
     * @return Response
     */
    public static function parseResponse($message)
    {
        $data = self::parseMessage($message);
        // According to https://tools.ietf.org/html/rfc7230#section-3.1.2 the space
        // between status-code and reason-phrase is required. But browsers accept
        // responses without space and reason as well.
        if (!preg_match('/^HTTP\/.* [0-9]{3}( .*|$)/', $data['start-line'])) {
            throw new \InvalidArgumentException('Invalid response string: ' . $data['start-line']);
        }
        $parts = explode(' ', $data['start-line'], 3);

        return new Response(
            (int) $parts[1],
            $data['headers'],
            $data['body'],
            explode('/', $parts[0])[1],
            isset($parts[2]) ? $parts[2] : null
        );
    }
}
vendor/guzzlehttp/psr7/src/Query.php000064400000006651151327705700013600 0ustar00<?php

namespace WPvividGuzzleHttp\Psr7;

final class Query
{
    /**
     * Parse a query string into an associative array.
     *
     * If multiple values are found for the same key, the value of that key
     * value pair will become an array. This function does not parse nested
     * PHP style arrays into an associative array (e.g., `foo[a]=1&foo[b]=2`
     * will be parsed into `['foo[a]' => '1', 'foo[b]' => '2'])`.
     *
     * @param string   $str         Query string to parse
     * @param int|bool $urlEncoding How the query string is encoded
     *
     * @return array
     */
    public static function parse($str, $urlEncoding = true)
    {
        $result = [];

        if ($str === '') {
            return $result;
        }

        if ($urlEncoding === true) {
            $decoder = function ($value) {
                return rawurldecode(str_replace('+', ' ', $value));
            };
        } elseif ($urlEncoding === PHP_QUERY_RFC3986) {
            $decoder = 'rawurldecode';
        } elseif ($urlEncoding === PHP_QUERY_RFC1738) {
            $decoder = 'urldecode';
        } else {
            $decoder = function ($str) {
                return $str;
            };
        }

        foreach (explode('&', $str) as $kvp) {
            $parts = explode('=', $kvp, 2);
            $key = $decoder($parts[0]);
            $value = isset($parts[1]) ? $decoder($parts[1]) : null;
            if (!isset($result[$key])) {
                $result[$key] = $value;
            } else {
                if (!is_array($result[$key])) {
                    $result[$key] = [$result[$key]];
                }
                $result[$key][] = $value;
            }
        }

        return $result;
    }

    /**
     * Build a query string from an array of key value pairs.
     *
     * This function can use the return value of `parse()` to build a query
     * string. This function does not modify the provided keys when an array is
     * encountered (like `http_build_query()` would).
     *
     * @param array     $params   Query string parameters.
     * @param int|false $encoding Set to false to not encode, PHP_QUERY_RFC3986
     *                            to encode using RFC3986, or PHP_QUERY_RFC1738
     *                            to encode using RFC1738.
     *
     * @return string
     */
    public static function build(array $params, $encoding = PHP_QUERY_RFC3986)
    {
        if (!$params) {
            return '';
        }

        if ($encoding === false) {
            $encoder = function ($str) {
                return $str;
            };
        } elseif ($encoding === PHP_QUERY_RFC3986) {
            $encoder = 'rawurlencode';
        } elseif ($encoding === PHP_QUERY_RFC1738) {
            $encoder = 'urlencode';
        } else {
            throw new \InvalidArgumentException('Invalid type');
        }

        $qs = '';
        foreach ($params as $k => $v) {
            $k = $encoder($k);
            if (!is_array($v)) {
                $qs .= $k;
                if ($v !== null) {
                    $qs .= '=' . $encoder($v);
                }
                $qs .= '&';
            } else {
                foreach ($v as $vv) {
                    $qs .= $k;
                    if ($vv !== null) {
                        $qs .= '=' . $encoder($vv);
                    }
                    $qs .= '&';
                }
            }
        }

        return $qs ? (string) substr($qs, 0, -1) : '';
    }
}
vendor/guzzlehttp/psr7/src/StreamDecoratorTrait.php000064400000006351151327705700016572 0ustar00<?php

namespace WPvividGuzzleHttp\Psr7;

use WPvividPsr\Http\Message\StreamInterface;

/**
 * Stream decorator trait
 *
 * @property StreamInterface stream
 */
trait StreamDecoratorTrait
{
    /**
     * @param StreamInterface $stream Stream to decorate
     */
    public function __construct(StreamInterface $stream)
    {
        $this->stream = $stream;
    }

    /**
     * Magic method used to create a new stream if streams are not added in
     * the constructor of a decorator (e.g., LazyOpenStream).
     *
     * @param string $name Name of the property (allows "stream" only).
     *
     * @return StreamInterface
     */
    public function __get($name)
    {
        if ($name == 'stream') {
            $this->stream = $this->createStream();
            return $this->stream;
        }

        throw new \UnexpectedValueException("$name not found on class");
    }

    public function __toString()
    {
        try {
            if ($this->isSeekable()) {
                $this->seek(0);
            }
            return $this->getContents();
        } catch (\Exception $e) {
            // Really, PHP? https://bugs.php.net/bug.php?id=53648
            trigger_error('StreamDecorator::__toString exception: '
                . (string) $e, E_USER_ERROR);
            return '';
        }
    }

    public function getContents()
    {
        return Utils::copyToString($this);
    }

    /**
     * Allow decorators to implement custom methods
     *
     * @param string $method Missing method name
     * @param array  $args   Method arguments
     *
     * @return mixed
     */
    public function __call($method, array $args)
    {
        $result = call_user_func_array([$this->stream, $method], $args);

        // Always return the wrapped object if the result is a return $this
        return $result === $this->stream ? $this : $result;
    }

    public function close()
    {
        $this->stream->close();
    }

    public function getMetadata($key = null)
    {
        return $this->stream->getMetadata($key);
    }

    public function detach()
    {
        return $this->stream->detach();
    }

    public function getSize()
    {
        return $this->stream->getSize();
    }

    public function eof()
    {
        return $this->stream->eof();
    }

    public function tell()
    {
        return $this->stream->tell();
    }

    public function isReadable()
    {
        return $this->stream->isReadable();
    }

    public function isWritable()
    {
        return $this->stream->isWritable();
    }

    public function isSeekable()
    {
        return $this->stream->isSeekable();
    }

    public function rewind()
    {
        $this->seek(0);
    }

    public function seek($offset, $whence = SEEK_SET)
    {
        $this->stream->seek($offset, $whence);
    }

    public function read($length)
    {
        return $this->stream->read($length);
    }

    public function write($string)
    {
        return $this->stream->write($string);
    }

    /**
     * Implement in subclasses to dynamically create streams when requested.
     *
     * @return StreamInterface
     *
     * @throws \BadMethodCallException
     */
    protected function createStream()
    {
        throw new \BadMethodCallException('Not implemented');
    }
}
vendor/guzzlehttp/psr7/src/UploadedFile.php000064400000017160151327705700015025 0ustar00<?php

namespace WPvividGuzzleHttp\Psr7;

use InvalidArgumentException;
use WPvividPsr\Http\Message\StreamInterface;
use WPvividPsr\Http\Message\UploadedFileInterface;
use RuntimeException;

class UploadedFile implements UploadedFileInterface
{
    /**
     * @var int[]
     */
    private static $errors = [
        UPLOAD_ERR_OK,
        UPLOAD_ERR_INI_SIZE,
        UPLOAD_ERR_FORM_SIZE,
        UPLOAD_ERR_PARTIAL,
        UPLOAD_ERR_NO_FILE,
        UPLOAD_ERR_NO_TMP_DIR,
        UPLOAD_ERR_CANT_WRITE,
        UPLOAD_ERR_EXTENSION,
    ];

    /**
     * @var string
     */
    private $clientFilename;

    /**
     * @var string
     */
    private $clientMediaType;

    /**
     * @var int
     */
    private $error;

    /**
     * @var string|null
     */
    private $file;

    /**
     * @var bool
     */
    private $moved = false;

    /**
     * @var int
     */
    private $size;

    /**
     * @var StreamInterface|null
     */
    private $stream;

    /**
     * @param StreamInterface|string|resource $streamOrFile
     * @param int                             $size
     * @param int                             $errorStatus
     * @param string|null                     $clientFilename
     * @param string|null                     $clientMediaType
     */
    public function __construct(
        $streamOrFile,
        $size,
        $errorStatus,
        $clientFilename = null,
        $clientMediaType = null
    ) {
        $this->setError($errorStatus);
        $this->setSize($size);
        $this->setClientFilename($clientFilename);
        $this->setClientMediaType($clientMediaType);

        if ($this->isOk()) {
            $this->setStreamOrFile($streamOrFile);
        }
    }

    /**
     * Depending on the value set file or stream variable
     *
     * @param mixed $streamOrFile
     *
     * @throws InvalidArgumentException
     */
    private function setStreamOrFile($streamOrFile)
    {
        if (is_string($streamOrFile)) {
            $this->file = $streamOrFile;
        } elseif (is_resource($streamOrFile)) {
            $this->stream = new Stream($streamOrFile);
        } elseif ($streamOrFile instanceof StreamInterface) {
            $this->stream = $streamOrFile;
        } else {
            throw new InvalidArgumentException(
                'Invalid stream or file provided for UploadedFile'
            );
        }
    }

    /**
     * @param int $error
     *
     * @throws InvalidArgumentException
     */
    private function setError($error)
    {
        if (false === is_int($error)) {
            throw new InvalidArgumentException(
                'Upload file error status must be an integer'
            );
        }

        if (false === in_array($error, UploadedFile::$errors)) {
            throw new InvalidArgumentException(
                'Invalid error status for UploadedFile'
            );
        }

        $this->error = $error;
    }

    /**
     * @param int $size
     *
     * @throws InvalidArgumentException
     */
    private function setSize($size)
    {
        if (false === is_int($size)) {
            throw new InvalidArgumentException(
                'Upload file size must be an integer'
            );
        }

        $this->size = $size;
    }

    /**
     * @param mixed $param
     *
     * @return bool
     */
    private function isStringOrNull($param)
    {
        return in_array(gettype($param), ['string', 'NULL']);
    }

    /**
     * @param mixed $param
     *
     * @return bool
     */
    private function isStringNotEmpty($param)
    {
        return is_string($param) && false === empty($param);
    }

    /**
     * @param string|null $clientFilename
     *
     * @throws InvalidArgumentException
     */
    private function setClientFilename($clientFilename)
    {
        if (false === $this->isStringOrNull($clientFilename)) {
            throw new InvalidArgumentException(
                'Upload file client filename must be a string or null'
            );
        }

        $this->clientFilename = $clientFilename;
    }

    /**
     * @param string|null $clientMediaType
     *
     * @throws InvalidArgumentException
     */
    private function setClientMediaType($clientMediaType)
    {
        if (false === $this->isStringOrNull($clientMediaType)) {
            throw new InvalidArgumentException(
                'Upload file client media type must be a string or null'
            );
        }

        $this->clientMediaType = $clientMediaType;
    }

    /**
     * Return true if there is no upload error
     *
     * @return bool
     */
    private function isOk()
    {
        return $this->error === UPLOAD_ERR_OK;
    }

    /**
     * @return bool
     */
    public function isMoved()
    {
        return $this->moved;
    }

    /**
     * @throws RuntimeException if is moved or not ok
     */
    private function validateActive()
    {
        if (false === $this->isOk()) {
            throw new RuntimeException('Cannot retrieve stream due to upload error');
        }

        if ($this->isMoved()) {
            throw new RuntimeException('Cannot retrieve stream after it has already been moved');
        }
    }

    /**
     * {@inheritdoc}
     *
     * @throws RuntimeException if the upload was not successful.
     */
    public function getStream()
    {
        $this->validateActive();

        if ($this->stream instanceof StreamInterface) {
            return $this->stream;
        }

        return new LazyOpenStream($this->file, 'r+');
    }

    /**
     * {@inheritdoc}
     *
     * @see http://php.net/is_uploaded_file
     * @see http://php.net/move_uploaded_file
     *
     * @param string $targetPath Path to which to move the uploaded file.
     *
     * @throws RuntimeException         if the upload was not successful.
     * @throws InvalidArgumentException if the $path specified is invalid.
     * @throws RuntimeException         on any error during the move operation, or on
     *                                  the second or subsequent call to the method.
     */
    public function moveTo($targetPath)
    {
        $this->validateActive();

        if (false === $this->isStringNotEmpty($targetPath)) {
            throw new InvalidArgumentException(
                'Invalid path provided for move operation; must be a non-empty string'
            );
        }

        if ($this->file) {
            $this->moved = php_sapi_name() == 'cli'
                ? rename($this->file, $targetPath)
                : move_uploaded_file($this->file, $targetPath);
        } else {
            Utils::copyToStream(
                $this->getStream(),
                new LazyOpenStream($targetPath, 'w')
            );

            $this->moved = true;
        }

        if (false === $this->moved) {
            throw new RuntimeException(
                sprintf('Uploaded file could not be moved to %s', $targetPath)
            );
        }
    }

    /**
     * {@inheritdoc}
     *
     * @return int|null The file size in bytes or null if unknown.
     */
    public function getSize()
    {
        return $this->size;
    }

    /**
     * {@inheritdoc}
     *
     * @see http://php.net/manual/en/features.file-upload.errors.php
     *
     * @return int One of PHP's UPLOAD_ERR_XXX constants.
     */
    public function getError()
    {
        return $this->error;
    }

    /**
     * {@inheritdoc}
     *
     * @return string|null The filename sent by the client or null if none
     *                     was provided.
     */
    public function getClientFilename()
    {
        return $this->clientFilename;
    }

    /**
     * {@inheritdoc}
     */
    public function getClientMediaType()
    {
        return $this->clientMediaType;
    }
}
vendor/guzzlehttp/psr7/src/InflateStream.php000064400000003503151327705700015222 0ustar00<?php

namespace WPvividGuzzleHttp\Psr7;

use WPvividPsr\Http\Message\StreamInterface;

/**
 * Uses PHP's zlib.inflate filter to inflate deflate or gzipped content.
 *
 * This stream decorator skips the first 10 bytes of the given stream to remove
 * the gzip header, converts the provided stream to a PHP stream resource,
 * then appends the zlib.inflate filter. The stream is then converted back
 * to a Guzzle stream resource to be used as a Guzzle stream.
 *
 * @link http://tools.ietf.org/html/rfc1952
 * @link http://php.net/manual/en/filters.compression.php
 *
 * @final
 */
class InflateStream implements StreamInterface
{
    use StreamDecoratorTrait;

    public function __construct(StreamInterface $stream)
    {
        // read the first 10 bytes, ie. gzip header
        $header = $stream->read(10);
        $filenameHeaderLength = $this->getLengthOfPossibleFilenameHeader($stream, $header);
        // Skip the header, that is 10 + length of filename + 1 (nil) bytes
        $stream = new LimitStream($stream, -1, 10 + $filenameHeaderLength);
        $resource = StreamWrapper::getResource($stream);
        stream_filter_append($resource, 'zlib.inflate', STREAM_FILTER_READ);
        $this->stream = $stream->isSeekable() ? new Stream($resource) : new NoSeekStream(new Stream($resource));
    }

    /**
     * @param StreamInterface $stream
     * @param $header
     *
     * @return int
     */
    private function getLengthOfPossibleFilenameHeader(StreamInterface $stream, $header)
    {
        $filename_header_length = 0;

        if (substr(bin2hex($header), 6, 2) === '08') {
            // we have a filename, read until nil
            $filename_header_length = 1;
            while ($stream->read(1) !== chr(0)) {
                $filename_header_length++;
            }
        }

        return $filename_header_length;
    }
}
vendor/guzzlehttp/psr7/src/MultipartStream.php000064400000011257151327705700015626 0ustar00<?php

namespace WPvividGuzzleHttp\Psr7;

use WPvividPsr\Http\Message\StreamInterface;

/**
 * Stream that when read returns bytes for a streaming multipart or
 * multipart/form-data stream.
 *
 * @final
 */
class MultipartStream implements StreamInterface
{
    use StreamDecoratorTrait;

    private $boundary;

    /**
     * @param array  $elements Array of associative arrays, each containing a
     *                         required "name" key mapping to the form field,
     *                         name, a required "contents" key mapping to a
     *                         StreamInterface/resource/string, an optional
     *                         "headers" associative array of custom headers,
     *                         and an optional "filename" key mapping to a
     *                         string to send as the filename in the part.
     * @param string $boundary You can optionally provide a specific boundary
     *
     * @throws \InvalidArgumentException
     */
    public function __construct(array $elements = [], $boundary = null)
    {
        $this->boundary = $boundary ?: sha1(uniqid('', true));
        $this->stream = $this->createStream($elements);
    }

    /**
     * Get the boundary
     *
     * @return string
     */
    public function getBoundary()
    {
        return $this->boundary;
    }

    public function isWritable()
    {
        return false;
    }

    /**
     * Get the headers needed before transferring the content of a POST file
     */
    private function getHeaders(array $headers)
    {
        $str = '';
        foreach ($headers as $key => $value) {
            $str .= "{$key}: {$value}\r\n";
        }

        return "--{$this->boundary}\r\n" . trim($str) . "\r\n\r\n";
    }

    /**
     * Create the aggregate stream that will be used to upload the POST data
     */
    protected function createStream(array $elements)
    {
        $stream = new AppendStream();

        foreach ($elements as $element) {
            $this->addElement($stream, $element);
        }

        // Add the trailing boundary with CRLF
        $stream->addStream(Utils::streamFor("--{$this->boundary}--\r\n"));

        return $stream;
    }

    private function addElement(AppendStream $stream, array $element)
    {
        foreach (['contents', 'name'] as $key) {
            if (!array_key_exists($key, $element)) {
                throw new \InvalidArgumentException("A '{$key}' key is required");
            }
        }

        $element['contents'] = Utils::streamFor($element['contents']);

        if (empty($element['filename'])) {
            $uri = $element['contents']->getMetadata('uri');
            if (substr($uri, 0, 6) !== 'php://') {
                $element['filename'] = $uri;
            }
        }

        list($body, $headers) = $this->createElement(
            $element['name'],
            $element['contents'],
            isset($element['filename']) ? $element['filename'] : null,
            isset($element['headers']) ? $element['headers'] : []
        );

        $stream->addStream(Utils::streamFor($this->getHeaders($headers)));
        $stream->addStream($body);
        $stream->addStream(Utils::streamFor("\r\n"));
    }

    /**
     * @return array
     */
    private function createElement($name, StreamInterface $stream, $filename, array $headers)
    {
        // Set a default content-disposition header if one was no provided
        $disposition = $this->getHeader($headers, 'content-disposition');
        if (!$disposition) {
            $headers['Content-Disposition'] = ($filename === '0' || $filename)
                ? sprintf(
                    'form-data; name="%s"; filename="%s"',
                    $name,
                    basename($filename)
                )
                : "form-data; name=\"{$name}\"";
        }

        // Set a default content-length header if one was no provided
        $length = $this->getHeader($headers, 'content-length');
        if (!$length) {
            if ($length = $stream->getSize()) {
                $headers['Content-Length'] = (string) $length;
            }
        }

        // Set a default Content-Type if one was not supplied
        $type = $this->getHeader($headers, 'content-type');
        if (!$type && ($filename === '0' || $filename)) {
            if ($type = MimeType::fromFilename($filename)) {
                $headers['Content-Type'] = $type;
            }
        }

        return [$stream, $headers];
    }

    private function getHeader(array $headers, $key)
    {
        $lowercaseHeader = strtolower($key);
        foreach ($headers as $k => $v) {
            if (strtolower($k) === $lowercaseHeader) {
                return $v;
            }
        }

        return null;
    }
}
vendor/guzzlehttp/psr7/src/PumpStream.php000064400000010002151327705700014551 0ustar00<?php

namespace WPvividGuzzleHttp\Psr7;

use WPvividPsr\Http\Message\StreamInterface;

/**
 * Provides a read only stream that pumps data from a PHP callable.
 *
 * When invoking the provided callable, the PumpStream will pass the amount of
 * data requested to read to the callable. The callable can choose to ignore
 * this value and return fewer or more bytes than requested. Any extra data
 * returned by the provided callable is buffered internally until drained using
 * the read() function of the PumpStream. The provided callable MUST return
 * false when there is no more data to read.
 *
 * @final
 */
class PumpStream implements StreamInterface
{
    /** @var callable */
    private $source;

    /** @var int */
    private $size;

    /** @var int */
    private $tellPos = 0;

    /** @var array */
    private $metadata;

    /** @var BufferStream */
    private $buffer;

    /**
     * @param callable $source  Source of the stream data. The callable MAY
     *                          accept an integer argument used to control the
     *                          amount of data to return. The callable MUST
     *                          return a string when called, or false on error
     *                          or EOF.
     * @param array    $options Stream options:
     *                          - metadata: Hash of metadata to use with stream.
     *                          - size: Size of the stream, if known.
     */
    public function __construct(callable $source, array $options = [])
    {
        $this->source = $source;
        $this->size = isset($options['size']) ? $options['size'] : null;
        $this->metadata = isset($options['metadata']) ? $options['metadata'] : [];
        $this->buffer = new BufferStream();
    }

    public function __toString()
    {
        try {
            return Utils::copyToString($this);
        } catch (\Exception $e) {
            return '';
        }
    }

    public function close()
    {
        $this->detach();
    }

    public function detach()
    {
        $this->tellPos = false;
        $this->source = null;

        return null;
    }

    public function getSize()
    {
        return $this->size;
    }

    public function tell()
    {
        return $this->tellPos;
    }

    public function eof()
    {
        return !$this->source;
    }

    public function isSeekable()
    {
        return false;
    }

    public function rewind()
    {
        $this->seek(0);
    }

    public function seek($offset, $whence = SEEK_SET)
    {
        throw new \RuntimeException('Cannot seek a PumpStream');
    }

    public function isWritable()
    {
        return false;
    }

    public function write($string)
    {
        throw new \RuntimeException('Cannot write to a PumpStream');
    }

    public function isReadable()
    {
        return true;
    }

    public function read($length)
    {
        $data = $this->buffer->read($length);
        $readLen = strlen($data);
        $this->tellPos += $readLen;
        $remaining = $length - $readLen;

        if ($remaining) {
            $this->pump($remaining);
            $data .= $this->buffer->read($remaining);
            $this->tellPos += strlen($data) - $readLen;
        }

        return $data;
    }

    public function getContents()
    {
        $result = '';
        while (!$this->eof()) {
            $result .= $this->read(1000000);
        }

        return $result;
    }

    public function getMetadata($key = null)
    {
        if (!$key) {
            return $this->metadata;
        }

        return isset($this->metadata[$key]) ? $this->metadata[$key] : null;
    }

    private function pump($length)
    {
        if ($this->source) {
            do {
                $data = call_user_func($this->source, $length);
                if ($data === false || $data === null) {
                    $this->source = null;
                    return;
                }
                $this->buffer->write($data);
                $length -= strlen($data);
            } while ($length > 0);
        }
    }
}
vendor/guzzlehttp/psr7/src/LimitStream.php000064400000010217151327705700014716 0ustar00<?php

namespace WPvividGuzzleHttp\Psr7;

use WPvividPsr\Http\Message\StreamInterface;

/**
 * Decorator used to return only a subset of a stream.
 *
 * @final
 */
class LimitStream implements StreamInterface
{
    use StreamDecoratorTrait;

    /** @var int Offset to start reading from */
    private $offset;

    /** @var int Limit the number of bytes that can be read */
    private $limit;

    /**
     * @param StreamInterface $stream Stream to wrap
     * @param int             $limit  Total number of bytes to allow to be read
     *                                from the stream. Pass -1 for no limit.
     * @param int             $offset Position to seek to before reading (only
     *                                works on seekable streams).
     */
    public function __construct(
        StreamInterface $stream,
        $limit = -1,
        $offset = 0
    ) {
        $this->stream = $stream;
        $this->setLimit($limit);
        $this->setOffset($offset);
    }

    public function eof()
    {
        // Always return true if the underlying stream is EOF
        if ($this->stream->eof()) {
            return true;
        }

        // No limit and the underlying stream is not at EOF
        if ($this->limit == -1) {
            return false;
        }

        return $this->stream->tell() >= $this->offset + $this->limit;
    }

    /**
     * Returns the size of the limited subset of data
     * {@inheritdoc}
     */
    public function getSize()
    {
        if (null === ($length = $this->stream->getSize())) {
            return null;
        } elseif ($this->limit == -1) {
            return $length - $this->offset;
        } else {
            return min($this->limit, $length - $this->offset);
        }
    }

    /**
     * Allow for a bounded seek on the read limited stream
     * {@inheritdoc}
     */
    public function seek($offset, $whence = SEEK_SET)
    {
        if ($whence !== SEEK_SET || $offset < 0) {
            throw new \RuntimeException(sprintf(
                'Cannot seek to offset %s with whence %s',
                $offset,
                $whence
            ));
        }

        $offset += $this->offset;

        if ($this->limit !== -1) {
            if ($offset > $this->offset + $this->limit) {
                $offset = $this->offset + $this->limit;
            }
        }

        $this->stream->seek($offset);
    }

    /**
     * Give a relative tell()
     * {@inheritdoc}
     */
    public function tell()
    {
        return $this->stream->tell() - $this->offset;
    }

    /**
     * Set the offset to start limiting from
     *
     * @param int $offset Offset to seek to and begin byte limiting from
     *
     * @throws \RuntimeException if the stream cannot be seeked.
     */
    public function setOffset($offset)
    {
        $current = $this->stream->tell();

        if ($current !== $offset) {
            // If the stream cannot seek to the offset position, then read to it
            if ($this->stream->isSeekable()) {
                $this->stream->seek($offset);
            } elseif ($current > $offset) {
                throw new \RuntimeException("Could not seek to stream offset $offset");
            } else {
                $this->stream->read($offset - $current);
            }
        }

        $this->offset = $offset;
    }

    /**
     * Set the limit of bytes that the decorator allows to be read from the
     * stream.
     *
     * @param int $limit Number of bytes to allow to be read from the stream.
     *                   Use -1 for no limit.
     */
    public function setLimit($limit)
    {
        $this->limit = $limit;
    }

    public function read($length)
    {
        if ($this->limit == -1) {
            return $this->stream->read($length);
        }

        // Check if the current position is less than the total allowed
        // bytes + original offset
        $remaining = ($this->offset + $this->limit) - $this->stream->tell();
        if ($remaining > 0) {
            // Only return the amount of requested data, ensuring that the byte
            // limit is not exceeded
            return $this->stream->read(min($remaining, $length));
        }

        return '';
    }
}
vendor/guzzlehttp/psr7/src/FnStream.php000064400000007604151327705700014211 0ustar00<?php

namespace WPvividGuzzleHttp\Psr7;

use WPvividPsr\Http\Message\StreamInterface;

/**
 * Compose stream implementations based on a hash of functions.
 *
 * Allows for easy testing and extension of a provided stream without needing
 * to create a concrete class for a simple extension point.
 *
 * @final
 */
class FnStream implements StreamInterface
{
    /** @var array */
    private $methods;

    /** @var array Methods that must be implemented in the given array */
    private static $slots = ['__toString', 'close', 'detach', 'rewind',
        'getSize', 'tell', 'eof', 'isSeekable', 'seek', 'isWritable', 'write',
        'isReadable', 'read', 'getContents', 'getMetadata'];

    /**
     * @param array $methods Hash of method name to a callable.
     */
    public function __construct(array $methods)
    {
        $this->methods = $methods;

        // Create the functions on the class
        foreach ($methods as $name => $fn) {
            $this->{'_fn_' . $name} = $fn;
        }
    }

    /**
     * Lazily determine which methods are not implemented.
     *
     * @throws \BadMethodCallException
     */
    public function __get($name)
    {
        throw new \BadMethodCallException(str_replace('_fn_', '', $name)
            . '() is not implemented in the FnStream');
    }

    /**
     * The close method is called on the underlying stream only if possible.
     */
    public function __destruct()
    {
        if (isset($this->_fn_close)) {
            call_user_func($this->_fn_close);
        }
    }

    /**
     * An unserialize would allow the __destruct to run when the unserialized value goes out of scope.
     *
     * @throws \LogicException
     */
    public function __wakeup()
    {
        throw new \LogicException('FnStream should never be unserialized');
    }

    /**
     * Adds custom functionality to an underlying stream by intercepting
     * specific method calls.
     *
     * @param StreamInterface $stream  Stream to decorate
     * @param array           $methods Hash of method name to a closure
     *
     * @return FnStream
     */
    public static function decorate(StreamInterface $stream, array $methods)
    {
        // If any of the required methods were not provided, then simply
        // proxy to the decorated stream.
        foreach (array_diff(self::$slots, array_keys($methods)) as $diff) {
            $methods[$diff] = [$stream, $diff];
        }

        return new self($methods);
    }

    public function __toString()
    {
        return call_user_func($this->_fn___toString);
    }

    public function close()
    {
        return call_user_func($this->_fn_close);
    }

    public function detach()
    {
        return call_user_func($this->_fn_detach);
    }

    public function getSize()
    {
        return call_user_func($this->_fn_getSize);
    }

    public function tell()
    {
        return call_user_func($this->_fn_tell);
    }

    public function eof()
    {
        return call_user_func($this->_fn_eof);
    }

    public function isSeekable()
    {
        return call_user_func($this->_fn_isSeekable);
    }

    public function rewind()
    {
        call_user_func($this->_fn_rewind);
    }

    public function seek($offset, $whence = SEEK_SET)
    {
        call_user_func($this->_fn_seek, $offset, $whence);
    }

    public function isWritable()
    {
        return call_user_func($this->_fn_isWritable);
    }

    public function write($string)
    {
        return call_user_func($this->_fn_write, $string);
    }

    public function isReadable()
    {
        return call_user_func($this->_fn_isReadable);
    }

    public function read($length)
    {
        return call_user_func($this->_fn_read, $length);
    }

    public function getContents()
    {
        return call_user_func($this->_fn_getContents);
    }

    public function getMetadata($key = null)
    {
        return call_user_func($this->_fn_getMetadata, $key);
    }
}
vendor/guzzlehttp/psr7/src/Response.php000064400000011336151327705700014265 0ustar00<?php

namespace WPvividGuzzleHttp\Psr7;

use WPvividPsr\Http\Message\ResponseInterface;
use WPvividPsr\Http\Message\StreamInterface;

/**
 * PSR-7 response implementation.
 */
class Response implements ResponseInterface
{
    use MessageTrait;

    /** @var array Map of standard HTTP status code/reason phrases */
    private static $phrases = [
        100 => 'Continue',
        101 => 'Switching Protocols',
        102 => 'Processing',
        200 => 'OK',
        201 => 'Created',
        202 => 'Accepted',
        203 => 'Non-Authoritative Information',
        204 => 'No Content',
        205 => 'Reset Content',
        206 => 'Partial Content',
        207 => 'Multi-status',
        208 => 'Already Reported',
        300 => 'Multiple Choices',
        301 => 'Moved Permanently',
        302 => 'Found',
        303 => 'See Other',
        304 => 'Not Modified',
        305 => 'Use Proxy',
        306 => 'Switch Proxy',
        307 => 'Temporary Redirect',
        400 => 'Bad Request',
        401 => 'Unauthorized',
        402 => 'Payment Required',
        403 => 'Forbidden',
        404 => 'Not Found',
        405 => 'Method Not Allowed',
        406 => 'Not Acceptable',
        407 => 'Proxy Authentication Required',
        408 => 'Request Time-out',
        409 => 'Conflict',
        410 => 'Gone',
        411 => 'Length Required',
        412 => 'Precondition Failed',
        413 => 'Request Entity Too Large',
        414 => 'Request-URI Too Large',
        415 => 'Unsupported Media Type',
        416 => 'Requested range not satisfiable',
        417 => 'Expectation Failed',
        418 => 'I\'m a teapot',
        422 => 'Unprocessable Entity',
        423 => 'Locked',
        424 => 'Failed Dependency',
        425 => 'Unordered Collection',
        426 => 'Upgrade Required',
        428 => 'Precondition Required',
        429 => 'Too Many Requests',
        431 => 'Request Header Fields Too Large',
        451 => 'Unavailable For Legal Reasons',
        500 => 'Internal Server Error',
        501 => 'Not Implemented',
        502 => 'Bad Gateway',
        503 => 'Service Unavailable',
        504 => 'Gateway Time-out',
        505 => 'HTTP Version not supported',
        506 => 'Variant Also Negotiates',
        507 => 'Insufficient Storage',
        508 => 'Loop Detected',
        511 => 'Network Authentication Required',
    ];

    /** @var string */
    private $reasonPhrase = '';

    /** @var int */
    private $statusCode = 200;

    /**
     * @param int                                  $status  Status code
     * @param array                                $headers Response headers
     * @param string|resource|StreamInterface|null $body    Response body
     * @param string                               $version Protocol version
     * @param string|null                          $reason  Reason phrase (when empty a default will be used based on the status code)
     */
    public function __construct(
        $status = 200,
        array $headers = [],
        $body = null,
        $version = '1.1',
        $reason = null
    ) {
        $this->assertStatusCodeIsInteger($status);
        $status = (int) $status;
        $this->assertStatusCodeRange($status);

        $this->statusCode = $status;

        if ($body !== '' && $body !== null) {
            $this->stream = Utils::streamFor($body);
        }

        $this->setHeaders($headers);
        if ($reason == '' && isset(self::$phrases[$this->statusCode])) {
            $this->reasonPhrase = self::$phrases[$this->statusCode];
        } else {
            $this->reasonPhrase = (string) $reason;
        }

        $this->protocol = $version;
    }

    public function getStatusCode()
    {
        return $this->statusCode;
    }

    public function getReasonPhrase()
    {
        return $this->reasonPhrase;
    }

    public function withStatus($code, $reasonPhrase = '')
    {
        $this->assertStatusCodeIsInteger($code);
        $code = (int) $code;
        $this->assertStatusCodeRange($code);

        $new = clone $this;
        $new->statusCode = $code;
        if ($reasonPhrase == '' && isset(self::$phrases[$new->statusCode])) {
            $reasonPhrase = self::$phrases[$new->statusCode];
        }
        $new->reasonPhrase = (string) $reasonPhrase;
        return $new;
    }

    private function assertStatusCodeIsInteger($statusCode)
    {
        if (filter_var($statusCode, FILTER_VALIDATE_INT) === false) {
            throw new \InvalidArgumentException('Status code must be an integer value.');
        }
    }

    private function assertStatusCodeRange($statusCode)
    {
        if ($statusCode < 100 || $statusCode >= 600) {
            throw new \InvalidArgumentException('Status code must be an integer value between 1xx and 5xx.');
        }
    }
}
vendor/guzzlehttp/psr7/src/Request.php000064400000007243151327705700014121 0ustar00<?php

namespace WPvividGuzzleHttp\Psr7;

use InvalidArgumentException;
use WPvividPsr\Http\Message\RequestInterface;
use WPvividPsr\Http\Message\StreamInterface;
use WPvividPsr\Http\Message\UriInterface;

/**
 * PSR-7 request implementation.
 */
class Request implements RequestInterface
{
    use MessageTrait;

    /** @var string */
    private $method;

    /** @var string|null */
    private $requestTarget;

    /** @var UriInterface */
    private $uri;

    /**
     * @param string                               $method  HTTP method
     * @param string|UriInterface                  $uri     URI
     * @param array                                $headers Request headers
     * @param string|resource|StreamInterface|null $body    Request body
     * @param string                               $version Protocol version
     */
    public function __construct(
        $method,
        $uri,
        array $headers = [],
        $body = null,
        $version = '1.1'
    ) {
        $this->assertMethod($method);
        if (!($uri instanceof UriInterface)) {
            $uri = new Uri($uri);
        }

        $this->method = strtoupper($method);
        $this->uri = $uri;
        $this->setHeaders($headers);
        $this->protocol = $version;

        if (!isset($this->headerNames['host'])) {
            $this->updateHostFromUri();
        }

        if ($body !== '' && $body !== null) {
            $this->stream = Utils::streamFor($body);
        }
    }

    public function getRequestTarget()
    {
        if ($this->requestTarget !== null) {
            return $this->requestTarget;
        }

        $target = $this->uri->getPath();
        if ($target == '') {
            $target = '/';
        }
        if ($this->uri->getQuery() != '') {
            $target .= '?' . $this->uri->getQuery();
        }

        return $target;
    }

    public function withRequestTarget($requestTarget)
    {
        if (preg_match('#\s#', $requestTarget)) {
            throw new InvalidArgumentException(
                'Invalid request target provided; cannot contain whitespace'
            );
        }

        $new = clone $this;
        $new->requestTarget = $requestTarget;
        return $new;
    }

    public function getMethod()
    {
        return $this->method;
    }

    public function withMethod($method)
    {
        $this->assertMethod($method);
        $new = clone $this;
        $new->method = strtoupper($method);
        return $new;
    }

    public function getUri()
    {
        return $this->uri;
    }

    public function withUri(UriInterface $uri, $preserveHost = false)
    {
        if ($uri === $this->uri) {
            return $this;
        }

        $new = clone $this;
        $new->uri = $uri;

        if (!$preserveHost || !isset($this->headerNames['host'])) {
            $new->updateHostFromUri();
        }

        return $new;
    }

    private function updateHostFromUri()
    {
        $host = $this->uri->getHost();

        if ($host == '') {
            return;
        }

        if (($port = $this->uri->getPort()) !== null) {
            $host .= ':' . $port;
        }

        if (isset($this->headerNames['host'])) {
            $header = $this->headerNames['host'];
        } else {
            $header = 'Host';
            $this->headerNames['host'] = 'Host';
        }
        // Ensure Host is the first header.
        // See: http://tools.ietf.org/html/rfc7230#section-5.4
        $this->headers = [$header => [$host]] + $this->headers;
    }

    private function assertMethod($method)
    {
        if (!is_string($method) || $method === '') {
            throw new \InvalidArgumentException('Method must be a non-empty string.');
        }
    }
}
vendor/guzzlehttp/psr7/src/functions_include.php000064400000000243151327705700016175 0ustar00<?php

// Don't redefine the functions if included multiple times.
if (!function_exists('WPvividGuzzleHttp\Psr7\str')) {
    require __DIR__ . '/functions.php';
}
vendor/guzzlehttp/psr7/src/functions.php000064400000032204151327705700014474 0ustar00<?php

namespace WPvividGuzzleHttp\Psr7;

use WPvividPsr\Http\Message\MessageInterface;
use WPvividPsr\Http\Message\RequestInterface;
use WPvividPsr\Http\Message\StreamInterface;
use WPvividPsr\Http\Message\UriInterface;

/**
 * Returns the string representation of an HTTP message.
 *
 * @param MessageInterface $message Message to convert to a string.
 *
 * @return string
 *
 * @deprecated str will be removed in guzzlehttp/psr7:2.0. Use Message::toString instead.
 */
function str(MessageInterface $message)
{
    return Message::toString($message);
}

/**
 * Returns a UriInterface for the given value.
 *
 * This function accepts a string or UriInterface and returns a
 * UriInterface for the given value. If the value is already a
 * UriInterface, it is returned as-is.
 *
 * @param string|UriInterface $uri
 *
 * @return UriInterface
 *
 * @throws \InvalidArgumentException
 *
 * @deprecated uri_for will be removed in guzzlehttp/psr7:2.0. Use Utils::uriFor instead.
 */
function uri_for($uri)
{
    return Utils::uriFor($uri);
}

/**
 * Create a new stream based on the input type.
 *
 * Options is an associative array that can contain the following keys:
 * - metadata: Array of custom metadata.
 * - size: Size of the stream.
 *
 * This method accepts the following `$resource` types:
 * - `Psr\Http\Message\StreamInterface`: Returns the value as-is.
 * - `string`: Creates a stream object that uses the given string as the contents.
 * - `resource`: Creates a stream object that wraps the given PHP stream resource.
 * - `Iterator`: If the provided value implements `Iterator`, then a read-only
 *   stream object will be created that wraps the given iterable. Each time the
 *   stream is read from, data from the iterator will fill a buffer and will be
 *   continuously called until the buffer is equal to the requested read size.
 *   Subsequent read calls will first read from the buffer and then call `next`
 *   on the underlying iterator until it is exhausted.
 * - `object` with `__toString()`: If the object has the `__toString()` method,
 *   the object will be cast to a string and then a stream will be returned that
 *   uses the string value.
 * - `NULL`: When `null` is passed, an empty stream object is returned.
 * - `callable` When a callable is passed, a read-only stream object will be
 *   created that invokes the given callable. The callable is invoked with the
 *   number of suggested bytes to read. The callable can return any number of
 *   bytes, but MUST return `false` when there is no more data to return. The
 *   stream object that wraps the callable will invoke the callable until the
 *   number of requested bytes are available. Any additional bytes will be
 *   buffered and used in subsequent reads.
 *
 * @param resource|string|int|float|bool|StreamInterface|callable|\Iterator|null $resource Entity body data
 * @param array                                                                  $options  Additional options
 *
 * @return StreamInterface
 *
 * @throws \InvalidArgumentException if the $resource arg is not valid.
 *
 * @deprecated stream_for will be removed in guzzlehttp/psr7:2.0. Use Utils::streamFor instead.
 */
function stream_for($resource = '', array $options = [])
{
    return Utils::streamFor($resource, $options);
}

/**
 * Parse an array of header values containing ";" separated data into an
 * array of associative arrays representing the header key value pair data
 * of the header. When a parameter does not contain a value, but just
 * contains a key, this function will inject a key with a '' string value.
 *
 * @param string|array $header Header to parse into components.
 *
 * @return array Returns the parsed header values.
 *
 * @deprecated parse_header will be removed in guzzlehttp/psr7:2.0. Use Header::parse instead.
 */
function parse_header($header)
{
    return Header::parse($header);
}

/**
 * Converts an array of header values that may contain comma separated
 * headers into an array of headers with no comma separated values.
 *
 * @param string|array $header Header to normalize.
 *
 * @return array Returns the normalized header field values.
 *
 * @deprecated normalize_header will be removed in guzzlehttp/psr7:2.0. Use Header::normalize instead.
 */
function normalize_header($header)
{
    return Header::normalize($header);
}

/**
 * Clone and modify a request with the given changes.
 *
 * This method is useful for reducing the number of clones needed to mutate a
 * message.
 *
 * The changes can be one of:
 * - method: (string) Changes the HTTP method.
 * - set_headers: (array) Sets the given headers.
 * - remove_headers: (array) Remove the given headers.
 * - body: (mixed) Sets the given body.
 * - uri: (UriInterface) Set the URI.
 * - query: (string) Set the query string value of the URI.
 * - version: (string) Set the protocol version.
 *
 * @param RequestInterface $request Request to clone and modify.
 * @param array            $changes Changes to apply.
 *
 * @return RequestInterface
 *
 * @deprecated modify_request will be removed in guzzlehttp/psr7:2.0. Use Utils::modifyRequest instead.
 */
function modify_request(RequestInterface $request, array $changes)
{
    return Utils::modifyRequest($request, $changes);
}

/**
 * Attempts to rewind a message body and throws an exception on failure.
 *
 * The body of the message will only be rewound if a call to `tell()` returns a
 * value other than `0`.
 *
 * @param MessageInterface $message Message to rewind
 *
 * @throws \RuntimeException
 *
 * @deprecated rewind_body will be removed in guzzlehttp/psr7:2.0. Use Message::rewindBody instead.
 */
function rewind_body(MessageInterface $message)
{
    Message::rewindBody($message);
}

/**
 * Safely opens a PHP stream resource using a filename.
 *
 * When fopen fails, PHP normally raises a warning. This function adds an
 * error handler that checks for errors and throws an exception instead.
 *
 * @param string $filename File to open
 * @param string $mode     Mode used to open the file
 *
 * @return resource
 *
 * @throws \RuntimeException if the file cannot be opened
 *
 * @deprecated try_fopen will be removed in guzzlehttp/psr7:2.0. Use Utils::tryFopen instead.
 */
function try_fopen($filename, $mode)
{
    return Utils::tryFopen($filename, $mode);
}

/**
 * Copy the contents of a stream into a string until the given number of
 * bytes have been read.
 *
 * @param StreamInterface $stream Stream to read
 * @param int             $maxLen Maximum number of bytes to read. Pass -1
 *                                to read the entire stream.
 *
 * @return string
 *
 * @throws \RuntimeException on error.
 *
 * @deprecated copy_to_string will be removed in guzzlehttp/psr7:2.0. Use Utils::copyToString instead.
 */
function copy_to_string(StreamInterface $stream, $maxLen = -1)
{
    return Utils::copyToString($stream, $maxLen);
}

/**
 * Copy the contents of a stream into another stream until the given number
 * of bytes have been read.
 *
 * @param StreamInterface $source Stream to read from
 * @param StreamInterface $dest   Stream to write to
 * @param int             $maxLen Maximum number of bytes to read. Pass -1
 *                                to read the entire stream.
 *
 * @throws \RuntimeException on error.
 *
 * @deprecated copy_to_stream will be removed in guzzlehttp/psr7:2.0. Use Utils::copyToStream instead.
 */
function copy_to_stream(StreamInterface $source, StreamInterface $dest, $maxLen = -1)
{
    return Utils::copyToStream($source, $dest, $maxLen);
}

/**
 * Calculate a hash of a stream.
 *
 * This method reads the entire stream to calculate a rolling hash, based on
 * PHP's `hash_init` functions.
 *
 * @param StreamInterface $stream    Stream to calculate the hash for
 * @param string          $algo      Hash algorithm (e.g. md5, crc32, etc)
 * @param bool            $rawOutput Whether or not to use raw output
 *
 * @return string Returns the hash of the stream
 *
 * @throws \RuntimeException on error.
 *
 * @deprecated hash will be removed in guzzlehttp/psr7:2.0. Use Utils::hash instead.
 */
function hash(StreamInterface $stream, $algo, $rawOutput = false)
{
    return Utils::hash($stream, $algo, $rawOutput);
}

/**
 * Read a line from the stream up to the maximum allowed buffer length.
 *
 * @param StreamInterface $stream    Stream to read from
 * @param int|null        $maxLength Maximum buffer length
 *
 * @return string
 *
 * @deprecated readline will be removed in guzzlehttp/psr7:2.0. Use Utils::readLine instead.
 */
function readline(StreamInterface $stream, $maxLength = null)
{
    return Utils::readLine($stream, $maxLength);
}

/**
 * Parses a request message string into a request object.
 *
 * @param string $message Request message string.
 *
 * @return Request
 *
 * @deprecated parse_request will be removed in guzzlehttp/psr7:2.0. Use Message::parseRequest instead.
 */
function parse_request($message)
{
    return Message::parseRequest($message);
}

/**
 * Parses a response message string into a response object.
 *
 * @param string $message Response message string.
 *
 * @return Response
 *
 * @deprecated parse_response will be removed in guzzlehttp/psr7:2.0. Use Message::parseResponse instead.
 */
function parse_response($message)
{
    return Message::parseResponse($message);
}

/**
 * Parse a query string into an associative array.
 *
 * If multiple values are found for the same key, the value of that key value
 * pair will become an array. This function does not parse nested PHP style
 * arrays into an associative array (e.g., `foo[a]=1&foo[b]=2` will be parsed
 * into `['foo[a]' => '1', 'foo[b]' => '2'])`.
 *
 * @param string   $str         Query string to parse
 * @param int|bool $urlEncoding How the query string is encoded
 *
 * @return array
 *
 * @deprecated parse_query will be removed in guzzlehttp/psr7:2.0. Use Query::parse instead.
 */
function parse_query($str, $urlEncoding = true)
{
    return Query::parse($str, $urlEncoding);
}

/**
 * Build a query string from an array of key value pairs.
 *
 * This function can use the return value of `parse_query()` to build a query
 * string. This function does not modify the provided keys when an array is
 * encountered (like `http_build_query()` would).
 *
 * @param array     $params   Query string parameters.
 * @param int|false $encoding Set to false to not encode, PHP_QUERY_RFC3986
 *                            to encode using RFC3986, or PHP_QUERY_RFC1738
 *                            to encode using RFC1738.
 *
 * @return string
 *
 * @deprecated build_query will be removed in guzzlehttp/psr7:2.0. Use Query::build instead.
 */
function build_query(array $params, $encoding = PHP_QUERY_RFC3986)
{
    return Query::build($params, $encoding);
}

/**
 * Determines the mimetype of a file by looking at its extension.
 *
 * @param string $filename
 *
 * @return string|null
 *
 * @deprecated mimetype_from_filename will be removed in guzzlehttp/psr7:2.0. Use MimeType::fromFilename instead.
 */
function mimetype_from_filename($filename)
{
    return MimeType::fromFilename($filename);
}

/**
 * Maps a file extensions to a mimetype.
 *
 * @param $extension string The file extension.
 *
 * @return string|null
 *
 * @link http://svn.apache.org/repos/asf/httpd/httpd/branches/1.3.x/conf/mime.types
 * @deprecated mimetype_from_extension will be removed in guzzlehttp/psr7:2.0. Use MimeType::fromExtension instead.
 */
function mimetype_from_extension($extension)
{
    return MimeType::fromExtension($extension);
}

/**
 * Parses an HTTP message into an associative array.
 *
 * The array contains the "start-line" key containing the start line of
 * the message, "headers" key containing an associative array of header
 * array values, and a "body" key containing the body of the message.
 *
 * @param string $message HTTP request or response to parse.
 *
 * @return array
 *
 * @internal
 *
 * @deprecated _parse_message will be removed in guzzlehttp/psr7:2.0. Use Message::parseMessage instead.
 */
function _parse_message($message)
{
    return Message::parseMessage($message);
}

/**
 * Constructs a URI for an HTTP request message.
 *
 * @param string $path    Path from the start-line
 * @param array  $headers Array of headers (each value an array).
 *
 * @return string
 *
 * @internal
 *
 * @deprecated _parse_request_uri will be removed in guzzlehttp/psr7:2.0. Use Message::parseRequestUri instead.
 */
function _parse_request_uri($path, array $headers)
{
    return Message::parseRequestUri($path, $headers);
}

/**
 * Get a short summary of the message body.
 *
 * Will return `null` if the response is not printable.
 *
 * @param MessageInterface $message    The message to get the body summary
 * @param int              $truncateAt The maximum allowed size of the summary
 *
 * @return string|null
 *
 * @deprecated get_message_body_summary will be removed in guzzlehttp/psr7:2.0. Use Message::bodySummary instead.
 */
function get_message_body_summary(MessageInterface $message, $truncateAt = 120)
{
    return Message::bodySummary($message, $truncateAt);
}

/**
 * Remove the items given by the keys, case insensitively from the data.
 *
 * @param iterable<string> $keys
 *
 * @return array
 *
 * @internal
 *
 * @deprecated _caseless_remove will be removed in guzzlehttp/psr7:2.0. Use Utils::caselessRemove instead.
 */
function _caseless_remove($keys, array $data)
{
    return Utils::caselessRemove($keys, $data);
}
vendor/guzzlehttp/psr7/README.md000064400000066047151327705700012457 0ustar00# PSR-7 Message Implementation

This repository contains a full [PSR-7](http://www.php-fig.org/psr/psr-7/)
message implementation, several stream decorators, and some helpful
functionality like query string parsing.


[![Build Status](https://travis-ci.org/guzzle/psr7.svg?branch=master)](https://travis-ci.org/guzzle/psr7)


# Stream implementation

This package comes with a number of stream implementations and stream
decorators.


## AppendStream

`GuzzleHttp\Psr7\AppendStream`

Reads from multiple streams, one after the other.

```php
use GuzzleHttp\Psr7;

$a = Psr7\Utils::streamFor('abc, ');
$b = Psr7\Utils::streamFor('123.');
$composed = new Psr7\AppendStream([$a, $b]);

$composed->addStream(Psr7\Utils::streamFor(' Above all listen to me'));

echo $composed; // abc, 123. Above all listen to me.
```


## BufferStream

`GuzzleHttp\Psr7\BufferStream`

Provides a buffer stream that can be written to fill a buffer, and read
from to remove bytes from the buffer.

This stream returns a "hwm" metadata value that tells upstream consumers
what the configured high water mark of the stream is, or the maximum
preferred size of the buffer.

```php
use GuzzleHttp\Psr7;

// When more than 1024 bytes are in the buffer, it will begin returning
// false to writes. This is an indication that writers should slow down.
$buffer = new Psr7\BufferStream(1024);
```


## CachingStream

The CachingStream is used to allow seeking over previously read bytes on
non-seekable streams. This can be useful when transferring a non-seekable
entity body fails due to needing to rewind the stream (for example, resulting
from a redirect). Data that is read from the remote stream will be buffered in
a PHP temp stream so that previously read bytes are cached first in memory,
then on disk.

```php
use GuzzleHttp\Psr7;

$original = Psr7\Utils::streamFor(fopen('http://www.google.com', 'r'));
$stream = new Psr7\CachingStream($original);

$stream->read(1024);
echo $stream->tell();
// 1024

$stream->seek(0);
echo $stream->tell();
// 0
```


## DroppingStream

`GuzzleHttp\Psr7\DroppingStream`

Stream decorator that begins dropping data once the size of the underlying
stream becomes too full.

```php
use GuzzleHttp\Psr7;

// Create an empty stream
$stream = Psr7\Utils::streamFor();

// Start dropping data when the stream has more than 10 bytes
$dropping = new Psr7\DroppingStream($stream, 10);

$dropping->write('01234567890123456789');
echo $stream; // 0123456789
```


## FnStream

`GuzzleHttp\Psr7\FnStream`

Compose stream implementations based on a hash of functions.

Allows for easy testing and extension of a provided stream without needing
to create a concrete class for a simple extension point.

```php

use GuzzleHttp\Psr7;

$stream = Psr7\Utils::streamFor('hi');
$fnStream = Psr7\FnStream::decorate($stream, [
    'rewind' => function () use ($stream) {
        echo 'About to rewind - ';
        $stream->rewind();
        echo 'rewound!';
    }
]);

$fnStream->rewind();
// Outputs: About to rewind - rewound!
```


## InflateStream

`GuzzleHttp\Psr7\InflateStream`

Uses PHP's zlib.inflate filter to inflate deflate or gzipped content.

This stream decorator skips the first 10 bytes of the given stream to remove
the gzip header, converts the provided stream to a PHP stream resource,
then appends the zlib.inflate filter. The stream is then converted back
to a Guzzle stream resource to be used as a Guzzle stream.


## LazyOpenStream

`GuzzleHttp\Psr7\LazyOpenStream`

Lazily reads or writes to a file that is opened only after an IO operation
take place on the stream.

```php
use GuzzleHttp\Psr7;

$stream = new Psr7\LazyOpenStream('/path/to/file', 'r');
// The file has not yet been opened...

echo $stream->read(10);
// The file is opened and read from only when needed.
```


## LimitStream

`GuzzleHttp\Psr7\LimitStream`

LimitStream can be used to read a subset or slice of an existing stream object.
This can be useful for breaking a large file into smaller pieces to be sent in
chunks (e.g. Amazon S3's multipart upload API).

```php
use GuzzleHttp\Psr7;

$original = Psr7\Utils::streamFor(fopen('/tmp/test.txt', 'r+'));
echo $original->getSize();
// >>> 1048576

// Limit the size of the body to 1024 bytes and start reading from byte 2048
$stream = new Psr7\LimitStream($original, 1024, 2048);
echo $stream->getSize();
// >>> 1024
echo $stream->tell();
// >>> 0
```


## MultipartStream

`GuzzleHttp\Psr7\MultipartStream`

Stream that when read returns bytes for a streaming multipart or
multipart/form-data stream.


## NoSeekStream

`GuzzleHttp\Psr7\NoSeekStream`

NoSeekStream wraps a stream and does not allow seeking.

```php
use GuzzleHttp\Psr7;

$original = Psr7\Utils::streamFor('foo');
$noSeek = new Psr7\NoSeekStream($original);

echo $noSeek->read(3);
// foo
var_export($noSeek->isSeekable());
// false
$noSeek->seek(0);
var_export($noSeek->read(3));
// NULL
```


## PumpStream

`GuzzleHttp\Psr7\PumpStream`

Provides a read only stream that pumps data from a PHP callable.

When invoking the provided callable, the PumpStream will pass the amount of
data requested to read to the callable. The callable can choose to ignore
this value and return fewer or more bytes than requested. Any extra data
returned by the provided callable is buffered internally until drained using
the read() function of the PumpStream. The provided callable MUST return
false when there is no more data to read.


## Implementing stream decorators

Creating a stream decorator is very easy thanks to the
`GuzzleHttp\Psr7\StreamDecoratorTrait`. This trait provides methods that
implement `Psr\Http\Message\StreamInterface` by proxying to an underlying
stream. Just `use` the `StreamDecoratorTrait` and implement your custom
methods.

For example, let's say we wanted to call a specific function each time the last
byte is read from a stream. This could be implemented by overriding the
`read()` method.

```php
use Psr\Http\Message\StreamInterface;
use GuzzleHttp\Psr7\StreamDecoratorTrait;

class EofCallbackStream implements StreamInterface
{
    use StreamDecoratorTrait;

    private $callback;

    public function __construct(StreamInterface $stream, callable $cb)
    {
        $this->stream = $stream;
        $this->callback = $cb;
    }

    public function read($length)
    {
        $result = $this->stream->read($length);

        // Invoke the callback when EOF is hit.
        if ($this->eof()) {
            call_user_func($this->callback);
        }

        return $result;
    }
}
```

This decorator could be added to any existing stream and used like so:

```php
use GuzzleHttp\Psr7;

$original = Psr7\Utils::streamFor('foo');

$eofStream = new EofCallbackStream($original, function () {
    echo 'EOF!';
});

$eofStream->read(2);
$eofStream->read(1);
// echoes "EOF!"
$eofStream->seek(0);
$eofStream->read(3);
// echoes "EOF!"
```


## PHP StreamWrapper

You can use the `GuzzleHttp\Psr7\StreamWrapper` class if you need to use a
PSR-7 stream as a PHP stream resource.

Use the `GuzzleHttp\Psr7\StreamWrapper::getResource()` method to create a PHP
stream from a PSR-7 stream.

```php
use GuzzleHttp\Psr7\StreamWrapper;

$stream = GuzzleHttp\Psr7\Utils::streamFor('hello!');
$resource = StreamWrapper::getResource($stream);
echo fread($resource, 6); // outputs hello!
```


# Static API

There are various static methods available under the `GuzzleHttp\Psr7` namespace.


## `GuzzleHttp\Psr7\Message::toString`

`public static function toString(MessageInterface $message): string`

Returns the string representation of an HTTP message.

```php
$request = new GuzzleHttp\Psr7\Request('GET', 'http://example.com');
echo GuzzleHttp\Psr7\Message::toString($request);
```


## `GuzzleHttp\Psr7\Message::bodySummary`

`public static function bodySummary(MessageInterface $message, int $truncateAt = 120): string|null`

Get a short summary of the message body.

Will return `null` if the response is not printable.


## `GuzzleHttp\Psr7\Message::rewindBody`

`public static function rewindBody(MessageInterface $message): void`

Attempts to rewind a message body and throws an exception on failure.

The body of the message will only be rewound if a call to `tell()`
returns a value other than `0`.


## `GuzzleHttp\Psr7\Message::parseMessage`

`public static function parseMessage(string $message): array`

Parses an HTTP message into an associative array.

The array contains the "start-line" key containing the start line of
the message, "headers" key containing an associative array of header
array values, and a "body" key containing the body of the message.


## `GuzzleHttp\Psr7\Message::parseRequestUri`

`public static function parseRequestUri(string $path, array $headers): string`

Constructs a URI for an HTTP request message.


## `GuzzleHttp\Psr7\Message::parseRequest`

`public static function parseRequest(string $message): Request`

Parses a request message string into a request object.


## `GuzzleHttp\Psr7\Message::parseResponse`

`public static function parseResponse(string $message): Response`

Parses a response message string into a response object.


## `GuzzleHttp\Psr7\Header::parse`

`public static function parse(string|array $header): array`

Parse an array of header values containing ";" separated data into an
array of associative arrays representing the header key value pair data
of the header. When a parameter does not contain a value, but just
contains a key, this function will inject a key with a '' string value.


## `GuzzleHttp\Psr7\Header::normalize`

`public static function normalize(string|array $header): array`

Converts an array of header values that may contain comma separated
headers into an array of headers with no comma separated values.


## `GuzzleHttp\Psr7\Query::parse`

`public static function parse(string $str, int|bool $urlEncoding = true): array`

Parse a query string into an associative array.

If multiple values are found for the same key, the value of that key
value pair will become an array. This function does not parse nested
PHP style arrays into an associative array (e.g., `foo[a]=1&foo[b]=2`
will be parsed into `['foo[a]' => '1', 'foo[b]' => '2'])`.


## `GuzzleHttp\Psr7\Query::build`

`public static function build(array $params, int|false $encoding = PHP_QUERY_RFC3986): string`

Build a query string from an array of key value pairs.

This function can use the return value of `parse()` to build a query
string. This function does not modify the provided keys when an array is
encountered (like `http_build_query()` would).


## `GuzzleHttp\Psr7\Utils::caselessRemove`

`public static function caselessRemove(iterable<string> $keys, $keys, array $data): array`

Remove the items given by the keys, case insensitively from the data.


## `GuzzleHttp\Psr7\Utils::copyToStream`

`public static function copyToStream(StreamInterface $source, StreamInterface $dest, int $maxLen = -1): void`

Copy the contents of a stream into another stream until the given number
of bytes have been read.


## `GuzzleHttp\Psr7\Utils::copyToString`

`public static function copyToString(StreamInterface $stream, int $maxLen = -1): string`

Copy the contents of a stream into a string until the given number of
bytes have been read.


## `GuzzleHttp\Psr7\Utils::hash`

`public static function hash(StreamInterface $stream, string $algo, bool $rawOutput = false): string`

Calculate a hash of a stream.

This method reads the entire stream to calculate a rolling hash, based on
PHP's `hash_init` functions.


## `GuzzleHttp\Psr7\Utils::modifyRequest`

`public static function modifyRequest(RequestInterface $request, array $changes): RequestInterface`

Clone and modify a request with the given changes.

This method is useful for reducing the number of clones needed to mutate
a message.

- method: (string) Changes the HTTP method.
- set_headers: (array) Sets the given headers.
- remove_headers: (array) Remove the given headers.
- body: (mixed) Sets the given body.
- uri: (UriInterface) Set the URI.
- query: (string) Set the query string value of the URI.
- version: (string) Set the protocol version.


## `GuzzleHttp\Psr7\Utils::readLine`

`public static function readLine(StreamInterface $stream, int $maxLength = null): string`

Read a line from the stream up to the maximum allowed buffer length.


## `GuzzleHttp\Psr7\Utils::streamFor`

`public static function streamFor(resource|string|null|int|float|bool|StreamInterface|callable|\Iterator $resource = '', array $options = []): StreamInterface`

Create a new stream based on the input type.

Options is an associative array that can contain the following keys:

- metadata: Array of custom metadata.
- size: Size of the stream.

This method accepts the following `$resource` types:

- `Psr\Http\Message\StreamInterface`: Returns the value as-is.
- `string`: Creates a stream object that uses the given string as the contents.
- `resource`: Creates a stream object that wraps the given PHP stream resource.
- `Iterator`: If the provided value implements `Iterator`, then a read-only
  stream object will be created that wraps the given iterable. Each time the
  stream is read from, data from the iterator will fill a buffer and will be
  continuously called until the buffer is equal to the requested read size.
  Subsequent read calls will first read from the buffer and then call `next`
  on the underlying iterator until it is exhausted.
- `object` with `__toString()`: If the object has the `__toString()` method,
  the object will be cast to a string and then a stream will be returned that
  uses the string value.
- `NULL`: When `null` is passed, an empty stream object is returned.
- `callable` When a callable is passed, a read-only stream object will be
  created that invokes the given callable. The callable is invoked with the
  number of suggested bytes to read. The callable can return any number of
  bytes, but MUST return `false` when there is no more data to return. The
  stream object that wraps the callable will invoke the callable until the
  number of requested bytes are available. Any additional bytes will be
  buffered and used in subsequent reads.

```php
$stream = GuzzleHttp\Psr7\Utils::streamFor('foo');
$stream = GuzzleHttp\Psr7\Utils::streamFor(fopen('/path/to/file', 'r'));

$generator = function ($bytes) {
    for ($i = 0; $i < $bytes; $i++) {
        yield ' ';
    }
}

$stream = GuzzleHttp\Psr7\Utils::streamFor($generator(100));
```


## `GuzzleHttp\Psr7\Utils::tryFopen`

`public static function tryFopen(string $filename, string $mode): resource`

Safely opens a PHP stream resource using a filename.

When fopen fails, PHP normally raises a warning. This function adds an
error handler that checks for errors and throws an exception instead.


## `GuzzleHttp\Psr7\Utils::uriFor`

`public static function uriFor(string|UriInterface $uri): UriInterface`

Returns a UriInterface for the given value.

This function accepts a string or UriInterface and returns a
UriInterface for the given value. If the value is already a
UriInterface, it is returned as-is.


## `GuzzleHttp\Psr7\MimeType::fromFilename`

`public static function fromFilename(string $filename): string|null`

Determines the mimetype of a file by looking at its extension.


## `GuzzleHttp\Psr7\MimeType::fromExtension`

`public static function fromExtension(string $extension): string|null`

Maps a file extensions to a mimetype.


## Upgrading from Function API

The static API was first introduced in 1.7.0, in order to mitigate problems with functions conflicting between global and local copies of the package. The function API will be removed in 2.0.0. A migration table has been provided here for your convenience:

| Original Function | Replacement Method |
|----------------|----------------|
| `str` | `Message::toString` |
| `uri_for` | `Utils::uriFor` |
| `stream_for` | `Utils::streamFor` |
| `parse_header` | `Header::parse` |
| `normalize_header` | `Header::normalize` |
| `modify_request` | `Utils::modifyRequest` |
| `rewind_body` | `Message::rewindBody` |
| `try_fopen` | `Utils::tryFopen` |
| `copy_to_string` | `Utils::copyToString` |
| `copy_to_stream` | `Utils::copyToStream` |
| `hash` | `Utils::hash` |
| `readline` | `Utils::readLine` |
| `parse_request` | `Message::parseRequest` |
| `parse_response` | `Message::parseResponse` |
| `parse_query` | `Query::parse` |
| `build_query` | `Query::build` |
| `mimetype_from_filename` | `MimeType::fromFilename` |
| `mimetype_from_extension` | `MimeType::fromExtension` |
| `_parse_message` | `Message::parseMessage` |
| `_parse_request_uri` | `Message::parseRequestUri` |
| `get_message_body_summary` | `Message::bodySummary` |
| `_caseless_remove` | `Utils::caselessRemove` |


# Additional URI Methods

Aside from the standard `Psr\Http\Message\UriInterface` implementation in form of the `GuzzleHttp\Psr7\Uri` class,
this library also provides additional functionality when working with URIs as static methods.

## URI Types

An instance of `Psr\Http\Message\UriInterface` can either be an absolute URI or a relative reference.
An absolute URI has a scheme. A relative reference is used to express a URI relative to another URI,
the base URI. Relative references can be divided into several forms according to
[RFC 3986 Section 4.2](https://tools.ietf.org/html/rfc3986#section-4.2):

- network-path references, e.g. `//example.com/path`
- absolute-path references, e.g. `/path`
- relative-path references, e.g. `subpath`

The following methods can be used to identify the type of the URI.

### `GuzzleHttp\Psr7\Uri::isAbsolute`

`public static function isAbsolute(UriInterface $uri): bool`

Whether the URI is absolute, i.e. it has a scheme.

### `GuzzleHttp\Psr7\Uri::isNetworkPathReference`

`public static function isNetworkPathReference(UriInterface $uri): bool`

Whether the URI is a network-path reference. A relative reference that begins with two slash characters is
termed an network-path reference.

### `GuzzleHttp\Psr7\Uri::isAbsolutePathReference`

`public static function isAbsolutePathReference(UriInterface $uri): bool`

Whether the URI is a absolute-path reference. A relative reference that begins with a single slash character is
termed an absolute-path reference.

### `GuzzleHttp\Psr7\Uri::isRelativePathReference`

`public static function isRelativePathReference(UriInterface $uri): bool`

Whether the URI is a relative-path reference. A relative reference that does not begin with a slash character is
termed a relative-path reference.

### `GuzzleHttp\Psr7\Uri::isSameDocumentReference`

`public static function isSameDocumentReference(UriInterface $uri, UriInterface $base = null): bool`

Whether the URI is a same-document reference. A same-document reference refers to a URI that is, aside from its
fragment component, identical to the base URI. When no base URI is given, only an empty URI reference
(apart from its fragment) is considered a same-document reference.

## URI Components

Additional methods to work with URI components.

### `GuzzleHttp\Psr7\Uri::isDefaultPort`

`public static function isDefaultPort(UriInterface $uri): bool`

Whether the URI has the default port of the current scheme. `Psr\Http\Message\UriInterface::getPort` may return null
or the standard port. This method can be used independently of the implementation.

### `GuzzleHttp\Psr7\Uri::composeComponents`

`public static function composeComponents($scheme, $authority, $path, $query, $fragment): string`

Composes a URI reference string from its various components according to
[RFC 3986 Section 5.3](https://tools.ietf.org/html/rfc3986#section-5.3). Usually this method does not need to be called
manually but instead is used indirectly via `Psr\Http\Message\UriInterface::__toString`.

### `GuzzleHttp\Psr7\Uri::fromParts`

`public static function fromParts(array $parts): UriInterface`

Creates a URI from a hash of [`parse_url`](http://php.net/manual/en/function.parse-url.php) components.


### `GuzzleHttp\Psr7\Uri::withQueryValue`

`public static function withQueryValue(UriInterface $uri, $key, $value): UriInterface`

Creates a new URI with a specific query string value. Any existing query string values that exactly match the
provided key are removed and replaced with the given key value pair. A value of null will set the query string
key without a value, e.g. "key" instead of "key=value".

### `GuzzleHttp\Psr7\Uri::withQueryValues`

`public static function withQueryValues(UriInterface $uri, array $keyValueArray): UriInterface`

Creates a new URI with multiple query string values. It has the same behavior as `withQueryValue()` but for an
associative array of key => value.

### `GuzzleHttp\Psr7\Uri::withoutQueryValue`

`public static function withoutQueryValue(UriInterface $uri, $key): UriInterface`

Creates a new URI with a specific query string value removed. Any existing query string values that exactly match the
provided key are removed.

## Reference Resolution

`GuzzleHttp\Psr7\UriResolver` provides methods to resolve a URI reference in the context of a base URI according
to [RFC 3986 Section 5](https://tools.ietf.org/html/rfc3986#section-5). This is for example also what web browsers
do when resolving a link in a website based on the current request URI.

### `GuzzleHttp\Psr7\UriResolver::resolve`

`public static function resolve(UriInterface $base, UriInterface $rel): UriInterface`

Converts the relative URI into a new URI that is resolved against the base URI.

### `GuzzleHttp\Psr7\UriResolver::removeDotSegments`

`public static function removeDotSegments(string $path): string`

Removes dot segments from a path and returns the new path according to
[RFC 3986 Section 5.2.4](https://tools.ietf.org/html/rfc3986#section-5.2.4).

### `GuzzleHttp\Psr7\UriResolver::relativize`

`public static function relativize(UriInterface $base, UriInterface $target): UriInterface`

Returns the target URI as a relative reference from the base URI. This method is the counterpart to resolve():

```php
(string) $target === (string) UriResolver::resolve($base, UriResolver::relativize($base, $target))
```

One use-case is to use the current request URI as base URI and then generate relative links in your documents
to reduce the document size or offer self-contained downloadable document archives.

```php
$base = new Uri('http://example.com/a/b/');
echo UriResolver::relativize($base, new Uri('http://example.com/a/b/c'));  // prints 'c'.
echo UriResolver::relativize($base, new Uri('http://example.com/a/x/y'));  // prints '../x/y'.
echo UriResolver::relativize($base, new Uri('http://example.com/a/b/?q')); // prints '?q'.
echo UriResolver::relativize($base, new Uri('http://example.org/a/b/'));   // prints '//example.org/a/b/'.
```

## Normalization and Comparison

`GuzzleHttp\Psr7\UriNormalizer` provides methods to normalize and compare URIs according to
[RFC 3986 Section 6](https://tools.ietf.org/html/rfc3986#section-6).

### `GuzzleHttp\Psr7\UriNormalizer::normalize`

`public static function normalize(UriInterface $uri, $flags = self::PRESERVING_NORMALIZATIONS): UriInterface`

Returns a normalized URI. The scheme and host component are already normalized to lowercase per PSR-7 UriInterface.
This methods adds additional normalizations that can be configured with the `$flags` parameter which is a bitmask
of normalizations to apply. The following normalizations are available:

- `UriNormalizer::PRESERVING_NORMALIZATIONS`

    Default normalizations which only include the ones that preserve semantics.

- `UriNormalizer::CAPITALIZE_PERCENT_ENCODING`

    All letters within a percent-encoding triplet (e.g., "%3A") are case-insensitive, and should be capitalized.

    Example: `http://example.org/a%c2%b1b` → `http://example.org/a%C2%B1b`

- `UriNormalizer::DECODE_UNRESERVED_CHARACTERS`

    Decodes percent-encoded octets of unreserved characters. For consistency, percent-encoded octets in the ranges of
    ALPHA (%41–%5A and %61–%7A), DIGIT (%30–%39), hyphen (%2D), period (%2E), underscore (%5F), or tilde (%7E) should
    not be created by URI producers and, when found in a URI, should be decoded to their corresponding unreserved
    characters by URI normalizers.

    Example: `http://example.org/%7Eusern%61me/` → `http://example.org/~username/`

- `UriNormalizer::CONVERT_EMPTY_PATH`

    Converts the empty path to "/" for http and https URIs.

    Example: `http://example.org` → `http://example.org/`

- `UriNormalizer::REMOVE_DEFAULT_HOST`

    Removes the default host of the given URI scheme from the URI. Only the "file" scheme defines the default host
    "localhost". All of `file:/myfile`, `file:///myfile`, and `file://localhost/myfile` are equivalent according to
    RFC 3986.

    Example: `file://localhost/myfile` → `file:///myfile`

- `UriNormalizer::REMOVE_DEFAULT_PORT`

    Removes the default port of the given URI scheme from the URI.

    Example: `http://example.org:80/` → `http://example.org/`

- `UriNormalizer::REMOVE_DOT_SEGMENTS`

    Removes unnecessary dot-segments. Dot-segments in relative-path references are not removed as it would
    change the semantics of the URI reference.

    Example: `http://example.org/../a/b/../c/./d.html` → `http://example.org/a/c/d.html`

- `UriNormalizer::REMOVE_DUPLICATE_SLASHES`

    Paths which include two or more adjacent slashes are converted to one. Webservers usually ignore duplicate slashes
    and treat those URIs equivalent. But in theory those URIs do not need to be equivalent. So this normalization
    may change the semantics. Encoded slashes (%2F) are not removed.

    Example: `http://example.org//foo///bar.html` → `http://example.org/foo/bar.html`

- `UriNormalizer::SORT_QUERY_PARAMETERS`

    Sort query parameters with their values in alphabetical order. However, the order of parameters in a URI may be
    significant (this is not defined by the standard). So this normalization is not safe and may change the semantics
    of the URI.

    Example: `?lang=en&article=fred` → `?article=fred&lang=en`

### `GuzzleHttp\Psr7\UriNormalizer::isEquivalent`

`public static function isEquivalent(UriInterface $uri1, UriInterface $uri2, $normalizations = self::PRESERVING_NORMALIZATIONS): bool`

Whether two URIs can be considered equivalent. Both URIs are normalized automatically before comparison with the given
`$normalizations` bitmask. The method also accepts relative URI references and returns true when they are equivalent.
This of course assumes they will be resolved against the same base URI. If this is not the case, determination of
equivalence or difference of relative references does not mean anything.


## Security

If you discover a security vulnerability within this package, please send an email to security@tidelift.com. All security vulnerabilities will be promptly addressed. Please do not disclose security-related issues publicly until a fix has been announced. Please see [Security Policy](https://github.com/guzzle/psr7/security/policy) for more information.

## License

Guzzle is made available under the MIT License (MIT). Please see [License File](LICENSE) for more information.

## For Enterprise

Available as part of the Tidelift Subscription

The maintainers of Guzzle and thousands of other packages are working with Tidelift to deliver commercial support and maintenance for the open source dependencies you use to build your applications. Save time, reduce risk, and improve code health, while paying the maintainers of the exact dependencies you use. [Learn more.](https://tidelift.com/subscription/pkg/packagist-guzzlehttp-psr7?utm_source=packagist-guzzlehttp-psr7&utm_medium=referral&utm_campaign=enterprise&utm_term=repo)
vendor/guzzlehttp/psr7/composer.json000064400000004251151327705700013707 0ustar00{
    "name": "guzzlehttp/psr7",
    "description": "PSR-7 message implementation that also provides common utility methods",
    "keywords": ["request", "response", "message", "stream", "http", "uri", "url", "psr-7"],
    "license": "MIT",
    "authors": [
        {
            "name": "Graham Campbell",
            "email": "hello@gjcampbell.co.uk",
            "homepage": "https://github.com/GrahamCampbell"
        },
        {
            "name": "Michael Dowling",
            "email": "mtdowling@gmail.com",
            "homepage": "https://github.com/mtdowling"
        },
        {
            "name": "George Mponos",
            "email": "gmponos@gmail.com",
            "homepage": "https://github.com/gmponos"
        },
        {
            "name": "Tobias Nyholm",
            "email": "tobias.nyholm@gmail.com",
            "homepage": "https://github.com/Nyholm"
        },
        {
            "name": "Márk Sági-Kazár",
            "email": "mark.sagikazar@gmail.com",
            "homepage": "https://github.com/sagikazarmark"
        },
        {
            "name": "Tobias Schultze",
            "email": "webmaster@tubo-world.de",
            "homepage": "https://github.com/Tobion"
        }
    ],
    "require": {
        "php": ">=5.4.0",
        "psr/http-message": "~1.0",
        "ralouphie/getallheaders": "^2.0.5 || ^3.0.0"
    },
    "require-dev": {
        "phpunit/phpunit": "~4.8.36 || ^5.7.27 || ^6.5.14 || ^7.5.20 || ^8.5.8 || ^9.3.10",
        "ext-zlib": "*"
    },
    "provide": {
        "psr/http-message-implementation": "1.0"
    },
    "suggest": {
        "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses"
    },
    "autoload": {
        "psr-4": {
            "WPvividGuzzleHttp\\Psr7\\": "src/"
        },
        "files": ["src/functions_include.php"]
    },
    "autoload-dev": {
        "psr-4": {
            "WPvividGuzzleHttp\\Tests\\Psr7\\": "tests/"
        }
    },
    "extra": {
        "branch-alias": {
            "dev-master": "1.7-dev"
        }
    },
    "config": {
        "preferred-install": "dist",
        "sort-packages": true,
        "allow-plugins": {
            "bamarni/composer-bin-plugin": true
        }
    }
}
vendor/guzzlehttp/promises/src/functions.php000064400000027435151327705700015454 0ustar00<?php
namespace WPvividGuzzleHttp\Promise;

/**
 * Get the global task queue used for promise resolution.
 *
 * This task queue MUST be run in an event loop in order for promises to be
 * settled asynchronously. It will be automatically run when synchronously
 * waiting on a promise.
 *
 * <code>
 * while ($eventLoop->isRunning()) {
 *     GuzzleHttp\Promise\queue()->run();
 * }
 * </code>
 *
 * @param TaskQueueInterface $assign Optionally specify a new queue instance.
 *
 * @return TaskQueueInterface
 */
function queue(TaskQueueInterface $assign = null)
{
    static $queue;

    if ($assign) {
        $queue = $assign;
    } elseif (!$queue) {
        $queue = new TaskQueue();
    }

    return $queue;
}

/**
 * Adds a function to run in the task queue when it is next `run()` and returns
 * a promise that is fulfilled or rejected with the result.
 *
 * @param callable $task Task function to run.
 *
 * @return PromiseInterface
 */
function task(callable $task)
{
    $queue = queue();
    $promise = new Promise([$queue, 'run']);
    $queue->add(function () use ($task, $promise) {
        try {
            $promise->resolve($task());
        } catch (\Throwable $e) {
            $promise->reject($e);
        } catch (\Exception $e) {
            $promise->reject($e);
        }
    });

    return $promise;
}

/**
 * Creates a promise for a value if the value is not a promise.
 *
 * @param mixed $value Promise or value.
 *
 * @return PromiseInterface
 */
function promise_for($value)
{
    if ($value instanceof PromiseInterface) {
        return $value;
    }

    // Return a Guzzle promise that shadows the given promise.
    if (method_exists($value, 'then')) {
        $wfn = method_exists($value, 'wait') ? [$value, 'wait'] : null;
        $cfn = method_exists($value, 'cancel') ? [$value, 'cancel'] : null;
        $promise = new Promise($wfn, $cfn);
        $value->then([$promise, 'resolve'], [$promise, 'reject']);
        return $promise;
    }

    return new FulfilledPromise($value);
}

/**
 * Creates a rejected promise for a reason if the reason is not a promise. If
 * the provided reason is a promise, then it is returned as-is.
 *
 * @param mixed $reason Promise or reason.
 *
 * @return PromiseInterface
 */
function rejection_for($reason)
{
    if ($reason instanceof PromiseInterface) {
        return $reason;
    }

    return new RejectedPromise($reason);
}

/**
 * Create an exception for a rejected promise value.
 *
 * @param mixed $reason
 *
 * @return \Exception|\Throwable
 */
function exception_for($reason)
{
    return $reason instanceof \Exception || $reason instanceof \Throwable
        ? $reason
        : new RejectionException($reason);
}

/**
 * Returns an iterator for the given value.
 *
 * @param mixed $value
 *
 * @return \Iterator
 */
function iter_for($value)
{
    if ($value instanceof \Iterator) {
        return $value;
    } elseif (is_array($value)) {
        return new \ArrayIterator($value);
    } else {
        return new \ArrayIterator([$value]);
    }
}

/**
 * Synchronously waits on a promise to resolve and returns an inspection state
 * array.
 *
 * Returns a state associative array containing a "state" key mapping to a
 * valid promise state. If the state of the promise is "fulfilled", the array
 * will contain a "value" key mapping to the fulfilled value of the promise. If
 * the promise is rejected, the array will contain a "reason" key mapping to
 * the rejection reason of the promise.
 *
 * @param PromiseInterface $promise Promise or value.
 *
 * @return array
 */
function inspect(PromiseInterface $promise)
{
    try {
        return [
            'state' => PromiseInterface::FULFILLED,
            'value' => $promise->wait()
        ];
    } catch (RejectionException $e) {
        return ['state' => PromiseInterface::REJECTED, 'reason' => $e->getReason()];
    } catch (\Throwable $e) {
        return ['state' => PromiseInterface::REJECTED, 'reason' => $e];
    } catch (\Exception $e) {
        return ['state' => PromiseInterface::REJECTED, 'reason' => $e];
    }
}

/**
 * Waits on all of the provided promises, but does not unwrap rejected promises
 * as thrown exception.
 *
 * Returns an array of inspection state arrays.
 *
 * @param PromiseInterface[] $promises Traversable of promises to wait upon.
 *
 * @return array
 * @see GuzzleHttp\Promise\inspect for the inspection state array format.
 */
function inspect_all($promises)
{
    $results = [];
    foreach ($promises as $key => $promise) {
        $results[$key] = inspect($promise);
    }

    return $results;
}

/**
 * Waits on all of the provided promises and returns the fulfilled values.
 *
 * Returns an array that contains the value of each promise (in the same order
 * the promises were provided). An exception is thrown if any of the promises
 * are rejected.
 *
 * @param mixed $promises Iterable of PromiseInterface objects to wait on.
 *
 * @return array
 * @throws \Exception on error
 * @throws \Throwable on error in PHP >=7
 */
function unwrap($promises)
{
    $results = [];
    foreach ($promises as $key => $promise) {
        $results[$key] = $promise->wait();
    }

    return $results;
}

/**
 * Given an array of promises, return a promise that is fulfilled when all the
 * items in the array are fulfilled.
 *
 * The promise's fulfillment value is an array with fulfillment values at
 * respective positions to the original array. If any promise in the array
 * rejects, the returned promise is rejected with the rejection reason.
 *
 * @param mixed $promises Promises or values.
 *
 * @return PromiseInterface
 */
function all($promises)
{
    $results = [];
    return each(
        $promises,
        function ($value, $idx) use (&$results) {
            $results[$idx] = $value;
        },
        function ($reason, $idx, Promise $aggregate) {
            $aggregate->reject($reason);
        }
    )->then(function () use (&$results) {
        ksort($results);
        return $results;
    });
}

/**
 * Initiate a competitive race between multiple promises or values (values will
 * become immediately fulfilled promises).
 *
 * When count amount of promises have been fulfilled, the returned promise is
 * fulfilled with an array that contains the fulfillment values of the winners
 * in order of resolution.
 *
 * This prommise is rejected with a {@see GuzzleHttp\Promise\AggregateException}
 * if the number of fulfilled promises is less than the desired $count.
 *
 * @param int   $count    Total number of promises.
 * @param mixed $promises Promises or values.
 *
 * @return PromiseInterface
 */
function some($count, $promises)
{
    $results = [];
    $rejections = [];

    return each(
        $promises,
        function ($value, $idx, PromiseInterface $p) use (&$results, $count) {
            if ($p->getState() !== PromiseInterface::PENDING) {
                return;
            }
            $results[$idx] = $value;
            if (count($results) >= $count) {
                $p->resolve(null);
            }
        },
        function ($reason) use (&$rejections) {
            $rejections[] = $reason;
        }
    )->then(
        function () use (&$results, &$rejections, $count) {
            if (count($results) !== $count) {
                throw new AggregateException(
                    'Not enough promises to fulfill count',
                    $rejections
                );
            }
            ksort($results);
            return array_values($results);
        }
    );
}

/**
 * Like some(), with 1 as count. However, if the promise fulfills, the
 * fulfillment value is not an array of 1 but the value directly.
 *
 * @param mixed $promises Promises or values.
 *
 * @return PromiseInterface
 */
function any($promises)
{
    return some(1, $promises)->then(function ($values) { return $values[0]; });
}

/**
 * Returns a promise that is fulfilled when all of the provided promises have
 * been fulfilled or rejected.
 *
 * The returned promise is fulfilled with an array of inspection state arrays.
 *
 * @param mixed $promises Promises or values.
 *
 * @return PromiseInterface
 * @see GuzzleHttp\Promise\inspect for the inspection state array format.
 */
function settle($promises)
{
    $results = [];

    return each(
        $promises,
        function ($value, $idx) use (&$results) {
            $results[$idx] = ['state' => PromiseInterface::FULFILLED, 'value' => $value];
        },
        function ($reason, $idx) use (&$results) {
            $results[$idx] = ['state' => PromiseInterface::REJECTED, 'reason' => $reason];
        }
    )->then(function () use (&$results) {
        ksort($results);
        return $results;
    });
}

/**
 * Given an iterator that yields promises or values, returns a promise that is
 * fulfilled with a null value when the iterator has been consumed or the
 * aggregate promise has been fulfilled or rejected.
 *
 * $onFulfilled is a function that accepts the fulfilled value, iterator
 * index, and the aggregate promise. The callback can invoke any necessary side
 * effects and choose to resolve or reject the aggregate promise if needed.
 *
 * $onRejected is a function that accepts the rejection reason, iterator
 * index, and the aggregate promise. The callback can invoke any necessary side
 * effects and choose to resolve or reject the aggregate promise if needed.
 *
 * @param mixed    $iterable    Iterator or array to iterate over.
 * @param callable $onFulfilled
 * @param callable $onRejected
 *
 * @return PromiseInterface
 */
function each(
    $iterable,
    callable $onFulfilled = null,
    callable $onRejected = null
) {
    return (new EachPromise($iterable, [
        'fulfilled' => $onFulfilled,
        'rejected'  => $onRejected
    ]))->promise();
}

/**
 * Like each, but only allows a certain number of outstanding promises at any
 * given time.
 *
 * $concurrency may be an integer or a function that accepts the number of
 * pending promises and returns a numeric concurrency limit value to allow for
 * dynamic a concurrency size.
 *
 * @param mixed        $iterable
 * @param int|callable $concurrency
 * @param callable     $onFulfilled
 * @param callable     $onRejected
 *
 * @return PromiseInterface
 */
function each_limit(
    $iterable,
    $concurrency,
    callable $onFulfilled = null,
    callable $onRejected = null
) {
    return (new EachPromise($iterable, [
        'fulfilled'   => $onFulfilled,
        'rejected'    => $onRejected,
        'concurrency' => $concurrency
    ]))->promise();
}

/**
 * Like each_limit, but ensures that no promise in the given $iterable argument
 * is rejected. If any promise is rejected, then the aggregate promise is
 * rejected with the encountered rejection.
 *
 * @param mixed        $iterable
 * @param int|callable $concurrency
 * @param callable     $onFulfilled
 *
 * @return PromiseInterface
 */
function each_limit_all(
    $iterable,
    $concurrency,
    callable $onFulfilled = null
) {
    return each_limit(
        $iterable,
        $concurrency,
        $onFulfilled,
        function ($reason, $idx, PromiseInterface $aggregate) {
            $aggregate->reject($reason);
        }
    );
}

/**
 * Returns true if a promise is fulfilled.
 *
 * @param PromiseInterface $promise
 *
 * @return bool
 */
function is_fulfilled(PromiseInterface $promise)
{
    return $promise->getState() === PromiseInterface::FULFILLED;
}

/**
 * Returns true if a promise is rejected.
 *
 * @param PromiseInterface $promise
 *
 * @return bool
 */
function is_rejected(PromiseInterface $promise)
{
    return $promise->getState() === PromiseInterface::REJECTED;
}

/**
 * Returns true if a promise is fulfilled or rejected.
 *
 * @param PromiseInterface $promise
 *
 * @return bool
 */
function is_settled(PromiseInterface $promise)
{
    return $promise->getState() !== PromiseInterface::PENDING;
}

/**
 * @see Coroutine
 *
 * @param callable $generatorFn
 *
 * @return PromiseInterface
 */
function coroutine(callable $generatorFn)
{
    return new Coroutine($generatorFn);
}
vendor/guzzlehttp/promises/src/CancellationException.php000064400000000275151327705700017710 0ustar00<?php
namespace WPvividGuzzleHttp\Promise;

/**
 * Exception that is set as the reason for a promise that has been cancelled.
 */
class CancellationException extends RejectionException
{
}
vendor/guzzlehttp/promises/src/functions_include.php000064400000000256151327705700017147 0ustar00<?php

// Don't redefine the functions if included multiple times.
if (!function_exists('WPvividGuzzleHttp\Promise\promise_for')) {
    require __DIR__ . '/functions.php';
}
vendor/guzzlehttp/promises/src/Promise.php000064400000021122151327705700015045 0ustar00<?php
namespace WPvividGuzzleHttp\Promise;

/**
 * Promises/A+ implementation that avoids recursion when possible.
 *
 * @link https://promisesaplus.com/
 */
class Promise implements PromiseInterface
{
    private $state = self::PENDING;
    private $result;
    private $cancelFn;
    private $waitFn;
    private $waitList;
    private $handlers = [];

    /**
     * @param callable $waitFn   Fn that when invoked resolves the promise.
     * @param callable $cancelFn Fn that when invoked cancels the promise.
     */
    public function __construct(
        callable $waitFn = null,
        callable $cancelFn = null
    ) {
        $this->waitFn = $waitFn;
        $this->cancelFn = $cancelFn;
    }

    public function then(
        callable $onFulfilled = null,
        callable $onRejected = null
    ) {
        if ($this->state === self::PENDING) {
            $p = new Promise(null, [$this, 'cancel']);
            $this->handlers[] = [$p, $onFulfilled, $onRejected];
            $p->waitList = $this->waitList;
            $p->waitList[] = $this;
            return $p;
        }

        // Return a fulfilled promise and immediately invoke any callbacks.
        if ($this->state === self::FULFILLED) {
            return $onFulfilled
                ? promise_for($this->result)->then($onFulfilled)
                : promise_for($this->result);
        }

        // It's either cancelled or rejected, so return a rejected promise
        // and immediately invoke any callbacks.
        $rejection = rejection_for($this->result);
        return $onRejected ? $rejection->then(null, $onRejected) : $rejection;
    }

    public function otherwise(callable $onRejected)
    {
        return $this->then(null, $onRejected);
    }

    public function wait($unwrap = true)
    {
        $this->waitIfPending();

        $inner = $this->result instanceof PromiseInterface
            ? $this->result->wait($unwrap)
            : $this->result;

        if ($unwrap) {
            if ($this->result instanceof PromiseInterface
                || $this->state === self::FULFILLED
            ) {
                return $inner;
            } else {
                // It's rejected so "unwrap" and throw an exception.
                throw exception_for($inner);
            }
        }
    }

    public function getState()
    {
        return $this->state;
    }

    public function cancel()
    {
        if ($this->state !== self::PENDING) {
            return;
        }

        $this->waitFn = $this->waitList = null;

        if ($this->cancelFn) {
            $fn = $this->cancelFn;
            $this->cancelFn = null;
            try {
                $fn();
            } catch (\Throwable $e) {
                $this->reject($e);
            } catch (\Exception $e) {
                $this->reject($e);
            }
        }

        // Reject the promise only if it wasn't rejected in a then callback.
        if ($this->state === self::PENDING) {
            $this->reject(new CancellationException('Promise has been cancelled'));
        }
    }

    public function resolve($value)
    {
        $this->settle(self::FULFILLED, $value);
    }

    public function reject($reason)
    {
        $this->settle(self::REJECTED, $reason);
    }

    private function settle($state, $value)
    {
        if ($this->state !== self::PENDING) {
            // Ignore calls with the same resolution.
            if ($state === $this->state && $value === $this->result) {
                return;
            }
            throw $this->state === $state
                ? new \LogicException("The promise is already {$state}.")
                : new \LogicException("Cannot change a {$this->state} promise to {$state}");
        }

        if ($value === $this) {
            throw new \LogicException('Cannot fulfill or reject a promise with itself');
        }

        // Clear out the state of the promise but stash the handlers.
        $this->state = $state;
        $this->result = $value;
        $handlers = $this->handlers;
        $this->handlers = null;
        $this->waitList = $this->waitFn = null;
        $this->cancelFn = null;

        if (!$handlers) {
            return;
        }

        // If the value was not a settled promise or a thenable, then resolve
        // it in the task queue using the correct ID.
        if (!method_exists($value, 'then')) {
            $id = $state === self::FULFILLED ? 1 : 2;
            // It's a success, so resolve the handlers in the queue.
            queue()->add(static function () use ($id, $value, $handlers) {
                foreach ($handlers as $handler) {
                    self::callHandler($id, $value, $handler);
                }
            });
        } elseif ($value instanceof Promise
            && $value->getState() === self::PENDING
        ) {
            // We can just merge our handlers onto the next promise.
            $value->handlers = array_merge($value->handlers, $handlers);
        } else {
            // Resolve the handlers when the forwarded promise is resolved.
            $value->then(
                static function ($value) use ($handlers) {
                    foreach ($handlers as $handler) {
                        self::callHandler(1, $value, $handler);
                    }
                },
                static function ($reason) use ($handlers) {
                    foreach ($handlers as $handler) {
                        self::callHandler(2, $reason, $handler);
                    }
                }
            );
        }
    }

    /**
     * Call a stack of handlers using a specific callback index and value.
     *
     * @param int   $index   1 (resolve) or 2 (reject).
     * @param mixed $value   Value to pass to the callback.
     * @param array $handler Array of handler data (promise and callbacks).
     *
     * @return array Returns the next group to resolve.
     */
    private static function callHandler($index, $value, array $handler)
    {
        /** @var PromiseInterface $promise */
        $promise = $handler[0];

        // The promise may have been cancelled or resolved before placing
        // this thunk in the queue.
        if ($promise->getState() !== self::PENDING) {
            return;
        }

        try {
            if (isset($handler[$index])) {
                $promise->resolve($handler[$index]($value));
            } elseif ($index === 1) {
                // Forward resolution values as-is.
                $promise->resolve($value);
            } else {
                // Forward rejections down the chain.
                $promise->reject($value);
            }
        } catch (\Throwable $reason) {
            $promise->reject($reason);
        } catch (\Exception $reason) {
            $promise->reject($reason);
        }
    }

    private function waitIfPending()
    {
        if ($this->state !== self::PENDING) {
            return;
        } elseif ($this->waitFn) {
            $this->invokeWaitFn();
        } elseif ($this->waitList) {
            $this->invokeWaitList();
        } else {
            // If there's not wait function, then reject the promise.
            $this->reject('Cannot wait on a promise that has '
                . 'no internal wait function. You must provide a wait '
                . 'function when constructing the promise to be able to '
                . 'wait on a promise.');
        }

        queue()->run();

        if ($this->state === self::PENDING) {
            $this->reject('Invoking the wait callback did not resolve the promise');
        }
    }

    private function invokeWaitFn()
    {
        try {
            $wfn = $this->waitFn;
            $this->waitFn = null;
            $wfn(true);
        } catch (\Exception $reason) {
            if ($this->state === self::PENDING) {
                // The promise has not been resolved yet, so reject the promise
                // with the exception.
                $this->reject($reason);
            } else {
                // The promise was already resolved, so there's a problem in
                // the application.
                throw $reason;
            }
        }
    }

    private function invokeWaitList()
    {
        $waitList = $this->waitList;
        $this->waitList = null;

        foreach ($waitList as $result) {
            while (true) {
                $result->waitIfPending();

                if ($result->result instanceof Promise) {
                    $result = $result->result;
                } else {
                    if ($result->result instanceof PromiseInterface) {
                        $result->result->wait(false);
                    }
                    break;
                }
            }
        }
    }
}
vendor/guzzlehttp/promises/src/Coroutine.php000064400000007551151327705700015410 0ustar00<?php
namespace WPvividGuzzleHttp\Promise;

use Exception;
use Generator;
use Throwable;

/**
 * Creates a promise that is resolved using a generator that yields values or
 * promises (somewhat similar to C#'s async keyword).
 *
 * When called, the coroutine function will start an instance of the generator
 * and returns a promise that is fulfilled with its final yielded value.
 *
 * Control is returned back to the generator when the yielded promise settles.
 * This can lead to less verbose code when doing lots of sequential async calls
 * with minimal processing in between.
 *
 *     use GuzzleHttp\Promise;
 *
 *     function createPromise($value) {
 *         return new Promise\FulfilledPromise($value);
 *     }
 *
 *     $promise = Promise\coroutine(function () {
 *         $value = (yield createPromise('a'));
 *         try {
 *             $value = (yield createPromise($value . 'b'));
 *         } catch (\Exception $e) {
 *             // The promise was rejected.
 *         }
 *         yield $value . 'c';
 *     });
 *
 *     // Outputs "abc"
 *     $promise->then(function ($v) { echo $v; });
 *
 * @param callable $generatorFn Generator function to wrap into a promise.
 *
 * @return Promise
 * @link https://github.com/petkaantonov/bluebird/blob/master/API.md#generators inspiration
 */
final class Coroutine implements PromiseInterface
{
    /**
     * @var PromiseInterface|null
     */
    private $currentPromise;

    /**
     * @var Generator
     */
    private $generator;

    /**
     * @var Promise
     */
    private $result;

    public function __construct(callable $generatorFn)
    {
        $this->generator = $generatorFn();
        $this->result = new Promise(function () {
            while (isset($this->currentPromise)) {
                $this->currentPromise->wait();
            }
        });
        $this->nextCoroutine($this->generator->current());
    }

    public function then(
        callable $onFulfilled = null,
        callable $onRejected = null
    ) {
        return $this->result->then($onFulfilled, $onRejected);
    }

    public function otherwise(callable $onRejected)
    {
        return $this->result->otherwise($onRejected);
    }

    public function wait($unwrap = true)
    {
        return $this->result->wait($unwrap);
    }

    public function getState()
    {
        return $this->result->getState();
    }

    public function resolve($value)
    {
        $this->result->resolve($value);
    }

    public function reject($reason)
    {
        $this->result->reject($reason);
    }

    public function cancel()
    {
        $this->currentPromise->cancel();
        $this->result->cancel();
    }

    private function nextCoroutine($yielded)
    {
        $this->currentPromise = promise_for($yielded)
            ->then([$this, '_handleSuccess'], [$this, '_handleFailure']);
    }

    /**
     * @internal
     */
    public function _handleSuccess($value)
    {
        unset($this->currentPromise);
        try {
            $next = $this->generator->send($value);
            if ($this->generator->valid()) {
                $this->nextCoroutine($next);
            } else {
                $this->result->resolve($value);
            }
        } catch (Exception $exception) {
            $this->result->reject($exception);
        } catch (Throwable $throwable) {
            $this->result->reject($throwable);
        }
    }

    /**
     * @internal
     */
    public function _handleFailure($reason)
    {
        unset($this->currentPromise);
        try {
            $nextYield = $this->generator->throw(exception_for($reason));
            // The throw was caught, so keep iterating on the coroutine
            $this->nextCoroutine($nextYield);
        } catch (Exception $exception) {
            $this->result->reject($exception);
        } catch (Throwable $throwable) {
            $this->result->reject($throwable);
        }
    }
}
vendor/guzzlehttp/promises/src/TaskQueue.php000064400000003617151327705700015347 0ustar00<?php
namespace WPvividGuzzleHttp\Promise;

/**
 * A task queue that executes tasks in a FIFO order.
 *
 * This task queue class is used to settle promises asynchronously and
 * maintains a constant stack size. You can use the task queue asynchronously
 * by calling the `run()` function of the global task queue in an event loop.
 *
 *     GuzzleHttp\Promise\queue()->run();
 */
class TaskQueue implements TaskQueueInterface
{
    private $enableShutdown = true;
    private $queue = [];

    public function __construct($withShutdown = true)
    {
        if ($withShutdown) {
            register_shutdown_function(function () {
                if ($this->enableShutdown) {
                    // Only run the tasks if an E_ERROR didn't occur.
                    $err = error_get_last();
                    if (!$err || ($err['type'] ^ E_ERROR)) {
                        $this->run();
                    }
                }
            });
        }
    }

    public function isEmpty()
    {
        return !$this->queue;
    }

    public function add(callable $task)
    {
        $this->queue[] = $task;
    }

    public function run()
    {
        /** @var callable $task */
        while ($task = array_shift($this->queue)) {
            $task();
        }
    }

    /**
     * The task queue will be run and exhausted by default when the process
     * exits IFF the exit is not the result of a PHP E_ERROR error.
     *
     * You can disable running the automatic shutdown of the queue by calling
     * this function. If you disable the task queue shutdown process, then you
     * MUST either run the task queue (as a result of running your event loop
     * or manually using the run() method) or wait on each outstanding promise.
     *
     * Note: This shutdown will occur before any destructors are triggered.
     */
    public function disableShutdown()
    {
        $this->enableShutdown = false;
    }
}
vendor/guzzlehttp/promises/src/PromisorInterface.php000064400000000372151327705700017066 0ustar00<?php
namespace WPvividGuzzleHttp\Promise;

/**
 * Interface used with classes that return a promise.
 */
interface PromisorInterface
{
    /**
     * Returns a promise.
     *
     * @return PromiseInterface
     */
    public function promise();
}
vendor/guzzlehttp/promises/src/PromiseInterface.php000064400000005425151327705700016676 0ustar00<?php
namespace WPvividGuzzleHttp\Promise;

/**
 * A promise represents the eventual result of an asynchronous operation.
 *
 * The primary way of interacting with a promise is through its then method,
 * which registers callbacks to receive either a promise’s eventual value or
 * the reason why the promise cannot be fulfilled.
 *
 * @link https://promisesaplus.com/
 */
interface PromiseInterface
{
    const PENDING = 'pending';
    const FULFILLED = 'fulfilled';
    const REJECTED = 'rejected';

    /**
     * Appends fulfillment and rejection handlers to the promise, and returns
     * a new promise resolving to the return value of the called handler.
     *
     * @param callable $onFulfilled Invoked when the promise fulfills.
     * @param callable $onRejected  Invoked when the promise is rejected.
     *
     * @return PromiseInterface
     */
    public function then(
        callable $onFulfilled = null,
        callable $onRejected = null
    );

    /**
     * Appends a rejection handler callback to the promise, and returns a new
     * promise resolving to the return value of the callback if it is called,
     * or to its original fulfillment value if the promise is instead
     * fulfilled.
     *
     * @param callable $onRejected Invoked when the promise is rejected.
     *
     * @return PromiseInterface
     */
    public function otherwise(callable $onRejected);

    /**
     * Get the state of the promise ("pending", "rejected", or "fulfilled").
     *
     * The three states can be checked against the constants defined on
     * PromiseInterface: PENDING, FULFILLED, and REJECTED.
     *
     * @return string
     */
    public function getState();

    /**
     * Resolve the promise with the given value.
     *
     * @param mixed $value
     * @throws \RuntimeException if the promise is already resolved.
     */
    public function resolve($value);

    /**
     * Reject the promise with the given reason.
     *
     * @param mixed $reason
     * @throws \RuntimeException if the promise is already resolved.
     */
    public function reject($reason);

    /**
     * Cancels the promise if possible.
     *
     * @link https://github.com/promises-aplus/cancellation-spec/issues/7
     */
    public function cancel();

    /**
     * Waits until the promise completes if possible.
     *
     * Pass $unwrap as true to unwrap the result of the promise, either
     * returning the resolved value or throwing the rejected exception.
     *
     * If the promise cannot be waited on, then the promise will be rejected.
     *
     * @param bool $unwrap
     *
     * @return mixed
     * @throws \LogicException if the promise has no wait function or if the
     *                         promise does not settle after waiting.
     */
    public function wait($unwrap = true);
}
vendor/guzzlehttp/promises/src/RejectedPromise.php000064400000004274151327705700016524 0ustar00<?php
namespace WPvividGuzzleHttp\Promise;

/**
 * A promise that has been rejected.
 *
 * Thenning off of this promise will invoke the onRejected callback
 * immediately and ignore other callbacks.
 */
class RejectedPromise implements PromiseInterface
{
    private $reason;

    public function __construct($reason)
    {
        if (method_exists($reason, 'then')) {
            throw new \InvalidArgumentException(
                'You cannot create a RejectedPromise with a promise.');
        }

        $this->reason = $reason;
    }

    public function then(
        callable $onFulfilled = null,
        callable $onRejected = null
    ) {
        // If there's no onRejected callback then just return self.
        if (!$onRejected) {
            return $this;
        }

        $queue = queue();
        $reason = $this->reason;
        $p = new Promise([$queue, 'run']);
        $queue->add(static function () use ($p, $reason, $onRejected) {
            if ($p->getState() === self::PENDING) {
                try {
                    // Return a resolved promise if onRejected does not throw.
                    $p->resolve($onRejected($reason));
                } catch (\Throwable $e) {
                    // onRejected threw, so return a rejected promise.
                    $p->reject($e);
                } catch (\Exception $e) {
                    // onRejected threw, so return a rejected promise.
                    $p->reject($e);
                }
            }
        });

        return $p;
    }

    public function otherwise(callable $onRejected)
    {
        return $this->then(null, $onRejected);
    }

    public function wait($unwrap = true, $defaultDelivery = null)
    {
        if ($unwrap) {
            throw exception_for($this->reason);
        }
    }

    public function getState()
    {
        return self::REJECTED;
    }

    public function resolve($value)
    {
        throw new \LogicException("Cannot resolve a rejected promise");
    }

    public function reject($reason)
    {
        if ($reason !== $this->reason) {
            throw new \LogicException("Cannot reject a rejected promise");
        }
    }

    public function cancel()
    {
        // pass
    }
}
vendor/guzzlehttp/promises/src/EachPromise.php000064400000016165151327705700015641 0ustar00<?php
namespace WPvividGuzzleHttp\Promise;

/**
 * Represents a promise that iterates over many promises and invokes
 * side-effect functions in the process.
 */
class EachPromise implements PromisorInterface
{
    private $pending = [];

    /** @var \Iterator */
    private $iterable;

    /** @var callable|int */
    private $concurrency;

    /** @var callable */
    private $onFulfilled;

    /** @var callable */
    private $onRejected;

    /** @var Promise */
    private $aggregate;

    /** @var bool */
    private $mutex;

    /**
     * Configuration hash can include the following key value pairs:
     *
     * - fulfilled: (callable) Invoked when a promise fulfills. The function
     *   is invoked with three arguments: the fulfillment value, the index
     *   position from the iterable list of the promise, and the aggregate
     *   promise that manages all of the promises. The aggregate promise may
     *   be resolved from within the callback to short-circuit the promise.
     * - rejected: (callable) Invoked when a promise is rejected. The
     *   function is invoked with three arguments: the rejection reason, the
     *   index position from the iterable list of the promise, and the
     *   aggregate promise that manages all of the promises. The aggregate
     *   promise may be resolved from within the callback to short-circuit
     *   the promise.
     * - concurrency: (integer) Pass this configuration option to limit the
     *   allowed number of outstanding concurrently executing promises,
     *   creating a capped pool of promises. There is no limit by default.
     *
     * @param mixed    $iterable Promises or values to iterate.
     * @param array    $config   Configuration options
     */
    public function __construct($iterable, array $config = [])
    {
        $this->iterable = iter_for($iterable);

        if (isset($config['concurrency'])) {
            $this->concurrency = $config['concurrency'];
        }

        if (isset($config['fulfilled'])) {
            $this->onFulfilled = $config['fulfilled'];
        }

        if (isset($config['rejected'])) {
            $this->onRejected = $config['rejected'];
        }
    }

    public function promise()
    {
        if ($this->aggregate) {
            return $this->aggregate;
        }

        try {
            $this->createPromise();
            $this->iterable->rewind();
            $this->refillPending();
        } catch (\Throwable $e) {
            $this->aggregate->reject($e);
        } catch (\Exception $e) {
            $this->aggregate->reject($e);
        }

        return $this->aggregate;
    }

    private function createPromise()
    {
        $this->mutex = false;
        $this->aggregate = new Promise(function () {
            reset($this->pending);
            if (empty($this->pending) && !$this->iterable->valid()) {
                $this->aggregate->resolve(null);
                return;
            }

            // Consume a potentially fluctuating list of promises while
            // ensuring that indexes are maintained (precluding array_shift).
            while ($promise = current($this->pending)) {
                next($this->pending);
                $promise->wait();
                if ($this->aggregate->getState() !== PromiseInterface::PENDING) {
                    return;
                }
            }
        });

        // Clear the references when the promise is resolved.
        $clearFn = function () {
            $this->iterable = $this->concurrency = $this->pending = null;
            $this->onFulfilled = $this->onRejected = null;
        };

        $this->aggregate->then($clearFn, $clearFn);
    }

    private function refillPending()
    {
        if (!$this->concurrency) {
            // Add all pending promises.
            while ($this->addPending() && $this->advanceIterator());
            return;
        }

        // Add only up to N pending promises.
        $concurrency = is_callable($this->concurrency)
            ? call_user_func($this->concurrency, count($this->pending))
            : $this->concurrency;
        $concurrency = max($concurrency - count($this->pending), 0);
        // Concurrency may be set to 0 to disallow new promises.
        if (!$concurrency) {
            return;
        }
        // Add the first pending promise.
        $this->addPending();
        // Note this is special handling for concurrency=1 so that we do
        // not advance the iterator after adding the first promise. This
        // helps work around issues with generators that might not have the
        // next value to yield until promise callbacks are called.
        while (--$concurrency
            && $this->advanceIterator()
            && $this->addPending());
    }

    private function addPending()
    {
        if (!$this->iterable || !$this->iterable->valid()) {
            return false;
        }

        $promise = promise_for($this->iterable->current());
        $idx = $this->iterable->key();

        $this->pending[$idx] = $promise->then(
            function ($value) use ($idx) {
                if ($this->onFulfilled) {
                    call_user_func(
                        $this->onFulfilled, $value, $idx, $this->aggregate
                    );
                }
                $this->step($idx);
            },
            function ($reason) use ($idx) {
                if ($this->onRejected) {
                    call_user_func(
                        $this->onRejected, $reason, $idx, $this->aggregate
                    );
                }
                $this->step($idx);
            }
        );

        return true;
    }

    private function advanceIterator()
    {
        // Place a lock on the iterator so that we ensure to not recurse,
        // preventing fatal generator errors.
        if ($this->mutex) {
            return false;
        }

        $this->mutex = true;

        try {
            $this->iterable->next();
            $this->mutex = false;
            return true;
        } catch (\Throwable $e) {
            $this->aggregate->reject($e);
            $this->mutex = false;
            return false;
        } catch (\Exception $e) {
            $this->aggregate->reject($e);
            $this->mutex = false;
            return false;
        }
    }

    private function step($idx)
    {
        // If the promise was already resolved, then ignore this step.
        if ($this->aggregate->getState() !== PromiseInterface::PENDING) {
            return;
        }

        unset($this->pending[$idx]);

        // Only refill pending promises if we are not locked, preventing the
        // EachPromise to recursively invoke the provided iterator, which
        // cause a fatal error: "Cannot resume an already running generator"
        if ($this->advanceIterator() && !$this->checkIfFinished()) {
            // Add more pending promises if possible.
            $this->refillPending();
        }
    }

    private function checkIfFinished()
    {
        if (!$this->pending && !$this->iterable->valid()) {
            // Resolve the promise if there's nothing left to do.
            $this->aggregate->resolve(null);
            return true;
        }

        return false;
    }
}
vendor/guzzlehttp/promises/src/TaskQueueInterface.php000064400000000733151327705700017164 0ustar00<?php
namespace WPvividGuzzleHttp\Promise;

interface TaskQueueInterface
{
    /**
     * Returns true if the queue is empty.
     *
     * @return bool
     */
    public function isEmpty();

    /**
     * Adds a task to the queue that will be executed the next time run is
     * called.
     *
     * @param callable $task
     */
    public function add(callable $task);

    /**
     * Execute all of the pending task in the queue.
     */
    public function run();
}
vendor/guzzlehttp/promises/src/AggregateException.php000064400000000602151327705700017174 0ustar00<?php
namespace WPvividGuzzleHttp\Promise;

/**
 * Exception thrown when too many errors occur in the some() or any() methods.
 */
class AggregateException extends RejectionException
{
    public function __construct($msg, array $reasons)
    {
        parent::__construct(
            $reasons,
            sprintf('%s; %d rejected promises', $msg, count($reasons))
        );
    }
}
vendor/guzzlehttp/promises/src/FulfilledPromise.php000064400000003665151327705700016710 0ustar00<?php
namespace WPvividGuzzleHttp\Promise;

/**
 * A promise that has been fulfilled.
 *
 * Thenning off of this promise will invoke the onFulfilled callback
 * immediately and ignore other callbacks.
 */
class FulfilledPromise implements PromiseInterface
{
    private $value;

    public function __construct($value)
    {
        if (method_exists($value, 'then')) {
            throw new \InvalidArgumentException(
                'You cannot create a FulfilledPromise with a promise.');
        }

        $this->value = $value;
    }

    public function then(
        callable $onFulfilled = null,
        callable $onRejected = null
    ) {
        // Return itself if there is no onFulfilled function.
        if (!$onFulfilled) {
            return $this;
        }

        $queue = queue();
        $p = new Promise([$queue, 'run']);
        $value = $this->value;
        $queue->add(static function () use ($p, $value, $onFulfilled) {
            if ($p->getState() === self::PENDING) {
                try {
                    $p->resolve($onFulfilled($value));
                } catch (\Throwable $e) {
                    $p->reject($e);
                } catch (\Exception $e) {
                    $p->reject($e);
                }
            }
        });

        return $p;
    }

    public function otherwise(callable $onRejected)
    {
        return $this->then(null, $onRejected);
    }

    public function wait($unwrap = true, $defaultDelivery = null)
    {
        return $unwrap ? $this->value : null;
    }

    public function getState()
    {
        return self::FULFILLED;
    }

    public function resolve($value)
    {
        if ($value !== $this->value) {
            throw new \LogicException("Cannot resolve a fulfilled promise");
        }
    }

    public function reject($reason)
    {
        throw new \LogicException("Cannot reject a fulfilled promise");
    }

    public function cancel()
    {
        // pass
    }
}
vendor/guzzlehttp/promises/src/RejectionException.php000064400000002310151327705700017226 0ustar00<?php
namespace WPvividGuzzleHttp\Promise;

/**
 * A special exception that is thrown when waiting on a rejected promise.
 *
 * The reason value is available via the getReason() method.
 */
class RejectionException extends \RuntimeException
{
    /** @var mixed Rejection reason. */
    private $reason;

    /**
     * @param mixed $reason       Rejection reason.
     * @param string $description Optional description
     */
    public function __construct($reason, $description = null)
    {
        $this->reason = $reason;

        $message = 'The promise was rejected';

        if ($description) {
            $message .= ' with reason: ' . $description;
        } elseif (is_string($reason)
            || (is_object($reason) && method_exists($reason, '__toString'))
        ) {
            $message .= ' with reason: ' . $this->reason;
        } elseif ($reason instanceof \JsonSerializable) {
            $message .= ' with reason: '
                . json_encode($this->reason, JSON_PRETTY_PRINT);
        }

        parent::__construct($message);
    }

    /**
     * Returns the rejection reason.
     *
     * @return mixed
     */
    public function getReason()
    {
        return $this->reason;
    }
}
vendor/guzzlehttp/promises/composer.json000064400000001431151327705700014652 0ustar00{
    "name": "guzzlehttp/promises",
    "description": "Guzzle promises library",
    "keywords": ["promise"],
    "license": "MIT",
    "authors": [
        {
            "name": "Michael Dowling",
            "email": "mtdowling@gmail.com",
            "homepage": "https://github.com/mtdowling"
        }
    ],
    "require": {
        "php": ">=5.5.0"
    },
    "require-dev": {
        "phpunit/phpunit": "^4.0"
    },
    "autoload": {
        "psr-4": {
            "WPvividGuzzleHttp\\Promise\\": "src/"
        },
        "files": ["src/functions_include.php"]
    },
    "scripts": {
        "test": "vendor/bin/phpunit",
        "test-ci": "vendor/bin/phpunit --coverage-text"
    },
    "extra": {
        "branch-alias": {
            "dev-master": "1.4-dev"
        }
    }
}
vendor/guzzlehttp/promises/README.md000064400000035704151327705700013421 0ustar00# Guzzle Promises

[Promises/A+](https://promisesaplus.com/) implementation that handles promise
chaining and resolution iteratively, allowing for "infinite" promise chaining
while keeping the stack size constant. Read [this blog post](https://blog.domenic.me/youre-missing-the-point-of-promises/)
for a general introduction to promises.

- [Features](#features)
- [Quick start](#quick-start)
- [Synchronous wait](#synchronous-wait)
- [Cancellation](#cancellation)
- [API](#api)
  - [Promise](#promise)
  - [FulfilledPromise](#fulfilledpromise)
  - [RejectedPromise](#rejectedpromise)
- [Promise interop](#promise-interop)
- [Implementation notes](#implementation-notes)


# Features

- [Promises/A+](https://promisesaplus.com/) implementation.
- Promise resolution and chaining is handled iteratively, allowing for
  "infinite" promise chaining.
- Promises have a synchronous `wait` method.
- Promises can be cancelled.
- Works with any object that has a `then` function.
- C# style async/await coroutine promises using
  `GuzzleHttp\Promise\coroutine()`.


# Quick start

A *promise* represents the eventual result of an asynchronous operation. The
primary way of interacting with a promise is through its `then` method, which
registers callbacks to receive either a promise's eventual value or the reason
why the promise cannot be fulfilled.


## Callbacks

Callbacks are registered with the `then` method by providing an optional 
`$onFulfilled` followed by an optional `$onRejected` function.


```php
use GuzzleHttp\Promise\Promise;

$promise = new Promise();
$promise->then(
    // $onFulfilled
    function ($value) {
        echo 'The promise was fulfilled.';
    },
    // $onRejected
    function ($reason) {
        echo 'The promise was rejected.';
    }
);
```

*Resolving* a promise means that you either fulfill a promise with a *value* or
reject a promise with a *reason*. Resolving a promises triggers callbacks
registered with the promises's `then` method. These callbacks are triggered
only once and in the order in which they were added.


## Resolving a promise

Promises are fulfilled using the `resolve($value)` method. Resolving a promise
with any value other than a `GuzzleHttp\Promise\RejectedPromise` will trigger
all of the onFulfilled callbacks (resolving a promise with a rejected promise
will reject the promise and trigger the `$onRejected` callbacks).

```php
use GuzzleHttp\Promise\Promise;

$promise = new Promise();
$promise
    ->then(function ($value) {
        // Return a value and don't break the chain
        return "Hello, " . $value;
    })
    // This then is executed after the first then and receives the value
    // returned from the first then.
    ->then(function ($value) {
        echo $value;
    });

// Resolving the promise triggers the $onFulfilled callbacks and outputs
// "Hello, reader".
$promise->resolve('reader.');
```


## Promise forwarding

Promises can be chained one after the other. Each then in the chain is a new
promise. The return value of a promise is what's forwarded to the next
promise in the chain. Returning a promise in a `then` callback will cause the
subsequent promises in the chain to only be fulfilled when the returned promise
has been fulfilled. The next promise in the chain will be invoked with the
resolved value of the promise.

```php
use GuzzleHttp\Promise\Promise;

$promise = new Promise();
$nextPromise = new Promise();

$promise
    ->then(function ($value) use ($nextPromise) {
        echo $value;
        return $nextPromise;
    })
    ->then(function ($value) {
        echo $value;
    });

// Triggers the first callback and outputs "A"
$promise->resolve('A');
// Triggers the second callback and outputs "B"
$nextPromise->resolve('B');
```

## Promise rejection

When a promise is rejected, the `$onRejected` callbacks are invoked with the
rejection reason.

```php
use GuzzleHttp\Promise\Promise;

$promise = new Promise();
$promise->then(null, function ($reason) {
    echo $reason;
});

$promise->reject('Error!');
// Outputs "Error!"
```

## Rejection forwarding

If an exception is thrown in an `$onRejected` callback, subsequent
`$onRejected` callbacks are invoked with the thrown exception as the reason.

```php
use GuzzleHttp\Promise\Promise;

$promise = new Promise();
$promise->then(null, function ($reason) {
    throw new \Exception($reason);
})->then(null, function ($reason) {
    assert($reason->getMessage() === 'Error!');
});

$promise->reject('Error!');
```

You can also forward a rejection down the promise chain by returning a
`GuzzleHttp\Promise\RejectedPromise` in either an `$onFulfilled` or
`$onRejected` callback.

```php
use GuzzleHttp\Promise\Promise;
use GuzzleHttp\Promise\RejectedPromise;

$promise = new Promise();
$promise->then(null, function ($reason) {
    return new RejectedPromise($reason);
})->then(null, function ($reason) {
    assert($reason === 'Error!');
});

$promise->reject('Error!');
```

If an exception is not thrown in a `$onRejected` callback and the callback
does not return a rejected promise, downstream `$onFulfilled` callbacks are
invoked using the value returned from the `$onRejected` callback.

```php
use GuzzleHttp\Promise\Promise;
use GuzzleHttp\Promise\RejectedPromise;

$promise = new Promise();
$promise
    ->then(null, function ($reason) {
        return "It's ok";
    })
    ->then(function ($value) {
        assert($value === "It's ok");
    });

$promise->reject('Error!');
```

# Synchronous wait

You can synchronously force promises to complete using a promise's `wait`
method. When creating a promise, you can provide a wait function that is used
to synchronously force a promise to complete. When a wait function is invoked
it is expected to deliver a value to the promise or reject the promise. If the
wait function does not deliver a value, then an exception is thrown. The wait
function provided to a promise constructor is invoked when the `wait` function
of the promise is called.

```php
$promise = new Promise(function () use (&$promise) {
    $promise->resolve('foo');
});

// Calling wait will return the value of the promise.
echo $promise->wait(); // outputs "foo"
```

If an exception is encountered while invoking the wait function of a promise,
the promise is rejected with the exception and the exception is thrown.

```php
$promise = new Promise(function () use (&$promise) {
    throw new \Exception('foo');
});

$promise->wait(); // throws the exception.
```

Calling `wait` on a promise that has been fulfilled will not trigger the wait
function. It will simply return the previously resolved value.

```php
$promise = new Promise(function () { die('this is not called!'); });
$promise->resolve('foo');
echo $promise->wait(); // outputs "foo"
```

Calling `wait` on a promise that has been rejected will throw an exception. If
the rejection reason is an instance of `\Exception` the reason is thrown.
Otherwise, a `GuzzleHttp\Promise\RejectionException` is thrown and the reason
can be obtained by calling the `getReason` method of the exception.

```php
$promise = new Promise();
$promise->reject('foo');
$promise->wait();
```

> PHP Fatal error:  Uncaught exception 'GuzzleHttp\Promise\RejectionException' with message 'The promise was rejected with value: foo'


## Unwrapping a promise

When synchronously waiting on a promise, you are joining the state of the
promise into the current state of execution (i.e., return the value of the
promise if it was fulfilled or throw an exception if it was rejected). This is
called "unwrapping" the promise. Waiting on a promise will by default unwrap
the promise state.

You can force a promise to resolve and *not* unwrap the state of the promise
by passing `false` to the first argument of the `wait` function:

```php
$promise = new Promise();
$promise->reject('foo');
// This will not throw an exception. It simply ensures the promise has
// been resolved.
$promise->wait(false);
```

When unwrapping a promise, the resolved value of the promise will be waited
upon until the unwrapped value is not a promise. This means that if you resolve
promise A with a promise B and unwrap promise A, the value returned by the
wait function will be the value delivered to promise B.

**Note**: when you do not unwrap the promise, no value is returned.


# Cancellation

You can cancel a promise that has not yet been fulfilled using the `cancel()`
method of a promise. When creating a promise you can provide an optional
cancel function that when invoked cancels the action of computing a resolution
of the promise.


# API


## Promise

When creating a promise object, you can provide an optional `$waitFn` and
`$cancelFn`. `$waitFn` is a function that is invoked with no arguments and is
expected to resolve the promise. `$cancelFn` is a function with no arguments
that is expected to cancel the computation of a promise. It is invoked when the
`cancel()` method of a promise is called.

```php
use GuzzleHttp\Promise\Promise;

$promise = new Promise(
    function () use (&$promise) {
        $promise->resolve('waited');
    },
    function () {
        // do something that will cancel the promise computation (e.g., close
        // a socket, cancel a database query, etc...)
    }
);

assert('waited' === $promise->wait());
```

A promise has the following methods:

- `then(callable $onFulfilled, callable $onRejected) : PromiseInterface`
  
  Appends fulfillment and rejection handlers to the promise, and returns a new promise resolving to the return value of the called handler.

- `otherwise(callable $onRejected) : PromiseInterface`
  
  Appends a rejection handler callback to the promise, and returns a new promise resolving to the return value of the callback if it is called, or to its original fulfillment value if the promise is instead fulfilled.

- `wait($unwrap = true) : mixed`

  Synchronously waits on the promise to complete.
  
  `$unwrap` controls whether or not the value of the promise is returned for a
  fulfilled promise or if an exception is thrown if the promise is rejected.
  This is set to `true` by default.

- `cancel()`

  Attempts to cancel the promise if possible. The promise being cancelled and
  the parent most ancestor that has not yet been resolved will also be
  cancelled. Any promises waiting on the cancelled promise to resolve will also
  be cancelled.

- `getState() : string`

  Returns the state of the promise. One of `pending`, `fulfilled`, or
  `rejected`.

- `resolve($value)`

  Fulfills the promise with the given `$value`.

- `reject($reason)`

  Rejects the promise with the given `$reason`.


## FulfilledPromise

A fulfilled promise can be created to represent a promise that has been
fulfilled.

```php
use GuzzleHttp\Promise\FulfilledPromise;

$promise = new FulfilledPromise('value');

// Fulfilled callbacks are immediately invoked.
$promise->then(function ($value) {
    echo $value;
});
```


## RejectedPromise

A rejected promise can be created to represent a promise that has been
rejected.

```php
use GuzzleHttp\Promise\RejectedPromise;

$promise = new RejectedPromise('Error');

// Rejected callbacks are immediately invoked.
$promise->then(null, function ($reason) {
    echo $reason;
});
```


# Promise interop

This library works with foreign promises that have a `then` method. This means
you can use Guzzle promises with [React promises](https://github.com/reactphp/promise)
for example. When a foreign promise is returned inside of a then method
callback, promise resolution will occur recursively.

```php
// Create a React promise
$deferred = new React\Promise\Deferred();
$reactPromise = $deferred->promise();

// Create a Guzzle promise that is fulfilled with a React promise.
$guzzlePromise = new \GuzzleHttp\Promise\Promise();
$guzzlePromise->then(function ($value) use ($reactPromise) {
    // Do something something with the value...
    // Return the React promise
    return $reactPromise;
});
```

Please note that wait and cancel chaining is no longer possible when forwarding
a foreign promise. You will need to wrap a third-party promise with a Guzzle
promise in order to utilize wait and cancel functions with foreign promises.


## Event Loop Integration

In order to keep the stack size constant, Guzzle promises are resolved
asynchronously using a task queue. When waiting on promises synchronously, the
task queue will be automatically run to ensure that the blocking promise and
any forwarded promises are resolved. When using promises asynchronously in an
event loop, you will need to run the task queue on each tick of the loop. If
you do not run the task queue, then promises will not be resolved.

You can run the task queue using the `run()` method of the global task queue
instance.

```php
// Get the global task queue
$queue = \GuzzleHttp\Promise\queue();
$queue->run();
```

For example, you could use Guzzle promises with React using a periodic timer:

```php
$loop = React\EventLoop\Factory::create();
$loop->addPeriodicTimer(0, [$queue, 'run']);
```

*TODO*: Perhaps adding a `futureTick()` on each tick would be faster?


# Implementation notes


## Promise resolution and chaining is handled iteratively

By shuffling pending handlers from one owner to another, promises are
resolved iteratively, allowing for "infinite" then chaining.

```php
<?php
require 'vendor/autoload.php';

use GuzzleHttp\Promise\Promise;

$parent = new Promise();
$p = $parent;

for ($i = 0; $i < 1000; $i++) {
    $p = $p->then(function ($v) {
        // The stack size remains constant (a good thing)
        echo xdebug_get_stack_depth() . ', ';
        return $v + 1;
    });
}

$parent->resolve(0);
var_dump($p->wait()); // int(1000)

```

When a promise is fulfilled or rejected with a non-promise value, the promise
then takes ownership of the handlers of each child promise and delivers values
down the chain without using recursion.

When a promise is resolved with another promise, the original promise transfers
all of its pending handlers to the new promise. When the new promise is
eventually resolved, all of the pending handlers are delivered the forwarded
value.


## A promise is the deferred.

Some promise libraries implement promises using a deferred object to represent
a computation and a promise object to represent the delivery of the result of
the computation. This is a nice separation of computation and delivery because
consumers of the promise cannot modify the value that will be eventually
delivered.

One side effect of being able to implement promise resolution and chaining
iteratively is that you need to be able for one promise to reach into the state
of another promise to shuffle around ownership of handlers. In order to achieve
this without making the handlers of a promise publicly mutable, a promise is
also the deferred value, allowing promises of the same parent class to reach
into and modify the private properties of promises of the same type. While this
does allow consumers of the value to modify the resolution or rejection of the
deferred, it is a small price to pay for keeping the stack size constant.

```php
$promise = new Promise();
$promise->then(function ($value) { echo $value; });
// The promise is the deferred value, so you can deliver a value to it.
$promise->resolve('foo');
// prints "foo"
```
vendor/guzzlehttp/promises/Makefile000064400000000275151327705700013575 0ustar00all: clean test

test:
	vendor/bin/phpunit

coverage:
	vendor/bin/phpunit --coverage-html=artifacts/coverage

view-coverage:
	open artifacts/coverage/index.html

clean:
	rm -rf artifacts/*
vendor/guzzlehttp/promises/CHANGELOG.md000064400000002152151327705700013742 0ustar00# CHANGELOG


## 1.3.1 - 2016-12-20

### Fixed

- `wait()` foreign promise compatibility


## 1.3.0 - 2016-11-18

### Added

- Adds support for custom task queues.

### Fixed

- Fixed coroutine promise memory leak.


## 1.2.0 - 2016-05-18

### Changed

- Update to now catch `\Throwable` on PHP 7+


## 1.1.0 - 2016-03-07

### Changed

- Update EachPromise to prevent recurring on a iterator when advancing, as this
  could trigger fatal generator errors.
- Update Promise to allow recursive waiting without unwrapping exceptions.


## 1.0.3 - 2015-10-15

### Changed

- Update EachPromise to immediately resolve when the underlying promise iterator
  is empty. Previously, such a promise would throw an exception when its `wait`
  function was called.


## 1.0.2 - 2015-05-15

### Changed

- Conditionally require functions.php.


## 1.0.1 - 2015-06-24

### Changed

- Updating EachPromise to call next on the underlying promise iterator as late
  as possible to ensure that generators that generate new requests based on
  callbacks are not iterated until after callbacks are invoked.


## 1.0.0 - 2015-05-12

- Initial release
vendor/guzzlehttp/promises/LICENSE000064400000002134151327705700013136 0ustar00Copyright (c) 2015-2016 Michael Dowling, https://github.com/mtdowling <mtdowling@gmail.com>

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
vendor/guzzlehttp/guzzle/src/Exception/ServerException.php000064400000000250151327705700020170 0ustar00<?php
namespace WPvividGuzzleHttp\Exception;

/**
 * Exception when a server error is encountered (5xx codes)
 */
class ServerException extends BadResponseException {}
vendor/guzzlehttp/guzzle/src/Exception/ClientException.php000064400000000250151327705700020140 0ustar00<?php
namespace WPvividGuzzleHttp\Exception;

/**
 * Exception when a client error is encountered (4xx codes)
 */
class ClientException extends BadResponseException {}
vendor/guzzlehttp/guzzle/src/Exception/TooManyRedirectsException.php000064400000000152151327705700022156 0ustar00<?php
namespace WPvividGuzzleHttp\Exception;

class TooManyRedirectsException extends RequestException {}
vendor/guzzlehttp/guzzle/src/Exception/RequestException.php000064400000013132151327705700020355 0ustar00<?php
namespace WPvividGuzzleHttp\Exception;

use WPvividPsr\Http\Message\RequestInterface;
use WPvividPsr\Http\Message\ResponseInterface;
use WPvividGuzzleHttp\Promise\PromiseInterface;
use WPvividPsr\Http\Message\UriInterface;

/**
 * HTTP Request exception
 */
class RequestException extends TransferException
{
    /** @var RequestInterface */
    private $request;

    /** @var ResponseInterface */
    private $response;

    /** @var array */
    private $handlerContext;

    public function __construct(
        $message,
        RequestInterface $request,
        ResponseInterface $response = null,
        \Exception $previous = null,
        array $handlerContext = []
    ) {
        // Set the code of the exception if the response is set and not future.
        $code = $response && !($response instanceof PromiseInterface)
            ? $response->getStatusCode()
            : 0;
        parent::__construct($message, $code, $previous);
        $this->request = $request;
        $this->response = $response;
        $this->handlerContext = $handlerContext;
    }

    /**
     * Wrap non-RequestExceptions with a RequestException
     *
     * @param RequestInterface $request
     * @param \Exception       $e
     *
     * @return RequestException
     */
    public static function wrapException(RequestInterface $request, \Exception $e)
    {
        return $e instanceof RequestException
            ? $e
            : new RequestException($e->getMessage(), $request, null, $e);
    }

    /**
     * Factory method to create a new exception with a normalized error message
     *
     * @param RequestInterface  $request  Request
     * @param ResponseInterface $response Response received
     * @param \Exception        $previous Previous exception
     * @param array             $ctx      Optional handler context.
     *
     * @return self
     */
    public static function create(
        RequestInterface $request,
        ResponseInterface $response = null,
        \Exception $previous = null,
        array $ctx = []
    ) {
        if (!$response) {
            return new self(
                'Error completing request',
                $request,
                null,
                $previous,
                $ctx
            );
        }

        $level = (int) floor($response->getStatusCode() / 100);
        if ($level === 4) {
            $label = 'Client error';
            $className = ClientException::class;
        } elseif ($level === 5) {
            $label = 'Server error';
            $className = ServerException::class;
        } else {
            $label = 'Unsuccessful request';
            $className = __CLASS__;
        }

        $uri = $request->getUri();
        $uri = static::obfuscateUri($uri);

        // Client Error: `GET /` resulted in a `404 Not Found` response:
        // <html> ... (truncated)
        $message = sprintf(
            '%s: `%s %s` resulted in a `%s %s` response',
            $label,
            $request->getMethod(),
            $uri,
            $response->getStatusCode(),
            $response->getReasonPhrase()
        );

        $summary = static::getResponseBodySummary($response);

        if ($summary !== null) {
            $message .= ":\n{$summary}\n";
        }

        return new $className($message, $request, $response, $previous, $ctx);
    }

    /**
     * Get a short summary of the response
     *
     * Will return `null` if the response is not printable.
     *
     * @param ResponseInterface $response
     *
     * @return string|null
     */
    public static function getResponseBodySummary(ResponseInterface $response)
    {
        $body = $response->getBody();

        if (!$body->isSeekable()) {
            return null;
        }

        $size = $body->getSize();

        if ($size === 0) {
            return null;
        }

        $summary = $body->read(120);
        $body->rewind();

        if ($size > 120) {
            $summary .= ' (truncated...)';
        }

        // Matches any printable character, including unicode characters:
        // letters, marks, numbers, punctuation, spacing, and separators.
        if (preg_match('/[^\pL\pM\pN\pP\pS\pZ\n\r\t]/', $summary)) {
            return null;
        }

        return $summary;
    }

    /**
     * Obfuscates URI if there is an username and a password present
     *
     * @param UriInterface $uri
     *
     * @return UriInterface
     */
    private static function obfuscateUri($uri)
    {
        $userInfo = $uri->getUserInfo();

        if (false !== ($pos = strpos($userInfo, ':'))) {
            return $uri->withUserInfo(substr($userInfo, 0, $pos), '***');
        }

        return $uri;
    }

    /**
     * Get the request that caused the exception
     *
     * @return RequestInterface
     */
    public function getRequest()
    {
        return $this->request;
    }

    /**
     * Get the associated response
     *
     * @return ResponseInterface|null
     */
    public function getResponse()
    {
        return $this->response;
    }

    /**
     * Check if a response was received
     *
     * @return bool
     */
    public function hasResponse()
    {
        return $this->response !== null;
    }

    /**
     * Get contextual information about the error from the underlying handler.
     *
     * The contents of this array will vary depending on which handler you are
     * using. It may also be just an empty array. Relying on this data will
     * couple you to a specific handler, but can give more debug information
     * when needed.
     *
     * @return array
     */
    public function getHandlerContext()
    {
        return $this->handlerContext;
    }
}
vendor/guzzlehttp/guzzle/src/Exception/GuzzleException.php000064400000000454151327705700020210 0ustar00<?php
namespace WPvividGuzzleHttp\Exception;

/**
 * @method string getMessage()
 * @method \Throwable|null getPrevious()
 * @method mixed getCode()
 * @method string getFile()
 * @method int getLine()
 * @method array getTrace()
 * @method string getTraceAsString()
 */
interface GuzzleException {}
vendor/guzzlehttp/guzzle/src/Exception/TransferException.php000064400000000176151327705700020515 0ustar00<?php
namespace WPvividGuzzleHttp\Exception;

class TransferException extends \RuntimeException implements GuzzleException {}
vendor/guzzlehttp/guzzle/src/Exception/ConnectException.php000064400000001344151327705700020320 0ustar00<?php
namespace WPvividGuzzleHttp\Exception;

use WPvividPsr\Http\Message\RequestInterface;

/**
 * Exception thrown when a connection cannot be established.
 *
 * Note that no response is present for a ConnectException
 */
class ConnectException extends RequestException
{
    public function __construct(
        $message,
        RequestInterface $request,
        \Exception $previous = null,
        array $handlerContext = []
    ) {
        parent::__construct($message, $request, null, $previous, $handlerContext);
    }

    /**
     * @return null
     */
    public function getResponse()
    {
        return null;
    }

    /**
     * @return bool
     */
    public function hasResponse()
    {
        return false;
    }
}
vendor/guzzlehttp/guzzle/src/Exception/SeekException.php000064400000001132151327705700017611 0ustar00<?php
namespace WPvividGuzzleHttp\Exception;

use WPvividPsr\Http\Message\StreamInterface;

/**
 * Exception thrown when a seek fails on a stream.
 */
class SeekException extends \RuntimeException implements GuzzleException
{
    private $stream;

    public function __construct(StreamInterface $stream, $pos = 0, $msg = '')
    {
        $this->stream = $stream;
        $msg = $msg ?: 'Could not seek the stream to position ' . $pos;
        parent::__construct($msg);
    }

    /**
     * @return StreamInterface
     */
    public function getStream()
    {
        return $this->stream;
    }
}
vendor/guzzlehttp/guzzle/src/Exception/BadResponseException.php000064400000001473151327705700021137 0ustar00<?php
namespace WPvividGuzzleHttp\Exception;

use WPvividPsr\Http\Message\RequestInterface;
use WPvividPsr\Http\Message\ResponseInterface;

/**
 * Exception when an HTTP error occurs (4xx or 5xx error)
 */
class BadResponseException extends RequestException
{
    public function __construct(
        $message,
        RequestInterface $request,
        ResponseInterface $response = null,
        \Exception $previous = null,
        array $handlerContext = []
    ) {
        if (null === $response) {
            @trigger_error(
                'Instantiating the ' . __CLASS__ . ' class without a Response is deprecated since version 6.3 and will be removed in 7.0.',
                E_USER_DEPRECATED
            );
        }
        parent::__construct($message, $request, $response, $previous, $handlerContext);
    }
}
vendor/guzzlehttp/guzzle/src/HandlerStack.php000064400000016720151327705700015461 0ustar00<?php
namespace WPvividGuzzleHttp;

use WPvividPsr\Http\Message\RequestInterface;

/**
 * Creates a composed Guzzle handler function by stacking middlewares on top of
 * an HTTP handler function.
 */
class HandlerStack
{
    /** @var callable */
    private $handler;

    /** @var array */
    private $stack = [];

    /** @var callable|null */
    private $cached;

    /**
     * Creates a default handler stack that can be used by clients.
     *
     * The returned handler will wrap the provided handler or use the most
     * appropriate default handler for your system. The returned HandlerStack has
     * support for cookies, redirects, HTTP error exceptions, and preparing a body
     * before sending.
     *
     * The returned handler stack can be passed to a client in the "handler"
     * option.
     *
     * @param callable $handler HTTP handler function to use with the stack. If no
     *                          handler is provided, the best handler for your
     *                          system will be utilized.
     *
     * @return HandlerStack
     */
    public static function create(callable $handler = null)
    {
        $stack = new self($handler ?: choose_handler());
        $stack->push(Middleware::httpErrors(), 'http_errors');
        $stack->push(Middleware::redirect(), 'allow_redirects');
        $stack->push(Middleware::cookies(), 'cookies');
        $stack->push(Middleware::prepareBody(), 'prepare_body');

        return $stack;
    }

    /**
     * @param callable $handler Underlying HTTP handler.
     */
    public function __construct(callable $handler = null)
    {
        $this->handler = $handler;
    }

    /**
     * Invokes the handler stack as a composed handler
     *
     * @param RequestInterface $request
     * @param array            $options
     */
    public function __invoke(RequestInterface $request, array $options)
    {
        $handler = $this->resolve();

        return $handler($request, $options);
    }

    /**
     * Dumps a string representation of the stack.
     *
     * @return string
     */
    public function __toString()
    {
        $depth = 0;
        $stack = [];
        if ($this->handler) {
            $stack[] = "0) Handler: " . $this->debugCallable($this->handler);
        }

        $result = '';
        foreach (array_reverse($this->stack) as $tuple) {
            $depth++;
            $str = "{$depth}) Name: '{$tuple[1]}', ";
            $str .= "Function: " . $this->debugCallable($tuple[0]);
            $result = "> {$str}\n{$result}";
            $stack[] = $str;
        }

        foreach (array_keys($stack) as $k) {
            $result .= "< {$stack[$k]}\n";
        }

        return $result;
    }

    /**
     * Set the HTTP handler that actually returns a promise.
     *
     * @param callable $handler Accepts a request and array of options and
     *                          returns a Promise.
     */
    public function setHandler(callable $handler)
    {
        $this->handler = $handler;
        $this->cached = null;
    }

    /**
     * Returns true if the builder has a handler.
     *
     * @return bool
     */
    public function hasHandler()
    {
        return (bool) $this->handler;
    }

    /**
     * Unshift a middleware to the bottom of the stack.
     *
     * @param callable $middleware Middleware function
     * @param string   $name       Name to register for this middleware.
     */
    public function unshift(callable $middleware, $name = null)
    {
        array_unshift($this->stack, [$middleware, $name]);
        $this->cached = null;
    }

    /**
     * Push a middleware to the top of the stack.
     *
     * @param callable $middleware Middleware function
     * @param string   $name       Name to register for this middleware.
     */
    public function push(callable $middleware, $name = '')
    {
        $this->stack[] = [$middleware, $name];
        $this->cached = null;
    }

    /**
     * Add a middleware before another middleware by name.
     *
     * @param string   $findName   Middleware to find
     * @param callable $middleware Middleware function
     * @param string   $withName   Name to register for this middleware.
     */
    public function before($findName, callable $middleware, $withName = '')
    {
        $this->splice($findName, $withName, $middleware, true);
    }

    /**
     * Add a middleware after another middleware by name.
     *
     * @param string   $findName   Middleware to find
     * @param callable $middleware Middleware function
     * @param string   $withName   Name to register for this middleware.
     */
    public function after($findName, callable $middleware, $withName = '')
    {
        $this->splice($findName, $withName, $middleware, false);
    }

    /**
     * Remove a middleware by instance or name from the stack.
     *
     * @param callable|string $remove Middleware to remove by instance or name.
     */
    public function remove($remove)
    {
        $this->cached = null;
        $idx = is_callable($remove) ? 0 : 1;
        $this->stack = array_values(array_filter(
            $this->stack,
            function ($tuple) use ($idx, $remove) {
                return $tuple[$idx] !== $remove;
            }
        ));
    }

    /**
     * Compose the middleware and handler into a single callable function.
     *
     * @return callable
     */
    public function resolve()
    {
        if (!$this->cached) {
            if (!($prev = $this->handler)) {
                throw new \LogicException('No handler has been specified');
            }

            foreach (array_reverse($this->stack) as $fn) {
                $prev = $fn[0]($prev);
            }

            $this->cached = $prev;
        }

        return $this->cached;
    }

    /**
     * @param $name
     * @return int
     */
    private function findByName($name)
    {
        foreach ($this->stack as $k => $v) {
            if ($v[1] === $name) {
                return $k;
            }
        }

        throw new \InvalidArgumentException("Middleware not found: $name");
    }

    /**
     * Splices a function into the middleware list at a specific position.
     *
     * @param          $findName
     * @param          $withName
     * @param callable $middleware
     * @param          $before
     */
    private function splice($findName, $withName, callable $middleware, $before)
    {
        $this->cached = null;
        $idx = $this->findByName($findName);
        $tuple = [$middleware, $withName];

        if ($before) {
            if ($idx === 0) {
                array_unshift($this->stack, $tuple);
            } else {
                $replacement = [$tuple, $this->stack[$idx]];
                array_splice($this->stack, $idx, 1, $replacement);
            }
        } elseif ($idx === count($this->stack) - 1) {
            $this->stack[] = $tuple;
        } else {
            $replacement = [$this->stack[$idx], $tuple];
            array_splice($this->stack, $idx, 1, $replacement);
        }
    }

    /**
     * Provides a debug string for a given callable.
     *
     * @param array|callable $fn Function to write as a string.
     *
     * @return string
     */
    private function debugCallable($fn)
    {
        if (is_string($fn)) {
            return "callable({$fn})";
        }

        if (is_array($fn)) {
            return is_string($fn[0])
                ? "callable({$fn[0]}::{$fn[1]})"
                : "callable(['" . get_class($fn[0]) . "', '{$fn[1]}'])";
        }

        return 'callable(' . spl_object_hash($fn) . ')';
    }
}
vendor/guzzlehttp/guzzle/src/Middleware.php000064400000023367151327705700015200 0ustar00<?php
namespace WPvividGuzzleHttp;

use WPvividGuzzleHttp\Cookie\CookieJarInterface;
use WPvividGuzzleHttp\Exception\RequestException;
use WPvividGuzzleHttp\Promise\RejectedPromise;
use WPvividGuzzleHttp\Psr7;
use WPvividPsr\Http\Message\ResponseInterface;
use WPvividPsr\Log\LoggerInterface;
use WPvividPsr\Log\LogLevel;

/**
 * Functions used to create and wrap handlers with handler middleware.
 */
final class Middleware
{
    /**
     * Middleware that adds cookies to requests.
     *
     * The options array must be set to a CookieJarInterface in order to use
     * cookies. This is typically handled for you by a client.
     *
     * @return callable Returns a function that accepts the next handler.
     */
    public static function cookies()
    {
        return function (callable $handler) {
            return function ($request, array $options) use ($handler) {
                if (empty($options['cookies'])) {
                    return $handler($request, $options);
                } elseif (!($options['cookies'] instanceof CookieJarInterface)) {
                    throw new \InvalidArgumentException('cookies must be an instance of WPvividGuzzleHttp\Cookie\CookieJarInterface');
                }
                $cookieJar = $options['cookies'];
                $request = $cookieJar->withCookieHeader($request);
                return $handler($request, $options)
                    ->then(
                        function ($response) use ($cookieJar, $request) {
                            $cookieJar->extractCookies($request, $response);
                            return $response;
                        }
                );
            };
        };
    }

    /**
     * Middleware that throws exceptions for 4xx or 5xx responses when the
     * "http_error" request option is set to true.
     *
     * @return callable Returns a function that accepts the next handler.
     */
    public static function httpErrors()
    {
        return function (callable $handler) {
            return function ($request, array $options) use ($handler) {
                if (empty($options['http_errors'])) {
                    return $handler($request, $options);
                }
                return $handler($request, $options)->then(
                    function (ResponseInterface $response) use ($request, $handler) {
                        $code = $response->getStatusCode();
                        if ($code < 400) {
                            return $response;
                        }
                        throw RequestException::create($request, $response);
                    }
                );
            };
        };
    }

    /**
     * Middleware that pushes history data to an ArrayAccess container.
     *
     * @param array|\ArrayAccess $container Container to hold the history (by reference).
     *
     * @return callable Returns a function that accepts the next handler.
     * @throws \InvalidArgumentException if container is not an array or ArrayAccess.
     */
    public static function history(&$container)
    {
        if (!is_array($container) && !$container instanceof \ArrayAccess) {
            throw new \InvalidArgumentException('history container must be an array or object implementing ArrayAccess');
        }

        return function (callable $handler) use (&$container) {
            return function ($request, array $options) use ($handler, &$container) {
                return $handler($request, $options)->then(
                    function ($value) use ($request, &$container, $options) {
                        $container[] = [
                            'request'  => $request,
                            'response' => $value,
                            'error'    => null,
                            'options'  => $options
                        ];
                        return $value;
                    },
                    function ($reason) use ($request, &$container, $options) {
                        $container[] = [
                            'request'  => $request,
                            'response' => null,
                            'error'    => $reason,
                            'options'  => $options
                        ];
                        return \WPvividGuzzleHttp\Promise\rejection_for($reason);
                    }
                );
            };
        };
    }

    /**
     * Middleware that invokes a callback before and after sending a request.
     *
     * The provided listener cannot modify or alter the response. It simply
     * "taps" into the chain to be notified before returning the promise. The
     * before listener accepts a request and options array, and the after
     * listener accepts a request, options array, and response promise.
     *
     * @param callable $before Function to invoke before forwarding the request.
     * @param callable $after  Function invoked after forwarding.
     *
     * @return callable Returns a function that accepts the next handler.
     */
    public static function tap(callable $before = null, callable $after = null)
    {
        return function (callable $handler) use ($before, $after) {
            return function ($request, array $options) use ($handler, $before, $after) {
                if ($before) {
                    $before($request, $options);
                }
                $response = $handler($request, $options);
                if ($after) {
                    $after($request, $options, $response);
                }
                return $response;
            };
        };
    }

    /**
     * Middleware that handles request redirects.
     *
     * @return callable Returns a function that accepts the next handler.
     */
    public static function redirect()
    {
        return function (callable $handler) {
            return new RedirectMiddleware($handler);
        };
    }

    /**
     * Middleware that retries requests based on the boolean result of
     * invoking the provided "decider" function.
     *
     * If no delay function is provided, a simple implementation of exponential
     * backoff will be utilized.
     *
     * @param callable $decider Function that accepts the number of retries,
     *                          a request, [response], and [exception] and
     *                          returns true if the request is to be retried.
     * @param callable $delay   Function that accepts the number of retries and
     *                          returns the number of milliseconds to delay.
     *
     * @return callable Returns a function that accepts the next handler.
     */
    public static function retry(callable $decider, callable $delay = null)
    {
        return function (callable $handler) use ($decider, $delay) {
            return new RetryMiddleware($decider, $handler, $delay);
        };
    }

    /**
     * Middleware that logs requests, responses, and errors using a message
     * formatter.
     *
     * @param LoggerInterface  $logger Logs messages.
     * @param MessageFormatter $formatter Formatter used to create message strings.
     * @param string           $logLevel Level at which to log requests.
     *
     * @return callable Returns a function that accepts the next handler.
     */
    public static function log(LoggerInterface $logger, MessageFormatter $formatter, $logLevel = LogLevel::INFO)
    {
        return function (callable $handler) use ($logger, $formatter, $logLevel) {
            return function ($request, array $options) use ($handler, $logger, $formatter, $logLevel) {
                return $handler($request, $options)->then(
                    function ($response) use ($logger, $request, $formatter, $logLevel) {
                        $message = $formatter->format($request, $response);
                        $logger->log($logLevel, $message);
                        return $response;
                    },
                    function ($reason) use ($logger, $request, $formatter) {
                        $response = $reason instanceof RequestException
                            ? $reason->getResponse()
                            : null;
                        $message = $formatter->format($request, $response, $reason);
                        $logger->notice($message);
                        return \WPvividGuzzleHttp\Promise\rejection_for($reason);
                    }
                );
            };
        };
    }

    /**
     * This middleware adds a default content-type if possible, a default
     * content-length or transfer-encoding header, and the expect header.
     *
     * @return callable
     */
    public static function prepareBody()
    {
        return function (callable $handler) {
            return new PrepareBodyMiddleware($handler);
        };
    }

    /**
     * Middleware that applies a map function to the request before passing to
     * the next handler.
     *
     * @param callable $fn Function that accepts a RequestInterface and returns
     *                     a RequestInterface.
     * @return callable
     */
    public static function mapRequest(callable $fn)
    {
        return function (callable $handler) use ($fn) {
            return function ($request, array $options) use ($handler, $fn) {
                return $handler($fn($request), $options);
            };
        };
    }

    /**
     * Middleware that applies a map function to the resolved promise's
     * response.
     *
     * @param callable $fn Function that accepts a ResponseInterface and
     *                     returns a ResponseInterface.
     * @return callable
     */
    public static function mapResponse(callable $fn)
    {
        return function (callable $handler) use ($fn) {
            return function ($request, array $options) use ($handler, $fn) {
                return $handler($request, $options)->then($fn);
            };
        };
    }
}
vendor/guzzlehttp/guzzle/src/RequestOptions.php000064400000023460151327705700016121 0ustar00<?php
namespace WPvividGuzzleHttp;

/**
 * This class contains a list of built-in Guzzle request options.
 *
 * More documentation for each option can be found at http://guzzlephp.org/.
 *
 * @link http://docs.guzzlephp.org/en/v6/request-options.html
 */
final class RequestOptions
{
    /**
     * allow_redirects: (bool|array) Controls redirect behavior. Pass false
     * to disable redirects, pass true to enable redirects, pass an
     * associative to provide custom redirect settings. Defaults to "false".
     * This option only works if your handler has the RedirectMiddleware. When
     * passing an associative array, you can provide the following key value
     * pairs:
     *
     * - max: (int, default=5) maximum number of allowed redirects.
     * - strict: (bool, default=false) Set to true to use strict redirects
     *   meaning redirect POST requests with POST requests vs. doing what most
     *   browsers do which is redirect POST requests with GET requests
     * - referer: (bool, default=true) Set to false to disable the Referer
     *   header.
     * - protocols: (array, default=['http', 'https']) Allowed redirect
     *   protocols.
     * - on_redirect: (callable) PHP callable that is invoked when a redirect
     *   is encountered. The callable is invoked with the request, the redirect
     *   response that was received, and the effective URI. Any return value
     *   from the on_redirect function is ignored.
     */
    const ALLOW_REDIRECTS = 'allow_redirects';

    /**
     * auth: (array) Pass an array of HTTP authentication parameters to use
     * with the request. The array must contain the username in index [0],
     * the password in index [1], and you can optionally provide a built-in
     * authentication type in index [2]. Pass null to disable authentication
     * for a request.
     */
    const AUTH = 'auth';

    /**
     * body: (resource|string|null|int|float|StreamInterface|callable|\Iterator)
     * Body to send in the request.
     */
    const BODY = 'body';

    /**
     * cert: (string|array) Set to a string to specify the path to a file
     * containing a PEM formatted SSL client side certificate. If a password
     * is required, then set cert to an array containing the path to the PEM
     * file in the first array element followed by the certificate password
     * in the second array element.
     */
    const CERT = 'cert';

    /**
     * cookies: (bool|GuzzleHttp\Cookie\CookieJarInterface, default=false)
     * Specifies whether or not cookies are used in a request or what cookie
     * jar to use or what cookies to send. This option only works if your
     * handler has the `cookie` middleware. Valid values are `false` and
     * an instance of {@see GuzzleHttp\Cookie\CookieJarInterface}.
     */
    const COOKIES = 'cookies';

    /**
     * connect_timeout: (float, default=0) Float describing the number of
     * seconds to wait while trying to connect to a server. Use 0 to wait
     * indefinitely (the default behavior).
     */
    const CONNECT_TIMEOUT = 'connect_timeout';

    /**
     * debug: (bool|resource) Set to true or set to a PHP stream returned by
     * fopen()  enable debug output with the HTTP handler used to send a
     * request.
     */
    const DEBUG = 'debug';

    /**
     * decode_content: (bool, default=true) Specify whether or not
     * Content-Encoding responses (gzip, deflate, etc.) are automatically
     * decoded.
     */
    const DECODE_CONTENT = 'decode_content';

    /**
     * delay: (int) The amount of time to delay before sending in milliseconds.
     */
    const DELAY = 'delay';

    /**
     * expect: (bool|integer) Controls the behavior of the
     * "Expect: 100-Continue" header.
     *
     * Set to `true` to enable the "Expect: 100-Continue" header for all
     * requests that sends a body. Set to `false` to disable the
     * "Expect: 100-Continue" header for all requests. Set to a number so that
     * the size of the payload must be greater than the number in order to send
     * the Expect header. Setting to a number will send the Expect header for
     * all requests in which the size of the payload cannot be determined or
     * where the body is not rewindable.
     *
     * By default, Guzzle will add the "Expect: 100-Continue" header when the
     * size of the body of a request is greater than 1 MB and a request is
     * using HTTP/1.1.
     */
    const EXPECT = 'expect';

    /**
     * form_params: (array) Associative array of form field names to values
     * where each value is a string or array of strings. Sets the Content-Type
     * header to application/x-www-form-urlencoded when no Content-Type header
     * is already present.
     */
    const FORM_PARAMS = 'form_params';

    /**
     * headers: (array) Associative array of HTTP headers. Each value MUST be
     * a string or array of strings.
     */
    const HEADERS = 'headers';

    /**
     * http_errors: (bool, default=true) Set to false to disable exceptions
     * when a non- successful HTTP response is received. By default,
     * exceptions will be thrown for 4xx and 5xx responses. This option only
     * works if your handler has the `httpErrors` middleware.
     */
    const HTTP_ERRORS = 'http_errors';

    /**
     * json: (mixed) Adds JSON data to a request. The provided value is JSON
     * encoded and a Content-Type header of application/json will be added to
     * the request if no Content-Type header is already present.
     */
    const JSON = 'json';

    /**
     * multipart: (array) Array of associative arrays, each containing a
     * required "name" key mapping to the form field, name, a required
     * "contents" key mapping to a StreamInterface|resource|string, an
     * optional "headers" associative array of custom headers, and an
     * optional "filename" key mapping to a string to send as the filename in
     * the part. If no "filename" key is present, then no "filename" attribute
     * will be added to the part.
     */
    const MULTIPART = 'multipart';

    /**
     * on_headers: (callable) A callable that is invoked when the HTTP headers
     * of the response have been received but the body has not yet begun to
     * download.
     */
    const ON_HEADERS = 'on_headers';

    /**
     * on_stats: (callable) allows you to get access to transfer statistics of
     * a request and access the lower level transfer details of the handler
     * associated with your client. ``on_stats`` is a callable that is invoked
     * when a handler has finished sending a request. The callback is invoked
     * with transfer statistics about the request, the response received, or
     * the error encountered. Included in the data is the total amount of time
     * taken to send the request.
     */
    const ON_STATS = 'on_stats';

    /**
     * progress: (callable) Defines a function to invoke when transfer
     * progress is made. The function accepts the following positional
     * arguments: the total number of bytes expected to be downloaded, the
     * number of bytes downloaded so far, the number of bytes expected to be
     * uploaded, the number of bytes uploaded so far.
     */
    const PROGRESS = 'progress';

    /**
     * proxy: (string|array) Pass a string to specify an HTTP proxy, or an
     * array to specify different proxies for different protocols (where the
     * key is the protocol and the value is a proxy string).
     */
    const PROXY = 'proxy';

    /**
     * query: (array|string) Associative array of query string values to add
     * to the request. This option uses PHP's http_build_query() to create
     * the string representation. Pass a string value if you need more
     * control than what this method provides
     */
    const QUERY = 'query';

    /**
     * sink: (resource|string|StreamInterface) Where the data of the
     * response is written to. Defaults to a PHP temp stream. Providing a
     * string will write data to a file by the given name.
     */
    const SINK = 'sink';

    /**
     * synchronous: (bool) Set to true to inform HTTP handlers that you intend
     * on waiting on the response. This can be useful for optimizations. Note
     * that a promise is still returned if you are using one of the async
     * client methods.
     */
    const SYNCHRONOUS = 'synchronous';

    /**
     * ssl_key: (array|string) Specify the path to a file containing a private
     * SSL key in PEM format. If a password is required, then set to an array
     * containing the path to the SSL key in the first array element followed
     * by the password required for the certificate in the second element.
     */
    const SSL_KEY = 'ssl_key';

    /**
     * stream: Set to true to attempt to stream a response rather than
     * download it all up-front.
     */
    const STREAM = 'stream';

    /**
     * verify: (bool|string, default=true) Describes the SSL certificate
     * verification behavior of a request. Set to true to enable SSL
     * certificate verification using the system CA bundle when available
     * (the default). Set to false to disable certificate verification (this
     * is insecure!). Set to a string to provide the path to a CA bundle on
     * disk to enable verification using a custom certificate.
     */
    const VERIFY = 'verify';

    /**
     * timeout: (float, default=0) Float describing the timeout of the
     * request in seconds. Use 0 to wait indefinitely (the default behavior).
     */
    const TIMEOUT = 'timeout';

    /**
     * read_timeout: (float, default=default_socket_timeout ini setting) Float describing
     * the body read timeout, for stream requests.
     */
    const READ_TIMEOUT = 'read_timeout';

    /**
     * version: (float) Specifies the HTTP protocol version to attempt to use.
     */
    const VERSION = 'version';

    /**
     * force_ip_resolve: (bool) Force client to use only ipv4 or ipv6 protocol
     */
    const FORCE_IP_RESOLVE = 'force_ip_resolve';
}
vendor/guzzlehttp/guzzle/src/TransferStats.php000064400000006051151327705700015715 0ustar00<?php
namespace WPvividGuzzleHttp;

use WPvividPsr\Http\Message\RequestInterface;
use WPvividPsr\Http\Message\ResponseInterface;
use WPvividPsr\Http\Message\UriInterface;

/**
 * Represents data at the point after it was transferred either successfully
 * or after a network error.
 */
final class TransferStats
{
    private $request;
    private $response;
    private $transferTime;
    private $handlerStats;
    private $handlerErrorData;

    /**
     * @param RequestInterface  $request          Request that was sent.
     * @param ResponseInterface $response         Response received (if any)
     * @param null              $transferTime     Total handler transfer time.
     * @param mixed             $handlerErrorData Handler error data.
     * @param array             $handlerStats     Handler specific stats.
     */
    public function __construct(
        RequestInterface $request,
        ResponseInterface $response = null,
        $transferTime = null,
        $handlerErrorData = null,
        $handlerStats = []
    ) {
        $this->request = $request;
        $this->response = $response;
        $this->transferTime = $transferTime;
        $this->handlerErrorData = $handlerErrorData;
        $this->handlerStats = $handlerStats;
    }

    /**
     * @return RequestInterface
     */
    public function getRequest()
    {
        return $this->request;
    }

    /**
     * Returns the response that was received (if any).
     *
     * @return ResponseInterface|null
     */
    public function getResponse()
    {
        return $this->response;
    }

    /**
     * Returns true if a response was received.
     *
     * @return bool
     */
    public function hasResponse()
    {
        return $this->response !== null;
    }

    /**
     * Gets handler specific error data.
     *
     * This might be an exception, a integer representing an error code, or
     * anything else. Relying on this value assumes that you know what handler
     * you are using.
     *
     * @return mixed
     */
    public function getHandlerErrorData()
    {
        return $this->handlerErrorData;
    }

    /**
     * Get the effective URI the request was sent to.
     *
     * @return UriInterface
     */
    public function getEffectiveUri()
    {
        return $this->request->getUri();
    }

    /**
     * Get the estimated time the request was being transferred by the handler.
     *
     * @return float Time in seconds.
     */
    public function getTransferTime()
    {
        return $this->transferTime;
    }

    /**
     * Gets an array of all of the handler specific transfer data.
     *
     * @return array
     */
    public function getHandlerStats()
    {
        return $this->handlerStats;
    }

    /**
     * Get a specific handler statistic from the handler by name.
     *
     * @param string $stat Handler specific transfer stat to retrieve.
     *
     * @return mixed|null
     */
    public function getHandlerStat($stat)
    {
        return isset($this->handlerStats[$stat])
            ? $this->handlerStats[$stat]
            : null;
    }
}
vendor/guzzlehttp/guzzle/src/UriTemplate.php000064400000017675151327705700015363 0ustar00<?php
namespace WPvividGuzzleHttp;

/**
 * Expands URI templates. Userland implementation of PECL uri_template.
 *
 * @link http://tools.ietf.org/html/rfc6570
 */
class UriTemplate
{
    /** @var string URI template */
    private $template;

    /** @var array Variables to use in the template expansion */
    private $variables;

    /** @var array Hash for quick operator lookups */
    private static $operatorHash = [
        ''  => ['prefix' => '',  'joiner' => ',', 'query' => false],
        '+' => ['prefix' => '',  'joiner' => ',', 'query' => false],
        '#' => ['prefix' => '#', 'joiner' => ',', 'query' => false],
        '.' => ['prefix' => '.', 'joiner' => '.', 'query' => false],
        '/' => ['prefix' => '/', 'joiner' => '/', 'query' => false],
        ';' => ['prefix' => ';', 'joiner' => ';', 'query' => true],
        '?' => ['prefix' => '?', 'joiner' => '&', 'query' => true],
        '&' => ['prefix' => '&', 'joiner' => '&', 'query' => true]
    ];

    /** @var array Delimiters */
    private static $delims = [':', '/', '?', '#', '[', ']', '@', '!', '$',
        '&', '\'', '(', ')', '*', '+', ',', ';', '='];

    /** @var array Percent encoded delimiters */
    private static $delimsPct = ['%3A', '%2F', '%3F', '%23', '%5B', '%5D',
        '%40', '%21', '%24', '%26', '%27', '%28', '%29', '%2A', '%2B', '%2C',
        '%3B', '%3D'];

    public function expand($template, array $variables)
    {
        if (false === strpos($template, '{')) {
            return $template;
        }

        $this->template = $template;
        $this->variables = $variables;

        return preg_replace_callback(
            '/\{([^\}]+)\}/',
            [$this, 'expandMatch'],
            $this->template
        );
    }

    /**
     * Parse an expression into parts
     *
     * @param string $expression Expression to parse
     *
     * @return array Returns an associative array of parts
     */
    private function parseExpression($expression)
    {
        $result = [];

        if (isset(self::$operatorHash[$expression[0]])) {
            $result['operator'] = $expression[0];
            $expression = substr($expression, 1);
        } else {
            $result['operator'] = '';
        }

        foreach (explode(',', $expression) as $value) {
            $value = trim($value);
            $varspec = [];
            if ($colonPos = strpos($value, ':')) {
                $varspec['value'] = substr($value, 0, $colonPos);
                $varspec['modifier'] = ':';
                $varspec['position'] = (int) substr($value, $colonPos + 1);
            } elseif (substr($value, -1) === '*') {
                $varspec['modifier'] = '*';
                $varspec['value'] = substr($value, 0, -1);
            } else {
                $varspec['value'] = (string) $value;
                $varspec['modifier'] = '';
            }
            $result['values'][] = $varspec;
        }

        return $result;
    }

    /**
     * Process an expansion
     *
     * @param array $matches Matches met in the preg_replace_callback
     *
     * @return string Returns the replacement string
     */
    private function expandMatch(array $matches)
    {
        static $rfc1738to3986 = ['+' => '%20', '%7e' => '~'];

        $replacements = [];
        $parsed = self::parseExpression($matches[1]);
        $prefix = self::$operatorHash[$parsed['operator']]['prefix'];
        $joiner = self::$operatorHash[$parsed['operator']]['joiner'];
        $useQuery = self::$operatorHash[$parsed['operator']]['query'];

        foreach ($parsed['values'] as $value) {
            if (!isset($this->variables[$value['value']])) {
                continue;
            }

            $variable = $this->variables[$value['value']];
            $actuallyUseQuery = $useQuery;
            $expanded = '';

            if (is_array($variable)) {
                $isAssoc = $this->isAssoc($variable);
                $kvp = [];
                foreach ($variable as $key => $var) {
                    if ($isAssoc) {
                        $key = rawurlencode($key);
                        $isNestedArray = is_array($var);
                    } else {
                        $isNestedArray = false;
                    }

                    if (!$isNestedArray) {
                        $var = rawurlencode($var);
                        if ($parsed['operator'] === '+' ||
                            $parsed['operator'] === '#'
                        ) {
                            $var = $this->decodeReserved($var);
                        }
                    }

                    if ($value['modifier'] === '*') {
                        if ($isAssoc) {
                            if ($isNestedArray) {
                                // Nested arrays must allow for deeply nested
                                // structures.
                                $var = strtr(
                                    http_build_query([$key => $var]),
                                    $rfc1738to3986
                                );
                            } else {
                                $var = $key . '=' . $var;
                            }
                        } elseif ($key > 0 && $actuallyUseQuery) {
                            $var = $value['value'] . '=' . $var;
                        }
                    }

                    $kvp[$key] = $var;
                }

                if (empty($variable)) {
                    $actuallyUseQuery = false;
                } elseif ($value['modifier'] === '*') {
                    $expanded = implode($joiner, $kvp);
                    if ($isAssoc) {
                        // Don't prepend the value name when using the explode
                        // modifier with an associative array.
                        $actuallyUseQuery = false;
                    }
                } else {
                    if ($isAssoc) {
                        // When an associative array is encountered and the
                        // explode modifier is not set, then the result must be
                        // a comma separated list of keys followed by their
                        // respective values.
                        foreach ($kvp as $k => &$v) {
                            $v = $k . ',' . $v;
                        }
                    }
                    $expanded = implode(',', $kvp);
                }
            } else {
                if ($value['modifier'] === ':') {
                    $variable = substr($variable, 0, $value['position']);
                }
                $expanded = rawurlencode($variable);
                if ($parsed['operator'] === '+' || $parsed['operator'] === '#') {
                    $expanded = $this->decodeReserved($expanded);
                }
            }

            if ($actuallyUseQuery) {
                if (!$expanded && $joiner !== '&') {
                    $expanded = $value['value'];
                } else {
                    $expanded = $value['value'] . '=' . $expanded;
                }
            }

            $replacements[] = $expanded;
        }

        $ret = implode($joiner, $replacements);
        if ($ret && $prefix) {
            return $prefix . $ret;
        }

        return $ret;
    }

    /**
     * Determines if an array is associative.
     *
     * This makes the assumption that input arrays are sequences or hashes.
     * This assumption is a tradeoff for accuracy in favor of speed, but it
     * should work in almost every case where input is supplied for a URI
     * template.
     *
     * @param array $array Array to check
     *
     * @return bool
     */
    private function isAssoc(array $array)
    {
        return $array && array_keys($array)[0] !== 0;
    }

    /**
     * Removes percent encoding on reserved characters (used with + and #
     * modifiers).
     *
     * @param string $string String to fix
     *
     * @return string
     */
    private function decodeReserved($string)
    {
        return str_replace(self::$delimsPct, self::$delims, $string);
    }
}
vendor/guzzlehttp/guzzle/src/PrepareBodyMiddleware.php000064400000006160151327705700017325 0ustar00<?php
namespace WPvividGuzzleHttp;

use WPvividGuzzleHttp\Promise\PromiseInterface;
use WPvividGuzzleHttp\Psr7;
use WPvividPsr\Http\Message\RequestInterface;

/**
 * Prepares requests that contain a body, adding the Content-Length,
 * Content-Type, and Expect headers.
 */
class PrepareBodyMiddleware
{
    /** @var callable  */
    private $nextHandler;

    /**
     * @param callable $nextHandler Next handler to invoke.
     */
    public function __construct(callable $nextHandler)
    {
        $this->nextHandler = $nextHandler;
    }

    /**
     * @param RequestInterface $request
     * @param array            $options
     *
     * @return PromiseInterface
     */
    public function __invoke(RequestInterface $request, array $options)
    {
        $fn = $this->nextHandler;

        // Don't do anything if the request has no body.
        if ($request->getBody()->getSize() === 0) {
            return $fn($request, $options);
        }

        $modify = [];

        // Add a default content-type if possible.
        if (!$request->hasHeader('Content-Type')) {
            if ($uri = $request->getBody()->getMetadata('uri')) {
                if ($type = Psr7\mimetype_from_filename($uri)) {
                    $modify['set_headers']['Content-Type'] = $type;
                }
            }
        }

        // Add a default content-length or transfer-encoding header.
        if (!$request->hasHeader('Content-Length')
            && !$request->hasHeader('Transfer-Encoding')
        ) {
            $size = $request->getBody()->getSize();
            if ($size !== null) {
                $modify['set_headers']['Content-Length'] = $size;
            } else {
                $modify['set_headers']['Transfer-Encoding'] = 'chunked';
            }
        }

        // Add the expect header if needed.
        $this->addExpectHeader($request, $options, $modify);

        return $fn(Psr7\modify_request($request, $modify), $options);
    }

    private function addExpectHeader(
        RequestInterface $request,
        array $options,
        array &$modify
    ) {
        // Determine if the Expect header should be used
        if ($request->hasHeader('Expect')) {
            return;
        }

        $expect = isset($options['expect']) ? $options['expect'] : null;

        // Return if disabled or if you're not using HTTP/1.1 or HTTP/2.0
        if ($expect === false || $request->getProtocolVersion() < 1.1) {
            return;
        }

        // The expect header is unconditionally enabled
        if ($expect === true) {
            $modify['set_headers']['Expect'] = '100-Continue';
            return;
        }

        // By default, send the expect header when the payload is > 1mb
        if ($expect === null) {
            $expect = 1048576;
        }

        // Always add if the body cannot be rewound, the size cannot be
        // determined, or the size is greater than the cutoff threshold
        $body = $request->getBody();
        $size = $body->getSize();

        if ($size === null || $size >= (int) $expect || !$body->isSeekable()) {
            $modify['set_headers']['Expect'] = '100-Continue';
        }
    }
}
vendor/guzzlehttp/guzzle/src/functions.php000064400000023311151327705700015120 0ustar00<?php
namespace WPvividGuzzleHttp;

use WPvividGuzzleHttp\Handler\CurlHandler;
use WPvividGuzzleHttp\Handler\CurlMultiHandler;
use WPvividGuzzleHttp\Handler\Proxy;
use WPvividGuzzleHttp\Handler\StreamHandler;

/**
 * Expands a URI template
 *
 * @param string $template  URI template
 * @param array  $variables Template variables
 *
 * @return string
 */
function uri_template($template, array $variables)
{
    if (extension_loaded('uri_template')) {
        // @codeCoverageIgnoreStart
        return \uri_template($template, $variables);
        // @codeCoverageIgnoreEnd
    }

    static $uriTemplate;
    if (!$uriTemplate) {
        $uriTemplate = new UriTemplate();
    }

    return $uriTemplate->expand($template, $variables);
}

/**
 * Debug function used to describe the provided value type and class.
 *
 * @param mixed $input
 *
 * @return string Returns a string containing the type of the variable and
 *                if a class is provided, the class name.
 */
function describe_type($input)
{
    switch (gettype($input)) {
        case 'object':
            return 'object(' . get_class($input) . ')';
        case 'array':
            return 'array(' . count($input) . ')';
        default:
            ob_start();
            var_dump($input);
            // normalize float vs double
            return str_replace('double(', 'float(', rtrim(ob_get_clean()));
    }
}

/**
 * Parses an array of header lines into an associative array of headers.
 *
 * @param array $lines Header lines array of strings in the following
 *                     format: "Name: Value"
 * @return array
 */
function headers_from_lines($lines)
{
    $headers = [];

    foreach ($lines as $line) {
        $parts = explode(':', $line, 2);
        $headers[trim($parts[0])][] = isset($parts[1])
            ? trim($parts[1])
            : null;
    }

    return $headers;
}

/**
 * Returns a debug stream based on the provided variable.
 *
 * @param mixed $value Optional value
 *
 * @return resource
 */
function debug_resource($value = null)
{
    if (is_resource($value)) {
        return $value;
    } elseif (defined('STDOUT')) {
        return STDOUT;
    }

    return fopen('php://output', 'w');
}

/**
 * Chooses and creates a default handler to use based on the environment.
 *
 * The returned handler is not wrapped by any default middlewares.
 *
 * @throws \RuntimeException if no viable Handler is available.
 * @return callable Returns the best handler for the given system.
 */
function choose_handler()
{
    $handler = null;
    if (function_exists('curl_multi_exec') && function_exists('curl_exec')) {
        $handler = Proxy::wrapSync(new CurlMultiHandler(), new CurlHandler());
    } elseif (function_exists('curl_exec')) {
        $handler = new CurlHandler();
    } elseif (function_exists('curl_multi_exec')) {
        $handler = new CurlMultiHandler();
    }

    if (ini_get('allow_url_fopen')) {
        $handler = $handler
            ? Proxy::wrapStreaming($handler, new StreamHandler())
            : new StreamHandler();
    } elseif (!$handler) {
        throw new \RuntimeException('WPvividGuzzleHttp requires cURL, the '
            . 'allow_url_fopen ini setting, or a custom HTTP handler.');
    }

    return $handler;
}

/**
 * Get the default User-Agent string to use with Guzzle
 *
 * @return string
 */
function default_user_agent()
{
    static $defaultAgent = '';

    if (!$defaultAgent) {
        $defaultAgent = 'WPvividGuzzleHttp/' . Client::VERSION;
        if (extension_loaded('curl') && function_exists('curl_version')) {
            $defaultAgent .= ' curl/' . \curl_version()['version'];
        }
        $defaultAgent .= ' PHP/' . PHP_VERSION;
    }

    return $defaultAgent;
}

/**
 * Returns the default cacert bundle for the current system.
 *
 * First, the openssl.cafile and curl.cainfo php.ini settings are checked.
 * If those settings are not configured, then the common locations for
 * bundles found on Red Hat, CentOS, Fedora, Ubuntu, Debian, FreeBSD, OS X
 * and Windows are checked. If any of these file locations are found on
 * disk, they will be utilized.
 *
 * Note: the result of this function is cached for subsequent calls.
 *
 * @return string
 * @throws \RuntimeException if no bundle can be found.
 */
function default_ca_bundle()
{
    static $cached = null;
    static $cafiles = [
        // Red Hat, CentOS, Fedora (provided by the ca-certificates package)
        '/etc/pki/tls/certs/ca-bundle.crt',
        // Ubuntu, Debian (provided by the ca-certificates package)
        '/etc/ssl/certs/ca-certificates.crt',
        // FreeBSD (provided by the ca_root_nss package)
        '/usr/local/share/certs/ca-root-nss.crt',
        // SLES 12 (provided by the ca-certificates package)
        '/var/lib/ca-certificates/ca-bundle.pem',
        // OS X provided by homebrew (using the default path)
        '/usr/local/etc/openssl/cert.pem',
        // Google app engine
        '/etc/ca-certificates.crt',
        // Windows?
        'C:\\windows\\system32\\curl-ca-bundle.crt',
        'C:\\windows\\curl-ca-bundle.crt',
    ];

    if ($cached) {
        return $cached;
    }

    if ($ca = ini_get('openssl.cafile')) {
        return $cached = $ca;
    }

    if ($ca = ini_get('curl.cainfo')) {
        return $cached = $ca;
    }

    foreach ($cafiles as $filename) {
        if (file_exists($filename)) {
            return $cached = $filename;
        }
    }

    throw new \RuntimeException(<<< EOT
No system CA bundle could be found in any of the the common system locations.
PHP versions earlier than 5.6 are not properly configured to use the system's
CA bundle by default. In order to verify peer certificates, you will need to
supply the path on disk to a certificate bundle to the 'verify' request
option: http://docs.guzzlephp.org/en/latest/clients.html#verify. If you do not
need a specific certificate bundle, then Mozilla provides a commonly used CA
bundle which can be downloaded here (provided by the maintainer of cURL):
https://raw.githubusercontent.com/bagder/ca-bundle/master/ca-bundle.crt. Once
you have a CA bundle available on disk, you can set the 'openssl.cafile' PHP
ini setting to point to the path to the file, allowing you to omit the 'verify'
request option. See http://curl.haxx.se/docs/sslcerts.html for more
information.
EOT
    );
}

/**
 * Creates an associative array of lowercase header names to the actual
 * header casing.
 *
 * @param array $headers
 *
 * @return array
 */
function normalize_header_keys(array $headers)
{
    $result = [];
    foreach (array_keys($headers) as $key) {
        $result[strtolower($key)] = $key;
    }

    return $result;
}

/**
 * Returns true if the provided host matches any of the no proxy areas.
 *
 * This method will strip a port from the host if it is present. Each pattern
 * can be matched with an exact match (e.g., "foo.com" == "foo.com") or a
 * partial match: (e.g., "foo.com" == "baz.foo.com" and ".foo.com" ==
 * "baz.foo.com", but ".foo.com" != "foo.com").
 *
 * Areas are matched in the following cases:
 * 1. "*" (without quotes) always matches any hosts.
 * 2. An exact match.
 * 3. The area starts with "." and the area is the last part of the host. e.g.
 *    '.mit.edu' will match any host that ends with '.mit.edu'.
 *
 * @param string $host         Host to check against the patterns.
 * @param array  $noProxyArray An array of host patterns.
 *
 * @return bool
 */
function is_host_in_noproxy($host, array $noProxyArray)
{
    if (strlen($host) === 0) {
        throw new \InvalidArgumentException('Empty host provided');
    }

    // Strip port if present.
    if (strpos($host, ':')) {
        $host = explode($host, ':', 2)[0];
    }

    foreach ($noProxyArray as $area) {
        // Always match on wildcards.
        if ($area === '*') {
            return true;
        } elseif (empty($area)) {
            // Don't match on empty values.
            continue;
        } elseif ($area === $host) {
            // Exact matches.
            return true;
        } else {
            // Special match if the area when prefixed with ".". Remove any
            // existing leading "." and add a new leading ".".
            $area = '.' . ltrim($area, '.');
            if (substr($host, -(strlen($area))) === $area) {
                return true;
            }
        }
    }

    return false;
}

/**
 * Wrapper for json_decode that throws when an error occurs.
 *
 * @param string $json    JSON data to parse
 * @param bool $assoc     When true, returned objects will be converted
 *                        into associative arrays.
 * @param int    $depth   User specified recursion depth.
 * @param int    $options Bitmask of JSON decode options.
 *
 * @return mixed
 * @throws \InvalidArgumentException if the JSON cannot be decoded.
 * @link http://www.php.net/manual/en/function.json-decode.php
 */
function json_decode($json, $assoc = false, $depth = 512, $options = 0)
{
    $data = \json_decode($json, $assoc, $depth, $options);
    if (JSON_ERROR_NONE !== json_last_error()) {
        throw new \InvalidArgumentException(
            'json_decode error: ' . json_last_error_msg()
        );
    }

    return $data;
}

/**
 * Wrapper for JSON encoding that throws when an error occurs.
 *
 * @param mixed $value   The value being encoded
 * @param int    $options JSON encode option bitmask
 * @param int    $depth   Set the maximum depth. Must be greater than zero.
 *
 * @return string
 * @throws \InvalidArgumentException if the JSON cannot be encoded.
 * @link http://www.php.net/manual/en/function.json-encode.php
 */
function json_encode($value, $options = 0, $depth = 512)
{
    $json = \json_encode($value, $options, $depth);
    if (JSON_ERROR_NONE !== json_last_error()) {
        throw new \InvalidArgumentException(
            'json_encode error: ' . json_last_error_msg()
        );
    }

    return $json;
}
vendor/guzzlehttp/guzzle/src/ClientInterface.php000064400000005427151327705700016157 0ustar00<?php
namespace WPvividGuzzleHttp;

use WPvividGuzzleHttp\Promise\PromiseInterface;
use WPvividGuzzleHttp\Exception\GuzzleException;
use WPvividPsr\Http\Message\RequestInterface;
use WPvividPsr\Http\Message\ResponseInterface;
use WPvividPsr\Http\Message\UriInterface;

/**
 * Client interface for sending HTTP requests.
 */
interface ClientInterface
{
    const VERSION = '6.3.3';

    /**
     * Send an HTTP request.
     *
     * @param RequestInterface $request Request to send
     * @param array            $options Request options to apply to the given
     *                                  request and to the transfer.
     *
     * @return ResponseInterface
     * @throws GuzzleException
     */
    public function send(RequestInterface $request, array $options = []);

    /**
     * Asynchronously send an HTTP request.
     *
     * @param RequestInterface $request Request to send
     * @param array            $options Request options to apply to the given
     *                                  request and to the transfer.
     *
     * @return PromiseInterface
     */
    public function sendAsync(RequestInterface $request, array $options = []);

    /**
     * Create and send an HTTP request.
     *
     * Use an absolute path to override the base path of the client, or a
     * relative path to append to the base path of the client. The URL can
     * contain the query string as well.
     *
     * @param string              $method  HTTP method.
     * @param string|UriInterface $uri     URI object or string.
     * @param array               $options Request options to apply.
     *
     * @return ResponseInterface
     * @throws GuzzleException
     */
    public function request($method, $uri, array $options = []);

    /**
     * Create and send an asynchronous HTTP request.
     *
     * Use an absolute path to override the base path of the client, or a
     * relative path to append to the base path of the client. The URL can
     * contain the query string as well. Use an array to provide a URL
     * template and additional variables to use in the URL template expansion.
     *
     * @param string              $method  HTTP method
     * @param string|UriInterface $uri     URI object or string.
     * @param array               $options Request options to apply.
     *
     * @return PromiseInterface
     */
    public function requestAsync($method, $uri, array $options = []);

    /**
     * Get a client configuration option.
     *
     * These options include default request options of the client, a "handler"
     * (if utilized by the concrete client), and a "base_uri" if utilized by
     * the concrete client.
     *
     * @param string|null $option The config option to retrieve.
     *
     * @return mixed
     */
    public function getConfig($option = null);
}
vendor/guzzlehttp/guzzle/src/MessageFormatter.php000064400000016043151327705700016364 0ustar00<?php
namespace WPvividGuzzleHttp;

use WPvividPsr\Http\Message\MessageInterface;
use WPvividPsr\Http\Message\RequestInterface;
use WPvividPsr\Http\Message\ResponseInterface;

/**
 * Formats log messages using variable substitutions for requests, responses,
 * and other transactional data.
 *
 * The following variable substitutions are supported:
 *
 * - {request}:        Full HTTP request message
 * - {response}:       Full HTTP response message
 * - {ts}:             ISO 8601 date in GMT
 * - {date_iso_8601}   ISO 8601 date in GMT
 * - {date_common_log} Apache common log date using the configured timezone.
 * - {host}:           Host of the request
 * - {method}:         Method of the request
 * - {uri}:            URI of the request
 * - {version}:        Protocol version
 * - {target}:         Request target of the request (path + query + fragment)
 * - {hostname}:       Hostname of the machine that sent the request
 * - {code}:           Status code of the response (if available)
 * - {phrase}:         Reason phrase of the response  (if available)
 * - {error}:          Any error messages (if available)
 * - {req_header_*}:   Replace `*` with the lowercased name of a request header to add to the message
 * - {res_header_*}:   Replace `*` with the lowercased name of a response header to add to the message
 * - {req_headers}:    Request headers
 * - {res_headers}:    Response headers
 * - {req_body}:       Request body
 * - {res_body}:       Response body
 */
class MessageFormatter
{
    /**
     * Apache Common Log Format.
     * @link http://httpd.apache.org/docs/2.4/logs.html#common
     * @var string
     */
    const CLF = "{hostname} {req_header_User-Agent} - [{date_common_log}] \"{method} {target} HTTP/{version}\" {code} {res_header_Content-Length}";
    const DEBUG = ">>>>>>>>\n{request}\n<<<<<<<<\n{response}\n--------\n{error}";
    const SHORT = '[{ts}] "{method} {target} HTTP/{version}" {code}';

    /** @var string Template used to format log messages */
    private $template;

    /**
     * @param string $template Log message template
     */
    public function __construct($template = self::CLF)
    {
        $this->template = $template ?: self::CLF;
    }

    /**
     * Returns a formatted message string.
     *
     * @param RequestInterface  $request  Request that was sent
     * @param ResponseInterface $response Response that was received
     * @param \Exception        $error    Exception that was received
     *
     * @return string
     */
    public function format(
        RequestInterface $request,
        ResponseInterface $response = null,
        \Exception $error = null
    ) {
        $cache = [];

        return preg_replace_callback(
            '/{\s*([A-Za-z_\-\.0-9]+)\s*}/',
            function (array $matches) use ($request, $response, $error, &$cache) {
                if (isset($cache[$matches[1]])) {
                    return $cache[$matches[1]];
                }

                $result = '';
                switch ($matches[1]) {
                    case 'request':
                        $result = Psr7\str($request);
                        break;
                    case 'response':
                        $result = $response ? Psr7\str($response) : '';
                        break;
                    case 'req_headers':
                        $result = trim($request->getMethod()
                                . ' ' . $request->getRequestTarget())
                            . ' HTTP/' . $request->getProtocolVersion() . "\r\n"
                            . $this->headers($request);
                        break;
                    case 'res_headers':
                        $result = $response ?
                            sprintf(
                                'HTTP/%s %d %s',
                                $response->getProtocolVersion(),
                                $response->getStatusCode(),
                                $response->getReasonPhrase()
                            ) . "\r\n" . $this->headers($response)
                            : 'NULL';
                        break;
                    case 'req_body':
                        $result = $request->getBody();
                        break;
                    case 'res_body':
                        $result = $response ? $response->getBody() : 'NULL';
                        break;
                    case 'ts':
                    case 'date_iso_8601':
                        $result = gmdate('c');
                        break;
                    case 'date_common_log':
                        $result = date('d/M/Y:H:i:s O');
                        break;
                    case 'method':
                        $result = $request->getMethod();
                        break;
                    case 'version':
                        $result = $request->getProtocolVersion();
                        break;
                    case 'uri':
                    case 'url':
                        $result = $request->getUri();
                        break;
                    case 'target':
                        $result = $request->getRequestTarget();
                        break;
                    case 'req_version':
                        $result = $request->getProtocolVersion();
                        break;
                    case 'res_version':
                        $result = $response
                            ? $response->getProtocolVersion()
                            : 'NULL';
                        break;
                    case 'host':
                        $result = $request->getHeaderLine('Host');
                        break;
                    case 'hostname':
                        $result = gethostname();
                        break;
                    case 'code':
                        $result = $response ? $response->getStatusCode() : 'NULL';
                        break;
                    case 'phrase':
                        $result = $response ? $response->getReasonPhrase() : 'NULL';
                        break;
                    case 'error':
                        $result = $error ? $error->getMessage() : 'NULL';
                        break;
                    default:
                        // handle prefixed dynamic headers
                        if (strpos($matches[1], 'req_header_') === 0) {
                            $result = $request->getHeaderLine(substr($matches[1], 11));
                        } elseif (strpos($matches[1], 'res_header_') === 0) {
                            $result = $response
                                ? $response->getHeaderLine(substr($matches[1], 11))
                                : 'NULL';
                        }
                }

                $cache[$matches[1]] = $result;
                return $result;
            },
            $this->template
        );
    }

    private function headers(MessageInterface $message)
    {
        $result = '';
        foreach ($message->getHeaders() as $name => $values) {
            $result .= $name . ': ' . implode(', ', $values) . "\r\n";
        }

        return trim($result);
    }
}
vendor/guzzlehttp/guzzle/src/functions_include.php000064400000000247151327705700016626 0ustar00<?php

// Don't redefine the functions if included multiple times.
if (!function_exists('WPvividGuzzleHttp\uri_template')) {
    require __DIR__ . '/functions.php';
}
vendor/guzzlehttp/guzzle/src/Handler/CurlFactoryInterface.php000064400000001314151327705700020542 0ustar00<?php
namespace WPvividGuzzleHttp\Handler;

use WPvividPsr\Http\Message\RequestInterface;

interface CurlFactoryInterface
{
    /**
     * Creates a cURL handle resource.
     *
     * @param RequestInterface $request Request
     * @param array            $options Transfer options
     *
     * @return EasyHandle
     * @throws \RuntimeException when an option cannot be applied
     */
    public function create(RequestInterface $request, array $options);

    /**
     * Release an easy handle, allowing it to be reused or closed.
     *
     * This function must call unset on the easy handle's "handle" property.
     *
     * @param EasyHandle $easy
     */
    public function release(EasyHandle $easy);
}
vendor/guzzlehttp/guzzle/src/Handler/Proxy.php000064400000003404151327705700015607 0ustar00<?php
namespace WPvividGuzzleHttp\Handler;

use WPvividGuzzleHttp\RequestOptions;
use WPvividPsr\Http\Message\RequestInterface;

/**
 * Provides basic proxies for handlers.
 */
class Proxy
{
    /**
     * Sends synchronous requests to a specific handler while sending all other
     * requests to another handler.
     *
     * @param callable $default Handler used for normal responses
     * @param callable $sync    Handler used for synchronous responses.
     *
     * @return callable Returns the composed handler.
     */
    public static function wrapSync(
        callable $default,
        callable $sync
    ) {
        return function (RequestInterface $request, array $options) use ($default, $sync) {
            return empty($options[RequestOptions::SYNCHRONOUS])
                ? $default($request, $options)
                : $sync($request, $options);
        };
    }

    /**
     * Sends streaming requests to a streaming compatible handler while sending
     * all other requests to a default handler.
     *
     * This, for example, could be useful for taking advantage of the
     * performance benefits of curl while still supporting true streaming
     * through the StreamHandler.
     *
     * @param callable $default   Handler used for non-streaming responses
     * @param callable $streaming Handler used for streaming responses
     *
     * @return callable Returns the composed handler.
     */
    public static function wrapStreaming(
        callable $default,
        callable $streaming
    ) {
        return function (RequestInterface $request, array $options) use ($default, $streaming) {
            return empty($options['stream'])
                ? $default($request, $options)
                : $streaming($request, $options);
        };
    }
}
vendor/guzzlehttp/guzzle/src/Handler/CurlFactory.php000064400000050324151327705700016726 0ustar00<?php
namespace WPvividGuzzleHttp\Handler;

use WPvividGuzzleHttp\Exception\RequestException;
use WPvividGuzzleHttp\Exception\ConnectException;
use WPvividGuzzleHttp\Promise\FulfilledPromise;
use WPvividGuzzleHttp\Psr7;
use WPvividGuzzleHttp\Psr7\LazyOpenStream;
use WPvividGuzzleHttp\TransferStats;
use WPvividPsr\Http\Message\RequestInterface;

/**
 * Creates curl resources from a request
 */
class CurlFactory implements CurlFactoryInterface
{
    /** @var array */
    private $handles = [];

    /** @var int Total number of idle handles to keep in cache */
    private $maxHandles;

    /**
     * @param int $maxHandles Maximum number of idle handles.
     */
    public function __construct($maxHandles)
    {
        $this->maxHandles = $maxHandles;
    }

    public function create(RequestInterface $request, array $options)
    {
        if (isset($options['curl']['body_as_string'])) {
            $options['_body_as_string'] = $options['curl']['body_as_string'];
            unset($options['curl']['body_as_string']);
        }

        $easy = new EasyHandle;
        $easy->request = $request;
        $easy->options = $options;
        $conf = $this->getDefaultConf($easy);
        $this->applyMethod($easy, $conf);
        $this->applyHandlerOptions($easy, $conf);
        $this->applyHeaders($easy, $conf);
        unset($conf['_headers']);

        // Add handler options from the request configuration options
        if (isset($options['curl'])) {
            $conf = array_replace($conf, $options['curl']);
        }

        $conf[CURLOPT_HEADERFUNCTION] = $this->createHeaderFn($easy);
        $easy->handle = $this->handles
            ? array_pop($this->handles)
            : curl_init();
        curl_setopt_array($easy->handle, $conf);

        return $easy;
    }

    public function release(EasyHandle $easy)
    {
        $resource = $easy->handle;
        unset($easy->handle);

        if (count($this->handles) >= $this->maxHandles) {
            curl_close($resource);
        } else {
            // Remove all callback functions as they can hold onto references
            // and are not cleaned up by curl_reset. Using curl_setopt_array
            // does not work for some reason, so removing each one
            // individually.
            curl_setopt($resource, CURLOPT_HEADERFUNCTION, null);
            curl_setopt($resource, CURLOPT_READFUNCTION, null);
            curl_setopt($resource, CURLOPT_WRITEFUNCTION, null);
            curl_setopt($resource, CURLOPT_PROGRESSFUNCTION, null);
            curl_reset($resource);
            $this->handles[] = $resource;
        }
    }

    /**
     * Completes a cURL transaction, either returning a response promise or a
     * rejected promise.
     *
     * @param callable             $handler
     * @param EasyHandle           $easy
     * @param CurlFactoryInterface $factory Dictates how the handle is released
     *
     * @return \GuzzleHttp\Promise\PromiseInterface
     */
    public static function finish(
        callable $handler,
        EasyHandle $easy,
        CurlFactoryInterface $factory
    ) {
        if (isset($easy->options['on_stats'])) {
            self::invokeStats($easy);
        }

        if (!$easy->response || $easy->errno) {
            return self::finishError($handler, $easy, $factory);
        }

        // Return the response if it is present and there is no error.
        $factory->release($easy);

        // Rewind the body of the response if possible.
        $body = $easy->response->getBody();
        if ($body->isSeekable()) {
            $body->rewind();
        }

        return new FulfilledPromise($easy->response);
    }

    private static function invokeStats(EasyHandle $easy)
    {
        $curlStats = curl_getinfo($easy->handle);
        $stats = new TransferStats(
            $easy->request,
            $easy->response,
            $curlStats['total_time'],
            $easy->errno,
            $curlStats
        );
        call_user_func($easy->options['on_stats'], $stats);
    }

    private static function finishError(
        callable $handler,
        EasyHandle $easy,
        CurlFactoryInterface $factory
    ) {
        // Get error information and release the handle to the factory.
        $ctx = [
            'errno' => $easy->errno,
            'error' => curl_error($easy->handle),
        ] + curl_getinfo($easy->handle);
        $factory->release($easy);

        // Retry when nothing is present or when curl failed to rewind.
        if (empty($easy->options['_err_message'])
            && (!$easy->errno || $easy->errno == 65)
        ) {
            return self::retryFailedRewind($handler, $easy, $ctx);
        }

        return self::createRejection($easy, $ctx);
    }

    private static function createRejection(EasyHandle $easy, array $ctx)
    {
        static $connectionErrors = [
            CURLE_OPERATION_TIMEOUTED  => true,
            CURLE_COULDNT_RESOLVE_HOST => true,
            CURLE_COULDNT_CONNECT      => true,
            CURLE_SSL_CONNECT_ERROR    => true,
            CURLE_GOT_NOTHING          => true,
        ];

        // If an exception was encountered during the onHeaders event, then
        // return a rejected promise that wraps that exception.
        if ($easy->onHeadersException) {
            return \WPvividGuzzleHttp\Promise\rejection_for(
                new RequestException(
                    'An error was encountered during the on_headers event',
                    $easy->request,
                    $easy->response,
                    $easy->onHeadersException,
                    $ctx
                )
            );
        }

        $message = sprintf(
            'cURL error %s: %s (%s)',
            $ctx['errno'],
            $ctx['error'],
            'see http://curl.haxx.se/libcurl/c/libcurl-errors.html'
        );

        // Create a connection exception if it was a specific error code.
        $error = isset($connectionErrors[$easy->errno])
            ? new ConnectException($message, $easy->request, null, $ctx)
            : new RequestException($message, $easy->request, $easy->response, null, $ctx);

        return \WPvividGuzzleHttp\Promise\rejection_for($error);
    }

    private function getDefaultConf(EasyHandle $easy)
    {
        $conf = [
            '_headers'             => $easy->request->getHeaders(),
            CURLOPT_CUSTOMREQUEST  => $easy->request->getMethod(),
            CURLOPT_URL            => (string) $easy->request->getUri()->withFragment(''),
            CURLOPT_RETURNTRANSFER => false,
            CURLOPT_HEADER         => false,
            CURLOPT_CONNECTTIMEOUT => 150,
        ];

        if (defined('CURLOPT_PROTOCOLS')) {
            $conf[CURLOPT_PROTOCOLS] = CURLPROTO_HTTP | CURLPROTO_HTTPS;
        }

        $version = $easy->request->getProtocolVersion();
        if ($version == 1.1) {
            $conf[CURLOPT_HTTP_VERSION] = CURL_HTTP_VERSION_1_1;
        } elseif ($version == 2.0) {
            $conf[CURLOPT_HTTP_VERSION] = CURL_HTTP_VERSION_2_0;
        } else {
            $conf[CURLOPT_HTTP_VERSION] = CURL_HTTP_VERSION_1_0;
        }

        return $conf;
    }

    private function applyMethod(EasyHandle $easy, array &$conf)
    {
        $body = $easy->request->getBody();
        $size = $body->getSize();

        if ($size === null || $size > 0) {
            $this->applyBody($easy->request, $easy->options, $conf);
            return;
        }

        $method = $easy->request->getMethod();
        if ($method === 'PUT' || $method === 'POST') {
            // See http://tools.ietf.org/html/rfc7230#section-3.3.2
            if (!$easy->request->hasHeader('Content-Length')) {
                $conf[CURLOPT_HTTPHEADER][] = 'Content-Length: 0';
            }
        } elseif ($method === 'HEAD') {
            $conf[CURLOPT_NOBODY] = true;
            unset(
                $conf[CURLOPT_WRITEFUNCTION],
                $conf[CURLOPT_READFUNCTION],
                $conf[CURLOPT_FILE],
                $conf[CURLOPT_INFILE]
            );
        }
    }

    private function applyBody(RequestInterface $request, array $options, array &$conf)
    {
        $size = $request->hasHeader('Content-Length')
            ? (int) $request->getHeaderLine('Content-Length')
            : null;

        // Send the body as a string if the size is less than 1MB OR if the
        // [curl][body_as_string] request value is set.
        if (($size !== null && $size < 1000000) ||
            !empty($options['_body_as_string'])
        ) {
            $conf[CURLOPT_POSTFIELDS] = (string) $request->getBody();
            // Don't duplicate the Content-Length header
            $this->removeHeader('Content-Length', $conf);
            $this->removeHeader('Transfer-Encoding', $conf);
        } else {
            $conf[CURLOPT_UPLOAD] = true;
            if ($size !== null) {
                $conf[CURLOPT_INFILESIZE] = $size;
                $this->removeHeader('Content-Length', $conf);
            }
            $body = $request->getBody();
            if ($body->isSeekable()) {
                $body->rewind();
            }
            $conf[CURLOPT_READFUNCTION] = function ($ch, $fd, $length) use ($body) {
                return $body->read($length);
            };
        }

        // If the Expect header is not present, prevent curl from adding it
        if (!$request->hasHeader('Expect')) {
            $conf[CURLOPT_HTTPHEADER][] = 'Expect:';
        }

        // cURL sometimes adds a content-type by default. Prevent this.
        if (!$request->hasHeader('Content-Type')) {
            $conf[CURLOPT_HTTPHEADER][] = 'Content-Type:';
        }
    }

    private function applyHeaders(EasyHandle $easy, array &$conf)
    {
        foreach ($conf['_headers'] as $name => $values) {
            foreach ($values as $value) {
                $value = (string) $value;
                if ($value === '') {
                    // cURL requires a special format for empty headers.
                    // See https://github.com/guzzle/guzzle/issues/1882 for more details.
                    $conf[CURLOPT_HTTPHEADER][] = "$name;";
                } else {
                    $conf[CURLOPT_HTTPHEADER][] = "$name: $value";
                }
            }
        }

        // Remove the Accept header if one was not set
        if (!$easy->request->hasHeader('Accept')) {
            $conf[CURLOPT_HTTPHEADER][] = 'Accept:';
        }
    }

    /**
     * Remove a header from the options array.
     *
     * @param string $name    Case-insensitive header to remove
     * @param array  $options Array of options to modify
     */
    private function removeHeader($name, array &$options)
    {
        foreach (array_keys($options['_headers']) as $key) {
            if (!strcasecmp($key, $name)) {
                unset($options['_headers'][$key]);
                return;
            }
        }
    }

    private function applyHandlerOptions(EasyHandle $easy, array &$conf)
    {
        $options = $easy->options;
        if (isset($options['verify'])) {
            if ($options['verify'] === false) {
                unset($conf[CURLOPT_CAINFO]);
                $conf[CURLOPT_SSL_VERIFYHOST] = 0;
                $conf[CURLOPT_SSL_VERIFYPEER] = false;
            } else {
                $conf[CURLOPT_SSL_VERIFYHOST] = 2;
                $conf[CURLOPT_SSL_VERIFYPEER] = true;
                if (is_string($options['verify'])) {
                    // Throw an error if the file/folder/link path is not valid or doesn't exist.
                    if (!file_exists($options['verify'])) {
                        throw new \InvalidArgumentException(
                            "SSL CA bundle not found: {$options['verify']}"
                        );
                    }
                    // If it's a directory or a link to a directory use CURLOPT_CAPATH.
                    // If not, it's probably a file, or a link to a file, so use CURLOPT_CAINFO.
                    if (is_dir($options['verify']) ||
                        (is_link($options['verify']) && is_dir(readlink($options['verify'])))) {
                        $conf[CURLOPT_CAPATH] = $options['verify'];
                    } else {
                        $conf[CURLOPT_CAINFO] = $options['verify'];
                    }
                }
            }
        }

        if (!empty($options['decode_content'])) {
            $accept = $easy->request->getHeaderLine('Accept-Encoding');
            if ($accept) {
                $conf[CURLOPT_ENCODING] = $accept;
            } else {
                $conf[CURLOPT_ENCODING] = '';
                // Don't let curl send the header over the wire
                $conf[CURLOPT_HTTPHEADER][] = 'Accept-Encoding:';
            }
        }

        if (isset($options['sink'])) {
            $sink = $options['sink'];
            if (!is_string($sink)) {
                $sink = \WPvividGuzzleHttp\Psr7\stream_for($sink);
            } elseif (!is_dir(dirname($sink))) {
                // Ensure that the directory exists before failing in curl.
                throw new \RuntimeException(sprintf(
                    'Directory %s does not exist for sink value of %s',
                    dirname($sink),
                    $sink
                ));
            } else {
                $sink = new LazyOpenStream($sink, 'w+');
            }
            $easy->sink = $sink;
            $conf[CURLOPT_WRITEFUNCTION] = function ($ch, $write) use ($sink) {
                return $sink->write($write);
            };
        } else {
            // Use a default temp stream if no sink was set.
            $conf[CURLOPT_FILE] = fopen('php://temp', 'w+');
            $easy->sink = Psr7\stream_for($conf[CURLOPT_FILE]);
        }
        $timeoutRequiresNoSignal = false;
        if (isset($options['timeout'])) {
            $timeoutRequiresNoSignal |= $options['timeout'] < 1;
            $conf[CURLOPT_TIMEOUT_MS] = $options['timeout'] * 1000;
        }

        // CURL default value is CURL_IPRESOLVE_WHATEVER
        if (isset($options['force_ip_resolve'])) {
            if ('v4' === $options['force_ip_resolve']) {
                $conf[CURLOPT_IPRESOLVE] = CURL_IPRESOLVE_V4;
            } elseif ('v6' === $options['force_ip_resolve']) {
                $conf[CURLOPT_IPRESOLVE] = CURL_IPRESOLVE_V6;
            }
        }

        if (isset($options['connect_timeout'])) {
            $timeoutRequiresNoSignal |= $options['connect_timeout'] < 1;
            $conf[CURLOPT_CONNECTTIMEOUT_MS] = $options['connect_timeout'] * 1000;
        }

        if ($timeoutRequiresNoSignal && strtoupper(substr(PHP_OS, 0, 3)) !== 'WIN') {
            $conf[CURLOPT_NOSIGNAL] = true;
        }

        if (isset($options['proxy'])) {
            if (!is_array($options['proxy'])) {
                $conf[CURLOPT_PROXY] = $options['proxy'];
            } else {
                $scheme = $easy->request->getUri()->getScheme();
                if (isset($options['proxy'][$scheme])) {
                    $host = $easy->request->getUri()->getHost();
                    if (!isset($options['proxy']['no']) ||
                        !\WPvividGuzzleHttp\is_host_in_noproxy($host, $options['proxy']['no'])
                    ) {
                        $conf[CURLOPT_PROXY] = $options['proxy'][$scheme];
                    }
                }
            }
        }

        if (isset($options['cert'])) {
            $cert = $options['cert'];
            if (is_array($cert)) {
                $conf[CURLOPT_SSLCERTPASSWD] = $cert[1];
                $cert = $cert[0];
            }
            if (!file_exists($cert)) {
                throw new \InvalidArgumentException(
                    "SSL certificate not found: {$cert}"
                );
            }
            $conf[CURLOPT_SSLCERT] = $cert;
        }

        if (isset($options['ssl_key'])) {
            $sslKey = $options['ssl_key'];
            if (is_array($sslKey)) {
                $conf[CURLOPT_SSLKEYPASSWD] = $sslKey[1];
                $sslKey = $sslKey[0];
            }
            if (!file_exists($sslKey)) {
                throw new \InvalidArgumentException(
                    "SSL private key not found: {$sslKey}"
                );
            }
            $conf[CURLOPT_SSLKEY] = $sslKey;
        }

        if (isset($options['progress'])) {
            $progress = $options['progress'];
            if (!is_callable($progress)) {
                throw new \InvalidArgumentException(
                    'progress client option must be callable'
                );
            }
            $conf[CURLOPT_NOPROGRESS] = false;
            $conf[CURLOPT_PROGRESSFUNCTION] = function () use ($progress) {
                $args = func_get_args();
                // PHP 5.5 pushed the handle onto the start of the args
                if (is_resource($args[0])) {
                    array_shift($args);
                }
                call_user_func_array($progress, $args);
            };
        }

        if (!empty($options['debug'])) {
            $conf[CURLOPT_STDERR] = \WPvividGuzzleHttp\debug_resource($options['debug']);
            $conf[CURLOPT_VERBOSE] = true;
        }
    }

    /**
     * This function ensures that a response was set on a transaction. If one
     * was not set, then the request is retried if possible. This error
     * typically means you are sending a payload, curl encountered a
     * "Connection died, retrying a fresh connect" error, tried to rewind the
     * stream, and then encountered a "necessary data rewind wasn't possible"
     * error, causing the request to be sent through curl_multi_info_read()
     * without an error status.
     */
    private static function retryFailedRewind(
        callable $handler,
        EasyHandle $easy,
        array $ctx
    ) {
        try {
            // Only rewind if the body has been read from.
            $body = $easy->request->getBody();
            if ($body->tell() > 0) {
                $body->rewind();
            }
        } catch (\RuntimeException $e) {
            $ctx['error'] = 'The connection unexpectedly failed without '
                . 'providing an error. The request would have been retried, '
                . 'but attempting to rewind the request body failed. '
                . 'Exception: ' . $e;
            return self::createRejection($easy, $ctx);
        }

        // Retry no more than 3 times before giving up.
        if (!isset($easy->options['_curl_retries'])) {
            $easy->options['_curl_retries'] = 1;
        } elseif ($easy->options['_curl_retries'] == 2) {
            $ctx['error'] = 'The cURL request was retried 3 times '
                . 'and did not succeed. The most likely reason for the failure '
                . 'is that cURL was unable to rewind the body of the request '
                . 'and subsequent retries resulted in the same error. Turn on '
                . 'the debug option to see what went wrong. See '
                . 'https://bugs.php.net/bug.php?id=47204 for more information.';
            return self::createRejection($easy, $ctx);
        } else {
            $easy->options['_curl_retries']++;
        }

        return $handler($easy->request, $easy->options);
    }

    private function createHeaderFn(EasyHandle $easy)
    {
        if (isset($easy->options['on_headers'])) {
            $onHeaders = $easy->options['on_headers'];

            if (!is_callable($onHeaders)) {
                throw new \InvalidArgumentException('on_headers must be callable');
            }
        } else {
            $onHeaders = null;
        }

        return function ($ch, $h) use (
            $onHeaders,
            $easy,
            &$startingResponse
        ) {
            $value = trim($h);
            if ($value === '') {
                $startingResponse = true;
                $easy->createResponse();
                if ($onHeaders !== null) {
                    try {
                        $onHeaders($easy->response);
                    } catch (\Exception $e) {
                        // Associate the exception with the handle and trigger
                        // a curl header write error by returning 0.
                        $easy->onHeadersException = $e;
                        return -1;
                    }
                }
            } elseif ($startingResponse) {
                $startingResponse = false;
                $easy->headers = [$value];
            } else {
                $easy->headers[] = $value;
            }
            return strlen($h);
        };
    }
}
vendor/guzzlehttp/guzzle/src/Handler/EasyHandle.php000064400000005472151327705700016512 0ustar00<?php
namespace WPvividGuzzleHttp\Handler;

use WPvividGuzzleHttp\Psr7\Response;
use WPvividPsr\Http\Message\RequestInterface;
use WPvividPsr\Http\Message\ResponseInterface;
use WPvividPsr\Http\Message\StreamInterface;

/**
 * Represents a cURL easy handle and the data it populates.
 *
 * @internal
 */
final class EasyHandle
{
    /** @var resource cURL resource */
    public $handle;

    /** @var StreamInterface Where data is being written */
    public $sink;

    /** @var array Received HTTP headers so far */
    public $headers = [];

    /** @var ResponseInterface Received response (if any) */
    public $response;

    /** @var RequestInterface Request being sent */
    public $request;

    /** @var array Request options */
    public $options = [];

    /** @var int cURL error number (if any) */
    public $errno = 0;

    /** @var \Exception Exception during on_headers (if any) */
    public $onHeadersException;

    /**
     * Attach a response to the easy handle based on the received headers.
     *
     * @throws \RuntimeException if no headers have been received.
     */
    public function createResponse()
    {
        if (empty($this->headers)) {
            throw new \RuntimeException('No headers have been received');
        }

        // HTTP-version SP status-code SP reason-phrase
        $startLine = explode(' ', array_shift($this->headers), 3);
        $headers = \WPvividGuzzleHttp\headers_from_lines($this->headers);
        $normalizedKeys = \WPvividGuzzleHttp\normalize_header_keys($headers);

        if (!empty($this->options['decode_content'])
            && isset($normalizedKeys['content-encoding'])
        ) {
            $headers['x-encoded-content-encoding']
                = $headers[$normalizedKeys['content-encoding']];
            unset($headers[$normalizedKeys['content-encoding']]);
            if (isset($normalizedKeys['content-length'])) {
                $headers['x-encoded-content-length']
                    = $headers[$normalizedKeys['content-length']];

                $bodyLength = (int) $this->sink->getSize();
                if ($bodyLength) {
                    $headers[$normalizedKeys['content-length']] = $bodyLength;
                } else {
                    unset($headers[$normalizedKeys['content-length']]);
                }
            }
        }

        // Attach a response to the easy handle with the parsed headers.
        $this->response = new Response(
            $startLine[1],
            $headers,
            $this->sink,
            substr($startLine[0], 5),
            isset($startLine[2]) ? (string) $startLine[2] : null
        );
    }

    public function __get($name)
    {
        $msg = $name === 'handle'
            ? 'The EasyHandle has been released'
            : 'Invalid property: ' . $name;
        throw new \BadMethodCallException($msg);
    }
}
vendor/guzzlehttp/guzzle/src/Handler/MockHandler.php000064400000013477151327705700016670 0ustar00<?php
namespace WPvividGuzzleHttp\Handler;

use WPvividGuzzleHttp\Exception\RequestException;
use WPvividGuzzleHttp\HandlerStack;
use WPvividGuzzleHttp\Promise\PromiseInterface;
use WPvividGuzzleHttp\Promise\RejectedPromise;
use WPvividGuzzleHttp\TransferStats;
use WPvividPsr\Http\Message\RequestInterface;
use WPvividPsr\Http\Message\ResponseInterface;

/**
 * Handler that returns responses or throw exceptions from a queue.
 */
class MockHandler implements \Countable
{
    private $queue = [];
    private $lastRequest;
    private $lastOptions;
    private $onFulfilled;
    private $onRejected;

    /**
     * Creates a new MockHandler that uses the default handler stack list of
     * middlewares.
     *
     * @param array $queue Array of responses, callables, or exceptions.
     * @param callable $onFulfilled Callback to invoke when the return value is fulfilled.
     * @param callable $onRejected  Callback to invoke when the return value is rejected.
     *
     * @return HandlerStack
     */
    public static function createWithMiddleware(
        array $queue = null,
        callable $onFulfilled = null,
        callable $onRejected = null
    ) {
        return HandlerStack::create(new self($queue, $onFulfilled, $onRejected));
    }

    /**
     * The passed in value must be an array of
     * {@see Psr7\Http\Message\ResponseInterface} objects, Exceptions,
     * callables, or Promises.
     *
     * @param array $queue
     * @param callable $onFulfilled Callback to invoke when the return value is fulfilled.
     * @param callable $onRejected  Callback to invoke when the return value is rejected.
     */
    public function __construct(
        array $queue = null,
        callable $onFulfilled = null,
        callable $onRejected = null
    ) {
        $this->onFulfilled = $onFulfilled;
        $this->onRejected = $onRejected;

        if ($queue) {
            call_user_func_array([$this, 'append'], $queue);
        }
    }

    public function __invoke(RequestInterface $request, array $options)
    {
        if (!$this->queue) {
            throw new \OutOfBoundsException('Mock queue is empty');
        }

        if (isset($options['delay'])) {
            usleep($options['delay'] * 1000);
        }

        $this->lastRequest = $request;
        $this->lastOptions = $options;
        $response = array_shift($this->queue);

        if (isset($options['on_headers'])) {
            if (!is_callable($options['on_headers'])) {
                throw new \InvalidArgumentException('on_headers must be callable');
            }
            try {
                $options['on_headers']($response);
            } catch (\Exception $e) {
                $msg = 'An error was encountered during the on_headers event';
                $response = new RequestException($msg, $request, $response, $e);
            }
        }

        if (is_callable($response)) {
            $response = call_user_func($response, $request, $options);
        }

        $response = $response instanceof \Exception
            ? \WPvividGuzzleHttp\Promise\rejection_for($response)
            : \WPvividGuzzleHttp\Promise\promise_for($response);

        return $response->then(
            function ($value) use ($request, $options) {
                $this->invokeStats($request, $options, $value);
                if ($this->onFulfilled) {
                    call_user_func($this->onFulfilled, $value);
                }
                if (isset($options['sink'])) {
                    $contents = (string) $value->getBody();
                    $sink = $options['sink'];

                    if (is_resource($sink)) {
                        fwrite($sink, $contents);
                    } elseif (is_string($sink)) {
                        file_put_contents($sink, $contents);
                    } elseif ($sink instanceof \WPvividPsr\Http\Message\StreamInterface) {
                        $sink->write($contents);
                    }
                }

                return $value;
            },
            function ($reason) use ($request, $options) {
                $this->invokeStats($request, $options, null, $reason);
                if ($this->onRejected) {
                    call_user_func($this->onRejected, $reason);
                }
                return \WPvividGuzzleHttp\Promise\rejection_for($reason);
            }
        );
    }

    /**
     * Adds one or more variadic requests, exceptions, callables, or promises
     * to the queue.
     */
    public function append()
    {
        foreach (func_get_args() as $value) {
            if ($value instanceof ResponseInterface
                || $value instanceof \Exception
                || $value instanceof PromiseInterface
                || is_callable($value)
            ) {
                $this->queue[] = $value;
            } else {
                throw new \InvalidArgumentException('Expected a response or '
                    . 'exception. Found ' . \WPvividGuzzleHttp\describe_type($value));
            }
        }
    }

    /**
     * Get the last received request.
     *
     * @return RequestInterface
     */
    public function getLastRequest()
    {
        return $this->lastRequest;
    }

    /**
     * Get the last received request options.
     *
     * @return array
     */
    public function getLastOptions()
    {
        return $this->lastOptions;
    }

    /**
     * Returns the number of remaining items in the queue.
     *
     * @return int
     */
    public function count()
    {
        return count($this->queue);
    }

    private function invokeStats(
        RequestInterface $request,
        array $options,
        ResponseInterface $response = null,
        $reason = null
    ) {
        if (isset($options['on_stats'])) {
            $stats = new TransferStats($request, $response, 0, $reason);
            call_user_func($options['on_stats'], $stats);
        }
    }
}
vendor/guzzlehttp/guzzle/src/Handler/CurlMultiHandler.php000064400000013025151327705700017704 0ustar00<?php
namespace WPvividGuzzleHttp\Handler;

use WPvividGuzzleHttp\Promise as P;
use WPvividGuzzleHttp\Promise\Promise;
use WPvividGuzzleHttp\Psr7;
use WPvividPsr\Http\Message\RequestInterface;

/**
 * Returns an asynchronous response using curl_multi_* functions.
 *
 * When using the CurlMultiHandler, custom curl options can be specified as an
 * associative array of curl option constants mapping to values in the
 * **curl** key of the provided request options.
 *
 * @property resource $_mh Internal use only. Lazy loaded multi-handle.
 */
class CurlMultiHandler
{
    /** @var CurlFactoryInterface */
    private $factory;
    private $selectTimeout;
    private $active;
    private $handles = [];
    private $delays = [];

    /**
     * This handler accepts the following options:
     *
     * - handle_factory: An optional factory  used to create curl handles
     * - select_timeout: Optional timeout (in seconds) to block before timing
     *   out while selecting curl handles. Defaults to 1 second.
     *
     * @param array $options
     */
    public function __construct(array $options = [])
    {
        $this->factory = isset($options['handle_factory'])
            ? $options['handle_factory'] : new CurlFactory(50);
        $this->selectTimeout = isset($options['select_timeout'])
            ? $options['select_timeout'] : 1;
    }

    public function __get($name)
    {
        if ($name === '_mh') {
            return $this->_mh = curl_multi_init();
        }

        throw new \BadMethodCallException();
    }

    public function __destruct()
    {
        if (isset($this->_mh)) {
            curl_multi_close($this->_mh);
            unset($this->_mh);
        }
    }

    public function __invoke(RequestInterface $request, array $options)
    {
        $easy = $this->factory->create($request, $options);
        $id = (int) $easy->handle;

        $promise = new Promise(
            [$this, 'execute'],
            function () use ($id) {
                return $this->cancel($id);
            }
        );

        $this->addRequest(['easy' => $easy, 'deferred' => $promise]);

        return $promise;
    }

    /**
     * Ticks the curl event loop.
     */
    public function tick()
    {
        // Add any delayed handles if needed.
        if ($this->delays) {
            $currentTime = microtime(true);
            foreach ($this->delays as $id => $delay) {
                if ($currentTime >= $delay) {
                    unset($this->delays[$id]);
                    curl_multi_add_handle(
                        $this->_mh,
                        $this->handles[$id]['easy']->handle
                    );
                }
            }
        }

        // Step through the task queue which may add additional requests.
        P\queue()->run();

        if ($this->active &&
            curl_multi_select($this->_mh, $this->selectTimeout) === -1
        ) {
            // Perform a usleep if a select returns -1.
            // See: https://bugs.php.net/bug.php?id=61141
            usleep(250);
        }

        while (curl_multi_exec($this->_mh, $this->active) === CURLM_CALL_MULTI_PERFORM);

        $this->processMessages();
    }

    /**
     * Runs until all outstanding connections have completed.
     */
    public function execute()
    {
        $queue = P\queue();

        while ($this->handles || !$queue->isEmpty()) {
            // If there are no transfers, then sleep for the next delay
            if (!$this->active && $this->delays) {
                usleep($this->timeToNext());
            }
            $this->tick();
        }
    }

    private function addRequest(array $entry)
    {
        $easy = $entry['easy'];
        $id = (int) $easy->handle;
        $this->handles[$id] = $entry;
        if (empty($easy->options['delay'])) {
            curl_multi_add_handle($this->_mh, $easy->handle);
        } else {
            $this->delays[$id] = microtime(true) + ($easy->options['delay'] / 1000);
        }
    }

    /**
     * Cancels a handle from sending and removes references to it.
     *
     * @param int $id Handle ID to cancel and remove.
     *
     * @return bool True on success, false on failure.
     */
    private function cancel($id)
    {
        // Cannot cancel if it has been processed.
        if (!isset($this->handles[$id])) {
            return false;
        }

        $handle = $this->handles[$id]['easy']->handle;
        unset($this->delays[$id], $this->handles[$id]);
        curl_multi_remove_handle($this->_mh, $handle);
        curl_close($handle);

        return true;
    }

    private function processMessages()
    {
        while ($done = curl_multi_info_read($this->_mh)) {
            $id = (int) $done['handle'];
            curl_multi_remove_handle($this->_mh, $done['handle']);

            if (!isset($this->handles[$id])) {
                // Probably was cancelled.
                continue;
            }

            $entry = $this->handles[$id];
            unset($this->handles[$id], $this->delays[$id]);
            $entry['easy']->errno = $done['result'];
            $entry['deferred']->resolve(
                CurlFactory::finish(
                    $this,
                    $entry['easy'],
                    $this->factory
                )
            );
        }
    }

    private function timeToNext()
    {
        $currentTime = microtime(true);
        $nextTime = PHP_INT_MAX;
        foreach ($this->delays as $time) {
            if ($time < $nextTime) {
                $nextTime = $time;
            }
        }

        return max(0, $nextTime - $currentTime) * 1000000;
    }
}
vendor/guzzlehttp/guzzle/src/Handler/StreamHandler.php000064400000043737151327705700017234 0ustar00<?php
namespace WPvividGuzzleHttp\Handler;

use WPvividGuzzleHttp\Exception\RequestException;
use WPvividGuzzleHttp\Exception\ConnectException;
use WPvividGuzzleHttp\Promise\FulfilledPromise;
use WPvividGuzzleHttp\Promise\PromiseInterface;
use WPvividGuzzleHttp\Psr7;
use WPvividGuzzleHttp\TransferStats;
use WPvividPsr\Http\Message\RequestInterface;
use WPvividPsr\Http\Message\ResponseInterface;
use WPvividPsr\Http\Message\StreamInterface;

/**
 * HTTP handler that uses PHP's HTTP stream wrapper.
 */
class StreamHandler
{
    private $lastHeaders = [];

    /**
     * Sends an HTTP request.
     *
     * @param RequestInterface $request Request to send.
     * @param array            $options Request transfer options.
     *
     * @return PromiseInterface
     */
    public function __invoke(RequestInterface $request, array $options)
    {
        // Sleep if there is a delay specified.
        if (isset($options['delay'])) {
            usleep($options['delay'] * 1000);
        }

        $startTime = isset($options['on_stats']) ? microtime(true) : null;

        try {
            // Does not support the expect header.
            $request = $request->withoutHeader('Expect');

            // Append a content-length header if body size is zero to match
            // cURL's behavior.
            if (0 === $request->getBody()->getSize()) {
                $request = $request->withHeader('Content-Length', 0);
            }

            return $this->createResponse(
                $request,
                $options,
                $this->createStream($request, $options),
                $startTime
            );
        } catch (\InvalidArgumentException $e) {
            throw $e;
        } catch (\Exception $e) {
            // Determine if the error was a networking error.
            $message = $e->getMessage();
            // This list can probably get more comprehensive.
            if (strpos($message, 'getaddrinfo') // DNS lookup failed
                || strpos($message, 'Connection refused')
                || strpos($message, "couldn't connect to host") // error on HHVM
                || strpos($message, "connection attempt failed")
            ) {
                $e = new ConnectException($e->getMessage(), $request, $e);
            }
            $e = RequestException::wrapException($request, $e);
            $this->invokeStats($options, $request, $startTime, null, $e);

            return \WPvividGuzzleHttp\Promise\rejection_for($e);
        }
    }

    private function invokeStats(
        array $options,
        RequestInterface $request,
        $startTime,
        ResponseInterface $response = null,
        $error = null
    ) {
        if (isset($options['on_stats'])) {
            $stats = new TransferStats(
                $request,
                $response,
                microtime(true) - $startTime,
                $error,
                []
            );
            call_user_func($options['on_stats'], $stats);
        }
    }

    private function createResponse(
        RequestInterface $request,
        array $options,
        $stream,
        $startTime
    ) {
        $hdrs = $this->lastHeaders;
        $this->lastHeaders = [];
        $parts = explode(' ', array_shift($hdrs), 3);
        $ver = explode('/', $parts[0])[1];
        $status = $parts[1];
        $reason = isset($parts[2]) ? $parts[2] : null;
        $headers = \WPvividGuzzleHttp\headers_from_lines($hdrs);
        list($stream, $headers) = $this->checkDecode($options, $headers, $stream);
        $stream = Psr7\stream_for($stream);
        $sink = $stream;

        if (strcasecmp('HEAD', $request->getMethod())) {
            $sink = $this->createSink($stream, $options);
        }

        $response = new Psr7\Response($status, $headers, $sink, $ver, $reason);

        if (isset($options['on_headers'])) {
            try {
                $options['on_headers']($response);
            } catch (\Exception $e) {
                $msg = 'An error was encountered during the on_headers event';
                $ex = new RequestException($msg, $request, $response, $e);
                return \WPvividGuzzleHttp\Promise\rejection_for($ex);
            }
        }

        // Do not drain when the request is a HEAD request because they have
        // no body.
        if ($sink !== $stream) {
            $this->drain(
                $stream,
                $sink,
                $response->getHeaderLine('Content-Length')
            );
        }

        $this->invokeStats($options, $request, $startTime, $response, null);

        return new FulfilledPromise($response);
    }

    private function createSink(StreamInterface $stream, array $options)
    {
        if (!empty($options['stream'])) {
            return $stream;
        }

        $sink = isset($options['sink'])
            ? $options['sink']
            : fopen('php://temp', 'r+');

        return is_string($sink)
            ? new Psr7\LazyOpenStream($sink, 'w+')
            : Psr7\stream_for($sink);
    }

    private function checkDecode(array $options, array $headers, $stream)
    {
        // Automatically decode responses when instructed.
        if (!empty($options['decode_content'])) {
            $normalizedKeys = \WPvividGuzzleHttp\normalize_header_keys($headers);
            if (isset($normalizedKeys['content-encoding'])) {
                $encoding = $headers[$normalizedKeys['content-encoding']];
                if ($encoding[0] === 'gzip' || $encoding[0] === 'deflate') {
                    $stream = new Psr7\InflateStream(
                        Psr7\stream_for($stream)
                    );
                    $headers['x-encoded-content-encoding']
                        = $headers[$normalizedKeys['content-encoding']];
                    // Remove content-encoding header
                    unset($headers[$normalizedKeys['content-encoding']]);
                    // Fix content-length header
                    if (isset($normalizedKeys['content-length'])) {
                        $headers['x-encoded-content-length']
                            = $headers[$normalizedKeys['content-length']];

                        $length = (int) $stream->getSize();
                        if ($length === 0) {
                            unset($headers[$normalizedKeys['content-length']]);
                        } else {
                            $headers[$normalizedKeys['content-length']] = [$length];
                        }
                    }
                }
            }
        }

        return [$stream, $headers];
    }

    /**
     * Drains the source stream into the "sink" client option.
     *
     * @param StreamInterface $source
     * @param StreamInterface $sink
     * @param string          $contentLength Header specifying the amount of
     *                                       data to read.
     *
     * @return StreamInterface
     * @throws \RuntimeException when the sink option is invalid.
     */
    private function drain(
        StreamInterface $source,
        StreamInterface $sink,
        $contentLength
    ) {
        // If a content-length header is provided, then stop reading once
        // that number of bytes has been read. This can prevent infinitely
        // reading from a stream when dealing with servers that do not honor
        // Connection: Close headers.
        Psr7\copy_to_stream(
            $source,
            $sink,
            (strlen($contentLength) > 0 && (int) $contentLength > 0) ? (int) $contentLength : -1
        );

        $sink->seek(0);
        $source->close();

        return $sink;
    }

    /**
     * Create a resource and check to ensure it was created successfully
     *
     * @param callable $callback Callable that returns stream resource
     *
     * @return resource
     * @throws \RuntimeException on error
     */
    private function createResource(callable $callback)
    {
        $errors = null;
        set_error_handler(function ($_, $msg, $file, $line) use (&$errors) {
            $errors[] = [
                'message' => $msg,
                'file'    => $file,
                'line'    => $line
            ];
            return true;
        });

        $resource = $callback();
        restore_error_handler();

        if (!$resource) {
            $message = 'Error creating resource: ';
            foreach ($errors as $err) {
                foreach ($err as $key => $value) {
                    $message .= "[$key] $value" . PHP_EOL;
                }
            }
            throw new \RuntimeException(trim($message));
        }

        return $resource;
    }

    private function createStream(RequestInterface $request, array $options)
    {
        static $methods;
        if (!$methods) {
            $methods = array_flip(get_class_methods(__CLASS__));
        }

        // HTTP/1.1 streams using the PHP stream wrapper require a
        // Connection: close header
        if ($request->getProtocolVersion() == '1.1'
            && !$request->hasHeader('Connection')
        ) {
            $request = $request->withHeader('Connection', 'close');
        }

        // Ensure SSL is verified by default
        if (!isset($options['verify'])) {
            $options['verify'] = true;
        }

        $params = [];
        $context = $this->getDefaultContext($request);

        if (isset($options['on_headers']) && !is_callable($options['on_headers'])) {
            throw new \InvalidArgumentException('on_headers must be callable');
        }

        if (!empty($options)) {
            foreach ($options as $key => $value) {
                $method = "add_{$key}";
                if (isset($methods[$method])) {
                    $this->{$method}($request, $context, $value, $params);
                }
            }
        }

        if (isset($options['stream_context'])) {
            if (!is_array($options['stream_context'])) {
                throw new \InvalidArgumentException('stream_context must be an array');
            }
            $context = array_replace_recursive(
                $context,
                $options['stream_context']
            );
        }

        // Microsoft NTLM authentication only supported with curl handler
        if (isset($options['auth'])
            && is_array($options['auth'])
            && isset($options['auth'][2])
            && 'ntlm' == $options['auth'][2]
        ) {
            throw new \InvalidArgumentException('Microsoft NTLM authentication only supported with curl handler');
        }

        $uri = $this->resolveHost($request, $options);

        $context = $this->createResource(
            function () use ($context, $params) {
                return stream_context_create($context, $params);
            }
        );

        return $this->createResource(
            function () use ($uri, &$http_response_header, $context, $options) {
                $resource = fopen((string) $uri, 'r', null, $context);
                $this->lastHeaders = $http_response_header;

                if (isset($options['read_timeout'])) {
                    $readTimeout = $options['read_timeout'];
                    $sec = (int) $readTimeout;
                    $usec = ($readTimeout - $sec) * 100000;
                    stream_set_timeout($resource, $sec, $usec);
                }

                return $resource;
            }
        );
    }

    private function resolveHost(RequestInterface $request, array $options)
    {
        $uri = $request->getUri();

        if (isset($options['force_ip_resolve']) && !filter_var($uri->getHost(), FILTER_VALIDATE_IP)) {
            if ('v4' === $options['force_ip_resolve']) {
                $records = dns_get_record($uri->getHost(), DNS_A);
                if (!isset($records[0]['ip'])) {
                    throw new ConnectException(sprintf("Could not resolve IPv4 address for host '%s'", $uri->getHost()), $request);
                }
                $uri = $uri->withHost($records[0]['ip']);
            } elseif ('v6' === $options['force_ip_resolve']) {
                $records = dns_get_record($uri->getHost(), DNS_AAAA);
                if (!isset($records[0]['ipv6'])) {
                    throw new ConnectException(sprintf("Could not resolve IPv6 address for host '%s'", $uri->getHost()), $request);
                }
                $uri = $uri->withHost('[' . $records[0]['ipv6'] . ']');
            }
        }

        return $uri;
    }

    private function getDefaultContext(RequestInterface $request)
    {
        $headers = '';
        foreach ($request->getHeaders() as $name => $value) {
            foreach ($value as $val) {
                $headers .= "$name: $val\r\n";
            }
        }

        $context = [
            'http' => [
                'method'           => $request->getMethod(),
                'header'           => $headers,
                'protocol_version' => $request->getProtocolVersion(),
                'ignore_errors'    => true,
                'follow_location'  => 0,
            ],
        ];

        $body = (string) $request->getBody();

        if (!empty($body)) {
            $context['http']['content'] = $body;
            // Prevent the HTTP handler from adding a Content-Type header.
            if (!$request->hasHeader('Content-Type')) {
                $context['http']['header'] .= "Content-Type:\r\n";
            }
        }

        $context['http']['header'] = rtrim($context['http']['header']);

        return $context;
    }

    private function add_proxy(RequestInterface $request, &$options, $value, &$params)
    {
        if (!is_array($value)) {
            $options['http']['proxy'] = $value;
        } else {
            $scheme = $request->getUri()->getScheme();
            if (isset($value[$scheme])) {
                if (!isset($value['no'])
                    || !\WPvividGuzzleHttp\is_host_in_noproxy(
                        $request->getUri()->getHost(),
                        $value['no']
                    )
                ) {
                    $options['http']['proxy'] = $value[$scheme];
                }
            }
        }
    }

    private function add_timeout(RequestInterface $request, &$options, $value, &$params)
    {
        if ($value > 0) {
            $options['http']['timeout'] = $value;
        }
    }

    private function add_verify(RequestInterface $request, &$options, $value, &$params)
    {
        if ($value === true) {
            // PHP 5.6 or greater will find the system cert by default. When
            // < 5.6, use the Guzzle bundled cacert.
            if (PHP_VERSION_ID < 50600) {
                $options['ssl']['cafile'] = \WPvividGuzzleHttp\default_ca_bundle();
            }
        } elseif (is_string($value)) {
            $options['ssl']['cafile'] = $value;
            if (!file_exists($value)) {
                throw new \RuntimeException("SSL CA bundle not found: $value");
            }
        } elseif ($value === false) {
            $options['ssl']['verify_peer'] = false;
            $options['ssl']['verify_peer_name'] = false;
            return;
        } else {
            throw new \InvalidArgumentException('Invalid verify request option');
        }

        $options['ssl']['verify_peer'] = true;
        $options['ssl']['verify_peer_name'] = true;
        $options['ssl']['allow_self_signed'] = false;
    }

    private function add_cert(RequestInterface $request, &$options, $value, &$params)
    {
        if (is_array($value)) {
            $options['ssl']['passphrase'] = $value[1];
            $value = $value[0];
        }

        if (!file_exists($value)) {
            throw new \RuntimeException("SSL certificate not found: {$value}");
        }

        $options['ssl']['local_cert'] = $value;
    }

    private function add_progress(RequestInterface $request, &$options, $value, &$params)
    {
        $this->addNotification(
            $params,
            function ($code, $a, $b, $c, $transferred, $total) use ($value) {
                if ($code == STREAM_NOTIFY_PROGRESS) {
                    $value($total, $transferred, null, null);
                }
            }
        );
    }

    private function add_debug(RequestInterface $request, &$options, $value, &$params)
    {
        if ($value === false) {
            return;
        }

        static $map = [
            STREAM_NOTIFY_CONNECT       => 'CONNECT',
            STREAM_NOTIFY_AUTH_REQUIRED => 'AUTH_REQUIRED',
            STREAM_NOTIFY_AUTH_RESULT   => 'AUTH_RESULT',
            STREAM_NOTIFY_MIME_TYPE_IS  => 'MIME_TYPE_IS',
            STREAM_NOTIFY_FILE_SIZE_IS  => 'FILE_SIZE_IS',
            STREAM_NOTIFY_REDIRECTED    => 'REDIRECTED',
            STREAM_NOTIFY_PROGRESS      => 'PROGRESS',
            STREAM_NOTIFY_FAILURE       => 'FAILURE',
            STREAM_NOTIFY_COMPLETED     => 'COMPLETED',
            STREAM_NOTIFY_RESOLVE       => 'RESOLVE',
        ];
        static $args = ['severity', 'message', 'message_code',
            'bytes_transferred', 'bytes_max'];

        $value = \WPvividGuzzleHttp\debug_resource($value);
        $ident = $request->getMethod() . ' ' . $request->getUri()->withFragment('');
        $this->addNotification(
            $params,
            function () use ($ident, $value, $map, $args) {
                $passed = func_get_args();
                $code = array_shift($passed);
                fprintf($value, '<%s> [%s] ', $ident, $map[$code]);
                foreach (array_filter($passed) as $i => $v) {
                    fwrite($value, $args[$i] . ': "' . $v . '" ');
                }
                fwrite($value, "\n");
            }
        );
    }

    private function addNotification(array &$params, callable $notify)
    {
        // Wrap the existing function if needed.
        if (!isset($params['notification'])) {
            $params['notification'] = $notify;
        } else {
            $params['notification'] = $this->callArray([
                $params['notification'],
                $notify
            ]);
        }
    }

    private function callArray(array $functions)
    {
        return function () use ($functions) {
            $args = func_get_args();
            foreach ($functions as $fn) {
                call_user_func_array($fn, $args);
            }
        };
    }
}
vendor/guzzlehttp/guzzle/src/Handler/CurlHandler.php000064400000002404151327705700016670 0ustar00<?php
namespace WPvividGuzzleHttp\Handler;

use WPvividGuzzleHttp\Psr7;
use WPvividPsr\Http\Message\RequestInterface;

/**
 * HTTP handler that uses cURL easy handles as a transport layer.
 *
 * When using the CurlHandler, custom curl options can be specified as an
 * associative array of curl option constants mapping to values in the
 * **curl** key of the "client" key of the request.
 */
class CurlHandler
{
    /** @var CurlFactoryInterface */
    private $factory;

    /**
     * Accepts an associative array of options:
     *
     * - factory: Optional curl factory used to create cURL handles.
     *
     * @param array $options Array of options to use with the handler
     */
    public function __construct(array $options = [])
    {
        $this->factory = isset($options['handle_factory'])
            ? $options['handle_factory']
            : new CurlFactory(3);
    }

    public function __invoke(RequestInterface $request, array $options)
    {
        if (isset($options['delay'])) {
            usleep($options['delay'] * 1000);
        }

        $easy = $this->factory->create($request, $options);
        curl_exec($easy->handle);
        $easy->errno = curl_errno($easy->handle);

        return CurlFactory::finish($this, $easy, $this->factory);
    }
}
vendor/guzzlehttp/guzzle/src/RedirectMiddleware.php000064400000017254151327705700016660 0ustar00<?php
namespace WPvividGuzzleHttp;

use WPvividGuzzleHttp\Exception\BadResponseException;
use WPvividGuzzleHttp\Exception\TooManyRedirectsException;
use WPvividGuzzleHttp\Promise\PromiseInterface;
use WPvividGuzzleHttp\Psr7;
use WPvividPsr\Http\Message\RequestInterface;
use WPvividPsr\Http\Message\ResponseInterface;
use WPvividPsr\Http\Message\UriInterface;

/**
 * Request redirect middleware.
 *
 * Apply this middleware like other middleware using
 * {@see GuzzleHttp\Middleware::redirect()}.
 */
class RedirectMiddleware
{
    const HISTORY_HEADER = 'X-Guzzle-Redirect-History';

    const STATUS_HISTORY_HEADER = 'X-Guzzle-Redirect-Status-History';

    public static $defaultSettings = [
        'max'             => 5,
        'protocols'       => ['http', 'https'],
        'strict'          => false,
        'referer'         => false,
        'track_redirects' => false,
    ];

    /** @var callable  */
    private $nextHandler;

    /**
     * @param callable $nextHandler Next handler to invoke.
     */
    public function __construct(callable $nextHandler)
    {
        $this->nextHandler = $nextHandler;
    }

    /**
     * @param RequestInterface $request
     * @param array            $options
     *
     * @return PromiseInterface
     */
    public function __invoke(RequestInterface $request, array $options)
    {
        $fn = $this->nextHandler;

        if (empty($options['allow_redirects'])) {
            return $fn($request, $options);
        }

        if ($options['allow_redirects'] === true) {
            $options['allow_redirects'] = self::$defaultSettings;
        } elseif (!is_array($options['allow_redirects'])) {
            throw new \InvalidArgumentException('allow_redirects must be true, false, or array');
        } else {
            // Merge the default settings with the provided settings
            $options['allow_redirects'] += self::$defaultSettings;
        }

        if (empty($options['allow_redirects']['max'])) {
            return $fn($request, $options);
        }

        return $fn($request, $options)
            ->then(function (ResponseInterface $response) use ($request, $options) {
                return $this->checkRedirect($request, $options, $response);
            });
    }

    /**
     * @param RequestInterface  $request
     * @param array             $options
     * @param ResponseInterface|PromiseInterface $response
     *
     * @return ResponseInterface|PromiseInterface
     */
    public function checkRedirect(
        RequestInterface $request,
        array $options,
        ResponseInterface $response
    ) {
        if (substr($response->getStatusCode(), 0, 1) != '3'
            || !$response->hasHeader('Location')
        ) {
            return $response;
        }

        $this->guardMax($request, $options);
        $nextRequest = $this->modifyRequest($request, $options, $response);

        if (isset($options['allow_redirects']['on_redirect'])) {
            call_user_func(
                $options['allow_redirects']['on_redirect'],
                $request,
                $response,
                $nextRequest->getUri()
            );
        }

        /** @var PromiseInterface|ResponseInterface $promise */
        $promise = $this($nextRequest, $options);

        // Add headers to be able to track history of redirects.
        if (!empty($options['allow_redirects']['track_redirects'])) {
            return $this->withTracking(
                $promise,
                (string) $nextRequest->getUri(),
                $response->getStatusCode()
            );
        }

        return $promise;
    }

    private function withTracking(PromiseInterface $promise, $uri, $statusCode)
    {
        return $promise->then(
            function (ResponseInterface $response) use ($uri, $statusCode) {
                // Note that we are pushing to the front of the list as this
                // would be an earlier response than what is currently present
                // in the history header.
                $historyHeader = $response->getHeader(self::HISTORY_HEADER);
                $statusHeader = $response->getHeader(self::STATUS_HISTORY_HEADER);
                array_unshift($historyHeader, $uri);
                array_unshift($statusHeader, $statusCode);
                return $response->withHeader(self::HISTORY_HEADER, $historyHeader)
                                ->withHeader(self::STATUS_HISTORY_HEADER, $statusHeader);
            }
        );
    }

    private function guardMax(RequestInterface $request, array &$options)
    {
        $current = isset($options['__redirect_count'])
            ? $options['__redirect_count']
            : 0;
        $options['__redirect_count'] = $current + 1;
        $max = $options['allow_redirects']['max'];

        if ($options['__redirect_count'] > $max) {
            throw new TooManyRedirectsException(
                "Will not follow more than {$max} redirects",
                $request
            );
        }
    }

    /**
     * @param RequestInterface  $request
     * @param array             $options
     * @param ResponseInterface $response
     *
     * @return RequestInterface
     */
    public function modifyRequest(
        RequestInterface $request,
        array $options,
        ResponseInterface $response
    ) {
        // Request modifications to apply.
        $modify = [];
        $protocols = $options['allow_redirects']['protocols'];

        // Use a GET request if this is an entity enclosing request and we are
        // not forcing RFC compliance, but rather emulating what all browsers
        // would do.
        $statusCode = $response->getStatusCode();
        if ($statusCode == 303 ||
            ($statusCode <= 302 && $request->getBody() && !$options['allow_redirects']['strict'])
        ) {
            $modify['method'] = 'GET';
            $modify['body'] = '';
        }

        $modify['uri'] = $this->redirectUri($request, $response, $protocols);
        Psr7\rewind_body($request);

        // Add the Referer header if it is told to do so and only
        // add the header if we are not redirecting from https to http.
        if ($options['allow_redirects']['referer']
            && $modify['uri']->getScheme() === $request->getUri()->getScheme()
        ) {
            $uri = $request->getUri()->withUserInfo('', '');
            $modify['set_headers']['Referer'] = (string) $uri;
        } else {
            $modify['remove_headers'][] = 'Referer';
        }

        // Remove Authorization header if host is different.
        if ($request->getUri()->getHost() !== $modify['uri']->getHost()) {
            $modify['remove_headers'][] = 'Authorization';
        }

        return Psr7\modify_request($request, $modify);
    }

    /**
     * Set the appropriate URL on the request based on the location header
     *
     * @param RequestInterface  $request
     * @param ResponseInterface $response
     * @param array             $protocols
     *
     * @return UriInterface
     */
    private function redirectUri(
        RequestInterface $request,
        ResponseInterface $response,
        array $protocols
    ) {
        $location = Psr7\UriResolver::resolve(
            $request->getUri(),
            new Psr7\Uri($response->getHeaderLine('Location'))
        );

        // Ensure that the redirect URI is allowed based on the protocols.
        if (!in_array($location->getScheme(), $protocols)) {
            throw new BadResponseException(
                sprintf(
                    'Redirect URI, %s, does not use one of the allowed redirect protocols: %s',
                    $location,
                    implode(', ', $protocols)
                ),
                $request,
                $response
            );
        }

        return $location;
    }
}
vendor/guzzlehttp/guzzle/src/Client.php000064400000040366151327705700014337 0ustar00<?php
namespace WPvividGuzzleHttp;

use WPvividGuzzleHttp\Cookie\CookieJar;
use WPvividGuzzleHttp\Promise;
use WPvividGuzzleHttp\Psr7;
use WPvividPsr\Http\Message\UriInterface;
use WPvividPsr\Http\Message\RequestInterface;
use WPvividPsr\Http\Message\ResponseInterface;

/**
 * @method ResponseInterface get(string|UriInterface $uri, array $options = [])
 * @method ResponseInterface head(string|UriInterface $uri, array $options = [])
 * @method ResponseInterface put(string|UriInterface $uri, array $options = [])
 * @method ResponseInterface post(string|UriInterface $uri, array $options = [])
 * @method ResponseInterface patch(string|UriInterface $uri, array $options = [])
 * @method ResponseInterface delete(string|UriInterface $uri, array $options = [])
 * @method Promise\PromiseInterface getAsync(string|UriInterface $uri, array $options = [])
 * @method Promise\PromiseInterface headAsync(string|UriInterface $uri, array $options = [])
 * @method Promise\PromiseInterface putAsync(string|UriInterface $uri, array $options = [])
 * @method Promise\PromiseInterface postAsync(string|UriInterface $uri, array $options = [])
 * @method Promise\PromiseInterface patchAsync(string|UriInterface $uri, array $options = [])
 * @method Promise\PromiseInterface deleteAsync(string|UriInterface $uri, array $options = [])
 */
class Client implements ClientInterface
{
    /** @var array Default request options */
    private $config;

    /**
     * Clients accept an array of constructor parameters.
     *
     * Here's an example of creating a client using a base_uri and an array of
     * default request options to apply to each request:
     *
     *     $client = new Client([
     *         'base_uri'        => 'http://www.foo.com/1.0/',
     *         'timeout'         => 0,
     *         'allow_redirects' => false,
     *         'proxy'           => '192.168.16.1:10'
     *     ]);
     *
     * Client configuration settings include the following options:
     *
     * - handler: (callable) Function that transfers HTTP requests over the
     *   wire. The function is called with a Psr7\Http\Message\RequestInterface
     *   and array of transfer options, and must return a
     *   GuzzleHttp\Promise\PromiseInterface that is fulfilled with a
     *   Psr7\Http\Message\ResponseInterface on success. "handler" is a
     *   constructor only option that cannot be overridden in per/request
     *   options. If no handler is provided, a default handler will be created
     *   that enables all of the request options below by attaching all of the
     *   default middleware to the handler.
     * - base_uri: (string|UriInterface) Base URI of the client that is merged
     *   into relative URIs. Can be a string or instance of UriInterface.
     * - **: any request option
     *
     * @param array $config Client configuration settings.
     *
     * @see \GuzzleHttp\RequestOptions for a list of available request options.
     */
    public function __construct(array $config = [])
    {
        if (!isset($config['handler'])) {
            $config['handler'] = HandlerStack::create();
        } elseif (!is_callable($config['handler'])) {
            throw new \InvalidArgumentException('handler must be a callable');
        }

        // Convert the base_uri to a UriInterface
        if (isset($config['base_uri'])) {
            $config['base_uri'] = Psr7\uri_for($config['base_uri']);
        }

        $this->configureDefaults($config);
    }

    public function __call($method, $args)
    {
        if (count($args) < 1) {
            throw new \InvalidArgumentException('Magic request methods require a URI and optional options array');
        }

        $uri = $args[0];
        $opts = isset($args[1]) ? $args[1] : [];

        return substr($method, -5) === 'Async'
            ? $this->requestAsync(substr($method, 0, -5), $uri, $opts)
            : $this->request($method, $uri, $opts);
    }

    public function sendAsync(RequestInterface $request, array $options = [])
    {
        // Merge the base URI into the request URI if needed.
        $options = $this->prepareDefaults($options);

        return $this->transfer(
            $request->withUri($this->buildUri($request->getUri(), $options), $request->hasHeader('Host')),
            $options
        );
    }

    public function send(RequestInterface $request, array $options = [])
    {
        $options[RequestOptions::SYNCHRONOUS] = true;
        return $this->sendAsync($request, $options)->wait();
    }

    public function requestAsync($method, $uri = '', array $options = [])
    {
        $options = $this->prepareDefaults($options);
        // Remove request modifying parameter because it can be done up-front.
        $headers = isset($options['headers']) ? $options['headers'] : [];
        $body = isset($options['body']) ? $options['body'] : null;
        $version = isset($options['version']) ? $options['version'] : '1.1';
        // Merge the URI into the base URI.
        $uri = $this->buildUri($uri, $options);
        if (is_array($body)) {
            $this->invalidBody();
        }
        $request = new Psr7\Request($method, $uri, $headers, $body, $version);
        // Remove the option so that they are not doubly-applied.
        unset($options['headers'], $options['body'], $options['version']);

        return $this->transfer($request, $options);
    }

    public function request($method, $uri = '', array $options = [])
    {
        $options[RequestOptions::SYNCHRONOUS] = true;
        return $this->requestAsync($method, $uri, $options)->wait();
    }

    public function getConfig($option = null)
    {
        return $option === null
            ? $this->config
            : (isset($this->config[$option]) ? $this->config[$option] : null);
    }

    private function buildUri($uri, array $config)
    {
        // for BC we accept null which would otherwise fail in uri_for
        $uri = Psr7\uri_for($uri === null ? '' : $uri);

        if (isset($config['base_uri'])) {
            $uri = Psr7\UriResolver::resolve(Psr7\uri_for($config['base_uri']), $uri);
        }

        return $uri->getScheme() === '' && $uri->getHost() !== '' ? $uri->withScheme('http') : $uri;
    }

    /**
     * Configures the default options for a client.
     *
     * @param array $config
     */
    private function configureDefaults(array $config)
    {
        $defaults = [
            'allow_redirects' => RedirectMiddleware::$defaultSettings,
            'http_errors'     => true,
            'decode_content'  => true,
            'verify'          => true,
            'cookies'         => false
        ];

        // Use the standard Linux HTTP_PROXY and HTTPS_PROXY if set.

        // We can only trust the HTTP_PROXY environment variable in a CLI
        // process due to the fact that PHP has no reliable mechanism to
        // get environment variables that start with "HTTP_".
        if (php_sapi_name() == 'cli' && getenv('HTTP_PROXY')) {
            $defaults['proxy']['http'] = getenv('HTTP_PROXY');
        }

        if ($proxy = getenv('HTTPS_PROXY')) {
            $defaults['proxy']['https'] = $proxy;
        }

        if ($noProxy = getenv('NO_PROXY')) {
            $cleanedNoProxy = str_replace(' ', '', $noProxy);
            $defaults['proxy']['no'] = explode(',', $cleanedNoProxy);
        }

        $this->config = $config + $defaults;

        if (!empty($config['cookies']) && $config['cookies'] === true) {
            $this->config['cookies'] = new CookieJar();
        }

        // Add the default user-agent header.
        if (!isset($this->config['headers'])) {
            $this->config['headers'] = ['User-Agent' => default_user_agent()];
        } else {
            // Add the User-Agent header if one was not already set.
            foreach (array_keys($this->config['headers']) as $name) {
                if (strtolower($name) === 'user-agent') {
                    return;
                }
            }
            $this->config['headers']['User-Agent'] = default_user_agent();
        }
    }

    /**
     * Merges default options into the array.
     *
     * @param array $options Options to modify by reference
     *
     * @return array
     */
    private function prepareDefaults($options)
    {
        $defaults = $this->config;

        if (!empty($defaults['headers'])) {
            // Default headers are only added if they are not present.
            $defaults['_conditional'] = $defaults['headers'];
            unset($defaults['headers']);
        }

        // Special handling for headers is required as they are added as
        // conditional headers and as headers passed to a request ctor.
        if (array_key_exists('headers', $options)) {
            // Allows default headers to be unset.
            if ($options['headers'] === null) {
                $defaults['_conditional'] = null;
                unset($options['headers']);
            } elseif (!is_array($options['headers'])) {
                throw new \InvalidArgumentException('headers must be an array');
            }
        }

        // Shallow merge defaults underneath options.
        $result = $options + $defaults;

        // Remove null values.
        foreach ($result as $k => $v) {
            if ($v === null) {
                unset($result[$k]);
            }
        }

        return $result;
    }

    /**
     * Transfers the given request and applies request options.
     *
     * The URI of the request is not modified and the request options are used
     * as-is without merging in default options.
     *
     * @param RequestInterface $request
     * @param array            $options
     *
     * @return Promise\PromiseInterface
     */
    private function transfer(RequestInterface $request, array $options)
    {
        // save_to -> sink
        if (isset($options['save_to'])) {
            $options['sink'] = $options['save_to'];
            unset($options['save_to']);
        }

        // exceptions -> http_errors
        if (isset($options['exceptions'])) {
            $options['http_errors'] = $options['exceptions'];
            unset($options['exceptions']);
        }

        $request = $this->applyOptions($request, $options);
        $handler = $options['handler'];

        try {
            return Promise\promise_for($handler($request, $options));
        } catch (\Exception $e) {
            return Promise\rejection_for($e);
        }
    }

    /**
     * Applies the array of request options to a request.
     *
     * @param RequestInterface $request
     * @param array            $options
     *
     * @return RequestInterface
     */
    private function applyOptions(RequestInterface $request, array &$options)
    {
        $modify = [
            'set_headers' => [],
        ];

        if (isset($options['headers'])) {
            $modify['set_headers'] = $options['headers'];
            unset($options['headers']);
        }

        if (isset($options['form_params'])) {
            if (isset($options['multipart'])) {
                throw new \InvalidArgumentException('You cannot use '
                    . 'form_params and multipart at the same time. Use the '
                    . 'form_params option if you want to send application/'
                    . 'x-www-form-urlencoded requests, and the multipart '
                    . 'option to send multipart/form-data requests.');
            }
            $options['body'] = http_build_query($options['form_params'], '', '&');
            unset($options['form_params']);
            // Ensure that we don't have the header in different case and set the new value.
            $options['_conditional'] = Psr7\_caseless_remove(['Content-Type'], $options['_conditional']);
            $options['_conditional']['Content-Type'] = 'application/x-www-form-urlencoded';
        }

        if (isset($options['multipart'])) {
            $options['body'] = new Psr7\MultipartStream($options['multipart']);
            unset($options['multipart']);
        }

        if (isset($options['json'])) {
            $options['body'] = \WPvividGuzzleHttp\json_encode($options['json']);
            unset($options['json']);
            // Ensure that we don't have the header in different case and set the new value.
            $options['_conditional'] = Psr7\_caseless_remove(['Content-Type'], $options['_conditional']);
            $options['_conditional']['Content-Type'] = 'application/json';
        }

        if (!empty($options['decode_content'])
            && $options['decode_content'] !== true
        ) {
            // Ensure that we don't have the header in different case and set the new value.
            $options['_conditional'] = Psr7\_caseless_remove(['Accept-Encoding'], $options['_conditional']);
            $modify['set_headers']['Accept-Encoding'] = $options['decode_content'];
        }

        if (isset($options['body'])) {
            if (is_array($options['body'])) {
                $this->invalidBody();
            }
            $modify['body'] = Psr7\stream_for($options['body']);
            unset($options['body']);
        }

        if (!empty($options['auth']) && is_array($options['auth'])) {
            $value = $options['auth'];
            $type = isset($value[2]) ? strtolower($value[2]) : 'basic';
            switch ($type) {
                case 'basic':
                    // Ensure that we don't have the header in different case and set the new value.
                    $modify['set_headers'] = Psr7\_caseless_remove(['Authorization'], $modify['set_headers']);
                    $modify['set_headers']['Authorization'] = 'Basic '
                        . base64_encode("$value[0]:$value[1]");
                    break;
                case 'digest':
                    // @todo: Do not rely on curl
                    $options['curl'][CURLOPT_HTTPAUTH] = CURLAUTH_DIGEST;
                    $options['curl'][CURLOPT_USERPWD] = "$value[0]:$value[1]";
                    break;
                case 'ntlm':
                    $options['curl'][CURLOPT_HTTPAUTH] = CURLAUTH_NTLM;
                    $options['curl'][CURLOPT_USERPWD] = "$value[0]:$value[1]";
                    break;
            }
        }

        if (isset($options['query'])) {
            $value = $options['query'];
            if (is_array($value)) {
                $value = http_build_query($value, null, '&', PHP_QUERY_RFC3986);
            }
            if (!is_string($value)) {
                throw new \InvalidArgumentException('query must be a string or array');
            }
            $modify['query'] = $value;
            unset($options['query']);
        }

        // Ensure that sink is not an invalid value.
        if (isset($options['sink'])) {
            // TODO: Add more sink validation?
            if (is_bool($options['sink'])) {
                throw new \InvalidArgumentException('sink must not be a boolean');
            }
        }

        $request = Psr7\modify_request($request, $modify);
        if ($request->getBody() instanceof Psr7\MultipartStream) {
            // Use a multipart/form-data POST if a Content-Type is not set.
            // Ensure that we don't have the header in different case and set the new value.
            $options['_conditional'] = Psr7\_caseless_remove(['Content-Type'], $options['_conditional']);
            $options['_conditional']['Content-Type'] = 'multipart/form-data; boundary='
                . $request->getBody()->getBoundary();
        }

        // Merge in conditional headers if they are not present.
        if (isset($options['_conditional'])) {
            // Build up the changes so it's in a single clone of the message.
            $modify = [];
            foreach ($options['_conditional'] as $k => $v) {
                if (!$request->hasHeader($k)) {
                    $modify['set_headers'][$k] = $v;
                }
            }
            $request = Psr7\modify_request($request, $modify);
            // Don't pass this internal value along to middleware/handlers.
            unset($options['_conditional']);
        }

        return $request;
    }

    private function invalidBody()
    {
        throw new \InvalidArgumentException('Passing in the "body" request '
            . 'option as an array to send a POST request has been deprecated. '
            . 'Please use the "form_params" request option to send a '
            . 'application/x-www-form-urlencoded request, or the "multipart" '
            . 'request option to send a multipart/form-data request.');
    }
}
vendor/guzzlehttp/guzzle/src/RetryMiddleware.php000064400000006331151327705700016216 0ustar00<?php
namespace WPvividGuzzleHttp;

use WPvividGuzzleHttp\Promise\PromiseInterface;
use WPvividGuzzleHttp\Promise\RejectedPromise;
use WPvividGuzzleHttp\Psr7;
use WPvividPsr\Http\Message\RequestInterface;
use WPvividPsr\Http\Message\ResponseInterface;

/**
 * Middleware that retries requests based on the boolean result of
 * invoking the provided "decider" function.
 */
class RetryMiddleware
{
    /** @var callable  */
    private $nextHandler;

    /** @var callable */
    private $decider;

    /**
     * @param callable $decider     Function that accepts the number of retries,
     *                              a request, [response], and [exception] and
     *                              returns true if the request is to be
     *                              retried.
     * @param callable $nextHandler Next handler to invoke.
     * @param callable $delay       Function that accepts the number of retries
     *                              and [response] and returns the number of
     *                              milliseconds to delay.
     */
    public function __construct(
        callable $decider,
        callable $nextHandler,
        callable $delay = null
    ) {
        $this->decider = $decider;
        $this->nextHandler = $nextHandler;
        $this->delay = $delay ?: __CLASS__ . '::exponentialDelay';
    }

    /**
     * Default exponential backoff delay function.
     *
     * @param $retries
     *
     * @return int
     */
    public static function exponentialDelay($retries)
    {
        return (int) pow(2, $retries - 1);
    }

    /**
     * @param RequestInterface $request
     * @param array            $options
     *
     * @return PromiseInterface
     */
    public function __invoke(RequestInterface $request, array $options)
    {
        if (!isset($options['retries'])) {
            $options['retries'] = 0;
        }

        $fn = $this->nextHandler;
        return $fn($request, $options)
            ->then(
                $this->onFulfilled($request, $options),
                $this->onRejected($request, $options)
            );
    }

    private function onFulfilled(RequestInterface $req, array $options)
    {
        return function ($value) use ($req, $options) {
            if (!call_user_func(
                $this->decider,
                $options['retries'],
                $req,
                $value,
                null
            )) {
                return $value;
            }
            return $this->doRetry($req, $options, $value);
        };
    }

    private function onRejected(RequestInterface $req, array $options)
    {
        return function ($reason) use ($req, $options) {
            if (!call_user_func(
                $this->decider,
                $options['retries'],
                $req,
                null,
                $reason
            )) {
                return \WPvividGuzzleHttp\Promise\rejection_for($reason);
            }
            return $this->doRetry($req, $options);
        };
    }

    private function doRetry(RequestInterface $request, array $options, ResponseInterface $response = null)
    {
        $options['delay'] = call_user_func($this->delay, ++$options['retries'], $response);

        return $this($request, $options);
    }
}
vendor/guzzlehttp/guzzle/src/Cookie/SessionCookieJar.php000064400000003606151327705700017540 0ustar00<?php
namespace WPvividGuzzleHttp\Cookie;

/**
 * Persists cookies in the client session
 */
class SessionCookieJar extends CookieJar
{
    /** @var string session key */
    private $sessionKey;
    
    /** @var bool Control whether to persist session cookies or not. */
    private $storeSessionCookies;

    /**
     * Create a new SessionCookieJar object
     *
     * @param string $sessionKey        Session key name to store the cookie
     *                                  data in session
     * @param bool $storeSessionCookies Set to true to store session cookies
     *                                  in the cookie jar.
     */
    public function __construct($sessionKey, $storeSessionCookies = false)
    {
        $this->sessionKey = $sessionKey;
        $this->storeSessionCookies = $storeSessionCookies;
        $this->load();
    }

    /**
     * Saves cookies to session when shutting down
     */
    public function __destruct()
    {
        $this->save();
    }

    /**
     * Save cookies to the client session
     */
    public function save()
    {
        $json = [];
        foreach ($this as $cookie) {
            /** @var SetCookie $cookie */
            if (CookieJar::shouldPersist($cookie, $this->storeSessionCookies)) {
                $json[] = $cookie->toArray();
            }
        }

        $_SESSION[$this->sessionKey] = json_encode($json);
    }

    /**
     * Load the contents of the client session into the data array
     */
    protected function load()
    {
        if (!isset($_SESSION[$this->sessionKey])) {
            return;
        }
        $data = json_decode($_SESSION[$this->sessionKey], true);
        if (is_array($data)) {
            foreach ($data as $cookie) {
                $this->setCookie(new SetCookie($cookie));
            }
        } elseif (strlen($data)) {
            throw new \RuntimeException("Invalid cookie data");
        }
    }
}
vendor/guzzlehttp/guzzle/src/Cookie/SetCookie.php000064400000024312151327705700016210 0ustar00<?php
namespace WPvividGuzzleHttp\Cookie;

/**
 * Set-Cookie object
 */
class SetCookie
{
    /** @var array */
    private static $defaults = [
        'Name'     => null,
        'Value'    => null,
        'Domain'   => null,
        'Path'     => '/',
        'Max-Age'  => null,
        'Expires'  => null,
        'Secure'   => false,
        'Discard'  => false,
        'HttpOnly' => false
    ];

    /** @var array Cookie data */
    private $data;

    /**
     * Create a new SetCookie object from a string
     *
     * @param string $cookie Set-Cookie header string
     *
     * @return self
     */
    public static function fromString($cookie)
    {
        // Create the default return array
        $data = self::$defaults;
        // Explode the cookie string using a series of semicolons
        $pieces = array_filter(array_map('trim', explode(';', $cookie)));
        // The name of the cookie (first kvp) must exist and include an equal sign.
        if (empty($pieces[0]) || !strpos($pieces[0], '=')) {
            return new self($data);
        }

        // Add the cookie pieces into the parsed data array
        foreach ($pieces as $part) {
            $cookieParts = explode('=', $part, 2);
            $key = trim($cookieParts[0]);
            $value = isset($cookieParts[1])
                ? trim($cookieParts[1], " \n\r\t\0\x0B")
                : true;

            // Only check for non-cookies when cookies have been found
            if (empty($data['Name'])) {
                $data['Name'] = $key;
                $data['Value'] = $value;
            } else {
                foreach (array_keys(self::$defaults) as $search) {
                    if (!strcasecmp($search, $key)) {
                        $data[$search] = $value;
                        continue 2;
                    }
                }
                $data[$key] = $value;
            }
        }

        return new self($data);
    }

    /**
     * @param array $data Array of cookie data provided by a Cookie parser
     */
    public function __construct(array $data = [])
    {
        $this->data = array_replace(self::$defaults, $data);
        // Extract the Expires value and turn it into a UNIX timestamp if needed
        if (!$this->getExpires() && $this->getMaxAge()) {
            // Calculate the Expires date
            $this->setExpires(time() + $this->getMaxAge());
        } elseif ($this->getExpires() && !is_numeric($this->getExpires())) {
            $this->setExpires($this->getExpires());
        }
    }

    public function __toString()
    {
        $str = $this->data['Name'] . '=' . $this->data['Value'] . '; ';
        foreach ($this->data as $k => $v) {
            if ($k !== 'Name' && $k !== 'Value' && $v !== null && $v !== false) {
                if ($k === 'Expires') {
                    $str .= 'Expires=' . gmdate('D, d M Y H:i:s \G\M\T', $v) . '; ';
                } else {
                    $str .= ($v === true ? $k : "{$k}={$v}") . '; ';
                }
            }
        }

        return rtrim($str, '; ');
    }

    public function toArray()
    {
        return $this->data;
    }

    /**
     * Get the cookie name
     *
     * @return string
     */
    public function getName()
    {
        return $this->data['Name'];
    }

    /**
     * Set the cookie name
     *
     * @param string $name Cookie name
     */
    public function setName($name)
    {
        $this->data['Name'] = $name;
    }

    /**
     * Get the cookie value
     *
     * @return string
     */
    public function getValue()
    {
        return $this->data['Value'];
    }

    /**
     * Set the cookie value
     *
     * @param string $value Cookie value
     */
    public function setValue($value)
    {
        $this->data['Value'] = $value;
    }

    /**
     * Get the domain
     *
     * @return string|null
     */
    public function getDomain()
    {
        return $this->data['Domain'];
    }

    /**
     * Set the domain of the cookie
     *
     * @param string $domain
     */
    public function setDomain($domain)
    {
        $this->data['Domain'] = $domain;
    }

    /**
     * Get the path
     *
     * @return string
     */
    public function getPath()
    {
        return $this->data['Path'];
    }

    /**
     * Set the path of the cookie
     *
     * @param string $path Path of the cookie
     */
    public function setPath($path)
    {
        $this->data['Path'] = $path;
    }

    /**
     * Maximum lifetime of the cookie in seconds
     *
     * @return int|null
     */
    public function getMaxAge()
    {
        return $this->data['Max-Age'];
    }

    /**
     * Set the max-age of the cookie
     *
     * @param int $maxAge Max age of the cookie in seconds
     */
    public function setMaxAge($maxAge)
    {
        $this->data['Max-Age'] = $maxAge;
    }

    /**
     * The UNIX timestamp when the cookie Expires
     *
     * @return mixed
     */
    public function getExpires()
    {
        return $this->data['Expires'];
    }

    /**
     * Set the unix timestamp for which the cookie will expire
     *
     * @param int $timestamp Unix timestamp
     */
    public function setExpires($timestamp)
    {
        $this->data['Expires'] = is_numeric($timestamp)
            ? (int) $timestamp
            : strtotime($timestamp);
    }

    /**
     * Get whether or not this is a secure cookie
     *
     * @return null|bool
     */
    public function getSecure()
    {
        return $this->data['Secure'];
    }

    /**
     * Set whether or not the cookie is secure
     *
     * @param bool $secure Set to true or false if secure
     */
    public function setSecure($secure)
    {
        $this->data['Secure'] = $secure;
    }

    /**
     * Get whether or not this is a session cookie
     *
     * @return null|bool
     */
    public function getDiscard()
    {
        return $this->data['Discard'];
    }

    /**
     * Set whether or not this is a session cookie
     *
     * @param bool $discard Set to true or false if this is a session cookie
     */
    public function setDiscard($discard)
    {
        $this->data['Discard'] = $discard;
    }

    /**
     * Get whether or not this is an HTTP only cookie
     *
     * @return bool
     */
    public function getHttpOnly()
    {
        return $this->data['HttpOnly'];
    }

    /**
     * Set whether or not this is an HTTP only cookie
     *
     * @param bool $httpOnly Set to true or false if this is HTTP only
     */
    public function setHttpOnly($httpOnly)
    {
        $this->data['HttpOnly'] = $httpOnly;
    }

    /**
     * Check if the cookie matches a path value.
     *
     * A request-path path-matches a given cookie-path if at least one of
     * the following conditions holds:
     *
     * - The cookie-path and the request-path are identical.
     * - The cookie-path is a prefix of the request-path, and the last
     *   character of the cookie-path is %x2F ("/").
     * - The cookie-path is a prefix of the request-path, and the first
     *   character of the request-path that is not included in the cookie-
     *   path is a %x2F ("/") character.
     *
     * @param string $requestPath Path to check against
     *
     * @return bool
     */
    public function matchesPath($requestPath)
    {
        $cookiePath = $this->getPath();

        // Match on exact matches or when path is the default empty "/"
        if ($cookiePath === '/' || $cookiePath == $requestPath) {
            return true;
        }

        // Ensure that the cookie-path is a prefix of the request path.
        if (0 !== strpos($requestPath, $cookiePath)) {
            return false;
        }

        // Match if the last character of the cookie-path is "/"
        if (substr($cookiePath, -1, 1) === '/') {
            return true;
        }

        // Match if the first character not included in cookie path is "/"
        return substr($requestPath, strlen($cookiePath), 1) === '/';
    }

    /**
     * Check if the cookie matches a domain value
     *
     * @param string $domain Domain to check against
     *
     * @return bool
     */
    public function matchesDomain($domain)
    {
        // Remove the leading '.' as per spec in RFC 6265.
        // http://tools.ietf.org/html/rfc6265#section-5.2.3
        $cookieDomain = ltrim($this->getDomain(), '.');

        // Domain not set or exact match.
        if (!$cookieDomain || !strcasecmp($domain, $cookieDomain)) {
            return true;
        }

        // Matching the subdomain according to RFC 6265.
        // http://tools.ietf.org/html/rfc6265#section-5.1.3
        if (filter_var($domain, FILTER_VALIDATE_IP)) {
            return false;
        }

        return (bool) preg_match('/\.' . preg_quote($cookieDomain, '/') . '$/', $domain);
    }

    /**
     * Check if the cookie is expired
     *
     * @return bool
     */
    public function isExpired()
    {
        return $this->getExpires() !== null && time() > $this->getExpires();
    }

    /**
     * Check if the cookie is valid according to RFC 6265
     *
     * @return bool|string Returns true if valid or an error message if invalid
     */
    public function validate()
    {
        // Names must not be empty, but can be 0
        $name = $this->getName();
        if (empty($name) && !is_numeric($name)) {
            return 'The cookie name must not be empty';
        }

        // Check if any of the invalid characters are present in the cookie name
        if (preg_match(
            '/[\x00-\x20\x22\x28-\x29\x2c\x2f\x3a-\x40\x5c\x7b\x7d\x7f]/',
            $name
        )) {
            return 'Cookie name must not contain invalid characters: ASCII '
                . 'Control characters (0-31;127), space, tab and the '
                . 'following characters: ()<>@,;:\"/?={}';
        }

        // Value must not be empty, but can be 0
        $value = $this->getValue();
        if (empty($value) && !is_numeric($value)) {
            return 'The cookie value must not be empty';
        }

        // Domains must not be empty, but can be 0
        // A "0" is not a valid internet domain, but may be used as server name
        // in a private network.
        $domain = $this->getDomain();
        if (empty($domain) && !is_numeric($domain)) {
            return 'The cookie domain must not be empty';
        }

        return true;
    }
}
vendor/guzzlehttp/guzzle/src/Cookie/CookieJar.php000064400000022115151327705700016170 0ustar00<?php
namespace WPvividGuzzleHttp\Cookie;

use WPvividPsr\Http\Message\RequestInterface;
use WPvividPsr\Http\Message\ResponseInterface;

/**
 * Cookie jar that stores cookies as an array
 */
class CookieJar implements CookieJarInterface
{
    /** @var SetCookie[] Loaded cookie data */
    private $cookies = [];

    /** @var bool */
    private $strictMode;

    /**
     * @param bool $strictMode   Set to true to throw exceptions when invalid
     *                           cookies are added to the cookie jar.
     * @param array $cookieArray Array of SetCookie objects or a hash of
     *                           arrays that can be used with the SetCookie
     *                           constructor
     */
    public function __construct($strictMode = false, $cookieArray = [])
    {
        $this->strictMode = $strictMode;

        foreach ($cookieArray as $cookie) {
            if (!($cookie instanceof SetCookie)) {
                $cookie = new SetCookie($cookie);
            }
            $this->setCookie($cookie);
        }
    }

    /**
     * Create a new Cookie jar from an associative array and domain.
     *
     * @param array  $cookies Cookies to create the jar from
     * @param string $domain  Domain to set the cookies to
     *
     * @return self
     */
    public static function fromArray(array $cookies, $domain)
    {
        $cookieJar = new self();
        foreach ($cookies as $name => $value) {
            $cookieJar->setCookie(new SetCookie([
                'Domain'  => $domain,
                'Name'    => $name,
                'Value'   => $value,
                'Discard' => true
            ]));
        }

        return $cookieJar;
    }

    /**
     * @deprecated
     */
    public static function getCookieValue($value)
    {
        return $value;
    }

    /**
     * Evaluate if this cookie should be persisted to storage
     * that survives between requests.
     *
     * @param SetCookie $cookie Being evaluated.
     * @param bool $allowSessionCookies If we should persist session cookies
     * @return bool
     */
    public static function shouldPersist(
        SetCookie $cookie,
        $allowSessionCookies = false
    ) {
        if ($cookie->getExpires() || $allowSessionCookies) {
            if (!$cookie->getDiscard()) {
                return true;
            }
        }

        return false;
    }

    /**
     * Finds and returns the cookie based on the name
     *
     * @param string $name cookie name to search for
     * @return SetCookie|null cookie that was found or null if not found
     */
    public function getCookieByName($name)
    {
        // don't allow a null name
        if ($name === null) {
            return null;
        }
        foreach ($this->cookies as $cookie) {
            if ($cookie->getName() !== null && strcasecmp($cookie->getName(), $name) === 0) {
                return $cookie;
            }
        }
    }

    public function toArray()
    {
        return array_map(function (SetCookie $cookie) {
            return $cookie->toArray();
        }, $this->getIterator()->getArrayCopy());
    }

    public function clear($domain = null, $path = null, $name = null)
    {
        if (!$domain) {
            $this->cookies = [];
            return;
        } elseif (!$path) {
            $this->cookies = array_filter(
                $this->cookies,
                function (SetCookie $cookie) use ($path, $domain) {
                    return !$cookie->matchesDomain($domain);
                }
            );
        } elseif (!$name) {
            $this->cookies = array_filter(
                $this->cookies,
                function (SetCookie $cookie) use ($path, $domain) {
                    return !($cookie->matchesPath($path) &&
                        $cookie->matchesDomain($domain));
                }
            );
        } else {
            $this->cookies = array_filter(
                $this->cookies,
                function (SetCookie $cookie) use ($path, $domain, $name) {
                    return !($cookie->getName() == $name &&
                        $cookie->matchesPath($path) &&
                        $cookie->matchesDomain($domain));
                }
            );
        }
    }

    public function clearSessionCookies()
    {
        $this->cookies = array_filter(
            $this->cookies,
            function (SetCookie $cookie) {
                return !$cookie->getDiscard() && $cookie->getExpires();
            }
        );
    }

    public function setCookie(SetCookie $cookie)
    {
        // If the name string is empty (but not 0), ignore the set-cookie
        // string entirely.
        $name = $cookie->getName();
        if (!$name && $name !== '0') {
            return false;
        }

        // Only allow cookies with set and valid domain, name, value
        $result = $cookie->validate();
        if ($result !== true) {
            if ($this->strictMode) {
                throw new \RuntimeException('Invalid cookie: ' . $result);
            } else {
                $this->removeCookieIfEmpty($cookie);
                return false;
            }
        }

        // Resolve conflicts with previously set cookies
        foreach ($this->cookies as $i => $c) {

            // Two cookies are identical, when their path, and domain are
            // identical.
            if ($c->getPath() != $cookie->getPath() ||
                $c->getDomain() != $cookie->getDomain() ||
                $c->getName() != $cookie->getName()
            ) {
                continue;
            }

            // The previously set cookie is a discard cookie and this one is
            // not so allow the new cookie to be set
            if (!$cookie->getDiscard() && $c->getDiscard()) {
                unset($this->cookies[$i]);
                continue;
            }

            // If the new cookie's expiration is further into the future, then
            // replace the old cookie
            if ($cookie->getExpires() > $c->getExpires()) {
                unset($this->cookies[$i]);
                continue;
            }

            // If the value has changed, we better change it
            if ($cookie->getValue() !== $c->getValue()) {
                unset($this->cookies[$i]);
                continue;
            }

            // The cookie exists, so no need to continue
            return false;
        }

        $this->cookies[] = $cookie;

        return true;
    }

    public function count()
    {
        return count($this->cookies);
    }

    public function getIterator()
    {
        return new \ArrayIterator(array_values($this->cookies));
    }

    public function extractCookies(
        RequestInterface $request,
        ResponseInterface $response
    ) {
        if ($cookieHeader = $response->getHeader('Set-Cookie')) {
            foreach ($cookieHeader as $cookie) {
                $sc = SetCookie::fromString($cookie);
                if (!$sc->getDomain()) {
                    $sc->setDomain($request->getUri()->getHost());
                }
                if (0 !== strpos($sc->getPath(), '/')) {
                    $sc->setPath($this->getCookiePathFromRequest($request));
                }
                $this->setCookie($sc);
            }
        }
    }

    /**
     * Computes cookie path following RFC 6265 section 5.1.4
     *
     * @link https://tools.ietf.org/html/rfc6265#section-5.1.4
     *
     * @param RequestInterface $request
     * @return string
     */
    private function getCookiePathFromRequest(RequestInterface $request)
    {
        $uriPath = $request->getUri()->getPath();
        if (''  === $uriPath) {
            return '/';
        }
        if (0 !== strpos($uriPath, '/')) {
            return '/';
        }
        if ('/' === $uriPath) {
            return '/';
        }
        if (0 === $lastSlashPos = strrpos($uriPath, '/')) {
            return '/';
        }

        return substr($uriPath, 0, $lastSlashPos);
    }

    public function withCookieHeader(RequestInterface $request)
    {
        $values = [];
        $uri = $request->getUri();
        $scheme = $uri->getScheme();
        $host = $uri->getHost();
        $path = $uri->getPath() ?: '/';

        foreach ($this->cookies as $cookie) {
            if ($cookie->matchesPath($path) &&
                $cookie->matchesDomain($host) &&
                !$cookie->isExpired() &&
                (!$cookie->getSecure() || $scheme === 'https')
            ) {
                $values[] = $cookie->getName() . '='
                    . $cookie->getValue();
            }
        }

        return $values
            ? $request->withHeader('Cookie', implode('; ', $values))
            : $request;
    }

    /**
     * If a cookie already exists and the server asks to set it again with a
     * null value, the cookie must be deleted.
     *
     * @param SetCookie $cookie
     */
    private function removeCookieIfEmpty(SetCookie $cookie)
    {
        $cookieValue = $cookie->getValue();
        if ($cookieValue === null || $cookieValue === '') {
            $this->clear(
                $cookie->getDomain(),
                $cookie->getPath(),
                $cookie->getName()
            );
        }
    }
}
vendor/guzzlehttp/guzzle/src/Cookie/FileCookieJar.php000064400000005116151327705700016772 0ustar00<?php
namespace WPvividGuzzleHttp\Cookie;

/**
 * Persists non-session cookies using a JSON formatted file
 */
class FileCookieJar extends CookieJar
{
    /** @var string filename */
    private $filename;

    /** @var bool Control whether to persist session cookies or not. */
    private $storeSessionCookies;

    /**
     * Create a new FileCookieJar object
     *
     * @param string $cookieFile        File to store the cookie data
     * @param bool $storeSessionCookies Set to true to store session cookies
     *                                  in the cookie jar.
     *
     * @throws \RuntimeException if the file cannot be found or created
     */
    public function __construct($cookieFile, $storeSessionCookies = false)
    {
        $this->filename = $cookieFile;
        $this->storeSessionCookies = $storeSessionCookies;

        if (file_exists($cookieFile)) {
            $this->load($cookieFile);
        }
    }

    /**
     * Saves the file when shutting down
     */
    public function __destruct()
    {
        $this->save($this->filename);
    }

    /**
     * Saves the cookies to a file.
     *
     * @param string $filename File to save
     * @throws \RuntimeException if the file cannot be found or created
     */
    public function save($filename)
    {
        $json = [];
        foreach ($this as $cookie) {
            /** @var SetCookie $cookie */
            if (CookieJar::shouldPersist($cookie, $this->storeSessionCookies)) {
                $json[] = $cookie->toArray();
            }
        }

        $jsonStr = \WPvividGuzzleHttp\json_encode($json);
        if (false === file_put_contents($filename, $jsonStr)) {
            throw new \RuntimeException("Unable to save file {$filename}");
        }
    }

    /**
     * Load cookies from a JSON formatted file.
     *
     * Old cookies are kept unless overwritten by newly loaded ones.
     *
     * @param string $filename Cookie file to load.
     * @throws \RuntimeException if the file cannot be loaded.
     */
    public function load($filename)
    {
        $json = file_get_contents($filename);
        if (false === $json) {
            throw new \RuntimeException("Unable to load file {$filename}");
        } elseif ($json === '') {
            return;
        }

        $data = \WPvividGuzzleHttp\json_decode($json, true);
        if (is_array($data)) {
            foreach (json_decode($json, true) as $cookie) {
                $this->setCookie(new SetCookie($cookie));
            }
        } elseif (strlen($data)) {
            throw new \RuntimeException("Invalid cookie file: {$filename}");
        }
    }
}
vendor/guzzlehttp/guzzle/src/Cookie/CookieJarInterface.php000064400000005367151327705700020023 0ustar00<?php
namespace WPvividGuzzleHttp\Cookie;

use WPvividPsr\Http\Message\RequestInterface;
use WPvividPsr\Http\Message\ResponseInterface;

/**
 * Stores HTTP cookies.
 *
 * It extracts cookies from HTTP requests, and returns them in HTTP responses.
 * CookieJarInterface instances automatically expire contained cookies when
 * necessary. Subclasses are also responsible for storing and retrieving
 * cookies from a file, database, etc.
 *
 * @link http://docs.python.org/2/library/cookielib.html Inspiration
 */
interface CookieJarInterface extends \Countable, \IteratorAggregate
{
    /**
     * Create a request with added cookie headers.
     *
     * If no matching cookies are found in the cookie jar, then no Cookie
     * header is added to the request and the same request is returned.
     *
     * @param RequestInterface $request Request object to modify.
     *
     * @return RequestInterface returns the modified request.
     */
    public function withCookieHeader(RequestInterface $request);

    /**
     * Extract cookies from an HTTP response and store them in the CookieJar.
     *
     * @param RequestInterface  $request  Request that was sent
     * @param ResponseInterface $response Response that was received
     */
    public function extractCookies(
        RequestInterface $request,
        ResponseInterface $response
    );

    /**
     * Sets a cookie in the cookie jar.
     *
     * @param SetCookie $cookie Cookie to set.
     *
     * @return bool Returns true on success or false on failure
     */
    public function setCookie(SetCookie $cookie);

    /**
     * Remove cookies currently held in the cookie jar.
     *
     * Invoking this method without arguments will empty the whole cookie jar.
     * If given a $domain argument only cookies belonging to that domain will
     * be removed. If given a $domain and $path argument, cookies belonging to
     * the specified path within that domain are removed. If given all three
     * arguments, then the cookie with the specified name, path and domain is
     * removed.
     *
     * @param string $domain Clears cookies matching a domain
     * @param string $path   Clears cookies matching a domain and path
     * @param string $name   Clears cookies matching a domain, path, and name
     *
     * @return CookieJarInterface
     */
    public function clear($domain = null, $path = null, $name = null);

    /**
     * Discard all sessions cookies.
     *
     * Removes cookies that don't have an expire field or a have a discard
     * field set to true. To be called when the user agent shuts down according
     * to RFC 2965.
     */
    public function clearSessionCookies();

    /**
     * Converts the cookie jar to an array.
     *
     * @return array
     */
    public function toArray();
}
vendor/guzzlehttp/guzzle/src/Pool.php000064400000011117151327705700014022 0ustar00<?php
namespace WPvividGuzzleHttp;

use WPvividGuzzleHttp\Promise\PromisorInterface;
use WPvividPsr\Http\Message\RequestInterface;
use WPvividGuzzleHttp\Promise\EachPromise;

/**
 * Sends and iterator of requests concurrently using a capped pool size.
 *
 * The pool will read from an iterator until it is cancelled or until the
 * iterator is consumed. When a request is yielded, the request is sent after
 * applying the "request_options" request options (if provided in the ctor).
 *
 * When a function is yielded by the iterator, the function is provided the
 * "request_options" array that should be merged on top of any existing
 * options, and the function MUST then return a wait-able promise.
 */
class Pool implements PromisorInterface
{
    /** @var EachPromise */
    private $each;

    /**
     * @param ClientInterface $client   Client used to send the requests.
     * @param array|\Iterator $requests Requests or functions that return
     *                                  requests to send concurrently.
     * @param array           $config   Associative array of options
     *     - concurrency: (int) Maximum number of requests to send concurrently
     *     - options: Array of request options to apply to each request.
     *     - fulfilled: (callable) Function to invoke when a request completes.
     *     - rejected: (callable) Function to invoke when a request is rejected.
     */
    public function __construct(
        ClientInterface $client,
        $requests,
        array $config = []
    ) {
        // Backwards compatibility.
        if (isset($config['pool_size'])) {
            $config['concurrency'] = $config['pool_size'];
        } elseif (!isset($config['concurrency'])) {
            $config['concurrency'] = 25;
        }

        if (isset($config['options'])) {
            $opts = $config['options'];
            unset($config['options']);
        } else {
            $opts = [];
        }

        $iterable = \WPvividGuzzleHttp\Promise\iter_for($requests);
        $requests = function () use ($iterable, $client, $opts) {
            foreach ($iterable as $key => $rfn) {
                if ($rfn instanceof RequestInterface) {
                    yield $key => $client->sendAsync($rfn, $opts);
                } elseif (is_callable($rfn)) {
                    yield $key => $rfn($opts);
                } else {
                    throw new \InvalidArgumentException('Each value yielded by '
                        . 'the iterator must be a Psr7\Http\Message\RequestInterface '
                        . 'or a callable that returns a promise that fulfills '
                        . 'with a Psr7\Message\Http\ResponseInterface object.');
                }
            }
        };

        $this->each = new EachPromise($requests(), $config);
    }

    public function promise()
    {
        return $this->each->promise();
    }

    /**
     * Sends multiple requests concurrently and returns an array of responses
     * and exceptions that uses the same ordering as the provided requests.
     *
     * IMPORTANT: This method keeps every request and response in memory, and
     * as such, is NOT recommended when sending a large number or an
     * indeterminate number of requests concurrently.
     *
     * @param ClientInterface $client   Client used to send the requests
     * @param array|\Iterator $requests Requests to send concurrently.
     * @param array           $options  Passes through the options available in
     *                                  {@see GuzzleHttp\Pool::__construct}
     *
     * @return array Returns an array containing the response or an exception
     *               in the same order that the requests were sent.
     * @throws \InvalidArgumentException if the event format is incorrect.
     */
    public static function batch(
        ClientInterface $client,
        $requests,
        array $options = []
    ) {
        $res = [];
        self::cmpCallback($options, 'fulfilled', $res);
        self::cmpCallback($options, 'rejected', $res);
        $pool = new static($client, $requests, $options);
        $pool->promise()->wait();
        ksort($res);

        return $res;
    }

    private static function cmpCallback(array &$options, $name, array &$results)
    {
        if (!isset($options[$name])) {
            $options[$name] = function ($v, $k) use (&$results) {
                $results[$k] = $v;
            };
        } else {
            $currentFn = $options[$name];
            $options[$name] = function ($v, $k) use (&$results, $currentFn) {
                $currentFn($v, $k);
                $results[$k] = $v;
            };
        }
    }
}
vendor/guzzlehttp/guzzle/README.md000064400000007101151327705700013066 0ustar00Guzzle, PHP HTTP client
=======================

[![Latest Version](https://img.shields.io/github/release/guzzle/guzzle.svg?style=flat-square)](https://github.com/guzzle/guzzle/releases)
[![Build Status](https://img.shields.io/travis/guzzle/guzzle.svg?style=flat-square)](https://travis-ci.org/guzzle/guzzle)
[![Total Downloads](https://img.shields.io/packagist/dt/guzzlehttp/guzzle.svg?style=flat-square)](https://packagist.org/packages/guzzlehttp/guzzle)

Guzzle is a PHP HTTP client that makes it easy to send HTTP requests and
trivial to integrate with web services.

- Simple interface for building query strings, POST requests, streaming large
  uploads, streaming large downloads, using HTTP cookies, uploading JSON data,
  etc...
- Can send both synchronous and asynchronous requests using the same interface.
- Uses PSR-7 interfaces for requests, responses, and streams. This allows you
  to utilize other PSR-7 compatible libraries with Guzzle.
- Abstracts away the underlying HTTP transport, allowing you to write
  environment and transport agnostic code; i.e., no hard dependency on cURL,
  PHP streams, sockets, or non-blocking event loops.
- Middleware system allows you to augment and compose client behavior.

```php
$client = new \GuzzleHttp\Client();
$res = $client->request('GET', 'https://api.github.com/repos/guzzle/guzzle');
echo $res->getStatusCode();
// 200
echo $res->getHeaderLine('content-type');
// 'application/json; charset=utf8'
echo $res->getBody();
// '{"id": 1420053, "name": "guzzle", ...}'

// Send an asynchronous request.
$request = new \GuzzleHttp\Psr7\Request('GET', 'http://httpbin.org');
$promise = $client->sendAsync($request)->then(function ($response) {
    echo 'I completed! ' . $response->getBody();
});
$promise->wait();
```

## Help and docs

- [Documentation](http://guzzlephp.org/)
- [Stack Overflow](http://stackoverflow.com/questions/tagged/guzzle)
- [Gitter](https://gitter.im/guzzle/guzzle)


## Installing Guzzle

The recommended way to install Guzzle is through
[Composer](http://getcomposer.org).

```bash
# Install Composer
curl -sS https://getcomposer.org/installer | php
```

Next, run the Composer command to install the latest stable version of Guzzle:

```bash
php composer.phar require guzzlehttp/guzzle
```

After installing, you need to require Composer's autoloader:

```php
require 'vendor/autoload.php';
```

You can then later update Guzzle using composer:

 ```bash
composer.phar update
 ```


## Version Guidance

| Version | Status     | Packagist           | Namespace    | Repo                | Docs                | PSR-7 | PHP Version |
|---------|------------|---------------------|--------------|---------------------|---------------------|-------|-------------|
| 3.x     | EOL        | `guzzle/guzzle`     | `Guzzle`     | [v3][guzzle-3-repo] | [v3][guzzle-3-docs] | No    | >= 5.3.3    |
| 4.x     | EOL        | `guzzlehttp/guzzle` | `GuzzleHttp` | [v4][guzzle-4-repo] | N/A                 | No    | >= 5.4      |
| 5.x     | Maintained | `guzzlehttp/guzzle` | `GuzzleHttp` | [v5][guzzle-5-repo] | [v5][guzzle-5-docs] | No    | >= 5.4      |
| 6.x     | Latest     | `guzzlehttp/guzzle` | `GuzzleHttp` | [v6][guzzle-6-repo] | [v6][guzzle-6-docs] | Yes   | >= 5.5      |

[guzzle-3-repo]: https://github.com/guzzle/guzzle3
[guzzle-4-repo]: https://github.com/guzzle/guzzle/tree/4.x
[guzzle-5-repo]: https://github.com/guzzle/guzzle/tree/5.3
[guzzle-6-repo]: https://github.com/guzzle/guzzle
[guzzle-3-docs]: http://guzzle3.readthedocs.org/en/latest/
[guzzle-5-docs]: http://guzzle.readthedocs.org/en/5.3/
[guzzle-6-docs]: http://guzzle.readthedocs.org/en/latest/
vendor/guzzlehttp/guzzle/UPGRADING.md000064400000143120151327705700013453 0ustar00Guzzle Upgrade Guide
====================

5.0 to 6.0
----------

Guzzle now uses [PSR-7](http://www.php-fig.org/psr/psr-7/) for HTTP messages.
Due to the fact that these messages are immutable, this prompted a refactoring
of Guzzle to use a middleware based system rather than an event system. Any
HTTP message interaction (e.g., `GuzzleHttp\Message\Request`) need to be
updated to work with the new immutable PSR-7 request and response objects. Any
event listeners or subscribers need to be updated to become middleware
functions that wrap handlers (or are injected into a
`GuzzleHttp\HandlerStack`).

- Removed `GuzzleHttp\BatchResults`
- Removed `GuzzleHttp\Collection`
- Removed `GuzzleHttp\HasDataTrait`
- Removed `GuzzleHttp\ToArrayInterface`
- The `guzzlehttp/streams` dependency has been removed. Stream functionality
  is now present in the `GuzzleHttp\Psr7` namespace provided by the
  `guzzlehttp/psr7` package.
- Guzzle no longer uses ReactPHP promises and now uses the
  `guzzlehttp/promises` library. We use a custom promise library for three
  significant reasons:
  1. React promises (at the time of writing this) are recursive. Promise
     chaining and promise resolution will eventually blow the stack. Guzzle
     promises are not recursive as they use a sort of trampolining technique.
     Note: there has been movement in the React project to modify promises to
     no longer utilize recursion.
  2. Guzzle needs to have the ability to synchronously block on a promise to
     wait for a result. Guzzle promises allows this functionality (and does
     not require the use of recursion).
  3. Because we need to be able to wait on a result, doing so using React
     promises requires wrapping react promises with RingPHP futures. This
     overhead is no longer needed, reducing stack sizes, reducing complexity,
     and improving performance.
- `GuzzleHttp\Mimetypes` has been moved to a function in
  `GuzzleHttp\Psr7\mimetype_from_extension` and
  `GuzzleHttp\Psr7\mimetype_from_filename`.
- `GuzzleHttp\Query` and `GuzzleHttp\QueryParser` have been removed. Query
  strings must now be passed into request objects as strings, or provided to
  the `query` request option when creating requests with clients. The `query`
  option uses PHP's `http_build_query` to convert an array to a string. If you
  need a different serialization technique, you will need to pass the query
  string in as a string. There are a couple helper functions that will make
  working with query strings easier: `GuzzleHttp\Psr7\parse_query` and
  `GuzzleHttp\Psr7\build_query`.
- Guzzle no longer has a dependency on RingPHP. Due to the use of a middleware
  system based on PSR-7, using RingPHP and it's middleware system as well adds
  more complexity than the benefits it provides. All HTTP handlers that were
  present in RingPHP have been modified to work directly with PSR-7 messages
  and placed in the `GuzzleHttp\Handler` namespace. This significantly reduces
  complexity in Guzzle, removes a dependency, and improves performance. RingPHP
  will be maintained for Guzzle 5 support, but will no longer be a part of
  Guzzle 6.
- As Guzzle now uses a middleware based systems the event system and RingPHP
  integration has been removed. Note: while the event system has been removed,
  it is possible to add your own type of event system that is powered by the
  middleware system.
  - Removed the `Event` namespace.
  - Removed the `Subscriber` namespace.
  - Removed `Transaction` class
  - Removed `RequestFsm`
  - Removed `RingBridge`
  - `GuzzleHttp\Subscriber\Cookie` is now provided by
    `GuzzleHttp\Middleware::cookies`
  - `GuzzleHttp\Subscriber\HttpError` is now provided by
    `GuzzleHttp\Middleware::httpError`
  - `GuzzleHttp\Subscriber\History` is now provided by
    `GuzzleHttp\Middleware::history`
  - `GuzzleHttp\Subscriber\Mock` is now provided by
    `GuzzleHttp\Handler\MockHandler`
  - `GuzzleHttp\Subscriber\Prepare` is now provided by
    `GuzzleHttp\PrepareBodyMiddleware`
  - `GuzzleHttp\Subscriber\Redirect` is now provided by
    `GuzzleHttp\RedirectMiddleware`
- Guzzle now uses `Psr\Http\Message\UriInterface` (implements in
  `GuzzleHttp\Psr7\Uri`) for URI support. `GuzzleHttp\Url` is now gone.
- Static functions in `GuzzleHttp\Utils` have been moved to namespaced
  functions under the `GuzzleHttp` namespace. This requires either a Composer
  based autoloader or you to include functions.php.
- `GuzzleHttp\ClientInterface::getDefaultOption` has been renamed to
  `GuzzleHttp\ClientInterface::getConfig`.
- `GuzzleHttp\ClientInterface::setDefaultOption` has been removed.
- The `json` and `xml` methods of response objects has been removed. With the
  migration to strictly adhering to PSR-7 as the interface for Guzzle messages,
  adding methods to message interfaces would actually require Guzzle messages
  to extend from PSR-7 messages rather then work with them directly.

## Migrating to middleware

The change to PSR-7 unfortunately required significant refactoring to Guzzle
due to the fact that PSR-7 messages are immutable. Guzzle 5 relied on an event
system from plugins. The event system relied on mutability of HTTP messages and
side effects in order to work. With immutable messages, you have to change your
workflow to become more about either returning a value (e.g., functional
middlewares) or setting a value on an object. Guzzle v6 has chosen the
functional middleware approach.

Instead of using the event system to listen for things like the `before` event,
you now create a stack based middleware function that intercepts a request on
the way in and the promise of the response on the way out. This is a much
simpler and more predictable approach than the event system and works nicely
with PSR-7 middleware. Due to the use of promises, the middleware system is
also asynchronous.

v5:

```php
use GuzzleHttp\Event\BeforeEvent;
$client = new GuzzleHttp\Client();
// Get the emitter and listen to the before event.
$client->getEmitter()->on('before', function (BeforeEvent $e) {
    // Guzzle v5 events relied on mutation
    $e->getRequest()->setHeader('X-Foo', 'Bar');
});
```

v6:

In v6, you can modify the request before it is sent using the `mapRequest`
middleware. The idiomatic way in v6 to modify the request/response lifecycle is
to setup a handler middleware stack up front and inject the handler into a
client.

```php
use GuzzleHttp\Middleware;
// Create a handler stack that has all of the default middlewares attached
$handler = GuzzleHttp\HandlerStack::create();
// Push the handler onto the handler stack
$handler->push(Middleware::mapRequest(function (RequestInterface $request) {
    // Notice that we have to return a request object
    return $request->withHeader('X-Foo', 'Bar');
}));
// Inject the handler into the client
$client = new GuzzleHttp\Client(['handler' => $handler]);
```

## POST Requests

This version added the [`form_params`](http://guzzle.readthedocs.org/en/latest/request-options.html#form_params)
and `multipart` request options. `form_params` is an associative array of
strings or array of strings and is used to serialize an
`application/x-www-form-urlencoded` POST request. The
[`multipart`](http://guzzle.readthedocs.org/en/latest/request-options.html#multipart)
option is now used to send a multipart/form-data POST request.

`GuzzleHttp\Post\PostFile` has been removed. Use the `multipart` option to add
POST files to a multipart/form-data request.

The `body` option no longer accepts an array to send POST requests. Please use
`multipart` or `form_params` instead.

The `base_url` option has been renamed to `base_uri`.

4.x to 5.0
----------

## Rewritten Adapter Layer

Guzzle now uses [RingPHP](http://ringphp.readthedocs.org/en/latest) to send
HTTP requests. The `adapter` option in a `GuzzleHttp\Client` constructor
is still supported, but it has now been renamed to `handler`. Instead of
passing a `GuzzleHttp\Adapter\AdapterInterface`, you must now pass a PHP
`callable` that follows the RingPHP specification.

## Removed Fluent Interfaces

[Fluent interfaces were removed](http://ocramius.github.io/blog/fluent-interfaces-are-evil)
from the following classes:

- `GuzzleHttp\Collection`
- `GuzzleHttp\Url`
- `GuzzleHttp\Query`
- `GuzzleHttp\Post\PostBody`
- `GuzzleHttp\Cookie\SetCookie`

## Removed functions.php

Removed "functions.php", so that Guzzle is truly PSR-4 compliant. The following
functions can be used as replacements.

- `GuzzleHttp\json_decode` -> `GuzzleHttp\Utils::jsonDecode`
- `GuzzleHttp\get_path` -> `GuzzleHttp\Utils::getPath`
- `GuzzleHttp\Utils::setPath` -> `GuzzleHttp\set_path`
- `GuzzleHttp\Pool::batch` -> `GuzzleHttp\batch`. This function is, however,
  deprecated in favor of using `GuzzleHttp\Pool::batch()`.

The "procedural" global client has been removed with no replacement (e.g.,
`GuzzleHttp\get()`, `GuzzleHttp\post()`, etc.). Use a `GuzzleHttp\Client`
object as a replacement.

## `throwImmediately` has been removed

The concept of "throwImmediately" has been removed from exceptions and error
events. This control mechanism was used to stop a transfer of concurrent
requests from completing. This can now be handled by throwing the exception or
by cancelling a pool of requests or each outstanding future request
individually.

## headers event has been removed

Removed the "headers" event. This event was only useful for changing the
body a response once the headers of the response were known. You can implement
a similar behavior in a number of ways. One example might be to use a
FnStream that has access to the transaction being sent. For example, when the
first byte is written, you could check if the response headers match your
expectations, and if so, change the actual stream body that is being
written to.

## Updates to HTTP Messages

Removed the `asArray` parameter from
`GuzzleHttp\Message\MessageInterface::getHeader`. If you want to get a header
value as an array, then use the newly added `getHeaderAsArray()` method of
`MessageInterface`. This change makes the Guzzle interfaces compatible with
the PSR-7 interfaces.

3.x to 4.0
----------

## Overarching changes:

- Now requires PHP 5.4 or greater.
- No longer requires cURL to send requests.
- Guzzle no longer wraps every exception it throws. Only exceptions that are
  recoverable are now wrapped by Guzzle.
- Various namespaces have been removed or renamed.
- No longer requiring the Symfony EventDispatcher. A custom event dispatcher
  based on the Symfony EventDispatcher is
  now utilized in `GuzzleHttp\Event\EmitterInterface` (resulting in significant
  speed and functionality improvements).

Changes per Guzzle 3.x namespace are described below.

## Batch

The `Guzzle\Batch` namespace has been removed. This is best left to
third-parties to implement on top of Guzzle's core HTTP library.

## Cache

The `Guzzle\Cache` namespace has been removed. (Todo: No suitable replacement
has been implemented yet, but hoping to utilize a PSR cache interface).

## Common

- Removed all of the wrapped exceptions. It's better to use the standard PHP
  library for unrecoverable exceptions.
- `FromConfigInterface` has been removed.
- `Guzzle\Common\Version` has been removed. The VERSION constant can be found
  at `GuzzleHttp\ClientInterface::VERSION`.

### Collection

- `getAll` has been removed. Use `toArray` to convert a collection to an array.
- `inject` has been removed.
- `keySearch` has been removed.
- `getPath` no longer supports wildcard expressions. Use something better like
  JMESPath for this.
- `setPath` now supports appending to an existing array via the `[]` notation.

### Events

Guzzle no longer requires Symfony's EventDispatcher component. Guzzle now uses
`GuzzleHttp\Event\Emitter`.

- `Symfony\Component\EventDispatcher\EventDispatcherInterface` is replaced by
  `GuzzleHttp\Event\EmitterInterface`.
- `Symfony\Component\EventDispatcher\EventDispatcher` is replaced by
  `GuzzleHttp\Event\Emitter`.
- `Symfony\Component\EventDispatcher\Event` is replaced by
  `GuzzleHttp\Event\Event`, and Guzzle now has an EventInterface in
  `GuzzleHttp\Event\EventInterface`.
- `AbstractHasDispatcher` has moved to a trait, `HasEmitterTrait`, and
  `HasDispatcherInterface` has moved to `HasEmitterInterface`. Retrieving the
  event emitter of a request, client, etc. now uses the `getEmitter` method
  rather than the `getDispatcher` method.

#### Emitter

- Use the `once()` method to add a listener that automatically removes itself
  the first time it is invoked.
- Use the `listeners()` method to retrieve a list of event listeners rather than
  the `getListeners()` method.
- Use `emit()` instead of `dispatch()` to emit an event from an emitter.
- Use `attach()` instead of `addSubscriber()` and `detach()` instead of
  `removeSubscriber()`.

```php
$mock = new Mock();
// 3.x
$request->getEventDispatcher()->addSubscriber($mock);
$request->getEventDispatcher()->removeSubscriber($mock);
// 4.x
$request->getEmitter()->attach($mock);
$request->getEmitter()->detach($mock);
```

Use the `on()` method to add a listener rather than the `addListener()` method.

```php
// 3.x
$request->getEventDispatcher()->addListener('foo', function (Event $event) { /* ... */ } );
// 4.x
$request->getEmitter()->on('foo', function (Event $event, $name) { /* ... */ } );
```

## Http

### General changes

- The cacert.pem certificate has been moved to `src/cacert.pem`.
- Added the concept of adapters that are used to transfer requests over the
  wire.
- Simplified the event system.
- Sending requests in parallel is still possible, but batching is no longer a
  concept of the HTTP layer. Instead, you must use the `complete` and `error`
  events to asynchronously manage parallel request transfers.
- `Guzzle\Http\Url` has moved to `GuzzleHttp\Url`.
- `Guzzle\Http\QueryString` has moved to `GuzzleHttp\Query`.
- QueryAggregators have been rewritten so that they are simply callable
  functions.
- `GuzzleHttp\StaticClient` has been removed. Use the functions provided in
  `functions.php` for an easy to use static client instance.
- Exceptions in `GuzzleHttp\Exception` have been updated to all extend from
  `GuzzleHttp\Exception\TransferException`.

### Client

Calling methods like `get()`, `post()`, `head()`, etc. no longer create and
return a request, but rather creates a request, sends the request, and returns
the response.

```php
// 3.0
$request = $client->get('/');
$response = $request->send();

// 4.0
$response = $client->get('/');

// or, to mirror the previous behavior
$request = $client->createRequest('GET', '/');
$response = $client->send($request);
```

`GuzzleHttp\ClientInterface` has changed.

- The `send` method no longer accepts more than one request. Use `sendAll` to
  send multiple requests in parallel.
- `setUserAgent()` has been removed. Use a default request option instead. You
  could, for example, do something like:
  `$client->setConfig('defaults/headers/User-Agent', 'Foo/Bar ' . $client::getDefaultUserAgent())`.
- `setSslVerification()` has been removed. Use default request options instead,
  like `$client->setConfig('defaults/verify', true)`.

`GuzzleHttp\Client` has changed.

- The constructor now accepts only an associative array. You can include a
  `base_url` string or array to use a URI template as the base URL of a client.
  You can also specify a `defaults` key that is an associative array of default
  request options. You can pass an `adapter` to use a custom adapter,
  `batch_adapter` to use a custom adapter for sending requests in parallel, or
  a `message_factory` to change the factory used to create HTTP requests and
  responses.
- The client no longer emits a `client.create_request` event.
- Creating requests with a client no longer automatically utilize a URI
  template. You must pass an array into a creational method (e.g.,
  `createRequest`, `get`, `put`, etc.) in order to expand a URI template.

### Messages

Messages no longer have references to their counterparts (i.e., a request no
longer has a reference to it's response, and a response no loger has a
reference to its request). This association is now managed through a
`GuzzleHttp\Adapter\TransactionInterface` object. You can get references to
these transaction objects using request events that are emitted over the
lifecycle of a request.

#### Requests with a body

- `GuzzleHttp\Message\EntityEnclosingRequest` and
  `GuzzleHttp\Message\EntityEnclosingRequestInterface` have been removed. The
  separation between requests that contain a body and requests that do not
  contain a body has been removed, and now `GuzzleHttp\Message\RequestInterface`
  handles both use cases.
- Any method that previously accepts a `GuzzleHttp\Response` object now accept a
  `GuzzleHttp\Message\ResponseInterface`.
- `GuzzleHttp\Message\RequestFactoryInterface` has been renamed to
  `GuzzleHttp\Message\MessageFactoryInterface`. This interface is used to create
  both requests and responses and is implemented in
  `GuzzleHttp\Message\MessageFactory`.
- POST field and file methods have been removed from the request object. You
  must now use the methods made available to `GuzzleHttp\Post\PostBodyInterface`
  to control the format of a POST body. Requests that are created using a
  standard `GuzzleHttp\Message\MessageFactoryInterface` will automatically use
  a `GuzzleHttp\Post\PostBody` body if the body was passed as an array or if
  the method is POST and no body is provided.

```php
$request = $client->createRequest('POST', '/');
$request->getBody()->setField('foo', 'bar');
$request->getBody()->addFile(new PostFile('file_key', fopen('/path/to/content', 'r')));
```

#### Headers

- `GuzzleHttp\Message\Header` has been removed. Header values are now simply
  represented by an array of values or as a string. Header values are returned
  as a string by default when retrieving a header value from a message. You can
  pass an optional argument of `true` to retrieve a header value as an array
  of strings instead of a single concatenated string.
- `GuzzleHttp\PostFile` and `GuzzleHttp\PostFileInterface` have been moved to
  `GuzzleHttp\Post`. This interface has been simplified and now allows the
  addition of arbitrary headers.
- Custom headers like `GuzzleHttp\Message\Header\Link` have been removed. Most
  of the custom headers are now handled separately in specific
  subscribers/plugins, and `GuzzleHttp\Message\HeaderValues::parseParams()` has
  been updated to properly handle headers that contain parameters (like the
  `Link` header).

#### Responses

- `GuzzleHttp\Message\Response::getInfo()` and
  `GuzzleHttp\Message\Response::setInfo()` have been removed. Use the event
  system to retrieve this type of information.
- `GuzzleHttp\Message\Response::getRawHeaders()` has been removed.
- `GuzzleHttp\Message\Response::getMessage()` has been removed.
- `GuzzleHttp\Message\Response::calculateAge()` and other cache specific
  methods have moved to the CacheSubscriber.
- Header specific helper functions like `getContentMd5()` have been removed.
  Just use `getHeader('Content-MD5')` instead.
- `GuzzleHttp\Message\Response::setRequest()` and
  `GuzzleHttp\Message\Response::getRequest()` have been removed. Use the event
  system to work with request and response objects as a transaction.
- `GuzzleHttp\Message\Response::getRedirectCount()` has been removed. Use the
  Redirect subscriber instead.
- `GuzzleHttp\Message\Response::isSuccessful()` and other related methods have
  been removed. Use `getStatusCode()` instead.

#### Streaming responses

Streaming requests can now be created by a client directly, returning a
`GuzzleHttp\Message\ResponseInterface` object that contains a body stream
referencing an open PHP HTTP stream.

```php
// 3.0
use Guzzle\Stream\PhpStreamRequestFactory;
$request = $client->get('/');
$factory = new PhpStreamRequestFactory();
$stream = $factory->fromRequest($request);
$data = $stream->read(1024);

// 4.0
$response = $client->get('/', ['stream' => true]);
// Read some data off of the stream in the response body
$data = $response->getBody()->read(1024);
```

#### Redirects

The `configureRedirects()` method has been removed in favor of a
`allow_redirects` request option.

```php
// Standard redirects with a default of a max of 5 redirects
$request = $client->createRequest('GET', '/', ['allow_redirects' => true]);

// Strict redirects with a custom number of redirects
$request = $client->createRequest('GET', '/', [
    'allow_redirects' => ['max' => 5, 'strict' => true]
]);
```

#### EntityBody

EntityBody interfaces and classes have been removed or moved to
`GuzzleHttp\Stream`. All classes and interfaces that once required
`GuzzleHttp\EntityBodyInterface` now require
`GuzzleHttp\Stream\StreamInterface`. Creating a new body for a request no
longer uses `GuzzleHttp\EntityBody::factory` but now uses
`GuzzleHttp\Stream\Stream::factory` or even better:
`GuzzleHttp\Stream\create()`.

- `Guzzle\Http\EntityBodyInterface` is now `GuzzleHttp\Stream\StreamInterface`
- `Guzzle\Http\EntityBody` is now `GuzzleHttp\Stream\Stream`
- `Guzzle\Http\CachingEntityBody` is now `GuzzleHttp\Stream\CachingStream`
- `Guzzle\Http\ReadLimitEntityBody` is now `GuzzleHttp\Stream\LimitStream`
- `Guzzle\Http\IoEmittyinEntityBody` has been removed.

#### Request lifecycle events

Requests previously submitted a large number of requests. The number of events
emitted over the lifecycle of a request has been significantly reduced to make
it easier to understand how to extend the behavior of a request. All events
emitted during the lifecycle of a request now emit a custom
`GuzzleHttp\Event\EventInterface` object that contains context providing
methods and a way in which to modify the transaction at that specific point in
time (e.g., intercept the request and set a response on the transaction).

- `request.before_send` has been renamed to `before` and now emits a
  `GuzzleHttp\Event\BeforeEvent`
- `request.complete` has been renamed to `complete` and now emits a
  `GuzzleHttp\Event\CompleteEvent`.
- `request.sent` has been removed. Use `complete`.
- `request.success` has been removed. Use `complete`.
- `error` is now an event that emits a `GuzzleHttp\Event\ErrorEvent`.
- `request.exception` has been removed. Use `error`.
- `request.receive.status_line` has been removed.
- `curl.callback.progress` has been removed. Use a custom `StreamInterface` to
  maintain a status update.
- `curl.callback.write` has been removed. Use a custom `StreamInterface` to
  intercept writes.
- `curl.callback.read` has been removed. Use a custom `StreamInterface` to
  intercept reads.

`headers` is a new event that is emitted after the response headers of a
request have been received before the body of the response is downloaded. This
event emits a `GuzzleHttp\Event\HeadersEvent`.

You can intercept a request and inject a response using the `intercept()` event
of a `GuzzleHttp\Event\BeforeEvent`, `GuzzleHttp\Event\CompleteEvent`, and
`GuzzleHttp\Event\ErrorEvent` event.

See: http://docs.guzzlephp.org/en/latest/events.html

## Inflection

The `Guzzle\Inflection` namespace has been removed. This is not a core concern
of Guzzle.

## Iterator

The `Guzzle\Iterator` namespace has been removed.

- `Guzzle\Iterator\AppendIterator`, `Guzzle\Iterator\ChunkedIterator`, and
  `Guzzle\Iterator\MethodProxyIterator` are nice, but not a core requirement of
  Guzzle itself.
- `Guzzle\Iterator\FilterIterator` is no longer needed because an equivalent
  class is shipped with PHP 5.4.
- `Guzzle\Iterator\MapIterator` is not really needed when using PHP 5.5 because
  it's easier to just wrap an iterator in a generator that maps values.

For a replacement of these iterators, see https://github.com/nikic/iter

## Log

The LogPlugin has moved to https://github.com/guzzle/log-subscriber. The
`Guzzle\Log` namespace has been removed. Guzzle now relies on
`Psr\Log\LoggerInterface` for all logging. The MessageFormatter class has been
moved to `GuzzleHttp\Subscriber\Log\Formatter`.

## Parser

The `Guzzle\Parser` namespace has been removed. This was previously used to
make it possible to plug in custom parsers for cookies, messages, URI
templates, and URLs; however, this level of complexity is not needed in Guzzle
so it has been removed.

- Cookie: Cookie parsing logic has been moved to
  `GuzzleHttp\Cookie\SetCookie::fromString`.
- Message: Message parsing logic for both requests and responses has been moved
  to `GuzzleHttp\Message\MessageFactory::fromMessage`. Message parsing is only
  used in debugging or deserializing messages, so it doesn't make sense for
  Guzzle as a library to add this level of complexity to parsing messages.
- UriTemplate: URI template parsing has been moved to
  `GuzzleHttp\UriTemplate`. The Guzzle library will automatically use the PECL
  URI template library if it is installed.
- Url: URL parsing is now performed in `GuzzleHttp\Url::fromString` (previously
  it was `Guzzle\Http\Url::factory()`). If custom URL parsing is necessary,
  then developers are free to subclass `GuzzleHttp\Url`.

## Plugin

The `Guzzle\Plugin` namespace has been renamed to `GuzzleHttp\Subscriber`.
Several plugins are shipping with the core Guzzle library under this namespace.

- `GuzzleHttp\Subscriber\Cookie`: Replaces the old CookiePlugin. Cookie jar
  code has moved to `GuzzleHttp\Cookie`.
- `GuzzleHttp\Subscriber\History`: Replaces the old HistoryPlugin.
- `GuzzleHttp\Subscriber\HttpError`: Throws errors when a bad HTTP response is
  received.
- `GuzzleHttp\Subscriber\Mock`: Replaces the old MockPlugin.
- `GuzzleHttp\Subscriber\Prepare`: Prepares the body of a request just before
  sending. This subscriber is attached to all requests by default.
- `GuzzleHttp\Subscriber\Redirect`: Replaces the RedirectPlugin.

The following plugins have been removed (third-parties are free to re-implement
these if needed):

- `GuzzleHttp\Plugin\Async` has been removed.
- `GuzzleHttp\Plugin\CurlAuth` has been removed.
- `GuzzleHttp\Plugin\ErrorResponse\ErrorResponsePlugin` has been removed. This
  functionality should instead be implemented with event listeners that occur
  after normal response parsing occurs in the guzzle/command package.

The following plugins are not part of the core Guzzle package, but are provided
in separate repositories:

- `Guzzle\Http\Plugin\BackoffPlugin` has been rewritten to be much simpler
  to build custom retry policies using simple functions rather than various
  chained classes. See: https://github.com/guzzle/retry-subscriber
- `Guzzle\Http\Plugin\Cache\CachePlugin` has moved to
  https://github.com/guzzle/cache-subscriber
- `Guzzle\Http\Plugin\Log\LogPlugin` has moved to
  https://github.com/guzzle/log-subscriber
- `Guzzle\Http\Plugin\Md5\Md5Plugin` has moved to
  https://github.com/guzzle/message-integrity-subscriber
- `Guzzle\Http\Plugin\Mock\MockPlugin` has moved to
  `GuzzleHttp\Subscriber\MockSubscriber`.
- `Guzzle\Http\Plugin\Oauth\OauthPlugin` has moved to
  https://github.com/guzzle/oauth-subscriber

## Service

The service description layer of Guzzle has moved into two separate packages:

- http://github.com/guzzle/command Provides a high level abstraction over web
  services by representing web service operations using commands.
- http://github.com/guzzle/guzzle-services Provides an implementation of
  guzzle/command that provides request serialization and response parsing using
  Guzzle service descriptions.

## Stream

Stream have moved to a separate package available at
https://github.com/guzzle/streams.

`Guzzle\Stream\StreamInterface` has been given a large update to cleanly take
on the responsibilities of `Guzzle\Http\EntityBody` and
`Guzzle\Http\EntityBodyInterface` now that they have been removed. The number
of methods implemented by the `StreamInterface` has been drastically reduced to
allow developers to more easily extend and decorate stream behavior.

## Removed methods from StreamInterface

- `getStream` and `setStream` have been removed to better encapsulate streams.
- `getMetadata` and `setMetadata` have been removed in favor of
  `GuzzleHttp\Stream\MetadataStreamInterface`.
- `getWrapper`, `getWrapperData`, `getStreamType`, and `getUri` have all been
  removed. This data is accessible when
  using streams that implement `GuzzleHttp\Stream\MetadataStreamInterface`.
- `rewind` has been removed. Use `seek(0)` for a similar behavior.

## Renamed methods

- `detachStream` has been renamed to `detach`.
- `feof` has been renamed to `eof`.
- `ftell` has been renamed to `tell`.
- `readLine` has moved from an instance method to a static class method of
  `GuzzleHttp\Stream\Stream`.

## Metadata streams

`GuzzleHttp\Stream\MetadataStreamInterface` has been added to denote streams
that contain additional metadata accessible via `getMetadata()`.
`GuzzleHttp\Stream\StreamInterface::getMetadata` and
`GuzzleHttp\Stream\StreamInterface::setMetadata` have been removed.

## StreamRequestFactory

The entire concept of the StreamRequestFactory has been removed. The way this
was used in Guzzle 3 broke the actual interface of sending streaming requests
(instead of getting back a Response, you got a StreamInterface). Streaming
PHP requests are now implemented through the `GuzzleHttp\Adapter\StreamAdapter`.

3.6 to 3.7
----------

### Deprecations

- You can now enable E_USER_DEPRECATED warnings to see if you are using any deprecated methods.:

```php
\Guzzle\Common\Version::$emitWarnings = true;
```

The following APIs and options have been marked as deprecated:

- Marked `Guzzle\Http\Message\Request::isResponseBodyRepeatable()` as deprecated. Use `$request->getResponseBody()->isRepeatable()` instead.
- Marked `Guzzle\Http\Message\Request::canCache()` as deprecated. Use `Guzzle\Plugin\Cache\DefaultCanCacheStrategy->canCacheRequest()` instead.
- Marked `Guzzle\Http\Message\Request::canCache()` as deprecated. Use `Guzzle\Plugin\Cache\DefaultCanCacheStrategy->canCacheRequest()` instead.
- Marked `Guzzle\Http\Message\Request::setIsRedirect()` as deprecated. Use the HistoryPlugin instead.
- Marked `Guzzle\Http\Message\Request::isRedirect()` as deprecated. Use the HistoryPlugin instead.
- Marked `Guzzle\Cache\CacheAdapterFactory::factory()` as deprecated
- Marked `Guzzle\Service\Client::enableMagicMethods()` as deprecated. Magic methods can no longer be disabled on a Guzzle\Service\Client.
- Marked `Guzzle\Parser\Url\UrlParser` as deprecated. Just use PHP's `parse_url()` and percent encode your UTF-8.
- Marked `Guzzle\Common\Collection::inject()` as deprecated.
- Marked `Guzzle\Plugin\CurlAuth\CurlAuthPlugin` as deprecated. Use
  `$client->getConfig()->setPath('request.options/auth', array('user', 'pass', 'Basic|Digest|NTLM|Any'));` or
  `$client->setDefaultOption('auth', array('user', 'pass', 'Basic|Digest|NTLM|Any'));`

3.7 introduces `request.options` as a parameter for a client configuration and as an optional argument to all creational
request methods. When paired with a client's configuration settings, these options allow you to specify default settings
for various aspects of a request. Because these options make other previous configuration options redundant, several
configuration options and methods of a client and AbstractCommand have been deprecated.

- Marked `Guzzle\Service\Client::getDefaultHeaders()` as deprecated. Use `$client->getDefaultOption('headers')`.
- Marked `Guzzle\Service\Client::setDefaultHeaders()` as deprecated. Use `$client->setDefaultOption('headers/{header_name}', 'value')`.
- Marked 'request.params' for `Guzzle\Http\Client` as deprecated. Use `$client->setDefaultOption('params/{param_name}', 'value')`
- Marked 'command.headers', 'command.response_body' and 'command.on_complete' as deprecated for AbstractCommand. These will work through Guzzle 4.0

        $command = $client->getCommand('foo', array(
            'command.headers' => array('Test' => '123'),
            'command.response_body' => '/path/to/file'
        ));

        // Should be changed to:

        $command = $client->getCommand('foo', array(
            'command.request_options' => array(
                'headers' => array('Test' => '123'),
                'save_as' => '/path/to/file'
            )
        ));

### Interface changes

Additions and changes (you will need to update any implementations or subclasses you may have created):

- Added an `$options` argument to the end of the following methods of `Guzzle\Http\ClientInterface`:
  createRequest, head, delete, put, patch, post, options, prepareRequest
- Added an `$options` argument to the end of `Guzzle\Http\Message\Request\RequestFactoryInterface::createRequest()`
- Added an `applyOptions()` method to `Guzzle\Http\Message\Request\RequestFactoryInterface`
- Changed `Guzzle\Http\ClientInterface::get($uri = null, $headers = null, $body = null)` to
  `Guzzle\Http\ClientInterface::get($uri = null, $headers = null, $options = array())`. You can still pass in a
  resource, string, or EntityBody into the $options parameter to specify the download location of the response.
- Changed `Guzzle\Common\Collection::__construct($data)` to no longer accepts a null value for `$data` but a
  default `array()`
- Added `Guzzle\Stream\StreamInterface::isRepeatable`
- Made `Guzzle\Http\Client::expandTemplate` and `getUriTemplate` protected methods.

The following methods were removed from interfaces. All of these methods are still available in the concrete classes
that implement them, but you should update your code to use alternative methods:

- Removed `Guzzle\Http\ClientInterface::setDefaultHeaders(). Use
  `$client->getConfig()->setPath('request.options/headers/{header_name}', 'value')`. or
  `$client->getConfig()->setPath('request.options/headers', array('header_name' => 'value'))` or
  `$client->setDefaultOption('headers/{header_name}', 'value')`. or
  `$client->setDefaultOption('headers', array('header_name' => 'value'))`.
- Removed `Guzzle\Http\ClientInterface::getDefaultHeaders(). Use `$client->getConfig()->getPath('request.options/headers')`.
- Removed `Guzzle\Http\ClientInterface::expandTemplate()`. This is an implementation detail.
- Removed `Guzzle\Http\ClientInterface::setRequestFactory()`. This is an implementation detail.
- Removed `Guzzle\Http\ClientInterface::getCurlMulti()`. This is a very specific implementation detail.
- Removed `Guzzle\Http\Message\RequestInterface::canCache`. Use the CachePlugin.
- Removed `Guzzle\Http\Message\RequestInterface::setIsRedirect`. Use the HistoryPlugin.
- Removed `Guzzle\Http\Message\RequestInterface::isRedirect`. Use the HistoryPlugin.

### Cache plugin breaking changes

- CacheKeyProviderInterface and DefaultCacheKeyProvider are no longer used. All of this logic is handled in a
  CacheStorageInterface. These two objects and interface will be removed in a future version.
- Always setting X-cache headers on cached responses
- Default cache TTLs are now handled by the CacheStorageInterface of a CachePlugin
- `CacheStorageInterface::cache($key, Response $response, $ttl = null)` has changed to `cache(RequestInterface
  $request, Response $response);`
- `CacheStorageInterface::fetch($key)` has changed to `fetch(RequestInterface $request);`
- `CacheStorageInterface::delete($key)` has changed to `delete(RequestInterface $request);`
- Added `CacheStorageInterface::purge($url)`
- `DefaultRevalidation::__construct(CacheKeyProviderInterface $cacheKey, CacheStorageInterface $cache, CachePlugin
  $plugin)` has changed to `DefaultRevalidation::__construct(CacheStorageInterface $cache,
  CanCacheStrategyInterface $canCache = null)`
- Added `RevalidationInterface::shouldRevalidate(RequestInterface $request, Response $response)`

3.5 to 3.6
----------

* Mixed casing of headers are now forced to be a single consistent casing across all values for that header.
* Messages internally use a HeaderCollection object to delegate handling case-insensitive header resolution
* Removed the whole changedHeader() function system of messages because all header changes now go through addHeader().
  For example, setHeader() first removes the header using unset on a HeaderCollection and then calls addHeader().
  Keeping the Host header and URL host in sync is now handled by overriding the addHeader method in Request.
* Specific header implementations can be created for complex headers. When a message creates a header, it uses a
  HeaderFactory which can map specific headers to specific header classes. There is now a Link header and
  CacheControl header implementation.
* Moved getLinks() from Response to just be used on a Link header object.

If you previously relied on Guzzle\Http\Message\Header::raw(), then you will need to update your code to use the
HeaderInterface (e.g. toArray(), getAll(), etc.).

### Interface changes

* Removed from interface: Guzzle\Http\ClientInterface::setUriTemplate
* Removed from interface: Guzzle\Http\ClientInterface::setCurlMulti()
* Removed Guzzle\Http\Message\Request::receivedRequestHeader() and implemented this functionality in
  Guzzle\Http\Curl\RequestMediator
* Removed the optional $asString parameter from MessageInterface::getHeader(). Just cast the header to a string.
* Removed the optional $tryChunkedTransfer option from Guzzle\Http\Message\EntityEnclosingRequestInterface
* Removed the $asObjects argument from Guzzle\Http\Message\MessageInterface::getHeaders()

### Removed deprecated functions

* Removed Guzzle\Parser\ParserRegister::get(). Use getParser()
* Removed Guzzle\Parser\ParserRegister::set(). Use registerParser().

### Deprecations

* The ability to case-insensitively search for header values
* Guzzle\Http\Message\Header::hasExactHeader
* Guzzle\Http\Message\Header::raw. Use getAll()
* Deprecated cache control specific methods on Guzzle\Http\Message\AbstractMessage. Use the CacheControl header object
  instead.

### Other changes

* All response header helper functions return a string rather than mixing Header objects and strings inconsistently
* Removed cURL blacklist support. This is no longer necessary now that Expect, Accept, etc. are managed by Guzzle
  directly via interfaces
* Removed the injecting of a request object onto a response object. The methods to get and set a request still exist
  but are a no-op until removed.
* Most classes that used to require a `Guzzle\Service\Command\CommandInterface` typehint now request a
  `Guzzle\Service\Command\ArrayCommandInterface`.
* Added `Guzzle\Http\Message\RequestInterface::startResponse()` to the RequestInterface to handle injecting a response
  on a request while the request is still being transferred
* `Guzzle\Service\Command\CommandInterface` now extends from ToArrayInterface and ArrayAccess

3.3 to 3.4
----------

Base URLs of a client now follow the rules of http://tools.ietf.org/html/rfc3986#section-5.2.2 when merging URLs.

3.2 to 3.3
----------

### Response::getEtag() quote stripping removed

`Guzzle\Http\Message\Response::getEtag()` no longer strips quotes around the ETag response header

### Removed `Guzzle\Http\Utils`

The `Guzzle\Http\Utils` class was removed. This class was only used for testing.

### Stream wrapper and type

`Guzzle\Stream\Stream::getWrapper()` and `Guzzle\Stream\Stream::getStreamType()` are no longer converted to lowercase.

### curl.emit_io became emit_io

Emitting IO events from a RequestMediator is now a parameter that must be set in a request's curl options using the
'emit_io' key. This was previously set under a request's parameters using 'curl.emit_io'

3.1 to 3.2
----------

### CurlMulti is no longer reused globally

Before 3.2, the same CurlMulti object was reused globally for each client. This can cause issue where plugins added
to a single client can pollute requests dispatched from other clients.

If you still wish to reuse the same CurlMulti object with each client, then you can add a listener to the
ServiceBuilder's `service_builder.create_client` event to inject a custom CurlMulti object into each client as it is
created.

```php
$multi = new Guzzle\Http\Curl\CurlMulti();
$builder = Guzzle\Service\Builder\ServiceBuilder::factory('/path/to/config.json');
$builder->addListener('service_builder.create_client', function ($event) use ($multi) {
    $event['client']->setCurlMulti($multi);
}
});
```

### No default path

URLs no longer have a default path value of '/' if no path was specified.

Before:

```php
$request = $client->get('http://www.foo.com');
echo $request->getUrl();
// >> http://www.foo.com/
```

After:

```php
$request = $client->get('http://www.foo.com');
echo $request->getUrl();
// >> http://www.foo.com
```

### Less verbose BadResponseException

The exception message for `Guzzle\Http\Exception\BadResponseException` no longer contains the full HTTP request and
response information. You can, however, get access to the request and response object by calling `getRequest()` or
`getResponse()` on the exception object.

### Query parameter aggregation

Multi-valued query parameters are no longer aggregated using a callback function. `Guzzle\Http\Query` now has a
setAggregator() method that accepts a `Guzzle\Http\QueryAggregator\QueryAggregatorInterface` object. This object is
responsible for handling the aggregation of multi-valued query string variables into a flattened hash.

2.8 to 3.x
----------

### Guzzle\Service\Inspector

Change `\Guzzle\Service\Inspector::fromConfig` to `\Guzzle\Common\Collection::fromConfig`

**Before**

```php
use Guzzle\Service\Inspector;

class YourClient extends \Guzzle\Service\Client
{
    public static function factory($config = array())
    {
        $default = array();
        $required = array('base_url', 'username', 'api_key');
        $config = Inspector::fromConfig($config, $default, $required);

        $client = new self(
            $config->get('base_url'),
            $config->get('username'),
            $config->get('api_key')
        );
        $client->setConfig($config);

        $client->setDescription(ServiceDescription::factory(__DIR__ . DIRECTORY_SEPARATOR . 'client.json'));

        return $client;
    }
```

**After**

```php
use Guzzle\Common\Collection;

class YourClient extends \Guzzle\Service\Client
{
    public static function factory($config = array())
    {
        $default = array();
        $required = array('base_url', 'username', 'api_key');
        $config = Collection::fromConfig($config, $default, $required);

        $client = new self(
            $config->get('base_url'),
            $config->get('username'),
            $config->get('api_key')
        );
        $client->setConfig($config);

        $client->setDescription(ServiceDescription::factory(__DIR__ . DIRECTORY_SEPARATOR . 'client.json'));

        return $client;
    }
```

### Convert XML Service Descriptions to JSON

**Before**

```xml
<?xml version="1.0" encoding="UTF-8"?>
<client>
    <commands>
        <!-- Groups -->
        <command name="list_groups" method="GET" uri="groups.json">
            <doc>Get a list of groups</doc>
        </command>
        <command name="search_groups" method="GET" uri='search.json?query="{{query}} type:group"'>
            <doc>Uses a search query to get a list of groups</doc>
            <param name="query" type="string" required="true" />
        </command>
        <command name="create_group" method="POST" uri="groups.json">
            <doc>Create a group</doc>
            <param name="data" type="array" location="body" filters="json_encode" doc="Group JSON"/>
            <param name="Content-Type" location="header" static="application/json"/>
        </command>
        <command name="delete_group" method="DELETE" uri="groups/{{id}}.json">
            <doc>Delete a group by ID</doc>
            <param name="id" type="integer" required="true"/>
        </command>
        <command name="get_group" method="GET" uri="groups/{{id}}.json">
            <param name="id" type="integer" required="true"/>
        </command>
        <command name="update_group" method="PUT" uri="groups/{{id}}.json">
            <doc>Update a group</doc>
            <param name="id" type="integer" required="true"/>
            <param name="data" type="array" location="body" filters="json_encode" doc="Group JSON"/>
            <param name="Content-Type" location="header" static="application/json"/>
        </command>
    </commands>
</client>
```

**After**

```json
{
    "name":       "Zendesk REST API v2",
    "apiVersion": "2012-12-31",
    "description":"Provides access to Zendesk views, groups, tickets, ticket fields, and users",
    "operations": {
        "list_groups":  {
            "httpMethod":"GET",
            "uri":       "groups.json",
            "summary":   "Get a list of groups"
        },
        "search_groups":{
            "httpMethod":"GET",
            "uri":       "search.json?query=\"{query} type:group\"",
            "summary":   "Uses a search query to get a list of groups",
            "parameters":{
                "query":{
                    "location":   "uri",
                    "description":"Zendesk Search Query",
                    "type":       "string",
                    "required":   true
                }
            }
        },
        "create_group": {
            "httpMethod":"POST",
            "uri":       "groups.json",
            "summary":   "Create a group",
            "parameters":{
                "data":        {
                    "type":       "array",
                    "location":   "body",
                    "description":"Group JSON",
                    "filters":    "json_encode",
                    "required":   true
                },
                "Content-Type":{
                    "type":    "string",
                    "location":"header",
                    "static":  "application/json"
                }
            }
        },
        "delete_group": {
            "httpMethod":"DELETE",
            "uri":       "groups/{id}.json",
            "summary":   "Delete a group",
            "parameters":{
                "id":{
                    "location":   "uri",
                    "description":"Group to delete by ID",
                    "type":       "integer",
                    "required":   true
                }
            }
        },
        "get_group":    {
            "httpMethod":"GET",
            "uri":       "groups/{id}.json",
            "summary":   "Get a ticket",
            "parameters":{
                "id":{
                    "location":   "uri",
                    "description":"Group to get by ID",
                    "type":       "integer",
                    "required":   true
                }
            }
        },
        "update_group": {
            "httpMethod":"PUT",
            "uri":       "groups/{id}.json",
            "summary":   "Update a group",
            "parameters":{
                "id":          {
                    "location":   "uri",
                    "description":"Group to update by ID",
                    "type":       "integer",
                    "required":   true
                },
                "data":        {
                    "type":       "array",
                    "location":   "body",
                    "description":"Group JSON",
                    "filters":    "json_encode",
                    "required":   true
                },
                "Content-Type":{
                    "type":    "string",
                    "location":"header",
                    "static":  "application/json"
                }
            }
        }
}
```

### Guzzle\Service\Description\ServiceDescription

Commands are now called Operations

**Before**

```php
use Guzzle\Service\Description\ServiceDescription;

$sd = new ServiceDescription();
$sd->getCommands();     // @returns ApiCommandInterface[]
$sd->hasCommand($name);
$sd->getCommand($name); // @returns ApiCommandInterface|null
$sd->addCommand($command); // @param ApiCommandInterface $command
```

**After**

```php
use Guzzle\Service\Description\ServiceDescription;

$sd = new ServiceDescription();
$sd->getOperations();           // @returns OperationInterface[]
$sd->hasOperation($name);
$sd->getOperation($name);       // @returns OperationInterface|null
$sd->addOperation($operation);  // @param OperationInterface $operation
```

### Guzzle\Common\Inflection\Inflector

Namespace is now `Guzzle\Inflection\Inflector`

### Guzzle\Http\Plugin

Namespace is now `Guzzle\Plugin`. Many other changes occur within this namespace and are detailed in their own sections below.

### Guzzle\Http\Plugin\LogPlugin and Guzzle\Common\Log

Now `Guzzle\Plugin\Log\LogPlugin` and `Guzzle\Log` respectively.

**Before**

```php
use Guzzle\Common\Log\ClosureLogAdapter;
use Guzzle\Http\Plugin\LogPlugin;

/** @var \Guzzle\Http\Client */
$client;

// $verbosity is an integer indicating desired message verbosity level
$client->addSubscriber(new LogPlugin(new ClosureLogAdapter(function($m) { echo $m; }, $verbosity = LogPlugin::LOG_VERBOSE);
```

**After**

```php
use Guzzle\Log\ClosureLogAdapter;
use Guzzle\Log\MessageFormatter;
use Guzzle\Plugin\Log\LogPlugin;

/** @var \Guzzle\Http\Client */
$client;

// $format is a string indicating desired message format -- @see MessageFormatter
$client->addSubscriber(new LogPlugin(new ClosureLogAdapter(function($m) { echo $m; }, $format = MessageFormatter::DEBUG_FORMAT);
```

### Guzzle\Http\Plugin\CurlAuthPlugin

Now `Guzzle\Plugin\CurlAuth\CurlAuthPlugin`.

### Guzzle\Http\Plugin\ExponentialBackoffPlugin

Now `Guzzle\Plugin\Backoff\BackoffPlugin`, and other changes.

**Before**

```php
use Guzzle\Http\Plugin\ExponentialBackoffPlugin;

$backoffPlugin = new ExponentialBackoffPlugin($maxRetries, array_merge(
        ExponentialBackoffPlugin::getDefaultFailureCodes(), array(429)
    ));

$client->addSubscriber($backoffPlugin);
```

**After**

```php
use Guzzle\Plugin\Backoff\BackoffPlugin;
use Guzzle\Plugin\Backoff\HttpBackoffStrategy;

// Use convenient factory method instead -- see implementation for ideas of what
// you can do with chaining backoff strategies
$backoffPlugin = BackoffPlugin::getExponentialBackoff($maxRetries, array_merge(
        HttpBackoffStrategy::getDefaultFailureCodes(), array(429)
    ));
$client->addSubscriber($backoffPlugin);
```

### Known Issues

#### [BUG] Accept-Encoding header behavior changed unintentionally.

(See #217) (Fixed in 09daeb8c666fb44499a0646d655a8ae36456575e)

In version 2.8 setting the `Accept-Encoding` header would set the CURLOPT_ENCODING option, which permitted cURL to
properly handle gzip/deflate compressed responses from the server. In versions affected by this bug this does not happen.
See issue #217 for a workaround, or use a version containing the fix.
vendor/guzzlehttp/guzzle/LICENSE000064400000002134151327705700012615 0ustar00Copyright (c) 2011-2018 Michael Dowling, https://github.com/mtdowling <mtdowling@gmail.com>

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
vendor/guzzlehttp/guzzle/CHANGELOG.md000064400000223642151327705700013432 0ustar00# Change Log

## 6.3.3 - 2018-04-22

* Fix: Default headers when decode_content is specified


## 6.3.2 - 2018-03-26

* Fix: Release process


## 6.3.1 - 2018-03-26

* Bug fix: Parsing 0 epoch expiry times in cookies [#2014](https://github.com/guzzle/guzzle/pull/2014)
* Improvement: Better ConnectException detection [#2012](https://github.com/guzzle/guzzle/pull/2012)
* Bug fix: Malformed domain that contains a "/" [#1999](https://github.com/guzzle/guzzle/pull/1999)
* Bug fix: Undefined offset when a cookie has no first key-value pair [#1998](https://github.com/guzzle/guzzle/pull/1998)
* Improvement: Support PHPUnit 6 [#1953](https://github.com/guzzle/guzzle/pull/1953)
* Bug fix: Support empty headers [#1915](https://github.com/guzzle/guzzle/pull/1915)
* Bug fix: Ignore case during header modifications [#1916](https://github.com/guzzle/guzzle/pull/1916)

+ Minor code cleanups, documentation fixes and clarifications.


## 6.3.0 - 2017-06-22

* Feature: force IP resolution (ipv4 or ipv6) [#1608](https://github.com/guzzle/guzzle/pull/1608), [#1659](https://github.com/guzzle/guzzle/pull/1659)
* Improvement: Don't include summary in exception message when body is empty [#1621](https://github.com/guzzle/guzzle/pull/1621)
* Improvement: Handle `on_headers` option in MockHandler [#1580](https://github.com/guzzle/guzzle/pull/1580)
* Improvement: Added SUSE Linux CA path [#1609](https://github.com/guzzle/guzzle/issues/1609)
* Improvement: Use class reference for getting the name of the class instead of using hardcoded strings [#1641](https://github.com/guzzle/guzzle/pull/1641)
* Feature: Added `read_timeout` option [#1611](https://github.com/guzzle/guzzle/pull/1611)
* Bug fix: PHP 7.x fixes [#1685](https://github.com/guzzle/guzzle/pull/1685), [#1686](https://github.com/guzzle/guzzle/pull/1686), [#1811](https://github.com/guzzle/guzzle/pull/1811)
* Deprecation: BadResponseException instantiation without a response [#1642](https://github.com/guzzle/guzzle/pull/1642)
* Feature: Added NTLM auth [#1569](https://github.com/guzzle/guzzle/pull/1569)
* Feature: Track redirect HTTP status codes [#1711](https://github.com/guzzle/guzzle/pull/1711)
* Improvement: Check handler type during construction [#1745](https://github.com/guzzle/guzzle/pull/1745)
* Improvement: Always include the Content-Length if there's a body [#1721](https://github.com/guzzle/guzzle/pull/1721)
* Feature: Added convenience method to access a cookie by name [#1318](https://github.com/guzzle/guzzle/pull/1318)
* Bug fix: Fill `CURLOPT_CAPATH` and `CURLOPT_CAINFO` properly [#1684](https://github.com/guzzle/guzzle/pull/1684)
* Improvement:  	Use `\GuzzleHttp\Promise\rejection_for` function instead of object init [#1827](https://github.com/guzzle/guzzle/pull/1827)


+ Minor code cleanups, documentation fixes and clarifications.

## 6.2.3 - 2017-02-28

* Fix deprecations with guzzle/psr7 version 1.4

## 6.2.2 - 2016-10-08

* Allow to pass nullable Response to delay callable
* Only add scheme when host is present
* Fix drain case where content-length is the literal string zero
* Obfuscate in-URL credentials in exceptions

## 6.2.1 - 2016-07-18

* Address HTTP_PROXY security vulnerability, CVE-2016-5385:
  https://httpoxy.org/
* Fixing timeout bug with StreamHandler:
  https://github.com/guzzle/guzzle/pull/1488
* Only read up to `Content-Length` in PHP StreamHandler to avoid timeouts when
  a server does not honor `Connection: close`.
* Ignore URI fragment when sending requests.

## 6.2.0 - 2016-03-21

* Feature: added `GuzzleHttp\json_encode` and `GuzzleHttp\json_decode`.
  https://github.com/guzzle/guzzle/pull/1389
* Bug fix: Fix sleep calculation when waiting for delayed requests.
  https://github.com/guzzle/guzzle/pull/1324
* Feature: More flexible history containers.
  https://github.com/guzzle/guzzle/pull/1373
* Bug fix: defer sink stream opening in StreamHandler.
  https://github.com/guzzle/guzzle/pull/1377
* Bug fix: do not attempt to escape cookie values.
  https://github.com/guzzle/guzzle/pull/1406
* Feature: report original content encoding and length on decoded responses.
  https://github.com/guzzle/guzzle/pull/1409
* Bug fix: rewind seekable request bodies before dispatching to cURL.
  https://github.com/guzzle/guzzle/pull/1422
* Bug fix: provide an empty string to `http_build_query` for HHVM workaround.
  https://github.com/guzzle/guzzle/pull/1367

## 6.1.1 - 2015-11-22

* Bug fix: Proxy::wrapSync() now correctly proxies to the appropriate handler
  https://github.com/guzzle/guzzle/commit/911bcbc8b434adce64e223a6d1d14e9a8f63e4e4
* Feature: HandlerStack is now more generic.
  https://github.com/guzzle/guzzle/commit/f2102941331cda544745eedd97fc8fd46e1ee33e
* Bug fix: setting verify to false in the StreamHandler now disables peer
  verification. https://github.com/guzzle/guzzle/issues/1256
* Feature: Middleware now uses an exception factory, including more error
  context. https://github.com/guzzle/guzzle/pull/1282
* Feature: better support for disabled functions.
  https://github.com/guzzle/guzzle/pull/1287
* Bug fix: fixed regression where MockHandler was not using `sink`.
  https://github.com/guzzle/guzzle/pull/1292

## 6.1.0 - 2015-09-08

* Feature: Added the `on_stats` request option to provide access to transfer
  statistics for requests. https://github.com/guzzle/guzzle/pull/1202
* Feature: Added the ability to persist session cookies in CookieJars.
  https://github.com/guzzle/guzzle/pull/1195
* Feature: Some compatibility updates for Google APP Engine
  https://github.com/guzzle/guzzle/pull/1216
* Feature: Added support for NO_PROXY to prevent the use of a proxy based on
  a simple set of rules. https://github.com/guzzle/guzzle/pull/1197
* Feature: Cookies can now contain square brackets.
  https://github.com/guzzle/guzzle/pull/1237
* Bug fix: Now correctly parsing `=` inside of quotes in Cookies.
  https://github.com/guzzle/guzzle/pull/1232
* Bug fix: Cusotm cURL options now correctly override curl options of the
  same name. https://github.com/guzzle/guzzle/pull/1221
* Bug fix: Content-Type header is now added when using an explicitly provided
  multipart body. https://github.com/guzzle/guzzle/pull/1218
* Bug fix: Now ignoring Set-Cookie headers that have no name.
* Bug fix: Reason phrase is no longer cast to an int in some cases in the
  cURL handler. https://github.com/guzzle/guzzle/pull/1187
* Bug fix: Remove the Authorization header when redirecting if the Host
  header changes. https://github.com/guzzle/guzzle/pull/1207
* Bug fix: Cookie path matching fixes
  https://github.com/guzzle/guzzle/issues/1129
* Bug fix: Fixing the cURL `body_as_string` setting
  https://github.com/guzzle/guzzle/pull/1201
* Bug fix: quotes are no longer stripped when parsing cookies.
  https://github.com/guzzle/guzzle/issues/1172
* Bug fix: `form_params` and `query` now always uses the `&` separator.
  https://github.com/guzzle/guzzle/pull/1163
* Bug fix: Adding a Content-Length to PHP stream wrapper requests if not set.
  https://github.com/guzzle/guzzle/pull/1189

## 6.0.2 - 2015-07-04

* Fixed a memory leak in the curl handlers in which references to callbacks
  were not being removed by `curl_reset`.
* Cookies are now extracted properly before redirects.
* Cookies now allow more character ranges.
* Decoded Content-Encoding responses are now modified to correctly reflect
  their state if the encoding was automatically removed by a handler. This
  means that the `Content-Encoding` header may be removed an the
  `Content-Length` modified to reflect the message size after removing the
  encoding.
* Added a more explicit error message when trying to use `form_params` and
  `multipart` in the same request.
* Several fixes for HHVM support.
* Functions are now conditionally required using an additional level of
  indirection to help with global Composer installations.

## 6.0.1 - 2015-05-27

* Fixed a bug with serializing the `query` request option where the `&`
  separator was missing.
* Added a better error message for when `body` is provided as an array. Please
  use `form_params` or `multipart` instead.
* Various doc fixes.

## 6.0.0 - 2015-05-26

* See the UPGRADING.md document for more information.
* Added `multipart` and `form_params` request options.
* Added `synchronous` request option.
* Added the `on_headers` request option.
* Fixed `expect` handling.
* No longer adding default middlewares in the client ctor. These need to be
  present on the provided handler in order to work.
* Requests are no longer initiated when sending async requests with the
  CurlMultiHandler. This prevents unexpected recursion from requests completing
  while ticking the cURL loop.
* Removed the semantics of setting `default` to `true`. This is no longer
  required now that the cURL loop is not ticked for async requests.
* Added request and response logging middleware.
* No longer allowing self signed certificates when using the StreamHandler.
* Ensuring that `sink` is valid if saving to a file.
* Request exceptions now include a "handler context" which provides handler
  specific contextual information.
* Added `GuzzleHttp\RequestOptions` to allow request options to be applied
  using constants.
* `$maxHandles` has been removed from CurlMultiHandler.
* `MultipartPostBody` is now part of the `guzzlehttp/psr7` package.

## 5.3.0 - 2015-05-19

* Mock now supports `save_to`
* Marked `AbstractRequestEvent::getTransaction()` as public.
* Fixed a bug in which multiple headers using different casing would overwrite
  previous headers in the associative array.
* Added `Utils::getDefaultHandler()`
* Marked `GuzzleHttp\Client::getDefaultUserAgent` as deprecated.
* URL scheme is now always lowercased.

## 6.0.0-beta.1

* Requires PHP >= 5.5
* Updated to use PSR-7
  * Requires immutable messages, which basically means an event based system
    owned by a request instance is no longer possible.
  * Utilizing the [Guzzle PSR-7 package](https://github.com/guzzle/psr7).
  * Removed the dependency on `guzzlehttp/streams`. These stream abstractions
    are available in the `guzzlehttp/psr7` package under the `GuzzleHttp\Psr7`
    namespace.
* Added middleware and handler system
  * Replaced the Guzzle event and subscriber system with a middleware system.
  * No longer depends on RingPHP, but rather places the HTTP handlers directly
    in Guzzle, operating on PSR-7 messages.
  * Retry logic is now encapsulated in `GuzzleHttp\Middleware::retry`, which
    means the `guzzlehttp/retry-subscriber` is now obsolete.
  * Mocking responses is now handled using `GuzzleHttp\Handler\MockHandler`.
* Asynchronous responses
  * No longer supports the `future` request option to send an async request.
    Instead, use one of the `*Async` methods of a client (e.g., `requestAsync`,
    `getAsync`, etc.).
  * Utilizing `GuzzleHttp\Promise` instead of React's promise library to avoid
    recursion required by chaining and forwarding react promises. See
    https://github.com/guzzle/promises
  * Added `requestAsync` and `sendAsync` to send request asynchronously.
  * Added magic methods for `getAsync()`, `postAsync()`, etc. to send requests
    asynchronously.
* Request options
  * POST and form updates
    * Added the `form_fields` and `form_files` request options.
    * Removed the `GuzzleHttp\Post` namespace.
    * The `body` request option no longer accepts an array for POST requests.
  * The `exceptions` request option has been deprecated in favor of the
    `http_errors` request options.
  * The `save_to` request option has been deprecated in favor of `sink` request
    option.
* Clients no longer accept an array of URI template string and variables for
  URI variables. You will need to expand URI templates before passing them
  into a client constructor or request method.
* Client methods `get()`, `post()`, `put()`, `patch()`, `options()`, etc. are
  now magic methods that will send synchronous requests.
* Replaced `Utils.php` with plain functions in `functions.php`.
* Removed `GuzzleHttp\Collection`.
* Removed `GuzzleHttp\BatchResults`. Batched pool results are now returned as
  an array.
* Removed `GuzzleHttp\Query`. Query string handling is now handled using an
  associative array passed into the `query` request option. The query string
  is serialized using PHP's `http_build_query`. If you need more control, you
  can pass the query string in as a string.
* `GuzzleHttp\QueryParser` has been replaced with the
  `GuzzleHttp\Psr7\parse_query`.

## 5.2.0 - 2015-01-27

* Added `AppliesHeadersInterface` to make applying headers to a request based
  on the body more generic and not specific to `PostBodyInterface`.
* Reduced the number of stack frames needed to send requests.
* Nested futures are now resolved in the client rather than the RequestFsm
* Finishing state transitions is now handled in the RequestFsm rather than the
  RingBridge.
* Added a guard in the Pool class to not use recursion for request retries.

## 5.1.0 - 2014-12-19

* Pool class no longer uses recursion when a request is intercepted.
* The size of a Pool can now be dynamically adjusted using a callback.
  See https://github.com/guzzle/guzzle/pull/943.
* Setting a request option to `null` when creating a request with a client will
  ensure that the option is not set. This allows you to overwrite default
  request options on a per-request basis.
  See https://github.com/guzzle/guzzle/pull/937.
* Added the ability to limit which protocols are allowed for redirects by
  specifying a `protocols` array in the `allow_redirects` request option.
* Nested futures due to retries are now resolved when waiting for synchronous
  responses. See https://github.com/guzzle/guzzle/pull/947.
* `"0"` is now an allowed URI path. See
  https://github.com/guzzle/guzzle/pull/935.
* `Query` no longer typehints on the `$query` argument in the constructor,
  allowing for strings and arrays.
* Exceptions thrown in the `end` event are now correctly wrapped with Guzzle
  specific exceptions if necessary.

## 5.0.3 - 2014-11-03

This change updates query strings so that they are treated as un-encoded values
by default where the value represents an un-encoded value to send over the
wire. A Query object then encodes the value before sending over the wire. This
means that even value query string values (e.g., ":") are url encoded. This
makes the Query class match PHP's http_build_query function. However, if you
want to send requests over the wire using valid query string characters that do
not need to be encoded, then you can provide a string to Url::setQuery() and
pass true as the second argument to specify that the query string is a raw
string that should not be parsed or encoded (unless a call to getQuery() is
subsequently made, forcing the query-string to be converted into a Query
object).

## 5.0.2 - 2014-10-30

* Added a trailing `\r\n` to multipart/form-data payloads. See
  https://github.com/guzzle/guzzle/pull/871
* Added a `GuzzleHttp\Pool::send()` convenience method to match the docs.
* Status codes are now returned as integers. See
  https://github.com/guzzle/guzzle/issues/881
* No longer overwriting an existing `application/x-www-form-urlencoded` header
  when sending POST requests, allowing for customized headers. See
  https://github.com/guzzle/guzzle/issues/877
* Improved path URL serialization.

  * No longer double percent-encoding characters in the path or query string if
    they are already encoded.
  * Now properly encoding the supplied path to a URL object, instead of only
    encoding ' ' and '?'.
  * Note: This has been changed in 5.0.3 to now encode query string values by
    default unless the `rawString` argument is provided when setting the query
    string on a URL: Now allowing many more characters to be present in the
    query string without being percent encoded. See http://tools.ietf.org/html/rfc3986#appendix-A

## 5.0.1 - 2014-10-16

Bugfix release.

* Fixed an issue where connection errors still returned response object in
  error and end events event though the response is unusable. This has been
  corrected so that a response is not returned in the `getResponse` method of
  these events if the response did not complete. https://github.com/guzzle/guzzle/issues/867
* Fixed an issue where transfer statistics were not being populated in the
  RingBridge. https://github.com/guzzle/guzzle/issues/866

## 5.0.0 - 2014-10-12

Adding support for non-blocking responses and some minor API cleanup.

### New Features

* Added support for non-blocking responses based on `guzzlehttp/guzzle-ring`.
* Added a public API for creating a default HTTP adapter.
* Updated the redirect plugin to be non-blocking so that redirects are sent
  concurrently. Other plugins like this can now be updated to be non-blocking.
* Added a "progress" event so that you can get upload and download progress
  events.
* Added `GuzzleHttp\Pool` which implements FutureInterface and transfers
  requests concurrently using a capped pool size as efficiently as possible.
* Added `hasListeners()` to EmitterInterface.
* Removed `GuzzleHttp\ClientInterface::sendAll` and marked
  `GuzzleHttp\Client::sendAll` as deprecated (it's still there, just not the
  recommended way).

### Breaking changes

The breaking changes in this release are relatively minor. The biggest thing to
look out for is that request and response objects no longer implement fluent
interfaces.

* Removed the fluent interfaces (i.e., `return $this`) from requests,
  responses, `GuzzleHttp\Collection`, `GuzzleHttp\Url`,
  `GuzzleHttp\Query`, `GuzzleHttp\Post\PostBody`, and
  `GuzzleHttp\Cookie\SetCookie`. This blog post provides a good outline of
  why I did this: http://ocramius.github.io/blog/fluent-interfaces-are-evil/.
  This also makes the Guzzle message interfaces compatible with the current
  PSR-7 message proposal.
* Removed "functions.php", so that Guzzle is truly PSR-4 compliant. Except
  for the HTTP request functions from function.php, these functions are now
  implemented in `GuzzleHttp\Utils` using camelCase. `GuzzleHttp\json_decode`
  moved to `GuzzleHttp\Utils::jsonDecode`. `GuzzleHttp\get_path` moved to
  `GuzzleHttp\Utils::getPath`. `GuzzleHttp\set_path` moved to
  `GuzzleHttp\Utils::setPath`. `GuzzleHttp\batch` should now be
  `GuzzleHttp\Pool::batch`, which returns an `objectStorage`. Using functions.php
  caused problems for many users: they aren't PSR-4 compliant, require an
  explicit include, and needed an if-guard to ensure that the functions are not
  declared multiple times.
* Rewrote adapter layer.
    * Removing all classes from `GuzzleHttp\Adapter`, these are now
      implemented as callables that are stored in `GuzzleHttp\Ring\Client`.
    * Removed the concept of "parallel adapters". Sending requests serially or
      concurrently is now handled using a single adapter.
    * Moved `GuzzleHttp\Adapter\Transaction` to `GuzzleHttp\Transaction`. The
      Transaction object now exposes the request, response, and client as public
      properties. The getters and setters have been removed.
* Removed the "headers" event. This event was only useful for changing the
  body a response once the headers of the response were known. You can implement
  a similar behavior in a number of ways. One example might be to use a
  FnStream that has access to the transaction being sent. For example, when the
  first byte is written, you could check if the response headers match your
  expectations, and if so, change the actual stream body that is being
  written to.
* Removed the `asArray` parameter from
  `GuzzleHttp\Message\MessageInterface::getHeader`. If you want to get a header
  value as an array, then use the newly added `getHeaderAsArray()` method of
  `MessageInterface`. This change makes the Guzzle interfaces compatible with
  the PSR-7 interfaces.
* `GuzzleHttp\Message\MessageFactory` no longer allows subclasses to add
  custom request options using double-dispatch (this was an implementation
  detail). Instead, you should now provide an associative array to the
  constructor which is a mapping of the request option name mapping to a
  function that applies the option value to a request.
* Removed the concept of "throwImmediately" from exceptions and error events.
  This control mechanism was used to stop a transfer of concurrent requests
  from completing. This can now be handled by throwing the exception or by
  cancelling a pool of requests or each outstanding future request individually.
* Updated to "GuzzleHttp\Streams" 3.0.
    * `GuzzleHttp\Stream\StreamInterface::getContents()` no longer accepts a
      `maxLen` parameter. This update makes the Guzzle streams project
      compatible with the current PSR-7 proposal.
    * `GuzzleHttp\Stream\Stream::__construct`,
      `GuzzleHttp\Stream\Stream::factory`, and
      `GuzzleHttp\Stream\Utils::create` no longer accept a size in the second
      argument. They now accept an associative array of options, including the
      "size" key and "metadata" key which can be used to provide custom metadata.

## 4.2.2 - 2014-09-08

* Fixed a memory leak in the CurlAdapter when reusing cURL handles.
* No longer using `request_fulluri` in stream adapter proxies.
* Relative redirects are now based on the last response, not the first response.

## 4.2.1 - 2014-08-19

* Ensuring that the StreamAdapter does not always add a Content-Type header
* Adding automated github releases with a phar and zip

## 4.2.0 - 2014-08-17

* Now merging in default options using a case-insensitive comparison.
  Closes https://github.com/guzzle/guzzle/issues/767
* Added the ability to automatically decode `Content-Encoding` response bodies
  using the `decode_content` request option. This is set to `true` by default
  to decode the response body if it comes over the wire with a
  `Content-Encoding`. Set this value to `false` to disable decoding the
  response content, and pass a string to provide a request `Accept-Encoding`
  header and turn on automatic response decoding. This feature now allows you
  to pass an `Accept-Encoding` header in the headers of a request but still
  disable automatic response decoding.
  Closes https://github.com/guzzle/guzzle/issues/764
* Added the ability to throw an exception immediately when transferring
  requests in parallel. Closes https://github.com/guzzle/guzzle/issues/760
* Updating guzzlehttp/streams dependency to ~2.1
* No longer utilizing the now deprecated namespaced methods from the stream
  package.

## 4.1.8 - 2014-08-14

* Fixed an issue in the CurlFactory that caused setting the `stream=false`
  request option to throw an exception.
  See: https://github.com/guzzle/guzzle/issues/769
* TransactionIterator now calls rewind on the inner iterator.
  See: https://github.com/guzzle/guzzle/pull/765
* You can now set the `Content-Type` header to `multipart/form-data`
  when creating POST requests to force multipart bodies.
  See https://github.com/guzzle/guzzle/issues/768

## 4.1.7 - 2014-08-07

* Fixed an error in the HistoryPlugin that caused the same request and response
  to be logged multiple times when an HTTP protocol error occurs.
* Ensuring that cURL does not add a default Content-Type when no Content-Type
  has been supplied by the user. This prevents the adapter layer from modifying
  the request that is sent over the wire after any listeners may have already
  put the request in a desired state (e.g., signed the request).
* Throwing an exception when you attempt to send requests that have the
  "stream" set to true in parallel using the MultiAdapter.
* Only calling curl_multi_select when there are active cURL handles. This was
  previously changed and caused performance problems on some systems due to PHP
  always selecting until the maximum select timeout.
* Fixed a bug where multipart/form-data POST fields were not correctly
  aggregated (e.g., values with "&").

## 4.1.6 - 2014-08-03

* Added helper methods to make it easier to represent messages as strings,
  including getting the start line and getting headers as a string.

## 4.1.5 - 2014-08-02

* Automatically retrying cURL "Connection died, retrying a fresh connect"
  errors when possible.
* cURL implementation cleanup
* Allowing multiple event subscriber listeners to be registered per event by
  passing an array of arrays of listener configuration.

## 4.1.4 - 2014-07-22

* Fixed a bug that caused multi-part POST requests with more than one field to
  serialize incorrectly.
* Paths can now be set to "0"
* `ResponseInterface::xml` now accepts a `libxml_options` option and added a
  missing default argument that was required when parsing XML response bodies.
* A `save_to` stream is now created lazily, which means that files are not
  created on disk unless a request succeeds.

## 4.1.3 - 2014-07-15

* Various fixes to multipart/form-data POST uploads
* Wrapping function.php in an if-statement to ensure Guzzle can be used
  globally and in a Composer install
* Fixed an issue with generating and merging in events to an event array
* POST headers are only applied before sending a request to allow you to change
  the query aggregator used before uploading
* Added much more robust query string parsing
* Fixed various parsing and normalization issues with URLs
* Fixing an issue where multi-valued headers were not being utilized correctly
  in the StreamAdapter

## 4.1.2 - 2014-06-18

* Added support for sending payloads with GET requests

## 4.1.1 - 2014-06-08

* Fixed an issue related to using custom message factory options in subclasses
* Fixed an issue with nested form fields in a multi-part POST
* Fixed an issue with using the `json` request option for POST requests
* Added `ToArrayInterface` to `GuzzleHttp\Cookie\CookieJar`

## 4.1.0 - 2014-05-27

* Added a `json` request option to easily serialize JSON payloads.
* Added a `GuzzleHttp\json_decode()` wrapper to safely parse JSON.
* Added `setPort()` and `getPort()` to `GuzzleHttp\Message\RequestInterface`.
* Added the ability to provide an emitter to a client in the client constructor.
* Added the ability to persist a cookie session using $_SESSION.
* Added a trait that can be used to add event listeners to an iterator.
* Removed request method constants from RequestInterface.
* Fixed warning when invalid request start-lines are received.
* Updated MessageFactory to work with custom request option methods.
* Updated cacert bundle to latest build.

4.0.2 (2014-04-16)
------------------

* Proxy requests using the StreamAdapter now properly use request_fulluri (#632)
* Added the ability to set scalars as POST fields (#628)

## 4.0.1 - 2014-04-04

* The HTTP status code of a response is now set as the exception code of
  RequestException objects.
* 303 redirects will now correctly switch from POST to GET requests.
* The default parallel adapter of a client now correctly uses the MultiAdapter.
* HasDataTrait now initializes the internal data array as an empty array so
  that the toArray() method always returns an array.

## 4.0.0 - 2014-03-29

* For more information on the 4.0 transition, see:
  http://mtdowling.com/blog/2014/03/15/guzzle-4-rc/
* For information on changes and upgrading, see:
  https://github.com/guzzle/guzzle/blob/master/UPGRADING.md#3x-to-40
* Added `GuzzleHttp\batch()` as a convenience function for sending requests in
  parallel without needing to write asynchronous code.
* Restructured how events are added to `GuzzleHttp\ClientInterface::sendAll()`.
  You can now pass a callable or an array of associative arrays where each
  associative array contains the "fn", "priority", and "once" keys.

## 4.0.0.rc-2 - 2014-03-25

* Removed `getConfig()` and `setConfig()` from clients to avoid confusion
  around whether things like base_url, message_factory, etc. should be able to
  be retrieved or modified.
* Added `getDefaultOption()` and `setDefaultOption()` to ClientInterface
* functions.php functions were renamed using snake_case to match PHP idioms
* Added support for `HTTP_PROXY`, `HTTPS_PROXY`, and
  `GUZZLE_CURL_SELECT_TIMEOUT` environment variables
* Added the ability to specify custom `sendAll()` event priorities
* Added the ability to specify custom stream context options to the stream
  adapter.
* Added a functions.php function for `get_path()` and `set_path()`
* CurlAdapter and MultiAdapter now use a callable to generate curl resources
* MockAdapter now properly reads a body and emits a `headers` event
* Updated Url class to check if a scheme and host are set before adding ":"
  and "//". This allows empty Url (e.g., "") to be serialized as "".
* Parsing invalid XML no longer emits warnings
* Curl classes now properly throw AdapterExceptions
* Various performance optimizations
* Streams are created with the faster `Stream\create()` function
* Marked deprecation_proxy() as internal
* Test server is now a collection of static methods on a class

## 4.0.0-rc.1 - 2014-03-15

* See https://github.com/guzzle/guzzle/blob/master/UPGRADING.md#3x-to-40

## 3.8.1 - 2014-01-28

* Bug: Always using GET requests when redirecting from a 303 response
* Bug: CURLOPT_SSL_VERIFYHOST is now correctly set to false when setting `$certificateAuthority` to false in
  `Guzzle\Http\ClientInterface::setSslVerification()`
* Bug: RedirectPlugin now uses strict RFC 3986 compliance when combining a base URL with a relative URL
* Bug: The body of a request can now be set to `"0"`
* Sending PHP stream requests no longer forces `HTTP/1.0`
* Adding more information to ExceptionCollection exceptions so that users have more context, including a stack trace of
  each sub-exception
* Updated the `$ref` attribute in service descriptions to merge over any existing parameters of a schema (rather than
  clobbering everything).
* Merging URLs will now use the query string object from the relative URL (thus allowing custom query aggregators)
* Query strings are now parsed in a way that they do no convert empty keys with no value to have a dangling `=`.
  For example `foo&bar=baz` is now correctly parsed and recognized as `foo&bar=baz` rather than `foo=&bar=baz`.
* Now properly escaping the regular expression delimiter when matching Cookie domains.
* Network access is now disabled when loading XML documents

## 3.8.0 - 2013-12-05

* Added the ability to define a POST name for a file
* JSON response parsing now properly walks additionalProperties
* cURL error code 18 is now retried automatically in the BackoffPlugin
* Fixed a cURL error when URLs contain fragments
* Fixed an issue in the BackoffPlugin retry event where it was trying to access all exceptions as if they were
  CurlExceptions
* CURLOPT_PROGRESS function fix for PHP 5.5 (69fcc1e)
* Added the ability for Guzzle to work with older versions of cURL that do not support `CURLOPT_TIMEOUT_MS`
* Fixed a bug that was encountered when parsing empty header parameters
* UriTemplate now has a `setRegex()` method to match the docs
* The `debug` request parameter now checks if it is truthy rather than if it exists
* Setting the `debug` request parameter to true shows verbose cURL output instead of using the LogPlugin
* Added the ability to combine URLs using strict RFC 3986 compliance
* Command objects can now return the validation errors encountered by the command
* Various fixes to cache revalidation (#437 and 29797e5)
* Various fixes to the AsyncPlugin
* Cleaned up build scripts

## 3.7.4 - 2013-10-02

* Bug fix: 0 is now an allowed value in a description parameter that has a default value (#430)
* Bug fix: SchemaFormatter now returns an integer when formatting to a Unix timestamp
  (see https://github.com/aws/aws-sdk-php/issues/147)
* Bug fix: Cleaned up and fixed URL dot segment removal to properly resolve internal dots
* Minimum PHP version is now properly specified as 5.3.3 (up from 5.3.2) (#420)
* Updated the bundled cacert.pem (#419)
* OauthPlugin now supports adding authentication to headers or query string (#425)

## 3.7.3 - 2013-09-08

* Added the ability to get the exception associated with a request/command when using `MultiTransferException` and
  `CommandTransferException`.
* Setting `additionalParameters` of a response to false is now honored when parsing responses with a service description
* Schemas are only injected into response models when explicitly configured.
* No longer guessing Content-Type based on the path of a request. Content-Type is now only guessed based on the path of
  an EntityBody.
* Bug fix: ChunkedIterator can now properly chunk a \Traversable as well as an \Iterator.
* Bug fix: FilterIterator now relies on `\Iterator` instead of `\Traversable`.
* Bug fix: Gracefully handling malformed responses in RequestMediator::writeResponseBody()
* Bug fix: Replaced call to canCache with canCacheRequest in the CallbackCanCacheStrategy of the CachePlugin
* Bug fix: Visiting XML attributes first before visiting XML children when serializing requests
* Bug fix: Properly parsing headers that contain commas contained in quotes
* Bug fix: mimetype guessing based on a filename is now case-insensitive

## 3.7.2 - 2013-08-02

* Bug fix: Properly URL encoding paths when using the PHP-only version of the UriTemplate expander
  See https://github.com/guzzle/guzzle/issues/371
* Bug fix: Cookie domains are now matched correctly according to RFC 6265
  See https://github.com/guzzle/guzzle/issues/377
* Bug fix: GET parameters are now used when calculating an OAuth signature
* Bug fix: Fixed an issue with cache revalidation where the If-None-Match header was being double quoted
* `Guzzle\Common\AbstractHasDispatcher::dispatch()` now returns the event that was dispatched
* `Guzzle\Http\QueryString::factory()` now guesses the most appropriate query aggregator to used based on the input.
  See https://github.com/guzzle/guzzle/issues/379
* Added a way to add custom domain objects to service description parsing using the `operation.parse_class` event. See
  https://github.com/guzzle/guzzle/pull/380
* cURL multi cleanup and optimizations

## 3.7.1 - 2013-07-05

* Bug fix: Setting default options on a client now works
* Bug fix: Setting options on HEAD requests now works. See #352
* Bug fix: Moving stream factory before send event to before building the stream. See #353
* Bug fix: Cookies no longer match on IP addresses per RFC 6265
* Bug fix: Correctly parsing header parameters that are in `<>` and quotes
* Added `cert` and `ssl_key` as request options
* `Host` header can now diverge from the host part of a URL if the header is set manually
* `Guzzle\Service\Command\LocationVisitor\Request\XmlVisitor` was rewritten to change from using SimpleXML to XMLWriter
* OAuth parameters are only added via the plugin if they aren't already set
* Exceptions are now thrown when a URL cannot be parsed
* Returning `false` if `Guzzle\Http\EntityBody::getContentMd5()` fails
* Not setting a `Content-MD5` on a command if calculating the Content-MD5 fails via the CommandContentMd5Plugin

## 3.7.0 - 2013-06-10

* See UPGRADING.md for more information on how to upgrade.
* Requests now support the ability to specify an array of $options when creating a request to more easily modify a
  request. You can pass a 'request.options' configuration setting to a client to apply default request options to
  every request created by a client (e.g. default query string variables, headers, curl options, etc.).
* Added a static facade class that allows you to use Guzzle with static methods and mount the class to `\Guzzle`.
  See `Guzzle\Http\StaticClient::mount`.
* Added `command.request_options` to `Guzzle\Service\Command\AbstractCommand` to pass request options to requests
      created by a command (e.g. custom headers, query string variables, timeout settings, etc.).
* Stream size in `Guzzle\Stream\PhpStreamRequestFactory` will now be set if Content-Length is returned in the
  headers of a response
* Added `Guzzle\Common\Collection::setPath($path, $value)` to set a value into an array using a nested key
  (e.g. `$collection->setPath('foo/baz/bar', 'test'); echo $collection['foo']['bar']['bar'];`)
* ServiceBuilders now support storing and retrieving arbitrary data
* CachePlugin can now purge all resources for a given URI
* CachePlugin can automatically purge matching cached items when a non-idempotent request is sent to a resource
* CachePlugin now uses the Vary header to determine if a resource is a cache hit
* `Guzzle\Http\Message\Response` now implements `\Serializable`
* Added `Guzzle\Cache\CacheAdapterFactory::fromCache()` to more easily create cache adapters
* `Guzzle\Service\ClientInterface::execute()` now accepts an array, single command, or Traversable
* Fixed a bug in `Guzzle\Http\Message\Header\Link::addLink()`
* Better handling of calculating the size of a stream in `Guzzle\Stream\Stream` using fstat() and caching the size
* `Guzzle\Common\Exception\ExceptionCollection` now creates a more readable exception message
* Fixing BC break: Added back the MonologLogAdapter implementation rather than extending from PsrLog so that older
  Symfony users can still use the old version of Monolog.
* Fixing BC break: Added the implementation back in for `Guzzle\Http\Message\AbstractMessage::getTokenizedHeader()`.
  Now triggering an E_USER_DEPRECATED warning when used. Use `$message->getHeader()->parseParams()`.
* Several performance improvements to `Guzzle\Common\Collection`
* Added an `$options` argument to the end of the following methods of `Guzzle\Http\ClientInterface`:
  createRequest, head, delete, put, patch, post, options, prepareRequest
* Added an `$options` argument to the end of `Guzzle\Http\Message\Request\RequestFactoryInterface::createRequest()`
* Added an `applyOptions()` method to `Guzzle\Http\Message\Request\RequestFactoryInterface`
* Changed `Guzzle\Http\ClientInterface::get($uri = null, $headers = null, $body = null)` to
  `Guzzle\Http\ClientInterface::get($uri = null, $headers = null, $options = array())`. You can still pass in a
  resource, string, or EntityBody into the $options parameter to specify the download location of the response.
* Changed `Guzzle\Common\Collection::__construct($data)` to no longer accepts a null value for `$data` but a
  default `array()`
* Added `Guzzle\Stream\StreamInterface::isRepeatable`
* Removed `Guzzle\Http\ClientInterface::setDefaultHeaders(). Use
  $client->getConfig()->setPath('request.options/headers/{header_name}', 'value')`. or
  $client->getConfig()->setPath('request.options/headers', array('header_name' => 'value'))`.
* Removed `Guzzle\Http\ClientInterface::getDefaultHeaders(). Use $client->getConfig()->getPath('request.options/headers')`.
* Removed `Guzzle\Http\ClientInterface::expandTemplate()`
* Removed `Guzzle\Http\ClientInterface::setRequestFactory()`
* Removed `Guzzle\Http\ClientInterface::getCurlMulti()`
* Removed `Guzzle\Http\Message\RequestInterface::canCache`
* Removed `Guzzle\Http\Message\RequestInterface::setIsRedirect`
* Removed `Guzzle\Http\Message\RequestInterface::isRedirect`
* Made `Guzzle\Http\Client::expandTemplate` and `getUriTemplate` protected methods.
* You can now enable E_USER_DEPRECATED warnings to see if you are using a deprecated method by setting
  `Guzzle\Common\Version::$emitWarnings` to true.
* Marked `Guzzle\Http\Message\Request::isResponseBodyRepeatable()` as deprecated. Use
      `$request->getResponseBody()->isRepeatable()` instead.
* Marked `Guzzle\Http\Message\Request::canCache()` as deprecated. Use
  `Guzzle\Plugin\Cache\DefaultCanCacheStrategy->canCacheRequest()` instead.
* Marked `Guzzle\Http\Message\Request::canCache()` as deprecated. Use
  `Guzzle\Plugin\Cache\DefaultCanCacheStrategy->canCacheRequest()` instead.
* Marked `Guzzle\Http\Message\Request::setIsRedirect()` as deprecated. Use the HistoryPlugin instead.
* Marked `Guzzle\Http\Message\Request::isRedirect()` as deprecated. Use the HistoryPlugin instead.
* Marked `Guzzle\Cache\CacheAdapterFactory::factory()` as deprecated
* Marked 'command.headers', 'command.response_body' and 'command.on_complete' as deprecated for AbstractCommand.
  These will work through Guzzle 4.0
* Marked 'request.params' for `Guzzle\Http\Client` as deprecated. Use [request.options][params].
* Marked `Guzzle\Service\Client::enableMagicMethods()` as deprecated. Magic methods can no longer be disabled on a Guzzle\Service\Client.
* Marked `Guzzle\Service\Client::getDefaultHeaders()` as deprecated. Use $client->getConfig()->getPath('request.options/headers')`.
* Marked `Guzzle\Service\Client::setDefaultHeaders()` as deprecated. Use $client->getConfig()->setPath('request.options/headers/{header_name}', 'value')`.
* Marked `Guzzle\Parser\Url\UrlParser` as deprecated. Just use PHP's `parse_url()` and percent encode your UTF-8.
* Marked `Guzzle\Common\Collection::inject()` as deprecated.
* Marked `Guzzle\Plugin\CurlAuth\CurlAuthPlugin` as deprecated. Use `$client->getConfig()->setPath('request.options/auth', array('user', 'pass', 'Basic|Digest');`
* CacheKeyProviderInterface and DefaultCacheKeyProvider are no longer used. All of this logic is handled in a
  CacheStorageInterface. These two objects and interface will be removed in a future version.
* Always setting X-cache headers on cached responses
* Default cache TTLs are now handled by the CacheStorageInterface of a CachePlugin
* `CacheStorageInterface::cache($key, Response $response, $ttl = null)` has changed to `cache(RequestInterface
  $request, Response $response);`
* `CacheStorageInterface::fetch($key)` has changed to `fetch(RequestInterface $request);`
* `CacheStorageInterface::delete($key)` has changed to `delete(RequestInterface $request);`
* Added `CacheStorageInterface::purge($url)`
* `DefaultRevalidation::__construct(CacheKeyProviderInterface $cacheKey, CacheStorageInterface $cache, CachePlugin
  $plugin)` has changed to `DefaultRevalidation::__construct(CacheStorageInterface $cache,
  CanCacheStrategyInterface $canCache = null)`
* Added `RevalidationInterface::shouldRevalidate(RequestInterface $request, Response $response)`

## 3.6.0 - 2013-05-29

* ServiceDescription now implements ToArrayInterface
* Added command.hidden_params to blacklist certain headers from being treated as additionalParameters
* Guzzle can now correctly parse incomplete URLs
* Mixed casing of headers are now forced to be a single consistent casing across all values for that header.
* Messages internally use a HeaderCollection object to delegate handling case-insensitive header resolution
* Removed the whole changedHeader() function system of messages because all header changes now go through addHeader().
* Specific header implementations can be created for complex headers. When a message creates a header, it uses a
  HeaderFactory which can map specific headers to specific header classes. There is now a Link header and
  CacheControl header implementation.
* Removed from interface: Guzzle\Http\ClientInterface::setUriTemplate
* Removed from interface: Guzzle\Http\ClientInterface::setCurlMulti()
* Removed Guzzle\Http\Message\Request::receivedRequestHeader() and implemented this functionality in
  Guzzle\Http\Curl\RequestMediator
* Removed the optional $asString parameter from MessageInterface::getHeader(). Just cast the header to a string.
* Removed the optional $tryChunkedTransfer option from Guzzle\Http\Message\EntityEnclosingRequestInterface
* Removed the $asObjects argument from Guzzle\Http\Message\MessageInterface::getHeaders()
* Removed Guzzle\Parser\ParserRegister::get(). Use getParser()
* Removed Guzzle\Parser\ParserRegister::set(). Use registerParser().
* All response header helper functions return a string rather than mixing Header objects and strings inconsistently
* Removed cURL blacklist support. This is no longer necessary now that Expect, Accept, etc. are managed by Guzzle
  directly via interfaces
* Removed the injecting of a request object onto a response object. The methods to get and set a request still exist
  but are a no-op until removed.
* Most classes that used to require a `Guzzle\Service\Command\CommandInterface` typehint now request a
  `Guzzle\Service\Command\ArrayCommandInterface`.
* Added `Guzzle\Http\Message\RequestInterface::startResponse()` to the RequestInterface to handle injecting a response
  on a request while the request is still being transferred
* The ability to case-insensitively search for header values
* Guzzle\Http\Message\Header::hasExactHeader
* Guzzle\Http\Message\Header::raw. Use getAll()
* Deprecated cache control specific methods on Guzzle\Http\Message\AbstractMessage. Use the CacheControl header object
  instead.
* `Guzzle\Service\Command\CommandInterface` now extends from ToArrayInterface and ArrayAccess
* Added the ability to cast Model objects to a string to view debug information.

## 3.5.0 - 2013-05-13

* Bug: Fixed a regression so that request responses are parsed only once per oncomplete event rather than multiple times
* Bug: Better cleanup of one-time events across the board (when an event is meant to fire once, it will now remove
  itself from the EventDispatcher)
* Bug: `Guzzle\Log\MessageFormatter` now properly writes "total_time" and "connect_time" values
* Bug: Cloning an EntityEnclosingRequest now clones the EntityBody too
* Bug: Fixed an undefined index error when parsing nested JSON responses with a sentAs parameter that reference a
  non-existent key
* Bug: All __call() method arguments are now required (helps with mocking frameworks)
* Deprecating Response::getRequest() and now using a shallow clone of a request object to remove a circular reference
  to help with refcount based garbage collection of resources created by sending a request
* Deprecating ZF1 cache and log adapters. These will be removed in the next major version.
* Deprecating `Response::getPreviousResponse()` (method signature still exists, but it's deprecated). Use the
  HistoryPlugin for a history.
* Added a `responseBody` alias for the `response_body` location
* Refactored internals to no longer rely on Response::getRequest()
* HistoryPlugin can now be cast to a string
* HistoryPlugin now logs transactions rather than requests and responses to more accurately keep track of the requests
  and responses that are sent over the wire
* Added `getEffectiveUrl()` and `getRedirectCount()` to Response objects

## 3.4.3 - 2013-04-30

* Bug fix: Fixing bug introduced in 3.4.2 where redirect responses are duplicated on the final redirected response
* Added a check to re-extract the temp cacert bundle from the phar before sending each request

## 3.4.2 - 2013-04-29

* Bug fix: Stream objects now work correctly with "a" and "a+" modes
* Bug fix: Removing `Transfer-Encoding: chunked` header when a Content-Length is present
* Bug fix: AsyncPlugin no longer forces HEAD requests
* Bug fix: DateTime timezones are now properly handled when using the service description schema formatter
* Bug fix: CachePlugin now properly handles stale-if-error directives when a request to the origin server fails
* Setting a response on a request will write to the custom request body from the response body if one is specified
* LogPlugin now writes to php://output when STDERR is undefined
* Added the ability to set multiple POST files for the same key in a single call
* application/x-www-form-urlencoded POSTs now use the utf-8 charset by default
* Added the ability to queue CurlExceptions to the MockPlugin
* Cleaned up how manual responses are queued on requests (removed "queued_response" and now using request.before_send)
* Configuration loading now allows remote files

## 3.4.1 - 2013-04-16

* Large refactoring to how CurlMulti handles work. There is now a proxy that sits in front of a pool of CurlMulti
  handles. This greatly simplifies the implementation, fixes a couple bugs, and provides a small performance boost.
* Exceptions are now properly grouped when sending requests in parallel
* Redirects are now properly aggregated when a multi transaction fails
* Redirects now set the response on the original object even in the event of a failure
* Bug fix: Model names are now properly set even when using $refs
* Added support for PHP 5.5's CurlFile to prevent warnings with the deprecated @ syntax
* Added support for oauth_callback in OAuth signatures
* Added support for oauth_verifier in OAuth signatures
* Added support to attempt to retrieve a command first literally, then ucfirst, the with inflection

## 3.4.0 - 2013-04-11

* Bug fix: URLs are now resolved correctly based on http://tools.ietf.org/html/rfc3986#section-5.2. #289
* Bug fix: Absolute URLs with a path in a service description will now properly override the base URL. #289
* Bug fix: Parsing a query string with a single PHP array value will now result in an array. #263
* Bug fix: Better normalization of the User-Agent header to prevent duplicate headers. #264.
* Bug fix: Added `number` type to service descriptions.
* Bug fix: empty parameters are removed from an OAuth signature
* Bug fix: Revalidating a cache entry prefers the Last-Modified over the Date header
* Bug fix: Fixed "array to string" error when validating a union of types in a service description
* Bug fix: Removed code that attempted to determine the size of a stream when data is written to the stream
* Bug fix: Not including an `oauth_token` if the value is null in the OauthPlugin.
* Bug fix: Now correctly aggregating successful requests and failed requests in CurlMulti when a redirect occurs.
* The new default CURLOPT_TIMEOUT setting has been increased to 150 seconds so that Guzzle works on poor connections.
* Added a feature to EntityEnclosingRequest::setBody() that will automatically set the Content-Type of the request if
  the Content-Type can be determined based on the entity body or the path of the request.
* Added the ability to overwrite configuration settings in a client when grabbing a throwaway client from a builder.
* Added support for a PSR-3 LogAdapter.
* Added a `command.after_prepare` event
* Added `oauth_callback` parameter to the OauthPlugin
* Added the ability to create a custom stream class when using a stream factory
* Added a CachingEntityBody decorator
* Added support for `additionalParameters` in service descriptions to define how custom parameters are serialized.
* The bundled SSL certificate is now provided in the phar file and extracted when running Guzzle from a phar.
* You can now send any EntityEnclosingRequest with POST fields or POST files and cURL will handle creating bodies
* POST requests using a custom entity body are now treated exactly like PUT requests but with a custom cURL method. This
  means that the redirect behavior of POST requests with custom bodies will not be the same as POST requests that use
  POST fields or files (the latter is only used when emulating a form POST in the browser).
* Lots of cleanup to CurlHandle::factory and RequestFactory::createRequest

## 3.3.1 - 2013-03-10

* Added the ability to create PHP streaming responses from HTTP requests
* Bug fix: Running any filters when parsing response headers with service descriptions
* Bug fix: OauthPlugin fixes to allow for multi-dimensional array signing, and sorting parameters before signing
* Bug fix: Removed the adding of default empty arrays and false Booleans to responses in order to be consistent across
  response location visitors.
* Bug fix: Removed the possibility of creating configuration files with circular dependencies
* RequestFactory::create() now uses the key of a POST file when setting the POST file name
* Added xmlAllowEmpty to serialize an XML body even if no XML specific parameters are set

## 3.3.0 - 2013-03-03

* A large number of performance optimizations have been made
* Bug fix: Added 'wb' as a valid write mode for streams
* Bug fix: `Guzzle\Http\Message\Response::json()` now allows scalar values to be returned
* Bug fix: Fixed bug in `Guzzle\Http\Message\Response` where wrapping quotes were stripped from `getEtag()`
* BC: Removed `Guzzle\Http\Utils` class
* BC: Setting a service description on a client will no longer modify the client's command factories.
* BC: Emitting IO events from a RequestMediator is now a parameter that must be set in a request's curl options using
  the 'emit_io' key. This was previously set under a request's parameters using 'curl.emit_io'
* BC: `Guzzle\Stream\Stream::getWrapper()` and `Guzzle\Stream\Stream::getSteamType()` are no longer converted to
  lowercase
* Operation parameter objects are now lazy loaded internally
* Added ErrorResponsePlugin that can throw errors for responses defined in service description operations' errorResponses
* Added support for instantiating responseType=class responseClass classes. Classes must implement
  `Guzzle\Service\Command\ResponseClassInterface`
* Added support for additionalProperties for top-level parameters in responseType=model responseClasses. These
  additional properties also support locations and can be used to parse JSON responses where the outermost part of the
  JSON is an array
* Added support for nested renaming of JSON models (rename sentAs to name)
* CachePlugin
    * Added support for stale-if-error so that the CachePlugin can now serve stale content from the cache on error
    * Debug headers can now added to cached response in the CachePlugin

## 3.2.0 - 2013-02-14

* CurlMulti is no longer reused globally. A new multi object is created per-client. This helps to isolate clients.
* URLs with no path no longer contain a "/" by default
* Guzzle\Http\QueryString does no longer manages the leading "?". This is now handled in Guzzle\Http\Url.
* BadResponseException no longer includes the full request and response message
* Adding setData() to Guzzle\Service\Description\ServiceDescriptionInterface
* Adding getResponseBody() to Guzzle\Http\Message\RequestInterface
* Various updates to classes to use ServiceDescriptionInterface type hints rather than ServiceDescription
* Header values can now be normalized into distinct values when multiple headers are combined with a comma separated list
* xmlEncoding can now be customized for the XML declaration of a XML service description operation
* Guzzle\Http\QueryString now uses Guzzle\Http\QueryAggregator\QueryAggregatorInterface objects to add custom value
  aggregation and no longer uses callbacks
* The URL encoding implementation of Guzzle\Http\QueryString can now be customized
* Bug fix: Filters were not always invoked for array service description parameters
* Bug fix: Redirects now use a target response body rather than a temporary response body
* Bug fix: The default exponential backoff BackoffPlugin was not giving when the request threshold was exceeded
* Bug fix: Guzzle now takes the first found value when grabbing Cache-Control directives

## 3.1.2 - 2013-01-27

* Refactored how operation responses are parsed. Visitors now include a before() method responsible for parsing the
  response body. For example, the XmlVisitor now parses the XML response into an array in the before() method.
* Fixed an issue where cURL would not automatically decompress responses when the Accept-Encoding header was sent
* CURLOPT_SSL_VERIFYHOST is never set to 1 because it is deprecated (see 5e0ff2ef20f839e19d1eeb298f90ba3598784444)
* Fixed a bug where redirect responses were not chained correctly using getPreviousResponse()
* Setting default headers on a client after setting the user-agent will not erase the user-agent setting

## 3.1.1 - 2013-01-20

* Adding wildcard support to Guzzle\Common\Collection::getPath()
* Adding alias support to ServiceBuilder configs
* Adding Guzzle\Service\Resource\CompositeResourceIteratorFactory and cleaning up factory interface

## 3.1.0 - 2013-01-12

* BC: CurlException now extends from RequestException rather than BadResponseException
* BC: Renamed Guzzle\Plugin\Cache\CanCacheStrategyInterface::canCache() to canCacheRequest() and added CanCacheResponse()
* Added getData to ServiceDescriptionInterface
* Added context array to RequestInterface::setState()
* Bug: Removing hard dependency on the BackoffPlugin from Guzzle\Http
* Bug: Adding required content-type when JSON request visitor adds JSON to a command
* Bug: Fixing the serialization of a service description with custom data
* Made it easier to deal with exceptions thrown when transferring commands or requests in parallel by providing
  an array of successful and failed responses
* Moved getPath from Guzzle\Service\Resource\Model to Guzzle\Common\Collection
* Added Guzzle\Http\IoEmittingEntityBody
* Moved command filtration from validators to location visitors
* Added `extends` attributes to service description parameters
* Added getModels to ServiceDescriptionInterface

## 3.0.7 - 2012-12-19

* Fixing phar detection when forcing a cacert to system if null or true
* Allowing filename to be passed to `Guzzle\Http\Message\Request::setResponseBody()`
* Cleaning up `Guzzle\Common\Collection::inject` method
* Adding a response_body location to service descriptions

## 3.0.6 - 2012-12-09

* CurlMulti performance improvements
* Adding setErrorResponses() to Operation
* composer.json tweaks

## 3.0.5 - 2012-11-18

* Bug: Fixing an infinite recursion bug caused from revalidating with the CachePlugin
* Bug: Response body can now be a string containing "0"
* Bug: Using Guzzle inside of a phar uses system by default but now allows for a custom cacert
* Bug: QueryString::fromString now properly parses query string parameters that contain equal signs
* Added support for XML attributes in service description responses
* DefaultRequestSerializer now supports array URI parameter values for URI template expansion
* Added better mimetype guessing to requests and post files

## 3.0.4 - 2012-11-11

* Bug: Fixed a bug when adding multiple cookies to a request to use the correct glue value
* Bug: Cookies can now be added that have a name, domain, or value set to "0"
* Bug: Using the system cacert bundle when using the Phar
* Added json and xml methods to Response to make it easier to parse JSON and XML response data into data structures
* Enhanced cookie jar de-duplication
* Added the ability to enable strict cookie jars that throw exceptions when invalid cookies are added
* Added setStream to StreamInterface to actually make it possible to implement custom rewind behavior for entity bodies
* Added the ability to create any sort of hash for a stream rather than just an MD5 hash

## 3.0.3 - 2012-11-04

* Implementing redirects in PHP rather than cURL
* Added PECL URI template extension and using as default parser if available
* Bug: Fixed Content-Length parsing of Response factory
* Adding rewind() method to entity bodies and streams. Allows for custom rewinding of non-repeatable streams.
* Adding ToArrayInterface throughout library
* Fixing OauthPlugin to create unique nonce values per request

## 3.0.2 - 2012-10-25

* Magic methods are enabled by default on clients
* Magic methods return the result of a command
* Service clients no longer require a base_url option in the factory
* Bug: Fixed an issue with URI templates where null template variables were being expanded

## 3.0.1 - 2012-10-22

* Models can now be used like regular collection objects by calling filter, map, etc.
* Models no longer require a Parameter structure or initial data in the constructor
* Added a custom AppendIterator to get around a PHP bug with the `\AppendIterator`

## 3.0.0 - 2012-10-15

* Rewrote service description format to be based on Swagger
    * Now based on JSON schema
    * Added nested input structures and nested response models
    * Support for JSON and XML input and output models
    * Renamed `commands` to `operations`
    * Removed dot class notation
    * Removed custom types
* Broke the project into smaller top-level namespaces to be more component friendly
* Removed support for XML configs and descriptions. Use arrays or JSON files.
* Removed the Validation component and Inspector
* Moved all cookie code to Guzzle\Plugin\Cookie
* Magic methods on a Guzzle\Service\Client now return the command un-executed.
* Calling getResult() or getResponse() on a command will lazily execute the command if needed.
* Now shipping with cURL's CA certs and using it by default
* Added previousResponse() method to response objects
* No longer sending Accept and Accept-Encoding headers on every request
* Only sending an Expect header by default when a payload is greater than 1MB
* Added/moved client options:
    * curl.blacklist to curl.option.blacklist
    * Added ssl.certificate_authority
* Added a Guzzle\Iterator component
* Moved plugins from Guzzle\Http\Plugin to Guzzle\Plugin
* Added a more robust backoff retry strategy (replaced the ExponentialBackoffPlugin)
* Added a more robust caching plugin
* Added setBody to response objects
* Updating LogPlugin to use a more flexible MessageFormatter
* Added a completely revamped build process
* Cleaning up Collection class and removing default values from the get method
* Fixed ZF2 cache adapters

## 2.8.8 - 2012-10-15

* Bug: Fixed a cookie issue that caused dot prefixed domains to not match where popular browsers did

## 2.8.7 - 2012-09-30

* Bug: Fixed config file aliases for JSON includes
* Bug: Fixed cookie bug on a request object by using CookieParser to parse cookies on requests
* Bug: Removing the path to a file when sending a Content-Disposition header on a POST upload
* Bug: Hardening request and response parsing to account for missing parts
* Bug: Fixed PEAR packaging
* Bug: Fixed Request::getInfo
* Bug: Fixed cases where CURLM_CALL_MULTI_PERFORM return codes were causing curl transactions to fail
* Adding the ability for the namespace Iterator factory to look in multiple directories
* Added more getters/setters/removers from service descriptions
* Added the ability to remove POST fields from OAuth signatures
* OAuth plugin now supports 2-legged OAuth

## 2.8.6 - 2012-09-05

* Added the ability to modify and build service descriptions
* Added the use of visitors to apply parameters to locations in service descriptions using the dynamic command
* Added a `json` parameter location
* Now allowing dot notation for classes in the CacheAdapterFactory
* Using the union of two arrays rather than an array_merge when extending service builder services and service params
* Ensuring that a service is a string before doing strpos() checks on it when substituting services for references
  in service builder config files.
* Services defined in two different config files that include one another will by default replace the previously
  defined service, but you can now create services that extend themselves and merge their settings over the previous
* The JsonLoader now supports aliasing filenames with different filenames. This allows you to alias something like
  '_default' with a default JSON configuration file.

## 2.8.5 - 2012-08-29

* Bug: Suppressed empty arrays from URI templates
* Bug: Added the missing $options argument from ServiceDescription::factory to enable caching
* Added support for HTTP responses that do not contain a reason phrase in the start-line
* AbstractCommand commands are now invokable
* Added a way to get the data used when signing an Oauth request before a request is sent

## 2.8.4 - 2012-08-15

* Bug: Custom delay time calculations are no longer ignored in the ExponentialBackoffPlugin
* Added the ability to transfer entity bodies as a string rather than streamed. This gets around curl error 65. Set `body_as_string` in a request's curl options to enable.
* Added a StreamInterface, EntityBodyInterface, and added ftell() to Guzzle\Common\Stream
* Added an AbstractEntityBodyDecorator and a ReadLimitEntityBody decorator to transfer only a subset of a decorated stream
* Stream and EntityBody objects will now return the file position to the previous position after a read required operation (e.g. getContentMd5())
* Added additional response status codes
* Removed SSL information from the default User-Agent header
* DELETE requests can now send an entity body
* Added an EventDispatcher to the ExponentialBackoffPlugin and added an ExponentialBackoffLogger to log backoff retries
* Added the ability of the MockPlugin to consume mocked request bodies
* LogPlugin now exposes request and response objects in the extras array

## 2.8.3 - 2012-07-30

* Bug: Fixed a case where empty POST requests were sent as GET requests
* Bug: Fixed a bug in ExponentialBackoffPlugin that caused fatal errors when retrying an EntityEnclosingRequest that does not have a body
* Bug: Setting the response body of a request to null after completing a request, not when setting the state of a request to new
* Added multiple inheritance to service description commands
* Added an ApiCommandInterface and added `getParamNames()` and `hasParam()`
* Removed the default 2mb size cutoff from the Md5ValidatorPlugin so that it now defaults to validating everything
* Changed CurlMulti::perform to pass a smaller timeout to CurlMulti::executeHandles

## 2.8.2 - 2012-07-24

* Bug: Query string values set to 0 are no longer dropped from the query string
* Bug: A Collection object is no longer created each time a call is made to `Guzzle\Service\Command\AbstractCommand::getRequestHeaders()`
* Bug: `+` is now treated as an encoded space when parsing query strings
* QueryString and Collection performance improvements
* Allowing dot notation for class paths in filters attribute of a service descriptions

## 2.8.1 - 2012-07-16

* Loosening Event Dispatcher dependency
* POST redirects can now be customized using CURLOPT_POSTREDIR

## 2.8.0 - 2012-07-15

* BC: Guzzle\Http\Query
    * Query strings with empty variables will always show an equal sign unless the variable is set to QueryString::BLANK (e.g. ?acl= vs ?acl)
    * Changed isEncodingValues() and isEncodingFields() to isUrlEncoding()
    * Changed setEncodeValues(bool) and setEncodeFields(bool) to useUrlEncoding(bool)
    * Changed the aggregation functions of QueryString to be static methods
    * Can now use fromString() with querystrings that have a leading ?
* cURL configuration values can be specified in service descriptions using `curl.` prefixed parameters
* Content-Length is set to 0 before emitting the request.before_send event when sending an empty request body
* Cookies are no longer URL decoded by default
* Bug: URI template variables set to null are no longer expanded

## 2.7.2 - 2012-07-02

* BC: Moving things to get ready for subtree splits. Moving Inflection into Common. Moving Guzzle\Http\Parser to Guzzle\Parser.
* BC: Removing Guzzle\Common\Batch\Batch::count() and replacing it with isEmpty()
* CachePlugin now allows for a custom request parameter function to check if a request can be cached
* Bug fix: CachePlugin now only caches GET and HEAD requests by default
* Bug fix: Using header glue when transferring headers over the wire
* Allowing deeply nested arrays for composite variables in URI templates
* Batch divisors can now return iterators or arrays

## 2.7.1 - 2012-06-26

* Minor patch to update version number in UA string
* Updating build process

## 2.7.0 - 2012-06-25

* BC: Inflection classes moved to Guzzle\Inflection. No longer static methods. Can now inject custom inflectors into classes.
* BC: Removed magic setX methods from commands
* BC: Magic methods mapped to service description commands are now inflected in the command factory rather than the client __call() method
* Verbose cURL options are no longer enabled by default. Set curl.debug to true on a client to enable.
* Bug: Now allowing colons in a response start-line (e.g. HTTP/1.1 503 Service Unavailable: Back-end server is at capacity)
* Guzzle\Service\Resource\ResourceIteratorApplyBatched now internally uses the Guzzle\Common\Batch namespace
* Added Guzzle\Service\Plugin namespace and a PluginCollectionPlugin
* Added the ability to set POST fields and files in a service description
* Guzzle\Http\EntityBody::factory() now accepts objects with a __toString() method
* Adding a command.before_prepare event to clients
* Added BatchClosureTransfer and BatchClosureDivisor
* BatchTransferException now includes references to the batch divisor and transfer strategies
* Fixed some tests so that they pass more reliably
* Added Guzzle\Common\Log\ArrayLogAdapter

## 2.6.6 - 2012-06-10

* BC: Removing Guzzle\Http\Plugin\BatchQueuePlugin
* BC: Removing Guzzle\Service\Command\CommandSet
* Adding generic batching system (replaces the batch queue plugin and command set)
* Updating ZF cache and log adapters and now using ZF's composer repository
* Bug: Setting the name of each ApiParam when creating through an ApiCommand
* Adding result_type, result_doc, deprecated, and doc_url to service descriptions
* Bug: Changed the default cookie header casing back to 'Cookie'

## 2.6.5 - 2012-06-03

* BC: Renaming Guzzle\Http\Message\RequestInterface::getResourceUri() to getResource()
* BC: Removing unused AUTH_BASIC and AUTH_DIGEST constants from
* BC: Guzzle\Http\Cookie is now used to manage Set-Cookie data, not Cookie data
* BC: Renaming methods in the CookieJarInterface
* Moving almost all cookie logic out of the CookiePlugin and into the Cookie or CookieJar implementations
* Making the default glue for HTTP headers ';' instead of ','
* Adding a removeValue to Guzzle\Http\Message\Header
* Adding getCookies() to request interface.
* Making it easier to add event subscribers to HasDispatcherInterface classes. Can now directly call addSubscriber()

## 2.6.4 - 2012-05-30

* BC: Cleaning up how POST files are stored in EntityEnclosingRequest objects. Adding PostFile class.
* BC: Moving ApiCommand specific functionality from the Inspector and on to the ApiCommand
* Bug: Fixing magic method command calls on clients
* Bug: Email constraint only validates strings
* Bug: Aggregate POST fields when POST files are present in curl handle
* Bug: Fixing default User-Agent header
* Bug: Only appending or prepending parameters in commands if they are specified
* Bug: Not requiring response reason phrases or status codes to match a predefined list of codes
* Allowing the use of dot notation for class namespaces when using instance_of constraint
* Added any_match validation constraint
* Added an AsyncPlugin
* Passing request object to the calculateWait method of the ExponentialBackoffPlugin
* Allowing the result of a command object to be changed
* Parsing location and type sub values when instantiating a service description rather than over and over at runtime

## 2.6.3 - 2012-05-23

* [BC] Guzzle\Common\FromConfigInterface no longer requires any config options.
* [BC] Refactoring how POST files are stored on an EntityEnclosingRequest. They are now separate from POST fields.
* You can now use an array of data when creating PUT request bodies in the request factory.
* Removing the requirement that HTTPS requests needed a Cache-Control: public directive to be cacheable.
* [Http] Adding support for Content-Type in multipart POST uploads per upload
* [Http] Added support for uploading multiple files using the same name (foo[0], foo[1])
* Adding more POST data operations for easier manipulation of POST data.
* You can now set empty POST fields.
* The body of a request is only shown on EntityEnclosingRequest objects that do not use POST files.
* Split the Guzzle\Service\Inspector::validateConfig method into two methods. One to initialize when a command is created, and one to validate.
* CS updates

## 2.6.2 - 2012-05-19

* [Http] Better handling of nested scope requests in CurlMulti.  Requests are now always prepares in the send() method rather than the addRequest() method.

## 2.6.1 - 2012-05-19

* [BC] Removing 'path' support in service descriptions.  Use 'uri'.
* [BC] Guzzle\Service\Inspector::parseDocBlock is now protected. Adding getApiParamsForClass() with cache.
* [BC] Removing Guzzle\Common\NullObject.  Use https://github.com/mtdowling/NullObject if you need it.
* [BC] Removing Guzzle\Common\XmlElement.
* All commands, both dynamic and concrete, have ApiCommand objects.
* Adding a fix for CurlMulti so that if all of the connections encounter some sort of curl error, then the loop exits.
* Adding checks to EntityEnclosingRequest so that empty POST files and fields are ignored.
* Making the method signature of Guzzle\Service\Builder\ServiceBuilder::factory more flexible.

## 2.6.0 - 2012-05-15

* [BC] Moving Guzzle\Service\Builder to Guzzle\Service\Builder\ServiceBuilder
* [BC] Executing a Command returns the result of the command rather than the command
* [BC] Moving all HTTP parsing logic to Guzzle\Http\Parsers. Allows for faster C implementations if needed.
* [BC] Changing the Guzzle\Http\Message\Response::setProtocol() method to accept a protocol and version in separate args.
* [BC] Moving ResourceIterator* to Guzzle\Service\Resource
* [BC] Completely refactored ResourceIterators to iterate over a cloned command object
* [BC] Moved Guzzle\Http\UriTemplate to Guzzle\Http\Parser\UriTemplate\UriTemplate
* [BC] Guzzle\Guzzle is now deprecated
* Moving Guzzle\Common\Guzzle::inject to Guzzle\Common\Collection::inject
* Adding Guzzle\Version class to give version information about Guzzle
* Adding Guzzle\Http\Utils class to provide getDefaultUserAgent() and getHttpDate()
* Adding Guzzle\Curl\CurlVersion to manage caching curl_version() data
* ServiceDescription and ServiceBuilder are now cacheable using similar configs
* Changing the format of XML and JSON service builder configs.  Backwards compatible.
* Cleaned up Cookie parsing
* Trimming the default Guzzle User-Agent header
* Adding a setOnComplete() method to Commands that is called when a command completes
* Keeping track of requests that were mocked in the MockPlugin
* Fixed a caching bug in the CacheAdapterFactory
* Inspector objects can be injected into a Command object
* Refactoring a lot of code and tests to be case insensitive when dealing with headers
* Adding Guzzle\Http\Message\HeaderComparison for easy comparison of HTTP headers using a DSL
* Adding the ability to set global option overrides to service builder configs
* Adding the ability to include other service builder config files from within XML and JSON files
* Moving the parseQuery method out of Url and on to QueryString::fromString() as a static factory method.

## 2.5.0 - 2012-05-08

* Major performance improvements
* [BC] Simplifying Guzzle\Common\Collection.  Please check to see if you are using features that are now deprecated.
* [BC] Using a custom validation system that allows a flyweight implementation for much faster validation. No longer using Symfony2 Validation component.
* [BC] No longer supporting "{{ }}" for injecting into command or UriTemplates.  Use "{}"
* Added the ability to passed parameters to all requests created by a client
* Added callback functionality to the ExponentialBackoffPlugin
* Using microtime in ExponentialBackoffPlugin to allow more granular backoff strategies.
* Rewinding request stream bodies when retrying requests
* Exception is thrown when JSON response body cannot be decoded
* Added configurable magic method calls to clients and commands.  This is off by default.
* Fixed a defect that added a hash to every parsed URL part
* Fixed duplicate none generation for OauthPlugin.
* Emitting an event each time a client is generated by a ServiceBuilder
* Using an ApiParams object instead of a Collection for parameters of an ApiCommand
* cache.* request parameters should be renamed to params.cache.*
* Added the ability to set arbitrary curl options on requests (disable_wire, progress, etc.). See CurlHandle.
* Added the ability to disable type validation of service descriptions
* ServiceDescriptions and ServiceBuilders are now Serializable
vendor/guzzlehttp/guzzle/composer.json000064400000002175151327705700014337 0ustar00{
    "name": "guzzlehttp/guzzle",
    "type": "library",
    "description": "Guzzle is a PHP HTTP client library",
    "keywords": ["framework", "http", "rest", "web service", "curl", "client", "HTTP client"],
    "homepage": "http://guzzlephp.org/",
    "license": "MIT",
    "authors": [
        {
            "name": "Michael Dowling",
            "email": "mtdowling@gmail.com",
            "homepage": "https://github.com/mtdowling"
        }
    ],
    "require": {
        "php": ">=5.5",
        "guzzlehttp/psr7": "^1.4",
        "guzzlehttp/promises": "^1.0"
    },
    "require-dev": {
        "ext-curl": "*",
        "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.4 || ^7.0",
        "psr/log": "^1.0"
    },
    "autoload": {
        "files": ["src/functions_include.php"],
        "psr-4": {
            "WPvividGuzzleHttp\\": "src/"
        }
    },
    "autoload-dev": {
        "psr-4": {
            "WPvividGuzzleHttp\\Tests\\": "tests/"
        }
    },
    "suggest": {
        "psr/log": "Required for using the Log middleware"
    },
    "extra": {
        "branch-alias": {
            "dev-master": "6.3-dev"
        }
    }
}
readme.txt000064400000077735151327705700006574 0ustar00=== Migration, Backup, Staging – WPvivid Backup & Migration ===
Contributors: wpvivid
Tags: duplicate, clone, migrate, staging, backup
Requires at least: 4.5
Tested up to: 6.7.2
Requires PHP: 5.3
Stable tag: 0.9.113
License: GPLv3 or later
License URI: https://www.gnu.org/licenses/gpl-3.0.en.html

Migrate, staging, backup WordPress, all in one.

== Description ==
WPvivid Backup & Migration Plugin offers backup, migration, and staging (create a staging site on a subdirectory to safely test WordPress, plugins, themes and website changes) as basic features.

== WPvivid Backup & Migration for MainWP ==
[WPvivid Backup & Migration for MainWP](https://wordpress.org/plugins/wpvivid-backup-mainwp/) is now available to download.
WPvivid Backup & Migration for MainWP allows you to set up and control WPvivid Backup & Migration plugins for all child sites directly from your MainWP dashboard.

== WPvivid Backup & Migration Pro is Now Available ==
* Customize everything to backup
* Create staging sites and push staging sites to live
* Incremental backups
* Database backup encryption
* Auto backup WordPress, themes, and plugins
* WordPress multisite backup
* WordPress multisite staging
* Create a fresh WP install
* Advanced remote backups
* Advanced backup schedules
* Restore remote backups
* Migrate a site via remote storage
* Migrate a childsite (MU) to a single WordPress install
* White label WPvivid Backup & Migration Pro
* Control user access to WPvivid Backup & Migration Pro
* [More amazing features](https://wpvivid.com/backup-plugin-pro)

See a review video on WPvivid Backup & Migration Pro:

https://www.youtube.com/watch?v=D1aYbayFpfU&t=7s

[Get WPvivid Backup & Migration Pro](https://wpvivid.com/pricing)

== Core Features ==

= 1. Easy Backups =
Easily create a backup of your WordPress site. You can choose to backup the entire site(database+files), all files, or database only.
= 2. Auto Migration =
Clone and migrate your WordPress site to a new domain with a single click. WPvivid Backup & Migration Plugin supports site migration from dev environment to a new server, from dev environment to a new domain or from a live server to another.
= 3. Create A Staging Site =
Create a staging site on a subdirectory of your production site to safely test WordPress, plugins, themes and website changes. You can choose what to copy from the the live site to the staging site.
= 4. Scheduled Backups =
Set a schedule to run backups automatically on your website. You can set the backups to run every 12 hours, daily, weekly, fortnightly, monthly, choose backup items and destination.
= 5. Offsite Backup to Remote Storage =
Send your backups offsite to a remote location. WPvivid Backup & Migration Plugin supports the leading cloud storage providers: Dropbox, Google Drive, Amazon S3, Microsoft OneDrive, DigitalOcean Spaces, FTP and SFTP.
= 6. One-Click Restore =
Restore your WordPress site from a backup with a single click.
= 7. Cloud Storage Supported =
WPvivid Backup & Migration plugin supports Dropbox, Google Drive, Microsoft OneDrive, Amazon S3, DigitalOcean Spaces, SFTP, FTP. WPvivid Backup & Migration Pro also supports Wasabi, pCloud, Backblaze, WebDav and more.

== Minimum Requirements to use WPvivid Backup & Migration plugin ==
* Character Encoding UTF-8
* PHP version 5.3
* MySQL version 4.1
* WordPress 4.5

== Screenshots ==
1. Backing up a site
2. Backup list
3. WPvivid Backup & Migration plugin Dashboard
4. Configure remote backups
5. Migrate a WordPress site to a new domain
6. Upload a backup to restore or migrate

== Installation ==

= Install WPvivid Backup & Migration Plugin =
1.Go to your sites admin dashboard.
2.Navigate to Plugins Menu and search for WPvivid Backup & Migration.
3.Find WPvivid Backup & Migration and click Install Now.
4. Click Activate.

== Frequently Asked Questions ==
= What does WPvivid Backup & Migration Plugin do? =
As the name says, WPvivid Backup & Migration Plugin is an all in one free WP backup & migration plugin that enables you to easily clone and migrate a WordPress site to a new domain, to perform manual backups and schedule automatic backups of your WordPress site, to backup to cloud storage and restore backups directly from your sites admin dashboard.
= Does WPvivid Backup & Migration Plugin also migrate my site? Is it a free feature? =
Yes, WPvivid Backup & Migration Plugin supports migration of a WordPress site.
Yes, the migration feature is completely free.
= How many cloud options does WPvivid Backup & Migration Plugin support? Are they free to access? =
Out of the box WPvivid Backup & Migration Plugin supports Dropbox, Google Drive, Amazon S3, Microsoft OneDrive, DigitalOcean Spaces, FTP, and SFTP.
Yes, all the cloud access is free.
= Can I use WPvivid Backup & Migration Plugin to restore my site? =
Yes, you can use WPvivid Backup & Migration Plugin to restore a WordPress site from a backup. With no limits, no strings attached.
= Do you provide support for WPvivid Backup & Migration Plugin? Where? =
Yes, absolutely. Whenever you need help, start a thread on the [support forum](https://wordpress.org/support/plugin/wpvivid-backuprestore/) for WPvivid Backup & Migration Plugin, or [contact us](https://wpvivid.com/contact-us).
= Do you have any get-started guides/docs? =
Yes, we do. Here are the guides for [migrating your site to a new host](https://wpvivid.com/get-started-transfer-site.html), [creating a manual backup](https://wpvivid.com/get-started-create-a-manual-backup.html), [restoring your site from a backup](https://wpvivid.com/get-started-restore-site.html), and more on [our docs page](https://wpvivid.com/documents).

== Changelog ==
= 0.9.113 =
- Added 4 backup performance modes for different scenarios.
- Fixed a vulnerability in the plugin code.
- Successfully tested with WordPress 6.7.2.
= 0.9.112 =
- Fixed: Backup status could not be properly caught on LocalWP sites with Nginx server.
- Optimized the plugin code.
= 0.9.111 =
- Fixed: Backups to SFTP would fail on sites of PHP 8.2.26-nmm1 and 8.3.14-nmm1.
- Optimized the plugin code.
= 0.9.110 =
- Added an option to include symlink folders in a backup.
- Fixed: Could not connect to SFTP on sites of PHP 8.2.26 and 8.3.14.
- Fixed: Quick Snapshot popup would appear upon each page loading.
- Successfully tested with WordPress 6.7.1.
= 0.9.109 =
- Fixed a warning that would appear with WordPress 6.7.0.
- Optimized the plugin code.
- Successfully tested with WordPress 6.7.0.
= 0.9.108 =
- Fixed a vulnerability in the plugin code.
- Fixed: WP Cerber plugin was excluded from a backup by default.
= 0.9.107 =
- Fixed: Backups to GoogleDrive could fail with an error of 'Will not follow more than 5 redirects' in some environments.
- Fixed some PHP warnings of 'Return type of WPvivid_Google_Collection' that would appear in some environments.
- Fixed a vulnerability in the plugin code.
= 0.9.106 =
- Fixed a vulnerability in the plugin code.
- Fixed: Could not send email report when the address contain '-'.
- Optimized the plugin code.
= 0.9.105 =
- Fixed: Uploading backups to OneDrive failed with a 401 error in some environments.
- Optimized the plugin code.
= 0.9.104 =
- Updated: Autoload of WPvivid options is set to 'No' by default.
- Fixed: Downloading backup files could fail in some environments.
- Fixed: Uploading backups to GoogleDrive could fail in some environments.
- Fixed some bugs in the plugin code.
- Optimized the plugin code.
= 0.9.103 =
- Fixed: Restore would fail when a backup contained mu-plugins/wp-stack-cache.php.
- Fixed some bugs in the plugin code.
- Refined and optimized the plugin code.
- Successfully tested with WordPress 6.6.
= 0.9.102 =
- Added: Cloud storage tokens are now encrypted in the database.
- Added: lotties folder (if any) will be included in backups by default.
- Fixed: Domain could not be replaced during migration in some cases.
- Fixed: Adding Digital Ocean Space would fail in some cases.
- Fixed: Images added via ACF plugin would be scanned as unused.
- Fixed some bugs in the plugin code.
- Optimized the plugin code.
= 0.9.101 =
- Fixed: Retention settings did not work for scheduled backups.
- Fixed: Scanning unused images would fail in some cases.
= 0.9.100 =
- Added a column to the backup list to show the backup size.
- Fixed: URLs could not be replaced during migration in some cases.
- Fixed: Adding SFTP remote storage could fail in some cases.
- Fixed a vulnerability in the plugin code.
= 0.9.99 =
- Fixed: Scheduled database backups could fail in some cases.
- Optimized the plugin code.
- Successfully tested with WordPress 6.5.
= 0.9.98 =
- Fixed: Backups to OneDrive failed in some environments.
- Fixed some PHP warnings.
- Optimized the plugin code.
= 0.9.97 =
- Fixed some vulnerable code and optimized the plugin code.
- Fixed: The option 'Keep backups in local after uploading them to cloud' could not take effect.
- Successfully tested with WordPress 6.4.3.
= 0.9.96 =
- Fixed: Restore could fail when max_allowe_packet of the server is low.
- Fixed some bugs in the plugin code.
- Optimized the plugin code.
- Successfully tested with WordPress 6.4.3.
= 0.9.95 =
- Fixed: Backup to SFTP would fail in some environments.
- Fixed: Backup to Google Drive would fail in some environments.
- Fixed: Creating a staging site would fail in some cases.
- Fixed: Some special characters would not display properly after website migration.
- Fixed some vulnerabilities in the plugin code.
- Fixed some bugs in the plugin code.
- Optimized the plugin code.
= 0.9.94 =
- Fixed: Prefix of tables with foreign keys would not be replaced in a migration process.
- Fixed: Corrupted backups would not be detected in some environments.
- Fixed some bugs in the plugin code.
- Optimized the plugin code.
= 0.9.93 =
- Added support for migration of sites without a database prefix.
- Fixed some bugs in the plugin code.
- Optimized the plugin code.
= 0.9.92 =
- Fixed a vulnerability in the plugin code.
- Fixed some bugs in the plugin code.
- Optimized the plugin code.
= 0.9.91 =
- Fixed: Error logs would not be attached to backup email reports.
- Fixed: Uploading backups to OneDrive would fail in some environments.
- Fixed a compatibility issue with JetBackup plugin.
- Fixed some vulnerabilities in the plugin code.
- Fixed some bugs in the plugin code.
- Optimized the plugin code.
= 0.9.90 =
- Successfully tested with WordPress 6.3.
- Fixed: restore would fail when a backup contained zero dates '0000-00-00'.
- Fixed: Customized site icons and logos would be falsely scanned as unused.
- Added an option to exclude folders from unused image scan.
- Fixed some bugs in the plugin code.
- Optimized the plugin code.
= 0.9.89 =
- Excluded backup-migration and backups-dup-lite from a backup.
- Fixed some bugs in the plugin code.
- Optimized the plugin code.
= 0.9.88 =
- Fixed: Database restoration would fail in some environments.
- Fixed: Staging creation would fail when the database contained the aiowps_debug_log table.
- Fixed a library conflict with the Skaut Google Drive Gallery plugin.
- Fixed some PHP warnings that appeared on sites with newer PHP versions.
- Fixed some bugs in the plugin code.
- Optimized the plugin code.
= 0.9.87 =
- Fixed: Uploading backups to GoogleDrive failed because of an 'invalid credential' error in some cases.
- Fixed: Backup email report did not display properly in Outlook emails.
- Fixed some PHP warnings that appeared on sites with newer PHP versions.
- Fixed: Non admin users could see the plugin menus in the top admin bar.
- Fixed: Locked backups would not be deleted by backup retention.
- Fixed some bugs in the plugin code.
- Optimized the plugin code.
= 0.9.86 =
- Fixed a library conflict with the ElementsKit plugin.
- Fixed a library conflict with the YaySMTP plugin.
- Fixed some bugs in the plugin code.
- Optimized the plugin code.
= 0.9.85 =
- Added breakpoint resume for GoogleDrive, OneDrive and Dropbox upload.
- Optimized the process of uploading backups to cloud storage.
- Fixed some bugs in the plugin code.
- Optimized the plugin code.
= 0.9.84 =
- Upgraded the version of guzzlehttp/psr7 library in the plugin to 1.8.4.
- Fixed a bug of false positive backup failed email notifications.
- Fixed: Backup to Dropbox failed in some environments.
- Fixed a PHP warning of 'WPvivid_S3Request'.
- Fixed: some used images were falsely scanned as unused.
- Fixed some UI bugs.
- Fixed some bugs in the plugin code.
- Optimized the plugin code.
= 0.9.83 =
- Upgraded the backup and restore engine to improve the backup and restore success rate.
- Fixed some bugs in the plugin code.
- Fixed some UI bugs.
- Optimized the plugin code.
= 0.9.82 =
- Fixed: Backup failed when php_uname is disabled on the server.
- Fixed: 'Quick Snapshot' did not work on non-wpvivid pages.
- Fixed some PHP warnings on PHP 8.2 sites.
- Fixed some bugs in the plugin code.
- Optimized the plugin code.
= 0.9.81 =
- Fixed the compatibility issue with servers that have phpinfo() function disabled.
- Fixed some bugs in the plugin code.
- Optimized the plugin code.
= 0.9.80 =
- Optimized backup process on Litespeed web server.
- Staging error logs were not included in the Debug zip.
- Fixed some bugs in the plugin code.
- Optimized the plugin code.
- Successfully tested with WordPress 6.1.1.
= 0.9.79 =
- Fixed: All target pages except for home page showed 404 error in some cases after migration.
- Fixed some bugs in the plugin code.
- Optimized the plugin code.
- Successfully tested with WordPress 6.1.
= 0.9.78 =
- Added an option to create quick database snapshots.
- Added a check for siteurl and home in a restore process.
- Fixed: Some used images were falsely scanned as unused.
- Fixed some bugs in the plugin code and optimized the plugin code.
= 0.9.77 =
- Updated: Transferred files will be deleted automatically when auto migration fails.
- Fixed a vulnerability in the plugin code.
- Fixed some bugs in the plugin code.
- Optimized the plugin code.
= 0.9.76 =
- Added a check to the integrity of uploaded backups.
- Fixed a vulnerability in the plugin code.
- Fixed some bugs in the plugin code.
- Optimized the plugin code.
= 0.9.75 =
- Fixed: Page styling got lost after importing the page in some cases.
- Fixed: Some used images were falsely scanned as unused.
- Fixed some UI bugs.
- Fixed some bugs in the plugin code and optimized the plugin code.
= 0.9.74 =
- Fixed some i18n issues in the plugin code.
- Updated: Last backup time will be updated once the backup schedule is triggered.
- Fixed some bugs in the plugin code.
- Optimized the plugin code.
= 0.9.73 =
- Fixed some bugs in the plugin code and UI.
- Optimized the plugin code.
- Successfully tested with WordPress 6.0.
= 0.9.72 =
- Improved the upload function. Now when uploading a zip(part) failed, you will be notified immediately, and you can continue to upload the problematic zip rather than uploading all zips again.
- Added: Cloud storage credentials in the database are encrypted now.
- Changed: Cloud storage credentials are not showing in the storage edit page.
- Fixed: Selected themes were not copied when creating a fresh install in some cases.
- Fixed the wpvivid_request error that could appear in some cases when scanning unused images.
- Fixed: some used images were falsely scanned as unused.
- Optimized the plugin code to reduce server consumption.
- Fixed some bugs in the plugin code.
= 0.9.71 =
- Fixed the warning: Undefined array key "page" when editing pages in some cases.
- Fixed: Creating a fresh install failed when Elementor plugin is enabled.
- Fixed some vulnerabilities in the plugin code.
- Fixed some UI bugs.
- Fixed some bugs in the plugin code.
= 0.9.70 =
- Fixed: There was no notification after restoration in some environments.
- Fixed some vulnerabilities in the plugin code.
- Fixed: Backup information of live site would be copied to the staging site when creating a staging site.
- Changed staging site creation time to local time.
- Fixed some bugs in the plugin code.
- Successfully tested with WordPress 5.9.2.
= 0.9.69 =
- Updated: For security reasons, adding Google Drive, Dropbox, OneDrive now needs to get authentication first.
- Updated: Changed time in a log file to local time.
- Fixed the curl 60 error that could appear when backing up to Google Drive in some cases.
- Fixed: Disabling backup splitting did not take effect on PHP 8 sites.
- Fixed: Uploading backups to Dropbox failed in some cases.
- Fixed some bugs in the plugin code.
- Optimized the plugin code.
= 0.9.68 =
- Fixed: Failed to upload backups to Dropbox in some cases.
- Updated: Changed timezone in email report title to local time.
- Optimized the plugin code.
= 0.9.67 =
- Fixed: The object-cache.php file and protection files generated by Wordfence were not excluded during restore.
- Fixed: Some used images were falsely identified as unused.
- Added creation time for a staging site.
- Optimized the plugin code.
= 0.9.66 =
- Fixed a Dropbox folder bug.
- Fixed a conflict between the unused image cleaner and some themes.
- Fixed a problem that some used images in Elementor were identified as unused.
- Fixed: Downloading backup would failed in some cases.
- Added a check to Nginx server when creating a staging site.
- Optimized the plugin code.
= 0.9.65 =
- Fixed: Some WPvivid Backup Plugin settings were reset to default after restore.
- Fixed: Some urls could not be replaced because of escape format problems after restore.
- Fixed: Unused image could not be scanned in PHP 8.
- Fixed: Staging site admin url did not display correctly when the live site has a 'custom login url'.
- Optimized the plugin code.
= 0.9.64 =
- Fixed: Failed to refresh Dropbox token in some cases.
- Fixed: Custom menu style could not be properly migrated in some cases.
- Optimized the process of creating a staging site.
- Added an option to resume the task of creating a staging site when it was interrupted.
- Optimized the plugin code.
= 0.9.63 =
- Added support for Dropbox's new API.
- Fixed: some images used in Elementor would be scanned as 'unused'.
- Fixed some bugs in the plugin code.
- Optimized the plugin code.
= 0.9.62 =
- Added a check to the permissions of the staging folder before creating a staging site.
- Fixed some bugs in the plugin code.
- Optimized the plugin code.
- Successfully tested with WordPress 5.8.1.
= 0.9.61 =
- Added support for migration of unconventional save of the media paths.
- Fixed some bugs in the plugin code.
- Optimized the plugin code.
= 0.9.60 =
- Fixed: Failed to back up files of 0 KB in PHP 8 environment.
- Changed: The information of backup folder name will not be included when you export the plugin settings.
- Fixed some bugs in the plugin code.
- Optimized the plugin code.
= 0.9.59 =
- Fixed the PHP Guzzle library support compatibility issue which could cause backup failure in some cases.
- Fixed some bugs in the plugin code.
- Optimized the plugin code.
= 0.9.58 =
- Fixed a fatal error with the last update.
= 0.9.57 =
- Added a new feature of creating a staging site.
- Fixed some bugs in the plugin code.
- Optimized the plugin code.
= 0.9.56 =
- Fixed: Some used images would show up in the image cleaner results in some cases.
- Fixed some bugs in the plugin code.
- Optimized the plugin code.
= 0.9.55 =
- Successfully tested with WordPress 5.8.
- Fixed: Creating tables failed when restoring the database in some cases.
- Fixed some bugs in the plugin code.
- Optimized the plugin code.
= 0.9.54 =
- Added support for PHP 8.
- Fixed some bugs in the plugin code.
- Optimized the plugin code.
= 0.9.53 =
- Fixed a SQL injection vulnerability.
- Fixed some bugs in the plugin code and optimized the plugin code.
= 0.9.52 =
- Fixed a fatal error occurred during website transfer in some cases.
- Fixed some bugs in the plugin code.
- Optimized the plugin code.
= 0.9.51 =
- Added: Once a backup is created, the plugin will check whether the zip is good and will prompt you if it is corrupted.
- Fixed some bugs in the plugin code.
- Successfully tested with WordPress 5.7.
= 0.9.50 =
- Fixed some bugs in the plugin code.
- Optimized the plugin code.
- Successfully tested with WordPress 5.6.1.
= 0.9.49 =
- Fixed: A 404 error would returned when sending a request to wp-cron.php in some multilingual websites.
- Fixed: Could not turn pages in the backup list.
- Fixed some bugs in the plugin code.
- Optimized the plugin code.
= 0.9.48 =
- Redesigned the Download section for better UX.
- Fixed the insufficient permission error that occurred when authenticating FTP in some cases.
- Fixed the incorrect credential error that occurred when authenticating SFTP in some cases.
- Successfully tested with WordPress 5.6.
= 0.9.47 =
- Added support for Amazon S3 South Africa region.
- Fixed: Folder would not be backed up when it's name matches regex: ^uploads.*$.
- Successfully tested with WordPress 5.5.3.
- Fixed some bugs in the plugin code.
- Optimized the plugin code.
= 0.9.46 =
- Fixed: Some special characters in database could not be restored properly.
- Fixed: Only 1000 backups stored on Amazon S3 could be displayed.
- Fixed: Unused image cleaner also isolated images used in CSS files.
- Successfully tested with WordPress 5.5.1.
- Fixed some bugs in the plugin code.
- Optimized the plugin code.
= 0.9.45 =
- New feature Added: Find and clean unused images in your WP media library.
- Fixed some bugs in the plugin code.
- Optimized the plugin code.
= 0.9.44 =
- Successfully tested with WordPress 5.5.
- Fixed: Refreshing Google Drive token failed in some cases.
= 0.9.43 =
- Optimized migration process.
- Fixed some bugs in the plugin code.
- Optimized the plugin code.
= 0.9.42 =
- Added Bulgarian language translation.
- Fixed a fatal error occurred during website transfer in some cases.
- Fixed some bugs in the plugin code.
- Optimized the plugin code.
= 0.9.41 =
- Added an option in the plugin settings to delete the WPvivid directory when deleting the plugin.
- Added Italian language translation.
- Optimized the plugin UI.
- Fixed some bugs in the plugin code.
= 0.9.40 =
- Fixed: Backup schedules failed in some cases.
- Excluded the session_mm_cgi-fcgi file when creating a backup.
- Fixed some bugs in the plugin code.
= 0.9.39 =
- Excluded the /wphb-cache directory when creating a backup.
- Fixed: Root directory is now forbidden to set to '/' when connecting to a FTP server.
- Fixed the pagination issue in the process of exporting pages.
- Fixed some bugs in the plugin code.
= 0.9.38 =
- Successfully tested with WordPress 5.4.
- Added a new language template for translators.
= 0.9.37 =
- Changed the time in the name of the backup zip to the sites local time.
- Changed the time showed in the backup list and log list to the sites local time.
- Fixed some bugs in the plugin code.
= 0.9.36 =
- Added an option to overwrite existing pages in an import.
- Fixed: Could not retrieve posts list on a multilingual site in an export.
- Fixed some bugs in the plugin code and optimized the plugin code.
= 0.9.35 =
- Fixed a bug occurred when connecting with remote storage in some cases.
- Fixed some bugs in the plugin code.
- Optimized the plugin UI.
= 0.9.34 =
- Fixed the PHP 7.4 compatibility issue.
- Fixed: Backing up upload directory failed in some cases.
- Fixed: Backup filenames did not match the downloads part numbers.
- Updated the API for WPvivid Backup for MainWP extension.
- Fixed some bugs in the plugin code.
= 0.9.33 =
- Fixed:Replacing domain failed after migrating on servers using innodb database engine.
- Fixed: Compressed packages were lost in some cases.
- Added a column to the backup list to display backup content type.
- Temporarily removed translation files.
- Optimized the plugin code.
= 0.9.32 =
- Updated the plugin code for WPvivid Backup for MainWP extension.
- Fixed some bugs in the plugin code.
- Optimized the plugin code.
= 0.9.31 =
- Successfully tested with WordPress 5.3.2.
- Fixed: Backup could fail when the split file size was set to 0 MB in the shared hosting optimization mode.
- Fixed some small bugs in the plugin code.
- Optimized the process of restoring large amounts of data.
- Optimized the split backup file size to bring it closer to the value you set.
- Added Japanese language translation.
= 0.9.30 =
- Added an option to select database accessing method for a backup or restore process.
- Optimized plugin code and set the autoload attribute to no.
- Improved the success rate of backing up the uploads folder when the optimization mode for web hosting/shared hosting is enabled.
- Fixed some bugs in plugin code.
= 0.9.29 =
- Successfully tested with WordPress 5.3.
- Fixed: Locked backups were deleted automatically.
- Changed: Backups will now be split every 200MB by default.
- Fixed some bugs in the plugin code.
= 0.9.28 =
- New feature Added: Export and import posts or pages with images in bulk.
- Fixed: URL replacement failures after website migration in some cases.
- Fixed: Too many resumption attempts error that occurred when uploading backups in some cases.
- Fixed some bugs in the plugin code.
= 0.9.27 =
- Fixed a fatal error that could be triggered by some firewall or security plugins.
- Refined and simplified the plugin menu in admin menu and top admin bar.
- Optimized the plugin code.
- Added Polish language translation.
= 0.9.26 =
- Optimized the plugin's UI.
- Added a new tab for downloading WPvivid Backup for MainWP.
= 0.9.25 =
- Fixed: Could not restore websites in some cases.
- Fixed: The setting of PHP version that had been changed in .htaccess was lost after restoration.
- Added an option to merge all backup files into a package when a backup completes. This can increase backup and migration success rate in a website with insufficient server resources.
- Upgraded: Amazon S3 and DigitalOcean Space have upgraded their connection methods, so you will need to delete the previous connections and re-add your Amazon S3/DigtalOcean Space accounts to make sure the connections work.
- Optimized the plugin code.
= 0.9.24 =
- Fixed some bugs in the plugin code.
- Fixed: Could not restore files to proper directories if one had customized the sites file structure.
- Fixed: The page could not properly display when one chose Remote Storage option from the admin sidebar menu.
- Optimized backup process, now it saves more disk space.
- Optimized the plugin code.
= 0.9.23 =
- Added an option to hide the plugin menu on the top admin bar.
- Fixed: Always sent email notifications even the Only send an email notification when a backup fails option was selected.
- Fixed: The plugin menu on the top admin bar is visible to all users.
- Refined some error messages.
= 0.9.22 =
- Fixed: Backup created in web hosting/shared hosting optimization mode was incomplete in some cases.
- Fixed: Backup actually failed but was reported as a success in some cases.
- Refined error messages of migration process.
- Added a notice to the situation where backup schedules were unusable because the WP Cron on the server was disabled.
- Optimized the plugin code.
= 0.9.21 =
- Fixed: Special data in some database tables could not be replaced during a restore, which would cause failure of the restore.
- Fixed: Migration between sites that have different backup storage directories would fail.
- Fixed: The error establishing database connection occurred in some cases while loading the plugin page.
- Optimized the plugin code.
= 0.9.20 =
- Added an advanced section in settings page.
- Optimized the layout of settings page and display of some settings.
- Added an option of enabling optimization mode for web hosting/shared hosting in advanced settings.
- Added a memory_limit option in advanced settings.
- Added a chunk size option in advanced settings. 
- Added an option to cancel a running migration.
- Provided the WPDB as the interface with the database for the sites missing PDO_MYSQL.
- Fixed a timeout error occurred in some cases during backup process.
- Optimized the plugin code.
= 0.9.19 =
- Added a php memory limit option to settings, you can use it to temporarily increase php memory limit when encountering a memory exhausted error in backup process.
- Fixed: Backup does not exist error that occurred in some cases when downloading the backup to local.
- Fixed: Backup error that occurred when the wp-content/plugins folder on a web server was moved or renamed.
- Fixed: Restore error that occurred in some cases when restoring a backup to a different domain.
- Enhanced the clean backup cache option in settings.
- Optimized backup process.
= 0.9.18 =
- Optimized migration process, improved compatibility for migration. Old keys will be expired after you update to the new version.
- Added an option to retry the backup operation when encountering a timeout error.
- Added an option to hide settings in admin menu.
- Changed the plugin icon showing in admin menu.
- Included more info in the error log when sending to support.
- Improved compatibility with some hosting like GoDaddy.
- Optimized the cache directory in backup process.
- Fixed errors occurred in some cases during the authentication process with Google Drive, Dropbox, Microsoft OneDrive.
- Optimized plugin code.
= 0.9.17 =
- Added a sole tab for backup schedules.
- Refined descriptions in the UI.
- Fixed a few UI bugs.
- Successfully tested with WordPress 5.2.
= 0.9.16 =
- Fixed a fatal error occurred during website transfer.
= 0.9.15 =
- Fixed: Scheduled backups failed to run as configured after the last update.
- Improved the Restore UI.
- Refined some descriptions in the UI.
= 0.9.14 =
- Added free website transfer feature. We highly recommend all our users to update.
- Added backup upload feature. Now you can upload a backup to restore or transfer.
= 0.9.13 =
- Fixed: Sometimes could not correctly determine database privileges when backing up.
= 0.9.12 =
- Added an 'Send Debug Information to Us' button in Website Info page.
- Improved the compatibility with PHP v5.3 to v5.5.
- Fixed the compatibility issue with MainWP plugin.
- Fixed: Could not correctly calculate files size when backing up.
- Fixed: Could not back up to SFTP server sometimes.
- Fixed: Database backup failure because of insufficient privileges.
- Enriched backup logs with more details.
- Refined some descriptions on user interface.
- Optimized code of the plugin.
= 0.9.11 =
- Added support for DigitalOcean Spaces.
- Added an HTML email template to backup reports.
= 0.9.10 =
- Fixed: Some icons were missing in UI.
= 0.9.9 =
- Fixed a packaging error which might cause the failure of activating the plugin.
= 0.9.8 =
- Added support for Google Drive, Micosoft OneDrive, Dropbox cloud storage.
- Fixed: Could not restore a backup from cloud storage.
- Optimized code of the plugin.
= 0.9.7 =
- Fixed data type errors caused by the last update. The errors would cause the failure of running of scheduled backup tasks. We highly recommend you upgrade.
- Fixed a bug where the last backup information was not displayed in backup schedule list.
- Changed a few error messages that appear during the backup process.
- Optimized code of the plugin.
= 0.9.6 =
- Optimized code of the plugin.
- A more lightweight Amazon S3 library has been used, so that you do not need to fill in Region field while configuring a S3 storage account.
= 0.9.5 =
- Refined descriptions on user interface.
- Fixed a few UI bugs.
- Fixed a bug where backups were runnable in some cases during the process of a restoration.
= 0.9.4 =
- Added support for responsive design. Now the plugin is compatible with smartphones, tablets and PC.
- Fixed some UI bugs.
= 0.9.3 =
- Fixed some display errors on user interface.
- Fixed a bug where backups could not be completed in exceptional cases.
= 0.9.2 =
- Fixed image path display error.
= 0.9.1 =
- Initial release of the plugin. Now you see it.

== Upgrade Notice ==
Latest version 0.9.113:
= 0.9.113 =
- Added 4 backup performance modes for different scenarios.
- Fixed a vulnerability in the plugin code.
- Successfully tested with WordPress 6.7.2.wpvivid-backuprestore.php000064400000033476151327705700011640 0ustar00<?php
/**
 * @link              https://wpvivid.com
 * @since             0.9.1
 * @package           wpvivid
 *
 * @wordpress-plugin
 * Plugin Name:       WPvivid Backup Plugin
 * Description:       Clone or copy WP sites then move or migrate them to new host (new domain), schedule backups, transfer backups to leading remote storage. All in one.
 * Version:           0.9.113
 * Author:            WPvivid Backup & Migration
 * Author URI:        https://wpvivid.com
 * License:           GPL-3.0+
 * License URI:       http://www.gnu.org/copyleft/gpl.html
 * Text Domain:       wpvivid-backuprestore
 * Domain Path:       /languages
 */

// If this file is called directly, abort.
if ( ! defined( 'WPINC' ) ) {
    die;
}

define( 'WPVIVID_PLUGIN_VERSION', '0.9.113' );
//
define('WPVIVID_RESTORE_INIT','init');
define('WPVIVID_RESTORE_READY','ready');
define('WPVIVID_RESTORE_COMPLETED','completed');
define('WPVIVID_RESTORE_RUNNING','running');
define('WPVIVID_RESTORE_ERROR','error');
define('WPVIVID_RESTORE_WAIT','wait');
define('WPVIVID_RESTORE_TIMEOUT',180);

define('WPVIVID_PLUGIN_SLUG','WPvivid');
define('WPVIVID_PLUGIN_NAME',plugin_basename(__FILE__));
define('WPVIVID_PLUGIN_URL',plugins_url('',__FILE__));
define('WPVIVID_PLUGIN_DIR',dirname(__FILE__));
define('WPVIVID_PLUGIN_DIR_URL',plugin_dir_url(__FILE__).'admin/');
define('WPVIVID_PLUGIN_IMAGES_URL',WPVIVID_PLUGIN_URL.'/admin/partials/images/');
//We set a long enough default execution time (10 min) to ensure that the backup process can be completed.
define('WPVIVID_MAX_EXECUTION_TIME',300);
define('WPVIVID_RESTORE_MAX_EXECUTION_TIME', 300);
define('WPVIVID_MEMORY_LIMIT','512M');
define('WPVIVID_RESTORE_MEMORY_LIMIT','512M');
define('WPVIVID_MIGRATE_SIZE', '2048');
//If the server uses fastcgi then default execution time should be set to 2 min for more efficient.
define('WPVIVID_MAX_EXECUTION_TIME_FCGI',180);
//Default number of reserved backups
define('WPVIVID_MAX_BACKUP_COUNT',7);
define('WPVIVID_DEFAULT_BACKUP_COUNT',3);
define('WPVIVID_DEFAULT_COMPRESS_TYPE','zip');
//Max zip file size.
define('WPVIVID_DEFAULT_MAX_FILE_SIZE',200);
//Instruct PclZip to use all the time temporary files to create the zip archive or not.The default value is 1.
define('WPVIVID_DEFAULT_USE_TEMP',1);
//Instruct PclZip to use temporary files for files with size greater than.The default value is 16M.
define('WPVIVID_DEFAULT_USE_TEMP_SIZE',16);
//Exclude the files which is larger than.The default value is 0 means unlimited.
define('WPVIVID_DEFAULT_EXCLUDE_FILE_SIZE',0);
//Add a file in an archive without compressing the file.The default value is 200.
define('WPVIVID_DEFAULT_NO_COMPRESS',true);
//Backup save folder under WP_CONTENT_DIR
define('WPVIVID_DEFAULT_BACKUP_DIR','wpvividbackups');
//Log save folder under WP_CONTENT_DIR
define('WPVIVID_DEFAULT_LOG_DIR','wpvividbackups'.DIRECTORY_SEPARATOR.'wpvivid_log');
//Old files folder under WPVIVID_DEFAULT_BACKUP_DIR
define('WPVIVID_DEFAULT_ROLLBACK_DIR','wpvivid-old-files');
//
define('WPVIVID_DEFAULT_ADMIN_BAR', true);
define('WPVIVID_DEFAULT_TAB_MENU', true);
define('WPVIVID_DEFAULT_DOMAIN_INCLUDE', true);
//
define('WPVIVID_DEFAULT_ESTIMATE_BACKUP', true);
//Specify the folder and database to be backed up
define('WPVIVID_DEFAULT_SUBPACKAGE_PLUGIN_UPLOAD', false);

//define schedule hooks
define('WPVIVID_MAIN_SCHEDULE_EVENT','wpvivid_main_schedule_event');
define('WPVIVID_RESUME_SCHEDULE_EVENT','wpvivid_resume_schedule_event');
define('WPVIVID_CLEAN_BACKING_UP_DATA_EVENT','wpvivid_clean_backing_up_data_event');
define('WPVIVID_TASK_MONITOR_EVENT','wpvivid_task_monitor_event');
define('WPVIVID_CLEAN_BACKUP_RECORD_EVENT','wpvivid_clean_backup_record_event');
//backup resume retry times
define('WPVIVID_RESUME_RETRY_TIMES',6);
define('WPVIVID_RESUME_INTERVAL',60);

define('WPVIVID_REMOTE_CONNECT_RETRY_TIMES','3');
define('WPVIVID_REMOTE_CONNECT_RETRY_INTERVAL','3');

define('WPVIVID_PACK_SIZE',1 << 20);

define('WPVIVID_SUCCESS','success');
define('WPVIVID_FAILED','failed');
/**
 * The core plugin class that is used to define internationalization,
 * admin-specific hooks, and public-facing site hooks.
 */
//when active plugin redirect plugin page.


function wpvivid_plugin_activate()
{
    $dir=get_option('wpvivid_local_setting');

    if(!isset($dir['path']))
    {
        $backup_dir=WPVIVID_DEFAULT_BACKUP_DIR;
        $backup_log_dir=WPVIVID_DEFAULT_LOG_DIR;
    }
    else
    {
        $backup_dir=$dir['path'];
        $backup_log_dir=$dir['path'].DIRECTORY_SEPARATOR.'wpvivid_log';
    }

    if(!is_dir(WP_CONTENT_DIR.DIRECTORY_SEPARATOR.$backup_dir))
    {
        @mkdir(WP_CONTENT_DIR.DIRECTORY_SEPARATOR.$backup_dir,0777,true);
        @fopen(WP_CONTENT_DIR.DIRECTORY_SEPARATOR.$backup_dir.DIRECTORY_SEPARATOR.'index.html', 'x');
        $tempfile=@fopen(WP_CONTENT_DIR.DIRECTORY_SEPARATOR.$backup_dir.DIRECTORY_SEPARATOR.'.htaccess', 'x');
        if($tempfile)
        {
            $text="<IfModule mod_rewrite.c>\r\nRewriteEngine On\r\nRewriteRule .* - [F,L]\r\n</IfModule>";
            fwrite($tempfile,$text );
            fclose($tempfile);
        }
    }
    if(!is_dir(WP_CONTENT_DIR.DIRECTORY_SEPARATOR.$backup_log_dir))
    {
        @mkdir(WP_CONTENT_DIR.DIRECTORY_SEPARATOR.$backup_log_dir,0777,true);
        @fopen(WP_CONTENT_DIR.DIRECTORY_SEPARATOR.$backup_log_dir.DIRECTORY_SEPARATOR.'index.html', 'x');
        $tempfile=@fopen(WP_CONTENT_DIR.DIRECTORY_SEPARATOR.$backup_log_dir.DIRECTORY_SEPARATOR.'.htaccess', 'x');
        if($tempfile)
        {
            $text="<IfModule mod_rewrite.c>\r\nRewriteEngine On\r\nRewriteRule .* - [F,L]\r\n</IfModule>";
            fwrite($tempfile,$text );
            fclose($tempfile);
        }
    }
    if(!is_dir(WP_CONTENT_DIR.DIRECTORY_SEPARATOR.$backup_log_dir.DIRECTORY_SEPARATOR.'error'))
    {
        @mkdir(WP_CONTENT_DIR.DIRECTORY_SEPARATOR.$backup_log_dir.DIRECTORY_SEPARATOR.'error',0777,true);
        @fopen(WP_CONTENT_DIR.DIRECTORY_SEPARATOR.$backup_log_dir.DIRECTORY_SEPARATOR.'error'.DIRECTORY_SEPARATOR.'index.html', 'x');
        $tempfile=@fopen(WP_CONTENT_DIR.DIRECTORY_SEPARATOR.$backup_log_dir.DIRECTORY_SEPARATOR.'error'.DIRECTORY_SEPARATOR.'.htaccess', 'x');
        if($tempfile)
        {
            $text="<IfModule mod_rewrite.c>\r\nRewriteEngine On\r\nRewriteRule .* - [F,L]\r\n</IfModule>";
            fwrite($tempfile,$text );
            fclose($tempfile);
        }
    }

    //A flag to determine whether plugin had been initialized
    $init=get_option('wpvivid_init', 'not init');
    if($init=='not init')
    {
        include_once WPVIVID_PLUGIN_DIR . '/includes/class-wpvivid-setting.php';
        //Initialization settings
        WPvivid_Setting::init_option();
        update_option('wpvivid_init','init','no');
    }

    $wpvivid_remote_init=get_option('wpvivid_remote_init', 'not init');
    if($wpvivid_remote_init=='not init')
    {
        include_once WPVIVID_PLUGIN_DIR . '/includes/class-wpvivid-setting.php';
        wpvivid_init_remote_option();
        update_option('wpvivid_remote_init','init','no');
    }

    add_option('wpvivid_do_activation_redirect', true);
}

function wpvivid_init_plugin_redirect()
{
    if (get_option('wpvivid_do_activation_redirect', false))
    {
        delete_option('wpvivid_do_activation_redirect');

        $active_plugins = get_option('active_plugins');
        if(!function_exists('get_plugins'))
            require_once(ABSPATH . 'wp-admin/includes/plugin.php');
        $plugins=get_plugins();
        $pro_wpvivid_slug='wpvivid-backup-pro/wpvivid-backup-pro.php';
        $b_redirect_pro=false;
        if(!empty($plugins))
        {
            if(isset($plugins[$pro_wpvivid_slug]))
            {
                if(in_array($pro_wpvivid_slug, $active_plugins))
                {
                    $b_redirect_pro=true;
                }
            }
        }

        if($b_redirect_pro)
        {
            $url=apply_filters('wpvivid_backup_activate_redirect_url','admin.php?page=wpvivid-dashboard');
            if (is_multisite())
            {
                wp_redirect(network_admin_url().$url);
            }
            else
            {
                wp_redirect(admin_url().$url);
            }
        }
        else
        {
            $url=apply_filters('wpvivid_backup_activate_redirect_url','admin.php?page=WPvivid');
            if (is_multisite())
            {
                wp_redirect(network_admin_url().$url);
            }
            else
            {
                wp_redirect(admin_url().$url);
            }
        }
    }
}

function wpvivid_init_remote_option()
{
    $remoteslist=WPvivid_Setting::get_all_remote_options();
    foreach ($remoteslist as $key=>$value)
    {
        if(!array_key_exists('options',$value))
        {
            continue;
        }
        $remote = array();
        if($value['type'] === 'ftp')
        {
            $remote['host']=$value['options']['host'];
            $remote['username']=$value['options']['username'];
            $remote['password']=$value['options']['password'];
            $remote['path']=$value['options']['path'];
            $remote['name']=$value['options']['name'];
            $remote['passive']=$value['options']['passive'];
            $value['type'] = strtolower($value['type']);
            $remote['type']=$value['type'];
            $remoteslist[$key]=$remote;
        }
        elseif ($value['type'] === 'sftp')
        {
            $remote['host']=$value['options']['host'];
            $remote['username']=$value['options']['username'];
            $remote['password']=$value['options']['password'];
            $remote['path']=$value['options']['path'];
            $remote['name']=$value['options']['name'];
            $remote['port']=$value['options']['port'];
            $value['type'] = strtolower($value['type']);
            $remote['type']=$value['type'];
            $remoteslist[$key]=$remote;
        }
        elseif ($value['type'] === 'amazonS3')
        {
            $remote['classMode']='0';
            $remote['sse']='0';
            $remote['name']=$value['options']['name'];
            $remote['access']=$value['options']['access'];
            $remote['secret']=$value['options']['secret'];
            $remote['s3Path']=$value['options']['s3Path'];
            $value['type'] = strtolower($value['type']);
            $remote['type']=$value['type'];
            $remoteslist[$key]=$remote;
        }
    }
    WPvivid_Setting::update_option('wpvivid_upload_setting',$remoteslist);

    include_once WPVIVID_PLUGIN_DIR . '/includes/class-wpvivid-backuplist.php';

    $backuplist=WPvivid_Backuplist::get_backuplist();
    foreach ($backuplist as $key=>$value)
    {
        if(is_array($value['remote']))
        {
            foreach ($value['remote'] as $remote_key=>$storage_type)
            {
                if(!array_key_exists('options',$storage_type))
                {
                    continue;
                }
                $remote = array();
                if($storage_type['type'] === 'ftp')
                {
                    $remote['host']=$storage_type['options']['host'];
                    $remote['username']=$storage_type['options']['username'];
                    $remote['password']=$storage_type['options']['password'];
                    $remote['path']=$storage_type['options']['path'];
                    $remote['name']=$storage_type['options']['name'];
                    $remote['passive']=$storage_type['options']['passive'];
                    $storage_type['type'] = strtolower($storage_type['type']);
                    $remote['type']=$storage_type['type'];
                }
                elseif ($storage_type['type'] === 'sftp')
                {
                    $remote['host']=$storage_type['options']['host'];
                    $remote['username']=$storage_type['options']['username'];
                    $remote['password']=$storage_type['options']['password'];
                    $remote['path']=$storage_type['options']['path'];
                    $remote['name']=$storage_type['options']['name'];
                    $remote['port']=$storage_type['options']['port'];
                    $storage_type['type'] = strtolower($storage_type['type']);
                    $remote['type']=$storage_type['type'];
                }
                elseif ($storage_type['type'] === 'amazonS3')
                {
                    $remote['classMode']='0';
                    $remote['sse']='0';
                    $remote['name']=$storage_type['options']['name'];
                    $remote['access']=$storage_type['options']['access'];
                    $remote['secret']=$storage_type['options']['secret'];
                    $remote['s3Path']=$storage_type['options']['s3Path'];
                    $storage_type['type'] = strtolower($storage_type['type']);
                    $remote['type']=$storage_type['type'];
                }
                $backuplist[$key]['remote'][$remote_key]=$remote;
            }
        }
    }
    WPvivid_Setting::update_option('wpvivid_backup_list',$backuplist);
}

register_activation_hook(__FILE__, 'wpvivid_plugin_activate');
add_action('admin_init', 'wpvivid_init_plugin_redirect');

/**
 * Begins execution of the plugin.
 *
 * Since everything within the plugin is registered via hooks,
 * then kicking off the plugin from this point in the file does
 * not affect the page life cycle.
 *
 * @since    0.9.1
 */
if(isset($wpvivid_plugin)&&is_a($wpvivid_plugin,'WPvivid'))
{
    return ;
}

require plugin_dir_path( __FILE__ ) . 'includes/class-wpvivid.php';

function run_wpvivid()
{
    $wpvivid_plugin = new WPvivid();
    $GLOBALS['wpvivid_plugin'] = $wpvivid_plugin;
}
run_wpvivid();

Youez - 2016 - github.com/yon3zu
LinuXploit